Tap Forms app icon half
Tap Forms Forum text image
Blue gradient background

Exchange tips and ideas with the Tap Forms community

Search Results for 'script'

Viewing 15 results - 2,011 through 2,025 (of 2,952 total)
  • Author
    Search Results
  • #39648

    In reply to: Finding earliest date

    Larry Stoter
    Participant

    I actually want to find the earliest date in a field as a step in a more complex script.

    However, I have an apparently easy solution :-) Create a new calculation field Date#, simply put the Date field into the calculation and then set the Result Type to Number. The Date# field then shows numbers – not sure what numbers but the earliest date has the smallest number in the Date# field.

    Which seems to do what I want.

    #39645
    Sam Moffatt
    Participant

    Ok, so I found the other one which is actually a variation of the one I posted though simplified in a way. It works on two forms: one for boxes and a second for purchases that has a “box ID” field in it as a text field.

    This uses the prompter functions again and also getRecordFromFormWithKey to find matching records.

    Let’s get started!

    var PARENT_SCRIPT = 'Boxes::Bulk Add To Box';
    document.getFormNamed('Script Manager').runScriptNamed('Prompter Functions');
    document.getFormNamed('Script Manager').runScriptNamed('getRecordFromFormWithKey');
    

    Define the PARENT_SCRIPT to prevent stuff like Profiler running it’s self tests. My internal version of getRecordFromFormWithKey currently has profiler support, I’ll update it later. We need the prompter functions again from earlier as mentioned.

    // Boxes
    var box_id_id = 'fld-f25492cf56384369b278f7ea41ee258f';
    
    var box_id = record.getFieldValue(box_id_id);
    

    This form I have this in is called “Boxes” and it has a field called “Box ID” which we grab when we start the form script. These are those variables.

    // Purchases
    var state_id = 'fld-9402f8c0d53c43b986fee4ebc3468929';
    var bag_barcode_id = 'fld-32d459f0b5fb4dc4974795c484832af1';
    var storage_box_id = 'fld-c08e3a9eb7784d7f8ee3a5576c0adffa';
    var title_id = 'fld-0d0edd2552ea461e929f806a4e5552b5';
    

    Purchases is the track of things I bought, it has it’s own barcode (in “Bag Barcode”) and it also has a text field for the storage box. There are also state and title fields for it as well.

    async function start(){
    	try {
    		while (1)
    		{
    			let barcode = '';
    			let scanBarcode = await promptText("Scan Bag", "Barcode:");
    			
    			if (!scanBarcode)
    			{
    				break;
    			}
    			
    			console.log(scanBarcode);
    

    If this looks familiar, it should because it’s the previous code without the zipcode and carrier removed. Also “Shipment” is now “Bag”. Cool beans!

    let correctBarcode = await promptConfirm(`Is ${scanBarcode} correct?`);
    			
    			if (!correctBarcode)
    			{
    				console.log('Incorrect barcode, skipping');
    				continue;
    			}			
    

    This part is new, it asks if the scanned bar code is correct. This lets you hit cancel if it’s wrong. If you’re using this with a hand held scanner (which I am), then generally it’ll insert an “enter” at the end of the input. This means you can scan a barcode, validate it on screen and scan the barcode again to confirm this dialog. It gives you an escape key to abort from.

    
    			let targetRecord = getRecordFromFormWithKey('Purchases', bag_barcode_id, scanBarcode, false, false);
    				
    			if (targetRecord)
    			{
    

    This looks for a record in my Purchases form looking in the bag_barcode_id field for the value of scanBarcode that we just scanned. The last two prevent some optimisations and also prevents autocreating records.

    
    				let title = targetRecord.getFieldValue(title_id);
    				
    				let correctTitle = await promptConfirm(`Is ${title} expected?`);
    				
    				if (!correctTitle)
    				{
    					console.log('Incorrect title, skipping');
    					continue;
    				}
    

    We do an extra confirmation to check that the title matches what we expect. Again if you are using a barcode scanner you can scan again to progress.

    
    				// Set the delivered flag just in case it isn't set already.
    				targetRecord.setFieldValue(state_id, 'Delivered');
    

    My purchases has a state field and we just update this to delivered when we’re scanning something. My state tracking was a later addition so this helps to clean up anything I touch.

    
    				let boxId = targetRecord.getFieldValue(storage_box_id);
    				
    				if (!boxId)
    				{
    				targetRecord.setFieldValue(storage_box_id, box_id);
    				}
    				else
    				{
    					Utils.alertWithMessage('Box ID Already Set', `This barcode already has already been scanned with ${boxId}`);
    				}
    

    We then check to see if we have a box ID already and if it isn’t set we set it. If it is set then we flag this with the user. In your case you could change this up to confirm with another prompt (like shown earlier) and confirm reassigning the boxes.

    
    				document.saveAllChanges();
    				console.log("Updated existing record for " + scanBarcode);
    			}
    

    Save all the changes and I make a log message. I like log messages.

    
    			else
    			{
    				Utils.alertWithMessage('Unable to find record', `No matching record for ${scanBarcode}`);
    			}
    		}
    

    The else is to prompt a message saying we can’t find the barcode.

    
    	} catch (error) {
    		console.log("Error: " + error);
    	}
    }
    

    Just like the last one, we catch the error and log it as a fall back.

    
    if (box_id)
    {
    	start();
    }
    else
    {
    	Utils.alertWithMessage('Invalid box', `Invalid box ID found: ${box_id}`);
    }

    This is a little different, it checks to see if you have a record in the Box form before starting up. The intent is that you are in a box and scanning items into said box.

    Here’s the script in full:

    var PARENT_SCRIPT = 'Boxes::Bulk Add To Box';
    document.getFormNamed('Script Manager').runScriptNamed('Prompter Functions');
    document.getFormNamed('Script Manager').runScriptNamed('getRecordFromFormWithKey');
    
    // Boxes
    var box_id_id = 'fld-f25492cf56384369b278f7ea41ee258f';
    
    var box_id = record.getFieldValue(box_id_id);
    
    // Purchases
    var state_id = 'fld-9402f8c0d53c43b986fee4ebc3468929';
    var bag_barcode_id = 'fld-32d459f0b5fb4dc4974795c484832af1';
    var storage_box_id = 'fld-c08e3a9eb7784d7f8ee3a5576c0adffa';
    var title_id = 'fld-0d0edd2552ea461e929f806a4e5552b5';
    
    async function start(){
    	try {
    		while (1)
    		{
    			let zipcode = undefined;
    			let carrier = '';
    			let barcode = '';
    			let scanBarcode = await promptText("Scan Bag", "Barcode:");
    			
    			if (!scanBarcode)
    			{
    				break;
    			}
    			
    			console.log(scanBarcode);
    			
    			let correctBarcode = await promptConfirm(`Is ${scanBarcode} correct?`);
    			
    			if (!correctBarcode)
    			{
    				console.log('Incorrect barcode, skipping');
    				continue;
    			}			
    			
    			let targetRecord = getRecordFromFormWithKey('Purchases', bag_barcode_id, scanBarcode, false, false);
    				
    			if (targetRecord)
    			{
    				let title = targetRecord.getFieldValue(title_id);
    				
    				let correctTitle = await promptConfirm(`Is ${title} expected?`);
    				
    				if (!correctTitle)
    				{
    					console.log('Incorrect title, skipping');
    					continue;
    				}
    			
    				// Set the delivered flag just in case it isn't set already.
    				targetRecord.setFieldValue(state_id, 'Delivered');
    				
    				let boxId = targetRecord.getFieldValue(storage_box_id);
    				
    				if (!boxId)
    				{
    				targetRecord.setFieldValue(storage_box_id, box_id);
    				}
    				else
    				{
    					Utils.alertWithMessage('Box ID Already Set', `This barcode already has already been scanned with ${boxId}`);
    				}
    				document.saveAllChanges();
    				console.log("Updated existing record for " + scanBarcode);
    			}
    			else
    			{
    				Utils.alertWithMessage('Unable to find record', `No matching record for ${scanBarcode}`);
    			}
    		}
    	} catch (error) {
    		console.log("Error: " + error);
    	}
    }
    
    if (box_id)
    {
    	start();
    }
    else
    {
    	Utils.alertWithMessage('Invalid box', `Invalid box ID found: ${box_id}`);
    }
    #39644

    Topic: Profiler Script

    in forum Script Talk
    Sam Moffatt
    Participant

    One of the scripts I use a lot is my profiler. I use it to figure out timing data for how long something takes for my scripts. It’s useful for understanding where your bottlenecks are in code and for figuring out what the true slow downs are.

    If you look at the very bottom of the script, there is an example/test for the script so you can see how it works. I use this with a Script Manager form which makes including easy like this:

    document.getFormNamed('Script Manager').runScriptNamed('Profiler');
    

    Due to the way the tests are coded, you want to define a variable called PARENT_SCRIPT to prevent the test from firing when being included. Generally I do this in the format of Form Name::Script Name, e.g.:

    var PARENT_SCRIPT = 'Boxes::Bulk Add To Box';

    Using it is easy once you’ve included it:

    profiler.start('Start time');
    ...
    your code that does stuff!
    ...
    profiler.end();
    

    Profiler internally uses a stack so end closes the most recent start. If you do a profiler.dump() you will get back a string with the profiling information formatted with indentation. Here is a copy of the test running as an example:

    24/2/20, 9:59:19 pm / Script Manager / Profiler
    Caught expected error from dump: Error: Profiler imbalance: started called 1 more times than end.
    Uneven profiler.end called (are you missing a start?)
    Dump called with index 7 and indent 0
    0ms  	Test 1
        0ms  	Test 2
        0ms  	Test 3
            0ms  	Test 4
    0ms  	Function start
        0ms  	Complex logic
    0ms  	Mismatch
    

    Here’s the full script:

    // ========== Profiler Start ========== //
    // NAME: Profiler
    // VERSION: 1.0.0
    /**
     * Profiler module stores timing data.
     */
    if (profiler == undefined)
    
    var profiler = (function() {
    	let stack = [];
    	let entries = [];
    	let index = 0;
    	let indent = 0;
    	let current = undefined;
    	return {
    		custom: function(start, end, message)
    		{
    			entries[index] = {'start': start, 'end': end, 'message': message, 'index': index, 'indent': indent};
    			index++;
    		},
    		/**
    		 * Start a timer.
    		 *
    		 * Takes a <code>message</code> and adds it to the internal <code>logdata</code> variable
    		 *   and also logs it to the console with a timestamp.
    		 */
    		start: function(message)
    			{
    				let now = new Date();
    				stack.push({'start': now.getTime(), 'message': message, 'index': index, 'indent': indent});
    				index++;
    				indent++;
    			},
    
    		/**
    		 * Log an error
    		 *
    		 * Takes a <code>message</code> and <code>error</code> object and then formats into an error
    		 *   via <code>logMessage</code>.
    		 */
    		end: function(endCount = 1)
    			{
    				for (let i = 0; i < endCount; i++)
    				{
    					let now = new Date();
    					let entry = stack.pop();
    					if (entry)
    					{
    						entry['end'] = now.getTime();
    						entries[entry.index]= entry;
    						indent--;
    					}
    					else
    					{
    						console.log('Uneven profiler.end called (are you missing a start?)');
    					}
    				}
    			},
    
    		/**
    		 * Get the internal log buffer
    		 *
    		 * Returns the raw log buffer to output elsewhere.
    		 */
    		dump: function(autoClose = true)
    			{
    				let returnValue = [];
    				returnValue.push(<code>Dump called with index ${index} and indent ${indent}</code>);
    				let dumpIndent = indent;
    				
    				if (indent > 0)
    				{
    					if (!autoClose)
    					{
    						throw Error("Profiler imbalance: started called " + indent + " more times than end.");
    					}
    					else
    					{
    						// Close out any mismatched items.
    						this.end(indent);
    					}
    				}
    				
    				let entry = null;
    				for(entry of entries)
    				{
    					let message = (entry.end - entry.start) + 'ms  \t' + entry.message;
    					returnValue.push(message.padStart(message.length + (entry.indent * 4)));
    				}
    				return returnValue.join('\n');
    			},
    
    		/**
    		 * Clear the internal state.
    		 */
    		reset: function()
    			{
    				stack = [];
    				entries = [];
    				index = 0;
    				indent = 1;
    			}
    	}
    })();
    
    // Testing section. Fake guard for testing.
    if (typeof PARENT_SCRIPT === 'undefined')
    {
    	profiler.start('Test 1');
    		profiler.start('Test 2');
    		profiler.end();
    		profiler.start('Test 3');
    			profiler.start('Test 4');
    			profiler.end();
    		profiler.end();
    	profiler.end();
    	profiler.start('Function start');
    	profiler.start('Complex logic');
    	profiler.end(2);
    	profiler.start('Mismatch');
    	try 
    	{
    		profiler.dump(false);
    	}
    	catch (e)
    	{
    		console.log('Caught expected error from dump: ' + e);
    	}
    	// Call end one too many times.
    	profiler.end(2);
    	
    	console.log(profiler.dump());
    	profiler.reset();
    }
    // ========== Profiler End ========== //
    

    Latest version of this script is available on GitHub: https://github.com/pasamio/tftools/blob/master/scripts/js/profiler.js

    #39643
    Sam Moffatt
    Participant

    I’ll walk through this a little as this is my bulk scan script. It’s probably a bit much but it has a bunch of piece that might help.

    document.getFormNamed('Script Manager').runScriptNamed('Prompter Functions');
    

    This is pulling in my Prompter Functions via the Script Manager method. Basically Script Manager is a Tap Forms form named ‘Script Manager’ and then it uses ‘Form Scripts’.

    var tracking_number_id = 'fld-c487390743c947969cbe661cff596855';
    var received_date_id = 'fld-e3e3539ee04f4cc7971c7098c572104d';
    var confirmed_id = 'fld-2adb9ba8cdd048bbbb614d46b415ada5';
    var alternate_tracking_numbers_id = 'fld-cf8718051bea4cc2aba0069ae76f32b7';
    var alternate_tracking_number_id = 'fld-7342203d8f36415191bf8419fb6f70dc';
    var carrier_id = 'fld-0950c430cb0c41f79c51d43a544b366b';
    var zip_code_id = 'fld-4f73faa8937446a0a3b24e6dd4624d6b';
    var shipper_id = 'fld-1789ae45fd1f4f588294eff0c2fb6045';
    

    These are all of the field ID’s I care about, you’ll see them spliced through. Obviously your fields will be different.

    function findRecord(targetTrackingNumber)
    {
    	var targetRecord = null;
    	MainLoop:
    	for(candidateRecord of form.getRecords())
    	{
    		if (targetTrackingNumber == candidateRecord.getFieldValue(tracking_number_id))
    		{
    			targetRecord = candidateRecord;
    			break MainLoop;
    		}
    		
    		
    		for (alternateRecord of candidateRecord.getFieldValue(alternate_tracking_numbers_id))
    		{
    			if (targetTrackingNumber == alternateRecord.getFieldValue(alternate_tracking_number_id))
    			{
    				targetRecord = candidateRecord;
    				break MainLoop;
    			}
    		}
    	}
    	
    	return targetRecord;
    }
    

    This is a quick method to basically brute force scan all of the records and search for them. I mentioned earlier that Brendan is adding a function to make this a little less relevant however what it’s doing is getting a list of all of the records, checking if the tracking number matches (in your case it’ll be your box barcode or item barcode) and it also checks the values of a table field that is in my shipments form. Because there are two levels of loops in this, I use a trick to make sure I break out of them easily. Shipping is fun where a package might have more than one tracking number, hence the crazy setup.

    async function start(){
    	try {
    		while (1)
    		{
    

    This is the start of the meat and it has three things to be interested in. The async keyword is required to get my prompter functions to work properly and enable you to do blocking calls without having to manage the callbacks all yourself. The try is a fallback for an exceptions that might escape and the while (1) is our infinite loop.

    			let zipcode = undefined;
    			let carrier = '';
    			let barcode = '';
    			let scanBarcode = await promptText("Scan Shipment", "Barcode:");
    

    This sets up some variables, in my case I’m tracking zipcode for the package (USPS embed this into their tracking number), a carrier that I can automatically infer (USPS and UPS) and then obviously the core barcode. This uses the promptText function which is in that Prompter Functions I mentioned earlier. It has await prefixed to make it block until we get a response back.

    			if (!scanBarcode)
    			{
    				break;
    			}
    

    This is pretty straight forward, if we don’t get anything back from the barcode then we abort the loop. This is our exit criteria.

    			let matches = scanBarcode.match(/420(9[0-9]{4})(.*)/);
    			if (matches)
    			{
    				carrier = 'USPS';
    				zipcode = matches[1];
    				barcode = matches[2];
    			}
    			else
    			{
    				barcode = scanBarcode;
    			}
    

    This isn’t relevant to you as much but it basically looks for a USPS barcode and extracts the useful information out of it. It resets the barcode to be what the label and tracking number actually is versus the full details that are encoded into the barcode. I’m in California so my zipcode is prefixed with a 9 which is why it looks the way it does.

    			matches = scanBarcode.match(/^1Z/);
    			if (matches)
    			{
    				carrier = 'UPS';
    			}
    

    This also isn’t as relevant but it looks for a UPS style barcode and sets it automatically. Depending on how your barcode generation is done, this might be something you can apply where you have different barcode prefixes for stuff (or not).

    			console.log(barcode);
    			
    			let targetRecord = findRecord(barcode);
    

    I logged the barcode because I wanted to see what it was in the console. This is useful for understanding when something weird happens. This is then fed into that findRecord method before to find a record that matches, or not.

    			if (targetRecord)
    			{
    				// Flip confirmed flag but otherwise leave it alone.
    				targetRecord.setFieldValue(confirmed_id, true);
    				document.saveAllChanges();
    				console.log("Updated existing record for " + barcode);
    			}
    

    For me I’m looking to confirm or create a record that I’m scanning. In this case if for some reason the shipping number already exists and I found a matching record, I just toggle a flag saying that I know it exists and move on.

    			else
    			{
    				let payload = {
    					[tracking_number_id]: barcode,
    					[confirmed_id]: true
    				};
    

    Ok, the else case means this tracking number doesn’t exist already so we need to create it. I start to create a new record here. This syntax with the square brackets is to get the value of tracking_number_id instead of using tracking_number_id as the key.

    				if (carrier)
    				{
    					payload[carrier_id] = carrier;
    					payload[zip_code_id] = zipcode;
    				}
    

    If there is a carrier set then this gets set up as well including the zipcode (USPS).

    				let shipper = await promptText("Enter Shipper Name", "Shipper: ");
    				console.log(shipper);
    				
    				if (shipper)
    				{
    					payload[shipper_id] = shipper;
    				}
    

    I ask for the shipper name in case that’s obvious, again with the promptText method. That’s useful for knowing where something is from if I want to add it in.

    				console.log(JSON.stringify(payload));
    
    				let newRecord = form.addNewRecord();
    				newRecord.setFieldValues(payload);
    				document.saveAllChanges();
    

    I log out what I’m about to create to see what it is during debugging. I then create a new record, use setFieldValues to set up the values and then save the changes. Too easy!

    			}
    		}
    	} catch (error) {
    		console.log("Error: " + error);
    	}
    }
    
    start();

    This is closing everything out and then triggering the script to begin with. The catch is the follow up to the try and is a fail safe to log the message and go from there. It’s closing out the loop so assuming you enter in a valid barcode, it’ll keep looping until it’s done.

    I thought I had another script that handled a little closer to what you were doing but I can’t find where I put it.

    Here’s the script in full:

    document.getFormNamed('Script Manager').runScriptNamed('Prompter Functions');
    
    var tracking_number_id = 'fld-c487390743c947969cbe661cff596855';
    var received_date_id = 'fld-e3e3539ee04f4cc7971c7098c572104d';
    var confirmed_id = 'fld-2adb9ba8cdd048bbbb614d46b415ada5';
    var alternate_tracking_numbers_id = 'fld-cf8718051bea4cc2aba0069ae76f32b7';
    var alternate_tracking_number_id = 'fld-7342203d8f36415191bf8419fb6f70dc';
    var carrier_id = 'fld-0950c430cb0c41f79c51d43a544b366b';
    var zip_code_id = 'fld-4f73faa8937446a0a3b24e6dd4624d6b';
    var shipper_id = 'fld-1789ae45fd1f4f588294eff0c2fb6045';
    
    function findRecord(targetTrackingNumber)
    {
    	var targetRecord = null;
    	MainLoop:
    	for(candidateRecord of form.getRecords())
    	{
    		if (targetTrackingNumber == candidateRecord.getFieldValue(tracking_number_id))
    		{
    			targetRecord = candidateRecord;
    			break MainLoop;
    		}
    		
    		
    		for (alternateRecord of candidateRecord.getFieldValue(alternate_tracking_numbers_id))
    		{
    			if (targetTrackingNumber == alternateRecord.getFieldValue(alternate_tracking_number_id))
    			{
    				targetRecord = candidateRecord;
    				break MainLoop;
    			}
    		}
    	}
    	
    	return targetRecord;
    }
    
    async function start(){
    	try {
    		while (1)
    		{
    			let zipcode = undefined;
    			let carrier = '';
    			let barcode = '';
    			let scanBarcode = await promptText("Scan Shipment", "Barcode:");
    			
    			if (!scanBarcode)
    			{
    				break;
    			}
    
    			let matches = scanBarcode.match(/420(9[0-9]{4})(.*)/);
    			if (matches)
    			{
    				carrier = 'USPS';
    				zipcode = matches[1];
    				barcode = matches[2];
    			}
    			else
    			{
    				barcode = scanBarcode;
    			}
    			
    			matches = scanBarcode.match(/^1Z/);
    			if (matches)
    			{
    				carrier = 'UPS';
    			}
    			console.log(barcode);
    			
    			let targetRecord = findRecord(barcode);
    				
    			if (targetRecord)
    			{
    				// Flip confirmed flag but otherwise leave it alone.
    				targetRecord.setFieldValue(confirmed_id, true);
    				document.saveAllChanges();
    				console.log("Updated existing record for " + barcode);
    			}
    			else
    			{
    				let payload = {
    					[tracking_number_id]: barcode,
    					[confirmed_id]: true
    				};
    				
    				if (carrier)
    				{
    					payload[carrier_id] = carrier;
    					payload[zip_code_id] = zipcode;
    				}
    
    				let shipper = await promptText("Enter Shipper Name", "Shipper: ");
    				console.log(shipper);
    				
    				if (shipper)
    				{
    					payload[shipper_id] = shipper;
    				}
    				
    				console.log(JSON.stringify(payload));
    
    				let newRecord = form.addNewRecord();
    				newRecord.setFieldValues(payload);
    				document.saveAllChanges();
    			}
    		}
    	} catch (error) {
    		console.log("Error: " + error);
    	}
    }
    
    start();
    #39641
    Sam Moffatt
    Participant

    Credit to @daniel_leu for this originally and I think a variant of them will be coming in the sample scripts. These are the two that I use personally and I figured I’d share them. I added this in my “Script Manager” form as “Prompter Functions”, so if you have that included already then you can add this as a new one.

    If you’re going to use these, the calling function needs to be prefixed with async to make it work properly.

    // ========== Prompter Functions Start ========== //
    // NAME: Prompter Functions
    // VERSION: 1.0
    /**
     * Prompter Functions for using the async system to handle some requests.
     */
     
    // Temporary variable to store callback details.
    var prompterTempVar = undefined;
    
    /**
     * Prompt a confirmation dialog with a simple message.
     *
     * NOTE: This method uses async, please make sure your calling method is declared 'async'.
     *
     * @param {string} text - The text to display in the dialog for the confirmation.
     * @param {string} continueTitle - The text for the continue button.
     * @param {string} cancelTitle - The text for the cancel button.
     *
     * @return {boolean} Boolean result if the user confirmed or declined the dialog.
     */
    function promptConfirm(text, continueTitle = 'Continue', cancelTitle = 'Cancel') {
    	return new Promise(function(resolve, reject) {
    	  	let prompter = Prompter	.new();
      		prompter.cancelButtonTitle = cancelTitle;
    		prompter.continueButtonTitle = continueTitle;
    		prompter.show(text, ((status) => {
    			if (status == true) {
    				resolve(true);
    			} else {
    				resolve(false);
    			}		
    		}));
    	});
    }
    
    /**
     * Prompt 
     *
     * NOTE: This method uses async, please make sure your calling method is declared 'async'.
     *
     * @param {string} text - The text to display in the dialog for the confirmation.
     * @param {string} popupName - The prefix to display for the text box of the prompt.
     * @param {string} [continueTitle=Continue] - The text for the continue button.
     * @param {string} [cancelTitle=Cancel] - The text for the cancel button.
     *
     * @return {(string|boolean)} The input text or false if cancelled.
     */
    function promptText(text, popupName, continueTitle = 'Continue', cancelTitle = 'Cancel'){
    	return new Promise(function(resolve, reject) {
    		prompterTempVar = undefined;
    	  	let prompter = Prompter	.new();
    	  	prompter.addParameter(popupName, 'prompterTempVar')
      		prompter.cancelButtonTitle = cancelTitle;
    		prompter.continueButtonTitle = continueTitle;
    		prompter.show(text, ((status) => {
    			if (status == true && prompterTempVar) {
    				resolve(prompterTempVar);
    			} else {
    				resolve(false);
    			}		
    		}));
    	});
    }
    // ========== Prompter Functions End ========== //
    

    You can also get a copy of this file here: https://github.com/pasamio/tftools/blob/master/scripts/js/prompter.js.

    #39640
    Max Heart
    Participant

    Hi again,

    I just followed your advise and for the manual quick linking of items from box to checkout and back it works. Thank you very much for this part.

    Now it would be awesome to run this procedure script based: Scan a box and keep assigning items to this box by batch scanning their barcodes until the script is stopped.

    So the script should do this:
    1) Ask for a box to scan
    2) Pull the box record from the box form
    3) Prompt for barcode
    4) Read barcode, assign the item to box, best with short feedback for 3-5 seconds “Item XX added to box”.
    5) Ask for next barcode
    6) Repeat procedure until stopped.

    @Sam: Is this what your shipment script does?

    #39639
    Max Heart
    Participant

    Hi Sam,

    thank you very much for the advise. I am happy to hear that my objective should be reachable. When you post your script can you please give me a hint on how to get it running? I haven’t worked with scripts in Tapform yet. I know that “script” is a field type. Is it enough to just paste a script there?

    Best, Max

    #39638
    Sam Moffatt
    Participant

    I have recently been working on something like that to scan a bunch of items into boxes and similar. There are a couple of ways of handling it.

    I’m going to rephrase some of your questions:

    Can I pull up a box by scanning it’s barcode?

    This one is relatively straight forward application of the search functionality, that’s easy out of the box regardless of what platform you’re on. On the desktop you’ll need a barcode scanner to make that work but on iOS the built in barcode scanning functionality will make that work for you there.

    Can I scan barcodes of items as I place them into the box?

    The way I did this in January when I was doing a lot of box packing for moving was to create a second form called “Box Items” that is linked from my main “Box” form and then created child records. I was taking pictures of the item and adding some extra metadata in this case so I just used the built in Tap Forms barcode scanner to scan items and used voice to text to quick enter titles (worked enough times to save me having to type).

    Now if you invest in a barcode scanner, you can take it a little bit further and create a prompter loop to scan barcodes and automatically create new child records. You can also pivot and if you’re confident an item will only be in a single box, you can make it’s box barcode a property of the item. I have a prompter loop I use for bulk scanning shipments, I’ll post that on the Script Talk forum later.

    Can I checkout of a box?

    If you go with the Link to Form and enable show inverse relationship (or what ever that checkbox is called), then when you scan in your item in Tap Forms using the search feature it will show you the record and the “Link From Form” will show you the details of the box that it thinks it should be in. In Tap Forms on the desktop you can select this and change it to search for a new box which you should be able to do by barcode for your checkout box or just have a box named “checkout”. By changing the value of the Link from Form field parent will register it to a new box. The iOS version has a similar interaction here but I feel that it isn’t quite as smooth as the desktop more due to the limitations of iOS rather than Tap Forms (the desktop shows a popover which makes it much easier to interact with).

    If you go for the field based approach, you just need to update the field to where ever you put the item.

    In both cases you can script it to handle it easier. Scripting the Link to Form/Link from Form is a little more difficult because in the current production release of Tap Forms there is no search by text via the script interface. Brendan mentioned that he’s adding a form.getRecordsForSearchTerm() to the next beta which would make that easier because the JavaScript API for the link fields works with records. Until then, it’s a little easier to update a simple text field based on a search.

    I have a dedicated barcode scanner for the desktop, I bought a NETUM Bluetooth scanner that has both a USB cable for input and can pair via Bluetooth. I’m not sure I’d recommend it, it works fine in Bluetooth mode but when I plug in the USB cable it mangles the numbers some how (it’s like it’s pressing on the keypad numbers upside down). On the Mac I use Karabiner Elements whose mere existence seemed to make the scanner behave properly.

    Max Heart
    Participant

    Hi everyone,

    I am trying again to find help. The first round, including some good advise was here:

    Inventory barcode scanning – assign box/ placement


    Unfortunately, I did not manage to create a workflow as I am not familiar with scripting.

    Here’s my goal and setting:
    I want to create a home inventory which helps me finding my stuff quickly. I have two forms: one for boxes and one for the items. From the boxes form I created a one to many link as one box contains many items but one item can only be in one box at a time. Each box has a QR code, each item has a bar code.
    I managed to create my forms and the relationship and search is working. The barcode search will find items and boxes.

    Here comes the challenges:
    1) Batch entry of items using barcode
    Can I pull up a box by scanning its barcode and then start a batch scan barcodes of items as I place them in the box?
    2) Batch checkout of items
    Let’s say I pack for a trip and remove a lot of stuff from different boxes. I guess, in the boxes form I should create a box called “Checked Out”. Is it possible to first search for an item, have the box displayed where it is and then move this item to “Checked Out”?

    I use my iPhone for scanning but in case this workflow only works with a dedicated barcode scanner on the desktop version, I would be happy to buy one. I am just kind of lost and couldn’t find a tutorial.

    Thank you for your help, Max

    #39632
    JBW
    Participant

    Hey there. I’m trying to move from airtable to tap forms.

    I can (and have) downloaded the tables from airtable to csv files. I can import these csv files as forms into tap forms. However, I cannot seem to link certain fields to other forms during the import. After I’ve toyed around a bit, I think I understand why I couldn’t do that. But maybe I’m missing something.

    So I tried to build a script that would take an existing text field, and move it over to an already “linked to” form. But I’m not a script writer.

    I need some help.

    I have a form called “classes” with a field called “players” that has comma delimited names in it as text. I would like to have a script that goes to each “players” field, grab each name, and add them to the “linked to” field called “players-linked”.

    I have another form called “players” that has each individual as a record (first name, last name, etc.).

    I can code a little, but I don’t understand how to handle the loops, or selections in tap forms. I need to do this script to a handful of other fields as well. If I can get one to work, I feel confident I can modify it to make the others work. Any ideas?

    #39616
    Sam Moffatt
    Participant

    Try revenue_total.toFixed(2); as your last line and see if that gives you the desired effect.

    As a lateral answer, you can configure the script field with a particular format. If you reconfigure your script field to use a “Number” return type, you can then use the number formatting options in the field configuration to have it display the way you want.

    #39611
    Bernhard
    Participant

    @Sam: Sounds good – thanks for the advice!
    For the record, here is how it works:

    
    var customerRecordIds = [];
    
    // Callback function of the Prompter call.
    function chooseCustomerCallback() {
    	createInvoice(customerRecordIds[choosenTitle]);
    }
    
    // Do something with the record after the user choose one.
    function createInvoice(recordId) {
    	var customerRecord = form.getRecordWithId(recordId);	
    	...
    }
    
    // The entry function of the script
    function Erstelle_Rechnung() {	
    	var titleFieldId = 'fld-aa817f3bd885458883d3e25802fd4037';
     	var customersForm = document.getFormNamed('Kunden');
     	var customers = customersForm.getRecords();
    	var companyTitles = [];
    	
    	for (var index = 0, count = customers.length; index < count; index++) {
    		const title = customers[index].getFieldValue(titleFieldId);
    		companyTitles.push(title);
    		// Remember the Record ID of this customer record to be used later.
    		customerRecordIds[title] = customers[index].getId();
    	}
    
    	var choosenTitle;
    
    	let prompter = Prompter.new();
    	prompter.addParameter('Kunde', 'choosenTitle', 'popup', companyTitles)
    		.show('Message prompt', chooseCustomerCallback);
    
    	return null;
    }
    
    Erstelle_Rechnung();
    
    #39609
    Bernhard
    Participant

    Sounds good – thanks for the advice!
    For the record, here is how it works:

    
    var customerRecordIds = [];
    
    // Callback function of the Prompter call.
    function chooseCustomerCallback() {
    	createInvoice(customerRecordIds[choosenTitle]);
    }
    
    // Do something with the record after the user choose one.
    function createInvoice(recordId) {
    	var customerRecord = form.getRecordWithId(recordId);	
    	...
    }
    
    // The entry function of the script
    function Erstelle_Rechnung() {	
    	var titleFieldId = 'fld-aa817f3bd885458883d3e25802fd4037';
     	var customersForm = document.getFormNamed('Kunden');
     	var customers = customersForm.getRecords();
    	var companyTitles = [];
    	
    	for (var index = 0, count = customers.length; index < count; index++) {
    		const title = customers[index].getFieldValue(titleFieldId);
    		companyTitles.push(title);
    		// Remember the Record ID of this customer record to be used later.
    		customerRecordIds[title] = customers[index].getId();
    	}
    
    	var choosenTitle;
    
    	let prompter = Prompter.new();
    	prompter.addParameter('Kunde', 'choosenTitle', 'popup', companyTitles)
    		.show('Message prompt', chooseCustomerCallback);
    
    	return null;
    }
    
    Erstelle_Rechnung();
    
    #39608
    Barry Shevlin
    Participant

    I’m almost embarrassed to ask this given it’s probably such a basic question. However, I could well use the following script given as an example in the TF5 manual:

    var records = form.getRecords();
    var revenue_total = 0;

    for (var index = 0, count = records.length; index < count; index++){
    var theRec = records[index];
    var movie_revenue = theRec.getFieldValue(‘fld-987c9aac738a4f0fa1d18395902b3fc1’);
    if (movie_revenue) {
    revenue_total += movie_revenue;
    console.log(‘Revenue: ‘ + movie_revenue);
    }
    }

    revenue_total;

    My question is this: how do I limit the revenue_total return to 2 decimal places (currency)? I am getting this sort of thing: £63.9300000000001.

    Sorry if this time-wasting but I’m sure there’s a quick answer that would save me hours of fruitless futzing. Thanks.

    #39594
    Bernhard
    Participant

    Hi all,

    as Javascript/Typescript programmer I’m used to the comfort of IDE’s like Visual Studio Code.
    I would like to edit Tap Forms scripts in my preferred editing application. In the _Edit form script_ window I could imagine a button like _Edit in external editor_ which opens the script in a configured editor application and automatically syncs changes back from the editor to the Tap Forms window.

    What do you think?

Viewing 15 results - 2,011 through 2,025 (of 2,952 total)