This commit is contained in:
2025-03-31 20:17:05 +02:00
parent a03df0b268
commit d4d4c0c09d
1617 changed files with 1106381 additions and 268 deletions

View File

@@ -0,0 +1,52 @@
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
*/
/**
* Checks if correct addresses are selected.
* There is a case when options list cannot contain cart addresses 'selected' values
* because those are outdated in db (e.g. deleted after cart creation or country is disabled)
*
* @param {Array} addresses
*
* @returns {boolean}
*/
export const ValidateAddresses = (addresses) => {
let deliveryValid = false;
let invoiceValid = false;
addresses.forEach((address) => {
if (address.delivery) {
deliveryValid = true;
}
if (address.invoice) {
invoiceValid = true;
}
});
return deliveryValid && invoiceValid;
};
export default ValidateAddresses;

View File

@@ -0,0 +1,180 @@
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
*/
import createOrderMap from './create-order-map';
import Router from '../../../components/router';
const {$} = window;
/**
* Renders Delivery & Invoice addresses select
*/
export default class AddressesRenderer {
constructor() {
this.router = new Router();
}
/**
* @param {Array} addresses
* @param {int} cartId
*/
render(addresses, cartId) {
this.cleanAddresses();
if (addresses.length === 0) {
this.hideAddressesContent();
this.showEmptyAddressesWarning();
this.showAddressesBlock();
return;
}
this.showAddressesContent();
this.hideEmptyAddressesWarning();
Object.values(addresses).forEach((address) => {
this.renderDeliveryAddress(address, cartId);
this.renderInvoiceAddress(address, cartId);
});
this.showAddressesBlock();
}
/**
* Renders delivery address content
*
* @param address
* @param cartId
*
* @private
*/
renderDeliveryAddress(address, cartId) {
const deliveryAddressOption = {
value: address.addressId,
text: address.alias,
};
if (address.delivery) {
$(createOrderMap.deliveryAddressDetails).html(address.formattedAddress);
deliveryAddressOption.selected = 'selected';
$(createOrderMap.deliveryAddressEditBtn).prop('href', this.router.generate('admin_cart_addresses_edit', {
addressId: address.addressId,
cartId,
addressType: 'delivery',
liteDisplaying: 1,
submitFormAjax: 1,
}));
}
$(createOrderMap.deliveryAddressSelect).append($('<option>', deliveryAddressOption));
}
/**
* Renders invoice address content
*
* @param address
* @param cartId
*
* @private
*/
renderInvoiceAddress(address, cartId) {
const invoiceAddressOption = {
value: address.addressId,
text: address.alias,
};
if (address.invoice) {
$(createOrderMap.invoiceAddressDetails).html(address.formattedAddress);
invoiceAddressOption.selected = 'selected';
$(createOrderMap.invoiceAddressEditBtn).prop('href', this.router.generate('admin_cart_addresses_edit', {
addressId: address.addressId,
cartId,
addressType: 'invoice',
liteDisplaying: 1,
submitFormAjax: 1,
}));
}
$(createOrderMap.invoiceAddressSelect).append($('<option>', invoiceAddressOption));
}
/**
* Shows addresses block
*
* @private
*/
showAddressesBlock() {
$(createOrderMap.addressesBlock).removeClass('d-none');
}
/**
* Empties addresses content
*
* @private
*/
cleanAddresses() {
$(createOrderMap.deliveryAddressDetails).empty();
$(createOrderMap.deliveryAddressSelect).empty();
$(createOrderMap.invoiceAddressDetails).empty();
$(createOrderMap.invoiceAddressSelect).empty();
}
/**
* Shows addresses content and hides warning
*
* @private
*/
showAddressesContent() {
$(createOrderMap.addressesContent).removeClass('d-none');
$(createOrderMap.addressesWarning).addClass('d-none');
}
/**
* Hides addresses content and shows warning
*
* @private
*/
hideAddressesContent() {
$(createOrderMap.addressesContent).addClass('d-none');
$(createOrderMap.addressesWarning).removeClass('d-none');
}
/**
* Shows warning empty addresses warning
*
* @private
*/
showEmptyAddressesWarning() {
$(createOrderMap.addressesWarning).removeClass('d-none');
}
/**
* Hides empty addresses warning
*
* @private
*/
hideEmptyAddressesWarning() {
$(createOrderMap.addressesWarning).addClass('d-none');
}
}

View File

@@ -0,0 +1,219 @@
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
*/
import Router from '@components/router';
import {EventEmitter} from '@components/event-emitter';
import eventMap from '@pages/order/create/event-map';
import createOrderMap from './create-order-map';
const {$} = window;
/**
* Provides ajax calls for cart editing actions
* Each method emits an event with updated cart information after success.
*/
export default class CartEditor {
constructor() {
this.router = new Router();
}
/**
* Changes cart addresses
*
* @param {Number} cartId
* @param {Object} addresses
*/
changeCartAddresses(cartId, addresses) {
$.post(this.router.generate('admin_carts_edit_addresses', {cartId}), addresses)
.then((cartInfo) => EventEmitter.emit(eventMap.cartAddressesChanged, cartInfo))
.catch((response) => window.showErrorMessage(response.responseJSON.message));
}
/**
* Modifies cart delivery option
*
* @param {Number} cartId
* @param {Number} value
*/
changeDeliveryOption(cartId, value) {
$.post(this.router.generate('admin_carts_edit_carrier', {cartId}), {
carrierId: value,
}).then((cartInfo) => EventEmitter.emit(eventMap.cartDeliveryOptionChanged, cartInfo))
.catch((response) => window.showErrorMessage(response.responseJSON.message));
}
/**
* Changes cart free shipping value
*
* @param {Number} cartId
*/
updateDeliveryOptions(cartId) {
const freeShippingEnabled = $(createOrderMap.freeShippingSwitch)[1].checked;
const isAGiftEnabled = $(createOrderMap.isAGiftSwitchValue).val() === '1';
const useRecycledPackagingEnabled = $(createOrderMap.recycledPackagingSwitchValue).val() === '1';
const giftMessage = $(createOrderMap.giftMessageField).val();
$.post(this.router.generate('admin_carts_set_delivery_settings', {cartId}), {
freeShipping: freeShippingEnabled,
isAGift: isAGiftEnabled,
useRecycledPackaging: useRecycledPackagingEnabled,
giftMessage,
}).then((cartInfo) => EventEmitter.emit(eventMap.cartDeliverySettingChanged, cartInfo))
.catch((response) => window.showErrorMessage(response.responseJSON.message));
}
/**
* Adds cart rule to cart
*
* @param {Number} cartRuleId
* @param {Number} cartId
*/
addCartRuleToCart(cartRuleId, cartId) {
$.post(this.router.generate('admin_carts_add_cart_rule', {cartId}), {
cartRuleId,
}).then((cartInfo) => EventEmitter.emit(eventMap.cartRuleAdded, cartInfo))
.catch((response) => EventEmitter.emit(eventMap.cartRuleFailedToAdd, response.responseJSON.message));
}
/**
* Removes cart rule from cart
*
* @param {Number} cartRuleId
* @param {Number} cartId
*/
removeCartRuleFromCart(cartRuleId, cartId) {
$.post(this.router.generate('admin_carts_delete_cart_rule', {
cartId,
cartRuleId,
})).then((cartInfo) => EventEmitter.emit(eventMap.cartRuleRemoved, cartInfo))
.catch((response) => window.showErrorMessage(response.responseJSON.message));
}
/**
* Adds product to cart
*
* @param {Number} cartId
* @param {Object} data
*/
addProduct(cartId, data) {
let fileSizeHeader = '';
if (!$.isEmptyObject(data.fileSizes)) {
fileSizeHeader = JSON.stringify(data.fileSizes);
}
$.ajax(this.router.generate('admin_carts_add_product', {cartId}), {
headers: {
// Adds custom headers with submitted file sizes, to track if all files reached server side.
'file-sizes': fileSizeHeader,
},
method: 'POST',
data: data.product,
processData: false,
contentType: false,
}).then((cartInfo) => EventEmitter.emit(eventMap.productAddedToCart, cartInfo))
.catch((response) => EventEmitter.emit(eventMap.productAddToCartFailed, response.responseJSON.message));
}
/**
* Removes product from cart
*
* @param {Number} cartId
* @param {Object} product
*/
removeProductFromCart(cartId, product) {
$.post(this.router.generate('admin_carts_delete_product', {cartId}), {
productId: product.productId,
attributeId: product.attributeId,
customizationId: product.customizationId,
}).then((cartInfo) => EventEmitter.emit(eventMap.productRemovedFromCart, {cartInfo, product}))
.catch((response) => window.showErrorMessage(response.responseJSON.message));
}
/**
* Changes product price in cart
*
* @param {Number} cartId
* @param {Number} customerId
* @param {Object} product the updated product
*/
changeProductPrice(cartId, customerId, product) {
$.post(this.router.generate('admin_carts_edit_product_price', {
cartId,
productId: product.productId,
productAttributeId: product.attributeId,
}), {
newPrice: product.price,
customerId,
}).then((cartInfo) => EventEmitter.emit(eventMap.productPriceChanged, cartInfo))
.catch((response) => window.showErrorMessage(response.responseJSON.message));
}
/**
* Updates product quantity in cart
*
* @param cartId
* @param product
*/
changeProductQty(cartId, product) {
$.post(this.router.generate('admin_carts_edit_product_quantity', {
cartId,
productId: product.productId,
}), {
newQty: product.newQty,
attributeId: product.attributeId,
customizationId: product.customizationId,
}).then((cartInfo) => EventEmitter.emit(eventMap.productQtyChanged, {cartInfo, product}))
.catch((response) => EventEmitter.emit(eventMap.productQtyChangeFailed, response));
}
/**
* Changes cart currency
*
* @param {Number} cartId
* @param {Number} currencyId
*/
changeCartCurrency(cartId, currencyId) {
$(createOrderMap.cartCurrencySelect).data('selectedCurrencyId', currencyId);
$.post(this.router.generate('admin_carts_edit_currency', {cartId}), {
currencyId,
}).then((cartInfo) => EventEmitter.emit(eventMap.cartCurrencyChanged, cartInfo))
.catch((response) => EventEmitter.emit(eventMap.cartCurrencyChangeFailed, response));
}
/**
* Changes cart language
*
* @param {Number} cartId
* @param {Number} languageId
*/
changeCartLanguage(cartId, languageId) {
$.post(this.router.generate('admin_carts_edit_language', {cartId}), {
languageId,
}).then((cartInfo) => EventEmitter.emit(eventMap.cartLanguageChanged, cartInfo))
.catch((response) => window.showErrorMessage(response.responseJSON.message));
}
}

View File

@@ -0,0 +1,82 @@
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
*/
import createOrderPageMap from '@pages/order/create/create-order-map';
import Router from '@components/router';
import {EventEmitter} from '@components/event-emitter';
import eventMap from '@pages/order/create/event-map';
const {$} = window;
/**
* Provides ajax calls for getting cart information
*/
export default class CartProvider {
constructor() {
this.$container = $(createOrderPageMap.orderCreationContainer);
this.router = new Router();
}
/**
* Gets cart information
*
* @param cartId
*
* @returns {jqXHR}. Object with cart information in response.
*/
getCart(cartId) {
$.get(this.router.generate('admin_carts_info', {cartId})).then((cartInfo) => {
EventEmitter.emit(eventMap.cartLoaded, cartInfo);
});
}
/**
* Gets existing empty cart or creates new empty cart for customer.
*
* @param customerId
*
* @returns {jqXHR}. Object with cart information in response
*/
loadEmptyCart(customerId) {
$.post(this.router.generate('admin_carts_create'), {
customerId,
}).then((cartInfo) => {
EventEmitter.emit(eventMap.cartLoaded, cartInfo);
});
}
/**
* Duplicates cart from provided order
*
* @param orderId
*
* @returns {jqXHR}. Object with cart information in response
*/
duplicateOrderCart(orderId) {
$.post(this.router.generate('admin_orders_duplicate_cart', {orderId})).then((cartInfo) => {
EventEmitter.emit(eventMap.cartLoaded, cartInfo);
});
}
}

View File

@@ -0,0 +1,149 @@
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
*/
import CartEditor from '@pages/order/create/cart-editor';
import CartRulesRenderer from '@pages/order/create/cart-rules-renderer';
import {EventEmitter} from '@components/event-emitter';
import eventMap from '@pages/order/create/event-map';
import Router from '@components/router';
import SummaryRenderer from '@pages/order/create/summary-renderer';
import ShippingRenderer from '@pages/order/create/shipping-renderer';
import ProductRenderer from '@pages/order/create/product-renderer';
const {$} = window;
/**
* Responsible for searching cart rules and managing cart rules search block
*/
export default class CartRuleManager {
constructor() {
this.activeSearchRequest = null;
this.router = new Router();
this.cartRulesRenderer = new CartRulesRenderer();
this.cartEditor = new CartEditor();
this.summaryRenderer = new SummaryRenderer();
this.shippingRenderer = new ShippingRenderer();
this.productRenderer = new ProductRenderer();
this.initListeners();
return {
search: (searchPhrase) => this.search(searchPhrase),
stopSearching: () => this.cartRulesRenderer.hideResultsDropdown(),
addCartRuleToCart: (cartRuleId, cartId) => this.cartEditor.addCartRuleToCart(cartRuleId, cartId),
removeCartRuleFromCart: (cartRuleId, cartId) => this.cartEditor.removeCartRuleFromCart(cartRuleId, cartId),
};
}
/**
* Initiates event listeners for cart rule actions
*
* @private
*/
initListeners() {
this.onCartRuleSearch();
this.onAddCartRuleToCart();
this.onAddCartRuleToCartFailure();
this.onRemoveCartRuleFromCart();
}
/**
* Listens for cart rule search action
*
* @private
*/
onCartRuleSearch() {
EventEmitter.on(eventMap.cartRuleSearched, (cartRules) => {
this.cartRulesRenderer.renderSearchResults(cartRules);
});
}
/**
* Listens event of add cart rule to cart action
*
* @private
*/
onAddCartRuleToCart() {
EventEmitter.on(eventMap.cartRuleAdded, (cartInfo) => {
const cartIsEmpty = cartInfo.products.length === 0;
this.cartRulesRenderer.renderCartRulesBlock(cartInfo.cartRules, cartIsEmpty);
this.productRenderer.renderList(cartInfo.products);
this.shippingRenderer.render(cartInfo.shipping, cartIsEmpty);
this.summaryRenderer.render(cartInfo);
});
}
/**
* Listens event when add cart rule to cart fails
*
* @private
*/
onAddCartRuleToCartFailure() {
EventEmitter.on(eventMap.cartRuleFailedToAdd, (message) => {
this.cartRulesRenderer.displayErrorMessage(message);
});
}
/**
* Listens event for remove cart rule from cart action
*
* @private
*/
onRemoveCartRuleFromCart() {
EventEmitter.on(eventMap.cartRuleRemoved, (cartInfo) => {
const cartIsEmpty = cartInfo.products.length === 0;
this.shippingRenderer.render(cartInfo.shipping, cartIsEmpty);
this.cartRulesRenderer.renderCartRulesBlock(cartInfo.cartRules, cartIsEmpty);
this.summaryRenderer.render(cartInfo);
this.productRenderer.renderList(cartInfo.products);
});
}
/**
* Searches for cart rules by search phrase
*
* @private
*/
search(searchPhrase) {
if (this.activeSearchRequest !== null) {
this.activeSearchRequest.abort();
}
this.activeSearchRequest = $.get(this.router.generate('admin_cart_rules_search'), {
search_phrase: searchPhrase,
});
this.activeSearchRequest.then((cartRules) => {
EventEmitter.emit(eventMap.cartRuleSearched, cartRules);
}).catch((e) => {
if (e.statusText === 'abort') {
return;
}
window.showErrorMessage(e.responseJSON.message);
});
}
}

View File

@@ -0,0 +1,238 @@
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
*/
import createOrderMap from './create-order-map';
const {$} = window;
/**
* Renders cart rules (cartRules) block
*/
export default class CartRulesRenderer {
constructor() {
this.$cartRulesBlock = $(createOrderMap.cartRulesBlock);
this.$cartRulesTable = $(createOrderMap.cartRulesTable);
this.$searchResultBox = $(createOrderMap.cartRulesSearchResultBox);
}
/**
* Responsible for rendering cartRules (a.k.a cart rules/discounts) block
*
* @param {Array} cartRules
* @param {Boolean} emptyCart
*/
renderCartRulesBlock(cartRules, emptyCart) {
this.hideErrorBlock();
// do not render cart rules block at all if cart has no products
if (emptyCart) {
this.hideCartRulesBlock();
return;
}
this.showCartRulesBlock();
// do not render cart rules list when there are no cart rules
if (cartRules.length === 0) {
this.hideCartRulesList();
return;
}
this.renderList(cartRules);
}
/**
* Responsible for rendering search results dropdown
*
* @param searchResults
*/
renderSearchResults(searchResults) {
this.clearSearchResults();
if (searchResults.cart_rules.length === 0) {
this.renderNotFound();
} else {
this.renderFoundCartRules(searchResults.cart_rules);
}
this.showResultsDropdown();
}
/**
* Displays error message bellow search input
*
* @param message
*/
displayErrorMessage(message) {
$(createOrderMap.cartRuleErrorText).text(message);
this.showErrorBlock();
}
/**
* Hides cart rules search result dropdown
*/
hideResultsDropdown() {
this.$searchResultBox.addClass('d-none');
}
/**
* Displays cart rules search result dropdown
*
* @private
*/
showResultsDropdown() {
this.$searchResultBox.removeClass('d-none');
}
/**
* Renders warning that no cart rule was found
*
* @private
*/
renderNotFound() {
const $template = $($(createOrderMap.cartRulesNotFoundTemplate).html()).clone();
this.$searchResultBox.html($template);
}
/**
* Empties cart rule search results block
*
* @private
*/
clearSearchResults() {
this.$searchResultBox.empty();
}
/**
* Renders found cart rules after search
*
* @param cartRules
*
* @private
*/
renderFoundCartRules(cartRules) {
const $cartRuleTemplate = $($(createOrderMap.foundCartRuleTemplate).html());
Object.values(cartRules).forEach((cartRule) => {
const $template = $cartRuleTemplate.clone();
let cartRuleName = cartRule.name;
if (cartRule.code !== '') {
cartRuleName = `${cartRule.name} - ${cartRule.code}`;
}
$template.text(cartRuleName);
$template.data('cart-rule-id', cartRule.cartRuleId);
this.$searchResultBox.append($template);
});
}
/**
* Responsible for rendering the list of cart rules
*
* @param {Array} cartRules
*
* @private
*/
renderList(cartRules) {
this.cleanCartRulesList();
const $cartRulesTableRowTemplate = $($(createOrderMap.cartRulesTableRowTemplate).html());
Object.values(cartRules).forEach((cartRule) => {
const $template = $cartRulesTableRowTemplate.clone();
$template.find(createOrderMap.cartRuleNameField).text(cartRule.name);
$template.find(createOrderMap.cartRuleDescriptionField).text(cartRule.description);
$template.find(createOrderMap.cartRuleValueField).text(cartRule.value);
$template.find(createOrderMap.cartRuleDeleteBtn).data('cart-rule-id', cartRule.cartRuleId);
this.$cartRulesTable.find('tbody').append($template);
});
this.showCartRulesList();
}
/**
* Shows error block
*
* @private
*/
showErrorBlock() {
$(createOrderMap.cartRuleErrorBlock).removeClass('d-none');
}
/**
* Hides error block
*
* @private
*/
hideErrorBlock() {
$(createOrderMap.cartRuleErrorBlock).addClass('d-none');
}
/**
* Shows cartRules block
*
* @private
*/
showCartRulesBlock() {
this.$cartRulesBlock.removeClass('d-none');
}
/**
* hide cartRules block
*
* @private
*/
hideCartRulesBlock() {
this.$cartRulesBlock.addClass('d-none');
}
/**
* Display the list block of cart rules
*
* @private
*/
showCartRulesList() {
this.$cartRulesTable.removeClass('d-none');
}
/**
* Hide list block of cart rules
*
* @private
*/
hideCartRulesList() {
this.$cartRulesTable.addClass('d-none');
}
/**
* remove items in cart rules list
*
* @private
*/
cleanCartRulesList() {
this.$cartRulesTable.find('tbody').empty();
}
}

View File

