224 lines
7.9 KiB
PHP
224 lines
7.9 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Mail\PreventivoAdmin;
|
|
use App\Mail\PreventivoCliente;
|
|
use App\Models\Cliente;
|
|
use App\Models\Preventivo;
|
|
use App\Models\PreventivoItem;
|
|
use App\Support\ConfigurazioneFormatter;
|
|
use App\Support\PreventivoPdf;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Mail;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Illuminate\Support\Str;
|
|
|
|
class CarrelloController extends Controller
|
|
{
|
|
/**
|
|
* POST /api/carrello/aggiungi
|
|
* Aggiunge un prodotto configurato al carrello (preventivo in stato bozza).
|
|
*/
|
|
public function aggiungi(Request $request): JsonResponse
|
|
{
|
|
$data = $request->validate([
|
|
'token' => ['nullable', 'string', 'max:64'],
|
|
'sistema' => ['nullable', 'string', 'max:50'],
|
|
'type' => ['nullable', 'string', 'max:100'],
|
|
'nome' => ['nullable', 'string', 'max:255'],
|
|
'larghezza' => ['nullable', 'integer', 'min:0'],
|
|
'altezza' => ['nullable', 'integer', 'min:0'],
|
|
'qty' => ['nullable', 'integer', 'min:1', 'max:999'],
|
|
'prezzo' => ['required', 'numeric', 'min:0'],
|
|
'payload' => ['nullable', 'array'],
|
|
'img' => ['nullable', 'string'],
|
|
]);
|
|
|
|
$preventivo = $this->risolviCarrello($data['token'] ?? null);
|
|
|
|
$qty = $data['qty'] ?? 1;
|
|
$prezzoUnitario = round((float) $data['prezzo'], 2);
|
|
|
|
$item = new PreventivoItem([
|
|
'sistema' => $data['sistema'] ?? null,
|
|
'type' => $data['type'] ?? null,
|
|
'nome_modello' => $data['nome'] ?? ($data['payload']['nome'] ?? null),
|
|
'larghezza' => $data['larghezza'] ?? ($data['payload']['width'] ?? null),
|
|
'altezza' => $data['altezza'] ?? ($data['payload']['height'] ?? null),
|
|
'quantita' => $qty,
|
|
'prezzo_unitario' => $prezzoUnitario,
|
|
'prezzo_totale' => round($prezzoUnitario * $qty, 2),
|
|
'payload' => $data['payload'] ?? [],
|
|
'immagine_path' => $this->salvaImmagine($data['img'] ?? null),
|
|
]);
|
|
|
|
$preventivo->items()->save($item);
|
|
$preventivo->ricalcolaTotale();
|
|
|
|
return response()->json([
|
|
'token' => $preventivo->token,
|
|
'carrello' => $this->serializzaCarrello($preventivo->fresh('items')),
|
|
], 201);
|
|
}
|
|
|
|
/** GET /api/carrello/{token} */
|
|
public function mostra(string $token): JsonResponse
|
|
{
|
|
$preventivo = Preventivo::where('token', $token)->where('stato', 'bozza')
|
|
->with('items')->first();
|
|
|
|
if (! $preventivo) {
|
|
return response()->json(['token' => $token, 'carrello' => ['items' => [], 'totale' => 0]]);
|
|
}
|
|
|
|
return response()->json([
|
|
'token' => $preventivo->token,
|
|
'carrello' => $this->serializzaCarrello($preventivo),
|
|
]);
|
|
}
|
|
|
|
/** DELETE /api/carrello/{token}/item/{item} */
|
|
public function rimuoviItem(string $token, int $item): JsonResponse
|
|
{
|
|
$preventivo = Preventivo::where('token', $token)->where('stato', 'bozza')
|
|
->with('items')->firstOrFail();
|
|
|
|
$preventivo->items()->whereKey($item)->delete();
|
|
$preventivo->ricalcolaTotale();
|
|
|
|
return response()->json([
|
|
'token' => $preventivo->token,
|
|
'carrello' => $this->serializzaCarrello($preventivo->fresh('items')),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* POST /api/carrello/{token}/conferma
|
|
* Raccoglie i dati cliente, finalizza il preventivo, genera il PDF e invia le email.
|
|
*/
|
|
public function conferma(Request $request, string $token): JsonResponse
|
|
{
|
|
$preventivo = Preventivo::where('token', $token)->where('stato', 'bozza')
|
|
->with('items')->firstOrFail();
|
|
|
|
if ($preventivo->items->isEmpty()) {
|
|
return response()->json(['message' => 'Il carrello è vuoto.'], 422);
|
|
}
|
|
|
|
$dati = $request->validate([
|
|
'nome' => ['required', 'string', 'max:255'],
|
|
'cognome' => ['nullable', 'string', 'max:255'],
|
|
'ragione_sociale' => ['nullable', 'string', 'max:255'],
|
|
'email' => ['required', 'email', 'max:255'],
|
|
'telefono' => ['nullable', 'string', 'max:50'],
|
|
'indirizzo' => ['nullable', 'string', 'max:255'],
|
|
'cap' => ['nullable', 'string', 'max:10'],
|
|
'citta' => ['nullable', 'string', 'max:255'],
|
|
'provincia' => ['nullable', 'string', 'max:5'],
|
|
'note' => ['nullable', 'string', 'max:2000'],
|
|
]);
|
|
|
|
DB::transaction(function () use ($preventivo, $dati) {
|
|
$cliente = Cliente::create($dati);
|
|
|
|
$preventivo->cliente_id = $cliente->id;
|
|
$preventivo->stato = 'inviato';
|
|
$preventivo->numero = Preventivo::prossimoNumero();
|
|
$preventivo->confirmed_at = now();
|
|
$preventivo->save();
|
|
});
|
|
|
|
$preventivo->refresh()->load('cliente', 'items');
|
|
|
|
// Genera il PDF e salva il path.
|
|
$pdfPath = PreventivoPdf::genera($preventivo);
|
|
$preventivo->pdf_path = $pdfPath;
|
|
$preventivo->save();
|
|
|
|
// Invia le email (cliente con PDF allegato, admin di notifica).
|
|
$this->inviaEmail($preventivo);
|
|
|
|
return response()->json([
|
|
'message' => 'Preventivo inviato. Verrai ricontattato al più presto.',
|
|
'numero' => $preventivo->numero,
|
|
]);
|
|
}
|
|
|
|
/** Trova il carrello bozza per token o ne crea uno nuovo. */
|
|
private function risolviCarrello(?string $token): Preventivo
|
|
{
|
|
if ($token) {
|
|
$preventivo = Preventivo::where('token', $token)->where('stato', 'bozza')->first();
|
|
if ($preventivo) {
|
|
return $preventivo;
|
|
}
|
|
}
|
|
|
|
return Preventivo::create([
|
|
'token' => (string) Str::uuid(),
|
|
'stato' => 'bozza',
|
|
'totale' => 0,
|
|
]);
|
|
}
|
|
|
|
/** Decodifica un data URL base64 e salva il PNG; ritorna il path relativo (disk public). */
|
|
private function salvaImmagine(?string $dataUrl): ?string
|
|
{
|
|
if (! $dataUrl || ! Str::startsWith($dataUrl, 'data:image')) {
|
|
return null;
|
|
}
|
|
|
|
[$meta, $contenuto] = array_pad(explode(',', $dataUrl, 2), 2, null);
|
|
if ($contenuto === null) {
|
|
return null;
|
|
}
|
|
|
|
$binario = base64_decode($contenuto, true);
|
|
if ($binario === false) {
|
|
return null;
|
|
}
|
|
|
|
$path = 'preventivi/items/'.Str::uuid()->toString().'.png';
|
|
Storage::disk('public')->put($path, $binario);
|
|
|
|
return $path;
|
|
}
|
|
|
|
/** @return array{items:array,totale:float} */
|
|
private function serializzaCarrello(Preventivo $preventivo): array
|
|
{
|
|
return [
|
|
'items' => $preventivo->items->map(fn (PreventivoItem $item) => [
|
|
'id' => $item->id,
|
|
'sistema' => $item->sistema,
|
|
'type' => $item->type,
|
|
'nome_modello' => $item->nome_modello,
|
|
'larghezza' => $item->larghezza,
|
|
'altezza' => $item->altezza,
|
|
'quantita' => $item->quantita,
|
|
'prezzo_unitario' => (float) $item->prezzo_unitario,
|
|
'prezzo_totale' => (float) $item->prezzo_totale,
|
|
'immagine_url' => $item->immagine_path ? Storage::disk('public')->url($item->immagine_path) : null,
|
|
'configurazione' => ConfigurazioneFormatter::toRows($item->payload),
|
|
])->all(),
|
|
'totale' => (float) $preventivo->totale,
|
|
];
|
|
}
|
|
|
|
private function inviaEmail(Preventivo $preventivo): void
|
|
{
|
|
if ($preventivo->cliente?->email) {
|
|
Mail::to($preventivo->cliente->email)->send(new PreventivoCliente($preventivo));
|
|
}
|
|
|
|
$adminEmail = config('preventivi.admin_email');
|
|
if ($adminEmail) {
|
|
Mail::to($adminEmail)->send(new PreventivoAdmin($preventivo));
|
|
}
|
|
}
|
|
}
|