Files
esun3d/app/code/Esun/Cart/Model/ShippingEstimate.php
T
2026-09-15 10:35:56 +08:00

147 lines
4.5 KiB
PHP

<?php
declare(strict_types=1);
namespace Esun\Cart\Model;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\Pricing\PriceCurrencyInterface;
use Magento\Quote\Model\Quote;
use Magento\Quote\Model\Quote\Item\AbstractItem;
use Magento\SalesRule\Model\ResourceModel\Rule\CollectionFactory as RuleCollectionFactory;
use Magento\Store\Model\ScopeInterface;
class ShippingEstimate
{
private const XML_PATH_FLATRATE_ACTIVE = 'carriers/flatrate/active';
private const XML_PATH_FLATRATE_PRICE = 'carriers/flatrate/price';
private const XML_PATH_FLATRATE_TITLE = 'carriers/flatrate/title';
private const XML_PATH_FLATRATE_NAME = 'carriers/flatrate/name';
public function __construct(
private readonly ScopeConfigInterface $scopeConfig,
private readonly PriceCurrencyInterface $priceCurrency,
private readonly RuleCollectionFactory $ruleCollectionFactory
) {
}
public function getData(Quote $quote): array
{
$storeId = (int)$quote->getStoreId();
$available = $this->hasPhysicalItems($quote)
&& $this->scopeConfig->isSetFlag(
self::XML_PATH_FLATRATE_ACTIVE,
ScopeInterface::SCOPE_STORE,
$storeId
);
$freeShipping = $available && $this->isFreeShipping($quote);
$baseAmount = $available && !$freeShipping
? (float)$this->scopeConfig->getValue(
self::XML_PATH_FLATRATE_PRICE,
ScopeInterface::SCOPE_STORE,
$storeId
)
: 0.0;
$amount = $available
? (float)$this->priceCurrency->convertAndRound($baseAmount, $quote->getStore())
: 0.0;
return [
'available' => $available,
'free_shipping' => $freeShipping,
'quantity' => $this->getPhysicalQuantity($quote),
'base_amount' => $baseAmount,
'amount' => $amount,
'description' => $available ? $this->getDescription($storeId, $freeShipping) : '',
];
}
public function isFreeShipping(Quote $quote): bool
{
if (!$this->hasPhysicalItems($quote) || !$quote->getStoreId()) {
return false;
}
$address = clone $quote->getShippingAddress();
$address->setTotalQty($this->getPhysicalQuantity($quote))
->setSubtotal((float)$quote->getSubtotal())
->setBaseSubtotal((float)$quote->getBaseSubtotal());
$collection = $this->ruleCollectionFactory->create();
$collection->setValidationFilter(
(int)$quote->getStore()->getWebsiteId(),
(int)$quote->getCustomerGroupId(),
(string)$quote->getCouponCode(),
null,
$address
);
foreach ($collection as $rule) {
if ((int)$rule->getSimpleFreeShipping() > 0
&& $rule->getConditions()->validate($address)
) {
return true;
}
}
return false;
}
/**
* @param AbstractItem[] $items
*/
public function isFreeShippingForItems(array $items): bool
{
foreach ($items as $item) {
$quote = $item->getQuote();
if ($quote instanceof Quote) {
return $this->isFreeShipping($quote);
}
$addressQuote = $item->getAddress()?->getQuote();
if ($addressQuote instanceof Quote) {
return $this->isFreeShipping($addressQuote);
}
}
return false;
}
private function hasPhysicalItems(Quote $quote): bool
{
return $this->getPhysicalQuantity($quote) > 0;
}
private function getPhysicalQuantity(Quote $quote): float
{
$quantity = 0.0;
foreach ($quote->getAllVisibleItems() as $item) {
if ($item->getProduct()->isVirtual()) {
continue;
}
$quantity += (float)$item->getQty();
}
return $quantity;
}
private function getDescription(int $storeId, bool $freeShipping): string
{
if ($freeShipping) {
return (string)__('Free Shipping');
}
$title = (string)$this->scopeConfig->getValue(
self::XML_PATH_FLATRATE_TITLE,
ScopeInterface::SCOPE_STORE,
$storeId
);
$name = (string)$this->scopeConfig->getValue(
self::XML_PATH_FLATRATE_NAME,
ScopeInterface::SCOPE_STORE,
$storeId
);
return trim($title . ($title !== '' && $name !== '' ? ' - ' : '') . $name);
}
}