Merge branch '23.0' of git@github.com:Dolibarr/dolibarr.git into 24.0

This commit is contained in:
Laurent Destailleur 2026-08-13 17:16:49 +02:00
commit 72a97f32ff
19 changed files with 406 additions and 345 deletions

View file

@ -136,7 +136,7 @@ class DolibarrApiAccess implements iAuthenticate
if (!getDolGlobalString('API_IN_TOKEN_TABLE')) {
if (isModEnabled('multicompany') && getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE') && defined("DOLENTITY")) {
$sql = "SELECT u.login, u.datec, u.api_key as use_api, u.api_key as api_key, 0 as token_rowid,";
$sql = "SELECT DISTINCT u.login, u.datec, u.api_key as use_api, u.api_key as api_key, 0 as token_rowid,";
$sql .= " u.tms as date_modification,";
$sql .= " gu.entity, gu.entity as token_entity";
$sql .= " FROM ".$this->db->prefix()."user as u";
@ -150,7 +150,7 @@ class DolibarrApiAccess implements iAuthenticate
}
} else {
if (isModEnabled('multicompany') && getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE') && defined("DOLENTITY")) {
$sql = "SELECT u.login, u.datec, u.api_key as use_api, oat.tokenstring as api_key, oat.entity as token_entity, oat.rowid as token_rowid,";
$sql = "SELECT DISTINCT u.login, u.datec, u.api_key as use_api, oat.tokenstring as api_key, oat.entity as token_entity, rowid as token_rowid,";
$sql .= " oat.tms as date_modification,";
$sql .= " gu.entity";
$sql .= " FROM ".$this->db->prefix()."oauth_token AS oat";

View file

@ -1210,8 +1210,10 @@ if ($user->hasRight("holiday", "read")) {
$event->type = 'holiday';
$event->type_picto = 'holiday';
$event->datep = (int) $db->jdate($obj->date_start) + (int) ((empty($obj->halfday) || $obj->halfday == 1 ? 0 : 12) * 60 * 60);
$event->datef = (int) $db->jdate($obj->date_end) + (int) ((empty($obj->halfday) || $obj->halfday == -1 ? 24 : 12) * 60 * 60 - 1);
// date_debut and date_fin are dates without time, so they must be read and rendered in GMT to
// stay independent from the server and user timezones (otherwise the calendar day box is shifted).
$event->datep = (int) $db->jdate($obj->date_start, 'gmt') + (empty($obj->halfday) || $obj->halfday == 1 ? 0 : 12) * 60 * 60;
$event->datef = (int) $db->jdate($obj->date_end, 'gmt') + (empty($obj->halfday) || $obj->halfday == -1 ? 24 : 12) * 60 * 60 - 1;
$event->date_start_in_calendar = $event->datep;
$event->date_end_in_calendar = $event->datef;
@ -1226,14 +1228,14 @@ if ($user->hasRight("holiday", "read")) {
$event->label = $langs->trans("Holiday");
$daycursor = $event->date_start_in_calendar;
$annee = (int) dol_print_date($daycursor, '%Y', 'tzuserrel');
$mois = (int) dol_print_date($daycursor, '%m', 'tzuserrel');
$jour = (int) dol_print_date($daycursor, '%d', 'tzuserrel');
$annee = (int) dol_print_date($daycursor, '%Y', 'gmt');
$mois = (int) dol_print_date($daycursor, '%m', 'gmt');
$jour = (int) dol_print_date($daycursor, '%d', 'gmt');
$daycursorend = $event->date_end_in_calendar;
$anneeend = (int) dol_print_date($daycursorend, '%Y', 'tzuserrel');
$moisend = (int) dol_print_date($daycursorend, '%m', 'tzuserrel');
$jourend = (int) dol_print_date($daycursorend, '%d', 'tzuserrel');
$anneeend = (int) dol_print_date($daycursorend, '%Y', 'gmt');
$moisend = (int) dol_print_date($daycursorend, '%m', 'gmt');
$jourend = (int) dol_print_date($daycursorend, '%d', 'gmt');
// daykey must be date that represent day box in calendar so must be a user time
$daykey = dol_mktime(0, 0, 0, $mois, $jour, $annee, 'gmt');

View file

@ -1149,8 +1149,10 @@ if ($user->hasRight("holiday", "read")) {
$event->type = 'holiday';
$event->type_picto = 'holiday';
$event->datep = $db->jdate($obj->date_start) + (int) ((empty($obj->halfday) || $obj->halfday == 1 ? 0 : 12) * 60 * 60);
$event->datef = $db->jdate($obj->date_end) + (int) ((empty($obj->halfday) || $obj->halfday == -1 ? 24 : 12) * 60 * 60 - 1);
// date_debut and date_fin are dates without time, so they must be read and rendered in GMT to
// stay independent from the server and user timezones (otherwise the calendar day box is shifted).
$event->datep = (int) $db->jdate($obj->date_start, 'gmt') + ((empty($obj->halfday) || $obj->halfday == 1) ? 0 : 12) * 60 * 60;
$event->datef = (int) $db->jdate($obj->date_end, 'gmt') + ((empty($obj->halfday) || $obj->halfday == -1) ? 24 : 12) * 60 * 60 - 1;
$event->date_start_in_calendar = $event->datep;
$event->date_end_in_calendar = $event->datef;
@ -1169,14 +1171,14 @@ if ($user->hasRight("holiday", "read")) {
$daycursor = $event->date_start_in_calendar;
$annee = (int) dol_print_date($daycursor, '%Y', 'tzuserrel');
$mois = (int) dol_print_date($daycursor, '%m', 'tzuserrel');
$jour = (int) dol_print_date($daycursor, '%d', 'tzuserrel');
$annee = (int) dol_print_date($daycursor, '%Y', 'gmt');
$mois = (int) dol_print_date($daycursor, '%m', 'gmt');
$jour = (int) dol_print_date($daycursor, '%d', 'gmt');
$daycursorend = $event->date_end_in_calendar;
$anneeend = (int) dol_print_date($daycursorend, '%Y', 'tzuserrel');
$moisend = (int) dol_print_date($daycursorend, '%m', 'tzuserrel');
$jourend = (int) dol_print_date($daycursorend, '%d', 'tzuserrel');
$anneeend = (int) dol_print_date($daycursorend, '%Y', 'gmt');
$moisend = (int) dol_print_date($daycursorend, '%m', 'gmt');
$jourend = (int) dol_print_date($daycursorend, '%d', 'gmt');
// daykey must be date that represent day box in calendar so must be a user time
$daykey = dol_mktime(0, 0, 0, $mois, $jour, $annee, 'gmt');

View file

@ -3446,6 +3446,7 @@ class Commande extends CommonOrder
$sql .= " fk_soc=".(isset($this->socid) ? ((int) $this->socid) : "null").",";
$sql .= " date_commande=".(strval($this->date_commande) != '' ? "'".$this->db->idate($this->date_commande)."'" : 'null').",";
$sql .= " date_valid=".(strval($this->date_validation) != '' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
$sql .= " total_tva=".(isset($this->total_tva) ? ((float) $this->total_tva) : "null").",";
$sql .= " localtax1=".(isset($this->total_localtax1) ? ((float) $this->total_localtax1) : "null").",";
$sql .= " localtax2=".(isset($this->total_localtax2) ? ((float) $this->total_localtax2) : "null").",";
@ -3456,7 +3457,7 @@ class Commande extends CommonOrder
$sql .= " fk_user_valid=".((isset($this->user_validation_id) && $this->user_validation_id > 0) ? ((int) $this->user_validation_id) : "null").",";
$sql .= " fk_projet=".(isset($this->fk_project) ? ((int) $this->fk_project) : "null").",";
$sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? ((int) $this->cond_reglement_id) : "null").",";
$sql .= " deposit_percent=".(!empty($this->deposit_percent) ? ((float) $this->deposit_percent) : "null").",";
$sql .= " deposit_percent=".(!empty($this->deposit_percent) ? "'".$this->db->escape($this->deposit_percent)."'" : "null").",";
$sql .= " fk_mode_reglement=".(isset($this->mode_reglement_id) ? ((int) $this->mode_reglement_id) : "null").",";
$sql .= " date_livraison=".(strval($this->delivery_date) != '' ? "'".$this->db->idate($this->delivery_date)."'" : 'null').",";
$sql .= " fk_shipping_method=".(isset($this->shipping_method_id) ? ((int) $this->shipping_method_id) : "null").",";

View file

@ -73,6 +73,11 @@ class Facture extends CommonInvoice
*/
public $element = 'facture';
/**
* @var string Prefix used to build trigger event names in generic CommonObject methods (BILL_MODIFY, ...)
*/
public $TRIGGER_PREFIX = 'BILL';
/**
* @var string Name of table without prefix where object is stored
*/

View file

@ -277,24 +277,31 @@ if ($action == 'confirm_deletefile' && $confirm == 'yes' && !empty($permissionto
}
if (empty($error) && $filenamefrom != $filenameto) {
// For backward compatibility, the file to rename may be stored into an old path (see PRODUCT_USE_OLD_PATH_FOR_PHOTO).
// Upload and delete actions already fall back on $upload_dirold, so the rename action must do the same.
$dirforfile = $upload_dir;
if (!empty($upload_dirold) && !dol_is_file($upload_dir.'/'.$filenamefrom) && dol_is_file($upload_dirold.'/'.$filenamefrom)) {
$dirforfile = $upload_dirold;
}
// Security:
// Disallow file with some extensions. We rename them.
// Because if we put the documents directory into a directory inside web root (very bad), this allows to execute on demand arbitrary code.
if (isAFileWithExecutableContent($filenameto) && !getDolGlobalString('MAIN_DOCUMENT_IS_OUTSIDE_WEBROOT_SO_NOEXE_NOT_REQUIRED')) {
// $upload_dir ends with a slash, so be must be sure the medias dir to compare to ends with slash too.
// $dirforfile ends with a slash, so be must be sure the medias dir to compare to ends with slash too.
$publicmediasdirwithslash = $conf->medias->multidir_output[$conf->entity];
if (!preg_match('/\/$/', $publicmediasdirwithslash)) {
$publicmediasdirwithslash .= '/';
}
if (strpos($upload_dir, $publicmediasdirwithslash) !== 0) { // We never add .noexe on files into media directory
if (strpos($dirforfile, $publicmediasdirwithslash) !== 0) { // We never add .noexe on files into media directory
$filenameto .= '.noexe';
}
}
if ($filenamefrom && $filenameto) {
$srcpath = $upload_dir.'/'.$filenamefrom;
$destpath = $upload_dir.'/'.$filenameto;
$srcpath = $dirforfile.'/'.$filenamefrom;
$destpath = $dirforfile.'/'.$filenameto;
/* disabled. Too many bugs. All files of an object must remain into directory of object. link with event should be done in llx_ecm_files with column agenda_id.
if ($modulepart == "ticket" && !dol_is_file($srcpath)) {
$srcbis = $conf->agenda->dir_output.'/'.GETPOST('section_dir').$filenamefrom;
@ -305,7 +312,7 @@ if ($action == 'confirm_deletefile' && $confirm == 'yes' && !empty($permissionto
}*/
$reshook = $hookmanager->initHooks(array('actionlinkedfiles'));
$parameters = array('filenamefrom' => $filenamefrom, 'filenameto' => $filenameto, 'upload_dir' => $upload_dir);
$parameters = array('filenamefrom' => $filenamefrom, 'filenameto' => $filenameto, 'upload_dir' => $dirforfile);
$reshook = $hookmanager->executeHooks('renameUploadedFile', $parameters, $object);
if (empty($reshook)) {

View file

@ -439,7 +439,8 @@ function completeFileArrayWithDatabaseInfo(&$filearray, $relativedir, $object =
$filearrayindatabase = dol_dir_list_in_database(rtrim($relativedir, "/\\"), '', null, 'name', SORT_ASC, 0, '', $object);
global $modulepart;
if ($modulepart == 'produit' && getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
// Note: $modulepart is 'product' when set by product/document.php, but 'produit' in some other contexts, so we accept both.
if (in_array($modulepart, array('produit', 'product')) && getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
// TODO Remove this when PRODUCT_USE_OLD_PATH_FOR_PHOTO will be removed
global $object;
if (!empty($object->id)) {
@ -452,7 +453,8 @@ function completeFileArrayWithDatabaseInfo(&$filearray, $relativedir, $object =
$relativedirold = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $upload_dirold);
$relativedirold = ltrim($relativedirold, "/\\");
$filearrayindatabase = array_merge($filearrayindatabase, dol_dir_list_in_database($relativedirold, '', null, 'name', SORT_ASC));
// Note: $object must be provided so the entity filter matches the one used to forge $upload_dirold (multicompany)
$filearrayindatabase = array_merge($filearrayindatabase, dol_dir_list_in_database($relativedirold, '', null, 'name', SORT_ASC, 0, '', $object));
}
} elseif ($modulepart == 'ticket') {
foreach ($filearray as $key => $val) {

View file

@ -157,7 +157,7 @@ function rebuildObjectClass($destdir, $module, $objectname, $newmask, $readdir =
$texttoinsert .= ' "index" => "'.(int) $val['index'].'",';
}
if (!empty($val['foreignkey'])) {
$texttoinsert .= ' "foreignkey" => "'.(int) $val['foreignkey'].'",';
$texttoinsert .= ' "foreignkey" => "'.dol_escape_php($val['foreignkey']).'",';
}
if (!empty($val['searchall'])) {
$texttoinsert .= ' "searchall" => "'.(int) $val['searchall'].'",';

View file

@ -565,6 +565,8 @@ class pdf_eratosthene extends ModelePDFCommandes
$pdf_sub_options['titleshowuponpdf'] = 1;
$pdf_sub_options['titleshowtotalexludingvatonpdf'] = 1;
$hidenextline = 0;
for ($i = 0; $i < $nblines; $i++) {
$linePosition = $i + 1;
$curY = $nexY;
@ -574,6 +576,7 @@ class pdf_eratosthene extends ModelePDFCommandes
if ($object->lines[$i]->special_code == SUBTOTALS_SPECIAL_CODE) {
$level = $object->lines[$i]->qty;
if ($sub_options) {
$hidenextline = 0;
if (isset($sub_options['titleshowuponpdf'])) {
$pdf_sub_options['titleshowuponpdf'] = isset($pdf_sub_options['titleshowuponpdf']) && $pdf_sub_options['titleshowuponpdf'] < $level ? $pdf_sub_options['titleshowuponpdf'] : $level;
} elseif (isset($pdf_sub_options['titleshowuponpdf']) && abs($level) <= $pdf_sub_options['titleshowuponpdf']) {
@ -594,167 +597,178 @@ class pdf_eratosthene extends ModelePDFCommandes
}
}
if (($curY + 6) > ($this->page_hauteur - $heightforfooter) || isset($sub_options['titleforcepagebreak']) && !($pdf->getNumPages() == 1 && $curY == $tab_top + $this->tabTitleHeight)) {
$object->lines[$i]->pagebreak = true;
if ($object->lines[$i]->special_code == SUBTOTALS_SPECIAL_CODE && isset($sub_options['subtotalshowtotalexludingvatonpdf']) && getDolGlobalString('SUBTOTAL_HIDE_LINES_UNDER_TITLE')) { // TODO Use $sub_options['titlehidelinesundertitle'] instead of SUBTOTAL_HIDE_LINES_UNDER_TITLE
$hidenextline = 0;
$pdf_sub_options = array();
$pdf_sub_options['titleshowuponpdf'] = 1;
$pdf_sub_options['titleshowtotalexludingvatonpdf'] = 1;
}
// in First Check line page break and add page if needed
if (isset($object->lines[$i]->pagebreak) && $object->lines[$i]->pagebreak) {
// New page
$pdf->AddPage();
if (!empty($tplidx)) {
$pdf->useTemplate($tplidx);
//var_dump($i, $sub_options, $hidenextline);
if ($hidenextline) {
$linePosition--;
} else {
if (($curY + 6) > ($this->page_hauteur - $heightforfooter) || isset($sub_options['titleforcepagebreak']) && !($pdf->getNumPages() == 1 && $curY == $tab_top + $this->tabTitleHeight)) {
$object->lines[$i]->pagebreak = true;
}
$pdf->setPage($pdf->getNumPages());
$nexY = $curY = $tab_top_newpage;
}
$this->resetAfterColsLinePositionsData($nexY, $pdf->getPage());
$pdf->SetFont('', '', $default_font_size - 1); // Into loop to work with multipage
$pdf->SetTextColor(0, 0, 0);
// Define size of image if we need it
$imglinesize = array();
if (!empty($realpatharray[$i])) {
$imglinesize = pdf_getSizeForImage($realpatharray[$i]);
}
$pdf->setTopMargin($tab_top_newpage);
$pdf->setPageOrientation('', true, $heightforfooter); // The only function to edit the bottom margin of current page to set it.
$pageposbefore = $pdf->getPage();
$curYBefore = $curY;
// Allows data in the first page if description is long enough to break in multiples pages
$showpricebeforepagebreak = getDolGlobalInt('MAIN_PDF_DATA_ON_FIRST_PAGE');
$posYAfterImage = 0;
if ($this->getColumnStatus('photo')) {
// We start with Photo of product line
$imageTopMargin = 1;
if (isset($imglinesize['width']) && isset($imglinesize['height']) && ($curY + $imageTopMargin + $imglinesize['height']) > ($this->page_hauteur - $heightforfooter)) { // If photo too high, we moved completely on new page
$pdf->AddPage('', '', true);
// in First Check line page break and add page if needed
if (isset($object->lines[$i]->pagebreak) && $object->lines[$i]->pagebreak) {
// New page
$pdf->AddPage();
if (!empty($tplidx)) {
$pdf->useTemplate($tplidx);
}
$pdf->setPage($pageposbefore + 1);
$pdf->setPageOrientation('', true, $heightforfooter); // The only function to edit the bottom margin of current page to set it.
$curY = $tab_top_newpage;
$showpricebeforepagebreak = 0;
$pdf->setPage($pdf->getNumPages());
$nexY = $curY = $tab_top_newpage;
}
// I remove the line commented below because it probably uselesss (or bug source) no need to change bottom margin because we have checked image fit
//$pdf->setPageOrientation('', 0, $heightforfooter + $heightforfreetext); // The only function to edit the bottom margin of current page to set it.
$this->resetAfterColsLinePositionsData($nexY, $pdf->getPage());
// @phan-suppress-next-line PhanTypeMismatchProperty
if (!empty($this->cols['photo']) && isset($imglinesize['width']) && isset($imglinesize['height'])) {
$pdf->Image($realpatharray[$i], $this->getColumnContentXStart('photo'), $curY + $imageTopMargin, $imglinesize['width'], $imglinesize['height'], '', '', '', 2, 300); // Use 300 dpi
// $pdf->Image does not increase value return by getY, so we save it manually
$posYAfterImage = $curY + $imglinesize['height'];
$this->setAfterColsLinePositionsData('photo', $posYAfterImage, $pdf->getPage());
$pdf->SetFont('', '', $default_font_size - 1); // Into loop to work with multipage
$pdf->SetTextColor(0, 0, 0);
// Define size of image if we need it
$imglinesize = array();
if (!empty($realpatharray[$i])) {
$imglinesize = pdf_getSizeForImage($realpatharray[$i]);
}
}
// restore Page orientation for text
$pdf->setPageOrientation('', true, $heightforfooter); // The only function to edit the bottom margin of current page to set it.
$pdf->setTopMargin($tab_top_newpage);
$pdf->setPageOrientation('', true, $heightforfooter); // The only function to edit the bottom margin of current page to set it.
$pageposbefore = $pdf->getPage();
$curYBefore = $curY;
// Description of product line
if ($this->getColumnStatus('desc')) {
if ($object->lines[$i]->special_code != SUBTOTALS_SPECIAL_CODE) {
$this->printColDescContent($pdf, $curY, 'desc', $object, $i, $outputlangs, $hideref, $hidedesc);
$this->setAfterColsLinePositionsData('desc', $pdf->GetY(), $pdf->getPage());
} else {
$bg_color = colorStringToArray(getDolGlobalString("SUBTOTAL_BACK_COLOR_LEVEL_".abs($object->lines[$i]->qty), 'ffffff'));
pdf_render_subtotals($pdf, $this, $curY, $object, $i, $outputlangs, $hideref, $hidedesc, $bg_color, true, true);
// Allows data in the first page if description is long enough to break in multiples pages
$showpricebeforepagebreak = getDolGlobalInt('MAIN_PDF_DATA_ON_FIRST_PAGE');
$posYAfterImage = 0;
if ($this->getColumnStatus('photo')) {
// We start with Photo of product line
$imageTopMargin = 1;
if (isset($imglinesize['width']) && isset($imglinesize['height']) && ($curY + $imageTopMargin + $imglinesize['height']) > ($this->page_hauteur - $heightforfooter)) { // If photo too high, we moved completely on new page
$pdf->AddPage('', '', true);
if (!empty($tplidx)) {
$pdf->useTemplate($tplidx);
}
$pdf->setPage($pageposbefore + 1);
$pdf->setPageOrientation('', true, $heightforfooter); // The only function to edit the bottom margin of current page to set it.
$curY = $tab_top_newpage;
$showpricebeforepagebreak = 0;
}
// I remove the line commented below because it probably uselesss (or bug source) no need to change bottom margin because we have checked image fit
//$pdf->setPageOrientation('', 0, $heightforfooter + $heightforfreetext); // The only function to edit the bottom margin of current page to set it.
// @phan-suppress-next-line PhanTypeMismatchProperty
if (!empty($this->cols['photo']) && isset($imglinesize['width']) && isset($imglinesize['height'])) {
$pdf->Image($realpatharray[$i], $this->getColumnContentXStart('photo'), $curY + $imageTopMargin, $imglinesize['width'], $imglinesize['height'], '', '', '', 2, 300); // Use 300 dpi
// $pdf->Image does not increase value return by getY, so we save it manually
$posYAfterImage = $curY + $imglinesize['height'];
$this->setAfterColsLinePositionsData('photo', $posYAfterImage, $pdf->getPage());
}
}
}
$afterPosData = $this->getMaxAfterColsLinePositionsData();
$pdf->setPage($pageposbefore);
$pdf->setTopMargin($this->marge_haute);
$pdf->setPageOrientation('', false, $heightforfooter); // The only function to edit the bottom margin of current page to set it.
// restore Page orientation for text
$pdf->setPageOrientation('', true, $heightforfooter); // The only function to edit the bottom margin of current page to set it.
// We suppose that a too long description or photo were moved completely on next page
if ($afterPosData['page'] > $pageposbefore && (empty($showpricebeforepagebreak) || ($curY + 4) > ($this->page_hauteur - $heightforfooter))) {
$pdf->setPage($afterPosData['page']);
$curY = $tab_top_newpage;
}
$pdf->SetFont('', '', $default_font_size - 1); // We reposition the default font
// Line position
if ($this->getColumnStatus('position')) {
$this->printStdColumnContent($pdf, $curY, 'position', strval($linePosition));
}
// VAT Rate
if ($this->getColumnStatus('vat') && $object->lines[$i]->special_code != SUBTOTALS_SPECIAL_CODE) {
$vat_rate = pdf_getlinevatrate($object, $i, $outputlangs, $hidedetails);
$this->printStdColumnContent($pdf, $curY, 'vat', $vat_rate);
}
// Unit price before discount
if ($this->getColumnStatus('subprice') && $object->lines[$i]->special_code != SUBTOTALS_SPECIAL_CODE && isset($pdf_sub_options['titleshowuponpdf'])) {
$up_excl_tax = pdf_getlineupexcltax($object, $i, $outputlangs, $hidedetails);
$this->printStdColumnContent($pdf, $curY, 'subprice', $up_excl_tax);
}
// Quantity
// Enough for 6 chars
if ($this->getColumnStatus('qty') && $object->lines[$i]->special_code != SUBTOTALS_SPECIAL_CODE) {
$qty = pdf_getlineqty($object, $i, $outputlangs, $hidedetails);
$this->printStdColumnContent($pdf, $curY, 'qty', $qty);
}
// Unit
if ($this->getColumnStatus('unit') && $object->lines[$i]->special_code != SUBTOTALS_SPECIAL_CODE) {
$unit = pdf_getlineunit($object, $i, $outputlangs, $hidedetails);
$this->printStdColumnContent($pdf, $curY, 'unit', $unit);
}
// Discount on line
if ($this->getColumnStatus('discount') && $object->lines[$i]->remise_percent && $object->lines[$i]->special_code != SUBTOTALS_SPECIAL_CODE) {
$remise_percent = pdf_getlineremisepercent($object, $i, $outputlangs, $hidedetails);
$this->printStdColumnContent($pdf, $curY, 'discount', $remise_percent);
}
// Total excl tax line (HT)
if ($this->getColumnStatus('totalexcltax')) {
if ($object->lines[$i]->special_code != SUBTOTALS_SPECIAL_CODE && isset($pdf_sub_options['titleshowtotalexludingvatonpdf'])) {
$total_excl_tax = pdf_getlinetotalexcltax($object, $i, $outputlangs, $hidedetails);
$this->printStdColumnContent($pdf, $curY, 'totalexcltax', $total_excl_tax);
} elseif ($object->lines[$i]->qty < 0 && isset($sub_options['subtotalshowtotalexludingvatonpdf'])) {
if (isModEnabled('multicurrency') && $object->multicurrency_code != getDolCurrency()) {
$total_excl_tax = $object->getSubtotalLineMulticurrencyAmount($object->lines[$i]);
// Description of product line
if ($this->getColumnStatus('desc')) {
if ($object->lines[$i]->special_code != SUBTOTALS_SPECIAL_CODE) {
$this->printColDescContent($pdf, $curY, 'desc', $object, $i, $outputlangs, $hideref, $hidedesc);
$this->setAfterColsLinePositionsData('desc', $pdf->GetY(), $pdf->getPage());
} else {
$total_excl_tax = $object->getSubtotalLineAmount($object->lines[$i]);
}
$this->printStdColumnContent($pdf, $curY, 'totalexcltax', $total_excl_tax);
}
}
// Total with tax line (TTC)
if ($this->getColumnStatus('totalincltax')) {
$total_incl_tax = pdf_getlinetotalwithtax($object, $i, $outputlangs, $hidedetails);
$this->printStdColumnContent($pdf, $curY, 'totalincltax', $total_incl_tax);
}
// Extrafields
if (!empty($object->lines[$i]->array_options)) {
foreach ($object->lines[$i]->array_options as $extrafieldColKey => $extrafieldValue) {
if ($this->getColumnStatus($extrafieldColKey)) {
$extrafieldValue = $this->getExtrafieldContent($object->lines[$i], $extrafieldColKey, $outputlangs);
$this->printStdColumnContent($pdf, $curY, $extrafieldColKey, $extrafieldValue);
$this->setAfterColsLinePositionsData('options_'.$extrafieldColKey, $pdf->GetY(), $pdf->getPage());
$bg_color = colorStringToArray(getDolGlobalString("SUBTOTAL_BACK_COLOR_LEVEL_".abs($object->lines[$i]->qty), 'ffffff'));
pdf_render_subtotals($pdf, $this, $curY, $object, $i, $outputlangs, $hideref, $hidedesc, $bg_color, true, true);
}
}
}
$afterPosData = $this->getMaxAfterColsLinePositionsData();
$parameters = array(
$afterPosData = $this->getMaxAfterColsLinePositionsData();
$pdf->setPage($pageposbefore);
$pdf->setTopMargin($this->marge_haute);
$pdf->setPageOrientation('', false, $heightforfooter); // The only function to edit the bottom margin of current page to set it.
// We suppose that a too long description or photo were moved completely on next page
if ($afterPosData['page'] > $pageposbefore && (empty($showpricebeforepagebreak) || ($curY + 4) > ($this->page_hauteur - $heightforfooter))) {
$pdf->setPage($afterPosData['page']);
$curY = $tab_top_newpage;
}
$pdf->SetFont('', '', $default_font_size - 1); // We reposition the default font
// Line position
if ($this->getColumnStatus('position')) {
$this->printStdColumnContent($pdf, $curY, 'position', strval($linePosition));
}
// VAT Rate
if ($this->getColumnStatus('vat') && $object->lines[$i]->special_code != SUBTOTALS_SPECIAL_CODE) {
$vat_rate = pdf_getlinevatrate($object, $i, $outputlangs, $hidedetails);
$this->printStdColumnContent($pdf, $curY, 'vat', $vat_rate);
}
// Unit price before discount
if ($this->getColumnStatus('subprice') && $object->lines[$i]->special_code != SUBTOTALS_SPECIAL_CODE && isset($pdf_sub_options['titleshowuponpdf'])) {
$up_excl_tax = pdf_getlineupexcltax($object, $i, $outputlangs, $hidedetails);
$this->printStdColumnContent($pdf, $curY, 'subprice', $up_excl_tax);
}
// Quantity
// Enough for 6 chars
if ($this->getColumnStatus('qty') && $object->lines[$i]->special_code != SUBTOTALS_SPECIAL_CODE) {
$qty = pdf_getlineqty($object, $i, $outputlangs, $hidedetails);
$this->printStdColumnContent($pdf, $curY, 'qty', $qty);
}
// Unit
if ($this->getColumnStatus('unit') && $object->lines[$i]->special_code != SUBTOTALS_SPECIAL_CODE) {
$unit = pdf_getlineunit($object, $i, $outputlangs, $hidedetails);
$this->printStdColumnContent($pdf, $curY, 'unit', $unit);
}
// Discount on line
if ($this->getColumnStatus('discount') && $object->lines[$i]->remise_percent && $object->lines[$i]->special_code != SUBTOTALS_SPECIAL_CODE) {
$remise_percent = pdf_getlineremisepercent($object, $i, $outputlangs, $hidedetails);
$this->printStdColumnContent($pdf, $curY, 'discount', $remise_percent);
}
// Total excl tax line (HT)
if ($this->getColumnStatus('totalexcltax')) {
if ($object->lines[$i]->special_code != SUBTOTALS_SPECIAL_CODE && isset($pdf_sub_options['titleshowtotalexludingvatonpdf'])) {
$total_excl_tax = pdf_getlinetotalexcltax($object, $i, $outputlangs, $hidedetails);
$this->printStdColumnContent($pdf, $curY, 'totalexcltax', $total_excl_tax);
} elseif ($object->lines[$i]->qty < 0 && isset($sub_options['subtotalshowtotalexludingvatonpdf'])) {
if (isModEnabled('multicurrency') && $object->multicurrency_code != getDolCurrency()) {
$total_excl_tax = $object->getSubtotalLineMulticurrencyAmount($object->lines[$i]);
} else {
$total_excl_tax = $object->getSubtotalLineAmount($object->lines[$i]);
}
$this->printStdColumnContent($pdf, $curY, 'totalexcltax', $total_excl_tax);
}
}
// Total with tax line (TTC)
if ($this->getColumnStatus('totalincltax')) {
$total_incl_tax = pdf_getlinetotalwithtax($object, $i, $outputlangs, $hidedetails);
$this->printStdColumnContent($pdf, $curY, 'totalincltax', $total_incl_tax);
}
// Extrafields
if (!empty($object->lines[$i]->array_options)) {
foreach ($object->lines[$i]->array_options as $extrafieldColKey => $extrafieldValue) {
if ($this->getColumnStatus($extrafieldColKey)) {
$extrafieldValue = $this->getExtrafieldContent($object->lines[$i], $extrafieldColKey, $outputlangs);
$this->printStdColumnContent($pdf, $curY, $extrafieldColKey, $extrafieldValue);
$this->setAfterColsLinePositionsData('options_'.$extrafieldColKey, $pdf->GetY(), $pdf->getPage());
}
}
}
$afterPosData = $this->getMaxAfterColsLinePositionsData();
$parameters = array(
'object' => $object,
'i' => $i,
'pdf' => & $pdf,
@ -762,9 +776,9 @@ class pdf_eratosthene extends ModelePDFCommandes
'nexY' => & $afterPosData['y'], // for backward module hook compatibility Y will be accessible by $object->getMaxAfterColsLinePositionsData()
'outputlangs' => $outputlangs,
'hidedetails' => $hidedetails
);
$reshook = $hookmanager->executeHooks('printPDFline', $parameters, $this); // Note that $object may have been modified by hook
);
$reshook = $hookmanager->executeHooks('printPDFline', $parameters, $this); // Note that $object may have been modified by hook
}
// Collection of totals by value of vat in $this->tva["rate"] = total_tva
if (isModEnabled("multicurrency") && $object->multicurrency_tx != 1) {
@ -821,16 +835,22 @@ class pdf_eratosthene extends ModelePDFCommandes
}
$this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')] = array('vatrate' => $vatrate, 'vatcode' => $vatcode, 'amount' => $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['amount'] + $tvaligne);
$afterPosData = $this->getMaxAfterColsLinePositionsData();
$pdf->setPage($afterPosData['page']);
$nexY = $afterPosData['y'];
// Add line
if (getDolGlobalString('MAIN_PDF_DASH_BETWEEN_LINES') && $i < ($nblines - 1) && $afterPosData['y'] < $this->page_hauteur - $heightforfooter - 5) {
$pdf->SetLineStyle(array('dash' => '1,1', 'color' => array(80, 80, 80)));
//$pdf->SetDrawColor(190,190,200);
$pdf->line($this->marge_gauche, $nexY, $this->page_largeur - $this->marge_droite, $nexY);
$pdf->SetLineStyle(array('dash' => 0));
if (!$hidenextline) {
$afterPosData = $this->getMaxAfterColsLinePositionsData();
$pdf->setPage($afterPosData['page']);
$nexY = $afterPosData['y'];
// Add line
if (getDolGlobalString('MAIN_PDF_DASH_BETWEEN_LINES') && $i < ($nblines - 1) && $afterPosData['y'] < $this->page_hauteur - $heightforfooter - 5) {
$pdf->SetLineStyle(array('dash' => '1,1', 'color' => array(80, 80, 80)));
//$pdf->SetDrawColor(190,190,200);
$pdf->line($this->marge_gauche, $nexY, $this->page_largeur - $this->marge_droite, $nexY);
$pdf->SetLineStyle(array('dash' => 0));
}
}
if ($object->lines[$i]->special_code == SUBTOTALS_SPECIAL_CODE && (isset($sub_options['titleshowuponpdf']) || isset($sub_options['titleshowtotalexludingvatonpdf'])) && getDolGlobalString('SUBTOTAL_HIDE_LINES_UNDER_TITLE')) { // TODO Use $sub_options['titlehidelinesundertitle'] instead of SUBTOTAL_HIDE_LINES_UNDER_TITLE
$hidenextline = 1;
}
$nexY += 0; // Add space between lines

View file

@ -715,6 +715,8 @@ class pdf_sponge extends ModelePDFFactures
$pdf_sub_options['titleshowuponpdf'] = 1;
$pdf_sub_options['titleshowtotalexludingvatonpdf'] = 1;
$hidenextline = 0;
for ($i = 0; $i < $nblines; $i++) {
$linePosition = $i + 1;
$curY = $nexY;
@ -724,6 +726,7 @@ class pdf_sponge extends ModelePDFFactures
if ($object->lines[$i]->special_code == SUBTOTALS_SPECIAL_CODE) {
$level = $object->lines[$i]->qty;
if ($sub_options) {
$hidenextline = 0;
if (isset($sub_options['titleshowuponpdf'])) {
$pdf_sub_options['titleshowuponpdf'] = isset($pdf_sub_options['titleshowuponpdf']) && $pdf_sub_options['titleshowuponpdf'] < $level ? $pdf_sub_options['titleshowuponpdf'] : $level;
} elseif (isset($pdf_sub_options['titleshowuponpdf']) && abs($level) <= $pdf_sub_options['titleshowuponpdf']) {
@ -744,172 +747,182 @@ class pdf_sponge extends ModelePDFFactures
}
}
if (($curY + 6) > ($this->page_hauteur - $this->heightforfooter) || isset($sub_options['titleforcepagebreak']) && !($pdf->getNumPages() == 1 && $curY == $this->tab_top + $this->tabTitleHeight)) {
$object->lines[$i]->pagebreak = true;
if ($object->lines[$i]->special_code == SUBTOTALS_SPECIAL_CODE && isset($sub_options['subtotalshowtotalexludingvatonpdf']) && getDolGlobalString('SUBTOTAL_HIDE_LINES_UNDER_TITLE')) { // TODO Use $sub_options['titlehidelinesundertitle'] instead of SUBTOTAL_HIDE_LINES_UNDER_TITLE
$hidenextline = 0;
$pdf_sub_options = array();
$pdf_sub_options['titleshowuponpdf'] = 1;
$pdf_sub_options['titleshowtotalexludingvatonpdf'] = 1;
}
// in First Check line page break and add page if needed
if (isset($object->lines[$i]->pagebreak) && $object->lines[$i]->pagebreak) {
// New page
$pdf->AddPage();
if (!empty($tplidx)) {
$pdf->useTemplate($tplidx);
if ($hidenextline) {
$linePosition--;
} else {
if (($curY + 6) > ($this->page_hauteur - $this->heightforfooter) || isset($sub_options['titleforcepagebreak']) && !($pdf->getNumPages() == 1 && $curY == $this->tab_top + $this->tabTitleHeight)) {
$object->lines[$i]->pagebreak = true;
}
$pdf->setPage($pdf->getNumPages());
$nexY = $curY = $this->tab_top_newpage;
}
$this->resetAfterColsLinePositionsData($nexY, $pdf->getPage());
$pdf->SetFont('', '', $default_font_size - 1); // Into loop to work with multipage
$pdf->SetTextColor(0, 0, 0);
// Define size of image if we need it
$imglinesize = array();
if (!empty($realpatharray[$i])) {
$imglinesize = pdf_getSizeForImage($realpatharray[$i]);
}
$pdf->setTopMargin($this->tab_top_newpage);
$pdf->setPageOrientation('', true, $this->heightforfooter);
$pageposbefore = $pdf->getPage();
$curYBefore = $curY;
// Allows data in the first page if description is long enough to break in multiples pages
$showpricebeforepagebreak = getDolGlobalInt('MAIN_PDF_DATA_ON_FIRST_PAGE');
if ($this->getColumnStatus('photo')) {
// We start with Photo of product line
$imageTopMargin = 1;
if (isset($imglinesize['width']) && isset($imglinesize['height']) && ($curY + $imageTopMargin + $imglinesize['height']) > ($this->page_hauteur - $this->heightforfooter)) { // If photo too high, we moved completely on new page
$pdf->AddPage('', '', true);
// in First Check line page break and add page if needed
if (isset($object->lines[$i]->pagebreak) && $object->lines[$i]->pagebreak) {
// New page
$pdf->AddPage();
if (!empty($tplidx)) {
$pdf->useTemplate($tplidx);
}
$pdf->setPage($pageposbefore + 1);
$pdf->setPageOrientation('', true, $this->heightforfooter); // The only function to edit the bottom margin of current page to set it.
$curY = $this->tab_top_newpage;
$showpricebeforepagebreak = 0;
$pdf->setPage($pdf->getNumPages());
$nexY = $curY = $this->tab_top_newpage;
}
$pdf->setPageOrientation('', false, $this->heightforfooter + $this->heightforfreetext); // The only function to edit the bottom margin of current page to set it.
// @phan-suppress-next-line PhanTypeMismatchProperty
if (!empty($this->cols['photo']) && isset($imglinesize['width']) && isset($imglinesize['height'])) {
$pdf->Image($realpatharray[$i], $this->getColumnContentXStart('photo'), $curY + $imageTopMargin, $imglinesize['width'], $imglinesize['height'], '', '', '', 2, 300); // Use 300 dpi
// $pdf->Image does not increase value return by getY, so we save it manually
$posYAfterImage = $curY + $imglinesize['height'];
$this->resetAfterColsLinePositionsData($nexY, $pdf->getPage());
$this->setAfterColsLinePositionsData('photo', $posYAfterImage, $pdf->getPage());
$pdf->SetFont('', '', $default_font_size - 1); // Into loop to work with multipage
$pdf->SetTextColor(0, 0, 0);
// Define size of image if we need it
$imglinesize = array();
if (!empty($realpatharray[$i])) {
$imglinesize = pdf_getSizeForImage($realpatharray[$i]);
}
}
// restore Page orientation for text
$pdf->setPageOrientation('', true, $this->heightforfooter); // The only function to edit the bottom margin of current page to set it.
$pdf->setTopMargin($this->tab_top_newpage);
$pdf->setPageOrientation('', true, $this->heightforfooter);
$pageposbefore = $pdf->getPage();
$curYBefore = $curY;
// Description of product line
if ($this->getColumnStatus('desc')) {
if ($object->lines[$i]->special_code != SUBTOTALS_SPECIAL_CODE) {
$this->printColDescContent($pdf, $curY, 'desc', $object, $i, $outputlangs, $hideref, $hidedesc);
$this->setAfterColsLinePositionsData('desc', $pdf->GetY(), $pdf->getPage());
} else {
$bg_color = colorStringToArray(getDolGlobalString("SUBTOTAL_BACK_COLOR_LEVEL_".abs($object->lines[$i]->qty), 'ffffff'));
pdf_render_subtotals($pdf, $this, $curY, $object, $i, $outputlangs, $hideref, $hidedesc, $bg_color, true, true);
// Allows data in the first page if description is long enough to break in multiples pages
$showpricebeforepagebreak = getDolGlobalInt('MAIN_PDF_DATA_ON_FIRST_PAGE');
if ($this->getColumnStatus('photo')) {
// We start with Photo of product line
$imageTopMargin = 1;
if (isset($imglinesize['width']) && isset($imglinesize['height']) && ($curY + $imageTopMargin + $imglinesize['height']) > ($this->page_hauteur - $this->heightforfooter)) { // If photo too high, we moved completely on new page
$pdf->AddPage('', '', true);
if (!empty($tplidx)) {
$pdf->useTemplate($tplidx);
}
$pdf->setPage($pageposbefore + 1);
$pdf->setPageOrientation('', true, $this->heightforfooter); // The only function to edit the bottom margin of current page to set it.
$curY = $this->tab_top_newpage;
$showpricebeforepagebreak = 0;
}
$pdf->setPageOrientation('', false, $this->heightforfooter + $this->heightforfreetext); // The only function to edit the bottom margin of current page to set it.
// @phan-suppress-next-line PhanTypeMismatchProperty
if (!empty($this->cols['photo']) && isset($imglinesize['width']) && isset($imglinesize['height'])) {
$pdf->Image($realpatharray[$i], $this->getColumnContentXStart('photo'), $curY + $imageTopMargin, $imglinesize['width'], $imglinesize['height'], '', '', '', 2, 300); // Use 300 dpi
// $pdf->Image does not increase value return by getY, so we save it manually
$posYAfterImage = $curY + $imglinesize['height'];
$this->setAfterColsLinePositionsData('photo', $posYAfterImage, $pdf->getPage());
}
}
}
// restore Page orientation for text
$pdf->setPageOrientation('', true, $this->heightforfooter); // The only function to edit the bottom margin of current page to set it.
$afterPosData = $this->getMaxAfterColsLinePositionsData();
$pdf->setPage($pageposbefore);
$pdf->setTopMargin($this->marge_haute);
$curY = $curYBefore;
$pdf->setPageOrientation('', false, $this->heightforfooter); // The only function to edit the bottom margin of current page to set it.
// We suppose that a too long description or photo were moved completely on next page
if ($afterPosData['page'] > $pageposbefore && (empty($showpricebeforepagebreak) || ($curY + 4) > ($this->page_hauteur - $this->heightforfooter))) {
$pdf->setPage($afterPosData['page']);
$curY = $this->tab_top_newpage;
}
$pdf->SetFont('', '', $default_font_size - 1); // We reposition the default font
// Line position
if ($this->getColumnStatus('position')) {
$this->printStdColumnContent($pdf, $curY, 'position', strval($linePosition));
}
// VAT Rate
if ($this->getColumnStatus('vat') && $object->lines[$i]->special_code != SUBTOTALS_SPECIAL_CODE) {
$vat_rate = pdf_getlinevatrate($object, $i, $outputlangs, $hidedetails);
$this->printStdColumnContent($pdf, $curY, 'vat', $vat_rate);
}
// Unit price before discount
if ($this->getColumnStatus('subprice') && $object->lines[$i]->special_code != SUBTOTALS_SPECIAL_CODE && isset($pdf_sub_options['titleshowuponpdf'])) {
$up_excl_tax = pdf_getlineupexcltax($object, $i, $outputlangs, $hidedetails);
$this->printStdColumnContent($pdf, $curY, 'subprice', $up_excl_tax);
}
// Quantity
// Enough for 6 chars
if ($this->getColumnStatus('qty') && $object->lines[$i]->special_code != SUBTOTALS_SPECIAL_CODE) {
$qty = pdf_getlineqty($object, $i, $outputlangs, $hidedetails);
$this->printStdColumnContent($pdf, $curY, 'qty', $qty);
}
// Situation progress
if ($this->getColumnStatus('progress') && $object->lines[$i]->special_code != SUBTOTALS_SPECIAL_CODE) {
$progress = pdf_getlineprogress($object, $i, $outputlangs, $hidedetails);
$this->printStdColumnContent($pdf, $curY, 'progress', $progress);
}
// Unit
if ($this->getColumnStatus('unit') && $object->lines[$i]->special_code != SUBTOTALS_SPECIAL_CODE) {
$unit = pdf_getlineunit($object, $i, $outputlangs, $hidedetails);
$this->printStdColumnContent($pdf, $curY, 'unit', $unit);
}
// Discount on line
if ($this->getColumnStatus('discount') && $object->lines[$i]->remise_percent && $object->lines[$i]->special_code != SUBTOTALS_SPECIAL_CODE) {
$remise_percent = pdf_getlineremisepercent($object, $i, $outputlangs, $hidedetails);
$this->printStdColumnContent($pdf, $curY, 'discount', $remise_percent);
}
// Total excl tax line (HT)
if ($this->getColumnStatus('totalexcltax')) {
if ($object->lines[$i]->special_code != SUBTOTALS_SPECIAL_CODE && isset($pdf_sub_options['titleshowtotalexludingvatonpdf'])) {
$total_excl_tax = pdf_getlinetotalexcltax($object, $i, $outputlangs, $hidedetails);
$this->printStdColumnContent($pdf, $curY, 'totalexcltax', $total_excl_tax);
} elseif ($object->lines[$i]->qty < 0 && isset($sub_options['subtotalshowtotalexludingvatonpdf'])) {
if (isModEnabled('multicurrency') && $object->multicurrency_code != $conf->currency) {
$total_excl_tax = $object->getSubtotalLineMulticurrencyAmount($object->lines[$i]);
// Description of product line
if ($this->getColumnStatus('desc')) {
if ($object->lines[$i]->special_code != SUBTOTALS_SPECIAL_CODE) {
$this->printColDescContent($pdf, $curY, 'desc', $object, $i, $outputlangs, $hideref, $hidedesc);
$this->setAfterColsLinePositionsData('desc', $pdf->GetY(), $pdf->getPage());
} else {
$total_excl_tax = $object->getSubtotalLineAmount($object->lines[$i]);
}
$this->printStdColumnContent($pdf, $curY, 'totalexcltax', $total_excl_tax);
}
}
// Total with tax line (TTC)
if ($this->getColumnStatus('totalincltax')) {
$total_incl_tax = pdf_getlinetotalwithtax($object, $i, $outputlangs, $hidedetails);
$this->printStdColumnContent($pdf, $curY, 'totalincltax', $total_incl_tax);
}
// Extrafields
if (!empty($object->lines[$i]->array_options)) {
foreach ($object->lines[$i]->array_options as $extrafieldColKey => $extrafieldValue) {
if ($this->getColumnStatus($extrafieldColKey)) {
$extrafieldValue = $this->getExtrafieldContent($object->lines[$i], $extrafieldColKey, $outputlangs);
$this->printStdColumnContent($pdf, $curY, $extrafieldColKey, $extrafieldValue);
$this->setAfterColsLinePositionsData('options_' . $extrafieldColKey, $pdf->GetY(), $pdf->getPage());
$bg_color = colorStringToArray(getDolGlobalString("SUBTOTAL_BACK_COLOR_LEVEL_".abs($object->lines[$i]->qty), 'ffffff'));
pdf_render_subtotals($pdf, $this, $curY, $object, $i, $outputlangs, $hideref, $hidedesc, $bg_color, true, true);
}
}
}
$afterPosData = $this->getMaxAfterColsLinePositionsData();
$parameters = array(
$afterPosData = $this->getMaxAfterColsLinePositionsData();
$pdf->setPage($pageposbefore);
$pdf->setTopMargin($this->marge_haute);
$curY = $curYBefore;
$pdf->setPageOrientation('', false, $this->heightforfooter); // The only function to edit the bottom margin of current page to set it.
// We suppose that a too long description or photo were moved completely on next page
if ($afterPosData['page'] > $pageposbefore && (empty($showpricebeforepagebreak) || ($curY + 4) > ($this->page_hauteur - $this->heightforfooter))) {
$pdf->setPage($afterPosData['page']);
$curY = $this->tab_top_newpage;
}
$pdf->SetFont('', '', $default_font_size - 1); // We reposition the default font
// Line position
if ($this->getColumnStatus('position')) {
$this->printStdColumnContent($pdf, $curY, 'position', strval($linePosition));
}
// VAT Rate
if ($this->getColumnStatus('vat') && $object->lines[$i]->special_code != SUBTOTALS_SPECIAL_CODE) {
$vat_rate = pdf_getlinevatrate($object, $i, $outputlangs, $hidedetails);
$this->printStdColumnContent($pdf, $curY, 'vat', $vat_rate);
}
// Unit price before discount
if ($this->getColumnStatus('subprice') && $object->lines[$i]->special_code != SUBTOTALS_SPECIAL_CODE && isset($pdf_sub_options['titleshowuponpdf'])) {
$up_excl_tax = pdf_getlineupexcltax($object, $i, $outputlangs, $hidedetails);
$this->printStdColumnContent($pdf, $curY, 'subprice', $up_excl_tax);
}
// Quantity
// Enough for 6 chars
if ($this->getColumnStatus('qty') && $object->lines[$i]->special_code != SUBTOTALS_SPECIAL_CODE) {
$qty = pdf_getlineqty($object, $i, $outputlangs, $hidedetails);
$this->printStdColumnContent($pdf, $curY, 'qty', $qty);
}
// Situation progress
if ($this->getColumnStatus('progress') && $object->lines[$i]->special_code != SUBTOTALS_SPECIAL_CODE) {
$progress = pdf_getlineprogress($object, $i, $outputlangs, $hidedetails);
$this->printStdColumnContent($pdf, $curY, 'progress', $progress);
}
// Unit
if ($this->getColumnStatus('unit') && $object->lines[$i]->special_code != SUBTOTALS_SPECIAL_CODE) {
$unit = pdf_getlineunit($object, $i, $outputlangs, $hidedetails);
$this->printStdColumnContent($pdf, $curY, 'unit', $unit);
}
// Discount on line
if ($this->getColumnStatus('discount') && $object->lines[$i]->remise_percent && $object->lines[$i]->special_code != SUBTOTALS_SPECIAL_CODE) {
$remise_percent = pdf_getlineremisepercent($object, $i, $outputlangs, $hidedetails);
$this->printStdColumnContent($pdf, $curY, 'discount', $remise_percent);
}
// Total excl tax line (HT)
if ($this->getColumnStatus('totalexcltax')) {
if ($object->lines[$i]->special_code != SUBTOTALS_SPECIAL_CODE && isset($pdf_sub_options['titleshowtotalexludingvatonpdf'])) {
$total_excl_tax = pdf_getlinetotalexcltax($object, $i, $outputlangs, $hidedetails);
$this->printStdColumnContent($pdf, $curY, 'totalexcltax', $total_excl_tax);
} elseif ($object->lines[$i]->qty < 0 && isset($sub_options['subtotalshowtotalexludingvatonpdf'])) {
if (isModEnabled('multicurrency') && $object->multicurrency_code != $conf->currency) {
$total_excl_tax = $object->getSubtotalLineMulticurrencyAmount($object->lines[$i]);
} else {
$total_excl_tax = $object->getSubtotalLineAmount($object->lines[$i]);
}
$this->printStdColumnContent($pdf, $curY, 'totalexcltax', $total_excl_tax);
}
}
// Total with tax line (TTC)
if ($this->getColumnStatus('totalincltax')) {
$total_incl_tax = pdf_getlinetotalwithtax($object, $i, $outputlangs, $hidedetails);
$this->printStdColumnContent($pdf, $curY, 'totalincltax', $total_incl_tax);
}
// Extrafields
if (!empty($object->lines[$i]->array_options)) {
foreach ($object->lines[$i]->array_options as $extrafieldColKey => $extrafieldValue) {
if ($this->getColumnStatus($extrafieldColKey)) {
$extrafieldValue = $this->getExtrafieldContent($object->lines[$i], $extrafieldColKey, $outputlangs);
$this->printStdColumnContent($pdf, $curY, $extrafieldColKey, $extrafieldValue);
$this->setAfterColsLinePositionsData('options_' . $extrafieldColKey, $pdf->GetY(), $pdf->getPage());
}
}
}
$afterPosData = $this->getMaxAfterColsLinePositionsData();
$parameters = array(
'object' => $object,
'i' => $i,
'pdf' => & $pdf,
@ -917,9 +930,9 @@ class pdf_sponge extends ModelePDFFactures
'nexY' => & $afterPosData['y'], // for backward module hook compatibility Y will be accessible by $object->getMaxAfterColsLinePositionsData()
'outputlangs' => $outputlangs,
'hidedetails' => $hidedetails
);
$reshook = $hookmanager->executeHooks('printPDFline', $parameters, $this); // Note that $object may have been modified by hook
);
$reshook = $hookmanager->executeHooks('printPDFline', $parameters, $this); // Note that $object may have been modified by hook
}
$sign = 1;
if (isset($object->type) && $object->type == 2 && getDolGlobalString('INVOICE_POSITIVE_CREDIT_NOTE')) {
@ -999,16 +1012,22 @@ class pdf_sponge extends ModelePDFFactures
$this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')] = array('vatrate' => $vatrate, 'vatcode' => $vatcode, 'amount' => $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['amount'] + $tvaligne);
}
$afterPosData = $this->getMaxAfterColsLinePositionsData();
$pdf->setPage($afterPosData['page']);
$nexY = $afterPosData['y'];
if (!$hidenextline) {
$afterPosData = $this->getMaxAfterColsLinePositionsData();
$pdf->setPage($afterPosData['page']);
$nexY = $afterPosData['y'];
// Add line
if (getDolGlobalString('MAIN_PDF_DASH_BETWEEN_LINES') && $i < ($nblines - 1) && $afterPosData['y'] < $this->page_hauteur - $this->heightforfooter - 5) {
$pdf->SetLineStyle(array('dash' => '1,1', 'color' => array(80, 80, 80)));
//$pdf->SetDrawColor(190,190,200);
$pdf->line($this->marge_gauche, $nexY, $this->page_largeur - $this->marge_droite, $nexY);
$pdf->SetLineStyle(array('dash' => 0));
// Add line
if (getDolGlobalString('MAIN_PDF_DASH_BETWEEN_LINES') && $i < ($nblines - 1) && $afterPosData['y'] < $this->page_hauteur - $this->heightforfooter - 5) {
$pdf->SetLineStyle(array('dash' => '1,1', 'color' => array(80, 80, 80)));
//$pdf->SetDrawColor(190,190,200);
$pdf->line($this->marge_gauche, $nexY, $this->page_largeur - $this->marge_droite, $nexY);
$pdf->SetLineStyle(array('dash' => 0));
}
}
if ($object->lines[$i]->special_code == SUBTOTALS_SPECIAL_CODE && (isset($sub_options['titleshowuponpdf']) || isset($sub_options['titleshowtotalexludingvatonpdf'])) && getDolGlobalString('SUBTOTAL_HIDE_LINES_UNDER_TITLE')) { // TODO Use $sub_options['titlehidelinesundertitle'] instead of SUBTOTAL_HIDE_LINES_UNDER_TITLE
$hidenextline = 1;
}
$nexY += 0; // Add space between lines

View file

@ -73,7 +73,6 @@ class modReception extends DolibarrModules
$this->requiredby = array();
$this->conflictwith = array();
$this->langfiles = array('receptions');
// Constants
$this->const = [
[

View file

@ -100,6 +100,7 @@ class modSupplierProposal extends DolibarrModules
0,
],
];
// Boxes
$this->boxes = [];

View file

@ -583,6 +583,7 @@ class pdf_cyan extends ModelePDFPropales
if ($object->lines[$i]->special_code == SUBTOTALS_SPECIAL_CODE) {
$level = $object->lines[$i]->qty;
if ($sub_options) {
$hidenextline = 0;
if (isset($sub_options['titleshowuponpdf'])) {
$pdf_sub_options['titleshowuponpdf'] = isset($pdf_sub_options['titleshowuponpdf']) && $pdf_sub_options['titleshowuponpdf'] < $level ? $pdf_sub_options['titleshowuponpdf'] : $level;
} elseif (isset($pdf_sub_options['titleshowuponpdf']) && abs($level) <= $pdf_sub_options['titleshowuponpdf']) {
@ -868,7 +869,7 @@ class pdf_cyan extends ModelePDFPropales
}
}
if ($object->lines[$i]->special_code == SUBTOTALS_SPECIAL_CODE && isset($sub_options['titleshowuponpdf']) && getDolGlobalString('SUBTOTAL_HIDE_LINES_UNDER_TITLE')) { // TODO Use $sub_options['titlehidelinesundertitle'] instead of SUBTOTAL_HIDE_LINES_UNDER_TITLE
if ($object->lines[$i]->special_code == SUBTOTALS_SPECIAL_CODE && (isset($sub_options['titleshowuponpdf']) || isset($sub_options['titleshowtotalexludingvatonpdf'])) && getDolGlobalString('SUBTOTAL_HIDE_LINES_UNDER_TITLE')) { // TODO Use $sub_options['titlehidelinesundertitle'] instead of SUBTOTAL_HIDE_LINES_UNDER_TITLE
$hidenextline = 1;
}

View file

@ -186,7 +186,7 @@ if (in_array($modulepart, array('facture_paiement', 'unpaid'))) {
// If we have a hash public (hashp), we guess the original_file.
$ecmfile = '';
if (!empty($hashp) && $hashp !='shared') {
if (!empty($hashp) && $hashp != 'shared') {
if (GETPOST('type', 'alpha') == 'link') {
require_once DOL_DOCUMENT_ROOT.'/core/class/link.class.php';
$link = new Link($db);

View file

@ -166,7 +166,7 @@ function addDispatchLine(index, type, mode)
* @param $row object
*/
function addDispatchTR(qtyOrdered, qtyDispatched, index, nbrTrs, warehouseId, inputId, type, qty, mode, $row) {
if (qtyOrdered <= 1) {
if (qtyOrdered <= 0) {
let errormsg = '<?php echo dol_escape_js($langs->trans('QtyCantBeSplit')); ?>';
$.jnotify(errormsg, 'error', true);
return -1;

View file

@ -48,7 +48,7 @@ require_once DOL_DOCUMENT_ROOT.'/mrp/class/mo.class.php';
require_once DOL_DOCUMENT_ROOT.'/mrp/lib/mrp_mo.lib.php';
// Load translation files required by the page
$langs->loadLangs(array("mrp", "stocks", "other"));
$langs->loadLangs(array("mrp", "stocks", "other", "productbatch"));
// Get parameters
$id = GETPOSTINT('id');

View file

@ -1538,13 +1538,13 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea
// Split
$type = 'batch';
print '<td align="right" class="split">';
print ' '.img_picto($langs->trans('AddStockLocationLine'), 'split', 'class="splitbutton" onClick="addDispatchLine('.((int) $line->id).', \''.dol_escape_js($type).'\', \'qtymissingconsume\')"');
print ' '.img_picto($langs->trans('AddStockLocationLine'), 'split', 'class="splitbutton" onClick="addDispatchLine('.((int) $line->id).', \''.dol_escape_js($type).'\', \'qtymissingconsume\', 1)"');
print '</td>';
// Split All
print '<td align="right" class="splitall">';
if (($action == 'consumeorproduce' || $action == 'consumeandproduceall') && $tmpproduct->status_batch == 2) {
print img_picto($langs->trans('SplitAllQuantity'), 'split', 'class="splitbutton splitallbutton field-error-icon" data-max-qty="1" onClick="addDispatchLine('.$line->id.', \'batch\', \'allmissingconsume\')"');
print img_picto($langs->trans('SplitAllQuantity'), 'split', 'class="splitbutton splitallbutton field-error-icon" data-max-qty="1" onClick="addDispatchLine('.$line->id.', \'batch\', \'allmissingconsume\', 1)"');
}
print '</td>';

View file

@ -1197,6 +1197,7 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($canvasdisplayactio
$object->country = $tmparray['label'];
}
$object->forme_juridique_code = GETPOSTINT('forme_juridique_code');
$object->birth = dol_mktime(0, 0, 0, GETPOSTINT('birthmonth'), GETPOSTINT('birthday'), GETPOSTINT('birthyear'));
// We set multicurrency_code if enabled
if (isModEnabled("multicurrency")) {

View file

@ -2391,6 +2391,7 @@ class User extends CommonObject
if (!empty($user->admin) && empty($user->entity) && $user->id != $this->id) {
$sql .= ", entity = ".((int) $this->entity); // entity flag can be set/unset only by an another superadmin user
}
$sql .= ", default_range = ".($this->default_range > 0 ? ((int) $this->default_range) : 'null');
$sql .= ", default_c_exp_tax_cat = ".($this->default_c_exp_tax_cat > 0 ? ((int) $this->default_c_exp_tax_cat) : 'null');
$sql .= ", fk_warehouse = ".($this->fk_warehouse > 0 ? ((int) $this->fk_warehouse) : "null");