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

# Financial Management System
# نظام الإدارة المالية

## Domain Model

```csharp
// === شجرة الحسابات ===
public class ChartOfAccount
{
    public int Id { get; set; }
    public string AccountCode { get; set; } = string.Empty;   // 1-01-001
    public string NameAr { get; set; } = string.Empty;
    public string? NameEn { get; set; }
    public int? ParentAccountId { get; set; }
    public AccountType Type { get; set; }
    public AccountNature Nature { get; set; }        // مدين/دائن
    public int Level { get; set; }                   // مستوى الحساب
    public bool IsPostable { get; set; }             // قابل للترحيل
    public bool IsActive { get; set; } = true;
    public int? CostCenterId { get; set; }
}

public enum AccountType
{
    Asset,              // أصول
    Liability,          // التزامات (خصوم)
    Equity,             // حقوق ملكية
    Revenue,            // إيرادات
    Expense             // مصروفات
}

public enum AccountNature { Debit, Credit }

// === القيود المحاسبية ===
public class JournalEntry : BaseAuditableEntity
{
    public int Id { get; set; }
    public string EntryNumber { get; set; } = string.Empty;    // QYD-2024-0001
    public DateTime EntryDate { get; set; }
    public string DescriptionAr { get; set; } = string.Empty;
    public JournalEntryType Type { get; set; }
    public JournalEntryStatus Status { get; set; }
    public int FiscalYearId { get; set; }
    public int FiscalPeriodId { get; set; }
    public string? ReferenceNumber { get; set; }
    public string? SourceDocument { get; set; }      // نوع المستند المصدر
    public int? SourceDocumentId { get; set; }
    public decimal TotalDebit { get; set; }
    public decimal TotalCredit { get; set; }
    public int? ApprovalWorkflowId { get; set; }
    public List<JournalEntryLine> Lines { get; set; } = new();
}

public class JournalEntryLine
{
    public int Id { get; set; }
    public int JournalEntryId { get; set; }
    public int AccountId { get; set; }
    public int? CostCenterId { get; set; }
    public decimal DebitAmount { get; set; }
    public decimal CreditAmount { get; set; }
    public string? DescriptionAr { get; set; }
    public string? AnalyticalCode { get; set; }
    public ChartOfAccount Account { get; set; } = null!;
}

public enum JournalEntryType
{
    Standard,           // قيد عادي
    Opening,            // قيد افتتاحي
    Closing,            // قيد إقفال
    Adjusting,          // قيد تسوية
    Reversing           // قيد عكسي
}

// === الميزانية ===
public class Budget : BaseAuditableEntity
{
    public int Id { get; set; }
    public string BudgetNumber { get; set; } = string.Empty;
    public string NameAr { get; set; } = string.Empty;
    public int FiscalYearId { get; set; }
    public int DepartmentId { get; set; }
    public BudgetStatus Status { get; set; }
    public decimal TotalAmount { get; set; }
    public decimal AllocatedAmount { get; set; }
    public decimal SpentAmount { get; set; }
    public decimal RemainingAmount => AllocatedAmount - SpentAmount;
    public decimal UtilizationRate => AllocatedAmount > 0 
        ? (SpentAmount / AllocatedAmount) * 100 : 0;
    public List<BudgetLine> Lines { get; set; } = new();
}

public class BudgetLine
{
    public int Id { get; set; }
    public int BudgetId { get; set; }
    public int AccountId { get; set; }
    public int? CostCenterId { get; set; }
    public string ItemNameAr { get; set; } = string.Empty;
    public decimal PlannedAmount { get; set; }       // المخطط
    public decimal AllocatedAmount { get; set; }     // المعتمد
    public decimal CommittedAmount { get; set; }     // الملتزم به
    public decimal ActualAmount { get; set; }        // الفعلي
    public decimal AvailableBalance => AllocatedAmount - CommittedAmount - ActualAmount;
}

// === أوامر الصرف والدفع ===
public class PaymentOrder : BaseAuditableEntity
{
    public int Id { get; set; }
    public string OrderNumber { get; set; } = string.Empty;    // PO-2024-0001
    public PaymentOrderType Type { get; set; }
    public int BeneficiaryId { get; set; }
    public string BeneficiaryNameAr { get; set; } = string.Empty;
    public string? BeneficiaryIBAN { get; set; }
    public decimal Amount { get; set; }
    public string PurposeAr { get; set; } = string.Empty;
    public int BudgetLineId { get; set; }
    public PaymentStatus Status { get; set; }
    public int? InvoiceId { get; set; }
    public int? PurchaseOrderId { get; set; }
    public int? ApprovalWorkflowId { get; set; }
    public DateTime? PaymentDate { get; set; }
    public string? BankReference { get; set; }
    public List<PaymentOrderAttachment> Attachments { get; set; } = new();
}

public enum PaymentOrderType
{
    Supplier,           // مورد
    Employee,           // موظف (سُلف/عهد)
    Contractor,         // مقاول
    Refund,             // استرداد
    Other               // أخرى
}

// === العهد والسُلف ===
public class FinancialCustody : BaseAuditableEntity
{
    public int Id { get; set; }
    public string CustodyNumber { get; set; } = string.Empty;
    public int EmployeeId { get; set; }
    public CustodyType Type { get; set; }
    public decimal Amount { get; set; }
    public decimal SettledAmount { get; set; }
    public decimal RemainingAmount => Amount - SettledAmount;
    public string PurposeAr { get; set; } = string.Empty;
    public DateTime IssueDate { get; set; }
    public DateTime DueDate { get; set; }
    public CustodyStatus Status { get; set; }
    public List<CustodySettlement> Settlements { get; set; } = new();
}

public enum CustodyType
{
    PermanentCustody,    // عهدة دائمة
    TemporaryCustody,    // عهدة مؤقتة
    Advance,             // سلفة
    PettyCash            // صندوق نثريات
}

// === الفواتير ===
public class Invoice : BaseAuditableEntity
{
    public int Id { get; set; }
    public string InvoiceNumber { get; set; } = string.Empty;
    public InvoiceType Type { get; set; }
    public int? SupplierId { get; set; }
    public DateTime InvoiceDate { get; set; }
    public DateTime? DueDate { get; set; }
    public decimal SubTotal { get; set; }
    public decimal VatAmount { get; set; }           // ضريبة القيمة المضافة 15%
    public decimal TotalAmount { get; set; }
    public InvoiceStatus Status { get; set; }
    public string? ZatcaQrCode { get; set; }         // رمز QR هيئة الزكاة
    public string? ZatcaInvoiceHash { get; set; }
    public List<InvoiceLine> Lines { get; set; } = new();
}
```

