phpexcel PHPExcel_Shared_Date::isDateTime not working - phpexcel

the file is xlsx and the column format is date MM-DD-YYYY
I have tried several different ways to determine if the value is a date.
PHPExcel_Shared_Date::isDateTime just is not working and do not know why. The data is being save in database and is showing the numbers as such:
41137
41618
42206
42076
41137
42206
41137
41988
my code:
$inputFileType = PHPExcel_IOFactory::identify($fullFilePath);
$objReader = PHPExcel_IOFactory::createReader($inputFileType);
$objReader->setReadDataOnly(false);
$objPHPExcel = $objReader->load($fullFilePath);
$objPHPExcel->setActiveSheetIndex(0);
$worksheetIndex = 1;
$worksheetName = '';
$actualRows = 0;
foreach($objPHPExcel->getWorksheetIterator() as $worksheet)
{
$lineNumber = 1;
$worksheetName = $worksheet->getTitle();
$columnSum = array();
foreach($worksheet->getRowIterator() as $row)
{
$cellIterator = $row->getCellIterator();
$cellIterator->setIterateOnlyExistingCells(true); // Loop all cells, even if it is not set = true else set to false
$columnNumber = 1;
foreach($cellIterator as $cell)
{
$dataValue = $cell->getCalculatedValue();
//$dataValue = $cell->getFormattedValue();
if(!empty($dataValue))
{
if(PHPExcel_Shared_Date::isDateTime($cell))
{
$dataValue = date('Y-m-d H', PHPExcel_Shared_Date::ExcelToPHP($dataValue));
}
else
{
// do something
}
}
}
}
}

Analysing the file, there's a few problems.... it doesn't validate cleanly under the Microsoft's Open XML SDK 2.0 productivity tool for MS Office.
Initial problem (which should trigger a loader error in PHPExcel) is the SheetView Zoom Scale, which should be a minimum value of 1. This problem can by "bypassed" by editing Classes/PHPExcel/Worksheet/SheetView.php and modifying the setZoomScaleNormal() method to avoid throwing the exception if the supplied argument value is out of range.
public function setZoomScaleNormal($pValue = 100)
{
if (($pValue >= 1) || is_null($pValue)) {
$this->zoomScaleNormal = $pValue;
// } else {
// throw new PHPExcel_Exception("Scale must be greater than or equal to 1.");
}
return $this;
}
The second problem is that custom number formats are defined with ids in the range 100-118, but all format ids below 164 are documented as reserved for Microsoft use. Excel itself is obviously more forgiving about breaking its documented rules.
You can resolved this by hacking the Classes/PHPExcel/Reader/Excel2007.php file and modifying the load() method, around line 512, by commenting out the check that tells PHPExcel to use the built-in number formats:
// if ((int)$xf["numFmtId"] < 164) {
// $numFmt = PHPExcel_Style_NumberFormat::builtInFormatCode((int)$xf["numFmtId"]);
// }
or
if ((int)$xf["numFmtId"] < 164 &&
PHPExcel_Style_NumberFormat::builtInFormatCodeIndex((int)$xf["numFmtId"]) !== false) {
$numFmt = PHPExcel_Style_NumberFormat::builtInFormatCode((int)$xf["numFmtId"]);
}
There are also issues with font size definitions, but these won't prevent the file from loading

Related

Writing to General Journal, Financial Dimensions not working

