---
name: warehouse-management
description: >
  نظام إدارة المستودعات الحكومية مع الجرد والتتبع والتقارير.
  استخدم هذا الـ skill عند: بناء نظام مستودعات، إدارة المخزون، تتبع الأصناف،
  إنشاء أوامر صرف أو توريد، تصميم تقارير المخزون، إدارة المستودعات المتعددة،
  أو أي مهمة تتعلق بإدارة المستودعات. يشمل الباركود والـ Geofencing والبصمة.
---

# Warehouse Management System
# نظام إدارة المستودعات

## Domain Model

```csharp
// 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
```csharp
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
```csharp
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
```csharp
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
```csharp
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

```sql
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 | الأصناف الراكدة حسب المدة |
