File: /var/www/vhost/disk-apps/qas.sports-crowd.com/app/Http/Controllers/ShopifyController.php
<?php
namespace App\Http\Controllers;
use App\Erp;
use App\Core\Membership\Application\MembershipService;
use App\Core\Parameter\Application\ParameterService;
use App\Core\Payment\Application\PaymentTransactionService;
use App\Core\Tag\Application\TagService;
use Exception;
use App\ErpLog;
use App\Product;
use App\Parameter;
use App\SystemLog;
use Carbon\Carbon;
use App\ErpParameter;
use App\ProductAttribute;
use App\CorporateIdentity;
use Slince\Shopify\Client;
use App\IntegrationProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use PHPMailer\PHPMailer\PHPMailer;
use Illuminate\Support\Facades\Auth;
use App\Repositories\ERPLogRepository;
use App\Http\Controllers\UserController;
use Slince\Shopify\PrivateAppCredential;
use App\Http\Controllers\OrderController;
use App\Http\Controllers\BrandsController;
use App\Http\Controllers\AddressController;
use App\Http\Controllers\ProductController;
use App\Http\Controllers\CategoryController;
use App\Http\Controllers\AttributeController;
use App\ShopifyLog;
class ShopifyController extends Controller implements ERPLogRepository
{
private $validSync = false;
private $parameters;
private $controllerOrder;
private $utilController;
private $inventoryController;
private $paymentTransactionService;
private $shopifyProducts;
private $parameterService;
public function __construct($ERPRepository = null)
{
$this->utilController = new UtilController;
$this->initApiRest();
$this->controllerOrder = new OrderController;
$this->inventoryController = new InventoryController;
$this->paymentTransactionService = new PaymentTransactionService();
$this->parameterService = new ParameterService();
$this->shopifyProducts = [];
}
//Mostrar tienda embebida en un iframe
public function showExternalShop()
{
$collections = $this->httpRequest('GET', 'custom_collections.json', 'COP')['custom_collections'];
$corporateIdentity = CorporateIdentity::first();
return view('shopify.collections', compact('collections', 'corporateIdentity'));
}
public function showExternalShopProducts($collectionId)
{
return view('shopify.shop', compact('collectionId'));
}
public function initApiRest()
{
$this->parameters = Parameter::select('sync_shopify', 'sucursal_products')->find(1);
if ($this->parameters && $this->parameters->sync_shopify) {
$this->validSync = true;
}
}
public function getIntegrationProvider($currency)
{
return IntegrationProvider::where([['ecommerce_type_id', 3], ['active', true], ['currency', $currency]])->first();
}
// produtcs
public function createProduct($data, $currency)
{
try {
if ($this->validSync) {
return $this->httpRequest('POST', "products.json", $currency, $data)['product'];
}
return null;
} catch (Exception $e) {
$error = [
'data' => $data,
'currency' => $currency,
'message' => $e->getMessage(),
'getFile' => $e->getFile(),
'getLine' => $e->getLine(),
];
$this->utilController->logFile(json_encode($error));
return null;
}
}
public function updateProduct($id, $data, $currency)
{
try {
if ($this->validSync && $id && $id != "") {
$this->httpRequest('PUT', "products/" . $id . ".json", $currency, $data);
}
} catch (Exception $e) {
$error = [
'id' => $id,
'data' => $data,
'currency' => $currency,
'message' => $e->getMessage(),
'getFile' => $e->getFile(),
'getLine' => $e->getLine(),
];
$this->utilController->logFile(json_encode($error));
return null;
}
}
public function addImageProduct($id, $data, $currency)
{
try {
if ($this->validSync && $id && $id != "") {
$integrationProvider = $this->getIntegrationProvider($currency);
$credential = new PrivateAppCredential(
$integrationProvider->key_public,
$integrationProvider->password,
$integrationProvider->key_private
);
$shopify = new Client(
$credential,
$integrationProvider->endpoint,
[
'metaCacheDir' => './tmp',
'apiVersion' => '2023-04',
],
);
$shopify->getProductImageManager()->create($id, $data);
}
} catch (Exception $e) {
$error = [
'id' => $id,
'data' => $data,
'currency' => $currency,
'message' => $e->getMessage(),
'getFile' => $e->getFile(),
'getLine' => $e->getLine(),
];
$this->utilController->logFile(json_encode($error));
return null;
}
}
public function deleteProduct($id, $currency)
{
try {
if ($this->validSync && $id && $id != "") {
$integrationProvider = $this->getIntegrationProvider($currency);
$credential = new PrivateAppCredential(
$integrationProvider->key_public,
$integrationProvider->password,
$integrationProvider->key_private
);
$shopify = new Client(
$credential,
$integrationProvider->endpoint,
[
'metaCacheDir' => './tmp',
'apiVersion' => '2023-04',
],
);
$shopify->delete('products/' . $id);
}
} catch (Exception $e) {
$error = [
'id' => $id,
'currency' => $currency,
'message' => $e->getMessage(),
'getFile' => $e->getFile(),
'getLine' => $e->getLine(),
];
$this->utilController->logFile(json_encode($error));
return null;
}
}
// Variant
public function createVariant($product_id, $data, $currency)
{
try {
if ($this->validSync && $product_id && $product_id != "") {
$response = $this->httpRequest(
'POST',
"products/" . $product_id . "/variants.json",
$currency,
$data
);
return $response['variant'];
}
return null;
} catch (Exception $e) {
$error = [
'product_id' => $product_id,
'data' => $data,
'currency' => $currency,
'message' => $e->getMessage(),
'getFile' => $e->getFile(),
'getLine' => $e->getLine(),
];
$this->utilController->logFile(json_encode($error));
return null;
}
}
public function updateVariant($variant_id, $data, $currency)
{
try {
if ($this->validSync && $variant_id && $variant_id != "") {
$response = $this->httpRequest(
'PUT',
"variants/" . $variant_id . ".json",
$currency,
$data
);
return $response;
}
return null;
} catch (Exception $e) {
$error = [
'variant_id' => $variant_id,
'data' => $data,
'message' => $e->getMessage(),
'getFile' => $e->getFile(),
'getLine' => $e->getLine(),
];
$this->utilController->logFile(json_encode($error));
return null;
}
}
public function deleteVariant($variant_id, $product_id, $currency)
{
try {
if ($this->validSync && $variant_id && $variant_id != "" && $product_id && $product_id != "") {
$integrationProvider = $this->getIntegrationProvider($currency);
$credential = new PrivateAppCredential(
$integrationProvider->key_public,
$integrationProvider->password,
$integrationProvider->key_private
);
$shopify = new Client(
$credential,
$integrationProvider->endpoint,
[
'metaCacheDir' => './tmp',
'apiVersion' => '2023-04',
],
);
$shopify->getProductVariantManager()->remove($product_id, $variant_id);
}
} catch (Exception $e) {
$error = [
'variant_id' => $variant_id,
'product_id' => $product_id,
'currency' => $currency,
'message' => $e->getMessage(),
'getFile' => $e->getFile(),
'getLine' => $e->getLine(),
];
$this->utilController->logFile(json_encode($error));
return null;
}
}
public function updateInventoryLevel($inventory_item_id, $quantity_adjustment, $location, $currency)
{
try {
if ($this->validSync && $inventory_item_id && $inventory_item_id != "") {
$locations = $this->httpRequest('GET', 'locations.json', $currency)['locations'];
$locations = array_filter($locations, function ($item) use ($location) {
return $item['name'] == $location;
}, ARRAY_FILTER_USE_BOTH);
$data = [
"location_id" => end($locations)['id'],
"inventory_item_id" => $inventory_item_id,
"available" => intval($quantity_adjustment),
];
$this->httpRequest('POST', "inventory_levels/set.json", $currency, $data);
}
} catch (Exception $e) {
$error = [
'inventory_item_id' => $inventory_item_id,
'quantity_adjustment' => $quantity_adjustment,
'location' => $location,
'currency' => $currency,
'message' => $e->getMessage(),
'getFile' => $e->getFile(),
'getLine' => $e->getLine(),
];
$this->utilController->logFile(json_encode($error));
return null;
}
}
private function updateShopifyMetafield($value, $order, $fieldName)
{
try {
$data = array(
"metafield" => array(
"value" => $value,
"owner_id" => $order['id'],
"namespace" => "checkoutcustomizer",
"key" => $fieldName,
"owner_resource" => "order",
"type" => "single_line_text_field",
)
);
$this->httpRequest('POST', "orders/" . $order['id'] . "/metafields.json", $order['currency'], $data);
} catch (Exception $e) {
$error = [
'value' => $value,
'order' => $order,
'fieldName' => $fieldName,
'message' => $e->getMessage(),
'getFile' => $e->getFile(),
'getLine' => $e->getLine(),
];
$this->utilController->logFile(json_encode($error));
return null;
}
}
public function newRequest($data)
{
return new Request($data);
}
public function sync()
{
if ($this->validSync) {
$loadDate = Carbon::now();
$integrationProviders = IntegrationProvider::where([['ecommerce_type_id', 3], ['active', true]])->get();
foreach ($integrationProviders as $integrationProvider) {
$this->getOrdersFromShopify($integrationProvider->currency, $integrationProvider->last_load_date);
$integrationProvider->last_load_date = $loadDate;
$integrationProvider->update();
$this->syncInventoryERP($integrationProvider->currency);
}
}
}
//---------------------------------------------------weebhook-------------------------------------------------------
public function webhooksListener(Request $request)
{
$order = [];
try {
$this->utilController->logFile($request, 'shopify');
$order = json_decode($request->getContent(), true);
$previousId = ShopifyLog::onWriteConnection()->find($order['id']);
if (!$previousId) {
ShopifyLog::onWriteConnection()->create(['id' => $order['id']]);
$this->saveData($order);
}
} catch (Exception $e) {
$error = [
'message' => $e->getMessage(),
'getFile' => $e->getFile(),
'getLine' => $e->getLine(),
];
$this->saveERPLog(json_encode($order), json_encode($error), 'webhooksListener');
}
return response(array('r' => true, 'm' => "Recibido", 'd' => null));
}
private function saveData($order)
{
try {
if (!$order || $order == 'null') {
return;
}
$order = $this->validateOriginOrderNumber($order);
$this->getShopifyOrderProducts($order);
if (!$this->controllerOrder->getOrderByReference($order['order_number'])) {
$user = $this->registerUserFromOrder($order);
if ($user) {
$this->saveShopifyOrderProducts($order);
$this->saveOrder($order);
$this->createMemberships($order, $user);
}
}
$this->syncERP($order);
} catch (Exception $e) {
$error = [
'message' => $e->getMessage(),
'getFile' => $e->getFile(),
'getLine' => $e->getLine(),
];
$this->saveERPLog(json_encode($order), json_encode($error), 'saveData');
}
}
private function httpRequest($method, $endpoint, $currency, $data = null)
{
$integrationProviders = $this->getIntegrationProvider($currency);
$client = new \GuzzleHttp\Client();
$url = 'https://' . $integrationProviders->endpoint . '/admin/api/2023-04/';
$body = [
'headers' => ['X-Shopify-Access-Token' => $integrationProviders->password]
];
switch ($method) {
case 'GET':
$response = $client->get($url . $endpoint, $body);
break;
case 'POST':
$body = array_merge($body, [\GuzzleHttp\RequestOptions::JSON => $data]);
$response = $client->post($url . $endpoint, $body);
break;
case 'PUT':
$body = array_merge($body, [\GuzzleHttp\RequestOptions::JSON => $data]);
$response = $client->put($url . $endpoint, $body);
break;
}
return json_decode($response->getBody()->getContents(), true);
}
public function getProduct($productId, $currency)
{
return $this->httpRequest('GET', 'products/' . $productId . '.json', $currency)['product'];
}
private function getMetafields($order)
{
$response = $this->httpRequest(
'GET',
'orders/' . $order['id'] . '/metafields.json',
$order['currency']
)['metafields'];
$jsonDataItem = current(array_filter($response, function ($item) {
return $item['key'] === 'jsondata';
}));
if ($jsonDataItem) {
return json_decode($jsonDataItem['value']);
}
return null;
}
public function getSuccessTransaction($order)
{
$transactions = $this->httpRequest(
'GET',
'orders/' . $order['id'] . '/transactions.json',
$order['currency']
)['transactions'];
$successTransactions = array_filter($transactions, function ($item) {
return $item['status'] == 'success';
}, ARRAY_FILTER_USE_BOTH);
if (count($successTransactions)) {
return end($successTransactions);
}
return null;
}
public function getOrdersFromShopify($currency, $lastLoadDate, $sinceId = 1)
{
$date = Carbon::parse($lastLoadDate)->toIso8601String();
$filters = $lastLoadDate ? '&updated_at_min=' . $date : '';
$filters .= '&since_id=' . $sinceId;
$response = $this->httpRequest(
'GET',
'orders.json?status=any&financial_status=paid&limit=250' . $filters,
$currency
);
$orders = $response["orders"];
foreach ($orders as $order) {
$this->saveData($order);
}
if (count($orders) > 0) {
$this->getOrdersFromShopify($currency, $lastLoadDate, end($orders)['id']);
}
}
private function saveOrder($order)
{
$request_params = new Request([]);
$controllerAddress = new AddressController();
$controllerUser = new UserController($request_params);
$address_id = null;
$customer = $order['customer'];
$billingAddress = $order['billing_address'];
$shippingAddress = $order['shipping_address'] ?? $billingAddress;
$document = $this->getBuyerDocument($order);
if (!$customer || !$customer['email']) {
return;
}
$user = $controllerUser->searchUser(
$document,
$customer['email'],
$customer['first_name'],
$customer['last_name'],
$order['phone']
);
if ($user) {
$user_id = $user->id;
if ($shippingAddress) {
$address = $controllerAddress->searchAddressByAddress($shippingAddress['address1'], $user_id);
if ($address) {
$address_id = $address;
}
}
}
if ($shippingAddress) {
$city_id = $controllerAddress->searchCityByName($shippingAddress['city']);
}
$products = [];
foreach ($order['line_items'] as $key => $product) {
$current_product = Product::where('reference_shopify_id', $product['product_id'])->first();
if ($current_product) {
$valuesAttributes = [];
$product_atribute = ProductAttribute::where([['product_id', $current_product->id], ['reference_shopify_id', $product['variant_id']]])
->with('attribute')
->first();
if ($product_atribute) {
$arrayAttribute = array(
'attribute' => $product_atribute->attribute->toArray(),
'attribute_id' => $product_atribute->attribute->id,
'available_units' => $product_atribute->available_units,
'dispatched_units' => $product_atribute->dispatched_units,
'ean' => $product_atribute->ean,
'high' => $product_atribute->high,
'id' => $product_atribute->id,
'length' => $product_atribute->length,
'main_position' => $product_atribute->main_position,
'observation' => $product_atribute->observation,
'packaging' => $product_atribute->packaging,
'pmi' => $product_atribute->pmi,
'price_additional' => $product_atribute->price_additional,
'product_id' => $product_atribute->product_id,
'quantity' => $product['quantity'],
'sku' => $product_atribute->sku,
'stowage_pattern' => null,
'value' => $product['variant_title'],
'weight' => null,
'width' => null,
'created_at' => null,
'updated_at' => null,
);
$valuesAttributes[] = $arrayAttribute;
}
$newProduct = array(
'quantity' => $product['quantity'],
'max_units_per_order' => $current_product->max_units_per_order,
'price' => $product['price'],
'priceTotal' => $product['price'],
'id' => $current_product->id,
'plu' => $current_product->plu,
'name' => $current_product->name,
'priceAfterDiscount' => null,
'priceAfterFlash' => null,
'percentage_discount' => null,
'start_discount' => null,
'limit_discount' => null,
'limit_hour_discount' => null,
'flash_price' => null,
'start_flash_discount' => null,
'limit_flash_discount' => null,
'limit_hour_flash_discount' => null,
'product_attributes_selected' => $valuesAttributes
);
$products[] = $newProduct;
}
}
$priceDomicile = $order['shipping_lines'];
if (count($priceDomicile) > 0) {
$priceDomicile = $priceDomicile[0]['price'];
} else {
$priceDomicile = 0;
}
$phone = $shippingAddress['phone'];
$address = $shippingAddress['address1'];
$data = array('data' => array(
'first_name' => $customer ? $customer['first_name'] : null,
'last_name' => $customer ? $customer['last_name'] : null,
'document' => $document,
'phone' => $phone,
'email' => $customer ? $customer['email'] : null,
'order_reference' => $order['order_number'],
'sucursal' => null,
'products' => $products,
'confirmOrder' => array(
'observations' => "",
'effectivePayment' => 0,
'cellphone' => $phone,
'wayPay' => 5,
'address' => $address_id,
'address_new' => array(
'direction' => $shippingAddress['address1'],
'district' => '',
'instru' => $shippingAddress['address2'],
'lat' => '',
'lng' => '',
),
),
'dCampana' => null,
'dReferred' => null,
'dCoupone' => null,
'subtotal' => $order['subtotal_price'],
'discount' => $order['total_discounts'],
'priceDomicile' => $priceDomicile,
'total' => $order['total_price'],
'productsNotFound' => null,
'order_type_id' => 5,
'client_id' => $user_id ?? null,
'city_id' => $city_id,
'delivery_man_id' => null,
'job_sync' => true,
'code' => $order['id'],
'purchase_origin' => $this->getOriginShopifyOrder($order),
'payment_transaction_id' => $order['reference'] ?? $order['confirmation_number'],
'status_notification_email_client' => 'send',
));
if (count($products)) {
$request = new Request($data);
$newOrder = $this->controllerOrder->create($request, false);
if (!is_array($newOrder)) {
$this->registerLog(
$user_id,
'Error creando order: ' . json_encode($newOrder->getData()),
json_encode($order),
"Create",
3
);
return;
}
}
}
public function getBuyerDocument($order)
{
$metafields = $this->getMetafields($order);
if ($metafields) {
$juridicalPerson = isset($metafields->selectedTypeCustomer) && $metafields->selectedTypeCustomer == 'persona_juridica';
if (!empty($metafields->selectedNIT) || !empty($metafields->selectedDocument)) {
return $juridicalPerson ? $metafields->selectedNIT : $metafields->selectedDocument;
}
}
$customer = $order['customer'];
$billingAddress = $order['billing_address'];
$customerDocument = isset($customer['document']) ? $customer['document'] : null;
if ($billingAddress) {
$customerDocument = $customerDocument ?? $billingAddress['document'] ?? $billingAddress['company'];
}
return $customerDocument;
}
private function registerUserFromOrder($order)
{
$userController = new UserController();
$customer = $order['customer'];
$billingAddress = $order['billing_address'];
if (!$customer) {
return null;
}
$email = $customer['email'] ?? $order['contact_email'] ?? $order['email'];
$password = base64_encode(random_bytes(4));
$tags = $this->getSegmentationByShopifyOrderTags($order);
$customerDocument = $this->getBuyerDocument($order);
$userResponse = $userController->createClient(new Request([
'first_name' => $customer['first_name'] ?? $billingAddress['first_name'] ?? 'User',
'last_name' => $customer['last_name'] ?? $billingAddress['last_name'],
'email' => $email,
'password' => $password,
'tags' => $tags->pluck('id')->toArray(),
'phone' => $customer['phone'] ?? $billingAddress['phone'] ?? '1234',
'document' => $customerDocument,
'document_type' => '',
'direction' => $billingAddress['address1'] . ',' . $billingAddress['city'] . ',' . $billingAddress['province'] . ',' . $billingAddress['country']
]), false);
if (!$userResponse['r']) {
return null;
}
if (count($tags) > 0) {
$appStoreUrl = $this->parameterService->appStoreUrl();
$playStoreUrl = $this->parameterService->playStoreUrl();
$corporateIdentity = CorporateIdentity::first();
$segmentationNames = implode(", ", $tags->pluck('name')->toArray());
$customer['email'] = $email;
$parameters = [
'isNewUser' => !isset($userResponse['oldUser']) || !$userResponse['oldUser'],
'email' => $email,
'password' => $password,
'segmentation' => $segmentationNames,
'appStoreUrl' => $appStoreUrl,
'playStoreUrl' => $playStoreUrl,
'corporateIdentity' => $corporateIdentity
];
$this->sendConfirmationEmail($customer, $segmentationNames, $parameters);
}
return $userResponse['d'];
}
public function sendConfirmationEmail(array $customer, string $subject, array $params = [])
{
try {
$mail = new PHPMailer(true);
// $mail->SMTPDebug = SMTP::DEBUG_SERVER; //Enable verbose debug output
$mail->isSMTP(); //Send using SMTP
$mail->Host = env('MAIL_HOST_HED'); //Set the SMTP server to send through
$mail->SMTPAuth = true; //Enable SMTP authentication
$mail->Username = env('MAIL_USERNAME_HED'); //SMTP username
$mail->Password = env('MAIL_PASSWORD_HED'); //SMTP password
$mail->SMTPSecure = env('MAIL_ENCRYPTION'); //Enable implicit TLS encryption
$mail->Port = env('MAIL_PORT');
$mail->setFrom(env('MAIL_USERNAME_HED'), env('MAIL_FROM_NAME'));
$mail->addAddress($customer['email'], $customer['first_name']);
$mail->isHTML(true);
$mail->Subject = $subject;
$mail->Body = view('mails.createdUserAndCarnet')->with([
'isNewUser' => $parameters['isNewUser'] ?? false,
'email' => $customer['email'],
'password' => $params['password'] ?? '',
'segmentation' => $params['tag'] ?? '',
'appStoreUrl' => $params['appStoreUrl'],
'playStoreUrl' => $params['playStoreUrl'],
'corporateIdentity' => $params['corporateIdentity']
])->render();
$mail->send();
} catch (Exception $e) {
$error = [
'customer' => $customer,
'subject' => $subject,
'params' => $params,
'message' => $e->getMessage(),
'getFile' => $e->getFile(),
'getLine' => $e->getLine(),
];
$this->utilController->logFile(json_encode($error));
echo "Message could not be sent. Mailer Error: " . $e->getMessage();
}
}
private function saveProduct($product)
{
$request_params = $this->newRequest([]);
$brand = new BrandsController($request_params);
$category = new CategoryController($request_params);
$product_controller = new ProductController($request_params);
$productAttribute_controller = new ProductAttributeController($request_params);
$attribute_controller = new AttributeController($request_params);
$current_product = Product::where('name', $product['title'])->first();
// si existe actualizamos si no creamos
if ($current_product) {
$current_product->reference_shopify_id = $product['id'];
$product_id = $current_product->id;
$current_product->update();
$this->saveProductImages($product, $current_product->id);
} else {
$default_sucursal_id = 2;
if (Auth::user()) {
$default_sucursal_id = Auth::user()->userInfo->sucursal_id;
}
$sucursal_id = $this->parameters->sucursal_products ? $default_sucursal_id : null;
$brand_id = null;
$c_brand = $brand->create($this->newRequest(['name' => $product['vendor']]));
if ($c_brand) {
$brand_id = $c_brand["d"]["id"];
}
$subcategory_id = null;
$data_category = [
'name' => $product['product_type'],
'sucursal_id' => $sucursal_id,
'priority' => 1,
'store_type' => 'main'
];
$c_category = $category->create($this->newRequest($data_category), false);
if ($c_category) {
$subcategory_id = $c_category["s"];
}
$price = 0;
$available_units = 0;
foreach ($product['variants'] as $variant) {
$price = $variant['price'];
$available_units = $available_units + $variant['inventory_quantity'];
}
$product_id = null;
$data_product = [
'name' => $product['title'],
'plu' => $product['handle'],
'bar_code' => null,
'price' => $price,
'available_units' => $available_units,
'max_units_per_order' => 100,
'percentage_discount' => null,
'start_discount' => null,
'limit_discount' => null,
'limit_hour_discount' => null,
'flash_price' => null,
'start_flash_discount' => null,
'limit_flash_discount' => null,
'limit_hour_flash_discount' => null,
'brand_id' => $brand_id,
'order' => 1,
'chkAgeProduct' => false,
'sucursal_id' => $sucursal_id,
'sub_categories' => [$subcategory_id],
'reference_shopify_id' => $product['id'],
'active' => true,
'store_type' => 'main'
];
$cproduct = $product_controller->create($this->newRequest($data_product), false);
if ($cproduct) {
$product_id = $cproduct["d"]["id"];
$this->saveProductImages($product, $cproduct['d']['id']);
}
}
foreach ($product['variants'] as $variant) {
$name_attribute = "Tallas checkbox único";
foreach ($product['options'] as $options) {
foreach ($options['values'] as $option) {
if ($option == $variant['title']) {
$name_attribute = $options['name'];
break;
}
}
}
$attribute_id = null;
$data_attribute = [
"name" => $name_attribute,
"display_name" => $name_attribute,
"is_more_one" => false,
"required" => true,
"attribute_type_id" => 2,
"active" => true,
];
$catrribute = $attribute_controller->store($this->newRequest($data_attribute), false);
if ($catrribute) {
$attribute_id = $catrribute["d"];
}
$data_productAttribute = [
"product_id" => $product_id,
"attribute_id" => $attribute_id,
"value" => $variant['title'],
"price_additional" => 0,
"sku" => $variant['sku'],
"available_units" => $variant['inventory_quantity'],
"pmi" => 0,
"packaging" => 0,
"weight" => 0,
"ean" => 0,
"width" => 0,
"length" => 0,
"high" => 0,
"observation" => 0,
"main_position" => 0,
"stowage_pattern" => 0,
"reference_shopify_variation_id" => $variant['inventory_item_id'],
"reference_shopify_id" => $variant['id'],
];
$productAttribute_controller->store($this->newRequest($data_productAttribute), $product_id, false);
}
}
private function saveProductImages($productShopify, $productId)
{
foreach ($productShopify['images'] as $image) {
$product_controller = new ProductController();
$product_controller->uploadImageFromUrl($image['src'], $image['id'], $productId);
}
}
public function searchProductVariant($data, $variant)
{
$result = array_filter($data, function ($productVariant) use ($variant) {
return $productVariant['id'] == $variant;
}, ARRAY_FILTER_USE_BOTH);
if (count($result)) {
$result = reset($result);
}
return $result;
}
public function saveERPLog($data, $message, $action)
{
$transactionId = json_decode($data)->id;
ErpLog::updateOrCreate(
[
'origin' => 'shopify',
'transaction_id' => $transactionId,
'resolved' => false
],
[
'origin' => 'shopify',
'action' => $action,
'origin_class' => get_class($this),
'data' => $data,
'transaction_id' => $transactionId,
'message' => $message,
]
);
}
public function retrySyncERP($data, $action)
{
$this->syncERP(json_decode($data, true), $action);
}
public function syncERP($order, $action = null)
{
$erpSync = ErpParameter::where('key', 'shop_sync')->first();
if (!$erpSync || $erpSync->value == 'false') {
$this->saveERPLog(json_encode($order), 'Sinc ERP deshabilitada', 'syncERP');
return;
}
$erps = Erp::select('id', 'class')->where('active', true)->get();
foreach ($erps as $index => $erp) {
$classPath = $erp->class;
$ERPRepository = new $classPath($erp->id);
if (!$this->controllerOrder->validateSyncWithERPPayment($order['id'])) {
return;
}
$previousBill = $ERPRepository->getBill($order['order_number']);
if ($previousBill['r'] && count($previousBill['d']->detalle->Table)) {
$this->controllerOrder->confirmSyncERP($order['order_number']);
return;
}
switch ($action) {
case 'createOrder':
$this->createOrderERP($order, $ERPRepository);
break;
case 'createRC':
$this->createRCERP($order, $ERPRepository);
break;
case 'createBill':
$this->createBillERP($order, $ERPRepository);
break;
case 'saveData':
$this->saveData($order);
break;
default:
$this->createThirdClientERP($order, $ERPRepository);
break;
}
}
}
private function createThirdClientERP($order, $ERPRepository)
{
$billingAddress = $order['billing_address'];
$isColombia = $billingAddress['country'] == 'Colombia';
$metafields = $this->getMetafields($order);
$birthDate = $metafields ? str_replace('-', '/', $metafields->selectedAge) : Carbon::now();
$documentType = $metafields && !empty($metafields->selectedTypeDocument) ? $metafields->selectedTypeDocument : 'O';
$typeCustomer = $metafields && isset($metafields->selectedTypeCustomer) ? $metafields->selectedTypeCustomer : 'persona_natural';
$juridicalPerson = $typeCustomer == 'persona_juridica';
$gender = $metafields ? $metafields->selectedGenero : 'O';
$establishmentName = $metafields && isset($metafields->selectedNombreEstablecimiento) ? $metafields->selectedNombreEstablecimiento : null;
$district = $metafields && isset($metafields->selectedNeighborhood) ? $metafields->selectedNeighborhood : '';
$customerDocument = $this->getBuyerDocument($order);
$birthDate = str_contains($birthDate, '/') ? Carbon::createFromFormat('d/m/Y', $birthDate) : Carbon::parse($birthDate);
$province = $metafields && $isColombia && !empty($metafields->selectedDepartment) ? $metafields->selectedDepartment : $billingAddress['province'];
$city = $metafields && $isColombia && !empty($metafields->selectedCity) ? $metafields->selectedCity : $billingAddress['city'];
$socialReason = $metafields && isset($metafields->selectedRazonSocial) ? $metafields->selectedRazonSocial : null;
$ciiu = $metafields && isset($metafields->selectedCodigoCIIU) ? $metafields->selectedCodigoCIIU : null;
$namePersonContact = $metafields && isset($metafields->selectedPersonaContacto) ? $metafields->selectedPersonaContacto : null;
$nit = $metafields && isset($metafields->selectedNIT) ? $metafields->selectedNIT : null;
$this->updateShopifyMetafield($birthDate->toDateString(), $order, 'fechadecumpleaos_v1');
$this->updateShopifyMetafield($customerDocument, $order, 'numerodedocumento_v1');
$this->updateShopifyMetafield($documentType, $order, 'tipodedocumento_v1');
$this->updateShopifyMetafield($typeCustomer, $order, 'tipodecomprador_v1');
$this->updateShopifyMetafield($province, $order, 'departamento_v1');
$this->updateShopifyMetafield($district, $order, 'barrio_v1');
$this->updateShopifyMetafield($gender, $order, 'genero_v1');
$this->updateShopifyMetafield($city, $order, 'ciudad_v1');
$this->updateShopifyMetafield($establishmentName, $order, 'nombredelestablecimiento_v1');
$this->updateShopifyMetafield($namePersonContact, $order, 'nombredelapersonadecontacto_v1');
$this->updateShopifyMetafield($ciiu, $order, 'codigociiu_v1');
$this->updateShopifyMetafield($nit, $order, 'nit_v1');
$this->updateShopifyMetafield($socialReason, $order, 'razonsocial_v1');
$siesaThirdClient = $ERPRepository->thirdClientValidate($customerDocument);
$isHED = $this->isHED($order);
$isLocal = $order['currency'] == 'COP';
$params = new \stdClass();
$third = new \stdClass();
$third->document = $customerDocument;
$third->firstName = $billingAddress['first_name'];
$third->lastName = $billingAddress['last_name'];
$third->contact = $juridicalPerson ? $namePersonContact : $billingAddress['name'];
$third->phone = $billingAddress['phone'];
$third->email = $order['customer']['email'];
$third->isNew = count($siesaThirdClient) == 0;
$third->documentType = $ERPRepository->mapDocumentType($juridicalPerson ? 'nit' : $documentType);
$third->birthDate = $birthDate->format('Ymd');
$third->typeThird = $typeCustomer;
$third->gender = $ERPRepository->mapGender($gender);
$third->socialReason = $juridicalPerson ? $socialReason : $billingAddress['name'];
$third->establishmentName = $establishmentName;
$third->ciiu = $juridicalPerson ? $ciiu : null;
$params->third = $third;
$params->client = $third;
$params->address = $billingAddress['address1'] . ' | ' . $district;
$params->shortAddress = $billingAddress['address1'];
$params->country = $billingAddress['country'];
$params->province = $province;
$params->city = $city;
$params->sucursal = $isLocal ? '001' : '010';
$params->price_list = $isLocal ? '001' : '014';
$params->typeClient = $isHED ? 'CHED' : ($isLocal ? 'CTDS' : 'CTTI');
$params->postal = substr($billingAddress['zip'], 0, 10);
$params->hasTaxes = $isLocal ? '1' : '0';
$params->createThirdPos = !$isHED;
$params->idCriteria = $isHED ? '105' : null;
$params->criteria = $isHED ? '1051' : null;
$params->currency = $order['currency'];
$response = $ERPRepository->createThirdClient($params);
if (!$response['r']) {
$this->saveERPLog(json_encode($order), json_encode($response['d']), 'createThirdClient');
return;
}
$this->createOrderERP($order, $ERPRepository);
}
public function isHED($order)
{
return count($this->getOrderMemberships()) > 0;
}
private function createOrderERP($order, $ERPRepository)
{
$successTransaction = $this->getSuccessTransaction($order);
if (!$successTransaction) {
$this->saveERPLog(json_encode($order), 'La orden no tiene transacciones exitosas', 'createOrder');
return;
}
$response = $ERPRepository->getOrder($order['order_number']);
if ($response['r']) {
$previousOrders = array_filter($response['d']->detalle->Table, function ($siesaOrder) use ($order, $successTransaction) {
return str_contains($siesaOrder->Notas, $order['checkout_id']) ||
str_contains($siesaOrder->Notas, $successTransaction['payment_id']);
}, ARRAY_FILTER_USE_BOTH);
if (count($previousOrders)) {
$this->createRCERP($order, $ERPRepository);
return;
}
}
$response = $ERPRepository->createOrderShop($order, $this);
if (!$response['r']) {
$this->saveERPLog(json_encode($order), json_encode($response['d']), 'createOrder');
return;
}
$this->syncInventoryERPFromOrder($order);
$this->createRCERP($order, $ERPRepository);
}
private function createRCERP($order, $ERPRepository)
{
sleep(60);
$response = $ERPRepository->getOrder($order['order_number']);
if (!$response['r']) {
$this->saveERPLog(json_encode($order), json_encode($response['d']), 'createRC');
return;
}
$successTransaction = $this->getSuccessTransaction($order);
$isHED = $this->isHED($order);
$isLocal = $order['currency'] == 'COP';
$prefixNote = $isHED ? 'HED - ' : '';
foreach ($response['d']->detalle->Table as $index => $siesaOrder) {
$response = $ERPRepository->getRC($this->getBuyerDocument($order), isset($siesaOrder->CentroDeOperacion) ? $siesaOrder->CentroDeOperacion : config('erp_siesa.shopOperationCenterId'));
if ($response['r']) {
$previousRC = array_filter($response['d']->detalle->Table, function ($item) use ($order) {
return str_contains($item->Notas, $order['order_number']);
}, ARRAY_FILTER_USE_BOTH);
if (count($previousRC)) {
$this->createBillERP($order, $ERPRepository);
continue;
}
}
$parameters = new \stdClass();
$parameters->paymentDate = Carbon::parse($order['created_at'])->format('Ymd');
$parameters->idThird = $this->getBuyerDocument($order);
$parameters->currency = $order['currency'];
$parameters->siesaOrder = $siesaOrder;
$parameters->fe = '1104';
$parameters->note = $prefixNote . 'Número de pedido:' . $order['order_number'] . ' - IDPAGO:' . $successTransaction['payment_id'];
$parameters->assistant = $isHED ? '28050507' : ($isLocal ? '28050506' : '28050508');
$parameters->sucursal = $isLocal ? '001' : '010';
$parameters->paymetMethod = $isLocal ? 'PYV' : 'PYE';
$parameters->UN = $isHED ? '104' : '102';
$parameters->price = round($order['total_price']);
$response = $ERPRepository->createRC($parameters);
if (!$response['r']) {
$this->saveERPLog(json_encode($order), json_encode($response['d']), 'createRC');
return;
}
$this->createBillERP($order, $ERPRepository);
}
}
private function createBillERP($order, $ERPRepository)
{
$previousBill = $ERPRepository->getBill($order['order_number']);
if ($previousBill['r'] && count($previousBill['d']->detalle->Table)) {
return;
}
$response = $ERPRepository->getOrder($order['order_number']);
if (!$response['r']) {
$this->saveERPLog(json_encode($order), json_encode($response['d']), 'createBill');
return;
}
$thirdDocument = $this->getBuyerDocument($order);
foreach ($response['d']->detalle->Table as $index => $siesaOrder) {
$parameters = new \stdClass();
$parameters->siesaOrder = $siesaOrder;
$parameters->thirdDocument = $thirdDocument;
$parameters->note = $order['order_number'];
$parameters->price = $order['total_price'];
$parameters->typeDocument = $order['currency'] == 'COP' ? 'FE1' : 'FE2';
$response = $ERPRepository->createBill($parameters);
if (!$response['r']) {
$this->saveERPLog(json_encode($order), json_encode($response['d']), 'createBill');
return;
}
}
$this->controllerOrder->confirmSyncERP($order['order_number']);
}
private function syncInventoryERPFromOrder($order)
{
foreach ($order['line_items'] as $index => $product) {
$variantResponse = $this->getShopifyVariant(null, null, 1, $product['variant_id'], $order['currency']);
if (!$variantResponse['r']) {
continue;
}
if ($variantResponse['variantExists']) {
$variant = $variantResponse['variant'];
if ($variant['barcode']) {
$integrationProviders = IntegrationProvider::where([['ecommerce_type_id', 3], ['active', true]])->get();
foreach ($integrationProviders as $integrationProvider) {
$this->syncInventoryERP($integrationProvider->currency, $variant['barcode']);
}
}
}
}
}
private function syncInventoryERP($currency, $barcode = null)
{
$erpSync = ErpParameter::where('key', 'shop_sync')->first();
if (!config('erp_siesa.sync_inventory') || !$erpSync || $erpSync->value == 'false') {
return;
}
$priceList = $currency == 'COP' ? '001' : '014';
$locationNames = ['BDHED', 'TI108'];
foreach ($locationNames as $index => $locationName) {
$siesaInventory = $this->inventoryController->syncInventoryERP(
$priceList,
'main',
true,
$this,
$locationName,
$currency,
$barcode
);
if (!$barcode) {
$this->syncInventoryWithOutStock(1, $siesaInventory, $locationName, $currency);
}
}
}
public function syncProductByReference($item, $location, $currency)
{
$variantResponse = $this->getShopifyVariant($item->CodigoDeBarras, null, 1, null, $currency);
if (!$variantResponse['r']) {
return;
}
if ($variantResponse['variantExists']) {
$variant = $variantResponse['variant'];
$dataToUpdate = array(
"id" => $variant['id']
);
if ($variant['compare_at_price'] > 0) {
$dataToUpdate['compare_at_price'] = $item->Precio;
} else {
$dataToUpdate['price'] = $item->Precio;
}
$data_variant = array(
"variant" => $dataToUpdate
);
$this->updateVariant($variant['id'], $data_variant, $currency);
$this->updateInventoryFromVariant($variant, $item->Existencias, $location, $currency);
return;
}
$data_variant = array(
"option1" => $item->Talla ?? 'ÚNICA',
"option2" => $item->Color ?? 'ÚNICO',
"price" => $item->Precio,
"barcode" => $item->CodigoDeBarras,
"inventory_management" => "shopify",
);
$productResponse = $this->getShopifyVariant(null, $item->Referencia, 1, null, $currency);
if (!$productResponse['r']) {
return;
}
if ($productResponse['variantExists']) {
$product = $productResponse['variant'];
$variant = $this->createVariant($product['product_id'], array("variant" => $data_variant), $currency);
$this->updateInventoryFromVariant($variant, $item->Existencias, $location, $currency);
return;
}
$data_sho = array("product" => array(
"title" => $item->Descripcion,
"vendor" => $item->Marca,
"product_type" => $item->Linea,
"status" => "draft",
"variants" => [
$data_variant
],
"options" => [
array(
"name" => "Tamaño",
"position" => 1
),
array(
"name" => "Color",
"position" => 2
),
]
));
$shopifyProduct = $this->createProduct($data_sho, $currency);
if ($shopifyProduct) {
$this->updateInventoryFromVariant($shopifyProduct['variants'][0], $item->Existencias, $location, $currency);
}
}
private function updateInventoryFromVariant($variant, $stock, $location, $currency)
{
if ($variant) {
$reference_shopify_variation_id = $variant['inventory_item_id'];
$this->updateInventoryLevel($reference_shopify_variation_id, $stock, $location, $currency);
}
}
private function getShopifyVariant($barcode, $reference, $sinceId = 1, $variantId = null, $currency)
{
try {
$content = $this->httpRequest(
'GET',
'variants' . ($variantId ? '/' . $variantId : '') . '.json?limit=250&since_id=' . $sinceId,
$currency
);
if ($variantId) {
return array('r' => true, 'variantExists' => true, 'variant' => $content['variant']);
}
$variants = $content["variants"];
$result = array_filter($variants, function ($productVariant) use ($barcode, $reference) {
if ($barcode) {
return $productVariant['barcode'] == $barcode;
} else {
$productReference = substr($productVariant['barcode'], 0, 5) . substr($productVariant['barcode'], -4);
return $productReference == $reference;
}
}, ARRAY_FILTER_USE_BOTH);
if (count($result)) {
return array('r' => true, 'variantExists' => true, 'variant' => reset($result));
}
if (count($variants)) {
return $this->getShopifyVariant($barcode, $reference, end($variants)['id'], $variantId, $currency);
}
return array('r' => true, 'variantExists' => false);
} catch (Exception $e) {
$error = [
'barcode' => $barcode,
'reference' => $reference,
'sinceId' => $sinceId,
'variantId' => $variantId,
'currency' => $currency,
'message' => $e->getMessage(),
'getFile' => $e->getFile(),
'getLine' => $e->getLine(),
];
$this->utilController->logFile(json_encode($error));
return array('r' => false);
}
}
function syncInventoryWithOutStock($sinceId = 1, $siesaInventory, $location, $currency)
{
$variants = $this->httpRequest('GET', 'variants.json?limit=250&since_id=' . $sinceId, $currency)["variants"];
foreach ($variants as $index => $item) {
if (!$item['barcode']) {
continue;
}
$siesaProducts = array_filter($siesaInventory, function ($productVariant) use ($item) {
return $productVariant->CodigoDeBarras == $item['barcode'];
}, ARRAY_FILTER_USE_BOTH);
if (!count($siesaProducts)) {
$this->updateInventoryFromVariant($item, 0, $location, $currency);
}
}
if (count($variants)) {
return $this->syncInventoryWithOutStock(end($variants)['id'], $siesaInventory, $location, $currency);
}
}
//-----------------------------------------------END_weebhook-------------------------------------------------------
function createMemberships($order, $user)
{
$membershipService = new MembershipService();
$memberships = $this->getOrderMemberships();
if (count($memberships) == 0) {
return false;
}
$deliveryPrice = 0;
foreach ($order['shipping_lines'] as $index => $item) {
$discount = array_sum(array_column($item['discount_allocations'], 'amount'));
$deliveryPrice += round($item['price']) - $discount;
}
$shopifyPaymentTransaction = $this->getSuccessTransaction($order);
$log = [
'order' => $order,
'shopifyProducts' => $this->shopifyProducts,
'shopifyPaymentTransaction' => $shopifyPaymentTransaction,
'user' => $user,
'memberships' => $memberships
];
$this->utilController->logFile(json_encode($log), 'createMemberships');
foreach ($memberships as $item) {
$paymentTransaction = $this->paymentTransactionService->getByPaymentGatewayTxId(
$shopifyPaymentTransaction['payment_id']
);
if ($paymentTransaction) {
continue;
}
$params = new \stdClass();
$params->membership = $item;
$params->user = $user;
$params->startDate = Carbon::parse($order['created_at']);
$params->deliveryPrice = $deliveryPrice;
$params->transactionReference = $shopifyPaymentTransaction['payment_id'];
$membershipService->createConfirmedMembershipSubscription($params);
}
return true;
}
public function getOrderMemberships()
{
$membershipController = new MembershipController();
$segmentation = $this->getSegmentationByShopifyOrderTags();
return $membershipController->getBySegmentation($segmentation->pluck('id')->toArray());
}
public function productHasMemberships($shopifyProduct)
{
$membershipController = new MembershipController();
$segmentation = $this->getSegmentationByTagNames(explode(', ', $shopifyProduct['tags']));
$memberships = $membershipController->getBySegmentation($segmentation->pluck('id')->toArray());
return (count($memberships) > 0);
}
private function getShopifyOrderProducts($order)
{
$this->shopifyProducts = [];
foreach ($order['line_items'] as $item) {
if (!$item['product_id']) {
continue;
}
$this->shopifyProducts[] = $this->getProduct($item['product_id'], $order['currency']);
}
}
private function getSegmentationByShopifyOrderTags()
{
$tags = [];
foreach ($this->shopifyProducts as $shopifyProduct) {
$tags = array_merge($tags, explode(', ', $shopifyProduct['tags']));
}
return $this->getSegmentationByTagNames(array_unique($tags));
}
private function getSegmentationByTagNames($tags)
{
$tagService = new TagService();
return $tagService->getByNames($tags);
}
private function saveShopifyOrderProducts()
{
foreach ($this->shopifyProducts as $shopifyProduct) {
$this->saveProduct($shopifyProduct);
}
}
public function updateOrdersFromShopify($currency, $lastLoadDate, $sinceId = 1, $orders = [])
{
if (is_string($orders)) {
$orders = json_decode($orders, true);
}
$date = Carbon::parse($lastLoadDate)->toIso8601String();
$filters = $lastLoadDate ? '&updated_at_min=' . $date : '';
$filters .= '&since_id=' . $sinceId;
$response = $this->httpRequest(
'GET',
'orders.json?status=any&financial_status=paid&limit=250' . $filters,
$currency
);
$newOrders = $response["orders"];
if (count($newOrders) > 0) {
$orders = array_merge($orders, $newOrders);
$this->updateOrdersFromShopify($currency, $lastLoadDate, end($orders)['id'], $orders);
} else {
if (!count($orders)) {
$orders = $newOrders;
}
foreach ($orders as $order) {
$orderBD = $this->controllerOrder->getOrderByReference($order['order_number']);
if ($orderBD && !$orderBD->payment_transaction_id) {
$purchaseOrigin = $this->getOriginShopifyOrder($order);
$paymentTransactionId = $order['reference'] ?? $order['confirmation_number'];
$orderBD->purchase_origin = $purchaseOrigin;
$orderBD->payment_transaction_id = $paymentTransactionId;
$orderBD->gw_code_transaction = $paymentTransactionId;
$orderBD->gw_state = 'CONFIRMED';
$orderBD->update();
$this->utilController->logFile('Order ' . $order['order_number'] . ' updated');
}
}
}
}
private function getOriginShopifyOrder($order)
{
$origin = 'OTROS MEDIOS';
if (!$order['referring_site'] && (str_contains($order['client_details']['user_agent'], 'Android') || str_contains($order['client_details']['user_agent'], 'iPhone'))) {
$origin = 'APP';
}
return $origin;
}
public function resyncSystemLogs($days = 30)
{
$logs = SystemLog::select(
'id',
DB::raw("SUBSTRING_INDEX(data, '\n', -1) AS json")
)
->where('origin', 'shopify')
->where('created_at', '>=', Carbon::now()->subDays($days))
->get();
foreach ($logs as $log) {
$order = [];
try {
$order = json_decode($log->json, true);
$this->saveData($order);
} catch (Exception $e) {
$error = [
'message' => $e->getMessage(),
'getFile' => $e->getFile(),
'getLine' => $e->getLine(),
];
$this->saveERPLog(json_encode($order), json_encode($error), 'resyncSystemLogs');
}
}
return response(array('r' => true, 'm' => "Ejecución exitosa resyncSystemLogs, registros: " . count($logs)));
}
private function validateOriginOrderNumber($order)
{
if ($order['order_status_url'] && str_contains($order['order_status_url'], 'internacional') && $order['order_number'] && !str_contains($order['order_number'], '-I')) {
$order['order_number'] = $order['order_number'] . '-I';
}
return $order;
}
}