I'm writing to the general journal in x++. I am able to post there currently but the dimensions on the line item are not showing up. The code runs just will have 701100- - - - - and the rest not populated on the line item. i'm not sure why... I've tried several different things... such as below.
ledgerDimensions = ["701100","701100", "MIDWHS", "ACCT", "000001", "AIR", "019-000100"];
journalTrans.parmLedgerDimension(AxdDimensionUtil::getLedgerAccountId(ledgerDimensions));
offsetDimensions = ["701100","701100", "MIDWHS", "ACCT", "000001", "AIR", "019-000100"];
journalTrans.parmOffsetLedgerDimension(AxdDimensionUtil::getLedgerAccountId(offsetDimensions));
journaltrans.save()
and also have tried
// dimensionAttribute = DimensionAttribute::findByName("Location");
// dimensionAttributeValue = //DimensionAttributeValue::findByDimensionAttributeAndValue(dimensionAttribute, "MINOT");
// dimStorage = DimensionAttributeValueSetStorage::find(0);
// dimStorage.addItem(dimensionAttributeValue);
// journalTrans.parmOffsetDefaultDimension(dimStorage.save());
//journalTrans.save()
both are just adding the account number and nothing else.. don't know why!
I used the service for our import interface for general journal.
Below my code, try it this way, it's easy to create the dimensions with this service.
I copied the code from our system and commented it, if you need any further explaination, ask me.
Declarations:
LedgerGeneralJournal ledgerGeneralJournal;
LedgerGeneralJournalService ledgerGeneralJournalService;
AfStronglyTypedDataContainerList journalHeaderCollection;
LedgerGeneralJournal_LedgerJournalTable journalHeader;
AfStronglyTypedDataContainerList journalLineCollection;
AifEntityKeyList journalHeaderCollectionKeyList;
int journalLineCounter; // to create more than one line
int entityKeyCount;
int journalHeaderCounter; // to create more than one journal header if needed
Add parm methods for your counter variables if needed:
public int parmJournalLineCounter(int _journalLineCounter = journalLineCounter)
{
journalLineCounter = _journalLineCounter;
return journalLineCounter;
}
public int parmJournalHeaderCounter(int _journalHeaderCounter = journalHeaderCounter)
{
journalHeaderCounter = _journalHeaderCounter;
return journalHeaderCounter;
}
initializations:
Header
ledgerGeneralJournalService = LedgerGeneralJournalService::construct();
ledgerGeneralJournal = new LedgerGeneralJournal();
journalHeaderCollection = ledgerGeneralJournal.createLedgerJournalTable();
this.parmJournalLineCounter(0); // note to add a parm method for your counter variables
this.parmJournalHeaderCounter(_newInstance ? 1 : this.parmJournalHeaderCounter() + 1); // i added a parameter if more headers are needed
journalHeader = journalHeaderCollection.insertNew(this.parmJournalHeaderCounter());
journalHeader.parmJournalName(contract.parmJournalNameId()); // init your own journal ID
Here my method to create the lines with dimensions.
Note that i only use CostCenter here, if you need more, add them as done with CostCenter (i commented the lines)
protected void writeLine(
str _oldCompany,
TransDate _transDate,
Voucher _voucher,
str _mainAccountNum,
AmountMST _amount,
LedgerJournalTransTxt _transTxt,
CurrencyCode _currencyCode,
str _offsetAccountNum,
str costCenter
)
{
LedgerGeneralJournal_LedgerJournalTrans journalLine;
AifMultiTypeAccount journalLineLedgerDimensionMain;
AifDimensionAttributeValue journalLineDim1Main;
AfStronglyTypedDataContainerList journalLineDimensionCollectionMain;
AifMultiTypeAccount journalLineLedgerDimensionOffset;
str lineMainAccount;
str lineFullAccount;
str lineMainDimensionName = 'CostCenter';
str lineMainDimensionValue;
str lineOffsetAccount;
str lineOffsetFullAccount;
this.parmJournalLineCounter(this.parmJournalLineCounter()+1);
journalLine = this.parmJournalLineCollection().insertNew(this.parmJournalLineCounter());
journalLine.parmLineNum(this.parmJournalLineCounter());
journalLine.parmCompany(CompanyInfo::findByPTROldCompany(_oldCompany).company());
journalLine.parmOffsetCompany(journalLine.parmCompany());
journalLine.parmTransDate(_transDate);
journalLine.parmVoucher(_voucher);
journalLine.parmAccountType(LedgerJournalACType::Ledger);
lineMainAccount = _mainAccountNum;
journalLine.parmAmountCurCredit(_amount > 0 ? 0 : _amount);
journalLine.parmAmountCurDebit(_amount > 0 ? _amount : 0);
journalLine.parmTxt(_transTxt);
journalLine.parmCurrencyCode(_currencyCode);
journalLine.parmOffsetAccountType(LedgerJournalACType::Ledger);
lineOffsetAccount = _offsetAccountNum;
// Create Main Account Dimensions
journalLineLedgerDimensionMain = journalLine.createLedgerDimension();
journalLineLedgerDimensionMain.parmAccount(lineMainAccount);
lineFullAccount = strFmt("%1-%2-", lineMainAccount, costCenter ? lineMainDimensionValue : ''); // if you need more dimensions, add them here first
journalLineLedgerDimensionMain.parmDisplayValue(lineFullAccount);
// and then add the values here like costcenter:
if (costCenter)
{
lineMainDimensionValue = costCenter;
journalLineDimensionCollectionMain = journalLineLedgerDimensionMain.createValues();
journalLineDim1Main = new AifDimensionAttributeValue();
journalLineDim1Main.parmName(lineMainDimensionName);
journalLineDim1Main.parmValue(lineMainDimensionValue);
journalLineDimensionCollectionMain.add(journalLineDim1Main);
journalLineLedgerDimensionMain.parmValues(journalLineDimensionCollectionMain);
}
journalLine.parmLedgerDimension(journalLineLedgerDimensionMain);
// Create Offset Account Dimensions
// same procedure with offset dimensions if needed
if (_offsetAccountNum)
{
journalLineLedgerDimensionOffset = journalLine.createOffsetLedgerDimension();
journalLineLedgerDimensionOffset.parmAccount(lineOffsetAccount);
lineOffsetFullAccount = strFmt("%1--", lineOffsetAccount);
journalLineLedgerDimensionOffset.parmDisplayValue(lineOffsetFullAccount);
journalLine.parmOffsetLedgerDimension(journalLineLedgerDimensionOffset);
}
}
// Create Lines
journalLineCollection = journalHeader.createLedgerJournalTrans();
Finally to write the journal:
public void finalizeLedgerJournal()
{
int keyCount;
List journalIdList;
journalHeader.parmLedgerJournalTrans(journalLineCollection);
ledgerGeneralJournal.parmLedgerJournalTable(journalHeaderCollection);
journalHeaderCollectionKeyList = LedgerGeneralJournalService.create(ledgerGeneralJournal);
// if you need the journalId for further processing:
this.parmEntityKeyCount(journalHeaderCollectionKeyList.getEntityKeyCount());
if (entityKeyCount > 0)
{
for (keyCount = 1;keyCount <= entityKeyCount;keyCount++)
{
if (!contract.parmJournalIdList())
{
contract.parmJournalIdList(new List(Types::String));
}
journalIdList = contract.parmJournalIdList();
journalIdList.addEnd(LedgerJournalTable::findByRecId(journalHeaderCollectionKeyList.getEntityKey(keyCount).parmRecId()).JournalNum);
contract.parmJournalId(LedgerJournalTable::findByRecId(journalHeaderCollectionKeyList.getEntityKey(keyCount).parmRecId()).JournalNum);
}
}
}

