Dynamics 365 Business Central

Coding, Ideas

A new (old) app idea, bank layouts

This time I want to write about a problem that affects every Latam implementation, the bank payments.

Unlike Europe and countries with more organized banking systems, in Latam we have a chaotic bank system in which every bank creates and use their own “schema” to accept the payment transactions.

Some bank even has multiple format definitions based in the operation type, vendor payment, payroll payment, other bank payment, and others.

I saw some “payment solutions” in the appsource, I don’t know how much cost those apps but is very difficult to understand why we don’t have similar solutions from Latam partners.

I believe that Latam partners work with banks, I mean, they have bank accounts and know someone in the bank, maybe the people which manages their accounts, so, why don’t ask that person for the bank layouts, and develop an extension for that?

Yes, I know about the Host2Host service but you can use that service or upload a txt file to the bank site.

I’m going to focus on the txt file this time.

First, you need to decide if you want only one codeunit or one per format or bank, this time I chose only one codeunit to rule them all layouts.

I created a “base payment table” to put the information there in the first place and then start witht he txt file. This allows me to grow the solution and add more fields to add more banks and avoid to add fields in the General Journal Line table keeping that table clean.

/// <summary>
/// Table ExportPayment (ID 58100).
/// </summary>
table 58100 ExportPayment
{
    Caption = 'ExportPayment';
    DataClassification = CustomerContent;

    fields
    {
        field(1; "Line No"; Integer)
        {
            Caption = 'Line No';
            DataClassification = CustomerContent;
        }
        field(2; "Origin Bank"; Code[20])
        {
            Caption = 'Origin Bank';
            DataClassification = CustomerContent;
        }
        field(3; "Origin Account No"; Text[30])
        {
            Caption = 'Origin Account No';
            DataClassification = CustomerContent;
        }
        field(4; "Origin Branch No"; Text[20])
        {
            Caption = 'Origin Branch No';
            DataClassification = CustomerContent;
        }
        field(5; "Origin Bank Place"; Text[20])
        {
            Caption = 'Origin Bank Place';
            DataClassification = CustomerContent;
        }
        field(7; "Origin Bank Currency"; Code[10])
        {
            Caption = 'Origin Bank Currency';
            DataClassification = CustomerContent;
        }
        field(8; "Origin Bank Country"; Code[10])
        {
            Caption = 'Origin Bank Country';
            DataClassification = CustomerContent;
        }
        field(9; "Origin Bank Export Code"; Enum CCExpBankCodes)
        {
            Caption = 'Origin Bank Export Code';
            DataClassification = CustomerContent;
        }
        field(10; "Bank Agreement"; Code[9])
        {
            Caption = 'Bank Agreement';
            DataClassification = CustomerContent;
        }
        field(11;"Payment Reference"; Text[30])
        {
            Caption = 'Payment Reference';
            DataClassification = CustomerContent;
        }
        field(12; Description; Text[50])
        {
            Caption = 'Description';
            DataClassification = CustomerContent;
        }
        field(20; "Vendor No"; Code[20])
        {
            Caption = 'Vendor No';
            DataClassification = CustomerContent;
        }
        field(21; "Vendor ID "; Code[20])
        {
            Caption = 'Vendor ID ';
            DataClassification = CustomerContent;
        }
        field(22; "Invoice to Pay"; Code[20])
        {
            Caption = 'Invoice to Pay';
            DataClassification = CustomerContent;
        }
        field(23; "Vendor Bank"; Code[20])
        {
            Caption = 'Vendor Bank';
            DataClassification = CustomerContent;
        }
        field(24; "Vendor Bank Account No"; Text[30])
        {
            Caption = 'Vendor Bank Account No';
            DataClassification = CustomerContent;
        }
        field(25; "Destination Bank Branch"; Text[20])
        {
            Caption = 'Destination Bank Branch';
            DataClassification = CustomerContent;
        }
        field(26; "Destination Bank Place"; Text[20])
        {
            Caption = 'Destination Bank Place';
            DataClassification = CustomerContent;
        }
        field(27; "Payment Currency Code"; Code[10])
        {
            Caption = 'Payment Currency Code';
            DataClassification = CustomerContent;
        }
        field(28; "Destination Bank Country"; Code[10])
        {
            Caption = 'Destination Bank Country';
            DataClassification = CustomerContent;
        }
        field(29; "Destination Bank Export Code"; Enum CCExpBankCodes)
        {
            Caption = 'Destination Bank Export Code';
            DataClassification = CustomerContent;
        }
        field(30; "Payment Amount"; Decimal)
        {
            Caption = 'Payment Amount';
            DataClassification = CustomerContent;
        }
        field(36;"Vendor Name";Text[50])
        {
            Caption = 'Vendor Name';
            DataClassification = CustomerContent;
        }
        field(40; BBVAOperType; Text[1])
        {
            Caption = 'BBVAOperType';
            DataClassification = CustomerContent;
        }
        field(41; BBVARefNo; Text[25])
        {
            Caption = 'BBVARefNo';
            DataClassification = CustomerContent;
        }
        field(42; BBVAAccType; Text[2])
        {
            Caption = 'BBVAAccType';
            DataClassification = CustomerContent;
        }
        field(60; AreSameBank; Boolean)
        {
            Caption = 'AreSameBank';
            DataClassification = CustomerContent;
        }
        field(61; AreSameCurrency; Boolean)
        {
            Caption = 'AreSameCurrency';
            DataClassification = CustomerContent;
        }
        field(62; AreSameCountry; Boolean)
        {
            Caption = 'AreSameCountry';
            DataClassification = CustomerContent;
        }
        field(100; "Exported to Payment File"; Boolean)
        {
            Caption ='Exported to Payment File';
            DataClassification = CustomerContent;
        }
    }
    keys
    {
        key(PK; "Line No")
        {
            Clustered = true;
        }
    }

}

I call everything from the payment journal so my first procedure to call in every export is the next code and then, create the txt file.

/// <summary>
    /// PrepareExport.
    /// </summary>
    /// <param name="JTN">Code[10].</param>
    /// <param name="JBN">Code[10].</param>
    procedure PrepareExport(JTN: Code[10]; JBN: Code[10])
    var
        ExpData: Record ExportPayment;
        GenJnlLine: Record "Gen. Journal Line";
    begin
        ExpData.Reset();
        if ExpData.FindSet() then
            ExpData.DeleteAll();

        GenJnlLine.Reset();
        GenJnlLine.SetRange("Journal Template Name", JTN);
        GenJnlLine.SetRange("Journal Batch Name", JBN);
        GenJnlLine.SetRange("Exported to Payment File", false);
        if GenJnlLine.FindSet() then
            repeat
                ExpData."Line No" := GenJnlLine."Line No.";
                ExpData."Origin Bank" := GenJnlLine."Bal. Account No.";
                ExpData."Origin Account No" := GetBankAccNo(GenJnlLine."Bal. Account No.");
                ExpData."Origin Branch No" := GetBankBranch(GenJnlLine."Bal. Account No.");
                ExpData."Origin Bank Place" := GetBankPlace(GenJnlLine."Bal. Account No.");
                ExpData."Origin Bank Currency" := GetBankCurr(GenJnlLine."Bal. Account No.");
                ExpData."Origin Bank Country" := GetBankCountry(GenJnlLine."Bal. Account No.");
                ExpData."Origin Bank Export Code" := GetBankExpCode(GenJnlLine."Bal. Account No.");
                ExpData.Description := GenJnlLine.Description;
                ExpData."Bank Agreement" := GetBankAgr(GenJnlLine."Bal. Account No.");
                ExpData."Vendor No" := GenJnlLine."Account No.";
                ExpData."Vendor Name" := GetVendorName(GenJnlLine."Account No.");
                ExpData."Vendor ID " := GetVendorID(GenJnlLine."Account No.");
                ExpData."Invoice to Pay" := GenJnlLine."Applies-to Doc. No.";
                ExpData."Vendor Bank" := GenJnlLine."Recipient Bank Account";
                ExpData."Vendor Bank Account No" := GetVendBankAcc(GenJnlLine."Account No.", GenJnlLine."Recipient Bank Account");
                ExpData."Destination Bank Branch" := '';
                ExpData."Destination Bank Place" := '';
                ExpData."Destination Bank Country" := GetVendBankCountry(GenJnlLine."Account No.", GenJnlLine."Recipient Bank Account");
                ExpData."Destination Bank Export Code" := GetVendBankExpCode(GenJnlLine."Account No.", GenJnlLine."Recipient Bank Account");
                if GenJnlLine."Currency Code" <> '' then
                    ExpData."Payment Currency Code" := GenJnlLine."Currency Code"
                else
                    ExpData."Payment Currency Code" := 'MXN';
                ExpData."Payment Amount" := GenJnlLine.Amount;

                //Here starts a new section to define some options for each bank in the codeunit
                if ExpData."Origin Bank Export Code" = ExpData."Destination Bank Export Code" then
                    ExpData.AreSameBank := true
                else
                    ExpData.AreSameBank := false;

                if ExpData."Origin Bank Country" = ExpData."Destination Bank Country" then
                    ExpData.AreSameCountry := true
                else
                    ExpData.AreSameCountry := false;
                if ExpData."Origin Bank Currency" = ExpData."Payment Currency Code" then
                    ExpData.AreSameCurrency := true
                else
                    ExpData.AreSameCurrency := false;

                if ExpData.AreSameCountry = true then
                    ExpData.BBVAOperType := '2'
                else
                    ExpData.BBVAOperType := '4';

                if (ExpData.AreSameCurrency = true) and (ExpData.AreSameBank = true) then
                    ExpData.BBVAOperType := '2'
                else
                    ExpData.BBVAOperType := '6';

                ExpData.Insert(true);

            until GenJnlLine.Next() = 0;
    end;

Let’s look one of the most used banks in Mexico, BBVA.

They have a 1,366 positions layout, this layout has a 2 sections, header, and detail. The detail sections contain 106 fields that sums those 1366 positions and covers almost any BBVA supported transaction.

This code contains all fields for bbva layout

    /// <summary>
    /// CreateLayout1.
    /// </summary>
    /// <param name="JTN">Code[10].</param>
    /// <param name="JBN">Code[10].</param>
    procedure CreateBBVA(JTN: Code[10]; JBN: Code[10])
    var
        InStr: InStream;
        OutStr: OutStream;
        tmpblob: Codeunit "Temp Blob";
        FileName: Text;
        CRLF: Text[2];
        SpaceStr: Text;
        ZeroStr: Text;
        TotalAmnt: Decimal;
        ExpPaym: Record ExportPayment;

    begin
        CRLF[1] := 13;
        CRLF[2] := 10;
        FileName := 'BBVA.txt';
        SpaceStr := ' ';
        TotalAmnt := 0;

        ExpPaym.Reset();
        ExpPaym.SetRange("Exported to Payment File", false);
        if ExpPaym.FindSet() then begin
            tmpBlob.CreateOutStream(OutStr, TextEncoding::Windows);
            OutStr.WriteText(
            //>> Start Header/Inicia Header
            'H' + //1 Record type/Indicador del registro
            Format(ExpPaym."Bank Agreement").PadLeft(9, '0') + //2 Arrangement/Convenio
            Format(WorkDate(), 0, 9) + //3 Send Date/Fecha de envio
            '01' + //4 Recipient Type/Tipo de validación del tercero
            PadStr('PAGO' + Format(WorkDate, 0, '<Year4><Month,2><Day,2>'), 30, ' ') + //5 file name/Clave del archivo
            '00' + //6 Response Code/Código de respuesta
            PadStr(SpaceStr, 20, ' ') + //7 Response Description/Descripción código de respuesta
            PadStr(SpaceStr, 3, ' ') + //8 Channel/Canal
            PadStr(SpaceStr, 35, ' ') + //9 CHarge Account/Cuenta de cargo
            PadStr(SpaceStr, 3, ' ') +//10 Arrangement Currency/Divisa del convenio
            PadStr(SpaceStr, 1251, ' ') +//11 Filler 1251 spaces/espacios
            CRLF
            );
            //<<End Header/termina header
            repeat
                OutStr.WriteText(
                //>>Start Detail/Inicia Detalle 
                'D' + //1 Record number/Indicador del registtro
                'A' + //2 Instruction/Instrucción
                'P' + //3 Document Type/Tipo de documento
                PadStr(ExpPaym."Invoice to Pay", 20, ' ') + //4 Reference/Referencia SIT
                PadStr(ExpPaym."Vendor No", 30, ' ') + //5 Vendor No, add empty spaces/Clave de proveedor o concepto o datos RSTM TODO, revisar el largo de la cuenta proveedor y sumarle a 30 padstr
                'PDA' + //6 Service Type/Tipo de servicio
                ExpPaym.BBVAOperType + //7 Operation Code/Código de operación
                PadStr(ZeroStr, 20, ' ') + //8 Charge Account and vendor No for SPID/Cuenta de cargo y RFC del proveedor para SPID
                PadStr(SpaceStr, 15, ' ') + //9 Filler
                PadStr(SpaceStr, 25, ' ') + //10 Numerical reference/Referencia Numérica o Leyenda personalizada de CARGO/Concepto o Codigo de seguridad y folio identificador RSTM
                PadStr('PAGO' + Format(ExpPaym."Invoice to Pay"), 37, ' ') + //11 Pay Reason/Motivo de pago o Leyenda personalizada de CARGO/ Referencia Amplia TODO, revisar el largo de la cuenta proveedor y sumarle a 30 padstr
                PadStr(SpaceStr, 15, ' ') + //12 Filler
                PadStr(ExpPaym."Payment Reference", 25, ' ') + //13 Payment Description/Leyenda personalizada de ABONO/CONCEPTO
                PadStr(ExpPaym."Payment Reference", 37, ' ') + //14 CIE Concept/Concepto CIE O Leyenda personalizada de ABONO Referencia Amplia
                'N' + //15 Fiscal Receipt/Comprobante fiscal
                PadStr(SpaceStr, 8, ' ') + //16 Filler
                '40' +//17 Account Type/Tipo de cuenta
                PadStr(SpaceStr, 35, ' ') + //18 Credit Account/Cuenta de abono
                PadStr(SpaceStr, 40, ' ') + //19 Receipt Name/Nombre del 1er Beneficiario o Nombre del titular de la cuenta de abono
                PadStr(SpaceStr, 1, ' ') + //20 Receipt ID Code/Clave de identificación del 1er Beneficiario
                PadStr(SpaceStr, 30, ' ') + //21 Receipt ID Number/Número identificación 1er Beneficiario
                PadStr(SpaceStr, 40, ' ') + //22 Receipt Name/Nombre del 2do Beneficiario o Nombre comercial de la empresa
                PadStr(SpaceStr, 1, ' ') + //23 2nd Receipt ID Code/Clave de identificación del 2do Beneficiario
                PadStr(SpaceStr, 30, ' ') + //24 2nd Receipt ID Number/Número identificación 2do Beneficiario o CURP del Beneficiario para RSTM O Clave de rastreo para interbanciarios
                ExpPaym."Payment Currency Code" + //25 Destination transfer currency/Divisa de la transferencia destino
                PadStr(SpaceStr, 11, ' ') + //26 BIC CODE/CLAVE BIC
                PadStr(SpaceStr, 9, ' ') + //27 ABA CODE/CLAVE ABA
                'FA' + //28 Document Code/Clave de documento
                Format(ExpPaym."Payment Amount" * 100, 0, 2).PadLeft(15, '0') + //29 Payment Amount/Importe del documento
                PadStr(ZeroStr, 15, '0') + //30 Filler
                '01' + //31 Confirmation Type/Tipo de confirmación
                PadStr(SpaceStr, 50, ' ') + //32 Email or celullar
                'N' + //33 Interest frequency/Periodicidad de interes
                PadStr(ZeroStr, 8, '0') + //34 Filler Núm (2.6)
                PadStr(ZeroStr, 4, '0') + //35 Filler Núm (2.2)
                Format(WorkDate(), 0, 9) + //36 Payment Date/Fecha de pago
                '0001-01-01' + //37 Effective date/Fecha de vigencia
                '0001-01-01' + //38 Grace date/Fecha de gracia
                '0001-01-01' + //39 Effective date/Fecha de vigencia
                Format(WorkDate(), 0, 9) + //40 Document Date/Fecha del documento
                'N' + //41 Periodic Payment Indicator/Indicador del pago recurrente
                PadStr(SpaceStr, 1, ' ') + //42 Filler
                '0001-01-01' + //43
                '700' + //44 Payment Detail/Longitud de Datos adicionales o Detalle de pago
                PadStr(SpaceStr, 700, ' ') + //45 Additional Data/Datos adicionales
                PadStr(SpaceStr, 10, ' ') + //46 Filler
                PadStr(SpaceStr, 10, ' ') + //47 Filler
                PadStr(SpaceStr, 2, ' ') + //48 Document Status Code/Código de estatus del documento
                PadStr(SpaceStr, 30, ' ') + //49 Description of the document status code/Descripción del código estatus del documento
                '0001-01-01' + //50 Date of last document event/Fecha de ultimo evento del documento
                CRLF
                );
                TotalAmnt += ExpPaym."Payment Amount";
            until ExpPaym.Next() = 0;
            //<<End Detail/Termina Detalle
            //>>Start summary/Inicia Sumario
            OutStr.WriteText(
            'T' + //1 REcord Indicator/Indicador del registro
            '' + //2 Number of records High MXP (Mexican peso) Number of providers to pay are newrecords validate if payments can be summarized by provider and not n payments for n invoices/Número de registros Altas MXP (peso mexicano) Numero de proveedores a pagar son altas validar si se pueden sumarizar los pagos por proveedor y no n pagos por n facturas
            Format(TotalAmnt, 0, 2).PadLeft(15, '0') + //3 Total amount newrecords MXP sum of invoice payments by supplier/Importe total Altas MXP suma de pagos de facturas por proveedor
            PadStr(ZeroStr, 10, '0') + //4 Number of records Deletions MXP 10 zeros because there are no withdrawals is the registration file (Payments)/Número de registros Bajas MXP 10 ceros por que no hay bajas es archivo de altas (Pagos)
            PadStr(ZeroStr, 215, '0') + //5 al 21
            '' + //22 Number of new records USD (American dollar) how many payments will normally be paid per provider USD or 10 zeros/Número de registros ALTAS USD (dólar americano) cuantos pagos se haran normalmente se paga por proveedor USD o 10 ceros
            '' + //23 Total Amount new records USD sum of invoice payments per supplier USD or 15 zerosImportte Total Altas USD suma de pagos de facturas por proveedor USD o 15 ceros
            PadStr(ZeroStr, 125, '0') + //24 al 33
            '' + //34 Registration number new records EUR how many payments will normally be paid per provider EUR or 10 zerosNúmero de registro Altas EUR cuantos pagos se haran normalmente se paga por proveedor EUR o 10 ceros
            '' + //35 New Records Total Amount EUR/Importe total Altas EUR
            PadStr(ZeroStr, 125, '0') + //36 al 45
            '' + //46 New Records Count CAD/Número de registros Altas CAD
            '' + //47 New Records Total Amount CAD/Importe total Altas CAD
            PadStr(ZeroStr, 125, '0') + //48 al 57
            '' + //58 New Records Count CHF/Número de registros Altas CHF
            '' + //59 New Records Total Amount CHF/Importe total Altas CHF
            PadStr(ZeroStr, 125, '0') + //60 al 69
            '' + //70 New Records Count GBP/Número de registros Altas GBP
            '' + //71 New Records Total Amount GBP/Importe total Altas GBP
            PadStr(ZeroStr, 125, '0') + //72 al 81
            '' + //82 New Records Count SEK/Número de registros Altas SEK
            '' + //83 New Records Total Amount SEK/Importe total Altas SEK
            PadStr(ZeroStr, 125, '0') + //84 al 93
            '' + //94 New Records Count JPY/Número de registros Altas JPY
            '' + //95 New Records Total Amount JPY/Importe total Altas JPY
            PadStr(ZeroStr, 190, '0') + //96 al 106
            //<<Termina Sumario
            CRLF
            );
            tmpBlob.CreateInStream(InStr, TextEncoding::Windows);
            DownloadFromStream(InStr, '', '', '', FileName);
        end;
    end;

when you check the code, you will find references to journal and new table fields, also, you can add code to use more currencies in the summary. Text dates are as BBVA ask it.

Now, let’s check a small layout, this is Banorte and has fewer fields

/// <summary>
    /// CreateBNTE.
    /// </summary>
    /// <param name="JTN">Code[10].</param>
    /// <param name="JBN">Code[10].</param>
    procedure CreateBNTE(JTN: Code[10]; JBN: Code[10])
    var
        InStr: InStream;
        OutStr: OutStream;
        tmpblob1: Codeunit "Temp Blob";
        FileName: Text;
        CRLF: Text[2];
        SpaceStr: Text;
        ZeroStr: Text;
        TotalAmnt: Decimal;
        ExpPaym: Record ExportPayment;
    begin
        CRLF[1] := 13;
        CRLF[2] := 10;
        FileName := 'BNTE.txt';
        SpaceStr := ' ';
        TotalAmnt := 0;

        ExpPaym.Reset();
        ExpPaym.SetRange("Exported to Payment File", false);
        if ExpPaym.FindSet() then begin
            tmpBlob1.CreateOutStream(OutStr, TextEncoding::Windows);
            repeat
                OutStr.WriteText(
                '07' + //1 OPI Operation/Operación OPIs
                PadStr(ExpPaym."Vendor No", 13, ' ') + //2 ID Code/Clave ID
                PadStr(SpaceStr, 10, ' ') + //3 Origin Account/Cuenta Origen
                PadStr(SpaceStr, 20, ' ') + //4 Destination Account CLABE/Cuenta/CLABE Destino
                Format(ExpPaym."Payment Amount", 0, 2).PadLeft(16,'0') + //5 Amount/Importe
                Format(ExpPaym."Payment Reference").PadLeft(7,'0') + //6 Reference/Referencia
                PadStr(ExpPaym.Description, 30, ' ') + //7 Description/Descripción
                PadStr(GetCompRFC, 13, ' ') + //8 Paying TAX ID/RFC Ordenante
                Format((ExpPaym."Payment Amount"-(ExpPaym."Payment Amount"/1.16)),0,2).PadLeft(14,'0') + //9 TAX(VAT)/IVA
                Format(WorkDate, 0, '<Day,2><Month,2><Year4>') + //10 Apply on Date/Fecha aplicación
                'X' + //11 Payment Instruction/Instrucción de pago
                CRLF
                );
            until ExpPaym.Next() = 0;
        end;
        tmpBlob1.CreateInStream(InStr, TextEncoding::Windows);
        DownloadFromStream(InStr, '', '', '', FileName);
    end;

