|DhlShipment active() * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment newQuery() * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment outbound() * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment query() * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment returns() * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment trackable() * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereApiErrors($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereApiRequestData($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereApiResponseData($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereCreatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereCurrency($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereDeliveredAt($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereHeight($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereId($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereLabelFormat($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereLabelPath($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereLabelPrinted($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereLastTrackedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereLength($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereMetadata($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereNotes($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereProductCode($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereRecipientCity($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereRecipientCompany($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereRecipientCountry($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereRecipientEmail($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereRecipientName($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereRecipientPhone($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereRecipientPostalCode($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereRecipientState($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereRecipientStreet($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereRecipientStreetNumber($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereRelatedShipmentId($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereServices($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereShipmentNumber($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereShippedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereShippingCost($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereShoppingOrderId($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereStatus($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereTrackingDetails($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereTrackingNumber($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereTrackingStatus($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereType($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereUpdatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereWeight($value) * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereWidth($value) * @mixin \Eloquent */ class DhlShipment extends Model { use HasFactory; /** * The table associated with the model. */ protected $table = 'dhl_shipments'; /** * The attributes that are mass assignable. */ protected $fillable = [ 'shopping_order_id', 'shipment_number', 'tracking_number', 'type', 'related_shipment_id', 'weight', 'length', 'width', 'height', 'product_code', 'services', 'label_path', 'label_format', 'label_printed', 'status', 'tracking_status', 'tracking_details', 'last_tracked_at', 'recipient_name', 'recipient_company', 'recipient_street', 'recipient_street_number', 'recipient_postal_code', 'recipient_city', 'recipient_state', 'recipient_country', 'recipient_email', 'recipient_phone', 'api_request_data', 'api_response_data', 'api_errors', 'shipping_cost', 'currency', 'notes', 'metadata', 'shipped_at', 'delivered_at', ]; /** * The attributes that should be cast. */ protected $casts = [ 'weight' => 'float', 'length' => 'integer', 'width' => 'integer', 'height' => 'integer', 'services' => 'array', 'label_printed' => 'boolean', 'tracking_details' => 'array', 'last_tracked_at' => 'datetime', 'api_request_data' => 'array', 'api_response_data' => 'array', 'shipping_cost' => 'decimal:2', 'metadata' => 'array', 'shipped_at' => 'datetime', 'delivered_at' => 'datetime', ]; /** * Shipment types */ public const TYPE_OUTBOUND = 'outbound'; public const TYPE_RETURN = 'return'; /** * Shipment statuses */ public const STATUS_CREATED = 'created'; public const STATUS_SUBMITTED = 'submitted'; public const STATUS_IN_TRANSIT = 'in_transit'; public const STATUS_DELIVERED = 'delivered'; public const STATUS_RETURNED = 'returned'; public const STATUS_CANCELLED = 'cancelled'; public const STATUS_FAILED = 'failed'; /** * Get the shopping order that owns the shipment */ public function shoppingOrder(): BelongsTo { return $this->belongsTo(ShoppingOrder::class, 'shopping_order_id'); } /** * Get the related shipment (for returns) */ public function relatedShipment(): BelongsTo { return $this->belongsTo(DhlShipment::class, 'related_shipment_id'); } /** * Get the return shipment (for outbound shipments) */ public function returnShipment(): HasOne { return $this->hasOne(DhlShipment::class, 'related_shipment_id'); } /** * Scope for outbound shipments */ public function scopeOutbound($query) { return $query->where('type', self::TYPE_OUTBOUND); } /** * Scope for return shipments */ public function scopeReturns($query) { return $query->where('type', self::TYPE_RETURN); } /** * Scope for active shipments (not cancelled or failed) */ public function scopeActive($query) { return $query->whereNotIn('status', [self::STATUS_CANCELLED, self::STATUS_FAILED]); } /** * Scope for trackable shipments (have tracking number) */ public function scopeTrackable($query) { return $query->whereNotNull('tracking_number'); } /** * Check if shipment is outbound */ public function isOutbound(): bool { return $this->type === self::TYPE_OUTBOUND; } /** * Check if shipment is return */ public function isReturn(): bool { return $this->type === self::TYPE_RETURN; } /** * Check if shipment can be cancelled */ public function canBeCancelled(): bool { return in_array($this->status, [ self::STATUS_CREATED, self::STATUS_SUBMITTED, ]); } /** * Check if shipment is delivered */ public function isDelivered(): bool { return $this->status === self::STATUS_DELIVERED; } /** * Check if shipment has tracking information */ public function hasTracking(): bool { return !empty($this->tracking_number); } /** * Check if label is available */ public function hasLabel(): bool { return !empty($this->label_path) && file_exists(storage_path('app/' . $this->label_path)); } /** * Get full recipient address as formatted string */ public function getRecipientAddressAttribute(): string { $address = $this->recipient_name; if ($this->recipient_company) { $address .= "\n" . $this->recipient_company; } $address .= "\n" . $this->recipient_street . ' ' . $this->recipient_street_number; $address .= "\n" . $this->recipient_postal_code . ' ' . $this->recipient_city; if ($this->recipient_state) { $address .= "\n" . $this->recipient_state; } $address .= "\n" . $this->recipient_country; return $address; } /** * Get package dimensions as formatted string */ public function getDimensionsAttribute(): ?string { if (!$this->length || !$this->width || !$this->height) { return null; } return $this->length . ' x ' . $this->width . ' x ' . $this->height . ' cm'; } /** * Get human-readable status */ public function getStatusLabelAttribute(): string { return match($this->status) { self::STATUS_CREATED => 'Erstellt', self::STATUS_SUBMITTED => 'Übertragen', self::STATUS_IN_TRANSIT => 'Unterwegs', self::STATUS_DELIVERED => 'Zugestellt', self::STATUS_RETURNED => 'Zurückgeschickt', self::STATUS_CANCELLED => 'Storniert', self::STATUS_FAILED => 'Fehler', default => 'Unbekannt', }; } /** * Get human-readable type */ public function getTypeLabelAttribute(): string { return match($this->type) { self::TYPE_OUTBOUND => 'Versand', self::TYPE_RETURN => 'Retoure', default => 'Unbekannt', }; } /** * Get label file URL for download */ public function getLabelUrlAttribute(): ?string { if (!$this->hasLabel()) { return null; } return route('admin.dhl.shipments.label', $this->id); } /** * Boot the model */ protected static function boot() { parent::boot(); static::creating(function ($shipment) { // Set default values if (empty($shipment->currency)) { $shipment->currency = config('dhl.defaults.currency', 'EUR'); } if (empty($shipment->product_code)) { $shipment->product_code = config('dhl.defaults.product', 'V01PAK'); } if (empty($shipment->label_format)) { $shipment->label_format = config('dhl.labels.format', 'PDF'); } }); } }