ifnex/AGENT.md
Kazem Alghasi a4fc807160 feat(address-book): implement customer address book system (phase 3.9)
Introduce a comprehensive address management system allowing users to
save, manage, and reuse sender and receiver addresses during the
ordering process. This feature bridges the WordPress frontend with the
Laravel backend API.

Key changes:
- Backend (Laravel):
  - Added `AddressBookEntry` model and migration for storing addresses
    with role (sender/receiver), usage tracking, and default status.
  - Implemented `AddressBookController` with full CRUD functionality
    secured via Sanctum and ownership validation.
  - Integrated address usage tracking and upsert logic into the
    `CustomerOrderController` to prevent duplicate entries.
  - Added 18 new feature tests covering ownership, limits, and
    validation.
- Frontend (WordPress):
  - Added a new "Address Book" tab in the user dashboard with card-based
    UI, filtering, and SweetAlert2 integration.
  - Enhanced the order form with dropdown selectors for saved addresses
    and "save this address" checkboxes for both sender and receiver.
  - Implemented secure AJAX proxies in `user-bridge.php` with nonce
    and sanitization.
- Documentation:
  - Updated `AGENT.md` and `CLIENT_DELIVERY.md` to reflect phase 3.9
    completion and updated the project roadmap.
  - Removed obsolete `README-INSTALL.md`.
2026-10-31 19:57:01 +03:30

41 KiB
Raw Blame History

راهنمای ایجنت — پروژهٔ IFNEX Logistics

تاریخ: ۲۰۲۶-۱۰-۳۱ نسخه: ۱.۵ تهیه‌کننده: VernaSoft Group — Kazem Alghasi


۱. خلاصهٔ پروژه

سیستم مدیریت لجستیک بین‌المللی با معماری Headless: Laravel 11 به‌عنوان بک‌اند، WordPress به‌عنوان فرانت‌اند و Filament 3.3 به‌عنوان پنل مدیریت. این سیستم جایگزین فرآیندهای دستی مبتنی بر اکسل شده و از Multi-Package، فاکتور گمرکی، کیف پول دیجیتال، درگاه پرداخت Zarinpal، تولید PDF با بارکد، فلوی تأیید سفارش، تعهدنامه، SMS کاوه‌نگار، سیستم اعتبار چندارزی، رهگیری پیشرفته، سیستم ۴-رُبعی نرخ‌ها و فاکتورهای دستی مدیر پشتیبانی می‌کند.

وضعیت فعلی

فازهای ۰، ۱، ۲، ۳، ۳.۵، ۳.۶ و ۳.۷ تکمیل شده‌اند. فاز ۳.۷ (اصلاحات ارزی و فاکتور دستی) تکمیل شد:

  • فلوی کامل سفارش (ثبت ← تأیید ← تعهدنامه ← پرداخت) — تست End-to-End
  • بازطراحی داشبورد و Login پنل Filament (هویت بصری navy + amber)
  • بازطراحی PDFهای AWB، Invoice و Label (dompdf، چیدمان جدولی)
  • صفحهٔ «وضعیت مالی مشتری» در پنل ادمین (۴ KPI + بدهی‌های ارزی + سفارش‌ها و تراکنش‌ها)
  • سیستم اعتبار چندارزی کامل (درخواست ۹) — بدهی به همان ارز، تسویه با نرخ روز
  • ویجت هشدار بدهی‌های ارزی تسویه‌نشده + badge روی منو
  • Rate Limiting روی همهٔ APIها (۶ لایه throttle: auth / sms / public / customer / wallet / staff)
  • چک‌لیست خودکار کارمند پس از تأیید سفارش (درخواست ۵)
  • نمایش بدهی ارزی در پورتال مشتری وردپرس
  • استایل‌دهی صفحهٔ پروفایل مشتری در وردپرس
  • پاک‌سازی کد (حذف فایل‌های تستی، تأیید تمیزی opcache_reset / dd / dump)
  • سیستم ۴-رُبعی نرخ‌ها (Export/Import × Doc/Parcel) با ایمپورت از اکسل
  • معماری IRR-only — ارز مبنا = ریال ایران؛ تبدیل ارز در زمان ایمپورت
  • فاکتورهای دستی مدیر — صدور، پرداخت از کیف پول، اعلان خودکار به مشتری

فاز ۳.۸ (کارهای سریع ظاهری / برندینگ) نیز تکمیل شد:

  • Favicon حرفه‌ای IFNEX — ico چندسایزی + svg وکتور + apple-touch-icon + PWA icons + webmanifest + لینک در head پنل
  • صفحهٔ / برندشده — جایگزین welcome پیش‌فرض لاراول
  • جمع‌شدن کارت لاگین فیلمنت — ۳۸۰px، فشرده‌تر
  • SweetAlert2 برای خطاهای فرم سفارش وردپرس (باندل محلی)

