develop full initial module
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
using ERPCore.Domain.Enums;
|
||||
|
||||
namespace ERPCore.Domain.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Per employee/day attendance row (FR-HR-ATT). <see cref="WorkShiftId"/> is
|
||||
/// snapshotted from the employee's shift at ingestion time (docs/12-BACKEND-HRM.md
|
||||
/// A.3) so a later shift reassignment never retroactively changes historical
|
||||
/// Late/OT figures. Model: docs/12-BACKEND-HRM.md Part C.4.
|
||||
/// </summary>
|
||||
public class AttendanceRecord
|
||||
{
|
||||
public int AttendanceRecordId { get; set; }
|
||||
public int? AttendanceUploadBatchId { get; set; }
|
||||
public AttendanceUploadBatch? AttendanceUploadBatch { get; set; }
|
||||
public int EmployeeId { get; set; }
|
||||
public Employee? Employee { get; set; }
|
||||
|
||||
public DateTime AttendanceDate { get; set; }
|
||||
public TimeSpan? CheckIn { get; set; }
|
||||
public TimeSpan? CheckOut { get; set; }
|
||||
public int WorkShiftId { get; set; }
|
||||
public WorkShift? WorkShift { get; set; }
|
||||
|
||||
public int WorkingMinutes { get; set; }
|
||||
public int LateMinutes { get; set; }
|
||||
public int EarlyLeaveMinutes { get; set; }
|
||||
public int OvertimeMinutes { get; set; }
|
||||
|
||||
public AttendanceStatus AttendanceStatus { get; set; }
|
||||
public RowValidationStatus RowValidationStatus { get; set; } = RowValidationStatus.Valid;
|
||||
public int? DuplicateOfAttendanceRecordId { get; set; }
|
||||
|
||||
public string? Notes { get; set; }
|
||||
public bool IsManualOverride { get; set; }
|
||||
public int? EditedBy { get; set; }
|
||||
public DateTime? EditedAt { get; set; }
|
||||
|
||||
public uint RowVersion { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using ERPCore.Domain.Enums;
|
||||
|
||||
namespace ERPCore.Domain.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Attendance upload batch (FR-HR-ATT) — the transactional document driving the
|
||||
/// exact status flow Draft→Validated→Confirmed→UsedInPayroll. Scoped to exactly
|
||||
/// one payroll period, numbered via <see cref="NumberSequence"/> (docType "ATT").
|
||||
/// Model: docs/12-BACKEND-HRM.md Part C.4.
|
||||
/// </summary>
|
||||
public class AttendanceUploadBatch
|
||||
{
|
||||
public int AttendanceUploadBatchId { get; set; }
|
||||
public string DocNo { get; set; } = string.Empty;
|
||||
public DateTime PeriodStart { get; set; }
|
||||
public DateTime PeriodEnd { get; set; }
|
||||
public AttendanceSourceType SourceType { get; set; }
|
||||
public string? OriginalFileName { get; set; }
|
||||
|
||||
public int UploadedBy { get; set; }
|
||||
public DateTime UploadedAt { get; set; }
|
||||
public AttendanceBatchStatus Status { get; set; } = AttendanceBatchStatus.Draft;
|
||||
public int? ConfirmedBy { get; set; }
|
||||
public DateTime? ConfirmedAt { get; set; }
|
||||
|
||||
public int RowCountTotal { get; set; }
|
||||
public int RowCountDuplicate { get; set; }
|
||||
public int RowCountError { get; set; }
|
||||
|
||||
public uint RowVersion { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using ERPCore.Domain.Enums;
|
||||
|
||||
namespace ERPCore.Domain.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Branch/location master (FR-HR-MD-01) — multi-branch readiness. Referenced
|
||||
/// optionally by <see cref="Employee.BranchId"/> and <see cref="PayrollRun.BranchId"/>.
|
||||
/// Deactivated, not deleted, when referenced. Model: docs/12-BACKEND-HRM.md Part C.1.
|
||||
/// </summary>
|
||||
public class Branch
|
||||
{
|
||||
public int BranchId { get; set; }
|
||||
public string Code { get; set; } = string.Empty;
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string? Address { get; set; }
|
||||
public EntityStatus Status { get; set; } = EntityStatus.Active;
|
||||
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
|
||||
/// <summary>PostgreSQL xmin-backed optimistic concurrency token (ETag source).</summary>
|
||||
public uint RowVersion { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using ERPCore.Domain.Enums;
|
||||
|
||||
namespace ERPCore.Domain.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Department master (FR-HR-MD-01) — unlimited self-nesting for a real org chart
|
||||
/// (unlike the two-level-capped <see cref="Category"/>); cycle prevention is a
|
||||
/// service-level check on write, not a DB constraint. Model: docs/12-BACKEND-HRM.md Part C.1.
|
||||
/// </summary>
|
||||
public class Department
|
||||
{
|
||||
public int DepartmentId { get; set; }
|
||||
public string Code { get; set; } = string.Empty;
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public int? ParentDepartmentId { get; set; }
|
||||
public Department? ParentDepartment { get; set; }
|
||||
public int? HeadEmployeeId { get; set; }
|
||||
public Employee? HeadEmployee { get; set; }
|
||||
public int? BranchId { get; set; }
|
||||
public Branch? Branch { get; set; }
|
||||
public EntityStatus Status { get; set; } = EntityStatus.Active;
|
||||
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
public uint RowVersion { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using ERPCore.Domain.Enums;
|
||||
|
||||
namespace ERPCore.Domain.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Job title master (FR-HR-MD-01), standalone — not FK'd to Department, since a
|
||||
/// title like "Accountant" can exist in multiple departments. Model: docs/12-BACKEND-HRM.md Part C.1.
|
||||
/// </summary>
|
||||
public class Designation
|
||||
{
|
||||
public int DesignationId { get; set; }
|
||||
public string Code { get; set; } = string.Empty;
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public EntityStatus Status { get; set; } = EntityStatus.Active;
|
||||
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
public uint RowVersion { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
using ERPCore.Domain.Enums;
|
||||
|
||||
namespace ERPCore.Domain.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Staff record (FR-HR-MD-02) — distinct from <see cref="User"/> (the system login
|
||||
/// account): not every employee has a login, and not every login belongs to an
|
||||
/// employee. <see cref="UserId"/> is the optional, explicit, human-confirmed link
|
||||
/// between the two (docs/12-BACKEND-HRM.md A.5/C.2, Part B.3.2). Never hard-deleted —
|
||||
/// separation is recorded via <see cref="Status"/> + <see cref="LastWorkingDate"/>.
|
||||
/// <see cref="EmployeeCode"/> is user-entered (not <see cref="NumberSequence"/>-issued):
|
||||
/// HR departments keep their own legacy numbering scheme, and NumberSequence's
|
||||
/// year-scoping is the wrong shape for an identifier that must never look "reset".
|
||||
/// </summary>
|
||||
public class Employee
|
||||
{
|
||||
public int EmployeeId { get; set; }
|
||||
public string EmployeeCode { get; set; } = string.Empty;
|
||||
|
||||
// Identity
|
||||
public string FullName { get; set; } = string.Empty;
|
||||
public string? Nic { get; set; }
|
||||
public DateTime? DateOfBirth { get; set; }
|
||||
public Gender? Gender { get; set; }
|
||||
public string? Nationality { get; set; }
|
||||
public string? ProfilePhotoPath { get; set; }
|
||||
|
||||
// Contact
|
||||
/// <summary>The field used for the bidirectional Employee<->User email cross-check.</summary>
|
||||
public string? Email { get; set; }
|
||||
public string? PersonalMobile { get; set; }
|
||||
public string? AddressLine1 { get; set; }
|
||||
public string? AddressLine2 { get; set; }
|
||||
public string? City { get; set; }
|
||||
public string? PostalCode { get; set; }
|
||||
public string? Country { get; set; }
|
||||
|
||||
// Emergency contact
|
||||
public string? EmergencyContactName { get; set; }
|
||||
public string? EmergencyContactRelationship { get; set; }
|
||||
public string? EmergencyContactPhone { get; set; }
|
||||
|
||||
// Employment
|
||||
public DateTime HireDate { get; set; }
|
||||
public DateTime? ConfirmationDate { get; set; }
|
||||
public DateTime? LastWorkingDate { get; set; }
|
||||
public int DepartmentId { get; set; }
|
||||
public Department? Department { get; set; }
|
||||
public int DesignationId { get; set; }
|
||||
public Designation? Designation { get; set; }
|
||||
public int EmploymentTypeId { get; set; }
|
||||
public EmploymentType? EmploymentType { get; set; }
|
||||
public int? BranchId { get; set; }
|
||||
public Branch? Branch { get; set; }
|
||||
public int WorkShiftId { get; set; }
|
||||
public WorkShift? WorkShift { get; set; }
|
||||
public int? ReportingManagerId { get; set; }
|
||||
public Employee? ReportingManager { get; set; }
|
||||
|
||||
// Statutory (Sri Lanka)
|
||||
public string? EpfNumber { get; set; }
|
||||
public string? EtfNumber { get; set; }
|
||||
public string? TaxIdentificationNumber { get; set; }
|
||||
|
||||
/// <summary>Optional login account link (unique — one User backs at most one Employee).</summary>
|
||||
public int? UserId { get; set; }
|
||||
public User? User { get; set; }
|
||||
|
||||
public EmployeeStatus Status { get; set; } = EmployeeStatus.Active;
|
||||
|
||||
public int CreatedBy { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public int? UpdatedBy { get; set; }
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
public uint RowVersion { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using ERPCore.Domain.Enums;
|
||||
|
||||
namespace ERPCore.Domain.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Employee bank account (FR-HR-MD-03), one-to-many — a future split-payment
|
||||
/// improvement is possible since this isn't a 1:1 scalar set. Exactly one row per
|
||||
/// employee is <see cref="IsPrimary"/>; payroll disbursement targets it.
|
||||
/// Model: docs/12-BACKEND-HRM.md Part C.2.
|
||||
/// </summary>
|
||||
public class EmployeeBankDetail
|
||||
{
|
||||
public int EmployeeBankDetailId { get; set; }
|
||||
public int EmployeeId { get; set; }
|
||||
public Employee? Employee { get; set; }
|
||||
|
||||
public string BankName { get; set; } = string.Empty;
|
||||
public string BranchName { get; set; } = string.Empty;
|
||||
public string AccountNumber { get; set; } = string.Empty;
|
||||
public string AccountHolderName { get; set; } = string.Empty;
|
||||
public string? SwiftCode { get; set; }
|
||||
public bool IsPrimary { get; set; }
|
||||
public EntityStatus Status { get; set; } = EntityStatus.Active;
|
||||
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
public uint RowVersion { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using ERPCore.Domain.Enums;
|
||||
|
||||
namespace ERPCore.Domain.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Uploaded staff document (FR-HR-DOC-02..04) — the user's "Doc". <see cref="StoredFileName"/>/
|
||||
/// <see cref="RelativePath"/> are server-generated (never the client's filename), so the
|
||||
/// file is only ever reachable through <see cref="Services.Interfaces.IFileStorageService"/>,
|
||||
/// never a guessable static path. Archived, not deleted, so the audit trail of what was
|
||||
/// once on file is retained. Model: docs/12-BACKEND-HRM.md Part C.3.
|
||||
/// </summary>
|
||||
public class EmployeeDocument
|
||||
{
|
||||
public int EmployeeDocumentId { get; set; }
|
||||
public int EmployeeId { get; set; }
|
||||
public Employee? Employee { get; set; }
|
||||
public int HrDocumentTypeId { get; set; }
|
||||
public HrDocumentType? HrDocumentType { get; set; }
|
||||
|
||||
public string OriginalFileName { get; set; } = string.Empty;
|
||||
public string StoredFileName { get; set; } = string.Empty;
|
||||
public string RelativePath { get; set; } = string.Empty;
|
||||
public string ContentType { get; set; } = string.Empty;
|
||||
public long SizeBytes { get; set; }
|
||||
public DateTime? IssueDate { get; set; }
|
||||
public DateTime? ExpiryDate { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
|
||||
public int UploadedBy { get; set; }
|
||||
public DateTime UploadedAt { get; set; }
|
||||
public int? VerifiedBy { get; set; }
|
||||
public DateTime? VerifiedAt { get; set; }
|
||||
|
||||
public EmployeeDocumentStatus Status { get; set; } = EmployeeDocumentStatus.Active;
|
||||
public uint RowVersion { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using ERPCore.Domain.Enums;
|
||||
|
||||
namespace ERPCore.Domain.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Loan/Advance (FR-HR-PAY-03) — <see cref="LoanKind"/> discriminates, structurally
|
||||
/// identical otherwise. <see cref="OutstandingBalance"/> is denormalized (parallel to
|
||||
/// <c>StockLayer.QtyRemaining</c>). Numbered via <see cref="NumberSequence"/> (docType "LOAN").
|
||||
/// Model: docs/12-BACKEND-HRM.md Part C.6.
|
||||
/// </summary>
|
||||
public class EmployeeLoan
|
||||
{
|
||||
public int EmployeeLoanId { get; set; }
|
||||
public string DocNo { get; set; } = string.Empty;
|
||||
public int EmployeeId { get; set; }
|
||||
public Employee? Employee { get; set; }
|
||||
public LoanKind LoanKind { get; set; }
|
||||
|
||||
public decimal PrincipalAmount { get; set; }
|
||||
public decimal InterestRate { get; set; }
|
||||
public decimal InstallmentAmount { get; set; }
|
||||
public int NumberOfInstallments { get; set; }
|
||||
public int StartYear { get; set; }
|
||||
public int StartMonth { get; set; }
|
||||
public decimal OutstandingBalance { get; set; }
|
||||
public LoanStatus Status { get; set; } = LoanStatus.Active;
|
||||
|
||||
public int ApprovedBy { get; set; }
|
||||
public DateTime ApprovedAt { get; set; }
|
||||
public int CreatedBy { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public uint RowVersion { get; set; }
|
||||
|
||||
public List<LoanInstallment> Installments { get; set; } = new();
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using ERPCore.Domain.Enums;
|
||||
|
||||
namespace ERPCore.Domain.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Effective-dated salary structure header (FR-HR-PAY-02) — the audit trail a
|
||||
/// salary revision needs (docs/12-BACKEND-HRM.md §13): exactly one row with
|
||||
/// <see cref="EffectiveTo"/> null (the current one) per employee at a time.
|
||||
/// Model: docs/12-BACKEND-HRM.md Part C.6.
|
||||
/// </summary>
|
||||
public class EmployeeSalaryStructure
|
||||
{
|
||||
public int EmployeeSalaryStructureId { get; set; }
|
||||
public int EmployeeId { get; set; }
|
||||
public Employee? Employee { get; set; }
|
||||
|
||||
public DateTime EffectiveFrom { get; set; }
|
||||
public DateTime? EffectiveTo { get; set; }
|
||||
public decimal BasicSalary { get; set; }
|
||||
public string Currency { get; set; } = "LKR";
|
||||
public SalaryStructureStatus Status { get; set; } = SalaryStructureStatus.Active;
|
||||
|
||||
public int ApprovedBy { get; set; }
|
||||
public DateTime ApprovedAt { get; set; }
|
||||
public int CreatedBy { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public uint RowVersion { get; set; }
|
||||
|
||||
public List<EmployeeSalaryStructureLine> Lines { get; set; } = new();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace ERPCore.Domain.Entities;
|
||||
|
||||
/// <summary>Allowance/other-deduction line on a salary structure. Model: docs/12-BACKEND-HRM.md Part C.6.</summary>
|
||||
public class EmployeeSalaryStructureLine
|
||||
{
|
||||
public int EmployeeSalaryStructureLineId { get; set; }
|
||||
public int EmployeeSalaryStructureId { get; set; }
|
||||
public EmployeeSalaryStructure? EmployeeSalaryStructure { get; set; }
|
||||
public int SalaryComponentId { get; set; }
|
||||
public SalaryComponent? SalaryComponent { get; set; }
|
||||
public decimal Amount { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using ERPCore.Domain.Enums;
|
||||
|
||||
namespace ERPCore.Domain.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Labor category master (FR-HR-MD-01) — a master, not an enum, mirroring
|
||||
/// <see cref="Brand"/>: employment categories change with company/labor-law
|
||||
/// policy without wanting a code deploy. Model: docs/12-BACKEND-HRM.md Part C.1.
|
||||
/// </summary>
|
||||
public class EmploymentType
|
||||
{
|
||||
public int EmploymentTypeId { get; set; }
|
||||
public string Code { get; set; } = string.Empty;
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public EntityStatus Status { get; set; } = EntityStatus.Active;
|
||||
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
public uint RowVersion { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using ERPCore.Domain.Enums;
|
||||
|
||||
namespace ERPCore.Domain.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Staff document catalog (FR-HR-DOC-01) — the user's "DocType": a category of
|
||||
/// document (NIC, contract, certificate...), not the uploaded file itself (see
|
||||
/// <see cref="EmployeeDocument"/>, the "Doc"). Deactivated, not deleted, when
|
||||
/// referenced. Model: docs/12-BACKEND-HRM.md Part C.3.
|
||||
/// </summary>
|
||||
public class HrDocumentType
|
||||
{
|
||||
public int HrDocumentTypeId { get; set; }
|
||||
public string Code { get; set; } = string.Empty;
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public HrDocumentCategory Category { get; set; }
|
||||
public bool RequiredAtOnboarding { get; set; }
|
||||
public bool ExpiryTracked { get; set; }
|
||||
public EntityStatus Status { get; set; } = EntityStatus.Active;
|
||||
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
public uint RowVersion { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
namespace ERPCore.Domain.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Per employee/type/year leave entitlement (FR-HR-LV-03). Unique on
|
||||
/// (EmployeeId, LeaveTypeId, Year). RemainingDays is a computed projection, not
|
||||
/// stored. Model: docs/12-BACKEND-HRM.md Part C.5.
|
||||
/// </summary>
|
||||
public class LeaveBalance
|
||||
{
|
||||
public int LeaveBalanceId { get; set; }
|
||||
public int EmployeeId { get; set; }
|
||||
public Employee? Employee { get; set; }
|
||||
public int LeaveTypeId { get; set; }
|
||||
public LeaveType? LeaveType { get; set; }
|
||||
public int Year { get; set; }
|
||||
|
||||
public decimal EntitledDays { get; set; }
|
||||
public decimal TakenDays { get; set; }
|
||||
public decimal CarriedForwardDays { get; set; }
|
||||
public decimal AdjustmentDays { get; set; }
|
||||
|
||||
public uint RowVersion { get; set; }
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using ERPCore.Domain.Enums;
|
||||
|
||||
namespace ERPCore.Domain.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Leave request (FR-HR-LV-02) — transactional document, numbered via
|
||||
/// <see cref="NumberSequence"/> (docType "LV"). Model: docs/12-BACKEND-HRM.md Part C.5.
|
||||
/// </summary>
|
||||
public class LeaveRequest
|
||||
{
|
||||
public int LeaveRequestId { get; set; }
|
||||
public string DocNo { get; set; } = string.Empty;
|
||||
public int EmployeeId { get; set; }
|
||||
public Employee? Employee { get; set; }
|
||||
public int LeaveTypeId { get; set; }
|
||||
public LeaveType? LeaveType { get; set; }
|
||||
|
||||
public DateTime StartDate { get; set; }
|
||||
public DateTime EndDate { get; set; }
|
||||
public decimal DaysCount { get; set; }
|
||||
public string? Reason { get; set; }
|
||||
|
||||
public LeaveRequestStatus Status { get; set; } = LeaveRequestStatus.Draft;
|
||||
public int? ApprovedBy { get; set; }
|
||||
public DateTime? ApprovedAt { get; set; }
|
||||
public string? RejectionReason { get; set; }
|
||||
|
||||
public int CreatedBy { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public uint RowVersion { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using ERPCore.Domain.Enums;
|
||||
|
||||
namespace ERPCore.Domain.Entities;
|
||||
|
||||
/// <summary>Leave type master (FR-HR-LV-01). Model: docs/12-BACKEND-HRM.md Part C.5.</summary>
|
||||
public class LeaveType
|
||||
{
|
||||
public int LeaveTypeId { get; set; }
|
||||
public string Code { get; set; } = string.Empty;
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public bool IsPaid { get; set; } = true;
|
||||
/// <summary>Feeds Payroll's No-Pay deduction when true (docs/12-BACKEND-HRM.md §6).</summary>
|
||||
public bool CountsAsNoPay { get; set; }
|
||||
public decimal AccrualPerYear { get; set; }
|
||||
public bool CarryForwardAllowed { get; set; }
|
||||
public int? MaxCarryForwardDays { get; set; }
|
||||
public bool RequiresApproval { get; set; } = true;
|
||||
public EntityStatus Status { get; set; } = EntityStatus.Active;
|
||||
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
public uint RowVersion { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using ERPCore.Domain.Enums;
|
||||
|
||||
namespace ERPCore.Domain.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Loan installment ledger row. <see cref="PayrollRunId"/> is stamped only when the
|
||||
/// consuming <see cref="PayrollRun"/> reaches Locked (docs/12-BACKEND-HRM.md A.4).
|
||||
/// Model: docs/12-BACKEND-HRM.md Part C.6.
|
||||
/// </summary>
|
||||
public class LoanInstallment
|
||||
{
|
||||
public int LoanInstallmentId { get; set; }
|
||||
public int EmployeeLoanId { get; set; }
|
||||
public EmployeeLoan? EmployeeLoan { get; set; }
|
||||
|
||||
public int InstallmentNumber { get; set; }
|
||||
public int DueYear { get; set; }
|
||||
public int DueMonth { get; set; }
|
||||
public decimal ScheduledAmount { get; set; }
|
||||
public decimal? PaidAmount { get; set; }
|
||||
public int? PayrollRunId { get; set; }
|
||||
public PayrollRun? PayrollRun { get; set; }
|
||||
public LoanInstallmentStatus Status { get; set; } = LoanInstallmentStatus.Pending;
|
||||
|
||||
public uint RowVersion { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
namespace ERPCore.Domain.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Per-employee payroll summary row (FR-HR-PAY-05). EpfEmployerAmount/EtfEmployerAmount
|
||||
/// are informational/liability only, never subtracted from NetSalary
|
||||
/// (docs/12-BACKEND-HRM.md B.4). Model: docs/12-BACKEND-HRM.md Part C.6.
|
||||
/// </summary>
|
||||
public class PayrollLine
|
||||
{
|
||||
public int PayrollLineId { get; set; }
|
||||
public int PayrollRunId { get; set; }
|
||||
public PayrollRun? PayrollRun { get; set; }
|
||||
public int EmployeeId { get; set; }
|
||||
public Employee? Employee { get; set; }
|
||||
|
||||
public decimal BasicSalary { get; set; }
|
||||
public decimal TotalAllowances { get; set; }
|
||||
public decimal OvertimeAmount { get; set; }
|
||||
public decimal GrossSalary { get; set; }
|
||||
|
||||
public decimal LateDeductionAmount { get; set; }
|
||||
public decimal NoPayAmount { get; set; }
|
||||
public decimal LoanDeductionAmount { get; set; }
|
||||
public decimal EpfEmployeeAmount { get; set; }
|
||||
public decimal EpfEmployerAmount { get; set; }
|
||||
public decimal EtfEmployerAmount { get; set; }
|
||||
public decimal TaxAmount { get; set; }
|
||||
public decimal OtherDeductionsAmount { get; set; }
|
||||
public decimal NetSalary { get; set; }
|
||||
|
||||
public int WorkingDays { get; set; }
|
||||
public int PresentDays { get; set; }
|
||||
public int AbsentDays { get; set; }
|
||||
public int LeaveDays { get; set; }
|
||||
public int OtMinutesTotal { get; set; }
|
||||
public int LateMinutesTotal { get; set; }
|
||||
|
||||
public uint RowVersion { get; set; }
|
||||
|
||||
public List<PayrollLineComponent> Components { get; set; } = new();
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using ERPCore.Domain.Enums;
|
||||
|
||||
namespace ERPCore.Domain.Entities;
|
||||
|
||||
/// <summary>The detailed Basic/Transport/Meal/OT/Late/No-Pay/Loan/EPF/ETF/Tax breakdown. Model: docs/12-BACKEND-HRM.md Part C.6.</summary>
|
||||
public class PayrollLineComponent
|
||||
{
|
||||
public int PayrollLineComponentId { get; set; }
|
||||
public int PayrollLineId { get; set; }
|
||||
public PayrollLine? PayrollLine { get; set; }
|
||||
public PayrollLineComponentCategory ComponentCategory { get; set; }
|
||||
/// <summary>Set only for structure-sourced Allowance/OtherDeduction lines; null for system-computed lines.</summary>
|
||||
public int? SalaryComponentId { get; set; }
|
||||
public SalaryComponent? SalaryComponent { get; set; }
|
||||
public string Label { get; set; } = string.Empty;
|
||||
public decimal Amount { get; set; }
|
||||
public int SortOrder { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using ERPCore.Domain.Enums;
|
||||
|
||||
namespace ERPCore.Domain.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Payroll run (FR-HR-PAY-05/06) — the transactional document. Numbered via
|
||||
/// <see cref="NumberSequence"/> (docType "PAY"). Model: docs/12-BACKEND-HRM.md Part C.6.
|
||||
/// </summary>
|
||||
public class PayrollRun
|
||||
{
|
||||
public int PayrollRunId { get; set; }
|
||||
public string DocNo { get; set; } = string.Empty;
|
||||
public int PeriodYear { get; set; }
|
||||
public int PeriodMonth { get; set; }
|
||||
/// <summary>Null = company-wide run.</summary>
|
||||
public int? BranchId { get; set; }
|
||||
public Branch? Branch { get; set; }
|
||||
public PayrollRunStatus Status { get; set; } = PayrollRunStatus.Draft;
|
||||
|
||||
public int GeneratedBy { get; set; }
|
||||
public DateTime GeneratedAt { get; set; }
|
||||
public int? ApprovedBy { get; set; }
|
||||
public DateTime? ApprovedAt { get; set; }
|
||||
public int? LockedBy { get; set; }
|
||||
public DateTime? LockedAt { get; set; }
|
||||
public int? UnlockedBy { get; set; }
|
||||
public DateTime? UnlockedAt { get; set; }
|
||||
public string? UnlockReason { get; set; }
|
||||
|
||||
public uint RowVersion { get; set; }
|
||||
|
||||
public List<PayrollLine> Lines { get; set; } = new();
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
namespace ERPCore.Domain.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Effective-dated EPF/ETF rates (FR-HR-PAY-04) — Sri Lanka defaults (EPF 8%
|
||||
/// employee / 12% employer, ETF 3% employer-only), configurable since government
|
||||
/// rates can change. Model: docs/12-BACKEND-HRM.md Part C.6.
|
||||
/// </summary>
|
||||
public class PayrollStatutorySetting
|
||||
{
|
||||
public int PayrollStatutorySettingId { get; set; }
|
||||
public decimal EpfEmployeeRate { get; set; } = 0.08m;
|
||||
public decimal EpfEmployerRate { get; set; } = 0.12m;
|
||||
public decimal EtfEmployerRate { get; set; } = 0.03m;
|
||||
public decimal OtMultiplierDefault { get; set; } = 1.5m;
|
||||
public DateTime EffectiveFrom { get; set; }
|
||||
public DateTime? EffectiveTo { get; set; }
|
||||
|
||||
public int CreatedBy { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public uint RowVersion { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace ERPCore.Domain.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Thin generation/release marker over a <see cref="PayrollLine"/> — ships as an
|
||||
/// HTML print view in this phase, per the confirmed decision (no PDF dependency).
|
||||
/// Model: docs/12-BACKEND-HRM.md Part C.6.
|
||||
/// </summary>
|
||||
public class Payslip
|
||||
{
|
||||
public int PayslipId { get; set; }
|
||||
public int PayrollLineId { get; set; }
|
||||
public PayrollLine? PayrollLine { get; set; }
|
||||
public DateTime GeneratedAt { get; set; }
|
||||
public DateTime? ReleasedAt { get; set; }
|
||||
public int? ReleasedBy { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using ERPCore.Domain.Enums;
|
||||
|
||||
namespace ERPCore.Domain.Entities;
|
||||
|
||||
/// <summary>Allowance/ad hoc deduction master (FR-HR-PAY-01). Model: docs/12-BACKEND-HRM.md Part C.6.</summary>
|
||||
public class SalaryComponent
|
||||
{
|
||||
public int SalaryComponentId { get; set; }
|
||||
public string Code { get; set; } = string.Empty;
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public SalaryComponentType ComponentType { get; set; }
|
||||
public bool IsTaxable { get; set; }
|
||||
public bool IsEpfEtfApplicable { get; set; }
|
||||
public EntityStatus Status { get; set; } = EntityStatus.Active;
|
||||
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
public uint RowVersion { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
namespace ERPCore.Domain.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Configurable APIT-style marginal tax slab (FR-HR-PAY-04) — government slabs
|
||||
/// change with the yearly budget, so this is never hardcoded. <see cref="UpperBound"/>
|
||||
/// null means "and above". Model: docs/12-BACKEND-HRM.md Part C.6.
|
||||
/// </summary>
|
||||
public class TaxSlab
|
||||
{
|
||||
public int TaxSlabId { get; set; }
|
||||
public DateTime EffectiveFrom { get; set; }
|
||||
public DateTime? EffectiveTo { get; set; }
|
||||
public decimal LowerBound { get; set; }
|
||||
public decimal? UpperBound { get; set; }
|
||||
public decimal Rate { get; set; }
|
||||
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public uint RowVersion { get; set; }
|
||||
}
|
||||
@@ -23,6 +23,14 @@ public class User
|
||||
/// <summary>AuthHex identity (token <c>UserId</c> GUID); null for the seeded system user.</summary>
|
||||
public Guid? AuthUserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Mirrors the AuthHex identity's email (backfilled at create time by
|
||||
/// <c>UsersController.Create</c>, best-effort by JIT provisioning otherwise).
|
||||
/// Used only for the Employee<->User cross-link soft match (docs/12-BACKEND-HRM.md
|
||||
/// A.5) — never for authentication, which stays AuthHex's responsibility.
|
||||
/// </summary>
|
||||
public string? Email { get; set; }
|
||||
|
||||
/// <summary>Local shadow <see cref="Role"/> assignment; null until an admin assigns one.</summary>
|
||||
public int? RoleId { get; set; }
|
||||
public Role? Role { get; set; }
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
using ERPCore.Domain.Enums;
|
||||
|
||||
namespace ERPCore.Domain.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Attendance baseline (FR-HR-MD-01) — the shift definition Late/Early/OT figures
|
||||
/// are computed against (docs/12-BACKEND-HRM.md A.3, C.1). <see cref="IsOvernight"/>
|
||||
/// is explicit rather than inferred from End<Start, since that comparison alone
|
||||
/// is ambiguous for a shift that starts and ends the same clock time next day.
|
||||
/// </summary>
|
||||
public class WorkShift
|
||||
{
|
||||
public int WorkShiftId { get; set; }
|
||||
public string Code { get; set; } = string.Empty;
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public TimeSpan StartTime { get; set; }
|
||||
public TimeSpan EndTime { get; set; }
|
||||
public bool IsOvernight { get; set; }
|
||||
public int GraceMinutes { get; set; } = 15;
|
||||
public int BreakMinutes { get; set; } = 60;
|
||||
public int StandardWorkingMinutes { get; set; } = 480;
|
||||
public decimal OtMultiplier { get; set; } = 1.5m;
|
||||
|
||||
/// <summary>Bitmask, bit 0 = Monday .. bit 6 = Sunday.</summary>
|
||||
public int WorkingDaysMask { get; set; } = 0b0111111;
|
||||
|
||||
public EntityStatus Status { get; set; } = EntityStatus.Active;
|
||||
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
public uint RowVersion { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace ERPCore.Domain.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// Attendance upload batch lifecycle (FR-HR-ATT, docs/12-BACKEND-HRM.md C.4) —
|
||||
/// exactly the flow specified by the business: once <see cref="Confirmed"/> it
|
||||
/// becomes payroll's source of truth; once <see cref="UsedInPayroll"/> it is
|
||||
/// immutable even to Unlock (a payroll run must be unlocked/regenerated first).
|
||||
/// </summary>
|
||||
public enum AttendanceBatchStatus
|
||||
{
|
||||
Draft,
|
||||
Validated,
|
||||
Confirmed,
|
||||
UsedInPayroll
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace ERPCore.Domain.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// Origin of an attendance batch (docs/12-BACKEND-HRM.md C.4). <see cref="BiometricDevice"/>
|
||||
/// is a reserved future integration seam (docs §B.7) — no device feed exists yet.
|
||||
/// </summary>
|
||||
public enum AttendanceSourceType
|
||||
{
|
||||
Excel,
|
||||
Csv,
|
||||
Manual,
|
||||
BiometricDevice
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace ERPCore.Domain.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// Per-day attendance classification (docs/12-BACKEND-HRM.md C.4). Lateness/OT are
|
||||
/// derived facts (LateMinutes/OvertimeMinutes > 0) on an otherwise Present record,
|
||||
/// not separate statuses. <see cref="OnLeave"/> is derived from an overlapping
|
||||
/// Approved LeaveRequest with no uploaded punch (§6).
|
||||
/// </summary>
|
||||
public enum AttendanceStatus
|
||||
{
|
||||
Present,
|
||||
Absent,
|
||||
HalfDay,
|
||||
OnLeave,
|
||||
Holiday,
|
||||
WeekOff
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace ERPCore.Domain.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// Uploaded staff document status (docs/12-BACKEND-HRM.md C.3). Never hard-deleted —
|
||||
/// archived instead, mirroring the deactivate-not-delete master convention (FR-MD-08).
|
||||
/// </summary>
|
||||
public enum EmployeeDocumentStatus
|
||||
{
|
||||
Active,
|
||||
Archived
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace ERPCore.Domain.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// Employee lifecycle status (docs/12-BACKEND-HRM.md C.2). An Employee is never
|
||||
/// hard-deleted; separation is recorded here instead (with <c>LastWorkingDate</c>
|
||||
/// set), matching the deactivate-not-delete convention for masters (FR-MD-08)
|
||||
/// taken one step further since the record must be retained for audit/payroll history.
|
||||
/// </summary>
|
||||
public enum EmployeeStatus
|
||||
{
|
||||
Active,
|
||||
Suspended,
|
||||
Resigned,
|
||||
Terminated,
|
||||
Retired
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace ERPCore.Domain.Enums;
|
||||
|
||||
/// <summary>Employee gender (docs/12-BACKEND-HRM.md C.2). Optional field, stored as a string.</summary>
|
||||
public enum Gender
|
||||
{
|
||||
Male,
|
||||
Female,
|
||||
Other
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace ERPCore.Domain.Enums;
|
||||
|
||||
/// <summary>Staff document catalog category (docs/12-BACKEND-HRM.md C.3). Stored as a string.</summary>
|
||||
public enum HrDocumentCategory
|
||||
{
|
||||
Identity,
|
||||
Educational,
|
||||
Contract,
|
||||
Certification,
|
||||
Statutory,
|
||||
Other
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace ERPCore.Domain.Enums;
|
||||
|
||||
/// <summary>Leave request approval lifecycle (FR-HR-LV-02, docs/12-BACKEND-HRM.md C.5).</summary>
|
||||
public enum LeaveRequestStatus
|
||||
{
|
||||
Draft,
|
||||
Submitted,
|
||||
Approved,
|
||||
Rejected,
|
||||
Cancelled
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace ERPCore.Domain.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// An installment flips Pending→Deducted only when its PayrollRun reaches Locked
|
||||
/// (docs/12-BACKEND-HRM.md A.4) — never at Generate/Draft, so a discarded/regenerated
|
||||
/// draft never prematurely consumes it.
|
||||
/// </summary>
|
||||
public enum LoanInstallmentStatus
|
||||
{
|
||||
Pending,
|
||||
Deducted,
|
||||
Skipped
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace ERPCore.Domain.Enums;
|
||||
|
||||
/// <summary>Loan vs Advance discriminator (docs/12-BACKEND-HRM.md C.6) — structurally identical, differ only in intent/labeling.</summary>
|
||||
public enum LoanKind
|
||||
{
|
||||
Loan,
|
||||
Advance
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace ERPCore.Domain.Enums;
|
||||
|
||||
public enum LoanStatus
|
||||
{
|
||||
Active,
|
||||
Closed,
|
||||
Cancelled
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace ERPCore.Domain.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// EmployerContribution lines (EPF-employer, ETF) are informational/liability only —
|
||||
/// never subtracted from Net Salary (docs/12-BACKEND-HRM.md B.4).
|
||||
/// </summary>
|
||||
public enum PayrollLineComponentCategory
|
||||
{
|
||||
Earning,
|
||||
Deduction,
|
||||
EmployerContribution
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace ERPCore.Domain.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// PayrollRun approval workflow (FR-HR-PAY-06, docs/12-BACKEND-HRM.md C.6). Maps the
|
||||
/// business's 5-step flow to 3 stored states: Generate→Draft, Review is a human
|
||||
/// action (not stored), Approve→Approved, Lock→Locked (the point loan installments
|
||||
/// and attendance batches are stamped consumed — see A.4), Generate Payslips is an
|
||||
/// action gated on Locked, not a state.
|
||||
/// </summary>
|
||||
public enum PayrollRunStatus
|
||||
{
|
||||
Draft,
|
||||
Approved,
|
||||
Locked
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace ERPCore.Domain.Enums;
|
||||
|
||||
/// <summary>Per-record outcome of the attendance upload validation pipeline (docs/12-BACKEND-HRM.md §B.3.4).</summary>
|
||||
public enum RowValidationStatus
|
||||
{
|
||||
Valid,
|
||||
DuplicateWithinBatch,
|
||||
DuplicateConfirmed,
|
||||
EmployeeNotFound,
|
||||
InvalidDateTime,
|
||||
Error
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace ERPCore.Domain.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// SalaryComponent master category (docs/12-BACKEND-HRM.md C.6) — for Allowances and
|
||||
/// ad hoc Other Deductions only. OT/Late/No-Pay/Loan/EPF/ETF/Tax are system-computed,
|
||||
/// not user-defined components, to avoid a generic formula engine nobody asked for.
|
||||
/// </summary>
|
||||
public enum SalaryComponentType
|
||||
{
|
||||
Earning,
|
||||
Deduction
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace ERPCore.Domain.Enums;
|
||||
|
||||
/// <summary>Effective-dated salary structure status (docs/12-BACKEND-HRM.md C.6) — exactly one Active (open-ended) row per employee at a time.</summary>
|
||||
public enum SalaryStructureStatus
|
||||
{
|
||||
Active,
|
||||
Superseded
|
||||
}
|
||||
Reference in New Issue
Block a user