Blog

Le Preferenze in Genropy

Le preferenze in Genropy

Ipotizziamo di voler inserire all’interno del nostro applicativo una procedura di controllo che impedisca il salvataggio di un record “fattura” se l’importo totale è al di sotto di un importo minimo, oppure di voler aggiungere un limite massimo allo sconto inseribile, o ancora impostare un costo della spedizione sulla base di valori reperibili da una tabella di riferimento.

La soluzione potrebbe essere estremamente semplice. Prendendo il primo caso, sarebbe sufficiente posizionarsi all’interno di un trigger e al momento del calcolo del totale in vista del salvataggio andare a sollevare un’eccezione se il totale è inferiore all’importo che specifichiamo, ad esempio “1”:

if record['totale_fattura'] < 1: 
    raise self.exception('standard', msg="Devi raggiungere l'importo minimo per salvare la fattura")

La soluzione proposta funzionerebbe perfettamente, tuttavia per ogni aggiornamento successivo sarebbe necessario intervenire sul codice e riavviare l’istanza. Cosa succederebbe quindi nel caso in cui un nostro cliente cambiasse idea spesso in merito alla soglia di questo importo minimo?

Il sistema delle preferenze di Genropy ci permette di impostare dei valori rendendoli aggiornabili e disponibili in tutto l’applicativo in modo estremamente facile e dinamico. Possono inoltre essere utilizzate per mostrare o meno colonne, container, addirittura tabelle intere, ed essere poi modificate dall’utente, dall’amministratore, o dallo sviluppatore, ogni volta che ce ne sarà bisogno, senza necessità di toccare ulteriormente il codice o riavviare l’istanza dell’applicativo.

Le preferenze possono essere definite a livello di intero applicativo o a livello di singolo utente, a seconda che i valori in esse impostati siano poi gli stessi per tutti gli utenti oppure solo per l’utente che le ha impostate. Nel primo caso, vi si accede dal bottone in basso a destra con il nome del proprietario dell’istanza, nel secondo caso dal bottone immediatamente accanto con il nome dello user.

Riprendiamo quindi il nostro esempio e vediamo come impostare una soglia minima per il totale della fattura nel nostro progetto Sandbox.

Le preferenze sono sempre definite a livello di package. Creiamo quindi innanzitutto un file *preference.py* nelle risorse del package *fatt*, e nel quale definiamo una classe AppPref:

class AppPref(object): 

      def permission_fatt(self,**kwargs): 
          return 'admin'

      def prefpane_fatt(self,parent,**kwargs): 
          tc = parent.tabContainer(margin='2px',**kwargs)   
          self.fatt_generali(tc.contentPane(title='!![it]Generali', datapath='.generali')) 

      def fatt_generali(self, pane): 
           bc = pane.borderContainer(region='center', margin='10px') 
           fb = bc.contentPane(region='top', height='80px').formbuilder(cols=1,border_spacing='3px') 
           fb.numbertextbox('^.min_importo', lbl='Min. importo fatt.', width='5em') 

Con permission_fatt stabiliamo il tag di autorizzazione necessario per poter visualizzare e modificare le successive preferenze. Con prefpane_fatt definiamo invece il contenuto, in termini di spazi e campi di imputazione. Notiamo che stiamo costruendo una struttura già annidata, con dei TabContainer, in modo da poter classificare le preferenze sulla base della destinazione d’uso.

Inseriamo le preferenze relative all’importo minimo.

Tutte le preferenze, con la relativa gerarchia, compresa la Bag con le spese di spedizione che abbiamo costruito con la quickGrid, verranno a loro volta inscatolate in una Bag, e saranno raggiungibili fornendone il path completo. Ad esempio, nel model di fattura, aggiungeremo un metodo che si occuperà di effettuare il controllo in merito all’importo minimo in fase di salvataggio:

if record['totale_fattura'] < self.db.application.getPreference('generali.min_importo', pkg='fatt', mandatoryMsg='!![it]Non hai impostato un importo minimo per le fatture'):
     raise self.exception('standard', msg="Devi raggiungere l'importo minimo per salvare la fattura")

Con il metodo getPreference (che in questo caso, trovandoci nella classe Table, andiamo a leggere dall’application) possiamo infatti leggere le preferenze che abbiamo impostato: è sufficiente passare al metodo il path completo della preferenza e il relativo package. Notiamo che passiamo anche un parametro in più non obbligatorio, mandatoryMsg, con il quale definiamo il contenuto del messaggio di avviso da mostrare se le preferenze non sono state impostate.

