Copy Excel OneDrive table to an specific cell on another Excel OneDrive table (Power Automate) - onedrive

I need to copy data from one excel worksheet and paste (values only) on another worksheet using power automate + Office script
I started to creat a flow using the answer in the link bellow.
Power Automate: Copy Excel OneDrive table to the bottom of another Excel OneDrive table
The problem is I didnt understood the second script so I was not able to modify it to what I need ( that one paste on the end of the workbook)
SCRIPT on the link
For Run script I have
function main(workbook: ExcelScript.Workbook) {
const sheet = workbook.getWorksheets()[0];
let lastRow = sheet.getUsedRange(true).getLastCell().getRowIndex() + 1;
let rng = "A3:P" + lastRow
let tableTest = sheet.getRange(rng).getValues();
console.log(tableTest);
}
Then under Compose
#{outputs('Run_script')?['body']?['Logs'][0]}
Then Initialize the "RemoveString" variable
#{split(outputs('Compose'),' ')[0]}
Then Initialize the "NewString" variable
#{replace(outputs('Compose'),variables('RemoveString'),'')}
Then Run Script 2 and add "NewString" as the parameter.
function main(workbook: ExcelScript.Workbook, rangeTest: string) {
let table = workbook.getTable("BacklogTable");
let str = rangeTest;
let testerTest = JSON.parse(str);
table.addRows(null, testerTest);
}
The reason for RemoveString is to remove the Date & Time Stamp from the outputs

This requires a little different workflow.
Run Script
function main(workbook: ExcelScript.Workbook) {
const sheet = workbook.getWorksheets()[0];
let lastRow = sheet.getUsedRange(true).getLastCell().getRowIndex() + 1;
let rng = "A2:C" + lastRow
let tableTest = sheet.getRange(rng).getValues();
console.log(tableTest);
console.log(tableTest.length)
}
Compose
#{outputs('Run_script')?['body']?['Logs'][0]}
Compose 2
#{outputs('Run_script')?['body']?['Logs'][1]}
RemoveString
#{split(outputs('Compose'),' ')[0]}
NewString
#{replace(outputs('Compose'),variables('RemoveString'),'')}
RemoveString2
#{split(outputs('Compose_2'),' ')[0]}
NewString2
#{int(replace(outputs('Compose_2'),variables('RemoveString2'),''))}
Num
#{int(variables('NewString2'))}
Run Script 2
function main(workbook: ExcelScript.Workbook, rangeTest: string, length: number) {
let str = rangeTest;
const arr = JSON.parse(str);
let sheet = workbook.getWorksheet("Sheet2");
let rng = "A7:C" + (6 + length); //Change C to whichever column you want to end on
sheet.getRange(rng).setValues(arr);
sheet.getRange(rng).setNumberFormatLocal("0.00");
}

I may not be following this correctly, but you can also return values and pass them to another connector in Power Automate. Here is some documentation on how to return values with Office Scripts. Also, below is an example script with parameters and a number type return value. With returning values rather than console.logging them, you won't need to remove any output in your Flow steps. Let me know if you have any questions!
function main(
workbook: ExcelScript.Workbook,
issueId: string,
issueTitle: string): number {
// Get the "GitHub" worksheet.
let worksheet = workbook.getWorksheet("GitHub");
// Get the first table in this worksheet, which contains the table of GitHub issues.
let issueTable = worksheet.getTables()[0];
// Add the issue ID and issue title as a row.
issueTable.addRow(-1, [issueId, issueTitle]);
// Return the number of rows in the table, which represents how many issues are assigned to this user.
return issueTable.getRangeBetweenHeaderAndTotal().getRowCount();
}

Related

How to delete a worksheet if it exists and create a new worksheet with the same name with Office Scripts

