'import', 'sell' => 'export', ]; if (! isset($typeMap[$transaction->type])) { return null; } $business_id = (int) $transaction->business_id; $existing = CustomsClearance::where('business_id', $business_id) ->where('transaction_id', $transaction->id) ->first(); if ($existing) { return $existing; } $clearance = CustomsClearance::create([ 'business_id' => $business_id, 'ref_no' => $this->tmsUtil->generateCustomsRefNo($business_id), 'clearance_type' => $typeMap[$transaction->type], 'transaction_id' => $transaction->id, 'contact_id' => $transaction->contact_id, 'customs_value' => (float) ($transaction->final_total ?? 0), 'status' => 'draft', 'created_by' => auth()->id(), ...$extra, ]); $this->importItemsFromTransaction($clearance, $transaction); $this->recalculateTotals($clearance); $this->logStatus($clearance, 'draft', __('tms::lang.auto_created_from_transaction')); return $this->customsSyncService->linkByTransaction($clearance->fresh(['items'])); } public function createFromShipment(Shipment $shipment, array $extra = []): CustomsClearance { $business_id = (int) $shipment->business_id; $existing = CustomsClearance::where('business_id', $business_id) ->where('shipment_id', $shipment->id) ->first(); if ($existing) { return $existing; } $clearance = CustomsClearance::create([ 'business_id' => $business_id, 'ref_no' => $this->tmsUtil->generateCustomsRefNo($business_id), 'clearance_type' => $extra['clearance_type'] ?? 'import', 'shipment_id' => $shipment->id, 'transaction_id' => $shipment->transaction_id, 'contact_id' => $shipment->contact_id, 'customs_value' => (float) ($shipment->freight_cost ?? 0), 'status' => 'draft', 'created_by' => auth()->id(), ...$extra, ]); if ($shipment->transaction_id) { $transaction = Transaction::find($shipment->transaction_id); if ($transaction) { $this->importItemsFromTransaction($clearance, $transaction); } } $this->recalculateTotals($clearance); $this->logStatus($clearance, 'draft', __('tms::lang.created_from_shipment', ['ref' => $shipment->ref_no])); $this->customsSyncService->linkMutually($clearance, $shipment); $this->customsSyncService->syncShipmentFromCustoms($clearance->fresh()); return $clearance->fresh(['items', 'shipment']); } public function syncItems(CustomsClearance $clearance, array $items): void { $clearance->items()->delete(); foreach ($items as $row) { if (empty($row['description'])) { continue; } $qty = (float) ($row['quantity'] ?? 1); $value = (float) ($row['customs_value'] ?? 0); $dutyRate = (float) ($row['duty_rate'] ?? 0); $vatRate = (float) ($row['vat_rate'] ?? 0); $dutyAmount = round($value * $dutyRate / 100, 4); $vatAmount = round(($value + $dutyAmount) * $vatRate / 100, 4); CustomsItem::create([ 'customs_clearance_id' => $clearance->id, 'description' => $row['description'], 'hs_code' => $row['hs_code'] ?? null, 'quantity' => $qty, 'unit' => $row['unit'] ?? null, 'customs_value' => $value, 'duty_rate' => $dutyRate, 'duty_amount' => $dutyAmount, 'vat_rate' => $vatRate, 'vat_amount' => $vatAmount, ]); } $this->recalculateTotals($clearance->fresh()); } public function recalculateTotals(CustomsClearance $clearance): void { $clearance->load('items'); $customsValue = $clearance->items->sum('customs_value'); $dutyAmount = $clearance->items->sum('duty_amount'); $vatAmount = $clearance->items->sum('vat_amount'); $other = (float) $clearance->other_charges; $clearance->update([ 'customs_value' => $customsValue > 0 ? $customsValue : $clearance->customs_value, 'duty_amount' => $dutyAmount, 'vat_amount' => $vatAmount, 'total_duties' => $dutyAmount + $vatAmount + $other, ]); } public function updateStatus(CustomsClearance $clearance, string $status, ?string $note = null): CustomsClearance { $updates = ['status' => $status]; if ($status === 'declared' && ! $clearance->declared_at) { $updates['declared_at'] = now(); } if ($status === 'cleared' && ! $clearance->cleared_at) { $updates['cleared_at'] = now(); } if ($status === 'released' && ! $clearance->released_at) { $updates['released_at'] = now(); } $clearance->update($updates); $this->logStatus($clearance, $status, $note); if (in_array($status, ['cleared', 'released'], true)) { $this->accountingService->postCustomsDutiesOnClearance( $clearance->fresh(), (int) (auth()->id() ?? $clearance->created_by ?? 0) ); } $this->customsSyncService->syncShipmentFromCustoms($clearance->fresh()); return $clearance->fresh(); } public function logStatus(CustomsClearance $clearance, string $status, ?string $note = null): CustomsStatusLog { return CustomsStatusLog::create([ 'customs_clearance_id' => $clearance->id, 'status' => $status, 'note' => $note, 'created_by' => auth()->id(), 'logged_at' => now(), ]); } protected function importItemsFromTransaction(CustomsClearance $clearance, Transaction $transaction): void { $lines = $transaction->type === 'purchase' ? $transaction->purchase_lines : ($transaction->sell_lines ?? collect()); if (! $lines || $lines->isEmpty()) { return; } $items = []; foreach ($lines as $line) { $product = $line->product ?? null; $items[] = [ 'description' => $product?->name ?? ($line->product?->name ?? __('tms::lang.item')), 'hs_code' => $product?->sku ?? null, 'quantity' => (float) ($line->quantity ?? 1), 'unit' => $line->sub_unit?->short_name ?? $product?->unit?->short_name ?? null, 'customs_value' => (float) ($line->purchase_price_inc_tax ?? $line->unit_price_inc_tax ?? $line->unit_price ?? 0) * (float) ($line->quantity ?? 1), 'duty_rate' => 0, 'vat_rate' => 0, ]; } $this->syncItems($clearance, $items); } }