Lezione 9: Creazione stampa fattura¶
In questa lezione vedremo come definire la stampa della fattura e come rendere questa stampa eseguibile direttamente dalla pagina delle fatture.
Una delle problematiche più faticose da gestire nelle applicazioni gestionali è quella delle stampe. In Genropy viene utilizzato un sistema per creare con poche istruzioni delle stampe html che poi vengono trasformate in PDF da wkhtmltopdf. Per utilizzare quindi le procedure di stampa di Genropy accertarsi di aver fatto le installazioni necessarie
Ma prima di incominciare è necessario illustrare in estrema sintesi la sintassi di Genropy per definire le stampe html.
GnrHtml: layout, row, cell¶
Essenzialmente la definizione di una stampa in Genropy si ottiene usando uno dentro l’altro elementi layout
, row
e cell
.
La sintassi è del tutto simile a quella che abbiamo già visto nella definizione del model del database o nella creazione delle pagine Classe View .
layout = mypage.layout()
row = layout.row(height = 10)
cell = row.cell(value='Ciao', width = 20)
Vediamo in breve le regole per usare questi elementi.
Un elemento
layout
è un oggetto che può contenere solo elementirow
. Cioè un layout può essere diviso orizzontalmente in fasce orizzontali delle quali si indica l’altezza con il parametroheight
.Se si omette l’altezza di una
row
questa sarà considerata elastica andando ad occupare tutto lo spazio disponibile calcolato per differenza rispetto a quello delle altre righe.Qualora più righe fossero prive di altezza lo spazio libero verrà ripartito in parti uguali suddiviso sul numero di righe elastiche.
Ogni elemento
row
puo contenere solo elementicell
.Agli elementi
cell
bisogna assegnare una larghezza con il parametrowidth
. Così come per l’altezza delle rows, se non viene indicata lawidth
di unacell
, questa sarà considerata elastica ed occuperà lo spazio disponibile nella row. Qualora più celle non abbiano una larghezza assegnata lo spazio disponibile verrà ripartito in parti uguali fra tutte le celle senza larghezza della stessa riga.Ogni cell a sua volta può contenere testo, un pezzo di html oppure a sua volta un altro elemento
layout
. In questo caso il layout, a sua volta, sarà ripartito in rows e via dicendo in una modalità frattale.
La risorsa stampa: mia_fattura.py¶
Le stampe che rappresentano i dati delle table del nostro applicativo vengono definite come risorse, pertanto dovremo creare un modulo mia_fattura.py
al path resources/tables/fattura/html_res
creando quindi la cartella html_res.
L’oggetto stampa lo definiamo implementando la Main
che eredita da TableScriptToHtml
.
from gnr.web.gnrbaseclasses import TableScriptToHtml
class Main(TableScriptToHtml):
maintable = 'fatt.fattura'
doc_header_height = 32
doc_footer_height = 12
grid_header_height = 5
- Come attributi di classe indichiamo:
maintable , ovvero la table di database i cui record verrano stampati dalla risorsa. In questo caso è
fatt.fattura
doc_header_height, ovvero l’altezza in millimetri della testata del documento
doc_footer_height, ovvero l’altezza in millimetri del piede del documento
grid_header_height, ovvero l’altezza in millimetri della testata della griglia, dove cioè metteremo i nomi delle colonne
Nella classe TableScriptToHtml
sono definiti già molti valori di default per questi attributi, pertanto nella nostra risorsa andiamo a impostare solo quelli che desideriamo ridefinire.
La testata della fattura¶
Cominciamo dalla parte superiore della stampa, ovvero la testata del documento che viene definita implementando il metodo di hook docHeader
.
def docHeader(self, header):
layout = header.layout(name='doc_header', margin='5mm', border_width=0)
row = layout.row()
left_cell = row.cell(width=80)
center_cell = row.cell()
right_cell = row.cell(width=80)
self.datiFattura(left_cell)
self.datiCliente(right_cell)
Vediamo quindi che il metodo docHeader riceve un elemento cell, già dell’altezza giusta e nella posizione giusta e noi andremo a riempirlo di contenuto grafico.
Iniziamo quindi subito a definire un layout a cui diamo border_width=0
poiché non vogliamo vederne il bordo.
Qui definiamo dunque una row
che andremo a suddividere in 3 spazi creando 3 elementi cell
:
quella a sinistra la useremo per mettere i dati della fattura, cioè la data e il numero di protocollo
quella al centro non le passiamo il parametro della larghezza per far sì che si espanda al massimo facendo da elemento spaziatore
quella a destra invece andrà a contenere i dati di intestazione relativi al cliente
def datiFattura(self, c):
l = c.layout('dati_fattura',
lbl_class='cell_label',
border_width=0)
r = l.row(height=8)
r.cell(self.field('data'), lbl='Data')
r = l.row(height=8)
r.cell(self.field('protocollo'), lbl='N.Fattura')
def datiCliente(self, c):
l = c.layout('dati_cliente', border_width=0)
l.row(height=5).cell('Spett.')
l.row(height=5).cell(self.field('@cliente_id.ragione_sociale'))
l.row(height=5).cell(self.field('@cliente_id.indirizzo'))
comune = self.field('@cliente_id.@comune_id.denominazione')
provincia = self.field('@cliente_id.provincia')
l.row(height=5).cell('%s (%s)' % (comune, provincia))
def defineCustomStyles(self):
self.body.style(""".cell_label{
font-size:8pt;
text-align:left;
color:gray;
text-indent:1mm;}
.footer_content{
text-align:right;
margin:2mm;
}
""")
In ciascuno di questi metodi creaiamo dei layout che ci permetteranno di organizzare i nostri dati in righe e celle.
Nel blocco dei dati fattura creiamo due righe, ciascuna delle quali contiene una cella. In queste celle andremo a scrivere la data della fattura e il protocollo.
Da notare che si accede ai dati del record di fattura usando il metodo
field
, che in analogia con quello omonimo usato nel formbuilder (nella definizione delle form Modifiche a th_cliente ), provvede a leggere il dato e a formattarlo nel modo più opportuno a seconda del dtype.
Si può notare che cell
, proprio come il field del formbuilder accetta un parametro lbl
che permette di dare un’etichetta alla cella. La classe css associata alle etichette di un layout è stata passata nel parametro lbl_class
quando abbiamo definito il layout. Nel metodo di hook defineCustomStyles
posso definire con l’elemento style
tutte le classi css di cui avrò bisogno nella stampa.
Nel blocco datiCliente creaiamo invece tre righe e nell’ultima andiamo a scrivere su tre righe i dati del cliente.
Si può anche vedere che con il metodo field
non solo possiamo accedere ai dati del record fattura, ma possiamo leggere anche i campi in relazione usando il path separato da punti che percorre le relazioni ascendenti con la sintassi.
@cliente_id.ragione sociale
@cliente_id.@comune_id.denominazione
Definizione della griglia delle righe fattura¶
Questo tipo di stampa, che eredita da TableScriptToHtml
prevede di riferirsi ad un record principale, che in questo caso è quello della fattura stampata e ad una selezione di righe ad esso collegate. Questa selezione viene rappresentata con la griglia centrale. Nel nostro caso questa conterrà le righe fattura.
Nella definizione di questa griglia è innanzitutto fondamentale fornire una descrizione delle colonne. Per fare questo si usa una sintassi del tutto simile a quella che abbiamo già incontrato nella definizione delle view ( Lezione 2 ) dei tablehandler
.
Si deve infatti implementare il metodo gridStruct
in modo analogo a come veniva implementato il metodo th_struct
.
def gridStruct(self,struct):
r = struct.view().rows()
r.fieldcell('prodotto_id',mm_width=0, name='Prodotto')
r.fieldcell('quantita',mm_width=10)
r.fieldcell('prezzo_unitario',mm_width=20)
r.fieldcell('aliquota_iva',mm_width=20)
r.fieldcell('prezzo_totale',mm_width=20, name='Totale')
r.fieldcell('iva',mm_width=20)
La sola differenza è che qui si specifica come parametro mm_width
che indica la larghezza della colonna invece di width
. Questo a ricordarci che le larghezze qui sono sempre espresse in millimetri.
L’altra cosa da fare è definire quale selezione di record deve essere letta dal database. Il modo più semplice per farlo è passare i parametri di query necessari per leggere i record desiderati. Questo si fa implementando il metodo gridQueryParameters
, il quale ritorna il dizionario dei parametri necessari.
def gridQueryParameters(self):
return dict(relation='@righe')
Questo caso è particolarmente semplice perché ci basta indicare nel parametro relation
il relation_name che collega la table fattura
a quella fattura_riga
. In alternativa avrei potuto essere più esplicito e fornire la table sulla quale fare la query e la condition.
def gridQueryParameters(self):
return dict(table='fatt.fattura_riga', condition='$fattura_id = :f_id', f_id=self.record['id'])
Noterete che in questi parametri non ho definito columns, infatti le colonne sono già implicitamente definite da gridStruct
, nel quale ho già dichiarato quali colonne voglio visualizzare.
Piede fattura¶
Questo tipo di stampa, prevede anche la possibiltà di definire sotto la griglia una zona a fondo pagina. Nel nostro caso la useremo per scrivere i totali della fattura. Per fare questo è sufficiente implementare il metodo di hook docFooter
def docFooter(self, footer, lastPage=None):
l = footer.layout('totali_fattura',top=1,
lbl_class='cell_label',
content_class = 'footer_content')
r = l.row(height=12)
r.cell()
r.cell(self.field('totale_imponibile'),lbl='Imponibile', width=20)
r.cell(self.field('totale_iva'),lbl='IVA', width=20)
r.cell(self.field('totale_fattura'),lbl='Totale', width=20)
Come abbiamo già fatto nella testata definiamo un layout, con una riga e delle celle, nelle quali scriveremo i totali. Notiamo che nel parametro content_class
del layout, utilizziamo una delle due classi definite in precedenza. Il parametro content_class
serve a dire quali classi css devono essere associate al contenuto delle celle che si trovano nel layout.
Abbiamo dunque definito la nostra risorsa di stampa, adesso per poterla vedere dobbiamo definire anche una risorsa di tipo batch che ci permetterà di eseguire la stampa dalla pagina delle fatture.
La risorsa batch di stampa: stampa_fatture.py¶
Andiamo a definire il modulo stampa_fatture.py
che si deve collocare nella directory resources/tables/fattura/print
creando quindi la cartella print. Nella directory print vanno collocate quelle risorse che servono a lanciare l’esecuzione delle stampe.
from gnr.web.batch.btcprint import BaseResourcePrint
caption = 'Stampa Fatture'
class Main(BaseResourcePrint):
batch_title = 'Stampa fattura'
html_res = 'html_res/fattura_stampa'
Questa classe eredita da BaseResourcePrint
che, a meno di non ridefinire alcuni metodi di hook, si occupa di invocare la stampa definita nel parametro html_res
su ciascun record alla selezione corrente.
E nel caso, se sulla selezione corrente a video ci fossero elementi evidenziati, solo ad essi.
La variabile globale caption
serve a fornire il nome con cui questo batch di stampa verrà mostrato dal menu delle stampe disponibili.
L’attributo batch_title
indica invece il titolo che sarà attribuito al batch nel monitor laterale dei batch in esecuzione.
In questo esempio non vogliamo ridefinire alcun metodo di hook e ci atteniamo al comportamento del batch di stampa generico.
Proviamo a lanciare la nostra stampa su una selezione di fatture dalla pagina delle fatture.
Carta Intestata¶
Genropy offre nel package adm un comodo modo di generare carte intestate. Nel video viene mostrato come generare una carta intestata da utilizzare nella fattura.
Chiamiamo la nostra carta intestata con il nome carta_intestata e poi andiamo ad aggiungere alla nostra riga di stampa la seguente riga
templates = 'carta_intestata'
Proviamo a lanciare dunque la stampa e vediamo che ha la carta intestata che abbiamo creato.
Allegati: