feat: add vendor optional fields and configuration hooks
- Implemented VendorOptionalFields component to handle optional vendor details (mobile, email, address) with validation. - Created useCustomerConfig hook to load optional customer field settings from Sales configuration. - Developed useProcurementConfig hook to manage Procurement configuration flags. - Introduced accessConfigApi for managing access configuration settings. - Added openingBalancesApi for handling opening balances operations. - Created procurementConfigApi for managing procurement configuration settings. - Implemented nav-routes for permission-based routing. - Developed PDF generation for purchase orders with dynamic vendor and item details. - Added purchase-order tax helpers for tax calculations and formatting. - Defined types for opening balances and related operations.
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
using ERPCore.Dtos.Config;
|
||||
using ERPCore.Infra.Auth;
|
||||
using ERPCore.Services.Interfaces;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ERPCore.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Access configuration — the default role for people who have none. Both reading and changing it need the
|
||||
/// <c>settings</c> nav permission (it decides what a brand-new sign-in can do).
|
||||
/// </summary>
|
||||
[Route("api/v1/access-config")]
|
||||
[Authorize(Policy = NavPolicies.Prefix + "settings")]
|
||||
public sealed class AccessConfigController : ApiControllerBase
|
||||
{
|
||||
private readonly IAccessConfigService _config;
|
||||
|
||||
public AccessConfigController(IAccessConfigService config) => _config = config;
|
||||
|
||||
[HttpGet]
|
||||
[ProducesResponseType(typeof(AccessConfigDto), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<AccessConfigDto>> Get(CancellationToken ct)
|
||||
{
|
||||
var result = await _config.GetAsync(ct);
|
||||
SetETag(result.RowVersion);
|
||||
return Ok(result.Value);
|
||||
}
|
||||
|
||||
[HttpPut]
|
||||
[ProducesResponseType(typeof(AccessConfigDto), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status412PreconditionFailed)]
|
||||
public async Task<ActionResult<AccessConfigDto>> Update([FromBody] UpdateAccessConfigRequest request, CancellationToken ct)
|
||||
{
|
||||
var expected = RequireIfMatch();
|
||||
var result = await _config.UpdateAsync(request, expected, ct);
|
||||
SetETag(result.RowVersion);
|
||||
return Ok(result.Value);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
using ERPCore.Dtos.Audit;
|
||||
using ERPCore.Dtos.Common;
|
||||
using ERPCore.Infra.Auth;
|
||||
using ERPCore.Services.Interfaces;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ERPCore.Controllers;
|
||||
@@ -10,6 +12,7 @@ namespace ERPCore.Controllers;
|
||||
/// the audit trail is required (AR-01 compensating control) and read access is the
|
||||
/// only way to consume it.
|
||||
/// </summary>
|
||||
[Authorize(Policy = NavPolicies.Prefix + "settings.audit-logs")]
|
||||
[Route("api/v1/audit-logs")]
|
||||
public sealed class AuditLogsController : ApiControllerBase
|
||||
{
|
||||
|
||||
@@ -47,8 +47,7 @@ public sealed class AuthController : ControllerBase
|
||||
[ProducesResponseType(typeof(MeResponseDto), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<MeResponseDto>> Me(CancellationToken ct)
|
||||
{
|
||||
var roleCode = User.FindFirst(AuthHexClaims.RoleCode)?.Value;
|
||||
return Ok(await _roles.GetMeAsync(roleCode, ct));
|
||||
return Ok(await _roles.GetMeAsync(ct));
|
||||
}
|
||||
|
||||
// ---- Session-issuing (UserManager) ------------------------------------
|
||||
|
||||
@@ -25,8 +25,9 @@ public sealed class GrnsController : ApiControllerBase
|
||||
[ProducesResponseType(typeof(PagedResponse<GrnSummaryDto>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedResponse<GrnSummaryDto>>> List(
|
||||
[FromQuery] PageQuery query, [FromQuery] GrnStatus? status, [FromQuery] int? poId,
|
||||
[FromQuery] int? vendorId, [FromQuery] int? warehouseId, [FromQuery] int? branchId, CancellationToken ct)
|
||||
=> Ok(await _grns.ListAsync(query, status, poId, vendorId, warehouseId, branchId, ct));
|
||||
[FromQuery] int? vendorId, [FromQuery] int? warehouseId, [FromQuery] int? branchId,
|
||||
[FromQuery] DateOnly? from, [FromQuery] DateOnly? to, CancellationToken ct)
|
||||
=> Ok(await _grns.ListAsync(query, status, poId, vendorId, warehouseId, branchId, from, to, ct));
|
||||
|
||||
[HttpGet("{grnId:int}")]
|
||||
[ProducesResponseType(typeof(GrnDto), StatusCodes.Status200OK)]
|
||||
|
||||
@@ -3,11 +3,13 @@ using ERPCore.Dtos.Common;
|
||||
using ERPCore.Dtos.Hrm;
|
||||
using ERPCore.Infra.Auth;
|
||||
using ERPCore.Services.Interfaces;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ERPCore.Controllers.Hrm;
|
||||
|
||||
/// <summary>Attendance upload batch endpoints (docs/13-BACKEND-HRM-API.md §4).</summary>
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.attendance")]
|
||||
[Route("api/v1/attendance-batches")]
|
||||
public sealed class AttendanceBatchesController : ApiControllerBase
|
||||
{
|
||||
@@ -44,6 +46,7 @@ public sealed class AttendanceBatchesController : ApiControllerBase
|
||||
return result is null ? NotFound() : Ok(result);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.attendance" + NavPolicies.ActionSeparator + NavPolicies.CreateAction)]
|
||||
[HttpPost]
|
||||
[RequestSizeLimit(20 * 1024 * 1024)]
|
||||
[ProducesResponseType(typeof(AttendanceUploadBatchDto), StatusCodes.Status201Created)]
|
||||
@@ -62,6 +65,7 @@ public sealed class AttendanceBatchesController : ApiControllerBase
|
||||
int batchId, [FromQuery] RowValidationStatus? status, CancellationToken ct)
|
||||
=> Ok(await _attendance.ListRecordsAsync(batchId, status, ct));
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.attendance" + NavPolicies.ActionSeparator + NavPolicies.EditAction)]
|
||||
[HttpPut("{batchId:int}/records/{recordId:int}")]
|
||||
[ProducesResponseType(typeof(AttendanceRecordDto), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
@@ -70,6 +74,7 @@ public sealed class AttendanceBatchesController : ApiControllerBase
|
||||
int batchId, int recordId, [FromBody] UpdateAttendanceRecordRequest request, CancellationToken ct)
|
||||
=> Ok(await _attendance.UpdateRecordAsync(batchId, recordId, request, _currentUser.AuditUserId, ct));
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.attendance" + NavPolicies.ActionSeparator + NavPolicies.EditAction)]
|
||||
[HttpPost("{batchId:int}/resolve-duplicate")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status409Conflict)]
|
||||
@@ -79,18 +84,21 @@ public sealed class AttendanceBatchesController : ApiControllerBase
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.attendance" + NavPolicies.ActionSeparator + NavPolicies.EditAction)]
|
||||
[HttpPost("{batchId:int}/validate")]
|
||||
[ProducesResponseType(typeof(AttendanceUploadBatchDto), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status422UnprocessableEntity)]
|
||||
public async Task<ActionResult<AttendanceUploadBatchDto>> Validate(int batchId, CancellationToken ct)
|
||||
=> Ok(await _attendance.ValidateAsync(batchId, ct));
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.attendance" + NavPolicies.ActionSeparator + NavPolicies.ApproveAction)]
|
||||
[HttpPost("{batchId:int}/confirm")]
|
||||
[ProducesResponseType(typeof(AttendanceUploadBatchDto), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status409Conflict)]
|
||||
public async Task<ActionResult<AttendanceUploadBatchDto>> Confirm(int batchId, CancellationToken ct)
|
||||
=> Ok(await _attendance.ConfirmAsync(batchId, _currentUser.AuditUserId, ct));
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.attendance" + NavPolicies.ActionSeparator + NavPolicies.ApproveAction)]
|
||||
[HttpPost("{batchId:int}/unlock")]
|
||||
[ProducesResponseType(typeof(AttendanceUploadBatchDto), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status409Conflict)]
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using ERPCore.Domain.Enums;
|
||||
using ERPCore.Dtos.Common;
|
||||
using ERPCore.Dtos.Hrm;
|
||||
using ERPCore.Infra.Auth;
|
||||
using ERPCore.Services.Interfaces;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ERPCore.Controllers.Hrm;
|
||||
@@ -31,6 +33,7 @@ public sealed class BranchesController : ApiControllerBase
|
||||
return Ok(result.Value);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "settings.branches")]
|
||||
[HttpPost]
|
||||
[ProducesResponseType(typeof(BranchDto), StatusCodes.Status201Created)]
|
||||
[ProducesResponseType(StatusCodes.Status409Conflict)]
|
||||
@@ -41,6 +44,7 @@ public sealed class BranchesController : ApiControllerBase
|
||||
return Created($"/api/v1/branches/{result.Value.BranchId}", result.Value);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "settings.branches")]
|
||||
[HttpPut("{branchId:int}")]
|
||||
[ProducesResponseType(typeof(BranchDto), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
@@ -53,6 +57,7 @@ public sealed class BranchesController : ApiControllerBase
|
||||
return Ok(result.Value);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "settings.branches")]
|
||||
[HttpPatch("{branchId:int}/status")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
@@ -62,6 +67,7 @@ public sealed class BranchesController : ApiControllerBase
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "settings.branches")]
|
||||
[HttpDelete("{branchId:int}")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
using ERPCore.Domain.Enums;
|
||||
using ERPCore.Dtos.Common;
|
||||
using ERPCore.Dtos.Hrm;
|
||||
using ERPCore.Infra.Auth;
|
||||
using ERPCore.Services.Interfaces;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ERPCore.Controllers.Hrm;
|
||||
|
||||
/// <summary>Department master endpoints (docs/13-BACKEND-HRM-API.md §2).</summary>
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm")]
|
||||
[Route("api/v1/departments")]
|
||||
public sealed class DepartmentsController : ApiControllerBase
|
||||
{
|
||||
@@ -31,6 +34,7 @@ public sealed class DepartmentsController : ApiControllerBase
|
||||
return Ok(result.Value);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.settings")]
|
||||
[HttpPost]
|
||||
[ProducesResponseType(typeof(DepartmentDto), StatusCodes.Status201Created)]
|
||||
[ProducesResponseType(StatusCodes.Status409Conflict)]
|
||||
@@ -41,6 +45,7 @@ public sealed class DepartmentsController : ApiControllerBase
|
||||
return Created($"/api/v1/departments/{result.Value.DepartmentId}", result.Value);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.settings")]
|
||||
[HttpPut("{departmentId:int}")]
|
||||
[ProducesResponseType(typeof(DepartmentDto), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
@@ -54,6 +59,7 @@ public sealed class DepartmentsController : ApiControllerBase
|
||||
return Ok(result.Value);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.settings")]
|
||||
[HttpPatch("{departmentId:int}/status")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
@@ -63,6 +69,7 @@ public sealed class DepartmentsController : ApiControllerBase
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.settings")]
|
||||
[HttpDelete("{departmentId:int}")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
using ERPCore.Domain.Enums;
|
||||
using ERPCore.Dtos.Common;
|
||||
using ERPCore.Dtos.Hrm;
|
||||
using ERPCore.Infra.Auth;
|
||||
using ERPCore.Services.Interfaces;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ERPCore.Controllers.Hrm;
|
||||
|
||||
/// <summary>Designation (job title) master endpoints (docs/13-BACKEND-HRM-API.md §2).</summary>
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm")]
|
||||
[Route("api/v1/designations")]
|
||||
public sealed class DesignationsController : ApiControllerBase
|
||||
{
|
||||
@@ -31,6 +34,7 @@ public sealed class DesignationsController : ApiControllerBase
|
||||
return Ok(result.Value);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.settings")]
|
||||
[HttpPost]
|
||||
[ProducesResponseType(typeof(DesignationDto), StatusCodes.Status201Created)]
|
||||
[ProducesResponseType(StatusCodes.Status409Conflict)]
|
||||
@@ -41,6 +45,7 @@ public sealed class DesignationsController : ApiControllerBase
|
||||
return Created($"/api/v1/designations/{result.Value.DesignationId}", result.Value);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.settings")]
|
||||
[HttpPut("{designationId:int}")]
|
||||
[ProducesResponseType(typeof(DesignationDto), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
@@ -53,6 +58,7 @@ public sealed class DesignationsController : ApiControllerBase
|
||||
return Ok(result.Value);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.settings")]
|
||||
[HttpPatch("{designationId:int}/status")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
@@ -62,6 +68,7 @@ public sealed class DesignationsController : ApiControllerBase
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.settings")]
|
||||
[HttpDelete("{designationId:int}")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
|
||||
@@ -3,6 +3,7 @@ using ERPCore.Dtos.Common;
|
||||
using ERPCore.Dtos.Hrm;
|
||||
using ERPCore.Infra.Auth;
|
||||
using ERPCore.Services.Interfaces;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ERPCore.Controllers.Hrm;
|
||||
@@ -11,6 +12,9 @@ namespace ERPCore.Controllers.Hrm;
|
||||
/// Employee (staff) endpoints, incl. the Employee<->User cross-link and staff
|
||||
/// document sub-resources (docs/13-BACKEND-HRM-API.md §3).
|
||||
/// </summary>
|
||||
// Any HR role may list employees (the leave and payroll screens pick from it); everything else about a person
|
||||
// - the record itself, documents, pay data - is behind the Employees (or Payroll) permission, endpoint by endpoint.
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm")]
|
||||
[Route("api/v1/employees")]
|
||||
public sealed class EmployeesController : ApiControllerBase
|
||||
{
|
||||
@@ -44,11 +48,13 @@ public sealed class EmployeesController : ApiControllerBase
|
||||
=> Ok(await _employees.ListAsync(query, status, departmentId, designationId, branchId, ct));
|
||||
|
||||
/// <summary>Advisory reverse-direction lookup: does a System User already exist with this email? (docs/12-BACKEND-HRM.md A.5)</summary>
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.employees")]
|
||||
[HttpGet("email-lookup")]
|
||||
[ProducesResponseType(typeof(UserMatchResponse), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<UserMatchResponse>> EmailLookup([FromQuery] string email, CancellationToken ct)
|
||||
=> Ok(new UserMatchResponse(await _links.FindUserCandidateByEmailAsync(email, ct)));
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.employees")]
|
||||
[HttpGet("{employeeId:int}")]
|
||||
[ProducesResponseType(typeof(EmployeeDetailDto), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
@@ -60,6 +66,7 @@ public sealed class EmployeesController : ApiControllerBase
|
||||
return Ok(result.Value);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.employees" + NavPolicies.ActionSeparator + NavPolicies.CreateAction)]
|
||||
[HttpPost]
|
||||
[ProducesResponseType(typeof(EmployeeDetailDto), StatusCodes.Status201Created)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
@@ -71,6 +78,7 @@ public sealed class EmployeesController : ApiControllerBase
|
||||
return Created($"/api/v1/employees/{result.Value.EmployeeId}", result.Value);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.employees" + NavPolicies.ActionSeparator + NavPolicies.EditAction)]
|
||||
[HttpPut("{employeeId:int}")]
|
||||
[ProducesResponseType(typeof(EmployeeDetailDto), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
@@ -83,6 +91,7 @@ public sealed class EmployeesController : ApiControllerBase
|
||||
return Ok(result.Value);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.employees" + NavPolicies.ActionSeparator + NavPolicies.EditAction)]
|
||||
[HttpPatch("{employeeId:int}/status")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
@@ -93,6 +102,7 @@ public sealed class EmployeesController : ApiControllerBase
|
||||
}
|
||||
|
||||
/// <summary>Permanently deletes the employee and all dependent HR records. Blocked (409) if payroll history exists.</summary>
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.employees" + NavPolicies.ActionSeparator + NavPolicies.DeleteAction)]
|
||||
[HttpDelete("{employeeId:int}")]
|
||||
[ProducesResponseType(typeof(EmployeeDeleteResultDto), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
@@ -103,6 +113,7 @@ public sealed class EmployeesController : ApiControllerBase
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.employees" + NavPolicies.ActionSeparator + NavPolicies.EditAction)]
|
||||
[HttpPost("{employeeId:int}/link-user")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
@@ -113,6 +124,7 @@ public sealed class EmployeesController : ApiControllerBase
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.employees" + NavPolicies.ActionSeparator + NavPolicies.EditAction)]
|
||||
[HttpDelete("{employeeId:int}/link-user")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
@@ -122,11 +134,13 @@ public sealed class EmployeesController : ApiControllerBase
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.payroll")]
|
||||
[HttpGet("{employeeId:int}/bank-details")]
|
||||
[ProducesResponseType(typeof(List<EmployeeBankDetailDto>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<List<EmployeeBankDetailDto>>> ListBankDetails(int employeeId, CancellationToken ct)
|
||||
=> Ok(await _employees.ListBankDetailsAsync(employeeId, ct));
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.payroll" + NavPolicies.ActionSeparator + NavPolicies.EditAction)]
|
||||
[HttpPut("{employeeId:int}/bank-details")]
|
||||
[ProducesResponseType(typeof(List<EmployeeBankDetailDto>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
@@ -140,6 +154,7 @@ public sealed class EmployeesController : ApiControllerBase
|
||||
public async Task<ActionResult<List<LeaveBalanceDto>>> ListLeaveBalances(int employeeId, [FromQuery] int? year, CancellationToken ct)
|
||||
=> Ok(await _leaveBalances.ListAsync(employeeId, year, ct));
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.employees" + NavPolicies.ActionSeparator + NavPolicies.EditAction)]
|
||||
[HttpPut("{employeeId:int}/leave-balances")]
|
||||
[ProducesResponseType(typeof(List<LeaveBalanceDto>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
@@ -147,11 +162,13 @@ public sealed class EmployeesController : ApiControllerBase
|
||||
int employeeId, [FromBody] UpdateLeaveBalancesRequest request, CancellationToken ct)
|
||||
=> Ok(await _leaveBalances.ApplyAdjustmentsAsync(employeeId, request, ct));
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.payroll")]
|
||||
[HttpGet("{employeeId:int}/salary-structure")]
|
||||
[ProducesResponseType(typeof(List<EmployeeSalaryStructureDto>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<List<EmployeeSalaryStructureDto>>> GetSalaryStructureHistory(int employeeId, CancellationToken ct)
|
||||
=> Ok(await _salaryStructures.ListHistoryAsync(employeeId, ct));
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.payroll" + NavPolicies.ActionSeparator + NavPolicies.EditAction)]
|
||||
[HttpPost("{employeeId:int}/salary-structure")]
|
||||
[ProducesResponseType(typeof(EmployeeSalaryStructureDto), StatusCodes.Status201Created)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
@@ -163,11 +180,13 @@ public sealed class EmployeesController : ApiControllerBase
|
||||
return Created($"/api/v1/employees/{employeeId}/salary-structure", result);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.payroll")]
|
||||
[HttpGet("{employeeId:int}/loans")]
|
||||
[ProducesResponseType(typeof(List<EmployeeLoanDto>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<List<EmployeeLoanDto>>> ListLoans(int employeeId, CancellationToken ct)
|
||||
=> Ok(await _loans.ListAsync(employeeId, ct));
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.payroll")]
|
||||
[HttpGet("{employeeId:int}/loans/{loanId:int}")]
|
||||
[ProducesResponseType(typeof(EmployeeLoanDto), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
@@ -177,6 +196,7 @@ public sealed class EmployeesController : ApiControllerBase
|
||||
return result is null ? NotFound() : Ok(result);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.payroll" + NavPolicies.ActionSeparator + NavPolicies.CreateAction)]
|
||||
[HttpPost("{employeeId:int}/loans")]
|
||||
[ProducesResponseType(typeof(EmployeeLoanDto), StatusCodes.Status201Created)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
@@ -186,11 +206,13 @@ public sealed class EmployeesController : ApiControllerBase
|
||||
return Created($"/api/v1/employees/{employeeId}/loans/{result.EmployeeLoanId}", result);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.employees")]
|
||||
[HttpGet("{employeeId:int}/documents")]
|
||||
[ProducesResponseType(typeof(List<EmployeeDocumentDto>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<List<EmployeeDocumentDto>>> ListDocuments(int employeeId, CancellationToken ct)
|
||||
=> Ok(await _documents.ListAsync(employeeId, ct));
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.employees" + NavPolicies.ActionSeparator + NavPolicies.EditAction)]
|
||||
[HttpPost("{employeeId:int}/documents")]
|
||||
[RequestSizeLimit(20 * 1024 * 1024)]
|
||||
[ProducesResponseType(typeof(EmployeeDocumentDto), StatusCodes.Status201Created)]
|
||||
@@ -206,6 +228,7 @@ public sealed class EmployeesController : ApiControllerBase
|
||||
return Created($"/api/v1/employees/{employeeId}/documents/{result.EmployeeDocumentId}", result);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.employees")]
|
||||
[HttpGet("{employeeId:int}/documents/{documentId:int}/download")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
@@ -215,6 +238,7 @@ public sealed class EmployeesController : ApiControllerBase
|
||||
return File(content, contentType, fileName);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.employees" + NavPolicies.ActionSeparator + NavPolicies.EditAction)]
|
||||
[HttpPatch("{employeeId:int}/documents/{documentId:int}/status")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
using ERPCore.Domain.Enums;
|
||||
using ERPCore.Dtos.Common;
|
||||
using ERPCore.Dtos.Hrm;
|
||||
using ERPCore.Infra.Auth;
|
||||
using ERPCore.Services.Interfaces;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ERPCore.Controllers.Hrm;
|
||||
|
||||
/// <summary>EmploymentType master endpoints (docs/13-BACKEND-HRM-API.md §2).</summary>
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm")]
|
||||
[Route("api/v1/employment-types")]
|
||||
public sealed class EmploymentTypesController : ApiControllerBase
|
||||
{
|
||||
@@ -31,6 +34,7 @@ public sealed class EmploymentTypesController : ApiControllerBase
|
||||
return Ok(result.Value);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.settings")]
|
||||
[HttpPost]
|
||||
[ProducesResponseType(typeof(EmploymentTypeDto), StatusCodes.Status201Created)]
|
||||
[ProducesResponseType(StatusCodes.Status409Conflict)]
|
||||
@@ -41,6 +45,7 @@ public sealed class EmploymentTypesController : ApiControllerBase
|
||||
return Created($"/api/v1/employment-types/{result.Value.EmploymentTypeId}", result.Value);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.settings")]
|
||||
[HttpPut("{employmentTypeId:int}")]
|
||||
[ProducesResponseType(typeof(EmploymentTypeDto), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
@@ -53,6 +58,7 @@ public sealed class EmploymentTypesController : ApiControllerBase
|
||||
return Ok(result.Value);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.settings")]
|
||||
[HttpPatch("{employmentTypeId:int}/status")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
@@ -62,6 +68,7 @@ public sealed class EmploymentTypesController : ApiControllerBase
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.settings")]
|
||||
[HttpDelete("{employmentTypeId:int}")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
using ERPCore.Domain.Enums;
|
||||
using ERPCore.Dtos.Common;
|
||||
using ERPCore.Dtos.Hrm;
|
||||
using ERPCore.Infra.Auth;
|
||||
using ERPCore.Services.Interfaces;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ERPCore.Controllers.Hrm;
|
||||
|
||||
/// <summary>Staff document-type catalog ("DocType") endpoints (docs/13-BACKEND-HRM-API.md §2).</summary>
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm")]
|
||||
[Route("api/v1/hr-document-types")]
|
||||
public sealed class HrDocumentTypesController : ApiControllerBase
|
||||
{
|
||||
@@ -31,6 +34,7 @@ public sealed class HrDocumentTypesController : ApiControllerBase
|
||||
return Ok(result.Value);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.settings")]
|
||||
[HttpPost]
|
||||
[ProducesResponseType(typeof(HrDocumentTypeDto), StatusCodes.Status201Created)]
|
||||
[ProducesResponseType(StatusCodes.Status409Conflict)]
|
||||
@@ -41,6 +45,7 @@ public sealed class HrDocumentTypesController : ApiControllerBase
|
||||
return Created($"/api/v1/hr-document-types/{result.Value.HrDocumentTypeId}", result.Value);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.settings")]
|
||||
[HttpPut("{hrDocumentTypeId:int}")]
|
||||
[ProducesResponseType(typeof(HrDocumentTypeDto), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
@@ -53,6 +58,7 @@ public sealed class HrDocumentTypesController : ApiControllerBase
|
||||
return Ok(result.Value);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.settings")]
|
||||
[HttpPatch("{hrDocumentTypeId:int}/status")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
@@ -62,6 +68,7 @@ public sealed class HrDocumentTypesController : ApiControllerBase
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.settings")]
|
||||
[HttpDelete("{hrDocumentTypeId:int}")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
using ERPCore.Dtos.Hrm;
|
||||
using ERPCore.Infra.Auth;
|
||||
using ERPCore.Services.Interfaces;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ERPCore.Controllers.Hrm;
|
||||
|
||||
/// <summary>Read-only HRM reports (FR-HR-RPT, docs/13-BACKEND-HRM-API.md §6). No new entities — aggregation over existing tables.</summary>
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.reports")]
|
||||
[Route("api/v1/reports/hrm")]
|
||||
public sealed class HrReportsController : ApiControllerBase
|
||||
{
|
||||
@@ -30,11 +33,13 @@ public sealed class HrReportsController : ApiControllerBase
|
||||
[FromQuery] int periodYear, [FromQuery] int periodMonth, CancellationToken ct)
|
||||
=> Ok(await _reports.LateArrivalReportAsync(periodYear, periodMonth, ct));
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.payroll")]
|
||||
[HttpGet("payroll-register")]
|
||||
[ProducesResponseType(typeof(List<PayrollRegisterRowDto>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<List<PayrollRegisterRowDto>>> PayrollRegister([FromQuery] int payrollRunId, CancellationToken ct)
|
||||
=> Ok(await _reports.PayrollRegisterAsync(payrollRunId, ct));
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.payroll")]
|
||||
[HttpGet("salary-history")]
|
||||
[ProducesResponseType(typeof(List<SalaryHistoryRowDto>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<List<SalaryHistoryRowDto>>> SalaryHistory([FromQuery] int employeeId, CancellationToken ct)
|
||||
|
||||
@@ -3,11 +3,13 @@ using ERPCore.Dtos.Common;
|
||||
using ERPCore.Dtos.Hrm;
|
||||
using ERPCore.Infra.Auth;
|
||||
using ERPCore.Services.Interfaces;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ERPCore.Controllers.Hrm;
|
||||
|
||||
/// <summary>Leave request endpoints (docs/13-BACKEND-HRM-API.md §5).</summary>
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.leave")]
|
||||
[Route("api/v1/leave-requests")]
|
||||
public sealed class LeaveRequestsController : ApiControllerBase
|
||||
{
|
||||
@@ -35,6 +37,7 @@ public sealed class LeaveRequestsController : ApiControllerBase
|
||||
return result is null ? NotFound() : Ok(result);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.leave" + NavPolicies.ActionSeparator + NavPolicies.CreateAction)]
|
||||
[HttpPost]
|
||||
[ProducesResponseType(typeof(LeaveRequestDto), StatusCodes.Status201Created)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
@@ -45,24 +48,28 @@ public sealed class LeaveRequestsController : ApiControllerBase
|
||||
return Created($"/api/v1/leave-requests/{result.LeaveRequestId}", result);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.leave" + NavPolicies.ActionSeparator + NavPolicies.CreateAction)]
|
||||
[HttpPost("{leaveRequestId:int}/submit")]
|
||||
[ProducesResponseType(typeof(LeaveRequestDto), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status409Conflict)]
|
||||
public async Task<ActionResult<LeaveRequestDto>> Submit(int leaveRequestId, CancellationToken ct)
|
||||
=> Ok(await _requests.SubmitAsync(leaveRequestId, ct));
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.leave" + NavPolicies.ActionSeparator + NavPolicies.ApproveAction)]
|
||||
[HttpPost("{leaveRequestId:int}/approve")]
|
||||
[ProducesResponseType(typeof(LeaveRequestDto), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status409Conflict)]
|
||||
public async Task<ActionResult<LeaveRequestDto>> Approve(int leaveRequestId, CancellationToken ct)
|
||||
=> Ok(await _requests.ApproveAsync(leaveRequestId, _currentUser.AuditUserId, ct));
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.leave" + NavPolicies.ActionSeparator + NavPolicies.ApproveAction)]
|
||||
[HttpPost("{leaveRequestId:int}/reject")]
|
||||
[ProducesResponseType(typeof(LeaveRequestDto), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status409Conflict)]
|
||||
public async Task<ActionResult<LeaveRequestDto>> Reject(int leaveRequestId, [FromBody] RejectLeaveRequestRequest request, CancellationToken ct)
|
||||
=> Ok(await _requests.RejectAsync(leaveRequestId, request.Reason, _currentUser.AuditUserId, ct));
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.leave" + NavPolicies.ActionSeparator + NavPolicies.CreateAction)]
|
||||
[HttpPost("{leaveRequestId:int}/cancel")]
|
||||
[ProducesResponseType(typeof(LeaveRequestDto), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status409Conflict)]
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
using ERPCore.Domain.Enums;
|
||||
using ERPCore.Dtos.Common;
|
||||
using ERPCore.Dtos.Hrm;
|
||||
using ERPCore.Infra.Auth;
|
||||
using ERPCore.Services.Interfaces;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ERPCore.Controllers.Hrm;
|
||||
|
||||
/// <summary>Leave type master endpoints (docs/13-BACKEND-HRM-API.md §5).</summary>
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm")]
|
||||
[Route("api/v1/leave-types")]
|
||||
public sealed class LeaveTypesController : ApiControllerBase
|
||||
{
|
||||
@@ -31,6 +34,7 @@ public sealed class LeaveTypesController : ApiControllerBase
|
||||
return Ok(result.Value);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.settings")]
|
||||
[HttpPost]
|
||||
[ProducesResponseType(typeof(LeaveTypeDto), StatusCodes.Status201Created)]
|
||||
[ProducesResponseType(StatusCodes.Status409Conflict)]
|
||||
@@ -41,6 +45,7 @@ public sealed class LeaveTypesController : ApiControllerBase
|
||||
return Created($"/api/v1/leave-types/{result.Value.LeaveTypeId}", result.Value);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.settings")]
|
||||
[HttpPut("{leaveTypeId:int}")]
|
||||
[ProducesResponseType(typeof(LeaveTypeDto), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
@@ -53,6 +58,7 @@ public sealed class LeaveTypesController : ApiControllerBase
|
||||
return Ok(result.Value);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.settings")]
|
||||
[HttpPatch("{leaveTypeId:int}/status")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
@@ -62,6 +68,7 @@ public sealed class LeaveTypesController : ApiControllerBase
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.settings")]
|
||||
[HttpDelete("{leaveTypeId:int}")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
|
||||
@@ -3,11 +3,13 @@ using ERPCore.Dtos.Common;
|
||||
using ERPCore.Dtos.Hrm;
|
||||
using ERPCore.Infra.Auth;
|
||||
using ERPCore.Services.Interfaces;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ERPCore.Controllers.Hrm;
|
||||
|
||||
/// <summary>Payroll run endpoints (docs/13-BACKEND-HRM-API.md §6).</summary>
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.payroll")]
|
||||
[Route("api/v1/payroll-runs")]
|
||||
public sealed class PayrollRunsController : ApiControllerBase
|
||||
{
|
||||
@@ -50,6 +52,7 @@ public sealed class PayrollRunsController : ApiControllerBase
|
||||
return result is null ? NotFound() : Ok(result);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.payroll" + NavPolicies.ActionSeparator + NavPolicies.CreateAction)]
|
||||
[HttpPost]
|
||||
[ProducesResponseType(typeof(PayrollRunDto), StatusCodes.Status201Created)]
|
||||
[ProducesResponseType(StatusCodes.Status422UnprocessableEntity)]
|
||||
@@ -59,24 +62,28 @@ public sealed class PayrollRunsController : ApiControllerBase
|
||||
return Created($"/api/v1/payroll-runs/{result.PayrollRunId}", result);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.payroll" + NavPolicies.ActionSeparator + NavPolicies.ApproveAction)]
|
||||
[HttpPost("{payrollRunId:int}/approve")]
|
||||
[ProducesResponseType(typeof(PayrollRunDto), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status409Conflict)]
|
||||
public async Task<ActionResult<PayrollRunDto>> Approve(int payrollRunId, CancellationToken ct)
|
||||
=> Ok(await _payrollRuns.ApproveAsync(payrollRunId, _currentUser.AuditUserId, ct));
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.payroll" + NavPolicies.ActionSeparator + NavPolicies.ApproveAction)]
|
||||
[HttpPost("{payrollRunId:int}/lock")]
|
||||
[ProducesResponseType(typeof(PayrollRunDto), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status409Conflict)]
|
||||
public async Task<ActionResult<PayrollRunDto>> Lock(int payrollRunId, CancellationToken ct)
|
||||
=> Ok(await _payrollRuns.LockAsync(payrollRunId, _currentUser.AuditUserId, ct));
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.payroll" + NavPolicies.ActionSeparator + NavPolicies.ApproveAction)]
|
||||
[HttpPost("{payrollRunId:int}/unlock")]
|
||||
[ProducesResponseType(typeof(PayrollRunDto), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status409Conflict)]
|
||||
public async Task<ActionResult<PayrollRunDto>> Unlock(int payrollRunId, [FromBody] UnlockPayrollRunRequest request, CancellationToken ct)
|
||||
=> Ok(await _payrollRuns.UnlockAsync(payrollRunId, request.Reason, _currentUser.AuditUserId, ct));
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.payroll" + NavPolicies.ActionSeparator + NavPolicies.CreateAction)]
|
||||
[HttpPost("{payrollRunId:int}/generate-payslips")]
|
||||
[ProducesResponseType(typeof(List<PayslipDto>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status409Conflict)]
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
using ERPCore.Dtos.Hrm;
|
||||
using ERPCore.Infra.Auth;
|
||||
using ERPCore.Services.Interfaces;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ERPCore.Controllers.Hrm;
|
||||
|
||||
/// <summary>Effective-dated EPF/ETF settings (docs/13-BACKEND-HRM-API.md §6).</summary>
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm")]
|
||||
[Route("api/v1/payroll-statutory-settings")]
|
||||
public sealed class PayrollStatutorySettingsController : ApiControllerBase
|
||||
{
|
||||
@@ -23,6 +25,7 @@ public sealed class PayrollStatutorySettingsController : ApiControllerBase
|
||||
public async Task<ActionResult<List<PayrollStatutorySettingDto>>> List(CancellationToken ct)
|
||||
=> Ok(await _settings.ListAsync(ct));
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.settings")]
|
||||
[HttpPost]
|
||||
[ProducesResponseType(typeof(PayrollStatutorySettingDto), StatusCodes.Status201Created)]
|
||||
public async Task<ActionResult<PayrollStatutorySettingDto>> Create([FromBody] UpsertPayrollStatutorySettingRequest request, CancellationToken ct)
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
using ERPCore.Dtos.Hrm;
|
||||
using ERPCore.Infra.Auth;
|
||||
using ERPCore.Services.Interfaces;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ERPCore.Controllers.Hrm;
|
||||
|
||||
/// <summary>Payslip retrieval + HTML print view (docs/13-BACKEND-HRM-API.md §6).</summary>
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.payroll")]
|
||||
[Route("api/v1/payslips")]
|
||||
public sealed class PayslipsController : ApiControllerBase
|
||||
{
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
using ERPCore.Domain.Enums;
|
||||
using ERPCore.Dtos.Common;
|
||||
using ERPCore.Dtos.Hrm;
|
||||
using ERPCore.Infra.Auth;
|
||||
using ERPCore.Services.Interfaces;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ERPCore.Controllers.Hrm;
|
||||
|
||||
/// <summary>SalaryComponent master endpoints (docs/13-BACKEND-HRM-API.md §6).</summary>
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm")]
|
||||
[Route("api/v1/salary-components")]
|
||||
public sealed class SalaryComponentsController : ApiControllerBase
|
||||
{
|
||||
@@ -31,6 +34,7 @@ public sealed class SalaryComponentsController : ApiControllerBase
|
||||
return Ok(result.Value);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.settings")]
|
||||
[HttpPost]
|
||||
[ProducesResponseType(typeof(SalaryComponentDto), StatusCodes.Status201Created)]
|
||||
[ProducesResponseType(StatusCodes.Status409Conflict)]
|
||||
@@ -41,6 +45,7 @@ public sealed class SalaryComponentsController : ApiControllerBase
|
||||
return Created($"/api/v1/salary-components/{result.Value.SalaryComponentId}", result.Value);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.settings")]
|
||||
[HttpPut("{salaryComponentId:int}")]
|
||||
[ProducesResponseType(typeof(SalaryComponentDto), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
@@ -53,6 +58,7 @@ public sealed class SalaryComponentsController : ApiControllerBase
|
||||
return Ok(result.Value);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.settings")]
|
||||
[HttpPatch("{salaryComponentId:int}/status")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
@@ -62,6 +68,7 @@ public sealed class SalaryComponentsController : ApiControllerBase
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.settings")]
|
||||
[HttpDelete("{salaryComponentId:int}")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
using ERPCore.Dtos.Hrm;
|
||||
using ERPCore.Infra.Auth;
|
||||
using ERPCore.Services.Interfaces;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ERPCore.Controllers.Hrm;
|
||||
|
||||
/// <summary>Configurable APIT-style tax slabs (docs/13-BACKEND-HRM-API.md §6).</summary>
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm")]
|
||||
[Route("api/v1/tax-slabs")]
|
||||
public sealed class TaxSlabsController : ApiControllerBase
|
||||
{
|
||||
@@ -17,6 +20,7 @@ public sealed class TaxSlabsController : ApiControllerBase
|
||||
public async Task<ActionResult<List<TaxSlabDto>>> List(CancellationToken ct)
|
||||
=> Ok(await _taxSlabs.ListAsync(ct));
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.settings")]
|
||||
[HttpPost]
|
||||
[ProducesResponseType(typeof(TaxSlabDto), StatusCodes.Status201Created)]
|
||||
[ProducesResponseType(StatusCodes.Status422UnprocessableEntity)]
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
using ERPCore.Domain.Enums;
|
||||
using ERPCore.Dtos.Common;
|
||||
using ERPCore.Dtos.Hrm;
|
||||
using ERPCore.Infra.Auth;
|
||||
using ERPCore.Services.Interfaces;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ERPCore.Controllers.Hrm;
|
||||
|
||||
/// <summary>WorkShift master endpoints (docs/13-BACKEND-HRM-API.md §2).</summary>
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm")]
|
||||
[Route("api/v1/work-shifts")]
|
||||
public sealed class WorkShiftsController : ApiControllerBase
|
||||
{
|
||||
@@ -31,6 +34,7 @@ public sealed class WorkShiftsController : ApiControllerBase
|
||||
return Ok(result.Value);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.settings")]
|
||||
[HttpPost]
|
||||
[ProducesResponseType(typeof(WorkShiftDto), StatusCodes.Status201Created)]
|
||||
[ProducesResponseType(StatusCodes.Status409Conflict)]
|
||||
@@ -41,6 +45,7 @@ public sealed class WorkShiftsController : ApiControllerBase
|
||||
return Created($"/api/v1/work-shifts/{result.Value.WorkShiftId}", result.Value);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.settings")]
|
||||
[HttpPut("{workShiftId:int}")]
|
||||
[ProducesResponseType(typeof(WorkShiftDto), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
@@ -53,6 +58,7 @@ public sealed class WorkShiftsController : ApiControllerBase
|
||||
return Ok(result.Value);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.settings")]
|
||||
[HttpPatch("{workShiftId:int}/status")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
@@ -62,6 +68,7 @@ public sealed class WorkShiftsController : ApiControllerBase
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "hrm.settings")]
|
||||
[HttpDelete("{workShiftId:int}")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
using ERPCore.Dtos.OpeningBalances;
|
||||
using ERPCore.Infra.Auth;
|
||||
using ERPCore.Services.Interfaces;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ERPCore.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// One-time opening balances. Gated on the <c>accounts.opening-balances</c> nav code (Admin always passes;
|
||||
/// other roles need it granted under Settings → Roles).
|
||||
/// </summary>
|
||||
[Route("api/v1/opening-balances")]
|
||||
[Authorize(Policy = NavPolicies.Prefix + "accounts.opening-balances")]
|
||||
public sealed class OpeningBalancesController : ApiControllerBase
|
||||
{
|
||||
private readonly IOpeningBalanceService _openingBalances;
|
||||
private readonly IOpeningStockService _openingStock;
|
||||
|
||||
public OpeningBalancesController(IOpeningBalanceService openingBalances, IOpeningStockService openingStock)
|
||||
{
|
||||
_openingBalances = openingBalances;
|
||||
_openingStock = openingStock;
|
||||
}
|
||||
|
||||
/// <summary>The cash and bank accounts to enter opening amounts for — or the posted record, once done.</summary>
|
||||
[HttpGet("cash-bank")]
|
||||
[ProducesResponseType(typeof(CashBankOpeningStateDto), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<CashBankOpeningStateDto>> GetCashBank(CancellationToken ct) =>
|
||||
Ok(await _openingBalances.GetCashBankAsync(ct));
|
||||
|
||||
/// <summary>Posts the cash and bank opening balances as one journal. Allowed once; later calls get 409.</summary>
|
||||
[HttpPost("cash-bank")]
|
||||
[ProducesResponseType(typeof(OpeningBalanceRunDto), StatusCodes.Status201Created)]
|
||||
[ProducesResponseType(StatusCodes.Status409Conflict)]
|
||||
public async Task<ActionResult<OpeningBalanceRunDto>> PostCashBank(
|
||||
[FromBody] PostCashBankOpeningRequest request, CancellationToken ct)
|
||||
{
|
||||
var run = await _openingBalances.PostCashBankAsync(request, ct);
|
||||
return StatusCode(StatusCodes.Status201Created, run);
|
||||
}
|
||||
|
||||
/// <summary>Opening-stock runs posted so far, newest first.</summary>
|
||||
[HttpGet("stock")]
|
||||
[ProducesResponseType(typeof(IReadOnlyList<OpeningStockRunSummaryDto>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<IReadOnlyList<OpeningStockRunSummaryDto>>> ListStock(CancellationToken ct) =>
|
||||
Ok(await _openingStock.ListAsync(ct));
|
||||
|
||||
/// <summary>One opening-stock run with its lines.</summary>
|
||||
[HttpGet("stock/{runId:int}")]
|
||||
[ProducesResponseType(typeof(OpeningStockRunDto), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<OpeningStockRunDto>> GetStock(int runId, CancellationToken ct)
|
||||
{
|
||||
var run = await _openingStock.GetAsync(runId, ct);
|
||||
return run is null ? NotFound() : Ok(run);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Posts opening stock: a FIFO layer per line and one journal (Dr Inventory / Cr Opening Balance Equity).
|
||||
/// An item that already has stock movements in that warehouse is refused with 409.
|
||||
/// </summary>
|
||||
[HttpPost("stock")]
|
||||
[ProducesResponseType(typeof(OpeningStockRunDto), StatusCodes.Status201Created)]
|
||||
[ProducesResponseType(StatusCodes.Status409Conflict)]
|
||||
public async Task<ActionResult<OpeningStockRunDto>> PostStock([FromBody] PostOpeningStockRequest request, CancellationToken ct)
|
||||
{
|
||||
var run = await _openingStock.PostAsync(request, ct);
|
||||
return StatusCode(StatusCodes.Status201Created, run);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using ERPCore.Dtos.Config;
|
||||
using ERPCore.Infra.Auth;
|
||||
using ERPCore.Services.Interfaces;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ERPCore.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Procurement configuration endpoints — optional vendor fields and PO tax.
|
||||
/// <para>
|
||||
/// <b>Authorization:</b> reading is open to any signed-in ERP user, because the vendor and purchase-order
|
||||
/// forms must read the flags and those screens sit under different nav codes. The flags are not
|
||||
/// sensitive. Changing them needs the <c>settings</c> nav policy, like <see cref="BranchConfigController"/>.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[Route("api/v1/procurement-config")]
|
||||
public sealed class ProcurementConfigController : ApiControllerBase
|
||||
{
|
||||
private readonly IProcurementConfigService _config;
|
||||
|
||||
public ProcurementConfigController(IProcurementConfigService config) => _config = config;
|
||||
|
||||
[HttpGet]
|
||||
[ProducesResponseType(typeof(ProcurementConfigDto), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<ProcurementConfigDto>> Get(CancellationToken ct)
|
||||
{
|
||||
var result = await _config.GetAsync(ct);
|
||||
SetETag(result.RowVersion);
|
||||
return Ok(result.Value);
|
||||
}
|
||||
|
||||
[HttpPut]
|
||||
[Authorize(Policy = NavPolicies.Prefix + "settings")]
|
||||
[ProducesResponseType(typeof(ProcurementConfigDto), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status412PreconditionFailed)]
|
||||
public async Task<ActionResult<ProcurementConfigDto>> Update(
|
||||
[FromBody] UpdateProcurementConfigRequest request, CancellationToken ct)
|
||||
{
|
||||
var expected = RequireIfMatch();
|
||||
var result = await _config.UpdateAsync(request, expected, ct);
|
||||
SetETag(result.RowVersion);
|
||||
return Ok(result.Value);
|
||||
}
|
||||
}
|
||||
@@ -20,8 +20,9 @@ public sealed class PurchaseOrdersController : ApiControllerBase
|
||||
[HttpGet]
|
||||
[ProducesResponseType(typeof(PagedResponse<PurchaseOrderSummaryDto>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedResponse<PurchaseOrderSummaryDto>>> List(
|
||||
[FromQuery] PageQuery query, [FromQuery] PurchaseOrderStatus? status, [FromQuery] int? vendorId, CancellationToken ct)
|
||||
=> Ok(await _pos.ListAsync(query, status, vendorId, ct));
|
||||
[FromQuery] PageQuery query, [FromQuery] PurchaseOrderStatus? status, [FromQuery] int? vendorId,
|
||||
[FromQuery] DateOnly? from, [FromQuery] DateOnly? to, CancellationToken ct)
|
||||
=> Ok(await _pos.ListAsync(query, status, vendorId, from, to, ct));
|
||||
|
||||
[HttpGet("{poId:int}")]
|
||||
[ProducesResponseType(typeof(PurchaseOrderDto), StatusCodes.Status200OK)]
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using ERPCore.Domain.Enums;
|
||||
using ERPCore.Dtos.Common;
|
||||
using ERPCore.Dtos.Reference;
|
||||
using ERPCore.Infra.Auth;
|
||||
using ERPCore.Services.Interfaces;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ERPCore.Controllers;
|
||||
@@ -20,6 +22,7 @@ public sealed class ReasonCodesController : ApiControllerBase
|
||||
[FromQuery] ReasonContext? context, [FromQuery] PageQuery query, CancellationToken ct)
|
||||
=> Ok(await _codes.ListAsync(context, query, ct));
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "settings")]
|
||||
[HttpPost]
|
||||
[ProducesResponseType(typeof(ReasonCodeDto), StatusCodes.Status201Created)]
|
||||
[ProducesResponseType(StatusCodes.Status409Conflict)]
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using ERPCore.Infra.Auth;
|
||||
using ERPCore.Dtos.Common;
|
||||
using ERPCore.Dtos.Sales;
|
||||
using ERPCore.Infra.Auth;
|
||||
using ERPCore.Services.Interfaces;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ERPCore.Controllers;
|
||||
|
||||
/// <summary>Sales-return endpoints — customer returns of previously sold goods.</summary>
|
||||
[Route("api/v1/sales-returns")]
|
||||
[Authorize(Policy = NavPolicies.Prefix + "sales")]
|
||||
[Authorize(Policy = NavPolicies.Prefix + "sales-returns")]
|
||||
public sealed class SalesReturnsController : ApiControllerBase
|
||||
{
|
||||
private readonly ISalesReturnService _returns;
|
||||
|
||||
@@ -41,8 +41,8 @@ public sealed class UsersController : ApiControllerBase
|
||||
|
||||
[HttpGet]
|
||||
[ProducesResponseType(typeof(PagedResponse<ManagedUserDto>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedResponse<ManagedUserDto>>> List([FromQuery] PageQuery query, [FromQuery] EntityStatus? status, [FromQuery] string? roleCode, CancellationToken ct)
|
||||
=> Ok(await _users.ListAsync(query, status, roleCode, ct));
|
||||
public async Task<ActionResult<PagedResponse<ManagedUserDto>>> List([FromQuery] PageQuery query, [FromQuery] EntityStatus? status, [FromQuery] string? roleCode, [FromQuery] bool? isAdmin, CancellationToken ct)
|
||||
=> Ok(await _users.ListAsync(query, status, roleCode, isAdmin, ct));
|
||||
|
||||
/// <summary>AuthHex UserType options for the create-user form's select.</summary>
|
||||
[HttpGet("user-types")]
|
||||
@@ -117,4 +117,20 @@ public sealed class UsersController : ApiControllerBase
|
||||
|
||||
return Ok(await _users.PurgeAuthHexIdentityAsync(request, ct));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// One-time migration helper (administrators only): copies each role-less user's role from AuthHex into the ERP.
|
||||
/// Safe to repeat — users that already have a role are left alone. See
|
||||
/// <see cref="IUserManagementService.BackfillRolesFromAuthHexAsync"/>.
|
||||
/// </summary>
|
||||
[HttpPost("backfill-roles")]
|
||||
[ProducesResponseType(typeof(BackfillRolesResultDto), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
public async Task<ActionResult<BackfillRolesResultDto>> BackfillRoles(CancellationToken ct)
|
||||
{
|
||||
if (!_currentUser.IsAdmin)
|
||||
throw new DomainException("FORBIDDEN", "Only an administrator can run the role backfill.", 403);
|
||||
|
||||
return Ok(await _users.BackfillRolesFromAuthHexAsync(ct));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using ERPCore.Infra.Auth;
|
||||
using ERPCore.Domain.Enums;
|
||||
using ERPCore.Dtos.Common;
|
||||
using ERPCore.Dtos.Vendors;
|
||||
using ERPCore.Infra.Auth;
|
||||
using ERPCore.Services.Interfaces;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ERPCore.Controllers;
|
||||
|
||||
/// <summary>Vendor master endpoints (docs/11-BACKEND-PHASE1.md §2.4).</summary>
|
||||
[Route("api/v1/vendors")]
|
||||
[Authorize(Policy = NavPolicies.Prefix + "vendors")]
|
||||
[Authorize(Policy = NavPolicies.Prefix + "procurement")]
|
||||
public sealed class VendorsController : ApiControllerBase
|
||||
{
|
||||
private readonly IVendorService _vendors;
|
||||
@@ -35,6 +35,7 @@ public sealed class VendorsController : ApiControllerBase
|
||||
return Ok(result.Value);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "procurement.vendors" + NavPolicies.ActionSeparator + NavPolicies.CreateAction)]
|
||||
[HttpPost]
|
||||
[ProducesResponseType(typeof(VendorDto), StatusCodes.Status201Created)]
|
||||
[ProducesResponseType(StatusCodes.Status409Conflict)]
|
||||
@@ -45,6 +46,7 @@ public sealed class VendorsController : ApiControllerBase
|
||||
return Created($"/api/v1/vendors/{result.Value.VendorId}", result.Value);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "procurement.vendors" + NavPolicies.ActionSeparator + NavPolicies.EditAction)]
|
||||
[HttpPut("{vendorId:int}")]
|
||||
[ProducesResponseType(typeof(VendorDto), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
@@ -57,6 +59,7 @@ public sealed class VendorsController : ApiControllerBase
|
||||
return Ok(result.Value);
|
||||
}
|
||||
|
||||
[Authorize(Policy = NavPolicies.Prefix + "procurement.vendors" + NavPolicies.ActionSeparator + NavPolicies.EditAction)]
|
||||
[HttpPatch("{vendorId:int}/status")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
@@ -67,7 +70,7 @@ public sealed class VendorsController : ApiControllerBase
|
||||
}
|
||||
|
||||
[HttpDelete("{vendorId:int}")]
|
||||
[Authorize(Policy = NavPolicies.Prefix + "vendors" + NavPolicies.ActionSeparator + NavPolicies.DeleteAction)]
|
||||
[Authorize(Policy = NavPolicies.Prefix + "procurement.vendors" + NavPolicies.ActionSeparator + NavPolicies.DeleteAction)]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(StatusCodes.Status409Conflict)]
|
||||
|
||||
Reference in New Issue
Block a user