
' ============================================
' Module General - الدوال المساعدة العامة
' ============================================

Option Explicit

' ============================================
' دوال قاعدة البيانات
' ============================================

' الحصول على آخر صف في الجدول
Public Function GetLastRow(ws As Worksheet, Optional col As Long = 1) As Long
    On Error Resume Next
    GetLastRow = ws.Cells(ws.Rows.count, col).End(xlUp).Row
    If GetLastRow < 2 Then GetLastRow = 2
    On Error GoTo 0
End Function

' الحصول على آخر صف في الجدول المحدد
Public Function GetTableLastRow(tableName As String) As Long
    Dim ws As Worksheet
    Dim tbl As ListObject
    Dim lastRow As Long
    
    On Error Resume Next
    Set tbl = GetTableByName(tableName)
    If Not tbl Is Nothing Then
        lastRow = tbl.ListRows.count + 1
        If lastRow < 2 Then lastRow = 2
    Else
        lastRow = 2
    End If
    GetTableLastRow = lastRow
    On Error GoTo 0
End Function

' الحصول على الجدول بالاسم
Public Function GetTableByName(tableName As String) As ListObject
    Dim ws As Worksheet
    Dim tbl As ListObject
    
    On Error Resume Next
    For Each ws In ThisWorkbook.Worksheets
        For Each tbl In ws.ListObjects
            If UCase(tbl.Name) = UCase(tableName) Then
                Set GetTableByName = tbl
                Exit Function
            End If
        Next tbl
    Next ws
    Set GetTableByName = Nothing
    On Error GoTo 0
End Function

' الحصول على ورقة العمل بالاسم
Public Function GetWorksheetByName(sheetName As String) As Worksheet
    On Error Resume Next
    Set GetWorksheetByName = ThisWorkbook.Worksheets(sheetName)
    On Error GoTo 0
End Function

' إضافة صف جديد للجدول
Public Function AddRowToTable(tableName As String, values As Variant) As Long
    Dim tbl As ListObject
    Dim newRow As ListRow
    Dim i As Long
    
    Set tbl = GetTableByName(tableName)
    If tbl Is Nothing Then Exit Function
    
    Set newRow = tbl.ListRows.Add
    For i = LBound(values) To UBound(values)
        If i <= tbl.ListColumns.count - 1 Then
            newRow.Range(i + 1) = values(i)
        End If
    Next i
    
    AddRowToTable = newRow.Index
End Function

' تحديث صف في الجدول
Public Sub UpdateRowInTable(tableName As String, rowIndex As Long, values As Variant)
    Dim tbl As ListObject
    Dim i As Long
    
    Set tbl = GetTableByName(tableName)
    If tbl Is Nothing Then Exit Sub
    If rowIndex < 1 Or rowIndex > tbl.ListRows.count Then Exit Sub
    
    For i = LBound(values) To UBound(values)
        If i <= tbl.ListColumns.count - 1 Then
            tbl.ListRows(rowIndex).Range(i + 1) = values(i)
        End If
    Next i
End Sub

' حذف صف من الجدول
Public Sub DeleteRowFromTable(tableName As String, rowIndex As Long)
    Dim tbl As ListObject
    
    Set tbl = GetTableByName(tableName)
    If tbl Is Nothing Then Exit Sub
    If rowIndex < 1 Or rowIndex > tbl.ListRows.count Then Exit Sub
    
    tbl.ListRows(rowIndex).Delete
End Sub

' ============================================
' دوال المنتجات
' ============================================

' الحصول على بيانات المنتج برمزه
Public Function GetProductByCode(productCode As String) As Variant
    Dim tbl As ListObject
    Dim rng As Range
    Dim i As Long
    Dim result(1 To 11) As Variant
    
    Set tbl = GetTableByName("tlbproduct")
    If tbl Is Nothing Then Exit Function
    
    For i = 1 To tbl.ListRows.count
        If UCase(Trim(tbl.ListRows(i).Range(1).Value)) = UCase(Trim(productCode)) Then
            result(1) = tbl.ListRows(i).Range(1).Value  ' رمز الصنف
            result(2) = tbl.ListRows(i).Range(2).Value  ' اسم الصنف
            result(3) = tbl.ListRows(i).Range(3).Value  ' الوحدة
            result(4) = tbl.ListRows(i).Range(4).Value  ' سعر الشراء
            result(5) = tbl.ListRows(i).Range(5).Value  ' سعر البيع
            result(6) = tbl.ListRows(i).Range(6).Value  ' تاريخ الإضافة
            result(7) = tbl.ListRows(i).Range(7).Value  ' حالة المنتج
            result(8) = tbl.ListRows(i).Range(8).Value  ' تاريخ آخر تحديث
            result(9) = tbl.ListRows(i).Range(9).Value  ' آخر سعر شراء
            result(10) = tbl.ListRows(i).Range(10).Value ' آخر سعر بيع
            result(11) = tbl.ListRows(i).Range(11).Value ' ملاحظات
            GetProductByCode = result
            Exit Function
        End If
    Next i
    
    GetProductByCode = Empty
