Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
21.74% covered (danger)
21.74%
5 / 23
CRAP
7.34% covered (danger)
7.34%
8 / 109
Senh\Lib\Prices\KortingFactory
0.00% covered (danger)
0.00%
0 / 1
21.74% covered (danger)
21.74%
5 / 23
2550.94
7.34% covered (danger)
7.34%
8 / 109
 __construct
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
2 / 2
 init
0.00% covered (danger)
0.00%
0 / 1
56
0.00% covered (danger)
0.00%
0 / 13
 getAllowedProductIds
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 setAllowedProductIds
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 2
 setAvailableKortingModels
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 2
 getAvailableKortingModels
0.00% covered (danger)
0.00%
0 / 1
30
0.00% covered (danger)
0.00%
0 / 8
 getModelByKortingsCode
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 2
 hasModel
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 hasModelWithAllowedProductId
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
1 / 1
 getModel
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 setModelByParameters
0.00% covered (danger)
0.00%
0 / 1
20
0.00% covered (danger)
0.00%
0 / 13
 getModelByParameters
0.00% covered (danger)
0.00%
0 / 1
90
0.00% covered (danger)
0.00%
0 / 24
 getModelBySession
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 3
 getSessionSaleId
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 1
 getModelByCookies
0.00% covered (danger)
0.00%
0 / 1
20
0.00% covered (danger)
0.00%
0 / 7
 getSpecificModelFromCookies
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 6
 setModel
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
3 / 3
 calcDatumStop
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 8
 hasKortingsCodeParameters
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 1
 hasKortingsCodeCookie
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 2
 getCookieName
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 getCookie
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 4
 setCookie
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 3
<?php
namespace Senh\Lib\Prices;
use Senh\Lib\Prices\PricesHelper;
use Senh\Lib\Managers\InternalProductManager;
use Senh\Lib\Prices\KortingModel;
use DateTime;
use DateInterval;
use DateTimeZone;
class KortingFactory
{
    const COOKIE_NAME_PREFIX = 'kc_';
    const COOKIE_LIFETIME = 31536000; // 1 year
    const SESSION_NAME_ACTIVE_ACTION = 'kortings_actie';
    /**
     * @var InternalProductManager;
     */
    protected $internalProductManager;
    /**
     * @var KortingModel: use setModel to set, session info is set as well
     */
    protected $model;
    /**
     * @var array
     */
    protected $allowedProductIds;
    /**
     * @var KortingModel[]
     */
    protected $availableKortingModels;
    /**
     * @param InternalProductManager $internalProductManager
     */
    public function __construct(InternalProductManager $internalProductManager)
    {
        $this->internalProductManager = $internalProductManager;
    }
    public function init()
    {
        if (session_id() == '') {
            session_start();
        }
        $this->internalProductManager = InternalProductManager::getInstance();
        $model = $this->getModelByParameters();
        // get from session
        if (!$model instanceof KortingModel) {
            $model = $this->getModelBySession();
            // if the model's date from the parameter is not valid, we still use it, since it is a specific request.
            // however, from the session we do not want to indefinitely show an 0 0 0 0 timer
            if ($model instanceof KortingModel && !$model->isFutureDatumStop()) {
                $model = null;
            }
        }
        // get from cookies
        if (!$model instanceof KortingModel) {
            $model = $this->getModelByCookies(); // only returns models that have valid dates
        }
        if ($model instanceof KortingModel) {
            $this->setModel($model);
        }
        return $this;
    }
    /**
     * @return array
     */
    public function getAllowedProductIds()
    {
        return $this->allowedProductIds;
    }
    /**
     * @param $allowedProductIds
     * @return $this
     */
    public function setAllowedProductIds($allowedProductIds)
    {
        $this->allowedProductIds = $allowedProductIds;
        return $this;
    }
    /**
     * @param KortingModel[]|null $availableKortingModels
     */
    public function setAvailableKortingModels($availableKortingModels)
    {
        $this->availableKortingModels = $availableKortingModels;
    }
    /**
     * @param $allowedProductIds: if provided, limit to those ids
     * @return KortingModel[]
     */
    public function getAvailableKortingModels($allowedProductIds = null)
    {
        /**
         * @var KortingModel[] $availableModels
         */
        if (!$allowedProductIds) {
            return $this->availableKortingModels;
        }
        $allowedModels = array();
        foreach ($allowedProductIds as $allowedProductId) {
            foreach ($this->availableKortingModels as $availableModel) {
                if ($availableModel->hasAllowedProductId($allowedProductId)) {
                    $allowedModels[$availableModel->getCode()] = $availableModel;
                }
            }
        }
        return $allowedModels;
    }
    /**
     * @param $kortingsCode
     *
     * @return KortingModel|null
     */
    public function getModelByKortingsCode($kortingsCode, $allowedProductIds = null)
    {
        $availableKortingsModels = $this->getAvailableKortingModels($allowedProductIds);
        return isset($availableKortingsModels[$kortingsCode]) ? $availableKortingsModels[$kortingsCode] : null;
    }
    /**
     * @return bool
     */
    public function hasModel()
    {
        return $this->model instanceof KortingModel;
    }
    public function hasModelWithAllowedProductId($productId)
    {
        return $this->hasModel() && $this->model->hasAllowedProductId($productId);
    }
    /**
     * @return KortingModel
     */
    public function getModel()
    {
        return $this->model;
    }
    /**
     * This forces the model to be set by the code.
     * It checks the cookies first to get the datumStop, if not aivailable, the parameter datumStop will be used instead
     *
     * @param string $kortingsCode
     * @param DateTime $datumVerstuurd
     * @return null|KortingModel
     */
    public function setModelByParameters($kortingsCode, $datumVerstuurd, $dagNummer, $ignoreCookie = false)
    {
        if (!$ignoreCookie) {
            $model = $this->getSpecificModelFromCookies($kortingsCode);
            if ($model instanceof KortingModel) {
                $this->setModel($model);
                return $model;
            }
        }
        $model = $this->getModelByKortingsCode($kortingsCode, $this->allowedProductIds);
        if (!$model instanceof KortingModel) {
            return null;
        }
        $datumStop = $this->calcDatumStop($kortingsCode, $datumVerstuurd, $dagNummer);
        $model->setDatumStop($datumStop);
        $this->setModel($model);
        $this->setCookie($model);
        return $model;
    }
    /**
     * @return KortingModel|null
     */
    protected function getModelByParameters()
    {
        if (!$this->hasKortingsCodeParameters()) {
            return null;
        }
        $kortingsCode = $_GET['kc'];
        $kortingModel = $this->getModelByKortingsCode($kortingsCode, $this->allowedProductIds);
        if ($kortingModel instanceof KortingModel) {
            $dagNummer = isset($_GET['dg']) ? $_GET['dg'] : 1;
            $datumVerstuurd = isset($_GET['dt']) ? $_GET['dt'] : null;
            if ($datumVerstuurd) {
                $datumVerstuurd .= ' 00:00:00';
                
                // we try to keep everything UTC
                $datumVerstuurd = DateTime::createFromFormat('d/m/Y H:i:s', $datumVerstuurd,new DateTimeZone('UTC'));
                if (!$datumVerstuurd instanceof DateTime) {
                    return null;
                }
                // correct for local timezone difference (timer should stop at midnight locallay)
                $localTimeZone = new DateTimeZone('Europe/Amsterdam');
                $datumVerstuurd->sub(new DateInterval('PT'.$localTimeZone->getOffset($datumVerstuurd).'S'));
            } else {
                $datumVerstuurd = new DateTime(null,new DateTimeZone('UTC'));
            }
            $datumStop = $this->calcDatumStop($kortingsCode, $datumVerstuurd, $dagNummer);
            $kortingModel->setDatumStop($datumStop);
            if ($this->hasKortingsCodeCookie($kortingsCode)) {
                // overschrijf datumStop eventueel met de cookie data
                $datumStop = $this->getCookie($kortingsCode);
            } elseif ($kortingModel->isFutureDatumStop()) {
                // set cookie
                $this->setCookie($kortingModel);
            }
            // als is de datum in het verleden is, dan moet 'actie verlopen' getoond worden
            $kortingModel->setDatumStop($datumStop);
            return $kortingModel;
        }
        return null;
    }
    /**
     * @return KortingModel|null
     */
    protected function getModelBySession()
    {
        if (!isset($_SESSION[self::SESSION_NAME_ACTIVE_ACTION])) {
            return null;
        }
        return $this->getSpecificModelFromCookies($_SESSION[self::SESSION_NAME_ACTIVE_ACTION]);
    }
    /**
     * @return null | float
     */
    protected function getSessionSaleId()
    {
        return isset($_SESSION['sale_id']) ? $_SESSION['sale_id'] : null;
    }
    /**
     * Loop through available kortingscodes to see if any of these is set in the cookies and return the first valid one
     * @return KortingModel|null
     */
    protected function getModelByCookies()
    {
        foreach ($this->getAvailableKortingModels($this->allowedProductIds) as $kortingsCode => $kortingModel) {
            if ($this->hasKortingsCodeCookie($kortingsCode)) {
                $datumStop = $this->getCookie($kortingsCode);
                $kortingModel->setDatumStop($datumStop);
                if ($kortingModel->isFutureDatumStop()) {
                    return $kortingModel;
                }
            };
        }
        return null;
    }
    /**
     * Return the KortingModel that matches $kortingsCode if it is an available action AND if the kortingsCode is set in
     * the cookies, and the model is allowed for the current product even if the datumStop is not valid.
     *
     * @param $kortingsCode
     * @return null | KortingModel
     */
    protected function getSpecificModelFromCookies($kortingsCode)
    {
        $kortingModel = $this->getModelByKortingsCode($kortingsCode, $this->allowedProductIds);
        if (!$kortingModel instanceof KortingModel || !$this->hasKortingsCodeCookie($kortingsCode)) {
            return null;
        }
        $datumStop = $this->getCookie($kortingsCode);
        $kortingModel->setDatumStop($datumStop);
        return $kortingModel;
    }
    /**
     * @param KortingModel|null $kortingModel
     */
    public function setModel($kortingModel)
    {
        $this->model = $kortingModel;
        $_SESSION[self::SESSION_NAME_ACTIVE_ACTION] = $kortingModel ? $kortingModel->getCode() : null;;
    }
    /**
     * @param string $kortingsCode
     * @param DateTime $datumVerstuurd
     * @param int $dagNummer
     *
     * @return DateTime
     */
    protected function calcDatumStop($kortingsCode, $datumVerstuurd, $dagNummer)
    {
        $model = $this->getModelByKortingsCode($kortingsCode);
        $urenOver = $model->getDuur() - ($dagNummer - 1) * 24;
        if ($urenOver < 0) {
            $datumStop = $datumVerstuurd->sub(new DateInterval("PT1H"));
        } else {
            $minutenOver = round(($urenOver - floor($urenOver)) * 60);
            $urenOver = floor($urenOver);
            $datumStop = $datumVerstuurd->add(new DateInterval("PT{$urenOver}H{$minutenOver}M"));
        }
        return $datumStop;
    }
    protected function hasKortingsCodeParameters()
    {
        return (isset($_GET['kc']) && isset($_GET['pn']));
    }
    protected function hasKortingsCodeCookie($kortingsCode)
    {
        $cookieName = $this->getCookieName($kortingsCode);
        return isset($_COOKIE[$cookieName]);
    }
    protected function getCookieName($kortingsCode)
    {
        return self::COOKIE_NAME_PREFIX.$kortingsCode;
    }
    /**
     * @return DateTime: datumStop
     */
    protected function getCookie($kortingsCode)
    {
        $cookieName = $this->getCookieName($kortingsCode);
        $dateTime = new DateTime(date('Y-m-d H:i:s', $_COOKIE[$cookieName]), new \DateTimeZone(SERVER_TIME_ZONE));
        $dateTime->setTimezone(new DateTimeZone('UTC'));
        return $dateTime;
    }
    /**
     * @param KortingModel $model
     */
    protected function setCookie($model)
    {
        $cookieName = $this->getCookieName($model->getCode());
        // note: DateTime->getTimeStamp() returns the time that corresponds with the server's time zone. This is corrected in getCookie.
        setcookie($cookieName, $model->getDatumStop()->getTimestamp(), time() + self::COOKIE_LIFETIME, '/');
    }
}