backoffice first commit
This commit is contained in:
@@ -0,0 +1,223 @@
|
||||
<?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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
abstract class Controller
|
||||
{
|
||||
//
|
||||
}
|
||||
Reference in New Issue
Block a user