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.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 1.9.13 All 162 releases
woocommerce-pos / includes / Sync / Coupon_Modified_Date.php

Coupon_Modified_Date.php in WCPOS – Point of Sale (POS) plugin for WooCommerce 1.10.17, at includes/Sync/Coupon_Modified_Date.php

86 lines 3.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Coupon post-date touch shared by the v1 and v2 write lanes.
4 *
5 * @package WCPOS\WooCommercePOS\Sync
6 */
7
8 namespace WCPOS\WooCommercePOS\Sync;
9
10 /**
11 * WC's coupon CPT data store only calls wp_update_post() when a POST field
12 * (code, description, status, dates) actually changes. A meta-backed edit —
13 * `amount`, `discount_type`, the usage limits — is written straight to postmeta,
14 * so `post_modified(_gmt)` is left stale.
15 *
16 * That is not cosmetic. The client's catalogue replication is INCREMENTAL and
17 * date-based: it polls `?modified_after=<cursor>&fields=id,date_modified_gmt`
18 * (packages/query/src/{collection-replication-state,data-fetcher}.ts), and
19 * `Catalog_Proxy_Controller` forwards that parameter untouched to wc/v3, where
20 * WooCommerce filters on `post_modified_gmt`. A coupon whose amount changed but
21 * whose post date did not move is therefore INVISIBLE to every other till until
22 * something else happens to touch the post.
23 *
24 * v1 covered this by installing a `woocommerce_update_coupon` listener for the
25 * duration of a v1 REST dispatch (see API/V1/Coupons_Controller). The v2 push
26 * lane forwards to stock wc/v3 and installs no such listener, so the writer has
27 * to apply the touch itself — hence this shared helper rather than a second
28 * copy of the SQL.
29 *
30 * @see https://github.com/wcpos/woocommerce-pos-pro/issues/86
31 */
32 class Coupon_Modified_Date {
33 /**
34 * Listen for every coupon save, whatever wrote it.
35 *
36 * Registered UNCONDITIONALLY from Init — deliberately not behind the schema
37 * latch, because this touches `wp_posts` only and the client's replication is
38 * date-based on BOTH lanes. A cashier does not care which surface a discount
39 * was edited from: a merchant changing a coupon amount in wp-admin, over
40 * WP-CLI, or from another plugin has changed the coupon, and every till must
41 * see it on its next incremental poll. v1 only ever installed this for the
42 * duration of its own REST dispatch, so an admin-side edit was invisible to
43 * the POS — that was a gap, not a design.
44 *
45 * `touch()` writes with $wpdb->update() rather than wp_update_post(), so it
46 * cannot re-enter `woocommerce_update_coupon` — no recursion guard needed.
47 */
48 public static function register_hooks(): void {
49 add_action( 'woocommerce_update_coupon', array( __CLASS__, 'touch' ), 10, 1 );
50 }
51
52 /**
53 * Advance a coupon's post_modified(_gmt) beyond its previous value.
54 *
55 * Unconditional by design, exactly like the v1 listener: the caller only
56 * reaches this after a write it already knows succeeded. The stored value
57 * must advance by at least one second because incremental queries use a
58 * strict `modified_after` comparison and WordPress timestamps have
59 * one-second precision.
60 *
61 * @param int $coupon_id The coupon post ID.
62 */
63 public static function touch( int $coupon_id ): void {
64 global $wpdb;
65
66 $previous_gmt = (string) get_post_field( 'post_modified_gmt', $coupon_id );
67 $modified_gmt = gmdate(
68 'Y-m-d H:i:s',
69 max( time(), strtotime( $previous_gmt . ' UTC' ) + 1 )
70 );
71
72 $wpdb->update(
73 $wpdb->posts,
74 array(
75 'post_modified' => get_date_from_gmt( $modified_gmt ),
76 'post_modified_gmt' => $modified_gmt,
77 ),
78 array( 'ID' => $coupon_id ),
79 array( '%s', '%s' ),
80 array( '%d' )
81 );
82
83 clean_post_cache( $coupon_id );
84 }
85 }
86