I see you have tried hard.
But isn't it more clever to validate input from that pin pad to have always correctly formatted serial number in hands?
I have an example for you prepared ..
[{"id":"f6a3dd5921436153","type":"ui_template","z":"f8b8c963bdc117f5","group":"07ca9eabfe300b52","name":"pinpad","order":2,"width":"4","height":"5","format":"<div class=\"serialNumber-widget\">\n    <div class=\"serialNumbertext\">{{serialNumber}}</div>\n    <div class=\"serialNumberpad\">\n        <div ng-click=\"serialNumberClick($event)\">1</div>\n        <div ng-click=\"serialNumberClick($event)\">2</div>\n        <div ng-click=\"serialNumberClick($event)\">3</div>\n        <div ng-click=\"serialNumberClick($event)\">4</div>\n        <div ng-click=\"serialNumberClick($event)\">5</div>\n        <div ng-click=\"serialNumberClick($event)\">6</div>\n        <div ng-click=\"serialNumberClick($event)\">7</div>\n        <div ng-click=\"serialNumberClick($event)\">8</div>\n        <div ng-click=\"serialNumberClick($event)\">9</div>\n        <div ng-click=\"serialNumberClick($event)\" class=\"control\">CLR</div>\n        <div ng-click=\"serialNumberClick($event)\">0</div>\n        <div ng-click=\"serialNumberClick($event)\" class=\"control\">🡄</div>\n    </div>\n    <div class=\"submit\" ng-click=\"submitSerialNumber()\">\n        SUBMIT\n    </div>\n<div>\n<style>\n    .serialNumberpad-container{\n        position:relative;\n        display:flex;\n        align-content: center;\n        flex-direction: row;\n        flex-wrap: wrap;\n        justify-content: center;\n    }\n    .serialNumber-widget{\n        width: 100%;\n        height: 100%;\n        display:grid;\n        gap:4px;\n        grid-template-rows:1fr 4fr 1fr;\n    }\n    .serialNumbertext{        \n        display: flex;\n        flex-direction: row;\n        align-items: center;\n        justify-content: flex-end;\n        font-size: large;\n        font-weight: 600;\n        min-height:1em;\n        padding-inline: 0.5em;\n        background: #a9a9a920;\n    }\n    .serialNumberpad{\n        display:grid;\n        grid-template-columns: 1fr 1fr 1fr;\n        gap: 4px;\n        width: 100%;\n        height: 100%;\n        cursor:auto;\n    }\n    .serialNumberpad div, .submit{\n        background:green;\n        display: flex;\n        flex-direction: row;\n        align-items: center;\n        justify-content: center;\n        font-size: large;\n        font-weight: 600;\n        user-select:none;\n        cursor:pointer;\n    }\n    .serialNumberpad div.control{\n        background:cornflowerblue;        \n    }\n    .submit{\n        background:palevioletred;\n        font-size: medium;\n        font-weight: 500;\n    }\n    \n</style>\n<script>\n(function(scope) {\n\n    scope.serialNumber = \"\"   \n    const delimiters = [{char:\"S\", pos:0},{char:\"n\", pos:1},{char:\" \", pos:2},{char:\"-\", pos:5}]\n    const length = 7 + delimiters.length\n\n    function clearSerialNumber(){\n        scope.serialNumber = \"\"\n        //scope.send({payload:scope.serialNumber,topic:'serialNumberpad-submit'})\n    }\n\n    function removeLast(){\n        // it does remove last but also delimitters when needed.\n        let count = -1      \n        delimiters.forEach(delimiter => {\n            if(scope.serialNumber.endsWith(delimiter.char)){\n                count = (delimiter.char.length + 1) * -1            \n            }            \n        })\n        scope.serialNumber = scope.serialNumber.slice(0, count)            \n    }    \n\n    function checkSerialNumber(){\n        // it must have correct length    \n        return scope.serialNumber.length == length\n    }\n\n    function updateSerialNumber(v){\n        if(checkSerialNumber()){\n            //if it has correct length, dont add more\n            return\n        }\n        // chek if next character needs to be delimiter, if so, it will be added\n        scope.serialNumber = checkDelimiters(scope.serialNumber)\n        // add input from key pad\n        scope.serialNumber = scope.serialNumber.concat(v)\n        // chek again for delimitters        \n        scope.serialNumber = checkDelimiters(scope.serialNumber)\n    }\n\n    function checkDelimiters(s){\n        // check if it is neede to add some of delimitters\n        delimiters.forEach(delimiter => {\n            if(s.length == delimiter.pos){\n                s = s.concat(delimiter.char)\n            }\n        })        \n        return s\n    }\n\n   \n\n    scope.serialNumberClick = function(e){\n        // input from key pad elements\n        let v = e.target.innerHTML\n        if(v == \"CLR\"){\n            clearSerialNumber()\n        }\n        else if(v == \"🡄\"){\n            removeLast()\n        }\n        else{\n            updateSerialNumber(v)\n            \n        }        \n    }\n    scope.submitSerialNumber = function(){\n        if(!checkSerialNumber()){\n            //don't send if the number is not complete            \n            return\n        }       \n        scope.send({payload:scope.serialNumber,topic:'serialNumberpad-submit'})\n    }\n    \n\n})(scope);\n</script>","storeOutMessages":true,"fwdInMessages":false,"resendOnRefresh":true,"templateScope":"local","className":"pinpad-container","x":490,"y":380,"wires":[["354adbba6efd0aa1"]]},{"id":"354adbba6efd0aa1","type":"debug","z":"f8b8c963bdc117f5","name":"SERIAL","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":640,"y":380,"wires":[]},{"id":"07ca9eabfe300b52","type":"ui_group","name":"Pinpad","tab":"62083694d0eab7ca","order":2,"disp":true,"width":"6","collapse":false,"className":""},{"id":"62083694d0eab7ca","type":"ui_tab","name":"Home","icon":"dashboard","order":1,"disabled":false,"hidden":false}]
It doesn't take any input (it's for you to add and figure out what to do with the input then) but happily creates correctly formatted string when you use the keypad.