update
This commit is contained in:
900
.vscode/ftp-kr.diff.OrdersController.php
vendored
Normal file
900
.vscode/ftp-kr.diff.OrdersController.php
vendored
Normal file
@@ -0,0 +1,900 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Modules\Orders;
|
||||
|
||||
use App\Core\Http\Request;
|
||||
use App\Core\Http\Response;
|
||||
use App\Core\I18n\Translator;
|
||||
use App\Core\Security\Csrf;
|
||||
use App\Core\View\Template;
|
||||
use App\Core\Support\Flash;
|
||||
use App\Core\Support\StringHelper;
|
||||
use App\Modules\Accounting\ReceiptRepository;
|
||||
use App\Modules\Auth\AuthService;
|
||||
use App\Modules\Email\EmailSendingService;
|
||||
use App\Modules\Settings\EmailMailboxRepository;
|
||||
use App\Modules\Settings\EmailTemplateRepository;
|
||||
use App\Modules\Settings\ReceiptConfigRepository;
|
||||
use App\Modules\Automation\AutomationService;
|
||||
use App\Modules\Settings\ShopproApiClient;
|
||||
use App\Modules\Settings\ShopproIntegrationsRepository;
|
||||
use App\Modules\Shipments\ShipmentPackageRepository;
|
||||
|
||||
final class OrdersController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly Template $template,
|
||||
private readonly Translator $translator,
|
||||
private readonly AuthService $auth,
|
||||
private readonly OrdersRepository $orders,
|
||||
private readonly ?ShipmentPackageRepository $shipmentPackages = null,
|
||||
private readonly ?ReceiptRepository $receiptRepo = null,
|
||||
private readonly ?ReceiptConfigRepository $receiptConfigRepo = null,
|
||||
private readonly ?EmailSendingService $emailService = null,
|
||||
private readonly ?EmailTemplateRepository $emailTemplateRepo = null,
|
||||
private readonly ?EmailMailboxRepository $emailMailboxRepo = null,
|
||||
private readonly string $storagePath = '',
|
||||
private readonly ?\App\Modules\Printing\PrintJobRepository $printJobRepo = null,
|
||||
private readonly ?ShopproIntegrationsRepository $shopproIntegrations = null,
|
||||
private readonly ?AutomationService $automation = null
|
||||
) {
|
||||
}
|
||||
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
$filters = [
|
||||
'search' => trim((string) $request->input('search', '')),
|
||||
'source' => trim((string) $request->input('source', '')),
|
||||
'status' => trim((string) $request->input('status', '')),
|
||||
'payment_status' => trim((string) $request->input('payment_status', '')),
|
||||
'date_from' => trim((string) $request->input('date_from', '')),
|
||||
'date_to' => trim((string) $request->input('date_to', '')),
|
||||
'sort' => (string) $request->input('sort', 'ordered_at'),
|
||||
'sort_dir' => (string) $request->input('sort_dir', 'DESC'),
|
||||
'page' => max(1, (int) $request->input('page', 1)),
|
||||
'per_page' => max(1, min(100, (int) $request->input('per_page', 20)),
|
||||
),
|
||||
];
|
||||
|
||||
$result = $this->orders->paginate($filters);
|
||||
$totalPages = max(1, (int) ceil(((int) $result['total']) / max(1, (int) $result['per_page'])));
|
||||
$sourceOptions = $this->orders->sourceOptions();
|
||||
$stats = $this->orders->quickStats();
|
||||
$statusCounts = $this->orders->statusCounts();
|
||||
$statusConfig = $this->orders->statusPanelConfig();
|
||||
$statusLabelMap = $this->statusLabelMap($statusConfig);
|
||||
$statusColorMap = $this->statusColorMap($statusConfig);
|
||||
$statusOptions = $this->buildStatusFilterOptions($this->orders->statusOptions(), $statusLabelMap);
|
||||
$statusPanel = $this->buildStatusPanel($statusConfig, $statusCounts, $filters['status'], $filters);
|
||||
|
||||
$tableRows = array_map(fn (array $row): array => $this->toTableRow($row, $statusLabelMap, $statusColorMap), (array) ($result['items'] ?? []));
|
||||
|
||||
$html = $this->template->render('orders/list', [
|
||||
'title' => $this->translator->get('orders.title'),
|
||||
'activeMenu' => 'orders',
|
||||
'activeOrders' => 'list',
|
||||
'user' => $this->auth->user(),
|
||||
'csrfToken' => Csrf::token(),
|
||||
'tableList' => [
|
||||
'list_key' => 'orders',
|
||||
'base_path' => '/orders/list',
|
||||
'query' => $filters,
|
||||
'filters' => [
|
||||
[
|
||||
'key' => 'search',
|
||||
'label' => $this->translator->get('orders.filters.search'),
|
||||
'type' => 'text',
|
||||
'value' => $filters['search'],
|
||||
],
|
||||
[
|
||||
'key' => 'source',
|
||||
'label' => $this->translator->get('orders.filters.source'),
|
||||
'type' => 'select',
|
||||
'value' => $filters['source'],
|
||||
'options' => ['' => $this->translator->get('orders.filters.any')] + $sourceOptions,
|
||||
],
|
||||
[
|
||||
'key' => 'status',
|
||||
'label' => $this->translator->get('orders.filters.status'),
|
||||
'type' => 'select',
|
||||
'value' => $filters['status'],
|
||||
'options' => ['' => $this->translator->get('orders.filters.any')] + $statusOptions,
|
||||
],
|
||||
[
|
||||
'key' => 'payment_status',
|
||||
'label' => $this->translator->get('orders.filters.payment_status'),
|
||||
'type' => 'select',
|
||||
'value' => $filters['payment_status'],
|
||||
'options' => $this->paymentStatusFilterOptions(),
|
||||
],
|
||||
[
|
||||
'key' => 'date_from',
|
||||
'label' => $this->translator->get('orders.filters.date_from'),
|
||||
'type' => 'date',
|
||||
'value' => $filters['date_from'],
|
||||
],
|
||||
[
|
||||
'key' => 'date_to',
|
||||
'label' => $this->translator->get('orders.filters.date_to'),
|
||||
'type' => 'date',
|
||||
'value' => $filters['date_to'],
|
||||
],
|
||||
],
|
||||
'columns' => [
|
||||
['key' => 'order_ref', 'label' => $this->translator->get('orders.fields.order_ref'), 'sortable' => true, 'sort_key' => 'source_order_id', 'raw' => true],
|
||||
['key' => 'buyer', 'label' => $this->translator->get('orders.fields.buyer'), 'raw' => true],
|
||||
['key' => 'status_badges', 'label' => $this->translator->get('orders.fields.status'), 'sortable' => true, 'sort_key' => 'external_status_id', 'raw' => true],
|
||||
['key' => 'products', 'label' => $this->translator->get('orders.fields.products'), 'raw' => true],
|
||||
['key' => 'totals', 'label' => $this->translator->get('orders.fields.totals'), 'sortable' => true, 'sort_key' => 'total_with_tax', 'raw' => true],
|
||||
['key' => 'shipping', 'label' => $this->translator->get('orders.fields.shipping'), 'raw' => true],
|
||||
['key' => 'ordered_at', 'label' => $this->translator->get('orders.fields.ordered_at'), 'sortable' => true, 'sort_key' => 'ordered_at'],
|
||||
],
|
||||
'rows' => $tableRows,
|
||||
'pagination' => [
|
||||
'page' => (int) ($result['page'] ?? 1),
|
||||
'total_pages' => $totalPages,
|
||||
'total' => (int) ($result['total'] ?? 0),
|
||||
'per_page' => (int) ($result['per_page'] ?? 20),
|
||||
],
|
||||
'per_page_options' => [20, 50, 100],
|
||||
'selectable' => true,
|
||||
'select_name' => 'selected_ids[]',
|
||||
'select_value_key' => 'id',
|
||||
'header_actions' => [],
|
||||
'empty_message' => $this->translator->get('orders.empty'),
|
||||
'show_actions' => false,
|
||||
],
|
||||
'stats' => $stats,
|
||||
'statusPanel' => $statusPanel,
|
||||
'allStatuses' => $this->buildAllStatusOptions($statusConfig),
|
||||
'statusColorMap' => $statusColorMap,
|
||||
'errorMessage' => (string) ($result['error'] ?? ''),
|
||||
], 'layouts/app');
|
||||
|
||||
return Response::html($html);
|
||||
}
|
||||
|
||||
public function show(Request $request): Response
|
||||
{
|
||||
$orderId = max(0, (int) $request->input('id', 0));
|
||||
$details = $this->orders->findDetails($orderId);
|
||||
if ($details === null) {
|
||||
return Response::html('Not found', 404);
|
||||
}
|
||||
|
||||
$order = is_array($details['order'] ?? null) ? $details['order'] : [];
|
||||
$items = is_array($details['items'] ?? null) ? $details['items'] : [];
|
||||
$addresses = is_array($details['addresses'] ?? null) ? $details['addresses'] : [];
|
||||
$payments = is_array($details['payments'] ?? null) ? $details['payments'] : [];
|
||||
$shipments = is_array($details['shipments'] ?? null) ? $details['shipments'] : [];
|
||||
$documents = is_array($details['documents'] ?? null) ? $details['documents'] : [];
|
||||
$notes = is_array($details['notes'] ?? null) ? $details['notes'] : [];
|
||||
$history = is_array($details['status_history'] ?? null) ? $details['status_history'] : [];
|
||||
$activityLog = is_array($details['activity_log'] ?? null) ? $details['activity_log'] : [];
|
||||
$statusCode = (string) (($order['effective_status_id'] ?? '') !== '' ? $order['effective_status_id'] : ($order['external_status_id'] ?? ''));
|
||||
$statusCounts = $this->orders->statusCounts();
|
||||
$statusConfig = $this->orders->statusPanelConfig();
|
||||
$statusLabelMap = $this->statusLabelMap($statusConfig);
|
||||
|
||||
$resolvedHistory = $this->resolveHistoryLabels($history, $statusLabelMap);
|
||||
|
||||
$allStatuses = $this->buildAllStatusOptions($statusConfig);
|
||||
|
||||
$packages = $this->shipmentPackages !== null
|
||||
? $this->shipmentPackages->findByOrderId($orderId)
|
||||
: [];
|
||||
|
||||
if ($this->storagePath !== '') {
|
||||
foreach ($packages as &$pkg) {
|
||||
$lp = trim((string) ($pkg['label_path'] ?? ''));
|
||||
if ($lp !== '' && !file_exists($this->storagePath . '/' . $lp)) {
|
||||
$pkg['label_path'] = '';
|
||||
}
|
||||
}
|
||||
unset($pkg);
|
||||
}
|
||||
|
||||
$receipts = $this->receiptRepo !== null
|
||||
? $this->receiptRepo->findByOrderId($orderId)
|
||||
: [];
|
||||
|
||||
$activeReceiptConfigs = [];
|
||||
if ($this->receiptConfigRepo !== null) {
|
||||
$activeReceiptConfigs = array_filter(
|
||||
$this->receiptConfigRepo->listAll(),
|
||||
static fn(array $c): bool => (int) ($c['is_active'] ?? 0) === 1
|
||||
);
|
||||
}
|
||||
|
||||
$emailTemplates = $this->emailTemplateRepo !== null ? $this->emailTemplateRepo->listActive() : [];
|
||||
$emailMailboxes = $this->emailMailboxRepo !== null ? $this->emailMailboxRepo->listActive() : [];
|
||||
|
||||
$flashSuccess = (string) Flash::get('order.success', '');
|
||||
$flashError = (string) Flash::get('order.error', '');
|
||||
|
||||
$html = $this->template->render('orders/show', [
|
||||
'title' => $this->translator->get('orders.details.title') . ' #' . $orderId,
|
||||
'activeMenu' => 'orders',
|
||||
'activeOrders' => 'list',
|
||||
'user' => $this->auth->user(),
|
||||
'csrfToken' => Csrf::token(),
|
||||
'orderId' => $orderId,
|
||||
'order' => $order,
|
||||
'items' => $items,
|
||||
'addresses' => $addresses,
|
||||
'payments' => $payments,
|
||||
'shipments' => $shipments,
|
||||
'packages' => $packages,
|
||||
'pendingPrintPackageIds' => $this->printJobRepo !== null ? $this->printJobRepo->pendingPackageIds() : [],
|
||||
'documents' => $documents,
|
||||
'notes' => $notes,
|
||||
'history' => $resolvedHistory,
|
||||
'activityLog' => $activityLog,
|
||||
'statusLabel' => $this->statusLabel($statusCode, $statusLabelMap),
|
||||
'statusPanel' => $this->buildStatusPanel($statusConfig, $statusCounts, $statusCode),
|
||||
'allStatuses' => $allStatuses,
|
||||
'currentStatusCode' => $statusCode,
|
||||
'flashSuccess' => $flashSuccess,
|
||||
'flashError' => $flashError,
|
||||
'receipts' => $receipts,
|
||||
'receiptConfigs' => $activeReceiptConfigs,
|
||||
'emailTemplates' => $emailTemplates,
|
||||
'emailMailboxes' => $emailMailboxes,
|
||||
], 'layouts/app');
|
||||
|
||||
return Response::html($html);
|
||||
}
|
||||
|
||||
public function updateStatus(Request $request): Response
|
||||
{
|
||||
$isAjax = strtolower($request->header('X-Requested-With')) === 'xmlhttprequest';
|
||||
$orderId = max(0, (int) $request->input('id', 0));
|
||||
if ($orderId <= 0) {
|
||||
return $isAjax
|
||||
? Response::json(['success' => false, 'error' => 'Not found'], 404)
|
||||
: Response::html('Not found', 404);
|
||||
}
|
||||
|
||||
$csrfToken = (string) $request->input('_token', '');
|
||||
if (!Csrf::validate($csrfToken)) {
|
||||
if ($isAjax) {
|
||||
return Response::json(['success' => false, 'error' => $this->translator->get('auth.errors.csrf_expired')], 403);
|
||||
}
|
||||
Flash::set('order.error', $this->translator->get('auth.errors.csrf_expired'));
|
||||
return Response::redirect('/orders/' . $orderId);
|
||||
}
|
||||
|
||||
$newStatus = trim((string) $request->input('new_status', ''));
|
||||
if ($newStatus === '') {
|
||||
if ($isAjax) {
|
||||
return Response::json(['success' => false, 'error' => $this->translator->get('orders.details.status_change.status_required')], 422);
|
||||
}
|
||||
Flash::set('order.error', $this->translator->get('orders.details.status_change.status_required'));
|
||||
return Response::redirect('/orders/' . $orderId);
|
||||
}
|
||||
|
||||
$user = $this->auth->user();
|
||||
$actorName = is_array($user) ? trim((string) ($user['name'] ?? $user['email'] ?? '')) : null;
|
||||
|
||||
$success = $this->orders->updateOrderStatus($orderId, $newStatus, 'user', $actorName !== '' ? $actorName : null);
|
||||
|
||||
if ($isAjax) {
|
||||
if (!$success) {
|
||||
return Response::json(['success' => false, 'error' => $this->translator->get('orders.details.status_change.failed')], 500);
|
||||
}
|
||||
$statusConfig = $this->orders->statusPanelConfig();
|
||||
$statusLabelMap = $this->statusLabelMap($statusConfig);
|
||||
$statusColorMap = $this->statusColorMap($statusConfig);
|
||||
$normalizedCode = strtolower(trim($newStatus));
|
||||
return Response::json([
|
||||
'success' => true,
|
||||
'status_code' => $normalizedCode,
|
||||
'status_label' => $this->statusLabel($normalizedCode, $statusLabelMap),
|
||||
'status_color' => $statusColorMap[$normalizedCode] ?? '',
|
||||
]);
|
||||
}
|
||||
|
||||
if ($success) {
|
||||
Flash::set('order.success', $this->translator->get('orders.details.status_change.success'));
|
||||
} else {
|
||||
Flash::set('order.error', $this->translator->get('orders.details.status_change.failed'));
|
||||
}
|
||||
|
||||
return Response::redirect('/orders/' . $orderId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $row
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function toTableRow(array $row, array $statusLabelMap, array $statusColorMap = []): array
|
||||
{
|
||||
$internalOrderNumber = trim((string) ($row['internal_order_number'] ?? ''));
|
||||
$sourceOrderId = trim((string) ($row['source_order_id'] ?? ''));
|
||||
$externalOrderId = trim((string) ($row['external_order_id'] ?? ''));
|
||||
$source = trim((string) ($row['source'] ?? ''));
|
||||
$integrationName = trim((string) ($row['integration_name'] ?? ''));
|
||||
$buyerName = trim((string) ($row['buyer_name'] ?? ''));
|
||||
$buyerEmail = trim((string) ($row['buyer_email'] ?? ''));
|
||||
$buyerCity = trim((string) ($row['buyer_city'] ?? ''));
|
||||
$status = trim((string) (($row['effective_status_id'] ?? '') !== '' ? $row['effective_status_id'] : ($row['external_status_id'] ?? '')));
|
||||
$currency = trim((string) ($row['currency'] ?? ''));
|
||||
$totalWithTax = $row['total_with_tax'] !== null ? number_format((float) $row['total_with_tax'], 2, '.', ' ') : '-';
|
||||
$totalPaid = $row['total_paid'] !== null ? number_format((float) $row['total_paid'], 2, '.', ' ') : '-';
|
||||
$paymentType = strtoupper(trim((string) ($row['external_payment_type_id'] ?? '')));
|
||||
$isCod = $paymentType === 'CASH_ON_DELIVERY';
|
||||
$paymentStatus = isset($row['payment_status']) ? (int) $row['payment_status'] : null;
|
||||
$isUnpaid = !$isCod && $paymentStatus === 0;
|
||||
$itemsCount = max(0, (int) ($row['items_count'] ?? 0));
|
||||
$itemsQty = $this->formatQuantity((float) ($row['items_qty'] ?? 0));
|
||||
$shipments = max(0, (int) ($row['shipments_count'] ?? 0));
|
||||
$documents = max(0, (int) ($row['documents_count'] ?? 0));
|
||||
$itemsPreview = is_array($row['items_preview'] ?? null) ? $row['items_preview'] : [];
|
||||
|
||||
return [
|
||||
'id' => (int) ($row['id'] ?? 0),
|
||||
'order_ref' => '<div class="orders-ref">'
|
||||
. '<div class="orders-ref__main"><a href="/orders/' . (int) ($row['id'] ?? 0) . '">'
|
||||
. htmlspecialchars($internalOrderNumber !== '' ? $internalOrderNumber : ('#' . (string) ($row['id'] ?? 0)), ENT_QUOTES, 'UTF-8')
|
||||
. '</a></div>'
|
||||
. '<div class="orders-ref__meta">'
|
||||
. '<span>' . htmlspecialchars($integrationName !== '' ? $integrationName : $this->sourceLabel($source), ENT_QUOTES, 'UTF-8') . '</span>'
|
||||
. '<span>ID: ' . htmlspecialchars($sourceOrderId !== '' ? $sourceOrderId : $externalOrderId, ENT_QUOTES, 'UTF-8') . '</span>'
|
||||
. '</div>'
|
||||
. '</div>',
|
||||
'buyer' => '<div class="orders-buyer">'
|
||||
. '<div class="orders-buyer__name">' . htmlspecialchars($buyerName !== '' ? $buyerName : '-', ENT_QUOTES, 'UTF-8') . '</div>'
|
||||
. '<div class="orders-buyer__meta">'
|
||||
. '<span>' . htmlspecialchars($buyerEmail, ENT_QUOTES, 'UTF-8') . '</span>'
|
||||
. '<span>' . htmlspecialchars($buyerCity, ENT_QUOTES, 'UTF-8') . '</span>'
|
||||
. '</div>'
|
||||
. '</div>',
|
||||
'status_badges' => '<div class="orders-status-wrap" data-order-id="' . (int) ($row['id'] ?? 0) . '" data-current-status="' . htmlspecialchars($status, ENT_QUOTES, 'UTF-8') . '">'
|
||||
. $this->statusBadge($status, $this->statusLabel($status, $statusLabelMap), $statusColorMap[strtolower(trim($status))] ?? '')
|
||||
. '</div>',
|
||||
'products' => $this->productsHtml($itemsPreview, $itemsCount, $itemsQty),
|
||||
'totals' => '<div class="orders-money">'
|
||||
. '<div class="orders-money__main">' . htmlspecialchars($totalWithTax . ' ' . $currency, ENT_QUOTES, 'UTF-8') . ($isUnpaid ? ' <span class="order-tag is-unpaid">Nieopłacone</span>' : '') . '</div>'
|
||||
. '<div class="orders-money__meta">' . ($isCod ? '<span class="order-tag is-cod">Za pobraniem</span>' : 'oplacono: ' . htmlspecialchars($totalPaid . ' ' . $currency, ENT_QUOTES, 'UTF-8')) . '</div>'
|
||||
. '</div>',
|
||||
'shipping' => $this->shippingHtml(
|
||||
trim((string) ($row['external_carrier_id'] ?? '')),
|
||||
$shipments,
|
||||
$documents
|
||||
),
|
||||
'ordered_at' => (string) ($row['ordered_at'] ?? ''),
|
||||
];
|
||||
}
|
||||
|
||||
private function statusBadge(string $statusCode, string $statusLabel, string $colorHex = ''): string
|
||||
{
|
||||
$label = $statusLabel !== '' ? $statusLabel : '-';
|
||||
$code = strtolower(trim($statusCode));
|
||||
|
||||
if ($colorHex !== '') {
|
||||
$style = 'background-color:' . htmlspecialchars($colorHex, ENT_QUOTES, 'UTF-8') . ';color:#fff';
|
||||
return '<span class="order-tag" style="' . $style . '">' . htmlspecialchars($label, ENT_QUOTES, 'UTF-8') . '</span>';
|
||||
}
|
||||
|
||||
$class = 'is-neutral';
|
||||
if (in_array($code, ['shipped', 'delivered'], true)) {
|
||||
$class = 'is-success';
|
||||
} elseif (in_array($code, ['cancelled', 'returned'], true)) {
|
||||
$class = 'is-danger';
|
||||
} elseif (in_array($code, ['new', 'confirmed'], true)) {
|
||||
$class = 'is-info';
|
||||
} elseif (in_array($code, ['processing', 'packed', 'paid'], true)) {
|
||||
$class = 'is-warn';
|
||||
}
|
||||
|
||||
return '<span class="order-tag ' . $class . '">' . htmlspecialchars($label, ENT_QUOTES, 'UTF-8') . '</span>';
|
||||
}
|
||||
|
||||
private function sourceLabel(string $source): string
|
||||
{
|
||||
return match (strtolower(trim($source))) {
|
||||
'allegro' => 'Allegro',
|
||||
'shoppro' => 'shopPRO',
|
||||
'erli' => 'Erli',
|
||||
default => ucfirst(strtolower(trim($source))),
|
||||
};
|
||||
}
|
||||
|
||||
private function statusLabel(string $statusCode, array $statusLabelMap = []): string
|
||||
{
|
||||
$key = strtolower(trim($statusCode));
|
||||
if ($key === '') {
|
||||
return '-';
|
||||
}
|
||||
|
||||
if (isset($statusLabelMap[$key])) {
|
||||
return (string) $statusLabelMap[$key];
|
||||
}
|
||||
|
||||
$normalized = str_replace(['_', '-'], ' ', $key);
|
||||
return ucfirst($normalized);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, array{name:string,color_hex:string,items:array<int, array{code:string,name:string}>}> $config
|
||||
* @param array<string, int> $counts
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
private function buildStatusPanel(array $config, array $counts, string $currentStatusCode, array $query = []): array
|
||||
{
|
||||
$allCount = 0;
|
||||
foreach ($counts as $count) {
|
||||
$allCount += (int) $count;
|
||||
}
|
||||
|
||||
$result = [[
|
||||
'name' => '',
|
||||
'items' => [[
|
||||
'code' => '',
|
||||
'label' => 'Wszystkie',
|
||||
'count' => $allCount,
|
||||
'is_active' => trim($currentStatusCode) === '',
|
||||
'tone' => 'neutral',
|
||||
'color_hex' => '#64748b',
|
||||
'url' => $this->statusFilterUrl($query, ''),
|
||||
]],
|
||||
]];
|
||||
|
||||
foreach ($config as $group) {
|
||||
$items = [];
|
||||
$groupColor = StringHelper::normalizeColorHex((string) ($group['color_hex'] ?? '#64748b'));
|
||||
$groupItems = is_array($group['items'] ?? null) ? $group['items'] : [];
|
||||
foreach ($groupItems as $status) {
|
||||
$code = strtolower(trim((string) ($status['code'] ?? '')));
|
||||
if ($code === '') {
|
||||
continue;
|
||||
}
|
||||
$items[] = [
|
||||
'code' => $code,
|
||||
'label' => (string) ($status['name'] ?? $code),
|
||||
'count' => (int) ($counts[$code] ?? 0),
|
||||
'is_active' => trim(strtolower($currentStatusCode)) === $code,
|
||||
'tone' => $this->statusTone($code),
|
||||
'color_hex' => $groupColor,
|
||||
'url' => $this->statusFilterUrl($query, $code),
|
||||
];
|
||||
}
|
||||
if ($items === []) {
|
||||
continue;
|
||||
}
|
||||
$result[] = [
|
||||
'name' => (string) ($group['name'] ?? ''),
|
||||
'color_hex' => $groupColor,
|
||||
'items' => $items,
|
||||
];
|
||||
}
|
||||
|
||||
$usedCodes = [];
|
||||
foreach ($result as $group) {
|
||||
$items = is_array($group['items'] ?? null) ? $group['items'] : [];
|
||||
foreach ($items as $item) {
|
||||
$code = strtolower(trim((string) ($item['code'] ?? '')));
|
||||
if ($code !== '') {
|
||||
$usedCodes[$code] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$extraItems = [];
|
||||
foreach ($counts as $code => $count) {
|
||||
$normalizedCode = strtolower(trim((string) $code));
|
||||
if ($normalizedCode === '' || $normalizedCode === '_empty' || isset($usedCodes[$normalizedCode])) {
|
||||
continue;
|
||||
}
|
||||
$extraItems[] = [
|
||||
'code' => $normalizedCode,
|
||||
'label' => $this->statusLabel($normalizedCode),
|
||||
'count' => (int) $count,
|
||||
'is_active' => trim(strtolower($currentStatusCode)) === $normalizedCode,
|
||||
'tone' => $this->statusTone($normalizedCode),
|
||||
'color_hex' => '#64748b',
|
||||
'url' => $this->statusFilterUrl($query, $normalizedCode),
|
||||
];
|
||||
}
|
||||
if ($extraItems !== []) {
|
||||
$result[] = [
|
||||
'name' => 'Pozostale',
|
||||
'color_hex' => '#64748b',
|
||||
'items' => $extraItems,
|
||||
];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $query
|
||||
*/
|
||||
private function statusFilterUrl(array $query, string $statusCode): string
|
||||
{
|
||||
$params = $query;
|
||||
if ($statusCode === '') {
|
||||
unset($params['status']);
|
||||
} else {
|
||||
$params['status'] = $statusCode;
|
||||
}
|
||||
$params['page'] = 1;
|
||||
|
||||
$clean = [];
|
||||
foreach ($params as $key => $value) {
|
||||
if ($value === '' || $value === null) {
|
||||
continue;
|
||||
}
|
||||
$clean[(string) $key] = (string) $value;
|
||||
}
|
||||
|
||||
$qs = http_build_query($clean);
|
||||
return $qs === '' ? '/orders/list' : '/orders/list?' . $qs;
|
||||
}
|
||||
|
||||
private function statusTone(string $statusCode): string
|
||||
{
|
||||
$code = strtolower(trim($statusCode));
|
||||
if (in_array($code, ['new', 'confirmed'], true)) {
|
||||
return 'info';
|
||||
}
|
||||
if (in_array($code, ['paid', 'processing', 'packed'], true)) {
|
||||
return 'warn';
|
||||
}
|
||||
if (in_array($code, ['shipped', 'delivered'], true)) {
|
||||
return 'success';
|
||||
}
|
||||
if (in_array($code, ['cancelled', 'returned'], true)) {
|
||||
return 'danger';
|
||||
}
|
||||
|
||||
return 'neutral';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, array{name:string,color_hex:string,items:array<int, array{code:string,name:string}>}> $config
|
||||
* @return array<string, string>
|
||||
*/
|
||||
private function statusLabelMap(array $config): array
|
||||
{
|
||||
$map = [];
|
||||
foreach ($config as $group) {
|
||||
$items = is_array($group['items'] ?? null) ? $group['items'] : [];
|
||||
foreach ($items as $item) {
|
||||
$code = strtolower(trim((string) ($item['code'] ?? '')));
|
||||
if ($code === '') {
|
||||
continue;
|
||||
}
|
||||
$map[$code] = (string) ($item['name'] ?? $code);
|
||||
}
|
||||
}
|
||||
|
||||
return $map;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, array{name:string,color_hex:string,items:array<int, array{code:string,name:string}>}> $config
|
||||
* @return array<string, string>
|
||||
*/
|
||||
private function statusColorMap(array $config): array
|
||||
{
|
||||
$map = [];
|
||||
foreach ($config as $group) {
|
||||
$groupColor = StringHelper::normalizeColorHex((string) ($group['color_hex'] ?? ''));
|
||||
if ($groupColor === '') {
|
||||
continue;
|
||||
}
|
||||
$items = is_array($group['items'] ?? null) ? $group['items'] : [];
|
||||
foreach ($items as $item) {
|
||||
$code = strtolower(trim((string) ($item['code'] ?? '')));
|
||||
if ($code !== '') {
|
||||
$map[$code] = $groupColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $map;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string> $statusCodes
|
||||
* @param array<string, string> $statusLabelMap
|
||||
* @return array<string, string>
|
||||
*/
|
||||
private function buildStatusFilterOptions(array $statusCodes, array $statusLabelMap): array
|
||||
{
|
||||
$options = [];
|
||||
foreach ($statusCodes as $code => $value) {
|
||||
$rawCode = trim((string) ($code !== '' ? $code : $value));
|
||||
if ($rawCode === '') {
|
||||
continue;
|
||||
}
|
||||
$normalizedCode = strtolower($rawCode);
|
||||
$options[$normalizedCode] = $this->statusLabel($normalizedCode, $statusLabelMap);
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, array<string, mixed>> $itemsPreview
|
||||
*/
|
||||
private function productsHtml(array $itemsPreview, int $itemsCount, string $itemsQty): string
|
||||
{
|
||||
if ($itemsPreview === []) {
|
||||
return '<div class="orders-products">'
|
||||
. '<div class="orders-products__meta">0 pozycji / 0.000 szt.</div>'
|
||||
. '</div>';
|
||||
}
|
||||
|
||||
$html = '<div class="orders-products">';
|
||||
foreach ($itemsPreview as $item) {
|
||||
$name = trim((string) ($item['name'] ?? ''));
|
||||
$qty = $this->formatQuantity((float) ($item['quantity'] ?? 0));
|
||||
$mediaUrl = trim((string) ($item['media_url'] ?? ''));
|
||||
|
||||
$thumb = $mediaUrl !== ''
|
||||
? '<span class="orders-image-hover-wrap">'
|
||||
. '<img class="orders-product__thumb" src="' . htmlspecialchars($mediaUrl, ENT_QUOTES, 'UTF-8') . '" alt="">'
|
||||
. '<img class="orders-image-hover-popup" src="' . htmlspecialchars($mediaUrl, ENT_QUOTES, 'UTF-8') . '" alt="">'
|
||||
. '</span>'
|
||||
: '<span class="orders-product__thumb orders-product__thumb--empty"></span>';
|
||||
|
||||
$html .= '<div class="orders-product">'
|
||||
. $thumb
|
||||
. '<div class="orders-product__txt">'
|
||||
. '<div class="orders-product__name">' . htmlspecialchars($name !== '' ? $name : '-', ENT_QUOTES, 'UTF-8') . '</div>'
|
||||
. '<div class="orders-product__qty">' . htmlspecialchars($qty, ENT_QUOTES, 'UTF-8') . ' szt.</div>'
|
||||
. '</div>'
|
||||
. '</div>';
|
||||
}
|
||||
|
||||
if ($itemsCount > count($itemsPreview)) {
|
||||
$html .= '<div class="orders-products__more">+' . ($itemsCount - count($itemsPreview)) . ' pozycji</div>';
|
||||
}
|
||||
$html .= '<div class="orders-products__meta">' . $itemsCount . ' pozycji / ' . htmlspecialchars($itemsQty, ENT_QUOTES, 'UTF-8') . ' szt.</div>';
|
||||
$html .= '</div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
private function shippingHtml(string $deliveryMethod, int $shipments, int $documents): string
|
||||
{
|
||||
$deliveryMethod = trim(html_entity_decode(strip_tags($deliveryMethod), ENT_QUOTES | ENT_HTML5, 'UTF-8'));
|
||||
$html = '<div class="orders-mini">';
|
||||
if ($deliveryMethod !== '' && !preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i', $deliveryMethod)) {
|
||||
$html .= '<div class="orders-mini__delivery">' . htmlspecialchars($deliveryMethod, ENT_QUOTES, 'UTF-8') . '</div>';
|
||||
}
|
||||
$html .= '<div>wys.: <strong>' . $shipments . '</strong> dok.: <strong>' . $documents . '</strong></div>';
|
||||
$html .= '</div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
private function formatQuantity(float $value): string
|
||||
{
|
||||
$rounded = round($value, 3);
|
||||
if (abs($rounded - round($rounded)) < 0.0005) {
|
||||
return (string) (int) round($rounded);
|
||||
}
|
||||
|
||||
$formatted = number_format($rounded, 3, '.', '');
|
||||
return rtrim(rtrim($formatted, '0'), '.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
private function paymentStatusFilterOptions(): array
|
||||
{
|
||||
return [
|
||||
'' => $this->translator->get('orders.filters.any'),
|
||||
'0' => 'nieoplacone',
|
||||
'1' => 'czesciowo oplacone',
|
||||
'2' => 'oplacone',
|
||||
'3' => 'zwrocone',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, array{name:string,color_hex:string,items:array<int, array{code:string,name:string}>}> $config
|
||||
* @return array<int, array{code:string, name:string, group:string}>
|
||||
*/
|
||||
private function buildAllStatusOptions(array $config): array
|
||||
{
|
||||
$options = [];
|
||||
foreach ($config as $group) {
|
||||
$groupName = trim((string) ($group['name'] ?? ''));
|
||||
$items = is_array($group['items'] ?? null) ? $group['items'] : [];
|
||||
foreach ($items as $item) {
|
||||
$code = strtolower(trim((string) ($item['code'] ?? '')));
|
||||
if ($code === '') {
|
||||
continue;
|
||||
}
|
||||
$options[] = [
|
||||
'code' => $code,
|
||||
'name' => (string) ($item['name'] ?? $code),
|
||||
'group' => $groupName,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, array<string, mixed>> $history
|
||||
* @param array<string, string> $statusLabelMap
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
private function resolveHistoryLabels(array $history, array $statusLabelMap): array
|
||||
{
|
||||
return array_map(function (array $entry) use ($statusLabelMap): array {
|
||||
$fromCode = trim((string) ($entry['from_status_id'] ?? ''));
|
||||
$toCode = trim((string) ($entry['to_status_id'] ?? ''));
|
||||
$entry['from_label'] = $fromCode !== '' ? $this->statusLabel($fromCode, $statusLabelMap) : '-';
|
||||
$entry['to_label'] = $toCode !== '' ? $this->statusLabel($toCode, $statusLabelMap) : '-';
|
||||
|
||||
return $entry;
|
||||
}, $history);
|
||||
}
|
||||
|
||||
public function sendEmail(Request $request): Response
|
||||
{
|
||||
$orderId = max(0, (int) $request->input('id', 0));
|
||||
if ($orderId <= 0) {
|
||||
return Response::json(['success' => false, 'message' => 'Nieprawidlowe zamowienie'], 400);
|
||||
}
|
||||
|
||||
$csrfToken = (string) $request->input('_token', '');
|
||||
if (!Csrf::validate($csrfToken)) {
|
||||
return Response::json(['success' => false, 'message' => 'Sesja wygasla, odswiez strone'], 403);
|
||||
}
|
||||
|
||||
if ($this->emailService === null) {
|
||||
return Response::json(['success' => false, 'message' => 'Modul e-mail nie jest skonfigurowany'], 500);
|
||||
}
|
||||
|
||||
$templateId = max(0, (int) $request->input('template_id', 0));
|
||||
if ($templateId <= 0) {
|
||||
return Response::json(['success' => false, 'message' => 'Wybierz szablon'], 400);
|
||||
}
|
||||
|
||||
$mailboxId = (int) $request->input('mailbox_id', 0);
|
||||
$user = $this->auth->user();
|
||||
$userName = is_array($user) ? trim((string) ($user['name'] ?? $user['email'] ?? '')) : '';
|
||||
$result = $this->emailService->send($orderId, $templateId, $mailboxId > 0 ? $mailboxId : null, $userName !== '' ? $userName : null);
|
||||
|
||||
return Response::json([
|
||||
'success' => $result['success'],
|
||||
'message' => $result['success'] ? 'E-mail wyslany pomyslnie' : ('Blad wysylki: ' . ($result['error'] ?? 'nieznany')),
|
||||
]);
|
||||
}
|
||||
|
||||
public function emailPreview(Request $request): Response
|
||||
{
|
||||
$orderId = max(0, (int) $request->input('id', 0));
|
||||
$templateId = max(0, (int) $request->input('template_id', 0));
|
||||
|
||||
if ($orderId <= 0 || $templateId <= 0 || $this->emailService === null) {
|
||||
return Response::json(['subject' => '', 'body_html' => '', 'attachments' => []], 400);
|
||||
}
|
||||
|
||||
$preview = $this->emailService->preview($orderId, $templateId);
|
||||
|
||||
return Response::json($preview);
|
||||
}
|
||||
|
||||
public function addPayment(Request $request): Response
|
||||
{
|
||||
$orderId = max(0, (int) $request->input('id', 0));
|
||||
if ($orderId <= 0) {
|
||||
return Response::json(['ok' => false, 'error' => 'Nieprawidłowe ID zamówienia.'], 400);
|
||||
}
|
||||
|
||||
if (!Csrf::validate((string) $request->input('_token', ''))) {
|
||||
return Response::json(['ok' => false, 'error' => 'Nieprawidłowy token CSRF.'], 403);
|
||||
}
|
||||
|
||||
$amount = (float) $request->input('amount', 0);
|
||||
$paymentTypeId = trim((string) $request->input('payment_type_id', ''));
|
||||
$paymentDate = trim((string) $request->input('payment_date', ''));
|
||||
$comment = trim((string) $request->input('comment', ''));
|
||||
|
||||
if ($amount <= 0) {
|
||||
return Response::json(['ok' => false, 'error' => 'Kwota musi być większa od 0.'], 422);
|
||||
}
|
||||
if ($paymentTypeId === '') {
|
||||
return Response::json(['ok' => false, 'error' => 'Wybierz typ płatności.'], 422);
|
||||
}
|
||||
|
||||
try {
|
||||
$result = $this->orders->addPayment($orderId, [
|
||||
'amount' => $amount,
|
||||
'payment_type_id' => $paymentTypeId,
|
||||
'payment_date' => $paymentDate !== '' ? $paymentDate . ' ' . date('H:i:s') : '',
|
||||
'comment' => $comment,
|
||||
]);
|
||||
} catch (\Throwable $ex) {
|
||||
return Response::json(['ok' => false, 'error' => 'Błąd zapisu: ' . $ex->getMessage()], 500);
|
||||
}
|
||||
|
||||
if ($result === null) {
|
||||
return Response::json(['ok' => false, 'error' => 'Nie udało się zapisać płatności.'], 500);
|
||||
}
|
||||
|
||||
$this->orders->recordActivity(
|
||||
$orderId,
|
||||
'payment',
|
||||
'Dodano płatność: ' . number_format($amount, 2, '.', ' ') . ' PLN (' . $paymentTypeId . ')',
|
||||
['payment_id' => $result['id'], 'amount' => $amount, 'type' => $paymentTypeId],
|
||||
'user',
|
||||
($this->auth->user() ?? [])['name'] ?? null
|
||||
);
|
||||
|
||||
try {
|
||||
$this->automation?->trigger('payment.status_changed', $orderId, [
|
||||
'new_payment_status' => (string) $result['payment_status'],
|
||||
'total_paid' => $result['total_paid'],
|
||||
'payment_type_id' => $paymentTypeId,
|
||||
]);
|
||||
} catch (\Throwable) {
|
||||
}
|
||||
|
||||
$this->pushPaymentToShoppro($orderId, $result['payment_status']);
|
||||
|
||||
return Response::json([
|
||||
'ok' => true,
|
||||
'payment_id' => $result['id'],
|
||||
'payment_status' => $result['payment_status'],
|
||||
'total_paid' => $result['total_paid'],
|
||||
]);
|
||||
}
|
||||
|
||||
private function pushPaymentToShoppro(int $orderId, int $paymentStatus): void
|
||||
{
|
||||
if ($paymentStatus !== 2 || $this->shopproIntegrations === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$orderStmt = $this->orders->findOrderSourceInfo($orderId);
|
||||
if ($orderStmt === null || ($orderStmt['source'] ?? '') !== 'shoppro') {
|
||||
return;
|
||||
}
|
||||
|
||||
$integrationId = (int) ($orderStmt['integration_id'] ?? 0);
|
||||
$sourceOrderId = trim((string) ($orderStmt['source_order_id'] ?? ''));
|
||||
if ($integrationId <= 0 || $sourceOrderId === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
$integration = $this->shopproIntegrations->findIntegration($integrationId);
|
||||
if ($integration === null || empty($integration['is_active']) || empty($integration['has_api_key'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$baseUrl = trim((string) ($integration['base_url'] ?? ''));
|
||||
$apiKey = $this->shopproIntegrations->getApiKeyDecrypted($integrationId);
|
||||
if ($baseUrl === '' || $apiKey === null || trim($apiKey) === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
$client = new ShopproApiClient();
|
||||
$pushResult = $client->setOrderPaid($baseUrl, $apiKey, 10, $sourceOrderId);
|
||||
|
||||
$this->orders->recordActivity(
|
||||
$orderId,
|
||||
'sync',
|
||||
$pushResult['ok']
|
||||
? 'Wysłano status płatności do shopPRO (opłacone)'
|
||||
: 'Błąd push płatności do shopPRO: ' . ($pushResult['message'] ?? 'unknown'),
|
||||
['direction' => 'push', 'target' => 'shoppro', 'ok' => $pushResult['ok']],
|
||||
'system'
|
||||
);
|
||||
} catch (\Throwable) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
108
.vscode/ftp-kr.sync.cache.json
vendored
108
.vscode/ftp-kr.sync.cache.json
vendored
@@ -288,9 +288,9 @@
|
||||
},
|
||||
"20260304_000023_create_allegro_integration_settings_table.sql": {
|
||||
"type": "-",
|
||||
"size": 1416,
|
||||
"size": 1427,
|
||||
"lmtime": 1772654150195,
|
||||
"modified": false
|
||||
"modified": true
|
||||
},
|
||||
"20260304_000024_add_allegro_token_refresh_schedule.sql": {
|
||||
"type": "-",
|
||||
@@ -538,16 +538,22 @@
|
||||
"lmtime": 1774611787688,
|
||||
"modified": false
|
||||
},
|
||||
"20260328_000001_add_html_layout_to_email_mailboxes.sql": {
|
||||
"type": "-",
|
||||
"size": 136,
|
||||
"lmtime": 1774725255424,
|
||||
"modified": false
|
||||
},
|
||||
"20260328_000072_create_automation_execution_logs_table.sql": {
|
||||
"type": "-",
|
||||
"size": 1653,
|
||||
"lmtime": 1774702996564,
|
||||
"modified": false
|
||||
},
|
||||
"20260328_000001_add_html_layout_to_email_mailboxes.sql": {
|
||||
"20260330_000073_create_order_payments_table.sql": {
|
||||
"type": "-",
|
||||
"size": 136,
|
||||
"lmtime": 1774725255424,
|
||||
"size": 2507,
|
||||
"lmtime": 1774904625009,
|
||||
"modified": false
|
||||
}
|
||||
},
|
||||
@@ -570,14 +576,14 @@
|
||||
"DOCS": {
|
||||
"ARCHITECTURE.md": {
|
||||
"type": "-",
|
||||
"size": 39868,
|
||||
"lmtime": 1774725452817,
|
||||
"size": 40166,
|
||||
"lmtime": 1774905621286,
|
||||
"modified": false
|
||||
},
|
||||
"DB_SCHEMA.md": {
|
||||
"type": "-",
|
||||
"size": 32413,
|
||||
"lmtime": 1774725420710,
|
||||
"size": 33256,
|
||||
"lmtime": 1774904646453,
|
||||
"modified": false
|
||||
},
|
||||
"ORDERS_SCHEMA_APILO_DRAFT.md": {
|
||||
@@ -600,8 +606,8 @@
|
||||
},
|
||||
"TECH_CHANGELOG.md": {
|
||||
"type": "-",
|
||||
"size": 66422,
|
||||
"lmtime": 1774725440560,
|
||||
"size": 69323,
|
||||
"lmtime": 1774906997086,
|
||||
"modified": false
|
||||
},
|
||||
"todo.md": {
|
||||
@@ -1824,8 +1830,8 @@
|
||||
"css": {
|
||||
"app.css": {
|
||||
"type": "-",
|
||||
"size": 57250,
|
||||
"lmtime": 1774820920578,
|
||||
"size": 57932,
|
||||
"lmtime": 1774874372180,
|
||||
"modified": false
|
||||
},
|
||||
"app.css.map": {
|
||||
@@ -1860,8 +1866,8 @@
|
||||
"modules": {
|
||||
"automation-form.js": {
|
||||
"type": "-",
|
||||
"size": 8789,
|
||||
"lmtime": 1774704031241,
|
||||
"size": 9406,
|
||||
"lmtime": 1774905596185,
|
||||
"modified": false
|
||||
},
|
||||
"inline-status-change.js": {
|
||||
@@ -1921,8 +1927,8 @@
|
||||
"scss": {
|
||||
"app.css": {
|
||||
"type": "-",
|
||||
"size": 41813,
|
||||
"lmtime": 1773532822690,
|
||||
"size": 57932,
|
||||
"lmtime": 1774874360396,
|
||||
"modified": false
|
||||
},
|
||||
"app.css.map": {
|
||||
@@ -1933,8 +1939,8 @@
|
||||
},
|
||||
"app.scss": {
|
||||
"type": "-",
|
||||
"size": 45727,
|
||||
"lmtime": 1774820913052,
|
||||
"size": 46381,
|
||||
"lmtime": 1774874345832,
|
||||
"modified": false
|
||||
},
|
||||
"login.css": {
|
||||
@@ -2091,8 +2097,8 @@
|
||||
},
|
||||
"show.php": {
|
||||
"type": "-",
|
||||
"size": 41726,
|
||||
"lmtime": 1774820344044,
|
||||
"size": 50549,
|
||||
"lmtime": 1774874320939,
|
||||
"modified": false
|
||||
}
|
||||
},
|
||||
@@ -2252,8 +2258,8 @@
|
||||
"automation": {
|
||||
"form.php": {
|
||||
"type": "-",
|
||||
"size": 14751,
|
||||
"lmtime": 1774704020030,
|
||||
"size": 15894,
|
||||
"lmtime": 1774906988194,
|
||||
"modified": false
|
||||
},
|
||||
"index.php": {
|
||||
@@ -2268,8 +2274,8 @@
|
||||
"routes": {
|
||||
"web.php": {
|
||||
"type": "-",
|
||||
"size": 27445,
|
||||
"lmtime": 1774706514882,
|
||||
"size": 27598,
|
||||
"lmtime": 1774905481955,
|
||||
"modified": false
|
||||
}
|
||||
},
|
||||
@@ -2488,8 +2494,8 @@
|
||||
},
|
||||
"CronHandlerFactory.php": {
|
||||
"type": "-",
|
||||
"size": 8710,
|
||||
"lmtime": 1774702549664,
|
||||
"size": 8757,
|
||||
"lmtime": 1774905521818,
|
||||
"modified": false
|
||||
},
|
||||
"CronJobProcessor.php": {
|
||||
@@ -2634,14 +2640,14 @@
|
||||
},
|
||||
"OrdersController.php": {
|
||||
"type": "-",
|
||||
"size": 34671,
|
||||
"lmtime": 1774599151246,
|
||||
"size": 39402,
|
||||
"lmtime": 1774905472960,
|
||||
"modified": false
|
||||
},
|
||||
"OrdersRepository.php": {
|
||||
"type": "-",
|
||||
"size": 34109,
|
||||
"lmtime": 1774474648512,
|
||||
"size": 37722,
|
||||
"lmtime": 1774874236106,
|
||||
"modified": false
|
||||
},
|
||||
"OrderStatusSyncService.php": {
|
||||
@@ -2748,9 +2754,9 @@
|
||||
},
|
||||
"AllegroIntegrationController.php": {
|
||||
"type": "-",
|
||||
"size": 26499,
|
||||
"size": 26547,
|
||||
"lmtime": 1773418757646,
|
||||
"modified": false
|
||||
"modified": true
|
||||
},
|
||||
"AllegroIntegrationRepository.php": {
|
||||
"type": "-",
|
||||
@@ -2760,7 +2766,7 @@
|
||||
},
|
||||
"AllegroOAuthClient.php": {
|
||||
"type": "-",
|
||||
"size": 7133,
|
||||
"size": 7199,
|
||||
"lmtime": 1773396209523,
|
||||
"modified": true
|
||||
},
|
||||
@@ -2964,8 +2970,8 @@
|
||||
},
|
||||
"ShopproApiClient.php": {
|
||||
"type": "-",
|
||||
"size": 12582,
|
||||
"lmtime": 1774612664232,
|
||||
"size": 13506,
|
||||
"lmtime": 1774874195967,
|
||||
"modified": false
|
||||
},
|
||||
"ShopProClient.php": {
|
||||
@@ -3012,8 +3018,8 @@
|
||||
},
|
||||
"ShopproPaymentStatusSyncService.php": {
|
||||
"type": "-",
|
||||
"size": 13746,
|
||||
"lmtime": 1773397561728,
|
||||
"size": 14390,
|
||||
"lmtime": 1774905506418,
|
||||
"modified": false
|
||||
},
|
||||
"ShopproProductImageResolver.php": {
|
||||
@@ -3150,8 +3156,8 @@
|
||||
"Automation": {
|
||||
"AutomationController.php": {
|
||||
"type": "-",
|
||||
"size": 19948,
|
||||
"lmtime": 1774703990637,
|
||||
"size": 22513,
|
||||
"lmtime": 1774906921315,
|
||||
"modified": false
|
||||
},
|
||||
"AutomationExecutionLogRepository.php": {
|
||||
@@ -3168,8 +3174,8 @@
|
||||
},
|
||||
"AutomationService.php": {
|
||||
"type": "-",
|
||||
"size": 29708,
|
||||
"lmtime": 1774704000450,
|
||||
"size": 30415,
|
||||
"lmtime": 1774905433588,
|
||||
"modified": false
|
||||
}
|
||||
},
|
||||
@@ -5351,6 +5357,24 @@
|
||||
"phpmailer": {
|
||||
"phpmailer": {}
|
||||
}
|
||||
},
|
||||
".serena": {
|
||||
"cache": {
|
||||
"php": {
|
||||
"document_symbols.pkl": {
|
||||
"type": "-",
|
||||
"size": 13361425,
|
||||
"lmtime": 1774906273656,
|
||||
"modified": false
|
||||
},
|
||||
"raw_document_symbols.pkl": {
|
||||
"type": "-",
|
||||
"size": 4107469,
|
||||
"lmtime": 1774906272460,
|
||||
"modified": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user