Implement Inventory Management Service with CRUD operations for warehouses and inventory items, stock operations, and bulk processing capabilities.
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 1m48s
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 1m48s
This commit is contained in:
+122
@@ -0,0 +1,122 @@
|
||||
using CMSMicroservice.Domain.Entities;
|
||||
using CMSMicroservice.Domain.Entities.DiscountShop;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
|
||||
namespace CMSMicroservice.Infrastructure.Persistence.Configurations;
|
||||
|
||||
/// <summary>
|
||||
/// تنظیمات Entity Framework برای موجودی کالا
|
||||
/// </summary>
|
||||
public class InventoryItemConfiguration : IEntityTypeConfiguration<InventoryItem>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<InventoryItem> builder)
|
||||
{
|
||||
// ========== تنظیمات پایه ==========
|
||||
builder.HasQueryFilter(i => !i.IsDeleted);
|
||||
builder.Ignore(entity => entity.DomainEvents);
|
||||
builder.HasKey(entity => entity.Id);
|
||||
builder.Property(entity => entity.Id).UseIdentityColumn();
|
||||
|
||||
// ========== فیلدهای اصلی ==========
|
||||
builder.Property(e => e.ProductType)
|
||||
.IsRequired()
|
||||
.HasConversion<int>();
|
||||
|
||||
builder.Property(e => e.Quantity)
|
||||
.IsRequired()
|
||||
.HasDefaultValue(0);
|
||||
|
||||
builder.Property(e => e.ReservedQuantity)
|
||||
.IsRequired()
|
||||
.HasDefaultValue(0);
|
||||
|
||||
// ========== تنظیمات انبار ==========
|
||||
builder.Property(e => e.LowStockThreshold)
|
||||
.IsRequired()
|
||||
.HasDefaultValue(10);
|
||||
|
||||
builder.Property(e => e.ReorderPoint)
|
||||
.IsRequired()
|
||||
.HasDefaultValue(5);
|
||||
|
||||
builder.Property(e => e.MaxStockLevel)
|
||||
.IsRequired()
|
||||
.HasDefaultValue(1000);
|
||||
|
||||
// ========== تاریخها ==========
|
||||
builder.Property(e => e.LastRestockedAt)
|
||||
.IsRequired(false);
|
||||
|
||||
builder.Property(e => e.LastSoldAt)
|
||||
.IsRequired(false);
|
||||
|
||||
// ========== انبار ==========
|
||||
builder.Property(e => e.WarehouseId)
|
||||
.IsRequired()
|
||||
.HasDefaultValue(1);
|
||||
|
||||
// ========== روابط ==========
|
||||
|
||||
// رابطه با Product (اختیاری)
|
||||
builder.HasOne(e => e.Product)
|
||||
.WithMany()
|
||||
.HasForeignKey(e => e.ProductId)
|
||||
.IsRequired(false)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
// رابطه با DiscountProduct (اختیاری)
|
||||
builder.HasOne(e => e.DiscountProduct)
|
||||
.WithMany()
|
||||
.HasForeignKey(e => e.DiscountProductId)
|
||||
.IsRequired(false)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
// رابطه با Warehouse
|
||||
builder.HasOne(e => e.Warehouse)
|
||||
.WithMany(w => w.InventoryItems)
|
||||
.HasForeignKey(e => e.WarehouseId)
|
||||
.IsRequired()
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
// ========== Index ها ==========
|
||||
|
||||
// Index روی ProductId برای جستجوی سریع محصولات معمولی
|
||||
builder.HasIndex(e => e.ProductId)
|
||||
.HasDatabaseName("IX_InventoryItems_ProductId");
|
||||
|
||||
// Index روی DiscountProductId برای جستجوی سریع محصولات تخفیفی
|
||||
builder.HasIndex(e => e.DiscountProductId)
|
||||
.HasDatabaseName("IX_InventoryItems_DiscountProductId");
|
||||
|
||||
// Index ترکیبی روی ProductType و Quantity برای گزارشگیری
|
||||
builder.HasIndex(e => new { e.ProductType, e.Quantity })
|
||||
.HasDatabaseName("IX_InventoryItems_ProductType_Quantity");
|
||||
|
||||
// Index ساده روی WarehouseId برای انبار
|
||||
builder.HasIndex(e => e.WarehouseId)
|
||||
.HasDatabaseName("IX_InventoryItems_WarehouseId");
|
||||
|
||||
// ========== محدودیتها ==========
|
||||
|
||||
// محدودیت: یا ProductId یا DiscountProductId باید پر باشد (نه هر دو، نه هیچکدام)
|
||||
builder.HasCheckConstraint("CK_InventoryItem_ProductReference",
|
||||
"(ProductId IS NOT NULL AND DiscountProductId IS NULL) OR (ProductId IS NULL AND DiscountProductId IS NOT NULL)");
|
||||
|
||||
// محدودیت: ProductType باید با نوع محصول مطابقت داشته باشد
|
||||
builder.HasCheckConstraint("CK_InventoryItem_ProductType_Match",
|
||||
"(ProductType = 1 AND ProductId IS NOT NULL) OR (ProductType = 2 AND DiscountProductId IS NOT NULL)");
|
||||
|
||||
// محدودیت: موجودی نمیتواند منفی باشد
|
||||
builder.HasCheckConstraint("CK_InventoryItem_Quantity_NonNegative",
|
||||
"Quantity >= 0");
|
||||
|
||||
// محدودیت: موجودی رزرو شده نمیتواند منفی باشد
|
||||
builder.HasCheckConstraint("CK_InventoryItem_ReservedQuantity_NonNegative",
|
||||
"ReservedQuantity >= 0");
|
||||
|
||||
// محدودیت: موجودی رزرو شده نمیتواند بیشتر از موجودی کل باشد
|
||||
builder.HasCheckConstraint("CK_InventoryItem_ReservedQuantity_LessOrEqualQuantity",
|
||||
"ReservedQuantity <= Quantity");
|
||||
}
|
||||
}
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
using CMSMicroservice.Domain.Entities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
|
||||
namespace CMSMicroservice.Infrastructure.Persistence.Configurations;
|
||||
|
||||
/// <summary>
|
||||
/// تنظیمات Entity Framework برای حرکات انبار
|
||||
/// </summary>
|
||||
public class StockMovementConfiguration : IEntityTypeConfiguration<StockMovement>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<StockMovement> builder)
|
||||
{
|
||||
// ========== تنظیمات پایه ==========
|
||||
builder.HasQueryFilter(s => !s.IsDeleted);
|
||||
builder.Ignore(entity => entity.DomainEvents);
|
||||
builder.HasKey(entity => entity.Id);
|
||||
builder.Property(entity => entity.Id).UseIdentityColumn();
|
||||
|
||||
// ========== فیلدهای اصلی ==========
|
||||
|
||||
// نوع حرکت (enum به int)
|
||||
builder.Property(e => e.MovementType)
|
||||
.IsRequired()
|
||||
.HasConversion<int>();
|
||||
|
||||
// مقدار تغییر (میتواند منفی باشد)
|
||||
builder.Property(e => e.Quantity)
|
||||
.IsRequired();
|
||||
|
||||
// موجودی قبل و بعد
|
||||
builder.Property(e => e.QuantityBefore)
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(e => e.QuantityAfter)
|
||||
.IsRequired();
|
||||
|
||||
// ========== فیلدهای اختیاری ==========
|
||||
|
||||
// شناسه سفارش معمولی
|
||||
builder.Property(e => e.OrderId)
|
||||
.IsRequired(false);
|
||||
|
||||
// شناسه سفارش تخفیفی
|
||||
builder.Property(e => e.DiscountOrderId)
|
||||
.IsRequired(false);
|
||||
|
||||
// شماره مرجع
|
||||
builder.Property(e => e.ReferenceNumber)
|
||||
.IsRequired(false)
|
||||
.HasMaxLength(100);
|
||||
|
||||
// یادداشت
|
||||
builder.Property(e => e.Note)
|
||||
.IsRequired(false)
|
||||
.HasMaxLength(500);
|
||||
|
||||
// کاربر انجامدهنده
|
||||
builder.Property(e => e.PerformedByUserId)
|
||||
.IsRequired(false);
|
||||
|
||||
// ========== روابط ==========
|
||||
|
||||
// رابطه با InventoryItem (اجباری)
|
||||
builder.HasOne(e => e.InventoryItem)
|
||||
.WithMany(i => i.StockMovements)
|
||||
.HasForeignKey(e => e.InventoryItemId)
|
||||
.IsRequired()
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
// ========== Index ها ==========
|
||||
|
||||
// Index روی InventoryItemId برای جستجوی حرکات یک آیتم
|
||||
builder.HasIndex(e => e.InventoryItemId)
|
||||
.HasDatabaseName("IX_StockMovements_InventoryItemId");
|
||||
|
||||
// Index روی MovementType برای فیلتر بر اساس نوع حرکت
|
||||
builder.HasIndex(e => e.MovementType)
|
||||
.HasDatabaseName("IX_StockMovements_MovementType");
|
||||
|
||||
// Index روی Created برای مرتبسازی زمانی
|
||||
builder.HasIndex(e => e.Created)
|
||||
.HasDatabaseName("IX_StockMovements_Created");
|
||||
|
||||
// Index ترکیبی برای گزارشگیری
|
||||
builder.HasIndex(e => new { e.InventoryItemId, e.MovementType, e.Created })
|
||||
.HasDatabaseName("IX_StockMovements_Item_Type_Date");
|
||||
|
||||
// Index روی OrderId برای ردیابی حرکات مرتبط با سفارش
|
||||
builder.HasIndex(e => e.OrderId)
|
||||
.HasDatabaseName("IX_StockMovements_OrderId")
|
||||
.HasFilter("[OrderId] IS NOT NULL");
|
||||
|
||||
// Index روی DiscountOrderId برای ردیابی حرکات مرتبط با سفارش تخفیفی
|
||||
builder.HasIndex(e => e.DiscountOrderId)
|
||||
.HasDatabaseName("IX_StockMovements_DiscountOrderId")
|
||||
.HasFilter("[DiscountOrderId] IS NOT NULL");
|
||||
|
||||
// Index روی ReferenceNumber برای جستجوی سریع با شماره مرجع
|
||||
builder.HasIndex(e => e.ReferenceNumber)
|
||||
.HasDatabaseName("IX_StockMovements_ReferenceNumber")
|
||||
.HasFilter("[ReferenceNumber] IS NOT NULL");
|
||||
|
||||
// ========== محدودیتها ==========
|
||||
|
||||
// محدودیت: QuantityAfter باید برابر QuantityBefore + Quantity باشد
|
||||
builder.HasCheckConstraint("CK_StockMovement_QuantityAfter_Calculation",
|
||||
"QuantityAfter = QuantityBefore + Quantity");
|
||||
|
||||
// محدودیت: QuantityBefore و QuantityAfter نمیتوانند منفی باشند
|
||||
builder.HasCheckConstraint("CK_StockMovement_QuantityBefore_NonNegative",
|
||||
"QuantityBefore >= 0");
|
||||
|
||||
builder.HasCheckConstraint("CK_StockMovement_QuantityAfter_NonNegative",
|
||||
"QuantityAfter >= 0");
|
||||
}
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
using CMSMicroservice.Domain.Entities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
|
||||
namespace CMSMicroservice.Infrastructure.Persistence.Configurations;
|
||||
|
||||
/// <summary>
|
||||
/// تنظیمات Entity Framework برای انبار
|
||||
/// </summary>
|
||||
public class WarehouseConfiguration : IEntityTypeConfiguration<Warehouse>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<Warehouse> builder)
|
||||
{
|
||||
// ========== تنظیمات پایه ==========
|
||||
builder.HasQueryFilter(w => !w.IsDeleted);
|
||||
builder.Ignore(entity => entity.DomainEvents);
|
||||
builder.HasKey(entity => entity.Id);
|
||||
builder.Property(entity => entity.Id).UseIdentityColumn();
|
||||
|
||||
// ========== فیلدهای اصلی ==========
|
||||
|
||||
// نام انبار (اجباری)
|
||||
builder.Property(e => e.Name)
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
// کد انبار (اجباری و یکتا)
|
||||
builder.Property(e => e.Code)
|
||||
.IsRequired()
|
||||
.HasMaxLength(50);
|
||||
|
||||
// آدرس انبار (اختیاری)
|
||||
builder.Property(e => e.Address)
|
||||
.IsRequired(false)
|
||||
.HasMaxLength(1000);
|
||||
|
||||
// ========== تنظیمات ==========
|
||||
|
||||
// انبار پیشفرض
|
||||
builder.Property(e => e.IsDefault)
|
||||
.IsRequired()
|
||||
.HasDefaultValue(false);
|
||||
|
||||
// وضعیت فعال/غیرفعال
|
||||
builder.Property(e => e.IsActive)
|
||||
.IsRequired()
|
||||
.HasDefaultValue(true);
|
||||
|
||||
// ========== Index ها ==========
|
||||
|
||||
// Index یکتا روی کد انبار
|
||||
builder.HasIndex(e => e.Code)
|
||||
.IsUnique()
|
||||
.HasDatabaseName("IX_Warehouses_Code_Unique");
|
||||
|
||||
// Index روی IsDefault برای پیدا کردن سریع انبار پیشفرض
|
||||
builder.HasIndex(e => e.IsDefault)
|
||||
.HasDatabaseName("IX_Warehouses_IsDefault")
|
||||
.HasFilter("[IsDefault] = 1");
|
||||
|
||||
// Index روی IsActive برای فیلتر انبارهای فعال
|
||||
builder.HasIndex(e => e.IsActive)
|
||||
.HasDatabaseName("IX_Warehouses_IsActive");
|
||||
|
||||
// ========== روابط ==========
|
||||
|
||||
// رابطه یک-به-چند با InventoryItems
|
||||
builder.HasMany(w => w.InventoryItems)
|
||||
.WithOne(i => i.Warehouse)
|
||||
.HasForeignKey(i => i.WarehouseId)
|
||||
.OnDelete(DeleteBehavior.Restrict); // جلوگیری از حذف انبار در صورت وجود موجودی
|
||||
|
||||
// ========== دادههای اولیه ==========
|
||||
|
||||
// انبار پیشفرض
|
||||
builder.HasData(new Warehouse
|
||||
{
|
||||
Id = 1,
|
||||
Name = "انبار اصلی",
|
||||
Code = "WH-001",
|
||||
Address = "تهران - انبار مرکزی فروشگاه",
|
||||
IsDefault = true,
|
||||
IsActive = true,
|
||||
Created = new DateTime(2026, 1, 1, 0, 0, 0, DateTimeKind.Utc),
|
||||
CreatedBy = "System",
|
||||
IsDeleted = false
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user