فاز ۳.۸.۱ (سخت‌سازی Production) نیز تکمیل شد (۲۰۲۶-۱۰-۳۱):

  • P0 — باگ کد تخفیف: متد consumeDiscountCode() فراخوانی می‌شد ولی وجود نداشت (ثبت سفارش را می‌شکست). سرویس جدید DiscountCodeService با جدول discount_code_usages (Audit + Idempotency + قفل رقابتی) جایگزین شد؛ مصرف فقط در ثبت اولیه، ارسال مجدد دوباره مصرف نمی‌کند
  • گارد ظرفیت کد تخفیف در قیمت‌گذاری — کدهای پرشده در calculate() هم اعمال نمی‌شوند
  • گارد Mock Gateway در Production — اگر APP_ENV=production و درگاه تستی باشد، پردازش با RuntimeException + Log::critical مسدود می‌شود (قبلاً پیش‌فرض sandbox=true خطرناک بود)
  • حذف هک‌های دیباگ وردپرس — opcache_reset() + header('Cache-Control') از ۵ فایل پلاگین حذف شد
  • نسخه‌بندی سالم assetها — cache-buster '2.0.0-' . time() (هر ریکوئست کش را می‌شکند) با ifnex_asset_version() مبتنی بر filemtime + ثابت IFNEX_BRIDGE_VERSION (2.1.0) جایگزین شد (۶ نقطه)
  • سخت‌سازی آپلود تعهدنامه — mimetypes:application/pdf,image/jpeg,image/png (بررسی محتوا، نه فقط پسوند)
  • خروج wp-config.php از گیت — saltهای واقعی لو‌رفته فرض می‌شوند؛ فایل در .gitignore و فقط wp-config-sample.php در مخزن است
  • .env.example بروز — بلوک Production (لاگ روزانه، APP_DEBUG=false، کش‌ها) + IFNEX_BRIDGE_API_KEY + راهنمای چرخش کلید
  • سند استقرار — بخش ۴.۴ «چک‌لیست امنیتی سخت‌سازی» در DEPLOYMENT_HANDOFF.md
  • تست‌ها: ۷ تست شکستهٔ قدیمی (auth کد تخفیف + معماری قدیمی محاسبه‌گر) تعمیر شد؛ ۷ تست جدید برای سرویس مصرف + ۲ تست برای ظرفیت/تخفیف — ۳۳ تست پاس، ۱۰۱ assertion

فاز ۳.۹ (دفترچه آدرس) نیز تکمیل شد (۲۰۲۶-۱۱-۰۱):

  • مدل + جدول address_book_entries — نقش (sender/receiver)، برچسب، فیلدهای تماس عیناً مطابق ستون‌های sender_/receiver_ در shipments، پیش‌فرض، شمارندهٔ استفاده + last_used_at، سقف ۵۰ رکورد per-user
  • CRUD API با Sanctum — GET/POST/PUT/DELETE /api/v1/addresses + POST /addresses/{id}/default؛ Ownership در همهٔ اکشن‌ها؛ دتکت رکورد تکراری (نقش + تلفن + آدرس → بروزرسانی به‌جای ساخت دوباره)؛ فیلتر role/search
  • اتصال به ثبت سفارش — چک‌باکس «ذخیره این آدرس» (upsert بدون رکورد تکراری) + شمارش استفاده از آدرس انتخاب‌شده؛ عملیات غیرحیاتی (هر خطا فقط Log — سفارش rollback نمی‌شود)
  • فرم سفارش وردپرس — dropdown «آدرس‌های ذخیره‌شده» بالای بخش فرستنده/گیرنده (پر شدن یک‌کلیکی ۷ فیلد) + چک‌باکس ذخیره برای هر نقش
  • تب «دفترچه آدرس» در پنل کاربری — کارت‌های آدرس با فیلتر همه/فرستنده/گیرنده، فرم افزودن/ویرایش، حذف با تأیید SweetAlert، تنظیم پیش‌فرض، حالت خالی راهنما
  • پروکسی AJAX امن — ۴ هندلر جدید (get/save/delete/set-default) با nonce + sanitize + توکن Sanctum کاربر
  • تست‌ها: ۱۸ تست جدید (AddressBookControllerTest) — لیست/فیلتر/ساخت/ویرایش/حذف/مالکیت/سقف/تکراری/پیش‌فرض/markUsed/formFields

چندزبانه (i18n) همچنان به زمان دیپلوی موکول است.

معماری ارزی (Currency Architecture)

قاعده طلایی: ارز مبنا در کل سیستم = ریال ایران (IRR). هیچ واحد دیگری (تومان، درهم، دلار، یورو) در نمایش قیمت‌های داخلی یا ذخیره‌سازی استفاده نمی‌شود.

نوع داده ارز ذخیره تبدیل
نرخ‌های حمل (rate_entries) IRR در زمان ایمپورت با نرخ روز
فاکتورهای دستی (manual_invoices) IRR مستقیم
تراکنش‌های کیف پول (wallet_transactions) IRR مستقیم
بدهی‌های ارزی مشتری (customer_credits) ارز خارجی در زمان تسویه با نرخ روز

نرخ‌های ارز از جدول currencies خوانده می‌شوند. مدیر به‌صورت دستی یا از API (Exir.io، موقتاً غیرفعال) نرخ را به‌روز می‌کند. API نرخ‌ها با وضعیت pending ذخیره می‌شوند و مدیر باید تأیید کند (approved).

تفاوت نرخ‌های حمل و بدهی ارزی:

  • نرخ‌های حمل به IRR ذخیره می‌شوند (در زمان ایمپورت با نرخ روز تبدیل می‌شوند) — تغییرات نرخ روز روی نرخ‌های ذخیره‌شده اثر ندارد.
  • بدهی ارزی به ارز خارجی ذخیره می‌شود — در زمان تسویه با نرخ روز محاسبه می‌شود.

فیچر فاکتورهای دستی مدیر (Manual Invoices)

