PluginProbe
WCPOS – Point of Sale (POS) plugin for WooCommerce / 1.10.17
WCPOS – Point of Sale (POS) plugin for WooCommerce v1.10.17
1.10.25 1.10.24 1.10.23 1.10.22 1.10.21 1.10.20 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 All 169 releases
← All changes | includes/Orders.php +0 -166 1.10.22 → 1.10.17 View file →
@@ -61,9 +61,8 @@
61 61 add_filter( 'woocommerce_payment_complete_order_status', array( $this, 'payment_complete_order_status' ), 10, 3 );
62 62 add_filter( 'woocommerce_bacs_process_payment_order_status', array( $this, 'offline_process_payment_order_status' ), 10, 2 );
63 63 add_filter( 'woocommerce_cheque_process_payment_order_status', array( $this, 'offline_process_payment_order_status' ), 10, 2 );
64 64 add_filter( 'woocommerce_cod_process_payment_order_status', array( $this, 'offline_process_payment_order_status' ), 10, 2 );
65 - add_filter( 'woocommerce_payment_successful_result', array( $this, 'apply_unpaid_gateway_order_status' ), 10, 2 );
66 65 add_filter( 'woocommerce_hidden_order_itemmeta', array( $this, 'hidden_order_itemmeta' ) );
67 66 add_filter( 'woocommerce_order_item_product', array( $this, 'order_item_product' ), 10, 2 );
68 67 add_filter( 'woocommerce_order_get_tax_location', array( $this, 'get_tax_location' ), 10, 2 );
69 68 add_action( 'woocommerce_order_item_after_calculate_taxes', array( $this, 'order_item_after_calculate_taxes' ) );
@@ -214,173 +213,8 @@
214 213
215 214 return \in_array( $normalized_status, $valid_statuses, true )
216 215 ? $normalized_status
217 216 : $fallback;
218 - }
219 -
220 - /**
221 - * Apply the configured POS order status when a gateway settles without payment.
222 - *
223 - * The generic form of offline_process_payment_order_status(). Those three
224 - * gateways are hooked by name only because BACS, cheque and COD each expose a
225 - * `woocommerce_{id}_process_payment_order_status` filter. A gateway that takes
226 - * no money at the till and exposes no such filter — a quote, invoice or
227 - * purchase-order gateway — returned success while leaving the order at
228 - * pos-open, so the configured status was never applied and the till never
229 - * finished the sale. `woocommerce_payment_successful_result` is the seam every
230 - * gateway passes through: WooCommerce applies it after any successful
231 - * process_payment(), including on the POS pay page.
232 - *
233 - * Deliberately narrow, because "returned success but left the order open" is
234 - * also what a gateway awaiting an async confirmation looks like:
235 - *
236 - * - `pos-open` only, never `pos-partial` — a partial tender is still owed
237 - * money, and closing it would lose that.
238 - * - no `date_paid` — money moving means the payment_complete path owns the
239 - * status.
240 - * - the gateway must be enabled for POS *and* carry an explicitly stored
241 - * status. The settings view synthesizes `wc-completed` for every installed
242 - * gateway it has never seen, so trusting the computed value would mark an
243 - * unconfigured third-party gateway Completed with no money taken.
244 - *
245 - * @param array $result Gateway result, passed through untouched.
246 - * @param int $order_id Order ID.
247 - *
248 - * @return array
249 - */
250 - public function apply_unpaid_gateway_order_status( $result, $order_id ) {
251 - if ( ! woocommerce_pos_request() ) {
252 - return $result;
253 - }
254 -
255 - $order = wc_get_order( $order_id );
256 -
257 - if ( ! $order instanceof WC_Order || ! woocommerce_pos_is_pos_order( $order ) ) {
258 - return $result;
259 - }
260 -
261 - if ( ! $order->has_status( 'pos-open' ) || $order->get_date_paid( 'edit' ) ) {
262 - return $result;
263 - }
264 -
265 - $gateway_id = $order->get_payment_method();
266 - $configured = $this->get_stored_gateway_order_status( $gateway_id );
267 -
268 - if ( '' === $configured ) {
269 - return $result;
270 - }
271 -
272 - $status = $this->normalize_status( $configured, '' );
273 -
274 - if ( '' === $status || $order->has_status( $status ) ) {
275 - return $result;
276 - }
277 -
278 - /*
279 - * payment_complete_order_status() reports the configured status as this
280 - * order's paid status, which makes WC_Order::set_status() stamp date_paid
281 - * the moment the status changes — booking an unpaid order as revenue. No
282 - * payment was taken here, so suppress it for this transition only.
283 - *
284 - * Scoped to this order id: a status-transition handler can call
285 - * payment_complete() on a *different* order while this filter is live
286 - * (subscriptions, bundles and gift-card plugins all do), and an
287 - * unconditional '' would reach that order too — set_status() rejects an
288 - * unknown status and falls back to 'pending', leaving an order that was
289 - * just paid sitting unpaid.
290 - */
291 - $target_id = $order->get_id();
292 - $suppress_paid_date = static function ( $payment_status, $filtered_order_id ) use ( $target_id ) {
293 - return (int) $filtered_order_id === $target_id ? '' : $payment_status;
294 - };
295 -
296 - add_filter( 'woocommerce_payment_complete_order_status', $suppress_paid_date, PHP_INT_MAX, 2 );
297 -
298 - try {
299 - /*
300 - * update_status()'s return value is deliberately not checked. It reports
301 - * false only when the order has no id — impossible here — because
302 - * WC_Abstract_Order::save() and WC_Order::status_transition() each catch
303 - * Exception themselves and handle_exception() does not rethrow, so a
304 - * throwing hook never reaches update_status()'s own catch and it still
305 - * returns true. Nor could the checkout be aborted from here:
306 - * WC_Form_Handler::pay_action() applies this filter inside its
307 - * `'success' === $result['result']` branch and redirects unconditionally
308 - * on the next line.
309 - */
310 - $order->update_status(
311 - $status,
312 - /* translators: %s: payment gateway title. */
313 - sprintf( __( 'Order status set by %s; no payment was taken at the till.', 'woocommerce-pos' ), $order->get_payment_method_title() )
314 - );
315 - } finally {
316 - // Must come off even if a status-change handler throws: left in place it
317 - // would suppress the configured status, and date_paid, for every later
318 - // payment in this request.
319 - remove_filter( 'woocommerce_payment_complete_order_status', $suppress_paid_date, PHP_INT_MAX );
320 - }
321 -
322 - return $result;
323 - }
324 -
325 - /**
326 - * Read the explicitly stored per-gateway order status.
327 - *
328 - * Reads the raw options rather than the settings service, because the service
329 - * rebuilds its view from the installed gateways and synthesizes a default
330 - * status for gateways the merchant has never configured. Only a status the
331 - * merchant actually chose, on a gateway they enabled for POS, counts as intent.
332 - *
333 - * Two places hold such a choice, matching Payment_Gateways_Section::read():
334 - * the per-gateway entry, and — on sites upgraded from before per-gateway
335 - * statuses — the legacy global `checkout.order_status`, which that section
336 - * still applies in memory to any gateway with no explicit status of its own
337 - * until the merchant next saves.
338 - *
339 - * @param string $gateway_id The payment gateway ID.
340 - *
341 - * @return string The stored status (may include the wc- prefix), or '' when absent.
342 - */
343 - private function get_stored_gateway_order_status( string $gateway_id ): string {
344 - if ( '' === $gateway_id ) {
345 - return '';
346 - }
347 -
348 - $stored = get_option( 'woocommerce_pos_settings_payment_gateways', array() );
349 -
350 - if ( ! \is_array( $stored ) || ! isset( $stored['gateways'][ $gateway_id ] ) || ! \is_array( $stored['gateways'][ $gateway_id ] ) ) {
351 - return '';
352 - }
353 -
354 - $gateway = $stored['gateways'][ $gateway_id ];
355 -
356 - if ( ! isset( $gateway['enabled'] ) || ! wc_string_to_bool( $gateway['enabled'] ) ) {
357 - return '';
358 - }
359 -
360 - if ( isset( $gateway['order_status'] ) && \is_string( $gateway['order_status'] ) && '' !== $gateway['order_status'] ) {
361 - return $gateway['order_status'];
362 - }
363 -
364 - return $this->get_legacy_checkout_order_status();
365 - }
366 -
367 - /**
368 - * Read the legacy global checkout order status.
369 - *
370 - * Pre-dates per-gateway statuses. Payment_Gateways_Section::read() still seeds
371 - * it in memory for gateways with no explicit status, and leaves the key in
372 - * place until the merchant saves, so an upgraded site can have an enabled
373 - * gateway whose only configured status lives here.
374 - *
375 - * @return string The stored legacy status, or '' when absent.
376 - */
377 - private function get_legacy_checkout_order_status(): string {
378 - $checkout = get_option( 'woocommerce_pos_settings_checkout', array() );
379 -
380 - return \is_array( $checkout ) && isset( $checkout['order_status'] ) && \is_string( $checkout['order_status'] )
381 - ? $checkout['order_status']
382 - : '';
383 217 }
384 218
385 219 /**
386 220 * Resolve the configured POS order status for a given payment gateway.