## Financial Services

```csharp
public interface IFinancialService
{
    // القيود
    Task<JournalEntry> CreateJournalEntryAsync(CreateJournalEntryCommand cmd);
    Task PostJournalEntryAsync(int entryId);
    Task ReverseJournalEntryAsync(int entryId, string reason);
    
    // الميزانية
    Task<bool> CheckBudgetAvailabilityAsync(int budgetLineId, decimal amount);
    Task CommitBudgetAsync(int budgetLineId, decimal amount, string reference);
    Task ReleaseBudgetCommitmentAsync(int budgetLineId, decimal amount);
    
    // التقارير
    Task<TrialBalance> GenerateTrialBalanceAsync(int fiscalYearId, DateTime? asOfDate);
    Task<IncomeStatement> GenerateIncomeStatementAsync(int fiscalYearId, int periodId);
    Task<BalanceSheet> GenerateBalanceSheetAsync(int fiscalYearId, DateTime asOfDate);
    Task<BudgetVsActual> GenerateBudgetComparisonAsync(int budgetId);
}

public class FinancialService : IFinancialService
{
    public async Task<JournalEntry> CreateJournalEntryAsync(CreateJournalEntryCommand cmd)
    {
        // Validate: Total Debit = Total Credit
        var totalDebit = cmd.Lines.Sum(l => l.DebitAmount);
        var totalCredit = cmd.Lines.Sum(l => l.CreditAmount);
        
        if (totalDebit != totalCredit)
            throw new ValidationException(
                $"القيد غير متوازن: مدين {totalDebit:N2} ≠ دائن {totalCredit:N2}");

        // Validate fiscal period is open
        var period = await _context.FiscalPeriods.FindAsync(cmd.FiscalPeriodId);
        if (period?.Status != PeriodStatus.Open)
            throw new ValidationException("الفترة المالية مغلقة");

        // Validate budget availability for expense accounts
        foreach (var line in cmd.Lines.Where(l => l.DebitAmount > 0))
        {
            var account = await _context.ChartOfAccounts.FindAsync(line.AccountId);
            if (account?.Type == AccountType.Expense)
            {
                var hasbudget = await CheckBudgetAvailabilityAsync(
                    line.BudgetLineId!.Value, line.DebitAmount);
                if (!hasbudget)
                    throw new InsufficientBudgetException(
                        $"لا يوجد رصيد كافٍ في بند الميزانية للحساب {account.NameAr}");
            }
        }

        // Create entry
        var entry = _mapper.Map<JournalEntry>(cmd);
        entry.TotalDebit = totalDebit;
        entry.TotalCredit = totalCredit;
        entry.Status = JournalEntryStatus.Draft;
        
        _context.JournalEntries.Add(entry);
        await _context.SaveChangesAsync();
        return entry;
    }
}
```

## ZATCA (هيئة الزكاة والضريبة) Integration

```csharp
public interface IZatcaService
{
    Task<string> GenerateQrCodeAsync(Invoice invoice);
    Task<ZatcaResponse> SubmitInvoiceAsync(Invoice invoice);
    Task<ZatcaResponse> ValidateInvoiceAsync(Invoice invoice);
}

// VAT calculation
public static class VatCalculator
{
    public const decimal VatRate = 0.15m;  // 15%
    
    public static decimal CalculateVat(decimal amount) => amount * VatRate;
    public static decimal CalculateAmountWithVat(decimal amount) => amount * (1 + VatRate);
    public static decimal ExtractVatFromTotal(decimal total) => total - (total / (1 + VatRate));
}
```

## SQL Schema

```sql
CREATE SCHEMA [Finance];

CREATE TABLE [Finance].[ChartOfAccounts] (
    [Id] INT IDENTITY(1,1) PRIMARY KEY,
    [AccountCode] NVARCHAR(20) NOT NULL UNIQUE,
    [NameAr] NVARCHAR(200) NOT NULL,
    [ParentAccountId] INT NULL FOREIGN KEY REFERENCES [Finance].[ChartOfAccounts]([Id]),
    [Type] NVARCHAR(20) NOT NULL,
    [Nature] NVARCHAR(10) NOT NULL,
    [Level] INT NOT NULL,
    [IsPostable] BIT NOT NULL DEFAULT 0,
    [IsActive] BIT NOT NULL DEFAULT 1
);

CREATE TABLE [Finance].[JournalEntries] (
    [Id] INT IDENTITY(1,1) PRIMARY KEY,
    [EntryNumber] NVARCHAR(50) NOT NULL UNIQUE,
    [EntryDate] DATE NOT NULL,
    [DescriptionAr] NVARCHAR(500) NOT NULL,
    [Type] NVARCHAR(20) NOT NULL,
    [Status] NVARCHAR(20) NOT NULL DEFAULT 'Draft',
    [FiscalYearId] INT NOT NULL,
    [FiscalPeriodId] INT NOT NULL,
    [TotalDebit] DECIMAL(18,2) NOT NULL,
    [TotalCredit] DECIMAL(18,2) NOT NULL,
    [CreatedBy] INT NOT NULL,
    [CreatedAt] DATETIME2 NOT NULL DEFAULT GETUTCDATE(),
    CONSTRAINT CK_JournalEntry_Balanced CHECK ([TotalDebit] = [TotalCredit])
);

CREATE TABLE [Finance].[JournalEntryLines] (
    [Id] INT IDENTITY(1,1) PRIMARY KEY,
    [JournalEntryId] INT NOT NULL FOREIGN KEY REFERENCES [Finance].[JournalEntries]([Id]),
    [AccountId] INT NOT NULL FOREIGN KEY REFERENCES [Finance].[ChartOfAccounts]([Id]),
    [CostCenterId] INT NULL,
    [DebitAmount] DECIMAL(18,2) NOT NULL DEFAULT 0,
    [CreditAmount] DECIMAL(18,2) NOT NULL DEFAULT 0,
    [DescriptionAr] NVARCHAR(500) NULL
);

CREATE TABLE [Finance].[Budgets] (
    [Id] INT IDENTITY(1,1) PRIMARY KEY,
    [BudgetNumber] NVARCHAR(50) NOT NULL UNIQUE,
    [NameAr] NVARCHAR(200) NOT NULL,
    [FiscalYearId] INT NOT NULL,
    [DepartmentId] INT NOT NULL,
    [Status] NVARCHAR(20) NOT NULL DEFAULT 'Draft',
    [TotalAmount] DECIMAL(18,2) NOT NULL
);

-- مراكز التكلفة
CREATE TABLE [Finance].[CostCenters] (
    [Id] INT IDENTITY(1,1) PRIMARY KEY,
    [Code] NVARCHAR(20) NOT NULL UNIQUE,
    [NameAr] NVARCHAR(200) NOT NULL,
    [ParentId] INT NULL,
    [DepartmentId] INT NULL,
    [IsActive] BIT NOT NULL DEFAULT 1
);

-- ميزان المراجعة (View)
CREATE VIEW [Finance].[vw_TrialBalance] AS
SELECT 
    a.AccountCode, a.NameAr, a.Type, a.Nature,
    ISNULL(SUM(l.DebitAmount), 0) AS TotalDebit,
    ISNULL(SUM(l.CreditAmount), 0) AS TotalCredit,
    ISNULL(SUM(l.DebitAmount), 0) - ISNULL(SUM(l.CreditAmount), 0) AS Balance
FROM [Finance].[ChartOfAccounts] a
LEFT JOIN [Finance].[JournalEntryLines] l ON l.AccountId = a.Id
LEFT JOIN [Finance].[JournalEntries] e ON e.Id = l.JournalEntryId AND e.Status = 'Posted'
WHERE a.IsPostable = 1
GROUP BY a.AccountCode, a.NameAr, a.Type, a.Nature;
```

## Reports

| التقرير | الوصف |
|---------|--------|
| ميزان المراجعة | أرصدة جميع الحسابات |
| قائمة الدخل | الإيرادات والمصروفات |
| الميزانية العمومية | الأصول والالتزامات |
| الميزانية مقابل الفعلي | مقارنة المخطط بالمنفذ |
| كشف حساب | حركات حساب محدد |
| تقرير العهد | العهد المعلقة والمسواة |
| تقرير أعمار الذمم | الفواتير المستحقة حسب العمر |
| تقرير مراكز التكلفة | المصروفات حسب مركز التكلفة |
| تقرير الضريبة (VAT) | إقرار ضريبة القيمة المضافة |
