186 lines
6.7 KiB
PHP
186 lines
6.7 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\App;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\Client;
|
|
use App\Models\Sale;
|
|
use Illuminate\Http\Request;
|
|
use Notsoweb\ApiResponse\Enums\ApiResponse;
|
|
|
|
class FacturaDataController extends Controller
|
|
{
|
|
/**
|
|
* Muestra los datos de la venta para el formulario de facturación.
|
|
*/
|
|
public function show(string $invoiceNumber)
|
|
{
|
|
$sale = Sale::where('invoice_number', $invoiceNumber)
|
|
->with([
|
|
'client',
|
|
'details.inventory.category',
|
|
'details.serials',
|
|
'user:id,name,email'
|
|
])
|
|
->first();
|
|
|
|
if (!$sale) {
|
|
return ApiResponse::INTERNAL_ERROR->response([
|
|
'message' => 'Venta no encontrada'
|
|
]);
|
|
}
|
|
|
|
// Si ya tiene datos de facturación
|
|
if ($sale->client_id) {
|
|
return ApiResponse::NO_CONTENT->response([
|
|
'message' => 'Esta venta ya tiene datos de facturación registrados',
|
|
'client' => $sale->client,
|
|
'sale' => $this->formatSaleData($sale)
|
|
]);
|
|
}
|
|
|
|
return ApiResponse::OK->response([
|
|
'sale' => $this->formatSaleData($sale)
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Guarda los datos fiscales del cliente para la venta.
|
|
*/
|
|
public function store(Request $request, string $invoiceNumber)
|
|
{
|
|
$sale = Sale::where('invoice_number', $invoiceNumber)
|
|
->with('details.serials')
|
|
->first();
|
|
|
|
if (!$sale) {
|
|
return ApiResponse::INTERNAL_ERROR->response([
|
|
'message' => 'Venta no encontrada'
|
|
]);
|
|
}
|
|
|
|
if ($sale->client_id) {
|
|
return ApiResponse::NO_CONTENT->response([
|
|
'message' => 'Esta venta ya tiene datos de facturación registrados'
|
|
]);
|
|
}
|
|
|
|
// Verificar que la venta esté completada
|
|
if ($sale->status !== 'completed') {
|
|
return ApiResponse::BAD_REQUEST->response([
|
|
'message' => 'Solo se pueden facturar ventas completadas'
|
|
]);
|
|
}
|
|
|
|
$validated = $request->validate([
|
|
'name' => 'required|string|max:255',
|
|
'email' => 'required|email|max:255',
|
|
'phone' => 'nullable|string|max:20',
|
|
'address' => 'nullable|string|max:500',
|
|
'rfc' => 'required|string|size:13|regex:/^[A-ZÑ&]{3,4}\d{6}[A-Z0-9]{3}$/i',
|
|
'razon_social' => 'required|string|max:255',
|
|
'regimen_fiscal' => 'required|string|max:100',
|
|
'cp_fiscal' => 'required|string|size:5|regex:/^\d{5}$/',
|
|
'uso_cfdi' => 'required|string|max:100',
|
|
], [
|
|
'rfc.regex' => 'El RFC no tiene un formato válido',
|
|
'rfc.size' => 'El RFC debe tener 13 caracteres',
|
|
'cp_fiscal.regex' => 'El código postal debe ser de 5 dígitos',
|
|
'cp_fiscal.size' => 'El código postal debe ser de 5 dígitos',
|
|
'name.required' => 'El nombre es obligatorio',
|
|
'email.required' => 'El correo electrónico es obligatorio',
|
|
'email.email' => 'El correo electrónico debe ser válido',
|
|
'razon_social.required' => 'La razón social es obligatoria',
|
|
'regimen_fiscal.required' => 'El régimen fiscal es obligatorio',
|
|
'uso_cfdi.required' => 'El uso de CFDI es obligatorio',
|
|
]);
|
|
|
|
// Buscar si ya existe un cliente con ese RFC
|
|
$client = Client::where('rfc', strtoupper($validated['rfc']))->first();
|
|
|
|
if ($client) {
|
|
// Actualizar datos del cliente existente
|
|
$client->update([
|
|
'name' => $validated['name'],
|
|
'email' => $validated['email'],
|
|
'phone' => $validated['phone'] ?? $client->phone,
|
|
'address' => $validated['address'] ?? $client->address,
|
|
'razon_social' => $validated['razon_social'],
|
|
'regimen_fiscal' => $validated['regimen_fiscal'],
|
|
'cp_fiscal' => $validated['cp_fiscal'],
|
|
'uso_cfdi' => $validated['uso_cfdi'],
|
|
]);
|
|
} else {
|
|
// Crear nuevo cliente
|
|
$client = Client::create([
|
|
'name' => $validated['name'],
|
|
'email' => $validated['email'],
|
|
'phone' => $validated['phone'],
|
|
'address' => $validated['address'],
|
|
'rfc' => strtoupper($validated['rfc']),
|
|
'razon_social' => $validated['razon_social'],
|
|
'regimen_fiscal' => $validated['regimen_fiscal'],
|
|
'cp_fiscal' => $validated['cp_fiscal'],
|
|
'uso_cfdi' => $validated['uso_cfdi'],
|
|
]);
|
|
}
|
|
|
|
// Asociar cliente a la venta
|
|
$sale->update(['client_id' => $client->id]);
|
|
|
|
// Recargar relaciones
|
|
$sale->load([
|
|
'client',
|
|
'details.inventory.category',
|
|
'details.serials',
|
|
'user:id,name,email'
|
|
]);
|
|
|
|
return ApiResponse::OK->response([
|
|
'message' => 'Datos de facturación guardados correctamente',
|
|
'client' => $client,
|
|
'sale' => $this->formatSaleData($sale)
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Formatear datos de la venta incluyendo números de serie
|
|
*/
|
|
private function formatSaleData(Sale $sale): array
|
|
{
|
|
return [
|
|
'id' => $sale->id,
|
|
'invoice_number' => $sale->invoice_number,
|
|
'total' => $sale->total,
|
|
'subtotal' => $sale->subtotal,
|
|
'tax' => $sale->tax,
|
|
'payment_method' => $sale->payment_method,
|
|
'status' => $sale->status,
|
|
'created_at' => $sale->created_at,
|
|
'user' => $sale->user ? [
|
|
'id' => $sale->user->id,
|
|
'name' => $sale->user->name,
|
|
'email' => $sale->user->email,
|
|
] : null,
|
|
'items' => $sale->details->map(function ($detail) {
|
|
return [
|
|
'id' => $detail->id,
|
|
'product_name' => $detail->product_name,
|
|
'quantity' => $detail->quantity,
|
|
'unit_price' => $detail->unit_price,
|
|
'subtotal' => $detail->subtotal,
|
|
'category' => $detail->inventory->category->name ?? null,
|
|
'sku' => $detail->inventory->sku ?? null,
|
|
// Números de serie vendidos
|
|
'serial_numbers' => $detail->serials->map(function ($serial) {
|
|
return [
|
|
'serial_number' => $serial->serial_number,
|
|
'status' => $serial->status,
|
|
];
|
|
})->toArray(),
|
|
];
|
|
})->toArray(),
|
|
];
|
|
}
|
|
}
|