When all payment methods for a transport are filtered out by min_order_amount/max_order_amount limits, the transport is now hidden from the basket. Prevents showing delivery options with empty payment method lists (e.g. "Kurier - płatność przy odbiorze" when COD exceeds max amount). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
506 lines
15 KiB
PHP
506 lines
15 KiB
PHP
<?php
|
|
namespace Domain\Transport;
|
|
|
|
class TransportRepository
|
|
{
|
|
private const MAX_PER_PAGE = 100;
|
|
|
|
private $db;
|
|
|
|
public function __construct($db)
|
|
{
|
|
$this->db = $db;
|
|
}
|
|
|
|
public function listForAdmin(
|
|
array $filters = [],
|
|
string $sortColumn = 'name',
|
|
string $sortDir = 'ASC',
|
|
int $page = 1,
|
|
int $perPage = 15
|
|
): array {
|
|
$allowedSortColumns = [
|
|
'id' => 'st.id',
|
|
'name' => 'st.name',
|
|
'status' => 'st.status',
|
|
'cost' => 'st.cost',
|
|
'max_wp' => 'st.max_wp',
|
|
'default' => 'st.default',
|
|
'o' => 'st.o',
|
|
];
|
|
|
|
$sortSql = $allowedSortColumns[$sortColumn] ?? 'st.name';
|
|
$sortDir = strtoupper(trim($sortDir)) === 'DESC' ? 'DESC' : 'ASC';
|
|
$page = max(1, $page);
|
|
$perPage = min(self::MAX_PER_PAGE, max(1, $perPage));
|
|
$offset = ($page - 1) * $perPage;
|
|
|
|
$where = ['1 = 1'];
|
|
$params = [];
|
|
|
|
$name = trim((string)($filters['name'] ?? ''));
|
|
if ($name !== '') {
|
|
if (strlen($name) > 255) {
|
|
$name = substr($name, 0, 255);
|
|
}
|
|
$where[] = 'st.name LIKE :name';
|
|
$params[':name'] = '%' . $name . '%';
|
|
}
|
|
|
|
$status = trim((string)($filters['status'] ?? ''));
|
|
if ($status === '0' || $status === '1') {
|
|
$where[] = 'st.status = :status';
|
|
$params[':status'] = (int)$status;
|
|
}
|
|
|
|
$whereSql = implode(' AND ', $where);
|
|
|
|
$sqlCount = "
|
|
SELECT COUNT(0)
|
|
FROM pp_shop_transports AS st
|
|
WHERE {$whereSql}
|
|
";
|
|
|
|
$stmtCount = $this->db->query($sqlCount, $params);
|
|
$countRows = $stmtCount ? $stmtCount->fetchAll() : [];
|
|
$total = isset($countRows[0][0]) ? (int)$countRows[0][0] : 0;
|
|
|
|
$sql = "
|
|
SELECT
|
|
st.id,
|
|
st.name,
|
|
st.name_visible,
|
|
st.description,
|
|
st.status,
|
|
st.cost,
|
|
st.max_wp,
|
|
st.default,
|
|
st.apilo_carrier_account_id,
|
|
st.delivery_free,
|
|
st.o
|
|
FROM pp_shop_transports AS st
|
|
WHERE {$whereSql}
|
|
ORDER BY {$sortSql} {$sortDir}, st.id ASC
|
|
LIMIT {$perPage} OFFSET {$offset}
|
|
";
|
|
|
|
$stmt = $this->db->query($sql, $params);
|
|
$items = $stmt ? $stmt->fetchAll() : [];
|
|
|
|
if (!is_array($items)) {
|
|
$items = [];
|
|
}
|
|
|
|
foreach ($items as &$item) {
|
|
$item = $this->normalizeTransport($item);
|
|
}
|
|
unset($item);
|
|
|
|
return [
|
|
'items' => $items,
|
|
'total' => $total,
|
|
];
|
|
}
|
|
|
|
public function find(int $transportId): ?array
|
|
{
|
|
if ($transportId <= 0) {
|
|
return null;
|
|
}
|
|
|
|
$transport = $this->db->get('pp_shop_transports', '*', ['id' => $transportId]);
|
|
|
|
if (!is_array($transport)) {
|
|
return null;
|
|
}
|
|
|
|
$transport = $this->normalizeTransport($transport);
|
|
|
|
$paymentMethods = $this->db->select(
|
|
'pp_shop_transport_payment_methods',
|
|
'id_payment_method',
|
|
['id_transport' => $transportId]
|
|
);
|
|
|
|
$transport['payment_methods'] = is_array($paymentMethods) ? $paymentMethods : [];
|
|
|
|
return $transport;
|
|
}
|
|
|
|
public function save(array $data): ?int
|
|
{
|
|
$transportId = isset($data['id']) ? (int)$data['id'] : 0;
|
|
$name = trim((string)($data['name'] ?? ''));
|
|
$nameVisible = trim((string)($data['name_visible'] ?? ''));
|
|
$description = trim((string)($data['description'] ?? ''));
|
|
$status = $this->toSwitchValue($data['status'] ?? 0);
|
|
$cost = isset($data['cost']) ? (float)$data['cost'] : 0.0;
|
|
$maxWp = isset($data['max_wp']) && $data['max_wp'] !== '' ? (int)$data['max_wp'] : null;
|
|
$default = $this->toSwitchValue($data['default'] ?? 0);
|
|
$apiloCarrierAccountId = isset($data['apilo_carrier_account_id']) && $data['apilo_carrier_account_id'] !== ''
|
|
? (int)$data['apilo_carrier_account_id']
|
|
: null;
|
|
$deliveryFree = $this->toSwitchValue($data['delivery_free'] ?? 0);
|
|
$paymentMethods = $data['payment_methods'] ?? [];
|
|
|
|
if ($default === 1) {
|
|
$this->db->update('pp_shop_transports', ['default' => 0]);
|
|
}
|
|
|
|
$transportData = [
|
|
'name' => $name,
|
|
'name_visible' => $nameVisible,
|
|
'description' => $description,
|
|
'status' => $status,
|
|
'default' => $default,
|
|
'cost' => $cost,
|
|
'max_wp' => $maxWp,
|
|
'apilo_carrier_account_id' => $apiloCarrierAccountId,
|
|
'delivery_free' => $deliveryFree,
|
|
];
|
|
|
|
if (!$transportId) {
|
|
$this->db->insert('pp_shop_transports', $transportData);
|
|
$id = $this->db->id();
|
|
|
|
if ($id) {
|
|
$this->savePaymentMethodLinks((int)$id, $paymentMethods);
|
|
return (int)$id;
|
|
}
|
|
|
|
return null;
|
|
} else {
|
|
$this->db->update('pp_shop_transports', $transportData, ['id' => $transportId]);
|
|
$this->db->delete('pp_shop_transport_payment_methods', ['id_transport' => $transportId]);
|
|
$this->savePaymentMethodLinks($transportId, $paymentMethods);
|
|
return $transportId;
|
|
}
|
|
}
|
|
|
|
public function allActive(): array
|
|
{
|
|
$transports = $this->db->select(
|
|
'pp_shop_transports',
|
|
'*',
|
|
[
|
|
'status' => 1,
|
|
'ORDER' => ['o' => 'ASC'],
|
|
]
|
|
);
|
|
|
|
if (!is_array($transports)) {
|
|
return [];
|
|
}
|
|
|
|
foreach ($transports as &$transport) {
|
|
$transport = $this->normalizeTransport($transport);
|
|
}
|
|
unset($transport);
|
|
|
|
return $transports;
|
|
}
|
|
|
|
public function findActiveById(int $transportId): ?array
|
|
{
|
|
if ($transportId <= 0) {
|
|
return null;
|
|
}
|
|
|
|
$transport = $this->db->get(
|
|
'pp_shop_transports',
|
|
'*',
|
|
['AND' => ['id' => $transportId, 'status' => 1]]
|
|
);
|
|
|
|
if (!$transport) {
|
|
return null;
|
|
}
|
|
|
|
return $this->normalizeTransport($transport);
|
|
}
|
|
|
|
public function getApiloCarrierAccountId(int $transportId): ?int
|
|
{
|
|
if ($transportId <= 0) {
|
|
return null;
|
|
}
|
|
|
|
$result = $this->db->get(
|
|
'pp_shop_transports',
|
|
'apilo_carrier_account_id',
|
|
['id' => $transportId]
|
|
);
|
|
|
|
return $result !== null ? (int)$result : null;
|
|
}
|
|
|
|
public function getTransportCost(int $transportId): ?float
|
|
{
|
|
if ($transportId <= 0) {
|
|
return null;
|
|
}
|
|
|
|
$result = $this->db->get(
|
|
'pp_shop_transports',
|
|
'cost',
|
|
['AND' => ['id' => $transportId, 'status' => 1]]
|
|
);
|
|
|
|
return $result !== null ? (float)$result : null;
|
|
}
|
|
|
|
public function lowestTransportPrice(int $wp): ?float
|
|
{
|
|
$result = $this->db->get(
|
|
'pp_shop_transports',
|
|
'cost',
|
|
[
|
|
'AND' => [
|
|
'status' => 1,
|
|
'id' => [2, 4, 6, 8, 9],
|
|
'max_wp[>=]' => $wp
|
|
],
|
|
'ORDER' => ['cost' => 'ASC']
|
|
]
|
|
);
|
|
|
|
return $result !== null ? (float)$result : null;
|
|
}
|
|
|
|
public function allForAdmin(): array
|
|
{
|
|
$transports = $this->db->select(
|
|
'pp_shop_transports',
|
|
'*',
|
|
['ORDER' => ['o' => 'ASC']]
|
|
);
|
|
|
|
if (!is_array($transports)) {
|
|
return [];
|
|
}
|
|
|
|
foreach ($transports as &$transport) {
|
|
$transport = $this->normalizeTransport($transport);
|
|
}
|
|
unset($transport);
|
|
|
|
return $transports;
|
|
}
|
|
|
|
// =========================================================================
|
|
// Frontend methods (migrated from front\factory\ShopTransport)
|
|
// =========================================================================
|
|
|
|
/**
|
|
* Lista metod transportu dla koszyka (z filtrowaniem wagi + darmowa dostawa)
|
|
*/
|
|
public function transportMethodsFront( $basket, $coupon ): array
|
|
{
|
|
global $settings;
|
|
|
|
$cacheHandler = new \Shared\Cache\CacheHandler();
|
|
$cacheKey = 'transport_methods_front';
|
|
$cached = $cacheHandler->get( $cacheKey );
|
|
|
|
if ( $cached )
|
|
{
|
|
$transports_tmp = unserialize( $cached );
|
|
}
|
|
else
|
|
{
|
|
$transports_tmp = $this->allActive();
|
|
$cacheHandler->set( $cacheKey, $transports_tmp );
|
|
}
|
|
|
|
$wp_summary = \Domain\Basket\BasketCalculator::summaryWp( $basket );
|
|
|
|
$transports = [];
|
|
foreach ( $transports_tmp as $tr )
|
|
{
|
|
if ( $tr['max_wp'] == null )
|
|
$transports[] = $tr;
|
|
elseif ( $tr['max_wp'] != null and $wp_summary <= $tr['max_wp'] )
|
|
$transports[] = $tr;
|
|
}
|
|
|
|
$products_summary = (float)\Domain\Basket\BasketCalculator::summaryPrice( $basket, $coupon );
|
|
|
|
if ( \Shared\Helpers\Helpers::normalize_decimal( $products_summary ) >= \Shared\Helpers\Helpers::normalize_decimal( $settings['free_delivery'] ) )
|
|
{
|
|
for ( $i = 0; $i < count( $transports ); $i++ ) {
|
|
if ( $transports[$i]['delivery_free'] == 1 ) {
|
|
$transports[$i]['cost'] = 0.00;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Ukryj transporty, dla których nie ma żadnej dostępnej formy płatności
|
|
$paymentMethodRepo = new \Domain\PaymentMethod\PaymentMethodRepository( $this->db );
|
|
$filtered = [];
|
|
|
|
foreach ( $transports as $tr )
|
|
{
|
|
$paymentMethods = $paymentMethodRepo->paymentMethodsByTransport( $tr['id'] );
|
|
$order_total = $products_summary + (float)$tr['cost'];
|
|
$has_available_pm = false;
|
|
|
|
foreach ( $paymentMethods as $pm )
|
|
{
|
|
$min = isset( $pm['min_order_amount'] ) ? (float)$pm['min_order_amount'] : null;
|
|
$max = isset( $pm['max_order_amount'] ) ? (float)$pm['max_order_amount'] : null;
|
|
$available = true;
|
|
|
|
if ( $min !== null && $min > 0 && $order_total < $min ) $available = false;
|
|
if ( $max !== null && $max > 0 && $order_total > $max ) $available = false;
|
|
|
|
if ( $available )
|
|
{
|
|
$has_available_pm = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( $has_available_pm )
|
|
{
|
|
$filtered[] = $tr;
|
|
}
|
|
}
|
|
|
|
return $filtered;
|
|
}
|
|
|
|
/**
|
|
* Koszt transportu z cache
|
|
*/
|
|
public function transportCostCached( $transportId )
|
|
{
|
|
$cacheHandler = new \Shared\Cache\CacheHandler();
|
|
$cacheKey = 'transport_cost_' . $transportId;
|
|
$cached = $cacheHandler->get( $cacheKey );
|
|
|
|
if ( $cached )
|
|
{
|
|
return unserialize( $cached );
|
|
}
|
|
|
|
$cost = $this->getTransportCost( (int)$transportId );
|
|
$cacheHandler->set( $cacheKey, $cost );
|
|
|
|
return $cost;
|
|
}
|
|
|
|
/**
|
|
* Aktywny transport z cache
|
|
*/
|
|
public function findActiveByIdCached( $transportId )
|
|
{
|
|
$cacheHandler = new \Shared\Cache\CacheHandler();
|
|
$cacheKey = 'transport' . $transportId;
|
|
$cached = $cacheHandler->get( $cacheKey );
|
|
|
|
if ( $cached )
|
|
{
|
|
return unserialize( $cached );
|
|
}
|
|
|
|
$transport = $this->findActiveById( (int)$transportId );
|
|
$cacheHandler->set( $cacheKey, $transport );
|
|
|
|
return $transport;
|
|
}
|
|
|
|
/**
|
|
* Transporty powiązane z metodą płatności
|
|
*/
|
|
public function forPaymentMethod( int $paymentMethodId ): array
|
|
{
|
|
if ( $paymentMethodId <= 0 )
|
|
{
|
|
return [];
|
|
}
|
|
|
|
$transportIds = $this->db->select(
|
|
'pp_shop_transport_payment_methods',
|
|
'id_transport',
|
|
['id_payment_method' => $paymentMethodId]
|
|
);
|
|
|
|
if ( !is_array( $transportIds ) || empty( $transportIds ) )
|
|
{
|
|
return [];
|
|
}
|
|
|
|
$transports = $this->db->select(
|
|
'pp_shop_transports',
|
|
'*',
|
|
['AND' => ['id' => $transportIds, 'status' => 1], 'ORDER' => ['o' => 'ASC']]
|
|
);
|
|
|
|
if ( !is_array( $transports ) )
|
|
{
|
|
return [];
|
|
}
|
|
|
|
foreach ( $transports as &$transport )
|
|
{
|
|
$transport = $this->normalizeTransport( $transport );
|
|
}
|
|
unset( $transport );
|
|
|
|
return $transports;
|
|
}
|
|
|
|
private function savePaymentMethodLinks(int $transportId, $paymentMethods): void
|
|
{
|
|
if (is_array($paymentMethods)) {
|
|
foreach ($paymentMethods as $paymentMethodId) {
|
|
$this->db->insert('pp_shop_transport_payment_methods', [
|
|
'id_payment_method' => (int)$paymentMethodId,
|
|
'id_transport' => $transportId,
|
|
]);
|
|
}
|
|
} elseif ($paymentMethods) {
|
|
$this->db->insert('pp_shop_transport_payment_methods', [
|
|
'id_payment_method' => (int)$paymentMethods,
|
|
'id_transport' => $transportId,
|
|
]);
|
|
}
|
|
}
|
|
|
|
private function normalizeTransport(array $transport): array
|
|
{
|
|
$transport['id'] = isset($transport['id']) ? (int)$transport['id'] : 0;
|
|
$transport['status'] = $this->toSwitchValue($transport['status'] ?? 0);
|
|
$transport['default'] = $this->toSwitchValue($transport['default'] ?? 0);
|
|
$transport['delivery_free'] = $this->toSwitchValue($transport['delivery_free'] ?? 0);
|
|
$transport['cost'] = isset($transport['cost']) ? (float)$transport['cost'] : 0.0;
|
|
$transport['max_wp'] = isset($transport['max_wp']) && $transport['max_wp'] !== null
|
|
? (int)$transport['max_wp']
|
|
: null;
|
|
$transport['apilo_carrier_account_id'] = isset($transport['apilo_carrier_account_id']) && $transport['apilo_carrier_account_id'] !== null
|
|
? (int)$transport['apilo_carrier_account_id']
|
|
: null;
|
|
$transport['o'] = isset($transport['o']) ? (int)$transport['o'] : 0;
|
|
|
|
return $transport;
|
|
}
|
|
|
|
private function toSwitchValue($value): int
|
|
{
|
|
if (is_bool($value)) {
|
|
return $value ? 1 : 0;
|
|
}
|
|
|
|
if (is_numeric($value)) {
|
|
return ((int)$value) === 1 ? 1 : 0;
|
|
}
|
|
|
|
if (is_string($value)) {
|
|
$normalized = strtolower(trim($value));
|
|
return in_array($normalized, ['1', 'on', 'true', 'yes'], true) ? 1 : 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
}
|