Ipotizziamo a questo punto di voler fare di più:

  • impostare una scontistica massima in percentuale, sopra la quale lo sconto considerato eccessivo verrà ignorato e verrà considerata la percentuale massima
  • impostare un costo di spedizione, che venga letto automaticamente da una griglia dove sono definiti degli scaglioni basati sul peso della spedizione

Innanzitutto aggiungiamo a preference.py il nostro sconto massimo e costruiamo una quickGrid, ovvero un widget di Genropy estremamente “leggero” ma veloce per costruire griglie, con i pesi di spedizione minimo e massimo e il relativo costo di spedizione. Questi valori verranno inscatolati in una Bag.

           fb.numbertextbox('^.max_sconto', lbl='Max. sconto', width='5em') 
           spese_sped = bc.contentPane(region='center').quickGrid(value='^.spese_spedizione') 
           spese_sped.tools('addrow,delrow',title='Impostazioni costi spedizione') 
           spese_sped.column('peso_min',width='10em',name='Peso min.',edit=True) 
           spese_sped.column('peso_max',width='10em',name='Peso max.',edit=True) 
           spese_sped.column('costo', width='10em', dtype='money', name='Costo', edit=True)

Nella risorsa di fattura_riga leggiamo il valore dello sconto massimo e lo confrontiamo con quello che abbiamo impostato nelle preferenze:

if row['sconto']:
    max_sconto = self.getPreference('generali.max_sconto', pkg='fatt') 
    sconto = row['sconto'] if row['sconto'] < max_sconto else max_sconto 
    row['sconto'] = sconto * row['prezzo_unitario'] / 100

Notiamo che tutto ciò avviene all’interno di un th_remoteRowController, un metodo di hook particolare di Genropy che scatta automaticamente all’inserimento del valore all’interno di una colonna della griglia e permette di effettuare delle operazioni in Python.

Infine, nella risorsa di fattura, aggiungiamo una dataRpc che scatta all’inserimento del peso di spedizione e va a reperire il valore corretto dalla tabella:

    fb.dataRpc('^.costo_spedizione', self.leggiSpeseSpedizione, peso_spedizione='^.peso_spedizione', _userChanges=True)

@public_method 
def leggiSpeseSpedizione(self, peso_spedizione=None): 
     spese_spedizione = self.getPreference('generali.spese_spedizione', pkg='fatt') 
     if not peso_spedizione: 
          return 0 
     for v in spese_spedizione.values(): 
          if peso_spedizione >= int(v['peso_min']) and peso_spedizione < int(v['peso_max']): 
          return v['costo']

L’uso di checkpref

Le preferenze ci offrono però un’ulteriore possibilità. In un qualsiasi container, widget, colonna, o addirittura tabella, è possibile utilizzare l’attributo checkpref('package.path_della_preferenza') per attivare o disattivare la visualizzazione del component sulla base della preferenza indicata.

Modifichiamo quindi nuovamente le nostre preferenze aggiungendo anche un ulteriore tab “Magazzino”:

def prefpane_fatt(self,parent,**kwargs): 
    tc = parent.tabContainer(margin='2px',**kwargs) 
    self.fatt_generali(tc.contentPane(title='!![it]Generali', datapath='.generali')) 
    self.fatt_magazzino(tc.contentPane(title='!![it]Magazzino', datapath='.magazzino'))

def fatt_magazzino(self, pane):
    fb = pane.formbuilder(cols=1,border_spacing='3px', margin='10px')
    fb.checkbox(value='^.campi_dinamici_magazzino',label='Campi dinamici magazzino')

E a questo punto potremo aggiungere al container che mostra il tab “Caratteristiche” con i dynamic fields:

self.caratteristicheProdotto(tc.contentPane(title='Caratteristiche',datapath='.record', checkpref='fatt.magazzino.campi_dinamici_magazzino'))

L’attributo è di per sé sufficiente a interpretare la preferenza booleana e apportare automaticamente tutte le conseguenze.


Abbiamo visto insomma alcune delle numerose applicazioni che possono avere le Preferenze di Genropy, uno strumento utilissimo per gestire valori e mostrare/nascondere oggetti, mantenendo il codice dell’applicativo pulito, flessibile e facilmente aggiornabile.

Vuoi saperne di più? Leggi la documentazione dedicata nel Manuale dell’ORM: