Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ dist/
.angular/
*.db
appsettings.Development.json
package-lock.json
14 changes: 7 additions & 7 deletions src/OrderManager.Api/Controllers/InventoryController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,32 @@ namespace OrderManager.Api.Controllers;
[Route("api/[controller]")]
public class InventoryController : ControllerBase
{
private readonly InventoryService _inventoryService;
private readonly InventoryHttpClient _inventoryClient;

public InventoryController(InventoryService inventoryService)
public InventoryController(InventoryHttpClient inventoryClient)
{
_inventoryService = inventoryService;
_inventoryClient = inventoryClient;
}

[HttpGet]
public async Task<IActionResult> GetAll() => Ok(await _inventoryService.GetAllInventoryAsync());
public async Task<IActionResult> GetAll() => Ok(await _inventoryClient.GetAllInventoryAsync());

[HttpGet("product/{productId}")]
public async Task<IActionResult> GetByProduct(int productId)
{
var item = await _inventoryService.GetInventoryByProductIdAsync(productId);
var item = await _inventoryClient.GetInventoryByProductIdAsync(productId);
return item is null ? NotFound() : Ok(item);
}

[HttpPost("product/{productId}/restock")]
public async Task<IActionResult> Restock(int productId, [FromBody] RestockRequest request)
{
var item = await _inventoryService.RestockAsync(productId, request.Quantity);
var item = await _inventoryClient.RestockAsync(productId, request.Quantity);
return Ok(item);
}

[HttpGet("low-stock")]
public async Task<IActionResult> GetLowStock() => Ok(await _inventoryService.GetLowStockItemsAsync());
public async Task<IActionResult> GetLowStock() => Ok(await _inventoryClient.GetLowStockItemsAsync());
}

public record RestockRequest(int Quantity);
7 changes: 0 additions & 7 deletions src/OrderManager.Api/Data/AppDbContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
public DbSet<Product> Products => Set<Product>();
public DbSet<Order> Orders => Set<Order>();
public DbSet<OrderItem> OrderItems => Set<OrderItem>();
public DbSet<InventoryItem> InventoryItems => Set<InventoryItem>();

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
Expand Down Expand Up @@ -46,11 +45,5 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
entity.HasOne(e => e.Product).WithMany(p => p.OrderItems).HasForeignKey(e => e.ProductId);
entity.Property(e => e.UnitPrice).HasColumnType("decimal(18,2)");
});

modelBuilder.Entity<InventoryItem>(entity =>
{
entity.HasKey(e => e.Id);
entity.HasOne(e => e.Product).WithOne(p => p.Inventory).HasForeignKey<InventoryItem>(e => e.ProductId);
});
}
}
10 changes: 0 additions & 10 deletions src/OrderManager.Api/Data/SeedData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,5 @@ public static void Initialize(AppDbContext context)
};
context.Products.AddRange(products);
context.SaveChanges();

var inventoryItems = products.Select((p, i) => new InventoryItem
{
ProductId = p.Id,
QuantityOnHand = (i + 1) * 50,
ReorderLevel = 10,
WarehouseLocation = $"A-{i + 1:D2}"
}).ToArray();
context.InventoryItems.AddRange(inventoryItems);
context.SaveChanges();
}
}
12 changes: 0 additions & 12 deletions src/OrderManager.Api/Models/InventoryItem.cs

This file was deleted.

1 change: 0 additions & 1 deletion src/OrderManager.Api/Models/Product.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,5 @@ public class Product
public decimal Price { get; set; }
public string Sku { get; set; } = string.Empty;
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public InventoryItem? Inventory { get; set; }
public ICollection<OrderItem> OrderItems { get; set; } = new List<OrderItem>();
}
8 changes: 7 additions & 1 deletion src/OrderManager.Api/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,16 @@
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlite(builder.Configuration.GetConnectionString("DefaultConnection") ?? "Data Source=ordermanager.db"));

builder.Services.AddHttpClient<InventoryHttpClient>(client =>
{
var baseUrl = builder.Configuration["InventoryService:BaseUrl"] ?? "http://localhost:5001";
client.BaseAddress = new Uri(baseUrl);
client.Timeout = TimeSpan.FromSeconds(30);
});

builder.Services.AddScoped<OrderService>();
builder.Services.AddScoped<ProductService>();
builder.Services.AddScoped<CustomerService>();
builder.Services.AddScoped<InventoryService>();

builder.Services.AddControllers().AddJsonOptions(options =>
options.JsonSerializerOptions.ReferenceHandler = System.Text.Json.Serialization.ReferenceHandler.IgnoreCycles);
Expand Down
62 changes: 62 additions & 0 deletions src/OrderManager.Api/Services/InventoryHttpClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using System.Net.Http.Json;

namespace OrderManager.Api.Services;

public class InventoryItemDto
{
public int Id { get; set; }
public int ProductId { get; set; }
public string ProductName { get; set; } = string.Empty;
public string Sku { get; set; } = string.Empty;
public int QuantityOnHand { get; set; }
public int ReorderLevel { get; set; }
public string WarehouseLocation { get; set; } = string.Empty;
public DateTime LastRestocked { get; set; }
}

public class InventoryHttpClient
{
private readonly HttpClient _httpClient;

public InventoryHttpClient(HttpClient httpClient)
{
_httpClient = httpClient;
}

public async Task<List<InventoryItemDto>> GetAllInventoryAsync()
{
var items = await _httpClient.GetFromJsonAsync<List<InventoryItemDto>>("api/inventory");
return items ?? new List<InventoryItemDto>();
}

public async Task<InventoryItemDto?> GetInventoryByProductIdAsync(int productId)
{
var response = await _httpClient.GetAsync($"api/inventory/product/{productId}");
if (!response.IsSuccessStatusCode) return null;
return await response.Content.ReadFromJsonAsync<InventoryItemDto>();
}

public async Task<InventoryItemDto?> RestockAsync(int productId, int quantity)
{
var response = await _httpClient.PostAsJsonAsync(
$"api/inventory/product/{productId}/restock",
new { quantity });
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<InventoryItemDto>();
}

public async Task<List<InventoryItemDto>> GetLowStockItemsAsync()
{
var items = await _httpClient.GetFromJsonAsync<List<InventoryItemDto>>("api/inventory/low-stock");
return items ?? new List<InventoryItemDto>();
}

public async Task<InventoryItemDto?> DeductStockAsync(int productId, int quantity)
{
var response = await _httpClient.PostAsJsonAsync(
$"api/inventory/product/{productId}/deduct",
new { quantity });
if (!response.IsSuccessStatusCode) return null;
return await response.Content.ReadFromJsonAsync<InventoryItemDto>();
}
}
43 changes: 0 additions & 43 deletions src/OrderManager.Api/Services/InventoryService.cs

This file was deleted.

13 changes: 5 additions & 8 deletions src/OrderManager.Api/Services/OrderService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ namespace OrderManager.Api.Services;
public class OrderService
{
private readonly AppDbContext _context;
private readonly InventoryHttpClient _inventoryClient;

public OrderService(AppDbContext context)
public OrderService(AppDbContext context, InventoryHttpClient inventoryClient)
{
_context = context;
_inventoryClient = inventoryClient;
}

public async Task<List<Order>> GetAllOrdersAsync()
Expand Down Expand Up @@ -46,13 +48,8 @@ public async Task<Order> CreateOrderAsync(int customerId, List<(int ProductId, i
var product = await _context.Products.FindAsync(productId)
?? throw new ArgumentException($"Product {productId} not found");

var inventory = await _context.InventoryItems.FirstOrDefaultAsync(i => i.ProductId == productId)
?? throw new InvalidOperationException($"No inventory record for product {productId}");

if (inventory.QuantityOnHand < quantity)
throw new InvalidOperationException($"Insufficient stock for {product.Name}. Available: {inventory.QuantityOnHand}");

inventory.QuantityOnHand -= quantity;
var deducted = await _inventoryClient.DeductStockAsync(productId, quantity)
?? throw new InvalidOperationException($"Insufficient stock for {product.Name}");

order.Items.Add(new OrderItem
{
Expand Down
6 changes: 3 additions & 3 deletions src/OrderManager.Api/Services/ProductService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ public ProductService(AppDbContext context)

public async Task<List<Product>> GetAllProductsAsync()
{
return await _context.Products.Include(p => p.Inventory).ToListAsync();
return await _context.Products.ToListAsync();
}

public async Task<Product?> GetProductByIdAsync(int id)
{
return await _context.Products.Include(p => p.Inventory).FirstOrDefaultAsync(p => p.Id == id);
return await _context.Products.FirstOrDefaultAsync(p => p.Id == id);
}

public async Task<Product> CreateProductAsync(Product product)
Expand All @@ -32,6 +32,6 @@ public async Task<Product> CreateProductAsync(Product product)

public async Task<List<Product>> GetProductsByCategoryAsync(string category)
{
return await _context.Products.Where(p => p.Category == category).Include(p => p.Inventory).ToListAsync();
return await _context.Products.Where(p => p.Category == category).ToListAsync();
}
}
3 changes: 3 additions & 0 deletions src/OrderManager.Api/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,8 @@
"ConnectionStrings": {
"DefaultConnection": "Data Source=ordermanager.db"
},
"InventoryService": {
"BaseUrl": "http://localhost:5001"
},
"AllowedHosts": "*"
}
Loading