PHPexcel - getOldCalculatedValue and rangeToArray

Searched for quite a while now, but I'm stuck at the following problem.
I am using PHPexcel 1.8.0
The spreadsheet is read using the following code:
$rowData = $sheet->rangeToArray('A' . $row . ':' . $highestColumn . $row, NULL, TRUE, TRUE);
So far ok and it works well.
But some spreadsheets contain external referenced data.
And for that I want to use "getOldCalculatedValue".
How do I combine "getOldCalculatedValue" with "rangeToArray" ?
Or is "rangeToArray" inappropriate for this ?
Thanks for any help or hints !
Simple answer, you can't combine the two
rangeToArray() is a simple method for a simple purpose, it doesn't try to do anything clever, simply to return the data from the worksheet as efficiently and quickly as possible
getOldCalculatedValue() is used for a very specific circumstance, and isn't guaranteed to be correct even then, because it retrieves the last value calculated for the cell in MS EXcel itself, which ,ay not be correct if the external workbook wasn't available to MS Excel in that circumstance, or MS Excel formula evaluation was disable.
When calculating cells values from a formula, the PHPExcel calculation engine should use the getOldCalculatedValue() as a fallback if it finds an external reference, and rangeToArray() will try to use this method, but it isn't perfect, especially when that reference in nested deep inside other formulae referenced in other cells.
If you know that a formula in a cell contains an external reference, you should use getOldCalculatedValue() directly for that cell
I came up with the following solution.
Maybe not perfect, but it currently does the job. Thanks for any improvements!
With PHPExcel included and the excel file uploaded and ready, I continue with:
$sheet = $objPHPExcel->getSheet(0);
$highestRow = $sheet->getHighestRow();
Create a new array to store the cell values of a row
$arr_row = array();
Loop through the rows
for ($rownumber = 2; $rownumber <= $highestRow; $rownumber++){
$row = $sheet->getRowIterator($rownumber)->current();
$cellIterator = $row->getCellIterator();
$cellIterator->setIterateOnlyExistingCells(false);
Then loop through the cells of the current row
foreach ($cellIterator as $cell) {
Find cells with a formula
$cellcheck = substr($cell->getValue(),0,1);
if($cellcheck == '='){
$cell_content = $cell->getOldCalculatedValue();
}
else{
$cell_content = $cell->getValue();
}
Add the cell values to the array
array_push($arr_row,$cell_content);
Close cell loop
}
At this point I use the $arr_row to do further calculations and string formatting, before finally inserting it into a mysql table.
Close row loop
}
I made some changes in the function rangeToArray() inside Worksheet.php.
Worked fine!
public function rangeToArray($pRange = 'A1', $nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false) {
// Returnvalue
$returnValue = array();
// Identify the range that we need to extract from the worksheet
list($rangeStart, $rangeEnd) = PHPExcel_Cell::rangeBoundaries($pRange);
$minCol = PHPExcel_Cell::stringFromColumnIndex($rangeStart[0] -1);
$minRow = $rangeStart[1];
$maxCol = PHPExcel_Cell::stringFromColumnIndex($rangeEnd[0] -1);
$maxRow = $rangeEnd[1];
$maxCol++;
// Loop through rows
$r = -1;
for ($row = $minRow; $row <= $maxRow; ++$row) {
$rRef = ($returnCellRef) ? $row : ++$r;
$c = -1;
// Loop through columns in the current row
for ($col = $minCol; $col != $maxCol; ++$col) {
$cRef = ($returnCellRef) ? $col : ++$c;
// Using getCell() will create a new cell if it doesn't already exist. We don't want that to happen
// so we test and retrieve directly against _cellCollection
if ($this->_cellCollection->isDataSet($col.$row)) {
// Cell exists
$cell = $this->_cellCollection->getCacheData($col.$row);
if ($cell->getValue() !== null) {
if ($cell->getValue() instanceof PHPExcel_RichText) {
$returnValue[$rRef][$cRef] = $cell->getValue()->getPlainText();
} else {
if ($calculateFormulas)
{ ##################################### CHANGED LINES
if(!preg_match('/^[=].*/', $cell->getValue()))
{
$returnValue[$rRef][$cRef] = $cell->getCalculatedValue(); # THE ORIGINAL CODE ONLY HAD THIS LINE
}
else
{
$returnValue[$rRef][$cRef] = $cell->getOldCalculatedValue();
}
} ##################################### CHANGED LINES
else
{
$returnValue[$rRef][$cRef] = $cell->getValue();
}
}
if ($formatData) {
$style = $this->_parent->getCellXfByIndex($cell->getXfIndex());
$returnValue[$rRef][$cRef] = PHPExcel_Style_NumberFormat::toFormattedString(
$returnValue[$rRef][$cRef],
($style && $style->getNumberFormat()) ?
$style->getNumberFormat()->getFormatCode() :
PHPExcel_Style_NumberFormat::FORMAT_GENERAL
);
}
} else {
// Cell holds a NULL
$returnValue[$rRef][$cRef] = $nullValue;
}
} else {
// Cell doesn't exist
$returnValue[$rRef][$cRef] = $nullValue;
}
}
}
// Return
return $returnValue;
}

