Ruby Module
Overview
Ruby is the Human Resource management module of the OATERS ERP system. It provides comprehensive HR functionality including employee management, payroll processing, attendance tracking, leave management, recruitment, and organizational structure management. Ruby heavily integrates with the Common module for contact management and employee information.
Business Functionality
Core Features
1. Employee Management
- Employee Profiles: Complete employee information via Common module Contact integration
- Department Organization: Organize employees by departments with hierarchical structure
- Document Management: Store and manage employee/applicant documents
- Reporting Relationships: Define manager-subordinate hierarchies within departments
2. Payroll Management
- Salary Structure: Define salary types (hourly, monthly, annual)
- Payment Components: Manage earnings, allowances, and incentives
- Payroll Processing: Calculate and process payroll payments
- Component Tracking: Track individual salary components per employee
- Multi-language Support: Payroll component names in multiple languages
3. Attendance & Time Tracking
- Punch In/Out: Record employee check-in and check-out times
- Attendance Records: Track employee presence with timestamp tracking
- Simple Time Tracking: Streamlined punch system with in/out types
4. Leave Management
- Leave Applications: Submit and manage leave requests
- Leave Types: Support for various leave categories (sick, annual, casual)
- Leave Status: Approve or reject leave applications
- Date Range Tracking: Track leave start and end dates
5. Recruitment Management
- Job Vacancies: Post and manage job openings by department
- Vacancy Localization: Multi-language job titles and descriptions
- Applicant Tracking: Manage job applications with contact integration
- Applicant Documents: Store resumes, certificates, and related files
- Recruitment Pipeline: Track candidates through hiring process
6. Organizational Structure
- Department Management: Create and manage organizational departments
- Department Localization: Multi-language department names
- Degree/Position Management: Define job titles and educational requirements
- Hierarchical Structure: Build complex organizational charts with manager-employee relationships
- Department Heads: Automatically identify department leadership
7. Notices & Communications
- Company Notices: Broadcast announcements to employees
- Notice Types: Support for notices, warnings, and terminations
- Targeted Communications: Send notices to specific employees
- Payroll-Linked Notices: Associate notices with payroll payments
Technical Architecture
Technology Stack
- Framework: Laravel 10+
- Module System: nwidart/laravel-modules (^10.0)
- Localization: astrotomic/laravel-translatable (^11.12)
- Permission System: spatie/laravel-permission (^6.4) - via Contact model
- Multi-tenancy: Tenant-scoped data via Sapphire module
- Database: MySQL/PostgreSQL (tenant-specific)
- Authentication: Integrated with Sapphire module
Database Schema
Core Models
Department Model (Department.php)
// Organizational departments with hierarchical support
// Table: r_departments
// Uses Translatable trait from AstrotomicSchema:
- id: Primary key
- created_at, updated_at
Translated Attributes (via r_department_locales):
- name: Department name (localized)
Relationships:
managers(): belongsToMany(Contact) viar_subordinatespivotemployees(): belongsToMany(Contact) viar_subordinatespivothead(): Complex query to find department heads (managers with no superiors)staff(): hasMany(Subordinate) - for counting memberssubordinates(): hasMany(Subordinate) - direct pivot accessapplicants(): morphToMany(Applicant) viar_applicablesvacancies(): hasMany(Vacancy)
Features:
- Hierarchical department structure
- Automatic department head detection
- Staff member aggregation including managers and employees
DepartmentLocale Model (DepartmentLocale.php)
// Localized department information
// Table: r_department_localesSchema:
- id: Primary key
- department_id: Foreign key to departments
- locale: Language code (2 chars)
- name: Translated department name
Degree Model (Degree.php)
// Job positions/titles and educational qualifications
// Table: r_degrees
// Uses Translatable traitSchema:
- id: Primary key
- No timestamps
Translated Attributes (via r_degree_locales):
- name: Position/degree name (localized)
Relationships:
applicants(): hasMany(Applicant)
DegreeLocale Model (DegreeLocale.php)
// Localized degree/position information
// Table: r_degree_localesSchema:
- id: Primary key
- degree_id: Foreign key to degrees
- locale: Language code (2 chars)
- name: Translated degree/position name
Salary Model (Salary.php)
// Employee salary records
// Table: r_salaries
// Primary key is the Contact ID (shared primary key pattern)Schema:
- id: Primary key (references lc_contacts.id)
- type: enum('hourly', 'monthly', 'annual')
- created_at, updated_at
Foreign Keys:
- id → lc_contacts.id (cascade on update/delete)
Note: Currently a minimal implementation - ready for expansion
SalaryComponent Model (SalaryComponent.php)
// Individual salary component values per employee
// Table: r_salary_componentsSchema:
- salary_id: Part of composite primary key
- payroll_component_id: Part of composite primary key
- amount: Component monetary value (float)
Relationships:
salary(): belongsTo(Salary)payrollComponent(): belongsTo(PayrollComponent)
PayrollComponent Model (PayrollComponent.php)
// Salary component definitions (allowances, deductions, incentives)
// Table: r_payroll_componentsSchema:
- id: Primary key
- type: enum('basic', 'allowance', 'incentive')
- No timestamps
Translated Attributes (via r_payroll_component_locales):
- title: Component name (localized)
PayrollComponentLocale Model (PayrollComponentLocale.php)
// Localized payroll component information
// Table: r_payroll_component_localesSchema:
- id: Primary key
- payroll_component_id: Foreign key
- locale: Language code (2 chars)
- title: Translated component name
PaymentComponent Model (PaymentComponent.php)
// Payment processing components for payroll
// Table: r_payment_componentsSchema:
- payroll_payment_id: Part of composite primary key
- payroll_component_id: Part of composite primary key
- amount: Payment amount (float)
Relationships:
payrollPayment(): belongsTo(PayrollPayment)payrollComponent(): belongsTo(PayrollComponent)
PayrollPayment Model (PayrollPayment.php)
// Payroll payment records and disbursements
// Table: r_payroll_paymentsSchema:
- id: Primary key
- salary_id: Foreign key to salaries (nullable, set null on delete)
- bank_account_id: Foreign key to bank accounts (nullable)
- units: Number of payment units (default 1, tinyint)
- paid_at: Payment timestamp (nullable)
- created_at, updated_at
Relationships:
salary(): belongsTo(Salary)bankAccount(): belongsTo(BankAccount) - from financial modulepaymentComponents(): hasMany(PaymentComponent)
Punch Model (Punch.php)
// Attendance punch records (check-in/check-out)
// Table: r_punchesSchema:
- id: Primary key (bigint)
- contact_id: Foreign key to contacts
- type: enum('in', 'out')
- created_at: Timestamp (no updated_at)
Relationships:
contact(): belongsTo(Contact) from Common module
Features:
- Simplified punch tracking
- Records timestamp on creation
- Type indicates check-in or check-out
Leave Model (Leave.php)
// Leave applications and tracking
// Table: r_leavesSchema:
- id: Primary key
- contact_id: Foreign key to contacts
- type: enum('sick', 'casual', 'annual')
- status: boolean (nullable) - true=approved, false=rejected, null=pending
- starts_at: Leave start date
- ends_at: Leave end date
- created_at, updated_at
Casts:
- starts_at, ends_at: datetime
Relationships:
contact(): belongsTo(Contact) from Common module
Vacancy Model (Vacancy.php)
// Job openings and positions
// Table: r_vacanciesSchema:
- id: Primary key
- department_id: Foreign key to departments
- active: boolean (default false)
- created_at, updated_at
Translated Attributes (via r_vacancy_locales):
- title: Job title (localized)
- description: Job description (localized)
Relationships:
department(): belongsTo(Department)applicants(): morphToMany(Applicant) viar_applicables
VacancyLocale Model (VacancyLocale.php)
// Localized vacancy information
// Table: r_vacancy_localesSchema:
- id: Primary key
- vacancy_id: Foreign key to vacancies
- locale: Language code (2 chars)
- title: Translated job title
- description: Translated job description (text)
Applicant Model (Applicant.php)
// Job applicants and recruitment candidates
// Table: r_applicants
// Primary key is the Contact ID (shared primary key pattern)Schema:
- id: Primary key (references lc_contacts.id, non-incrementing)
- country_id: Foreign key to countries (nationality)
- degree_id: Foreign key to degrees (educational qualification)
- degree_date: Year of degree completion (unsigned int)
- tenure: Years of experience (tinyint)
- recruited_at: Recruitment timestamp (nullable)
- created_at, updated_at
Casts:
- recruited_at: datetime
Relationships:
contact(): belongsTo(Contact, 'id') - one-to-one with Contactdocuments(): hasMany(Document)nationality(): belongsTo(Country) from Common moduledegree(): belongsTo(Degree)vacancies(): morphedByMany(Vacancy) viar_applicablesdepartments(): morphedByMany(Department) viar_applicables
Features:
- Applicant shares ID with Contact (no separate user needed)
- Can be recruited (recruited_at timestamp)
- Links to multiple vacancies and departments via polymorphic relation
Document Model (Document.php)
// Employee and applicant documents (resumes, certificates, etc.)
// Table: r_documentsSchema:
- id: Primary key
- applicant_id: Foreign key to applicants
- title: Document title/name
- filename: Stored filename
- created_at, updated_at
Relationships:
applicant(): belongsTo(Applicant)
Accessors:
download_url: Generates route for document download
Subordinate Model (Subordinate.php)
// Employee reporting relationships within departments
// Table: r_subordinates
// Connects managers, employees, and departmentsSchema:
- id: Primary key (string UUID from uuid_short(), non-incrementing)
- manager_id: Foreign key to contacts (nullable, set null on delete)
- contact_id: Foreign key to contacts (nullable, set null on delete)
- department_id: Foreign key to departments (required)
- Unique constraint on (manager_id, contact_id)
- Timestamps
Relationships:
manager(): belongsTo(Contact, 'manager_id')contact(): belongsTo(Contact, 'contact_id')department(): belongsTo(Department)
Features:
- Three-way relationship: Links manager, employee, and department
- Either manager_id or contact_id (or both) can be null
- Allows flexible organizational hierarchies
- Used to build department staff lists and reporting chains
Notice Model (Notice.php)
// Company announcements and HR notices
// Table: r_noticesSchema:
- id: Primary key
- author_id: Foreign key to contacts (who created the notice)
- contact_id: Foreign key to contacts (recipient, nullable)
- payroll_payment_id: Foreign key to payroll payments (nullable)
- type: enum('notice', 'warning', 'termination')
- content: Notice text (text field)
- created_at, updated_at
Relationships:
author(): belongsTo(Contact, 'author_id')contact(): belongsTo(Contact, 'contact_id') - recipientpayrollPayment(): belongsTo(PayrollPayment)
Directory Structure
Modules/Ruby/
├── App/
│ ├── Http/
│ │ └── Controllers/
│ │ ├── AttendanceController.php
│ │ ├── ContactController.php
│ │ ├── DashboardController.php
│ │ ├── DepartmentController.php
│ │ ├── DocumentController.php
│ │ └── ModuleController.php
│ ├── Models/ # Eloquent models (19 models)
│ │ ├── Applicant.php
│ │ ├── Degree.php
│ │ ├── DegreeLocale.php
│ │ ├── Department.php
│ │ ├── DepartmentLocale.php
│ │ ├── Document.php
│ │ ├── Leave.php
│ │ ├── Notice.php
│ │ ├── PaymentComponent.php
│ │ ├── PayrollComponent.php
│ │ ├── PayrollComponentLocale.php
│ │ ├── PayrollPayment.php
│ │ ├── Punch.php
│ │ ├── Salary.php
│ │ ├── SalaryComponent.php
│ │ ├── Subordinate.php
│ │ ├── Vacancy.php
│ │ └── VacancyLocale.php
│ └── Providers/
│ └── ModuleServiceProvider.php
├── Database/
│ ├── Factories/ # Model factories for testing
│ ├── migrations/ # Database migrations
│ │ └── 2024_04_02_104117_ruby_base.php
│ └── Seeders/ # Database seeders
├── resources/
│ ├── views/
│ │ └── components/ # Reusable UI components
│ └── lang/ # Translations
│ └── en/
├── routes/
│ ├── web.php # Web routes
│ └── api.php # API routes
├── config/
│ └── config.php # Module configuration
└── module.json # Module metadataKey Relationships
Contact (Common) (1) ─────── (1) Salary
Contact (1) ─────── (N) Punch (Attendance)
Contact (1) ─────── (N) Leave
Contact (1) ─────── (N) Notices (as author)
Contact (1) ─────── (N) Notices (as recipient)
Contact (1) ─────── (1) Applicant (shared primary key)
Department (1) ─────── (N) Vacancies
Department (1) ─────── (N) Subordinates
Department (1) ─────── (N) DepartmentLocale
Department (N) ─────── (N) Contacts (via r_subordinates) - managers
Department (N) ─────── (N) Contacts (via r_subordinates) - employees
Department (N) ─────── (N) Applicants (via r_applicables polymorphic)
Degree (1) ─────── (N) Applicants
Degree (1) ─────── (N) DegreeLocale
Salary (1) ─────── (N) SalaryComponent
Salary (1) ─────── (N) PayrollPayment
PayrollComponent (1) ─────── (N) SalaryComponent
PayrollComponent (1) ─────── (N) PaymentComponent
PayrollComponent (1) ─────── (N) PayrollComponentLocale
PayrollPayment (1) ─────── (N) PaymentComponent
PayrollPayment (1) ─────── (N) Notices
Vacancy (1) ─────── (N) VacancyLocale
Vacancy (N) ─────── (N) Applicants (via r_applicables polymorphic)
Applicant (1) ─────── (N) Documents
Applicant (N) ─────── (1) Contact (shared ID)
Applicant (N) ─────── (1) Country (nationality)
Applicant (N) ─────── (1) Degree
Subordinate (N) ─────── (1) Contact (as manager)
Subordinate (N) ─────── (1) Contact (as employee)
Subordinate (N) ─────── (1) DepartmentIntegration with Common Module
Ruby module heavily depends on the Common module for contact management:
Contact Model (from Common module)
The Contact model (Modules\Common\App\Models\Contact) is the central entity for:
- Employee information (names, personal details)
- Communication details (phones, emails)
- Addresses
- Role assignments (via Spatie Permission)
Key Contact Relationships Used by Ruby:
// From Common module Contact model
Contact::class
->departments() // Employee's departments
->managed_departments() // Departments they manage
->superiors() // Their managers
->subordinates() // Their direct reports
->applicant() // Recruitment information
->leaves() // Leave history
->hasRole() // Permission checksContact Scopes Used by Ruby:
// Useful query scopes from Contact
Contact::withRoles() // Load roles with translations
Contact::withDefaultInfo() // Load primary phone/email
Contact::withAllInfo() // Load all contact information
Contact::withRecruitmentDetails() // Load applicant info
Contact::activeRecruit() // Only hired employees
Contact::withDepartments() // Load department relationshipsIntegration with Sapphire Module
Ruby integrates with Sapphire for:
- Authentication: User authentication and session management
- Authorization: Permission checks via Spatie Permission (inherited by Contact)
- Tenant Context: All data is automatically scoped to current tenant
use Modules\Sapphire\App\Models\User;
// Get authenticated user
$user = auth()->user();
// Get user's contact for HR operations
$contact = $user->contact;
// Check HR permissions
if ($contact->can('manage-employees')) {
// HR management logic
}Organizational Hierarchy
The Ruby module implements a flexible three-tier hierarchy:
- Departments: Organizational units
- Subordinates: Pivot table linking managers, employees, and departments
- Contacts: Individual people (managers and employees)
How It Works:
// Add an employee to a department with a manager
Subordinate::create([
'department_id' => $dept->id,
'contact_id' => $employee->id,
'manager_id' => $manager->id
]);
// Add a department head (no manager)
Subordinate::create([
'department_id' => $dept->id,
'manager_id' => $head->id,
'contact_id' => null // Managers don't need contact_id
]);
// Get department staff
$department->staff; // Returns all managers and employees
// Get department head
$department->head; // Returns top-level managersRecruitment Workflow
- Create Vacancy: Post job opening for a department
- Receive Applications: Applicants (linked to Contacts) apply via polymorphic relation
- Document Upload: Store resumes, certificates via Document model
- Recruitment: Set
recruited_attimestamp on Applicant - Onboarding: Recruited applicants become employees via Subordinate relationship
API Endpoints
Implemented Controllers:
- AttendanceController: Punch in/out functionality
- ContactController: Employee contact management
- DashboardController: HR dashboard and statistics
- DepartmentController: Department CRUD operations
- DocumentController: Document upload/download
- ModuleController: Module-specific settings
Pending Implementation:
- Salary management
- Payroll processing
- Leave approval workflow
- Vacancy management
- Applicant tracking
- Notice distribution
Development Notes
- Priority: 8
- Alias: ruby
- Service Provider:
Modules\Ruby\App\Providers\ModuleServiceProvider - Dependencies:
- Common module (primary - for Contact management)
- Sapphire module (authentication/authorization)
- Required Packages: astrotomic/laravel-translatable, spatie/laravel-permission
Future Enhancements
Based on the current minimal implementation, planned expansions include:
- Payroll System: Full payroll calculation engine
- Leave Approval: Multi-level approval workflow
- Performance Reviews: Employee evaluation system
- Training Management: Track employee training and certifications
- Shift Management: Work schedule and shift assignments
- Time Off Requests: Integrated leave request system
- Onboarding Workflow: Automated new hire processes
- Exit Management: Employee offboarding procedures