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

This commit is contained in:
Laurent Destailleur 2024-06-19 20:20:20 +02:00
commit b09b6d4b91
21 changed files with 373 additions and 107 deletions

View file

@ -1,6 +1,7 @@
# How to use it ?
export HOST_USER_ID=$(id -u)
export HOST_GROUP_ID=$(id -g)
export MYSQL_ROOT_PWD=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 13; echo)
docker-compose up -d

View file

@ -1,25 +1,15 @@
# This docker-compose.yml file is used to build and test quickly
# a specific version of Dolibarr
#
# Before build, define the variable DOLI_VERSION as following:
# $ export DOLI_VERSION=4.0.4
# And then, you can run :
# $ docker-compose up
#
# More information about Docker-compose : https://docs.docker.com/compose/
version: "3"
services:
mariadb:
container_name: dolibarr-mariadb-dev
mariadb-prod:
container_name: dolibarr-mariadb-prod
image: mariadb:latest
environment:
MYSQL_ROOT_PASSWORD: $MYSQL_ROOT_PWD
MYSQL_DATABASE: "dolibarr-prod"
web:
container_name: dolibarr-web-dev
container_name: dolibarr-web-prod
build: .
volumes:
@ -27,12 +17,12 @@ services:
- ../../documents:/var/www/html/documents
depends_on:
- mariadb
- mariadb-prod
environment:
WWW_USER_ID: 1000
WWW_GROUP_ID: 1000
HOST_USER_ID: $HOST_USER_ID
HOST_GROUP_ID: $HOST_GROUP_ID
DOLI_ROOT_PASSWORD: $MYSQL_ROOT_PWD
DOLI_DATABASE: "dolibarr-prod"
DOLI_DB_SERVER: "mariadb"
DOLI_DB_SERVER: "mariadb-prod"
ports:
- "8080:80"

View file

@ -3,7 +3,7 @@
# See README.md to know how to create a Dolibarr env with docker
usermod -u "${HOST_USER_ID}" www-data
groupmod -g "${HOST_USER_ID}" www-data
groupmod -g "${HOST_GROUP_ID}" www-data
chgrp -hR www-data /var/www/html
chmod g+rwx /var/www/html/conf

View file

