213 lines
8.6 KiB
PHP
213 lines
8.6 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace Esun\SeventeenTrack\Model;
|
|
|
|
use Magento\Framework\App\ResourceConnection;
|
|
use Magento\Sales\Model\Order\Shipment\Track;
|
|
|
|
class TrackingRepository
|
|
{
|
|
private const TABLE = 'esun_seventeen_track';
|
|
|
|
public function __construct(private readonly ResourceConnection $resource)
|
|
{
|
|
}
|
|
|
|
public function enqueue(Track $track): void
|
|
{
|
|
if (!$track->getId() || !$track->getTrackNumber() || !$track->getParentId() || !$track->getOrderId()) {
|
|
return;
|
|
}
|
|
$connection = $this->connection();
|
|
$existing = $connection->fetchRow($connection->select()->from($this->table())
|
|
->where('shipment_track_id = ?', (int)$track->getId())->limit(1));
|
|
$number = trim((string)$track->getTrackNumber());
|
|
$data = [
|
|
'shipment_track_id' => (int)$track->getId(),
|
|
'shipment_id' => (int)$track->getParentId(),
|
|
'order_id' => (int)$track->getOrderId(),
|
|
'tracking_number' => $number,
|
|
];
|
|
if ($existing && (string)$existing['tracking_number'] !== $number) {
|
|
$data += [
|
|
'carrier_code' => null,
|
|
'sync_status' => 'pending',
|
|
'error_code' => null,
|
|
'attempt_count' => 0,
|
|
'next_attempt_at' => null,
|
|
'package_status' => null,
|
|
'latest_event' => null,
|
|
'delivered_at' => null,
|
|
'last_error' => null,
|
|
'response_data' => null,
|
|
'last_synced_at' => null,
|
|
];
|
|
}
|
|
$connection->insertOnDuplicate($this->table(), $data, array_keys($data));
|
|
}
|
|
|
|
public function backfill(): void
|
|
{
|
|
$connection = $this->connection();
|
|
$track = $this->resource->getTableName('sales_shipment_track');
|
|
$select = $connection->select()->from(['track' => $track], [
|
|
'shipment_track_id' => 'entity_id',
|
|
'shipment_id' => 'parent_id',
|
|
'order_id' => 'order_id',
|
|
'tracking_number' => 'track_number',
|
|
])->where('track.track_number IS NOT NULL')->where('track.track_number != ?', '');
|
|
$connection->query($connection->insertFromSelect(
|
|
$select,
|
|
$this->table(),
|
|
['shipment_track_id', 'shipment_id', 'order_id', 'tracking_number'],
|
|
\Magento\Framework\DB\Adapter\AdapterInterface::INSERT_IGNORE
|
|
));
|
|
}
|
|
|
|
public function getPending(int $limit): array
|
|
{
|
|
return $this->connection()->fetchAll($this->connection()->select()->from($this->table())
|
|
->where('sync_status IN (?)', ['pending', 'error'])
|
|
->where('next_attempt_at IS NULL OR next_attempt_at <= UTC_TIMESTAMP()')
|
|
->order('updated_at ASC')->limit($limit));
|
|
}
|
|
|
|
public function getTracking(int $limit): array
|
|
{
|
|
return $this->connection()->fetchAll($this->connection()->select()->from($this->table())
|
|
->where('sync_status = ?', 'tracking')->where('package_status IS NULL OR package_status != ?', 'Delivered')
|
|
->where('next_attempt_at IS NULL OR next_attempt_at <= UTC_TIMESTAMP()')
|
|
->order('last_synced_at ASC')->limit($limit));
|
|
}
|
|
|
|
public function markRegistered(string $number, ?int $carrier): void
|
|
{
|
|
$this->connection()->update($this->table(), [
|
|
'sync_status' => 'tracking',
|
|
'carrier_code' => $carrier,
|
|
'error_code' => null,
|
|
'attempt_count' => 0,
|
|
'next_attempt_at' => null,
|
|
'last_error' => null,
|
|
], ['tracking_number = ?' => $number]);
|
|
}
|
|
|
|
public function markError(string $number, string $message, ?int $code, string $nextAttemptAt): void
|
|
{
|
|
$this->connection()->update($this->table(), [
|
|
'sync_status' => 'error',
|
|
'error_code' => $code,
|
|
'attempt_count' => new \Zend_Db_Expr('attempt_count + 1'),
|
|
'next_attempt_at' => $nextAttemptAt,
|
|
'last_error' => mb_substr($message, 0, 2000),
|
|
], ['tracking_number = ?' => $number]);
|
|
}
|
|
|
|
public function markQueryError(string $number, string $message, ?int $code, string $nextAttemptAt): void
|
|
{
|
|
$this->connection()->update($this->table(), [
|
|
'sync_status' => 'tracking',
|
|
'error_code' => $code,
|
|
'attempt_count' => new \Zend_Db_Expr('attempt_count + 1'),
|
|
'next_attempt_at' => $nextAttemptAt,
|
|
'last_error' => mb_substr($message, 0, 2000),
|
|
], ['tracking_number = ?' => $number]);
|
|
}
|
|
|
|
public function markPending(string $number, string $message, ?int $code, string $nextAttemptAt): void
|
|
{
|
|
$this->connection()->update($this->table(), [
|
|
'sync_status' => 'pending',
|
|
'carrier_code' => null,
|
|
'error_code' => $code,
|
|
'attempt_count' => new \Zend_Db_Expr('attempt_count + 1'),
|
|
'next_attempt_at' => $nextAttemptAt,
|
|
'last_error' => mb_substr($message, 0, 2000),
|
|
], ['tracking_number = ?' => $number]);
|
|
}
|
|
|
|
public function markRejected(string $number, string $message, ?int $code): void
|
|
{
|
|
$this->connection()->update($this->table(), [
|
|
'sync_status' => 'rejected',
|
|
'error_code' => $code,
|
|
'attempt_count' => new \Zend_Db_Expr('attempt_count + 1'),
|
|
'next_attempt_at' => null,
|
|
'last_error' => mb_substr($message, 0, 2000),
|
|
], ['tracking_number = ?' => $number]);
|
|
}
|
|
|
|
public function saveTrackingInfo(string $number, array $info, string $serialized): array
|
|
{
|
|
$trackInfo = (array)($info['track_info'] ?? []);
|
|
$latestStatus = (array)($trackInfo['latest_status'] ?? []);
|
|
$latestEvent = (array)($trackInfo['latest_event'] ?? []);
|
|
$status = (string)($latestStatus['status'] ?? $info['package_status'] ?? '');
|
|
$event = (string)($latestEvent['description'] ?? $latestEvent['event_description'] ?? $info['latest_event_info'] ?? '');
|
|
$deliveredAt = null;
|
|
if ($status === 'Delivered') {
|
|
$deliveredAt = $trackInfo['time_metrics']['delivery_time']
|
|
?? $trackInfo['time_metrics']['delievery_time']
|
|
?? $latestEvent['time_utc'] ?? gmdate('Y-m-d H:i:s');
|
|
}
|
|
$rows = $this->connection()->fetchAll($this->connection()->select()->from($this->table(), ['entity_id', 'order_id'])
|
|
->where('tracking_number = ?', $number));
|
|
$this->connection()->update($this->table(), [
|
|
'carrier_code' => isset($info['carrier']) ? (int)$info['carrier'] : null,
|
|
'sync_status' => 'tracking',
|
|
'error_code' => null,
|
|
'attempt_count' => 0,
|
|
'next_attempt_at' => null,
|
|
'package_status' => $status !== '' ? $status : null,
|
|
'latest_event' => $event !== '' ? mb_substr($event, 0, 1024) : null,
|
|
'delivered_at' => $deliveredAt ? $this->toMysqlDate((string)$deliveredAt) : null,
|
|
'response_data' => $serialized,
|
|
'last_error' => null,
|
|
'last_synced_at' => gmdate('Y-m-d H:i:s'),
|
|
], ['tracking_number = ?' => $number]);
|
|
return array_values(array_unique(array_map('intval', array_column($rows, 'order_id'))));
|
|
}
|
|
|
|
public function saveStoppedInfo(string $number, string $serialized): void
|
|
{
|
|
$this->connection()->update($this->table(), [
|
|
'sync_status' => 'stopped',
|
|
'error_code' => null,
|
|
'next_attempt_at' => null,
|
|
'response_data' => $serialized,
|
|
'last_error' => null,
|
|
'last_synced_at' => gmdate('Y-m-d H:i:s'),
|
|
], ['tracking_number = ?' => $number]);
|
|
}
|
|
|
|
public function isOrderDelivered(int $orderId): bool
|
|
{
|
|
$select = $this->connection()->select()->from($this->table(), [
|
|
'total' => new \Zend_Db_Expr('COUNT(*)'),
|
|
'delivered' => new \Zend_Db_Expr("SUM(package_status = 'Delivered')"),
|
|
])->where('order_id = ?', $orderId);
|
|
$row = $this->connection()->fetchRow($select) ?: [];
|
|
return (int)($row['total'] ?? 0) > 0 && (int)($row['total'] ?? 0) === (int)($row['delivered'] ?? 0);
|
|
}
|
|
|
|
private function toMysqlDate(string $value): string
|
|
{
|
|
try {
|
|
return (new \DateTimeImmutable($value))->setTimezone(new \DateTimeZone('UTC'))->format('Y-m-d H:i:s');
|
|
} catch (\Throwable) {
|
|
return gmdate('Y-m-d H:i:s');
|
|
}
|
|
}
|
|
|
|
private function connection(): \Magento\Framework\DB\Adapter\AdapterInterface
|
|
{
|
|
return $this->resource->getConnection('sales');
|
|
}
|
|
|
|
private function table(): string
|
|
{
|
|
return $this->resource->getTableName(self::TABLE);
|
|
}
|
|
}
|