You are here

commerce_invoice.module in Commerce Invoice 8.2

Same filename and directory in other branches
  1. 7.2 commerce_invoice.module
  2. 7 commerce_invoice.module

Defines the Invoice entity and associated features.

File

commerce_invoice.module
View source
<?php

/**
 * @file
 * Defines the Invoice entity and associated features.
 */
use Drupal\commerce\EntityHelper;
use Drupal\commerce\PurchasableEntityInterface;
use Drupal\commerce_invoice\Entity\Invoice;
use Drupal\commerce_invoice\Entity\InvoiceInterface;
use Drupal\commerce_invoice\Entity\InvoiceType;
use Drupal\Core\Entity\EntityFormInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
use Drupal\language\Entity\ContentLanguageSettings;

/**
 * Implements hook_entity_type_alter().
 */
function commerce_invoice_entity_type_alter(array &$entity_types) {

  /** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */
  if (isset($entity_types['commerce_order'])) {
    $entity_types['commerce_order']
      ->setLinkTemplate('invoices', '/admin/commerce/orders/{commerce_order}/invoices');
    $entity_types['commerce_order']
      ->setLinkTemplate('credit-memos', '/admin/commerce/orders/{commerce_order}/credit-memos');
    $entity_types['commerce_order']
      ->setLinkTemplate('invoice-add-form', '/admin/commerce/orders/{commerce_order}/invoice/add/{commerce_invoice_type}');
  }
}

/**
 * Implements hook_entity_bundle_info().
 */
function commerce_invoice_entity_bundle_info() {
  $purchasable_entity_types = commerce_invoice_get_purchasable_entity_types();
  $bundles = [];
  foreach ($purchasable_entity_types as $entity_type_id => $entity_type) {
    $bundles['commerce_invoice_item'][$entity_type_id] = [
      'label' => $entity_type
        ->getLabel(),
      'translatable' => FALSE,
      'provider' => 'commerce_invoice',
    ];
  }
  return $bundles;
}

/**
 * Gets the purchasable entity types.
 *
 * @return \Drupal\Core\Entity\EntityTypeInterface[]
 *   The purchasable entity types, keyed by entity type ID.
 */
function commerce_invoice_get_purchasable_entity_types() {
  $entity_types = \Drupal::entityTypeManager()
    ->getDefinitions();
  return array_filter($entity_types, function (EntityTypeInterface $entity_type) {
    return $entity_type
      ->entityClassImplements(PurchasableEntityInterface::class);
  });
}

/**
 * Implements hook_form_FORM_ID_alter() for 'commerce_order_type_form'.
 */
function commerce_invoice_form_commerce_order_type_form_alter(array &$form, FormStateInterface $form_state) {

  /** @var \Drupal\commerce_order\Entity\OrderTypeInterface $order_type */
  $order_type = $form_state
    ->getFormObject()
    ->getEntity();
  $invoice_type_storage = \Drupal::entityTypeManager()
    ->getStorage('commerce_invoice_type');
  $invoice_types = EntityHelper::extractLabels($invoice_type_storage
    ->loadMultiple());
  $invoice_type_id = $order_type
    ->getThirdPartySetting('commerce_invoice', 'invoice_type');
  $order_placed_generation = $order_type
    ->getThirdPartySetting('commerce_invoice', 'order_placed_generation', FALSE);
  $form['commerce_invoice'] = [
    '#type' => 'details',
    '#title' => t('Invoice settings'),
    '#weight' => 6,
    '#open' => TRUE,
  ];
  $form['commerce_invoice']['enable_invoice'] = [
    '#type' => 'checkbox',
    '#title' => t('Invoice orders of this type'),
    '#default_value' => !empty($invoice_type_id),
  ];
  $form['commerce_invoice']['invoice_type'] = [
    '#type' => 'select',
    '#title' => t('Invoice type'),
    '#options' => $invoice_types,
    '#default_value' => $invoice_type_id ?: key($invoice_types),
    '#required' => TRUE,
    '#states' => [
      'visible' => [
        ':input[name="commerce_invoice[enable_invoice]"]' => [
          'checked' => TRUE,
        ],
      ],
    ],
  ];
  $form['commerce_invoice']['order_placed_generation'] = [
    '#type' => 'checkbox',
    '#title' => t('Invoice when the order is placed'),
    '#default_value' => $order_placed_generation,
    '#states' => [
      'visible' => [
        ':input[name="commerce_invoice[enable_invoice]"]' => [
          'checked' => TRUE,
        ],
      ],
    ],
  ];
  $form['actions']['submit']['#submit'][] = 'commerce_invoice_order_type_form_submit';
}

/**
 * Submission handler for commerce_invoice_form_commerce_order_type_form_alter().
 */