Unsufficient privileges from responseText in Plone4.3

I use PloneBooking3.0.0a2 with Plone4.3.3, but if I want to show periodic bookings I get an unsufficient privileges error. In my opinion there are two functions responsible for that:
function showPeriodicityResult(url, alt_url, target_id, form_id, waiting_text) {
ajaxobject = getXmlHttpRequest();
form = document.getElementById(form_id);
periodicity_type = getPeriodicityType(form);
periodicity_end_date = form['periodicity_form_periodicity_end_date_0'].value;
periodicity_variable = form['periodicity2_x'].value;
query = getPeriodicityQuery(periodicity_type, periodicity_end_date, periodicity_variable);
url = url + query + "&d=" + (new Date()).getTime();
alt_url = alt_url + query;
// Opera does not support ajax
if (ajaxobject == null) {
window.location = alt_url;
} else {
var node = document.getElementById(target_id);
node.innerHTML = waiting_text;
ajaxobject.open('GET', url, true);
ajaxobject.onreadystatechange = function(){CallBackGenerateAjaxHTML(ajaxobject, target_id);};
ajaxobject.send(null);
}
}
and
function CallBackGenerateAjaxHTML(ajaxobject, target_id) {
if (ajaxobject.readyState == 4) {
if (ajaxobject.status > 299 || ajaxobject.status < 200) {
return;
}
elem = document.getElementById(target_id);
elem.innerHTML = ajaxobject.responseText;
}
}
Especially the innerHTML setting with responseText seems to be a problem. Is there is a quick answer like Plone version diff from 3 to 4 or must I work in-depth?
You mentioned in the comments that the portal.uid_catalog raises the Unauthorized.
When I recall correctly the uid-catalog requires a higher permission since the last Plone hotfix. But you also can search an Item when given a UID with the normal Catalog.
here_obj python:portal.portal_catalog(UID=here_uid)[0].getObject();
This way you should be able to get your Object.

