migrate to gtea from bistbucket

This commit is contained in:
2026-03-15 17:08:23 +07:00
commit 129ca2260c
3716 changed files with 566316 additions and 0 deletions

View File

@@ -0,0 +1,242 @@
<?php
namespace Modules\HRM\Repositories;
use App\Abstracts\EntityRepository;
use Carbon\Carbon;
use Illuminate\Contracts\Pagination\Paginator;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
use Modules\HRM\Models\Attendance;
use Modules\HRM\Models\AttendanceLog;
class AttendanceRepository extends EntityRepository
{
public string $table = Attendance::TABLE_NAME;
protected array $fillableColumns = [
'restaurant_id',
'employee_id',
'date',
'first_clock_in',
'last_clock_out',
'hours_worked',
'status',
'breaks',
'notes',
];
protected function getQuery(): Builder
{
return parent::getQuery();
}
protected function getFilterData(array $filterData = []): array
{
return array_merge([
'perPage' => 10,
'search' => '',
'orderBy' => 'id',
'order' => 'desc',
'with_deleted' => false,
], $filterData);
}
public function getAttendanceQuery(): EloquentBuilder
{
return Attendance::with(['employee', 'logs']);
}
protected function filterSearchQuery(Builder|EloquentBuilder $query, string $searchedText): Builder
{
$searchable = "%$searchedText%";
return $query->where("{$this->table}.status", 'LIKE', $searchable);
}
public function getAll(array $filterData = []): Paginator
{
$filter = $this->getFilterData($filterData);
$query = $this->getAttendanceQuery();
if (! $filter['with_deleted']) {
$query->whereNull("{$this->table}.deleted_at");
}
if (! empty($filter['search'])) {
$query = $this->filterSearchQuery($query, $filter['search']);
}
return $query->orderBy($filter['orderBy'], $filter['order'])
->paginate($filter['perPage']);
}
public function getCount(array $filterData = []): int
{
$filter = $this->getFilterData($filterData);
$query = $this->getQuery();
if (! $filter['with_deleted']) {
$query->whereNull("{$this->table}.deleted_at");
}
return $query->count();
}
/**
* CREATE ATTENDANCE + LOGS
*/
public function create(array $data): Attendance
{
return DB::transaction(function () use ($data) {
$date = $data['date'];
$employee = $data['employee_id'];
// Always fetch today attendance or create new
$attendance = Attendance::firstOrNew([
'employee_id' => $employee,
'date' => $date,
]);
$prepared = $this->prepareForDB($data, $attendance->exists ? $attendance : null);
// Save attendance
$attendance->fill($prepared)->save();
// Insert Punch Logs
if (! empty($data['clock_in'])) {
$this->insertLog($attendance, 'in', $data['clock_in']);
}
if (! empty($data['clock_out'])) {
$this->insertLog($attendance, 'out', $data['clock_out']);
}
// Recalculate working hours after logs
$this->recalculateWorkedHours($attendance);
return $attendance;
});
}
/**
* UPDATE ATTENDANCE + REBUILD LOGS
*/
public function update(int $id, array $data): object
{
return DB::transaction(function () use ($id, $data) {
$attendance = Attendance::findOrFail($id);
$prepared = $this->prepareForDB($data, $attendance);
$attendance->update($prepared);
// Remove old logs
AttendanceLog::where('attendance_id', $id)->delete();
// Add new logs
if (! empty($data['clock_in'])) {
$this->insertLog($attendance, 'in', $data['clock_in']);
}
if (! empty($data['clock_out'])) {
$this->insertLog($attendance, 'out', $data['clock_out']);
}
$this->recalculateWorkedHours($attendance);
return $this->getById($id);
});
}
/**
* MAP REQUEST TO DB FIELDS
*/
public function prepareForDB(array $data, ?object $item = null): array
{
$data = parent::prepareForDB($data, $item);
if (! $item) {
$data['created_at'] = now();
$data['restaurant_id'] = $this->getCurrentRestaurantId();
} else {
$data['updated_at'] = now();
}
if (isset($data['clock_in'])) {
$data['first_clock_in'] = $data['clock_in'];
unset($data['clock_in']);
}
if (isset($data['clock_out'])) {
$data['last_clock_out'] = $data['clock_out'];
unset($data['clock_out']);
}
if (isset($data['breaks']) && is_array($data['breaks'])) {
$data['breaks'] = json_encode($data['breaks']);
}
if (isset($data['notes']) && is_array($data['notes'])) {
$data['notes'] = json_encode($data['notes']);
}
return $data;
}
/**
* INSERT SINGLE PUNCH LOG ENTRY
*/
private function insertLog(Attendance $attendance, string $type, string $time)
{
if (empty($time)) {
return;
}
$dateTime = Carbon::parse($attendance->date.' '.$time)->format('Y-m-d H:i:s');
AttendanceLog::create([
'attendance_id' => $attendance->id,
'employee_id' => $attendance->employee_id,
'restaurant_id' => $attendance->restaurant_id,
'type' => $type, // in or out
'punch_time' => $dateTime,
'created_at' => now(),
]);
}
/**
* RECALCULATE TOTAL WORKED HOURS BASED ON ALL LOGS
*/
private function recalculateWorkedHours(Attendance $attendance)
{
$logs = AttendanceLog::where('attendance_id', $attendance->id)
->orderBy('punch_time')
->get();
if ($logs->count() < 2) {
return;
}
$first = Carbon::parse($logs->first()->punch_time);
$last = Carbon::parse($logs->last()->punch_time);
$hours = round($first->diffInMinutes($last) / 60, 2);
$attendance->update([
'first_clock_in' => $first,
'last_clock_out' => $last,
'hours_worked' => $hours,
]);
}
protected function getExceptionMessages(): array
{
return [
static::MESSAGE_ITEM_DOES_NOT_EXIST_MESSAGE => 'Attendance does not exist.',
static::MESSAGE_ITEM_COULD_NOT_BE_DELETED => 'Attendance could not be deleted.',
];
}
}