How to create add and subtract buttons to track inventory - button

I've been searching for similar solutions out there but am coming up short so far. Here is what I want to accomplish:
I need to come up with a basic solution to sync inventory quantities at the end of each day. We take physical counts of inventory sold throughout the day but need something to log these changes and share between users. I would like to utilize two buttons (click one to subtract amount of items sold at the end of the day and click one button to add newly received inventory).
This is how my sheet is set up:
Col A: Product Tag
Col B: Product sku
Col C: Amount Sold Today
Col D: Total Inventory Quantity
Col E: Add New Inventory
Column D will be pre-populated with initial inventory counts. At the end of each day, I would like to go down my product list and fill in the amount of each item sold that day in Column C. Once Column C is fully populated, I would like to click the "subtract" button and have Column C subtracted from Column D.
On the other side, once we receive new stock of an item I would like to enter these counts into Column E. Once this column is fully populated, I would like to click the "Add" button and have Column E added to Column D. Ideally once the add or subtract function has been completed, columns C or E will be cleared and ready for the next days entry.
I already have designed my buttons, I just need help coming up with the scripts to accomplish this.

You can use Google Apps Script for this.
If you are unfamiliar, in your particular spreadsheet, go to Tools → Script Editor and then select the Blank Project option.
Then you can write functions like this to achieve what you want!
function subtractSold() {
var sheet = SpreadsheetApp.getActiveSheet();
var c1 = sheet.getRange("C2");
var c2 = sheet.getRange("D2");
while (!c1.isBlank() && !c2.isBlank()){
c2.setValue(c2.getValue() - c1.getValue());
c1.clear();
c1 = c1.offset(1, 0);
c2 = c2.offset(1, 0);
}
}
Basically what the function does is:
Get a reference to the active spreadsheet
Get references to the cells C2 and D2, for the first row of data.
Use a while loop to repeated go through the rows. Terminate when either cell is empty.
In the loop, we get the appropriate values, subtract and set the value back into the cell. Then we clear the cell in column C. We then move both cell references down by one row (the offset method returns a reference to the original cell, but offset by row, column).
Then assign the script to the button image by entering the name of the function (subtractSold in this case) in the "Assign script" option for the button.
I have made an example sheet here (go to File → Make a Copy to try the scripts and see the code): https://docs.google.com/spreadsheets/d/1qIJdTvG0d7ttWAUEov23HY5aLhq5wgv9Tdzk531yhfU/edit?usp=sharing
A bit faster
If you try the sheet above you can see it processes one row at a time, which might get pretty slow when you have a lot of rows. It is probably faster to process the entire column in bulk, but it may be a bit more complicated to understand:
function subtractSoldBulk() {
var sheet = SpreadsheetApp.getActiveSheet();
var maxRows = sheet.getMaxRows();
var soldRange = sheet.getRange(2, 3, maxRows); // row, column, number of rows
var totalRange = sheet.getRange(2, 4, maxRows);
var soldValues = soldRange.getValues();
var totalValues = totalRange.getValues();
for (var row in soldValues) {
var soldCellData = soldValues[row][0];
var totalCellData = totalValues[row][0];
if (soldCellData != "" && totalCellData != "") {
totalValues[row][0] = totalCellData - soldCellData;
soldValues[row][0] = "";
}
}
soldRange.setValues(soldValues);
totalRange.setValues(totalValues);
}
The difference here is that instead of getting one cell, we get one range of cells. The getValues() method then gives us a 2D array of the data in that range. We do the calculations on the two arrays, update the data in the arrays, and then set the values of the ranges based on the array data.
You can find documentation for the methods used above from Google's documentation: https://developers.google.com/apps-script/reference/spreadsheet/sheet

Related

Google Sheets: Click button to unhighlight row, highlight the next row, and update cell with date and time button was clicked

