order-review.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. /**
  2. * Copyright © Magento, Inc. All rights reserved.
  3. * See COPYING.txt for license details.
  4. */
  5. define([
  6. 'jquery',
  7. 'Magento_Ui/js/modal/alert',
  8. 'jquery/ui',
  9. 'mage/translate',
  10. 'mage/mage',
  11. 'mage/validation'
  12. ], function ($, alert) {
  13. 'use strict';
  14. $.widget('mage.orderReview', {
  15. options: {
  16. orderReviewSubmitSelector: '#review-button',
  17. shippingSelector: '#shipping_method',
  18. shippingSubmitFormSelector: null,
  19. updateOrderSelector: '#update-order',
  20. billingAsShippingSelector: '#billing\\:as_shipping',
  21. updateContainerSelector: '#details-reload',
  22. waitLoadingContainer: '#review-please-wait',
  23. shippingMethodContainer: '#shipping-method-container',
  24. agreementSelector: 'div.checkout-agreements input',
  25. isAjax: false,
  26. updateShippingMethodSubmitSelector: '#update-shipping-method-submit',
  27. reviewSubmitSelector: '#review-submit',
  28. shippingMethodUpdateUrl: null,
  29. updateOrderSubmitUrl: null,
  30. canEditShippingMethod: false
  31. },
  32. /**
  33. * Widget instance properties
  34. */
  35. triggerPropertyChange: true,
  36. isShippingSubmitForm: false,
  37. /** @inheritdoc */
  38. _create: function () {
  39. var isDisable;
  40. //change handler for ajaxEnabled
  41. if (this.options.isAjax) {
  42. this._submitOrder = this._ajaxSubmitOrder;
  43. }
  44. this.element.on('click', this.options.orderReviewSubmitSelector, $.proxy(this._submitOrder, this))
  45. .on('click', this.options.billingAsShippingSelector, $.proxy(this._shippingTobilling, this))
  46. .on('change',
  47. this.options.shippingSelector,
  48. $.proxy(this._submitUpdateOrder,
  49. this,
  50. this.options.updateOrderSubmitUrl,
  51. this.options.updateContainerSelector
  52. )
  53. ).find(this.options.updateOrderSelector).on('click', $.proxy(this._updateOrderHandler, this)).end()
  54. .find(this.options.updateShippingMethodSubmitSelector).hide().end()
  55. .find(this.options.reviewSubmitSelector).hide();
  56. this._shippingTobilling();
  57. if ($(this.options.shippingSubmitFormSelector).length && this.options.canEditShippingMethod) {
  58. this.isShippingSubmitForm = true;
  59. $(this.options.shippingSubmitFormSelector)
  60. .find(this.options.updateShippingMethodSubmitSelector).hide().end()
  61. .on('change',
  62. this.options.shippingSelector,
  63. $.proxy(
  64. this._submitUpdateOrder,
  65. this,
  66. $(this.options.shippingSubmitFormSelector).prop('action'),
  67. this.options.updateContainerSelector
  68. )
  69. );
  70. this._updateOrderSubmit(!$(this.options.shippingSubmitFormSelector)
  71. .find(this.options.shippingSelector).val());
  72. } else {
  73. isDisable = this.isShippingSubmitForm && this.element.find(this.options.shippingSelector).val();
  74. this.element
  75. .on('input propertychange', ':input[name]',
  76. $.proxy(this._updateOrderSubmit, this, isDisable, this._onShippingChange)
  77. ).find('select').not(this.options.shippingSelector).on('change', this._propertyChange);
  78. this._updateOrderSubmit(isDisable);
  79. }
  80. },
  81. /**
  82. * show ajax loader
  83. */
  84. _ajaxBeforeSend: function () {
  85. this.element.find(this.options.waitLoadingContainer).show();
  86. },
  87. /**
  88. * hide ajax loader
  89. */
  90. _ajaxComplete: function () {
  91. this.element.find(this.options.waitLoadingContainer).hide();
  92. },
  93. /**
  94. * trigger propertychange for input type select
  95. */
  96. _propertyChange: function () {
  97. $(this).trigger('propertychange');
  98. },
  99. /**
  100. * trigger change for the update of shipping methods from server
  101. */
  102. _updateOrderHandler: function () {
  103. $(this.options.shippingSelector).trigger('change');
  104. },
  105. /**
  106. * Attempt to submit order
  107. */
  108. _submitOrder: function () {
  109. if (this._validateForm()) {
  110. this.element.find(this.options.updateOrderSelector).fadeTo(0, 0.5)
  111. .end().find(this.options.waitLoadingContainer).show()
  112. .end().submit();
  113. this._updateOrderSubmit(true);
  114. }
  115. },
  116. /**
  117. * Attempt to ajax submit order
  118. */
  119. _ajaxSubmitOrder: function () {
  120. if (this.element.find(this.options.waitLoadingContainer).is(':visible')) {
  121. return false;
  122. }
  123. $.ajax({
  124. url: this.element.prop('action'),
  125. type: 'post',
  126. context: this,
  127. data: {
  128. isAjax: 1
  129. },
  130. dataType: 'json',
  131. beforeSend: this._ajaxBeforeSend,
  132. complete: this._ajaxComplete,
  133. /** @inheritdoc */
  134. success: function (response) {
  135. var msg;
  136. if ($.type(response) === 'object' && !$.isEmptyObject(response)) {
  137. if (response['error_messages']) {
  138. this._ajaxComplete();
  139. msg = response['error_messages'];
  140. /* eslint-disable max-depth */
  141. if (msg) {
  142. if ($.type(msg) === 'array') {
  143. msg = msg.join('\n');
  144. }
  145. }
  146. /* eslint-enablemax-depth */
  147. alert({
  148. content: msg
  149. });
  150. return false;
  151. }
  152. if (response.redirect) {
  153. $.mage.redirect(response.redirect);
  154. return false;
  155. } else if (response.success) {
  156. $.mage.redirect(this.options.successUrl);
  157. return false;
  158. }
  159. this._ajaxComplete();
  160. alert({
  161. content: $.mage.__('Sorry, something went wrong.')
  162. });
  163. }
  164. },
  165. /** @inheritdoc */
  166. error: function () {
  167. alert({
  168. content: $.mage.__('Sorry, something went wrong. Please try again later.')
  169. });
  170. this._ajaxComplete();
  171. }
  172. });
  173. },
  174. /**
  175. * Validate Order form
  176. */
  177. _validateForm: function () {
  178. this.element.find(this.options.agreementSelector).off('change').on('change', $.proxy(function () {
  179. var isValid = this._validateForm();
  180. this._updateOrderSubmit(!isValid);
  181. }, this));
  182. if (this.element.data('mageValidation')) {
  183. return this.element.validation().valid();
  184. }
  185. return true;
  186. },
  187. /**
  188. * Check/Set whether order can be submitted
  189. * Also disables form submission element, if any
  190. * @param {*} shouldDisable - whether should prevent order submission explicitly
  191. * @param {Function} [fn] - function for shipping change handler
  192. * @param {*} [*] - if true the property change will be set to true
  193. */
  194. _updateOrderSubmit: function (shouldDisable, fn) {
  195. this._toggleButton(this.options.orderReviewSubmitSelector, shouldDisable);
  196. if ($.type(fn) === 'function') {
  197. fn.call(this);
  198. }
  199. },
  200. /**
  201. * Enable/Disable button
  202. * @param {jQuery} button - button selector to be toggled
  203. * @param {*} disable - boolean for toggling
  204. */
  205. _toggleButton: function (button, disable) {
  206. $(button).prop({
  207. 'disabled': disable
  208. }).toggleClass('no-checkout', disable).fadeTo(0, disable ? 0.5 : 1);
  209. },
  210. /**
  211. * Copy element value from shipping to billing address
  212. * @param {jQuery.Event} e - optional
  213. */
  214. _shippingTobilling: function (e) {
  215. var isChecked, opacity;
  216. if (this.options.shippingSubmitFormSelector) {
  217. return false;
  218. }
  219. isChecked = $(this.options.billingAsShippingSelector).is(':checked');
  220. opacity = isChecked ? 0.5 : 1;
  221. if (isChecked) {
  222. this.element.validation('clearError', ':input[name^="billing"]');
  223. }
  224. $(':input[name^="shipping"]', this.element).each($.proxy(function (key, value) {
  225. var fieldObj = $(value.id.replace('shipping:', '#billing\\:'));
  226. if (isChecked) {
  227. fieldObj = fieldObj.val($(value).val());
  228. }
  229. fieldObj.prop({
  230. 'readonly': isChecked,
  231. 'disabled': isChecked
  232. }).fadeTo(0, opacity);
  233. if (fieldObj.is('select')) {
  234. this.triggerPropertyChange = false;
  235. fieldObj.trigger('change');
  236. }
  237. }, this));
  238. if (isChecked || e) {
  239. this._updateOrderSubmit(true);
  240. }
  241. this.triggerPropertyChange = true;
  242. },
  243. /**
  244. * Dispatch an ajax request of Update Order submission
  245. * @param {*} url - url where to submit shipping method
  246. * @param {*} resultId - id of element to be updated
  247. */
  248. _submitUpdateOrder: function (url, resultId) {
  249. var isChecked, formData, callBackResponseHandler, shippingMethod;
  250. if (this.element.find(this.options.waitLoadingContainer).is(':visible')) {
  251. return false;
  252. }
  253. isChecked = $(this.options.billingAsShippingSelector).is(':checked');
  254. formData = null;
  255. callBackResponseHandler = null;
  256. shippingMethod = $.trim($(this.options.shippingSelector).val());
  257. this._shippingTobilling();
  258. if (url && resultId && shippingMethod) {
  259. this._updateOrderSubmit(true);
  260. this._toggleButton(this.options.updateOrderSelector, true);
  261. // form data and callBack updated based on the shipping Form element
  262. if (this.isShippingSubmitForm) {
  263. formData = $(this.options.shippingSubmitFormSelector).serialize() + '&isAjax=true';
  264. /**
  265. * @param {Object} response
  266. */
  267. callBackResponseHandler = function (response) {
  268. $(resultId).html(response);
  269. this._updateOrderSubmit(false);
  270. this._ajaxComplete();
  271. };
  272. } else {
  273. formData = this.element.serialize() + '&isAjax=true';
  274. /**
  275. * @param {Object} response
  276. */
  277. callBackResponseHandler = function (response) {
  278. $(resultId).html(response);
  279. this._ajaxShippingUpdate(shippingMethod);
  280. };
  281. }
  282. if (isChecked) {
  283. $(this.options.shippingSelect).prop('disabled', true);
  284. }
  285. $.ajax({
  286. url: url,
  287. type: 'post',
  288. context: this,
  289. beforeSend: this._ajaxBeforeSend,
  290. data: formData,
  291. success: callBackResponseHandler
  292. });
  293. }
  294. },
  295. /**
  296. * Update Shipping Methods Element from server
  297. * @param {*} shippingMethod
  298. */
  299. _ajaxShippingUpdate: function (shippingMethod) {
  300. $.ajax({
  301. url: this.options.shippingMethodUpdateUrl,
  302. data: {
  303. isAjax: true,
  304. 'shipping_method': shippingMethod
  305. },
  306. type: 'post',
  307. context: this,
  308. /** @inheritdoc */
  309. success: function (response) {
  310. $(this.options.shippingMethodContainer).parent().html(response);
  311. this._toggleButton(this.options.updateOrderSelector, false);
  312. this._updateOrderSubmit(false);
  313. },
  314. complete: this._ajaxComplete
  315. });
  316. },
  317. /**
  318. * Actions on change Shipping Address data
  319. */
  320. _onShippingChange: function () {
  321. if (this.triggerPropertyChange && $.trim($(this.options.shippingSelector).val())) {
  322. this.element.find(this.options.shippingSelector).hide().end()
  323. .find(this.options.shippingSelector + '_update').show();
  324. }
  325. }
  326. });
  327. return $.mage.orderReview;
  328. });