# دليل تكامل نظام الأقساط مع تطبيق الجوال

## نظرة عامة

يوفر هذا الدليل معلومات حول كيفية تكامل نظام إدارة أقساط الفواتير مع تطبيق الجوال الخاص بشركات المصاعد. يتضمن الدليل شرحاً لواجهة برمجة التطبيقات (API) المتاحة، وأمثلة على الاستخدام، وتوصيات للتنفيذ.

## المتطلبات الأساسية

1. تطبيق جوال يعمل على نظام Android أو iOS
2. إمكانية إجراء طلبات HTTP
3. القدرة على التعامل مع استجابات JSON
4. آلية للمصادقة وتخزين رموز المصادقة (tokens)

## المصادقة

يستخدم نظام الأقساط Laravel Sanctum للمصادقة. يجب على تطبيق الجوال:

1. تسجيل دخول المستخدم واستلام رمز المصادقة (token)
2. تخزين الرمز بشكل آمن على الجهاز
3. إرسال الرمز مع كل طلب في رأس الطلب:

```
Authorization: Bearer {token}
```

## واجهات برمجة التطبيقات الرئيسية

### 1. عرض لوحة معلومات الأقساط

```
GET /api/installments/stats
```

هذه النقطة النهائية توفر إحصائيات شاملة عن الأقساط، مثل:
- إجمالي عدد الأقساط
- عدد الأقساط المدفوعة
- عدد الأقساط قيد الانتظار
- عدد الأقساط المتأخرة
- إجمالي المبالغ
- المبالغ المدفوعة
- المبالغ المستحقة
- المبالغ المتأخرة

**مثال على الاستجابة:**

```json
{
    "status": "success",
    "data": {
        "total": 10,
        "paid": 3,
        "pending": 5,
        "overdue": 2,
        "total_amount": 5000.00,
        "paid_amount": 1500.00,
        "due_amount": 3500.00,
        "overdue_amount": 1200.00
    }
}
```

### 2. عرض الأقساط المستحقة قريباً

```
GET /api/installments/reports/upcoming?days=30&limit=10
```

هذه النقطة النهائية توفر قائمة بالأقساط المستحقة خلال فترة زمنية محددة (الافتراضي: 30 يوم).

**المعلمات:**
- `days`: عدد الأيام القادمة (اختياري، الافتراضي: 30)
- `limit`: الحد الأقصى لعدد النتائج (اختياري، الافتراضي: 100)

### 3. عرض الأقساط المتأخرة

```
GET /api/installments/reports/overdue?limit=10
```

هذه النقطة النهائية توفر قائمة بالأقساط المتأخرة.

**المعلمات:**
- `limit`: الحد الأقصى لعدد النتائج (اختياري، الافتراضي: 100)

### 4. تسجيل دفعة لقسط

```
POST /api/installments/{id}/payments
```

هذه النقطة النهائية تسمح بتسجيل دفعة لقسط محدد.

**المعلمات:**
- `amount`: مبلغ الدفعة (مطلوب)
- `payment_date`: تاريخ الدفع (مطلوب)
- `payment_method_id`: معرف طريقة الدفع (مطلوب)
- `reference_number`: رقم المرجع (اختياري)
- `notes`: ملاحظات إضافية (اختياري)

## تنفيذ واجهة المستخدم في تطبيق الجوال

### 1. لوحة معلومات الأقساط

يجب أن تتضمن لوحة معلومات الأقساط في تطبيق الجوال:

1. **بطاقات الإحصائيات**:
   - إجمالي الأقساط
   - الأقساط المدفوعة
   - الأقساط قيد الانتظار
   - الأقساط المتأخرة

2. **رسم بياني للأقساط**:
   - رسم بياني دائري يوضح توزيع الأقساط حسب الحالة
   - رسم بياني شريطي يوضح توزيع الأقساط حسب الشهر

3. **قائمة الأقساط المستحقة قريباً**:
   - عرض الأقساط المستحقة خلال الأسبوع القادم
   - إمكانية تسجيل دفعة سريعة
   - إمكانية إرسال تذكير للعميل

4. **قائمة الأقساط المتأخرة**:
   - عرض الأقساط المتأخرة
   - إمكانية تسجيل دفعة سريعة
   - إمكانية إرسال تذكير للعميل

### 2. صفحة تفاصيل القسط

يجب أن تتضمن صفحة تفاصيل القسط:

