/**
  * Process partial refunds; prepare data and send Returned request to TaxCloud
  * 
  * @since 4.4
  * @param (WC_WooTax_Refund) $refund refund order object
  * @param (bool) $cron is this method being called from a WooTax cronjob?
  */
 public static function process_refund($refund, $cron = false)
 {
     $refund_items = array();
     // Get order object (use original order ID, not refund order ID)
     $order_id = $refund->post->post_parent;
     $order = WT_Orders::get_order($order_id);
     // Holds the key of the first found origin address for the order
     $first_found = WT_Orders::get_meta($order_id, 'first_found');
     $first_found = empty($first_found) ? false : $first_found;
     // Create array mapping product IDs to locations
     // This needs to be done since the refund order and original order will not have same item IDs
     $mapping_array = array();
     $id_array = array();
     $identifiers = WT_Orders::get_meta($order_id, 'identifiers');
     foreach ($order->order->get_items() as $item_id => $item) {
         $product_id = !empty($item['variation_id']) ? $item['variation_id'] : $item['product_id'];
         $mapping_array[$product_id] = $order->get_item_meta($item_id, '_wootax_location_id');
         if (isset($identifiers[$product_id])) {
             $identifier = $identifiers[$product_id];
         } else {
             $identifier = $item_id;
         }
         $id_array[$product_id] = $identifier;
     }
     foreach ($order->order->get_fees() as $fee_id => $item) {
         $fee_key = sanitize_title($item['name']);
         if (isset($identifiers[$fee_key])) {
             $identifier = $fee_key;
         } else {
             $identifier = $fee_id;
         }
         $id_array[$fee_key] = $identifier;
     }
     $id_array[WT_SHIPPING_ITEM] = isset($identifiers[WT_SHIPPING_ITEM]) ? $identifiers[WT_SHIPPING_ITEM] : WT_SHIPPING_ITEM;
     if (version_compare(WOOCOMMERCE_VERSION, '2.2', '>=') && !isset($identifiers[WT_SHIPPING_ITEM])) {
         foreach ($order->order->get_shipping_methods() as $method_id => $method) {
             $id_array[WT_SHIPPING_ITEM] = $method_id;
         }
     }
     // Add items
     foreach ($refund->get_items() as $item_id => $item) {
         $product = $refund->get_product_from_item($item);
         if ($item['qty'] == 0) {
             continue;
         }
         $product_id = !empty($item['variation_id']) ? $item['variation_id'] : $item['product_id'];
         $tic = get_post_meta($item['product_id'], 'wootax_tic', true);
         // Get location key for item
         $location_key = $mapping_array[$product_id];
         // Get real item ID
         // When a Lookup has not been sent from the backend yet, this will be the item key sent during checkout
         $product_id = $id_array[$product_id];
         // Set first found if needed
         if ($first_found === false) {
             $first_found = $location_key;
         }
         if (!isset($refund_items[$location_key])) {
             $refund_items[$location_key] = array();
         }
         $new_item = array('Index' => '', 'ItemID' => $product_id, 'TIC' => '', 'Qty' => $item['qty'], 'Price' => $item['line_total'] * -1 / $item['qty']);
         if ($tic !== false && !empty($tic)) {
             $new_item['TIC'] = $tic;
         }
         $refund_items[$location_key][] = $new_item;
     }
     // Add fees
     foreach ($refund->get_fees() as $fee_id => $fee) {
         if ($fee['line_total'] == 0) {
             continue;
         }
         // Get item ID
         $key = sanitize_title($fee['name']);
         $real_id = $id_array[$key];
         $refund_items[$first_found][] = array('Index' => '', 'ItemID' => $real_id, 'TIC' => WT_FEE_TIC, 'Qty' => 1, 'Price' => $fee['line_total'] * -1);
     }
     // Add shipping costs
     // Shipping costs are always associated with first found location
     $shipping_cost = $refund->get_total_shipping();
     if ($shipping_cost != 0) {
         $item_id = $id_array[WT_SHIPPING_ITEM];
         $refund_items[$first_found][] = array('Index' => '', 'ItemID' => $item_id, 'TIC' => WT_SHIPPING_TIC, 'Qty' => 1, 'Price' => $shipping_cost * -1);
     }
     // Process refund
     $res = WT_Orders::refund_order($order_id, $cron, $refund_items);
     if ($res !== true && !$cron) {
         // Delete refund
         wp_delete_post($refund->post->ID);
         // Throw exception so refund is halted
         throw new Exception('Refund failed: ' . $res);
     } else {
         if ($res !== true && $cron) {
             return $res;
         } else {
             if ($cron) {
                 return true;
             }
         }
     }
 }