* @copyright 2007-2011 PrestaShop SA * @version Release: $Revision: 9841 $ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) * International Registered Trademark & Property of PrestaShop SA */ class AdminAccountingExportControllerCore extends AdminController { public $exportTypeList = array(); public $pathTheme = ''; public $defaultType = 'block_global_export'; public $downloadDir = ''; public $downloadFile = ''; public $exportSelected = ''; public $fd = null; public $date = array( 'begin' => '', 'end' => '' ); public $prevent = array( 'errors' => array(), 'warns' => array(), 'hints' => array()); public $exportedFilePath = ''; public $clientPrefix = ''; public function __construct() { $this->className = 'Accounting'; $this->content = ''; $this->downloadDir = _PS_ADMIN_DIR_.'/export/'; $this->exportSelected = 'global_export'; $this->initExportFieldList(); parent::__construct(); } /** * Init the available fields by export type with associated translation */ protected function initExportFieldList() { $this->exportTypeList = array( 'global_export' => array( 'name' => $this->l('Global Export'), 'type' => 'global_export', 'file' => 'accounting_global_export.csv', 'fields' => array( 'invoice_date' => $this->l('Invoice Date', 'AdminTab', false, false), 'journal' => $this->l('Journal', 'AdminTab', false, false), 'account' => $this->l('Account', 'AdminTab', false, false), 'invoice_number' => $this->l('Invoice Number', 'AdminTab', false, false), 'credit' => $this->l('Credit (TTC)', 'AdminTab', false, false), 'debit' => $this->l('Debit (TVA+HT)', 'AdminTab', false, false), 'transaction_id' => $this->l('Transaction Number', 'AdminTab', false, false), 'payment_type' => $this->l('Payment Type', 'AdminTab', false, false), 'currency_code' => $this->l('Currency Code', 'AdminTab', false, false), 'wording' => $this->l('Wording', 'AdminTab', false, false) ) ), 'reconciliation_export' => array( 'name' => $this->l('Reconciliation Export'), 'type' => 'reconciliation_export', 'file' => 'accounting_reconciliation_export.csv', 'fields' => array( 'invoice_number' => $this->l('Invoice Number', 'AdminTab', false, false), 'wording' => $this->l('Wording', 'AdminTab', false, false), 'total_paid_real' => $this->l('Total TTC', 'AdminTab', false, false), 'invoice_date' => $this->l('Invoice Date', 'AdminTab', false, false), 'transaction_id' => $this->l('Transaction Number', 'AdminTab', false, false), 'account_client' => $this->l('Account client', 'AdminTab', false, false) ) ) ); } /** * Init the block Menu */ protected function initMenu() { $this->context->smarty->assign(array( 'exportTypeList' => $this->exportTypeList, 'defaultType' => $this->defaultType, 'preventList' => $this->prevent ) ); $this->content .= $this->createTemplate('menu.tpl')->fetch(); } /** * AdminController::setMedia() override * @see AdminController::setMedia() */ public function setMedia() { parent::setMedia(); $this->addJqueryUi('ui.datepicker'); } protected function checkRights() { if (!is_writeable($this->downloadDir)) $this->errors[] = $this->l('The download folder doesn\'t have the sufficient right…'); if (!($this->fd = fopen($this->downloadFile, 'w+'))) $this->errors[] = $this->l('The file can\'t be opened or created, please check the right'); @chmod($this->downloadFile, 0777); } /** * AdminController::init() override * @see AdminController::init() */ public function initContent() { $this->initMenu(); $this->context->smarty->assign(array( 'clientPrefix' => Configuration::get('ACCOUNTING_CLIENT_PREFIX_EXPORT'), 'journal' => Configuration::get('ACCOUNTING_JOURNAL_EXPORT'), 'begin_date' => Tools::getValue('beginDate'), 'end_date' => Tools::getValue('endDate'), 'urlDownload' => Tools::getShopDomain().'/download/' )); foreach ($this->exportTypeList as $exportType) { $this->context->smarty->assign(array( 'title' => $exportType['name'], 'type' => $exportType['type'], 'existingExport' => file_exists($this->downloadDir.$exportType['file']) ? true : false )); $this->content .= $this->createTemplate($exportType['type'].'.tpl')->fetch(); } parent::initContent(); } /** * AdminController::postProcess() override * @see AdminController::postProcess() */ public function postProcess() { if (Tools::isSubmit('submitAccountingExportType')) { $this->date['begin'] = Tools::getValue('beginDate'); $this->date['end'] = Tools::getValue('endDate'); $this->clientPrefix = Tools::getValue('clientPrefix'); $this->exportSelected = Tools::getValue('exportType'); $this->downloadFile = $this->downloadDir.'accounting_'.$this->exportSelected.'.csv'; Configuration::updateValue('ACCOUNTING_CLIENT_PREFIX_EXPORT', $this->clientPrefix); // Depends of the number of order and the range dates // Switch to ajax if there is any problems with time ini_set('max_execution_time', 0); switch ($this->exportSelected) { case 'reconciliation_export': $this->runReconciliationExport(); break; case 'global_export': $this->runGlobalExport(); Configuration::updateValue('ACCOUNTING_JOURNAL_EXPORT', Tools::getValue('journal')); break; default: // If not defined, set export type to default $this->exportSelected = 'global_export'; } // Set back the default block type to display $this->defaultType = 'block_'.$this->exportSelected.''; } else if (($type = Tools::getValue('download')) && array_key_exists($type, $this->exportTypeList)) $this->downloadFile($this->exportTypeList[$type]['file']); } /** * Write the exported content tout a file * @var array $list Result of the SQL query */ protected function writeExportToFile($list) { $this->checkRights(); if (!count($this->errors) && $this->fd !== null) { $buffer = ''; foreach ($this->exportTypeList[$this->exportSelected]['fields'] as $key => $translation) $buffer .= '"'.$translation.'";'; fwrite($this->fd, mb_convert_encoding(rtrim($buffer, ';')."\r\n", 'UTF-16LE')); // Bufferize line by line and write it to the file // Todo :: Allow to configure the size of the buffer before flushing it foreach ($list as $row) { $buffer = ''; foreach ($row as $col => $val) $buffer .= '"'.$val.'";'; fwrite($this->fd, mb_convert_encoding(rtrim($buffer, ';')."\r\n", 'UTF-16LE')); } $this->confirmations[] = $this->l('Export has been successfully done'); } } /** * Start the reconciliation export type */ protected function runReconciliationExport() { $query = ' SELECT CONCAT(\''.Configuration::get('PS_INVOICE_PREFIX').'\', LPAD(oi.`number`, 6, "0")) AS invoice_number, CASE WHEN (a.`company` != "" AND a.`company` IS NOT NULL) THEN a.`company` ELSE a.`lastname` END AS wording, o.`total_paid_real`, oi.`date_add` as invoice_date, pcc.`transaction_id`, CONCAT(\''.pSQL($this->clientPrefix).'\', LPAD(c.`id_customer`, 6, "0")) AS account_client FROM `'._DB_PREFIX_.'orders` o LEFT JOIN `'._DB_PREFIX_.'customer` c ON c.`id_customer` = o.`id_customer` LEFT JOIN `'._DB_PREFIX_.'address` a ON a.`id_customer` = o.`id_customer` LEFT JOIN `'._DB_PREFIX_.'order_payment` pcc ON pcc.`id_order` = o.`id_order` LEFT JOIN `'._DB_PREFIX_.'order_invoice` oi ON oi.`id_order` = o.`id_order` WHERE o.`valid` = 1 AND oi.`date_add` BETWEEN \''.pSQL($this->date['begin']).'\' AND \''.pSQL($this->date['end']).'\''; $list = Db::getInstance()->executeS($query); $this->writeExportToFile($list); } /** * Generate a line for the CSV for the global export * * @param $row * @param $line_number * @return array */ protected function createLine($row, $line_number) { $line = array(); // Default Values $line[0] = $row['invoice_date']; $line[1] = Tools::getValue('journal'); $line[2] = ''; // account number $line[3] = $row['invoice_number']; $line[4] = 0.00; // Credit TTC (Total for first csv line, 0 for others) $line[5] = 0.00; // Debit HT (0 For the first line, used for tax too) $line[6] = $row['transaction_id']; $line[7] = $row['payment_type']; $line[8] = $row['currency_code']; $line[9] = $row['wording']; // Override case depending of the whished line switch ($line_number) { case 0: $line[2] = $row['account_client']; $line[4] = $row['total_price_tax_incl']; break; case 1: $line[2] = !empty($row['account']) ? $row['account'] : Configuration::get('default_account_number', null, null, $row['id_shop']); // Force an empty string if Configuration send false $line[2] = empty($line[2]) ? '' : $line[2]; $line[5] = $row['product_price_ht']; break; case 2: $line[2] = $row['tax_accounting_account_number']; $line[5] = $row['tax_total_amount']; break; } return $line; } /** * Build an proper list to be written into the export file * * @param $db_details * @return array */ protected function buildGlobalExportlist($db_details) { // List use to write data in csv file $list = array(); // Cache list to merge easily the content with the same accounting for different invoice number $cache_list = array(); $num = 0; foreach ($db_details as $row) { // Init the list for the current invoice number if (!array_key_exists($row['invoice_number'], $cache_list)) $cache_list[$row['invoice_number']] = array(); // Need to Generate 3 lines for a product for ($i = 0; $i < 3; ++$i) // Create the two first line and check if a tax exist for the last one if ($i < 2 || ($i == 2 && $row['id_tax'] !== null)) { // Generate a product line $line = $this->createLine($row, $i); if ($i == 0) $list[$num++] = $line; else { // Check if the account number hadn't already be used for this invoice number // $line[3] = invoice_number, $line[2] = account_number if (!array_key_exists($line[2], $cache_list[$line[3]])) $cache_list[$line[3]][$line[2]] = array(); // If this id_product doesn't exist for this invoice number, then we create it as a cache if (!in_array($row['id_product'], $cache_list[$line[3]][$line[2]])) { $cache_list[$line[3]][$line[2]][$row['id_product']] = array( 'position' => $num, 'id_order' => $row['id_order'], 'id_product_attribute' => $row['id_product_attribute'], 'quantity' => 1, 'advanced_stock_management' => $row['advanced_stock_management']); $list[$num++] = $line; } else { // Merge amount retrieving the position in the list of the invoice number // Some information could change (quantity) for the Stock movement price calculation $pos = $cache_list[$line[3]][$line[2]][$row['id_product']]['position']; $cache_list[$line[3]][$line[2]][$row['id_product']]['quantity'] += 1; if (!$i) $list[$pos][4] += $line[4]; else $list[$pos][5] += $line[5]; } } } } // If advanced stock management enable then we foreach the cache_list to know // if a product use the system to store back the movement price. if (Configuration::get('PS_ADVANCED_STOCK_MANAGEMENT')) foreach ($cache_list as $invoice_list) foreach ($invoice_list as $product_list) foreach ($product_list as $id_product => $product_detail) if ($product_detail['advanced_stock_management']) { // Get stock product stock movement detail $stock_mvt = StockMvt::getNegativeStockMvts( $product_detail['id_order'], $id_product, $product_detail['id_product_attribute'], $product_detail['quantity']); // Store new price $list[$product_detail['position']] = $stock_mvt['price_te']; } return $list; } /** * Start the global export type * */ protected function runGlobalExport() { $query = ' SELECT od.`id_order`, oi.`date_add` as invoice_date, CASE WHEN (acc_pzs.`account_number` != "" AND acc_pzs.`account_number` IS NOT NULL) THEN acc_pzs.`account_number` WHEN (acc_zs.`account_number` != "" AND acc_zs.`account_number` IS NOT NULL) THEN acc_zs.`account_number` ELSE "" END AS account, CONCAT(\''.Configuration::get('PS_INVOICE_PREFIX').'\', LPAD(oi.`number`, 6, "0")) AS invoice_number, od.`total_price_tax_incl`, od.`product_price` AS product_price_ht, pcc.`transaction_id`, o.`payment` AS payment_type, currency.`iso_code` AS currency_code, CONCAT(\''.pSQL($this->clientPrefix).'\', LPAD(customer.`id_customer`, 6, "0")) AS account_client, CASE WHEN (a.`company` != "" AND a.`company` IS NOT NULL) THEN a.`company` ELSE a.`lastname` END AS wording, t.`account_number` AS tax_accounting_account_number, t.`id_tax`, o.`id_shop`, odt.`total_amount` AS tax_total_amount, od.`product_id` AS id_product, od.`product_attribute_id` as id_product_attribute, p.`advanced_stock_management` FROM `'._DB_PREFIX_.'orders` o LEFT JOIN `'._DB_PREFIX_.'customer` customer ON customer.`id_customer` = o.`id_customer` LEFT JOIN `'._DB_PREFIX_.'address` a ON a.`id_customer` = o.`id_customer` LEFT JOIN `'._DB_PREFIX_.'order_payment` pcc ON pcc.`id_order` = o.`id_order` LEFT JOIN `'._DB_PREFIX_.'order_detail` od ON od.`id_order` = o.`id_order` LEFT JOIN `'._DB_PREFIX_.'currency` currency ON currency.`id_currency` = o.`id_currency` LEFT JOIN `'._DB_PREFIX_.'order_detail_tax` odt ON odt.`id_order_detail` = od.`id_order_detail` LEFT JOIN `'._DB_PREFIX_.'tax` t ON t.`id_tax` = odt.`id_tax` LEFT JOIN `'._DB_PREFIX_.'country` country ON country.`id_country` = a.`id_country` LEFT JOIN `'._DB_PREFIX_.'product` p ON p.`id_product` = od.`product_id` LEFT JOIN `'._DB_PREFIX_.'accounting_product_zone_shop` acc_pzs ON (acc_pzs.`id_shop` = o.`id_shop` AND acc_pzs.`id_zone` = country.`id_zone` AND acc_pzs.`id_product` = od.`product_id`) LEFT JOIN `'._DB_PREFIX_.'accounting_zone_shop` acc_zs ON (acc_zs.`id_shop` = o.`id_shop` AND acc_zs.`id_zone` = country.`id_zone`) LEFT JOIN `'._DB_PREFIX_.'order_invoice` oi ON oi.id_order = o.id_order WHERE o.`valid` = 1 AND oi.`date_add` BETWEEN \''.pSQL($this->date['begin']).'\' AND \''.pSQL($this->date['end']).'\' ORDER BY o.`id_order` ASC'; $list = $this->buildGlobalExportlist(Db::getInstance()->executeS($query)); $this->writeExportToFile($list); } /** * Allow to download the last export file * @var string File name */ protected function downloadFile($fileName) { $path = $this->downloadDir.$fileName; header('Content-length: '.filesize($path)); header('Content-Disposition: attachment; filename="'.$fileName.'"'); // Flush buffered data before reading the file ob_clean(); flush(); @readfile($path); exit(); } }