feat(contabilita): atomic multicanale contacts, CF conguaglio badges and modernized situazione iniziale UI
This commit is contained in:
parent
88829fcace
commit
3699d388cd
|
|
@ -1281,10 +1281,7 @@ private function loadDettTabCrudRows(string $codStabileLegacy, ?string $legacyYe
|
|||
}
|
||||
|
||||
$q = DB::connection('gescon_import')->table('dett_tab')
|
||||
->where('cod_tab', 'CONG.O')
|
||||
->where(function ($query) {
|
||||
$query->whereNull('n_stra')->orWhere('n_stra', '<=', 0);
|
||||
});
|
||||
->where('cod_tab', 'like', 'CONG.%');
|
||||
|
||||
if (Schema::connection('gescon_import')->hasColumn('dett_tab', 'cod_stabile')) {
|
||||
$hasForStabile = DB::connection('gescon_import')->table('dett_tab')
|
||||
|
|
@ -1458,15 +1455,17 @@ public function saveDettTabRow(int $id): void
|
|||
$cons = 0.0;
|
||||
}
|
||||
|
||||
$codTab = ! empty($edit['cod_tab']) ? trim((string) $edit['cod_tab']) : 'CONG.O';
|
||||
|
||||
DB::connection('gescon_import')->table('dett_tab')
|
||||
->where('id', $id)
|
||||
->update([
|
||||
'cod_tab' => 'CONG.O',
|
||||
'cod_tab' => $codTab,
|
||||
'id_cond' => is_string($edit['id_cond'] ?? null) ? trim((string) $edit['id_cond']) : null,
|
||||
'cond_inquil' => is_string($edit['cond_inquil'] ?? null) ? trim((string) $edit['cond_inquil']) : null,
|
||||
'mm' => is_numeric($edit['mm'] ?? null) ? (float) $edit['mm'] : null,
|
||||
'cons_euro' => round((float) $cons, 2),
|
||||
'n_stra' => null,
|
||||
'n_stra' => is_numeric($edit['n_stra'] ?? null) ? (int) $edit['n_stra'] : null,
|
||||
'unico' => ! empty($edit['unico']) ? 1 : 0,
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
|
@ -1497,7 +1496,7 @@ public function addDettTabRow(): void
|
|||
return;
|
||||
}
|
||||
|
||||
$codTab = 'CONG.O';
|
||||
$codTab = ! empty($this->formDettTabAdd['cod_tab']) ? trim((string) $this->formDettTabAdd['cod_tab']) : 'CONG.O';
|
||||
$idCond = is_string($this->formDettTabAdd['id_cond'] ?? null) ? trim((string) $this->formDettTabAdd['id_cond']) : '';
|
||||
if ($codTab === '' || $idCond === '') {
|
||||
return;
|
||||
|
|
@ -1514,7 +1513,7 @@ public function addDettTabRow(): void
|
|||
'cond_inquil' => is_string($this->formDettTabAdd['cond_inquil'] ?? null) ? trim((string) $this->formDettTabAdd['cond_inquil']) : null,
|
||||
'mm' => is_numeric($this->formDettTabAdd['mm'] ?? null) ? (float) $this->formDettTabAdd['mm'] : null,
|
||||
'cons_euro' => round((float) $cons, 2),
|
||||
'n_stra' => null,
|
||||
'n_stra' => is_numeric($this->formDettTabAdd['n_stra'] ?? null) ? (int) $this->formDettTabAdd['n_stra'] : null,
|
||||
'unico' => ! empty($this->formDettTabAdd['unico']) ? 1 : 0,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
|
|
|
|||
|
|
@ -159,6 +159,8 @@ public static function canAccess(): bool
|
|||
*/
|
||||
public string $estrattoTipo = 'condomini';
|
||||
|
||||
public string $recapitiViewMode = 'diviso';
|
||||
|
||||
/**
|
||||
* @var array<int, string>
|
||||
*/
|
||||
|
|
@ -539,6 +541,8 @@ public function getRecapitiMulticanaleTableProperty(): array
|
|||
continue;
|
||||
}
|
||||
|
||||
$tipoSoggetto = in_array(strtolower($ruolo), ['inquilino', 'conduttore', 'locatario'], true) || (string) ($entity['tipo_relazione'] ?? '') === 'inquilino' ? 'inquilino' : 'condomino';
|
||||
|
||||
$anagId = (int) ($entity['persona_id'] ?? ($entity['soggetto_id'] ?? 0));
|
||||
$anag = null;
|
||||
if ($anagId > 0) {
|
||||
|
|
@ -589,15 +593,16 @@ public function getRecapitiMulticanaleTableProperty(): array
|
|||
$cleanPrefix = ucwords(str_replace(['.', '_', '-'], ' ', $prefix));
|
||||
$lbl = count($parts) > 1 ? ($idx === 0 ? 'Principale (' . $cleanPrefix . ')' : 'Email ' . $cleanPrefix) : 'Principale';
|
||||
$tableRows[] = [
|
||||
'canale' => 'Email',
|
||||
'icon' => '✉️',
|
||||
'color' => 'bg-blue-50 text-blue-700 border-blue-200',
|
||||
'etichetta' => $lbl,
|
||||
'soggetto' => $nome,
|
||||
'ruolo' => $ruolo,
|
||||
'valore' => $em,
|
||||
'action_url' => 'mailto:' . $em,
|
||||
'is_principal' => $idx === 0 && count($tableRows) === 0,
|
||||
'canale' => 'Email',
|
||||
'icon' => '✉️',
|
||||
'color' => 'bg-blue-50 text-blue-700 border-blue-200',
|
||||
'etichetta' => $lbl,
|
||||
'soggetto' => $nome,
|
||||
'ruolo' => $ruolo,
|
||||
'tipo_soggetto' => $tipoSoggetto,
|
||||
'valore' => $em,
|
||||
'action_url' => 'mailto:' . $em,
|
||||
'is_principal' => $idx === 0 && count($tableRows) === 0,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -608,15 +613,16 @@ public function getRecapitiMulticanaleTableProperty(): array
|
|||
$pc = trim($pc);
|
||||
if ($pc !== '' && ! $this->hasContactValue($tableRows, $pc)) {
|
||||
$tableRows[] = [
|
||||
'canale' => 'Email PEC',
|
||||
'icon' => '🛡️',
|
||||
'color' => 'bg-emerald-50 text-emerald-700 border-emerald-200',
|
||||
'etichetta' => count($parts) > 1 ? 'PEC #' . ($idx + 1) : 'PEC Ufficiale',
|
||||
'soggetto' => $nome,
|
||||
'ruolo' => $ruolo,
|
||||
'valore' => $pc,
|
||||
'action_url' => 'mailto:' . $pc,
|
||||
'is_principal' => false,
|
||||
'canale' => 'Email PEC',
|
||||
'icon' => '🛡️',
|
||||
'color' => 'bg-emerald-50 text-emerald-700 border-emerald-200',
|
||||
'etichetta' => count($parts) > 1 ? 'PEC #' . ($idx + 1) : 'PEC Ufficiale',
|
||||
'soggetto' => $nome,
|
||||
'ruolo' => $ruolo,
|
||||
'tipo_soggetto' => $tipoSoggetto,
|
||||
'valore' => $pc,
|
||||
'action_url' => 'mailto:' . $pc,
|
||||
'is_principal' => false,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -627,30 +633,32 @@ public function getRecapitiMulticanaleTableProperty(): array
|
|||
$ph = trim($ph);
|
||||
if ($ph !== '' && ! $this->hasContactValue($tableRows, $ph)) {
|
||||
$tableRows[] = [
|
||||
'canale' => 'Cellulare / Tel',
|
||||
'icon' => '📱',
|
||||
'color' => 'bg-amber-50 text-amber-700 border-amber-200',
|
||||
'etichetta' => count($parts) > 1 ? 'Telefono #' . ($idx + 1) : 'Personale',
|
||||
'soggetto' => $nome,
|
||||
'ruolo' => $ruolo,
|
||||
'valore' => $ph,
|
||||
'action_url' => 'tel:' . preg_replace('/[^\d\+]/', '', $ph),
|
||||
'is_principal' => false,
|
||||
'canale' => 'Cellulare / Tel',
|
||||
'icon' => '📱',
|
||||
'color' => 'bg-amber-50 text-amber-700 border-amber-200',
|
||||
'etichetta' => count($parts) > 1 ? 'Telefono #' . ($idx + 1) : 'Personale',
|
||||
'soggetto' => $nome,
|
||||
'ruolo' => $ruolo,
|
||||
'tipo_soggetto' => $tipoSoggetto,
|
||||
'valore' => $ph,
|
||||
'action_url' => 'tel:' . preg_replace('/[^\d\+]/', '', $ph),
|
||||
'is_principal' => false,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($rawInd !== '') {
|
||||
$tableRows[] = [
|
||||
'canale' => 'Indirizzo',
|
||||
'icon' => '📍',
|
||||
'color' => 'bg-slate-50 text-slate-700 border-slate-200',
|
||||
'etichetta' => 'Residenza / Sede',
|
||||
'soggetto' => $nome,
|
||||
'ruolo' => $ruolo,
|
||||
'valore' => $rawInd,
|
||||
'action_url' => null,
|
||||
'is_principal' => false,
|
||||
'canale' => 'Indirizzo',
|
||||
'icon' => '📍',
|
||||
'color' => 'bg-slate-50 text-slate-700 border-slate-200',
|
||||
'etichetta' => 'Residenza / Sede',
|
||||
'soggetto' => $nome,
|
||||
'ruolo' => $ruolo,
|
||||
'tipo_soggetto' => $tipoSoggetto,
|
||||
'valore' => $rawInd,
|
||||
'action_url' => null,
|
||||
'is_principal' => false,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -671,15 +679,16 @@ public function getRecapitiMulticanaleTableProperty(): array
|
|||
$cleanPrefix = ucwords(str_replace(['.', '_', '-'], ' ', $prefix));
|
||||
$lbl = count($parts) > 1 ? ($idx === 0 ? 'Email Condomino (' . $cleanPrefix . ')' : 'Email ' . $cleanPrefix) : 'Email Condomino';
|
||||
$tableRows[] = [
|
||||
'canale' => 'Email',
|
||||
'icon' => '✉️',
|
||||
'color' => 'bg-blue-50 text-blue-700 border-blue-200',
|
||||
'etichetta' => $lbl,
|
||||
'soggetto' => $condName,
|
||||
'ruolo' => 'Condomino',
|
||||
'valore' => $em,
|
||||
'action_url' => 'mailto:' . $em,
|
||||
'is_principal' => count($tableRows) === 0,
|
||||
'canale' => 'Email',
|
||||
'icon' => '✉️',
|
||||
'color' => 'bg-blue-50 text-blue-700 border-blue-200',
|
||||
'etichetta' => $lbl,
|
||||
'soggetto' => $condName,
|
||||
'ruolo' => 'Condomino',
|
||||
'tipo_soggetto' => 'condomino',
|
||||
'valore' => $em,
|
||||
'action_url' => 'mailto:' . $em,
|
||||
'is_principal' => count($tableRows) === 0,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -690,15 +699,16 @@ public function getRecapitiMulticanaleTableProperty(): array
|
|||
$pc = trim($pc);
|
||||
if ($pc !== '' && ! $this->hasContactValue($tableRows, $pc)) {
|
||||
$tableRows[] = [
|
||||
'canale' => 'Email PEC',
|
||||
'icon' => '🛡️',
|
||||
'color' => 'bg-emerald-50 text-emerald-700 border-emerald-200',
|
||||
'etichetta' => count($parts) > 1 ? 'PEC Condomino #' . ($idx + 1) : 'PEC Condomino',
|
||||
'soggetto' => $condName,
|
||||
'ruolo' => 'Condomino',
|
||||
'valore' => $pc,
|
||||
'action_url' => 'mailto:' . $pc,
|
||||
'is_principal' => false,
|
||||
'canale' => 'Email PEC',
|
||||
'icon' => '🛡️',
|
||||
'color' => 'bg-emerald-50 text-emerald-700 border-emerald-200',
|
||||
'etichetta' => count($parts) > 1 ? 'PEC Condomino #' . ($idx + 1) : 'PEC Condomino',
|
||||
'soggetto' => $condName,
|
||||
'ruolo' => 'Condomino',
|
||||
'tipo_soggetto' => 'condomino',
|
||||
'valore' => $pc,
|
||||
'action_url' => 'mailto:' . $pc,
|
||||
'is_principal' => false,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -709,15 +719,16 @@ public function getRecapitiMulticanaleTableProperty(): array
|
|||
$ph = trim($ph);
|
||||
if ($ph !== '' && ! $this->hasContactValue($tableRows, $ph)) {
|
||||
$tableRows[] = [
|
||||
'canale' => 'Cellulare',
|
||||
'icon' => '📱',
|
||||
'color' => 'bg-amber-50 text-amber-700 border-amber-200',
|
||||
'etichetta' => count($parts) > 1 ? 'Cellulare Condomino #' . ($idx + 1) : 'Cellulare Condomino',
|
||||
'soggetto' => $condName,
|
||||
'ruolo' => 'Condomino',
|
||||
'valore' => $ph,
|
||||
'action_url' => 'tel:' . preg_replace('/[^\d\+]/', '', $ph),
|
||||
'is_principal' => false,
|
||||
'canale' => 'Cellulare',
|
||||
'icon' => '📱',
|
||||
'color' => 'bg-amber-50 text-amber-700 border-amber-200',
|
||||
'etichetta' => count($parts) > 1 ? 'Cellulare Condomino #' . ($idx + 1) : 'Cellulare Condomino',
|
||||
'soggetto' => $condName,
|
||||
'ruolo' => 'Condomino',
|
||||
'tipo_soggetto' => 'condomino',
|
||||
'valore' => $ph,
|
||||
'action_url' => 'tel:' . preg_replace('/[^\d\+]/', '', $ph),
|
||||
'is_principal' => false,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -728,15 +739,16 @@ public function getRecapitiMulticanaleTableProperty(): array
|
|||
$ph = trim($ph);
|
||||
if ($ph !== '' && ! $this->hasContactValue($tableRows, $ph)) {
|
||||
$tableRows[] = [
|
||||
'canale' => 'Telefono Fisso',
|
||||
'icon' => '☎️',
|
||||
'color' => 'bg-purple-50 text-purple-700 border-purple-200',
|
||||
'etichetta' => count($parts) > 1 ? 'Fisso Condomino #' . ($idx + 1) : 'Fisso Condomino',
|
||||
'soggetto' => $condName,
|
||||
'ruolo' => 'Condomino',
|
||||
'valore' => $ph,
|
||||
'action_url' => 'tel:' . preg_replace('/[^\d\+]/', '', $ph),
|
||||
'is_principal' => false,
|
||||
'canale' => 'Telefono Fisso',
|
||||
'icon' => '☎️',
|
||||
'color' => 'bg-purple-50 text-purple-700 border-purple-200',
|
||||
'etichetta' => count($parts) > 1 ? 'Fisso Condomino #' . ($idx + 1) : 'Fisso Condomino',
|
||||
'soggetto' => $condName,
|
||||
'ruolo' => 'Condomino',
|
||||
'tipo_soggetto' => 'condomino',
|
||||
'valore' => $ph,
|
||||
'action_url' => 'tel:' . preg_replace('/[^\d\+]/', '', $ph),
|
||||
'is_principal' => false,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -750,15 +762,16 @@ public function getRecapitiMulticanaleTableProperty(): array
|
|||
$cleanPrefix = ucwords(str_replace(['.', '_', '-'], ' ', $prefix));
|
||||
$lbl = count($parts) > 1 ? ($idx === 0 ? 'Email Inquilino (' . $cleanPrefix . ')' : 'Email ' . $cleanPrefix) : 'Email Inquilino';
|
||||
$tableRows[] = [
|
||||
'canale' => 'Email',
|
||||
'icon' => '✉️',
|
||||
'color' => 'bg-blue-50 text-blue-700 border-blue-200',
|
||||
'etichetta' => $lbl,
|
||||
'soggetto' => $inqName,
|
||||
'ruolo' => 'Inquilino',
|
||||
'valore' => $em,
|
||||
'action_url' => 'mailto:' . $em,
|
||||
'is_principal' => false,
|
||||
'canale' => 'Email',
|
||||
'icon' => '✉️',
|
||||
'color' => 'bg-blue-50 text-blue-700 border-blue-200',
|
||||
'etichetta' => $lbl,
|
||||
'soggetto' => $inqName,
|
||||
'ruolo' => 'Inquilino',
|
||||
'tipo_soggetto' => 'inquilino',
|
||||
'valore' => $em,
|
||||
'action_url' => 'mailto:' . $em,
|
||||
'is_principal' => false,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -769,15 +782,16 @@ public function getRecapitiMulticanaleTableProperty(): array
|
|||
$ph = trim($ph);
|
||||
if ($ph !== '' && ! $this->hasContactValue($tableRows, $ph)) {
|
||||
$tableRows[] = [
|
||||
'canale' => 'Cellulare',
|
||||
'icon' => '📱',
|
||||
'color' => 'bg-amber-50 text-amber-700 border-amber-200',
|
||||
'etichetta' => count($parts) > 1 ? 'Cellulare Inquilino #' . ($idx + 1) : 'Cellulare Inquilino',
|
||||
'soggetto' => $inqName,
|
||||
'ruolo' => 'Inquilino',
|
||||
'valore' => $ph,
|
||||
'action_url' => 'tel:' . preg_replace('/[^\d\+]/', '', $ph),
|
||||
'is_principal' => false,
|
||||
'canale' => 'Cellulare',
|
||||
'icon' => '📱',
|
||||
'color' => 'bg-amber-50 text-amber-700 border-amber-200',
|
||||
'etichetta' => count($parts) > 1 ? 'Cellulare Inquilino #' . ($idx + 1) : 'Cellulare Inquilino',
|
||||
'soggetto' => $inqName,
|
||||
'ruolo' => 'Inquilino',
|
||||
'tipo_soggetto' => 'inquilino',
|
||||
'valore' => $ph,
|
||||
'action_url' => 'tel:' . preg_replace('/[^\d\+]/', '', $ph),
|
||||
'is_principal' => false,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1563,6 +1577,14 @@ public function setEstrattoTipo(string $tipo): void
|
|||
$this->condInquilActive = $tipo === 'inquilini' ? 'I' : 'C';
|
||||
}
|
||||
|
||||
public function setRecapitiViewMode(string $mode): void
|
||||
{
|
||||
$mode = strtolower(trim($mode));
|
||||
if (in_array($mode, ['diviso', 'tutti', 'condomino', 'inquilino'], true)) {
|
||||
$this->recapitiViewMode = $mode;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{0:string,1:int,2:string}
|
||||
*/
|
||||
|
|
@ -3813,11 +3835,67 @@ protected function hydrateRelazioni(): void
|
|||
];
|
||||
});
|
||||
|
||||
$uapRelations = collect();
|
||||
if (DbSchema::hasTable('unita_anagrafica_periodo') && DbSchema::hasTable('anagrafiche')) {
|
||||
try {
|
||||
$startOfYear = "{$activeAnno}-01-01";
|
||||
$endOfYear = "{$activeAnno}-12-31";
|
||||
|
||||
$uapList = DB::table('unita_anagrafica_periodo as uap')
|
||||
->join('anagrafiche as a', 'uap.anagrafica_id', '=', 'a.id')
|
||||
->where('uap.unita_immobiliare_id', $this->unita->id)
|
||||
->where(function ($q) use ($endOfYear) {
|
||||
$q->whereNull('uap.data_inizio')
|
||||
->orWhere('uap.data_inizio', '<=', $endOfYear);
|
||||
})
|
||||
->where(function ($q) use ($startOfYear) {
|
||||
$q->whereNull('uap.data_fine')
|
||||
->orWhere('uap.data_fine', '>=', $startOfYear);
|
||||
})
|
||||
->orderBy('uap.id')
|
||||
->select('uap.*', 'a.nome', 'a.cognome', 'a.ragione_sociale', 'a.codice_fiscale')
|
||||
->get();
|
||||
|
||||
foreach ($uapList as $uRow) {
|
||||
$nome = trim((string) ($uRow->ragione_sociale ?: ($uRow->cognome . ' ' . $uRow->nome)));
|
||||
if ($nome === '') {
|
||||
$nome = trim((string) ($uRow->nome . ' ' . $uRow->cognome));
|
||||
}
|
||||
$tipoRaw = strtolower(trim((string) ($uRow->ruolo_occupazione ?? 'condomino')));
|
||||
$uapRelations->push([
|
||||
'id' => (int) $uRow->id,
|
||||
'persona_id' => (int) $uRow->anagrafica_id,
|
||||
'legacy_persona_id' => (int) $uRow->anagrafica_id,
|
||||
'rubrica_id' => null,
|
||||
'nome' => $nome,
|
||||
'codice_fiscale' => $uRow->codice_fiscale,
|
||||
'tipo_raw' => $tipoRaw,
|
||||
'tipo' => ucfirst(str_replace('_', ' ', $tipoRaw)),
|
||||
'ruolo_custom' => null,
|
||||
'has_custom_role' => false,
|
||||
'ruolo_rate' => $tipoRaw === 'inquilino' ? 'I' : 'C',
|
||||
'quota' => 100.0,
|
||||
'quota_label' => '100,00',
|
||||
'data_inizio' => $formatDate($uRow->data_inizio),
|
||||
'data_fine' => $formatDate($uRow->data_fine),
|
||||
'attivo' => $uRow->data_fine === null || $uRow->data_fine >= date('Y-m-d'),
|
||||
'telefono' => '',
|
||||
'email' => '',
|
||||
'rubrica_url' => null,
|
||||
'riceve_comunicazioni' => true,
|
||||
'riceve_convocazioni' => true,
|
||||
'vota_assemblea' => true,
|
||||
]);
|
||||
}
|
||||
} catch (\Throwable $e) {}
|
||||
}
|
||||
|
||||
$fallbackKeys = $fallbackRelations
|
||||
->map(fn(array $rel) => strtolower(trim((string) ($rel['tipo_raw'] ?? ''))) . '|' . $this->relationIdentityKey($rel))
|
||||
->all();
|
||||
|
||||
$relazioniMapped = $fallbackRelations
|
||||
$relazioniMapped = $uapRelations
|
||||
->concat($fallbackRelations)
|
||||
->concat($relazioniMapped->reject(function (array $rel) use ($fallbackKeys): bool {
|
||||
$key = strtolower(trim((string) ($rel['tipo_raw'] ?? ''))) . '|' . $this->relationIdentityKey($rel);
|
||||
|
||||
|
|
@ -4050,6 +4128,22 @@ protected function hydrateRelazioni(): void
|
|||
if ($matchedInquilini->isNotEmpty()) {
|
||||
$inquilini = $matchedInquilini;
|
||||
$relazioniStoriche = array_values(array_unique(array_merge($relazioniStoriche, $unmatchedInquilini), SORT_REGULAR));
|
||||
} else {
|
||||
$legacyInqRecord = [
|
||||
'persona_id' => null,
|
||||
'nome' => $legacyInqName,
|
||||
'codice_fiscale'=> trim((string) ($legacyRow?->inquil_cod_fisc ?? '')),
|
||||
'tipo_relazione'=> 'inquilino',
|
||||
'ruolo_label' => 'Inquilino / Conduttore',
|
||||
'quota' => 100.0,
|
||||
'quota_label' => '100,00',
|
||||
'data_inizio' => 'Gestione ' . $activeAnno,
|
||||
'data_fine' => null,
|
||||
'attivo' => true,
|
||||
'fonte' => 'legacy_condomin_mirror',
|
||||
];
|
||||
$relazioniStoriche = array_values(array_unique(array_merge($relazioniStoriche, $inquilini->all()), SORT_REGULAR));
|
||||
$inquilini = collect([$legacyInqRecord]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -317,23 +317,47 @@ class="inline-flex items-center gap-2 rounded-lg border px-3 py-2 text-sm font-s
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{{-- 2. SEZIONE RECAPITI MULTICANALE DINAMICI (MATRICE TABELLARE COMPATTA) --}}
|
||||
{{-- 2. SEZIONE RECAPITI MULTICANALE DINAMICI (MATRICE ATOMICA DIVISA COND/INQ + AGGREGATA) --}}
|
||||
@php
|
||||
$multiRecapiti = $this->getRecapitiMulticanaleTableProperty();
|
||||
$recapitiCondomino = array_values(array_filter($multiRecapiti, fn($r) => ($r['tipo_soggetto'] ?? 'condomino') === 'condomino'));
|
||||
$recapitiInquilino = array_values(array_filter($multiRecapiti, fn($r) => ($r['tipo_soggetto'] ?? '') === 'inquilino'));
|
||||
$currentRecapitiMode = $this->recapitiViewMode ?? 'diviso';
|
||||
@endphp
|
||||
|
||||
<div class="rounded-xl border border-gray-200 bg-white shadow-xs overflow-hidden mt-4">
|
||||
<div class="flex items-center justify-between bg-gray-50/80 px-4 py-3 border-b border-gray-200">
|
||||
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between bg-gray-50/80 px-4 py-3 border-b border-gray-200 gap-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-base">📱</span>
|
||||
<div>
|
||||
<div class="text-sm font-bold text-gray-900">Recapiti Multicanale Dinamici dell'Unità</div>
|
||||
<div class="text-[11px] text-gray-500">Email, PEC, cellulari, telefoni e recapiti postali dei soggetti collegati</div>
|
||||
<div class="text-[11px] text-gray-500">Email, PEC, cellulari, telefoni e recapiti postali divisi per Condomino e Inquilino</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="rounded-full bg-indigo-50 border border-indigo-200 px-2.5 py-0.5 text-xs font-bold text-indigo-700">
|
||||
{{ count($multiRecapiti) }} canali attivi
|
||||
</span>
|
||||
|
||||
{{-- Selettore Vista Atomica / Aggregata --}}
|
||||
<div class="flex items-center gap-1.5 bg-white border border-gray-200 p-1 rounded-lg">
|
||||
<button type="button"
|
||||
wire:click="setRecapitiViewMode('diviso')"
|
||||
class="px-2.5 py-1 text-xs font-semibold rounded-md transition {{ $currentRecapitiMode === 'diviso' ? 'bg-indigo-600 text-white shadow-xs' : 'text-gray-600 hover:text-gray-900 hover:bg-gray-100' }}">
|
||||
📊 Diviso per Soggetto
|
||||
</button>
|
||||
<button type="button"
|
||||
wire:click="setRecapitiViewMode('condomino')"
|
||||
class="px-2.5 py-1 text-xs font-semibold rounded-md transition {{ $currentRecapitiMode === 'condomino' ? 'bg-amber-500 text-white shadow-xs' : 'text-gray-600 hover:text-gray-900 hover:bg-gray-100' }}">
|
||||
🏢 Condomino ({{ count($recapitiCondomino) }})
|
||||
</button>
|
||||
<button type="button"
|
||||
wire:click="setRecapitiViewMode('inquilino')"
|
||||
class="px-2.5 py-1 text-xs font-semibold rounded-md transition {{ $currentRecapitiMode === 'inquilino' ? 'bg-indigo-500 text-white shadow-xs' : 'text-gray-600 hover:text-gray-900 hover:bg-gray-100' }}">
|
||||
👤 Inquilino ({{ count($recapitiInquilino) }})
|
||||
</button>
|
||||
<button type="button"
|
||||
wire:click="setRecapitiViewMode('tutti')"
|
||||
class="px-2.5 py-1 text-xs font-semibold rounded-md transition {{ $currentRecapitiMode === 'tutti' ? 'bg-slate-700 text-white shadow-xs' : 'text-gray-600 hover:text-gray-900 hover:bg-gray-100' }}">
|
||||
🌐 Tutti ({{ count($multiRecapiti) }})
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if(empty($multiRecapiti))
|
||||
|
|
@ -341,56 +365,195 @@ class="inline-flex items-center gap-2 rounded-lg border px-3 py-2 text-sm font-s
|
|||
Nessun recapito multicanale o contatto registrato per i soggetti di questa unità.
|
||||
</div>
|
||||
@else
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-left text-xs text-gray-700 divide-y divide-gray-100">
|
||||
<thead class="bg-gray-50 text-[11px] font-bold uppercase tracking-wider text-gray-500">
|
||||
<tr>
|
||||
<th class="px-4 py-2.5">Canale</th>
|
||||
<th class="px-4 py-2.5">Etichetta / Uso</th>
|
||||
<th class="px-4 py-2.5">Soggetto Collegato</th>
|
||||
<th class="px-4 py-2.5">Valore Contatto</th>
|
||||
<th class="px-4 py-2.5 text-center">Stato</th>
|
||||
<th class="px-4 py-2.5 text-right">Azioni Rapide</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-100 bg-white">
|
||||
@foreach($multiRecapiti as $rec)
|
||||
<tr class="hover:bg-indigo-50/30 transition">
|
||||
<td class="px-4 py-2.5 whitespace-nowrap">
|
||||
<span class="inline-flex items-center gap-1.5 rounded-md border px-2 py-0.5 font-semibold text-[11px] {{ $rec['color'] }}">
|
||||
<span>{{ $rec['icon'] }}</span>
|
||||
<span>{{ $rec['canale'] }}</span>
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-4 py-2.5 font-medium text-gray-800 whitespace-nowrap">
|
||||
{{ $rec['etichetta'] }}
|
||||
</td>
|
||||
<td class="px-4 py-2.5 whitespace-nowrap">
|
||||
<span class="font-bold text-gray-900">{{ $rec['soggetto'] }}</span>
|
||||
<span class="text-gray-400 font-normal">({{ $rec['ruolo'] }})</span>
|
||||
</td>
|
||||
<td class="px-4 py-2.5 font-mono text-gray-900 font-medium">
|
||||
{{ $rec['valore'] }}
|
||||
</td>
|
||||
<td class="px-4 py-2.5 text-center whitespace-nowrap">
|
||||
@if($rec['is_principal'])
|
||||
<span class="rounded bg-emerald-100 text-emerald-800 px-1.5 py-0.5 text-[10px] font-bold">★ Principale</span>
|
||||
@else
|
||||
<span class="text-gray-400 text-[11px]">—</span>
|
||||
@endif
|
||||
</td>
|
||||
<td class="px-4 py-2.5 text-right whitespace-nowrap">
|
||||
@if(!empty($rec['action_url']))
|
||||
<a href="{{ $rec['action_url'] }}" class="inline-flex items-center gap-1 rounded bg-indigo-50 hover:bg-indigo-100 border border-indigo-200 text-indigo-700 font-semibold px-2 py-1 text-[11px] transition">
|
||||
<span>Apri</span>
|
||||
</a>
|
||||
@endif
|
||||
</td>
|
||||
{{-- VISTA 1: DIVISO PER SOGGETTO (DUE SOTTO-TABELLE ATOMICHE) --}}
|
||||
@if($currentRecapitiMode === 'diviso')
|
||||
<div class="p-4 space-y-4">
|
||||
{{-- SOTTO-TABELLA CONDOMINO --}}
|
||||
<div class="rounded-lg border border-amber-200 bg-amber-50/20 overflow-hidden">
|
||||
<div class="flex items-center justify-between px-3.5 py-2 bg-amber-50 border-b border-amber-200">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-sm">🏢</span>
|
||||
<span class="text-xs font-bold text-amber-950 uppercase tracking-wide">Recapiti Condomino / Aventi Diritto</span>
|
||||
</div>
|
||||
<span class="rounded bg-amber-200/80 text-amber-900 font-bold px-2 py-0.5 text-[10px]">
|
||||
{{ count($recapitiCondomino) }} canali
|
||||
</span>
|
||||
</div>
|
||||
@if(empty($recapitiCondomino))
|
||||
<div class="p-3 text-center text-xs text-gray-500">Nessun contatto registrato per il Condomino.</div>
|
||||
@else
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-left text-xs text-gray-700 divide-y divide-amber-100/60">
|
||||
<thead class="bg-amber-50/50 text-[10px] font-bold uppercase tracking-wider text-amber-900/70">
|
||||
<tr>
|
||||
<th class="px-3.5 py-2">Canale</th>
|
||||
<th class="px-3.5 py-2">Etichetta</th>
|
||||
<th class="px-3.5 py-2">Soggetto</th>
|
||||
<th class="px-3.5 py-2">Valore Contatto</th>
|
||||
<th class="px-3.5 py-2 text-center">Stato</th>
|
||||
<th class="px-3.5 py-2 text-right">Azione</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-amber-100/60 bg-white">
|
||||
@foreach($recapitiCondomino as $rec)
|
||||
<tr class="hover:bg-amber-50/40 transition">
|
||||
<td class="px-3.5 py-2 whitespace-nowrap">
|
||||
<span class="inline-flex items-center gap-1.5 rounded-md border px-2 py-0.5 font-semibold text-[11px] {{ $rec['color'] }}">
|
||||
<span>{{ $rec['icon'] }}</span>
|
||||
<span>{{ $rec['canale'] }}</span>
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-3.5 py-2 font-medium text-gray-800 whitespace-nowrap">{{ $rec['etichetta'] }}</td>
|
||||
<td class="px-3.5 py-2 whitespace-nowrap">
|
||||
<span class="font-bold text-gray-900">{{ $rec['soggetto'] }}</span>
|
||||
<span class="text-gray-400 font-normal">({{ $rec['ruolo'] }})</span>
|
||||
</td>
|
||||
<td class="px-3.5 py-2 font-mono text-gray-900 font-medium">{{ $rec['valore'] }}</td>
|
||||
<td class="px-3.5 py-2 text-center whitespace-nowrap">
|
||||
@if($rec['is_principal'])
|
||||
<span class="rounded bg-emerald-100 text-emerald-800 px-1.5 py-0.5 text-[10px] font-bold">★ Principale</span>
|
||||
@else
|
||||
<span class="text-gray-400 text-[11px]">—</span>
|
||||
@endif
|
||||
</td>
|
||||
<td class="px-3.5 py-2 text-right whitespace-nowrap">
|
||||
@if(!empty($rec['action_url']))
|
||||
<a href="{{ $rec['action_url'] }}" class="inline-flex items-center gap-1 rounded bg-amber-50 hover:bg-amber-100 border border-amber-200 text-amber-900 font-semibold px-2 py-0.5 text-[11px] transition">
|
||||
<span>Apri</span>
|
||||
</a>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- SOTTO-TABELLA INQUILINO --}}
|
||||
<div class="rounded-lg border border-indigo-200 bg-indigo-50/20 overflow-hidden">
|
||||
<div class="flex items-center justify-between px-3.5 py-2 bg-indigo-50 border-b border-indigo-200">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-sm">👤</span>
|
||||
<span class="text-xs font-bold text-indigo-950 uppercase tracking-wide">Recapiti Inquilino / Conduttore</span>
|
||||
</div>
|
||||
<span class="rounded bg-indigo-200/80 text-indigo-900 font-bold px-2 py-0.5 text-[10px]">
|
||||
{{ count($recapitiInquilino) }} canali
|
||||
</span>
|
||||
</div>
|
||||
@if(empty($recapitiInquilino))
|
||||
<div class="p-3 text-center text-xs text-gray-500">Nessun contatto o recapito registrato per l'Inquilino.</div>
|
||||
@else
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-left text-xs text-gray-700 divide-y divide-indigo-100/60">
|
||||
<thead class="bg-indigo-50/50 text-[10px] font-bold uppercase tracking-wider text-indigo-900/70">
|
||||
<tr>
|
||||
<th class="px-3.5 py-2">Canale</th>
|
||||
<th class="px-3.5 py-2">Etichetta</th>
|
||||
<th class="px-3.5 py-2">Soggetto</th>
|
||||
<th class="px-3.5 py-2">Valore Contatto</th>
|
||||
<th class="px-3.5 py-2 text-center">Stato</th>
|
||||
<th class="px-3.5 py-2 text-right">Azione</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-indigo-100/60 bg-white">
|
||||
@foreach($recapitiInquilino as $rec)
|
||||
<tr class="hover:bg-indigo-50/40 transition">
|
||||
<td class="px-3.5 py-2 whitespace-nowrap">
|
||||
<span class="inline-flex items-center gap-1.5 rounded-md border px-2 py-0.5 font-semibold text-[11px] {{ $rec['color'] }}">
|
||||
<span>{{ $rec['icon'] }}</span>
|
||||
<span>{{ $rec['canale'] }}</span>
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-3.5 py-2 font-medium text-gray-800 whitespace-nowrap">{{ $rec['etichetta'] }}</td>
|
||||
<td class="px-3.5 py-2 whitespace-nowrap">
|
||||
<span class="font-bold text-gray-900">{{ $rec['soggetto'] }}</span>
|
||||
<span class="text-gray-400 font-normal">({{ $rec['ruolo'] }})</span>
|
||||
</td>
|
||||
<td class="px-3.5 py-2 font-mono text-gray-900 font-medium">{{ $rec['valore'] }}</td>
|
||||
<td class="px-3.5 py-2 text-center whitespace-nowrap">
|
||||
@if($rec['is_principal'])
|
||||
<span class="rounded bg-emerald-100 text-emerald-800 px-1.5 py-0.5 text-[10px] font-bold">★ Principale</span>
|
||||
@else
|
||||
<span class="text-gray-400 text-[11px]">—</span>
|
||||
@endif
|
||||
</td>
|
||||
<td class="px-3.5 py-2 text-right whitespace-nowrap">
|
||||
@if(!empty($rec['action_url']))
|
||||
<a href="{{ $rec['action_url'] }}" class="inline-flex items-center gap-1 rounded bg-indigo-50 hover:bg-indigo-100 border border-indigo-200 text-indigo-700 font-semibold px-2 py-0.5 text-[11px] transition">
|
||||
<span>Apri</span>
|
||||
</a>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
{{-- VISTA 2: FILTRATA PER CONDOMINO, INQUILINO O TUTTI --}}
|
||||
@else
|
||||
@php
|
||||
$displayedRows = match($currentRecapitiMode) {
|
||||
'condomino' => $recapitiCondomino,
|
||||
'inquilino' => $recapitiInquilino,
|
||||
default => $multiRecapiti,
|
||||
};
|
||||
@endphp
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-left text-xs text-gray-700 divide-y divide-gray-100">
|
||||
<thead class="bg-gray-50 text-[11px] font-bold uppercase tracking-wider text-gray-500">
|
||||
<tr>
|
||||
<th class="px-4 py-2.5">Canale</th>
|
||||
<th class="px-4 py-2.5">Etichetta / Uso</th>
|
||||
<th class="px-4 py-2.5">Soggetto Collegato</th>
|
||||
<th class="px-4 py-2.5">Valore Contatto</th>
|
||||
<th class="px-4 py-2.5 text-center">Stato</th>
|
||||
<th class="px-4 py-2.5 text-right">Azioni Rapide</th>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-100 bg-white">
|
||||
@foreach($displayedRows as $rec)
|
||||
<tr class="hover:bg-indigo-50/30 transition">
|
||||
<td class="px-4 py-2.5 whitespace-nowrap">
|
||||
<span class="inline-flex items-center gap-1.5 rounded-md border px-2 py-0.5 font-semibold text-[11px] {{ $rec['color'] }}">
|
||||
<span>{{ $rec['icon'] }}</span>
|
||||
<span>{{ $rec['canale'] }}</span>
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-4 py-2.5 font-medium text-gray-800 whitespace-nowrap">
|
||||
{{ $rec['etichetta'] }}
|
||||
</td>
|
||||
<td class="px-4 py-2.5 whitespace-nowrap">
|
||||
<span class="font-bold text-gray-900">{{ $rec['soggetto'] }}</span>
|
||||
<span class="text-gray-400 font-normal">({{ $rec['ruolo'] }})</span>
|
||||
</td>
|
||||
<td class="px-4 py-2.5 font-mono text-gray-900 font-medium">
|
||||
{{ $rec['valore'] }}
|
||||
</td>
|
||||
<td class="px-4 py-2.5 text-center whitespace-nowrap">
|
||||
@if($rec['is_principal'])
|
||||
<span class="rounded bg-emerald-100 text-emerald-800 px-1.5 py-0.5 text-[10px] font-bold">★ Principale</span>
|
||||
@else
|
||||
<span class="text-gray-400 text-[11px]">—</span>
|
||||
@endif
|
||||
</td>
|
||||
<td class="px-4 py-2.5 text-right whitespace-nowrap">
|
||||
@if(!empty($rec['action_url']))
|
||||
<a href="{{ $rec['action_url'] }}" class="inline-flex items-center gap-1 rounded bg-indigo-50 hover:bg-indigo-100 border border-indigo-200 text-indigo-700 font-semibold px-2 py-1 text-[11px] transition">
|
||||
<span>Apri</span>
|
||||
</a>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
|
@ -610,7 +773,16 @@ class="inline-flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-xs font-semi
|
|||
{{ $rE['gestione'] }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-4 py-2 font-medium text-slate-900">{{ $rE['descrizione'] }}</td>
|
||||
<td class="px-4 py-2 font-medium text-slate-900">
|
||||
<div class="flex items-center gap-1.5">
|
||||
@if(str_contains(strtoupper($rE['descrizione'] ?? ''), 'CONGUAGLIO FINALE') || str_contains(strtoupper($rE['descrizione'] ?? ''), ' CF') || ($rE['descrizione'] ?? '') === 'CF')
|
||||
<span class="inline-flex items-center rounded bg-purple-100 text-purple-800 px-1.5 py-0.5 text-[10px] font-bold">🏁 Conguaglio Finale (CF)</span>
|
||||
@elseif(str_contains(strtoupper($rE['descrizione'] ?? ''), 'CONGUAGLIO INIZIALE'))
|
||||
<span class="inline-flex items-center rounded bg-blue-100 text-blue-800 px-1.5 py-0.5 text-[10px] font-bold">🚀 Conguaglio Iniziale</span>
|
||||
@endif
|
||||
<span>{{ $rE['descrizione'] }}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-4 py-2 text-right font-mono font-medium text-slate-900">€ {{ number_format($rE['dovuto'], 2, ',', '.') }}</td>
|
||||
<td class="px-4 py-2 text-right font-mono font-medium text-emerald-700">€ {{ number_format($rE['pagato'], 2, ',', '.') }}</td>
|
||||
<td class="px-4 py-2 font-mono text-slate-700 font-medium">{{ $rE['data_pagamento'] ?? '—' }}</td>
|
||||
|
|
|
|||
|
|
@ -154,3 +154,62 @@
|
|||
expect($protocollo)->toBeArray();
|
||||
});
|
||||
|
||||
test('unita immobiliare categorizes contacts into condomino and inquilino sub-tables and supports toggle', function () {
|
||||
$user = User::first() ?: User::factory()->create();
|
||||
try {
|
||||
if (method_exists($user, 'assignRole')) {
|
||||
\Spatie\Permission\Models\Role::firstOrCreate(['name' => 'admin', 'guard_name' => 'web']);
|
||||
$user->assignRole('admin');
|
||||
}
|
||||
} catch (\Throwable $e) {}
|
||||
Auth::login($user);
|
||||
|
||||
$stabile = Stabile::where('codice_stabile', '0021')->first();
|
||||
expect($stabile)->not->toBeNull();
|
||||
StabileContext::setActiveStabileId($user, $stabile->id);
|
||||
|
||||
$unit = UnitaImmobiliare::where('stabile_id', $stabile->id)->where('scala', 'A')->where('interno', '11')->first();
|
||||
expect($unit)->not->toBeNull();
|
||||
|
||||
request()->merge(['unita_id' => $unit->id]);
|
||||
$page = new UnitaImmobiliarePage();
|
||||
$page->mount();
|
||||
|
||||
$recapiti = $page->getRecapitiMulticanaleTableProperty();
|
||||
expect($recapiti)->toBeArray();
|
||||
|
||||
foreach ($recapiti as $rec) {
|
||||
expect($rec)->toHaveKey('tipo_soggetto');
|
||||
expect(['condomino', 'inquilino'])->toContain($rec['tipo_soggetto']);
|
||||
}
|
||||
|
||||
$page->setRecapitiViewMode('inquilino');
|
||||
expect($page->recapitiViewMode)->toBe('inquilino');
|
||||
|
||||
$page->setRecapitiViewMode('diviso');
|
||||
expect($page->recapitiViewMode)->toBe('diviso');
|
||||
});
|
||||
|
||||
test('situazione iniziale renders and loads conguagli apertura from dett_tab', function () {
|
||||
$user = User::first() ?: User::factory()->create();
|
||||
try {
|
||||
if (method_exists($user, 'assignRole')) {
|
||||
\Spatie\Permission\Models\Role::firstOrCreate(['name' => 'admin', 'guard_name' => 'web']);
|
||||
$user->assignRole('admin');
|
||||
}
|
||||
} catch (\Throwable $e) {}
|
||||
Auth::login($user);
|
||||
|
||||
$stabile = Stabile::where('codice_stabile', '0021')->first();
|
||||
expect($stabile)->not->toBeNull();
|
||||
StabileContext::setActiveStabileId($user, $stabile->id);
|
||||
|
||||
$sitPage = new \App\Filament\Pages\Contabilita\SituazioneIniziale();
|
||||
$sitPage->mount();
|
||||
|
||||
expect($sitPage->tab)->toBe('bilancio');
|
||||
$sitPage->tab = 'conguagli';
|
||||
expect($sitPage->tab)->toBe('conguagli');
|
||||
});
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user