Daniel 3 lat temu
rodzic
commit
6e8029669a
19 zmienionych plików z 5126 dodań i 10 usunięć
  1. +0
    -2
      api/IsotopeDatabaseHandler.php
  2. +1
    -1
      api/Product.php
  3. +106
    -6
      api/listener/PostCheckoutListener.php
  4. +1
    -0
      api/listener/tokenData
  5. +1
    -1
      api/tokenData
  6. +441
    -0
      api2/Address.php
  7. +178
    -0
      api2/ApiExact.php
  8. +197
    -0
      api2/IsotopeDatabaseHandler.php
  9. +614
    -0
      api2/Order.php
  10. +147
    -0
      api2/PostCheckoutListener.php
  11. +809
    -0
      api2/Product.php
  12. +2097
    -0
      api2/ProductCollection.php
  13. +489
    -0
      api2/ProductCollectionItem.php
  14. +7
    -0
      api2/apiTestCreateCustomer.php
  15. +7
    -0
      api2/apiTestCreateSalesOrder.php
  16. +9
    -0
      api2/apiTestGetAccessToken.php
  17. +10
    -0
      api2/apiUpdateProducts.php
  18. +11
    -0
      api2/dan.txt
  19. +1
    -0
      api2/tokenData

+ 0
- 2
api/IsotopeDatabaseHandler.php Wyświetl plik

@@ -71,8 +71,6 @@ class IsotopeDatabaseHandler
$sql =
"UPDATE `tl_iso_product` SET
tstamp = ".time().",
name = '".$product->getDescription()."',
description = '".$product->getExtraDescription()."',
shipping_weight = '$shippingWeight'
WHERE `id` = ".$productDbId;
$this->dbHandle->query($sql);


+ 1
- 1
api/Product.php Wyświetl plik

@@ -1,6 +1,6 @@
<?php

class Product
class Product
{
const VAT = 0.19;



+ 106
- 6
api/listener/PostCheckoutListener.php Wyświetl plik

@@ -1,22 +1,122 @@
<?php
/**
* @author Daniel Knudsen <d.knudsen@spawntree.de>
* @date 12.07.22
*/

namespace App\EventListener;


use Contao\CoreBundle\ServiceAnnotation\Hook;
use Contao\FrontendTemplate;
use Contao\Module;
use App\ExactApi\ApiExact;
use Isotope\Model\Address;
use Isotope\Model\Product;
use Isotope\Model\ProductCollection\Order;
use Isotope\Model\ProductCollectionItem;

class PostCheckoutListener
{
const WAREHOUSE_ID = 'd8a6a9b8-d0ac-4d36-8d00-bd420d0d81f5';

/**
* @Hook("postCheckout")
*/
public function __invoke(int $userId, array $userData, Module $module): void
// public function __invoke(int $userId, array $userData, Module $module): void
public function __invoke(Order $order, $item): void
{
$apiExact = new ApiExact();

//ob_start();
$salesOrderItems = [];
/** @var ProductCollectionItem $orderItem */
foreach($order->getItems() as $orderItem) {
$salesOrderItems[] = $this->createOrderItem($orderItem);
// /** @var Product $orderItem */
// $product = $orderItem->getProduct();
// var_dump($orderItem->id);
// var_dump($orderItem->sku);
// var_dump($orderItem->quantity);
// var_dump($orderItem->tax_free_price);
//
// var_dump($product->id);
// var_dump($product->name);
// var_dump($product->is_set);

}
//var_dump($apiExact->test());
// file_put_contents('dan.txt', ob_get_contents());
// ob_end_clean();



$shippingCustomer = null;
// if ($this->compareAddresses() === false) {
// $shippingCustomer = $apiExact->createCustomer(
// $this->createCustomerApiData($order->getShippingAddress())
// );
// }
//
$billingCustomer = $apiExact->createCustomer(
$this->createCustomerApiData($order->getBillingAddress())
);
$apiExact->createSalesOrder($this->createSalesOrderData($order, $billingCustomer, $shippingCustomer));

}

private function compareAddresses(Order $order)
{
throw new \Exception('ARRRRRRRGGGGGGGGGG');
return
$order->getBillingAddress()->firstname === $order->getShippingAddress()->firstname &&
$order->getBillingAddress()->lastname === $order->getShippingAddress()->lastname &&
$order->getBillingAddress()->street_1 === $order->getShippingAddress()->street_1 &&
$order->getBillingAddress()->postal === $order->getShippingAddress()->postal &&
$order->getBillingAddress()->city === $order->getShippingAddress()->city &&
$order->getBillingAddress()->company === $order->getShippingAddress()->company &&
$order->getBillingAddress()->phone === $order->getShippingAddress()->phone &&
$order->getBillingAddress()->email === $order->getShippingAddress()->email;
}

private function createCustomerApiData(Address $address)
{
return [
'Name' => $address->firstname . ' ' . $address->lastname,
'AddressLine1' => $address->street_1,
'AddressLine2' => $address->company,
'City' => $address->city,
'Postcode' => $address->postal,
'Email' => $address->email,
'Country' => 'DE',
'Phone' => $address->phone,
'Status' => 'C',
];
}

private function createOrderItem(ProductCollectionItem $orderItem)
{
/** @var Product $orderItem */
$product = $orderItem->getProduct();
return [
'Description' => $product->name,
'Item' => $product->exact_id,
'UnitPrice' => (float) $orderItem->tax_free_price / (int) $orderItem->quantity,
'Quantity' => $orderItem->quantity
];
}

private function createSalesOrderData(Order $order, $billingCustomer, $salesOrderItems, $shippingCustomer = null)
{
$date = new \DateTime('now');
$res = [
'Description' => $billingCustomer['Name'],
'OrderDate' => $date->format('m/d/Y H:i:s'),
'OrderedBy' => $billingCustomer['ID'],
'WarehouseID' => self::WAREHOUSE_ID,
'SalesOrderLines' => [
$salesOrderItems
]
];
if ($shippingCustomer !== null) {
$res['DeliverTo'] = $shippingCustomer['ID'];
}

return $res;
}
}

+ 1
- 0
api/listener/tokenData Wyświetl plik

@@ -0,0 +1 @@
{"access_token":"stampDE001.gAAAADRG83iY-fhuf-2rSWvPzCjE4DL5hlcUcyDpkEk4rP6B5FF9PnD3qKjveOit6vWjnxeM0UzXu1kUOX0cR0AV0jf16XSQy_-3nQ6XsTx5wIucuNmT52WpjP7-LU2E3TzSvjVilP-ufMtgTLnmuLvtC7ALRoAcoiNrHuU6sKACxco0VAIAAIAAAABYJx_THjmOsX2jvj-lBpx0NXCBxY4506xGpS8bmvY0JoYWsebqFuORCSupfTU3VebYy-Vn_oyv7nUkBwPpsmLUVmJWJB-VX5X9dTj23-tJMYkUxjNzvdrbbTdnwuNbIFYws_5bwsJ-1NiP9nZJYllsGstqbvUyfGmsnXrBSyX6eujzHH7eWOEfL7wHIfVbl2Eg2HwP1w3vMzUgG9h-P7OCw69-EpsfJVaOKYWL_F39zic6yBf0oWDSEBvsbIeL2o4KdkCSJD0NY0bqGViDfd_vJVfXP9lossW9fu-rtQNehko5GSkEybe4x30kDbUZHqB_LhL8a5_b9zqPF79-iDxN_vuY_FDCopvBBNUjcyeDGZcn_GDpzqEhK4BxIqJFWlw2umG4qd61OB3WkSXv952cPKBsVQJWoZWrudQymHa6WLy27DXNoyS3WJiO2VnyZybszl0Mk1G6_ZHso4PmgEsOdM2RyrsGaN7_pOoIyOG7c2ZlBLJTM_q_lDVVaAfPrNcrBon2UTFDqUtexlrTpt50nEjBUlxOyrDva1KOGyfHrHdMGEpetr6YoEmAbcMM6jId72OHLy2ztfoYL91kzXScnEAJB3PL3-btD85hU8rBaFYAgZemoPY0T4g6ZqBcYLXoMDT5ltW2McI74u819Q1V7ZrAPkVB2oICaFppnKzOe9ot7DChNk5nrNFUaK49hosQpPtTUzPyfPb2oX3Z5GhjHe2X4b6wT4DiSORY6gtu7ZdPyJeDWnoyCE_c1m9PLj_v4i6cyfWMJHbIaCHLuZGU","token_type":"bearer","expires_in":"600","refresh_token":"stampDE001.vhK0!IAAAAG4XGo_RV3d60tiy1gRnVAQrHp9xnEP4_CuBLQ_D5Z0C8QEAAAFnedzeXXOKto6NMohmWMQOWm7Qptgu2o0z6LYlWxTRWf9IPpRYC-fa5XigKwDbB5lquTf8gj90Fg0YkuniSwf9jIBUjBOzul0ZdAPWdolHZLAQtHQr3a53ylG4d7yLyrQCMctPl8yUHNWk1e5PH4YCOZH9lKTW7qWlt1SJAfLcgPa2F0I-9XnS-n-AZK7bxpCL4coztVGCA6vP9igYofpArxYc7W-9rNbzTOyF-fxp_hXzTtCaPetA5Zpg8v-4sx3Mvx1AY_NscwCcG-V1mJnolxlLowgL8fBeUdv9WrWrx5uHVqcKyhAYCB7BR_QqZfgl5mlv_62hbK2s_HKNcQsG591qymyG5KYjOlpSlKyEe3h-hGYaP3PaD-xiUx87C9Z3iRPKWE79i_9B6H4rCcltIJARKp3UGUJbJtYikzeexB7wCM3uHkcljXziwXkBO9PIVWTJhsauv7BTSAmyODuuSSbL1PTGHPIJJdRCQUn8U0xOlSvWWngpLD9CenpAiMRt8-uFfC3zAOBxIHZ5FNDuNlcvKhgEG0Q-94qFySKFt1J5xCUCKpKZ1VQyDz33uylXzb30SoGLmAIPVPhMFdJo08xIAFZaPRQKsvqOEj7M4t3KeGYkehyAv7ugQcJrlWo5TD9kYlHA1CRZs5YiK2a7","expiry_time":1657816808}

+ 1
- 1
api/tokenData Wyświetl plik

@@ -1 +1 @@
{"access_token":"stampDE001.gAAAAKLeomGUJnO_59CX7KmwE4tkt1bPxLR7EfU5E2eHjj6LeU2dAPfkRstKdlJJoudf3y6ehyKwF5wQQ1SljbA5sRrGwQsw4M876o0RRtxVHwyR6_QSoM7kP3qj7HfJLEDWjY-KrlJf_rlKtZZ2sRGJkX9jbd-4XDA_uqQECkf2NnIfVAIAAIAAAAC2ADvWhSjIjtyvs4_rhH9HzqaVKUbQthUoKf6StMSX4O5GzilHhM2cQCQJly_7hbpKHJMe4o167cPYIQLTeOilQdlQh21nhGykWfOKtXG31h210WtT2bg8Kc0MtrHQGi_f161wE_xINPXr-H7GTcXLe4klIGUkv0kmmmUacNoDUDfU1fPRTCCFvmkRnE7iBzd116aY9XMVw6mDucFExI9U-dLOD8IFWCPa9U6DSd50T7WoglDHC8Enh-J92C1c8kAhCCiGyA5GHyb9S1eJAnHFDYzOl9y0pwtIHekBJq-LxpzAbVieBUZrXEsTJnD7eiHvKoy9g_Bl-jOVgPTJT29n0dFDUClE_iQ6MGydAiEVKoyhQO20W7EJg64G8l6ZHZrd8r1J36TZ4jmdZ8E8_wqw3MzYGZm4vEXK4G0Lf2-j5uD0eh3aKPQCCdn_jEiCaEABQ-bsvd9sQazNNHbDwzl82GZdlhY8sqln14NpNloahOxhY9-jKu5nT9IBN2EftT-VoLNWp_E4ydPltM_7SS0Xro9j8nf7xNjpBDa__GeR2QSg_He5VR6u2n4lUbU1Qzks2W38RiCA3jXrQ-T6FCtiiaf5rhMzudqeq2c94Ggbn1g7BD_3S9xUFeA5kmSCkn1NZ3RzSqmkMq4IVPcbsJFYeoCiuZ1gYcvSOoBMHN8mjZ8j3xpPEvzJSX39arD5IeNqrLZDXuEU5aORiRnqjN6kPC4w1GQbbMsEL94r8tYGy8VkW7bstxkar7vb2J8vOPhLamN0mcxJdTZCCJ8tj18e","token_type":"bearer","expires_in":"600","refresh_token":"stampDE001.vhK0!IAAAANKu8TC2-MBN5wFyro7uwanpxXBsRFy7YkYSPaM9jMCm8QEAAAEVEJUYfzoc3Q1hatgTMAOm__pmwjTgdkOyzb7qUj7oRvir1XftD_cb5eE7J-06Q0SxWegFPfULR65PDc6Id0pDXZbugqaKhK24Gg9Gr0x74g4EW8HMYJ0PsW1uqvpOb_ph9mQxYllDQf598_uPZvuvxAMN4NOipKiHEXn9tExbn-rmJdrV9bwa3fAm5b2ALbzfBEKCuzuwLkrjH3cheaMC2TaksJ6M2we9IeAezapsyOVEl6QtnhY67L3Irp_bUV_SCZ6H-gbPDX3mFujxyw-lvRoC-ZcgIpM8EuWC3pN24abFp7Qaj8wE2X9_oAgvoJgn42lHl-x6M-QChdf78f6wiCPquk3OwwuBYsjmiJrHFQt4a6nRS-Y_A-J8IOdwq-_KZkvNTY5UI1ZmiGH5pvIDfaXTcvPn6r3C-sQecdImJM43Y_tgZ9a6p2gwPi8zNZSkXfwQH5bfal6tBcH43_8eGSp_M-VbLb7TotZfJgEIePSlZ--VV1aTaRMJ78_JBloFWvQabvhYEvvuvRaePbdGRgXzj95KgQeW8DzBXE6_zHSmhwkUEv11xgXimH3BFToSV_F25ZND7H3OkVM1-M8Jhbvrzh4SR5wRV5WfzC83Mk58HmDtI7xR0owYAu-9mtidFsK8mRC3nJzZSOWdGenZ","expiry_time":1657726518}
{"access_token":"stampDE001.gAAAAATOqLu_FvySDkT1YxBYQOSbyTqVC-jQWfCBmfomFxKEuiSUp2H5XYFwHOjuZ_cbURYObp_CuoijjuXPHYAvgR5GjxZVLpnI33116cIBqeKkdnQ2617l-DfjsiPlSO9ilA5CtrV75A1ZhxtWJqJ5uZynH0eT0ZB8LytI81Fg2p4tVAIAAIAAAAAqO6Xcq6ab64TEJDE2QyPHpfueoxrCkLlFVkxbiXgoANGrrSTu9qQtOTYyszkuc3sGZSdg1zD8eJLx8sL0zlTy7BYRYgTmiUhLZhECjG_7PfpObMuaz_Q4NOlIeyGOX23B6p-lbSZ_HHcVx1XzDgSry7MSIcFZGIqUWqr4jWW6EDqU0Q9A1ypbkKAPxh77V68LrHAzIm8yjoQell4GBnNSVOwNWKcLgmqkznUIsywr-Kq6dvfs_Ziy0nu5DR6PbpYLIVY4g1eCE4ncfgtPYsndKKuSIOisf8pYn_7NaLfSxvYWVwFxuTHw3roRufszbE-toAsUzfBVv2mQjKdyop9xRri3Adh3vfsEAKEyksZdsduCjFEPu6KoSv_pgQfExSURkHKj0gOX8_GjPtcbT4OWZ3JuZ7n9-4U5uSV-HHOvd7T2ojgQ0lFcVukuwHq086KTMNGzcBPfM-_vcBYAx1nRRpupQL2Np0fm85iycZwycSKAFydpcMeB1VaP5PYkVLiLOsXBPy4SMM0_-c51NykUm338m8SwAVeVQXN3VYcT0kJwUsyMdOtYoDvXUL-5p_NafKwwPwdrgpIwnjjO3s37O43RsGWaY8kp42y2I4lQbMwcqTde_bOcPCRQWium4OB__fkFaiCJ5eE5EGcbPUVzjEfW_Lhr8yrkRacFfut9YqCTVuGxZepi0eyX3FHk_-qAtXTBav11EZ40G5slokVlyPu63pQfhpSrsNIGZv-j5YIsN5MqjHn7HgtbS0UMRUAQERHvDkz6ddfbkqwjOgYE","token_type":"bearer","expires_in":"600","refresh_token":"stampDE001.vhK0!IAAAAIMZSdkYxeTLXpkXHeNZCQAgV_DZ3JM_sqN9Y81kYggu8QEAAAE1Ecbn6_tJNnyZQn4ktg02ypEuBII4NQc3bgVgWB1lM0IZaxRyKB5C9AVOCqh9ewKECN3eNMP0C0P2QVzNyoBsOHrK_QHqqypFPBfs2fPG2psKUngqf4nTWfGWpjnaeevGqll88v4_Cka58o-Kmlrv2Ho_uc3Tjl4gE6GeLF870gyWnW1Qx3AJUTx4730yswUr1YwEODVTl0WBSJ2LEqELKvx4MQ603b642QkevFCrH6ttZBef-ufEKcRpT7Fic9JHcSdUEJCGrhsGe__Ah0dS2WtxNfhD3m7YgGpxBqSjCJXWvXzBpBd9UsZ6vCuF0CP2EVvSZ_HS3iTgD5UOBDoQkTn8kt65TixrpQWZE5j2-vfgoQy9CbPv8aO75XT4gZGPtjij7GAdXNc17ZH-P_3N5CFmLnycGyfPNdnS58PP4nkYcl6LzVF5pBOBj2mIZa9Ic7W1fcSWDePaGC_WjVQvuV18tjvsMxp738XNTPdZfta5V769cSq1tuRblIyLYIJhhYhmprqg1KADYkcNC7RHYSVOHbds83VORW9gvgjFLCWt15TLhJaGbydgaMMUGHlZ4gb8YFMCzbRiq_20_K8iP_J5BdP2omIeHCNnH6360wmJybbgcFlYv0FZNDXUwkRxaTsREpfSvDUzZHCsk0Od","expiry_time":1657818416}

+ 441
- 0
api2/Address.php Wyświetl plik

@@ -0,0 +1,441 @@
<?php

/*
* Isotope eCommerce for Contao Open Source CMS
*
* Copyright (C) 2009 - 2019 terminal42 gmbh & Isotope eCommerce Workgroup
*
* @link https://isotopeecommerce.org
* @license https://opensource.org/licenses/lgpl-3.0.html
*/

namespace Isotope\Model;

use Contao\Controller;
use Contao\MemberModel;
use Contao\Model;
use Contao\StringUtil;
use Contao\System;
use Database\Result;
use Haste\Util\Format;
use Isotope\Backend;
use Isotope\Interfaces\IsotopeProductCollection;
use Isotope\Interfaces\IsotopeVatNoValidator;
use Isotope\Isotope;


/**
* Class Address
*
* @property int $id
* @property int $pid
* @property string $ptable
* @property string $label
* @property int $store_id
* @property string $gender
* @property string $salutation
* @property string $firstname
* @property string $lastname
* @property int $dateOfBirth
* @property string $company
* @property string $vat_no
* @property string $street_1
* @property string $street_2
* @property string $street_3
* @property string $postal
* @property string $city
* @property string $subdivision
* @property string $country
* @property string $phone
* @property string $email
* @property bool $isDefaultShipping
* @property bool $isDefaultBilling
*/
class Address extends Model
{

/**
* Table
* @var string
*/
protected static $strTable = 'tl_iso_address';

/**
* Construct the model
*
* @param Result $objResult
*/
public function __construct(Result $objResult = null)
{
parent::__construct($objResult);

if (!\is_array($GLOBALS['ISO_ADR'] ?? null)) {
Controller::loadDataContainer(static::$strTable);
System::loadLanguageFile('addresses');
}
}

/**
* @return string
*/
public function __toString()
{
try {
return $this->generate();
} catch (\Exception $e) {
return '';
}
}

/**
* Check if the address has a valid VAT number
*
* @param Config $config
*
* @return bool
*
* @throws \LogicException if a validator does not implement the correct interface
* @throws \RuntimeException if a validators reports an error about the VAT number
*/
public function hasValidVatNo(Config $config = null)
{
if (null === $config) {
$config = Isotope::getConfig();
}

$validators = StringUtil::deserialize($config->vatNoValidators);

// if no validators are enabled, the VAT No is always valid
if (!\is_array($validators) || 0 === \count($validators)) {
return true;
}

foreach ($validators as $class) {
$service = new $class();

if (!($service instanceof IsotopeVatNoValidator)) {
throw new \LogicException($class . ' does not implement IsotopeVatNoValidator interface');
}

$result = $service->validate($this);

if (true === $result) {
return true;
}
}

return false;
}

/**
* Return formatted address (hCard)
*
* @param array $arrFields
*
* @return string
*
* @throws \Exception on error parsing simple tokens
*/
public function generate($arrFields = null)
{
// We need a country to format the address, use default country if none is available
$strCountry = $this->country ?: Isotope::getConfig()->country;

// Use generic format if no country specific format is available
$strFormat = $GLOBALS['ISO_ADR'][$strCountry] ?? $GLOBALS['ISO_ADR']['generic'];

$arrTokens = $this->getTokens($arrFields);

return StringUtil::parseSimpleTokens($strFormat, $arrTokens);
}

/**
* Return this address formatted as text
*
* @param array $arrFields
*
* @return string
*
* @deprecated use Address::generate() and strip_tags
* @throws \Exception on invalid simple tokens
*/
public function generateText($arrFields = null)
{
return strip_tags($this->generate($arrFields));
}

/**
* Return an address formatted with HTML (hCard)
*
* @param array $arrFields
*
* @return string
*
* @deprecated use Address::generate()
* @throws \Exception on invalid simple tokens
*/
public function generateHtml($arrFields = null)
{
return $this->generate($arrFields);
}

/**
* Compile the list of hCard tokens for this address
*
* @param array $arrFields
*
* @return array
*/
public function getTokens($arrFields = null)
{
if (!\is_array($arrFields)) {
$arrFields = Isotope::getConfig()->getBillingFieldsConfig();
}

$arrTokens = array('outputFormat' => 'html');

foreach ($arrFields as $arrField) {
$strField = $arrField['value'];

// Set an empty value for disabled fields, otherwise the token would not be replaced
if (!$arrField['enabled']) {
$arrTokens[$strField] = '';
continue;
}

if ('subdivision' === $strField && $this->subdivision != '') {
[$country, $subdivision] = explode('-', $this->subdivision);

$arrTokens['subdivision_abbr'] = $subdivision;
$arrTokens['subdivision'] = Backend::getLabelForSubdivision($country, $this->subdivision);

continue;
}

$arrTokens[$strField] = Format::dcaValue(static::$strTable, $strField, $this->$strField);
}


/**
* Generate hCard fields
* See http://microformats.org/wiki/hcard
*/

// Set "fn" (full name) to company if no first- and lastname is given
if ($arrTokens['company'] != '') {
$fn = $arrTokens['company'];
$fnCompany = ' fn';
} else {
$fn = trim($arrTokens['firstname'] . ' ' . $arrTokens['lastname']);
$fnCompany = '';
}

$street = implode('<br>', array_filter([$this->street_1, $this->street_2, $this->street_3]));

$arrTokens += [
'hcard_fn' => $fn ? '<span class="fn">' . $fn . '</span>' : '',
'hcard_n' => ($arrTokens['firstname'] || $arrTokens['lastname']) ? '1' : '',
'hcard_honorific_prefix' => $arrTokens['salutation'] ? '<span class="honorific-prefix">' . $arrTokens['salutation'] . '</span>' : '',
'hcard_given_name' => $arrTokens['firstname'] ? '<span class="given-name">' . $arrTokens['firstname'] . '</span>' : '',
'hcard_family_name' => $arrTokens['lastname'] ? '<span class="family-name">' . $arrTokens['lastname'] . '</span>' : '',
'hcard_org' => $arrTokens['company'] ? '<div class="org' . $fnCompany . '">' . $arrTokens['company'] . '</div>' : '',
'hcard_email' => $arrTokens['email'] ? '<a href="mailto:' . $arrTokens['email'] . '">' . $arrTokens['email'] . '</a>' : '',
'hcard_tel' => $arrTokens['phone'] ? '<div class="tel">' . $arrTokens['phone'] . '</div>' : '',
'hcard_adr' => ($street || $arrTokens['city'] || $arrTokens['postal'] || $arrTokens['subdivision'] || $arrTokens['country']) ? '1' : '',
'hcard_street_address' => $street ? '<div class="street-address">' . $street . '</div>' : '',
'hcard_locality' => $arrTokens['city'] ? '<span class="locality">' . $arrTokens['city'] . '</span>' : '',
'hcard_region' => $arrTokens['subdivision'] ? '<span class="region">' . $arrTokens['subdivision'] . '</span>' : '',
'hcard_region_abbr' => !empty($arrTokens['subdivision_abbr']) ? '<abbr class="region" title="' . $arrTokens['subdivision'] . '">' . $arrTokens['subdivision_abbr'] . '</abbr>' : '',
'hcard_postal_code' => $arrTokens['postal'] ? '<span class="postal-code">' . $arrTokens['postal'] . '</span>' : '',
'hcard_country_name' => $arrTokens['country'] ? '<div class="country-name">' . $arrTokens['country'] . '</div>' : '',
];

return $arrTokens;
}

/**
* Find address for member, automatically checking the current store ID and tl_member parent table
*
* @param int $intMember
* @param array $arrOptions
*
* @return \Model\Collection|null
*/
public static function findForMember($intMember, array $arrOptions = array())
{
return static::findBy(
array('pid=?', 'ptable=?', 'store_id=?'),
array($intMember, 'tl_member', Isotope::getCart()->store_id),
$arrOptions
);
}

/**
* Find address by ID and member, automatically checking the current store ID and tl_member parent table
*
* @param int $intId
* @param int $intMember
* @param array $arrOptions
*
* @return Address|null
*/
public static function findOneForMember($intId, $intMember, array $arrOptions = array())
{
return static::findOneBy(
array('id=?', 'pid=?', 'ptable=?', 'store_id=?'),
array($intId, $intMember, 'tl_member', Isotope::getCart()->store_id),
$arrOptions
);
}

/**
* Find default billing adddress for a member, automatically checking the current store ID and tl_member parent table
* @param int
* @param array
* @return static|null
*/
public static function findDefaultBillingForMember($intMember, array $arrOptions = array())
{
return static::findOneBy(
array('pid=?', 'ptable=?', 'store_id=?', 'isDefaultBilling=?'),
array($intMember, 'tl_member', Isotope::getCart()->store_id, '1'),
$arrOptions
);
}

/**
* Find default shipping adddress for a member, automatically checking the current store ID and tl_member parent table
* @param int
* @param array
* @return static|null
*/
public static function findDefaultShippingForMember($intMember, array $arrOptions = array())
{
return static::findOneBy(array('pid=?', 'ptable=?', 'store_id=?', 'isDefaultShipping=?'), array($intMember, 'tl_member', Isotope::getCart()->store_id, '1'), $arrOptions);
}

/**
* Find default billing address for a product collection
*
* @param int $intCollection
* @param array $arrOptions
*
* @return static|null
*/
public static function findDefaultBillingForProductCollection($intCollection, array $arrOptions = array())
{
return static::findOneBy(
array('pid=?', 'ptable=?', 'isDefaultBilling=?'),
array($intCollection, 'tl_iso_product_collection', '1'),
$arrOptions
);
}

/**
* Find default shipping address for a product collection
*
* @param int $intCollection
* @param array $arrOptions
*
* @return static|null
*/
public static function findDefaultShippingForProductCollection($intCollection, array $arrOptions = array())
{
return static::findOneBy(
array('pid=?', 'ptable=?', 'isDefaultShipping=?'),
array($intCollection, 'tl_iso_product_collection', '1'),
$arrOptions
);
}

/**
* Create a new address for a member and automatically set default properties
*
* @param int $intMember
* @param array|null $arrFill
*
* @return static
*/
public static function createForMember($intMember, $arrFill = null)
{
$objAddress = new static();

$arrData = array(
'pid' => $intMember,
'ptable' => 'tl_member',
'tstamp' => time(),
'store_id' => (int) Isotope::getCart()->store_id,
);

if (!empty($arrFill) && \is_array($arrFill) && ($objMember = MemberModel::findByPk($intMember)) !== null) {
$arrData = array_merge(static::getAddressDataForMember($objMember, $arrFill), $arrData);
}

$objAddress->setRow($arrData);

return $objAddress;
}

/**
* Create a new address for a product collection
*
* @param IsotopeProductCollection $objCollection
* @param array|null $arrFill an array of member fields to inherit
* @param bool $blnDefaultBilling
* @param bool $blnDefaultShipping
*
* @return static
*/
public static function createForProductCollection(
IsotopeProductCollection $objCollection,
$arrFill = null,
$blnDefaultBilling = false,
$blnDefaultShipping = false
) {
$objAddress = new static();

$arrData = array(
'pid' => $objCollection->getId(),
'ptable' => 'tl_iso_product_collection',
'tstamp' => time(),
'store_id' => $objCollection->getStoreId(),
'isDefaultBilling' => $blnDefaultBilling ? '1' : '',
'isDefaultShipping' => $blnDefaultShipping ? '1' : '',
);

if (!empty($arrFill) && \is_array($arrFill) && ($objMember = $objCollection->getMember()) !== null) {
$arrData = array_merge(static::getAddressDataForMember($objMember, $arrFill), $arrData);
}

if (empty($arrData['country']) && null !== ($objConfig = $objCollection->getConfig())) {
if ($blnDefaultBilling) {
$arrData['country'] = $objConfig->billing_country ?: $objConfig->country;
} elseif ($blnDefaultShipping) {
$arrData['country'] = $objConfig->shipping_country ?: $objConfig->country;
}
}

$objAddress->setRow($arrData);

return $objAddress;
}

/**
* Generate address data from tl_member, limit to fields enabled in the shop configuration
*/
public static function getAddressDataForMember(MemberModel $member, array $fields)
{
return array_intersect_key(
array_merge(
$member->row(),
array(
'street_1' => $member->street,

// Trying to guess subdivision by country and state
'subdivision' => strtoupper($member->country . '-' . $member->state)
)
),
array_flip($fields)
);
}
}

+ 178
- 0
api2/ApiExact.php Wyświetl plik

@@ -0,0 +1,178 @@
<?php

namespace App\ExactApi;

use App\ExactApi\Product;

//require_once('Product.php');

class ApiExact
{
const DIVISION = '58687';
const API_URL = "https://start.exactonline.de/api";
const API_URL_WNR = self::API_URL . "/v1/" . self::DIVISION;
const CLIENT_ID = "5a04d118-349c-4750-aac3-fa3a386999c6";
const CLIENT_SECRET = "E7Wuqcsp4Lih";

const ACCESS_TOKEN_VALID_DURATION = 600 - 50;
const ITEM_GROUP_UUID = 'df17bdaf-2af7-4e9f-8d60-326e36b57764';
const PRODUCT_CODE_PREFIX = "WR";

public function getAccessToken()
{
$tokenData = json_decode(file_get_contents(__DIR__.'tokenData', true));
// ob_start();
// var_dump($tokenData);
//
// file_put_contents('flo.txt', ob_get_contents());
// ob_end_clean();

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')) {
throw new \Exception($jsonResult);
}
$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_WNR . "/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;
}

public function createCustomer($customerData)
{
$url = self::API_URL_WNR . "/crm/Accounts";
return json_decode($this->postApiData($url, $customerData));
}

public function createSalesOrder($salesOrderData)
{
$url = self::API_URL_WNR . "/salesorder/SalesOrders";
// $parameters = [
// 'Description' => 'Daniel Knudsen',
// 'OrderDate' => '07/13/2022 17:00:00',
// 'OrderedBy' => '9ba706a3-d6f5-40c8-8d4f-e55a37692cef',
// 'WarehouseID' => 'd8a6a9b8-d0ac-4d36-8d00-bd420d0d81f5',
// 'SalesOrderLines' => [
// [
// 'Description' => 'TEST: Reinigungstuch grün',
// 'Item' => '9dcd8b50-5de6-4f15-a51a-c51282292383',
// 'UnitPrice' => '2.45',
// 'Quantity' => '3'
// ],
// [
// 'Description' => 'TEST: McQuade´s E-Bike Kettenrückführ - Tool',
// 'Item' => '0e04113f-1299-4d3c-b65e-01e83ca5b3b2',
// 'UnitPrice' => '476.00',
// 'Quantity' => '1'
// ],
// ]
// ];
return json_decode($this->postApiData($url, $salesOrderData));
}

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,
));
$res = curl_exec($ch);
curl_close($ch);
return $res;
}

private function postApiData($url, $data)
{
$ch = curl_init();
curl_setopt_array($ch, array(
CURLOPT_URL => $url,
CURLOPT_HTTPHEADER => array(
'Content-Type: application/json',
'Accept: application/json',
"Authorization: Bearer " . $this->getAccessToken() ),
CURLOPT_RETURNTRANSFER => TRUE,
CURLOPT_POST => TRUE,
CURLOPT_POSTFIELDS => json_encode($data)
));
$res = curl_exec($ch);
curl_close($ch);
return $res;
}

private function getFieldString($fields)
{
$res = '';
$cnt = count($fields);
$i = 1;
foreach ($fields as $field) {
$res .= $cnt !== $i ? $field . ',' : $field;
$i++;
}
return $res;
}

public function test()
{
return 'fuck off contao';
}
}

+ 197
- 0
api2/IsotopeDatabaseHandler.php Wyświetl plik

@@ -0,0 +1,197 @@
<?php

namespace App\ExactApi;

use App\ExactApi\Product;

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 processProducts(array $products)
{
$this->dbHandle->beginTransaction();

$sql = "SELECT * FROM `tl_iso_product`";
$stmt = $this->dbHandle->query($sql);
$dbProducts = $stmt->fetchAll(PDO::FETCH_ASSOC);

$sql = "SELECT * FROM `tl_iso_product_price`";
$stmt = $this->dbHandle->query($sql);
$dbProductPrices = $stmt->fetchAll(PDO::FETCH_ASSOC);

$dbProductsBySku = [];
foreach ($dbProducts as $prod) {
$dbProductsBySku[$prod['sku']] = $prod;
}

$dbProductsPricesByPid = [];
foreach ($dbProductPrices as $price) {
$dbProductsPricesByPid[$price['pid']] = $price;
}

/** @var Product $product */
foreach ($products as $product) {

if (array_key_exists($product->getCode(), $dbProductsBySku)) {
$dbProduct = $dbProductsBySku[$product->getCode()];
$dbPrice = $dbProductsPricesByPid[$dbProduct['id']];
$this->updateProduct($product, $dbProduct['id']);
$this->updatePriceTier($product, $dbPrice['id']);

} else {
$productId = $this->createProduct($product);
$productPriceId = $this->createPrice($product, $productId);
$this->createPriceTier($product, $productPriceId);
}
}
$this->dbHandle->commit();
}

private function updateProduct(Product $product, $productDbId)
{
$shippingWeight = $product->getGrossWeight() !== '' ?
($product->getNetWeight() !== '' ? $product->getNetWeight() : '') : '';

$sql =
"UPDATE `tl_iso_product` SET
tstamp = ".time().",
shipping_weight = '$shippingWeight'
WHERE `id` = ".$productDbId;
$this->dbHandle->query($sql);
}

private function updatePriceTier(Product $product, $priceDbId)
{
$price = $product->getStandardSalesPrice() * (1 + Product::VAT);
$sql =
"UPDATE `tl_iso_product_pricetier` SET
tstamp = ".time().",
price = $price
WHERE `pid` = ".$priceDbId;
$this->dbHandle->query($sql);
}

private function createProduct(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,
exact_id
) VALUES (
0,
0,
".time().",
'',
".time().",
2,
'',
'$alias',
'',
'".$product->getCode()."',
'".$product->getDescription()."',
'".$product->getExtraDescription()."',
'',
'',
'$shippingWeight',
'',
'',
0.00,
'',
'',
'',
'0',
'',
'',
'".$product->getId()."'
)";
$this->dbHandle->query($sql);
return $this->dbHandle->lastInsertId();
}

private function createPrice(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 createPriceTier(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();
}
}

+ 614
- 0
api2/Order.php Wyświetl plik

@@ -0,0 +1,614 @@
<?php

/*
* Isotope eCommerce for Contao Open Source CMS
*
* Copyright (C) 2009 - 2019 terminal42 gmbh & Isotope eCommerce Workgroup
*
* @link https://isotopeecommerce.org
* @license https://opensource.org/licenses/lgpl-3.0.html
*/

namespace Isotope\Model\ProductCollection;

use Contao\Controller;
use Contao\Message;
use Contao\StringUtil;
use Contao\System;
use Contao\Template;
use Haste\Generator\RowClass;
use Haste\Util\Format;
use Isotope\Interfaces\IsotopeNotificationTokens;
use Isotope\Interfaces\IsotopeOrderStatusAware;
use Isotope\Interfaces\IsotopePurchasableCollection;
use Isotope\Isotope;
use Isotope\Model\Address;
use Isotope\Model\Document;
use Isotope\Model\OrderStatus;
use Isotope\Model\ProductCollection;
use Isotope\Model\ProductCollectionLog;
use NotificationCenter\Model\Notification;


/**
* Class Order
*
* Provide methods to handle Isotope orders.
*
* @method static Order findOneBy(string $strColumn, $varValue, array $arrOptions=array())
*
* @property array $checkout_info
* @property array $payment_data
* @property array $shipping_data
* @property string $document_number
* @property int $order_status
* @property int $date_paid
* @property int $date_shipped
* @property string $notes
*
* @method static Order|null findByPk($id)
*/
class Order extends ProductCollection implements IsotopePurchasableCollection
{
const STATUS_UPDATE_SKIP_NOTIFICATION = 1;
const STATUS_UPDATE_SKIP_LOG = 2;


/**
* @inheritdoc
*/
public function isPaid()
{
// Order is paid if a payment date is set
if (null !== $this->date_paid && $this->date_paid <= time()) {
return true;
}

// Otherwise we check the orderstatus checkbox
try {
/** @var OrderStatus $objStatus */
$objStatus = $this->getRelated('order_status');

return (null !== $objStatus && $objStatus->isPaid());
} catch (\Exception $e) {
return false;
}
}

/**
* @inheritdoc
*/
public function getDatePaid()
{
return $this->date_paid;
}

/**
* @inheritdoc
*/
public function setDatePaid($timestamp = null)
{
$this->date_paid = $timestamp;
}

/**
* @inheritdoc
*/
public function isShipped()
{
return null !== $this->date_shipped;
}

/**
* @inheritdoc
*/
public function getDateShipped()
{
return $this->date_shipped;
}

/**
* @inheritdoc
*/
public function setDateShipped($timestamp = null)
{
$this->date_shipped = $timestamp;
}

/**
* Returns true if checkout has been completed
*
* @return bool
*/
public function isCheckoutComplete()
{
return (bool) $this->checkout_complete;
}

/**
* Get label for current order status
*
* @return string
*/
public function getStatusLabel()
{
try {
/** @var OrderStatus $objStatus */
$objStatus = $this->getRelated('order_status');

return (null === $objStatus) ? '' : $objStatus->getName();
} catch (\Exception $e) {
return '';
}
}

/**
* Get the alias for current order status
*
* @return string
*/
public function getStatusAlias()
{
try {
/** @var OrderStatus $objStatus */
$objStatus = $this->getRelated('order_status');

return null === $objStatus ? $this->order_status : $objStatus->getAlias();
} catch (\Exception $e) {
return $this->order_status;
}
}

/**
* Process the order checkout
*
* @return bool
*/
public function checkout()
{
if ($this->isCheckoutComplete()) {
return true;
}

// Finish and lock the order
// (do this now, because otherwise surcharges etc. will not be loaded form the database)
$this->checkout_complete = true;
$this->generateDocumentNumber(
$this->getConfig()->orderPrefix,
(int) $this->getConfig()->orderDigits
);

if (!$this->isLocked()) {
$this->lock();
}

System::log('New order ID ' . $this->id . ' has been placed', __METHOD__, TL_ACCESS);

// Delete cart after migrating to order
if (($objCart = Cart::findByPk($this->source_collection_id)) !== null) {
$objCart->delete();
}

// Delete all other orders that relate to the current cart
if (($objOrders = static::findSiblingsBy('source_collection_id', $this)) !== null) {

/** @var static $objOrder */
foreach ($objOrders as $objOrder) {
if (!$objOrder->isCheckoutComplete()) {
$objOrder->delete(true);
}
}
}

$notificationIds = array_filter(explode(',', $this->nc_notification));

// Send the notifications
if (\count($notificationIds) > 0) {
foreach ($notificationIds as $notificationId) {
// Generate tokens
$arrTokens = $this->getNotificationTokens($notificationId);

// Send notification
$blnNotificationError = true;

/** @var Notification $objNotification */
if (($objNotification = Notification::findByPk($notificationId)) !== null) {
$arrResult = $objNotification->send($arrTokens, $this->language);

if (\count($arrResult) > 0 && !\in_array(false, $arrResult, true)) {
$blnNotificationError = false;
}
}

if ($blnNotificationError === true) {
System::log('Error sending new order notification for order ID ' . $this->id, __METHOD__, TL_ERROR);
}
}
}

// Set order status only if a payment module has not already set it
if ($this->order_status == 0) {
$this->updateOrderStatus($this->getRelated('config_id')->orderstatus_new);
}

// !HOOK: post-process checkout
if (isset($GLOBALS['ISO_HOOKS']['postCheckout']) && \is_array($GLOBALS['ISO_HOOKS']['postCheckout'])) {
// Generate the default notification tokens if none set yet
if (!isset($arrTokens)) {
$arrTokens = $this->getNotificationTokens(0);
}

foreach ($GLOBALS['ISO_HOOKS']['postCheckout'] as $callback) {
System::importStatic($callback[0])->{$callback[1]}($this, $arrTokens);
}
}

return true;
}

/**
* Complete order if the checkout has been made. This will cleanup session data
*
* @return bool
*/
public function complete()
{
if ($this->isCheckoutComplete()) {
unset($_SESSION['CHECKOUT_DATA'], $_SESSION['FILES']);

// Retain custom config ID
if (($objCart = Isotope::getCart()) !== null && $objCart->config_id != $this->config_id) {
$objCart->config_id = $this->config_id;
}

return true;
}

return false;
}

/**
* Update the status of this order and trigger actions (email & hook)
*
* @param int|array $updates
* @param int $flags Order::STATUS_UPDATE_SKIP_NOTIFICATION and/or Order::STATUS_UPDATE_SKIP_LOG
*
* @return bool
*/
public function updateOrderStatus($updates, $flags = 0)
{
// For BC reasons the parameter can be the new order status ID
if (!is_array($updates)) {
$updates = ['order_status' => $updates];
}

$previous = [];
$hasChanges = false;
foreach ($updates as $k => $v) {
$previous[$k] = $this->{$k};
$hasChanges = $hasChanges ?: $v !== $previous[$k];
}

if (!isset($updates['order_status'])) {
throw new \LogicException('You must update the order status when calling Order::updateOrderStatus()');
}

if (!$hasChanges) {
return true;
}

/** @var OrderStatus $objNewStatus */
$objNewStatus = OrderStatus::findByPk($updates['order_status']);

if (null === $objNewStatus) {
return false;
}

// !HOOK: allow to cancel a status update
if (isset($GLOBALS['ISO_HOOKS']['preOrderStatusUpdate'])
&& \is_array($GLOBALS['ISO_HOOKS']['preOrderStatusUpdate'])
) {
foreach ($GLOBALS['ISO_HOOKS']['preOrderStatusUpdate'] as $callback) {
$blnCancel = System::importStatic($callback[0])->{$callback[1]}($this, $objNewStatus, $updates);

if ($blnCancel === true) {
return false;
}
}
}

// Add the payment date if there is none
if ($objNewStatus->isPaid() && $this->date_paid == '' && !isset($updates['date_paid'])) {
$updates['date_paid'] = time();
}

// Store old status and set the new one
$oldStatusId = $this->order_status;
$oldStatusLabel = $this->getStatusLabel();
foreach ($updates as $k => $v) {
$this->{$k} = $v;
}
$this->save();

if (!($flags & static::STATUS_UPDATE_SKIP_NOTIFICATION)) {
// Trigger notification
$blnNotificationError = null;
foreach (array_filter(explode(',', $objNewStatus->notification)) as $notificationId) {
$arrTokens = $this->getNotificationTokens($notificationId);

// Override order status and save the old one to the tokens too
$arrTokens['order_status_id'] = $objNewStatus->id;
$arrTokens['order_status'] = $objNewStatus->getName();
$arrTokens['order_status_old'] = $oldStatusLabel;
$arrTokens['order_status_id_old'] = $oldStatusId;

$blnNotificationError = true;

/** @var Notification $objNotification */
if (($objNotification = Notification::findByPk($notificationId)) !== null) {
$arrResult = $objNotification->send($arrTokens, $this->language);

if (\in_array(false, $arrResult, true)) {
$blnNotificationError = true;
System::log(
'Error sending status update notification for order ID ' . $this->id,
__METHOD__,
TL_ERROR
);
} elseif (\count($arrResult) > 0) {
$blnNotificationError = false;
}
} else {
System::log('Invalid notification for order status ID ' . $objNewStatus->id, __METHOD__, TL_ERROR);
}
}

if ('BE' === TL_MODE) {
Message::addConfirmation($GLOBALS['TL_LANG']['tl_iso_product_collection']['orderStatusUpdate']);

if ($blnNotificationError === true) {
Message::addError($GLOBALS['TL_LANG']['tl_iso_product_collection']['orderStatusNotificationError']);
} elseif ($blnNotificationError === false) {
Message::addConfirmation($GLOBALS['TL_LANG']['tl_iso_product_collection']['orderStatusNotificationSuccess']);
}
}
}

// Add a log entry
if (!($flags & static::STATUS_UPDATE_SKIP_LOG)) {
$log = new ProductCollectionLog();
$log->pid = $this->id;
$log->tstamp = time();
$log->setData([
'order_status' => $this->order_status,
'date_paid' => $this->date_paid,
'date_shipped' => $this->date_shipped,
]);
$log->save();
}

// !HOOK: order status has been updated
if (isset($GLOBALS['ISO_HOOKS']['postOrderStatusUpdate'])
&& \is_array($GLOBALS['ISO_HOOKS']['postOrderStatusUpdate'])
) {
foreach ($GLOBALS['ISO_HOOKS']['postOrderStatusUpdate'] as $callback) {
System::importStatic($callback[0])->{$callback[1]}($this, $oldStatusId, $objNewStatus);
}
}

// Trigger payment and shipping methods that implement the interface
if (($objPayment = $this->getPaymentMethod()) !== null && $objPayment instanceof IsotopeOrderStatusAware) {
$objPayment->onOrderStatusUpdate($this, $oldStatusId, $objNewStatus);
}
if (($objShipping = $this->getShippingMethod()) !== null && $objShipping instanceof IsotopeOrderStatusAware) {
$objShipping->onOrderStatusUpdate($this, $oldStatusId, $objNewStatus);
}

return true;
}

/**
* Retrieve the array of notification data for parsing simple tokens
*
* @param int $intNotification
*
* @return array
*/
public function getNotificationTokens($intNotification)
{
$objConfig = $this->getRelated('config_id') ?: Isotope::getConfig();
Isotope::setConfig($objConfig);

$arrTokens = StringUtil::deserialize($this->email_data, true);
$arrTokens['uniqid'] = $this->uniqid;
$arrTokens['order_status_id'] = $this->order_status;
$arrTokens['order_status'] = $this->getStatusLabel();
$arrTokens['recipient_email'] = $this->getEmailRecipient();
$arrTokens['order_id'] = $this->id;
$arrTokens['order_items'] = $this->sumItemsQuantity();
$arrTokens['order_products'] = $this->countItems();
$arrTokens['order_subtotal'] = Isotope::formatPriceWithCurrency($this->getSubtotal(), false, $objConfig->currency);
$arrTokens['order_total'] = Isotope::formatPriceWithCurrency($this->getTotal(), false, $objConfig->currency);
$arrTokens['document_number'] = $this->document_number;
$arrTokens['bank_name'] = $objConfig->bankName;
$arrTokens['bank_account'] = $objConfig->bankAccount;
$arrTokens['bank_code'] = $objConfig->bankCode;
$arrTokens['tax_number'] = $objConfig->taxNumber;
$arrTokens['cart_html'] = '';
$arrTokens['cart_text'] = '';
$arrTokens['document'] = '';

// Add all the collection fields
foreach ($this->row() as $k => $v) {
$arrTokens['collection_' . $k] = $v;
}

// Add billing/customer address fields
if (($objAddress = $this->getBillingAddress()) !== null) {
foreach ($objAddress->row() as $k => $v) {
$arrTokens['billing_address_' . $k] = Format::dcaValue(Address::getTable(), $k, $v);

// @deprecated (use ##billing_address_*##)
$arrTokens['billing_' . $k] = $arrTokens['billing_address_' . $k];
}

$arrTokens['billing_address'] = $objAddress->generate($objConfig->getBillingFieldsConfig());

// @deprecated (use ##billing_address##)
$arrTokens['billing_address_text'] = $arrTokens['billing_address'];
}

// Add shipping address fields
if (($objAddress = $this->getShippingAddress()) !== null) {
foreach ($objAddress->row() as $k => $v) {
$arrTokens['shipping_address_' . $k] = Format::dcaValue(Address::getTable(), $k, $v);

// @deprecated (use ##billing_address_*##)
$arrTokens['shipping_' . $k] = $arrTokens['shipping_address_' . $k];
}

$arrTokens['shipping_address'] = $objAddress->generate($objConfig->getShippingFieldsConfig());

// Shipping address equals billing address
// @deprecated (use ##shipping_address##)
if ($objAddress->id == $this->getBillingAddress()->id) {
$arrTokens['shipping_address_text'] = ($this->requiresPayment() ? $GLOBALS['TL_LANG']['MSC']['useBillingAddress'] : $GLOBALS['TL_LANG']['MSC']['useCustomerAddress']);
} else {
$arrTokens['shipping_address_text'] = $arrTokens['shipping_address'];
}
}

// Add payment method info
if ($this->hasPayment() && ($objPayment = $this->getPaymentMethod()) !== null) {
$arrTokens['payment_id'] = $objPayment->getId();
$arrTokens['payment_label'] = $objPayment->getLabel();
$arrTokens['payment_note'] = $objPayment->getNote();

if ($objPayment instanceof IsotopeNotificationTokens) {
$arrTokens = array_merge($arrTokens, $objPayment->getNotificationTokens($this));
}
}

// Add shipping method info
if ($this->hasShipping() && ($objShipping = $this->getShippingMethod()) !== null) {
$arrTokens['shipping_id'] = $objShipping->getId();
$arrTokens['shipping_label'] = $objShipping->getLabel();
$arrTokens['shipping_note'] = $objShipping->getNote();

if ($objShipping instanceof IsotopeNotificationTokens) {
$arrTokens = array_merge($arrTokens, $objShipping->getNotificationTokens($this));
}
}

// Add config fields
if ($this->getRelated('config_id') !== null) {
foreach ($this->getRelated('config_id')->row() as $k => $v) {
$arrTokens['config_' . $k] = Format::dcaValue($this->getRelated('config_id')->getTable(), $k, $v);
}
}

// Add member fields
if ($this->member > 0 && $this->getRelated('member') !== null) {
foreach ($this->getRelated('member')->row() as $k => $v) {
$arrTokens['member_' . $k] = Format::dcaValue($this->getRelated('member')->getTable(), $k, $v);
}
}

/** @var Notification|object $objNotification */
if ($intNotification > 0 && ($objNotification = Notification::findByPk($intNotification)) !== null) {
/** @var \Isotope\Template|object $objTemplate */
$objTemplate = new \Isotope\Template($objNotification->iso_collectionTpl);
$objTemplate->isNotification = true;

$this->addToTemplate(
$objTemplate,
array(
'gallery' => $objNotification->iso_gallery,
'sorting' => static::getItemsSortingCallable($objNotification->iso_orderCollectionBy),
)
);

$arrTokens['cart_html'] = Controller::replaceInsertTags($objTemplate->parse(), false);
$objTemplate->textOnly = true;
$arrTokens['cart_text'] = strip_tags(Controller::replaceInsertTags($objTemplate->parse(), true));

// Generate and "attach" document
/** @var \Isotope\Interfaces\IsotopeDocument $objDocument */
if ($objNotification->iso_document > 0
&& (($objDocument = Document::findByPk($objNotification->iso_document)) !== null)
) {
$strFilePath = $objDocument->outputToFile($this, TL_ROOT . '/system/tmp');
$arrTokens['document'] = str_replace(TL_ROOT . '/', '', $strFilePath);
}
}

// !HOOK: add custom email tokens
if (isset($GLOBALS['ISO_HOOKS']['getOrderNotificationTokens'])
&& \is_array($GLOBALS['ISO_HOOKS']['getOrderNotificationTokens'])
) {
foreach ($GLOBALS['ISO_HOOKS']['getOrderNotificationTokens'] as $callback) {
$arrTokens = System::importStatic($callback[0])->{$callback[1]}($this, $arrTokens);
}
}

return $arrTokens;
}

/**
* Include downloads when adding items to template
*
* @return array
*/
protected function addItemsToTemplate(Template $objTemplate, $varCallable = null)
{
$taxIds = [];
$arrItems = [];
$arrAllDownloads = [];

foreach ($this->getItems($varCallable) as $objItem) {
$arrDownloads = [];
$arrItem = $this->generateItem($objItem);

foreach ($objItem->getDownloads() as $objDownload) {
$arrDownloads = array_merge($arrDownloads, $objDownload->getForTemplate($this->isPaid(), $this->orderdetails_page));
}

$arrItem['downloads'] = $arrDownloads;
$arrAllDownloads = array_merge($arrAllDownloads, $arrDownloads);

$taxIds[] = $arrItem['tax_id'];
$arrItems[] = $arrItem;
}

RowClass::withKey('rowClass')->addCount('row_')->addFirstLast('row_')->addEvenOdd('row_')->applyTo($arrItems);

$objTemplate->items = $arrItems;
$objTemplate->downloads = $arrAllDownloads;
$objTemplate->total_tax_ids = \count(array_count_values($taxIds));

return $arrItems;
}

/**
* Generate unique order ID including the order prefix
*
* @return string
*/
protected function generateUniqueId()
{
if (!empty($this->arrData['uniqid'])) {
return $this->arrData['uniqid'];
}

$objConfig = $this->getConfig();

if (null === $objConfig) {
$objConfig = Isotope::getConfig();
}

return uniqid(
Controller::replaceInsertTags((string) $objConfig->orderPrefix, false),
true
);
}
}

