.. _le_stampe/stampe_genropy/stampe_risorsa/esempi_stampa/stampa_righe_2: Stampa righe 2: Vendite cliente =============================== Supponiamo di voler stampare non una classica "griglia", ma una serie di blocchetti contenenti una serie di dati per ogni record. In quest'esempio vedremo come definire la risorsa di stampa relativa alle **Vendite per cliente**. La stampa verrà lanciata dalla tabella *Cliente* del progetto `Sandbox `_. In particolare, questo esempio ci permetterà di vedere come stampare **dei blocchetti con i 10 prodotti più venduti per ogni record cliente** della *selezione dei dati*. Per fare questo, utilizzeremo il :ref:`metodo prepareRow` per specificare il comportamento di ogni singola riga della stampa. Definizione della print action ------------------------------ Per prima cosa andiamo a definire la :ref:`risorsa print` che chiamiamo *stampa_fatture.py* all'interno della cartella ``resources/tables/cliente/print``, esattamente come abbiamo fatto nella :ref:`Stampa con griglia 1: Fatturato per esercizio` . Come parametri inseriamo la classica ``data_inizio`` e ``data_fine`` come abbiamo fatto nella :ref:`Stampa griglia 4: Righe Fattura` . Useremo sempre come dati quelli della *selezione corrente*: non prevediamo quindi la definizione del parametro ``use_current_selection`` come avevamo fatto in quel caso ma la diamo per scontata. A tal proposito inseriamo invece un *div* contenente un avviso sul numero dei record che stiamo per stampare:: def table_script_parameters_pane(self, pane, **kwargs): fb = pane.formbuilder(cols=1,border_spacing='3px') fb.div('Stai per stampare i 10 prodotti più venduti a ' + str(kwargs['record_count']) + ' clienti') fb.dateTextBox(value='^.data_inizio',lbl='Dal', period_to='.data_fine') fb.dateTextBox(value='^.data_fine', lbl='Al', default=self.db.workdate) Si noti che in ``kwargs['record_count']`` viene automaticamente immagazzinato il conteggio dei record della selezione. Definizione della risorsa html_res ---------------------------------- Passiamo a questo punto alla definizione della :ref:`risorsa html_res` che chiamiamo *stampa_vendite_clienti.py* all'interno della cartella ``resources/tables/cliente/html_res``. Rimandiamo alla :ref:`Stampa con griglia 1: Fatturato per esercizio` per la trattazione della definizione dei parametri della classe *Main*, dei metodi ``defineCustomStyles``, ``docHeader``, ``docFooter`` e ``outputDocName``, che si comportano sempre allo stesso modo. La particolarità del nostro caso è che non definiremo una ``gridStruct`` come abbiamo fatto finora, ma trattandosi di stampa *custom* la griglia verrà ridefinita con il metodo ``prepareRows``:: def gridStruct(self,struct): r = struct.view().rows() In ``gridData`` effettueremo due query sul database, una sulla tabella delle *righe fattura*, per estrapolare le vendite raggruppate per cliente e per prodotto, l'altra sui clienti, partendo dalla selezione, per individuare i record oggetto della stampa:: def gridData(self): condition = ['$cliente_id IN :pkeys AND @fattura_id.data <= :data_fine'] if self.parameter('data_inizio'): condition.append('@fattura_id.data >= :data_inizio') where = ' AND '.join(condition) self.vendite_per_cliente = self.db.table('fatt.fattura_riga').query(columns="""$cliente_id, @prodotto_id.descrizione AS prodotto, SUM($quantita) AS quantita, SUM($prezzo_totale) AS prezzo_totale""", where = where, pkeys=self.record['selectionPkeys'], data_inizio = self.parameter('data_inizio'), data_fine = self.parameter('data_fine'), group_by='$cliente_id,@prodotto_id.descrizione', order_by='SUM($prezzo_totale) DESC').fetchGrouped('cliente_id') clienti = self.db.table('fatt.cliente').query(columns='$id,$ragione_sociale', where='$id IN :pkeys', pkeys=self.record['selectionPkeys']).fetch() return clienti Si noti che il :ref:`metodo gridData` ritornerà la bag piatta dei record dei clienti, mentre l'altra query "salverà" in *self* un dizionario che ha come chiave il ``cliente_id`` e come valori le liste di vendite per cliente e prodotto, come risultato della ``fetchGrouped('cliente_id')``. A questo punto possiamo procedere con la ridefinizione di ``prepareRow``:: def prepareRow(self, row): l = row.cell().layout(top=2, bottom=2, left=1, right=1, border_width=0, font_size='9pt', style='text-indent:1mm;') cliente_row = l.row(height=self.cliente_height) intestazione = "I 10 prodotti più venduti a {cliente}".format(cliente=self.rowField('ragione_sociale')) if self.parameter('data_inizio'): intestazione = intestazione + " dal {data_inizio} a {data_fine}".format(data_inizio=self.parameter('data_inizio'), data_fine=self.parameter('data_fine')) cliente_row.cell("""
{intestazione}
::HTML""".format( intestazione=intestazione)) cliente_id = self.rowField('id') vendite_cliente = self.vendite_per_cliente.get(cliente_id) if not vendite_cliente: nota_row = l.row(height=self.cliente_height) nota_row.cell('Nessuna vendita al cliente') return else: vendite_cliente = vendite_cliente[:10] Creiamo quindi un :ref:`Layout` all'interno del quale inseriamo :ref:`Row` e :ref:`Cell` che facciano da **intestazione con la ragione sociale del cliente**. Recuperando poi il dizionario delle vendite per cliente e prodotto che abbiamo costruito in ``gridData``, valutiamo se ci sono dati o meno da mostrare: in caso negativo mostriamo un avviso "Nessuna vendita al cliente", in caso positivo prendiamo i primi 10 elementi e procediamo con la generazione di una griglia:: vendite_row = l.row(height=self.grid_row_height * (len(vendite_cliente))) venditeLayout = vendite_row.cell().layout(name='vendite', border_width=0.3, left=1, style='text-indent:1mm;') header_row = venditeLayout.row(background='grey', color='white',height=5) header_row.cell('Prodotto', content_class='aligned_center', width=0) header_row.cell('Quantità', content_class='aligned_center', width=20) header_row.cell('Prezzo tot.', content_class='aligned_center', width=20) for vendita in vendite_cliente: r = venditeLayout.row(height=5) r.cell(vendita['prodotto'], width=0) r.cell(self.toText(vendita['quantita'], format='#,###'), width=20) r.cell(self.toText(vendita['prezzo_totale'], format=self.currencyFormat), width=20) Costruiamo quindi un altro :ref:`Layout` all'interno del quale definiremo una prima :ref:`Row` con le etichette *Prodotto*, *Quantità* e *Prezzo tot.*, e in seguito per ogni vendita presente nel dizionario una :ref:`Row` con i dati. Si noti che al secondo *layout* viene aggiunto un margine sinistro ``left=1``, questo per evitare la sovrapposizione con altri layer che utilizzano il ``border_width=0`` standard. .. raw:: html
Puoi provare la stampa direttamente su `Sandbox `_, oppure scaricare i file di stampa qui di seguito. .. raw:: html
**Allegati:** - `print_vendite_clienti `_ - `stampa_vendite_clienti `_ - `vendite-per-cliente-dal-2019-01-01-a-2019-12-31 `_