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
a3cb80f0
Commit
a3cb80f0
authored
Oct 31, 2025
by
Hannah Zahra
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fifth Update
parent
90f032c8
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
476 additions
and
75 deletions
+476
-75
database/migrations/2025_09_22_042439_create_claims_table.php
+6
-0
packages/portal/mileage/resources/views/application/index.blade.php
+37
-2
packages/portal/mileage/resources/views/application/show.blade.php
+186
-3
packages/portal/mileage/resources/views/projectDirector/show.blade.php
+0
-0
packages/portal/mileage/resources/views/projectManager/show.blade.php
+113
-9
packages/portal/mileage/resources/views/projectManager/view.blade.php
+0
-0
packages/portal/mileage/routes/web.php
+4
-0
packages/portal/mileage/src/Http/Controllers/ApplicationsController.php
+26
-24
packages/portal/mileage/src/Http/Controllers/ProjectDirectorController.php
+68
-3
packages/portal/mileage/src/Http/Controllers/ProjectManagerController.php
+36
-34
No files found.
database/migrations/2025_09_22_042439_create_claims_table.php
View file @
a3cb80f0
...
...
@@ -57,6 +57,10 @@ public function up()
$table
->
unsignedInteger
(
'rejected_by'
)
->
nullable
();
$table
->
foreign
(
'rejected_by'
)
->
references
(
'id'
)
->
on
(
'users'
)
->
nullOnDelete
();
// Reject reason
$table
->
string
(
'reject_reason'
,
500
)
->
nullable
();
// Claim status (workflow)
$table
->
enum
(
'status'
,
[
'Diproses'
,
...
...
@@ -88,6 +92,8 @@ public function down()
$table
->
dropForeign
([
'rejected_by'
]);
$table
->
dropColumn
(
'rejected_by'
);
$table
->
dropColumn
(
'reject_reason'
);
});
// Drop the 'claims' table entirely
...
...
packages/portal/mileage/resources/views/application/index.blade.php
View file @
a3cb80f0
...
...
@@ -371,7 +371,19 @@
<td>
RM {{ number_format($totalAmount, 2) }}
</td>
<td>
@foreach($projectClaims->pluck('status')->unique() as $status)
<span
class=
"status-label status-{{ $status }}"
>
{{ $status }}
</span>
@if($status === 'Ditolak')
<span
class=
"status-label status-Ditolak"
data-tooltip=
"Tekan butang 'Lihat' untuk lihat butiran tuntutan ini."
data-inverted
data-position=
"top center"
style=
"cursor: help;"
>
{{ $status }}
</span>
@else
<span
class=
"status-label status-{{ $status }}"
>
{{ $status }}
</span>
@endif
@endforeach
</td>
<td>
...
...
@@ -409,7 +421,19 @@
<td>
{{ number_format($claim->total_mileage, 1) }}
</td>
<td>
RM {{ number_format($claim->total_claim_amount, 2) }}
</td>
<td>
<span
class=
"status-label status-{{ $claim->status }}"
>
{{ $claim->status }}
</span>
@if($claim->status === 'Ditolak')
<span
class=
"status-label status-Ditolak"
data-tooltip=
"{{ $claim->reject_reason ?? 'Tiada sebab diberikan.' }}"
data-inverted
data-position=
"top center"
style=
"cursor: help;"
>
{{ $claim->status }}
</span>
@else
<span
class=
"status-label status-{{ $claim->status }}"
>
{{ $claim->status }}
</span>
@endif
</td>
<td>
{{-- Always show Lihat --}}
...
...
@@ -525,6 +549,17 @@
<script
src=
"https://code.jquery.com/jquery-3.7.1.min.js"
></script>
<script>
$
(
document
).
ready
(
function
()
{
// Initialize Semantic/Fomantic UI tooltips
$
(
'[data-tooltip]'
).
popup
({
variation
:
'very wide'
,
hoverable
:
true
,
position
:
'top center'
,
delay
:
{
show
:
100
,
hide
:
200
}
});
// Toggle Summary Section
$
(
'#summaryToggle'
).
on
(
'click'
,
function
()
{
$
(
this
).
toggleClass
(
'active'
);
...
...
packages/portal/mileage/resources/views/application/show.blade.php
View file @
a3cb80f0
...
...
@@ -58,6 +58,7 @@
<th>
Lain-lain (RM)
</th>
<th>
Status
</th>
<th>
Jumlah (RM)
</th>
<th>
Tindakan
</th>
</tr>
</thead>
<tbody>
...
...
@@ -70,7 +71,7 @@
@foreach($claims as $c)
@php
$total += $c->total_claim
_amount
;
$total += $c->total_claim;
$totalMileage += $c->total_mileage ?? 0;
$totalToll += $c->toll_amount ?? 0;
$totalOthers += $c->others_amount ?? 0;
...
...
@@ -85,9 +86,51 @@
<td>
{{ number_format($c->toll_amount ?? 0, 2) }}
</td>
<td>
{{ number_format($c->others_amount ?? 0, 2) }}
</td>
<td>
<span
class=
"status-label status-{{ $c->status }}"
>
{{ $c->status }}
</span>
@if($c->status === 'Ditolak')
<span
class=
"status-label status-Ditolak"
data-tooltip=
"{{ $c->reject_reason ?? 'Tiada sebab diberikan.' }}"
data-inverted
data-position=
"top center"
style=
"cursor: help;"
>
{{ $c->status }}
</span>
@else
<span
class=
"status-label status-{{ $c->status }}"
>
{{ $c->status }}
</span>
@endif
</td>
<td>
RM {{ number_format($c->total_claim_amount, 2) }}
</td>
<td>
{{-- actions for Diproses --}}
@if($c->status === 'Diproses')
<div
style=
"margin-top:8px;"
>
<button
class=
"ui yellow tiny button edit-btn"
data-id=
"{{ $c->id }}"
data-date=
"{{ $c->claim_date }}"
data-description=
"{{ $c->description }}"
data-distance-from=
"{{ $c->distance_from }}"
data-distance-to=
"{{ $c->distance_to }}"
data-mileage=
"{{ $c->total_mileage }}"
data-toll=
"{{ $c->toll_amount }}"
data-others=
"{{ $c->others_amount }}"
>
<i
class=
"edit icon"
></i>
Kemaskini
</button>
<form
action=
"{{ route('mileage::applications.destroy', $c->id) }}"
method=
"POST"
style=
"display:inline;"
onsubmit=
"return confirm('Padam tuntutan ini?');"
>
@csrf
@method('DELETE')
<button
type=
"submit"
class=
"ui red tiny button"
>
<i
class=
"trash icon"
></i>
Padam
</button>
</form>
</div>
@endif
</td>
<td>
RM {{ number_format($c->total_claim_amount, 2) }}
</td>
</tr>
@endforeach
</tbody>
...
...
@@ -118,5 +161,145 @@
<a
href=
"{{ route('mileage::applications.index', ['month' => $month]) }}"
class=
"ui grey button"
>
<i
class=
"arrow left icon"
></i>
Kembali
</a>
{{-- Edit Modal --}}
<div
class=
"ui modal"
id=
"editModal"
>
<i
class=
"close icon"
></i>
<div
class=
"header"
>
Kemaskini Tuntutan
</div>
<div
class=
"content"
>
<form
id=
"editForm"
method=
"POST"
action=
""
>
@csrf
@method('PUT')
<div
class=
"ui form"
>
<div
class=
"field required"
>
<label>
Tarikh
</label>
<input
type=
"date"
name=
"claim_date"
id=
"editDate"
required
>
</div>
<div
class=
"two fields"
>
<div
class=
"field required"
>
<label>
Dari
</label>
<input
type=
"text"
name=
"distance_from"
id=
"editFrom"
required
>
</div>
<div
class=
"field required"
>
<label>
Ke
</label>
<input
type=
"text"
name=
"distance_to"
id=
"editTo"
required
>
</div>
</div>
<div
class=
"field required"
>
<label>
Butiran
</label>
<input
type=
"text"
name=
"description"
id=
"editDescription"
required
>
</div>
<div
class=
"three fields"
>
<div
class=
"field required"
>
<label>
Jumlah KM
</label>
<input
type=
"number"
step=
"0.1"
name=
"total_mileage"
id=
"editMileage"
required
>
</div>
<div
class=
"field"
>
<label>
Tol (RM)
</label>
<input
type=
"number"
step=
"0.01"
name=
"toll_amount"
id=
"editToll"
>
</div>
<div
class=
"field"
>
<label>
Lain-lain (RM)
</label>
<input
type=
"number"
step=
"0.01"
name=
"others_amount"
id=
"editOthers"
>
</div>
</div>
<div
class=
"field"
id=
"othersDetailsField"
style=
"display:none;"
>
<label>
Butiran Lain-lain
</label>
<input
type=
"text"
name=
"others_details"
id=
"editOthersDetails"
placeholder=
"Nyatakan butiran lain-lain (contoh: bayaran penghantaran, dokumen, dsb)"
>
</div>
</div>
</form>
</div>
<div
class=
"actions"
>
<div
class=
"ui cancel button"
>
Batal
</div>
<button
type=
"submit"
form=
"editForm"
class=
"ui primary button"
>
Simpan
</button>
</div>
</div>
{{-- Edit Modal Script --}}
<script
src=
"https://code.jquery.com/jquery-3.7.1.min.js"
></script>
<script>
$
(
document
).
ready
(
function
()
{
// Initialize tooltips for Ditolak statuses
$
(
'[data-tooltip]'
).
popup
({
variation
:
'very wide'
,
hoverable
:
true
,
position
:
'top center'
,
delay
:
{
show
:
100
,
hide
:
200
}
});
// Handle Edit button click
$
(
'.edit-btn'
).
on
(
'click'
,
function
()
{
const
id
=
$
(
this
).
data
(
'id'
);
const
date
=
$
(
this
).
data
(
'date'
);
const
desc
=
$
(
this
).
data
(
'description'
);
const
from
=
$
(
this
).
data
(
'distance-from'
);
const
to
=
$
(
this
).
data
(
'distance-to'
);
const
mileage
=
$
(
this
).
data
(
'mileage'
);
const
toll
=
$
(
this
).
data
(
'toll'
);
const
others
=
$
(
this
).
data
(
'others'
);
const
updateUrl
=
"{{ route('mileage::applications.update', ':id') }}"
.
replace
(
':id'
,
id
);
$
(
'#editForm'
).
attr
(
'action'
,
updateUrl
);
$
(
'#editDate'
).
val
(
date
);
$
(
'#editDescription'
).
val
(
desc
);
$
(
'#editFrom'
).
val
(
from
);
$
(
'#editTo'
).
val
(
to
);
$
(
'#editMileage'
).
val
(
mileage
);
$
(
'#editToll'
).
val
(
toll
);
$
(
'#editOthers'
).
val
(
others
);
// Trigger others field logic
$
(
'#editOthers'
).
trigger
(
'input'
);
$
(
'#editModal'
).
modal
(
'show'
);
});
// Show/Hide "Butiran Lain-lain" based on input
$
(
'#editOthers'
).
on
(
'input'
,
function
()
{
const
value
=
parseFloat
(
$
(
this
).
val
())
||
0
;
if
(
value
>
0
)
{
$
(
'#othersDetailsField'
).
slideDown
();
$
(
'#editOthersDetails'
).
attr
(
'required'
,
true
);
}
else
{
$
(
'#othersDetailsField'
).
slideUp
();
$
(
'#editOthersDetails'
).
removeAttr
(
'required'
);
$
(
'#editOthersDetails'
).
val
(
''
);
}
});
// Validate before form submission
$
(
'#editForm'
).
on
(
'submit'
,
function
(
e
)
{
const
others
=
parseFloat
(
$
(
'#editOthers'
).
val
())
||
0
;
const
othersDetails
=
$
(
'#editOthersDetails'
).
val
().
trim
();
if
(
others
>
0
&&
othersDetails
===
''
)
{
e
.
preventDefault
();
alert
(
'Sila isi Butiran Lain-lain kerana jumlah Lain-lain (RM) lebih daripada 0.'
);
$
(
'#editOthersDetails'
).
focus
();
return
;
}
let
valid
=
true
;
$
(
'#editForm [required]'
).
each
(
function
()
{
if
(
$
(
this
).
val
().
trim
()
===
''
)
{
valid
=
false
;
$
(
this
).
focus
();
alert
(
'Sila isi semua ruangan yang diperlukan.'
);
e
.
preventDefault
();
return
false
;
}
});
});
});
</script>
</div>
@endsection
packages/portal/mileage/resources/views/projectDirector/show.blade.php
View file @
a3cb80f0
This diff is collapsed.
Click to expand it.
packages/portal/mileage/resources/views/projectManager/show.blade.php
View file @
a3cb80f0
...
...
@@ -85,6 +85,12 @@
gap
:
1rem
;
margin-top
:
1rem
;
}
.approval-actions
.ui.button
{
height
:
42px
;
text-align
:
center
;
}
</style>
<div
class=
"ui container"
>
...
...
@@ -113,6 +119,7 @@
<th>
Lain-lain (RM)
</th>
<th>
Status
</th>
<th>
Jumlah (RM)
</th>
<th>
Tindakan
</th>
</tr>
</thead>
<tbody>
...
...
@@ -143,6 +150,31 @@
</span>
</td>
<td>
{{ number_format($claim->total_claim_amount, 2) }}
</td>
<td>
{{-- individual approve --}}
@if($claim->status === 'Diproses')
{{-- only show if status = Diproses --}}
<form
action=
"{{ route('mileage::projectManager.approveClaim', $claim->id) }}"
method=
"POST"
style=
"display:inline;"
>
@csrf
<button
type=
"submit"
class=
"ui green tiny button"
>
<i
class=
"check icon"
></i>
Sahkan
</button>
</form>
<button
type=
"button"
class=
"ui red tiny button reject-single-btn"
data-claim-id=
"{{ $claim->id }}"
data-project=
"{{ $project->id }}"
data-user=
"{{ $user->id }}"
data-month=
"{{ $month }}"
>
<i
class=
"times icon"
></i>
Tolak
</button>
@else
{{-- show dash for processed claims --}}
<span
style=
"color:#999;"
>
—
</span>
@endif
</td>
</tr>
@if(!empty($claim->others_description))
...
...
@@ -196,16 +228,15 @@
</button>
</form>
{{-- Reject All --}}
<
form
action=
"{{ route('mileage::projectManager.rejectMonth') }}"
method=
"POST"
style=
"display:inline;"
>
@csrf
<input
type=
"hidden"
name=
"project_id"
value=
"{{ $project->id }}"
>
<input
type=
"hidden"
name=
"user_id"
value=
"{{ $user->id }}"
>
<input
type=
"hidden"
name=
"month"
value=
"{{ $month }}"
>
<button
type=
"submit"
class=
"ui red button
"
>
{{-- Reject All
with Modal Trigger
--}}
<
button
type=
"button"
class=
"ui red button reject-btn"
data-project=
"{{ $project->id }}"
data-user=
"{{ $user->id }}"
data-month=
"{{ $month }}
"
>
<i
class=
"times icon"
></i>
Tolak Semua
</button>
</form>
</div>
@endif
</div>
...
...
@@ -246,7 +277,7 @@
}
//
now
match
the
total
(
no
recomputation
)
$
finalTotal =
$totalClaimAmount
+
$
totalToll
+
$
totalOthers
-
$
penaltyAmount
;
$
finalTotal =
$totalClaimAmount
-
$
penaltyAmount
;
@
endphp
<
div
class=
"ui segment"
>
...
...
@@ -309,4 +340,77 @@
</p>
</div>
</div>
{{-- Reject Modal --}}
<div
class=
"ui small modal"
id=
"rejectModal"
>
<i
class=
"close icon"
></i>
<div
class=
"header"
>
Sebab Penolakan
</div>
<div
class=
"content"
>
<form
id=
"rejectForm"
method=
"POST"
action=
"{{ route('mileage::projectManager.rejectMonth') }}"
>
@csrf
<input
type=
"hidden"
name=
"project_id"
id=
"rejectProjectId"
>
<input
type=
"hidden"
name=
"user_id"
id=
"rejectUserId"
>
<input
type=
"hidden"
name=
"month"
id=
"rejectMonth"
>
<div
class=
"ui form"
>
<div
class=
"field required"
>
<label>
Nyatakan sebab tuntutan ini ditolak:
</label>
<textarea
name=
"reject_reason"
id=
"rejectReason"
rows=
"3"
placeholder=
"Contoh: Tuntutan tidak lengkap atau tiada resit sokongan"
required
></textarea>
</div>
</div>
</form>
</div>
<div
class=
"actions"
>
<div
class=
"ui cancel button"
>
Batal
</div>
<button
type=
"submit"
form=
"rejectForm"
class=
"ui red button"
>
Hantar Penolakan
</button>
</div>
</div>
<script
src=
"https://code.jquery.com/jquery-3.7.1.min.js"
></script>
<script>
$
(
document
).
ready
(
function
()
{
// Handle single claim rejection
$
(
'.reject-single-btn'
).
on
(
'click'
,
function
()
{
const
claimId
=
$
(
this
).
data
(
'claim-id'
);
const
projectId
=
$
(
this
).
data
(
'project'
);
const
userId
=
$
(
this
).
data
(
'user'
);
const
month
=
$
(
this
).
data
(
'month'
);
// Change form action dynamically for single reject
const
action
=
"{{ route('mileage::projectManager.rejectClaim', ':id') }}"
.
replace
(
':id'
,
claimId
);
$
(
'#rejectForm'
).
attr
(
'action'
,
action
);
// Fill modal hidden fields
$
(
'#rejectProjectId'
).
val
(
projectId
);
$
(
'#rejectUserId'
).
val
(
userId
);
$
(
'#rejectMonth'
).
val
(
month
);
$
(
'#rejectReason'
).
val
(
''
);
// Show modal
$
(
'#rejectModal'
).
modal
(
'show'
);
});
// Show reject modal when button clicked
$
(
'.reject-btn'
).
on
(
'click'
,
function
()
{
$
(
'#rejectProjectId'
).
val
(
$
(
this
).
data
(
'project'
));
$
(
'#rejectUserId'
).
val
(
$
(
this
).
data
(
'user'
));
$
(
'#rejectMonth'
).
val
(
$
(
this
).
data
(
'month'
));
$
(
'#rejectReason'
).
val
(
''
);
// clear textarea
$
(
'#rejectModal'
).
modal
(
'show'
);
});
// Prevent submit if no reason entered
$
(
'#rejectForm'
).
on
(
'submit'
,
function
(
e
)
{
const
reason
=
$
(
'#rejectReason'
).
val
().
trim
();
if
(
reason
===
''
)
{
e
.
preventDefault
();
alert
(
'Sila nyatakan sebab penolakan sebelum menghantar.'
);
$
(
'#rejectReason'
).
focus
();
}
});
});
</script>
@endsection
packages/portal/mileage/resources/views/projectManager/view.blade.php
0 → 100644
View file @
a3cb80f0
packages/portal/mileage/routes/web.php
View file @
a3cb80f0
...
...
@@ -39,6 +39,8 @@ function () {
Route
::
get
(
'/show/{projectId}/{userId}'
,
[
ProjectManagerController
::
class
,
'show'
])
->
name
(
'.show'
);
Route
::
post
(
'/approve-month'
,
[
ProjectManagerController
::
class
,
'approveMonth'
])
->
name
(
'.approveMonth'
);
Route
::
post
(
'/reject-month'
,
[
ProjectManagerController
::
class
,
'rejectMonth'
])
->
name
(
'.rejectMonth'
);
Route
::
post
(
'/claim/{id}/approve'
,
[
ProjectManagerController
::
class
,
'approveClaim'
])
->
name
(
'.approveClaim'
);
Route
::
post
(
'/claim/{id}/reject'
,
[
ProjectManagerController
::
class
,
'rejectClaim'
])
->
name
(
'.rejectClaim'
);
});
// Project Director
...
...
@@ -47,6 +49,8 @@ function () {
Route
::
get
(
'/show/{projectId}/{userId}'
,
[
ProjectDirectorController
::
class
,
'show'
])
->
name
(
'.show'
);
Route
::
post
(
'/approve-month'
,
[
ProjectDirectorController
::
class
,
'approveMonth'
])
->
name
(
'.approveMonth'
);
Route
::
post
(
'/reject-month'
,
[
ProjectDirectorController
::
class
,
'rejectMonth'
])
->
name
(
'.rejectMonth'
);
Route
::
post
(
'/claim/{id}/approve'
,
[
ProjectDirectorController
::
class
,
'approveClaim'
])
->
name
(
'.approveClaim'
);
Route
::
post
(
'/claim/{id}/reject'
,
[
ProjectDirectorController
::
class
,
'rejectClaim'
])
->
name
(
'.rejectClaim'
);
});
// Finance Department
...
...
packages/portal/mileage/src/Http/Controllers/ApplicationsController.php
View file @
a3cb80f0
...
...
@@ -107,43 +107,45 @@ public function update(Request $request, $id)
{
$claim
=
Claim
::
findOrFail
(
$id
);
// Check if any required DB fields are null before allowing update
$requiredFields
=
[
'claim_date'
,
'distance_from'
,
'distance_to'
,
'description'
,
'total_mileage'
];
foreach
(
$requiredFields
as
$field
)
{
if
(
is_null
(
$claim
->
$field
)
||
$claim
->
$field
===
''
)
{
return
redirect
()
->
back
()
->
with
(
'error'
,
'Tidak boleh simpan kerana maklumat tuntutan asal tidak lengkap.'
);
}
}
// Validate new input before updating
// Validate input
$request
->
validate
([
'claim_date'
=>
'required|date'
,
'distance_from'
=>
'required|string'
,
'distance_to'
=>
'required|string'
,
'description'
=>
'required|string'
,
'distance_from'
=>
'required|string
|max:255
'
,
'distance_to'
=>
'required|string
|max:255
'
,
'description'
=>
'required|string
|max:255
'
,
'total_mileage'
=>
'required|numeric|min:0'
,
'toll_amount'
=>
'nullable|numeric|min:0'
,
'others_amount'
=>
'nullable|numeric|min:0'
,
'others_details'
=>
'
required_if:others_amount,>,0|string|nullable
'
,
'others_details'
=>
'
nullable|string|max:255
'
,
]);
// If claim data is valid, update it
// Calculate new claim amount using same logic as store()
$totalMileage
=
(
float
)
$request
->
total_mileage
;
$toll
=
(
float
)
(
$request
->
toll_amount
??
0
);
$others
=
(
float
)
(
$request
->
others_amount
??
0
);
// Deduct first 40km
$claimableMileage
=
$totalMileage
>
40
?
$totalMileage
-
40
:
0
;
// Apply rate logic (same as store)
$rate
=
$claimableMileage
>
500
?
0.50
:
0.55
;
$mileageAmount
=
$claimableMileage
*
$rate
;
$totalClaim
=
$mileageAmount
+
$toll
+
$others
;
// Update the record
$claim
->
update
([
'claim_date'
=>
$request
->
claim_date
,
'distance_from'
=>
$request
->
distance_from
,
'distance_to'
=>
$request
->
distance_to
,
'description'
=>
$request
->
description
,
'total_mileage'
=>
$
request
->
total_m
ileage
,
'toll_amount'
=>
$
request
->
toll_amount
,
'others_amount'
=>
$
request
->
others_amount
,
'total_mileage'
=>
$
totalM
ileage
,
'toll_amount'
=>
$
toll
,
'others_amount'
=>
$
others
,
'others_details'
=>
$request
->
others_details
,
'claimable_mileage'
=>
$claimableMileage
,
'rate'
=>
$rate
,
'mileage_amount'
=>
$mileageAmount
,
'total_claim_amount'
=>
$totalClaim
,
]);
return
redirect
()
->
back
()
->
with
(
'success'
,
'Tuntutan berjaya dikemaskini.'
);
...
...
packages/portal/mileage/src/Http/Controllers/ProjectDirectorController.php
View file @
a3cb80f0
...
...
@@ -167,6 +167,7 @@ public function rejectMonth(Request $request)
if
(
!
in_array
(
$projectId
,
$allowedProjectIds
))
{
return
back
()
->
with
(
'error'
,
'Anda tidak dibenarkan melakukan tindakan ini untuk projek terpilih.'
);
}
$rejectReason
=
$request
->
input
(
'reject_reason'
,
'Tiada sebab diberikan.'
);
// update only claims that belong to this project and month and are Disokong
$rejectedCount
=
Claim
::
where
(
'project_id'
,
$projectId
)
...
...
@@ -176,13 +177,77 @@ public function rejectMonth(Request $request)
->
update
([
'status'
=>
'Ditolak'
,
'rejected_by'
=>
$director
->
id
,
'reject_reason'
=>
$rejectReason
,
'updated_at'
=>
now
(),
]);
if
(
$rejectedCount
>
0
)
{
return
back
()
->
with
(
'success'
,
$rejectedCount
.
' tuntutan telah ditolak untuk projek ini bagi bulan '
.
$monthLabel
.
'.'
);
}
return
back
()
->
with
(
'warning'
,
'Tiada tuntutan "Disokong" untuk ditolak bagi projek ini pada bulan '
.
$monthLabel
.
'.'
);
}
/**
* Approve a single claim.
*/
public
function
approveClaim
(
$id
,
Request
$request
)
{
$director
=
Auth
::
user
();
$claim
=
Claim
::
findOrFail
(
$id
);
// Ensure this director has access to the project
$allowedProjectIds
=
ProjectTeam
::
where
(
'pt_staff_id'
,
$director
->
employeeCode
)
->
where
(
'role_id'
,
7
)
->
pluck
(
'fk_project_id'
)
->
toArray
();
if
(
!
in_array
(
$claim
->
project_id
,
$allowedProjectIds
))
{
return
back
()
->
with
(
'error'
,
'Anda tidak dibenarkan untuk meluluskan tuntutan ini.'
);
}
if
(
$claim
->
status
!==
'Disokong'
)
{
return
back
()
->
with
(
'warning'
,
'Tuntutan ini tidak berada dalam status Disokong.'
);
}
$claim
->
update
([
'status'
=>
'Disahkan'
,
'approved_by'
=>
$director
->
id
,
'updated_at'
=>
now
(),
]);
return
back
()
->
with
(
'success'
,
'Tuntutan pada '
.
$claim
->
claim_date
->
format
(
'd/m/Y'
)
.
' telah disahkan.'
);
}
/**
* Reject a single claim with reason.
*/
public
function
rejectClaim
(
$id
,
Request
$request
)
{
$director
=
Auth
::
user
();
$rejectReason
=
$request
->
input
(
'reject_reason'
,
'Tiada sebab diberikan.'
);
$claim
=
Claim
::
findOrFail
(
$id
);
// Ensure this director has access to this project
$allowedProjectIds
=
ProjectTeam
::
where
(
'pt_staff_id'
,
$director
->
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.'
);
}
if
(
$claim
->
status
!==
'Disokong'
)
{
return
back
()
->
with
(
'warning'
,
'Tuntutan ini tidak berada dalam status Disokong.'
);
}
$claim
->
update
([
'status'
=>
'Ditolak'
,
'rejected_by'
=>
$director
->
id
,
'reject_reason'
=>
$rejectReason
,
'updated_at'
=>
now
(),
]);
return
back
()
->
with
(
'success'
,
'Tuntutan telah ditolak dengan sebab: '
.
$rejectReason
);
}
}
packages/portal/mileage/src/Http/Controllers/ProjectManagerController.php
View file @
a3cb80f0
...
...
@@ -141,49 +141,51 @@ public function approveMonth(Request $request)
*/
public
function
rejectMonth
(
Request
$request
)
{
$manager
=
Auth
::
user
();
$monthLabel
=
$request
->
input
(
'month'
);
$projectId
=
$request
->
input
(
'project_id'
);
$request
->
validate
([
'project_id'
=>
'required|integer'
,
'user_id'
=>
'required|integer'
,
'month'
=>
'required|string'
,
'reject_reason'
=>
'required|string|max:500'
,
]);
if
(
!
$projectId
)
{
return
back
()
->
with
(
'error'
,
'Sila pilih projek untuk ditolak.'
);
}
$month
=
Carbon
::
createFromFormat
(
'Y-m'
,
$request
->
month
);
$claims
=
Claim
::
where
(
'project_id'
,
$request
->
project_id
)
->
where
(
'user_id'
,
$request
->
user_id
)
->
whereMonth
(
'claim_date'
,
$month
->
month
)
->
whereYear
(
'claim_date'
,
$month
->
year
)
->
get
();
// validate month format (expects YYYY-MM)
try
{
if
(
!
preg_match
(
'/^\d{4}-\d{2}$/'
,
$monthLabel
))
{
throw
new
\Exception
(
'Invalid month format'
);
foreach
(
$claims
as
$claim
)
{
$claim
->
status
=
'Ditolak'
;
$claim
->
rejected_by
=
auth
()
->
id
();
$claim
->
reject_reason
=
$request
->
reject_reason
;
$claim
->
save
();
}
$parsedMonth
=
Carbon
::
createFromFormat
(
'Y-m'
,
$monthLabel
);
}
catch
(
\Exception
$e
)
{
return
back
()
->
with
(
'error'
,
'Format bulan tidak sah. Sila pilih semula bulan dalam format YYYY-MM.'
);
return
redirect
()
->
back
()
->
with
(
'success'
,
'Semua tuntutan telah ditolak dengan sebab: '
.
$request
->
reject_reason
);
}
// ensure manager oversees this project
$allowedProjectIds
=
ProjectTeam
::
where
(
'pt_staff_id'
,
$manager
->
employeeCode
)
->
where
(
'role_id'
,
1
)
->
pluck
(
'fk_project_id'
)
->
toArray
();
public
function
approveClaim
(
$id
)
{
$claim
=
Claim
::
findOrFail
(
$id
);
$claim
->
status
=
'Disokong'
;
$claim
->
verified_by
=
auth
()
->
id
();
$claim
->
save
();
if
(
!
in_array
(
$projectId
,
$allowedProjectIds
))
{
return
back
()
->
with
(
'error'
,
'Anda tidak dibenarkan melakukan tindakan ini untuk projek terpilih.'
);
return
back
()
->
with
(
'success'
,
'Tuntutan telah disahkan.'
);
}
// update only claims for this project and month with status 'Diproses'
$rejectedCount
=
Claim
::
where
(
'project_id'
,
$projectId
)
->
whereMonth
(
'claim_date'
,
$parsedMonth
->
month
)
->
whereYear
(
'claim_date'
,
$parsedMonth
->
year
)
->
where
(
'status'
,
'Diproses'
)
->
update
([
'status'
=>
'Ditolak'
,
'rejected_by'
=>
$manager
->
id
,
'updated_at'
=>
now
(),
]);
public
function
rejectClaim
(
Request
$request
,
$id
)
{
$request
->
validate
([
'reject_reason'
=>
'required|string|max:500'
]);
if
(
$rejectedCount
>
0
)
{
return
back
()
->
with
(
'success'
,
$rejectedCount
.
' tuntutan telah ditolak untuk projek ini bagi bulan '
.
$monthLabel
.
'.'
);
}
$claim
=
Claim
::
findOrFail
(
$id
);
$claim
->
status
=
'Ditolak'
;
$claim
->
rejected_by
=
auth
()
->
id
();
$claim
->
reject_reason
=
$request
->
reject_reason
;
$claim
->
save
();
return
back
()
->
with
(
'
warning'
,
'Tiada tuntutan "Diproses" untuk ditolak bagi projek ini pada bulan '
.
$monthLabel
.
'.'
);
return
back
()
->
with
(
'
success'
,
'Tuntutan telah ditolak dengan sebab: '
.
$request
->
reject_reason
);
}
}
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