netgescon-day0/app/Services/Consumi/GasLucePdfTextParser.php

136 lines
5.1 KiB
PHP

<?php
namespace App\Services\Consumi;
use Carbon\Carbon;
class GasLucePdfTextParser
{
/**
* @return array{
* codici: array{cliente: string|null, contratto: string|null, pdr: string|null, pod: string|null},
* pagamento: array{scadenza: string|null},
* consumi: array<int, array{dal: string|null, al: string|null, valore: float|null, unita: string, tipologia_lettura: string}>
* }
*/
public function parse(string $text): array
{
$text = str_replace("\r\n", "\n", $text);
$text = str_replace("\r", "\n", $text);
// Cliente
$cliente = $this->firstMatch($text, '/(?:Codice\s+Cliente|Cod\.\s*Cliente|Cliente\s*n\.|Client\s*code)\s*:?\s*([A-Z0-9_]{4,})/i');
// Contratto
$contratto = $this->firstMatch($text, '/(?:Contratto|Cod\.\s*Contratto|Codice\s+Contratto|Contract\s*n\.)\s*:?\s*([A-Z0-9_]{4,})/i');
// PDR / POD
$pdr = $this->firstMatch($text, '/\bPDR\b\s*:?\s*([0-9]{14})\b/i');
$pod = $this->firstMatch($text, '/\bPOD\b\s*:?\s*([A-Z0-9]{14,15})\b/i');
// Scadenza
$scadenza = $this->toIsoDate($this->firstMatch($text, '/(?:Scadenza|Entro\s+il|Data\s+scadenza)\s*:?\s*(\d{2}\/\d{2}\/\d{4})/i'));
// Cerca consumi del tipo: "Consumo Fatturato APRILE 2026 910 Smc"
// O genericamente "Consumo... [valore] Smc/kWh"
$consumi = [];
if (preg_match_all('/(?:Consumo\s+Fatturato|Consumo|Consumo\s+rilevato|Consumo\s+stimato)\s+(?:([A-Z]+)\s+(\d{4})\s+)?(\d+(?:[\.,]\d+)?)\s*(Smc|kWh)/i', $text, $matches, PREG_SET_ORDER)) {
foreach ($matches as $m) {
$monthName = !empty($m[1]) ? strtoupper(trim($m[1])) : null;
$yearVal = !empty($m[2]) ? (int) $m[2] : null;
$valore = $this->toFloatIt($m[3]);
$unita = trim($m[4]);
$dal = null;
$al = null;
if ($monthName && $yearVal) {
$months = [
'GENNAIO' => 1, 'FEBBRAIO' => 2, 'MARZO' => 3, 'APRILE' => 4,
'MAGGIO' => 5, 'GIUGNO' => 6, 'LUGLIO' => 7, 'AGOSTO' => 8,
'SETTEMBRE' => 9, 'OTTOBRE' => 10, 'NOVEMBRE' => 11, 'DICEMBRE' => 12,
'JANUARY' => 1, 'FEBRUARY' => 2, 'MARCH' => 3, 'APRIL' => 4,
'MAY' => 5, 'JUNE' => 6, 'JULY' => 7, 'AUGUST' => 8,
'SEPTEMBER' => 9, 'OCTOBER' => 10, 'NOVEMBER' => 11, 'DECEMBER' => 12
];
$mNum = $months[$monthName] ?? null;
if ($mNum) {
$dal = Carbon::create($yearVal, $mNum, 1)->startOfMonth()->format('Y-m-d');
$al = Carbon::create($yearVal, $mNum, 1)->endOfMonth()->format('Y-m-d');
}
}
$consumi[] = [
'dal' => $dal,
'al' => $al,
'valore' => $valore,
'unita' => $unita,
'tipologia_lettura' => 'fatturata',
];
}
}
// Se non troviamo date dai consumi ma c'è un intervallo di date nel testo, tipo "dal 01/04/2026 al 30/04/2026"
if (count($consumi) === 0 && preg_match('/dal\s+(\d{2}\/\d{2}\/\d{4})\s+al\s+(\d{2}\/\d{2}\/\d{4})/i', $text, $mDates)) {
$dal = $this->toIsoDate($mDates[1]);
$al = $this->toIsoDate($mDates[2]);
// Prova a estrarre il valore di consumo vicino a Smc o kWh
$val = null;
$uni = 'Smc';
if (preg_match('/(\d+(?:[\.,]\d+)?)\s*(Smc|kWh)/i', $text, $mVal)) {
$val = $this->toFloatIt($mVal[1]);
$uni = $mVal[2];
}
if ($val !== null) {
$consumi[] = [
'dal' => $dal,
'al' => $al,
'valore' => $val,
'unita' => $uni,
'tipologia_lettura' => 'rilevata',
];
}
}
return [
'codici' => [
'cliente' => $cliente,
'contratto' => $contratto,
'pdr' => $pdr,
'pod' => $pod,
],
'pagamento' => [
'scadenza' => $scadenza,
],
'consumi' => $consumi,
];
}
private function firstMatch(string $text, string $pattern): ?string
{
if (preg_match($pattern, $text, $matches)) {
return trim($matches[1]);
}
return null;
}
private function toFloatIt(mixed $val): ?float
{
if ($val === null) return null;
$val = str_replace('.', '', $val);
$val = str_replace(',', '.', $val);
return is_numeric($val) ? (float) $val : null;
}
private function toIsoDate(?string $value): ?string
{
if (! $value) {
return null;
}
try {
return Carbon::createFromFormat('d/m/Y', $value)->format('Y-m-d');
} catch (\Throwable) {
return null;
}
}
}