مدیر می‌تواند برای هر مشتری فاکتور صادر کند (برای خدمات خارج از سفارش: انبارداری، مشاوره تلفنی، هزینه‌های جانبی و...).

  • پنل ادمین: Manual invoices (گروه «مالی»)
  • مدل: ManualInvoice + ManualInvoiceService + CustomerInvoiceController
  • جریان:
    1. مدیر فاکتور صادر می‌کند → status = 'pending_payment'
    2. اعلان خودکار به مشتری ارسال می‌شود (ManualInvoiceCreatedNotification)
    3. مشتری در پورتال وردپرس فاکتور را می‌بیند (تب «فاکتورهای دستی»)
    4. با کلیک روی «پرداخت از کیف پول»، مبلغ از کیف پول کسر می‌شود
    5. تراکنش با نوع invoice_payment در wallet_transactions ثبت می‌شود
  • شماره فاکتور: فرمت INV-YYYY-NNNNNN (مثلاً INV-2026-000001)
  • وضعیت‌ها: pending_payment / paid / cancelled / expired
  • اعلام در پورتال: badge قرمز در منوی سایدبار + اعلان در تب اعلان‌ها + ویجت داشبورد + تفکیک در وضعیت مالی

خط قرمزهای معماری ارزی

❌ هرگز ✅ همیشه
ذخیره نرخ‌ها به AED/USD ذخیره به IRR
نمایش قیمت به «درهم» در فرم/پورتال نمایش به «ریال»
تبدیل در زمان محاسبه قیمت تبدیل در زمان ایمپورت
وجود fallback عددی برای نرخ ارز خطای واضح اگه نرخ تنظیم نشده
استفاده از «تومان» در کد/UI فقط «ریال»

۲. تکنولوژی‌ها و نسخه‌ها

لایه تکنولوژی نسخه / توضیح
Backend Laravel 11.x
زبان PHP ^8.2 (۸.۳ نیز پشتیبانی می‌شود)
پنل مدیریت Filament 3.3.x
فرانت‌اند WordPress 7.x + قالب سفارشی IFNEX
احراز هویت Laravel Sanctum + Bridge Auth بدون رمز عبور (کلید API مشترک)
پرداخت Zarinpal + Mock Gateway (تست) کلید sandbox برای تست لوکال
PDF و بارکد Dompdf + picqer/php-barcode-generator AWB، Invoice، Label، فاکتور واردات
پیامک Kavenegar تأیید موبایل + اعلان وضعیت سفارش
اکسل maatwebsite/excel + PhpSpreadsheet Import / Export نرخ‌ها و داده‌های تاریخی
تاریخ و زمان morilog/jalali + Carbon تاریخ شمسی در نمایش، میلادی در DB
دیتابیس MySQL 8.0+ (تست‌ها با SQLite)
صف Redis (ترجیحی) / Database برای پردازش‌های سنگین
سرور HestiaCP + Nginx + PHP-FPM تولید (api.ifnex.vernahost.ir)

۳. ساختار دایرکتوری

IFNEX-Logistics/
├── 01_Documents/                      # مستندات فنی
│   ├── IFNEX_File_Map.md               # نقشهٔ ۱۰۰+ فایل پروژه
│   ├── IFNEX_Roadmap.md                # نقشهٔ راه فازی
│   ├── IFNEX_Phase0_Checklist.md       # چک‌لیست تکمیل فازها
│   ├── IFNEX_ADR.md                    # تصمیمات معماری (ADR-001 تا 009)
│   ├── EXCEL_ANALYSIS.md               # تحلیل داده‌های تاریخی
│   ├── IFNEX_I18N_Strategy.md          # استراتژی چندزبانه
│   └── IFNEX_Commercial_Model.md       # مدل تجاری
│
├── 03_WordPress/                      # فرانت‌اند وردپرس
│   └── wp-content/
│       ├── themes/ifnex/               # قالب سفارشی
│       └── plugins/ifnex-bridge/       # پلاگین ارتباط با لاراول
│           ├── includes/               # api-client، user-bridge، shortcodes، tracking-form
│           └── assets/                 # CSS و JS فرم سفارش
│
├── 04_Laravel/                        # بک‌اند لاراول (هستهٔ اصلی)
│   ├── app/
│   │   ├── Enums/                      # ShipmentStatus، ShipmentDirection، ShipmentType، PaymentGateway، TransactionStatus
│   │   ├── Filament/                   # ۱۷ Resource + Pages + Widgets
│   │   ├── Http/
│   │   │   ├── Controllers/Api/        # Track، Pricing، Auth، Bridge، Customer، Wallet، Payment، Staff، CommitmentForm
│   │   │   └── Middleware/             # ApiKeyMiddleware
│   │   ├── Models/                     # ۲۶ مدل Eloquent
│   │   ├── Notifications/              # DB notifications + SMS
│   │   ├── Observers/                  # ShipmentObserver
│   │   ├── Services/                   # PriceCalculator، Pdf، Tracking، OrderPayment، Wallet، Zarinpal، KavenegarSms
│   │   ├── Traits/                     # Auditable
│   │   ├── Imports/                    # OldShipments، ShippingRates
│   │   └── Exports/                    # ShippingRatesTemplateExport
│   ├── database/migrations/           # ۵۲ migration
│   ├── resources/views/pdfs/           # awb، invoice، label، import-invoice
│   └── routes/api.php                  # ۴۰+ endpoint
│
├── AGENT.md                            # همین سند
├── CLIENT_DELIVERY.md                  # چک‌لیست وضعیت تحویل مشتری (منبع اصلی)
├── DEPLOYMENT.md                       # راهنمای استقرار روی سرور
├── DEPLOYMENT_HANDOFF.md               # تحویل استقرار به تیم IT کارفرما
└── README.md                           # معرفی کلی پروژه

