name: warehouse-management
description: >
نظام إدارة المستودعات الحكومية مع الجرد والتتبع والتقارير.
استخدم هذا الـ skill عند: بناء نظام مستودعات، إدارة المخزون، تتبع الأصناف،
إنشاء أوامر صرف أو توريد، تصميم تقارير المخزون، إدارة المستودعات المتعددة،
أو أي مهمة تتعلق بإدارة المستودعات. يشمل الباركود والـ Geofencing والبصمة.
Warehouse Management System
نظام إدارة المستودعات
Domain Model
// Core Entities
public class Warehouse
{
public int Id { get; set; }
public string NameAr { get; set; } = string.Empty;
public string? NameEn { get; set; }
public string Code { get; set; } = string.Empty; // e.g., "WH-001"
public string? Location { get; set; }
public double? Latitude { get; set; }
public double? Longitude { get; set; }
public double? GeofenceRadius { get; set; } // meters
public int ManagerUserId { get; set; }
public bool IsActive { get; set; } = true;
public List<WarehouseSection> Sections { get; set; } = new();
}
public class WarehouseSection
{
public int Id { get; set; }
public int WarehouseId { get; set; }
public string NameAr { get; set; } = string.Empty;
public string Code { get; set; } = string.Empty; // e.g., "A-01"
public int Capacity { get; set; }
public int CurrentStock { get; set; }
}
public class Item
{
public int Id { get; set; }
public string NameAr { get; set; } = string.Empty;
public string? NameEn { get; set; }
public string SKU { get; set; } = string.Empty;
public string? Barcode { get; set; }
public int CategoryId { get; set; }
public int UnitOfMeasureId { get; set; }
public decimal? MinimumStock { get; set; }
public decimal? MaximumStock { get; set; }
public decimal? ReorderPoint { get; set; }
public bool IsConsumable { get; set; }
public bool RequiresSerialNumber { get; set; }
public ItemCategory Category { get; set; } = null!;
public UnitOfMeasure UnitOfMeasure { get; set; } = null!;
}
public class StockTransaction
{
public int Id { get; set; }
public string TransactionNumber { get; set; } = string.Empty; // Auto: "TRX-2024-001"
public TransactionType Type { get; set; }
public int ItemId { get; set; }
public int WarehouseId { get; set; }
public int? FromSectionId { get; set; }
public int? ToSectionId { get; set; }
public decimal Quantity { get; set; }
public decimal UnitCost { get; set; }
public decimal TotalCost { get; set; }
public string? ReferenceNumber { get; set; } // PO or SO number
public string? Notes { get; set; }
public int? ApprovalWorkflowId { get; set; }
public DateTime TransactionDate { get; set; }
public int CreatedBy { get; set; }
}
public enum TransactionType
{
Receipt, // توريد (استلام)
Issue, // صرف
Transfer, // تحويل بين مستودعات
Return, // إرجاع
Adjustment, // تسوية جرد
Disposal, // إتلاف
InitialStock // رصيد افتتاحي
}
// Inventory Count (الجرد)
public class InventoryCount
{
public int Id { get; set; }
public string CountNumber { get; set; } = string.Empty;
public int WarehouseId { get; set; }
public CountType Type { get; set; }
public CountStatus Status { get; set; }
public DateTime CountDate { get; set; }
public int SupervisorUserId { get; set; }
public List<InventoryCountDetail> Details { get; set; } = new();
}
public class InventoryCountDetail
{
public int Id { get; set; }
public int InventoryCountId { get; set; }
public int ItemId { get; set; }
public decimal SystemQuantity { get; set; } // الكمية في النظام
public decimal? ActualQuantity { get; set; } // الكمية الفعلية
public decimal Variance => (ActualQuantity ?? 0) - SystemQuantity; // الفرق
public string? Notes { get; set; }
public bool IsVerified { get; set; }
}
public enum CountType
{
Full, // جرد شامل
Partial, // جرد جزئي
Cycle, // جرد دوري
Spot // جرد مفاجئ
}
Key Features
1. Stock Tracking Service
public interface IStockService
{
Task<decimal> GetCurrentStockAsync(int itemId, int warehouseId);
Task<StockTransaction> ReceiveStockAsync(ReceiveStockCommand command);
Task<StockTransaction> IssueStockAsync(IssueStockCommand command);
Task<StockTransaction> TransferStockAsync(TransferStockCommand command);
Task<List<LowStockAlert>> GetLowStockAlertsAsync(int warehouseId);
Task<StockMovementReport> GetMovementReportAsync(int itemId, DateRange range);
}
public class StockService : IStockService
{
public async Task<StockTransaction> IssueStockAsync(IssueStockCommand command)
{
// Validate stock availability
var currentStock = await GetCurrentStockAsync(command.ItemId, command.WarehouseId);
if (currentStock < command.Quantity)
throw new InsufficientStockException(
$"الكمية المتاحة ({currentStock}) أقل من المطلوبة ({command.Quantity})");
// Check approval requirement
if (command.Quantity > _settings.AutoApproveLimit)
{
// Start approval workflow
var workflow = await _workflowEngine.StartWorkflowAsync(
"StockIssue", command.Id, command.RequestedBy);
return new StockTransaction { ApprovalWorkflowId = workflow.Id };
}
// Process issuance
var transaction = new StockTransaction
{
Type = TransactionType.Issue,
ItemId = command.ItemId,
WarehouseId = command.WarehouseId,
Quantity = -command.Quantity, // Negative for issues
TransactionDate = DateTime.UtcNow
};
_context.StockTransactions.Add(transaction);
await _context.SaveChangesAsync();
// Check reorder point
var newStock = currentStock - command.Quantity;
var item = await _context.Items.FindAsync(command.ItemId);
if (item?.ReorderPoint.HasValue == true && newStock <= item.ReorderPoint.Value)
{
await _notificationService.SendLowStockAlertAsync(item, newStock);
}
return transaction;
}
}
2. Barcode Integration
public interface IBarcodeService
{
Task<byte[]> GenerateBarcodeAsync(string data, BarcodeFormat format);
Task<string> ScanBarcodeAsync(byte[] imageData);
Task<Item?> LookupByBarcodeAsync(string barcode);
}
3. Geofencing for Warehouse Access
public class GeofenceService
{
public bool IsWithinWarehouse(double userLat, double userLng, Warehouse warehouse)
{
if (!warehouse.Latitude.HasValue || !warehouse.Longitude.HasValue)
return true; // No geofence configured
var distance = CalculateDistance(
userLat, userLng,
warehouse.Latitude.Value, warehouse.Longitude.Value);
return distance <= (warehouse.GeofenceRadius ?? 100); // Default 100m
}
private double CalculateDistance(double lat1, double lng1, double lat2, double lng2)
{
// Haversine formula
var R = 6371000; // Earth's radius in meters
var dLat = ToRad(lat2 - lat1);
var dLng = ToRad(lng2 - lng1);
var a = Math.Sin(dLat / 2) * Math.Sin(dLat / 2) +
Math.Cos(ToRad(lat1)) * Math.Cos(ToRad(lat2)) *
Math.Sin(dLng / 2) * Math.Sin(dLng / 2);
var c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
return R * c;
}
}
4. Biometric Authentication for Critical Operations
public interface IBiometricService
{
Task<bool> VerifyFingerprintAsync(int userId, byte[] fingerprintData);
Task<bool> VerifyForOperationAsync(int userId, string operationType);
}
// Operations requiring biometric:
// - Inventory count submission (تسليم الجرد)
// - High-value item issuance (صرف أصناف عالية القيمة)
// - Disposal approval (اعتماد الإتلاف)
// - Stock adjustment (تسوية الجرد)
SQL Schema
CREATE SCHEMA [Warehouse];
CREATE TABLE [Warehouse].[Warehouses] (
[Id] INT IDENTITY(1,1) PRIMARY KEY,
[NameAr] NVARCHAR(200) NOT NULL,
[NameEn] NVARCHAR(200) NULL,
[Code] NVARCHAR(20) NOT NULL UNIQUE,
[Location] NVARCHAR(500) NULL,
[Latitude] FLOAT NULL,
[Longitude] FLOAT NULL,
[GeofenceRadius] FLOAT NULL,
[ManagerUserId] INT NOT NULL,
[IsActive] BIT NOT NULL DEFAULT 1
);
CREATE TABLE [Warehouse].[Items] (
[Id] INT IDENTITY(1,1) PRIMARY KEY,
[NameAr] NVARCHAR(300) NOT NULL,
[NameEn] NVARCHAR(300) NULL,
[SKU] NVARCHAR(50) NOT NULL UNIQUE,
[Barcode] NVARCHAR(100) NULL,
[CategoryId] INT NOT NULL,
[UnitOfMeasureId] INT NOT NULL,
[MinimumStock] DECIMAL(18,2) NULL,
[MaximumStock] DECIMAL(18,2) NULL,
[ReorderPoint] DECIMAL(18,2) NULL,
[IsConsumable] BIT NOT NULL DEFAULT 1,
[IsActive] BIT NOT NULL DEFAULT 1
);
CREATE TABLE [Warehouse].[StockTransactions] (
[Id] INT IDENTITY(1,1) PRIMARY KEY,
[TransactionNumber] NVARCHAR(50) NOT NULL UNIQUE,
[Type] NVARCHAR(50) NOT NULL,
[ItemId] INT NOT NULL FOREIGN KEY REFERENCES [Warehouse].[Items]([Id]),
[WarehouseId] INT NOT NULL FOREIGN KEY REFERENCES [Warehouse].[Warehouses]([Id]),
[Quantity] DECIMAL(18,2) NOT NULL,
[UnitCost] DECIMAL(18,2) NOT NULL DEFAULT 0,
[TotalCost] DECIMAL(18,2) NOT NULL DEFAULT 0,
[ReferenceNumber] NVARCHAR(100) NULL,
[Notes] NVARCHAR(1000) NULL,
[TransactionDate] DATETIME2 NOT NULL,
[CreatedBy] INT NOT NULL,
[CreatedAt] DATETIME2 NOT NULL DEFAULT GETUTCDATE()
);
-- Current stock view (computed from transactions)
CREATE VIEW [Warehouse].[vw_CurrentStock] AS
SELECT
t.ItemId,
t.WarehouseId,
i.NameAr AS ItemNameAr,
i.SKU,
w.NameAr AS WarehouseNameAr,
SUM(t.Quantity) AS CurrentQuantity,
i.MinimumStock,
i.ReorderPoint,
CASE
WHEN SUM(t.Quantity) <= ISNULL(i.MinimumStock, 0) THEN N'منخفض'
WHEN SUM(t.Quantity) <= ISNULL(i.ReorderPoint, 0) THEN N'يحتاج طلب'
ELSE N'متوفر'
END AS StockStatus
FROM [Warehouse].[StockTransactions] t
JOIN [Warehouse].[Items] i ON i.Id = t.ItemId
JOIN [Warehouse].[Warehouses] w ON w.Id = t.WarehouseId
GROUP BY t.ItemId, t.WarehouseId, i.NameAr, i.SKU, w.NameAr,
i.MinimumStock, i.ReorderPoint;
-- Auto-generate transaction numbers
CREATE SEQUENCE [Warehouse].[TransactionNumberSeq]
START WITH 1 INCREMENT BY 1;
Reports
| التقرير |
Report |
الوصف |
| رصيد المخزون |
Stock Balance |
الكميات الحالية لكل صنف بكل مستودع |
| حركة الأصناف |
Item Movement |
جميع الحركات لصنف محدد خلال فترة |
| تقرير الجرد |
Inventory Count |
نتائج الجرد مع الفروقات |
| أصناف تحت الحد |
Low Stock |
الأصناف التي وصلت لحد إعادة الطلب |
| تقرير التوريدات |
Receipts Report |
جميع عمليات التوريد خلال فترة |
| تقرير الصرف |
Issues Report |
جميع عمليات الصرف خلال فترة |
| تقرير الإتلاف |
Disposal Report |
الأصناف المتلفة مع الأسباب |
| تقادم المخزون |
Stock Aging |
الأصناف الراكدة حسب المدة |