@@ -0,0 +1,184 @@
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
*/
/**
* Encapsulates selectors for "Create order" page
*/
export default {
productCustomizationFieldTypeFile: 0,
productCustomizationFieldTypeText: 1,
orderCreationContainer: '#order-creation-container',
requiredFieldMark: '.js-required-field-mark',
cartInfoWrapper: '#js-cart-info-wrapper',
// selectors related to customer block
customerSearchInput: '#customer-search-input',
customerSearchResultsBlock: '.js-customer-search-results',
customerSearchResultTemplate: '#customer-search-result-template',
customerSearchEmptyResultWarning: '#customer-search-empty-result-warn',
customerSearchLoadingNotice: '#customer-search-loading-notice',
customerAddBtn: '#customer-add-btn',
changeCustomerBtn: '.js-change-customer-btn',
customerSearchRow: '.js-search-customer-row',
chooseCustomerBtn: '.js-choose-customer-btn',
notSelectedCustomerSearchResults: '.js-customer-search-result:not(.border-success)',
customerSearchResultName: '.js-customer-name',
customerSearchResultEmail: '.js-customer-email',
customerSearchResultId: '.js-customer-id',
customerSearchResultBirthday: '.js-customer-birthday',
customerDetailsBtn: '.js-details-customer-btn',
customerSearchResultColumn: '.js-customer-search-result-col',
customerSearchBlock: '#customer-search-block',
customerCartsTab: '.js-customer-carts-tab',
customerOrdersTab: '.js-customer-orders-tab',
customerCartsTable: '#customer-carts-table',
customerCartsTableRowTemplate: '#customer-carts-table-row-template',
customerCheckoutHistory: '#customer-checkout-history',
customerOrdersTable: '#customer-orders-table',
customerOrdersTableRowTemplate: '#customer-orders-table-row-template',
cartRulesTable: '#cart-rules-table',
cartRulesTableRowTemplate: '#cart-rules-table-row-template',
useCartBtn: '.js-use-cart-btn',
cartDetailsBtn: '.js-cart-details-btn',
cartIdField: '.js-cart-id',
cartDateField: '.js-cart-date',
cartTotalField: '.js-cart-total',
useOrderBtn: '.js-use-order-btn',
orderDetailsBtn: '.js-order-details-btn',
orderIdField: '.js-order-id',
orderDateField: '.js-order-date',
orderProductsField: '.js-order-products',
orderTotalField: '.js-order-total-paid',
orderPaymentMethod: '.js-order-payment-method',
orderStatusField: '.js-order-status',
emptyListRowTemplate: '#js-empty-list-row',
loadingListRowTemplate: '#js-loading-list-row',
emptyListRow: '.js-empty-row',
// selectors related to cartRules block
cartRulesBlock: '#cart-rules-block',
cartRuleSearchInput: '#search-cart-rules-input',
cartRulesSearchResultBox: '#search-cart-rules-result-box',
cartRulesNotFoundTemplate: '#cart-rules-not-found-template',
foundCartRuleTemplate: '#found-cart-rule-template',
foundCartRuleListItem: '.js-found-cart-rule',
cartRuleNameField: '.js-cart-rule-name',
cartRuleDescriptionField: '.js-cart-rule-description',
cartRuleValueField: '.js-cart-rule-value',
cartRuleDeleteBtn: '.js-cart-rule-delete-btn',
cartRuleErrorBlock: '#js-cart-rule-error-block',
cartRuleErrorText: '#js-cart-rule-error-text',
// selectors related to addresses block
addressesBlock: '#addresses-block',
deliveryAddressDetails: '#delivery-address-details',
invoiceAddressDetails: '#invoice-address-details',
deliveryAddressSelect: '#delivery-address-select',
invoiceAddressSelect: '#invoice-address-select',
addressSelect: '.js-address-select',
addressesContent: '#addresses-content',
addressesWarning: '#addresses-warning',
deliveryAddressEditBtn: '#js-delivery-address-edit-btn',
invoiceAddressEditBtn: '#js-invoice-address-edit-btn',
addressAddBtn: '#js-add-address-btn',
// selectors related to summary block
summaryBlock: '#summary-block',
summaryTotalProducts: '.js-total-products',
summaryTotalDiscount: '.js-total-discounts',
summaryTotalShipping: '.js-total-shipping',
summaryTotalTaxes: '.js-total-taxes',
summaryTotalWithoutTax: '.js-total-without-tax',
summaryTotalWithTax: '.js-total-with-tax',
placeOrderCartIdField: '.js-place-order-cart-id',
processOrderLinkTag: '#js-process-order-link',
orderMessageField: '#js-order-message-wrap textarea',
sendProcessOrderEmailBtn: '#js-send-process-order-email-btn',
summarySuccessAlertBlock: '#js-summary-success-block',
summaryErrorAlertBlock: '#js-summary-error-block',
summarySuccessAlertText: '#js-summary-success-block .alert-text',
summaryErrorAlertText: '#js-summary-error-block .alert-text',
// selectors related to shipping block
shippingBlock: '#shipping-block',
shippingForm: '.js-shipping-form',
noCarrierBlock: '.js-no-carrier-block',
deliveryOptionSelect: '#delivery-option-select',
totalShippingField: '.js-total-shipping-tax-inc',
freeShippingSwitch: '.js-free-shipping-switch',
recycledPackagingSwitch: '.js-recycled-packaging-switch',
recycledPackagingSwitchValue: '.js-recycled-packaging-switch:checked',
isAGiftSwitch: '.js-is-gift-switch',
isAGiftSwitchValue: '.js-is-gift-switch:checked',
giftMessageField: '#cart_gift_message',
// selectors related to cart block
cartBlock: '#cart-block',
cartCurrencySelect: '#js-cart-currency-select',
cartLanguageSelect: '#js-cart-language-select',
productSearch: '#product-search',
combinationsSelect: '#combination-select',
productResultBlock: '#product-search-results',
productSelect: '#product-select',
quantityInput: '#quantity-input',
inStockCounter: '.js-in-stock-counter',
combinationsTemplate: '#combinations-template',
combinationsRow: '.js-combinations-row',
productSelectRow: '.js-product-select-row',
productCustomFieldsContainer: '#js-custom-fields-container',
productCustomizationContainer: '#js-customization-container',
productCustomFileTemplate: '#js-product-custom-file-template',
productCustomTextTemplate: '#js-product-custom-text-template',
productCustomInputLabel: '.js-product-custom-input-label',
productCustomInput: '.js-product-custom-input',
quantityRow: '.js-quantity-row',
addToCartButton: '#add-product-to-cart-btn',
productsTable: '#products-table',
productsTableRowTemplate: '#products-table-row-template',
productsTableGiftRowTemplate: '#products-table-gift-row-template',
listedProductImageField: '.js-product-image',
listedProductNameField: '.js-product-name',
listedProductAttrField: '.js-product-attr',
listedProductReferenceField: '.js-product-ref',
listedProductUnitPriceInput: '.js-product-unit-input',
listedProductQtyInput: '.js-product-qty-input',
listedProductQtyStock: '.js-product-qty-stock',
listedProductGiftQty: '.js-product-gift-qty',
productTotalPriceField: '.js-product-total-price',
listedProductCustomizedTextTemplate: '#js-table-product-customized-text-template',
listedProductCustomizedFileTemplate: '#js-table-product-customized-file-template',
listedProductCustomizationName: '.js-customization-name',
listedProductCustomizationValue: '.js-customization-value',
listedProductDefinition: '.js-product-definition-td',
productRemoveBtn: '.js-product-remove-btn',
productTaxWarning: '.js-tax-warning',
noProductsFoundWarning: '.js-no-products-found',
searchingProductsNotice: '.js-searching-products',
productAddForm: '#js-add-product-form',
cartErrorAlertBlock: '#js-cart-error-block',
cartErrorAlertText: '#js-cart-error-block .alert-text',
createOrderButton: '#create-order-button',
};

View File

