Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <?php
- namespace Drupal\commerce_quickbooks_enterprise\SoapBundle\Services;
- use Drupal\commerce_quickbooks_enterprise\QbeUtilities;
- use Drupal\Core\Config\ConfigFactoryInterface;
- use Drupal\Core\Entity\EntityTypeManagerInterface;
- use Drupal\Core\State\StateInterface;
- use Drupal\migrate\MigrateExecutable;
- use Drupal\migrate\MigrateMessage;
- use Drupal\migrate\Plugin\MigratePluginManagerInterface;
- use Drupal\migrate\Plugin\MigrationPluginManager;
- use Drupal\user\Entity\User;
- use Drupal\user\UserAuthInterface;
- /**
- * Handle SOAP requests and return a response.
- *
- * Class SoapService.
- *
- * @package Drupal\commerce_quickbooks_enterprise\SoapBundle\Services
- */
- class SoapService implements SoapServiceInterface {
- /**
- * The row currently being migrated.
- *
- * @var \Drupal\migrate\Row
- */
- protected $row;
- /**
- * The migration plugin manager.
- *
- * @var \Drupal\migrate\Plugin\MigrationPluginManager
- */
- protected $migrationPluginManager;
- /**
- * The id map plugin manager.
- *
- * @var \Drupal\migrate\Plugin\MigratePluginManagerInterface
- */
- protected $idMapPluginManager;
- /**
- * The user auth service.
- *
- * @var \Drupal\user\UserAuthInterface
- */
- private $userAuthInterface;
- /**
- * The session manager.
- *
- * Responsible for managing, validating and invalidating SOAP sessions.
- *
- * @var \Drupal\commerce_quickbooks_enterprise\SoapBundle\Services\SoapSessionManager
- */
- protected $sessionManager;
- /**
- * The entity type manager.
- *
- * @var \Drupal\Core\Entity\EntityTypeManagerInterface
- */
- protected $entityTypeManager;
- /**
- * The module's configuration.
- *
- * @var \Drupal\Core\Config\ImmutableConfig
- */
- protected $config;
- /**
- * The state service.
- *
- * @var \Drupal\Core\State\StateInterface
- */
- protected $state;
- /**
- * The current server version.
- *
- * @var string
- */
- protected $serverVersion = '1.0';
- /**
- * The version returned by the client.
- *
- * @var string
- */
- protected $clientVersion;
- /**
- * The default order in which to process exportable QB Items.
- *
- * @var array
- */
- protected $itemPriorities = [
- 'add_customer',
- 'mod_customer',
- 'add_inventory_product',
- 'mod_inventory_product',
- 'add_non_inventory_product',
- 'mod_non_inventory_product',
- 'add_invoice',
- 'mod_invoice',
- 'add_sales_receipt',
- 'mod_sales_receipt',
- 'add_payment',
- ];
- /**
- * Constructs a new SoapService.
- *
- * @param \Drupal\migrate\Plugin\MigrationPluginManager $migrationPluginManager
- * The migration plugin manager.
- * @param \Drupal\migrate\Plugin\MigratePluginManagerInterface $idMapPluginManager
- * The id mapping plugin migrate manager.
- * @param \Drupal\user\UserAuthInterface $userAuthInterface
- * The user auth service.
- * @param \Drupal\commerce_quickbooks_enterprise\SoapBundle\Services\SoapSessionManager $sessionManager
- * The session manager.
- * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
- * The entity type manager.
- * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
- * The config factory.
- * @param \Drupal\Core\State\StateInterface $state
- * The state service.
- */
- public function __construct(
- MigrationPluginManager $migrationPluginManager,
- MigratePluginManagerInterface $idMapPluginManager,
- UserAuthInterface $userAuthInterface,
- SoapSessionManager $sessionManager,
- EntityTypeManagerInterface $entityTypeManager,
- ConfigFactoryInterface $configFactory,
- StateInterface $state
- ) {
- $this->migrationPluginManager = $migrationPluginManager;
- $this->idMapPluginManager = $idMapPluginManager;
- $this->userAuthInterface = $userAuthInterface;
- $this->sessionManager = $sessionManager;
- $this->entityTypeManager = $entityTypeManager;
- $this->config = $configFactory->get('commerce_quickbooks_enterprise.quickbooks_admin');
- $this->state = $state;
- }
- /**
- * {@inheritdoc}
- */
- public function __call($method, array $data) {
- \Drupal::logger('commerce_qbe')->info("QB SOAP service [$method] called. Incoming request: " . print_r($data, TRUE));
- $public_services = ['clientVersion', 'serverVersion', 'authenticate'];
- $request = $this->prepareResponse($method, $data);
- $uc = ucfirst($method);
- $callable = "call$uc";
- $response = NULL;
- // If the method being requested requires a validated user, do that now.
- if (!in_array($method, $public_services)) {
- // The request must have a ticket to proceed.
- if (empty($request->ticket)) {
- return $request;
- }
- $valid = $this->sessionManager
- ->setUuid($request->ticket)
- ->validateSession($method);
- // If the client has a valid ticket and request, log in now.
- if ($valid) {
- /** @var \Drupal\user\UserInterface $user */
- $user = User::load($this->sessionManager->getUid());
- user_login_finalize($user);
- if (!$user->hasPermission('access quickbooks soap service')) {
- \Drupal::logger('commerce_qbe')->warning('User logged in successfully but didn\'t have Quickbooks SOAP Service access permissions.');
- return $request;
- }
- }
- else {
- \Drupal::logger('commerce_qbe')->error('The user had an invalid session token or made an invalid request. Aborting communication...');
- return $request;
- }
- }
- // If a valid method method is being called, parse the incoming request
- // and call the method with the parsed data passed in.
- if (is_callable([$this, $callable])) {
- // Prepare the response to the client.
- $response = $this->$callable($request);
- }
- return $response;
- }
- /****************************************************
- * Private helper functions *
- ****************************************************/
- /**
- * Builds the stdClass object required by a service response handler.
- *
- * @param string $method_name
- * The Quickbooks method being called.
- * @param string $data
- * The raw incoming soap request.
- *
- * @return \stdClass
- * An object with the following properties:
- * stdClass {
- * methodNameResult => '',
- * requestParam1 => 'foo',
- * ...
- * requestParamN => 'bar',
- * }
- */
- private function prepareResponse($method_name, $data) {
- $response = isset($data[0]) ? $data[0] : new \stdClass();
- $response->$method_name = '';
- return $response;
- }
- /**
- * Calculate the completion progress of the current SOAP session.
- *
- * @return int
- * The percentage completed.
- */
- private function getCompletionProgress() {
- $done = 0;
- $todo = 0;
- foreach ($this->migrationPluginManager->createInstancesByTag('quickbooks_enterprise') as $id => $migration) {
- $map = $migration->getIdMap();
- $done += $map->importedCount();
- $todo += $migration->getSourcePlugin()->count() - $map->processedCount();
- }
- return $done + $todo ? (int) (100 * ($done / ($done + $todo))) : 1;
- }
- /****************************************************
- * The WSDL defined SOAP service calls *
- ****************************************************/
- /**
- * {@inheritdoc}
- */
- public function callServerVersion(\stdClass $request) {
- $request->serverVersionResult = $this->serverVersion;
- return $request;
- }
- /**
- * {@inheritdoc}
- */
- public function callClientVersion(\stdClass $request) {
- $this->clientVersion = $request->strVersion;
- $request->clientVersionResult = '';
- return $request;
- }
- /**
- * {@inheritdoc}
- *
- * @TODO: Reset failed exports id requested.
- */
- public function callAuthenticate(\stdClass $request) {
- $strUserName = $request->strUserName;
- $strPassword = $request->strPassword;
- // Initial "fail" response.
- $result = ['', 'nvu'];
- // If the service isn't set for whatever reason we can't continue.
- if (!isset($this->userAuthInterface)) {
- \Drupal::logger('commerce_qbe')->error("User Auth service couldn't be initialized.");
- }
- else {
- $uid = $this->userAuthInterface->authenticate($strUserName, $strPassword);
- if (!$uid) {
- \Drupal::logger('commerce_qbe')->error("Invalid login credentials, aborting quickbooks SOAP service.");
- }
- else {
- \Drupal::logger('commerce_qbe')->info("Quickbooks user $strUserName successfully connected! Commencing data exchange with client.");
- $uuid = \Drupal::service('uuid')->generate();
- $this->sessionManager->startSession($uuid, $uid);
- $result = [$uuid, ''];
- }
- }
- $request->authenticateResult = $result;
- return $request;
- }
- /**
- * {@inheritdoc}
- */
- public function callSendRequestXML(\stdClass $request) {
- \Drupal::logger('commerce_qbe')->info("Request received, searching for content to export.");
- $migrations = $this->migrationPluginManager->createInstancesByTag('quickbooks_enterprise');
- /** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
- foreach ($this->migrationPluginManager->buildDependencyMigration($migrations, []) as $migration) {
- // Proceed to next migration if there are no remaining items to import.
- $remaining = $migration->getSourcePlugin()->count() - $migration->getIdMapPlugin()->processedCount();
- if (!$remaining) {
- continue;
- }
- // Our MigrateSubscriber stops this migration after a single row.
- (new MigrateExecutable($migration, new MigrateMessage()))->import();
- $this->row = $this->state->get('qbe.current_row');
- if ($migration->getIdMap()->errorCount()) {
- $errors = iterator_to_array($migration->getIdMap()->getMessageIterator());
- $error = array_shift($errors);
- \Drupal::logger('commerce_qbe')->error($error->message);
- return $request;
- }
- // Let's end the import for now and we'll continue next time.
- break;
- }
- $callback = $this->row->getSourceProperty('static/send_callback');
- if (is_callable([$this, $callback])) {
- $qbxml = call_user_func([$this, $callback]);
- $request->sendRequestXMLResult = $this->addXMLEnvelope($qbxml);
- return $request;
- }
- \Drupal::logger('commerce_qbe')->error("Unable to prepare data for export. No method found for [$callback]");
- $this->state->get('qbe.current_row');
- return $request;
- }
- /**
- * Add an XML envelope.
- *
- * @param string $qbxml
- * The qbxml.
- *
- * @return string
- * The xml wrapped in an envelope.
- */
- protected function addXMLEnvelope($qbxml) {
- return '<?xml version="1.0" encoding="utf-8"?><?qbxml version="2.0"?><QBXML><QBXMLMsgsRq onError="stopOnError">' . $qbxml . '</QBXMLMsgsRq></QBXML>';
- }
- /**
- * {@inheritdoc}
- */
- public function callReceiveResponseXML(\stdClass $request) {
- $this->row = $this->state->get('qbe.current_row');
- $retry = FALSE;
- // Parse any errors if we have them to decide our next action.
- if (!empty($request->response)) {
- if ($code = QbeUtilities::extractStatusCode($request->response)) {
- $error = [
- 'statusCode' => $code,
- 'statusMessage' => QbeUtilities::extractStatusMessage($request->response),
- ];
- $error_msg = "Response error statusCode: " . print_r($error, TRUE);
- \Drupal::logger('commerce_qbe_errors')->error($error_msg);
- // 3180 is a temporary error with no clear reason. Just retry it.
- if ($error['statusCode'] == "3180") {
- $retry = TRUE;
- }
- }
- }
- $callback = $this->row->getSourceProperty('static/receive_callback');
- if (!$retry && is_callable([$this, $callback])) {
- call_user_func([$this, $callback], $request);
- }
- $request->receiveResponseXMLResult = $this->getCompletionProgress();
- return $request;
- }
- /**
- * Update identifiers.
- *
- * @param \stdClass $request
- * The request.
- */
- protected function updateIdentifier(\stdClass $request) {
- $identifier = QbeUtilities::extractIdentifiers($request->response, $this->row->getSource()['entity_type']);
- if ($identifier) {
- /** @var \Drupal\migrate\Plugin\Migration $migration */
- $migration = $this->migrationPluginManager->createInstance($this->state->get('qbe.current_migration'));
- $migration->getIdMap()->saveIdMapping($this->row, ['uuid' => $identifier]);
- }
- }
- /**
- * {@inheritdoc}
- */
- public function callGetLastError(\stdClass $request) {
- $progress = $this->getCompletionProgress();
- if ($progress == 100) {
- $request->getLastErrorResult = 'No new exports remaining.';
- }
- else {
- $request->getLastErrorResult = "$progress% remaining remaining.";
- }
- return $request;
- }
- /**
- * {@inheritdoc}
- */
- public function callCloseConnection(\stdClass $request) {
- $this->sessionManager->closeSession();
- $request->closeConnectionResult = 'OK';
- return $request;
- }
- /**
- * Parse profile entities into a template-ready object.
- */
- protected function prepareCustomerExport() {
- if ($this->row->getSourceProperty('bundle') == 'customer') {
- $uuid = $this->row->getDestinationProperty('uuid');
- $addresses = $this->row->getSourceProperty('address');
- $address = reset($addresses);
- $customer = new \QuickBooks_QBXML_Object_Customer();
- if (QbeUtilities::isQuickbooksIdentifier($uuid)) {
- $customer->setListID($uuid);
- return $customer->asQBXML(QUICKBOOKS_QUERY_CUSTOMER);
- }
- $address1 = $address['address_line1'];
- $address2 = $address['address_line2'];
- $address3 = '';
- $address4 = '';
- $address5 = $address['dependent_locality'];
- $city = $address['locality'];
- $state = $address['administrative_area'];
- $province = '';
- $postal_code = $address['postal_code'];
- $country = $address['country_code'];
- $customer->setBillAddress($address1, $address2, $address3, $address4, $address5, $city, $state, $province, $postal_code, $country);
- $customer->setFirstName($address['given_name']);
- $customer->setLastName($address['family_name']);
- $user = $this->entityTypeManager->getStorage('user')->load($this->row->getSourceProperty('uid'));
- $customer->setEmail($user->mail->value);
- return $customer->asQBXML(QUICKBOOKS_ADD_CUSTOMER);
- }
- }
- /**
- * Parse Order entities into a template-ready object.
- *
- * @return string
- * An xml export of order data.
- */
- protected function prepareOrderExport() {
- $isInvoice = $this->config->get('exportables')['order_type'] == 'invoices';
- $invoice = new \QuickBooks_QBXML_Object_Invoice();
- $orderId = $this->row->getSourceProperty('order_id');
- /** @var \Drupal\commerce_order\Entity\Order $order */
- $order = $this->entityTypeManager->getStorage('commerce_order')->load($orderId);
- /** @var \Drupal\migrate\Plugin\Migration $customerMigration */
- $customerMigration = $this->migrationPluginManager->createInstance('qbe_customer');
- /** @var \Drupal\profile\Entity\Profile $billingProfile */
- $billingProfile = $order->getBillingProfile();
- if ($db_row = $customerMigration->getIdMap()->getRowBySource(['profile_id' => $billingProfile->id()])) {
- $invoice->setCustomerListID($db_row['destid1']);
- }
- /** @var \Drupal\commerce_payment\Entity\PaymentInterface[] $payments */
- $payments = $this->entityTypeManager->getStorage('commerce_payment')->loadMultipleByOrder($order);
- $orderPrefix = $this->config->get('id_prefixes')['po_number_prefix'];
- $invoice->setRefNumber($orderPrefix . $orderId);
- $invoice->setTransactionDate($order->getCompletedTime());
- if ($billingProfile) {
- /** @var \Drupal\address\Plugin\Field\FieldType\AddressItem $address */
- $address = $billingProfile->address->get(0);
- $invoice->setCustomerFullName("{$address->getGivenName()} {$address->getFamilyName()}");
- $address1 = $address->getAddressLine1();
- $address2 = $address->getAddressLine2();
- $address3 = '';
- $address4 = '';
- $address5 = $address->getDependentLocality();
- $city = $address->getLocality();
- $state = $address->getAdministrativeArea();
- $province = '';
- $postal_code = $address->getPostalCode();
- $country = $address->getCountryCode();
- $invoice->setBillAddress($address1, $address2, $address3, $address4, $address5, $city, $state, $province, $postal_code, $country);
- }
- if ($order->hasField('shipments')) {
- foreach ($order->shipments->referencedEntities() as $shipment) {
- /** @var \Drupal\profile\Entity\Profile $shipping_profile */
- if ($shippingProfile = $shipment->getShippingProfile()) {
- break;
- }
- }
- }
- if (!empty($shippingProfile)) {
- /** @var \Drupal\address\Plugin\Field\FieldType\AddressItem $address */
- $address = $shippingProfile->address->get(0);
- $address1 = $address->getAddressLine1();
- $address2 = $address->getAddressLine2();
- $address3 = '';
- $address4 = '';
- $address5 = $address->getDependentLocality();
- $city = $address->getLocality();
- $state = $address->getAdministrativeArea();
- $province = '';
- $postal_code = $address->getPostalCode();
- $country = $address->getCountryCode();
- $invoice->setShipAddress($address1, $address2, $address3, $address4, $address5, $city, $state, $province, $postal_code, $country);
- }
- foreach ($payments as $payment) {
- if ($gateway = $payment->getPaymentGateway()) {
- $paymentMethod = $gateway->getPlugin()->getDisplayLabel();
- }
- }
- if (!empty($paymentMethod)) {
- $invoice->setPaymentMethodName($paymentMethod);
- }
- /** @var \Drupal\commerce_order\Entity\OrderItem $item */
- foreach ($order->getItems() as $item) {
- $line = $isInvoice ? new \QuickBooks_QBXML_Object_Invoice_InvoiceLine() : new \QuickBooks_QBXML_Object_SalesReceipt_SalesReceiptLine();
- /** @var \Drupal\commerce_product\Entity\ProductVariationInterface $purchasedItem */
- $purchasedItem = $item->getPurchasedEntity();
- $line->setItemName($purchasedItem->getSku());
- $line->setDescription($purchasedItem->label());
- $line->setQuantity($item->getQuantity());
- $line->setAmount($item->getUnitPrice()->getNumber());
- if ($isInvoice) {
- $invoice->addInvoiceLine($line);
- }
- else {
- $invoice->addListItem('SalesReceiptLineAdd', $line);
- }
- }
- /** @var \Drupal\commerce_order\Adjustment $adjustment */
- foreach ($order->getAdjustments() as $adjustment) {
- switch ($adjustment->getType()) {
- case 'tax':
- $taxName = $this->config->get('tax')['tax_name'];
- $invoice->setSalesTaxItemFullName($taxName);
- break;
- case 'shipping':
- $line = $isInvoice ? new \QuickBooks_QBXML_Object_Invoice_InvoiceLine() : new \QuickBooks_QBXML_Object_SalesReceipt_SalesReceiptLine();
- $shippingName = $this->config->get('shipping')['shipping_service'];
- $shippingDescription = $this->config->get('shipping')['shipping_service_description'];
- $line->setItemName($shippingName);
- $line->setDescription($shippingDescription);
- $line->setQuantity(1);
- $line->setAmount($adjustment->getAmount()->getNumber());
- if ($isInvoice) {
- $invoice->addInvoiceLine($line);
- }
- else {
- $invoice->addListItem('SalesReceiptLineAdd', $line);
- }
- break;
- }
- }
- return $isInvoice ? $invoice->asXML(\QUICKBOOKS_ADD_INVOICE)->asXML(\QuickBooks_XML::XML_DROP, '') : $invoice->asXML(QUICKBOOKS_ADD_SALESRECEIPT)->asXML(\QuickBooks_XML::XML_DROP, '');
- }
- /**
- * Parse payment entities into a template-ready object.
- *
- * @return string
- * An xml export of payment data.
- */
- protected function preparePaymentExport() {
- $receivePayment = new \QuickBooks_QBXML_Object_ReceivePayment();
- $paymentId = $this->row->getSourceProperty('payment_id');
- /** @var \Drupal\commerce_payment\Entity\Payment $payment */
- $payment = $this->entityTypeManager->getStorage('commerce_payment')->load($paymentId);
- $orderId = $payment->getOrderId();
- /** @var \Drupal\commerce_order\Entity\Order $order */
- $order = $this->entityTypeManager->getStorage('commerce_order')->load($orderId);
- /** @var \Drupal\migrate\Plugin\Migration $customerMigration */
- $customerMigration = $this->migrationPluginManager->createInstance('qbe_customer');
- /** @var \Drupal\profile\Entity\Profile $billingProfile */
- $billingProfile = $order->getBillingProfile();
- if ($db_row = $customerMigration->getIdMap()->getRowBySource(['profile_id' => $billingProfile->id()])) {
- $receivePayment->setCustomerListID($db_row['destid1']);
- }
- $paymentPrefix = $this->config->get('id_prefixes')['payment_prefix'];
- if ($paymentId = $payment->getRemoteId()) {
- $receivePayment->setRefNumber($paymentPrefix . $paymentId);
- }
- else {
- $receivePayment->setRefNumber($paymentPrefix . $payment->id());
- }
- $receivePayment->setPaymentMethodFullName($payment->getPaymentGateway()->label());
- $receivePayment->setTransactionDate($payment->getCompletedTime());
- $transactionAdd = new \QuickBooks_QBXML_Object_ReceivePayment_AppliedToTxn();
- /** @var \Drupal\migrate\Plugin\Migration $orderMigration */
- $orderMigration = $this->migrationPluginManager->createInstance('qbe_order');
- if ($db_row = $orderMigration->getIdMap()->getRowBySource(['order_id' => $order->id()])) {
- $transactionAdd->setTxnID($db_row['destid1']);
- $transactionAdd->setPaymentAmount($payment->getAmount()->getNumber());
- $receivePayment->addAppliedToTxn($transactionAdd);
- }
- else {
- $receivePayment->setIsAutoApply(TRUE);
- }
- return $receivePayment->asQBXML(\QUICKBOOKS_ADD_RECEIVEPAYMENT, \QuickBooks_XML::XML_DROP, '');
- }
- /**
- * Parse product variation entities into a template-ready object.
- *
- * @return string
- * An xml export of product variation data.
- */
- protected function prepareProductVariationExport() {
- $inventoryItem = new \QuickBooks_QBXML_Object_InventoryItem();
- $variationId = $this->row->getSourceProperty('variation_id');
- /** @var \Drupal\commerce_product\Entity\ProductVariation $variation */
- $variation = $this->entityTypeManager->getStorage('commerce_product_variation')->load($variationId);
- $inventoryItem->setName($variation->label());
- $inventoryItem->setSalesPrice($variation->getPrice()->getNumber());
- /** @var \Drupal\migrate\Plugin\Migration $productMigration */
- $productMigration = $this->migrationPluginManager->createInstance('qbe_product');
- /** @var \Drupal\Core\Language\LanguageManagerInterface $languageManager */
- $languageManager = \Drupal::service('language_manager');
- $langcode = $languageManager->getDefaultLanguage()->getId();
- if ($db_row = $productMigration->getIdMap()->getRowBySource(['product_id' => $variation->getProductId(), 'langcode' => $langcode])) {
- $inventoryItem->set('ParentRef ListID', $db_row['destid1']);
- }
- return $inventoryItem->asQBXML(\QUICKBOOKS_ADD_INVENTORYITEM, \QuickBooks_XML::XML_DROP, '');
- }
- /**
- * Parse product entities into a template-ready object.
- *
- * @return string
- * An xml export of product data.
- */
- protected function prepareProductExport() {
- $inventoryItem = new \QuickBooks_QBXML_Object_InventoryItem();
- $productId = $this->row->getSourceProperty('product_id');
- /** @var \Drupal\commerce_product\Entity\Product $product */
- $product = $this->entityTypeManager->getStorage('commerce_product')->load($productId);
- $inventoryItem->setName($product->label());
- return $inventoryItem->asQBXML(\QUICKBOOKS_ADD_INVENTORYITEM, \QuickBooks_XML::XML_DROP, '');
- }
- }
Add Comment
Please, Sign In to add comment