HR 360 Platform
Introduction
1.1 Purpose
This document specifies the software design of the HR 360 Platform, an enterprise human-resources system built on Laravel (PHP), Blade, and MySQL. It presents the system as a set of functional modules — one per dashboard service — and models each module with UML and structured-analysis diagrams so it can be reviewed module by module, exactly as the modules appear on the platform dashboard.
1.2 Scope
The document covers the whole platform at Level 0 (system context, use cases, data flow, domain model and a representative interaction) and then fully decomposes every one of the 22 dashboard modules. Each module is documented with an overview, responsibilities, actors, its constituent subsystems, the end-to-end workflow, the key functional requirements (with stable identifiers), the data entities it touches, and a complete diagram set — architecture (Levels 1–2), use-case, sequence, data-flow (Levels 1–2) and SOLID class. The content is descriptive of the system's behaviour and structure.
1.3 Diagram Notation
Architecture (L1 / L2)
Layered component views grouped into Presentation, Application (Controllers), Domain (Services / Workflow), Data (Models / Tables) and External. Arrows show dependency and call direction.
Use-Case
Actors (rectangles) are associated with use cases (rounded) inside the module's system boundary. Dashed arrows denote «include» / «extend» relationships.
Sequence
Lifelines for actors and participating classes; solid arrows are calls, dashed arrows are returns. alt / opt / loop frames show branching and iteration.
Data-Flow (L0 / L1 / L2)
External entities are rectangles, processes are circles (numbered), data stores are open cylinders, and labelled arrows carry data. Levels decompose progressively.
Class (SOLID)
Interfaces marked «interface» express abstractions; controllers depend on abstractions (Dependency Inversion); concrete services implement them; entities carry attributes, methods and relationships with multiplicity.
System Overview & Level 0 Models
This chapter presents the platform as a whole: its layered architecture, the system-wide use-case model, the context and level-1/level-2 data-flow diagrams, the core domain class model, and a representative end-to-end interaction that the modular workflows share.
2.1 Architecture — Level 0 (System Context)
flowchart TB
subgraph Users["User Classes"]
E["Employee"]
M["Manager / Approver"]
HR["HR / Recruitment"]
ADM["Administrator"]
EXT["Candidate / Agency / Public Verifier"]
end
subgraph Platform["HR 360 Platform (Laravel / Blade / MySQL)"]
WEB["Presentation Layer (Blade Views, AJAX)"]
APP["Application Layer (Controllers, Auth & RBAC Middleware)"]
DOM["Domain Layer (Workflow Engines & Services)"]
DATA["Data Access Layer (Eloquent Models)"]
end
subgraph Infra["Data & Integrations"]
DB[("MySQL Database")]
MAIL["SMTP Mail Service"]
SCH["Task Scheduler (cron)"]
FILES["File & Asset Storage"]
end
E --> WEB
M --> WEB
HR --> WEB
ADM --> WEB
EXT --> WEB
WEB --> APP
APP --> DOM
DOM --> DATA
DATA --> DB
DOM --> MAIL
SCH --> APP
DOM --> FILES2.2 System Use-Case Model
flowchart LR
E["Employee"]
M["Manager / Approver"]
HR["HR / Recruitment"]
ADM["Administrator"]
EXT["Candidate / Agency / Public"]
subgraph SB["HR 360 Platform - System Use Cases"]
U1(["Authenticate & Access Control"])
U2(["Manage Performance (KPI / Review)"])
U3(["Request Leave & Approvals"])
U4(["Recruit & Onboard"])
U5(["Submit Expenses"])
U6(["Request Documents & Certificates"])
U7(["Broadcast & Acknowledge"])
U8(["Administer Employees & RBAC"])
end
E --- U1
E --- U2
E --- U3
E --- U5
E --- U6
M --- U2
M --- U3
HR --- U4
HR --- U6
HR --- U7
ADM --- U8
EXT --- U42.3 Data-Flow — Level 0 (Context Diagram)
flowchart LR
E["Employee"]
M["Manager / Approver"]
HR["HR / Administrator"]
EXT["Candidate / Agency / Public"]
MAIL["Email Recipients"]
P0(("0 HR 360 Platform"))
E -->|requests and forms| P0
M -->|approvals and decisions| P0
HR -->|administration and reviews| P0
EXT -->|applications and verification| P0
P0 -->|notifications| MAIL
P0 -->|status and documents| E
P0 -->|queues and reports| HR2.4 Data-Flow — Level 1 (Major Subsystems)
flowchart TB
E["Employee"]
M["Manager / Approver"]
HR["HR / Administrator"]
P1(("1.0 Access Control"))
P2(("2.0 Performance Management"))
P3(("3.0 Leave Management"))
P4(("4.0 Recruitment & Onboarding"))
P5(("5.0 Expenses"))
P6(("6.0 Documents & Self-Service"))
d1[("D1 employees")]
d2[("D2 workflow stages")]
d3[("D3 documents and letters")]
E --> P1
P1 --> d1
E --> P2
M --> P2
P2 --> d2
E --> P3
M --> P3
P3 --> d2
HR --> P4
P4 --> d1
E --> P5
P5 --> d2
E --> P6
P6 --> d3
d1 --> P2
d1 --> P32.5 Data-Flow — Level 2 (Shared Approval Workflow)
flowchart LR
A["Requester"]
R["Approver(s)"]
N["Email Recipients"]
P1(("x.1 Submit and Validate"))
P2(("x.2 Resolve Route and Stage"))
P3(("x.3 Record Decision"))
P4(("x.4 Advance or Return"))
P5(("x.5 Notify and Finalize"))
d1[("D request records")]
d2[("D stage and audit trail")]
A -->|request| P1
P1 --> d1
P1 --> P2
d1 --> P2
P2 -->|active stage| P3
R -->|approve / return / reject| P3
P3 --> d2
P3 --> P4
P4 -->|next or previous stage| P2
P4 -->|final| P5
P5 -->|emails| N
P5 -->|status| A2.6 Domain Model — Class Diagram (SOLID)
classDiagram
class Authenticatable {
<<interface>>
+getAuthIdentifier()
}
class Authorizable {
<<interface>>
+isHasPermission(name) bool
}
class ApprovalWorkflow {
<<interface>>
+submit(request)
+advance(request, actor)
+returnToSender(request)
}
class Notifier {
<<interface>>
+notify(recipient, event)
}
class Company {
+int id
+string name
}
class Employee {
+int id
+int company_id
+string empName
+string designation
+isHasPermission(name) bool
}
class UserPermission {
+int employee_id
+string name
}
class Designation {
+int id
+int company_id
+string name
}
class WorkflowRequest {
+int id
+int employee_id
+string status
}
class ApprovalStage {
+int id
+int request_id
+string stage_name
+string status
}
Employee ..|> Authenticatable
Employee ..|> Authorizable
Company "1" --> "0..*" Employee : employs
Company "1" --> "0..*" Designation : defines
Employee "1" --> "0..*" UserPermission : granted
Employee "1" --> "0..*" WorkflowRequest : raises
WorkflowRequest "1" --> "0..*" ApprovalStage : routed through
ApprovalWorkflow ..> WorkflowRequest
ApprovalWorkflow ..> Notifier2.7 Representative End-to-End Sequence
sequenceDiagram
actor A as Requester (Employee)
participant UI as Web UI (Blade)
participant MW as Auth / RBAC Middleware
participant CT as Module Controller
participant WF as Workflow Engine
participant DB as MySQL
participant ML as Mail Service
actor R as Approver
A->>UI: open module and submit request
UI->>MW: authenticated request
MW->>CT: authorize (isHasPermission)
CT->>WF: create and route request
WF->>DB: persist request and first stage
WF->>ML: notify first approver
ML-->>R: notification email
loop Approval chain
R->>CT: approve / return / reject
CT->>WF: apply decision
WF->>DB: update stage and status
WF->>ML: notify next actor
end
WF-->>A: final status and documentEmployee Dashboard
This chapter decomposes the 12 modules presented under the Employee Dashboard area of the platform dashboard.
Annual KPI Live
The Annual KPI module manages yearly objective-setting and a two-tier managerial evaluation across a fiscal KPI window running from 1 August to 31 July. Employees define weighted objectives with a self-rating and nominate one or two approvers, after which a line manager and an optional final approver record ratings and development plans until the record is fully evaluated. A role-derived dashboard presents live status, and HR or KPI administrators can override values, add feedback, and export results to Excel.
Responsibilities
- Capture weighted objectives, self-ratings, and approver selection in a draft record
- Route submissions through first (mid) and final evaluation stages based on manager_count
- Record per-objective manager ratings, development plans, and summary ratings
- Support return and cancel actions between approvers and the employee
- Derive record status and per-role permissions from evaluation flags
- Provide a role-based dashboard and administrative override with Excel export
Actors
Data Entities
Subsystems
Workflow
- Employee sets objectives + weightings + self-rating, picks manager_count & approver(s) (Draft)
- Employee submits (Pending First Approval)
- First Approver records per-objective ratings + development plan
- If manager_count = 1: single manager completes → Evaluated
- If manager_count = 2: Final Approver records Final_Summary_Rating → Toty_Rating → Evaluated
- Approver may return to first approver / employee with reasons
- HR / KpiAdmin override & feedback
Key Functional Requirements
| ID | Requirement |
|---|---|
| FR-PERF-01 | Allow an employee to create exactly one Annual KPI (and one Mid-year KPI) per configured KPI year, with weighted objectives, a self-rating, and a nominated first and/or final approver. |
| FR-PERF-02 | Let the employee choose a one-manager or two-manager path (manager_count); when one manager is chosen, both evaluations route to that approver. |
| FR-PERF-03 | Derive each user's KPI role (employee/first/final) by matching the authenticated employee's name against Emp_Id, First_Approval, and Final_Approval. |
| FR-PERF-04 | Compute a KPI's status solely from is_draft, is_first_approval, is_final_approval, manager_count, and final_approval_for, presenting one of eight canonical statuses. |
| FR-PERF-05 | Let the first approver record per-objective manager ratings, a development plan, and a first-approval summary rating. |
| FR-PERF-06 | Let the final approver record a final summary rating stored as the KPI total (Toty_Rating). |
| FR-PERF-07 | Let an approver return a KPI to the previous stage or the employee with reasons, resetting the relevant flags for edit/resubmit. |
| FR-PERF-08 | Confine KPI cross-company dashboards/exports to KpiAdmin holders (or configured privileged codes) and provide Excel export. |
Diagrams
flowchart TD
subgraph PL["Presentation"]
V1["KPI Form"]
V2["KPI Dashboard"]
end
subgraph AP["Application (Controllers)"]
C1["KpiController"]
end
subgraph DM["Domain (Services/Workflow)"]
S1["KpiWorkflowService"]
S2["PermissionResolver"]
S3["KpiYearResolver"]
end
subgraph DA["Data (Models/Tables)"]
M1["Kpi (kpi)"]
M2["Setting (settings)"]
M3["Employee"]
end
subgraph EX["External"]
X1["Excel Export"]
end
V1 --> C1
V2 --> C1
C1 --> S1
C1 --> S2
S1 --> S3
S1 --> M1
S2 --> M1
S3 --> M2
M1 --> M3
C1 --> X1flowchart LR
subgraph Controller["KpiController"]
a1["store()"]
a2["update(id)"]
a3["show(role, year)"]
a4["dashboard()"]
a5["destroy(id)"]
end
subgraph Workflow["KpiWorkflowService"]
m1["getStatus()"]
m2["getPermissions(role)"]
m3["getButtonsForRole()"]
end
subgraph Entity["Kpi Model"]
r1["employee() belongsTo"]
r2["getKPIYearFromDate()"]
end
a1 --> m1
a2 --> m2
a3 --> m2
a4 --> m1
a2 --> m3
m1 --> r1
m2 --> r2flowchart LR
Emp["Employee"]
First["Line Manager"]
Final["Final Approver"]
Admin["HR / KPI Admin"]
subgraph SB["Annual KPI - System Boundary"]
uc1(["Set Objectives"])
uc2(["Submit KPI"])
uc3(["First Evaluation"])
uc4(["Final Evaluation"])
uc5(["Return / Cancel"])
uc6(["View Dashboard"])
uc7(["Override and Export"])
end
Emp --- uc1
Emp --- uc2
Emp --- uc6
First --- uc3
Final --- uc4
First --- uc5
Admin --- uc7
Admin --- uc6
uc2 -.->|include| uc1
uc4 -.->|extend| uc3
uc5 -.->|extend| uc3sequenceDiagram
actor Emp as Employee
actor Mgr as First Approver
actor Fin as Final Approver
participant C as KpiController
participant S as KpiWorkflowService
participant DB as kpi table
Emp->>C: submit objectives + self rating
C->>S: resolveStatus()
S->>DB: save Pending First Approval
Mgr->>C: first evaluation ratings + plan
C->>S: recordFirstApproval()
alt manager_count = 1
S->>DB: set Evaluated
else manager_count = 2
S->>DB: set Pending Final Approval
Fin->>C: final evaluation Toty_Rating
C->>S: recordFinalApproval()
S->>DB: set Evaluated
end
opt Return to sender
Mgr->>C: return with reason
C-->>Emp: notify returned
end
DB-->>C: resolved status
C-->>Emp: updated KPI viewflowchart LR
E1["Employee"]
E2["First Approver"]
E3["Final Approver"]
E4["HR / KPI Admin"]
p1(("1.0 Set and Submit"))
p2(("2.0 First Evaluate"))
p3(("3.0 Final Evaluate"))
p4(("4.0 Dashboard and Export"))
d1[("D1 kpi")]
d2[("D2 settings")]
E1 -->|objectives, self rating| p1
p1 -->|draft / pending| d1
d2 -->|fiscal window| p1
E2 -->|per objective ratings| p2
p2 -->|first summary| d1
E3 -->|Toty_Rating| p3
p3 -->|final summary| d1
d1 -->|status| p4
p4 -->|report / excel| E4flowchart LR
E2["First Approver"]
p21(("2.1 Open KPI"))
p22(("2.2 Rate Objectives"))
p23(("2.3 Development Plan"))
p24(("2.4 First Summary"))
p25(("2.5 Resolve Status"))
d1[("D1 kpi")]
E2 -->|open assigned KPI| p21
p21 -->|load record| d1
d1 -->|objectives| p22
p22 -->|Review_Manager_Rating| d1
E2 -->|development inputs| p23
p23 -->|development fields| d1
p22 -->|ratings| p24
p24 -->|First_Approval_Summary_Rating| d1
p24 --> p25
p25 -->|pending final / evaluated| d1classDiagram
class KpiController {
+store(Request) Response
+update(Request, id) Response
+show(role, year) View
+dashboard() View
}
class WorkflowService {
<<interface>>
+resolveStatus(Kpi) string
+transition(Kpi, action) void
}
class KpiWorkflowService {
+resolveStatus(Kpi) string
+transition(Kpi, action) void
}
class NotificationService {
<<interface>>
+notify(Employee, event) void
}
class KpiRepository {
<<interface>>
+find(id) Kpi
+save(Kpi) void
}
class Kpi {
+int id
+string First_Approval
+string Final_Approval
+int manager_count
+float Toty_Rating
+getStatus() string
+getPermissions(role) array
}
class Employee {
+int id
+string empName
}
KpiController ..> WorkflowService
KpiController ..> NotificationService
KpiController ..> KpiRepository
KpiWorkflowService ..|> WorkflowService
KpiWorkflowService --> Kpi
KpiRepository ..> Kpi
Kpi "1" --> "1" Employee : belongsToMid-Year KPI Live
The Mid-Year KPI module runs the same objective-setting and two-tier evaluation cycle as the annual process, operating on a separate mid_kpi table. Employees set weighted mid-year objectives with a self-rating and choose one or two approvers, and a line manager and an optional final approver evaluate the record through to completion. A dedicated mid-year dashboard reflects role-derived status, with return and cancel actions available across stages.
Responsibilities
- Capture mid-year weighted objectives, self-ratings, and approver selection as a draft
- Advance records from pending first approval through first and final evaluation
- Record manager ratings, development plans, and mid-year summary ratings
- Handle single-manager and dual-manager completion paths
- Support return and cancel flows between approvers and the employee
- Present a mid-year dashboard with role-derived status and permissions
Actors
Data Entities
Subsystems
Workflow
- Employee sets mid-year objectives + weightings + self-rating, picks manager_count & approver(s) (Draft)
- Employee submits (Pending First Approval)
- First Approver records per-objective ratings + development plan
- If manager_count = 1: single manager completes → Evaluated
- If manager_count = 2: Final Approver records Final_Summary_Rating → Toty_Rating → Evaluated
- Approver may return to first approver / employee with reasons
Key Functional Requirements
| ID | Requirement |
|---|---|
| FR-PERF-01 | Allow an employee to create exactly one Mid-year KPI per configured KPI year, with weighted objectives, a self-rating, and a nominated first and/or final approver. |
| FR-PERF-02 | Let the employee choose a one-manager or two-manager path (manager_count); when one manager is chosen, both evaluations route to that approver. |
| FR-PERF-03 | Derive each user's KPI role (employee/first/final) by matching the authenticated employee's name against Emp_Id, First_Approval, and Final_Approval. |
| FR-PERF-04 | Compute the mid-KPI status solely from is_draft, is_first_approval, is_final_approval, manager_count, and final_approval_for, presenting one of eight canonical statuses. |
| FR-PERF-05 | Let the first approver record per-objective manager ratings, a development plan, and a first-approval summary rating. |
| FR-PERF-06 | Let the final approver record a final summary rating stored as the KPI total (Toty_Rating). |
| FR-PERF-07 | Let an approver return a mid-KPI to the previous stage or the employee with reasons, resetting the relevant flags for edit/resubmit. |
Diagrams
flowchart TD
subgraph PL["Presentation"]
V1["Mid-KPI Form"]
V2["Mid-KPI Dashboard"]
end
subgraph AP["Application (Controllers)"]
C1["MidKpiController"]
end
subgraph DM["Domain (Services/Workflow)"]
S1["MidKpiWorkflowService"]
S2["PermissionResolver"]
S3["KpiYearResolver"]
end
subgraph DA["Data (Models/Tables)"]
M1["MidKpi (mid_kpi)"]
M2["Setting (settings)"]
M3["Employee"]
end
subgraph EX["External"]
X1["Excel Export"]
end
V1 --> C1
V2 --> C1
C1 --> S1
C1 --> S2
S1 --> S3
S1 --> M1
S2 --> M1
S3 --> M2
M1 --> M3
C1 --> X1flowchart LR
subgraph Controller["MidKpiController"]
a1["store()"]
a2["update(id)"]
a3["show(role, year)"]
a4["dashboard()"]
a5["destroy(id)"]
end
subgraph Workflow["MidKpiWorkflowService"]
m1["getStatus()"]
m2["getPermissions(role)"]
m3["getButtonsForRole()"]
end
subgraph Entity["MidKpi Model"]
r1["employee() belongsTo"]
r2["getKPIYearFromDate()"]
end
a1 --> m1
a2 --> m2
a3 --> m2
a4 --> m1
a2 --> m3
m1 --> r1
m2 --> r2flowchart LR
Emp["Employee"]
First["Line Manager"]
Final["Final Approver"]
Admin["HR / KPI Admin"]
subgraph SB["Mid-Year KPI - System Boundary"]
uc1(["Set Mid Objectives"])
uc2(["Submit Mid-KPI"])
uc3(["First Evaluation"])
uc4(["Final Evaluation"])
uc5(["Return / Cancel"])
uc6(["View Dashboard"])
end
Emp --- uc1
Emp --- uc2
Emp --- uc6
First --- uc3
Final --- uc4
First --- uc5
Admin --- uc6
uc2 -.->|include| uc1
uc4 -.->|extend| uc3
uc5 -.->|extend| uc3sequenceDiagram
actor Emp as Employee
actor Mgr as First Approver
actor Fin as Final Approver
participant C as MidKpiController
participant S as MidKpiWorkflowService
participant DB as mid_kpi table
Emp->>C: submit mid objectives + self rating
C->>S: resolveStatus()
S->>DB: save Pending First Approval
Mgr->>C: first evaluation ratings + plan
C->>S: recordFirstApproval()
alt manager_count = 1
S->>DB: set Evaluated
else manager_count = 2
S->>DB: set Pending Final Approval
Fin->>C: final evaluation Toty_Rating
C->>S: recordFinalApproval()
S->>DB: set Evaluated
end
opt Return to sender
Mgr->>C: return with reason
C-->>Emp: notify returned
end
DB-->>C: resolved status
C-->>Emp: updated Mid-KPI viewflowchart LR
E1["Employee"]
E2["First Approver"]
E3["Final Approver"]
E4["HR / KPI Admin"]
p1(("1.0 Set and Submit"))
p2(("2.0 First Evaluate"))
p3(("3.0 Final Evaluate"))
p4(("4.0 Mid Dashboard"))
d1[("D1 mid_kpi")]
d2[("D2 settings")]
E1 -->|mid objectives, self rating| p1
p1 -->|draft / pending| d1
d2 -->|mid window| p1
E2 -->|per objective ratings| p2
p2 -->|first summary| d1
E3 -->|Toty_Rating| p3
p3 -->|final summary| d1
d1 -->|status| p4
p4 -->|report| E4flowchart LR
E3["Final Approver"]
p31(("3.1 Open Evaluated KPI"))
p32(("3.2 Review First Summary"))
p33(("3.3 Set Final Summary"))
p34(("3.4 Set Toty_Rating"))
p35(("3.5 Resolve Status"))
d1[("D1 mid_kpi")]
E3 -->|open pending final| p31
p31 -->|load record| d1
d1 -->|first summary| p32
p32 -->|final remark| d1
E3 -->|Final_Summary_Rating| p33
p33 -->|store| d1
E3 -->|Toty_Rating| p34
p34 -->|store| d1
p34 --> p35
p35 -->|evaluated| d1classDiagram
class MidKpiController {
+store(Request) Response
+update(Request, id) Response
+show(role, year) View
+dashboard() View
}
class WorkflowService {
<<interface>>
+resolveStatus(MidKpi) string
+transition(MidKpi, action) void
}
class MidKpiWorkflowService {
+resolveStatus(MidKpi) string
+transition(MidKpi, action) void
}
class NotificationService {
<<interface>>
+notify(Employee, event) void
}
class MidKpiRepository {
<<interface>>
+find(id) MidKpi
+save(MidKpi) void
}
class MidKpi {
+int id
+string First_Approval
+string Final_Approval
+int manager_count
+float Toty_Rating
+getStatus() string
+getPermissions(role) array
}
class Employee {
+int id
+string empName
}
MidKpiController ..> WorkflowService
MidKpiController ..> NotificationService
MidKpiController ..> MidKpiRepository
MidKpiWorkflowService ..|> WorkflowService
MidKpiWorkflowService --> MidKpi
MidKpiRepository ..> MidKpi
MidKpi "1" --> "1" Employee : belongsToCertificate Request Live
Enables an employee to request a bilingual HR certificate that flows through a two-stage review-then-approval chain and is issued as a QR-verifiable, publicly scannable document. A reviewer confirms or returns the request, an approver finalises it and freezes an employee_snapshot, and a signature, stamp and encrypted QR code are enabled for public verification.
Responsibilities
- Provide a bilingual request form capturing certificate type reference, English or Arabic template selection and a remark.
- Notify the requester and same-company CertificatesReviewer holders when a request is submitted.
- Support a reviewer stage that confirms or returns each request with review status and remark.
- Support an approver stage that finalises approval, freezes the employee_snapshot and enables signature, stamp and QR code.
- Generate an encrypted certificate identifier and render it as a scannable QR code.
- Expose a public, login-free verification view that decrypts the identifier to a read-only certificate.
Actors
Data Entities
Subsystems
Workflow
- Employee submits certificate request (ref + remark); status fields null
- System emails requester and notifies same-company CertificatesReviewer holders
- Reviewer confirms review or returns to employee
- Approver confirms approval; system freezes employee_snapshot
- Signature + stamp + QR enabled (both stages complete)
- Public Verifier scans QR → decrypt id → read-only view (no login)
Key Functional Requirements
| ID | Requirement |
|---|---|
| FR-DOC-01 | Allow an employee to request an English or Arabic certificate by selecting a type (ref) and remark, persisting it with approval fields empty. |
| FR-DOC-02 | Route each certificate through a two-stage chain (CertificatesReviewer then CertificatesApproval), recording name, timestamp, remark and return reasons at each stage. |
| FR-DOC-04 | Snapshot the employee's identity/salary data into employee_snapshot at final approval so the document reflects data as-of-approval. |
| FR-DOC-05 | Enable signature, company stamp and QR only when both review and approval statuses are set. |
| FR-DOC-06 | Expose a public verification URL containing a Crypt-encrypted certificate id, decrypt it, and render a read-only rendition without login. |
| FR-DOC-03 | Email the requester on submission and notify all same-company holders of the relevant permission at each transition. |
Diagrams
flowchart TD
subgraph P["Presentation"]
RequestForm["Certificate Request Form"]
VerifyView["Public Verification View"]
end
subgraph AP["Application (Controllers)"]
CertCtrl["CertificatesController"]
end
subgraph DM["Domain (Services/Workflow)"]
ReviewWF["Reviewer Stage"]
ApproveWF["Approver Stage"]
QrGen["generateQrCode()"]
end
subgraph DA["Data (Models/Tables)"]
CertModel["Certificates (certificate)"]
TypeModel["CertificatesType (certificate_type)"]
SnapStore["employee_snapshot (json)"]
end
subgraph EX["External"]
MailSvc["Mail (SMTP)"]
QrLib["QR Encoder"]
end
RequestForm --> CertCtrl
VerifyView --> CertCtrl
CertCtrl --> ReviewWF
CertCtrl --> ApproveWF
CertCtrl --> QrGen
ReviewWF --> CertModel
ApproveWF --> CertModel
ApproveWF --> SnapStore
CertModel --> TypeModel
CertCtrl --> MailSvc
QrGen --> QrLibflowchart TD
subgraph CTRL["CertificatesController"]
StoreEn["storeEnglishcertificate()"]
StoreAr["storeArabicCertificate()"]
RetRev["returncertificatereviewer()"]
ConfApp["confirm approval"]
RetApp["returncertificateapproval()"]
end
subgraph MODEL["Certificates Model"]
MyRole["myRole()"]
Approval["approval()"]
GenQr["generateQrCode()"]
end
StoreEn --> MyRole
StoreAr --> MyRole
RetRev --> MyRole
ConfApp --> Approval
ConfApp --> GenQr
RetApp --> MyRole
GenQr --> QrOut["Encrypted QR URL"]flowchart LR
Emp["Employee"]
Rev["Certificate Reviewer"]
App["Certificate Approver"]
Pub["Public Verifier"]
subgraph SB["Certificate Request - System Boundary"]
UC1(["Submit Certificate Request"])
UC2(["Review or Return Request"])
UC3(["Approve and Finalise"])
UC4(["Scan QR Code"])
UC5(["View Read-only Certificate"])
end
Emp --- UC1
Rev --- UC2
App --- UC3
Pub --- UC4
Pub --- UC5
UC2 -.->|extend| UC3
UC4 -.->|include| UC5sequenceDiagram
actor Emp as Employee
actor Rev as Reviewer
actor App as Approver
participant C as CertificatesController
participant M as Certificates Model
participant Mail as Mail SMTP
Emp->>C: submit request (ref, remark)
C->>M: create record, status fields null
C-->>Mail: notify requester and reviewers
Rev->>C: open pending request
alt Confirm
Rev->>M: set review_status ConfirmCertificate
C-->>Mail: notify approver
App->>C: confirm approval
M->>M: freeze employee_snapshot
C->>M: enable signature, stamp and QR
App-->>Emp: certificate issued
else Return
Rev->>M: set review_status Return To Sender
C-->>Mail: notify requester
endflowchart LR
Emp["Employee"]
Rev["Certificate Reviewer"]
App["Certificate Approver"]
Pub["Public Verifier"]
p1(("1.0 Submit Request"))
p2(("2.0 Review Request"))
p3(("3.0 Approve and Issue"))
p4(("4.0 Verify QR"))
d1[("D1 certificate")]
d2[("D2 certificate_type")]
d3[("D3 employee_snapshot")]
Emp -->|request ref, remark| p1
p1 -->|new record| d1
p1 -->|template ref| d2
Rev -->|confirm or return| p2
p2 -->|review status| d1
App -->|approval decision| p3
p3 -->|frozen snapshot| d3
p3 -->|issued flag| d1
Pub -->|scan encrypted id| p4
d1 -->|certificate data| p4
p4 -->|read-only view| Pubflowchart LR
App["Certificate Approver"]
Pub["Public Verifier"]
p31(("3.1 Validate Approval"))
p32(("3.2 Freeze Snapshot"))
p33(("3.3 Enable Signature and Stamp"))
p34(("3.4 Generate QR"))
d1[("D1 certificate")]
d3[("D3 employee_snapshot")]
App -->|approval decision| p31
p31 -->|approval status| d1
p31 --> p32
p32 -->|employee fields json| d3
p32 --> p33
p33 --> p34
p34 -->|encrypted id URL| d1
d1 -->|verifiable certificate| PubclassDiagram
class CertificateWorkflow {
<<interface>>
+confirm(id, remark)
+returnToSender(id, reason)
}
class ReviewerStageService {
+confirm(id, remark)
+returnToSender(id, reason)
}
class ApproverStageService {
+confirm(id, remark)
+freezeSnapshot(id)
}
class CertificatesController {
+storeEnglishcertificate(request)
+storeArabicCertificate(request)
+returncertificatereviewer(request, id)
+returncertificateapproval(request, id)
}
class Certificates {
+int id
+string ref
+string review_status
+string approval_status
+json employee_snapshot
+myRole()
+generateQrCode()
}
class CertificatesType {
+int id
+string type
}
class EmployeesCertificate {
+int id
+int certificate_id
+int employee_id
}
class Employee {
+int id
+string empName
+int company_id
}
CertificateWorkflow <|.. ReviewerStageService
CertificateWorkflow <|.. ApproverStageService
CertificatesController ..> CertificateWorkflow
ReviewerStageService ..> Certificates
ApproverStageService ..> Certificates
Certificates "*" --> "1" CertificatesType : certificateType_id
Certificates "*" --> "1" Employee : Emp_id
Certificates "1" --> "*" EmployeesCertificate : issuesHR Expenses Live
The HR Expenses module lets employees raise reimbursement and entitlement claims from a fixed catalogue, each captured as a line item with entitlement type, cost center, invoice number, amount, date and a mandatory attachment. Submitted claims flow through a five-step HR and Finance approval chain that records a per-stage JSON audit trail. The chain terminates in an internal memo to Finance, and approved records feed Excel and per-invoice ZIP exports.
Responsibilities
- Capture claim line items with entitlement type, cost center, invoice number, amount, date and a mandatory attachment (jpg, png or pdf up to 20MB).
- Persist reusable drafts that can be saved, edited or deleted before submission.
- Route each submitted claim through the hr_checked, hr_reviewed, hr_director, hr_gm and Finance memo stages.
- Record reviewer actions of approve, reject with reason, send back one stage, and amend amount or cost center while preserving the prior value.
- Maintain a per-stage JSON audit trail of remarks and status transitions.
- Generate Excel reports and per-invoice or ZIP attachment exports for approved claims.
Actors
Data Entities
Subsystems
Workflow
- Draft (ExpensesDraftItems) → Submit → stage = hr_checked, status = pending
- HR Checked approve → stage hr_review
- HR Reviewed approve → stage hr_director
- HR Director approve → stage hr_gm
- HR GM approve → status = approval, stage = hr_internalmemeofinance
- Finance memo (finance remark) → Approved — terminal
- Any reviewer reject → status rejected; send back → immediately preceding stage
Key Functional Requirements
| ID | Requirement |
|---|---|
| FR-EXP-01 | Allow an employee to create expense claim line items specifying entitlement type, cost center, invoice number, amount, an entitlement-specific date, and a mandatory attachment (jpg/jpeg/png/pdf ≤20 MB). |
| FR-EXP-02 | Let employees save claims as drafts and later edit or delete them before submission. |
| FR-EXP-03 | Reject duplicate submissions with the same invoice number, amount, and entitlement type while a non-rejected copy exists. |
| FR-EXP-04 | Route each submitted item through the sequential chain hr_checked → hr_review → hr_director → hr_gm → finance memo. |
| FR-EXP-05 | Record, per stage, the reviewer's employee id, action date, decision status, and remark in JSON audit fields. |
| FR-EXP-08 | Allow a reviewer to amend an item's amount or cost center, preserving the prior value and flagging it as edited. |
| FR-EXP-09 | Restrict each approval page to employees holding the corresponding permission (hrchecked, hrreview, hrdirector, hrInternalMemo, hrInternalMemofinance). |
| FR-EXP-10 | Isolate all expense data by company_id. |
| FR-EXP-13 | Export expense listings and per-entitlement totals to Excel. |
Diagrams
flowchart TD
subgraph PR["Presentation"]
V1["Claim Line-Item Form"]
V2["Reviewer Console"]
end
subgraph AP["Application (Controllers)"]
C1["ExpensesController.store()"]
C2["ExpensesController.hrfuncontent()"]
C3["ExpensesController.exportExpenses()"]
end
subgraph DM["Domain (Services/Workflow)"]
S1["Approval Chain Workflow"]
S2["Per-Stage JSON Audit"]
end
subgraph DA["Data (Models/Tables)"]
M1["ExpensesRequestsItems"]
M2["ExpensesDraftItems"]
M3["Employee"]
end
subgraph EX["External"]
E1["Finance Internal Memo"]
E2["Excel / ZIP Export"]
end
V1 --> C1
V2 --> C2
V2 --> C3
C1 --> S1
C2 --> S1
S1 --> S2
C1 --> M2
S1 --> M1
S2 --> M1
M1 --> M3
S1 --> E1
C3 --> E2flowchart TD
A["ExpensesController.store()"] --> B{"isdraft?"}
B -->|"yes"| C["Persist ExpensesDraftItems"]
B -->|"no"| D["Create ExpensesRequestsItems"]
D --> E["Set stage = hr_checked"]
E --> F["Set status = pending"]
F --> G["hrfuncontent() reviewer action"]
G --> H{"decision"}
H -->|"approve"| I["Advance stage & append JSON remark"]
H -->|"reject"| J["status = rejected + reason"]
H -->|"send back"| K["Move to preceding stage"]
I --> L["updateexpensesfun() amend amount / cost center"]flowchart LR
EMP["Employee"]
HRC["HR Checked"]
DIR["HR Director"]
FIN["Finance"]
subgraph SB["HR Expenses - System Boundary"]
U1(["Submit Expense Claim"])
U2(["Save Draft"])
U3(["Review & Approve"])
U4(["Reject With Reason"])
U5(["Send Back One Stage"])
U6(["Finance Memo Remark"])
U7(["Export Excel / ZIP"])
end
EMP --- U1
EMP --- U2
HRC --- U3
DIR --- U3
HRC --- U4
HRC --- U5
FIN --- U6
FIN --- U7
U1 -.->|"include"| U3
U3 -.->|"extend"| U5sequenceDiagram
actor EMP as Employee
participant C as ExpensesController
participant W as ApprovalWorkflow
participant M as ExpensesRequestsItems
participant FIN as Finance
EMP->>C: submit claim with attachment
C->>M: create item stage hr_checked status pending
loop HR approval chain
C->>W: reviewer decision
alt approve
W->>M: advance stage and append JSON audit
else send back
W->>M: move to preceding stage
else reject
W-->>EMP: status rejected with reason
end
end
W->>M: status approval stage hr_internalmemeofinance
FIN->>C: attach memo remark
C->>M: status approved
C-->>FIN: internal memo generatedflowchart LR
EMP["Employee"]
REV["HR / Finance Reviewers"]
p1(("1.0 Capture Claim"))
p2(("2.0 Route Approval Chain"))
p3(("3.0 Finance Memo"))
p4(("4.0 Reports & Export"))
d1[("D1 employee_expenses_draft_items")]
d2[("D2 employee_expenses_requests_items")]
EMP -->|"claim line items"| p1
p1 -->|"draft"| d1
p1 -->|"submitted item"| d2
REV -->|"decisions"| p2
p2 -->|"stage + JSON audit"| d2
d2 -->|"approved item"| p3
p3 -->|"memo remark"| d2
d2 -->|"records"| p4
p4 -->|"Excel / ZIP"| REVflowchart LR
REV["HR / Finance Reviewers"]
d2[("D2 employee_expenses_requests_items")]
p21(("2.1 Validate Stage Access"))
p22(("2.2 Approve & Advance Stage"))
p23(("2.3 Reject With Reason"))
p24(("2.4 Send Back One Stage"))
p25(("2.5 Amend Amount / Cost Center"))
p26(("2.6 Append JSON Audit"))
REV -->|"reviewer action"| p21
p21 -->|"approve"| p22
p21 -->|"reject"| p23
p21 -->|"send back"| p24
p21 -->|"amend"| p25
p22 -->|"next stage"| d2
p23 -->|"status rejected"| d2
p24 -->|"preceding stage"| d2
p25 -->|"preserve prior value"| d2
p22 --> p26
p26 -->|"per-stage remark"| d2classDiagram
class ApprovalWorkflow {
<<interface>>
+approve(item, stage)
+reject(item, reason)
+sendBack(item)
+amend(item, field, value)
}
class ExpensesChainWorkflow {
+approve(item, stage)
+reject(item, reason)
+sendBack(item)
+amend(item, field, value)
}
class ExpensesController {
+store(request, stage, isdraft)
+hrfuncontent(request, page, time)
+updateexpensesfun(request)
+exportExpenses(request, page, time)
}
class ExpensesRequestsItems {
+int id
+int employee_id
+string entailment_type
+string cost_center
+int amount_cents
+string stage
+string status
+json remarks
+employee()
}
class ExpensesDraftItems {
+int id
+int employee_id
+string invoice_number
+employee()
}
class Employee {
+int id
+string name
+int company_id
}
ApprovalWorkflow <|.. ExpensesChainWorkflow
ExpensesController ..> ApprovalWorkflow
ExpensesChainWorkflow ..> ExpensesRequestsItems
ExpensesController ..> ExpensesDraftItems
ExpensesRequestsItems "1" --> "1" Employee
ExpensesDraftItems "*" --> "1" EmployeeLeaves Live
The Leave Management module enables employees to submit leave requests specifying leave type, date range, day count, reasons, and travel or ticket details, and routes each request through a per-company, configuration-driven multi-stage approval workflow. A workflow engine coordinates the manager chain, HR and Employee Relations review, an applicant-owned handover decision, parallel departmental clearance, and final approvals, emitting stage-driven email notifications and seven-day pre-travel reminders. On completion the module decrements the employee's leave balance and records the request in the Leaves Center for search, editing, soft-delete, and Excel export.
Responsibilities
- Capture and persist leave requests, storing type, date range, day count, reasons, and travel or ticket data in the leave record.
- Maintain a leave balance ledger, computing available days as quantity minus used minus pending and splitting overflow into paid and unpaid days.
- Drive the per-company approval workflow through handover, the config-driven manager chain, HR and Employee Relations, the handover decision, clearance, and final approvals.
- Sequence work-handover stages one at a time and run the six clearance departments in parallel with an all-versus-specific selection.
- Dispatch stage-appropriate emails and seven-day pre-travel reminders, held by the clearanceEmailsAllowed gate until the handover decision and handover stages resolve.
- Deduct consumed days on final joining-report approval and provide a Leaves Center for search, filter, stage editing, soft-delete, and Excel export.
Actors
Data Entities
Subsystems
Workflow
- Submit → status handOver, or skip-handover starting at chosen ManagerApprovalX
- ManagerApproval1..6 — per-company config-driven manager chain (grade/permission gated)
- ManagerApproval7 — General Manager (legacy)
- EmployeeRelation (HR) — sets clearance & ticket requirements
- HrApproval (HR) — finalize or create Handover Decision
- EmployeeRelationforFlightTicket (HR) — only if travel/ticket required
- ManagerApprovalDecision — Handover Decision, owned by applicant (has_handover yes/no)
- Work handover stages — sequential, activated one at a time
- Clearance (parallel): Accommodation · Plant · StoresMain · StoresSite · IT · Finance
- Final approvals: Administration · HR-Final · Immigration → approved
- JoiningReportEmployee → JoiningReportManagementApproval → balance deducted, complete
- Any stage → returned (backToSender) or rejected/cancelled halts flow
Key Functional Requirements
| ID | Requirement |
|---|---|
| FR-LEA-01 | Let an employee submit a leave request specifying leave type, date range, day count, reasons, and travel/ticket details, persisted to leaves.extraInfo. |
| FR-LEA-03 | Enforce per-type limits and eligibility: Casual 3/yr & 1/day; Maternity 50 & female-only; Unpaid 10; Long 179; Medical/Sick 42 & female-only; Compassionate 10 & 10-day return; Annual uncapped. |
| FR-LEA-04 | When requested days exceed available balance, split the request into paid and unpaid-overflow days and record the leave_breakdown. |
| FR-LEA-05 | Route each request through a per-company, config-driven approval workflow keyed by the applicant's company_id. |
| FR-LEA-09 | Create a Handover Decision (ManagerApprovalDecision) stage after HR approval and any flight-ticket stage, owned by the applicant, who decides whether a work handover is required. |
| FR-LEA-11 | Run the six clearance departments in parallel and honour an all-vs-specific clearance selection, marking unselected departments not_applicable. |
| FR-LEA-13 | Block all clearance-department emails until the Handover Decision and every handover stage are resolved (clearanceEmailsAllowed gate). |
| FR-LEA-16 | Send a 7-day pre-travel reminder (daily 08:00 cron) to approvers with an outstanding handover/clearance stage, respecting the clearance gate. |
| FR-LEA-17 | Deduct consumed leave days when JoiningReportManagementApproval is approved, and return days when a leave is rejected, cancelled, or soft-deleted. |
| FR-LEA-19 | Provide a Leaves Center to search/filter, view, edit a stage with admin audit stamp, soft-delete, and export the filtered set to Excel. |
Diagrams
flowchart TD
subgraph PRES["Presentation"]
UI["Leave Request Form and Leaves Center"]
SHOW["Leave Show Page"]
end
subgraph APPL["Application (Controllers)"]
LC["LeavesController"]
LSC["LeaveStagesController"]
end
subgraph DOM["Domain (Services / Workflow)"]
LMS["LeaveMailService"]
LSS["LeaveStatusService"]
LCA["LeaveShowCacheService"]
end
subgraph DAT["Data (Models / Tables)"]
LV["Leave (leaves)"]
LST["LeaveStage (user_leaves_stages)"]
LT["LeaveType (leave_types)"]
EL["EmployeeLeave (employee_leaves)"]
end
subgraph EXT["External (Mail, Scheduler)"]
MAIL["SMTP Mail"]
CRON["Travel Reminder Scheduler"]
end
UI --> LC
SHOW --> LSC
LC --> LSC
LC --> LSS
LSC --> LMS
LSC --> LCA
LC --> LV
LSC --> LST
LMS --> MAIL
CRON --> LMS
LV --> LT
LV --> ELflowchart TD
STORE["store(status action)"]
GUARD["HR-only guard and validateHandoverData"]
UPD["updateStage"]
PROG["handleWorkflowProgression"]
ANS["activateNextStage (skip not_applicable)"]
MAD["ensureManagerApprovalDecisionExists"]
BTS["backToSender (snapshot and restore)"]
GATE{"clearanceEmailsAllowed?"}
CLR["Activate parallel clearance stages"]
EMAIL["sendEmailNotifications"]
DEDUCT["Deduct balance on JoiningReportManagementApproval"]
CACHE["Invalidate LeaveShowCache"]
STORE --> GUARD
GUARD --> UPD
UPD --> PROG
PROG --> ANS
PROG --> MAD
PROG -->|"return"| BTS
ANS --> GATE
GATE -->|"yes"| CLR
GATE -->|"no"| EMAIL
CLR --> EMAIL
ANS -->|"final report approved"| DEDUCT
PROG --> CACHEflowchart LR
EMP["Employee / Applicant"]
MGR["Line / Senior Manager"]
HR["HR / Employee Relations"]
CLRO["Clearance Officer"]
FIN["Final Approver"]
ADM["Leaves Admin"]
SCH["Scheduler"]
subgraph BND["Leaves - System Boundary"]
U1(["Submit leave request"])
U2(["Approve manager stage"])
U3(["HR approval and clearance setup"])
U4(["Handover Decision"])
U5(["Departmental clearance"])
U6(["Final approval"])
U7(["Return or reject request"])
U8(["Admin search / edit / export"])
U9(["Send travel reminder"])
end
EMP --- U1
EMP --- U4
MGR --- U2
HR --- U3
CLRO --- U5
FIN --- U6
MGR --- U7
ADM --- U8
SCH --- U9
U3 -.->|"include"| U5
U9 -.->|"extend"| U5sequenceDiagram
actor EMP as Employee
participant LC as LeavesController
participant LSC as LeaveStagesController
participant MS as LeaveMailService
participant DB as MySQL
EMP->>LC: store(leave request)
LC->>DB: persist leave and first stage
LC->>MS: sendActionNotification(submitted)
MS-->>EMP: submission email
loop Manager approval chain
EMP->>LSC: store(approved)
LSC->>DB: mark stage resolved and activate next
LSC->>MS: sendActionNotification(approved)
MS-->>EMP: stage-change email
end
opt HR and Handover Decision
LSC->>DB: EmployeeRelation, HrApproval, ManagerApprovalDecision
end
alt Clearance allowed
LSC->>MS: sendActionNotification(clearance)
MS->>MS: clearanceEmailsAllowed gate
MS-->>CLRO: clearance emails
else Returned or rejected
LSC->>DB: backToSender or rejected
LSC->>MS: sendActionNotification(returned)
MS-->>EMP: return notice
end
opt Final approval and deduction
EMP->>LSC: store(JoiningReportManagementApproval approved)
LSC->>DB: deduct employee_leaves balance
endflowchart LR
EMP["Employee"]
MGR["Manager"]
HR["HR"]
CLRO["Clearance Officer"]
SCH["Scheduler"]
P1(("1.0 Submit Request"))
P2(("2.0 Route Approvals"))
P3(("3.0 Handover Decision"))
P4(("4.0 Clearance"))
P5(("5.0 Finalize and Deduct Balance"))
P6(("6.0 Notify"))
D1[("D1 leaves")]
D2[("D2 user_leaves_stages")]
D3[("D3 employee_leaves")]
D4[("D4 leave_types")]
EMP -->|"request details"| P1
P1 -->|"leave record"| D1
P1 -->|"read type"| D4
MGR -->|"approval"| P2
P2 -->|"stage rows"| D2
HR -->|"clearance setup"| P2
EMP -->|"handover choice"| P3
P3 --> P4
CLRO -->|"signoff"| P4
P4 --> P5
P5 -->|"deduct days"| D3
P2 --> P6
SCH -->|"daily trigger"| P6
P6 -->|"emails"| EMPflowchart LR
MGR["Manager"]
HR["HR"]
APP["Applicant"]
CLRO["Clearance Officer"]
FIN["Final Approver"]
P21(("2.1 Manager Chain"))
P22(("2.2 HR / Employee Relation"))
P23(("2.3 Handover Decision"))
P24(("2.4 Parallel Clearance"))
P25(("2.5 Final Approvals"))
D1[("D1 leaves")]
D2[("D2 user_leaves_stages")]
MGR -->|"approve level"| P21
P21 -->|"chain done"| P22
HR -->|"set clearance and ticket"| P22
P22 -->|"create decision"| P23
APP -->|"has handover"| P23
P23 -->|"gate open"| P24
CLRO -->|"department signoff"| P24
P24 -->|"all cleared"| P25
FIN -->|"admin / hr-final / immigration"| P25
P21 --> D2
P24 --> D2
P25 -->|"approved"| D1classDiagram
class LeaveWorkflowStage {
<<interface>>
+activateNextStage()
+handleWorkflowProgression()
+backToSender()
}
class LeaveNotifier {
<<interface>>
+sendActionNotification(stage, action)
+clearanceEmailsAllowed(leave) bool
}
class LeaveBalanceCalculator {
<<interface>>
+available(employee, type) int
+deduct(leave)
}
class LeavesController {
+store(request)
+index(request)
+export(request)
}
class LeaveStagesController {
+store(request)
-updateStage()
-activateNextStage()
}
class LeaveMailService {
+sendActionNotification(stage, action)
-clearanceEmailsAllowed(leave)
}
class Leave {
+int id
+int leave_type_id
+int employee_id
+json extraInfo
+string status
}
class LeaveStage {
+int id
+int leave_id
+string stage_name
+string status
+json extraInfo
}
class LeaveType {
+int id
+string name
+int quantity
}
class EmployeeLeave {
+int employee_id
+int leave_type
+int leave_days
}
LeaveStagesController ..|> LeaveWorkflowStage
LeaveMailService ..|> LeaveNotifier
LeaveStagesController ..|> LeaveBalanceCalculator
LeavesController ..> LeaveWorkflowStage
LeavesController ..> LeaveNotifier
LeavesController ..> LeaveBalanceCalculator
Leave "1" --> "many" LeaveStage : has
Leave "many" --> "1" LeaveType : categorized
LeaveType "1" --> "many" EmployeeLeave : allocates
Leave "many" --> "1" EmployeeLeave : consumesJob Description Live
The Job Description module maintains the per-company designation catalogue, holding each role's job summary, qualifications and responsibilities. It drives a submit and review workflow in which a sender proposes a job description and a reviewer approves, rejects with cause, or sends it back for revision. Approved designations, combined with the integer prefix of the class, feed the grade and seniority values consumed by Performance Review.
Responsibilities
- Maintain the per-company designation catalogue of job summary, qualifications and responsibilities.
- Drive the submit and review workflow with approve, reject-with-cause and send-back outcomes.
- Capture reviewer edits to job_summary_review, qualifications_review and responsibilities_review alongside a rejected_cause JSON record.
- Track review status through Pending, Approved, Rejected and Back to Sender states, resetting to Pending on resubmission.
- Feed the approved designation and the integer prefix of the class as grade and seniority to Performance Review.
- Support bulk designation import from Excel.
Actors
Data Entities
Subsystems
Workflow
- Sender submits JD with summary, qualifications, responsibilities — status Pending (0)
- JD Reviewer reviews the submission and records review text
- Approve → status Approved (1), Designation record updated
- Reject → status Rejected (2), rejected_cause (JSON) captured
- Send back → status Back to Sender (3) → sender revises and resubmits → Pending
- Approved designation becomes selectable as new grade in Performance Review
Key Functional Requirements
| ID | Requirement |
|---|---|
| FR-EMP-07 | Maintain a designation (job-description) catalogue per company, each with job summary, qualifications, and responsibilities, and auto-create a designation when a manageJobDescription holder references an unknown one. |
| FR-EMP-08 | Support a job-description review workflow with pending / approved / rejected / back-to-sender states and captured review text and rejection causes. |
| FR-EMP-09 | Import designations from Excel. |
| FR-EMP-11 | Compute an employee's seniority grade from the integer prefix of class and expose it for approval routing. |
| FR-EMP-12 | Let an employee view and update their own contact/identity fields used on signatures and generated documents. |
| FR-EMP-04 | Let administrators grant/revoke each RBAC permission per employee (e.g. manageJobDescription that gates the JD workflow). |
Diagrams
flowchart TD
subgraph PR["Presentation"]
V1["Designation Catalogue Form"]
V2["JD Review Console"]
end
subgraph AP["Application (Controllers)"]
C1["DesignationController.store()"]
C2["DesignationController.reviewDesignation()"]
C3["UserDesignationsController.import()"]
end
subgraph DM["Domain (Services/Workflow)"]
S1["JD Review Workflow"]
S2["Grade / Seniority Feed"]
end
subgraph DA["Data (Models/Tables)"]
M1["Designation"]
M2["DesignationReview"]
M3["UserDesignation"]
end
subgraph EX["External"]
E1["Performance Review Grade"]
E2["Excel Designation Import"]
end
V1 --> C1
V2 --> C2
C3 --> E2
C1 --> M1
C2 --> S1
S1 --> M2
S1 --> M1
M1 --> S2
S2 --> E1
C1 --> M3
M2 --> M1flowchart TD
A["DesignationController.store()"] --> B["Create / Update Designation"]
B --> C["Create DesignationReview status Pending(0)"]
C --> D["reviewDesignation() reviewer action"]
D --> E{"decision"}
E -->|"approve"| F["status Approved(1)"]
F --> G["Designation fields updated"]
E -->|"reject"| H["status Rejected(2) + rejected_cause"]
E -->|"send back"| I["status Back to Sender(3)"]
I --> J["Sender revises & resubmits"]
J --> C
G --> K["Capture review fields"]flowchart LR
EMP["Employee"]
REV["Job-Description Reviewer"]
ADM["Administrator"]
HR["HR"]
subgraph SB["Job Description - System Boundary"]
U1(["Submit Job Description"])
U2(["Review Job Description"])
U3(["Approve Designation"])
U4(["Reject With Cause"])
U5(["Send Back To Sender"])
U6(["Revise & Resubmit"])
U7(["Import Designations Excel"])
end
EMP --- U1
EMP --- U6
REV --- U2
REV --- U3
REV --- U4
REV --- U5
ADM --- U7
HR --- U2
U2 -.->|"include"| U3
U5 -.->|"extend"| U6sequenceDiagram
actor EMP as Sender
participant C as DesignationController
participant W as ReviewWorkflow
participant DR as DesignationReview
participant D as Designation
EMP->>C: submit JD summary qualifications responsibilities
C->>DR: create review status Pending
C->>W: reviewDesignation decision
alt approve
W->>DR: status Approved(1)
W->>D: update designation fields
else reject
W->>DR: status Rejected(2) with rejected_cause
else send back
W->>DR: status Back to Sender(3)
W-->>EMP: return for revision
EMP->>C: revise and resubmit as Pending
end
D-->>C: approved designation feeds gradeflowchart LR
SND["Sender (Employee)"]
REV["JD Reviewer"]
ADM["Administrator"]
p1(("1.0 Submit Job Description"))
p2(("2.0 Review Decision"))
p3(("3.0 Update Catalogue"))
p4(("4.0 Grade / Seniority Feed"))
d1[("D1 designations")]
d2[("D2 designation_review")]
SND -->|"JD content"| p1
p1 -->|"review record"| d2
REV -->|"decision"| p2
p2 -->|"status + rejected_cause"| d2
p2 -->|"approved fields"| p3
p3 -->|"designation"| d1
d1 -->|"class prefix + designation"| p4
ADM -->|"Excel import"| p3flowchart LR
REV["JD Reviewer"]
d1[("D1 designations")]
d2[("D2 designation_review")]
p21(("2.1 Load Pending Review"))
p22(("2.2 Capture Review Fields"))
p23(("2.3 Approve Designation"))
p24(("2.4 Reject With Cause"))
p25(("2.5 Send Back To Sender"))
REV -->|"open review"| p21
p21 -->|"pending record"| d2
p21 --> p22
p22 -->|"approve"| p23
p22 -->|"reject"| p24
p22 -->|"send back"| p25
p23 -->|"update fields"| d1
p23 -->|"status Approved(1)"| d2
p24 -->|"status Rejected(2)"| d2
p25 -->|"status Back to Sender(3)"| d2classDiagram
class ReviewWorkflow {
<<interface>>
+submit(designation)
+approve(review)
+reject(review, cause)
+sendBack(review)
}
class DesignationReviewWorkflow {
+submit(designation)
+approve(review)
+reject(review, cause)
+sendBack(review)
}
class DesignationController {
+store(request)
+reviewDesignation(request)
+index(request)
+destroy(id)
}
class Designation {
+int id
+int company_id
+string name
+text job_summary
+text qualifications_for_position
+text responsibilities
+company()
+reviews(status)
}
class DesignationReview {
+int id
+int sender_id
+int designation_id
+text job_summary_review
+text qualifications_review
+text responsibilities_review
+json rejected_cause
+int status
+designation()
+sender()
}
class Company {
+int id
+string name
}
class Employee {
+int id
+string name
}
ReviewWorkflow <|.. DesignationReviewWorkflow
DesignationController ..> ReviewWorkflow
DesignationReviewWorkflow ..> DesignationReview
Designation "1" --> "*" DesignationReview
DesignationReview "*" --> "1" Employee
Designation "*" --> "1" Company360 Feedback Planned
The 360 Feedback module runs anonymous multi-rater questionnaires about a chosen manager, distributing participation through unique encrypted links that expire after seven days and accept a single submission each. Each rater answers fifteen manager questions and five self-reflection items on a one-to-five Likert scale across six competency categories. Collected responses are aggregated into per-question rating distributions and a downloadable 360 report.
Responsibilities
- Launch evaluation campaigns pairing a subject manager with a rater list
- Issue each rater a unique encrypted invitation link with a seven-day expiry
- Enforce single submission by blocking expired or already-submitted links
- Collect twenty Likert responses across six competency categories
- Aggregate responses into per-question rating distributions
- Generate and download the consolidated 360 feedback report
Actors
Data Entities
Subsystems
Workflow
- HR/user launches campaign (manager + rater list) → ManagerEvaluation created
- System issues each rater a unique encrypted link (expires 7 days)
- Rater opens link — blocked if already submitted or expired
- Rater submits 20 Likert answers (15 manager + 5 self-reflection, 1–5) → status approved
- HR views aggregated distribution + 360° feedback PDF
Key Functional Requirements
| ID | Requirement |
|---|---|
| FR-PERF-19 | Let a user launch a 360° Manager Evaluation naming a manager and raters, issuing each rater a unique encrypted link that expires after 7 days and blocks duplicate submission. |
| FR-PERF-20 | Collect 1–5 Likert responses across six competency categories plus self-reflection and report, per question, the distribution of ratings and an aggregated 360° feedback PDF. |
Diagrams
flowchart TD
subgraph PL["Presentation"]
V1["Campaign Setup"]
V2["Rater Form"]
V3["Aggregated Report"]
end
subgraph AP["Application (Controllers)"]
C1["DegreeController"]
end
subgraph DM["Domain (Services/Workflow)"]
S1["EncryptedLinkService"]
S2["ResponseValidator"]
S3["StatsAggregator"]
end
subgraph DA["Data (Models/Tables)"]
M1["ManagerEvaluation"]
M2["ManagerEvaluationQuestions"]
M3["Employee"]
end
subgraph EX["External"]
X1["PDF Generator"]
X2["Mail (rater links)"]
end
V1 --> C1
V2 --> C1
V3 --> C1
C1 --> S1
C1 --> S2
C1 --> S3
S1 --> X2
S2 --> M2
S3 --> M2
M2 --> M1
M1 --> M3
C1 --> X1flowchart LR
subgraph Controller["DegreeController"]
a1["generateEncryptedLink(ids)"]
a2["showFirstForm(token, page)"]
a3["storequestions(req, ids)"]
a4["show(id, page)"]
a5["downloadAllFeedback(id)"]
end
subgraph Link["EncryptedLinkService"]
m1["encrypt(payload)"]
m2["verify(token)"]
m3["checkExpiry(7 days)"]
end
subgraph Stats["StatsAggregator"]
s1["calculateAllStats(id)"]
s2["getRatingCounts(column)"]
end
a1 --> m1
a2 --> m2
m2 --> m3
a3 --> s2
a4 --> s1
a5 --> s1
s1 --> s2flowchart LR
HR["HR"]
Rater["360 Rater"]
Mgr["Manager (Subject)"]
subgraph SB["360 Feedback - System Boundary"]
uc1(["Launch Campaign"])
uc2(["Issue Rater Links"])
uc3(["Open Invitation"])
uc4(["Submit Likert Answers"])
uc5(["View Distribution"])
uc6(["Download 360 PDF"])
end
HR --- uc1
HR --- uc5
HR --- uc6
Rater --- uc3
Rater --- uc4
Mgr --- uc5
uc1 -.->|include| uc2
uc3 -.->|include| uc4
uc6 -.->|extend| uc5sequenceDiagram
actor HR as HR
actor Rater as 360 Rater
participant C as DegreeController
participant L as EncryptedLinkService
participant DB as manager_evaluation
HR->>C: launch campaign with rater list
C->>DB: create ManagerEvaluation
loop each rater
C->>L: generateEncryptedLink(ids)
L-->>Rater: unique link expires 7 days
end
Rater->>C: open invitation token
C->>L: verify(token)
alt expired or already submitted
L-->>Rater: blocked page
else valid link
L-->>C: decrypted payload
Rater->>C: submit 20 Likert answers
C->>DB: store questions
end
HR->>C: view report(id)
C->>DB: aggregate distribution
C-->>HR: distribution and PDFflowchart LR
E1["HR"]
E2["360 Rater"]
E3["Manager"]
p1(("1.0 Launch Campaign"))
p2(("2.0 Issue Links"))
p3(("3.0 Collect Responses"))
p4(("4.0 Aggregate Report"))
d1[("D1 manager_evaluation")]
d2[("D2 manager_evaluation_questions")]
E1 -->|manager + rater list| p1
p1 -->|campaign record| d1
d1 -->|rater ids| p2
p2 -->|unique link| E2
E2 -->|20 Likert answers| p3
p3 -->|responses| d2
d2 -->|ratings| p4
p4 -->|distribution + PDF| E1
p4 -->|summary| E3flowchart LR
E2["360 Rater"]
p31(("3.1 Verify Token"))
p32(("3.2 Check Expiry"))
p33(("3.3 Check Submitted"))
p34(("3.4 Render Form"))
p35(("3.5 Store Answers"))
d2[("D2 manager_evaluation_questions")]
E2 -->|token| p31
p31 -->|decrypted ids| p32
p32 -->|within 7 days| p33
d2 -->|prior submission| p33
p33 -->|not submitted| p34
E2 -->|15 manager + 5 self answers| p35
p34 --> p35
p35 -->|Likert 1-5 rows| d2classDiagram
class DegreeController {
+store(Request) Response
+generateEncryptedLink(evaluateId, manId, empId) string
+showFirstForm(token, page) View
+storequestions(req, ids) Response
+downloadAllFeedback(id) Pdf
}
class LinkService {
<<interface>>
+issue(payload) string
+verify(token) array
}
class EncryptedLinkService {
+issue(payload) string
+verify(token) array
+isExpired(createdAt) bool
}
class ReportService {
<<interface>>
+aggregate(evaluationId) array
+export(evaluationId) Pdf
}
class StatsAggregator {
+aggregate(evaluationId) array
+getRatingCounts(column) array
}
class ManagerEvaluation {
+int id
+int employee_id
+string manager_name
+employee() Employee
}
class ManagerEvaluationQuestions {
+int id
+int manager_evaluate_id
+int rating
+employee() Employee
}
DegreeController ..> LinkService
DegreeController ..> ReportService
EncryptedLinkService ..|> LinkService
StatsAggregator ..|> ReportService
ManagerEvaluation "1" --> "0..*" ManagerEvaluationQuestions : has
ManagerEvaluation "1" --> "1" Employee : subjectEmployment Probation Live
The Employment Probation module manages the end-of-probation lifecycle for new hires, from an automated joining reminder through a multi-stage approval chain to a final Confirm or Extend decision. A scheduled daily job emails a Probation Request sixty days after joining, after which the employee submits job description details and selects a first approver. The request then advances through line approvers, employee review, HR review, and the HR Director, producing a confirmation or extension decision letter and a probation report.
Responsibilities
- Run a daily 09:00 scheduled job that emails a Probation Request sixty days after each employee's joining date.
- Capture employee-submitted job description details and the selected first approver, opening the confirmation request as Pending 1st Approval.
- Route the request through a sequential chain of first and optional second, third, and fourth approvers, each recording a Confirm or Extend evaluation and an optional next approver.
- Advance the request through Employee Review, HR Review, and HR Director stages, incrementing the decision letter counter on final approval.
- Return the request to a prior stage with recorded reasons at any point in the chain.
- Produce the Excel probation report and the confirmation or extension decision letter PDF.
Actors
Data Entities
Subsystems
Workflow
- System emails Probation Request 60 days after joining (daily 09:00)
- Employee submits JD details + picks 1st approver (Pending 1st Approval)
- 1st approver evaluates → optional 2nd → 3rd → 4th approvals
- Pending Employee Review — employee resubmits
- Pending HR Review — HR records Confirm/Extend
- Pending HR Director — final decision
- Approved (increments letter_count) with Confirm or Extend decision
- Any stage may Return to employee / 1st approver / HR Review with reasons
Key Functional Requirements
| ID | Requirement |
|---|---|
| FR-PERF-15 | Email a 'Probation Request' to each employee exactly 60 days after joining, on a daily 09:00 schedule. |
| FR-PERF-16 | Process an Employment Confirmation through 1st → (2nd→3rd→4th optional) → Employee Review → HR Review → HR Director, ending Approved with a Confirm or Extend decision. |
| FR-PERF-17 | Support returning an Employment Confirmation to the employee, 1st approver, or HR Review by stage, deleting superseded intermediate approvals and emailing the recipient. |
| FR-PERF-18 | Generate a role-scoped, company-filtered Employment Probation Excel report and a confirmation/extension letter PDF. |
Diagrams
flowchart TD
subgraph Presentation
V1["Confirmation Views (index/show)"]
V2["Create Request Form"]
end
subgraph Application["Application (Controllers)"]
C1["EmploymentConfirmationController"]
end
subgraph Domain["Domain (Services/Workflow)"]
S1["Approval Chain Workflow"]
S2["EmploymentProbationExport"]
S3["Confirmation Letter PDF"]
end
subgraph Data["Data (Models/Tables)"]
M1["EmploymentConfirmationRequest"]
M2["EmploymentConfirmationFirstApproval"]
M3["EmploymentConfirmationNextApproval"]
M4["EmploymentConfirmationReturn"]
M5["Employee"]
end
subgraph External
SCH["Scheduler (cron 09:00)"]
MAIL["Mail (SMTP)"]
end
V1 --> C1
V2 --> C1
C1 --> S1
C1 --> S2
C1 --> S3
S1 --> M1
S1 --> M2
S1 --> M3
S1 --> M4
M1 --> M5
SCH --> C1
S1 --> MAILflowchart LR
subgraph EmploymentConfirmationController
m1["store()"]
m2["storeFirstApproval()"]
m3["storeNextApproval()"]
m4["return()"]
m5["sendEmail()"]
m6["downloadconfirmationletter()"]
end
m1 -->|"status = Pending 1st Approval"| R["EmploymentConfirmationRequest"]
m2 -->|"Confirm/Extend + next"| FA["EmploymentConfirmationFirstApproval"]
m3 -->|"type 2nd/3rd/4th/HR"| NA["EmploymentConfirmationNextApproval"]
m4 -->|"Return to stage"| RET["EmploymentConfirmationReturn"]
m3 -->|"letter_count + 1"| R
m6 --> PDF["Confirmation Letter PDF"]
m5 --> MAIL["Mail Notification"]flowchart LR
Emp["Employee"]
A1["1st Approver"]
AN["2nd/3rd/4th Approver"]
HRR["HR Review"]
HRD["HR Director"]
SCH["Scheduler"]
subgraph SB["Employment Probation - System Boundary"]
uc8(["Send Probation Reminder"])
uc1(["Submit Confirmation Request"])
uc2(["Evaluate Probation"])
uc4(["Resubmit Employee Review"])
uc5(["HR Review Decision"])
uc6(["Final Confirm or Extend"])
uc7(["Generate Decision Letter"])
end
SCH --- uc8
Emp --- uc1
Emp --- uc4
A1 --- uc2
AN --- uc2
HRR --- uc5
HRD --- uc6
HRD --- uc7
uc8 -.->|"triggers"| uc1
uc6 -.->|"include"| uc7sequenceDiagram
participant SCH as Scheduler
participant EMP as Employee
participant CTRL as ConfirmationController
participant DB as Confirmation Models
participant MAIL as Mail Service
SCH->>MAIL: send Probation Request after 60 days
MAIL-->>EMP: Probation Request email
EMP->>CTRL: store() JD details + 1st approver
CTRL->>DB: create Request (Pending 1st Approval)
CTRL->>MAIL: notify 1st approver
loop Approval chain 1st to 4th
CTRL->>DB: storeFirstApproval / storeNextApproval
alt Confirm and next approver
CTRL->>MAIL: notify next approver
else Return to prior stage
CTRL->>DB: create Return record
CTRL->>MAIL: notify prior stage
end
end
CTRL->>DB: Pending HR Review then HR Director
CTRL->>DB: Approved and letter_count incremented
CTRL-->>EMP: confirmation or extension letterflowchart LR
E1["Employee"]
E2["Approvers"]
E3["HR and HR Director"]
E4["Scheduler"]
p1(("1.0 Send Reminder"))
p2(("2.0 Submit Request"))
p3(("3.0 Run Approval Chain"))
p4(("4.0 HR Decision"))
p5(("5.0 Issue Letter and Report"))
d1[("D1 Confirmation Requests")]
d2[("D2 Approvals")]
d4[("D4 Employee Master")]
E4 --> p1
p1 -->|"Probation Request"| E1
E1 -->|"JD + 1st approver"| p2
p2 --> d1
p2 --> p3
E2 -->|"Confirm/Extend"| p3
p3 --> d2
p3 --> p4
E3 -->|"final decision"| p4
p4 --> d1
p4 --> p5
p5 -->|"decision letter"| E1
d4 --> p5flowchart LR
E1["Employee"]
E2["1st Approver"]
E3["Next Approver"]
E4["HR Review"]
p31(("3.1 First Approval"))
p32(("3.2 Next Approvals 2nd-4th"))
p33(("3.3 Employee Review"))
p34(("3.4 Route to HR Review"))
p35(("3.5 Return Handling"))
d1[("D1 Confirmation Requests")]
d2[("D2 Approvals")]
d3[("D3 Returns")]
E2 --> p31
p31 -->|"next approver"| p32
E3 --> p32
p32 --> p33
E1 --> p33
p33 --> p34
E4 --> p34
p31 --> d2
p32 --> d2
p34 --> d1
p35 --> d3
p33 -.->|"return"| p35
p34 -.->|"return"| p35classDiagram
class ApprovalWorkflow {
<<interface>>
+submitRequest()
+evaluateStage()
+routeNext()
}
class NotificationService {
<<interface>>
+notify()
}
class LetterGenerator {
<<interface>>
+generate()
}
class EmploymentConfirmationController {
+store()
+storeFirstApproval()
+storeNextApproval()
+return()
}
class ChainApprovalWorkflow {
+evaluateStage()
+routeNext()
}
class MailNotificationService {
+notify()
}
class PdfLetterGenerator {
+generate()
}
class EmploymentConfirmationRequest {
+int id
+string status
+json job_description_details
+int next_approval_employee_id
}
class EmploymentConfirmationFirstApproval {
+int id
+string evaluation
+string status
}
class EmploymentConfirmationNextApproval {
+int id
+string type
+string evaluation
+int letter_count
}
class EmploymentConfirmationReturn {
+int id
+string status
+string reasons
}
class Employee {
+int id
+string empName
+date joiningDate
}
EmploymentConfirmationController ..> ApprovalWorkflow
EmploymentConfirmationController ..> NotificationService
EmploymentConfirmationController ..> LetterGenerator
ChainApprovalWorkflow ..|> ApprovalWorkflow
MailNotificationService ..|> NotificationService
PdfLetterGenerator ..|> LetterGenerator
Employee "1" --> "0..*" EmploymentConfirmationRequest
EmploymentConfirmationRequest "1" --> "1" EmploymentConfirmationFirstApproval
EmploymentConfirmationRequest "1" --> "0..*" EmploymentConfirmationNextApproval
EmploymentConfirmationRequest "1" --> "0..*" EmploymentConfirmationReturnResignation Planned
Lets an employee submit a resignation with an embedded exit interview and routes it through a dynamic multi-manager plus HR approval chain with a full audit trail. Sequential manager approvals are archived per stage, a route-to-HR shortcut hands off to a fixed HR pipeline, and a per-request visibility guard governs who may view and act on each request.
Responsibilities
- Capture a resignation with last working day after today, reason, next-approval manager and fifteen exit-interview responses in form_data.
- Drive a dynamic sequential multi-manager approval chain with archived approval stages and a route-to-HR shortcut.
- Run the HR pipeline through hr_check, hr_recruitment, hr_review, hr_decision, hr_director and completed.
- Record stage-specific approve, return and reject actions with an audit trail in the stages history.
- Serve a public, token-addressed exit interview form.
- Enforce a per-request visibility guard covering ownership, same-company, next-approver and stage-permission checks.
Actors
Data Entities
Subsystems
Workflow
- Employee submits resignation (stage=approval) with last working day, reason, next manager, 15 exit-interview answers
- Manager approves → next manager (archived approval_N), or routes directly to HR
- HR chain: hr_check → hr_recruitment → hr_review → hr_decision → hr_director
- Status becomes completed; alternatively returned (to employee/manager) or rejected
- Resigning employee is deliberately never emailed on approve/return/reject
Key Functional Requirements
| ID | Requirement |
|---|---|
| FR-DOC-08 | Allow an employee to submit one resignation with last working day (after today), reason, a next-approval manager, and 15 exit-interview responses. |
| FR-DOC-09 | Support a dynamic approval chain with sequential multi-manager approvals (archived approval_N), a route-to-HR shortcut, stage-specific return/reject destinations, and a full stages audit trail. |
| FR-DOC-10 | Enforce per-request visibility via ownership, same-company, next-approver, stage-permission, completed-status and prior-approval rules (403 otherwise). |
| FR-DOC-11 | Provide an unauthenticated, token-addressed exit-interview form that validates and stores responses idempotently. |
Diagrams
flowchart TD
subgraph P["Presentation"]
ResForm["Resignation Form"]
ExitForm["Public Exit Interview Form"]
ListView["Resignation List View"]
end
subgraph AP["Application (Controllers)"]
ResCtrl["ResignationController"]
end
subgraph DM["Domain (Services/Workflow)"]
Policy["ResignationRequestPolicy"]
ApprovalChain["Dynamic Approval Chain"]
HrPipeline["HR Stage Pipeline"]
MailSvc["ResignationMailService"]
end
subgraph DA["Data (Models/Tables)"]
ResModel["ResignationRequest"]
StagesJson["stages (json)"]
FormJson["form_data (json)"]
end
subgraph EX["External"]
Mail["Mail (SMTP)"]
end
ResForm --> ResCtrl
ExitForm --> ResCtrl
ListView --> ResCtrl
ResCtrl --> Policy
ResCtrl --> ApprovalChain
ResCtrl --> HrPipeline
ResCtrl --> MailSvc
ApprovalChain --> ResModel
HrPipeline --> ResModel
ResModel --> StagesJson
ResModel --> FormJson
MailSvc --> Mailflowchart TD
subgraph CTRL["ResignationController"]
Store["store(request)"]
Show["show(id)"]
UpdStatus["updateStatus(request, id)"]
ExitPub["showExitInterviewPublic(token)"]
end
UpdStatus --> Decide{"stage action"}
Decide -->|approve| Next["advance stage"]
Decide -->|return| Back["return to sender"]
Decide -->|reject| Rej["reject request"]
Next --> Archive["archive approval_N"]
Next --> Route["route to HR shortcut"]
Archive --> Persist["save stages and form_data"]
Route --> Persistflowchart LR
Emp["Employee"]
Mgr["Manager"]
Hr["HR Employee Relations"]
Ext["Exit Interviewee"]
subgraph SB["Resignation - System Boundary"]
UC1(["Submit Resignation"])
UC2(["Complete Exit Interview"])
UC3(["Approve or Return or Reject"])
UC4(["Route to HR"])
UC5(["Process HR Stages"])
UC6(["Track Approval Trail"])
end
Emp --- UC1
Emp --- UC6
Mgr --- UC3
Mgr --- UC4
Hr --- UC5
Ext --- UC2
UC1 -.->|include| UC2
UC3 -.->|extend| UC4
UC5 -.->|include| UC6sequenceDiagram
actor Emp as Employee
actor Mgr as Manager
actor Hr as HR Relations
participant C as ResignationController
participant R as ResignationRequest
participant Mail as ResignationMailService
Emp->>C: submit resignation (form_data)
C->>R: create (current_stage approval)
C->>Mail: notify next manager
loop Manager chain
Mgr->>C: review request
alt Approve to next manager
C->>R: archive approval_N, set next_approval
C->>Mail: notify next approver
else Route to HR
C->>R: set current_stage hr_check
else Return or Reject
C->>R: update status
C->>Mail: notify employee
end
end
Hr->>C: process HR stages
C->>R: hr_check through hr_director to completed
C-->>Emp: resignation completedflowchart LR
Emp["Employee"]
Mgr["Manager"]
Hr["HR Relations"]
Ext["Exit Interviewee"]
p1(("1.0 Submit Resignation"))
p2(("2.0 Manager Approval"))
p3(("3.0 HR Pipeline"))
p4(("4.0 Capture Exit Interview"))
d1[("D1 resignation_requests")]
d2[("D2 form_data")]
d3[("D3 stages")]
Emp -->|resignation details| p1
p1 -->|new request| d1
p1 -->|exit responses| d2
Ext -->|public token form| p4
p4 -->|interview answers| d2
Mgr -->|approve return reject| p2
p2 -->|archived approval_N| d3
p2 -->|stage update| d1
Hr -->|stage decisions| p3
p3 -->|hr stage trail| d3
p3 -->|final status| d1flowchart LR
Hr["HR Relations"]
p31(("3.1 Employee Relations Check"))
p32(("3.2 HR Recruitment"))
p33(("3.3 HR Review"))
p34(("3.4 Final Decision"))
p35(("3.5 HR Director"))
d1[("D1 resignation_requests")]
d3[("D3 stages")]
Hr -->|stage action| p31
p31 --> p32
p32 --> p33
p33 --> p34
p34 --> p35
p35 -->|current_stage completed| d1
p31 -->|stage log| d3
p35 -->|stage log| d3classDiagram
class ResignationStageHandler {
<<interface>>
+advance(request, actor)
+returnToSender(request, reason)
+reject(request, reason)
}
class ManagerApprovalService {
+advance(request, actor)
+routeToHr(request)
}
class HrPipelineService {
+advance(request, actor)
+complete(request)
}
class ResignationRequestPolicy {
+view(employee, request)
+update(employee, request)
}
class ResignationController {
+store(request)
+show(id)
+updateStatus(request, id)
+showExitInterviewPublic(token)
}
class ResignationRequest {
+int id
+int employee_id
+string current_stage
+string status
+json stages
+json form_data
+employee()
}
class Employee {
+int id
+string empName
+int company_id
+isHasPermission(name)
}
ResignationStageHandler <|.. ManagerApprovalService
ResignationStageHandler <|.. HrPipelineService
ResignationController ..> ResignationStageHandler
ResignationController ..> ResignationRequestPolicy
ManagerApprovalService ..> ResignationRequest
HrPipelineService ..> ResignationRequest
ResignationRequest "*" --> "1" Employee : employee_idEmail Signature Live
Presents a per-company digital signature block containing letterhead, contact and commercial registration data, built from the employee's identity fields for use on emails and documents. The block resolves company signature data from the authenticated employee's company and reflects any identity fields the employee updates through an AJAX form.
Responsibilities
- Require an authenticated employee session before rendering the signature page.
- Resolve company signature data (name, address, phone, fax, website, CR number, capital, logo) by company identifier.
- Load the employee identity fields that populate the signature block.
- Provide an AJAX form for the employee to edit their own identity fields.
- Validate submitted identity fields and persist them to the employee record.
- Return the refreshed signature block reflecting the saved identity values.
Actors
Data Entities
Subsystems
Workflow
- Employee opens signature page (redirected to login if unauthenticated)
- System resolves company signature data by company_id and loads employee identity
- Employee edits identity fields and submits
- Validated fields saved to the employee record; block reflects updates
Key Functional Requirements
| ID | Requirement |
|---|---|
| FR-DOC-20 | Present a per-company digital-signature block (letterhead/contact/CR data) on documents. |
| — | Render the signature block using the authenticated employee's company and require login to access it. |
| — | Let an employee update their own identity fields that populate the signature block, with server-side validation. |
Diagrams
flowchart TD
subgraph P["Presentation"]
SigView["Signature Block View"]
IdentityForm["Identity Update Form"]
end
subgraph AP["Application (Controllers)"]
SigCtrl["SignatureController"]
end
subgraph DM["Domain (Services/Workflow)"]
ResolveSig["getCompanySignatureData()"]
UpdateIdentity["update()"]
end
subgraph DA["Data (Models/Tables)"]
EmpModel["Employee (employees)"]
CompModel["Company (companies)"]
end
subgraph EX["External"]
AuthGuard["Employee Auth Guard"]
Assets["Company Logo Assets"]
end
SigView --> SigCtrl
IdentityForm --> SigCtrl
SigCtrl --> AuthGuard
SigCtrl --> ResolveSig
SigCtrl --> UpdateIdentity
ResolveSig --> CompModel
ResolveSig --> Assets
UpdateIdentity --> EmpModel
EmpModel --> CompModelflowchart TD
subgraph CTRL["SignatureController"]
Index["index()"]
Update["update()"]
Resolve["getCompanySignatureData(companyId)"]
end
Index --> LoadEmp["load employee with company"]
LoadEmp --> Resolve
Resolve --> Block["companySignatureData array"]
Block --> View["signature.index view"]
Update --> Validate["validate identity rules"]
Validate --> Save["employee.update(validated)"]
Save --> Json["JSON success response"]flowchart LR
Emp["Employee"]
subgraph SB["Email Signature - System Boundary"]
UC1(["View Signature Block"])
UC2(["Load Company Signature Data"])
UC3(["Edit Identity Fields"])
UC4(["Submit Identity Update"])
UC5(["Validate and Save Fields"])
end
Emp --- UC1
Emp --- UC3
Emp --- UC4
UC1 -.->|include| UC2
UC4 -.->|include| UC5
UC5 -.->|extend| UC1sequenceDiagram
actor Emp as Employee
participant C as SignatureController
participant E as Employee Model
participant Co as Company Model
Emp->>C: open signature page
C->>E: load employee with company
E->>Co: resolve company_id
Co-->>C: company signature data
C-->>Emp: render signature block
opt Edit identity
Emp->>C: submit identity fields
C->>C: validate rules
alt Valid
C->>E: update employee record
E-->>C: saved
C-->>Emp: JSON success
else Invalid
C-->>Emp: JSON validation errors
end
endflowchart LR
Emp["Employee"]
p1(("1.0 Resolve Signature Data"))
p2(("2.0 Render Signature Block"))
p3(("3.0 Update Identity Fields"))
d1[("D1 employees")]
d2[("D2 companies")]
Emp -->|open page| p1
d1 -->|identity fields| p1
d2 -->|company signature data| p1
p1 -->|resolved data| p2
p2 -->|signature block| Emp
Emp -->|edited fields| p3
p3 -->|validated fields| d1
p3 -->|updated block| Empflowchart LR
Emp["Employee"]
p31(("3.1 Receive AJAX Request"))
p32(("3.2 Validate Fields"))
p33(("3.3 Persist Identity"))
p34(("3.4 Return JSON Result"))
d1[("D1 employees")]
Emp -->|identity fields| p31
p31 --> p32
p32 -->|valid data| p33
p32 -->|errors| p34
p33 -->|update record| d1
p33 --> p34
p34 -->|success or errors| EmpclassDiagram
class SignatureDataProvider {
<<interface>>
+getCompanySignatureData(companyId)
}
class CompanySignatureProvider {
+getCompanySignatureData(companyId)
}
class SignatureController {
+index()
+update(request)
}
class Employee {
+int id
+string empName
+string emailWork
+string mobile
+int company_id
+company()
+update(fields)
}
class Company {
+int id
+string name
+string website
+string cr_number
+string logo_path
}
SignatureController ..> SignatureDataProvider
SignatureDataProvider <|.. CompanySignatureProvider
SignatureController ..> Employee
Employee "*" --> "1" Company : company_idCandidate Interview Live
The Candidate Interview module manages individually sourced professional applicants from public application through sequential interviewer evaluations, an HR check, immigration recording, and a final HR Director decision. It coordinates a strict status pipeline in which each stage unlocks the next, moving a candidate from registration to an Approved, Rejected, or Returned outcome. The flow spans external candidate self-service and internal HR, interviewer, and director actions.
Responsibilities
- Register candidate requests and assign up to four sequential interviewers.
- Serve an encryption-token-protected public application form capturing personal, salary, education, experience, and document data.
- Record per-interviewer scored evaluations across sections A to D with a hire or do-not-hire recommendation and a total score, advancing after the last interviewer.
- Maintain the HR check package comparing offered, current, and expected salary with joining date and a Draft or Done state.
- Capture immigration details including the Transfer-of-Sponsorship path and route the HR Director Approve, Reject, or Return decision.
- Enforce sequential status transitions across the full recruitment pipeline.
Actors
Data Entities
Subsystems
Workflow
- HR creates candidate request → Pending Application
- Candidate submits public application → Pending HR Reviewed
- HR approves review → Pending 1st Interviewer (or rejects)
- Sequential interviewers submit evaluations → Pending HR Check (after last interviewer)
- HR check done → Pending Immigration (or Rejected By HR Check)
- Immigration recorded → Pending HR Director
- HR Director → Approved / Rejected / Returned (to earlier stage)
Key Functional Requirements
| ID | Requirement |
|---|---|
| FR-REC-04 | Let HR register a professional candidate, assign up to four interviewers, and progress it through Application → HR Reviewed → sequential Interviewers → HR Check → Immigration → HR Director. |
| FR-REC-05 | Expose a public, encryption-token-protected application form allowing candidates to submit personal, salary, and document data, transitioning the candidate to Pending HR Reviewed. |
| FR-REC-06 | Capture per-interviewer evaluations (scored sections, hire/do-not-hire recommendation, total score) and advance only after the last assigned interviewer submits. |
| FR-REC-07 | Let HR record an offer/HR-check package (offered vs current vs expected) with Draft/Done states and support returning a completed check for re-processing. |
| FR-REC-08 | Record immigration data and route the candidate to the HR Director, supporting Transfer-of-Sponsorship as a reduced-data path. |
| FR-REC-09 | Let the HR Director approve, reject, or return a candidate with structured reasons, persisting each decision with actor and date. |
Diagrams
flowchart TD
subgraph Pres["Presentation"]
V1["Public Application Form"]
V2["HR & Interviewer Views"]
end
subgraph App["Application (Controllers)"]
C1["CandidatesController"]
C2["RecruitmentRequestsController"]
end
subgraph Dom["Domain (Services/Workflow)"]
S1["Interview Status Workflow"]
S2["Evaluation Scoring Logic"]
end
subgraph Dat["Data (Models/Tables)"]
M1["CandidateRequest"]
M2["CandidatesApplication"]
M3["CandidateInterviewEvaluation"]
M4["CandidateHrCheck"]
M5["CandidateImmigration / CandidateHrDirector"]
end
subgraph Ext["External"]
X1["Mail Notification"]
end
V1 --> C1
V2 --> C1
V2 --> C2
C1 --> S1
C1 --> S2
S1 --> M1
S1 --> M2
S2 --> M3
M3 --> M4
M4 --> M5
S1 --> X1flowchart TD
A["CandidatesController.storeEvaluation()"]
B["validate sections A-D and scores"]
C["computeTotalScore()"]
D["setRecommendation(hire / doNotHire)"]
E["CandidateInterviewEvaluation.save()"]
F{"is last interviewer?"}
G["advanceStatus(Pending HR Check)"]
H["assignNextInterviewer()"]
A --> B
B --> C
C --> D
D --> E
E --> F
F -->|Yes| G
F -->|No| Hflowchart LR
HR["HR / Recruitment Officer"]
CAND["Candidate"]
INT["Interviewer"]
DIR["HR Director"]
subgraph SB["Candidate Interview - System Boundary"]
U1(["Create Candidate Request"])
U2(["Submit Public Application"])
U3(["Approve HR Review"])
U4(["Submit Interview Evaluation"])
U5(["Complete HR Check"])
U6(["Record Immigration"])
U7(["Make Final Decision"])
end
HR --- U1
HR --- U3
HR --- U5
HR --- U6
CAND --- U2
INT --- U4
DIR --- U7
U2 -.->|include| U3
U5 -.->|include| U6sequenceDiagram
actor HR as HR Officer
actor CAND as Candidate
actor INT as Interviewer
participant C as CandidatesController
participant S as Interview Workflow
participant DB as Database
HR->>C: createCandidateRequest()
C->>S: init Pending Application
S->>DB: insert CandidateRequest
CAND->>C: submitApplication(token)
C->>DB: store Pending HR Reviewed
HR->>C: approveReview()
C->>S: advance Pending 1st Interviewer
loop each interviewer
INT->>C: submitEvaluation(scores)
C->>DB: insert CandidateInterviewEvaluation
end
alt last interviewer done
S->>DB: set Pending HR Check
end
HR->>DB: HR check and immigration
S-->>HR: notify HR Director stageflowchart LR
E1["Candidate"]
E2["HR Officer"]
E3["Interviewer"]
E4["HR Director"]
P1(("1.0 Register Candidate"))
P2(("2.0 Capture Application"))
P3(("3.0 Evaluate Interviews"))
P4(("4.0 HR Check and Immigration"))
P5(("5.0 Final Decision"))
D1[("D1 Candidate Records")]
D2[("D2 Evaluations")]
E2 -->|request| P1
P1 -->|placeholder| D1
E1 -->|application| P2
P2 -->|profile| D1
E3 -->|scores| P3
P3 -->|evaluation| D2
P4 -->|check package| D1
E4 -->|verdict| P5
P5 -->|status| D1flowchart LR
IN["Interviewer"]
HR["HR Officer"]
P31(("3.1 Load Assigned Candidate"))
P32(("3.2 Score Sections A-D"))
P33(("3.3 Set Hire Recommendation"))
P34(("3.4 Compute Total Score"))
P35(("3.5 Advance After Last Interviewer"))
D1[("D1 Candidate Records")]
D2[("D2 Evaluations")]
IN -->|open| P31
P31 -->|form| P32
P32 -->|section scores| P33
P33 -->|recommendation| P34
P34 -->|total| D2
P34 -->|sequence check| P35
P35 -->|Pending HR Check| D1
P35 -->|notify| HRclassDiagram
class InterviewWorkflow {
<<interface>>
+advanceStatus(candidate)
+assignInterviewer(candidate)
}
class InterviewWorkflowService {
+advanceStatus(candidate)
+assignInterviewer(candidate)
+computeTotalScore(evaluation)
}
class CandidatesController {
+storeEvaluation(request)
+approveReview(id)
}
class CandidateRequest {
+int id
+string status
+assignInterviewers()
}
class CandidatesApplication {
+int candidate_id
+decimal expectedSalary
}
class CandidateInterviewEvaluation {
+int interviewer_no
+int totalScore
+string recommendation
}
class CandidateHrCheck {
+decimal offeredSalary
+string state
}
class CandidateImmigration {
+bool transferOfSponsorship
+string decision
}
InterviewWorkflow <|.. InterviewWorkflowService
CandidatesController ..> InterviewWorkflow
CandidateRequest "1" --> "1" CandidatesApplication
CandidateRequest "1" --> "1..4" CandidateInterviewEvaluation
CandidateRequest "1" --> "1" CandidateHrCheck
CandidateRequest "1" --> "1" CandidateImmigrationPerformance Review Planned
The Performance Review module lets an appraiser initiate a structured review of an employee and routes it through up to three sequential line approvers followed by a three-stage HR chain. During HR handling, paired old and new pay components, a target designation, and an effective date form a pay and grade proposal, with the proposed salary computed as the sum of the new pay components. Final approval by the HR Director applies the proposed salary and grade to the employee master record and issues a decision letter.
Responsibilities
- Allow an appraiser to create a review, rate the performance factors, and name the first line approver.
- Route the review sequentially through line approvers one to three, each approving, rejecting, or nominating the next approver.
- Capture the HR pay and grade proposal of paired old and new pay components, a target designation, and an effective date.
- Compute the proposed salary as the sum of the seven new pay components.
- Advance the review through HR Approval and HR Director stages and apply the salary and grade change to the employee master only at HR Director approval.
- Record bounded returns and terminal rejections and generate the decision letter PDF.
Actors
Data Entities
Subsystems
Workflow
- Appraiser creates review → Pending Approver 1
- Pending Approver 1 → (Approver 2 → Approver 3) if next needed
- Approve, no next → Pending HR Checked
- HR enters pay/grade proposal → Pending HR Approval
- HR Approval approves → Pending HR Director
- HR Director approves → Approved (apply salary + grade to Employee)
- Reject at any stage → Rejected (terminal); bounded returns at each HR stage
Key Functional Requirements
| ID | Requirement |
|---|---|
| FR-PERF-10 | Let an appraiser initiate a Performance Review naming a first approver, supporting up to three sequential, dynamically assigned line approvers before HR. |
| FR-PERF-11 | Route an HR-checked review through HR Checked → HR Approval → HR Director, allowing reject (terminal) or bounded return at each HR stage. |
| FR-PERF-12 | Only upon HR-Director approval, apply the HR-checked new salary components and designation to the employee master record. |
| FR-PERF-13 | Recompute the proposed total salary as the sum of the seven new pay components whenever HR Approval edits them. |
| FR-PERF-14 | Persist every Performance-Review rejection/return with reasons and support generating a downloadable decision letter (PDF). |
| FR-PERF-21 | Manage Job-Description (Designation) reviews (pending/approved/rejected/back), with approved designations selectable as the target grade. |
Diagrams
flowchart TD
subgraph Presentation
V1["Review Views (index/show)"]
V2["Create Review Form"]
end
subgraph Application["Application (Controllers)"]
C1["PerformanceReviewController"]
end
subgraph Domain["Domain (Services/Workflow)"]
S1["Line Approval Chain"]
S2["Pay and Grade Proposal"]
S3["Decision Letter PDF"]
end
subgraph Data["Data (Models/Tables)"]
M1["PerformanceReviewRequest"]
M2["PerformanceReviewApproval"]
M3["PerformanceReviewHRChecked"]
M4["PerformanceReviewReturn"]
M5["Designation"]
M6["Employee"]
end
subgraph External
ST["File Storage"]
end
V1 --> C1
V2 --> C1
C1 --> S1
C1 --> S2
C1 --> S3
S1 --> M1
S1 --> M2
S2 --> M3
S2 --> M5
S2 --> M6
M4 --> M1
C1 --> STflowchart LR
subgraph PerformanceReviewController
m1["store()"]
m2["approverAction()"]
m3["storeHRChecked()"]
m4["hrApprovalAction()"]
m5["hrDirectorAction()"]
m6["downloadLetter()"]
end
m1 -->|"Pending Approver 1"| R["PerformanceReviewRequest"]
m2 -->|"approve/reject/next"| A["PerformanceReviewApproval"]
m3 -->|"pay/grade proposal"| H["PerformanceReviewHRChecked"]
m4 -->|"new_proposed_salary"| H
m5 -->|"apply salary + grade"| E["Employee"]
m2 -.->|"return"| RET["PerformanceReviewReturn"]
m6 --> PDF["Decision Letter PDF"]flowchart LR
AP["Appraiser"]
L1["Line Approver 1"]
L23["Line Approver 2/3"]
HC["HR Checked"]
HA["HR Approval"]
HD["HR Director"]
subgraph SB["Performance Review - System Boundary"]
uc1(["Create Appraisal"])
uc2(["Line Approval"])
uc3(["Enter Pay and Grade Proposal"])
uc4(["HR Approval Decision"])
uc5(["Approve and Apply Changes"])
uc6(["Download Decision Letter"])
end
AP --- uc1
L1 --- uc2
L23 --- uc2
HC --- uc3
HA --- uc4
HD --- uc5
HD --- uc6
uc4 -.->|"include"| uc3
uc5 -.->|"extend"| uc6sequenceDiagram
participant AP as Appraiser
participant CTRL as ReviewController
participant LA as Line Approvers
participant HR as HR Chain
participant DB as Review Models
participant EMP as Employee Master
AP->>CTRL: store() create review
CTRL->>DB: Pending Approver 1
loop Approvers 1 to 3
LA->>CTRL: approverAction (approve or next)
CTRL->>DB: save Approval
alt Next approver
CTRL->>DB: Pending Approver 2 or 3
else Reject
CTRL->>DB: Rejected (terminal)
end
end
CTRL->>DB: Pending HR Checked
HR->>CTRL: storeHRChecked pay/grade proposal
CTRL->>DB: new_proposed_salary = sum of new components
HR->>CTRL: hrApprovalAction approve
HR->>CTRL: hrDirectorAction approve
CTRL->>EMP: apply salary and grade
CTRL-->>AP: decision letterflowchart LR
E1["Appraiser"]
E2["Line Approvers"]
E3["HR Checked/Approval"]
E4["HR Director"]
p1(("1.0 Create Appraisal"))
p2(("2.0 Line Approval Chain"))
p3(("3.0 HR Pay and Grade Check"))
p4(("4.0 HR Approval and Director"))
p5(("5.0 Apply Salary and Grade"))
d1[("D1 Review Requests")]
d2[("D2 Approvals")]
d3[("D3 HR Checked")]
d4[("D4 Employee Master")]
E1 -->|"appraisal + approver 1"| p1
p1 --> d1
p1 --> p2
E2 -->|"approve/next"| p2
p2 --> d2
p2 --> p3
E3 -->|"pay/grade proposal"| p3
p3 --> d3
p3 --> p4
E4 -->|"final approve"| p4
p4 --> p5
p5 --> d4flowchart LR
E3["HR Checked"]
E4["HR Approval"]
E5["HR Director"]
p31(("3.1 Capture Old Pay"))
p32(("3.2 Propose New Pay Components"))
p33(("3.3 Compute Proposed Salary"))
p34(("3.4 Set Target Designation"))
p35(("3.5 Approve and Apply"))
d3[("D3 HR Checked")]
d5[("D5 Designations")]
d4[("D4 Employee Master")]
E3 --> p31
p31 --> p32
E4 --> p32
p32 --> p33
p33 -->|"sum of seven components"| d3
E3 --> p34
d5 --> p34
p34 --> d3
E5 --> p35
p35 --> d4classDiagram
class ReviewWorkflow {
<<interface>>
+initiate()
+approveStage()
+routeNext()
}
class PayGradeProposal {
<<interface>>
+buildProposal()
+computeSalary()
}
class LetterGenerator {
<<interface>>
+generate()
}
class PerformanceReviewController {
+store()
+approverAction()
+storeHRChecked()
+hrApprovalAction()
+hrDirectorAction()
}
class SequentialReviewWorkflow {
+approveStage()
+routeNext()
}
class HrPayGradeService {
+buildProposal()
+computeSalary()
}
class PdfLetterGenerator {
+generate()
}
class PerformanceReviewRequest {
+int id
+int appraiser_id
+int approver_1_id
+string status
}
class PerformanceReviewApproval {
+int id
+string stage
+string proposed_designation_name
}
class PerformanceReviewHRChecked {
+int id
+decimal new_proposed_salary
+date effective_date
}
class PerformanceReviewReturn {
+int id
+string status
+string other_reason
}
class Designation {
+int id
+string name
}
class Employee {
+int id
+decimal basicSalary
+string designation
}
PerformanceReviewController ..> ReviewWorkflow
PerformanceReviewController ..> PayGradeProposal
PerformanceReviewController ..> LetterGenerator
SequentialReviewWorkflow ..|> ReviewWorkflow
HrPayGradeService ..|> PayGradeProposal
PdfLetterGenerator ..|> LetterGenerator
Employee "1" --> "0..*" PerformanceReviewRequest
PerformanceReviewRequest "1" --> "0..*" PerformanceReviewApproval
PerformanceReviewRequest "1" --> "1" PerformanceReviewHRChecked
PerformanceReviewRequest "1" --> "0..*" PerformanceReviewReturn
PerformanceReviewRequest "0..*" --> "1" DesignationKnow Your Company
This chapter decomposes the 3 modules presented under the Know Your Company area of the platform dashboard.
Induction Live
Delivers a per-company induction and introduction program of videos and attachments to employees. Administrators maintain a single IntroductionProgram record per company using an update-instead-of-insert pattern that merges newly uploaded attachments with existing ones. Employees open a decrypt-addressed link whose id is decrypted and de-obfuscated to resolve the program and render the introduction_program view of videos and attachments.
Responsibilities
- Maintain one IntroductionProgram per company with videos and attachments.
- Merge newly uploaded attachments and names with existing ones instead of replacing them.
- Resolve an employee link by decrypting and de-obfuscating the id.
- Render the introduction_program view of the resolved company program.
Actors
Data Entities
Subsystems
Workflow
- Administrator maintains the company's single induction program (videos + attachments)
- System publishes a decrypt-addressed acknowledgement link
- Employee opens link; id is decrypted and de-obfuscated (id/975684)
- Employee views the induction videos and attachments
Key Functional Requirements
| ID | Requirement |
|---|---|
| FR-DOC-18 | Let admins maintain one Induction/Internal-Policy/Procedure/Circular program per company (videos + attachments) and publish decrypt-addressed acknowledgement links. |
| — | Maintain exactly one induction program per company (update-instead-of-insert). |
Diagrams
flowchart TD
subgraph pres["Presentation"]
ADM["Program Maintenance Form"]
VIEW["introduction_program View"]
end
subgraph app["Application (Controllers)"]
IPC["IntroductionProgramController"]
end
subgraph dom["Domain (Services/Workflow)"]
UPS["Update-instead-of-insert Merge"]
DEC["Decrypt and De-obfuscate Id"]
end
subgraph data["Data (Models/Tables)"]
IP["IntroductionProgram (introduction_program)"]
CO["Company"]
end
subgraph ext["External"]
FS["assets/images Storage"]
YT["YouTube Embed"]
end
ADM --> IPC
IPC --> UPS
UPS --> IP
UPS --> FS
IP --> CO
VIEW --> DEC
DEC --> IP
IP --> YTflowchart TD
STORE["store() upsert by company_id"]
MERGE["merge old and new attachments"]
YID["extractYouTubeVideoId(url)"]
SHOW["show(id) decrypt and divide"]
DEL["deleteAttachment() splice"]
subgraph model["IntroductionProgram"]
FIND["where company_id first"]
UPD["update(youtube_urls, attachments)"]
CRT["create fresh record"]
end
STORE --> YID
STORE --> FIND
FIND --> MERGE
MERGE --> UPD
FIND --> CRT
SHOW --> FIND
DEL --> UPDflowchart LR
ADM["Administrator"]
EMP["Employee"]
subgraph sb["Induction - System Boundary"]
UC1(["Maintain Induction Program"])
UC2(["Add Videos and Attachments"])
UC3(["Delete Attachment"])
UC4(["Open Induction Link"])
UC5(["View Videos and Attachments"])
end
ADM --- UC1
ADM --- UC3
EMP --- UC4
UC1 -.->|include| UC2
UC4 -.->|include| UC5sequenceDiagram
actor ADM as Administrator
actor EMP as Employee
participant IPC as IntroductionProgramController
participant IP as introduction_program
participant FS as File Storage
ADM->>IPC: store(company_id, videos, attachments)
IPC->>IP: where company_id first
alt record exists
IPC->>IP: merge and update attachments
else new company
IPC->>IP: create record
end
ADM->>FS: upload attachment files
EMP->>IPC: open link (encrypted id)
IPC->>IPC: decrypt and divide by 975684
IPC->>IP: find(id)
IP-->>EMP: render introduction_program viewflowchart LR
ADM["Administrator"]
EMP["Employee"]
P1(("1.0 Maintain Program"))
P2(("2.0 Resolve Link"))
P3(("3.0 Render Program"))
D1[("D1 introduction_program")]
D2[("D2 assets/images")]
ADM -->|videos and files| P1
P1 -->|merged record| D1
P1 -->|files| D2
EMP -->|encrypted id| P2
D1 -->|program record| P2
P2 -->|resolved program| P3
P3 -->|videos and attachments| EMPflowchart LR
ADM["Administrator"]
P11(("1.1 Extract YouTube Id"))
P12(("1.2 Store Uploaded Files"))
P13(("1.3 Lookup by company_id"))
P14(("1.4 Merge or Insert"))
D1[("D1 introduction_program")]
D2[("D2 assets/images")]
ADM -->|youtube url| P11
ADM -->|attachment files| P12
P12 -->|file names| D2
P11 -->|video id| P14
P13 -->|existing record| P14
D1 -->|current record| P13
P14 -->|merged attachments| D1classDiagram
class ProgramProvider {
<<interface>>
+maintain(companyId, data)
+resolveByLink(token)
}
class IntroductionProgramController {
+index()
+store(request)
+show(id)
+deleteAttachment(request)
}
class IntroductionProgram {
+int id
+int company_id
+string name
+string youtube_urls
+string video_from_pc
+json attachments
+company()
}
class Company {
+int id
+string name
+introductionProgram()
}
IntroductionProgramController ..> ProgramProvider
ProgramProvider <|.. IntroductionProgram
IntroductionProgram "*" --> "1" CompanyInternal Policy and Procedures Live
Delivers a per-company internal-policy program and a companion procedures program, each comprising videos and attachments, to employees via decrypt-addressed links. Administrators maintain one Internal_PolicyProgram and one ProceduresProgram per company using an update-instead-of-insert merge pattern. Employees open decrypt-addressed links that resolve each program and render the corresponding policy or procedures view.
Responsibilities
- Maintain one Internal_PolicyProgram per company with videos and attachments.
- Maintain a companion ProceduresProgram per company on its own route.
- Merge newly uploaded attachments with existing ones instead of replacing them.
- Resolve employee links by decrypting and de-obfuscating the id.
- Render the policy and procedures views for the resolved company program.
Actors
Data Entities
Subsystems
Workflow
- Administrator maintains the company's single internal-policy and procedures programs
- System publishes decrypt-addressed acknowledgement links
- Employee opens link; id decrypted and de-obfuscated (id/975684)
- Employee views the policy/procedures videos and attachments
Key Functional Requirements
| ID | Requirement |
|---|---|
| FR-DOC-18 | Let admins maintain one Induction/Internal-Policy/Procedure/Circular program per company (videos + attachments) and publish decrypt-addressed acknowledgement links. |
| — | Maintain exactly one internal-policy and one procedures program per company (update-instead-of-insert). |
Diagrams
flowchart TD
subgraph pres["Presentation"]
ADM["Program Maintenance Form"]
PV["Policy View"]
PRV["Procedures View"]
end
subgraph app["Application (Controllers)"]
PC["InternalpolicyProgramController"]
PRC["ProcedureProgramController"]
end
subgraph dom["Domain (Services/Workflow)"]
UPS["Update-instead-of-insert Merge"]
DEC["Decrypt and De-obfuscate Id"]
end
subgraph data["Data (Models/Tables)"]
IPP["Internal_PolicyProgram"]
PP["ProceduresProgram"]
CO["Company"]
end
ADM --> PC
ADM --> PRC
PC --> UPS
PRC --> UPS
UPS --> IPP
UPS --> PP
IPP --> CO
PP --> CO
PV --> DEC
PRV --> DEC
DEC --> IPP
DEC --> PPflowchart TD
STORE["store() upsert by company_id"]
MERGE["merge old and new attachments"]
YID["extractYouTubeVideoId(url)"]
SHOW["show(id) decrypt and divide"]
VIEW["internal_policy_program View"]
subgraph model["Internal_PolicyProgram"]
FIND["where company_id first"]
UPD["update(youtube_urls, attachments)"]
CRT["create fresh record"]
end
STORE --> YID
STORE --> FIND
FIND --> MERGE
MERGE --> UPD
FIND --> CRT
SHOW --> FIND
FIND --> VIEWflowchart LR
ADM["Administrator"]
EMP["Employee"]
subgraph sb["Internal Policy and Procedures - System Boundary"]
UC1(["Maintain Policy Program"])
UC2(["Maintain Procedures Program"])
UC3(["Open Policy Link"])
UC4(["Open Procedures Link"])
UC5(["View Content"])
end
ADM --- UC1
ADM --- UC2
EMP --- UC3
EMP --- UC4
UC3 -.->|include| UC5
UC4 -.->|include| UC5sequenceDiagram
actor ADM as Administrator
actor EMP as Employee
participant PC as InternalpolicyProgramController
participant IPP as internal_policy
participant PP as procedures_program
ADM->>PC: store(company_id, videos, attachments)
PC->>IPP: where company_id first
alt record exists
PC->>IPP: merge and update attachments
else new company
PC->>IPP: create record
end
ADM->>PP: maintain procedures program
EMP->>PC: open policy link (encrypted id)
PC->>PC: decrypt and divide by 975684
PC->>IPP: find(id)
IPP-->>EMP: render internal_policy_program viewflowchart LR
ADM["Administrator"]
EMP["Employee"]
P1(("1.0 Maintain Policy Program"))
P2(("2.0 Maintain Procedures Program"))
P3(("3.0 Resolve and Render Link"))
D1[("D1 internal_policy")]
D2[("D2 procedures_program")]
ADM -->|policy content| P1
P1 -->|merged record| D1
ADM -->|procedures content| P2
P2 -->|merged record| D2
EMP -->|encrypted id| P3
D1 -->|policy record| P3
D2 -->|procedures record| P3
P3 -->|videos and attachments| EMPflowchart LR
ADM["Administrator"]
P11(("1.1 Extract YouTube Id"))
P12(("1.2 Store Uploaded Files"))
P13(("1.3 Lookup by company_id"))
P14(("1.4 Merge or Insert"))
D1[("D1 internal_policy")]
D2[("D2 assets/images")]
ADM -->|youtube url| P11
ADM -->|attachment files| P12
P12 -->|file names| D2
P11 -->|video id| P14
P13 -->|existing record| P14
D1 -->|current record| P13
P14 -->|merged attachments| D1classDiagram
class ProgramProvider {
<<interface>>
+maintain(companyId, data)
+resolveByLink(token)
}
class InternalpolicyProgramController {
+store(request)
+show(id)
+deleteAttachment(request)
}
class ProcedureProgramController {
+store(request)
+show(id)
}
class Internal_PolicyProgram {
+int id
+int company_id
+string youtube_urls
+json attachments
+company()
}
class ProceduresProgram {
+int id
+int company_id
+string youtube_urls
+json attachments
+company()
}
class Company {
+int id
+string name
}
InternalpolicyProgramController ..> ProgramProvider
ProcedureProgramController ..> ProgramProvider
ProgramProvider <|.. Internal_PolicyProgram
ProgramProvider <|.. ProceduresProgram
Internal_PolicyProgram "*" --> "1" Company
ProceduresProgram "*" --> "1" CompanyCircular Memos Live
Delivers a per-company circular-memo program of videos and attachments to employees for acknowledgement via a decrypt-addressed link. Administrators maintain a single Circular_Memo record per company using an update-instead-of-insert merge pattern. Employees open a decrypt-addressed link that resolves the program and renders the circulated_memo_program view for acknowledgement.
Responsibilities
- Maintain one Circular_Memo program per company with videos and attachments.
- Merge newly uploaded attachments with existing ones instead of replacing them.
- Resolve an employee link by decrypting and de-obfuscating the id.
- Render the circulated_memo_program view for memo acknowledgement.
Actors
Data Entities
Subsystems
Workflow
- Administrator maintains the company's single circular-memo program (videos + attachments)
- System publishes a decrypt-addressed acknowledgement link
- Employee opens link; id decrypted and de-obfuscated (id/975684)
- Employee views the circular memo content for acknowledgement
Key Functional Requirements
| ID | Requirement |
|---|---|
| FR-DOC-18 | Let admins maintain one Induction/Internal-Policy/Procedure/Circular program per company (videos + attachments) and publish decrypt-addressed acknowledgement links. |
| — | Maintain exactly one circular-memo program per company (update-instead-of-insert). |
Diagrams
flowchart TD
subgraph pres["Presentation"]
ADM["Program Maintenance Form"]
VIEW["circulated_memo_program View"]
end
subgraph app["Application (Controllers)"]
CMC["CircularmemoProgramController"]
end
subgraph dom["Domain (Services/Workflow)"]
UPS["Update-instead-of-insert Merge"]
DEC["Decrypt and De-obfuscate Id"]
end
subgraph data["Data (Models/Tables)"]
CM["Circular_Memo (circular_memo)"]
CO["Company"]
end
subgraph ext["External"]
FS["assets/images Storage"]
end
ADM --> CMC
CMC --> UPS
UPS --> CM
UPS --> FS
CM --> CO
VIEW --> DEC
DEC --> CMflowchart TD
STORE["store() upsert by company_id"]
MERGE["merge old and new attachments"]
YID["extractYouTubeVideoId(url)"]
SHOW["show(id) decrypt and divide"]
DEL["deleteAttachment() splice"]
subgraph model["Circular_Memo"]
FIND["where company_id first"]
UPD["update(youtube_urls, attachments)"]
CRT["create fresh record"]
end
STORE --> YID
STORE --> FIND
FIND --> MERGE
MERGE --> UPD
FIND --> CRT
SHOW --> FIND
DEL --> UPDflowchart LR
ADM["Administrator"]
EMP["Employee"]
subgraph sb["Circular Memos - System Boundary"]
UC1(["Maintain Memo Program"])
UC2(["Add Videos and Attachments"])
UC3(["Delete Attachment"])
UC4(["Open Memo Link"])
UC5(["Acknowledge Memo"])
end
ADM --- UC1
ADM --- UC3
EMP --- UC4
UC1 -.->|include| UC2
UC4 -.->|include| UC5sequenceDiagram
actor ADM as Administrator
actor EMP as Employee
participant CMC as CircularmemoProgramController
participant CM as circular_memo
participant FS as File Storage
ADM->>CMC: store(company_id, videos, attachments)
CMC->>CM: where company_id first
alt record exists
CMC->>CM: merge and update attachments
else new company
CMC->>CM: create record
end
ADM->>FS: upload attachment files
EMP->>CMC: open link (encrypted id)
CMC->>CMC: decrypt and divide by 975684
CMC->>CM: find(id)
CM-->>EMP: render circulated_memo_program viewflowchart LR
ADM["Administrator"]
EMP["Employee"]
P1(("1.0 Maintain Memo Program"))
P2(("2.0 Resolve Link"))
P3(("3.0 Render for Acknowledgement"))
D1[("D1 circular_memo")]
D2[("D2 assets/images")]
ADM -->|videos and files| P1
P1 -->|merged record| D1
P1 -->|files| D2
EMP -->|encrypted id| P2
D1 -->|memo record| P2
P2 -->|resolved program| P3
P3 -->|videos and attachments| EMPflowchart LR
ADM["Administrator"]
P11(("1.1 Extract YouTube Id"))
P12(("1.2 Store Uploaded Files"))
P13(("1.3 Lookup by company_id"))
P14(("1.4 Merge or Insert"))
D1[("D1 circular_memo")]
D2[("D2 assets/images")]
ADM -->|youtube url| P11
ADM -->|attachment files| P12
P12 -->|file names| D2
P11 -->|video id| P14
P13 -->|existing record| P14
D1 -->|current record| P13
P14 -->|merged attachments| D1classDiagram
class ProgramProvider {
<<interface>>
+maintain(companyId, data)
+resolveByLink(token)
}
class CircularmemoProgramController {
+index()
+store(request)
+show(id)
+deleteAttachment(request)
}
class Circular_Memo {
+int id
+int company_id
+string name
+string youtube_urls
+json attachments
+company()
}
class Company {
+int id
+string name
+circularMemo()
}
CircularmemoProgramController ..> ProgramProvider
ProgramProvider <|.. Circular_Memo
Circular_Memo "*" --> "1" CompanyHR Staff Dashboard
This chapter decomposes the 7 modules presented under the HR Staff Dashboard area of the platform dashboard.
Welcome Note Live
Onboards a new hire by creating the employee record and dispatching a welcome note to one or more managers grouped under a shared request identifier. Recipient managers open the note and change its state, and each change cascades across every row that shares the request identifier. An Excel import backed by a downloadable template supports bulk onboarding.
Responsibilities
- Create the new employee record and dispatch welcome notes to the selected managers.
- Group all dispatched notes for one onboarding under a shared request identifier.
- Provide recipient managers with a view of the welcome note.
- Apply pending, evaluated, or canceled states across the whole recipient group.
- Attach an optional signature on evaluation or a reason on cancellation.
- Import bulk onboarding data from an Excel template and create the corresponding employee records.
Actors
Data Entities
Subsystems
Workflow
- HR creates Welcome Note → new employee record created + note dispatched to manager(s) under a shared request_id (status pending)
- Recipient manager(s) view the note
- Recipient changes state → evaluated (optional signature) or canceled (with reason)
- State change cascades across all rows sharing request_id
Key Functional Requirements
| ID | Requirement |
|---|---|
| FR-REC-18 | Let HR onboard a new hire via a Welcome Note — creating the employee record and dispatching the note to one or more managers grouped by a shared request_id — with Excel bulk onboarding. |
| FR-REC-19 | Let recipients change a welcome note's state (pending/evaluated/canceled) for the whole recipient group, optionally attaching a signature and cancellation reason. |
Diagrams
flowchart TD
subgraph Presentation["Presentation"]
UI1["Welcome Note Form"]
UI2["Excel Import UI"]
UI3["Manager Recipient View"]
end
subgraph Application["Application (Controllers)"]
C1["Employee WelcomeNoteController"]
C2["Admin WelcomeNoteController"]
end
subgraph Domain["Domain (Services/Workflow)"]
W1["Employee Creation & Dispatch"]
W2["State Change Handler"]
W3["WelcomeNoteImport"]
end
subgraph Data["Data (Models/Tables)"]
M1["Employee"]
M2["WelcomeNoteRequest"]
end
subgraph External["External"]
E1["Excel Spreadsheet"]
end
UI1 --> C1
UI3 --> C1
UI2 --> C1
C1 --> W1
C1 --> W2
C1 --> W3
E1 --> W3
W1 --> M1
W1 --> M2
W2 --> M2
W3 --> M1
C2 --> M2flowchart TD
subgraph WNC["WelcomeNoteController"]
a1["getManagers() : filter by entity"]
a2["store() : create Employee"]
a3["store() : dispatch per manager"]
a4["changeState() : group update"]
a5["import() : bulk onboarding"]
end
M1[("employees")]
M2[("welcome_notes_requests")]
a1 --> a3
a2 --> M1
a2 -->|"shared request_id (rand)"| a3
a3 -->|"row per receiver (pending)"| M2
a4 -->|"where request_id group"| M2
a4 -->|"evaluated / canceled"| M2
a5 --> M1flowchart LR
HR["HR / Recruitment Officer"]
MG["Manager (Recipient)"]
subgraph SB["Welcome Note - System Boundary"]
U1(["Create Welcome Note"])
U2(["Create Employee Record"])
U3(["Bulk Import (Excel)"])
U4(["View Welcome Note"])
U5(["Mark Evaluated"])
U6(["Cancel with Reason"])
end
HR --- U1
HR --- U3
MG --- U4
MG --- U5
MG --- U6
U1 -.->|"include"| U2
U5 -.->|"extend"| U4
U6 -.->|"extend"| U4sequenceDiagram
actor HR as HR Officer
actor MG as Manager
participant C as WelcomeNoteController
participant E as Employee
participant W as WelcomeNoteRequest
HR->>C: store()
C->>E: create employee record
loop Each selected manager
C->>W: create row (shared request_id, pending)
end
MG->>C: index() view note
alt Evaluated
MG->>C: changeState(evaluated)
C->>W: update group + signature
else Canceled
MG->>C: changeState(canceled)
C->>W: update group + reason
endflowchart LR
HR["HR Officer"]
MG["Manager"]
XL["Excel Spreadsheet"]
p1(("1.0 Create Welcome Note"))
p2(("2.0 Dispatch to Managers"))
p3(("3.0 Bulk Import"))
p4(("4.0 Change State"))
d1[("D1 Employees")]
d2[("D2 Welcome Note Requests")]
HR -->|"employee data + managers"| p1
p1 --> d1
p1 -->|"shared request_id"| p2
p2 -->|"pending rows"| d2
XL -->|"rows"| p3
p3 --> d1
MG -->|"evaluate / cancel"| p4
d2 -->|"recipient group"| p4
p4 -->|"evaluated / canceled"| d2flowchart LR
d2[("D2 Welcome Note Requests")]
MG["Manager"]
p41(("4.1 Resolve request_id Group"))
p42(("4.2 Set Status"))
p43(("4.3 Attach Signature"))
p44(("4.4 Record Cancel Reason"))
MG -->|"chosen state"| p41
d2 -->|"rows sharing request_id"| p41
p41 --> p42
p42 -->|"status cascade"| d2
p42 -->|"evaluated"| p43
p42 -->|"canceled"| p44
p43 -->|"signature file"| d2
p44 -->|"cancellation reason"| d2classDiagram
class WelcomeNoteController {
+store()
+changeState()
+import()
}
class NoteDispatcher {
<<interface>>
+dispatch(employee, managers)
}
class ManagerDispatcher {
+dispatch(employee, managers)
}
class ExcelDispatcher {
+dispatch(employee, managers)
}
class WelcomeNoteRequest {
+int id
+int new_emp_request
+int emp_receiver_request
+string request_id
+string status
+newemployee()
+employeeReceiver()
}
class Employee {
+int id
+string empName
+string welcomeNote
+int company_id
}
WelcomeNoteController ..> NoteDispatcher
NoteDispatcher <|.. ManagerDispatcher
NoteDispatcher <|.. ExcelDispatcher
Employee "1" --> "*" WelcomeNoteRequest : onboarded in
WelcomeNoteRequest "*" --> "1" Employee : addressed toSeparation Letters Live
Employees raise paired sender and receiver separation-letter requests grouped by a shared request_id and letter_type, with type-specific fields captured on creation. Each request appears for both the sender and the receiver in a pending state, and a single state change transitions every row sharing that request_id to evaluated or canceled while uploading a signature image. HR staff monitor request volumes across all, pending, evaluated, and canceled states.
Responsibilities
- Present a letter-request list with all, pending, evaluated, and canceled statistics for the signed-in employee.
- Create paired sender and receiver rows sharing one request_id and letter_type with type-specific extra fields.
- Transition all rows of a request_id to evaluated or canceled on a single state change.
- Store an uploaded signature image and any cancellation reasons when the state changes.
- Route each request to its sender and receiver based on current status.
Actors
Data Entities
Subsystems
Workflow
- Employee creates a paired sender/receiver letter request (shared request_id, letter_type)
- Request appears for sender and receiver with status pending
- Reviewer/receiver changes state (pending → evaluated/canceled), uploading a signature image
- HR Staff monitors requests and statistics by status
Key Functional Requirements
| ID | Requirement |
|---|---|
| FR-DOC-07 | Let employees create paired sender/receiver letter requests grouped by request_id, with type-specific fields and a signature-image upload on state change. |
| — | List letter requests where the authenticated employee is the sender or receiver and summarise counts by status. |
| — | Transition a letter request's status and persist an uploaded signature image on each state change. |
Diagrams
flowchart TD
subgraph pres["Presentation"]
V1["Letter Request List"]
V2["Create Paired Request"]
end
subgraph app["Application (Controllers)"]
LC["LetterController"]
end
subgraph dom["Domain (Services/Workflow)"]
ST["changeState Transition"]
STAT["Statistics Aggregation"]
end
subgraph data["Data (Models/Tables)"]
LR["LetterRequest (letters)"]
EMP["Employee"]
CO["Company"]
end
V1 --> LC
V2 --> LC
LC --> STAT
LC --> ST
STAT --> LR
ST --> LR
LR --> EMP
LR --> COflowchart TD
IDX["index() list plus statistics"]
STORE["store() create paired rows"]
CHG["changeState(id, status, all)"]
SHOW["show() render letter"]
subgraph model["LetterRequest (letters)"]
QRY["where request_id"]
CRT["create sender and receiver rows"]
UPD["update status and signature"]
end
IDX --> QRY
STORE --> CRT
CHG --> QRY
QRY --> UPD
SHOW --> QRYflowchart LR
EMP["Employee Sender"]
RCV["Receiver / Manager"]
HR["HR Staff"]
subgraph sb["Separation Letters - System Boundary"]
UC1(["Create Paired Request"])
UC2(["View Request List"])
UC3(["Change State"])
UC4(["Upload Signature"])
UC5(["Monitor Statistics"])
end
EMP --- UC1
EMP --- UC2
RCV --- UC3
HR --- UC5
UC3 -.->|include| UC4sequenceDiagram
actor EMP as Employee Sender
actor RCV as Receiver
participant LC as LetterController
participant LR as letters table
EMP->>LC: store(letter_type, receiver)
LC->>LR: create sender and receiver rows (request_id)
LR-->>EMP: pending request listed
RCV->>LC: changeState(id, status)
opt signature provided
LC->>LC: move signature image to storage
end
LC->>LR: update all rows sharing request_id
LR-->>RCV: status evaluated or canceledflowchart LR
EMP["Employee Sender"]
RCV["Receiver / Manager"]
HR["HR Staff"]
P1(("1.0 Create Paired Request"))
P2(("2.0 Change State"))
P3(("3.0 Report Statistics"))
D1[("D1 letters")]
EMP -->|letter details| P1
P1 -->|paired rows| D1
RCV -->|status and signature| P2
P2 -->|updated status| D1
D1 -->|counts| P3
P3 -->|statistics| HRflowchart LR
RCV["Receiver / Manager"]
P21(("2.1 Validate State Change"))
P22(("2.2 Store Signature Image"))
P23(("2.3 Update Paired Rows"))
P24(("2.4 Record Cancellation Reason"))
D1[("D1 letters")]
D2[("D2 signature files")]
RCV -->|status and signature| P21
P21 -->|image file| P22
P22 -->|file name| D2
P21 -->|request_id| P23
P23 -->|status update| D1
P21 -->|reason| P24
P24 -->|cancellation_reasons| D1classDiagram
class LetterWorkflow {
<<interface>>
+createPairedRequest(data)
+changeState(id, status, signature)
}
class LetterService {
+createPairedRequest(data)
+changeState(id, status, signature)
}
class LetterController {
+index()
+store(request)
+changeState(request, id, status, all)
}
class LetterRequest {
+int request_id
+string letter_type
+string status
+string signature
+employeeSender()
+employeeReceiver()
+newemployee()
}
class Employee {
+int id
+string empName
}
class Company {
+int id
+string name
}
LetterController ..> LetterWorkflow
LetterWorkflow <|.. LetterService
LetterService --> LetterRequest
LetterRequest "*" --> "1" Employee : sender and receiver
LetterRequest "*" --> "1" CompanyRecruitment Live
The Recruitment module lets HR raise headcount requisitions broken down per designation across five action statuses, automatically computing grand totals and a remaining balance. From each requisition it auto-generates one Pending candidate placeholder per unit of demand, then supports Excel import and export together with manual status transitions until the allocated demand is filled. Balance enforcement keeps the number of candidates aligned with the approved requisition.
Responsibilities
- Capture manpower requisitions with per-designation counts across five action statuses and auto-compute grand totals and remaining balance.
- Auto-generate one Pending ManpowerCandidate placeholder per unit of allocated, ticket, visa, and internal demand through createCandidatesFromManpower.
- Import candidates from Excel with header validation, duplicate-passport rejection, and balance-limit enforcement.
- Apply manual candidate status transitions of Approved, Rejected, and Returned and block uploads when the remaining balance reaches zero.
- Export candidate data as single, all-candidates, per-designation, and grouped-main sheets.
- Track remaining balance as grand total minus current candidate count throughout the requisition lifecycle.
Actors
Data Entities
Subsystems
Workflow
- HR submits Manpower requisition (per-designation counts, status Open/Closed/Cancel/Hold)
- Compute grand totals & remaining balance
- createCandidatesFromManpower(): one ManpowerCandidate (Pending) per unit of allocated/ticket/visa/internal
- remaining = grandTotal − candidatesCount
- Fill candidates manually or via Excel import (passport unique, balance ≤ capacity)
- Manual candidate status transitions: Approved / Rejected / Returned
Key Functional Requirements
| ID | Requirement |
|---|---|
| FR-REC-01 | Let HR raise a manpower requisition with per-designation required counts across five action statuses, auto-computing grand totals and remaining balance. |
| FR-REC-02 | Auto-generate one placeholder ManpowerCandidate (status Pending) per unit of allocated / ticket-issued / visa-under-process / internal-arrangements demand at requisition creation. |
| FR-REC-03 | Support Excel import of requisitions and candidates, validating headers, rejecting duplicate passport numbers, and refusing imports exceeding the remaining balance. |
| FR-REC-20 | Export manpower requisitions and candidates to Excel (single, all-candidates, designation, and grouped-main variants). |
Diagrams
flowchart TD
subgraph Pres["Presentation"]
V1["Manpower Requisition View"]
V2["Candidate List & Excel Tools"]
end
subgraph App["Application (Controllers)"]
C1["ManpowerController"]
C2["ManpowerCandidateController"]
end
subgraph Dom["Domain (Services/Workflow)"]
S1["createCandidatesFromManpower()"]
S2["Totals & Balance Calculator"]
S3["Excel Import / Export"]
end
subgraph Dat["Data (Models/Tables)"]
M1["Manpower (manpower)"]
M2["ManpowerCandidate (manpowercandidates)"]
end
subgraph Ext["External"]
X1["Excel Files"]
end
V1 --> C1
V2 --> C2
C1 --> S1
C1 --> S2
C2 --> S3
S1 --> M2
S2 --> M1
S3 --> M2
S3 --> X1flowchart TD
A["ManpowerController.store()"]
B["computeGrandTotals()"]
C["createCandidatesFromManpower()"]
D{"demand unit remaining?"}
E["spawn ManpowerCandidate (Pending)"]
F["remaining = grandTotal - candidatesCount"]
G["persist Manpower"]
A --> B
B --> C
C --> D
D -->|Yes| E
E --> D
D -->|No| F
F --> Gflowchart LR
HR["HR / Recruitment Officer"]
subgraph SB["Recruitment - System Boundary"]
U1(["Submit Manpower Requisition"])
U2(["Auto-Generate Candidates"])
U3(["Import Candidates via Excel"])
U4(["Change Candidate Status"])
U5(["Export Candidates"])
U6(["View Remaining Balance"])
end
HR --- U1
HR --- U3
HR --- U4
HR --- U5
HR --- U6
U1 -.->|include| U2
U3 -.->|extend| U4sequenceDiagram
actor HR as HR Officer
participant C as ManpowerController
participant S as Requisition Service
participant XL as Excel Handler
participant DB as Database
HR->>C: submitRequisition(designationCounts)
C->>S: computeGrandTotals()
S->>DB: save Manpower
C->>S: createCandidatesFromManpower()
loop each demand unit
S->>DB: insert ManpowerCandidate Pending
end
S-->>C: remaining balance
HR->>C: importExcel(file)
C->>XL: validateHeaders()
alt duplicate passport or over balance
XL-->>HR: reject rows
else valid rows
XL->>DB: insert candidates
end
HR->>C: updateStatus(Approved)
C->>DB: set statusflowchart LR
E1["HR Officer"]
E2["Excel File"]
P1(("1.0 Capture Requisition"))
P2(("2.0 Compute Balance"))
P3(("3.0 Generate Candidates"))
P4(("4.0 Import and Export"))
P5(("5.0 Update Status"))
D1[("D1 Manpower")]
D2[("D2 ManpowerCandidates")]
E1 -->|counts| P1
P1 -->|requisition| D1
D1 -->|totals| P2
P2 -->|demand units| P3
P3 -->|placeholders| D2
E2 -->|rows| P4
P4 -->|validated| D2
E1 -->|decision| P5
P5 -->|status| D2flowchart LR
HR["HR Officer"]
XL["Excel File"]
P41(("4.1 Validate Headers"))
P42(("4.2 Reject Duplicate Passports"))
P43(("4.3 Check Remaining Balance"))
P44(("4.4 Insert Candidates"))
P45(("4.5 Export Sheets"))
D2[("D2 ManpowerCandidates")]
XL -->|upload| P41
P41 -->|clean rows| P42
P42 -->|unique| P43
P43 -->|within balance| P44
P44 -->|new rows| D2
D2 -->|records| P45
P45 -->|file| HRclassDiagram
class RequisitionService {
<<interface>>
+computeGrandTotals(manpower)
+createCandidatesFromManpower(manpower)
}
class ManpowerRequisitionService {
+computeGrandTotals(manpower)
+createCandidatesFromManpower(manpower)
+remainingBalance(manpower)
}
class ManpowerController {
+store(request)
+import(file)
+export()
}
class Manpower {
+int id
+string designation
+int grandTotal
+int remaining
+candidates()
}
class ManpowerCandidate {
+int manpower_id
+string passport
+string status
+manpower()
}
RequisitionService <|.. ManpowerRequisitionService
ManpowerController ..> RequisitionService
Manpower "1" --> "0..*" ManpowerCandidateEmployee Settings Live
The Employee Settings module provides HR master-data administration over the central employee record, covering create-read-update-delete operations, Excel import and export, and multi-company partitioning. It also manages departments, the KPI fiscal-year configuration, and per-employee role-based permission assignment through user_permissions rows with cached checks. Company scoping keeps employees, departments, and designations partitioned by company_id.
Responsibilities
- Create, search, edit, and maintain the roughly 120-column central employee record, including Excel-based editing.
- Provide Excel import and export of employee data with an option to edit through spreadsheets.
- Grant and revoke named permissions per employee via user_permissions rows using case-insensitive, trimmed, cached checks.
- Partition employees, departments, and designations by company_id across three companies with logos and switch active company context.
- Manage departments with name, description, icon, and per-company manager lists.
- Configure the KPI fiscal year of 1 August to 31 July and general settings, while separation status blocks login and hides an employee as an approver.
Actors
Data Entities
Subsystems
Workflow
- HR/Administrator opens employee master list scoped to the active company_id
- Create or import employee records (identity, org, payroll, visa, leave data)
- Edit records directly or via Excel edit path; search by employee code
- Assign employee to company / department and set class (seniority grade)
- Grant or revoke RBAC permissions per employee (writes user_permissions rows)
- Configure KPI fiscal year and shared settings
- Separation status set → employee blocked from login and hidden as approver across modules
Key Functional Requirements
| ID | Requirement |
|---|---|
| FR-EMP-01 | Maintain a central employee record capturing identity, organisational placement, payroll/compensation, visa/immigration, and leave/service data, partitioned by company_id. |
| FR-EMP-02 | Let administrators create, edit, and delete employee records and search employees by code. |
| FR-EMP-03 | Support bulk import and export of employees via Excel, including an Excel-based edit path. |
| FR-EMP-04 | Let administrators grant/revoke each RBAC permission per employee. |
| FR-EMP-05 | Maintain the three tenant companies and their logos, and partition all employees, departments, and designations by company. |
| FR-EMP-06 | Let administrators maintain departments (name, description, icon, and a list of manager employees) per company. |
| FR-AUTH-08 | Resolve authorization through the user_permissions table, granting an action only when a matching (employee_id, permission) row exists, matched case-insensitively and whitespace-trimmed. |
| FR-AUTH-09 | Let an administrator grant or revoke each named permission per employee, creating or deleting the corresponding user_permissions row. |
Diagrams
flowchart TD
subgraph Pres["Presentation"]
V1["Employee Master List"]
V2["Settings & Permission Views"]
end
subgraph App["Application (Controllers)"]
C1["EmployeeController"]
C2["DepartmentController"]
C3["KPIController"]
end
subgraph Dom["Domain (Services/Workflow)"]
S1["Employee Import / Export"]
S2["Permission Cache (isHasPermission)"]
S3["Company Partition (switchCompany)"]
end
subgraph Dat["Data (Models/Tables)"]
M1["Employee (employees)"]
M2["Company (companies)"]
M3["Department"]
M4["UserPermission / Setting"]
end
subgraph Ext["External"]
X1["Excel Files"]
end
V1 --> C1
V2 --> C2
V2 --> C3
C1 --> S1
C1 --> S2
C1 --> S3
S1 --> M1
S2 --> M4
S3 --> M2
S3 --> M3
S1 --> X1flowchart TD
A["EmployeeController.assignPermission()"]
B["normalize (trim + lowercase)"]
C{"grant or revoke?"}
D["insert user_permissions row"]
E["delete user_permissions row"]
F["flush cached permission set"]
G["isHasPermission(name) check"]
A --> B
B --> C
C -->|grant| D
C -->|revoke| E
D --> F
E --> F
F --> Gflowchart LR
ADM["Administrator"]
HR["HR"]
EMP["Employee"]
SYS["System"]
subgraph SB["Employee Settings - System Boundary"]
U1(["Manage Employee Records"])
U2(["Import / Export Excel"])
U3(["Assign RBAC Permission"])
U4(["Switch Company"])
U5(["Manage Departments"])
U6(["Configure KPI Fiscal Year"])
U7(["Update Own Profile"])
end
ADM --- U1
HR --- U2
ADM --- U3
HR --- U4
HR --- U5
ADM --- U6
EMP --- U7
U1 -.->|include| U2
SYS -.->|enforce| U3sequenceDiagram
actor ADM as Administrator
actor HR as HR
participant C as EmployeeController
participant PS as Permission Service
participant XL as Excel Handler
participant DB as Database
HR->>C: openMasterList(company_id)
C->>DB: query employees by company
DB-->>C: employee rows
HR->>C: importEmployees(file)
C->>XL: validate and parse
XL->>DB: upsert employees
ADM->>C: assignPermission(employee, name)
C->>PS: normalize(trim, lowercase)
PS->>DB: write user_permissions
PS->>PS: flush cache
opt separation status
DB-->>C: hide as approver and block login
endflowchart LR
E1["Administrator"]
E2["HR"]
E3["Employee"]
P1(("1.0 Manage Employees"))
P2(("2.0 Import and Export"))
P3(("3.0 Assign Permissions"))
P4(("4.0 Partition by Company"))
P5(("5.0 Configure Settings"))
D1[("D1 Employees")]
D2[("D2 User Permissions")]
D3[("D3 Companies and Settings")]
E2 -->|record data| P1
P1 -->|profile| D1
E2 -->|file| P2
P2 -->|rows| D1
E1 -->|grant or revoke| P3
P3 -->|permission| D2
P4 -->|company scope| D1
E1 -->|kpi year| P5
P5 -->|config| D3
E3 -->|own profile| P1flowchart LR
ADM["Administrator"]
SYS["Session Middleware"]
P31(("3.1 Select Employee"))
P32(("3.2 Normalize Name"))
P33(("3.3 Grant or Revoke"))
P34(("3.4 Write user_permissions"))
P35(("3.5 Cache Permission Check"))
D2[("D2 User Permissions")]
ADM -->|permission name| P31
P31 -->|target| P32
P32 -->|trimmed lowercase| P33
P33 -->|change| P34
P34 -->|rows| D2
D2 -->|cached set| P35
P35 -->|authorize| SYSclassDiagram
class PermissionChecker {
<<interface>>
+isHasPermission(employee, name)
+grant(employee, name)
+revoke(employee, name)
}
class PermissionCacheService {
+isHasPermission(employee, name)
+grant(employee, name)
+revoke(employee, name)
+normalize(name)
}
class EmployeeController {
+index(company_id)
+import(file)
+assignPermission(request)
}
class Employee {
+int id
+string code
+int company_id
+string grade
+permissions()
}
class Company {
+int id
+string name
+string logo
+employees()
}
class Department {
+int id
+int company_id
+string name
+managers()
}
class UserPermission {
+int employee_id
+string name
}
class Setting {
+string kpi_year
}
PermissionChecker <|.. PermissionCacheService
EmployeeController ..> PermissionChecker
Employee "1" --> "0..*" UserPermission
Company "1" --> "0..*" Employee
Company "1" --> "0..*" DepartmentOffer Letter Live
Generates offer letters from candidates already marked Approved, keeping a single letter per candidate through an updateOrCreate on the candidate reference. Each letter carries around thirty content toggles with values and a serial number, and routes through a three-stage Recruitment, HR Director, and GM approval chain. Every approval stamps the signatory name, position, and signature configured per company at the moment of sign-off.
Responsibilities
- Generate an offer letter only from an Approved candidate that has no existing letter.
- Provide a content editor with clause enable toggles, values, and a serial number.
- Route each letter through the Recruitment Reviewed, HR Director, and GM Approval stages in sequence.
- Snapshot the per-company signatory name, position, and signature onto each approval record.
- Support structured Return and Reject actions with reasons and role at any stage.
- Let an Administrator maintain the HR Director and GM signatory configuration per company.
Actors
Data Entities
Subsystems
Workflow
- HR store() from Approved candidate → Pending Recruitment Reviewed (candidate.has_offer_letter = 1)
- HR approve() → Pending HR Director
- HR Director approve() → Pending GM Approval (+ OfferLetterApproval: HR Director, signature)
- GM approve() → Approved (+ OfferLetterApproval: GM, signature)
- returnReject() at any stage → Return / Reject with reason and role
Key Functional Requirements
| ID | Requirement |
|---|---|
| FR-REC-14 | Generate offer letters only from Approved candidates and prevent duplicate offer letters per candidate. |
| FR-REC-15 | Enforce a three-stage offer-letter approval chain (Recruitment Reviewed → HR Director → GM Approval), stamping the configured signatory name/position/signature per company at each approval. |
| FR-REC-16 | Allow offer letters to be returned/rejected at any approval stage with reasons and role attribution. |
| FR-REC-17 | Let administrators configure per-company HR-Director and GM signatory details used on approved offer letters. |
Diagrams
flowchart TD
subgraph Presentation["Presentation"]
UI1["Offer Letter Editor"]
UI2["Approval Queues"]
UI3["Signatory Config (Admin)"]
end
subgraph Application["Application (Controllers)"]
C1["Employee OfferLetterController"]
C2["Admin OfferLetterController"]
end
subgraph Domain["Domain (Services/Workflow)"]
W1["Offer Generation"]
W2["Approval Chain Engine"]
W3["Return / Reject Handler"]
end
subgraph Data["Data (Models/Tables)"]
M1["OfferLetter"]
M2["OfferLetterApproval"]
M3["OfferLetterManage"]
M4["OfferLetterReturnReject"]
M5["CandidateRequest"]
end
subgraph External["External"]
E1["PDF Renderer"]
end
UI1 --> C1
UI2 --> C1
UI3 --> C2
C1 --> W1
C1 --> W2
C1 --> W3
C2 --> M3
W1 --> M1
W1 --> M5
W2 --> M2
W3 --> M4
M1 --> E1flowchart TD
subgraph OLC["OfferLetterController"]
a1["store() : updateOrCreate"]
a2["approve() : stage transition"]
a3["returnReject() : reasons + role"]
a4["deleteOfferLetter()"]
end
CR[("candidate_requests.has_offer_letter")]
MG[("offer_letter_manages")]
M1[("offer_letters.status")]
M2[("offer_letter_approvals")]
M4[("offer_letter_return_rejects")]
a1 -->|"has_offer_letter = 1"| CR
a1 -->|"Pending Recruitment Reviewed"| M1
a2 -->|"Recruitment to HR Director to GM"| M1
MG -->|"signatory snapshot"| a2
a2 --> M2
a3 -->|"Return or Reject"| M1
a3 --> M4
a4 --> M1flowchart LR
HR["HR / Recruitment Officer"]
HD["HR Director"]
GM["General Manager"]
AD["Administrator"]
subgraph SB["Offer Letter - System Boundary"]
U1(["Generate Offer Letter"])
U2(["Edit Offer Content"])
U3(["Recruitment Review"])
U4(["HR Director Approve"])
U5(["GM Approve"])
U6(["Return or Reject"])
U7(["Configure Signatories"])
end
HR --- U1
HR --- U2
HR --- U3
HD --- U4
GM --- U5
HR --- U6
HD --- U6
GM --- U6
AD --- U7
U1 -.->|"include"| U2
U4 -.->|"include"| U7sequenceDiagram
actor HR as HR Officer
actor HD as HR Director
actor GM as General Manager
participant C as OfferLetterController
participant OL as OfferLetter
participant MG as OfferLetterManage
participant AP as OfferLetterApproval
HR->>C: store() from Approved candidate
C->>OL: updateOrCreate (Pending Recruitment Reviewed)
HR->>C: approve()
C-->>OL: Pending HR Director
HD->>C: approve()
C->>MG: read HR Director signatory
C->>AP: snapshot signature
C-->>OL: Pending GM Approval
GM->>C: approve()
C->>MG: read GM signatory
C->>AP: snapshot signature
C-->>OL: Approved
opt Any stage
HR->>C: returnReject()
C-->>OL: Return or Reject
endflowchart LR
HR["HR Officer"]
HD["HR Director"]
GM["General Manager"]
AD["Administrator"]
p1(("1.0 Generate Offer"))
p2(("2.0 Recruitment Review"))
p3(("3.0 Director & GM Approval"))
p4(("4.0 Return / Reject"))
d1[("D1 Offer Letters")]
d2[("D2 Approvals")]
d3[("D3 Signatory Config")]
HR -->|"candidate & content"| p1
p1 -->|"Pending Recruitment Reviewed"| d1
HR -->|"review"| p2
p2 --> d1
HD -->|"approve"| p3
GM -->|"approve"| p3
d3 -->|"name / position / signature"| p3
p3 --> d2
p3 -->|"Approved"| d1
AD -->|"signatory setup"| d3
HR -->|"return / reject"| p4
p4 -->|"reasons + role"| d1flowchart LR
d1[("D1 Offer Letters")]
d3[("D3 Signatory Config")]
HD["HR Director"]
GM["General Manager"]
p31(("3.1 HR Director Approve"))
p32(("3.2 Snapshot Signatory"))
p33(("3.3 GM Approve"))
d2[("D2 Approvals")]
d1 -->|"Pending HR Director"| p31
HD --> p31
p31 -->|"Pending GM Approval"| p32
d3 -->|"per company signatory"| p32
p32 --> d2
p32 --> p33
GM --> p33
p33 -->|"Approved"| d1
p33 --> d2classDiagram
class OfferLetterController {
+store()
+approve()
+returnReject()
}
class ApprovalStage {
<<interface>>
+approve(letter)
}
class HrDirectorStage {
+approve(letter)
}
class GmApprovalStage {
+approve(letter)
}
class OfferLetter {
+int id
+string candidate_request_id
+string status
+bool basic_salary_enabled
+candidateRequest()
}
class OfferLetterApproval {
+int offer_letter_id
+string name
+string position
+string signature
}
class OfferLetterManage {
+int company_id
+string hr_director_name
+string gm_approval_name
}
class OfferLetterReturnReject {
+int offer_letter_request_id
+string reason
+string role
}
OfferLetterController ..> ApprovalStage
ApprovalStage <|.. HrDirectorStage
ApprovalStage <|.. GmApprovalStage
OfferLetter "1" --> "*" OfferLetterApproval : stamped by
OfferLetter "1" --> "*" OfferLetterReturnReject : logs
OfferLetterManage "1" --> "*" OfferLetterApproval : suppliesWorker Recruitment Live
Coordinates bulk labour recruitment by dispatching manpower requisitions to external recruitment agencies that bulk-submit worker applications. Each submission advances through interviewer scoring, HR Use and HR Offer decisions, immigration recording, and a final HR Director acceptance. A dedicated session-based agency portal lets an agency complete one application row per required worker.
Responsibilities
- Create and manage recruitment agencies and their separate session-based portal credentials.
- Generate bulk worker requisitions that create one blank application row per required worker and designation.
- Capture agency-submitted worker bio data, passport copies, and certificates against each application row.
- Record first and second interviewer scores and selection status for each applicant.
- Capture HR Use proceed-for-employment decisions and HR Offer salary, allowance, probation, and contract terms.
- Record immigration data and the final HR Director accept or reject decision together with the rejecting role.
Actors
Data Entities
Subsystems
Workflow
- HR requisition creates blank rows → Pending Agency
- Agency submits applications → Pending HR Reviewed
- HR assigns interviewers → Pending First Interviewer
- 2nd interviewer set → Pending Second Interviewer (else → Pending HR Checked)
- Interviews done → Pending HR Checked
- → Pending Immigration if HR Offer exists, else → Pending HR Offer → Pending Immigration
- Immigration recorded → Pending HR Director
- HR Director → Accepted / Rejected (record rejection_role)
Key Functional Requirements
| ID | Requirement |
|---|---|
| FR-REC-10 | Let HR create recruitment agencies and provide a separate session-based portal for agencies to view assigned worker requisitions. |
| FR-REC-11 | Let HR raise bulk worker requisitions against a manpower reference and agency, creating one blank application row per required worker per designation. |
| FR-REC-12 | Allow agencies to bulk-populate worker applications (bio, passport copies, certificates) and submit them for HR review. |
| FR-REC-13 | Route worker applications through HR-assigned interviewer(s), HR Use, HR Offer, Immigration, and HR-Director acceptance/rejection, recording the rejecting role. |
Diagrams
flowchart TD
subgraph Presentation["Presentation"]
UI1["HR & Interview UI"]
UI2["Agency Portal (Session)"]
end
subgraph Application["Application (Controllers)"]
C1["Workers"]
C2["WorkersEgencyController"]
end
subgraph Domain["Domain (Services/Workflow)"]
W1["Requisition & Status Workflow"]
W2["Interview Scoring"]
W3["Acceptance Decision"]
end
subgraph Data["Data (Models/Tables)"]
M1["WorkersRequest"]
M2["WorkersAgencyApplication"]
M3["WorkersInterview"]
M4["WorkersHrUse / HrOffer / Immigration"]
M6["WorkersEgency"]
end
subgraph External["External"]
E1["Recruitment Agency"]
end
UI1 --> C1
UI2 --> C1
UI2 --> C2
E1 --> UI2
C1 --> W1
C1 --> W2
C1 --> W3
C2 --> M6
W1 --> M1
W1 --> M2
W2 --> M3
W3 --> M4flowchart TD
subgraph Workers["Workers Controller"]
m1["store() : create blank rows"]
m2["store_all_applications() : agency submit"]
m3["confirmRowsHrReviewed() : assign interviewers"]
m4["storeInterviewerFirst() / storeInterviewerSecond()"]
m5["storeHrUse() : proceed & salary"]
m6["storeHrOffer() : offer terms"]
m7["storeImmigration() : record visa"]
m8["hrDirectorConfirm() / hrDirectorReject()"]
end
DB[("workers_agency_applications.status")]
m1 -->|"Pending Agency"| DB
m2 -->|"Pending HR Reviewed"| DB
m3 -->|"Pending First Interviewer"| DB
m4 -->|"Pending HR Checked"| DB
m5 -->|"Pending HR Offer"| DB
m6 -->|"Pending Immigration"| DB
m7 -->|"Pending HR Director"| DB
m8 -->|"Accepted / Rejected"| DBflowchart LR
HR["HR / Recruitment Officer"]
AG["Recruitment Agency"]
IN["Interviewer"]
HD["HR Director"]
subgraph SB["Worker Recruitment - System Boundary"]
U1(["Create Agency"])
U2(["Raise Bulk Requisition"])
U3(["Submit Applications"])
U4(["Score Interviews"])
U5(["Record HR Use / Offer"])
U6(["Record Immigration"])
U7(["Accept or Reject"])
end
HR --- U1
HR --- U2
HR --- U5
HR --- U6
AG --- U3
IN --- U4
HD --- U7
U2 -.->|"include"| U3
U3 -.->|"include"| U4
U7 -.->|"extend"| U6sequenceDiagram
actor HR as HR Officer
actor AG as Recruitment Agency
actor HD as HR Director
participant C as Workers Controller
participant M as WorkersAgencyApplication
participant I as WorkersInterview
HR->>C: store() bulk requisition
C->>M: create blank rows Pending Agency
AG->>C: store_all_applications()
C-->>M: update Pending HR Reviewed
HR->>C: confirmRowsHrReviewed()
C-->>M: Pending First Interviewer
loop Each interview
HR->>C: storeInterviewerFirst()
C->>I: create interview score
end
opt HR Use and Offer
HR->>C: storeHrUse() and storeHrOffer()
C-->>M: Pending Immigration
end
HD->>C: hrDirectorConfirm()
C-->>M: Accepted or Rejectedflowchart LR
HR["HR Officer"]
AG["Recruitment Agency"]
HD["HR Director"]
p1(("1.0 Raise Requisition"))
p2(("2.0 Submit Applications"))
p3(("3.0 Conduct Interviews"))
p4(("4.0 Employment & Immigration"))
p5(("5.0 Director Decision"))
d1[("D1 Worker Requests")]
d2[("D2 Applications")]
d3[("D3 Interviews")]
HR -->|"designations & counts"| p1
p1 -->|"blank rows"| d2
p1 --> d1
AG -->|"worker bio & documents"| p2
p2 --> d2
d2 -->|"reviewed rows"| p3
p3 -->|"scores"| d3
p3 --> p4
p4 -->|"offer & visa"| d2
p4 --> p5
HD -->|"accept / reject"| p5
p5 -->|"final status"| d2flowchart LR
d2[("D2 Applications")]
HR["HR Officer"]
p41(("4.1 Record HR Use"))
p42(("4.2 Build HR Offer"))
p43(("4.3 Record Immigration"))
d4[("D4 HR Use")]
d5[("D5 HR Offer")]
d6[("D6 Immigration")]
d2 -->|"Pending HR Checked rows"| p41
HR -->|"proceed & salary"| p41
p41 --> d4
p41 -->|"Pending HR Offer"| p42
HR -->|"salary, OT, allowances, contract"| p42
p42 --> d5
p42 -->|"Pending Immigration"| p43
HR -->|"visa & recruitment number"| p43
p43 --> d6
p43 -->|"Pending HR Director"| d2classDiagram
class Workers {
+store()
+confirmRowsHrReviewed()
+storeHrUse()
+hrDirectorConfirm()
}
class RecruitmentStage {
<<interface>>
+advance(app)
}
class InterviewStage {
+advance(app)
}
class OfferStage {
+advance(app)
}
class WorkersRequest {
+int id
+int agency_id
+string status
+applications()
}
class WorkersAgencyApplication {
+int id
+int worker_request_id
+string status
+json interviewers
+worker_request()
}
class WorkersInterview {
+int worker_application_id
+string selection_status
+string overall_interview_evaluation
}
class WorkersEgency {
+int id
+string email
+string password_show
}
Workers ..> RecruitmentStage
RecruitmentStage <|.. InterviewStage
RecruitmentStage <|.. OfferStage
WorkersEgency "1" --> "*" WorkersRequest : receives
WorkersRequest "1" --> "*" WorkersAgencyApplication : contains
WorkersAgencyApplication "1" --> "*" WorkersInterview : scored byHR Announcement Live
Authorised HR staff publish targeted announcements to all employees or a selected department, each carrying a priority and a publish and expiry window and optionally requiring per-employee read confirmation. Employees view published announcements inside their window and confirm reading, which is recorded with a timestamp in the announcement_employees ledger. Once confirmations exist for an announcement, its deletion is blocked.
Responsibilities
- Author announcements with target audience, title, message, priority, and a publish and expiry window.
- Publish or unpublish announcements and scope visibility by company, department, and date window.
- Record each employee read confirmation with a timestamp in the ledger.
- Prevent deletion of an announcement once confirmations exist.
Actors
Data Entities
Subsystems
Workflow
- HR / authorised employee creates an announcement (target, priority, publish/expiry window, optional confirmation)
- Announcement published within its window
- Employees view and confirm read; confirmation recorded with timestamp
- System prevents deletion once any confirmations exist
Key Functional Requirements
| ID | Requirement |
|---|---|
| FR-DOC-15 | Let authorised employees create announcements targeting all employees or a department, with priority, publish/expiry window and optional read-confirmation, recording each confirmation with timestamp. |
| FR-DOC-16 | Prevent deletion of an announcement that already has employee confirmations. |
Diagrams
flowchart TD
subgraph pres["Presentation"]
V1["Announcement Index"]
V2["Create Announcement"]
end
subgraph app["Application (Controllers)"]
AC["AnnouncementController"]
end
subgraph dom["Domain (Services/Workflow)"]
PUB["Publish and Window Scope"]
CNF["Confirmation Ledger"]
GRD["Deletion Guard"]
end
subgraph data["Data (Models/Tables)"]
AN["Announcement (announcements)"]
AE["AnnouncementEmployee (announcement_employees)"]
end
V1 --> AC
V2 --> AC
AC --> PUB
AC --> CNF
AC --> GRD
PUB --> AN
CNF --> AE
GRD --> AEflowchart TD
STORE["store() validate and create"]
PUB["publish() status Published"]
CONF["confirm() firstOrCreate ledger"]
DEL["destroy() guarded delete"]
subgraph model["Announcement"]
SP["scopePublished(window)"]
SNC["scopeNotConfirmedBy(emp)"]
CNT["confirmations count"]
end
AE["AnnouncementEmployee"]
STORE --> SP
PUB --> SP
CONF --> AE
DEL --> CNT
SNC --> AEflowchart LR
HR["HR / Employee Relations"]
AUTH["Authorised Employee"]
EMP["Employee"]
subgraph sb["HR Announcement - System Boundary"]
UC1(["Author Announcement"])
UC2(["Publish / Unpublish"])
UC3(["View Announcement"])
UC4(["Confirm Read"])
UC5(["Delete Announcement"])
end
HR --- UC1
HR --- UC2
AUTH --- UC1
EMP --- UC3
EMP --- UC4
HR --- UC5
UC3 -.->|extend| UC4
UC5 -.->|guarded by| UC4sequenceDiagram
actor HR as HR Staff
actor EMP as Employee
participant AC as AnnouncementController
participant AN as announcements
participant AE as announcement_employees
HR->>AC: store(title, priority, window)
AC->>AN: create announcement
HR->>AC: publish(announcement)
AC->>AN: status = Published
EMP->>AC: confirm(announcement)
AC->>AE: firstOrCreate(read_date = now)
AE-->>EMP: confirmation recorded
opt delete requested
HR->>AC: destroy(announcement)
AC->>AE: count confirmations
alt confirmations exist
AC-->>HR: deletion blocked
else none exist
AC->>AN: delete
end
endflowchart LR
HR["HR Staff"]
EMP["Employee"]
P1(("1.0 Author Announcement"))
P2(("2.0 Publish and Scope"))
P3(("3.0 Confirm Read"))
D1[("D1 announcements")]
D2[("D2 announcement_employees")]
HR -->|announcement data| P1
P1 -->|draft record| D1
HR -->|publish| P2
P2 -->|status and window| D1
D1 -->|published items| EMP
EMP -->|read confirmation| P3
P3 -->|timestamped row| D2flowchart LR
EMP["Employee"]
HR["HR Staff"]
P31(("3.1 Check Require Confirmation"))
P32(("3.2 Record Read Timestamp"))
P33(("3.3 Evaluate Deletion Guard"))
D1[("D1 announcements")]
D2[("D2 announcement_employees")]
EMP -->|read action| P31
P31 -->|timestamp| P32
P32 -->|firstOrCreate| D2
HR -->|delete request| P33
D2 -->|confirmation count| P33
P33 -->|allow or block| D1classDiagram
class AnnouncementRepository {
<<interface>>
+publish(id)
+recordConfirmation(id, employeeId)
+canDelete(id)
}
class AnnouncementController {
+store(request)
+publish(announcement)
+confirm(request, announcement)
+destroy(announcement)
}
class Announcement {
+int id
+string title
+string priority
+date published_date
+date expiry_date
+bool require_confirmation
+string status
+scopePublished()
+confirmations()
}
class AnnouncementEmployee {
+int announcement_id
+int employee_id
+datetime read_date
+employee()
+announcement()
}
class Employee {
+int id
+string empName
}
AnnouncementController ..> AnnouncementRepository
AnnouncementRepository <|.. Announcement
Announcement "1" --> "0..*" AnnouncementEmployee : confirmations
AnnouncementEmployee "*" --> "1" EmployeeNotation Reference
| Symbol / Convention | Meaning |
|---|---|
| Rectangle | External entity (DFD) or actor / component (architecture) |
| Circle (numbered) | Process in a data-flow diagram; the number encodes its decomposition level |
| Open cylinder | Data store (database table or persisted collection) |
| Rounded shape | Use case within a system boundary |
| «interface» | Abstraction (SOLID) that concrete classes implement and controllers depend upon |
| Solid arrow | Dependency, call, or data flow (direction as drawn) |
| Dashed arrow | Return message (sequence) or «include» / «extend» (use case) or realizes / depends (class) |
| "1" ··· "0..*" | Multiplicity on class-diagram associations |