I have a script that makes a copy of an existing worksheet and generates a table within this new worksheet called Lists.
Looking for a way to delete the new worksheet whenever the script runs again, but I keep getting the 'Worksheet activate: The requested resource doesn't exist' error with selectedSheet.activate();
let selectedSheet = workbook.getWorksheets()[0];
selectedSheet.getAutoFilter().remove();
// Duplicate worksheet.
let itemsName = "Lists";
let sheetItem = workbook.getWorksheet(itemsName);
// If `null` wasn't returned, then there's already a worksheet with the same name.
if (sheetItem) {
console.log(`Worksheet ${itemsName} already exists. Deleting...`);
// Delete the sheet.
sheetItem.delete();
} else {
let selectedSheet = workbook.getWorksheets()[0];
}
selectedSheet.activate();
let itemsSheet = selectedSheet.copy(ExcelScripWorksheetPositionType.before, selectedSheet);
itemsSheet.setName("Lists");
itemsSheet.activate();
I suggest to use the optional chaining operator (?.).
function main(workbook: ExcelScript.Workbook)
{
const itemsName = "Lists";
workbook.getWorksheet(itemsName)?.delete();
const selectedSheet = workbook.getFirstWorksheet();
const itemsSheet = selectedSheet.copy(ExcelScript.WorksheetPositionType.before, selectedSheet);
itemsSheet.setName(itemsName);
itemsSheet.activate();
}
Please refer to the below link.
https://learn.microsoft.com/en-us/office/dev/scripts/develop/best-practices#verify-an-object-is-present
https://github.com/sumurthy/officescripts-projects/tree/main/Top%205%20Tips

Workbook cell style in POI/NPOI doesn't work properly with multiple styles in workbook