1. **معلومات القسط**:
   - رقم القسط
   - رقم الفاتورة
   - العميل
   - المبلغ
   - المبلغ المدفوع
   - المبلغ المتبقي
   - تاريخ الاستحقاق
   - الحالة
   - ملاحظات

2. **شريط تقدم الدفع**:
   - نسبة الدفع
   - المبلغ المدفوع / المبلغ الإجمالي

3. **سجل المدفوعات**:
   - قائمة بالمدفوعات المسجلة للقسط
   - تاريخ الدفع
   - المبلغ
   - طريقة الدفع
   - رقم المرجع

4. **الإجراءات**:
   - تسجيل دفعة
   - إرسال تذكير
   - تعديل القسط
   - حذف القسط

### 3. نموذج تسجيل دفعة

يجب أن يتضمن نموذج تسجيل دفعة:

1. **حقول النموذج**:
   - المبلغ (مع التحقق من أنه لا يتجاوز المبلغ المتبقي)
   - تاريخ الدفع (الافتراضي: اليوم الحالي)
   - طريقة الدفع (قائمة منسدلة)
   - رقم المرجع (اختياري)
   - ملاحظات (اختياري)

2. **أزرار الإجراءات**:
   - تسجيل الدفعة
   - إلغاء

## أمثلة على الكود

### مثال 1: عرض لوحة معلومات الأقساط (React Native)

```javascript
import React, { useState, useEffect } from 'react';
import { View, Text, StyleSheet, ActivityIndicator } from 'react-native';
import { PieChart, BarChart } from 'react-native-chart-kit';
import axios from 'axios';

const InstallmentsDashboard = () => {
  const [stats, setStats] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchStats = async () => {
      try {
        const token = await AsyncStorage.getItem('userToken');
        const response = await axios.get('https://example.com/api/installments/stats', {
          headers: {
            'Authorization': `Bearer ${token}`,
            'Accept': 'application/json'
          }
        });
        setStats(response.data.data);
        setLoading(false);
      } catch (err) {
        setError(err.message);
        setLoading(false);
      }
    };

    fetchStats();
  }, []);

  if (loading) {
    return <ActivityIndicator size="large" color="#0000ff" />;
  }

  if (error) {
    return <Text style={styles.error}>Error: {error}</Text>;
  }

  const pieData = [
    {
      name: 'Paid',
      population: stats.paid,
      color: '#28a745',
      legendFontColor: '#7F7F7F',
      legendFontSize: 15
    },
    {
      name: 'Pending',
      population: stats.pending,
      color: '#ffc107',
      legendFontColor: '#7F7F7F',
      legendFontSize: 15
    },
    {
      name: 'Overdue',
      population: stats.overdue,
      color: '#dc3545',
      legendFontColor: '#7F7F7F',
      legendFontSize: 15
    }
  ];

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Installments Dashboard</Text>
      
      <View style={styles.statsContainer}>
        <View style={[styles.statCard, { backgroundColor: '#17a2b8' }]}>
          <Text style={styles.statNumber}>{stats.total}</Text>
          <Text style={styles.statLabel}>Total Installments</Text>
        </View>
        
        <View style={[styles.statCard, { backgroundColor: '#28a745' }]}>
          <Text style={styles.statNumber}>{stats.paid}</Text>
          <Text style={styles.statLabel}>Paid Installments</Text>
        </View>
        
        <View style={[styles.statCard, { backgroundColor: '#ffc107' }]}>
          <Text style={styles.statNumber}>{stats.pending}</Text>
          <Text style={styles.statLabel}>Pending Installments</Text>
        </View>
        
        <View style={[styles.statCard, { backgroundColor: '#dc3545' }]}>
          <Text style={styles.statNumber}>{stats.overdue}</Text>
          <Text style={styles.statLabel}>Overdue Installments</Text>
        </View>
      </View>
      
      <Text style={styles.sectionTitle}>Installments by Status</Text>
      <PieChart
        data={pieData}
        width={350}
        height={200}
        chartConfig={{
          backgroundColor: '#ffffff',
          backgroundGradientFrom: '#ffffff',
          backgroundGradientTo: '#ffffff',
          decimalPlaces: 0,
          color: (opacity = 1) => `rgba(0, 0, 0, ${opacity})`,
        }}
        accessor="population"
        backgroundColor="transparent"
        paddingLeft="15"
        absolute
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 16,
    backgroundColor: '#f8f9fa',
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    marginBottom: 16,
    textAlign: 'center',
  },
  statsContainer: {
    flexDirection: 'row',
    flexWrap: 'wrap',
    justifyContent: 'space-between',
    marginBottom: 24,
  },
  statCard: {
    width: '48%',
    padding: 16,
    borderRadius: 8,
    marginBottom: 16,
    alignItems: 'center',
  },
  statNumber: {
    fontSize: 24,
    fontWeight: 'bold',
    color: '#ffffff',
  },
  statLabel: {
    fontSize: 14,
    color: '#ffffff',
    marginTop: 8,
  },
  sectionTitle: {
    fontSize: 18,
    fontWeight: 'bold',
    marginBottom: 16,
  },
  error: {
    color: 'red',
    fontSize: 16,
    textAlign: 'center',
    marginTop: 20,
  },
});

export default InstallmentsDashboard;
```