۴. مفاهیم کلیدی بیزینس

۴.۱ مرسوله (Shipment)

مورد مقدار
انواع (Type) DOC_NORMAL، DOC_ECONOMY، PARCEL
جهت (Direction) export (صادرات) و import (واردات)
وضعیت (Status) pending_approval ← approved ← processed ← picked_up ← in_transit ← out_for_delivery ← delivered / failed / returned / cancelled (به‌همراه pending_payment legacy و archived)

هر مرسوله دارای چند بسته (packages)، اقلام گمرکی (items — فقط برای PARCEL)، تاریخچهٔ تغییرات وضعیت (statusHistories)، شرکت‌های حمل (carrierMappings)، رویدادهای رهگیری (trackingEvents) و تعهدنامه‌ها (commitmentForms) است.

۴.۲ بسته (Package)

  • هر مرسوله می‌تواند چندین بسته داشته باشد (جدول shipment_packages).
  • وزن حجمی = (Length × Width × Height) / 5000 (استاندارد IATA).
  • وزن قابل پرداخت = max(وزن واقعی، وزن حجمی).

۴.۳ فاکتور گمرکی (Invoice)

  • فقط برای مرسوله‌های نوع PARCEL صادر می‌شود.
  • حداکثر ۹ قلم کالا (شرح، HS Code، تعداد، قیمت واحد، مجموع به USD).
  • مجموع کل فاکتور در shipments.invoice_total_usd ذخیره می‌شود.
  • فاکتور واردات (Import Invoice): فیلدهای brand_fee، report_fee، customs_clearance_cost، order_registration_fee و… به‌همراه قالب PDF مطابق شیت ENG Invoice.

۴.۴ کیف پول (Wallet)

  • هر کاربر یک کیف پول دارد.
  • تراکنش‌ها (WalletTransaction) با نوع deposit، order_payment، refund، withdrawal به‌صورت polymorphic به Payment یا سفارش متصل هستند.
  • پرداخت از کیف پول یا درگاه Zarinpal (با Mock برای تست)، به‌همراه freeze / unfreeze و activity log کامل.

۴.۵ احراز هویت Bridge

  • کاربران وردپرس بدون نیاز به رمز عبور از طریق POST /api/v1/bridge/login با ارسال bridge_api_key و wp_user_id وارد لاراول می‌شوند.
  • پاسخ شامل توکن Sanctum (معتبر ۳۰ روز) و اطلاعات کاربر است.
  • کلید IFNEX_BRIDGE_API_KEY باید در هر دو طرف (.env لاراول و تنظیمات پلاگین وردپرس) یکسان باشد.
  • توکن در usermeta کاربر وردپرس (ifnex_laravel_token) ذخیره می‌شود و روی ۴۰۱ خودکار باطل و تجدید می‌شود.
  • کلید IFNEX_API_KEY کلید عمومی بخش رهگیری است و ApiKeyMiddleware آن را با !== مقایسه می‌کند؛ اگر خالی بماند، درخواست با توکن خالی از guard عبور می‌کند.
  • ⚠️ BridgeAuthController فقط مقدار change-this-secret-key را رد می‌کند. این مقدار عمداً غیرکارکردی است تا جایگزینی‌اش فراموش نشود — هر مقدار دیگری، حتی مواردی که شبیه placeholder باشند، یک کلید معتبر محسوب می‌شود.
  • 🔐 هرگز کلید واقعی را در این فایل، در DEPLOYMENT.md یا در 04_Laravel/README.md ننویس. مقادیری که قبلاً در این مخزن ثبت شده‌اند را لو‌رفته فرض کن و چرخش بده.

۴.۶ رهگیری (Tracking)

  • کاربر با شمارهٔ AWB جستجو می‌کند.
  • سیستم رویدادهای رهگیری را از جدول shipment_tracking_events با فیلد source (manual، api، import، system، customer) نمایش می‌دهد.
  • ایمپورت گروهی وضعیت ترکینگ با CSV از پنل (صفحهٔ BulkTrackingImport) انجام می‌شود.

۴.۷ قیمت‌گذاری (Pricing)

  • بر اساس ۴ زون مجزا (Export/Import × Parcel/Doc) و ۳ نوع سرویس.
  • فرمول: قیمت پایه (AED) × ضریب سود × نرخ تبدیل به ریال + هزینه‌های جانبی (Packing، Domestic Pickup، …) + مالیات بر ارزش افزوده (۹٪).
  • محموله‌های بالای ۳۰ کیلوگرم مشمول نرخ ویژه (Spot Rate) هستند و محاسبهٔ آنلاین ندارند.

۴.۸ فلوی تأیید سفارش (Approval Flow)

  • مشتری بعد از ثبت سفارش مستقیم به درگاه نمی‌رود؛ سفارش با وضعیت pending_approval ثبت می‌شود.
  • کارمند یا مدیر از پنل فیلمنت (اکشن تأیید در ShipmentResource) یا از API (/staff/orders/{id}/approve|reject) تأیید می‌کند.
  • فقط پس از approved، گزینه‌های پرداخت (کیف پول / درگاه) در وردپرس باز می‌شود. پرداخت موفق سفارش را processed می‌کند (PaymentController در callback با is_order_payment در metadata).
  • سفارش‌های approved پرداخت‌نشده روی مانده حساب کاربر (account_balance در پروفایل) اثر می‌گذارند.

