Advertisement
myworks

Feedback from user's developer around syncing tracking number backwards from QuickBooks into WooCom

Aug 11th, 2022 (edited)
1,191
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 19.78 KB | None | 0 0
  1. <?php
  2.  
  3. //What I ended up doing was a little tricky as there aren't wordpress actions/filters really to hook into in the desktop version of your plugin. I ended up creating a php class from within the theme files of the website, not physically touching any myworks plugin files. I started by programmatically backing up the public/class-mw-qbo-desktop-public.php file and then changing a line that includes the partials/mw-qbo-desktop-public-qwc-server.php to include my own version of that file. The top of that file has a require_once spot for the includes/class-mw-qbo-desktop-qwc-server-lib.php file. In this spot I require_once'd my own qbc-server-lib file.
  4.  
  5.        // Change file
  6.         $file_name = 'public/class-mw-qbo-desktop-public.php';
  7.         $file_uri = $this->myworks_plugin_dir_path . $file_name;
  8.  
  9.         // Make a copy
  10.         if ( !file_exists( $file_uri . '.bak' ) ) {
  11.             copy( $file_uri, $file_uri . '.bak' );
  12.         }
  13.  
  14.         if ( !is_dir( $this->child_mwqb_dir_path . 'public' ) ) {
  15.             mkdir( $this->child_mwqb_dir_path . 'public', 0777, true );
  16.         }
  17.  
  18.         copy( $file_uri . '.bak', str_replace( 'public/class-mw-qbo-desktop-public.php', 'public/temp_class-mw-qbo-desktop-public.php', $file_uri ) . '.bak' );
  19.  
  20.         rename( str_replace( 'public/class-mw-qbo-desktop-public.php', 'public/temp_class-mw-qbo-desktop-public.php', $file_uri ) . '.bak', $this->child_mwqb_dir_path . $file_name . '.bak' );
  21.  
  22.         $file = fopen( $file_uri, "r+" );
  23.         $text = fread( $file, filesize( $file_uri ) );
  24.         $lines = explode( PHP_EOL, $text );
  25.  
  26.         foreach ( $lines as $key => $line ) {
  27.             if ( strpos( $line, 'array_key_exists( \'mw_qbo_desktop_qwc_server\', $wp->query_vars )' ) !== false ) {
  28.                 $next_key = $key + 1;
  29.  
  30.                 if ( strpos( $lines[$next_key], 'require_once plugin_dir_path( __FILE__ ) . \'partials/mw-qbo-desktop-public-qwc-server.php\';' ) !== false ) {
  31.                     // Change line in file
  32.                     $lines[$next_key] = 'require_once \'' . $this->child_mwqb_dir_path . 'partials/child-mw-qbo-desktop-public-qwc-server.php\';';
  33.                 }
  34.             }
  35.         }
  36.         $lines = implode( PHP_EOL, $lines );
  37.        
  38.         rewind( $file );
  39.         fwrite( $file, $lines );
  40.  
  41.  
  42. //The part in question though, the tracking, I had to add a couple lines in my own qwc-server.php file. In the array under the comment "Map QuickBooks actions to hander functions", I added the importing invoices quickbooks handlers. The original adding invoice request and response are still in place and I added my code at the bottom. I think I found these extra handlers flipping through the includes/lib/qbo-lib files in the depths of the plugin. (I actually had to implement a way to trigger customer sync's as well with Customer_MOD_Import_Request but that was for some different functionality with price levels. My client was demanding.).
  43.  
  44.         // Map QuickBooks actions to handler functions
  45.         $map = array(
  46.             QUICKBOOKS_ADD_CUSTOMER => array( array( $MWQDC_QWC, 'AddCustomerRequest' ), array( $MWQDC_QWC, 'AddCustomerResponse' ) ),
  47.             QUICKBOOKS_ADD_GUEST => array( array( $MWQDC_QWC, 'AddGuestRequest' ), array( $MWQDC_QWC, 'AddGuestResponse' ) ),
  48.            
  49.             'CustomerJobAdd' => array( array( $MWQDC_QWC, 'AddCustomerJobRequest' ), array( $MWQDC_QWC, 'AddCustomerJobResponse' ) ),
  50.            
  51.             QUICKBOOKS_ADD_INVOICE => array( array( $MWQDC_QWC, 'AddInvoiceRequest' ), array( $MWQDC_QWC, 'AddInvoiceResponse' ) ),
  52.             QUICKBOOKS_ADD_SALESRECEIPT => array( array( $MWQDC_QWC, 'AddSalesReceiptRequest' ), array( $MWQDC_QWC, 'AddSalesReceiptResponse' ) ),
  53.            
  54.             //
  55.             'SalesReceiptMod_GPI' => array( array( $MWQDC_QWC, 'UpdateSalesReceiptRequest_GPI' ), array( $MWQDC_QWC, 'UpdateSalesReceiptResponse_GPI' ) ),
  56.            
  57.             /**/
  58.             'InvoiceDataExt' => array( array( $MWQDC_QWC, 'OrderDataExtAddRequest' ), array( $MWQDC_QWC, 'OrderDataExtAddResponse' ) ),
  59.             'SalesReceiptDataExt' => array( array( $MWQDC_QWC, 'OrderDataExtAddRequest' ), array( $MWQDC_QWC, 'OrderDataExtAddResponse' ) ),
  60.             'SalesOrderDataExt' => array( array( $MWQDC_QWC, 'OrderDataExtAddRequest' ), array( $MWQDC_QWC, 'OrderDataExtAddResponse' ) ),
  61.             'EstimateDataExt' => array( array( $MWQDC_QWC, 'OrderDataExtAddRequest' ), array( $MWQDC_QWC, 'OrderDataExtAddResponse' ) ),
  62.            
  63.             QUICKBOOKS_ADD_SALESORDER => array( array( $MWQDC_QWC, 'AddSalesOrderRequest' ), array( $MWQDC_QWC, 'AddSalesOrderResponse' ) ),
  64.            
  65.             //
  66.             QUICKBOOKS_ADD_ESTIMATE => array( array( $MWQDC_QWC, 'AddEstimateRequest' ), array( $MWQDC_QWC, 'AddEstimateResponse' ) ),
  67.            
  68.             //
  69.             QUICKBOOKS_ADD_PURCHASEORDER => array( array( $MWQDC_QWC, 'AddPurchaseOrderRequest' ), array( $MWQDC_QWC, 'AddPurchaseOrderResponse' ) ),
  70.            
  71.             'ReceivePaymentAdd' => array( array( $MWQDC_QWC, 'AddPaymentRequest' ), array( $MWQDC_QWC, 'AddPaymentResponse' ) ),
  72.            
  73.             'OrderPaymentAdd' => array( array( $MWQDC_QWC, 'AddOrderPaymentRequest' ), array( $MWQDC_QWC, 'AddOrderPaymentResponse' ) ),
  74.            
  75.             /*Update*/
  76.             'CustomerMod_Query' => array( array( $MWQDC_QWC, 'CustomerMod_Query_Request' ), array( $MWQDC_QWC, 'CustomerMod_Query_Response' ) ),
  77.             QUICKBOOKS_MOD_CUSTOMER => array( array( $MWQDC_QWC, 'UpdateCustomerRequest' ), array( $MWQDC_QWC, 'UpdateCustomerResponse' ) ),
  78.            
  79.             //
  80.             'CheckAdd' => array( array( $MWQDC_QWC, 'AddRefundRequest' ), array( $MWQDC_QWC, 'AddRefundResponse' ) ),
  81.            
  82.             'CreditMemoAdd' => array( array( $MWQDC_QWC, 'AddRefundRequest' ), array( $MWQDC_QWC, 'AddRefundResponse' ) ),
  83.            
  84.             QUICKBOOKS_ADD_INVENTORYITEM => array( array( $MWQDC_QWC, 'AddInventoryItemRequest' ), array( $MWQDC_QWC, 'AddInventoryItemResponse' ) ),
  85.             QUICKBOOKS_ADD_NONINVENTORYITEM => array( array( $MWQDC_QWC, 'AddNonInventoryRequest' ), array( $MWQDC_QWC, 'AddNonInventoryResponse' ) ),
  86.             QUICKBOOKS_ADD_SERVICEITEM => array( array( $MWQDC_QWC, 'AddServiceItemRequest' ), array( $MWQDC_QWC, 'AddServiceItemResponse' ) ),
  87.            
  88.             'V_'.QUICKBOOKS_ADD_INVENTORYITEM => array( array( $MWQDC_QWC, 'AddInventoryItemRequest' ), array( $MWQDC_QWC, 'AddInventoryItemResponse' ) ),
  89.             'V_'.QUICKBOOKS_ADD_NONINVENTORYITEM => array( array( $MWQDC_QWC, 'AddNonInventoryRequest' ), array( $MWQDC_QWC, 'AddNonInventoryResponse' ) ),
  90.             'V_'.QUICKBOOKS_ADD_SERVICEITEM => array( array( $MWQDC_QWC, 'AddServiceItemRequest' ), array( $MWQDC_QWC, 'AddServiceItemResponse' ) ),
  91.            
  92.             'InventoryAdjustmentAdd' => array( array( $MWQDC_QWC, 'InventoryAdjustmentAddRequest' ), array( $MWQDC_QWC, 'InventoryAdjustmentAddResponse' ) ),
  93.             'V_InventoryAdjustmentAdd' => array( array( $MWQDC_QWC, 'InventoryAdjustmentAddRequest' ), array( $MWQDC_QWC, 'InventoryAdjustmentAddResponse' ) ),
  94.  
  95.             /** @author M.E. */
  96.             QUICKBOOKS_IMPORT_INVOICE => array( array( $MWQDC_QWC, 'Invoice_Import_Request' ), array( $MWQDC_QWC, 'Invoice_Import_Response') ),
  97.             'New_' . QUICKBOOKS_IMPORT_INVOICE => array( array( $MWQDC_QWC, 'Invoice_Import_Request' ), array( $MWQDC_QWC, 'Invoice_Import_Response') ),
  98.  
  99.             'Manage_' . QUICKBOOKS_MOD_CUSTOMER => array( array( $MWQDC_QWC, 'Customer_MOD_Import_Request' ), array( $MWQDC_QWC, 'Customer_Import_Response' ) ),
  100.         );
  101.  
  102. //Then within the includes/class-mw-qbo-desktop-qwc-server-lib.php file, Within the Hook_Login_Success() method - which runs every web connector connection - I added the New_Quickbooks_Import_Invoice action to the $Queue.
  103.  
  104.                 / **
  105.                  * @author M.E.
  106.                  */
  107.                 if ( class_exists( 'Child_MWQB' ) ) {
  108.                     if (!$this->_quickbooks_get_last_run($user, 'New_'.QUICKBOOKS_IMPORT_ITEM)){
  109.                         $this->_quickbooks_set_last_run($user, 'New_'.QUICKBOOKS_IMPORT_ITEM, $date);
  110.                     }
  111.  
  112.                     $Child_MWQB = new Child_MWQB();
  113.                     //$Child_MWQB->new_customer_import_response( $arr['ListID'] );
  114.                     $Queue->enqueue( 'New_' . QUICKBOOKS_IMPORT_INVOICE, null, QB_PRIORITY_REFRESH_DATA_IMPORT, array( 'new'=>true, 'timestamp_from'=>$timestamp_from ), $this->get_qbun() );
  115.                 }
  116.                 /* End @author M.E. */
  117.  
  118. //The Invoice_Import_Request and Response, I created by mostly borrowing parts from elsewhere within the file, I believe. I added a spot within the response that looks for the tracking number.
  119.  
  120.      /**
  121.      * @author M.E.
  122.      */
  123.     public function Invoice_Import_Request( $requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $version, $locale ) {
  124.         if(!$this->is_qwc_connected()){
  125.             return false;
  126.         }
  127.  
  128.         $attr_iteratorID = '';
  129.         $attr_iterator = ' iterator="Start" ';
  130.         if (empty($extra['iteratorID'])){
  131.             $last = $this->_quickbooks_get_last_run($user, $action);
  132.             $this->_quickbooks_set_last_run($user, $action);
  133.             $this->_quickbooks_set_current_run($user, $action, $last);
  134.         }else{
  135.             $attr_iteratorID = ' iteratorID="' . $extra['iteratorID'] . '" ';
  136.             $attr_iterator = ' iterator="Continue" ';
  137.  
  138.             $last = $this->_quickbooks_get_current_run($user, $action);
  139.         }
  140.        
  141.         $dr = '';
  142.         if(is_array($extra) && isset($extra['new']) && $extra['new'] && isset($extra['timestamp_from']) && !empty($extra['timestamp_from'])){
  143.             //$dr = '<FromModifiedDate>' . $last . '</FromModifiedDate>';
  144.             $timestamp_from = $extra['timestamp_from'];
  145.             //$timestamp_from = '2020-12-28T09:04:30';
  146.             $dr = '<FromModifiedDate>' . $timestamp_from . '</FromModifiedDate>';          
  147.         }
  148.  
  149.         // Build the request
  150.         $xml = '<?xml version="1.0" encoding="utf-8"?>
  151.            <?qbxml version="' . $version . '"?>
  152.            <QBXML>
  153.                <QBXMLMsgsRq onError="'.$this->getonError().'">
  154.                    <InvoiceQueryRq ' . $attr_iterator . ' ' . $attr_iteratorID . ' requestID="' . $requestID . '">
  155.                        <MaxReturned>' . QB_QUICKBOOKS_MAX_RETURNED . '</MaxReturned>
  156.                        <ModifiedDateRangeFilter>
  157.                            <FromModifiedDate>' . $timestamp_from . '</FromModifiedDate>
  158.                        </ModifiedDateRangeFilter>
  159.                        <IncludeLineItems>1</IncludeLineItems>
  160.                        <IncludeLinkedTxns>1</IncludeLinkedTxns>
  161.                        <OwnerID>0</OwnerID>
  162.                    </InvoiceQueryRq>
  163.                </QBXMLMsgsRq>
  164.            </QBXML>';
  165.        
  166.         return $xml;
  167.     }
  168.  
  169.  
  170.     /**
  171.      * @author M.E.
  172.      */
  173.     public function Invoice_Import_Response( $requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $xml, $idents ) {
  174.  
  175.         if(!$this->is_qwc_connected()){
  176.             return false;
  177.         }
  178.        
  179.         $is_only_new = false;
  180.         if(is_array($extra) && isset($extra['new']) && $extra['new']){
  181.             $is_only_new = true;
  182.         }
  183.        
  184.         //$this->add_test_log($xml);
  185.         //$this->add_test_log(print_r($idents,true));
  186.         global $wpdb;
  187.         if (!empty($idents['iteratorRemainingCount'])){
  188.             $Queue = QuickBooks_WebConnector_Queue_Singleton::getInstance();
  189.             if($is_only_new){
  190.                 $Queue->enqueue('New_'.QUICKBOOKS_IMPORT_INVOICE, null, QB_PRIORITY_REFRESH_DATA_IMPORT, array( 'iteratorID' => $idents['iteratorID'], 'new'=>$extra['new'], 'timestamp_from'=>$extra['timestamp_from'] ),$this->get_qbun());
  191.             }else{
  192.                 $Queue->enqueue(QUICKBOOKS_IMPORT_INVOICE, null, QB_PRIORITY_REFRESH_DATA_IMPORT, array( 'iteratorID' => $idents['iteratorID'] ),$this->get_qbun());
  193.             }          
  194.         }
  195.        
  196.         $iteratorID = (isset($idents['iteratorID']) && !empty($idents['iteratorID']))?$idents['iteratorID']:'';
  197.  
  198.         $errnum = 0;
  199.         $errmsg = '';
  200.         $Parser = new QuickBooks_XML_Parser($xml);
  201.         $tot_imported = 0;
  202.         if ($Doc = $Parser->parse($errnum, $errmsg)){
  203.  
  204.             $Root = $Doc->getRoot();
  205.             $List = $Root->getChildAt('QBXML/QBXMLMsgsRs/InvoiceQueryRs');
  206.             /*
  207.             $table = $wpdb->prefix.'mw_wc_qbo_desk_qbd_customers';
  208.  
  209.             /*Delete Old Data*
  210.             if(!$is_only_new && $this->get_session_val('import_customer_iteratorID','',true)!=$iteratorID){
  211.                 $wpdb->query("DELETE FROM {$table} WHERE `id` > 0");
  212.                 $wpdb->query("TRUNCATE TABLE {$table}");
  213.             }
  214.             */
  215.  
  216.             /**
  217.              * @author M.E.
  218.              */
  219.             if ( class_exists( 'Child_MWQB' ) ) {
  220.                 $Child_MWQB = new Child_MWQB();
  221.  
  222.                 $Child_MWQB->new_invoice_response( $List );
  223.             }
  224.             /* End @author M.E. */
  225.            
  226.             //
  227.             $this->set_session_val('import_invoice_iteratorID',$iteratorID);
  228.  
  229.         }
  230.        
  231.         if($tot_imported>0){
  232.             $log_title = "Recognized Invoice Successfully";
  233.             $details = "Total Invoices Recognized:{$tot_imported}";
  234.             $status = 1;
  235.            
  236.             //Clear Invalid Mappings
  237.             /*
  238.             if (!$is_only_new && empty($idents['iteratorRemainingCount'])){
  239.                 $this->clear_customer_invalid_mappings();
  240.             }
  241.             */          
  242.         }else{
  243.             $log_title = "Recognized Invoice Error";
  244.             $errmsg = (!empty($errmsg))?$errmsg:"No Invoice Found";
  245.             $details = ($errnum)?"Error Number:{$errnum}\n"."Error:{$errmsg}":"Error:{$errmsg}";
  246.             $status = 0;
  247.         }
  248.        
  249.         if(!$is_only_new || ($is_only_new && $status)){
  250.             $this->save_log(array('log_type'=>'Invoice','log_title'=>$log_title,'details'=>$details,'status'=>$status),true);
  251.         }
  252.        
  253.         return true;
  254.     }
  255.  
  256. //Within that new_invoice_response(), it looks for a tracking number and if so adds it to the woocommerce shipping tracking plugin.
  257.  
  258.     /**
  259.      * Invoice Response
  260.      */
  261.     public function new_invoice_response( $List ) {
  262.         //$listID = $list->getChildDataAt('InvoiceRet ListID');
  263.         foreach ( $List->children() as $invoice ) {
  264.             $txnDate = $invoice->getChildDataAt( 'InvoiceRet TxnDate');
  265.             $invoice_number = $invoice->getChildDataAt( 'InvoiceRet RefNumber' );
  266.             //$customerListID = $invoice->getChildDataAt( 'InvoiceRet CustomerRef ListID' );
  267.             $so_number = $invoice->getChildDataAt( 'InvoiceRet LinkedTxn RefNumber' );
  268.             //$web_order_id = $invoice->getChildDataAt( 'InvoiceRet Memo' );
  269.            
  270.             if ( empty( $so_number ) ) {
  271.                 continue;
  272.             }
  273.  
  274.             $ship_date = $invoice->getChildDataAt( 'InvoiceRet ShipDate' );
  275.             $carrier = '';
  276.             foreach ( $invoice->children() as $extras ) {  
  277.                 if ( 'DataExtRet' === $extras->name() ) {
  278.                     if ( 'Carrier' === $extras->getChildDataAt( 'DataExtRet DataExtName' ) ) {
  279.                         $carrier = $extras->getChildDataAt( 'DataExtRet DataExtValue' );
  280.                         break;
  281.                     }
  282.                 }
  283.             }
  284.             $method = $invoice->getChildDataAt( 'InvoiceRet ShipMethodRef FullName' );
  285.             $tracking_number = '';
  286.             foreach ( $invoice->children() as $extras ) {  
  287.                 if ( 'DataExtRet' === $extras->name() ) {
  288.                     if ( 'Tracking Number' === $extras->getChildDataAt( 'DataExtRet DataExtName' ) ) {
  289.                         $tracking_number = $extras->getChildDataAt( 'DataExtRet DataExtValue' );
  290.                         break;
  291.                     }
  292.                 }
  293.             }
  294.  
  295.             // Invoice Tracking
  296.             if ( ! empty( $tracking_number ) ) {
  297.  
  298.                 if ( ! $this->get_tracked_invoice( $invoice_number ) ) {
  299.                     $invoice_date = $txnDate;
  300.                     $this->add_invoice_tracking( $invoice_number, $invoice_date, sanitize_title( $carrier ), $tracking_number );
  301.                 }
  302.             }
  303.  
  304.             $orders = wc_get_orders( array(
  305.                 'limit' => 1,
  306.                 'meta_key' => '_so_number',
  307.                 'meta_value' => $so_number
  308.             ));
  309.  
  310.             if ( $orders ) {
  311.  
  312.                 foreach ( $orders as $order ) {
  313.  
  314.                     if ( $order->get_meta( '_invoice_number' ) !== $invoice_number ) {
  315.                         $order->update_meta_data( '_invoice_number', $invoice_number );
  316.                         $order->add_order_note( __( 'Invoice Order #: ' . $invoice_number, 'woocommerce' ) );
  317.                         $order->set_status( 'completed' );
  318.                     }
  319.  
  320.                     if ( empty( $order->get_meta( '_tracking_number' ) ) ) {
  321.                         if ( ! empty( $ship_date ) && ! empty( $carrier ) && ! empty( $method ) && ! empty( $tracking_number ) ) {
  322.                             $date = explode( '-', $ship_date );
  323.                             $timestamp = mktime( 0, 0, 0, $date[1], $date[2], $date[0] );
  324.                             $ship_date = date( 'Y-F-d', $timestamp );
  325.                             $meta_ship_date = date( 'n/j/Y', $timestamp );
  326.  
  327.                             $order->update_meta_data( '_ship_date', $meta_ship_date );
  328.                             $order->update_meta_data( '_ship_carrier', $carrier );
  329.                             $order->update_meta_data( '_ship_method', $method );
  330.                             $order->update_meta_data( '_tracking_number', $tracking_number );
  331.  
  332.                             $tracking = WC_Shipment_Tracking();
  333.                             $has_trackings = $tracking->actions->get_tracking_items( $order->get_id() );
  334.                            
  335.                             if ( ! empty( $has_trackings ) ) {
  336.                                 foreach ( $has_trackings as $has_tracking ) {
  337.                                     if ( $has_tracking['tracking_number'] != $tracking_number ) {
  338.                                         $tracking->actions->add_tracking_item( $order->get_id(), array(
  339.                                             'tracking_provider' => sanitize_title( $carrier ),
  340.                                             'tracking_number' => $tracking_number,
  341.                                             'date-shipped' => $ship_date
  342.                                         ));
  343.                                         $order->add_order_note( __( 'Added ' . $carrier . ' Tracking #: ' . $tracking_number, 'woocommerce' ) );
  344.                                     }
  345.                                 }
  346.                             } else {
  347.                                 $tracking->actions->add_tracking_item( $order->get_id(), array(
  348.                                     'tracking_provider' => sanitize_title( $carrier ),
  349.                                     'tracking_number' => $tracking_number,
  350.                                     'date-shipped' => $ship_date
  351.                                 ));
  352.                                 $order->add_order_note( __( 'Added ' . $carrier . ' Tracking #: ' . $tracking_number, 'woocommerce' ) );
  353.                             }
  354.                         }
  355.                     }
  356.  
  357.                     $order->save();
  358.                 }
  359.             }
  360.         }
  361.     }
  362.  
  363. // That was about it for the tracking code used to achieve this. Like I said, I'm re-working my client's website and am hoping to remove some of these after-the-fact customizations.
  364.  
  365. Thanks,
  366. Matt Ediger
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement