Lezione 8: Modifiche alle viste

In questa lezione vedremo

  • Come aggiungere ad una form griglie di dati in relazioni con il record primario.
  • Aggiungere sotto una griglia una riga che totalizzi gli importi numerici
  • Definire nel model delle formulaColumn, ovvero colonne astratte che riportano valori calcolati via espressioni SQL
  • Definire dei bottoni di sections su una View.

Vista fatture cliente

Per mostrare le fatture di un cliente, dall'interno della form cliente, andiamo a modificare il modulo th_cliente.py. Ricordiamo che nella parte superiore della form era stato definito un formbuilder con i dati del cliente mentre al centro avevamo le note.

Provvediamo ora ad aggiungere un TabContainer in modo da avere più pannelli nella parte centrale. Nel primo rimetteremo le note mentre per riempire il secondo creeremo il metodo fattureCliente.

In tale metodo utilizzeremo il component plainTableHandler che ci darà modo di visualizzare i record di fattura in relazione con il cliente correntemente visualizzato. Passiamo come unico parametro relation='@fatture' per indicare che seguiamo la relazione definita in fattura fattura.py con relation_name='fatture'.

def th_form(self, form):
      bc = form.center.borderContainer()
      self.datiCliente(bc.roundedGroupFrame(
          title='Dati cliente', region='top', datapath='.record', height='160px'))
      tc = bc.tabContainer(region='center', margin='2px')
      self.fattureCliente(tc.contentPane(title='Fatture'))
      self.noteCliente(tc.contentPane(title='Note', datapath='.record'))

def fattureCliente(self, pane):
      pane.plainTableHandler(relation='@fatture')

Totali su una vista

Ci piacerebbe che il nostro utente potesse immediatamente conoscere il totale del fatturato, dell'imponibile e dell'iva per il cliente corrente e decidiamo quindi di aggiungere al piede della griglia questi totali.

Il video illustra una procedura che attualmente è stata superata da ulteriori migliorie del component tableHandler. Qui spiegheremo sia la vecchia procedura basata sulla definizione di una toolbar sotto la griglia, che quella più moderna e semplice.

Metodo "vecchio"

Andiamo a modificare th_fattura.py e creiamo una classe ViewFromCliente, inizialmente copiando la classe View togliendo il campo r.fieldcell('cliente_id'). Qui andiamo a definire il metodo th_bottom_custom.

class ViewFromCliente(BaseComponent):

    def th_struct(self, struct):
        r = struct.view().rows()
        r.fieldcell('protocollo')
        r.fieldcell('data')
        r.fieldcell('totale_imponibile')
        r.fieldcell('totale_iva')
        r.fieldcell('totale_fattura')

    def th_order(self):
        return 'protocollo'

    def th_bottom_custom(self, bottom):
        bottom.slotBar('*,sum@totale_imponibile,5,sum@totale_iva,5,sum@totale_fattura,5',
                     border_top='1px solid silver', height='23px')

In una classe di tableHandler è possibile definire dei metodi che iniziano con il prefisso th seguito dal nome di una region, in questo caso bottom. Il resto del nome può essere deciso a piacere. Avendo dichiarato tale metodo abbiamo modo di aggiungere o modificare gli elementi standard previsti dal component. In questo caso aggiungiamo una slotBar la quale è un component per creare delle barre di strumenti suddivise in settori chiamati slot e separatori. Il suo parametro principale è una stringa che definisce la sequenza degli slot, separati da virgole.

In questo caso * è un separatore elastico a sinistra che occuperà tutto lo spazio disponibile, poi abbiamo gli elementi sum@ che sono i nostri totalizzatori, separati da separatori di 5 pixel.

_images/totali_old.jpg

Metodo "nuovo"

Semplicemente aggiungiamo totalize=True alle colonne della griglia che vogliamo totalizzare. Suggeriamo di utilizzare sempre questa medotologia per aggiungere i totali in fondo ad una griglia.

class ViewFromCliente(BaseComponent):

    def th_struct(self, struct):
        r = struct.view().rows()
        r.fieldcell('protocollo', width='10em')
        r.fieldcell('data', width='7em')
        r.fieldcell('totale_imponibile', totalize=True)
        r.fieldcell('totale_iva', totalize=True)
        r.fieldcell('totale_fattura', totalize=True)
_images/totali_new.jpg

Ora specifichiamo nella form del cliente che il plainTableHandler delle fatture deve riferirsi alla classe ViewFromCliente.

pane.plainTableHandler(relation='@fatture', viewResource='ViewFromCliente')

Vista prodotti acquistati

Vogliamo offrire all'utente la possibilità di vedere immediatamente quali prodotti siano stati acquistati dal cliente. Per questa ragione torniamo a modificare th_cliente.py e aggiungiamo al tabContainer una nuova scheda, il cui contenuto sarà definito nel metodo prodottiCliente.

Qui usiamo nuovamente il component plainTableHandler ma, questa volta, non abbiamo una relazione diretta da seguire tra cliente e prodotto. Pertanto useremo una sintassi alternativa che prevede di indicare come parametri

self.prodottiCliente(tc.contentPane(title='Prodotti Acquistati'))
  • table (in questo caso prodotto)
  • condition , che corrisponde ad una clausola WHERE per la query che selezionerà gli elementi della griglia
def prodottiCliente(self, pane):
    pane.plainTableHandler(table='fatt.prodotto',
                           condition='@righe_fattura.@fattura_id.cliente_id =:cl_id',
                           condition_cl_id='^#FORM.record.id', export=True)

Il significato di questa sintassi è di imporre che i prodotti selezionati abbiano almeno una riga di fattura in cui il cliente_id della fattura stessa sia quello corrente. Il parametro formale cl_id viene passato al tablehandler come condition_cl_id per indicare che fa parte integrante della condition.

Con il parametro export=True viene aggiunto il bottone per l'export della tabella come worksheet .xls.

Le formulaColumn

Apriamo ora il modulo di model cliente.py e mostriamo come sia possibile aggiungere ad una table non solo colonne reali ma anche colonne calcolate.

In particolare desideriamo che per ogni cliente sia calcolato il numero delle fatture presenti e il valore del fatturato.

A tale scopo aggiungiamo a tbl una formulaColumn il cui nome è n_fatture e il cui valore è il risultato di una subselection nella tabella fattura.

tbl.formulaColumn('n_fatture', select=dict(table='fatt.fattura',
                                           columns='COUNT(*)',
                                           where='$cliente_id=#THIS.id'),
                   dtype='L',
                   name_long='N.Fatture')

Per il totale fatturato al cliente si procede in modo analogo.

Dal momento che le colonne sono solo calcolate non è necessario riallineare il database ma potremo tornare alla pagina cliente e senza nemmeno ricaricarla trascinare dal configuratore nel cassetto laterale della griglia le colonne n_fatture e tot_fatturato che abbbiamo appena creato.

Sections

Desideriamo offrire all'utente la possiblità di suddividere l'archivio dei clienti tra quelli che hanno già fatto acquisti e quelli che abbiamo caricato ma non hanno ancora acquistato.

Sfruttiamo a tale scopo una particolare feature di Genropy andando a modificare la classe View in th_cliente.py.

Per prima cosa aggiungiamo alla vista le colonne n_fatture e tot_fatturato e poi definiamo il metodo th_sections_acquisti.

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')
    r.fieldcell('n_fatture')
    r.fieldcell('tot_fatturato', format='#,###.00')

def th_sections_acquisti(self):
    return [dict(code='tutti', caption='Tutti'),
            dict(code='con_acquisti', caption='Con Acquisti', condition='$n_fatture>0'),
            dict(code='senza_acquisti', caption='Senza Acquisti', condition='$n_fatture=0')]

I metodi di una classe di tipo view che iniziano per th_sections servono a definire le sezioni in cui vogliamo ripartire i record della table. Tali sezioni vengono chiamate in Genropy sections, e si presentano come bottoni che aggiungono alla query corrente una condizione particolare specificata dal parametro condition.

Dovremo a tal fine rendere una lista di dizionari dove ogni elemento rappresenta una section e nel dizionario sono definiti i parametri di sezionamento. In particolare creiamo 3 sezioni:

  • Tutti: che mostra tutti i clienti selezionati senza restrizioni aggiuntive
  • Con Acquisti: la quale aggiunge come condizione che il numero di fatture del cliente sia maggiore di zero.
  • Senza Acquisti: la quale aggiunge come condizione che il numero di fatture del cliente sia zero.

Procediamo quindi ad utilizzare come visto in precedenza un metodo th_top per aggiungere una slotBar. In questa slotBar porremo i bottoni per selezionare le sections.

def th_top_toolbarsuperiore(self, top):
      top.slotToolbar('5,sections@acquisti,*', childname='superiore', _position='<bar')

Ricaricando la pagina del cliente notiamo la presenza dei bottoni di sections e notiamo che il titolo della vista cambia a seconda della section selezionata.

Desideriamo introdurre ora anche delle sections basate sul tipo_cliente. In questo caso non occorre definire una section con una condition. Basterà aggiungere nella slotToolbar un elemento sections@cliente_tipo_codice dove cliente_tipo_codice è la colonna sulla quale vogliamo suddividere i record. Automaticamente vengono creati i bottoni corrispondenti a tutti i tipi cliente che sono stati creati nel database e ad essi viene associata l'opporotuna condition.

top.slotToolbar('5,sections@acquisti,*,sections@cliente_tipo_codice,5', childname='superiore', _position='<bar')

Attachments: