update vehicle trips report to include trip duration and distance
This commit is contained in:
@ -176,11 +176,14 @@ class ReportsController extends Controller
|
|||||||
t.*,
|
t.*,
|
||||||
tm.total_mileage, total_trip,
|
tm.total_mileage, total_trip,
|
||||||
vt.name as type_name,
|
vt.name as type_name,
|
||||||
ROW_NUMBER() OVER (PARTITION BY t.id ORDER BY t.start) AS trip_id
|
ROW_NUMBER() OVER (PARTITION BY t.id ORDER BY t.start) AS trip_id,
|
||||||
|
tgt.latitude,
|
||||||
|
tgt.longitude
|
||||||
FROM trips t
|
FROM trips t
|
||||||
JOIN TotalMileage tm ON t.id = tm.id
|
JOIN TotalMileage tm ON t.id = tm.id
|
||||||
LEFT JOIN t_vehicles tvt ON tvt.id = t.vhc_id
|
LEFT JOIN t_vehicles tvt ON tvt.id = t.vhc_id
|
||||||
LEFT JOIN t_vehicles_cats vt ON vt.id = tvt.cat_id
|
LEFT JOIN t_vehicles_cats vt ON vt.id = tvt.cat_id
|
||||||
|
LEFT JOIN t_gps_tracks tgt ON t.vhc_id = tgt.vhc_id
|
||||||
WHERE
|
WHERE
|
||||||
t.start BETWEEN ? AND ?
|
t.start BETWEEN ? AND ?
|
||||||
and (? IS NULL or t.id = ?)
|
and (? IS NULL or t.id = ?)
|
||||||
|
|||||||
@ -25,8 +25,8 @@
|
|||||||
@if ($user_role == \App\Models\Users::ROLE_VENDOR || $user_role == \App\Models\Users::ROLE_ADMIN)
|
@if ($user_role == \App\Models\Users::ROLE_VENDOR || $user_role == \App\Models\Users::ROLE_ADMIN)
|
||||||
@can('vehicle.create')
|
@can('vehicle.create')
|
||||||
<!-- <div class="col text-end">
|
<!-- <div class="col text-end">
|
||||||
<button id="btnMdlNewVhc" class="btn btn-sm btn-danger">Add New Vehicle</button>
|
<button id="btnMdlNewVhc" class="btn btn-sm btn-danger">Add New Vehicle</button>
|
||||||
</div> -->
|
</div> -->
|
||||||
@endcan
|
@endcan
|
||||||
|
|
||||||
{{-- <div class="col-auto text-end ps-0">
|
{{-- <div class="col-auto text-end ps-0">
|
||||||
@ -117,6 +117,8 @@
|
|||||||
<th class="hidden">Date</th>
|
<th class="hidden">Date</th>
|
||||||
<th class="hidden">Hour Start</th>
|
<th class="hidden">Hour Start</th>
|
||||||
<th class="hidden">Hour Finish</th>
|
<th class="hidden">Hour Finish</th>
|
||||||
|
<th class="hidden">Latitude</th>
|
||||||
|
<th class="hidden">Longitude</th>
|
||||||
<th class="">Start</th>
|
<th class="">Start</th>
|
||||||
<th class="">Finish</th>
|
<th class="">Finish</th>
|
||||||
<th class="">Duration</th>
|
<th class="">Duration</th>
|
||||||
@ -129,73 +131,73 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<!-- <tbody>
|
<!-- <tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>24-679</td>
|
<td>24-679</td>
|
||||||
<td>24-679</td>
|
<td>24-679</td>
|
||||||
<td>26</td>
|
<td>26</td>
|
||||||
<td>442.3</td>
|
<td>442.3</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>24-682</td>
|
<td>24-682</td>
|
||||||
<td>24-682</td>
|
<td>24-682</td>
|
||||||
<td>28</td>
|
<td>28</td>
|
||||||
<td>312.7</td>
|
<td>312.7</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>24-877</td>
|
<td>24-877</td>
|
||||||
<td>24-877</td>
|
<td>24-877</td>
|
||||||
<td>15</td>
|
<td>15</td>
|
||||||
<td>276.4</td>
|
<td>276.4</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>A.16-347</td>
|
<td>A.16-347</td>
|
||||||
<td>A.16-347</td>
|
<td>A.16-347</td>
|
||||||
<td>37</td>
|
<td>37</td>
|
||||||
<td>543.2</td>
|
<td>543.2</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>AL2025-B21</td>
|
<td>AL2025-B21</td>
|
||||||
<td>AL2025-B21</td>
|
<td>AL2025-B21</td>
|
||||||
<td>18</td>
|
<td>18</td>
|
||||||
<td>295.4</td>
|
<td>295.4</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>B.16-542</td>
|
<td>B.16-542</td>
|
||||||
<td>B.16-542</td>
|
<td>B.16-542</td>
|
||||||
<td>32</td>
|
<td>32</td>
|
||||||
<td>478.2</td>
|
<td>478.2</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>B.24-264</td>
|
<td>B.24-264</td>
|
||||||
<td>B.24-264</td>
|
<td>B.24-264</td>
|
||||||
<td>24</td>
|
<td>24</td>
|
||||||
<td>188.4</td>
|
<td>188.4</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>B.24-274</td>
|
<td>B.24-274</td>
|
||||||
<td>B.24-274</td>
|
<td>B.24-274</td>
|
||||||
<td>33</td>
|
<td>33</td>
|
||||||
<td>440.1</td>
|
<td>440.1</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Ford Ranger</td>
|
<td>Ford Ranger</td>
|
||||||
<td>26-199</td>
|
<td>26-199</td>
|
||||||
<td>30</td>
|
<td>30</td>
|
||||||
<td>631.4</td>
|
<td>631.4</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Nissan AD</td>
|
<td>Nissan AD</td>
|
||||||
<td>B.10-517</td>
|
<td>B.10-517</td>
|
||||||
<td>27</td>
|
<td>27</td>
|
||||||
<td>375.9</td>
|
<td>375.9</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Triton</td>
|
<td>Triton</td>
|
||||||
<td>A.29-127</td>
|
<td>A.29-127</td>
|
||||||
<td>26</td>
|
<td>26</td>
|
||||||
<td>508.2</td>
|
<td>508.2</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody> -->
|
</tbody> -->
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -207,17 +209,17 @@
|
|||||||
<div class="modal fade" data-bs-backdrop="static" data-bs-keyboard="false" id="mdlDetailTrip"
|
<div class="modal fade" data-bs-backdrop="static" data-bs-keyboard="false" id="mdlDetailTrip"
|
||||||
aria-labelledby="mdlDetailTripLabel" aria-hidden="true">
|
aria-labelledby="mdlDetailTripLabel" aria-hidden="true">
|
||||||
<!-- <div class="modal-dialog modal-dialog modal-dialog-centered modal-dialog-scrollable modal-xl">
|
<!-- <div class="modal-dialog modal-dialog modal-dialog-centered modal-dialog-scrollable modal-xl">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title" id="mdlDetailTripLabel">-----</h5>
|
<h5 class="modal-title" id="mdlDetailTripLabel">-----</h5>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div> -->
|
</div> -->
|
||||||
</div>
|
</div>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
@ -291,6 +293,70 @@
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
// Data referensi site (lat/lng dalam desimal, bukan koma)
|
||||||
|
const SITE_LOCATIONS = [{
|
||||||
|
name: 'Bitterlaun',
|
||||||
|
district: 'Dili',
|
||||||
|
subdistrict: 'Cristo Rei',
|
||||||
|
village: 'Hera',
|
||||||
|
lat: -8.548328,
|
||||||
|
lng: 125.623808
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Bebonuk',
|
||||||
|
district: 'Dili',
|
||||||
|
subdistrict: 'Dom Aleixo',
|
||||||
|
village: 'Comoro',
|
||||||
|
lat: -8.547527,
|
||||||
|
lng: 125.548280
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Balide',
|
||||||
|
district: 'Dili',
|
||||||
|
subdistrict: 'Nain Feto',
|
||||||
|
village: 'Santa Cruz',
|
||||||
|
lat: -8.564396,
|
||||||
|
lng: 125.582151
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Radius toleransi "berdekatan" dalam meter — sesuaikan sesuai kebutuhan
|
||||||
|
const SITE_PROXIMITY_RADIUS_METERS = 150;
|
||||||
|
|
||||||
|
// Hitung jarak antara 2 koordinat (formula Haversine), hasil dalam meter
|
||||||
|
function getDistanceMeters(lat1, lon1, lat2, lon2) {
|
||||||
|
const R = 6371000; // radius bumi (meter)
|
||||||
|
const toRad = (deg) => deg * Math.PI / 180;
|
||||||
|
const dLat = toRad(lat2 - lat1);
|
||||||
|
const dLon = toRad(lon2 - lon1);
|
||||||
|
const a = Math.sin(dLat / 2) ** 2 +
|
||||||
|
Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) *
|
||||||
|
Math.sin(dLon / 2) ** 2;
|
||||||
|
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||||
|
return R * c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cari site terdekat dari koordinat yang diberikan; null jika tidak ada yang cukup dekat
|
||||||
|
function findNearestSite(lat, lng) {
|
||||||
|
if (lat === undefined || lng === undefined || lat === null || lng === null || lat === '' || lng === '') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let nearest = null;
|
||||||
|
let minDist = Infinity;
|
||||||
|
SITE_LOCATIONS.forEach(site => {
|
||||||
|
const dist = getDistanceMeters(parseFloat(lat), parseFloat(lng), site.lat, site.lng);
|
||||||
|
if (dist <= SITE_PROXIMITY_RADIUS_METERS && dist < minDist) {
|
||||||
|
minDist = dist;
|
||||||
|
nearest = site;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return nearest;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format label sesuai output yang diinginkan
|
||||||
|
function formatSiteLabel(site) {
|
||||||
|
return `Site ${site.name}, ${site.village}, ${site.subdistrict}, ${site.district}, Timor-Leste`;
|
||||||
|
}
|
||||||
|
|
||||||
const DTable = {
|
const DTable = {
|
||||||
table: null,
|
table: null,
|
||||||
@ -417,18 +483,38 @@
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// {
|
||||||
|
// data: 'finish',
|
||||||
|
// render: (data, type, row, meta) => {
|
||||||
|
// // saat export, hanya tampilkan lokasi tanpa tanggal/jam
|
||||||
|
// if (type === 'export') {
|
||||||
|
// return Helper.shortenText(decodeURIComponent(row.finishLoc ||
|
||||||
|
// 'address'), 255) || "-";
|
||||||
|
// }
|
||||||
|
// return `
|
||||||
|
// ${moment.unix(data + AppState.TIMEFIX).format('DD MMM YYYY HH:mm:ss')}<br>
|
||||||
|
// ${Helper.shortenText(decodeURIComponent(row.finishLoc || 'address'), 255) || "-"}
|
||||||
|
// `;
|
||||||
|
// }
|
||||||
|
// },
|
||||||
{
|
{
|
||||||
data: 'finish',
|
data: 'finish',
|
||||||
render: (data, type, row, meta) => {
|
render: (data, type, row, meta) => {
|
||||||
// saat export, hanya tampilkan lokasi tanpa tanggal/jam
|
// saat export, cek apakah koordinat finish berdekatan dengan site referensi
|
||||||
if (type === 'export') {
|
if (type === 'export') {
|
||||||
|
const nearestSite = findNearestSite(row.finishLat, row
|
||||||
|
.finishLng); // sesuaikan nama field lat/lng
|
||||||
|
if (nearestSite) {
|
||||||
|
return formatSiteLabel(nearestSite);
|
||||||
|
}
|
||||||
|
// default: tetap pakai alamat dari database
|
||||||
return Helper.shortenText(decodeURIComponent(row.finishLoc ||
|
return Helper.shortenText(decodeURIComponent(row.finishLoc ||
|
||||||
'address'), 255) || "-";
|
'address'), 255) || "-";
|
||||||
}
|
}
|
||||||
return `
|
return `
|
||||||
${moment.unix(data + AppState.TIMEFIX).format('DD MMM YYYY HH:mm:ss')}<br>
|
${moment.unix(data + AppState.TIMEFIX).format('DD MMM YYYY HH:mm:ss')}<br>
|
||||||
${Helper.shortenText(decodeURIComponent(row.finishLoc || 'address'), 255) || "-"}
|
${Helper.shortenText(decodeURIComponent(row.finishLoc || 'address'), 255) || "-"}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user