۴.۹ تعهدنامه و اسناد سفارش (Commitment Forms)

  • مدیر فرم‌های تعهدنامه را در فیلمنت (CommitmentFormResource) با direction (export / import / both) آپلود می‌کند.
  • مشتری در جزئیات سفارش وردپرس لیست تعهدنامه‌ها را می‌بیند ← دانلود / چاپ / امضا ← آپلود فایل امضاشده (POST /orders/{shipment}/commitment-forms/{form}/upload) که در جدول shipment_commitment_forms ثبت می‌شود.
  • دانلود AWB، Invoice و Label نیز از همین بخش (/orders/{shipment}/pdf/*) انجام می‌شود.
  • ✅ چک مالکیت اعمال شده: ShipmentPolicy + بررسی user_id در کنترلرهای PDF و تعهدنامه (کامیت 8cf4075).
  • دانلود قالب تعهدنامه از route محافظت‌شده با auth (جایگزین asset عمومی) و دانلود ادمین از دیسک secure از طریق روت‌های admin.commitment-forms.* انجام می‌شود.

۴.۱۰ اعلان‌ها و SMS کاوه‌نگار

  • تنظیمات از SystemSetting خوانده می‌شود (kavenegar_api_key به‌همراه سوییچ هر نوع پیام، مانند kavenegar_send_shipment_approved).
  • نوتیفیکیشن‌ها: ShipmentApprovedSms، ShipmentRejectedSms، PaymentSuccessSms، TrackingUpdatedSms به‌همراه کانال SmsChannel.
  • تریگر اصلی: ShipmentObserver (روی تغییر وضعیت Shipment) و PaymentController (پرداخت موفق). تأیید موبایل: MobileVerificationController.
  • در .env لوکال کلید کاوه‌نگار خالی است؛ تا وقتی تنظیم نشود SMS ارسال نمی‌شود (فقط DB notification).

۴.۱۱ اعتبار مشتری (Credit) — چندارزی

  • سیستم کامل با CustomerCreditService (متدهای grantCredit()، settle()، getCustomerDebtsByCurrency()) و جدول‌های customer_credits و credit_settlements پیاده‌سازی شده است.
  • بدهی به همان ارز ثبت می‌شود و تسویه با نرخ روز انجام می‌گیرد. race condition در settle() با lockForUpdate() و بررسی مجدد مانده اصلاح شد.
  • فقط super_admin مجاز به اعطای اعتبار است (Policy).

۴.۱۲ Audit Log

  • trait App\Traits\Auditable روی مدل‌های اصلی (User، Shipment، Wallet و…) اعمال شده و در AuditLogResource قابل مشاهده است.
  • ShipmentStatusHistory دارای ستون‌های from_status / to_status / reason به‌همراه نام کاربر تغییردهنده است.

۴.۱۳ معماری کیف‌پول‌محور (Wallet-Centric Architecture)

سیستم مالی IFNEX بر مبنای کیف پول مشتری کار می‌کند. همه پرداخت‌ها از کیف پول عبور می‌کنند.

ساختار کیف پول:

  • هر کاربر یک کیف پول دارد (wallets table)
  • موجودی کیف پول به ریال (IRR) است
  • تراکنش‌ها در wallet_transactions ثبت می‌شوند با نوع‌های:
    • deposit — شارژ کیف پول (از درگاه یا دستی توسط ادمین)
    • withdrawal — برداشت/کسر
    • order_payment — پرداخت سفارش
    • refund — بازگشت وجه
    • credit_settlement — تسویه بدهی ارزی (با نرخ روز)
    • invoice_payment — پرداخت فاکتور دستی

جریان پرداخت (همه از کیف پول می‌گذرند):

سناریو مسیر
شارژ کیف پول درگاه Zarinpal → walletService->completeDeposit() → افزایش موجودی
پرداخت سفارش OrderPaymentService->payFromWallet() → کسر از کیف پول + ثبت order_payment
پرداخت فاکتور دستی ManualInvoiceService->payFromWallet() → کسر از کیف پول + ثبت invoice_payment
تسویه بدهی ارزی CustomerCreditService->settle() (with $fromWallet=true) → کسر از کیف پول + ثبت credit_settlement

مزایای این معماری:

  • ✅ همه پرداخت‌ها در یک جدول (wallet_transactions) ثبت می‌شوند — تاریخچه واحد
  • ✅ موجودی کیف پول منهای بدهی‌ها = مانده حساب واقعی مشتری
  • ✅ مدیر می‌تواند موجودی را به‌صورت دستی تنظیم کند (manualDeposit / manualWithdrawal)
  • ✅ قفل کیف پول (freeze) در همه پرداخت‌ها اعمال می‌شود
  • ✅ Race condition با lockForUpdate() در پرداخت‌های همزمان مدیریت می‌شود

ویژگی‌های کلیدی مدل Wallet:

$wallet->balance              // موجودی فعلی (ریال)
$wallet->isFrozen()          // آیا مسدود است؟
$wallet->hasSufficientBalance($amount)  // آیا موجودی کافی است؟
$wallet->freeze($reason, $admin)       // مسدود کردن توسط ادمین
$wallet->unfreeze($admin)              // رفع مسدودیت

قاعده طلایی:

هیچ پرداختی بدون عبور از کیف پول ثبت نمی‌شود. اگه در آینده درگاه جدیدی اضافه شود (مثلاً Stripe)، باید مبلغ ابتدا به کیف پول واریز شود (deposit) و سپس از کیف پول کسر شود (order_payment / invoice_payment).

۴.۱۴ فاکتورهای دستی مدیر (Manual Invoices)

مدیر می‌تواند برای هر مشتری فاکتور صادر کند (خدمات خارج از سفارش). مشتری در پورتالش فاکتور را می‌بیند و از کیف پول پرداخت می‌کند.

  • مدل: ManualInvoice + ManualInvoiceService + CustomerInvoiceController
  • پنل ادمین: ManualInvoiceResource در گروه «مالی»
  • پورتال وردپرس: تب «فاکتورهای دستی» با badge قرمز برای فاکتورهای pending
  • شماره فاکتور: INV-YYYY-NNNNNN (مثلاً INV-2026-000001)
  • وضعیت‌ها: pending_payment / paid / cancelled / expired
  • اعلان خودکار: ManualInvoiceCreatedNotification به مشتری ارسال می‌شود
  • پرداخت: از کیف پول مشتری (کسر + ثبت تراکنش invoice_payment)

۴.۱۵ نرخ‌های حمل و سیستم ۴-رُبعی

نرخ‌های حمل در جدول rate_entries به‌صورت ۴-رُبعی ذخیره می‌شوند:

  • جهت: import / export
  • نوع سرویس: document / parcel
  • وزن × زون: ماتریس

نگاشت زون‌ها در country_zones (هر کشور می‌تواند زون متفاوت برای Doc و Parcel داشته باشد).

نکته مهم: DOC_ECONOMY فعلاً به همان نرخ DOC_NORMAL محاسبه می‌شود. برای تفکیک کامل، باید service_type در rate_entries به ۳ مقدار گسترش پیدا کند.


۵. دستورات روزمره

# نصب وابستگی‌ها
composer install

# تنظیم محیط
cp .env.example .env
php artisan key:generate

# دیتابیس (ابتدا باید ایجاد شود)
php artisan migrate --force
php artisan db:seed --force

# اجرای سرور توسعه
php artisan serve

# اجرای صف (پردازش‌های سنگین)
php artisan queue:work

# تست‌ها
php artisan test

# پاک‌سازی و بازسازی کش
php artisan optimize:clear
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan filament:clear-cached-components

# ایمپورت / اکسپورت نرخ‌ها
php artisan ifnex:import:rates {path}
php artisan ifnex:import:shipments {path}
php artisan ifnex:import:tracking {path}

# به‌روزرسانی نرخ ارز (کرون)
php artisan ifnex:update-rates --source=ecb

# تولید API Token برای ادمین
php artisan ifnex:token

۶. متغیرهای محیطی کلیدی (.env)

APP_ENV=local
APP_DEBUG=true
APP_URL=http://localhost:8000

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=ifnex_db
DB_USERNAME=root
DB_PASSWORD=

# IFNEX اختصاصی — پیش از استقرار با رشتهٔ تصادفی جایگزین کن
# مقدار change-this-secret-key عمداً غیرکارکردی است تا جایگزینی فراموش نشود
IFNEX_API_KEY=change-this-secret-key
IFNEX_BRIDGE_API_KEY=change-this-secret-key   # باید عیناً با وردپرس یکی باشد
IFNEX_TRACKING_RATE_LIMIT=60

# CORS (فقط دامنه‌های مجاز وردپرس)
CORS_ALLOWED_ORIGINS=http://localhost,http://127.0.0.1

# Zarinpal (برای تست از Mock استفاده کن)
ZARINPAL_MERCHANT_ID=fake-merchant-id-for-testing   # اگر fake باشد، MockGateway فعال می‌شود
ZARINPAL_SANDBOX=true
ZARINPAL_CALLBACK_URL=http://localhost:8000/api/v1/payment/callback
ZARINPAL_FRONTEND_FAILURE_URL=http://localhost/IFNEX-Logistics/03_WordPress/wallet
# نکته: ZARINPAL_FRONTEND_SUCCESS_URL در کد خوانده نمی‌شود؛ ریدایرکت موفق
# از frontend_callback ارسالی وردپرس تعیین می‌شود.

# Kavenegar SMS (خالی = فقط DB notification)
KAVENEGAR_API_KEY=
KAVENEGAR_SENDER=10008566

# Wallet
WALLET_MIN_DEPOSIT=10000
WALLET_MAX_DEPOSIT=500000000
WALLET_AUTO_CREATE=true

۷. خط قرمزها (ممنوعیت‌های مطلق)

❌ هرگز ✅ همیشه
برگرداندن کشورها به ۲ زون ۴ زون مجزا (Export/Import × Parcel/Doc)
استفاده از ۲ نوع سرویس ۳ نوع (DOC_NORMAL، DOC_ECONOMY، PARCEL)
ذخیرهٔ تاریخ شمسی در DB ذخیرهٔ timestamp میلادی + تبدیل در نمایش
CORS * در Production CORS محدود به دامنهٔ وردپرس
کامیت .env در Git قرار دادن .env در .gitignore
APP_DEBUG=true در Production APP_DEBUG=false
PDF فارسی (AWB / Invoice / Label) همیشه انگلیسی (برای حمل بین‌المللی)
کپی از DHL طراحی منحصر به فرد IFNEX
استفاده از wire:click برای دانلود استفاده از <a href> با روت مستقیم
getFormActions() در Custom Pages استفاده از wire:click در Blade
هدایت AJAX به redirect() استفاده از payment_url در response JSON
متدهای نوتیفیکیشن بیرون از کلاس داخل کلاس IFNEX_User_Bridge
روت staff/admin بدون چک نقش چک نقش یا مالکیت در middleware یا ابتدای کنترلر
هدایت مستقیم سفارش به درگاه پس از ثبت فلوی pending_approval ← approved ← پرداخت
ذخیره نرخ‌ها به AED/USD ذخیره به IRR (تبدیل در ایمپورت)
نمایش قیمت به «درهم» در UI نمایش به «ریال»
استفاده از «تومان» در کد/UI فقط «ریال»
fallback عددی برای نرخ ارز خطای واضح اگه نرخ تنظیم نشده
تغییر customer_credits به IRR بدهی ارزی به ارز خارجی، تسویه با نرخ روز

۸. نکات ویژه برای ایجنت

اصول کلی

  • هنگام تولید کد جدید، حتماً از Enum‌ها به‌جای رشته‌های سخت‌کدشده استفاده کن.
  • برای هر مدل جدید، migration، مدل و در صورت نیاز کنترلر یا Resource فیلمنت بساز.
  • هنگام استفاده از هر Model در فایل جدید، حتماً use App\Models\... را import کن. (باگ اخیر نبودِ import برای SystemSetting در PaymentController / ShipmentObserver / TrackingService، فلوی تأیید و پرداخت را می‌شکست — کامیت 2737e26).
  • مدیریت تاریخ همیشه با Carbon انجام می‌شود و ذخیره به‌صورت Y-m-d H:i:s در DB است.
  • برای کوئری‌های سنگین از chunk() یا cursor() استفاده کن تا حافظه مصرف نشود.

خطاهای رایج

  • نبودِ فیلد deleted_at در کوئری‌ها (در صورت استفاده از SoftDeletes).
  • فراموشی fillable یا casts در مدل‌ها.
  • فراموشی $with برای بارگذاری روابط در Resourceها.
  • استفاده از redirect() در کنترلرهای API (باید JSON برگردانند).

قواعد حوزهٔ کار

  • برای تغییر وضعیت مرسوله، حتماً تاریخچه را به‌روز کن (ShipmentStatusHistory با from_status / to_status / reason).
  • در فرم سفارش مشتری، مرحلهٔ Invoice فقط برای نوع PARCEL نمایش داده می‌شود.
  • تولید PDF با بارکد به‌صورت base64 embed انجام می‌شود.
  • Bridge Auth نیازی به رمز عبور ندارد و فقط از IFNEX_BRIDGE_API_KEY استفاده می‌کند.
  • تست لوکال پرداخت: از ZARINPAL_MERCHANT_ID=fake-merchant-id-for-testing برای فعال‌سازی Mock Gateway استفاده کن.

هشدارهای تاریخی

  • مارک‌آپ شورت‌کدهای وردپرس: باز و بسته بودن divها را متوازن نگه دار. باگ «مرحلهٔ ۲ فرم سفارش لود نمی‌شود» ریشه‌اش یک div بسته‌نشده در shortcodes.php بود که مراحل را داخل هم nest می‌کرد. هک‌های MutationObserver و setInterval هیچ‌کدام مشکل را حل نکردند و حذف شدند.
  • هک‌های موقت دیباگ: opcache_reset() و header() در بالای فایل‌های پلاگین و cache-buster با time() فقط برای کار لوکال هستند و باید پیش از دیپلوی حذف شوند.

۹. کارهای باقی‌مانده

فاز ۳.۶ (اصلاحات جلسه کارفرما) — ✅ تکمیل (۲۰۲۶-۱۰-۰۴)

# کار وضعیت
۱ تست انتهای فلوی سفارش: آپلود تعهدنامه ← تأیید کارمند ← پرداخت کیف پول یا درگاه ✅
۲ امنیت: چک نقش /staff/*، چک مالکیت PDF و تعهدنامه، محدودسازی discount-codes، rate limiting ✅
۳ بازطراحی سیستم اعتبار چندارزی + نمایش در پورتال مشتری + ویجت هشدار داشبورد ✅
۴ چک‌لیست خودکار کارمند پس از تأیید سفارش ✅ (instantiateChecklist() در approve())
۶ پاک‌سازی: فایل‌های تستی، cache-busterها، !importantها ✅
۷ نمایش وزن واقعی و حجمی در خلاصهٔ قیمت وردپرس ✅ (در API موجود است)

فاز ۳.۷ (اصلاحات ارزی و فاکتور دستی) — ✅ تکمیل (۲۰۲۶-۱۰-۳۰)

# کار وضعیت
۱ سیستم ۴-رُبعی نرخ‌ها (Export/Import × Doc/Parcel) با ایمپورت از اکسل ✅
۲ معماری IRR-only — ارز مبنا = ریال ایران، حذف جفت‌ارز ✅
۳ ایمپورت فایل اکسل اصلی مشتری + قالب جدید IFNEX_Rate_Template.xlsx ✅
۴ حذف تمام «تومان» و «درهم» از نمایش قیمت‌های داخلی ✅
۵ فاکتورهای دستی مدیر (صدور + پرداخت از کیف پول + اعلان خودکار) ✅
۶ تب «فاکتورهای دستی» در پورتال وردپرس (لیست + جزئیات + پرداخت) ✅
۷ اضافه شدن فاکتورها به وضعیت مالی و داشبورد مشتری ✅
۸ فرمت هزارگان در فیلدهای عددی فرم فاکتور ادمین ✅

فاز ۳.۸ (کارهای سریع ظاهری) — ✅ تکمیل (۲۰۲۶-۱۰-۳۱)

# کار وضعیت
۱ Favicon IFNEX (هواپیمای کاغذی کهربایی روی سرمه‌ای — هماهنگ با هویت لاگین) در public/favicon.ico + public/images/favicon.svg + apple-touch-icon + icon-192/512 + site.webmanifest + لینک در filament/hooks/head ✅
۲ صفحهٔ / برندشده (RTL، وزیرمتن، کارت‌های «پنل مدیریت / استعلام نرخ / ثبت سفارش»، چیپ‌های وضعیت سیستم) جایگزین welcome پیش‌فرض ✅
۳ جمع‌کردن کارت لاگین فیلمنت (max-width ۴۴۰←۳۸۰px، padding ۲.۵rem←۱.۷۵rem، فاصله‌های فشرده) ✅
۴ SweetAlert2 (v11.14.5 باندل محلی در پلاگین bridge) برای خطاهای فرم سفارش: اعتبارسنجی مراحل، خطای ثبت، خطای ارتباط + جایگزینی هر ۳ alert() بومی با پاپ‌آپ برند ✅
۵ سازگارسازی migration فاکتور دستی با SQLite (تست‌ها طبق همین سند با SQLite اجرا می‌شوند) ✅

فاز ۳.۹ (دفترچه آدرس) — ✅ تکمیل (۲۰۲۶-۱۱-۰۱)

# کار وضعیت
۱ مایگریشن + مدل AddressBookEntry (سقف ۵۰ رکورد، upsert ضدتکرار، آمار استفاده) ✅
۲ CRUD API /api/v1/addresses + default-set (Sanctum + Ownership) ✅
۳ اتصال به POST /customer/orders (save flags + address_id usage) ✅
۴ dropdown آدرس ذخیره‌شده + چک‌باکس «ذخیره این آدرس» در فرم سفارش وردپرس ✅
۵ تب «دفترچه آدرس» در پنل کاربری (کارت‌ها + فیلتر + فرم + حذف/پیش‌فرض) ✅
۶ ۴ پروکسی AJAX جدید با nonce/sanitize ✅
۷ ۱۸ تست Feature جدید ✅

فاز ۳.۱۰ — دیپلوی Production + کارهای موکول‌شده به آینده

# کار وضعیت اولویت
۱ دیپلوی روی سرور Production (pull + migrate + .env سرور + ایمپورت نرخ واقعی + E2E) ⏳ قدم بعدی بالا
۲ چندزبانه (i18n) ⏳ موکول به زمان دیپلوی متوسط
۳ سند پیشنهادی سیستم مالی (درخواست ۱۳ کارفرما) ⏳ موکول به آینده پایین
۴ پلاگین SMS (به‌صورت ماژول قابل فعال‌سازی) ⏳ منتظر پنل SMS مشتری متوسط
۵ لاین واردات از چین (ماژول مجزا) ⏳ مطالعاتاتی پایین
۶ ارز مرجع قابل تنظیم (نسخه Pro) 📋 مطرح‌شده — 01_Documents/IFNEX_Pro_Backlog.md پایین (نسخه Pro)
۷ سیستم بروزرسانی + لایسنس (Domain Lock) 📋 مطرح‌شده — 01_Documents/IFNEX_Pro_Backlog.md پایین (نسخه Pro)

۱۰. مستندات مرجع

فایل محتوا
01_Documents/EXCEL_ANALYSIS.md تحلیل کامل فایل‌های اکسل (۳۹۵۰ رکورد، فرمول‌ها، زون‌ها)
01_Documents/IFNEX_Phase0_Checklist.md چک‌لیست کامل فازها (۰ تا ۳.۵)
01_Documents/IFNEX_Roadmap.md نقشهٔ راه فازی (تا فاز ۳.۶)
01_Documents/IFNEX_File_Map.md نقشهٔ ۱۰۰+ فایل پروژه
01_Documents/IFNEX_I18N_Strategy.md استراتژی چندزبانه (برای زمان دیپلوی)
01_Documents/IFNEX_Commercial_Model.md مدل تجاری و پلن‌های فروش
01_Documents/IFNEX_Pro_Backlog.md فیچرهای نسخه Pro (ارز مرجع قابل تنظیم، لایسنس، آپدیت)
01_Documents/DESIGN_SYSTEM.md رنگ‌ها، تایپوگرافی، فاصله‌گذاری
DEPLOYMENT.md راهنمای ایمپورت نرخ‌ها (سیستم ۴-رُبعی)
DEPLOYMENT_HANDOFF.md تحویل استقرار به تیم IT کارفرما
04_Laravel/README.md راهنمای بک‌اند
CLIENT_DELIVERY.md چک‌لیست وضعیت تحویل مشتری (منبع اصلی وضعیت تحویل)