@@ -0,0 +1,644 @@
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
*/
import Router from '@components/router';
import {EventEmitter} from '@components/event-emitter';
import _ from 'lodash';
import createOrderMap from './create-order-map';
import CustomerManager from './customer-manager';
import ShippingRenderer from './shipping-renderer';
import CartProvider from './cart-provider';
import AddressesRenderer from './addresses-renderer';
import CartRulesRenderer from './cart-rules-renderer';
import CartEditor from './cart-editor';
import eventMap from './event-map';
import CartRuleManager from './cart-rule-manager';
import ProductManager from './product-manager';
import ProductRenderer from './product-renderer';
import SummaryRenderer from './summary-renderer';
import SummaryManager from './summary-manager';
import {ValidateAddresses} from './address-validator';
const {$} = window;
/**
* Page Object for "Create order" page
*/
export default class CreateOrderPage {
constructor() {
this.cartId = null;
this.customerId = null;
this.$container = $(createOrderMap.orderCreationContainer);
this.cartProvider = new CartProvider();
this.customerManager = new CustomerManager();
this.shippingRenderer = new ShippingRenderer();
this.addressesRenderer = new AddressesRenderer();
this.cartRulesRenderer = new CartRulesRenderer();
this.router = new Router();
this.cartEditor = new CartEditor();
this.cartRuleManager = new CartRuleManager();
this.productManager = new ProductManager();
this.productRenderer = new ProductRenderer();
this.summaryRenderer = new SummaryRenderer();
this.summaryManager = new SummaryManager();
this.initListeners();
this.loadCartFromUrlParams();
return {
refreshAddressesList: (refreshCartAddresses) => this.refreshAddressesList(refreshCartAddresses),
refreshCart: (refreshCart) => this.refreshCart(refreshCart),
search: (string) => this.customerManager.search(string),
};
}
/**
* Checks if correct addresses are selected.
* There is a case when options list cannot contain cart addresses 'selected' values
* because those are outdated in db (e.g. deleted after cart creation or country is disabled)
*
* @param {Array} addresses
*
* @returns {boolean}
*/
static validateSelectedAddresses(addresses) {
let deliveryValid = false;
let invoiceValid = false;
const keys = Object.keys(addresses);
for (let i = 0; i < keys.length; i += 1) {
const address = addresses[keys[i]];
if (address.delivery) {
deliveryValid = true;
}
if (address.invoice) {
invoiceValid = true;
}
if (deliveryValid && invoiceValid) {
return true;
}
}
return false;
}
/**
* Hides whole cart information wrapper
*/
hideCartInfo() {
$(createOrderMap.cartInfoWrapper).addClass('d-none');
}
/**
* Shows whole cart information wrapper
*/
showCartInfo() {
$(createOrderMap.cartInfoWrapper).removeClass('d-none');
}
/**
* Loads cart if query params contains valid cartId
*
* @private
*/
loadCartFromUrlParams() {
const urlParams = new URLSearchParams(window.location.search);
const cartId = Number(urlParams.get('cartId'));
if (!Number.isNaN(cartId) && cartId !== 0) {
this.cartProvider.getCart(cartId);
}
}
/**
* Initializes event listeners
*
* @private
*/
initListeners() {
this.$container.on('input', createOrderMap.customerSearchInput, (e) => this.initCustomerSearch(e));
this.$container.on('click', createOrderMap.chooseCustomerBtn, (e) => this.initCustomerSelect(e));
this.$container.on('click', createOrderMap.useCartBtn, (e) => this.initCartSelect(e));
this.$container.on('click', createOrderMap.useOrderBtn, (e) => this.initDuplicateOrderCart(e));
this.$container.on('input', createOrderMap.productSearch, (e) => this.initProductSearch(e));
this.$container.on('input', createOrderMap.cartRuleSearchInput, (e) => this.initCartRuleSearch(e));
this.$container.on('blur', createOrderMap.cartRuleSearchInput, () => this.cartRuleManager.stopSearching());
this.listenForCartEdit();
this.onCartLoaded();
this.onCustomersNotFound();
this.onCustomerSelected();
this.initAddressButtonsIframe();
this.initCartRuleButtonsIframe();
}
/**
* @private
*/
initAddressButtonsIframe() {
$(createOrderMap.addressAddBtn).fancybox({
type: 'iframe',
width: '90%',
height: '90%',
});
$(createOrderMap.invoiceAddressEditBtn).fancybox({
type: 'iframe',
width: '90%',
height: '90%',
});
$(createOrderMap.deliveryAddressEditBtn).fancybox({
type: 'iframe',
width: '90%',
height: '90%',
});
}
initCartRuleButtonsIframe() {
$('#js-add-cart-rule-btn').fancybox({
type: 'iframe',
width: '90%',
height: '90%',
});
}
/**
* Delegates actions to events associated with cart update (e.g. change cart address)
*
* @private
*/
listenForCartEdit() {
this.onCartAddressesChanged();
this.onDeliveryOptionChanged();
this.onDeliverySettingChanged();
this.addCartRuleToCart();
this.removeCartRuleFromCart();
this.onCartCurrencyChanged();
this.onCartLanguageChanged();
this.$container.on(
'change',
createOrderMap.deliveryOptionSelect,
(e) => this.cartEditor.changeDeliveryOption(this.cartId, e.currentTarget.value),
);
this.$container.on(
'change',
createOrderMap.freeShippingSwitch,
() => this.cartEditor.updateDeliveryOptions(this.cartId),
);
this.$container.on(
'change',
createOrderMap.recycledPackagingSwitch,
() => this.cartEditor.updateDeliveryOptions(this.cartId),
);
this.$container.on(
'change',
createOrderMap.isAGiftSwitch,
() => this.cartEditor.updateDeliveryOptions(this.cartId),
);
this.$container.on(
'blur',
createOrderMap.giftMessageField,
() => this.cartEditor.updateDeliveryOptions(this.cartId),
);
this.$container.on(
'click',
createOrderMap.addToCartButton,
() => this.productManager.addProductToCart(this.cartId),
);
this.$container.on(
'change',
createOrderMap.cartCurrencySelect,
(e) => this.cartEditor.changeCartCurrency(this.cartId, e.currentTarget.value),
);
this.$container.on(
'change',
createOrderMap.cartLanguageSelect,
(e) => this.cartEditor.changeCartLanguage(this.cartId, e.currentTarget.value),
);
this.$container.on(
'click',
createOrderMap.sendProcessOrderEmailBtn,
() => this.summaryManager.sendProcessOrderEmail(this.cartId),
);
this.$container.on('change', createOrderMap.listedProductUnitPriceInput, (e) => this.initProductChangePrice(e));
this.$container.on(
'change',
createOrderMap.listedProductQtyInput,
_.debounce((e) => {
const inputsQty = document.querySelectorAll(createOrderMap.listedProductQtyInput);
inputsQty.forEach((inputQty) => {
inputQty.setAttribute('disabled', true);
});
this.initProductChangeQty(e);
}, 500),
);
this.$container.on('change', createOrderMap.addressSelect, () => this.changeCartAddresses());
this.$container.on('click', createOrderMap.productRemoveBtn, (e) => this.initProductRemoveFromCart(e));
}
/**
* Listens for event when cart is loaded
*
* @private
*/
onCartLoaded() {
EventEmitter.on(eventMap.cartLoaded, (cartInfo) => {
this.cartId = cartInfo.cartId;
this.renderCartInfo(cartInfo);
if (cartInfo.addresses.length !== 0 && !ValidateAddresses(cartInfo.addresses)) {
this.changeCartAddresses();
}
this.customerManager.loadCustomerCarts(this.cartId);
this.customerManager.loadCustomerOrders();
});
}
/**
* Listens for event when no customers were found by search
*
* @private
*/
onCustomersNotFound() {
EventEmitter.on(eventMap.customersNotFound, () => {
this.hideCartInfo();
});
}
/**
* Listens for event when customer is selected
*
* @private
*/
onCustomerSelected() {
EventEmitter.on(eventMap.customerSelected, () => {
this.showCartInfo();
});
}
/**
* Listens for cart addresses update event
*
* @private
*/
onCartAddressesChanged() {
EventEmitter.on(eventMap.cartAddressesChanged, (cartInfo) => {
this.addressesRenderer.render(cartInfo.addresses, cartInfo.cartId);
this.cartRulesRenderer.renderCartRulesBlock(cartInfo.cartRules, cartInfo.products.length === 0);
this.shippingRenderer.render(cartInfo.shipping, cartInfo.products.length === 0);
this.productRenderer.renderList(cartInfo.products);
this.summaryRenderer.render(cartInfo);
});
}
/**
* Listens for cart delivery option update event
*
* @private
*/
onDeliveryOptionChanged() {
EventEmitter.on(eventMap.cartDeliveryOptionChanged, (cartInfo) => {
this.cartRulesRenderer.renderCartRulesBlock(cartInfo.cartRules, cartInfo.products.length === 0);
this.shippingRenderer.render(cartInfo.shipping, cartInfo.products.length === 0);
this.summaryRenderer.render(cartInfo);
this.productRenderer.renderList(cartInfo.products);
});
}
/**
* Listens for cart delivery setting update event
*
* @private
*/
onDeliverySettingChanged() {
EventEmitter.on(eventMap.cartDeliverySettingChanged, (cartInfo) => {
this.cartRulesRenderer.renderCartRulesBlock(cartInfo.cartRules, cartInfo.products.length === 0);
this.shippingRenderer.render(cartInfo.shipping, cartInfo.products.length === 0);
this.summaryRenderer.render(cartInfo);
});
}
/**
* Listens for cart language update event
*
* @private
*/
onCartLanguageChanged() {
EventEmitter.on(eventMap.cartLanguageChanged, (cartInfo) => {
this.preselectCartLanguage(cartInfo.langId);
this.renderCartInfo(cartInfo);
});
}
/**
* Listens for cart currency update event
*
* @private
*/
onCartCurrencyChanged() {
// on success
EventEmitter.on(eventMap.cartCurrencyChanged, (cartInfo) => {
this.renderCartInfo(cartInfo);
this.productRenderer.reset();
});
// on failure
EventEmitter.on(eventMap.cartCurrencyChangeFailed, (response) => {
this.productRenderer.renderCartBlockErrorAlert(response.responseJSON.message);
});
}
/**
* Init customer searching
*
* @param event
*
* @private
*/
initCustomerSearch(event) {
clearTimeout(this.timeoutId);
this.timeoutId = setTimeout(() => this.customerManager.search($(event.currentTarget).val()), 300);
}
/**
* Init selecting customer for which order is being created
*
* @param event
*
* @private
*/
initCustomerSelect(event) {
const customerId = this.customerManager.selectCustomer(event);
this.customerId = customerId;
this.cartProvider.loadEmptyCart(customerId);
}
/**
* Inits selecting cart to load
*
* @param event
*
* @private
*/
initCartSelect(event) {
const cartId = $(event.currentTarget).data('cart-id');
this.cartProvider.getCart(cartId);
}
/**
* Inits duplicating order cart
*
* @private
*/
initDuplicateOrderCart(event) {
const orderId = $(event.currentTarget).data('order-id');
this.cartProvider.duplicateOrderCart(orderId);
}
/**
* Triggers cart rule searching
*
* @private
*/
initCartRuleSearch(event) {
const searchPhrase = event.currentTarget.value;
clearTimeout(this.timeoutId);
this.timeoutId = setTimeout(() => this.cartRuleManager.search(searchPhrase), 300);
}
/**
* Triggers cart rule select
*
* @private
*/
addCartRuleToCart() {
this.$container.on('mousedown', createOrderMap.foundCartRuleListItem, (event) => {
// prevent blur event to allow selecting cart rule
event.preventDefault();
const cartRuleId = $(event.currentTarget).data('cart-rule-id');
this.cartRuleManager.addCartRuleToCart(cartRuleId, this.cartId);
// manually fire blur event after cart rule is selected.
})
.on('click', createOrderMap.foundCartRuleListItem, () => {
$(createOrderMap.cartRuleSearchInput).blur();
});
}
/**
* Triggers cart rule removal from cart
*
* @private
*/
removeCartRuleFromCart() {
this.$container.on('click', createOrderMap.cartRuleDeleteBtn, (event) => {
this.cartRuleManager.removeCartRuleFromCart($(event.currentTarget).data('cart-rule-id'), this.cartId);
});
}
/**
* Inits product searching
*
* @param event
*
* @private
*/
initProductSearch(event) {
const $productSearchInput = $(event.currentTarget);
const searchPhrase = $productSearchInput.val();
clearTimeout(this.timeoutId);
this.timeoutId = setTimeout(() => this.productManager.search(searchPhrase), 300);
}
/**
* Inits product removing from cart
*
* @param event
*
* @private
*/
initProductRemoveFromCart(event) {
const productQty = Number($(event.currentTarget).parents().find(createOrderMap.listedProductQtyInput).val());
const product = {
productId: $(event.currentTarget).data('product-id'),
attributeId: $(event.currentTarget).data('attribute-id'),
customizationId: $(event.currentTarget).data('customization-id'),
qtyToRemove: productQty,
};
this.productManager.removeProductFromCart(this.cartId, product);
}
/**
* Inits product in cart price change
*
* @param event
*
* @private
*/
initProductChangePrice(event) {
const product = {
productId: $(event.currentTarget).data('product-id'),
attributeId: $(event.currentTarget).data('attribute-id'),
customizationId: $(event.currentTarget).data('customization-id'),
price: $(event.currentTarget).val(),
};
this.productManager.changeProductPrice(this.cartId, this.customerId, product);
}
/**
* Inits product in cart quantity update
*
* @param event
*
* @private
*/
initProductChangeQty(event) {
const product = {
productId: $(event.currentTarget).data('product-id'),
attributeId: $(event.currentTarget).data('attribute-id'),
customizationId: $(event.currentTarget).data('customization-id'),
newQty: $(event.currentTarget).val(),
prevQty: $(event.currentTarget).data('prev-qty'),
};
if (
typeof product.productId !== 'undefined'
&& product.productId !== null
&& typeof product.attributeId !== 'undefined'
&& product.attributeId !== null
) {
this.productManager.changeProductQty(this.cartId, product);
} else {
const inputsQty = document.querySelectorAll(createOrderMap.listedProductQtyInput);
inputsQty.forEach((inputQty) => {
inputQty.disabled = false;
});
}
}
/**
* Renders cart summary on the page
*
* @param {Object} cartInfo
*
* @private
*/
renderCartInfo(cartInfo) {
this.addressesRenderer.render(cartInfo.addresses, cartInfo.cartId);
this.cartRulesRenderer.renderCartRulesBlock(cartInfo.cartRules, cartInfo.products.length === 0);
this.shippingRenderer.render(cartInfo.shipping, cartInfo.products.length === 0);
this.productRenderer.cleanCartBlockAlerts();
this.productRenderer.renderList(cartInfo.products);
this.summaryRenderer.render(cartInfo);
this.preselectCartCurrency(cartInfo.currencyId);
this.preselectCartLanguage(cartInfo.langId);
$(createOrderMap.cartBlock).removeClass('d-none');
$(createOrderMap.cartBlock).data('cartId', cartInfo.cartId);
}
/**
* Sets cart currency selection value
*
* @param currencyId
*
* @private
*/
preselectCartCurrency(currencyId) {
$(createOrderMap.cartCurrencySelect).val(currencyId);
}
/**
* Sets cart language selection value
*
* @param langId
*
* @private
*/
preselectCartLanguage(langId) {
$(createOrderMap.cartLanguageSelect).val(langId);
}
/**
* Changes cart addresses
*
* @private
*/
changeCartAddresses() {
const addresses = {
deliveryAddressId: $(createOrderMap.deliveryAddressSelect).val(),
invoiceAddressId: $(createOrderMap.invoiceAddressSelect).val(),
};
this.cartEditor.changeCartAddresses(this.cartId, addresses);
}
/**
* Refresh addresses list
*
* @param {boolean} refreshCartAddresses optional
*
* @private
*/
refreshAddressesList(refreshCartAddresses) {
const cartId = $(createOrderMap.cartBlock).data('cartId');
$.get(this.router.generate('admin_carts_info', {cartId})).then((cartInfo) => {
this.addressesRenderer.render(cartInfo.addresses, cartInfo.cartId);
if (refreshCartAddresses) {
this.changeCartAddresses();
}
}).catch((e) => {
window.showErrorMessage(e.responseJSON.message);
});
}
/**
* proxy to allow other scripts within the page to refresh addresses list
*/
refreshCart() {
const cartId = $(createOrderMap.cartBlock).data('cartId');
this.cartProvider.getCart(cartId);
}
}

View File

@@ -0,0 +1,220 @@
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
*/
import createOrderMap from '@pages/order/create/create-order-map';
import CustomerRenderer from '@pages/order/create/customer-renderer';
import {EventEmitter} from '@components/event-emitter';
import eventMap from '@pages/order/create/event-map';
import Router from '@components/router';
const {$} = window;
/**
* Responsible for customers managing. (search, select, get customer info etc.)
*/
export default class CustomerManager {
constructor() {
this.customerId = null;
this.activeSearchRequest = null;
this.router = new Router();
this.$container = $(createOrderMap.customerSearchBlock);
this.$searchInput = $(createOrderMap.customerSearchInput);
this.$customerSearchResultBlock = $(createOrderMap.customerSearchResultsBlock);
this.customerRenderer = new CustomerRenderer();
this.initListeners();
this.initAddCustomerIframe();
return {
search: (searchPhrase) => this.search(searchPhrase),
selectCustomer: (event) => this.selectCustomer(event),
loadCustomerCarts: (currentCartId) => this.loadCustomerCarts(currentCartId),
loadCustomerOrders: () => this.loadCustomerOrders(),
};
}
/**
* Initializes event listeners
*
* @private
*/
initListeners() {
this.$container.on('click', createOrderMap.changeCustomerBtn, () => this.changeCustomer());
this.onCustomerSearch();
this.onCustomerSelect();
this.onCustomersNotFound();
}
/**
* @private
*/
initAddCustomerIframe() {
$(createOrderMap.customerAddBtn).fancybox({
type: 'iframe',
width: '90%',
height: '90%',
});
}
/**
* Listens for customer search event
*
* @private
*/
onCustomerSearch() {
EventEmitter.on(eventMap.customerSearched, (response) => {
this.activeSearchRequest = null;
this.customerRenderer.hideSearchingCustomers();
if (response.customers.length === 0) {
EventEmitter.emit(eventMap.customersNotFound);
return;
}
this.customerRenderer.renderSearchResults(response.customers);
});
}
/**
* Listens for event of when no customers were found by search
*
* @private
*/
onCustomersNotFound() {
EventEmitter.on(eventMap.customersNotFound, () => {
this.customerRenderer.showNotFoundCustomers();
this.customerRenderer.hideCheckoutHistoryBlock();
});
}
/**
* Listens for customer select event
*
* @private
*/
onCustomerSelect() {
EventEmitter.on(eventMap.customerSelected, (event) => {
const $chooseBtn = $(event.currentTarget);
this.customerId = $chooseBtn.data('customer-id');
const createAddressUrl = this.router.generate(
'admin_addresses_create',
{
liteDisplaying: 1,
submitFormAjax: 1,
id_customer: this.customerId,
},
);
$(createOrderMap.addressAddBtn).attr('href', createAddressUrl);
this.customerRenderer.displaySelectedCustomerBlock($chooseBtn);
});
}
/**
* Handles use case when customer is changed
*
* @private
*/
changeCustomer() {
this.customerRenderer.showCustomerSearch();
}
/**
* Loads customer carts list
*
* @param currentCartId
*/
loadCustomerCarts(currentCartId) {
const {customerId} = this;
this.customerRenderer.showLoadingCarts();
$.get(this.router.generate('admin_customers_carts', {customerId})).then((response) => {
this.customerRenderer.renderCarts(response.carts, currentCartId);
}).catch((e) => {
window.showErrorMessage(e.responseJSON.message);
});
}
/**
* Loads customer orders list
*/
loadCustomerOrders() {
const {customerId} = this;
this.customerRenderer.showLoadingOrders();
$.get(this.router.generate('admin_customers_orders', {customerId})).then((response) => {
this.customerRenderer.renderOrders(response.orders);
}).catch((e) => {
window.showErrorMessage(e.responseJSON.message);
});
}
/**
* @param {Event} chooseCustomerEvent
*
* @return {Number}
*/
selectCustomer(chooseCustomerEvent) {
EventEmitter.emit(eventMap.customerSelected, chooseCustomerEvent);
return this.customerId;
}
/**
* Searches for customers
*
* @private
*/
search(searchPhrase) {
if (searchPhrase.length === 0) {
return;
}
if (this.activeSearchRequest !== null) {
this.activeSearchRequest.abort();
}
this.customerRenderer.clearShownCustomers();
this.customerRenderer.hideNotFoundCustomers();
this.customerRenderer.showSearchingCustomers();
const $searchRequest = $.get(this.router.generate('admin_customers_search'), {
customer_search: searchPhrase,
});
this.activeSearchRequest = $searchRequest;
$searchRequest.then((response) => {
EventEmitter.emit(eventMap.customerSearched, response);
}).catch((response) => {
if (response.statusText === 'abort') {
return;
}
window.showErrorMessage(response.responseJSON.message);
});
}
}

View File

@@ -0,0 +1,341 @@
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
*/
import createOrderMap from '@pages/order/create/create-order-map';
import Router from '@components/router';
import eventMap from '@pages/order/create/event-map';
import {EventEmitter} from '@components/event-emitter';
const {$} = window;
/**
* Responsible for customer information rendering
*/
export default class CustomerRenderer {
constructor() {
this.$container = $(createOrderMap.customerSearchBlock);
this.$customerSearchResultBlock = $(createOrderMap.customerSearchResultsBlock);
this.router = new Router();
}
/**
* Renders customer search results
*
* @param foundCustomers
*/
renderSearchResults(foundCustomers) {
if (foundCustomers.length === 0) {
EventEmitter.emit(eventMap.customersNotFound);
return;
}
Object.entries(foundCustomers).forEach(([customerId, customerResult]) => {
const customer = {
id: customerId,
firstName: customerResult.firstname,
lastName: customerResult.lastname,
email: customerResult.email,
birthday: customerResult.birthday !== '0000-00-00' ? customerResult.birthday : ' ',
};
this.renderFoundCustomer(customer);
});
// Show customer details in fancy box
$(createOrderMap.customerDetailsBtn).fancybox({
type: 'iframe',
width: '90%',
height: '90%',
});
}
/**
* Responsible for displaying customer block after customer select
*
* @param $targetedBtn
*/
displaySelectedCustomerBlock($targetedBtn) {
this.showCheckoutHistoryBlock();
$targetedBtn.addClass('d-none');
const $customerCard = $targetedBtn.closest('.card');
$customerCard.addClass('border-success');
$customerCard.find(createOrderMap.changeCustomerBtn).removeClass('d-none');
this.$container.find(createOrderMap.customerSearchRow).addClass('d-none');
this.$container.find(createOrderMap.notSelectedCustomerSearchResults)
.closest(createOrderMap.customerSearchResultColumn)
.remove();
// Initial display of the customer, the cart is gonna be created then customer's carts
// and orders are going to be fetched, but we can display the loading messages right now
this.showLoadingCarts();
this.showLoadingOrders();
}
/**
* Shows customer search block
*/
showCustomerSearch() {
this.$container.find(createOrderMap.customerSearchRow).removeClass('d-none');
}
/**
* Empty the cart list and display a loading message.
*/
showLoadingCarts() {
const $cartsTable = $(createOrderMap.customerCartsTable);
$cartsTable.find('tbody').empty();
this.renderLoading($cartsTable);
}
/**
* Renders customer carts list
*
* @param {Array} carts
* @param {Int} currentCartId
*/
renderCarts(carts, currentCartId) {
const $cartsTable = $(createOrderMap.customerCartsTable);
const $cartsTableRowTemplate = $($(createOrderMap.customerCartsTableRowTemplate).html());
$cartsTable.find('tbody').empty();
this.showCheckoutHistoryBlock();
this.removeEmptyListRowFromTable($cartsTable);
Object.values(carts).forEach((cart) => {
// do not render current cart
if (cart.cartId === currentCartId) {
// render 'No records found' warn if carts only contain current cart
if (carts.length === 1) {
this.renderEmptyList($cartsTable);
}
return;
}
const $cartsTableRow = $cartsTableRowTemplate.clone();
$cartsTableRow.find(createOrderMap.cartIdField).text(cart.cartId);
$cartsTableRow.find(createOrderMap.cartDateField).text(cart.creationDate);
$cartsTableRow.find(createOrderMap.cartTotalField).text(cart.totalPrice);
$cartsTableRow.find(createOrderMap.cartDetailsBtn).prop(
'href',
this.router.generate('admin_carts_view', {
cartId: cart.cartId,
liteDisplaying: 1,
}),
);
$cartsTableRow.find(createOrderMap.useCartBtn).data('cart-id', cart.cartId);
$cartsTable.find('thead').removeClass('d-none');
$cartsTable.find('tbody').append($cartsTableRow);
});
// Show cart details in fancy box
$(createOrderMap.cartDetailsBtn).fancybox({
type: 'iframe',
width: '90%',
height: '90%',
});
}
/**
* Empty the order list and display a loading message.
*/
showLoadingOrders() {
const $ordersTable = $(createOrderMap.customerOrdersTable);
$ordersTable.find('tbody').empty();
this.renderLoading($ordersTable);
}
/**
* Renders customer orders list
*
* @param {Array} orders
*/
renderOrders(orders) {
const $ordersTable = $(createOrderMap.customerOrdersTable);
const $rowTemplate = $($(createOrderMap.customerOrdersTableRowTemplate).html());
$ordersTable.find('tbody').empty();
this.showCheckoutHistoryBlock();
this.removeEmptyListRowFromTable($ordersTable);
// render 'No records found' when list is empty
if (orders.length === 0) {
this.renderEmptyList($ordersTable);
return;
}
Object.values(orders).forEach((order) => {
const $template = $rowTemplate.clone();
$template.find(createOrderMap.orderIdField).text(order.orderId);
$template.find(createOrderMap.orderDateField).text(order.orderPlacedDate);
$template.find(createOrderMap.orderProductsField).text(order.orderProductsCount);
$template.find(createOrderMap.orderTotalField).text(order.totalPaid);
$template.find(createOrderMap.orderPaymentMethod).text(order.paymentMethodName);
$template.find(createOrderMap.orderStatusField).text(order.orderStatus);
$template.find(createOrderMap.orderDetailsBtn).prop(
'href',
this.router.generate('admin_orders_view', {
orderId: order.orderId,
liteDisplaying: 1,
}),
);
$template.find(createOrderMap.useOrderBtn).data('order-id', order.orderId);
$ordersTable.find('thead').removeClass('d-none');
$ordersTable.find('tbody').append($template);
});
// Show order details in fancy box
$(createOrderMap.orderDetailsBtn).fancybox({
type: 'iframe',
width: '90%',
height: '90%',
});
}
/**
* Shows empty result when customer is not found
*/
showNotFoundCustomers() {
$(createOrderMap.customerSearchEmptyResultWarning).removeClass('d-none');
}
/**
* Hides not found customers warning
*/
hideNotFoundCustomers() {
$(createOrderMap.customerSearchEmptyResultWarning).addClass('d-none');
}
/**
* Hides checkout history block where carts and orders are rendered
*/
hideCheckoutHistoryBlock() {
$(createOrderMap.customerCheckoutHistory).addClass('d-none');
}
/**
* Shows searching customers notice during request
*/
showSearchingCustomers() {
$(createOrderMap.customerSearchLoadingNotice).removeClass('d-none');
}
/**
* Hide searching notice
*/
hideSearchingCustomers() {
$(createOrderMap.customerSearchLoadingNotice).addClass('d-none');
}
/**
* Renders 'No records' warning in list
*
* @param $table
*
* @private
*/
renderEmptyList($table) {
const $emptyTableRow = $($(createOrderMap.emptyListRowTemplate).html()).clone();
$table.find('tbody').append($emptyTableRow);
}
/**
* Renders 'Loading' message in list
*
* @param $table
*
* @private
*/
renderLoading($table) {
const $emptyTableRow = $($(createOrderMap.loadingListRowTemplate).html()).clone();
$table.find('tbody').append($emptyTableRow);
}
/**
* Removes empty list row in case it was rendered
*/
removeEmptyListRowFromTable($table) {
$table.find(createOrderMap.emptyListRow).remove();
}
/**
* Renders customer information after search action
*
* @param {Object} customer
*
* @return {jQuery}
*
* @private
*/
renderFoundCustomer(customer) {
this.hideNotFoundCustomers();
const $customerSearchResultTemplate = $($(createOrderMap.customerSearchResultTemplate).html());
const $template = $customerSearchResultTemplate.clone();
$template.find(createOrderMap.customerSearchResultName).text(`${customer.firstName} ${customer.lastName}`);
$template.find(createOrderMap.customerSearchResultEmail).text(customer.email);
$template.find(createOrderMap.customerSearchResultId).text(customer.id);
$template.find(createOrderMap.customerSearchResultBirthday).text(customer.birthday);
$template.find(createOrderMap.chooseCustomerBtn).data('customer-id', customer.id);
$template.find(createOrderMap.customerDetailsBtn).prop(
'href',
this.router.generate('admin_customers_view', {
customerId: customer.id,
liteDisplaying: 1,
}),
);
return this.$customerSearchResultBlock.append($template);
}
/**
* Shows checkout history block where carts and orders are rendered
*
* @private
*/
showCheckoutHistoryBlock() {
$(createOrderMap.customerCheckoutHistory).removeClass('d-none');
}
/**
* Clears shown customers
*/
clearShownCustomers() {
this.$customerSearchResultBlock.empty();
}
}

View File

@@ -0,0 +1,77 @@
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
*/
/**
* Encapsulates js events used in create order page
*/
export default {
// when customer search action is done
customerSearched: 'OrderCreateCustomerSearched',
// when new customer is selected
customerSelected: 'OrderCreateCustomerSelected',
// when no customers found by search
customersNotFound: 'OrderCreateSearchCustomerNotFound',
// when new cart is loaded,
// no matter if its empty, selected from carts list or duplicated by order.
cartLoaded: 'OrderCreateCartLoaded',
// when cart currency has been changed
cartCurrencyChanged: 'OrderCreateCartCurrencyChanged',
// when cart currency changing fails
cartCurrencyChangeFailed: 'OrderCreateCartCurrencyChangeFailed',
// when cart language has been changed
cartLanguageChanged: 'OrderCreateCartLanguageChanged',
// when cart addresses information has been changed
cartAddressesChanged: 'OrderCreateCartAddressesChanged',
// when cart delivery option has been changed
cartDeliveryOptionChanged: 'OrderCreateCartDeliveryOptionChanged',
// when cart delivery setting has been changed
cartDeliverySettingChanged: 'OrderCreateCartDeliverySettingChangedSet',
// when cart rules search action is done
cartRuleSearched: 'OrderCreateCartRuleSearched',
// when cart rule is removed from cart
cartRuleRemoved: 'OrderCreateCartRuleRemoved',
// when cart rule is added to cart
cartRuleAdded: 'OrderCreateCartRuleAdded',
// when cart rule cannot be added to cart
cartRuleFailedToAdd: 'OrderCreateCartRuleFailedToAdd',
// when product search action is done
productSearched: 'OrderCreateProductSearched',
// when product is added to cart
productAddedToCart: 'OrderCreateProductAddedToCart',
// when adding product to cart fails
productAddToCartFailed: 'OrderCreateProductAddToCartFailed',
// when product is removed from cart
productRemovedFromCart: 'OrderCreateProductRemovedFromCart',
// when product in cart price has been changed
productPriceChanged: 'OrderCreateProductPriceChanged',
// when product quantity in cart has been changed
productQtyChanged: 'OrderCreateProductQtyChanged',
// when changing product quantity in cart failed
productQtyChangeFailed: 'OrderCreateProductQtyChangeFailed',
// when order process email has been sent to customer
processOrderEmailSent: 'OrderCreateProcessOrderEmailSent',
// when order process email sending failed
processOrderEmailFailed: 'OrderCreateProcessOrderEmailFailed',
};

View File

@@ -0,0 +1,422 @@
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
*/
import CartEditor from '@pages/order/create/cart-editor';
import createOrderMap from '@pages/order/create/create-order-map';
import eventMap from '@pages/order/create/event-map';
import {EventEmitter} from '@components/event-emitter';
import ProductRenderer from '@pages/order/create/product-renderer';
import Router from '@components/router';
const {$} = window;
/**
* Product component Object for "Create order" page
*/
export default class ProductManager {
constructor() {
this.products = [];
this.selectedProduct = null;
this.selectedCombinationId = null;
this.activeSearchRequest = null;
this.productRenderer = new ProductRenderer();
this.router = new Router();
this.cartEditor = new CartEditor();
this.initListeners();
return {
search: (searchPhrase) => this.search(searchPhrase),
addProductToCart: (cartId) => this.cartEditor.addProduct(cartId, this.getProductData()),
removeProductFromCart: (cartId, product) => this.cartEditor.removeProductFromCart(cartId, product),
/* eslint-disable-next-line max-len */
changeProductPrice: (cartId, customerId, updatedProduct) => this.cartEditor.changeProductPrice(cartId, customerId, updatedProduct),
changeProductQty: (cartId, updatedProduct) => this.cartEditor.changeProductQty(cartId, updatedProduct),
};
}
/**
* Initializes event listeners
*
* @private
*/
initListeners() {
$(createOrderMap.productSelect).on('change', (e) => this.initProductSelect(e));
$(createOrderMap.combinationsSelect).on('change', (e) => this.initCombinationSelect(e));
this.onProductSearch();
this.onAddProductToCart();
this.onRemoveProductFromCart();
this.onProductPriceChange();
this.onProductQtyChange();
}
/**
* Listens for product search event
*
* @private
*/
onProductSearch() {
EventEmitter.on(eventMap.productSearched, (response) => {
this.products = response.products;
this.productRenderer.renderSearchResults(this.products);
this.selectFirstResult();
});
}
/**
* Listens for add product to cart event
*
* @private
*/
onAddProductToCart() {
// on success
EventEmitter.on(eventMap.productAddedToCart, (cartInfo) => {
this.productRenderer.cleanCartBlockAlerts();
this.updateStockOnProductAdd();
EventEmitter.emit(eventMap.cartLoaded, cartInfo);
});
// on failure
EventEmitter.on(eventMap.productAddToCartFailed, (errorMessage) => {
this.productRenderer.renderCartBlockErrorAlert(errorMessage);
});
}
/**
* Listens for remove product from cart event
*
* @private
*/
onRemoveProductFromCart() {
EventEmitter.on(eventMap.productRemovedFromCart, (data) => {
this.updateStockOnProductRemove(data.product);
EventEmitter.emit(eventMap.cartLoaded, data.cartInfo);
});
}
/**
* Listens for product price change in cart event
*
* @private
*/
onProductPriceChange() {
EventEmitter.on(eventMap.productPriceChanged, (cartInfo) => {
this.productRenderer.cleanCartBlockAlerts();
EventEmitter.emit(eventMap.cartLoaded, cartInfo);
});
}
/**
* Listens for product quantity change in cart success/failure event
*
* @private
*/
onProductQtyChange() {
const enableQtyInputs = () => {
const inputsQty = document.querySelectorAll(createOrderMap.listedProductQtyInput);
inputsQty.forEach((inputQty) => {
inputQty.disabled = false;
});
};
// on success
EventEmitter.on(eventMap.productQtyChanged, (data) => {
this.productRenderer.cleanCartBlockAlerts();
this.updateStockOnQtyChange(data.product);
$(createOrderMap.createOrderButton).prop('disabled', false);
EventEmitter.emit(eventMap.cartLoaded, data.cartInfo);
enableQtyInputs();
});
// on failure
EventEmitter.on(eventMap.productQtyChangeFailed, (e) => {
this.productRenderer.renderCartBlockErrorAlert(e.responseJSON.message);
$(createOrderMap.createOrderButton).prop('disabled', true);
enableQtyInputs();
});
}
/**
* Initializes product select
*
* @param event
*
* @private
*/
initProductSelect(event) {
const productId = Number(
$(event.currentTarget)
.find(':selected')
.val(),
);
this.selectProduct(productId);
}
/**
* Initializes combination select
*
* @param event
*
* @private
*/
initCombinationSelect(event) {
const combinationId = Number(
$(event.currentTarget)
.find(':selected')
.val(),
);
this.selectCombination(combinationId);
}
/**
* Searches for product
*
* @private
*/
search(searchPhrase) {
// Search only if the search phrase length is greater than 2 characters
if (searchPhrase.length < 2) {
return;
}
this.productRenderer.renderSearching();
if (this.activeSearchRequest !== null) {
this.activeSearchRequest.abort();
}
const params = {
search_phrase: searchPhrase,
};
if ($(createOrderMap.cartCurrencySelect).data('selectedCurrencyId') !== undefined) {
params.currency_id = $(createOrderMap.cartCurrencySelect).data('selectedCurrencyId');
}
const $searchRequest = $.get(this.router.generate('admin_orders_products_search'), params);
this.activeSearchRequest = $searchRequest;
$searchRequest
.then((response) => {
EventEmitter.emit(eventMap.productSearched, response);
})
.catch((response) => {
if (response.statusText === 'abort') {
return;
}
window.showErrorMessage(response.responseJSON.message);
});
}
/**
* Initiate first result dataset after search
*
* @private
*/
selectFirstResult() {
this.unsetProduct();
if (this.products.length !== 0) {
this.selectProduct(this.products[0].productId);
}
}
/**
* Handles use case when product is selected from search results
*
* @private
*
* @param {Number} productId
*/
selectProduct(productId) {
this.unsetCombination();
const selectedProduct = Object.values(this.products).find((product) => product.productId === productId);
if (selectedProduct) {
this.selectedProduct = selectedProduct;
}
this.productRenderer.renderProductMetadata(this.selectedProduct);
// if product has combinations select the first else leave it null
if (this.selectedProduct.combinations.length !== 0) {
this.selectCombination(Object.keys(this.selectedProduct.combinations)[0]);
}
return this.selectedProduct;
}
/**
* Handles use case when new combination is selected
*
* @param combinationId
*
* @private
*/
selectCombination(combinationId) {
const combination = this.selectedProduct.combinations[combinationId];
this.selectedCombinationId = combinationId;
this.productRenderer.renderStock(
$(createOrderMap.inStockCounter),
$(createOrderMap.quantityInput),
combination.stock,
this.selectedProduct.availableOutOfStock || combination.stock <= 0,
);
return combination;
}
/**
* Sets the selected combination id to null
*
* @private
*/
unsetCombination() {
this.selectedCombinationId = null;
}
/**
* Sets the selected product to null
*
* @private
*/
unsetProduct() {
this.selectedProduct = null;
}
/**
* Retrieves product data from product search result block fields
*
* @returns {Object}
*
* @private
*/
getProductData() {
const $fileInputs = $(createOrderMap.productCustomizationContainer).find('input[type="file"]');
const formData = new FormData(document.querySelector(createOrderMap.productAddForm));
const fileSizes = {};
// adds key value pairs {input name: file size} of each file in separate object
// in case formData size exceeds server settings.
$.each($fileInputs, (key, input) => {
if (input.files.length !== 0) {
fileSizes[$(input).data('customization-field-id')] = input.files[0].size;
}
});
return {
product: formData,
fileSizes,
};
}
/**
* Updates the stock when the product is added to cart in "create new order" page
*
* @private
*/
updateStockOnProductAdd() {
const {productId} = this.selectedProduct;
const attributeId = this.selectedCombinationId;
const qty = -Number($(createOrderMap.quantityInput).val());
this.updateStock(productId, attributeId, qty);
}
/**
* Updates the stock when the product is removed from cart in Orders/"create new order page"
*
* @private
*/
updateStockOnProductRemove(product) {
const {productId, attributeId, qtyToRemove} = product;
const qty = qtyToRemove;
this.updateStock(productId, attributeId, qty);
}
/**
* Updates the stock when the quantity of product is changed from cart in Orders/"create new order page"
*
* @private
*/
updateStockOnQtyChange(product) {
const {
productId, attributeId, prevQty, newQty,
} = product;
const qty = prevQty - newQty;
this.updateStock(productId, attributeId, qty);
}
/**
* Updates the stock in products object and renders the new stock
*
* @private
*/
updateStock(productId, attributeId, qty) {
const productKeys = Object.keys(this.products);
const productValues = Object.values(this.products);
for (let i = 0; i < productKeys.length; i += 1) {
if (productValues[i].productId === productId) {
const $template = this.productRenderer.cloneProductTemplate(productValues[i]);
// Update the stock value in products object
productValues[i].stock += qty;
// Update the stock also for combination */
if (attributeId && attributeId > 0) {
productValues[i].combinations[attributeId].stock += qty;
}
// Render the new stock value
if (this.selectedProduct.productId === productId) {
if (this.selectedProduct.combinations.length === 0) {
this.productRenderer.renderStock(
$template.find(createOrderMap.listedProductQtyStock),
$template.find(createOrderMap.listedProductQtyInput),
productValues[i].stock,
productValues[i].availableOutOfStock || productValues[i].availableStock <= 0,
);
} else if (attributeId && Number(this.selectedCombinationId) === Number(attributeId)) {
this.productRenderer.renderStock(
$template.find(createOrderMap.listedProductQtyStock),
$template.find(createOrderMap.listedProductQtyInput),
productValues[i].combinations[attributeId].stock,
productValues[i].availableOutOfStock || productValues[i].availableStock <= 0,
);
}
}
break;
}
}
}
}

