develop full initial module
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using ERPCore.Domain.Enums;
|
||||
|
||||
namespace ERPCore.Dtos.Hrm;
|
||||
|
||||
public sealed record AttendanceUploadBatchDto(
|
||||
int AttendanceUploadBatchId, string DocNo, DateTime PeriodStart, DateTime PeriodEnd,
|
||||
AttendanceSourceType SourceType, string? OriginalFileName, int UploadedBy, DateTime UploadedAt,
|
||||
AttendanceBatchStatus Status, int? ConfirmedBy, DateTime? ConfirmedAt,
|
||||
int RowCountTotal, int RowCountDuplicate, int RowCountError);
|
||||
|
||||
public sealed class UploadAttendanceBatchMetadata
|
||||
{
|
||||
[Required] public DateTime PeriodStart { get; set; }
|
||||
[Required] public DateTime PeriodEnd { get; set; }
|
||||
}
|
||||
|
||||
public sealed record AttendanceRecordDto(
|
||||
int AttendanceRecordId, int? AttendanceUploadBatchId, int EmployeeId, string? EmployeeCode, string? EmployeeName,
|
||||
DateTime AttendanceDate, TimeSpan? CheckIn, TimeSpan? CheckOut,
|
||||
int WorkingMinutes, int LateMinutes, int EarlyLeaveMinutes, int OvertimeMinutes,
|
||||
AttendanceStatus AttendanceStatus, RowValidationStatus RowValidationStatus,
|
||||
int? DuplicateOfAttendanceRecordId, string? Notes);
|
||||
|
||||
public sealed class UpdateAttendanceRecordRequest
|
||||
{
|
||||
public TimeSpan? CheckIn { get; set; }
|
||||
public TimeSpan? CheckOut { get; set; }
|
||||
public AttendanceStatus? AttendanceStatus { get; set; }
|
||||
[StringLength(1000)] public string? Notes { get; set; }
|
||||
}
|
||||
|
||||
public sealed class ResolveDuplicateRequest
|
||||
{
|
||||
[Required] public int RecordId { get; set; }
|
||||
/// <summary>"keep" discards the other duplicate row(s); "discard" removes this row; "supersede" (cross-batch-confirmed only) replaces the prior confirmed record.</summary>
|
||||
[Required, RegularExpression("^(keep|discard|supersede)$")]
|
||||
public string Action { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public sealed class UnlockAttendanceBatchRequest
|
||||
{
|
||||
[Required, StringLength(500)] public string Reason { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using ERPCore.Domain.Enums;
|
||||
|
||||
namespace ERPCore.Dtos.Hrm;
|
||||
|
||||
public sealed record HrDocumentTypeDto(
|
||||
int HrDocumentTypeId, string Code, string Name, HrDocumentCategory Category,
|
||||
bool RequiredAtOnboarding, bool ExpiryTracked, EntityStatus Status,
|
||||
DateTime CreatedAt, DateTime? UpdatedAt);
|
||||
|
||||
public sealed class CreateHrDocumentTypeRequest
|
||||
{
|
||||
[Required, StringLength(20)] public string Code { get; set; } = string.Empty;
|
||||
[Required, StringLength(200)] public string Name { get; set; } = string.Empty;
|
||||
[Required, EnumDataType(typeof(HrDocumentCategory))] public HrDocumentCategory Category { get; set; }
|
||||
public bool RequiredAtOnboarding { get; set; }
|
||||
public bool ExpiryTracked { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UpdateHrDocumentTypeRequest
|
||||
{
|
||||
[Required, StringLength(200)] public string Name { get; set; } = string.Empty;
|
||||
[Required, EnumDataType(typeof(HrDocumentCategory))] public HrDocumentCategory Category { get; set; }
|
||||
public bool RequiredAtOnboarding { get; set; }
|
||||
public bool ExpiryTracked { get; set; }
|
||||
}
|
||||
|
||||
public sealed record EmployeeDocumentDto(
|
||||
int EmployeeDocumentId, int EmployeeId, int HrDocumentTypeId, string? HrDocumentTypeName,
|
||||
string OriginalFileName, string ContentType, long SizeBytes,
|
||||
DateTime? IssueDate, DateTime? ExpiryDate, string? Notes,
|
||||
int UploadedBy, DateTime UploadedAt, EmployeeDocumentStatus Status);
|
||||
|
||||
/// <summary>Metadata accompanying a multipart file upload (the file itself is bound separately).</summary>
|
||||
public sealed class UploadEmployeeDocumentRequest
|
||||
{
|
||||
[Required] public int HrDocumentTypeId { get; set; }
|
||||
public DateTime? IssueDate { get; set; }
|
||||
public DateTime? ExpiryDate { get; set; }
|
||||
[StringLength(1000)] public string? Notes { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UpdateEmployeeDocumentStatusRequest
|
||||
{
|
||||
[Required, EnumDataType(typeof(EmployeeDocumentStatus))] public EmployeeDocumentStatus Status { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using ERPCore.Domain.Enums;
|
||||
|
||||
namespace ERPCore.Dtos.Hrm;
|
||||
|
||||
public sealed record EmployeeListItemDto(
|
||||
int EmployeeId, string EmployeeCode, string FullName, string? Email,
|
||||
int DepartmentId, string? DepartmentName, int DesignationId, string? DesignationName,
|
||||
int EmploymentTypeId, string? EmploymentTypeName, int? BranchId, string? BranchName,
|
||||
EmployeeStatus Status, bool HasUserLink, DateTime HireDate);
|
||||
|
||||
public sealed record EmployeeDetailDto(
|
||||
int EmployeeId, string EmployeeCode, string FullName, string? Nic, DateTime? DateOfBirth,
|
||||
Gender? Gender, string? Nationality, string? ProfilePhotoPath,
|
||||
string? Email, string? PersonalMobile, string? AddressLine1, string? AddressLine2,
|
||||
string? City, string? PostalCode, string? Country,
|
||||
string? EmergencyContactName, string? EmergencyContactRelationship, string? EmergencyContactPhone,
|
||||
DateTime HireDate, DateTime? ConfirmationDate, DateTime? LastWorkingDate,
|
||||
int DepartmentId, int DesignationId, int EmploymentTypeId, int? BranchId, int WorkShiftId,
|
||||
int? ReportingManagerId, string? EpfNumber, string? EtfNumber, string? TaxIdentificationNumber,
|
||||
int? UserId, EmployeeStatus Status, DateTime CreatedAt, DateTime? UpdatedAt);
|
||||
|
||||
public sealed class CreateEmployeeRequest
|
||||
{
|
||||
[Required, StringLength(30)] public string EmployeeCode { get; set; } = string.Empty;
|
||||
[Required, StringLength(200)] public string FullName { get; set; } = string.Empty;
|
||||
[StringLength(30)] public string? Nic { get; set; }
|
||||
public DateTime? DateOfBirth { get; set; }
|
||||
public Gender? Gender { get; set; }
|
||||
[StringLength(100)] public string? Nationality { get; set; }
|
||||
|
||||
[EmailAddress, StringLength(320)] public string? Email { get; set; }
|
||||
[StringLength(30)] public string? PersonalMobile { get; set; }
|
||||
[StringLength(200)] public string? AddressLine1 { get; set; }
|
||||
[StringLength(200)] public string? AddressLine2 { get; set; }
|
||||
[StringLength(100)] public string? City { get; set; }
|
||||
[StringLength(20)] public string? PostalCode { get; set; }
|
||||
[StringLength(100)] public string? Country { get; set; }
|
||||
|
||||
[StringLength(200)] public string? EmergencyContactName { get; set; }
|
||||
[StringLength(100)] public string? EmergencyContactRelationship { get; set; }
|
||||
[StringLength(30)] public string? EmergencyContactPhone { get; set; }
|
||||
|
||||
[Required] public DateTime HireDate { get; set; }
|
||||
[Required] public int DepartmentId { get; set; }
|
||||
[Required] public int DesignationId { get; set; }
|
||||
[Required] public int EmploymentTypeId { get; set; }
|
||||
public int? BranchId { get; set; }
|
||||
[Required] public int WorkShiftId { get; set; }
|
||||
public int? ReportingManagerId { get; set; }
|
||||
|
||||
[StringLength(30)] public string? EpfNumber { get; set; }
|
||||
[StringLength(30)] public string? EtfNumber { get; set; }
|
||||
[StringLength(30)] public string? TaxIdentificationNumber { get; set; }
|
||||
|
||||
/// <summary>Explicit, human-confirmed link to an existing User found via email-lookup — never automatic.</summary>
|
||||
public int? LinkUserId { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UpdateEmployeeRequest
|
||||
{
|
||||
[Required, StringLength(200)] public string FullName { get; set; } = string.Empty;
|
||||
[StringLength(30)] public string? Nic { get; set; }
|
||||
public DateTime? DateOfBirth { get; set; }
|
||||
public Gender? Gender { get; set; }
|
||||
[StringLength(100)] public string? Nationality { get; set; }
|
||||
|
||||
[EmailAddress, StringLength(320)] public string? Email { get; set; }
|
||||
[StringLength(30)] public string? PersonalMobile { get; set; }
|
||||
[StringLength(200)] public string? AddressLine1 { get; set; }
|
||||
[StringLength(200)] public string? AddressLine2 { get; set; }
|
||||
[StringLength(100)] public string? City { get; set; }
|
||||
[StringLength(20)] public string? PostalCode { get; set; }
|
||||
[StringLength(100)] public string? Country { get; set; }
|
||||
|
||||
[StringLength(200)] public string? EmergencyContactName { get; set; }
|
||||
[StringLength(100)] public string? EmergencyContactRelationship { get; set; }
|
||||
[StringLength(30)] public string? EmergencyContactPhone { get; set; }
|
||||
|
||||
public DateTime? ConfirmationDate { get; set; }
|
||||
public DateTime? LastWorkingDate { get; set; }
|
||||
[Required] public int DepartmentId { get; set; }
|
||||
[Required] public int DesignationId { get; set; }
|
||||
[Required] public int EmploymentTypeId { get; set; }
|
||||
public int? BranchId { get; set; }
|
||||
[Required] public int WorkShiftId { get; set; }
|
||||
public int? ReportingManagerId { get; set; }
|
||||
|
||||
[StringLength(30)] public string? EpfNumber { get; set; }
|
||||
[StringLength(30)] public string? EtfNumber { get; set; }
|
||||
[StringLength(30)] public string? TaxIdentificationNumber { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UpdateEmployeeStatusRequest
|
||||
{
|
||||
[Required, EnumDataType(typeof(EmployeeStatus))] public EmployeeStatus Status { get; set; }
|
||||
}
|
||||
|
||||
public sealed record EmployeeBankDetailDto(
|
||||
int EmployeeBankDetailId, string BankName, string BranchName, string AccountNumber,
|
||||
string AccountHolderName, string? SwiftCode, bool IsPrimary, EntityStatus Status);
|
||||
|
||||
public sealed class UpsertEmployeeBankDetailRequest
|
||||
{
|
||||
public int? EmployeeBankDetailId { get; set; }
|
||||
[Required, StringLength(200)] public string BankName { get; set; } = string.Empty;
|
||||
[Required, StringLength(200)] public string BranchName { get; set; } = string.Empty;
|
||||
[Required, StringLength(50)] public string AccountNumber { get; set; } = string.Empty;
|
||||
[Required, StringLength(200)] public string AccountHolderName { get; set; } = string.Empty;
|
||||
[StringLength(20)] public string? SwiftCode { get; set; }
|
||||
public bool IsPrimary { get; set; }
|
||||
}
|
||||
|
||||
public sealed class ReplaceEmployeeBankDetailsRequest
|
||||
{
|
||||
[Required] public List<UpsertEmployeeBankDetailRequest> Items { get; set; } = new();
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace ERPCore.Dtos.Hrm;
|
||||
|
||||
/// <summary>Advisory match surfaced by the reverse-direction email-lookup (docs/12-BACKEND-HRM.md A.5).</summary>
|
||||
public sealed record EmployeeMatchDto(int EmployeeId, string EmployeeCode, string FullName, string Email);
|
||||
|
||||
public sealed record UserMatchDto(int UserId, string Username, string DisplayName, string Email);
|
||||
|
||||
public sealed record EmployeeMatchResponse(EmployeeMatchDto? Match);
|
||||
|
||||
public sealed record UserMatchResponse(UserMatchDto? Match);
|
||||
|
||||
public sealed class LinkUserRequest
|
||||
{
|
||||
[Required] public int UserId { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using ERPCore.Domain.Enums;
|
||||
|
||||
namespace ERPCore.Dtos.Hrm;
|
||||
|
||||
public sealed record LeaveTypeDto(
|
||||
int LeaveTypeId, string Code, string Name, bool IsPaid, bool CountsAsNoPay, decimal AccrualPerYear,
|
||||
bool CarryForwardAllowed, int? MaxCarryForwardDays, bool RequiresApproval, EntityStatus Status,
|
||||
DateTime CreatedAt, DateTime? UpdatedAt);
|
||||
|
||||
public sealed class CreateLeaveTypeRequest
|
||||
{
|
||||
[Required, StringLength(20)] public string Code { get; set; } = string.Empty;
|
||||
[Required, StringLength(200)] public string Name { get; set; } = string.Empty;
|
||||
public bool IsPaid { get; set; } = true;
|
||||
public bool CountsAsNoPay { get; set; }
|
||||
[Range(0, 365)] public decimal AccrualPerYear { get; set; }
|
||||
public bool CarryForwardAllowed { get; set; }
|
||||
public int? MaxCarryForwardDays { get; set; }
|
||||
public bool RequiresApproval { get; set; } = true;
|
||||
}
|
||||
|
||||
public sealed class UpdateLeaveTypeRequest
|
||||
{
|
||||
[Required, StringLength(200)] public string Name { get; set; } = string.Empty;
|
||||
public bool IsPaid { get; set; }
|
||||
public bool CountsAsNoPay { get; set; }
|
||||
[Range(0, 365)] public decimal AccrualPerYear { get; set; }
|
||||
public bool CarryForwardAllowed { get; set; }
|
||||
public int? MaxCarryForwardDays { get; set; }
|
||||
public bool RequiresApproval { get; set; }
|
||||
}
|
||||
|
||||
public sealed record LeaveRequestDto(
|
||||
int LeaveRequestId, string DocNo, int EmployeeId, string? EmployeeName, int LeaveTypeId, string? LeaveTypeName,
|
||||
DateTime StartDate, DateTime EndDate, decimal DaysCount, string? Reason,
|
||||
LeaveRequestStatus Status, int? ApprovedBy, DateTime? ApprovedAt, string? RejectionReason, DateTime CreatedAt);
|
||||
|
||||
public sealed class CreateLeaveRequestRequest
|
||||
{
|
||||
[Required] public int EmployeeId { get; set; }
|
||||
[Required] public int LeaveTypeId { get; set; }
|
||||
[Required] public DateTime StartDate { get; set; }
|
||||
[Required] public DateTime EndDate { get; set; }
|
||||
[StringLength(1000)] public string? Reason { get; set; }
|
||||
}
|
||||
|
||||
public sealed class RejectLeaveRequestRequest
|
||||
{
|
||||
[Required, StringLength(1000)] public string Reason { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public sealed record LeaveBalanceDto(
|
||||
int LeaveBalanceId, int EmployeeId, int LeaveTypeId, string? LeaveTypeName, int Year,
|
||||
decimal EntitledDays, decimal TakenDays, decimal CarriedForwardDays, decimal AdjustmentDays, decimal RemainingDays);
|
||||
|
||||
public sealed class LeaveBalanceAdjustmentItem
|
||||
{
|
||||
[Required] public int LeaveTypeId { get; set; }
|
||||
public decimal AdjustmentDays { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UpdateLeaveBalancesRequest
|
||||
{
|
||||
[Required] public int Year { get; set; }
|
||||
[Required] public List<LeaveBalanceAdjustmentItem> Items { get; set; } = new();
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using ERPCore.Domain.Enums;
|
||||
|
||||
namespace ERPCore.Dtos.Hrm;
|
||||
|
||||
// Narrow DTOs for the five HRM org masters — server-controlled fields (status,
|
||||
// ids, timestamps) excluded from create/update requests (02-SECURITY B.6/C.1).
|
||||
|
||||
public sealed record BranchDto(
|
||||
int BranchId, string Code, string Name, string? Address, EntityStatus Status,
|
||||
DateTime CreatedAt, DateTime? UpdatedAt);
|
||||
|
||||
public sealed class CreateBranchRequest
|
||||
{
|
||||
[Required, StringLength(20)] public string Code { get; set; } = string.Empty;
|
||||
[Required, StringLength(200)] public string Name { get; set; } = string.Empty;
|
||||
[StringLength(500)] public string? Address { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UpdateBranchRequest
|
||||
{
|
||||
[Required, StringLength(200)] public string Name { get; set; } = string.Empty;
|
||||
[StringLength(500)] public string? Address { get; set; }
|
||||
}
|
||||
|
||||
public sealed record DepartmentDto(
|
||||
int DepartmentId, string Code, string Name, int? ParentDepartmentId, int? HeadEmployeeId,
|
||||
int? BranchId, EntityStatus Status, DateTime CreatedAt, DateTime? UpdatedAt);
|
||||
|
||||
public sealed class CreateDepartmentRequest
|
||||
{
|
||||
[Required, StringLength(20)] public string Code { get; set; } = string.Empty;
|
||||
[Required, StringLength(200)] public string Name { get; set; } = string.Empty;
|
||||
public int? ParentDepartmentId { get; set; }
|
||||
public int? HeadEmployeeId { get; set; }
|
||||
public int? BranchId { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UpdateDepartmentRequest
|
||||
{
|
||||
[Required, StringLength(200)] public string Name { get; set; } = string.Empty;
|
||||
public int? ParentDepartmentId { get; set; }
|
||||
public int? HeadEmployeeId { get; set; }
|
||||
public int? BranchId { get; set; }
|
||||
}
|
||||
|
||||
public sealed record DesignationDto(
|
||||
int DesignationId, string Code, string Name, EntityStatus Status, DateTime CreatedAt, DateTime? UpdatedAt);
|
||||
|
||||
public sealed class CreateDesignationRequest
|
||||
{
|
||||
[Required, StringLength(20)] public string Code { get; set; } = string.Empty;
|
||||
[Required, StringLength(200)] public string Name { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public sealed class UpdateDesignationRequest
|
||||
{
|
||||
[Required, StringLength(200)] public string Name { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public sealed record EmploymentTypeDto(
|
||||
int EmploymentTypeId, string Code, string Name, EntityStatus Status, DateTime CreatedAt, DateTime? UpdatedAt);
|
||||
|
||||
public sealed class CreateEmploymentTypeRequest
|
||||
{
|
||||
[Required, StringLength(20)] public string Code { get; set; } = string.Empty;
|
||||
[Required, StringLength(200)] public string Name { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public sealed class UpdateEmploymentTypeRequest
|
||||
{
|
||||
[Required, StringLength(200)] public string Name { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public sealed record WorkShiftDto(
|
||||
int WorkShiftId, string Code, string Name, TimeSpan StartTime, TimeSpan EndTime, bool IsOvernight,
|
||||
int GraceMinutes, int BreakMinutes, int StandardWorkingMinutes, decimal OtMultiplier, int WorkingDaysMask,
|
||||
EntityStatus Status, DateTime CreatedAt, DateTime? UpdatedAt);
|
||||
|
||||
public sealed class CreateWorkShiftRequest
|
||||
{
|
||||
[Required, StringLength(20)] public string Code { get; set; } = string.Empty;
|
||||
[Required, StringLength(200)] public string Name { get; set; } = string.Empty;
|
||||
[Required] public TimeSpan StartTime { get; set; }
|
||||
[Required] public TimeSpan EndTime { get; set; }
|
||||
public bool IsOvernight { get; set; }
|
||||
[Range(0, 240)] public int GraceMinutes { get; set; } = 15;
|
||||
[Range(0, 240)] public int BreakMinutes { get; set; } = 60;
|
||||
[Range(1, 1440)] public int StandardWorkingMinutes { get; set; } = 480;
|
||||
[Range(1, 5)] public decimal OtMultiplier { get; set; } = 1.5m;
|
||||
[Range(0, 127)] public int WorkingDaysMask { get; set; } = 0b0111111;
|
||||
}
|
||||
|
||||
public sealed class UpdateWorkShiftRequest
|
||||
{
|
||||
[Required, StringLength(200)] public string Name { get; set; } = string.Empty;
|
||||
[Required] public TimeSpan StartTime { get; set; }
|
||||
[Required] public TimeSpan EndTime { get; set; }
|
||||
public bool IsOvernight { get; set; }
|
||||
[Range(0, 240)] public int GraceMinutes { get; set; }
|
||||
[Range(0, 240)] public int BreakMinutes { get; set; }
|
||||
[Range(1, 1440)] public int StandardWorkingMinutes { get; set; }
|
||||
[Range(1, 5)] public decimal OtMultiplier { get; set; }
|
||||
[Range(0, 127)] public int WorkingDaysMask { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>Shared by all five masters' PATCH .../status endpoints.</summary>
|
||||
public sealed class UpdateHrMasterStatusRequest
|
||||
{
|
||||
[Required, EnumDataType(typeof(EntityStatus))] public EntityStatus Status { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using ERPCore.Domain.Enums;
|
||||
|
||||
namespace ERPCore.Dtos.Hrm;
|
||||
|
||||
// --- Salary components ---
|
||||
|
||||
public sealed record SalaryComponentDto(
|
||||
int SalaryComponentId, string Code, string Name, SalaryComponentType ComponentType,
|
||||
bool IsTaxable, bool IsEpfEtfApplicable, EntityStatus Status, DateTime CreatedAt, DateTime? UpdatedAt);
|
||||
|
||||
public sealed class CreateSalaryComponentRequest
|
||||
{
|
||||
[Required, StringLength(20)] public string Code { get; set; } = string.Empty;
|
||||
[Required, StringLength(200)] public string Name { get; set; } = string.Empty;
|
||||
[Required, EnumDataType(typeof(SalaryComponentType))] public SalaryComponentType ComponentType { get; set; }
|
||||
public bool IsTaxable { get; set; }
|
||||
public bool IsEpfEtfApplicable { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UpdateSalaryComponentRequest
|
||||
{
|
||||
[Required, StringLength(200)] public string Name { get; set; } = string.Empty;
|
||||
public bool IsTaxable { get; set; }
|
||||
public bool IsEpfEtfApplicable { get; set; }
|
||||
}
|
||||
|
||||
// --- Salary structure ---
|
||||
|
||||
public sealed record EmployeeSalaryStructureLineDto(int SalaryComponentId, string? SalaryComponentName, decimal Amount);
|
||||
|
||||
public sealed record EmployeeSalaryStructureDto(
|
||||
int EmployeeSalaryStructureId, int EmployeeId, DateTime EffectiveFrom, DateTime? EffectiveTo,
|
||||
decimal BasicSalary, string Currency, SalaryStructureStatus Status,
|
||||
List<EmployeeSalaryStructureLineDto> Lines, DateTime CreatedAt);
|
||||
|
||||
public sealed class SalaryStructureLineRequest
|
||||
{
|
||||
[Required] public int SalaryComponentId { get; set; }
|
||||
[Range(0, double.MaxValue)] public decimal Amount { get; set; }
|
||||
}
|
||||
|
||||
public sealed class CreateSalaryStructureRequest
|
||||
{
|
||||
[Required] public DateTime EffectiveFrom { get; set; }
|
||||
[Range(0, double.MaxValue)] public decimal BasicSalary { get; set; }
|
||||
public List<SalaryStructureLineRequest> Lines { get; set; } = new();
|
||||
}
|
||||
|
||||
// --- Loans ---
|
||||
|
||||
public sealed record LoanInstallmentDto(
|
||||
int LoanInstallmentId, int InstallmentNumber, int DueYear, int DueMonth,
|
||||
decimal ScheduledAmount, decimal? PaidAmount, int? PayrollRunId, LoanInstallmentStatus Status);
|
||||
|
||||
public sealed record EmployeeLoanDto(
|
||||
int EmployeeLoanId, string DocNo, int EmployeeId, LoanKind LoanKind, decimal PrincipalAmount,
|
||||
decimal InterestRate, decimal InstallmentAmount, int NumberOfInstallments, int StartYear, int StartMonth,
|
||||
decimal OutstandingBalance, LoanStatus Status, List<LoanInstallmentDto> Installments, DateTime CreatedAt);
|
||||
|
||||
public sealed class CreateEmployeeLoanRequest
|
||||
{
|
||||
[Required, EnumDataType(typeof(LoanKind))] public LoanKind LoanKind { get; set; }
|
||||
[Range(0.01, double.MaxValue)] public decimal PrincipalAmount { get; set; }
|
||||
[Range(0, 1)] public decimal InterestRate { get; set; }
|
||||
[Range(0.01, double.MaxValue)] public decimal InstallmentAmount { get; set; }
|
||||
[Range(1, 360)] public int NumberOfInstallments { get; set; }
|
||||
[Range(2000, 2100)] public int StartYear { get; set; }
|
||||
[Range(1, 12)] public int StartMonth { get; set; }
|
||||
}
|
||||
|
||||
// --- Statutory settings ---
|
||||
|
||||
public sealed record PayrollStatutorySettingDto(
|
||||
int PayrollStatutorySettingId, decimal EpfEmployeeRate, decimal EpfEmployerRate, decimal EtfEmployerRate,
|
||||
decimal OtMultiplierDefault, DateTime EffectiveFrom, DateTime? EffectiveTo);
|
||||
|
||||
public sealed class UpsertPayrollStatutorySettingRequest
|
||||
{
|
||||
[Range(0, 1)] public decimal EpfEmployeeRate { get; set; } = 0.08m;
|
||||
[Range(0, 1)] public decimal EpfEmployerRate { get; set; } = 0.12m;
|
||||
[Range(0, 1)] public decimal EtfEmployerRate { get; set; } = 0.03m;
|
||||
[Range(1, 5)] public decimal OtMultiplierDefault { get; set; } = 1.5m;
|
||||
[Required] public DateTime EffectiveFrom { get; set; }
|
||||
}
|
||||
|
||||
public sealed record TaxSlabDto(int TaxSlabId, DateTime EffectiveFrom, DateTime? EffectiveTo, decimal LowerBound, decimal? UpperBound, decimal Rate);
|
||||
|
||||
public sealed class CreateTaxSlabRequest
|
||||
{
|
||||
[Required] public DateTime EffectiveFrom { get; set; }
|
||||
[Range(0, double.MaxValue)] public decimal LowerBound { get; set; }
|
||||
public decimal? UpperBound { get; set; }
|
||||
[Range(0, 1)] public decimal Rate { get; set; }
|
||||
}
|
||||
|
||||
// --- Payroll run ---
|
||||
|
||||
public sealed record PayrollLineComponentDto(
|
||||
PayrollLineComponentCategory ComponentCategory, int? SalaryComponentId, string Label, decimal Amount, int SortOrder);
|
||||
|
||||
public sealed record PayrollLineDto(
|
||||
int PayrollLineId, int PayrollRunId, int EmployeeId, string? EmployeeCode, string? EmployeeName,
|
||||
decimal BasicSalary, decimal TotalAllowances, decimal OvertimeAmount, decimal GrossSalary,
|
||||
decimal LateDeductionAmount, decimal NoPayAmount, decimal LoanDeductionAmount,
|
||||
decimal EpfEmployeeAmount, decimal EpfEmployerAmount, decimal EtfEmployerAmount,
|
||||
decimal TaxAmount, decimal OtherDeductionsAmount, decimal NetSalary,
|
||||
int WorkingDays, int PresentDays, int AbsentDays, int LeaveDays, int OtMinutesTotal, int LateMinutesTotal);
|
||||
|
||||
public sealed record PayrollLineDetailDto(PayrollLineDto Line, List<PayrollLineComponentDto> Components);
|
||||
|
||||
public sealed record PayrollRunDto(
|
||||
int PayrollRunId, string DocNo, int PeriodYear, int PeriodMonth, int? BranchId, PayrollRunStatus Status,
|
||||
int GeneratedBy, DateTime GeneratedAt, int? ApprovedBy, DateTime? ApprovedAt,
|
||||
int? LockedBy, DateTime? LockedAt, int? UnlockedBy, DateTime? UnlockedAt, string? UnlockReason,
|
||||
decimal TotalGross, decimal TotalNet, int EmployeeCount);
|
||||
|
||||
public sealed class GeneratePayrollRunRequest
|
||||
{
|
||||
[Range(2000, 2100)] public int PeriodYear { get; set; }
|
||||
[Range(1, 12)] public int PeriodMonth { get; set; }
|
||||
public int? BranchId { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UnlockPayrollRunRequest
|
||||
{
|
||||
[Required, StringLength(500)] public string Reason { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public sealed record PayslipDto(int PayslipId, int PayrollLineId, DateTime GeneratedAt, DateTime? ReleasedAt, int? ReleasedBy);
|
||||
@@ -0,0 +1,26 @@
|
||||
namespace ERPCore.Dtos.Hrm;
|
||||
|
||||
public sealed record AttendanceSummaryRowDto(
|
||||
int EmployeeId, string EmployeeCode, string EmployeeName, string? DepartmentName,
|
||||
int PresentDays, int AbsentDays, int LeaveDays, int HalfDays, int OtMinutesTotal, int LateMinutesTotal);
|
||||
|
||||
public sealed record OvertimeReportRowDto(
|
||||
int EmployeeId, string EmployeeCode, string EmployeeName, DateTime AttendanceDate, int OvertimeMinutes);
|
||||
|
||||
public sealed record LateArrivalReportRowDto(
|
||||
int EmployeeId, string EmployeeCode, string EmployeeName, DateTime AttendanceDate, int LateMinutes);
|
||||
|
||||
public sealed record PayrollRegisterRowDto(
|
||||
int PayrollLineId, int EmployeeId, string EmployeeCode, string EmployeeName,
|
||||
decimal GrossSalary, decimal TotalDeductions, decimal NetSalary);
|
||||
|
||||
public sealed record SalaryHistoryRowDto(
|
||||
int EmployeeSalaryStructureId, DateTime EffectiveFrom, DateTime? EffectiveTo, decimal BasicSalary, string Status);
|
||||
|
||||
public sealed record LeaveBalanceReportRowDto(
|
||||
int EmployeeId, string EmployeeCode, string EmployeeName, string LeaveTypeName,
|
||||
decimal EntitledDays, decimal TakenDays, decimal RemainingDays);
|
||||
|
||||
public sealed record DocumentExpiryReportRowDto(
|
||||
int EmployeeDocumentId, int EmployeeId, string EmployeeCode, string EmployeeName,
|
||||
string DocumentTypeName, DateTime ExpiryDate, int DaysUntilExpiry);
|
||||
@@ -4,7 +4,7 @@ using ERPCore.Domain.Enums;
|
||||
namespace ERPCore.Dtos.Users;
|
||||
|
||||
public sealed record ManagedUserDto(
|
||||
int UserId, string Username, string DisplayName, EntityStatus Status,
|
||||
int UserId, string Username, string DisplayName, string? Email, EntityStatus Status,
|
||||
int? RoleId, string? RoleCode, string? RoleName);
|
||||
|
||||
/// <summary>
|
||||
@@ -24,6 +24,12 @@ public sealed class CreateUserRequest
|
||||
public string? MobileNumber { get; set; }
|
||||
/// <summary>Left empty to auto-generate (AuthHex emails it to <see cref="Email"/>).</summary>
|
||||
public string? Password { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Explicit, human-confirmed link to an existing unlinked Employee found via
|
||||
/// email-lookup — never automatic, even on an exact email match (docs/12-BACKEND-HRM.md A.5).
|
||||
/// </summary>
|
||||
public int? LinkEmployeeId { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UpdateUserRoleRequest
|
||||
|
||||
Reference in New Issue
Block a user