End Function

' الحصول على بيانات المنتج باسمه
Public Function GetProductByName(productName As String) As Variant
    Dim tbl As ListObject
    Dim i As Long
    Dim result(1 To 11) As Variant
    
    Set tbl = GetTableByName("tlbproduct")
    If tbl Is Nothing Then Exit Function
    
    For i = 1 To tbl.ListRows.count
        If UCase(Trim(tbl.ListRows(i).Range(2).Value)) = UCase(Trim(productName)) Then
            result(1) = tbl.ListRows(i).Range(1).Value
            result(2) = tbl.ListRows(i).Range(2).Value
            result(3) = tbl.ListRows(i).Range(3).Value
            result(4) = tbl.ListRows(i).Range(4).Value
            result(5) = tbl.ListRows(i).Range(5).Value
            result(6) = tbl.ListRows(i).Range(6).Value
            result(7) = tbl.ListRows(i).Range(7).Value
            result(8) = tbl.ListRows(i).Range(8).Value
            result(9) = tbl.ListRows(i).Range(9).Value
            result(10) = tbl.ListRows(i).Range(10).Value
            result(11) = tbl.ListRows(i).Range(11).Value
            GetProductByName = result
            Exit Function
        End If
    Next i
    
    GetProductByName = Empty
End Function

' ============================================
' دوال المخزون
' ============================================

' الحصول على المخزون الحالي للصنف
Public Function GetCurrentStock(productCode As String) As Double
    Dim tbl As ListObject
    Dim i As Long
    
    Set tbl = GetTableByName("tlbInventory")
    If tbl Is Nothing Then
        GetCurrentStock = 0
        Exit Function
    End If
    
    For i = 1 To tbl.ListRows.count
        If UCase(Trim(tbl.ListRows(i).Range(1).Value)) = UCase(Trim(productCode)) Then
            GetCurrentStock = CDbl(tbl.ListRows(i).Range(7).Value) ' المخزون الحالي
            Exit Function
        End If
    Next i
    
    GetCurrentStock = 0
End Function

' الحصول على متوسط التكلفة (Average Cost) للصنف
Public Function GetAverageCost(productCode As String) As Double
    Dim tbl As ListObject
    Dim i As Long
    
    Set tbl = GetTableByName("tlbInventory")
    If tbl Is Nothing Then
        GetAverageCost = 0
        Exit Function
    End If
    
    For i = 1 To tbl.ListRows.count
        If UCase(Trim(tbl.ListRows(i).Range(1).Value)) = UCase(Trim(productCode)) Then
            ' محاولة الحصول من العمود 12 (Average Cost)، وإلا من العمود 4
            If tbl.ListColumns.count >= 12 And IsNumeric(tbl.ListRows(i).Range(12).Value) Then
                GetAverageCost = CDbl(tbl.ListRows(i).Range(12).Value)
            ElseIf IsNumeric(tbl.ListRows(i).Range(4).Value) Then
                GetAverageCost = CDbl(tbl.ListRows(i).Range(4).Value)
            Else
                GetAverageCost = 0
            End If
            Exit Function
        End If
    Next i
    
    GetAverageCost = 0
End Function