As you can see, I´m using the same source table, so if I need to add more fields because I add a new bank layout, I just simply add the required fields without touch the existing layouts.

Finally, I added a last bank layout, this time Santander

/// <summary>
    /// CreateSANT.
    /// </summary>
    /// <param name="JTN">Code[10].</param>
    /// <param name="JBN">Code[10].</param>
    procedure CreateSANT(JTN: Code[10]; JBN: Code[10])
    var
        InStr: InStream;
        OutStr: OutStream;
        tmpblob2: Codeunit "Temp Blob";
        FileName: Text;
        CRLF: Text[2];
        SpaceStr: Text;
        ZeroStr: Text;
        TotalAmnt: Decimal;
        ExpPaym: Record ExportPayment;
    begin
        CRLF[1] := 13;
        CRLF[2] := 10;
        FileName := 'SANT.txt';
        SpaceStr := ' ';
        TotalAmnt := 0;

        ExpPaym.Reset();
        ExpPaym.SetRange("Exported to Payment File", false);
        if ExpPaym.FindSet() then begin
            tmpBlob2.CreateOutStream(OutStr, TextEncoding::Windows);
            repeat
                OutStr.WriteText(
                PadStr(ExpPaym."Origin Account No", 16, ' ') + //1 Charged account/Cuenta de Cargo
                PadStr(ExpPaym."Vendor Bank Account No", 20, ' ') + //2 Crediot Account/Cuenta de abono / Número Móvil
                PadStr(SpaceStr, 5, ' ') + //3 Bank/Banco
                PadStr(ExpPaym."Vendor Name", 40, ' ') + //4 Receipt/Beneficiario
                PadStr(SpaceStr, 4, ' ') + //5 Branch/Sucursal
                Format(ExpPaym."Payment Amount", 0, 2).PadLeft(16,'0')+ //6 Amount/Importe
                PadStr(SpaceStr, 7, ' ') + //7 City/Plaza
                PadStr(ExpPaym.Description, 40, ' ') + //8 Concept/Concepto
                PadStr(SpaceStr, 90, ' ') + //9 Blank Space/Espacio en Blanco
                PadStr(SpaceStr, 1, ' ') + //10 Fiscal Accoutn Statement/Estado de cuenta fiscal
                PadStr(SpaceStr, 13, ' ') + //11 TAX ID/RFC
                PadStr(ZeroStr, 15, '0') + //12 TAX(VAT)/IVA
                PadStr(ExpPaym."Payment Reference", 7, ' ') + //13 Originator Reference/Referencia Ordenante
                PadStr(SpaceStr, 1, ' ') + //14 Application Form/Forma de aplicación
                PadStr(SpaceStr, 2, ' ') + //15 Payment Type/Tipo de Pago
                PadStr(SpaceStr, 1, ' ') + //16 Application Date/Fecha de aplicación
                CRLF
                );
            until ExpPaym.Next() = 0;
        end;
        tmpBlob2.CreateInStream(InStr, TextEncoding::Windows);
        DownloadFromStream(InStr, '', '', '', FileName);
    end;

