MERGE_TEST_SALES_CR
This commit is contained in:
commit
aff74592d2
@ -1710,8 +1710,11 @@ private function buildOppAchievement($db, array $sales_manager_ids, $branchId, s
|
||||
// leads.type: 1 = EB, else = Non-EB
|
||||
$wonLeads = $db->query("
|
||||
SELECT
|
||||
sal.lead_id,
|
||||
l.id AS opportunities_id,
|
||||
l.actual_lead_id,
|
||||
l.lead_type AS lead_type_id,
|
||||
sal.company_name AS company,
|
||||
l.client_name AS client_name,
|
||||
CASE WHEN l.lead_type = 1 THEN 'EB' ELSE 'Non-EB' END AS lead_type,
|
||||
l.created_at AS created_at,
|
||||
l.status
|
||||
@ -1725,6 +1728,7 @@ private function buildOppAchievement($db, array $sales_manager_ids, $branchId, s
|
||||
ORDER BY l.created_at DESC
|
||||
", [$uid, $fyStart, $fyEnd])->getResultArray();
|
||||
|
||||
// -- NOTE: AND TRIM(sal.company_name) = TRIM(l.client_name)
|
||||
$result[$uid] = [
|
||||
'total_policies' => $totalPolicies,
|
||||
'total_exp_amt' => $totalExpAmt,
|
||||
|
||||
@ -122,6 +122,10 @@ if (! function_exists('merge_ticket_pdfs')) {
|
||||
if (! is_dir($tempDir)) {
|
||||
@mkdir($tempDir, 0775, true);
|
||||
}
|
||||
$mpdfCacheDir = $tempDir . DIRECTORY_SEPARATOR . 'mpdf';
|
||||
if (! is_dir($mpdfCacheDir)) {
|
||||
@mkdir($mpdfCacheDir, 0775, true);
|
||||
}
|
||||
|
||||
$mergedName = 'merged_' . $ticket_master_id . '_' . time() . '_' . bin2hex(random_bytes(5)) . '.pdf';
|
||||
$mergedPath = $uploadDir . $mergedName;
|
||||
@ -271,15 +275,13 @@ if (! function_exists('merge_ticket_pdf_add_image_page')) {
|
||||
function merge_ticket_pdf_add_image_page(Mpdf $mpdf, string $imagePath): bool
|
||||
{
|
||||
$imageInfo = @getimagesize($imagePath);
|
||||
if (empty($imageInfo[0]) || empty($imageInfo[1])) {
|
||||
log_message('error', "merge_ticket_pdfs | invalid image | {$imagePath}");
|
||||
return false;
|
||||
}
|
||||
$ext = strtolower(pathinfo($imagePath, PATHINFO_EXTENSION));
|
||||
$imageType = $ext === 'png' || ((int) ($imageInfo[2] ?? 0) === IMAGETYPE_PNG) ? 'png' : 'jpg';
|
||||
|
||||
$imageWidthPx = (int) $imageInfo[0];
|
||||
$imageHeightPx = (int) $imageInfo[1];
|
||||
$imageType = ((int) ($imageInfo[2] ?? 0) === IMAGETYPE_PNG) ? 'png' : 'jpg';
|
||||
$isLandscape = $imageWidthPx > $imageHeightPx;
|
||||
$imageWidthPx = (int) ($imageInfo[0] ?? 0);
|
||||
$imageHeightPx = (int) ($imageInfo[1] ?? 0);
|
||||
$hasSize = $imageWidthPx > 0 && $imageHeightPx > 0;
|
||||
$isLandscape = $hasSize && $imageWidthPx > $imageHeightPx;
|
||||
|
||||
$pageWidth = $isLandscape ? 297 : 210;
|
||||
$pageHeight = $isLandscape ? 210 : 297;
|
||||
@ -287,11 +289,21 @@ if (! function_exists('merge_ticket_pdf_add_image_page')) {
|
||||
|
||||
$availableWidth = $pageWidth - ($margin * 2);
|
||||
$availableHeight = $pageHeight - ($margin * 2);
|
||||
$scale = min($availableWidth / $imageWidthPx, $availableHeight / $imageHeightPx);
|
||||
$drawWidth = $imageWidthPx * $scale;
|
||||
$drawHeight = $imageHeightPx * $scale;
|
||||
$x = ($pageWidth - $drawWidth) / 2;
|
||||
$y = ($pageHeight - $drawHeight) / 2;
|
||||
if ($hasSize) {
|
||||
$scale = min($availableWidth / $imageWidthPx, $availableHeight / $imageHeightPx);
|
||||
$drawWidth = $imageWidthPx * $scale;
|
||||
$drawHeight = $imageHeightPx * $scale;
|
||||
$x = ($pageWidth - $drawWidth) / 2;
|
||||
$y = ($pageHeight - $drawHeight) / 2;
|
||||
} else {
|
||||
// Some PNGs fail getimagesize(), but mPDF can still render them.
|
||||
// Use a portrait A4 fallback and let mPDF calculate image height.
|
||||
log_message('error', "merge_ticket_pdfs | image size unavailable, using fallback page | {$imagePath}");
|
||||
$drawWidth = $availableWidth;
|
||||
$drawHeight = 0;
|
||||
$x = $margin;
|
||||
$y = $margin;
|
||||
}
|
||||
|
||||
$mpdf->AddPageByArray([
|
||||
'orientation' => $isLandscape ? 'L' : 'P',
|
||||
|
||||
@ -21,20 +21,28 @@
|
||||
/* Leads Grid */
|
||||
.lead-header { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; /* responsive */ gap: 15px; }
|
||||
.lead-actions { display: flex; align-items: center; gap: 10px; }
|
||||
.sales-toolbar { flex-wrap: nowrap; padding: 16px 30px; gap: 12px; overflow-x: auto; }
|
||||
.sales-toolbar .filter-tabs { padding: 0; flex: 1 1 auto; min-width: 0; overflow-x: auto; }
|
||||
.sales-toolbar .lead-actions { flex: 0 0 auto; flex-wrap: nowrap; }
|
||||
.sales-toolbar .search-input { width: 230px !important; }
|
||||
.sales-toolbar { flex-wrap: nowrap; padding: 16px 15px; gap: 12px; overflow-x: auto; }
|
||||
.sales-toolbar .filter-tabs { padding: 0; flex: 0 0 auto; min-width: max-content; overflow: visible; }
|
||||
.sales-toolbar .lead-actions { flex: 1 1 auto; min-width: 0; flex-wrap: nowrap; justify-content: flex-end; }
|
||||
.sales-toolbar .search-input { width: 230px !important; height: 38px; padding: 9px 12px; border: 1px solid #ddd !important; border-radius: 6px !important; flex: 0 0 230px; }
|
||||
.sales-toolbar .toolbar-select { width: 175px !important; min-width: 175px; padding: 9px 12px; }
|
||||
.sales-toolbar .toolbar-select + .select2-container { width: 175px !important; min-width: 175px; }
|
||||
.sales-toolbar .financial-year-select { width: 108px !important; min-width: 108px; }
|
||||
.sales-toolbar .financial-year-select { width: 122px !important; min-width: 122px; }
|
||||
.sales-toolbar .financial-year-select + .select2-container { width: 122px !important; min-width: 122px; flex: 0 0 122px; }
|
||||
.sales-toolbar .select2-container .select2-selection--single { height: 38px; border: 1px solid #ddd; border-radius: 6px; display: flex; align-items: center; }
|
||||
.sales-toolbar .select2-container--default .select2-selection--single .select2-selection__rendered { line-height: 36px; }
|
||||
.sales-toolbar .select2-container--default .select2-selection--single .select2-selection__arrow { height: 36px; }
|
||||
.sales-toolbar #memberFilter + .select2-container { width: 175px !important; min-width: 175px; flex: 0 0 175px; }
|
||||
.sales-toolbar #memberFilter + .select2-container .select2-selection--single { height: 38px !important; background: #fff !important; border: 1px solid #ddd !important; border-radius: 6px !important; display: flex; align-items: center; }
|
||||
.sales-toolbar #memberFilter + .select2-container .select2-selection__rendered { flex: 1; line-height: 36px !important; padding-left: 12px; padding-right: 28px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.sales-toolbar #memberFilter + .select2-container .select2-selection__arrow { height: 25px !important; width: 20px; top: 1px; right: 6px; }
|
||||
.sales-toolbar #memberFilter + .select2-container .select2-selection--single,
|
||||
.sales-toolbar #financialYearFilter + .select2-container .select2-selection--single { height: 38px !important; background: #fff !important; border: 1px solid #ddd !important; border-radius: 6px !important; display: flex; align-items: center; }
|
||||
.sales-toolbar #memberFilter + .select2-container .select2-selection__rendered,
|
||||
.sales-toolbar #financialYearFilter + .select2-container .select2-selection__rendered { flex: 1; line-height: 36px !important; padding-left: 12px; padding-right: 28px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.sales-toolbar #memberFilter + .select2-container .select2-selection__arrow,
|
||||
.sales-toolbar #financialYearFilter + .select2-container .select2-selection__arrow { height: 25px !important; width: 20px; top: 1px; right: 6px; }
|
||||
.sales-toolbar #memberFilter + .select2-container .select2-selection__arrow b,
|
||||
.sales-toolbar #financialYearFilter + .select2-container .select2-selection__arrow b { border-width: 4px 3px 0 3px !important; margin-left: -3px; margin-top: -2px; }
|
||||
.sales-toolbar #memberFilter + .select2-container--open .select2-selection__arrow b,
|
||||
.sales-toolbar #financialYearFilter + .select2-container--open .select2-selection__arrow b { border-width: 0 3px 4px 3px !important; }
|
||||
.toolbar-icon-btn { width: 38px; height: 38px; padding: 0; display: inline-flex; align-items: center; justify-content: center; border: none; border-radius: 8px; color: #fff; cursor: pointer; transition: all 0.2s; }
|
||||
.toolbar-icon-btn:hover { transform: translateY(-1px); filter: brightness(0.96); }
|
||||
.toolbar-icon-btn:focus { outline: none; box-shadow: 0 0 0 2px rgba(2, 168, 181, 0.25); }
|
||||
@ -211,7 +219,7 @@
|
||||
<option value="<?= $sm['id'] ?>"><?= esc($sm['first_name']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<select id="financialYearFilter" class="toolbar-select financial-year-select" onchange="fetchActivities(false)">
|
||||
<select id="financialYearFilter" class="toolbar-select financial-year-select searchable" onchange="fetchActivities(false)">
|
||||
<?php foreach(($fin_years ?? []) as $year): ?>
|
||||
<option value="<?= esc($year) ?>" <?= (($current_fin_year ?? '') === $year) ? 'selected' : '' ?>><?= esc($year) ?></option>
|
||||
<?php endforeach; ?>
|
||||
|
||||
@ -104,6 +104,8 @@
|
||||
th { text-align: left; padding: 10px 12px; color: #8896aa; font-size: 11px; text-transform: uppercase; letter-spacing: 0.07em; border-bottom: 1px solid #edf2f7; background: white; font-weight: 700; }
|
||||
td { padding: 13px 12px; border-bottom: 1px solid #f4f6fb; font-size: 14px; vertical-align: middle; }
|
||||
tbody tr:hover { background: #fafbfe; }
|
||||
.won-opportunity-row { cursor: pointer; }
|
||||
.won-opportunity-row:hover { background: #eff6ff !important; }
|
||||
|
||||
/* ── Status Pills ── */
|
||||
.status-pill { padding: 4px 12px; border-radius: 20px; font-size: 11px; font-weight: 700; display: inline-block; }
|
||||
@ -528,11 +530,21 @@ const progColor= p => p >= 100 ? 'linear-gradient(90deg,#10b981,#34d399)' : p >=
|
||||
const SALES_LEADS_URL = '<?= base_url('sales') ?>';
|
||||
const SALES_ACTIVITIES_URL = '<?= base_url('sales/loadactivities') ?>';
|
||||
const SALES_TARGETS_URL = '<?= base_url('sales/targets') ?>';
|
||||
const OPPORTUNITY_DETAIL_BASE = '<?= base_url('/util/getLeadNonEB/') ?>';
|
||||
|
||||
function getSelectedDashboardFY() {
|
||||
return document.getElementById('financial_year')?.value || getCurrentFY();
|
||||
}
|
||||
|
||||
function redirectToOpportunity(leadTypeId, actualLeadId, opportunityId) {
|
||||
if (!leadTypeId || !actualLeadId || !opportunityId) return;
|
||||
|
||||
window.location.href = OPPORTUNITY_DETAIL_BASE
|
||||
+ encodeURIComponent(leadTypeId) + '/'
|
||||
+ encodeURIComponent(actualLeadId) + '/'
|
||||
+ encodeURIComponent(opportunityId);
|
||||
}
|
||||
|
||||
function goSalesPage(page, status, memberId) {
|
||||
const url = new URL(page === 'activities' ? SALES_ACTIVITIES_URL : SALES_LEADS_URL, window.location.origin);
|
||||
url.searchParams.set('fy', getSelectedDashboardFY());
|
||||
@ -933,7 +945,15 @@ function openModal(id) {
|
||||
const typeBadgeClass = l.lead_type === 'EB'
|
||||
? 'style="background:#eff6ff;color:#2563eb;padding:3px 10px;border-radius:99px;font-size:11px;font-weight:700;"'
|
||||
: 'style="background:#f0fdf4;color:#16a34a;padding:3px 10px;border-radius:99px;font-size:11px;font-weight:700;"';
|
||||
return '<tr>'
|
||||
const canRedirect = l.lead_type_id && l.actual_lead_id && l.opportunities_id;
|
||||
const rowAttrs = canRedirect
|
||||
? ' class="won-opportunity-row" title="Open opportunity" onclick=\'redirectToOpportunity('
|
||||
+ JSON.stringify(String(l.lead_type_id)) + ','
|
||||
+ JSON.stringify(String(l.actual_lead_id)) + ','
|
||||
+ JSON.stringify(String(l.opportunities_id)) + ')\''
|
||||
: '';
|
||||
|
||||
return '<tr' + rowAttrs + '>'
|
||||
+ '<td style="font-weight:700;color:#0f172a;">' + (l.company || '—') + '</td>'
|
||||
+ '<td><span ' + typeBadgeClass + '>' + (l.lead_type || '—') + '</span></td>'
|
||||
+ '<td style="font-size:12px;color:#64748b;">' + fmtCreatedAt(l.created_at) + '</td>'
|
||||
|
||||
@ -18,20 +18,28 @@
|
||||
/* Leads Grid */
|
||||
.lead-header { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; /* responsive */ gap: 15px; }
|
||||
.lead-actions { display: flex; align-items: center; gap: 4px; }
|
||||
.sales-toolbar { flex-wrap: nowrap; padding: 16px 30px; gap: 12px; overflow-x: auto; }
|
||||
.sales-toolbar .filter-tabs { padding: 0; flex: 1 1 auto; min-width: 0; overflow-x: auto; }
|
||||
.sales-toolbar .lead-actions { flex: 0 0 auto; flex-wrap: nowrap; }
|
||||
.sales-toolbar .search-input { width: 230px !important; }
|
||||
.sales-toolbar { flex-wrap: nowrap; padding: 16px 15px; gap: 12px; overflow-x: auto; }
|
||||
.sales-toolbar .filter-tabs { padding: 0; flex: 0 0 auto; min-width: max-content; overflow: visible; }
|
||||
.sales-toolbar .lead-actions { flex: 1 1 auto; min-width: 0; flex-wrap: nowrap; justify-content: flex-end; }
|
||||
.sales-toolbar .search-input { width: 230px !important; height: 38px; padding: 9px 12px; border: 1px solid #ddd !important; border-radius: 6px !important; flex: 0 0 230px; }
|
||||
.sales-toolbar .toolbar-select { width: 175px !important; min-width: 175px; padding: 9px 12px; }
|
||||
.sales-toolbar .toolbar-select + .select2-container { width: 175px !important; min-width: 175px; }
|
||||
.sales-toolbar .financial-year-select { width: 108px !important; min-width: 108px; }
|
||||
.sales-toolbar .financial-year-select { width: 122px !important; min-width: 122px; }
|
||||
.sales-toolbar .financial-year-select + .select2-container { width: 122px !important; min-width: 122px; flex: 0 0 122px; }
|
||||
.sales-toolbar .select2-container .select2-selection--single { height: 38px; border: 1px solid #ddd; border-radius: 6px; display: flex; align-items: center; }
|
||||
.sales-toolbar .select2-container--default .select2-selection--single .select2-selection__rendered { line-height: 36px; }
|
||||
.sales-toolbar .select2-container--default .select2-selection--single .select2-selection__arrow { height: 36px; }
|
||||
.sales-toolbar #memberFilter + .select2-container { width: 175px !important; min-width: 175px; flex: 0 0 175px; }
|
||||
.sales-toolbar #memberFilter + .select2-container .select2-selection--single { height: 38px !important; background: #fff !important; border: 1px solid #ddd !important; border-radius: 6px !important; display: flex; align-items: center; }
|
||||
.sales-toolbar #memberFilter + .select2-container .select2-selection__rendered { flex: 1; line-height: 36px !important; padding-left: 12px; padding-right: 28px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.sales-toolbar #memberFilter + .select2-container .select2-selection__arrow { height: 25px !important; width: 20px; top: 1px; right: 6px; }
|
||||
.sales-toolbar #memberFilter + .select2-container .select2-selection--single,
|
||||
.sales-toolbar #financialYearFilter + .select2-container .select2-selection--single { height: 38px !important; background: #fff !important; border: 1px solid #ddd !important; border-radius: 6px !important; display: flex; align-items: center; }
|
||||
.sales-toolbar #memberFilter + .select2-container .select2-selection__rendered,
|
||||
.sales-toolbar #financialYearFilter + .select2-container .select2-selection__rendered { flex: 1; line-height: 36px !important; padding-left: 12px; padding-right: 28px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.sales-toolbar #memberFilter + .select2-container .select2-selection__arrow,
|
||||
.sales-toolbar #financialYearFilter + .select2-container .select2-selection__arrow { height: 25px !important; width: 20px; top: 1px; right: 6px; }
|
||||
.sales-toolbar #memberFilter + .select2-container .select2-selection__arrow b,
|
||||
.sales-toolbar #financialYearFilter + .select2-container .select2-selection__arrow b { border-width: 4px 3px 0 3px !important; margin-left: -3px; margin-top: -2px; }
|
||||
.sales-toolbar #memberFilter + .select2-container--open .select2-selection__arrow b,
|
||||
.sales-toolbar #financialYearFilter + .select2-container--open .select2-selection__arrow b { border-width: 0 3px 4px 3px !important; }
|
||||
.toolbar-icon-btn { width: 38px; height: 38px; padding: 0; display: inline-flex; align-items: center; justify-content: center; border: none; border-radius: 8px; color: #fff; cursor: pointer; transition: all 0.2s; }
|
||||
.toolbar-icon-btn:hover { transform: translateY(-1px); filter: brightness(0.96); }
|
||||
.toolbar-icon-btn:focus { outline: none; box-shadow: 0 0 0 2px rgba(2, 168, 181, 0.25); }
|
||||
@ -187,7 +195,7 @@
|
||||
<option value="<?= $sm['id'] ?>"><?= esc($sm['first_name']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<select id="financialYearFilter" class="toolbar-select financial-year-select" onchange="fetchLeads(false)">
|
||||
<select id="financialYearFilter" class="toolbar-select financial-year-select searchable" onchange="fetchLeads(false)">
|
||||
<?php foreach(($fin_years ?? []) as $year): ?>
|
||||
<option value="<?= esc($year) ?>" <?= (($current_fin_year ?? '') === $year) ? 'selected' : '' ?>><?= esc($year) ?></option>
|
||||
<?php endforeach; ?>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user