Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
M
mileage-claim
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
trainee
mileage-claim
Commits
b0d5b251
Commit
b0d5b251
authored
Oct 07, 2025
by
Hannah Zahra
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Second Update
parent
caf2415d
Show whitespace changes
Inline
Side-by-side
Showing
26 changed files
with
1207 additions
and
106 deletions
+1207
-106
app/Enums/Permission.php
+2
-6
app/Http/Controllers/Auth/Logout.php
+1
-1
app/Models/User.php
+20
-0
database/migrations/2025_09_22_042439_create_claims_table.php
+7
-0
database/migrations/2025_09_30_024339_create_project_user_table.php
+40
-0
packages/portal/mileage/resources/views/application/index.blade.php
+22
-4
packages/portal/mileage/resources/views/application/show.blade.php
+17
-7
packages/portal/mileage/resources/views/applyform/index.blade.php
+47
-11
packages/portal/mileage/resources/views/projectDirector/index.blade.php
+138
-0
packages/portal/mileage/resources/views/projectDirector/show.blade.php
+109
-0
packages/portal/mileage/resources/views/projectManager/index.blade.php
+134
-0
packages/portal/mileage/resources/views/projectManager/show.blade.php
+109
-0
packages/portal/mileage/routes/web.php
+23
-13
packages/portal/mileage/src/Http/Controllers/ApplicationsController.php
+132
-38
packages/portal/mileage/src/Http/Controllers/ApplyformController.php
+92
-12
packages/portal/mileage/src/Http/Controllers/ProjectDirectorController.php
+117
-0
packages/portal/mileage/src/Http/Controllers/ProjectManagerController.php
+69
-0
packages/portal/mileage/src/Model/AclRoleUser.php
+22
-0
packages/portal/mileage/src/Model/Aclrole.php
+14
-8
packages/portal/mileage/src/Model/Claim.php
+8
-1
packages/portal/mileage/src/Model/Project.php
+6
-0
packages/portal/mileage/src/Model/ProjectRole.php
+15
-0
packages/portal/mileage/src/Model/ProjectTeam.php
+33
-0
packages/portal/mileage/src/Model/Users.php
+18
-0
packages/portal/mileage/src/ServiceProvider.php
+11
-4
resources/views/vendor/ui/quick-switcher/sidebar.blade.php
+1
-1
No files found.
app/Enums/Permission.php
View file @
b0d5b251
...
...
@@ -16,12 +16,6 @@ final class Permission extends Enum
const
LULUS
=
'LULUS'
;
const
MAKLUMAT_CUTI
=
'MAKLUMAT_CUTI'
;
const
UPDATE_ACL
=
'UPDATE_ACL'
;
const
BATAL_CUTI
=
'BATAL_CUTI'
;
const
SETTING_EMAIL
=
'SETTING_EMAIL'
;
const
SETTING_PLAN
=
'SETTING_PLAN'
;
const
APPLY_PLAN
=
'APPLY_PLAN'
;
const
APPLY_GCR
=
'APPLY_GCR'
;
const
KUOTA
=
'KUOTA'
;
const
MANAGE_USER
=
'epicentrum::manage-user'
;
const
MANAGE_ROLE
=
'epicentrum::manage-role'
;
...
...
@@ -31,6 +25,8 @@ final class Permission extends Enum
const
APPLY_CLAIM
=
'APPLY_CLAIM'
;
const
HRVIEW
=
"HRVIEW"
;
const
TRY_PERMISSION
=
"TRY_PERMISSION"
;
const
PROJECT_MANAGER
=
"PROJECT_MANAGER"
;
const
PROJECT_DIRECTOR
=
"PROJECT_DIRECTOR"
;
//should added to in table:acl_permission
}
app/Http/Controllers/Auth/Logout.php
View file @
b0d5b251
...
...
@@ -14,6 +14,6 @@ public function __invoke(Request $request): RedirectResponse
$request
->
session
()
->
invalidate
();
$request
->
session
()
->
regenerateToken
();
return
redirect
(
'/'
);
return
redirect
(
'/
auth/login
'
);
}
}
app/Models/User.php
View file @
b0d5b251
...
...
@@ -2,6 +2,9 @@
namespace
App\Models
;
use
Portal\Mileage\Model\Project
;
use
Illuminate\Database\Eloquent\Relations\BelongsToMany
;
use
Illuminate\Database\Eloquent\Factories\HasFactory
;
use
Illuminate\Notifications\Notifiable
;
use
Laravolt\Suitable\AutoFilter
;
...
...
@@ -22,4 +25,21 @@ class User extends \Laravolt\Platform\Models\User
protected
$hidden
=
[
'password'
,
'remember_token'
];
protected
$fillable
=
[
'name'
,
'email'
,
'username'
,
'password'
,
'status'
,
'timezone'
];
public
function
managedProjects
()
:
BelongsToMany
{
return
$this
->
belongsToMany
(
Project
::
class
,
'project_team'
,
// pivot table
'pt_staff_id'
,
// user id column
'fk_project_id'
// project id column
)
->
withPivot
(
'role_id'
)
->
wherePivot
(
'role_id'
,
1
);
// filter only project managers
}
public
function
aclRoles
()
{
return
$this
->
hasMany
(
\Portal\Mileage\Model\AclRoleUser
::
class
,
'user_id'
,
'employeeCode'
);
}
}
database/migrations/2025_09_22_042439_create_claims_table.php
View file @
b0d5b251
...
...
@@ -12,6 +12,10 @@ public function up()
$table
->
id
();
$table
->
date
(
'claim_date'
);
// User FK
$table
->
unsignedBigInteger
(
'user_id'
)
->
nullable
();
// nullable if some claims don't have a user yet
$table
->
foreign
(
'user_id'
)
->
references
(
'id'
)
->
on
(
'users'
)
->
onDelete
(
'set null'
);
// Vehicle type FK
$table
->
unsignedBigInteger
(
'jenis_kenderaan_id'
);
$table
->
foreign
(
'jenis_kenderaan_id'
)
->
references
(
'id'
)
->
on
(
'lkp_jenis_kenderaans'
)
->
onDelete
(
'cascade'
);
...
...
@@ -37,6 +41,9 @@ public function up()
$table
->
string
(
'penalty_reason'
)
->
nullable
();
$table
->
decimal
(
'total_claim_amount'
,
10
,
2
)
->
default
(
0
);
$table
->
unsignedBigInteger
(
'rejected_by'
)
->
nullable
()
->
after
(
'total_claim_amount'
);
$table
->
foreign
(
'rejected_by'
)
->
references
(
'id'
)
->
on
(
'users'
)
->
onDelete
(
'set null'
);
// Claim status (workflow)
$table
->
enum
(
'status'
,
[
'Diproses'
,
// submitted by staff
...
...
database/migrations/2025_09_30_024339_create_project_user_table.php
0 → 100644
View file @
b0d5b251
<?php
use
Illuminate\Database\Migrations\Migration
;
use
Illuminate\Database\Schema\Blueprint
;
use
Illuminate\Support\Facades\Schema
;
return
new
class
extends
Migration
{
/**
* Run the migrations.
*
* @return void
*/
public
function
up
()
{
Schema
::
create
(
'project_user'
,
function
(
Blueprint
$table
)
{
$table
->
id
();
$table
->
unsignedBigInteger
(
'project_id'
);
$table
->
unsignedBigInteger
(
'user_id'
);
$table
->
unsignedInteger
(
'project_role_id'
);
$table
->
timestamps
();
$table
->
foreign
(
'project_id'
)
->
references
(
'id'
)
->
on
(
'projects'
)
->
onDelete
(
'cascade'
);
$table
->
foreign
(
'user_id'
)
->
references
(
'id'
)
->
on
(
'users'
)
->
onDelete
(
'cascade'
);
$table
->
foreign
(
'project_role_id'
)
->
references
(
'id'
)
->
on
(
'project_role'
)
->
onDelete
(
'restrict'
);
$table
->
unique
([
'project_id'
,
'user_id'
]);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public
function
down
()
{
Schema
::
dropIfExists
(
'project_user'
);
}
};
packages/portal/mileage/resources/views/application/index.blade.php
View file @
b0d5b251
<title>
Senarai Tuntutan
</title>
@extends('ui::layouts.app')
@section('content')
...
...
@@ -45,7 +46,7 @@
<div
id=
"loader"
><div></div></div>
<div
class=
"ui container"
>
<h2 class="
ui
header
">
All Claims
</h2>
<h2
class=
"ui header"
>
Senarai Tuntutan
</h2>
@if(session('success'))
<div
class=
"ui positive message"
>
...
...
@@ -53,6 +54,8 @@
</div>
@endif
@foreach($claimsByMonth as $month => $claims)
<h3
class=
"ui dividing header"
>
{{ $month }}
</h3>
<table
class=
"ui celled table"
>
<thead>
<tr>
...
...
@@ -60,7 +63,8 @@
<th>
Tarikh Tuntutan
</th>
<th>
Nama Projek
</th>
<th>
Jenis Kenderaan
</th>
<th>Jumlah Tuntutan (RM)</th>
<th>
Jumlah Perbatuan
</th>
<th>
Jumlah Tuntutan
</th>
<th>
Status
</th>
<th>
Tindakan
</th>
</tr>
...
...
@@ -72,12 +76,14 @@
<td>
{{ $claim->claim_date }}
</td>
<td>
{{ $claim->project->p_project_description ?? '-' }}
</td>
<td>
{{ $claim->jenisKenderaan->name ?? '-' }}
</td>
<td>
{{ $claim->total_mileage ?? 0 }} km
</td>
<td>
RM {{ number_format($claim->total_claim_amount, 2) }}
</td>
<td>
{{ $claim->status }}
</td>
<td>
<a
href=
"{{ route('mileage::applications.show', $claim->id) }}"
class=
"ui blue button tiny"
>
Lihat
</a>
@if($claim->status === 'Diproses')
<!-- Edit button (opens modal) -->
<button
class=
"ui yellow button tiny editBtn"
style=
"margin-bottom: 10px"
data-id=
"{{ $claim->id }}"
...
...
@@ -85,26 +91,30 @@
data-project=
"{{ $claim->project_id }}"
data-vehicle=
"{{ $claim->jenis_kenderaan_id }}"
data-description=
"{{ $claim->description }}"
data-distanceto=
"{{ $claim->distance_to }}"
data-mileage=
"{{ $claim->total_mileage }}"
data-toll=
"{{ $claim->toll_amount }}"
data-others=
"{{ $claim->others_amount }}"
data-othersdesc=
"{{ $claim->others_description }}"
>
Kemaskini
</button>
<span>
<!-- Delete button -->
<form
action=
"{{ route('mileage::applications.destroy', $claim->id) }}"
method=
"POST"
style=
"display:inline;"
>
@csrf
@method('DELETE')
<button
type=
"submit"
class=
"ui red button tiny"
onclick=
"return confirm('Are you sure you want to delete this claim?')"
>
Padam
</button>
</form>
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
@endforeach
</div>
<div
id=
"loader"
><div></div></div>
<!-- Edit Modal -->
<div
id=
"editModal"
class=
"ui modal"
>
<i
class=
"close icon"
></i>
...
...
@@ -145,6 +155,11 @@
</div>
<div
class=
"field"
>
<label>
Jarak Ke
</label>
<input
type=
"text"
name=
"distance_to"
id=
"editDistanceTo"
>
</div>
<div
class=
"field"
>
<label>
Jumlah Perbatuan (KM)
</label>
<input
type=
"number"
name=
"total_mileage"
id=
"editDistance"
>
</div>
...
...
@@ -164,7 +179,7 @@
<input
type=
"text"
name=
"others_description"
id=
"editOthersDescription"
>
</div>
<button type="
submit
" class="
ui
green
button
">
Update
</button>
<button
type=
"submit"
class=
"ui green button"
>
Kemaskini
</button>
</form>
</div>
</div>
...
...
@@ -196,6 +211,7 @@
let
project
=
$btn
.
data
(
'project'
);
let
vehicle
=
$btn
.
data
(
'vehicle'
);
let
description
=
$btn
.
data
(
'description'
);
let
distanceTo
=
$btn
.
data
(
'distanceto'
);
let
mileage
=
$btn
.
data
(
'mileage'
);
let
toll
=
$btn
.
data
(
'toll'
);
let
others
=
$btn
.
data
(
'others'
);
...
...
@@ -205,6 +221,7 @@
$
(
'#editId'
).
val
(
id
);
$
(
'#editDate'
).
val
(
date
||
''
);
$
(
'#editDescription'
).
val
(
description
||
''
);
$
(
'#editDistanceTo'
).
val
(
distanceTo
||
''
);
$
(
'#editDistance'
).
val
(
mileage
??
''
);
$
(
'#editToll'
).
val
(
toll
??
''
);
$
(
'#editOthers'
).
val
(
others
??
''
);
...
...
@@ -233,6 +250,7 @@
let
project
=
$
(
'#editProject'
).
val
();
let
vehicle
=
$
(
'#editVehicle'
).
val
();
let
description
=
$
(
'#editDescription'
).
val
();
let
distanceTo
=
$
(
'#editDistanceTo'
).
val
();
let
mileage
=
$
(
'#editDistance'
).
val
();
let
toll
=
$
(
'#editToll'
).
val
();
let
others
=
$
(
'#editOthers'
).
val
();
...
...
packages/portal/mileage/resources/views/application/show.blade.php
View file @
b0d5b251
...
...
@@ -69,21 +69,31 @@
</tr>
<tr>
<td>Penalti (RM) (Jika Ada)</td>
@if (
$claim->penalty_amount
> 0)
<td>RM {{ number_format(
$claim->penalty_amount
, 2) }}</td>
@elseif (Str::contains(
$claim->penalty_reason
, 'Budi Bicara')
)
<td><span style="
color
:
orange
;
">Tertakluk kepada BOD</span></td>
<td>
@if(isset(
$claim->display_penalty_amount
))
@if(
$claim->display_penalty_amount
> 0
)
RM {{ number_format(
$claim->display_penalty_amount
, 2) }}
@else
<td>RM 0.00</td>
@if(
$claim->display_penalty_reason
=== 'Tertakluk kepada BOD')
<span style="
color
:
orange
;
">{{
$claim->display_penalty_reason
}}</span>
@else
RM 0.00
@endif
@endif
@else
RM {{ number_format(
$claim->penalty_amount
?? 0, 2) }}
@endif
</td>
</tr>
<tr>
<td>Sebab Penalti</td>
<td>{{
$claim->penalty_reason
?? '-' }}</td>
<td>
{{
$claim->display_penalty_reason
??
$claim->penalty_reason
?? '-' }}
</td>
</tr>
<tr>
<td><strong>Jumlah Keseluruhan Tuntutan (RM)</strong></td>
<td><strong>RM {{ number_format(
$claim->total_claim_amount
?? 0, 2) }}</strong></td>
<td><strong>RM {{ number_format(
$claim->total_claim_amount
??
$claim->total_claim_amount
??
0, 2) }}</strong></td>
</tr>
<tr>
<td><strong>Status</strong></td>
...
...
packages/portal/mileage/resources/views/applyform/index.blade.php
View file @
b0d5b251
<title>
Form Tuntutan
</title>
@extends('ui::layouts.app')
@push('style')
...
...
@@ -95,7 +96,7 @@
</div>
<div
class=
"inline field required"
>
<label
class=
"three-wide field"
>
Nama Projek
<font
color=
"red"
>
*
</font></label>
<select class="
ui
search
dropdown
eight
wide
field
" name="
project_id
" id="
project_select
" required>
<select
class=
"ui search dropdown eight wide field
form-control
"
name=
"project_id"
id=
"project_select"
required
>
<option
value=
""
>
Sila Pilih Projek
</option>
@foreach($projects as $project)
<option
value=
"{{ $project->id }}"
>
{{ $project->p_project_description }}
</option>
...
...
@@ -110,7 +111,7 @@
<label
class=
"three-wide field"
>
Jarak Dari
</label>
<select
class=
"ui search dropdown eight wide field"
name=
"distance_from"
>
<option
value=
""
>
Sila Pilih Lokasi
</option>
<option value="
office
">Pejabat</option>
<option
value=
"
Pejabat
"
>
Pejabat
</option>
</select>
</div>
<div
class=
"inline field required"
>
...
...
@@ -139,7 +140,7 @@
{{-- Rumusan Tuntutan --}}
<div
class=
"ui segment"
>
<p>Rumusan Tuntutan</p>
<p>
Rumusan
Pengiraan
Tuntutan
</p>
<div
class=
"ui segment"
>
<table
class=
"ui celled table"
>
<tbody>
...
...
@@ -148,7 +149,7 @@
<td
id=
"claimable_mileage"
>
0 KM
</td>
</tr>
<tr>
<td>Jumlah Tuntutan Perjalanan
(RM)
</td>
<td>
Jumlah Tuntutan Perjalanan
<span
id=
"mileage_rate_label"
></span>
</td>
<td
id=
"mileage_claim_amount"
>
RM 0.00
</td>
</tr>
<tr>
...
...
@@ -177,11 +178,15 @@
</div>
<!-- Hidden inputs to send calculated values to server -->
<input
type=
"hidden"
name=
"user_id"
id=
"user_id_input"
value=
"{{ auth()->user()->id }}"
>
<!-- <input type="hidden" name="staff_id" id="staff_id_input" value="{{ auth()->user()->employeeCode }}"> -->
<input
type=
"hidden"
name=
"claimable_mileage"
id=
"claimable_mileage_input"
value=
"0"
>
<input
type=
"hidden"
name=
"mileage_claim_amount"
id=
"mileage_claim_amount_input"
value=
"0.00"
>
<input
type=
"hidden"
name=
"total_claim_amount"
id=
"total_claim_amount_input"
value=
"0.00"
>
<input
type=
"hidden"
name=
"toll_amount"
id=
"toll_amount_input"
value=
"0.00"
>
<input
type=
"hidden"
name=
"others_amount"
id=
"others_amount_input"
value=
"0.00"
>
<input
type=
"hidden"
name=
"penalty_amount"
id=
"penalty_amount_input"
value=
"0.00"
>
<input
type=
"hidden"
name=
"penalty_reason"
id=
"penalty_reason_input"
value=
"-"
>
<button
class=
"ui primary button blue"
type=
"submit"
id=
"btn_hantar"
>
Seterusnya
</button>
</form>
...
...
@@ -247,12 +252,38 @@ function calculateTotalClaim() {
if
(
vehicleRates
[
vehicleType
])
{
claimableMileage
=
Math
.
max
(
0
,
mileage
-
40
);
if
(
vehicleRates
[
vehicleType
].
type
===
'Kereta'
)
{
rate = (claimableMileage <= 500) ? vehicleRates[vehicleType].baseRate : vehicleRates[vehicleType].higherMileageRate;
if
(
claimableMileage
<=
500
)
{
mileageClaimAmount
=
claimableMileage
*
vehicleRates
[
vehicleType
].
baseRate
;
}
else
{
mileageClaimAmount
=
(
500
*
vehicleRates
[
vehicleType
].
baseRate
)
+
((
claimableMileage
-
500
)
*
vehicleRates
[
vehicleType
].
higherMileageRate
);
}
}
else
{
rate = vehicleRates[vehicleType].baseRate;
// Motosikal - flat rate
mileageClaimAmount
=
claimableMileage
*
vehicleRates
[
vehicleType
].
baseRate
;
}
}
let
rateLabel
=
""
;
if
(
vehicleRates
[
vehicleType
])
{
claimableMileage
=
Math
.
max
(
0
,
mileage
-
40
);
if
(
vehicleRates
[
vehicleType
].
type
===
'Kereta'
)
{
if
(
claimableMileage
<=
500
)
{
mileageClaimAmount
=
claimableMileage
*
vehicleRates
[
vehicleType
].
baseRate
;
rateLabel
=
`(RM
${
vehicleRates
[
vehicleType
].
baseRate
.
toFixed
(
2
)}
/km)`
;
}
else
{
mileageClaimAmount
=
(
500
*
vehicleRates
[
vehicleType
].
baseRate
)
+
((
claimableMileage
-
500
)
*
vehicleRates
[
vehicleType
].
higherMileageRate
);
rateLabel
=
`(RM
${
vehicleRates
[
vehicleType
].
baseRate
.
toFixed
(
2
)}
/km first 500km, RM
${
vehicleRates
[
vehicleType
].
higherMileageRate
.
toFixed
(
2
)}
/km after)`
;
}
}
else
{
mileageClaimAmount
=
claimableMileage
*
vehicleRates
[
vehicleType
].
baseRate
;
rateLabel
=
`(RM
${
vehicleRates
[
vehicleType
].
baseRate
.
toFixed
(
2
)}
/km)`
;
}
mileageClaimAmount = claimableMileage * rate;
}
// --- Penalty calculation ---
...
...
@@ -269,20 +300,22 @@ function calculateTotalClaim() {
if
(
monthsDiff
<=
1
)
{
penalty
=
0
;
penaltyReason
=
"-"
;
$
(
'#btn_hantar'
).
prop
(
'disabled'
,
false
);
}
else
if
(
monthsDiff
>
1
&&
monthsDiff
<=
3
)
{
penalty
=
mileageClaimAmount
*
0.10
;
penaltyReason
=
"Lewat hantar lebih 1 bulan tetapi ≤ 3 bulan (10%)"
;
$
(
'#btn_hantar'
).
prop
(
'disabled'
,
false
);
}
else
if
(
monthsDiff
>
3
&&
monthsDiff
<=
6
)
{
penalty
=
mileageClaimAmount
*
0.30
;
penaltyReason
=
"Lewat hantar lebih 3 bulan tetapi ≤ 6 bulan (30%)"
;
$
(
'#btn_hantar'
).
prop
(
'disabled'
,
false
);
}
else
if
(
monthsDiff
>
6
)
{
penaltyReason
=
"Lewat lebih 6 bulan - Tertakluk kepada budi bicara Lembaga Pengarah"
;
// prevent submission for >6 months
$
(
'#btn_hantar'
).
prop
(
'disabled'
,
true
);
showCustomAlert
(
"Tuntutan lebih daripada 6 bulan tidak boleh dihantar. Sila rujuk Lembaga Pengarah."
);
} else {
$('#btn_hantar').prop('disabled', false);
}
}
const
totalClaim
=
mileageClaimAmount
+
toll
+
others
-
penalty
;
...
...
@@ -290,6 +323,7 @@ function calculateTotalClaim() {
// Update visible summary
$
(
'#claimable_mileage'
).
text
(
claimableMileage
.
toFixed
(
2
)
+
' KM'
);
$
(
'#mileage_claim_amount'
).
text
(
'RM '
+
mileageClaimAmount
.
toFixed
(
2
));
$
(
'#mileage_rate_label'
).
text
(
rateLabel
);
$
(
'#toll_claim_amount'
).
text
(
'RM '
+
toll
.
toFixed
(
2
));
$
(
'#others_claim_amount'
).
text
(
'RM '
+
others
.
toFixed
(
2
));
$
(
'#penalty_claim_amount'
).
text
(
'RM '
+
penalty
.
toFixed
(
2
));
...
...
@@ -302,6 +336,8 @@ function calculateTotalClaim() {
$
(
'#toll_amount_input'
).
val
(
toll
.
toFixed
(
2
));
$
(
'#others_amount_input'
).
val
(
others
.
toFixed
(
2
));
$
(
'#total_claim_amount_input'
).
val
(
totalClaim
.
toFixed
(
2
));
$
(
'#penalty_amount_input'
).
val
(
penalty
.
toFixed
(
2
));
$
(
'#penalty_reason_input'
).
val
(
penaltyReason
);
}
...
...
@@ -346,7 +382,7 @@ function calculateTotalClaim() {
const
totalClaim
=
$
(
'#total_claim_amount_input'
).
val
()
||
'0.00'
;
const
summaryHtml
=
`
<div class="
ui
mini
modal
" id="
claimSummaryModal
">
<div class="ui
large
modal" id="claimSummaryModal">
<div class="header">Ringkasan Tuntutan Anda</div>
<div class="content">
<p><strong>Tarikh:</strong>
${
claimDate
}
</p>
...
...
packages/portal/mileage/resources/views/projectDirector/index.blade.php
0 → 100644
View file @
b0d5b251
<title>
Project Director Dashboard
</title>
@extends('ui::layouts.app')
@section('content')
<style
type=
"text/css"
>
body
{
background-color
:
#f0f2f5
;
}
.ui.container
{
max-width
:
100%
!important
;
overflow-x
:
hidden
;
padding
:
0
5rem
0
2rem
;
box-sizing
:
border-box
;
}
.ui.segments
{
margin-top
:
2rem
;
}
.ui.segment
{
box-shadow
:
0
4px
6px
rgba
(
0
,
0
,
0
,
0.1
);
}
@keyframes
spin
{
to
{
transform
:
rotate
(
360deg
);
}
}
#loader
{
position
:
fixed
;
top
:
0
;
left
:
0
;
width
:
100%
;
height
:
100%
;
display
:
none
;
align-items
:
center
;
justify-content
:
center
;
background
:
rgba
(
0
,
0
,
0
,
0.5
);
z-index
:
9999
;
}
#loader
div
{
margin-top
:
20%
;
margin-left
:
45%
;
width
:
60px
;
height
:
60px
;
border
:
6px
solid
#fff
;
border-top
:
6px
solid
transparent
;
border-radius
:
50%
;
animation
:
spin
1s
linear
infinite
;
}
</style>
<div
class=
"ui container mt-5"
>
<h2
class=
"ui header"
>
Project Director Dashboard
</h2>
{{-- Pending Claims --}}
<h3
class=
"ui dividing header"
>
Pending Claims
</h3>
<table
class=
"ui celled table"
>
<thead>
<tr>
<th>
#
</th>
<th>
Staff
</th>
<th>
Date
</th>
<th>
Project
</th>
<th>
Mileage
</th>
<th>
Total
</th>
<th>
Status
</th>
<th>
Action
</th>
</tr>
</thead>
<tbody>
@php
$pendingClaims = $claims->where('status', 'Disokong');
@endphp
@forelse($pendingClaims as $claim)
<tr>
<td>
{{ $loop->iteration }}
</td>
<td>
{{ $claim->user->name ?? '-' }}
</td>
<td>
{{ $claim->claim_date }}
</td>
<td>
{{ $claim->project->p_project_description ?? '-' }}
</td>
<td>
{{ $claim->total_mileage }} km
</td>
<td>
RM {{ number_format($claim->total_claim_amount, 2) }}
</td>
<td>
{{ $claim->status }}
</td>
<td>
<a
href=
"{{ route('mileage::projectDirector.show', $claim->id) }}"
class=
"ui blue button tiny"
>
Lihat
</a>
<form
action=
"{{ route('mileage::projectDirector.approve', $claim->id) }}"
method=
"POST"
style=
"display:inline;"
>
@csrf
<button
class=
"ui green button tiny"
type=
"submit"
>
Approve
</button>
</form>
<form
action=
"{{ route('mileage::projectDirector.reject', $claim->id) }}"
method=
"POST"
style=
"display:inline;"
>
@csrf
<button
class=
"ui red button tiny"
type=
"submit"
>
Reject
</button>
</form>
</td>
</tr>
@empty
<tr>
<td
colspan=
"8"
class=
"center aligned"
>
No pending claims
</td>
</tr>
@endforelse
</tbody>
</table>
{{-- Previous Claims --}}
<h3
class=
"ui dividing header mt-5"
>
Previous Claims
</h3>
<table
class=
"ui celled table"
>
<thead>
<tr>
<th>
#
</th>
<th>
Staff
</th>
<th>
Date
</th>
<th>
Project
</th>
<th>
Mileage
</th>
<th>
Total
</th>
<th>
Status
</th>
</tr>
</thead>
<tbody>
@php
$previousClaims = $claims->whereIn('status', ['Disahkan', 'Ditolak', 'Diluluskan']);
@endphp
@forelse($previousClaims as $claim)
<tr>
<td>
{{ $loop->iteration }}
</td>
<td>
{{ $claim->user->name ?? '-' }}
</td>
<td>
{{ $claim->claim_date }}
</td>
<td>
{{ $claim->project->p_project_description ?? '-' }}
</td>
<td>
{{ $claim->total_mileage }} km
</td>
<td>
RM {{ number_format($claim->total_claim_amount, 2) }}
</td>
<td>
{{ $claim->status }}
</td>
</tr>
@empty
<tr>
<td
colspan=
"7"
class=
"center aligned"
>
No previous claims found
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@endsection
packages/portal/mileage/resources/views/projectDirector/show.blade.php
0 → 100644
View file @
b0d5b251
@
extends
(
'ui::layouts.app'
)
@
section
(
'content'
)
<
style
>
body
{
background
-
color
:
#f0f2f5;
}
.
ui
.
container
{
max
-
width
:
70
%
!
important
;
margin
-
top
:
2
rem
;
}
.
ui
.
segment
{
box
-
shadow
:
0
4
px
6
px
rgba
(
0
,
0
,
0
,
0.1
);
}
</
style
>
<
div
class
="
ui
container
">
<h2 class="
ui
header
">Maklumat Tuntutan</h2>
<div class="
ui
segment
">
<table class="
ui
definition
table
">
<tr>
<td>Tarikh Tuntutan</td>
<td>{{
$claim->claim_date
}}</td>
</tr>
<tr>
<td>Jenis Kenderaan</td>
<td>{{
$claim->jenisKenderaan
->name ?? '-' }}</td>
</tr>
<tr>
<td>Nama Projek</td>
<td>{{
$claim->project
->p_project_description ?? '-' }}</td>
</tr>
<tr>
<td>Butiran Tuntutan</td>
<td>{{
$claim->description
?? '-' }}</td>
</tr>
<tr>
<td>Jarak Dari</td>
<td>{{
$claim->distance_from
?? '-' }}</td>
</tr>
<tr>
<td>Jarak Ke</td>
<td>{{
$claim->distance_to
?? '-' }}</td>
</tr>
<tr>
<td>Jumlah Perbatuan (KM)</td>
<td>{{
$claim->total_mileage
}} km</td>
</tr>
<tr>
<td>Jumlah Perbatuan Layak Tuntut</td>
<td>{{
$claim->claimable_mileage
?? 0 }} km</td>
</tr>
<tr>
<td>Jumlah Tuntutan Perjalanan (RM)</td>
<td>RM {{ number_format(
$claim->calculated_amount
?? 0, 2) }}</td>
</tr>
<tr>
<td>Toll (RM)</td>
<td>RM {{ number_format(
$claim->toll_amount
?? 0, 2) }}</td>
</tr>
<tr>
<td>Lain-lain (RM)</td>
<td>RM {{ number_format(
$claim->others_amount
?? 0, 2) }}</td>
</tr>
<tr>
<td>Butiran Lain-lain</td>
<td>{{
$claim->others_description
?? '-' }}</td>
</tr>
<tr>
<td>Penalti (RM) (Jika Ada)</td>
<td>
@if(isset(
$claim->display_penalty_amount
))
@if(
$claim->display_penalty_amount
> 0)
RM {{ number_format(
$claim->display_penalty_amount
, 2) }}
@else
@if(
$claim->display_penalty_reason
=== 'Tertakluk kepada BOD')
<span style="
color
:
orange
;
">{{
$claim->display_penalty_reason
}}</span>
@else
RM 0.00
@endif
@endif
@else
RM {{ number_format(
$claim->penalty_amount
?? 0, 2) }}
@endif
</td>
</tr>
<tr>
<td>Sebab Penalti</td>
<td>
{{
$claim->display_penalty_reason
??
$claim->penalty_reason
?? '-' }}
</td>
</tr>
<tr>
<td><strong>Jumlah Keseluruhan Tuntutan (RM)</strong></td>
<td><strong>RM {{ number_format(
$claim->total_claim_amount
??
$claim->total_claim_amount
?? 0, 2) }}</strong></td>
</tr>
<tr>
<td><strong>Status</strong></td>
<td><strong>{{
$claim->status
}}</strong></td>
</tr>
</table>
</div>
<a href="
{{
route
(
'mileage::projectDirector.index'
)
}}
" class="
ui
button
">
Kembali
</a>
</div>
@endsection
packages/portal/mileage/resources/views/projectManager/index.blade.php
0 → 100644
View file @
b0d5b251
<title>
Project Manager Dashboard
</title>
@extends('ui::layouts.app')
@section('content')
<style
type=
"text/css"
>
body
{
background-color
:
#f0f2f5
;
}
.ui.container
{
max-width
:
100%
!important
;
overflow-x
:
hidden
;
padding
:
0
5rem
0
2rem
;
box-sizing
:
border-box
;
}
.ui.segments
{
margin-top
:
2rem
;
}
.ui.segment
{
box-shadow
:
0
4px
6px
rgba
(
0
,
0
,
0
,
0.1
);
}
@keyframes
spin
{
to
{
transform
:
rotate
(
360deg
);
}
}
#loader
{
position
:
fixed
;
top
:
0
;
left
:
0
;
width
:
100%
;
height
:
100%
;
display
:
none
;
align-items
:
center
;
justify-content
:
center
;
background
:
rgba
(
0
,
0
,
0
,
0.5
);
z-index
:
9999
;
}
#loader
div
{
margin-top
:
20%
;
margin-left
:
45%
;
width
:
60px
;
height
:
60px
;
border
:
6px
solid
#fff
;
border-top
:
6px
solid
transparent
;
border-radius
:
50%
;
animation
:
spin
1s
linear
infinite
;
}
</style>
<div
id=
"loader"
><div></div></div>
<div
class=
"ui container"
>
<h2
class=
"ui header"
>
Project Manager Dashboard
</h2>
{{-- Pending Claims --}}
<h3
class=
"ui dividing header"
>
Pending Claims
</h3>
<table
class=
"ui celled table"
>
<thead>
<tr>
<th>
#
</th>
<th>
Staff
</th>
<th>
Date
</th>
<th>
Project
</th>
<th>
Distance
</th>
<th>
Total
</th>
<th>
Status
</th>
<th>
Action
</th>
</tr>
</thead>
<tbody>
@forelse($claims->where('status', 'Diproses') as $claim)
<tr>
<td>
{{ $loop->iteration }}
</td>
<td>
{{ $claim->user->name ?? '-' }}
</td>
<td>
{{ $claim->claim_date }}
</td>
<td>
{{ $claim->project->p_project_description ?? '-' }}
</td>
<td>
{{ $claim->total_mileage }} km
</td>
<td>
RM {{ number_format($claim->total_claim_amount, 2) }}
</td>
<td>
{{ $claim->status }}
</td>
<td>
<a
href=
"{{ route('mileage::projectManager.show', $claim->id) }}"
class=
"ui blue button tiny"
>
Lihat
</a>
<form
action=
"{{ route('mileage::applications.updateStatus', $claim->id) }}"
method=
"POST"
style=
"display:inline;"
>
@csrf
@method('PUT')
<input
type=
"hidden"
name=
"action"
value=
"verify"
>
<button
class=
"ui green button tiny"
type=
"submit"
>
Verify
</button>
</form>
<form
action=
"{{ route('mileage::applications.updateStatus', $claim->id) }}"
method=
"POST"
style=
"display:inline;"
>
@csrf
@method('PUT')
<input
type=
"hidden"
name=
"action"
value=
"reject"
>
<button
class=
"ui red button tiny"
type=
"submit"
>
Reject
</button>
</form>
</td>
</tr>
@empty
<tr>
<td
colspan=
"7"
>
No pending claims.
</td>
</tr>
@endforelse
</tbody>
</table>
{{-- Previous Claims --}}
<h3
class=
"ui dividing header"
>
Previous Claims
</h3>
<table
class=
"ui celled table"
>
<thead>
<tr>
<th>
#
</th>
<th>
Staff
</th>
<th>
Project
</th>
<th>
Distance
</th>
<th>
Total
</th>
<th>
Status
</th>
</tr>
</thead>
<tbody>
@forelse($claims->where('status', '!=', 'Diproses') as $claim)
<tr>
<td>
{{ $loop->iteration }}
</td>
<td>
{{ $claim->user->name ?? '-' }}
</td>
<td>
{{ $claim->project->p_project_description ?? '-' }}
</td>
<td>
{{ $claim->total_mileage }} km
</td>
<td>
RM {{ number_format($claim->total_claim_amount, 2) }}
</td>
<td>
{{ $claim->status }}
</td>
</tr>
@empty
<tr>
<td
colspan=
"6"
>
No previous claims.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@endsection
packages/portal/mileage/resources/views/projectManager/show.blade.php
0 → 100644
View file @
b0d5b251
@
extends
(
'ui::layouts.app'
)
@
section
(
'content'
)
<
style
>
body
{
background
-
color
:
#f0f2f5;
}
.
ui
.
container
{
max
-
width
:
70
%
!
important
;
margin
-
top
:
2
rem
;
}
.
ui
.
segment
{
box
-
shadow
:
0
4
px
6
px
rgba
(
0
,
0
,
0
,
0.1
);
}
</
style
>
<
div
class
="
ui
container
">
<h2 class="
ui
header
">Maklumat Tuntutan</h2>
<div class="
ui
segment
">
<table class="
ui
definition
table
">
<tr>
<td>Tarikh Tuntutan</td>
<td>{{
$claim->claim_date
}}</td>
</tr>
<tr>
<td>Jenis Kenderaan</td>
<td>{{
$claim->jenisKenderaan
->name ?? '-' }}</td>
</tr>
<tr>
<td>Nama Projek</td>
<td>{{
$claim->project
->p_project_description ?? '-' }}</td>
</tr>
<tr>
<td>Butiran Tuntutan</td>
<td>{{
$claim->description
?? '-' }}</td>
</tr>
<tr>
<td>Jarak Dari</td>
<td>{{
$claim->distance_from
?? '-' }}</td>
</tr>
<tr>
<td>Jarak Ke</td>
<td>{{
$claim->distance_to
?? '-' }}</td>
</tr>
<tr>
<td>Jumlah Perbatuan (KM)</td>
<td>{{
$claim->total_mileage
}} km</td>
</tr>
<tr>
<td>Jumlah Perbatuan Layak Tuntut</td>
<td>{{
$claim->claimable_mileage
?? 0 }} km</td>
</tr>
<tr>
<td>Jumlah Tuntutan Perjalanan (RM)</td>
<td>RM {{ number_format(
$claim->calculated_amount
?? 0, 2) }}</td>
</tr>
<tr>
<td>Toll (RM)</td>
<td>RM {{ number_format(
$claim->toll_amount
?? 0, 2) }}</td>
</tr>
<tr>
<td>Lain-lain (RM)</td>
<td>RM {{ number_format(
$claim->others_amount
?? 0, 2) }}</td>
</tr>
<tr>
<td>Butiran Lain-lain</td>
<td>{{
$claim->others_description
?? '-' }}</td>
</tr>
<tr>
<td>Penalti (RM) (Jika Ada)</td>
<td>
@if(isset(
$claim->display_penalty_amount
))
@if(
$claim->display_penalty_amount
> 0)
RM {{ number_format(
$claim->display_penalty_amount
, 2) }}
@else
@if(
$claim->display_penalty_reason
=== 'Tertakluk kepada BOD')
<span style="
color
:
orange
;
">{{
$claim->display_penalty_reason
}}</span>
@else
RM 0.00
@endif
@endif
@else
RM {{ number_format(
$claim->penalty_amount
?? 0, 2) }}
@endif
</td>
</tr>
<tr>
<td>Sebab Penalti</td>
<td>
{{
$claim->display_penalty_reason
??
$claim->penalty_reason
?? '-' }}
</td>
</tr>
<tr>
<td><strong>Jumlah Keseluruhan Tuntutan (RM)</strong></td>
<td><strong>RM {{ number_format(
$claim->total_claim_amount
??
$claim->total_claim_amount
?? 0, 2) }}</strong></td>
</tr>
<tr>
<td><strong>Status</strong></td>
<td><strong>{{
$claim->status
}}</strong></td>
</tr>
</table>
</div>
<a href="
{{
route
(
'mileage::projectManager.index'
)
}}
" class="
ui
button
">
Kembali
</a>
</div>
@endsection
packages/portal/mileage/routes/web.php
View file @
b0d5b251
...
...
@@ -3,38 +3,48 @@
use
Illuminate\Support\Facades\Route
;
use
Portal\Mileage\Http\Controllers\ApplyformController
;
use
Portal\Mileage\Http\Controllers\ApplicationsController
;
use
Portal\Mileage\Http\Controllers\ProjectManagerController
;
use
Portal\Mileage\Http\Controllers\ProjectDirectorController
;
Route
::
group
(
[
'namespace'
=>
'\Portal\Mileage\Http\Controllers'
,
'prefix'
=>
''
,
'as'
=>
'mileage::'
,
'middleware'
=>
[
'web'
,
'auth'
],
],
function
()
{
//
resource for a
pplyform
//
A
pplyform
Route
::
resource
(
'applyform'
,
ApplyformController
::
class
);
//
resource for application
//
Applications
Route
::
resource
(
'applications'
,
ApplicationsController
::
class
);
Route
::
put
(
'applications/{id}/updateStatus'
,
[
ApplicationsController
::
class
,
'updateStatus'
])
->
name
(
'applications.updateStatus'
);
// Project Manager
Route
::
prefix
(
'projectManager'
)
->
name
(
'projectManager.'
)
->
group
(
function
()
{
Route
::
get
(
'/'
,
[
ProjectManagerController
::
class
,
'index'
])
->
name
(
'index'
);
Route
::
get
(
'/show/{id}'
,
[
ProjectManagerController
::
class
,
'show'
])
->
name
(
'show'
);
});
// Project Director
Route
::
prefix
(
'projectDirector'
)
->
name
(
'projectDirector.'
)
->
group
(
function
()
{
Route
::
get
(
'/dashboard'
,
[
ProjectDirectorController
::
class
,
'index'
])
->
name
(
'index'
);
Route
::
get
(
'/show/{id}'
,
[
ProjectDirectorController
::
class
,
'show'
])
->
name
(
'show'
);
Route
::
post
(
'/{id}/approve'
,
[
ProjectDirectorController
::
class
,
'approve'
])
->
name
(
'approve'
);
Route
::
post
(
'/{id}/reject'
,
[
ProjectDirectorController
::
class
,
'reject'
])
->
name
(
'reject'
);
});
}
);
Route
::
group
(
// Guest group (currently unused)
Route
::
group
(
[
'namespace'
=>
'\Portal\Mileage\Http\Controllers'
,
'prefix'
=>
''
,
'as'
=>
'mileage::'
,
'middleware'
=>
[
'guest'
],
],
function
()
{
//
}
);
packages/portal/mileage/src/Http/Controllers/ApplicationsController.php
View file @
b0d5b251
...
...
@@ -20,17 +20,34 @@ public function __construct()
public
function
index
()
{
// Show all claims
$claims
=
Claim
::
with
([
'project'
,
'jenisKenderaan'
])
->
latest
()
->
get
();
$user
=
auth
()
->
user
();
// Base query with relationships
$query
=
Claim
::
with
([
'project'
,
'jenisKenderaan'
])
->
latest
();
// Restrict for Project Manager
if
(
$user
->
managedProjects
()
->
exists
())
{
$projectIds
=
$user
->
managedProjects
()
->
pluck
(
'fk_project_id'
);
$query
->
whereIn
(
'project_id'
,
$projectIds
);
}
$claims
=
$query
->
get
();
// Group by month-year (example: "2025-10")
$claimsByMonth
=
$claims
->
groupBy
(
function
(
$claim
)
{
return
\Carbon\Carbon
::
parse
(
$claim
->
claim_date
)
->
format
(
'F Y'
);
// "October 2025"
});
$projects
=
Project
::
all
();
$jenisKenderaan
=
JenisKenderaan
::
all
();
return
view
(
'mileage::application.index'
,
compact
(
'claims'
,
'projects'
,
'jenisKenderaan'
));
return
view
(
'mileage::application.index'
,
compact
(
'claims
ByMonth
'
,
'projects'
,
'jenisKenderaan'
));
}
public
function
store
(
Request
$request
)
{
$validated
=
$request
->
validate
([
'user_id'
=>
'required'
,
'claim_date'
=>
'required|date|before_or_equal:today'
,
'jenis_kenderaan_id'
=>
'required|exists:lkp_jenis_kenderaans,id'
,
'project_id'
=>
'required|exists:lkp_project,id'
,
...
...
@@ -59,31 +76,35 @@ public function store(Request $request)
$validated
[
'calculated_amount'
]
=
$calculatedAmount
;
$validated
[
'claimable_mileage'
]
=
$claimableMileage
;
// penalty
// penalty
calculation using DAYS (submission time = now)
$claimDate
=
Carbon
::
parse
(
$validated
[
'claim_date'
]);
$monthsDiff
=
$claimDate
->
diffInMonths
(
Carbon
::
now
());
$submissionDate
=
Carbon
::
now
();
$daysDiff
=
$claimDate
->
diffInDays
(
$submissionDate
);
if
(
$
monthsDiff
<=
3
)
{
if
(
$
daysDiff
<=
30
)
{
$penalty
=
0
;
$penaltyReason
=
'Tiada penalti (≤
3
bulan)'
;
}
elseif
(
$
monthsDiff
<=
6
)
{
$penaltyReason
=
'Tiada penalti (≤
1
bulan)'
;
}
elseif
(
$
daysDiff
<=
90
)
{
// >30 && <=90
$penalty
=
$calculatedAmount
*
0.10
;
$penaltyReason
=
'Lewat hantar
≤ 6
bulan (10%)'
;
}
else
{
$penaltyReason
=
'Lewat hantar
lebih 1 bulan tetapi ≤ 3
bulan (10%)'
;
}
else
if
(
$daysDiff
<=
180
)
{
// >90 && <=180
$penalty
=
$calculatedAmount
*
0.30
;
$penaltyReason
=
'Lewat hantar > 6 bulan (30%)'
;
$penaltyReason
=
'Lewat hantar lebih 3 bulan tetapi ≤ 6 bulan (30%)'
;
}
else
{
// >180
$penalty
=
0
;
// no numeric penalty applied automatically
$penaltyReason
=
'Lewat lebih 6 bulan - Tertakluk kepada budi bicara BOD'
;
}
$validated
[
'penalty_amount'
]
=
$penalty
;
$validated
[
'penalty_reason'
]
=
$penaltyReason
;
$validated
[
'total_claim_amount'
]
=
$calculatedAmount
-
$penalty
;
$validated
[
'status'
]
=
'Diproses'
;
// default status when first submitted
// default status
$validated
[
'status'
]
=
'Diproses'
;
//
IMPORTANT: assign created model to $claim so we can return/use it
//
create
$claim
=
Claim
::
create
(
$validated
);
// If front-end sent AJAX and expects JSON with claim data:
if
(
$request
->
ajax
())
{
return
response
()
->
json
([
'success'
=>
true
,
...
...
@@ -107,26 +128,26 @@ public function edit($id)
public
function
update
(
Request
$request
,
$id
)
{
$claim
=
Claim
::
findOrFail
(
$id
);
// decide new status based on role (if you want automated transitions)
$newStatus
=
null
;
if
(
auth
()
->
check
())
{
if
(
auth
()
->
user
()
->
hasRole
(
'Project Manager'
))
{
$claim
->
status
=
'Disokong'
;
$newStatus
=
'Disokong'
;
}
elseif
(
auth
()
->
user
()
->
hasRole
(
'HOD'
))
{
$newStatus
=
'Disokong'
;
}
elseif
(
auth
()
->
user
()
->
hasRole
(
'Project Director'
))
{
$newStatus
=
'Disahkan'
;
}
elseif
(
auth
()
->
user
()
->
hasRole
(
'Finance'
))
{
$newStatus
=
'Disemak'
;
}
elseif
(
auth
()
->
user
()
->
hasRole
(
'Finance Director'
))
{
$newStatus
=
'Diluluskan'
;
}
elseif
(
auth
()
->
user
()
->
hasRole
(
'HOD'
))
{
$claim
->
status
=
'Disokong'
;
}
elseif
(
auth
()
->
user
()
->
hasRole
(
'Project Director'
))
{
$claim
->
status
=
'Disahkan'
;
}
elseif
(
auth
()
->
user
()
->
hasRole
(
'Finance'
))
{
$claim
->
status
=
'Disemak'
;
}
elseif
(
auth
()
->
user
()
->
hasRole
(
'Finance Director'
))
{
$claim
->
status
=
'Diluluskan'
;
}
$validated
=
$request
->
validate
([
'user_id'
=>
'required'
,
'claim_date'
=>
'required|date'
,
'jenis_kenderaan_id'
=>
'required|exists:lkp_jenis_kenderaans,id'
,
'project_id'
=>
'required|exists:lkp_project,id'
,
...
...
@@ -155,31 +176,35 @@ public function update(Request $request, $id)
$validated
[
'calculated_amount'
]
=
$calculatedAmount
;
$validated
[
'claimable_mileage'
]
=
$claimableMileage
;
// penalty
// penalty
calculation using DAYS (submission time = now)
$claimDate
=
Carbon
::
parse
(
$validated
[
'claim_date'
]);
$
now
=
Carbon
::
now
();
$
monthsDiff
=
$claimDate
->
diffInMonths
(
$now
);
$
submissionDate
=
Carbon
::
now
();
$
daysDiff
=
$claimDate
->
diffInDays
(
$submissionDate
);
if
(
$
monthsDiff
<
1
)
{
if
(
$
daysDiff
<=
30
)
{
$penalty
=
0
;
$penaltyReason
=
'Tiada penalti (≤ 1 bulan)'
;
}
elseif
(
$
monthsDiff
<=
3
)
{
}
elseif
(
$
daysDiff
<=
90
)
{
$penalty
=
$calculatedAmount
*
0.10
;
$penaltyReason
=
'Lewat hantar >
1 bulan & ≤ 3 bulan (10% Penalti
)'
;
}
elseif
(
$
monthsDiff
<=
6
)
{
$penaltyReason
=
'Lewat hantar >
1 ≤3 bulan (10%
)'
;
}
elseif
(
$
daysDiff
<=
180
)
{
$penalty
=
$calculatedAmount
*
0.30
;
$penaltyReason
=
'Lewat hantar >
3 bulan & ≤ 6 bulan (30% Penalti
)'
;
$penaltyReason
=
'Lewat hantar >
3 ≤6 bulan (30%
)'
;
}
else
{
$penalty
=
0
;
$penaltyReason
=
'Melebihi 6 bulan (Tertakluk kepada budi bicara Lembaga Pengarah)'
;
// Optionally: $validated['status'] = 'Untuk Kelulusan BOD';
$penaltyReason
=
'Tertakluk kepada BOD'
;
}
$validated
[
'penalty_amount'
]
=
$penalty
;
$validated
[
'penalty_reason'
]
=
$penaltyReason
;
$validated
[
'total_claim_amount'
]
=
$calculatedAmount
-
$penalty
;
// update the model
// apply automated status change if applicable
if
(
$newStatus
)
{
$validated
[
'status'
]
=
$newStatus
;
}
// update
$claim
->
update
(
$validated
);
if
(
$request
->
ajax
())
{
...
...
@@ -203,8 +228,77 @@ public function destroy($id)
public
function
show
(
$id
)
{
// load claim
$claim
=
Claim
::
with
([
'project'
,
'jenisKenderaan'
])
->
findOrFail
(
$id
);
// Recompute penalty for display based on actual submission date (created_at)
// so show page always displays correct reason even if saved earlier incorrectly.
$claimDate
=
Carbon
::
parse
(
$claim
->
claim_date
);
$submissionDate
=
$claim
->
created_at
??
Carbon
::
now
();
$daysDiff
=
$claimDate
->
diffInDays
(
$submissionDate
);
$calculatedAmount
=
$claim
->
calculated_amount
??
0
;
if
(
$daysDiff
<=
30
)
{
$displayPenalty
=
0
;
$displayReason
=
'Tiada penalti (≤ 1 bulan)'
;
}
elseif
(
$daysDiff
<=
90
)
{
$displayPenalty
=
$calculatedAmount
*
0.10
;
$displayReason
=
'Lewat hantar >1 ≤3 bulan (10%)'
;
}
elseif
(
$daysDiff
<=
180
)
{
$displayPenalty
=
$calculatedAmount
*
0.30
;
$displayReason
=
'Lewat hantar >3 ≤6 bulan (30%)'
;
}
else
{
$displayPenalty
=
0
;
$displayReason
=
'Tertakluk kepada BOD'
;
}
// Attach computed display-only properties (won't persist to DB)
$claim
->
display_penalty_amount
=
$displayPenalty
;
$claim
->
display_penalty_reason
=
$displayReason
;
$claim
->
display_total_claim_amount
=
(
$calculatedAmount
-
$displayPenalty
);
return
view
(
'mileage::application.show'
,
compact
(
'claim'
));
}
public
function
updateStatus
(
Request
$request
,
$id
)
{
$claim
=
Claim
::
findOrFail
(
$id
);
$user
=
auth
()
->
user
();
switch
(
$request
->
action
)
{
case
'verify'
:
// PM/HOD
$claim
->
status
=
'Disokong'
;
break
;
case
'approve'
:
// PD/BOD
$claim
->
status
=
'Disahkan'
;
break
;
case
'review'
:
// Accounting
$claim
->
status
=
'Disemak'
;
break
;
case
'finance_approve'
:
// Finance Director
$claim
->
status
=
'Diluluskan'
;
break
;
case
'reject'
:
// Any approver
$claim
->
status
=
'Ditolak'
;
break
;
case
'amend'
:
// Finance edits
$claim
->
status
=
'Dibetulkan'
;
break
;
case
'pay'
:
// Final finance step
$claim
->
status
=
'Dibayar'
;
break
;
}
$claim
->
save
();
return
back
()
->
with
(
'success'
,
'Claim status updated to '
.
$claim
->
status
);
}
}
packages/portal/mileage/src/Http/Controllers/ApplyformController.php
View file @
b0d5b251
...
...
@@ -4,41 +4,121 @@
use
Portal\Mileage\Model\Project
;
use
Portal\Mileage\Model\JenisKenderaan
;
use
Portal\Mileage\Model\ProjectTeam
;
use
Portal\Mileage\Model\Users
;
use
Illuminate\Support\Facades\Auth
;
use
Illuminate\Routing\Controller
;
use
Illuminate\Http\Request
;
use
File
;
use
DB
;
use
Auth
;
use
Carbon\Carbon
;
use
Redirect
;
class
ApplyformController
extends
Controller
{
public
function
__construct
()
{
//
}
/**
* Build candidate IDs for current user
*/
protected
function
getCandidateIds
(
$user
)
{
$candidates
=
[];
// 1) user id
if
(
!
empty
(
$user
->
id
))
{
$candidates
[]
=
$user
->
id
;
}
// 2) employeeCode on User model
if
(
!
empty
(
$user
->
employeeCode
))
{
$candidates
[]
=
$user
->
employeeCode
;
}
// 3) extra lookup from portal Users table
$portalUser
=
Users
::
where
(
'id'
,
$user
->
id
)
->
first
();
if
(
$portalUser
)
{
if
(
!
empty
(
$portalUser
->
employeeCode
))
{
$candidates
[]
=
$portalUser
->
employeeCode
;
}
if
(
!
empty
(
$portalUser
->
pt_staff_id
))
{
$candidates
[]
=
$portalUser
->
pt_staff_id
;
}
}
// Clean up (remove nulls, duplicates)
return
array_values
(
array_unique
(
array_filter
(
$candidates
,
function
(
$v
)
{
return
$v
!==
null
&&
$v
!==
''
;
})));
}
/**
* Get projects for the logged in user
*/
protected
function
getProjectsForUser
(
$user
)
{
$candidates
=
$this
->
getCandidateIds
(
$user
);
$projectIds
=
[];
if
(
!
empty
(
$candidates
))
{
$projectIds
=
ProjectTeam
::
whereIn
(
'pt_staff_id'
,
$candidates
)
->
pluck
(
'fk_project_id'
)
->
unique
()
->
toArray
();
}
$isAdmin
=
(
$user
->
role_id
==
1
);
if
(
$isAdmin
)
{
// Admin sees all
return
Project
::
orderBy
(
'p_project_description'
)
->
get
();
}
elseif
(
!
empty
(
$projectIds
))
{
// Non-admin, but has assigned projects
return
Project
::
whereIn
(
'id'
,
$projectIds
)
->
orderBy
(
'p_project_description'
)
->
get
();
}
// Non-admin with no assigned projects
return
collect
([]);
}
public
function
index
()
{
$projects
=
Project
::
all
();
$jenisKenderaan
=
JenisKenderaan
::
all
();
$user
=
auth
()
->
user
();
return
view
(
'mileage::applyform.index'
,
compact
(
'projects'
,
'jenisKenderaan'
));
// fetch role names directly
$roles
=
$user
->
roles
->
pluck
(
'name'
)
->
toArray
();
$isAdmin
=
in_array
(
'Administrator'
,
$roles
);
// check by role name
if
(
$isAdmin
)
{
// Admin → all projects
$projects
=
Project
::
orderBy
(
'p_project_description'
)
->
get
();
}
else
{
// Non-admin → only their assigned projects
$projects
=
Project
::
whereHas
(
'projectTeam'
,
function
(
$q
)
use
(
$user
)
{
$q
->
where
(
'pt_staff_id'
,
$user
->
employee_code
);
})
->
get
();
}
$jenisKenderaan
=
JenisKenderaan
::
all
();
return
view
(
'mileage::applyform.index'
,
compact
(
'projects'
,
'jenisKenderaan'
));
}
public
function
create
()
{
$projects
=
Project
::
all
();
return
view
(
'mileage::applyform.index'
,
compact
(
'projects'
));
$user
=
Auth
::
user
();
$projects
=
$this
->
getProjectsForUser
(
$user
);
$jenisKenderaan
=
JenisKenderaan
::
all
();
return
view
(
'mileage::applyform.index'
,
compact
(
'projects'
,
'jenisKenderaan'
));
}
public
function
save
(
Request
$request
)
{
// TODO: Implement save logic
return
null
;
}
}
//end function
}
packages/portal/mileage/src/Http/Controllers/ProjectDirectorController.php
0 → 100644
View file @
b0d5b251
<?php
namespace
Portal\Mileage\Http\Controllers
;
use
Portal\Mileage\Model\Users
;
use
Portal\Mileage\Model\Project
;
use
Portal\Mileage\Model\JenisKenderaan
;
use
Portal\Mileage\Model\Claim
;
use
Portal\Mileage\Model\ProjectTeam
;
use
App\Http\Controllers\Controller
;
use
Illuminate\Http\Request
;
use
Carbon\Carbon
;
use
Illuminate\Support\Facades\Auth
;
class
ProjectDirectorController
extends
Controller
{
public
function
index
()
{
$user
=
auth
()
->
user
();
// Get project IDs where this user is the Project Director
$directedProjectIds
=
ProjectTeam
::
where
(
'pt_staff_id'
,
$user
->
employeeCode
)
->
where
(
'role_id'
,
7
)
// 7 = Project Director
->
pluck
(
'fk_project_id'
)
->
toArray
();
// Get claims for those projects, only after PM verification
$claims
=
Claim
::
with
([
'user'
,
'project'
])
->
whereIn
(
'project_id'
,
$directedProjectIds
)
->
whereIn
(
'status'
,
[
'Disahkan'
,
'Ditolak'
])
// Only show claims verified by Project Manager
->
orderBy
(
'created_at'
,
'desc'
)
->
get
();
return
view
(
'mileage::projectDirector.index'
,
compact
(
'claims'
));
}
public
function
show
(
$id
)
{
// load claim
$claim
=
Claim
::
with
([
'project'
,
'jenisKenderaan'
,
'user'
])
->
findOrFail
(
$id
);
// Recompute penalty for display based on actual submission date (created_at)
// so show page always displays correct reason even if saved earlier incorrectly.
$claimDate
=
Carbon
::
parse
(
$claim
->
claim_date
);
$submissionDate
=
$claim
->
submission_date
??
$claim
->
created_at
??
Carbon
::
now
();
$daysDiff
=
$claimDate
->
diffInDays
(
$submissionDate
);
$calculatedAmount
=
$claim
->
calculated_amount
??
0
;
if
(
$daysDiff
<=
30
)
{
$displayPenalty
=
0
;
$displayReason
=
'Tiada penalti (≤ 1 bulan)'
;
}
elseif
(
$daysDiff
<=
90
)
{
$displayPenalty
=
$calculatedAmount
*
0.10
;
$displayReason
=
'Lewat hantar >1 ≤3 bulan (10%)'
;
}
elseif
(
$daysDiff
<=
180
)
{
$displayPenalty
=
$calculatedAmount
*
0.30
;
$displayReason
=
'Lewat hantar >3 ≤6 bulan (30%)'
;
}
else
{
$displayPenalty
=
0
;
$displayReason
=
'Tertakluk kepada BOD'
;
}
// Attach computed display-only properties (won't persist to DB)
$claim
->
display_penalty_amount
=
$displayPenalty
;
$claim
->
display_penalty_reason
=
$displayReason
;
$totalClaim
=
(
$calculatedAmount
+
(
$claim
->
toll_amount
??
0
)
+
(
$claim
->
others_amount
??
0
))
-
$displayPenalty
;
$claim
->
display_total_claim_amount
=
$totalClaim
;
return
view
(
'mileage::projectDirector.show'
,
compact
(
'claim'
));
}
public
function
approve
(
$id
)
{
$user
=
auth
()
->
user
();
$claim
=
Claim
::
findOrFail
(
$id
);
$claim
->
status
=
'Disahkan'
;
$allowedProjectIds
=
ProjectTeam
::
where
(
'pt_staff_id'
,
$user
->
employeeCode
)
->
where
(
'role_id'
,
7
)
->
pluck
(
'fk_project_id'
)
->
toArray
();
if
(
!
in_array
(
$claim
->
project_id
,
$allowedProjectIds
))
{
return
back
()
->
with
(
'error'
,
'Anda tidak dibenarkan meluluskan tuntutan ini.'
);
}
$claim
->
status
=
'Disahkan'
;
$claim
->
save
();
return
back
()
->
with
(
'success'
,
'Claim approved successfully.'
);
}
public
function
reject
(
$id
)
{
$claim
=
Claim
::
findOrFail
(
$id
);
$user
=
auth
()
->
user
();
$allowedProjectIds
=
ProjectTeam
::
where
(
'pt_staff_id'
,
$user
->
employeeCode
)
->
where
(
'role_id'
,
7
)
->
pluck
(
'fk_project_id'
)
->
toArray
();
if
(
!
in_array
(
$claim
->
project_id
,
$allowedProjectIds
))
{
return
back
()
->
with
(
'error'
,
'Anda tidak dibenarkan menolak tuntutan ini.'
);
}
$claim
->
status
=
'Ditolak'
;
$claim
->
save
();
return
back
()
->
with
(
'error'
,
'Claim rejected.'
);
}
}
packages/portal/mileage/src/Http/Controllers/ProjectManagerController.php
0 → 100644
View file @
b0d5b251
<?php
namespace
Portal\Mileage\Http\Controllers
;
use
Portal\Mileage\Model\Users
;
use
Portal\Mileage\Model\Project
;
use
Portal\Mileage\Model\JenisKenderaan
;
use
Portal\Mileage\Model\Claim
;
use
Portal\Mileage\Model\ProjectTeam
;
use
Illuminate\Routing\Controller
;
use
Illuminate\Http\Request
;
use
Carbon\Carbon
;
class
ProjectManagerController
extends
Controller
{
public
function
index
()
{
$user
=
auth
()
->
user
();
// Get project IDs managed by this PM
$managedProjectIds
=
ProjectTeam
::
where
(
'pt_staff_id'
,
$user
->
employeeCode
)
->
where
(
'role_id'
,
1
)
// assuming 2 = PM
->
pluck
(
'fk_project_id'
)
->
toArray
();
// Get claims only for those projects
$claims
=
Claim
::
with
([
'user'
,
'project'
])
->
whereIn
(
'project_id'
,
$managedProjectIds
)
->
get
();
return
view
(
'mileage::projectManager.index'
,
compact
(
'claims'
));
}
public
function
show
(
$id
)
{
// load claim
$claim
=
Claim
::
with
([
'project'
,
'jenisKenderaan'
])
->
findOrFail
(
$id
);
// Recompute penalty for display based on actual submission date (created_at)
// so show page always displays correct reason even if saved earlier incorrectly.
$claimDate
=
Carbon
::
parse
(
$claim
->
claim_date
);
$submissionDate
=
$claim
->
created_at
??
Carbon
::
now
();
$daysDiff
=
$claimDate
->
diffInDays
(
$submissionDate
);
$calculatedAmount
=
$claim
->
calculated_amount
??
0
;
if
(
$daysDiff
<=
30
)
{
$displayPenalty
=
0
;
$displayReason
=
'Tiada penalti (≤ 1 bulan)'
;
}
elseif
(
$daysDiff
<=
90
)
{
$displayPenalty
=
$calculatedAmount
*
0.10
;
$displayReason
=
'Lewat hantar >1 ≤3 bulan (10%)'
;
}
elseif
(
$daysDiff
<=
180
)
{
$displayPenalty
=
$calculatedAmount
*
0.30
;
$displayReason
=
'Lewat hantar >3 ≤6 bulan (30%)'
;
}
else
{
$displayPenalty
=
0
;
$displayReason
=
'Tertakluk kepada BOD'
;
}
// Attach computed display-only properties (won't persist to DB)
$claim
->
display_penalty_amount
=
$displayPenalty
;
$claim
->
display_penalty_reason
=
$displayReason
;
$claim
->
display_total_claim_amount
=
(
$calculatedAmount
-
$displayPenalty
);
return
view
(
'mileage::projectManager.show'
,
compact
(
'claim'
));
}
}
packages/portal/mileage/src/Model/AclRoleUser.php
0 → 100644
View file @
b0d5b251
<?php
namespace
Portal\Mileage\Model
;
use
Illuminate\Database\Eloquent\Model
;
class
AclRoleUser
extends
Model
{
protected
$table
=
'acl_role_user'
;
protected
$fillable
=
[
'role_id'
,
'user_id'
];
public
$timestamps
=
false
;
public
function
role
()
{
return
$this
->
belongsTo
(
AclRole
::
class
,
'role_id'
);
}
public
function
user
()
{
return
$this
->
belongsTo
(
\App\Models\User
::
class
,
'user_id'
);
}
}
packages/portal/mileage/src/Model/Aclrole.php
View file @
b0d5b251
...
...
@@ -4,14 +4,20 @@
use
Illuminate\Database\Eloquent\Model
;
class
Acl
r
ole
extends
Model
class
Acl
R
ole
extends
Model
{
/**
* The database table used by the model.
*
* @var string
*/
protected
$table
=
'acl_role_user'
;
protected
$table
=
'acl_roles'
;
// ⚠️ must be your roles definition table
protected
$primaryKey
=
'id'
;
public
$timestamps
=
false
;
public
$primaryKey
=
'user_id'
;
public
function
users
()
{
return
$this
->
belongsToMany
(
\App\Models\User
::
class
,
'acl_role_user'
,
'role_id'
,
'user_id'
);
}
}
packages/portal/mileage/src/Model/Claim.php
View file @
b0d5b251
...
...
@@ -2,6 +2,8 @@
namespace
Portal\Mileage\Model
;
use
App\Models\User
;
use
Portal\Mileage\Model\Project
;
use
Portal\Mileage\Model\JenisKenderaan
;
...
...
@@ -12,13 +14,18 @@ class Claim extends Model
{
protected
$fillable
=
[
'claim_date'
,
'jenis_kenderaan_id'
,
'project_id'
,
'description'
,
'
user_id'
,
'
claim_date'
,
'jenis_kenderaan_id'
,
'project_id'
,
'description'
,
'distance_from'
,
'distance_to'
,
'total_mileage'
,
'toll_amount'
,
'others_amount'
,
'others_description'
,
'calculated_amount'
,
'penalty_amount'
,
'penalty_reason'
,
'total_claim_amount'
,
'status'
,
];
public
function
user
()
{
return
$this
->
belongsTo
(
User
::
class
,
'user_id'
,
'id'
);
}
public
function
project
()
{
return
$this
->
belongsTo
(
Project
::
class
,
'project_id'
,
'id'
,
'p_project_description'
);
...
...
packages/portal/mileage/src/Model/Project.php
View file @
b0d5b251
...
...
@@ -49,5 +49,11 @@ public function claims()
{
return
$this
->
hasMany
(
Claim
::
class
,
'project_id'
);
}
public
function
projectTeam
()
{
return
$this
->
hasMany
(
\Portal\Mileage\Model\ProjectTeam
::
class
,
'fk_project_id'
,
'id'
);
}
}
packages/portal/mileage/src/Model/ProjectRole.php
0 → 100644
View file @
b0d5b251
<?php
namespace
Portal\Mileage\Model
;
use
Portal\Mileage\Model\Project
;
use
Portal\Mileage\Model\JenisKenderaan
;
use
Illuminate\Database\Eloquent\Factories\HasFactory
;
use
Illuminate\Database\Eloquent\Model
;
class
ProjectRole
extends
Model
{
protected
$table
=
'project_role'
;
}
\ No newline at end of file
packages/portal/mileage/src/Model/ProjectTeam.php
0 → 100644
View file @
b0d5b251
<?php
namespace
Portal\Mileage\Model
;
use
Illuminate\Database\Eloquent\Model
;
class
ProjectTeam
extends
Model
{
protected
$table
=
'project_team'
;
public
$timestamps
=
true
;
protected
$primaryKey
=
'id'
;
protected
$fillable
=
[
'fk_project_id'
,
'pt_project_description'
,
'role_id'
,
'pt_team'
,
'pt_staff_id'
,
'pt_project_type'
,
'pt_project_task_sts'
,
'updated_by'
];
public
function
project
()
{
return
$this
->
belongsTo
(
Project
::
class
,
'fk_project_id'
,
'id'
);
}
// if you want, link to App user model (pt_staff_id commonly stores App user id or employee code)
public
function
user
()
{
return
$this
->
belongsTo
(
\App\Models\User
::
class
,
'pt_staff_id'
,
'id'
);
}
public
function
role
()
{
return
$this
->
belongsTo
(
ProjectRole
::
class
,
'role_id'
);
}
}
packages/portal/mileage/src/Model/Users.php
View file @
b0d5b251
...
...
@@ -39,5 +39,23 @@ public function staffinfo()
return
$this
->
belongsTo
(
'Portal\Mileage\Model\StaffInfo'
,
'id'
,
'fk_users'
);
}
public
function
projectTeams
()
{
return
$this
->
hasMany
(
'Portal\Mileage\Model\ProjectTeam'
,
'pt_staff_id'
,
'employeeCode'
);
}
public
function
managedProjects
()
{
return
$this
->
projectTeams
()
->
where
(
'role_id'
,
1
);
// 1 = Project Manager
}
public
function
roles
()
{
return
$this
->
belongsToMany
(
\Portal\Mileage\Model\AclRole
::
class
,
'acl_role_user'
,
'user_id'
,
'role_id'
);
}
}
packages/portal/mileage/src/ServiceProvider.php
View file @
b0d5b251
...
...
@@ -84,22 +84,29 @@ protected function menu()
->
data
(
'icon'
,
'home'
)
->
active
(
'home/*'
);
$menu
->
add
(
__
(
'Project Manager Dashboard'
),
route
(
'mileage::projectManager.index'
))
->
data
(
'icon'
,
'user tie'
)
->
active
(
'projectManager/*'
)
->
data
(
'permission'
,
Permission
::
PROJECT_MANAGER
);
$menu
->
add
(
__
(
'Project Director Dashboard'
),
route
(
'mileage::projectDirector.index'
))
->
data
(
'icon'
,
'user tie'
)
->
active
(
'projectDirector/*'
)
->
data
(
'permission'
,
Permission
::
PROJECT_DIRECTOR
);
$menu
=
$menus
->
add
(
__
(
'Borang Permohonan Mileage Claim'
))
->
data
(
'icon'
,
'align justify'
);
$now
=
Carbon
::
today
()
->
format
(
'Y-m-d'
);
$menu
->
add
(
__
(
'Permohonan Mileage Claim'
),
route
(
'mileage::applyform.index'
))
->
data
(
'icon'
,
'edit outline'
)
->
active
(
'applyform/*'
)
->
data
(
'permission'
,
Permission
::
APPLY_CLAIM
);
->
active
(
'applyform/*'
);
$menu
=
$menus
->
add
(
__
(
'Senarai Permohonan'
))
->
data
(
'icon'
,
'align justify'
);
$menu
->
add
(
__
(
'Senarai Permohonan Mileage Claim'
),
route
(
'mileage::applications.index'
))
->
data
(
'icon'
,
'bars'
)
->
active
(
'applications/*'
);
});
}
...
...
resources/views/vendor/ui/quick-switcher/sidebar.blade.php
View file @
b0d5b251
...
...
@@ -9,7 +9,7 @@
<div
class=
"detail"
>
{{auth()->user()->roles['0']->name}}
</div>
</a>
<p></p>
<a
href=
"
https://warga.ppj.gov.my
"
class=
"ui teal left ribbon label"
>
Kembali Ke EP
</a>
<a
href=
"
{{ route('auth::logout') }}
"
class=
"ui teal left ribbon label"
>
Kembali Ke EP
</a>
<p></p>
</div>
</div>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment