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.
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
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));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user