base commit

This commit is contained in:
Mekan1206
2026-07-30 17:24:40 +05:00
commit a794dacd39
345 changed files with 29597 additions and 0 deletions

1
database/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
*.sqlite*

View File

@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace Database\Factories;
use App\Enums\BonusType;
use App\Models\Bonus;
use App\Models\Employee;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends Factory<Bonus>
*/
class BonusFactory extends Factory
{
protected $model = Bonus::class;
/**
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'employee_id' => Employee::factory(),
'bonus_date' => fake()->dateTimeBetween('-1 year', 'now'),
'bonus_type' => fake()->randomElement(BonusType::cases()),
'amount' => fake()->randomFloat(2, 100, 5000),
'reason' => fake()->optional()->sentence(),
];
}
}

View File

@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace Database\Factories;
use App\Models\Department;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends Factory<Department>
*/
class DepartmentFactory extends Factory
{
protected $model = Department::class;
/**
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'name' => fake()->unique()->company(),
'code' => strtoupper(fake()->unique()->bothify('??##')),
'description' => fake()->optional()->sentence(),
'is_active' => true,
];
}
public function inactive(): static
{
return $this->state(fn (array $attributes): array => [
'is_active' => false,
]);
}
}

View File

@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace Database\Factories;
use App\Enums\DisciplinarySeverity;
use App\Models\DisciplinaryReport;
use App\Models\Employee;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends Factory<DisciplinaryReport>
*/
class DisciplinaryReportFactory extends Factory
{
protected $model = DisciplinaryReport::class;
/**
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'employee_id' => Employee::factory(),
'report_date' => fake()->dateTimeBetween('-1 year', 'now'),
'title' => fake()->sentence(4),
'description' => fake()->paragraph(),
'severity' => fake()->randomElement(DisciplinarySeverity::cases()),
'attachment_path' => null,
'created_by' => User::factory(),
];
}
}

View File

@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace Database\Factories;
use App\Enums\DocumentType;
use App\Models\Employee;
use App\Models\EmployeeDocument;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends Factory<EmployeeDocument>
*/
class EmployeeDocumentFactory extends Factory
{
protected $model = EmployeeDocument::class;
/**
* @return array<string, mixed>
*/
public function definition(): array
{
$type = fake()->randomElement(DocumentType::cases());
return [
'employee_id' => Employee::factory(),
'document_type' => $type,
'title' => $type->label().' - '.fake()->word(),
'file_path' => 'documents/'.fake()->uuid().'.pdf',
'uploaded_at' => fake()->dateTimeBetween('-1 year', 'now'),
];
}
}

View File

@@ -0,0 +1,72 @@
<?php
declare(strict_types=1);
namespace Database\Factories;
use App\Enums\EmploymentStatus;
use App\Enums\Gender;
use App\Models\Department;
use App\Models\Employee;
use App\Models\Position;
use App\Models\Shift;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends Factory<Employee>
*/
class EmployeeFactory extends Factory
{
protected $model = Employee::class;
/**
* @return array<string, mixed>
*/
public function definition(): array
{
$hireDate = fake()->dateTimeBetween('-10 years', '-1 month');
$birthDate = fake()->dateTimeBetween('-55 years', '-20 years');
$employmentStatus = fake()->randomElement(EmploymentStatus::cases());
$gender = fake()->randomElement(Gender::cases());
return [
'employee_number' => 'EMP-'.fake()->unique()->numerify('#####'),
'full_name' => fake()->name($gender === Gender::Female ? 'female' : 'male'),
'national_id' => fake()->optional(0.8)->numerify('##########'),
'gender' => $gender,
'birth_date' => $birthDate,
'phone' => '+993 61 92 92 48',
'address' => fake()->optional()->address(),
'department_id' => Department::factory(),
'position_id' => Position::factory(),
'shift_id' => Shift::factory(),
'employment_status' => $employmentStatus,
'hire_date' => $hireDate,
'termination_date' => $employmentStatus === EmploymentStatus::Terminated
? fake()->dateTimeBetween($hireDate, 'now')
: null,
'notes' => fake()->optional(0.3)->sentence(),
'profile_photo_path' => null,
];
}
public function active(): static
{
return $this->state(fn (array $attributes): array => [
'employment_status' => EmploymentStatus::Active,
'termination_date' => null,
]);
}
public function terminated(): static
{
return $this->state(function (array $attributes): array {
$hireDate = $attributes['hire_date'] ?? fake()->dateTimeBetween('-5 years', '-1 year');
return [
'employment_status' => EmploymentStatus::Terminated,
'termination_date' => fake()->dateTimeBetween($hireDate, 'now'),
];
});
}
}

View File

@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Database\Factories;
use App\Models\Employee;
use App\Models\Explanation;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends Factory<Explanation>
*/
class ExplanationFactory extends Factory
{
protected $model = Explanation::class;
/**
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'employee_id' => Employee::factory(),
'explanation_date' => fake()->dateTimeBetween('-1 year', 'now'),
'reason' => fake()->sentence(),
'description' => fake()->paragraph(),
'attachment_path' => null,
];
}
}

View File

@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Database\Factories;
use App\Models\Employee;
use App\Models\Gift;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends Factory<Gift>
*/
class GiftFactory extends Factory
{
protected $model = Gift::class;
/**
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'employee_id' => Employee::factory(),
'gift_date' => fake()->dateTimeBetween('-1 year', 'now'),
'gift_name' => fake()->words(3, true),
'value' => fake()->randomFloat(2, 50, 1000),
'reason' => fake()->optional()->sentence(),
];
}
}

View File

@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace Database\Factories;
use App\Models\Position;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends Factory<Position>
*/
class PositionFactory extends Factory
{
protected $model = Position::class;
/**
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'name' => fake()->unique()->jobTitle(),
'description' => fake()->optional()->sentence(),
];
}
}

View File

@@ -0,0 +1,84 @@
<?php
declare(strict_types=1);
namespace Database\Factories;
use App\Models\Shift;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends Factory<Shift>
*/
class ShiftFactory extends Factory
{
protected $model = Shift::class;
/**
* @return array<string, mixed>
*/
public function definition(): array
{
$code = fake()->randomElement(['A', 'B', 'C', 'Night']);
$times = match ($code) {
'A' => ['starts_at' => '06:00:00', 'ends_at' => '14:00:00'],
'B' => ['starts_at' => '14:00:00', 'ends_at' => '22:00:00'],
'C' => ['starts_at' => '22:00:00', 'ends_at' => '06:00:00'],
default => ['starts_at' => '22:00:00', 'ends_at' => '06:00:00'],
};
return [
'name' => match ($code) {
'A' => 'Morning Shift',
'B' => 'Afternoon Shift',
'C' => 'Evening Shift',
default => 'Night Shift',
},
'code' => $code.'-'.fake()->unique()->numerify('###'),
'starts_at' => $times['starts_at'],
'ends_at' => $times['ends_at'],
'description' => fake()->optional()->sentence(),
'is_active' => true,
];
}
public function shiftA(): static
{
return $this->state(fn (array $attributes): array => [
'name' => 'Shift A',
'code' => 'A',
'starts_at' => '06:00:00',
'ends_at' => '14:00:00',
]);
}
public function shiftB(): static
{
return $this->state(fn (array $attributes): array => [
'name' => 'Shift B',
'code' => 'B',
'starts_at' => '14:00:00',
'ends_at' => '22:00:00',
]);
}
public function shiftC(): static
{
return $this->state(fn (array $attributes): array => [
'name' => 'Shift C',
'code' => 'C',
'starts_at' => '22:00:00',
'ends_at' => '06:00:00',
]);
}
public function night(): static
{
return $this->state(fn (array $attributes): array => [
'name' => 'Night Shift',
'code' => 'Night',
'starts_at' => '22:00:00',
'ends_at' => '06:00:00',
]);
}
}

View File

@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace Database\Factories;
use App\Models\Employee;
use App\Models\SickLeave;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends Factory<SickLeave>
*/
class SickLeaveFactory extends Factory
{
protected $model = SickLeave::class;
/**
* @return array<string, mixed>
*/
public function definition(): array
{
$startDate = fake()->dateTimeBetween('-3 months', 'now');
$days = fake()->numberBetween(1, 10);
$endDate = (clone $startDate)->modify("+{$days} days");
return [
'employee_id' => Employee::factory(),
'start_date' => $startDate,
'end_date' => $endDate,
'days' => $days,
'medical_institution' => fake()->optional()->company().' Hospital',
'reason' => fake()->optional()->sentence(),
'document_path' => null,
];
}
}

View File

@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace Database\Factories;
use App\Enums\ApprovalStatus;
use App\Models\Employee;
use App\Models\UnpaidLeave;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends Factory<UnpaidLeave>
*/
class UnpaidLeaveFactory extends Factory
{
protected $model = UnpaidLeave::class;
/**
* @return array<string, mixed>
*/
public function definition(): array
{
$startDate = fake()->dateTimeBetween('-3 months', '+1 month');
$days = fake()->numberBetween(1, 30);
$endDate = (clone $startDate)->modify("+{$days} days");
$status = fake()->randomElement(ApprovalStatus::cases());
return [
'employee_id' => Employee::factory(),
'start_date' => $startDate,
'end_date' => $endDate,
'days' => $days,
'reason' => fake()->optional()->sentence(),
'status' => $status,
'approved_by' => $status === ApprovalStatus::Pending ? null : User::factory(),
'approved_at' => $status === ApprovalStatus::Pending ? null : fake()->dateTimeBetween($startDate, 'now'),
];
}
public function pending(): static
{
return $this->state(fn (array $attributes): array => [
'status' => ApprovalStatus::Pending,
'approved_by' => null,
'approved_at' => null,
]);
}
}

View File

@@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace Database\Factories;
use App\Models\Department;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
/**
* @extends Factory<User>
*/
class UserFactory extends Factory
{
protected $model = User::class;
/**
* The current password being used by the factory.
*/
protected static ?string $password;
/**
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'name' => fake()->name(),
'email' => fake()->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => static::$password ??= Hash::make('password'),
'remember_token' => Str::random(10),
'department_id' => Department::factory(),
];
}
public function unverified(): static
{
return $this->state(fn (array $attributes): array => [
'email_verified_at' => null,
]);
}
public function withoutDepartment(): static
{
return $this->state(fn (array $attributes): array => [
'department_id' => null,
]);
}
}

View File

@@ -0,0 +1,65 @@
<?php
declare(strict_types=1);
namespace Database\Factories;
use App\Enums\ApprovalStatus;
use App\Enums\VacationType;
use App\Models\Employee;
use App\Models\User;
use App\Models\Vacation;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends Factory<Vacation>
*/
class VacationFactory extends Factory
{
protected $model = Vacation::class;
/**
* @return array<string, mixed>
*/
public function definition(): array
{
$startDate = fake()->dateTimeBetween('-6 months', '-1 day');
$days = fake()->numberBetween(1, 14);
$endDate = (clone $startDate)->modify("+{$days} days");
$status = fake()->randomElement(ApprovalStatus::cases());
return [
'employee_id' => Employee::factory(),
'type' => fake()->randomElement(VacationType::cases()),
'start_date' => $startDate,
'end_date' => $endDate,
'return_date' => (clone $endDate)->modify('+1 day'),
'days' => $days,
'status' => $status,
'reason' => fake()->optional()->sentence(),
'attachment_path' => null,
'approved_by' => $status === ApprovalStatus::Pending ? null : User::factory(),
'approved_at' => $status === ApprovalStatus::Pending
? null
: fake()->dateTimeBetween($startDate, 'now'),
];
}
public function pending(): static
{
return $this->state(fn (array $attributes): array => [
'status' => ApprovalStatus::Pending,
'approved_by' => null,
'approved_at' => null,
]);
}
public function approved(): static
{
return $this->state(fn (array $attributes): array => [
'status' => ApprovalStatus::Approved,
'approved_by' => User::factory(),
'approved_at' => now(),
]);
}
}

View File

@@ -0,0 +1,49 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
Schema::create('password_reset_tokens', function (Blueprint $table) {
$table->string('email')->primary();
$table->string('token');
$table->timestamp('created_at')->nullable();
});
Schema::create('sessions', function (Blueprint $table) {
$table->string('id')->primary();
$table->foreignId('user_id')->nullable()->index();
$table->string('ip_address', 45)->nullable();
$table->text('user_agent')->nullable();
$table->longText('payload');
$table->integer('last_activity')->index();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('users');
Schema::dropIfExists('password_reset_tokens');
Schema::dropIfExists('sessions');
}
};

View File

@@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('cache', function (Blueprint $table) {
$table->string('key')->primary();
$table->mediumText('value');
$table->bigInteger('expiration')->index();
});
Schema::create('cache_locks', function (Blueprint $table) {
$table->string('key')->primary();
$table->string('owner');
$table->bigInteger('expiration')->index();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('cache');
Schema::dropIfExists('cache_locks');
}
};

View File

@@ -0,0 +1,59 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('jobs', function (Blueprint $table) {
$table->id();
$table->string('queue')->index();
$table->longText('payload');
$table->unsignedSmallInteger('attempts');
$table->unsignedInteger('reserved_at')->nullable();
$table->unsignedInteger('available_at');
$table->unsignedInteger('created_at');
});
Schema::create('job_batches', function (Blueprint $table) {
$table->string('id')->primary();
$table->string('name');
$table->integer('total_jobs');
$table->integer('pending_jobs');
$table->integer('failed_jobs');
$table->longText('failed_job_ids');
$table->mediumText('options')->nullable();
$table->integer('cancelled_at')->nullable();
$table->integer('created_at');
$table->integer('finished_at')->nullable();
});
Schema::create('failed_jobs', function (Blueprint $table) {
$table->id();
$table->string('uuid')->unique();
$table->string('connection');
$table->string('queue');
$table->longText('payload');
$table->longText('exception');
$table->timestamp('failed_at')->useCurrent();
$table->index(['connection', 'queue', 'failed_at']);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('jobs');
Schema::dropIfExists('job_batches');
Schema::dropIfExists('failed_jobs');
}
};

View File

@@ -0,0 +1,27 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateActivityLogTable extends Migration
{
public function up()
{
Schema::connection(config('activitylog.database_connection'))->create(config('activitylog.table_name'), function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('log_name')->nullable();
$table->text('description');
$table->nullableMorphs('subject', 'subject');
$table->nullableMorphs('causer', 'causer');
$table->json('properties')->nullable();
$table->timestamps();
$table->index('log_name');
});
}
public function down()
{
Schema::connection(config('activitylog.database_connection'))->dropIfExists(config('activitylog.table_name'));
}
}

View File

@@ -0,0 +1,22 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddEventColumnToActivityLogTable extends Migration
{
public function up()
{
Schema::connection(config('activitylog.database_connection'))->table(config('activitylog.table_name'), function (Blueprint $table) {
$table->string('event')->nullable()->after('subject_type');
});
}
public function down()
{
Schema::connection(config('activitylog.database_connection'))->table(config('activitylog.table_name'), function (Blueprint $table) {
$table->dropColumn('event');
});
}
}

View File

@@ -0,0 +1,137 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
$teams = config('permission.teams');
$tableNames = config('permission.table_names');
$columnNames = config('permission.column_names');
$pivotRole = $columnNames['role_pivot_key'] ?? 'role_id';
$pivotPermission = $columnNames['permission_pivot_key'] ?? 'permission_id';
throw_if(empty($tableNames), 'Error: config/permission.php not loaded. Run [php artisan config:clear] and try again.');
throw_if($teams && empty($columnNames['team_foreign_key'] ?? null), 'Error: team_foreign_key on config/permission.php not loaded. Run [php artisan config:clear] and try again.');
/**
* See `docs/prerequisites.md` for suggested lengths on 'name' and 'guard_name' if "1071 Specified key was too long" errors are encountered.
*/
Schema::create($tableNames['permissions'], static function (Blueprint $table) {
$table->id(); // permission id
$table->string('name');
$table->string('guard_name');
$table->timestamps();
$table->unique(['name', 'guard_name']);
});
/**
* See `docs/prerequisites.md` for suggested lengths on 'name' and 'guard_name' if "1071 Specified key was too long" errors are encountered.
*/
Schema::create($tableNames['roles'], static function (Blueprint $table) use ($teams, $columnNames) {
$table->id(); // role id
if ($teams || config('permission.testing')) { // permission.testing is a fix for sqlite testing
$table->unsignedBigInteger($columnNames['team_foreign_key'])->nullable();
$table->index($columnNames['team_foreign_key'], 'roles_team_foreign_key_index');
}
$table->string('name');
$table->string('guard_name');
$table->timestamps();
if ($teams || config('permission.testing')) {
$table->unique([$columnNames['team_foreign_key'], 'name', 'guard_name']);
} else {
$table->unique(['name', 'guard_name']);
}
});
Schema::create($tableNames['model_has_permissions'], static function (Blueprint $table) use ($tableNames, $columnNames, $pivotPermission, $teams) {
$table->unsignedBigInteger($pivotPermission);
$table->string('model_type');
$table->unsignedBigInteger($columnNames['model_morph_key']);
$table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_permissions_model_id_model_type_index');
$table->foreign($pivotPermission)
->references('id') // permission id
->on($tableNames['permissions'])
->cascadeOnDelete();
if ($teams) {
$table->unsignedBigInteger($columnNames['team_foreign_key']);
$table->index($columnNames['team_foreign_key'], 'model_has_permissions_team_foreign_key_index');
$table->primary([$columnNames['team_foreign_key'], $pivotPermission, $columnNames['model_morph_key'], 'model_type'],
'model_has_permissions_permission_model_type_primary');
} else {
$table->primary([$pivotPermission, $columnNames['model_morph_key'], 'model_type'],
'model_has_permissions_permission_model_type_primary');
}
});
Schema::create($tableNames['model_has_roles'], static function (Blueprint $table) use ($tableNames, $columnNames, $pivotRole, $teams) {
$table->unsignedBigInteger($pivotRole);
$table->string('model_type');
$table->unsignedBigInteger($columnNames['model_morph_key']);
$table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_roles_model_id_model_type_index');
$table->foreign($pivotRole)
->references('id') // role id
->on($tableNames['roles'])
->cascadeOnDelete();
if ($teams) {
$table->unsignedBigInteger($columnNames['team_foreign_key']);
$table->index($columnNames['team_foreign_key'], 'model_has_roles_team_foreign_key_index');
$table->primary([$columnNames['team_foreign_key'], $pivotRole, $columnNames['model_morph_key'], 'model_type'],
'model_has_roles_role_model_type_primary');
} else {
$table->primary([$pivotRole, $columnNames['model_morph_key'], 'model_type'],
'model_has_roles_role_model_type_primary');
}
});
Schema::create($tableNames['role_has_permissions'], static function (Blueprint $table) use ($tableNames, $pivotRole, $pivotPermission) {
$table->unsignedBigInteger($pivotPermission);
$table->unsignedBigInteger($pivotRole);
$table->foreign($pivotPermission)
->references('id') // permission id
->on($tableNames['permissions'])
->cascadeOnDelete();
$table->foreign($pivotRole)
->references('id') // role id
->on($tableNames['roles'])
->cascadeOnDelete();
$table->primary([$pivotPermission, $pivotRole], 'role_has_permissions_permission_id_role_id_primary');
});
app('cache')
->store(config('permission.cache.store') != 'default' ? config('permission.cache.store') : null)
->forget(config('permission.cache.key'));
}
/**
* Reverse the migrations.
*/
public function down(): void
{
$tableNames = config('permission.table_names');
throw_if(empty($tableNames), 'Error: config/permission.php not found and defaults could not be merged. Please publish the package configuration before proceeding, or drop the tables manually.');
Schema::dropIfExists($tableNames['role_has_permissions']);
Schema::dropIfExists($tableNames['model_has_roles']);
Schema::dropIfExists($tableNames['model_has_permissions']);
Schema::dropIfExists($tableNames['roles']);
Schema::dropIfExists($tableNames['permissions']);
}
};

View File

@@ -0,0 +1,22 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddBatchUuidColumnToActivityLogTable extends Migration
{
public function up()
{
Schema::connection(config('activitylog.database_connection'))->table(config('activitylog.table_name'), function (Blueprint $table) {
$table->uuid('batch_uuid')->nullable()->after('properties');
});
}
public function down()
{
Schema::connection(config('activitylog.database_connection'))->table(config('activitylog.table_name'), function (Blueprint $table) {
$table->dropColumn('batch_uuid');
});
}
}

View File

@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('departments', function (Blueprint $table): void {
$table->id();
$table->string('name')->unique();
$table->string('code')->unique();
$table->text('description')->nullable();
$table->boolean('is_active')->default(true);
$table->timestamps();
$table->softDeletes();
$table->index('is_active');
});
}
public function down(): void
{
Schema::dropIfExists('departments');
}
};

View File

@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('positions', function (Blueprint $table): void {
$table->id();
$table->string('name')->unique();
$table->text('description')->nullable();
$table->timestamps();
$table->softDeletes();
});
}
public function down(): void
{
Schema::dropIfExists('positions');
}
};

View File

@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('shifts', function (Blueprint $table): void {
$table->id();
$table->string('name');
$table->string('code')->unique();
$table->time('starts_at')->nullable();
$table->time('ends_at')->nullable();
$table->text('description')->nullable();
$table->boolean('is_active')->default(true);
$table->timestamps();
$table->softDeletes();
$table->index('is_active');
});
}
public function down(): void
{
Schema::dropIfExists('shifts');
}
};

View File

@@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('employees', function (Blueprint $table): void {
$table->id();
$table->string('employee_number')->unique();
$table->string('full_name');
$table->string('national_id')->nullable();
$table->string('gender');
$table->date('birth_date');
$table->string('phone');
$table->text('address')->nullable();
$table->foreignId('department_id')->constrained()->restrictOnDelete();
$table->foreignId('position_id')->constrained()->restrictOnDelete();
$table->foreignId('shift_id')->constrained()->restrictOnDelete();
$table->string('employment_status')->default('active');
$table->date('hire_date');
$table->date('termination_date')->nullable();
$table->text('notes')->nullable();
$table->string('profile_photo_path')->nullable();
$table->timestamps();
$table->softDeletes();
$table->index('full_name');
$table->index('phone');
$table->index('employment_status');
$table->index(['department_id', 'employment_status']);
});
}
public function down(): void
{
Schema::dropIfExists('employees');
}
};

