.. _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 `_