Preparazione file per flussi Ri.Ba. secondo il tracciato CBI
a cura di Luciano Bastianello (requisiti: conoscenza generica di .Net)

Premessa
La ricevuta bancaria (Ri.Ba.)è uno dei metodi di pagamento previsti per regolare i rapporti tra cliente e fornitore.
Il funzionamento è molto semplice: viene prodotto e stampato un tagliando, promemoria della scadenza di un debito.
Alla scadenza viene addebitato il conto del debitore e accreditato il conto del creditore.
Il normale flusso delle operazioni è :

  1. L'azienda creditrice prepara il flusso Ri.Ba. e lo invia alla banca
  2. La banca o l'azienda, a seconda degli accordi, stampa e invia gli avvisi al cliente
  3. Alla scadenza, la banca, a seconda degli accordi, prepara un flusso per l'azienda cliente dell'esito di ciascun pagamento

Questo articolo mostra come creare il file Ri.Ba. da inviare alla banca.

La struttura Ri.Ba.
Secondo gli accordi presi da banche e aziende clienti, per ciascuna tipologia di comunicazione è prevista una struttura particolare che ne descrive le particolarità.

Tutti i flussi sono definiti con righe (record) a lunghezza fissa di 120 caratteri.
La prima riga è il record di intestazione (header) del flusso, sostanzialmente contiene le informazioni generali relative al flusso.

HEADER
Campo Valore Descrizione
Tipo Record IB Valore fisso "IB" come qualificatore flusso Ri.Ba.
Mittente Codice SIA Azienda Codice SIA dell'azienda Mittente
Ricevente Codice ABI Banca Codice ABI della banca incasso Ri.Ba.

Come ultima riga del flusso è prevista la presenza di un record di coda (footer) che ha lo scopo sostanziale di "validare" gli elementi presenti nel flusso certificando che il flusso è completo.

FOOTER
Campo Descrizione
Tipo Record Valore fisso "EF" come qualificatore di fine flusso
Mittente Codice SIA dell'azienda Mittente
Ricevente Codice ABI della banca incasso Ri.Ba.
Importi Sommatoria degli importi dei documenti Ri.Ba.
Numero Righe Conteggio delle righe presenti nel flusso
Numero Ricevute Numero Ri.Ba.

Per ogni Ricevuta Bancaria sono previsti 7 record:

Tipo Record
14 Ciascuno di questi descrive una parte delle informazioni concernenti il rapporto di credito / debito, chi deve pagare, quando e dove.
20
30
40
50
51
70

La descrizione completa del tracciato Ri.Ba. CBI è codificata insieme a tutte le altre convenzioni di flusso nel documento "CBI-RIB-001.doc" scaricabile all'indirizzo Associazione CBI - Standard Tecnici.

La struttura dei dati
Per l'interfacciamento con i dati delle Ri.Ba. ho utilizzato due tabelle:

Tabella Descrizione
TestataDisposizioni Definizione dei dati generali per ciascun flusso. La chiave principale è un numero progressivo che è ripreso nelle righe come prima parte della PK (Primary Key)
RigheDisposizioni Dettaglio invio Ri.Ba.: una riga contiene una scadenza. La PK è composta dal numero progressivo di invio e da un numero progressivo di riga. La PK garantisce anche l'ordinamento dei dati nell'estrazione

La struttura del programma
Tramite il wizard di Visual Studio 2005 ho generato il DataSet tipizzato e i TableAdapter che ho utilizzato nella mia applicazione.
La realizzazione ha comportato la scrittura di un progetto con i moduli principali

Modulo Descrizione
MainForm Form principale dell'applicazione
GeneraDatiRibaForm Form di interfaccia per la creazione dei dati di prova
GeneraFileRibaForm Form di interfaccia per la creazione del file di flusso
DatiCbi Classe helper per il file di flusso

La form principale
La MainForm contiene:

