arch-8.x-1.x-dev/modules/order/arch_order.module
modules/order/arch_order.module
<?php /** * @file * Arch Order module. */ use Drupal\arch_order\Entity\OrderInterface; use Drupal\arch_order\Services\OrderAddressServiceInterface; use Drupal\Component\Plugin\Exception\PluginException; use Drupal\Component\Render\FormattableMarkup; use Drupal\Core\Entity\Display\EntityViewDisplayInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Render\Element; use Drupal\Core\Session\AccountInterface; use Drupal\Core\Template\Attribute; require_once __DIR__ . '/includes/deprecated.inc'; /** * Implements hook_theme(). */ function arch_order_theme() { return [ 'order' => [ 'render element' => 'elements', ], 'order_line_items__advanced' => [ 'render element' => 'elements', ], ]; } /** * Implements hook_entity_extra_field_info(). */ function arch_order_entity_extra_field_info() { $extra = []; $extra['order']['order']['display']['shipping_price'] = [ 'label' => t('Shipping price', [], ['context' => 'arch_order']), 'weight' => 100, 'visible' => TRUE, ]; $extra['order']['order']['display']['shipping_extra'] = [ 'label' => t('Shipping extra', [], ['context' => 'arch_order']), 'weight' => 101, 'visible' => TRUE, ]; $extra['order']['order']['display']['payment_fee'] = [ 'label' => t('Payment fee', [], ['context' => 'arch_order']), 'weight' => 110, 'visible' => TRUE, ]; $extra['order']['order']['display']['exchange_rate'] = [ 'label' => t('Exchange rate', [], ['context' => 'arch_order']), 'weight' => 120, 'visible' => TRUE, ]; return $extra; } /** * Implements hook_ENTITY_TYPE_view_alter(). */ function arch_order_order_view_alter(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display) { $order_data = []; if ( !$entity->get('data')->isEmpty() && $entity->get('data')->count() > 0 ) { $order_data = $entity->get('data')->first()->getValue(); } $shipping_price = NULL; $shipping_extra = NULL; $payment_fee = NULL; foreach ($entity->get('line_items') as $line_item) { /** @var \Drupal\arch_order\Plugin\Field\FieldType\OrderLineItemFieldItem $line_item */ if ($line_item->isShipping()) { $shipping_price = $line_item; } elseif ($line_item->isShippingExtra()) { $shipping_extra = $line_item; } elseif ($line_item->isPaymentFee()) { $payment_fee = $line_item; } } /** @var \Drupal\arch_price\Price\PriceFactoryInterface $price_factory */ $price_factory = \Drupal::service('price_factory'); /** @var \Drupal\arch_price\Price\PriceFormatterInterface $price_formatter */ $price_formatter = \Drupal::service('price_formatter'); $formatter_defaults = [ 'vat_info' => FALSE, 'label' => FALSE, 'wrapper_element' => 'span', ]; if ( $display->getComponent('shipping_price') && !empty($shipping_price) && (float) $shipping_price->get('calculated_gross')->getValue() > 0 ) { $price = $price_factory->getInstance([ 'base' => 'gross', 'net' => $shipping_price->get('calculated_net')->getValue(), 'gross' => $shipping_price->get('calculated_gross')->getValue(), 'vat_rate' => $shipping_price->get('calculated_vat_rate')->getValue(), 'vat_category' => $shipping_price->get('calculated_vat_cat_name')->getValue(), 'vat_amount' => $shipping_price->get('calculated_vat_amount')->getValue(), 'currency' => $entity->get('currency')->getString(), ]); $display_shipping_price = $display->getComponent('shipping_price'); $build['shipping_price'] = [ '#weight' => $display_shipping_price['weight'], '#type' => 'container', '#attributes' => [ 'class' => [ 'field', 'field--name-shipping-price', 'field--type-string', 'field--label-inline', ], ], 'label' => [ '#type' => 'html_tag', '#tag' => 'div', '#value' => t('Shipping price', [], ['context' => 'arch_order']), '#attributes' => [ 'class' => [ 'field--label', ], ], ], 'value' => [ '#type' => 'html_tag', '#tag' => 'div', '#value' => $price_formatter->formatGross($price, $formatter_defaults), '#attributes' => [ 'class' => [ 'field--item', ], ], ], ]; } if ( $display->getComponent('shipping_extra') && !empty($shipping_extra) && (float) $shipping_extra->get('calculated_gross')->getValue() > 0 ) { $price = $price_factory->getInstance([ 'base' => 'gross', 'net' => $shipping_extra->get('calculated_net')->getValue(), 'gross' => $shipping_extra->get('calculated_gross')->getValue(), 'vat_rate' => $shipping_extra->get('calculated_vat_rate')->getValue(), 'vat_category' => $shipping_extra->get('calculated_vat_cat_name')->getValue(), 'vat_amount' => $shipping_extra->get('calculated_vat_amount')->getValue(), 'currency' => $entity->get('currency')->getString(), ]); $display_shipping_extra = $display->getComponent('shipping_extra'); $build['shipping_extra'] = [ '#weight' => $display_shipping_extra['weight'], '#type' => 'container', '#attributes' => [ 'class' => [ 'field', 'field--name-shipping-extra-price', 'field--type-string', 'field--label-inline', ], ], 'label' => [ '#type' => 'html_tag', '#tag' => 'div', '#value' => t('Shipping extra', [], ['context' => 'arch_order']), '#attributes' => [ 'class' => [ 'field--label', ], ], ], 'value' => [ '#type' => 'html_tag', '#tag' => 'div', '#value' => $price_formatter->formatGross($price, $formatter_defaults), '#attributes' => [ 'class' => [ 'field--item', ], ], ], ]; } if ( $display->getComponent('payment_fee') && !empty($payment_fee) && (float) $payment_fee->get('calculated_gross')->getValue() > 0 ) { $price = $price_factory->getInstance([ 'base' => 'gross', 'net' => $payment_fee->get('calculated_net')->getValue(), 'gross' => $payment_fee->get('calculated_gross')->getValue(), 'vat_rate' => $payment_fee->get('calculated_vat_rate')->getValue(), 'vat_category' => $payment_fee->get('calculated_vat_cat_name')->getValue(), 'vat_amount' => $payment_fee->get('calculated_vat_amount')->getValue(), 'currency' => $entity->get('currency')->getString(), ]); $build['payment_fee'] = [ '#type' => 'container', '#attributes' => [ 'class' => [ 'field', 'field--name-payment-fee', 'field--type-string', 'field--label-inline', ], ], 'label' => [ '#type' => 'html_tag', '#tag' => 'div', '#value' => t('Payment fee', [], ['context' => 'arch_order']), '#attributes' => [ 'class' => [ 'field--label', ], ], ], 'value' => [ '#type' => 'html_tag', '#tag' => 'div', '#value' => $price_formatter->formatGross($price, $formatter_defaults), '#attributes' => [ 'class' => [ 'field--item', ], ], ], ]; } if ( $display->getComponent('exchange_rate') && isset($order_data['exchange_rate']) && !empty($order_data['exchange_rate']) ) { $exchange_currency = NULL; if (isset($order_data['exchange_currency'])) { $exchange_currency = '<span class="exchange-currency"><b>' . t('Source currency', [], ['context' => 'arch_order'])->render() . '</b>: ' . $order_data['exchange_currency'] . '</span>'; } $build['exchange_rate'] = [ '#type' => 'container', '#attributes' => [ 'class' => [ 'field', 'field--name-exchange-rate', 'field--type-string', 'field--label-inline', ], ], 'label' => [ '#type' => 'html_tag', '#tag' => 'div', '#value' => t('Exchange rate', [], ['context' => 'arch_order']), '#attributes' => [ 'class' => [ 'field--label', ], ], ], 'value' => [ '#type' => 'html_tag', '#tag' => 'div', '#value' => $order_data['exchange_rate'] . ($exchange_currency ?: ''), '#attributes' => [ 'class' => [ 'field--item', ], ], ], ]; } } /** * Add english translation for the order mails. */ function _arch_order_install_english_order_mails() { /** @var \Drupal\arch_order\OrderMail\OrderMailManagerInterface $mail_manager */ $mail_manager = \Drupal::service('arch_order_mail'); $langcode = 'en'; $mail_list = [ 'order_confirmation_to_user' => [ 'subject' => 'Thanks for your order!', 'body' => 'Thank you for your purchase!', ], 'order_confirmation_to_shop' => [ 'subject' => 'New order', 'body' => 'New order received!', ], 'order_modification' => [ 'subject' => 'Order modified', 'body' => 'The order has changed!', ], 'order_status_change' => [ 'subject' => 'Order status changed', 'body' => 'Order status has been updated!', ], ]; foreach ($mail_list as $plugin_id => $item) { /** @var \Drupal\arch_order\OrderMail\OrderMailBase $plugin */ $plugin = $mail_manager->get($plugin_id); if (!$plugin) { continue; } $plugin->setTranslation( $langcode, $item['subject'], [ 'format' => 'basic_html', 'value' => $item['body'], ] ); } } /** * Implements hook_theme_suggestions_HOOK(). */ function arch_order_theme_suggestions_order(array $variables) { $suggestions = []; /** @var \Drupal\arch_order\Entity\OrderInterface $order */ $order = $variables['elements']['#order']; $sanitized_view_mode = strtr($variables['elements']['#view_mode'], '.', '_'); $suggestions[] = 'order__' . $sanitized_view_mode; $suggestions[] = 'order__' . $order->id(); $suggestions[] = 'order__' . $order->id() . '__' . $sanitized_view_mode; return $suggestions; } /** * Implements hook_menu_links_discovered_alter(). */ function arch_order_menu_links_discovered_alter(&$links) { if (\Drupal::moduleHandler()->moduleExists('field_ui')) { $links['entity.order.field_ui_fields_'] = [ 'title' => t('Manage fields'), 'route_name' => 'entity.order.field_ui_fields', 'menu_name' => 'admin', 'parent' => 'entity.order.admin_form', ]; $links['entity.entity_form_display.order.default_'] = [ 'title' => t('Manage form display'), 'route_name' => 'entity.entity_form_display.order.default', 'menu_name' => 'admin', 'parent' => 'entity.order.admin_form', ]; $links['entity.entity_view_display.order.default_'] = [ 'title' => t('Manage display'), 'route_name' => 'entity.entity_view_display.order.default', 'menu_name' => 'admin', 'parent' => 'entity.order.admin_form', ]; } } /** * Fetches an array of permission IDs granted to the given user ID. * * The implementation here provides only the universal "all" grant. A produc * access module should implement hook_order_grants() to provide a grant list * for the user. * * After the default grants have been loaded, we allow modules to alter the * grants array by reference. This hook allows for complex business logic to be * applied when integrating multiple order access modules. * * @param string $op * The operation that the user is trying to perform. * @param \Drupal\Core\Session\AccountInterface $account * The account object for the user performing the operation. * * @return array * An associative array in which the keys are realms, and the values are * arrays of grants for those realms. */ function arch_order_access_grants($op, AccountInterface $account) { // Fetch order access grants from other modules. $grants = \Drupal::moduleHandler()->invokeAll('order_grants', [$account, $op]); // Allow modules to alter the assigned grants. \Drupal::moduleHandler()->alter('order_grants', $grants, $account, $op); return array_merge(['all' => [0]], $grants); } /** * Implements hook_entity_operation(). */ function arch_order_entity_operation(EntityInterface $entity) { if ($entity->getEntityTypeId() === 'order') { $operations = []; $has_view_access = $entity->access('view'); if ($has_view_access) { $operations['view'] = [ 'title' => t('View'), 'weight' => 20, 'url' => $entity->toUrl(), ]; } $has_revision_view_access = $entity->access('view all order revisions'); if ($has_revision_view_access && $entity->hasLinkTemplate('version-history')) { $operations['revisions'] = [ 'title' => t('Revisions'), 'weight' => 50, 'url' => $entity->toUrl('version-history'), ]; } return $operations; } } /** * Implements hook_entity_operation_alter(). */ function arch_order_entity_operation_alter(array &$operations, EntityInterface $entity) { if ($entity->getEntityTypeId() === 'order') { $current_language = \Drupal::languageManager()->getCurrentLanguage(); foreach ($operations as $key => $operation) { $operations[$key]['url']->setOption('language', $current_language); } } } /** * Implements hook_element_info_alter(). * * @see \Drupal\arch_order\Element\OrderStatusesSelect */ function arch_order_element_info_alter(array &$info) { // Alter the order_statuses_select element so that it will be rendered like a // select field. if (isset($info['order_statuses_select'])) { if (!isset($info['order_statuses_select']['#process'])) { $info['order_statuses_select']['#process'] = []; } if (!isset($info['order_statuses_select']['#theme_wrappers'])) { $info['order_statuses_select']['#theme_wrappers'] = []; } $info['order_statuses_select']['#process'] = array_merge( $info['order_statuses_select']['#process'], [ 'arch_order_process_order_statuses_select', ['Drupal\Core\Render\Element\Select', 'processSelect'], ['Drupal\Core\Render\Element\RenderElement', 'processAjaxForm'], ] ); $info['order_statuses_select']['#theme'] = 'select'; $info['order_statuses_select']['#theme_wrappers'] = array_merge($info['order_statuses_select']['#theme_wrappers'], ['form_element']); $info['order_statuses_select']['#multiple'] = FALSE; } } /** * Processes a order statuses select list form element. * * @param array $element * The form element to process. * * @return array * The processed form element. */ function arch_order_process_order_statuses_select(array $element) { if (!isset($element['#options'])) { /** @var \Drupal\arch_order\Entity\OrderStatusInterface[] $order_statuses */ $order_statuses = \Drupal::service('order.statuses')->getOrderStatuses(); foreach ($order_statuses as $id => $order_status) { $element['#options'][$id] = $order_status->getLabel(); } } return $element; } /** * Implements hook_local_tasks_alter(). */ function arch_order_local_tasks_alter(&$local_tasks) { if (isset($local_tasks['entity.revisions_overview:order'])) { unset($local_tasks['entity.revisions_overview:order']); } } /** * Implements hook_entity_load(). */ function arch_order_entity_load(array $entities, $entity_type_id) { if ($entity_type_id !== 'order') { return; } /** @var \Drupal\arch_order\Services\OrderAddressServiceInterface $order_address_service */ $order_address_service = \Drupal::service('order.address'); /** @var \Drupal\arch_payment\PaymentMethodManagerInterface $payment_method_manager */ $payment_method_manager = \Drupal::service('plugin.manager.payment_method'); /** @var \Drupal\arch_order\Entity\OrderInterface $entity */ foreach ($entities as $entity) { $entity->setOrderAddressService($order_address_service); $addresses = $order_address_service->getAddresses($entity->id()); if (!empty($addresses)) { foreach ($addresses as $address) { if ($address->address_type == OrderAddressServiceInterface::TYPE_BILLING) { $entity->setBillingAddress($address); } elseif ($address->address_type == OrderAddressServiceInterface::TYPE_SHIPPING) { $entity->setShippingAddress($address); } } } $payment_method = $entity->get('payment_method'); if (!empty($payment_method)) { $payment_method_plugin_id = $payment_method->getString(); try { $payment_method = $payment_method_manager->createInstance($payment_method_plugin_id); $entity->setPaymentMethod($payment_method); } catch (PluginException $e) { // @todo Handle error. } } } } /** * Implements hook_checkout_completed(). */ function arch_order_checkout_completed(OrderInterface $order) { if ($order->getEntityTypeId() !== 'order') { return; } /** @var \Drupal\arch_order\OrderMail\OrderMailManagerInterface $order_mail_manager */ $order_mail_manager = \Drupal::service('arch_order_mail'); $order_mail_manager->send('order_confirmation_to_user', $order); $order_mail_manager->send('order_confirmation_to_shop', $order); } /** * Implements hook_entity_update(). */ function arch_order_entity_update(EntityInterface $entity) { if (\Drupal::isConfigSyncing()) { // Do not change data while config import in progress. return; } if ($entity->getEntityTypeId() !== 'order' || !isset($entity->original)) { return; } /** @var \Drupal\arch_order\OrderMail\OrderMailManagerInterface $order_mail_manager */ $order_mail_manager = \Drupal::service('arch_order_mail'); $old_status = $entity->original->get('status')->getValue()[0]['value']; $new_status = $entity->get('status')->getValue()[0]['value']; if ($old_status !== $new_status) { $order_mail_manager->send('order_status_change', $entity); } else { $order_mail_manager->send('order_modification', $entity); } } /** * Checks if the current page is the full page view of the passed-in order. * * @param Drupal\arch_order\Entity\OrderInterface $order * A order entity. * * @return int|false * The ID of the order if this is a full page view, otherwise FALSE. */ function arch_order_is_page(OrderInterface $order) { $route_match = \Drupal::routeMatch(); if ($route_match->getRouteName() == 'entity.order.canonical') { $page = $route_match->getParameter('order'); } return (!empty($page) ? $page->id() == $order->id() : FALSE); } /** * Prepares variables for order templates. * * Default template: order.html.twig. * * @param array $variables * An associative array containing: * - elements: An array of elements to display in view mode. * - order: The order object. * - view_mode: View mode; e.g., 'full', 'teaser', etc. */ function template_preprocess_order(array &$variables) { $variables['view_mode'] = $variables['elements']['#view_mode']; // Provide a distinct $teaser boolean. $variables['teaser'] = $variables['view_mode'] == 'teaser'; $variables['order'] = $variables['elements']['#order']; /** @var \Drupal\arch_order\Entity\OrderInterface $order */ $order = $variables['order']; $variables['date'] = \Drupal::service('renderer')->render($variables['elements']['created']); unset($variables['elements']['created']); $variables['author_name'] = \Drupal::service('renderer')->render($variables['elements']['uid']); unset($variables['elements']['uid']); $variables['url'] = $order->toUrl('canonical'); $variables['label'] = $order->get($order->getEntityType()->getKey('label'))->getString(); // The 'page' variable is set to TRUE if: // - The view mode is 'full' and we are on the 'order.view' route. $variables['page'] = ( $variables['view_mode'] == 'full' && arch_order_is_page($order) ); // Helpful $content variable for templates. $variables += ['content' => []]; foreach (Element::children($variables['elements']) as $key) { $variables['content'][$key] = $variables['elements'][$key]; } // Used by RDF to add attributes around the author and date submitted. $variables['author_attributes'] = new Attribute(); // Add article ARIA role. $variables['attributes']['role'] = 'article'; } /** * Prepares variables for order templates. * * Default template: order-line-items--advanced.html.twig. * * @param array $variables * An associative array containing: * - elements: An array of elements to display in view mode. * - order: The order object. * - products: Product entities object. */ function template_preprocess_order_line_items__advanced(array &$variables) { // Add table ARIA role. $variables['attributes']['role'] = 'table'; $variables['attributes']['class'][] = 'order--line-items'; $variables['attributes']['class'][] = 'advanced'; $variables['order'] = $variables['elements']['#order']; $variables['products'] = $variables['elements']['#products']; $variables['rows'] = $variables['elements']['#rows']; $variables['url'] = $variables['elements']['#url']; } /** * Implements hook_mail(). */ function arch_order_mail($key, &$message, $params) { switch ($key) { case 'arch_order_mail_manager': if (isset($params['from'])) { $message['from'] = $params['from']; } else { $message['from'] = \Drupal::config('system.site')->get('mail'); } $message['subject'] = $params['subject']; $html = check_markup($params['message'], 'basic_html'); $message['body'][] = new FormattableMarkup($html, []); // Set HTML Header. $message['headers']['Content-Type'] = 'text/html; charset=UTF-8; format=flowed; delsp=yes'; $message['params']['format'] = 'text/html'; $message['params']['plain'] = NULL; break; } }