Files
Core_Be/Controllers/InventoryController.cs
T
HarithaRandunu 2eb88b058e Add inventory management features including Sales, Stock Returns, and Suppliers
- Implement Sale and SaleItem models for tracking sales transactions.
- Create StockReturn model to manage stock adjustments for various reasons.
- Introduce Supplier model to represent suppliers of products.
- Develop InventoryRepository for CRUD operations on products, suppliers, sales, and stock returns.
- Implement InventoryService to handle business logic for inventory management.
- Update TenantDbContext to include new models and relationships.
- Enhance ProfitLossRepository to account for sales, purchases, and stock returns in profit and loss calculations.
- Modify Program.cs to register new inventory-related services.
2026-10-07 15:43:20 +05:30

175 lines
7.6 KiB
C#

using Fitness_core_BE.DTOs.Inventory;
using Fitness_core_BE.Helpers;
using Fitness_core_BE.Interfaces.Services;
using Fitness_core_BE.Models.IOs;
using Microsoft.AspNetCore.Mvc;
using static Fitness_core_BE.Helpers.JwtMiddleware;
using static Fitness_core_BE.Helpers.ApiResponseHelper;
namespace Fitness_core_BE.Controllers
{
// Simple inventory: Products, Suppliers, GRN (stock in), Sales (stock out) and Returns
// (stock write-offs). All gated on the "inventory" nav permission.
[ApiController]
[Route("api/client/inventory")]
public class InventoryController(IInventoryService inventoryService) : ControllerBase
{
private readonly IInventoryService _inventoryService = inventoryService;
[CustomMiddleware]
[TenantResolution]
[RequiresPermission("inventory")]
[HttpGet("products")]
public async Task<ActionResult<ApiResponse<object>>> GetProducts([FromQuery] bool includeInactive = false)
=> Response(await _inventoryService.GetProducts(new { includeInactive }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[RequiresPermission("inventory")]
[HttpGet("products/{id:guid}")]
public async Task<ActionResult<ApiResponse<object>>> GetProductById(Guid id)
=> Response(await _inventoryService.GetProductById(new { productId = id }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[RequiresPermission("inventory")]
[HttpPost("products")]
public async Task<ActionResult<ApiResponse<object>>> CreateProduct([FromBody] CreateProductRequest request)
=> Response(await _inventoryService.CreateProduct(new
{
name = request.Name,
unitOfMeasure = request.UnitOfMeasure,
sellingPrice = request.SellingPrice
}, HttpContext));
[CustomMiddleware]
[TenantResolution]
[RequiresPermission("inventory")]
[HttpPut("products/{id:guid}")]
public async Task<ActionResult<ApiResponse<object>>> UpdateProduct(Guid id, [FromBody] UpdateProductRequest request)
{
var payload = new Dictionary<string, object?> { ["productId"] = id };
if (request.Name != null) payload["name"] = request.Name;
if (request.UnitOfMeasure != null) payload["unitOfMeasure"] = request.UnitOfMeasure;
if (request.SellingPrice.HasValue) payload["sellingPrice"] = request.SellingPrice.Value;
if (request.IsActive.HasValue) payload["isActive"] = request.IsActive.Value;
return Response(await _inventoryService.UpdateProduct(payload, HttpContext));
}
[CustomMiddleware]
[TenantResolution]
[RequiresPermission("inventory")]
[HttpPost("products/{id:guid}/image")]
[RequestSizeLimit(6_000_000)]
[Consumes("multipart/form-data")]
public async Task<ActionResult<ApiResponse<object>>> UploadProductImage(Guid id, [FromForm] IFormFile image)
=> Response(await _inventoryService.UploadProductImage(id, image, HttpContext));
[CustomMiddleware]
[TenantResolution]
[RequiresPermission("inventory")]
[HttpGet("suppliers")]
public async Task<ActionResult<ApiResponse<object>>> GetSuppliers([FromQuery] bool includeInactive = false)
=> Response(await _inventoryService.GetSuppliers(new { includeInactive }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[RequiresPermission("inventory")]
[HttpPost("suppliers")]
public async Task<ActionResult<ApiResponse<object>>> CreateSupplier([FromBody] CreateSupplierRequest request)
=> Response(await _inventoryService.CreateSupplier(new
{
name = request.Name,
phone = request.Phone,
email = request.Email,
address = request.Address
}, HttpContext));
[CustomMiddleware]
[TenantResolution]
[RequiresPermission("inventory")]
[HttpGet("grn")]
public async Task<ActionResult<ApiResponse<object>>> GetGrns()
=> Response(await _inventoryService.GetGrns(new { }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[RequiresPermission("inventory")]
[HttpGet("grn/{id:guid}")]
public async Task<ActionResult<ApiResponse<object>>> GetGrnById(Guid id)
=> Response(await _inventoryService.GetGrnById(new { grnId = id }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[RequiresPermission("inventory")]
[HttpPost("grn")]
public async Task<ActionResult<ApiResponse<object>>> CreateGrn([FromBody] CreateGrnRequest request)
=> Response(await _inventoryService.CreateGrn(new
{
supplierId = request.SupplierId,
receivedAt = request.ReceivedAt?.ToString("o"),
notes = request.Notes,
items = request.Items.Select(i => new { productId = i.ProductId, quantity = i.Quantity, unitCost = i.UnitCost })
}, HttpContext));
[CustomMiddleware]
[TenantResolution]
[RequiresPermission("inventory")]
[HttpGet("sales")]
public async Task<ActionResult<ApiResponse<object>>> GetSales()
=> Response(await _inventoryService.GetSales(new { }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[RequiresPermission("inventory")]
[HttpGet("sales/{id:guid}")]
public async Task<ActionResult<ApiResponse<object>>> GetSaleById(Guid id)
=> Response(await _inventoryService.GetSaleById(new { saleId = id }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[RequiresPermission("inventory")]
[HttpPost("sales")]
public async Task<ActionResult<ApiResponse<object>>> CreateSale([FromBody] CreateSaleRequest request)
=> Response(await _inventoryService.CreateSale(new
{
soldAt = request.SoldAt?.ToString("o"),
paymentMethod = request.PaymentMethod,
customerId = request.CustomerId,
customerName = request.CustomerName,
items = request.Items.Select(i => new { productId = i.ProductId, quantity = i.Quantity, unitPrice = i.UnitPrice })
}, HttpContext));
[CustomMiddleware]
[TenantResolution]
[RequiresPermission("inventory")]
[HttpGet("returns")]
public async Task<ActionResult<ApiResponse<object>>> GetStockReturns()
=> Response(await _inventoryService.GetStockReturns(new { }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[RequiresPermission("inventory")]
[HttpGet("returns/{id:guid}")]
public async Task<ActionResult<ApiResponse<object>>> GetStockReturnById(Guid id)
=> Response(await _inventoryService.GetStockReturnById(new { stockReturnId = id }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[RequiresPermission("inventory")]
[HttpPost("returns")]
public async Task<ActionResult<ApiResponse<object>>> CreateStockReturn([FromBody] CreateStockReturnRequest request)
=> Response(await _inventoryService.CreateStockReturn(new
{
productId = request.ProductId,
quantity = request.Quantity,
category = request.Category,
saleId = request.SaleId,
grnId = request.GrnId,
notes = request.Notes,
returnedAt = request.ReturnedAt?.ToString("o")
}, HttpContext));
}
}