1542 lines
63 KiB
PHP
1542 lines
63 KiB
PHP
<?php
|
|
|
|
namespace Modules\Woocommerce\Utils;
|
|
|
|
use App\Business;
|
|
use App\Category;
|
|
use App\Contact;
|
|
use App\Exceptions\PurchaseSellMismatch;
|
|
use App\Product;
|
|
use App\TaxRate;
|
|
use App\Transaction;
|
|
use App\Utils\ContactUtil;
|
|
use App\Utils\ProductUtil;
|
|
use App\Utils\TransactionUtil;
|
|
use App\Utils\Util;
|
|
use App\VariationLocationDetails;
|
|
use App\VariationTemplate;
|
|
use Automattic\WooCommerce\Client;
|
|
use DB;
|
|
use Modules\Woocommerce\Entities\WoocommerceSyncLog;
|
|
use Modules\Woocommerce\Exceptions\WooCommerceError;
|
|
|
|
class WoocommerceUtil extends Util
|
|
{
|
|
/**
|
|
* All Utils instance.
|
|
*/
|
|
protected $transactionUtil;
|
|
|
|
protected $productUtil;
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* @param ProductUtils $product
|
|
* @return void
|
|
*/
|
|
public function __construct(TransactionUtil $transactionUtil, ProductUtil $productUtil)
|
|
{
|
|
$this->transactionUtil = $transactionUtil;
|
|
$this->productUtil = $productUtil;
|
|
}
|
|
|
|
public function get_api_settings($business_id)
|
|
{
|
|
$business = Business::find($business_id);
|
|
$woocommerce_api_settings = json_decode($business->woocommerce_api_settings);
|
|
|
|
return $woocommerce_api_settings;
|
|
}
|
|
|
|
private function add_to_skipped_orders($business, $order_id)
|
|
{
|
|
$business = ! is_object($business) ? Business::find($business) : $business;
|
|
$skipped_orders = ! empty($business->woocommerce_skipped_orders) ? json_decode($business->woocommerce_skipped_orders, true) : [];
|
|
if (! in_array($order_id, $skipped_orders)) {
|
|
$skipped_orders[] = $order_id;
|
|
}
|
|
|
|
$business->woocommerce_skipped_orders = json_encode($skipped_orders);
|
|
$business->save();
|
|
}
|
|
|
|
private function remove_from_skipped_orders($business, $order_id)
|
|
{
|
|
$business = ! is_object($business) ? Business::find($business) : $business;
|
|
$skipped_orders = ! empty($business->woocommerce_skipped_orders) ? json_decode($business->woocommerce_skipped_orders, true) : [];
|
|
if (in_array($order_id, $skipped_orders)) {
|
|
$skipped_orders = array_diff($skipped_orders, [$order_id]);
|
|
}
|
|
|
|
$business->woocommerce_skipped_orders = json_encode($skipped_orders);
|
|
$business->save();
|
|
}
|
|
|
|
/**
|
|
* Creates Automattic\WooCommerce\Client object
|
|
*
|
|
* @param int $business_id
|
|
* @return obj
|
|
*/
|
|
public function woo_client($business_id)
|
|
{
|
|
$woocommerce_api_settings = $this->get_api_settings($business_id);
|
|
if (empty($woocommerce_api_settings)) {
|
|
throw new WooCommerceError(__('woocommerce::lang.unable_to_connect'));
|
|
}
|
|
|
|
$woocommerce = new Client(
|
|
$woocommerce_api_settings->woocommerce_app_url,
|
|
$woocommerce_api_settings->woocommerce_consumer_key,
|
|
$woocommerce_api_settings->woocommerce_consumer_secret,
|
|
[
|
|
'wp_api' => true,
|
|
'version' => 'wc/v2',
|
|
'timeout' => 10000,
|
|
'verify_ssl' => false,
|
|
]
|
|
);
|
|
|
|
return $woocommerce;
|
|
}
|
|
|
|
public function syncCat($business_id, $data, $type, $new_categories = [])
|
|
{
|
|
|
|
//woocommerce api client object
|
|
$woocommerce = $this->woo_client($business_id);
|
|
$count = 0;
|
|
foreach (array_chunk($data, 99) as $chunked_array) {
|
|
$sync_data = [];
|
|
$sync_data[$type] = $chunked_array;
|
|
//Batch update categories
|
|
|
|
$response = $woocommerce->post('products/categories/batch', $sync_data);
|
|
|
|
//update woocommerce_cat_id
|
|
if (! empty($response->create)) {
|
|
foreach ($response->create as $key => $value) {
|
|
$new_category = $new_categories[$count];
|
|
if ($value->id != 0) {
|
|
$new_category->woocommerce_cat_id = $value->id;
|
|
} else {
|
|
if (! empty($value->error->data->resource_id)) {
|
|
$new_category->woocommerce_cat_id = $value->error->data->resource_id;
|
|
}
|
|
}
|
|
$new_category->save();
|
|
$count++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Synchronizes pos categories with Woocommerce categories
|
|
*
|
|
* @param int $business_id
|
|
* @return void
|
|
*/
|
|
public function syncCategories($business_id, $user_id)
|
|
{
|
|
$last_synced = $this->getLastSync($business_id, 'categories', false);
|
|
|
|
//Update parent categories
|
|
$query = Category::where('business_id', $business_id)
|
|
->where('category_type', 'product')
|
|
->where('parent_id', 0);
|
|
|
|
//Limit query to last sync
|
|
if (! empty($last_synced)) {
|
|
$query->where('updated_at', '>', $last_synced);
|
|
}
|
|
|
|
$categories = $query->get();
|
|
|
|
$category_data = [];
|
|
$new_categories = [];
|
|
$created_data = [];
|
|
$updated_data = [];
|
|
foreach ($categories as $category) {
|
|
if (empty($category->woocommerce_cat_id)) {
|
|
$category_data['create'][] = [
|
|
'name' => $category->name,
|
|
];
|
|
$new_categories[] = $category;
|
|
$created_data[] = $category->name;
|
|
} else {
|
|
$category_data['update'][] = [
|
|
'id' => $category->woocommerce_cat_id,
|
|
'name' => $category->name,
|
|
];
|
|
$updated_data[] = $category->name;
|
|
}
|
|
}
|
|
|
|
if (! empty($category_data['create'])) {
|
|
$this->syncCat($business_id, $category_data['create'], 'create', $new_categories);
|
|
}
|
|
if (! empty($category_data['update'])) {
|
|
$this->syncCat($business_id, $category_data['update'], 'update', $new_categories);
|
|
}
|
|
|
|
//Sync child categories
|
|
$query2 = Category::where('business_id', $business_id)
|
|
->where('category_type', 'product')
|
|
->where('parent_id', '!=', 0);
|
|
//Limit query to last sync
|
|
if (! empty($last_synced)) {
|
|
$query2->where('updated_at', '>', $last_synced);
|
|
}
|
|
|
|
$child_categories = $query2->get();
|
|
|
|
$cat_id_woocommerce_id = Category::where('business_id', $business_id)
|
|
->where('parent_id', 0)
|
|
->where('category_type', 'product')
|
|
->pluck('woocommerce_cat_id', 'id')
|
|
->toArray();
|
|
|
|
$category_data = [];
|
|
$new_categories = [];
|
|
foreach ($child_categories as $category) {
|
|
if (empty($cat_id_woocommerce_id[$category->parent_id])) {
|
|
continue;
|
|
}
|
|
|
|
if (empty($category->woocommerce_cat_id)) {
|
|
$category_data['create'][] = [
|
|
'name' => $category->name,
|
|
'parent' => $cat_id_woocommerce_id[$category->parent_id],
|
|
];
|
|
$new_categories[] = $category;
|
|
$created_data[] = $category->name;
|
|
} else {
|
|
$category_data['update'][] = [
|
|
'id' => $category->woocommerce_cat_id,
|
|
'name' => $category->name,
|
|
'parent' => $cat_id_woocommerce_id[$category->parent_id],
|
|
];
|
|
$updated_data[] = $category->name;
|
|
}
|
|
}
|
|
|
|
if (! empty($category_data['create'])) {
|
|
$this->syncCat($business_id, $category_data['create'], 'create', $new_categories);
|
|
}
|
|
if (! empty($category_data['update'])) {
|
|
$this->syncCat($business_id, $category_data['update'], 'update', $new_categories);
|
|
}
|
|
|
|
//Create log
|
|
if (! empty($created_data)) {
|
|
$this->createSyncLog($business_id, $user_id, 'categories', 'created', $created_data);
|
|
}
|
|
if (! empty($updated_data)) {
|
|
$this->createSyncLog($business_id, $user_id, 'categories', 'updated', $updated_data);
|
|
}
|
|
if (empty($created_data) && empty($updated_data)) {
|
|
$this->createSyncLog($business_id, $user_id, 'categories');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Synchronizes pos products with Woocommerce products
|
|
*
|
|
* @param int $business_id
|
|
* @return void
|
|
*/
|
|
public function syncProducts($business_id, $user_id, $sync_type, $limit = 100, $page = 0)
|
|
{
|
|
//$limit is zero for console command
|
|
if ($page == 0 || $limit == 0) {
|
|
//Sync Categories
|
|
$this->syncCategories($business_id, $user_id);
|
|
|
|
//Sync variation attributes
|
|
$this->syncVariationAttributes($business_id);
|
|
|
|
if ($limit > 0) {
|
|
request()->session()->forget('last_product_synced');
|
|
}
|
|
}
|
|
|
|
$last_synced = ! empty(session('last_product_synced')) ? session('last_product_synced') : $this->getLastSync($business_id, 'all_products', false);
|
|
//store last_synced if page is 0
|
|
if ($page == 0) {
|
|
session(['last_product_synced' => $last_synced]);
|
|
}
|
|
|
|
$woocommerce_api_settings = $this->get_api_settings($business_id);
|
|
$created_data = [];
|
|
$updated_data = [];
|
|
|
|
$business_location_id = $woocommerce_api_settings->location_id;
|
|
$offset = $page * $limit;
|
|
$query = Product::where('business_id', $business_id)
|
|
->whereIn('type', ['single', 'variable'])
|
|
->where('woocommerce_disable_sync', 0)
|
|
->with(['variations', 'category', 'sub_category',
|
|
'variations.variation_location_details',
|
|
'variations.product_variation',
|
|
'variations.product_variation.variation_template', ]);
|
|
|
|
if ($limit > 0) {
|
|
$query->limit($limit)
|
|
->offset($offset);
|
|
}
|
|
|
|
if ($sync_type == 'new') {
|
|
$query->whereNull('woocommerce_product_id');
|
|
}
|
|
|
|
//Select products only from selected location
|
|
if (! empty($business_location_id)) {
|
|
$query->ForLocation($business_location_id);
|
|
}
|
|
|
|
$all_products = $query->get();
|
|
$product_data = [];
|
|
$new_products = [];
|
|
$updated_products = [];
|
|
|
|
if (count($all_products) == 0) {
|
|
request()->session()->forget('last_product_synced');
|
|
}
|
|
|
|
foreach ($all_products as $product) {
|
|
//Skip product if last updated is less than last sync
|
|
$last_updated = $product->updated_at;
|
|
//check last stock updated
|
|
$last_stock_updated = $this->getLastStockUpdated($business_location_id, $product->id);
|
|
|
|
if (! empty($last_stock_updated)) {
|
|
$last_updated = strtotime($last_stock_updated) > strtotime($last_updated) ?
|
|
$last_stock_updated : $last_updated;
|
|
}
|
|
if (! empty($product->woocommerce_product_id) && ! empty($last_synced) && strtotime($last_updated) < strtotime($last_synced)) {
|
|
continue;
|
|
}
|
|
|
|
//Set common data
|
|
$array = [
|
|
'type' => $product->type == 'single' ? 'simple' : 'variable',
|
|
'sku' => $product->sku,
|
|
];
|
|
|
|
$manage_stock = false;
|
|
if ($product->enable_stock == 1 && $product->type == 'single') {
|
|
$manage_stock = true;
|
|
}
|
|
|
|
//Get details from first variation for single product only
|
|
$first_variation = $product->variations->first();
|
|
if (empty($first_variation)) {
|
|
continue;
|
|
}
|
|
$price = $woocommerce_api_settings->product_tax_type == 'exc' ? $first_variation->default_sell_price : $first_variation->sell_price_inc_tax;
|
|
|
|
if (! empty($woocommerce_api_settings->default_selling_price_group)) {
|
|
$group_prices = $this->productUtil->getVariationGroupPrice($first_variation->id, $woocommerce_api_settings->default_selling_price_group, $product->tax_id);
|
|
|
|
$price = $woocommerce_api_settings->product_tax_type == 'exc' ? $group_prices['price_exc_tax'] : $group_prices['price_inc_tax'];
|
|
}
|
|
|
|
//Set product stock
|
|
$qty_available = 0;
|
|
if ($manage_stock) {
|
|
$variation_location_details = $first_variation->variation_location_details;
|
|
foreach ($variation_location_details as $vld) {
|
|
if ($vld->location_id == $business_location_id) {
|
|
$qty_available = $vld->qty_available;
|
|
}
|
|
}
|
|
}
|
|
|
|
//Set product category
|
|
$product_cat = [];
|
|
if (! empty($product->category)) {
|
|
$product_cat[] = ['id' => $product->category->woocommerce_cat_id];
|
|
}
|
|
if (! empty($product->sub_category)) {
|
|
$product_cat[] = ['id' => $product->sub_category->woocommerce_cat_id];
|
|
}
|
|
|
|
//set attributes for variable products
|
|
if ($product->type == 'variable') {
|
|
$variation_attr_data = [];
|
|
|
|
foreach ($product->variations as $variation) {
|
|
if (! empty($variation->product_variation->variation_template->woocommerce_attr_id)) {
|
|
$woocommerce_attr_id = $variation->product_variation->variation_template->woocommerce_attr_id;
|
|
$variation_attr_data[$woocommerce_attr_id][] = $variation->name;
|
|
}
|
|
}
|
|
|
|
foreach ($variation_attr_data as $key => $value) {
|
|
$array['attributes'][] = [
|
|
'id' => $key,
|
|
'variation' => true,
|
|
'visible' => true,
|
|
'options' => $value,
|
|
];
|
|
}
|
|
}
|
|
|
|
$sync_description_as = ! empty($woocommerce_api_settings->sync_description_as) ? $woocommerce_api_settings->sync_description_as : 'long';
|
|
|
|
if (empty($product->woocommerce_product_id)) {
|
|
$array['tax_class'] = ! empty($woocommerce_api_settings->default_tax_class) ?
|
|
$woocommerce_api_settings->default_tax_class : 'standard';
|
|
|
|
//assign category
|
|
if (in_array('category', $woocommerce_api_settings->product_fields_for_create)) {
|
|
if (! empty($product_cat)) {
|
|
$array['categories'] = $product_cat;
|
|
}
|
|
}
|
|
|
|
if (in_array('weight', $woocommerce_api_settings->product_fields_for_create)) {
|
|
$array['weight'] = $this->formatDecimalPoint($product->weight);
|
|
}
|
|
|
|
//sync product description
|
|
if (in_array('description', $woocommerce_api_settings->product_fields_for_create)) {
|
|
if ($sync_description_as == 'long') {
|
|
$array['description'] = $product->product_description;
|
|
} elseif ($sync_description_as == 'short') {
|
|
$array['short_description'] = $product->product_description;
|
|
} else {
|
|
$array['description'] = $product->product_description;
|
|
$array['short_description'] = $product->product_description;
|
|
}
|
|
}
|
|
|
|
//Set product image url
|
|
//If media id is set use media id else use image src
|
|
if (! empty($product->image) && in_array('image', $woocommerce_api_settings->product_fields_for_create)) {
|
|
if ($this->isValidImage($product->image_path)) {
|
|
$array['images'] = ! empty($product->woocommerce_media_id) ? [['id' => $product->woocommerce_media_id]] : [['src' => $product->image_url]];
|
|
}
|
|
}
|
|
|
|
//assign quantity and price if single product
|
|
if ($product->type == 'single') {
|
|
$array['manage_stock'] = $manage_stock;
|
|
if (in_array('quantity', $woocommerce_api_settings->product_fields_for_create)) {
|
|
$array['stock_quantity'] = $this->formatDecimalPoint($qty_available, 'quantity');
|
|
} else {
|
|
//set manage stock and in_stock if quantity disabled
|
|
if (isset($woocommerce_api_settings->manage_stock_for_create)) {
|
|
if ($woocommerce_api_settings->manage_stock_for_create == 'true') {
|
|
$array['manage_stock'] = true;
|
|
} elseif ($woocommerce_api_settings->manage_stock_for_create == 'false') {
|
|
$array['manage_stock'] = false;
|
|
} else {
|
|
unset($array['manage_stock']);
|
|
}
|
|
}
|
|
if (isset($woocommerce_api_settings->in_stock_for_create)) {
|
|
if ($woocommerce_api_settings->in_stock_for_create == 'true') {
|
|
$array['in_stock'] = true;
|
|
} elseif ($woocommerce_api_settings->in_stock_for_create == 'false') {
|
|
$array['in_stock'] = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
$array['regular_price'] = $this->formatDecimalPoint($price);
|
|
}
|
|
|
|
//assign name
|
|
$array['name'] = $product->name;
|
|
|
|
$product_data['create'][] = $array;
|
|
$new_products[] = $product;
|
|
|
|
$created_data[] = $product->sku;
|
|
} else {
|
|
$array['id'] = $product->woocommerce_product_id;
|
|
//assign category
|
|
if (in_array('category', $woocommerce_api_settings->product_fields_for_update)) {
|
|
if (! empty($product_cat)) {
|
|
$array['categories'] = $product_cat;
|
|
}
|
|
}
|
|
|
|
if (in_array('weight', $woocommerce_api_settings->product_fields_for_update)) {
|
|
$array['weight'] = $this->formatDecimalPoint($product->weight);
|
|
}
|
|
|
|
//sync product description
|
|
if (in_array('description', $woocommerce_api_settings->product_fields_for_update)) {
|
|
if ($sync_description_as == 'long') {
|
|
$array['description'] = $product->product_description;
|
|
} elseif ($sync_description_as == 'short') {
|
|
$array['short_description'] = $product->product_description;
|
|
} else {
|
|
$array['description'] = $product->product_description;
|
|
$array['short_description'] = $product->product_description;
|
|
}
|
|
}
|
|
|
|
//If media id is set use media id else use image src
|
|
if (! empty($product->image) && in_array('image', $woocommerce_api_settings->product_fields_for_update)) {
|
|
if ($this->isValidImage($product->image_path)) {
|
|
$array['images'] = ! empty($product->woocommerce_media_id) ? [['id' => $product->woocommerce_media_id]] : [['src' => $product->image_url]];
|
|
}
|
|
}
|
|
|
|
if ($product->type == 'single') {
|
|
//assign quantity
|
|
$array['manage_stock'] = $manage_stock;
|
|
if (in_array('quantity', $woocommerce_api_settings->product_fields_for_update)) {
|
|
$array['stock_quantity'] = $this->formatDecimalPoint($qty_available, 'quantity');
|
|
} else {
|
|
//set manage stock and in_stock if quantity disabled
|
|
if (isset($woocommerce_api_settings->manage_stock_for_update)) {
|
|
if ($woocommerce_api_settings->manage_stock_for_update == 'true') {
|
|
$array['manage_stock'] = true;
|
|
} elseif ($woocommerce_api_settings->manage_stock_for_update == 'false') {
|
|
$array['manage_stock'] = false;
|
|
} else {
|
|
unset($array['manage_stock']);
|
|
}
|
|
}
|
|
if (isset($woocommerce_api_settings->in_stock_for_update)) {
|
|
if ($woocommerce_api_settings->in_stock_for_update == 'true') {
|
|
$array['in_stock'] = true;
|
|
} elseif ($woocommerce_api_settings->in_stock_for_update == 'false') {
|
|
$array['in_stock'] = false;
|
|
}
|
|
}
|
|
}
|
|
//assign price
|
|
if (in_array('price', $woocommerce_api_settings->product_fields_for_update)) {
|
|
$array['regular_price'] = $this->formatDecimalPoint($price);
|
|
}
|
|
}
|
|
|
|
//assign name
|
|
if (in_array('name', $woocommerce_api_settings->product_fields_for_update)) {
|
|
$array['name'] = $product->name;
|
|
}
|
|
|
|
$product_data['update'][] = $array;
|
|
$updated_data[] = $product->sku;
|
|
$updated_products[] = $product;
|
|
}
|
|
}
|
|
|
|
$create_response = [];
|
|
$update_response = [];
|
|
|
|
if (! empty($product_data['create'])) {
|
|
$create_response = $this->syncProd($business_id, $product_data['create'], 'create', $new_products);
|
|
}
|
|
if (! empty($product_data['update'])) {
|
|
$update_response = $this->syncProd($business_id, $product_data['update'], 'update', $updated_products);
|
|
}
|
|
$new_woocommerce_product_ids = array_merge($create_response, $update_response);
|
|
|
|
//Create log
|
|
if (! empty($created_data)) {
|
|
if ($sync_type == 'new') {
|
|
$this->createSyncLog($business_id, $user_id, 'new_products', 'created', $created_data);
|
|
} else {
|
|
$this->createSyncLog($business_id, $user_id, 'all_products', 'created', $created_data);
|
|
}
|
|
}
|
|
if (! empty($updated_data)) {
|
|
$this->createSyncLog($business_id, $user_id, 'all_products', 'updated', $updated_data);
|
|
}
|
|
|
|
//Sync variable product variations
|
|
$this->syncProductVariations($business_id, $sync_type, $new_woocommerce_product_ids);
|
|
|
|
if (empty($created_data) && empty($updated_data)) {
|
|
if ($sync_type == 'new') {
|
|
$this->createSyncLog($business_id, $user_id, 'new_products');
|
|
} else {
|
|
$this->createSyncLog($business_id, $user_id, 'all_products');
|
|
}
|
|
}
|
|
|
|
return $all_products;
|
|
}
|
|
|
|
public function syncProd($business_id, $data, $type, $new_products)
|
|
{
|
|
//woocommerce api client object
|
|
$woocommerce = $this->woo_client($business_id);
|
|
|
|
$new_woocommerce_product_ids = [];
|
|
$count = 0;
|
|
foreach (array_chunk($data, 99) as $chunked_array) {
|
|
$sync_data = [];
|
|
$sync_data[$type] = $chunked_array;
|
|
$response = $woocommerce->post('products/batch', $sync_data);
|
|
if (! empty($response->create)) {
|
|
foreach ($response->create as $key => $value) {
|
|
$new_product = $new_products[$count];
|
|
if ($value->id != 0) {
|
|
$new_product->woocommerce_product_id = $value->id;
|
|
//Sync woocommerce media id
|
|
$new_product->woocommerce_media_id = ! empty($value->images[0]->id) ? $value->images[0]->id : null;
|
|
} else {
|
|
if (! empty($value->error->data->resource_id)) {
|
|
$new_product->woocommerce_product_id = $value->error->data->resource_id;
|
|
}
|
|
}
|
|
$new_product->save();
|
|
|
|
$new_woocommerce_product_ids[] = $new_product->woocommerce_product_id;
|
|
$count++;
|
|
}
|
|
}
|
|
|
|
if (! empty($response->update)) {
|
|
foreach ($response->update as $key => $value) {
|
|
$updated_product = $new_products[$count];
|
|
if ($value->id != 0) {
|
|
//Sync woocommerce media id
|
|
$updated_product->woocommerce_media_id = ! empty($value->images[0]->id) ? $value->images[0]->id : null;
|
|
$updated_product->save();
|
|
}
|
|
$new_woocommerce_product_ids[] = $updated_product->woocommerce_product_id;
|
|
$count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $new_woocommerce_product_ids;
|
|
}
|
|
|
|
/**
|
|
* Synchronizes pos variation templates with Woocommerce product attributes
|
|
*
|
|
* @param int $business_id
|
|
* @return void
|
|
*/
|
|
public function syncVariationAttributes($business_id)
|
|
{
|
|
$woocommerce = $this->woo_client($business_id);
|
|
$query = VariationTemplate::where('business_id', $business_id);
|
|
|
|
$attributes = $query->get();
|
|
$data = [];
|
|
$new_attrs = [];
|
|
foreach ($attributes as $attr) {
|
|
if (empty($attr->woocommerce_attr_id)) {
|
|
$data['create'][] = ['name' => $attr->name];
|
|
$new_attrs[] = $attr;
|
|
} else {
|
|
$data['update'][] = [
|
|
'name' => $attr->name,
|
|
'id' => $attr->woocommerce_attr_id,
|
|
];
|
|
}
|
|
}
|
|
|
|
if (! empty($data)) {
|
|
$response = $woocommerce->post('products/attributes/batch', $data);
|
|
|
|
//update woocommerce_attr_id
|
|
if (! empty($response->create)) {
|
|
foreach ($response->create as $key => $value) {
|
|
$new_attr = $new_attrs[$key];
|
|
if ($value->id != 0) {
|
|
$new_attr->woocommerce_attr_id = $value->id;
|
|
} else {
|
|
$all_attrs = $woocommerce->get('products/attributes');
|
|
foreach ($all_attrs as $attr) {
|
|
if (strtolower($attr->name) == strtolower($new_attr->name)) {
|
|
$new_attr->woocommerce_attr_id = $attr->id;
|
|
}
|
|
}
|
|
}
|
|
$new_attr->save();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Synchronizes pos products variations with Woocommerce product variations
|
|
*
|
|
* @param int $business_id
|
|
* @param string $sync_type
|
|
* @param array $new_woocommerce_product_ids (woocommerce product id of newly created products to sync)
|
|
* @return void
|
|
*/
|
|
public function syncProductVariations($business_id, $sync_type = 'all', $new_woocommerce_product_ids = [])
|
|
{
|
|
//woocommerce api client object
|
|
$woocommerce = $this->woo_client($business_id);
|
|
$woocommerce_api_settings = $this->get_api_settings($business_id);
|
|
|
|
$query = Product::where('business_id', $business_id)
|
|
->where('type', 'variable')
|
|
->where('woocommerce_disable_sync', 0)
|
|
->with(['variations',
|
|
'variations.variation_location_details',
|
|
'variations.product_variation',
|
|
'variations.product_variation.variation_template', ]);
|
|
|
|
$query->whereIn('woocommerce_product_id', $new_woocommerce_product_ids);
|
|
|
|
$variable_products = $query->get();
|
|
$business_location_id = $woocommerce_api_settings->location_id;
|
|
foreach ($variable_products as $product) {
|
|
|
|
//Skip product if last updated is less than last sync
|
|
$last_updated = $product->updated_at;
|
|
|
|
$last_stock_updated = $this->getLastStockUpdated($business_location_id, $product->id);
|
|
|
|
if (! empty($last_stock_updated)) {
|
|
$last_updated = strtotime($last_stock_updated) > strtotime($last_updated) ?
|
|
$last_stock_updated : $last_updated;
|
|
}
|
|
if (! empty($last_synced) && strtotime($last_updated) < strtotime($last_synced)) {
|
|
continue;
|
|
}
|
|
|
|
$variations = $product->variations;
|
|
|
|
$variation_data = [];
|
|
$new_variations = [];
|
|
$updated_variations = [];
|
|
foreach ($variations as $variation) {
|
|
$variation_arr = [
|
|
'sku' => $variation->sub_sku,
|
|
];
|
|
|
|
$manage_stock = false;
|
|
if ($product->enable_stock == 1) {
|
|
$manage_stock = true;
|
|
}
|
|
|
|
if (! empty($variation->product_variation->variation_template->woocommerce_attr_id)) {
|
|
$variation_arr['attributes'][] = [
|
|
'id' => $variation->product_variation->variation_template->woocommerce_attr_id,
|
|
'option' => $variation->name,
|
|
];
|
|
}
|
|
|
|
$price = $woocommerce_api_settings->product_tax_type == 'exc' ? $variation->default_sell_price : $variation->sell_price_inc_tax;
|
|
|
|
if (! empty($woocommerce_api_settings->default_selling_price_group)) {
|
|
$group_prices = $this->productUtil->getVariationGroupPrice($variation->id, $woocommerce_api_settings->default_selling_price_group, $product->tax_id);
|
|
|
|
$price = $woocommerce_api_settings->product_tax_type == 'exc' ? $group_prices['price_exc_tax'] : $group_prices['price_inc_tax'];
|
|
}
|
|
|
|
//Set product stock
|
|
$qty_available = 0;
|
|
if ($product->enable_stock == 1) {
|
|
$variation_location_details = $variation->variation_location_details;
|
|
foreach ($variation_location_details as $vld) {
|
|
if ($vld->location_id == $business_location_id) {
|
|
$qty_available = $vld->qty_available;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (empty($variation->woocommerce_variation_id)) {
|
|
$variation_arr['manage_stock'] = $manage_stock;
|
|
if (in_array('quantity', $woocommerce_api_settings->product_fields_for_create)) {
|
|
$variation_arr['stock_quantity'] = $this->formatDecimalPoint($qty_available, 'quantity');
|
|
} else {
|
|
//set manage stock and in_stock if quantity disabled
|
|
if (isset($woocommerce_api_settings->manage_stock_for_create)) {
|
|
if ($woocommerce_api_settings->manage_stock_for_create == 'true') {
|
|
$variation_arr['manage_stock'] = true;
|
|
} elseif ($woocommerce_api_settings->manage_stock_for_create == 'false') {
|
|
$variation_arr['manage_stock'] = false;
|
|
} else {
|
|
unset($variation_arr['manage_stock']);
|
|
}
|
|
}
|
|
if (isset($woocommerce_api_settings->in_stock_for_create)) {
|
|
if ($woocommerce_api_settings->in_stock_for_create == 'true') {
|
|
$variation_arr['in_stock'] = true;
|
|
} elseif ($woocommerce_api_settings->in_stock_for_create == 'false') {
|
|
$variation_arr['in_stock'] = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
//Set variation images
|
|
//If media id is set use media id else use image src
|
|
if (! empty($variation->media) && count($variation->media) > 0 && in_array('image', $woocommerce_api_settings->product_fields_for_create)) {
|
|
$url = $variation->media->first()->display_url;
|
|
$path = $variation->media->first()->display_path;
|
|
$woocommerce_media_id = $variation->media->first()->woocommerce_media_id;
|
|
if ($this->isValidImage($path)) {
|
|
$variation_arr['image'] = ! empty($woocommerce_media_id) ? ['id' => $woocommerce_media_id] : ['src' => $url];
|
|
}
|
|
}
|
|
|
|
$variation_arr['regular_price'] = $this->formatDecimalPoint($price);
|
|
$new_variations[] = $variation;
|
|
|
|
$variation_data['create'][] = $variation_arr;
|
|
} else {
|
|
$variation_arr['id'] = $variation->woocommerce_variation_id;
|
|
$variation_arr['manage_stock'] = $manage_stock;
|
|
if (in_array('quantity', $woocommerce_api_settings->product_fields_for_update)) {
|
|
$variation_arr['stock_quantity'] = $this->formatDecimalPoint($qty_available, 'quantity');
|
|
} else {
|
|
//set manage stock and in_stock if quantity disabled
|
|
if (isset($woocommerce_api_settings->manage_stock_for_update)) {
|
|
if ($woocommerce_api_settings->manage_stock_for_update == 'true') {
|
|
$variation_arr['manage_stock'] = true;
|
|
} elseif ($woocommerce_api_settings->manage_stock_for_update == 'false') {
|
|
$variation_arr['manage_stock'] = false;
|
|
} else {
|
|
unset($variation_arr['manage_stock']);
|
|
}
|
|
}
|
|
if (isset($woocommerce_api_settings->in_stock_for_update)) {
|
|
if ($woocommerce_api_settings->in_stock_for_update == 'true') {
|
|
$variation_arr['in_stock'] = true;
|
|
} elseif ($woocommerce_api_settings->in_stock_for_update == 'false') {
|
|
$variation_arr['in_stock'] = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
//Set variation images
|
|
//If media id is set use media id else use image src
|
|
if (! empty($variation->media) && count($variation->media) > 0 && in_array('image', $woocommerce_api_settings->product_fields_for_update)) {
|
|
$url = $variation->media->first()->display_url;
|
|
$path = $variation->media->first()->display_path;
|
|
$woocommerce_media_id = $variation->media->first()->woocommerce_media_id;
|
|
if ($this->isValidImage($path)) {
|
|
$variation_arr['image'] = ! empty($woocommerce_media_id) ? ['id' => $woocommerce_media_id] : ['src' => $url];
|
|
}
|
|
}
|
|
|
|
//assign price
|
|
if (in_array('price', $woocommerce_api_settings->product_fields_for_update)) {
|
|
$variation_arr['regular_price'] = $this->formatDecimalPoint($price);
|
|
}
|
|
|
|
$variation_data['update'][] = $variation_arr;
|
|
$updated_variations[] = $variation;
|
|
}
|
|
}
|
|
|
|
if (! empty($variation_data)) {
|
|
$response = $woocommerce->post('products/'.$product->woocommerce_product_id.'/variations/batch', $variation_data);
|
|
|
|
//update woocommerce_variation_id
|
|
if (! empty($response->create)) {
|
|
foreach ($response->create as $key => $value) {
|
|
$new_variation = $new_variations[$key];
|
|
if ($value->id != 0) {
|
|
$new_variation->woocommerce_variation_id = $value->id;
|
|
$media = $new_variation->media->first();
|
|
if (! empty($media)) {
|
|
$media->woocommerce_media_id = ! empty($value->image->id) ? $value->image->id : null;
|
|
$media->save();
|
|
}
|
|
} else {
|
|
if (! empty($value->error->data->resource_id)) {
|
|
$new_variation->woocommerce_variation_id = $value->error->data->resource_id;
|
|
}
|
|
}
|
|
$new_variation->save();
|
|
}
|
|
}
|
|
|
|
//Update media id if changed from woocommerce site
|
|
if (! empty($response->update)) {
|
|
foreach ($response->update as $key => $value) {
|
|
$updated_variation = $updated_variations[$key];
|
|
if ($value->id != 0) {
|
|
$media = $updated_variation->media->first();
|
|
if (! empty($media)) {
|
|
$media->woocommerce_media_id = ! empty($value->image->id) ? $value->image->id : null;
|
|
$media->save();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Synchronizes Woocommers Orders with POS sales
|
|
*
|
|
* @param int $business_id
|
|
* @param int $user_id
|
|
* @return void
|
|
*/
|
|
public function syncOrders($business_id, $user_id)
|
|
{
|
|
$last_synced = $this->getLastSync($business_id, 'orders', false);
|
|
$orders = $this->getAllResponse($business_id, 'orders');
|
|
|
|
$woocommerce_sells = Transaction::where('business_id', $business_id)
|
|
->whereNotNull('woocommerce_order_id')
|
|
->with('sell_lines', 'sell_lines.product', 'payment_lines')
|
|
->get();
|
|
|
|
$new_orders = [];
|
|
$updated_orders = [];
|
|
|
|
$woocommerce_api_settings = $this->get_api_settings($business_id);
|
|
$business = Business::find($business_id);
|
|
|
|
$skipped_orders = ! empty($business->woocommerce_skipped_orders) ? json_decode($business->woocommerce_skipped_orders, true) : [];
|
|
|
|
$business_data = [
|
|
'id' => $business_id,
|
|
'accounting_method' => $business->accounting_method,
|
|
'location_id' => $woocommerce_api_settings->location_id,
|
|
'pos_settings' => json_decode($business->pos_settings, true),
|
|
'business' => $business,
|
|
];
|
|
|
|
$created_data = [];
|
|
$updated_data = [];
|
|
$create_error_data = [];
|
|
$update_error_data = [];
|
|
|
|
foreach ($orders as $order) {
|
|
//Only consider orders modified after last sync
|
|
if ((! empty($last_synced) && strtotime($order->date_modified) <= strtotime($last_synced) && ! in_array($order->id, $skipped_orders)) || in_array($order->status, ['auto-draft'])) {
|
|
continue;
|
|
}
|
|
//Search if order already exists
|
|
$sell = $woocommerce_sells->filter(function ($item) use ($order) {
|
|
return $item->woocommerce_order_id == $order->id;
|
|
})->first();
|
|
|
|
$order_number = $order->number;
|
|
$sell_status = $this->woocommerceOrderStatusToPosSellStatus($order->status, $business_id);
|
|
|
|
if ($sell_status == 'draft') {
|
|
$order_number .= ' ('.__('sale.draft').')';
|
|
}
|
|
if (empty($sell)) {
|
|
$created = $this->createNewSaleFromOrder($business_id, $user_id, $order, $business_data);
|
|
$created_data[] = $order_number;
|
|
|
|
if ($created !== true) {
|
|
$create_error_data[] = $created;
|
|
}
|
|
} else {
|
|
$updated = $this->updateSaleFromOrder($business_id, $user_id, $order, $sell, $business_data);
|
|
$updated_data[] = $order_number;
|
|
|
|
if ($updated !== true) {
|
|
$update_error_data[] = $updated;
|
|
}
|
|
}
|
|
}
|
|
|
|
//Create log
|
|
if (! empty($created_data)) {
|
|
$this->createSyncLog($business_id, $user_id, 'orders', 'created', $created_data, $create_error_data);
|
|
}
|
|
if (! empty($updated_data)) {
|
|
$this->createSyncLog($business_id, $user_id, 'orders', 'updated', $updated_data, $update_error_data);
|
|
}
|
|
|
|
if (empty($created_data) && empty($updated_data)) {
|
|
$error_data = $create_error_data + $update_error_data;
|
|
$this->createSyncLog($business_id, $user_id, 'orders', null, [], $error_data);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates new sales in POSfrom woocommerce order list
|
|
*
|
|
* @param id $business_id
|
|
* @param id $user_id
|
|
* @param obj $order
|
|
* @param array $business_data
|
|
*/
|
|
public function createNewSaleFromOrder($business_id, $user_id, $order, $business_data)
|
|
{
|
|
$input = $this->formatOrderToSale($business_id, $user_id, $order);
|
|
|
|
if (! empty($input['has_error'])) {
|
|
return $input['has_error'];
|
|
}
|
|
|
|
$invoice_total = [
|
|
'total_before_tax' => $order->total,
|
|
'tax' => 0,
|
|
];
|
|
|
|
DB::beginTransaction();
|
|
|
|
$transaction = $this->transactionUtil->createSellTransaction($business_id, $input, $invoice_total, $user_id, false);
|
|
$transaction->woocommerce_order_id = $order->id;
|
|
$transaction->save();
|
|
|
|
//Create sell lines
|
|
$this->transactionUtil->createOrUpdateSellLines($transaction, $input['products'], $input['location_id'], false, null, ['woocommerce_line_items_id' => 'line_item_id'], false);
|
|
|
|
$this->transactionUtil->createOrUpdatePaymentLines($transaction, $input['payment'], $business_id, $user_id, false);
|
|
|
|
if ($input['status'] == 'final') {
|
|
//update product stock
|
|
foreach ($input['products'] as $product) {
|
|
if ($product['enable_stock']) {
|
|
$this->productUtil->decreaseProductQuantity(
|
|
$product['product_id'],
|
|
$product['variation_id'],
|
|
$input['location_id'],
|
|
$product['quantity']
|
|
);
|
|
}
|
|
}
|
|
|
|
//Update payment status
|
|
$transaction->payment_status = 'paid';
|
|
$transaction->save();
|
|
|
|
try {
|
|
$this->transactionUtil->mapPurchaseSell($business_data, $transaction->sell_lines, 'purchase');
|
|
} catch (PurchaseSellMismatch $e) {
|
|
DB::rollBack();
|
|
|
|
$this->add_to_skipped_orders($business_data['business'], $order->id);
|
|
|
|
return [
|
|
'error_type' => 'order_insuficient_product_qty',
|
|
'order_number' => $order->number,
|
|
'msg' => $e->getMessage(),
|
|
];
|
|
}
|
|
}
|
|
|
|
$this->remove_from_skipped_orders($business_data['business'], $order->id);
|
|
|
|
DB::commit();
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Formats Woocommerce order response to pos sale request
|
|
*
|
|
* @param id $business_id
|
|
* @param id $user_id
|
|
* @param obj $order
|
|
* @param obj $sell = null
|
|
*/
|
|
public function formatOrderToSale($business_id, $user_id, $order, $sell = null)
|
|
{
|
|
$woocommerce_api_settings = $this->get_api_settings($business_id);
|
|
|
|
//Create sell line data
|
|
$product_lines = [];
|
|
|
|
//For updating sell lines
|
|
$sell_lines = [];
|
|
if (! empty($sell)) {
|
|
$sell_lines = $sell->sell_lines;
|
|
}
|
|
|
|
foreach ($order->line_items as $product_line) {
|
|
$product = Product::where('business_id', $business_id)
|
|
->where('woocommerce_product_id', $product_line->product_id)
|
|
->with(['variations'])
|
|
->first();
|
|
|
|
$unit_price = $product_line->total / $product_line->quantity;
|
|
$line_tax = ! empty($product_line->total_tax) ? $product_line->total_tax : 0;
|
|
$unit_line_tax = $line_tax / $product_line->quantity;
|
|
$unit_price_inc_tax = $unit_price + $unit_line_tax;
|
|
if (! empty($product)) {
|
|
|
|
//Set sale line variation;If single product then first variation
|
|
//else search for woocommerce_variation_id in all the variations
|
|
if ($product->type == 'single') {
|
|
$variation = $product->variations->first();
|
|
} else {
|
|
foreach ($product->variations as $v) {
|
|
if ($v->woocommerce_variation_id == $product_line->variation_id) {
|
|
$variation = $v;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (empty($variation)) {
|
|
return ['has_error' => [
|
|
'error_type' => 'order_product_not_found',
|
|
'order_number' => $order->number,
|
|
'product' => $product_line->name.' SKU:'.$product_line->sku,
|
|
],
|
|
];
|
|
exit;
|
|
}
|
|
|
|
//Check if line tax exists append to sale line data
|
|
$tax_id = null;
|
|
if (! empty($product_line->taxes)) {
|
|
foreach ($product_line->taxes as $tax) {
|
|
$pos_tax = TaxRate::where('business_id', $business_id)
|
|
->where('woocommerce_tax_rate_id', $tax->id)
|
|
->first();
|
|
|
|
if (! empty($pos_tax)) {
|
|
$tax_id = $pos_tax->id;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
$product_data = [
|
|
'product_id' => $product->id,
|
|
'unit_price' => $unit_price,
|
|
'unit_price_inc_tax' => $unit_price_inc_tax,
|
|
'variation_id' => $variation->id,
|
|
'quantity' => $product_line->quantity,
|
|
'enable_stock' => $product->enable_stock,
|
|
'item_tax' => $line_tax,
|
|
'tax_id' => $tax_id,
|
|
'line_item_id' => $product_line->id,
|
|
];
|
|
|
|
//append transaction_sell_lines_id if update
|
|
if (! empty($sell_lines)) {
|
|
foreach ($sell_lines as $sell_line) {
|
|
if ($sell_line->woocommerce_line_items_id ==
|
|
$product_line->id) {
|
|
$product_data['transaction_sell_lines_id'] = $sell_line->id;
|
|
}
|
|
}
|
|
}
|
|
|
|
$product_lines[] = $product_data;
|
|
} else {
|
|
return ['has_error' => [
|
|
'error_type' => 'order_product_not_found',
|
|
'order_number' => $order->number,
|
|
'product' => $product_line->name.' SKU:'.$product_line->sku,
|
|
],
|
|
];
|
|
exit;
|
|
}
|
|
}
|
|
|
|
//Get customer details
|
|
$order_customer_id = $order->customer_id;
|
|
|
|
$customer_details = [];
|
|
|
|
//If Customer empty skip get guest customer details from billing address
|
|
if (empty($order_customer_id)) {
|
|
$f_name = ! empty($order->billing->first_name) ? $order->billing->first_name : '';
|
|
$l_name = ! empty($order->billing->last_name) ? $order->billing->last_name : '';
|
|
$customer_details = [
|
|
'first_name' => $f_name,
|
|
'last_name' => $l_name,
|
|
'email' => ! empty($order->billing->email) ? $order->billing->email : null,
|
|
'name' => $f_name.' '.$l_name,
|
|
'mobile' => $order->billing->phone,
|
|
'address_line_1' => ! empty($order->billing->address_1) ? $order->billing->address_1 : null,
|
|
'address_line_2' => ! empty($order->billing->address_2) ? $order->billing->address_2 : null,
|
|
'city' => ! empty($order->billing->city) ? $order->billing->city : null,
|
|
'state' => ! empty($order->billing->state) ? $order->billing->state : null,
|
|
'country' => ! empty($order->billing->country) ? $order->billing->country : null,
|
|
'zip_code' => ! empty($order->billing->postcode) ? $order->billing->postcode : null,
|
|
];
|
|
} else {
|
|
//woocommerce api client object
|
|
$woocommerce = $this->woo_client($business_id);
|
|
$order_customer = $woocommerce->get('customers/'.$order_customer_id);
|
|
|
|
$customer_details = [
|
|
'first_name' => $order_customer->first_name,
|
|
'last_name' => $order_customer->last_name,
|
|
'email' => $order_customer->email,
|
|
'name' => $order_customer->first_name.' '.$order_customer->last_name,
|
|
'mobile' => $order_customer->billing->phone,
|
|
'city' => $order_customer->billing->city,
|
|
'state' => $order_customer->billing->state,
|
|
'country' => $order_customer->billing->country,
|
|
'address_line_1' => $order_customer->billing->address_1,
|
|
'address_line_2' => $order_customer->billing->address_2,
|
|
'zip_code' => $order_customer->billing->postcode,
|
|
];
|
|
}
|
|
|
|
if (! empty($customer_details['email'])) {
|
|
$customer = Contact::where('business_id', $business_id)
|
|
->where('email', $customer_details['email'])
|
|
->OnlyCustomers()
|
|
->first();
|
|
}
|
|
|
|
if (empty($order_customer_id) && empty($customer_details['email'])) {
|
|
$contactUtil = new ContactUtil;
|
|
$customer = $contactUtil->getWalkInCustomer($business_id, false);
|
|
}
|
|
|
|
//If customer not found create new
|
|
if (empty($customer)) {
|
|
$ref_count = $this->transactionUtil->setAndGetReferenceCount('contacts', $business_id);
|
|
$contact_id = $this->transactionUtil->generateReferenceNumber('contacts', $ref_count, $business_id);
|
|
|
|
$customer_data = [
|
|
'business_id' => $business_id,
|
|
'type' => 'customer',
|
|
'first_name' => $customer_details['first_name'],
|
|
'last_name' => $customer_details['last_name'],
|
|
'name' => $customer_details['name'],
|
|
'email' => $customer_details['email'],
|
|
'contact_id' => $contact_id,
|
|
'mobile' => $customer_details['mobile'],
|
|
'city' => $customer_details['city'],
|
|
'state' => $customer_details['state'],
|
|
'country' => $customer_details['country'],
|
|
'created_by' => $user_id,
|
|
'address_line_1' => $customer_details['address_line_1'],
|
|
'address_line_2' => $customer_details['address_line_2'],
|
|
'zip_code' => $customer_details['zip_code'],
|
|
];
|
|
|
|
//if name is blank make email address as name
|
|
if (empty(trim($customer_data['name']))) {
|
|
$customer_data['first_name'] = $customer_details['email'];
|
|
$customer_data['name'] = $customer_details['email'];
|
|
}
|
|
$customer = Contact::create($customer_data);
|
|
}
|
|
|
|
$sell_status = $this->woocommerceOrderStatusToPosSellStatus($order->status, $business_id);
|
|
$shipping_status = $this->woocommerceOrderStatusToPosShippingStatus($order->status, $business_id);
|
|
$shipping_address = [];
|
|
if (! empty($order->shipping->first_name)) {
|
|
$shipping_address[] = $order->shipping->first_name.' '.$order->shipping->last_name;
|
|
}
|
|
if (! empty($order->shipping->company)) {
|
|
$shipping_address[] = $order->shipping->company;
|
|
}
|
|
if (! empty($order->shipping->address_1)) {
|
|
$shipping_address[] = $order->shipping->address_1;
|
|
}
|
|
if (! empty($order->shipping->address_2)) {
|
|
$shipping_address[] = $order->shipping->address_2;
|
|
}
|
|
if (! empty($order->shipping->city)) {
|
|
$shipping_address[] = $order->shipping->city;
|
|
}
|
|
if (! empty($order->shipping->state)) {
|
|
$shipping_address[] = $order->shipping->state;
|
|
}
|
|
if (! empty($order->shipping->country)) {
|
|
$shipping_address[] = $order->shipping->country;
|
|
}
|
|
if (! empty($order->shipping->postcode)) {
|
|
$shipping_address[] = $order->shipping->postcode;
|
|
}
|
|
$addresses['shipping_address'] = [
|
|
'shipping_name' => $order->shipping->first_name.' '.$order->shipping->last_name,
|
|
'company' => $order->shipping->company,
|
|
'shipping_address_line_1' => $order->shipping->address_1,
|
|
'shipping_address_line_2' => $order->shipping->address_2,
|
|
'shipping_city' => $order->shipping->city,
|
|
'shipping_state' => $order->shipping->state,
|
|
'shipping_country' => $order->shipping->country,
|
|
'shipping_zip_code' => $order->shipping->postcode,
|
|
];
|
|
$addresses['billing_address'] = [
|
|
'billing_name' => $order->billing->first_name.' '.$order->billing->last_name,
|
|
'company' => $order->billing->company,
|
|
'billing_address_line_1' => $order->billing->address_1,
|
|
'billing_address_line_2' => $order->billing->address_2,
|
|
'billing_city' => $order->billing->city,
|
|
'billing_state' => $order->billing->state,
|
|
'billing_country' => $order->billing->country,
|
|
'billing_zip_code' => $order->billing->postcode,
|
|
];
|
|
|
|
$shipping_lines_array = [];
|
|
if (! empty($order->shipping_lines)) {
|
|
foreach ($order->shipping_lines as $shipping_lines) {
|
|
$shipping_lines_array[] = $shipping_lines->method_title;
|
|
}
|
|
}
|
|
|
|
$new_sell_data = [
|
|
'business_id' => $business_id,
|
|
'location_id' => $woocommerce_api_settings->location_id,
|
|
'contact_id' => $customer->id,
|
|
'discount_type' => 'fixed',
|
|
'discount_amount' => $order->discount_total,
|
|
'shipping_charges' => $order->shipping_total,
|
|
'final_total' => $order->total,
|
|
'created_by' => $user_id,
|
|
'status' => $sell_status == 'quotation' ? 'draft' : $sell_status,
|
|
'is_quotation' => $sell_status == 'quotation' ? 1 : 0,
|
|
'sub_status' => $sell_status == 'quotation' ? 'quotation' : null,
|
|
'payment_status' => 'paid',
|
|
'additional_notes' => '',
|
|
'transaction_date' => $order->date_created,
|
|
'customer_group_id' => $customer->customer_group_id,
|
|
'tax_rate_id' => null,
|
|
'sale_note' => null,
|
|
'commission_agent' => null,
|
|
'invoice_no' => $order->number,
|
|
'order_addresses' => json_encode($addresses),
|
|
'shipping_charges' => ! empty($order->shipping_total) ? $order->shipping_total : 0,
|
|
'shipping_details' => ! empty($shipping_lines_array) ? implode(', ', $shipping_lines_array) : '',
|
|
'shipping_status' => $shipping_status,
|
|
'shipping_address' => implode(', ', $shipping_address),
|
|
];
|
|
|
|
$payment = [
|
|
'amount' => $order->total,
|
|
'method' => 'cash',
|
|
'card_transaction_number' => '',
|
|
'card_number' => '',
|
|
'card_type' => '',
|
|
'card_holder_name' => '',
|
|
'card_month' => '',
|
|
'card_security' => '',
|
|
'cheque_number' => '',
|
|
'bank_account_number' => '',
|
|
'note' => $order->payment_method_title,
|
|
'paid_on' => $order->date_paid,
|
|
];
|
|
|
|
if (! empty($sell) && count($sell->payment_lines) > 0) {
|
|
$payment['payment_id'] = $sell->payment_lines->first()->id;
|
|
}
|
|
|
|
$new_sell_data['products'] = $product_lines;
|
|
$new_sell_data['payment'] = [$payment];
|
|
|
|
return $new_sell_data;
|
|
}
|
|
|
|
/**
|
|
* Updates existing sale
|
|
*
|
|
* @param id $business_id
|
|
* @param id $user_id
|
|
* @param obj $order
|
|
* @param obj $sell
|
|
* @param array $business_data
|
|
*/
|
|
public function updateSaleFromOrder($business_id, $user_id, $order, $sell, $business_data)
|
|
{
|
|
$input = $this->formatOrderToSale($business_id, $user_id, $order, $sell);
|
|
|
|
if (! empty($input['has_error'])) {
|
|
return $input['has_error'];
|
|
}
|
|
|
|
$invoice_total = [
|
|
'total_before_tax' => $order->total,
|
|
'tax' => 0,
|
|
];
|
|
|
|
$status_before = $sell->status;
|
|
|
|
DB::beginTransaction();
|
|
$transaction = $this->transactionUtil->updateSellTransaction($sell, $business_id, $input, $invoice_total, $user_id, false, false);
|
|
|
|
//Update Sell lines
|
|
$deleted_lines = $this->transactionUtil->createOrUpdateSellLines($transaction, $input['products'], $input['location_id'], true, $status_before, [], false);
|
|
|
|
$this->transactionUtil->createOrUpdatePaymentLines($transaction, $input['payment'], null, null, false);
|
|
|
|
//Update payment status
|
|
$transaction->payment_status = 'paid';
|
|
$transaction->save();
|
|
|
|
//Update product stock
|
|
$this->productUtil->adjustProductStockForInvoice($status_before, $transaction, $input, false);
|
|
|
|
try {
|
|
$this->transactionUtil->adjustMappingPurchaseSell($status_before, $transaction, $business_data, $deleted_lines);
|
|
} catch (PurchaseSellMismatch $e) {
|
|
DB::rollBack();
|
|
|
|
return [
|
|
'error_type' => 'order_insuficient_product_qty',
|
|
'order_number' => $order->number,
|
|
'msg' => $e->getMessage(),
|
|
];
|
|
}
|
|
|
|
DB::commit();
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Creates sync log in the database
|
|
*
|
|
* @param id $business_id
|
|
* @param id $user_id
|
|
* @param string $type
|
|
* @param array $errors = null
|
|
*/
|
|
public function createSyncLog($business_id, $user_id, $type, $operation = null, $data = [], $errors = null)
|
|
{
|
|
WoocommerceSyncLog::create([
|
|
'business_id' => $business_id,
|
|
'sync_type' => $type,
|
|
'created_by' => $user_id,
|
|
'operation_type' => $operation,
|
|
'data' => ! empty($data) ? json_encode($data) : null,
|
|
'details' => ! empty($errors) ? json_encode($errors) : null,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Retrives last synced date from the database
|
|
*
|
|
* @param id $business_id
|
|
* @param string $type
|
|
* @param bool $for_humans = true
|
|
*/
|
|
public function getLastSync($business_id, $type, $for_humans = true)
|
|
{
|
|
$last_sync = WoocommerceSyncLog::where('business_id', $business_id)
|
|
->where('sync_type', $type)
|
|
->max('created_at');
|
|
|
|
//If last reset present make last sync to null
|
|
$last_reset = WoocommerceSyncLog::where('business_id', $business_id)
|
|
->where('sync_type', $type)
|
|
->where('operation_type', 'reset')
|
|
->max('created_at');
|
|
if (! empty($last_reset) && ! empty($last_sync) && $last_reset >= $last_sync) {
|
|
$last_sync = null;
|
|
}
|
|
|
|
if (! empty($last_sync) && $for_humans) {
|
|
$last_sync = format_relative_time($last_sync);
|
|
}
|
|
|
|
return $last_sync;
|
|
}
|
|
|
|
public function woocommerceOrderStatusToPosSellStatus($status, $business_id)
|
|
{
|
|
$default_status_array = [
|
|
'pending' => 'draft',
|
|
'processing' => 'final',
|
|
'on-hold' => 'draft',
|
|
'completed' => 'final',
|
|
'cancelled' => 'draft',
|
|
'refunded' => 'draft',
|
|
'failed' => 'draft',
|
|
'shipped' => 'final',
|
|
];
|
|
|
|
$api_settings = $this->get_api_settings($business_id);
|
|
|
|
$status_settings = $api_settings->order_statuses ?? null;
|
|
|
|
$sale_status = ! empty($status_settings) ? $status_settings->$status : null;
|
|
$sale_status = empty($sale_status) && array_key_exists($status, $default_status_array) ? $default_status_array[$status] : $sale_status;
|
|
$sale_status = empty($sale_status) ? 'final' : $sale_status;
|
|
|
|
return $sale_status;
|
|
}
|
|
|
|
public function woocommerceOrderStatusToPosShippingStatus($status, $business_id)
|
|
{
|
|
$api_settings = $this->get_api_settings($business_id);
|
|
|
|
$status_settings = $api_settings->shipping_statuses ?? null;
|
|
|
|
$shipping_status = ! empty($status_settings) ? $status_settings->$status : null;
|
|
|
|
return $shipping_status;
|
|
}
|
|
|
|
/**
|
|
* Splits response to list of 100 and merges all
|
|
*
|
|
* @param int $business_id
|
|
* @param string $endpoint
|
|
* @param array $params = []
|
|
* @return array
|
|
*/
|
|
public function getAllResponse($business_id, $endpoint, $params = [])
|
|
{
|
|
|
|
//woocommerce api client object
|
|
$woocommerce = $this->woo_client($business_id);
|
|
|
|
$page = 1;
|
|
$list = [];
|
|
$all_list = [];
|
|
$params['per_page'] = 100;
|
|
|
|
do {
|
|
$params['page'] = $page;
|
|
try {
|
|
$list = $woocommerce->get($endpoint, $params);
|
|
} catch (\Exception $e) {
|
|
return [];
|
|
}
|
|
$all_list = array_merge($all_list, $list);
|
|
$page++;
|
|
} while (count($list) > 0);
|
|
|
|
return $all_list;
|
|
}
|
|
|
|
/**
|
|
* Retrives all tax rates from woocommerce api
|
|
*
|
|
* @param id $business_id
|
|
* @param obj $tax_rates
|
|
*/
|
|
public function getTaxRates($business_id)
|
|
{
|
|
$tax_rates = $this->getAllResponse($business_id, 'taxes');
|
|
|
|
return $tax_rates;
|
|
}
|
|
|
|
public function getLastStockUpdated($location_id, $product_id)
|
|
{
|
|
$last_updated = VariationLocationDetails::where('location_id', $location_id)
|
|
->where('product_id', $product_id)
|
|
->max('updated_at');
|
|
|
|
return $last_updated;
|
|
}
|
|
|
|
private function formatDecimalPoint($number, $type = 'currency')
|
|
{
|
|
$precision = 4;
|
|
$currency_precision = session('business.currency_precision', 2);
|
|
$quantity_precision = session('business.quantity_precision', 2);
|
|
|
|
if ($type == 'currency' && ! empty($currency_precision)) {
|
|
$precision = $currency_precision;
|
|
}
|
|
if ($type == 'quantity' && ! empty($quantity_precision)) {
|
|
$precision = $quantity_precision;
|
|
}
|
|
|
|
return number_format((float) $number, $precision, '.', '');
|
|
}
|
|
|
|
public function isValidImage($path)
|
|
{
|
|
$valid_extenstions = ['jpg', 'jpeg', 'png', 'gif'];
|
|
|
|
return ! empty($path) && file_exists($path) && in_array(strtolower(pathinfo($path, PATHINFO_EXTENSION)), $valid_extenstions);
|
|
}
|
|
}
|