View File

@@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('vacations', function (Blueprint $table): void {
$table->id();
$table->foreignId('employee_id')->constrained()->cascadeOnDelete();
$table->string('type');
$table->date('start_date');
$table->date('end_date');
$table->date('return_date')->nullable();
$table->unsignedInteger('days');
$table->string('status')->default('pending');
$table->text('reason')->nullable();
$table->string('attachment_path')->nullable();
$table->foreignId('approved_by')->nullable()->constrained('users')->nullOnDelete();
$table->timestamp('approved_at')->nullable();
$table->timestamps();
$table->softDeletes();
$table->index('status');
$table->index('start_date');
$table->index('end_date');
$table->index(['employee_id', 'start_date']);
});
}
public function down(): void
{
Schema::dropIfExists('vacations');
}
};

View File

@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('sick_leaves', function (Blueprint $table): void {
$table->id();
$table->foreignId('employee_id')->constrained()->cascadeOnDelete();
$table->date('start_date');
$table->date('end_date');
$table->unsignedInteger('days');
$table->string('medical_institution')->nullable();
$table->text('reason')->nullable();
$table->string('document_path')->nullable();
$table->timestamps();
$table->softDeletes();
$table->index(['employee_id', 'start_date']);
$table->index('start_date');
$table->index('end_date');
});
}
public function down(): void
{
Schema::dropIfExists('sick_leaves');
}
};

View File

@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('unpaid_leaves', function (Blueprint $table): void {
$table->id();
$table->foreignId('employee_id')->constrained()->cascadeOnDelete();
$table->date('start_date');
$table->date('end_date');
$table->unsignedInteger('days');
$table->text('reason')->nullable();
$table->string('status')->default('pending');
$table->foreignId('approved_by')->nullable()->constrained('users')->nullOnDelete();
$table->timestamp('approved_at')->nullable();
$table->timestamps();
$table->softDeletes();
$table->index('status');
$table->index(['employee_id', 'start_date']);
});
}
public function down(): void
{
Schema::dropIfExists('unpaid_leaves');
}
};

View File

@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('disciplinary_reports', function (Blueprint $table): void {
$table->id();
$table->foreignId('employee_id')->constrained()->cascadeOnDelete();
$table->date('report_date');
$table->string('title');
$table->text('description');
$table->string('severity');
$table->string('attachment_path')->nullable();
$table->foreignId('created_by')->nullable()->constrained('users')->nullOnDelete();
$table->timestamps();
$table->softDeletes();
$table->index('report_date');
$table->index('severity');
$table->index(['employee_id', 'report_date']);
});
}
public function down(): void
{
Schema::dropIfExists('disciplinary_reports');
}
};

View File

