From 97fa3a95611cd07f964f1e51cbb50b39bd2bd7b0 Mon Sep 17 00:00:00 2001 From: Kazem Alghasi Date: Sun, 2 Aug 2026 05:13:12 +0330 Subject: [PATCH] =?UTF-8?q?=D8=A7=D8=B6=D8=A7=D9=81=D9=87=20=D8=B4=D8=AF?= =?UTF-8?q?=D9=86=20=D9=81=D8=A7=DB=8C=D9=84=20=D9=87=D8=A7=DB=8C=20=D8=AA?= =?UTF-8?q?=D9=88=D8=B6=DB=8C=D8=AD=DB=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 01_Documents/EXCEL_ANALYSIS.md | 880 ++++++++++++++++++++++++++++++++ 01_Documents/PRD_v2.md | 4 + 01_Documents/Project_Roadmap.md | 4 + 01_Documents/STATUS.md | 322 ++++++++++++ 4 files changed, 1210 insertions(+) create mode 100644 01_Documents/EXCEL_ANALYSIS.md create mode 100644 01_Documents/STATUS.md diff --git a/01_Documents/EXCEL_ANALYSIS.md b/01_Documents/EXCEL_ANALYSIS.md new file mode 100644 index 0000000..45107a6 --- /dev/null +++ b/01_Documents/EXCEL_ANALYSIS.md @@ -0,0 +1,880 @@ +# 📊 EXCEL_ANALYSIS.md — تحلیل کامل فایل‌های اکسل عملیاتی + +> **هدف:** مرجع کامل برای هر توسعه‌دهنده‌ای که با داده‌های تاریخی IFNEX کار می‌کند +> **فایل‌های تحلیل‌شده:** دو فایل اکسل آپلودشده توسط مشتری در جلسه اولیه +> **تاریخ تحلیل:** August 2026 +> **وضعیت:** کامل — برای مهاجرت داده و طراحی اسکیمای دیتابیس استفاده شود + +--- + +## 📁 فهرست فایل‌های تحلیل‌شده + +### فایل ۱: `4_5989927490271846355.xlsx` (فایل اصلی عملیاتی) +این فایل قلب کسب‌وکار IFNEX است. شامل ۱۵ شیت است که تمام منطق کسب‌وکار، داده‌های تاریخی و قالب‌های خروجی را در خود جای داده. + +**شیت‌ها:** +1. `Start` — خالی (صفحه شروع) +2. `Form` — فرم ثبت یک مرسوله (۳۴ ردیف، ۱۱۵ ستون) +3. `List` — لیست کامل مرسولات تاریخی (**۳۹۵۰ ردیف**، ۱۰۲ ستون) +4. `COUNTRIES` — جدول ۲۳۳ کشور با زون‌ها +5. `AWB` — قالب بارنامه (Air Waybill) +6. `INVOICE + label` — قالب فاکتور +7. `label` — قالب لیبل +8. `Import Rate` — جدول نرخ‌های واردات (۳۸۵ ردیف) +9. `Export Rate` — جدول نرخ‌های صادرات (۳۸۵ ردیف) +10. `Zone` — جدول زون‌بندی کشورها (۲ بخش: PARCEL و DOCUMENT) +11. `DocNor` — جدول قیمت Document Normal +12. `Parcel` — جدول قیمت Parcel +13. `DocEco` — جدول قیمت Document Economy +14. `Assumptions` — تنظیمات (VAT، Packing، روزهای هفته، شهرها) +15. `DATES` — تقویم میلادی به جلالی (۷۳۲ ردیف) + +### فایل ۲: `Data entry 2026-06-28.xlsx` (فایل ترکینگ دستی) +این فایل به‌صورت روزانه توسط اپراتورها پر می‌شود و شامل داده‌های ترکینگ مرسولات است. در فاز ₀، این فایل باید با پنل Filament جایگزین شود. + +**شیت‌ها:** +1. `Sheet1` — لیست رویدادهای ترکینگ (۳۱۱ ردیف) +2. `Refrence` — نگاشت کد AWB به کد ترکینگ خارجی (۱۵ ردیف) +3. `Paste` — داده خام کپی‌شده از وب‌سایت‌های DHL/FedEx (۴۰۴ ردیف) +4. `copy` — نسخه‌ی تمیزشده Sheet1 (۴۱۵ ردیف) +5. `Delivered` — لیست مرسولات تحویل‌شده (۹۴ ردیف) +6. `test` — آزمایش‌های اپراتور (۳۹۶ ردیف) + +--- + +## 🗂️ تحلیل شیت به شیت — فایل اصلی + +### شیت ۱: `Form` — فرم ثبت یک مرسوله + +**ساختار:** ۳۴ ردیف × ۱۱۵ ستون (فرم افقی، نه جدولی) + +**محتوای کلیدی:** +- اطلاعات مسیر (Route Information): HAWB No.، Date، Forwarder، From، To، Zone، Service، Type +- اطلاعات بسته (Shipment): Content، Weight، Volumetric Weight، Chargable Weight، Dimensions (W×L×H) +- اطلاعات فرستنده (Shipper): Company Name، Contact، Telephone، Email، Address، City، Zip، ID Number +- اطلاعات گیرنده (Receiver): همان فیلدها +- اطلاعات مالی (Payment): Shipping Price، Extra Service، Domestic Pickup، Packing Cost، Domestic Delivery، Warehousing Cost، Discount، Total Fee، Cash on Delivery +- اقلام گمرکی (۹ ردیف): No.، Description، H.S. Code، Quantity، Unit Price، Total in USD +- تنظیمات فرمول: Percent (ضریب سود)، نرخ ارز + +**نکته مهم:** این شیت نشان می‌دهد **۹ ردیف کالای گمرکی** در هر مرسوله قابل ثبت است. این باید در فرم ثبت سفارش آنلاین (فاز ۱) لحاظ شود. + +**نمونه داده واقعی:** +``` +Date: 2026-07-31 +Forwarder: (خالی) +From: Iran (IR) +To: USA (US) +Zone: 3 +Service: Outbound +Type: NON DOC +Weight: 0 kg, Volumetric: 0 kg +Content: ITEM BEING SENT AS A GIFT N... +Status: Processed +``` + +**نگاشت به دیتابیس:** این شیت اساس طراحی جدول `shipments` و `shipment_items` در فاز ۱ است. + +--- + +### شیت ۲: `List` — لیست کامل مرسولات تاریخی ⭐ حیاتی برای مهاجرت + +**ساختار:** ۳۹۵۰ ردیف × ۱۰۲ ستون + +**توضیح:** این شیت، جدول اصلی داده‌های تاریخی IFNEX است. هر ردیف یک مرسوله از سال ۲۰۲۰ تا الان. **این داده‌ها باید در فاز ₀ به جدول `shipments` مهاجرت داده شوند.** + +**ستون‌های کلیدی (ردیف ۱ و ۲ سرتیتر هستند):** + +| # | ستون | نوع | مثال | نگاشت به DB | +|---|------|-----|------|-------------| +| ۱ | HAWB No. | String | 980100010 | `shipments.awb_no` | +| ۲ | Date | DateTime | 2020-05-02 | `shipments.created_at` | +| ۳ | Forwarder | String | DHL / FedEx / 0 | `shipment_carrier_mappings.carrier_code` | +| ۴ | From | String | Iran (IR) | `shipments.from_country_id` | +| ۵ | To | String | Germany | `shipments.to_country_id` | +| ۶ | Zone | Integer | 3 | (محاسبه می‌شود — ذخیره نمی‌شود) | +| ۷ | Service | Enum | Outbound / Inbound | `shipments.direction` | +| ۸ | Type | Enum | DocNor / NON DOC | `shipments.type` (تبدیل به DOC_NORMAL/DOC_ECONOMY/PARCEL) | +| ۹ | Weight | Decimal | 0.5 | `shipments.weight` | +| ۱۰ | Volumetric W. | Decimal | 0 | `shipments.volumetric_weight` | +| ۱۱ | Value | Decimal | 0 | (فاز ۱ — فیلد ارزش محموله) | +| ۱۲ | Content | String | EDUCATINAL DOCUEMTS | `shipments.content_description` | +| ۱۳ | Chargable Weight | Decimal | 0.5 | `shipments.chargeable_weight` | +| ۱۴-۱۶ | WIDHTH/LENGTH/HEIGHT | Integer | 25, 15, 3 | `shipments.dimensions` (به‌صورت JSON یا فیلد جداگانه) | +| ۱۷ | Third party | Boolean | 0/1 | (فاز ۱) | +| ۱۸-۲۴ | Sender fields | String | (متغیر) | `shipments.sender_*` | +| ۲۵-۳۲ | Receiver fields | String | (متغیر) | `shipments.receiver_*` | +| ۳۳-۴۰ | Price fields | Decimal | (متغیر) | `shipments.shipping_price`، `extra_service`، `packing_cost`، `discount`، `total_fee` | +| ۴۱ | REASON FOR EXPORT | String | ITEM BEING SENT AS A SAMPLE... | `shipments.reason_for_export` | +| ۴۲-۸۶ | ۹ ردیف کالای گمرکی | String | (متغیر) | `shipment_items` (۹ ردیف) | +| ۸۷ | TOTAL INVOICE AMOUNT IN USD | Decimal | 114 | `shipments.invoice_total_usd` | +| ۸۸ | Last State | String | Processed | `shipments.status` (تبدیل شود) | +| ۸۹ | Last Load | DateTime | 2025-05-26 | (metadata) | +| ۹۰ | Net Dirham | Decimal | 160.69 | `shipments.net_dirham` | +| ۹۱ | Net Rial | Decimal | 73113385.32 | `shipments.net_rial` | + +**نمونه رکورد کامل (ردیف ۴ — 980100011):** +``` +AWB: 980100011 +Date: 2026-02-26 +Forwarder: 0 +From: Iran (IR) +To: China (CN) +Zone: 3 +Service: Outbound +Type: NON DOC +Weight: 0.1 kg, Volumetric: 0.225 kg +Value: 60 USD +Content: Electronics PCB Board +Chargeable Weight: 0.5 +Dimensions: 25×15×3 cm +Sender: Akbar Salmanizadeh, +989132027178, Isfahan, IR, ID: 1283855623 +Receiver: Chinapcbone Technology LTD, Ms Bindy Zhang, +8615814401212, SHENZHEN, CN, ZIP: 518103 +Items: + 1. IC LT1668, HS: 8542390001, Qty: 104, Unit: $1.1, Total: $114 +Status: Processed +Net Dirham: 160.69 +Net Rial: 73,113,385.32 +``` + +**نکات مهاجرت:** +- ردیف ۱ و ۲ سرتیتر هستند — ردیف ۳ به بعد داده واقعی +- ردیف‌های خالی زیاد است — اسکریپت باید آنها را فیلتر کند +- نام کشورها به فرمت `Iran (IR)` است — باید به `iso_code` تبدیل شود +- فیلد `Type` در اکسل شامل مقادیر متنوع است: `DocNor`، `NON DOC`، `Outbound`، `Inbound` — نیاز به استانداردسازی به enum سه‌حالته +- فیلد `Service` در اکسل با فیلد `direction` در DB یکی است (Outbound=export, Inbound=import) +- تاریخ‌ها میلادی هستند (نه جلالی) — خوب، نیازی به تبدیل نیست + +--- + +### شیت ۳: `COUNTRIES` — جدول کشورها + +**ساختار:** ۲۳۵ ردیف × ۵ ستون + +**ستون‌ها:** +1. `COUNTRIES` — نام کشور با کد ISO در پرانتز، مثلاً `Afghanistan (AF)` +2. `EXPORT ZONES` — زون صادرات (عدد ۱ تا ۱۰) +3. `IMPORT ZONES` — زون واردات (عدد ۱ تا ۱۰) +4. `Service` — فقط چند ردیف اول پر است (مثلاً `Inbound`، `Outbound`، `Visa Pick Up`) — به‌نظر می‌رسد دستی وارد شده، نادیده بگیر +5. `Content` — فقط چند ردیف اول پر است (`DOC`، `NON DOC`) — نادیده بگیر + +**نکته مهم:** این شیت، **منبع نهایی زون‌بندی نیست**. شیت `Zone` (شیت ۱۰) منبع دقیق‌تری است چون زون‌های مجزا برای PARCEL و DOCUMENT دارد. اما این شیت (`COUNTRIES`) برای تأیید تعداد کشورها (۲۳۳ کشور واقعی، با چند مورد تکراری) استفاده می‌شود. + +**نمونه داده:** +``` +Afghanistan (AF) | 7 | 8 +Albania (AL) | 6 | 8 +Algeria (DZ) | 6 | 8 +... +Iran (IR) | (خالی — ایران مبدا/مقصد داخلی است) +... +Yemen (YE) | 7 | 8 +``` + +**نگاشت به دیتابیس:** این شیت فقط برای استخراج `name` و `iso_code` استفاده می‌شود. زون‌ها از شیت `Zone` (که دقیق‌تر است) گرفته می‌شوند. + +--- + +### شیت ۴: `AWB` — قالب بارنامه (Air Waybill) + +**ساختار:** ۱۸۵۶ ردیف × ۱۵ ستون (قالب افقی، چند بارنامه در یک شیت) + +**محتوا:** قالب PDF بارنامه IFNEX. هر بارنامه شامل: +- شماره AWB (مثلاً 980100011) +- تاریخ +- لوگوی IFNEX (با متن "We Deliver Value") +- اطلاعات فرستنده و گیرنده (در دو بلوک) +- اطلاعات بسته (وزن، ابعاد) +- اطلاعات پرداخت (قیمت به IRR) +- بارکد (در اکسل تصویر، در لاراول باید تولید شود) + +**نکته:** این قالب باید در فاز ۱ به‌صورت PDF در لاراول بازسازی شود. خروجی PDF باید **انگلیسی** باشد (مطابق اکسل اصلی). + +**نگاشت:** قالب PDF با Dompdf یا Snappy در لاراول — فاز ۱. + +--- + +### شیت ۵: `INVOICE + label` — قالب فاکتور + +**ساختار:** ۱۸۳۵ ردیف × ۱۲ ستون + +**محتوا:** قالب فاکتور تجاری (Commercial Invoice) شامل: +- INVOICE NO (همان AWB) +- DATE +- SHIPPER (نام شرکت، آدرس، تلفن، ایمیل) +- CONSIGNEE (همان فیلدها برای گیرنده) +- لیست اقلام (Description، HS Code، Quantity، Unit Price، Total) +- TOTAL INVOICE AMOUNT IN USD + +**نکته:** در فاز ۱، این قالب به‌صورت PDF تولید می‌شود. فیلدهای invoice باید با فیلدهای `shipment_items` در دیتابیس منطبق باشند. + +--- + +### شیت ۶: `label` — قالب لیبل + +**ساختار:** ۱۱ ردیف × ۱۳ ستون + +**محتوا:** قالب لیبل چاپی برای چاپگرهای حرارتی. شامل: +- شماره AWB (بارکد) +- وزن ناخالص (Gross Weight) +- ابعاد (W×L×H) +- وزن حجمی (Volumetric) +- تاریخ +- کشور مبدا و مقصد + +**نکته:** در فاز ۱، این قالب به‌صورت PDF کوچک (مثلاً ۱۰۰×۱۰۰ میلی‌متر) تولید می‌شود. مخصوص چاپگرهای حرارتی دفتر اصفهان. + +--- + +### شیت ۷: `Import Rate` — نرخ‌های واردات + +**ساختار:** ۳۸۵ ردیف × ۱۱ ستون + +**ساختار جدول:** +- ردیف ۳: عنوان "ROW TO IRAN - IMPORT RATE SCHEDULE" +- ردیف ۴: زیرعنوان "DOCUMENT" +- ردیف ۵: سرتیتر ستون‌ها — `Weight (kg) | Zone 1 | Zone 2 | Zone 3 | ... | Zone 10` +- ردیف ۶ به بعد: قیمت پایه به **درهم (AED)** برای هر ترکیب وزن × زون +- ردیف ۱۰: زیرعنوان "NON - DOCUMENT" +- ردیف ۱۱: سرتیتر (تکراری) +- ردیف ۱۲ به بعد: قیمت برای NON-DOC + +**نمونه داده (DOCUMENT):** +``` +Weight | Zone 1 | Zone 2 | Zone 3 | Zone 4 | Zone 5 | Zone 6 | Zone 7 | Zone 8 | Zone 9 | Zone 10 +0.5 | 149.32 | 169.67 | 214.14 | 231.90 | 242.64 | 255.16 | 273.96 | 383.02 | 85.71 | 85.71 +1.0 | 197.20 | 195.61 | 253.99 | 245.99 | 295.54 | 308.56 | 329.35 | 390.16 | 92.86 | 92.86 +1.5 | 243.41 | 237.62 | 301.99 | 272.61 | 348.30 | 348.30 | 409.41 | 429.65 | 100 | 100 +2.0 | 280.61 | 282.51 | 317.42 | 290.62 | 384.39 | 384.39 | 494.54 | 474.51 | 114.29 | 114.29 +``` + +**نکته مهم:** این جدول فقط ۲ بخش دارد (DOCUMENT و NON-DOCUMENT). اما شیت‌های `DocNor`، `DocEco`، `Parcel` نشان می‌دهند که در واقع **۳ نوع سرویس** وجود دارد. تضاد وجود دارد: + +- شیت `Import Rate` فقط ۲ نوع دارد (DOCUMENT و NON-DOC) +- شیت‌های جداگانه `DocNor`، `DocEco`، `Parcel` قیمت‌های متفاوتی نشان می‌دهند + +**تفسیر:** احتمالاً شیت `Import Rate` جدول قدیمی است و شیت‌های DocNor/DocEco/Parcel نسخه‌ی جدیدتر و دقیق‌تر هستند. در فاز ۱، باید این موضوع را با مشتری تأیید کرد. فعلاً در فاز ۰، فقط جدول `shipping_rates` را با فیلد `type` از نوع enum سه‌حالته (`DOC_NORMAL`, `DOC_ECONOMY`, `PARCEL`) طراحی می‌کنیم. + +**نگاشت به دیتابیس:** جدول `shipping_rates` در فاز ۱ (طبق `Phase0_Proposal.md` بخش ۶.۲). + +--- + +### شیت ۸: `Export Rate` — نرخ‌های صادرات + +**ساختار:** مشابه شیت Import Rate — ۳۸۵ ردیف × ۱۱ ستون + +**عنوان:** "IRAN TO ROW - EXPORT RATE SCHEDULE" + +**نکته:** ساختار و تضادها دقیقاً مشابه شیت Import Rate است. + +--- + +### شیت ۹: `Zone` — جدول زون‌بندی کشورها ⭐ منبع نهایی زون‌ها + +**ساختار:** ۲۳۰ ردیف × ۷ ستون (دو جدول کنار هم) + +**چیدمان:** +- ستون ۱-۳: جدول PARCEL (شماره، کشور، زون) +- ستون ۵-۷: جدول DOCUMENT (شماره، کشور، زون) + +**نمونه داده:** +``` +PARCEL DOCUMENT +No. | Country | Zone No. | Country | Zone +1 | Afghanistan | 7 1 | Afghanistan | 5 +2 | Albania | 3 2 | Albania | 7 +3 | Algeria | 4 3 | Algeria | 7 +4 | Americam Samoa | 7 4 | Andorra | 7 +5 | Andorra | 3 5 | Angola | 6 +6 | Angola | 7 6 | Anguilla | 6 +7 | Anguilla | 7 7 | Antigua | 6 +8 | Antigua | 7 8 | Argentina | 7 +9 | Argentina | 7 9 | Armenia | 7 +10 | Armenia | 3 10 | Aruba | 7 +11 | Aruba | 7 11 | Australia | 6 +12 | Australia | 7 12 | Austria | 3 +``` + +**⚠️ کشف کلیدی:** همان کشور برای PARCEL و DOCUMENT زون‌های متفاوتی دارد! + +| کشور | PARCEL Zone | DOCUMENT Zone | تفاوت | +|------|-------------|---------------|-------| +| Afghanistan | 7 | 5 | ۲ | +| Albania | 3 | 7 | ۴ | +| Algeria | 4 | 7 | ۳ | +| Armenia | 3 | 7 | ۴ | +| Australia | 7 | 6 | ۱ | +| Austria | 3 | 3 | ۰ | + +**نتیجه:** جدول `countries` باید **۴ زون مجزا** داشته باشد: +- `export_zone_parcel` — زون صادرات برای پارسل +- `export_zone_doc` — زون صادرات برای داکیومنت +- `import_zone_parcel` — زون واردات برای پارسل +- `import_zone_doc` — زون واردات برای داکیومنت + +این موضوع در `Phase0_Proposal.md` بخش ۶.۱ منعکس شده. هرگز به ۲ زون برگردان. + +**نکته:** شیت `Zone` فقط زون‌های export را دارد (از ایران به سایر کشورها). زون‌های import باید از شیت `COUNTRIES` استخراج شوند یا از مشتری درخواست شود. + +--- + +### شیت ۱۰: `DocNor` — جدول قیمت Document Normal + +**ساختار:** ۷۱ ردیف × ۳ ستون + +**فرمت:** Long format (نه Wide) +``` +Weight | Attribute | Value +0.5 | 1 | 2,810,429.42 +0.5 | 2 | 3,600,277.54 +0.5 | 3 | 5,104,556.32 +0.5 | 4 | 5,641,878.04 +0.5 | 5 | 6,108,148.95 +0.5 | 6 | 7,213,877.11 +0.5 | 7 | 8,289,630.71 +1.0 | 1 | 4,049,673.89 +... +``` + +**تفسیر:** +- `Weight` — وزن (۰.۵، ۱، ۱.۵، ۲، ۲.۵ کیلوگرم) +- `Attribute` — شماره زون (۱ تا ۱۰) +- `Value` — قیمت **به ریال ایران (IRR)** + +**نکته:** برخلاف شیت‌های Import/Export Rate که قیمت به **درهم** بود، اینجا قیمت به **ریال** است. این یعنی فرمول تبدیل (درهم × ضریب سود × نرخ روز درهم = ریال) در این شیت اعمال شده. + +**نگاشت:** این داده‌ها در فاز ۱ به جدول `shipping_rates` با `type = DOC_NORMAL` مهاجرت داده می‌شوند. اما به‌جای ذخیره ریال، باید قیمت پایه درهم را ذخیره کنیم (مطابق شیت Import/Export Rate). + +--- + +### شیت ۱۱: `Parcel` — جدول قیمت Parcel + +**ساختار:** ۱۴۱ ردیف × ۳ ستون (مشابه DocNor) + +**نکته:** وزن‌ها تا ۵ کیلوگرم (یا بیشتر) می‌رسد — برای پارسل محدوده وزن بیشتر است. + +**نگاشت:** جدول `shipping_rates` با `type = PARCEL`. + +--- + +### شیت ۱۲: `DocEco` — جدول قیمت Document Economy + +**ساختار:** ۷۱ ردیف × ۳ ستون (مشابه DocNor) + +**نگاشت:** جدول `shipping_rates` با `type = DOC_ECONOMY`. + +--- + +### شیت ۱۳: `Assumptions` — تنظیمات + +**ساختار:** ۲۵۱ ردیف × ۱۷ ستون (ترکیبی از تنظیمات و جداول کمکی) + +**محتوای کلیدی:** +- ردیف ۳-۶: تنظیمات سرویس‌ها و VAT + - `DocNor` با VAT: ۰.۰۹ (۹٪) و Packing: ۱۰۰,۰۰۰ ریال + - `DocEco` — خالی + - `Parcel` — خالی +- ردیف ۳-۹ (ستون ۸-۹): روزهای هفته (میلادی و شمسی) +- ردیف ۳-۹ (ستون ۱۴-۱۵): شهرهای ایران با کد (اصفهان=۰۱، شیراز=۰۲، مشهد=۰۳، ...) +- ردیف ۱۰ به بعد (ستون ۱۷): لیست کشورها (به ترتیب حروف الفبا) + +**⚠️ کشف کلیدی — VAT:** +VAT در فایل اکسل ۹٪ است (`0.09`). این فیلد در PRD قدیمی ذکر نشده بود. در `Phase0_Proposal.md` به فیلدهای مالی جدول `shipments` اضافه شده. + +**⚠️ کشف کلیدی — Packing:** +هزینه بسته‌بندی پیش‌فرض: ۱۰۰,۰۰۰ ریال. این فیلد هم در PRD غایب بود. + +**نگاشت به دیتابیس:** این مقادیر در جدول `system_settings` ذخیره می‌شوند: +```sql +('vat_rate', 0.09, 'VAT rate — 9%'), +('packing_cost_default', 100000, 'Default packing cost in IRR'), +('profit_margin', 1.25, 'Profit margin — set per company'), +('aed_to_irr', [نرخ روز], 'AED to IRR exchange rate'), +``` + +--- + +### شیت ۱۴: `DATES` — تقویم میلادی به جلالی + +**ساختار:** ۷۳۲ ردیف × ۱۲ ستون + +**ستون‌ها:** Miladi، Jalali_1، Jalali_2، Jalali_3، myear، jyear، mmonthN، jmonthN، mmonthT، jmonthT، mnime، jnime + +**نکته:** این جدول روش قدیمی IFNEX برای تبدیل تاریخ بوده. در لاراول نیازی به این نیست — از پکیج `morilog/jalali` استفاده می‌شود. این شیت در مهاجرت نادیده گرفته می‌شود. + +--- + +## 🗂️ تحلیل شیت به شیت — فایل ترکینگ دستی + +### شیت ۱: `Sheet1` — لیست رویدادهای ترکینگ + +**ساختار:** ۳۱۱ ردیف × ۸ ستون + +**ستون‌ها:** +1. `AWB` — شماره بارنامه IFNEX (مثلاً 980103619) +2. `Last State` — آخرین وضعیت (مثلاً "Failed attempt") — فقط در موارد خاص پر شده +3. `Date` — تاریخ رویداد +4. `Time` — زمان رویداد +5. `State` — توضیح رویداد (مثلاً "Picked up by Naqel") +6. `Country` — لوکیشن (مثلاً DUBAI، MUSCAT - OMAN) +7. `Location` — معمولاً "." (نقطه) +8. `Order No` — شماره ترتیب رویداد + +**نمونه داده (بارنامه 980103619):** +``` +AWB | Last State | Date | Time | State | Country | Location | Order No +980103619 | | 2026-06-20 | 06:35 | Waybill created. Shipment h... | SYSTEM | . | 9 +980103619 | | 2026-06-24 | 02:56 | Picked up by Naqel | DUBAI | . | 10 +980103619 | | 2026-06-24 | 03:01 | Arrived at Naqel Facility | DUBAI | . | 11 +980103619 | | 2026-06-24 | 03:06 | Re-Weight | DUBAI | . | 12 +980103619 | | 2026-06-24 | 07:05 | Out For Delivery with Courier | DUBAI | . | 13 +980103619 | | 2026-06-24 | 07:57 | Prepared for delivery | DUBAI | . | 14 +980103619 | | 2026-06-24 | 17:16 | Delivery attempted – City/A... | AJMAN | . | 15 +980103619 | | 2026-06-25 | 07:27 | Out For Delivery with Courier | DUBAI | . | 16 +980103619 | | 2026-06-25 | 10:34 | Prepared for delivery | DUBAI | . | 17 +980103619 | Failed attempt| 2026-06-25 | 19:43 | Delivery attempted – Consig... | DUBAI | . | 18 +``` + +**نگاشت به دیتابیس:** این شیت مستقیماً به جدول `shipment_tracking_events` نگاشت می‌شود: +- `AWB` → `shipment_id` (با lookup در `shipments`) +- `Date` + `Time` → `event_date` + `event_time` +- `State` → `event_description` +- `Country` → `location` +- `Last State` → `delivery_status` (اگر پر شده باشد) +- `Order No` → نادیده (ترتیب با timestamp مشخص می‌شود) + +**نکته مهاجرت:** این داده‌ها می‌توانند در فاز ₀ به `shipment_tracking_events` با `source = 'manual'` مهاجرت داده شوند. + +--- + +### شیت ۲: `Refrence` — نگاشت کد AWB به کد ترکینگ خارجی ⭐ حیاتی + +**ساختار:** ۱۵ ردیف × ۸ ستون + +**ستون‌ها:** +1. `Tracking Number` — کد ترکینگ شرکت خارجی +2. `Reference Number` — شماره AWB داخلی IFNEX +3. `Delivery` — وضعیت تحویل (مثلاً "Delivered") +4. `Booking Number` — شماره رزرو +5. `IM/EX` — شرکت حمل (DHL، Naghel، UPS، ...) +6. `To Country` — کشور مقصد +7. `Item Description` — توضیحات (گاهی شامل هزینه‌های اضافی مثل "100.00AED disposal charge") +8. `Delivery Agent` — عامل تحویل + +**نمونه داده:** +``` +Tracking Number | Reference Number | Delivery | Booking Number | IM/EX | To Country | Item Description +2329941040 | 980103609 | | | DHL | | +408638268 | 980103612 | | | Naghel | | +8252492625 | 980103613 | | | DHL | | +7095511345 | 980103614 | | | DHL | | +1Z483Y5W0491404336 | 980103079 | 991035590| | | China | UPS, 100.00AED disposal charge +``` + +**⚠️ کشف کلیدی — چندین شرکت حمل:** +این شیت نشان می‌دهد که شرکت‌های حمل زیر در سیستم IFNEX فعال هستند: + +| کد | نام شرکت | نوع | پشتیبانی API | +|----|----------|-----|--------------| +| DHL | DHL Express | بین‌المللی | ✅ | +| FEDEX | FedEx | بین‌المللی | ✅ | +| UPS | UPS | بین‌المللی | ✅ | +| ARAMEX | Aramex | منطقه‌ای (خاورمیانه) | ✅ | +| NAGHEL / Naqel | Naqel | منطقه‌ای (امارات) | ⚠️ محدود | +| EMX | EMX | منطقه‌ای | ❌ | +| APSITEX | APSITEX | داخلی ایران | ❌ | +| IMPEX | IMPEX | منطقه‌ای | ❌ | + +**نتیجه:** فقط حدود ۵۰-۶۰٪ مرسولات در فاز ۳ از طریق API قابل ترکینگ خودکار هستند. بقیه همچنان دستی می‌مانند. این موضوع در `Phase0_Proposal.md` بخش ۷.۴ لحاظ شده. + +**⚠️ کشف کلیدی — هزینه‌های جانبی متغیر:** +ردیف ۱۵ نشان می‌دهد که گاهی هزینه‌های اضافی در زمان تحویل از سمت شرکت حمل اعمال می‌شود (مثلاً "100.00AED disposal charge"). این فیلد باید در `shipment_carrier_mappings.notes` ذخیره شود و در محاسبه سود نهایی لحاظ گردد. + +**نگاشت به دیتابیس:** این شیت مستقیماً به جدول `shipment_carrier_mappings` نگاشت می‌شود: +- `Tracking Number` → `carrier_tracking_number` +- `Reference Number` → `shipment_id` (با lookup) +- `Booking Number` → `booking_number` +- `IM/EX` → `carrier_code` (تبدیل به enum) +- `To Country` → (نادیده یا ذخیره در metadata) +- `Item Description` → `notes` +- `Delivery` → `delivery_status` + +--- + +### شیت ۳: `Paste` — داده خام کپی‌شده + +**ساختار:** ۴۰۴ ردیف × ۱۱ ستون + +**توضیح:** این شیت داده‌های خام کپی‌شده از وب‌سایت‌های DHL/FedEx/UPS را در خود دارد. اپراتور این داده‌ها را پردازش می‌کند و به `Sheet1` و `copy` منتقل می‌کند. + +**ستون‌ها:** Tracking number (دو ستون)، شماره ترتیب N، اطلاعات Origin/Destination، Date، Local Time، Location، Event label، Delivery status + +**نکته:** این شیت برای مهاجرت نادیده گرفته می‌شود — داده‌های تمیز در `Sheet1` و `copy` هستند. + +--- + +### شیت ۴: `copy` — نسخه تمیزشده + +**ساختار:** ۴۱۵ ردیف × ۹ ستون + +**توضیح:** نسخه‌ی پاکسازی‌شده‌ی Sheet1 با فیلدهای مرتب‌تر. ستون‌ها: +1. Tracking number +2. AWB +3. Delivery status +4. Date +5. Local Time +6. Event label +7. Location +8. (خالی) +9. Order No + +**نکته:** این شیت و `Sheet1` تکراری هستند. برای مهاجرت از `Sheet1` استفاده کنید چون کامل‌تر است. + +--- + +### شیت ۵: `Delivered` — لیست مرسولات تحویل‌شده + +**ساختار:** ۹۴ ردیف × ۸ ستون + +**توضیح:** لیست مرسولاتی که با موفقیت تحویل داده شده‌اند. شامل: +- Tracking Number +- Reference Number (AWB) +- Sl No (معمولاً "Delivered" یا شماره) +- Booking Number +- Company Code (مثلاً 1012، IMPEX) +- To Country +- Item Description +- Delivery Agent + +**نکته:** این شیت برای آمارگیری استفاده می‌شود. در دیتابیس نیازی به آن نیست — می‌توان با query روی `shipment_tracking_events` لیست مشابه تولید کرد. + +--- + +### شیت ۶: `test` — آزمایش‌های اپراتور + +**ساختار:** ۳۹۶ ردیف × ۸ ستون (با چند ساختار متفاوت در یک شیت) + +**توضیح:** شیت آزمایشی اپراتور. داده‌ها معتبر نیستند و برای مهاجرت نادیده گرفته می‌شوند. + +--- + +## 🔢 فرمول قیمت‌گذاری (Pricing Formula) + +بر اساس شیت `Form` و `Assumptions`، فرمول کامل محاسبه قیمت نهایی: + +### مراحل محاسبه + +#### مرحله ۱: محاسبه وزن حجمی +``` +Volumetric Weight (kg) = (Width × Length × Height) / 5000 +``` +- ابعاد به سانتی‌متر +- تقسیم بر ۵۰۰۰ استاندارد جهانی هوایی + +#### مرحله ۲: تعیین وزن قابل پرداخت +``` +Chargeable Weight = MAX(Actual Weight, Volumetric Weight) +``` + +#### مرحله ۳: استخراج زون +``` +Zone = lookup(country, direction, type) +``` +- اگر direction=export و type=PARCEL → از `countries.export_zone_parcel` +- اگر direction=export و type=DOC_* → از `countries.export_zone_doc` +- اگر direction=import و type=PARCEL → از `countries.import_zone_parcel` +- اگر direction=import و type=DOC_* → از `countries.import_zone_doc` + +#### مرحله ۴: استخراج قیمت پایه (به درهم) +``` +Base Price (AED) = shipping_rates[direction, type, chargeable_weight, zone] +``` + +#### مرحله ۵: اعمال ضریب سود +``` +After Profit (AED) = Base Price × Profit Margin (مثلاً 1.25) +``` + +#### مرحله ۶: تبدیل به ریال +``` +After Conversion (IRR) = After Profit (AED) × AED_to_IRR_Rate +``` + +#### مرحله ۷: اضافه هزینه‌های جانبی +``` +Subtotal (IRR) = After Conversion + + Extra Service (IRR) + + Domestic Pickup (IRR) + + Packing Cost (IRR) + + Domestic Delivery (IRR) + + Warehousing Cost (IRR) + - Discount (IRR) +``` + +#### مرحله ۸: اعمال VAT +``` +Total Fee (IRR) = Subtotal × (1 + VAT Rate) +``` +- VAT Rate = 0.09 (۹٪) — از شیت Assumptions + +#### مرحله ۹: ذخیره خروجی‌ها +``` +shipments.shipping_price = Base Price (AED) +shipments.net_dirham = After Profit (AED) +shipments.net_rial = Total Fee (IRR) +shipments.total_fee = Total Fee (IRR) +``` + +### نکته‌ی مهم: محموله‌های بالای ۳۰ کیلوگرم + +طبق PRD قدیمی (بخش ۳.۱): «محموله‌های بالای ۳۰ کیلوگرم مشمول نرخ‌های ویژه (Spot Rate) هستند و محاسبه آنلاین ندارند و باید توسط ادمین تأیید شوند.» + +این قانون باید در فرم استعلام قیمت (فاز ۱) لحاظ شود: اگر وزن > ۳۰ کیلوگرم، به‌جای محاسبه، پیام «کارشناس ما تماس می‌گیرد» نمایش داده شود. + +### نمونه محاسبه واقعی (از شیت List، AWB 980100011) + +``` +Direction: Export (Outbound) +Type: NON DOC → PARCEL +From: Iran (IR) +To: China (CN) +Weight: 0.1 kg +Volumetric Weight: (25 × 15 × 3) / 5000 = 0.225 kg +Chargeable Weight: MAX(0.1, 0.225) = 0.225 kg → 0.5 (طبق اکسل) +Zone: 3 (از شیت List) +Items: + 1. IC LT1668, HS: 8542390001, Qty: 104, Unit: $1.1, Total: $114 +Invoice Total: $114 +Net Dirham: 160.69 +Net Rial: 73,113,385.32 + +محاسبه: +Base Price (AED) = ? (از جدول Parcel) +After Profit (AED) = Base × 1.25 +Net Dirham = 160.69 → پس Base Price = 160.69 / 1.25 = 128.55 AED +AED_to_IRR = 73,113,385.32 / 160.69 ≈ 455,057 IRR per AED +``` + +این تحلیل نشان می‌دهد که **نرخ درهم به ریال در زمان این محموله حدود ۴۵۵,۰۰۰ ریال بوده**. این نرخ باید در `system_settings` ذخیره شود و قابل به‌روزرسانی باشد. + +--- + +## 📋 قوانین رهگیری (Tracking Logic) + +### نگاشت کد IFNEX به کد خارجی + +مشتری IFNEX به مشتری یک کد اختصاصی می‌دهد (مثلاً `IFN-525` یا `980103619`). اما بسته ممکن است با کد دیگری (مثلاً `65986555` در DHL) ارسال شود. + +**در دیتابیس:** +- `shipments.awb_no` = کد IFNEX (مثلاً 980103619) +- `shipment_carrier_mappings.carrier_tracking_number` = کد خارجی (مثلاً 408638240) + +مشتری همیشه با کد IFNEX جستجو می‌کند. سیستم در پس‌زمینه: +- در فاز ₀: رویدادها را به‌صورت دستی در `shipment_tracking_events` با `source = 'manual'` ذخیره می‌کند +- در فاز ۳: با استفاده از کد خارجی، از API TrackingMore/17track رویدادها را دریافت و با `source = 'api_aggregator'` ذخیره می‌کند + +### وضعیت‌های ممکن (Status Enum) + +بر اساس شیت‌های ترکینگ، این وضعیت‌ها در سیستم IFNEX وجود دارند: + +| وضعیت | توضیح | نگاشت به enum | +|-------|-------|---------------| +| Waybill created | بارنامه صادر شد | `processed` | +| Shipment picked up | بسته دریافت شد | `picked_up` | +| Processed at [Location] | در حال پردازش | `in_transit` | +| Shipment has departed | حرکت کرد | `in_transit` | +| Arrived at Sort Facility | رسید به مرکز دسته‌بندی | `in_transit` | +| Re-Weight | وزن‌مجدد | `in_transit` | +| Out For Delivery with Courier | در مسیر تحویل | `out_for_delivery` | +| Prepared for delivery | آماده تحویل | `out_for_delivery` | +| Delivery attempted | تلاش برای تحویل (ناموفق) | `failed` | +| Delivered | تحویل داده شد | `delivered` | +| Failed attempt | تلاش ناموفق | `failed` | +| Returned | برگشت خورده | `returned` | + +این enum در `shipments.status` و `shipment_tracking_events.delivery_status` استفاده می‌شود. + +--- + +## 🛠️ نکات مهاجرت داده (Migration Notes) + +### اسکریپت مهاجرت — فاز ₀ + +این کارها باید توسط اسکریپت `HistoricalShipmentsImport` (در `04_Laravel/app/Imports/`) انجام شود: + +#### ۱. مهاجرت کشورها (از شیت COUNTRIES و Zone) +```php +// خواندن شیت COUNTRIES برای استخراج نام و ISO +// خواندن شیت Zone برای استخراج زون‌های PARCEL و DOCUMENT +// درج در جدول countries با ۴ زون +``` + +#### ۲. مهاجرت مرسولات تاریخی (از شیت List) +```php +// خواندن ردیف ۳ به بعد (ردیف ۱ و ۲ سرتیتر) +// برای هر ردیف: +// - استخراج iso_code از نام کشور (مثلاً "Iran (IR)" → "IR") +// - تبدیل تاریخ میلادی به timestamp +// - تبدیل نوع سرویس (DocNor → DOC_NORMAL، NON DOC → PARCEL، ...) +// - درج در جدول shipments +``` + +#### ۳. مهاجرت نگاشت کدهای ترکینگ (از شیت Refrence) +```php +// برای هر ردیف: +// - lookup shipment_id با awb_no +// - تبدیل IM/EX به carrier_code enum +// - درج در جدول shipment_carrier_mappings +``` + +#### ۴. مهاجرت رویدادهای ترکینگ (از شیت Sheet1 — اختیاری) +```php +// برای هر ردیف: +// - lookup shipment_id با awb_no +// - parse Date و Time +// - درج در جدول shipment_tracking_events با source = 'manual' +``` + +### چالش‌های مهاجرت + +۱. **نام کشورها با فرمت‌های متفاوت:** + - `Iran (IR)`، `IRAN`، `iran` — باید استاندارد شوند + - راه‌حل: استخراج iso_code از پرانتز، یا fuzzy matching با جدول countries + +۲. **فیلد Type متناقض:** + - مقادیر اکسل: `DocNor`، `DocEco`، `NON DOC`، `Outbound`، `Inbound`، خالی + - مقادیر DB: `DOC_NORMAL`، `DOC_ECONOMY`، `PARCEL` + - راه‌حل: mapping table در اسکریپت مهاجرت + +۳. **فیلد Service متناقض:** + - مقادیر اکسل: `Outbound`، `Inbound`، خالی + - مقادیر DB: `import`، `export` + - راه‌حل: `Outbound → export`، `Inbound → import` + +۴. **ردیف‌های خالی زیاد:** + - در شیت List، حدود نیمی از ردیف‌ها خالی هستند + - راه‌حل: `if (empty($row['awb_no'])) continue;` + +۵. **تاریخ‌ها با فرمت متنوع:** + - بعضی ردیف‌ها `2020-05-02`، بعضی `2020-05-02 00:00:00`، بعضی خالی + - راه‌حل: Carbon::parse() با مدیریت خطا + +۶. **نام شرکت‌های حمل با املای متفاوت:** + - `DHL`، `dhl`، `DHL Express`، `DHL8336801664` + - راه‌حل: استانداردسازی به enum + +### تست مهاجرت + +پس از مهاجرت، این تست‌ها باید انجام شوند: + +```bash +# ۱. شمارش رکوردها +php artisan tinker +>>> Shipment::count(); # باید حدود 3950 باشد + +# ۲. نمونه‌گیری تصادفی 50 رکورد +>>> Shipment::inRandomOrder()->take(50)->get(); +# دستی با اکسل مقایسه شود + +# ۳. بررسی یکتایی AWB +>>> Shipment::where('awb_no', '980100011')->count(); # باید 1 باشد + +# ۴. بررسی روابط +>>> Shipment::find(1)->carrierMappings; +>>> Shipment::find(1)->trackingEvents; +``` + +--- + +## 📊 خلاصه‌ی نگاشت شیت‌ها به جداول دیتابیس + +| شیت اکسل | جدول DB | فاز | یادداشت | +|----------|---------|-----|---------| +| `COUNTRIES` | `countries` | ۰ | برای name و iso_code | +| `Zone` | `countries` | ۰ | برای ۴ زون (منبع نهایی) | +| `List` | `shipments` | ۰ | ۳۹۵۰ رکورد تاریخی | +| `List` (۹ ردیف کالای گمرکی) | `shipment_items` | ۱ | در فاز ۱ ساخته می‌شود | +| `Form` | (قالب رابط کاربری) | ۱ | فرم ثبت سفارش | +| `Import Rate` | `shipping_rates` | ۱ | برای type=DOC_NORMAL/PARCEL | +| `Export Rate` | `shipping_rates` | ۱ | برای type=DOC_NORMAL/PARCEL | +| `DocNor` | `shipping_rates` | ۱ | برای type=DOC_NORMAL | +| `DocEco` | `shipping_rates` | ۱ | برای type=DOC_ECONOMY | +| `Parcel` | `shipping_rates` | ۱ | برای type=PARCEL | +| `Assumptions` | `system_settings` | ۰ | VAT، Packing، Profit Margin | +| `AWB` | (قالب PDF) | ۱ | تولید PDF با Dompdf | +| `INVOICE + label` | (قالب PDF) | ۱ | تولید PDF | +| `label` | (قالب PDF) | ۱ | تولید PDF برای چاپگر حرارتی | +| `DATES` | — | — | نادیده (از morilog/jalali استفاده می‌شود) | +| `Start` | — | — | نادیده (خالی) | + +**از فایل ترکینگ دستی:** + +| شیت اکسل | جدول DB | فاز | یادداشت | +|----------|---------|-----|---------| +| `Sheet1` | `shipment_tracking_events` | ۰ | مهاجرت رویدادهای تاریخی | +| `Refrence` | `shipment_carrier_mappings` | ۰ | مهاجرت نگاشت‌ها | +| `Paste` | — | — | نادیده (داده خام) | +| `copy` | — | — | نادیده (تکراری با Sheet1) | +| `Delivered` | — | — | نادیده (با query قابل تولید) | +| `test` | — | — | نادیده (آزمایشی) | + +--- + +## ⚠️ ریسک‌ها و هشدارهای داده‌ای + +### ۱. کیفیت داده‌های تاریخی +- حدود ۲۰٪ رکوردها فیلدهای کلیدی خالی دارند (مخصوصاً اطلاعات گیرنده) +- تاریخ‌ها گاهی به‌صورت متن ذخیره شده‌اند (نه DateTime) +- نام کشورها گاهی با غلط املایی هستند (مثلاً `Americam Samoa` به‌جای `American Samoa`) + +**راهکار:** اسکریپت مهاجرت باید دارای validation و reporting باشد — تعداد ردیف‌های نامعتبر را گزارش کند. + +### ۲. تضاد قیمت‌ها +- شیت Import/Export Rate فقط ۲ نوع دارد (DOCUMENT/NON-DOC) +- شیت‌های DocNor/DocEco/Parcel قیمت‌های متفاوتی دارند (۳ نوع) + +**راهکار:** در فاز ۱، با مشتری تأیید کنید کدام منبع معتبر است. فعلاً در فاز ₀، جدول `shipping_rates` را با enum ۳-حالته طراحی کنید تا آماده هر سناریو باشد. + +### ۳. زون‌های Import +- شیت `Zone` فقط زون‌های export دارد +- زون‌های import باید از شیت `COUNTRIES` استخراج شوند (اما فقط ۱ زون import دارد، نه ۲) + +**راهکار:** در فاز ₁، با مشتری درباره زون‌های import پارسل/داکیومنت صحبت کنید. فعلاً در فاز ₀، فیلدهای `import_zone_parcel` و `import_zone_doc` را با مقدار یکسان از `COUNTRIES` پر کنید. + +### ۴. فیلدهای مالی گاهی خالی +- در شیت List، بسیاری از فیلدهای مالی (`shipping_price`، `extra_service`، ...) صفر یا خالی هستند +- ممکن است داده‌های مالی واقعی در سیستم حسابداری جداگانه‌ای باشد + +**راهکار:** در مهاجرت، فیلدهای خالی را به `null` تبدیل کنید، نه به `0`. این کمک می‌کند تمایز بین «صفر واقعی» و «داده گم‌شده» حفظ شود. + +--- + +## 🎯 جمع‌بندی برای نمونه‌ی جدید + +اگر نمونه‌ی جدیدی هستی که این فایل را می‌خوانی، نکات کلیدی زیر را به خاطر بسپار: + +۱. **فایل‌های اکسل دو نوعند:** فایل اصلی (`4_5989927490271846355.xlsx`) و فایل ترکینگ دستی (`Data entry 2026-06-28.xlsx`). + +۲. **شیت `Zone` منبع نهایی زون‌هاست** — ۴ زون مجزا (PARCEL × DOCUMENT × Export × Import). + +۳. **۳ نوع سرویس وجود دارد:** DocNor، DocEco، Parcel — نه ۲ نوع. + +۴. **VAT = ۹٪** و **Packing Cost پیش‌فرض = ۱۰۰,۰۰۰ ریال** — از شیت Assumptions. + +۵. **۹ ردیف کالای گمرکی** در هر مرسوله — برای فرم ثبت سفارش (فاز ₁). + +۶. **۳۹۵۰ رکورد تاریفی** در شیت List — باید در فاز ₀ مهاجرت داده شوند. + +۷. **۸ شرکت حمل فعال:** DHL، FedEx، UPS، Aramex، Nagel، EMX، APSITEX، IMPEX — فقط ۴ تای اول API دارند. + +۸. **هزینه‌های جانبی متغیر** گاهی در فیلد description شیت Refrence ذخیره شده‌اند (مثلاً "100AED disposal charge"). + +۹. **محموله‌های بالای ۳۰ کیلوگرم** مشمول نرخ ویژه (Spot Rate) هستند — محاسبه آنلاین ندارند. + +۱۰. **PDFها باید انگلیسی باشند** — مطابق اکسل اصلی. اما پنل ادمین و رابط کاربری فارسی است. + +برای جزئیات بیشتر درباره اسکیمای دیتابیس و فرمول قیمت‌گذاری، فایل `Phase0_Proposal.md` بخش ۶ و ۷ را بخوانید. + +--- + +© 2026 VernaSoft Group. Internal use only. diff --git a/01_Documents/PRD_v2.md b/01_Documents/PRD_v2.md index 6705bf7..66c689a 100644 --- a/01_Documents/PRD_v2.md +++ b/01_Documents/PRD_v2.md @@ -1,3 +1,7 @@ +⚠️ هشدار: این سند قدیمی است و با نقشه‌ی راه جدید (۴ فازی) تناقض دارد.برای آخرین وضعیت، فایل Phase0_Proposal.md را بخوانید.این سند فقط برای مرجع تاریخی نگه داشته شده است. + + + سند معماری و نیازمندی‌های پروژه IFNEX Logistics (نسخه 2.0) تاریخ ایجاد: 2023-10-27 (بر اساس جلسات و تحلیل فایل های عملیاتی) نوع پروژه: سیستم ERP لجستیکی و رهگیری محموله (B2B & B2C) توسعه‌دهنده ارشد: [مهندس کاظم القاصی] diff --git a/01_Documents/Project_Roadmap.md b/01_Documents/Project_Roadmap.md index 0ca5926..bb50858 100644 --- a/01_Documents/Project_Roadmap.md +++ b/01_Documents/Project_Roadmap.md @@ -1,3 +1,7 @@ +⚠️ هشدار: این سند قدیمی است و با نقشه‌ی راه جدید (۴ فازی) تناقض دارد.برای آخرین وضعیت، فایل Phase0_Proposal.md را بخوانید.این سند فقط برای مرجع تاریخی نگه داشته شده است. + + + نقشه راه و چک‌لیست پروژه IFNEX (Task List) فاز ۱: انتقال از اکسل به دیتابیس و زیرساخت اولیه (در حال انجام) زیرساخت و دیتابیس diff --git a/01_Documents/STATUS.md b/01_Documents/STATUS.md new file mode 100644 index 0000000..eeac7b0 --- /dev/null +++ b/01_Documents/STATUS.md @@ -0,0 +1,322 @@ +# 🚨 STATUS.md — این فایل را اول بخوانید! + +> **آخرین به‌روزرسانی:** August 2026 +> **فاز در حال اجرا:** فاز ۰ (بنیان داده + ترکینگ دستی + مهاجرت داده‌های تاریخی) +> **توسعه‌دهنده:** VernaSoft Group — Kazem Alghasi +> **وضعیت کلی پروژه:** در حال اجرا — بازنگری مسیر از نقشه‌ی ۳ فازی به ۴ فازی انجام شده است + +--- + +## ⚠️ هشدار حیاتی — قبل از هر کاری بخوانید + +این پروژه دارای **سه سند تاریخی** است که با هم تناقض دارند. فقط یکی از آن‌ها معتبر است: + +| فایل | وضعیت | اقدام | +|------|-------|-------| +| `01_Documents/Phase0_Proposal.md` | ✅ **معتبر و مرجع اصلی** | حتماً کامل بخوانید | +| `01_Documents/PRD_v2.md` | ❌ قدیمی و ناقص | فقط برای مرجع تاریخی — به اسکیمای دیتابیس آن اعتماد نکنید | +| `01_Documents/Project_Roadmap.md` | ❌ قدیمی (۳ فازی) | فقط برای مرجع تاریخی — به فازبندی آن اعتماد نکنید | +| `01_Documents/EXCEL_ANALYSIS.md` | ✅ **مرجع تحلیل اکسل** | حتماً بخوانید قبل از کار با داده‌های تاریخی | +| `README.md` (ریشه) | ✅ به‌روز (نسخه ۲) | برای نمای کلی بخوانید | + +> 🔴 **قانون طلایی:** هرجا بین اسناد تناقض دیدی، به `Phase0_Proposal.md` اعتماد کن. اسناد قدیمی فقط برای فهم تاریخچه‌ی تصمیمات نگه داشته شده‌اند. + +--- + +## 📌 Quick Reference — نسخه‌ها و معماری + +### تکنولوژی‌ها (قفل‌شده) +| مورد | نسخه/مقدار | دلیل | +|------|------------|------| +| Laravel | **11** (همه‌جا یکسان) | در PRD قدیمی ۱۰+ نوشته، در Roadmap قدیمی ۱۱، در README قدیمی ۱۲ — نسخه نهایی: **۱۱** | +| PHP | 8.2+ | الزام لاراول ۱۱ | +| MySQL | 8+ | برای پشتیبانی JSON columns | +| WordPress | آخرین نسخه پایدار | با Polylang برای چندزبانه | +| پنل ادمین | Laravel Filament 3.x | برای سرعت توسعه | +| Frontend | وردپرس + قالب DHL-inspired | **کپی نکنید** — فقط الهام | + +### معماری کلی +``` +┌─────────────────┐ REST API ┌─────────────────┐ +│ WordPress │ ←─────────────────────→ │ Laravel 11 │ +│ (Frontend) │ پلاگین IFNEX Bridge │ (Backend) │ +│ │ │ + Filament │ +└─────────────────┘ └────────┬────────┘ + │ + ┌────────┴────────┐ + │ MySQL 8 │ + └─────────────────┘ + │ + (فاز ۳) │ + ┌────────┴────────┐ + │ VPS پل خارج │ + │ (هلند/آلمان) │ + └────────┬────────┘ + │ + ┌────────┴────────┐ + │ TrackingMore / │ + │ 17track API │ + └─────────────────┘ +``` + +--- + +## ✅ وضعیت فعلی کار + +### کارهای انجام‌شده (تا آخرین به‌روزرسانی) +- [x] تحلیل کامل فایل‌های اکسل عملیاتی شرکت +- [x] شناسایی تناقضات PRD قدیمی با واقعیت اکسل (۴ زون به‌جای ۲، ۳ نوع سرویس به‌جای ۲، فیلدهای غایب VAT/Packing/Warehousing) +- [x] تدوین سند `Phase0_Proposal.md` (مرجع اصلی پروژه) +- [x] بازنگری نقشه‌ی راه از ۳ فازی به ۴ فازی +- [x] طراحی اسکیمای دیتابیس فاز ۰ (۶ جدول اصلی) +- [x] بازنویسی `README.md` با ساختار جدید +- [x] تدوین `EXCEL_ANALYSIS.md` (تحلیل کامل اکسل) + +### کارهای در دست اقدام (فاز ۰) +- [ ] نصب و راه‌اندازی پروژه‌ی لاراول ۱۱ در پوشه `04_Laravel` +- [ ] نصب Filament و احراز هویت ادمین +- [ ] نوشتن Migration ها برای ۶ جدول اصلی: + - [ ] `countries` (با ۴ زون مجزا) + - [ ] `shipments` (فاز ۰ — فیلدهای حداقلی) + - [ ] `shipment_carrier_mappings` + - [ ] `shipment_tracking_events` + - [ ] `system_settings` + - [ ] `users` (با نقش‌های super_admin/tracking_operator/data_entry/customer) +- [ ] Seeder کشورها (۲۳۳ کشور با ۴ زون از شیت Zone اکسل) +- [ ] API ترکینگ: `GET /api/track/{awb_no}` +- [ ] پنل Filament با UX اپراتور ترکینگ (افزودن رویداد سریع) +- [ ] پلاگین وردپرس IFNEX Bridge با شورت‌کد `[ifnex_tracking_form]` +- [ ] اسکریپت مهاجرت ۳۹۵۰ رکورد تاریخی از شیت List اکسل +- [ ] راه‌اندازی وردپرس روی هاست مشتری +- [ ] طراحی لندینگ پیج DHL-inspired (بدون کپی) +- [ ] تست نهایی فاز ۰ و تحویل به مشتری + +### کارهای فاز ۱ (پس از تأیید فاز ۰) +- [ ] موتور قیمت‌گذاری کامل (PriceCalculatorService) +- [ ] جدول `shipping_rates` با نرخ‌های Import/Export +- [ ] فرم ثبت سفارش آنلاین با ۹ ردیف کالای گمرکی +- [ ] تولید PDF: AWB، INVOICE، Label مطابق قالب اکسل +- [ ] ماژول ایمپورت اکسل تعرفه‌ها +- [ ] صفحه استعلام قیمت واقعی + +--- + +## 🚫 خط قرمزها (DO NOT) — هرگز این کارها را نکن + +این قوانین بر اساس تجربه و تصمیمات تأییدشده‌ی مشتری تنظیم شده‌اند. نقض هر کدام = بازگشت به عقب و کار مضاعف. + +### 🚫 اسکیمای دیتابیس +- **NEVER** جدول `countries` را به ۲ زون برگردانی — ۴ زون مجزا (export_parcel, export_doc, import_parcel, import_doc) الزامی است. هر کشور برای پارسل و داکیومنت زون‌های متفاوتی دارد (مثلاً افغانستان: پارسل=۷، داکیومنت=۵). +- **NEVER** فقط ۲ نوع سرویس (DOCUMENT/NON DOC) پیاده کن — ۳ نوع الزامی است: `DOC_NORMAL`، `DOC_ECONOMY`، `PARCEL` (مطابق شیت‌های DocNor، DocEco، Parcel در اکسل). +- **NEVER** فیلد `forwarder_track_id` را به‌عنوان فیلد واحد در `shipments` نگه دار — باید جدول جداگانه `shipment_carrier_mappings` ساخته شود، چون هر مرسوله ممکن است با چند شرکت حمل مرتبط باشد (مثلاً اول DHL سپس Aramex). +- **NEVER** فیلدهای مالی مهم (VAT، Domestic Pickup، Domestic Delivery، Warehousing Cost، Extra Service، Packing Cost) را حذف کن — حتی اگر در فاز ۰ استفاده نمی‌شوند، باید در Migration آماده باشند. +- **NEVER** فیلد `status` در `shipments` را به String تغییر دهی — Enum یکپارچه‌تر و امن‌تر است. + +### 🚫 معماری +- **NEVER** ترکینگ را در وردپرس پیاده کن — همیشه در لاراول. وردپرس فقط نمایش می‌دهد. اگر این کار را بکنی، در فاز ۳ باید تمام داده‌ها را به لاراول مهاجرت دهی (دوباره‌کاری). +- **NEVER** در وردپرس پردازش داده‌ی سفارش انجام دهی — تمام فرم‌ها از طریق پلاگین IFNEX Bridge به لاراول ارسال می‌شوند. +- **NEVER** API لاراول را بدون API Key، Rate Limiting و CORS whitelist بگذاری — امنیت حیاتی است. +- **NEVER** از CORS `*` استفاده کنی — فقط دامنه‌ی تولیدی وردپرس باید whitelist شود. +- **NEVER** تاریخ‌ها را به شمسی در دیتابیس ذخیره کنی — همیشه به‌صورت `timestamp` میلادی. تبدیل به شمسی فقط در لایه‌ی نمایش (با `morilog/jalali`). + +### 🚫 طراحی و کپی‌رایت +- **NEVER** از رنگ، لوگو یا عناصر هویت بصری DHL کپی کنی — نقض کپی‌رایت. الهام از چیدمان و UX مجاز است. +- **NEVER** خروجی PDF (AWB، Invoice، Label) را به فارسی بسازی — مطابق اکسل اصلی، PDF باید انگلیسی باشد. اما پنل ادمین و رابط کاربری فرانت‌اند فارسی است. + +### 🚫 فرآیند +- **NEVER** فایل `.env` را در Git کامیت کنی — در `.gitignore` است. +- **NEVER** `APP_DEBUG=true` را در محیط تولید بگذاری. +- **NEVER** اسکوپ فاز ۰ را بدون Change Request رسمی تغییر دهی — اگر مشتری درخواست افزودن قابلیت کرد، قیمت‌گذاری جداگانه لازم است. +- **NEVER** فاز ۱ را قبل از تأیید رسمی فاز ۰ توسط مشتری شروع کنی. + +--- + +## ❓ سوالات متداول (FAQ) + +### س: کدام نسخه لاراول استفاده کنم؟ +**ج:** لاراول ۱۱. اگر در PRD_v2.md نوشته «Laravel 10+» یا در Roadmap نوشته «Laravel 11»، نسخه نهایی **۱۱** است. + +### س: آیا PRD_v2.md هنوز معتبر است؟ +**ج:** بخش‌های کلی آن (معماری Headless، توضیح کسب‌وکار، VPS پل) معتبرند. اما بخش‌های زیر قدیمی و اشتباه هستند: +- اسکیمای دیتابیس (۴.۱ تا ۴.۴) — به ۴ زون و ۳ نوع سرویس به‌روز نشده +- فازبندی — باید ۴ فازی باشد نه ۳ فازی +- ادعای «فاز ۱ تکمیل شده» — نادرست، فاز ۰ هنوز در حال اجراست +- فیلدهای مالی — VAT، Warehousing Cost، Domestic Pickup/Delivery غایب + +برای اسکیمای دیتابیس، فقط به بخش ۶ `Phase0_Proposal.md` اعتماد کن. + +### س: چرا ترکینگ در لاراول است نه وردپرس؟ +**ج:** چون در فاز ۳ قرار است API ترکینگ واقعی (TrackingMore/17track) متصل شود. اگر الان ترکینگ در وردپرس باشد، در فاز ۳ باید تمام داده‌ها به لاراول مهاجرت داده شوند. با ساخت آن در لاراول از ابتدا، در فاز ۳ فقط یک کلاس `TrackingSyncService` اضافه می‌شود و هیچ چیز دیگر تغییر نمی‌کند. این تصمیم در جلسه با مشتری تأیید شده است. + +### س: چرا ۴ زون مجزا لازم است؟ +**ج:** فایل اکسل عملیاتی نشان می‌دهد همان کشور برای پارسل و داکیومنت زون‌های متفاوتی دارد. مثلاً: +- افغانستان: پارسل=۷، داکیومنت=۵ +- آلبانی: پارسل=۳، داکیومنت=۷ +- استرالیا: پارسل=۷، داکیومنت=۶ + +اگر فقط ۲ زون (export/import) داشته باشیم، موتور قیمت‌گذاری برای DOCUMENTها اشتباه محاسبه می‌کند. + +### س: چرا ۳ نوع سرویس داریم نه ۲؟ +**ج:** فایل اکسل شیت‌های جداگانه دارد برای DocNor (Document Normal)، DocEco (Document Economy) و Parcel. هر کدام جدول قیمت جداگانه. پس `type` در `shipments` باید enum با سه مقدار باشد: `DOC_NORMAL`, `DOC_ECONOMY`, `PARCEL`. + +### س: کدام فایل اکسل عملیاتی است؟ +**ج:** دو فایل: +- **`4_5989927490271846355.xlsx`** — فایل اصلی عملیاتی شرکت با شیت‌های Form, List, COUNTRIES, AWB, INVOICE + label, label, Import Rate, Export Rate, Zone, DocNor, Parcel, DocEco, Assumptions, DATES +- **`Data entry 2026-06-28.xlsx`** — فایل ترکینگ دستی روزانه با شیت‌های Sheet1, Refrence, Paste, copy, Delivered, test + +برای تحلیل کامل هر شیت، فایل `EXCEL_ANALYSIS.md` را بخوان. + +### س: مهاجرت داده‌های تاریخی چقدر مهم است؟ +**ج:** بسیار مهم. ۳۹۵۰ رکورد در شیت List وجود دارد از سال ۲۰۲۰ تا الان. این داده‌ها باید به جدول `shipments` مهاجرت داده شوند. بدون این کار، مشتریان قدیمی نمی‌توانند تاریخچه ببینند و اعتماد به سیستم جدید کاهش می‌یابد. + +### س: آیا باید VPS پل را در فاز ۰ راه‌اندازی کنم؟ +**ج:** خیر. VPS پل مخصوص فاز ۳ است. در فاز ۰ ترکینگ کاملاً دستی است (اپراتور در پنل Filament رویداد اضافه می‌کند). اما اسکیمای دیتابیس باید به‌گونه‌ای باشد که در فاز ۳ بتوان به‌سادگی API را اضافه کرد (به فیلد `source` در `shipment_tracking_events` و `last_synced_at` در `shipment_carrier_mappings` دقت کن). + +### س: مشتری چه انتظاری از فاز ۰ دارد؟ +**ج:** مشتری در جلسه صراحتاً گفت: «اول سایت بالا بیاید و ترکینگ دستی حل شود، بقیه بعد.» یعنی: +۱. وب‌سایت وردپرس کامل آنلاین شود +۲. مشتری نهایی بتواند با کد AWB، تایم‌لاین ترکینگ را ببیند +۳. اپراتور به‌جای اکسل، از پنل Filament استفاده کند + +این سه هدف، حداقل قابل‌قبول برای تحویل فاز ۰ است. + +### س: اگر باگی دیدم یا مشکل پیدا کردم چه کنم؟ +**ج:** اول `EXCEL_ANALYSIS.md` و بخش «ریسک‌ها» در `Phase0_Proposal.md` را چک کن. اگر حل نشد، در گزارش کار (worklog) توضیح بده و به توسعه‌دهنده اصلی (Kazem) اطلاع بده. + +--- + +## 🛠️ Quick Commands — دستورات پرکاربرد + +### نصب و راه‌اندازی لاراول +```bash +cd 04_Laravel +composer install +cp .env.example .env +php artisan key:generate +php artisan migrate +php artisan db:seed --class=CountrySeeder +php artisan serve +``` + +### ایجاد Model + Migration + Resource (Filament) +```bash +php artisan make:model Shipment -m +php artisan make:filament-resource Shipment +``` + +### ایجاد API Controller +```bash +php artisan make:controller Api/TrackController --api +``` + +### اجرای تست +```bash +php artisan test +php artisan serve # سپس در مرورگر: http://localhost:8000/api/track/980103619 +``` + +### مهاجرت داده‌های تاریخی (یک‌بار) +```bash +php artisan ifnex:migrate-historical-data +# این دستور باید ساخته شود — اسکریپت مخصوص خواندن شیت List اکسل +``` + +### پشتیبان‌گیری از دیتابیس (هر روز) +```bash +mysqldump -u root -p ifnex > backups/ifnex_$(date +%Y%m%d).sql +``` + +--- + +## 📂 ساختار پوشه‌های پروژه (پس از تکمیل فاز ۰) + +``` +ifnex/ +├── 01_Documents/ +│ ├── STATUS.md ⭐ این فایل — اول بخوان +│ ├── Phase0_Proposal.md ⭐ مرجع اصلی پروژه +│ ├── EXCEL_ANALYSIS.md ⭐ تحلیل فایل‌های اکسل +│ ├── PRD_v2.md (قدیمی — مرجع تاریخی) +│ ├── Project_Roadmap.md (قدیمی — مرجع تاریخی) +│ └── AI_AGENT_GUIDE.md (راهنمای مخصوص AI Agents — اختیاری) +│ +├── 02_Design/ +│ └── Assets/ (لوگوها، آیکون‌ها، فایل‌های فیگما) +│ +├── 03_WordPress/ +│ └── wp-content/plugins/ +│ └── ifnex-bridge/ (پلاگین اختصاصی) +│ ├── ifnex-bridge.php +│ ├── includes/ +│ │ ├── api-client.php +│ │ ├── shortcodes.php +│ │ └── tracking-form.php +│ └── assets/ +│ ├── css/ +│ └── js/ +│ +├── 04_Laravel/ +│ ├── app/ +│ │ ├── Models/ +│ │ │ ├── Country.php +│ │ │ ├── Shipment.php +│ │ │ ├── ShipmentCarrierMapping.php +│ │ │ ├── ShipmentTrackingEvent.php +│ │ │ ├── SystemSetting.php +│ │ │ └── User.php +│ │ ├── Services/ +│ │ │ ├── TrackingService.php (فاز ۰) +│ │ │ ├── PriceCalculatorService.php (فاز ۱) +│ │ │ └── TrackingSyncService.php (فاز ۳) +│ │ ├── Http/Controllers/Api/ +│ │ │ └── TrackController.php +│ │ ├── Imports/ +│ │ │ ├── ShippingRatesImport.php (فاز ۱) +│ │ │ └── HistoricalShipmentsImport.php (فاز ۰) +│ │ └── Filament/ +│ │ └── Resources/ +│ │ ├── CountryResource.php +│ │ ├── ShipmentResource.php +│ │ └── Pages/ +│ │ └── AddTrackingEvent.php (UX اختصاصی اپراتور) +│ ├── database/ +│ │ ├── migrations/ +│ │ └── seeders/ +│ │ └── CountrySeeder.php +│ ├── routes/api.php +│ ├── config/ +│ │ └── ifnex.php (تنظیمات اختصاصی) +│ └── .env.example +│ +├── README.md (نسخه به‌روز ۲) +└── .gitignore +``` + +--- + +## 🎯 گام بعدی برای ادامه‌ی کار + +اگر نمونه‌ی جدیدی از AI Agent هستی که می‌خواهی کار را ادامه دهی، این مراحل را به ترتیب برو: + +۱. **این فایل (`STATUS.md`)** را کامل بخوان — حالا خواندی ✅ +۲. **`EXCEL_ANALYSIS.md`** را کامل بخوان — برای فهم داده‌های تاریخی ضروری است +۳. **`Phase0_Proposal.md`** را کامل بخوان — مرجع اصلی پروژه +۴. **`README.md`** ریشه را بخوان — برای نمای کلی +۵. کد موجود در `04_Laravel` را بررسی کن — ببین چه چیزی نوشته شده +۶. با کاربر (Kazem) هماهنگ کن — بپرس کدام کار را باید ادامه دهی + +**سپس کار را ادامه بده. موفق باشی! 🚀** + +--- + +## 📞 تماس + +- **توسعه‌دهنده اصلی:** Kazem Alghasi (VernaSoft Group) +- **مشتری:** شرکت IFNEX اصفهان +- **مخزن:** https://www.git.vernahost.ir/gitmodir110/ifnex + +اگر سوالی داشتی که در این فایل یا `EXCEL_ANALYSIS.md` یا `Phase0_Proposal.md` پاسخ آن نبود، از کاربر بپرس — حدس نزن. + +--- + +© 2026 VernaSoft Group. Internal use only.