diff --git a/changelog.txt b/changelog.txt index f1b6eb24e..17300debe 100644 --- a/changelog.txt +++ b/changelog.txt @@ -2,6 +2,7 @@ = 1.25.6 - 202x-xx-xx = * Fix - Refreshes shipping methods after registering or removing carrier accounts. +* Add - Remember state of "Mark this order as complete and notify the customer" and "Notify the customer with shipment details" checkboxes. = 1.25.5 - 2021-01-11 = * Fix - Redux DevTools usage update. diff --git a/classes/class-wc-connect-options.php b/classes/class-wc-connect-options.php index 04d1b3b0d..cf032f501 100644 --- a/classes/class-wc-connect-options.php +++ b/classes/class-wc-connect-options.php @@ -43,6 +43,7 @@ public static function get_option_names( $type = 'compact' ) { 'payment_methods', 'account_settings', 'paper_size', + 'post_print_notification_settings', 'packages', 'predefined_packages', 'shipping_methods_migrated', diff --git a/classes/class-wc-connect-service-settings-store.php b/classes/class-wc-connect-service-settings-store.php index 8fb99b963..7a157a0e0 100644 --- a/classes/class-wc-connect-service-settings-store.php +++ b/classes/class-wc-connect-service-settings-store.php @@ -160,6 +160,67 @@ public function set_preferred_paper_size( $size ) { return WC_Connect_Options::update_option( 'paper_size', $size ); } + /** + * Gets post-label-printing notification settings. + * + * @return array Array of boolean values, indexed by setting names. + */ + public function get_post_print_notification_settings() { + $defaults = array( + 'mark_order_complete_and_notify_customer' => false, + 'notify_customer_with_shipment_details' => false, + ); + + $settings = WC_Connect_Options::get_option( 'post_print_notification_settings', $defaults ); + + return array_merge( $defaults, $settings ); + } + + /** + * Updates post-label-printing notification settings. + * + * @param string $name Name of the setting to enable/disable. + * @param bool $enabled Whether the setting should be enabled. + * + * @return true|WP_Error WP_Error if an error occurred, `true` otherwise. + */ + public function set_post_print_notification_setting( $name, $enabled ) { + $allowed_names = array( + 'mark_order_complete_and_notify_customer', + 'notify_customer_with_shipment_details', + ); + + if ( ! in_array( $name, $allowed_names, true ) ) { + return new WP_Error( + 'invalid_notification_setting_name', + __( 'Invalid notification setting name supplied.', 'woocommerce-services' ) + ); + } + + $old_settings = WC_Connect_Options::get_option( 'post_print_notification_settings' ); + $settings = $old_settings; + $settings[ $name ] = (bool) $enabled; + + /* + * WC_Connect_Options::update_option() returns `false` if the new value is the same as the old one. + * As this is not an issue in this case, we return `true` here and leave the option unchanged. + */ + if ( $settings === $old_settings ) { + return true; + } + + $result = WC_Connect_Options::update_option( 'post_print_notification_settings', $settings ); + + if ( ! $result ) { + return new WP_Error( + 'save_failed', + __( 'Updating the option failed.', 'woocommerce-services' ) + ); + } + + return $result; + } + /** * Attempts to recover faulty json string fields that might contain strings with unescaped quotes * diff --git a/classes/class-wc-connect-shipping-label.php b/classes/class-wc-connect-shipping-label.php index 4d5629a0a..f086c5943 100644 --- a/classes/class-wc-connect-shipping-label.php +++ b/classes/class-wc-connect-shipping-label.php @@ -391,10 +391,14 @@ public function get_label_payload( $post_order_or_id ) { return false; } + $notification_settings = $this->settings_store->get_post_print_notification_settings(); + $order_id = WC_Connect_Compatibility::instance()->get_order_id( $order ); $payload = array( 'orderId' => $order_id, 'paperSize' => $this->settings_store->get_preferred_paper_size(), + 'fulfillOrder' => $notification_settings['mark_order_complete_and_notify_customer'], + 'emailDetails' => $notification_settings['notify_customer_with_shipment_details'], 'formData' => $this->get_form_data( $order ), 'labelsData' => $this->settings_store->get_label_order_meta_data( $order_id ), 'storeOptions' => $this->settings_store->get_store_options(), diff --git a/classes/class-wc-rest-connect-post-print-notification-settings-controller.php b/classes/class-wc-rest-connect-post-print-notification-settings-controller.php new file mode 100644 index 000000000..2389afeed --- /dev/null +++ b/classes/class-wc-rest-connect-post-print-notification-settings-controller.php @@ -0,0 +1,34 @@ +settings_store->set_post_print_notification_setting( $request['name'], $request['enabled'] ); + + if ( is_wp_error( $result ) ) { + $error = new WP_Error( + 'save_failed', + sprintf( + __( 'Unable to update notification setting. %s', 'woocommerce-services' ), + $result->get_error_message() + ), + array( 'status' => 400 ) + ); + + $this->logger->log( $error, __CLASS__ ); + + return $error; + } + + return new WP_REST_Response( array( 'success' => true ), 200 ); + } +} diff --git a/client/extensions/woocommerce/woocommerce-services/api/url.js b/client/extensions/woocommerce/woocommerce-services/api/url.js index 33b4e9df5..60bbd9bae 100644 --- a/client/extensions/woocommerce/woocommerce-services/api/url.js +++ b/client/extensions/woocommerce/woocommerce-services/api/url.js @@ -1,5 +1,6 @@ /** @format */ export const accountSettings = 'connect/account/settings'; +export const postPrintNotificationSettings = 'connect/post-print-notification-settings'; export const packages = 'connect/packages'; export const orderLabels = ( orderId ) => `connect/label/${ orderId }`; export const getLabelRates = ( orderId ) => `connect/label/${ orderId }/rates`; diff --git a/client/extensions/woocommerce/woocommerce-services/lib/initialize-labels-state/index.js b/client/extensions/woocommerce/woocommerce-services/lib/initialize-labels-state/index.js index eb2ecaaf4..701c77227 100644 --- a/client/extensions/woocommerce/woocommerce-services/lib/initialize-labels-state/index.js +++ b/client/extensions/woocommerce/woocommerce-services/lib/initialize-labels-state/index.js @@ -31,12 +31,10 @@ export default data => { loaded: false, isFetching: false, error: false, - fulfillOrder: false, - emailDetails: false, }; } - const { formData, labelsData, paperSize, storeOptions, canChangeCountries } = data; + const { formData, labelsData, paperSize, fulfillOrder, emailDetails, storeOptions, canChangeCountries } = data; //old WCS required a phone number and detected normalization status based on the existence of the phone field //newer versions send the normalized flag const originNormalized = Boolean( formData.origin_normalized || formData.origin.phone ); @@ -74,8 +72,8 @@ export default data => { loaded: true, isFetching: false, error: false, - fulfillOrder: false, - emailDetails: false, + fulfillOrder, + emailDetails, refreshedLabelStatus: false, labels: labelsData || [], paperSize, diff --git a/client/extensions/woocommerce/woocommerce-services/state/shipping-label/actions.js b/client/extensions/woocommerce/woocommerce-services/state/shipping-label/actions.js index 24e855adb..36ab8a5cf 100644 --- a/client/extensions/woocommerce/woocommerce-services/state/shipping-label/actions.js +++ b/client/extensions/woocommerce/woocommerce-services/state/shipping-label/actions.js @@ -44,6 +44,8 @@ import { } from './selectors'; import { createNote } from 'woocommerce/state/sites/orders/notes/actions'; import { saveOrder } from 'woocommerce/state/sites/orders/actions'; +import { getOrder } from 'woocommerce/state/sites/orders/selectors'; +import { isOrderFinished } from 'woocommerce/lib/order-status'; import { getAllPackageDefinitions } from 'woocommerce/woocommerce-services/state/packages/selectors'; import { getEmailReceipts } from 'woocommerce/woocommerce-services/state/label-settings/selectors'; import getAddressValues from 'woocommerce/woocommerce-services/lib/utils/get-address-values'; @@ -729,6 +731,26 @@ export const setFulfillOrderOption = ( orderId, siteId, value ) => { }; }; +export const updatePostPrintNotificationSettings = ( state, orderId, siteId ) => { + const { fulfillOrder, emailDetails } = getShippingLabel( state, orderId, siteId ); + const order = getOrder( state, orderId ); + let data = {}; + + if ( isOrderFinished( order.status ) ) { + data = { + name: 'notify_customer_with_shipment_details', + enabled: emailDetails, + }; + } else { + data = { + name: 'mark_order_complete_and_notify_customer', + enabled: fulfillOrder, + }; + } + + api.post( siteId, api.url.postPrintNotificationSettings, data ); +}; + const purchaseLabelResponse = ( orderId, siteId, response, error ) => { return { type: WOOCOMMERCE_SERVICES_SHIPPING_LABEL_PURCHASE_RESPONSE, @@ -777,7 +799,7 @@ const labelStatusTask = ( orderId, siteId, labelId, retryCount ) => { } ); }; -const handlePrintFinished = ( orderId, siteId, dispatch, getState, hasError, labels ) => { +export const handlePrintFinished = ( orderId, siteId, dispatch, getState, hasError, labels ) => { dispatch( exitPrintingFlow( orderId, siteId, true ) ); dispatch( clearAvailableRates( orderId, siteId ) ); @@ -824,6 +846,8 @@ const handlePrintFinished = ( orderId, siteId, dispatch, getState, hasError, lab } ) ); } + + updatePostPrintNotificationSettings( getState(), orderId, siteId ); }; /** diff --git a/client/extensions/woocommerce/woocommerce-services/state/shipping-label/selectors.js b/client/extensions/woocommerce/woocommerce-services/state/shipping-label/selectors.js index b5bbe256d..6e4965c12 100644 --- a/client/extensions/woocommerce/woocommerce-services/state/shipping-label/selectors.js +++ b/client/extensions/woocommerce/woocommerce-services/state/shipping-label/selectors.js @@ -55,6 +55,8 @@ import { hasStates, } from 'woocommerce/state/sites/data/locations/selectors'; import { isWcsInternationalLabelsEnabled } from 'woocommerce/state/selectors/plugins'; +import { getOrder } from 'woocommerce/state/sites/orders/selectors'; +import { isOrderFinished } from '../../../lib/order-status'; export const getShippingLabel = ( state, orderId, siteId = getSelectedSiteId( state ) ) => { return get( @@ -92,12 +94,14 @@ export const getLabelById = ( state, orderId, siteId, labelId ) => { export const shouldFulfillOrder = ( state, orderId, siteId = getSelectedSiteId( state ) ) => { const shippingLabel = getShippingLabel( state, orderId, siteId ); - return shippingLabel && shippingLabel.fulfillOrder; + const order = getOrder( state, orderId ); + return shippingLabel && shippingLabel.fulfillOrder && order && ! isOrderFinished( order.status ); }; export const shouldEmailDetails = ( state, orderId, siteId = getSelectedSiteId( state ) ) => { const shippingLabel = getShippingLabel( state, orderId, siteId ); - return shippingLabel && shippingLabel.emailDetails; + const order = getOrder( state, orderId ); + return shippingLabel && shippingLabel.emailDetails && order && isOrderFinished( order.status ); }; export const getSelectedPaymentMethod = ( state, orderId, siteId = getSelectedSiteId( state ) ) => { diff --git a/client/extensions/woocommerce/woocommerce-services/state/shipping-label/test/actions.js b/client/extensions/woocommerce/woocommerce-services/state/shipping-label/test/actions.js index f2cdc449b..19cc0fb51 100644 --- a/client/extensions/woocommerce/woocommerce-services/state/shipping-label/test/actions.js +++ b/client/extensions/woocommerce/woocommerce-services/state/shipping-label/test/actions.js @@ -12,6 +12,7 @@ import nock from 'nock'; */ import { openPrintingFlow, + handlePrintFinished, convertToApiPackage, submitAddressForNormalization, confirmAddressSuggestion, @@ -269,6 +270,85 @@ describe( 'Shipping label Actions', () => { nock.cleanAll(); } ); + const createGetStateFnForPostPrintNotificationSettings = ( orderStatus, notificationSettings ) => () => ( { + ui: { + selectedSiteId: siteId, + }, + extensions: { + woocommerce: { + sites: { + [ siteId ]: { + orders: { + items: { + [ orderId ]: { + status: orderStatus, + }, + } + } + }, + }, + woocommerceServices: { + [ siteId ]: { + shippingLabel: { + [ orderId ]: notificationSettings, + }, + }, + }, + }, + }, + } ); + + const mockPostPrintNotificationSettingsUpdateRequest = expectedBody => { + return nock( 'https://public-api.wordpress.com:443' ) + .post( + `/rest/v1.1/jetpack-blogs/${ siteId }/rest-api/`, + body => { + const expectedPath = '/wc/v1/connect/post-print-notification-settings&_via_calypso&_method=post'; + + return expectedPath === body.path && JSON.stringify( expectedBody ) === body.body; + } + ) + .reply( 200, { success: true } ); + }; + + const testPostPrintNotificationSettingsUpdate = ( orderStatus, state, expectedBody ) => { + const getState = createGetStateFnForPostPrintNotificationSettings( orderStatus, state ); + const request = mockPostPrintNotificationSettingsUpdateRequest( expectedBody ); + handlePrintFinished( orderId, siteId, sinon.spy(), getState, false, [] ); + request.done(); + }; + + const testCases = [ + { + orderStatus: 'completed', + state: { name: 'emailDetails', value: true }, + expectedBody: { name: 'notify_customer_with_shipment_details', enabled: true }, + }, + { + orderStatus: 'completed', + state: { name: 'emailDetails', value: false }, + expectedBody: { name: 'notify_customer_with_shipment_details', enabled: false }, + }, + { + orderStatus: 'pending', + state: { name: 'fulfillOrder', value: true }, + expectedBody: { name: 'mark_order_complete_and_notify_customer', enabled: true }, + }, + { + orderStatus: 'pending', + state: { name: 'fulfillOrder', value: false }, + expectedBody: { name: 'mark_order_complete_and_notify_customer', enabled: false }, + }, + ]; + + describe.each( testCases )( '#handlePrintFinished', testCase => { + const { expectedBody, orderStatus, state } = testCase; + + it( `sets "${expectedBody.name}" to ${expectedBody.value}`, () => { + testPostPrintNotificationSettingsUpdate( orderStatus, { [ state.name ]: state.value }, expectedBody ); + } ); + } ); + describe( '#convertToApiPackage', () => { it( 'totals value correctly (by quantity)', () => { const pckg = { diff --git a/client/extensions/woocommerce/woocommerce-services/views/shipping-label/label-purchase-modal/rates-step/test/shipping-rate.js b/client/extensions/woocommerce/woocommerce-services/views/shipping-label/label-purchase-modal/rates-step/test/shipping-rate.js index 745461383..9a9101ca1 100644 --- a/client/extensions/woocommerce/woocommerce-services/views/shipping-label/label-purchase-modal/rates-step/test/shipping-rate.js +++ b/client/extensions/woocommerce/woocommerce-services/views/shipping-label/label-purchase-modal/rates-step/test/shipping-rate.js @@ -8,6 +8,7 @@ import { expect } from 'chai'; import { configure, mount } from 'enzyme'; import Adapter from 'enzyme-adapter-react-16' import { CheckboxControl, RadioControl } from '@wordpress/components'; +import { moment } from 'i18n-calypso'; /** * Internal dependencies @@ -94,7 +95,9 @@ describe( 'ShippingRate', () => { } ); it( 'renders the delivery date', () => { - expect( shippingRateWrapper ).to.contain(