@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('explanations', function (Blueprint $table): void {
$table->id();
$table->foreignId('employee_id')->constrained()->cascadeOnDelete();
$table->date('explanation_date');
$table->string('reason');
$table->text('description');
$table->string('attachment_path')->nullable();
$table->timestamps();
$table->softDeletes();
$table->index(['employee_id', 'explanation_date']);
});
}
public function down(): void
{
Schema::dropIfExists('explanations');
}
};

View File

@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('bonuses', function (Blueprint $table): void {
$table->id();
$table->foreignId('employee_id')->constrained()->cascadeOnDelete();
$table->date('bonus_date');
$table->string('bonus_type');
$table->decimal('amount', 10, 2);
$table->text('reason')->nullable();
$table->timestamps();
$table->softDeletes();
$table->index(['employee_id', 'bonus_date']);
$table->index('bonus_type');
});
}
public function down(): void
{
Schema::dropIfExists('bonuses');
}
};

View File

@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('gifts', function (Blueprint $table): void {
$table->id();
$table->foreignId('employee_id')->constrained()->cascadeOnDelete();
$table->date('gift_date');
$table->string('gift_name');
$table->decimal('value', 10, 2)->default(0);
$table->text('reason')->nullable();
$table->timestamps();
$table->softDeletes();
$table->index(['employee_id', 'gift_date']);
});
}
public function down(): void
{
Schema::dropIfExists('gifts');
}
};

