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 è :
- L'azienda creditrice prepara il flusso Ri.Ba. e lo invia alla banca
- La banca o l'azienda, a seconda degli accordi, stampa e invia gli avvisi al cliente
- 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.
HEADERCampo 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.
FOOTERCampo 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 Record14 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.