Script Include and UI Action return error Cannot read properties of null (reading 'createElement')

joao_fonseca
Tera Contributor

I have this code in this UI Action 

//remover alerts e logs teste

function getSelectedHardwares() {
    alert('Function executed!'); // Debugging

    var entries = g_list.getChecked();
    if (!entries) {
        alert('Nenhum consumível selecionado.');
    } else {
        alert('Consumíveis selecionados: ' + entries);

        // Chamando a UI Action Server-side com os IDs selecionados
        var ga = new GlideAjax('ConsumableBarcodeGenerator');
        ga.addParam('sysparm_name', 'generateBarcode2');

        // Test parameter
        //ga.addParam('sysparm_user_name', '<OP10000>');

        //ga.addParam('sysparm_selected_ids', entries.join(','));
        ga.addParam('sysparm_selected_ids', JSON.stringify(entries));
        // ga.addParam('sysparm_ui_action_source', 'alm_hardware');
        // ga.addParam('sysparm_id', 'serial_number');


        ga.getXML(ConsumableBarcodeGeneratorParse);
    }

    function ConsumableBarcodeGeneratorParse(response) {
        var answerFromXML = response.responseXML.documentElement.getAttribute('answer');
        alert("outro texto"+answerFromXML);

        //PARA receber codigo para links download na ui action
		
        try {
            var jsonResponse = JSON.parse(answerFromXML);

            //alert("jsonResponse"+ jsonResponse.barcode_links.length);

            if (jsonResponse.status === "success" && jsonResponse.barcode_links.length > 0) {
                startAutoDownload(jsonResponse.barcode_links);
            } else {
                alert('Nenhum código de barras gerado.');
            }
        } catch (e) {
            alert('Erro ao processar resposta JSON: ' + e.message);
        }

    }

    //funcao para fazer o auto download dos barcodes attachments
    function startAutoDownload(barcodeLinks) {
        barcodeLinks.forEach(function(url) {

            //alert("objetos de barcodes:"+barcodeLinks);
            try {
                var a = document.createElement('a');
                a.href = url;
                a.download = ''; // Ensures browser download behavior
                document.body.appendChild(a);
                a.click();
                document.body.removeChild(a);
            } catch (e) {
                alert('Erro reconstruir a: ' + e.message);
            }
        });

        alert('Download automático iniciado.');
    }
}

 

and this Script Include, 

//remover gs.infos e alerts returns no final