And some procedures to get general information for every layout

local procedure GetCompRFC(): Text
    var
        CompInfo: Record "Company Information";
    begin
        CompInfo.Get();
        exit(CompInfo."RFC No.")
    end;

    local procedure GetBankAccNo(BankNo: Code[20]): Text[30]
    var
        Banks: Record "Bank Account";
    begin
        Banks.Reset();
        Banks.SetRange("No.", BankNo);
        if Banks.findset then
            exit(Banks."Bank Account No.")
    end;

    local procedure GetBankBranch(BankNo: Code[20]): Text[30]
    var
        Banks: Record "Bank Account";
    begin
        Banks.Reset();
        Banks.SetRange("No.", BankNo);
        if Banks.findset then
            exit(Banks."Bank Branch No.")
    end;

    local procedure GetBankPlace(BankNo: Code[20]): Text[30]
    var
        Banks: Record "Bank Account";
    begin
        Banks.Reset();
        Banks.SetRange("No.", BankNo);
        if Banks.findset then
            exit(Banks."Bank Account No.")
    end;

    local procedure GetBankAgr(BankNo: Code[20]): Text[30]
    var
        Banks: Record "Bank Account";
    begin
        Banks.Reset();
        Banks.SetRange("No.", BankNo);
        if Banks.findset then
            exit(Banks."Bank Agreement")
    end;

    local procedure GetBankCurr(BankNo: Code[20]): Text[10]
    var
        Banks: Record "Bank Account";
    begin
        Banks.Reset();
        Banks.SetRange("No.", BankNo);
        if Banks.findset then
            if banks."Currency Code" <> '' then
                exit(Banks."Currency Code")
            else
                exit('MXN');
    end;

    local procedure GetBankCountry(BankNo: Code[20]): Text[10]
    var
        Banks: Record "Bank Account";
    begin
        Banks.Reset();
        Banks.SetRange("No.", BankNo);
        if Banks.findset then
            exit(Banks."Country/Region Code")
    end;

    local procedure GetBankExpCode(BankNo: Code[20]): Enum CCExpBankCodes
    var
        Banks: Record "Bank Account";
    begin
        Banks.Reset();
        Banks.SetRange("No.", BankNo);
        if Banks.findset then
            exit(Banks."Bank Export Code");
    end;

    local procedure GetVendorID(VendNo: Code[20]): Code[20]
    var
        Vend: Record Vendor;
    begin
        Vend.Reset();
        Vend.SetRange("No.", VendNo);
        if Vend.FindSet() then
            exit(Vend."RFC No.");
    end;
    local procedure GetVendorName(VendNo: Code[20]): Text[50]
    var
        Vend: Record Vendor;
    begin
        Vend.Reset();
        Vend.SetRange("No.", VendNo);
        if Vend.FindSet() then
            exit(Vend.Name);
    end;

    local procedure GetVendBankAcc(VendNo: Code[20]; Vendbank: Code[20]): Code[20]
    var
        VeBank: Record "Vendor Bank Account";
    begin
        VeBank.Reset();
        VeBank.SetRange("Vendor No.", VendNo);
        VeBank.SetRange(Code, Vendbank);
        if VeBank.FindSet() then
            exit(VeBank."Bank Account No.");
    end;

    local procedure GetVendBankCountry(VendNo: Code[20]; Vendbank: Code[20]): Code[20]
    var
        VeBank: Record "Vendor Bank Account";
    begin
        VeBank.Reset();
        VeBank.SetRange("Vendor No.", VendNo);
        VeBank.SetRange(Code, Vendbank);
        if VeBank.FindSet() then
            exit(VeBank."Country/Region Code");
    end;

    local procedure GetVendBankExpCode(VendNo: Code[20]; Vendbank: Code[20]): Enum ExpBankCodes
    var
        VeBank: Record "Vendor Bank Account";
    begin
        VeBank.Reset();
        VeBank.SetRange("Vendor No.", VendNo);
        VeBank.SetRange(Code, Vendbank);
        if VeBank.FindSet() then
            exit(VeBank."Bank Export Code");
    end;

So, lets talk about a new idea here:

Lets make the base table available as a base required app, available with your first “layout purchase”. For example, the customer wants to purchase BBVA so, we sell BBVA layout plus base table, later, the same customer wants Banorte, so our Banorte app, checks if the base table exists and the fields, if detects that the base table needs an udpate, then apply the updated and add the new fields, if not change is need, just add the new layout codeunit and make required mods in some pages.

With this approach, we can build a multi layout solution that we can sell by parts and those apps relies in a “single source table” and sell this solution to any customer in Mexico, and maybe you can “clone” this approach to other countries.

If you need to use Host to Host solutions or webservices , you simply add another layer to accomplish these throught powerapps or BC web services calls.

Feel free to use and modify the code according to your needs.

Leave a Reply