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 metodo prepareRow per specificare il comportamento di ogni singola riga della stampa.

Definizione della print action

Per prima cosa andiamo a definire la risorsa print che chiamiamo stampa_fatture.py all’interno della cartella resources/tables/cliente/print, esattamente come abbiamo fatto nella Stampa con griglia 1: Fatturato per esercizio .

Come parametri inseriamo la classica data_inizio e data_fine come abbiamo fatto nella 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 risorsa html_res che chiamiamo stampa_vendite_clienti.py all’interno della cartella resources/tables/cliente/html_res.

Rimandiamo alla 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 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("""<div style='font-size:14px;'><strong>{intestazione}</strong></div>::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 Layout all’interno del quale inseriamo Row e 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 Layout all’interno del quale definiremo una prima Row con le etichette Prodotto, Quantità e Prezzo tot., e in seguito per ogni vendita presente nel dizionario una 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.


Puoi provare la stampa direttamente su Sandbox, oppure scaricare i file di stampa qui di seguito.


Allegati: