881 lines
43 KiB
Markdown
881 lines
43 KiB
Markdown
# 📊 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.
|