static function checkAllProductsWithStatus($status)
 {
     $problems = array();
     // get all prepared products
     $lm = new WPLA_ListingsModel();
     $listings = $lm->findAllListingsByColumn($status, 'status');
     // echo "<pre>";print_r($listings);echo"</pre>";#die();
     // get all post_ids
     global $wpdb;
     $post_ids = $wpdb->get_col("SELECT ID FROM {$wpdb->posts} WHERE post_type = 'product' OR post_type = 'product_variation' ");
     foreach ($listings as $listing_id => $item) {
         // check if product exists
         if (!in_array($item->post_id, $post_ids)) {
             $problems[] = array('msg' => 'The product "' . $item->listing_title . '" does not exist in WooCommerce and will not be included in the next feed submission.', 'post_id' => $item->post_id);
         }
         // check SKU - all products
         if ($item->sku == '') {
             $problems[] = array('msg' => 'The product "' . $item->listing_title . '" has no SKU and will not be included in the next feed submission.', 'post_id' => $item->post_id);
         }
         if (strlen($item->sku) > 40) {
             $problems[] = array('msg' => 'The SKU <b>' . $item->sku . '</b> for product "' . $item->listing_title . '" is longer than 40 characters. Amazon requires SKUs to have 40 characters or less.', 'post_id' => $item->post_id);
         }
         // run checks for variable or simple product
         if ($item->product_type == 'variable') {
             // $problems = self::checkVariableProduct( $item, $problems );
         } elseif ($item->product_type == 'variation') {
             // $problems = self::checkSimpleProduct( $item, $problems );
         } else {
             $problems = self::checkSimpleProduct($item, $problems);
         }
     }
     // foreach listing
     return $problems;
 }
 function processFbaSubmissionOrderItem($order_item, $_order)
 {
     // Flat File FBA Shipment Injection Fulfillment Feed
     $feed_type = '_POST_FLAT_FILE_FULFILLMENT_ORDER_REQUEST_DATA_';
     // use account from first order item (for now)
     $lm = new WPLA_ListingsModel();
     $post_id = $order_item['variation_id'] ? $order_item['variation_id'] : $order_item['product_id'];
     $listing = $lm->getItemByPostID($post_id);
     $account_id = $listing->account_id;
     $account = new WPLA_AmazonAccount($account_id);
     WPLA()->logger->info('updateFbaSubmissionFeed() ' . $feed_type . ' - post id: ' . $post_id . ' - account id: ' . $account->id);
     // create pending feed if it doesn't exist
     if (!($this->id = self::getPendingFeedId($feed_type, null, $account->id))) {
         # build feed data
         WPLA()->logger->info('building FBA submission feed...');
         $csv = WPLA_FeedDataBuilder::buildFbaSubmissionFeedData($post_id, $_order, $order_item, $listing, $account->id, true);
         if (!$csv) {
             WPLA()->logger->warn('no feed data - not creating feed');
             return;
         }
         // add new feed
         $this->FeedType = $feed_type;
         $this->status = 'pending';
         $this->account_id = $account->id;
         $this->date_created = date('Y-m-d H:i:s');
         $this->data = $csv;
         $this->add();
         WPLA()->logger->info('added NEW feed - id ' . $this->id);
     } else {
         WPLA()->logger->info('found existing feed ' . $this->id);
         $existing_feed = new WPLA_AmazonFeed($this->id);
         # append feed data
         WPLA()->logger->info('updating FBA submission feed...');
         $csv = WPLA_FeedDataBuilder::buildFbaSubmissionFeedData($post_id, $_order, $order_item, $listing, $account->id, false);
         $this->data = $existing_feed->data . $csv;
     }
     // update feed
     $this->line_count = sizeof($csv);
     $this->FeedProcessingStatus = 'pending';
     $this->date_created = date('Y-m-d H:i:s');
     $this->update();
     WPLA()->logger->info('feed was built and updated - ' . $this->id);
 }
 public function showNotifications()
 {
     // get listing status summary
     $summary = WPLA_ListingsModel::getStatusSummary();
     // check for prepared items and display info
     if (isset($summary->prepared)) {
         // $next_schedule = $this->print_schedule_info( 'wpla_update_schedule' );
         // $msg  = '<p>';
         // $msg .= sprintf( __('%d %s product(s) will be submitted to Amazon %s.','wpla'), $summary->prepared, 'prepared', $next_schedule );
         // $msg .= '&nbsp;&nbsp;';
         // $msg .= '<a href="admin.php?page=wpla&listing_status=prepared" id="" class="button button-small wpl_job_button">' . __('Show products','wpla') . '</a>';
         // $msg .= '&nbsp;&nbsp;';
         // $msg .= '<a href="admin.php?page=wpla-feeds&action=submit_pending_feeds_to_amazon" id="" class="button button-small wpl_job_button">' . __('Submit pending feeds','wpla') . '</a>';
         // $msg .= '</p>';
         // $this->showMessage( $msg );
         // check prepared products for problems
         $problems = WPLA_FeedValidator::checkPreparedProducts();
         if ($problems) {
             $this->showMessage($problems, 1);
         }
     }
     // check for changed, matched and prepared items - and show message
     $is_feed_page = isset($_GET['page']) && $_GET['page'] == 'wpla-feeds';
     if (isset($summary->changed) || isset($summary->prepared) || isset($summary->matched)) {
         $next_schedule = $this->print_schedule_info('wpla_update_schedule');
         // build nice combined message
         $summary_msg = '';
         $summary_array = array();
         foreach (array('changed', 'prepared', 'matched') as $status) {
             if (!isset($summary->{$status})) {
                 continue;
             }
             $link_url = 'admin.php?page=wpla&listing_status=' . $status;
             $link_title = $summary->{$status} . ' ' . $status;
             $summary_array[] = '<a href="' . $link_url . '">' . $link_title . '</a>';
         }
         $summary_msg = join(' and ', $summary_array);
         $msg = '<p>';
         $msg .= sprintf(__('%s product(s) will be submitted to Amazon %s.', 'wpla'), $summary_msg, $next_schedule);
         $msg .= '&nbsp;&nbsp;';
         if ($is_feed_page) {
             $msg .= '<a href="admin.php?page=wpla-feeds&action=submit_pending_feeds_to_amazon" id="" class="button button-small wpl_job_button">' . __('Submit pending feeds', 'wpla') . '</a>';
         } else {
             $msg .= '<a href="admin.php?page=wpla-feeds" id="" class="button button-small wpl_job_button">' . __('Visit feeds', 'wpla') . '</a>';
         }
         $msg .= '</p>';
         $this->showMessage($msg);
     }
 }
 static function updateAmazonPrice($item, $target_price, $verbose)
 {
     // make sure we don't go below min_price
     if ($item->min_price) {
         $target_price = max($target_price, $item->min_price);
     }
     // make sure we don't go above max_price (prevent feed error)
     if ($item->max_price) {
         $target_price = min($target_price, $item->max_price);
     }
     // skip if there is no change in price
     if ($target_price == $item->price) {
         return false;
     }
     // update amazon price in WooCommerce
     update_post_meta($item->post_id, '_amazon_price', $target_price);
     // update price in listings table
     $data = array('price' => $target_price, 'pnq_status' => 1);
     WPLA_ListingsModel::updateWhere(array('id' => $item->id), $data);
     // show message
     if ($verbose) {
         wpla_show_message($item->sku . ': price was changed from ' . $item->price . ' to <b>' . $target_price . '</b>');
     }
     // price was changed
     return true;
 }
 public static function updateMinMaxPrices($item_ids)
 {
     // echo "<pre>";print_r($item_ids);echo"</pre>";
     // TODO: sanitize values
     $min_base_price = trim($_REQUEST['min_base_price']);
     $min_price_percentage = trim($_REQUEST['min_price_percentage']);
     $min_price_amount = trim($_REQUEST['min_price_amount']);
     $max_base_price = trim($_REQUEST['max_base_price']);
     $max_price_percentage = trim($_REQUEST['max_price_percentage']);
     $max_price_amount = trim($_REQUEST['max_price_amount']);
     $min_price_amount = str_replace(',', '.', $min_price_amount);
     // convert decimal comma
     $max_price_amount = str_replace(',', '.', $max_price_amount);
     // remember last used options
     $options = array('min_base_price' => $min_base_price, 'max_base_price' => $max_base_price, 'min_price_amount' => $min_price_amount, 'max_price_amount' => $max_price_amount, 'min_price_percentage' => $min_price_percentage, 'max_price_percentage' => $max_price_percentage);
     update_option('wpla_price_wizard_options', $options);
     $lm = new WPLA_ListingsModel();
     foreach ($item_ids as $listing_id) {
         // load listing item
         $item = $lm->getItem($listing_id, OBJECT);
         if (!$item) {
             continue;
         }
         if ($item->product_type == 'variable') {
             continue;
         }
         $post_id = $item->post_id;
         // get base price (min)
         $base_price = 0;
         if ($min_base_price == 'price') {
             $base_price = WPLA_ProductWrapper::getPrice($post_id);
         }
         if ($min_base_price == 'sale_price') {
             $base_price = WPLA_ProductWrapper::getSalePrice($post_id);
         }
         if ($min_base_price == 'msrp') {
             $base_price = get_post_meta($post_id, '_msrp', true) ? get_post_meta($post_id, '_msrp', true) : get_post_meta($post_id, '_msrp_price', true);
         }
         // calculate new min price
         if ($min_price_percentage) {
             $base_price = $base_price + $base_price * floatval($min_price_percentage) / 100;
         }
         if ($min_price_amount) {
             $base_price = $base_price + floatval($min_price_amount);
         }
         if ($min_base_price == 'no_change') {
             $base_price = $item->min_price;
         }
         $new_min_price = round($base_price, 2);
         // get base price (max)
         $base_price = 0;
         if ($max_base_price == 'price') {
             $base_price = WPLA_ProductWrapper::getPrice($post_id);
         }
         if ($max_base_price == 'sale_price') {
             $base_price = WPLA_ProductWrapper::getSalePrice($post_id);
         }
         if ($max_base_price == 'msrp') {
             $base_price = get_post_meta($post_id, '_msrp', true) ? get_post_meta($post_id, '_msrp', true) : get_post_meta($post_id, '_msrp_price', true);
         }
         // calculate new max price
         if ($max_price_percentage) {
             $base_price = $base_price + $base_price * floatval($max_price_percentage) / 100;
         }
         if ($max_price_amount) {
             $base_price = $base_price + floatval($max_price_amount);
         }
         if ($max_base_price == 'no_change') {
             $base_price = $item->max_price;
         }
         $new_max_price = round($base_price, 2);
         // update listing table
         $data = array('min_price' => $new_min_price, 'max_price' => $new_max_price, 'pnq_status' => 1);
         $lm->updateWhere(array('id' => $listing_id), $data);
         // update product
         update_post_meta($item->post_id, '_amazon_minimum_price', $new_min_price);
         update_post_meta($item->post_id, '_amazon_maximum_price', $new_max_price);
     }
     // foreach item
 }
    }
    if (!isset($row['seller-sku'])) {
        $product_sku = '<span style="color:darkred">Invalid Report - no SKU column found</span>';
    }
    ?>
        <tr>
            <th scope="row" class="check-column"><input type="checkbox" name="row[]" value="<?php 
    echo $row['seller-sku'];
    ?>