' تحديث المخزون مع حساب Average Cost
Public Sub UpdateInventory(productCode As String, qtyIn As Double, qtyOut As Double, Optional purchasePrice As Double = 0, Optional salePrice As Double = 0)
    Dim tbl As ListObject
    Dim i As Long
    Dim found As Boolean
    Dim currentStock As Double
    Dim currentValue As Double
    Dim newStock As Double
    Dim avgCost As Double
    Dim currentAvgCost As Double
    Dim totalCost As Double
    Dim totalQty As Double
    
    Set tbl = GetTableByName("tlbInventory")
    If tbl Is Nothing Then Exit Sub
    
    found = False
    For i = 1 To tbl.ListRows.count
        If UCase(Trim(tbl.ListRows(i).Range(1).Value)) = UCase(Trim(productCode)) Then
            found = True
            ' تحديث الكميات
            currentStock = CDbl(tbl.ListRows(i).Range(7).Value)
            tbl.ListRows(i).Range(5).Value = CDbl(tbl.ListRows(i).Range(5).Value) + qtyIn  ' الكمية الواردة
            tbl.ListRows(i).Range(6).Value = CDbl(tbl.ListRows(i).Range(6).Value) + qtyOut ' الكمية الصادرة
            newStock = currentStock + qtyIn - qtyOut
            tbl.ListRows(i).Range(7).Value = newStock ' المخزون الحالي
            
            ' حساب Average Cost
            ' الحصول على متوسط التكلفة الحالي (من العمود 12 إذا كان موجوداً، وإلا من العمود 4)
            If tbl.ListColumns.count >= 12 And IsNumeric(tbl.ListRows(i).Range(12).Value) Then
                currentAvgCost = CDbl(tbl.ListRows(i).Range(12).Value)
            ElseIf IsNumeric(tbl.ListRows(i).Range(4).Value) Then
                currentAvgCost = CDbl(tbl.ListRows(i).Range(4).Value)
            Else
                currentAvgCost = 0
            End If
            
            ' إذا كان هناك وارد بسعر شراء
            If qtyIn > 0 And purchasePrice > 0 Then
                If currentStock > 0 Then
                    ' حساب متوسط التكلفة: (الرصيد الحالي × متوسط التكلفة الحالي + الكمية الواردة × سعر الشراء) / (الرصيد الحالي + الكمية الواردة)
                    totalCost = (currentStock * currentAvgCost) + (qtyIn * purchasePrice)
                    totalQty = currentStock + qtyIn
                    avgCost = IIf(totalQty > 0, totalCost / totalQty, purchasePrice)
                Else
                    ' إذا لم يكن هناك رصيد، متوسط التكلفة = سعر الشراء
                    avgCost = purchasePrice
                End If
            Else
                ' عند الصادر، متوسط التكلفة يبقى كما هو
                avgCost = currentAvgCost
            End If
            
            ' حفظ متوسط التكلفة (في العمود 12 إذا كان موجوداً، وإلا في العمود 4)
            If tbl.ListColumns.count >= 12 Then
                tbl.ListRows(i).Range(12).Value = avgCost
            Else
                tbl.ListRows(i).Range(4).Value = avgCost
            End If
            
            ' تحديث قيمة المخزون باستخدام Average Cost
            currentValue = newStock * avgCost
            tbl.ListRows(i).Range(8).Value = currentValue
            
            ' تحديث آخر حركة (العمود 9)
            tbl.ListRows(i).Range(9).Value = Date
            
            ' تحديث الحالة (العمود 11)
            UpdateInventoryStatus i
            
            Exit For
        End If
    Next i
    
    ' إذا لم يوجد، إنشاء سجل جديد
    If Not found Then
        Dim productData As Variant
        productData = GetProductByCode(productCode)
        If Not IsEmpty(productData) Then
            Dim newRow As ListRow
            Set newRow = tbl.ListRows.Add
            newRow.Range(1) = productCode
            newRow.Range(2) = productData(2) ' اسم الصنف
            newRow.Range(3) = 0 ' المخزون أول الفترة
            newRow.Range(4) = IIf(purchasePrice > 0, purchasePrice, CDbl(productData(4))) ' متوسط التكلفة أو سعر الشراء
            newRow.Range(5) = qtyIn ' الكمية الواردة
            newRow.Range(6) = qtyOut ' الكمية الصادرة
            newRow.Range(7) = qtyIn - qtyOut ' المخزون الحالي
            If tbl.ListColumns.count >= 12 Then
                newRow.Range(12) = IIf(purchasePrice > 0, purchasePrice, CDbl(productData(4))) ' Average Cost
            End If
            newRow.Range(8) = (qtyIn - qtyOut) * IIf(purchasePrice > 0, purchasePrice, CDbl(productData(4))) ' قيمة المخزون
            newRow.Range(9) = Date ' آخر حركة
            newRow.Range(10) = 10 ' حد التنبية (افتراضي)
            UpdateInventoryStatus tbl.ListRows.count
        End If
    End If