Campo Descrizione
ImageMenu ImageList contenente le icone necessarie al programma
LvMenu ListView che interfaccia MainForm con le altre due form.
Nel campo Tag dei ListViewItem ho impostato il nome della form da caricare in seguito al doppio click sull'icona corrispondente.
ApriFormByName Metodo che consente di aprire una form passandone il nome
lvMenu_DoubleClick Gestore dell'evento doppio click sulla ListView

Questo è il codice (C# e VB) dei due metodi:

    private void ApriFormByName(string formName, string namespaceName)
    {
      string fullName = namespaceName;
      try
      {
        if (namespaceName == null)
          fullName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;
        if (!fullName.EndsWith("."))
          fullName += ".";
        fullName += formName;
        Type oFormType = Type.GetType(fullName, true, true);
        ((Form)oFormType.GetConstructor(System.Type.EmptyTypes).Invoke(null)).Show();
      }
      catch (Exception ex)
      {
        MessageBox.Show(ex.Message + " " + ex.StackTrace);
      }

    }

    private void lvMenu_DoubleClick(object sender, EventArgs e)
    {
      // ListViewItem it = ;
      string nomeForm = (string)lvMenu.SelectedItems[0].Tag;
      ApriFormByName(nomeForm, null);
    }
  Private Sub ApriFormByName(ByVal formName As String, ByVal namespaceName As String)
    Dim fullName As String = namespaceName
    Try
      If namespaceName Is Nothing Then
        Dim asm As System.Reflection.Assembly = System.Reflection.Assembly.GetExecutingAssembly()
        fullName = asm.GetName.Name
        'fullName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name
      End If
      If Not fullName.EndsWith(".") Then
        fullName += "."
      End If
      fullName += formName
      Dim oFormType As Type = Type.GetType(fullName, True, True)
      CType(oFormType.GetConstructor(System.Type.EmptyTypes).Invoke(Nothing), Form).Show()
    Catch ex As Exception
      MessageBox.Show(ex.Message + " " + ex.StackTrace)
    End Try

  End Sub

  Private Sub lvMenu_DoubleClick(ByVal sender As Object, ByVal e As EventArgs) _
                                 Handles lvMenu.DoubleClick
    ' ListViewItem it = ;
    Dim nomeForm As String = CType(lvMenu.SelectedItems(0).Tag, String)
    ApriFormByName(nomeForm, Nothing)
  End Sub
La form per la generazione dei dati di prova  


Questa form contiene una normalissima creazione di TestataDisposizioni e RigheDisposizioni con dati casuali.

Come si può notare, i dati sono palesemente falsati, perché non possano in alcun modo ricondurre a situazioni reali. Eventuali utilizzatori in condizioni reali dovranno provvedere in proprio a inserire i dati nel database.

Questo il codice della procedura di creazione dati:

    private void CreaDatiRiba()
    {
      int righe = 0;
      righe = CheckValore(righeTextBox);
      if (righe <= 0)
        return;
      testataTableAdapter.Fill(dsDati.TestataDisposizioni);
      int idTestata = dsDati.TestataDisposizioni.Rows.Count;
      if (idTestata > 0)
      {
        idTestata = (int)dsDati.TestataDisposizioni.Rows[idTestata - 1]["IdInvio"];
      }
      idTestata += 1;

      daticbiDataSet.TestataDisposizioniRow drTestata =
                     (daticbiDataSet.TestataDisposizioniRow)dsDati.TestataDisposizioni.NewRow();

      drTestata.AbiAzienda = rnd.Next(1, 99999);
      drTestata.CabAzienda = rnd.Next(1, 99999);
      drTestata.CodiceSia = "00SIA";
      drTestata.CodificaFiscale = "11111111016";
      drTestata.ContoAzienda = "123456";
      drTestata.DataAutorizzazione = DateTime.Now;
      drTestata.DataDisposizioni = DateTime.Now;
      drTestata.Descrizione = string.Format("{0} {1}-{2}", DateTime.Now, drTestata.AbiAzienda,
                                            drTestata.CabAzienda);
      drTestata.DescrizioneAzienda = "Azienda di prova";
      drTestata.IdInvio = idTestata;
      drTestata.NomeSupporto = string.Format("INVIO DEL {0}", DateTime.Now);
      drTestata.NumeroAutorizzazione = rnd.Next(1, 200000).ToString();
      drTestata.ProvinciaFinanza = "XX";
      drTestata.RagioneSociale = drTestata.DescrizioneAzienda;
      dsDati.TestataDisposizioni.Rows.Add(drTestata);
      for (int k = 0; k < righe; k++)
      {
        daticbiDataSet.RigheDisposizioniRow drRighe =
                       (daticbiDataSet.RigheDisposizioniRow)dsDati.RigheDisposizioni.NewRow();
        drRighe.AbiCliente = rnd.Next(1, 99999);
        drRighe.CabCliente = rnd.Next(1, 99999);
        drRighe.CapDebitore = rnd.Next(1, 99999).ToString();
        drRighe.ChiaveControllo = " ";
        drRighe.CodiceDebitore = rnd.Next(10000, 99999).ToString();
        drRighe.CodiceFiscaleDebitore = GetCodiceFiscale();
        drRighe.DataDocumento = DateTime.Now.AddDays(rnd.Next(10, 90));
        drRighe.DataScadenza = drRighe.DataDocumento.AddDays(60);
        drRighe.DescrizioneDebitore = string.Format("Ragione sociale {0}", drRighe.CodiceDebitore);
        drRighe.IdDisposizione = idTestata;
        drRighe.IdRiga = k + 1;
        drRighe.Importo = (decimal)rnd.NextDouble() * rnd.Next(1000, 10000);
        drRighe.IndirizzoDebitore = string.Format("Via Del debitore {0}", rnd.Next(1, 250));
        drRighe.LocalitaDebitore = string.Format("Località debitore {0}", drRighe.CodiceDebitore);
        drRighe.NumeroDocumento = rnd.Next(500, 500000).ToString();
        drRighe.NumeroRicevuta = k + 1;
        drRighe.RichiestaEsito = 0;
        drRighe.SiglaProvincia = "XX";
        drRighe.StampaAvviso = 0;
        drRighe.TipoCodice = 4;
        drRighe.TipoDocDebitore = 0;
        dsDati.RigheDisposizioni.Rows.Add(drRighe);
      }
      testataTableAdapter.Update(dsDati.TestataDisposizioni);
      righeTableAdapter.Update(dsDati.RigheDisposizioni);
      MessageBox.Show("Dati generati");

    }
  Private Sub CreaDatiRiba()
    Dim righe As Integer = 0
    righe = CheckValore(righeTextBox)
    If righe <= 0 Then
      Return
    End If
    TestataTableAdapter.Fill(dsDati.TestataDisposizioni)
    Dim idTestata As Integer = dsDati.TestataDisposizioni.Rows.Count
    If idTestata > 0 Then
      idTestata = CInt(dsDati.TestataDisposizioni.Rows(idTestata - 1)("IdInvio"))
    End If
    idTestata += 1

    Dim drTestata As daticbiDataSet.TestataDisposizioniRow = _
              DirectCast(dsDati.TestataDisposizioni.NewRow(), daticbiDataSet.TestataDisposizioniRow)

    drTestata.AbiAzienda = rnd.[Next](1, 99999)
    drTestata.CabAzienda = rnd.[Next](1, 99999)
    drTestata.CodiceSia = "00SIA"
    drTestata.CodificaFiscale = "11111111016"
    drTestata.ContoAzienda = "123456"
    drTestata.DataAutorizzazione = DateTime.Now
    drTestata.DataDisposizioni = DateTime.Now
    drTestata.Descrizione = String.Format("{0} {1}-{2}", DateTime.Now, drTestata.AbiAzienda, _
                                          drTestata.CabAzienda)
    drTestata.DescrizioneAzienda = "Azienda di prova"
    drTestata.IdInvio = idTestata
    drTestata.NomeSupporto = String.Format("INVIO DEL {0}", DateTime.Now)
    drTestata.NumeroAutorizzazione = rnd.[Next](1, 200000).ToString()
    drTestata.ProvinciaFinanza = "XX"
    drTestata.RagioneSociale = drTestata.DescrizioneAzienda
    dsDati.TestataDisposizioni.Rows.Add(drTestata)
    For k As Integer = 0 To righe - 1
      Dim drRighe As daticbiDataSet.RigheDisposizioniRow = _
                  DirectCast(dsDati.RigheDisposizioni.NewRow(), daticbiDataSet.RigheDisposizioniRow)
      drRighe.AbiCliente = rnd.[Next](1, 99999)
      drRighe.CabCliente = rnd.[Next](1, 99999)
      drRighe.CapDebitore = rnd.[Next](1, 99999).ToString()
      drRighe.ChiaveControllo = " "
      drRighe.CodiceDebitore = rnd.[Next](10000, 99999).ToString()
      drRighe.CodiceFiscaleDebitore = GetCodiceFiscale()
      drRighe.DataDocumento = DateTime.Now.AddDays(rnd.[Next](10, 90))
      drRighe.DataScadenza = drRighe.DataDocumento.AddDays(60)
      drRighe.DescrizioneDebitore = String.Format("Ragione sociale {0}", drRighe.CodiceDebitore)
      drRighe.IdDisposizione = idTestata
      drRighe.IdRiga = k + 1
      drRighe.Importo = CDec(rnd.NextDouble()) * rnd.[Next](1000, 10000)
      drRighe.IndirizzoDebitore = String.Format("Via Del debitore {0}", rnd.[Next](1, 250))
      drRighe.LocalitaDebitore = String.Format("Località debitore {0}", drRighe.CodiceDebitore)
      drRighe.NumeroDocumento = rnd.[Next](500, 500000).ToString()
      drRighe.NumeroRicevuta = k + 1
      drRighe.RichiestaEsito = 0
      drRighe.SiglaProvincia = "XX"
      drRighe.StampaAvviso = 0
      drRighe.TipoCodice = 4
      drRighe.TipoDocDebitore = 0
      dsDati.RigheDisposizioni.Rows.Add(drRighe)
    Next
    TestataTableAdapter.Update(dsDati.TestataDisposizioni)
    RigheTableAdapter.Update(dsDati.RigheDisposizioni)
    MessageBox.Show("Dati generati")

  End Sub
La form per la generazione del file di flusso Riba


Contiene i seguenti controlli:

Nome Descrizione
fileButton Usa la finestra di dialogo SaveFileDialog per far scegliere il nome del file da generare, e lo scrive nella fileTextBox
fileTextBox Contiene il nome del file
invioComboBox Associata alla datatable TestateDisposizioni
generaButton Avvia la creazione del file Ri.Ba.

e il metodo GeneraInvio, che fa uso della classe DatiCbiRiba.

Classe DatiCbiRiba
Si tratta di una classe costruita appositamente per fornire una interfaccia tra le tabelle TestataDisposizioni e RigheDisposizioni.
Le uniche cose degne di nota sono i metodi:

Nome Descrizione
RecordIB() Genera il testo del record header
RecordEF() Genera il testo del record footer
Record14() Genera il testo del record 14
Record20() Genera il testo del record 20
Record30() Genera il testo del record 30
Record40() Genera il testo del record 40
Record50() Genera il testo del record 50
Record51() Genera il testo del record 51
Record70() Genera il testo del record 70

Riferimenti:

Conclusioni
Nell'informare che i sorgenti di esempio (sia C# che VB.Net) sono scaricabili dall'Area Download, mi preme avvertire che il flusso generato dal programma non ha mai avuto il "battesimo del fuoco"; data la complessità sono convinto che ci siano alcuni (speriamo pochi) errori. Quindi ringrazio in anticipo se qualcuno farà questa prova e me ne comunicherà i risultati, lasciando un feedback al mio blog relativo a questo articolo.