| @@ -0,0 +1,119 @@ | |||||
| <?php | |||||
| /** | |||||
| * @author Daniel Knudsen <d.knudsen@spawntree.de> | |||||
| * @date 11.07.22 | |||||
| */ | |||||
| require_once('Product.php'); | |||||
| class ApiExact | |||||
| { | |||||
| const API_URL = "https://start.exactonline.de/api"; | |||||
| const CLIENT_ID = "5a04d118-349c-4750-aac3-fa3a386999c6"; | |||||
| const CLIENT_SECRET = "E7Wuqcsp4Lih"; | |||||
| const ACCESS_TOKEN_VALID_DURATION = 600 - 50; | |||||
| const DIVISION = '58687'; | |||||
| const ITEM_GROUP_UUID = 'df17bdaf-2af7-4e9f-8d60-326e36b57764'; | |||||
| const PRODUCT_CODE_PREFIX = "WR"; | |||||
| public function getAccessToken() | |||||
| { | |||||
| $tokenData = json_decode(file_get_contents('./tokenData', true)); | |||||
| if (!property_exists($tokenData, 'expiry_time') || (int)$tokenData->expiry_time < time()) { | |||||
| $postData = array( | |||||
| 'grant_type' => 'refresh_token', | |||||
| 'refresh_token' => $tokenData->refresh_token, | |||||
| 'client_id' => self::CLIENT_ID, | |||||
| 'client_secret' => self::CLIENT_SECRET | |||||
| ); | |||||
| $ch = curl_init(); | |||||
| curl_setopt_array($ch, array( | |||||
| CURLOPT_URL => self::API_URL . '/oauth2/token', | |||||
| CURLOPT_RETURNTRANSFER => TRUE, | |||||
| CURLOPT_HTTPHEADER => array('Content-Type: application/x-www-form-urlencoded'), | |||||
| CURLOPT_POSTFIELDS => http_build_query($postData) | |||||
| )); | |||||
| $response = curl_exec($ch); | |||||
| curl_close($ch); | |||||
| $jsonResult = json_decode($response); | |||||
| if (property_exists($jsonResult, 'error')) { | |||||
| $msg = $jsonResult->error; | |||||
| throw new Exception($msg); | |||||
| } | |||||
| $jsonResult->expiry_time = time() + self::ACCESS_TOKEN_VALID_DURATION; | |||||
| file_put_contents('./tokenData', json_encode($jsonResult)); | |||||
| $tokenData = json_decode(file_get_contents('./tokenData', true)); | |||||
| } | |||||
| return $tokenData->access_token; | |||||
| } | |||||
| public function getProducts() | |||||
| { | |||||
| $baseUrl = self::API_URL . '/v1/' . self::DIVISION . "/bulk/Logistics/Items?"; | |||||
| $fields = [ | |||||
| 'ID', | |||||
| 'StandardSalesPrice', | |||||
| 'SalesVatCode', | |||||
| 'SalesVatCodeDescription', | |||||
| 'Description', | |||||
| 'ExtraDescription', | |||||
| 'IsWebshopItem', | |||||
| 'ItemGroup', | |||||
| 'ItemGroupCode', | |||||
| 'Stock', | |||||
| 'NetWeight', | |||||
| 'GrossWeight', | |||||
| 'Code' | |||||
| ]; | |||||
| $filter = "\$filter=ItemGroup eq guid'".self::ITEM_GROUP_UUID."'"; | |||||
| $select = "&\$select=".$this->getFieldString($fields); | |||||
| $requestUrl = $baseUrl . $filter . $select; | |||||
| $response = $this->getApiData($requestUrl); | |||||
| $responseArr = json_decode($response, 1)['d']['results']; | |||||
| $res = []; | |||||
| foreach ($responseArr as $productItem) { | |||||
| if ( | |||||
| array_key_exists('IsWebshopItem', $productItem) && | |||||
| $productItem['IsWebshopItem'] === 1 && | |||||
| array_key_exists('Code', $productItem) && | |||||
| stripos(strtoupper($productItem['Code']), self::PRODUCT_CODE_PREFIX) === 0 | |||||
| ) { | |||||
| $res[] = Product::createProductFromApiItem($productItem); | |||||
| } | |||||
| } | |||||
| return $res; | |||||
| } | |||||
| private function getApiData($url) | |||||
| { | |||||
| $ch = curl_init(); | |||||
| curl_setopt_array($ch, array( | |||||
| CURLOPT_URL => $url, | |||||
| CURLOPT_HTTPHEADER => array('Accept: application/json' , "Authorization: Bearer " . $this->getAccessToken() ), | |||||
| CURLOPT_RETURNTRANSFER => TRUE, | |||||
| )); | |||||
| return curl_exec($ch); | |||||
| } | |||||
| private function getFieldString($fields) | |||||
| { | |||||
| $res = ''; | |||||
| $cnt = count($fields); | |||||
| $i = 1; | |||||
| foreach ($fields as $field) { | |||||
| $res .= $cnt !== $i ? $field . ',' : $field; | |||||
| $i++; | |||||
| } | |||||
| return $res; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,147 @@ | |||||
| <?php | |||||
| /** | |||||
| * @author Daniel Knudsen <d.knudsen@spawntree.de> | |||||
| * @date 11.07.22 | |||||
| */ | |||||
| require_once('Product.php'); | |||||
| class IsotopeDatabaseHandler | |||||
| { | |||||
| const USER = 'root'; | |||||
| const PASSWORD = ''; | |||||
| const HOST = '127.0.0.1'; | |||||
| const DB_NAME = 'wash_n_roll'; | |||||
| private $dbHandle; | |||||
| public function __construct() | |||||
| { | |||||
| try { | |||||
| $this->dbHandle = new PDO('mysql:host='.self::HOST.';dbname='.self::DB_NAME.';charset=utf8',self::USER, self::PASSWORD); | |||||
| $this->dbHandle->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); | |||||
| } catch (PDOException $e) { | |||||
| print "Error!: " . $e->getMessage() . "<br/>"; | |||||
| die(); | |||||
| } | |||||
| } | |||||
| public function updateProducts(array $products) | |||||
| { | |||||
| /** @var Product $product */ | |||||
| foreach ($products as $product) { | |||||
| $productId = $this->updateProduct($product); | |||||
| $productPriceId = $this->updatePrice($product, $productId); | |||||
| $this->updatePriceTier($product, $productPriceId); | |||||
| } | |||||
| } | |||||
| private function updateProduct(Product $product) | |||||
| { | |||||
| $alias = 'exact-product-'.$product->getCode(); | |||||
| $shippingWeight = $product->getGrossWeight() !== '' ? | |||||
| ($product->getNetWeight() !== '' ? $product->getNetWeight() : '') : ''; | |||||
| $sql = | |||||
| "INSERT into `tl_iso_product` ( | |||||
| pid, | |||||
| gid, | |||||
| tstamp, | |||||
| language, | |||||
| dateAdded, | |||||
| type, | |||||
| fallback, | |||||
| alias, | |||||
| gtin, | |||||
| sku, | |||||
| name, | |||||
| description, | |||||
| meta_title, | |||||
| baseprice, | |||||
| shipping_weight, | |||||
| shipping_exempt, | |||||
| shipping_pickup, | |||||
| shipping_price, | |||||
| protected, | |||||
| guests, | |||||
| cssID, | |||||
| published, | |||||
| start, | |||||
| stop | |||||
| ) VALUES ( | |||||
| 0, | |||||
| 0, | |||||
| ".time().", | |||||
| '', | |||||
| ".time().", | |||||
| 2, | |||||
| '', | |||||
| '$alias', | |||||
| '', | |||||
| '".$product->getCode()."', | |||||
| '".$product->getDescription()."', | |||||
| '".$product->getExtraDescription()."', | |||||
| '', | |||||
| '', | |||||
| '$shippingWeight', | |||||
| '', | |||||
| '', | |||||
| 0.00, | |||||
| '', | |||||
| '', | |||||
| '', | |||||
| '0', | |||||
| '', | |||||
| '' | |||||
| )"; | |||||
| $this->dbHandle->query($sql); | |||||
| return $this->dbHandle->lastInsertId(); | |||||
| } | |||||
| private function updatePrice(Product $product, $productDbId) | |||||
| { | |||||
| $sql = | |||||
| "INSERT INTO `tl_iso_product_price` ( | |||||
| pid, | |||||
| tstamp, | |||||
| tax_class, | |||||
| config_id, | |||||
| member_group, | |||||
| start, | |||||
| stop | |||||
| ) VALUES ( | |||||
| $productDbId, | |||||
| ".time().", | |||||
| 1, | |||||
| 0, | |||||
| 0, | |||||
| '', | |||||
| '' | |||||
| )"; | |||||
| $this->dbHandle->query($sql); | |||||
| return $this->dbHandle->lastInsertId(); | |||||
| } | |||||
| private function updatePriceTier(Product $product, $productPriceDbId) | |||||
| { | |||||
| $price = $product->getStandardSalesPrice() * (1 + Product::VAT); | |||||
| $sql = | |||||
| "INSERT INTO `tl_iso_product_pricetier` ( | |||||
| pid, | |||||
| tstamp, | |||||
| min, | |||||
| price | |||||
| ) VALUES ( | |||||
| $productPriceDbId, | |||||
| ".time().", | |||||
| 1, | |||||
| $price | |||||
| )"; | |||||
| $this->dbHandle->query($sql); | |||||
| return $this->dbHandle->lastInsertId(); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,271 @@ | |||||
| <?php | |||||
| /** | |||||
| * @author Daniel Knudsen <d.knudsen@spawntree.de> | |||||
| * @date 11.07.22 | |||||
| */ | |||||
| class Product | |||||
| { | |||||
| const VAT = 0.19; | |||||
| private $id; | |||||
| private $standardSalesPrice; | |||||
| private $salesVatCode; | |||||
| private $salesVatCodeDescription; | |||||
| private $description; | |||||
| private $extraDescription; | |||||
| private $isWebShopItem; | |||||
| private $itemGroup; | |||||
| private $itemGroupCode; | |||||
| private $stock; | |||||
| private $netWeight; | |||||
| private $grossWeight; | |||||
| private $code; | |||||
| public static function getFields() | |||||
| { | |||||
| return [ | |||||
| 'ID', | |||||
| 'StandardSalesPrice', | |||||
| 'SalesVatCode', | |||||
| 'SalesVatCodeDescription', | |||||
| 'Description', | |||||
| 'ExtraDescription', | |||||
| 'IsWebshopItem', | |||||
| 'ItemGroup', | |||||
| 'ItemGroupCode', | |||||
| 'Stock', | |||||
| 'NetWeight', | |||||
| 'GrossWeight', | |||||
| 'Code', | |||||
| ]; | |||||
| } | |||||
| public static function createProductFromApiItem(array $item) | |||||
| { | |||||
| $res = new self(); | |||||
| $res->setId($item['ID']); | |||||
| $res->setStandardSalesPrice($item['StandardSalesPrice']); | |||||
| $res->setSalesVatCode($item['SalesVatCode']); | |||||
| $res->setSalesVatCodeDescription($item['SalesVatCodeDescription']); | |||||
| $res->setDescription($item['Description']); | |||||
| $res->setExtraDescription($item['ExtraDescription']); | |||||
| $res->setIsWebShopItem($item['IsWebshopItem']); | |||||
| $res->setItemGroup($item['ItemGroup']); | |||||
| $res->setItemGroupCode($item['ItemGroupCode']); | |||||
| $res->setStock($item['Stock']); | |||||
| $res->setNetWeight($item['NetWeight']); | |||||
| $res->setGrossWeight($item['GrossWeight']); | |||||
| $res->setCode($item['Code']); | |||||
| return $res; | |||||
| } | |||||
| /** | |||||
| * @return mixed | |||||
| */ | |||||
| public function getId() | |||||
| { | |||||
| return $this->id; | |||||
| } | |||||
| /** | |||||
| * @param mixed $id | |||||
| */ | |||||
| public function setId($id) | |||||
| { | |||||
| $this->id = $id; | |||||
| } | |||||
| /** | |||||
| * @return mixed | |||||
| */ | |||||
| public function getStandardSalesPrice() | |||||
| { | |||||
| return $this->standardSalesPrice; | |||||
| } | |||||
| /** | |||||
| * @param mixed $standardSalesPrice | |||||
| */ | |||||
| public function setStandardSalesPrice($standardSalesPrice) | |||||
| { | |||||
| $this->standardSalesPrice = $standardSalesPrice; | |||||
| } | |||||
| /** | |||||
| * @return mixed | |||||
| */ | |||||
| public function getSalesVatCode() | |||||
| { | |||||
| return $this->salesVatCode; | |||||
| } | |||||
| /** | |||||
| * @param mixed $salesVatCode | |||||
| */ | |||||
| public function setSalesVatCode($salesVatCode) | |||||
| { | |||||
| $this->salesVatCode = $salesVatCode; | |||||
| } | |||||
| /** | |||||
| * @return mixed | |||||
| */ | |||||
| public function getSalesVatCodeDescription() | |||||
| { | |||||
| return $this->salesVatCodeDescription; | |||||
| } | |||||
| /** | |||||
| * @param mixed $salesVatCodeDescription | |||||
| */ | |||||
| public function setSalesVatCodeDescription($salesVatCodeDescription) | |||||
| { | |||||
| $this->salesVatCodeDescription = $salesVatCodeDescription; | |||||
| } | |||||
| /** | |||||
| * @return mixed | |||||
| */ | |||||
| public function getDescription() | |||||
| { | |||||
| return $this->description; | |||||
| } | |||||
| /** | |||||
| * @param mixed $description | |||||
| */ | |||||
| public function setDescription($description) | |||||
| { | |||||
| $this->description = $description; | |||||
| } | |||||
| /** | |||||
| * @return mixed | |||||
| */ | |||||
| public function getExtraDescription() | |||||
| { | |||||
| return $this->extraDescription; | |||||
| } | |||||
| /** | |||||
| * @param mixed $extraDescription | |||||
| */ | |||||
| public function setExtraDescription($extraDescription) | |||||
| { | |||||
| $this->extraDescription = $extraDescription; | |||||
| } | |||||
| /** | |||||
| * @return mixed | |||||
| */ | |||||
| public function getIsWebShopItem() | |||||
| { | |||||
| return $this->isWebShopItem; | |||||
| } | |||||
| /** | |||||
| * @param mixed $isWebShopItem | |||||
| */ | |||||
| public function setIsWebShopItem($isWebShopItem) | |||||
| { | |||||
| $this->isWebShopItem = $isWebShopItem; | |||||
| } | |||||
| /** | |||||
| * @return mixed | |||||
| */ | |||||
| public function getItemGroup() | |||||
| { | |||||
| return $this->itemGroup; | |||||
| } | |||||
| /** | |||||
| * @param mixed $itemGroup | |||||
| */ | |||||
| public function setItemGroup($itemGroup) | |||||
| { | |||||
| $this->itemGroup = $itemGroup; | |||||
| } | |||||
| /** | |||||
| * @return mixed | |||||
| */ | |||||
| public function getItemGroupCode() | |||||
| { | |||||
| return $this->itemGroupCode; | |||||
| } | |||||
| /** | |||||
| * @param mixed $itemGroupCode | |||||
| */ | |||||
| public function setItemGroupCode($itemGroupCode) | |||||
| { | |||||
| $this->itemGroupCode = $itemGroupCode; | |||||
| } | |||||
| /** | |||||
| * @return mixed | |||||
| */ | |||||
| public function getStock() | |||||
| { | |||||
| return $this->stock; | |||||
| } | |||||
| /** | |||||
| * @param mixed $stock | |||||
| */ | |||||
| public function setStock($stock) | |||||
| { | |||||
| $this->stock = $stock; | |||||
| } | |||||
| /** | |||||
| * @return mixed | |||||
| */ | |||||
| public function getNetWeight() | |||||
| { | |||||
| return $this->netWeight; | |||||
| } | |||||
| /** | |||||
| * @param mixed $netWeight | |||||
| */ | |||||
| public function setNetWeight($netWeight) | |||||
| { | |||||
| $this->netWeight = $netWeight; | |||||
| } | |||||
| /** | |||||
| * @return mixed | |||||
| */ | |||||
| public function getGrossWeight() | |||||
| { | |||||
| return $this->grossWeight; | |||||
| } | |||||
| /** | |||||
| * @param mixed $grossWeight | |||||
| */ | |||||
| public function setGrossWeight($grossWeight) | |||||
| { | |||||
| $this->grossWeight = $grossWeight; | |||||
| } | |||||
| /** | |||||
| * @return mixed | |||||
| */ | |||||
| public function getCode() | |||||
| { | |||||
| return $this->code; | |||||
| } | |||||
| /** | |||||
| * @param mixed $code | |||||
| */ | |||||
| public function setCode($code) | |||||
| { | |||||
| $this->code = $code; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,12 @@ | |||||
| <?php | |||||
| require __DIR__ . '/ApiExact.php'; | |||||
| require __DIR__ . '/IsotopeDatabaseHandler.php'; | |||||
| $apiConnector = new ApiExact(); | |||||
| $databaseHandler = new IsotopeDatabaseHandler(); | |||||
| //echo $apiConnector->getAccessToken() . "\n"; | |||||
| $products = $apiConnector->getProducts(); | |||||
| $databaseHandler->updateProducts($products); | |||||
| $b = 0; | |||||
| @@ -0,0 +1 @@ | |||||
| {"access_token":"stampDE001.gAAAAHwOSGEkvQm9cR0GLsLKi-DF5HztPTxwFs1cElb0YqYwsA4Du2xEN2_QDZQaRP2S7JqojAxH7Pgh9bGX8aMk_w2Mc6E8Za3DfOCs5_IU89TUfIZfo-OlriYaWguDBjIhWDV7uyb0fyMk83Vplk4hffbmyTdvoTA1GbmvO9x-wEWYVAIAAIAAAAAMQCUSoZTK5-qcCHPI6G15WhlRHy2qHRJKJIJ3ugg6F95dz8Wq4oLsgD4Ej7n_9zloDUbYzABmiTMs6vu-wa4R3DjXmDtvZ5tFauTNTvlSVdbo-rT5rbHUo_LcdGTdE18njXZDBpK7K58cQXVq4zGxViUoqzrEu8aiqDhfwI93_pAXr1ABdwTj6sFxzeqRTAwIo2WVBG7uwzvYLQCrCUqpzxfxUgwH0FBbjEzcglBx56hNshomL4SNqbqqGIKaK1lNGOKz0jkxnuRdY0-nlBPsi0FN6goQDAE3wfKUFzj8DU0nyQAZhdedAtk4q3AT1mPnM9wuOxIwp3EXDeuYkiCIQNWSvU7WJJaWMqfRPrab86diQ5ffBxWDVc0HnpHgQVWr0peKvzLXS4lx5_pTgwLaJUurvlRbG0LI88x-qcMmGgbj2a54FaF93RrNOtHaheiGZh_CBDwqbT54vrFaxxwp-yNmYLVk2-2c70RUwobUInw9E_eR2RNPU9oHGQTMTQR9at_3jZ5dR68UyrY6BMCGJrqrTvSpXkmJNKhkjP9WHlWtz-dC-gt_m2cYaWifow76NAKHONyRk3-i6Qu3vKDc8vXyyi-RLeX6R0nLYV07p7yHsgSo2E8em5C6KdkHIQjJIanYyHUVf7noHJMuKIrrvaHBfdTuiC1ZbemwzPCsb7sdOftAb5vW3oxSEMmckuCVEROmeTyoZCDAV5oyecNAah73Sx7WLx87npWFhC0F71myL11vkWf5MSt8lQ-o-kZCt0Wws7h9KStVrb3mux9v","token_type":"bearer","expires_in":"600","refresh_token":"stampDE001.vhK0!IAAAANf_ePo7TNLoW4eBzZwdyynZfR7KhO8joJp6EgWw0pF58QEAAAFBqPStZfQ04DNGVJ23edCAkbZsdhwNjz3GfWOQoMaGhf3uoG8iydpqma1dDKY3MmFI5CNNxIKvpiN0jnvHqgi0g6O5QijAmgtB3_sVmzs4L0ODxGt3Befp2rJKu2MMBDySEJ_00XVZzBIKmZqVA6XUflZ7mfbotp22vTRbthYOMZlyz0OIT1leJKHfU8XYgB5Zzp8gPHxsSca93Kh3_3CQRp0ZoGR5YKSh1C2zw8QyVCBU_092gLonzTGHKlaIUd6nVT4oIhzXC9i9Dh7wL-9PSfk7m9OqJ7wX0h-p8wkO4bqjkSJofeglSrAvS3G1U8yo0QpxmJQW1FHsm-7q1s6w4O03LV9LsSZX93Wjm8mMq789tlZFZh7-MqO5_zQcwmgizMVDMbBsxPUMU1ITOrTYBAalNf9kyLvrGE-M4jhK_qQbGQ3FATy25U3QjoEQC9K_PWNyqY2XrzPDHSAoprI6hZxY9lYY71tDg3L10KB6BFXDM6bDj_oWa0BAI-TfO_cKRPhXxt9bAquWxhLYHNFdV-d0Il7FeRgI_Dmp7OsKYuFw6rrd6C8Vuz1AxuTaNT1G3V-ugUn5L-VV12JiLgMTTrBLK6OGh5fooDE3Li8tM7WiA5-K9HgP3Jsesdrl2O7ClBUFRrrhtiQINNgkLN1f","expiry_time":1657581261} | |||||