All worksheets in spreadsheet appear to be a reference to sheet 0 - phpexcel

I am writing a price import script which reads from an Excel spreadsheet.
The spreadsheet is generated using Office 365 Excel however I am using LibreOffice Calc on Ubuntu 18.04 to view it during development - no issues here.
I'm using phpoffice/phpspreadsheet at version 1.10.1:
"name": "phpoffice/phpspreadsheet",
"version": "1.10.1",
"source": {
"type": "git",
"url": "https://github.com/PHPOffice/PhpSpreadsheet.git",
"reference": "1648dc9ebef6ebe0c5a172e16cf66732918416e0"
},
I am trying to convert the data of each worksheet within the spreadsheet to an array.
There are 3 worksheets, each representing 'Zones' - Zone 1, Zone 2 and Zone 3.
I appear to be getting the same data for Zone 2 and Zone 3 as Zone 1 - the worksheet title is correctly returned however the data is not changing between worksheets.
/**
* #param Spreadsheet $spreadsheet
*
* #return array
*/
private function parseZones(Spreadsheet $spreadsheet): array
{
$zones = [];
foreach ([0, 1, 2] as $sheetIndex) {
$sheet = $spreadsheet->getSheet($sheetIndex);
// this is correctly reporting 'Zone 1', 'Zone 2' and 'Zone 3' - sheet title is accurate
$sheetName = $sheet->getTitle();
// sheet 0 is accurate
$sheetData = $sheet->toArray();
// on sheet index 1 and 2 - $sheetData is identical to that of sheet index 0
// the XLSX file in OpenOffice / Excel has distinctly different row data - 50% less rows in both cases
// feels like a memory cache issue / some mis-referencing?
}
// retrieving rows using this approach yields the same result:
foreach ($spreadsheet->getAllSheets() as $sheet) {
// this is correctly reporting 'Zone 1', 'Zone 2' and 'Zone 3' - sheet title is accurate
$sheetName = $sheet->getTitle();
// on sheet index 1 and 2 - $sheetData is identical to that of sheet index 0
$sheetData = $sheet->toArray();
}
return $zones;
}
Any ideas?
Thanks

I'm a numpty - completely failed to see / check the row filtering in the spreadsheet.
It's returning the correct data.
None issue, sorry!
I've since started to investigate how to read a worksheet whilst obeying the filters embedded in the spreadsheet, and it appears Worksheet::toArray() does not automatically take filters in to account - nor does iterating columns and rows manually, see:
https://phpspreadsheet.readthedocs.io/en/latest/topics/autofilters/
You must manually test a row's visibility settings, as per the docs.
Hope this helps!

Try just change current active sheet before reads.
$spreadsheet->setActiveSheetIndex($sheetIndex);
$sheet = $spreadsheet->getActiveSheet();
$dataArray = $sheet
->rangeToArray(
'A4:O07', // The worksheet range that we want to retrieve
NULL, // Value that should be returned for empty cells
TRUE, // Should formulas be calculated (the equivalent of getCalculatedValue() for each cell)
TRUE, // Should values be formatted (the equivalent of getFormattedValue() for each cell)
TRUE // Should the array be indexed by cell row and cell column
);
PhpSpreadsheet

Related

Filemaker Pro - Using Script to populate report layout

