Skip to content

HR

Location: dg_smart_pos_api/modules/HR. Owns employee records, attendance clock-in/clock-out, commission plans and the commission records they produce, and payroll (salary records) for a store. HR does not run its own ledger: paid salary and commission records write a single Expense row each (via PayrollExpenseService), which is how payroll shows up on the P&L “Salaries” line without a second, competing source of truth. Jobs’s JobLabour lines reference HR’s Employee model directly for labour costing, so Jobs genuinely depends on HR being present (see config/modules.php’s requires map), not just conceptually related to it.

  • Employee - one per user per store. employee_number is auto-generated on create as EMP-{store_id}-{id, zero-padded to 4}. Also carries national_id, phone, emergency-contact fields, department, job_title, an employment_type enum (full_time/part_time/contract/casual), employment_date, a status enum (active/inactive/terminated), basic_salary, daily_rate, and an optional commission_plan_id. Soft-deletes, activity-logged.
  • Attendance - employee_id, clock_in_at/clock_out_at, a source (self for the employee’s own clock-in/out, manual for an admin correction), recorded_by, notes.
  • CommissionPlan / CommissionPlanTier - a plan has a basis (revenue or profit) and a calc_type (tiered, flat_percentage, fixed_per_target). Tiers carry target_from/target_to, reward_type (percentage/fixed_amount), reward_value, sort_order.
  • CommissionRecord - one computed record per employee per period, linked to the plan and to the specific tier it hit (tier_hit_id) so a later edit to the plan’s tiers can’t retroactively change what an already-computed record says it paid on.
  • SalaryRecord - period_start_date/period_end_date, basic_salary, allowances_json/deductions_json, days_worked, rate_per_day, computed gross_salary/net_salary, payment_status (pending/paid), payment_method, account_id, and journal_entry_id (an Accounting link, since Accounting is currently disabled in config/modules.php).

EmployeeController::index/store/show/update/destroy/deactivate. store explicitly blocks linking a super-admin user as an employee. index supports filtering by status, department, employment_type, and a search term matched against the linked user’s name/username or the employee number.

AttendanceController::clockIn/clockOut are self-service: they resolve the caller’s own Employee record from Auth::id() and are deliberately not permission-gated, since any employee should be able to clock themselves in. store/update/destroy are the admin-side correction/backfill path, tagged source: manual with recorded_by set. summary computes a soft, informational per-employee count of missing attendance days over a period (used to flag, never block, gaps when attendance_mandatory is on). The whole controller sits behind system.config:enable_attendance_tracking layered on top of enable_hr_module.

CommissionPlanController::store/update always fully replace a plan’s tiers (delete then recreate) rather than diff them, because CommissionRecord snapshots which tier it hit via tier_hit_id rather than re-deriving it later. CommissionRecordController::run computes and creates draft records for every employee with an assigned plan over one shared period via CommissionCalculationService; markPaid and destroy both refuse to touch a record that is already paid.

SalaryRecordController::store creates one record via PayrollService::createSalaryRecord; run does the same for a batch of employees over one shared period via PayrollService::runPayroll. update refuses to edit a record once payment_status is paid (“use reversal if needed” per the controller’s own message; no reversal endpoint exists yet). markPaid hands off to PayrollService::markPaid, which is also where the PayrollExpenseService write happens.

All under auth:sanctum and system.config:enable_hr_module, prefix v1_hr/stores/{store}, registered via modules/HR/Routes/hr.php (required once from routes/tenant.php):

Method Path Controller method Permission
GET employees EmployeeController::index hr:employees:view
GET employees/{employee} EmployeeController::show hr:employees:view
POST employees EmployeeController::store hr:employees:manage
PUT employees/{employee} EmployeeController::update hr:employees:manage
DELETE employees/{employee} EmployeeController::destroy hr:employees:manage
POST employees/{employee}/deactivate EmployeeController::deactivate hr:employees:manage
GET salary-records SalaryRecordController::index hr:payroll:view
GET salary-records/{salaryRecord} SalaryRecordController::show hr:payroll:view
POST salary-records SalaryRecordController::store hr:payroll:manage
POST salary-records/run SalaryRecordController::run hr:payroll:manage
PUT salary-records/{salaryRecord} SalaryRecordController::update hr:payroll:manage
POST salary-records/{salaryRecord}/mark-paid SalaryRecordController::markPaid hr:payroll:manage
POST attendance/clock-in AttendanceController::clockIn none (self-service)
POST attendance/clock-out AttendanceController::clockOut none (self-service)
GET attendance AttendanceController::index hr:attendance:view
GET attendance/summary AttendanceController::summary hr:attendance:view
POST attendance AttendanceController::store hr:attendance:manage
PUT attendance/{attendance} AttendanceController::update hr:attendance:manage
DELETE attendance/{attendance} AttendanceController::destroy hr:attendance:manage
GET commission-plans CommissionPlanController::index hr:commissions:view
GET commission-plans/{commissionPlan} CommissionPlanController::show hr:commissions:view
POST commission-plans CommissionPlanController::store hr:commissions:manage
PUT commission-plans/{commissionPlan} CommissionPlanController::update hr:commissions:manage
DELETE commission-plans/{commissionPlan} CommissionPlanController::destroy hr:commissions:manage
GET commissions CommissionRecordController::index hr:commissions:view
GET commissions/{commissionRecord} CommissionRecordController::show hr:commissions:view
POST commissions/run CommissionRecordController::run hr:commissions:manage
POST commissions/{commissionRecord}/mark-paid CommissionRecordController::markPaid hr:commissions:manage
DELETE commissions/{commissionRecord} CommissionRecordController::destroy hr:commissions:manage

Attendance routes additionally require system.config:enable_attendance_tracking. HR is a tenant-only-migration module (its migrations run via tenancy.php --path, not loadMigrationsFrom), and is listed in ModulesServiceProvider::TENANT_ROUTE_MODULES, which correctly matches the single require in routes/tenant.php.

  • Jobs - JobLabour costs labour against HR’s Employee model directly
  • Sales - commission basis (revenue/profit) is computed from sales activity
  • Accounting - SalaryRecord.journal_entry_id; currently dormant (commented out of config/modules.php’s enabled list)