Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 59
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
AuditLogsController
0.00% covered (danger)
0.00%
0 / 59
0.00% covered (danger)
0.00%
0 / 4
506
0.00% covered (danger)
0.00%
0 / 1
 list
0.00% covered (danger)
0.00%
0 / 26
0.00% covered (danger)
0.00%
0 / 1
110
 history
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
30
 show
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
20
 callerIsAdmin
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2
3namespace App\Http\Controllers;
4
5use App\Models\TblAuditLogs;
6use Illuminate\Http\Request;
7use Illuminate\Support\Facades\DB;
8
9class AuditLogsController extends Controller
10{
11    /**
12     * GET /api/audit-logs
13     *
14     * Filters (all optional):
15     *   user_id          integer — exact match
16     *   event            string  — created | updated | deleted
17     *   auditable_type   string  — full class (e.g. App\Models\TblQuotations)
18     *   model            string  — short alias matched against auditable_type
19     *                              (e.g. "TblQuotations" or "Quotations")
20     *   auditable_id     integer — target row id
21     *   date_from        date    — created_at >= (YYYY-MM-DD)
22     *   date_to          date    — created_at <= (YYYY-MM-DD)
23     *   per_page         int     — page size (default 50, max 200)
24     *   page             int     — page number
25     */
26    public function list(Request $request)
27    {
28        if (! $this->callerIsAdmin($request)) {
29            return response(['message' => 'KO', 'error' => 'Forbidden'], 403);
30        }
31
32        try {
33            $q = TblAuditLogs::query()->orderByDesc('created_at');
34
35            if ($request->filled('user_id')) {
36                $q->where('user_id', (int) $request->query('user_id'));
37            }
38
39            if ($request->filled('event')) {
40                $q->where('event', strtolower($request->query('event')));
41            }
42
43            if ($request->filled('auditable_type')) {
44                $q->where('auditable_type', $request->query('auditable_type'));
45            } elseif ($request->filled('model')) {
46                $q->where('auditable_type', 'like', '%' . $request->query('model'));
47            }
48
49            if ($request->filled('auditable_id')) {
50                $q->where('auditable_id', (int) $request->query('auditable_id'));
51            }
52
53            if ($request->filled('date_from')) {
54                $q->whereDate('created_at', '>=', $request->query('date_from'));
55            }
56
57            if ($request->filled('date_to')) {
58                $q->whereDate('created_at', '<=', $request->query('date_to'));
59            }
60
61            $perPage = min(max((int) $request->query('per_page', 50), 1), 200);
62
63            return response([
64                'message' => 'OK',
65                'data' => $q->paginate($perPage),
66            ]);
67        } catch (\Exception $e) {
68            /** @disregard P1014 */
69            $e->exceptionCode = 'LIST_AUDIT_LOGS_EXCEPTION';
70            report($e);
71
72            return response(['message' => 'KO', 'error' => $e->getMessage()]);
73        }
74    }
75
76    /**
77     * GET /api/audit-logs/history?model=TblQuotations&id=567
78     *
79     * Convenience endpoint: full timeline for one record across all fields.
80     */
81    public function history(Request $request)
82    {
83        if (! $this->callerIsAdmin($request)) {
84            return response(['message' => 'KO', 'error' => 'Forbidden'], 403);
85        }
86
87        if (! $request->filled('model') || ! $request->filled('id')) {
88            return response(['message' => 'KO', 'error' => 'model and id are required'], 422);
89        }
90
91        try {
92            $rows = TblAuditLogs::query()
93                ->where('auditable_type', 'like', '%' . $request->query('model'))
94                ->where('auditable_id', (int) $request->query('id'))
95                ->orderByDesc('created_at')
96                ->limit(500)
97                ->get();
98
99            return response(['message' => 'OK', 'data' => $rows]);
100        } catch (\Exception $e) {
101            /** @disregard P1014 */
102            $e->exceptionCode = 'AUDIT_LOG_HISTORY_EXCEPTION';
103            report($e);
104
105            return response(['message' => 'KO', 'error' => $e->getMessage()]);
106        }
107    }
108
109    /**
110     * GET /api/audit-logs/{id}
111     */
112    public function show(Request $request, int $id)
113    {
114        if (! $this->callerIsAdmin($request)) {
115            return response(['message' => 'KO', 'error' => 'Forbidden'], 403);
116        }
117
118        try {
119            $row = TblAuditLogs::find($id);
120            if (! $row) {
121                return response(['message' => 'KO', 'error' => 'Not found'], 404);
122            }
123
124            return response(['message' => 'OK', 'data' => $row]);
125        } catch (\Exception $e) {
126            /** @disregard P1014 */
127            $e->exceptionCode = 'SHOW_AUDIT_LOG_EXCEPTION';
128            report($e);
129
130            return response(['message' => 'KO', 'error' => $e->getMessage()]);
131        }
132    }
133
134    /**
135     * Admin gate. Mirrors IncentivesController until FIRE-955 lands an
136     * EnsureRole middleware.
137     */
138    private function callerIsAdmin(Request $request): bool
139    {
140        $userId = (int) $request->header('User-ID');
141        if (! $userId) {
142            return false;
143        }
144        $roleId = DB::table('tbl_users')->where('id', $userId)->value('role_id');
145        if (! $roleId) {
146            return false;
147        }
148        $roleName = DB::table('tbl_roles')->where('role_id', $roleId)->value('name');
149
150        return $roleName === 'Admin';
151    }
152}