I'm running into strange problem with .Net version of POI library for Excel Spreadsheets. I'm rewriting from text files to Excel 97-2003 documents and I'm like to add some formatting programmatically depend on some values gather at the begging of the program.
At the beginning, in the same method where I was creating a new cell from given value I was creating also a new Workbook CellStyle which was wrong, because I was running out of the styles very quickly (or I was just thought it was the cause of the problem).
Constructor of the class responsible for Excel Workbook:
public OldExcelWriter(TextWriter logger) : base(logger)
{
_workbook = new HSSFWorkbook();
_sheetData = _workbook.CreateSheet("sheet1");
_creationHelper = _workbook.GetCreationHelper();
}
Method that is calling all the chains of operations:
public void Write(string path, Data data)
{
FillSpreadSheetWithData(data, _sheetData);
SaveSpreadSheet(_workbook, path);
}
Long story short, in FillSpreadSheetWithData I have method for creating a row inside which I'm have a loop for each cell, so basically I'm iterating thru every column, passing IRow references to a row, column value, index and formatting information like this:
for (int j = 0; j < column.Count; j++)
{
CreateCell(row, column[j], j, data.Formatting[j]);
}
and while creating a new styles (for first shot I was trying to pass some date time values) I had situation like this in my rewrited Excel: screenshot of excel workbook
So formatting was passed correctly (also Horizontal Aligment etc.) but it get ugly after 15th row (always the same amount).
DateTime dataCell = DateTime.MaxValue;
var cell = row.CreateCell(columnIndex);
_cellStyle = _workbook.CreateCellStyle();
switch (format.Type)
{
case DataType.Date:
_cellStyle.DataFormat = _creationHelper.CreateDataFormat().GetFormat("m/dd/yyyy");
if (value.Replace("\n", "") != string.Empty)
{
dataCell = DateTime.ParseExact(value.Replace("\n", ""), "m/dd/yyyy",
System.Globalization.CultureInfo.InvariantCulture);
}
break;
}
switch (format.HorizontalAlignment)
{
case Enums.HorizontalAlignment.Left:
_cellStyle.Alignment = HorizontalAlignment.LEFT;
break;
case Enums.HorizontalAlignment.Center:
_cellStyle.Alignment = HorizontalAlignment.CENTER;
break;
}
if (dataCell != DateTime.MaxValue)
{
cell.CellStyle = _cellStyle;
cell.SetCellValue(dataCell);
dataCell = DateTime.MaxValue;
}
else
{
cell.CellStyle = _cellStyle;
cell.SetCellValue(value);
}
(It's not the cleanest code but I will don refactor after getting this work).
After running into this issue I thought that maybe I will create _cellStyle variable in the constructor and only change it's value depends on the case, because it's assigned to the new cell anyway and I see while debugging that object values are correct.
But after creating everything, it won't get any better. Styles was override by the last value of the style, and dates are spoiled also, but later: screnshoot of excel workbook after creating one instance of cell style
I'm running out of ideas, maybe I should create every combination of the cell styles (I'm using only few data formats and alignments) but before I will do that (because I'm running out of easy options right now) I wonder what you guys think that should be done here.
cell format is set to custom with date type
I am using this code to create my custom style and format. Its for XSSF Format of excel sheet. but it will work for HSSF format with some modification.
XSSFFont defaultFont = (XSSFFont)workbook.CreateFont();
defaultFont.FontHeightInPoints = (short)10;
defaultFont.FontName = "Arial";
defaultFont.Color = IndexedColors.Black.Index;
defaultFont.IsBold = false;
defaultFont.IsItalic = false;
XSSFCellStyle dateCellStyle = (XSSFCellStyle)workbook.CreateCellStyle();
XSSFDataFormat dateDataFormat = (XSSFDataFormat)workbook.CreateDataFormat();
dateCellStyle.SetDataFormat(dateDataFormat.GetFormat("m/d/yy h:mm")); //Replace format by m/dd/yyyy. try similar approach for phone number etc.
dateCellStyle.FillBackgroundColor = IndexedColors.LightYellow.Index;
//dateCellStyle.FillPattern = FillPattern.NoFill;
dateCellStyle.FillForegroundColor = IndexedColors.LightTurquoise.Index;
dateCellStyle.FillPattern = FillPattern.SolidForeground;
dateCellStyle.Alignment = HorizontalAlignment.Left;
dateCellStyle.VerticalAlignment = VerticalAlignment.Top;
dateCellStyle.BorderBottom = BorderStyle.Thin;
dateCellStyle.BorderTop = BorderStyle.Thin;
dateCellStyle.BorderLeft = BorderStyle.Thin;
dateCellStyle.BorderRight = BorderStyle.Thin;
dateCellStyle.SetFont(defaultFont);
//Apply your style to column
_sheetData.SetDefaultColumnStyle(columnIndex, dateCellStyle);
// Or you can also apply style cell wise like
var row = _sheetData.CreateRow(0);
for (int cellIndex = 0;cellIndex < TotalHeaderCount;cellIndex++)
{
row.Cells[cellIndex].CellStyle = dateCellStyle;
}

Need a more efficient solution than looping

I am building a spreadsheet that tracks work in progress as it moves through steps of a manufacturing process.
Each step of the process has a column with the total parts moved to each stage. To the left of this column is a column for number of parts moved to the stage (parts move through a few at a time).
My scrpit then takes the values in the "add" column, adds them to the "total" column, then reset the "add" column to "".
Here's the code:
function addColumns() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
// ss is now the spreadsheet the script is associated with
var sheet = ss.getSheets()[0]; // sheets are counted starting from 0
// sheet is the first worksheet in the spreadsheet
for (var i=4; i<500; i++ ) {
if(sheet.getRange(i,1).getValue()>0){ //Only run if order number not empty
//Breakout Column
var add = sheet.getRange(i,6);
var total = sheet.getRange(i,7);
total.setValue(total.getValue() + add.getValue());
add.setValue("");
//CNC Column
var add = sheet.getRange(i,8);
var total = sheet.getRange(i,9);
total.setValue(total.getValue() + add.getValue());
add.setValue("");
//CutSand Column
var add = sheet.getRange(i,10);
var total = sheet.getRange(i,11);
total.setValue(total.getValue() + add.getValue());
add.setValue("");
//Lasered Column
var add = sheet.getRange(i,12);
var total = sheet.getRange(i,13);
total.setValue(total.getValue() + add.getValue());
add.setValue("");
//To Finishing Column
var add = sheet.getRange(i,14);
var total = sheet.getRange(i,15);
total.setValue(total.getValue() + add.getValue());
add.setValue("");
// Defective Column
var add = sheet.getRange(i,17);
var total = sheet.getRange(i,18);
total.setValue(total.getValue() + add.getValue());
add.setValue("");
//Etsy Column
var add = sheet.getRange(i,20);
var total = sheet.getRange(i,21);
total.setValue(total.getValue() + add.getValue());
add.setValue("");
}
if(sheet.getRange(i,4).getValue()<1){i=500} //Once you find a blank order exit the loop
}
}
My code as written does accomplish this; it does exactly what I need. The problem is that since the code is accessing the spreadsheet on each loop it takes almost a full second per cell to run, and with 7 steps per order it can take minutes at a time to run through with lots of orders...
This is a pretty simple mathematical task, so there has to be a more efficient way of doing it, I just haven't been able to find the right keywords to describe what I need to do.
I am quite happy to learn whatever needs to be done, just need to know what direction to head.
Thanks in advance!
I would suggest to do something like this: (not tested)
function addColumns() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0]; // Refers to the first worksheet in the spreadsheet
var data = sheet.getDataRange().getValues(); // Acquires all values of the sheet
for (var i = 3; i < data.length; i++) { // Loop over every row
if (data[i][0].length > 0) { // Check if first column has a value
// Breakout
sheet.getRange(i+1,7).setValue(parseFloat(data[i][6]) + parseFloat(data[i][5]));
sheet.getRange(i+1,6).clear();
// Repeat code above for other columns
}
}
}
This code acquires all the data from the sheet instead of looping over a fixed amount of 500 rows. Assuming that your data starts at row 4, I've implemented this in the code above as well.
Variable data acquires all the data at one moment instead of trying to fetch values of every range (cell) all the time. I expect that this will save your script quite some time.
Because we acquire the data at once, the script sees the value as a string. Before we calculate the new value of the total column, we parse the value as a float (a number with decimals).
The code above is not tested as I don't have a sheet ready in the same format as you do but I think the logic is clear and if it doesn't work I suppose you should be able to adjust it to work for your sheet.