End Sub

' تحديث حالة المخزون
Private Sub UpdateInventoryStatus(rowIndex As Long)
    Dim tbl As ListObject
    Dim currentStock As Double
    Dim alertLimit As Double
    
    Set tbl = GetTableByName("tlbInventory")
    If tbl Is Nothing Then Exit Sub
    
    currentStock = CDbl(tbl.ListRows(rowIndex).Range(7).Value)
    alertLimit = CDbl(tbl.ListRows(rowIndex).Range(10).Value)
    
    If currentStock <= alertLimit Then
        tbl.ListRows(rowIndex).Range(11).Value = "منخفض"
    ElseIf currentStock > alertLimit * 2 Then
        tbl.ListRows(rowIndex).Range(11).Value = "زائد"
    Else
        tbl.ListRows(rowIndex).Range(11).Value = "كافي"
    End If
End Sub

' ============================================
' دوال العملاء والموردين
' ============================================

' الحصول على بيانات العميل
Public Function GetCustomerByCode(customerCode As String) As Variant
    Dim tbl As ListObject
    Dim i As Long
    Dim result(1 To 6) As Variant
    
    Set tbl = GetTableByName("tlbcustomer")
    If tbl Is Nothing Then Exit Function
    
    For i = 1 To tbl.ListRows.count
        If UCase(Trim(tbl.ListRows(i).Range(1).Value)) = UCase(Trim(customerCode)) Then
            result(1) = tbl.ListRows(i).Range(1).Value
            result(2) = tbl.ListRows(i).Range(2).Value
            result(3) = tbl.ListRows(i).Range(3).Value
            result(4) = tbl.ListRows(i).Range(4).Value
            result(5) = tbl.ListRows(i).Range(5).Value
            result(6) = tbl.ListRows(i).Range(6).Value
            GetCustomerByCode = result
            Exit Function
        End If
    Next i
    
    GetCustomerByCode = Empty
End Function

' الحصول على بيانات المورد
Public Function GetSupplierByCode(supplierCode As String) As Variant
    Dim tbl As ListObject
    Dim i As Long
    Dim result(1 To 6) As Variant
    
    Set tbl = GetTableByName("tlbsupplier")
    If tbl Is Nothing Then Exit Function
    
    For i = 1 To tbl.ListRows.count
        If UCase(Trim(tbl.ListRows(i).Range(1).Value)) = UCase(Trim(supplierCode)) Then
            result(1) = tbl.ListRows(i).Range(1).Value
            result(2) = tbl.ListRows(i).Range(2).Value
            result(3) = tbl.ListRows(i).Range(3).Value
            result(4) = tbl.ListRows(i).Range(4).Value
            result(5) = tbl.ListRows(i).Range(5).Value
            result(6) = tbl.ListRows(i).Range(6).Value
            GetSupplierByCode = result
            Exit Function
        End If
    Next i
    
    GetSupplierByCode = Empty
End Function

' ============================================
' دوال الأرقام التلقائية
' ============================================

' الحصول على رقم حركة جديد
Public Function GetNewTransactionNumber() As String
    Dim tbl As ListObject
    Dim lastNum As Long
    Dim i As Long
    Dim maxNum As Long
    
    Set tbl = GetTableByName("tlbTransaction")
    If tbl Is Nothing Then
        GetNewTransactionNumber = "TRX001"
        Exit Function
    End If
    
    maxNum = 0
    For i = 1 To tbl.ListRows.count
        Dim transNum As String
        transNum = CStr(tbl.ListRows(i).Range(1).Value)
        If Left(transNum, 3) = "TRX" Then
            lastNum = CLng(Mid(transNum, 4))
            If lastNum > maxNum Then maxNum = lastNum
        End If
    Next i
    
    GetNewTransactionNumber = "TRX" & Format(maxNum + 1, "000")
End Function

