I use pentaho report designer. I have got businessdate parameter in my prpt file. This has the value of the date range which is used to filter the sql query. I am able to handle and modify it while exporting to html however I have a problem in exporting to excel.
The date range comes in the formats below:
- BETWEEN {d '2014-01-01'} AND {d '2014-01-31'}
- IN ({d '2014-01-14'},{d '2014-01-15'}, {d '2014-01-19'} ,{d '2014-01-20'},{d '2014-01-21'})
I like to find out max and min date and display it. However in this case with excel, I am happy with displaying them separated with commas like shown below.
- 2014-01-01, 2014-01-31
- 2014-01-14, 2014-01-15, 2014-01-19, 2014-01-20, 2014-01-21
If I use the basic formula show below, it works in in excel but it does not work when I apply it to excel - formula section of the businessDate element in pentaho report designer.
=TRIM(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(B4,"{d '",""), "BETWEEN", ""), "IN", ""),"'}",", "), " AND ", ""),")",""),"(",))
It does not have to be this way. I am happy with any method suggested to format this raw date range before printing to excel.
Thank you in advance.
After wasting a lot of time, I have found a way which works for all export types. As I said in my question, I was modifying date for html print by using "Structure Tab" --> Select "Master Report" --> "Attributes" Tab --> html -> "append-header" attribute.
<script type="text/javascript">
function init() {
var tableList = document.getElementsByTagName("table");
var businessDate = document.getElementById("businessDatePeriodSelected");
businessDate.innerHTML = businessDate.innerHTML+"testDateModified";
}
window.onload = init;
</script>
This piece of code does the job. However just for html. I needed to come up with something new. I was looking for a solution for excel and pdf exports as well.
HERE IS THE SOLUTION:
Click on "Data" tab next to the "Structure" tab on the right top side. You will see "Functions" in the tree. Right click and hit "Add functions". Select "Script" -->"Bean-Scripting Framework (BSF)". Select function created under Functions. Give it a name and add the code below to the "Expression" section. [This does not need a starting or ending tag]
/* businessdate is one of my parameters that I like to display on the report.
dataRow is automatically recognized by the interpreter which can be used for calling parameter values. It seems like came out of nowhere.*/
String value = dataRow.get("businessdate");
value= value.replaceAll("[^0-9\\-\\{\\}]", "");
value= value.replaceAll("[\\{]", ""); // replace all {
value= value.replaceAll("[\\}]", ","); // replace all }
value= value.substring(0, value.length()-1);
String[] dateArr = value.split(",");
return dateArr[0] +" - "+dateArr[dateArr.length-1];
The last thing you need to do is drag and drop your function somewhere suitable on your report. It will locate a textbox which will display the modified businessdate.
If you like to print a parameter on your pentaho report, this does the job for all exports (html, pdf and excel). You can also modify it before printing. This link pretty helpful as the syntax is slightly different at some points.
good luck.
Related
I have generated an Excel table using PHPSpreadsheet including the style and the autofilter:
The problem is when I sort the data by the second and third columns, the table formatting is gone. This is how it looks like compared if I use Table Style directly from Excel (using Home-> Format as Table):
Is there any way to keep the formatting when I sort the table generated from PHPSpreadsheet?
Relevant PHP Code:
for ($rowNumber = 0, $rowNumberMax = sizeof($rows); $rowNumber < $rowNumberMax; $rowNumber++) //rows (all data)
{
$columnNumber = 0; //1 = A
for ($i = 0, $j = sizeof($tableColumns); $i < $j; $i++) //loop through table header label
{
foreach ($rows[$rowNumber] as $rowKey => $rowValue) //loop through single row data
{
if($tableColumns[$i] == $rowKey)
{
$sheet->setCellValueByColumnAndRow($columnNumber + 1, ($rowNumber + 5), $rowValue);
$currentCell = Utilities::num2alpha($columnNumber) .''. ($rowNumber + 5);
$sheet->getStyle($currentCell)->getNumberFormat()->setFormatCode('#');
$sheet->getStyle($currentCell)->getAlignment()->setVertical(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_LEFT);
if(($rowNumber+5) % 2 == 0)
{
//even row
$sheet->getStyle($currentCell)->getFill()->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID)->getStartColor()->setARGB('ffd9e1f2');
}
else
{
//odd row
}
$columnNumber++;
break;
}
}
}
}
//set autofilter
$headerFirstCellPosition = 'A4';
$tableLastCellPosition = Utilities::num2alpha(sizeof($tableColumns) - 1) . '' . (sizeof($rows) + 4);
$sheet->setAutoFilter($headerFirstCellPosition . ':' . $tableLastCellPosition);
The problem is you were just applying formatting to the cells based on if the row was even or odd, but it wasn't actually replicating a table in Excel. You would find the same result in Excel if you just formatted every other row like you did with your PHP code, where the "table" format would get lost.
Somebody just recently implemented a first pass of the actual table feature in Excel: https://github.com/PHPOffice/PhpSpreadsheet/pull/2671
You need to be on PHPSpreadSheet version 1.23.0 in order to be able to use this.
Using that, you would have to modify your code but you can go to the Samples section in the code area and view how to implement it: https://github.com/PHPOffice/PhpSpreadsheet/tree/master/samples/Table
https://github.com/PHPOffice/PhpSpreadsheet/blob/master/samples/Table/01_Table.php
Here is the relevant code (I removed some of the lines and added additional comments from the 01_Table.php sample at the link provided).
Table styles can be found here: https://github.com/PHPOffice/PhpSpreadsheet/blob/master/src/PhpSpreadsheet/Worksheet/Table/TableStyle.php
// Create Table
$table = new Table('A1:D17', 'Sales_Data');
// Create Table Style
$tableStyle = new TableStyle();
// this line is the style type you want, you can verify this in Excel by clicking the "Format as Table" button and then hovering over the style you like to get the name
$tableStyle->setTheme(TableStyle::TABLE_STYLE_MEDIUM2);
// this gives you the alternate row color; I suggest to use either this or columnStripes as both together do not look good
$tableStyle->setShowRowStripes(true);
// similar to the alternate row color but does it for columns; I suggest to use either this or rowStripes as both together do not look good; I personally set to false and only used the rowStripes
$tableStyle->setShowColumnStripes(true);
// this will bold everything in the first column; I personally set to false
$tableStyle->setShowFirstColumn(true);
// this will bold everything in the last column; I personally set to false
$tableStyle->setShowLastColumn(true);
$table->setStyle($tableStyle);
Also make sure that you include the following to be able to use these:
use PhpOffice\PhpSpreadsheet\Worksheet\Table;
use PhpOffice\PhpSpreadsheet\Worksheet\Table\TableStyle;
Implementing that into your code will then allow you to sort using the auto filters and keep the formatting like you are expecting.
There are a few caveats such as:
Note that PreCalculateFormulas needs to be disabled when saving spreadsheets containing tables with formulae (totals or column formulae).
Also, as I am actually currently working on doing this, it doesn't look like you can apply an autofilter and have a table at the same time at this point.
That does appear to be on the todo list though, as the first link I provided the contributor has "Filter expressions similar to AutoFilter."
Otherwise, that should get you what you want and aside from being able to auto filter prior to creating the Excel file, it has worked well in my small testing.
Edit to add:
I think you can actually simplify your code a bit by using the functionality of PHPSpreadsheet to create a a spreadsheet from an array.
Documentation from PHPSpreadsheet can be found here: https://phpspreadsheet.readthedocs.io/en/latest/topics/accessing-cells/#setting-a-range-of-cells-from-an-array
You'll need to change it so that the array that is holding the info starts with your headers, so I believe that would look similar to this for your code:
$rows = [
['header1', 'header2', 'header3', 'header4']
];
Then you can populate the $rows array with your data from the rows either with a loop or just a single declaration depending on what you are putting in there, but basically using the below to populate the array.
$rows[] = [
$field1Data,
$field2Data,
$field3Data,
$field4Data
];
After you do that, you can then generate the spreadsheet using the following:
$sheet->getActiveSheet()
->fromArray(
$rows, // the data to set
NULL, // array values with this value will not be set
'A1', // top left coordinate of the worksheet range where we want to set these values (default is A1)
true // adds 0 to cell instead of blank if a 0 is the value
);
After doing the above, you can then add the code to create the table I posted and then save the file and you should be good.
Also, if you are in a situation where you still need to use the autofilter (for instance if you want to pre-filter the file on one or more columns which at this point you can't use a table when doing), you can make the autofilter call a bit easier.
// determine the the number of rows in the active sheet
$highestRow = $spreadsheet->getActiveSheet()->getHighestRow();
// get the highest column letter
$highestColumn = $spreadsheet->getActiveSheet()->getHighestColumn();
// set autofilter range
$spreadsheet->getActiveSheet()->setAutoFilter('A1:'.$highestColumn.$highestRow);
I realize the additional edit goes beyond the question, but figured I'd point it out since there are some built-in methods that you could use to reduce some of your code.
-Matt
Trying to get data from Excel and merge it into Word using MailMerge (just like how it is done in this video).
However, fields aren't getting updated after running this code. VBA isn't throwing any error so looks like code is fine. Can you please help?
Sub getdata()
Dim numRecord As Integer
Dim myName As String
myName = InputBox("Enter the field name and relax!")
Set dsMain = ActiveDocument.MailMerge.DataSource
If dsMain.FindRecord(FindText:=myName, Field:="Fields") = True Then
numRecord = dsMain.ActiveRecord
End If
End Sub
Note: Data in Excel looks like this:
Fields First Layer Second Layer
CC 5 3
So when someone enters CC in Input box I want first_layer and Second_layer fields in word to get updated to 5 and 3 respectiely.
If you're running the mailmerge from Word, you don't actually need any VBA for this - it can all be done with a SKIPIF field. For example the following field code does the same as the macro in the video is supposed to:
{SKIPIF{FILLIN "Name to merge" \o}<> {MERGEFIELD Name}}
or:
{SKIPIF{FILLIN "Name to merge" \o}<> «Name»}
Note: The field brace pairs (i.e. '{ }') for the above example are all created in the document itself, via Ctrl-F9 (Cmd-F9 on a Mac or, if you’re using a laptop, you might need to use Ctrl-Fn-F9); you can't simply type them or copy & paste them from this message. Nor is it practical to add them via any of the standard Word dialogues. Likewise, the chevrons (i.e. '« »') are part of the actual mergefields - which you can insert from the 'Insert Merge Field' dropdown (i.e. you can't type or copy & paste them from this message, either). The spaces represented in the field constructions are all required.
In my rdlc report have following columns
SlNo, Item, Uom, Qty, Rate, Amount
Here the Amount field is a formula (Rate*Qty)
The report is working fine, and when i export to excel also displaying the values are correctly.
But my problem is, after export to excel, when i change the Qty or Rate columns in excel file the Amount is not get changed automatically, because the formula is missing in the excel cell.
How can we include the formula in Amount column while export to excel from .rdlc?
I'm afraid that this required behaviour isn't really possible by just using the rdlc rendering.
In my search I stumbled upon this same link that QHarr posted: https://social.msdn.microsoft.com/Forums/en-US/3ddf11bf-e10f-4a3e-bd6a-d666eacb5ce4/report-viewer-export-ms-report-data-to-excel-with-formula?forum=vsreportcontrols
I haven't tried the project that they're suggesting but this might possibly be your best solution if it works. Unfortunately I do not have the time to test it myself, so if you test this please share your results.
I thought of the following workaround that seems to work most of the times, but isn't really that reliable because the formula sometimes gets displayed as full-text instead of being calculated. But I guess this could be solved by editing the excel file just after being exported, and changing the cell properties of this column containing the formula or just triggering the calculate.
Using the built-in-field Globals!RenderFormat.Name you can determine the render mode, this way you can display the result correctly when the report is being rendered to something different than Excel. When you export to Excel, you could change the value of the cell to the actual formula.
To form the formula it's self you'll need to figure this out on your own, but the RowNumber(Scope as String) function can be of use here to determine the row number of your cells.
Here is a possible example for the expression value of your amount column
=IIF(Globals!RenderFormat.Name LIKE "EXCEL*", "=E" & Cstr(RowNumber("DataSet1")+2) & "*F" & Cstr(RowNumber("DataSet1")+2) ,Fields!Rate.Value * Fields!Qty.Value )
Now considering that this formula sometimes gets displayed as full-text, and you'll probably have to edit the file post-rendering. If it's too complicated to determine which row/column the cell is on, you could also do this post-rendering. But I believe that the above expression should be easy enough to use to get your desired result without having to do much after rendering.
Update: The following code could be used to force the calculation of the formula (post rendering)
var fpath = #"C:\MyReport.xlsx";
using (var fs = File.Create(fpath))
{
var lr = new LocalReport();
//Initializing your reporter
lr.ReportEmbeddedResource = "MyReport.rdlc";
//Rendering to excel
var fbytes = lr.Render("Excel");
fs.Write(fbytes, 0, fbytes.Length);
}
var xlApp = new Microsoft.Office.Interop.Excel.Application() { Visible = false };
var wb = xlApp.Workbooks.Open(fpath);
var ws = wb.Worksheets[1];
var range = ws.UsedRange;
foreach (var cell in range.Cells)
{
var cellv = cell.Text as string;
if (!string.IsNullOrWhiteSpace(cellv) && cellv.StartsWith("="))
{
cell.Formula = cellv;
}
}
wb.Save();
wb.Close(0);
xlApp.Quit();
I have working DXL code to export a DOORS module to Excel, including sizing pictures and placing them over the desired cell. (Slightly modified version of GalacticSolutions script ). The default export so far as I can tell applies the parameter "Move but do not size with cell." I'd like to specify "Move and size with cell." This is easy enough to do with an Excel VB macro after the export, but I'd like to avoid that step. I'm hoping there's some Oleput() string that will do this, but can't figure it out.
I just worked through this today.
In the script, I added a new constant under the Excel VBA Properties section.
const string cExcelPropertyPlacement = "Placement"
Created a new little subroutine:
void excelShapeRangePlacement( OleAutoObj objExcelShapeRange, int OlePlacement ) {
oleResult( olePut( objExcelShapeRange, cExcelPropertyPlacement, OlePlacement ) )
}
Then called the new routine a the end of the "excelSizeShape" subroutine.
// values: 1-MoveandSize, 2-Move, 3-Freefloating
excelShapeRangePlacement( objExcelShapeRange, 1 )
This should set the value for the OLEs output into Excel..
I am trying to get account numbers by a query and displaying those account numbers as column header after exporting into excel.
The following code is working fine but displaying wrongly:
<cfset variables.setHeader = "">
<cfoutput query="myLoop">
<cfset variables.setHeader = variables.setHeader & ", C_#myLoop.acc#>
</cfoutput>
The above code is displaying column header (just one sample data I am showing here) but it is showing as 'C_000391'. Which is wrong! For display, 'C_' should not be with account number, it is the account number which should create the column header alone. However, as soon as I am taking off 'C_' from infront of #myLoop.acc# it is giving the following error:
The column name 000391 is invalid.
Please let me know what will be the best solution(s) to resolve this toughest problem on earth :)
Thanks
Seems you are trying a form a comma separated list , try below , no looping required.
< cfset variables.setHeader = ValueList(myLoop.acc) >