var ConsumableBarcodeGenerator = Class.create();
ConsumableBarcodeGenerator.prototype = Object.extendsObject(AbstractAjaxProcessor, {
    generateBarcode: function() {
        gs.info('Script Include chamado!');

        // Obtém os IDs enviados via GlideAjax
        var selectedIdsRaw = this.getParameter('sysparm_selected_ids');
        var uiActionSource = this.getParameter('sysparm_ui_action_source');
        var idItem = this.getParameter('sysparm_id');

        // Verifica se os IDs foram realmente recebidos
        if (!selectedIdsRaw || selectedIdsRaw.trim() === '') {
            gs.info('Nenhum ID recebido.');
            return JSON.stringify({
                status: "error",
                message: "Nennhum consumível selecionado"
            });
        }

        // Divide os IDs em um array para processamento
        //var sysIds = selectedIds.split(',').map(id => id.trim());

        var selectedIds;

        try {
            selectedIds = JSON.parse(selectedIdsRaw);
        } catch (e) {
            gs.error('Erro ao converter JSON: ' + e.message);
            return JSON.stringify({
                status: "error",
                message: "Erro ao processar JSON recebido."
            });
        }

        var selectedIdsArray = Array.isArray(selectedIds) ? selectedIds : selectedIds.split(',').map(id => id.trim());

        selectedIdsArray.forEach(function(id) {
            gs.info('ID recebido: ' + id);
        });

        var consumiveisEncontrados = [];
        var attachmentLinks = []; // Store generated barcode URLs (ENVIAR URLS PARA ui action download auto)

        // Itera sobre cada ID e consulta na base de dados
        selectedIdsArray.forEach(function(id) {
            var targetConsumsGr = new GlideRecord(uiActionSource);
            targetConsumsGr.addQuery('sys_id', id); // Consulta cada ID individualmente
            targetConsumsGr.query();

            if (!targetConsumsGr.hasNext()) {
                gs.info('Consumível com ID ' + id + ' não encontrado.');
                return; // Se não encontrar, segue para o próximo
            }

            while (targetConsumsGr.next()) {
                gs.info('Consumível encontrado: ' + targetConsumsGr.display_name);

                consumiveisEncontrados.push(targetConsumsGr.sys_id.toString()); // Armazena IDs encontrados

                var assetSysId = targetConsumsGr.sys_id;

                // model name to para o attachment name
                var model;

                if (idItem == 'model.display_name') {
                    model = targetConsumsGr.model.display_name;
                } else if (idItem == 'serial_number') {
                    model = targetConsumsGr.serial_number;
                }

                // model name to para o barcode url
                var encodedModel = encodeURIComponent(model);

                // Verifica se este consumível já possui um anexo de código de barras
                var attachmentGr = new GlideRecord('sys_attachment');
                attachmentGr.addQuery('table_name', uiActionSource);

                //forma de criar tambem download auto para anexos já existentes
                attachmentGr.orderByDesc('sys_created_on'); // Obtém o último anexo criado

                attachmentGr.addQuery('table_sys_id', assetSysId);
                attachmentGr.query();

                if (attachmentGr.next()) {
                    gs.info('Consumível já tem código de barras. Adicionado á lista de downlaods');

                    var sysIdAttachment = attachmentGr.sys_id;
                    var attachmentLink = gs.getProperty('glide.servlet.uri') + 'sys_attachment.do?sys_id=' + sysIdAttachment;

                    attachmentLinks.push(attachmentLink); // Adiciona à lista de downloads
                    // Skip creating new barcode as one already exists
                    continue;

                    //codigo para seguir e ignorar auto download de anexos(barcodes) ja existentes, apenas contiinuue
                    //continue; // **Ignora este ID e passa para o próximo**
                }

                var imageUrl = 'https://quickchart.io/barcode?type=code128&includeText=true&text=' + encodedModel + '&format=png';
                //var imageUrl = 'https://barcode.tec-it.com/barcode.ashx?data='+serialNumber+'&		code=Code128&dpi=96';

                //teste
                //gs.info("O valor do model é: " + model);
                //gs.info("Model Name: " + model);
                //gs.info("Encoded Model: " + encodedModel);
                //gs.info("Barcode URL: " + imageUrl);
                //gs.info("sys_id Capturado: "+assetSysId);	

                // 1. Create temporary record for initial attachment storage
                var tempRec = new GlideRecord('sys_attachment');
                tempRec.initialize();
                tempRec.file_name = model + '_temp.png';
                tempRec.table_name = uiActionSource;
                tempRec.insert(); // Create temporary holder record

                // 2. Fetch and save image to temporary record
                var request = new sn_ws.RESTMessageV2();
                request.setHttpMethod("GET");
                request.setEndpoint(imageUrl);
                request.setRequestHeader("Accept", "image/png");
                request.saveResponseBodyAsAttachment('sys_attachment', tempRec.sys_id, model + '_barcode.png');
                var response = request.execute();


                if (response.getStatusCode() === 200) {
                    // 3. Copy attachment to target asset record
                    var gsa = new GlideSysAttachment();
                    var grAsset = new GlideRecord(uiActionSource);

                    if (grAsset.get(assetSysId)) {
                        gsa.copy('sys_attachment', tempRec.sys_id, uiActionSource, grAsset.sys_id);

                        // Query sys_attachment again to get the correct sys_id of the newly created barcode
                        var finalAttachmentGr = new GlideRecord('sys_attachment');
                        finalAttachmentGr.addQuery('table_name', uiActionSource);
                        finalAttachmentGr.addQuery('table_sys_id', assetSysId);
                        finalAttachmentGr.orderByDesc('sys_created_on'); // Get the latest attachment
                        finalAttachmentGr.query();

                        if (finalAttachmentGr.next()) {
                            sysIdAttachment = finalAttachmentGr.sys_id;
                            attachmentLink = gs.getProperty('glide.servlet.uri') + 'sys_attachment.do?sys_id=' + sysIdAttachment;
                            attachmentLinks.push(attachmentLink);
                        }

                        // 4. Cleanup temporary record
                        tempRec.deleteRecord();
                    }
                }
            }

            targetConsumsGr.update();


            //despistar quantity bug
            // // Instead of targetConsumsGr.update(); ensure these fields are not changed.
            //     var originalQuantity = targetConsumsGr.quantity;
            //     targetConsumsGr.update();
            //     targetConsumsGr.setValue('quantity', originalQuantity); // Explicitly preserve quantity
        });

        //fazer if para diferença entre hardware e consumable(generaBarcode/generateBarcode2)
        var responseJSON = JSON.stringify({
            status: "success",
            barcode_links: attachmentLinks
        });

        return responseJSON;
    },


    //})

    generateBarcode2: function() {
       gs.info('Script Include chamado!');

        // Obtém os IDs enviados via GlideAjax
        var selectedIdsRaw = this.getParameter('sysparm_selected_ids');

        // Verifica se os IDs foram realmente recebidos
        if (!selectedIdsRaw || selectedIdsRaw.trim() === '') {
            gs.info('Nenhum ID recebido.');
            return JSON.stringify({ status: "error", message: "Nennhum consumível selecionado"});
        }

        // Divide os IDs em um array para processamento
        //var sysIds = selectedIds.split(',').map(id => id.trim());

        var selectedIds;

		try {
            selectedIds = JSON.parse(selectedIdsRaw);
        } catch (e) {
            gs.error('Erro ao converter JSON: ' + e.message);
            return JSON.stringify({ status: "error", message: "Erro ao processar JSON recebido." });
        }

		var selectedIdsArray = Array.isArray(selectedIds) ? selectedIds : selectedIds.split(',').map(id => id.trim());
		
		selectedIdsArray.forEach(function(id) {
            gs.info('ID recebido: ' + id);
        });
		
		var consumiveisEncontrados = [];
		var attachmentLinks = []; // Store generated barcode URLs (ENVIAR URLS PARA ui action download auto)

        // Itera sobre cada ID e consulta na base de dados
        selectedIdsArray.forEach(function(id) {
            var targetConsumsGr = new GlideRecord('alm_hardware');
            targetConsumsGr.addQuery('sys_id', id); // Consulta cada ID individualmente
            targetConsumsGr.query();

            if (!targetConsumsGr.hasNext()) { 
                gs.info('Consumível com ID ' + id + ' não encontrado.');
                return; // Se não encontrar, segue para o próximo
            } 

            while (targetConsumsGr.next()) {
                gs.info('Consumível encontrado: ' + targetConsumsGr.display_name);
	
				consumiveisEncontrados.push(targetConsumsGr.sys_id.toString()); // Armazena IDs encontrados
            
			var assetSysId = targetConsumsGr.sys_id;

			// model name to para o attachment name
			var model = targetConsumsGr.serial_number;

			// model name to para o barcode url
            var encodedModel = encodeURIComponent(model);

			// Verifica se este consumível já possui um anexo de código de barras
        var attachmentGr = new GlideRecord('sys_attachment');
        attachmentGr.addQuery('table_name', 'alm_hardware');
        attachmentGr.addQuery('table_sys_id', assetSysId);
        attachmentGr.query();

        if (attachmentGr.next()) {
		    gs.info('Consumível já tem código de barras. SEgue...');
            continue; // **Ignora este ID e passa para o próximo**
        }

			var imageUrl = 'https://quickchart.io/barcode?type=code128&includeText=true&text=' + encodedModel + '&format=png';
//var imageUrl = 'https://barcode.tec-it.com/barcode.ashx?data='+serialNumber+'&		code=Code128&dpi=96';

			//teste
			//gs.info("O valor do model é: " + model);
			//gs.info("Model Name: " + model);
			//gs.info("Encoded Model: " + encodedModel);
			//gs.info("Barcode URL: " + imageUrl);
			//gs.info("sys_id Capturado: "+assetSysId);	

			// 1. Create temporary record for initial attachment storage
	var tempRec = new GlideRecord('sys_attachment');
	tempRec.initialize();
	tempRec.file_name = model + '_temp.png';
	tempRec.table_name = 'alm_hardware';
	tempRec.insert(); // Create temporary holder record

	// 2. Fetch and save image to temporary record
	var request = new sn_ws.RESTMessageV2();
	request.setHttpMethod("GET");
	request.setEndpoint(imageUrl);
	request.setRequestHeader("Accept", "image/png");
	request.saveResponseBodyAsAttachment('sys_attachment', tempRec.sys_id, model + '_barcode.png');
	var response = request.execute();
	

	if (response.getStatusCode() === 200) {
	    // 3. Copy attachment to target asset record
	    var gsa = new GlideSysAttachment();
	    var grAsset = new GlideRecord('alm_hardware');

	    if (grAsset.get(assetSysId)) {
	          gsa.copy('sys_attachment', tempRec.sys_id, 'alm_hardware', grAsset.sys_id);

                        // Query sys_attachment again to get the correct sys_id of the newly created barcode
                        var finalAttachmentGr = new GlideRecord('sys_attachment');
                        finalAttachmentGr.addQuery('table_name', 'alm_hardware');
                        finalAttachmentGr.addQuery('table_sys_id', assetSysId);
                        finalAttachmentGr.orderByDesc('sys_created_on'); // Get the latest attachment
                        finalAttachmentGr.query();

                        if (finalAttachmentGr.next()) {
                            var sysIdAttachment = finalAttachmentGr.sys_id;
                            var attachmentLink = gs.getProperty('glide.servlet.uri') + 'sys_attachment.do?sys_id=' + sysIdAttachment;
                            attachmentLinks.push(attachmentLink);
                        }

                        // 4. Cleanup temporary record
                        tempRec.deleteRecord();
                    }
                }
            }
            
            targetConsumsGr.update();
        });

        var responseJSON = JSON.stringify({
            status: "success", 
            barcode_links: attachmentLinks 
        });

        return responseJSON; 
    },

    type: 'ConsumableBarcodeGenerator'
});

 

When I'm in list view I select one or more rows and then select the UI Action to generate a Barcode Image and download it automatically. 

Everything works as expected, except the auto-download, I get the error: Cannot read properties of null (reading 'createElement') when the code enters the function startAutoDownload. 

Can you please help?

I leave the UI Action in the attachments.

1 REPLY 1

joao_fonseca
Tera Contributor

Could only find a workarround using

g_navigation in the hardware UI ACTION to auto donwload