function commerce_invoice_order_type_form_submit($form, FormStateInterface $form_state) {
  $settings = $form_state
    ->getValue([
    'commerce_invoice',
  ]);

  /** @var \Drupal\commerce_order\Entity\OrderTypeInterface $order_type */
  $order_type = $form_state
    ->getFormObject()
    ->getEntity();
  $invoice_type = $settings['enable_invoice'] ? $settings['invoice_type'] : NULL;
  $order_placed_generation = $settings['enable_invoice'] ? $settings['order_placed_generation'] : FALSE;
  $order_type
    ->setThirdPartySetting('commerce_invoice', 'invoice_type', $invoice_type);
  $order_type
    ->setThirdPartySetting('commerce_invoice', 'order_placed_generation', $order_placed_generation);
  $order_type
    ->save();
}

/**
 * Submit handler for the commerce_invoice_language_configuration element.
 */
function commerce_invoice_language_configuration_element_submit(&$form, FormStateInterface $form_state) {

  // Iterate through all the language_configuration elements and save their
  // values.
  // In case we are editing a bundle, we must check the new bundle name,
  // because e.g. hook_ENTITY_update fired before.
  if ($language = $form_state
    ->get('language')) {
    foreach ($language as $element_name => $values) {
      $entity_type_id = $values['entity_type'];
      $bundle = $values['bundle'];
      $form_object = $form_state
        ->getFormObject();
      if ($form_object instanceof EntityFormInterface) {

        /** @var \Drupal\Core\Entity\EntityFormInterface $form_object */
        $entity = $form_object
          ->getEntity();
        if ($entity
          ->getEntityType()
          ->getBundleOf()) {
          $bundle = $entity
            ->id();
          $language[$element_name]['bundle'] = $bundle;
        }
      }
      $config = ContentLanguageSettings::loadByEntityTypeBundle($entity_type_id, $bundle);
      $values = $form_state
        ->getValue([
        $element_name,
      ]);
      $config
        ->setDefaultLangcode($values['langcode']);
      $config
        ->setThirdPartySetting('commerce_invoice', 'generate_translations', $values['generate_translations']);
      $config
        ->setLanguageAlterable(FALSE);
      $config
        ->save();

      // Set the form_state language with the updated bundle.
      $form_state
        ->set('language', $language);
    }
  }
}

/**
 * Implements hook_form_BASE_FORM_ID_alter() for 'state_machine_transition_form'.
 */
function commerce_invoice_form_state_machine_transition_form_alter(array &$form, FormStateInterface $form_state) {
  $entity = $form_state
    ->getFormObject()
    ->getEntity();
  if ($entity instanceof InvoiceInterface) {
    foreach (Element::children($form['actions']) as $action) {

      // Append the invoice type label to the transition label.
      $form['actions'][$action]['#value'] = t('@transition_label @invoice_bundle', [
        '@transition_label' => $form['actions'][$action]['#value'],
        // @todo Stop using mb_strtolower() when
        //   https://www.drupal.org/project/drupal/issues/2765065 is fixed.
        '@invoice_bundle' => mb_strtolower($entity
          ->get('type')->entity
          ->label()),
      ]);
    }
  }
}

/**
 * Implements hook_form_FORM_ID_alter() for 'log_comment_form'.
 */
function commerce_invoice_form_log_comment_form_alter(array &$form, FormStateInterface $form_state) {

  // Use the invoice type label instead of the generic 'Invoice' label for the
  // activity form.
  if ($form['log_comment']['source_entity_type']['#value'] === 'commerce_invoice') {
    $invoice = Invoice::load($form['log_comment']['source_entity_id']['#value']);
    $form['log_comment']['#title'] = t('Comment on this @label', [
      '@label' => $invoice
        ->get('type')->entity
        ->label(),
    ]);
  }
}

/**
 * Implements hook_field_widget_multivalue_form_alter().
 */
function commerce_invoice_field_widget_multivalue_form_alter(array &$elements, FormStateInterface $form_state, array $context) {
  $field_definition = $context['items']
    ->getFieldDefinition();
  if ($field_definition
    ->getTargetEntityTypeId() === 'commerce_invoice_item' && $field_definition
    ->getName() === 'adjustments') {

    // Invoice item adjustments can be modified, just like their quantity and
    // unit price, but users shouldn't be able to add new adjustments at that
    // level.
    foreach ($context['items'] as $delta => $item) {
      if ($item
        ->isEmpty()) {
        unset($elements[$delta]);
        $elements['#max_delta']--;
      }
    }
    $elements['add_more']['#access'] = FALSE;
  }
}

/**
 * Implements hook_views_data_alter().
 */
function commerce_invoice_views_data_alter(array &$data) {
  $data['commerce_invoice_field_data']['store_id']['field']['id'] = 'commerce_store';
  $data['commerce_invoice_field_data']['state']['filter']['id'] = 'state_machine_state';
}

