FiveTech Support Forums

FiveWin / Harbour / xBase community
Board index FiveWin for Harbour/xHarbour That webinar from back then – is anyone still using it productively?
Posts: 6983
Joined: Fri Oct 07, 2005 07:07 PM
That webinar from back then – is anyone still using it productively?
Posted: Wed Mar 25, 2026 07:07 AM

A lot of hype – and then… surprisingly quiet

Does anyone remember that big webinar a few months ago?

80+ people live – and since then: no real feedback.

The scene has clearly moved on.

But here in the forum, you don’t see much of that.

What’s missing are real-world experiences – especially for Fivewin/Harbour developers.

Instead, everyone seems to be figuring things out on their own and losing time.

So, let’s be direct:
Who is still using it productively – and what actually works in practice?

Best regards,
Otto

Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: That webinar from back then – is anyone still using it productively?
Posted: Wed Mar 25, 2026 07:41 AM

Dear Otto,

I hope you’re doing well. I’m writing to you with a lot of respect and appreciation for your message, as I think you’ve touched on a very important point for our community.

First of all, I want to share that, from my perspective, that webinar was incredibly productive. Based on my interactions with many users since then, my takeaway is quite different from "quietness" being a sign of inactivity.

What I am seeing is a profound and very active shift toward AI. Developers aren't just experimenting; they are becoming true "solution engineers," advancing their development with a level of productivity we have never seen before in the Fivewin/Harbour ecosystem. The progress is happening, and it is happening fast.

However, I believe there is a distinction between using the technology and sharing those advances publicly. Often, when people discover a tool that gives them such a massive competitive advantage and saves them hours of manual labor, they tend to dive deep into their own production rather than posting about it. It’s not that they are losing time on their own—it’s that they are working with an intensity and focus that keeps them away from the forums.

The impact is definitely there, and it is incredibly positive. It might just take a little more time for everyone to feel comfortable "revealing their magic" or sharing their new workflows.

Sending you a warm hug and my sincere thanks for always keeping the conversation alive and pushing us forward.

With much respect and affection,

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 6983
Joined: Fri Oct 07, 2005 07:07 PM
Re: That webinar from back then – is anyone still using it productively?
Posted: Wed Mar 25, 2026 10:11 AM

Dear Antonio, That’s true — but if you, Antonio, didn’t share your knowledge so generously, most of us probably wouldn’t be at this level today.

I believe the real strength of this forum lies in the many experts across a wide range of fields. Exchanging tips and knowledge would certainly be very valuable — and also a matter of honor.

Of course, one can choose to only take. But then one shouldn’t be surprised if the source eventually runs dry.

Best regards, Otto

Posts: 113
Joined: Wed Feb 08, 2006 10:32 PM
Re: That webinar from back then – is anyone still using it productively?
Posted: Sun Mar 29, 2026 03:02 PM

Dear friends, In the last few weeks, I've started a project with Antigravity to convert my library to make all my desktop applications web-compatible. The library provides the layer that interfaces with the visual components (Fivewin), while apps very rarely use these components directly. The project is ambitious because the library provides numerous object classes and must mix existing CGI logic with windowed desktop logic.

My main problem with Antigravity is its usage quotas. After about a week of intensive use, I had to slow down significantly due to constant processing freezes and numerous interpretation errors, perhaps due to a very large context. These days, even though Antigravity has done a lot of the initial work very well, I've had to frequently intervene on the code to simplify and improve the logic, put many trace logs to fix added bugs and so on.

My impression from this first experience is that Antigravity can certainly be used to provide ideas and solutions and assist with initial development, but it doesn't (yet) have a broad vision for large projects with many objects involved, and it's unable to address a complex reality. As a result, I'm breaking down the processes and adding components little by little. Unfortunately, even though I have a paid plan, I'll have to take a break for a few days...

regards

Roberto Chiaiese
R&C Informatica S.n.c.
https://www.recinformatica.it
info@recinformatica.it

Harbour 3.2 - FW2512

Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: That webinar from back then – is anyone still using it productively?
Posted: Sun Mar 29, 2026 04:41 PM

Dear Roberto,

Please try Claude Code. You will be so much surprised :idea: :!: :wink:

Antigravity is a toy compared to Claude Code :idea:

You won't regret it. You will thank me for the advise :wink:

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 244
Joined: Mon Jun 05, 2006 09:39 PM
Re: That webinar from back then – is anyone still using it productively?
Posted: Mon Mar 30, 2026 02:02 PM

Hi Otto.
I paid for the webinar but couldn't watch it online that day. I watched it later and I'll tell you that it did work.
I contacted Carles to discuss the ADS issue and he recommended UThttp. Based on the webinar and some tips from Carles, I managed to create an example.
I managed to add my tAds class and other functions.
Then I watched some of Carles' videos at https://carles9000.github.io/index_doc_en.html and I believe there's still a lot to learn.
I'm using VS Code with Copilot and the Claude Opus 4.5 AI agent.
On Claude's ASK, talk to him and tell him to read the documentation on the page and the source code that you need to include in a GitHub project. Then ask him to memorize the subject and register it on GitHub.
I'll show you some screenshots and source files here.