Workaround for copying style with PHPExcel

I want to copy the style information from cells to ranges, like Format Painter in Excel. The documentation says to do something like this:
$activeSheet->duplicateStyle($activeSheet->getStyle('A1'), 'D1:D100');
$activeSheet->duplicateStyle($activeSheet->getStyle('B1'), 'E1:E100');
There appears to be a bug because both D1:D100 and E1:E100 get the style from cell B1. If I change the order of the two lines, both ranges get the style from A1. Similarly,
$styleA = $activeSheet->getStyle('A1');
$styleB = $activeSheet->getStyle('B1');
$activeSheet->duplicateStyle($styleA, 'D1:D100');
results in D1:D100 getting style info from cell B1. The last getStyle value is used in all duplicateStyle results.
I'm sure that a future release of PHPExcel will have a fix, I just need to figure out a work-around until then.
One workround for you might be to use the style xf Indexes:
$xfIndex = $activeSheet->getCell('A1')->getXfIndex();
Then to set that value for the xfIndex of all cells in the range
for ($col = 'D'; $col != 'E'; ++$col) {
for ($row = 1; $row <= 100; ++$row) {
$activeSheet->getCell($col . $row)->setXfIndex($xfIndex);
}
}
EDIT
Alternatively, apply fix to the duplicateStyle() method in Classes/PHPExcel/Worksheet.php
lines 1479 to 1486 currently read:
if ($this->_parent->cellXfExists($pCellStyle)) {
// there is already this cell Xf in our collection
$xfIndex = $pCellStyle->getIndex();
} else {
// we don't have such a cell Xf, need to add
$workbook->addCellXf($pCellStyle);
$xfIndex = $pCellStyle->getIndex();
}
change to:
if ($existingStyle = $this->_parent->getCellXfByHashCode($pCellStyle->getHashCode())) {
// there is already such cell Xf in our collection
$xfIndex = $existingStyle->getIndex();
} else {
// we don't have such a cell Xf, need to add
$workbook->addCellXf($pCellStyle);
$xfIndex = $pCellStyle->getIndex();
}
Similarly in the applyFromArray() method in Classes/PHPExcel/Style.php
lines 425 to 432 currently read:
if ($workbook->cellXfExists($newStyle)) {
// there is already such cell Xf in our collection
$newXfIndexes[$oldXfIndex] = $existingStyle->getIndex();
} else {
// we don't have such a cell Xf, need to add
$workbook->addCellXf($newStyle);
$newXfIndexes[$oldXfIndex] = $newStyle->getIndex();
}
change to:
if ($existingStyle = $workbook->getCellXfByHashCode($newStyle->getHashCode())) {
// there is already such cell Xf in our collection
$newXfIndexes[$oldXfIndex] = $existingStyle->getIndex();
} else {
// we don't have such a cell Xf, need to add
$workbook->addCellXf($newStyle);
$newXfIndexes[$oldXfIndex] = $newStyle->getIndex();
}
EDIT #2
Fix has now been pushed to the develop branch on github. It does give a slight performance hit, depending on the number of styles in use... I'll try and get a faster version tomorrow night