I have a problem where I have a list of fields from a table (not static, can be modified by user), and I need to generate a report using these user selected fields. The report can show all the rows, no need for aggregation or filtering.
I thought I could create a report layout then using a filemaker script to populate it but can't seem to find the right commands, can someone let me know how I could achieve this?
I'm using filemaker pro 18 advanced
Thanks in advance!
EDIT: Since you want a dynamic report, then I recommend you look up a technique called "Virtual List" for rendering the data.
Here's an example script that iterates over a found set of records and builds the virtual list data in a variable (it doesn't show how to render it though):
# Field names and delimiter
Set Variable [ $delim ; Value: Char(9) // tab character ]
# Set these dynamically with a script parameter
Set Variable [ $fields ; Value: List ( "Contacts::nameFirst" ; "Contacts::nameCompany" ; "Contacts::nameLast" ) ]
Set Variable [ $fieldCount ; Value: ValueCount ( $fields ) ]
Go to Layout [ “Contacts” (Contacts) ; Animation: None ]
Show All Records
Go to Record/Request/Page [ First ]
# Loop over all the records and append a row in the $data variable for each
Set Variable [ $data ; Value: "" ]
Loop
# Get the delimited field values
Set Variable [ $i ; Value: 0 ]
Set Variable [ $row ; Value: "" ]
Loop
Exit Loop If [ Let ( $i = $i + 1 ; $i > $fieldCount ) ]
Set Variable [ $value ; Value: GetField ( GetValue ( $fields ; $i ) ) ]
Insert Calculated Result [ Target: $row ; If ( $i > 1 ; $delim ) & $value ]
End Loop
enter code here
# Append the new row of data to the list variable
Insert Calculated Result [ Target: $data ; If ( Get ( RecordNumber ) > 1 ; ¶ ) & $row ]
Go to Record/Request/Page [ Next ; Exit after last: On ]
End Loop
# Save to a global variable to show in a virtual list layout
Set Variable [ $$DATA ; Value: $data ]
Exit Script [ Text Result: ]
please note this code is just one of many possible formats the virtual list can take. A lot of people, myself included, prefer to use JSON objects or arrays for each row of the list since it automatically handle field values with carriage returns. This is sort of the old-fashioned way. Kevin Frank at FileMaker Hacks has some good recent articles about virtual list techniques if you're interested.
PS, another great technique for rendering table data dynamically is to collect the data in a JSON array and render it in a webviewer with https://datatables.net/
I did something like this for the oncology department of UM om 1980 or so using 4th Dimension and a new plug in that used one line of code to create a web browser with all the functions that a doctor might want. The data was placed inside a variable as it was sent/returned and 4D could use a variable in the report to display the data.
FileMaker does not have this ability built in as 4D did so you will have to do it yourself.JSON is the most likely tool that I am familiar with. YouTube has many videos on JSON.
You have two classes of variables for your report: Column headers and column data to display. Fortunately Filemaker is quite good and very easy to design. Just make a typical report and replace the text/header or field names with a JSON variable or any. $ColumnName = JSON variable.
Create a JSON calculated field in the database. In that calculated field set the JSON variable and this can be used for all of the columns.
This is the essence of the idea with the final result to be determined by you. What you are asking for is not easy and would require serious work by a skilled JSON scripter.

firebase database set method appending to array instead of replacing the elements

I'm using firebase database to store a complex data structure, it looks like this:
../thousand_island/
genomes:[
0:{
"somthing": "some content"
},
... 49 more
],
version: 1223
The genomes array always have 50 elements. Every time I call set to replace all the data in /my_record_name then 50 new records will be inserted into the genomes array instead of replacing.
Code sample:
// set the ref into the instance
this.ref= firebase.database.ref('/genomes/thousand_island');
...
// record = { genomes:[...], version: *** }
if(version> record.version){
this.ref.set(record,function(){
console.log('updated thousand_island ', version, '->', record.version);
})
}
So how to make it replace it instead of appending to the array? I was thinking of deleting the data then insert again but it sounds tedious and it costs 2 requests.

phpexcel format value on import

I'm working on laravel project that takes in excel file to grab certain columns from it
$sheetData = PHPExcel_IOFactory::load($file)->getActiveSheet();
$hi_row = $sheetData->getHighestRow();
$hi_col = $sheetData->getHighestColumn();
$sheetData = $sheetData->toArray(null,false,true, true);
there are some files that have different formatting on cells where in my toArray() function if i do null, false, true, true) dates comes in fine but on some excel files currency value comes up with ? in it, if i set third true condition to false, than currency comes out fine but on some excel files data gets turn into random integer. so is there any way to set all cells to general on import? or if i choose to leave it at toArray(null, false,true, true) can i set the currency column to general or something so i won't get those ?s

Get cell types when reading and parsing excel files

I am trying to read and parse and excel and some unclear things come into play as usual for me.
Here is what i have:
while (true)
{
comVariantCell1 = cells.item(row, 1).value().variantType();
comVariantCell2 = cells.item(row, 2).value().variantType();
//if an empty cell is found, processing will stop and user will get an error message in order to solve the inconsistency.
if (comVariantCell1 != COMVariantType::VT_EMPTY && comVariantCell2 != COMVariantType::VT_EMPTY)
{
//both cells have values, check their types.
importedLine = conNull();
progress1.setText(strfmt("Importing row %1", row));
if (cells.item(row, 1).value().variantType() == COMVariantType::VT_BSTR)
{
importedLine += cells.item(row, 1).value().bStr();
}
else
{
importedLine += cells.item(row, 1).value().double();
}
importedLine += cells.item(row, 2).value().double();
importedLinesCollection += [importedLine]; //conIns(importedLinesCollection, row - 1, (importedLine));
row++;
}
else
{
info (strFmt("Empty cell found at line %1 - import will not continue and no records were saved.", row));
break;
}
}
Excel format:
Item number Transfer Qty
a100 50.5
a101 10
a102 25
This worked well to check if the cell type is string: COMVariantType::VT_BSTR
but what should i use to check for a real or integer value ?
I am pretty sure in this case, the quantity will be not contain real values but anyway, it could be useful in the future to make the difference between these two types.
I have to mention that, even if i have an int value and I use cells.item(row, 1).value().int() it won't work. I can't see why.
Why do i want to make the difference? Because if it's forbidden to have real values in the quantity column ( at least in my case ), i want to check that and give the user the opportunity to put a correct value in that place and maybe further investigate why that happened to be there.
Take a look on how it is done in \Classes\SysDataExcelCOM\readRow.
It is basically using switch to test the type. This is really boring!
Also take a look on ExcelIO, a class I made some years ago. It reads Excel and returns each row as a container. This is a more high-level approach.
As a last resort you could save the Excel as a tab separated file. Then use TextIO to read the content. This will be at least 10 times faster than using Excel!

How to create add and subtract buttons to track inventory

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

Resources