1.7.0: shipping rule "Only" reserves a method, "Show" no longer does

Since 1.6.0 "Show" both whitelisted a method for its group and hid it from
everybody else. A group shown ordinary methods next to a free one therefore
took the ordinary ones away from every stranger, who was left with only the
free method. "Only" now carries the reservation; "Show" is just the group's
whitelist. Existing show rules are converted to only once on update so the
checkout does not change until the shop chooses.

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
kristianwind
2026-10-02 12:28:45 +02:00
co-authored by Claude Code
parent 5875638f2c
commit 0793a1ee82
11 changed files with 129 additions and 37 deletions
+37 -7
View File
@@ -77,7 +77,7 @@ class WC_EDM_DB {
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
group_id bigint(20) unsigned NOT NULL,
shipping_method_id varchar(191) NOT NULL,
action enum('show','hide') NOT NULL DEFAULT 'show',
action enum('show','hide','only') NOT NULL DEFAULT 'show',
sort_order int(11) NOT NULL DEFAULT 0,
PRIMARY KEY (id),
KEY group_id (group_id)
@@ -102,10 +102,37 @@ class WC_EDM_DB {
dbDelta( $sql );
self::add_foreign_keys();
self::migrate_show_to_only();
update_option( 'wc_edm_db_version', WC_EDM_VERSION );
}
/**
* Once, when "only" was introduced: every existing "show" rule was reserved
* for its group, so it becomes "only" and the shop's checkout looks exactly
* as it did. Without this the update would silently put reserved free
* delivery back in front of every stranger.
*/
private static function migrate_show_to_only() {
global $wpdb;
if ( get_option( 'wc_edm_show_to_only_done' ) ) {
return;
}
$table = self::table( 'shipping_rules' );
$column = $wpdb->get_row( "SHOW COLUMNS FROM {$table} LIKE 'action'" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
// dbDelta did not widen the enum: converting now would store '' rather
// than 'only'. Leave the flag unset so the next load tries again.
if ( ! $column || false === strpos( (string) $column->Type, "'only'" ) ) {
return;
}
$wpdb->query( "UPDATE {$table} SET action = 'only' WHERE action = 'show'" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
update_option( 'wc_edm_show_to_only_done', 1 );
}
/**
* Run create_tables() again when the plugin was updated without
* re-activation (dbDelta adds any missing columns).
@@ -510,10 +537,13 @@ class WC_EDM_DB {
/**
* Every shipping method any group reserves for itself.
*
* A "show" rule means the group sees that method. It also has to mean
* nobody else does — otherwise reserving a free-delivery method for an
* approved group leaves it on the checkout for every other customer, which
* is what a shop notices as lost shipping revenue rather than as a bug.
* An "only" rule means the group sees that method AND nobody else does —
* otherwise reserving a free-delivery method for an approved group leaves
* it on the checkout for every other customer, which is what a shop
* notices as lost shipping revenue rather than as a bug. A plain "show"
* rule is a whitelist for its own group and reserves nothing: 1.6.0 made
* "show" do both, and a group that needed to show ordinary methods next to
* a reserved one took those ordinary methods away from everybody else.
*
* @return string[] Rate ids, e.g. "free_shipping:15".
*/
@@ -521,7 +551,7 @@ class WC_EDM_DB {
global $wpdb;
$table = self::table( 'shipping_rules' );
$ids = $wpdb->get_col( "SELECT DISTINCT shipping_method_id FROM {$table} WHERE action = 'show'" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$ids = $wpdb->get_col( "SELECT DISTINCT shipping_method_id FROM {$table} WHERE action = 'only'" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
return is_array( $ids ) ? $ids : array();
}
@@ -538,7 +568,7 @@ class WC_EDM_DB {
$wpdb->delete( self::table( 'shipping_rules' ), array( 'group_id' => $group_id ), array( '%d' ) );
foreach ( $rules as $rule ) {
if ( empty( $rule['shipping_method_id'] ) || ! in_array( $rule['action'], array( 'show', 'hide' ), true ) ) {
if ( empty( $rule['shipping_method_id'] ) || ! in_array( $rule['action'], array( 'show', 'hide', 'only' ), true ) ) {
continue;
}
$wpdb->insert(