View File

@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('employee_documents', function (Blueprint $table): void {
$table->id();
$table->foreignId('employee_id')->constrained()->cascadeOnDelete();
$table->string('document_type');
$table->string('title');
$table->string('file_path');
$table->timestamp('uploaded_at');
$table->timestamps();
$table->softDeletes();
$table->index(['employee_id', 'document_type']);
});
}
public function down(): void
{
Schema::dropIfExists('employee_documents');
}
};

View File

@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('users', function (Blueprint $table): void {
$table->foreignId('department_id')->nullable()->after('password')->constrained()->nullOnDelete();
});
}
public function down(): void
{
Schema::table('users', function (Blueprint $table): void {
$table->dropConstrainedForeignId('department_id');
});
}
};

View File

@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace Database\Seeders;
use App\Models\Department;
use App\Models\User;
use BezhanSalleh\FilamentShield\Support\Utils;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Hash;
class AdminUserSeeder extends Seeder
{
public function run(): void
{
$admin = User::query()->updateOrCreate(
['email' => 'admin@company.com'],
[
'name' => 'Admin',
'password' => Hash::make('password'),
'email_verified_at' => now(),
'department_id' => Department::query()->where('code', 'ADM')->value('id'),
],
);
$admin->assignRole(Utils::getSuperAdminName());
}
}

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
use WithoutModelEvents;
public function run(): void
{
$this->call([
DepartmentSeeder::class,
PositionSeeder::class,
ShiftSeeder::class,
RoleSeeder::class,
AdminUserSeeder::class,
DemoDataSeeder::class,
]);
}
}