Flex Newbie XMLList question - Sorting XML and XMLList

Is it possible to sort an XMLList? All the examples I can find on it create a new XMLListCollection like this:
MyXMLListCol = new XMLListCollection(MyXMLList);
I don't think the XMLListCollection in this case has any reference to the XMLList so sorting it would leave my XMLList unsorted, is this correct?
How can I sort the XMLList directly?
Thanks
~Mike
So I finally got my search terms altered enough I actually churned up an answer to this.
Using the technique I got from here:
http://freerpad.blogspot.com/2007/07/more-hierarchical-sorting-e4x-xml-for.html
I was able to come up with this:
public function sortXMLListByAttribute(parentNode:XML,xList:XMLList,attr:String):void{
//attr values must be ints
var xListItems:int = xList.length();
if(xListItems !=0){
var sortingArray:Array = new Array();
var sortAttr:Number = new Number();
for each (var item:XML in xList){
sortAttr = Number(item.attribute(attr));
if(sortingArray.indexOf(sortAttr)==-1){
sortingArray.push(sortAttr);
}
//piggy back the removal, just have to remove all of one localName without touching items of other localNames
delete parentNode.child(item.localName())[0];
}
if( sortingArray.length > 1 ) {
sortingArray.sort(Array.NUMERIC);
}
var sortedList:XMLList = new XMLList();
for each(var sortedAttr:Number in sortingArray){
for each (var item2:XML in xList){
var tempVar:Number = Number(item2.attribute(attr));
if(tempVar == sortedAttr){
sortedList += item2
}
}
}
for each(var item3:XML in sortedList){
parentNode.appendChild(item3);
}
}
}
Works pretty fast and keeps my original XML variable updated. I know I may be reinventing the wheel just to not use an XMLListCollection, but I think the ability to sort XML and XMLLists can be pretty important
While there is no native equivalent to the Array.sortOn function, it is trivial enough to implement your own sorting algorithm:
// Bubble sort.
// always initialize variables -- it save memory.
var ordered:Boolean = false;
var l:int = xmlList.length();
var i:int = 0;
var curr:XML = null;
var plus:XML = null;
while( !ordered )
{
// Assume that the order is correct
ordered = true;
for( i = 0; i < l; i++ )
{
curr = xmlList[ i ];
plus = xmlList[ i + 1 ];
// If the order is incorrect, swap and set ordered to false.
if( Number( curr.#order ) < Number( plus.#order ) )
{
xmlList[ i ] = plus;
xmlList[ i + 1 ] = curr;
ordered = false;
}
}
}
but, realistically, it is far easier and less buggy to use XMLListCollection. Further, if someone else is reading your code, they will find it easier to understand. Please do yourself a favor and avoid re-inventing the wheel on this.

Resources