Files
kupshop/bundles/KupShop/DeliveryPriceListBundle/DeliveryPriceCalculator.php
2025-08-02 16:30:27 +02:00

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;
}
}