' الحصول على رقم فاتورة وارد جديد
Public Function GetNewInvoiceInNumber() As String
    Dim tbl As ListObject
    Dim lastNum As Long
    Dim i As Long
    Dim maxNum As Long
    Dim invoiceNum As String
    
    Set tbl = GetTableByName("tlbTransaction")
    If tbl Is Nothing Then
        GetNewInvoiceInNumber = "IN001"
        Exit Function
    End If
    
    maxNum = 0
    For i = 1 To tbl.ListRows.count
        invoiceNum = CStr(tbl.ListRows(i).Range(11).Value)
        If Left(UCase(invoiceNum), 2) = "IN" Then
            lastNum = CLng(Mid(invoiceNum, 3))
            If lastNum > maxNum Then maxNum = lastNum
        End If
    Next i
    
    GetNewInvoiceInNumber = "IN" & Format(maxNum + 1, "000")
End Function

' الحصول على رقم فاتورة صادر جديد
Public Function GetNewInvoiceOutNumber() As String
    Dim tbl As ListObject
    Dim lastNum As Long
    Dim i As Long
    Dim maxNum As Long
    Dim invoiceNum As String
    
    Set tbl = GetTableByName("tlbTransaction")
    If tbl Is Nothing Then
        GetNewInvoiceOutNumber = "MG01"
        Exit Function
    End If
    
    maxNum = 0
    For i = 1 To tbl.ListRows.count
        invoiceNum = CStr(tbl.ListRows(i).Range(11).Value)
        If Left(UCase(invoiceNum), 2) = "MG" Then
            Dim numPart As String
            numPart = Mid(invoiceNum, 3)
            If IsNumeric(numPart) Then
                lastNum = CLng(numPart)
                If lastNum > maxNum Then maxNum = lastNum
            End If
        End If
    Next i
    
    GetNewInvoiceOutNumber = "MG" & Format(maxNum + 1, "00")
End Function

' الحصول على رقم جرد جديد
Public Function GetNewStockCountNumber() As String
    Dim tbl As ListObject
    Dim lastNum As Long
    Dim i As Long
    Dim maxNum As Long
    
    Set tbl = GetTableByName("tlbStockCountHeader")
    If tbl Is Nothing Then
        GetNewStockCountNumber = "JRD001"
        Exit Function
    End If
    
    maxNum = 0
    For i = 1 To tbl.ListRows.count
        Dim countNum As String
        countNum = CStr(tbl.ListRows(i).Range(1).Value)
        If Left(UCase(countNum), 3) = "JRD" Then
            lastNum = CLng(Mid(countNum, 4))
            If lastNum > maxNum Then maxNum = lastNum
        End If
    Next i
    
    GetNewStockCountNumber = "JRD" & Format(maxNum + 1, "000")
End Function

' ============================================
' دوال الإعدادات
' ============================================

' الحصول على إعداد الشركة
Public Function GetCompanySetting(settingName As String) As String
    Dim ws As Worksheet
    Dim i As Long
    
    Set ws = GetWorksheetByName("setting")
    If ws Is Nothing Then Exit Function
    
    Select Case UCase(settingName)
        Case "COMPANYNAME", "اسم الشركة"
            GetCompanySetting = ws.Range("B2").Value
        Case "ADDRESS", "العنوان"
            GetCompanySetting = ws.Range("B3").Value
        Case "PHONE", "الهاتف"
            GetCompanySetting = ws.Range("B4").Value
        Case "EMAIL", "البريد"
            GetCompanySetting = ws.Range("B5").Value
        Case "STARTDATE", "تاريخ البداية"
            GetCompanySetting = ws.Range("B6").Value
    End Select
End Function

' ============================================
' دوال التحقق
' ============================================

' التحقق من وجود منتج
Public Function ProductExists(productCode As String) As Boolean
    Dim productData As Variant
    productData = GetProductByCode(productCode)
    ProductExists = Not IsEmpty(productData)
End Function

' التحقق من وجود عميل
Public Function CustomerExists(customerCode As String) As Boolean
    Dim customerData As Variant
    customerData = GetCustomerByCode(customerCode)
    CustomerExists = Not IsEmpty(customerData)
End Function

' التحقق من وجود مورد
Public Function SupplierExists(supplierCode As String) As Boolean
    Dim supplierData As Variant
    supplierData = GetSupplierByCode(supplierCode)
    SupplierExists = Not IsEmpty(supplierData)
End Function


