diff --git a/app/Http/Controllers/App/BillController.php b/app/Http/Controllers/App/BillController.php index 70d0968..45cdf5a 100644 --- a/app/Http/Controllers/App/BillController.php +++ b/app/Http/Controllers/App/BillController.php @@ -20,7 +20,7 @@ class BillController extends Controller */ public function index() { - $query = Bill::query(); + $query = Bill::query()->with('supplier'); if (request()->filled('q')) { $query->where('name', 'like', '%' . request()->q . '%'); @@ -108,7 +108,7 @@ public function togglePaid(Bill $bill) */ public function export() { - $bills = Bill::where('paid', false) + $bills = Bill::with('supplier')->where('paid', false) ->orderBy('deadline') ->orderBy('created_at') ->get(); @@ -132,14 +132,14 @@ public function export() ]; // Título - $sheet->mergeCells('A1:E1'); + $sheet->mergeCells('A1:F1'); $sheet->setCellValue('A1', 'FACTURAS PENDIENTES DE PAGO'); $sheet->getStyle('A1')->applyFromArray([ 'font' => ['bold' => true, 'size' => 14], 'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER], ]); - $sheet->mergeCells('A2:E2'); + $sheet->mergeCells('A2:F2'); $sheet->setCellValue('A2', 'Generado el ' . Carbon::now()->format('d/m/Y H:i')); $sheet->getStyle('A2')->applyFromArray([ 'font' => ['italic' => true, 'color' => ['rgb' => '666666']], @@ -147,11 +147,11 @@ public function export() ]); // Encabezados - $headers = ['A4' => '#', 'B4' => 'NOMBRE', 'C4' => 'COSTO', 'D4' => 'FECHA LÍMITE', 'E4' => 'DÍAS RESTANTES']; + $headers = ['A4' => '#', 'B4' => 'NOMBRE', 'C4' => 'PROVEEDOR', 'D4' => 'COSTO', 'E4' => 'FECHA LÍMITE', 'F4' => 'DÍAS RESTANTES']; foreach ($headers as $cell => $text) { $sheet->setCellValue($cell, $text); } - $sheet->getStyle('A4:E4')->applyFromArray($styleHeader); + $sheet->getStyle('A4:F4')->applyFromArray($styleHeader); $sheet->getRowDimension(4)->setRowHeight(22); // Datos @@ -174,26 +174,27 @@ public function export() $sheet->setCellValue('A' . $row, $i + 1); $sheet->setCellValue('B' . $row, $bill->name); - $sheet->setCellValue('C' . $row, (float) $bill->cost); - $sheet->setCellValue('D' . $row, $deadline?->format('d/m/Y') ?? '—'); - $sheet->setCellValue('E' . $row, $daysLabel); + $sheet->setCellValue('C' . $row, $bill->supplier?->business_name ?? '—'); + $sheet->setCellValue('D' . $row, (float) $bill->cost); + $sheet->setCellValue('E' . $row, $deadline?->format('d/m/Y') ?? '—'); + $sheet->setCellValue('F' . $row, $daysLabel); - $sheet->getStyle('C' . $row)->getNumberFormat()->setFormatCode('$#,##0.00'); - $sheet->getStyle('A' . $row . ':E' . $row)->applyFromArray($styleData); + $sheet->getStyle('D' . $row)->getNumberFormat()->setFormatCode('$#,##0.00'); + $sheet->getStyle('A' . $row . ':F' . $row)->applyFromArray($styleData); $sheet->getStyle('A' . $row)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER); - $sheet->getStyle('C' . $row)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_RIGHT); - $sheet->getStyle('D' . $row)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER); + $sheet->getStyle('D' . $row)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_RIGHT); $sheet->getStyle('E' . $row)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER); + $sheet->getStyle('F' . $row)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER); // Verde si quedan días, rojo si está vencida if ($daysLeft !== null && $daysLeft > 0) { - $sheet->getStyle('E' . $row)->getFont()->getColor()->setRGB('1A7A1A'); + $sheet->getStyle('F' . $row)->getFont()->getColor()->setRGB('1A7A1A'); } elseif ($daysLeft !== null && $daysLeft < 0) { - $sheet->getStyle('D' . $row . ':E' . $row)->getFont()->getColor()->setRGB('CC0000'); - $sheet->getStyle('D' . $row . ':E' . $row)->getFont()->setBold(true); + $sheet->getStyle('E' . $row . ':F' . $row)->getFont()->getColor()->setRGB('CC0000'); + $sheet->getStyle('E' . $row . ':F' . $row)->getFont()->setBold(true); } elseif ($daysLeft === 0) { - $sheet->getStyle('E' . $row)->getFont()->getColor()->setRGB('B45309'); - $sheet->getStyle('E' . $row)->getFont()->setBold(true); + $sheet->getStyle('F' . $row)->getFont()->getColor()->setRGB('B45309'); + $sheet->getStyle('F' . $row)->getFont()->setBold(true); } $total += (float) $bill->cost; @@ -201,10 +202,10 @@ public function export() } // Total - $sheet->setCellValue('B' . $row, 'TOTAL PENDIENTE'); - $sheet->setCellValue('C' . $row, $total); - $sheet->getStyle('C' . $row)->getNumberFormat()->setFormatCode('$#,##0.00'); - $sheet->getStyle('B' . $row . ':C' . $row)->applyFromArray([ + $sheet->setCellValue('C' . $row, 'TOTAL PENDIENTE'); + $sheet->setCellValue('D' . $row, $total); + $sheet->getStyle('D' . $row)->getNumberFormat()->setFormatCode('$#,##0.00'); + $sheet->getStyle('C' . $row . ':D' . $row)->applyFromArray([ 'font' => ['bold' => true, 'size' => 11], 'fill' => ['fillType' => Fill::FILL_SOLID, 'startColor' => ['rgb' => 'FFF2CC']], 'borders' => ['allBorders' => ['borderStyle' => Border::BORDER_THIN]], @@ -213,10 +214,11 @@ public function export() // Anchos de columna $sheet->getColumnDimension('A')->setWidth(6); - $sheet->getColumnDimension('B')->setWidth(40); - $sheet->getColumnDimension('C')->setWidth(18); - $sheet->getColumnDimension('D')->setWidth(16); + $sheet->getColumnDimension('B')->setWidth(36); + $sheet->getColumnDimension('C')->setWidth(30); + $sheet->getColumnDimension('D')->setWidth(18); $sheet->getColumnDimension('E')->setWidth(16); + $sheet->getColumnDimension('F')->setWidth(16); // Generar archivo $fileName = 'Facturas_Pendientes_' . Carbon::now()->format('Ymd_His') . '.xlsx'; diff --git a/app/Http/Requests/App/BillStoreRequest.php b/app/Http/Requests/App/BillStoreRequest.php index cf4ce3c..a2e26ff 100644 --- a/app/Http/Requests/App/BillStoreRequest.php +++ b/app/Http/Requests/App/BillStoreRequest.php @@ -20,11 +20,12 @@ public function authorize(): bool public function rules(): array { return [ - 'name' => ['required', 'string', 'max:255'], - 'cost' => ['required', 'numeric', 'min:0'], - 'deadline' => ['nullable', 'date'], - 'paid' => ['boolean'], - 'file' => ['nullable', 'file', 'mimes:pdf,jpg,jpeg,png', 'max:10240'], + 'name' => ['required', 'string', 'max:255'], + 'supplier_id' => ['nullable', 'exists:suppliers,id'], + 'cost' => ['required', 'numeric', 'min:0'], + 'deadline' => ['nullable', 'date'], + 'paid' => ['boolean'], + 'file' => ['nullable', 'file', 'mimes:pdf,jpg,jpeg,png', 'max:10240'], ]; } @@ -35,6 +36,7 @@ public function messages(): array { return [ 'name.required' => 'El nombre de la factura es obligatorio.', + 'supplier_id.exists' => 'El proveedor seleccionado no es válido.', 'cost.required' => 'El costo es obligatorio.', 'cost.numeric' => 'El costo debe ser un número válido.', 'cost.min' => 'El costo debe ser un valor positivo.', diff --git a/app/Http/Requests/App/BillUpdateRequest.php b/app/Http/Requests/App/BillUpdateRequest.php index d60a88d..147a2b6 100644 --- a/app/Http/Requests/App/BillUpdateRequest.php +++ b/app/Http/Requests/App/BillUpdateRequest.php @@ -20,11 +20,12 @@ public function authorize(): bool public function rules(): array { return [ - 'name' => ['required', 'string', 'max:255'], - 'cost' => ['required', 'numeric', 'min:0'], - 'deadline' => ['nullable', 'date'], - 'paid' => ['boolean'], - 'file' => ['sometimes', 'nullable', 'file', 'mimes:pdf,jpg,jpeg,png', 'max:10240'], + 'name' => ['required', 'string', 'max:255'], + 'supplier_id' => ['nullable', 'exists:suppliers,id'], + 'cost' => ['required', 'numeric', 'min:0'], + 'deadline' => ['nullable', 'date'], + 'paid' => ['boolean'], + 'file' => ['sometimes', 'nullable', 'file', 'mimes:pdf,jpg,jpeg,png', 'max:10240'], ]; } @@ -35,6 +36,7 @@ public function messages(): array { return [ 'name.required' => 'El nombre de la factura es obligatorio.', + 'supplier_id.exists' => 'El proveedor seleccionado no es válido.', 'cost.required' => 'El costo es obligatorio.', 'cost.numeric' => 'El costo debe ser un número válido.', 'cost.min' => 'El costo debe ser un valor positivo.', diff --git a/app/Models/Bill.php b/app/Models/Bill.php index 4e1f6e3..cd6a620 100644 --- a/app/Models/Bill.php +++ b/app/Models/Bill.php @@ -10,6 +10,7 @@ class Bill extends Model { protected $fillable = [ 'name', + 'supplier_id', 'cost', 'file_path', 'deadline', @@ -23,6 +24,11 @@ class Bill extends Model protected $appends = ['file_url']; + public function supplier() + { + return $this->belongsTo(Supplier::class); + } + public function getFileUrlAttribute(): ?string { if (!$this->file_path) return null; diff --git a/database/migrations/2026_03_21_121041_add_suppliers_to_bills_table.php b/database/migrations/2026_03_21_121041_add_suppliers_to_bills_table.php new file mode 100644 index 0000000..8a6ba18 --- /dev/null +++ b/database/migrations/2026_03_21_121041_add_suppliers_to_bills_table.php @@ -0,0 +1,29 @@ +foreignId('supplier_id')->after('name')->nullable()->constrained('suppliers')->nullOnDelete(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('bills', function (Blueprint $table) { + $table->dropForeign(['supplier_id']); + $table->dropColumn('supplier_id'); + }); + } +};