2 /**********************************************************************
3 Copyright (C) FrontAccounting, LLC.
4 Released under the terms of the GNU General Public License, GPL,
5 as published by the Free Software Foundation, either version 3
6 of the License, or (at your option) any later version.
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10 See the License here <http://www.gnu.org/licenses/gpl-3.0.html>.
11 ***********************************************************************/
12 include_once($path_to_root . "/reporting/includes/Workbook.php");
13 include_once($path_to_root . "/admin/db/company_db.inc");
14 include_once($path_to_root . "/admin/db/fiscalyears_db.inc");
15 include_once($path_to_root . "/config.php");
16 define('MAX_ROW_SHEET', 65530);
18 class FrontReport extends Spreadsheet_Excel_Writer_Workbook
30 var $bottomMargin = 0;
33 var $pageWidth; // dummy
34 var $rightMargin; // dummy
49 var $excelColWidthFactor;
55 var $formatHeaderLeft;
56 var $formatHeaderRight;
58 var $formatAmount = array();
61 var $formatTopHeaderLeft;
62 var $formatBottomHeaderLeft;
63 var $formatTopHeaderRight;
64 var $formatBottomHeaderRight;
69 function __construct($title, $filename, $size = 'A4', $fontsize = 9, $orientation = 'P', $margins = NULL, $excelColWidthFactor = 6.5)
71 global $SysPrefs, $page_security;
73 if (!$_SESSION["wa_current_user"]->can_access_page($page_security))
75 display_error(_("The security settings on your account do not permit you to print this report"));
80 $this->title = $title;
81 $this->lineHeight = 12;
83 $this->SetFontSize($fontsize);
84 $this->oldFontSize = 0;
87 $this->excelColWidthFactor = $excelColWidthFactor;
88 $rtl = ($_SESSION['language']->dir == 'rtl');
89 $this->code = strtolower($_SESSION['language']->encoding);
90 $this->filename = $filename.".xls";
91 $this->unique_name = random_id().".xls";
92 $this->path = company_path(). '/pdf_files';
93 parent::__construct($this->path."/".$this->unique_name);
94 if ($this->code != "iso-8859-1")
95 $this->setVersion(8); // set biff version to 8 (0x0006 internal)
96 $this->sheet = $this->addWorksheet($this->worksheetNameGenerator($this->title));
97 if ($this->code != "iso-8859-1")
98 $this->sheet->setInputEncoding($this->code); // set sheet encoding
100 $this->sheet->setRTL();
101 $this->formatTitle =& $this->addFormat();
102 $this->formatTitle->setSize(16);
103 $this->formatTitle->setBold();
104 $this->formatTitle->setAlign($rtl ? 'right' : 'left');
105 $this->formatTitle->setTop(2);
106 $this->formatTitle->setTopColor('gray');
108 $how = user_date_format();
109 $sep = $SysPrefs->dateseps[user_date_sep()];
114 $dateformat_long = "mm{$sep}dd{$sep}yyyy\ \ hh:mm\ am/pm";
115 $dateformat = "mm{$sep}dd{$sep}yyyy";
119 $dateformat_long = "dd{$sep}mm{$sep}yyyy\ \ hh:mm";
120 $dateformat = "dd{$sep}mm{$sep}yyyy";
124 $dateformat_long = "yyyy{$sep}mm{$sep}dd\ \ hh:mm";
125 $dateformat = "yyyy{$sep}mm{$sep}dd";
127 $this->formatDateTime =& $this->addFormat();
128 $this->formatDateTime->setNumFormat($dateformat_long);
129 $this->formatDateTime->setAlign($rtl ? 'right' : 'left');
130 $this->formatDate =& $this->addFormat();
131 $this->formatDate->setNumFormat($dateformat);
132 $this->formatDate->setAlign($rtl ? 'right' : 'left');
133 $this->formatRight =& $this->addFormat();
134 $this->formatRight->setAlign($rtl ? 'left' : 'right');
135 $this->formatLeft =& $this->addFormat();
136 $this->formatLeft->setAlign($rtl ? 'right' : 'left');
138 $this->formatHeaderLeft =& $this->addFormat();
139 $this->formatHeaderLeft->setItalic();
140 $this->formatHeaderLeft->setTop(2);
141 $this->formatHeaderLeft->setTopColor('gray');
142 $this->formatHeaderLeft->setBottom(2);
143 $this->formatHeaderLeft->setBottomColor('gray');
144 $this->formatHeaderLeft->setAlign('vcenter');
145 $this->formatTopHeaderLeft =& $this->addFormat();
146 $this->formatTopHeaderLeft->setItalic();
147 $this->formatTopHeaderLeft->setTop(2);
148 $this->formatTopHeaderLeft->setTopColor('gray');
149 $this->formatTopHeaderLeft->setAlign('vcenter');
150 $this->formatBottomHeaderLeft =& $this->addFormat();
151 $this->formatBottomHeaderLeft->setItalic();
152 $this->formatBottomHeaderLeft->setBottom(2);
153 $this->formatBottomHeaderLeft->setBottomColor('gray');
154 $this->formatBottomHeaderLeft->setAlign('vcenter');
155 $this->formatDate->setAlign($rtl ? 'right' : 'left');
156 $this->formatHeaderRight =& $this->addFormat();
157 $this->formatHeaderRight->setItalic();
158 $this->formatHeaderRight->setTop(2);
159 $this->formatHeaderRight->setTopColor('gray');
160 $this->formatHeaderRight->setBottom(2);
161 $this->formatHeaderRight->setBottomColor('gray');
162 $this->formatHeaderRight->setAlign('vcenter');
163 $this->formatHeaderRight->setAlign('right');
164 $this->formatTopHeaderRight =& $this->addFormat();
165 $this->formatTopHeaderRight->setItalic();
166 $this->formatTopHeaderRight->setTop(2);
167 $this->formatTopHeaderRight->setTopColor('gray');
168 $this->formatTopHeaderRight->setAlign('vcenter');
169 $this->formatTopHeaderRight->setAlign('right');
170 $this->formatBottomHeaderRight =& $this->addFormat();
171 $this->formatBottomHeaderRight->setItalic();
172 $this->formatBottomHeaderRight->setBottom(2);
173 $this->formatBottomHeaderRight->setBottomColor('gray');
174 $this->formatBottomHeaderRight->setAlign('vcenter');
175 $this->formatBottomHeaderRight->setAlign('right');
176 $this->formatFooter =& $this->addFormat();
177 $this->formatFooter->setTop(2);
178 $this->formatFooter->setTopColor('gray');
179 $this->SetHeaderType("header");
186 function SetHeaderType($name) {
187 $this->headerFunc = $name;
189 // Check a given name to see if it's a valid Excel worksheet name,
190 // and fix if necessary
191 function worksheetNameGenerator($name)
193 // First, strip out characters which aren't allowed
194 $illegal_chars = array(':', '\\', '/', '?', '*', '[', ']');
195 for ($i = 0; $i < count($illegal_chars); $i++)
196 $name = str_replace($illegal_chars[$i], '', $name);
197 // Now, if name is longer than 31 chars, truncate it
198 if (strlen($name) > 31)
199 $name = substr($name, 0, 31);
203 function NumFormat($dec)
205 if (!isset($this->formatAmount[$dec]))
210 $format = "###{$tsep}###{$tsep}###{$tsep}##0";
212 $format .= "{$dsep}".str_repeat('0',$dec);
213 $this->formatAmount[$dec] =& $this->addFormat();
214 $this->formatAmount[$dec]->setNumFormat($format);
215 $this->formatAmount[$dec]->setAlign('right');
217 return $this->formatAmount[$dec];
220 function Font($fontname = '', $style = 'normal')
224 function SetFontSize($size)
226 $this->fontSize = $size;
229 function Info($params, $cols, $headers, $aligns,
230 $cols2 = null, $headers2 = null, $aligns2 = null)
232 $this->company = get_company_prefs();
233 $year = get_current_fiscalyear();
234 if ($year['closed'] == 0)
238 $this->fiscal_year = sql2date($year['begin']) . " - " . sql2date($year['end']) . " " . "(" . $how . ")";
239 $this->user = $_SESSION["wa_current_user"]->name;
240 $this->host = $_SERVER['SERVER_NAME'];
241 $this->params = $params;
243 $this->headers = $headers;
244 $this->aligns = $aligns;
245 $this->cols2 = $cols2;
246 $this->headers2 = $headers2;
247 $this->aligns2 = $aligns2;
248 $this->numcols = count($this->headers);
249 $tcols = count_array($this->headers2);
250 if ($tcols > $this->numcols)
251 $this->numcols = $tcols;
252 for ($i = 0; $i < $this->numcols; $i++)
253 $this->sheet->setColumn($i, $i, $this->px2units($this->cols[$i + 1] - $this->cols[$i]));
258 $tcol = $this->numcols - 1;
259 $this->sheet->setRow($this->y, 20);
260 for ($i = 0; $i < $this->numcols; $i++)
261 $this->sheet->writeBlank($this->y, $i, $this->formatTitle);
262 $this->sheet->writeString($this->y, 0, $this->title, $this->formatTitle);
263 $this->sheet->mergeCells($this->y, 0, $this->y, $tcol);
265 $str = _("Print Out Date") . ':';
266 $this->sheet->writeString($this->y, 0, $str, $this->formatLeft);
267 $this->sheet->writeString($this->y, 1, Today() . " ".Now(), $this->formatLeft);
268 $this->sheet->writeString($this->y, $tcol-1, $this->company['coy_name'], $this->formatLeft);
269 $this->sheet->mergeCells($this->y, $tcol-1, $this->y, $tcol);
271 $str = _("Fiscal Year") . ':';
272 $this->sheet->writeString($this->y, 0, $str, $this->formatLeft);
273 $str = $this->fiscal_year;
274 $this->sheet->writeString($this->y, 1, $str, $this->formatLeft);
275 $this->sheet->writeString($this->y, $tcol-1, $this->host, $this->formatLeft);
276 $this->sheet->mergeCells($this->y, $tcol-1, $this->y, $tcol);
277 for ($i = 1; $i < count($this->params); $i++)
279 if ($this->params[$i]['from'] != '')
282 $str = $this->params[$i]['text'] . ':';
283 $this->sheet->writeString($this->y, 0, $str);
284 $str = $this->params[$i]['from'];
285 if ($this->params[$i]['to'] != '')
286 $str .= " - " . $this->params[$i]['to'];
287 $this->sheet->writeString($this->y, 1, $str, $this->formatLeft);
290 $this->sheet->writeString($this->y, $tcol-1, $this->user, $this->formatLeft);
291 $this->sheet->mergeCells($this->y, $tcol-1, $this->y, $tcol);
295 if ($this->params[0] != '') // Comments
298 $str = _("Comments") . ':';
299 $this->sheet->writeString($this->y, 0, $str, $this->formatLeft);
300 $this->sheet->writeString($this->y, 1, $this->params[0], $this->formatLeft);
303 if ($this->headers2 != null)
305 for ($i = 0, $j = 0; $i < $this->numcols; $i++)
307 if ($this->cols2[$j] >= $this->cols[$i] && $this->cols2[$j] <= $this->cols[$i + 1])
309 if ($this->aligns2[$j] == "right")
310 $this->sheet->writeString($this->y, $i, $this->headers2[$j], $this->formatHeaderRight);
312 $this->sheet->writeString($this->y, $i, $this->headers2[$j], $this->formatHeaderLeft);
316 $this->sheet->writeString($this->y, $i, "", $this->formatHeaderLeft);
321 for ($i = 0; $i < $this->numcols; $i++)
323 if (!isset($this->headers[$i]))
326 $header = $this->headers[$i];
327 if ($this->aligns[$i] == "right")
328 $this->sheet->writeString($this->y, $i, $header, $this->formatHeaderRight);
330 $this->sheet->writeString($this->y, $i, $header, $this->formatHeaderLeft);
335 function Header2($myrow, $branch, $sales_order, $bankaccount, $doctype)
340 // Alternate header style - primary differences are for PDFs
343 // Flag to make sure we only print the company name once
344 $companyNamePrinted = false;
347 $tcol = $this->numcols - 1;
348 $this->sheet->setRow($this->y, 20);
350 for ($i = 0; $i < $this->numcols; $i++)
351 $this->sheet->writeBlank($this->y, $i, $this->formatTitle);
352 $this->sheet->writeString($this->y, 0, $this->title, $this->formatTitle);
353 $this->sheet->mergeCells($this->y, 0, $this->y, $tcol);
355 // Dimension 1 - optional
356 // - only print if available and not blank
357 if (count($this->params) > 3)
358 if ($this->params[3]['from'] != '')
361 $str = $this->params[3]['text'] . ':';
362 $this->sheet->writeString($this->y, 0, $str, $this->formatLeft);
363 $this->sheet->writeString($this->y, 1, $this->params[3]['from'], $this->formatLeft);
364 // Company Name - at end of this row
365 if (!$companyNamePrinted)
367 $this->sheet->writeString($this->y, $tcol-1, $this->company['coy_name'], $this->formatLeft);
368 $this->sheet->mergeCells($this->y, $tcol-1, $this->y, $tcol);
369 $companyNamePrinted = true;
374 // Dimension 2 - optional
375 // - only print if available and not blank
376 if (count($this->params) > 4)
377 if ($this->params[4]['from'] != '')
380 $str = $this->params[4]['text'] . ':';
381 $this->sheet->writeString($this->y, 0, $str, $this->formatLeft);
382 $this->sheet->writeString($this->y, 1, $this->params[4]['from'], $this->formatLeft);
383 // Company Name - at end of this row
384 if (!$companyNamePrinted)
386 $this->sheet->writeString($this->y, $tcol-1, $this->company['coy_name'], $this->formatLeft);
387 $this->sheet->mergeCells($this->y, $tcol-1, $this->y, $tcol);
388 $companyNamePrinted = true;
395 // Report Date - time period covered
396 // - can specify a range, or just the end date (and the report contents
397 // should make it obvious what the beginning date is)
399 $str = _("Report Date") . ':';
400 $this->sheet->writeString($this->y, 0, $str, $this->formatLeft);
402 if ($this->params[1]['from'] != '')
403 $str = $this->params[1]['from'] . ' - ';
404 $str .= $this->params[1]['to'];
405 $this->sheet->writeString($this->y, 1, $str, $this->formatLeft);
406 // Company Name - at end of this row
407 if (!$companyNamePrinted)
409 $this->sheet->writeString($this->y, $tcol-1, $this->company['coy_name'], $this->formatLeft);
410 $this->sheet->mergeCells($this->y, $tcol-1, $this->y, $tcol);
411 $companyNamePrinted = true;
414 // Timestamp of when this copy of the report was generated
416 $str = _("Generated At") . ':';
417 $this->sheet->writeString($this->y, 0, $str, $this->formatLeft);
418 $this->sheet->writeString($this->y, 1, Today() . " ".Now(), $this->formatLeft);
420 // Name of the user that generated this copy of the report
422 $str = _("Generated By") . ':';
423 $this->sheet->writeString($this->y, 0, $str, $this->formatLeft);
425 $this->sheet->writeString($this->y, 1, $str, $this->formatLeft);
427 // Comments - display any user-generated comments for this copy of the report
428 if ($this->params[0] != '')
431 $str = _("Comments") . ':';
432 $this->sheet->writeString($this->y, 0, $str, $this->formatLeft);
433 $this->sheet->writeString($this->y, 1, $this->params[0], $this->formatLeft);
437 if ($this->headers2 != null)
439 for ($i = 0, $j = 0; $i < $this->numcols; $i++)
441 if ($this->cols2[$j] >= $this->cols[$i] && $this->cols2[$j] <= $this->cols[$i + 1])
443 if ($this->aligns2[$j] == "right")
444 $this->sheet->writeString($this->y, $i, $this->headers2[$j], $this->formatTopHeaderRight);
446 $this->sheet->writeString($this->y, $i, $this->headers2[$j], $this->formatTopHeaderLeft);
450 $this->sheet->writeString($this->y, $i, "", $this->formatTopHeaderLeft);
455 for ($i = 0; $i < $this->numcols; $i++)
457 if (!isset($this->headers[$i]))
460 $header = $this->headers[$i];
461 if ($this->aligns[$i] == "right")
462 if ($this->headers2 == null)
463 $this->sheet->writeString($this->y, $i, $header, $this->formatHeaderRight);
465 $this->sheet->writeString($this->y, $i, $header, $this->formatBottomHeaderRight);
467 if ($this->headers2 == null)
468 $this->sheet->writeString($this->y, $i, $header, $this->formatHeaderLeft);
470 $this->sheet->writeString($this->y, $i, $header, $this->formatBottomHeaderLeft);
476 * Format a numeric string date into something nicer looking.
478 * @param string $date Date string to be formatted.
479 * @param int $input_format Format of the input string. Possible values are:<ul><li>0: user's default (default)</li></ul>
480 * @param int $output_format Format of the output string. Possible values are:<ul><li>0: Month (word) Day (numeric), 4-digit Year - Example: January 1, 2000 (default)</li><li>1: Month 4-digit Year - Example: January 2000</li><li>2: Month Abbreviation 4-digit Year - Example: Jan 2000</li></ul>
483 function DatePrettyPrint($date, $input_format = 0, $output_format = 0)
487 $date = date2sql($date);
488 $year = (int) (substr($date, 0, 4));
489 $month = (int) (substr($date, 5, 2));
490 $day = (int) (substr($date, 8, 2));
491 if ($output_format == 0)
492 return(date('F j, Y', mktime(12, 0, 0, $month, $day, $year)));
493 elseif ($output_format == 1)
494 return(date('F Y', mktime(12, 0, 0, $month, $day, $year)));
495 elseif ($output_format == 2)
496 return(date('M Y', mktime(12, 0, 0, $month, $day, $year)));
502 function AddImage($logo, $x, $y, $w, $h)
507 function SetDrawColor($r, $g, $b)
512 function SetTextColor($r, $g, $b)
517 function SetFillColor($r, $g, $b)
522 function GetCellPadding()
527 function SetCellPadding($pad)
532 function Text($c, $txt, $n=0, $corr=0, $r=0, $align='left', $border=0, $fill=0, $link=NULL, $stretch=0)
537 function TextWrap($xpos, $ypos, $len, $str, $align = 'left', $border = 0, $fill = 0, $link = NULL, $stretch = 0)
542 function TextCol($c, $n, $txt, $corr=0, $r=0, $border=0, $fill=0, $link=NULL, $stretch=0)
544 $txt = html_entity_decode($txt);
545 if ($this->aligns[$c] == 'right')
546 $this->sheet->writeString($this->y, $c, $txt, $this->formatRight);
548 $this->sheet->writeString($this->y, $c, $txt, $this->formatLeft);
550 $this->sheet->mergeCells($this->y, $c, $this->y, $n - 1);
553 function AmountCol($c, $n, $txt, $dec=0, $corr=0, $r=0, $border=0, $fill=0, $link=NULL, $stretch=0, $color_red=false)
555 if (!is_numeric($txt))
557 $this->sheet->writeNumber($this->y, $c, $txt, $this->NumFormat($dec));
560 function AmountCol2($c, $n, $txt, $dec=0, $corr=0, $r=0, $border=0, $fill=0, $link=NULL, $stretch=0, $color_red=false, $amount_locale = NULL, $amount_format = NULL)
562 if (!is_numeric($txt))
564 $this->sheet->writeNumber($this->y, $c, $txt, $this->NumFormat($dec));
567 function DateCol($c, $n, $txt, $conv=false, $corr=0, $r=0, $border=0, $fill=0, $link=NULL, $stretch=0)
570 $txt = date2sql($txt);
571 list($year, $mo, $day) = explode("-", $txt);
572 $date = $this->ymd2date((int)$year, (int)$mo, (int)$day);
573 $this->sheet->writeNumber($this->y, $c, $date, $this->formatDate);
576 function TextCol2($c, $n, $txt, $corr=0, $r=0, $border=0, $fill=0, $link=NULL, $stretch=0)
578 $txt = html_entity_decode($txt);
579 $this->sheet->writeString($this->y, $c, $txt, $this->formatLeft);
581 $this->sheet->mergeCells($this->y, $c, $this->y, $n - 1);
584 function TextColLines($c, $n, $txt, $corr=0, $r=0, $border=0, $fill=0, $link=NULL, $stretch=0)
589 function TextWrapLines($c, $width, $txt, $align='left', $border=0, $fill=0, $link=NULL, $stretch=0)
595 * Crude text wrap calculator based on PDF version.
597 function TextWrapCalc($txt, $width, $spacebreak=false)
599 // Assume an average character width
603 $w = strlen($txt) * $avg_char_width;
604 if ($w > $width && $w > 0 && $width != 0)
607 $k = intval($n * $width / $w);
608 if ($k > 0 && $k < $n)
610 $txt2 = substr($txt, 0, $k);
611 if ($spacebreak && (($pos = strrpos($txt2, " ")) !== false))
613 $txt2 = substr($txt2, 0, $pos);
614 $ret = substr($txt, $pos+1);
617 $ret = substr($txt, $k);
620 return array($txt2, $ret);
623 function SetLineStyle($style)
628 function SetLineWidth($width)
633 function LineTo($from, $row, $to, $row2)
638 function Line($row, $height = 0)
643 function UnderlineCell($c, $r = 0, $type = 1, $linewidth = 0, $style = array())
648 function NewLine($l=1, $np=0, $h=NULL)
651 if ($this->y > MAX_ROW_SHEET)
654 $this->sheet = $this->addWorksheet();
655 if ($this->code != "iso-8859-1")
656 $this->sheet->setInputEncoding($this->code); // set sheet encoding
657 if ($_SESSION['language']->dir == 'rtl')
658 $this->sheet->setRTL();
659 for ($i = 0; $i < $this->numcols; $i++)
660 $this->sheet->setColumn($i, $i, $this->px2units($this->cols[$i + 1] - $this->cols[$i]));
666 if (method_exists($this, $this->headerFunc)) // draw predefined page layout if any
667 $this->{$this->headerFunc}();
670 function ymd2Date($year, $mon, $day) // XLS internal date representation is a number between 1900-01-01 and 2078-12-31
671 { // if we need the time part too, we have to add this value after a decimalpoint.
672 $mo = array(0,31,28,31,30,31,30,31,31,30,31,30,31);
675 if (($year % 4) == 0)
683 elseif ($day > $mo[$mon])
687 elseif ($year > $MAXYEAR)
690 for ($n = 1; $n < $mon; $n++)
694 for ($n = $BASE; $n < $year; $n++)
703 function px2units($px) // XLS app conversion. Not bulletproof.
705 $excel_column_width_factor = 256;
706 $unit_offset_length = $this->excelColWidthFactor;
707 return ($px / $unit_offset_length);
710 function End($email=0, $subject='')
712 global $path_to_root;
714 for ($i = 0; $i < $this->numcols; $i++)
715 $this->sheet->writeBlank($this->y, $i, $this->formatFooter);
716 $this->sheet->mergeCells($this->y, 0, $this->y, $this->numcols - 1);
718 // first have a look through the directory,
719 // and remove old temporary pdfs
720 if ($d = @opendir($this->path)) {
721 while (($file = readdir($d)) !== false) {
722 if (!is_file($this->path.'/'.$file) || $file == 'index.php') continue;
723 // then check to see if this one is too old
724 $ftime = filemtime($this->path.'/'.$file);
725 // seems 3 min is enough for any report download, isn't it?
726 if (time()-$ftime > 180){
727 unlink($this->path.'/'.$file);
733 meta_forward($path_to_root.'/reporting/prn_redirect.php', "xls=1&filename=$this->filename&unique=$this->unique_name");