.. _tutorial_fatturazione/customer_2:
Lezione 3: Tabelle di lookup
============================
In questa lezione parleremo delle tabelle di lookup, definendo la table ``cliente_tipo``. E inizieremo a migliorare la pagina di gestione della table ``cliente``.
.. raw:: html
La tabella cliente_tipo
-----------------------
Supponiamo che serva, per la nostra applicazione, suddividere i clienti secondo una tipologia.
Definiamo quindi la tabella ``cliente_tipo`` con solo le colonne ``codice`` e ``descrizione``.
::
class Table(object):
def config_db(self, pkg):
tbl = pkg.table('cliente_tipo', pkey='codice', name_long='Tipo cliente',
name_plural='Tipi cliente', caption_field='descrizione', lookup=True)
self.sysFields(tbl, id=False)
tbl.column('codice', size=':5', name_long='Codice')
tbl.column('descrizione', name_long='Descrizione')
In questo caso, a differenza di quanto fatto nella :ref:`tabella cliente` vogliamo che la *primary key* della tabella sia la colonna ``codice`` e non la colonna ``id`` di sistema. E questo lo facciamo specificando nel metodo ``table`` il parametro ``pkey='codice'`` ed impedendo l'aggiunta della colonna ``id`` quando invochiamo la funzione ``sysFields``
::
self.sysFields(tbl, id=False)
Questo genere di tabelle, caratterizzate da un numero molto limitato di righe, servono in genere a creare una scelta multipla di valori per il campo di una table: nel nostro caso il ``cliente_tipo`` per la table ``cliente``.
Di norma per tabelle di questo tipo si usa come chiave primaria il codice stesso.
.. note::
Tabelle di questo genere vengono definite in Genropy **tabelle di lookup** e sono caratterizzati dal parametro ``lookup=True`` nella loro definizione. Mettere questo parametro a ``True`` fa sì che la tabella possa essere visualizzata e riempita attraverso una pagina generica per la gestione delle tabelle lookup e non sarà necessario creare la risorsa ``th_cliente_tipo`` .
Aggiungiamo poi al menu del package il gestore tabelle di lookup come mostrato
::
fatt.lookupBranch("Tabelle Ausiliarie", pkg="fatt")
O, nella sua versione *legacy*, precedente alla rivisitazione dei menu::
fatt.lookups(u"Tabelle Ausiliarie", lookup_manager="fatt")
.. hint::
Per un approfondimento sui nuovi menu di Genropy si rimanda alla `Guida ai Menu `_
Dopo aver riallineato il db con il comando **gnrdbsetup** e riattivato il server, usiamo il gestore tabelle di lookup nella nostra applicazione per caricare alcune voci nella tabella **cliente_tipo**.
.. raw:: html
Modifica tabella cliente
--------------------------
Dopo aver creato la tabella ``cliente_tipo`` per utilizzarla modifichiamo il model di ``cliente`` aggiungendo la colonna ``cliente_tipo_codice``.
Quindi metteremo questa colonna in relazione con la colonna ``codice`` della table ``cliente_tipo`` come *foreign key*.
::
tbl.column('cliente_tipo_codice', size=':5', name_long='Tipo cliente', name_short='Tipo').relation(
'cliente_tipo.codice',
relation_name='clienti',
mode='foreignkey',
onDelete='raise')
Adesso in modo del tutto analogo possiamo creare la tabella di lookup ``pagamento_tipo`` e collegarla alla colonna ``pagamento_tipo_codice`` che aggiungeremo nella table ``cliente`` modificado il model.
Modifiche a th_cliente
----------------------
Apriamo ora il modulo ``th_cliente.py`` nelle risorse ed aggiungiamo alla **view** le colonne e alla **form** i relativi campi.
Nel frattempo miglioreremo l'aspetto della form utilizzando attributi come **colspan** e altri.
Nella form notiamo che i campi vengono aggiunti ad un oggetto di tipo **formbuilder**.
Il **formbuilder** è un elemento di interfaccia che serve a costruire delle form di campi che possono presentarsi affiancati su più colonne. Esso è basato su una tabella HTML, pertanto organizza i contenuti in righe e colonne.
Per avere dei margini corretti spostiamo il formbuilder all'interno di un **div** cui diamo l'attributo **margin=10px**.
::
class View(BaseComponent):
def th_struct(self,struct):
r = struct.view().rows()
r.fieldcell('ragione_sociale')
r.fieldcell('cliente_tipo_codice')
r.fieldcell('pagamento_tipo_codice')
r.fieldcell('indirizzo')
r.fieldcell('provincia')
r.fieldcell('comune_id')
def th_order(self):
return 'ragione_sociale'
def th_query(self):
return dict(column='ragione_sociale', op='contains', val='')
class Form(BaseComponent):
def th_form(self, form):
pane = form.record
fb = pane.div(margin='10px').formbuilder(
cols=2, border_spacing='4px', colswidth='auto', fld_width='100%')
fb.field('ragione_sociale', colspan=2)
fb.field('cliente_tipo_codice')
fb.field('pagamento_tipo_codice')
fb.field('indirizzo', colspan=2)
fb.field('provincia')
fb.field('comune_id', condition='$sigla_provincia=:provincia',
condition_provincia='^.provincia')
def th_options(self):
return dict(dialog_height='400px', dialog_width='600px')
Funzionamento del campo cliente_tipo
------------------------------------
Dopo aver fatto aggiornare il db con il comando ``gnrdbsetup`` e riattivato il server, torniamo ad osservare come si presenta la pagina di gestione della table ``cliente``. Notiamo i campi appena aggiunti sia nella **view** che nella **form**.
Possiamo selezionare un **tipo cliente** ma, dal momento che non abbiamo caricato alcun valore per il **tipo pagamento**, non potremmo selezionarli.
Cliccando sulla label del campo però si può aprire una palette, che permette di inserire direttamente gli elementi della tabella in relazione, in questo caso ``pagamento_tipo``.
La facoltà di accedere alle tabelle di lookup per inserire e modificare valori, può ovviamente essere limitata agli utenti che hanno gli adeguati tag di autorizzazione.
.. raw:: html
Ulteriori miglioramenti
-----------------------
Torniamo ora al model del ``cliente`` e aggiungiamo le colonne ``note`` e ``email``.
::
tbl.column('note', name_long="Note")
tbl.column('email', name_long='Email')
Andiamo quindi alla risorsa di Form e utiliziamo l'elemento ``BorderContainer`` per dividere idealmente lo spazio della nostra pagina di form in più regioni.
Definiremo un ``contentPane`` nella parte superiore (``region =top``) che andrà a contenere la form con i dati principali del cliente.
Nello spazio rimanente definiamo un ``RoundedGroupFrame`` destinato a contenere le note, per le quali utilizziamo un widget ``simpleTextArea`` al quale l'attributo ``editor=True`` fornirà un editor di testo formattato.
Inoltre per migliorare la leggibilità del codice definiremo dei metodi dedicati ``datiCliente`` e ``noteCliente`` in cui sviluppare le relative parti.
::
class Form(BaseComponent):
def th_form(self, form):
bc = form.center.borderContainer(datapath='.record')
self.datiCliente(bc.contentPane(region='top'))
self.noteCliente(bc.roundedGroupFrame(title='Note', region='center'))
def datiCliente(self, pane):
fb = pane.div(margin_left='50px', margin_right='80px').formbuilder(
cols=2, border_spacing='4px', colswidth='auto', fld_width='100%')
fb.field('ragione_sociale', colspan=2)
fb.field('cliente_tipo_codice')
fb.field('pagamento_tipo_codice')
fb.field('indirizzo', colspan=2)
fb.field('provincia')
fb.field('comune_id', condition='$sigla_provincia=:provincia',
condition_provincia='^.provincia')
fb.field('email', validate_email=True)
def noteCliente(self, pane):
pane.simpleTextArea(value='^.note', editor=True)
.. image:: /_static/images/schermata-2019-04-16-alle-193616.png
:width: 500px
:align: center
.. raw:: html
**Allegati:**
- `cliente `_
- `th_cliente `_
- `cliente_tipo `_
- `pagamento_tipo `_
- `menu `_