I'm trying to create a rotation scheduler. My goal is to have a table of names, phone numbers and a button.
When the button ('Assign') is pressed, it highlights a row, and updates a cell outside the table with the date & time it was clicked. When it is pressed again, it un-highlights that row and highlights the next row, and updates the date/time cell.
When it reaches the bottom, it should move back to the top. It should save where it left off between closing and opening the document and it needs to sync even when several users have it open... I don't think Excel can do that, but Google Sheets should be able to, which is why I'm leaning towards Google Sheets.
I completely understand if that's too much to expect someone to sort out on a forum, but I would very much appreciate even a point in the right direction as to figuring it out myself!!
When the button ('Assign') is pressed, it highlights a row, and
updates a cell outside the table with the date & time it was clicked.
You can add or create an image that acts as a button and associate a script to be triggered when it is clicked.
When it reaches the bottom, it should move back to the top.
One way of doing it is to keep track of the highlighted row and use modular arithmetic to wrap back to the start of the table.
It should save where it left off between closing and opening the
document
One option is to use the Properties Service to store the highlighted row.
I tried to implement a sample using these ideas (make a copy before you run): https://docs.google.com/spreadsheets/d/1bHN5cf2HaLTW3EbAxfXUusQnDL3dNFY313J8cdTGp4o/
The associated script:
var NUMBER_OF_ASSIGNEES = 5;
var LAST_ASSIGNMENT_DATETIME_CELL = 'G3';
var TABLE_OFFSET = 2;
function assignNewPerson(){
var sheet = SpreadsheetApp.getActive().getActiveSheet();
setAssignTime(sheet);
assignNext(sheet);
}
function setAssignTime(sheet) {
var d = new Date();
sheet.getRange(LAST_ASSIGNMENT_DATETIME_CELL).setValue(d.toLocaleTimeString());
}
function assignNext(sheet) {
// Use the Properties Service to store the current assignee index.
var documentProperties = PropertiesService.getDocumentProperties();
var currentAssigneeIndex = parseInt(documentProperties.getProperty('CURRENT_ASSIGNEE_INDEX'));
if (isNaN(currentAssigneeIndex)) {
currentAssigneeIndex = 0;
}
// Remove highlight from assignee cells.
sheet.getRange(TABLE_OFFSET + currentAssigneeIndex, 1, 1, 2).setBackground("white");
// Increment the assignee index. If the last row is reached, go back to the start of the table.
currentAssigneeIndex = (currentAssigneeIndex + 1) % NUMBER_OF_ASSIGNEES;
documentProperties.setProperty('CURRENT_ASSIGNEE_INDEX', currentAssigneeIndex);
// Highlight assignee cells.
sheet.getRange(TABLE_OFFSET + currentAssigneeIndex, 1, 1, 2).setBackground("yellow");
}

javascript for Array how many and min

I am trying to create a form in Acrobat. I want it to do some calculations. I got almost all of them done aside from 2.
I have an array of cells DF1 to DF78 so I need a calculation script that will give me the minimum value in that array not counting the blank ones.
In the same array of cells DF1 to DF78 I need a calculation script to find how many fields in that array have value and bring me up the number.
I already tried using the min option on the acrobat DC and selecting the fields. Ii want to look at DF1 to DF78. However, it always shows 0 because it's counting the empty fields as well.
I tried looking online, but all the scripts that they show are very confusing. I can't find where to put the array in there.
I wish I had a script to put it in here... sorry.
I have fields DF1 to DF78 so a total of 78 fields, and I need to find the minimum value in that array not including the fields that are blank.
Another script for the same fields DF1 to DF78 needs to count how many of the fields actually have data ex: DF1, DF2, DF3 had data on it and the rest are empty so it should display the number 3 because 3 of the 78 fields have data in them.
I hope somebody can help me with this.
This should work... Add it to the calculate action of a new hidden field you want the numbers to show up. Fix the names on the last two lines first.
valueArray = [];
for (var i = 1; i <= 78 ; i++) {
//Get the fieldvalue by assembling the name with the prefix and the number increment
var fieldVal = this.getField("DF"+i).value;
//Acrobat field values are never null. The value of a blank field is an empty string
if (fieldVal != "") {
//Add non-empty field values to an Array.
valueArray.push(fieldValue);
}
}
// Get the minimum value in the array.
var minValue = Math.min.apply(null, valueArray);
// Get the number of non-blank fields.
var nonBlankFields = valueArray.length;
this.getField("RESULT FOR YOUR 1st QUESTION FIELD NAME HERE").value = minValue;
this.getField("RESULT FOR YOUR 2nd QUESTION FIELD NAME HERE").value = nonBlankFields;

Combine data from two datasources into a single table