/**
 * Implements hook_theme().
 */
function commerce_invoice_theme() {
  return [
    'commerce_invoice' => [
      'render element' => 'elements',
    ],
    'commerce_invoice__admin' => [
      'base hook' => 'commerce_invoice',
      'render element' => 'elements',
    ],
    'commerce_invoice_total_summary' => [
      'variables' => [
        'invoice_entity' => NULL,
        'totals' => NULL,
      ],
    ],
    'commerce_invoice_confirmation' => [
      'variables' => [
        'invoice_entity' => NULL,
        'totals' => NULL,
      ],
    ],
  ];
}

/**
 * Implements hook_theme_suggestions_HOOK().
 */
function commerce_invoice_theme_suggestions_commerce_invoice(array $variables) {
  return _commerce_entity_theme_suggestions('commerce_invoice', $variables);
}

/**
 * Implements hook_theme_suggestions_commerce_invoice_confirmation().
 */
function commerce_invoice_theme_suggestions_commerce_invoice_confirmation(array $variables) {
  $suggestions = [];
  if (isset($variables['invoice_entity'])) {
    $invoice = $variables['invoice_entity'];
    $suggestions[] = $variables['theme_hook_original'] . '__' . $invoice
      ->bundle();
  }
  return $suggestions;
}

/**
 * Prepares variables for invoice templates.
 *
 * Default template: commerce-invoice.html.twig.
 *
 * @param array $variables
 *   An associative array containing:
 *   - elements: An associative array containing rendered fields.
 *   - attributes: HTML attributes for the containing element.
 */
function template_preprocess_commerce_invoice(array &$variables) {

  /** @var Drupal\commerce_invoice\Entity\InvoiceInterface $invoice */
  $invoice = $variables['elements']['#commerce_invoice'];
  $token = \Drupal::token();
  $invoice_type_data = $invoice
    ->getData('invoice_type', []);
  $invoice_type = InvoiceType::create($invoice_type_data);

  // Replace the tokens in the footer text + the payment terms.
  $data = [
    'commerce_invoice' => $invoice,
  ];
  $footer_text = $invoice_type
    ->getFooterText();
  if ($footer_text) {
    $variables['footer_text'] = $token
      ->replace($footer_text, $data);
  }
  $payment_terms = $invoice_type
    ->getPaymentTerms();
  if ($payment_terms) {
    $variables['payment_terms'] = $token
      ->replace($payment_terms, $data);
  }
  if ($invoice_type
    ->getLogoUrl()) {
    $variables['logo_url'] = $invoice_type
      ->getLogoUrl();
  }
  $variables['invoice_entity'] = $invoice;
  $variables['invoice'] = [];
  foreach (Element::children($variables['elements']) as $key) {
    $variables['invoice'][$key] = $variables['elements'][$key];
  }
  if ($invoice
    ->getBillingProfile()) {
    $profile_view_bulder = \Drupal::entityTypeManager()
      ->getViewBuilder('profile');
    $variables['invoice']['billing_information'] = $profile_view_bulder
      ->view($invoice
      ->getBillingProfile());
  }

  /** @var \Drupal\commerce_invoice\InvoiceTotalSummaryInterface $invoice_total_summary */
  $invoice_total_summary = \Drupal::service('commerce_invoice.invoice_total_summary');
  $variables['totals'] = $invoice_total_summary
    ->buildTotals($invoice);
  if (\Drupal::moduleHandler()
    ->moduleExists('commerce_log')) {
    $variables['invoice']['activity'] = [
      '#type' => 'view',
      '#name' => 'commerce_activity',
      '#display_id' => 'default',
      '#arguments' => [
        $invoice
          ->id(),
        'commerce_invoice',
      ],
      '#embed' => TRUE,
      '#title' => t('Invoice activity'),
    ];
  }
}

/**
 * Implements hook_entity_operation().
 */
function commerce_invoice_entity_operation(EntityInterface $entity) {

  // Only show the "Invoices" operation link for orders.
  if ($entity
    ->getEntityTypeId() !== 'commerce_order') {
    return;
  }

  /** @var \Drupal\commerce_order\Entity\OrderInterface $entity */

  // Do not show the "Invoices" operation for draft orders.
  if ($entity
    ->getState()
    ->getId() == 'draft') {
    return;
  }

  // Only show if the user has the "administer commerce_invoice" permission.
  if (!\Drupal::currentUser()
    ->hasPermission('administer commerce_invoice')) {
    return;
  }
  $operations['invoices'] = [
    'title' => t('Invoices'),
    'url' => $entity
      ->toUrl('invoices'),
    'weight' => 50,
  ];
  return $operations;
}