View File

@@ -0,0 +1,500 @@
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
*/
import createOrderMap from './create-order-map';
const {$} = window;
export default class ProductRenderer {
constructor() {
this.$productsTable = $(createOrderMap.productsTable);
}
/**
* Renders cart products list
*
* @param products
*/
renderList(products) {
this.cleanProductsList();
if (products.length === 0) {
this.hideProductsList();
return;
}
Object.values(products).forEach((product) => {
const $template = this.cloneProductTemplate(product);
let customizationId = 0;
if (product.customization) {
({customizationId} = product.customization);
this.renderListedProductCustomization(product.customization, $template);
}
$template.find(createOrderMap.listedProductImageField).prop('src', product.imageLink);
$template.find(createOrderMap.listedProductNameField).text(product.name);
$template.find(createOrderMap.listedProductAttrField).text(product.attribute);
$template.find(createOrderMap.listedProductReferenceField).text(product.reference);
if (product.gift !== true) {
$template.find(createOrderMap.listedProductUnitPriceInput).val(product.unitPrice);
$template.find(createOrderMap.listedProductUnitPriceInput).data('product-id', product.productId);
$template.find(createOrderMap.listedProductUnitPriceInput).data('attribute-id', product.attributeId);
$template.find(createOrderMap.listedProductUnitPriceInput).data('customization-id', customizationId);
$template.find(createOrderMap.listedProductQtyInput).val(product.quantity);
$template.find(createOrderMap.listedProductQtyInput).data('product-id', product.productId);
$template.find(createOrderMap.listedProductQtyInput).data('attribute-id', product.attributeId);
$template.find(createOrderMap.listedProductQtyInput).data('customization-id', customizationId);
$template.find(createOrderMap.listedProductQtyInput).data('prev-qty', product.quantity);
this.renderStock(
$template.find(createOrderMap.listedProductQtyStock),
$template.find(createOrderMap.listedProductQtyInput),
product.availableStock,
product.availableOutOfStock || (product.availableStock <= 0),
);
$template.find(createOrderMap.productTotalPriceField).text(product.price);
$template.find(createOrderMap.productRemoveBtn).data('product-id', product.productId);
$template.find(createOrderMap.productRemoveBtn).data('attribute-id', product.attributeId);
$template.find(createOrderMap.productRemoveBtn).data('customization-id', customizationId);
} else {
$template.find(createOrderMap.listedProductGiftQty).text(product.quantity);
}
this.$productsTable.find('tbody').append($template);
});
this.showTaxWarning();
this.showProductsList();
}
/**
* Renders customization data for listed product
*
* @param customization
* @param $productRowTemplate
*
* @private
*/
renderListedProductCustomization(customization, $productRowTemplate) {
const $customizedTextTemplate = $($(createOrderMap.listedProductCustomizedTextTemplate).html());
const $customizedFileTemplate = $($(createOrderMap.listedProductCustomizedFileTemplate).html());
Object.values(customization.customizationFieldsData).forEach((customizedData) => {
let $customizationTemplate = $customizedTextTemplate.clone();
if (customizedData.type === createOrderMap.productCustomizationFieldTypeFile) {
$customizationTemplate = $customizedFileTemplate.clone();
$customizationTemplate.find(createOrderMap.listedProductCustomizationName).text(customizedData.name);
$customizationTemplate
.find(`${createOrderMap.listedProductCustomizationValue} img`)
.prop('src', customizedData.value);
} else {
$customizationTemplate.find(createOrderMap.listedProductCustomizationName).text(customizedData.name);
$customizationTemplate.find(createOrderMap.listedProductCustomizationValue).text(customizedData.value);
}
$productRowTemplate.find(createOrderMap.listedProductDefinition).append($customizationTemplate);
});
}
renderSearching() {
this.reset();
this.toggleSearchingNotice(true);
}
/**
* Renders cart products search results block
*
* @param foundProducts
*/
renderSearchResults(foundProducts) {
this.cleanSearchResults();
this.toggleSearchingNotice(false);
if (foundProducts.length === 0) {
this.showNotFound();
this.hideTaxWarning();
return;
}
this.renderFoundProducts(foundProducts);
this.hideNotFound();
this.showTaxWarning();
this.showResultBlock();
}
reset() {
this.cleanSearchResults();
this.hideTaxWarning();
this.hideResultBlock();
this.toggleSearchingNotice(false);
}
/**
* Renders available fields related to selected product
*
* @param {object} product
*/
renderProductMetadata(product) {
this.renderStock(
$(createOrderMap.inStockCounter),
$(createOrderMap.quantityInput),
product.stock,
product.availableOutOfStock || (product.stock <= 0),
);
this.renderCombinations(product.combinations);
this.renderCustomizations(product.customizationFields);
}
/**
* Updates stock text helper value
*
* @param {object} inputStockCounter Text Help with the stock counter
* @param {object} inputQuantity Input for the stock
* @param {number} stock Available stock for the product
* @param {boolean} infiniteMax If the product order has no limits
*/
renderStock(inputStockCounter, inputQuantity, stock, infiniteMax) {
inputStockCounter.text(stock);
if (!infiniteMax) {
inputQuantity.attr('max', stock);
} else {
inputQuantity.removeAttr('max');
}
}
/**
* @param product
*
* @private
*/
cloneProductTemplate(product) {
return product.gift === true
? $($(createOrderMap.productsTableGiftRowTemplate).html()).clone()
: $($(createOrderMap.productsTableRowTemplate).html()).clone();
}
/**
* Renders found products select
*
* @param foundProducts
*
* @private
*/
renderFoundProducts(foundProducts) {
Object.values(foundProducts).forEach((product) => {
let {name} = product;
if (product.combinations.length === 0) {
name += ` - ${product.formattedPrice}`;
}
$(createOrderMap.productSelect).append(`<option value="${product.productId}">${name}</option>`);
});
}
/**
* Cleans product search result fields
*
* @private
*/
cleanSearchResults() {
$(createOrderMap.productSelect).empty();
$(createOrderMap.combinationsSelect).empty();
$(createOrderMap.quantityInput).empty();
}
/**
* Renders combinations row with select options
*
* @param {Array} combinations
*
* @private
*/
renderCombinations(combinations) {
this.cleanCombinations();
if (combinations.length === 0) {
this.hideCombinations();
return;
}
Object.values(combinations).forEach((combination) => {
$(createOrderMap.combinationsSelect).append(
`<option
value="${combination.attributeCombinationId}">
${combination.attribute} - ${combination.formattedPrice}
</option>`,
);
});
this.showCombinations();
}
/**
* Resolves weather to add customization fields to result block and adds them if needed
*
* @param customizationFields
*
* @private
*/
renderCustomizations(customizationFields) {
// represents customization field type "file".
const fieldTypeFile = createOrderMap.productCustomizationFieldTypeFile;
// represents customization field type "text".
const fieldTypeText = createOrderMap.productCustomizationFieldTypeText;
this.cleanCustomizations();
if (customizationFields.length === 0) {
this.hideCustomizations();
return;
}
const $customFieldsContainer = $(createOrderMap.productCustomFieldsContainer);
const $fileInputTemplate = $($(createOrderMap.productCustomFileTemplate).html());
const $textInputTemplate = $($(createOrderMap.productCustomTextTemplate).html());
const templateTypeMap = {
[fieldTypeFile]: $fileInputTemplate,
[fieldTypeText]: $textInputTemplate,
};
Object.values(customizationFields).forEach((customField) => {
const $template = templateTypeMap[customField.type].clone();
if (customField.type === fieldTypeFile) {
$template.on('change', (e) => {
const fileName = e.target.files[0].name;
$(e.target).next('.custom-file-label').html(fileName);
});
}
$template
.find(createOrderMap.productCustomInput)
.attr('name', `customizations[${customField.customizationFieldId}]`)
.data('customization-field-id', customField.customizationFieldId);
$template
.find(createOrderMap.productCustomInputLabel)
.attr('for', `customizations[${customField.customizationFieldId}]`)
.text(customField.name);
if (customField.required === true) {
$template.find(createOrderMap.requiredFieldMark).removeClass('d-none');
}
$customFieldsContainer.append($template);
});
this.showCustomizations();
}
/**
* Renders error alert for cart block
*
* @param message
*/
renderCartBlockErrorAlert(message) {
$(createOrderMap.cartErrorAlertText).text(message);
this.showCartBlockError();
}
/**
* Cleans cart block alerts content and hides them
*/
cleanCartBlockAlerts() {
$(createOrderMap.cartErrorAlertText).text('');
this.hideCartBlockError();
}
/**
* Shows error alert block of cart block
*
* @private
*/
showCartBlockError() {
$(createOrderMap.cartErrorAlertBlock).removeClass('d-none');
}
/**
* Hides error alert block of cart block
*
* @private
*/
hideCartBlockError() {
$(createOrderMap.cartErrorAlertBlock).addClass('d-none');
}
/**
* Shows product customization container
*
* @private
*/
showCustomizations() {
$(createOrderMap.productCustomizationContainer).removeClass('d-none');
}
/**
* Hides product customization container
*
* @private
*/
hideCustomizations() {
$(createOrderMap.productCustomizationContainer).addClass('d-none');
}
/**
* Empties customization fields container
*
* @private
*/
cleanCustomizations() {
$(createOrderMap.productCustomFieldsContainer).empty();
}
/**
* Shows result block
*
* @private
*/
showResultBlock() {
$(createOrderMap.productResultBlock).removeClass('d-none');
}
/**
* Hides result block
*
* @private
*/
hideResultBlock() {
$(createOrderMap.productResultBlock).addClass('d-none');
}
/**
* Shows products list
*
* @private
*/
showProductsList() {
this.$productsTable.removeClass('d-none');
}
/**
* Hides products list
*
* @private
*/
hideProductsList() {
this.$productsTable.addClass('d-none');
}
/**
* Empties products list
*
* @private
*/
cleanProductsList() {
this.$productsTable.find('tbody').empty();
}
/**
* Empties combinations select
*
* @private
*/
cleanCombinations() {
$(createOrderMap.combinationsSelect).empty();
}
/**
* Shows combinations row
*
* @private
*/
showCombinations() {
$(createOrderMap.combinationsRow).removeClass('d-none');
}
/**
* Hides combinations row
*
* @private
*/
hideCombinations() {
$(createOrderMap.combinationsRow).addClass('d-none');
}
/**
* Shows warning of tax included/excluded
*
* @private
*/
showTaxWarning() {
$(createOrderMap.productTaxWarning).removeClass('d-none');
}
/**
* Hides warning of tax included/excluded
*
* @private
*/
hideTaxWarning() {
$(createOrderMap.productTaxWarning).addClass('d-none');
}
/**
* Shows product not found warning
*
* @private
*/
showNotFound() {
$(createOrderMap.noProductsFoundWarning).removeClass('d-none');
}
/**
* Hides product not found warning
*
* @private
*/
hideNotFound() {
$(createOrderMap.noProductsFoundWarning).addClass('d-none');
}
/**
* Toggles searching product notice
*
* @private
*/
toggleSearchingNotice(visible) {
$(createOrderMap.searchingProductsNotice).toggleClass('d-none', !visible);
}
}