I have two datasets:
Dataset 1 - Records the details of a store visit. Merchandiser name, location, date & a relation to SKU (Dataset 2)
Dataset 2 - This is the SKU data, where the stock levels for each sku are input as a new record, each associated to a visit from Dataset 1.
I have two issues:
I want to combine this data into a single table. I want to show each SKU record, with additional columns for the visit information (such as the location & date). How do I do this.
How do I combine this data for use elsewhere, such as google data studio. Essentially I want to be able to see an SKU's stock-level's history, or the date it was last updated.
You need to create Calculated Data Source. You can refer this sample.
On a high Level
Add data source in Appmaker.
Select Calculated, provide Name and Create the data source.
Once your Calculated Model is in place. Add fields as per need basis. e.g. If you want to store Sum of two fields, create one Integer field in Calculated Model. Here's how your calculated data model will look like.
Now go to Second Tab which is "Datasources". Click on the Data Model name there. You should see an option to write server side script.
Here you should write your logic for combining your data sources. I can provide you one sample to achieve this.
//server script
var calculatedModelRecords = [];
var recordsByStatus = {};
var allRecord = app.models.Request.newQuery().run(); //your existing data source.
for (var i = 0; i < allRecord.length; i++) {
var record = allRecord[i];
var draftRecord = app.models.TAT.newRecord(); //new data source
draftRecord.CreatedOn = record.CreatedOn;
draftRecord.DocumentName = record.DocumentName;
draftRecord.DueDate = record.DueDate;
draftRecord.DaysPerStage = record.DaysPerStage;
draftRecord.Status = record.Status;
calculatedModelRecords.push(draftRecord);
}
return calculatedModelRecords;

Crossfilter total by group

Im trying to show the total number of people in each geography when they hover over using crossfilter, but my current code is only showing the total of all geographies. So what is the equivalent in crossfilter to the sql query: SELECT COUNT(*) GROUP BY dma
This is my code so far
//geography that is being hovered over, getting dma name and removing everything that is after the comma
sel_geog = layer.feature.properties.dma_1;
sel_geog = sel_geog.split(",")[0];
console.log(sel_geog);
//crossfilter to get total number of people of each geography
var dmaDim = voter_data.dimension(function(d) {return d.dma == sel_geog}),
dma_grp = dmaDim.groupAll().reduceCount().value();
console.log(dma_grp);
Crossfilter isn't meant to be used in a way where you are building new dimensions and groups for each user interaction. It's meant to build dimensions and groups before interactions take place and then update them quickly when filtering based on user interactions.
It's not really clear from this question what your data looks like or what you are trying to do, but you probably want to create dimensions and group for your dma property and then build your map based on that:
var voter_data = crossfilter(my_data);
var dmaDim = voter_data.dimension(function(d) { return d.dma; });
var dmaGroup = dmaDim.group();
At this point dmaGroup.all() will be an array of objects that looks like { key: 'dmaKey', value: 10 } where 10 is the count of all records where d.dma === 'dmaKey'. There are lots of ways you can aggregate differently with Crossfilter, but that may get you started.

Flex - sorting a datagrid column by the row's label

I'm creating a table that displays information from a MySQL database, I'm using foreignkeys all over the place to cross-reference data.
Basically I have a datagrid with a column named 'system.' The system is an int that represents the id of an object in another table. I've used lableFunction to cross-reference the two and rename the column. But now sorting doesn't work, I understand that you have to create a custom sorting function. I have tried cross-referencing the two tables again, but that takes ~30sec to sort 1200 rows. Now I'm just clueless as to what I should try next.
Is there any way to access the columns field label inside the sort function?
public function order(a:Object,b:Object):int
{
var v1:String = a.sys;
var v2:String = b.sys;
if ( v1 < v2 ){
trace(-1);
return -1;
}else if ( v1 > v2 ){
trace(1);
return 1;
}else {
trace(0);
return 0;
}
}
One way to handle this is to go through the objects you received and add the label as a property on each of them based on the cross-referenced id. Then you can specify your label property to display in your data grid column instead of using a label function. That way you would get sorting as you'd expect rather than having to create your own sort function.
The way that DataGrids, and other list based classes work is by using itemRenderers. Renderers are only created for the data that is shown on screen. In most cases there is a lot more data in your dataProvider than what is seen on screen.
Trying to sort your data based on something displayed by the dataGrid will most likely not give you the results you want.
But, there is no reason you can't call the same label function on your data objects in the sortFunction.
One way is to use the itemToLabel function of the dataGrid:
var v1:String = dataGrid.itemToLabel(a);
var v2:String = dataGrid.itemToLabel(b);
A second way is to just call the labelFunction explicitly:
var v1:String = labelFunction(a);
var v2:String = = labelFunction(b);
In my experience I have found sorting to be extremely quick, however you're recordset is slightly larger than what I usually load in memory at a single time.

Resources