### مثال 2: تسجيل دفعة لقسط (React Native)

```javascript
import React, { useState } from 'react';
import { View, Text, TextInput, StyleSheet, TouchableOpacity, Alert } from 'react-native';
import DateTimePicker from '@react-native-community/datetimepicker';
import { Picker } from '@react-native-picker/picker';
import axios from 'axios';

const RecordPayment = ({ route, navigation }) => {
  const { installmentId, remainingAmount } = route.params;
  
  const [amount, setAmount] = useState(remainingAmount.toString());
  const [paymentDate, setPaymentDate] = useState(new Date());
  const [paymentMethodId, setPaymentMethodId] = useState('');
  const [referenceNumber, setReferenceNumber] = useState('');
  const [notes, setNotes] = useState('');
  const [showDatePicker, setShowDatePicker] = useState(false);
  const [paymentMethods, setPaymentMethods] = useState([]);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    const fetchPaymentMethods = async () => {
      try {
        const token = await AsyncStorage.getItem('userToken');
        const response = await axios.get('https://example.com/api/payment-methods', {
          headers: {
            'Authorization': `Bearer ${token}`,
            'Accept': 'application/json'
          }
        });
        setPaymentMethods(response.data.data);
        if (response.data.data.length > 0) {
          setPaymentMethodId(response.data.data[0].id.toString());
        }
      } catch (err) {
        Alert.alert('Error', 'Failed to load payment methods');
      }
    };

    fetchPaymentMethods();
  }, []);

  const handleSubmit = async () => {
    if (!amount || parseFloat(amount) <= 0) {
      Alert.alert('Error', 'Please enter a valid amount');
      return;
    }

    if (parseFloat(amount) > parseFloat(remainingAmount)) {
      Alert.alert('Error', 'Amount exceeds remaining amount');
      return;
    }

    if (!paymentMethodId) {
      Alert.alert('Error', 'Please select a payment method');
      return;
    }

    setLoading(true);

    try {
      const token = await AsyncStorage.getItem('userToken');
      const response = await axios.post(
        `https://example.com/api/installments/${installmentId}/payments`,
        {
          amount: parseFloat(amount),
          payment_date: paymentDate.toISOString().split('T')[0],
          payment_method_id: parseInt(paymentMethodId),
          reference_number: referenceNumber,
          notes: notes
        },
        {
          headers: {
            'Authorization': `Bearer ${token}`,
            'Accept': 'application/json',
            'Content-Type': 'application/json'
          }
        }
      );

      setLoading(false);
      Alert.alert('Success', 'Payment recorded successfully');
      navigation.goBack();
    } catch (err) {
      setLoading(false);
      Alert.alert('Error', err.response?.data?.message || 'Failed to record payment');
    }
  };

  const onDateChange = (event, selectedDate) => {
    setShowDatePicker(false);
    if (selectedDate) {
      setPaymentDate(selectedDate);
    }
  };

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Record Payment</Text>
      
      <View style={styles.formGroup}>
        <Text style={styles.label}>Amount</Text>
        <TextInput
          style={styles.input}
          value={amount}
          onChangeText={setAmount}
          keyboardType="numeric"
          placeholder="Enter amount"
        />
      </View>
      
      <View style={styles.formGroup}>
        <Text style={styles.label}>Payment Date</Text>
        <TouchableOpacity
          style={styles.dateInput}
          onPress={() => setShowDatePicker(true)}
        >
          <Text>{paymentDate.toISOString().split('T')[0]}</Text>
        </TouchableOpacity>
        {showDatePicker && (
          <DateTimePicker
            value={paymentDate}
            mode="date"
            display="default"
            onChange={onDateChange}
          />
        )}
      </View>
      
      <View style={styles.formGroup}>
        <Text style={styles.label}>Payment Method</Text>
        <View style={styles.pickerContainer}>
          <Picker
            selectedValue={paymentMethodId}
            onValueChange={(itemValue) => setPaymentMethodId(itemValue)}
            style={styles.picker}
          >
            {paymentMethods.map(method => (
              <Picker.Item
                key={method.id}
                label={method.name}
                value={method.id.toString()}
              />
            ))}
          </Picker>
        </View>
      </View>
      
      <View style={styles.formGroup}>
        <Text style={styles.label}>Reference Number</Text>
        <TextInput
          style={styles.input}
          value={referenceNumber}
          onChangeText={setReferenceNumber}
          placeholder="Enter reference number (optional)"
        />
      </View>
      
      <View style={styles.formGroup}>
        <Text style={styles.label}>Notes</Text>
        <TextInput
          style={[styles.input, styles.textArea]}
          value={notes}
          onChangeText={setNotes}
          placeholder="Enter notes (optional)"
          multiline
          numberOfLines={4}
        />
      </View>
      
      <TouchableOpacity
        style={styles.submitButton}
        onPress={handleSubmit}
        disabled={loading}
      >
        <Text style={styles.submitButtonText}>
          {loading ? 'Processing...' : 'Record Payment'}
        </Text>
      </TouchableOpacity>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 16,
    backgroundColor: '#ffffff',
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    marginBottom: 24,
    textAlign: 'center',
  },
  formGroup: {
    marginBottom: 16,
  },
  label: {
    fontSize: 16,
    marginBottom: 8,
    fontWeight: '500',
  },
  input: {
    borderWidth: 1,
    borderColor: '#ced4da',
    borderRadius: 4,
    padding: 12,
    fontSize: 16,
  },
  dateInput: {
    borderWidth: 1,
    borderColor: '#ced4da',
    borderRadius: 4,
    padding: 12,
    fontSize: 16,
  },
  pickerContainer: {
    borderWidth: 1,
    borderColor: '#ced4da',
    borderRadius: 4,
    overflow: 'hidden',
  },
  picker: {
    height: 50,
  },
  textArea: {
    height: 100,
    textAlignVertical: 'top',
  },
  submitButton: {
    backgroundColor: '#007bff',
    padding: 16,
    borderRadius: 4,
    alignItems: 'center',
    marginTop: 16,
  },
  submitButtonText: {
    color: '#ffffff',
    fontSize: 16,
    fontWeight: 'bold',
  },
});

