Join the #BuildWithBuildAgent Challenge! Get recognized, earn exclusive swag, and inspire the ServiceNow Community with what you can build using Build Agent.  Join the Challenge.

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