View File

@@ -0,0 +1,233 @@
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
*/
import createOrderMap from './create-order-map';
const {$} = window;
/**
* Manipulates UI of Shipping block in Order creation page
*/
export default class ShippingRenderer {
constructor() {
this.$container = $(createOrderMap.shippingBlock);
this.$form = $(createOrderMap.shippingForm);
this.$noCarrierBlock = $(createOrderMap.noCarrierBlock);
}
/**
* @param {Object} shipping
* @param {Boolean} emptyCart
*/
render(shipping, emptyCart) {
if (emptyCart) {
this.hideContainer();
} else if (shipping !== null) {
this.displayForm(shipping);
} else {
this.displayNoCarriersWarning();
}
}
/**
* Show form block with rendered delivery options instead of warning message
*
* @param shipping
*
* @private
*/
displayForm(shipping) {
this.hideNoCarrierBlock();
this.renderDeliveryOptions(shipping.deliveryOptions, shipping.selectedCarrierId);
this.renderTotalShipping(shipping.shippingPrice);
this.renderFreeShippingSwitch(shipping.freeShipping);
this.renderRecycledPackagingSwitch(shipping.recycledPackaging);
this.renderGiftMessageField(shipping.giftMessage);
this.renderGiftSwitch(shipping.gift);
this.showForm();
this.showContainer();
}
/**
* Renders free shipping switch depending on free shipping value
*
* @param isFreeShipping
*
* @private
*/
renderFreeShippingSwitch(isFreeShipping) {
$(createOrderMap.freeShippingSwitch).each((key, input) => {
if (input.value === '1') {
input.checked = isFreeShipping;
} else {
input.checked = !isFreeShipping;
}
});
}
/**
* @param useRecycledPackaging
*
* @private
*/
renderRecycledPackagingSwitch(useRecycledPackaging) {
$(createOrderMap.recycledPackagingSwitch).each((key, input) => {
if (input.value === '1') {
input.checked = useRecycledPackaging;
} else {
input.checked = !useRecycledPackaging;
}
});
}
/**
* @param isAGift
*
* @private
*/
renderGiftSwitch(isAGift) {
$(createOrderMap.isAGiftSwitch).each((key, input) => {
if (input.value === '1') {
input.checked = isAGift;
} else {
input.checked = !isAGift;
}
});
}
/**
* @param giftMessage
*
* @private
*/
renderGiftMessageField(giftMessage) {
$(createOrderMap.giftMessageField).val(giftMessage);
}
/**
* Show warning message that no carriers are available and hide form block
*
* @private
*/
displayNoCarriersWarning() {
this.showContainer();
this.hideForm();
this.showNoCarrierBlock();
}
/**
* Renders delivery options selection block
*
* @param deliveryOptions
* @param selectedVal
*
* @private
*/
renderDeliveryOptions(deliveryOptions, selectedVal) {
const $deliveryOptionSelect = $(createOrderMap.deliveryOptionSelect);
$deliveryOptionSelect.empty();
Object.values(deliveryOptions).forEach((option) => {
const deliveryOption = {
value: option.carrierId,
text: `${option.carrierName} - ${option.carrierDelay}`,
};
if (selectedVal === deliveryOption.value) {
deliveryOption.selected = 'selected';
}
$deliveryOptionSelect.append($('<option>', deliveryOption));
});
}
/**
* Renders dynamic value of shipping price
*
* @param shippingPrice
*
* @private
*/
renderTotalShipping(shippingPrice) {
const $totalShippingField = $(createOrderMap.totalShippingField);
$totalShippingField.empty();
$totalShippingField.append(shippingPrice);
}
/**
* Show whole shipping container
*
* @private
*/
showContainer() {
this.$container.removeClass('d-none');
}
/**
* Hide whole shipping container
*
* @private
*/
hideContainer() {
this.$container.addClass('d-none');
}
/**
* Show form block
*
* @private
*/
showForm() {
this.$form.removeClass('d-none');
}
/**
* Hide form block
*
* @private
*/
hideForm() {
this.$form.addClass('d-none');
}
/**
* Show warning message block which warns that no carriers are available
*
* @private
*/
showNoCarrierBlock() {
this.$noCarrierBlock.removeClass('d-none');
}
/**
* Hide warning message block which warns that no carriers are available
*
* @private
*/
hideNoCarrierBlock() {
this.$noCarrierBlock.addClass('d-none');
}
}

View File

@@ -0,0 +1,93 @@
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
*/
import {EventEmitter} from '@components/event-emitter';
import Router from '@components/router';
import eventMap from './event-map';
import SummaryRenderer from './summary-renderer';
const {$} = window;
/**
* Manages summary block
*/
export default class SummaryManager {
constructor() {
this.router = new Router();
this.summaryRenderer = new SummaryRenderer();
this.initListeners();
return {
sendProcessOrderEmail: (cartId) => this.sendProcessOrderEmail(cartId),
};
}
/**
* Inits event listeners
*
* @private
*/
initListeners() {
this.onProcessOrderEmailError();
this.onProcessOrderEmailSuccess();
}
/**
* Listens for process order email sending success event
*
* @private
*/
onProcessOrderEmailSuccess() {
EventEmitter.on(eventMap.processOrderEmailSent, (response) => {
this.summaryRenderer.cleanAlerts();
this.summaryRenderer.renderSuccessMessage(response.message);
});
}
/**
* Listens for process order email failed event
*
* @private
*/
onProcessOrderEmailError() {
EventEmitter.on(eventMap.processOrderEmailFailed, (response) => {
this.summaryRenderer.cleanAlerts();
this.summaryRenderer.renderErrorMessage(response.responseJSON.message);
});
}
/**
* Sends email to customer with link of order processing
*
* @param {Number} cartId
*/
sendProcessOrderEmail(cartId) {
$.post(this.router.generate('admin_orders_send_process_order_email'), {
cartId,
}).then((response) => EventEmitter.emit(eventMap.processOrderEmailSent, response)).catch((e) => {
EventEmitter.emit(eventMap.processOrderEmailFailed, e);
});
}
}

View File

@@ -0,0 +1,177 @@
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
*/
import createOrderMap from './create-order-map';
import {ValidateAddresses} from './address-validator';
const {$} = window;
/**
* Responsible for summary block rendering
*/
export default class SummaryRenderer {
constructor() {
this.$totalProducts = $(createOrderMap.summaryTotalProducts);
this.$totalDiscount = $(createOrderMap.summaryTotalDiscount);
this.$totalShipping = $(createOrderMap.totalShippingField);
this.$summaryTotalShipping = $(createOrderMap.summaryTotalShipping);
this.$totalTaxes = $(createOrderMap.summaryTotalTaxes);
this.$totalWithoutTax = $(createOrderMap.summaryTotalWithoutTax);
this.$totalWithTax = $(createOrderMap.summaryTotalWithTax);
this.$placeOrderCartIdField = $(createOrderMap.placeOrderCartIdField);
this.$orderMessageField = $(createOrderMap.orderMessageField);
this.$processOrderLink = $(createOrderMap.processOrderLinkTag);
}
/**
* Renders summary block
*
* @param {Object} cartInfo
*/
render(cartInfo) {
this.cleanSummary();
const noProducts = cartInfo.products.length === 0;
const noShippingOptions = cartInfo.shipping === null;
const addressesAreValid = ValidateAddresses(cartInfo.addresses);
if (noProducts || noShippingOptions || !addressesAreValid) {
this.hideSummaryBlock();
return;
}
const cartSummary = cartInfo.summary;
this.$totalProducts.text(cartSummary.totalProductsPrice);
this.$totalDiscount.text(cartSummary.totalDiscount);
this.$summaryTotalShipping.text(cartSummary.totalShippingWithoutTaxes);
this.$totalShipping.text(cartSummary.totalShippingPrice);
this.$totalTaxes.text(cartSummary.totalTaxes);
this.$totalWithoutTax.text(cartSummary.totalPriceWithoutTaxes);
this.$totalWithTax.text(cartSummary.totalPriceWithTaxes);
this.$processOrderLink.prop('href', cartSummary.processOrderLink);
this.$orderMessageField.text(cartSummary.orderMessage);
this.$placeOrderCartIdField.val(cartInfo.cartId);
this.showSummaryBlock();
}
/**
* Renders summary success message
*
* @param message
*/
renderSuccessMessage(message) {
$(createOrderMap.summarySuccessAlertText).text(message);
this.showSummarySuccessAlertBlock();
}
/**
* Renders summary error message
*
* @param message
*/
renderErrorMessage(message) {
$(createOrderMap.summaryErrorAlertText).text(message);
this.showSummaryErrorAlertBlock();
}
/**
* Cleans content of success/error summary alerts and hides them
*/
cleanAlerts() {
$(createOrderMap.summarySuccessAlertText).text('');
$(createOrderMap.summaryErrorAlertText).text('');
this.hideSummarySuccessAlertBlock();
this.hideSummaryErrorAlertBlock();
}
/**
* Shows summary block
*
* @private
*/
showSummaryBlock() {
$(createOrderMap.summaryBlock).removeClass('d-none');
}
/**
* Hides summary block
*
* @private
*/
hideSummaryBlock() {
$(createOrderMap.summaryBlock).addClass('d-none');
}
/**
* Shows error alert of summary block
*
* @private
*/
showSummaryErrorAlertBlock() {
$(createOrderMap.summaryErrorAlertBlock).removeClass('d-none');
}
/**
* Hides error alert of summary block
*
* @private
*/
hideSummaryErrorAlertBlock() {
$(createOrderMap.summaryErrorAlertBlock).addClass('d-none');
}
/**
* Shows success alert of summary block
*
* @private
*/
showSummarySuccessAlertBlock() {
$(createOrderMap.summarySuccessAlertBlock).removeClass('d-none');
}
/**
* Hides success alert of summary block
*
* @private
*/
hideSummarySuccessAlertBlock() {
$(createOrderMap.summarySuccessAlertBlock).addClass('d-none');
}
/**
* Empties cart summary fields
*/
cleanSummary() {
this.$totalProducts.empty();
this.$totalDiscount.empty();
this.$totalShipping.empty();
this.$totalTaxes.empty();
this.$totalWithoutTax.empty();
this.$totalWithTax.empty();
this.$processOrderLink.prop('href', '');
this.$orderMessageField.text('');
this.cleanAlerts();
}
}