export default RecordPayment;
```

## توصيات للتنفيذ

1. **الإشعارات المحلية**:
   - استخدم إشعارات الجهاز المحلية لتذكير المستخدمين بالأقساط المستحقة قريباً
   - قم بجدولة فحص دوري للأقساط المستحقة وإرسال إشعارات محلية

2. **وضع عدم الاتصال**:
   - قم بتخزين البيانات محلياً للسماح بالوصول إليها في وضع عدم الاتصال
   - قم بمزامنة البيانات عند استعادة الاتصال

3. **تحسين الأداء**:
   - استخدم التخزين المؤقت للبيانات لتقليل عدد الطلبات إلى الخادم
   - قم بتنفيذ التحميل المتكاسل (lazy loading) للقوائم الطويلة

4. **تجربة المستخدم**:
   - استخدم الألوان لتمييز حالات الأقساط المختلفة (مدفوع، قيد الانتظار، متأخر)
   - قم بتنفيذ تنبيهات مرئية للأقساط المتأخرة
   - وفر إجراءات سريعة للمهام الشائعة مثل تسجيل دفعة أو إرسال تذكير

5. **الأمان**:
   - لا تخزن بيانات حساسة على الجهاز
   - استخدم HTTPS لجميع الاتصالات مع الخادم
   - قم بتنفيذ آلية لتجديد رموز المصادقة (token refresh)

## الخاتمة

يوفر تكامل نظام إدارة أقساط الفواتير مع تطبيق الجوال تجربة سلسة للمستخدمين، مما يسمح لهم بإدارة الأقساط ومتابعتها من أي مكان. باستخدام واجهة برمجة التطبيقات (API) المتاحة، يمكن تنفيذ جميع وظائف إدارة الأقساط في تطبيق الجوال، مما يعزز كفاءة العمل ويحسن تجربة المستخدم.