+ 147
- 0
api2/PostCheckoutListener.php Wyświetl plik

@@ -0,0 +1,147 @@
<?php

namespace App\EventListener;


use Contao\CoreBundle\ServiceAnnotation\Hook;
use Contao\FrontendTemplate;
use Contao\Module;
use App\ExactApi\ApiExact;
use Isotope\Model\Address;
use Isotope\Model\Product;
use Isotope\Model\ProductCollection\Order;
use Isotope\Model\ProductCollectionItem;

class PostCheckoutListener
{
const WAREHOUSE_ID = 'd8a6a9b8-d0ac-4d36-8d00-bd420d0d81f5';

/**
* @Hook("postCheckout")
*/
// public function __invoke(int $userId, array $userData, Module $module): void
public function __invoke(Order $order, $item): void
{
//$a = $order->getBillingAddress();
// ob_start();
// var_dump($order);
// file_put_contents('flo.txt', ob_get_contents());
// ob_end_clean();
//
// ob_start();
// var_dump($item);
// file_put_contents('dan.txt', ob_get_contents());
// ob_end_clean();
// throw new \Exception('ARRRRRRRGGGGGGGGGG');
$apiExact = new ApiExact();

ob_start();
/** @var ProductCollectionItem $orderItem */
foreach($order->getItems() as $orderItem) {
/** @var Product $orderItem */
$product = $orderItem->getProduct();
var_dump($orderItem->id);
var_dump($orderItem->sku);
var_dump($product->id);
var_dump($product->name);
var_dump($product->is_set);
}
var_dump($apiExact->test());
// var_dump($order->getItems());
//var_dump($order);
file_put_contents('dan.txt', ob_get_contents());
ob_end_clean();



// $shippingCustomer = null;
// if ($this->compareAddresses() === false) {
// $shippingCustomer = $apiExact->createCustomer(
// $this->createCustomerApiData($order->getShippingAddress())
// );
// }
//
// $billingCustomer = $apiExact->createCustomer(
// $this->createCustomerApiData($order->getBillingAddress())
// );
//
// $this->createSalesOrderData($order, $billingCustomer, $shippingCustomer);

// $billingFirstName = $order->getBillingAddress()->firstname;
// $billingLastName = $order->getBillingAddress()->lastname;
// $billingStreet = $order->getBillingAddress()->street_1;
// $billingPostcode = $order->getBillingAddress()->postal;
// $billingCity = $order->getBillingAddress()->city;
// $billingCompany = $order->getBillingAddress()->company;
// $billingPhone = $order->getBillingAddress()->phone;
// $billingEmail = $order->getBillingAddress()->email;
//
// $shippingFirstName = $order->getShippingAddress()->firstname;
// $shippingLastName = $order->getShippingAddress()->lastname;
// $shippingStreet = $order->getShippingAddress()->street_1;
// $shippingPostcode = $order->getShippingAddress()->postal;
// $shippingCity = $order->getShippingAddress()->city;
// $shippingCompany = $order->getShippingAddress()->company;
// $shippingPhone = $order->getShippingAddress()->phone;
// $shippingEmail = $order->getShippingAddress()->email;

}

private function compareAddresses(Order $order)
{
return
$order->getBillingAddress()->firstname === $order->getShippingAddress()->firstname &&
$order->getBillingAddress()->lastname === $order->getShippingAddress()->lastname &&
$order->getBillingAddress()->street_1 === $order->getShippingAddress()->street_1 &&
$order->getBillingAddress()->postal === $order->getShippingAddress()->postal &&
$order->getBillingAddress()->city === $order->getShippingAddress()->city &&
$order->getBillingAddress()->company === $order->getShippingAddress()->company &&
$order->getBillingAddress()->phone === $order->getShippingAddress()->phone &&
$order->getBillingAddress()->email === $order->getShippingAddress()->email;
}

private function createCustomerApiData(Address $address)
{
return [
'Name' => $address->firstname . ' ' . $address->lastname,
'AddressLine1' => $address->street_1,
'AddressLine2' => $address->company,
'City' => $address->city,
'Postcode' => $address->postal,
'Email' => $address->email,
'Country' => 'DE',
'Phone' => $address->phone,
'Status' => 'C',
];
}

private function createSalesOrderData(Order $order, $billingCustomer, $shippingCustomer)
{
$date = new \DateTime('now');

$orderItems = $order->getItems();

return [
'Description' => $billingCustomer['Name'],
'OrderDate' => $date->format('m/d/Y H:i:s'),
'OrderedBy' => $billingCustomer['ID'],
'WarehouseID' => self::WAREHOUSE_ID,
'SalesOrderLines' => [
[
'Description' => 'TEST: Reinigungstuch grün',
'Item' => '9dcd8b50-5de6-4f15-a51a-c51282292383',
'UnitPrice' => '2.45',
'Quantity' => '3'
],
[
'Description' => 'TEST: McQuade´s E-Bike Kettenrückführ - Tool',
'Item' => '0e04113f-1299-4d3c-b65e-01e83ca5b3b2',
'UnitPrice' => '476.00',
'Quantity' => '1'
],
]
];
}
}

+ 809
- 0
api2/Product.php Wyświetl plik

@@ -0,0 +1,809 @@
<?php

/*
* Isotope eCommerce for Contao Open Source CMS
*
* Copyright (C) 2009 - 2019 terminal42 gmbh & Isotope eCommerce Workgroup
*
* @link https://isotopeecommerce.org
* @license https://opensource.org/licenses/lgpl-3.0.html
*/

namespace Isotope\Model;

use Contao\Database;
use Contao\Date;
use Contao\DcaExtractor;
use Contao\Model;
use Isotope\Interfaces\IsotopeProduct;
use Isotope\Model\Attribute;
use Isotope\RequestCache\Filter;
use Model\Collection;

/**
* The basic Isotope product model
*
* @property int $id
* @property int $pid
* @property int $gid
* @property int $tstamp
* @property string $language
* @property int $dateAdded
* @property int $type
* @property array $pages
* @property array $orderPages
* @property array $inherit
* @property bool $fallback
* @property string $alias
* @property string $sku
* @property string $name
* @property string $teaser
* @property string $description
* @property string $meta_title
* @property string $meta_description
* @property string $meta_keywords
* @property bool $shipping_exempt
* @property array $images
* @property bool $protected
* @property array $groups
* @property bool $guests
* @property array $cssID
* @property bool $published
* @property string $start
* @property string $stop
*/
abstract class Product extends TypeAgent implements IsotopeProduct
{

/**
* Table name
* @var string
*/
protected static $strTable = 'tl_iso_product';

/**
* Interface to validate attribute
* @var string
*/
protected static $strInterface = '\Isotope\Interfaces\IsotopeProduct';

/**
* List of types (classes) for this model
* @var array
*/
protected static $arrModelTypes = array();

/**
* Currently active product (LIFO queue)
* @var array
*/
protected static $arrActive = array();

/**
* Get categories (pages) assigned to this product
*
* @param bool $blnPublished Only return published categories (pages)
*
* @return array
*/
abstract public function getCategories($blnPublished = false);

/**
* Get product that is currently active (needed e.g. for insert tag replacement)
*
* @return IsotopeProduct|null
*/
public static function getActive()
{
return 0 === \count(static::$arrActive) ? null : end(static::$arrActive);
}

/**
* Set product that is currently active (needed e.g. for insert tag replacement)
*
* @param IsotopeProduct $objProduct
*/
public static function setActive(IsotopeProduct $objProduct)
{
static::$arrActive[] = $objProduct;
}

/**
* Unset product that is currently active (prevent later use of it)
*/
public static function unsetActive()
{
array_pop(static::$arrActive);
}

/**
* Find all published products
*
* @param array $arrOptions
*
* @return Collection|Product[]|null
*/
public static function findPublished(array $arrOptions = array())
{
return static::findPublishedBy(array(), array(), $arrOptions);
}

/**
* Find published products by condition
*
* @param mixed $arrColumns
* @param mixed $arrValues
* @param array $arrOptions
*
* @return Collection|Product[]|null
*/
public static function findPublishedBy($arrColumns, $arrValues, array $arrOptions = array())
{
$t = static::$strTable;

$arrValues = (array) $arrValues;

if (!\is_array($arrColumns)) {
$arrColumns = array(static::$strTable . '.' . $arrColumns . '=?');
}

// Add publish check to $arrColumns as the first item to enable SQL keys
if (BE_USER_LOGGED_IN !== true) {
$time = Date::floorToMinute();
array_unshift(
$arrColumns,
"$t.published='1' AND ($t.start='' OR $t.start<'$time') AND ($t.stop='' OR $t.stop>'" . ($time + 60) . "')"
);
}

return static::findBy($arrColumns, $arrValues, $arrOptions);
}

/**
* Find a single product by primary key
*
* @param int $intId
* @param array $arrOptions
*
* @return static|null
*/
public static function findPublishedByPk($intId, array $arrOptions = array())
{
$arrOptions = array_merge(
array(
'return' => 'Model'
),
$arrOptions
);

return static::findPublishedBy(static::$strPk, (int) $intId, $arrOptions);
}

/**
* Find a single product by its ID or alias
*
* @param mixed $varId The ID or alias
* @param array $arrOptions An optional options array
*
* @return static|null The model or null if the result is empty
*/
public static function findPublishedByIdOrAlias($varId, array $arrOptions = array())
{
$t = static::$strTable;

$arrColumns = array("($t.id=? OR $t.alias=?)");
$arrValues = array(is_numeric($varId) ? $varId : 0, $varId);

$arrOptions = array_merge(
array(
'limit' => 1,
'return' => 'Model'
),
$arrOptions
);

return static::findPublishedBy($arrColumns, $arrValues, $arrOptions);
}

/**
* Find products by IDs
*
* @param array $arrIds
* @param array $arrOptions
*
* @return Product[]|Collection
*/
public static function findPublishedByIds(array $arrIds, array $arrOptions = array())
{
if (0 === \count($arrIds)) {
return null;
}

return static::findPublishedBy(
array(static::$strTable . '.id IN (' . implode(',', array_map('intval', $arrIds)) . ')'),
null,
$arrOptions
);
}

/**
* Return collection of published product variants by product PID
*
* @param int $intPid
* @param array $arrOptions
*
* @return Collection|Product[]|null
*/
public static function findPublishedByPid($intPid, array $arrOptions = array())
{
return static::findPublishedBy('pid', (int) $intPid, $arrOptions);
}

/**
* Return collection of published products by categories
*
* @param array $arrCategories
* @param array $arrOptions
*
* @return Collection|Product[]|null
*/
public static function findPublishedByCategories(array $arrCategories, array $arrOptions = array())
{
return static::findPublishedBy(
array('c.page_id IN (' . implode(',', array_map('intval', $arrCategories)) . ')'),
null,
$arrOptions
);
}

/**
* Find a single frontend-available product by primary key
*
* @param int $intId
* @param array $arrOptions
*
* @return static|null
*/
public static function findAvailableByPk($intId, array $arrOptions = array())
{
$objProduct = static::findPublishedByPk($intId, $arrOptions);

if (null === $objProduct || !$objProduct->isAvailableInFrontend()) {
return null;
}

return $objProduct;
}

/**
* Find a single frontend-available product by its ID or alias
*
* @param mixed $varId The ID or alias
* @param array $arrOptions An optional options array
*
* @return Product|null The model or null if the result is empty
*/
public static function findAvailableByIdOrAlias($varId, array $arrOptions = array())
{
$objProduct = static::findPublishedByIdOrAlias($varId, $arrOptions);

if (null === $objProduct || !$objProduct->isAvailableInFrontend()) {
return null;
}

return $objProduct;
}

/**
* Find frontend-available products by IDs
*
* @param array $arrIds
* @param array $arrOptions
*
* @return Collection|Product[]|null
*/
public static function findAvailableByIds(array $arrIds, array $arrOptions = array())
{
$objProducts = static::findPublishedByIds($arrIds, $arrOptions);

if (null === $objProducts) {
return null;
}

$arrProducts = [];
foreach ($objProducts as $objProduct) {
if ($objProduct->isAvailableInFrontend()) {
$arrProducts[] = $objProduct;
}
}

if (0 === \count($arrProducts)) {
return null;
}

return new Collection($arrProducts, static::$strTable);
}

/**
* Find frontend-available products by condition
*
* @param mixed $arrColumns
* @param mixed $arrValues
* @param array $arrOptions
*
* @return Collection
*/
public static function findAvailableBy($arrColumns, $arrValues, array $arrOptions = array())
{
$objProducts = static::findPublishedBy($arrColumns, $arrValues, $arrOptions);

if (null === $objProducts) {
return null;
}

$arrProducts = [];
foreach ($objProducts as $objProduct) {
if ($objProduct->isAvailableInFrontend()) {
$arrProducts[] = $objProduct;
}
}

if (0 === \count($arrProducts)) {
return null;
}

return new Collection($arrProducts, static::$strTable);
}

/**
* Find variant of a product
*
* @param IsotopeProduct $objProduct
* @param array $arrVariant
* @param array $arrOptions
*
* @return Model|null
*/
public static function findVariantOfProduct(
IsotopeProduct $objProduct,
array $arrVariant,
array $arrOptions = array()
) {
$t = static::$strTable;

$arrColumns = array(
"$t.id IN (" . implode(',', $objProduct->getVariantIds()) . ')',
"$t." . implode("=? AND $t.", array_keys($arrVariant)) . '=?'
);

$arrOptions = array_merge(
array(
'limit' => 1,
'column' => $arrColumns,
'value' => $arrVariant,
'return' => 'Model'
),
$arrOptions
);

return static::find($arrOptions);
}

/**
* Finds the default variant of a product.
*
* @param IsotopeProduct $objProduct
* @param array $arrOptions
*
* @return static|null
*/
public static function findDefaultVariantOfProduct(IsotopeProduct $objProduct, array $arrOptions = array())
{
static $cache;

if (null === $cache) {
$cache = [];
$data = Database::getInstance()->execute(
"SELECT id, pid FROM tl_iso_product WHERE pid>0 AND language='' AND fallback='1'"
);

while ($data->next()) {
$cache[$data->pid] = $data->id;
}
}

$defaultId = $cache[$objProduct->getProductId()];

if ($defaultId < 1 || !\in_array($defaultId, $objProduct->getVariantIds())) {
return null;
}

return static::findByPk($defaultId, $arrOptions);
}

/**
* Returns the number of published products.
*
* @param array $arrOptions
*
* @return int
*/
public static function countPublished(array $arrOptions = array())
{
return static::countPublishedBy(array(), array(), $arrOptions);
}

/**
* Return the number of products matching certain criteria
*
* @param mixed $arrColumns
* @param mixed $arrValues
* @param array $arrOptions
*
* @return int
*/
public static function countPublishedBy($arrColumns, $arrValues, array $arrOptions = array())
{
$t = static::$strTable;

$arrValues = (array) $arrValues;

if (!\is_array($arrColumns)) {
$arrColumns = array(static::$strTable . '.' . $arrColumns . '=?');
}

// Add publish check to $arrColumns as the first item to enable SQL keys
if (BE_USER_LOGGED_IN !== true) {
$time = Date::floorToMinute();
array_unshift(
$arrColumns,
"
$t.published='1'
AND ($t.start='' OR $t.start<'$time')
AND ($t.stop='' OR $t.stop>'" . ($time + 60) . "')
"
);
}

return static::countBy($arrColumns, $arrValues, $arrOptions);
}

/**
* Gets the number of translation records in the product table.
* Mostly useful to see if there are any translations at all to optimize queries.
*
* @return int
*/
public static function countTranslatedProducts()
{
static $result;

if (null === $result) {
$result = Database::getInstance()->query(
"SELECT COUNT(*) AS total FROM tl_iso_product WHERE language!=''"
)->total;
}

return $result;
}

/**
* Return a model or collection based on the database result type
*
* @param array $arrOptions
*
* @return Product|Product[]|Collection|null
*/
protected static function find(array $arrOptions)
{
$arrOptions['group'] = static::getTable() . '.id' . (null === ($arrOptions['group'] ?? null) ? '' : ', '.$arrOptions['group']);

$objProducts = parent::find($arrOptions);

if (null === $objProducts) {
return null;
}

/** @var Filter[] $arrFilters */
$arrFilters = $arrOptions['filters'] ?? null;
$arrSorting = $arrOptions['sorting'] ?? null;

$hasFilters = \is_array($arrFilters) && 0 !== \count($arrFilters);
$hasSorting = \is_array($arrSorting) && 0 !== \count($arrSorting);

if ($hasFilters || $hasSorting) {

/** @var static[] $arrProducts */
$arrProducts = $objProducts->getModels();

if ($hasFilters) {
$arrProducts = array_filter($arrProducts, function ($objProduct) use ($arrFilters) {
$arrGroups = [];

foreach ($arrFilters as $objFilter) {
$blnMatch = $objFilter->matches($objProduct);

if ($objFilter->hasGroup()) {
$arrGroups[$objFilter->getGroup()] = $arrGroups[$objFilter->getGroup()] ? : $blnMatch;
} elseif (!$blnMatch) {
return false;
}
}

return !\in_array(false, $arrGroups, true);
});
}

// $arrProducts can be empty if the filter removed all records
if ($hasSorting && 0 !== \count($arrProducts)) {
$arrParam = array();
$arrData = array();

foreach ($arrSorting as $strField => $arrConfig) {
foreach ($arrProducts as $objProduct) {

// Both SORT_STRING and SORT_REGULAR are case sensitive, strings starting with a capital letter
// will come before strings starting with a lowercase letter. To perform a case insensitive
// search, force the sorting order to be determined by a lowercase copy of the original value.

// Temporary fix for price attribute (see #945)
if ('price' === $strField) {
if (null !== $objProduct->getPrice()) {
$arrData[$strField][$objProduct->id] = $objProduct->getPrice()->getAmount();
} else {
$arrData[$strField][$objProduct->id] = 0;
}
} else {
$arrData[$strField][$objProduct->id] = strtolower(
str_replace('"', '', $objProduct->$strField)
);
}
}

$arrParam[] = &$arrData[$strField];
$arrParam[] = $arrConfig[0];
$arrParam[] = $arrConfig[1];
}

// Add product array as the last item.
// This will sort the products array based on the sorting of the passed in arguments.
$arrParam[] = &$arrProducts;
\call_user_func_array('array_multisort', $arrParam);
}

$objProducts = new Collection($arrProducts, static::$strTable);
}

return $objProducts;
}

/**
* Return select statement to load product data including multilingual fields
*
* @param array $arrOptions an array of columns
*
* @return string
*/
protected static function buildFindQuery(array $arrOptions)
{
$objBase = DcaExtractor::getInstance($arrOptions['table']);
$hasTranslations = (static::countTranslatedProducts() > 0);
$hasVariants = (ProductType::countByVariants() > 0);

$arrJoins = array();
$arrFields = array(
$arrOptions['table'] . '.*',
"'" . str_replace('-', '_', $GLOBALS['TL_LANGUAGE']) . "' AS language",
);

if ($hasVariants) {
$arrFields[] = sprintf(
'IF(%s.pid>0, parent.type, %s.type) AS type',
$arrOptions['table'],
$arrOptions['table']
);
}

if ($hasTranslations) {
foreach (Attribute::getMultilingualFields() as $attribute) {
$arrFields[] = "IFNULL(translation.$attribute, " . $arrOptions['table'] . ".$attribute) AS $attribute";
}
}

foreach (Attribute::getFetchFallbackFields() as $attribute) {
$arrFields[] = "{$arrOptions['table']}.$attribute AS {$attribute}_fallback";
}

if ($hasTranslations) {
$arrJoins[] = sprintf(
" LEFT OUTER JOIN %s translation ON %s.id=translation.pid AND translation.language='%s'",
$arrOptions['table'],
$arrOptions['table'],
str_replace('-', '_', $GLOBALS['TL_LANGUAGE'])
);

$arrOptions['group'] = (null === $arrOptions['group'] ? '' : $arrOptions['group'].', ') . 'translation.id';
}

if ($hasVariants) {
$arrJoins[] = sprintf(
' LEFT OUTER JOIN %s parent ON %s.pid=parent.id',
$arrOptions['table'],
$arrOptions['table']
);
}

$arrFields[] = 'GROUP_CONCAT(c.page_id) AS product_categories';
$arrJoins[] = sprintf(
' LEFT OUTER JOIN %s c ON %s=c.pid',
ProductCategory::getTable(),
($hasVariants ? "IFNULL(parent.id, {$arrOptions['table']}.id)" : "{$arrOptions['table']}.id")
);

if ('c.sorting' === ($arrOptions['order'] ?? '')) {
$arrFields[] = 'c.sorting';

$arrOptions['group'] = (null === $arrOptions['group'] ? '' : $arrOptions['group'].', ') . 'c.id';
}

if ($objBase->hasRelations()) {
$intCount = 0;

foreach ($objBase->getRelations() as $strKey => $arrConfig) {
// Automatically join the single-relation records
if (('eager' === $arrConfig['load'] || ($arrOptions['eager'] ?? false))
&& ('hasOne' === $arrConfig['type'] || 'belongsTo' === $arrConfig['type'])
) {
if (\is_array($arrOptions['joinAliases'] ?? null)
&& ($key = array_search($arrConfig['table'], $arrOptions['joinAliases'], true)) !== false
) {
$strJoinAlias = $key;
unset($arrOptions['joinAliases'][$key]);
} else {
++$intCount;
$strJoinAlias = 'j' . $intCount;
}

$objRelated = DcaExtractor::getInstance($arrConfig['table']);

foreach ($objRelated->getFields() as $strField => $config) {
$arrFields[] = $strJoinAlias . '.' . $strField . ' AS ' . $strKey . '__' . $strField;
}

$arrJoins[] = sprintf(
' LEFT JOIN %s %s ON %s.%s=%s.id',
$arrConfig['table'],
$strJoinAlias,
$arrOptions['table'],
$strKey,
$strJoinAlias
);
}
}
}

// Generate the query
$strQuery = 'SELECT ' . implode(', ', $arrFields) . ' FROM ' . $arrOptions['table'] . implode('', $arrJoins);

// Where condition
if (!\is_array($arrOptions['column'] ?? null)) {
$arrOptions['column'] = array($arrOptions['table'] . '.' . $arrOptions['column'] . '=?');
}

// The model must never find a language record
$strQuery .= " WHERE {$arrOptions['table']}.language='' AND " . implode(' AND ', $arrOptions['column']);

// Group by
if (($arrOptions['group'] ?? null) !== null) {
$strQuery .= ' GROUP BY ' . $arrOptions['group'];
}

// Order by
if (($arrOptions['order'] ?? null) !== null) {
$strQuery .= ' ORDER BY ' . $arrOptions['order'];
}

return $strQuery;
}

/**
* Build a query based on the given options to count the number of products.
*
* @param array $arrOptions The options array
*
* @return string
*/
protected static function buildCountQuery(array $arrOptions)
{
$hasTranslations = (static::countTranslatedProducts() > 0);
$hasVariants = (ProductType::countByVariants() > 0);

$arrJoins = array();
$arrFields = array(
$arrOptions['table'] . '.id',
"'" . str_replace('-', '_', $GLOBALS['TL_LANGUAGE']) . "' AS language",
);

if ($hasVariants) {
$arrFields[] = sprintf(
'IF(%s.pid>0, parent.type, %s.type) AS type',
$arrOptions['table'],
$arrOptions['table']
);
}

if ($hasTranslations) {
foreach (Attribute::getMultilingualFields() as $attribute) {
$arrFields[] = "IFNULL(translation.$attribute, " . $arrOptions['table'] . ".$attribute) AS $attribute";
}
}

if ($hasTranslations) {
$arrJoins[] = sprintf(
" LEFT OUTER JOIN %s translation ON %s.id=translation.pid AND translation.language='%s'",
$arrOptions['table'],
$arrOptions['table'],
str_replace('-', '_', $GLOBALS['TL_LANGUAGE'])
);

$arrOptions['group'] = (null === $arrOptions['group'] ? '' : $arrOptions['group'].', ') . 'translation.id, tl_iso_product.id';
}

if ($hasVariants) {
$arrJoins[] = sprintf(
' LEFT OUTER JOIN %s parent ON %s.pid=parent.id',
$arrOptions['table'],
$arrOptions['table']
);
}

$arrJoins[] = sprintf(
' LEFT OUTER JOIN %s c ON %s=c.pid',
ProductCategory::getTable(),
($hasVariants ? "IFNULL(parent.id, {$arrOptions['table']}.id)" : "{$arrOptions['table']}.id")
);

// Generate the query
$strWhere = '';
$strQuery = '
SELECT
' . implode(', ', $arrFields) . '
FROM ' . $arrOptions['table'] . implode('', $arrJoins);

// Where condition
if (!empty($arrOptions['column'])) {
if (!\is_array($arrOptions['column'])) {
$arrOptions['column'] = array($arrOptions['table'] . '.' . $arrOptions['column'] . '=?');
}

$strWhere = ' AND ' . implode(' AND ', $arrOptions['column']);
}

// The model must never find a language record
$strQuery .= " WHERE {$arrOptions['table']}.language=''" . $strWhere;

// Group by
if ($arrOptions['group'] !== null) {
$strQuery .= ' GROUP BY ' . $arrOptions['group'];
}

return 'SELECT COUNT(*) AS count FROM ('.$strQuery.') c1';
}

/**
* Return select statement to load product data including multilingual fields
*
* @param array $arrOptions an array of columns
* @param array $arrJoinAliases an array of table join aliases
*
* @return string
*
* @deprecated use buildFindQuery introduced in Contao 3.3
*/
protected static function buildQueryString($arrOptions, $arrJoinAliases = array('t' => 'tl_iso_producttype'))
{
$arrOptions['joinAliases'] = $arrJoinAliases;

return static::buildFindQuery((array) $arrOptions);
}
}

+ 2097
- 0
api2/ProductCollection.php
Plik diff jest za duży
Wyświetl plik


+ 489
- 0
api2/ProductCollectionItem.php Wyświetl plik

@@ -0,0 +1,489 @@
<?php

/*
* Isotope eCommerce for Contao Open Source CMS
*
* Copyright (C) 2009 - 2019 terminal42 gmbh & Isotope eCommerce Workgroup
*
* @link https://isotopeecommerce.org
* @license https://opensource.org/licenses/lgpl-3.0.html
*/

namespace Isotope\Model;

use Contao\Database;
use Contao\Model;
use Contao\StringUtil;
use Contao\System;
use Haste\Data\Plain;
use Isotope\Interfaces\IsotopeProduct;
use Isotope\Interfaces\IsotopeProductWithOptions;
use Isotope\Isotope;


/**
* ProductCollectionItem represents an item in a product collection.
*
* @property int $id
* @property int $pid
* @property int $tstamp
* @property int $product_id
* @property string $type
* @property string $sku
* @property string $name
* @property mixed $configuration
* @property int $quantity
* @property float $price
* @property float $tax_free_price
* @property string $tax_id
* @property int $jumpTo
*/
class ProductCollectionItem extends \Model
{

/**
* Name of the current table
* @var string
*/
protected static $strTable = 'tl_iso_product_collection_item';

/**
* Cache the current product
* @var IsotopeProduct|IsotopeProductWithOptions|false
*/
protected $objProduct = false;

/**
* Cache downloads for the collection item
* @var array
*/
protected $arrDownloads;

/**
* Errors
* @var array
*/
protected $arrErrors = [];

/**
* True if product collection is locked
* @var bool
*/
protected $blnLocked = false;


/**
* Check if collection item is available
*
* @return bool
*/
public function isAvailable()
{
if ($this->isLocked()) {
return true;
}

if (isset($GLOBALS['ISO_HOOKS']['itemIsAvailable']) && \is_array($GLOBALS['ISO_HOOKS']['itemIsAvailable'])) {
foreach ($GLOBALS['ISO_HOOKS']['itemIsAvailable'] as $callback) {
$available = System::importStatic($callback[0])->{$callback[1]}($this);

// If return value is boolean then we accept it as result
if (true === $available || false === $available) {
return $available;
}
}
}

if (!$this->hasProduct() || !$this->getProduct()->isAvailableForCollection($this->getRelated('pid'))) {
return false;
}

$arrConfig = $this->getOptions();
foreach ($this->getProduct()->getOptions() as $k => $v) {
if ($arrConfig[$k] !== $v) {
return false;
}
}

return true;
}

/**
* Return true if product collection item is locked
*/
public function isLocked()
{
return $this->blnLocked;
}

/**
* Lock item, necessary if product collection is locked
*/
public function lock()
{
$this->blnLocked = true;
}

/**
* Delete downloads when deleting product collection item
*
* @return int
*/
public function delete()
{
$intId = $this->id;
$intAffected = parent::delete();

if ($intAffected) {
Database::getInstance()->query("DELETE FROM tl_iso_product_collection_download WHERE pid=$intId");
}

return $intAffected;
}

/**
* Get the product related to this item
*
* @param bool $blnNoCache
*
* @return IsotopeProduct|null
*/
public function getProduct($blnNoCache = false)
{
if (false === $this->objProduct || true === $blnNoCache) {

$this->objProduct = null;

/** @var string|\Isotope\Model\Product $strClass */
$strClass = Product::getClassForModelType($this->type);

if ($strClass == '' || !class_exists($strClass)) {
System::log('Error creating product object of type "' . $this->type . '"', __METHOD__, TL_ERROR);

return null;
}

try {
$this->objProduct = $strClass::findByPk($this->product_id);
} catch (\Exception $e) {
$this->objProduct = null;
$this->addError($e->getMessage());
}

if (null !== $this->objProduct && $this->objProduct instanceof IsotopeProductWithOptions) {
try {
if ($this->objProduct instanceof Model) {
$this->objProduct = clone $this->objProduct;
$this->objProduct->preventSaving(false);
$this->objProduct->id = $this->product_id;
}

$this->objProduct->setOptions($this->getOptions());
} catch (\RuntimeException $e) {
$this->addError($GLOBALS['TL_LANG']['ERR']['collectionItemNotAvailable']);
}
}
}

return $this->objProduct;
}

/**
* Return boolean flag if product could be loaded
*
* @return bool
*/
public function hasProduct()
{
return (null !== $this->getProduct());
}

/**
* Get product SKU. Automatically falls back to the collection item table if product is not found.
*
* @return string
*/
public function getSku()
{
return (string) ($this->isLocked() || !$this->hasProduct()) ? $this->sku : $this->getProduct()->getSku();
}

/**
* Get product name. Automatically falls back to the collection item table if product is not found.
*
* @return string
*/
public function getName()
{
return (string) ($this->isLocked() || !$this->hasProduct()) ? $this->name : $this->getProduct()->getName();
}

/**
* Returns key-value array for variant-enabled and customer editable attributes.
*
* @return array
*
* @deprecated Use getOptions()
*/
public function getAttributes()
{
return $this->getOptions();
}

/**
* Returns key-value array for variant-enabled and customer editable attributes.
*
* @return array
*/
public function getOptions()
{
$arrConfig = StringUtil::deserialize($this->configuration);

return \is_array($arrConfig) ? $arrConfig : [];
}

/**
* Get product configuration
*
* @return array
*
* @deprecated Deprecated since Isotope 2.4, to be removed in Isotope 3.0. Use getOptions() instead.
*/
public function getConfiguration()
{
$arrConfig = StringUtil::deserialize($this->configuration);

if (empty($arrConfig) || !\is_array($arrConfig)) {
return array();
}

if ($this->hasProduct()) {
return Isotope::formatProductConfiguration($arrConfig, $this->getProduct());

} else {
foreach ($arrConfig as $k => $v) {
$arrConfig[$k] = new Plain($v, $k);
}

return $arrConfig;
}
}

/**
* Get product price. Automatically falls back to the collection item table if product is not found.
*
* @return string
*/
public function getPrice()
{
if ($this->isLocked() || !$this->hasProduct()) {
return $this->price;
}

$objPrice = $this->getProduct()->getPrice($this->getRelated('pid'));

if (null === $objPrice) {
return '';
}

return $objPrice->getAmount((int) $this->quantity, $this->getOptions());
}

/**
* Get tax free product price. Automatically falls back to the collection item table if product is not found.
*
* @return string
*/
public function getTaxFreePrice()
{
if ($this->isLocked() || !$this->hasProduct()) {
return $this->tax_free_price;
}

$objPrice = $this->getProduct()->getPrice($this->getRelated('pid'));

if (null === $objPrice) {
return '';
}

return $objPrice->getNetAmount((int) $this->quantity, $this->getOptions());
}

/**
* Get original product price. Automatically falls back to the collection item table if product is not found.
*
* @return string
*/
public function getOriginalPrice()
{
if ($this->isLocked() || !$this->hasProduct()) {
return $this->price;
}

$objPrice = $this->getProduct()->getPrice($this->getRelated('pid'));

if (null === $objPrice) {
return '';
}

return $objPrice->getOriginalAmount((int) $this->quantity, $this->getOptions());
}

/**
* Get product price multiplied by the requested product quantity
*
* @return string
*/
public function getTotalPrice()
{
return (string) ($this->getPrice() * (int) $this->quantity);
}

/**
* Get original product price multiplied by the requested product quantity
*
* @return string
*/
public function getTotalOriginalPrice()
{
return (string) ($this->getOriginalPrice() * (int) $this->quantity);
}

/**
* Get tax free product price multiplied by the requested product quantity
*
* @return string
*/
public function getTaxFreeTotalPrice()
{
return (string) ($this->getTaxFreePrice() * (int) $this->quantity);
}

/**
* Return downloads associated with this product collection item
*
* @return ProductCollectionDownload[]
*/
public function getDownloads()
{
if (null === $this->arrDownloads) {
$this->arrDownloads = array();

$objDownloads = ProductCollectionDownload::findBy('pid', $this->id);

if (null !== $objDownloads) {
while ($objDownloads->next()) {
$this->arrDownloads[] = $objDownloads->current();
}
}
}

return $this->arrDownloads;
}

/**
* Increase quantity of product collection item
*
* @param int $intQuantity
*
* @return self
*/
public function increaseQuantityBy($intQuantity)
{
$time = time();

Database::getInstance()->query("
UPDATE tl_iso_product_collection_item
SET tstamp=$time, quantity=(quantity+" . (int) $intQuantity . ')
WHERE id=' . $this->id
);

$this->tstamp = $time;
$this->quantity = Database::getInstance()
->query("SELECT quantity FROM tl_iso_product_collection_item WHERE id=" . $this->id)
->quantity
;

return $this;
}

/**
* Decrease quantity of product collection item
*
* @param int $intQuantity
*
* @return self
*/
public function decreaseQuantityBy($intQuantity)
{
if (($this->quantity - $intQuantity) < 1) {
throw new \UnderflowException('Quantity of product collection item cannot be less than 1.');
}

$time = time();

Database::getInstance()->query("
UPDATE tl_iso_product_collection_item
SET tstamp=$time, quantity=(quantity-" . (int) $intQuantity . ')
WHERE id=' . $this->id
);

$this->tstamp = $time;
$this->quantity = Database::getInstance()
->query('SELECT quantity FROM tl_iso_product_collection_item WHERE id=' . $this->id)
->quantity
;

return $this;
}

/**
* Calculate the sum of a database column
*
* @param string $strField
* @param mixed $strColumn
* @param mixed $varValue
*
* @return int
*/
public static function sumBy($strField, $strColumn = null, $varValue = null)
{
$strQuery = "SELECT SUM($strField) AS sum FROM tl_iso_product_collection_item";

if ($strColumn !== null) {
$strQuery .= ' WHERE ' . (\is_array($strColumn) ? implode(' AND ', $strColumn) : static::$strTable . '.' . $strColumn . "=?");
}

return (int) Database::getInstance()->prepare($strQuery)->execute($varValue)->sum;
}

/**
* Add an error message
*
* @param string $strError
*/
public function addError($strError)
{
$this->arrErrors[] = $strError;
}

/**
* Return true if the collection item has errors
*
* @return bool
*/
public function hasErrors()
{
return 0 !== \count($this->arrErrors);
}

/**
* Return the errors array
*
* @return array
*/
public function getErrors()
{
return $this->arrErrors;
}
}

+ 7
- 0
api2/apiTestCreateCustomer.php Wyświetl plik

@@ -0,0 +1,7 @@
<?php

require __DIR__ . '/ApiExact.php';
require __DIR__ . '/IsotopeDatabaseHandler.php';

$apiExact = new ApiExact();
var_dump($apiExact->createCustomer());

+ 7
- 0
api2/apiTestCreateSalesOrder.php Wyświetl plik

@@ -0,0 +1,7 @@
<?php

require __DIR__ . '/ApiExact.php';
require __DIR__ . '/IsotopeDatabaseHandler.php';

$apiExact = new ApiExact();
var_dump($apiExact->createSalesOrder());

+ 9
- 0
api2/apiTestGetAccessToken.php Wyświetl plik

@@ -0,0 +1,9 @@
<?php

require __DIR__ . '/ApiExact.php';
require __DIR__ . '/IsotopeDatabaseHandler.php';

$b = __DIR__ . '/ApiExact.php';

$apiExact = new ApiExact();
echo $apiExact->getAccessToken() . "\n";

+ 10
- 0
api2/apiUpdateProducts.php Wyświetl plik

@@ -0,0 +1,10 @@
<?php

require __DIR__ . '/ApiExact.php';
require __DIR__ . '/IsotopeDatabaseHandler.php';

$apiExact = new ApiExact();
$databaseHandler = new IsotopeDatabaseHandler();
$products = $apiExact->getProducts();
$databaseHandler->processProducts($products);


+ 11
- 0
api2/dan.txt Wyświetl plik

@@ -0,0 +1,11 @@
string(3) "586"
string(8) "WR000400"
string(2) "41"
string(24) "Schaum-Sprüher 1,5Liter"
NULL
string(3) "587"
string(8) "WR000250"
string(2) "42"
string(11) "Tragebeutel"
NULL
string(15) "fuck off contao"

+ 1
- 0
api2/tokenData Wyświetl plik

@@ -0,0 +1 @@
{"access_token":"stampDE001.gAAAAKLeomGUJnO_59CX7KmwE4tkt1bPxLR7EfU5E2eHjj6LeU2dAPfkRstKdlJJoudf3y6ehyKwF5wQQ1SljbA5sRrGwQsw4M876o0RRtxVHwyR6_QSoM7kP3qj7HfJLEDWjY-KrlJf_rlKtZZ2sRGJkX9jbd-4XDA_uqQECkf2NnIfVAIAAIAAAAC2ADvWhSjIjtyvs4_rhH9HzqaVKUbQthUoKf6StMSX4O5GzilHhM2cQCQJly_7hbpKHJMe4o167cPYIQLTeOilQdlQh21nhGykWfOKtXG31h210WtT2bg8Kc0MtrHQGi_f161wE_xINPXr-H7GTcXLe4klIGUkv0kmmmUacNoDUDfU1fPRTCCFvmkRnE7iBzd116aY9XMVw6mDucFExI9U-dLOD8IFWCPa9U6DSd50T7WoglDHC8Enh-J92C1c8kAhCCiGyA5GHyb9S1eJAnHFDYzOl9y0pwtIHekBJq-LxpzAbVieBUZrXEsTJnD7eiHvKoy9g_Bl-jOVgPTJT29n0dFDUClE_iQ6MGydAiEVKoyhQO20W7EJg64G8l6ZHZrd8r1J36TZ4jmdZ8E8_wqw3MzYGZm4vEXK4G0Lf2-j5uD0eh3aKPQCCdn_jEiCaEABQ-bsvd9sQazNNHbDwzl82GZdlhY8sqln14NpNloahOxhY9-jKu5nT9IBN2EftT-VoLNWp_E4ydPltM_7SS0Xro9j8nf7xNjpBDa__GeR2QSg_He5VR6u2n4lUbU1Qzks2W38RiCA3jXrQ-T6FCtiiaf5rhMzudqeq2c94Ggbn1g7BD_3S9xUFeA5kmSCkn1NZ3RzSqmkMq4IVPcbsJFYeoCiuZ1gYcvSOoBMHN8mjZ8j3xpPEvzJSX39arD5IeNqrLZDXuEU5aORiRnqjN6kPC4w1GQbbMsEL94r8tYGy8VkW7bstxkar7vb2J8vOPhLamN0mcxJdTZCCJ8tj18e","token_type":"bearer","expires_in":"600","refresh_token":"stampDE001.vhK0!IAAAANKu8TC2-MBN5wFyro7uwanpxXBsRFy7YkYSPaM9jMCm8QEAAAEVEJUYfzoc3Q1hatgTMAOm__pmwjTgdkOyzb7qUj7oRvir1XftD_cb5eE7J-06Q0SxWegFPfULR65PDc6Id0pDXZbugqaKhK24Gg9Gr0x74g4EW8HMYJ0PsW1uqvpOb_ph9mQxYllDQf598_uPZvuvxAMN4NOipKiHEXn9tExbn-rmJdrV9bwa3fAm5b2ALbzfBEKCuzuwLkrjH3cheaMC2TaksJ6M2we9IeAezapsyOVEl6QtnhY67L3Irp_bUV_SCZ6H-gbPDX3mFujxyw-lvRoC-ZcgIpM8EuWC3pN24abFp7Qaj8wE2X9_oAgvoJgn42lHl-x6M-QChdf78f6wiCPquk3OwwuBYsjmiJrHFQt4a6nRS-Y_A-J8IOdwq-_KZkvNTY5UI1ZmiGH5pvIDfaXTcvPn6r3C-sQecdImJM43Y_tgZ9a6p2gwPi8zNZSkXfwQH5bfal6tBcH43_8eGSp_M-VbLb7TotZfJgEIePSlZ--VV1aTaRMJ78_JBloFWvQabvhYEvvuvRaePbdGRgXzj95KgQeW8DzBXE6_zHSmhwkUEv11xgXimH3BFToSV_F25ZND7H3OkVM1-M8Jhbvrzh4SR5wRV5WfzC83Mk58HmDtI7xR0owYAu-9mtidFsK8mRC3nJzZSOWdGenZ","expiry_time":1657726518}

Ładowanie…
Anuluj
Zapisz