@ -607,6 +607,14 @@ if ($action == 'create') {
}
}
// Due date
print '<tr>';
print '<td class="titlefield">' . $langs->trans('DateDue') . '</td>';
print '<td>';
print $object->date_lim_reglement ? dol_print_date($object->date_lim_reglement, 'day') : '&nbsp;';
print '</td>';
print '</tr>';
// Validate
/*
print '<tr>';

View file

@ -98,6 +98,16 @@ $search_date_validation_endmonth = GETPOSTINT('search_date_validation_endmonth'
$search_date_validation_endday = GETPOSTINT('search_date_validation_endday');
$search_date_validation_start = dol_mktime(0, 0, 0, $search_date_validation_startmonth, $search_date_validation_startday, $search_date_validation_startyear);
$search_date_validation_end = dol_mktime(23, 59, 59, $search_date_validation_endmonth, $search_date_validation_endday, $search_date_validation_endyear);
// Due date start
$search_date_due_start_day = GETPOSTINT('search_date_due_start_day');
$search_date_due_start_month = GETPOSTINT('search_date_due_start_month');
$search_date_due_start_year = GETPOSTINT('search_date_due_start_year');
$search_date_due_start = dol_mktime(0, 0, 0, $search_date_due_start_month, $search_date_due_start_day, $search_date_due_start_year);
// Due date end
$search_date_due_end_day = GETPOSTINT('search_date_due_end_day');
$search_date_due_end_month = GETPOSTINT('search_date_due_end_month');
$search_date_due_end_year = GETPOSTINT('search_date_due_end_year');
$search_date_due_end = dol_mktime(23, 59, 59, $search_date_due_end_month, $search_date_due_end_day, $search_date_due_end_year);
$search_import_key = GETPOST("search_import_key", 'alpha');
$search_account_category = GETPOSTINT('search_account_category');
@ -199,6 +209,7 @@ $arrayfields = array(
't.tms' => array('label' => $langs->trans("DateModification"), 'checked' => 0),
't.date_export' => array('label' => $langs->trans("DateExport"), 'checked' => 0),
't.date_validated' => array('label' => $langs->trans("DateValidationAndLock"), 'checked' => 0, 'enabled' => !getDolGlobalString("ACCOUNTANCY_DISABLE_CLOSURE_LINE_BY_LINE")),
't.date_lim_reglement' => array('label' => $langs->trans("DateDue"), 'checked' => 0),
't.import_key' => array('label' => $langs->trans("ImportId"), 'checked' => 0, 'position' => 1100),
);
@ -297,6 +308,16 @@ if (empty($reshook)) {
$search_date_validation_endday = '';
$search_date_validation_start = '';
$search_date_validation_end = '';
// Due date start
$search_date_due_start_day = '';
$search_date_due_start_month = '';
$search_date_due_start_year = '';
$search_date_due_start = '';
// Due date end
$search_date_due_end_day = '';
$search_date_due_end_month = '';
$search_date_due_end_year = '';
$search_date_due_end = '';
$search_debit = '';
$search_credit = '';
$search_lettering_code = '';
@ -426,6 +447,16 @@ if (empty($reshook)) {
$tmp = dol_getdate($search_date_validation_end);
$param .= '&search_date_validation_endmonth='.((int) $tmp['mon']).'&search_date_validation_endday='.((int) $tmp['mday']).'&search_date_validation_endyear='.((int) $tmp['year']);
}
// Due date start
if (!empty($search_date_due_start)) {
$filter['t.date_lim_reglement>='] = $search_date_due_start;
$param .= '&search_date_due_start_day='.$search_date_due_start_day.'&search_date_due_start_month='.$search_date_due_start_month.'&search_date_due_start_year='.$search_date_due_start_year;
}
// Due date end
if (!empty($search_date_due_end)) {
$filter['t.date_lim_reglement<='] = $search_date_due_end;
$param .= '&search_date_due_end_day='.$search_date_due_end_day.'&search_date_due_end_month='.$search_date_due_end_month.'&search_date_due_end_year='.$search_date_due_end_year;
}
if (!empty($search_debit)) {
$filter['t.debit'] = $search_debit;
$param .= '&search_debit='.urlencode($search_debit);
@ -612,6 +643,7 @@ $sql .= " t.date_creation,";
$sql .= " t.tms as date_modification,";
$sql .= " t.date_export,";
$sql .= " t.date_validated as date_validation,";
$sql .= " t.date_lim_reglement,";
$sql .= " t.import_key";
$sqlfields = $sql; // $sql fields to remove for count total
@ -661,6 +693,10 @@ if (count($filter) > 0) {
$sqlwhere[] = "t.date_validated >= '".$db->idate($value)."'";
} elseif ($key == 't.date_validated<=') {
$sqlwhere[] = "t.date_validated <= '".$db->idate($value)."'";
} elseif ($key == 't.date_lim_reglement>=') {
$sqlwhere[] = "t.date_lim_reglement >= '".$db->idate($value)."'";
} elseif ($key == 't.date_lim_reglement<=') {
$sqlwhere[] = "t.date_lim_reglement <= '".$db->idate($value)."'";
} elseif ($key == 't.credit' || $key == 't.debit') {
$sqlwhere[] = natural_search($key, $value, 1, 1);
} elseif ($key == 't.reconciled_option') {
@ -986,6 +1022,17 @@ if (!empty($arrayfields['t.date_validated']['checked'])) {
print '</div>';
print '</td>';
}
// Due date start and end
if (!empty($arrayfields['t.date_lim_reglement']['checked'])) {
print '<td class="liste_titre center">';
print '<div class="nowrapfordate">';
print $form->selectDate($search_date_due_start, 'search_date_due_start_', 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("From"));
print '</div>';
print '<div class="nowrapfordate">';
print $form->selectDate($search_date_due_end, 'search_date_due_end_', 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("to"));
print '</div>';
print '</td>';
}
if (!empty($arrayfields['t.import_key']['checked'])) {
print '<td class="liste_titre center">';
print '<input class="flat searchstring maxwidth50" type="text" name="search_import_key" value="'.dol_escape_htmltag($search_import_key).'">';
@ -1050,6 +1097,10 @@ if (!empty($arrayfields['t.date_export']['checked'])) {
if (!empty($arrayfields['t.date_validated']['checked'])) {
print_liste_field_titre($arrayfields['t.date_validated']['label'], $_SERVER['PHP_SELF'], "t.date_validated,t.doc_date", "", $param, '', $sortfield, $sortorder, 'center ');
}
// Due date
if (!empty($arrayfields['t.date_lim_reglement']['checked'])) {
print_liste_field_titre($arrayfields['t.date_lim_reglement']['label'], $_SERVER['PHP_SELF'], 't.date_lim_reglement', '', $param, '', $sortfield, $sortorder, 'center ');
}
if (!empty($arrayfields['t.import_key']['checked'])) {
print_liste_field_titre($arrayfields['t.import_key']['label'], $_SERVER["PHP_SELF"], "t.import_key", "", $param, '', $sortfield, $sortorder, 'center ');
}
@ -1103,6 +1154,8 @@ while ($i < min($num, $limit)) {
$line->date_modification = $db->jdate($obj->date_modification);
$line->date_export = $db->jdate($obj->date_export);
$line->date_validation = $db->jdate($obj->date_validation);
// Due date
$line->date_lim_reglement = $db->jdate($obj->date_lim_reglement);
print '<tr class="oddeven">';
// Action column
@ -1334,6 +1387,14 @@ while ($i < min($num, $limit)) {
}
}
// Due date
if (!empty($arrayfields['t.date_lim_reglement']['checked'])) {
print '<td class="center">'.dol_print_date($line->date_lim_reglement, 'day').'</td>';
if (!$i) {
$totalarray['nbfield']++;
}
}
if (!empty($arrayfields['t.import_key']['checked'])) {
print '<td class="tdoverflowmax125" title="'.dol_escape_htmltag($obj->import_key).'">'.dol_escape_htmltag($obj->import_key)."</td>\n";
if (!$i) {

View file

@ -78,6 +78,16 @@ $search_date_validation_endmonth = GETPOSTINT('search_date_validation_endmonth')
$search_date_validation_endday = GETPOSTINT('search_date_validation_endday');
$search_date_validation_start = dol_mktime(0, 0, 0, $search_date_validation_startmonth, $search_date_validation_startday, $search_date_validation_startyear);
$search_date_validation_end = dol_mktime(23, 59, 59, $search_date_validation_endmonth, $search_date_validation_endday, $search_date_validation_endyear);
// Due date start
$search_date_due_start_day = GETPOSTINT('search_date_due_start_day');
$search_date_due_start_month = GETPOSTINT('search_date_due_start_month');
$search_date_due_start_year = GETPOSTINT('search_date_due_start_year');
$search_date_due_start = dol_mktime(0, 0, 0, $search_date_due_start_month, $search_date_due_start_day, $search_date_due_start_year);
// Due date end
$search_date_due_end_day = GETPOSTINT('search_date_due_end_day');
$search_date_due_end_month = GETPOSTINT('search_date_due_end_month');
$search_date_due_end_year = GETPOSTINT('search_date_due_end_year');
$search_date_due_end = dol_mktime(23, 59, 59, $search_date_due_end_month, $search_date_due_end_day, $search_date_due_end_year);
$search_import_key = GETPOST("search_import_key", 'alpha');
$search_account_category = GETPOSTINT('search_account_category');
@ -171,6 +181,7 @@ $arrayfields = array(
't.balance' => array('label' => $langs->trans("Balance"), 'checked' => 1),
't.date_export' => array('label' => $langs->trans("DateExport"), 'checked' => -1),
't.date_validated' => array('label' => $langs->trans("DateValidation"), 'checked' => -1, 'enabled' => !getDolGlobalString("ACCOUNTANCY_DISABLE_CLOSURE_LINE_BY_LINE")),
't.date_lim_reglement' => array('label' => $langs->trans("DateDue"), 'checked' => 0),
't.import_key' => array('label' => $langs->trans("ImportId"), 'checked' => -1, 'position' => 1100),
);
@ -208,6 +219,7 @@ $error = 0;
* Action
*/
$filter = array();
$param = '';
if (GETPOST('cancel', 'alpha')) {
@ -262,6 +274,16 @@ if (empty($reshook)) {
$search_date_validation_endyear = '';
$search_date_validation_endmonth = '';
$search_date_validation_endday = '';
// Due date start
$search_date_due_start_day = '';
$search_date_due_start_month = '';
$search_date_due_start_year = '';
$search_date_due_start = '';
// Due date end
$search_date_due_end_day = '';
$search_date_due_end_month = '';
$search_date_due_end_year = '';
$search_date_due_end = '';
$search_lettering_code = '';
$search_debit = '';
$search_credit = '';
@ -270,9 +292,6 @@ if (empty($reshook)) {
$toselect = array();
}
// Must be after the remove filter action, before the export.
$filter = array();
if (!empty($search_date_start)) {
$filter['t.doc_date>='] = $search_date_start;
$param .= '&search_date_startmonth='.$search_date_startmonth.'&search_date_startday='.$search_date_startday.'&search_date_startyear='.$search_date_startyear;
@ -373,6 +392,16 @@ if (empty($reshook)) {
$filter['t.date_validated<='] = $search_date_validation_end;
$param .= '&search_date_validation_endmonth='.$search_date_validation_endmonth.'&search_date_validation_endday='.$search_date_validation_endday.'&search_date_validation_endyear='.$search_date_validation_endyear;
}
// Due date start
if (!empty($search_date_due_start)) {
$filter['t.date_lim_reglement>='] = $search_date_due_start;
$param .= '&search_date_due_start_day='.$search_date_due_start_day.'&search_date_due_start_month='.$search_date_due_start_month.'&search_date_due_start_year='.$search_date_due_start_year;
}
// Due date end
if (!empty($search_date_due_end)) {
$filter['t.date_lim_reglement<='] = $search_date_due_end;
$param .= '&search_date_due_end_day='.$search_date_due_end_day.'&search_date_due_end_month='.$search_date_due_end_month.'&search_date_due_end_year='.$search_date_due_end_year;
}
if (!empty($search_import_key)) {
$filter['t.import_key'] = $search_import_key;
$param .= '&search_import_key='.urlencode($search_import_key);
@ -504,7 +533,17 @@ if (empty($reshook)) {
if ($result < 0) {
setEventMessages('', $lettering->errors, 'errors');
} else {
setEventMessages($langs->trans('AccountancyOneLetteringModifiedSuccessfully'), array(), 'mesgs');
setEventMessages($langs->trans($result == 0 ? 'AccountancyNoLetteringModified' : 'AccountancyOneLetteringModifiedSuccessfully'), array(), 'mesgs');
header('Location: ' . $_SERVER['PHP_SELF'] . '?noreset=1' . $param);
exit();
}
} elseif ($type == 'sub' && $massaction == 'letteringpartial') {
$lettering = new Lettering($db);
$result = $lettering->updateLettering($toselect, false, true);
if ($result < 0) {
setEventMessages('', $lettering->errors, 'errors');
} else {
setEventMessages($langs->trans($result == 0 ? 'AccountancyNoLetteringModified' : 'AccountancyOneLetteringModifiedSuccessfully'), array(), 'mesgs');
header('Location: ' . $_SERVER['PHP_SELF'] . '?noreset=1' . $param);
exit();
}
@ -535,7 +574,7 @@ if (empty($reshook)) {
if ($result < 0) {
setEventMessages('', $lettering->errors, 'errors');
} else {
setEventMessages($langs->trans('AccountancyOneUnletteringModifiedSuccessfully'), array(), 'mesgs');
setEventMessages($langs->trans($result == 0 ? 'AccountancyNoUnletteringModified' : 'AccountancyOneUnletteringModifiedSuccessfully'), array(), 'mesgs');
header('Location: ' . $_SERVER['PHP_SELF'] . '?noreset=1' . $param);
exit();
}
@ -646,6 +685,9 @@ if (getDolGlobalInt('ACCOUNTING_ENABLE_LETTERING') && $user->hasRight('accountin
$arrayofmassactions['letteringauto'] = img_picto('', 'check', 'class="pictofixedwidth"') . $langs->trans('LetteringAuto');
$arrayofmassactions['preunletteringauto'] = img_picto('', 'uncheck', 'class="pictofixedwidth"') . $langs->trans('UnletteringAuto');
$arrayofmassactions['letteringmanual'] = img_picto('', 'check', 'class="pictofixedwidth"') . $langs->trans('LetteringManual');
if ($type == 'sub') {
$arrayofmassactions['letteringpartial'] = img_picto('', 'check', 'class="pictofixedwidth"') . $langs->trans('LetteringPartial');
}
$arrayofmassactions['preunletteringmanual'] = img_picto('', 'uncheck', 'class="pictofixedwidth"') . $langs->trans('UnletteringManual');
}
if ($user->hasRight('accounting', 'mouvements', 'supprimer')) {
@ -891,6 +933,17 @@ if (!empty($arrayfields['t.date_validated']['checked'])) {
print '</div>';
print '</td>';
}
// Due date start and end
if (!empty($arrayfields['t.date_lim_reglement']['checked'])) {
print '<td class="liste_titre center">';
print '<div class="nowrapfordate">';
print $form->selectDate($search_date_due_start, 'search_date_due_start_', 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("From"));
print '</div>';
print '<div class="nowrapfordate">';
print $form->selectDate($search_date_due_end, 'search_date_due_end_', 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("to"));
print '</div>';
print '</td>';
}
if (!empty($arrayfields['t.import_key']['checked'])) {
print '<td class="liste_titre center">';
print '<input class="flat searchstring maxwidth50" type="text" name="search_import_key" value="'.dol_escape_htmltag($search_import_key).'">';
@ -948,6 +1001,10 @@ if (!empty($arrayfields['t.date_export']['checked'])) {
if (!empty($arrayfields['t.date_validated']['checked'])) {
print_liste_field_titre($arrayfields['t.date_validated']['label'], $_SERVER['PHP_SELF'], "t.date_validated", "", $param, '', $sortfield, $sortorder, 'center ');
}
// Due date
if (!empty($arrayfields['t.date_lim_reglement']['checked'])) {
print_liste_field_titre($arrayfields['t.date_lim_reglement']['label'], $_SERVER['PHP_SELF'], 't.date_lim_reglement', '', $param, '', $sortfield, $sortorder, 'center ');
}
if (!empty($arrayfields['t.import_key']['checked'])) {
print_liste_field_titre($arrayfields['t.import_key']['label'], $_SERVER["PHP_SELF"], "t.import_key", "", $param, '', $sortfield, $sortorder, 'center ');
}
@ -1018,6 +1075,10 @@ while ($i < min($num, $limit)) {
if (!empty($arrayfields['t.date_validated']['checked'])) {
$colspanend++;
}
// Due date
if (!empty($arrayfields['t.date_lim_reglement']['checked'])) {
$colspanend++;
}
if (!empty($arrayfields['t.lettering_code']['checked'])) {
$colspanend++;
}
@ -1292,6 +1353,14 @@ while ($i < min($num, $limit)) {
}
}
// Due date
if (!empty($arrayfields['t.date_lim_reglement']['checked'])) {
print '<td class="center">'.dol_print_date($line->date_lim_reglement, 'day').'</td>';
if (!$i) {
$totalarray['nbfield']++;
}
}
if (!empty($arrayfields['t.import_key']['checked'])) {
print '<td class="tdoverflowmax125" title="'.dol_escape_htmltag($line->import_key).'">'.dol_escape_htmltag($line->import_key)."</td>\n";
if (!$i) {

View file

@ -891,6 +891,7 @@ class BookKeeping extends CommonObject
$sql .= " t.date_creation,";
$sql .= " t.date_export,";
$sql .= " t.date_validated as date_validation,";
$sql .= " t.date_lim_reglement,";
$sql .= " t.import_key";
}
// Manage filter
@ -929,6 +930,10 @@ class BookKeeping extends CommonObject
$sqlwhere[] = 't.date_validated >= \''.$this->db->idate($value).'\'';
} elseif ($key == 't.date_validated<=') {
$sqlwhere[] = 't.date_validated <= \''.$this->db->idate($value).'\'';
} elseif ($key == 't.date_lim_reglement>=') {
$sqlwhere[] = 't.date_lim_reglement>=\''.$this->db->idate($value).'\'';
} elseif ($key == 't.date_lim_reglement<=') {
$sqlwhere[] = 't.date_lim_reglement<=\''.$this->db->idate($value).'\'';
} elseif ($key == 't.credit' || $key == 't.debit') {
$sqlwhere[] = natural_search($key, $value, 1, 1);
} elseif ($key == 't.reconciled_option') {
@ -1013,6 +1018,8 @@ class BookKeeping extends CommonObject
$line->date_creation = $this->db->jdate($obj->date_creation);
$line->date_export = $this->db->jdate($obj->date_export);
$line->date_validation = $this->db->jdate($obj->date_validation);
// Due date
$line->date_lim_reglement = $this->db->jdate($obj->date_lim_reglement);
$line->import_key = $obj->import_key;
$this->lines[] = $line;
@ -1839,7 +1846,7 @@ class BookKeeping extends CommonObject
global $conf;
$sql = "SELECT piece_num, doc_date, code_journal, journal_label, doc_ref, doc_type,";
$sql .= " date_creation, tms as date_modification, date_validated as date_validation, import_key";
$sql .= " date_creation, tms as date_modification, date_validated as date_validation, date_lim_reglement, import_key";
// In llx_accounting_bookkeeping_tmp, field date_export doesn't exist
if ($mode != "_tmp") {
$sql .= ", date_export";
@ -1865,6 +1872,7 @@ class BookKeeping extends CommonObject
$this->date_export = $this->db->jdate($obj->date_export);
}
$this->date_validation = $this->db->jdate($obj->date_validation);
$this->date_lim_reglement = $this->db->jdate($obj->date_lim_reglement);
$this->import_key = $obj->import_key;
} else {
$this->error = "Error ".$this->db->lasterror();

View file

@ -1,7 +1,7 @@
<?php
/* Copyright (C) 2004-2005 Rodolphe Quiedeville <rodolphe@quiedeville.org>
* Copyright (C) 2013 Olivier Geffroy <jeff@jeffinfo.com>
* Copyright (C) 2013-2023 Alexandre Spangaro <aspangaro@open-dsi.fr>
* Copyright (C) 2013-2024 Alexandre Spangaro <alexandre@inovea-conseil.com>
* Copyright (C) 2018 Frédéric France <frederic.france@netlogic.fr>
*
* This program is free software; you can redistribute it and/or modify
@ -291,79 +291,136 @@ class Lettering extends BookKeeping
*
* @param array $ids ids array
* @param int $notrigger no trigger
* @param bool $partial Partial lettering
* @return int
*/
public function updateLettering($ids = array(), $notrigger = 0)
public function updateLettering($ids = array(), $notrigger = 0, $partial = false)
{
$error = 0;
// Generate a string with n char A where n is ACCOUNTING_LETTERING_NBLETTERS (So 'AA', 'AAA', ...) @phan-suppress-next-line PhanParamSuspiciousOrder
$lettre = str_pad("", getDolGlobalInt('ACCOUNTING_LETTERING_NBLETTERS', 3), "A");
$sql = "SELECT DISTINCT ab2.lettering_code";
$sql .= " FROM " . MAIN_DB_PREFIX . "accounting_bookkeeping AS ab";
$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "accounting_bookkeeping AS ab2 ON ab2.subledger_account = ab.subledger_account";
$sql .= " WHERE ab.rowid IN (" . $this->db->sanitize(implode(',', $ids)) . ")";
$sql .= " AND ab2.lettering_code != ''";
$sql .= " ORDER BY ab2.lettering_code DESC";
$sql .= " LIMIT 1 ";
$resqla = $this->db->query($sql);
if ($resqla) {
$obj = $this->db->fetch_object($resqla);
$lettre = (empty($obj->lettering_code) ? $lettre : $obj->lettering_code);
if (!empty($obj->lettering_code)) {
$lettre++;
}
$this->db->free($resqla);
} else {
$this->errors[] = 'Error'.$this->db->lasterror();
$error++;
}
$sql = "SELECT SUM(ABS(debit)) as deb, SUM(ABS(credit)) as cred FROM ".MAIN_DB_PREFIX."accounting_bookkeeping WHERE ";
$sql .= " rowid IN (".$this->db->sanitize(implode(',', $ids)).") AND lettering_code IS NULL AND subledger_account != ''";
$resqlb = $this->db->query($sql);
if ($resqlb) {
$obj = $this->db->fetch_object($resqlb);
if (!(round(abs($obj->deb), 2) === round(abs($obj->cred), 2))) {
$this->errors[] = 'Total not exacts '.round(abs($obj->deb), 2).' vs '.round(abs($obj->cred), 2);
$error++;
}
$this->db->free($resqlb);
} else {
$this->errors[] = 'Erreur sql'.$this->db->lasterror();
$error++;
}
// Update request
$now = dol_now();
$error = 0;
$affected_rows = 0;
if (!$error) {
$sql = "UPDATE ".MAIN_DB_PREFIX."accounting_bookkeeping SET";
$sql .= " lettering_code='".$this->db->escape($lettre)."'";
$sql .= ", date_lettering = '".$this->db->idate($now)."'"; // todo correct date it's false
$sql .= " WHERE rowid IN (".$this->db->sanitize(implode(',', $ids)).") AND lettering_code IS NULL AND subledger_account != ''";
// Generate a string with n char 'A' (for manual/auto reconcile) or 'a' (for partial reconcile) where n is ACCOUNTING_LETTERING_NBLETTERS (So 'AA'/'aa', 'AAA'/'aaa', ...) @phan-suppress-next-line PhanParamSuspiciousOrder
$letter = str_pad("", getDolGlobalInt('ACCOUNTING_LETTERING_NBLETTERS', 3), $partial ? 'a' : 'A');
dol_syslog(get_class($this)."::update", LOG_DEBUG);
$this->db->begin();
// Check partial / normal lettering case
$sql = "SELECT ab.lettering_code, GROUP_CONCAT(DISTINCT ab.rowid SEPARATOR ',') AS bookkeeping_ids";
$sql .= " FROM " . MAIN_DB_PREFIX . "accounting_bookkeeping AS ab";
$sql .= " WHERE ab.rowid IN (" . $this->db->sanitize(implode(',', $ids)) . ")";
$sql .= " GROUP BY ab.lettering_code";
$sql .= " ORDER BY ab.lettering_code DESC";
dol_syslog(__METHOD__ . " - Check partial / normal lettering case", LOG_DEBUG);
$resql = $this->db->query($sql);
if ($resql) {
while ($obj = $this->db->fetch_object($resql)) {
if (empty($obj->lettering_code)) continue;
// Remove normal lettering code if set partial lettering
if ($partial && preg_match('/^[A-Z]+$/', $obj->lettering_code)) {
if (!empty($obj->bookkeeping_ids)) $ids = array_diff($ids, explode(',', $obj->bookkeeping_ids));
} elseif (!$partial && preg_match('/^[a-z]+$/', $obj->lettering_code)) {
// Delete partial lettering code if set normal lettering
$sql2 = "UPDATE " . MAIN_DB_PREFIX . "accounting_bookkeeping SET";
$sql2 .= " lettering_code = NULL";
$sql2 .= ", date_lettering = NULL";
$sql2 .= " WHERE entity IN (" . getEntity('accountancy') . ")";
$sql2 .= " AND lettering_code = '" . $this->db->escape($obj->lettering_code) . "'";
dol_syslog(__METHOD__ . " - Remove partial lettering", LOG_DEBUG);
$resql2 = $this->db->query($sql2);
if (!$resql2) {
$this->errors[] = 'Error' . $this->db->lasterror();
$error++;
break;
}
}
}
$this->db->free($resql);
} else {
$this->errors[] = 'Error' . $this->db->lasterror();
$error++;
}
if (!$error && !empty($ids)) {
// Get next code
$sql = "SELECT DISTINCT ab2.lettering_code";
$sql .= " FROM " . MAIN_DB_PREFIX . "accounting_bookkeeping AS ab";
$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "accounting_bookkeeping AS ab2 ON ab2.subledger_account = ab.subledger_account";
$sql .= " WHERE ab.rowid IN (" . $this->db->sanitize(implode(',', $ids)) . ")";
$sql .= " AND ab2.lettering_code != ''";
$sql .= " ORDER BY ab2.lettering_code DESC";
dol_syslog(__METHOD__ . " - Get next code", LOG_DEBUG);
$resql = $this->db->query($sql);
if (!$resql) {
$error++;
$this->errors[] = "Error ".$this->db->lasterror();
if ($resql) {
while ($obj = $this->db->fetch_object($resql)) {
if (!empty($obj->lettering_code) &&
(($partial && preg_match('/^[a-z]+$/', $obj->lettering_code)) ||
(!$partial && preg_match('/^[A-Z]+$/', $obj->lettering_code)))
) {
$letter = $obj->lettering_code;
$letter++;
break;
}
}
$this->db->free($resql);
} else {
$affected_rows = $this->db->affected_rows($resql);
$this->errors[] = 'Error' . $this->db->lasterror();
$error++;
}
// Test amount integrity
if (!$error && !$partial) {
$sql = "SELECT SUM(ABS(debit)) as deb, SUM(ABS(credit)) as cred FROM " . MAIN_DB_PREFIX . "accounting_bookkeeping WHERE ";
$sql .= " rowid IN (" . $this->db->sanitize(implode(',', $ids)) . ") AND lettering_code IS NULL AND subledger_account != ''";
dol_syslog(__METHOD__ . " - Test amount integrity", LOG_DEBUG);
$resql = $this->db->query($sql);
if ($resql) {
if ($obj = $this->db->fetch_object($resql)) {
if (!(round(abs($obj->deb), 2) === round(abs($obj->cred), 2))) {
$this->errors[] = 'Total not exacts ' . round(abs($obj->deb), 2) . ' vs ' . round(abs($obj->cred), 2);
$error++;
}
}
$this->db->free($resql);
} else {
$this->errors[] = 'Erreur sql' . $this->db->lasterror();
$error++;
}
}
// Update lettering code
if (!$error) {
$sql = "UPDATE " . MAIN_DB_PREFIX . "accounting_bookkeeping SET";
$sql .= " lettering_code='" . $this->db->escape($letter) . "'";
$sql .= ", date_lettering = '" . $this->db->idate($now) . "'"; // todo correct date it's false
$sql .= " WHERE rowid IN (" . $this->db->sanitize(implode(',', $ids)) . ") AND lettering_code IS NULL AND subledger_account != ''";
dol_syslog(__METHOD__ . " - Update lettering code", LOG_DEBUG);
$resql = $this->db->query($sql);
if (!$resql) {
$error++;
$this->errors[] = "Error " . $this->db->lasterror();
} else {
$affected_rows = $this->db->affected_rows($resql);
}
}
}
// Commit or rollback
if ($error) {
$this->db->rollback();
foreach ($this->errors as $errmsg) {
dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
$this->error .= ($this->error ? ', '.$errmsg : $errmsg);
dol_syslog(get_class($this) . "::update " . $errmsg, LOG_ERR);
$this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
}
return -1 * $error;
} else {
$this->db->commit();
return $affected_rows;
}
}

View file

@ -63,7 +63,7 @@ abstract class CommonDocGenerator
protected $db;
/**
* @var Extrafields object
* @var ?Extrafields object
*/
public $extrafieldsCache;
@ -1368,7 +1368,7 @@ abstract class CommonDocGenerator
// Load extra fields if they haven't been loaded already.
if (empty($this->extrafieldsCache)) {
if (is_null($this->extrafieldsCache)) {
$this->extrafieldsCache = new ExtraFields($this->db);
}
if (empty($this->extrafieldsCache->attributes[$object->table_element])) {
@ -1422,7 +1422,7 @@ abstract class CommonDocGenerator
}
// Load extrafields if not already done
if (empty($this->extrafieldsCache)) {
if (is_null($this->extrafieldsCache)) {
$this->extrafieldsCache = new ExtraFields($this->db);
}
if (empty($this->extrafieldsCache->attributes[$object->table_element])) {
@ -1713,7 +1713,7 @@ abstract class CommonDocGenerator
}
// Load extra fields if they haven't been loaded already.
if (empty($this->extrafieldsCache)) {
if (is_null($this->extrafieldsCache)) {
$this->extrafieldsCache = new ExtraFields($this->db);
}
if (empty($this->extrafieldsCache->attributes[$object->table_element])) {

View file

@ -64,6 +64,11 @@ class CommandeFournisseur extends CommonOrder
*/
public $table_element_line = 'commande_fournisseurdet';
/**
* @var string Name of class line
*/
public $class_element_line = 'CommandeFournisseurLigne';
/**
* @var string Field with ID of parent key if this field has a parent
*/

View file

@ -67,6 +67,10 @@ class FactureFournisseur extends CommonInvoice
*/
public $table_element_line = 'facture_fourn_det';
/**
* @var string Name of class line
*/
public $class_element_line = 'SupplierInvoiceLine';
/**
* @var string Field with ID of parent key if this field has a parent
*/

View file

@ -46,7 +46,7 @@ dolibarr_install_syslog("- fileconf: entering fileconf.php page");
// installer like DoliWamp, DoliMamp or DoliBuntu).
// We first init "forced values" to nothing.
if (!isset($force_install_noedit)) {
$force_install_noedit = ''; // 1=To block vars specific to distrib, 2 to block all technical parameters
$force_install_noedit = ''; // 1=To block vars specific to distrib, 2 to block all technical parameters, 3 to block all technical parameters excepted main_url
}
if (!isset($force_install_type)) {
$force_install_type = '';
@ -220,7 +220,7 @@ if (!empty($force_install_noedit)) {
id="main_url"
name="main_url"
value="<?php print $dolibarr_main_url_root; ?> "
<?php if (!empty($force_install_noedit)) {
<?php if (!empty($force_install_noedit) && $force_install_noedit != 3) {
print ' disabled';
}
?>
@ -247,7 +247,7 @@ if (!empty($force_install_noedit)) {
<?php if (!empty($force_install_mainforcehttps)) {
print ' checked';
} ?>
<?php if ($force_install_noedit == 2 && $force_install_mainforcehttps !== null) {
<?php if (($force_install_noedit == 2 || $force_install_noedit == 3) && $force_install_mainforcehttps !== null) {
print ' disabled';
} ?>
>
@ -275,7 +275,7 @@ if (!empty($force_install_noedit)) {
id="db_name"
name="db_name"
value="<?php echo (!empty($dolibarr_main_db_name)) ? $dolibarr_main_db_name : ($force_install_database ? $force_install_database : 'dolibarr'); ?>"
<?php if ($force_install_noedit == 2 && $force_install_database !== null) {
<?php if (($force_install_noedit == 2 || $force_install_noedit == 3) && $force_install_database !== null) {
print ' disabled';
} ?>
>
@ -387,7 +387,7 @@ if (!empty($force_install_noedit)) {
?>
<select id="db_type"
name="db_type"
<?php if ($force_install_noedit == 2 && $force_install_type !== null) {
<?php if (($force_install_noedit == 2 || $force_install_noedit == 3) && $force_install_type !== null) {
print ' disabled';
} ?>
>
@ -406,7 +406,7 @@ if (!empty($force_install_noedit)) {
id="db_host"
name="db_host"
value="<?php print(!empty($force_install_dbserver) ? $force_install_dbserver : (!empty($dolibarr_main_db_host) ? $dolibarr_main_db_host : 'localhost')); ?>"
<?php if ($force_install_noedit == 2 && $force_install_dbserver !== null) {
<?php if (($force_install_noedit == 2 || $force_install_noedit == 3) && $force_install_dbserver !== null) {
print ' disabled';
} ?>
>
@ -423,7 +423,7 @@ if (!empty($force_install_noedit)) {
name="db_port"
id="db_port"
value="<?php print (!empty($force_install_port)) ? $force_install_port : $dolibarr_main_db_port; ?>"
<?php if ($force_install_noedit == 2 && $force_install_port !== null) {
<?php if (($force_install_noedit == 2 || $force_install_noedit == 3) && $force_install_port !== null) {
print ' disabled';
} ?>
>
@ -440,7 +440,7 @@ if (!empty($force_install_noedit)) {
id="db_prefix"
name="db_prefix"
value="<?php echo(!empty($force_install_prefix) ? $force_install_prefix : (!empty($dolibarr_main_db_prefix) ? $dolibarr_main_db_prefix : 'llx_')); ?>"
<?php if ($force_install_noedit == 2 && $force_install_prefix !== null) {
<?php if (($force_install_noedit == 2 || $force_install_noedit == 3) && $force_install_prefix !== null) {
print ' disabled';
} ?>
>
@ -461,7 +461,7 @@ if (!empty($force_install_noedit)) {
$checked = 1;
print ' checked';
} ?>
<?php if ($force_install_noedit == 2 && $force_install_createdatabase !== null) {
<?php if (($force_install_noedit == 2 || $force_install_noedit == 3) && $force_install_createdatabase !== null) {
print ' disabled';
} ?>
>
@ -478,7 +478,7 @@ if (!empty($force_install_noedit)) {
id="db_user"
name="db_user"
value="<?php print (!empty($force_install_databaselogin)) ? $force_install_databaselogin : $dolibarr_main_db_user; ?>"
<?php if ($force_install_noedit == 2 && $force_install_databaselogin !== null) {
<?php if (($force_install_noedit == 2 || $force_install_noedit == 3) && $force_install_databaselogin !== null) {
print ' disabled';
} ?>
>
@ -501,7 +501,7 @@ if (!empty($force_install_noedit)) {
}
print dol_escape_htmltag($autofill);
?>"
<?php if ($force_install_noedit == 2 && $force_install_databasepass !== null) {
<?php if (($force_install_noedit == 2 || $force_install_noedit == 3) && $force_install_databasepass !== null) {
print ' disabled';
} ?>
>
@ -522,7 +522,7 @@ if (!empty($force_install_noedit)) {
$checked = 1;
print ' checked';
} ?>
<?php if ($force_install_noedit == 2 && $force_install_createuser !== null) {
<?php if (($force_install_noedit == 2 || $force_install_noedit == 3) && $force_install_createuser !== null) {
print ' disabled';
} ?>
>
@ -723,7 +723,7 @@ jQuery(document).ready(function() { // TODO Test $( window ).load(function() to
console.log("click on db_create_user");
init_needroot();
});
<?php if ($force_install_noedit == 2 && empty($force_install_databasepass)) { ?>
<?php if (($force_install_noedit == 2 || $force_install_noedit == 3) && empty($force_install_databasepass)) { ?>
jQuery("#db_pass").focus();
<?php } ?>

View file

@ -20,7 +20,7 @@
$force_install_nophpinfo = true;
/** @var int $force_install_noedit 1 = Lock and hide environment variables, 2 = Lock all set variables */
$force_install_noedit = 2;
$force_install_noedit = 3;
/** @var string $force_install_message Information message */
$force_install_message = 'Welcome to your Dolibarr Docker install';
@ -71,4 +71,4 @@ $force_install_dolibarrlogin = 'admin';
$force_install_lockinstall = true;
/** @var string $force_install_module Enable module(s) (Comma separated class names list) */
$force_install_module = 'modSociete,modFournisseur,modFacture';
$force_install_module = '';

View file

@ -44,5 +44,6 @@ create table llx_projet_task
note_public text,
rang integer DEFAULT 0,
model_pdf varchar(255),
import_key varchar(14) -- Import key
import_key varchar(14), -- Import key,
billable smallint DEFAULT 1
)ENGINE=innodb;

View file

@ -96,6 +96,8 @@ if (@file_exists($forcedfile)) {
if (!empty($argv[4])) {
$main_data_dir = $argv[4]; // override when executing the script in command line
}
// In mode 3 the main_url is custom
if ($force_install_noedit != 3)
$main_url = detect_dolibarr_main_url_root();
if (!empty($argv[5])) {
$main_url = $argv[5]; // override when executing the script in command line
@ -114,7 +116,7 @@ if (@file_exists($forcedfile)) {
$passroot = $argv[7]; // override when executing the script in command line
}
}
if ($force_install_noedit == 2) {
if (($force_install_noedit == 2) || ($force_install_noedit == 3)) {
if (!empty($force_install_type)) {
$db_type = $force_install_type;
}
@ -872,7 +874,6 @@ function write_conf_file($conffile)
fwrite($fp, '// Take a look at conf.php.example file for an example of '.$conffiletoshowshort.' file'."\n");
fwrite($fp, '// and explanations for all possibles parameters.'."\n");
fwrite($fp, '//'."\n");
fwrite($fp, '$dolibarr_main_url_root=\''.dol_escape_php(trim($main_url), 1).'\';');
fwrite($fp, "\n");

View file

@ -409,6 +409,7 @@ Formula=Formula
## Reconcile
LetteringAuto=Reconcile auto
LetteringManual=Reconcile manual
LetteringPartial=Reconcile partial
Unlettering=Unreconcile
UnletteringAuto=Unreconcile auto
UnletteringManual=Unreconcile manual

View file

@ -522,6 +522,7 @@ if (!empty($conf->file->main_force_https) && !isHTTPS() && !defined('NOHTTPSREDI
$newurl = preg_replace('/^http:/i', 'https:', $_SERVER["SCRIPT_URI"]);
}
} else {
// If HTTPS is not defined in DOL_MAIN_URL_ROOT,
// Check HTTPS environment variable (Apache/mod_ssl only)
$newurl = preg_replace('/^http:/i', 'https:', DOL_MAIN_URL_ROOT).$_SERVER["REQUEST_URI"];
}

View file

@ -541,19 +541,19 @@ print '</td></tr>';
print '<tr class="oddeven"><td>';
print $langs->trans("MessageForm").'</td><td>';
$doleditor = new DolEditor('ONLINE_PAYMENT_MESSAGE_FORM', $conf->global->ONLINE_PAYMENT_MESSAGE_FORM, '', 100, 'dolibarr_details', 'In', false, true, true, ROWS_2, '90%');
$doleditor = new DolEditor('ONLINE_PAYMENT_MESSAGE_FORM', getDolGlobalString("ONLINE_PAYMENT_MESSAGE_FORM"), '', 100, 'dolibarr_details', 'In', false, true, true, ROWS_2, '90%');
$doleditor->Create();
print '</td></tr>';
print '<tr class="oddeven"><td>';
print $langs->trans("MessageOK").'</td><td>';
$doleditor = new DolEditor('ONLINE_PAYMENT_MESSAGE_OK', $conf->global->ONLINE_PAYMENT_MESSAGE_OK, '', 100, 'dolibarr_details', 'In', false, true, true, ROWS_2, '90%');
$doleditor = new DolEditor('ONLINE_PAYMENT_MESSAGE_OK', getDolGlobalString("ONLINE_PAYMENT_MESSAGE_OK"), '', 100, 'dolibarr_details', 'In', false, true, true, ROWS_2, '90%');
$doleditor->Create();
print '</td></tr>';
print '<tr class="oddeven"><td>';
print $langs->trans("MessageKO").'</td><td>';
$doleditor = new DolEditor('ONLINE_PAYMENT_MESSAGE_KO', $conf->global->ONLINE_PAYMENT_MESSAGE_KO, '', 100, 'dolibarr_details', 'In', false, true, true, ROWS_2, '90%');
$doleditor = new DolEditor('ONLINE_PAYMENT_MESSAGE_KO', getDolGlobalString("ONLINE_PAYMENT_MESSAGE_KO"), '', 100, 'dolibarr_details', 'In', false, true, true, ROWS_2, '90%');
$doleditor->Create();
print '</td></tr>';

View file

@ -67,6 +67,10 @@ class SupplierProposal extends CommonObject
*/
public $table_element_line = 'supplier_proposaldet';
/**
* @var string Name of class line
*/
public $class_element_line = 'SupplierProposalLine';
/**
* @var string Field with ID of parent key if this field has a parent
*/

View file

@ -201,6 +201,9 @@ class FormListWebPortal
$arrayfields['remain_to_pay'] = array('type' => 'price', 'label' => 'RemainderToPay', 'checked' => 1, 'enabled' => 1, 'visible' => 1, 'position' => 10000, 'help' => '',);
}
$arrayfields['download_link'] = array('label' => 'File', 'checked' => 1, 'enabled' => 1, 'visible' => 1, 'position' => 10001, 'help' => '',);
if ($elementEn == "propal" && getDolGlobalString("PROPOSAL_ALLOW_ONLINESIGN") != 0) {
$arrayfields['signature_link'] = array('label' => 'Signature', 'checked' => 1, 'enabled' => 1, 'visible' => 1, 'position' => 10002, 'help' => '',);
}
$object->fields = dol_sort_array($object->fields, 'position');
//$arrayfields['anotherfield'] = array('type'=>'integer', 'label'=>'AnotherField', 'checked'=>1, 'enabled'=>1, 'position'=>90, 'csslist'=>'right');
@ -514,7 +517,13 @@ class FormListWebPortal
$html .= '</td>';
}
$html .= '</tr>';
// Signature link
if ($elementEn == "propal" && getDolGlobalString("PROPOSAL_ALLOW_ONLINESIGN") != 0) {
if (!empty($arrayfields['signature_link']['checked'])) {
$html .= '<td data-label="' . $arrayfields['signature_link']['label'] . '">';
$html .= '</td>';
}
}
$totalarray = array();
$totalarray['nbfield'] = 0;
@ -557,6 +566,15 @@ class FormListWebPortal
$html .= '</th>';
$totalarray['nbfield']++;
}
// Signature link
if ($elementEn == "propal" && getDolGlobalString("PROPOSAL_ALLOW_ONLINESIGN") != 0) {
if (!empty($arrayfields['signature_link']['checked'])) {
$html .= '<th scope="col">';
$html .= $langs->trans($arrayfields['signature_link']['label']);
$html .= '</th>';
$totalarray['nbfield']++;
}
}
// Hook fields
$parameters = array('arrayfields' => $arrayfields, 'sortfield' => $sortfield, 'sortorder' => $sortorder, 'totalarray' => &$totalarray);
@ -680,12 +698,24 @@ class FormListWebPortal
$totalarray['nbfield']++;
}
}
// Signature link
if ($elementEn == "propal" && getDolGlobalString("PROPOSAL_ALLOW_ONLINESIGN") != 0) {
if (!empty($arrayfields['signature_link']['checked'])) {
$html .= '<td class="nowraponall" data-label="' . $arrayfields['signature_link']['label'] . '">';
if ($object->fk_statut == Propal::STATUS_VALIDATED) {
$html .= $this->form->getSignatureLink('proposal', $object);
}
$html .= '</td>';
if (!$i) {
$totalarray['nbfield']++;
}
}
}
// Fields from hook
$parameters = array('arrayfields' => $arrayfields, 'object' => $object, 'obj' => $obj, 'i' => $i, 'totalarray' => &$totalarray);
$reshook = $hookmanager->executeHooks('printFieldListValue', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
$html .= $hookmanager->resPrint;
$html .= '</tr>';
$i++;

View file

@ -339,6 +339,31 @@ class FormWebPortal extends Form
return $out;
}
/**
* Show a Signature icon with link
* You may want to call this into a div like this:
* print '<div class="inline-block valignmiddle">'.$formfile->getDocumentsLink($element_doc, $filename, $filedir).'</div>';
*
* @param string $modulepart 'proposal', 'facture', 'facture_fourn', ...
* @param Object $object Object linked to the document to be signed
* @param string $morecss Add more css to the download picto
* @return string Output string with HTML link of signature (might be empty string).
*/
public function getSignatureLink($modulepart, $object, $morecss = '')
{
global $langs;
require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
$out = '<!-- html.formwebportal::getSignatureLink -->' . "\n";
$url = getOnlineSignatureUrl(0, $modulepart, $object->ref, 1, $object);
if (!empty($url)) {
$out .= '<a target="_blank" rel="noopener noreferrer" href="' . $url . '"' . ($morecss ? ' class="' . $morecss . '"' : '') . ' role="signaturelink">';
$out .= '<i class="fa fa-file-signature"></i>';
$out .= $langs->trans("Sign");
$out .= '</a>';
}
return $out;
}
/**
* Generic method to select a component from a combo list.
* Can use autocomplete with ajax after x key pressed or a full combo, depending on setup.