develop full initial module
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
// Attendance upload/validate/confirm pipeline (docs/13-BACKEND-HRM-API.md §4).
|
||||
import { apiRequest, buildQuery } from "@/lib/api-client"
|
||||
import { PagedResponse } from "@/types/common"
|
||||
import { AttendanceBatchStatus, AttendanceRecord, AttendanceStatusValue, AttendanceUploadBatch, RowValidationStatus } from "@/types/hrm"
|
||||
|
||||
export interface ListAttendanceBatchesParams {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
status?: AttendanceBatchStatus
|
||||
periodYear?: number
|
||||
periodMonth?: number
|
||||
}
|
||||
|
||||
/** Direct-link download — the browser navigates/streams the binary response itself. */
|
||||
export function attendanceTemplateUrl(format?: "csv"): string {
|
||||
return `/api/v1/attendance-batches/template.xlsx${format ? "?format=csv" : ""}`
|
||||
}
|
||||
|
||||
export const attendanceApi = {
|
||||
list(params: ListAttendanceBatchesParams = {}): Promise<PagedResponse<AttendanceUploadBatch>> {
|
||||
return apiRequest<PagedResponse<AttendanceUploadBatch>>(`/attendance-batches${buildQuery(params)}`)
|
||||
},
|
||||
get(batchId: number): Promise<AttendanceUploadBatch> {
|
||||
return apiRequest<AttendanceUploadBatch>(`/attendance-batches/${batchId}`)
|
||||
},
|
||||
upload(file: File, periodStart: string, periodEnd: string): Promise<AttendanceUploadBatch> {
|
||||
const form = new FormData()
|
||||
form.append("file", file)
|
||||
form.append("PeriodStart", periodStart)
|
||||
form.append("PeriodEnd", periodEnd)
|
||||
return apiRequest<AttendanceUploadBatch>("/attendance-batches", { method: "POST", body: form })
|
||||
},
|
||||
|
||||
listRecords(batchId: number, status?: RowValidationStatus): Promise<AttendanceRecord[]> {
|
||||
return apiRequest<AttendanceRecord[]>(`/attendance-batches/${batchId}/records${buildQuery({ status })}`)
|
||||
},
|
||||
updateRecord(
|
||||
batchId: number,
|
||||
recordId: number,
|
||||
request: { checkIn?: string | null; checkOut?: string | null; attendanceStatus?: AttendanceStatusValue | null; notes?: string | null }
|
||||
): Promise<AttendanceRecord> {
|
||||
return apiRequest<AttendanceRecord>(`/attendance-batches/${batchId}/records/${recordId}`, { method: "PUT", body: request })
|
||||
},
|
||||
resolveDuplicate(batchId: number, recordId: number, action: "keep" | "discard" | "supersede"): Promise<void> {
|
||||
return apiRequest<void>(`/attendance-batches/${batchId}/resolve-duplicate`, { method: "POST", body: { recordId, action } })
|
||||
},
|
||||
|
||||
validate(batchId: number): Promise<AttendanceUploadBatch> {
|
||||
return apiRequest<AttendanceUploadBatch>(`/attendance-batches/${batchId}/validate`, { method: "POST" })
|
||||
},
|
||||
confirm(batchId: number): Promise<AttendanceUploadBatch> {
|
||||
return apiRequest<AttendanceUploadBatch>(`/attendance-batches/${batchId}/confirm`, { method: "POST" })
|
||||
},
|
||||
unlock(batchId: number, reason: string): Promise<AttendanceUploadBatch> {
|
||||
return apiRequest<AttendanceUploadBatch>(`/attendance-batches/${batchId}/unlock`, { method: "POST", body: { reason } })
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
// Employee (staff) endpoints, incl. the Employee<->User cross-link, bank details,
|
||||
// documents, salary structure, loans, and leave balances (docs/13-BACKEND-HRM-API.md §3).
|
||||
import { apiRequest, apiRequestWithETag, buildQuery } from "@/lib/api-client"
|
||||
import { ApiResult, PagedResponse } from "@/types/common"
|
||||
import {
|
||||
CreateEmployeeRequest,
|
||||
EmployeeBankDetail,
|
||||
EmployeeDetail,
|
||||
EmployeeDocument,
|
||||
EmployeeListItem,
|
||||
EmployeeLoan,
|
||||
EmployeeMatch,
|
||||
EmployeeSalaryStructure,
|
||||
EmployeeStatus,
|
||||
LeaveBalance,
|
||||
UpdateEmployeeRequest,
|
||||
UserMatch,
|
||||
} from "@/types/hrm"
|
||||
|
||||
export interface ListEmployeesParams {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
q?: string
|
||||
status?: EmployeeStatus
|
||||
departmentId?: number
|
||||
designationId?: number
|
||||
branchId?: number
|
||||
}
|
||||
|
||||
export interface CreateSalaryStructureRequest {
|
||||
effectiveFrom: string
|
||||
basicSalary: number
|
||||
lines: { salaryComponentId: number; amount: number }[]
|
||||
}
|
||||
|
||||
export interface CreateEmployeeLoanRequest {
|
||||
loanKind: "Loan" | "Advance"
|
||||
principalAmount: number
|
||||
interestRate: number
|
||||
installmentAmount: number
|
||||
numberOfInstallments: number
|
||||
startYear: number
|
||||
startMonth: number
|
||||
}
|
||||
|
||||
export const employeesApi = {
|
||||
list(params: ListEmployeesParams = {}): Promise<PagedResponse<EmployeeListItem>> {
|
||||
return apiRequest<PagedResponse<EmployeeListItem>>(`/employees${buildQuery(params)}`)
|
||||
},
|
||||
get(employeeId: number): Promise<ApiResult<EmployeeDetail>> {
|
||||
return apiRequestWithETag<EmployeeDetail>(`/employees/${employeeId}`)
|
||||
},
|
||||
create(request: CreateEmployeeRequest): Promise<ApiResult<EmployeeDetail>> {
|
||||
return apiRequestWithETag<EmployeeDetail>("/employees", { method: "POST", body: request })
|
||||
},
|
||||
update(employeeId: number, request: UpdateEmployeeRequest, ifMatch: string): Promise<ApiResult<EmployeeDetail>> {
|
||||
return apiRequestWithETag<EmployeeDetail>(`/employees/${employeeId}`, { method: "PUT", body: request, ifMatch })
|
||||
},
|
||||
updateStatus(employeeId: number, status: EmployeeStatus): Promise<void> {
|
||||
return apiRequest<void>(`/employees/${employeeId}/status`, { method: "PATCH", body: { status } })
|
||||
},
|
||||
|
||||
/** Advisory: does a System User already exist with this email? */
|
||||
emailLookup(email: string): Promise<{ match: UserMatch | null }> {
|
||||
return apiRequest<{ match: UserMatch | null }>(`/employees/email-lookup${buildQuery({ email })}`)
|
||||
},
|
||||
linkUser(employeeId: number, userId: number): Promise<void> {
|
||||
return apiRequest<void>(`/employees/${employeeId}/link-user`, { method: "POST", body: { userId } })
|
||||
},
|
||||
unlinkUser(employeeId: number): Promise<void> {
|
||||
return apiRequest<void>(`/employees/${employeeId}/link-user`, { method: "DELETE" })
|
||||
},
|
||||
|
||||
listBankDetails(employeeId: number): Promise<EmployeeBankDetail[]> {
|
||||
return apiRequest<EmployeeBankDetail[]>(`/employees/${employeeId}/bank-details`)
|
||||
},
|
||||
replaceBankDetails(employeeId: number, items: EmployeeBankDetail[]): Promise<EmployeeBankDetail[]> {
|
||||
return apiRequest<EmployeeBankDetail[]>(`/employees/${employeeId}/bank-details`, { method: "PUT", body: { items } })
|
||||
},
|
||||
|
||||
listDocuments(employeeId: number): Promise<EmployeeDocument[]> {
|
||||
return apiRequest<EmployeeDocument[]>(`/employees/${employeeId}/documents`)
|
||||
},
|
||||
async uploadDocument(
|
||||
employeeId: number,
|
||||
file: File,
|
||||
meta: { hrDocumentTypeId: number; issueDate?: string | null; expiryDate?: string | null; notes?: string | null }
|
||||
): Promise<EmployeeDocument> {
|
||||
const form = new FormData()
|
||||
form.append("file", file)
|
||||
form.append("HrDocumentTypeId", String(meta.hrDocumentTypeId))
|
||||
if (meta.issueDate) form.append("IssueDate", meta.issueDate)
|
||||
if (meta.expiryDate) form.append("ExpiryDate", meta.expiryDate)
|
||||
if (meta.notes) form.append("Notes", meta.notes)
|
||||
return apiRequest<EmployeeDocument>(`/employees/${employeeId}/documents`, { method: "POST", body: form })
|
||||
},
|
||||
documentDownloadUrl(employeeId: number, documentId: number): string {
|
||||
return `/api/v1/employees/${employeeId}/documents/${documentId}/download`
|
||||
},
|
||||
setDocumentStatus(employeeId: number, documentId: number, status: "Active" | "Archived"): Promise<void> {
|
||||
return apiRequest<void>(`/employees/${employeeId}/documents/${documentId}/status`, { method: "PATCH", body: { status } })
|
||||
},
|
||||
|
||||
salaryStructureHistory(employeeId: number): Promise<EmployeeSalaryStructure[]> {
|
||||
return apiRequest<EmployeeSalaryStructure[]>(`/employees/${employeeId}/salary-structure`)
|
||||
},
|
||||
createSalaryStructure(employeeId: number, request: CreateSalaryStructureRequest): Promise<EmployeeSalaryStructure> {
|
||||
return apiRequest<EmployeeSalaryStructure>(`/employees/${employeeId}/salary-structure`, { method: "POST", body: request })
|
||||
},
|
||||
|
||||
listLoans(employeeId: number): Promise<EmployeeLoan[]> {
|
||||
return apiRequest<EmployeeLoan[]>(`/employees/${employeeId}/loans`)
|
||||
},
|
||||
createLoan(employeeId: number, request: CreateEmployeeLoanRequest): Promise<EmployeeLoan> {
|
||||
return apiRequest<EmployeeLoan>(`/employees/${employeeId}/loans`, { method: "POST", body: request })
|
||||
},
|
||||
|
||||
listLeaveBalances(employeeId: number, year?: number): Promise<LeaveBalance[]> {
|
||||
return apiRequest<LeaveBalance[]>(`/employees/${employeeId}/leave-balances${buildQuery({ year })}`)
|
||||
},
|
||||
}
|
||||
|
||||
// Re-exported for the Employee create form's cross-link chip (mirrors employeesApi.emailLookup in reverse).
|
||||
export const employeeCrossLinkApi = {
|
||||
findStaffByEmail(email: string): Promise<{ match: EmployeeMatch | null }> {
|
||||
return apiRequest<{ match: EmployeeMatch | null }>(`/users/email-lookup${buildQuery({ email })}`)
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// Read-only HRM reports (docs/13-BACKEND-HRM-API.md §6, FR-HR-RPT).
|
||||
import { apiRequest, buildQuery } from "@/lib/api-client"
|
||||
import {
|
||||
AttendanceSummaryRow,
|
||||
DocumentExpiryReportRow,
|
||||
LateArrivalReportRow,
|
||||
LeaveBalanceReportRow,
|
||||
OvertimeReportRow,
|
||||
PayrollRegisterRow,
|
||||
SalaryHistoryRow,
|
||||
} from "@/types/hrm"
|
||||
|
||||
export const hrReportsApi = {
|
||||
attendanceSummary(periodYear: number, periodMonth: number, departmentId?: number): Promise<AttendanceSummaryRow[]> {
|
||||
return apiRequest<AttendanceSummaryRow[]>(`/reports/hrm/attendance-summary${buildQuery({ periodYear, periodMonth, departmentId })}`)
|
||||
},
|
||||
overtime(periodYear: number, periodMonth: number): Promise<OvertimeReportRow[]> {
|
||||
return apiRequest<OvertimeReportRow[]>(`/reports/hrm/overtime${buildQuery({ periodYear, periodMonth })}`)
|
||||
},
|
||||
lateArrivals(periodYear: number, periodMonth: number): Promise<LateArrivalReportRow[]> {
|
||||
return apiRequest<LateArrivalReportRow[]>(`/reports/hrm/late-arrivals${buildQuery({ periodYear, periodMonth })}`)
|
||||
},
|
||||
payrollRegister(payrollRunId: number): Promise<PayrollRegisterRow[]> {
|
||||
return apiRequest<PayrollRegisterRow[]>(`/reports/hrm/payroll-register${buildQuery({ payrollRunId })}`)
|
||||
},
|
||||
salaryHistory(employeeId: number): Promise<SalaryHistoryRow[]> {
|
||||
return apiRequest<SalaryHistoryRow[]>(`/reports/hrm/salary-history${buildQuery({ employeeId })}`)
|
||||
},
|
||||
leaveBalances(year: number): Promise<LeaveBalanceReportRow[]> {
|
||||
return apiRequest<LeaveBalanceReportRow[]>(`/reports/hrm/leave-balances${buildQuery({ year })}`)
|
||||
},
|
||||
documentExpiry(withinDays: number): Promise<DocumentExpiryReportRow[]> {
|
||||
return apiRequest<DocumentExpiryReportRow[]>(`/reports/hrm/document-expiry${buildQuery({ withinDays })}`)
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// Shared CRUD shape for the ~8 near-identical HRM masters (Branch, Department,
|
||||
// Designation, EmploymentType, WorkShift, HrDocumentType, LeaveType, SalaryComponent) —
|
||||
// same ETag/status/list pattern as brandsApi, factored out once instead of copy-pasted 8x.
|
||||
import { apiRequest, apiRequestWithETag, buildQuery } from "@/lib/api-client"
|
||||
import { ApiResult, EntityStatus, PagedResponse } from "@/types/common"
|
||||
|
||||
export interface ListMasterParams {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
q?: string
|
||||
status?: EntityStatus
|
||||
}
|
||||
|
||||
export function createMasterApi<T, TCreate, TUpdate>(resource: string) {
|
||||
return {
|
||||
list(params: ListMasterParams = {}): Promise<PagedResponse<T>> {
|
||||
return apiRequest<PagedResponse<T>>(`/${resource}${buildQuery(params)}`)
|
||||
},
|
||||
get(id: number): Promise<ApiResult<T>> {
|
||||
return apiRequestWithETag<T>(`/${resource}/${id}`)
|
||||
},
|
||||
create(request: TCreate): Promise<ApiResult<T>> {
|
||||
return apiRequestWithETag<T>(`/${resource}`, { method: "POST", body: request })
|
||||
},
|
||||
update(id: number, request: TUpdate, ifMatch: string): Promise<ApiResult<T>> {
|
||||
return apiRequestWithETag<T>(`/${resource}/${id}`, { method: "PUT", body: request, ifMatch })
|
||||
},
|
||||
updateStatus(id: number, status: EntityStatus): Promise<void> {
|
||||
return apiRequest<void>(`/${resource}/${id}/status`, { method: "PATCH", body: { status } })
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
// HRM org/reference masters (docs/13-BACKEND-HRM-API.md §2, §5, §6). Each is the
|
||||
// same list/get/create/update/status-toggle shape as brandsApi (see hrm-master-factory).
|
||||
import { createMasterApi } from "@/lib/api/hrm-master-factory"
|
||||
import {
|
||||
Branch,
|
||||
Department,
|
||||
Designation,
|
||||
EmploymentType,
|
||||
HrDocumentType,
|
||||
LeaveType,
|
||||
SalaryComponent,
|
||||
WorkShift,
|
||||
} from "@/types/hrm"
|
||||
|
||||
export interface CreateBranchRequest { code: string; name: string; address?: string | null }
|
||||
export type UpdateBranchRequest = Omit<CreateBranchRequest, "code">
|
||||
export const branchesHrmApi = createMasterApi<Branch, CreateBranchRequest, UpdateBranchRequest>("branches")
|
||||
|
||||
export interface CreateDepartmentRequest {
|
||||
code: string
|
||||
name: string
|
||||
parentDepartmentId?: number | null
|
||||
headEmployeeId?: number | null
|
||||
branchId?: number | null
|
||||
}
|
||||
export type UpdateDepartmentRequest = Omit<CreateDepartmentRequest, "code">
|
||||
export const departmentsApi = createMasterApi<Department, CreateDepartmentRequest, UpdateDepartmentRequest>("departments")
|
||||
|
||||
export interface CreateDesignationRequest { code: string; name: string }
|
||||
export type UpdateDesignationRequest = Omit<CreateDesignationRequest, "code">
|
||||
export const designationsApi = createMasterApi<Designation, CreateDesignationRequest, UpdateDesignationRequest>("designations")
|
||||
|
||||
export interface CreateEmploymentTypeRequest { code: string; name: string }
|
||||
export type UpdateEmploymentTypeRequest = Omit<CreateEmploymentTypeRequest, "code">
|
||||
export const employmentTypesApi = createMasterApi<EmploymentType, CreateEmploymentTypeRequest, UpdateEmploymentTypeRequest>("employment-types")
|
||||
|
||||
export interface CreateWorkShiftRequest {
|
||||
code: string
|
||||
name: string
|
||||
startTime: string
|
||||
endTime: string
|
||||
isOvernight: boolean
|
||||
graceMinutes: number
|
||||
breakMinutes: number
|
||||
standardWorkingMinutes: number
|
||||
otMultiplier: number
|
||||
workingDaysMask: number
|
||||
}
|
||||
export type UpdateWorkShiftRequest = Omit<CreateWorkShiftRequest, "code">
|
||||
export const workShiftsApi = createMasterApi<WorkShift, CreateWorkShiftRequest, UpdateWorkShiftRequest>("work-shifts")
|
||||
|
||||
export interface CreateHrDocumentTypeRequest {
|
||||
code: string
|
||||
name: string
|
||||
category: HrDocumentType["category"]
|
||||
requiredAtOnboarding: boolean
|
||||
expiryTracked: boolean
|
||||
}
|
||||
export type UpdateHrDocumentTypeRequest = Omit<CreateHrDocumentTypeRequest, "code">
|
||||
export const hrDocumentTypesApi = createMasterApi<HrDocumentType, CreateHrDocumentTypeRequest, UpdateHrDocumentTypeRequest>("hr-document-types")
|
||||
|
||||
export interface CreateLeaveTypeRequest {
|
||||
code: string
|
||||
name: string
|
||||
isPaid: boolean
|
||||
countsAsNoPay: boolean
|
||||
accrualPerYear: number
|
||||
carryForwardAllowed: boolean
|
||||
maxCarryForwardDays?: number | null
|
||||
requiresApproval: boolean
|
||||
}
|
||||
export type UpdateLeaveTypeRequest = Omit<CreateLeaveTypeRequest, "code">
|
||||
export const leaveTypesApi = createMasterApi<LeaveType, CreateLeaveTypeRequest, UpdateLeaveTypeRequest>("leave-types")
|
||||
|
||||
export interface CreateSalaryComponentRequest {
|
||||
code: string
|
||||
name: string
|
||||
componentType: SalaryComponent["componentType"]
|
||||
isTaxable: boolean
|
||||
isEpfEtfApplicable: boolean
|
||||
}
|
||||
export type UpdateSalaryComponentRequest = Omit<CreateSalaryComponentRequest, "code">
|
||||
export const salaryComponentsApi = createMasterApi<SalaryComponent, CreateSalaryComponentRequest, UpdateSalaryComponentRequest>("salary-components")
|
||||
@@ -0,0 +1,43 @@
|
||||
// Leave requests (docs/13-BACKEND-HRM-API.md §5).
|
||||
import { apiRequest, buildQuery } from "@/lib/api-client"
|
||||
import { PagedResponse } from "@/types/common"
|
||||
import { LeaveRequest, LeaveRequestStatus } from "@/types/hrm"
|
||||
|
||||
export interface ListLeaveRequestsParams {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
employeeId?: number
|
||||
status?: LeaveRequestStatus
|
||||
}
|
||||
|
||||
export interface CreateLeaveRequestRequest {
|
||||
employeeId: number
|
||||
leaveTypeId: number
|
||||
startDate: string
|
||||
endDate: string
|
||||
reason?: string | null
|
||||
}
|
||||
|
||||
export const leaveRequestsApi = {
|
||||
list(params: ListLeaveRequestsParams = {}): Promise<PagedResponse<LeaveRequest>> {
|
||||
return apiRequest<PagedResponse<LeaveRequest>>(`/leave-requests${buildQuery(params)}`)
|
||||
},
|
||||
get(leaveRequestId: number): Promise<LeaveRequest> {
|
||||
return apiRequest<LeaveRequest>(`/leave-requests/${leaveRequestId}`)
|
||||
},
|
||||
create(request: CreateLeaveRequestRequest): Promise<LeaveRequest> {
|
||||
return apiRequest<LeaveRequest>("/leave-requests", { method: "POST", body: request })
|
||||
},
|
||||
submit(leaveRequestId: number): Promise<LeaveRequest> {
|
||||
return apiRequest<LeaveRequest>(`/leave-requests/${leaveRequestId}/submit`, { method: "POST" })
|
||||
},
|
||||
approve(leaveRequestId: number): Promise<LeaveRequest> {
|
||||
return apiRequest<LeaveRequest>(`/leave-requests/${leaveRequestId}/approve`, { method: "POST" })
|
||||
},
|
||||
reject(leaveRequestId: number, reason: string): Promise<LeaveRequest> {
|
||||
return apiRequest<LeaveRequest>(`/leave-requests/${leaveRequestId}/reject`, { method: "POST", body: { reason } })
|
||||
},
|
||||
cancel(leaveRequestId: number): Promise<LeaveRequest> {
|
||||
return apiRequest<LeaveRequest>(`/leave-requests/${leaveRequestId}/cancel`, { method: "POST" })
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// Payroll runs, statutory settings, tax slabs, payslips (docs/13-BACKEND-HRM-API.md §6).
|
||||
import { apiRequest, buildQuery } from "@/lib/api-client"
|
||||
import { PagedResponse } from "@/types/common"
|
||||
import { Payslip, PayrollLine, PayrollLineDetail, PayrollRun, PayrollRunStatus, PayrollStatutorySetting, TaxSlab } from "@/types/hrm"
|
||||
|
||||
export interface ListPayrollRunsParams {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
periodYear?: number
|
||||
periodMonth?: number
|
||||
status?: PayrollRunStatus
|
||||
}
|
||||
|
||||
export interface GeneratePayrollRunRequest {
|
||||
periodYear: number
|
||||
periodMonth: number
|
||||
branchId?: number | null
|
||||
}
|
||||
|
||||
export const payrollRunsApi = {
|
||||
list(params: ListPayrollRunsParams = {}): Promise<PagedResponse<PayrollRun>> {
|
||||
return apiRequest<PagedResponse<PayrollRun>>(`/payroll-runs${buildQuery(params)}`)
|
||||
},
|
||||
get(payrollRunId: number): Promise<PayrollRun> {
|
||||
return apiRequest<PayrollRun>(`/payroll-runs/${payrollRunId}`)
|
||||
},
|
||||
listLines(payrollRunId: number): Promise<PayrollLine[]> {
|
||||
return apiRequest<PayrollLine[]>(`/payroll-runs/${payrollRunId}/lines`)
|
||||
},
|
||||
getLine(payrollRunId: number, lineId: number): Promise<PayrollLineDetail> {
|
||||
return apiRequest<PayrollLineDetail>(`/payroll-runs/${payrollRunId}/lines/${lineId}`)
|
||||
},
|
||||
generate(request: GeneratePayrollRunRequest): Promise<PayrollRun> {
|
||||
return apiRequest<PayrollRun>("/payroll-runs", { method: "POST", body: request })
|
||||
},
|
||||
approve(payrollRunId: number): Promise<PayrollRun> {
|
||||
return apiRequest<PayrollRun>(`/payroll-runs/${payrollRunId}/approve`, { method: "POST" })
|
||||
},
|
||||
lock(payrollRunId: number): Promise<PayrollRun> {
|
||||
return apiRequest<PayrollRun>(`/payroll-runs/${payrollRunId}/lock`, { method: "POST" })
|
||||
},
|
||||
unlock(payrollRunId: number, reason: string): Promise<PayrollRun> {
|
||||
return apiRequest<PayrollRun>(`/payroll-runs/${payrollRunId}/unlock`, { method: "POST", body: { reason } })
|
||||
},
|
||||
generatePayslips(payrollRunId: number): Promise<Payslip[]> {
|
||||
return apiRequest<Payslip[]>(`/payroll-runs/${payrollRunId}/generate-payslips`, { method: "POST" })
|
||||
},
|
||||
}
|
||||
|
||||
export const payrollStatutorySettingsApi = {
|
||||
list(): Promise<PayrollStatutorySetting[]> {
|
||||
return apiRequest<PayrollStatutorySetting[]>("/payroll-statutory-settings")
|
||||
},
|
||||
create(request: Omit<PayrollStatutorySetting, "payrollStatutorySettingId" | "effectiveTo">): Promise<PayrollStatutorySetting> {
|
||||
return apiRequest<PayrollStatutorySetting>("/payroll-statutory-settings", { method: "POST", body: request })
|
||||
},
|
||||
}
|
||||
|
||||
export const taxSlabsApi = {
|
||||
list(): Promise<TaxSlab[]> {
|
||||
return apiRequest<TaxSlab[]>("/tax-slabs")
|
||||
},
|
||||
create(request: Omit<TaxSlab, "taxSlabId" | "effectiveTo">): Promise<TaxSlab> {
|
||||
return apiRequest<TaxSlab>("/tax-slabs", { method: "POST", body: request })
|
||||
},
|
||||
}
|
||||
|
||||
export function payslipViewUrl(payslipId: number): string {
|
||||
return `/api/v1/payslips/${payslipId}/view`
|
||||
}
|
||||
Reference in New Issue
Block a user