Merge pull request #1 from devcode-it/master

Update from upstream
This commit is contained in:
xxfuma85xx 2019-01-04 23:13:40 +01:00 committed by GitHub
commit 360983abd6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
597 changed files with 42721 additions and 21077 deletions

View File

@ -7,7 +7,7 @@ currentMenu: contribuire
Sei interessato a contribuire allo sviluppo di OpenSTAManger? Ottimo, sei il benvenuto!
Siamo entusiasti di ogni nuova contribuzione che otteniamo dalla nostra community.
Ci sono molti modi per contribuire: segnalare bug, richiedere miglioramenti, scrivere tutorial, migliorare la documentazione...
Ci sono molti modi per contribuire: segnalare bug, richiedere miglioramenti, scrivere tutorial, migliorare la documentazione, ...
Non serve essere degli esperti programmatori per aiutarci! :smile_cat:
@ -18,6 +18,11 @@ Se ti serve un aiuto, crea una issue su GitHub.
Per migliorare il sistema con cui sviluppiamo il codice, abbiamo deciso di adottare alcune linee guida per facilitare la collaborazione tra più persone.
### Standard del codice
Per lo standard ufficiale riguardante i nomi e le strutture da utilizzare, visita la sezione [Standard](STANDARD.md).
### Codice di condotta
Per il momento non abbiamo adottato un vero e proprio codice di condotta, ma ti chiediamo di essere il più civile possibile nel comunicare con gli altri per questo progetto.
@ -69,3 +74,23 @@ Se sei in grado di risolvere uno dei bug segnalati oppure vuoi completare una nu
Siamo presenti su [Facebook](https://www.facebook.com/openstamanager), e il nostro forum ufficiale è disponibile all'indirizzo <http://www.openstamanager.com/forum/>.
Cerchiamo di essere disponibili quanto possibile, ma non sempre riusciamo a rispondere tempestivamente.
## Testing
Il progetto presenta, a partire dalla versione 2.4.2, un insieme di test per facilitare il controllo sul corretto funzionamento del gestionale.
Per eseguire i test è necessario seguire le seguenti istruzioni (https://codeception.com/docs/modules/WebDriver):
- Scaricare (Selenium Server)[https://docs.seleniumhq.org/download/] e salvarlo come `selenium-server-standalone.jar` nella cartella principale
- Scaricare (ChromeDriver)[https://sites.google.com/a/chromium.org/chromedriver/getting-started], rendendolo eseguibile da riga di comando (su Windows, aggiungerlo al PATH)
- Configurare localmente Codeception nel file `codeception.yml` con l'URL del web server locale
```yml
modules:
config:
WebDriver:
url: http://localhost/openstamanager
```
- Eseguire su shell differenti i seguenti comandi:
```bash
npm run tests-server # Avvia i server per i test di funzionamento grafico
npm run tests-OSM # Avvia i test
```

28
.github/STANDARD.md vendored Normal file
View File

@ -0,0 +1,28 @@
# Standard del codice
Lo standard prevede l'utilizzo di nomi in italiano per la maggior parte dei contenuti, esclusi i sistemi di gestione interna del gestionale (tabelle `zz_*` e codici particolarmente rilevanti).
I nomi delle variabili devono seguire uno standard comune, che prevede la sostituzione degli spazi con `_` (*underscore*) e la rimozione delle lettere accentate a favore di quelle semplici.
Le variabili devono possedere nomi completi e chiari.
Esempio:
- Partita IVA -> `partita_iva` nel database, `$partita_iva` in PHP
## Database
Gli identificatori devono iniziare per `id_*` e i flag per `is_*`.
E' fondamentale ricordarsi di impostare correttamente le **FOREIGN KEYS** delle relative tabelle.
Ci sono inoltre alcuni campi utilizzati in modo riccorrente all'interno del gestionale:
- `default boolean NOT NULL DEFAULT 0` per i valori di default, non cancellabili e con modificabilità limitata
- `predefined boolean NOT NULL DEFAULT 0` per i valori predefiniti in selezioni o gruppi
- `visible boolean NOT NULL DEFAULT 1` per nascondere gli elementi
- `deleted_at timestamp NULL DEFAULT NULL,` per segnare un elemento come eliminato
Per tabelle non presenti all'interno della lista ufficiale di OpenSTAManager (file **update/tables.php**), è necessario inoltre provvedere all'aggiunta dei seguenti campi:
- `updated_at timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP`
- `created_at timestamp NULL DEFAULT CURRENT_TIMESTAMP`
## Note
Malgrado una buona parte del codice ufficiale non segua completamente queste buone pratiche, è consigliato l'implementazione di queste linee guida per nuove funzioni e strutture mentre il sistema di base viene lentamente standardizzato.

10
.gitignore vendored
View File

@ -71,7 +71,7 @@ vendor/
######################
*.new
*.old
.couscous/
*.txt
assets/dist/
backup/*
!backup/.htaccess
@ -81,6 +81,14 @@ files/*
!files/my_impianti/
files/my_impianti/*
!files/my_impianti/componente.ini
tmp/
config.inc.php
database.sql
VERSION
REVISION
.php_cs.cache
/tests/_log/*
/tests/_temp/*
codeception.yml
!.gitkeep

View File

@ -3,16 +3,6 @@
IndexIgnore */*
</IfModule>
# Try to set PHP settings
<IfModule mod_php5.c>
php_value upload_max_filesize 20M
php_value post_max_size 20M
</IfModule>
<IfModule mod_php7.c>
php_value upload_max_filesize 20M
php_value post_max_size 20M
</IfModule>
# Deny access to files starting with dot
<FilesMatch "^\.">
Order allow,deny
@ -32,10 +22,9 @@
</Files>
# Disable indexing of php, html, htm, pdf files
ServerSignature Off
<IfModule mod_headers.c>
<FilesMatch "\.(php|html|htm|pdf|log)$">
Header set X-Robots-Tag: "noindex"
</FilesMatch>
Header set X-Robots-Tag: "noindex,nofollow"
</IfModule>
<IfModule mod_rewrite.c>
@ -58,6 +47,10 @@
RewriteRule ^node_modules/ - [F,L]
RewriteRule ^vendor/ - [F,L]
# Disable HTTP TRACE
RewriteCond %{REQUEST_METHOD} ^TRACE
RewriteRule .* - [F]
# Prevent hacks
# proc/self/environ? no way!
RewriteCond %{QUERY_STRING} proc/self/environ [OR]

View File

@ -16,6 +16,10 @@ $config = PhpCsFixer\Config::create()
'array_syntax' => ['syntax' => 'short'],
'yoda_style' => false,
'no_short_echo_tag' => true,
'ordered_imports' => true,
'no_alternative_syntax' => true,
'ordered_class_elements' => true,
'phpdoc_order' => true,
])
->setFinder($finder);

View File

@ -4,39 +4,184 @@ Tutti i maggiori cambiamenti di questo progetto saranno documentati in questo fi
Il formato utilizzato è basato sulle linee guida di [Keep a Changelog](http://keepachangelog.com/), e il progetto segue il [Semantic Versioning](http://semver.org/) per definire le versioni delle release.
- [2.4.4 (2018-12-12)](#244-2018-12-12)
- [Aggiunto (Added)](#aggiunto-added)
- [Fixed](#fixed)
- [2.4.3 (2018-12-07)](#243-2018-12-07)
- [Aggiunto (Added)](#aggiunto-added-1)
- [Fixed](#fixed-1)
- [2.4.2 (2018-11-14)](#242-2018-11-14)
- [Aggiunto (Added)](#aggiunto-added-2)
- [Modificato (Changed)](#modificato-changed)
- [Deprecato (Deprecated)](#deprecato-deprecated)
- [Rimosso (Removed)](#rimosso-removed)
- [Sicurezza (Security)](#sicurezza-security)
- [2.4.1 (2018-08-01)](#241-2018-08-01)
- [Aggiunto (Added)](#aggiunto-added-3)
- [Modificato (Changed)](#modificato-changed-1)
- [Fixed](#fixed-2)
- [2.4 (2018-03-30)](#24-2018-03-30)
- [Aggiunto (Added)](#aggiunto-added)
- [Modificato (Changed)](#modificato-changed)
- [Rimosso (Removed)](#rimosso-removed)
- [Fixed](#fixed)
- [Aggiunto (Added)](#aggiunto-added-4)
- [Modificato (Changed)](#modificato-changed-2)
- [Fixed](#fixed-3)
- [2.3.1 (2018-02-19)](#231-2018-02-19)
- [Aggiunto (Added)](#aggiunto-added)
- [Modificato (Changed)](#modificato-changed)
- [Rimosso (Removed)](#rimosso-removed)
- [Fixed](#fixed)
- [2.3 (2018-01-27)](#23-in-sviluppo)
- [Aggiunto (Added)](#aggiunto-added)
- [Modificato (Changed)](#modificato-changed)
- [Deprecato (Deprecated)](#deprecato-deprecated)
- [Rimosso (Removed)](#rimosso-removed)
- [Fixed](#fixed)
- [Sicurezza (Security)](#sicurezza-security)
- [Aggiunto (Added)](#aggiunto-added-5)
- [Modificato (Changed)](#modificato-changed-3)
- [Fixed](#fixed-4)
- [2.3 (2018-02-16)](#23-2018-02-16)
- [Aggiunto (Added)](#aggiunto-added-6)
- [Modificato (Changed)](#modificato-changed-4)
- [Deprecato (Deprecated)](#deprecato-deprecated-1)
- [Rimosso (Removed)](#rimosso-removed-1)
- [Fixed](#fixed-5)
- [Sicurezza (Security)](#sicurezza-security-1)
- [2.2 (2016-11-10)](#22-2016-11-10)
- [Aggiunto (Added)](#aggiunto-added)
- [Fixed](#fixed)
- [Aggiunto (Added)](#aggiunto-added-7)
- [Fixed](#fixed-6)
- [2.1 (2015-04-02)](#21-2015-04-02)
- [Aggiunto (Added)](#aggiunto-added)
- [Modificato (Changed)](#modificato-changed)
- [Fixed](#fixed)
- [Aggiunto (Added)](#aggiunto-added-8)
- [Modificato (Changed)](#modificato-changed-5)
- [Fixed](#fixed-7)
## 2.4.4 (2018-12-12)
### Aggiunto (Added)
- Controllo sulla presenza di personalizzazioni nel modulo **Aggiornamenti**
- Stati multipli per la Fatture Elettroniche (per ampliamenti futuri)
### Fixed
- Risolti malfunzionamenti negli import degli allegati della Fattura Elettronica
- Risolti diversi bug
## 2.4.3 (2018-12-07)
### Aggiunto (Added)
- Nodi secondari per la Fatturazione Elettronica
- Importazione di Fatture Elettroniche in formato P7M
- Messaggi informativi in vari campi
### Fixed
- Risolti alcuni problemi di compatibilità
- Risolti malfunzionamenti delle righe dei documenti
- Fix dei calcoli
## 2.4.2 (2018-11-14)
### Aggiunto (Added)
- Plugin per generazione della Fatturazione Elettronica (modulo **Fatture di vendita**) e l'importazione relativa (modulo **Fatture di acquisto**)
- Libreria autonoma per i messaggi da mostrare all'utente
- Logging completo delle azioni degli utente (accessibile agli Amministratori)
- Supporto a [Prepared Statements PDO](http://php.net/manual/it/pdo.prepared-statements.php)
- Impostazioni da definire durante l'installazione e l'aggiornamento del software
- Helper per semplificare lo sviluppo di codice indipendente (file `lib/helpers.php`)
- Funzioni generiche per moduli e plugin (file `lib/common.php`)
- API per la gestione dell'applicazione
- Classe `Util\Zip` per la gestione dei file ZIP
- Controllo automatico degli aggiornamenti da GitHub (modulo **Aggiornamenti**)
- Ripristino semplificato dei backup (modulo **Backup**)
- Impostazioni per impostare un orario lavorativo personalizzato nel modulo **Dashboard**
- Possibilità di impostare un elemento predefinito per i moduli **Porti**, **Causali** e **Tipi di spedizioni**
- Impostazione *Stampa per anteprima e firma* per selezionare la stampa da mostrare nella sezione **Anteprima e firma** di **Attività**
- Ritenuta d'acconto predefinita per le **Anagrafiche**
- Sistema automatizzato per l'importazione delle classi di moduli e plugin (file `config/namespaces.php`)
- Sistema di notifiche predefinito
- Notifica di chiusura delle **Attività** (impostabile dal modulo **Stati attività**)
- Notifica di aggiunta e rimozione del tecnico dalle **Attività**
- Gestione revisione preventivi
- Categorizzazione impianti
- Modulo per gestione documentale
- Categorizzazione allegati
### Modificato (Changed)
- Normalizzazione delle nazioni registrate dal gestionale (https://github.com/umpirsky/country-list)
- Gestione delle strutture principali attraverso modelli (**Eloquent**)[https://laravel.com/docs/5.6/eloquent]
- Miglioramenti nella gestione dei record (variabile `$record` al posto di `$records[0]`)
- Ottimizzazione delle query di conteggio (metodo `fetchNum`)
- Miglioramento del sistema di aggiornamento e installazione, con supporto completo ai plugin
- Drag&drop nella **Dashboard** permette di impostare le attività senza sessioni di lavoro
- Aggiungere un tecnico in una **Attività** salva le modifiche apportate in precedenza
- Rinominat moduli ddt in "Ddt in uscita" e "Ddt in ingresso"
- Miglioramenti grafici vari
### Deprecato (Deprecated)
- Variabili globali $post e $get, da sostituire con le funzioni `post()` e `get()`
- Funzione `get_var()`, da sostituire con la funzione `setting()`
- Funzioni PHP inutilizzate: `datediff()`, `unique_filename()`, `create_thumbnails()`
### Rimosso (Removed)
- Funzioni PHP deprecate nella versione 2.3.*
### Sicurezza (Security)
- Abilitata protezione contro attacchi CSRF (opzione `$disableCSRF` nella configurazione per disattivarla in caso si verifichino problemi)
## 2.4.1 (2018-08-01)
### Aggiunto (Added)
- Supporto alla generazione PDF/A
- Gestione di Note di accredito e di addebito per le Fatture
- Salvataggio AJAX delle righe in Fatture
- Cambio automatico dello stato dei documenti
- Nomi per i filtri di accesso ai moduli
- Anteprime degli upload (per immagini e PDF)
- Validazione di indirizzi email e codici fiscali
- Test della connessione al server email
- Widget *Attività da pianificare* per individuare le attività senza tecnici
- Esportazione tabelle in PDF ed Excel (impostazione *Abilita esportazione Excel e PDF*)
- Stampa dedicata al calendario attività in **Dashboard**
- Operazioni rapide su **Anagrafiche** di tipo *Cliente*
- Campi aggiuntivi nella creazione di nuove **Anagrafiche**
- Possibilità di specificare tempi standard per *Tipologia di attività*
- Seriali nella stampa delle **Attività**
- Quantità calcolata tramite movimenti in data attuale per **Articoli**
- Movimenti manuali con causale degli **Articoli**
### Modificato (Changed)
- Miglioramento della gestione di installazione/aggiornamento
- Migliorata la procedura per i moduli (esempi: https://github.com/devcode-it/example)
- Aggiunto supporto all'installazione dei plugin (esempio: https://github.com/devcode-it/example/tree/master/sedi)
- Aggiunto supporto a file ZIP con vari moduli/plugin (installazione in ordine alfabetico)
- Miglioramento dei pre-requisiti di installazione
- Gestione degli upload tramite AJAX
- Gestione del logo per le stampe come un allegato
- Gestione delle immagini di **Articoli** e **Impianti** come allegati
- Miglioramento del plugin *Pianificazione attività* in **Contratti**
- Miglioramento della ritenuta d'acconto (calcolo impostabile su Imponibile o Rivalsa INPS)
- Ripristinati plugin *Pianificazione fatturazione* e widget *Rate contrattuali*
- Miglioramento della tabella dei *Costi Totali* in **Attività**
- Collegamento ad un'anagrafica obbligatorio per i nuovi utenti
- Ridenominazione delle tabelle `co_righe_contratti` e `co_righe2_contratti` in `co_contratti_promemoria` e `co_righe_contratti`
- I movimenti articoli utilizzano la data del documento relativo
- I chilometri del cliente vengono riportati nell'attività
- I tecnici possono aggiungere **Attività** solo a loro nome
### Fixed
- Correzione dei link alle stampe sulle tabelle dei moduli
- Correzione della scontistica per la stampa **Attività**
- Correzione degli arrotondamenti su IVA e imponibili nei documenti
- Correzione del budget dei **Contratti**
- Correzione della scadenza "Data fattura fine mese"
- Correzione del plugin *Statistiche* in **Anagrafiche**
- Correzione del widget *Debiti verso fornitori*
- Correzioni minori
## 2.4 (2018-03-30)
### Aggiunto (Added)
- Modelli di stampa su database, con possibilità di creare più stampe per singolo modulo e raggrupparle in unica voce di menu
- Possibilità di inviare le email dai vari moduli e gestione degli account SMTP
- Introduzione dei segmenti: filtri aggiuntivi definibili per ogni modulo
- Aggiunti sezionali per fatture acquisto / vendita
- Aggiunti sezionali per fatture acquisto/vendita
- Nuovo modulo archivio banche per definire poi in ogni anagrafica (cliente o fornitore) la banca predefinita
- Nuova pagina dedicata all'utente dove è possibile:
- Cambiare la propria password
@ -51,43 +196,44 @@ Il formato utilizzato è basato sulle linee guida di [Keep a Changelog](http://k
- Aggiunta gestione allegati anche per contratti, anagrafiche, preventivi, articoli, impianti
- Modulo per import CSV (anagrafiche)
### Modificato (Changed)
- Modificati pulsanti principali dei moduli e fissati in alto durante lo scorrimento
- Resi i pulsanti principali dei moduli dinamici e personalizzabili
- Migliorati interventi da pianificare
- Migliorati attività da pianificare
- Migliorato il calcolo della numerazione per i documenti
- Modificato il numero per le fatture di acquisto utilizzabile per numeri di protocollo
- Migliorata gestione dei menu a tendina dinamici
- Modificata aggiunta interventi in fatturazione, con raggruppamento per costi orari e diritti di chiamata
- Modificata aggiunta attività in fatturazione, con raggruppamento per costi orari e diritti di chiamata
- Modificato calcolo ritenuta d'acconto, con scelta se calcolare su imponibile o imponibile + rivalsa inps
### Fixed
- Corretto calcolo IVA con sconto globale unitario
- Corretto calcolo numerazione dei ddt
- Correzione visualizzazione di attività a calendario a cavallo di periodi diversi
- Correzioni minori
## 2.3.1 (2018-02-19)
### Aggiunto (Added)
- Aggiunti i seriali in stampa
- Aggiunta la zona nelle attività (in sola lettura dall'anagrafica)
- Aggiunta tramite flag la possibilità di inserire la descrizione dell'intervento in fattura
- Aggiunta esportazione bulk in zip dei pdf degli interventi selezionati
- Aggiunta tramite flag la possibilità di inserire la descrizione dell'attività in fattura
- Aggiunta esportazione bulk in zip dei pdf delle attività selezionate
- Aggiunte informazioni del cliente e fornitore nelle relative stampe ordini
### Modificato (Changed)
- Migliorati i widget di "Crediti da clienti" e "Debiti verso fornitori", con calcolo parziale del rimanente
- Disabilitato di default il modulo "Viste"
- Migliorata la gestione della pianificazione attività sui contratti, con la possibilità di eliminare tutte le pianificazioni
o di creare direttamente un intervento collegato
- Modificato l'inserimento di interventi in fattura raggruppando per costo orario nel caso ci siano più costi orari
- Migliorata la gestione della pianificazione attività sui contratti, con la possibilità di eliminare tutte le pianificazioni o di creare direttamente una attività collegata
- Modificato l'inserimento di attività in fattura raggruppando per costo orario nel caso ci siano più costi orari
- Spostato il conto "Perdite e profitti" nello stato patrimoniale
### Fixed
- Corretti diversi problemi in fase di installazione
- Modifica e miglioramento dell'arrotondamento iva in fattura, sia a video che in stampa
- Corretto il caricamento di menu a tendina per gli utenti con permessi limitati
@ -101,8 +247,8 @@ Il formato utilizzato è basato sulle linee guida di [Keep a Changelog](http://k
- Correzioni varie sulla gestione viste
- Corretto il piano dei conti per arrotondare gli importi come negli altri moduli
- Corretto il calcolo iva nei contratti
- Corretto il salvataggio delle sessioni tecnico nei propri interventi
- Corretto un problema nel salvataggio firma intervento su alcuni tablet
- Corretto il salvataggio delle sessioni tecnico nelle proprie attività
- Corretto un problema nel salvataggio firma attività su alcuni tablet
- Corretto ordinamento voci di menu laterale
- Altre correzioni minori e strutturali
@ -149,11 +295,11 @@ Il formato utilizzato è basato sulle linee guida di [Keep a Changelog](http://k
- Nuovo file `lib/init.js` per permettere una rapida inizializzazione dei componenti JS
- Nuove funzioni relative ai diversi moduli
- Introduzione della numerazione univoca per gli impianti (**MyImpianti**)
- Possibilità di individuare i componenti dell'impianto su cui l'intervento viene effettuato (**Interventi**)
- Possibilità di firmare degli interventi (**Interventi**)
- Possibilità di selezionare della tipologia di attività per ogni sessione di lavoro (**Interventi**)
- Introduzione di una tabella riepilogativa più completa dei costi (**Interventi**)
- Introduzione di sconti globali e specifici (unitari e percentuali) in **Contratti**, **DDT**, **Fatture**, **Interventi**, **Preventivi**, **Ordini**
- Possibilità di individuare i componenti dell'impianto su cui l'attività viene effettuato (**Attività**)
- Possibilità di firmare le attività (**Attività**)
- Possibilità di selezionare della tipologia di attività per ogni sessione di lavoro (**Attività**)
- Introduzione di una tabella riepilogativa più completa dei costi (**Attività**)
- Introduzione di sconti globali e specifici (unitari e percentuali) in **Contratti**, **DDT**, **Fatture**, **Attività**, **Preventivi**, **Ordini**
### Modificato (Changed)
@ -174,7 +320,7 @@ Il formato utilizzato è basato sulle linee guida di [Keep a Changelog](http://k
- Miglioramento delle informazioni disponibili sul progetto e della procedura di segnalazione dei bug
- Miglioramento generale sull'identificazione del modulo attualmente in uso e sull'inclusione dei file necessari per il funzionamento
- La prima anagrafica di tipo Azienda caricata viene impostata come "Azienda predefinita"
- Ottimizzazione della schermata per aggiunta dell'intervento
- Ottimizzazione della schermata per aggiunta dell'attività
- Miglioramento dei riquadri delle spese aggiuntive e degli articoli
- Miglioramento dei permessi di visione per il modulo **MyImpianti** (ogni cliente vede solo i propri impianti)

View File

@ -9,7 +9,7 @@
<br>
<a href="http://openstamanager.com">Sito web</a>
&middot;
<a href="https://devcode-it.github.io/openstamanager">Documentazione tecnica</a>
<a href="https://devcode-it.github.io/openstamanager">Documentazione</a>
&middot;
<a href="http://openstamanager.com/forum">Forum</a>
</p>
@ -44,7 +44,7 @@ La documentazione ufficiale è disponibile all'indirizzo <https://devcode-it.git
- [Installazione](#installazione)
- [Versioni](#versioni)
- [GitHub](#github)
- [Perché software open-source](#perch%C3%A9-software-open-source)
- [Perché software open-source](#perché-software-open-source)
- [Community](#community)
- [Contribuire](#contribuire)
- [Sviluppatori](#sviluppatori)
@ -57,9 +57,9 @@ La documentazione ufficiale è disponibile all'indirizzo <https://devcode-it.git
L'installazione del gestionale richiede la presenza di un server web con abilitato il [DBMS MySQL](https://www.mysql.com) e il linguaggio di programmazione [PHP](http://php.net).
- PHP >= 5.6
- MySQL >= 5.0
- MySQL >= 5.6.5
Per ulteriori informazioni sui pacchetti che forniscono questi elementi di default, visitare la sezione [Installazione](https://devcode-it.github.io/openstamanager/installazione.html) della documentazione.
Per ulteriori informazioni sui pacchetti che forniscono questi elementi di default, visitare la sezione [Installazione](https://devcode-it.github.io/openstamanager/2.4.2/docs/installazione/) della documentazione.
## Installazione
@ -105,7 +105,7 @@ In alternativa alla sequenza di comandi precedente, è possibile utilizzare il s
yarn run develop-OSM
```
Per ulteriori informazioni, visitare le sezioni [Assets](https://devcode-it.github.io/openstamanager/assets.html) e [Framework](https://devcode-it.github.io/openstamanager/framework.html) della documentazione.
Per ulteriori informazioni, visitare le sezioni [Assets](https://devcode-it.github.io/openstamanager/2.4.2/docs/base/assets/) e [Framework](https://devcode-it.github.io/openstamanager/2.4.2/docs/base/framework/) della documentazione.
## Perché software open-source
@ -117,7 +117,7 @@ In questo modo è possibile ottenere un'ulteriore garanzia sul funzionamento del
La community è una componente importante in un progetto open-source, perché mette in contatto utenti e programmatori tra di loro e permette pertanto l'individuazione di soluzioni innovative e migliori.
Siamo presenti su [Facebook](https://www.facebook.com/openstamanager), e il nostro forum ufficiale è disponibile all'indirizzo <http://www.openstamanager.com/forum/>, dove potete segnalare i vostri problemi e soddisfare le vostre curiosità nelle sezioni più adeguate:
Siamo presenti su [Facebook](https://www.facebook.com/openstamanager), e il nostro forum ufficiale è disponibile all'indirizzo <http://www.openstamanager.com/forum>, dove potete segnalare i vostri problemi e soddisfare le vostre curiosità nelle sezioni più adeguate:
- [Idee, suggerimenti e consigli](http://www.openstamanager.com/forum/viewforum.php?f=1)
- [Problemi con la prima installazione](http://www.openstamanager.com/forum/viewforum.php?f=2)
@ -129,10 +129,7 @@ Siamo presenti su [Facebook](https://www.facebook.com/openstamanager), e il nost
## Contribuire
Per poter contribuire, si consiglia di seguire le indicazioni descritte all'interno della [documentazione ufficiale](https://devcode-it.github.io/openstamanager/contribuire.html).
Le impostazione di base per il codice sono disponibili attraverso [editor config](https://github.com/devcode-it/openstamanager/blob/master/.editorconfig) per l'utilizzo semplificato negli editor più comuni.
Maggiori informazioni sulla configurazione e sul plugin sono disponibili nel sito <http://editorconfig.org>.
Per poter contribuire ed eseguire i test automatici, si consiglia di seguire le indicazioni descritte all'interno della [documentazione ufficiale](https://github.com/devcode-it/openstamanager/blob/master/.github/CONTRIBUTING.md).
## Sviluppatori

View File

@ -2,438 +2,209 @@
include_once __DIR__.'/core.php';
// Lettura parametri iniziali
if (!empty($id_plugin)) {
$info = Plugins::get($id_plugin);
$directory = '/plugins/'.$info['directory'];
$permesso = $info['idmodule_to'];
} else {
$info = Modules::get($id_module);
$directory = '/modules/'.$info['directory'];
$permesso = $id_module;
if (empty($structure) || empty($structure['enabled'])) {
die(tr('Accesso negato'));
}
$upload_dir = DOCROOT.'/files/'.basename($directory);
$upload_dir = DOCROOT.'/'.Uploads::getDirectory($id_module, $id_plugin);
$dbo->query('START TRANSACTION');
$database->beginTransaction();
// GESTIONE UPLOAD
if (filter('op') == 'link_file' || filter('op') == 'unlink_file') {
// Controllo sui permessi di scrittura per il modulo
if (Modules::getPermission($id_module) != 'rw') {
$_SESSION['errors'][] = tr('Non hai permessi di scrittura per il modulo _MODULE_', [
flash()->error(tr('Non hai permessi di scrittura per il modulo _MODULE_', [
'_MODULE_' => '"'.Modules::get($id_module)['name'].'"',
]);
]));
}
// Controllo sui permessi di scrittura per il file system
elseif (!directory($upload_dir)) {
$_SESSION['errors'][] = tr('Non hai i permessi di scrittura nella cartella _DIR_!', [
flash()->error(tr('Non hai i permessi di scrittura nella cartella _DIR_!', [
'_DIR_' => '"files"',
]);
]));
}
// Gestione delle operazioni
else {
// UPLOAD
if (filter('op') == 'link_file' && !empty($_FILES) && !empty($_FILES['blob']['name'])) {
$nome = filter('nome_allegato');
$nome = !empty($nome) ? $nome : $_FILES['blob']['name'];
$src = $_FILES['blob']['tmp_name'];
$f = pathinfo($_FILES['blob']['name']);
/*
$allowed = [
// Image formats
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'jpe' => 'image/jpeg',
'gif' => 'image/gif',
'png' => 'image/png',
'bmp' => 'image/bmp',
'tif' => 'image/tiff',
'tiff' => 'image/tiff',
'ico' => 'image/x-icon',
// Video formats
'asx' => 'video/asf',
'asf' => 'video/asf',
'wax' => 'video/asf',
'wmv' => 'video/asf',
'wmx' => 'video/asf',
'avi' => 'video/avi',
'divx' => 'video/divx',
'flv' => 'video/x-flv',
'mov' => 'video/quicktime',
'qt' => 'video/quicktime',
'mpg' => 'video/mpeg',
'mpeg' => 'video/mpeg',
'mpe' => 'video/mpeg',
'mp4' => 'video/mp4',
'm4v' => 'video/mp4',
'ogv' => 'video/ogg',
'mkv' => 'video/x-matroska',
// Text formats
'txt' => 'text/plain',
'csv' => 'text/csv',
'tsv' => 'text/tab-separated-values',
'ics' => 'text/calendar',
'rtx' => 'text/richtext',
'css' => 'text/css',
'htm' => 'text/html',
'html' => 'text/html',
// Audio formats
'mp3' => 'audio/mpeg',
'm4a' => 'audio/mpeg',
'm4b' => 'audio/mpeg',
'mp' => 'audio/mpeg',
'm4b' => 'audio/mpeg',
'ra' => 'audio/x-realaudio',
'ram' => 'audio/x-realaudio',
'wav' => 'audio/wav',
'ogg' => 'audio/ogg',
'oga' => 'audio/ogg',
'mid' => 'audio/midi',
'midi' => 'audio/midi',
'wma' => 'audio/wma',
'mka' => 'audio/x-matroska',
// Misc application formats
'rtf' => 'application/rtf',
'js' => 'application/javascript',
'pdf' => 'application/pdf',
'swf' => 'application/x-shockwave-flash',
'class' => 'application/java',
'tar' => 'application/x-tar',
'zip' => 'application/zip',
'gz' => 'application/x-gzip',
'gzip' => 'application/x-gzip',
'rar' => 'application/rar',
'7z' => 'application/x-7z-compressed',
// MS Office formats
'doc' => 'application/msword',
'pot' => 'application/vnd.ms-powerpoint',
'pps' => 'application/vnd.ms-powerpoint',
'ppt' => 'application/vnd.ms-powerpoint',
'wri' => 'application/vnd.ms-write',
'xla' => 'application/vnd.ms-excel',
'xls' => 'application/vnd.ms-excel',
'xlt' => 'application/vnd.ms-excel',
'xlw' => 'application/vnd.ms-excel',
'mdb' => 'application/vnd.ms-access',
'mpp' => 'application/vnd.ms-project',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'docm' => 'application/vnd.ms-word.document.macroEnabled.12',
'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
'dotm' => 'application/vnd.ms-word.template.macroEnabled.12',
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12',
'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
'xltm' => 'application/vnd.ms-excel.template.macroEnabled.12',
'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12',
'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
'potm' => 'application/vnd.ms-powerpoint.template.macroEnabled.12',
'ppam' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12',
'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
'sldm' => 'application/vnd.ms-powerpoint.slide.macroEnabled.12',
'onetoc' => 'application/onenote',
'onetoc2' => 'application/onenote',
'onetmp' => 'application/onenote',
'onepkg' => 'application/onenote',
// OpenOffice formats
'odt' => 'application/vnd.oasis.opendocument.text',
'odp' => 'application/vnd.oasis.opendocument.presentation',
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
'odg' => 'application/vnd.oasis.opendocument.graphics',
'odc' => 'application/vnd.oasis.opendocument.chart',
'odb' => 'application/vnd.oasis.opendocument.database',
'odf' => 'application/vnd.oasis.opendocument.formula',
// WordPerfect formats
'wp' => 'application/wordperfect',
'wpd' => 'application/wordperfect',
];
if (in_array($f['extension'], array_keys($allowed))) {
*/
do {
$filename = random_string().'.'.$f['extension'];
} while (file_exists($upload_dir.'/'.$filename));
$upload = Uploads::upload($_FILES['blob'], [
'name' => filter('nome_allegato'),
'category' => filter('categoria'),
'id_module' => $id_module,
'id_plugin' => $id_plugin,
'id_record' => $id_record,
]);
// Creazione file fisico
if (move_uploaded_file($src, $upload_dir.'/'.$filename)) {
$dbo->insert('zz_files', [
'nome' => $nome,
'filename' => $filename,
'original' => $_FILES['blob']['name'],
'id_module' => $id_module,
'id_record' => $id_record,
]);
$_SESSION['infos'][] = tr('File caricato correttamente!');
if (!empty($upload)) {
flash()->info(tr('File caricato correttamente!'));
} else {
$_SESSION['errors'][] = tr('Errore durante il caricamento del file!');
flash()->error(tr('Errore durante il caricamento del file!'));
}
/*
} else {
$_SESSION['errors'][] = tr('Tipologia di file non permessa!');
}
*/
}
// DELETE
elseif (filter('op') == 'unlink_file' && filter('filename') !== null) {
$filename = filter('filename');
$name = Uploads::delete(filter('filename'), [
'id_module' => $id_module,
'id_plugin' => $id_plugin,
'id_record' => $id_record,
]);
$rs = $dbo->fetchArray('SELECT * FROM zz_files WHERE id_module='.prepare($id_module).' AND id='.prepare(filter('id')).' AND filename='.prepare($filename));
if (delete($upload_dir.'/'.$filename)) {
$query = 'DELETE FROM zz_files WHERE id_module='.prepare($id_module).' AND id='.prepare(filter('id')).' AND filename='.prepare($filename);
if ($dbo->query($query)) {
$_SESSION['infos'][] = tr('File _FILE_ eliminato!', [
'_FILE_' => '"'.$rs[0]['nome'].'"',
]);
}
if (!empty($name)) {
flash()->info(tr('File _FILE_ eliminato!', [
'_FILE_' => '"'.$name.'"',
]));
} else {
$_SESSION['errors'][] = tr("Errore durante l'eliminazione del file _FILE_ in _DIR_!", [
'_FILE_' => '"'.$rs[0]['nome'].'"',
'_DIR_' => '"files/'.$module_dir.'/"',
]);
flash()->error(tr("Errore durante l'eliminazione del file!"));
}
}
redirect(ROOTDIR.'/editor.php?id_module='.$id_module.'&id_record='.$id_record);
redirect(ROOTDIR.'/editor.php?id_module='.$id_module.'&id_record='.$id_record.((!empty($options['id_plugin'])) ? '#tab_'.$options['id_plugin'] : ''));
}
} elseif (filter('op') == 'download_file') {
$rs = $dbo->fetchArray('SELECT * FROM zz_files WHERE id_module='.prepare($id_module).' AND id='.prepare(filter('id')).' AND filename='.prepare(filter('filename')));
download($upload_dir.'/'.$rs[0]['filename'], $rs[0]['original']);
} elseif (filter('op') == 'send-email') {
$template = Mail::getTemplate($post['template']);
} elseif (post('op') == 'send-email') {
$id_template = post('template');
// Elenco degli allegati
$attachments = [];
// Stampe
foreach ($post['prints'] as $print) {
$print = Prints::get($print);
// Utilizzo di una cartella particolare per il salvataggio temporaneo degli allegati
$filename = DOCROOT.'/files/attachments/'.$print['title'].' - '.$id_record.'.pdf';
Prints::render($print['id'], $id_record, $filename);
$attachments[] = [
'path' => $filename,
'name' => $print['title'].'.pdf',
];
}
// Allegati del record
$selected = [];
if (!empty($post['attachments'])) {
$selected = $dbo->fetchArray('SELECT * FROM zz_files WHERE id IN ('.implode(',', $post['attachments']).') AND id_module = '.prepare($id_module).' AND id_record = '.prepare($id_record));
}
foreach ($selected as $attachment) {
$attachments[] = [
'path' => $upload_dir.'/'.$attachment['filename'],
'name' => $attachment['nome'],
];
}
// Allegati dell'Azienda predefinita
$anagrafiche = Modules::get('Anagrafiche');
$selected = [];
if (!empty($post['attachments'])) {
$selected = $dbo->fetchArray('SELECT * FROM zz_files WHERE id IN ('.implode(',', $post['attachments']).') AND id_module != '.prepare($id_module));
}
foreach ($selected as $attachment) {
$attachments[] = [
'path' => DOCROOT.'/files/'.$anagrafiche['directory'].'/'.$attachment['filename'],
'name' => $attachment['nome'],
];
}
// Preparazione email
$mail = new Mail();
// Conferma di lettura
if (!empty($post['read_notify'])) {
$mail->ConfirmReadingTo = $mail->From;
}
// Reply To
if (!empty($template['reply_to'])) {
$mail->AddReplyTo($template['reply_to']);
}
// CC
if (!empty($template['cc'])) {
$mail->AddCC($template['cc']);
}
// BCC
if (!empty($template['bcc'])) {
$mail->AddBCC($template['bcc']);
}
// Inizializzazione
$mail = new Notifications\EmailNotification();
$mail->setTemplate($id_template, $id_record);
// Destinatari
foreach ($post['destinatari'] as $key => $destinatario) {
$type = $post['tipo_destinatari'][$key];
$pieces = explode('<', $destinatario);
$count = count($pieces);
$name = null;
if ($count > 1) {
$email = substr(end($pieces), 0, -1);
$name = substr($destinatario, 0, strpos($destinatario, '<'.$email));
} else {
$email = $destinatario;
}
if (!empty($email)) {
if ($type == 'a') {
$mail->AddAddress($email, $name);
} elseif ($type == 'cc') {
$mail->AddCC($email, $name);
} elseif ($type == 'bcc') {
$mail->AddBCC($email, $name);
}
}
$receivers = array_clean(post('destinatari'));
$types = post('tipo_destinatari');
foreach ($receivers as $key => $receiver) {
$mail->addReceiver($receiver, $types[$key]);
}
// Oggetto
$mail->Subject = $post['subject'];
// Contenuti
$mail->setSubject(post('subject'));
$mail->setContent(post('body'));
// Allegati
foreach ($attachments as $attachment) {
$mail->AddAttachment($attachment['path'], $attachment['name']);
// Stampe da allegare
$prints = post('prints');
foreach ($prints as $print) {
$mail->addPrint($print, $id_record);
}
// Contenuto
$mail->Body = $post['body'];
// Allegati originali
$files = post('attachments');
foreach ($files as $file) {
$mail->addUpload($file);
}
// Invio mail
if (!$mail->send()) {
$_SESSION['errors'][] = tr("Errore durante l'invio dell'email").': '.$mail->ErrorInfo;
} else {
$_SESSION['infos'][] = tr('Email inviata correttamente!');
}
try {
$mail->send(true); // Il valore true impone la gestione degli errori tramite eccezioni
redirect(ROOTDIR.'/editor.php?id_module='.$id_module.'&id_record='.$id_record);
exit();
flash()->info(tr('Email inviata correttamente!'));
} catch (PHPMailer\PHPMailer\Exception $e) {
flash()->error(tr("Errore durante l'invio dell'email").': '.$e->errorMessage());
}
}
if (Modules::getPermission($permesso) == 'r' || Modules::getPermission($permesso) == 'rw') {
if (!empty($info['script'])) {
// Inclusione di eventuale plugin personalizzato
if (file_exists(DOCROOT.'/modules/'.$info['module_dir'].'/plugins/custom/'.$info['script'])) {
include DOCROOT.'/modules/'.$info['module_dir'].'/plugins/custom/'.$info['script'];
} elseif (file_exists(DOCROOT.'/modules/'.$info['module_dir'].'/plugins/'.$info['script'])) {
include DOCROOT.'/modules/'.$info['module_dir'].'/plugins/'.$info['script'];
}
// Inclusione di eventuale plugin personalizzato
if (!empty($structure['script'])) {
include $structure->getEditFile();
return;
}
$database->commitTransaction();
// Caricamento helper modulo (verifico se ci sono helper personalizzati)
if (file_exists(DOCROOT.$directory.'/custom/modutil.php')) {
include_once DOCROOT.$directory.'/custom/modutil.php';
} elseif (file_exists(DOCROOT.$directory.'/modutil.php')) {
include_once DOCROOT.$directory.'/modutil.php';
}
return;
}
// Lettura risultato query del modulo
if (file_exists(DOCROOT.$directory.'/custom/init.php')) {
include DOCROOT.$directory.'/custom/init.php';
} elseif (file_exists(DOCROOT.$directory.'/init.php')) {
include DOCROOT.$directory.'/init.php';
}
// Lettura risultato query del modulo
$init = $structure->filepath('init.php');
if (!empty($init)) {
include_once $init;
}
if (Modules::getPermission($permesso) == 'rw') {
// Esecuzione delle operazioni di gruppo
$id_records = post('id_records');
$id_records = is_array($id_records) ? $id_records : explode(';', $id_records);
$id_records = array_filter($id_records, function ($var) {return !empty($var); });
$id_records = array_unique($id_records);
// Retrocompatibilità
if (!isset($record) && isset($records[0])) {
$record = $records[0];
} elseif (!isset($records[0]) && isset($record)) {
$records = [$record];
} elseif (!isset($record)) {
$record = [];
$records = [$record];
}
$bulk = null;
if (file_exists(DOCROOT.$directory.'/custom/bulk.php')) {
$bulk = include DOCROOT.$directory.'/custom/bulk.php';
} elseif (file_exists(DOCROOT.$directory.'/bulk.php')) {
$bulk = include DOCROOT.$directory.'/bulk.php';
}
$bulk = (array) $bulk;
// Registrazione del record
HTMLBuilder\HTMLBuilder::setRecord($record);
if (in_array(post('op'), array_keys($bulk))) {
redirect(ROOTDIR.'/controller.php?id_module='.$id_module, 'js');
} else {
// Esecuzione delle operazioni del modulo
if (file_exists(DOCROOT.$directory.'/custom/actions.php')) {
include DOCROOT.$directory.'/custom/actions.php';
} elseif (file_exists(DOCROOT.$directory.'/actions.php')) {
include DOCROOT.$directory.'/actions.php';
if ($structure->permission == 'rw') {
// Esecuzione delle operazioni di gruppo
$id_records = post('id_records');
$id_records = is_array($id_records) ? $id_records : explode(';', $id_records);
$id_records = array_clean($id_records);
$id_records = array_unique($id_records);
$bulk = $structure->filepath('bulk.php');
$bulk = empty($bulk) ? [] : include $bulk;
$bulk = empty($bulk) ? [] : $bulk;
if (in_array(post('op'), array_keys($bulk))) {
redirect(ROOTDIR.'/controller.php?id_module='.$id_module, 'js');
} else {
// Esecuzione delle operazioni del modulo
include $structure->filepath('actions.php');
// Operazioni generiche per i campi personalizzati
if (post('op') != null) {
$query = 'SELECT `id`, `name` FROM `zz_fields` WHERE ';
if (!empty($id_plugin)) {
$query .= '`id_plugin` = '.prepare($id_plugin);
} else {
$query .= '`id_module` = '.prepare($id_module);
}
$customs = $dbo->fetchArray($query);
// Operazioni generiche per i campi personalizzati
if (post('op') != null) {
$query = 'SELECT `id`, `name` FROM `zz_fields` WHERE ';
if (!empty($id_plugin)) {
$query .= '`id_plugin` = '.prepare($id_plugin);
} else {
$query .= '`id_module` = '.prepare($id_module);
}
$customs = $dbo->fetchArray($query);
if (!starts_with(post('op'), 'delete')) {
$values = [];
foreach ($customs as $custom) {
if (isset($post[$custom['name']])) {
$values[$custom['id']] = $post[$custom['name']];
}
if (!starts_with(post('op'), 'delete')) {
$values = [];
foreach ($customs as $custom) {
if (post($custom['name']) !== null) {
$values[$custom['id']] = post($custom['name']);
}
}
// Inserimento iniziale
if (starts_with(post('op'), 'add')) {
foreach ($values as $key => $value) {
$dbo->insert('zz_field_record', [
// Inserimento iniziale
if (starts_with(post('op'), 'add')) {
// Informazioni di log
Filter::set('get', 'id_record', $id_record);
foreach ($values as $key => $value) {
$dbo->insert('zz_field_record', [
'id_record' => $id_record,
'id_field' => $key,
'value' => $value,
]);
}
}
}
// Aggiornamento
elseif (starts_with(post('op'), 'update')) {
foreach ($values as $key => $value) {
$dbo->update('zz_field_record', [
// Aggiornamento
elseif (starts_with(post('op'), 'update')) {
foreach ($values as $key => $value) {
$dbo->update('zz_field_record', [
'value' => $value,
], [
'id_record' => $id_record,
'id_field' => $key,
]);
}
}
}
}
// Eliminazione
elseif (!empty($customs)) {
$dbo->query('DELETE FROM `zz_field_record` WHERE `id_record` = '.prepare($id_record).' AND `id_field` IN ('.implode(array_column($customs, 'id')).')');
}
// Eliminazione
elseif (!empty($customs)) {
$dbo->query('DELETE FROM `zz_field_record` WHERE `id_record` = '.prepare($id_record).' AND `id_field` IN ('.implode(',', array_column($customs, 'id')).')');
}
}
}
}
$dbo->query('COMMIT');
$database->commitTransaction();

48
add.php
View File

@ -2,34 +2,20 @@
include_once __DIR__.'/core.php';
if (!empty($id_plugin)) {
$info = Plugins::get($id_plugin);
// Inclusione elementi fondamentali del modulo
include $docroot.'/actions.php';
$directory = '/plugins/'.$info['directory'];
} else {
// Controllo dei permessi
if (empty($id_plugin)) {
Permissions::check('rw');
$module = Modules::get($id_module);
$directory = '/modules/'.$module['directory'];
}
$module_dir = $module['directory'];
// Caricamento template
echo '
<div id="form_'.$id_module.'-'.$id_plugin.'">
';
// Caricamento template popup
if (file_exists($docroot.$directory.'/custom/add.php')) {
include $docroot.$directory.'/custom/add.php';
} elseif (file_exists($docroot.$directory.'/custom/add.html')) {
include $docroot.$directory.'/custom/add.html';
} elseif (file_exists($docroot.$directory.'/add.php')) {
include $docroot.$directory.'/add.php';
} elseif (file_exists($docroot.$directory.'/add.html')) {
include $docroot.$directory.'/add.html';
}
include !empty(get('edit')) ? $structure->getEditFile() : $structure->getAddFile();
echo '
</div>';
@ -38,6 +24,9 @@ echo '
echo '
<div class="hide" id="custom_fields_top-add">
<input type="hidden" name="id_module" value="'.$id_module.'">
<input type="hidden" name="id_plugin" value="'.$id_plugin.'">
{( "name": "custom_fields", "id_module": "'.$id_module.'", "id_plugin": "'.$id_plugin.'", "position": "top", "place": "add" )}
</div>
@ -54,6 +43,11 @@ $(document).ready(function(){
// Campi a fine form
var last = form.find(".panel").last();
if (!last.length) {
last = form.find(".box").last();
}
if (!last.length) {
last = form.find(".row").eq(-2);
}
@ -68,21 +62,15 @@ if (isAjaxRequest()) {
$(document).ready(function(){
data = {};';
foreach ($get as $key => $value) {
foreach (Filter::getGET() as $key => $value) {
echo '
data.'.$key.' = "'.$value.'";';
}
if (file_exists($docroot.$directory.'/custom/actions.php')) {
$url = $rootdir.$directory.'/custom/actions.php';
} elseif (file_exists($docroot.$directory.'/actions.php')) {
$url = $rootdir.$directory.'/actions.php';
}
echo '
$("#form_'.$id_module.'-'.$id_plugin.'").find("form").ajaxForm({
url: "'.$url.'",
url: globals.rootdir + "/actions.php",
beforeSubmit: function(arr, $form, options) {
return $form.parsley().validate();
},
@ -91,9 +79,9 @@ $(document).ready(function(){
success: function(data){
data = data.trim();
if(data && !$("#'.$get['select'].'").val()) {
if(data && $("#'.get('select').'").val() !== undefined ) {
result = JSON.parse(data);
$("#'.$get['select'].'").selectSetNew(result.id, result.text);
$("#'.get('select').'").selectSetNew(result.id, result.text);
}
$("#bs-popup2").modal("hide");

View File

@ -46,4 +46,36 @@ switch (get('op')) {
}
break;
case 'list_attachments':
echo '{( "name": "filelist_and_upload", "id_module": "'.$id_module.'", "id_record": "'.$id_record.'", "id_plugin": "'.$id_plugin.'" )}';
break;
case 'active_users':
$posizione = get('id_module');
if (isset($id_record)) {
$posizione .= ', '.get('id_record');
}
$user = Auth::user();
$interval = setting('Timeout notifica di presenza (minuti)') * 60 * 2;
$dbo->query('UPDATE zz_semaphores SET updated = NOW() WHERE id_utente = :user_id AND posizione = :position', [
':user_id' => $user['id'],
':position' => $posizione,
]);
// Rimozione record scaduti
$dbo->query('DELETE FROM zz_semaphores WHERE DATE_ADD(updated, INTERVAL :interval SECOND) <= NOW()', [
':interval' => $interval,
]);
$datas = $dbo->fetchArray('SELECT DISTINCT username FROM zz_semaphores INNER JOIN zz_users ON zz_semaphores.id_utente=zz_users.id WHERE zz_semaphores.id_utente != :user_id AND posizione = :position', [
':user_id' => $user['id'],
':position' => $posizione,
]);
echo json_encode($datas);
break;
}

View File

@ -3,8 +3,8 @@
include_once __DIR__.'/core.php';
if (!isset($resource)) {
$module = $get['module'];
$op = $get['op'];
$module = get('module');
$op = get('op');
$result = AJAX::complete($op);

View File

@ -11,13 +11,7 @@ $order = filter('order')[0];
$order['column'] = $order['column'] - 1;
array_shift($columns);
// Lettura parametri iniziali
if (!empty($id_plugin)) {
$element = Plugins::get($id_plugin);
} else {
$element = Modules::get($id_module);
}
$total = App::readQuery($element);
$total = App::readQuery($structure);
// Lettura parametri modulo
$result_query = $total['query'];
@ -31,11 +25,7 @@ $results['summable'] = [];
if (!empty($result_query) && $result_query != 'menu' && $result_query != 'custom') {
// Conteggio totale
$count_query = 'SELECT COUNT(*) as `tot` FROM ('.$result_query.') AS `count`';
$count = $dbo->fetchArray($count_query);
if (!empty($count)) {
$results['recordsTotal'] = $count[0]['tot'];
}
$results['recordsTotal'] = $dbo->fetchNum($result_query);
// Filtri di ricerica
$search_filters = [];
@ -48,12 +38,17 @@ if (!empty($result_query) && $result_query != 'menu' && $result_query != 'custom
$search_filters[] = str_replace('|search|', prepare('%'.$piece.'%'), $total['search_inside'][$i]);
}
} else {
// Per le icone cerco per il campo icon_title
if (preg_match('/^icon_(.+?)$/', $total['search_inside'][$i], $m)) {
$total['search_inside'][$i] = 'icon_title_'.$m[1];
// Per le icone cerco nel campo icon_title
if (preg_match('/^icon_(.+?)$/', $total['fields'][$i], $m)) {
$total['search_inside'][$i] = '`icon_title_'.$m[1].'`';
}
$search_filters[] = '`'.$total['search_inside'][$i].'` LIKE '.prepare('%'.trim($columns[$i]['search']['value'].'%'));
// Per i colori cerco nel campo color_title
elseif (preg_match('/^color_(.+?)$/', $total['fields'][$i], $m)) {
$total['search_inside'][$i] = '`color_title_'.$m[1].'`';
}
$search_filters[] = $total['search_inside'][$i].' LIKE '.prepare('%'.trim($columns[$i]['search']['value'].'%'));
}
}
}
@ -102,6 +97,7 @@ if (!empty($result_query) && $result_query != 'menu' && $result_query != 'custom
// Query effettiva
$query = str_replace_once('SELECT', 'SELECT SQL_CALC_FOUND_ROWS', $result_query);
$rs = $dbo->fetchArray($query);
// Conteggio dei record filtrati
@ -118,12 +114,12 @@ if (!empty($result_query) && $result_query != 'menu' && $result_query != 'custom
$value = trim($r[$field]);
// Allineamento a destra se il valore della prima riga risulta numerica
if (Translator::getFormatter()->isStandardNumber($value)) {
if (formatter()->isStandardNumber($value)) {
$align[$field] = 'text-right';
}
// Allineamento al centro se il valore della prima riga risulta relativo a date o icone
elseif (Translator::getFormatter()->isStandardDate($value) || preg_match('/^icon_(.+?)$/', $field)) {
elseif (formatter()->isStandardDate($value) || preg_match('/^icon_(.+?)$/', $field)) {
$align[$field] = 'text-center';
}
}
@ -147,20 +143,20 @@ if (!empty($result_query) && $result_query != 'menu' && $result_query != 'custom
// Formattazione automatica
if (!empty($total['format'][$pos]) && !empty($value)) {
if (Translator::getFormatter()->isStandardDate($value)) {
if (formatter()->isStandardDate($value)) {
$value = Translator::dateToLocale($value);
} elseif (Translator::getFormatter()->isStandardTime($value)) {
} elseif (formatter()->isStandardTime($value)) {
$value = Translator::timeToLocale($value);
} elseif (Translator::getFormatter()->isStandardTimestamp($value)) {
} elseif (formatter()->isStandardTimestamp($value)) {
$value = Translator::timestampToLocale($value);
} elseif (Translator::getFormatter()->isStandardNumber($value)) {
} elseif (formatter()->isStandardNumber($value)) {
$value = Translator::numberToLocale($value);
}
}
// Icona
if (preg_match('/^color_(.+?)$/', $field, $m)) {
$value = $r['color_title_'.$m[1]] ?: '';
$value = isset($r['color_title_'.$m[1]]) ? $r['color_title_'.$m[1]] : '';
$column['class'] = 'text-center small';
$column['data-background'] = $r[$field];
@ -168,15 +164,11 @@ if (!empty($result_query) && $result_query != 'menu' && $result_query != 'custom
// Icona di stampa
elseif ($field == '_print_') {
$print_url = $r['_print_'];
$print = $r['_print_'];
preg_match_all('/\$(.+?)\$/', $print_url, $matches);
$print_url = Prints::getHref($print, $r['id']);
for ($m = 0; $m < sizeof($matches[0]); ++$m) {
$print_url = str_replace($matches[0][$m], $r[$matches[1][$m]], $print_url);
}
$value = '<a href="'.$rootdir.'/'.$print_url.'" target="_blank"><i class="fa fa-2x fa-print"></i></a>';
$value = '<a href="'.$print_url.'" target="_blank"><i class="fa fa-2x fa-print"></i></a>';
}
// Icona
@ -186,7 +178,7 @@ if (!empty($result_query) && $result_query != 'menu' && $result_query != 'custom
// Colore del testo
if (!empty($column['data-background'])) {
$column['data-color'] = $column['data-color'] ?: color_inverse($column['data-background']);
$column['data-color'] = isset($column['data-color']) ? $column['data-color'] : color_inverse($column['data-background']);
}
// Link della colonna
@ -200,9 +192,14 @@ if (!empty($result_query) && $result_query != 'menu' && $result_query != 'custom
unset($id_plugin);
}
$column['data-link'] = $rootdir.'/'.(empty($id_plugin) ? '' : 'plugin_').'editor.php?id_module='.$id_module.'&id_record='.$id_record.(empty($id_plugin) ? '' : '&id_plugin='.$id_plugin.'&id_parent='.$id_parent).$hash;
// Link per i moduli
if (empty($id_plugin)) {
$column['data-link'] = $rootdir.'/editor.php?id_module='.$id_module.'&id_record='.$id_record.$hash;
}
// Link per i plugin
else {
$column['data-link'] = $rootdir.'/add.php?id_module='.$id_module.'&id_record='.$id_record.'&id_plugin='.$id_plugin.'&id_parent='.$id_parent.'&edit=1'.$hash;
if (!empty($id_plugin)) {
$column['data-type'] = 'dialog';
}
}
@ -222,4 +219,5 @@ if (!empty($result_query) && $result_query != 'menu' && $result_query != 'custom
}
}
echo json_encode($results);
$rows = json_encode($results);
echo $rows;

View File

@ -9,7 +9,7 @@ if (!isset($term)) {
Il risultato è in json
*/
$term = $get['term'];
$term = get('term');
$term = str_replace('/', '\\/', $term);
$results = AJAX::search($term);

View File

@ -20,6 +20,7 @@ session_write_close();
// Permesso di accesso all'API da ogni dispositivo
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, GET, PUT, DELETE, OPTIONS');
// Attenzione: al momento l'API permette la lettura di tutte le tabelle presenti nel database (non limitate a quelle del progetto)
@ -35,37 +36,57 @@ try {
switch ($method) {
// Richiesta PUT (modifica elementi)
case 'PUT':
$result = $api->update($request);
$response = $api->update($request);
break;
// Richiesta POST (creazione elementi)
case 'POST':
$result = $api->create($request);
$response = $api->create($request);
break;
// Richiesta GET (ottenimento elementi)
case 'GET':
// Risorsa specificata
if (count($request) > 1) {
$result = $api->retrieve($request);
$response = $api->retrieve($request);
}
// Risorsa non specificata (lista delle risorse disponibili)
else {
$result = API::response(API::getResources()['retrieve']);
$response = API::response([
'resources' => array_keys(API::getResources()['retrieve']),
]);
}
break;
// Richiesta DELETE (eliminazione elementi)
case 'DELETE':
$result = $api->delete($request);
$response = $api->delete($request);
break;
}
} catch (InvalidArgumentException $e) {
$result = API::error('unauthorized');
$response = API::error('unauthorized');
} catch (Exception $e) {
$result = API::error('serverError');
// Log dell'errore
$logger = logger();
$logger->addRecord(\Monolog\Logger::ERROR, $e);
$response = API::error('serverError');
}
// Richiesta OPTIONS (controllo da parte del dispositivo)
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
$response = API::error('ok');
}
json_decode($response);
// Impostazioni di Content-Type e Charset Header
if (json_last_error() == JSON_ERROR_NONE) {
header('Content-Type: application/json; charset=UTF-8');
} else {
header('Content-Type: text/plain; charset=UTF-8');
}
// Stampa dei risultati
echo $result;
echo $response;

View File

@ -37,7 +37,6 @@ a.disabled {
}
#progress {
width: 400px;
margin: auto;
position: relative;
display: none;
@ -307,6 +306,19 @@ span.form-control {
padding-right: 5px;
position: relative;
z-index: 1;
opacity: 0.4;
}
.close:focus, .close:hover{
opacity: 0.8;
}
.li-widget button.close:hover, .li-widget button.close:focus{
opacity: 0.3;
}
.li-widget button.close{
padding-right:5px;
opacity: 0.1;
}
@ -364,6 +376,7 @@ span.form-control {
.info-box-text {
white-space: normal;
overflow: auto;
font-size: 12px;
}
.colorpicker,
@ -425,7 +438,6 @@ span.form-control {
.sidebar-form {
border-radius: 3px;
border-color: 1px;
margin: 10px 10px;
}
@ -529,7 +541,7 @@ input.small-width {
}
}
@media screen and (max-width: 768px) {
@media screen and (max-width: 767px) {
.box-center,
.box-center-large {
width: 90%;
@ -580,25 +592,239 @@ input.small-width {
margin: 5%;
}
}
.callout a{
text-decoration: none;
.callout a {
text-decoration: none;
}
.dropdown-menu{
.dropdown-menu {
min-width: 130px;
}
/*fix per tabs editor */
.nav-tabs-custom > .nav-tabs > li{
border-top: 3px solid #ddd;
.nav-tabs-custom>.nav-tabs>li {
border-top: 3px solid #ddd;
}
.nav-tabs-custom > .nav-tabs.pull-right > li:first-of-type.active > a{
border-right-color: #f4f4f4;
border-right-width : 1px;
border-left-width : 0px;
.nav-tabs-custom>.nav-tabs.pull-right>li:first-of-type.active>a {
border-right-color: #f4f4f4;
border-right-width: 1px;
border-left-width: 0px;
}
.nav-tabs-custom > .nav-tabs > li.header{
padding:0px;
.nav-tabs-custom>.nav-tabs>li.header {
padding: 0px;
}
.nav-tabs-custom>.nav-tabs>li {
margin-bottom: -1px;
margin-right: 0px;
}
.ui-autocomplete {
background: #f6f6f6;
border-color: #ccc;
border-color: rgba(0, 0, 0, 0.2);
-webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
-moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
-webkit-background-clip: padding-box;
-moz-background-clip: padding;
background-clip: padding-box;
}
.parsley-errors-list {
color: red;
}
/** TIMELINE */
.timeline {
list-style: none;
padding: 20px 0 20px;
position: relative;
}
.timeline:before {
top: 0;
bottom: 0;
position: absolute;
content: " ";
width: 3px;
background-color: #eeeeee;
left: 50%;
margin-left: -4.5px;
}
.timeline>li {
margin-bottom: 20px;
position: relative;
}
.timeline>li:before,
.timeline>li:after {
content: " ";
display: table;
}
.timeline>li:after {
clear: both;
}
.timeline>li:before,
.timeline>li:after {
content: " ";
display: table;
}
.timeline>li:after {
clear: both;
}
.timeline>li>.timeline-panel {
width: 50%;
float: left;
border: 1px solid #d4d4d4;
border-radius: 2px;
padding: 20px;
position: relative;
-webkit-box-shadow: 0 1px 6px rgba(0, 0, 0, 0.175);
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.175);
}
.timeline>li.timeline-inverted+li:not(.timeline-inverted),
.timeline>li:not(.timeline-inverted)+li.timeline-inverted {
margin-top: -60px;
}
.timeline>li:not(.timeline-inverted) {
padding-right: 90px;
}
.timeline>li.timeline-inverted {
padding-left: 90px;
}
.timeline>li>.timeline-panel:before {
position: absolute;
top: 26px;
right: -15px;
display: inline-block;
border-top: 15px solid transparent;
border-left: 15px solid #ccc;
border-right: 0 solid #ccc;
border-bottom: 15px solid transparent;
content: " ";
}
.timeline>li>.timeline-panel:after {
position: absolute;
top: 27px;
right: -14px;
display: inline-block;
border-top: 14px solid transparent;
border-left: 14px solid #fff;
border-right: 0 solid #fff;
border-bottom: 14px solid transparent;
content: " ";
}
.timeline>li>.timeline-badge {
color: #fff;
width: 50px;
height: 50px;
line-height: 50px;
font-size: 1.4em;
text-align: center;
position: absolute;
top: 16px;
left: 50%;
margin-left: -25px;
background-color: #999999;
z-index: 100;
border-top-right-radius: 50%;
border-top-left-radius: 50%;
border-bottom-right-radius: 50%;
border-bottom-left-radius: 50%;
}
.timeline>li.timeline-inverted>.timeline-panel {
float: right;
}
.timeline>li.timeline-inverted>.timeline-panel:before {
border-left-width: 0;
border-right-width: 15px;
left: -15px;
right: auto;
}
.timeline>li.timeline-inverted>.timeline-panel:after {
border-left-width: 0;
border-right-width: 14px;
left: -14px;
right: auto;
}
.timeline-badge.primary {
background-color: #2e6da4 !important;
}
.timeline-badge.success {
background-color: #3f903f !important;
}
.timeline-badge.warning {
background-color: #f0ad4e !important;
}
.timeline-badge.danger {
background-color: #d9534f !important;
}
.timeline-badge.info {
background-color: #5bc0de !important;
}
.timeline-title {
margin-top: 0;
color: inherit;
}
.timeline-body>p,
.timeline-body>ul {
margin-bottom: 0;
}
.timeline-body>p+p {
margin-top: 5px;
}
.img-circle.square{
box-shadow: 0px 0px 1px 1px lightgray;
}
.select2-dropdown{
z-index: 2000;
}
.after.input-group-addon .select2-selection, .after.input-group-addon{
border-radius:0px 4px 4px 0px;
border-left:none;
}
.before.input-group-addon .select2-selection, .before.input-group-addon{
border-radius:4px 0px 0px 4px;
border-right:none;
}
.hide-it-off-screen {
position: absolute;
left: -10000px;
top: -10000px;
}
.box-info ul > li > :last-child{
margin-bottom:5px;
}
.nav-tabs-custom > .nav-tabs > li{
margin-bottom:-1px;
margin-right: 0px;
}

View File

@ -55,7 +55,8 @@
.skin-default .main-header .navbar .nav .open a:hover,
.skin-default .main-header .navbar .nav .open a:focus,
.skin-default .main-header .navbar .nav .active a,
.skin-default .main-header .navbar .nav .actual a {
.skin-default .main-header .navbar .nav .actual a,
.skin-default .main-header .navbar .nav .menu-open a {
background: rgba(0, 0, 0, 0.2);
color: #f6f6f6;
}
@ -132,7 +133,8 @@
.skin-default .sidebar-menu li:hover a,
.skin-default .sidebar-menu li.active a,
.skin-default .sidebar-menu li.actual a {
.skin-default .sidebar-menu li.actual a,
.skin-default .sidebar-menu li.menu-open a {
background: #222;
border-left-color: #222;
}
@ -195,20 +197,4 @@
.skin-default .panel-primary .panel-heading {
border-bottom: 2px solid #57a;
}
.skin-default .ui-autocomplete {
background: #f6f6f6;
border-color: #ccc;
border-color: rgba(0, 0, 0, 0.2);
-webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
-moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
-webkit-background-clip: padding-box;
-moz-background-clip: padding;
background-clip: padding-box;
}
.skin-default .parsley-errors-list{
color: red;
}
}

View File

@ -1,44 +1,8 @@
$(document).ready(function () {
// Fix per il menu principale
var animationSpeed = 500;
$(document).off('click', '.sidebar li a')
.on('click', '.sidebar li a', function (e) {
//Get the clicked link and the next element
var $this = $(this);
var checkElement = $this.next();
//Check if the next element is a menu and is visible
if ((checkElement.is('.treeview-menu')) && (checkElement.is(':visible')) && (!$('body').hasClass('sidebar-collapse'))) {
//Close the menu
checkElement.slideUp(animationSpeed, function () {
checkElement.removeClass('menu-open');
});
checkElement.parent("li").removeClass("active");
}
//If the menu is not visible
else if ((checkElement.is('.treeview-menu')) && (!checkElement.is(':visible'))) {
//Get the parent menu
var parent = $this.parents('ul').first();
//Close all open menus within the parent
var ul = parent.find('ul:visible').slideUp(animationSpeed);
//Remove the menu-open class from the parent
ul.removeClass('menu-open');
//Get the parent li
var parent_li = $this.parent("li");
//Open the target menu and add the menu-open class
checkElement.slideDown(animationSpeed, function () {
//Add the class active to the parent li
checkElement.addClass('menu-open');
parent.find('li.active').removeClass('active');
parent_li.addClass('active');
});
}
//if this isn't a link, prevent the page from being redirected
if (checkElement.is('.treeview-menu') && $(e.target).is('.pull-right-container')) {
e.preventDefault();
}
});
$('.sidebar-menu').tree({
followLink: true,
});
// Pulsante per il ritorno a inizio pagina
var slideToTop = $("<div />");
@ -85,4 +49,11 @@ $(document).ready(function () {
scrollTop: 0
}, 500);
});
$(".sidebar-toggle").click(function(){
setTimeout(function(){
window.dispatchEvent(new Event('resize'));
}, 350);
});
});

44
bug.php
View File

@ -18,18 +18,18 @@ if (filter('op') == 'send') {
$mail->Subject = 'Segnalazione bug OSM '.$version;
// Aggiunta dei file di log (facoltativo)
if (!empty($post['log']) && file_exists($docroot.'/logs/error.log')) {
if (!empty(post('log')) && file_exists($docroot.'/logs/error.log')) {
$mail->AddAttachment($docroot.'/logs/error.log');
}
// Aggiunta della copia del database (facoltativo)
if (!empty($post['sql'])) {
if (!empty(post('sql'))) {
$backup_file = $docroot.'/Backup OSM '.date('Y-m-d').' '.date('H_i_s').'.sql';
Backup::database($backup_file);
$mail->AddAttachment($backup_file);
$_SESSION['infos'][] = tr('Backup del database eseguito ed allegato correttamente!');
flash()->info(tr('Backup del database eseguito ed allegato correttamente!'));
}
// Aggiunta delle informazioni di base sull'installazione
@ -41,12 +41,12 @@ if (filter('op') == 'send') {
];
// Aggiunta delle informazioni sul sistema (facoltativo)
if (!empty($post['info'])) {
if (!empty(post('info'))) {
$infos['Sistema'] = $_SERVER['HTTP_USER_AGENT'].' - '.getOS();
}
// Completamento del body
$body = $post['body'].'<hr>';
$body = post('body').'<hr>';
foreach ($infos as $key => $value) {
$body .= '<p>'.$key.': '.$value.'</p>';
}
@ -57,13 +57,13 @@ if (filter('op') == 'send') {
// Invio mail
if (!$mail->send()) {
$_SESSION['errors'][] = tr("Errore durante l'invio della segnalazione").': '.$mail->ErrorInfo;
flash()->error(tr("Errore durante l'invio della segnalazione").': '.$mail->ErrorInfo);
} else {
$_SESSION['infos'][] = tr('Email inviata correttamente!');
flash()->info(tr('Email inviata correttamente!'));
}
// Rimozione del dump del database
if (!empty($post['sql'])) {
if (!empty(post('sql'))) {
delete($backup_file);
}
@ -72,13 +72,8 @@ if (filter('op') == 'send') {
}
$pageTitle = tr('Bug');
$jscript_modules[] = App::getPaths()['js'].'/ckeditor/ckeditor.js';
if (file_exists($docroot.'/include/custom/top.php')) {
include $docroot.'/include/custom/top.php';
} else {
include $docroot.'/include/top.php';
}
include_once App::filepath('include|custom|', 'top.php');
if (empty($mail['from_address']) || empty($mail['server'])) {
echo '
@ -137,7 +132,7 @@ echo '
<div class="clearfix"></div>
<br>
{[ "type": "textarea", "label": "'.tr('Descrizione del bug').'", "name": "body" ]}
{[ "type": "ckeditor", "label": "'.tr('Descrizione del bug').'", "name": "body" ]}
<!-- PULSANTI -->
<div class="row">
@ -156,7 +151,7 @@ echo '
var html = "<p>'.tr('Se hai riscontrato un bug ricordati di specificare').':</p>" +
"<ul>" +
"<li>'.tr('Modulo esatto (o pagina relativa) in cui questi si è verificato').';</li>" +
"<li>'.tr('Dopo quali specifiche operazioni hai notato il malfunzionameto').'.</li>" +
"<li>'.tr('Dopo quali specifiche operazioni hai notato il malfunzionamento').'.</li>" +
"</ul>" +
"<p>'.tr('Assicurati inoltre di controllare che il checkbox relativo ai file di log sia contrassegnato, oppure riporta qui l\'errore visualizzato').'.</p>" +
"<p>'.tr('Ti ringraziamo per il tuo contributo').',<br>" +
@ -164,13 +159,6 @@ echo '
var firstFocus = 1;
CKEDITOR.replace("body", {
toolbar: globals.ckeditorToolbar,
language: globals.locale,
scayt_autoStartup: true,
scayt_sLang: globals.full_locale
});
CKEDITOR.instances.body.on("key", function() {
setTimeout(function(){
if(CKEDITOR.instances.body.getData() == ""){
@ -191,10 +179,8 @@ echo '
}
});
});
</script>';
</script>
if (file_exists($docroot.'/include/custom/bottom.php')) {
include $docroot.'/include/custom/bottom.php';
} else {
include $docroot.'/include/bottom.php';
}
<script type="text/javascript" charset="utf-8" src="'.App::getPaths()['js'].'/ckeditor/ckeditor.js'.'"></script>';
include_once App::filepath('include|custom|', 'bottom.php');

View File

@ -1,22 +0,0 @@
<?php
include_once __DIR__.'/core.php';
$posizione = $id_module;
if (isset($id_record)) {
$posizione .= ', '.$id_record;
}
$dbo->query('UPDATE zz_semaphores SET updated = NOW() WHERE id_utente = '.prepare($_SESSION['id_utente']).' AND posizione = '.prepare($posizione));
$dbo->query('DELETE FROM zz_semaphores WHERE DATE_ADD(updated, INTERVAL '.(get_var('Timeout notifica di presenza (minuti)') * 2).' SECOND) <= NOW()');
$datas = $dbo->fetchArray('SELECT DISTINCT username FROM zz_semaphores INNER JOIN zz_users ON zz_semaphores.id_utente=zz_users.id WHERE zz_semaphores.id_utente != '.prepare($_SESSION['id_utente']).' AND posizione = '.prepare($posizione));
$result = [];
foreach ($datas as $data) {
$result[] = [
'username' => $data['username'],
];
}
echo json_encode($result);

13
codeception.dist.yml Normal file
View File

@ -0,0 +1,13 @@
paths:
tests: tests
output: tests/_output
data: tests/_data
support: tests/_support
envs: tests/_envs
settings:
bootstrap: _bootstrap.php
error_level: E_ALL & ~E_WARNING & ~E_NOTICE & ~E_USER_DEPRECATED
actor_suffix: Tester
extensions:
enabled:
- Codeception\Extension\RunFailed

View File

@ -1,5 +1,5 @@
{
"name": "devcode/openstamanager",
"name": "devcode-it/openstamanager",
"description": "Gestionale open-source per assistenza tecnica e fatturazione",
"license": "GPL-3.0",
"keywords": [
@ -22,27 +22,45 @@
"require": {
"php": ">=5.6",
"ext-mbstring": "*",
"ext-pdo_mysql": "*",
"ext-json": "*",
"ext-pdo": "*",
"ext-zip": "*",
"ext-intl": "*",
"ext-simplexml": "*",
"ext-libxml": "*",
"ext-dom": "*",
"ext-curl": "*",
"ext-openssl": "*",
"ext-xsl": "*",
"aluguest/ical-easy-reader": "^1.5",
"danielstjules/stringy": "^3.1",
"davidepastore/codice-fiscale": "^0.4.0",
"ezyang/htmlpurifier": "^4.8",
"filp/whoops": "^2.1",
"guzzlehttp/guzzle": "^6.3",
"ifsnop/mysqldump-php": "^2.3",
"illuminate/database": "~5.4.0",
"intervention/image": "^2.3",
"ircmaxell/password-compat": "^1.0",
"league/csv": "^8.2",
"maximebf/debugbar": "^1.15",
"monolog/monolog": "^1.22",
"mpdf/mpdf": "^7.0",
"paragonie/random_compat": "^2.0",
"mpdf/mpdf": "^7.1",
"mpociot/vat-calculator": "^2.3",
"owasp/csrf-protector-php": "^1.0",
"phpmailer/phpmailer": "^6.0",
"respect/validation": "^1.1",
"servo/fluidxml": "^1.21",
"slim/flash": "^0.4.0",
"spipu/html2pdf": "^5.0",
"symfony/filesystem": "^3.3",
"symfony/finder": "^3.3",
"symfony/polyfill-ctype": "^1.8",
"symfony/polyfill-php70": "^1.8",
"symfony/translation": "^3.3",
"symfony/var-dumper": "^3.3"
},
"require-dev": {
"codeception/codeception": "2.4.*",
"friendsofphp/php-cs-fixer": "^2.10"
},
"autoload": {
@ -51,6 +69,8 @@
},
"files": [
"lib/functions.php",
"lib/common.php",
"lib/helpers.php",
"lib/util.php",
"lib/deprecated.php"
]
@ -59,6 +79,9 @@
"sort-packages": true,
"optimize-autoloader": true,
"apcu-autoloader": true,
"prefer-stable": true
"prefer-stable": true,
"platform": {
"php": "5.6.4"
}
}
}

View File

@ -13,12 +13,12 @@ $backup_dir = __DIR__.'/backup/';
// Tema selezionato per il front-end
$theme = 'default';
// Redirect automatico delle richieste da HTTP a HTTPS
$redirectHTTPS = false;
// Impostazioni di sicurezza
$redirectHTTPS = false; // Redirect automatico delle richieste da HTTP a HTTPS
$disableCSRF = true; // Protezione contro CSRF
// Impostazioni di debug
$debug = false;
$operations_log = false;
// Personalizzazione dei gestori dei tag personalizzati
$HTMLWrapper = null;
@ -41,5 +41,6 @@ $formatter = [
// Ulteriori file CSS e JS da includere
$assets = [
'css' => [],
'print' => [],
'js' => [],
];

21
config/csrf_config.php Normal file
View File

@ -0,0 +1,21 @@
<?php
/**
* Configuration file for CSRF Protector.
*/
return [
'logDirectory' => DOCROOT.'/logs',
'failedAuthAction' => [
'GET' => 0,
'POST' => 0,
],
'jsUrl' => ROOTDIR.'/assets/dist/js/csrf/csrfprotector.js',
'tokenLength' => 10,
'cookieConfig' => [
'path' => ROOTDIR,
'secure' => isHTTPS(true),
],
'verifyGetFor' => [],
'CSRFP_TOKEN' => '',
'disabledJavascriptMessage' => '',
];

17
config/namespaces.php Normal file
View File

@ -0,0 +1,17 @@
<?php
return [
'include' => 'Common',
'modules/anagrafiche' => 'Modules\Anagrafiche',
'modules/articoli' => 'Modules\Articoli',
'modules/ritenute' => 'Modules\Ritenute',
'modules/iva' => 'Modules\Iva',
'modules/ddt' => 'Modules\DDT',
'modules/fatture' => 'Modules\Fatture',
'modules/ordini' => 'Modules\Ordini',
'modules/preventivi' => 'Modules\Preventivi',
'modules/interventi' => 'Modules\Interventi',
'plugins/exportFE' => 'Plugins\ExportFE',
'plugins/importFE' => 'Plugins\ImportFE',
'plugins/receiptFE' => 'Plugins\ReceiptFE',
];

View File

@ -8,22 +8,10 @@ if (!empty($id_record) && !empty($id_module)) {
redirect(ROOTDIR.'/index.php');
}
if (file_exists($docroot.'/include/custom/top.php')) {
include $docroot.'/include/custom/top.php';
} else {
include $docroot.'/include/top.php';
}
include_once App::filepath('include|custom|', 'top.php');
// Lettura parametri iniziali del modulo
$module = Modules::get($id_module);
if (empty($module) || empty($module['enabled'])) {
die(tr('Accesso negato'));
}
$module_dir = $module['directory'];
include $docroot.'/actions.php';
// Inclusione gli elementi fondamentali
include_once $docroot.'/actions.php';
// Widget in alto
echo '{( "name": "widgets", "id_module": "'.$id_module.'", "position": "top", "place": "controller" )}';
@ -32,24 +20,23 @@ echo '{( "name": "widgets", "id_module": "'.$id_module.'", "position": "top", "p
echo '
<div class="nav-tabs-custom">
<ul class="nav nav-tabs pull-right" id="tabs" role="tablist">
<li class="pull-left active header">';
// Verifico se ho impostato un nome modulo personalizzato
$name = $module['title'];
echo '
<li class="pull-left active header">
<a data-toggle="tab" href="#tab_0">
<i class="'.$module['icon'].'"></i> '.$name;
<i class="'.$structure['icon'].'"></i> '.$structure['title'];
// Pulsante "Aggiungi" solo se il modulo è di tipo "table" e se esiste il template per la popup
if (file_exists($docroot.'/modules/'.$module_dir.'/add.php') && $module['permessi'] == 'rw') {
if ($structure->hasAddFile() && $structure->permission == 'rw') {
echo '
<button type="button" class="btn btn-primary" data-toggle="modal" data-title="'.tr('Aggiungi').'..." data-target="#bs-popup" data-href="add.php?id_module='.$id_module.'"><i class="fa fa-plus"></i></button>';
<button type="button" class="btn btn-primary" data-toggle="modal" data-title="'.tr('Aggiungi').'..." data-href="add.php?id_module='.$id_module.'&id_plugin='.$id_plugin.'"><i class="fa fa-plus"></i></button>';
}
echo '
</a>
</li>';
$plugins = $dbo->fetchArray('SELECT id, title FROM zz_plugins WHERE idmodule_to='.prepare($id_module)." AND position='tab_main' AND enabled = 1");
// Tab dei plugin
foreach ($plugins as $plugin) {
echo '
<li>
@ -67,8 +54,11 @@ include $docroot.'/include/manager.php';
echo '
</div>';
// Inclusione contenuti varie tab dei plugin
// Plugin
$module_record = $record;
foreach ($plugins as $plugin) {
$record = $module_record;
echo '
<div id="tab_'.$plugin['id'].'" class="tab-pane">';
@ -80,17 +70,15 @@ foreach ($plugins as $plugin) {
</div>';
}
$record = $module_record;
echo '
</div>
</div>';
redirectOperation($id_module, $id_record);
redirectOperation($id_module, isset($id_parent) ? $id_parent : $id_record);
// Widget in basso
echo '{( "name": "widgets", "id_module": "'.$id_module.'", "position": "right", "place": "controller" )}';
if (file_exists($docroot.'/include/custom/bottom.php')) {
include $docroot.'/include/custom/bottom.php';
} else {
include $docroot.'/include/bottom.php';
}
include_once App::filepath('include|custom|', 'bottom.php');

193
core.php
View File

@ -1,8 +1,7 @@
<?php
// Impostazioni per la corretta interpretazione di UTF-8
header('Content-Type: text/html; charset=UTF-8');
ob_start();
// Rimozione header X-Powered-By
header_remove('X-Powered-By');
// Impostazioni di configurazione PHP
date_default_timezone_set('Europe/Rome');
@ -23,7 +22,28 @@ if (file_exists(__DIR__.'/config.inc.php')) {
}
// Caricamento delle dipendenze e delle librerie del progetto
require_once __DIR__.'/vendor/autoload.php';
$loader = require_once __DIR__.'/vendor/autoload.php';
$namespaces = require_once __DIR__.'/config/namespaces.php';
foreach ($namespaces as $path => $namespace) {
$loader->addPsr4($namespace.'\\', __DIR__.'/'.$path.'/custom/src');
$loader->addPsr4($namespace.'\\', __DIR__.'/'.$path.'/src');
}
// Inclusione dei file modutil.php
$files = glob(__DIR__.'/{modules,plugins}/*/modutil.php', GLOB_BRACE);
$custom_files = glob(__DIR__.'/{modules,plugins}/*/custom/modutil.php', GLOB_BRACE);
foreach ($custom_files as $key => $value) {
$index = array_search(str_replace('custom/', '', $value), $files);
if ($index !== false) {
unset($files[$index]);
}
}
$list = array_merge($files, $custom_files);
foreach ($list as $file) {
include_once $file;
}
// Individuazione dei percorsi di base
App::definePaths(__DIR__);
@ -32,16 +52,19 @@ $docroot = DOCROOT;
$rootdir = ROOTDIR;
$baseurl = BASEURL;
$config = App::getConfig();
// Redirect al percorso HTTPS se impostato nella configurazione
if (!empty($redirectHTTPS) && !isHTTPS(true)) {
if (!empty($config['redirectHTTPS']) && !isHTTPS(true)) {
header('HTTP/1.1 301 Moved Permanently');
header('Location: https://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']);
exit();
}
// Forzamento del debug
// $debug = true;
// Forza l'abilitazione del debug
// $debug = App::debug(true);
/* GESTIONE DEGLI ERRORI */
// Logger per la segnalazione degli errori
$logger = new Monolog\Logger('Logs');
$logger->pushProcessor(new Monolog\Processor\UidProcessor());
@ -50,9 +73,9 @@ $logger->pushProcessor(new Monolog\Processor\WebProcessor());
// Registrazione globale del logger
Monolog\Registry::addLogger($logger, 'logs');
use Monolog\Handler\StreamHandler;
use Monolog\Handler\FilterHandler;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Handler\StreamHandler;
$handlers = [];
if (!API::isAPIRequest()) {
@ -60,13 +83,10 @@ if (!API::isAPIRequest()) {
$handlers[] = new StreamHandler($docroot.'/logs/error.log', Monolog\Logger::ERROR);
$handlers[] = new StreamHandler($docroot.'/logs/setup.log', Monolog\Logger::EMERGENCY);
// Impostazione dei log estesi (per monitorare in modo completo le azioni degli utenti)
$handlers[] = new StreamHandler($docroot.'/logs/info.log', Monolog\Logger::INFO);
// Impostazioni di debug
if (!empty($debug)) {
// Ignoramento degli avvertimenti e delle informazioni relative alla deprecazione di componenti
error_reporting(E_ALL & ~E_WARNING & ~E_NOTICE & ~E_USER_DEPRECATED);
if (App::debug()) {
// Ignora gli avvertimenti e le informazioni relative alla deprecazione di componenti
error_reporting(E_ALL & ~E_WARNING & ~E_NOTICE & ~E_USER_DEPRECATED & ~E_STRICT);
// File di log ordinato in base alla data
$handlers[] = new RotatingFileHandler($docroot.'/logs/error.log', 0, Monolog\Logger::ERROR);
@ -90,12 +110,24 @@ if (!API::isAPIRequest()) {
}
// Disabilita la segnalazione degli errori (se il debug è disabilitato)
if (empty($debug)) {
if (!App::debug()) {
error_reporting(0);
ini_set('display_errors', 0);
}
// Imposta il formato di salvataggio dei log
$monologFormatter = new Monolog\Formatter\LineFormatter('[%datetime%] %channel%.%level_name%: %message%'.PHP_EOL.'%extra% '.PHP_EOL);
$pattern = '[%datetime%] %channel%.%level_name%: %message%';
if (App::debug()) {
$pattern .= ' %context%';
}
$pattern .= PHP_EOL.'%extra% '.PHP_EOL;
$monologFormatter = new Monolog\Formatter\LineFormatter($pattern);
if (App::debug()) {
$monologFormatter->includeStacktraces(true);
}
foreach ($handlers as $handler) {
$handler->setFormatter($monologFormatter);
$logger->pushHandler(new FilterHandler($handler, [$handler->getLevel()]));
@ -104,10 +136,38 @@ foreach ($handlers as $handler) {
// Imposta Monolog come gestore degli errori
Monolog\ErrorHandler::register($logger);
// Database
$dbo = $database = database();
/* SESSIONE */
if (!API::isAPIRequest()) {
// Sicurezza della sessioni
ini_set('session.use_trans_sid', '0');
ini_set('session.use_only_cookies', '1');
session_set_cookie_params(0, $rootdir, null, isHTTPS(true));
session_start();
// Barra di debug (necessario per loggare tutte le query)
if (App::debug()) {
$debugbar = new DebugBar\DebugBar();
$debugbar->addCollector(new DebugBar\DataCollector\MemoryCollector());
$debugbar->addCollector(new DebugBar\DataCollector\PhpInfoCollector());
$debugbar->addCollector(new DebugBar\DataCollector\RequestDataCollector());
$debugbar->addCollector(new DebugBar\DataCollector\TimeDataCollector());
$debugbar->addCollector(new DebugBar\Bridge\MonologCollector($logger));
$debugbar->addCollector(new Extensions\EloquentCollector($dbo->getCapsule()));
}
}
/* INTERNAZIONALIZZAZIONE */
// Istanziamento del gestore delle traduzioni del progetto
$lang = !empty($lang) ? $lang : 'it';
$formatter = !empty($formatter) ? $formatter : [];
$translator = Translator::getInstance();
$lang = !empty($config['lang']) ? $config['lang'] : 'it';
$formatter = !empty($config['formatter']) ? $config['formatter'] : [];
$translator = trans();
$translator->addLocalePath($docroot.'/locale');
$translator->addLocalePath($docroot.'/modules/*/locale');
$translator->setLocale($lang, $formatter);
@ -116,14 +176,7 @@ $translator->setLocale($lang, $formatter);
$version = Update::getVersion();
$revision = Update::getRevision();
// Inizializzazione della sessione
if (!API::isAPIRequest()) {
session_set_cookie_params(0, $rootdir);
session_start();
}
$dbo = Database::getConnection();
/* ACCESSO E INSTALLAZIONE */
// Controllo sulla presenza dei permessi di accesso basilari
$continue = $dbo->isInstalled() && !Update::isUpdateAvailable() && (Auth::check() || API::isAPIRequest());
@ -140,81 +193,87 @@ if (!$continue && getURLPath() != slashes(ROOTDIR.'/index.php') && !Permissions:
exit();
}
/* INIZIALIZZAZIONE GENERALE */
// Operazione aggiuntive (richieste non API)
if (!API::isAPIRequest()) {
/*
// Impostazioni di Content-Type e Charset Header
header('Content-Type: text/html; charset=UTF-8');
// Controllo CSRF
if(!CSRF::getInstance()->validate()){
die(tr('Constrollo CSRF fallito!'));
}*/
if (empty($config['disableCSRF'])) {
csrfProtector::init();
}
// Aggiunta del wrapper personalizzato per la generazione degli input
if (!empty($HTMLWrapper)) {
HTMLBuilder\HTMLBuilder::setWrapper($HTMLWrapper);
if (!empty($config['HTMLWrapper'])) {
HTMLBuilder\HTMLBuilder::setWrapper($config['HTMLWrapper']);
}
// Aggiunta dei gestori personalizzati per la generazione degli input
foreach ((array) $HTMLHandlers as $key => $value) {
foreach ((array) $config['HTMLHandlers'] as $key => $value) {
HTMLBuilder\HTMLBuilder::setHandler($key, $value);
}
// Aggiunta dei gestori per componenti personalizzate
foreach ((array) $HTMLManagers as $key => $value) {
foreach ((array) $config['HTMLManagers'] as $key => $value) {
HTMLBuilder\HTMLBuilder::setManager($key, $value);
}
// Registrazione globale del template per gli input HTML
register_shutdown_function('translateTemplate');
ob_start();
// Impostazione della sessione di base
$_SESSION['infos'] = array_unique((array) $_SESSION['infos']);
$_SESSION['warnings'] = array_unique((array) $_SESSION['warnings']);
$_SESSION['errors'] = array_unique((array) $_SESSION['errors']);
// Imposto il periodo di visualizzazione dei record dal 01-01-yyy al 31-12-yyyy
if (!empty($_GET['period_start'])) {
$_SESSION['period_start'] = $_GET['period_start'];
$_SESSION['period_end'] = $_GET['period_end'];
} elseif (!isset($_SESSION['period_start'])) {
$_SESSION['period_start'] = date('Y').'-01-01';
$_SESSION['period_end'] = date('Y').'-12-31';
}
// Retrocompatibilità
$_SESSION['infos'] = isset($_SESSION['infos']) ? array_unique($_SESSION['infos']) : [];
$_SESSION['warnings'] = isset($_SESSION['warnings']) ? array_unique($_SESSION['warnings']) : [];
$_SESSION['errors'] = isset($_SESSION['errors']) ? array_unique($_SESSION['errors']) : [];
// Impostazione del tema grafico di default
$theme = !empty($theme) ? $theme : 'default';
$assets = App::getAssets();
// CSS di base del progetto
$css_modules = $assets['css'];
// JS di base del progetto
$jscript_modules = $assets['js'];
$theme = !empty($config['theme']) ? $config['theme'] : 'default';
if ($continue) {
$id_module = filter('id_module');
// Periodo di visualizzazione dei record
// Personalizzato
if (!empty($_GET['period_start'])) {
$_SESSION['period_start'] = $_GET['period_start'];
$_SESSION['period_end'] = $_GET['period_end'];
}
// Dal 01-01-yyy al 31-12-yyyy
elseif (!isset($_SESSION['period_start'])) {
$_SESSION['period_start'] = date('Y').'-01-01';
$_SESSION['period_end'] = date('Y').'-12-31';
}
$id_record = filter('id_record');
$id_plugin = filter('id_plugin');
$id_parent = filter('id_parent');
Modules::setCurrent(filter('id_module'));
Plugins::setCurrent(filter('id_plugin'));
// Variabili fondamentali
$module = Modules::getCurrent();
$plugin = Plugins::getCurrent();
$structure = isset($plugin) ? $plugin : $module;
$id_module = $module['id'];
$id_plugin = $plugin['id'];
$user = Auth::user();
if (!empty($id_module)) {
$module = Modules::get($id_module);
$pageTitle = $module['title'];
// Segmenti
if (!isset($_SESSION['module_'.$id_module]['id_segment'])) {
$segments = Modules::getSegments($id_module);
$_SESSION['module_'.$id_module]['id_segment'] = isset($segments[0]['id']) ? $segments[0]['id'] : null;
}
Permissions::addModule($id_module);
}
if (!empty($skip_permissions)) {
Permissions::skip();
}
Permissions::check();
}
// Variabili GET e POST
// Retrocompatibilità
$post = Filter::getPOST();
$get = Filter::getGET();
}

View File

@ -1,87 +0,0 @@
template:
url: https://github.com/pnowy/CouscousNativeTemplate
include:
- docs
branch: gh-pages
baseUrl: .
github:
user: devcode-it
repo: openstamanager
title: OpenSTAManager
subTitle: Il software gestionale open-source per l'assistenza tecnica e la fatturazione
fontAwesomeIcon: fa fa-cog
footerText: 'Progettato e sviluppato da <a href="http://devcode.it/">DevCode</a>.'
googleAnalyticsCode: UA-42808312-1
scripts:
after:
- php sami.phar update .sami
# The left menu bar
menu:
sections:
main:
name: Introduzione
items:
home:
text: Home
relativeUrl: index.html
installazione:
text: Installazione
relativeUrl: installazione.html
aggiornamento:
text: Aggiornamento
relativeUrl: aggiornamento.html
api:
text: API
relativeUrl: api.html
contribuire:
text: Contribuire
relativeUrl: contributing.html
structure:
name: Approfondimenti
items:
struttura:
text: Struttura
relativeUrl: struttura.html
moduli:
text: Moduli
relativeUrl: moduli.html
upload:
text: Gestione degli upload
relativeUrl: upload.html
widget:
text: Plugin
relativeUrl: widget.html
stampe:
text: Stampe
relativeUrl: stampe.html
widget:
text: Widget
relativeUrl: widget.html
custom:
name: Personalizzazione
items:
framework:
text: Framework
relativeUrl: framework.html
assets:
text: Assets
relativeUrl: assets.html
more:
name: Link utili
items:
docs:
text: Documentazione
relativeUrl: docs/
osm:
text: Sito web
absoluteUrl: http://www.openstamanager.com
forum:
text: Forum
absoluteUrl: http://www.openstamanager.com/forum

View File

@ -1 +0,0 @@
Deny from all

View File

@ -1,152 +0,0 @@
---
currentMenu: api
---
# API
> Con application programming interface (in acronimo API, in italiano interfaccia di programmazione di un'applicazione), in informatica, si indica ogni insieme di procedure disponibili al programmatore, di solito raggruppate a formare un set di strumenti specifici per l'espletamento di un determinato compito all'interno di un certo programma.
>
> \-- <cite>[Wikipedia](https://it.wikipedia.org/wiki/Application_programming_interface)</cite>
L'API del progetto è attualmente ancora in sviluppo, e pertanto le funzioni disponibili potrebbero essere piuttosto ridotte.
Di seguito sono elencate le basi per connettersi al sistema e ottenere i dati a cui si è interessati.
<!-- TOC depthFrom:2 depthTo:6 orderedList:false updateOnSave:true withLinks:true -->
- [Requisiti aggiuntivi](#requisiti-aggiuntivi)
- [Standard di comunicazione](#standard-di-comunicazione)
- [Ottenere la chiave](#ottenere-la-chiave)
- [Formato dei componenti](#formato-dei-componenti)
- [Output](#output)
- [Stati](#stati)
- [Lettura](#lettura)
- [Personalizzazione](#personalizzazione)
- [Richieste di lettura](#richieste-di-lettura)
- [Interventi](#interventi)
- [Anagrafiche](#anagrafiche)
- [Richieste disabilitate](#richieste-disabilitate)
- [Modifiche](#modifiche)
- [Eliminazioni](#eliminazioni)
<!-- /TOC -->
## Requisiti aggiuntivi
Per rendere la gestione dell'API maggiormente mantenibile e unificata, il suo funzionamento è stato sottoposto al seguente requisito aggiuntivo:
- MySQL >= 5.6.5
Se queste requisito non viene soddisfatto, l'installazione del gestionale procederà correttamente, ma i tentativi di connessione con l'API saranno rifiutati con il codice di errore `503` e lo stato `Servizio non disponibile`.
Nel caso, a seguito dell'installazione di OpenSTAManager, venisse aggiornato il servizio MySQL per permettere il funzionamento dell'API, sarà necessaro causare l'esecuzione della procedura di aggiornamento del gestionale, che organizzarà correttamente il database per la compatibilità con l'API.
**Attenzione**: il solo aggiornamento del servizio MySQL senza il successivo aggiornamento del gestionale potrebbe causare malfunzionamenti di vario genere nell'utilizzo dell'API.
## Standard di comunicazione
Il funzionamento dell'API si basa fondamentalmente sull'utilizzo di una chiave di accesso, ospitata all'interno della tabella `zz_tokens` del database del progetto, univoca per ogni istanza dell'utente.
Ogni richiesta all'API deve contenere la chiave di accesso (campo `token`) e l'operazione richiesta (campo `resource`), inserendo questi elementi tra gli ulteriori contenuti che si intendono inviare.
I contenuti della richiesta devono quindi essere convertiti in formato JSON ed inviati all'API secondo uno specifico schema:
- `POST` - Richieste di creazione (**Create**).
- `GET` - Richieste di informazioni (**Retrieve**).
- `PUT` - Richieste di modifica (**Update**).
- `DELETE` - Richieste di eliminazione (**Delete**).
### Ottenere la chiave
La chiave di accesso può essere ottenuta sfruttando l'operazione di accesso nativa dell'API, che prevede l'invio di una richiesta **POST** corrispondente alla seguente struttura:
- `resource` - login.
- `username` - &lt;username dell'account>.
- `password` - &lt;password dell'account>.
### Formato dei componenti
I seguenti componenti delle richieste devono seguire una rigida struttura:
- `page` - intero.
- `upd` - yyyy-MM-dd hh:mm:ss.
## Output
L'API del progetto permette di ottenere le informazioni attraverso un array in formato JSON.
### Stati
Ogni richiesta effettuata all'API viene accompagnata da un messaggio predefinito che permette di interpretare in modo più preciso la risposta.
In particolare, sono presenti i seguenti _status_:
- `200: OK` - La richiesta è andata a buon fine.
- `400: Errore interno dell'API` - La richiesta effettuata risulta invalida per l'API.
- `401: Non autorizzato` - Accesso non autorizzato.
- `404: Non trovato` - La risorsa richiesta non risulta disponibile.
- `500: Errore del server` - Il gestionale non è in grado di completare la richiesta.
- `503: Servizio non disponibile` - L'API del gestionale non è abilitata.
### Lettura
Le richieste di lettura sono solitamente completate con l'invio di un numero predefinito di informazioni.
Per poter interpretare correttamente i dati, si devono ignorare gli indici numerici di primo livello (non rilevanti all'interno del formato) e sfruttare in particolare i seguenti campi generici:
- `records`, rappresentante il numero totale dei record richiesti;
- `pages`, indicante il numero totale della pagine disponibili.
Si ricorda che l'API prevede la restituzione di un insieme di dati limitato rispetto alla richiesta effettuatua: per ottenere l'intero insieme di informazioni è necessario eseguire molteplici richieste consecutive basate sul campo `page`.
## Personalizzazione
L'API sfrutta una struttura modulare per poter funzionare in modo completo e garantire al tempo stesso il possibile ampliamento delle sue funzioni.
In particolare, ogni modulo può specificare una determinata serie di operazioni che lo riguardano e che possono essere richiamate in vari modi.
Di seguito lo schema attraverso cui l'API individua la presenza delle possibili richieste supportate dai moduli (cartelle **api/** e **custom/api/**):
- `POST` - File `create.php`.
- `GET` - File `retrieve.php`.
- `PUT` - File `update.php`.
- `DELETE` - File `delete.php`.
## Richieste di lettura
### Interventi
Tutto il contenuto della tabella in_interventi:
http://<url_osm>/api/?token=<token>&resource=in_interventi
Singolo intervento (riga della tabella):
http://<url_osm>/api/?token=<token>&resource=in_interventi&filter[id]=[1]
### Anagrafiche
Tutto il contenuto della tabella an_anagrafiche:
http://<url_osm>/api/?token=<token>&resource=an_anagrafiche
Singolo intervento (riga della tabella):
http://<url_osm>/api/?token=<token>&resource=an_anagrafiche&filter[idanagrafica]=[1]
Ricerca per ragione sociale:
http://<url_osm>/api/?token=<token>&resource=an_anagrafiche&filter[ragione_sociale]=[%<stringa_ragione_sociale>%]
### Richieste disabilitate
#### Modifiche
Tutti i contenuti di tutte le tabelle:
http://<url_osm>/api/?token=<token>&resource=updates
Tutti i contenuti di tutte le tabelle, aggiornati a partire da una data precisa:
http://<url_osm>/api/?token=<token>&resource=updates&upd=2016-01-31%2010:44:31
#### Eliminazioni
Tutte le eliminazioni di tutte le tabelle:
http://<url_osm>/api/?token=<token>&resource=deleted

View File

@ -1,89 +0,0 @@
---
currentMenu: aggiornamento
---
# Aggiornamento
**Attenzione**: Questa documentazione è esclusivamente relativa all'aggiornamento del software. Per maggiori informazioni sull'installazione consultare la documentazione relativa nella sezione [Installazione](Installazione.md).
Esistono due procedure ufficiale per effettuare l'aggiornamento di OpenSTAManager in modo corretto: una semplificata (_consigliata_) e una manuale.
In ogni caso, il corretto procedimento prevede di [scaricare una release ufficiale del progetto](https://github.com/devcode-it/openstamanager/releases) ed **effettuare un backup della versione corrente** (comprensivo di file e database).
<!-- TOC depthFrom:2 depthTo:6 orderedList:false updateOnSave:true withLinks:true -->
- [Aggiornamento semplificato](#aggiornamento-semplificato)
- [Aggiornamento manuale](#aggiornamento-manuale)
- [Migrazione dalla versione 1.x](#migrazione-dalla-versione-1x)
- [Recupero della password](#recupero-della-password)
- [Account comune](#account-comune)
- [Account amministrativo](#account-amministrativo)
<!-- /TOC -->
## Aggiornamento semplificato
La procedura di aggiornamento semplificato ha l'obiettivo di fornire un sistema di facile utilizzo per favorire l'aggiornamento, e migliorare in questo modo l'interazione con l'utente finale.
L'utilizzo di questa procedura è però sottoposto alla seguenti condizioni nelle impostazioni PHP:
- `upload_max_filesize` >= 16MB
- `post_max_size` >= 16MB
Di seguito la procedura:
1. Accedere con un account amministrativo
2. Entrare nel modulo **Aggiornamenti** (disponibile nel menu principale a sinistra, eventualmente sotto la dicitura **Strumenti**)
3. Selezionare il file _.zip_ della release attraverso l'apposita sezione "Carica un aggiornamento" e cliccare sul pulsante "Carica"
Dopo l'esecuzione di queste azioni, il gestionale effettuerà automaticamente il logout di tutti gli utenti connessi e renderà disponibile l'interfaccia di aggiornamento.
## Aggiornamento manuale
La procedura di aggiornamento manuale è resa disponibile per ovviare ai problemi relativi al caricamento del file _.zip_ (in alcuni casi il file non viene correttamente rilevato, non sono disponibili i permessi per caricare file oppure la dimensione del file eccede il limite di upload sul server).
Di seguito la procedura:
1. De-comprimere il contenuto del file _.zip_ in una cartella temporanea
2. Rinominare il file `VERSION` dell'installazione corrente in `VERSION.old` (rispettando minuscole e maiuscole) [facoltativo a partire dalla versione 2.3]
3. Copiare i file della nuova versione dalla cartella temporanea alla cartella del server, in modo che le cartelle principali (`files`, `modules`, `templates`, ...) vengano sovrascritte
Dopo l'esecuzione di queste azioni, il gestionale effettuerà automaticamente il logout di tutti gli utenti connessi e renderà disponibile l'interfaccia di aggiornamento.
## Migrazione dalla versione 1.x
E' possibile effettuare la migrazione da una qualsiasi versione 1.x alla nuova 2.0, seguendo una procedura un po diversa dalle precedenti:
1. Scaricare la versione 2.0 per la migrazione da SourceForge ([openstamanager-2.0-migrazione.zip](https://sourceforge.net/projects/openstamanager/files/openstamanager/openstamanager-2.x/))
2. Creare un backup completo della versione in uso
1. De-comprimere il contenuto del file _.zip_ in una cartella temporanea
4. Effettuare le seguenti operazioni dal backup della precedente versione alla cartella della versione 2.0:
- Copiare il file `VERSION`, rinominandolo in `VERSION.old`
- Copiare il file `config.inc.php`
- Copiare la cartella `files/`
- Copiare i contenuti della cartella `/modules/magazzino/articoli/images/` in `/files/articoli/`
- Copiare la cartella `templates/` (mantenendo però i file `pdfgen.php` e `pdfgen_variables.php` di della versione 2.0)
Dopo l'esecuzione di queste azioni, il gestionale renderà disponibile l'interfaccia di aggiornamento.
**Attenzione**: le stampe di _Interventi_, _Riepilogo interventi_, _Contratti_ e _Preventivi_ potrebbero non essere compatibili per via dellaggiornamento degli orari di lavoro, perciò è possibile riscrivere solo la parte di calcolo ore o partire dal template nuovo e apportare le dovute modifiche.
## Recupero della password
Non esiste una procedura semplificata per permettere il recupero della password degli account di amministrazione (di default, _admin_) o di quelli comuni.
Si ricorda che è comunque possibile **cambiare** la password in ogni momento, se è stato effettuato l'accesso, attraverso l'utilizzo del modulo **Utenti e permessi** (**Gestione permessi** per versioni precedenti alla 2.3) disponibile sotto la dicitura **Strumenti**.
Può però essere necessario **reimpostare** la password, in particolare se è stata dimenticata, per ripristinare l'accesso ad OpenSTAManager.
### Account comune
Per procedere alla reimpostazione della password di un account comune (non amministrativo) è necessario accedere con un account amministrativo e utilizzare il modulo **Utenti e permessi** (**Gestione permessi** per versioni precedenti alla 2.3), disponibile sotto la dicitura **Strumenti**.
In particolare, una volta entrati nella corretta categoria di accesso (_Agenti_, _Amministratori_, _Clienti_, ...) dell'account da modificare, è possibile utilizzare la procedura semplificata di cambio password attraverso l'_icona del lucchetto aperto_.
Nel caso non sia possibile accedere con un account amministrativo, contattare l'amministratore.
### Account amministrativo
Per reimpostare la password di un account amministrativo è possibile procedere in due modi:
- Se esiste un altro account amministrativo, seguire la procedura precedente per gli account comuni;
- Accedere al database ed eseguire la seguente query:
```sql
UPDATE `zz_utenti` SET password = MD5('nuova_password') WHERE username = 'admin';
```

View File

@ -1,105 +0,0 @@
---
currentMenu: assets
---
# Assets
> Web assets are things like CSS, JavaScript and image files that make the frontend of your site look and work great.
>
> \-- <cite>[Symfony](http://symfony.com/doc/current/best_practices/web-assets.html)</cite>
Gli assets sono, allinterno dei moderni ambienti di sviluppo web, il cuore pulsante del software in relazione al layout e al livello di accessibilità; in particolare,il termine assets fa solitamente riferimento ai componenti di natura grafica di un software, quali immagini, fonts e icone, linguaggi di scripting client-side (JavaScript) e fogli di stile a cascata (_Cascading Style Sheets_).
Il progetto utilizza [Yarn](https://yarnpkg.com/) per gestire l'installazione e l'aggiornamento degli assets e [Gulp](http://gulpjs.com/) per compilarli e associarli con le personalizzazioni. Viene inoltre richiesta la presenza di [Git](https://git-scm.com/) asll'interno del sistema operativo.
<!-- TOC depthFrom:2 depthTo:6 orderedList:false updateOnSave:true withLinks:true -->
- [Struttura](#struttura)
- [Personalizzazione](#personalizzazione)
- [Tema grafico](#tema-grafico)
- [Aggiornamento e installazione pacchetti](#aggiornamento-e-installazione-pacchetti)
- [Compilazione](#compilazione)
- [Assets predefiniti](#assets-predefiniti)
<!-- /TOC -->
## Struttura
Yarn salva automaticamente gli assets da lui gestiti all'interno della cartella `node_modules`, non presente nella repository e nelle release del progetto per la sua natura estremamente variabile e facilmente riproducibile (tramite l'utilizzo dello strumento, come si vedrà in [Personalizzazione](#personalizzazione)).
Gli assets personalizzati del progetto sono al contrario contenuti all'interno della cartella `assets/src`; parallelamente, gli assets utilizzati direttamente dal progetto sono infine contenuti all'interno della cartella `assets/dist`, generata in automatico tramite l'utilizzo di [Gulp](http://gulpjs.com/).
## Personalizzazione
L'introduzione di una gestione automatizzata rende, di fatto, maggiromente semplificata la gestione delle dipendeze grafiche del progetto, portando però al tempo stesso alla necessità di utilizzare una specifica procedura per aggiornare e personalizzare gli assets.
Si ricorda che è altamente sconsigliato modificare i contenuti delle cartelle `node_modules` o `assets/dist` in modo manuale, poiché tali modifiche andrebbero perse a seguito di ogni aggiornamento.
### Tema grafico
La personalizzazione dello stile del gestionale può essere effettuata a partire dal foglio di stile `assets/src/css/themes/default.css`, contentente le principali impoistazioni grafiche del progetto.
L'aggiunta di un nuovo tema richieda la creazione di un file indipendente nella stessa cartella, rinominando la classe CSS generica con il nome della skin inserita (da `.skin-default` a `.skin-nome`).
Una volta eseguita la task automatica di compilazione, il nuovo file varrà aggiunto in `themes.min.css` di `assets/css`.
Per modificare lo stile utilizzato dal gestionale, vedere la variabile `$theme` in `config.inc.php`.
### Aggiornamento e installazione pacchetti
Nel caso si rivelasse necessario installare o aggiornare i pacchetti predisposti dal gestionale, è necessario procedere all'esecuzione dei comdani caratteristici di Yarn e successivamente eseguire una compliazione degli assets.
L'installazione di nuovi pacchetti viene eseguita attraverso il seguente comando:
```bash
yarn add <package>
```
L'aggiornamento degli assets gestiti è effettuabile tramite il seguente comando:
```bash
yarn upgrade
```
Per ulteriori informazioni, consultare la [documentazione ufficiale di Yarn](https://yarnpkg.com/en/docs).
### Compilazione
Per compilare gli assets, sia quelli gestiti da Yarn che quelli personalizzati, è necessario eseguire il seguente comando:
```bash
yarn run build-OSM
```
**Attenzione**: la compilazione è fondamentale a seguito di ogni modifica degli assets, poiché altrimenti i file utilizzati dal progetto non saranno aggiornati.
## Assets predefiniti
- admin-lte
- autosize
- bootstrap
- bootstrap-colorpicker
- bootstrap-daterangepicker
- ckeditor
- components-jqueryui
- datatables.net-bs
- datatables.net-scroller-bs
- eonasdan-bootstrap-datetimepicker
- font-awesome
- fullcalendar
- inputmask
- jquery
- jquery-form
- jquery-slimscroll
- jquery-ui-touch-punch
- moment
- parsleyjs
- promise-polyfill
- select2
- select2-bootstrap-theme
- signature_pad
- smartwizard
- sweetalert2
- tooltipster
_I nomi sono indicati secondo la notazione tipica dei pacchetti NPM._

View File

@ -1,73 +0,0 @@
---
currentMenu: framework
---
# Framework
> Un framework, termine della lingua inglese che può essere tradotto come intelaiatura o struttura, in informatica e specificatamente nello sviluppo software, è un'architettura logica di supporto (spesso un'implementazione logica di un particolare design pattern) su cui un software può essere progettato e realizzato, spesso facilitandone lo sviluppo da parte del programmatore.
>
> \-- <cite>[Wikipedia](https://it.wikipedia.org/wiki/Framework)</cite>
Il progetto utilizza [Composer](https://getcomposer.org/) per gestire le librerie PHP in modo completamente gratuito e open-source. Questo permette di completare l'installazione e l'aggiornamento dei diversi framework in modo facile ed intuitivo, senza doversi preoccupare in modo eccessivo delle dipendenze delle diverse librerie.
<!-- TOC depthFrom:2 depthTo:6 orderedList:false updateOnSave:true withLinks:true -->
- [Struttura](#struttura)
- [Personalizzazione](#personalizzazione)
- [Aggiornamento](#aggiornamento)
- [Installazione di nuovi pacchetti](#installazione-di-nuovi-pacchetti)
- [Framework predefiniti](#framework-predefiniti)
<!-- /TOC -->
## Struttura
I framework vengono automaticamente scaricati da Composer all'interno della cartella _vendor_ nella root del progetto, dove vengono memorizzati secondo un percorso derivante dall'origine del pacchetto (per maggiori informazioni, consultare la [documentazione ufficiale di Composer](https://getcomposer.org/doc/)).
La modifica dei contenuti di `vendor` è altamente sconsigliata, poichè qualunque aggiornamento potrebbe sovrascrivere ed annullare le modifiche effettuate.
## Personalizzazione
Nel caso si rivelasse necessario aggiornare i framework presenti o installare nuove librerie, è necessario avere disponibile una corretta e funzionante [installazione locale di Composer](https://getcomposer.org/download/).
Una volta completata l'installazione di Composer è possibile, partendo dalla cartella del gestionale, iniziare l'aggiornamento e la personalizzazione tramite le seguenti operazioni.
### Aggiornamento
L'aggiornamento dei framework è effettuabile tramite il seguente comando:
```bash
php composer.phar update
```
Per ulteriori informazioni, consultare la [documentazione ufficiale di Composer](https://getcomposer.org/doc/).
### Installazione di nuovi pacchetti
Per installare nuovi framework e/o librerie è utilizzabile il seguente comando:
```bash
php composer.phar require <package>
```
Per ulteriori informazioni, consultare la [documentazione ufficiale di Composer](https://getcomposer.org/doc/).
## Framework predefiniti
- danielstjules/stringy
- ezyang/htmlpurifier
- filp/whoops
- ifsnop/mysqldump-php
- intervention/image
- ircmaxell/password-compat
- maximebf/debugbar
- monolog/monolog
- mpdf/mpdf
- paragonie/random_compat
- phpmailer/phpmailer
- spipu/html2pdf
- symfony/filesystem
- symfony/finder
- symfony/translation
_I nomi sono indicati secondo la notazione tipica dei progetti pubblici su GitHub._

View File

@ -1,162 +0,0 @@
---
currentMenu: installazione
---
# Installazione
- [Requisiti](#requisiti)
- [Installazione](#installazione)
- [Versioni](#versioni)
- [GitHub](#github)
- [Strumenti utili](#strumenti-utili)
- [Windows](#windows)
- [Linux](#linux)
- [MAC](#mac)
- [Problemi comuni](#problemi-comuni)
- [Schermata bianca](#schermata-bianca)
- [Blocco dell'installazione (0%)](#blocco-dellinstallazione-0)
## Requisiti
L'installazione del gestionale richiede la presenza di un server web con abilitato il [DBMS MySQL](https://www.mysql.com) e il linguaggio di programmazione [PHP](http://php.net).
- PHP >= 5.6
- MySQL >= 5.0
Per ulteriori informazioni sui pacchetti che forniscono questi elementi di default, visitare la sezione [Installazione](https://devcode-it.github.io/openstamanager/installazione.html) della documentazione.
## Installazione
Per procedere all'installazione è necessario seguire i seguenti punti:
1. [Scaricare una release ufficiale del progetto](https://github.com/devcode-it/openstamanager/releases).
2. Creare una cartella (ad esempio `openstamanager`) nella root del server web installato ed estrarvi il contenuto della release scaricata. Il percorso della cartella root del server varia in base al software in utilizzo:
- LAMP (`/var/www/html`)
- XAMPP (`C:/xampp/htdocs` per Windows, `/opt/lampp/htdocs/` per Linux, `/Applications/XAMPP/htdocs/` per MAC)
- WAMP (`C:\wamp\www`)
- MAMP (`C:\MAMP\htdocs` per Windows, `/Applications/MAMP/htdocs` per MAC)
3. Creare un database vuoto (tramite [PHPMyAdmin](http://localhost/phpmyadmin/) o riga di comando).
4. Accedere a <http://localhost/openstamanager> dal vostro browser.
5. Inserire i dati di configurazione per collegarsi al database.
6. Procedere all'installazione del software, cliccando sul pulsante **Installa**.
**Attenzione**: è possibile che l'installazione richieda del tempo. Si consiglia pertanto di attendere almeno qualche minuto senza alcun cambiamento nella pagina di installazione (in particolare, della progress bar presente) prima di cercare una possibile soluzione nelle discussioni del forum o nella sezione dedicata.
### Versioni
Per mantenere un elevato grado di trasparenza riguardo al ciclo delle release, seguiamo le linee guida [Semantic Versioning (SemVer)](http://semver.org/) per definire le versioni del progetto.
Per vedere tutte le versioni disponibili al download, visitare la [pagina relativa](https://github.com/devcode-it/openstamanager/releases) su GitHub (per versioni precedenti alla 2.3, visitare [SourceForge](https://sourceforge.net/projects/openstamanager/files)).
Nel caso utilizziate il programma per uso commerciale, si consiglia di scaricare le release disponibili nel sito ufficiale del progetto (<http://www.openstamanager.com>), evitando di utilizzare direttamente il codice della repository.
Se siete inoltre interessati a supporto e assistenza professionali, li potete richiedere nella [sezione dedicata](http://www.openstamanager.com/per-le-aziende/).
### GitHub
Nel caso si stia utilizzando la versione direttamente ottenuta dalla repository di GitHub, è necessario eseguire i seguenti comandi da linea di comando per completare le dipendenze PHP (tramite [Composer](https://getcomposer.org)) e gli assets (tramite [Yarn](https://yarnpkg.com)) del progetto.
```bash
php composer.phar install
yarn global add gulp
yarn install
gulp
```
In alternativa alla sequenza di comandi precedente, è possibile utilizzare il seguente comando (richiede l'installazione di GIT e Yarn, oltre che l'inserimento dell'archivio `composer.phar` nella cartella principale del progetto):
```bash
yarn run develop-OSM
```
Per ulteriori informazioni, visitare le sezioni [Assets](https://devcode-it.github.io/openstamanager/assets.html) e [Framework](https://devcode-it.github.io/openstamanager/framework.html) della documentazione.
## Strumenti utili
### Windows
Per installare il server web si consiglia di scaricare [WAMP dal sito ufficiale](http://www.wampserver.com/en/#download-wrapper), seguendo l'installazione guidata senza particolari personalizzazioni.
Una volta terminata linstallazione è necessario creare una cartella per il gestionale in `C:\wamp\www\`, copiando al suo interno il contenuto della release scaricata.
### Linux
Per installare il web server è necessario installare i pacchetti `apache2`, `php5` e `mysql-server`.
```bash
sudo apt-get install apache2 php5 mysql-server
```
Una volta completata linstallazione è necessario creare una cartella per il gestionale, copiandobi al suo interno il contenuto della release scaricata, nel web server di Apache2:
- nella versione &lt;= 2.3, la cartella si trova in `/var/www/`;
- nella versione >= 2.4, la cartella si trova in `/var/www/html/`;
E' inoltre necessario assicurarsi di concedere i permessi di scrittura sulla cartella creata:
```bash
sudo chmod 777 -R /var/www/
```
Si consiglia l'installazione del pacchetto `phpmyadmin`, per poter gestire graficamente il database MySQL:
```bash
sudo apt-get install phpmyadmin
```
### MAC
La piattaforma Apple non è stata oggetto di molti test: pertanto si consiglia di individuare in prima persona un server web funzionante e con caratteristiche corrispondenti ai requisiti indicati.
Il gestionale è stato testato con successo su Mac OS X con [MAMP](http://www.mamp.info/en/) e XAMPP.
## Problemi comuni
### Schermata bianca
**Attenzione**: a partire dalla versione 2.3 questo problema non è più presente.
Nel caso si verifichi il problema di schermata bianca iniziale è necessario controllare i valori delle variabili `$rootdir` e `$docroot` nelle prime righe di _core.php_. Una possibile soluzione, implementata dalla versione 2.3, potrebbe essere:
```php
$docroot = __DIR__;
$rootdir = substr($_SERVER['SCRIPT_NAME'], 0, strrpos($_SERVER['SCRIPT_NAME'], '/')).'/';
if (strrpos($rootdir, '/'.basename($docroot).'/') !== false) {
$rootdir = substr($rootdir, 0, strrpos($rootdir, '/'.basename($docroot).'/')).'/'.basename($docroot);
} else {
$rootdir = '/';
}
$rootdir = rtrim($rootdir, '/');
$rootdir = str_replace('%2F', '/', rawurlencode($rootdir));
```
Si ricorda comunque che:
- `$docroot` deve corrispondere al percorso reale nel file system per raggiungere la cartella principale del gestionale.
- `$rootdir` deve corrispondere al percorso URL del browser per raggiungere il gestionale nel server web.
### Blocco dell'installazione (0%)
**Attenzione**: a partire dalla versione 2.3 questo problema non è più presente.
Nel caso l'installazione iniziale del database si blocchi allo 0% è necessario effettuare la seguente modifica nelle righe 15, 16 e 17 del file `lib\dbo.class.php` (https://www.openstamanager.com/forum/viewtopic.php?f=4&t=88353#p93976):
```php
if(@mysql_select_db($db_name, $conn)) {
@mysql_query("SET sql_mode = ''");
return "ok";
} else
```
Eventualmente, se questo primo passaggio si rivelasse non funzionante, si può procedere alla modifica delle impostazioni del DBMS (file `my.ini` di MySQL).
```ini
#sql-mode="STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"
sql-mode="NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"
```
La riga iniziante da `#` è quella originale, mentre quella seguente è l'opzione che permette il corretto funzionamento dell'installazione.
Discussioni originali:
- [\[RISOLTO\] Tabelle Mancanti](http://www.openstamanager.com/forum/viewtopic.php?f=2&t=86981)
- [MySQL running in Strict Mode and giving me problems. How to fix this?](http://stackoverflow.com/questions/21667601/mysql-running-in-strict-mode-and-giving-me-problems-how-to-fix-this)

View File

@ -1,271 +0,0 @@
---
currentMenu: moduli
---
# Moduli
> Un modulo (software) è un componente software autonomo e ben identificato, e quindi facilmente riusabile.
>
> \-- <cite>[Wikipedia](https://it.wikipedia.org/wiki/Modulo#Informatica)</cite>
All'interno del progetto, i moduli vengono genericamente definiti quali sistemi di gestione delle funzionalità del gestionale; proprio per questo, la loro struttura e composizione risulta spesso variabile e differenziata, presentando componenti uniche e talvolta complesse.
Ogni modulo è composto da diverse sezioni, generalmente suddivise in:
- Nucleo;
- [Stampe](Stampe.md);
- [Widget](Widget.md);
- [Plugin](Plugin.md).
Inoltre, OpenSTAManager presenta una struttura nativamente preposta alla personalizzazione delle proprie funzioni, che rende il progetto ancora più complicato da comprendere a prima vista.
Segue un'analisi della struttura fisica e logica del nucleo dei moduli supportati dal gestionale; per ulteriori informazioni e approfondimenti, si consiglia di osservare l'effettiva composizione dei moduli implementati in modo ufficiale.
<!-- TOC depthFrom:2 depthTo:6 orderedList:false updateOnSave:true withLinks:true -->
- [Struttura](#struttura)
- [actions.php](#actionsphp)
- [add.php e edit.php](#addphp-e-editphp)
- [init.php](#initphp)
- [controller_after.php e controller_before.php](#controllerafterphp-e-controllerbeforephp)
- [modutil.php](#modutilphp)
- [Database](#database)
- [zz_modules](#zzmodules)
- [zz_permissions e zz_group_module](#zzpermissions-e-zzgroupmodule)
- [zz_views e zz_group_view](#zzviews-e-zzgroupview)
- [zz_plugins e zz_widgets](#zzplugins-e-zzwidgets)
- [Consigli per lo sviluppo](#consigli-per-lo-sviluppo)
- [Progettazione](#progettazione)
- [Sviluppo](#sviluppo)
- [Test](#test)
- [Installazione](#installazione)
- [Archivio ZIP](#archivio-zip)
- [update/VERSIONE.sql](#updateversionesql)
- [update/unistall.php](#updateunistallphp)
- [MODULE](#module)
- [Moduli di base](#moduli-di-base)
<!-- /TOC -->
## Struttura
La sezione fondamentale di un qualsiasi modulo all'interno di OpenSTAManager risulta presenta all'interno della cartella `modules`, che contiene tutti i file su cui si basano tutti i moduli per funzionare correttamente.
In questo contensto, ogni modulo possiede una cartella univoca per gestire i propri contenuti in modo indipendente ma comunque sottoposto alla seguente struttura di base.
.
└── modulo
   ├── actions.php
   ├── add.php
   ├── controller_after.php
   ├── controller_before.php
   ├── edit.php
   ├── init.php
   └── modutil.php
Il gestionale supporta in modo nativo questa struttura, che può essere ampliata e personalizzata a necessità dagli sviluppatori: si consiglia pertanto di analizzare la struttuta dei moduli **Iva**, **Dashboard** e **Contratti** per esempi di diversa complessità e funzione.
**Attenzione**: la presenza dei file sopra indicati non è strettamente necessaria per il funzionamento di un modulo (si veda **Movimenti**, presente esclusivamente a livello di database).
### actions.php
Il file `actions.php` gestisce tutte le operazioni supportate dal modulo.
In generale, le diverse operazioni vengono gestite attraverso attraverso una logica programmativa basata su casi (solitamente, il parametro `op` permette di identificare quale azione viene richiesta); il funzionamento a livello di programmazione può essere comunque sottoposto a scelte personali.
L'unico requisito effettivo risulta relativo alle operazioni di creazione dei nuovi record, per cui deve essere definito all'interno della variabile `$id_record` l'identificativo del nuovo elemento.
Per osservare questo sistema, si consiglia di analizzare il relativo file del modulo **Iva**.
### add.php e edit.php
Il file `add.php` contiene il template HTML dedicato all'inserimento di nuovi elementi per il modulo, mentre `edit.php` contiene il template HTML dedicato alla modifica degli stessi.
In base alla configurazione del modulo nel database, il file `edit.php` può assumere il ruolo di gestore della sezione principale dell'interno modulo, permettendo così la personalizzazione dei contenuti come si può notare per i moduli **Dashboard** e **Gestione componenti**.
**Attenzione**: il progetto individua in automatico la presenza di questo file e agisce di conseguenza per permettere o meno l'inserimento di nuovi valori.
### init.php
Il file `init.php` si occupa di individuare le informazioni principali utili all'identificazione e alla modifica dei singoli elementi del modulo.
In particolare, questi file sono solitamente composti da una query dedicata ad ottenere tutti i dati dell'elemento nella variabile `$records`, successivamente utilizzata dal gestore dei template per completare le informazioni degli input.
### controller_after.php e controller_before.php
Il file `controller_after.php` contiene le funzioni javaScript aggiuntive specifiche del modulo.
### modutil.php
Il file `modutil.php` viene utilizzato per definire le funzioni PHP specifiche per il modulo, e permettere in questo modo uan gestione semplificata delle operazioni più comuni.
Si noti che un modulo non è necessariamente limitato all'utilizzo del proprio file `modutil.php`: come avviene per esempio in **Fatture** e **Interventi**, risulta possibile richiamare file di questa tipologia da altri moduli (in questo caso, da **Articoli** per la gestione delle movimentazioni di magazzino).
## Database
All'interno del database del progetto, le tabelle con il suffisso `zz` sono generalmente dedicate alla gestione delle funzioni di base del gestionale, finalizzate in particolare all'utilizzo dei moduli installati.
La gestione dei moduli avviene in questo senso grazie alle seguenti tabelle:
- `zz_modules`;
- `zz_permissions`;
- `zz_views`;
- `zz_plugins`;
- `zz_widgets`.
### zz_modules
La tabella `zz_modules` contiene tutte le informazioni dei diversi moduli installati nel gestionale in uso, con particolare riferimento a:
- Nome (utilizzato a livello di programmazione) [`name`]
- Titolo (visibile e personalizzabile) [`title`]
- Percorso nel file system (partendo da `modules/`) [`directory`]
- Icona [`icon`]
- Posizione nella sidebar [`order`]
- Compatibilità [`compatibility`]
- Query di default [`options`]
- Query personalizzata [`options2`]
Gli ultimi due attributi si rivelano di fondamentale importanza per garantire il corretto funzionamento del modulo, poiché descrivono il comportamento dello stesso per la generazione della schermata principale nativa di OpenSTAManager.
Sono permessi i seguenti valori:
- custom [Modulo con schermata principale personalizzata e definita nel file `edit.php`]
- {VUOTO} [Menu non navigabile]
- menu [Menu non navigabile]
- Oggetto JSON
```json
{ "main_query": [ { "type": "table", "fields": "Nome, Descrizione", "query": "SELECT `id`, `nome` AS `Nome`, `descrizione` AS `Descrizione` FROM `tabella` HAVING 1=1 ORDER BY `nome`"} ]}
```
- Query SQL \[vedasi la tabella [zz_views](#zz_views-e-zz_group_view)]
```sql
SELECT |select| FROM `tabella` HAVING 1=1
```
### zz_permissions e zz_group_module
La tabella `zz_permissions` contiene i permessi di accesso dei vari gruppi ai diversi moduli, mentre la tabella `zz_group_module` contiene le clausole DQL per permettere questo accesso.
### zz_views e zz_group_view
Le tabelle `zz_views` e `zz_group_view` vengono utilizzate dal gestionale per la visualizzazione delle informazioni secondo i permessi accordati, oltre che dal modulo **Viste** per la gestione dinamica delle query.
### zz_plugins e zz_widgets
La tabella `zz_plugins` contiene l'elenco di plugins relativi ai diversi moduli, mentre la tabella `zz_group_module` contiene l'elenco di widgets dei vari moduli.
## Consigli per lo sviluppo
### Progettazione
Alla base dello sviluppo di ogni modulo vi è una fase di analisi indirizzata all'individuazione dettagliata delle funzionalità dello stesso e della struttura interna al database atta a sostenere queste funzioni. Siete dunque pregati di identificare chiaramente tutte le caratteristiche del Vostro nuovo modulo o delle Vostre modifiche prima di iniziare lo sviluppo vero e proprio (comunemente identificato con la scrittura del codice).
> E' bene trascurare le fasi di analisi e di progetto e precipitarsi all'implementazione allo scopo di guadagnare il tempo necessario per rimediare agli errori commessi per aver trascurato la fase di analisi e di progetto.
>
> \-- <cite>Legge di Mayers</cite>
### Sviluppo
Lo sviluppo del codice deve seguire alcune direttive generali per la corretta interpretazione del codice all'interno del gestionale: ciò comporta una struttura di base fondata sui file precedentemente indicati nella sezione [Cartella `modules`](#Cartella_modules) ma ampliabile liberamente.
### Test
Prima di pubblicare un modulo si consiglia di effettuare svariati test in varie installazioni. Siete inoltre pregati di indicare i bug noti.
> Se cè una remota possibilità che qualcosa vada male, sicuramente ciò accadrà e produrrà il massimo danno.
>
> \-- <cite>Legge di Murphy</cite>
## Installazione
L'installazione di un modulo è completabile in modo automatico seguendo la seguente procedura:
- Scaricare l'archivio `.zip` del modulo da installare;
- Accedere al proprio gestionale con un account abilita all'accesso del modulo **Aggiornamenti**;
- Selezionare l'archivio scaricato nella selezione file della sezione "Carica un nuovo modulo";
- Cliccare il pulsante "Carica".
Si ricorda che per effettuare l'installazione è necessaria la presenza dell'estensione `php_zip` (per ulteriori informazioni guardare [qui](http://php.net/manual/it/zip.installation.php)).
**Attenzione**: la procedura può essere completata anche a livello manuale, ma si consiglia di evitare tale sistema a meno che non si conosca approfonditamente il funzionamento complessivo e specifico del database del progetto.
### Archivio ZIP
L'archivio scaricato deve contenere direttamente al proprio interno i contenuti del modulo da installare, organizzati secondo la seguente struttura:
modulo.zip
├── update
| ├── VERSIONE.sql
| └── unistall.php
├── ... - File contententi il codice del modulo
└── MODULE
#### update/VERSIONE.sql
Il file `VERSIONE.sql` (dove VERSIONE sta per la versione del modulo con `_`[underscore] al posto di `.`[punto]) contiene le operazioni di installazione del modulo a livello del database, comprendenti la creazione delle tabelle di base del modulo e l'inserimento di ulteriori dati nelle altre tabelle.
#### update/unistall.php
Il file `unistall.php` contiene le operazioni di disinstallazione del modulo a livello del database, comprendenti l'eliminazione delle tabelle non più necessarie e dei dati inutilizzati.
```php
<?php
include_once __DIR__.'/../../core.php';
$dbo->query("DROP TABLE `tabella`");
?>
```
#### MODULE
Il file `MODULE` è infine il diretto responsabile dell'installazione del modulo poiché definisce tutti i valori caratteristici dello stesso; in caso di sua assenza la cartella compressa viene considerata non corretta.
```ini
module_name = "Nome del modulo"
module_version = "Versione del modulo"
module_directory = "Cartella di installazione del modulo"
module_options = "Operazione da eseguire all'apertura del modulo"
module_icon = "Icona del modulo (Font-Awesome)"
module_compatibility = "Compatibilità del modulo"
module_parent = "Genitore del modulo"
```
## Moduli di base
Nella versione base del gestionale sono presenti, all'interno della cartella `modules`, i seguenti moduli.
.
├── aggiornamenti
├── anagrafiche
├── articoli
├── automezzi
├── backup
├── beni
├── categorie
├── causali
├── contratti
├── dashboard
├── ddt
├── fatture
├── gestione_componenti
├── interventi
├── impostazioni
├── iva
├── listini
├── misure
├── my_impianti
├── ordini
├── pagamenti
├── partitario
├── porti
├── preventivi
├── primanota
├── scadenzario
├── stati_intervento
├── tecnici_tariffe
├── tipi_anagrafiche
├── tipi_intervento
├── utenti
├── viste
├── voci_servizio
└── zone

View File

@ -1,12 +0,0 @@
---
currentMenu: plugin
---
# Plugin
<!-- TOC depthFrom:2 depthTo:6 orderedList:false updateOnSave:true withLinks:true -->
<!-- /TOC -->
Pagina in costruzione.

View File

@ -1,53 +0,0 @@
---
currentMenu: home
---
# OpenSTAManager - Documentazione
Benvenuti nella wiki di OpenSTAManager!
> Al giorno d'oggi, per le aziende coinvolte nel settore dell'assistenza tecnica la necessità di un software dedicato, flessibile, completo e funzionale in molteplici categorie di dispositivi risulta di fondamentale importanza per permettere un'efficiente gestione dell'attività lavorativa.
> Per rispondere a queste esigenze nasce il gestionale per il servizio di assistenza tecnica OpenSTAManager, che rende disponibile un supporto informatico per la gestione di questa tipologia di pratiche in un contesto talvolta povero di alternative.
La documentazione del progetto presenta informazioni valide a partire dalla versione 2.3 dello stesso.
Si avverte pertanto che questa documentazione non è utilizzabile con efficacia per le versioni precedenti del progetto, con particolare riguardo verso le strutture relative ad assets e framework, oltre che alle procedure di sviluppo dei moduli.
<!-- TOC depthFrom:2 depthTo:6 orderedList:false updateOnSave:true withLinks:true -->
- [Presentazione](#presentazione)
- [Storia](#storia)
- [Sviluppatori](#sviluppatori)
<!-- /TOC -->
## Presentazione
Il gestionale OpenSTAManager è un software open-source e web based, sviluppato dall'azienda informatica DevCode di Este per gestire ed archiviare il servizio di assistenza tecnica e la relativa fatturazione.
Il nome del progetto deriva dalla parziale traduzione in inglese degli elementi principali che lo compongono: la natura open-source e il suo obiettivo quale Gestore del Servizio Tecnico di Assistenza.
Un software gestionale, identificato nell'insieme degli applicativi che automatizzano i processi di gestione all'interno delle aziende, appartiene solitamente a una specifica categoria del settore, specializzata negli ambiti di:
- Gestione della contabilità;
- Gestione del magazzino;
- Gestione e ausilio della produzione;
- Gestione e previsione dei budget aziendali;
- Gestione ed analisi finanziaria.
Secondo questa definizione, OpenSTAManager riesce a generalizzare al proprio interno le funzionalità caratteristiche della contabilità e della gestione del magazzino, presentando inoltre moduli piuttosto avanzati e destinati a complementare l'attività aziendale in relazione agli interventi di assistenza della realtà lavorativa in oggetto.
## Storia
OpenSTAManager viene inizialmente ideato da Nicoletta Marampon e sviluppato da **Fabio Lovato** al fine di gestire esclusivamente le anagrafiche e gli interventi aziendali per piccole realtà lavorative.
La prima release (0.2RC2 dell'agosto 2008) viene successivamente ampliata con numerosi e innovativi componenti, quali il calendario delle attività, e ottimizzata in relazione al sistema di installazione e di aggiornamento; parallelamente, tra il 2011 e 2012, viene introdotto il sistema di gestione della contabilità e vengono migliorati numerosi moduli, soprattutto a livello grafico.
Fondamentale in questo senso risulta essere la collaborazione dei nuovi colleghi **Luca Salvà** e **Fabio Piovan**, che ha permesso il continuo adeguamento del progetto a un mondo informatico in continua evoluzione e a richieste sempre più complesse da parte dei clienti.
L'insieme di cambiamenti introdotti nel periodo compreso tra giugno 2016 e giugno 2017 hanno infine portato alla generazione di un ramo di sviluppo parallelo (_branch_) per la versione 2.3, destinata a cambiare e migliorare in modo fondamentale numerose sezioni _front-end_ e _back-end_ del progetto.
## Sviluppatori
- **Fabio Lovato**, il fondatore ([loviuz](https://github.com/loviuz))
- **Fabio Piovan** ([fpsoftware](https://github.com/fpsoftware))
- **Luca Salvà** ([lucasalva87](https://github.com/lucasalva87))
- **Matteo Baccarin**
- **Thomas Zilio** ([Dasc3er](https://github.com/Dasc3er))

View File

@ -1,48 +0,0 @@
---
currentMenu: stampe
---
# Stampe
Pagina in costruzione.
- [MPDF](#mpdf)
- [HTML2PDF](#html2pdf)
- [Struttura](#struttura)
- [pdfgen.php](#pdfgenphp)
- [pdfgen_variables.php](#pdfgenvariablesphp)
- [Struttura interna](#struttura-interna)
## MPDF
**Attenzione**: come indicato nel secondo punto in http://mpdf.github.io/tables/auto-layout-algorithm.html, MPDF effettua un resizing del font nel caso il contenuto di una cella superi l'altezza totale di una pagina.
Fino a quel punto, il rendering funziona perfettamente.
Nel caso fosse per esempio aumentare le dimensioni del font, si consiglia di effettuare alcuni test per controllare se le tabelle vengono renderizzate nel modo corretto e previsto.
## HTML2PDF
### Struttura
La cartella _templates_ contiene tutti i template per la creazione dei PDF, raggruppati in base al nome del modulo. QUesti vengono utilizzati da `pdfgen.php` e da `pdfgen_variables.php` per la generazione vera e propria del PDF tramite il framework [HTML2PDF](https://github.com/spipu/html2pdf).
#### pdfgen.php
Il file `pdfgen.php` si occupa della formattazione dei contenuti dei template per la visualizzazione vera e propria del PDF, inizializzando l'oggetto relativo ed eseguendone l'output.
#### pdfgen_variables.php
Il file `pdfgen_variables.php` si occupa della sostituzione delle variabili comuni a tutti i template, e viene richiamata dal file `pdfgen.MODULO.php` descritto di seguito.
#### Struttura interna
La cartella _templates_ contiene tutti i template per la creazione dei PDF relativi al modulo specifico, in una struttura interna simile alla seguente (modulo **Contratti** utilizzato come esempio).
.
└── contratti
   ├── contratto_body.html - Struttura di base del PDF
   ├── contratto.html - Contenitore personalizzato della struttura del PDF
   ├── logo_azienda.jpg - Logo dell'azienda specifico per il PDF
   └── pdfgen.contratti.php - Individuazione delle informazioni da visualizzare e generazione della loro struttura

View File

@ -1,261 +0,0 @@
---
currentMenu: struttura
---
# Struttura
<!-- TOC depthFrom:2 depthTo:6 orderedList:false updateOnSave:true withLinks:true -->
- [Caratteristiche](#caratteristiche)
- [open-source](#open-source)
- [Modulare e personalizzabile](#modulare-e-personalizzabile)
- [Multipiattaforma e user friendly](#multipiattaforma-e-user-friendly)
- [Struttura](#struttura)
- [Root](#root)
- [add.php](#addphp)
- [ajax_complete.php](#ajax_completephp)
- [ajax_dataload.php](#ajax_dataloadphp)
- [ajax_select.php](#ajax_selectphp)
- [bug.php](#bugphp)
- [core.php](#corephp)
- [config.inc.php](#configincphp)
- [controller.php](#controllerphp)
- [editor.php](#editorphp)
- [index.php](#indexphp)
- [info.php](#infophp)
- [log.php](#logphp)
- [composer.json, gulpfile.js, package.json](#composerjson-gulpfilejs-packagejson)
- [Cartella api](#cartella-api)
- [Cartella assets](#cartella-assets)
- [Cartella backup](#cartella-backup)
- [Cartella docs](#cartella-docs)
- [Cartella files](#cartella-files)
- [Cartella include](#cartella-include)
- [bottom.php](#bottomphp)
- [top.php](#topphp)
- [Cartella lib](#cartella-lib)
- [deprecated.php](#deprecatedphp)
- [functions.php](#functionsphp)
- [functionsjs.php](#functionsjsphp)
- [init.js](#initjs)
- [Cartella locale](#cartella-locale)
- [Cartella modules](#cartella-modules)
- [Cartella templates](#cartella-templates)
- [Cartella update](#cartella-update)
- [create_updates.sql](#create_updatessql)
- [VERSIONE.sql](#versionesql)
- [VERSIONE.php](#versionephp)
- [Cartella vendor](#cartella-vendor)
<!-- /TOC -->
## Caratteristiche
### open-source
La natura open-source (termine inglese che significa _sorgente aperta_) del progetto evidenzia lo spirito di collaborazione e condivisione che pervade l'attività di sviluppo del gestionale, di cui gli autori rendono pubblico il codice sorgente e ne favoriscono il libero studio, permettendo a programmatori indipendenti di apportarvi modifiche ed estensioni.
Particolarmente espressiva in questo senso risulta essere la documentazione ufficiale del progetto:
> Il progetto è un software open-source perché permette agli utilizzatori di studiarne il funzionamento ed adattarlo alle proprie esigenze; inoltre, in ambito commerciale, non obbliga l'utilizzatore ad essere legato allo stesso fornitore di assistenza.
La licenza in utilizzo è la GNU General Public License 3.0 (GPL 3.0).
### Modulare e personalizzabile
OpenSTAManager possiede una struttura fortemente modulare, che ne permette la rapida espandibilità e, nello specifico, la realizzazione di funzionalità _ad hoc_, personalizzate nel modo più completo secondo le richieste del cliente.
### Multipiattaforma e user friendly
Il progetto risulta compatibile con numerose piattaforme, necessitando esclusivamente un browser moderno da parte dei suoi utilizzatori per sfruttare appieno le sue potenzialità.
L'interfaccia di interazione con l'utente finale risulta inoltre estremamente semplificata e _user friendly_, oltre che _responsive_, presentando caratteristiche completamente compatibili con tutti i dispositivi mobili (in particolare, tablet e smartphone).
## Struttura
Scaricando la versione GIT del progetto dovreste trovare una struttura di base molto simile a quella seguente.
.
├── api
├── assets
├── backup
├── doc
├── files
├── include
├── lib
├── locale
├── modules
├── src
├── templates
├── update
└── vendor
Analizzeremo ora in dettaglio la funzione delle diverse cartelle e dei relativi contenuti.
Si avverte che il gestionale è fortemente basato sulla correttezza contemporanea di molti file: siete pertanto pregati di astenervi da modifiche o, se queste dovessero rivelarsi necessarie, procedere alla creazione di un relativo file custom nella cartella del file.
E' comunque consigliabile richiedere l'assistenza ufficiale.
Per maggiori informazioni riguardanti la procedura di personalizzazione, rivolgersi alle specifiche sezioni di ogni settore.
## Root
I contenuti della cartella _root_ sono estremamente importanti per il progetto, in quanto sono generalmente dedicati a garantire il corretto funzionamento dell'intero gestionale.
Questa centralizzazione permette al software di essere estremamente scalabile e personalizzabile, soprattutto in relazione ai moduli.
Per maggiori informazioni riguardanti lo sviluppo di un modulo, consultare la sezione [Moduli](Moduli.md).
### add.php
Il file `add.php` è dedicato alla gestione dei form di creazione nuovi elementi all'interno dei vari moduli.
In particolare si occupa parallelamente della funzionalità di aggiunta al volo, visibile in azione nei modulo **Interventi**, **Articoli** e in alcuni altri punti del software.
### ajax_complete.php
Il file `ajax_dataload.php` gestisce il caricamento dinamico dei dati in varie sezioni del sito, relativamente alle operazioni di auto-completamento dei form e della ricerca globale.
**Attenzione**: questo sistema è ormai deprecato e, tranne in rari casi, completamente sostituito dall'utilizzo del file `ajax_select.php` e dal plugin [Select2](https://select2.github.io/).
### ajax_dataload.php
Il file `ajax_dataload.php` gestisce il caricamento dinamico dei dati nelle tabelle fornite nella vista generale dei moduli (`controller.php`), filtrando i risultati in base alle richieste dell'utente.
### ajax_select.php
Il file `ajax_select.php` gestisce il caricamento dinamico dei dati nei diversi select abilitati, garantendo l'accesso a tutti i record senza provocare rallentamenti (persino per numeri più elevati).
### bug.php
Il file `bug.php` si occupa della segnalazione dei bug, fornendo un sistema integrato di invio email dopo la configurazione di pochi parametri iniziali.
Le opzioni relative alle informazioni da allegare sono:
- Allegare il file di log (fondamentale nel caso si stia effettuando una segnalazione)
- Allegare una copia del database
- Allegare le informazioni relative al PC utilizzato
### core.php
Il _core_ contiene il nucleo dell'intero gestionale: si occupa delle operazioni di inizializzazione fondamentali, compresa l'inclusione del file di configurazione `config.inc.php` e la creazione dell'elenco degli assets da includere.
Si occupa inoltre del controllo dei permessi, tramite il richiamo alla specifica classe `Permissions`, e dell'individuazione delle informazioni di base relative al modulo in utilizzo.
### config.inc.php
Il file `config.inc.php` contiene tutte le informazioni per accedere correttamente al database e per determinare il tema utilizzato dal gestionale.
**Attenzione**: questo file non è presente di default poiché obbligatoriamente da personalizzare tramite la procedura di installazione.
La struttura di base può essere comunque osservata all'interno del file `config.example.php`.
### controller.php
Il file `controller.php` si occupa di gestire l'accesso generico ai moduli, caricando le informazioni e i widget del modulo in oggetto.
Permette inoltre la visualizzazione, in base ai permessi accordati all'utente, del pulsante di creazione nuovi elementi.
### editor.php
Il file `editor.php` si occupa di gestire l'accesso specifico ai dati di un singolo elemento di un modulo, caricando al tempo stesso l'insieme di plugin (e, in casi più rari, di widget) legati alla visualizzazione dell'elemento in oggetto.
Permette inoltre, in base ai permessi accordati all'utente, la modifica dei dati inseriti e l'interazione con altri moduli.
### index.php
Il file `index.php` si occupa delle operazioni di accesso e scollegamento al gestionale, oltre che effettuare un controllo sulla disponibilità di aggiornamenti (tramite l'inclusione di `include/update.php`) e a garantire il redirect iniziale al primo modulo su cui l'utente possiede i permessi di accesso.
### info.php
Il file `info.php` contiene la sezione informativa relativa al progetto, indicante la community, la modalità di supporto e le offerte a pagamento.
### log.php
Il file `log.php` permette di visualizzare le informazioni relative agli ultimi 100 tentativi di accesso.
**Attenzione**: nel caso in cui l'utente sia un amministratore, le informazioni accessibili sono relative a **tutti** gli utenti (al contrario, un utente normale può visualizzare esclusivamente i propri tentativi).
### composer.json, gulpfile.js, package.json
Per maggiori informazioni questi file, consultare le sezioni [Framework](Framework.md) e [Assets](Assets.md).
## Cartella api
Per maggiori informazioni riguardanti la cartella `api` e i suoi contenuti, rivolgersi alla sezione [API](API.md).
## Cartella assets
Per maggiori informazioni riguardanti la cartella `assets` e i suoi contenuti, rivolgersi alla sezione [Assets](Assets.md).
## Cartella backup
La cartella _backup_ è quella di default utilizzata dall'operazione omonima del progetto.
## Cartella docs
La cartella `docs`, come si può intuire, contiene la documentazione di sviluppo del progetto.
## Cartella files
Per maggiori informazioni riguardanti la cartella `files` e i suoi contenuti, rivolgersi alla sezione [Moduli](Moduli.md).
## Cartella include
### bottom.php
Il file `bottom.php` si occupa delle operazioni di chiusura della pagina HTML, garantendo inoltre l'eliminazione dei log temporanei della sessione.
Si ricorda che è possibile creare una personalizzazione di questa pagina nella cartella `custom`.
### top.php
Il file `top.php` gestisce la creazione del layout di base del gestionale, comprendente la barra di navigazione e la sidebar.
Si ricorda che è possibile creare una personalizzazione di questa pagina nella cartella `custom`.
## Cartella lib
La cartella `lib` contiene le librerie personalizzate e le funzioni utilizzate dall'intero gestionale nei diversi moduli.
**Attenzione**: sono qui presenti solo i metodi generali e comunemente riutilizzati.
Per maggiori informazioni riguardanti la locazione delle funzioni specifiche di un modulo, visitare la sezione [Moduli](Moduli.md).
### deprecated.php
Il file `deprecated.php` contiente l'insieme di funzioni deprecate nella versione corrente del gestionale, che verranno successivamente rimosse nella futura release.
### functions.php
Il file `functions.php` contiene tutte le funzioni comunemente utilizzate nel progetto.
### functionsjs.php
Il file `functionsjs.php` contiene tutte le funzioni JavaScript comunemente utilizzate nel progetto.
### init.js
Il file `init.js` contiene le funzioni JavaScript comunemente richiamate al caricamento di parti indipendenti del progetto.
## Cartella locale
La cartella `locale` contiene tutte le traduzioni del progetto, nei formati tipici di Gettext (`.po` e `.mo`).
## Cartella modules
Per maggiori informazioni riguardanti la cartella `modules` e i suoi contenuti, rivolgersi alla sezione [Moduli](Moduli.md).
Si ricorda che per tutti i contenuti del modulo è possibile creare una personalizzazione nella cartella `custom`.
## Cartella templates
La cartella `templates` contiene tutti i file relativi alla visualizzazione in PDF dei dati dei vari moduli.
Per maggiori informazioni riguardanti la cartella `templates` e i suoi contenuti, rivolgersi alla sezione [Stampe](Stampe.md).
## Cartella update
### create_updates.sql
Il file `create_updates.sql` contiene la query SQL per la creazione della tabella di gestione degli aggiornamenti e delle installazioni del gestionale.
### VERSIONE.sql
I file `VERSIONE.sql` contengono l'insieme di query SQL necessarie per l'aggiornamento del gestionale alla versione _VERSIONE_.
### VERSIONE.php
I file `VERSIONE.php` contengono l'insieme di operazioni PHP (e, talvolta, SQL) necessarie per l'aggiornamento del gestionale alla versione _VERSIONE_.
## Cartella vendor
Per maggiori informazioni riguardanti la cartella `vendor` e i suoi contenuti, rivolgersi alla sezione [Framework](Framework.md).

View File

@ -1,39 +0,0 @@
---
currentMenu: upload
---
# Gestione degli upload
Pagina in costruzione.
La cartella `files` viene utilizzata dal progetto per gestire in modo unificato contenuti di vario tipo per i moduli installati.
In generale, questa cartella è dedicata alla memorizzazione dei file di cui viene fatto l'upload attraverso la funzione fornita in automatico dal getionale, ma sono presenti delle specifiche personalizzazioni necessarie per l'adeguato funzionamento di alcuni moduli.
<!-- TOC depthFrom:2 depthTo:6 orderedList:false updateOnSave:true withLinks:true -->
- [Modulo MyImpianti](#modulo-myimpianti)
<!-- /TOC -->
## Modulo MyImpianti
Il modulo **MyImpianti** sfrutta la propria cartella all'interno di `files` per gestire, oltre alle proprie immagini, le impostazioni (`.ini`) dei componenti disponibili.
I file `*.ini` devono seguire il seguente standard. {} = facoltativo
```ini
[Nome del campo]
tipo = tag_HTML
valore = {"Valore di default"}
{opzioni = "Opzione 1", "Opzione 2", "Opzione 3"}
[Nome del campo]
tipo = tag_HTML
valore = {"Valore di default"}
{opzioni = "Opzione 1", "Opzione 2", "Opzione 3"}
```
La dicitura "tag_HTML" indica la possibilità di inserire all'interno del campo il nome di un qualsiasi tag HTML per l'utilizzo durante la modifica.
In particolare, il gestionale supporta la maggior parte dei campi HTML di input (input, select, textarea, date, ...); se necessario, è inoltre possibile (span, p, ...).
Il file `my_impianti/componente.ini` è un esempio di base di questa funzionalità, e un'ulteriore personalizzazione può essere trovata [nel forum](http://www.openstamanager.com/forum/viewtopic.php?f=5&t=93).

View File

@ -1,12 +0,0 @@
---
currentMenu: widget
---
# Widget
<!-- TOC depthFrom:2 depthTo:6 orderedList:false updateOnSave:true withLinks:true -->
<!-- /TOC -->
Pagina in costruzione.

View File

@ -2,37 +2,27 @@
include_once __DIR__.'/core.php';
use Carbon\Carbon;
if (empty($id_record) && !empty($id_module)) {
redirect(ROOTDIR.'/controller.php?id_module='.$id_module);
} elseif (empty($id_record) && empty($id_module)) {
redirect(ROOTDIR.'/index.php');
}
if (file_exists($docroot.'/include/custom/top.php')) {
include $docroot.'/include/custom/top.php';
} else {
include $docroot.'/include/top.php';
}
include_once App::filepath('include|custom|', 'top.php');
// Lettura parametri iniziali del modulo
$module = Modules::get($id_module);
if (empty($module) || empty($module['enabled'])) {
die(tr('Accesso negato'));
}
$module_dir = $module['directory'];
// Inclusione elementi fondamentali del modulo
include $docroot.'/actions.php';
// Inclusione gli elementi fondamentali
include_once $docroot.'/actions.php';
// Widget in alto
echo '{( "name": "widgets", "id_module": "'.$id_module.'", "id_record": "'.$id_record.'", "position": "top", "place": "editor" )}';
$advanced_sessions = get_var('Attiva notifica di presenza utenti sul record');
if ($advanced_sessions) {
$dbo->query('DELETE FROM zz_semaphores WHERE id_utente='.prepare($_SESSION['id_utente']).' AND posizione='.prepare($id_module.', '.$id_record));
$dbo->query('INSERT INTO zz_semaphores (id_utente, posizione, updated) VALUES ('.prepare($_SESSION['id_utente']).', '.prepare($id_module.', '.$id_record).', NOW())');
$advanced_sessions = setting('Attiva notifica di presenza utenti sul record');
if (!empty($advanced_sessions)) {
$dbo->query('DELETE FROM zz_semaphores WHERE id_utente='.prepare(Auth::user()['id']).' AND posizione='.prepare($id_module.', '.$id_record));
$dbo->query('INSERT INTO zz_semaphores (id_utente, posizione, updated) VALUES ('.prepare(Auth::user()['id']).', '.prepare($id_module.', '.$id_record).', NOW())');
echo '
<div class="box box-warning box-solid text-center info-active hide">
@ -48,35 +38,38 @@ if ($advanced_sessions) {
</div>';
}
if (empty($records)) {
if (empty($record)) {
echo '
<p>'.tr('Record non trovato').'.</p>';
} else {
/*
* Lettura eventuali plugins modulo da inserire come tab
*/
echo '
<div class="nav-tabs-custom">
<ul class="nav nav-tabs pull-right" id="tabs" role="tablist">
<li class="pull-left active header">';
// Verifico se ho impostato un nome modulo personalizzato
$name = $module['title'];
echo '
<li class="pull-left active header">
<a data-toggle="tab" href="#tab_0">
<i class="'.$module['icon'].'"></i> '.$name;
<i class="'.$structure['icon'].'"></i> '.$structure['title'];
// Pulsante "Aggiungi" solo se il modulo è di tipo "table" e se esiste il template per la popup
if (file_exists($docroot.'/modules/'.$module_dir.'/add.php') && $module['permessi'] == 'rw') {
if ($structure->hasAddFile() && $structure->permission == 'rw') {
echo '
<button type="button" class="btn btn-primary" data-toggle="modal" data-title="'.tr('Aggiungi').'..." data-target="#bs-popup" data-href="add.php?id_module='.$id_module.'"><i class="fa fa-plus"></i></button>';
<button type="button" class="btn btn-primary" data-toggle="modal" data-title="'.tr('Aggiungi').'..." data-href="add.php?id_module='.$id_module.'&id_plugin='.$id_plugin.'"><i class="fa fa-plus"></i></button>';
}
echo '
</a>
</li>';
// Tab per le informazioni sulle operazioni
if (Auth::admin()) {
echo '
<li class="bg-warning">
<a data-toggle="tab" href="#tab_info" id="link-tab_info">'.tr('Info').'</a>
</li>';
}
$plugins = $dbo->fetchArray('SELECT id, title FROM zz_plugins WHERE idmodule_to='.prepare($id_module)." AND position='tab' AND enabled = 1 ORDER BY zz_plugins.order DESC");
// Tab dei plugin
foreach ($plugins as $plugin) {
echo '
<li>
@ -103,7 +96,7 @@ if (empty($records)) {
{( "name": "button", "type": "email", "id_module": "'.$id_module.'", "id_record": "'.$id_record.'" )}
<a class="btn btn-success" id="save">
<i class="fa fa-check"></i> '.tr('Salva modifiche').'
<i class="fa fa-check"></i> '.tr('Salva').'
</a>
</div>
</div>
@ -128,6 +121,10 @@ if (empty($records)) {
}
});
if ($("#pulsanti").hasClass("affix")) {
$("#pulsanti").css("width", $("#tab_0").css("width"));
}
$("#pulsanti").on("affix.bs.affix", function(){
$("#pulsanti").css("width", $("#tab_0").css("width"));
});
@ -145,15 +142,12 @@ if (empty($records)) {
<br>';
// Pulsanti personalizzati
ob_start();
if (file_exists($docroot.'/modules/'.$module_dir.'/custom/buttons.php')) {
include $docroot.'/modules/'.$module_dir.'/custom/buttons.php';
} elseif (file_exists($docroot.'/modules/'.$module_dir.'/buttons.php')) {
include $docroot.'/modules/'.$module_dir.'/buttons.php';
}
$buttons = ob_get_clean();
$buttons = $structure->filepath('buttons.php');
if (!empty($buttons)) {
ob_start();
include $buttons;
$buttons = ob_get_clean();
echo '
<div class="pull-right" id="pulsanti-modulo">
'.$buttons.'
@ -168,16 +162,7 @@ if (empty($records)) {
<div id="module-edit">';
// Lettura template modulo (verifico se ci sono template personalizzati, altrimenti uso quello base)
if (file_exists($docroot.'/modules/'.$module_dir.'/custom/edit.php')) {
include $docroot.'/modules/'.$module_dir.'/custom/edit.php';
} elseif (file_exists($docroot.'/modules/'.$module_dir.'/custom/edit.html')) {
include $docroot.'/modules/'.$module_dir.'/custom/edit.html';
} elseif (file_exists($docroot.'/modules/'.$module_dir.'/edit.php')) {
include $docroot.'/modules/'.$module_dir.'/edit.php';
} elseif (file_exists($docroot.'/modules/'.$module_dir.'/edit.html')) {
include $docroot.'/modules/'.$module_dir.'/edit.html';
}
include $structure->getEditFile();
echo '
</div>
@ -203,6 +188,11 @@ if (empty($records)) {
// Campi a fine form
var last = form.find(".panel").last();
if (!last.length) {
last = form.find(".box").last();
}
if (!last.length) {
last = form.find(".row").eq(-2);
}
@ -211,7 +201,92 @@ if (empty($records)) {
});
</script>';
// Informazioni sulle operazioni
if (Auth::admin()) {
echo '
<div id="tab_info" class="tab-pane">';
$operations = $dbo->fetchArray('SELECT `zz_operations`.*, `zz_users`.`username` FROM `zz_operations` JOIN `zz_users` ON `zz_operations`.`id_utente` = `zz_users`.`id` WHERE id_module = '.prepare($id_module).' AND id_record = '.prepare($id_record).' ORDER BY `created_at` ASC LIMIT 200');
if (!empty($operations)) {
echo '
<ul class="timeline">';
foreach ($operations as $operation) {
$description = $operation['op'];
$icon = 'pencil-square-o';
$color = null;
$timeline_class = null;
switch ($operation['op']) {
case 'add':
$description = tr('Creazione');
$icon = 'plus';
$color = 'success';
break;
case 'update':
$description = tr('Modifica');
$icon = 'pencil';
$color = 'info';
break;
case 'delete':
$description = tr('Eliminazione');
$icon = 'times';
$color = 'danger';
break;
default:
$timeline_class = ' class="timeline-inverted"';
break;
}
echo '
<li '.$timeline_class.'>
<div class="timeline-badge '.$color.'"><i class="fa fa-'.$icon.'"></i></div>
<div class="timeline-panel">
<div class="timeline-heading">
<div class="row">
<div class="col-md-8">
<h4 class="timeline-title">'.$description.'</h4>
</div>
<div class="col-md-4 text-right">
<p><small class="label label-default tip" title="'.Translator::timestampToLocale($operation['created_at']).'"><i class="fa fa-clock-o"></i> '.Carbon::parse($operation['created_at'])->diffForHumans().'</small></p>
<p><small class="label label-default"><i class="fa fa-user"></i> '.tr('_USER_', [
'_USER_' => $operation['username'],
]).
'</small></p>
</div>
</div>
</div>
<div class="timeline-body">
</div>
<div class="timeline-footer">
</div>
</div>
</li>';
}
echo ' </ul>';
} else {
echo '
<div class="alert alert-info">
<i class="fa fa-info-circle"></i>
<b>'.tr('Informazione:').'</b> '.tr('Nessun log disponibile per questa scheda').'.
</div>';
}
echo '
</div>';
}
// Plugin
$module_record = $record;
foreach ($plugins as $plugin) {
$record = $module_record;
echo '
<div id="tab_'.$plugin['id'].'" class="tab-pane">';
@ -223,12 +298,14 @@ if (empty($records)) {
</div>';
}
$record = $module_record;
echo '
</div>
</div>';
}
redirectOperation($id_module, $id_record);
redirectOperation($id_module, isset($id_parent) ? $id_parent : $id_record);
// Widget in basso
echo '{( "name": "widgets", "id_module": "'.$id_module.'", "id_record": "'.$id_record.'", "position": "right", "place": "editor" )}';
@ -240,72 +317,91 @@ echo '
</a>';
echo '
<script>';
<script>';
// Se l'utente ha i permessi in sola lettura per il modulo, converto tutti i campi di testo in span
if ($module['permessi'] == 'r') {
?>
$(document).ready( function(){
$('input, textarea, select', 'section.content').attr('readonly', 'true');
$('select, input[type="checkbox"]').prop('disabled', true);
$('a.btn, button, input[type=button], input[type=submit]', 'section.content').hide();
$('a.btn-info, button.btn-info, input[type=button].btn-info', 'section.content').show();
});
<?php
} ?>
$read_only = $structure->permission == 'r';
if ($read_only || !empty($block_edit)) {
$not = $read_only ? '' : '.not(".unblockable")';
var content_was_modified = false;
echo '
$(document).ready(function(){
$("input, textarea, select", "section.content")'.$not.'.attr("readonly", "true");
$("select, input[type=checkbox]", "section.content")'.$not.'.prop("disabled", true);';
//controllo se digito qualche valore o cambio qualche select
$("input, textarea, select").bind("change paste keyup", function(event) {
if( event.keyCode >= 32 ){
content_was_modified = true;
}
});
//tolgo il controllo se sto salvando
$(".btn-success, button[type=submit]").bind("click", function() {
content_was_modified = false;
});
// questo controllo blocca il modulo vendita al banco, dopo la lettura con barcode, appare il messaggio di conferma
window.onbeforeunload = function(){
if(content_was_modified) {
return 'Uscire senza salvare?';
}
};
<?php
if ($advanced_sessions) {
?>
function getActiveUsers(){
$.getJSON('<?php echo ROOTDIR; ?>/call.php', {
id_module: <?php echo $id_module; ?>,
id_record: <?php echo $id_record; ?>
},
function(data) {
if (data.length != 0) {
$(".info-active").removeClass("hide");
$(".info-active .list").html("");
$.each( data, function( key, val ) {
$(".info-active .list").append("<li>"+val.username+"</li>");
});
}
else $(".info-active").addClass("hide");
});
}
getActiveUsers();
setInterval(getActiveUsers, <?php echo get_var('Timeout notifica di presenza (minuti)') * 1000; ?>);
<?php
if ($read_only) {
echo '
$("a.btn, button, input[type=button], input[type=submit]", "section.content").hide();
$("a.btn-info, a.btn-warning, button.btn-info, button.btn-warning, input[type=button].btn-info", "section.content").show();';
}
echo '
});';
}
?>
</script>
var content_was_modified = false;
//controllo se digito qualche valore o cambio qualche select
$("input, textarea, select").bind("change paste keyup", function(event) {
if( event.keyCode >= 32 ){
content_was_modified = true;
}
});
//tolgo il controllo se sto salvando
$(".btn-success, button[type=submit]").bind("click", function() {
content_was_modified = false;
});
$( "form" ).bind( "submit", function() {
content_was_modified = false;
})
// questo controllo blocca il modulo vendita al banco, dopo la lettura con barcode, appare il messaggio di conferma
window.onbeforeunload = function(e){
if(content_was_modified) {
var dialogText = "Uscire senza salvare?";
e.returnValue = dialogText;
$("#main_loading").fadeOut();
return dialogText;
}
};
window.addEventListener("unload", function(e) {
//console.log(e);
$("#main_loading").show();
});
<?php
if (!empty($advanced_sessions)) {
?>
function getActiveUsers(){
$.getJSON('<?php echo ROOTDIR; ?>/ajax.php?op=active_users', {
id_module: <?php echo $id_module; ?>,
id_record: <?php echo $id_record; ?>
},
function(data) {
if (data.length != 0) {
$(".info-active").removeClass("hide");
$(".info-active .list").html("");
$.each( data, function( key, val ) {
$(".info-active .list").append("<li>"+val.username+"</li>");
});
}
else $(".info-active").addClass("hide");
});
}
getActiveUsers();
setInterval(getActiveUsers, <?php echo setting('Timeout notifica di presenza (minuti)') * 60 * 1000; ?>);
<?php
}
?>
</script>
<?php
if (file_exists($docroot.'/include/custom/bottom.php')) {
include $docroot.'/include/custom/bottom.php';
} else {
include $docroot.'/include/bottom.php';
}
include_once App::filepath('include|custom|', 'bottom.php');

View File

@ -121,7 +121,7 @@ gulp.task('srcCSS', function () {
gulp.src([
config.development + '/' + config.paths.css + '/themes/*.{css,scss,less,styl}',
config.main.bowerDirectory + '/adminlte/dist/css/skins/_all-skins.css',
config.main.bowerDirectory + '/admin-lte/dist/css/skins/_all-skins.css',
])
.pipe(gulpIf('*.scss', sass(), gulpIf('*.less', less(), gulpIf('*.styl', stylus()))))
.pipe(autoprefixer({
@ -206,18 +206,26 @@ gulp.task('chartjs', function () {
.pipe(gulp.dest(config.production + '/' + config.paths.js + '/chartjs'));
});
gulp.task('csrf', function () {
gulp.src([
'./vendor/owasp/csrf-protector-php/js/csrfprotector.js',
])
.pipe(flatten())
.pipe(gulp.dest(config.production + '/' + config.paths.js + '/csrf'));
});
gulp.task('pdfjs', function () {
gulp.src([
config.main.bowerDirectory + '/pdfjs/web/**/*',
'!' + config.main.bowerDirectory + '/pdfjs/web/cmaps/*',
'!' + config.main.bowerDirectory + '/pdfjs/web/*.map',
'!' + config.main.bowerDirectory + '/pdfjs/web/*.pdf',
config.main.bowerDirectory + '/pdf/web/**/*',
'!' + config.main.bowerDirectory + '/pdf/web/cmaps/*',
'!' + config.main.bowerDirectory + '/pdf/web/*.map',
'!' + config.main.bowerDirectory + '/pdf/web/*.pdf',
])
.pipe(gulp.dest(config.production + '/pdfjs/web'));
gulp.src([
config.main.bowerDirectory + '/pdfjs/build/*',
'!' + config.main.bowerDirectory + '/pdfjs/build/*.map',
config.main.bowerDirectory + '/pdf/build/*',
'!' + config.main.bowerDirectory + '/pdf/build/*.map',
])
.pipe(gulp.dest(config.production + '/pdfjs/build'));
});
@ -265,6 +273,7 @@ gulp.task('release', function () {
'!./vendor/mpdf/mpdf/ttfonts/DejaVuinfo.txt',
'!./vendor/mpdf/mpdf/ttfonts/DejaVu*Condensed*',
'./vendor/maximebf/debugbar/src/DebugBar/Resources/vendor/*',
'./vendor/respect/validation/tests/*',
]);
// Impostazione dello zip
@ -291,11 +300,13 @@ gulp.task('release', function () {
'files/**',
'logs/**',
'config.inc.php',
'*.lock',
'*.phar',
'**/*.lock',
'**/*.phar',
'**/*.log',
'**/*.zip',
'**/*.bak',
'**/*.jar',
'**/*.txt',
'**/~*',
]
});
@ -336,7 +347,7 @@ gulp.task('release', function () {
// Completamento dello zip
archive.finalize();
});;
});
});
// Pulizia
@ -363,7 +374,7 @@ gulp.task('other', ['clean'], function () {
gulp.start('chartjs');
gulp.start('php-debugbar');
gulp.start('csrf');
});
gulp.task('default', ['clean', 'bower']);

View File

@ -9,15 +9,17 @@ if (Auth::check()) {
</aside><!-- /.content-wrapper -->
<footer class="main-footer">
<a class="hidden-xs" href="https://www.openstamanager.com" title="'.tr('Il gestionale open source per l\'assistenza tecnica e la fatturazione').'." target="_blank"><strong>'.tr('OpenSTAManager').'</strong></a>
<a class="hidden-xs" href="https://www.openstamanager.com" title="'.tr("Il gestionale open source per l'assistenza tecnica e la fatturazione").'." target="_blank"><strong>'.tr('OpenSTAManager').'</strong></a>
<span class="pull-right hidden-xs">
<strong>'.tr('Versione').'</strong> '.$version.'
<small class="text-muted">('.(!empty($revision) ? $revision : tr('In sviluppo')).')</small>
</span>
</footer>
<div class="modal fade" id="bs-popup" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" data-backdrop="static" data-keyboard="true"></div>
<div class="modal fade" id="bs-popup2" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" data-backdrop="static" data-keyboard="true"></div>';
<div id="modals">
<div class="modal fade" id="bs-popup" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" data-backdrop="static" data-keyboard="true"></div>
<div class="modal fade" id="bs-popup2" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" data-backdrop="static" data-keyboard="true"></div>
</div>';
}
echo '
</div><!-- ./wrapper -->';
@ -28,35 +30,33 @@ if (Auth::check()) {
<script> setInterval("session_keep_alive()", 5*60*1000); </script>';
}
if (!empty($debug)) {
if (App::debug()) {
echo '
<!-- Fix per le icone di debug -->
<style>div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-copy-clipboard:before, div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-database:before, div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-duration:before, div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-memory:before, div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-row-count:before, div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-stmt-id:before {
font-family: FontAwesome;
}</style>';
}</style>
<!-- Rimozione del messaggio automatico riguardante la modifica di valori nella pagina -->
<script>
window.onbeforeunload = null;
</script>';
echo $debugbarRenderer->render();
}
$custom_css = get_var('CSS Personalizzato');
$custom_css = setting('CSS Personalizzato');
if (!empty($custom_css)) {
echo '
<style>'.$custom_css.'</style>';
}
if (!empty($debugbarRenderer)) {
echo $debugbarRenderer->render();
}
// Rimozione del messaggio automatico riguardante la modifica di valori nella pagina
echo '
<script>
window.onbeforeunload = null;
</script>';
}
echo '
</body>
</html>';
// Retrocompatibilità
if (!empty($id_record) || basename($_SERVER['PHP_SELF']) == 'controller.php' || basename($_SERVER['PHP_SELF']) == 'index.php') {
unset($_SESSION['infos']);
unset($_SESSION['errors']);

View File

@ -1,15 +1,21 @@
<?php
$result['idarticolo'] = isset($result['idarticolo']) ? $result['idarticolo'] : null;
$_SESSION['superselect']['dir'] = $options['dir'];
$_SESSION['superselect']['idanagrafica'] = $options['idanagrafica'];
$_SESSION['superselect']['idarticolo'] = $options['idarticolo'];
// Articolo
if (!isset($options['edit_articolo']) || !empty($options['edit_articolo'])) {
echo '
<div class="row">
<div class="col-md-12">
{[ "type": "select", "label": "'.tr('Articolo').'", "name": "idarticolo", "required": 1, "value": "'.$result['idarticolo'].'", "ajax-source": "articoli" ]}
{[ "type": "select", "label": "'.tr('Articolo').'", "name": "idarticolo", "required": 1, "value": "'.$result['idarticolo'].'", "ajax-source": "articoli" '.(($options['dir'] == 'uscita') ? ',"icon-after": "add|'.Modules::get('Articoli')['id'].'"' : "").' ]}
</div>
</div>';
} else {
$database = Database::getConnection();
$database = database();
$articolo = $database->fetchArray('SELECT codice, descrizione FROM mg_articoli WHERE id = '.prepare($result['idarticolo']))[0];
echo '
@ -55,12 +61,21 @@ if (!isset($options['edit_articolo']) || !empty($options['edit_articolo'])) {
$("#idarticolo").on("change", function(){
// Autoimpostazione dei valori relativi
if ($(this).val()) {
session_set("superselect,idarticolo", $(this).val(), 0);
$data = $(this).selectData();
session_set("superselect,idanagrafica", "'.$options['idanagrafica'].'", 0);
session_set("superselect,dir", "'.$options['dir'].'", 0);
$data = $(this).selectData();
var id_conto = $data.idconto_'.($options['dir'] == 'entrata' ? 'vendita' : 'acquisto').';
$("#prezzo").val($data.prezzo_'.($options['dir'] == 'entrata' ? 'vendita' : 'acquisto').');
$("#descrizione_riga").val($data.descrizione);
$("#idiva").selectSet($data.idiva_vendita, $data.iva_vendita);
$("#idiva").selectSetNew($data.idiva_vendita, $data.iva_vendita);
if(id_conto) {
$("#idconto").selectSetNew(id_conto, $data.idconto_'.($options['dir'] == 'entrata' ? 'vendita' : 'acquisto').'_title);
}
$("#um").selectSetNew($data.um, $data.um);
}';

83
include/common/conti.php Normal file
View File

@ -0,0 +1,83 @@
<?php
// Informazioni aggiuntive per Fatture
if ($module['name'] != 'Fatture di acquisto' && $module['name'] != 'Fatture di vendita') {
return;
}
if ($options['dir'] == 'entrata') {
$show_rivalsa_inps = (setting('Percentuale rivalsa INPS') != '');
$show_ritenuta_acconto = (setting("Percentuale ritenuta d'acconto") != '');
$show_ritenuta_acconto |= !empty($options['id_ritenuta_acconto_predefined']);
} else {
$show_rivalsa_inps = 1;
$show_ritenuta_acconto = 1;
}
$show_calcolo_ritenuta_acconto = $show_ritenuta_acconto;
// Percentuale rivalsa INPS e Percentuale ritenuta d'acconto
if ($options['action'] == 'edit') {
$id_rivalsa_inps = $result['idrivalsainps'];
$id_ritenuta_acconto = $result['idritenutaacconto'];
$calcolo_ritenuta_acconto = $result['calcolo_ritenuta_acconto'];
} elseif ($options['action'] == 'add') {
// Fattura di acquisto
if ($options['dir'] == 'uscita') {
// TODO: Luca S. questi campi non dovrebbero essere definiti all'interno della scheda fornitore?
$id_rivalsa_inps = '';
$id_ritenuta_acconto = '';
}
// Fattura di vendita
elseif ($options['dir'] == 'entrata') {
// Caso particolare per aggiunta articolo
$id_rivalsa_inps = ($options['op'] == 'addarticolo') ? '' : setting('Percentuale rivalsa INPS');
$id_ritenuta_acconto = $options['id_ritenuta_acconto_predefined'] ?: setting("Percentuale ritenuta d'acconto");
}
}
$calcolo_ritenuta_acconto = $calcolo_ritenuta_acconto ?: setting("Metodologia calcolo ritenuta d'acconto predefinito");
if ($show_rivalsa_inps == 1 || $show_ritenuta_acconto == 1) {
echo '
<div class="row">';
// Rivalsa INPS
if ($show_rivalsa_inps == 1) {
echo '
<div class="col-md-4">
{[ "type": "select", "label": "'.tr('Rivalsa INPS').'", "name": "id_rivalsa_inps", "value": "'.$id_rivalsa_inps.'", "values": "query=SELECT * FROM co_rivalsainps" ]}
</div>';
}
// Ritenuta d'acconto
if ($show_ritenuta_acconto == 1) {
echo '
<div class="col-md-4">
{[ "type": "select", "label": "'.tr("Ritenuta d'acconto").'", "name": "id_ritenuta_acconto", "value": "'.$id_ritenuta_acconto.'", "values": "query=SELECT * FROM co_ritenutaacconto" ]}
</div>';
}
// Calcola ritenuta d'acconto su
if ($show_calcolo_ritenuta_acconto == 1) {
echo '
<div class="col-md-4">
{[ "type": "select", "label": "'.tr("Calcola ritenuta d'acconto su").'", "name": "calcolo_ritenuta_acconto", "value": "'.$calcolo_ritenuta_acconto.'", "values": "list=\"IMP\":\"Imponibile\", \"IMP+RIV\":\"Imponibile + rivalsa inps\"", "required": "1" ]}
</div>';
}
echo '
</div>';
}
// Conto
if (empty($options['hide_conto'])) {
echo '
<div class="row">
<div class="col-md-12">
{[ "type": "select", "label": "'.tr('Conto').'", "name": "idconto", "required": 1, "value": "'.$result['idconto'].'", "ajax-source": "'.$options['conti'].'" ]}
</div>
</div>';
}

View File

@ -1,12 +1,17 @@
<?php
$result['id'] = isset($result['id']) ? $result['id'] : null;
/*
Form di inserimento riga documento
*/
echo '
<form action="'.ROOTDIR.'/editor.php?id_module='.$id_module.'&id_record='.$id_record.'" method="post">
<input type="hidden" name="op" value="'.$options['op'].'">
<input type="hidden" name="id_plugin" value="'.$id_plugin.'">
<input type="hidden" name="hash" value="tab_'.$id_plugin.'">
<input type="hidden" name="backto" value="record-edit">
<input type="hidden" name="op" value="'.$options['op'].'">
<input type="hidden" name="idriga" value="'.$result['id'].'">
<input type="hidden" name="dir" value="'.$options['dir'].'">';

View File

@ -3,115 +3,14 @@
// Descrizione
echo App::internalLoad('descrizione.php', $result, $options);
$show_idrivalsainps = 0;
$show_idritenutaacconto = 0;
$show_calcolo_ritenutaacconto = 0;
$idrivalsainps = 0;
$idritenutaacconto = 0;
$calcolo_ritenutaacconto = 0;
// Informazioni aggiuntive per Fatture
if ($module['name'] == 'Fatture di acquisto' || $module['name'] == 'Fatture di vendita') {
// Percentuale rivalsa INPS e Percentuale ritenuta d'acconto
if ($options['action'] == 'edit'){
if($options['dir'] == 'uscita'){
$show_idrivalsainps = 1;
$show_idritenutaacconto = 1;
$show_calcolo_ritenutaacconto = 1;
}
else if (($options['dir'] == 'entrata' && ( get_var('Percentuale rivalsa INPS') != '' || get_var("Percentuale ritenuta d'acconto") != ''))) {
if( get_var('Percentuale rivalsa INPS') != '' ){ $show_idrivalsainps = 1; }else{ $show_idrivalsainps = 0; }
if( get_var("Percentuale ritenuta d'acconto") != '' ){ $show_idritenutaacconto = 1; }else{ $show_idritenutaacconto = 0; }
if( get_var("Percentuale ritenuta d'acconto") != '' ){ $show_calcolo_ritenutaacconto = 1; }else{ $show_calcolo_ritenutaacconto = 0; }
}
$idrivalsainps = $result['idrivalsainps'];
$idritenutaacconto = $result['idritenutaacconto'];
$calcolo_ritenutaacconto = $result['calcolo_ritenutaacconto'];
}
else if ($options['action'] == 'add'){
if($options['dir'] == 'uscita'){
$show_idrivalsainps = 1;
$show_idritenutaacconto = 1;
$show_calcolo_ritenutaacconto = 1;
$idrivalsainps = "";
$idritenutaacconto = "";
$calcolo_ritenutaacconto = get_var("Metodologia calcolo ritenuta d'acconto predefinito");
}
else if ($options['dir'] == 'entrata' && $options['op']=='addriga' && ( get_var('Percentuale rivalsa INPS') != '' || get_var("Percentuale ritenuta d'acconto") != '')) {
if( get_var('Percentuale rivalsa INPS') != '' ){ $show_idrivalsainps = 1; }else{ $show_idrivalsainps = 0; }
if( get_var("Percentuale ritenuta d'acconto") != '' ){ $show_idritenutaacconto = 1; }else{ $show_idritenutaacconto = 0; }
if( get_var("Percentuale ritenuta d'acconto") != '' ){ $show_calcolo_ritenutaacconto = 1; }else{ $show_calcolo_ritenutaacconto = 0; }
$idrivalsainps = get_var('Percentuale rivalsa INPS');
$idritenutaacconto = get_var("Percentuale ritenuta d'acconto");
$calcolo_ritenutaacconto = get_var("Metodologia calcolo ritenuta d'acconto predefinito");
}
//Caso particolare per aggiunta articolo in fatture di vendita
else if($options['dir'] == 'entrata' && $options['op']=='addarticolo' && ( get_var('Percentuale rivalsa INPS') != '' || get_var("Percentuale ritenuta d'acconto") != '')){
if( get_var('Percentuale rivalsa INPS') != '' ){ $show_idrivalsainps = 1; }else{ $show_idrivalsainps = 0; }
if( get_var("Percentuale ritenuta d'acconto") != '' ){ $show_idritenutaacconto = 1; }else{ $show_idritenutaacconto = 0; }
if( get_var("Percentuale ritenuta d'acconto") != '' ){ $show_calcolo_ritenutaacconto = 1; }else{ $show_calcolo_ritenutaacconto = 0; }
$idrivalsainps = "";
$idritenutaacconto = get_var("Percentuale ritenuta d'acconto");
$calcolo_ritenutaacconto = get_var("Metodologia calcolo ritenuta d'acconto predefinito");
}
}
if($show_idrivalsainps==1 || $show_idritenutaacconto==1){
echo '
<div class="row">';
// Rivalsa INPS
if ( $show_idrivalsainps == 1 ) {
echo '
<div class="col-md-4">
{[ "type": "select", "label": "'.tr('Rivalsa INPS').'", "name": "idrivalsainps", "value": "'.$idrivalsainps.'", "values": "query=SELECT * FROM co_rivalsainps" ]}
</div>';
}
// Ritenuta d'acconto
if ( $show_idritenutaacconto == 1 ) {
echo '
<div class="col-md-4">
{[ "type": "select", "label": "'.tr("Ritenuta d'acconto").'", "name": "idritenutaacconto", "value": "'.$idritenutaacconto.'", "values": "query=SELECT * FROM co_ritenutaacconto" ]}
</div>';
}
//Calcola ritenuta d'acconto su
if ( $show_calcolo_ritenutaacconto == 1 ) {
echo '
<div class="col-md-4">
{[ "type": "select", "label": "'.tr("Calcola ritenuta d'acconto su").'", "name": "calcolo_ritenutaacconto", "value": "'.$calcolo_ritenutaacconto.'", "values": "list=\"Imponibile\":\"Imponibile\", \"Imponibile + rivalsa inps\":\"Imponibile + rivalsa inps\"", "required": "1" ]}
</div>';
}
echo '
</div>';
}
// Conto
echo '
<div class="row">
<div class="col-md-12">
{[ "type": "select", "label": "'.tr('Conto').'", "name": "idconto", "required": 1, "value": "'.$result['idconto'].'", "ajax-source": "'.$options['conti'].'" ]}
</div>
</div>';
}
// Conti, rivalsa INPS e ritenuta d'acconto
echo App::internalLoad('conti.php', $result, $options);
// Iva
echo '
<div class="row">
<div class="col-md-4">
{[ "type": "select", "label": "'.tr('Iva').'", "name": "idiva", "required": 1, "value": "'.$result['idiva'].'", "values": "query=SELECT * FROM co_iva ORDER BY descrizione ASC" ]}
{[ "type": "select", "label": "'.tr('Iva').'", "name": "idiva", "required": 1, "value": "'.$result['idiva'].'", "ajax-source": "iva" ]}
</div>';
// Quantità
@ -127,16 +26,128 @@ echo '
</div>
</div>';
// Costo unitario
echo '
<div class="row">
<div class="col-md-6">
{[ "type": "number", "label": "'.tr('Costo unitario').'", "name": "prezzo", "value": "'.$result['prezzo'].'", "required": 1, "icon-after": "&euro;" ]}
<div class="row">';
$width = $options['dir'] == 'entrata' ? 4 : 6;
$label = $options['dir'] == 'entrata' ? tr('Prezzo unitario di vendita') : tr('Prezzo unitario');
if ($options['dir'] == 'entrata') {
// Prezzo di acquisto unitario
echo '
<div class="col-md-'.$width.'">
{[ "type": "number", "label": "'.tr('Prezzo unitario di acquisto').'", "name": "prezzo_acquisto", "value": "'.$result['prezzo_unitario_acquisto'].'", "icon-after": "&euro;" ]}
</div>';
// Funzione per l'aggiornamento in tempo reale del guadagno
echo '
<script>
function aggiorna_guadagno() {
var prezzo_acquisto = $("#prezzo_acquisto").val().toEnglish();
var prezzo = $("#prezzo").val().toEnglish();
var sconto = $("#sconto").val().toEnglish();
if ($("#tipo_sconto").val() === "PRC") {
sconto = sconto / 100 * prezzo;
}
var guadagno = prezzo - sconto - prezzo_acquisto;
var parent = $("#prezzo_acquisto").closest("div").parent();
var div = parent.find("div[id*=\"errors\"]");
div.html("<small>'.tr('Guadagno').': " + guadagno.toLocale() + " &euro;</small>");
if (guadagno < 0) {
parent.addClass("has-error");
div.addClass("text-danger").removeClass("text-success");
} else {
parent.removeClass("has-error");
div.removeClass("text-danger").addClass("text-success");
}
}
aggiorna_guadagno();
$("#prezzo").keyup(aggiorna_guadagno);
$("#prezzo_acquisto").keyup(aggiorna_guadagno);
$("#sconto").keyup(aggiorna_guadagno);
$("#tipo_sconto").change(aggiorna_guadagno);
</script>';
}
// Prezzo di vendita unitario
echo '
<div class="col-md-'.$width.'">
{[ "type": "number", "label": "'.$label.'", "name": "prezzo", "value": "'.$result['prezzo'].'", "required": 1, "icon-after": "&euro;" ]}
</div>';
// Sconto unitario
echo '
<div class="col-md-6">
<div class="col-md-'.$width.'">
{[ "type": "number", "label": "'.tr('Sconto unitario').'", "name": "sconto", "value": "'.$result['sconto_unitario'].'", "icon-after": "choice|untprc|'.$result['tipo_sconto'].'" ]}
</div>
</div>';
if ($module['name'] == 'Fatture di vendita') {
$collapsed = empty($result['data_inizio_periodo']) && empty($result['data_fine_periodo']) && empty($result['riferimento_amministrazione']);
echo '
<div class="box box-info '.($collapsed ? 'collapsed-box' : '').'">
<div class="box-header with-border">
<h3 class="box-title">'.tr('Dati Fatturazione Elettronica').'</h3>
<div class="box-tools pull-right">
<button type="button" class="btn btn-box-tool" data-widget="collapse">
<i class="fa fa-plus"></i>
</button>
</div>
</div>
<div class="box-body">';
$tipi_cessione_prestazione = [
[
'id' => 'SC',
'text' => 'SC - '.tr('Sconto'),
],
[
'id' => 'PR',
'text' => 'PR - '.tr('Premio'),
],
[
'id' => 'AB',
'text' => 'AB - '.tr('Abbuono'),
],
[
'id' => 'AC',
'text' => 'AC - '.tr('Spesa accessoria'),
],
];
// Data inizio periodo
echo '
<div class="row">
<div class="col-md-6">
{[ "type": "select", "label": "'.tr('Tipo Cessione Prestazione').'", "name": "tipo_cessione_prestazione", "value": "'.$result['tipo_cessione_prestazione'].'", "values": '.json_encode($tipi_cessione_prestazione).' ]}
</div>';
// Riferimento amministrazione
echo '
<div class="col-md-6">
{[ "type": "text", "label": "'.tr('Riferimento Amministrazione').'", "name": "riferimento_amministrazione", "value": "'.$result['riferimento_amministrazione'].'", "maxlength": 20 ]}
</div>
</div>';
// Data inizio periodo
echo '
<div class="row">
<div class="col-md-6">
{[ "type": "date", "label": "'.tr('Data Inizio Periodo').'", "name": "data_inizio_periodo", "value": "'.$result['data_inizio_periodo'].'" ]}
</div>';
// Data fine periodo
echo '
<div class="col-md-6">
{[ "type": "date", "label": "'.tr('Data Fine Periodo').'", "name": "data_fine_periodo", "value": "'.$result['data_fine_periodo'].'" ]}
</div>
</div>';
echo '
</div>
</div>';
}

View File

@ -1,6 +1,6 @@
<?php
include_once __DIR__.'/../core.php';
include_once __DIR__.'/../../core.php';
$valid_config = isset($db_host) && isset($db_name) && isset($db_username) && isset($db_password);
@ -11,11 +11,7 @@ if (file_exists('config.inc.php') && $valid_config && $dbo->isConnected()) {
$pageTitle = tr('Configurazione');
if (file_exists($docroot.'/include/custom/top.php')) {
include_once $docroot.'/include/custom/top.php';
} else {
include_once $docroot.'/include/top.php';
}
include_once App::filepath('include|custom|', 'top.php');
// Controllo sull'esistenza di nuovi parametri di configurazione
if (post('db_host') !== null) {
@ -97,10 +93,6 @@ if (post('db_host') !== null) {
exit();
}
// Salvataggio dei valori da salvare successivamente
$_SESSION['osm_password'] = post('osm_password');
$_SESSION['osm_email'] = post('osm_email');
// Creazione della configurazione
if ($dbo->isConnected()) {
// Impostazioni di configurazione strettamente necessarie al funzionamento del progetto
@ -239,27 +231,42 @@ if (empty($creation) && (!file_exists('config.inc.php') || !$valid_config)) {
return result;
});
$("#install").on("click", function(){
if($(this).closest("form").parsley().validate()){
prev_html = $("#install").html();
$("#install").html("<i class=\'fa fa-spinner fa-pulse fa-fw\'></i> '.tr('Attendere').'...");
$("#install").prop(\'disabled\', true);
$("#test").prop(\'disabled\', true);
$("#config-form").submit();
}
});
$("#test").on("click", function(){
if($(this).closest("form").parsley().validate()){
prev_html = $("#test").html();
$("#test").html("<i class=\'fa fa-spinner fa-pulse fa-fw\'></i> '.tr("Attendere").'...");
$("#test").prop(\'disabled\', true);
$("#test").html("<i class=\'fa fa-spinner fa-pulse fa-fw\'></i> '.tr('Attendere').'...");
$("#test").prop(\'disabled\', true);
$("#install").prop(\'disabled\', true);
$(this).closest("form").ajaxSubmit({
url: "'.$rootdir.'/index.php",
data: {
test: 1,
},
type: "post",
type: "post",
success: function(data){
data = parseFloat(data.trim());
$("#test").html(prev_html);
$("#test").prop(\'disabled\', false);
$("#test").prop(\'disabled\', false);
$("#install").prop(\'disabled\', false);
if(data == 0){
swal("'.tr('Errore della configurazione').'", "'.tr('La configurazione non è corretta').'.", "error");
} else if(data == 1){
swal("'.tr('Permessi insufficienti').'", "'.tr("L'utente non possiede permessi sufficienti per il corretto funzionamento del software").'.", "error");
swal("'.tr('Permessi insufficienti').'", "'.tr("L'utente non possiede permessi sufficienti per il testing della connessione. Potresti rilevare problemi in fase di installazione.").'.", "error");
} else {
swal("'.tr('Configurazione corretta').'", "'.tr('Ti sei connesso con successo al database').'. '.tr("Clicca su 'Installa' per proseguire").'.", "success");
}
@ -282,7 +289,6 @@ if (empty($creation) && (!file_exists('config.inc.php') || !$valid_config)) {
<div class="box-body" id="smartwizard">';
// REQUISITI PER IL CORRETTO FUNZIONAMENTO
echo '
<ul>
<li><a href="#step-1">
@ -300,154 +306,25 @@ if (empty($creation) && (!file_exists('config.inc.php') || !$valid_config)) {
<div id="steps">
<div id="step-1">
<p>'.tr('Benvenuto in <strong>OpenSTAManager</strong>!').'</p>
<p>'.tr("Prima di procedere alla configurazione e all'installazione del software, sono necessari alcuni accorgimenti per garantire il corretto funzionamento del gestionale").'. '.tr('Stai utilizzando la versione PHP _PHP_', [
'_PHP_' => phpversion(),
]).'.</p>
<hr>';
<div id="step-1">';
// Estensioni di PHP
// Introduzione
echo '
<p>'.tr('Benvenuto in _NAME_!', [
'_NAME_' => '<strong>OpenSTAManager</strong>',
]).'</p>
<p>'.tr("Prima di procedere alla configurazione e all'installazione del software, sono necessari alcuni accorgimenti per garantire il corretto funzionamento del gestionale").'.</p>
<br>
<p>'.tr('Le estensioni e impostazioni PHP possono essere personalizzate nel file di configurazione _FILE_', [
'_FILE_' => '<b>php.ini</b>',
]).'.</p>
<hr>';
// REQUISITI PER IL CORRETTO FUNZIONAMENTO
include __DIR__.'/requirements.php';
<div class="row">
<div class="col-md-6">
<p>'.tr('Le seguenti estensioni PHP devono essere abilitate dal file di configurazione _FILE_', [
'_FILE_' => '<b>php.ini</b>',
]).':</p>
<div class="list-group">';
$extensions = [
'zip' => tr("Necessario per l'utilizzo delle funzioni di aggiornamento automatico e backup, oltre che per eventuali moduli aggiuntivi"),
'pdo_mysql' => tr('Necessario per la connessione al database'),
'openssl' => tr('Utile per la generazione di chiavi complesse (facoltativo)'),
'intl' => tr('Utile per la gestione automatizzata della conversione numerica (facoltativo)'),
];
foreach ($extensions as $key => $value) {
$check = extension_loaded($key);
echo '
<div class="list-group-item">
<h4 class="list-group-item-heading">
'.$key;
if ($check) {
echo '
<span class="label label-success pull-right">
<i class="fa fa-check"></i>
</span>';
} else {
echo '
<span class="label label-danger pull-right">
<i class="fa fa-times"></i>
</span>';
}
echo '
</h4>
<p class="list-group-item-text">'.$value.'</p>
</div>';
}
echo '
</div>
<hr>
</div>';
// Impostazione di valore per PHP
echo '
<div class="col-md-6">
<p>'.tr('Le seguenti impostazioni PHP devono essere modificate nel file di configurazione _FILE_', [
'_FILE_' => '<b>php.ini</b>',
]).':</p>
<div class="list-group">';
$values = [
'display_errors' => true,
'upload_max_filesize' => '>16M',
'post_max_size' => '>16M',
];
foreach ($values as $key => $value) {
$ini = str_replace(['k', 'M'], ['000', '000000'], ini_get($key));
$real = str_replace(['k', 'M'], ['000', '000000'], $value);
if (starts_with($real, '>')) {
$check = $ini >= substr($real, 1);
} elseif (starts_with($real, '<')) {
$check = $ini <= substr($real, 1);
} else {
$check = ($real == $ini);
}
if (is_bool($value)) {
$value = !empty($value) ? 'On' : 'Off';
} else {
$value = str_replace(['>', '<'], '', $value);
}
echo '
<div class="list-group-item">
<h4 class="list-group-item-heading">
'.$key;
if ($check) {
echo '
<span class="label label-success pull-right">
<i class="fa fa-check"></i>
</span>';
} else {
echo '
<span class="label label-danger pull-right">
<i class="fa fa-times"></i>
</span>';
}
echo '
</h4>
<p class="list-group-item-text">'.tr('Valore consigliato').': '.$value.'</p>
</div>';
}
echo '
</div>
<hr>
</div>
</div>';
// Percorsi necessari
echo '
<div class="row">
<div class="col-md-12">
<p>'.tr('Le seguenti cartelle devono risultare scrivibili da parte del gestionale').':</p>
<div class="list-group">';
$dirs = [
'backup' => tr('Necessario per il salvataggio dei backup'),
'files' => tr('Necessario per il salvataggio di file inseriti dagli utenti'),
'logs' => tr('Necessario per la gestione dei file di log'),
];
foreach ($dirs as $key => $value) {
$check = is_writable($docroot.DIRECTORY_SEPARATOR.$key);
echo '
<div class="list-group-item">
<h4 class="list-group-item-heading">
'.$key;
if ($check) {
echo '
<span class="label label-success pull-right">
<i class="fa fa-check"></i>
</span>';
} else {
echo '
<span class="label label-danger pull-right">
<i class="fa fa-times"></i>
</span>';
}
echo '
</h4>
<p class="list-group-item-text">'.$value.'</p>
</div>';
}
echo '
</div>
<hr>
</div>
</div>
</div>';
// LICENZA
@ -477,8 +354,6 @@ if (empty($creation) && (!file_exists('config.inc.php') || !$valid_config)) {
$username = !empty($db_username) ? $db_username : '';
$password = !empty($db_password) ? $db_password : '';
$name = !empty($db_name) ? $db_name : '';
$osm_password = !empty($_SESSION['osm_password']) ? $_SESSION['osm_password'] : '';
$osm_email = !empty($_SESSION['osm_email']) ? $_SESSION['osm_email'] : '';
// PARAMETRI
echo '
@ -492,13 +367,13 @@ if (empty($creation) && (!file_exists('config.inc.php') || !$valid_config)) {
// Form dei parametri
echo '
<form action="?action=updateconfig&firstuse=true" method="post" id="config_form">
<form action="?action=updateconfig&firstuse=true" method="post" id="config-form">
<div class="row">';
// db_host
echo '
<div class="col-md-12">
{[ "type": "text", "label": "'.tr('Host del database').'", "name": "db_host", "placeholder": "'.tr("Indirizzo dell'host del database").'", "value": "'.$host.'", "help": "'.tr('Esempio').': localhost", "show-help": 1, "required": 1 ]}
{[ "type": "text", "label": "'.tr('Host del database').'", "name": "db_host", "placeholder": "'.tr("Indirizzo dell'host del database").'", "value": "'.$host.'", "help": "'.tr('Esempio').': localhost", "show-help": 0, "required": 1 ]}
</div>
</div>
@ -507,40 +382,22 @@ if (empty($creation) && (!file_exists('config.inc.php') || !$valid_config)) {
// db_username
echo '
<div class="col-md-4">
{[ "type": "text", "label": "'.tr("Username dell'utente MySQL").'", "name": "db_username", "placeholder": "'.tr("Username dell'utente MySQL").'", "value": "'.$username.'", "help": "'.tr('Esempio').': root", "show-help": 1, "required": 1 ]}
{[ "type": "text", "label": "'.tr("Username dell'utente MySQL").'", "name": "db_username", "placeholder": "'.tr("Username dell'utente MySQL").'", "value": "'.$username.'", "help": "'.tr('Esempio').': root", "show-help": 0, "required": 1 ]}
</div>';
// db_password
echo '
<div class="col-md-4">
{[ "type": "password", "label": "'.tr("Password dell'utente MySQL").'", "name": "db_password", "placeholder": "'.tr("Password dell'utente MySQL").'", "value": "'.$password.'", "help": "'.tr('Esempio').': mysql", "show-help": 1 ]}
{[ "type": "password", "label": "'.tr("Password dell'utente MySQL").'", "name": "db_password", "placeholder": "'.tr("Password dell'utente MySQL").'", "value": "'.$password.'", "help": "'.tr('Esempio').': mysql", "show-help": 0 ]}
</div>';
// db_name
echo '
<div class="col-md-4">
{[ "type": "text", "label": "'.tr('Nome del database').'", "name": "db_name", "placeholder": "'.tr('Nome del database').'", "value": "'.$name.'", "help": "'.tr('Esempio').': openstamanager", "show-help": 1, "required": 1 ]}
</div>
</div>
<div class="row">';
// Password utente admin
echo '
<div class="col-md-6">
{[ "type": "password", "label": "'.tr("Password dell'amministratore").'", "name": "osm_password", "placeholder": "'.tr('Scegli la password di amministratore').'", "value": "'.$osm_password.'", "help": "'.tr('Valore di default').': admin", "show-help": 1 ]}
</div>';
// Email utente admin
echo '
<div class="col-md-6">
{[ "type": "email", "label": "'.tr("Email dell'amministratore").'", "name": "osm_email", "placeholder": "'.tr("Digita l'indirizzo email dell'amministratore").'", "value": "'.$osm_email.'" ]}
{[ "type": "text", "label": "'.tr('Nome del database').'", "name": "db_name", "placeholder": "'.tr('Nome del database').'", "value": "'.$name.'", "help": "'.tr('Esempio').': openstamanager", "show-help": 0, "required": 1 ]}
</div>
</div>';
echo '
';
echo '
<!-- PULSANTI -->
<div class="row">
@ -553,13 +410,12 @@ if (empty($creation) && (!file_exists('config.inc.php') || !$valid_config)) {
</button>
</div>
<div class="col-md-4 text-right">
<button type="submit" class="btn btn-success btn-block">
<button type="submit" id="install" class="btn btn-success btn-block">
<i class="fa fa-check"></i> '.tr('Installa').'
</button>
</div>
</div>
</form>
</div>
@ -568,10 +424,6 @@ if (empty($creation) && (!file_exists('config.inc.php') || !$valid_config)) {
</div>';
}
if (file_exists($docroot.'/include/custom/bottom.php')) {
include_once $docroot.'/include/custom/bottom.php';
} else {
include_once $docroot.'/include/bottom.php';
}
include_once App::filepath('include|custom|', 'bottom.php');
exit();

233
include/init/init.php Normal file
View File

@ -0,0 +1,233 @@
<?php
include_once __DIR__.'/../../core.php';
if (Update::isUpdateAvailable() || !$dbo->isInstalled()) {
return;
}
$has_azienda = $dbo->fetchNum("SELECT `an_anagrafiche`.`idanagrafica` FROM `an_anagrafiche`
LEFT JOIN `an_tipianagrafiche_anagrafiche` ON `an_anagrafiche`.`idanagrafica`=`an_tipianagrafiche_anagrafiche`.`idanagrafica`
LEFT JOIN `an_tipianagrafiche` ON `an_tipianagrafiche`.`idtipoanagrafica`=`an_tipianagrafiche_anagrafiche`.`idtipoanagrafica`
WHERE `an_tipianagrafiche`.`descrizione` = 'Azienda' AND `an_anagrafiche`.`deleted_at` IS NULL") != 0;
$has_user = $dbo->fetchNum('SELECT `id` FROM `zz_users`') != 0;
$settings = [
'Regime Fiscale' => true,
'Tipo Cassa' => true,
'Conto predefinito fatture di vendita' => true,
'Conto predefinito fatture di acquisto' => true,
"Percentuale ritenuta d'acconto" => false,
"Causale ritenuta d'acconto" => false,
];
if (!empty(setting("Percentuale ritenuta d'acconto"))) {
$settings["Causale ritenuta d'acconto"] = true;
}
$has_settings = true;
foreach ($settings as $setting => $required) {
if (empty(setting($setting)) && $required) {
$has_settings = false;
break;
}
}
if ($has_azienda && $has_user && $has_settings) {
return;
}
$pageTitle = tr('Inizializzazione');
include_once App::filepath('include|custom|', 'top.php');
// Controllo sull'esistenza di nuovi parametri di configurazione
if (post('action') == 'init') {
// Azienda predefinita
if (!$has_azienda) {
Filter::set('post', 'op', 'add');
$id_module = Modules::get('Anagrafiche')['id'];
include DOCROOT.'/modules/anagrafiche/actions.php';
// Logo stampe
if (!empty($_FILES) && !empty($_FILES['blob']['name'])) {
$file = Uploads::upload($_FILES['blob'], [
'name' => 'Logo stampe',
'id_module' => $id_module,
'id_record' => $id_record,
]);
Settings::setValue('Logo stampe', $file);
}
}
// Utente amministratore
if (!$has_user) {
$admin = $dbo->selectOne('zz_groups', ['id'], [
'nome' => 'Amministratori',
]);
$dbo->insert('zz_users', [
'username' => post('admin_username'),
'password' => Auth::hashPassword(post('admin_password')),
'email' => post('admin_email'),
'idgruppo' => $admin['id'],
'idanagrafica' => isset($id_record) ? $id_record : 0,
'enabled' => 1,
]);
}
if (!$has_settings) {
foreach ($settings as $setting => $required) {
$setting = Settings::get($setting);
$value = post('setting')[$setting['id']];
if (!empty($value)) {
Settings::setValue($setting['nome'], $value);
}
}
}
redirect(ROOTDIR, 'js');
exit();
}
$img = App::getPaths()['img'];
// Visualizzazione dell'interfaccia di impostazione iniziale, nel caso il file di configurazione sia mancante oppure i paramentri non siano sufficienti
echo '
<div class="box box-center-large box-warning">
<div class="box-header with-border text-center">
<img src="'.$img.'/logo.png" alt="'.tr('OSM Logo').'">
<h3 class="box-title">'.tr('OpenSTAManager').'</h3>
</div>
<div class="box-body">
<form action="" method="post" id="init-form" enctype="multipart/form-data">
<input type="hidden" name="action" value="init">';
if (!$has_user) {
echo '
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">'.tr('Amministrazione').'</h3>
</div>
<div class="panel-body">
<div class="row">
<div class="col-md-6">
{[ "type": "text", "label": "'.tr('Username').'", "name": "admin_username", "value": "'.$osm_password.'", "placeholder": "'.tr("Digita l'username dell'amministratore").'", "required": 1 ]}
</div>
<div class="col-md-6">
{[ "type": "password", "label": "'.tr('Password').'", "id": "password", "name": "admin_password", "value": "'.$osm_password.'", "placeholder": "'.tr("Digita la password dell'amministratore").'", "required": 1, "icon-after": "<i onclick=\" if ($(this).parent().find(\'i\').hasClass(\'fa-eye\')) { $(\'#password\').attr(\'type\', \'text\'); $(this).parent().find(\'i\').removeClass(\'fa-eye\').addClass(\'fa-eye-slash\'); $(this).parent().find(\'i\').attr(\'title\', \'Nascondi password\'); } else { $(\'#password\').attr(\'type\', \'password\'); $(this).parent().find(\'i\').removeClass(\'fa-eye-slash\').addClass(\'fa-eye\'); $(this).parent().find(\'i\').attr(\'title\', \'Visualizza password\'); } \" title=\"'.tr('Visualizza password').'\" class=\"fa fa-eye clickable\" ></i>" ]}
</div>
<div class="col-md-6">
{[ "type": "email", "label": "'.tr('Email').'", "name": "admin_email", "value": "'.$osm_email.'", "placeholder": "'.tr("Digita l'indirizzo email dell'amministratore").'" ]}
</div>
</div>
</div>
</div>';
}
if (!$has_azienda) {
echo '
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">'.tr('Azienda predefinita').'</h3>
</div>
<div class="panel-body">';
$idtipoanagrafica = $dbo->fetchArray("SELECT idtipoanagrafica FROM an_tipianagrafiche WHERE descrizione='Azienda'")[0]['idtipoanagrafica'];
$readonly_tipo = true;
ob_start();
include DOCROOT.'/modules/anagrafiche/add.php';
$anagrafica = ob_get_clean();
echo str_replace('</form>', '', $anagrafica);
echo '
<div class="box box-success collapsed-box">
<div class="box-header with-border">
<h3 class="box-title">'.tr('Logo stampe').'</h3>
<div class="box-tools pull-right">
<button type="button" class="btn btn-box-tool" data-widget="collapse">
<i class="fa fa-plus"></i>
</button>
</div>
</div>
<div class="box-body collapse">
<div class="col-md-12">
{[ "type": "file", "placeholder": "'.tr('File').'", "name": "blob" ]}
</div>
<p>&nbsp;</p><div class="col-md-12 alert alert-info text-center">'.tr('Per impostare il logo delle stampe, caricare un file ".jpg". Risoluzione consigliata 302x111 pixel').'.</div>
</div>
</div>';
echo '
</div>
</div>';
}
if (!$has_settings) {
echo '
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">'.tr('Impostazioni di base').'</h3>
</div>
<div class="panel-body">';
foreach ($settings as $setting => $required) {
if (empty(setting($setting))) {
echo '
<div class="col-md-6">
'.Settings::input($setting, $required).'
</div>';
}
}
echo '
</div>
</div>';
}
echo '
<!-- PULSANTI -->
<div class="row">
<div class="col-md-4">
<span>*<small><small>'.tr('Campi obbligatori').'</small></small></span>
</div>
<div class="col-md-4 text-right">
<button type="submit" id="config" class="btn btn-success btn-block">
<i class="fa fa-cog"></i> '.tr('Configura').'
</button>
</div>
</div>
</form>
</div>
</div>';
echo '
<script>
$(document).ready(function(){
$("button[type=submit]").not("#config").remove();
});
</script>
<script src="'.$rootdir.'/lib/functions.js"></script>
<script src="'.$rootdir.'/lib/init.js"></script>';
include_once App::filepath('include|custom|', 'bottom.php');
exit();

View File

@ -0,0 +1,190 @@
<?php
// Apache
$modules = [
'mod_rewrite' => [
'server' => 'HTTP_MOD_REWRITE',
'description' => tr('Fornisce un sistema di riscrittura URL basato su regole predefinite'),
],
];
if (function_exists('apache_get_modules')) {
$available_modules = apache_get_modules();
}
$apache = [];
foreach ($modules as $name => $values) {
$description = $values['description'];
$status = isset($available_modules) ? in_array($name, $available_modules) : $_SERVER[$values['server']] == 'On';
$apache[] = [
'name' => $name,
'description' => $description,
'status' => $status,
'type' => tr('Modulo'),
];
}
// PHP
$settings = [
'zip' => [
'type' => 'ext',
'description' => tr('Permette di leggere e scrivere gli archivi compressi ZIP e i file al loro interno'),
],
'mbstring' => [
'type' => 'ext',
'description' => tr('Permette di gestire i caratteri dello standard UTF-8'),
],
'pdo_mysql' => [
'type' => 'ext',
'description' => tr('Permette di effettuare la connessione al database MySQL'),
],
'dom' => [
'type' => 'ext',
'description' => tr('Permette la gestione dei file standard per la Fatturazione Elettronica'),
],
'xsl' => [
'type' => 'ext',
'description' => tr('Permette di visualizzazione grafica della Fattura Elettronica'),
],
'openssl' => [
'type' => 'ext',
'description' => tr("Permette l'utilizzo di funzioni crittografiche simmetriche e asimmetriche"),
],
'intl' => [
'type' => 'ext',
'description' => tr("Permette l'automazione della conversione dei numeri"),
],
'curl' => [
'type' => 'ext',
'description' => tr('Permette la comunicazione con servizi esterni'),
],
'soap' => [
'type' => 'ext',
'description' => tr('Permette la comunicazione con servizi esterni, quali il database europeo delle Partite IVA (facoltativo)'),
],
//'display_errors' => [
// 'type' => 'value',
// 'description' => true,
//],
'upload_max_filesize' => [
'type' => 'value',
'description' => '>16M',
],
'post_max_size' => [
'type' => 'value',
'description' => '>16M',
],
];
$php = [];
foreach ($settings as $name => $values) {
$description = $values['description'];
if ($values['type'] == 'ext') {
$status = extension_loaded($name);
} else {
$ini = str_replace(['k', 'M'], ['000', '000000'], ini_get($name));
$real = str_replace(['k', 'M'], ['000', '000000'], $description);
if (starts_with($real, '>')) {
$status = $ini >= substr($real, 1);
} elseif (starts_with($real, '<')) {
$status = $ini <= substr($real, 1);
} else {
$status = ($real == $ini);
}
if (is_bool($description)) {
$description = !empty($description) ? 'On' : 'Off';
} else {
$description = str_replace(['>', '<'], '', $description);
}
$description = tr('Valore consigliato: _VALUE_ (Valore attuale: _INI_)', [
'_VALUE_' => $description,
'_INI_' => ini_get($name),
]);
}
$type = ($values['type'] == 'ext') ? tr('Estensione') : tr('Impostazione');
$php[] = [
'name' => $name,
'description' => $description,
'status' => $status,
'type' => $type,
];
}
// Percorsi di servizio
$dirs = [
'backup' => tr('Necessario per il salvataggio dei backup'),
'files' => tr('Necessario per il salvataggio di file inseriti dagli utenti'),
'logs' => tr('Necessario per la gestione dei file di log'),
];
$directories = [];
foreach ($dirs as $name => $description) {
$status = is_writable($docroot.DIRECTORY_SEPARATOR.$name);
$directories[] = [
'name' => $name,
'description' => $description,
'status' => $status,
'type' => tr('Cartella'),
];
}
$requirements = [
tr('Apache') => $apache,
tr('PHP (_VERSION_)', [
'_VERSION_' => phpversion(),
]) => $php,
tr('Percorsi di servizio') => $directories,
];
// Tabelle di riepilogo
foreach ($requirements as $key => $values) {
$statuses = array_column($values, 'status');
$general_status = true;
foreach ($statuses as $status) {
$general_status &= $status;
}
echo '
<div class="box box-'.($general_status ? 'success collapsed-box' : 'danger').'">
<div class="box-header with-border">
<h3 class="box-title">'.$key.'</h3>';
if ($general_status) {
echo '
<div class="box-tools pull-right">
<button type="button" class="btn btn-box-tool" data-widget="collapse">
<i class="fa fa-plus"></i>
</button>
</div>';
}
echo '
</div>
<div class="box-body no-padding">
<table class="table">';
foreach ($values as $value) {
echo '
<tr class="'.($value['status'] ? 'success' : 'danger').'">
<td style="width: 10px"><i class="fa fa-'.($value['status'] ? 'check' : 'times').'"></i></td>
<td>'.$value['type'].'</td>
<td>'.$value['name'].'</td>
<td>'.$value['description'].'</td>
</tr>';
}
echo '
</table>
</div>
</div>';
}

View File

@ -1,6 +1,6 @@
<?php
include_once __DIR__.'/../core.php';
include_once __DIR__.'/../../core.php';
$updateRate = 20;
$scriptValue = $updateRate * 5;
@ -11,7 +11,7 @@ $scriptValue = $updateRate * 5;
if (filter('action') == 'do_update') {
// Aggiornamento in progresso
if (Update::isUpdateAvailable()) {
$update = Update::getUpdate();
$update = Update::getCurrentUpdate();
$result = Update::doUpdate($updateRate);
@ -19,7 +19,7 @@ if (filter('action') == 'do_update') {
// Aggiunta del messaggio generico riguardante l'aggiornamento
echo '
<script>
addVersion("'.$update['version'].'");
addVersion("'.$update['name'].'");
</script>';
if (is_array($result)) {
@ -73,44 +73,20 @@ if (filter('action') == 'do_update') {
Update::updateCleanup();
echo '
<p><strong>'.tr('Aggiornamento completato!!!').'</strong> <i class="fa fa-smile-o"></i></p>';
// Rimostro la finestra di login
echo '
<script>
$(".login-box").fadeIn();
</script>';
<p><strong>'.tr('Aggiornamento completato').'</strong> <i class="fa fa-smile-o"></i></p>';
// Istruzioni per la prima installazione
if ($_GET['firstuse'] == 'true') {
if (!empty($_SESSION['osm_password'])) {
$password = $_SESSION['osm_password'];
} else {
$password = 'admin';
}
echo '
<p>'.tr('Puoi procedere al login con i seguenti dati').':</p>
<p>'.tr('Username').': <i>admin</i></p>
<p>'.tr('Password').': <i> '.$password.'</i></p>
<p class="text-danger">'.tr("E' fortemente consigliato rimuovere i permessi di scrittura dal file _FILE_", [
'_FILE_' => '<b>config.inc.php</b>',
]).'.</p>';
// Imposto la password di admin che l'utente ha selezionato all'inizio
if (isset($_SESSION['osm_password'])) {
$dbo->query('UPDATE `zz_users` SET `password`='.prepare(Auth::hashPassword($password))." WHERE `username`='admin'");
unset($_SESSION['osm_password']);
}
if (isset($_SESSION['osm_email'])) {
if (!empty($_SESSION['osm_email'])) {
$dbo->query('UPDATE `zz_users` SET `email`='.prepare($_SESSION['osm_email'])." WHERE `username`='admin' ");
}
unset($_SESSION['osm_email']);
}
}
echo '
<a class="btn btn-success btn-block" href="'.ROOTDIR.'">
<i class="fa fa-check"></i> '.tr('Continua').'
</a>';
}
exit();
@ -119,11 +95,7 @@ if (filter('action') == 'do_update') {
if (Update::isUpdateLocked() && filter('force') === null) {
$pageTitle = tr('Aggiornamento in corso!');
if (file_exists($docroot.'/include/custom/top.php')) {
include_once $docroot.'/include/custom/top.php';
} else {
include_once $docroot.'/include/top.php';
}
include_once App::filepath('include|custom|', 'top.php');
echo '
<div class="box box-center box-danger box-solid text-center">
@ -137,11 +109,7 @@ if (filter('action') == 'do_update') {
</div>
</div>';
if (file_exists($docroot.'/include/custom/bottom.php')) {
include_once $docroot.'/include/custom/bottom.php';
} else {
include_once $docroot.'/include/bottom.php';
}
include_once App::filepath('include|custom|', 'bottom.php');
exit();
}
@ -151,11 +119,7 @@ if (filter('action') == 'do_update') {
$button = !$dbo->isInstalled() ? tr('Installa!') : tr('Aggiorna!');
$pageTitle = !$dbo->isInstalled() ? tr('Installazione') : tr('Aggiornamento');
if (file_exists($docroot.'/include/custom/top.php')) {
include_once $docroot.'/include/custom/top.php';
} else {
include_once $docroot.'/include/top.php';
}
include_once App::filepath('include|custom|', 'top.php');
echo '
<div class="box box-center-large box-warning text-center">
@ -214,7 +178,7 @@ if (filter('action') == 'do_update') {
<div id="result"></div>';
$total = 0;
$updates = Update::getTodos();
$updates = Update::getTodoUpdates();
foreach ($updates as $update) {
if ($update['sql'] && (!empty($update['done']) || is_null($update['done']))) {

View File

@ -2,92 +2,75 @@
include_once __DIR__.'/../core.php';
// Lettura parametri iniziali del modulo
// Compatibilità per controller ed editor
if (!empty($id_plugin)) {
$element = Plugins::get($id_plugin);
$structure = Plugins::get($id_plugin);
} else {
$structure = Modules::get($id_module);
}
if (!empty($element['script'])) {
// Inclusione di eventuale plugin personalizzato
if (file_exists($docroot.'/modules/'.$element['module_dir'].'/plugins/custom/'.$element['script'])) {
include $docroot.'/modules/'.$element['module_dir'].'/plugins/custom/'.$element['script'];
} elseif (file_exists($docroot.'/modules/'.$element['module_dir'].'/plugins/'.$element['script'])) {
include $docroot.'/modules/'.$element['module_dir'].'/plugins/'.$element['script'];
}
if (!empty($id_plugin)) {
// Inclusione di eventuale plugin personalizzato
if (!empty($structure['script'])) {
include $structure->getEditFile();
return;
}
echo '
<h4>
<span class="'.(!empty($element['help']) ? ' tip' : '').'"'.(!empty($element['help']) ? ' title="'.prepareToField($element['help']).'" data-position="bottom"' : '').' >
'.$element['title'].(!empty($element['help']) ? ' <i class="fa fa-question-circle-o"></i>' : '').'</span>';
<span class="'.(!empty($structure['help']) ? ' tip' : '').'"'.(!empty($structure['help']) ? ' title="'.prepareToField($structure['help']).'" data-position="bottom"' : '').' >
'.$structure['title'].(!empty($structure['help']) ? ' <i class="fa fa-question-circle-o"></i>' : '').'</span>';
if (file_exists($docroot.'/plugins/'.$element['directory'].'/add.php')) {
if ($structure->hasAddFile()) {
echo '
<button type="button" class="btn btn-primary" data-toggle="modal" data-title="'.tr('Aggiungi').'..." data-target="#bs-popup" data-href="add.php?id_module='.$id_module.'&id_plugin='.$id_plugin.'&id_parent='.$id_record.'"><i class="fa fa-plus"></i></button>';
<button type="button" class="btn btn-primary" data-toggle="modal" data-title="'.tr('Aggiungi').'..." data-href="add.php?id_module='.$id_module.'&id_plugin='.$id_plugin.'&id_parent='.$id_record.'"><i class="fa fa-plus"></i></button>';
}
echo '
</h4>';
$directory = '/plugins/'.$element['directory'];
} else {
$element = Modules::get($id_module);
$directory = '/modules/'.$element['directory'];
}
$total = App::readQuery($element);
$module_options = (!empty($element['options2'])) ? $element['options2'] : $element['options'];
$type = $structure['option'];
// Lettura risultato query del modulo
// include $structure->filepath('init.php');
// Caricamento file aggiuntivo su elenco record
if (file_exists($docroot.$directory.'/custom/controller_before.php')) {
include $docroot.$directory.'/custom/controller_before.php';
} elseif (file_exists($docroot.$directory.'/controller_before.php')) {
include $docroot.$directory.'/controller_before.php';
}
// Segmenti
/*deve sempre essere impostato almeno un sezionale*/
if (empty($_SESSION['m'.$id_module]['id_segment'])) {
$rs = $dbo->fetchArray('SELECT id FROM zz_segments WHERE predefined = 1 AND id_module = '.prepare($id_module).'LIMIT 0,1');
$_SESSION['m'.$id_module]['id_segment'] = $rs[0]['id'];
}
if (count($dbo->fetchArray("SELECT id FROM zz_segments WHERE id_module = \"$id_module\"")) > 1) {
?>
<div class="row">
<div class="col-md-4 pull-right">
{[ "type": "select", "label": "", "name": "id_segment_", "required": 0, "class": "", "values": "query=SELECT id, name AS descrizione FROM zz_segments WHERE id_module = '<?php echo $id_module; ?>'", "value": "<?php echo $_SESSION['m'.$id_module]['id_segment']; ?>", "extra": "" ]}
</div>
</div>
<br>
<script>
$(document).ready(function () {
$("#id_segment_").on("change", function(){
if ($(this).val()<1){
session_set('<?php echo 'm'.$id_module; ?>,id_segment', '', 1, 1);
}else{
session_set('<?php echo 'm'.$id_module; ?>,id_segment', $(this).val(), 0, 1);
}
});
});
</script>
<?php
$controller_before = $structure->filepath('controller_before.php');
if (!empty($controller_before)) {
include $controller_before;
}
/*
* Datatables con record
*/
if (!empty($module_options) && $module_options != 'menu' && $module_options != 'custom') {
if (!empty($type) && $type != 'menu' && $type != 'custom') {
$total = App::readQuery($structure);
if (empty($id_plugin) && count(Modules::getSegments($id_module)) > 1) {
echo '
<div class="row">
<div class="col-md-4 pull-right">
{[ "type": "select", "name": "id_segment_", "required": 0, "values": "query=SELECT id, name AS descrizione FROM zz_segments WHERE id_module = '.prepare($id_module).'", "value": "'.$_SESSION['module_'.$id_module]['id_segment'].'" ]}
</div>
</div>
<br>';
echo '
<script>
$(document).ready(function () {
$("#id_segment_").on("change", function(){
if ($(this).val() < 1){
session_set("module_'.$id_module.',id_segment", "", 1, 1);
} else {
session_set("module_'.$id_module.',id_segment", $(this).val(), 0, 1);
}
});
});
</script>';
}
$table_id = 'main_'.rand(0, 99);
echo '
<table data-idmodule="'.$id_module.'" data-idplugin="'.$id_plugin.'" data-idparent="'.$id_record.'" id="'.$table_id.'" width="100%" class="main-records table table-condensed table-bordered">
@ -121,7 +104,7 @@ if (!empty($module_options) && $module_options != 'menu' && $module_options != '
}
echo '
<th'.$attr_td.' id="th_'.str_replace([' ', '.'], ['-', ''], $name).'"';
<th'.$attr_td.' id="th_'.searchFieldName($name).'"';
if ($total['search'][$key] == 1) {
echo ' class="search"';
} else {
@ -175,11 +158,11 @@ if (!empty($module_options) && $module_options != 'menu' && $module_options != '
$data = is_array($value) ? $value['data'] : [];
$extra = [];
foreach ($data as $k => $v) {
$extra[] = 'data-'.$k.'="'.$v.'"';
$extra[] = 'data-'.$k.'="'.prepareToField(\HTMLBuilder\HTMLBuilder::replace($v)).'"';
}
echo '
<li role="presentation"><a class="bulk-action clickable" data-op="'.$key.'" data-backto="record-list" '.implode(' ', $extra).'>'.$text.'</a></li>';
<li role="presentation"><a class="bulk-action clickable" data-op="'.prepareToField($key).'" data-backto="record-list" '.implode(' ', $extra).'>'.$text.'</a></li>';
}
echo '
@ -190,10 +173,34 @@ if (!empty($module_options) && $module_options != 'menu' && $module_options != '
</div>
<div class="col-md-5 text-right">
<div class="btn-group" role="group">
<button type="button" class="btn btn-primary btn-csv disabled" disabled>'.tr('Esporta').'</button>
<button type="button" class="btn btn-default btn-copy disabled" disabled>'.tr('Copia').'</button>
<button type="button" class="btn btn-default btn-print disabled" disabled>'.tr('Stampa').'</button>
<div class="btn-group" role="group">';
if (setting('Abilita esportazione Excel e PDF')) {
echo '
<div class="btn-group">
<button type="button" class="btn btn-primary table-btn btn-csv disabled" disabled>'.tr('Esporta').'</button>
<button type="button" class="btn btn-primary table-btn disabled dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="caret"></span>
<span class="sr-only">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu">
<li><a class="table-btn btn-pdf disabled" disabled>'.tr('PDF').'</a></li>
<li><a class="table-btn btn-excel disabled" disabled>'.tr('Excel').'</a></li>
</ul>
</div>';
} else {
echo '
<button type="button" class="btn btn-primary table-btn btn-csv disabled" disabled>'.tr('Esporta').'</button>';
}
echo '
<button type="button" class="btn btn-default table-btn btn-copy disabled" disabled>'.tr('Copia').'</button>
<button type="button" class="btn btn-default table-btn btn-print disabled" disabled>'.tr('Stampa').'</button>
</div>
</div>
</div>';
@ -202,22 +209,12 @@ if (!empty($module_options) && $module_options != 'menu' && $module_options != '
/*
* Inclusione modulo personalizzato
*/
elseif ($module_options == 'custom') {
// Lettura template modulo (verifico se ci sono template personalizzati, altrimenti uso quello base)
if (file_exists($docroot.$directory.'/custom/edit.php')) {
include $docroot.$directory.'/custom/edit.php';
} elseif (file_exists($docroot.$directory.'/custom/edit.html')) {
include $docroot.$directory.'/custom/edit.html';
} elseif (file_exists($docroot.$directory.'/edit.php')) {
include $docroot.$directory.'/edit.php';
} elseif (file_exists($docroot.$directory.'/edit.html')) {
include $docroot.$directory.'/edit.html';
}
elseif ($type == 'custom') {
include $structure->getEditFile();
}
// Caricamento file aggiuntivo su elenco record
if (file_exists($docroot.$directory.'/custom/controller_after.php')) {
include $docroot.$directory.'/custom/controller_after.php';
} elseif (file_exists($docroot.$directory.'/controller_after.php')) {
include $docroot.$directory.'/controller_after.php';
$controller_after = $structure->filepath('controller_after.php');
if (!empty($controller_after)) {
include $controller_after;
}

View File

@ -0,0 +1,172 @@
<?php
namespace Common\Components;
use Common\Document;
use Illuminate\Database\Eloquent\Builder;
use Modules\Articoli\Articolo as Original;
use UnexpectedValueException;
abstract class Article extends Row
{
protected $serialRowID = 'documento';
public static function build(Document $document, Original $articolo)
{
$model = parent::build($document, true);
$model->articolo()->associate($articolo);
$model->descrizione = $articolo->descrizione;
$model->abilita_serial = $articolo->abilita_serial;
$model->um = $articolo->um;
return $model;
}
abstract public function movimenta($qta);
abstract public function getDirection();
/**
* Imposta i seriali collegati all'articolo del documento.
*
* @param array $serials
*/
public function setSerialsAttribute($serials)
{
database()->sync('mg_prodotti', [
'id_riga_'.$this->serialRowID => $this->id,
'dir' => $this->getDirection(),
'id_articolo' => $this->idarticolo,
], [
'serial' => array_clean($serials),
]);
}
/**
* Restituisce l'elenco dei seriali collegati all'articolo del documento.
*
* @return array
*/
public function getSerialsAttribute()
{
if (empty($this->abilita_serial)) {
return [];
}
// Individuazione dei seriali
$results = database()->fetchArray('SELECT serial FROM mg_prodotti WHERE serial IS NOT NULL AND id_riga_'.$this->serialRowID.' = '.prepare($this->id));
return array_column($results, 'serial');
}
/**
* Modifica la quantità dell'articolo e movimenta automaticamente il magazzino.
*
* @param float $value
*/
public function setQtaAttribute($value)
{
if (!$this->cleanupSerials($value)) {
throw new UnexpectedValueException();
}
$previous = $this->qta;
$diff = $value - $previous;
$this->attributes['qta'] = $value;
$this->movimenta($diff);
$database = database();
// Se c'è un collegamento ad un ddt, aggiorno la quantità evasa
if (!empty($this->idddt)) {
$database->query('UPDATE dt_righe_ddt SET qta_evasa = qta_evasa + '.$diff.' WHERE descrizione = '.prepare($this->descrizione).' AND idarticolo = '.prepare($this->idarticolo).' AND idddt = '.prepare($this->idddt).' AND idiva = '.prepare($this->idiva));
}
// Se c'è un collegamento ad un ordine, aggiorno la quantità evasa
if (!empty($this->idordine)) {
$database->query('UPDATE or_righe_ordini SET qta_evasa = qta_evasa + '.$diff.' WHERE descrizione = '.prepare($this->descrizione).' AND idarticolo = '.prepare($this->idarticolo).' AND idordine = '.prepare($this->idordine).' AND idiva = '.prepare($this->idiva));
}
}
public function articolo()
{
return $this->belongsTo(Original::class, 'idarticolo');
}
public function copiaIn(Document $document)
{
$class = get_class($document);
$namespace = implode('\\', explode('\\', $class, -1));
$current = get_class($this);
$pieces = explode('\\', $current);
$type = end($pieces);
$object = $namespace.'\\Components\\'.$type;
$attributes = $this->getAttributes();
unset($attributes['id']);
$model = $object::build($document, $this->articolo);
$model->save();
$model = $object::find($model->id);
$accepted = $model->getAttributes();
$attributes = array_intersect_key($attributes, $accepted);
$model->fill($attributes);
return $model;
}
protected static function boot()
{
parent::boot(true);
static::addGlobalScope('articles', function (Builder $builder) {
$builder->whereNotNull('idarticolo')->where('idarticolo', '<>', 0);
});
}
protected function usedSerials()
{
if ($this->getDirection() == 'uscita') {
$results = database()->fetchArray("SELECT serial FROM mg_prodotti WHERE serial IN (SELECT DISTINCT serial FROM mg_prodotti WHERE dir = 'entrata') AND serial IS NOT NULL AND id_riga_".$this->serialRowID.' = '.prepare($this->id));
return array_column($results, 'serial');
}
return [];
}
protected function cleanupSerials($new_qta)
{
// Se la nuova quantità è minore della precedente
if ($this->qta > $new_qta) {
$seriali_usati = $this->usedSerials();
$count_seriali_usati = count($seriali_usati);
// Controllo sulla possibilità di rimuovere i seriali (se non utilizzati da documenti di vendita)
if ($this->getDirection() == 'uscita' && $new_qta < $count_seriali_usati) {
return false;
} else {
// Controllo sul numero di seriali effettivi da rimuovere
$seriali = $this->serials;
if ($new_qta < count($seriali)) {
$rimovibili = array_diff($seriali, $seriali_usati);
// Rimozione dei seriali aggiuntivi
$serials = array_slice($rimovibili, 0, $new_qta - $count_seriali_usati);
$this->serials = array_merge($seriali_usati, $serials);
}
}
}
return true;
}
}

View File

@ -0,0 +1,86 @@
<?php
namespace Common\Components;
use Common\Document;
use Common\Model;
use Illuminate\Database\Eloquent\Builder;
abstract class Description extends Model
{
protected $guarded = [];
public static function build(Document $document, $bypass = false)
{
$model = parent::build();
if (!$bypass) {
$model->is_descrizione = 1;
}
$model->setParent($document);
return $model;
}
public function setParent(Document $document)
{
$this->parent()->associate($document);
// Ordine delle righe
if (empty($this->disableOrder)) {
$this->order = orderValue($this->table, $this->getParentID(), $document->id);
}
$this->save();
}
public function copiaIn(Document $document)
{
$class = get_class($document);
$namespace = implode('\\', explode('\\', $class, -1));
$current = get_class($this);
$pieces = explode('\\', $current);
$type = end($pieces);
$object = $namespace.'\\Components\\'.$type;
$attributes = $this->getAttributes();
unset($attributes['id']);
$model = $object::build($document);
$model->save();
$model = $object::find($model->id);
$accepted = $model->getAttributes();
$attributes = array_intersect_key($attributes, $accepted);
$model->fill($attributes);
return $model;
}
abstract public function parent();
abstract public function getParentID();
protected static function boot($bypass = false)
{
parent::boot();
if (!$bypass) {
static::addGlobalScope('descriptions', function (Builder $builder) {
$builder->where('is_descrizione', '=', 1);
});
} else {
static::addGlobalScope('not_descriptions', function (Builder $builder) {
$builder->where('is_descrizione', '=', 0);
});
}
static::addGlobalScope('not_discount', function (Builder $builder) {
$builder->where('sconto_globale', '=', 0);
});
}
}

View File

@ -0,0 +1,75 @@
<?php
namespace Common\Components;
use Common\Model;
use Illuminate\Database\Eloquent\Builder;
abstract class Discount extends Model
{
public static function build()
{
$model = parent::build();
$model->sconto_globale = 1;
return $model;
}
/**
* Restituisce il totale dello sconto.
*/
public function getTotaleAttribute()
{
return $this->imponibile + $this->iva;
}
/**
* Restituisce il netto dello sconto.
*/
public function getNettoAttribute()
{
return $this->totale;
}
/**
* Restituisce l'imponibile scontato dello sconto.
*/
public function getImponibileScontatoAttribute()
{
return $this->imponibile;
}
/**
* Restituisce l'imponibile dello sconto.
*/
public function getImponibileAttribute()
{
return $this->subtotale;
}
/**
* Restituisce il "guadagno" dello sconto.
*/
public function getGuadagnoAttribute()
{
return $this->imponibile;
}
/**
* Restituisce il totale dello sconto.
*/
public function getIvaAttribute()
{
return $this->attributes['iva'];
}
protected static function boot()
{
parent::boot();
static::addGlobalScope('is_discount', function (Builder $builder) {
$builder->where('sconto_globale', '=', 1);
});
}
}

View File

@ -0,0 +1,290 @@
<?php
namespace Common\Components;
use Common\Document;
use Illuminate\Database\Eloquent\Builder;
use Modules\Iva\Aliquota;
use Modules\Ritenute\RitenutaAcconto;
use Modules\Ritenute\RivalsaINPS;
abstract class Row extends Description
{
protected $prezzo_unitario_vendita_riga = null;
public static function build(Document $document, $bypass = false)
{
return parent::build($document, true);
}
// Attributi di contabilità
/**
* Restituisce l'imponibile dell'elemento.
*
* @return float
*/
public function getImponibileAttribute()
{
return $this->prezzo_unitario_vendita * $this->qta;
}
/**
* Restituisce l'imponibile scontato dell'elemento.
*
* @return float
*/
public function getImponibileScontatoAttribute()
{
return $this->imponibile - $this->sconto;
}
/**
* Restituisce il totale (imponibile + iva + rivalsa_inps) dell'elemento.
*
* @return float
*/
public function getTotaleAttribute()
{
return $this->imponibile_scontato + $this->iva + $this->rivalsa_inps;
}
/**
* Restituisce il netto a pagare (totale - ritenuta_acconto) dell'elemento.
*
* @return float
*/
public function getNettoAttribute()
{
return $this->totale - $this->ritenuta_acconto;
}
/**
* Restituisce la spesa (prezzo_unitario_acquisto * qta) relativa all'elemento.
*
* @return float
*/
public function getSpesaAttribute()
{
return $this->prezzo_unitario_acquisto * $this->qta;
}
/**
* Restituisce il gaudagno totale (imponibile_scontato - spesa) relativo all'elemento.
*
* @return float
*/
public function getGuadagnoAttribute()
{
return $this->imponibile_scontato - $this->spesa;
}
// Attributi della componente
public function getRivalsaINPSAttribute()
{
return ($this->imponibile_scontato) / 100 * $this->rivalsa->percentuale;
}
public function getRitenutaAccontoAttribute()
{
$result = $this->imponibile_scontato;
if ($this->calcolo_ritenuta_acconto == 'IMP+RIV') {
$result += $this->rivalsainps;
}
return $result / 100 * $this->ritenuta->percentuale;
}
public function getIvaIndetraibileAttribute()
{
return $this->iva / 100 * $this->aliquota->indetraibile;
}
public function getIvaAttribute()
{
return ($this->imponibile_scontato + $this->rivalsa_inps) * $this->aliquota->percentuale / 100;
}
public function getIvaDetraibileAttribute()
{
return $this->iva - $this->iva_indetraibile;
}
public function getSubtotaleAttribute()
{
return $this->imponibile;
}
/**
* Restituisce lo sconto della riga corrente in euro.
*
* @return float
*/
public function getScontoAttribute()
{
return calcola_sconto([
'sconto' => $this->sconto_unitario,
'prezzo' => $this->prezzo_unitario_vendita,
'tipo' => $this->tipo_sconto,
'qta' => $this->qta,
]);
}
/**
* Imposta l'identificatore della Rivalsa INPS.
*
* @param int $value
*/
public function setIdRivalsaINPSAttribute($value)
{
$this->attributes['idrivalsainps'] = $value;
$this->load('rivalsa');
}
/**
* Imposta l'identificatore della Ritenuta d'Acconto.
*
* @param int $value
*/
public function setIdRitenutaAccontoAttribute($value)
{
$this->attributes['idritenutaacconto'] = $value;
$this->load('ritenuta');
}
/**
* Imposta l'identificatore dell'IVA.
*
* @param int $value
*/
public function setIdIvaAttribute($value)
{
$this->attributes['idiva'] = $value;
$this->load('aliquota');
}
/**
* Imposta il costo unitario della riga.
*
* @param float $value
*/
public function setPrezzoUnitarioVenditaAttribute($value)
{
$this->prezzo_unitario_vendita_riga = $value;
}
/**
* Restituisce il costo unitario della riga.
*/
public function getPrezzoUnitarioVenditaAttribute()
{
if (!isset($this->prezzo_unitario_vendita_riga)) {
$this->prezzo_unitario_vendita_riga = $this->attributes['subtotale'] / $this->qta;
}
return $this->prezzo_unitario_vendita_riga;
}
/**
* Save the model to the database.
*
* @param array $options
*
* @return bool
*/
public function save(array $options = [])
{
// Fix dei campi statici
$this->fixSubtotale();
$this->fixSconto();
$this->fixIva();
$this->fixRitenutaAcconto();
$this->fixRivalsaINPS();
return parent::save($options);
}
protected static function boot($bypass = false)
{
parent::boot(true);
if (!$bypass) {
static::addGlobalScope('rows', function (Builder $builder) {
$builder->whereNull('idarticolo')->orWhere('idarticolo', '=', 0);
});
}
}
/**
* Effettua i conti per il subtotale della riga.
*/
protected function fixSubtotale()
{
$this->attributes['subtotale'] = $this->imponibile;
}
/**
* Effettua i conti per la Rivalsa INPS.
*/
protected function fixRivalsaINPS()
{
$this->attributes['rivalsainps'] = $this->rivalsa_inps;
}
/**
* Effettua i conti per la Ritenuta d'Acconto, basandosi sul valore del campo calcolo_ritenuta_acconto.
*/
protected function fixRitenutaAcconto()
{
$this->attributes['ritenutaacconto'] = $this->ritenuta_acconto;
}
/**
* Effettua i conti per l'IVA.
*/
protected function fixIva()
{
$this->attributes['iva'] = $this->iva;
$descrizione = $this->aliquota->descrizione;
if (!empty($descrizione)) {
$this->attributes['desc_iva'] = $descrizione;
}
$this->fixIvaIndetraibile();
}
/**
* Effettua i conti per l'IVA indetraibile.
*/
protected function fixIvaIndetraibile()
{
$this->attributes['iva_indetraibile'] = $this->iva_indetraibile;
}
/**
* Effettua i conti per lo sconto totale.
*/
protected function fixSconto()
{
$this->attributes['sconto'] = $this->sconto;
}
public function aliquota()
{
return $this->belongsTo(Aliquota::class, 'idiva');
}
public function rivalsa()
{
return $this->belongsTo(RivalsaINPS::class, 'idrivalsainps');
}
public function ritenuta()
{
return $this->belongsTo(RitenutaAcconto::class, 'idritenutaacconto');
}
}

171
include/src/Document.php Normal file
View File

@ -0,0 +1,171 @@
<?php
namespace Common;
abstract class Document extends Model
{
/**
* Restituisce la collezione di righe e articoli con valori rilevanti per i conti.
*
* @return iterable
*/
public function getRighe()
{
$descrizioni = $this->descrizioni;
$righe = $this->righe;
$articoli = $this->articoli;
return $descrizioni->merge($righe)->merge($articoli)->sortBy('order');
}
abstract public function righe();
abstract public function articoli();
abstract public function descrizioni();
abstract public function scontoGlobale();
/**
* Calcola la somma degli attributi indicati come parametri.
*
* @param mixed ...$args
*
* @return float
*/
public function calcola(...$args)
{
$result = 0;
foreach ($args as $arg) {
$result += $this->getRigheContabili()->sum($arg);
}
return $this->round($result);
}
/**
* Calcola l'imponibile della fattura.
*
* @return float
*/
public function getImponibileAttribute()
{
return $this->calcola('imponibile');
}
/**
* Calcola lo sconto totale della fattura.
*
* @return float
*/
public function getScontoAttribute()
{
return $this->calcola('sconto');
}
/**
* Calcola l'imponibile scontato della fattura.
*
* @return float
*/
public function getImponibileScontatoAttribute()
{
return $this->calcola('imponibile_scontato');
}
/**
* Calcola l'IVA totale della fattura.
*
* @return float
*/
public function getIvaAttribute()
{
return $this->calcola('iva');
}
/**
* Calcola la rivalsa INPS totale della fattura.
*
* @return float
*/
public function getRivalsaINPSAttribute()
{
return $this->calcola('rivalsa_inps');
}
/**
* Calcola la ritenuta d'acconto totale della fattura.
*
* @return float
*/
public function getRitenutaAccontoAttribute()
{
return $this->calcola('ritenuta_acconto');
}
/**
* Calcola il totale della fattura.
*
* @return float
*/
public function getTotaleAttribute()
{
return $this->calcola('totale');
}
/**
* Calcola il netto a pagare della fattura.
*
* @return float
*/
public function getNettoAttribute()
{
return $this->calcola('netto');
}
/**
* Calcola la spesa totale relativa alla fattura.
*
* @return float
*/
public function getSpesaAttribute()
{
return $this->calcola('spesa');
}
/**
* Calcola il guadagno della fattura.
*
* @return float
*/
public function getGuadagnoAttribute()
{
return $this->calcola('guadagno');
}
/**
* Restituisce la collezione di righe e articoli con valori rilevanti per i conti.
*
* @return iterable
*/
protected function getRigheContabili()
{
$sconto = $this->scontoGlobale ? [$this->scontoGlobale] : [];
return $this->getRighe()->merge(collect($sconto));
}
/**
* Funzione per l'arrotondamento degli importi.
*
* @param float $value
*
* @return float
*/
protected function round($value)
{
$decimals = 2;
return round($value, $decimals);
}
}

21
include/src/Model.php Normal file
View File

@ -0,0 +1,21 @@
<?php
namespace Common;
use Illuminate\Database\Eloquent\Model as Original;
abstract class Model extends Original
{
// Retrocompatibilità MySQL
const UPDATED_AT = null;
/**
* Crea una nuova istanza del modello.
*
* @return static
*/
public static function build()
{
return new static();
}
}

View File

@ -5,23 +5,9 @@ include_once __DIR__.'/../core.php';
$paths = App::getPaths();
$user = Auth::user();
// Istanziamento della barra di debug
if (!empty($debug)) {
$debugbar = new DebugBar\DebugBar();
$pageTitle = $pageTitle ?: $structure->title;
$debugbar->addCollector(new DebugBar\DataCollector\MemoryCollector());
$debugbar->addCollector(new DebugBar\DataCollector\PhpInfoCollector());
$debugbar->addCollector(new DebugBar\DataCollector\RequestDataCollector());
$debugbar->addCollector(new DebugBar\DataCollector\TimeDataCollector());
$debugbar->addCollector(new DebugBar\Bridge\MonologCollector($logger));
$debugbar->addCollector(new DebugBar\DataCollector\PDO\PDOCollector($dbo->getPDO()));
$debugbarRenderer = $debugbar->getJavascriptRenderer();
$debugbarRenderer->setIncludeVendors(false);
$debugbarRenderer->setBaseUrl($paths['assets'].'/php-debugbar');
}
$messages = flash()->getMessages();
echo '<!DOCTYPE html>
<html>
@ -31,13 +17,20 @@ echo '<!DOCTYPE html>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<meta name="robots" content="noindex,nofollow">
<link href="'.$paths['img'].'/favicon.png" rel="icon" type="image/x-icon" />';
foreach ($css_modules as $style) {
$style = (is_array($style)) ? $style : ['href' => $style, 'media' => 'all'];
// CSS
foreach (App::getAssets()['css'] as $style) {
echo '
<link rel="stylesheet" type="text/css" media="'.$style['media'].'" href="'.$style['href'].'"/>';
<link rel="stylesheet" type="text/css" media="all" href="'.$style.'"/>';
}
// Print CSS
foreach (App::getAssets()['print'] as $style) {
echo '
<link rel="stylesheet" type="text/css" media="print" href="'.$style.'"/>';
}
if (Auth::check()) {
@ -45,16 +38,15 @@ if (Auth::check()) {
<script>
search = []';
$array = [];
foreach ($_SESSION as $idx1 => $arr2) {
if ($idx1 == 'module_'.$id_module) {
foreach ($arr2 as $field => $value) {
if ($value != '') {
$field_name = str_replace('search_', '', $field);
echo '
$array = $_SESSION['module_'.$id_module];
if (!empty($array)) {
foreach ($array as $field => $value) {
if (!empty($value) && starts_with($field, 'search_')) {
$field_name = str_replace('search_', '', $field);
echo '
search.push("search_'.$field_name.'");
search["search_'.$field_name.'"] = "'.$value.'";';
}
}
}
}
@ -84,6 +76,8 @@ if (Auth::check()) {
'delete' => tr('Elimina'),
'deleteTitle' => tr('Sei sicuro?'),
'deleteMessage' => tr('Eliminare questo elemento?'),
'errorTitle' => tr('Errore'),
'errorMessage' => tr("Si è verificato un errore nell'esecuzione dell'operazione richiesta"),
'close' => tr('Chiudi'),
'filter' => tr('Filtra'),
'long' => tr('La ricerca potrebbe richiedere del tempo'),
@ -108,10 +102,10 @@ if (Auth::check()) {
aggiornamenti_id: \''.($dbo->isInstalled() ? Modules::get('Aggiornamenti')['id'] : '').'\',
cifre_decimali: '.get_var('Cifre decimali per importi').',
cifre_decimali: '.setting('Cifre decimali per importi').',
decimals: "'.Translator::getFormatter()->getNumberSeparators()['decimals'].'",
thousands: "'.Translator::getFormatter()->getNumberSeparators()['thousands'].'",
decimals: "'.formatter()->getNumberSeparators()['decimals'].'",
thousands: "'.formatter()->getNumberSeparators()['thousands'].'",
search: search,
translations: translations,
@ -122,13 +116,24 @@ if (Auth::check()) {
end_date: \''.Translator::dateToLocale($_SESSION['period_end']).'\',
ckeditorToolbar: [
["Undo","Redo","-","Cut","Copy","Paste","PasteText","PasteFromWord","-","Scayt", "-","Link","Unlink","-","Bold","Italic","Underline","Superscript","SpecialChar","HorizontalRule","-","NumberedList","BulletedList","Outdent","Indent","Blockquote","-","Styles","Format","Image","Table", "TextColor", "BGColor" ],
["Undo","Redo","-","Cut","Copy","Paste","PasteText","PasteFromWord","-","Scayt", "-","Link","Unlink","-","Bold","Italic","Underline","Superscript","SpecialChar","HorizontalRule","-","JustifyLeft","JustifyCenter","JustifyRight","JustifyBlock","-","NumberedList","BulletedList","Outdent","Indent","Blockquote","-","Styles","Format","Image","Table", "TextColor", "BGColor" ],
],
tempo_attesa_ricerche: '.setting('Tempo di attesa ricerche in secondi').',
};
</script>';
} else {
echo '
<script>
globals = {
locale: \''.$lang.'\',
full_locale: \''.$lang.'_'.strtoupper($lang).'\',
};
</script>';
}
foreach ($jscript_modules as $js) {
// JS
foreach (App::getAssets()['js'] as $js) {
echo '
<script type="text/javascript" charset="utf-8" src="'.$js.'"></script>';
}
@ -144,17 +149,31 @@ echo '
});
</script>';
if (!empty($debugbarRenderer) && Auth::check()) {
echo $debugbarRenderer->renderHead();
if (Auth::check()) {
// Barra di debug
if (App::debug()) {
$debugbarRenderer = $debugbar->getJavascriptRenderer();
$debugbarRenderer->setIncludeVendors(false);
$debugbarRenderer->setBaseUrl($paths['assets'].'/php-debugbar');
echo $debugbarRenderer->renderHead();
}
if (setting('Abilita esportazione Excel e PDF')) {
echo '
<script type="text/javascript" charset="utf-8" src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.3/jszip.min.js"></script>
<script type="text/javascript" charset="utf-8" src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.36/pdfmake.min.js"></script>
<script type="text/javascript" charset="utf-8" src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.36/vfs_fonts.js"></script>';
}
}
$hide_sidebar = get_var('Nascondere la barra sinistra di default');
$hide_sidebar = Auth::check() && setting('Nascondere la barra sinistra di default');
echo '
</head>
<body class="skin-'.$theme.(!empty($hide_sidebar) ? ' sidebar-collapse' : '').(!Auth::check() ? ' hold-transition login-page' : '').'">
<div class="wrapper">';
<div class="'.(!Auth::check() ? '' : 'wrapper').'">';
if (Auth::check()) {
$calendar = ($_SESSION['period_start'] != date('Y').'-01-01' || $_SESSION['period_end'] != date('Y').'-12-31') ? 'red' : 'white';
@ -185,7 +204,7 @@ if (Auth::check()) {
<!-- Header Navbar: style can be found in header.less -->
<nav class="navbar navbar-static-top" role="navigation">
<!-- Sidebar toggle button-->
<a href="#" class="sidebar-toggle" data-toggle="offcanvas" role="button">
<a href="#" class="sidebar-toggle" data-toggle="push-menu" role="button">
<span class="sr-only">'.tr('Mostra/nascondi menu').'</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
@ -193,8 +212,8 @@ if (Auth::check()) {
</a>
<div class="input-group btn-calendar pull-left">
<button id="daterange" class="btn"><i class="fa fa-calendar" style="color:'.$calendar.'"></i> <i class="fa fa-caret-down"></i></button>
<span class="hidden-xs" style="vertical-align:middle">
<button id="daterange" class="btn"><i class="fa fa-calendar" style="color:'.$calendar.'"></i> <i class="fa fa-caret-down" style="color:'.$calendar.';" ></i></button>
<span class="hidden-xs" style="vertical-align:middle; color:'.$calendar.';">
'.Translator::dateToLocale($_SESSION['period_start']).' - '.Translator::dateToLocale($_SESSION['period_end']).'
</span>
</div>
@ -273,42 +292,57 @@ if (Auth::check()) {
}
echo '
<div class="col-md-12">';
} elseif (!empty($_SESSION['infos']) || !empty($_SESSION['warnings']) || !empty($_SESSION['errors'])) {
echo '
<div class="col-md-12">';
// Eventuale messaggio personalizzato per l'installazione corrente
include_once App::filepath('include/custom/extra', 'extra.php');
} else {
// Eventuale messaggio personalizzato per l'installazione corrente
include_once App::filepath('include/custom/extra', 'login.php');
if (!empty($messages['info']) || !empty($messages['warning']) || !empty($messages['error'])) {
echo '
<div class="box box-warning box-center">
<div class="box-header with-border text-center">
<h3 class="box-title">'.tr('Informazioni').'</h3>
</div>
<div class="box-body">';
}
}
// Infomazioni
foreach ($_SESSION['infos'] as $value) {
echo '
if (!empty($messages['info'])) {
foreach ($messages['info'] as $value) {
echo '
<div class="alert alert-success push">
<i class="fa fa-check"></i> '.$value.'
</div>';
}
}
// Errori
foreach ($_SESSION['errors'] as $value) {
echo '
if (!empty($messages['error'])) {
foreach ($messages['error'] as $value) {
echo '
<div class="alert alert-danger push">
<i class="fa fa-times"></i> '.$value.'
</div>';
}
}
// Avvisi
foreach ($_SESSION['warnings'] as $value) {
echo '
if (!empty($messages['warning'])) {
foreach ($messages['warning'] as $value) {
echo '
<div class="alert alert-warning push">
<i class="fa fa-warning"></i>
'.$value.'
</div>';
}
}
if (!Auth::check() && (!empty($_SESSION['infos']) || !empty($_SESSION['warnings']) || !empty($_SESSION['errors']))) {
if (!Auth::check() && (!empty($messages['info']) || !empty($messages['warning']) || !empty($messages['error']))) {
echo '
</div>
</div>';

View File

@ -11,21 +11,31 @@ switch ($op) {
$username = post('username');
$password = post('password');
if ($dbo->isConnected() && $dbo->isInstalled() && Auth::getInstance()->attempt($username, $password)) {
if ($dbo->isConnected() && $dbo->isInstalled() && auth()->attempt($username, $password)) {
$_SESSION['keep_alive'] = (filter('keep_alive') != null);
// Rimozione log vecchi
$dbo->query('DELETE FROM `zz_operations` WHERE DATE_ADD(`created_at`, INTERVAL 30*24*60*60 SECOND) <= NOW()');
// Auto backup del database giornaliero
if (get_var('Backup automatico')) {
if (setting('Backup automatico')) {
$result = Backup::daily();
if (!isset($result)) {
$_SESSION['infos'][] = tr('Backup saltato perché già esistente!');
flash()->info(tr('Backup saltato perché già esistente!'));
} elseif (!empty($result)) {
$_SESSION['infos'][] = tr('Backup automatico eseguito correttamente!');
flash()->info(tr('Backup automatico eseguito correttamente!'));
} else {
$_SESSION['errors'][] = tr('Errore durante la generazione del backup automatico!');
flash()->error(tr('Errore durante la generazione del backup automatico!'));
}
}
} else {
$status = auth()->getCurrentStatus();
flash()->error(Auth::getStatus()[$status]['message']);
redirect(ROOTDIR.'/index.php');
exit();
}
break;
@ -34,7 +44,6 @@ switch ($op) {
Auth::logout();
redirect(ROOTDIR.'/index.php');
exit();
break;
@ -44,7 +53,7 @@ if (Auth::check() && isset($dbo) && $dbo->isConnected() && $dbo->isInstalled())
$module = Auth::firstModule();
if (!empty($module)) {
redirect(ROOTDIR.'/controller.php?id_module='.$module, 'js');
redirect(ROOTDIR.'/controller.php?id_module='.$module);
} else {
redirect(ROOTDIR.'/index.php?op=logout');
}
@ -52,21 +61,20 @@ if (Auth::check() && isset($dbo) && $dbo->isConnected() && $dbo->isInstalled())
}
// Procedura di installazione
include_once $docroot.'/include/configuration.php';
include_once $docroot.'/include/init/configuration.php';
// Procedura di aggiornamento
include_once $docroot.'/include/update.php';
include_once $docroot.'/include/init/update.php';
// Procedura di inizializzazione
include_once $docroot.'/include/init/init.php';
$pageTitle = tr('Login');
if (file_exists($docroot.'/include/custom/top.php')) {
include_once $docroot.'/include/custom/top.php';
} else {
include_once $docroot.'/include/top.php';
}
include_once App::filepath('include|custom|', 'top.php');
// Controllo se è una beta e in caso mostro un warning
if (str_contains($version, 'beta')) {
if (Update::isBeta()) {
echo '
<div class="clearfix">&nbsp;</div>
<div class="alert alert-warning alert-dismissable col-md-6 col-md-push-3 text-center fade in">
@ -109,7 +117,7 @@ if (Auth::isBrute()) {
</script>';
}
if (!empty($_SESSION['errors'])) {
if (!empty(flash()->getMessage('error'))) {
echo '
<script>
$(document).ready(function(){
@ -127,7 +135,7 @@ echo '
<div class="login-box-body box-body">
<div class="form-group input-group">
<span class="input-group-addon"><i class="fa fa-user"></i> </span>
<span class="input-group-addon before"><i class="fa fa-user"></i> </span>
<input type="text" name="username" autocomplete="off" class="form-control" placeholder="'.tr('Nome utente').'"';
if (isset($username)) {
echo ' value="'.$username.'"';
@ -135,7 +143,7 @@ if (isset($username)) {
echo'>
</div>
<div class="form-group input-group">
<span class="input-group-addon"><i class="fa fa-lock"></i> </span>
<span class="input-group-addon before"><i class="fa fa-lock"></i> </span>
<input type="password" name="password" autocomplete="off" class="form-control" placeholder="'.tr('Password').'">
</div>
<div class="form-group">
@ -158,7 +166,7 @@ echo '/> '.tr('Mantieni attiva la sessione').'
$(document).ready( function(){
$("#login").click(function(){
$("#login").text("';
if ($dbo->isInstalled() && get_var('Backup automatico')) {
if ($dbo->isInstalled() && !Update::isUpdateAvailable() && setting('Backup automatico')) {
echo tr('Backup automatico in corso');
} else {
echo tr('Autenticazione');
@ -175,8 +183,4 @@ echo '/> '.tr('Mantieni attiva la sessione').'
});
</script>';
if (file_exists($docroot.'/include/custom/bottom.php')) {
include_once $docroot.'/include/custom/bottom.php';
} else {
include_once $docroot.'/include/bottom.php';
}
include_once App::filepath('include|custom|', 'bottom.php');

View File

@ -6,11 +6,7 @@ $pageTitle = tr('Informazioni');
$paths = App::getPaths();
if (file_exists($docroot.'/include/custom/top.php')) {
include $docroot.'/include/custom/top.php';
} else {
include $docroot.'/include/top.php';
}
include_once App::filepath('include|custom|', 'top.php');
echo '
<div class="box">
@ -168,8 +164,4 @@ echo '
</div>
</div>';
if (file_exists($docroot.'/include/custom/bottom.php')) {
include $docroot.'/include/custom/bottom.php';
} else {
include $docroot.'/include/bottom.php';
}
include_once App::filepath('include|custom|', 'bottom.php');

311
lib/common.php Normal file
View File

@ -0,0 +1,311 @@
<?php
/**
* Funzioni globali utilizzate per il funzionamento dei componenti indipendenti del progetto (moduli, plugin, stampe, ...).
*
* @since 2.4.2
*/
/**
* Esegue una somma precisa tra due interi/array.
*
* @param array|float $first
* @param array|float $second
* @param int $decimals
*
* @since 2.3
*
* @return float
*/
function sum($first, $second = null, $decimals = 4)
{
$first = (array) $first;
$second = (array) $second;
$array = array_merge($first, $second);
$result = 0;
$decimals = is_numeric($decimals) ? $decimals : formatter()->getPrecision();
$bcadd = function_exists('bcadd');
foreach ($array as $value) {
$value = round($value, $decimals);
if ($bcadd) {
$result = bcadd($result, $value, $decimals);
} else {
$result += $value;
}
}
return floatval($result);
}
/**
* Calcola lo sconto globale per i documenti e lo aggiorna di conseguenza.
* Attenzione: eseguire la funzione dopo l'inserimento e la modifica di tutte le righe del documento.
*
* @param array $tables
* @param array $fields
* @param int $id_record
* @param array $options
*/
function aggiorna_sconto(array $tables, array $fields, $id_record, array $options = [])
{
$dbo = database();
$descrizione = tr('Sconto', [], ['upper' => true]);
// Rimozione dello sconto precedente
$dbo->query('DELETE FROM '.$tables['row'].' WHERE sconto_globale = 1 AND '.$fields['row'].'='.prepare($id_record));
// Individuazione del nuovo sconto
$sconto = $dbo->select($tables['parent'], ['sconto_globale', 'tipo_sconto_globale'], [$fields['parent'] => $id_record]);
$sconto[0]['sconto_globale'] = floatval($sconto[0]['sconto_globale']);
// Aggiorno l'eventuale sconto gestendolo con le righe in fattura
$iva = 0;
if (!empty($sconto[0]['sconto_globale'])) {
// Commit: 2c7f69867accb4f12a0c7cfd320714bb98e31a67
// L'aliquota IVA viene calcolata in percentuale sull'imponibile e l'iva effettiva delle righe del documento.
if ($sconto[0]['tipo_sconto_globale'] == 'PRC') {
$rs = $dbo->fetchArray('SELECT SUM(subtotale - sconto) AS imponibile, SUM(iva) AS iva FROM (SELECT '.$tables['row'].'.subtotale, '.$tables['row'].'.sconto, '.$tables['row'].'.iva FROM '.$tables['row'].' WHERE '.$fields['row'].'='.prepare($id_record).') AS t');
$subtotale = $rs[0]['imponibile'];
$iva += $rs[0]['iva'] / 100 * $sconto[0]['sconto_globale'];
$subtotale = -$subtotale / 100 * $sconto[0]['sconto_globale'];
$descrizione = $descrizione.' '.Translator::numberToLocale($sconto[0]['sconto_globale']).'%';
} else {
$rs = $dbo->fetchArray('SELECT SUM(subtotale - sconto) AS imponibile, SUM(iva) AS iva FROM (SELECT '.$tables['row'].'.subtotale, '.$tables['row'].'.sconto, '.$tables['row'].'.iva FROM '.$tables['row'].' WHERE '.$fields['row'].'='.prepare($id_record).') AS t');
$subtotale = $rs[0]['imponibile'];
$iva += $sconto[0]['sconto_globale'] * $rs[0]['iva'] / $subtotale;
$subtotale = -$sconto[0]['sconto_globale'];
}
// Calcolo dell'IVA da scontare
$idiva = setting('Iva predefinita');
$rsi = $dbo->select('co_iva', ['descrizione', 'percentuale'], ['id' => $idiva]);
$values = [
$fields['row'] => $id_record,
'descrizione' => $descrizione,
'subtotale' => $subtotale,
'qta' => 1,
'idiva' => $idiva,
//'desc_iva' => $rsi[0]['descrizione'],
'iva' => -$iva,
'sconto_globale' => 1,
'order' => orderValue($tables['row'], $fields['row'], $id_record),
];
$dbo->insert($tables['row'], $values);
}
}
function controlla_seriali($field, $id_riga, $old_qta, $new_qta, $dir)
{
$dbo = database();
$new_qta = abs($new_qta);
$old_qta = abs($old_qta);
if ($old_qta >= $new_qta) {
// Controllo sulla possibilità di rimuovere i seriali (se non utilizzati da documenti di vendita)
if ($dir == 'uscita' && $new_qta < count(seriali_non_rimuovibili($field, $id_riga, $dir))) {
return false;
} else {
// Controllo sul numero di seriali effettivi da rimuovere
$count = $dbo->fetchArray('SELECT COUNT(*) AS tot FROM mg_prodotti WHERE '.$field.'='.prepare($id_riga))[0]['tot'];
if ($new_qta < $count) {
$deletes = $dbo->fetchArray("SELECT id FROM mg_prodotti WHERE serial NOT IN (SELECT serial FROM mg_prodotti WHERE dir = 'entrata' AND ".$field.'!='.prepare($id_riga).') AND '.$field.'='.prepare($id_riga).' ORDER BY serial DESC LIMIT '.abs($count - $new_qta));
// Rimozione
foreach ($deletes as $delete) {
$dbo->query('DELETE FROM mg_prodotti WHERE id = '.prepare($delete['id']));
}
}
}
}
return true;
}
/**
* Individua i seriali non rimuovibili poichè utilizzati in documenti rilasciati.
*
* @param string $field
* @param int $id_riga
* @param string $dir
*
* @return array
*/
function seriali_non_rimuovibili($field, $id_riga, $dir)
{
$dbo = database();
$results = [];
if ($dir == 'uscita') {
$results = $dbo->fetchArray("SELECT serial FROM mg_prodotti WHERE serial IN (SELECT serial FROM mg_prodotti WHERE dir = 'entrata') AND ".$field.'='.prepare($id_riga));
}
return $results;
}
/**
* Calcola gli sconti in modo automatico.
*
* @param array $data
*
* @return float
*/
function calcola_sconto($data)
{
if ($data['tipo'] == 'PRC') {
$result = 0;
$price = floatval($data['prezzo']);
$percentages = explode('+', $data['sconto']);
foreach ($percentages as $percentage) {
$discount = $price / 100 * floatval($percentage);
$result += $discount;
$price -= $discount;
}
} else {
$result = floatval($data['sconto']);
}
if (!empty($data['qta'])) {
$result = $result * $data['qta'];
}
return $result;
}
/**
* Restistuisce le informazioni sull'eventuale riferimento ai documenti.
*
* @param array $data
* @param string $dir
*
* @return array
*/
function doc_references($info, $dir, $ignore = [])
{
$dbo = database();
// Rimozione valori da non controllare
foreach ($ignore as $field) {
if (isset($info[$field])) {
unset($info[$field]);
}
}
$module = null;
$id = null;
// Ordine
if (!empty($info['idordine'])) {
$data = $dbo->fetchArray("SELECT IF(numero_esterno != '', numero_esterno, numero) AS numero, data FROM or_ordini WHERE id=".prepare($info['idordine']));
$module = ($dir == 'entrata') ? 'Ordini cliente' : 'Ordini fornitore';
$id = $info['idordine'];
$document = tr('Ordine');
}
// DDT
elseif (!empty($info['idddt'])) {
$data = $dbo->fetchArray("SELECT IF(numero_esterno != '', numero_esterno, numero) AS numero, data FROM dt_ddt WHERE id=".prepare($info['idddt']));
$module = ($dir == 'entrata') ? 'Ddt di vendita' : 'Ddt di acquisto';
$id = $info['idddt'];
$document = tr('Ddt');
}
// Preventivo
elseif (!empty($info['idpreventivo'])) {
$data = $dbo->fetchArray('SELECT numero, data_bozza AS data FROM co_preventivi WHERE id='.prepare($info['idpreventivo']));
$module = 'Preventivi';
$id = $info['idpreventivo'];
$document = tr('Preventivo');
}
// Contratto
elseif (!empty($info['idcontratto'])) {
$data = $dbo->fetchArray('SELECT numero, data_bozza AS data FROM co_contratti WHERE id='.prepare($info['idcontratto']));
$module = 'Contratti';
$id = $info['idcontratto'];
$document = tr('Contratto');
}
// Intervento
elseif (!empty($info['idintervento'])) {
$data = $dbo->fetchArray('SELECT codice AS numero, IFNULL( (SELECT MIN(orario_inizio) FROM in_interventi_tecnici WHERE in_interventi_tecnici.idintervento=in_interventi.id), data_richiesta) AS data FROM in_interventi WHERE id='.prepare($info['idintervento']));
$module = 'Interventi';
$id = $info['idintervento'];
$document = tr('Intervento');
}
// Testo relativo
if (!empty($module) && !empty($id)) {
$document = Stringy\Stringy::create($document)->toLowerCase();
if (!empty($data)) {
$description = tr('Rif. _DOC_ num. _NUM_ del _DATE_', [
'_DOC_' => $document,
'_NUM_' => $data[0]['numero'],
'_DATE_' => Translator::dateToLocale($data[0]['data']),
]);
} else {
$description = tr('_DOC_ di riferimento _ID_ eliminato', [
'_DOC_' => $document->upperCaseFirst(),
'_ID_' => $id,
]);
}
return [
'module' => $module,
'id' => $id,
'description' => $description,
];
}
return [];
}
function months()
{
return [
1 => tr('Gennaio'),
2 => tr('Febbraio'),
3 => tr('Marzo'),
4 => tr('Aprile'),
5 => tr('Maggio'),
6 => tr('Giugno'),
7 => tr('Luglio'),
8 => tr('Agosto'),
9 => tr('Settembre'),
10 => tr('Ottobre'),
11 => tr('Novembre'),
12 => tr('Dicembre'),
];
}
function orderValue($table, $field, $id)
{
return database()->fetchOne('SELECT IFNULL(MAX(`order`) + 1, 0) AS value FROM '.$table.' WHERE '.$field.' = '.prepare($id))['value'];
}

File diff suppressed because it is too large Load Diff

View File

@ -18,11 +18,8 @@ var isMobile = {
return (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(navigator.userAgent) ||
/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(navigator.userAgent.substr(0, 4)));
}
};
// Aggiunta dell'ingranaggio all'unload della pagina
$(window).on("beforeunload", function () {
$("#main_loading").show();
@ -33,6 +30,11 @@ $(window).on("load", function () {
$("#main_loading").fadeOut();
});
// Fix multi-modal
$(document).on('hidden.bs.modal', '.modal', function () {
$('.modal:visible').length && $(document.body).addClass('modal-open');
});
$(document).ready(function () {
// Imposta la lingua per la gestione automatica delle date dei diversi plugin
moment.locale(globals.locale);
@ -101,6 +103,19 @@ $(document).ready(function () {
}
);
// Pulsante per visualizzare/ nascondere la password
$(".input-group-addon").on('click', function () {
if ($(this).parent().find("i").hasClass('fa-eye')) {
$("#password").attr("type", "text");
$(this).parent().find("i").removeClass('fa-eye').addClass('fa-eye-slash');
$(this).parent().find("i").attr('title', 'Nascondi password');
} else if ($(this).parent().find("i").hasClass('fa-eye-slash')) {
$("#password").attr("type", "password");
$(this).parent().find("i").removeClass('fa-eye-slash').addClass('fa-eye');
$(this).parent().find("i").attr('title', 'Visualizza password');
}
});
// Messaggi automatici di eliminazione
$(document).on('click', '.ask', function () {
message(this);
@ -108,19 +123,31 @@ $(document).ready(function () {
// Pulsanti di Datatables
$(".btn-csv").click(function (e) {
var table = $(document).find("#" + $(this).parent().parent().parent().data("target")).DataTable();
var table = $(document).find("#" + $(this).closest("[data-target]").data("target")).DataTable();
table.buttons(0).trigger();
});
$(".btn-excel").click(function (e) {
var table = $(document).find("#" + $(this).closest("[data-target]").data("target")).DataTable();
table.buttons(3).trigger();
});
$(".btn-pdf").click(function (e) {
var table = $(document).find("#" + $(this).closest("[data-target]").data("target")).DataTable();
table.buttons(4).trigger();
});
$(".btn-copy").click(function (e) {
var table = $(document).find("#" + $(this).parent().parent().parent().data("target")).DataTable();
var table = $(document).find("#" + $(this).closest("[data-target]").data("target")).DataTable();
table.buttons(1).trigger();
});
$(".btn-print").click(function (e) {
var table = $(document).find("#" + $(this).parent().parent().parent().data("target")).DataTable();
var table = $(document).find("#" + $(this).closest("[data-target]").data("target")).DataTable();
table.buttons(2).trigger();
});
@ -183,7 +210,7 @@ $(document).ready(function () {
stop: function (event, ui) {
var order = $(this).sortable('toArray').toString();
$.post(globals.rootdir + "/modules/aggiornamenti/actions.php?id_module=" + globals.aggiornamenti_id, {
$.post(globals.rootdir + "/actions.php?id_module=" + globals.aggiornamenti_id, {
op: 'sortmodules',
ids: order
});
@ -359,7 +386,7 @@ $(document).ready(function () {
// Widgets ordinabili
function start_widgets($widgets) {
cls = new Array();
cls = [];
for (i = 0; i < $widgets.length; i++) {
$widget = $($widgets[i]);
@ -392,7 +419,7 @@ function start_widgets($widgets) {
var new_class = "";
var order = $(this).sortable('toArray').toString();
$.post(globals.rootdir + "/modules/aggiornamenti/actions.php?id_module=" + globals.aggiornamenti_id, {
$.post(globals.rootdir + "/actions.php?id_module=" + globals.aggiornamenti_id, {
op: 'updatewidget',
location: dst_list,
id_module: globals.id_module,
@ -401,7 +428,7 @@ function start_widgets($widgets) {
id: ui.item.attr('id')
});
$.post(globals.rootdir + "/modules/aggiornamenti/actions.php?id_module=" + globals.aggiornamenti_id, {
$.post(globals.rootdir + "/actions.php?id_module=" + globals.aggiornamenti_id, {
op: 'sortwidget',
location: dst_list,
ids: order,
@ -416,29 +443,31 @@ function start_widgets($widgets) {
// Modal
function launch_modal(title, href, init_modal, id) {
//Fix - Select2 does not function properly when I use it inside a Bootstrap modal.
$.fn.modal.Constructor.prototype.enforceFocus = function() {};
// Fix - Select2 does not function properly when I use it inside a Bootstrap modal.
$.fn.modal.Constructor.prototype.enforceFocus = function () {
};
if (id == null) {
id = '#bs-popup';
}
// Generazione dinamica modal
/*
id = 'bs-popup-' + Math.floor(Math.random() * 100);
$('#modals').append('<div class="modal fade" id="' + id + '" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" data-backdrop="static" data-keyboard="true"></div>');
id = '#' + id;
*/
}
if (init_modal == null) {
init_modal = 1;
}
$('html').addClass('modal-open');
$(id).on('hidden.bs.modal', function () {
if ($('.modal-backdrop').length < 1 ) {
$('html').removeClass('modal-open');
if ($('.modal-backdrop').length < 1) {
$(this).html('');
$(this).data('modal', null);
}
});
// Lettura contenuto div
@ -448,9 +477,9 @@ function launch_modal(title, href, init_modal, id) {
$(id).html(
'<div class="modal-dialog modal-lg">' +
' <div class="modal-content">' +
' <div class="modal-header">' +
' <div class="modal-header bg-light-blue">' +
' <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">' + globals.translations.close + '</span></button>' +
' <h4 class="modal-title">' + title + '</h4>' +
' <h4 class="modal-title"><i class="fa fa-pencil"></i> ' + title + '</h4>' +
' </div>' +
' <div class="modal-body">' + data + '</div>'
);
@ -463,9 +492,9 @@ function launch_modal(title, href, init_modal, id) {
$(id).html(
'<div class="modal-dialog modal-lg">' +
' <div class="modal-content">' +
' <div class="modal-header">' +
' <div class="modal-header bg-light-blue">' +
' <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">' + globals.translations.close + '</span></button>' +
' <h4 class="modal-title">' + title + '</h4>' +
' <h4 class="modal-title"><i class="fa fa-pencil"></i> ' + title + '</h4>' +
' </div>' +
' <div class="modal-body">' + data + '</div>'
);
@ -515,12 +544,12 @@ function start_datatables() {
search.push(array[index]);
search[array[index]] = array[value];
}
})
});
// Fix per l'URL encoding
search.forEach(function (value, index, array) {
search[array[index]] = decodeURIComponent(array[value]);
})
});
var res = [];
$this.find("th").each(function () {
@ -534,6 +563,9 @@ function start_datatables() {
});
var sum;
var tempo;
var tempo_attesa_ricerche = (globals.tempo_attesa_ricerche * 1000);
var table = $this.DataTable({
language: {
url: globals.js + '/i18n/datatables/' + globals.locale + '.min.json'
@ -568,14 +600,14 @@ function start_datatables() {
selector: 'td:first-child'
},
buttons: [{
extend: 'csv',
fieldSeparator: ";",
exportOptions: {
modifier: {
selected: true
}
extend: 'csv',
fieldSeparator: ";",
exportOptions: {
modifier: {
selected: true
}
},
}
},
{
extend: 'copy',
exportOptions: {
@ -586,6 +618,37 @@ function start_datatables() {
},
{
extend: 'print',
autoPrint: true,
customize: function (win) {
$(win.document.body)
.css('font-size', '10pt')
.append(
'<table class="main-records table table-condensed table-bordered dataTable"><tfoot><tr><td></td><td class="pull-right">' + $('#summable').text() + '</td><td></td></tr></tfoot></table>'
);
$(win.document.body).find('table')
.addClass('compact')
.css('font-size', 'inherit');
$(win.document.body).find('td:first-child')
.addClass('hide');
$(win.document.body).find('th:first-child')
.addClass('hide');
},
exportOptions: {
modifier: {
selected: true
}
}
},
{
extend: 'excel',
exportOptions: {
modifier: {
selected: true
}
}
},
{
extend: 'pdf',
exportOptions: {
modifier: {
selected: true
@ -611,6 +674,10 @@ function start_datatables() {
$('<br><input type="text" style="width:100%" class="form-control" placeholder="' + globals.translations.filter + '..."><i class="deleteicon fa fa-times fa-2x hide"></i>')
.appendTo(column.header())
.on('keyup', function (e) {
clearInterval(tempo);
// Fix del pulsante di pulizia ricerca e del messaggio sulla ricerca lenta
if (e.which != 9) {
if (!$(this).val()) {
if ($(this).parent().data("slow") != undefined) $("#slow").remove();
@ -625,16 +692,28 @@ function start_datatables() {
}
}
idx1 = 'module_' + $this.data('idmodule'); //+ "-" + $this.data('idplugin');
idx2 = 'search_' + $(this).parent().attr('id').replace('th_', '');
function start_search(module_id, field, search_value) {
searchTable(module_id, field, search_value);
column.search(search_value).draw();
}
// Imposto delle sessioni per le ricerche del modulo e del campo specificatsi
session_set(idx1 + ',' + idx2, $(this).val(), 0);
column.search(this.value).draw();
// Impostazione delle sessioni per le ricerche del modulo e del campo specificati
var module_id = $this.data('idmodule'); //+ "-" + $this.data('idplugin');
var field = $(this).parent().attr('id').replace('th_', '');
var value = $(this).val();
if (e.keyCode == 13 || $(this).val() == '') {
start_search(module_id, field, value);
} else {
tempo = window.setTimeout(start_search, tempo_attesa_ricerche, module_id, field, value);
}
});
});
// Disabilito l'ordinamento alla pressione del tasto invio sull'<input>
$("thead input, .search").on('keypress', function (e) {
stopTableSorting(e);
});
// Disabilito l'ordinamento al click sull'<input>
$("thead input, .deleteicon").click(function (e) {
stopTableSorting(e);
@ -689,21 +768,11 @@ function start_datatables() {
var container = $(document).find('[data-target=' + $this.attr('id') + ']');
if (api.rows({
selected: true
}).count() > 0) {
container.find('.btn-csv').removeClass('disabled');
container.find('.btn-print').removeClass('disabled');
container.find('.btn-copy').removeClass('disabled');
container.find('.btn-csv').attr('disabled', false);
container.find('.btn-print').attr('disabled', false);
container.find('.btn-copy').attr('disabled', false);
selected: true
}).count() > 0) {
container.find('.table-btn').removeClass('disabled').attr('disabled', false);
} else {
container.find('.btn-csv').addClass('disabled');
container.find('.btn-print').addClass('disabled');
container.find('.btn-copy').addClass('disabled');
container.find('.btn-csv').attr('disabled', true);
container.find('.btn-print').attr('disabled', true);
container.find('.btn-copy').attr('disabled', true);
container.find('.table-btn').addClass('disabled').attr('disabled', true);
}
// Seleziona tutto
@ -713,8 +782,8 @@ function start_datatables() {
}).select();
if (this.fnSettings().fnRecordsDisplay() == api.rows({
selected: true
}).count()) {
selected: true
}).count()) {
$("#main_loading").fadeOut();
}
}
@ -724,6 +793,7 @@ function start_datatables() {
this.api().columns().every(function () {
if (sum.summable[i] != undefined) {
$(this.footer()).addClass("text-right");
$(this.footer()).attr("id", "summable");
$(this.footer()).html(sum.summable[i]);
} else $(this.footer()).html("&nbsp;");
i++;
@ -735,6 +805,10 @@ function start_datatables() {
if (type === 'row') {
var selected = $this.data('selected').split(';');
selected = selected.filter(function (value, index, self) {
return value != '' && self.indexOf(value) === index;
});
var data = table.rows(indexes).data();
data.each(function (item) {
@ -752,8 +826,8 @@ function start_datatables() {
}
});
selected = selected.filter(function (entry) {
return entry != '';
selected = selected.filter(function (value, index, self) {
return value != '' && self.indexOf(value) === index;
});
$this.data('selected', selected.join(';'));
@ -769,21 +843,11 @@ function start_datatables() {
}
if (table.rows({
selected: true
}).count() > 0) {
container.find('.btn-csv').removeClass('disabled');
container.find('.btn-print').removeClass('disabled');
container.find('.btn-copy').removeClass('disabled');
container.find('.btn-csv').attr('disabled', false);
container.find('.btn-print').attr('disabled', false);
container.find('.btn-copy').attr('disabled', false);
selected: true
}).count() > 0) {
container.find('.table-btn').removeClass('disabled').attr('disabled', false);
} else {
container.find('.btn-csv').addClass('disabled');
container.find('.btn-print').addClass('disabled');
container.find('.btn-copy').addClass('disabled');
container.find('.btn-csv').attr('disabled', true);
container.find('.btn-print').attr('disabled', true);
container.find('.btn-copy').attr('disabled', true);
container.find('.table-btn').addClass('disabled').attr('disabled', true);
}
}
});
@ -793,6 +857,23 @@ function start_datatables() {
$('#mini-loader').show();
} else {
$('#mini-loader').hide();
//Reimposto il flag sulle righe ricaricate selezionate in precedenza
var selected = $this.data('selected').split(';');
table.rows().every(function (rowIdx, tableLoop, rowLoop) {
var object_span = $.parseHTML(this.data()[0])[0];
var id = $(object_span).data('id');
for (i = 0; i < selected.length; i++) {
var value = selected[i];
if (value == id) {
table.row(':eq(' + rowIdx + ')', {
page: 'current'
}).select();
}
}
});
}
})
}
@ -800,7 +881,7 @@ function start_datatables() {
}
function stopTableSorting(e) {
if (!e) var e = window.event
if (!e) var e = window.event;
e.cancelBubble = true;
if (e.stopPropagation) e.stopPropagation();
}
@ -918,10 +999,15 @@ jQuery.fn.selectClear = function () {
/**
* Resetta i contenuti di un <select> creato con select2.
*/
jQuery.fn.selectReset = function () {
jQuery.fn.selectReset = function (placeholder) {
this.selectClear();
this.empty();
if (placeholder != undefined) {
this.next().find('.select2-selection__placeholder').text(placeholder);
this.next().find('input.select2-search__field').attr('placeholder', placeholder);
}
return this;
};
@ -988,7 +1074,7 @@ jQuery.fn.selectData = function () {
// Inputmask
function start_inputmask(element) {
if( element == undefined ){
if (element == undefined) {
element = '';
} else {
element = element + ' ';
@ -996,33 +1082,32 @@ function start_inputmask(element) {
var date = moment.localeData().longDateFormat('L').toLowerCase();
$(element+".date-mask").inputmask(date, {
$(element + ".date-mask").inputmask(date, {
"placeholder": date
});
$(element+'.email-mask').inputmask('Regex', {
$(element + '.email-mask').inputmask('Regex', {
regex: "^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^-]+(?:\\.[a-zA-Z0-9_!#$%&'*+/=?`{|}~^-]+)*@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$",
});
$(element+'.alphanumeric-mask').inputmask('Regex', {
regex: "[A-Za-z0-9#_|\/\\-]*",
casing: "upper",
$(element + '.alphanumeric-mask').inputmask('Regex', {
regex: "[A-Za-z0-9#_|\/\\-.]*",
});
if (isMobile.any()) {
$(element+'.inputmask-decimal, '+element+'.date-mask, '+element+'.timestamp-mask').each(function () {
$(element + '.inputmask-decimal, ' + element + '.date-mask, ' + element + '.timestamp-mask').each(function () {
$(this).attr('type', 'tel');
});
} else {
$(element+'.inputmask-decimal').each(function () {
$(element + '.inputmask-decimal').each(function () {
var $this = $(this);
var min = $this.attr('min-value');
if (min == 'undefined') {
min = false;
}
var max = $this.attr('max-value');
var max = $this.attr('max-value');
if (max == 'undefined') {
max = false;
}
@ -1030,8 +1115,8 @@ function start_inputmask(element) {
$this.inputmask("decimal", {
min: min ? min : undefined,
allowMinus: !min || min < 0 ? true : false,
max: max ? max : undefined,
allowPlus: !max || max < 0 ? true : false,
max: max ? max : undefined,
allowPlus: !max || max < 0 ? true : false,
digits: $this.attr('decimals') ? $this.attr('decimals') : globals.cifre_decimali,
digitsOptional: true, // Necessario per un problema di inputmask con i numeri negativi durante l'init
enforceDigitsOnBlur: true,
@ -1116,17 +1201,17 @@ function session_set(session_array, value, clear, reload) {
reload = 0;
}
return $.get(globals.rootdir + "/ajax.php?op=session_set&session=" + session_array + "&value=" + value + "&clear=" + clear, function(data, status){
if (reload==1)
location.reload();
return $.get(globals.rootdir + "/ajax.php?op=session_set&session=" + session_array + "&value=" + value + "&clear=" + clear, function (data, status) {
if (reload == 1)
location.reload();
});
}
function session_keep_alive() {
$.get(globals.rootdir + '/core.php');
};
}
/**
* Funzione per gestire i contatori testuali nel formato x/total.
@ -1165,7 +1250,7 @@ Number.prototype.formatMoney = function (c, d, t) {
j = (j = i.length) > 3 ? j % 3 : 0;
return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : "");
}
};
String.prototype.toEnglish = function () {
return numeral(this.toString()).value();
@ -1225,16 +1310,36 @@ function message(element) {
swal({
title: title,
text: msg,
html: '<div id="swal-form" data-parsley-validate>' + msg + '</div>',
type: "warning",
showCancelButton: true,
confirmButtonText: button,
confirmButtonClass: btn_class,
onOpen: function () {
start_superselect();
start_inputmask();
},
preConfirm: function () {
$form = $('#swal-form');
$form.find(':input').each(function () {
data[$(this).attr('name')] = $(this).val();
});
if ($form.parsley().validate()) {
return new Promise(function (resolve) {
resolve();
});
} else {
$('.swal2-buttonswrapper button').each(function () {
$(this).prop('disabled', false);
});
}
}
}).then(
function (result) {
function () {
if (data["op"] == undefined) data["op"] = "delete";
var href = window.location.href.split("#")[0]
var href = window.location.href.split("#")[0];
if (data["href"] != undefined) {
href = data["href"];
delete data.href;
@ -1256,9 +1361,33 @@ function message(element) {
blank = data.blank != undefined && data.blank;
delete data.blank;
redirect(href, data, method, blank);
if (data.callback) {
$.ajax({
type: method,
crossDomain: true,
url: href,
data: data,
success: function (response) {
var callback = window[data.callback];
if (typeof callback === 'function') {
callback(response);
}
},
error: function (xhr, ajaxOptions, error) {
swal({
title: globals.translations.errorTitle,
html: globals.translations.errorMessage,
type: "error",
})
},
});
} else {
redirect(href, data, method, blank);
}
},
function (dismiss) {}
function (dismiss) {
}
);
}
@ -1298,8 +1427,8 @@ function redirect(href, data, method, blank) {
function setCookie(cname, cvalue, exdays) {
var d = new Date();
d.setTime(d.getTime() + (exdays*24*60*60*1000));
var expires = "expires="+ d.toUTCString();
d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toUTCString();
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
}
@ -1307,7 +1436,7 @@ function getCookie(cname) {
var name = cname + "=";
var decodedCookie = decodeURIComponent(document.cookie);
var ca = decodedCookie.split(';');
for(var i = 0; i <ca.length; i++) {
for (var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1);
@ -1318,3 +1447,60 @@ function getCookie(cname) {
}
return "";
}
/**
* Funzione per controllare se un file esiste
*/
function UrlExists(url) {
var http = new XMLHttpRequest();
http.open('HEAD', url, false);
http.send();
return http.status != 404;
}
function buttonLoading(button) {
var $this = $(button);
var result = [
$this.html(),
$this.attr("class")
];
$this.html('<i class="fa fa-spinner fa-pulse fa-fw"></i> Attendere...');
$this.addClass("btn-warning");
$this.prop("disabled", true);
return result;
}
function buttonRestore(button, loadingResult) {
var $this = $(button);
$this.html(loadingResult[0]);
$this.attr("class", "");
$this.addClass(loadingResult[1]);
$this.prop("disabled", false);
}
/**
* Sostituisce i caratteri speciali per la ricerca attraverso le tabelle Datatables.
*
* @param string field
*
* @return string
*/
function searchFieldName(field) {
return field.replace(' ', '-').replace('.', '');
}
/**
* Salva nella sessione la ricerca per le tabelle Datatables.
*
* @param int module_id
* @param string field
* @param mixed value
*/
function searchTable(module_id, field, value) {
session_set('module_' + module_id + ',' + 'search_' + searchFieldName(field), value, 0);
}

View File

@ -1,5 +1,10 @@
<?php
/**
* Funzioni fondamentali per il corretto funzionamento del nucleo del progetto.
*
* @since 2.3
*/
/**
* Esegue il redirect.
@ -36,6 +41,13 @@ function sanitizeFilename($filename)
return $filename;
}
/**
* Elimina i file indicati.
*
* @param array $files
*
* @return bool
*/
function delete($files)
{
// Filesystem Symfony
@ -51,6 +63,13 @@ function delete($files)
return true;
}
/**
* Controlla l'esistenza e i permessi di scrittura sul percorso indicato.
*
* @param string $path
*
* @return bool
*/
function directory($path)
{
if (is_dir($path) && is_writable($path)) {
@ -74,230 +93,45 @@ function directory($path)
/**
* Copy a file, or recursively copy a folder and its contents.
*
* @author Aidan Lister <aidan@php.net>
*
* @version 1.0.1
*
* @see http://aidanlister.com/repos/v/function.copyr.php
*
* @param string $source
* Source path
* @param string $dest
* Destination path
* @param array|string $ignores
* Paths to ingore
* @param string $source Source path
* @param string $dest Destination path
* @param array|string $ignores Paths to ingore
*
* @return bool Returns TRUE on success, FALSE on failure
*/
function copyr($source, $destination, $ignores = [])
{
$finder = Symfony\Component\Finder\Finder::create()
if (!directory($destination)) {
return false;
}
$files = Symfony\Component\Finder\Finder::create()
->files()
->exclude((array) $ignores['dirs'])
->ignoreDotFiles(true)
->ignoreDotFiles(false)
->ignoreVCS(true)
->in($source);
foreach ((array) $ignores['files'] as $value) {
$finder->notName($value);
$files->notName($value);
}
foreach ($finder as $file) {
$filename = rtrim($destination, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.$file->getRelativePathname();
$result = true;
// Filesystem Symfony
$fs = new Symfony\Component\Filesystem\Filesystem();
// Filesystem Symfony
$fs = new Symfony\Component\Filesystem\Filesystem();
foreach ($files as $file) {
$filename = rtrim($destination, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.$file->getRelativePathname();
// Copia
try {
$fs->copy($file, $filename);
} catch (Symfony\Component\Filesystem\Exception\IOException $e) {
$result = false;
}
}
return true;
}
/**
* Crea un file zip comprimendo ricorsivamente tutte le sottocartelle a partire da una cartella specificata.
* *.
*
* @param string $source
* @param string $destination
* @param array $ignores
*/
function create_zip($source, $destination, $ignores = [])
{
if (!extension_loaded('zip')) {
$_SESSION['errors'][] = tr('Estensione zip non supportata!');
return false;
}
$zip = new ZipArchive();
$result = $zip->open($destination, ZIPARCHIVE::CREATE);
if ($result === true && is_writable(dirname($destination))) {
$finder = Symfony\Component\Finder\Finder::create()
->files()
->exclude((array) $ignores['dirs'])
->ignoreDotFiles(true)
->ignoreVCS(true)
->in($source);
foreach ((array) $ignores['files'] as $value) {
$finder->notName($value);
}
foreach ($finder as $file) {
$zip->addFile($file, $file->getRelativePathname());
}
$zip->close();
} else {
$_SESSION['errors'][] = tr("Errore durante la creazione dell'archivio!");
}
return $result === true;
}
/**
* Controllo dei file zip e gestione errori.
*
* @param string $zip_file
*
* @return string|bool
*/
function checkZip($zip_file)
{
$errno = zip_open($zip_file);
zip_close($errno);
if (!is_resource($errno)) {
// using constant name as a string to make this function PHP4 compatible
$errors = [
ZIPARCHIVE::ER_MULTIDISK => tr('archivi multi-disco non supportati'),
ZIPARCHIVE::ER_RENAME => tr('ridenominazione del file temporaneo fallita'),
ZIPARCHIVE::ER_CLOSE => tr('impossibile chiudere il file zip'),
ZIPARCHIVE::ER_SEEK => tr('errore durante la ricerca dei file'),
ZIPARCHIVE::ER_READ => tr('errore di lettura'),
ZIPARCHIVE::ER_WRITE => tr('errore di scrittura'),
ZIPARCHIVE::ER_CRC => tr('errore CRC'),
ZIPARCHIVE::ER_ZIPCLOSED => tr("l'archivio zip è stato chiuso"),
ZIPARCHIVE::ER_NOENT => tr('file non trovato'),
ZIPARCHIVE::ER_EXISTS => tr('il file esiste già'),
ZIPARCHIVE::ER_OPEN => tr('impossibile aprire il file'),
ZIPARCHIVE::ER_TMPOPEN => tr('impossibile creare il file temporaneo'),
ZIPARCHIVE::ER_ZLIB => tr('errore nella libreria Zlib'),
ZIPARCHIVE::ER_MEMORY => tr("fallimento nell'allocare memoria"),
ZIPARCHIVE::ER_CHANGED => tr('voce modificata'),
ZIPARCHIVE::ER_COMPNOTSUPP => tr('metodo di compressione non supportato'),
ZIPARCHIVE::ER_EOF => tr('fine del file non prevista'),
ZIPARCHIVE::ER_INVAL => tr('argomento non valido'),
ZIPARCHIVE::ER_NOZIP => tr('file zip non valido'),
ZIPARCHIVE::ER_INTERNAL => tr('errore interno'),
ZIPARCHIVE::ER_INCONS => tr('archivio zip inconsistente'),
ZIPARCHIVE::ER_REMOVE => tr('impossibile rimuovere la voce'),
ZIPARCHIVE::ER_DELETED => tr('voce eliminata'),
];
if (isset($errors[$errno])) {
return tr('Errore').': '.$errors[$errno];
}
return false;
} else {
return true;
}
}
/**
* Individua la differenza tra le date indicate.
* $interval può essere:
* yyyy - Number of full years
* q - Number of full quarters
* m - Number of full months
* y - Difference between day numbers
* (eg 1st Jan 2004 is "1", the first day. 2nd Feb 2003 is "33". The datediff is "-32".)
* d - Number of full days
* w - Number of full weekdays
* ww - Number of full weeks
* h - Number of full hours
* n - Number of full minutes
* s - Number of full seconds (default).
*
* @param unknown $interval
* @param unknown $datefrom
* @param unknown $dateto
* @param string $using_timestamps
*/
function datediff($interval, $datefrom, $dateto, $using_timestamps = false)
{
if (!$using_timestamps) {
$datefrom = strtotime($datefrom, 0);
$dateto = strtotime($dateto, 0);
}
$difference = $dateto - $datefrom; // Difference in seconds
switch ($interval) {
case 'yyyy': // Number of full years
$years_difference = floor($difference / 31536000);
if (mktime(date('H', $datefrom), date('i', $datefrom), date('s', $datefrom), date('n', $datefrom), date('j', $datefrom), date('Y', $datefrom) + $years_difference) > $dateto) {
--$years_difference;
}
if (mktime(date('H', $dateto), date('i', $dateto), date('s', $dateto), date('n', $dateto), date('j', $dateto), date('Y', $dateto) - ($years_difference + 1)) > $datefrom) {
++$years_difference;
}
$datediff = $years_difference;
break;
case 'q': // Number of full quarters
$quarters_difference = floor($difference / 8035200);
while (mktime(date('H', $datefrom), date('i', $datefrom), date('s', $datefrom), date('n', $datefrom) + ($quarters_difference * 3), date('j', $dateto), date('Y', $datefrom)) < $dateto) {
++$months_difference;
}
--$quarters_difference;
$datediff = $quarters_difference;
break;
case 'm': // Number of full months
$months_difference = floor($difference / 2678400);
while (mktime(date('H', $datefrom), date('i', $datefrom), date('s', $datefrom), date('n', $datefrom) + ($months_difference), date('j', $dateto), date('Y', $datefrom)) < $dateto) {
++$months_difference;
}
--$months_difference;
$datediff = $months_difference;
break;
case 'y': // Difference between day numbers
$datediff = date('z', $dateto) - date('z', $datefrom);
break;
case 'd': // Number of full days
$datediff = floor($difference / 86400);
break;
case 'w': // Number of full weekdays
$days_difference = floor($difference / 86400);
$weeks_difference = floor($days_difference / 7); // Complete weeks
$first_day = date('w', $datefrom);
$days_remainder = floor($days_difference % 7);
$odd_days = $first_day + $days_remainder; // Do we have a Saturday or Sunday in the remainder?
if ($odd_days > 7) { // Sunday
--$days_remainder;
}
if ($odd_days > 6) { // Saturday
--$days_remainder;
}
$datediff = ($weeks_difference * 5) + $days_remainder;
break;
case 'ww': // Number of full weeks
$datediff = floor($difference / 604800);
break;
case 'h': // Number of full hours
$datediff = floor($difference / 3600);
break;
case 'n': // Number of full minutes
$datediff = floor($difference / 60);
break;
default: // Number of full seconds (default)
$datediff = $difference;
break;
}
return $datediff;
return $result;
}
/**
@ -337,68 +171,6 @@ function getOS()
return tr('Altro');
}
/**
* Verifica che il nome del file non sia già usato nella cartella inserita, nel qual caso aggiungo un suffisso.
*
* @param string $filename
* @param string $dir
*
* @return string
*/
function unique_filename($filename, $dir)
{
$f = pathinfo($filename);
$suffix = 1;
while (file_exists($dir.'/'.$filename)) {
$filename = $f['filename'].'_'.$suffix.'.'.$f['extension'];
++$suffix;
}
return $filename;
}
/**
* Crea le thumbnails di $filename da dentro $dir e le salva in $dir.
*
* @param string $tmp
* @param string $filename
* @param string $dir
*
* @return bool
*/
function create_thumbnails($tmp, $filename, $dir)
{
$infos = pathinfo($filename);
$name = $infos['filename'];
$extension = strtolower($infos['extension']);
if (!directory($dir)) {
return false;
}
$driver = extension_loaded('gd') ? 'gd' : 'imagick';
Intervention\Image\ImageManagerStatic::configure(['driver' => $driver]);
$img = Intervention\Image\ImageManagerStatic::make($tmp);
$img->resize(600, null, function ($constraint) {
$constraint->aspectRatio();
});
$img->save(slashes($dir.'/'.$name.'.'.$extension));
$img->resize(250, null, function ($constraint) {
$constraint->aspectRatio();
});
$img->save(slashes($dir.'/'.$name.'_thumb250.'.$extension));
$img->resize(100, null, function ($constraint) {
$constraint->aspectRatio();
});
$img->save(slashes($dir.'/'.$name.'_thumb100.'.$extension));
return true;
}
/**
* Ottiene l'indirizzo IP del client.
*
@ -407,7 +179,7 @@ function create_thumbnails($tmp, $filename, $dir)
function get_client_ip()
{
$ipaddress = '';
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
$ipaddress = $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ipaddress = $_SERVER['HTTP_X_FORWARDED_FOR'];
@ -417,10 +189,10 @@ function get_client_ip()
$ipaddress = $_SERVER['HTTP_FORWARDED_FOR'];
} elseif (!empty($_SERVER['HTTP_FORWARDED'])) {
$ipaddress = $_SERVER['HTTP_FORWARDED'];
} elseif (!empty($_SERVER['REMOTE_ADDR']) AND $_SERVER['REMOTE_ADDR']!='127.0.0.1' ) {
} elseif (!empty($_SERVER['REMOTE_ADDR']) and $_SERVER['REMOTE_ADDR'] != '127.0.0.1') {
$ipaddress = $_SERVER['REMOTE_ADDR'];
} elseif (!empty(getHostByName(getHostName()))){
$ipaddress = getHostByName(getHostName());
} elseif (!empty(gethostbyname(gethostname()))) {
$ipaddress = gethostbyname(gethostname());
} else {
$ipaddress = 'UNKNOWN';
}
@ -435,63 +207,51 @@ function get_client_ip()
*/
function translateTemplate()
{
global $id_module;
global $id_record;
global $id_plugin;
global $id_parent;
global $operations_log;
$id_record = filter('id_record');
$id_parent = filter('id_parent');
$id_email = filter('id_email');
$id_module = Modules::getCurrent()['id'];
$id_plugin = Plugins::getCurrent()['id'];
$template = ob_get_clean();
$template = \HTMLBuilder\HTMLBuilder::replace($template);
$template = str_replace('$id_module$', $id_module, $template);
$template = str_replace('$id_plugin$', $id_plugin, $template);
$template = str_replace('$id_record$', $id_record, $template);
$template = str_replace('$id_parent$', $id_parent, $template);
// Completamento delle informazioni estese sulle azioni dell'utente
if (Auth::check() && !empty($operations_log) && !empty($_SESSION['infos'])) {
$user = Auth::user();
$logger = Monolog\Registry::getInstance('logs');
$template = \HTMLBuilder\HTMLBuilder::replace($template);
foreach ($_SESSION['infos'] as $value) {
$logger->info($value.PHP_EOL.json_encode([
'user' => $user['username'],
]));
// Informazioni estese sulle azioni dell'utente
if (!empty(post('op')) && post('op') != 'send-email') {
operationLog(post('op'));
}
// Retrocompatibilità
if (!empty($_SESSION['infos'])) {
foreach ($_SESSION['infos'] as $message) {
flash()->info($message);
}
}
if (!empty($_SESSION['warnings'])) {
foreach ($_SESSION['warnings'] as $message) {
flash()->warning($message);
}
}
if (!empty($_SESSION['errors'])) {
foreach ($_SESSION['errors'] as $message) {
flash()->error($message);
}
}
// Annullo le notifiche (AJAX)
if (isAjaxRequest()) {
unset($_SESSION['infos']);
flash()->clearMessage('info');
}
echo $template;
}
/**
* Sostituisce la prima occorenza di una determinata stringa.
*
* @param string $str_pattern
* @param string $str_replacement
* @param string $string
*
* @since 2.3
*
* @return string
*/
function str_replace_once($str_pattern, $str_replacement, $string)
{
if (strpos($string, $str_pattern) !== false) {
$occurrence = strpos($string, $str_pattern);
return substr_replace($string, $str_replacement, strpos($string, $str_pattern), strlen($str_pattern));
}
return $string;
}
/**
* Restituisce il percorso del filesystem in modo indipendente dal sistema operativo.
*
@ -506,122 +266,6 @@ function slashes($string)
return str_replace(['\\', '/'], DIRECTORY_SEPARATOR, $string);
}
/**
* Prepara il parametro inserito per l'inserimento in una query SQL.
* Attenzione: protezione di base contro SQL Injection.
*
* @param string $parameter
*
* @since 2.3
*
* @return string
*/
function prepare($parameter)
{
return p($parameter);
}
/**
* Prepara il parametro inserito per l'inserimento in una query SQL.
* Attenzione: protezione di base contro SQL Injection.
*
* @param string $parameter
*
* @since 2.3
*
* @return string
*/
function p($parameter)
{
return Database::getConnection()->prepare($parameter);
}
/**
* Restituisce la traduzione del messaggio inserito.
*
* @param string $string
* @param array $parameters
* @param string $domain
* @param string $locale
*
* @since 2.3
*
* @return string
*/
function tr($string, $parameters = [], $operations = [])
{
return Translator::translate($string, $parameters, $operations);
}
// Retrocompatibilità (con la funzione gettext)
if (!function_exists('_')) {
function _($string, $parameters = [], $operations = [])
{
return tr($string, $parameters, $operations);
}
}
/**
* Legge il valore di un'impostazione dalla tabella zz_settings.
* Se descrizione = 1 e il tipo è 'query=' mi restituisce il valore del campo descrizione della query.
*
* @param string $name
* @param string $sezione
* @param string $descrizione
*
* @return mixed
*/
function get_var($nome, $sezione = null, $descrizione = false, $again = false)
{
return Settings::get($nome, $sezione, $descrizione, $again);
}
/**
* Restituisce il contenuto sanitarizzato dell'input dell'utente.
*
* @param string $param Nome del parametro
* @param string $rule Regola di filtraggio
* @param string $method Posizione del parametro (post o get)
*
* @since 2.3
*
* @return string
*/
function filter($param, $method = null)
{
return Filter::getValue($param, $method = null);
}
/**
* Restituisce il contenuto sanitarizzato dell'input dell'utente.
*
* @param string $param Nome del parametro
* @param string $rule Regola di filtraggio
*
* @since 2.3
*
* @return string
*/
function post($param, $rule = 'text')
{
return Filter::getValue($param, 'post');
}
/**
* Restituisce il contenuto sanitarizzato dell'input dell'utente.
*
* @param string $param Nome del parametro
* @param string $rule Regola di filtraggio
*
* @since 2.3
*
* @return string
*/
function get($param, $rule = 'text')
{
return Filter::getValue($param, 'get');
}
/**
* Controlla se è in corso una richiesta AJAX generata dal progetto.
*
@ -634,45 +278,6 @@ function isAjaxRequest()
return \Whoops\Util\Misc::isAjaxRequest() && filter('ajax') !== null;
}
/**
* Esegue una somma precisa tra due interi/array.
*
* @param array|float $first
* @param array|float $second
* @param int $decimals
*
* @since 2.3
*
* @return float
*/
function sum($first, $second = null, $decimals = null)
{
$first = (array) $first;
$second = (array) $second;
$array = array_merge($first, $second);
$result = 0;
if (!is_numeric($decimals)) {
$decimals = is_numeric($decimals) ? $decimals : Translator::getFormatter()->getPrecision();
}
$bcadd = function_exists('bcadd');
foreach ($array as $value) {
$value = round($value, $decimals);
if ($bcadd) {
$result = bcadd($result, $value, $decimals);
} else {
$result += $value;
}
}
return floatval($result);
}
/**
* Effettua le operazioni automatiche di redirect tra le pagine.
*
@ -705,6 +310,8 @@ function redirectOperation($id_module, $id_record)
*
* @param string $string
*
* @since 2.3
*
* @return string
*/
function prepareToField($string)
@ -715,6 +322,8 @@ function prepareToField($string)
/**
* Restituisce se l'user-agent (browser web) è una versione mobile.
*
* @since 2.3
*
* @return bool
*/
function isMobile()
@ -725,6 +334,8 @@ function isMobile()
/**
* Restituisce il percorso derivante dal file in esecuzione.
*
* @since 2.4.1
*
* @return string
*/
function getURLPath()
@ -740,3 +351,51 @@ function getURLPath()
return slashes($path);
}
/**
* Sostituisce i caratteri speciali per la ricerca attraverso le tabelle Datatables.
*
* @since 2.4.2
*
* @param string $field
*
* @return string
*/
function searchFieldName($field)
{
return str_replace([' ', '.'], ['-', ''], $field);
}
/**
* Registra un'azione specifica nei log.
*
* @since 2.4.3
*
* @param string $operation
* @param int $id_record
* @param int $id_module
* @param int $id_plugin
* @param int $id_parent
* @param int $id_email
* @param array $options
*/
function operationLog($operation, array $ids = [], array $options = [])
{
if (!Auth::check()) {
return false;
}
$ids['id_module'] = $ids['id_module'] ?: Modules::getCurrent()['id'];
$ids['id_plugin'] = $ids['id_plugin'] ?: Plugins::getCurrent()['id'];
$ids['id_record'] = $ids['id_record'] ?: filter('id_record');
//$ids['id_parent'] = $ids['id_parent'] ?: filter('id_parent');
database()->insert('zz_operations', array_merge($ids, [
'op' => $operation,
'id_utente' => Auth::user()['id'],
'options' => !empty($options) ? json_encode($options) : null,
]));
return true;
}

177
lib/helpers.php Normal file
View File

@ -0,0 +1,177 @@
<?php
/**
* Funzioni di aiuto per la semplificazione del codice.
*
* @since 2.4.2
*/
/**
* Restituisce l'oggetto dedicato alla gestione della connessione con il database.
*
* @return \Database
*/
function database()
{
return \Database::getConnection();
}
/**
* Prepara il parametro inserito per l'inserimento in una query SQL.
* Attenzione: protezione di base contro SQL Injection.
*
* @param string $parameter
*
* @since 2.3
*
* @return mixed
*/
function prepare($parameter)
{
return database()->prepare($parameter);
}
/**
* Restituisce il contenuto sanitarizzato dell'input dell'utente.
*
* @param string $param Nome del parametro
* @param string $method Posizione del parametro (post o get)
* @param bool $raw Restituire il valore non formattato
*
* @since 2.3
*
* @return string
*/
function filter($param, $method = null, $raw = false)
{
return \Filter::getValue($param, $method, $raw);
}
/**
* Restituisce il contenuto sanitarizzato dell'input dell'utente.
*
* @param string $param Nome del parametro
* @param bool $raw Restituire il valore non formattato
*
* @since 2.3
*
* @return string
*/
function post($param, $raw = false)
{
return \Filter::getValue($param, 'post', $raw);
}
/**
* Restituisce il contenuto sanitarizzato dell'input dell'utente.
*
* @param string $param Nome del parametro
* @param bool $raw Restituire il valore non formattato
*
* @since 2.3
*
* @return string
*/
function get($param, $raw = false)
{
return \Filter::getValue($param, 'get', $raw);
}
/**
* Legge il valore di un'impostazione dalla tabella zz_settings.
*
* @param string $name
* @param bool $again
*
* @since 2.4.2
*
* @return string
*/
function setting($name, $again = false)
{
return \Settings::getValue($name);
}
/**
* Restituisce l'oggetto dedicato alla gestione dei messaggi per l'utente.
*
* @since 2.4.2
*
* @return \Util\Messages
*/
function flash()
{
return App::flash();
}
/**
* Restituisce l'oggetto dedicato alla gestione dell'autenticazione degli utente.
*
* @since 2.4.2
*
* @return \Auth
*/
function auth()
{
return \Auth::getInstance();
}
/**
* Restituisce l'oggetto dedicato alla gestione della traduzione del progetto.
*
* @since 2.4.2
*
* @return \Translator
*/
function trans()
{
return \Translator::getInstance();
}
/**
* Restituisce l'oggetto dedicato alla gestione della conversione di numeri e date.
*
* @since 2.4.2
*
* @return \Intl\Formatter
*/
function formatter()
{
return \Translator::getFormatter();
}
/**
* Restituisce la traduzione del messaggio inserito.
*
* @param string $string
* @param array $parameters
* @param string $operations
*
* @since 2.3
*
* @return string
*/
function tr($string, $parameters = [], $operations = [])
{
return \Translator::translate($string, $parameters, $operations);
}
// Retrocompatibilità (con la funzione gettext)
if (!function_exists('_')) {
function _($string, $parameters = [], $operations = [])
{
return tr($string, $parameters, $operations);
}
}
/**
* Restituisce l'oggetto dedicato alla gestione dei log.
*
* @since 2.4.2
*
* @return \Monolog\Logger
*/
function logger()
{
return Monolog\Registry::getInstance('logs');
}

View File

@ -1,4 +1,7 @@
$(document).ready(function () {
// Inizializzazzione dei box AdminLTE
$('.box').boxWidget();
// Modal di default
$('[data-href]').not('.ask, .bound').click(function () {
launch_modal($(this).data('title'), $(this).data('href'), 1, $(this).data('target'));
@ -41,7 +44,7 @@ $(document).ready(function () {
today: 'fa fa-street-view',
clear: 'fa fa-trash',
close: 'fa fa-times'
}
};
$('.timestamp-picker').each(function () {
$this = $(this);
@ -51,11 +54,26 @@ $(document).ready(function () {
collapse: false,
sideBySide: true,
useCurrent: false,
stepping: 5,
stepping: 5,
widgetPositioning: {
horizontal: 'left',
vertical: 'bottom'
},
minDate: moment($this.attr('min-date')).isValid() ? $this.attr('min-date') : false,
maxDate: moment($this.attr('max-date')).isValid() ? $this.attr('max-date') : false,
});
});
//fix per timestamp-picker non visibile con la classe table-responsive
$('.timestamp-picker').each(function () {
$this = $(this);
$this.on("dp.show", function (e) {
$('#tecnici > div').removeClass('table-responsive');
});
$this.on("dp.hide", function (e) {
$('#tecnici > div').addClass('table-responsive');
})
});
$('.datepicker').each(function () {
$this = $(this);

View File

@ -2,6 +2,12 @@
use Stringy\Stringy as S;
/*
* Funzioni esterne di utilità per il progetto.
*
* @since 2.3
*/
if (!function_exists('array_column')) {
/**
* Pluck an array of values from an array.
@ -11,7 +17,7 @@ if (!function_exists('array_column')) {
*
* @since 2.3
*
* @return plucked array only with key data
* @return array plucked array only with key data
*/
function array_column($array, $key)
{
@ -21,6 +27,24 @@ if (!function_exists('array_column')) {
}
}
if (!function_exists('array_clean')) {
/**
* Pulisce i contenuti vuoti di un array.
*
* @param $array
*
* @since 2.3.2
*
* @return array
*/
function array_clean($array)
{
return array_unique(array_values(array_filter($array, function ($value) {
return !empty($value);
})));
}
}
if (!function_exists('starts_with')) {
/**
* Check if a string starts with the given string.
@ -53,6 +77,30 @@ if (!function_exists('ends_with')) {
}
}
if (!function_exists('str_replace_once')) {
/**
* Sostituisce la prima occorenza di una determinata stringa.
*
* @param string $str_pattern
* @param string $str_replacement
* @param string $string
*
* @since 2.3
*
* @return string
*/
function str_replace_once($str_pattern, $str_replacement, $string)
{
if (strpos($string, $str_pattern) !== false) {
$occurrence = strpos($string, $str_pattern);
return substr_replace($string, $str_replacement, strpos($string, $str_pattern), strlen($str_pattern));
}
return $string;
}
}
if (!function_exists('str_contains')) {
/**
* Check if a string contains the given string.
@ -69,13 +117,53 @@ if (!function_exists('str_contains')) {
}
}
if (!function_exists('str_to_lower')) {
/**
* Converts a string in the lower-case version.
*
* @param string $string
*
* @return bool
*/
function str_to_lower($string)
{
return S::create($string)->toLowerCase();
}
}
if (!function_exists('str_to_upper')) {
/**
* Converts a string in the upper-case version.
*
* @param string $string
*
* @return bool
*/
function str_to_upper($string)
{
return S::create($string)->toUpperCase();
}
}
if (!function_exists('replace')) {
/**
* Sostituisce gli elementi dell'array all'interno della stringa.
*
* @param string $string
* @param array $array
*
* @return string
*/
function replace($string, $array)
{
return str_replace(array_keys($array), array_values($array), $string);
}
}
if (!function_exists('random_string')) {
/**
* Generates a string of random characters.
*
* @throws LengthException If $length is bigger than the available
* character pool and $no_duplicate_chars is
* enabled
*
* @param int $length The length of the string to
* generate
@ -91,6 +179,10 @@ if (!function_exists('random_string')) {
* @param bool $no_duplicate_chars whether or not to only use
* characters once in the string
*
* @throws LengthException If $length is bigger than the available
* character pool and $no_duplicate_chars is
* enabled
*
* @return string
*/
function random_string($length = 16, $human_friendly = true, $include_symbols = false, $no_duplicate_chars = false)
@ -192,12 +284,14 @@ if (!function_exists('download')) {
*/
function download($file, $filename = null)
{
ob_end_clean();
if (!headers_sent()) {
$filename = !empty($filename) ? $filename : basename($file);
// Required for some browsers
if (ini_get('zlib.output_compression')) {
@ini_set('zlib.output_compression', 'Off');
ini_set('zlib.output_compression', 'Off');
}
header('Pragma: public');
@ -405,3 +499,199 @@ if (!function_exists('readSQLFile')) {
return $queryLine;
}
}
if (!function_exists('get_remote_data')) {
/**
* echo get_remote_data("http://example.com/"); // GET request
* echo get_remote_data("http://example.com/", "var2=something&var3=blabla" ); // POST request.
*
* Automatically handles FOLLOWLOCATION problem;
* Using 'replace_src'=>true, it fixes domain-relative urls (i.e.: src="./file.jpg" -----> src="http://example.com/file.jpg" )
* Using 'schemeless'=>true, it converts urls in schemeless (i.e.: src="http://exampl.. -----> src="//exampl... )\
*
* @source tazotodua/useful-php-scripts
*/
function get_remote_data($url, $post_paramtrs = false, $extra = ['schemeless' => true, 'replace_src' => true, 'return_array' => false])
{
// start curl
$c = curl_init();
curl_setopt($c, CURLOPT_URL, $url);
curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
//if parameters were passed to this function, then transform into POST method.. (if you need GET request, then simply change the passed URL)
if ($post_paramtrs) {
curl_setopt($c, CURLOPT_POST, true);
curl_setopt($c, CURLOPT_POSTFIELDS, (is_array($post_paramtrs) ? http_build_query($post_paramtrs) : $post_paramtrs));
}
curl_setopt($c, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($c, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($c, CURLOPT_COOKIE, 'CookieName1=Value;');
$headers[] = 'User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:76.0) Gecko/20100101 Firefox/76.0';
$headers[] = 'Pragma: ';
$headers[] = 'Cache-Control: max-age=0';
if (!empty($post_paramtrs) && !is_array($post_paramtrs) && is_object(json_decode($post_paramtrs))) {
$headers[] = 'Content-Type: application/json';
$headers[] = 'Content-Length: '.strlen($post_paramtrs);
}
curl_setopt($c, CURLOPT_HTTPHEADER, $headers);
curl_setopt($c, CURLOPT_MAXREDIRS, 10);
//if SAFE_MODE or OPEN_BASEDIR is set,then FollowLocation cant be used.. so...
$follow_allowed = (ini_get('open_basedir') || ini_get('safe_mode')) ? false : true;
if ($follow_allowed) {
curl_setopt($c, CURLOPT_FOLLOWLOCATION, 1);
}
curl_setopt($c, CURLOPT_CONNECTTIMEOUT, 9);
curl_setopt($c, CURLOPT_REFERER, $url);
curl_setopt($c, CURLOPT_TIMEOUT, 60);
curl_setopt($c, CURLOPT_AUTOREFERER, true);
curl_setopt($c, CURLOPT_ENCODING, 'gzip,deflate');
curl_setopt($c, CURLOPT_HEADER, !empty($extra['return_array']));
$data = curl_exec($c);
if (!empty($extra['return_array'])) {
preg_match("/(.*?)\r\n\r\n((?!HTTP\/\d\.\d).*)/si", $data, $x);
preg_match_all('/(.*?): (.*?)\r\n/i', trim('head_line: '.$x[1]), $headers_, PREG_SET_ORDER);
foreach ($headers_ as $each) {
$header[$each[1]] = $each[2];
}
$data = trim($x[2]);
}
$status = curl_getinfo($c);
curl_close($c);
// if redirected, then get that redirected page
if ($status['http_code'] == 301 || $status['http_code'] == 302) {
//if we FOLLOWLOCATION was not allowed, then re-get REDIRECTED URL
//p.s. WE dont need "else", because if FOLLOWLOCATION was allowed, then we wouldnt have come to this place, because 301 could already auto-followed by curl :)
if (!$follow_allowed) {
//if REDIRECT URL is found in HEADER
if (empty($redirURL)) {
if (!empty($status['redirect_url'])) {
$redirURL = $status['redirect_url'];
}
}
//if REDIRECT URL is found in RESPONSE
if (empty($redirURL)) {
preg_match('/(Location:|URI:)(.*?)(\r|\n)/si', $data, $m);
if (!empty($m[2])) {
$redirURL = $m[2];
}
}
//if REDIRECT URL is found in OUTPUT
if (empty($redirURL)) {
preg_match('/moved\s\<a(.*?)href\=\"(.*?)\"(.*?)here\<\/a\>/si', $data, $m);
if (!empty($m[1])) {
$redirURL = $m[1];
}
}
//if URL found, then re-use this function again, for the found url
if (!empty($redirURL)) {
$t = debug_backtrace();
return call_user_func($t[0]['function'], trim($redirURL), $post_paramtrs);
}
}
}
// if not redirected,and nor "status 200" page, then error..
elseif ($status['http_code'] != 200) {
$data = "ERRORCODE22 with $url<br/><br/>Last status codes:".json_encode($status)."<br/><br/>Last data got:$data";
}
//URLS correction
if (function_exists('url_corrections_for_content_HELPER')) {
$data = url_corrections_for_content_HELPER($data, $status['url'], ['schemeless' => !empty($extra['schemeless']), 'replace_src' => !empty($extra['replace_src']), 'rawgit_replace' => !empty($extra['rawgit_replace'])]);
}
$answer = (!empty($extra['return_array']) ? ['data' => $data, 'header' => $header, 'info' => $status] : $data);
return $answer;
}
function url_corrections_for_content_HELPER($content = false, $url = false, $extra_opts = ['schemeless' => false, 'replace_src' => false, 'rawgit_replace' => false])
{
$GLOBALS['rdgr']['schemeless'] = $extra_opts['schemeless'];
$GLOBALS['rdgr']['replace_src'] = $extra_opts['replace_src'];
$GLOBALS['rdgr']['rawgit_replace'] = $extra_opts['rawgit_replace'];
if ($GLOBALS['rdgr']['schemeless'] || $GLOBALS['rdgr']['replace_src']) {
if ($url) {
$GLOBALS['rdgr']['parsed_url'] = parse_url($url);
$GLOBALS['rdgr']['urlparts']['domain_X'] = $GLOBALS['rdgr']['parsed_url']['scheme'].'://'.$GLOBALS['rdgr']['parsed_url']['host'];
$GLOBALS['rdgr']['urlparts']['path_X'] = stripslashes(dirname($GLOBALS['rdgr']['parsed_url']['path']).'/');
$GLOBALS['rdgr']['all_protocols'] = ['adc', 'afp', 'amqp', 'bacnet', 'bittorrent', 'bootp', 'camel', 'dict', 'dns', 'dsnp', 'dhcp', 'ed2k', 'empp', 'finger', 'ftp', 'gnutella', 'gopher', 'http', 'https', 'imap', 'irc', 'isup', 'javascript', 'ldap', 'mime', 'msnp', 'map', 'modbus', 'mosh', 'mqtt', 'nntp', 'ntp', 'ntcip', 'openadr', 'pop3', 'radius', 'rdp', 'rlogin', 'rsync', 'rtp', 'rtsp', 'ssh', 'sisnapi', 'sip', 'smtp', 'snmp', 'soap', 'smb', 'ssdp', 'stun', 'tup', 'telnet', 'tcap', 'tftp', 'upnp', 'webdav', 'xmpp'];
}
$GLOBALS['rdgr']['ext_array'] = [
'src' => ['audio', 'embed', 'iframe', 'img', 'input', 'script', 'source', 'track', 'video'],
'srcset' => ['source'],
'data' => ['object'],
'href' => ['link', 'area', 'a'],
'action' => ['form'],
//'param', 'applet' and 'base' tags are exclusion, because of a bit complex structure
];
$content = preg_replace_callback(
'/<(((?!<).)*?)>/si', //avoids unclosed & closing tags
function ($matches_A) {
$content_A = $matches_A[0];
$tagname = preg_match('/((.*?)(\s|$))/si', $matches_A[1], $n) ? $n[2] : '';
foreach ($GLOBALS['rdgr']['ext_array'] as $key => $value) {
if (in_array($tagname, $value)) {
preg_match('/ '.$key.'=(\'|\")/i', $content_A, $n);
if (!empty($n[1])) {
$GLOBALS['rdgr']['aphostrope_type'] = $n[1];
$content_A = preg_replace_callback(
'/( '.$key.'='.$GLOBALS['rdgr']['aphostrope_type'].')(.*?)('.$GLOBALS['rdgr']['aphostrope_type'].')/i',
function ($matches_B) {
$full_link = $matches_B[2];
//correction to files/urls
if (!empty($GLOBALS['rdgr']['replace_src'])) {
//if not schemeless url
if (substr($full_link, 0, 2) != '//') {
$replace_src_allow = true;
//check if the link is a type of any special protocol
foreach ($GLOBALS['rdgr']['all_protocols'] as $each_protocol) {
//if protocol found - dont continue
if (substr($full_link, 0, strlen($each_protocol) + 1) == $each_protocol.':') {
$replace_src_allow = false;
break;
}
}
if ($replace_src_allow) {
$full_link = $GLOBALS['rdgr']['urlparts']['domain_X'].(str_replace('//', '/', $GLOBALS['rdgr']['urlparts']['path_X'].$full_link));
}
}
}
//replace http(s) with sheme-less urls
if (!empty($GLOBALS['rdgr']['schemeless'])) {
$full_link = str_replace(['https://', 'http://'], '//', $full_link);
}
//replace github mime
if (!empty($GLOBALS['rdgr']['rawgit_replace'])) {
$full_link = str_replace('//raw.github'.'usercontent.com/', '//rawgit.com/', $full_link);
}
$matches_B[2] = $full_link;
unset($matches_B[0]);
$content_B = '';
foreach ($matches_B as $each) {
$content_B .= $each;
}
return $content_B;
},
$content_A
);
}
}
}
return $content_A;
},
$content
);
$content = preg_replace_callback(
'/style="(.*?)background(\-image|)(.*?|)\:(.*?|)url\((\'|\"|)(.*?)(\'|\"|)\)/i',
function ($matches_A) {
$url = $matches_A[7];
$url = (substr($url, 0, 2) == '//' || substr($url, 0, 7) == 'http://' || substr($url, 0, 8) == 'https://' ? $url : '#');
return 'style="'.$matches_A[1].'background'.$matches_A[2].$matches_A[3].':'.$matches_A[4].'url('.$url.')'; //$matches_A[5] is url taged ,7 is url
},
$content
);
}
return $content;
}
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

14
log.php
View File

@ -4,11 +4,7 @@ include_once __DIR__.'/core.php';
$pageTitle = tr('Log');
if (file_exists($docroot.'/include/custom/top.php')) {
include $docroot.'/include/custom/top.php';
} else {
include $docroot.'/include/top.php';
}
include_once App::filepath('include|custom|', 'top.php');
echo '
<div class="box">
@ -35,7 +31,7 @@ echo '
if (Auth::admin()) {
$q = 'SELECT * FROM `zz_logs` ORDER BY `created_at` DESC LIMIT 0, 100';
} else {
$q = 'SELECT * FROM `zz_logs` WHERE `id_utente`='.prepare($_SESSION['id_utente']).' ORDER BY `created_at` DESC LIMIT 0, 100';
$q = 'SELECT * FROM `zz_logs` WHERE `id_utente`='.prepare(Auth::user()['id']).' ORDER BY `created_at` DESC LIMIT 0, 100';
}
$rs = $dbo->fetchArray($q);
$n = sizeof($rs);
@ -81,8 +77,4 @@ echo '
</div>
<!-- /.box -->';
if (file_exists($docroot.'/include/custom/bottom.php')) {
include $docroot.'/include/custom/bottom.php';
} else {
include $docroot.'/include/bottom.php';
}
include_once App::filepath('include|custom|', 'bottom.php');

View File

@ -2,7 +2,7 @@
include_once __DIR__.'/core.php';
$template = Mail::getTemplate($get['id']);
$template = Mail::getTemplate(get('id'));
$module = Modules::get($id_module);
$smtp = Mail::get($template['id_smtp']);
@ -35,8 +35,13 @@ if (empty($smtp['port'])) {
}
if (sizeof($campi_mancanti) > 0) {
echo "<div class='alert alert-warning'><i class='fa fa-warning'></i> Prima di procedere all'invio completa: <b>".implode(', ', $campi_mancanti).'</b><br/>
'.Modules::link('Account email', $smtp['id'], tr('Vai alla scheda account email'), null).'</div>';
echo '
<div class="alert alert-warning">
<i class="fa fa-warning"></i> '.tr("Prima di procedere all'invio completa: _VALUES_", [
'_VALUES_' => '<b>'.implode(', ', $campi_mancanti).'</b>',
]).'<br/>
'.Modules::link('Account email', $smtp['id'], tr('Vai alla scheda account email'), null).'
</div>';
}
// Form
@ -49,14 +54,14 @@ echo '
<p><b>'.tr('Mittente').'</b>: '.$smtp['from_name'].' &lt;'.$smtp['from_address'].'&gt;</p>';
if (!empty($smtp['cc'])) {
if (!empty($template['cc'])) {
echo '
<p><b>'.tr('CC').'</b>: '.$smtp['cc'].'</p>';
<p><b>'.tr('CC').'</b>: '.$template['cc'].'</p>';
}
if (!empty($smtp['cc'])) {
if (!empty($template['bcc'])) {
echo '
<p><b>'.tr('CCN').'</b>: '.$smtp['bcc'].'</p>';
<p><b>'.tr('CCN').'</b>: '.$template['bcc'].'</p>';
}
echo '
@ -76,7 +81,7 @@ echo '
</div>
<div class="col-md-4">
{[ "type": "checkbox", "label": "'.tr('Notifica di lettura').'", "name": "read_notify", "value": "'.$template['read_notify'].'" ]}
{[ "type": "checkbox", "label": "'.tr('Richiedi notifica di lettura').'", "name": "read_notify", "value": "'.$template['read_notify'].'" ]}
</div>
</div>';
@ -88,14 +93,20 @@ echo '
<div class="row">
<div class="col-md-6">
{[ "type": "select", "multiple": "1", "label": "'.tr('Stampe').'", "name": "prints[]", "value": "'.implode(',', $selected).'", "values": "query=SELECT id, title AS text FROM zz_prints WHERE id_module = '.prepare($id_module).'" ]}
{[ "type": "select", "multiple": "1", "label": "'.tr('Stampe').'", "name": "prints[]", "value": "'.implode(',', $selected).'", "values": "query=SELECT id, title AS text FROM zz_prints WHERE id_module = '.prepare($id_module).' AND enabled=1" ]}
</div>';
$attachments = [];
if ($template['name'] == 'Fattura Elettronica') {
$attachments = $dbo->fetchArray('SELECT id FROM zz_files WHERE id_module = '.prepare($module['id']).' AND id_record = '.prepare($id_record).' AND category = \'Fattura elettronica\'');
$attachments = array_column($attachments, 'id');
}
// Allegati
echo '
<div class="col-md-6">
{[ "type": "select", "multiple": "1", "label": "'.tr('Allegati').'", "name": "attachments[]", "values": "query=SELECT id, nome AS text FROM zz_files WHERE id_module = '.prepare($id_module).' AND id_record = '.prepare($id_record)." UNION SELECT id, CONCAT(nome, ' (Azienda)') AS text FROM zz_files WHERE id_module = ".prepare(Modules::get('Anagrafiche')['id'])." AND id_record = (SELECT valore FROM zz_settings WHERE nome = 'Azienda predefinita')\" ]}
{[ "type": "select", "multiple": "1", "label": "'.tr('Allegati').'", "name": "attachments[]", "value": "'.implode(',', $attachments).'", "values": "query=SELECT id, name AS text FROM zz_files WHERE id_module = '.prepare($id_module).' AND id_record = '.prepare($id_record)." UNION SELECT id, CONCAT(name, ' (Azienda)') AS text FROM zz_files WHERE id_module = ".prepare(Modules::get('Anagrafiche')['id'])." AND id_record = (SELECT valore FROM zz_settings WHERE nome = 'Azienda predefinita')\" ]}
</div>
</div>";
@ -103,7 +114,7 @@ echo '
<div class="row">
<div class="col-md-12">
{[ "type": "textarea", "label": "'.tr('Contenuto').'", "name": "body", "value": '.json_encode($body).' ]}
{[ "type": "ckeditor", "label": "'.tr('Contenuto').'", "name": "body", "value": '.json_encode($body).' ]}
</div>
</div>';
@ -124,16 +135,16 @@ echo '
</div>
</div>';
echo '
<script src="'.$rootdir.'/assets/dist/js/ckeditor/ckeditor.js"></script>';
echo '
<script>
var emails = [];
$(document).ready(function(){
$(document).ready(function(){';
// Autocompletamento destinatario
$(document).load(globals.rootdir + "/ajax_complete.php?module=Anagrafiche&op=get_email&id_anagrafica='.$variables['id_anagrafica'].'", function(response) {
if (!empty($variables['id_anagrafica'])) {
echo '
$(document).load(globals.rootdir + "/ajax_complete.php?module=Anagrafiche&op=get_email&id_anagrafica='.$variables['id_anagrafica'].(($smtp['pec']) ? '&type=pec' : '').'", function(response) {
emails = JSON.parse(response);
$(".destinatari").each(function(){
@ -142,16 +153,15 @@ echo '
minLength: 0
}).focus(function() {
$(this).autocomplete("search", $(this).val())
});;
});
});
});
CKEDITOR.replace("body", {
toolbar: globals.ckeditorToolbar,
language: globals.locale,
scayt_autoStartup: true,
scayt_sLang: globals.full_locale
});
aggiungi_destinatario();
});';
}
echo '
});
function send(){

View File

@ -5,6 +5,20 @@ include_once __DIR__.'/../../core.php';
$id = post('id');
switch (post('op')) {
case 'check':
$api = json_decode(get_remote_data('https://api.github.com/repos/devcode-it/openstamanager/releases'), true);
$version = ltrim($api[0]['tag_name'], 'v');
$current = Update::getVersion();
if (version_compare($current, $version) < 0) {
echo $version;
} else {
echo 'none';
}
break;
case 'upload':
include $docroot.'/modules/aggiornamenti/upload_modules.php';
@ -14,7 +28,7 @@ switch (post('op')) {
if (!empty($id)) {
// Leggo l'id del modulo
$rs = $dbo->fetchArray('SELECT id, name, directory FROM zz_modules WHERE id='.prepare($id).' AND `default`=0');
$modulo = $rs[0]['name'];
$modulo = $rs[0]['title'];
$module_dir = $rs[0]['directory'];
if (count($rs) == 1) {
@ -29,47 +43,41 @@ switch (post('op')) {
delete($docroot.'/modules/'.$module_dir.'/');
$_SESSION['infos'][] = tr('Modulo _MODULE_ disinstallato!', [
flash()->info(tr('Modulo _MODULE_ disinstallato!', [
'_MODULE_' => '"'.$modulo.'"',
]);
]));
}
}
break;
case 'disable':
$dbo->query('UPDATE zz_modules SET enabled=0 WHERE id='.prepare($id));
$dbo->query('UPDATE `zz_modules` SET `enabled` = 0 WHERE (`id` = '.prepare($id).' OR `parent` = '.prepare($id).') AND `id` != '.prepare(Modules::get('Aggiornamenti')['id']));
$rs = $dbo->fetchArray('SELECT id, name FROM zz_modules WHERE id='.prepare($id));
$modulo = $rs[0]['name'];
$_SESSION['infos'][] = tr('Modulo _MODULE_ disabilitato!', [
'_MODULE_' => '"'.$modulo.'"',
]);
flash()->info(tr('Modulo _MODULE_ disabilitato!', [
'_MODULE_' => '"'.Modules::get($id)['title'].'"',
]));
break;
case 'enable':
$dbo->query('UPDATE zz_modules SET enabled=1 WHERE id='.prepare($id));
$dbo->query('UPDATE `zz_modules` SET `enabled` = 1 WHERE `id` = '.prepare($id).' OR `parent` = '.prepare($id));
$rs = $dbo->fetchArray('SELECT id, name FROM zz_modules WHERE id='.prepare($id));
$modulo = $rs[0]['name'];
$_SESSION['infos'][] = tr('Modulo _MODULE_ abilitato!', [
'_MODULE_' => '"'.$modulo.'"',
]);
flash()->info(tr('Modulo _MODULE_ abilitato!', [
'_MODULE_' => '"'.Modules::get($id)['title'].'"',
]));
break;
case 'disable_widget':
$dbo->query('UPDATE zz_widgets SET enabled=0 WHERE id='.prepare($id));
$dbo->query('UPDATE zz_widgets SET enabled = 0 WHERE id = '.prepare($id));
$rs = $dbo->fetchArray('SELECT id, name FROM zz_widgets WHERE id='.prepare($id));
$widget = $rs[0]['name'];
$_SESSION['infos'][] = tr('Widget _WIDGET_ disabilitato!', [
flash()->info(tr('Widget _WIDGET_ disabilitato!', [
'_WIDGET_' => '"'.$widget.'"',
]);
]));
break;
@ -79,9 +87,9 @@ switch (post('op')) {
$rs = $dbo->fetchArray('SELECT id, name FROM zz_widgets WHERE id='.prepare($id));
$widget = $rs[0]['name'];
$_SESSION['infos'][] = tr('Widget _WIDGET_ abilitato!', [
flash()->info(tr('Widget _WIDGET_ abilitato!', [
'_WIDGET_' => '"'.$widget.'"',
]);
]));
break;
@ -91,9 +99,9 @@ switch (post('op')) {
$rs = $dbo->fetchArray('SELECT id, name FROM zz_widgets WHERE id='.prepare($id));
$widget = $rs[0]['name'];
$_SESSION['infos'][] = tr('Posizione del widget _WIDGET_ aggiornata!', [
flash()->info(tr('Posizione del widget _WIDGET_ aggiornata!', [
'_WIDGET_' => '"'.$widget.'"',
]);
]));
break;
@ -103,9 +111,9 @@ switch (post('op')) {
$rs = $dbo->fetchArray('SELECT id, name FROM zz_widgets WHERE id='.prepare($id));
$widget = $rs[0]['name'];
$_SESSION['infos'][] = tr('Posizione del widget _WIDGET_ aggiornata!', [
flash()->info(tr('Posizione del widget _WIDGET_ aggiornata!', [
'_WIDGET_' => '"'.$widget.'"',
]);
]));
break;
@ -120,14 +128,12 @@ switch (post('op')) {
$dbo->query('UPDATE zz_modules SET `order`='.prepare($i).' WHERE id='.prepare($ids[$i]));
}
$_SESSION['infos'][] = tr('Posizione voci di menù aggiornate!');
flash()->info(tr('Posizione voci di menù aggiornate!'));
}
break;
case 'sortwidget':
$id_module = post('id_module');
$id_record = post('id_record');
$location = post('location');
$location = empty($id_record) ? 'controller_'.$location : 'editor_'.$location;
@ -142,13 +148,11 @@ switch (post('op')) {
$dbo->query('UPDATE zz_widgets SET `order`='.prepare($i).' WHERE id='.prepare($id[1]));
}
$_SESSION['infos'][] = tr('Posizioni widgets aggiornate!');
flash()->info(tr('Posizioni widgets aggiornate!'));
}
break;
case 'updatewidget':
$id_module = post('id_module');
$id_record = post('id_record');
$location = post('location');
$class = post('class');
$id = explode('_', post('id'));

View File

@ -0,0 +1,20 @@
<?php
switch ($resource) {
case 'allegato':
$module = Modules::get($request['module']);
$upload = Uploads::upload($_FILES['upload'], [
'name' => $request['name'],
'id_module' => $module['id'],
'id_record' => $request['id'],
]);
$response['filename'] = $upload;
break;
}
return [
'allegato',
];

View File

@ -4,14 +4,14 @@ switch ($resource) {
case 'updates':
$custom_where = !empty($updated) ? ' WHERE updated_at >= '.prepare($updated) : '';
$excluded = explode(',', Settings::get('Tabelle escluse per la sincronizzazione API automatica'));
$excluded = explode(',', setting('Tabelle escluse per la sincronizzazione API automatica'));
// Attenzione: query specifica per MySQL
$datas = $dbo->fetchArray("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_SCHEMA=".prepare($db_name));
if (!empty($datas)) {
foreach ($datas as $data) {
if (!in_array($data['TABLE_NAME'], $excluded)) {
$results[$data['TABLE_NAME']] = $dbo->fetchArray('SELECT * FROM '.$data['TABLE_NAME'].$custom_where);
$response[$data['TABLE_NAME']] = $dbo->fetchArray('SELECT * FROM '.$data['TABLE_NAME'].$custom_where);
}
}
}
@ -19,7 +19,7 @@ switch ($resource) {
// Attualmente vengono considerate solo le tabelle che eseguono l'eliminazione fisica della riga
case 'deleted':
$excluded = explode(',', Settings::get('Tabelle escluse per la sincronizzazione API automatica'));
$excluded = explode(',', setting('Tabelle escluse per la sincronizzazione API automatica'));
// Attenzione: query specifica per MySQL
$datas = $dbo->fetchArray("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_SCHEMA=".prepare($db_name));
@ -48,7 +48,7 @@ switch ($resource) {
}
}
$results[$table_name] = $total;
$response[$table_name] = $total;
}
}
}

View File

@ -2,7 +2,63 @@
include_once __DIR__.'/../../core.php';
if (get_var('Attiva aggiornamenti')) {
// Personalizzazioni di codice
if (function_exists(custom)) {
$custom = custom();
$tables = customTables();
if (!empty($custom) || !empty($tables)) {
echo '
<div class="box box-warning">
<div class="box-header with-border">
<h3 class="box-title"><span class="tip" title="'.tr('Elenco delle personalizzazioni rilevabili dal gestionale').'.">
<i class="fa fa-edit"></i> '.tr('Personalizzazioni').'
</span></h3>
</div>
<div class="box-body">';
if (!empty($custom)) {
echo '
<table class="table table-hover table-striped">
<tr>
<th width="10%">'.tr('Percorso').'</th>
<th width="15%">'.tr('Cartella personalizzata').'</th>
<th width="15%">'.tr('Database personalizzato').'</th>
</tr>';
foreach ($custom as $element) {
echo '
<tr>
<td>'.$element['path'].'</td>
<td>'.($element['directory'] ? 'Si' : 'No').'</td>
<td>'.($element['database'] ? 'Si' : 'No').'</td>
</tr>';
}
echo '
</table>
<p><strong>'.tr("Si sconsiglia l'aggiornamento senza il supporto dell'assistenza ufficiale").'.</strong></p>';
} else {
echo '
<p>'.tr('Non ci sono strutture personalizzate').'.</p>';
}
if (!empty($tables)) {
echo '
<div class="alert alert-warning">
<i class="fa fa-warning"></i>
<b>Attenzione!</b> Ci sono delle tabelle non previste nella versione standard del gestionale: '.implode(', ', $tables).'.
</div>';
}
echo '
</div>
</div>';
}
}
// Aggiornamenti
if (setting('Attiva aggiornamenti')) {
$alerts = [];
if (!extension_loaded('zip')) {
@ -39,51 +95,95 @@ if (get_var('Attiva aggiornamenti')) {
}
echo '
<div class="row">';
// Aggiornamento
<script>
function update() {
if ($("#blob").val()) {
swal({
title: "'.tr('Avviare la procedura?').'",
type: "warning",
showCancelButton: true,
confirmButtonText: "'.tr('Sì').'"
}).then(function (result) {
$("#update").submit();
})
} else {
swal({
title: "'.tr('Selezionare un file!').'",
type: "error",
})
}
}
function search(button) {
buttonLoading(button);
$.ajax({
url: globals.rootdir + "/actions.php",
type: "post",
data: {
id_module: globals.id_module,
op: "check",
},
success: function(data){
if (data == "none") {
$("#update-search").html("'.tr('Nessun aggiornamento disponibile').'.");
} else {
$("#update-search").html("'.tr("E' stato individuato un nuovo aggiornamento").': " + data + ".<br>'.tr('Scaricalo ora: _LINK_', [
'_LINK_' => "<a target='_blank' href='https://github.com/devcode-it/openstamanager/releases'>https://github.com/devcode-it/openstamanager/releases</a>",
]).'");
}
}
});
}
</script>';
echo '
<div class="col-md-6">
<div class="box box-success">
<div class="box-header with-border">
<h3 class="box-title">'.tr('Carica un aggiornamento').'</h3>
</div>
<div class="box-body">
<form action="'.$rootdir.'/controller.php?id_module='.$id_module.'" method="post" enctype="multipart/form-data" class="form-inline" id="update">
<input type="hidden" name="op" value="upload">
<input type="hidden" name="type" value="update">
<div class="row">
<div class="col-md-8">
<div class="box box-success">
<div class="box-header with-border">
<h3 class="box-title">
'.tr('Carica un aggiornamento').' <span class="tip" title="'.tr('Form di caricamento aggiornamenti del gestionale e innesti di moduli e plugin').'."><i class="fa fa-question-circle-o"></i></span>
</h3>
</div>
<div class="box-body">
<form action="'.ROOTDIR.'/controller.php?id_module='.$id_module.'" method="post" enctype="multipart/form-data" class="form-inline" id="update">
<input type="hidden" name="op" value="upload">
<label><input type="file" name="blob"></label>
<label><input type="file" name="blob" id="blob"></label>
<button type="button" class="btn btn-primary" onclick="if( confirm(\''.tr('Avviare la procedura?').'\') ){ $(\'#update\').submit(); }">
<i class="fa fa-upload"></i> '.tr('Carica').'...
</button>
</form>
</div>
</div>
</div>';
<button type="button" class="btn btn-primary pull-right" onclick="update()">
<i class="fa fa-upload"></i> '.tr('Carica').'
</button>
</form>
</div>
</div>
</div>';
// Nuovo modulo
echo '
<div class="col-md-6">
<div class="box box-info">
<div class="box-header with-border">
<h3 class="box-title">'.tr('Carica un nuovo modulo').'</h3>
</div>
<div class="box-body">
<form action="'.$rootdir.'/controller.php?id_module='.$id_module.'" method="post" enctype="multipart/form-data" class="form-inline" id="module">
<input type="hidden" name="op" value="upload">
<input type="hidden" name="type" value="new">
<label><input type="file" name="blob"></label>
<button type="button" class="btn btn-primary" onclick="if( confirm(\''.tr('Avviare la procedura?').'\') ){ $(\'#module\').submit(); }">
<i class="fa fa-upload"></i> '.tr('Carica').'...
</button>
</form>
</div>
</div>
</div>';
echo '
</div>';
<div class="col-md-4">
<div class="box box-info">
<div class="box-header with-border">
<h3 class="box-title">
'.tr('Ricerca aggiornamenti').' <span class="tip" title="'.tr('Controllo automatico della presenza di aggiornamenti per il gestionale').'."><i class="fa fa-question-circle-o"></i></span>
</h3>
</div>
<div class="box-body" id="update-search">';
if (extension_loaded('curl')) {
echo' <button type="button" class="btn btn-info btn-block" onclick="search(this)">
<i class="fa fa-search"></i> '.tr('Ricerca').'
</button>';
} else {
echo' <button type="button" class="btn btn-warning btn-block disabled" >
<i class="fa fa-warning"></i> '.tr('Estensione curl non supportata').'.
</button>';
}
echo' </div>
</div>
</div>
</div>';
}
// Elenco moduli installati
@ -100,138 +200,11 @@ echo '
<th width="20">'.tr('Opzioni').'</th>
</tr>';
$modules = $dbo->fetchArray('SELECT * FROM zz_modules WHERE parent IS NULL ORDER BY `order` ASC');
$modules = Modules::getHierarchy();
$osm_version = Update::getVersion();
foreach ($modules as $module) {
// STATO
if (!empty($module['enabled'])) {
$text = tr('Abilitato');
$text .= ($module['id'] != $id_module) ? '. '.tr('Clicca per disabilitarlo').'...' : '';
$stato = '<i class="fa fa-cog fa-spin text-success" data-toggle="tooltip" title="'.$text.'"></i>';
} else {
$stato = '<i class="fa fa-cog text-warning" data-toggle="tooltip" title="'.tr('Non abilitato').'"></i>';
$class = 'warning';
}
// Possibilità di disabilitare o abilitare i moduli tranne quello degli aggiornamenti
if ($module['id'] != $id_module) {
if ($module['enabled']) {
$stato = "<a href='javascript:;' onclick=\"if( confirm('".tr('Disabilitare questo modulo?')."') ){ $.post( '".$rootdir.'/actions.php?id_module='.$id_module."', { op: 'disable', id: '".$module['id']."' }, function(response){ location.href='".$rootdir.'/controller.php?id_module='.$id_module."'; }); }\">".$stato."</a>\n";
} else {
$stato = "<a href='javascript:;' onclick=\"if( confirm('".tr('Abilitare questo modulo?')."') ){ $.post( '".$rootdir.'/actions.php?id_module='.$id_module."', { op: 'enable', id: '".$module['id']."' }, function(response){ location.href='".$rootdir.'/controller.php?id_module='.$id_module."'; }); }\"\">".$stato."</a>\n";
}
}
// COMPATIBILITA'
$compatibilities = explode(',', $module['compatibility']);
// Controllo per ogni versione se la regexp combacia per dire che è compatibile o meno
$comp = false;
foreach ($compatibilities as $compatibility) {
$comp = (preg_match('/'.$compatibility.'/', $osm_version)) ? true : $comp;
}
if ($comp) {
$compatible = '<i class="fa fa-check-circle text-success" data-toggle="tooltip" title="'.tr('Compatibile').'"></i>';
($module['enabled']) ? $class = 'success': $class = 'warning';
} else {
$compatible = '<i class="fa fa-warning text-danger" data-toggle="tooltip" title="'.tr('Non compatibile!').tr('Questo modulo è compatibile solo con le versioni').': '.$module['compatibility'].'"></i>';
$class = 'danger';
}
echo '
<tr class="'.$class.'">
<td>'.$module['name'].'</td>
<td align="right">'.$module['version'].'</td>
<td align="center">'.$stato.'</td>
<td align="center">'.$compatible.'</td>';
echo '
<td>';
// Possibilità di disinstallare solo se il modulo non è tra quelli predefiniti
if (empty($module['default'])) {
echo "
<a href=\"javascript:;\" data-toggle='tooltip' title=\"".tr('Disinstalla')."...\" onclick=\"if( confirm('".tr('Vuoi disinstallare questo modulo?').' '.tr('Tutti i dati salvati andranno persi!')."') ){ if( confirm('".tr('Sei veramente sicuro?')."') ){ $.post( '".$rootdir.'/actions.php?id_module='.$id_module."', { op: 'uninstall', id: '".$module['id']."' }, function(response){ location.href='".$rootdir.'/controller.php?id_module='.$id_module."'; }); } }\"><i class='fa fa-trash'></i></a>";
} else {
echo "
<a class='disabled text-muted'>
<i class='fa fa-trash'></i>
</a>";
}
echo '
</td>
</tr>';
// Prima di cambiare modulo verifico se ci sono sottomoduli
$submodules = $dbo->fetchArray('SELECT * FROM zz_modules WHERE parent='.prepare($module['id']).' ORDER BY `order` ASC');
foreach ($submodules as $sub) {
// STATO
if (!empty($sub['enabled'])) {
$text = tr('Abilitato');
$text .= ($sub['id'] != $id_module) ? '. '.tr('Clicca per disabilitarlo').'...' : '';
$stato = '<i class="fa fa-cog fa-spin text-success" data-toggle="tooltip" title="'.$text.'"></i>';
} else {
$stato = '<i class="fa fa-cog text-warning" data-toggle="tooltip" title="'.tr('Non abilitato').'"></i>';
$class = 'warning';
}
// Possibilità di disabilitare o abilitare i moduli tranne quello degli aggiornamenti
if ($sub['id'] != $id_module) {
if ($sub['enabled']) {
$stato = "<a href='javascript:;' onclick=\"if( confirm('".tr('Disabilitare questo modulo?')."') ){ $.post( '".$rootdir.'/actions.php?id_module='.$id_module."', { op: 'disable', id: '".$sub['id']."' }, function(response){ location.href='".$rootdir.'/controller.php?id_module='.$id_module."'; }); }\">".$stato."</a>\n";
} else {
$stato = "<a href='javascript:;' onclick=\"if( confirm('".tr('Abilitare questo modulo?')."') ){ $.post( '".$rootdir.'/actions.php?id_module='.$id_module."', { op: 'enable', id: '".$sub['id']."' }, function(response){ location.href='".$rootdir.'/controller.php?id_module='.$id_module."'; }); }\"\">".$stato."</a>\n";
}
}
// COMPATIBILITA'
$compatibilities = explode(',', $sub['compatibility']);
// Controllo per ogni versione se la regexp combacia per dire che è compatibile o meno
$comp = false;
foreach ($compatibilities as $compatibility) {
$comp = (preg_match('/'.$compatibility.'/', $osm_version)) ? true : $comp;
}
if ($comp) {
$compatible = '<i class="fa fa-check-circle text-success" data-toggle="tooltip" title="'.tr('Compatibile').'"></i>';
($sub['enabled']) ? $class = 'success': $class = 'warning';
} else {
$compatible = '<i class="fa fa-warning text-danger" data-toggle="tooltip" title="'.tr('Non compatibile!').tr('Questo modulo è compatibile solo con le versioni').': '.$sub['compatibility'].'"></i>';
$class = 'danger';
}
echo '
<tr class="'.$class.'">
<td><small>&nbsp;&nbsp;- '.$sub['name'].'</small></td>
<td align="right">'.$sub['version'].'</td>
<td align="center">'.$stato.'</td>
<td align="center">'.$compatible.'</td>';
echo '
<td>';
// Possibilità di disinstallare solo se il modulo non è tra quelli predefiniti
if (empty($sub['default'])) {
echo "
<a href=\"javascript:;\" data-toggle='tooltip' title=\"".tr('Disinstalla')."...\" onclick=\"if( confirm('".tr('Vuoi disinstallare questo modulo?').' '.tr('Tutti i dati salvati andranno persi!')."') ){ if( confirm('".tr('Sei veramente sicuro?')."') ){ $.post( '".$rootdir.'/actions.php?id_module='.$id_module."', { op: 'uninstall', id: '".$sub['id']."' }, function(response){ location.href='".$rootdir.'/controller.php?id_module='.$id_module."'; }); } }\">
<i class='fa fa-trash'></i>
</a>";
} else {
echo "
<a class='disabled text-muted'>
<i class='fa fa-trash'></i>
</a>";
}
echo '
</td>
</tr>';
}
}
echo submodules($modules);
echo '
</table>
@ -273,9 +246,9 @@ foreach ($widgets as $widget) {
// Possibilità di disabilitare o abilitare i moduli tranne quello degli aggiornamenti
if ($widget['enabled']) {
$stato = "<a href='javascript:;' onclick=\"if( confirm('".tr('Disabilitare questo widget?')."') ){ $.post( '".$rootdir.'/actions.php?id_module='.$id_module."', { op: 'disable_widget', id: '".$widget['id']."' }, function(response){ location.href='".$rootdir.'/controller.php?id_module='.$id_module."'; }); }\">".$stato."</a>\n";
$stato = "<a href='javascript:;' onclick=\"if( confirm('".tr('Disabilitare questo widget?')."') ){ $.post( '".ROOTDIR.'/actions.php?id_module='.$id_module."', { op: 'disable_widget', id: '".$widget['id']."' }, function(response){ location.href='".ROOTDIR.'/controller.php?id_module='.$id_module."'; }); }\">".$stato."</a>\n";
} else {
$stato = "<a href='javascript:;' onclick=\"if( confirm('".tr('Abilitare questo widget?')."') ){ $.post( '".$rootdir.'/actions.php?id_module='.$id_module."', { op: 'enable_widget', id: '".$widget['id']."' }, function(response){ location.href='".$rootdir.'/controller.php?id_module='.$id_module."'; }); }\"\">".$stato."</a>\n";
$stato = "<a href='javascript:;' onclick=\"if( confirm('".tr('Abilitare questo widget?')."') ){ $.post( '".ROOTDIR.'/actions.php?id_module='.$id_module."', { op: 'enable_widget', id: '".$widget['id']."' }, function(response){ location.href='".ROOTDIR.'/controller.php?id_module='.$id_module."'; }); }\"\">".$stato."</a>\n";
}
// POSIZIONE
@ -287,10 +260,10 @@ foreach ($widgets as $widget) {
if ($widget['location'] == 'controller_right') {
$posizione = "<i class='fa fa-arrow-up text-warning' data-toggle='tooltip' title=\"".tr('Clicca per cambiare la posizione...')."\"></i>&nbsp;<i class='fa fa-arrow-right text-success' data-toggle='tooltip' title=\"\"></i>";
$posizione = "<a href='javascript:;' onclick=\"if( confirm('".tr('Cambiare la posizione di questo widget?')."') ){ $.post( '".$rootdir.'/actions.php?id_module='.$id_module."', { op: 'change_position_widget_top', id: '".$widget['id']."' }, function(response){ location.href='".$rootdir.'/controller.php?id_module='.$id_module."'; }); }\"\">".$posizione."</a>\n";
$posizione = "<a href='javascript:;' onclick=\"if( confirm('".tr('Cambiare la posizione di questo widget?')."') ){ $.post( '".ROOTDIR.'/actions.php?id_module='.$id_module."', { op: 'change_position_widget_top', id: '".$widget['id']."' }, function(response){ location.href='".ROOTDIR.'/controller.php?id_module='.$id_module."'; }); }\"\">".$posizione."</a>\n";
} elseif ($widget['location'] == 'controller_top') {
$posizione = "<i class='fa fa-arrow-up text-success' data-toggle='tooltip' title=\"\"></i>&nbsp;<i class='fa fa-arrow-right text-warning' data-toggle='tooltip' title=\"".tr('Clicca per cambiare la posizione...').'"></i></i>';
$posizione = "<a href='javascript:;' onclick=\"if( confirm('".tr('Cambiare la posizione di questo widget?')."') ){ $.post( '".$rootdir.'/actions.php?id_module='.$id_module."', { op: 'change_position_widget_right', id: '".$widget['id']."' }, function(response){ location.href='".$rootdir.'/controller.php?id_module='.$id_module."'; }); }\"\">".$posizione."</a>\n";
$posizione = "<a href='javascript:;' onclick=\"if( confirm('".tr('Cambiare la posizione di questo widget?')."') ){ $.post( '".ROOTDIR.'/actions.php?id_module='.$id_module."', { op: 'change_position_widget_right', id: '".$widget['id']."' }, function(response){ location.href='".ROOTDIR.'/controller.php?id_module='.$id_module."'; }); }\"\">".$posizione."</a>\n";
}
echo '
@ -308,3 +281,15 @@ echo '
</table>
</div>
</div>';
// Requisiti
echo '
<hr>
<div>
<h3>'.tr('Requisiti').'</h3>';
include DOCROOT.'/include/init/requirements.php';
echo '
</div>';

View File

@ -0,0 +1,178 @@
<?php
function submodules($list, $depth = 0)
{
$osm_version = Update::getVersion();
$id_module = Modules::getCurrent()['id'];
$result = '';
foreach ($list as $sub) {
// STATO
if (!empty($sub['enabled'])) {
$text = tr('Abilitato');
$text .= ($sub['id'] != $id_module) ? '. '.tr('Clicca per disabilitarlo').'...' : '';
$stato = '<i class="fa fa-cog fa-spin text-success" data-toggle="tooltip" title="'.$text.'"></i>';
} else {
$stato = '<i class="fa fa-cog text-warning" data-toggle="tooltip" title="'.tr('Non abilitato').'"></i>';
$class = 'warning';
}
// Possibilità di disabilitare o abilitare i moduli tranne quello degli aggiornamenti
if ($sub['id'] != $id_module) {
if ($sub['enabled']) {
$stato = "<a href='javascript:;' onclick=\"if( confirm('".tr('Disabilitare questo modulo?')."') ){ $.post( '".ROOTDIR.'/actions.php?id_module='.$id_module."', { op: 'disable', id: '".$sub['id']."' }, function(response){ location.href='".ROOTDIR.'/controller.php?id_module='.$id_module."'; }); }\">".$stato."</a>\n";
} else {
$stato = "<a href='javascript:;' onclick=\"if( confirm('".tr('Abilitare questo modulo?')."') ){ $.post( '".ROOTDIR.'/actions.php?id_module='.$id_module."', { op: 'enable', id: '".$sub['id']."' }, function(response){ location.href='".ROOTDIR.'/controller.php?id_module='.$id_module."'; }); }\"\">".$stato."</a>\n";
}
}
// COMPATIBILITA'
// Controllo per ogni versione se la regexp combacia per dire che è compatibile o meno
$compatibilities = explode(',', $sub['compatibility']);
$comp = false;
foreach ($compatibilities as $compatibility) {
$comp = (preg_match('/'.$compatibility.'/', $osm_version)) ? true : $comp;
}
if ($comp) {
$compatible = '<i class="fa fa-check-circle text-success" data-toggle="tooltip" title="'.tr('Compatibile').'"></i>';
($sub['enabled']) ? $class = 'success' : $class = 'warning';
} else {
$compatible = '<i class="fa fa-warning text-danger" data-toggle="tooltip" title="'.tr('Non compatibile!').tr('Questo modulo è compatibile solo con le versioni').': '.$sub['compatibility'].'"></i>';
$class = 'danger';
}
$result .= '
<tr class="'.$class.'">
<td><small>'.str_repeat('&nbsp;', $depth * 4).'- '.$sub['title'].'</small></td>
<td align="right">'.$sub['version'].'</td>
<td align="center">'.$stato.'</td>
<td align="center">'.$compatible.'</td>';
$result .= '
<td>';
// Possibilità di disinstallare solo se il modulo non è tra quelli predefiniti
if (empty($sub['default'])) {
$result .= "
<a href=\"javascript:;\" data-toggle='tooltip' title=\"".tr('Disinstalla')."...\" onclick=\"if( confirm('".tr('Vuoi disinstallare questo modulo?').' '.tr('Tutti i dati salvati andranno persi!')."') ){ if( confirm('".tr('Sei veramente sicuro?')."') ){ $.post( '".ROOTDIR.'/actions.php?id_module='.$id_module."', { op: 'uninstall', id: '".$sub['id']."' }, function(response){ location.href='".ROOTDIR.'/controller.php?id_module='.$id_module."'; }); } }\">
<i class='fa fa-trash'></i>
</a>";
} else {
$result .= "
<a class='disabled text-muted'>
<i class='fa fa-trash'></i>
</a>";
}
$result .= '
</td>
</tr>';
$result .= submodules($sub['all_children'], $depth + 1);
}
return $result;
}
/**
* Controlla se il database presenta alcune sezioni personalizzate.
*
* @return array
*/
function customStructure()
{
$results = [];
$dirs = [
'modules',
'templates',
'plugins',
];
// Controlli di personalizzazione fisica
foreach ($dirs as $dir) {
$files = glob(DOCROOT.'/'.$dir.'/*/custom/*.{php,html}', GLOB_BRACE);
foreach ($files as $file) {
$file = str_replace(DOCROOT.'/', '', $file);
$result = explode('/custom/', $file)[0];
if (!in_array($result, $results)) {
$results[] = $result;
}
}
}
return $results;
}
/**
* Controlla se il database presenta alcune sezioni personalizzate.
*
* @return array
*/
function customTables()
{
$tables = include DOCROOT.'/update/tables.php';
$names = [];
foreach ($tables as $table) {
$names[] = prepare($table);
}
$database = database();
$results = $database->fetchArray('SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '.prepare($database->getDatabaseName()).' AND TABLE_NAME NOT IN ('.implode(',', $names).") AND TABLE_NAME != 'updates'");
return array_column($results, 'TABLE_NAME');
}
/**
* Controlla se il database presenta alcune sezioni personalizzate.
*
* @return array
*/
function customDatabase()
{
$database = database();
$modules = $database->fetchArray("SELECT name, CONCAT('modules/', directory) AS directory FROM zz_modules WHERE options2 != ''");
$plugins = $database->fetchArray("SELECT name, CONCAT('plugins/', directory) AS directory FROM zz_plugins WHERE options2 != ''");
$results = array_merge($modules, $plugins);
return $results;
}
function custom()
{
$database_check = customDatabase();
$structure_check = customStructure();
$list = [];
foreach ($database_check as $element) {
$pos = array_search($element['directory'], $structure_check);
$list[] = [
'path' => $element['directory'],
'database' => true,
'directory' => $pos !== false,
];
if ($pos !== false) {
unset($structure_check[$pos]);
}
}
foreach ($structure_check as $element) {
$list[] = [
'path' => $element,
'database' => false,
'directory' => true,
];
}
return $list;
}

View File

@ -2,117 +2,95 @@
include_once __DIR__.'/../../core.php';
if (!get_var('Attiva aggiornamenti')) {
use Util\Zip;
if (!setting('Attiva aggiornamenti')) {
die(tr('Accesso negato'));
}
$tmp = $_FILES['blob']['tmp_name'];
$filename = $_FILES['blob']['name'];
$filetype = $_FILES['blob']['type'];
$size = $_FILES['blob']['size'];
$type = $_POST['type'];
if (!extension_loaded('zip')) {
$_SESSION['errors'][] = tr('Estensione zip non supportata!').'<br>'.tr('Verifica e attivala sul tuo file _FILE_', [
flash()->error(tr('Estensione zip non supportata!').'<br>'.tr('Verifica e attivala sul tuo file _FILE_', [
'_FILE_' => '<b>php.ini</b>',
]);
} elseif (!ends_with($filename, '.zip')) {
$_SESSION['errors'][] = tr('Il file non è un archivio zip!');
} elseif (!empty($tmp) && is_file($tmp)) {
$zip = new ZipArchive();
]));
if ($zip->open($tmp)) {
$tmp_dir = $docroot.'/tmp';
// Controllo sulla cartella
directory($tmp_dir);
$zip->extractTo($tmp_dir);
// AGGIORNAMENTO
if ('update' == $type) {
// Salvo i file di configurazione e versione attuale
$old_config = file_get_contents($docroot.'/config.inc.php');
// Aggiornamento del CORE
if (file_exists($tmp_dir.'/VERSION')) {
//rename($docroot.'/VERSION', $docroot.'/VERSION.old');
// Copia i file dalla cartella temporanea alla root
copyr($tmp_dir, $docroot);
// Scollego l'utente per eventuali aggiornamenti del db
Auth::logout();
}
// Aggiornamento di un MODULO
elseif (file_exists($tmp_dir.'/MODULE')) {
$module_info = parse_ini_file($tmp_dir.'/MODULE', true);
$module_name = $module_info['module_name'];
$module_dir = $module_info['module_directory'];
// Copio i file nella cartella "modules/<nomemodulo>/"
copyr($tmp_dir, $docroot.'/modules/'.$module_dir.'/');
// Rinomino il file di versione per forzare l'aggiornamento
//rename($docroot.'/VERSION_'.$module, $docroot.'/VERSION_'.$module.'.old');
// Scollego l'utente per eventuali aggiornamenti del db
Auth::logout();
} else {
$_SESSION['errors'][] = tr('File di aggiornamento non riconosciuto!');
}
// Ripristino il file di configurazione dell'utente
file_put_contents($docroot.'/config.inc.php', $old_config);
}
// NUOVO MODULO
elseif ('new' == $type) {
// Se non c'è il file MODULE non é un modulo
if (is_file($tmp_dir.'/MODULE')) {
// Leggo le info dal file di configurazione del modulo
$module_info = parse_ini_file($tmp_dir.'/MODULE', true);
$module_name = $module_info['module_name'];
$module_version = $module_info['module_version'];
$module_dir = $module_info['module_directory'];
// Copio i file nella cartella "modules/<nomemodulo>/"
copyr($tmp_dir, $docroot.'/modules/'.$module_dir.'/');
// Scollego l'utente per eventuali aggiornamenti del db
Auth::logout();
// Sposto i file della cartella "files/" nella root
$files_dir = $docroot.'/modules/'.$module_dir.'/files/';
if (is_dir($files_dir)) {
copyr($files_dir, $docroot.'/files');
delete($files_dir);
}
// Inserimento delle voci del modulo nel db per ogni sezione [sezione]
// Verifico che il modulo non esista già
$n = $dbo->fetchNum('SELECT name FROM zz_modules WHERE name='.prepare($module_name));
if (0 == $n) {
$module_info['module_parent'] = $dbo->fetchNum('SELECT name FROM zz_modules WHERE id='.prepare($module_info['module_parent'])) ? prepare($module_info['module_parent']) : 'NULL';
$query = 'INSERT INTO zz_modules(`name`, `title`, `directory`, `options`, `icon`, `version`, `compatibility`, `order`, `parent`, `default`, `enabled`) VALUES('.prepare($module_name).', '.prepare($module_name).', '.prepare($module_dir).', '.prepare($module_info['module_options']).', '.prepare($module_info['module_icon']).', '.prepare($module_version).', '.prepare($module_info['module_compatibility']).', "100", '.$module_info['module_parent'].', 0, 1)';
$dbo->query($query);
}
}
// File zip non contiene il file MODULE
else {
$_SESSION['errors'][] = tr('File di installazione non valido!');
}
}
delete($tmp_dir);
redirect($rootdir);
} else {
$_SESSION['errors'][] = checkZip($tmp);
}
$zip->close();
return;
}
$extraction_dir = Zip::extract($_FILES['blob']['tmp_name']);
// Aggiornamento del progetto
if (file_exists($extraction_dir.'/VERSION')) {
// Salva il file di configurazione
$config = file_get_contents($docroot.'/config.inc.php');
// Copia i file dalla cartella temporanea alla root
copyr($extraction_dir, $docroot);
// Ripristina il file di configurazione dell'installazione
file_put_contents($docroot.'/config.inc.php', $config);
} else {
$finder = Symfony\Component\Finder\Finder::create()
->files()
->ignoreDotFiles(true)
->ignoreVCS(true)
->in($extraction_dir);
$files = $finder->name('MODULE')->name('PLUGIN');
foreach ($files as $file) {
// Informazioni dal file di configurazione
$info = Util\Ini::readFile($file->getRealPath());
// Informazioni aggiuntive per il database
$insert = [];
// Modulo
if (basename($file->getRealPath()) == 'MODULE') {
$directory = 'modules';
$table = 'zz_modules';
$installed = Modules::get($info['name']);
$insert['parent'] = Modules::get($info['parent']);
$insert['icon'] = $info['icon'];
}
// Plugin
elseif (basename($file->getRealPath()) == 'PLUGIN') {
$directory = 'plugins';
$table = 'zz_plugins';
$installed = Plugins::get($info['name']);
$insert['idmodule_from'] = Modules::get($info['module_from'])['id'];
$insert['idmodule_to'] = Modules::get($info['module_to'])['id'];
$insert['position'] = $info['position'];
}
// Copia dei file nella cartella relativa
copyr(dirname($file->getRealPath()), $docroot.'/'.$directory.'/'.$info['directory']);
// Eventuale registrazione nel database
if (empty($installed)) {
$dbo->insert($table, array_merge($insert, [
'name' => $info['name'],
'title' => !empty($info['title']) ? $info['title'] : $info['name'],
'directory' => $info['directory'],
'options' => $info['options'],
'version' => $info['version'],
'compatibility' => $info['compatibility'],
'order' => 100,
'default' => 0,
'enabled' => 1,
]));
flash()->error(tr('Installazione completata!'));
} else {
flash()->error(tr('Aggiornamento completato!'));
}
}
}
// Rimozione delle risorse inutilizzate
delete($extraction_dir);
// Redirect
redirect(ROOTDIR.'/editor.php?id_module='.$id_module);

View File

@ -2,147 +2,124 @@
include_once __DIR__.'/../../core.php';
$id_azienda = $dbo->fetchArray("SELECT idtipoanagrafica FROM an_tipianagrafiche WHERE descrizione='Azienda'")[0]['idtipoanagrafica'];
$id_cliente = $dbo->fetchArray("SELECT idtipoanagrafica FROM an_tipianagrafiche WHERE descrizione='Cliente'")[0]['idtipoanagrafica'];
$id_fornitore = $dbo->fetchArray("SELECT idtipoanagrafica FROM an_tipianagrafiche WHERE descrizione='Fornitore'")[0]['idtipoanagrafica'];
$id_tecnico = $dbo->fetchArray("SELECT idtipoanagrafica FROM an_tipianagrafiche WHERE descrizione='Tecnico'")[0]['idtipoanagrafica'];
use Modules\Anagrafiche\Anagrafica;
switch (post('op')) {
case 'update':
$post['piva'] = trim(strtoupper($post['piva']));
$post['codice_fiscale'] = trim(strtoupper($post['codice_fiscale']));
// Informazioni sulla sede
$sede = $anagrafica->sedeLegale;
$sede->indirizzo = post('indirizzo');
$sede->indirizzo2 = post('indirizzo2');
$sede->citta = post('citta');
$sede->cap = post('cap');
$sede->provincia = post('provincia');
$sede->km = post('km');
$sede->id_nazione = post('id_nazione') ?: null;
$sede->gaddress = post('gaddress');
$sede->lat = post('lat');
$sede->lng = post('lng');
$sede->telefono = post('telefono');
$sede->cellulare = post('cellulare');
$sede->fax = post('fax');
$sede->idzona = post('idzona');
$sede->email = post('email');
// Leggo tutti i valori passati dal POST e li salvo in un array
$dbo->update('an_anagrafiche', [
'ragione_sociale' => $post['ragione_sociale'],
'tipo' => $post['tipo'],
'piva' => $post['piva'],
'codice_fiscale' => $post['codice_fiscale'],
'data_nascita' => $post['data_nascita'],
'luogo_nascita' => $post['luogo_nascita'],
'sesso' => $post['sesso'],
'capitale_sociale' => $post['capitale_sociale'],
'indirizzo' => $post['indirizzo'],
'indirizzo2' => $post['indirizzo2'],
'citta' => $post['citta'],
'cap' => $post['cap'],
'provincia' => $post['provincia'],
'km' => $post['km'],
'id_nazione' => !empty($post['id_nazione']) ? $post['id_nazione'] : null,
'telefono' => $post['telefono'],
'cellulare' => $post['cellulare'],
'fax' => $post['fax'],
'email' => $post['email'],
'pec' => $post['pec'],
'idsede_fatturazione' => $post['idsede_fatturazione'],
'note' => $post['note'],
'codiceri' => $post['codiceri'],
'codicerea' => $post['codicerea'],
'appoggiobancario' => $post['appoggiobancario'],
'filiale' => $post['filiale'],
'codiceiban' => $post['codiceiban'],
'bic' => $post['bic'],
'diciturafissafattura' => $post['diciturafissafattura'],
'idpagamento_acquisti' => $post['idpagamento_acquisti'],
'idpagamento_vendite' => $post['idpagamento_vendite'],
'idlistino_acquisti' => $post['idlistino_acquisti'],
'idlistino_vendite' => $post['idlistino_vendite'],
'idiva_acquisti' => $post['idiva_acquisti'],
'idiva_vendite' => $post['idiva_vendite'],
'idbanca_acquisti' => $post['idbanca_acquisti'],
'idbanca_vendite' => $post['idbanca_vendite'],
'settore' => $post['settore'],
'marche' => $post['marche'],
'dipendenti' => $post['dipendenti'],
'macchine' => $post['macchine'],
'idagente' => $post['idagente'],
'idrelazione' => $post['idrelazione'],
'sitoweb' => $post['sitoweb'],
'idzona' => $post['idzona'],
'nome_cognome' => $post['nome_cognome'],
'iscrizione_tribunale' => $post['iscrizione_tribunale'],
'cciaa' => $post['cciaa'],
'cciaa_citta' => $post['cciaa_citta'],
'n_alboartigiani' => $post['n_alboartigiani'],
'foro_competenza' => $post['foro_competenza'],
'colore' => $post['colore'],
'idtipointervento_default' => $post['idtipointervento_default'],
'gaddress' => $post['gaddress'],
'lat' => $post['lat'],
'lng' => $post['lng'],
], ['idanagrafica' => $id_record]);
$sede->save();
if (!empty(post('nome')) and !empty(post('cognome')) ){
$ragione_sociale = post('nome').' '.post('cognome');
}else{
$ragione_sociale = post('ragione_sociale');
}
// Informazioni sull'anagrafica
$anagrafica->codice = post('codice');
$anagrafica->tipo = post('tipo');
$anagrafica->codice_destinatario = post('codice_destinatario');
$anagrafica->ragione_sociale = $ragione_sociale;
$anagrafica->partita_iva = post('piva');
$anagrafica->codice_fiscale = post('codice_fiscale');
$anagrafica->tipo = post('tipo');
$anagrafica->data_nascita = post('data_nascita');
$anagrafica->luogo_nascita = post('luogo_nascita');
$anagrafica->sesso = post('sesso');
$anagrafica->capitale_sociale = post('capitale_sociale');
$anagrafica->pec = post('pec');
$anagrafica->idsede_fatturazione = post('idsede_fatturazione');
$anagrafica->note = post('note');
$anagrafica->codiceri = post('codiceri');
$anagrafica->codicerea = post('codicerea');
$anagrafica->appoggiobancario = post('appoggiobancario');
$anagrafica->filiale = post('filiale');
$anagrafica->codiceiban = post('codiceiban');
$anagrafica->bic = post('bic');
$anagrafica->diciturafissafattura = post('diciturafissafattura');
$anagrafica->idpagamento_acquisti = post('idpagamento_acquisti');
$anagrafica->idpagamento_vendite = post('idpagamento_vendite');
$anagrafica->idlistino_acquisti = post('idlistino_acquisti');
$anagrafica->idlistino_vendite = post('idlistino_vendite');
$anagrafica->idiva_acquisti = post('idiva_acquisti');
$anagrafica->idiva_vendite = post('idiva_vendite');
$anagrafica->idbanca_acquisti = post('idbanca_acquisti');
$anagrafica->idbanca_vendite = post('idbanca_vendite');
$anagrafica->settore = post('settore');
$anagrafica->marche = post('marche');
$anagrafica->dipendenti = post('dipendenti');
$anagrafica->macchine = post('macchine');
$anagrafica->idagente = post('idagente');
$anagrafica->idrelazione = post('idrelazione');
$anagrafica->sitoweb = post('sitoweb');
$anagrafica->nome = post('nome');
$anagrafica->cognome = post('cognome');
$anagrafica->iscrizione_tribunale = post('iscrizione_tribunale');
$anagrafica->cciaa = post('cciaa');
$anagrafica->cciaa_citta = post('cciaa_citta');
$anagrafica->n_alboartigiani = post('n_alboartigiani');
$anagrafica->foro_competenza = post('foro_competenza');
$anagrafica->colore = post('colore');
$anagrafica->idtipointervento_default = post('idtipointervento_default');
$anagrafica->id_ritenuta_acconto_acquisti = post('id_ritenuta_acconto_acquisti');
$anagrafica->id_ritenuta_acconto_vendite = post('id_ritenuta_acconto_vendite');
$anagrafica->split_payment = post('split_payment');
$_SESSION['infos'][] = str_replace('_NAME_', '"'.$post['ragione_sociale'].'"', "Informazioni per l'anagrafica _NAME_ salvate correttamente!");
$anagrafica->tipologie = (array) post('idtipoanagrafica');
$anagrafica->save();
flash()->info(str_replace('_NAME_', '"'.post('ragione_sociale').'"', "Informazioni per l'anagrafica _NAME_ salvate correttamente!"));
// Validazione della Partita IVA
$check_vat_number = Validate::isValidVatNumber(strtoupper($post['piva']));
// Se $check_vat_number non è null e la riposta è negativa --> mostro il messaggio di avviso.
if ((!is_null($check_vat_number)) && (!$check_vat_number->valid)) {
if (!empty($check_vat_number->error->info)) {
$_SESSION['errors'][] = $check_vat_number->error->info;
} else {
$_SESSION['errors'][] = tr('Attenzione: la partita IVA _IVA_ sembra non essere valida', [
'_IVA_' => $post['piva'],
]);
$partita_iva = $anagrafica->partita_iva;
$partita_iva = strlen($partita_iva) == 11 ? $anagrafica->nazione->iso2.$partita_iva : $partita_iva;
$check_vat_number = Validate::isValidVatNumber($partita_iva);
if (empty($check_vat_number)) {
flash()->warning(tr('Attenzione: la partita IVA _IVA_ sembra non essere valida', [
'_IVA_' => $partita_iva,
]));
}
// Validazione del Codice Fiscale, solo per anagrafiche Private e Aziende, ignoro controllo se codice fiscale e settato uguale alla p.iva
$codice_fiscale = $anagrafica->codice_fiscale;
if ($anagrafica->tipo != 'Ente pubblico' and $codice_fiscale != $partita_iva) {
$check_codice_fiscale = Validate::isValidTaxCode($codice_fiscale);
if (empty($check_codice_fiscale)) {
flash()->warning(tr('Attenzione: il codice fiscale _COD_ sembra non essere valido', [
'_COD_' => $codice_fiscale,
]));
}
}
// Aggiorno il codice anagrafica se non è già presente, altrimenti lo ignoro
$esiste = $dbo->fetchNum('SELECT idanagrafica FROM an_anagrafiche WHERE codice='.prepare($post['codice']).' AND NOT idanagrafica='.prepare($id_record));
// Verifica dell'esistenza codice anagrafica
if ($esiste) {
$_SESSION['errors'][] = tr("Il codice anagrafica inserito esiste già! Inserirne un'altro...");
} else {
$dbo->query('UPDATE an_anagrafiche SET codice='.prepare($post['codice']).' WHERE idanagrafica='.prepare($id_record));
if ($anagrafica->codice != post('codice')) {
flash()->error(tr("Il codice anagrafica inserito esiste già! Inserirne un'altro..."));
}
// Aggiorno gli agenti collegati
$dbo->sync('an_anagrafiche_agenti', ['idanagrafica' => $id_record], ['idagente' => (array) $post['idagenti']]);
$dbo->sync('an_anagrafiche_agenti', ['idanagrafica' => $id_record], ['idagente' => (array) post('idagenti')]);
// Se l'agente di default è stato elencato anche tra gli agenti secondari lo rimuovo
if (!empty($post['idagente'])) {
$dbo->query('DELETE FROM an_anagrafiche_agenti WHERE idanagrafica='.prepare($id_record).' AND idagente='.prepare($post['idagente']));
}
// Aggiorno le tipologie di anagrafica
$post['idtipoanagrafica'] = (array) $post['idtipoanagrafica'];
if (str_contains($records[0]['idtipianagrafica'], $id_azienda)) {
$post['idtipoanagrafica'][] = $id_azienda;
}
$dbo->sync('an_tipianagrafiche_anagrafiche', ['idanagrafica' => $id_record], ['idtipoanagrafica' => $post['idtipoanagrafica']]);
// Verifico se esiste già l'associazione dell'anagrafica a conti del partitario
$rs = $dbo->fetchArray('SELECT idconto_cliente, idconto_fornitore FROM an_anagrafiche WHERE idanagrafica='.prepare($id_record));
$idconto_cliente = $rs[0]['idconto_cliente'];
$idconto_fornitore = $rs[0]['idconto_fornitore'];
// Creo il relativo conto nel partitario se non esiste
if (empty($idconto_cliente) && in_array($id_cliente, $post['idtipoanagrafica'])) {
// Calcolo prossimo numero cliente
$rs = $dbo->fetchArray("SELECT MAX(CAST(co_pianodeiconti3.numero AS UNSIGNED)) AS max_numero FROM co_pianodeiconti3 INNER JOIN co_pianodeiconti2 ON co_pianodeiconti3.idpianodeiconti2=co_pianodeiconti2.id WHERE co_pianodeiconti2.descrizione='Crediti clienti e crediti diversi'");
$new_numero = $rs[0]['max_numero'] + 1;
$new_numero = str_pad($new_numero, 6, '0', STR_PAD_LEFT);
$dbo->query('INSERT INTO co_pianodeiconti3(numero, descrizione, idpianodeiconti2, can_delete, can_edit) VALUES('.prepare($new_numero).', '.prepare($post['ragione_sociale']).", (SELECT id FROM co_pianodeiconti2 WHERE descrizione='Crediti clienti e crediti diversi'), 1, 1)");
$idconto = $dbo->lastInsertedID();
// Collegamento conto
$dbo->query('UPDATE an_anagrafiche SET idconto_cliente='.prepare($idconto).' WHERE idanagrafica='.prepare($id_record));
}
if (empty($idconto_fornitore) && in_array($id_fornitore, $post['idtipoanagrafica'])) {
// Calcolo prossimo numero cliente
$rs = $dbo->fetchArray("SELECT MAX(CAST(co_pianodeiconti3.numero AS UNSIGNED)) AS max_numero FROM co_pianodeiconti3 INNER JOIN co_pianodeiconti2 ON co_pianodeiconti3.idpianodeiconti2=co_pianodeiconti2.id WHERE co_pianodeiconti2.descrizione='Debiti fornitori e debiti diversi'");
$new_numero = $rs[0]['max_numero'] + 1;
$new_numero = str_pad($new_numero, 6, '0', STR_PAD_LEFT);
$dbo->query('INSERT INTO co_pianodeiconti3(numero, descrizione, idpianodeiconti2, can_delete, can_edit) VALUES('.prepare($new_numero).', '.prepare($post['ragione_sociale']).", (SELECT id FROM co_pianodeiconti2 WHERE descrizione='Debiti fornitori e debiti diversi'), 1, 1)");
$idconto = $dbo->lastInsertedID();
// Collegamento conto
$dbo->query('UPDATE an_anagrafiche SET idconto_fornitore='.prepare($idconto).' WHERE idanagrafica='.prepare($id_record));
if (!empty(post('idagente'))) {
$dbo->query('DELETE FROM an_anagrafiche_agenti WHERE idanagrafica='.prepare($id_record).' AND idagente='.prepare(post('idagente')));
}
break;
@ -150,11 +127,9 @@ switch (post('op')) {
case 'add':
$idtipoanagrafica = post('idtipoanagrafica');
$ragione_sociale = post('ragione_sociale');
// Inserimento anagrafica base
// Leggo l'ultimo codice anagrafica per calcolare il successivo
$rs = $dbo->fetchArray('SELECT codice FROM an_anagrafiche ORDER BY CAST(codice AS SIGNED) DESC LIMIT 0, 1');
$codice = Util\Generator::generate(get_var('Formato codice anagrafica'), $rs[0]['codice']);
$anagrafica = Anagrafica::build($ragione_sociale, $idtipoanagrafica);
$id_record = $anagrafica->id;
// Se ad aggiungere un cliente è un agente, lo imposto come agente di quel cliente
// Lettura tipologia dell'utente loggato
@ -169,104 +144,59 @@ switch (post('op')) {
}
}
$idagente = ($agente_is_logged && in_array($id_cliente, $post['idtipoanagrafica'])) ? $user['idanagrafica'] : 0;
$idagente = ($agente_is_logged && in_array($id_cliente, $idtipoanagrafica)) ? $user['idanagrafica'] : 0;
$anagrafica->nome = post('nome');
$anagrafica->cognome = post('cognome');
$anagrafica->partita_iva = post('piva');
$anagrafica->codice_fiscale = post('codice_fiscale');
$anagrafica->indirizzo = post('indirizzo');
$anagrafica->citta = post('citta');
$anagrafica->cap = post('cap');
$anagrafica->provincia = post('provincia');
$anagrafica->telefono = post('telefono');
$anagrafica->cellulare = post('cellulare');
$anagrafica->email = post('email');
$anagrafica->idrelazione = post('idrelazione');
$anagrafica->idagente = $idagente;
// Inserisco l'anagrafica
$dbo->insert('an_anagrafiche', [
'ragione_sociale' => $ragione_sociale,
'codice' => $codice,
'piva' => post('piva'),
'codice_fiscale' => post('codice_fiscale'),
'indirizzo' => post('indirizzo'),
'citta' => post('citta'),
'cap' => post('cap'),
'provincia' => post('provincia'),
'telefono' => post('telefono'),
'cellulare' => post('cellulare'),
'email' => post('email'),
'idrelazione' => post('idrelazione'),
'idagente' => $idagente,
]);
$anagrafica->save();
$new_id = $dbo->lastInsertedID();
// Inserisco il rapporto dell'anagrafica (cliente, tecnico, ecc)
$dbo->sync('an_tipianagrafiche_anagrafiche', ['idanagrafica' => $new_id], ['idtipoanagrafica' => (array) $idtipoanagrafica]);
if (in_array($id_azienda, $post['idtipoanagrafica'])) {
$dbo->query('UPDATE zz_settings SET valore='.prepare($new_id)." WHERE nome='Azienda predefinita'");
$_SESSION['infos'][] = tr('Anagrafica Azienda impostata come predefinita. Per ulteriori informazionioni, visitare "Strumenti -> Impostazioni -> Generali".');
if ($anagrafica->isAzienda()) {
flash()->info(tr('Anagrafica Azienda impostata come predefinita').'. '.tr('Per ulteriori informazionioni, visitare "Strumenti -> Impostazioni -> Generali"'));
}
//se sto inserendo un tecnico, mi copio già le tariffe per le varie attività
if (in_array($id_tecnico, $post['idtipoanagrafica'])) {
//per ogni tipo di attività
$rs_tipiintervento = $dbo->fetchArray('SELECT * FROM in_tipiintervento');
for ($i = 0; $i < count($rs_tipiintervento); ++$i) {
if ($dbo->query('INSERT INTO in_tariffe( idtecnico, idtipointervento, costo_ore, costo_km, costo_dirittochiamata, costo_ore_tecnico, costo_km_tecnico, costo_dirittochiamata_tecnico ) VALUES( '.prepare($new_id).', '.prepare($rs_tipiintervento[$i]['idtipointervento']).', (SELECT costo_orario FROM in_tipiintervento WHERE idtipointervento='.prepare($rs_tipiintervento[$i]['idtipointervento']).'), (SELECT costo_km FROM in_tipiintervento WHERE idtipointervento='.prepare($rs_tipiintervento[$i]['idtipointervento']).'), (SELECT costo_diritto_chiamata FROM in_tipiintervento WHERE idtipointervento='.prepare($rs_tipiintervento[$i]['idtipointervento']).'), (SELECT costo_orario_tecnico FROM in_tipiintervento WHERE idtipointervento='.prepare($rs_tipiintervento[$i]['idtipointervento']).'), (SELECT costo_km_tecnico FROM in_tipiintervento WHERE idtipointervento='.prepare($rs_tipiintervento[$i]['idtipointervento']).'), (SELECT costo_diritto_chiamata_tecnico FROM in_tipiintervento WHERE idtipointervento='.prepare($rs_tipiintervento[$i]['idtipointervento']).') )')) {
//$_SESSION['infos'][] = tr('Informazioni salvate correttamente!');
} else {
$_SESSION['errors'][] = tr("Errore durante l'importazione tariffe!");
}
}
}
// Creo il relativo conto nel partitario (cliente)
if (in_array($id_cliente, $post['idtipoanagrafica'])) {
// Calcolo prossimo numero cliente
$rs = $dbo->fetchArray("SELECT MAX(CAST(co_pianodeiconti3.numero AS UNSIGNED)) AS max_numero FROM co_pianodeiconti3 INNER JOIN co_pianodeiconti2 ON co_pianodeiconti3.idpianodeiconti2=co_pianodeiconti2.id WHERE co_pianodeiconti2.descrizione='Crediti clienti e crediti diversi'");
$new_numero = $rs[0]['max_numero'] + 1;
$new_numero = str_pad($new_numero, 6, '0', STR_PAD_LEFT);
// Creazione conto
$dbo->query('INSERT INTO co_pianodeiconti3(numero, descrizione, idpianodeiconti2, can_delete, can_edit) VALUES('.prepare($new_numero).', '.prepare($ragione_sociale).", (SELECT id FROM co_pianodeiconti2 WHERE descrizione='Crediti clienti e crediti diversi'), 1, 1)");
$idconto = $dbo->lastInsertedID();
// Collegamento conto
$dbo->query('UPDATE an_anagrafiche SET idconto_cliente='.prepare($idconto).' WHERE idanagrafica='.prepare($new_id));
}
// Creo il relativo conto nel partitario (fornitore)
if (in_array($id_fornitore, $post['idtipoanagrafica'])) {
// Calcolo prossimo numero cliente
$rs = $dbo->fetchArray("SELECT MAX(CAST(co_pianodeiconti3.numero AS UNSIGNED)) AS max_numero FROM co_pianodeiconti3 INNER JOIN co_pianodeiconti2 ON co_pianodeiconti3.idpianodeiconti2=co_pianodeiconti2.id WHERE co_pianodeiconti2.descrizione='Debiti fornitori e debiti diversi'");
$new_numero = $rs[0]['max_numero'] + 1;
$new_numero = str_pad($new_numero, 6, '0', STR_PAD_LEFT);
// Creazione conto
$dbo->query('INSERT INTO co_pianodeiconti3(numero, descrizione, idpianodeiconti2, can_delete, can_edit) VALUES('.prepare($new_numero).', '.prepare($ragione_sociale).", (SELECT id FROM co_pianodeiconti2 WHERE descrizione='Debiti fornitori e debiti diversi'), 1, 1)");
$idconto = $dbo->lastInsertedID();
// Collegamento conto
$dbo->query('UPDATE an_anagrafiche SET idconto_fornitore='.prepare($idconto).' WHERE idanagrafica='.prepare($new_id));
}
$id_record = $new_id;
// Lettura tipologia della nuova anagrafica
if (!empty($idtipoanagrafica)) {
$rs = $dbo->fetchArray('SELECT descrizione FROM an_tipianagrafiche WHERE idtipoanagrafica IN ('.implode(',', $idtipoanagrafica).')');
$tipoanagrafica_dst = implode(', ', array_column($rs, 'descrizione'));
}
if (isAjaxRequest() && str_contains($tipoanagrafica_dst, post('tipoanagrafica'))) {
$descrizioni_tipi = $anagrafica->tipi()->get()->pluck('descrizione')->toArray();
if (isAjaxRequest() && in_array(post('tipoanagrafica'), $descrizioni_tipi)) {
echo json_encode(['id' => $id_record, 'text' => $ragione_sociale]);
}
$_SESSION['infos'][] = tr('Aggiunta nuova anagrafica di tipo _TYPE_', [
'_TYPE_' => '"'.$tipoanagrafica_dst.'"',
]);
flash()->info(tr('Aggiunta nuova anagrafica di tipo _TYPE_', [
'_TYPE_' => '"'.implode(', ', $descrizioni_tipi).'"',
]));
break;
case 'delete':
// Se l'anagrafica non è l'azienda principale, la disattivo
if (!str_contains($records[0]['idtipianagrafica'], $id_azienda)) {
$dbo->query('UPDATE an_anagrafiche SET deleted = 1 WHERE idanagrafica = '.prepare($id_record).Modules::getAdditionalsQuery($id_module));
if (!in_array($id_azienda, $tipi_anagrafica)) {
$dbo->query('UPDATE an_anagrafiche SET deleted_at = NOW() WHERE idanagrafica = '.prepare($id_record).Modules::getAdditionalsQuery($id_module));
$_SESSION['infos'][] = tr('Anagrafica eliminata!');
// Se l'anagrafica è collegata ad un utente lo disabilito
$dbo->query('UPDATE zz_users SET enabled = 0 WHERE idanagrafica = '.prepare($id_record).Modules::getAdditionalsQuery($id_module));
flash()->info(tr('Anagrafica eliminata!'));
}
break;
}
// Operazioni aggiuntive per il logo
if (filter('op') == 'link_file') {
$nome = 'Logo stampe';
if (setting('Azienda predefinita') == $id_record && filter('nome_allegato') == $nome) {
Settings::setValue($nome, $upload);
}
}

View File

@ -5,8 +5,6 @@ include_once __DIR__.'/../../core.php';
if (get('tipoanagrafica') != '') {
$rs = $dbo->fetchArray('SELECT idtipoanagrafica FROM an_tipianagrafiche WHERE descrizione='.prepare(get('tipoanagrafica')));
$idtipoanagrafica = $rs[0]['idtipoanagrafica'];
} else {
$idtipoanagrafica = '';
}
echo '
@ -16,18 +14,27 @@ echo '
<div class="row">
<div class="col-md-6">
{[ "type": "text", "label": "'.tr('Ragione sociale').'", "name": "ragione_sociale", "required": 1 ]}
{[ "type": "text", "label": "'.tr('Denominazione').'", "name": "ragione_sociale", "required": 1 ]}
</div>
<div class="col-md-6">
{[ "type": "select", "label": "'.tr('Tipo di anagrafica').'", "name": "idtipoanagrafica[]", "multiple": "1", "required": 1, "values": "query=SELECT idtipoanagrafica AS id, descrizione FROM an_tipianagrafiche WHERE idtipoanagrafica NOT IN (SELECT DISTINCT(x.idtipoanagrafica) FROM an_tipianagrafiche_anagrafiche x INNER JOIN an_tipianagrafiche t ON x.idtipoanagrafica = t.idtipoanagrafica INNER JOIN an_anagrafiche ON an_anagrafiche.idanagrafica = x.idanagrafica WHERE t.descrizione = \'Azienda\' AND deleted = 0) ORDER BY descrizione", "value": "'.$idtipoanagrafica.'" ]}
{[ "type": "select", "label": "'.tr('Tipo di anagrafica').'", "name": "idtipoanagrafica[]", "multiple": "1", "required": 1, "values": "query=SELECT idtipoanagrafica AS id, descrizione FROM an_tipianagrafiche WHERE idtipoanagrafica NOT IN (SELECT DISTINCT(x.idtipoanagrafica) FROM an_tipianagrafiche_anagrafiche x INNER JOIN an_tipianagrafiche t ON x.idtipoanagrafica = t.idtipoanagrafica INNER JOIN an_anagrafiche ON an_anagrafiche.idanagrafica = x.idanagrafica WHERE t.descrizione = \'Azienda\' AND deleted_at IS NULL) ORDER BY descrizione", "value": "'.(isset($idtipoanagrafica) ? $idtipoanagrafica : null).'", "readonly": '.(!empty($readonly_tipo) ? 1 : 0).' ]}
</div>
</div>
<div class="row">
<div class="col-md-6">
{[ "type": "text", "label": "'.tr('Nome').'", "name": "nome", "required": 0 ]}
</div>
<div class="col-md-6">
{[ "type": "text", "label": "'.tr('Cognome').'", "name": "cognome", "required": 0 ]}
</div>
</div>';
echo
'<div class="box box-info collapsed-box">
echo '
<div class="box box-info collapsed-box">
<div class="box-header with-border">
<h3 class="box-title">'.tr('Dati anagrafici').'</h3>
<div class="box-tools pull-right">
@ -36,49 +43,49 @@ echo
</button>
</div>
</div>
<div class="box-body collapse">
<div class="box-body">
<div class="row">
<div class="col-md-4">
{[ "type": "text", "label": "'.tr('Partita IVA').'", "maxlength": 13, "name": "piva", "class": "text-center alphanumeric-mask", "value": "" ]}
{[ "type": "text", "label": "'.tr('Partita IVA').'", "maxlength": 13, "name": "piva", "class": "text-center alphanumeric-mask" ]}
</div>
<div class="col-md-4">
{[ "type": "text", "label": "'.tr('Codice fiscale').'", "maxlength": 16, "name": "codice_fiscale", "class": "text-center alphanumeric-mask", "value": "" ]}
{[ "type": "text", "label": "'.tr('Codice fiscale').'", "maxlength": 16, "name": "codice_fiscale", "class": "text-center alphanumeric-mask" ]}
</div>
<div class="col-md-4">
{[ "type": "select", "label": "'.tr('Relazione').'", "name": "idrelazione", "values": "query=SELECT id, descrizione, colore AS _bgcolor_ FROM an_relazioni ORDER BY descrizione", "value": "" ]}
{[ "type": "select", "label": "'.tr('Relazione').'", "name": "idrelazione", "values": "query=SELECT id, descrizione, colore AS _bgcolor_ FROM an_relazioni ORDER BY descrizione" ]}
</div>
</div>
<div class="row">
<div class="col-md-4">
{[ "type": "text", "label": "'.tr('Indirizzo').'", "name": "indirizzo", "value": "" ]}
{[ "type": "text", "label": "'.tr('Indirizzo').'", "name": "indirizzo" ]}
</div>
<div class="col-md-2">
{[ "type": "text", "label": "'.tr('C.A.P.').'", "name": "cap", "maxlength": 5, "class": "text-center", "value": "" ]}
{[ "type": "text", "label": "'.tr('C.A.P.').'", "name": "cap", "maxlength": 5, "class": "text-center" ]}
</div>
<div class="col-md-4">
{[ "type": "text", "label": "'.tr('Città').'", "name": "citta", "class": "text-center", "value": "" ]}
{[ "type": "text", "label": "'.tr('Città').'", "name": "citta", "class": "text-center" ]}
</div>
<div class="col-md-2">
{[ "type": "text", "label": "'.tr('Provincia').'", "name": "provincia", "maxlength": 2, "class": "text-center", "value": "" ]}
{[ "type": "text", "label": "'.tr('Provincia').'", "name": "provincia", "maxlength": 2, "class": "text-center", "extra": "onkeyup=\"this.value = this.value.toUpperCase();\"" ]}
</div>
</div>
<div class="row">
<div class="col-md-4">
{[ "type": "text", "label": "'.tr('Telefono').'", "name": "telefono", "class": "text-center", "value": "", "icon-before": "<i class=\"fa fa-phone\"></i>" ]}
{[ "type": "text", "label": "'.tr('Telefono').'", "name": "telefono", "class": "text-center", "icon-before": "<i class=\"fa fa-phone\"></i>" ]}
</div>
<div class="col-md-4">
{[ "type": "text", "label": "'.tr('Cellulare').'", "name": "cellulare", "class": "text-center", "value": "", "icon-before": "<i class=\"fa fa-mobile\"></i>" ]}
{[ "type": "text", "label": "'.tr('Cellulare').'", "name": "cellulare", "class": "text-center", "icon-before": "<i class=\"fa fa-mobile\"></i>" ]}
</div>
<div class="col-md-4">
{[ "type": "text", "label": "'.tr('Email').'", "name": "email", "class": "email-mask", "placeholder":"casella@dominio.ext", "value": "", "icon-before": "<i class=\"fa fa-envelope\"></i>" ]}
{[ "type": "text", "label": "'.tr('Email').'", "name": "email", "class": "email-mask", "placeholder":"casella@dominio.ext", "icon-before": "<i class=\"fa fa-envelope\"></i>" ]}
</div>
</div>
@ -86,11 +93,8 @@ echo
</div>
</div>';
echo
'<div class="row">
echo
'<div class="row">
<div class="col-md-12 text-right">
<button type="submit" class="btn btn-primary"><i class="fa fa-plus"></i> '.tr('Aggiungi').'</button>
</div>

View File

@ -31,4 +31,56 @@ switch ($resource) {
echo '<option value="'.$rs[$i]['id'].'">'.$rs[$i]['descrizione']."</option>\n";
}
break;
// Elenco e-mail
case 'get_email':
$id_anagrafica = get('id_anagrafica');
if (!empty($id_anagrafica)) {
$where = 'AND idanagrafica = '.prepare($id_anagrafica);
}
$results = [];
// Tutti i referenti per questo cliente
$q = "SELECT DISTINCT(email), idanagrafica, nome AS ragione_sociale FROM an_referenti WHERE email != '' ".$where.' ORDER BY idanagrafica';
$rs = $dbo->fetchArray($q);
foreach ($rs as $r) {
$results[] = [
'value' => $r['email'],
'label' => $r['ragione_sociale'].' <'.$r['email'].'>',
];
}
// Tutti gli agenti
$q = "SELECT DISTINCT(email), ragione_sociale, an_anagrafiche.idanagrafica FROM an_anagrafiche INNER JOIN an_tipianagrafiche_anagrafiche ON an_anagrafiche.idanagrafica=an_tipianagrafiche_anagrafiche.idanagrafica WHERE idtipoanagrafica = (SELECT idtipoanagrafica FROM an_tipianagrafiche WHERE descrizione='Agente') AND email != '' ORDER BY idanagrafica";
$rs = $dbo->fetchArray($q);
foreach ($rs as $r) {
$results[] = [
'value' => $r['email'],
'label' => $r['ragione_sociale'].' <'.$r['email'].'>',
];
}
// Email del cliente
$query = "SELECT DISTINCT(email) AS email, ragione_sociale, idanagrafica FROM an_anagrafiche WHERE email != '' ".$where;
// Se type pec, propongo anche la pec
if (get('type') == 'pec') {
$query .= " UNION SELECT DISTINCT(pec), ragione_sociale, idanagrafica FROM an_anagrafiche WHERE email != '' ".$where;
}
$query .= ' ORDER BY idanagrafica';
$rs = $dbo->fetchArray($query);
foreach ($rs as $r) {
$results[] = [
'value' => $r['email'],
'label' => $r['ragione_sociale'].' <'.$r['email'].'>',
];
}
echo json_encode($results);
break;
}

View File

@ -6,7 +6,7 @@ include_once __DIR__.'/../../../core.php';
Anagrafiche
*/
$id_module = Modules::get('Anagrafiche')['id'];
$link_id = Modules::get('Anagrafiche')['id'];
$fields = [
'Codice' => 'codice',
@ -50,8 +50,9 @@ $rs = $dbo->fetchArray($query);
foreach ($rs as $r) {
$result = [];
$result['link'] = ROOTDIR.'/editor.php?id_module='.$id_module.'&id_record='.$r['id'];
$result['link'] = ROOTDIR.'/editor.php?id_module='.$link_id.'&id_record='.$r['id'];
$result['title'] = $r['ragione_sociale'];
$result['title'] .= !empty($r['deleted_at']) ? ' <small class="text-danger"><em>('.tr('Eliminato').')</em></small>' : '';
$result['category'] = 'Anagrafiche';
// Campi da evidenziare
@ -96,7 +97,7 @@ $plugin = $dbo->fetchArray("SELECT id FROM zz_plugins WHERE name='Referenti'");
foreach ($rs as $r) {
$result = [];
$result['link'] = ROOTDIR.'/editor.php?id_module='.$id_module.'&id_record='.$r['id'].'#tab_'.$plugin[0]['id'];
$result['link'] = ROOTDIR.'/editor.php?id_module='.$link_id.'&id_record='.$r['id'].'#tab_'.$plugin[0]['id'];
$result['title'] = $r['nome'];
$result['category'] = 'Referenti';

View File

@ -4,17 +4,15 @@ include_once __DIR__.'/../../../core.php';
switch ($resource) {
case 'clienti':
//$citta_cliente = ", IF(citta IS NULL OR citta = '', '', CONCAT(' (', citta, ')'))";
$query = "SELECT an_anagrafiche.idanagrafica AS id, CONCAT(ragione_sociale $citta_cliente) AS descrizione, idtipointervento_default FROM an_anagrafiche INNER JOIN (an_tipianagrafiche_anagrafiche INNER JOIN an_tipianagrafiche ON an_tipianagrafiche_anagrafiche.idtipoanagrafica=an_tipianagrafiche.idtipoanagrafica) ON an_anagrafiche.idanagrafica=an_tipianagrafiche_anagrafiche.idanagrafica |where| ORDER BY ragione_sociale";
$query = 'SELECT an_anagrafiche.idanagrafica AS id, CONCAT(ragione_sociale) AS descrizione, idtipointervento_default FROM an_anagrafiche INNER JOIN (an_tipianagrafiche_anagrafiche INNER JOIN an_tipianagrafiche ON an_tipianagrafiche_anagrafiche.idtipoanagrafica=an_tipianagrafiche.idtipoanagrafica) ON an_anagrafiche.idanagrafica=an_tipianagrafiche_anagrafiche.idanagrafica |where| ORDER BY ragione_sociale';
foreach ($elements as $element) {
$filter[] = 'an_anagrafiche.idanagrafica='.prepare($element);
}
$where[] = "descrizione='Cliente'";
if (empty($filter)) {
$where[] = "descrizione='Cliente'";
$where[] = 'deleted=0';
$where[] = 'deleted_at IS NULL';
}
if (!empty($search)) {
@ -34,9 +32,31 @@ switch ($resource) {
$filter[] = 'an_anagrafiche.idanagrafica='.prepare($element);
}
$where[] = "descrizione='Fornitore'";
if (empty($filter)) {
$where[] = "descrizione='Fornitore'";
$where[] = 'deleted=0';
$where[] = 'deleted_at IS NULL';
}
if (!empty($search)) {
$search_fields[] = 'ragione_sociale LIKE '.prepare('%'.$search.'%');
$search_fields[] = 'citta LIKE '.prepare('%'.$search.'%');
$search_fields[] = 'provincia LIKE '.prepare('%'.$search.'%');
}
$custom['idtipointervento'] = 'idtipointervento_default';
break;
case 'vettori':
$query = "SELECT an_anagrafiche.idanagrafica AS id, CONCAT(ragione_sociale, IF(citta IS NULL OR citta = '', '', CONCAT(' (', citta, ')'))) AS descrizione, idtipointervento_default FROM an_anagrafiche INNER JOIN (an_tipianagrafiche_anagrafiche INNER JOIN an_tipianagrafiche ON an_tipianagrafiche_anagrafiche.idtipoanagrafica=an_tipianagrafiche.idtipoanagrafica) ON an_anagrafiche.idanagrafica=an_tipianagrafiche_anagrafiche.idanagrafica |where| ORDER BY ragione_sociale";
foreach ($elements as $element) {
$filter[] = 'an_anagrafiche.idanagrafica='.prepare($element);
}
$where[] = "descrizione='Vettore'";
if (empty($filter)) {
$where[] = 'deleted_at IS NULL';
}
if (!empty($search)) {
@ -56,9 +76,9 @@ switch ($resource) {
$filter[] = 'an_anagrafiche.idanagrafica='.prepare($element);
}
$where[] = "descrizione='Agente'";
if (empty($filter)) {
$where[] = "descrizione='Agente'";
$where[] = 'deleted=0';
$where[] = 'deleted_at IS NULL';
}
if (!empty($search)) {
@ -77,7 +97,7 @@ switch ($resource) {
$idagente_default = 0;
}
$ids = array_column($results, $id);
$ids = array_column($results, 'idanagrafica');
$pos = array_search($idagente_default, $ids);
if ($pos !== false) {
$results[$pos]['_bgcolor_'] = '#ff0';
@ -91,9 +111,15 @@ switch ($resource) {
$filter[] = 'an_anagrafiche.idanagrafica='.prepare($element);
}
$where[] = "descrizione='Tecnico'";
if (empty($filter)) {
$where[] = "descrizione='Tecnico'";
$where[] = 'deleted=0';
$where[] = 'deleted_at IS NULL';
//come tecnico posso aprire attività solo a mio nome
$user = Auth::user();
if ($user['gruppo'] == 'Tecnici' && !empty($user['idanagrafica'])) {
$where[] = 'an_anagrafiche.idanagrafica='.$user['idanagrafica'];
}
}
if (!empty($search)) {
@ -105,16 +131,62 @@ switch ($resource) {
// $custom['idtipointervento'] = 'idtipointervento_default';
break;
case 'clienti_fornitori':
$query = "SELECT `an_anagrafiche`.`idanagrafica` AS id, CONCAT_WS('', ragione_sociale, IF(citta !='' OR provincia != '', CONCAT(' (', citta, IF(provincia!='', CONCAT(' ', provincia), ''), ')'), '')) AS descrizione, `an_tipianagrafiche`.`descrizione` AS optgroup, idtipointervento_default, an_tipianagrafiche.idtipoanagrafica FROM `an_tipianagrafiche` INNER JOIN `an_tipianagrafiche_anagrafiche` ON `an_tipianagrafiche`.`idtipoanagrafica`=`an_tipianagrafiche_anagrafiche`.`idtipoanagrafica` INNER JOIN `an_anagrafiche` ON `an_anagrafiche`.`idanagrafica`=`an_tipianagrafiche_anagrafiche`.`idanagrafica` |where| ORDER BY `optgroup` ASC, ragione_sociale ASC";
foreach ($elements as $element) {
$filter[] = 'an_anagrafiche.idanagrafica='.prepare($element);
}
$where = [];
if (empty($filter)) {
$where[] = 'deleted_at IS NULL';
$where[] = "an_tipianagrafiche_anagrafiche.idtipoanagrafica IN (SELECT idtipoanagrafica FROM an_tipianagrafiche WHERE descrizione = 'Cliente' OR descrizione = 'Fornitore')";
}
if (!empty($search)) {
$search_fields[] = 'ragione_sociale LIKE '.prepare('%'.$search.'%');
$search_fields[] = 'citta LIKE '.prepare('%'.$search.'%');
$search_fields[] = 'provincia LIKE '.prepare('%'.$search.'%');
}
// Aggiunta filtri di ricerca
if (!empty($search_fields)) {
$where[] = '('.implode(' OR ', $search_fields).')';
}
if (!empty($filter)) {
$where[] = '('.implode(' OR ', $filter).')';
}
$query = str_replace('|where|', !empty($where) ? 'WHERE '.implode(' AND ', $where) : '', $query);
$rs = $dbo->fetchArray($query);
foreach ($rs as $r) {
if ($prev != $r['optgroup']) {
$results[] = ['text' => $r['optgroup'], 'children' => []];
$prev = $r['optgroup'];
}
$results[count($results) - 1]['children'][] = [
'id' => $r['id'],
'text' => $r['descrizione'],
'descrizione' => $r['descrizione'],
];
}
break;
// Nota Bene: nel campo id viene specificato idtipoanagrafica-idanagrafica -> modulo Utenti e permessi, creazione nuovo utente
case 'anagrafiche':
$query = "SELECT CONCAT(an_tipianagrafiche.idtipoanagrafica, '-', an_anagrafiche.idanagrafica) AS id, CONCAT_WS('', ragione_sociale, ' (', citta, ' ', provincia, ')') AS descrizione idtipointervento_default FROM an_anagrafiche INNER JOIN (an_tipianagrafiche_anagrafiche INNER JOIN an_tipianagrafiche ON an_tipianagrafiche_anagrafiche.idtipoanagrafica=an_tipianagrafiche.idtipoanagrafica) ON an_anagrafiche.idanagrafica=an_tipianagrafiche_anagrafiche.idanagrafica |where| ORDER BY ragione_sociale";
$query = "SELECT CONCAT(an_tipianagrafiche.idtipoanagrafica, '-', an_anagrafiche.idanagrafica) AS id, CONCAT_WS('', ragione_sociale, IF(citta !='' OR provincia != '', CONCAT(' (', citta, IF(provincia!='', CONCAT(' ', provincia), ''), ')'), '')) AS descrizione, idtipointervento_default FROM an_anagrafiche INNER JOIN (an_tipianagrafiche_anagrafiche INNER JOIN an_tipianagrafiche ON an_tipianagrafiche_anagrafiche.idtipoanagrafica=an_tipianagrafiche.idtipoanagrafica) ON an_anagrafiche.idanagrafica=an_tipianagrafiche_anagrafiche.idanagrafica |where| ORDER BY ragione_sociale";
foreach ($elements as $element) {
$filter[] = 'an_anagrafiche.idanagrafica='.prepare($element);
}
if (empty($filter)) {
$where[] = 'deleted=0';
$where[] = 'deleted_at IS NULL';
}
if (!empty($search)) {
@ -128,7 +200,7 @@ switch ($resource) {
case 'sedi':
if (isset($superselect['idanagrafica'])) {
$query = "SELECT * FROM (SELECT 0 AS id, 'Sede legale' AS descrizione UNION SELECT id, CONCAT_WS(' - ', nomesede, citta) FROM an_sedi |where|) AS tab |filter| ORDER BY id";
$query = "SELECT * FROM (SELECT 0 AS id, 'Sede legale' AS descrizione UNION SELECT id, CONCAT_WS(' - ', nomesede, citta) FROM an_sedi |where|) AS tab |filter| ORDER BY descrizione";
foreach ($elements as $element) {
$filter[] = 'id='.prepare($element);
@ -145,7 +217,7 @@ switch ($resource) {
case 'referenti':
if (isset($superselect['idanagrafica'])) {
$query = 'SELECT id, nome AS descrizione FROM an_referenti |where| ORDER BY id';
$query = 'SELECT id, nome AS descrizione FROM an_referenti |where| ORDER BY nome';
foreach ($elements as $element) {
$filter[] = 'id='.prepare($element);

View File

@ -3,7 +3,7 @@
switch ($resource) {
case 'add_anagrafica':
$rs = $dbo->fetchArray('SELECT codice FROM an_anagrafiche ORDER BY CAST(codice AS SIGNED) DESC LIMIT 0, 1');
$codice = Util\Generator::generate(get_var('Formato codice anagrafica'), $rs[0]['codice']);
$codice = Util\Generator::generate(setting('Formato codice anagrafica'), $rs[0]['codice']);
// Inserisco l'anagrafica
$dbo->insert('an_anagrafiche', [

View File

@ -4,12 +4,12 @@ switch ($resource) {
case 'delete_anagrafica':
$id_azienda = $dbo->fetchArray("SELECT idtipoanagrafica FROM an_tipianagrafiche WHERE descrizione='Azienda'")[0]['idtipoanagrafica'];
$records = $dbo->fetchArray('SELECT an_tipianagrafiche.idtipoanagrafica FROM an_tipianagrafiche INNER JOIN an_tipianagrafiche_anagrafiche ON an_tipianagrafiche.idtipoanagrafica=an_tipianagrafiche_anagrafiche.idtipoanagrafica WHERE idanagrafica='.prepare($request['id']));
$tipi = array_column($records, 'idtipoanagrafica');
$anagrafica = $dbo->fetchArray('SELECT an_tipianagrafiche.idtipoanagrafica FROM an_tipianagrafiche INNER JOIN an_tipianagrafiche_anagrafiche ON an_tipianagrafiche.idtipoanagrafica=an_tipianagrafiche_anagrafiche.idtipoanagrafica WHERE idanagrafica='.prepare($request['id']));
$tipi = array_column($anagrafica, 'idtipoanagrafica');
// Se l'anagrafica non è l'azienda principale, la disattivo
if (!in_array($id_azienda, $tipi)) {
$dbo->query('UPDATE an_anagrafiche SET deleted = 1 WHERE idanagrafica = '.prepare($request['id']));
$dbo->query('UPDATE an_anagrafiche SET deleted_at = NOW() WHERE idanagrafica = '.prepare($request['id']));
}
break;

View File

@ -8,8 +8,49 @@ switch ($resource) {
$order[] = 'idanagrafica';
}
if (empty($where['deleted'])) {
$where['deleted'] = 0;
if (empty($where['deleted_at'])) {
$where['deleted_at'] = null;
}
break;
// Elenco clienti per l'applicazione
case 'clienti':
$query = "SELECT an_anagrafiche.idanagrafica AS id,
an_anagrafiche.ragione_sociale,
an_anagrafiche.piva,
an_anagrafiche.codice_fiscale,
an_anagrafiche.indirizzo,
an_anagrafiche.indirizzo2,
an_anagrafiche.citta,
an_anagrafiche.cap,
an_anagrafiche.provincia,
an_anagrafiche.km,
IFNULL(an_anagrafiche.lat, 0.00) AS latitudine,
IFNULL(an_anagrafiche.lng, 0.00) AS longitudine,
an_nazioni.nome AS nazione,
an_anagrafiche.telefono,
an_anagrafiche.fax,
an_anagrafiche.cellulare,
an_anagrafiche.email,
an_anagrafiche.sitoweb,
an_anagrafiche.note,
an_anagrafiche.idzona,
an_anagrafiche.deleted_at
FROM an_anagrafiche
LEFT OUTER JOIN an_nazioni ON an_anagrafiche.id_nazione=an_nazioni.id
WHERE
an_anagrafiche.deleted_at IS NULL AND
an_anagrafiche.idanagrafica IN (SELECT idanagrafica FROM an_tipianagrafiche_anagrafiche WHERE idtipoanagrafica = (SELECT idtipoanagrafica FROM an_tipianagrafiche WHERE descrizione = 'Cliente'))
ORDER BY an_anagrafiche.ragione_sociale";
break;
// Elenco sedi per l'applicazione
case 'sedi':
$table = 'an_sedi';
if (empty($order)) {
$order[] = 'id';
}
break;
@ -17,4 +58,6 @@ switch ($resource) {
return [
'an_anagrafiche',
'clienti',
'sedi',
];

View File

@ -4,19 +4,24 @@ include_once __DIR__.'/../../core.php';
switch (post('op')) {
case 'delete-bulk':
$id_azienda = $dbo->fetchArray("SELECT idtipoanagrafica FROM an_tipianagrafiche WHERE descrizione='Azienda'")[0]['idtipoanagrafica'];
foreach ($id_records as $id) {
$records = $dbo->fetchArray('SELECT an_tipianagrafiche.idtipoanagrafica FROM an_tipianagrafiche INNER JOIN an_tipianagrafiche_anagrafiche ON an_tipianagrafiche.idtipoanagrafica=an_tipianagrafiche_anagrafiche.idtipoanagrafica WHERE idanagrafica='.prepare($id));
$tipi = array_column($records, 'idtipoanagrafica');
if (App::debug()) {
$idtipoanagrafica_azienda = $dbo->fetchArray("SELECT idtipoanagrafica FROM an_tipianagrafiche WHERE descrizione='Azienda'")[0]['idtipoanagrafica'];
// Se l'anagrafica non è l'azienda principale, la disattivo
if (!in_array($id_azienda, $tipi)) {
$dbo->query('UPDATE an_anagrafiche SET deleted = 1 WHERE idanagrafica = '.prepare($id).Modules::getAdditionalsQuery($id_module));
foreach ($id_records as $id) {
$anagrafica = $dbo->fetchArray('SELECT an_tipianagrafiche.idtipoanagrafica FROM an_tipianagrafiche INNER JOIN an_tipianagrafiche_anagrafiche ON an_tipianagrafiche.idtipoanagrafica=an_tipianagrafiche_anagrafiche.idtipoanagrafica WHERE idanagrafica='.prepare($id));
$tipi = array_column($anagrafica, 'idtipoanagrafica');
// Se l'anagrafica non è di tipo Azienda
if (!in_array($idtipoanagrafica_azienda, $tipi)) {
$dbo->query('UPDATE an_anagrafiche SET deleted_at = NOW() WHERE idanagrafica = '.prepare($id).Modules::getAdditionalsQuery($id_module));
}
}
}
$_SESSION['infos'][] = tr('Anagrafiche eliminate!');
flash()->info(tr('Anagrafiche eliminate!'));
} else {
flash()->warning(tr('Procedura in fase di sviluppo. Nessuna modifica apportata.'));
}
break;
}

View File

@ -0,0 +1,38 @@
<?php
if (in_array($id_cliente, $tipi_anagrafica)) {
echo '
<div class="btn-group">
<button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
'.tr('Nuovo').' <span class="caret"></span>
<span class="sr-only">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu dropdown-menu-right">
<li><a data-toggle="modal" data-title="'.tr('Aggiungi intervento').'" data-href="add.php?id_module='.Modules::get('Interventi')['id'].'&idanagrafica='.$record['idanagrafica'].'">
'.tr('Nuovo intervento').'
</a></li>
<li><a data-toggle="modal" data-title="'.tr('Aggiungi preventivo').'" data-href="add.php?id_module='.Modules::get('Preventivi')['id'].'&idanagrafica='.$record['idanagrafica'].'">
'.tr('Nuovo preventivo').'
</a></li>
<li><a data-toggle="modal" data-title="'.tr('Aggiungi contratto').'" data-href="add.php?id_module='.Modules::get('Contratti')['id'].'&idanagrafica='.$record['idanagrafica'].'">
'.tr('Nuovo contratto').'
</a></li>
<li><a data-toggle="modal" data-title="'.tr('Aggiungi ordine').'" data-href="add.php?id_module='.Modules::get('Ordini cliente')['id'].'&idanagrafica='.$record['idanagrafica'].'">
'.tr('Nuovo ordine').'
</a></li>
<li><a data-toggle="modal" data-title="'.tr('Aggiungi ddt').'" data-href="add.php?id_module='.Modules::get('Ddt di vendita')['id'].'&idanagrafica='.$record['idanagrafica'].'">
'.tr('Nuovo ddt').'
</a></li>
<li><a data-toggle="modal" data-title="'.tr('Aggiungi fattura').'" data-href="add.php?id_module='.Modules::get('Fatture di vendita')['id'].'&idanagrafica='.$record['idanagrafica'].'">
'.tr('Nuova fattura').'
</a></li>
</ul>
</div>';
}

View File

@ -2,10 +2,10 @@
include_once __DIR__.'/../../core.php';
$fornitore = in_array('Fornitore', explode(',', $records[0]['tipianagrafica']));
$cliente = in_array('Cliente', explode(',', $records[0]['tipianagrafica']));
$fornitore = in_array($id_fornitore, $tipi_anagrafica);
$cliente = in_array($id_cliente, $tipi_anagrafica);
$google = Settings::get('Google Maps API key');
$google = setting('Google Maps API key');
if (!empty($google)) {
echo '
@ -24,354 +24,157 @@ if (!$cliente) {
}
?>
<form action="" method="post" id="edit-form">
<input type="hidden" name="backto" value="record-edit">
<input type="hidden" name="op" value="update">
<!-- DATI ANAGRAFICI -->
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title"><?php echo tr('Dati anagrafici'); ?></h3>
</div>
<form action="" method="post" id="edit-form" >
<fieldset <?php echo (empty($record['deleted_at'])) ? '' : 'disabled'; ?> >
<input type="hidden" name="backto" value="record-edit">
<input type="hidden" name="op" value="update">
<div class="panel-body">
<div class="row">
<div class="col-md-8">
{[ "type": "text", "label": "<?php echo tr('Ragione sociale'); ?>", "name": "ragione_sociale", "required": 1, "value": "$ragione_sociale$" ]}
</div>
<div class="col-md-4">
{[ "type": "select", "label": "<?php echo tr('Tipologia'); ?>", "name": "tipo", "values": "list=\"\": \"<?php echo tr('Non specificato'); ?>\", \"Azienda\": \"<?php echo tr('Azienda'); ?>\", \"Privato\": \"<?php echo tr('Privato'); ?>\", \"Ente pubblico\": \"<?php echo tr('Ente pubblico'); ?>\"", "value": "$tipo$" ]}
</div>
<!-- DATI ANAGRAFICI -->
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title"><?php echo tr('Dati anagrafici'); ?></h3>
</div>
<div class="row">
<div class="col-md-4">
{[ "type": "text", "label": "<?php echo tr('Partita IVA'); ?>", "maxlength": 13, "name": "piva", "class": "text-center alphanumeric-mask", "value": "$piva$" ]}
</div>
<div class="col-md-4">
{[ "type": "text", "label": "<?php echo tr('Codice fiscale'); ?>", "maxlength": 16, "name": "codice_fiscale", "class": "text-center alphanumeric-mask", "value": "$codice_fiscale$" ]}
</div>
<div class="col-md-4">
{[ "type": "text", "label": "<?php echo tr('Codice anagrafica'); ?>", "name": "codice", "required": 1, "class": "text-center", "value": "$codice$" ]}
</div>
</div>
<div class="row">
<div class="col-md-4">
{[ "type": "text", "label": "<?php echo tr('Luogo di nascita'); ?>", "name": "luogo_nascita", "value": "$luogo_nascita$" ]}
</div>
<div class="col-md-4">
{[ "type": "date", "label": "<?php echo tr('Data di nascita'); ?>", "maxlength": 10, "name": "data_nascita", "value": "$data_nascita$" ]}
</div>
<div class="col-md-4">
{[ "type": "select", "label": "<?php echo tr('Sesso'); ?>", "name": "sesso", "values": "list=\"\": \"Non specificato\", \"M\": \"<?php echo tr('Uomo'); ?>\", \"F\": \"<?php echo tr('Donna'); ?>\"", "value": "$sesso$" ]}
</div>
</div>
<div class="row">
<div class="col-md-4">
{[ "type": "text", "label": "<?php echo tr('Indirizzo'); ?>", "name": "indirizzo", "value": "$indirizzo$" ]}
</div>
<div class="col-md-4">
{[ "type": "text", "label": "<?php echo tr('Indirizzo2'); ?>", "name": "indirizzo2", "value": "$indirizzo2$" ]}
</div>
<div class="col-md-4">
{[ "type": "select", "label": "<?php echo tr('Zona'); ?>", "name": "idzona", "values": "query=SELECT id, CONCAT_WS( ' - ', nome, descrizione) AS descrizione FROM an_zone ORDER BY descrizione ASC", "value": "$idzona$", "placeholder": "<?php echo tr('Nessuna zona'); ?>", "icon-after": "add|<?php echo Modules::get('Zone')['id']; ?>" ]}
</div>
</div>
<div class="row">
<div class="col-md-2">
{[ "type": "select", "label": "<?php echo tr('Nazione'); ?>", "name": "id_nazione", "values": "query=SELECT id AS id, nome AS descrizione FROM an_nazioni ORDER BY nome ASC", "value": "$id_nazione$" ]}
</div>
<div class="col-md-2">
{[ "type": "text", "label": "<?php echo tr('C.A.P.'); ?>", "name": "cap", "maxlength": 5, "class": "text-center", "value": "$cap$" ]}
</div>
<div class="col-md-4">
{[ "type": "text", "label": "<?php echo tr('Città'); ?>", "name": "citta", "class": "text-center", "value": "$citta$" ]}
</div>
<div class="col-md-2">
{[ "type": "text", "label": "<?php echo tr('Provincia'); ?>", "name": "provincia", "maxlength": 2, "class": "text-center", "value": "$provincia$" ]}
</div>
<div class="col-md-2">
{[ "type": "number", "label": "<?php echo tr('Km'); ?>", "name": "km", "maxlength": 4, "class": "text-center", "value": "$km$" ]}
</div>
</div>
</div>
</div>
<!-- CONTATTI -->
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title"><?php echo tr('Contatti'); ?></h3>
</div>
<div class="panel-body">
<div class="row">
<div class="col-md-4">
{[ "type": "text", "label": "<?php echo tr('Telefono'); ?>", "name": "telefono", "class": "text-center", "value": "$telefono$", "icon-before": "<i class='fa fa-phone'></i>" ]}
</div>
<div class="col-md-4">
{[ "type": "text", "label": "<?php echo tr('Fax'); ?>", "name": "fax", "class": "text-center", "value": "$fax$", "icon-before": "<i class='fa fa-fax'></i>" ]}
</div>
<div class="col-md-4">
{[ "type": "text", "label": "<?php echo tr('Cellulare'); ?>", "name": "cellulare", "class": "text-center", "value": "$cellulare$", "icon-before": "<i class='fa fa-mobile'></i>" ]}
</div>
</div>
<div class="row">
<div class="col-md-4">
{[ "type": "text", "label": "<?php echo tr('Email'); ?>", "name": "email", "class": "email-mask", "placeholder":"casella@dominio.ext", "value": "$email$", "icon-before": "<i class='fa fa-envelope'></i>" ]}
</div>
<div class="col-md-4">
{[ "type": "text", "label": "<?php echo tr('PEC'); ?>", "name": "pec", "class": "email-mask", "placeholder":"pec@dominio.ext", "value": "$pec$", "icon-before": "<i class='fa fa-envelope-o'></i>" ]}
</div>
<div class="col-md-4">
{[ "type": "text", "label": "<?php echo tr('Sito web'); ?>", "name": "sitoweb", "placeholder":"www.dominio.ext", "value": "$sitoweb$", "icon-before": "<i class='fa fa-globe'></i>" ]}
</div>
</div>
</div>
</div>
<?php
if ($cliente || $fornitore) {
?>
<!-- ACQUISTI -->
<div class = "row">
<div class="col-md-6">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title"><?php echo tr('Acquisti'); ?></h3>
</div>
<div class="panel-body">
<div class="row">
<div class="col-md-6">
{[ "type": "select", "label": "<?php echo tr('Pagamento predefinito'); ?>", "name": "idpagamento_acquisti", "values": "query=SELECT id, descrizione FROM co_pagamenti GROUP BY descrizione ORDER BY descrizione ASC", "value": "$idpagamento_acquisti$", "extra": "<?php echo ($fornitore) ? '' : 'readonly'; ?>" ]}
</div>
<div class="col-md-6">
{[ "type": "select", "label": "<?php echo tr('Banca predefinita'); ?>", "name": "idbanca_acquisti", "values": "query=SELECT id, nome AS descrizione FROM co_banche ORDER BY nome ASC", "value": "$idbanca_acquisti$", "extra": "<?php echo ($fornitore) ? '' : 'readonly'; ?>", "icon-after": "add|<?php echo Modules::get('Banche')['id']; ?>|||<?php echo ($fornitore) ? '' : 'disabled'; ?>" ]}
</div>
<div class="col-md-6">
{[ "type": "select", "label": "<?php echo tr('Iva predefinita'); ?>", "name": "idiva_acquisti", "values": "query=SELECT id, descrizione FROM co_iva ORDER BY descrizione ASC", "value": "$idiva_acquisti$", "extra": "<?php echo ($fornitore) ? '' : 'readonly'; ?>" ]}
</div>
<div class="col-md-6">
{[ "type": "select", "label": "<?php echo tr('Listino articoli'); ?>", "name": "idlistino_acquisti", "values": "query=SELECT id, nome AS descrizione FROM mg_listini ORDER BY nome ASC", "value": "$idlistino_acquisti$", "extra": "<?php echo ($fornitore) ? '' : 'readonly'; ?>" ]}
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<!-- VENDITE -->
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title"><?php echo tr('Vendite'); ?></h3>
</div>
<div class="panel-body">
<div class="row">
<div class="col-md-6">
{[ "type": "select", "label": "<?php echo tr('Pagamento predefinito'); ?>", "name": "idpagamento_vendite", "values": "query=SELECT id, descrizione FROM co_pagamenti GROUP BY descrizione ORDER BY descrizione ASC", "value": "$idpagamento_vendite$", "extra": "<?php echo ($cliente) ? '' : 'readonly'; ?>" ]}
</div>
<div class="col-md-6">
{[ "type": "select", "label": "<?php echo tr('Banca predefinita'); ?>", "name": "idbanca_vendite", "values": "query=SELECT id, nome AS descrizione FROM co_banche ORDER BY nome ASC", "value": "$idbanca_vendite$", "extra": "<?php echo ($cliente) ? '' : 'readonly'; ?>", "icon-after": "add|<?php echo Modules::get('Banche')['id']; ?>|||<?php echo ($cliente) ? '' : 'disabled'; ?>" ]}
</div>
<div class="col-md-6">
{[ "type": "select", "label": "<?php echo tr('Iva predefinita'); ?>", "name": "idiva_vendite", "values": "query=SELECT id, descrizione FROM co_iva ORDER BY descrizione ASC", "value": "$idiva_vendite$", "extra": "<?php echo ($cliente) ? '' : 'readonly'; ?>" ]}
</div>
<div class="col-md-6">
{[ "type": "select", "label": "<?php echo tr('Listino articoli'); ?>", "name": "idlistino_vendite", "values": "query=SELECT id, nome AS descrizione FROM mg_listini ORDER BY nome ASC", "value": "$idlistino_vendite$", "extra": "<?php echo ($cliente) ? '' : 'readonly'; ?>" ]}
</div>
<div class="col-md-6">
{[ "type": "select", "label": "<?php echo tr('Indirizzo di fatturazione'); ?>", "name": "idsede_fatturazione", "values": "query=SELECT id, IF(citta = '', nomesede, CONCAT_WS(', ', nomesede, citta)) AS descrizione FROM an_sedi WHERE idanagrafica='<?php echo $id_record; ?>' UNION SELECT '0' AS id, 'Sede legale' AS descrizione ORDER BY descrizione", "value": "$idsede_fatturazione$" , "extra": "<?php echo ($cliente) ? '' : 'readonly'; ?>" ]}
</div>
<div class="col-md-6">
{[ "type": "select", "label": "<?php echo tr('Tipo attività'); ?>", "name": "idtipointervento_default", "values": "query=SELECT idtipointervento AS id, descrizione FROM in_tipiintervento ORDER BY descrizione ASC", "value": "$idtipointervento_default$", "extra": "<?php echo ($cliente) ? '' : 'readonly'; ?>" ]}
</div>
<div class="col-md-6">
{[ "type": "select", "label": "Agente principale", "name": "idagente", "values": "query=SELECT an_anagrafiche.idanagrafica AS id, IF(deleted=1, CONCAT(ragione_sociale, ' (Eliminato)'), ragione_sociale ) AS descrizione FROM an_anagrafiche INNER JOIN (an_tipianagrafiche_anagrafiche INNER JOIN an_tipianagrafiche ON an_tipianagrafiche_anagrafiche.idtipoanagrafica=an_tipianagrafiche.idtipoanagrafica) ON an_anagrafiche.idanagrafica=an_tipianagrafiche_anagrafiche.idanagrafica WHERE (descrizione='Agente' AND deleted=0)<?php echo isset($records[0]['idagente']) ? 'OR (an_anagrafiche.idanagrafica = '.prepare($records[0]['idagente']).'AND deleted=1) ' : ''; ?>ORDER BY ragione_sociale", "value": "$idagente$", "extra": "<?php echo ($cliente) ? '' : 'readonly'; ?>" ]}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="clearfix" ></div>
<?php
}
?>
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title"><?php echo tr('Informazioni aggiuntive'); ?></h3>
</div>
<div class="panel-body">
<div class="row">
<div class="col-md-3">
{[ "type": "text", "label": "<?php echo tr('Codice registro imprese'); ?>", "name": "codiceri", "value": "$codiceri$" ]}
</div>
<div class="col-md-3">
{[ "type": "text", "label": "<?php echo tr('Codice R.E.A.').'<small>('.tr('provincia/C.C.I.A.A.').')</small>'; ?>", "name": "codicerea", "value": "$codicerea$" ]}
</div>
<div class="col-md-3">
{[ "type": "text", "label": "<?php echo tr('Num. iscr. C.C.I.A.A.'); ?>", "name": "cciaa", "value": "$cciaa$" ]}
</div>
<div class="col-md-3">
{[ "type": "text", "label": "<?php echo tr('Città iscr. C.C.I.A.A.'); ?>", "name": "cciaa_citta", "value": "$cciaa_citta$" ]}
</div>
</div>
<div class="row">
<div class="col-md-3">
{[ "type": "text", "label": "<?php echo tr('Num. iscr. tribunale'); ?>", "name": "iscrizione_tribunale", "value": "$iscrizione_tribunale$" ]}
</div>
<div class="col-md-3">
{[ "type": "text", "label": "<?php echo tr('Num. iscr. albo artigiani'); ?>", "name": "n_alboartigiani", "value": "$n_alboartigiani$" ]}
</div>
<div class="col-md-3">
{[ "type": "text", "label": "<?php echo tr('Foro di competenza'); ?>", "name": "foro_competenza", "value": "$foro_competenza$" ]}
</div>
<div class="col-md-3">
{[ "type": "text", "label": "<?php echo tr('Capitale sociale'); ?>", "name": "capitale_sociale", "value": "$capitale_sociale$" ]}
</div>
</div>
<?php
//se non è l'anagrafica azienda, ma cliente o fornitore
if ((!str_contains($records[0]['idtipianagrafica'], $id_azienda)) or (($cliente or $fornitore))) {
?>
<div class="row">
<div class="col-md-3">
{[ "type": "text", "label": "<?php echo tr('Appoggio bancario'); ?>", "name": "appoggiobancario", "value": "$appoggiobancario$" ]}
</div>
<div class="col-md-3">
{[ "type": "text", "label": "<?php echo tr('Filiale banca'); ?>", "name": "filiale", "value": "$filiale$" ]}
</div>
<div class="col-md-3">
{[ "type": "text", "label": "<?php echo tr('Codice IBAN'); ?>", "name": "codiceiban", "value": "$codiceiban$" ]}
</div>
<div class="col-md-3">
{[ "type": "text", "label": "<?php echo tr('Codice BIC'); ?>", "name": "bic", "value": "$bic$" ]}
</div>
</div>
<?php
}
?>
<div class="row">
<div class="col-md-12">
{[ "type": "text", "label": "<?php echo tr('Dicitura fissa in fattura'); ?>", "name": "diciturafissafattura", "value": "$diciturafissafattura$" ]}
</div>
</div>
<div class="row">
<div class="col-md-3">
{[ "type": "text", "label": "<?php echo tr('Settore merceologico'); ?>", "name": "settore", "value": "$settore$" ]}
</div>
<div class="col-md-3">
{[ "type": "text", "label": "<?php echo tr('Marche trattate'); ?>", "name": "marche", "value": "$marche$" ]}
</div>
<div class="col-md-3">
{[ "type": "number", "label": "<?php echo tr('Num. dipendenti'); ?>", "name": "dipendenti", "decimals": 0, "value": "$dipendenti$" ]}
</div>
<div class="col-md-3">
{[ "type": "number", "label": "<?php echo tr('Num. macchine'); ?>", "name": "macchine", "decimals": 0, "value": "$macchine$" ]}
</div>
</div>
<div class="row">
<div class="col-md-12">
{[ "type": "select", "multiple": "1", "label": "<?php echo tr('Tipo di anagrafica'); ?>", "name": "idtipoanagrafica[]", "values": "query=SELECT idtipoanagrafica AS id, descrizione FROM an_tipianagrafiche WHERE idtipoanagrafica NOT IN (SELECT DISTINCT(x.idtipoanagrafica) FROM an_tipianagrafiche_anagrafiche x INNER JOIN an_tipianagrafiche t ON x.idtipoanagrafica = t.idtipoanagrafica INNER JOIN an_anagrafiche ON an_anagrafiche.idanagrafica = x.idanagrafica WHERE t.descrizione = 'Azienda' AND deleted = 0) ORDER BY descrizione", "value": "$idtipianagrafica$" ]}
<?php
if (str_contains($records[0]['idtipianagrafica'], $id_azienda)) {
echo '
<p class=\'badge badge-info\' >'.tr('Questa anagrafica appartiene alla tipologia "Azienda"').'.</p>';
}
?>
</div>
</div>
<div class="row">
<?php
if (in_array('Tecnico', explode(',', $records[0]['tipianagrafica']))) {
?>
<div class="col-md-3">
{[ "type": "text", "label": "<?php echo tr('Colore'); ?>", "name": "colore", "class": "colorpicker text-center", "value": "$colore$", "extra": "maxlength='7'", "icon-after": "<div class='img-circle square'></div>" ]}
</div>
<?php
} ?>
<?php
if (in_array('Cliente', explode(',', $records[0]['tipianagrafica']))) {
?>
<div class="panel-body">
<div class="row">
<div class="col-md-6">
{[ "type": "select", "label": "Agenti secondari", "multiple": "1", "name": "idagenti[]", "values": "query=SELECT an_anagrafiche.idanagrafica AS id, IF(deleted=1, CONCAT(ragione_sociale, ' (Eliminato)'), ragione_sociale ) AS descrizione FROM an_anagrafiche INNER JOIN (an_tipianagrafiche_anagrafiche INNER JOIN an_tipianagrafiche ON an_tipianagrafiche_anagrafiche.idtipoanagrafica=an_tipianagrafiche.idtipoanagrafica) ON an_anagrafiche.idanagrafica=an_tipianagrafiche_anagrafiche.idanagrafica WHERE (descrizione='Agente' AND deleted=0 AND an_anagrafiche.idanagrafica NOT IN (SELECT idagente FROM an_anagrafiche WHERE idanagrafica = <?php echo prepare($records[0]['idanagrafica']); ?> )) OR (an_anagrafiche.idanagrafica IN (SELECT idagente FROM an_anagrafiche_agenti WHERE idanagrafica = <?php echo prepare($records[0]['idanagrafica']); ?> ) ) ORDER BY ragione_sociale", "value": "$idagenti$" ]}
{[ "type": "text", "label": "<?php echo tr('Denominazione'); ?>", "name": "ragione_sociale", "required": 1, "value": "$ragione_sociale$" ]}
</div>
<div class="col-md-3">
{[ "type": "select", "label": "<?php echo tr('Relazione con il cliente'); ?>", "name": "idrelazione", "values": "query=SELECT id, descrizione, colore AS _bgcolor_ FROM an_relazioni ORDER BY descrizione", "value": "$idrelazione$" ]}
{[ "type": "text", "label": "<?php echo tr('Partita IVA'); ?>", "maxlength": 13, "name": "piva", "class": "text-center alphanumeric-mask text-uppercase", "value": "$piva$" ]}
</div>
<div class="col-md-3">
{[ "type": "select", "label": "<?php echo tr('Tipologia'); ?>", "name": "tipo", "values": "list=\"\": \"<?php echo tr('Non specificato'); ?>\", \"Azienda\": \"<?php echo tr('Azienda'); ?>\", \"Privato\": \"<?php echo tr('Privato'); ?>\", \"Ente pubblico\": \"<?php echo tr('Ente pubblico'); ?>\"", "value": "$tipo$" ]}
</div>
</div>
<div class="row">
<div class="col-md-4">
{[ "type": "text", "label": "<?php echo tr('Nome'); ?>", "name": "nome", "required": 0, "value": "$nome$" ]}
</div>
<div class="col-md-4">
{[ "type": "text", "label": "<?php echo tr('Cognome'); ?>", "name": "cognome", "required": 0, "value": "$cognome$" ]}
</div>
<div class="col-md-4">
{[ "type": "text", "label": "<?php echo tr('Codice fiscale'); ?>", "maxlength": 16, "name": "codice_fiscale", "class": "text-center alphanumeric-mask text-uppercase", "value": "$codice_fiscale$" ]}
</div>
</div>
<!-- RIGA PER LE ANAGRAFICHE CON TIPOLOGIA 'PRIVATO' -->
<?php if ($record['tipo'] == 'Privato') {
?>
<div class="row">
<div class="col-md-4">
{[ "type": "text", "label": "<?php echo tr('Luogo di nascita'); ?>", "name": "luogo_nascita", "value": "$luogo_nascita$" ]}
</div>
<div class="col-md-4">
{[ "type": "date", "label": "<?php echo tr('Data di nascita'); ?>", "name": "data_nascita", "value": "$data_nascita$" ]}
</div>
<div class="col-md-4">
{[ "type": "select", "label": "<?php echo tr('Sesso'); ?>", "name": "sesso", "values": "list=\"\": \"Non specificato\", \"M\": \"<?php echo tr('Uomo'); ?>\", \"F\": \"<?php echo tr('Donna'); ?>\"", "value": "$sesso$" ]}
</div>
</div>
<?php
} ?>
</div>
} ?>
<div class="row">
<div class="col-md-12">
{[ "type": "textarea", "label": "<?php echo tr('Note'); ?>", "name": "note", "value": "$note$" ]}
<div class="row">
<div class="col-md-2">
{[ "type": "text", "label": "<?php echo tr('Codice anagrafica'); ?>", "name": "codice", "required": 1, "class": "text-center alphanumeric-mask", "value": "$codice$", "maxlength": 20 ]}
</div>
<div class="col-md-2">
<?php
$help_text = '<b>Attenzione</b>: per impostare il codice specificare prima \'Tipologia\' e \'Nazione\' dell\'anagrafica:<br><ul><li>Ente pubblico (B2G/PA) - Codice Univoco Ufficio (www.indicepa.gov.it), 6 caratteri</li><li>Azienda (B2B) - Codice Destinatario, 7 caratteri</li><li>Privato (B2C) - viene utilizzato il Codice Fiscale</li></ul>'.((in_array($id_azienda, $tipi_anagrafica)) ? '<p>N.B. <b>non è necessario</b> comunicare il proprio codice destinatario ai fornitori in quanto è sufficiente che questo sia registrato nel portale del Sistema Di Interscambio dell\'Agenzia Entrate (SDI)</p>' : '').'';
?>
{[ "type": "text", "label": "<?php echo ($record['tipo'] == 'Ente pubblico') ? tr('Codice unico ufficio') : tr('Codice destinatario'); ?>", "name": "codice_destinatario", "required": 0, "class": "text-center text-uppercase alphanumeric-mask", "value": "$codice_destinatario$", "maxlength": <?php echo ($record['tipo'] == 'Ente pubblico') ? '6' : '7'; ?>, "extra": "<?php echo (empty($record['tipo']) or ($record['tipo'] == 'Privato')) ? 'disabled' : ''; ?>", "help": "<?php echo tr($help_text); ?>", "readonly": "<?php echo intval($anagrafica->sedeLegale->nazione->iso2 != 'IT'); ?>" ]}
</div>
<div class="col-md-4">
{[ "type": "text", "label": "<?php echo tr('PEC'); ?>", "name": "pec", "class": "email-mask", "placeholder":"pec@dominio.ext", "value": "$pec$", "icon-before": "<i class='fa fa-envelope-o'></i>" ]}
</div>
<div class="col-md-4">
{[ "type": "text", "label": "<?php echo tr('Sito web'); ?>", "name": "sitoweb", "placeholder":"www.dominio.ext", "value": "$sitoweb$", "icon-before": "<i class='fa fa-globe'></i>" ]}
</div>
</div>
</div>
</div>
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title"><i class="fa fa-building"></i> <?php echo tr('Sede legale'); ?></h3>
</div>
<div class="panel-body">
<div class="row">
<div class="col-md-<?php echo (empty($record['indirizzo2'])) ? '6' : '4'; ?>">
{[ "type": "text", "label": "<?php echo tr('Indirizzo'); ?>", "name": "indirizzo", "value": "$indirizzo$" ]}
</div>
<div class="col-md-2<?php echo (empty($record['indirizzo2'])) ? ' hide' : ''; ?>">
{[ "type": "text", "label": "<?php echo tr('Civico'); ?>", "name": "indirizzo2", "value": "$indirizzo2$" ]}
</div>
<div class="col-md-2">
{[ "type": "text", "label": "<?php echo tr('C.A.P.'); ?>", "name": "cap", "maxlength": 5, "class": "text-center", "value": "$cap$" ]}
</div>
<div class="col-md-4">
{[ "type": "text", "label": "<?php echo tr('Città'); ?>", "name": "citta", "class": "text-center", "value": "$citta$" ]}
</div>
</div>
<div class="row">
<div class="col-md-3">
{[ "type": "text", "label": "<?php echo tr('Provincia'); ?>", "name": "provincia", "maxlength": 2, "class": "text-center text-uppercase", "value": "$provincia$", "extra": "onkeyup=\"this.value = this.value.toUpperCase();\"" ]}
</div>
<div class="col-md-3">
{[ "type": "select", "label": "<?php echo tr('Nazione'); ?>", "name": "id_nazione", "values": "query=SELECT id AS id, nome AS descrizione FROM an_nazioni ORDER BY nome ASC", "value": "$id_nazione$" ]}
</div>
<div class="col-md-3">
{[ "type": "select", "label": "<?php echo tr('Zona'); ?>", "name": "idzona", "values": "query=SELECT id, CONCAT_WS( ' - ', nome, descrizione) AS descrizione FROM an_zone ORDER BY descrizione ASC", "value": "$idzona$", "placeholder": "<?php echo tr('Nessuna zona'); ?>", "icon-after": "add|<?php echo Modules::get('Zone')['id']; ?>" ]}
</div>
<div class="col-md-3">
{[ "type": "number", "label": "<?php echo tr('Distanza'); ?>", "name": "km", "decimals":"1", "class": "text-center", "value": "$km$", "icon-after": "Km" ]}
</div>
</div>
<div class="row">
<div class="col-md-3">
{[ "type": "text", "label": "<?php echo tr('Telefono'); ?>", "name": "telefono", "class": "text-center", "value": "$telefono$", "icon-before": "<i class='fa fa-phone'></i>" ]}
</div>
<div class="col-md-3">
{[ "type": "text", "label": "<?php echo tr('Fax'); ?>", "name": "fax", "class": "text-center", "value": "$fax$", "icon-before": "<i class='fa fa-fax'></i>" ]}
</div>
<div class="col-md-3">
{[ "type": "text", "label": "<?php echo tr('Cellulare'); ?>", "name": "cellulare", "class": "text-center", "value": "$cellulare$", "icon-before": "<i class='fa fa-mobile'></i>" ]}
</div>
<div class="col-md-3">
{[ "type": "text", "label": "<?php echo tr('Email'); ?>", "name": "email", "class": "email-mask", "placeholder":"casella@dominio.ext", "value": "$email$", "icon-before": "<i class='fa fa-envelope'></i>" ]}
</div>
</div>
<?php
if (!empty($google)) {
@ -394,7 +197,7 @@ if (!empty($google)) {
</div>';
// Calcola percorso
if (empty($records[0]['gaddress']) || (empty($records[0]['lat']) && empty($records[0]['lng']))) {
if (empty($record['gaddress']) || (empty($record['lat']) && empty($record['lng']))) {
echo '
<div class="col-md-3">
<label>&nbsp;</label><br>
@ -405,35 +208,429 @@ if (!empty($google)) {
echo '
</div>';
if (!empty($records[0]['gaddress']) || (!empty($records[0]['lat']) && !empty($records[0]['lng']))) {
if (!empty($record['gaddress']) || (!empty($record['lat']) && !empty($record['lng']))) {
echo '
<div id="map" style="height:400px; width:100%"></div>';
}
} else {
echo '
<div class="alert alert-info">
'.Modules::link('Impostazioni', $dbo->fetchArray("SELECT `idimpostazione` FROM `zz_settings` WHERE sezione='Generali'")[0]['idimpostazione'], tr('Per abilitare la visualizzazione delle anagrafiche nella mappa, inserire la Google Maps API Key nella scheda Impostazioni')).'.
'.Modules::link('Impostazioni', $dbo->fetchOne("SELECT `id` FROM `zz_settings` WHERE sezione='Generali'")['id'], tr('Per abilitare la visualizzazione delle anagrafiche nella mappa, inserire la Google Maps API Key nella scheda Impostazioni')).'.
</div>';
}
?>
</div>
</div>
</form>
</div>
</div>
{( "name": "filelist_and_upload", "id_module": "<?php echo $id_module; ?>", "id_record": "<?php echo $id_record; ?>" )}
<?php
if ($cliente || $fornitore) {
?>
<!-- ACQUISTI -->
<div class = "row">
<div class="col-md-6">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title"><?php echo tr('Acquisti'); ?></h3>
</div>
<div class="panel-body">
<div class="row">
<div class="col-md-6">
{[ "type": "select", "label": "<?php echo tr('Pagamento predefinito'); ?>", "name": "idpagamento_acquisti", "values": "query=SELECT id, descrizione FROM co_pagamenti GROUP BY descrizione ORDER BY descrizione ASC", "value": "$idpagamento_acquisti$", "extra": "<?php echo ($fornitore) ? '' : 'readonly'; ?>" ]}
</div>
<div class="col-md-6">
{[ "type": "select", "label": "<?php echo tr('Banca predefinita'); ?>", "name": "idbanca_acquisti", "values": "query=SELECT id, nome AS descrizione FROM co_banche ORDER BY nome ASC", "value": "$idbanca_acquisti$", "extra": "<?php echo ($fornitore) ? '' : 'readonly'; ?>", "icon-after": "add|<?php echo Modules::get('Banche')['id']; ?>|||<?php echo ($fornitore) ? '' : 'disabled'; ?>" ]}
</div>
</div>
<div class="row">
<div class="col-md-6">
{[ "type": "select", "label": "<?php echo tr('Iva predefinita'); ?>", "name": "idiva_acquisti", "ajax-source": "iva", "value": "$idiva_acquisti$", "extra": "<?php echo ($fornitore) ? '' : 'readonly'; ?>" ]}
</div>
<div class="col-md-6">
{[ "type": "select", "label": "<?php echo tr('Ritenuta d\'acconto predefinita'); ?>", "name": "id_ritenuta_acconto_acquisti", "values": "query=SELECT id, descrizione FROM co_ritenutaacconto ORDER BY descrizione ASC", "value": "$id_ritenuta_acconto_acquisti$", "extra": "<?php echo ($fornitore) ? '' : 'readonly'; ?>" ]}
</div>
</div>
<div class="row">
<div class="col-md-6">
{[ "type": "select", "label": "<?php echo tr('Listino articoli'); ?>", "name": "idlistino_acquisti", "values": "query=SELECT id, nome AS descrizione FROM mg_listini ORDER BY nome ASC", "value": "$idlistino_acquisti$", "extra": "<?php echo ($fornitore) ? '' : 'readonly'; ?>" ]}
</div>
<div class="col-md-6">
<?php
if (!str_contains($records[0]['idtipianagrafica'], $id_azienda)) {
// Collegamento con il conto
$conto = $dbo->fetchOne('SELECT co_pianodeiconti2.numero as numero, co_pianodeiconti3.numero as numero_conto, co_pianodeiconti3.descrizione as descrizione FROM co_pianodeiconti3 INNER JOIN co_pianodeiconti2 ON co_pianodeiconti3.idpianodeiconti2=co_pianodeiconti2.id WHERE co_pianodeiconti3.id = '.prepare($record['idconto_fornitore']));
/*echo '
<p>'.tr('Piano dei conti collegato: _NAME_', [
'_NAME_' => $conto['numero'].'.'.$conto['numero_conto'].' '.$conto['descrizione'],
]).Modules::link('Piano dei conti', null, '').'</p>';*/
if (!empty($conto['numero_conto'])) {
$piano_dei_conti_fornitore = tr('_NAME_', [
'_NAME_' => $conto['numero'].'.'.$conto['numero_conto'].' '.$conto['descrizione'],
]);
echo Modules::link('Piano dei conti', null, null, null, 'class="pull-right"');
} else {
$piano_dei_conti_fornitore = tr('Nessuno');
} ?>
{[ "type": "select", "label": "<?php echo tr('Piano dei conti fornitore'); ?>", "name": "piano_dei_conti_fornitore", "values": "list=\"\": \"<?php echo $piano_dei_conti_fornitore; ?>\"", "readonly": 1, "value": "", "extra": "" ]}
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<!-- VENDITE -->
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title"><?php echo tr('Vendite'); ?></h3>
</div>
<div class="panel-body">
<div class="row">
<div class="col-md-6">
{[ "type": "select", "label": "<?php echo tr('Pagamento predefinito'); ?>", "name": "idpagamento_vendite", "values": "query=SELECT id, descrizione FROM co_pagamenti GROUP BY descrizione ORDER BY descrizione ASC", "value": "$idpagamento_vendite$", "extra": "<?php echo ($cliente) ? '' : 'readonly'; ?>" ]}
</div>
<div class="col-md-6">
{[ "type": "select", "label": "<?php echo tr('Banca predefinita'); ?>", "name": "idbanca_vendite", "values": "query=SELECT id, nome AS descrizione FROM co_banche ORDER BY nome ASC", "value": "$idbanca_vendite$", "extra": "<?php echo ($cliente) ? '' : 'readonly'; ?>", "icon-after": "add|<?php echo Modules::get('Banche')['id']; ?>|||<?php echo ($cliente) ? '' : 'disabled'; ?>", "help": "<?php echo tr('Banca predefinita su cui accreditare i pagamenti.'); ?>" ]}
</div>
</div>
<div class="row">
<div class="col-md-6">
{[ "type": "select", "label": "<?php echo tr('Iva predefinita'); ?>", "name": "idiva_vendite", "ajax-source": "iva", "value": "$idiva_vendite$", "extra": "<?php echo ($cliente) ? '' : 'readonly'; ?>" ]}
</div>
<div class="col-md-6">
{[ "type": "select", "label": "<?php echo tr('Ritenuta d\'acconto predefinita'); ?>", "name": "id_ritenuta_acconto_vendite", "values": "query=SELECT id, descrizione FROM co_ritenutaacconto ORDER BY descrizione ASC", "value": "$id_ritenuta_acconto_vendite$", "extra": "<?php echo ($cliente) ? '' : 'readonly'; ?>" ]}
</div>
</div>
<div class="row">
<div class="col-md-6">
{[ "type": "select", "label": "<?php echo tr('Listino articoli'); ?>", "name": "idlistino_vendite", "values": "query=SELECT id, nome AS descrizione FROM mg_listini ORDER BY nome ASC", "value": "$idlistino_vendite$", "extra": "<?php echo ($cliente) ? '' : 'readonly'; ?>" ]}
</div>
<div class="col-md-6">
{[ "type": "select", "label": "<?php echo tr('Indirizzo di fatturazione'); ?>", "name": "idsede_fatturazione", "values": "query=SELECT id, IF(citta = '', nomesede, CONCAT_WS(', ', nomesede, citta)) AS descrizione FROM an_sedi WHERE idanagrafica='<?php echo $id_record; ?>' UNION SELECT '0' AS id, 'Sede legale' AS descrizione ORDER BY descrizione", "value": "$idsede_fatturazione$" , "extra": "<?php echo ($cliente) ? '' : 'readonly'; ?>" ]}
</div>
</div>
<div class="row">
<div class="col-md-6">
{[ "type": "select", "label": "<?php echo tr('Tipo attività predefinita'); ?>", "name": "idtipointervento_default", "values": "query=SELECT idtipointervento AS id, descrizione FROM in_tipiintervento ORDER BY descrizione ASC", "value": "$idtipointervento_default$", "extra": "<?php echo ($cliente) ? '' : 'readonly'; ?>" ]}
</div>
<div class="col-md-6">
{[ "type": "select", "label": "<?php echo tr('Agente principale'); ?>", "name": "idagente", "values": "query=SELECT an_anagrafiche.idanagrafica AS id, IF(deleted_at IS NOT NULL, CONCAT(ragione_sociale, ' (Eliminato)'), ragione_sociale ) AS descrizione FROM an_anagrafiche INNER JOIN (an_tipianagrafiche_anagrafiche INNER JOIN an_tipianagrafiche ON an_tipianagrafiche_anagrafiche.idtipoanagrafica=an_tipianagrafiche.idtipoanagrafica) ON an_anagrafiche.idanagrafica=an_tipianagrafiche_anagrafiche.idanagrafica WHERE (descrizione='Agente' AND deleted_at IS NULL)<?php echo isset($record['idagente']) ? 'OR (an_anagrafiche.idanagrafica = '.prepare($record['idagente']).' AND deleted_at IS NOT NULL) ' : ''; ?>ORDER BY ragione_sociale", "value": "$idagente$", "extra": "<?php echo ($cliente) ? '' : 'readonly'; ?>" ]}
</div>
</div>
<div class="row">
<div class="col-md-6">
<?php
// Collegamento con il conto
$conto = $dbo->fetchOne('SELECT co_pianodeiconti2.numero as numero, co_pianodeiconti3.numero as numero_conto, co_pianodeiconti3.descrizione as descrizione FROM co_pianodeiconti3 INNER JOIN co_pianodeiconti2 ON co_pianodeiconti3.idpianodeiconti2=co_pianodeiconti2.id WHERE co_pianodeiconti3.id = '.prepare($record['idconto_cliente']));
/*echo '
<p>'.tr('Piano dei conti collegato: _NAME_', [
'_NAME_' => $conto['numero'].'.'.$conto['numero_conto'].' '.$conto['descrizione'],
]).Modules::link('Piano dei conti', null, '').'</p>';*/
if (!empty($conto['numero_conto'])) {
$piano_dei_conti_cliente = tr('_NAME_', [
'_NAME_' => $conto['numero'].'.'.$conto['numero_conto'].' '.$conto['descrizione'],
]);
echo Modules::link('Piano dei conti', null, null, null, 'class="pull-right"');
} else {
$piano_dei_conti_cliente = tr('Nessuno');
} ?>
{[ "type": "select", "label": "<?php echo tr('Piano dei conti cliente'); ?>", "name": "piano_dei_conti_cliente", "values": "list=\"\": \"<?php echo $piano_dei_conti_cliente; ?>\"", "readonly": 1, "value": "", "extra": "" ]}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="clearfix" ></div>
<?php
}
?>
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title"><?php echo tr('Informazioni aggiuntive'); ?></h3>
</div>
<div class="panel-body">
<div class="row">
<div class="col-md-3">
{[ "type": "text", "label": "<?php echo tr('Codice registro imprese'); ?>", "name": "codiceri", "value": "$codiceri$" ]}
</div>
<div class="col-md-3">
{[ "type": "text", "label": "<?php echo tr('Codice R.E.A.').'<small>('.tr('provincia-C.C.I.A.A.').')</small>'; ?>", "name": "codicerea", "value": "$codicerea$", "help": "<?php echo tr('Formato: _PATTERN_', [
'_PATTERN_' => 'RM-123456',
]); ?>" ]}
</div>
<div class="col-md-3">
{[ "type": "text", "label": "<?php echo tr('Num. iscr. C.C.I.A.A.'); ?>", "name": "cciaa", "value": "$cciaa$" ]}
</div>
<div class="col-md-3">
{[ "type": "text", "label": "<?php echo tr('Città iscr. C.C.I.A.A.'); ?>", "name": "cciaa_citta", "value": "$cciaa_citta$" ]}
</div>
</div>
<div class="row">
<div class="col-md-3">
{[ "type": "text", "label": "<?php echo tr('Num. iscr. tribunale'); ?>", "name": "iscrizione_tribunale", "value": "$iscrizione_tribunale$" ]}
</div>
<div class="col-md-3">
{[ "type": "text", "label": "<?php echo tr('Num. iscr. albo artigiani'); ?>", "name": "n_alboartigiani", "value": "$n_alboartigiani$" ]}
</div>
<div class="col-md-3">
{[ "type": "text", "label": "<?php echo tr('Foro di competenza'); ?>", "name": "foro_competenza", "value": "$foro_competenza$" ]}
</div>
<div class="col-md-3">
{[ "type": "text", "label": "<?php echo tr('Capitale sociale'); ?>", "name": "capitale_sociale", "value": "$capitale_sociale$" ]}
</div>
</div>
<?php
//se non è l'anagrafica azienda, ma cliente o fornitore
if (!in_array($id_azienda, $tipi_anagrafica) and (($cliente or $fornitore))) {
?>
<div class="row">
<div class="col-md-3">
{[ "type": "text", "label": "<?php echo tr('Appoggio bancario'); ?>", "name": "appoggiobancario", "value": "$appoggiobancario$" ]}
</div>
<div class="col-md-3">
{[ "type": "text", "label": "<?php echo tr('Filiale banca'); ?>", "name": "filiale", "value": "$filiale$" ]}
</div>
<div class="col-md-3">
{[ "type": "text", "label": "<?php echo tr('Codice IBAN'); ?>", "name": "codiceiban", "value": "$codiceiban$" ]}
</div>
<div class="col-md-3">
{[ "type": "text", "label": "<?php echo tr('Codice BIC'); ?>", "name": "bic", "value": "$bic$" ]}
</div>
</div>
<div class="row">
<div class="col-md-3">
{[ "type": "checkbox", "label": "<?php echo tr('Abilitare lo split payment'); ?>", "name": "split_payment", "value": "$split_payment$", "help": "<?php echo tr('Lo split payment è disponibile per le anagrafiche di tipologia \"Ente pubblico\" o \"Azienda\" ed <strong>&egrave; obbligatorio</strong> per:<ul><li>Stato;</li><li>organi statali ancorch&eacute; dotati di personalit&agrave; giuridica;</li><li>enti pubblici territoriali e dei consorzi tra essi costituiti;</li><li>Camere di Commercio;</li><li>Istituti universitari;</li><li>ASL e degli enti ospedalieri;</li><li>enti pubblici di ricovero e cura aventi prevalente carattere scientifico;</li><li>enti pubblici di assistenza e beneficienza;</li><li>enti di previdenza;</li><li>consorzi tra questi costituiti.</li></ul>'); ?>", "placeholder": "<?php echo tr('Split payment'); ?>", "extra" : "<?php echo ($record['tipo'] == 'Ente pubblico' or $record['tipo'] == 'Azienda') ? '' : 'disabled'; ?>" ]}
</div>
<div class="col-md-9">
{[ "type": "text", "label": "<?php echo tr('Dicitura fissa in fattura'); ?>", "name": "diciturafissafattura", "value": "$diciturafissafattura$" ]}
</div>
</div>
<?php
}
?>
<div class="row">
<div class="col-md-3">
{[ "type": "text", "label": "<?php echo tr('Settore merceologico'); ?>", "name": "settore", "value": "$settore$" ]}
</div>
<div class="col-md-3">
{[ "type": "text", "label": "<?php echo tr('Marche trattate'); ?>", "name": "marche", "value": "$marche$" ]}
</div>
<div class="col-md-3">
{[ "type": "number", "label": "<?php echo tr('Num. dipendenti'); ?>", "name": "dipendenti", "decimals": 0, "value": "$dipendenti$" ]}
</div>
<div class="col-md-3">
{[ "type": "number", "label": "<?php echo tr('Num. macchine'); ?>", "name": "macchine", "decimals": 0, "value": "$macchine$" ]}
</div>
</div>
<div class="row">
<div class="col-md-12">
{[ "type": "select", "multiple": "1", "label": "<?php echo tr('Tipo di anagrafica'); ?>", "name": "idtipoanagrafica[]", "values": "query=SELECT idtipoanagrafica AS id, descrizione FROM an_tipianagrafiche WHERE idtipoanagrafica NOT IN (SELECT DISTINCT(x.idtipoanagrafica) FROM an_tipianagrafiche_anagrafiche x INNER JOIN an_tipianagrafiche t ON x.idtipoanagrafica = t.idtipoanagrafica INNER JOIN an_anagrafiche ON an_anagrafiche.idanagrafica = x.idanagrafica WHERE t.descrizione = 'Azienda' AND deleted_at IS NULL) ORDER BY descrizione", "value": "$idtipianagrafica$" ]}
<?php
if (in_array($id_azienda, $tipi_anagrafica)) {
echo '
<p class=\'badge badge-info\' >'.tr('Questa anagrafica &egrave; di tipo "Azienda"').'.</p>';
}
?>
</div>
</div>
<div class="row">
<?php
if (in_array('Tecnico', explode(',', $record['tipianagrafica']))) {
?>
<div class="col-md-3">
{[ "type": "text", "label": "<?php echo tr('Colore'); ?>", "name": "colore", "class": "colorpicker text-center", "value": "$colore$", "extra": "maxlength='7'", "icon-after": "<div class='img-circle square'></div>" ]}
</div>
<?php
} ?>
<?php
if (in_array('Cliente', explode(',', $record['tipianagrafica']))) {
?>
<div class="col-md-6">
{[ "type": "select", "label": "Agenti secondari", "multiple": "1", "name": "idagenti[]", "values": "query=SELECT an_anagrafiche.idanagrafica AS id, IF(deleted_at IS NOT NULL, CONCAT(ragione_sociale, ' (Eliminato)'), ragione_sociale ) AS descrizione FROM an_anagrafiche INNER JOIN (an_tipianagrafiche_anagrafiche INNER JOIN an_tipianagrafiche ON an_tipianagrafiche_anagrafiche.idtipoanagrafica=an_tipianagrafiche.idtipoanagrafica) ON an_anagrafiche.idanagrafica=an_tipianagrafiche_anagrafiche.idanagrafica WHERE (descrizione='Agente' AND deleted_at IS NULL AND an_anagrafiche.idanagrafica NOT IN (SELECT idagente FROM an_anagrafiche WHERE idanagrafica = <?php echo prepare($record['idanagrafica']); ?> )) OR (an_anagrafiche.idanagrafica IN (SELECT idagente FROM an_anagrafiche_agenti WHERE idanagrafica = <?php echo prepare($record['idanagrafica']); ?> ) ) ORDER BY ragione_sociale", "value": "$idagenti$" ]}
</div>
<div class="col-md-3">
{[ "type": "select", "label": "<?php echo tr('Relazione con il cliente'); ?>", "name": "idrelazione", "values": "query=SELECT id, descrizione, colore AS _bgcolor_ FROM an_relazioni ORDER BY descrizione", "value": "$idrelazione$" ]}
</div>
<?php
} ?>
</div>
<div class="row">
<div class="col-md-12">
{[ "type": "textarea", "label": "<?php echo tr('Note'); ?>", "name": "note", "value": "$note$" ]}
</div>
</div>
</div>
</div>
</fieldset>
</form>
{( "name": "filelist_and_upload", "id_module": "$id_module$", "id_record": "$id_record$" )}
<?php
if (setting('Azienda predefinita') == $id_record) {
echo '
<div class="alert alert-info text-center">'.tr('Per impostare il logo delle stampe, caricare un file ".jpg" specificando come nome dell\'allegato "Logo stampe" (Risoluzione consigliata 302x111 pixel)').'.</div>';
}
// Collegamenti diretti
// Fatture, ddt, preventivi, contratti, ordini, interventi, utenti collegati a questa anagrafica
$elementi = $dbo->fetchArray('SELECT `co_documenti`.`id`, `co_documenti`.`data`, `co_documenti`.`numero`, `co_documenti`.`numero_esterno`, `co_tipidocumento`.`descrizione` AS tipo_documento, `co_tipidocumento`.`dir` FROM `co_documenti` JOIN `co_tipidocumento` ON `co_tipidocumento`.`id` = `co_documenti`.`idtipodocumento` WHERE `co_documenti`.`idanagrafica` = '.prepare($id_record).'
UNION
SELECT `zz_users`.`id`, `zz_users`.`created_at` AS data, `zz_users`.`username` AS numero, 0 AS `numero_esterno`, "Utente" AS tipo_documento, 0 AS `dir` FROM `zz_users` WHERE `zz_users`.`idanagrafica` = '.prepare($id_record).'
UNION
SELECT `or_ordini`.`id`, `or_ordini`.`data`, `or_ordini`.`numero`, `or_ordini`.`numero_esterno`, `or_tipiordine`.`descrizione` AS tipo_documento, `or_tipiordine`.`dir` FROM `or_ordini` JOIN `or_tipiordine` ON `or_tipiordine`.`id` = `or_ordini`.`idtipoordine` WHERE `or_ordini`.`idanagrafica` = '.prepare($id_record).'
UNION
SELECT `dt_ddt`.`id`, `dt_ddt`.`data`, `dt_ddt`.`numero`, `dt_ddt`.`numero_esterno`, `dt_tipiddt`.`descrizione` AS tipo_documento, `dt_tipiddt`.`dir` FROM `dt_ddt` JOIN `dt_tipiddt` ON `dt_tipiddt`.`id` = `dt_ddt`.`idtipoddt` WHERE `dt_ddt`.`idanagrafica` = '.prepare($id_record).'
UNION
SELECT `in_interventi`.`id`, `in_interventi`.`data_richiesta`, `in_interventi`.`codice` AS numero, 0 AS numero_esterno, "Intervento" AS tipo_documento, 0 AS dir FROM `in_interventi` LEFT JOIN `in_interventi_tecnici` ON `in_interventi`.`id` = `in_interventi_tecnici`.`idintervento` WHERE `in_interventi`.`id` IN (SELECT `idintervento` FROM `in_interventi_tecnici` WHERE `idtecnico` = '.prepare($id_record).') OR `in_interventi`.`idanagrafica` = '.prepare($id_record).'
UNION
SELECT `co_contratti`.`id`, `co_contratti`.`data_bozza`, `co_contratti`.`numero`, 0 AS numero_esterno , "Contratto" AS tipo_documento, 0 AS dir FROM `co_contratti` WHERE `co_contratti`.`idanagrafica` = '.prepare($id_record).'
UNION
SELECT `co_preventivi`.`id`, `co_preventivi`.`data_bozza`, `co_preventivi`.`numero`, 0 AS numero_esterno , "Preventivo" AS tipo_documento, 0 AS dir FROM `co_preventivi` WHERE `co_preventivi`.`idanagrafica` = '.prepare($id_record).'
ORDER BY `data`');
if (!empty($elementi)) {
echo '
<div class="box box-warning collapsable collapsed-box">
<div class="box-header with-border">
<h3 class="box-title"><i class="fa fa-warning"></i> '.tr('Documenti collegati: _NUM_', [
'_NUM_' => count($elementi),
]).'</h3>
<div class="box-tools pull-right">
<button type="button" class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-plus"></i></button>
</div>
</div>
<div class="box-body">
<ul>';
foreach ($elementi as $elemento) {
$descrizione = tr('_DOC_ _NUM_ del _DATE_', [
'_DOC_' => $elemento['tipo_documento'],
'_NUM_' => !empty($elemento['numero_esterno']) ? $elemento['numero_esterno'] : $elemento['numero'],
'_DATE_' => Translator::dateToLocale($elemento['data']),
]);
//se non è un preventivo è un ddt o una fattura
//se non è un ddt è una fattura.
if (in_array($elemento['tipo_documento'], ['Utente'])) {
$modulo = 'Utenti e permessi';
} elseif (in_array($elemento['tipo_documento'], ['Intervento'])) {
$modulo = 'Interventi';
} elseif (in_array($elemento['tipo_documento'], ['Preventivo'])) {
$modulo = 'Preventivi';
} elseif (in_array($elemento['tipo_documento'], ['Contratto'])) {
$modulo = 'Contratti';
} elseif (in_array($elemento['tipo_documento'], ['Ordine cliente', 'Ordine fornitore'])) {
$modulo = ($elemento['dir'] == 'entrata') ? 'Ordini cliente' : 'Ordini fornitore';
} elseif (in_array($elemento['tipo_documento'], ['Ddt di vendita', 'Ddt di acquisto'])) {
$modulo = ($elemento['dir'] == 'entrata') ? 'Ddt di vendita' : 'Ddt di acquisto';
} else {
$modulo = ($elemento['dir'] == 'entrata') ? 'Fatture di vendita' : 'Fatture di acquisto';
}
$id = $elemento['id'];
echo '
<li>'.Modules::link($modulo, $id, $descrizione).'</li>';
}
echo '
</ul>
</div>
</div>';
}
if (empty($record['deleted_at'])) {
if (!in_array($id_azienda, $tipi_anagrafica)) {
if (!empty($elementi)) {
echo '
<div class="alert alert-error">
'.tr('Eliminando questo documento si potrebbero verificare problemi nelle altre sezioni del gestionale').'.
</div>';
}
echo '
<a class="btn btn-danger ask" data-backto="record-list">
<i class="fa fa-trash"></i> '.tr('Elimina').'
</a>';
}else{
echo '
<div class=\'alert alert-warning\' >'.tr('Questa è l\'anagrafica "Azienda" e non è possibile eliminarla').'.</div>';
} else {
echo '
<div class="alert alert-warning">'.tr('Questa è l\'anagrafica "Azienda" e non è possibile eliminarla').'.</div>';
}
} else {
echo '
<div class="alert alert-danger">'.tr('Questa anagrafica è stata eliminata').'.</div>';
}
?>
<script>
@ -453,6 +650,42 @@ if (!str_contains($records[0]['idtipianagrafica'], $id_azienda)) {
$("#lat").val(result.geometry.location.lat());
$("#lng").val(result.geometry.location.lng());
});
/* Campo nome e cognome*/
if ($('#ragione_sociale').val()!='' && $('#ragione_sociale').val() != $('#nome').val()+' '+$('#cognome').val()){
$('#nome').prop('disabled', true);
$('#cognome').prop('disabled', true);
};
if ($('#nome').val()!='' && $('#cognome').val()!=''){
$('#ragione_sociale').prop('disabled', true);
$("#ragione_sociale").attr('required', false);
};
$('#nome, #cognome').keyup(function(){
if ($('#nome').val() =='' && $('#cognome').val() =='' ){
$('#ragione_sociale').prop('disabled', false);
$("#ragione_sociale").attr('required', true);
}else{
$('#ragione_sociale').prop('disabled', true);
$("#ragione_sociale").attr('required', false);
}
});
$('#ragione_sociale').keyup(function(){
if ($(this).val()!=''){
$('#nome').prop('disabled', true);
$('#cognome').prop('disabled', true);
$("#ragione_sociale").attr('required', true);
}else{
$('#nome').prop('disabled', false);
$('#cognome').prop('disabled', false);
$("#ragione_sociale").attr('required', false);
}
});
});
</script>

View File

@ -2,43 +2,81 @@
include_once __DIR__.'/../../core.php';
use Modules\Anagrafiche\Anagrafica;
switch (post('op')) {
case 'example':
$module = filter('module');
$list = [
['Codice', 'Ragione sociale', 'Partita IVA', 'Nazione', 'Indirizzo', 'CAP', 'Città', 'Provincia', 'Telefono', 'Fax', 'Cellulare', 'Email', 'IBAN', 'Note', 'Tipologia'],
['00001', 'Cliente', '12345678910', 'ITALIA', 'Via Giuseppe Mazzini, 123', '12345', 'Este', 'PD', '786 543 21', '123 456 78', '321 123 456 78', 'email@cliente.it', 'IT60 X054 2811 1010 0000 0123 456', 'Anagrafica di esempio', 'Cliente'],
];
directory('../../files/'.$module);
$fp = fopen('../../files/'.$module.'/'.$module.'.csv', 'w');
fprintf($fp, chr(0xEF).chr(0xBB).chr(0xBF));
foreach ($list as $fields) {
fputcsv($fp, $fields, ';');
}
fclose($fp);
exit;
break;
case 'import':
$sede_fields = [
'piva',
'codice_fiscale',
'indirizzo',
'indirizzo2',
'citta',
'cap',
'provincia',
'km',
'id_nazione',
'telefono',
'fax',
'cellulare',
'email',
'idzona',
'gaddress',
'lat',
'lng',
];
foreach ($data as $key => $value) {
if (!empty($value)) {
$idtipoanagrafica = (array) $data[$key]['tipologia'];
$id_tipo_anagrafica = (array) $data[$key]['tipologia'];
unset($data[$key]['tipologia']);
// Insert o update
$insert = true;
$dati_anagrafica = $data[$key];
$dati_sede = [];
foreach ($sede_fields as $field) {
$dati_sede[$field] = $dati_anagrafica[$field];
unset($dati_anagrafica[$field]);
}
// Ricerca di eventuale anagrafica corrispondente
if (!empty($primary_key)) {
$rs = $dbo->select('an_anagrafiche', $primary_key, [
$primary_key => $data[$key][$primary_key],
]);
$insert = !in_array($data[$key][$primary_key], $rs[0]);
$anagrafica = Anagrafica::where($primary_key, '=', $dati_anagrafica[$primary_key])->first();
}
// Insert
if ($insert) {
$dbo->insert('an_anagrafiche', $data[$key]);
// Campi extra
if (count($idtipoanagrafica) > 0) {
// Aggiornamento della tipologia di anagrafiche
$dbo->sync('an_tipianagrafiche_anagrafiche', [
'idanagrafica' => $dbo->lastInsertedID(),
], [
'idtipoanagrafica' => (array) $idtipoanagrafica,
]);
}
if (empty($anagrafica)) {
$anagrafica = Anagrafica::build($dati_anagrafica['ragione_sociale']);
}
// Update
else {
$dbo->update('an_anagrafiche', $data[$key], [$primary_key => $data[$key][$primary_key]]);
}
$anagrafica->fill($dati_anagrafica);
$anagrafica->tipologie = (array) $id_tipo_anagrafica;
$anagrafica->save();
$sede = $anagrafica->sedeLegale;
$sede->fill($dati_sede);
$sede->save();
unset($data[$key]);
}
@ -73,6 +111,10 @@ return [
'field' => 'indirizzo',
'label' => 'Indirizzo',
],
[
'field' => 'indirizzo2',
'label' => 'Civico',
],
[
'field' => 'cap',
'label' => 'CAP',
@ -93,6 +135,18 @@ return [
'field' => 'codice_fiscale',
'label' => 'Codice Fiscale',
],
[
'field' => 'data_nascita',
'label' => 'Data di nascita',
],
[
'field' => 'luogo_nascita',
'label' => 'Luogo di nascita',
],
[
'field' => 'sesso',
'label' => 'Sesso',
],
[
'field' => 'piva',
'label' => 'Partita IVA',

View File

@ -2,12 +2,28 @@
include_once __DIR__.'/../../core.php';
use Modules\Anagrafiche\Anagrafica;
$id_azienda = $dbo->fetchArray("SELECT idtipoanagrafica FROM an_tipianagrafiche WHERE descrizione='Azienda'")[0]['idtipoanagrafica'];
$id_cliente = $dbo->fetchArray("SELECT idtipoanagrafica FROM an_tipianagrafiche WHERE descrizione='Cliente'")[0]['idtipoanagrafica'];
$id_fornitore = $dbo->fetchArray("SELECT idtipoanagrafica FROM an_tipianagrafiche WHERE descrizione='Fornitore'")[0]['idtipoanagrafica'];
$id_tecnico = $dbo->fetchArray("SELECT idtipoanagrafica FROM an_tipianagrafiche WHERE descrizione='Tecnico'")[0]['idtipoanagrafica'];
if (isset($id_record)) {
$records = $dbo->fetchArray('SELECT *, (SELECT GROUP_CONCAT(an_tipianagrafiche.idtipoanagrafica) FROM an_tipianagrafiche INNER JOIN an_tipianagrafiche_anagrafiche ON an_tipianagrafiche.idtipoanagrafica=an_tipianagrafiche_anagrafiche.idtipoanagrafica WHERE idanagrafica=an_anagrafiche.idanagrafica) AS idtipianagrafica, (SELECT GROUP_CONCAT(idagente) FROM an_anagrafiche_agenti WHERE idanagrafica=an_anagrafiche.idanagrafica) AS idagenti, (SELECT GROUP_CONCAT(descrizione) FROM an_tipianagrafiche INNER JOIN an_tipianagrafiche_anagrafiche ON an_tipianagrafiche.idtipoanagrafica=an_tipianagrafiche_anagrafiche.idtipoanagrafica WHERE idanagrafica=an_anagrafiche.idanagrafica) AS tipianagrafica FROM an_anagrafiche GROUP BY idanagrafica HAVING idanagrafica='.prepare($id_record).' '.Modules::getAdditionalsQuery($id_module));
$anagrafica = Anagrafica::find($id_record);
$record = $dbo->fetchOne('SELECT *,
(SELECT GROUP_CONCAT(an_tipianagrafiche.idtipoanagrafica) FROM an_tipianagrafiche INNER JOIN an_tipianagrafiche_anagrafiche ON an_tipianagrafiche.idtipoanagrafica=an_tipianagrafiche_anagrafiche.idtipoanagrafica WHERE idanagrafica=an_anagrafiche.idanagrafica) AS idtipianagrafica,
(SELECT GROUP_CONCAT(idagente) FROM an_anagrafiche_agenti WHERE idanagrafica=an_anagrafiche.idanagrafica) AS idagenti,
(SELECT GROUP_CONCAT(descrizione) FROM an_tipianagrafiche INNER JOIN an_tipianagrafiche_anagrafiche ON an_tipianagrafiche.idtipoanagrafica=an_tipianagrafiche_anagrafiche.idtipoanagrafica WHERE idanagrafica=an_anagrafiche.idanagrafica) AS tipianagrafica
FROM an_anagrafiche WHERE idanagrafica='.prepare($id_record).' '.Modules::getAdditionalsQuery($id_module));
// Cast per latitudine e longitudine
if (!empty($records)) {
$records[0]['lat'] = floatval($records[0]['lat']);
$records[0]['lng'] = floatval($records[0]['lng']);
if (!empty($record)) {
$record['lat'] = floatval($record['lat']);
$record['lng'] = floatval($record['lng']);
}
$tipi_anagrafica = $dbo->fetchArray('SELECT an_tipianagrafiche.idtipoanagrafica FROM an_tipianagrafiche INNER JOIN an_tipianagrafiche_anagrafiche ON an_tipianagrafiche.idtipoanagrafica=an_tipianagrafiche_anagrafiche.idtipoanagrafica WHERE idanagrafica='.prepare($id_record));
$tipi_anagrafica = array_column($tipi_anagrafica, 'idtipoanagrafica');
}

View File

@ -3,8 +3,14 @@
include_once __DIR__.'/../../../core.php';
// Interventi
$rsi = $dbo->fetchArray('SELECT ragione_sociale, (SELECT MIN(orario_inizio) FROM in_interventi_tecnici WHERE idintervento=in_interventi.id) AS data, (SELECT SUM(prezzo_ore_consuntivo+prezzo_km_consuntivo+prezzo_dirittochiamata) FROM in_interventi_tecnici WHERE idintervento=in_interventi.id) AS totale FROM in_interventi INNER JOIN an_anagrafiche ON in_interventi.idanagrafica=an_anagrafiche.idanagrafica WHERE in_interventi.idanagrafica='.prepare($id_record));
$rsi = [];
if (in_array('Cliente', explode(',', $record['tipianagrafica']))) {
//Clienti
$rsi = $dbo->fetchArray('SELECT ragione_sociale, (SELECT MIN(orario_inizio) FROM in_interventi_tecnici WHERE idintervento=in_interventi.id) AS data, (SELECT SUM(prezzo_ore_consuntivo+prezzo_km_consuntivo+prezzo_dirittochiamata) FROM in_interventi_tecnici WHERE idintervento=in_interventi.id) AS totale FROM in_interventi INNER JOIN an_anagrafiche ON in_interventi.idanagrafica=an_anagrafiche.idanagrafica WHERE in_interventi.idanagrafica='.prepare($id_record));
} elseif (in_array('Tecnico', explode(',', $record['tipianagrafica']))) {
//Tecnici
$rsi = $dbo->fetchArray('SELECT ragione_sociale, (SELECT MIN(orario_inizio) FROM in_interventi_tecnici WHERE idintervento=in_interventi.id) AS data, (SELECT SUM(prezzo_ore_consuntivo+prezzo_km_consuntivo+prezzo_dirittochiamata) FROM in_interventi_tecnici WHERE idintervento=in_interventi.id AND in_interventi_tecnici.idtecnico = '.prepare($id_record).' ) AS totale FROM in_interventi INNER JOIN an_anagrafiche ON in_interventi.idanagrafica=an_anagrafiche.idanagrafica INNER JOIN in_interventi_tecnici ON in_interventi.id = in_interventi_tecnici.idintervento WHERE in_interventi_tecnici.idtecnico='.prepare($id_record));
}
$totale_interventi = 0;
$data_start = strtotime('now');
@ -42,19 +48,19 @@ echo '
</div>';
// Preventivi
$rsi = $dbo->fetchArray('SELECT data_accettazione AS data, ragione_sociale, budget FROM co_preventivi INNER JOIN an_anagrafiche ON co_preventivi.idanagrafica=an_anagrafiche.idanagrafica WHERE co_preventivi.idanagrafica='.prepare($id_record));
$rsi = $dbo->fetchArray('SELECT co_preventivi.id AS idpreventivo, data_accettazione AS data, ragione_sociale, budget FROM co_preventivi INNER JOIN an_anagrafiche ON co_preventivi.idanagrafica=an_anagrafiche.idanagrafica WHERE co_preventivi.idanagrafica='.prepare($id_record).' AND default_revision = 1');
$totale_preventivi = 0;
$data_start = strtotime('now');
for ($i = 0; $i < count($rsi); ++$i) {
$totale_preventivi += $rsi[$i]['budget'];
//$totale_preventivi += $rsi[$i]['budget'];
$totale_preventivi += get_imponibile_preventivo($rsi[$i]['idpreventivo']);
// Calcolo data più bassa per la ricerca
if (strtotime($rsi[$i]['data']) < $data_start) {
$data_start = strtotime($rsi[$i]['data']);
}
}
echo '
<div class="col-md-6">
<div class="box box-info">
@ -64,7 +70,7 @@ echo '
<div class="box-body">';
if (count($rsi) > 0) {
echo '
<p>'.tr('Si è lavorato per <strong>_NUMBER_ preventivi</strong> per un totale di _EUR_ &euro;', [
<p>'.tr('Sono stati fatti <strong>_NUMBER_ preventivi</strong> per un totale di _EUR_ &euro;', [
'_NUMBER_' => count($rsi),
'_EUR_' => Translator::numberToLocale($totale_preventivi),
]).'</p>
@ -81,7 +87,7 @@ echo '
</div>';
// Contratti
$rsi = $dbo->fetchArray('SELECT data_accettazione AS data, ragione_sociale, budget FROM co_contratti INNER JOIN an_anagrafiche ON co_contratti.idanagrafica=an_anagrafiche.idanagrafica WHERE co_contratti.idanagrafica='.prepare($id_record));
$rsi = $dbo->fetchArray('SELECT data_accettazione AS data, ragione_sociale, (SELECT SUM(co_righe_contratti.subtotale - co_righe_contratti.sconto) FROM co_righe_contratti WHERE co_righe_contratti.idcontratto = co_contratti.id) AS budget FROM co_contratti INNER JOIN an_anagrafiche ON co_contratti.idanagrafica=an_anagrafiche.idanagrafica WHERE co_contratti.idanagrafica='.prepare($id_record));
$totale_contratti = 0;
$data_start = strtotime(date('Ymd'));
@ -104,7 +110,7 @@ echo '
<div class="box-body">';
if (count($rsi) > 0) {
echo '
<p>'.tr('Si è lavorato per <strong>_NUMBER_ contratti</strong> per un totale di _EUR_ &euro;', [
<p>'.tr('Sono stati stipulati <strong>_NUMBER_ contratti</strong> per un totale di _EUR_ &euro;', [
'_NUMBER_' => count($rsi),
'_EUR_' => Translator::numberToLocale($totale_contratti),
]).'</p>

View File

@ -0,0 +1,221 @@
<?php
namespace Modules\Anagrafiche;
use Common\Model;
use Modules\Fatture\Fattura;
use Settings;
use Traits\RecordTrait;
use Util\Generator;
class Anagrafica extends Model
{
use RecordTrait;
protected $table = 'an_anagrafiche';
protected $primaryKey = 'idanagrafica';
protected $module = 'Anagrafiche';
protected $guarded = [];
protected $appends = [
'id',
'partita_iva',
];
protected $hidden = [
'idanagrafica',
'piva',
];
/**
* Crea una nuova anagrafica.
*
* @param string $ragione_sociale
* @param array $tipologie
*
* @return self
*/
public static function build($ragione_sociale, array $tipologie = [])
{
$model = parent::build();
$model->ragione_sociale = $ragione_sociale;
$ultimo = database()->fetchOne('SELECT codice FROM an_anagrafiche ORDER BY CAST(codice AS SIGNED) DESC LIMIT 1');
$codice = Generator::generate(setting('Formato codice anagrafica'), $ultimo['codice']);
$model->codice = $codice;
$model->save();
$model->tipologie = $tipologie;
$model->save();
return $model;
}
public static function fixAzienda(Anagrafica $anagrafica)
{
Settings::setValue('Azienda predefinita', $anagrafica->id);
}
public static function fixCliente(Anagrafica $anagrafica)
{
$database = database();
// Creo il relativo conto nel partitario se non esiste
if (empty($anagrafica->idconto_cliente)) {
// Calcolo prossimo numero cliente
$rs = $database->fetchArray("SELECT MAX(CAST(co_pianodeiconti3.numero AS UNSIGNED)) AS max_numero FROM co_pianodeiconti3 INNER JOIN co_pianodeiconti2 ON co_pianodeiconti3.idpianodeiconti2=co_pianodeiconti2.id WHERE co_pianodeiconti2.descrizione='Crediti clienti e crediti diversi'");
$new_numero = $rs[0]['max_numero'] + 1;
$new_numero = str_pad($new_numero, 6, '0', STR_PAD_LEFT);
$database->query('INSERT INTO co_pianodeiconti3(numero, descrizione, idpianodeiconti2, can_delete, can_edit) VALUES('.prepare($new_numero).', '.prepare(post('ragione_sociale')).", (SELECT id FROM co_pianodeiconti2 WHERE descrizione='Crediti clienti e crediti diversi'), 1, 1)");
$idconto = $database->lastInsertedID();
// Collegamento conto
$anagrafica->idconto_cliente = $idconto;
$anagrafica->save();
}
}
public static function fixFornitore(Anagrafica $anagrafica)
{
$database = database();
// Creo il relativo conto nel partitario se non esiste
if (empty($anagrafica->idconto_fornitore)) {
// Calcolo prossimo numero cliente
$rs = $database->fetchArray("SELECT MAX(CAST(co_pianodeiconti3.numero AS UNSIGNED)) AS max_numero FROM co_pianodeiconti3 INNER JOIN co_pianodeiconti2 ON co_pianodeiconti3.idpianodeiconti2=co_pianodeiconti2.id WHERE co_pianodeiconti2.descrizione='Debiti fornitori e debiti diversi'");
$new_numero = $rs[0]['max_numero'] + 1;
$new_numero = str_pad($new_numero, 6, '0', STR_PAD_LEFT);
$database->query('INSERT INTO co_pianodeiconti3(numero, descrizione, idpianodeiconti2, can_delete, can_edit) VALUES('.prepare($new_numero).', '.prepare(post('ragione_sociale')).", (SELECT id FROM co_pianodeiconti2 WHERE descrizione='Debiti fornitori e debiti diversi'), 1, 1)");
$idconto = $database->lastInsertedID();
// Collegamento conto
$anagrafica->idconto_fornitore = $idconto;
$anagrafica->save();
}
}
public static function fixTecnico(Anagrafica $anagrafica)
{
// Copio già le tariffe per le varie attività
$result = database()->query('INSERT INTO in_tariffe(idtecnico, idtipointervento, costo_ore, costo_km, costo_dirittochiamata, costo_ore_tecnico, costo_km_tecnico, costo_dirittochiamata_tecnico) SELECT '.prepare($model->id).', idtipointervento, costo_orario, costo_km, costo_diritto_chiamata, costo_orario_tecnico, costo_km_tecnico, costo_diritto_chiamata_tecnico FROM in_tipiintervento');
if (!$result) {
flash()->error(tr("Errore durante l'importazione tariffe!"));
}
}
/**
* Aggiorna la tipologia dell'anagrafica.
*
* @param array $tipologie
*/
public function setTipologieAttribute(array $tipologie)
{
if ($this->isAzienda()) {
$tipologie[] = Tipo::where('descrizione', 'Azienda')->first()->id;
}
$tipologie = array_clean($tipologie);
$previous = $this->tipi()->get();
$this->tipi()->sync($tipologie);
$actual = $this->tipi()->get();
$diff = $actual->diff($previous);
foreach ($diff as $tipo) {
$method = 'fix'.$tipo->descrizione;
if (method_exists($this, $method)) {
self::$method($this);
}
}
}
/**
* Controlla se l'anagrafica è di tipo 'Azienda'.
*
* @return bool
*/
public function isAzienda()
{
return $this->tipi()->get()->search(function ($item, $key) {
return $item->descrizione == 'Azienda';
}) !== false;
}
/**
* Restituisce l'identificativo.
*
* @return int
*/
public function getIdAttribute()
{
return $this->idanagrafica;
}
public function setCodiceAttribute($value)
{
if (self::where([
['codice', $value],
[$this->primaryKey, '<>', $this->id],
])->count() == 0) {
$this->attributes['codice'] = $value;
}
}
public function getPartitaIvaAttribute()
{
return $this->piva;
}
public function setPartitaIvaAttribute($value)
{
$this->attributes['piva'] = trim(strtoupper($value));
}
public function setCodiceFiscaleAttribute($value)
{
$this->attributes['codice_fiscale'] = trim(strtoupper($value));
}
public function setCodiceDestinatarioAttribute($value)
{
if (empty($this->tipo) || $this->tipo == 'Privato' || in_array($value, ['999999', '0000000']) || $this->sedeLegale->nazione->iso2 != 'IT') {
$codice_destinatario = '';
} else {
$codice_destinatario = $value;
}
$this->attributes['codice_destinatario'] = trim(strtoupper($codice_destinatario));
}
public function tipi()
{
return $this->belongsToMany(Tipo::class, 'an_tipianagrafiche_anagrafiche', 'idanagrafica', 'idtipoanagrafica');
}
public function fatture()
{
return $this->hasMany(Fattura::class, 'idanagrafica');
}
public function nazione()
{
return $this->belongsTo(Nazione::class, 'id_nazione');
}
/**
* Restituisce la sede legale collegata.
*
* @return self
*/
public function getSedeLegaleAttribute()
{
return $this;
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace Modules\Anagrafiche;
use Common\Model;
class Nazione extends Model
{
protected $table = 'an_nazioni';
public function anagrafiche()
{
return $this->hasMany(Anagrafica::class, 'id_nazione');
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace Modules\Anagrafiche;
use Common\Model;
class Tipo extends Model
{
protected $table = 'an_tipianagrafiche';
protected $primaryKey = 'idtipoanagrafica';
protected $appends = [
'id',
];
protected $hidden = [
'idtipoanagrafica',
];
/**
* Restituisce l'identificativo.
*
* @return int
*/
public function getIdAttribute()
{
return $this->idtipoanagrafica;
}
public function anagrafiche()
{
return $this->hasMany(Anagrafica::class, 'an_tipianagrafiche_anagrafiche', 'idtipoanagrafica', 'idanagrafica');
}
}

View File

@ -3,66 +3,71 @@
include_once __DIR__.'/../../core.php';
switch (post('op')) {
case 'update':
// Aggiunta articolo
case 'add':
$codice = post('codice');
$descrizione = post('descrizione');
$um = post('um');
$categoria = post('categoria');
$subcategoria = post('subcategoria');
// Inserisco l'articolo e avviso se esiste un altro articolo con stesso codice.
if ($dbo->fetchNum('SELECT * FROM mg_articoli WHERE codice='.prepare($codice)) == 1) {
flash()->warning(tr('Esiste già un articolo con questo codice'));
}
$dbo->insert('mg_articoli', [
'codice' => $codice,
'descrizione' => post('descrizione'),
'id_categoria' => post('categoria'),
'id_sottocategoria' => post('subcategoria'),
'attivo' => 1,
]);
$id_record = $dbo->lastInsertedID();
if (isAjaxRequest()) {
echo json_encode(['id' => $id_record, 'text' => post('descrizione')]);
}
flash()->info(tr('Aggiunto un nuovo articolo'));
break;
// Modifica articolo
case 'update':
$componente = post('componente_filename');
$qta = post('qta');
$threshold_qta = post('threshold_qta');
$abilita_serial = post('abilita_serial');
$prezzo_vendita = post('prezzo_vendita');
$prezzo_acquisto = post('prezzo_acquisto');
$idiva_vendita = post('idiva_vendita');
$gg_garanzia = post('gg_garanzia');
$servizio = post('servizio');
$componente_filename = post('componente_filename');
$volume = post('volume');
$peso_lordo = post('peso_lordo');
$attivo = post('attivo');
$note = post('note');
$query = 'UPDATE mg_articoli SET '.
' codice='.prepare($codice).','.
' descrizione='.prepare($descrizione).','.
' um='.prepare($um).','.
' id_categoria='.prepare($categoria).','.
' id_sottocategoria='.prepare($subcategoria).','.
' abilita_serial='.prepare($abilita_serial).','.
' threshold_qta='.prepare($threshold_qta).','.
' prezzo_vendita='.prepare($prezzo_vendita).','.
' prezzo_acquisto='.prepare($prezzo_acquisto).','.
' idiva_vendita='.prepare($idiva_vendita).','.
' gg_garanzia='.prepare($gg_garanzia).','.
' servizio='.prepare($servizio).','.
' volume='.prepare($volume).','.
' peso_lordo='.prepare($peso_lordo).','.
' componente_filename='.prepare($componente_filename).','.
' attivo='.prepare($attivo).', '.
' note='.prepare($note).
' WHERE id='.prepare($id_record);
$dbo->query($query);
$dbo->update('mg_articoli', [
'codice' => post('codice'),
'descrizione' => post('descrizione'),
'um' => post('um'),
'id_categoria' => post('categoria'),
'id_sottocategoria' => post('subcategoria'),
'abilita_serial' => post('abilita_serial'),
'threshold_qta' => post('threshold_qta'),
'prezzo_vendita' => post('prezzo_vendita'),
'prezzo_acquisto' => post('prezzo_acquisto'),
'idconto_vendita' => post('idconto_vendita'),
'idconto_acquisto' => post('idconto_acquisto'),
'idiva_vendita' => post('idiva_vendita'),
'gg_garanzia' => post('gg_garanzia'),
'servizio' => post('servizio'),
'volume' => post('volume'),
'peso_lordo' => post('peso_lordo'),
'componente_filename' => $componente,
'attivo' => post('attivo'),
'note' => post('note'),
], ['id' => $id_record]);
// Leggo la quantità attuale per capire se l'ho modificata
$rs = $dbo->fetchArray('SELECT qta FROM mg_articoli WHERE id='.prepare($id_record));
$old_qta = $rs[0]['qta'];
$old_qta = $record['qta'];
$movimento = $qta - $old_qta;
if ($movimento != 0) {
add_movimento_magazzino($id_record, $movimento);
$descrizione_movimento = post('descrizione_movimento');
$data_movimento = post('data_movimento');
add_movimento_magazzino($id_record, $movimento, [], $descrizione_movimento, $data_movimento);
}
/*
Salvataggio info componente (campo `contenuto`)
*/
$componente = post('componente_filename');
// Salvataggio info componente (campo `contenuto`)
if (!empty($componente)) {
$contenuto = \Util\Ini::write(file_get_contents($docroot.'/files/my_impianti/'.$componente), $post);
@ -70,56 +75,58 @@ switch (post('op')) {
}
// Upload file
if (!empty($_FILES) && !empty($_FILES['immagine01']['name'])) {
$tmp = $_FILES['immagine01']['tmp_name'];
if (!empty($_FILES) && !empty($_FILES['immagine']['name'])) {
$filename = Uploads::upload($_FILES['immagine'], [
'name' => 'Immagine',
'id_module' => $id_module,
'id_record' => $id_record,
], [
'thumbnails' => true,
]);
$filename = basename($_FILES['immagine01']['name']);
$filename = unique_filename($filename, $upload_dir);
if (create_thumbnails($tmp, $filename, $upload_dir)) {
$dbo->query('UPDATE mg_articoli SET immagine01='.prepare($filename).' WHERE id='.prepare($id_record));
} else {
$_SESSION['warnings'][] = tr('Errore durante il caricamento del file in _DIR_!', [
'_DIR_' => $upload_dir,
if (!empty($filename)) {
$dbo->update('mg_articoli', [
'immagine' => $filename,
], [
'id' => $id_record,
]);
} else {
flash()->warning(tr('Errore durante il caricamento del file in _DIR_!', [
'_DIR_' => $upload_dir,
]));
}
}
// Eliminazione file
if (post('delete_immagine01') !== null) {
$filename = post('immagine01');
$f = pathinfo($filename);
if (post('delete_immagine') !== null) {
Uploads::delete($record['immagine'], [
'id_module' => $id_module,
'id_record' => $id_record,
]);
delete($upload_dir.'/'.$f['filename'].'.'.$f['extension']);
delete($upload_dir.'/'.$f['filename'].'_thumb100.'.$f['extension']);
delete($upload_dir.'/'.$f['filename'].'_thumb250.'.$f['extension']);
$dbo->query("UPDATE mg_articoli SET immagine01 = '' WHERE id=".prepare($id_record));
$dbo->update('mg_articoli', [
'immagine' => null,
], [
'id' => $id_record,
]);
}
$_SESSION['infos'][] = tr('Informazioni salvate correttamente!');
flash()->info(tr('Informazioni salvate correttamente!'));
break;
// Aggiunta articolo
case 'add':
$codice = post('codice');
$descrizione = post('descrizione');
$categoria = post('categoria');
$subcategoria = post('subcategoria');
// Duplica articolo
case 'copy':
$dbo->query('CREATE TEMPORARY TABLE tmp SELECT * FROM mg_articoli WHERE id = '.prepare($id_record));
$dbo->query('ALTER TABLE tmp DROP id');
$dbo->query('INSERT INTO mg_articoli SELECT NULL,tmp.* FROM tmp');
$id_record = $dbo->lastInsertedID();
$dbo->query('DROP TEMPORARY TABLE tmp');
$dbo->query('UPDATE mg_articoli SET qta=0 WHERE id='.prepare($id_record));
// Inserisco l'articolo solo se non esiste un altro articolo con stesso codice
if ($dbo->fetchNum('SELECT * FROM mg_articoli WHERE codice='.prepare($codice)) == 0) {
$query = 'INSERT INTO mg_articoli(codice, descrizione, id_categoria, id_sottocategoria, attivo) VALUES ('.prepare($codice).', '.prepare($descrizione).', '.prepare($categoria).', '.prepare($subcategoria).', 1)';
$dbo->query($query);
$_SESSION['infos'][] = tr('Aggiunto un nuovo articolo!');
flash()->info(tr('Articolo duplicato correttamente!'));
$query = 'SELECT * FROM mg_articoli WHERE codice='.prepare($codice);
$rs = $dbo->fetchArray($query);
$id_record = $rs[0]['id'];
} else {
$_SESSION['errors'][] = tr('Esiste già un articolo con questo codice!');
}
break;
break;
// Aggiunta prodotto
case 'addprodotto':
@ -201,23 +208,23 @@ switch (post('op')) {
if ($c > 0) {
if ($dbo->query($query)) {
// Movimento il magazzino se l'ho specificato nelle impostazioni
if (get_var("Movimenta il magazzino durante l'inserimento o eliminazione dei lotti/serial number")) {
if (setting("Movimenta il magazzino durante l'inserimento o eliminazione dei lotti/serial number")) {
add_movimento_magazzino($id_record, $c, [], tr('Carico magazzino con serial da _SERIAL_INIZIO_ a _SERIAL_FINE_', [
'_SERIAL_INIZIO_' => $serial__start,
'_SERIAL_FINE_' => $serial__end,
]));
}
$_SESSION['infos'][] = tr('Aggiunti _NUM_ prodotti!', [
flash()->info(tr('Aggiunti _NUM_ prodotti!', [
'_NUM_' => $c,
]);
]));
} else {
$_SESSION['errors'][] = tr("Errore durante l'inserimento!");
flash()->error(tr("Errore durante l'inserimento!"));
}
}
if ($c != $n_prodotti) {
$_SESSION['warnings'][] = tr('Alcuni seriali erano già presenti').'...';
flash()->warning(tr('Alcuni seriali erano già presenti').'...');
}
break;
@ -231,22 +238,30 @@ switch (post('op')) {
$query = 'DELETE FROM mg_prodotti WHERE id='.prepare($idprodotto);
if ($dbo->query($query)) {
// Movimento il magazzino se l'ho specificato nelle impostazioni
if (get_var("Movimenta il magazzino durante l'inserimento o eliminazione dei lotti/serial number")) {
if (setting("Movimenta il magazzino durante l'inserimento o eliminazione dei lotti/serial number")) {
add_movimento_magazzino($id_record, -1, [], tr('Eliminazione dal magazzino del prodotto con serial _SERIAL_', [
'_SERIAL_' => $rs[0]['serial'],
]));
}
$_SESSION['infos'][] = tr('Prodotto rimosso!');
flash()->info(tr('Prodotto rimosso!'));
}
break;
case 'delmovimento':
$idmovimento = post('idmovimento');
// Lettura qtà movimento
$rs = $dbo->fetchArray('SELECT idarticolo, qta FROM mg_movimenti WHERE id='.prepare($idmovimento));
$qta = $rs[0]['qta'];
$idarticolo = $rs[0]['idarticolo'];
// Aggiorno la quantità dell'articolo
$dbo->query('UPDATE mg_articoli SET qta=qta-'.$qta.' WHERE id='.prepare($idarticolo));
$query = 'DELETE FROM mg_movimenti WHERE id='.prepare($idmovimento);
if ($dbo->query($query)) {
$_SESSION['infos'][] = tr('Movimento rimosso!');
flash()->info(tr('Movimento rimosso!'));
}
break;
@ -259,6 +274,21 @@ switch (post('op')) {
//$dbo->query('DELETE FROM mg_prodotti WHERE id_articolo='.prepare($id_record));
$dbo->query('DELETE FROM mg_articoli_automezzi WHERE idarticolo='.prepare($id_record));
$_SESSION['infos'][] = tr('Articolo eliminato!');
flash()->info(tr('Articolo eliminato!'));
break;
}
// Operazioni aggiuntive per l'immagine
if (filter('op') == 'unlink_file' && filter('filename') == $record['immagine']) {
$dbo->update('mg_articoli', [
'immagine' => null,
], [
'id' => $id_record,
]);
} elseif (filter('op') == 'link_file' && filter('nome_allegato') == 'Immagine') {
$dbo->update('mg_articoli', [
'immagine' => $upload,
], [
'id' => $id_record,
]);
}

View File

@ -10,19 +10,19 @@ unset($_SESSION['superselect']['id_categoria']);
<div class="row">
<div class="col-md-6">
{[ "type": "text", "label": "<?php echo tr('Inserisci il codice:'); ?>", "name": "codice", "class":"alphanumeric-mask", "required": 1, "value": "" ]}
{[ "type": "text", "label": "<?php echo tr('Inserisci il codice:'); ?>", "name": "codice", "class":"alphanumeric-mask", "required": 1 ]}
</div>
<div class="col-md-6">
{[ "type": "text", "label": "<?php echo tr('Inserisci la descrizione:'); ?>", "name": "descrizione", "required": 1, "value": "" ]}
{[ "type": "text", "label": "<?php echo tr('Inserisci la descrizione:'); ?>", "name": "descrizione", "required": 1 ]}
</div>
<div class="col-md-6">
{[ "type": "select", "label": "<?php echo tr('Inserisci la categoria:'); ?>", "name": "categoria", "required": 1, "value": "", "ajax-source": "categorie", "icon-after": "add|<?php echo Modules::get('Categorie')['id']; ?>" ]}
{[ "type": "select", "label": "<?php echo tr('Inserisci la categoria:'); ?>", "name": "categoria", "required": 1, "ajax-source": "categorie", "icon-after": "add|<?php echo Modules::get('Categorie articoli')['id']; ?>" ]}
</div>
<div class="col-md-6">
{[ "type": "select", "label": "<?php echo tr('Inserisci la subcategoria:'); ?>", "name": "subcategoria", "id": "subcategoria_add", "value": "", "ajax-source": "sottocategorie", "icon-after": "add|<?php echo Modules::get('Categorie')['id']; ?>||hide" ]}
{[ "type": "select", "label": "<?php echo tr('Inserisci la sottocategoria:'); ?>", "name": "subcategoria", "id": "subcategoria_add", "ajax-source": "sottocategorie", "icon-after": "add|<?php echo Modules::get('Categorie articoli')['id']; ?>||hide" ]}
</div>
</div>

View File

@ -24,8 +24,8 @@ switch ($resource) {
for ($i = 0; $i < sizeof($fatture); ++$i) {
($fatture[$i]['n2_fattura'] != '') ? $n_fattura = $fatture[$i]['n2_fattura'] : $n_fattura = $fatture[$i]['n_fattura'];
$id_module = Modules::get('Fatture di vendita')['id'];
echo "<tr><td class='first_cell text-left'><a href='".ROOTDIR.'/editor.php?id_module='.$id_module.'&id_record='.$fatture[$i]['iddocumento']."' target=\"_blank\" title=\"Apri il documento su una nuova finestra\">Fatt. n. ".$n_fattura."</a></td>\n";
$link_id = Modules::get('Fatture di vendita')['id'];
echo "<tr><td class='first_cell text-left'><a href='".ROOTDIR.'/editor.php?id_module='.$link_id.'&id_record='.$fatture[$i]['iddocumento']."' target=\"_blank\" title=\"Apri il documento su una nuova finestra\">Fatt. n. ".$n_fattura."</a></td>\n";
echo "<td class='table_cell text-left'>".Translator::dateToLocale($fatture[$i]['data_fattura'])."</td>\n";
echo "<td class='table_cell text-right'>".Translator::numberToLocale($fatture[$i]['costo_unitario'])." &euro;</td></tr>\n";
@ -54,8 +54,8 @@ switch ($resource) {
for ($i = 0; $i < sizeof($fatture); ++$i) {
($fatture[$i]['n2_fattura'] != '') ? $n_fattura = $fatture[$i]['n2_fattura'] : $n_fattura = $fatture[$i]['n_fattura'];
$id_module = Modules::get('Fatture di vendita')['id'];
echo "<tr><td class='first_cell text-left'><a href='".ROOTDIR.'/editor.php?id_module='.$id_module.'&id_record='.$fatture[$i]['iddocumento']."' target=\"_blank\" title=\"Apri il documento su una nuova finestra\">Fatt. n. ".$n_fattura."</a></td>\n";
$link_id = Modules::get('Fatture di vendita')['id'];
echo "<tr><td class='first_cell text-left'><a href='".ROOTDIR.'/editor.php?id_module='.$link_id.'&id_record='.$fatture[$i]['iddocumento']."' target=\"_blank\" title=\"Apri il documento su una nuova finestra\">Fatt. n. ".$n_fattura."</a></td>\n";
echo "<td class='table_cell text-left'>".Translator::dateToLocale($fatture[$i]['data_fattura'])."</td>\n";
echo "<td class='table_cell text-right'>".Translator::numberToLocale($fatture[$i]['costo_unitario'])." &euro;</td></tr>\n";
@ -84,8 +84,8 @@ switch ($resource) {
for ($i = 0; $i < sizeof($fatture); ++$i) {
($fatture[$i]['n2_fattura'] != '') ? $n_fattura = $fatture[$i]['n2_fattura'] : $n_fattura = $fatture[$i]['n_fattura'];
$id_module = Modules::get('Fatture di acquisto')['id'];
echo "<tr><td class='first_cell text-left'><a href='".ROOTDIR.'/editor.php?id_module='.$id_module.'&id_record='.$fatture[$i]['iddocumento']."' target=\"_blank\" title=\"Apri il documento su una nuova finestra\">Fatt. n. ".$n_fattura."</a></td>\n";
$link_id = Modules::get('Fatture di acquisto')['id'];
echo "<tr><td class='first_cell text-left'><a href='".ROOTDIR.'/editor.php?id_module='.$link_id.'&id_record='.$fatture[$i]['iddocumento']."' target=\"_blank\" title=\"Apri il documento su una nuova finestra\">Fatt. n. ".$n_fattura."</a></td>\n";
echo "<td class='table_cell text-left'>".Translator::dateToLocale($fatture[$i]['data_fattura'])."</td>\n";
echo "<td class='table_cell text-right'>".Translator::numberToLocale($fatture[$i]['costo_unitario'])." &euro;</td></tr>\n";

View File

@ -2,7 +2,7 @@
include_once __DIR__.'/../../../core.php';
$id_module = Modules::get('Articoli')['id'];
$link_id = Modules::get('Articoli')['id'];
$fields = [
'Codice' => 'codice',
@ -31,7 +31,7 @@ $rs = $dbo->fetchArray($query);
foreach ($rs as $r) {
$result = [];
$result['link'] = ROOTDIR.'/editor.php?id_module='.$id_module.'&id_record='.$r['id'];
$result['link'] = ROOTDIR.'/editor.php?id_module='.$link_id.'&id_record='.$r['id'];
$result['title'] = $r['codice'].' - '.$r['descrizione'];
$result['category'] = 'Articoli';

Some files were not shown because too many files have changed in this diff Show More