ifnex/04_Laravel/app/Models/Shipment.php
Kazem Alghasi b35e933475 Phase 3.5 — Core Features Complete:
✅ Multi-Package Support (3.5.1)
- Add shipment_packages migration and ShipmentPackage model
- Add packages() relation to Shipment model
- Update CustomerOrderController to accept packages[] array
- Auto-calculate volumetric weight from dimensions (L*W*H/5000)
- Repeater UI for adding/removing packages
- Real-time summary of total weights

✅ Invoice Form for PARCEL (3.5.2)
- New step in order form (only for PARCEL type)
- Customs items: description, HS Code, quantity, unit price (USD)
- Auto-calculate total per item and invoice total
- Maximum 9 items
- 5-step order form structure

✅ PDF Redesign (3.5.4)
- Install picqer/php-barcode-generator
- Redesign AWB with barcode, From/To, Value, Service, Type
- Redesign Invoice with 9-row table, legal declaration
- Redesign Label to A5 landscape with large barcode
- DOC/PARCEL condition (only PARCEL has invoice)
- Barcode as base64 embed

✅ Technical Fixes (3.5.5)
- Add Enum casts to Shipment model (status, direction, type)
- Fix deselectAfterCompletion -> deselectRecordsAfterCompletion
- Migration for shipment_items (row_number, unit_price)
- Migration to make name nullable
- Fix Enum method names (DocNormal/DocEconomy)

✅ Tracking Test (3.5.6)
- Update TrackController with status_label
- Redesign tracking page with beautiful timeline
- Status box with colored badge
- Timeline with date, description, location, source
- Carrier mappings section

Documentation:
- Update README, Laravel_README, Roadmap, Checklist
- Add IFNEX_Meeting_Summary.md for client presentation
- Update IFNEX_I18N_Strategy.md (moved to deploy phase)

Phase 3.5 — 90% complete (i18n moved to deploy phase)"
2026-08-29 08:35:34 +03:30

142 lines
3.2 KiB
PHP

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Shipment extends Model
{
protected $fillable = [
'awb_no',
'forwarder',
'direction',
'type',
'status',
'from_country_id',
'to_country_id',
// Weight & Dimensions
'weight',
'volumetric_weight',
'chargeable_weight',
'dimensions',
// Financial
'shipping_price',
'extra_service',
'domestic_pickup',
'packing_cost',
'domestic_delivery',
'warehousing_cost',
'discount',
'vat_amount',
'total_fee',
'net_dirham',
'net_rial',
'invoice_total_usd',
// Sender
'sender_name',
'sender_company',
'sender_phone',
'sender_email',
'sender_address',
'sender_city',
'sender_zip',
'sender_id_number',
// Receiver
'receiver_name',
'receiver_company',
'receiver_phone',
'receiver_email',
'receiver_address',
'receiver_city',
'receiver_zip',
'receiver_id_number',
// Customs
'reason_for_export',
'content_description',
];
protected $casts = [
'weight' => 'decimal:3',
'volumetric_weight' => 'decimal:3',
'chargeable_weight' => 'decimal:3',
'status' => \App\Enums\ShipmentStatus::class,
'direction' => \App\Enums\ShipmentDirection::class,
'type' => \App\Enums\ShipmentType::class,
];
// === Relationships ===
public function fromCountry(): BelongsTo
{
return $this->belongsTo(Country::class, 'from_country_id');
}
public function toCountry(): BelongsTo
{
return $this->belongsTo(Country::class, 'to_country_id');
}
public function items(): HasMany
{
return $this->hasMany(ShipmentItem::class);
}
public function carrierMappings(): HasMany
{
return $this->hasMany(ShipmentCarrierMapping::class);
}
public function trackingEvents(): HasMany
{
return $this->hasMany(ShipmentTrackingEvent::class);
}
/**
* بسته‌های مرسوله (Multi-Package)
*/
public function packages(): HasMany
{
return $this->hasMany(ShipmentPackage::class);
}
// === Scopes ===
public function scopeByAwb($query, string $awbNo)
{
return $query->where('awb_no', $awbNo);
}
public function scopeExport($query)
{
return $query->where('direction', 'export');
}
public function scopeImport($query)
{
return $query->where('direction', 'import');
}
// === Helpers ===
/**
* دریافت آخرین رویداد ترکینگ
*/
public function latestTrackingEvent()
{
return $this->trackingEvents()
->orderBy('event_date', 'desc')
->orderBy('event_time', 'desc')
->first();
}
/**
* آیا مرسوله تحویل داده شده؟
*/
public function isDelivered(): bool
{
return $this->status === 'delivered';
}
}