Problems with insert a Timestamp

I have to insert a link with a sheet with the basic's of my original sheet.
=> At the original sheet there is a importrange which insert the data. In the next tab a query take the data - now there should be appear a timestamp when the data in col B (in the Sheet at the link) is updated, but only at the first time the col change from an empty col to a filled col.
I searching at the internet, but I didn't find a helpful answer. On the one hand the most of the Scripts I found didn't work in general or doing a little bit. As a example:
This script worked:
function onEdit(event)
{
var timezone = "GMT-5";
var timestamp_format = "MM-dd-yyyy";
var updatedColName = "Bid Responses";
var sheet = event.source.getSheetByName('Overview - Working (Hidden)');
var actRng = event.source.getActiveRange();
var editColumn = actRng.getColum();
var index = actRng.getRowIndex();
var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues();
var dateCol = headers[0].indexOf(timeStampCoName);
var updateCol = headers[0].indexOf(updateColName); updateCol = updateCol+1;
if (dateCol > -1 && index > 1 && editColumn == updateCol) {
var cell = sheet.getRange(index, dateCol + 1);
var date = Utilities.formatDate(new Date(), timezone, timestamp_format);
cell.setValue(date);
}
}
But the timestamp was not inserted at the row where the col data change, it appear in a completely different row.
Can someone help me to write a Script that do exactly what I want?
Unru,
an onEdit trigger will always require a manual edit to the spreadsheet. In other words: the script runs when a user changes a value in a spreadsheet.A recalculation of a formula does NOT fire an onEdit script.
More info: here

Is there a way to get 'named' cells using EPPlus?

I have an Excel file that I am populating programmatically with EPPlus.
I have tried the following:
// provides access to named ranges, does not appear to work with single cells
worksheet.Names["namedCell1"].Value = "abc123";
// provides access to cells by address
worksheet.Cells["namedCell1"].Value = "abc123";
The following does work - so I know I am at least close.
worksheet.Cells["A1"].Value = "abc123";
Actually, its a bit misleading. The Named Ranges are stored at the workBOOK level and not the workSHEET level. So if you do something like this:
[TestMethod]
public void Get_Named_Range_Test()
{
//http://stackoverflow.com/questions/30494913/is-there-a-way-to-get-named-cells-using-epplus
var existingFile = new FileInfo(#"c:\temp\NamedRange.xlsx");
using (var pck = new ExcelPackage(existingFile))
{
var wb = pck.Workbook; //Not workSHEET
var namedCell1 = wb.Names["namedCell1"];
Console.WriteLine("{{\"before\": {0}}}", namedCell1.Value);
namedCell1.Value = "abc123";
Console.WriteLine("{{\"after\": {0}}}", namedCell1.Value);
}
}
You get this in the output (using an excel file with dummy data in it):
{"before": Range1 B2}
{"after": abc123}