update
This commit is contained in:
@@ -231,6 +231,8 @@ final class OrdersController
|
||||
$flashSuccess = (string) Flash::get('order.success', '');
|
||||
$flashError = (string) Flash::get('order.error', '');
|
||||
|
||||
$customerRiskInfo = $this->buildCustomerRiskInfo($order, $orderId);
|
||||
|
||||
$html = $this->template->render('orders/show', [
|
||||
'title' => $this->translator->get('orders.details.title') . ' #' . $orderId,
|
||||
'activeMenu' => 'orders',
|
||||
@@ -259,11 +261,69 @@ final class OrdersController
|
||||
'receiptConfigs' => $activeReceiptConfigs,
|
||||
'emailTemplates' => $emailTemplates,
|
||||
'emailMailboxes' => $emailMailboxes,
|
||||
'customerRiskInfo' => $customerRiskInfo,
|
||||
], 'layouts/app');
|
||||
|
||||
return Response::html($html);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sklada informacje o historii zwrotow klienta biezacego zamowienia.
|
||||
*
|
||||
* @param array<string, mixed> $order
|
||||
* @return array{count:int, orders:array<int, array<string, mixed>>, email:string, phone:string, name:string, text:string}
|
||||
*/
|
||||
private function buildCustomerRiskInfo(array $order, int $orderId): array
|
||||
{
|
||||
$count = max(0, (int) ($order['customer_returned_count'] ?? 0));
|
||||
$email = trim((string) ($order['buyer_email'] ?? ''));
|
||||
$phone = trim((string) ($order['buyer_phone'] ?? ''));
|
||||
$name = trim((string) ($order['buyer_name'] ?? ''));
|
||||
|
||||
$returnedOrders = [];
|
||||
if ($count > 0 && $this->shipmentPackages !== null) {
|
||||
$returnedOrders = $this->shipmentPackages->findReturnedByCustomer(
|
||||
['email' => $email, 'phone' => $phone, 'name' => $name],
|
||||
$orderId
|
||||
);
|
||||
}
|
||||
|
||||
return [
|
||||
'count' => $count,
|
||||
'orders' => $returnedOrders,
|
||||
'email' => $email,
|
||||
'phone' => $phone,
|
||||
'name' => $name,
|
||||
'text' => $this->composeCustomerRiskText($count, $email, $phone, $name),
|
||||
];
|
||||
}
|
||||
|
||||
private function composeCustomerRiskText(int $count, string $email, string $phone, string $name): string
|
||||
{
|
||||
if ($count <= 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$hasPhone = $phone !== '';
|
||||
$hasEmail = $email !== '';
|
||||
$hasName = $name !== '';
|
||||
|
||||
if ($hasPhone && $hasEmail) {
|
||||
$subject = 'Osoba o numerze telefonu ' . $phone . ' oraz email ' . $email;
|
||||
} elseif ($hasEmail) {
|
||||
$subject = 'Osoba o emailu ' . $email;
|
||||
} elseif ($hasPhone) {
|
||||
$subject = 'Osoba o numerze telefonu ' . $phone;
|
||||
} elseif ($hasName) {
|
||||
$subject = 'Osoba o imieniu i nazwisku ' . $name;
|
||||
} else {
|
||||
$subject = 'Ten klient';
|
||||
}
|
||||
|
||||
$noun = $count === 1 ? 'przesylke' : 'przesylek';
|
||||
return $subject . ' nie odebrala ' . $count . ' ' . $noun . '.';
|
||||
}
|
||||
|
||||
public function updateDetails(Request $request): Response
|
||||
{
|
||||
$orderId = max(0, (int) $request->input('id', 0));
|
||||
@@ -429,6 +489,10 @@ final class OrdersController
|
||||
$itemsPreview = is_array($row['items_preview'] ?? null) ? $row['items_preview'] : [];
|
||||
$projectsDone = max(0, (int) ($row['projects_done'] ?? 0));
|
||||
$projectsTotal = max(0, (int) ($row['projects_total'] ?? 0));
|
||||
$returnedCount = max(0, (int) ($row['customer_returned_count'] ?? 0));
|
||||
$returnedBadge = $returnedCount >= 1
|
||||
? ' <span class="risk-return-badge" title="Klient nie odebral ' . $returnedCount . ' przesylek w historii">zwroty: ' . $returnedCount . '</span>'
|
||||
: '';
|
||||
|
||||
$previewBtn = '<button type="button" class="btn-icon js-order-preview-btn" data-order-id="' . (int) ($row['id'] ?? 0) . '" title="Podglad">'
|
||||
. '<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>'
|
||||
@@ -446,7 +510,7 @@ final class OrdersController
|
||||
. '</div>'
|
||||
. '</div>',
|
||||
'buyer' => '<div class="orders-buyer">'
|
||||
. '<div class="orders-buyer__name">' . htmlspecialchars($buyerName !== '' ? $buyerName : '-', ENT_QUOTES, 'UTF-8') . '</div>'
|
||||
. '<div class="orders-buyer__name">' . htmlspecialchars($buyerName !== '' ? $buyerName : '-', ENT_QUOTES, 'UTF-8') . $returnedBadge . '</div>'
|
||||
. '<div class="orders-buyer__meta">'
|
||||
. '<span>' . htmlspecialchars($buyerEmail, ENT_QUOTES, 'UTF-8') . '</span>'
|
||||
. '<span>' . htmlspecialchars($buyerCity, ENT_QUOTES, 'UTF-8') . '</span>'
|
||||
@@ -466,7 +530,7 @@ final class OrdersController
|
||||
$documents
|
||||
),
|
||||
'ordered_at' => (string) ($row['ordered_at'] ?? ''),
|
||||
'_row_class' => $this->agedRowClass((string) ($row['ordered_at'] ?? '')),
|
||||
'_row_class' => trim($this->agedRowClass((string) ($row['ordered_at'] ?? '')) . ($returnedCount >= 1 ? ' is-risk-return' : '')),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -182,7 +182,8 @@ final class OrdersRepository
|
||||
COALESCE(oi_agg.projects_total, 0) AS projects_total,
|
||||
COALESCE(sh_agg.shipments_count, 0) AS shipments_count,
|
||||
COALESCE(od_agg.documents_count, 0) AS documents_count,
|
||||
ig.name AS integration_name
|
||||
ig.name AS integration_name,
|
||||
' . $this->customerReturnedCountSubquerySql('o', 'a') . ' AS customer_returned_count
|
||||
FROM orders o
|
||||
LEFT JOIN order_addresses a ON a.order_id = o.id AND a.address_type = "customer"
|
||||
LEFT JOIN allegro_order_status_mappings asm ON o.source = "allegro" AND LOWER(o.status_code) = asm.allegro_status_code
|
||||
@@ -246,6 +247,7 @@ final class OrdersRepository
|
||||
'items_preview' => (array) ($itemPreviewsByOrderId[$orderId] ?? []),
|
||||
'projects_done' => (int) ($row['projects_done'] ?? 0),
|
||||
'projects_total' => (int) ($row['projects_total'] ?? 0),
|
||||
'customer_returned_count' => max(0, (int) ($row['customer_returned_count'] ?? 0)),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -479,10 +481,15 @@ final class OrdersRepository
|
||||
$effectiveStatusSql = $this->effectiveStatusSql('o', 'asm');
|
||||
$orderStmt = $this->pdo->prepare(
|
||||
'SELECT o.*, ' . $effectiveStatusSql . ' AS effective_status_id,
|
||||
ig.name AS integration_name
|
||||
ig.name AS integration_name,
|
||||
a.email AS buyer_email,
|
||||
a.phone AS buyer_phone,
|
||||
a.name AS buyer_name,
|
||||
' . $this->customerReturnedCountSubquerySql('o', 'a') . ' AS customer_returned_count
|
||||
FROM orders o
|
||||
LEFT JOIN allegro_order_status_mappings asm ON o.source = "allegro" AND LOWER(o.status_code) = asm.allegro_status_code
|
||||
LEFT JOIN integrations ig ON ig.id = o.integration_id
|
||||
LEFT JOIN order_addresses a ON a.order_id = o.id AND a.address_type = "customer"
|
||||
WHERE o.id = :id
|
||||
LIMIT 1'
|
||||
);
|
||||
@@ -491,6 +498,7 @@ final class OrdersRepository
|
||||
if (!is_array($order)) {
|
||||
return null;
|
||||
}
|
||||
$order['customer_returned_count'] = max(0, (int) ($order['customer_returned_count'] ?? 0));
|
||||
|
||||
return [
|
||||
'order' => $order,
|
||||
@@ -669,6 +677,38 @@ final class OrdersRepository
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subquery zliczajaca zamowienia klienta biezacego wiersza, ktore w historii
|
||||
* mialy paczke z delivery_status='returned' (zwrot do nadawcy).
|
||||
* Matching po email LUB phone (tylko cyfry, min 6) LUB name — identyczne dopasowanie
|
||||
* po LOWER/TRIM. Wyklucza biezace zamowienie (self-exclusion).
|
||||
*
|
||||
* Wymagania: MySQL 8.0+ (REGEXP_REPLACE).
|
||||
*
|
||||
* @param string $orderAlias alias tabeli orders w outer query (np. 'o')
|
||||
* @param string $addressAlias alias joina order_addresses (customer) w outer query (np. 'a')
|
||||
*/
|
||||
private function customerReturnedCountSubquerySql(string $orderAlias, string $addressAlias): string
|
||||
{
|
||||
return '(SELECT COUNT(DISTINCT sp.order_id)
|
||||
FROM shipment_packages sp
|
||||
INNER JOIN order_addresses a2
|
||||
ON a2.order_id = sp.order_id AND a2.address_type = "customer"
|
||||
WHERE sp.delivery_status = "returned"
|
||||
AND sp.order_id != ' . $orderAlias . '.id
|
||||
AND (
|
||||
(' . $addressAlias . '.email IS NOT NULL AND ' . $addressAlias . '.email <> ""
|
||||
AND LOWER(TRIM(a2.email)) = LOWER(TRIM(' . $addressAlias . '.email)))
|
||||
OR
|
||||
(' . $addressAlias . '.phone IS NOT NULL
|
||||
AND LENGTH(REGEXP_REPLACE(' . $addressAlias . '.phone, "[^0-9]+", "")) >= 6
|
||||
AND REGEXP_REPLACE(a2.phone, "[^0-9]+", "") = REGEXP_REPLACE(' . $addressAlias . '.phone, "[^0-9]+", ""))
|
||||
OR
|
||||
(' . $addressAlias . '.name IS NOT NULL AND ' . $addressAlias . '.name <> ""
|
||||
AND LOWER(TRIM(a2.name)) = LOWER(TRIM(' . $addressAlias . '.name)))
|
||||
))';
|
||||
}
|
||||
|
||||
private function effectiveStatusSql(string $orderAlias, string $mappingAlias): string
|
||||
{
|
||||
return 'CASE
|
||||
|
||||
Reference in New Issue
Block a user