In the Copilot settings panel, enable Memory.

Some screens

Some sources from the program.

IniCobrnca.html

<?prg
#include "lib/tweb/tweb.ch" 

LOCAL o, oWeb

DEFINE WEB oWeb TITLE 'CartWeb - Identificação html'
    oWeb:cLang := 'pt-br'
    oWeb:AddJs( 'https://code.jquery.com/jquery-3.6.0.min.js' )
    oWeb:AddJs( 'https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.bundle.min.js' )
    oWeb:AddCss( 'https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css' )
    oWeb:AddCss( 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css' )         

    DEFINE FORM o ID 'myform' API 'api_cobranca' OF oWeb 
        o:lDessign := .f.           
        o:lFluid := .t.         
    
    INIT FORM o     
    
        HTML o 

            <style>
                body {
                    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
                    min-height: 100vh;
                    margin: 0;
                }

                .card-cpf {
                    margin: auto;
                    margin-top: 12vh;
                    width: 90%;
                    max-width: 460px;
                    border: none;
                    border-radius: 12px;
                    box-shadow: 0 8px 32px rgba(0,0,0,0.2);
                    background-color: #fff;
                    padding: 40px 30px;
                }

                @media only screen and (max-width: 576px) {
                    .card-cpf {
                        width: 95% !important;
                        max-width: 100% !important;
                        margin-top: 4vh;
                        padding: 20px 15px;
                    }
                }

                .card-cpf h4 {
                    color: #333;
                    font-weight: 600;
                }

                .card-cpf .subtitle {
                    color: #777;
                    font-size: 0.9rem;
                    margin-bottom: 25px;
                }

                .toggle-doc {
                    display: flex;
                    justify-content: center;
                    gap: 10px;
                    margin-bottom: 20px;
                }

                .toggle-doc .btn {
                    min-width: 100px;
                }

                .toggle-doc .btn.active {
                    background-color: #667eea;
                    border-color: #667eea;
                    color: #fff;
                }

                .toggle-doc .btn:not(.active) {
                    background-color: #f0f0f0;
                    border-color: #ddd;
                    color: #555;
                }
            </style>

        ENDTEXT

        HTML o 

            <div class="card-cpf">
                <div class="text-center">
                    <h4><i class="fa fa-id-card"></i>&nbsp; Identificação</h4>
                    <p class="subtitle">Informe seu CPF ou CNPJ para continuar</p>
                </div>

                <div class="toggle-doc">
                    <button type="button" class="btn btn-sm active" id="btnCpf" onclick="toggleDoc('cpf')">CPF</button>
                    <button type="button" class="btn btn-sm" id="btnCnpj" onclick="toggleDoc('cnpj')">CNPJ</button>
                </div>

                <div class="form-group">
                    <label for="documento"><i class="fa fa-file-text-o"></i>&nbsp; Documento</label>
                    <input type="text" class="form-control form-control-lg" id="documento" 
                           placeholder="000.000.000-00" maxlength="18" autocomplete="off"
                           inputmode="numeric" style="text-align:center; font-size:1.2rem; letter-spacing:1px;">
                    <input type="hidden" id="tipo_doc" value="cpf">
                </div>

                <div class="text-center mt-4" style="display:flex; justify-content:center; gap:10px;">
                    <button type="button" class="btn btn-lg"
                            style="background-color:#667eea; color:#fff; min-width:150px; border-radius:8px;"
                            onclick="validarDocumento()">
                        <i class="fa fa-arrow-right"></i>&nbsp; Continuar
                    </button>
                    <button type="button" class="btn btn-lg btn-outline-secondary"
                            style="min-width:150px; border-radius:8px;"
                            onclick="fecharPagina();">
                        <i class="fa fa-times"></i>&nbsp; Cancelar
                    </button>
                </div>
            </div>

        ENDTEXT

        HTML o

            <script>
                var tipoDoc = 'cpf';

                function fecharPagina() {
                    window.close();
                    // Navegadores mĂłveis bloqueiam window.close() em abas nĂŁo abertas via JS
                    setTimeout(function() {
                        // Se nĂŁo fechou, navega para about:blank
                        window.location.href = 'about:blank';
                    }, 300);
                }

                function toggleDoc(tipo) {
                    tipoDoc = tipo;
                    $('#tipo_doc').val(tipo);
                    var inp = $('#documento');
                    inp.val('');
                    inp.focus();

                    if (tipo === 'cpf') {
                        inp.attr('placeholder', '000.000.000-00');
                        inp.attr('maxlength', '14');
                        $('#btnCpf').addClass('active');
                        $('#btnCnpj').removeClass('active');
                    } else {
                        inp.attr('placeholder', '00.000.000/0000-00');
                        inp.attr('maxlength', '18');
                        $('#btnCnpj').addClass('active');
                        $('#btnCpf').removeClass('active');
                    }
                }

                // Máscara CPF: 000.000.000-00
                // Máscara CNPJ: 00.000.000/0000-00
                $('#documento').on('input', function() {
                    var v = $(this).val().replace(/\D/g, '');

                    if (tipoDoc === 'cpf') {
                        if (v.length > 11) v = v.substring(0, 11);
                        if (v.length > 9)       v = v.replace(/(\d{3})(\d{3})(\d{3})(\d{1,2})/, '$1.$2.$3-$4');
                        else if (v.length > 6)  v = v.replace(/(\d{3})(\d{3})(\d{1,3})/, '$1.$2.$3');
                        else if (v.length > 3)  v = v.replace(/(\d{3})(\d{1,3})/, '$1.$2');
                    } else {
                        if (v.length > 14) v = v.substring(0, 14);
                        if (v.length > 12)      v = v.replace(/(\d{2})(\d{3})(\d{3})(\d{4})(\d{1,2})/, '$1.$2.$3/$4-$5');
                        else if (v.length > 8)  v = v.replace(/(\d{2})(\d{3})(\d{3})(\d{1,4})/, '$1.$2.$3/$4');
                        else if (v.length > 5)  v = v.replace(/(\d{2})(\d{3})(\d{1,3})/, '$1.$2.$3');
                        else if (v.length > 2)  v = v.replace(/(\d{2})(\d{1,3})/, '$1.$2');
                    }

                    $(this).val(v);
                });

                $('#documento').on('keypress', function(e) {
                    if (e.which === 13) {
                        validarDocumento();
                    }
                });

                function validarCPF(cpf) {
                    cpf = cpf.replace(/\D/g, '');
                    if (cpf.length !== 11) return false;
                    if (/^(\d)\1{10}$/.test(cpf)) return false;
                    var soma = 0, resto;
                    for (var i = 1; i <= 9; i++) soma += parseInt(cpf.substring(i-1, i)) * (11 - i);
                    resto = (soma * 10) % 11;
                    if (resto === 10 || resto === 11) resto = 0;
                    if (resto !== parseInt(cpf.substring(9, 10))) return false;
                    soma = 0;
                    for (var i = 1; i <= 10; i++) soma += parseInt(cpf.substring(i-1, i)) * (12 - i);
                    resto = (soma * 10) % 11;
                    if (resto === 10 || resto === 11) resto = 0;
                    if (resto !== parseInt(cpf.substring(10, 11))) return false;
                    return true;
                }

                function validarCNPJ(cnpj) {
                    cnpj = cnpj.replace(/\D/g, '');
                    if (cnpj.length !== 14) return false;
                    if (/^(\d)\1{13}$/.test(cnpj)) return false;
                    var tamanho = cnpj.length - 2;
                    var numeros = cnpj.substring(0, tamanho);
                    var digitos = cnpj.substring(tamanho);
                    var soma = 0, pos = tamanho - 7;
                    for (var i = tamanho; i >= 1; i--) {
                        soma += numeros.charAt(tamanho - i) * pos--;
                        if (pos < 2) pos = 9;
                    }
                    var resultado = soma % 11 < 2 ? 0 : 11 - soma % 11;
                    if (resultado != digitos.charAt(0)) return false;
                    tamanho = tamanho + 1;
                    numeros = cnpj.substring(0, tamanho);
                    soma = 0; pos = tamanho - 7;
                    for (var i = tamanho; i >= 1; i--) {
                        soma += numeros.charAt(tamanho - i) * pos--;
                        if (pos < 2) pos = 9;
                    }
                    resultado = soma % 11 < 2 ? 0 : 11 - soma % 11;
                    if (resultado != digitos.charAt(1)) return false;
                    return true;
                }

                function validarDocumento() {
                    var doc = $('#documento').val().replace(/\D/g, '');
                    var tipo = tipoDoc;

                    if (doc.length === 0) {
                        alert('Por favor, informe o ' + tipo.toUpperCase());
                        $('#documento').focus();
                        return;
                    }

                    if (tipo === 'cpf' && !validarCPF(doc)) {
                        alert('CPF inválido. Verifique e tente novamente.');
                        $('#documento').focus();
                        return;
                    }

                    if (tipo === 'cnpj' && !validarCNPJ(doc)) {
                        alert('CNPJ inválido. Verifique e tente novamente.');
                        $('#documento').focus();
                        return;
                    }

                    MsgApi('api_cobranca', 'consultar2', { tipo: tipo, documento: doc });
                }

                function exibirResultado(f_hDsCursorResultado) {
                    var aStruct = f_hDsCursorResultado.struct;
                    var aData   = f_hDsCursorResultado.data;

                    if (!aData || aData.length === 0) {
                        alert('Nenhum registro encontrado.');
                        return;
                    }

                    var labelMap = {
                        'P_DEVPRO_C_DEVEDOR_NOME': 'Nome ',
                        'DEVEDOR_DOC_VALIDO': 'Documento CPF/CNPJ ',
                        'PROTESTO_APONTA': 'NĂşmero do Apontamento',
                        'PROTESTO_SEQ_DATA': 'Data da Apresentação',
                        'PROTESTO_VALOR': 'Valor do TĂ­tulo',
                        'PROTESTO_NUMERO': 'NĂşmero do TĂ­tulo',
                        'PROTESTO_DT_EMISSAO': 'Data de EmissĂŁo',
                        'PROTESTO_DT_VENCE': 'Data de Vencimento',
                        'PROTESTO_CREDOR': 'Credor ',
                        'PROTESTO_DAT_APONTA': 'Data de Apontamento',
                        'PROTESTO_DAT_VENCE': 'Data Limite para Pagamento',
                        'PROTESTO_VAL_APONTA': 'Valor dos Emolumentos',
                        'PROTESTO_VAL_DILIGE': 'Valor das DiligĂŞncias'
                    };

                    var camposData = ['PROTESTO_SEQ_DATA', 'PROTESTO_DT_EMISSAO', 'PROTESTO_DT_VENCE',
                                      'PROTESTO_DAT_APONTA', 'PROTESTO_DAT_VENCE'];
                    var camposValor = ['PROTESTO_VALOR', 'PROTESTO_VAL_APONTA', 'PROTESTO_VAL_DILIGE'];

                    function formatarData(v) {
                        if (!v) return '';
                        var s = String(v).replace(/\D/g, '');
                        if (s.length === 8) return s.substring(6,8) + '/' + s.substring(4,6) + '/' + s.substring(0,4);
                        var d = new Date(v);
                        if (isNaN(d.getTime())) return v;
                        var dd = ('0' + d.getDate()).slice(-2);
                        var mm = ('0' + (d.getMonth()+1)).slice(-2);
                        return dd + '/' + mm + '/' + d.getFullYear();
                    }

                    function formatarValor(v) {
                        if (v === null || v === undefined || v === '') return 'R$ 0,00';
                        var n = parseFloat(v);
                        if (isNaN(n)) return v;
                        return 'R$ ' + n.toFixed(2).replace('.', ',').replace(/\B(?=(\d{3})+(?!\d))/g, '.');
                    }

                    function formatarCampo(fieldName, val) {
                        if (camposData.indexOf(fieldName) !== -1) return formatarData(val);
                        if (camposValor.indexOf(fieldName) !== -1) return formatarValor(val);
                        return (val !== null && val !== undefined ? val : '');
                    }

                    var html = '';
                    for (var i = 0; i < aData.length; i++) {
                        html += '<div class="card mb-3" style="border-radius:10px; box-shadow:0 2px 8px rgba(0,0,0,0.1);">';
                        html += '<div class="card-body">';
                        html += '<h6 class="card-title" style="color:#667eea; font-weight:600;">TĂ­tulo ' + (i + 1) + '</h6>';
                        html += '<hr style="margin:8px 0;">';
                        for (var c = 0; c < aStruct.length; c++) {
                            var fieldName = aStruct[c][0];
                            var label = labelMap[fieldName] || fieldName;
                            var val = formatarCampo(fieldName, aData[i][fieldName]);
                            var negrito = (fieldName === 'PROTESTO_VALOR' || fieldName === 'PROTESTO_DAT_VENCE');
                            html += '<div class="d-flex justify-content-between" style="padding:3px 0; border-bottom:1px solid #f0f0f0;">';
                            html += '<strong style="color:#555; font-size:0.76rem;">' + label + '</strong>';
                            html += negrito
                                ? '<strong style="font-size:0.76rem;">' + val + '</strong>'
                                : '<span style="font-size:0.76rem;">' + val + '</span>';
                            html += '</div>';
                        }
                        var totalPag = (parseFloat(aData[i]['PROTESTO_VALOR']) || 0)
                                     + (parseFloat(aData[i]['PROTESTO_VAL_APONTA']) || 0)
                                     + (parseFloat(aData[i]['PROTESTO_VAL_DILIGE']) || 0);
                        html += '<div class="d-flex justify-content-between" style="padding:8px 0; margin-top:6px; background-color:#f0f4ff; border-radius:6px; padding-left:8px; padding-right:8px;">';
                        html += '<strong style="color:#667eea; font-size:0.85rem;">Valor Total para Pagamento</strong>';
                        html += '<strong style="color:#667eea; font-size:0.85rem;">' + formatarValor(totalPag) + '</strong>';
                        html += '</div>';
                        html += '<div class="text-center" style="margin-top:10px;">';
                        html += '<button type="button" class="btn btn-sm" style="background-color:#28a745; color:#fff; border-radius:6px; width:100%;" onclick="baixarBoleto(' + i + ')">';
                        html += '<i class="fa fa-download"></i>&nbsp; Baixe o Boleto para Pagamento</button>';
                        html += '</div>';
                        html += '</div></div>';
                    }

                    $('.card-cpf').css({'width': '90%', 'max-width': '870px'}).html(
                        '<div class="text-center"><h4><i class="fa fa-list"></i>&nbsp; TĂ­tulos para pagamentos</h4>' +
                        '<p class="subtitle">' + aData.length + ' tĂ­tulo(s) encontrado(s)</p></div>' +
                        '<div style="max-height:60vh; overflow-y:auto;">' + html + '</div>' +
                        '<div class="text-center mt-3">' +
                        '<button type="button" class="btn btn-lg" style="background-color:#667eea; color:#fff; border-radius:8px;" onclick="location.reload();">' +
                        '<i class="fa fa-arrow-left"></i>&nbsp; Voltar</button></div>'
                    );
                }

                var _dadosResultado = null;

                var _exibirResultadoOriginal = exibirResultado;
                exibirResultado = function(f_hDsCursorResultado) {
                    _dadosResultado = f_hDsCursorResultado;
                    _exibirResultadoOriginal(f_hDsCursorResultado);
                };

                function baixarBoleto(idx) {
                    if (!_dadosResultado || !_dadosResultado.data || !_dadosResultado.data[idx]) return;
                    var reg = _dadosResultado.data[idx];
                    // TODO: chamar API para gerar boleto
                    // MsgApi('api_cobranca', 'boleto', { aponta: reg['PROTESTO_APONTA'] });
                    alert('Função de boleto será implementada em breve.');
                }

                $(document).ready(function() {
                    toggleDoc('cpf');
                    $('#documento').focus();
                });
            </script>

        ENDTEXT

    ENDFORM o

INIT WEB oWeb RETURN
?>

CartWeb.prg = App.prg

#include 'lib/uhttpd2/uhttpd2.ch'

request DBFCDX
request TWEB
request ORDKEYNO
request DBSKIP, __DbREINDEX 

//	Default codepage ------
//REQUEST HB_CODEPAGE_ES850  	
//REQUEST HB_LANG_ES
//REQUEST HB_CODEPAGE_ESWIN 
//	-----------------------
//REQUEST HB_CODEPAGE_ESMWIN
//REQUEST HB_CODEPAGE_ES850C
//REQUEST HB_CODEPAGE_FR850
//REQUEST HB_CODEPAGE_FR850M
//REQUEST HB_CODEPAGE_FRISO
//REQUEST HB_CODEPAGE_PL852
//REQUEST HB_CODEPAGE_PLISO
//REQUEST HB_CODEPAGE_PLMAZ
//REQUEST HB_CODEPAGE_PLWIN
//REQUEST HB_CODEPAGE_PT850
//REQUEST HB_CODEPAGE_PT860
REQUEST HB_CODEPAGE_PTISO
//	-----------------------
REQUEST hb_cdpList 

#define VK_ESCAPE	27
#define APP_VERSION 'CartWeb v1.0.0'


// -------------------------------------------------- //

function main()

hb_threadStart( @WebServer() )	

while inkey(0) != VK_ESCAPE
end

retu nil 

// -------------------------------------------------- //

function WebServer( hConfig )

local oServer 		:= Httpd2()
local hCfg 			:= Config()

HB_HCaseMatch( hCfg, .F. )

oServer:SetPort( HB_HGetDef( hCfg, 'port', 81 ) )

oServer:bInit := {|hInfo| ServerInfo(), OpenUrl( hInfo ) }

//	Routing...			

	//oServer:Route( '/'			   , 'main.html' )  												
	//oServer:Route( 'splash'	 	   , 'splash.html' )  												
	
	//oServer:Route( '/'		   , 'security/login.html' )  												
	oServer:Route( 'meuprotesto'		   , 'InitCobranca.html' )  												
	//oServer:Route( 'login'		   , 'security/login.html' )  												
	//oServer:Route( 'logout'	      , 'logout' )  					
	
	//oServer:Route( 'dashboard'  , 'dashboard' )  				//	Ull! direct to dashboard function :-)
	//oServer:Route( 'browse'  	 , 'dbu/browse.html' )  												
	//oServer:Route( 'structure'  , 'dbu/structure.html' )  												
	//oServer:Route( 'indexes'    , 'dbu/indexes.html' )  												
	//oServer:Route( 'pack'       , 'dbu/pack.html' )  												
	//oServer:Route( 'server_info', 'dbu/server_info.html' )  												
	//oServer:Route( 'users'		 , 'dbu/tables/users.html' )  												
	//oServer:Route( 'repository' , 'dbu/tables/repository.html' )  												
	//oServer:Route( 'about'		 , 'dbu/about.html' )  												
	
//	-----------------------------------------------------------------------//	

IF ! oServer:Run()

	? "=> Server error:", oServer:cError

	RETU 1
ENDIF

RETURN 0

// -------------------------------------------------- //

function ServerInfo() 	
	Local lc_hCfgsIni
	local hCfg := UGetServerInfo()	

lc_hCfgsIni := INI_CFGS()

hCfg[ 'path' ] 			:= HB_DIRBASE()		
hCfg[ 'os' ] 			   := os()
hCfg[ 'harbour' ] 		:= version()
hCfg[ 'builddate' ] 	   := HB_BUILDDATE()
hCfg[ 'compiler' ] 		:= HB_compiler()
hCfg[ 'codepage' ] 		:= hb_SetCodePage() + '/' + hb_cdpUniID( hb_SetCodePage() )
hCfg[ 'version_tweb' ] 	:= TWebVersion()

Console  '---------------------------------'	
Console  'DominiumCart WebServer Info'
Console  '---------------------------------'

Console  'Path.............: ' + hCfg[ 'path' ] 		
Console  'Version UT.......: ' + hCfg[ 'version' ] 	
Console  'Start............: ' + hCfg[ 'start' ] 		
Console  'Port.............: ' + ltrim(str(hCfg[ 'port' ])) 		
Console  'OS...............: ' + hCfg[ 'os' ] 		
Console  'Harbour..........: ' + hCfg[ 'harbour' ] 	
Console  'Build date.......: ' + hCfg[ 'builddate' ] 	
Console  'Compiler.........: ' + hCfg[ 'compiler' ] 	
Console  'SSL..............: ' + if( hCfg[ 'ssl' ], 'Yes', 'No' )
Console  'Trace............: ' + if( hCfg[ 'debug' ], 'Yes', 'No' )		
Console  'Codepage.........: ' + hCfg[ 'codepage' ] 	
Console  'UTF8 (actived)...: ' + if( hCfg[ 'utf8' ], 'Yes', 'No' )
Console  'Base de dados....: ' + lc_hCfgsIni[ 'ServerDados' ] 
Console  '---------------------------------'
Console  'Escape for exit...' 		

retu nil 

// -------------------------------------------------- //

function Config()	

local hCfg

//RddSetDefault( 'DBFCDX' )

//HB_LANGSELECT('ES')        
   //HB_SetCodePage ( "ESWIN" )		
	
//SET( _SET_DBCODEPAGE, 'ESWIN' )		
//SET( _SET_DELETED, 'ON' )			

//HB_LANGSELECT('ES')        
   //HB_SetCodePage ( "ESWIN" )		
	
//SET( _SET_DBCODEPAGE, 'ESWIN' )		
//SET( _SET_DELETED, 'ON' )			

REQUEST HB_CODEPAGE_PTISO
HB_SETCODEPAGE( "PTISO" )

REQUEST HB_LANG_PT_BR 
HB_LANGSELECT( 'PT_BR' )

SET AUTOPEN OFF 
SET DATE FORMAT TO 'DD/MM/YYYY' 

CheckDbfs()

hCfg := CheckIni()	

retu hCfg 

// -------------------------------------------------- //

function AppVersion() 		; retu APP_VERSION
function AppPathData()		; retu HB_DIRBASE() + 'data.sys' + hb_ps()

// -------------------------------------------------- //

function AppGetRepo() 

local oRepo := TDbf():New( 'repository.dbf', 'repository.cdx', 'name' )
local aRows := {}

Aadd( aRows, '' )

while (oRepo:cAlias)->( !eof() )

	if (oRepo:cAlias)->active
		Aadd( aRows, alltrim((oRepo:cAlias)->name ))
	endif
	
	(oRepo:cAlias)->(dbskip())
end	

(oRepo:cAlias)->( DbCloseArea() )

retu aRows

// -------------------------------------------------- //

function AppRepo2Path( cRepo ) 

local cPath := ''
local oRepo := TDbf():New( 'repository.dbf', 'repository.cdx', 'name' )

if oRepo:Seek( cRepo )
	cPath := oRepo:Row()[ 'PATH' ]
endif

retu cPath 

//----------------------------------------------------------------------------//

#define SW_SHOW             5

static function OpenUrl( hInfo )

local cUrl := ''

cUrl := if( hInfo[ 'ssl' ], 'https://localhost', 'http://localhost' )

if hInfo[ 'port' ] != 80 
	cUrl += ':' + ltrim(str( hInfo[ 'port' ] ))
endif				

#ifdef __PLATFORM__WINDOWS	
	WAPI_ShellExecute( nil, "open", cUrl, nil, nil, SW_SHOW )		
#else     

	hb_run( 'xdg-open ' + cUrl  )
#endif

retu nil

Api_cobranca.prg

#include '.\uhttpd2.ch'
#include ".\tweb.ch"

function Api_Cobranca( oDom )

   do case
      case oDom:GetProc() == 'consultar2'   ; Cobranca_Consultar( oDom )

  otherwise
     oDom:SetError( "Proc don't defined => " + oDom:GetProc())
   endcase

retu oDom:Send()

// -------------------------------------------------- //

static function Cobranca_Consultar( oDom )

   local cTipo      := oDom:Get( 'tipo' )
   local cDocumento := oDom:Get( 'documento' )
   local nConn      := -1
   local cQuery     := ""
   local aVars      := {}
   local aResultado := {}
   local nI
   Local lc_oDs_TitulosPendentes    := Nil
   Local lc_hCursorResultado        := Nil // Hash para armazenar o cursor de resultado da consulta de tads em hDsCursorsHash

   // Remove formatação do documento (pontos, traços, barras)
   //cDocumento := StrTran( cDocumento, ".", "" )
   //cDocumento := StrTran( cDocumento, "-", "" )
   //cDocumento := StrTran( cDocumento, "/", "" ) 
   cDocumento := AllTrim( cDocumento )
   cDocumento := DOC_CPF_CNPJ_FORMAT( cDocumento )

   if empty( cDocumento )
      oDom:SetError( "Documento nĂŁo informado." )
      return nil
   endif

   // Conecta ao dicionário de dados
   nConn := DictionaryConnectionNew(oDom)

   //nConn := -1 // Forçando erro de conexão para teste

   //oDom:SetMsg( "nConn = " + AllTrim(Str(nConn)) )
   //Return nil

   if nConn < 0
      oDom:SetError( "NĂŁo foi possĂ­vel conectar ao banco de dados." )
      return nil
   endif

   // Cria objeto tAds para executar a query SELECT
   // DB_PROTESTO DB_P_DEVPRO DB_DEVEDOR
   lc_oDs_TitulosPendentes := tAds():DsNew(1/*1-Select*/, nConn)

   /*
   Text Into lc_oDs_TitulosPendentes:cQrySql 
      Select 
      Tb01.SEQ_PROT                 AS P_DEVPRO_SEQ_PROT,
      Tb01.SEQ_DATA                 AS P_DEVPRO_SEQ_DATA,
      Tb01.N_DEVEDOR                AS P_DEVPRO_N_DEVEDOR,
      Tb01.cDevedorNome             AS P_DEVPRO_C_DEVEDOR_NOME,
      Tb02.SEQ_PROT                 AS PROTESTO_SEQ_PROT,
      Tb02.SEQ_DATA                 AS PROTESTO_SEQ_DATA,
      Tb02.nCodigoPortador          AS PROTESTO_N_CODIGO_PORTADOR,
      Tb02.cCodigoPortador          AS PROTESTO_C_CODIGO_PORTADOR,
      Tb02.NTIPO_TIT                AS PROTESTO_N_TIPO_TIT,
      Tb02.VALOR                    AS PROTESTO_VALOR,
      Tb02.NUMERO                   AS PROTESTO_NUMERO,
      Tb02.DT_EMISSAO               AS PROTESTO_DT_EMISSAO,
      Tb02.DT_VENCE                 AS PROTESTO_DT_VENCE,
      Tb02.CREDOR                   AS PROTESTO_CREDOR,
      Tb02.APONTA                   AS PROTESTO_APONTA,
      Tb02.N_SQBOLETO               AS PROTESTO_N_SQBOLETO,
      Tb02.DAT_APONTA               AS PROTESTO_DAT_APONTA,
      Tb02.DAT_VENCE                AS PROTESTO_DAT_VENCE,
      Tb02.VAL_APONTA               AS PROTESTO_VAL_APONTA,
      Tb02.VAL_DILIGE               AS PROTESTO_VAL_DILIGE,
      Tb03.NCODIGO                  AS DEVEDOR_NCODIGO,
      Tb03.NOME                     AS DEVEDOR_NOME,
      Tb03.CPF                      AS DEVEDOR_CPF,
      Tb03.CGC                      AS DEVEDOR_CGC,
      Tb03.cCelular                 AS DEVEDOR_CELULAR
   */

   Text Into lc_oDs_TitulosPendentes:cQrySql 
      Select 
      Tb01.cDevedorNome             AS P_DEVPRO_C_DEVEDOR_NOME,
      IIF(Tb03.NPESSOA = 1, Tb03.CPF, Tb03.CGC) AS DEVEDOR_DOC_VALIDO,       

      Tb02.APONTA                   AS PROTESTO_APONTA,
      Tb02.SEQ_DATA                 AS PROTESTO_SEQ_DATA,
      Tb02.VALOR                    AS PROTESTO_VALOR,
      Tb02.NUMERO                   AS PROTESTO_NUMERO,
      Tb02.DT_EMISSAO               AS PROTESTO_DT_EMISSAO,
      Tb02.DT_VENCE                 AS PROTESTO_DT_VENCE,
      Tb02.CREDOR                   AS PROTESTO_CREDOR,
      Tb02.DAT_APONTA               AS PROTESTO_DAT_APONTA,
      Tb02.DAT_VENCE                AS PROTESTO_DAT_VENCE,
      Tb02.VAL_APONTA               AS PROTESTO_VAL_APONTA,
      Tb02.VAL_DILIGE               AS PROTESTO_VAL_DILIGE
      From P_DEVPRO AS Tb01 
         Inner join PROTESTO AS Tb02 On Tb01.SEQ_PROT = Tb02.SEQ_PROT
         left join DEVEDOR AS Tb03 On Tb01.N_DEVEDOR = Tb03.NCODIGO
      __WHERE__ __GROUP_BY__ __ORDER_BY__ ;
   EndText
   lc_oDs_TitulosPendentes:nCacheDefault        := 100 //Page
   lc_oDs_TitulosPendentes:lDataLoadOnSkip      := .T.  

   lc_oDs_TitulosPendentes:lDataLoadBinaryField := .F.  

   lc_oDs_TitulosPendentes:lDsCursorsToTemp     := .F.
   lc_oDs_TitulosPendentes:lDsCursorsToJson     := .T.
   //lc_oDs_TitulosPendentes:aFieldsTempIndex   := {"Field1","Field2"}
   If cTipo == "cpf"
      Text Into lc_oDs_TitulosPendentes:cDsWhere 
         Where Tb03.CPF = _cDocumento_ 
               and Tb02.APONTA > 0
               and Tb02.TIP_SOLUCA = 0
      EndText
   Elseif cTipo == "cnpj"
       Text Into lc_oDs_TitulosPendentes:cDsWhere 
         Where Tb03.CGC = _cDocumento_
               and Tb02.APONTA > 0
               and Tb02.TIP_SOLUCA = 0
      EndText
   Else
      oDom:SetError( "Tipo de documento inválido. Use 'cpf' ou 'cnpj'." )
      lc_oDs_TitulosPendentes:End()
      DictionaryConnectionClose( nConn )
      return nil
   EndIf
   //Text Into lc_oDs_TitulosPendentes:cDsGroupBy 
   //   Group By 
   //EndText
   //Text Into lc_oDs_TitulosPendentes:cDsOrderBy 
   //   Order By  

   //EndText
   lc_oDs_TitulosPendentes:DsAddVar("_cDocumento_",cDocumento)
   //lc_oDs_TitulosPendentes:DsExecute() 

   // Executa a query
   if ! lc_oDs_TitulosPendentes:DsExecute()
      oDom:SetError( "Erro ao consultar documento: " + AllTrim(Str(lc_oDs_TitulosPendentes:nErrorSql)) )
      lc_oDs_TitulosPendentes:End()
      DictionaryConnectionClose( nConn )
      return nil
   endif

   hb_MemoWrit( "Last_Qry.txt", lc_oDs_TitulosPendentes:cQrySqlLast)

   //oDom:SetMsg( "Documento encontrado. " + AllTrim(Str(Len(lc_hCursorResultado['data']))) + " registro(s)." )
   //Return Nil

   // Verifica se encontrou registros
   if lc_oDs_TitulosPendentes:KeyCount() == 0
      lc_oDs_TitulosPendentes:End()
      DictionaryConnectionClose( nConn )
      oDom:SetError( "Nenhum registro encontrado para o documento informado." )
      return nil
   endif

   // Armazena o cursor de resultado em hDsCursorsHash usando nConn como chave
   lc_hCursorResultado := lc_oDs_TitulosPendentes:hDsCursorsHash 

   // Fecha cursor e conexĂŁo
   lc_oDs_TitulosPendentes:End()
   DictionaryConnectionClose( nConn )

   // Envia os dados para o frontend
   oDom:SetJS( "exibirResultado", lc_hCursorResultado ) // Envia o hash com estrutura e dados para a função JavaScript exibirResultado(data)

return nil

// -------------------------------------------------- //
Posts: 6983
Joined: Fri Oct 07, 2005 07:07 PM
Re: That webinar from back then – is anyone still using it productively?
Posted: Mon Mar 30, 2026 05:20 PM

Hello Giovanni, Really interesting—thanks a lot for sharing this.

I think it’s great that you’re openly showing your work and experience. These kinds of real-world examples are incredibly valuable, because they go beyond theory and show how things actually come together in practice.

You can clearly see how much knowledge and effort is behind it. The combination of Harbour, web technologies, and all the components involved is quite impressive and highlights what’s possible with this stack.

Appreciate you sharing this, best regards and thank you Otto

Continue the discussion