View File

@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace Database\Seeders;
use App\Models\Employee;
use Illuminate\Database\Seeder;
class DemoDataSeeder extends Seeder
{
public function run(): void
{
if (Employee::query()->exists()) {
return;
}
Employee::factory()
->count(25)
->create();
}
}

View File

@@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
namespace Database\Seeders;
use App\Models\Department;
use Illuminate\Database\Seeder;
class DepartmentSeeder extends Seeder
{
/**
* @var array<string, array{name: string, code: string, description: string}>
*/
private const DEPARTMENTS = [
'production' => [
'name' => 'Production',
'code' => 'PROD',
'description' => 'Manufacturing and production operations.',
],
'warehouse' => [
'name' => 'Warehouse',
'code' => 'WH',
'description' => 'Inventory and logistics.',
],
'administration' => [
'name' => 'Administration',
'code' => 'ADM',
'description' => 'General administration and office management.',
],
'hr' => [
'name' => 'HR',
'code' => 'HR',
'description' => 'Human resources and personnel management.',
],
'accounting' => [
'name' => 'Accounting',
'code' => 'ACC',
'description' => 'Finance and accounting.',
],
];
public function run(): void
{
foreach (self::DEPARTMENTS as $department) {
Department::query()->updateOrCreate(
['code' => $department['code']],
[
'name' => $department['name'],
'description' => $department['description'],
'is_active' => true,
],
);
}
}
}

