Separate supplier archive roots and normalize Drive templates
This commit is contained in:
parent
24c2318436
commit
c114090882
|
|
@ -335,6 +335,31 @@ private function ensureDriveTemplateStructure(string $token, string $rootFolderI
|
|||
}
|
||||
$this->ensureDriveFolder($token, $name, $templateRoot);
|
||||
}
|
||||
|
||||
$yearRootName = trim((string) config('netgescon.google.drive_template_year_root', ''));
|
||||
$yearModelName = trim((string) config('netgescon.google.drive_template_year_model', ''));
|
||||
|
||||
if ($yearRootName === '' || $yearModelName === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
$yearRootId = $this->ensureDriveFolder($token, $yearRootName, $templateRoot);
|
||||
if ($yearRootId === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$yearModelId = $this->ensureDriveFolder($token, $yearModelName, $yearRootId);
|
||||
if ($yearModelId === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($folders as $folder) {
|
||||
$name = trim((string) $folder);
|
||||
if ($name === '') {
|
||||
continue;
|
||||
}
|
||||
$this->ensureDriveFolder($token, $name, $yearModelId);
|
||||
}
|
||||
}
|
||||
|
||||
private function ensureDriveFolder(string $token, string $name, string $parentId): ?string
|
||||
|
|
|
|||
|
|
@ -1259,11 +1259,23 @@ public function getDocumentazioneStatsProperty(): array
|
|||
*/
|
||||
public function getDriveTemplateFoldersProperty(): array
|
||||
{
|
||||
$folders = config('netgescon.google.drive_template_folders', []);
|
||||
$items = config('netgescon.google.drive_template_folders', []);
|
||||
$yearRoot = trim((string) config('netgescon.google.drive_template_year_root', ''));
|
||||
$yearModel = trim((string) config('netgescon.google.drive_template_year_model', ''));
|
||||
|
||||
return is_array($folders)
|
||||
? array_values(array_filter(array_map('strval', $folders), fn(string $row) : bool => trim($row) !== ''))
|
||||
$folders = is_array($items)
|
||||
? array_values(array_filter(array_map('strval', $items), fn(string $row) : bool => trim($row) !== ''))
|
||||
: [];
|
||||
|
||||
if ($yearRoot !== '' && $yearModel !== '') {
|
||||
$baseFolders = $folders;
|
||||
|
||||
foreach ($baseFolders as $folder) {
|
||||
$folders[] = $yearRoot . '/' . $yearModel . '/' . $folder;
|
||||
}
|
||||
}
|
||||
|
||||
return array_values(array_unique($folders));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
use App\Models\FornitoreStabileImpostazione;
|
||||
use App\Models\Stabile;
|
||||
use App\Models\User;
|
||||
use App\Services\TenantArchivePathService;
|
||||
use App\Support\ArchivioPaths;
|
||||
use App\Support\GoogleAccountStore;
|
||||
use BackedEnum;
|
||||
|
|
@ -353,23 +354,12 @@ private function refreshArchiveSummary(): void
|
|||
{
|
||||
$relativeBase = $this->getArchiveRelativeBase();
|
||||
$absoluteBase = $relativeBase ? storage_path('app/' . $relativeBase) : null;
|
||||
$legacyRelativeBase = $this->getLegacyArchiveRelativeBase();
|
||||
$legacyAbsoluteBase = $legacyRelativeBase ? storage_path('app/' . $legacyRelativeBase) : null;
|
||||
$this->operationalConfigAvailable = Schema::hasColumn('fornitori', 'operational_config');
|
||||
|
||||
if ($relativeBase !== null) {
|
||||
foreach ([
|
||||
'documenti',
|
||||
'rubrica',
|
||||
'fe',
|
||||
'prodotti',
|
||||
'comunicazioni',
|
||||
'tecnorepair/imports',
|
||||
'tecnorepair/allegati',
|
||||
'temp/upload',
|
||||
'temp/processing',
|
||||
'logs',
|
||||
] as $folder) {
|
||||
Storage::disk('local')->makeDirectory($relativeBase . '/' . $folder);
|
||||
}
|
||||
if ($this->fornitore instanceof Fornitore) {
|
||||
$this->fornitore->ensureArchiveStructure();
|
||||
}
|
||||
|
||||
$this->archiveFolders = $relativeBase === null ? [] : [
|
||||
|
|
@ -379,17 +369,19 @@ private function refreshArchiveSummary(): void
|
|||
['label' => 'Prodotti e catalogo', 'path' => storage_path('app/' . $relativeBase . '/prodotti')],
|
||||
['label' => 'Comunicazioni / Post-it', 'path' => storage_path('app/' . $relativeBase . '/comunicazioni')],
|
||||
['label' => 'TecnoRepair import', 'path' => storage_path('app/' . $relativeBase . '/tecnorepair/imports')],
|
||||
['label' => 'Archivio per anno', 'path' => storage_path('app/' . $relativeBase . '/documenti/archivio_per_anno')],
|
||||
];
|
||||
|
||||
if ($relativeBase !== null && $this->tecnorepairMdbPath === '') {
|
||||
$latest = collect(Storage::disk('local')->files($relativeBase . '/tecnorepair/imports'))
|
||||
->filter(fn(string $path): bool => str_ends_with(strtolower($path), '.mdb'))
|
||||
->sortDesc()
|
||||
->first();
|
||||
|
||||
if (is_string($latest) && $latest !== '') {
|
||||
$this->tecnorepairMdbPath = storage_path('app/' . $latest);
|
||||
if (is_string($legacyAbsoluteBase) && $legacyAbsoluteBase !== '' && $legacyAbsoluteBase !== $absoluteBase && is_dir($legacyAbsoluteBase)) {
|
||||
$this->archiveFolders[] = ['label' => 'Base archivio legacy', 'path' => $legacyAbsoluteBase];
|
||||
$this->archiveFolders[] = ['label' => 'TecnoRepair import legacy', 'path' => storage_path('app/' . $legacyRelativeBase . '/tecnorepair/imports')];
|
||||
}
|
||||
|
||||
$currentMdbPath = $this->resolveTecnoRepairMdbPath($this->tecnorepairMdbPath);
|
||||
if ($currentMdbPath === null) {
|
||||
$this->tecnorepairMdbPath = $this->discoverLatestTecnoRepairMdbPath() ?? '';
|
||||
} else {
|
||||
$this->tecnorepairMdbPath = $currentMdbPath;
|
||||
}
|
||||
|
||||
$stats = $this->fornitore?->tecnorepair_stats ?? ['total' => 0, 'open' => 0, 'closed' => 0, 'serials' => 0];
|
||||
|
|
@ -397,7 +389,7 @@ private function refreshArchiveSummary(): void
|
|||
$this->loadOperationalWorkflowSettings();
|
||||
$this->moduleRows = [
|
||||
['module' => 'Rubrica fornitore', 'status' => 'pronto', 'note' => 'Vista fornitore filtrata ma agganciata all\'anagrafica unica.'],
|
||||
['module' => 'Google Workspace', 'status' => data_get($this->googleWorkspaceStatus, 'connected', false) ? 'collegato' : 'da collegare', 'note' => 'Account dedicato al fornitore, isolato con chiave tecnica separata pur restando governato dal backend amministratore.'],
|
||||
['module' => 'Google Workspace', 'status' => data_get($this->googleWorkspaceStatus, 'connected', false) ? 'collegato' : 'da collegare', 'note' => 'Account dedicato al fornitore, con archivio root separato e template cartelle in naming Linux con underscore.'],
|
||||
['module' => 'TecnoRepair MDB', 'status' => ((int) ($stats['total'] ?? 0) > 0) ? 'importato' : 'da importare', 'note' => 'Schede: ' . (int) ($stats['total'] ?? 0) . ' · aperte: ' . (int) ($stats['open'] ?? 0) . ' · chiuse: ' . (int) ($stats['closed'] ?? 0)],
|
||||
['module' => 'Catalogo fornitore', 'status' => 'attivo', 'note' => 'Il catalogo vero resta nella pagina Prodotti.'],
|
||||
['module' => 'Chiavi e reperibilità', 'status' => count($this->stabileAccessRows) > 0 ? 'configurato' : 'da configurare', 'note' => 'Istruzioni per accesso, chi suonare e dove recuperare chiavi per ciascuno stabile.'],
|
||||
|
|
@ -603,6 +595,13 @@ private function resolveTecnoRepairMdbPath(?string $rawPath): ?string
|
|||
if (Storage::disk('local')->exists($relative)) {
|
||||
$candidatePaths[] = storage_path('app/' . $relative);
|
||||
}
|
||||
|
||||
foreach ($this->getTecnoRepairImportBases() as $importBase) {
|
||||
$joined = trim($importBase . '/' . $relative, '/');
|
||||
if (Storage::disk('local')->exists($joined)) {
|
||||
$candidatePaths[] = storage_path('app/' . $joined);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (array_values(array_unique($candidatePaths)) as $candidate) {
|
||||
|
|
@ -619,12 +618,48 @@ private function resolveTecnoRepairMdbPath(?string $rawPath): ?string
|
|||
return null;
|
||||
}
|
||||
|
||||
private function discoverLatestTecnoRepairMdbPath(): ?string
|
||||
{
|
||||
foreach ($this->getTecnoRepairImportBases() as $importBase) {
|
||||
$latest = collect(Storage::disk('local')->files($importBase))
|
||||
->filter(fn(string $path): bool => str_ends_with(strtolower($path), '.mdb'))
|
||||
->sortDesc()
|
||||
->first();
|
||||
|
||||
if (is_string($latest) && $latest !== '') {
|
||||
return storage_path('app/' . $latest);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
private function getTecnoRepairImportBases(): array
|
||||
{
|
||||
return array_values(array_filter([
|
||||
$this->getArchiveRelativeBase() ? $this->getArchiveRelativeBase() . '/tecnorepair/imports' : null,
|
||||
$this->getLegacyArchiveRelativeBase() ? $this->getLegacyArchiveRelativeBase() . '/tecnorepair/imports' : null,
|
||||
]));
|
||||
}
|
||||
|
||||
private function getArchiveRelativeBase(): ?string
|
||||
{
|
||||
if (! $this->fornitore instanceof Fornitore) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return ArchivioPaths::fornitoreBase($this->fornitore, $this->fornitore->amministratore);
|
||||
return app(TenantArchivePathService::class)->fornitoreRelativePath($this->fornitore);
|
||||
}
|
||||
|
||||
private function getLegacyArchiveRelativeBase(): ?string
|
||||
{
|
||||
if (! $this->fornitore instanceof Fornitore || ! $this->fornitore->amministratore) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return app(TenantArchivePathService::class)->fornitoreLegacyRelativePath($this->fornitore);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -742,9 +742,22 @@ private function buildContactNameParts(string $nomeRaw, string $cognomeRaw, stri
|
|||
private function loadDriveTemplateFolders(): void
|
||||
{
|
||||
$items = config('netgescon.google.drive_template_folders', []);
|
||||
$this->driveTemplateFolders = is_array($items)
|
||||
$yearRoot = trim((string) config('netgescon.google.drive_template_year_root', ''));
|
||||
$yearModel = trim((string) config('netgescon.google.drive_template_year_model', ''));
|
||||
|
||||
$folders = is_array($items)
|
||||
? array_values(array_filter(array_map('strval', $items), fn(string $v) : bool => trim($v) !== ''))
|
||||
: [];
|
||||
|
||||
if ($yearRoot !== '' && $yearModel !== '') {
|
||||
$baseFolders = $folders;
|
||||
|
||||
foreach ($baseFolders as $folder) {
|
||||
$folders[] = $yearRoot . '/' . $yearModel . '/' . $folder;
|
||||
}
|
||||
}
|
||||
|
||||
$this->driveTemplateFolders = array_values(array_unique($folders));
|
||||
}
|
||||
|
||||
private function googleErrorBody($response, string $fallback): string
|
||||
|
|
|
|||
|
|
@ -117,13 +117,28 @@ protected static function booted(): void
|
|||
|
||||
static::created(function (Fornitore $fornitore) {
|
||||
try {
|
||||
$base = ArchivioPaths::fornitoreBase($fornitore);
|
||||
if (! $base) {
|
||||
$fornitore->ensureArchiveStructure();
|
||||
} catch (\Throwable) {
|
||||
// ignore
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function ensureArchiveStructure(): void
|
||||
{
|
||||
$bases = array_values(array_filter([
|
||||
ArchivioPaths::fornitoreBase($this),
|
||||
ArchivioPaths::fornitoreLegacyBase($this),
|
||||
]));
|
||||
|
||||
if ($bases === []) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($bases as $base) {
|
||||
foreach ([
|
||||
'documenti',
|
||||
'documenti/archivio_per_anno',
|
||||
'rubrica',
|
||||
'fe',
|
||||
'prodotti',
|
||||
|
|
@ -133,13 +148,11 @@ protected static function booted(): void
|
|||
'temp/upload',
|
||||
'temp/processing',
|
||||
'logs',
|
||||
'google_drive',
|
||||
] as $folder) {
|
||||
Storage::disk('local')->makeDirectory($base . '/' . $folder);
|
||||
}
|
||||
} catch (\Throwable) {
|
||||
// ignore
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected function shouldGenerateCodiceUnivocoLocally(): bool
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
namespace App\Services;
|
||||
|
||||
use App\Models\Amministratore;
|
||||
use App\Models\Fornitore;
|
||||
use App\Models\Stabile;
|
||||
use InvalidArgumentException;
|
||||
|
||||
|
|
@ -73,6 +74,51 @@ public function stabileAbsolutePath(Stabile $stabile, ?string $suffix = null): s
|
|||
return storage_path('app/' . $this->stabileRelativePath($stabile, $suffix));
|
||||
}
|
||||
|
||||
public function fornitoreCode(Fornitore | string $fornitore): string
|
||||
{
|
||||
if (is_string($fornitore)) {
|
||||
$code = trim($fornitore);
|
||||
|
||||
if ($code === '') {
|
||||
throw new InvalidArgumentException('Codice fornitore non valido.');
|
||||
}
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
$code = trim((string) ($fornitore->codice_univoco ?: ''));
|
||||
|
||||
if ($code === '') {
|
||||
throw new InvalidArgumentException('Fornitore senza codice archivio canonico.');
|
||||
}
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
public function fornitoreRelativePath(Fornitore | string $fornitore, ?string $suffix = null): string
|
||||
{
|
||||
$basePath = 'fornitori/' . $this->fornitoreCode($fornitore);
|
||||
|
||||
return $this->appendSuffix($basePath, $suffix);
|
||||
}
|
||||
|
||||
public function fornitoreAbsolutePath(Fornitore | string $fornitore, ?string $suffix = null): string
|
||||
{
|
||||
return storage_path('app/' . $this->fornitoreRelativePath($fornitore, $suffix));
|
||||
}
|
||||
|
||||
public function fornitoreLegacyRelativePath(Fornitore $fornitore, ?string $suffix = null): string
|
||||
{
|
||||
$basePath = $this->amministratoreRelativePath($fornitore->amministratore) . '/fornitori/' . $this->fornitoreCode($fornitore);
|
||||
|
||||
return $this->appendSuffix($basePath, $suffix);
|
||||
}
|
||||
|
||||
public function fornitoreLegacyAbsolutePath(Fornitore $fornitore, ?string $suffix = null): string
|
||||
{
|
||||
return storage_path('app/' . $this->fornitoreLegacyRelativePath($fornitore, $suffix));
|
||||
}
|
||||
|
||||
private function appendSuffix(string $basePath, ?string $suffix): string
|
||||
{
|
||||
$normalizedSuffix = trim((string) $suffix, '/');
|
||||
|
|
|
|||
|
|
@ -75,6 +75,17 @@ public static function fornitoreBase(Fornitore $fornitore, ?Amministratore $admi
|
|||
return null;
|
||||
}
|
||||
|
||||
return 'fornitori/' . $code;
|
||||
}
|
||||
|
||||
public static function fornitoreLegacyBase(Fornitore $fornitore, ?Amministratore $admin = null): ?string
|
||||
{
|
||||
$code = (string) ($fornitore->codice_univoco ?: '');
|
||||
$code = self::sanitizeFolder($code);
|
||||
if ($code === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$admin = $admin ?: $fornitore->amministratore;
|
||||
if (! $admin instanceof Amministratore) {
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -138,38 +138,40 @@
|
|||
|
||||
'google' => [
|
||||
'drive_template_folders' => [
|
||||
'Bilanci',
|
||||
'Riscaldamento',
|
||||
'Visure catastali',
|
||||
'Ricevute Incassi',
|
||||
'Passaggio consegne',
|
||||
'Locali Condominiali',
|
||||
'Cassetto Fiscale',
|
||||
'Certificazione Unica',
|
||||
'Agenzia entrate',
|
||||
'Fatture XML',
|
||||
'Posta e comunicazioni',
|
||||
'Fatture PDF',
|
||||
'PEC',
|
||||
'Verbali Assemblea',
|
||||
'Regolamento Condominio',
|
||||
'Tabelle Millesimali',
|
||||
'Fornitori',
|
||||
'Banca CC',
|
||||
'Posta CC',
|
||||
'Utenze',
|
||||
'Assicurazione',
|
||||
'Assemblee',
|
||||
'Codice Fiscale',
|
||||
'Scambio',
|
||||
'ZIP',
|
||||
'Anagrafiche',
|
||||
'Bonifici',
|
||||
'F 24',
|
||||
'Condomini',
|
||||
'Lavori Vari',
|
||||
'Informative',
|
||||
'Verifiche periodiche',
|
||||
'bilanci',
|
||||
'riscaldamento',
|
||||
'visure_catastali',
|
||||
'ricevute_incassi',
|
||||
'passaggio_consegne',
|
||||
'locali_condominiali',
|
||||
'cassetto_fiscale',
|
||||
'certificazione_unica',
|
||||
'agenzia_entrate',
|
||||
'fatture_xml',
|
||||
'posta_e_comunicazioni',
|
||||
'fatture_pdf',
|
||||
'pec',
|
||||
'verbali_assemblea',
|
||||
'regolamento_condominio',
|
||||
'tabelle_millesimali',
|
||||
'fornitori',
|
||||
'banca_cc',
|
||||
'posta_cc',
|
||||
'utenze',
|
||||
'assicurazione',
|
||||
'assemblee',
|
||||
'codice_fiscale',
|
||||
'scambio',
|
||||
'zip',
|
||||
'anagrafiche',
|
||||
'bonifici',
|
||||
'f_24',
|
||||
'condomini',
|
||||
'lavori_vari',
|
||||
'informative',
|
||||
'verifiche_periodiche',
|
||||
],
|
||||
'drive_template_year_root' => 'archivio_per_anno',
|
||||
'drive_template_year_model' => '_anno_modello',
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@
|
|||
@if(($showTechnicalBoxes ?? true) === true)
|
||||
<div class="rounded-lg border border-gray-200 p-3 lg:col-span-2">
|
||||
<div class="mb-2 text-sm font-semibold text-gray-800">Template Cartelle Drive Condominio</div>
|
||||
<div class="mb-2 text-xs text-gray-500">Struttura base replicabile per ogni stabile (allineata alla tua organizzazione in Drive).</div>
|
||||
<div class="mb-2 text-xs text-gray-500">Struttura base replicabile per ogni stabile, con naming Linux a underscore e modello annuale sotto archivio_per_anno.</div>
|
||||
@if(count($driveTemplateFolders) === 0)
|
||||
<div class="text-sm text-gray-500">Template non configurato.</div>
|
||||
@else
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user