"></th>
            <!-- <td><?php 
    echo utf8_encode($row['item-name']);
    ?>
</td> -->
            <td><?php 
    echo WPLA_ListingsModel::convertToUTF8($row['item-name']);
    ?>
</td>
            <td><?php 
    echo $product_sku;
    ?>
</td>
            <td><?php 
    echo $listing_asin;
    ?>
</td>
            <td style="text-align:right;">
                <?php 
    if ($row['quantity']) {
        echo $row['quantity'];
    } elseif (isset($row['fulfillment-channel']) && $row['fulfillment-channel'] != 'DEFAULT') {
 public function process_product_meta_variable($post_id)
 {
     WPLA()->logger->info('process_product_meta_variable() - ' . $post_id);
     if (!isset($_POST['variable_sku'])) {
         return;
     }
     $variable_post_id = $_POST['variable_post_id'];
     $variable_amazon_product_id = $_POST['variable_amazon_product_id'];
     $variable_amazon_id_type = $_POST['variable_amazon_id_type'];
     $variable_amazon_asin = $_POST['variable_amazon_asin'];
     $variable_sku = $_POST['variable_sku'];
     $variable_amazon_price = isset($_POST['variable_amazon_price']) ? $_POST['variable_amazon_price'] : '';
     $variable_amazon_minimum_price = isset($_POST['variable_amazon_minimum_price']) ? $_POST['variable_amazon_minimum_price'] : '';
     $variable_amazon_maximum_price = isset($_POST['variable_amazon_maximum_price']) ? $_POST['variable_amazon_maximum_price'] : '';
     $variable_amazon_condition_type = isset($_POST['variable_amazon_condition_type']) ? $_POST['variable_amazon_condition_type'] : '';
     $variable_amazon_condition_note = isset($_POST['variable_amazon_condition_note']) ? $_POST['variable_amazon_condition_note'] : '';
     $variable_amazon_is_disabled = isset($_POST['variable_amazon_is_disabled']) ? $_POST['variable_amazon_is_disabled'] : '';
     // convert decimal comma for all price fields
     $variable_amazon_price = str_replace(',', '.', $variable_amazon_price);
     $variable_amazon_minimum_price = str_replace(',', '.', $variable_amazon_minimum_price);
     $variable_amazon_maximum_price = str_replace(',', '.', $variable_amazon_maximum_price);
     $lm = new WPLA_ListingsModel();
     $all_variations_with_SKU = array();
     $all_variations_with_ASIN = array();
     $max_loop = max(array_keys($_POST['variable_post_id']));
     for ($i = 0; $i <= $max_loop; $i++) {
         if (!isset($variable_post_id[$i])) {
             continue;
         }
         $variation_id = (int) $variable_post_id[$i];
         // Update post meta
         update_post_meta($variation_id, '_amazon_product_id', trim($variable_amazon_product_id[$i]));
         update_post_meta($variation_id, '_amazon_id_type', $variable_amazon_id_type[$i]);
         update_post_meta($variation_id, '_wpla_asin', trim($variable_amazon_asin[$i]));
         update_post_meta($variation_id, '_amazon_price', isset($variable_amazon_price[$i]) ? trim($variable_amazon_price[$i]) : '');
         update_post_meta($variation_id, '_amazon_minimum_price', isset($variable_amazon_minimum_price[$i]) ? trim($variable_amazon_minimum_price[$i]) : '');
         update_post_meta($variation_id, '_amazon_maximum_price', isset($variable_amazon_maximum_price[$i]) ? trim($variable_amazon_maximum_price[$i]) : '');
         update_post_meta($variation_id, '_amazon_condition_type', isset($variable_amazon_condition_type[$i]) ? trim($variable_amazon_condition_type[$i]) : '');
         update_post_meta($variation_id, '_amazon_condition_note', isset($variable_amazon_condition_note[$i]) ? trim($variable_amazon_condition_note[$i]) : '');
         update_post_meta($variation_id, '_amazon_is_disabled', isset($variable_amazon_is_disabled[$i]) ? $variable_amazon_is_disabled[$i] : '');
         // if ( $variable_amazon_product_id[$i] !== 'parent' )
         //     update_post_meta( $variation_id, '_amazon_product_id', $variable_amazon_product_id[$i] );
         // else
         //     delete_post_meta( $variation_id, '_amazon_product_id' );
         // update min/max prices in listings table
         if (isset($_POST['variable_amazon_minimum_price'])) {
             $min_price = isset($variable_amazon_minimum_price[$i]) ? $variable_amazon_minimum_price[$i] : '';
             $max_price = isset($variable_amazon_maximum_price[$i]) ? $variable_amazon_maximum_price[$i] : '';
             $data = array();
             if ($min_price || $max_price) {
                 if ($listing = $lm->getItemByPostID($variation_id)) {
                     if ($min_price != $listing->min_price) {
                         $data['min_price'] = $min_price;
                         $data['pnq_status'] = 1;
                         // mark as changed
                     }
                     if ($max_price != $listing->max_price) {
                         $data['max_price'] = $max_price;
                         $data['pnq_status'] = 1;
                         // mark as changed
                     }
                     // update listing
                     if (!empty($data)) {
                         $lm->updateWhere(array('id' => $listing->id), $data);
                     }
                 }
             }
         }
         // collect (matched) variations with ASIN
         if ($variable_amazon_asin[$i]) {
             $all_variations_with_ASIN[$variation_id] = $variable_amazon_asin[$i];
         }
         // collect all variations with SKU
         if ($variable_sku[$i]) {
             $all_variations_with_SKU[$variation_id] = $variable_sku[$i];
         }
     }
     // each variation
     WPLA()->logger->info('Variations with ASIN: ' . print_r($all_variations_with_ASIN, 1));
     WPLA()->logger->info('Variations with SKU : ' . print_r($all_variations_with_SKU, 1));
     // process matched variations
     // check all variations with ASIN and add missing ones to listings table
     if (!empty($all_variations_with_ASIN)) {
         $lm = new WPLA_ListingsModel();
         $default_account_id = get_option('wpla_default_account_id', 1);
         if (!$default_account_id) {
             return;
         }
         // ***
         foreach ($all_variations_with_ASIN as $variation_id => $asin) {
             // check if this ASIN / ID already exist - skip if it does
             WPLA()->logger->info("searching for existing listing for #{$variation_id} / {$asin}");
             if ($lm->getItemByASIN($asin, false)) {
                 continue;
             }
             if ($lm->getItemByPostID($variation_id)) {
                 continue;
             }
             WPLA()->logger->info("no listing found for variation #{$variation_id} / {$asin}");
             // skip hidden variations
             if (get_post_meta($variation_id, '_amazon_is_disabled', true) == 'on') {
                 continue;
             }
             // insert matched listing
             $success = $lm->insertMatchedProduct($variation_id, $asin, $default_account_id);
             $error_msg = isset($lm->lastError) ? $lm->lastError : '';
             if ($success) {
                 // TODO: use persistent admin message
                 WPLA()->logger->info("Matched variation #{$variation_id} / {$asin} - {$error_msg}");
             } else {
                 echo "Failed to match variation #{$variation_id} - please report this to support: {$error_msg}";
                 WPLA()->logger->error("Failed to match variation #{$variation_id} / {$asin} - {$error_msg}");
             }
         }
         // each matched variation
     }
     // if $all_variations_with_ASIN
     // add missing variations
     // if the parent product has a listing item, then check for and add missing variation listings
     $lm = new WPLA_ListingsModel();
     $parent_listing = $lm->getItemByPostID($post_id);
     if ($parent_listing) {
         // get account from parent listing
         $account = WPLA_AmazonAccount::getAccount($parent_listing->account_id);
         if (!$account) {
             return;
         }
         foreach ($all_variations_with_SKU as $variation_id => $sku) {
             // check if this SKU / ID already exist - skip if it does
             if ($lm->getItemBySKU($sku, false)) {
                 continue;
             }
             if ($lm->getItemByPostID($variation_id)) {
                 continue;
             }
             WPLA()->logger->info("no listing found for missing variation #{$variation_id} / {$sku}");
             // check if this variation has a UPC/EAN set - skip if empty (unless brand registry is enabled)
             $_amazon_product_id = get_post_meta($variation_id, '_amazon_product_id', true);
             if (!$_amazon_product_id && !$account->is_reg_brand) {
                 continue;
             }
             // skip hidden variations
             if (get_post_meta($variation_id, '_amazon_is_disabled', true) == 'on') {
                 continue;
             }
             // insert variation listing
             $success = $lm->insertMissingVariation($variation_id, $sku, $parent_listing);
             $error_msg = isset($lm->lastError) ? $lm->lastError : '';
             if ($success) {
                 // TODO: use persistent admin message
                 WPLA()->logger->info("Matched missing variation #{$variation_id} / {$sku} - {$error_msg}");
             } else {
                 echo "Failed to match missing variation #{$variation_id} - please report this to support: {$error_msg}";
                 WPLA()->logger->error("Failed to match missing variation #{$variation_id} / {$sku} - {$error_msg}");
             }
         }
         // each variation
     }
     // if parent listing exists
 }
 public function processReportData($id)
 {
     $report = new WPLA_AmazonReport($id);
     // $data = $report->data;
     $rows = $report->get_data_rows();
     $lm = new WPLA_ListingsModel();
     foreach ($rows as $row) {
         $lm->updateItemFromReportCSV($row, $report->account_id);
     }
     $msg = 'Imported: ' . $lm->imported_count . '<br>';
     $msg .= 'Updated: ' . $lm->updated_count . '<br>';
     $this->showMessage($msg);
 }
 public function jobs_load_tasks()
 {
     // quit if no job name provided
     if (!isset($_REQUEST['job'])) {
         return false;
     }
     $jobname = $_REQUEST['job'];
     // check if an array of listing IDs was provided
     $lm = new WPLA_ListingsModel();
     $listing_ids = isset($_REQUEST['item_ids']) && is_array($_REQUEST['item_ids']) ? $_REQUEST['item_ids'] : false;
     if ($listing_ids) {
         $items = $lm->getItemsByIdArray($listing_ids);
     }
     // register shutdown handler
     global $wpla_shutdown_handler_enabled;
     $wpla_shutdown_handler_enabled = true;
     register_shutdown_function(array($this, 'shutdown_handler'));
     // handle job name
     switch ($jobname) {
         case 'updateProductsWithoutASIN':
             // get prepared items
             $sm = new WPLA_ListingsModel();
             $items = $sm->getAllOnlineWithoutASIN();
             // create job from items and send response
             $response = $this->_create_bulk_listing_job('updateProduct', $items, $jobname);
             $this->returnJSON($response);
             exit;
         case 'createAllImportedProducts':
             // get prepared items
             $sm = new WPLA_ListingsModel();
             $items = $sm->getAllImported();
             // DEV: limit to 10 tasks at a time ***
             // $items = array_slice($items, 0, 10, true);
             // create job from items and send response
             $response = $this->_create_bulk_listing_job('createProduct', $items, $jobname);
             $this->returnJSON($response);
             exit;
         case 'processAmazonReport':
             // get report
             $id = $_REQUEST['item_id'];
             $report = new WPLA_AmazonReport($id);
             $rows = $report->get_data_rows();
             $rows_count = sizeof($rows);
             $page_size = 500;
             $number_of_pages = intval($rows_count / $page_size) + 1;
             $items = array();
             if ($number_of_pages > 0) {
                 for ($page = 0; $page < $number_of_pages; $page++) {
                     $from_row = $page * $page_size + 1;
                     $to_row = ($page + 1) * $page_size;
                     if ($to_row > $rows_count) {
                         $to_row = $rows_count;
                     }
                     $items[] = array('id' => $id, 'page' => $page, 'from_row' => $from_row, 'to_row' => $to_row, 'title' => 'Processing rows ' . $from_row . ' to ' . $to_row);
                 }
             }
             // create job from items and send response
             $response = $this->_create_bulk_listing_job('processReportPage', $items, $jobname);
             $this->returnJSON($response);
             exit;
         case 'processRowsFromAmazonReport':
             $id = $_REQUEST['report_id'];
             $skus = $_REQUEST['sku_list'];
             foreach ($skus as $sku) {
                 $items[] = array('id' => $id, 'sku' => $sku, 'title' => 'Processing SKU ' . $sku);
             }
             // create job from items and send response
             $response = $this->_create_bulk_listing_job('processSingleSkuFromReport', $items, $jobname);
             $this->returnJSON($response);
             exit;
         case 'fetchProductDescription':
             // create job from items and send response
             $response = $this->_create_bulk_listing_job('fetchFullProductDescription', $items, $jobname);
             $this->returnJSON($response);
             exit;
         default:
             // echo "unknown job";
             // break;
     }
     // exit();
 }
 public function getListingURL($listing_id)
 {
     $lm = new WPLA_ListingsModel();
     $item = $lm->getItem($listing_id);
     // build listing URL
     $listing_url = 'http://www.amazon.com/dp/' . $item['asin'] . '/';
     if ($item['account_id']) {
         $account = new WPLA_AmazonAccount($item['account_id']);
         $market = new WPLA_AmazonMarket($account->market_id);
         $listing_url = 'http://www.' . $market->url . '/dp/' . $item['asin'] . '/';
     }
     return $listing_url;
 }
 public static function orderCanBeFulfilledViaFBA($post, $is_cron = false)
 {
     // make sure we have a wp post object
     if (is_numeric($post)) {
         $post = get_post($post);
     }
     // check if this is an order created by WP-Lister for Amazon
     $amazon_order_id = get_post_meta($post->ID, '_wpla_amazon_order_id', true);
     if ($amazon_order_id) {
         return 'Order was placed on Amazon';
     }
     // check if this order has already been submitted to FBA
     $submission_status = get_post_meta($post->ID, '_wpla_fba_submission_status', true);
     if ($submission_status == 'pending') {
         return __('This order is going to be submitted to Amazon and will be fulfilled via FBA.', 'wpla');
     }
     if ($submission_status == 'success') {
         return __('This order has been successfully submitted to Amazon and will be fulfilled via FBA.', 'wpla');
     }
     if ($submission_status == 'shipped') {
         return __('This order has been fulfilled by Amazon.', 'wpla');
     }
     if ($submission_status == 'failed') {
         // failed submissions can be submitted again - but only manually for now
         // (automatic resubmittion will require proper error handling for Error 560001: Delivery SLA is not available for destination address - and fallback to Standard shipping)
         if ($is_cron) {
             return __('There was a problem submitting this order to be fulfilled by Amazon!', 'wpla');
         }
     }
     // skip cancelled and pending orders
     if (!in_array($post->post_status, array('wc-processing', 'wc-completed', 'wc-on-hold'))) {
         return __('Order status is neither processing nor completed not on hold.', 'wpla');
     }
     // check if FBA is enabled (not really required)
     // if ( !  get_option( 'wpla_fba_enabled' ) ) return 'FBA support is disabled.';
     // get order and order items
     if (!function_exists('wc_get_order')) {
         return;
     }
     $_order = wc_get_order($post->ID);
     $order_items = $_order->get_items();
     // check if destination country matches fulfillment center
     $fba_default_fcid = get_option('wpla_fba_fulfillment_center_id', 'AMAZON_NA');
     if ('AMAZON_NA' == $fba_default_fcid) {
         $allowed_countries = array('US');
         if (!in_array($_order->shipping_country, $allowed_countries)) {
             return __('Shipping destination is not within the US.', 'wpla');
         }
     } elseif ('AMAZON_EU' == $fba_default_fcid) {
         $allowed_countries = array('AT', 'BE', 'BG', 'CY', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI', 'FR', 'GB', 'GR', 'HU', 'HR', 'IE', 'IT', 'LT', 'LU', 'LV', 'MT', 'NL', 'PL', 'PT', 'RO', 'SE', 'SI', 'SK');
         if (!in_array($_order->shipping_country, $allowed_countries)) {
             return __('Shipping destination is not within the EU.', 'wpla');
         }
     } elseif ('AMAZON_IN' == $fba_default_fcid) {
         $allowed_countries = array('IN');
         if (!in_array($_order->shipping_country, $allowed_countries)) {
             return __('Shipping destination is not within India.', 'wpla');
         }
     }
     // check if ordered items are available on FBA
     $items_available_on_fba = array();
     $count_not_available_on_fba = 0;
     $lm = new WPLA_ListingsModel();
     foreach ($order_items as $item) {
         // skip tax and shipping rows
         if ($item['type'] != 'line_item') {
             continue;
         }
         // find amazon listing
         $post_id = $item['variation_id'] ? $item['variation_id'] : $item['product_id'];
         $listing = $lm->getItemByPostID($post_id);
         if (!$listing) {
             $count_not_available_on_fba++;
             continue;
         }
         // check FBA inventory
         $fba_quantity = $listing->fba_quantity;
         if ($fba_quantity > 0) {
             $listing->purchased_qty = $item['qty'];
             $items_available_on_fba[] = $listing;
         } else {
             $count_not_available_on_fba++;
         }
     }
     // each order line item
     if (empty($items_available_on_fba)) {
         $msg = __('This order can not be fulfilled by Amazon.', 'wpla') . ' ';
         $msg .= __('The purchased item(s) are currently not available on FBA.', 'wpla');
         return $msg;
     }
     if ($count_not_available_on_fba > 0) {
         $msg = __('This order can not be fulfilled by Amazon.', 'wpla') . ' ';
         $msg .= __('Not all purchased items are currently available on FBA.', 'wpla');
         return $msg;
     }
     // this order can be filfilled via FBA - return array of items
     return $items_available_on_fba;
 }
 function update_products_on_shutdown()
 {
     // get queue
     $collected_products = get_option('wpla_updated_products_queue', array());
     if (!is_array($collected_products)) {
         $collected_products = array();
     }
     // DEBUG
     WPLA()->logger->info("CSV: update_products_on_shutdown() - collected_products: " . print_r($collected_products, 1));
     // mark each queued product as modified
     $lm = new WPLA_ListingsModel();
     foreach ($collected_products as $post_id) {
         // do_action( 'wpla_product_has_changed', $post_id );
         $lm->markItemAsModified($post_id, true);
         // set $skip_updating_feeds = true
     }
     // clear queue
     delete_option('wpla_updated_products_queue');
     // update pending feeds - after all items have been updated
     if (!empty($collected_products)) {
         WPLA_AmazonFeed::updatePendingFeeds();
     }
 }
 function processListingItem($item, $order)
 {
     global $wpdb;
     // abort if item data is invalid
     if (!isset($item->ASIN) && !isset($item->QuantityOrdered)) {
         $history_message = "Error fetching order line items - request throttled?";
         $history_details = array();
         $this->addHistory($order->AmazonOrderId, 'request_throttled', $history_message, $history_details);
         return false;
     }
     $order_id = $order->AmazonOrderId;
     $asin = $item->ASIN;
     $sku = $item->SellerSKU;
     $quantity_purchased = $item->QuantityOrdered;
     // get listing item
     $lm = new WPLA_ListingsModel();
     $listing = $lm->getItemBySKU($sku);
     // skip if this listing does not exist in WP-Lister
     if (!$listing) {
         $history_message = "Skipped unknown SKU {$sku} ({$asin})";
         $history_details = array('sku' => $sku, 'asin' => $asin);
         $this->addHistory($order_id, 'skipped_item', $history_message, $history_details);
         return true;
     }
     // handle FBA orders
     if ($order->FulfillmentChannel == 'AFN') {
         // update quantity for FBA orders
         $fba_quantity = $listing->fba_quantity - $quantity_purchased;
         $quantity_sold = $listing->quantity_sold + $quantity_purchased;
         $wpdb->update($wpdb->prefix . 'amazon_listings', array('fba_quantity' => $fba_quantity, 'quantity_sold' => $quantity_sold), array('sku' => $sku));
         // add history record
         $history_message = "FBA quantity reduced by {$quantity_purchased} for listing {$sku} ({$asin}) - FBA stock {$fba_quantity} ({$quantity_sold} sold)";
         $history_details = array('fba_quantity' => $fba_quantity, 'sku' => $sku, 'asin' => $asin, 'qty_purchased' => $quantity_purchased, 'listing_id' => $listing->id);
         $this->addHistory($order_id, 'reduce_stock', $history_message, $history_details);
     } else {
         // update quantity for non-FBA orders
         $quantity_total = $listing->quantity - $quantity_purchased;
         $quantity_sold = $listing->quantity_sold + $quantity_purchased;
         $wpdb->update($wpdb->prefix . 'amazon_listings', array('quantity' => $quantity_total, 'quantity_sold' => $quantity_sold), array('sku' => $sku));
         // add history record
         $history_message = "Quantity reduced by {$quantity_purchased} for listing {$sku} ({$asin}) - new stock: {$quantity_total} ({$quantity_sold} sold)";
         $history_details = array('newstock' => $quantity_total, 'sku' => $sku, 'asin' => $asin, 'qty_purchased' => $quantity_purchased, 'listing_id' => $listing->id);
         $this->addHistory($order_id, 'reduce_stock', $history_message, $history_details);
     }
     // mark listing as sold when last item is sold
     // if ( $quantity_total == 0 ) {
     // 	$wpdb->update( $wpdb->prefix.'amazon_listings',
     // 		array( 'status' => 'sold', 'date_finished' => $data['date_created'], ),
     // 		array( 'sku' => $sku )
     // 	);
     // 	WPLA()->logger->info( 'marked item '.$sku.' as SOLD ');
     // }
     return true;
 }
 function wpla_product_admin_notices()
 {
     global $post, $post_ID;
     if (!$post) {
         return;
     }
     if (!$post_ID) {
         return;
     }
     if (!$post->post_type == 'product') {
         return;
     }
     $errors_msg = '';
     // warn about missing details
     $this->checkForMissingData($post);
     $this->checkForInvalidData($post);
     // get listing item
     $lm = new WPLA_ListingsModel();
     $listing = $lm->getItemByPostID($post_ID);
     if (!$listing) {
         return;
     }
     // parse history
     $history = maybe_unserialize($listing->history);
     if (empty($history) && $listing->product_type != 'variable') {
         return;
     }
     // echo "<pre>";print_r($history);echo"</pre>";#die();
     // show errors and warning on online and failed items only
     if (!in_array($listing->status, array('online', 'failed'))) {
         return;
     }
     // process errors and warnings
     $tips_errors = array();
     $tips_warnings = array();
     if (is_array($history)) {
         foreach ($history['errors'] as $feed_error) {
             $tips_errors[] = WPLA_FeedValidator::formatAmazonFeedError($feed_error);
         }
         foreach ($history['warnings'] as $feed_error) {
             $tips_warnings[] = WPLA_FeedValidator::formatAmazonFeedError($feed_error);
         }
     }
     if (!empty($tips_errors)) {
         $errors_msg .= 'Amazon returned the following error(s) when this product was submitted.' . ' ';
         $errors_msg .= '(Status: ' . $listing->status . ')<br>';
         $errors_msg .= '<small style="color:darkred">' . join('<br>', $tips_errors) . '</small>';
     }
     // check variations for errors
     if ($listing->product_type == 'variable') {
         $variations_msg = $errors_msg ? '<br><br>' : '';
         $variations_msg .= '<small><a href="#" onclick="jQuery(\'#variation_error_container\').slideToggle();return false;" class="button button-small">' . 'Show errors for all variations' . '</a></small>';
         $variations_msg .= '<div id="variation_error_container" style="display:none">';
         $variations_have_errors = false;
         $child_items = $lm->getAllItemsByParentID($post_ID);
         foreach ($child_items as $child) {
             $history = maybe_unserialize($child->history);
             $tips_errors = array();
             if (is_array($history)) {
                 foreach ($history['errors'] as $feed_error) {
                     $tips_errors[] = WPLA_FeedValidator::formatAmazonFeedError($feed_error);
                 }
                 // foreach ( $history['warnings'] as $feed_error ) {
                 //     $tips_warnings[] = WPLA_FeedValidator::formatAmazonFeedError( $feed_error );
                 // }
             }
             if (!empty($tips_errors)) {
                 $variations_msg .= 'Errors for variation ' . $child->sku . ':' . '<br>';
                 $variations_msg .= '<small style="color:darkred">' . join('<br>', $tips_errors) . '</small><br><br>';
                 $variations_have_errors = true;
             }
         }
         $variations_msg .= '</div>';
         if ($variations_have_errors) {
             $errors_msg .= $variations_msg;
         }
     }
     if ($errors_msg) {
         self::showMessage($errors_msg, 1, 1);
     }
 }
 public function importASINs()
 {
     $asin_list = trim($_REQUEST['wpla_asin_list']);
     if (!$asin_list) {
         $this->showMessage('You need to enter a least one ASIN to import.', 1);
         return false;
     }
     $lm = new WPLA_ListingsModel();
     $import_account_id = $_REQUEST['wpla_import_account_id'];
     if (!$import_account_id) {
         $import_account_id = get_option('wpla_default_account_id', 1);
     }
     $asin_array = explode("\n", $asin_list);
     foreach ($asin_array as $ASIN) {
         $ASIN = trim($ASIN);
         if (!$ASIN) {
             continue;
         }
         $row = array();
         $row['asin'] = $ASIN;
         $row['seller-sku'] = $ASIN;
         $row['item-name'] = $ASIN . ' (import to fetch title from Amazon)';
         $row['open-date'] = date('Y-m-d H:i:s');
         $row['item-description'] = '';
         $row['fulfillment-channel'] = '';
         $row['quantity'] = 0;
         $row['price'] = 0;
         $row['source'] = 'foreign_import';
         $lm->updateItemFromReportCSV($row, $import_account_id);
         // $this->showMessage('Product '.$ASIN.' was prepared for import.');
     }
     if ($lm->imported_count) {
         $this->showMessage($lm->imported_count . ' new products were prepared for import.');
     }
     if ($lm->updated_count) {
         $this->showMessage($lm->updated_count . ' ASINs already exist and have been skipped.');
     }
     return $lm->imported_count + $lm->updated_count;
 }
 public function update_pricing_info_for_asins($listing_ASINs, $account_id)
 {
     $listingsModel = new WPLA_ListingsModel();
     // fetch Buy Box pricing info and process result
     $api = new WPLA_AmazonAPI($account_id);
     $result = $api->getCompetitivePricingForId($listing_ASINs);
     $listingsModel->processBuyBoxPricingResult($result);
     // return if lowest offers are disabled
     // if ( ! get_option('wpla_repricing_use_lowest_offer') ) return;
     // fetch Lowest Offer info and process result
     $api = new WPLA_AmazonAPI($account_id);
     $result = $api->getLowestOfferListingsForASIN($listing_ASINs);
     $listingsModel->processLowestOfferPricingResult($result);
 }
 public function checkSoldStock()
 {
     // get all published listings
     $lm = new WPLA_ListingsModel();
     $listings = $lm->getWhere('status', 'sold');
     $out_of_stock_products = array();
     // process published listings
     foreach ($listings as $item) {
         // get wc product
         $_product = $this->getProduct($item['post_id']);
         // checking parent variations makes no sense in WPLA, so skip them
         if ($_product->product_type == 'variable') {
             continue;
         }
         // check stock level
         // $stock = WPLA_ProductWrapper::getStock( $item['post_id'] );
         $stock = $_product ? $_product->get_total_stock() : 0;
         if ($stock == 0) {
             continue;
         }
         // mark listing as changed
         // if ( isset( $_REQUEST['mark_as_changed'] ) ) {
         // 	$lm->updateListing( $item['id'], array( 'status' => 'changed' ) );
         // 	$item['status'] = 'changed';
         // }
         // add to list of out of stock products
         $item['stock'] = $stock;
         $item['exists'] = $_product ? true : false;
         $item['parent_id'] = $_product->product_type == 'variation' ? $_product->parent->id : false;
         $out_of_stock_products[] = $item;
     }
     // return if empty
     if (empty($out_of_stock_products)) {
         WPLA()->showMessage('No sold products have stock in WooCommerce.', 0, 1);
         return;
     }
     $msg = '<p>';
     $msg .= 'Warning: Some sold listings are still in stock in WooCommerce.';
     $msg .= '</p>';
     // table header
     $msg .= '<table style="width:100%">';
     $msg .= "<tr>";
     $msg .= "<th style='text-align:left'>Stock</th>";
     $msg .= "<th style='text-align:left'>SKU</th>";
     $msg .= "<th style='text-align:left'>Product</th>";
     $msg .= "<th style='text-align:left'>Qty</th>";
     $msg .= "<th style='text-align:left'>ASIN</th>";
     $msg .= "<th style='text-align:left'>Ended at</th>";
     $msg .= "<th style='text-align:left'>Status</th>";
     $msg .= "</tr>";
     // table rows
     foreach ($out_of_stock_products as $item) {
         // get column data
         // $qty     = $item['quantity'] - $item['quantity_sold'];
         $sku = $item['sku'];
         $qty = $item['quantity'];
         $stock = $item['stock'] . ' x ';
         $title = $item['listing_title'];
         $post_id = $item['post_id'];
         $asin = $item['asin'];
         $status = $item['status'];
         $exists = $item['exists'];
         $date_ended = $item['date_finished'] ? $item['date_finished'] : $item['end_date'];
         // build links
         $amazon_url = $item['ViewItemURL'] ? $item['ViewItemURL'] : ($amazon_url = 'http://www.amazon.com/itm/' . $asin);
         $amazon_url = 'admin.php?page=wpla&s=' . $asin;
         $amazon_link = '<a href="' . $amazon_url . '" target="_blank">' . $asin . '</a>';
         $edit_link = '<a href="post.php?action=edit&post=' . ($item['parent_id'] ? $item['parent_id'] : $post_id) . '" target="_blank">' . $title . '</a>';
         // mark non existent products
         if (!$exists) {
             $stock = 'N/A';
             $post_id .= ' missing!';
         }
         // build table row
         $msg .= "<tr>";
         $msg .= "<td>{$stock}</td>";
         $msg .= "<td>{$sku}</td>";
         $msg .= "<td>{$edit_link} (ID {$post_id})</td>";
         $msg .= "<td>{$qty} x </td>";
         $msg .= "<td>{$amazon_link}</td>";
         $msg .= "<td>{$date_ended}</td>";
         $msg .= "<td>{$status}</td>";
         $msg .= "</tr>";
     }
     $msg .= '</table>';
     // show 'check again' button
     $msg .= '<p>';
     $url = 'admin.php?page=wpla-tools&action=check_wc_sold_stock&_wpnonce=' . wp_create_nonce('wpla_tools_page');
     $msg .= '<a href="' . $url . '" class="button">' . __('Check again', 'wpla') . '</a> &nbsp; ';
     $msg .= '</p>';
     // $msg .= '<p>';
     // $url = 'admin.php?page=wpla-tools&action=check_wc_out_of_stock&mark_as_changed=yes&_wpnonce='.wp_create_nonce('wpla_tools_page');
     // $msg .= '<a href="'.$url.'" class="button">'.__('Mark all as changed','wpla').'</a> &nbsp; ';
     // $msg .= 'Click this button to mark all found listings as changed in WP-Lister.';
     // $msg .= '</p>';
     WPLA()->showMessage($msg, 1, 1);
 }
 public function insertVariationListing($var, $product_node, $parent_listing, $account)
 {
     // get variation product data
     // $variation_id         = $var['post_id'];
     // echo "<pre>";print_r($var);echo"</pre>";#die();
     // generate title suffix from attribute values
     $suffix = join(', ', $var->attribute_values);
     // build single variation listing item
     $data = array();
     $data['post_id'] = '';
     // filled in after creating the product
     $data['parent_id'] = $parent_listing->post_id;
     $data['vtheme'] = $var->vtheme;
     $data['listing_title'] = $product_node->AttributeSets->ItemAttributes->Title . ' - ' . $suffix;
     // $data['post_content']  = $post->post_content;
     $data['price'] = $var->price;
     $data['quantity'] = $var->qty;
     $data['sku'] = $var->sku;
     $data['asin'] = $var->asin;
     $data['date_created'] = date('Y-m-d H:i:s', time());
     $data['status'] = 'matched';
     $data['source'] = 'foreign_import';
     $data['account_id'] = $account->id;
     $data['product_type'] = 'variation';
     // $data['profile_id']    = '';
     $lm = new WPLA_ListingsModel();
     $variation_listing_id = $lm->insertListingData($data);
     return $variation_listing_id;
 }
 /** ************************************************************************
  * REQUIRED! This is where you prepare your data for display. This method will
  * usually be used to query the database, sort and filter the data, and generally
  * get it ready to be displayed. At a minimum, we should set $this->items and
  * $this->set_pagination_args(), although the following properties and methods
  * are frequently interacted with here...
  * 
  * @uses $this->_column_headers
  * @uses $this->items
  * @uses $this->get_columns()
  * @uses $this->get_sortable_columns()
  * @uses $this->get_pagenum()
  * @uses $this->set_pagination_args()
  **************************************************************************/
 function prepare_items($items = false)
 {
     // process bulk actions
     $this->process_bulk_action();
     // get pagination state
     $current_page = $this->get_pagenum();
     $per_page = $this->get_items_per_page('listings_per_page', 20);
     // define columns
     $this->_column_headers = $this->get_column_info();
     // fetch listings from model - if no parameter passed
     if (!$items) {
         $listingsModel = new WPLA_ListingsModel();
         $this->items = $listingsModel->getPageItems($current_page, $per_page, 'repricing');
         $this->total_items = $listingsModel->total_items;
     } else {
         $this->items = $items;
         $this->total_items = count($items);
     }
     // register our pagination options & calculations.
     $this->set_pagination_args(array('total_items' => $this->total_items, 'per_page' => $per_page, 'total_pages' => ceil($this->total_items / $per_page)));
 }
 function customize_toolbar($wp_admin_bar)
 {
     // check if current user can manage listings
     if (!current_user_can('manage_amazon_listings')) {
         return;
     }
     // get stats about active and scheduled jobs
     $feeds_in_progress = get_option('wpla_feeds_in_progress', 0);
     $reports_in_progress = get_option('wpla_reports_in_progress', 0);
     $pending_feeds = get_option('wpla_db_version') > 0 ? WPLA_AmazonFeed::getAllPendingFeeds() : array();
     $total_active_jobs = $feeds_in_progress + $reports_in_progress + sizeof($pending_feeds);
     if ($total_active_jobs) {
         add_action('admin_footer', array(&$this, 'print_admin_toolbar_styles'));
     }
     // top level 'Amazon'
     $extra_class = $total_active_jobs ? '-spinner' : '';
     $args = array('id' => 'wpla_top', 'title' => __('Amazon', 'wpla'), 'href' => admin_url('admin.php?page=wpla'), 'meta' => array('class' => 'wpla-toolbar-top' . $extra_class));
     $wp_admin_bar->add_node($args);
     // Activity Monitor
     $activity_title = sprintf(__('%s active tasks', 'wpla'), $total_active_jobs);
     $activity_title = $total_active_jobs ? $activity_title : __('No active tasks', 'wpla');
     $args = array('id' => 'wpla_current_activity', 'title' => $activity_title, 'href' => '#', 'parent' => 'wpla_top', 'meta' => array('class' => 'wpla-toolbar-page wpla-activity-monitor'));
     $wp_admin_bar->add_node($args);
     // Activity: Reports
     $args = array('id' => 'wpla_current_reports', 'title' => __('Reports in progress', 'wpla') . ': ' . $reports_in_progress, 'href' => admin_url('admin.php?page=wpla-reports'), 'parent' => 'wpla_current_activity', 'meta' => array('class' => 'wpla-toolbar-page wpla-activity-monitor'));
     if ($reports_in_progress) {
         $wp_admin_bar->add_node($args);
     }
     // Activity: Feeds (submitted)
     $args = array('id' => 'wpla_current_feeds_submitted', 'title' => __('Feeds in progress', 'wpla') . ': ' . $feeds_in_progress, 'href' => admin_url('admin.php?page=wpla-feeds&feed_status=submitted'), 'parent' => 'wpla_current_activity', 'meta' => array('class' => 'wpla-toolbar-page wpla-activity-monitor'));
     if ($feeds_in_progress) {
         $wp_admin_bar->add_node($args);
     }
     // Activity: Feeds (pending)
     $args = array('id' => 'wpla_current_feeds_pending', 'title' => __('Scheduled feeds', 'wpla') . ': ' . sizeof($pending_feeds), 'href' => admin_url('admin.php?page=wpla-feeds&feed_status=pending'), 'parent' => 'wpla_current_activity', 'meta' => array('class' => 'wpla-toolbar-page wpla-activity-monitor'));
     if (!empty($pending_feeds)) {
         $wp_admin_bar->add_node($args);
     }
     // Listings page
     $args = array('id' => 'wpla_listings', 'title' => __('Listings', 'wpla'), 'href' => admin_url('admin.php?page=wpla'), 'parent' => 'wpla_top', 'meta' => array('class' => 'wpla-toolbar-page'));
     $wp_admin_bar->add_node($args);
     // Orders page
     $args = array('id' => 'wpla_orders', 'title' => __('Orders', 'wpla'), 'href' => admin_url('admin.php?page=wpla-orders'), 'parent' => 'wpla_top', 'meta' => array('class' => 'wpla-toolbar-page'));
     $wp_admin_bar->add_node($args);
     // Reports page
     $args = array('id' => 'wpla_reports', 'title' => __('Reports', 'wpla'), 'href' => admin_url('admin.php?page=wpla-reports'), 'parent' => 'wpla_top', 'meta' => array('class' => 'wpla-toolbar-page'));
     $wp_admin_bar->add_node($args);
     // Feeds page
     $args = array('id' => 'wpla_feeds', 'title' => __('Feeds', 'wpla'), 'href' => admin_url('admin.php?page=wpla-feeds'), 'parent' => 'wpla_top', 'meta' => array('class' => 'wpla-toolbar-page'));
     $wp_admin_bar->add_node($args);
     // Profiles page
     $args = array('id' => 'wpla_profiles', 'title' => __('Profiles', 'wpla'), 'href' => admin_url('admin.php?page=wpla-profiles'), 'parent' => 'wpla_top', 'meta' => array('class' => 'wpla-toolbar-page'));
     $wp_admin_bar->add_node($args);
     // Import page
     $args = array('id' => 'wpla_import', 'title' => __('Import', 'wpla'), 'href' => admin_url('admin.php?page=wpla-import'), 'parent' => 'wpla_top', 'meta' => array('class' => 'wpla-toolbar-page'));
     $wp_admin_bar->add_node($args);
     // Tools page
     $args = array('id' => 'wpla_tools', 'title' => __('Tools', 'wpla'), 'href' => admin_url('admin.php?page=wpla-tools'), 'parent' => 'wpla_top', 'meta' => array('class' => 'wpla-toolbar-page'));
     $wp_admin_bar->add_node($args);
     // Repricing Tool
     $args = array('id' => 'wpla_tools_repricing', 'title' => __('Repricing Tool', 'wpla'), 'href' => admin_url('admin.php?page=wpla-tools&tab=repricing'), 'parent' => 'wpla_tools', 'meta' => array('class' => 'wpla-toolbar-page'));
     $wp_admin_bar->add_node($args);
     // Inventory Check
     $args = array('id' => 'wpla_tools_inventory', 'title' => __('Inventory Check', 'wpla'), 'href' => admin_url('admin.php?page=wpla-tools&tab=inventory'), 'parent' => 'wpla_tools', 'meta' => array('class' => 'wpla-toolbar-page'));
     $wp_admin_bar->add_node($args);
     // SKU Generator
     $args = array('id' => 'wpla_tools_skugen', 'title' => __('SKU Generator', 'wpla'), 'href' => admin_url('admin.php?page=wpla-tools&tab=skugen'), 'parent' => 'wpla_tools', 'meta' => array('class' => 'wpla-toolbar-page'));
     $wp_admin_bar->add_node($args);
     // Developer Tools
     $args = array('id' => 'wpla_tools_developer', 'title' => __('Developer', 'wpla'), 'href' => admin_url('admin.php?page=wpla-tools&tab=developer'), 'parent' => 'wpla_tools', 'meta' => array('class' => 'wpla-toolbar-page'));
     $wp_admin_bar->add_node($args);
     if (current_user_can('manage_amazon_options')) {
         // Settings page
         $args = array('id' => 'wpla_settings', 'title' => __('Settings', 'wpla'), 'href' => admin_url('admin.php?page=wpla-settings'), 'parent' => 'wpla_top', 'meta' => array('class' => 'wpla-toolbar-page'));
         $wp_admin_bar->add_node($args);
         // Settings - General tab
         $args = array('id' => 'wpla_settings_general', 'title' => __('General Settings', 'wpla'), 'href' => admin_url('admin.php?page=wpla-settings&tab=settings'), 'parent' => 'wpla_settings', 'meta' => array('class' => 'wpla-toolbar-page'));
         $wp_admin_bar->add_node($args);
         // Settings - Accounts tab
         $args = array('id' => 'wpla_settings_accounts', 'title' => __('Accounts', 'wpla'), 'href' => admin_url('admin.php?page=wpla-settings&tab=accounts'), 'parent' => 'wpla_settings', 'meta' => array('class' => 'wpla-toolbar-page'));
         $wp_admin_bar->add_node($args);
         // Settings - Categories tab
         $args = array('id' => 'wpla_settings_categories', 'title' => __('Categories', 'wpla'), 'href' => admin_url('admin.php?page=wpla-settings&tab=categories'), 'parent' => 'wpla_settings', 'meta' => array('class' => 'wpla-toolbar-page'));
         $wp_admin_bar->add_node($args);
         // Settings - Advanced tab
         $args = array('id' => 'wpla_settings_advanced', 'title' => __('Advanced', 'wpla'), 'href' => admin_url('admin.php?page=wpla-settings&tab=advanced'), 'parent' => 'wpla_settings', 'meta' => array('class' => 'wpla-toolbar-page'));
         $wp_admin_bar->add_node($args);
         // Settings - Developer tab
         $args = array('id' => 'wpla_settings_developer', 'title' => __('Developer', 'wpla'), 'href' => admin_url('admin.php?page=wpla-settings&tab=developer'), 'parent' => 'wpla_settings', 'meta' => array('class' => 'wpla-toolbar-page'));
         $wp_admin_bar->add_node($args);
     }
     // if current_user_can('manage_amazon_options')
     if (current_user_can('manage_amazon_options') && get_option('wpla_log_to_db') == '1') {
         // Logs page
         $args = array('id' => 'wpla_log', 'title' => __('Logs', 'wpla'), 'href' => admin_url('admin.php?page=wpla-log'), 'parent' => 'wpla_top', 'meta' => array('class' => 'wpla-toolbar-page'));
         $wp_admin_bar->add_node($args);
     }
     // product page
     global $post;
     global $wp_query;
     global $pagenow;
     $post_id = false;
     if ($wp_query->in_the_loop && isset($wp_query->post->post_type) && $wp_query->post->post_type == 'product') {
         $post_id = $wp_query->post->ID;
     } elseif (is_object($post) && isset($post->post_type) && $post->post_type == 'product') {
         $post_id = $post->ID;
     }
     // skip product links on the main products page
     if ($pagenow == 'edit.php') {
         return;
     }
     // do we have a single product page?
     if (empty($post_id)) {
         return;
     }
     // enqueue ProductMatcher.js
     wp_register_script('wpla_product_matcher', WPLA_URL . '/js/classes/ProductMatcher.js?ver=' . time(), array('jquery'));
     wp_enqueue_script('wpla_product_matcher');
     wp_localize_script('wpla_product_matcher', 'wpla_ProductMatcher_i18n', array('WPLA_URL' => WPLA_URL));
     // get all items
     $lm = new WPLA_ListingsModel();
     $listings = $lm->getAllItemsByPostOrParentID($post_id);
     if (sizeof($listings) > 0) {
         $asin = $lm->getASINFromPostID($post_id);
         // $url = $lm->getViewItemURLFromPostID( $post_id );
         // View on Amazon link
         $args = array('id' => 'wpla_view_on_amazon', 'title' => __('View item on Amazon', 'wpla'), 'parent' => 'wpla_top', 'meta' => array('target' => '_blank', 'class' => 'wpla-toolbar-link'));
         if ($asin) {
             $wp_admin_bar->add_node($args);
         }
         foreach ($listings as $listing) {
             $listing_url = 'http://www.amazon.com/dp/' . $listing->asin . '/';
             if ($listing->account_id) {
                 $account = new WPLA_AmazonAccount($listing->account_id);
                 $market = new WPLA_AmazonMarket($account->market_id);
                 $listing_url = 'http://www.' . $market->url . '/dp/' . $listing->asin . '/';
             }
             $args = array('id' => 'wpla_view_on_amazon_' . $listing->id, 'title' => '#' . $listing->asin . ': ' . $listing->listing_title, 'href' => $listing_url, 'parent' => 'wpla_view_on_amazon', 'meta' => array('target' => '_blank', 'class' => 'wpla-toolbar-link'));
             if ($listing_url) {
                 $wp_admin_bar->add_node($args);
             }
         }
         // View in WP-Lister
         $url = admin_url('admin.php?page=wpla&s=' . $post_id);
         $args = array('id' => 'wpla_view_on_listings_page', 'title' => __('View item in WP-Lister', 'wpla'), 'href' => $url, 'parent' => 'wpla_top', 'meta' => array('target' => '_blank', 'class' => 'wpla-toolbar-link'));
         $wp_admin_bar->add_node($args);
     } else {
         // no listings
         // match product option
         $tb_url = admin_url('admin-ajax.php?action=wpla_show_product_matches&id=' . $post_id . '&width=640&height=420');
         // echo '<a href="'.$tb_url.'" class="thickbox" title="Match product on Amazon"><img src="'.WPLA_URL.'/img/search3.png" alt="match" /></a>';
         $onclick = 'tb_show("' . __('Match on Amazon', 'wpla') . '", "' . $tb_url . '");return false;';
         $args = array('id' => 'wpla_match_on_amazon', 'title' => __('Match on Amazon', 'wpla'), 'href' => $tb_url, 'parent' => 'wpla_top', 'meta' => array('onclick' => $onclick, 'class' => 'wpla-toolbar-link'));
         $wp_admin_bar->add_node($args);
         // $args = $this->addPrepareActions( $args );
     }
     // if ( current_user_can('prepare_amazon_listings') )
     $this->addPrepareActions($wp_admin_bar, $post_id);
 }
 public function deleteProfiles($profiles)
 {
     if (!is_array($profiles)) {
         $profiles = array($profiles);
     }
     $count = 0;
     foreach ($profiles as $id) {
         if (!$id) {
             continue;
         }
         // check if there are listings using this profile
         $lm = new WPLA_ListingsModel();
         $listings = $lm->findAllListingsByColumn($id, 'profile_id');
         if (!empty($listings)) {
             $this->showMessage('This profile is applied to ' . count($listings) . ' listings and can not be deleted.', 1, 1);
             continue;
         }
         $profile = new WPLA_AmazonProfile($id);
         $profile->delete();
         $count++;
     }
     if ($count) {
         $this->showMessage(sprintf(__('%s profile(s) were removed.', 'wpla'), $count));
     }
 }
 public static function processQualityReportPage($report, $rows, $job, $task)
 {
     $listingsModel = new WPLA_ListingsModel();
     // reset quality info for all products using this account
     $account_id = $report->account_id;
     $update_data = array('quality_status' => null, 'quality_info' => null);
     $listingsModel->updateWhere(array('account_id' => $account_id), $update_data);
     // process rows
     foreach ($rows as $row) {
         $asin = $row['asin'];
         $sku = $row['sku'];
         $quality_info = array('sku' => $row['sku'], 'product-name' => $row['product-name'], 'asin' => $row['asin'], 'field-name' => $row['field-name'], 'alert-type' => $row['alert-type'], 'current-value' => $row['current-value'], 'last-updated' => $row['last-updated'], 'alert-name' => $row['alert-name'], 'status' => $row['status'], 'explanation' => $row['explanation'], 'ts' => time());
         $update_data = array('quality_status' => $row['alert-name'], 'quality_info' => serialize($quality_info));
         if ($asin) {
             $listingsModel->updateWhere(array('asin' => $asin), $update_data);
         }
         if ($sku) {
             $listingsModel->updateWhere(array('sku' => $sku), $update_data);
         }
     }
     // build response
     $response = new stdClass();
     $response->job = $job;
     $response->task = $task;
     $response->errors = '';
     $response->success = true;
     return $response;
 }
 public function displayRepricingPage()
 {
     // handle actions and show notes
     // $this->handleActions();
     if ($this->requestAction() == 'wpla_apply_lowest_price_to_all_items') {
         $this->applyLowestPricesToAllItems();
     }
     if ($this->requestAction() == 'wpla_resubmit_pnq_update') {
         $this->resubmitPnqUpdateForSelectedItems();
     }
     if ($this->requestAction() == 'wpla_bulk_apply_lowest_prices') {
         $this->applyLowestPricesToSelectedItems();
     }
     if ($this->requestAction() == 'wpla_bulk_apply_minmax_prices') {
         $this->applyMinMaxPrices();
     }
     // handle bulk action - get_compet_price
     if ($this->requestAction() == 'get_compet_price') {
         WPLA()->pages['listings']->get_compet_price();
         WPLA()->pages['listings']->get_lowest_offers();
     }
     if ($this->requestAction() == 'wpla_resubmit_all_failed_prices') {
         $lm = new WPLA_ListingsModel();
         $items = $lm->getWhere('pnq_status', -1);
         foreach ($items as $item) {
             // set pnq status to changed (1)
             $lm->updateWhere(array('id' => $item->id), array('pnq_status' => 1));
         }
         $this->showMessage(sprintf(__('%s failed prices were scheduled for resubmission.', 'wpla'), count($items)));
     }
     // create table and fetch items to show
     $this->repricingTable = new WPLA_RepricingTable();
     $this->repricingTable->prepare_items();
     $active_tab = 'repricing';
     $aData = array('plugin_url' => self::$PLUGIN_URL, 'message' => $this->message, 'listingsTable' => $this->repricingTable, 'default_account' => get_option('wpla_default_account_id'), 'tools_url' => 'admin.php?page=' . self::ParentMenuId . '-tools', 'form_action' => 'admin.php?page=' . self::ParentMenuId . '-tools' . '&tab=' . $active_tab);
     $this->display('tools_repricing', $aData);
 }
 public function updateProductFromItem($item, $report_row)
 {
     global $woocommerce;
     WPLA()->logger->info("==============================================================================");
     //WPLA()->logger->info( "updateProductFromItem() - ID: ".$data['asin'] );
     // some shortcuts
     $asin = $item->asin;
     $product_id = $item->post_id;
     $amazon_name = $item->listing_title;
     $amazon_price = $item->price;
     $amazon_quantity = $item->quantity;
     $report_quantity = $report_row['quantity'];
     $updated = false;
     // get WC product for reference
     $product = $this->getProduct($product_id);
     if (!$product) {
         return;
     }
     // echo "<pre>";print_r($product);echo"</pre>";#die();
     // get options
     $reports_update_woo_stock = get_option('wpla_reports_update_woo_stock', 1) == 1 ? true : false;
     $reports_update_woo_price = get_option('wpla_reports_update_woo_price', 1) == 1 ? true : false;
     $reports_update_woo_condition = get_option('wpla_reports_update_woo_condition', 1) == 1 ? true : false;
     //
     // update item-condition - if enabled
     //
     if ($reports_update_woo_condition) {
         $amazon_condition_type = WPLA_ImportHelper::convertNumericConditionIdToType($report_row['item-condition']);
         update_post_meta($product_id, '_amazon_condition_type', $amazon_condition_type);
         $amazon_condition_note = WPLA_ListingsModel::convertToUTF8($report_row['item-note']);
         update_post_meta($product_id, '_amazon_condition_note', $amazon_condition_note);
         WPLA()->logger->info("updated condition for product {$product_id}: {$amazon_condition_type} / " . $amazon_condition_note);
         // WPLA()->logger->info( "stored condition note: " . get_post_meta( $product_id, '_amazon_condition_note', true ) );
     }
     //
     // update price - if enabled
     //
     if ($reports_update_woo_price) {
         // if this item has a profile, we need to apply the price modifiers to the product price
         $product_price = $product->price;
         $profile = $item->profile_id ? new WPLA_AmazonProfile($item->profile_id) : false;
         if ($profile) {
             $product_price = $profile->processProfilePrice($product_price);
             $amazon_price = $profile->reverseProfilePrice($amazon_price);
         }
         // update price - unless custom amazon price is set
         if ($product_price != $amazon_price) {
             if (!get_post_meta($product_id, '_amazon_price', true)) {
                 update_post_meta($product_id, '_price', $amazon_price);
                 update_post_meta($product_id, '_regular_price', $amazon_price);
                 WPLA()->logger->info("updated price for product {$product_id} - new price: " . $amazon_price);
                 $updated = true;
             }
         }
     }
     // if update price
     //
     // update stock - if enabled and the report quantity column is not empty
     //
     if ($reports_update_woo_stock && $report_quantity !== '' && $report_quantity !== false) {
         if ($product->stock != $amazon_quantity) {
             update_post_meta($product_id, '_stock', $amazon_quantity);
             WPLA()->logger->info("updated stock for product {$product_id} - new stock: " . $amazon_quantity);
             $updated = true;
         }
         // update out of stock attribute
         if ($amazon_quantity > 0) {
             $stock_status = 'instock';
         } elseif ($item->product_type == 'variable') {
             $stock_status = 'instock';
         } else {
             $stock_status = 'outofstock';
         }
         update_post_meta($product_id, '_stock_status', $stock_status);
     }
     if ($updated) {
         // $woocommerce->clear_product_transients( $product_id );
         if (function_exists('wc_delete_product_transients')) {
             wc_delete_product_transients($product_id);
         }
         WPLA()->logger->info("updated product {$product_id} ({$asin}): {$amazon_name} ");
         $this->updated_count++;
     }
     return $product_id;
 }
 public function findMissingProducts()
 {
     $items = WPLA_ListingQueryHelper::findMissingProducts();
     $mode = isset($_REQUEST['mode']) ? $_REQUEST['mode'] : false;
     if ($mode == 'delete') {
         foreach ($items as $item) {
             WPLA_ListingsModel::deleteItem($item->id);
         }
         wpla_show_message(sizeof($items) . ' items have been deleted.');
         return;
     }
     if ($mode == 'import') {
         foreach ($items as $item) {
             $data = array('status' => 'imported');
             WPLA_ListingsModel::updateWhere(array('id' => $item->id), $data);
         }
         wpla_show_message(sizeof($items) . ' items have been added to the import queue.');
         return;
     }
     if (!empty($items)) {
         $nonce = wp_create_nonce('wpla_tools_page');
         $btn_delete = '<a href="admin.php?page=wpla-tools&tab=inventory&action=wpla_check_for_missing_products&mode=delete&_wpnonce=' . $nonce . '" class="button button-small button-secondary">' . 'Delete all from DB' . '</a> &nbsp; ';
         $btn_import = '<a href="admin.php?page=wpla-tools&tab=inventory&action=wpla_check_for_missing_products&mode=import&_wpnonce=' . $nonce . '" class="button button-small button-primary"  >' . 'Add to import queue' . '</a>';
         $buttons = ' &nbsp; ' . $btn_delete . $btn_import;
         wpla_show_message('There are ' . sizeof($items) . ' listing(s) without a linked product in WooCommerce.' . $buttons, 'error');
     } else {
         wpla_show_message('No missing products found.');
     }
 }
 public function renderDupeTable($listings, $column = 'post_id')
 {
     if (empty($listings)) {
         return '';
     }
     // get current page with paging as url param
     $page = $_REQUEST['page'];
     if (isset($_REQUEST['paged'])) {
         $page .= '&paged=' . $_REQUEST['paged'];
     }
     $listingsModel = new WPLA_ListingsModel();
     $msg = '';
     foreach ($listings as $dupe) {
         $account_title = WPLA_AmazonAccount::getAccountTitle($dupe->account_id);
         $msg .= '<b>' . __('Listings for', 'wpla') . ' ' . strtoupper($column) . ' ' . $dupe->{$column} . ' (' . $account_title . '):</b>';
         $msg .= '<br>';
         $duplicateListings = $listingsModel->findAllListingsByColumn($dupe->{$column}, $column, $dupe->account_id);
         $msg .= '<table style="width:100%">';
         foreach ($duplicateListings as $listing) {
             $color = $listing->status == 'archived' ? 'silver' : '';
             // check if WooCommerce SKU matches Amazon SKU
             $woo_sku = get_post_meta($listing->post_id, '_sku', true);
             $sku_label = $listing->sku == $woo_sku ? $woo_sku : '<span style="color:darkred">' . $woo_sku . ' / ' . $listing->sku . '</span>';
             $msg .= '<tr><td style="width:40%;">';
             $msg .= '<span style="color:' . $color . '">';
             $msg .= $listing->listing_title;
             $msg .= '</span>';
             $msg .= '</td><td style="width:10%;">';
             $msg .= '<i style="color:silver">' . $listing->product_type . '</i>';
             $msg .= '</td><td style="width:10%;">';
             $msg .= '<a href="admin.php?page=wpla&s=' . $listing->sku . '" title="SKU" target="_blank">';
             $msg .= $sku_label . '</a>';
             $msg .= '</td><td style="width:10%;">';
             $msg .= '<a href="admin.php?page=wpla&s=' . $listing->asin . '" title="ASIN" target="_blank">';
             $msg .= $listing->asin . '</a>';
             $msg .= '</td><td style="width:10%;">';
             $msg .= '<a href="admin.php?page=wpla&s=' . $listing->post_id . '" title="Product ID" target="_blank">';
             $msg .= 'ID ' . $listing->post_id . '</a>';
             $msg .= '</td><td style="width:10%;">';
             $msg .= '<i>' . $listing->status . '</i>';
             // if ( in_array( $listing->status, array( 'prepared', 'verified', 'ended', 'sold' ) ) ) {
             // 	$archive_link = sprintf('<a class="archive button button-small" href="?page=%s&action=%s&listing=%s">%s</a>',$page,'archive',$listing->id,__('Click to move to archive','wpla'));
             // 	$msg .= '&nbsp;&nbsp;&nbsp;&nbsp;'.$archive_link;
             // 	$msg .= '<br>';
             // }
             $msg .= '</td><td align="right" style="width:10%;">';
             $delete_btn = sprintf('<a class="delete button button-small button-secondary" href="?page=%s&action=%s&listing=%s">%s</a>', $page, 'delete', $listing->id, __('Remove from database', 'wpla'));
             $msg .= $delete_btn;
             $msg .= '</td></tr>';
         }
         $msg .= '</table>';
         $msg .= '<br>';
     }
     return $msg;
 }