View File

@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace Database\Seeders;
use App\Models\Position;
use Illuminate\Database\Seeder;
class PositionSeeder extends Seeder
{
/**
* @var list<string>
*/
private const POSITIONS = [
'Operator',
'Mechanic',
'Electrician',
'Cleaner',
'Manager',
];
public function run(): void
{
foreach (self::POSITIONS as $positionName) {
Position::query()->firstOrCreate(
['name' => $positionName],
['description' => null],
);
}
}
}

View File

@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
namespace Database\Seeders;
use App\Models\User;
use BezhanSalleh\FilamentShield\Support\Utils;
use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Role;
class RoleSeeder extends Seeder
{
/**
* @var list<string>
*/
private const ROLES = [
'administrator',
'hr_manager',
'supervisor',
'department_manager',
'viewer',
];
public function run(): void
{
Utils::createRole();
$guardName = $this->guardName();
foreach (self::ROLES as $roleName) {
Role::firstOrCreate([
'name' => $roleName,
'guard_name' => $guardName,
]);
}
$admin = User::query()->where('email', 'admin@company.com')->first();
if ($admin !== null) {
$admin->assignRole(Utils::getSuperAdminName());
}
}
private function guardName(): string
{
$guard = Utils::getFilamentAuthGuard();
return $guard !== '' ? $guard : (string) config('auth.defaults.guard', 'web');
}
}

View File

@@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace Database\Seeders;
use App\Models\Shift;
use Illuminate\Database\Seeder;
class ShiftSeeder extends Seeder
{
/**
* @var array<string, array{name: string, starts_at: string, ends_at: string}>
*/
private const SHIFTS = [
'A' => [
'name' => 'Shift A',
'starts_at' => '06:00:00',
'ends_at' => '14:00:00',
],
'B' => [
'name' => 'Shift B',
'starts_at' => '14:00:00',
'ends_at' => '22:00:00',
],
'C' => [
'name' => 'Shift C',
'starts_at' => '22:00:00',
'ends_at' => '06:00:00',
],
'Night' => [
'name' => 'Night Shift',
'starts_at' => '22:00:00',
'ends_at' => '06:00:00',
],
];
public function run(): void
{
foreach (self::SHIFTS as $code => $shift) {
Shift::query()->updateOrCreate(
['code' => $code],
[
'name' => $shift['name'],
'starts_at' => $shift['starts_at'],
'ends_at' => $shift['ends_at'],
'description' => null,
'is_active' => true,
],
);
}
}
}