428 lines
11 KiB
PHP
428 lines
11 KiB
PHP
<?php
|
|
|
|
namespace KupShop\DeliveryPriceListBundle;
|
|
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
use KupShop\DeliveryPriceListBundle\Entity\Price;
|
|
use KupShop\DeliveryPriceListBundle\Entity\PriceList;
|
|
use KupShop\DeliveryPriceListBundle\Entity\RegionCountry;
|
|
use KupShop\DeliveryPriceListBundle\Entity\WeightGroup;
|
|
use KupShop\I18nBundle\Entity\Country;
|
|
use KupShop\KupShopBundle\Context\CountryContext;
|
|
use KupShop\KupShopBundle\Util\Contexts;
|
|
use KupShop\KupShopBundle\Util\Functional\Mapping;
|
|
|
|
class DeliveryPriceCalculator
|
|
{
|
|
/** @var EntityManagerInterface */
|
|
private $em;
|
|
|
|
private $pricelistId;
|
|
|
|
private $regionId;
|
|
|
|
private $country;
|
|
|
|
private $totalWeight;
|
|
private $maxItemWeight;
|
|
|
|
private $divide = false;
|
|
|
|
private $priceMultiplier = 1;
|
|
|
|
// without VAT
|
|
private $insurancePrices = [
|
|
1 => 3.00,
|
|
2 => 3.31,
|
|
];
|
|
|
|
public function __construct(EntityManagerInterface $em)
|
|
{
|
|
$this->em = $em;
|
|
}
|
|
|
|
/**
|
|
* @param \Decimal $price
|
|
*
|
|
* @return int|mixed
|
|
*/
|
|
public function getInsurancePrice($price)
|
|
{
|
|
$multiplier = ceil($price->div(\Decimal::create(1000))->asFloat());
|
|
|
|
if ($this->totalWeight <= 10) {
|
|
$iprice = $this->insurancePrices[1] * $multiplier;
|
|
} elseif ($this->totalWeight > 10) {
|
|
$iprice = $this->insurancePrices[2] * $multiplier;
|
|
} else {
|
|
$iprice = 0;
|
|
}
|
|
|
|
$iprice = \Decimal::create($iprice);
|
|
$iprice = $iprice->addVat(getVat());
|
|
|
|
return $price->add($iprice);
|
|
}
|
|
|
|
/**
|
|
* @param $price \Decimal
|
|
*
|
|
* @return \Decimal
|
|
*/
|
|
public function mulPrice($price)
|
|
{
|
|
$multiplier = \Decimal::ensureDecimal($this->priceMultiplier);
|
|
|
|
return $price->mul($multiplier, 8);
|
|
}
|
|
|
|
public function checkWeightGroup()
|
|
{
|
|
$onMaxWeight = $this->getOnMaxWeight();
|
|
if ($onMaxWeight == 'divide') {
|
|
$this->setDivide(true);
|
|
}
|
|
|
|
if ($this->getWeightGroupId()) {
|
|
return true;
|
|
}
|
|
|
|
if ($onMaxWeight == 'divide') {
|
|
if ($this->divide) {
|
|
return 'divide';
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/** @return \Decimal */
|
|
public function getPriceWithoutVat($vat, $priceWithVat = null)
|
|
{
|
|
if ($priceWithVat == null) {
|
|
$price = $this->getPrice();
|
|
} else {
|
|
$price = $priceWithVat;
|
|
}
|
|
|
|
$decimal = \Decimal::ensureDecimal($price);
|
|
$withoutVat = $decimal->removeVat($vat);
|
|
|
|
return $withoutVat;
|
|
}
|
|
|
|
/** @return \Decimal */
|
|
public function getValueVat($valWithoutVat, $priceWithVat = null)
|
|
{
|
|
if ($priceWithVat == null) {
|
|
$price = $this->getPrice();
|
|
} else {
|
|
$price = $priceWithVat;
|
|
}
|
|
|
|
$valueVat = $price->sub($valWithoutVat);
|
|
|
|
return $valueVat;
|
|
}
|
|
|
|
/** @return \Decimal */
|
|
public function getPrice()
|
|
{
|
|
$repository = $this->em->getRepository(Price::class);
|
|
|
|
$this->getRegionByCountry($this->country);
|
|
|
|
$data = $repository->findOneBy(['priceList' => $this->pricelistId, 'weightGroup' => $this->getWeightGroupId(), 'region' => $this->regionId]);
|
|
if ($data) {
|
|
if ($this->divide) {
|
|
// add insurance
|
|
if (findModule('delivery_pricelist', 'insurance') && $this->hasCountryInsurance($this->country)) {
|
|
return $this->getInsurancePrice($this->mulPrice($data->getPrice()));
|
|
}
|
|
|
|
return $this->mulPrice($data->getPrice());
|
|
}
|
|
// add insurance
|
|
if (findModule('delivery_pricelist', 'insurance') && $this->hasCountryInsurance($this->country)) {
|
|
return $this->getInsurancePrice($data->getPrice());
|
|
}
|
|
|
|
return $data->getPrice();
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @param string $countryId
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function hasCountryInsurance($countryId)
|
|
{
|
|
$qb = sqlQueryBuilder()->select('*')->from('delivery_pricelists_insurance')
|
|
->where('id_pricelist = :id_pricelist AND id_country = :id_country')
|
|
->setParameters([
|
|
'id_pricelist' => $this->getPricelistId(),
|
|
'id_country' => $countryId,
|
|
])->execute();
|
|
|
|
if ($qb->rowCount() != 0) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public function isDeliverySupported()
|
|
{
|
|
return $this->getRegionByCountry($this->country);
|
|
}
|
|
|
|
public function getRegionByCountry($countryId)
|
|
{
|
|
$country = $this->em->getRepository(Country::class);
|
|
$data = $country->findOneBy(['id' => $countryId]);
|
|
if ($data) {
|
|
$regionCountry = $this->em->getRepository(RegionCountry::class);
|
|
/** @var RegionCountry $region */
|
|
$region = $regionCountry->findOneBy(['pricelist' => $this->pricelistId, 'country' => $countryId]);
|
|
if ($region) {
|
|
$this->regionId = $region->getRegion();
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public function multiFetchDeliveryTime(array &$deliveries): void
|
|
{
|
|
$countryContext = Contexts::get(CountryContext::class);
|
|
|
|
$regionCountry = $this->em->getRepository(RegionCountry::class);
|
|
$items = Mapping::mapKeys(
|
|
$regionCountry->findBy(
|
|
[
|
|
'country' => $countryContext->getActiveId(),
|
|
'pricelist' => array_map(
|
|
function ($x) {
|
|
return $x->id_pricelist;
|
|
},
|
|
array_filter(
|
|
$deliveries,
|
|
function ($x) {
|
|
return !empty($x->id_pricelist);
|
|
}
|
|
)
|
|
),
|
|
]
|
|
),
|
|
function ($k, $v) {
|
|
return [$v->getPricelist()->getId(), $v];
|
|
}
|
|
);
|
|
|
|
foreach ($deliveries as &$delivery) {
|
|
if (!($item = $items[$delivery->id_pricelist] ?? null)) {
|
|
continue;
|
|
}
|
|
|
|
$delivery->pricelistDeliveryTime = $this->prepareDeliveryTime($item);
|
|
}
|
|
}
|
|
|
|
private function prepareDeliveryTime(?RegionCountry $item): ?array
|
|
{
|
|
if (!$item) {
|
|
return null;
|
|
}
|
|
|
|
if ($item->getDeliveryTimeMin() === null && $item->getDeliveryTimeMax() === null) {
|
|
return null;
|
|
}
|
|
|
|
return [
|
|
'min' => $item->getDeliveryTimeMin(),
|
|
'max' => $item->getDeliveryTimeMax(),
|
|
];
|
|
}
|
|
|
|
public function getDeliveryTime($countryId)
|
|
{
|
|
$qb = sqlQueryBuilder()->select('MIN(delivery_time_min) as deliveryMin', 'MAX(delivery_time_max) as deliveryMax')
|
|
->from('delivery_pricelists_regions_countries')
|
|
->where('id_country = ?')->setParameter(0, $countryId)
|
|
->execute()->fetch();
|
|
|
|
return [
|
|
'min' => $qb['deliveryMin'],
|
|
'max' => $qb['deliveryMax'],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @return bool|int|null
|
|
*/
|
|
public function getWeightGroupId()
|
|
{
|
|
$weightGroupId = false;
|
|
$repository = $this->em->getRepository(WeightGroup::class);
|
|
$data = $repository->findBy(['priceList' => $this->pricelistId], ['maxWeight' => 'ASC']);
|
|
if ($data) {
|
|
/** @var WeightGroup $wg */
|
|
foreach ($data as $wg) {
|
|
$groupMaxWeight = $wg->getMaxWeight();
|
|
if ($this->totalWeight <= $groupMaxWeight) {
|
|
$weightGroupId = $wg->getId();
|
|
break;
|
|
}
|
|
}
|
|
if ($weightGroupId) {
|
|
return $weightGroupId;
|
|
}
|
|
// pokud je povolene rozdeleni baliku
|
|
if ($this->divide == true) {
|
|
$wg = end($data);
|
|
$weight = $wg->getMaxWeight();
|
|
if ($this->maxItemWeight <= $weight) {
|
|
$divide = $this->findWeightGroupForDivide($data);
|
|
$weightGroupId = $divide['weightGroup'] ? $divide['weightGroup']->getId() : false;
|
|
if ($divide['multiplier'] !== null) {
|
|
$this->priceMultiplier = $divide['multiplier'];
|
|
}
|
|
} else {
|
|
$this->setDivide(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
return $weightGroupId;
|
|
}
|
|
|
|
public function getCountOfPackages()
|
|
{
|
|
$packagesWeight = null;
|
|
$packagesCount = null;
|
|
|
|
$repository = $this->em->getRepository(WeightGroup::class);
|
|
$data = $repository->findBy(['priceList' => $this->pricelistId], ['maxWeight' => 'ASC']);
|
|
if ($data) {
|
|
$divide = $this->findWeightGroupForDivide($data);
|
|
$packagesWeight = $divide['weightGroup'] ? $divide['weightGroup']->getMaxWeight() : null;
|
|
$packagesCount = $divide['multiplier'];
|
|
}
|
|
|
|
return [
|
|
'package_weight' => $packagesWeight,
|
|
'package_count' => $packagesCount,
|
|
];
|
|
}
|
|
|
|
private function findWeightGroupForDivide($weightGroups)
|
|
{
|
|
$bestGroup = false;
|
|
$bestMultiplier = null;
|
|
/** @var WeightGroup $group */
|
|
foreach ($weightGroups as $group) {
|
|
$multiplier = ceil($this->totalWeight / $group->getMaxWeight());
|
|
if ($bestMultiplier == null || $bestMultiplier > $multiplier) {
|
|
$bestMultiplier = $multiplier;
|
|
$bestGroup = $group;
|
|
}
|
|
}
|
|
|
|
return [
|
|
'weightGroup' => $bestGroup,
|
|
'multiplier' => $bestMultiplier,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @return string|null
|
|
*/
|
|
public function getPricelistCurrency($pricelistId)
|
|
{
|
|
$data = sqlQueryBuilder()->select('id_currency')->from('delivery_pricelists')
|
|
->where('id = ?')->setParameter(0, $pricelistId)->execute()->fetch();
|
|
|
|
if ($data) {
|
|
return $data['id_currency'];
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @return int
|
|
*/
|
|
public function getPricelistId()
|
|
{
|
|
return $this->pricelistId;
|
|
}
|
|
|
|
/**
|
|
* @return bool|string
|
|
*/
|
|
public function getOnMaxWeight()
|
|
{
|
|
$repository = $this->em->getRepository(PriceList::class);
|
|
/** @var PriceList $data */
|
|
$data = $repository->findOneBy(['id' => $this->pricelistId]);
|
|
if ($data) {
|
|
return $data->getOnMaxWeight();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @param $id string
|
|
*/
|
|
public function setCountry($id)
|
|
{
|
|
$this->country = $id;
|
|
}
|
|
|
|
public function setDivide($bool)
|
|
{
|
|
$this->divide = $bool;
|
|
}
|
|
|
|
public function getDivide()
|
|
{
|
|
return $this->divide;
|
|
}
|
|
|
|
public function setRegionId($id)
|
|
{
|
|
$this->regionId = $id;
|
|
}
|
|
|
|
public function setPricelistId($id)
|
|
{
|
|
$this->pricelistId = $id;
|
|
}
|
|
|
|
public function setTotalWeight($weight)
|
|
{
|
|
$this->totalWeight = $weight;
|
|
}
|
|
|
|
public function setMaxItemWeight($weight)
|
|
{
|
|
$this->maxItemWeight = $weight;
|
|
}
|
|
|
|
public function setPriceMultiplier($mul)
|
|
{
|
|
$this->priceMultiplier = $mul;
|
|
}
|
|
|
|
public function getPriceMultiplier()
|
|
{
|
|
return $this->priceMultiplier;
|
|
}
|
|
}
|