diff --git a/htdocs/accountancy/class/api_accountancy.class.php b/htdocs/accountancy/class/api_accountancy.class.php new file mode 100644 index 00000000000..289e17203e3 --- /dev/null +++ b/htdocs/accountancy/class/api_accountancy.class.php @@ -0,0 +1,276 @@ + + * Copyright (C) 2019 Cedric Ancelin + * Copyright (C) 2023 Lionel Vessiller + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +use Luracast\Restler\RestException; + +/** + * API class for accountancy + * + * @access protected + * @class DolibarrApiAccess {@requires user,external} + * + */ +class Accountancy extends DolibarrApi +{ + /** + * + * @var array $FIELDS Mandatory fields, checked when create and update object + */ + public static $FIELDS = array(); + + /** + * @var BookKeeping $bookkeeping {@type BookKeeping} + */ + public $bookkeeping; + + /** + * @var AccountancyExport $accountancy_export {@type AccountancyExport} + */ + public $accountancyexport; + + /** + * Constructor + */ + public function __construct() + { + global $db, $langs; + $this->db = $db; + + require_once DOL_DOCUMENT_ROOT.'/accountancy/class/bookkeeping.class.php'; + require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountancyexport.class.php'; + + $langs->load('accountancy'); + + $this->bookkeeping = new BookKeeping($this->db); + $this->accountancyexport = new AccountancyExport($this->db); + } + + /** + * Accountancy export data + * + * @param string $period Period : 'lastmonth', 'currentmonth', 'last3months', 'last6months', 'currentyear', 'lastyear', 'fiscalyear', 'lastfiscalyear', 'actualandlastfiscalyear' or 'custom' (see above) + * @param string $date_min [=''] Start date of period if 'custom' is set in period parameter + * Date format is 'YYYY-MM-DD' + * @param string $date_max [=''] End date of period if 'custom' is set in period parameter + * Date format is 'YYYY-MM-DD' + * @param string $format [=''] by default uses '1' for 'Configurable (CSV)' for format number + * or '1000' for FEC + * or '1010' for FEC2 + * (see AccountancyExport class) + * @param int $lettering [=0] by default don't export or 1 to export lettering data (columns 'letterring_code' and 'date_lettering' returns empty or not) + * @param int $alreadyexport [=0] by default export data only if it's not yet exported or 1 already exported (always export data even if 'date_export" is set) + * @param int $notnotifiedasexport [=0] by default notified as exported or 1 not notified as exported (when the export is done, notified or not the column 'date_export') + * + * @return string + * + * @url GET exportdata + * + * @throws RestException 401 Insufficient rights + * @throws RestException 404 Accountancy export period not found + * @throws RestException 404 Accountancy export start or end date not defined + * @throws RestException 404 Accountancy export format not found + * @throws RestException 500 Error on accountancy export + */ + public function exportData($period, $date_min = '', $date_max = '', $format = '', $lettering = 0, $alreadyexport = 0, $notnotifiedasexport = 0) + { + global $conf, $langs; + + // check rights + if (!DolibarrApiAccess::$user->rights->accounting->mouvements->export) { + throw new RestException(401, 'No permission to export accounting'); + } + + // check parameters + $period_available_list = array('lastmonth', 'currentmonth', 'last3months', 'last6months', 'currentyear', 'lastyear', 'fiscalyear', 'lastfiscalyear', 'actualandlastfiscalyear', 'custom'); + if (!in_array($period, $period_available_list)) { + throw new RestException(404, 'Accountancy export period not found'); + } + if ($period == 'custom') { + if ($date_min == '' && $date_max == '') { + throw new RestException(404, 'Accountancy export start and end date for custom period not defined'); + } + } + if ($format == '') { + $format = AccountancyExport::$EXPORT_TYPE_CONFIGURABLE; // uses default + } + + // get objects + $bookkeeping = $this->bookkeeping; + $accountancyexport = $this->accountancyexport; + + // find export format code from format number + $format_number_available_list = $accountancyexport->getType(); + if (is_numeric($format)) { + $format_number = (int) $format; + } else { + $format_number = 0; + $format_label_available_list = array_flip($format_number_available_list); + if (isset($format_label_available_list[$format])) { + $format_number = $format_label_available_list[$format]; + } + } + + // get all format available and check if exists + if (!array_key_exists($format_number, $format_number_available_list)) { + throw new RestException(404, 'Accountancy export format not found'); + } + + $sortorder = 'ASC'; // by default + $sortfield = 't.piece_num, t.rowid'; // by default + + // set filter for each period available + $filter = array(); + $doc_date_start = null; + $doc_date_end= null; + $now = dol_now(); + $now_arr = dol_getdate($now); + $now_month = $now_arr['mon']; + $now_year = $now_arr['year']; + if ($period == 'custom') { + if ($date_min != '') { + $time_min = strtotime($date_min); + if ($time_min !== false) { + $doc_date_start = $time_min; + } + } + if ($date_max != '') { + $time_max = strtotime($date_max); + if ($time_max !== false) { + $doc_date_end = $time_max; + } + } + } elseif ($period == 'lastmonth') { + $prev_date_arr = dol_get_prev_month($now_month, $now_year); // get previous month and year if month is january + $doc_date_start = dol_mktime(0, 0, 0, $prev_date_arr['month'], 1, $prev_date_arr['year']); // first day of previous month + $doc_date_end = dol_get_last_day($prev_date_arr['year'], $prev_date_arr['month']); // last day of previous month + } elseif ($period == 'currentmonth') { + $doc_date_start = dol_mktime(0, 0, 0, $now_month, 1, $now_year); // first day of current month + $doc_date_end = dol_get_last_day($now_year, $now_month); // last day of current month + } elseif ($period == 'last3months' || $period == 'last6months') { + if ($period == 'last3months') { + // last 3 months + $nb_prev_month = 3; + } else { + // last 6 months + $nb_prev_month = 6; + } + $prev_month_date_list = array(); + $prev_month_date_list[] = dol_get_prev_month($now_month, $now_year); // get previous month for index = 0 + for ($i = 1; $i < $nb_prev_month; $i++) { + $prev_month_date_list[] = dol_get_prev_month($prev_month_date_list[$i-1]['month'], $prev_month_date_list[$i-1]['year']); // get i+1 previous month for index=i + } + $doc_date_start = dol_mktime(0, 0, 0, $prev_month_date_list[$nb_prev_month-1]['month'], 1, $prev_month_date_list[$nb_prev_month-1]['year']); // first day of n previous month for index=n-1 + $doc_date_end = dol_get_last_day($prev_month_date_list[0]['year'], $prev_month_date_list[0]['month']); // last day of previous month for index = 0 + } elseif ($period == 'currentyear' || $period == 'lastyear') { + $period_year = $now_year; + if ($period == 'lastyear') { + $period_year--; + } + $doc_date_start = dol_mktime(0, 0, 0, 1, 1, $period_year); // first day of year + $doc_date_end = dol_mktime(23, 59, 59, 12, 31, $period_year); // last day of year + } elseif ($period == 'fiscalyear' || $period == 'lastfiscalyear' || $period == 'actualandlastfiscalyear') { + // find actual fiscal year + $cur_fiscal_period = getCurrentPeriodOfFiscalYear($this->db, $conf); + $cur_fiscal_date_start = $cur_fiscal_period['date_start']; + $cur_fiscal_date_end = $cur_fiscal_period['date_end']; + + if ($period == 'fiscalyear') { + $doc_date_start = $cur_fiscal_date_start; + $doc_date_end = $cur_fiscal_date_end; + } else { + // get one day before current fiscal date start (to find previous fiscal period) + $prev_fiscal_date_search = dol_time_plus_duree($cur_fiscal_date_start, -1, 'd'); + + // find previous fiscal year from current fiscal year + $prev_fiscal_period = getCurrentPeriodOfFiscalYear($this->db, $conf, $prev_fiscal_date_search); + $prev_fiscal_date_start = $prev_fiscal_period['date_start']; + $prev_fiscal_date_end = $prev_fiscal_period['date_end']; + + if ($period == 'lastfiscalyear') { + $doc_date_start = $prev_fiscal_date_start; + $doc_date_end = $prev_fiscal_date_end; + } else { + // period == 'actualandlastfiscalyear' + $doc_date_start = $prev_fiscal_date_start; + $doc_date_end = $cur_fiscal_date_end; + } + } + } + if (is_numeric($doc_date_start)) { + $filter['t.doc_date>='] = $doc_date_start; + } + if (is_numeric($doc_date_end)) { + $filter['t.doc_date<='] = $doc_date_end; + } + + $result = $bookkeeping->fetchAll($sortorder, $sortfield, 0, 0, $filter, 'AND', $alreadyexport); + + if ($result < 0) { + throw new RestException(500, 'Error bookkeeping fetch all : '.$bookkeeping->errorsToString()); + } else { + // export files then exit + if (empty($lettering)) { + if (is_array($bookkeeping->lines)) { + foreach ($bookkeeping->lines as $k => $movement) { + unset($bookkeeping->lines[$k]->lettering_code); + unset($bookkeeping->lines[$k]->date_lettering); + } + } + } + + $error = 0; + $this->db->begin(); + + if (empty($notnotifiedasexport)) { + if (is_array($bookkeeping->lines)) { + foreach ($bookkeeping->lines as $movement) { + $now = dol_now(); + + $sql = " UPDATE " . MAIN_DB_PREFIX . "accounting_bookkeeping"; + $sql .= " SET date_export = '" . $this->db->idate($now) . "'"; + $sql .= " WHERE rowid = " . ((int)$movement->id); + + $result = $this->db->query($sql); + if (!$result) { + $accountancyexport->errors[] = $langs->trans('NotAllExportedMovementsCouldBeRecordedAsExportedOrValidated'); + $error++; + break; + } + } + } + } + + // export and only write file without downloading + if (!$error) { + $result = $accountancyexport->export($bookkeeping->lines, $format_number, 0, 1, 2); + if ($result < 0) { + $error++; + } + } + + if ($error) { + $this->db->rollback(); + throw new RestException(500, 'Error accountancy export : '.implode(',', $accountancyexport->errors)); + } else { + $this->db->commit(); + exit(); + } + } + } +} diff --git a/htdocs/accountancy/tpl/export_journal.tpl.php b/htdocs/accountancy/tpl/export_journal.tpl.php index 8e3c914a6f5..767f95cb252 100644 --- a/htdocs/accountancy/tpl/export_journal.tpl.php +++ b/htdocs/accountancy/tpl/export_journal.tpl.php @@ -19,6 +19,7 @@ */ // $formatexportset must be defined +// $downloadMode =0 for direct download or =1 to download after writing files or =-1 not to download files // Protection to avoid direct call of template if (empty($conf) || !is_object($conf)) { @@ -35,7 +36,7 @@ $siren = getDolGlobalString('MAIN_INFO_SIREN'); $date_export = "_".dol_print_date(dol_now(), '%Y%m%d%H%M%S'); $endaccountingperiod = dol_print_date(dol_now(), '%Y%m%d'); -if (empty($withAttachment)) { +if (empty($downloadMode)) { header('Content-Type: text/csv'); } @@ -70,6 +71,6 @@ if (($accountancyexport->getFormatCode($formatexportset) == 'fec' || $accountanc $completefilename = ($code ? $code."_" : "").($prefix ? $prefix."_" : "").$filename.($nodateexport ? "" : $date_export).".".$format; } -if (empty($withAttachment)) { +if (empty($downloadMode)) { header('Content-Disposition: attachment;filename=' . $completefilename); } diff --git a/htdocs/core/lib/accounting.lib.php b/htdocs/core/lib/accounting.lib.php index 5c1f0b078a3..38a7fefc483 100644 --- a/htdocs/core/lib/accounting.lib.php +++ b/htdocs/core/lib/accounting.lib.php @@ -331,3 +331,56 @@ function getDefaultDatesForTransfer() 'pastmonth' => $pastmonth ); } + +/** + * Get current period of fiscal year + * + * @param DoliDB $db Database handler + * @param stdClass $conf Config + * @param int $from_time [=null] Get current time or set time to find fiscal period + * @return array Period of fiscal year : [date_start, date_end] + */ +function getCurrentPeriodOfFiscalYear($db, $conf, $from_time = null) +{ + $now = dol_now(); + $now_arr = dol_getdate($now); + if ($from_time === null) { + $from_time = $now; + } + $from_db_time = $db->idate($from_time); + + $sql = "SELECT date_start, date_end FROM ".$db->prefix()."accounting_fiscalyear"; + $sql .= " WHERE date_start <= '".$from_db_time."' AND date_end >= '".$from_db_time."'"; + $sql .= $db->order('date_start', 'DESC'); + $sql .= $db->plimit(1); + $res = $db->query($sql); + if ($db->num_rows($res) > 0) { + $obj = $db->fetch_object($res); + + $date_start = $db->jdate($obj->date_start); + $date_end = $db->jdate($obj->date_end); + } else { + $month_start = 1; + $conf_fiscal_month_start = (int) $conf->global->SOCIETE_FISCAL_MONTH_START; + if ($conf_fiscal_month_start >= 1 && $conf_fiscal_month_start <= 12) { + $month_start = $conf_fiscal_month_start; + } + $year_start = $now_arr['year']; + if ($conf_fiscal_month_start > $now_arr['mon']) { + $year_start = $year_start - 1; + } + $year_end = $year_start + 1; + $month_end = $month_start - 1; + if ($month_end < 1) { + $month_end = 12; + $year_end--; + } + $date_start = dol_mktime(0, 0, 0, $month_start, 1, $year_start); + $date_end = dol_get_last_day($year_end, $month_end); + } + + return array( + 'date_start' => $date_start, + 'date_end' => $date_end, + ); +} diff --git a/htdocs/core/lib/functions2.lib.php b/htdocs/core/lib/functions2.lib.php index ce639624f1a..3ed3d03c527 100644 --- a/htdocs/core/lib/functions2.lib.php +++ b/htdocs/core/lib/functions2.lib.php @@ -2668,6 +2668,8 @@ function getModuleDirForApiClass($moduleobject) $moduledirforclass = 'fichinter'; } elseif ($moduleobject == 'mos') { $moduledirforclass = 'mrp'; + } elseif ($moduleobject == 'accounting') { + $moduledirforclass = 'accountancy'; } elseif (in_array($moduleobject, array('products', 'expensereports', 'users', 'tickets', 'boms', 'receptions'))) { $moduledirforclass = preg_replace('/s$/', '', $moduleobject); } diff --git a/htdocs/core/modules/modAccounting.class.php b/htdocs/core/modules/modAccounting.class.php index 7a4ffabd4ab..07591de93a7 100644 --- a/htdocs/core/modules/modAccounting.class.php +++ b/htdocs/core/modules/modAccounting.class.php @@ -57,7 +57,7 @@ class modAccounting extends DolibarrModules $this->picto = 'accountancy'; // Data directories to create when module is enabled - $this->dirs = array('/accounting/temp'); + $this->dirs = array('/accounting/temp', '/accounting/export'); // Config pages $this->config_page_url = array('accounting.php?mainmenu=accountancy&leftmenu=accountancy_admin');