fix(apps): إصلاحات من أول تجربة حقيقية على الجهاز
## تعذّر حساب الأجرة — الخطأ في التطبيق لا في جدول الكازان الخادم يبذر التعرفة بـ city: 'default' (tenants.service.ts:156) ويستعمل dto.city ?? 'default' عند إنشاء الرحلة، وكنت أرسل city: '' فيرجّع 404 «No active tariff for /economy». تحقّق حيّ: city='default' → total 1.55 JOD ✅ city='' → 404 ❌ city='amman' → 404 ❌ لا تغيير مطلوب على السيرفر. أُضيف AppConfig.defaultCity موثّقاً بالسبب. ## التسعير مرة واحدة لكل الأنواع كان نداء شبكة عند كل ضغطة نوع. صار _priceAll يسعّر كل الأنواع بالتوازي بعد اكتمال المسار — المسافة والمدة ثابتتان والفرق في التعرفة وحدها. الراكب يرى سعر الاقتصادي والمريح معاً تحت كل خيار ويقارن بنظرة، وتبديل النوع بلا نداء. ## الكاميرا لا تتبع الموقع سببان: الموقع يصل قبل إنشاء الخريطة فيضيع التحريك، ولا فحص للإذن/الخدمة قبل getCurrentPosition فيُبتلع الخطأ صامتاً. - _onCreated يلحق بما فات عند جهوز الخريطة - LocationService.ensureReady يفحص الخدمة والإذن أولاً - didChangeAppLifecycleState يعيد المحاولة عند العودة من إعدادات النظام - حالة noLocation تُعرض للمستخدم مع زر إعادة بدل خريطة صامتة ## الخريطة والتنقّل - إطار المسار من حدوده الفعلية مع حشوة سفلية بمقدار ما تغطّيه الورقة - علامتا البداية والنهاية دائرتان ملوّنتان تطابقان ألوان الخط الزمني - زر رجوع في ورقة التأكيد + زر إعادة حساب عند فشل التسعيرة: كان المستخدم يعلق في الشاشة بلا مخرج ## المحفظة — على أي أساس يُشحن POST /wallet/topup شحنٌ تجريبي بلا مزوّد دفع خلفه (الخادم نفسه يصفه كذلك). أُضيف PaymentChannel مشتقّاً من countryPack المستأجر لا من لغة الجهاز: jo → CliQ · sy → سيرياتيل/MTN/شام كاش · eg → PayMob. ويُعرض تنبيه صريح بأن الشحن بلا تحصيل فعلي — إخفاؤه يجعل المستخدم يظنّ أنه دفع. وسحب السائق صار يستعمل القناة المختارة بدل 'cliq' المكتوبة في الكود. flutter analyze نظيف في التطبيقين. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
29f375b122
commit
8976821a93
@@ -0,0 +1,50 @@
|
||||
/// قنوات الدفع والسحب حسب **حزمة دولة المستأجر** (`countryPack` من
|
||||
/// `GET /tenant/config/:slug` — docs/38 §10)، لا حسب لغة الجهاز ولا موقعه.
|
||||
///
|
||||
/// المستأجر هو من يحدّد السوق: مشغّل أردني يخدم مستخدماً هاتفه إنجليزي
|
||||
/// وموقعه خارج الأردن يبقى أردنيّ القنوات.
|
||||
///
|
||||
/// ⚠️ **`POST /wallet/topup` شحنٌ تجريبي بلا مزوّد دفع خلفه** — الخادم نفسه
|
||||
/// يصفه بـ«كاش-إن، للإنتاج يُربط بمزوّد دفع أو يُقيَّد للأدمن». اختيار
|
||||
/// القناة هنا يجهّز الواجهة والعقد؛ التحصيل الفعلي عبر `/payments/charge`
|
||||
/// وبوابة كل قناة عملٌ منفصل لم يُنفَّذ بعد.
|
||||
enum PaymentChannel {
|
||||
cliq('cliq', 'CliQ', 'كليك'),
|
||||
syriatel('syriatel_cash', 'Syriatel Cash', 'سيرياتيل كاش'),
|
||||
mtn('mtn_cash', 'MTN Cash', 'إم تي إن كاش'),
|
||||
shamCash('sham_cash', 'Sham Cash', 'شام كاش'),
|
||||
paymob('paymob', 'PayMob', 'باي موب'),
|
||||
cash('cash', 'Cash', 'نقداً');
|
||||
|
||||
const PaymentChannel(this.wire, this.nameEn, this.nameAr);
|
||||
|
||||
/// القيمة المرسلة للخادم في حقل `channel`.
|
||||
final String wire;
|
||||
final String nameEn;
|
||||
final String nameAr;
|
||||
|
||||
String nameFor(String languageCode) =>
|
||||
languageCode == 'ar' ? nameAr : nameEn;
|
||||
|
||||
/// القنوات المتاحة لكل حزمة دولة — مطابقة لما هو مدعوم فعلياً في السوق
|
||||
/// (نفس قنوات سيرو الميدانية).
|
||||
static List<PaymentChannel> forCountry(String countryPack) =>
|
||||
switch (countryPack) {
|
||||
'jo' => const [PaymentChannel.cliq, PaymentChannel.cash],
|
||||
'sy' => const [
|
||||
PaymentChannel.syriatel,
|
||||
PaymentChannel.mtn,
|
||||
PaymentChannel.shamCash,
|
||||
PaymentChannel.cash,
|
||||
],
|
||||
'eg' => const [PaymentChannel.paymob, PaymentChannel.cash],
|
||||
// حزمة غير معروفة: النقد وحده — لا نعرض قناة قد لا تعمل في سوقها.
|
||||
_ => const [PaymentChannel.cash],
|
||||
};
|
||||
|
||||
/// قنوات السحب — النقد ليس قناة سحب.
|
||||
static List<PaymentChannel> payoutFor(String countryPack) =>
|
||||
forCountry(countryPack)
|
||||
.where((c) => c != PaymentChannel.cash)
|
||||
.toList(growable: false);
|
||||
}
|
||||
Reference in New Issue
Block a user