From 0f1e51fc64f1410428d146597da5116773f93c2d Mon Sep 17 00:00:00 2001 From: WooCommerce Date: Wed, 22 May 2024 10:12:34 +0000 Subject: [PATCH] Updates to 2.9.4 --- assets/js/admin.js | 1 + assets/js/admin.min.js | 2 +- assets/js/eu-vat.js | 134 ++++++++-- assets/js/eu-vat.min.js | 2 +- build/frontend.asset.php | 2 +- build/frontend.js | 6 +- changelog.txt | 6 + includes/class-wc-eu-vat-admin.php | 92 ++++++- .../class-wc-eu-vat-extend-store-endpoint.php | 33 ++- includes/class-wc-eu-vat-my-account.php | 76 +++--- includes/class-wc-eu-vat-number.php | 150 +++++++++--- .../class-wc-eu-vat-report-ec-sales-list.php | 40 +-- includes/class-wc-non-eu-sales-report.php | 25 +- includes/data/eu-vat-number-settings.php | 9 +- includes/wc-eu-vat-functions.php | 11 + languages/woocommerce-eu-vat-number.pot | 228 ++++++++++-------- src/components/vat-input/index.js | 46 ++-- src/hooks.js | 1 + woocommerce-eu-vat-number.php | 10 +- 19 files changed, 637 insertions(+), 237 deletions(-) diff --git a/assets/js/admin.js b/assets/js/admin.js index daaa93c..f7292fb 100644 --- a/assets/js/admin.js +++ b/assets/js/admin.js @@ -8,6 +8,7 @@ data._billing_country = $( '#_billing_country' ).val(); data._shipping_country = $( '#_shipping_country' ).val(); data._billing_postcode = $( '#_billing_postcode' ).val(); + data._shipping_postcode = $( '#_shipping_postcode' ).val(); } } ); diff --git a/assets/js/admin.min.js b/assets/js/admin.min.js index e2dea7d..5365a1e 100644 --- a/assets/js/admin.min.js +++ b/assets/js/admin.min.js @@ -1 +1 @@ -!function(_){_(document.body).on("order-totals-recalculate-before",function(i,n){n&&_("#_billing_vat_number").length&&(n._billing_vat_number=_("#_billing_vat_number").val(),n._billing_country=_("#_billing_country").val(),n._shipping_country=_("#_shipping_country").val(),n._billing_postcode=_("#_billing_postcode").val())})}(jQuery),function(i){i(".wc-eu-vat-block-checkout-notice").on("click",".notice-dismiss",function(){i.ajax({url:wc_eu_vat_admin_params.ajax_url,type:"POST",data:{action:"wc_eu_vat_dismiss_checkout_notice",security:wc_eu_vat_admin_params.dismiss_nonce}})})}(jQuery); +!function(n){n(document.body).on("order-totals-recalculate-before",function(i,_){_&&n("#_billing_vat_number").length&&(_._billing_vat_number=n("#_billing_vat_number").val(),_._billing_country=n("#_billing_country").val(),_._shipping_country=n("#_shipping_country").val(),_._billing_postcode=n("#_billing_postcode").val(),_._shipping_postcode=n("#_shipping_postcode").val())})}(jQuery),function(i){i(".wc-eu-vat-block-checkout-notice").on("click",".notice-dismiss",function(){i.ajax({url:wc_eu_vat_admin_params.ajax_url,type:"POST",data:{action:"wc_eu_vat_dismiss_checkout_notice",security:wc_eu_vat_admin_params.dismiss_nonce}})})}(jQuery); diff --git a/assets/js/eu-vat.js b/assets/js/eu-vat.js index 48db22f..11deeee 100644 --- a/assets/js/eu-vat.js +++ b/assets/js/eu-vat.js @@ -1,54 +1,144 @@ -jQuery(function(){ +/* global wc_eu_vat_params, wc_address_i18n_params */ +/* eslint-disable camelcase */ +jQuery( function () { + const useShippingCountry = wc_eu_vat_params.use_shipping_country; + const billingVatNumber = '#woocommerce_eu_vat_number_field'; + const shippingVatNumber = '#woocommerce_eu_vat_number_shipping_field'; + function field_is_required( field, is_required ) { if ( is_required ) { field.find( 'label .optional' ).remove(); field.addClass( 'validate-required' ); if ( field.find( 'label .required' ).length === 0 ) { - field.find( 'label' ).append( - ' *' - ); + field + .find( 'label' ) + .append( + '*' + ); } } else { field.find( 'label .required' ).remove(); - field.removeClass( 'validate-required woocommerce-invalid woocommerce-invalid-required-field' ); + field.removeClass( + 'validate-required woocommerce-invalid woocommerce-invalid-required-field' + ); if ( field.find( 'label .optional' ).length === 0 ) { - field.find( 'label' ).append( ' (' + wc_address_i18n_params.i18n_optional_text + ')' ); + field + .find( 'label' ) + .append( + '(' + + wc_address_i18n_params.i18n_optional_text + + ')' + ); } } } - jQuery( 'form.checkout, form#order_review').on( 'change', '#billing_country', function() { - var country = jQuery( '#billing_country' ).val(); - var check_countries = wc_eu_vat_params.eu_countries; - var b2b_enabled = wc_eu_vat_params.b2b_required; + // Handle change billing/shipping country. + function changeCountry() { + const billingCountry = jQuery( '#billing_country' ).val(); + const shippingCountry = jQuery( '#shipping_country' ).val(); + const shipToDifferent = jQuery( + '#ship-to-different-address-checkbox' + ).is( ':checked' ); + const validateShippingCountry = + useShippingCountry && shippingCountry && shipToDifferent; + const country = validateShippingCountry + ? shippingCountry + : billingCountry; + const check_countries = wc_eu_vat_params.eu_countries; + const b2b_enabled = wc_eu_vat_params.b2b_required; + + const vat_number_field = validateShippingCountry + ? jQuery( shippingVatNumber ) + : jQuery( billingVatNumber ); - field_is_required( jQuery( '#woocommerce_eu_vat_number_field' ), false ); + field_is_required( vat_number_field, false ); if ( country && jQuery.inArray( country, check_countries ) >= 0 ) { - jQuery( '#woocommerce_eu_vat_number_field' ).fadeIn(); - if ( 'yes' === b2b_enabled ) { - field_is_required( jQuery( '#woocommerce_eu_vat_number_field' ), true ); + vat_number_field.fadeIn(); + if ( b2b_enabled === 'yes' ) { + field_is_required( vat_number_field, true ); } } else { - jQuery( '#woocommerce_eu_vat_number_field' ).fadeOut(); + vat_number_field.fadeOut(); } - }); + } + + jQuery( 'form.checkout, form#order_review' ).on( + 'change', + '#billing_country', + changeCountry + ); jQuery( '#billing_country' ).trigger( 'change' ); + if ( useShippingCountry ) { + jQuery( 'form.checkout, form#order_review' ).on( + 'change', + '#shipping_country', + changeCountry + ); + jQuery( '#shipping_country' ).trigger( 'change' ); + } + + // Trigger country change on ship to different address checkbox change. + jQuery( 'form.checkout, form#order_review' ).on( + 'change', + '#ship-to-different-address-checkbox', + function () { + if ( ! useShippingCountry ) { + return; + } + + const isChecked = jQuery( + '#ship-to-different-address-checkbox' + ).is( ':checked' ); + + if ( isChecked ) { + jQuery( billingVatNumber ).fadeOut(); + jQuery( '#shipping_country' ).trigger( 'change' ); + } else { + jQuery( shippingVatNumber ).fadeOut(); + jQuery( '#billing_country' ).trigger( 'change' ); + } + } + ); + jQuery( '#ship-to-different-address-checkbox' ).trigger( 'change' ); + /* Validate EU VAT Number field only on change event */ - jQuery( 'form.checkout, form#order_review' ).on( 'change', '#woocommerce_eu_vat_number', function() { - jQuery( 'body' ).trigger( 'update_checkout' ); - } ); + jQuery( 'form.checkout, form#order_review' ).on( + 'change', + billingVatNumber, + function () { + jQuery( 'body' ).trigger( 'update_checkout' ); + } + ); + + if ( useShippingCountry ) { + jQuery( 'form.checkout, form#order_review' ).on( + 'change', + shippingVatNumber, + function () { + jQuery( 'body' ).trigger( 'update_checkout' ); + } + ); + } /** * Handles checkout field UI when VAT field validation fails. */ jQuery( document.body ).on( 'updated_checkout', function( e, data ) { - $vat_field = jQuery( '#woocommerce_eu_vat_number' ); + const shippingCountry = jQuery( '#shipping_country' ).val(); + const shipToDifferent = jQuery( + '#ship-to-different-address-checkbox' + ).is( ':checked' ); + const $vat_field = + useShippingCountry && shippingCountry && shipToDifferent + ? jQuery( shippingVatNumber ) + : jQuery( billingVatNumber ); if ( ! $vat_field.is( ':visible' ) ) { return; diff --git a/assets/js/eu-vat.min.js b/assets/js/eu-vat.min.js index 6bf2c95..9952263 100644 --- a/assets/js/eu-vat.min.js +++ b/assets/js/eu-vat.min.js @@ -1 +1 @@ -jQuery(function(){function o(e,r){r?(e.find("label .optional").remove(),e.addClass("validate-required"),0===e.find("label .required").length&&e.find("label").append(' *')):(e.find("label .required").remove(),e.removeClass("validate-required woocommerce-invalid woocommerce-invalid-required-field"),0===e.find("label .optional").length&&e.find("label").append(' ('+wc_address_i18n_params.i18n_optional_text+")"))}jQuery("form.checkout, form#order_review").on("change","#billing_country",function(){var e=jQuery("#billing_country").val(),r=wc_eu_vat_params.eu_countries,a=wc_eu_vat_params.b2b_required;o(jQuery("#woocommerce_eu_vat_number_field"),!1),e&&0<=jQuery.inArray(e,r)?(jQuery("#woocommerce_eu_vat_number_field").fadeIn(),"yes"===a&&o(jQuery("#woocommerce_eu_vat_number_field"),!0)):jQuery("#woocommerce_eu_vat_number_field").fadeOut()}),jQuery("#billing_country").trigger("change"),jQuery("form.checkout, form#order_review").on("change","#woocommerce_eu_vat_number",function(){jQuery("body").trigger("update_checkout")}),jQuery(document.body).on("updated_checkout",function(e,r){($vat_field=jQuery("#woocommerce_eu_vat_number")).is(":visible")&&($vat_code=$vat_field.val(),$vat_field_wrapper=$vat_field.closest(".form-row"),"success"===r.result?$vat_code.length||$vat_field_wrapper.removeClass("woocommerce-validated"):r.messages.length&&r.messages.includes($vat_code.toUpperCase())&&($vat_field_wrapper.removeClass("woocommerce-validated"),$vat_field_wrapper.addClass("woocommerce-invalid")))})}); +jQuery(function(){const a=wc_eu_vat_params.use_shipping_country,c="#woocommerce_eu_vat_number_field",n="#woocommerce_eu_vat_number_shipping_field";function t(e,r){r?(e.find("label .optional").remove(),e.addClass("validate-required"),0===e.find("label .required").length&&e.find("label").append('*')):(e.find("label .required").remove(),e.removeClass("validate-required woocommerce-invalid woocommerce-invalid-required-field"),0===e.find("label .optional").length&&e.find("label").append('('+wc_address_i18n_params.i18n_optional_text+")"))}function e(){var e=jQuery("#billing_country").val(),r=jQuery("#shipping_country").val(),o=jQuery("#ship-to-different-address-checkbox").is(":checked"),o=a&&r&&o,r=o?r:e,e=wc_eu_vat_params.eu_countries,i=wc_eu_vat_params.b2b_required,o=o?jQuery(n):jQuery(c);t(o,!1),r&&0<=jQuery.inArray(r,e)?(o.fadeIn(),"yes"===i&&t(o,!0)):o.fadeOut()}jQuery("form.checkout, form#order_review").on("change","#billing_country",e),jQuery("#billing_country").trigger("change"),a&&(jQuery("form.checkout, form#order_review").on("change","#shipping_country",e),jQuery("#shipping_country").trigger("change")),jQuery("form.checkout, form#order_review").on("change","#ship-to-different-address-checkbox",function(){a&&(jQuery("#ship-to-different-address-checkbox").is(":checked")?(jQuery(c).fadeOut(),jQuery("#shipping_country")):(jQuery(n).fadeOut(),jQuery("#billing_country"))).trigger("change")}),jQuery("#ship-to-different-address-checkbox").trigger("change"),jQuery("form.checkout, form#order_review").on("change",c,function(){jQuery("body").trigger("update_checkout")}),a&&jQuery("form.checkout, form#order_review").on("change",n,function(){jQuery("body").trigger("update_checkout")}),jQuery(document.body).on("updated_checkout",function(e,r){var o=jQuery("#shipping_country").val(),i=jQuery("#ship-to-different-address-checkbox").is(":checked"),o=a&&o&&i?jQuery(n):jQuery(c);o.is(":visible")&&($vat_code=o.val(),$vat_field_wrapper=o.closest(".form-row"),"success"===r.result?$vat_code.length||$vat_field_wrapper.removeClass("woocommerce-validated"):r.messages.length&&r.messages.includes($vat_code.toUpperCase())&&($vat_field_wrapper.removeClass("woocommerce-validated"),$vat_field_wrapper.addClass("woocommerce-invalid")))})}); diff --git a/build/frontend.asset.php b/build/frontend.asset.php index 2ae4141..4c28052 100644 --- a/build/frontend.asset.php +++ b/build/frontend.asset.php @@ -1 +1 @@ - array('wc-blocks-checkout', 'wc-settings', 'wp-compose', 'wp-data', 'wp-element', 'wp-i18n'), 'version' => 'e3d7d07c67aecd20c69de0d94c963346'); \ No newline at end of file + array('wc-blocks-checkout', 'wc-settings', 'wp-compose', 'wp-data', 'wp-element', 'wp-i18n'), 'version' => '16638e3a3498458e80c2f3e08daa4d3e'); \ No newline at end of file diff --git a/build/frontend.js b/build/frontend.js index 5dac85d..914fa83 100644 --- a/build/frontend.js +++ b/build/frontend.js @@ -1,5 +1,5 @@ -(window.webpackJsonp_woocommerce_eu_vat_number=window.webpackJsonp_woocommerce_eu_vat_number||[]).push([[2],{20:function(e,t,n){}}]),function(e){function t(t){for(var r,a,i=t[0],u=t[1],s=t[2],d=0,m=[];de.length)&&(t=e.length);for(var n=0,r=new Array(t);ne.length)&&(t=e.length);for(var n=0,r=new Array(t);n%s.","woocommerce-eu-vat-number"),b),{strong:Object(u.createElement)("strong",null)}),checked:y,onChange:function(e){S||A(!0),x(e),m("woocommerce-eu-vat-number","location_confirmation",e)}})),null==C||!C.message||null!=C&&C.hidden?null:Object(u.createElement)("div",{className:"wc-eu-vat-checkout-ip-notice__error wc-block-components-validation-error"},C.message))},O=(n(20),n(5)),g=function(e){var t=e.title,n=e.stepHeadingContent;return Object(u.createElement)("div",{className:"wc-block-components-checkout-step__heading"},Object(u.createElement)("h2",{"aria-hidden":"true",className:"wc-block-components-checkout-step__title wc-block-components-title"},t),!!n&&Object(u.createElement)("span",{className:"wc-block-components-checkout-step__heading-content"},n))},j=(r=Object(O.a)({}),function(e){return function(t){var n=function(e,t){var n=[];return Object.keys(e).forEach((function(r){if(void 0!==t[r])switch(e[r].type){case"boolean":n[r]="false"!==t[r]&&!1!==t[r];break;case"number":n[r]=parseInt(t[r],10);break;case"array":case"object":n[r]=JSON.parse(t[r]);break;default:n[r]=t[r]}else n[r]=e[r].default})),n}(r,t);return Object(u.createElement)(e,c()({},t,n))}})((function(e){var t=e.id,n=e.className,r=e.title,o=e.legend,c=e.description,a=e.children,i=e.disabled,s=void 0!==i&&i,l=e.showStepNumber,d=void 0===l||l,m=e.stepHeadingContent,p=void 0===m?function(){}:m,b=o||r?"fieldset":"div";return Object(u.createElement)(b,{className:h()(n,"wc-block-components-checkout-step",{"wc-block-components-checkout-step--with-step-number":d,"wc-block-components-checkout-step--disabled":s}),id:t,disabled:s},!(!o&&!r)&&Object(u.createElement)("legend",{className:"screen-reader-text"},o||r),!!r&&Object(u.createElement)(g,{title:r,stepHeadingContent:p()}),Object(u.createElement)("div",{className:"wc-block-components-checkout-step__container"},!!c&&Object(u.createElement)("p",{className:"wc-block-components-checkout-step__description"},c),Object(u.createElement)("div",{className:"wc-block-components-checkout-step__content"},a)))})),y=Object(b.withInstanceId)((function(e){var t,n,r,o,c=e.validation,a=c.setValidationErrors,d=c.clearValidationError,m=c.getValidationError,b=e.checkoutExtensionData,v=Object(_.useSelect)((function(e){var t=e("wc/store/cart").getCartData();return{cartItems:t.items,billingAddress:t.billingAddress,extensions:t.extensions,needsShipping:t.needsShipping}})),O=v.billingAddress,g=v.extensions,y=wc_eu_vat_params,x=y.b2b_required,E=y.eu_countries,k=y.uk_ni_notice,S=y.input_label,A=y.input_description,N=y.failure_handler,P=e.title,T=void 0===P?Object(l.__)("VAT Number","woocommerce-eu-vat-number"):P,C=e.description,V=void 0===C?"":C,I=e.showStepNumber,D=void 0===I||I,M=Object(f.getSetting)("woocommerce-eu-vat-number_data"),B=M.woocommerce_eu_vat_number_validate_ip,U=M.ip_address,Z=M.ip_country,q="yes"===B,H=Object(u.useState)(!1),J=p()(H,2),L=J[0],F=J[1],R=Object(u.useState)(null===(t=g["woocommerce-eu-vat-number"])||void 0===t?void 0:t.vat_number),G=p()(R,2),$=G[0],K=G[1],Y=Object(u.useState)(null===(n=g["woocommerce-eu-vat-number"])||void 0===n?void 0:n.vat_number),X=p()(Y,2),z=X[0],Q=X[1],W=Object(u.useState)("yes"===x&&-1!==E.indexOf(O.country)),ee=p()(W,1)[0],te=Object(u.useState)(-1!==E.indexOf(O.country)),ne=p()(te,2),re=ne[0],oe=ne[1],ce=Object(u.useState)("GB"===O.country),ae=p()(ce,2),ie=ae[0],ue=ae[1],se="billing_vat_number_error",le=m(se),de=!1===(null==le?void 0:le.hidden)&&""!==(null==le?void 0:le.message);return Object(u.useEffect)((function(){var e;K(null===(e=g["woocommerce-eu-vat-number"])||void 0===e?void 0:e.vat_number)}),[null===(r=g["woocommerce-eu-vat-number"])||void 0===r?void 0:r.vat_number]),Object(u.useEffect)((function(){b.setExtensionData("woocommerce-eu-vat-number","location_confirmation",!1)}),[b.setExtensionData]),Object(u.useEffect)((function(){oe(-1!==E.indexOf(O.country)),ue("GB"===O.country)}),[E,O.country]),Object(u.useEffect)((function(){!ee||"string"==typeof $&&$.length>0||a(i()({},se,{message:Object(l.__)("VAT number is required.","woocommerce-eu-vat-number"),hidden:!0}))}),[ee,a,se]),Object(u.useEffect)((function(){var e,t,n,r=m(se);!$&&null!=r&&r.hidden||((ee||$)&&null!==(e=g["woocommerce-eu-vat-number"])&&void 0!==e&&null!==(t=e.validation)&&void 0!==t&&t.error?a(i()({},se,{message:null===(n=g["woocommerce-eu-vat-number"])||void 0===n?void 0:n.validation.error,hidden:!1})):d(se))}),[null===(o=g["woocommerce-eu-vat-number"])||void 0===o?void 0:o.validation.error]),Object(u.useEffect)((function(){var e,t,n,r,o,c,u;"string"==typeof $&&$.length>0&&(o=O.country,c=$,(!(u={AT:"U[A-Z\\d]{8}",BE:"0\\d{9}",BG:"\\d{9,10}",CY:"\\d{8}[A-Z]",CZ:"\\d{8,10}",DE:"\\d{9}",DK:"(\\d{2} ?){3}\\d{2}",EE:"\\d{9}",EL:"\\d{9}",ES:"[A-Z]\\d{7}[A-Z]|\\d{8}[A-Z]|[A-Z]\\d{8}",FI:"\\d{8}",FR:"([A-Z]{2}|[A-Z0-9]{2})\\d{9}",XI:"\\d{9}|\\d{12}|(GD|HA)\\d{3}",HR:"\\d{11}",HU:"\\d{8}",IE:"[A-Z\\d]{8,10}",IT:"\\d{11}",LT:"(\\d{9}|\\d{12})",LU:"\\d{8}",LV:"\\d{11}",MT:"\\d{8}",NL:"\\d{9}B\\d{2}",PL:"\\d{10}",PT:"\\d{9}",RO:"\\d{2,10}",SE:"\\d{12}",SI:"\\d{8}",SK:"\\d{10}"})[o]||null===new RegExp(u[o]).exec(c))&&"reject"===N||(null===(e=g["woocommerce-eu-vat-number"])||void 0===e||null===(t=e.validation)||void 0===t||!t.valid)&&$===(null===(n=g["woocommerce-eu-vat-number"])||void 0===n?void 0:n.vat_number)&&"reject"===N)&&a(i()({},se,{message:null===(r=g["woocommerce-eu-vat-number"])||void 0===r?void 0:r.validation.error,hidden:!1}))}),[]),re?Object(u.createElement)(j,{id:"shipping-fields",className:h()("wc-block-checkout__shipping-fields","eu-vat-extra-css"),title:T,description:V,showStepNumber:D},Object(u.createElement)("div",null,Object(u.createElement)("div",{className:h()("wc-block-components-text-input","eu-vat-extra-css",{"is-active":L||!!$},{"has-error":de})},Object(u.createElement)("input",{type:"text","aria-label":S,id:"billing_vat_number",value:$||"",onChange:function(e){!function(e){var t=e.target.value;d(se),"string"==typeof t&&0===t.length&&ee&&a(i()({},se,{message:Object(l.__)("VAT Number is required.","woocommerce-eu-vat-number"),hidden:!1})),K(t)}(e)},onFocus:function(){return F(!0)},onBlur:function(){F(!1),$!==z&&(d(se),ee&&(null==$||"string"==typeof $&&0===$.length)&&a(i()({},se,{message:Object(l.__)("VAT number is required.","woocommerce-eu-vat-number"),hidden:!1})),Object(s.extensionCartUpdate)({namespace:"woocommerce-eu-vat-number",data:{vat_number:$},cartPropsToReceive:["extensions"]}).then((function(){Q($),ee||$||d(se)})))},"aria-invalid":!0===de,disabled:!1,required:ee}),Object(u.createElement)("label",{htmlFor:"billing_vat_number"},S,!0===ee?null:" (optional)"),Object(u.createElement)((function(){var e;return de?Object(u.createElement)("div",{className:"wc-block-components-validation-error",role:"alert"},Object(u.createElement)("p",{id:se},null===(e=m(se))||void 0===e?void 0:e.message)):null}),null),Object(u.createElement)("div",{className:"wc-eu-vat-checkout-uk-notice"},Object(u.createElement)("div",null,Object(u.createElement)("span",null,A)),Object(u.createElement)("span",null,ie?k:null))),!$&&"no"===x||!$&&!x?Object(u.createElement)(w,{validation:e.validation,ipAddress:U,ipCountry:Z,billingCountry:O.country,shouldValidateIp:q,checkoutExtensionData:b}):null)):Object(u.createElement)(u.Fragment,null)}));function x(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function E(e){for(var t=1;t%s.","woocommerce-eu-vat-number"),b),{strong:Object(u.createElement)("strong",null)}),checked:y,onChange:function(e){S||A(!0),x(e),p("woocommerce-eu-vat-number","location_confirmation",e)}})),null==C||!C.message||null!=C&&C.hidden?null:Object(u.createElement)("div",{className:"wc-eu-vat-checkout-ip-notice__error wc-block-components-validation-error"},C.message))},O=(n(20),n(5)),g=function(e){var t=e.title,n=e.stepHeadingContent;return Object(u.createElement)("div",{className:"wc-block-components-checkout-step__heading"},Object(u.createElement)("h2",{"aria-hidden":"true",className:"wc-block-components-checkout-step__title wc-block-components-title"},t),!!n&&Object(u.createElement)("span",{className:"wc-block-components-checkout-step__heading-content"},n))},j=(r=Object(O.a)({}),function(e){return function(t){var n=function(e,t){var n=[];return Object.keys(e).forEach((function(r){if(void 0!==t[r])switch(e[r].type){case"boolean":n[r]="false"!==t[r]&&!1!==t[r];break;case"number":n[r]=parseInt(t[r],10);break;case"array":case"object":n[r]=JSON.parse(t[r]);break;default:n[r]=t[r]}else n[r]=e[r].default})),n}(r,t);return Object(u.createElement)(e,c()({},t,n))}})((function(e){var t=e.id,n=e.className,r=e.title,o=e.legend,c=e.description,i=e.children,a=e.disabled,s=void 0!==a&&a,l=e.showStepNumber,d=void 0===l||l,p=e.stepHeadingContent,m=void 0===p?function(){}:p,b=o||r?"fieldset":"div";return Object(u.createElement)(b,{className:h()(n,"wc-block-components-checkout-step",{"wc-block-components-checkout-step--with-step-number":d,"wc-block-components-checkout-step--disabled":s}),id:t,disabled:s},!(!o&&!r)&&Object(u.createElement)("legend",{className:"screen-reader-text"},o||r),!!r&&Object(u.createElement)(g,{title:r,stepHeadingContent:m()}),Object(u.createElement)("div",{className:"wc-block-components-checkout-step__container"},!!c&&Object(u.createElement)("p",{className:"wc-block-components-checkout-step__description"},c),Object(u.createElement)("div",{className:"wc-block-components-checkout-step__content"},i)))})),y=Object(b.withInstanceId)((function(e){var t,n,r,o,c=e.validation,i=c.setValidationErrors,d=c.clearValidationError,p=c.getValidationError,b=e.checkoutExtensionData,v=Object(_.useSelect)((function(e){var t=e("wc/store/cart").getCartData();return{cartItems:t.items,billingAddress:t.billingAddress,shippingAddress:t.shippingAddress,extensions:t.extensions,needsShipping:t.needsShipping}})),O=v.billingAddress,g=v.shippingAddress,y=v.needsShipping,x=v.extensions,E=window.wc_eu_vat_params,k=E.b2b_required,S=E.eu_countries,A=E.uk_ni_notice,N=E.input_label,P=E.input_description,T=E.failure_handler,C=E.use_shipping_country&&y?g.country:O.country,V=e.title,I=void 0===V?Object(l.__)("VAT Number","woocommerce-eu-vat-number"):V,D=e.description,M=void 0===D?"":D,B=e.showStepNumber,U=void 0===B||B,Z=Object(f.getSetting)("woocommerce-eu-vat-number_data"),q=Z.woocommerce_eu_vat_number_validate_ip,H=Z.ip_address,J=Z.ip_country,L="yes"===q,F=Object(u.useState)(!1),R=m()(F,2),G=R[0],$=R[1],K=Object(u.useState)(null===(t=x["woocommerce-eu-vat-number"])||void 0===t?void 0:t.vat_number),Y=m()(K,2),X=Y[0],z=Y[1],Q=Object(u.useState)(null===(n=x["woocommerce-eu-vat-number"])||void 0===n?void 0:n.vat_number),W=m()(Q,2),ee=W[0],te=W[1],ne=Object(u.useState)("yes"===k&&-1!==S.indexOf(C)),re=m()(ne,2),oe=re[0],ce=re[1],ie=Object(u.useState)(-1!==S.indexOf(C)),ae=m()(ie,2),ue=ae[0],se=ae[1],le=Object(u.useState)("GB"===C),de=m()(le,2),pe=de[0],me=de[1],be="billing_vat_number_error",fe=p(be),ve=!1===(null==fe?void 0:fe.hidden)&&""!==(null==fe?void 0:fe.message);return Object(u.useEffect)((function(){var e;z(null===(e=x["woocommerce-eu-vat-number"])||void 0===e?void 0:e.vat_number)}),[null===(r=x["woocommerce-eu-vat-number"])||void 0===r?void 0:r.vat_number]),Object(u.useEffect)((function(){ce("yes"===k&&-1!==S.indexOf(C))}),[k,C,S]),Object(u.useEffect)((function(){b.setExtensionData("woocommerce-eu-vat-number","location_confirmation",!1)}),[b.setExtensionData]),Object(u.useEffect)((function(){se(-1!==S.indexOf(C)),me("GB"===C)}),[S,C]),Object(u.useEffect)((function(){!oe||"string"==typeof X&&X.length>0||i(a()({},be,{message:Object(l.__)("VAT number is required.","woocommerce-eu-vat-number"),hidden:!0}))}),[oe,i,be,X]),Object(u.useEffect)((function(){var e,t,n,r=p(be);!X&&null!=r&&r.hidden||((oe||X)&&null!==(e=x["woocommerce-eu-vat-number"])&&void 0!==e&&null!==(t=e.validation)&&void 0!==t&&t.error?i(a()({},be,{message:null===(n=x["woocommerce-eu-vat-number"])||void 0===n?void 0:n.validation.error,hidden:!1})):d(be))}),[null===(o=x["woocommerce-eu-vat-number"])||void 0===o?void 0:o.validation.error]),Object(u.useEffect)((function(){var e,t,n,r;"string"==typeof X&&X.length>0&&(!function(e,t){var n={AT:"U[A-Z\\d]{8}",BE:"0\\d{9}",BG:"\\d{9,10}",CY:"\\d{8}[A-Z]",CZ:"\\d{8,10}",DE:"\\d{9}",DK:"(\\d{2} ?){3}\\d{2}",EE:"\\d{9}",EL:"\\d{9}",ES:"[A-Z]\\d{7}[A-Z]|\\d{8}[A-Z]|[A-Z]\\d{8}",FI:"\\d{8}",FR:"([A-Z]{2}|[A-Z0-9]{2})\\d{9}",XI:"\\d{9}|\\d{12}|(GD|HA)\\d{3}",HR:"\\d{11}",HU:"\\d{8}",IE:"[A-Z\\d]{8,10}",IT:"\\d{11}",LT:"(\\d{9}|\\d{12})",LU:"\\d{8}",LV:"\\d{11}",MT:"\\d{8}",NL:"\\d{9}B\\d{2}",PL:"\\d{10}",PT:"\\d{9}",RO:"\\d{2,10}",SE:"\\d{12}",SI:"\\d{8}",SK:"\\d{10}"};return!!n[e]&&null!==new RegExp(n[e]).exec(t)}(C,X)&&"reject"===T||(null===(e=x["woocommerce-eu-vat-number"])||void 0===e||null===(t=e.validation)||void 0===t||!t.valid)&&X===(null===(n=x["woocommerce-eu-vat-number"])||void 0===n?void 0:n.vat_number)&&"reject"===T)&&i(a()({},be,{message:null===(r=x["woocommerce-eu-vat-number"])||void 0===r?void 0:r.validation.error,hidden:!1}))}),[]),ue?Object(u.createElement)(j,{id:"shipping-fields",className:h()("wc-block-checkout__shipping-fields","eu-vat-extra-css"),title:I,description:M,showStepNumber:U},Object(u.createElement)("div",null,Object(u.createElement)("div",{className:h()("wc-block-components-text-input","eu-vat-extra-css",{"is-active":G||!!X},{"has-error":ve})},Object(u.createElement)("input",{type:"text","aria-label":N,id:"billing_vat_number",value:X||"",onChange:function(e){!function(e){var t=e.target.value;d(be),"string"==typeof t&&0===t.length&&oe&&i(a()({},be,{message:Object(l.__)("VAT Number is required.","woocommerce-eu-vat-number"),hidden:!1})),z(t)}(e)},onFocus:function(){return $(!0)},onBlur:function(){$(!1),X!==ee&&(d(be),oe&&(null==X||"string"==typeof X&&0===X.length)&&i(a()({},be,{message:Object(l.__)("VAT number is required.","woocommerce-eu-vat-number"),hidden:!1})),Object(s.extensionCartUpdate)({namespace:"woocommerce-eu-vat-number",data:{vat_number:X},cartPropsToReceive:["extensions"]}).then((function(){te(X),oe||X||d(be)})))},"aria-invalid":!0===ve,disabled:!1,required:oe}),Object(u.createElement)("label",{htmlFor:"billing_vat_number"},N,!0===oe?null:" (optional)"),Object(u.createElement)((function(){var e;return ve?Object(u.createElement)("div",{className:"wc-block-components-validation-error",role:"alert"},Object(u.createElement)("p",{id:be},null===(e=p(be))||void 0===e?void 0:e.message)):null}),null),Object(u.createElement)("div",{className:"wc-eu-vat-checkout-uk-notice"},Object(u.createElement)("div",null,Object(u.createElement)("span",null,P)),Object(u.createElement)("span",null,pe?A:null))),!X&&"no"===k||!X&&!k?Object(u.createElement)(w,{validation:e.validation,ipAddress:H,ipCountry:J,billingCountry:O.country,shouldValidateIp:L,checkoutExtensionData:b}):null)):Object(u.createElement)(u.Fragment,null)}));function x(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function E(e){for(var t=1;thas_billing_address() && ! $theorder->has_shipping_address() ) { + return self::add_vat_number_field( $fields ); + } + + return $fields; + } + + /** + * Add VAT Number fields to admin. This also handles save. + * + * @param array $fields Fields being shown in admin. + * @return array + */ + public static function admin_shipping_fields( $fields ) { + global $theorder; + + if ( wc_eu_vat_use_shipping_country() ) { + if ( $theorder && $theorder->has_billing_address() && ! $theorder->has_shipping_address() ) { + return $fields; + } + + return self::add_vat_number_field( $fields ); + } + + return $fields; + } + + /** + * Get VAT Number from order for shipping address edit form. + * + * @param string $value The VAT Number value in DB. + * @param WC_Order $order The order object. + * @return string + */ + public static function get_vat_number_from_order( $value, $order ) { + $value = is_object( $order ) ? wc_eu_vat_get_vat_from_order( $order ) : $value; + return $value; + } + + /** + * Add VAT Number fields to admin. This also handles save. + * + * @param array $fields Fields being shown in admin. + * @return array + */ + public static function add_vat_number_field( $fields ) { + global $theorder; $vat_number = is_object( $theorder ) ? wc_eu_vat_get_vat_from_order( $theorder ) : ''; @@ -125,7 +176,11 @@ public static function styles_and_scripts( $hook ) { * @return boolean */ protected static function is_eu_order( $order ) { - return in_array( $order->get_billing_country(), WC_EU_VAT_Number::get_eu_countries(), true ); + $country = $order->get_billing_country(); + if ( $order && $order->has_shipping_address() && $order->get_shipping_country() && wc_eu_vat_use_shipping_country() ) { + $country = $order->get_shipping_country(); + } + return in_array( $country, WC_EU_VAT_Number::get_eu_countries(), true ); } /** @@ -331,9 +386,10 @@ public static function admin_order( $args, $order ) { * address form (adding new order). If it is not * found, get it from the order (editing the order). */ - $billing_country = isset( $_POST['_billing_country'] ) ? wc_clean( wp_unslash( $_POST['_billing_country'] ) ) : $order->get_billing_country(); // phpcs:ignore WordPress.Security.NonceVerification.Missing - $shipping_country = isset( $_POST['_shipping_country'] ) ? wc_clean( wp_unslash( $_POST['_shipping_country'] ) ) : $order->get_shipping_country(); // phpcs:ignore WordPress.Security.NonceVerification.Missing - $billing_postcode = isset( $_POST['_billing_postcode'] ) ? wc_clean( wp_unslash( $_POST['_billing_postcode'] ) ) : $order->get_billing_postcode(); // phpcs:ignore WordPress.Security.NonceVerification.Missing + $billing_country = isset( $_POST['_billing_country'] ) ? wc_clean( wp_unslash( $_POST['_billing_country'] ) ) : $order->get_billing_country(); // phpcs:ignore WordPress.Security.NonceVerification.Missing + $shipping_country = isset( $_POST['_shipping_country'] ) ? wc_clean( wp_unslash( $_POST['_shipping_country'] ) ) : $order->get_shipping_country(); // phpcs:ignore WordPress.Security.NonceVerification.Missing + $billing_postcode = isset( $_POST['_billing_postcode'] ) ? wc_clean( wp_unslash( $_POST['_billing_postcode'] ) ) : $order->get_billing_postcode(); // phpcs:ignore WordPress.Security.NonceVerification.Missing + $shipping_postcode = isset( $_POST['_shipping_postcode'] ) ? wc_clean( wp_unslash( $_POST['_shipping_postcode'] ) ) : $order->get_shipping_postcode(); // phpcs:ignore WordPress.Security.NonceVerification.Missing /* * First try and get the VAT number from the @@ -342,12 +398,21 @@ public static function admin_order( $args, $order ) { */ $vat_number = isset( $_POST['_billing_vat_number'] ) ? wc_clean( wp_unslash( $_POST['_billing_vat_number'] ) ) : wc_eu_vat_get_vat_from_order( $order ); // phpcs:ignore WordPress.Security.NonceVerification.Missing + $country = $billing_country; + $postcode = $billing_postcode; + $type = __( 'billing', 'woocommerce-eu-vat-number' ); + if ( $order->has_shipping_address() && wc_eu_vat_use_shipping_country() ) { + $country = $shipping_country; + $postcode = $shipping_postcode; + $type = __( 'shipping', 'woocommerce-eu-vat-number' ); + } + // Ignore empty VAT Number and countries outside EU. - if ( empty( $vat_number ) || ! in_array( $billing_country, WC_EU_VAT_Number::get_eu_countries(), true ) ) { + if ( empty( $vat_number ) || ! in_array( $country, WC_EU_VAT_Number::get_eu_countries(), true ) ) { return; } - $valid = WC_EU_VAT_Number::vat_number_is_valid( $vat_number, $billing_country, $billing_postcode ); + $valid = WC_EU_VAT_Number::vat_number_is_valid( $vat_number, $country, $postcode ); $base_country_match = WC_EU_VAT_Number::is_base_country_match( $billing_country, $shipping_country ); if ( 'no' === get_option( 'woocommerce_eu_vat_number_deduct_in_base', 'yes' ) && $base_country_match ) { @@ -374,8 +439,8 @@ public static function admin_order( $args, $order ) { } if ( ! $valid ) { - // translators: %1$s VAT number field label, %2$s VAT number, %3$s Billing Country. - throw new Exception( sprintf( esc_html__( 'You have entered an invalid %1$s (%2$s) for your billing country (%3$s).', 'woocommerce-eu-vat-number' ), get_option( 'woocommerce_eu_vat_number_field_label', __( 'VAT number', 'woocommerce-eu-vat-number' ) ), $vat_number, $billing_country ) ); + // translators: %1$s VAT number field label, %2$s VAT number, %3$s Country %4$s country type: billing/shipping. + throw new Exception( sprintf( esc_html__( 'You have entered an invalid %1$s (%2$s) for your %4$s country (%3$s).', 'woocommerce-eu-vat-number' ), get_option( 'woocommerce_eu_vat_number_field_label', __( 'VAT number', 'woocommerce-eu-vat-number' ) ), $vat_number, $country, $type ) ); } } catch ( Exception $e ) { $order->update_meta_data( '_vat_number_is_valid', 'false' ); @@ -391,6 +456,14 @@ public static function admin_order( $args, $order ) { * @return array $fields Modified user fields. */ public static function add_customer_meta_fields( $fields ) { + if ( wc_eu_vat_use_shipping_country() ) { + $fields['shipping']['fields']['vat_number'] = array( + 'label' => esc_html__( 'VAT number', 'woocommerce-eu-vat-number' ), + 'description' => '', + ); + + return $fields; + } $fields['billing']['fields']['vat_number'] = array( 'label' => esc_html__( 'VAT number', 'woocommerce-eu-vat-number' ), 'description' => '', @@ -409,7 +482,8 @@ public static function add_customer_meta_fields( $fields ) { * @return array $data Modified user data. */ public static function get_customer_details( $data, $customer, $user_id ) { - $data['billing']['vat_number'] = get_user_meta( $user_id, 'vat_number', true ); + $data['billing']['vat_number'] = get_user_meta( $user_id, 'vat_number', true ); + $data['shipping']['vat_number'] = get_user_meta( $user_id, 'vat_number', true ); return $data; } diff --git a/includes/class-wc-eu-vat-extend-store-endpoint.php b/includes/class-wc-eu-vat-extend-store-endpoint.php index dbc1a0d..a2412c6 100644 --- a/includes/class-wc-eu-vat-extend-store-endpoint.php +++ b/includes/class-wc-eu-vat-extend-store-endpoint.php @@ -47,13 +47,24 @@ public function init() { * @param \WP_REST_Request $request Full details about the request. */ public function set_vat_session_data( $customer, $request ) { - $params = $request->get_body_params(); + $params = $request->get_body_params(); + $use_shipping_country = wc_eu_vat_use_shipping_country(); + $needs_shipping = WC()->cart ? WC()->cart->needs_shipping_address() : false; - if ( ! ( isset( $params['billing_address'] ) && isset( $params['billing_address']['country'] ) ) ) { - return; + if ( $use_shipping_country && $needs_shipping ) { + if ( ! ( isset( $params['shipping_address'] ) && isset( $params['shipping_address']['country'] ) ) ) { + return; + } + + $country = $params['shipping_address']['country']; + } else { + if ( ! ( isset( $params['billing_address'] ) && isset( $params['billing_address']['country'] ) ) ) { + return; + } + + $country = $params['billing_address']['country']; } - $country = $params['billing_address']['country']; $vat_prefix = WC_EU_VAT_Number::get_vat_number_prefix( $country ); $country_codes_patterns = WC_EU_VAT_Number::get_country_code_patterns(); @@ -228,8 +239,18 @@ public function validate() { return $data; } - $country = WC()->customer->get_billing_country(); - $postcode = WC()->customer->get_billing_postcode(); + $needs_shipping = WC()->cart ? WC()->cart->needs_shipping_address() : false; + $use_shipping_country = wc_eu_vat_use_shipping_country(); + $billing_country = WC()->customer->get_billing_country(); + $shipping_country = WC()->customer->get_shipping_country() ? WC()->customer->get_shipping_country() : $billing_country; + + $country = $billing_country; + $postcode = WC()->customer->get_billing_postcode(); + if ( $use_shipping_country && $needs_shipping ) { + $country = $shipping_country; + $postcode = WC()->customer->get_shipping_postcode() ? WC()->customer->get_shipping_postcode() : $postcode; + } + $fail_handler = get_option( 'woocommerce_eu_vat_number_failure_handling', 'reject' ); $valid = WC_EU_VAT_Number::vat_number_is_valid( $vat_number, $country, $postcode ); diff --git a/includes/class-wc-eu-vat-my-account.php b/includes/class-wc-eu-vat-my-account.php index 5882bae..9a93a34 100644 --- a/includes/class-wc-eu-vat-my-account.php +++ b/includes/class-wc-eu-vat-my-account.php @@ -101,10 +101,19 @@ public function maybe_remove_vat() { // Validate if VAT is valid. If valid, check for VAT exempt. try { - $billing_country = WC()->customer->get_billing_country(); - $shipping_country = WC()->customer->get_shipping_country(); + $billing_country = WC()->customer->get_billing_country(); + $billing_postcode = WC()->customer->get_billing_postcode() ?? ''; + $shipping_country = WC()->customer->get_shipping_country() ?? $billing_country; + $shipping_postcode = WC()->customer->get_shipping_postcode() ?? ''; + + $country = $billing_country; + $postcode = $billing_postcode; + if ( WC()->customer->has_shipping_address() && wc_eu_vat_use_shipping_country() ) { + $country = $shipping_country; + $postcode = $shipping_postcode; + } - if ( $this->validate( $vat_number, $billing_country ) ) { + if ( $this->validate( $vat_number, $country, $postcode ) ) { WC_EU_VAT_Number::maybe_set_vat_exempt( true, $billing_country, $shipping_country ); } } catch ( Exception $e ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch @@ -214,15 +223,15 @@ public function render_my_vat_number_content() { * * @version 2.3.0 * @since 2.3.0 - * @param string $vat_number VAT number passed by the form. - * @param string $billing_country Billing country of the order. - * @param string $billing_postcode Billing postcode of the order. - * @param string $current_vat VAT number saved in database. + * @param string $vat_number VAT number passed by the form. + * @param string $country Country of the customer. + * @param string $postcode Postcode of the customer. + * @param string $current_vat VAT number saved in database. * * @return boolean * @throws Exception For invalid VAT Number. */ - public function validate( $vat_number, $billing_country, $billing_postcode = '', $current_vat = '' ) { + public function validate( $vat_number, $country, $postcode = '', $current_vat = '' ) { if ( empty( $vat_number ) ) { if ( empty( $current_vat ) ) { throw new Exception( esc_html__( 'VAT number cannot be empty.', 'woocommerce-eu-vat-number' ) ); @@ -231,33 +240,33 @@ public function validate( $vat_number, $billing_country, $billing_postcode = '', return true; } - if ( empty( $billing_country ) ) { + if ( empty( $country ) ) { + $country_type = wc_eu_vat_use_shipping_country() ? __( 'shipping', 'woocommerce-eu-vat-number' ) : __( 'billing', 'woocommerce-eu-vat-number' ); /* translators: 1: VAT Number */ throw new Exception( sprintf( - // translators: %1$s VAT number field label. - esc_html__( '%1$s can not be validated because the billing country is missing. Please update your billing address.', 'woocommerce-eu-vat-number' ), - '' . esc_html( get_option( 'woocommerce_eu_vat_number_field_label', __( 'VAT number', 'woocommerce-eu-vat-number' ) ) ) . '' + // translators: %1$s VAT number field label, %2$s Country type. + esc_html__( '%1$s can not be validated because the %2$s country is missing. Please update your %2$s address.', 'woocommerce-eu-vat-number' ), + '' . esc_html( get_option( 'woocommerce_eu_vat_number_field_label', __( 'VAT number', 'woocommerce-eu-vat-number' ) ) ) . '', + esc_html( $country_type ) ) ); } - $valid = WC_EU_VAT_Number::vat_number_is_valid( $vat_number, $billing_country, $billing_postcode ); + $valid = WC_EU_VAT_Number::vat_number_is_valid( $vat_number, $country, $postcode ); if ( is_wp_error( $valid ) ) { throw new Exception( esc_html( $valid->get_error_message() ) ); } if ( ! $valid ) { - // translators: %1$s VAT number field label, %2$s VAT number, %3$s Billing Country. throw new Exception( sprintf( - /* translators: %1$s - VAT field label, %2$s - VAT number, %2$s - billing country */ - esc_html__( 'You have entered an invalid %1$s (%2$s) for your billing country (%3$s).', 'woocommerce-eu-vat-number' ), - esc_html( get_option( 'woocommerce_eu_vat_number_field_label' ) ), - esc_html__( 'VAT number', 'woocommerce-eu-vat-number' ), + /* translators: %1$s VAT number field label, %2$s VAT number, %3$s Country.*/ + esc_html__( 'You have entered an invalid %1$s (%2$s) for your country (%3$s).', 'woocommerce-eu-vat-number' ), + esc_html( get_option( 'woocommerce_eu_vat_number_field_label', __( 'VAT number', 'woocommerce-eu-vat-number' ) ) ), esc_html( $vat_number ), - esc_html( $billing_country ) + esc_html( $country ) ) ); } @@ -273,17 +282,26 @@ public function save_vat_number() { return; } - if ( isset( $_POST['_wpnonce'] ) && wp_verify_nonce( wp_unslash( $_POST['_wpnonce'] ), 'woocommerce-edit_vat_number' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + if ( isset( $_POST['_wpnonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ), 'woocommerce-edit_vat_number' ) ) { try { - $current_user_id = get_current_user_id(); - $vat_number = isset( $_POST['vat_number'] ) ? wc_clean( wp_unslash( $_POST['vat_number'] ) ) : ''; - $posted_vat = strtoupper( str_replace( array( ' ', '.', '-', ',', ', ' ), '', $vat_number ) ); - $user = get_userdata( $current_user_id ); - $current_vat = $user->vat_number; - $billing_country = $user->billing_country; - $billing_postcode = $user->billing_postcode; - - $this->validate( $posted_vat, $billing_country, $billing_postcode, $current_vat ); + $current_user_id = get_current_user_id(); + $vat_number = isset( $_POST['vat_number'] ) ? wc_clean( wp_unslash( $_POST['vat_number'] ) ) : ''; + $posted_vat = strtoupper( str_replace( array( ' ', '.', '-', ',', ', ' ), '', $vat_number ) ); + $user = get_userdata( $current_user_id ); + $current_vat = $user->vat_number; + $billing_country = $user->billing_country; + $billing_postcode = $user->billing_postcode; + $shipping_country = $user->shipping_country ? $user->shipping_country : $billing_country; + $shipping_postcode = $user->shipping_postcode; + + $country = $billing_country; + $postcode = $billing_postcode; + if ( wc_eu_vat_use_shipping_country() ) { + $country = $shipping_country; + $postcode = $shipping_postcode; + } + + $this->validate( $posted_vat, $country, $postcode, $current_vat ); update_user_meta( $current_user_id, 'vat_number', $posted_vat ); update_user_meta( $current_user_id, 'billing_vat_number', $posted_vat ); diff --git a/includes/class-wc-eu-vat-number.php b/includes/class-wc-eu-vat-number.php index 176029d..867a854 100644 --- a/includes/class-wc-eu-vat-number.php +++ b/includes/class-wc-eu-vat-number.php @@ -88,6 +88,9 @@ public static function init() { // Add fields to checkout process. add_action( 'wp_enqueue_scripts', array( __CLASS__, 'load_scripts' ) ); add_filter( 'woocommerce_billing_fields', array( __CLASS__, 'vat_number_field' ) ); + if ( wc_eu_vat_use_shipping_country() ) { + add_filter( 'woocommerce_shipping_fields', array( __CLASS__, 'shipping_vat_number_field' ) ); + } add_action( 'woocommerce_checkout_process', array( __CLASS__, 'process_checkout' ) ); add_action( 'woocommerce_checkout_update_order_review', array( __CLASS__, 'ajax_update_checkout_totals' ) ); add_action( 'woocommerce_review_order_before_submit', array( __CLASS__, 'location_confirmation' ) ); @@ -99,6 +102,7 @@ public static function init() { // Add VAT to addresses. add_filter( 'woocommerce_order_formatted_billing_address', array( __CLASS__, 'formatted_billing_address' ), 10, 2 ); + add_filter( 'woocommerce_order_formatted_shipping_address', array( __CLASS__, 'formatted_shipping_address' ), 10, 2 ); add_filter( 'woocommerce_formatted_address_replacements', array( __CLASS__, 'output_company_vat_number' ), 10, 2 ); add_filter( 'woocommerce_localisation_address_formats', array( __CLASS__, 'localisation_address_formats' ), 10, 2 ); @@ -132,11 +136,12 @@ public static function localize_wc_eu_vat_params( $script_handle ) { $script_handle, 'wc_eu_vat_params', array( - 'eu_countries' => self::get_eu_countries(), - 'b2b_required' => get_option( 'woocommerce_eu_vat_number_b2b', 'false' ), - 'input_label' => get_option( 'woocommerce_eu_vat_number_field_label', 'VAT number' ), - 'input_description' => get_option( 'woocommerce_eu_vat_number_field_description', '' ), - 'failure_handler' => get_option( 'woocommerce_eu_vat_number_failure_handling', 'reject' ), + 'eu_countries' => self::get_eu_countries(), + 'b2b_required' => get_option( 'woocommerce_eu_vat_number_b2b', 'false' ), + 'input_label' => get_option( 'woocommerce_eu_vat_number_field_label', 'VAT number' ), + 'input_description' => get_option( 'woocommerce_eu_vat_number_field_description', '' ), + 'failure_handler' => get_option( 'woocommerce_eu_vat_number_failure_handling', 'reject' ), + 'use_shipping_country' => wc_eu_vat_use_shipping_country(), ) ); } @@ -167,6 +172,40 @@ public static function reset() { ); } + /** + * Add VAT Number field wrapper on the shipping fields. + * + * @since 2.9.4 + * + * @param array $fields Shipping Fields. + * @return array + */ + public static function shipping_vat_number_field( $fields ) { + $user_id = get_current_user_id(); + + // If on edit address page, unset vat number field. + if ( is_wc_endpoint_url( 'edit-address' ) ) { + if ( isset( $fields['shipping_vat_number'] ) ) { + unset( $fields['shipping_vat_number'] ); + } + return $fields; + } + + $fields['shipping_vat_number'] = array( + 'label' => get_option( 'woocommerce_eu_vat_number_field_label', __( 'VAT number', 'woocommerce-eu-vat-number' ) ), + 'default' => $user_id > 0 ? get_user_meta( $user_id, 'vat_number', true ) : '', + 'required' => false, + 'class' => array( + 'form-row-wide', + ), + 'description' => get_option( 'woocommerce_eu_vat_number_field_description', '' ), + 'id' => 'woocommerce_eu_vat_number_shipping', + 'priority' => 120, + ); + + return $fields; + } + /** * Show the VAT field on the checkout. * @@ -286,7 +325,7 @@ public static function vat_number_is_valid( $vat_number, $country, $postcode = ' // Return error if VAT Country Code doesn't match or exist. if ( ! isset( self::$country_codes_patterns[ $vat_prefix ] ) || ( $vat_prefix . $vat_number_formatted !== $vat_number ) ) { // translators: %1$s - VAT number field label, %2$s - VAT Number from user, %3$s - Billing country. - return new WP_Error( 'api', sprintf( __( 'You have entered an invalid country code for %1$s (%2$s) for your billing country (%3$s).', 'woocommerce-eu-vat-number' ), get_option( 'woocommerce_eu_vat_number_field_label', 'VAT number' ), $vat_number, $country ) ); + return new WP_Error( 'api', sprintf( __( 'You have entered an invalid country code for %1$s (%2$s) for your country (%3$s).', 'woocommerce-eu-vat-number' ), get_option( 'woocommerce_eu_vat_number_field_label', 'VAT number' ), $vat_number, $country ) ); } if ( ! empty( $cached_result ) ) { @@ -355,13 +394,13 @@ public static function get_country_code_patterns() { /** * Validate a number and store the result. * - * @param string $vat_number VAT Number. - * @param string $billing_country Billing CountryCode. - * @param string $billing_postcode Billing PostCode. + * @param string $vat_number VAT Number. + * @param string $country Billing CountryCode. + * @param string $postcode Billing PostCode. * @return void */ - public static function validate( $vat_number, $billing_country, $billing_postcode = '' ) { - $valid = self::vat_number_is_valid( $vat_number, $billing_country, $billing_postcode ); + public static function validate( $vat_number, $country, $postcode = '' ) { + $valid = self::vat_number_is_valid( $vat_number, $country, $postcode ); $vat_number_formatted = self::get_formatted_vat_number( $vat_number ); if ( is_wp_error( $valid ) ) { @@ -371,7 +410,7 @@ public static function validate( $vat_number, $billing_country, $billing_postcod 'error' => $valid->get_error_message(), ); } else { - self::$data['vat_number'] = $valid ? self::get_vat_number_prefix( $billing_country ) . $vat_number_formatted : $vat_number; + self::$data['vat_number'] = $valid ? self::get_vat_number_prefix( $country ) . $vat_number_formatted : $vat_number; self::$data['validation'] = array( 'valid' => $valid, 'error' => false, @@ -536,7 +575,7 @@ public static function ajax_update_checkout_totals( $form_data ) { self::reset(); - if ( empty( $form_data['billing_country'] ) && empty( $form_data['shipping_country'] ) || empty( $form_data['billing_vat_number'] ) ) { + if ( empty( $form_data['billing_country'] ) && empty( $form_data['shipping_country'] ) || ( empty( $form_data['billing_vat_number'] ) && empty( $form_data['shipping_vat_number'] ) ) ) { return; } @@ -578,6 +617,10 @@ public static function cart_has_digital_goods() { * @return array */ public static function formatted_billing_address( $address, $order ) { + if ( $order->has_shipping_address() && wc_eu_vat_use_shipping_country() ) { + return $address; + } + $vat_id = wc_eu_vat_get_vat_from_order( $order ); if ( $vat_id ) { @@ -586,6 +629,28 @@ public static function formatted_billing_address( $address, $order ) { return $address; } + /** + * Add VAT ID to the formatted shipping address array + * + * @param array $address Address Array. + * @param WC_Order $order WC Order Object. + * @return array + */ + public static function formatted_shipping_address( $address, $order ) { + if ( ! $order->has_shipping_address() || ! wc_eu_vat_use_shipping_country() ) { + return $address; + } + + $vat_id = wc_eu_vat_get_vat_from_order( $order ); + + if ( $vat_id ) { + $address['vat_id'] = $vat_id; + } + return $address; + } + + + /** * Add {vat_id} placeholder * @@ -700,15 +765,29 @@ public static function set_refund_data( $refund ) { * @param boolean $doing_checkout True if doing checkout. False if AJAX order review. */ public static function validate_checkout( $data, $doing_checkout = false ) { - $b2b_vat_enabled = get_option( 'woocommerce_eu_vat_number_b2b', 'no' ); - $fail_handler = get_option( 'woocommerce_eu_vat_number_failure_handling', 'reject' ); - $billing_country = wc_clean( $data['billing_country'] ); - $shipping_country = wc_clean( ! empty( $data['shipping_country'] ) && ! empty( $data['ship_to_different_address'] ) ? $data['shipping_country'] : $data['billing_country'] ); - $billing_vat_number = wc_clean( $data['billing_vat_number'] ); - $billing_postcode = wc_clean( $data['billing_postcode'] ); - - if ( in_array( $billing_country, self::get_eu_countries(), true ) && ! empty( $billing_vat_number ) ) { - self::validate( $billing_vat_number, $billing_country, $billing_postcode ); + $use_shipping_country = wc_eu_vat_use_shipping_country(); + $b2b_vat_enabled = get_option( 'woocommerce_eu_vat_number_b2b', 'no' ); + $fail_handler = get_option( 'woocommerce_eu_vat_number_failure_handling', 'reject' ); + $billing_country = wc_clean( $data['billing_country'] ); + $shipping_country = wc_clean( ! empty( $data['shipping_country'] ) && ! empty( $data['ship_to_different_address'] ) ? $data['shipping_country'] : $data['billing_country'] ); + $billing_vat_number = wc_clean( $data['billing_vat_number'] ); + $billing_postcode = wc_clean( $data['billing_postcode'] ); + $shipping_postcode = wc_clean( ( ! empty( $data['ship_to_different_address'] ) && isset( $data['shipping_postcode'] ) ) ? $data['shipping_postcode'] : $data['billing_postcode'] ); + $ship_to_different = ! empty( $data['ship_to_different_address'] ) ? true : false; + + // If using shipping country, use shipping VAT number. + if ( ! empty( $data['shipping_country'] ) && $use_shipping_country && $ship_to_different ) { + $billing_vat_number = wc_clean( $data['shipping_vat_number'] ); + } + $country = $billing_country; + $postcode = $billing_postcode; + if ( $use_shipping_country && $ship_to_different ) { + $country = $shipping_country; + $postcode = $shipping_postcode; + } + + if ( in_array( $country, self::get_eu_countries(), true ) && ! empty( $billing_vat_number ) ) { + self::validate( $billing_vat_number, $country, $postcode ); if ( true === (bool) self::$data['validation']['valid'] ) { self::maybe_set_vat_exempt( true, $billing_country, $shipping_country ); @@ -722,8 +801,17 @@ public static function validate_checkout( $data, $doing_checkout = false ) { break; default: if ( false === self::$data['validation']['valid'] ) { - /* translators: 1: VAT number field label, 2: VAT Number, 3: Billing country */ - wc_add_notice( sprintf( __( 'You have entered an invalid %1$s (%2$s) for your billing country (%3$s).', 'woocommerce-eu-vat-number' ), get_option( 'woocommerce_eu_vat_number_field_label', __( 'VAT number', 'woocommerce-eu-vat-number' ) ), self::$data['vat_number'], $billing_country ), 'error' ); + wc_add_notice( + sprintf( + /* translators: 1: VAT number field label, 2: VAT Number, 3: Address type, 4: Country */ + __( 'You have entered an invalid %1$s (%2$s) for your %3$s country (%4$s).', 'woocommerce-eu-vat-number' ), + get_option( 'woocommerce_eu_vat_number_field_label', __( 'VAT number', 'woocommerce-eu-vat-number' ) ), + self::$data['vat_number'], + ( $use_shipping_country && $ship_to_different ) ? __( 'shipping', 'woocommerce-eu-vat-number' ) : __( 'billing', 'woocommerce-eu-vat-number' ), + $country + ), + 'error' + ); } else { wc_add_notice( self::$data['validation']['error'], 'error' ); } @@ -734,10 +822,18 @@ public static function validate_checkout( $data, $doing_checkout = false ) { // If doing checkout, check for additional conditions. if ( $doing_checkout ) { - if ( in_array( $billing_country, self::get_eu_countries(), true ) && empty( $billing_vat_number ) ) { + if ( in_array( $country, self::get_eu_countries(), true ) && empty( $billing_vat_number ) ) { if ( 'yes' === $b2b_vat_enabled ) { - /* translators: 1: VAT number field label, 2: Billing country */ - wc_add_notice( sprintf( __( '%1$s is a required field for your billing country (%2$s).', 'woocommerce-eu-vat-number' ), '' . get_option( 'woocommerce_eu_vat_number_field_label', __( 'VAT number', 'woocommerce-eu-vat-number' ) ) . '', $billing_country ), 'error' ); + wc_add_notice( + sprintf( + /* translators: 1: VAT number field label, 2: Address type, 3: Billing country */ + __( '%1$s is a required field for your %2$s country (%3$s).', 'woocommerce-eu-vat-number' ), + '' . get_option( 'woocommerce_eu_vat_number_field_label', __( 'VAT number', 'woocommerce-eu-vat-number' ) ) . '', + ( $use_shipping_country && $ship_to_different ) ? __( 'shipping', 'woocommerce-eu-vat-number' ) : __( 'billing', 'woocommerce-eu-vat-number' ), + $billing_country + ), + 'error' + ); } if ( 'yes' === get_option( 'woocommerce_eu_vat_number_validate_ip', 'no' ) && self::cart_has_digital_goods() ) { diff --git a/includes/class-wc-eu-vat-report-ec-sales-list.php b/includes/class-wc-eu-vat-report-ec-sales-list.php index d757c60..604ced0 100644 --- a/includes/class-wc-eu-vat-report-ec-sales-list.php +++ b/includes/class-wc-eu-vat-report-ec-sales-list.php @@ -137,6 +137,11 @@ public function get_main_chart() { 'function' => '', 'name' => '_billing_country', ), + '_shipping_country' => array( + 'type' => 'meta', + 'function' => '', + 'name' => '_shipping_country', + ), '_order_currency' => array( 'type' => 'meta', 'function' => '', @@ -144,11 +149,6 @@ public function get_main_chart() { ), ), 'where' => array( - array( - 'key' => 'meta__billing_country.meta_value', - 'value' => WC_EU_VAT_Number::get_eu_countries(), - 'operator' => 'in', - ), array( 'key' => 'meta__billing_vat_number.meta_value', 'value' => '', @@ -165,33 +165,33 @@ public function get_main_chart() { $ec_sales2 = $this->get_order_report_data( array( 'data' => array( - '_order_total' => array( + '_order_total' => array( 'type' => 'meta', 'function' => 'SUM', 'name' => 'total_sales', ), - '_vat_number' => array( + '_vat_number' => array( 'type' => 'meta', 'function' => '', 'name' => '_vat_number', ), - '_billing_country' => array( + '_billing_country' => array( 'type' => 'meta', 'function' => '', 'name' => '_billing_country', ), - '_order_currency' => array( + '_shipping_country' => array( + 'type' => 'meta', + 'function' => '', + 'name' => '_shipping_country', + ), + '_order_currency' => array( 'type' => 'meta', 'function' => '', 'name' => '_order_currency', ), ), 'where' => array( - array( - 'key' => 'meta__billing_country.meta_value', - 'value' => WC_EU_VAT_Number::get_eu_countries(), - 'operator' => 'in', - ), array( 'key' => 'meta__vat_number.meta_value', 'value' => '', @@ -211,7 +211,8 @@ public function get_main_chart() { - + + @@ -220,11 +221,18 @@ public function get_main_chart() { _billing_country, WC_EU_VAT_Number::get_eu_countries(), true ) && + ! in_array( $ec_sale->_shipping_country, WC_EU_VAT_Number::get_eu_countries(), true ) + ) { + continue; + } $vat_number = ! empty( $ec_sale->_billing_vat_number ) ? $ec_sale->_billing_vat_number : $ec_sale->_vat_number; ?> - + + get_order_report_data( array( @@ -175,8 +176,15 @@ public function get_main_chart() { } } } else { - $order = wc_get_order( $data->ID ); - $country = $order->get_meta( '_billing_country', true ); + $order = wc_get_order( $data->ID ); + $country = $order->get_meta( '_billing_country', true ); + $vat_number = $order->get_meta( '_billing_vat_number', true ); + if ( $use_shipping_country && ! empty( $vat_number ) ) { + $shipping_country = $order->get_meta( '_shipping_country', true ); + if ( ! empty( $shipping_country ) ) { + $country = $shipping_country; + } + } if ( $country ) { if ( ! isset( $grouped_tax_rows[ $country ] ) ) { @@ -244,8 +252,15 @@ public function get_main_chart() { $grouped_tax_rows[ $tax_id ]->refunded_tax_amount += ( wc_round_tax_total( $tax_value ) * -1 ); } } else { - $order = wc_get_order( $data->ID ); - $country = $order->get_meta( '_billing_country', true ); + $order = wc_get_order( $data->ID ); + $country = $order->get_meta( '_billing_country', true ); + $vat_number = $order->get_meta( '_billing_vat_number', true ); + if ( $use_shipping_country && ! empty( $vat_number ) ) { + $shipping_country = $order->get_meta( '_shipping_country', true ); + if ( ! empty( $shipping_country ) ) { + $country = $shipping_country; + } + } if ( $country ) { if ( ! isset( $grouped_tax_rows[ $country ] ) ) { @@ -375,7 +390,7 @@ public function get_main_chart() { __( 'Accept the order and remove VAT.', 'woocommerce-eu-vat-number' ), ), ), + array( + 'name' => __( 'Use shipping address for validation', 'woocommerce-eu-vat-number' ), + 'desc' => __( 'Always use the customer shipping address country for VAT Number validation.', 'woocommerce-eu-vat-number' ), + 'id' => 'woocommerce_eu_vat_number_use_shipping_country', + 'type' => 'checkbox', + 'default' => 'yes', + ), array( 'name' => __( 'Enable B2B Transactions', 'woocommerce-eu-vat-number' ), 'desc' => __( 'This will force users to check out with a VAT number, useful for sites that transact purely from B2B.', 'woocommerce-eu-vat-number' ), @@ -99,7 +106,7 @@ class_exists( 'WC_PayPal_Braintree_Loader' ), 'type' => 'title', 'title' => __( 'EU VAT Digital Goods Handling', 'woocommerce-eu-vat-number' ), /* translators: %1$s Opening anchor tag, %2$s Closing anchor tag */ - 'desc' => sprintf( __( 'EU VAT laws are changing for digital goods from the 1st Jan 2015 (affecting B2C transactions only). The VAT on digital goods must be calculated based on the customer location, and you need to collect evidence of this (IP address and Billing Address). You also need to setup VAT rates to charge the correct amount. %1$sRead this guide%2$s for instructions on doing this.', 'woocommerce-eu-vat-number' ), '', '' ), + 'desc' => sprintf( __( 'As of January 1st, 2015, EU VAT laws have been changed for digital goods (affecting B2C transactions only). The VAT on digital goods must be calculated based on the customer location, and you need to collect evidence of this (IP address and Billing Address). You also need to setup VAT rates to charge the correct amount. %1$sRead this guide%2$s for instructions on doing this.', 'woocommerce-eu-vat-number' ), '', '' ), 'id' => 'vat_number_digital_goods', ), array( diff --git a/includes/wc-eu-vat-functions.php b/includes/wc-eu-vat-functions.php index 2fb70a0..485f609 100644 --- a/includes/wc-eu-vat-functions.php +++ b/includes/wc-eu-vat-functions.php @@ -77,3 +77,14 @@ function wc_eu_vat_maybe_add_zero_tax_display( $total_rows, $order, $tax_display return $total_rows; } + +/** + * Get whether to use shipping country for VAT validation. + * + * @since 2.9.4 + * + * @return bool + */ +function wc_eu_vat_use_shipping_country() { + return 'yes' === get_option( 'woocommerce_eu_vat_number_use_shipping_country', 'yes' ); +} diff --git a/languages/woocommerce-eu-vat-number.pot b/languages/woocommerce-eu-vat-number.pot index e7588c0..8022fc6 100644 --- a/languages/woocommerce-eu-vat-number.pot +++ b/languages/woocommerce-eu-vat-number.pot @@ -2,10 +2,10 @@ # This file is distributed under the GNU General Public License v3.0. msgid "" msgstr "" -"Project-Id-Version: WooCommerce EU VAT Number 2.9.3\n" +"Project-Id-Version: WooCommerce EU VAT Number 2.9.4\n" "Report-Msgid-Bugs-To: " "https://wordpress.org/support/plugin/woocommerce-eu-vat-number\n" -"POT-Creation-Date: 2024-04-22 13:33:45+00:00\n" +"POT-Creation-Date: 2024-05-20 14:12:16+00:00\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -14,86 +14,99 @@ msgstr "" "Language-Team: LANGUAGE \n" "X-Generator: node-wp-i18n 1.2.6\n" -#: includes/class-wc-eu-vat-admin.php:63 includes/class-wc-eu-vat-admin.php:182 -#: includes/class-wc-eu-vat-admin.php:378 -#: includes/class-wc-eu-vat-admin.php:395 -#: includes/class-wc-eu-vat-my-account.php:154 -#: includes/class-wc-eu-vat-my-account.php:176 -#: includes/class-wc-eu-vat-my-account.php:240 -#: includes/class-wc-eu-vat-my-account.php:258 -#: includes/class-wc-eu-vat-number.php:191 -#: includes/class-wc-eu-vat-number.php:726 -#: includes/class-wc-eu-vat-number.php:740 +#: includes/class-wc-eu-vat-admin.php:114 +#: includes/class-wc-eu-vat-admin.php:237 +#: includes/class-wc-eu-vat-admin.php:443 +#: includes/class-wc-eu-vat-admin.php:461 +#: includes/class-wc-eu-vat-admin.php:468 +#: includes/class-wc-eu-vat-my-account.php:163 +#: includes/class-wc-eu-vat-my-account.php:185 +#: includes/class-wc-eu-vat-my-account.php:250 +#: includes/class-wc-eu-vat-my-account.php:267 +#: includes/class-wc-eu-vat-number.php:195 +#: includes/class-wc-eu-vat-number.php:230 +#: includes/class-wc-eu-vat-number.php:808 +#: includes/class-wc-eu-vat-number.php:831 #: includes/class-wc-eu-vat-privacy.php:147 -#: includes/class-wc-eu-vat-report-ec-sales-list.php:215 +#: includes/class-wc-eu-vat-report-ec-sales-list.php:216 #: templates/my-account/my-vat-number.php:22 msgid "VAT number" msgstr "" -#: includes/class-wc-eu-vat-admin.php:83 includes/class-wc-eu-vat-admin.php:262 +#: includes/class-wc-eu-vat-admin.php:134 +#: includes/class-wc-eu-vat-admin.php:317 #: includes/class-wc-eu-vat-privacy.php:20 msgid "EU VAT" msgstr "" -#: includes/class-wc-eu-vat-admin.php:163 +#: includes/class-wc-eu-vat-admin.php:218 msgid "This order is out of scope for EU VAT." msgstr "" -#: includes/class-wc-eu-vat-admin.php:175 +#: includes/class-wc-eu-vat-admin.php:230 msgid "B2B" msgstr "" -#: includes/class-wc-eu-vat-admin.php:176 +#: includes/class-wc-eu-vat-admin.php:231 msgid "Yes" msgstr "" -#: includes/class-wc-eu-vat-admin.php:176 +#: includes/class-wc-eu-vat-admin.php:231 msgid "No" msgstr "" -#: includes/class-wc-eu-vat-admin.php:187 +#: includes/class-wc-eu-vat-admin.php:242 msgid "Validation was not possible" msgstr "" -#: includes/class-wc-eu-vat-admin.php:196 +#: includes/class-wc-eu-vat-admin.php:251 msgid "IP Address" msgstr "" -#: includes/class-wc-eu-vat-admin.php:197 -#: includes/class-wc-eu-vat-admin.php:215 -#: includes/class-wc-eu-vat-admin.php:223 +#: includes/class-wc-eu-vat-admin.php:252 +#: includes/class-wc-eu-vat-admin.php:270 +#: includes/class-wc-eu-vat-admin.php:278 msgid "Unknown" msgstr "" -#: includes/class-wc-eu-vat-admin.php:201 +#: includes/class-wc-eu-vat-admin.php:256 msgid "IP Country" msgstr "" -#: includes/class-wc-eu-vat-admin.php:210 -#: includes/class-wc-eu-vat-admin.php:306 +#: includes/class-wc-eu-vat-admin.php:265 +#: includes/class-wc-eu-vat-admin.php:361 msgid "(self-declared)" msgstr "" -#: includes/class-wc-eu-vat-admin.php:222 +#: includes/class-wc-eu-vat-admin.php:277 msgid "Billing Country" msgstr "" -#: includes/class-wc-eu-vat-admin.php:294 +#: includes/class-wc-eu-vat-admin.php:349 msgid "(validation failed)" msgstr "" -#: includes/class-wc-eu-vat-admin.php:378 -#: includes/class-wc-eu-vat-my-account.php:256 -#: includes/class-wc-eu-vat-number.php:726 -#. translators: %1$s VAT number field label, %2$s VAT number, %3$s Billing -#. Country. -#. translators: %1$s - VAT field label, %2$s - VAT number, %2$s - billing -#. country -#. translators: 1: VAT number field label, 2: VAT Number, 3: Billing country -msgid "You have entered an invalid %1$s (%2$s) for your billing country (%3$s)." +#: includes/class-wc-eu-vat-admin.php:403 +#: includes/class-wc-eu-vat-my-account.php:244 +#: includes/class-wc-eu-vat-number.php:810 +#: includes/class-wc-eu-vat-number.php:832 +msgid "billing" +msgstr "" + +#: includes/class-wc-eu-vat-admin.php:407 +#: includes/class-wc-eu-vat-my-account.php:244 +#: includes/class-wc-eu-vat-number.php:810 +#: includes/class-wc-eu-vat-number.php:832 +msgid "shipping" msgstr "" -#: includes/class-wc-eu-vat-admin.php:474 +#: includes/class-wc-eu-vat-admin.php:443 +#. translators: %1$s VAT number field label, %2$s VAT number, %3$s Country %4$s +#. country type: billing/shipping. +msgid "You have entered an invalid %1$s (%2$s) for your %4$s country (%3$s)." +msgstr "" + +#: includes/class-wc-eu-vat-admin.php:548 #. translators: %1$s Opening strong tag, %2$s Closing strong tag, %3$s Break #. tag. msgid "" @@ -105,7 +118,7 @@ msgid "" "from %1$sNorthern Ireland%2$s." msgstr "" -#: includes/class-wc-eu-vat-admin.php:485 +#: includes/class-wc-eu-vat-admin.php:559 #. translators: %1$s Opening strong tag, %2$s Closing strong tag. msgid "" "By using %1$sWooCommerce EU VAT Number%2$s plugin, you've agreed that the " @@ -114,11 +127,11 @@ msgid "" "tax specific questions." msgstr "" -#: includes/class-wc-eu-vat-admin.php:490 +#: includes/class-wc-eu-vat-admin.php:564 msgid "I understand" msgstr "" -#: includes/class-wc-eu-vat-admin.php:561 +#: includes/class-wc-eu-vat-admin.php:635 #. translators: %1$s - , %2$s - , %3$s - Link to edit checkout #. page, %4$s - closing tag msgid "" @@ -128,70 +141,81 @@ msgid "" "Number field accordingly." msgstr "" -#: includes/class-wc-eu-vat-extend-store-endpoint.php:191 +#: includes/class-wc-eu-vat-extend-store-endpoint.php:202 msgid "Location confirmation." msgstr "" -#: includes/class-wc-eu-vat-extend-store-endpoint.php:253 +#: includes/class-wc-eu-vat-extend-store-endpoint.php:274 msgid "Invalid VAT number." msgstr "" -#: includes/class-wc-eu-vat-extend-store-endpoint.php:332 +#: includes/class-wc-eu-vat-extend-store-endpoint.php:353 msgid "VAT Data" msgstr "" -#: includes/class-wc-eu-vat-my-account.php:228 +#: includes/class-wc-eu-vat-my-account.php:237 msgid "VAT number cannot be empty." msgstr "" -#: includes/class-wc-eu-vat-my-account.php:239 -#. translators: %1$s VAT number field label. +#: includes/class-wc-eu-vat-my-account.php:249 +#. translators: %1$s VAT number field label, %2$s Country type. msgid "" -"%1$s can not be validated because the billing country is missing. Please " -"update your billing address." +"%1$s can not be validated because the %2$s country is missing. Please " +"update your %2$s address." +msgstr "" + +#: includes/class-wc-eu-vat-my-account.php:266 +#. translators: %1$s VAT number field label, %2$s VAT number, %3$s Country. +msgid "You have entered an invalid %1$s (%2$s) for your country (%3$s)." msgstr "" -#: includes/class-wc-eu-vat-my-account.php:296 +#: includes/class-wc-eu-vat-my-account.php:314 msgid "VAT number removed successfully!" msgstr "" -#: includes/class-wc-eu-vat-my-account.php:298 +#: includes/class-wc-eu-vat-my-account.php:316 msgid "VAT number saved successfully!" msgstr "" -#: includes/class-wc-eu-vat-my-account.php:300 +#: includes/class-wc-eu-vat-my-account.php:318 msgid "VAT number updated successfully!" msgstr "" -#: includes/class-wc-eu-vat-number.php:269 +#: includes/class-wc-eu-vat-number.php:308 msgid "VAT number is required." msgstr "" -#: includes/class-wc-eu-vat-number.php:289 +#: includes/class-wc-eu-vat-number.php:328 #. translators: %1$s - VAT number field label, %2$s - VAT Number from user, #. %3$s - Billing country. msgid "" -"You have entered an invalid country code for %1$s (%2$s) for your billing " -"country (%3$s)." +"You have entered an invalid country code for %1$s (%2$s) for your country " +"(%3$s)." msgstr "" -#: includes/class-wc-eu-vat-number.php:303 -#: includes/class-wc-eu-vat-number.php:321 +#: includes/class-wc-eu-vat-number.php:342 +#: includes/class-wc-eu-vat-number.php:360 #: includes/vies/class-vies-client.php:92 msgid "Error communicating with the VAT validation server - please try again." msgstr "" -#: includes/class-wc-eu-vat-number.php:599 +#: includes/class-wc-eu-vat-number.php:664 #. translators: %s: VAT Number msgid "VAT Number: %s" msgstr "" -#: includes/class-wc-eu-vat-number.php:740 -#. translators: 1: VAT number field label, 2: Billing country -msgid "%1$s is a required field for your billing country (%2$s)." +#: includes/class-wc-eu-vat-number.php:807 +#. translators: 1: VAT number field label, 2: VAT Number, 3: Address type, 4: +#. Country +msgid "You have entered an invalid %1$s (%2$s) for your %3$s country (%4$s)." msgstr "" -#: includes/class-wc-eu-vat-number.php:753 +#: includes/class-wc-eu-vat-number.php:830 +#. translators: 1: VAT number field label, 2: Address type, 3: Billing country +msgid "%1$s is a required field for your %2$s country (%3$s)." +msgstr "" + +#: includes/class-wc-eu-vat-number.php:849 #. translators: 1: Ip Address. msgid "" "Your IP Address (%1$s) does not match your billing country (%2$s). European " @@ -277,56 +301,63 @@ msgid "This Quarter" msgstr "" #: includes/class-wc-eu-vat-report-ec-sales-list.php:214 -#: includes/class-wc-eu-vat-report-eu-vat.php:342 -#: includes/class-wc-non-eu-sales-report.php:307 -msgid "Country" +msgid "Billing country" msgstr "" -#: includes/class-wc-eu-vat-report-ec-sales-list.php:216 +#: includes/class-wc-eu-vat-report-ec-sales-list.php:215 +msgid "Shipping country" +msgstr "" + +#: includes/class-wc-eu-vat-report-ec-sales-list.php:217 msgid "Value" msgstr "" -#: includes/class-wc-eu-vat-report-ec-sales-list.php:237 +#: includes/class-wc-eu-vat-report-ec-sales-list.php:245 msgid "No B2B EU orders found within this period." msgstr "" +#: includes/class-wc-eu-vat-report-eu-vat.php:342 +#: includes/class-wc-non-eu-sales-report.php:322 +msgid "Country" +msgstr "" + #: includes/class-wc-eu-vat-report-eu-vat.php:343 -#: includes/class-wc-non-eu-sales-report.php:308 +#: includes/class-wc-non-eu-sales-report.php:323 msgid "Code" msgstr "" #: includes/class-wc-eu-vat-report-eu-vat.php:344 -#: includes/class-wc-non-eu-sales-report.php:309 +#: includes/class-wc-non-eu-sales-report.php:324 msgid "Tax Rate" msgstr "" #: includes/class-wc-eu-vat-report-eu-vat.php:345 -#: includes/class-wc-non-eu-sales-report.php:310 +#: includes/class-wc-non-eu-sales-report.php:325 msgid "Amount" msgstr "" #: includes/class-wc-eu-vat-report-eu-vat.php:346 -#: includes/class-wc-non-eu-sales-report.php:311 +#: includes/class-wc-non-eu-sales-report.php:326 msgid "Refunded Amount" msgstr "" #: includes/class-wc-eu-vat-report-eu-vat.php:347 -#: includes/class-wc-non-eu-sales-report.php:312 +#: includes/class-wc-non-eu-sales-report.php:327 msgid "Final Amount" msgstr "" #: includes/class-wc-eu-vat-report-eu-vat.php:348 -#: includes/class-wc-non-eu-sales-report.php:313 +#: includes/class-wc-non-eu-sales-report.php:328 msgid "Tax Amount" msgstr "" #: includes/class-wc-eu-vat-report-eu-vat.php:349 -#: includes/class-wc-non-eu-sales-report.php:314 +#: includes/class-wc-non-eu-sales-report.php:329 msgid "Tax Refunded Amount" msgstr "" #: includes/class-wc-eu-vat-report-eu-vat.php:350 -#: includes/class-wc-non-eu-sales-report.php:315 +#: includes/class-wc-non-eu-sales-report.php:330 msgid "Final Tax Amount " msgstr "" @@ -346,11 +377,11 @@ msgstr "" msgid "Non EU Sales" msgstr "" -#: includes/class-wc-non-eu-sales-report.php:385 +#: includes/class-wc-non-eu-sales-report.php:400 msgid "Totals" msgstr "" -#: includes/class-wc-non-eu-sales-report.php:400 +#: includes/class-wc-non-eu-sales-report.php:415 msgid "No non-eu sales found in this period" msgstr "" @@ -411,62 +442,71 @@ msgid "Accept the order and remove VAT." msgstr "" #: includes/data/eu-vat-number-settings.php:77 -msgid "Enable B2B Transactions" +msgid "Use shipping address for validation" msgstr "" #: includes/data/eu-vat-number-settings.php:78 +msgid "Always use the customer shipping address country for VAT Number validation." +msgstr "" + +#: includes/data/eu-vat-number-settings.php:84 +msgid "Enable B2B Transactions" +msgstr "" + +#: includes/data/eu-vat-number-settings.php:85 msgid "" "This will force users to check out with a VAT number, useful for sites that " "transact purely from B2B." msgstr "" -#: includes/data/eu-vat-number-settings.php:86 +#: includes/data/eu-vat-number-settings.php:93 msgid "Disable Express Pay" msgstr "" -#: includes/data/eu-vat-number-settings.php:87 +#: includes/data/eu-vat-number-settings.php:94 msgid "Prevent incompatible payment methods." msgstr "" -#: includes/data/eu-vat-number-settings.php:88 +#: includes/data/eu-vat-number-settings.php:95 msgid "" "This will prevent payment methods that do not require customers to enter a " "VAT number. Limited to compatible payment gateways." msgstr "" -#: includes/data/eu-vat-number-settings.php:100 +#: includes/data/eu-vat-number-settings.php:107 msgid "EU VAT Digital Goods Handling" msgstr "" -#: includes/data/eu-vat-number-settings.php:102 +#: includes/data/eu-vat-number-settings.php:109 #. translators: %1$s Opening anchor tag, %2$s Closing anchor tag msgid "" -"EU VAT laws are changing for digital goods from the 1st Jan 2015 (affecting " -"B2C transactions only). The VAT on digital goods must be calculated based " -"on the customer location, and you need to collect evidence of this (IP " -"address and Billing Address). You also need to setup VAT rates to charge " -"the correct amount. %1$sRead this guide%2$s for instructions on doing this." +"As of January 1st, 2015, EU VAT laws have been changed for digital goods " +"(affecting B2C transactions only). The VAT on digital goods must be " +"calculated based on the customer location, and you need to collect evidence " +"of this (IP address and Billing Address). You also need to setup VAT rates " +"to charge the correct amount. %1$sRead this guide%2$s for instructions on " +"doing this." msgstr "" -#: includes/data/eu-vat-number-settings.php:106 +#: includes/data/eu-vat-number-settings.php:113 msgid "Tax Classes for Digital Goods" msgstr "" -#: includes/data/eu-vat-number-settings.php:107 +#: includes/data/eu-vat-number-settings.php:114 msgid "" "This option tells the plugin which of your tax classes are for digital " "goods. This affects the taxable location of the user as of 1st Jan 2015." msgstr "" -#: includes/data/eu-vat-number-settings.php:116 +#: includes/data/eu-vat-number-settings.php:123 msgid "Select some tax classes" msgstr "" -#: includes/data/eu-vat-number-settings.php:120 +#: includes/data/eu-vat-number-settings.php:127 msgid "Collect and Validate Evidence" msgstr "" -#: includes/data/eu-vat-number-settings.php:121 +#: includes/data/eu-vat-number-settings.php:128 msgid "" "This validates the customer IP address against their billing address, and " "prompts the customer to self-declare their address if they do not match. " diff --git a/src/components/vat-input/index.js b/src/components/vat-input/index.js index 47e3e43..ed5a329 100644 --- a/src/components/vat-input/index.js +++ b/src/components/vat-input/index.js @@ -1,3 +1,4 @@ +/* eslint-disable camelcase */ /** * External dependencies */ @@ -22,7 +23,8 @@ const VatInput = ( props ) => { props.validation; const { checkoutExtensionData } = props; - const { billingAddress, extensions } = useStoreCart(); + const { billingAddress, shippingAddress, needsShipping, extensions } = + useStoreCart(); const { b2b_required, eu_countries, @@ -30,7 +32,14 @@ const VatInput = ( props ) => { input_label, input_description, failure_handler, - } = wc_eu_vat_params; + use_shipping_country, + } = window.wc_eu_vat_params; + + // Get the country from the shipping address if use_shipping_country is true and shipping is required. + const country = + use_shipping_country && needsShipping + ? shippingAddress.country + : billingAddress.country; const { title = __( 'VAT Number', 'woocommerce-eu-vat-number' ), @@ -53,16 +62,13 @@ const VatInput = ( props ) => { extensions[ 'woocommerce-eu-vat-number' ]?.vat_number ); - const [ required ] = useState( - b2b_required === 'yes' && - eu_countries.indexOf( billingAddress.country ) !== -1 + const [ required, setRequired ] = useState( + b2b_required === 'yes' && eu_countries.indexOf( country ) !== -1 ); const [ available, setAvailable ] = useState( - eu_countries.indexOf( billingAddress.country ) !== -1 - ); - const [ showGBNotice, setShowGBNotice ] = useState( - billingAddress.country === 'GB' + eu_countries.indexOf( country ) !== -1 ); + const [ showGBNotice, setShowGBNotice ] = useState( country === 'GB' ); const textInputId = 'billing_vat_number'; const validationErrorId = 'billing_vat_number_error'; @@ -78,6 +84,15 @@ const VatInput = ( props ) => { setVat( extensions[ 'woocommerce-eu-vat-number' ]?.vat_number ); }, [ extensions[ 'woocommerce-eu-vat-number' ]?.vat_number ] ); + /** + * Sets the VAT field required if the country is in the EU and b2b_required is set to yes. + */ + useEffect( () => { + setRequired( + b2b_required === 'yes' && eu_countries.indexOf( country ) !== -1 + ); + }, [ b2b_required, country, eu_countries ] ); + /** * This effect sets location_confirmation, it is required regardless of shouldValidateIp, or else the API will give * us an error due to missing parameters. @@ -95,9 +110,9 @@ const VatInput = ( props ) => { * Also update whether the GB Notice should show. */ useEffect( () => { - setAvailable( eu_countries.indexOf( billingAddress.country ) !== -1 ); - setShowGBNotice( billingAddress.country === 'GB' ); - }, [ eu_countries, billingAddress.country ] ); + setAvailable( eu_countries.indexOf( country ) !== -1 ); + setShowGBNotice( country === 'GB' ); + }, [ eu_countries, country ] ); /** * This effect handles setting the validation error immediately. @@ -118,7 +133,7 @@ const VatInput = ( props ) => { hidden: true, }, } ); - }, [ required, setValidationErrors, validationErrorId ] ); + }, [ required, setValidationErrors, validationErrorId, vat ] ); const verifyVat = () => { if ( vat === previousVat ) { @@ -163,10 +178,7 @@ const VatInput = ( props ) => { if ( typeof vat === 'string' && vat.length > 0 && - ( ( ! validateCountryVatNumberFormat( - billingAddress.country, - vat - ) && + ( ( ! validateCountryVatNumberFormat( country, vat ) && failure_handler === 'reject' ) || ( ! extensions[ 'woocommerce-eu-vat-number' ]?.validation ?.valid && diff --git a/src/hooks.js b/src/hooks.js index b1d0583..93bb205 100644 --- a/src/hooks.js +++ b/src/hooks.js @@ -12,6 +12,7 @@ export const useStoreCart = () => { return { cartItems: cartData.items, billingAddress: cartData.billingAddress, + shippingAddress: cartData.shippingAddress, extensions: cartData.extensions, needsShipping: cartData.needsShipping, }; diff --git a/woocommerce-eu-vat-number.php b/woocommerce-eu-vat-number.php index 494add3..d4443ca 100644 --- a/woocommerce-eu-vat-number.php +++ b/woocommerce-eu-vat-number.php @@ -4,15 +4,15 @@ * Requires Plugins: woocommerce * Plugin URI: https://woocommerce.com/products/eu-vat-number/ * Description: The EU VAT Number extension lets you collect and validate EU VAT numbers during checkout to identify B2B transactions verses B2C. IP Addresses can also be validated to ensure they match the billing address. EU businesses with a valid VAT number can have their VAT removed prior to payment. - * Version: 2.9.3 + * Version: 2.9.4 * Author: WooCommerce * Author URI: https://woocommerce.com/ * Text Domain: woocommerce-eu-vat-number * Domain Path: /languages * Requires at least: 6.3 * Tested up to: 6.5 - * WC requires at least: 8.6 - * WC tested up to: 8.8 + * WC requires at least: 8.7 + * WC tested up to: 8.9 * Requires PHP: 7.4 * PHP tested up to: 8.3 * @@ -26,7 +26,7 @@ // phpcs:disable WordPress.Files.FileName -define( 'WC_EU_VAT_VERSION', '2.9.3' ); // WRCS: DEFINED_VERSION. +define( 'WC_EU_VAT_VERSION', '2.9.4' ); // WRCS: DEFINED_VERSION. define( 'WC_EU_VAT_FILE', __FILE__ ); define( 'WC_EU_ABSPATH', __DIR__ . '/' ); define( 'WC_EU_VAT_PLUGIN_URL', untrailingslashit( plugins_url( basename( plugin_dir_path( __FILE__ ) ), basename( __FILE__ ) ) ) ); @@ -41,7 +41,7 @@ class WC_EU_VAT_Number_Init { * * @var string */ - const WC_MIN_VERSION = '8.5'; + const WC_MIN_VERSION = '8.7'; /** * Constructor.
_billing_country ); ?>_billing_country, '', $vat_number ) ); ?>_shipping_country ); ?> total_sales, array( 'currency', $ec_sale->_order_currency ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
tax_amount + $tax_row->refunded_tax_amount ); ?>