2
0
forked from Wavyzz/dolibarr

Merge branch '22_accfydefault' of https://github.com/aspangaro/dolibarr into 22_accfydefault

This commit is contained in:
Alexandre SPANGARO
2025-02-17 13:51:49 +01:00
39 changed files with 477 additions and 197 deletions

103
ChangeLog
View File

@@ -238,6 +238,109 @@ The following changes may create regressions for some external modules, but were
* If you were using the substitution key __MEMBER_CIVILITY__, you must now use __MEMBER_TITLE__
***** ChangeLog for 20.0.4 compared to 20.0.2 *****
FIX: $this->origin_object can not be instance of CommandeFournisseur if it is already an instanceof CommonObject
FIX: 17.0 API endpoints "PUT": prevent overwriting all extrafields if only some are supplied in the request cf. PR #29237
FIX: 17.0 - collisions in cache for dol_getIdFromCode
FIX: #18713
FIX: 20.0 - PHP8 fatal when creating a reception unless corresponding PDF model is enabled
FIX: #21294 Stock import sql query
FIX: #26250 fatal error on kit
FIX: #28702
FIX: #29624 - substitution of __DATE_DELIVERY__
FIX: #32113
FIX: #32186
FIX: #32339 Delete a loan settlement is partial
FIX: #32387
FIX: #32477 Loan - Insurance amount need decimals
FIX: #32611
FIX: #32736 + avoid php warning
FIX: #32743
FIX: #32765 JS Error: Uncaught TypeError
FIX: #32801 VAT type is inverted in form VAT selector
FIX: #32840
FIX: #32843
FIX: #32880 - Tags are using a special rendering.
FIX: add other fields
FIX: autofill price with multicurrency on supplier doc
FIX: avoid phan error
FIX: avoid php8 warnings
FIX: avoid warning with the new Dolistore website
FIX: backport from develop to avoid php warning
FIX: Bad calculation of the theoretical stock. Did not take into account
FIX: bad dispatched quantities for batches on shipment card
FIX: Brian is in the kitchen
FIX: broken feature, compatibility with "Default search filters"
FIX: broken feature with check $pa_ht_isemptystring
FIX: Bug on select user on time.php (all project list)
FIX: can not delete files in task card
FIX: Check "$search_sale" only if it's an internal user
FIX: clean unique extrafields when create product combination
FIX: code not visible correctly into view of dictionary
FIX: compatibility between next_prev_filter and hook return
FIX: compatibility with multicompany
FIX: Complete path was started in #17243 for pdf_cannelle
FIX: Continue for eagle_proforma
FIX: country id is not saved when we provide country_code only
FIX: #CVE-2024-34051
FIX: delete supplier order when at least one line linked to customer order line
FIX: display error when loan can't be deleted
FIX: display full tree on shipment card when a kit contains a same component in other sub-kit
FIX: DROP INDEX IF EXISTS is not possible !
FIX: extrafields lost during creation from rec invoice
FIX: FEC import
FIX: Fiscal year - missing translation on status
FIX: Fix return value of hook sendMail when hook return -1 who must be return false in sendfile() function
FIX: GETPOST('private_message')
FIX: glob is better for search files with wildcard + avoid warning
FIX: if $force_entity = 0 ($force_entity != 'default') = false
FIX: Loan - Insurance amount need decimals
FIX: Many status on invoice linked object block
FIX: merge problem
FIX: missing company name if dontaion is linked to third party
FIX: missing default values if $objsrc or $soc fields are empty
FIX: missing edit extrafields inline for member card
FIX: missing quick edit for extrafields
FIX: more bugs and warnings
FIX: Multilangs : PDF lines description
FIX: Must not have both thirdparty and member.
FIX: ODT substitution when many HTML tags in string
FIX: on the road again
FIX: pdf_cannelle (supplier_invoice) add background - Complete #17243
FIX: Prices didn't update when clone a propale with update prices
FIX: product variants copy: also copy multiprice variations
FIX: refactorize (maybe broken feature for not received completely)
FIX: remove debug trace
FIX: remove socid when cloning a project without third parties
FIX: removes traces of <<<HEAD conflicts following the postponement of branch 13 modifications (#32014)
FIX: remove unused code
FIX: same broken feature for propal and invoice
FIX: select 2 no record found translate
FIX: selectcontact is loading all contacts if socid is empty and MAIN_ACTIONCOM_CAN_ADD_ANY_CONTACT is not set
FIX: selectcontact is loading all contacts when update event
FIX: select group and severity search fields on ticket list
FIX: send email to assigned user on ticket create
FIX: sql error with the new sql forge filter
FIX: sql "order by" is defined twice
FIX: status ticket update for new message
FIX: swap tests
FIX: switch on/off status of a page of the second website.
FIX: There were many status indicator in the invoice linked object block (propal card)
FIX: uniformize code
FIX: units used scale and scale is an integer
FIX: wrong alias table
FIX: wrong file path + avoid warning
FIX: wrong filter format
FIX: wrong "fournisseur" var value checking
FIX: wrong left margin
FIX: wrong message on update shipment
FIX: wrong ODT path for multicompany
FIX: wrong path for odt models
FIX: wrong search filter, empty product unit is "none"
FIX: wrong update function parameter
FIX: some wrong var type
FIX: some wrong var name
***** ChangeLog for 20.0.3 compared to 20.0.2 *****
FIX: 17.0 - missing error handling for FactureRec::fetch in card-rec.php
FIX: 17.0 - warnings due to uninitialized variables + delete code that doesn't apply to recurring invoices (AFAIK, there is no recurring credit note feature)

View File

@@ -36,7 +36,7 @@ $PUBLISHBETARC="dolibarr\@vmprod1.dolibarr.org:/home/dolibarr/asso.dolibarr.org/
"RPM_FEDORA"=>"rpmbuild",
"RPM_MANDRIVA"=>"rpmbuild",
"RPM_OPENSUSE"=>"rpmbuild",
"DEB"=>"dpkg",
"DEB"=>"dpkg,po2debconf", # need also debhelper
"FLATPACK"=>"flatpack",
"EXEDOLIWAMP"=>"ISCC.exe",
"SNAPSHOT"=>"tar"
@@ -85,8 +85,9 @@ if (! $ENV{"DESTIBETARC"} || ! $ENV{"DESTISTABLE"})
print "set DESTIBETARC=c:/tmp\n";
print "set DESTISTABLE=c:/tmp\n";
print "\n";
print "Example: DESTIBETARC='/media/HDDATA1_LD/Mes Sites/Web/Dolibarr/dolibarr.org/files/lastbuild'\n";
print "Example: DESTISTABLE='/media/HDDATA1_LD/Mes Sites/Web/Dolibarr/dolibarr.org/files/stable'\n";
print "Example in .bashrc:\n";
print "export DESTIBETARC='/mnt/HDDATA1_LD/Mes Archives/Doli/dolibarr/lastbuild'\n";
print "export DESTISTABLE='/mnt/HDDATA1_LD/Mes Archives/Doli/dolibarr/stable'\n";
sleep 2;
exit 1;
}

View File

@@ -44,8 +44,22 @@ the project: `pre-commit-config.yaml`.
The good file redirects output to the error channel so your IDE will be able to catch the error.
### Troubleshooting
* If you get error "ModuleNotFoundError: No module named 'platformdirs'"
Install the python package with
`pip3 install platformdirs` or `pip3 install platformdirs --break-system-packages`
* If you get error "ERROR: PHP_CodeSniffer requires the tokenizer, xmlwriter and SimpleXML extensions to be enabled. Please enable xmlwriter and SimpleXML."
Install the PHP package xml
`sudo apt install php-simplexml`
### Tips
After installing `pre-commit` onto your local git clone, pre-commit will run
on every commit. The first time, all tools required by pre-commit will be installed
into ~/.cache/pre-commit

View File

@@ -100,14 +100,14 @@ $formother = new FormOther($db);
$form = new Form($db);
if (empty($search_date_start) && !GETPOSTISSET('formfilteraction')) {
$sql = "SELECT date_start, date_end";
$sql .=" FROM ".MAIN_DB_PREFIX."accounting_fiscalyear ";
if (getDolGlobalInt('ACCOUNTANCY_FISCALYEAR_DEFAULT')) {
$sql .= " WHERE rowid = " . getDolGlobalInt('ACCOUNTANCY_FISCALYEAR_DEFAULT');
} else {
$sql .= " WHERE date_start < '" . $db->idate(dol_now()) . "' and date_end > '" . $db->idate(dol_now()) . "'";
}
$sql .= $db->plimit(1);
$sql = "SELECT date_start, date_end";
$sql .=" FROM ".MAIN_DB_PREFIX."accounting_fiscalyear ";
if (getDolGlobalInt('ACCOUNTANCY_FISCALYEAR_DEFAULT')) {
$sql .= " WHERE rowid = " . getDolGlobalInt('ACCOUNTANCY_FISCALYEAR_DEFAULT');
} else {
$sql .= " WHERE date_start < '" . $db->idate(dol_now()) . "' and date_end > '" . $db->idate(dol_now()) . "'";
}
$sql .= $db->plimit(1);
$res = $db->query($sql);
if ($db->num_rows($res) > 0) {

View File

@@ -32,7 +32,6 @@
require '../../main.inc.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formaccounting.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfiscalyear.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
@@ -881,14 +880,6 @@ if ($massactionbutton && $contextpage != 'poslist') {
$selectedfields .= $form->showCheckAddButtons('checkforselect', 1);
}
if (getDolGlobalInt('ACCOUNTANCY_FISCALYEAR_DEFAULT')) {
$fiscalYear = getDolGlobalInt('ACCOUNTANCY_FISCALYEAR_DEFAULT');
$useempty = 0;
} else {
$fiscalYear = 0;
$useempty = 1;
}
$moreforfilter = '';
$moreforfilter .= '<div class="divsearchfield">';
$moreforfilter .= $langs->trans('AccountingCategory').': ';

View File

@@ -27,7 +27,7 @@
// Load Dolibarr environment
require '../../main.inc.php';
require_once DOL_DOCUMENT_ROOT."/core/lib/admin.lib.php";
require_once '../lib/ai.lib.php';
require_once DOL_DOCUMENT_ROOT."/ai/lib/ai.lib.php";
/**
* @var Conf $conf
@@ -39,6 +39,8 @@ require_once '../lib/ai.lib.php';
$langs->loadLangs(array("admin", "website", "other"));
$arrayofaifeatures = getLitOfAIFeatures();
// Parameters
$action = GETPOST('action', 'aZ09');
$backtopage = GETPOST('backtopage', 'alpha');
@@ -81,18 +83,6 @@ $setupnotempty += count($formSetup->items);
$dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
// List of AI features
$arrayofaifeatures = array(
'textgenerationemail' => array('label' => $langs->trans('TextGeneration').' ('.$langs->trans("EmailContent").')', 'picto'=>'', 'status'=>'development'),
'textgenerationwebpage' => array('label' => $langs->trans('TextGeneration').' ('.$langs->trans("WebsitePage").')', 'picto'=>'', 'status'=>'development'),
'textgeneration' => array('label' => $langs->trans('TextGeneration').' ('.$langs->trans("Other").')', 'picto'=>'', 'status'=>'notused'),
'imagegeneration' => array('label' => 'ImageGeneration', 'picto'=>'', 'status'=>'notused'),
'videogeneration' => array('label' => 'VideoGeneration', 'picto'=>'', 'status'=>'notused'),
'audiogeneration' => array('label' => 'AudioGeneration', 'picto'=>'', 'status'=>'notused'),
'transcription' => array('label' => 'Transcription', 'picto'=>'', 'status'=>'notused'),
'translation' => array('label' => 'Translation', 'picto'=>'', 'status'=>'notused')
);
/*
* Actions
@@ -213,7 +203,7 @@ print load_fiche_titre($langs->trans($title), $linkback, 'title_setup');
// Configuration header
$head = aiAdminPrepareHead();
print dol_get_fiche_head($head, 'custom', $langs->trans($title), -1, "fa-microchip");
print dol_get_fiche_head($head, 'custom', $langs->trans($title), -1, "ai");
//$newbutton = '<a href="'.$_SERVER["PHP_SELF"].'?action=create">'.$langs->trans("New").'</a>';
$newbutton = '';
@@ -279,6 +269,7 @@ if ($action == 'edit' || $action == 'deleteproperty') {
$out .= '</td>';
$out .= '</tr>';
$out .= '<tr class="oddeven">';
$out .= '<td class="col-setup-title">';
$out .= '<span id="prePrompt" class="spanforparamtooltip">';
@@ -314,6 +305,7 @@ if ($action == 'edit' || $action == 'deleteproperty') {
$out .= $form->buttonsSaveCancel("Add", "");
$out .= '</form>';
$out .= '<br><br><br>';
print $out;
@@ -374,7 +366,7 @@ if ($action == 'edit' || $action == 'create' || $action == 'deleteproperty') {
$out .= '<tr>';
$out .= '<td></td>';
$out .= '<td>';
$out .= '<input type="submit" class="button small submitBtn reposition" name="modify" data-index="'.$key.'" value="'.dol_escape_htmltag($langs->trans("Modify")).'"/>';
$out .= '<input type="submit" class="button small submitBtn reposition" name="modify" data-index="'.$key.'" value="'.dol_escape_htmltag($langs->trans("Save")).'"/>';
$out .= ' &nbsp; ';
include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
@@ -383,6 +375,8 @@ if ($action == 'edit' || $action == 'create' || $action == 'deleteproperty') {
$formmail = new FormMail($db);
$htmlname = $key;
$out .= '<br><br>';
// Fill $out
include DOL_DOCUMENT_ROOT.'/core/tpl/formlayoutai.tpl.php';

View File

@@ -28,7 +28,8 @@
// Load Dolibarr environment
require '../../main.inc.php';
require_once DOL_DOCUMENT_ROOT."/core/lib/admin.lib.php";
require_once '../lib/ai.lib.php';
require_once DOL_DOCUMENT_ROOT."/core/class/doleditor.class.php";
require_once DOL_DOCUMENT_ROOT."/ai/lib/ai.lib.php";
/**
* @var Conf $conf
@@ -38,7 +39,9 @@ require_once '../lib/ai.lib.php';
* @var User $user
*/
$langs->loadLangs(array("admin"));
$langs->loadLangs(array("admin", "website", "other"));
$arrayofaifeatures = getLitOfAIFeatures();
// Parameters
$action = GETPOST('action', 'aZ09');
@@ -49,10 +52,7 @@ if (empty($action)) {
$action = 'edit';
}
$value = GETPOST('value', 'alpha');
$label = GETPOST('label', 'alpha');
$scandir = GETPOST('scan_dir', 'alpha');
$type = 'myobject';
$content = GETPOST('content');
$error = 0;
$setupnotempty = 0;
@@ -69,6 +69,7 @@ $formSetup = new FormSetup($db);
// List all available IA
$arrayofia = array(
'-1' => $langs->trans('SelectAService'),
'chatgpt' => 'ChatGPT',
'groq' => 'Groq',
'custom' => 'Custom'
@@ -89,14 +90,14 @@ foreach ($arrayofia as $ia => $ialabel) {
$item->nameText = $langs->trans("AI_API_KEY").' ('.$ialabel.')';
$item->defaultFieldValue = '';
$item->fieldParams['hideGenerateButton'] = 1;
$item->fieldParams['trClass'] = $ia;
$item->cssClass = 'minwidth500 text-security';
$item->fieldParams['trClass'] = 'iaservice '.$ia;
$item->cssClass = 'minwidth500 text-security input'.$ia;
$item = $formSetup->newItem('AI_API_'.strtoupper($ia).'_URL'); // Name of constant must end with _KEY so it is encrypted when saved into database.
$item->nameText = $langs->trans("AI_API_URL").' ('.$ialabel.')';
$item->defaultFieldValue = '';
$item->fieldParams['trClass'] = $ia;
$item->cssClass = 'minwidth500';
$item->fieldParams['trClass'] = 'iaservice '.$ia;
$item->cssClass = 'minwidth500 input'.$ia;
}
$setupnotempty = + count($formSetup->items);
@@ -140,7 +141,7 @@ print load_fiche_titre($langs->trans($title), $linkback, 'title_setup');
// Configuration header
$head = aiAdminPrepareHead();
print dol_get_fiche_head($head, 'settings', $langs->trans($title), -1, "fa-microchip");
print dol_get_fiche_head($head, 'settings', $langs->trans($title), -1, "ai");
if ($action == 'edit') {
@@ -160,8 +161,86 @@ if (empty($setupnotempty)) {
print '<br>'.$langs->trans("NothingToSetup");
}
print '<script type="text/javascript">
jQuery(document).ready(function() {
function showHideAIService(aiservice) {
console.log("We select the AI service "+aiservice);
jQuery(".iaservice").hide();
if (aiservice != "-1") {
jQuery(".iaservice."+aiservice).show();
}
}
jQuery("#AI_API_SERVICE").change(function() {
var aiservice = $(this).val();
showHideAIService(aiservice);
});
showHideAIService("'.getDolGlobalString("AI_API_SERVICE").'");
});
</script>';
// Page end
print dol_get_fiche_end();
if (getDolGlobalString("AI_API_SERVICE")) {
// Section to test
print '<form action="'.$_SERVER["PHP_SELF"].'" method="POST">';
print '<input type="hidden" name="token" value="'.newToken().'">';
print '<input type="hidden" name="action" value="add">';
print '<input type="hidden" name="backtopage" value="'.$backtopage.'">';
$functioncode = GETPOST('functioncode');
$out = '';
if ($functioncode) {
$labeloffeature = empty($arrayofaifeatures[GETPOST('functioncode')]['label']) ? 'Undefined' : $arrayofaifeatures[GETPOST('functioncode')]['label'];
//$out .= $langs->trans("Test").' '.$labeloffeature.'...<br><br>';
if (GETPOST('functioncode') == 'textgenerationemail') {
$key = 'textgenerationemail'; // The HTML ID of field to fill
include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
$showlinktoai = $key; // 'textgeneration', 'imagegeneration', ...
$showlinktoailabel = $langs->trans("Test").' '.$labeloffeature;
$showlinktolayout = 0;
$formmail = new FormMail($db);
$htmlname = $key;
// Fill $out
include DOL_DOCUMENT_ROOT.'/core/tpl/formlayoutai.tpl.php';
$out .= '<div id="'.$key.'"></div>';
} else {
$out .= $langs->trans("FeatureNotYetAvailable").'<br><br>';
$functioncode = '';
}
}
if (!$functioncode) {
// Combo list of AI features
$out .= '<select name="functioncode" id="functioncode" class="flat minwidth300" placeholder="Test feature">';
$out .= '<option value="-1">'.$langs->trans("SelectFeatureToTest").'</option>';
foreach ($arrayofaifeatures as $key => $val) {
$labelhtml = $langs->trans($arrayofaifeatures[$key]['label']).($arrayofaifeatures[$key]['status'] == 'notused' ? ' <span class="opacitymedium">('.$langs->trans("NotYetAvailable").')</span>' : "");
$labeltext = $langs->trans($arrayofaifeatures[$key]['label']);
$out .= '<option value="'.$key.'" data-html="'.dol_escape_htmltag($labelhtml).'"';
$out .= (GETPOST('functioncode') == $key ? ' selected="selected"' : '');
$out .= '>'.dol_escape_htmltag($labeltext).'</option>';
}
$out .= '</select>';
$out .= ajax_combobox("functioncode");
$out .= '<input class="button small" type="submit" name="testmode" value="'.$langs->trans("Test").'">';
}
print $out;
print '</form>';
}
llxFooter();
$db->close();

View File

@@ -81,15 +81,17 @@ $format = empty($jsonData['format']) ? '' : $jsonData['format'];
$generatedContent = $ai->generateContent($instructions, 'auto', $function, $format);
if (is_array($generatedContent) && $generatedContent['error']) {
if (is_null($generatedContent) || (is_array($generatedContent) && $generatedContent['error'])) {
// Output error
if (!empty($generatedContent['code']) && $generatedContent['code'] == 429) {
print "Quota or allowed period exceeded. Retry Later !";
} elseif ($generatedContent['code'] >= 400) {
} elseif (!empty($generatedContent['code']) && $generatedContent['code'] >= 400) {
print "Error : " . $generatedContent['message'];
print '<br><a href="'.DOL_MAIN_URL_ROOT.'/ai/admin/setup.php">'.$langs->trans('ErrorGoToModuleSetup').'</a>';
} else {
} elseif (!empty($generatedContent['message'])) {
print "Error returned by API call: " . $generatedContent['message'];
} else {
print "Error API returned no answer";
}
} else {
if ($function == 'textgenerationemail' || $function == 'textgenerationwebpage') {

View File

@@ -80,6 +80,8 @@ class Ai
*/
public function generateContent($instructions, $model = 'auto', $function = 'textgeneration', $format = '')
{
global $dolibarr_main_data_root;
if (empty($this->apiKey)) {
return array('error' => true, 'message' => 'API key is not defined for the AI enabled service ('.$this->apiService.')');
}
@@ -192,14 +194,14 @@ class Ai
if (isset($configurations[$function])) {
if (isset($configurations[$function]['prePrompt'])) {
$prePrompt = $configurations[$function]['prePrompt']; // TODO We can send prePrompt into a separated message with role system.
$prePrompt = $configurations[$function]['prePrompt'];
}
if (isset($configurations[$function]['postPrompt'])) {
$postPrompt = $configurations[$function]['postPrompt'];
}
}
$fullInstructions = ($prePrompt ? $prePrompt.' ' : '').$instructions.($postPrompt ? '. '.$postPrompt : '');
$fullInstructions = $instructions.($postPrompt ? (preg_match('/[\.\!\?]$/', $instructions) ? '' : '.').' '.$postPrompt : '');
// Set payload string
/*{
@@ -224,18 +226,48 @@ class Ai
"temperature": 0.7,
"top_p": 0.95
}*/
$payload = json_encode([
'messages' => [
['role' => 'user', 'content' => $fullInstructions]
],
'model' => $model,
//'stream' => false
]);
$headers = ([
$arrayforpayload = array(
'messages' => array(array('role' => 'user', 'content' => $fullInstructions)),
'model' => $model,
);
// Add a system message
$addDateTimeContext = false;
if ($addDateTimeContext) { // @phpstan-ignore-line
$prePrompt = ($prePrompt ? $prePrompt.(preg_match('/[\.\!\?]$/', $prePrompt) ? '' : '.').' ' : '').'Today we are '.dol_print_date(dol_now(), 'dayhourtext');
}
if ($prePrompt) {
$arrayforpayload['messages'][] = array('role' => 'system', 'content' => $prePrompt);
}
/*
$arrayforpayload['temperature'] = 0.7;
$arrayforpayload['max_tokens'] = -1;
$arrayforpayload['stream'] = false;
*/
$payload = json_encode($arrayforpayload);
$headers = array(
'Authorization: Bearer ' . $this->apiKey,
'Content-Type: application/json'
]);
);
if (getDolGlobalString("AI_DEBUG")) {
if (@is_writable($dolibarr_main_data_root)) { // Avoid fatal error on fopen with open_basedir
$outputfile = $dolibarr_main_data_root."/dolibarr_ai.log";
$fp = fopen($outputfile, "w"); // overwrite
if ($fp) {
fwrite($fp, var_export($headers, true)."\n");
fwrite($fp, var_export($payload, true)."\n");
fclose($fp);
dolChmod($outputfile);
}
}
}
$localurl = 2; // Accept both local and external endpoints
$response = getURLContent($this->apiEndpoint, 'POST', $payload, 1, $headers, array('http', 'https'), $localurl);
@@ -248,7 +280,17 @@ class Ai
}
if (getDolGlobalString("AI_DEBUG")) {
dol_syslog("response content = ".var_export($response['content'], true));
if (@is_writable($dolibarr_main_data_root)) { // Avoid fatal error on fopen with open_basedir
$outputfile = $dolibarr_main_data_root."/dolibarr_ai.log";
$fp = fopen($outputfile, "a");
if ($fp) {
fwrite($fp, var_export((empty($response['content']) ? 'No content result' : $response['content']), true)."\n");
fclose($fp);
dolChmod($outputfile);
}
}
}
// Decode JSON response

View File

@@ -23,6 +23,30 @@
* \brief Library files with common functions for Ai
*/
/**
* Prepare admin pages header
*
* @return array<string,array<string,string>>
*/
function getLitOfAIFeatures()
{
global $langs;
$arrayofaifeatures = array(
'textgenerationemail' => array('label' => $langs->trans('TextGeneration').' ('.$langs->trans("EmailContent").')', 'picto'=>'', 'status'=>'dolibarr'),
'textgenerationwebpage' => array('label' => $langs->trans('TextGeneration').' ('.$langs->trans("WebsitePage").')', 'picto'=>'', 'status'=>'dolibarr'),
'textgeneration' => array('label' => $langs->trans('TextGeneration').' ('.$langs->trans("Other").')', 'picto'=>'', 'status'=>'notused'),
'imagegeneration' => array('label' => 'ImageGeneration', 'picto'=>'', 'status'=>'notused'),
'videogeneration' => array('label' => 'VideoGeneration', 'picto'=>'', 'status'=>'notused'),
'audiogeneration' => array('label' => 'AudioGeneration', 'picto'=>'', 'status'=>'notused'),
'transcription' => array('label' => 'Transcription', 'picto'=>'', 'status'=>'notused'),
'translation' => array('label' => 'Translation', 'picto'=>'', 'status'=>'notused')
);
return $arrayofaifeatures;
}
/**
* Prepare admin pages header
*

View File

@@ -53,10 +53,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/infobox.class.php';
$boxid = GETPOSTINT('boxid');
$boxorder = GETPOST('boxorder');
$zone = GETPOST('zone'); // Can be key for zone
if ($zone !== '') {
$zone = (int) $zone;
}
$zone = GETPOST('zone'); // Can be '0' or '1' or 'pagename'...
$userid = GETPOSTINT('userid');
// Security check
@@ -91,7 +88,7 @@ if ($boxorder && $zone != '' && $userid > 0) {
// boxorder value is the target order: "A:idboxA1,idboxA2,A-B:idboxB1,idboxB2,B"
dol_syslog("AjaxBox boxorder=".$boxorder." zone=".$zone." userid=".$userid, LOG_DEBUG);
$result = InfoBox::saveboxorder($db, (int) $zone, $boxorder, $userid);
$result = InfoBox::saveboxorder($db, $zone, $boxorder, $userid);
if ($result > 0) {
$langs->load("boxes");
if (!GETPOST('closing')) {

View File

@@ -1483,24 +1483,26 @@ class CMailFile
$outputfile = $dolibarr_main_data_root."/dolibarr_mail.log";
$fp = fopen($outputfile, "w"); // overwrite
if ($this->sendmode == 'mail') {
fwrite($fp, $this->headers);
fwrite($fp, $this->eol); // This eol is added by the mail function, so we add it in log
fwrite($fp, $this->message);
} elseif ($this->sendmode == 'smtps') {
fwrite($fp, $this->smtps->log); // this->smtps->log is filled only if MAIN_MAIL_DEBUG was set to on
} elseif ($this->sendmode == 'swiftmailer') {
fwrite($fp, "smtpheader=\n".$this->message->getHeaders()->toString()."\n");
fwrite($fp, $this->logger->dump()); // this->logger is filled only if MAIN_MAIL_DEBUG was set to on
}
if ($fp) {
if ($this->sendmode == 'mail') {
fwrite($fp, $this->headers);
fwrite($fp, $this->eol); // This eol is added by the mail function, so we add it in log
fwrite($fp, $this->message);
} elseif ($this->sendmode == 'smtps') {
fwrite($fp, $this->smtps->log); // this->smtps->log is filled only if MAIN_MAIL_DEBUG was set to on
} elseif ($this->sendmode == 'swiftmailer') {
fwrite($fp, "smtpheader=\n".$this->message->getHeaders()->toString()."\n");
fwrite($fp, $this->logger->dump()); // this->logger is filled only if MAIN_MAIL_DEBUG was set to on
}
fclose($fp);
dolChmod($outputfile);
fclose($fp);
dolChmod($outputfile);
// Move dolibarr_mail.log into a dolibarr_mail.log.v123456789
if (getDolGlobalInt('MAIN_MAIL_DEBUG_LOG_WITH_DATE')) {
require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
archiveOrBackupFile($outputfile, getDolGlobalInt('MAIN_MAIL_DEBUG_LOG_WITH_DATE'));
// Move dolibarr_mail.log into a dolibarr_mail.log.v123456789
if (getDolGlobalInt('MAIN_MAIL_DEBUG_LOG_WITH_DATE')) {
require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
archiveOrBackupFile($outputfile, getDolGlobalInt('MAIN_MAIL_DEBUG_LOG_WITH_DATE'));
}
}
}
}

View File

@@ -10360,9 +10360,9 @@ abstract class CommonObject
/**
* Create object in the database
*
* @param User $user User that creates
* @param int<0,1> $notrigger 0=launch triggers after, 1=disable triggers
* @return int<-1,max> Return integer <0 if KO, Id of created object if OK
* @param User $user User that creates
* @param int<0,1> $notrigger 0=launch triggers after, 1=disable triggers
* @return int<-1,max> Return integer <0 if KO, Id of created object if OK
*/
public function createCommon(User $user, $notrigger = 0)
{
@@ -10382,6 +10382,7 @@ abstract class CommonObject
$fieldvalues['date_creation'] = $this->db->idate($now);
$this->date_creation = $this->db->idate($now);
}
// For backward compatibility, if a property ->fk_user_creat exists and not filled.
if (array_key_exists('fk_user_creat', $fieldvalues) && !($fieldvalues['fk_user_creat'] > 0)) {
$fieldvalues['fk_user_creat'] = $user->id;
$this->fk_user_creat = $user->id;

View File

@@ -1493,6 +1493,7 @@ class FormMail extends Form
* @param string $format Format for output ('', 'html', ...)
* @param string $htmlContent HTML name of WYSIWYG field
* @return string HTML code to ask AI instruction and autofill result
* TODO Move into a file html.formai.class.php
*/
public function getSectionForAIPrompt($function = 'textgeneration', $format = '', $htmlContent = 'message')
{
@@ -1502,7 +1503,7 @@ class FormMail extends Form
$htmlContent = preg_replace('/[^a-z0-9_]/', '', $htmlContent);
$out = '<div id="ai_input'.$htmlContent.'" class="hidden paddingtop paddingbottom">';
$out = '<div id="ai_input'.$htmlContent.'" class="ai_input'.$htmlContent.' hidden paddingtop paddingbottom">';
$out .= '<input type="text" class="quatrevingtpercent" id="ai_instructions'.$htmlContent.'" name="instruction" placeholder="'.$langs->trans("EnterYourAIPromptHere").'..." />';
$out .= '<input id="generate_button'.$htmlContent.'" type="button" class="button smallpaddingimp" value="'.$langs->trans('Generate').'"/>';
$out .= '<div id="ai_status_message'.$htmlContent.'" class="fieldrequired hideobject marginrightonly margintoponly">';
@@ -1526,7 +1527,7 @@ class FormMail extends Form
});
$('#generate_button".$htmlContent."').click(function() {
console.log('We click on generate_button".$htmlContent." ai button');
console.log('We click on generate_button".$htmlContent." ai button, so we make an ajax on url /ai/ajax/generate_content.php');
var instructions = $('#ai_instructions".$htmlContent."').val();
var timeoutfinished = 0;
@@ -1589,49 +1590,50 @@ class FormMail extends Form
CKEDITOR.instances.".$htmlContent.".setReadOnly(1);
}
$.ajax({
url: '". DOL_URL_ROOT."/ai/ajax/generate_content.php?token=".currentToken()."',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({
'format': '".dol_escape_js($format)."', /* the format for output */
'function': '".dol_escape_js($function)."', /* the AI feature to call */
'instructions': instructions, /* the prompt string */
}),
success: function(response) {
console.log('Add response into field \'#".$htmlContent."\': '+response);
$.ajax({
url: '". DOL_URL_ROOT."/ai/ajax/generate_content.php?token=".currentToken()."',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({
'format': '".dol_escape_js($format)."', /* the format for output */
'function': '".dol_escape_js($function)."', /* the AI feature to call */
'instructions': instructions, /* the prompt string */
}),
success: function(response) {
console.log('Add response into field \'#".$htmlContent."\': '+response);
jQuery('#".$htmlContent."').val(response); // If #htmlcontent is a input name or textarea
jQuery('#".$htmlContent."').html(response); // If #htmlContent is a div
//jQuery('#".$htmlContent."preview').val(response);
jQuery('#".$htmlContent."').val(response); // If #htmlcontent is a input name or textarea
jQuery('#".$htmlContent."').html(response); // If #htmlContent is a div
//jQuery('#".$htmlContent."preview').val(response);
if (CKEDITOR.instances) {
var editorInstance = CKEDITOR.instances.".$htmlContent.";
if (editorInstance) {
editorInstance.setReadOnly(0);
editorInstance.setData(response);
if (CKEDITOR.instances) {
var editorInstance = CKEDITOR.instances.".$htmlContent.";
if (editorInstance) {
editorInstance.setReadOnly(0);
editorInstance.setData(response);
}
//var editorInstancepreview = CKEDITOR.instances.".$htmlContent."preview;
//if (editorInstancepreview) {
// editorInstancepreview.setData(response);
//}
}
//var editorInstancepreview = CKEDITOR.instances.".$htmlContent."preview;
//if (editorInstancepreview) {
// editorInstancepreview.setData(response);
//}
}
// remove readonly
$('#ai_instructions".$htmlContent."').val('');
// remove readonly
$('#ai_instructions".$htmlContent."').val('');
apicallfinished = 1;
if (timeoutfinished) {
apicallfinished = 1;
if (timeoutfinished) {
$('#ai_status_message".$htmlContent."').hide();
}
},
error: function(xhr, status, error) {
alert(error);
console.error('error ajax', status, error);
$('#ai_status_message".$htmlContent."').hide();
}
},
error: function(xhr, status, error) {
alert(error);
console.error('error ajax', status, error);
$('#ai_status_message".$htmlContent."').hide();
}
});
});
}
});
});
</script>
@@ -1658,7 +1660,7 @@ class FormMail extends Form
$websitepage = new WebsitePage($this->db);
$arrayofblogs = $websitepage->fetchAll('', 'DESC', 'date_creation', 0, 0, array('type_container' => 'blogpost'));
$out = '<div id="template-selector" class="email-layout-container hidden" style="display:none;">';
$out = '<div id="template-selector" class="template-selector email-layout-container hidden" style="display:none;">';
// Define list of email layouts to use
$layouts = array(

View File

@@ -217,11 +217,11 @@ class InfoBox
/**
* Save order of boxes for area and user
*
* @param DoliDB $dbs Database handler
* @param int $zone Key of area (0 for Homepage, ...)
* @param string $boxorder List of boxes with correct order 'A:123,456,...-B:789,321...'
* @param int $userid Id of user
* @return int Return integer <0 if KO, 0=Nothing done, > 0 if OK
* @param DoliDB $dbs Database handler
* @param int|string $zone Key of area ('0' for Homepage, '1', 'pagename', ...)
* @param string $boxorder List of boxes with correct order 'A:123,456,...-B:789,321...'
* @param int $userid Id of user
* @return int Return integer <0 if KO, 0=Nothing done, > 0 if OK
*/
public static function saveboxorder($dbs, $zone, $boxorder, $userid = 0)
{
@@ -252,6 +252,10 @@ class InfoBox
return -3;
}
if (!is_numeric($zone)) {
$zone = '0'; // Force $zone to a numeric value string
}
// Delete all lines
$sql = "DELETE FROM ".$dbs->prefix()."boxes";
$sql .= " WHERE entity = ".$conf->entity;

View File

@@ -123,70 +123,87 @@ class TimeSpent extends CommonObject
'datec' => array('type' => 'datetime', 'label' => 'datec', 'enabled' => 1, 'position' => 16, 'notnull' => 0, 'visible' => -1,),
'note' => array('type' => 'text', 'label' => 'note', 'enabled' => 1, 'position' => 18, 'notnull' => 0, 'visible' => -1,),
);
/**
* @var int
*/
public $rowid;
/**
* @var string
*/
public $import_key;
/**
* @var int
*/
public $fk_element;
/**
* @var string
*/
public $elementtype;
/**
* @var int|string
*/
public $element_date;
/**
* @var int
*/
public $element_datehour;
/**
* @var int
*/
public $element_date_withhour;
/**
* @var int Note seems to be int (seconds) even if declared as double in DB.
*/
public $element_duration;
/**
* @var int
*/
public $fk_product;
/**
* @var int
*/
public $fk_user;
/**
* @var float
*/
public $thm;
/**
* @var int
*/
public $invoice_id;
/**
* @var int
*/
public $invoice_line_id;
/**
* @var int
*/
public $intervention_id;
/**
* @var int
*/
public $intervention_line_id;
/**
* @var string
* @var ?int Date creation
*/
public $datec;
/**
* @var string
*/

View File

@@ -51,6 +51,10 @@ function getURLContent($url, $postorget = 'GET', $param = '', $followlocation =
dol_syslog("getURLContent postorget=".$postorget." URL=".$url." param=".$param);
if (!function_exists('curl_init')) {
return array('http_code' => 500, 'content' => '', 'curl_error_no' => 1, 'curl_error_msg' => 'PHP curl library must be installed');
}
//setting the curl parameters.
$ch = curl_init();

View File

@@ -182,13 +182,12 @@ class modAdherent extends DolibarrModules
[
"MEMBER_ADDON_PDF_ODT_PATH",
"chaine",
"DOL_DATA_ROOT/doctemplates/members",
"DOL_DATA_ROOT".($conf->entity > 1 ? '/'.$conf->entity : '')."/doctemplates/members",
"",
0,
],
];
// Boxes
//-------
$this->boxes = array(
@@ -439,8 +438,8 @@ class modAdherent extends DolibarrModules
// ODT template
/*
$src=DOL_DOCUMENT_ROOT.'/install/doctemplates/orders/template_order.odt';
$dirodt=DOL_DATA_ROOT.'/doctemplates/orders';
$src=DOL_DOCUMENT_ROOT.'/install/doctemplates/members/template_member.odt';
$dirodt=DOL_DATA_ROOT.($conf->entity > 1 ? '/'.$conf->entity : '').'/doctemplates/members';
$dest=$dirodt.'/template_order.odt';
if (file_exists($src) && ! file_exists($dest)) {

View File

@@ -128,7 +128,7 @@ class modBom extends DolibarrModules
$this->const = array(
1 => array('BOM_ADDON_PDF', 'chaine', 'generic_bom_odt', 'Name of PDF model of BOM', 0),
2 => array('BOM_ADDON', 'chaine', 'mod_bom_standard', 'Name of numbering rules of BOM', 0),
3 => array('BOM_ADDON_PDF_ODT_PATH', 'chaine', 'DOL_DATA_ROOT/doctemplates/boms', '', 0)
3 => array('BOM_ADDON_PDF_ODT_PATH', 'chaine', 'DOL_DATA_ROOT'.($conf->entity > 1 ? '/'.$conf->entity : '').'/doctemplates/boms', '', 0)
);
// Some keys to add into the overwriting translation tables
@@ -480,7 +480,7 @@ class modBom extends DolibarrModules
// ODT template
$src = DOL_DOCUMENT_ROOT.'/install/doctemplates/boms/template_bom.odt';
$dirodt = DOL_DATA_ROOT.'/doctemplates/boms';
$dirodt = DOL_DATA_ROOT.($conf->entity > 1 ? '/'.$conf->entity : '').'/doctemplates/boms';
$dest = $dirodt.'/template_bom.odt';
if (file_exists($src) && !file_exists($dest)) {

View File

@@ -87,7 +87,7 @@ class modContrat extends DolibarrModules
[
"CONTRACT_ADDON_PDF_ODT_PATH",
"chaine",
"DOL_DATA_ROOT/doctemplates/contracts",
"DOL_DATA_ROOT".($conf->entity > 1 ? '/'.$conf->entity : '')."/doctemplates/contracts",
"",
0,
],
@@ -231,7 +231,7 @@ class modContrat extends DolibarrModules
//ODT template
$src = DOL_DOCUMENT_ROOT.'/install/doctemplates/contracts/template_contract.odt';
$dirodt = DOL_DATA_ROOT.'/doctemplates/contracts';
$dirodt = DOL_DATA_ROOT.($conf->entity > 1 ? '/'.$conf->entity : '').'/doctemplates/contracts';
$dest = $dirodt.'/template_contract.odt';
if (file_exists($src) && !file_exists($dest)) {

View File

@@ -100,7 +100,7 @@ class modExpedition extends DolibarrModules
[
"EXPEDITION_ADDON_PDF_ODT_PATH",
"chaine",
"DOL_DATA_ROOT/doctemplates/shipments",
"DOL_DATA_ROOT".($conf->entity > 1 ? '/'.$conf->entity : '')."/doctemplates/shipments",
"",
0,
],
@@ -121,7 +121,7 @@ class modExpedition extends DolibarrModules
[
"DELIVERY_ADDON_PDF_ODT_PATH",
"chaine",
"DOL_DATA_ROOT/doctemplates/deliveries",
"DOL_DATA_ROOT".($conf->entity > 1 ? '/'.$conf->entity : '')."/doctemplates/deliveries",
"",
0,
],
@@ -133,6 +133,7 @@ class modExpedition extends DolibarrModules
0,
],
];
// Boxes
$this->boxes = array(
0 => array('file'=>'box_shipments.php', 'enabledbydefaulton'=>'Home'),
@@ -344,7 +345,7 @@ class modExpedition extends DolibarrModules
//ODT template
$src = DOL_DOCUMENT_ROOT.'/install/doctemplates/shipments/template_shipment.odt';
$dirodt = DOL_DATA_ROOT.'/doctemplates/shipments';
$dirodt = DOL_DATA_ROOT.($conf->entity > 1 ? '/'.$conf->entity : '').'/doctemplates/shipments';
$dest = $dirodt.'/template_shipment.odt';
if (file_exists($src) && !file_exists($dest)) {

View File

@@ -118,7 +118,7 @@ class modFournisseur extends DolibarrModules
// Add ability ODT for Supplier orders
$this->const[$r][0] = "SUPPLIER_ORDER_ADDON_PDF_ODT_PATH";
$this->const[$r][1] = "chaine";
$this->const[$r][2] = "DOL_DATA_ROOT/doctemplates/supplier_orders";
$this->const[$r][2] = "DOL_DATA_ROOT".($conf->entity > 1 ? '/'.$conf->entity : '')."/doctemplates/supplier_orders";
$this->const[$r][3] = '';
$this->const[$r][4] = 0;
$r++;
@@ -126,7 +126,7 @@ class modFournisseur extends DolibarrModules
// Add ability ODT for Supplier Invoices
$this->const[$r][0] = "SUPPLIER_INVOICE_ADDON_PDF_ODT_PATH";
$this->const[$r][1] = "chaine";
$this->const[$r][2] = "";
$this->const[$r][2] = "DOL_DATA_ROOT".($conf->entity > 1 ? '/'.$conf->entity : '')."/doctemplates/supplier_invoices";
$this->const[$r][3] = "";
$this->const[$r][4] = 0;
$r++;
@@ -973,7 +973,7 @@ class modFournisseur extends DolibarrModules
//ODT template for Supplier Orders
$src = DOL_DOCUMENT_ROOT.'/install/doctemplates/supplier_orders/template_supplier_order.odt';
$dirodt = DOL_DATA_ROOT.'/doctemplates/supplier_orders';
$dirodt = DOL_DATA_ROOT.($conf->entity > 1 ? '/'.$conf->entity : '').'/doctemplates/supplier_orders';
$dest = $dirodt.'/template_supplier_order.odt';
if (file_exists($src) && !file_exists($dest)) {
@@ -994,7 +994,7 @@ class modFournisseur extends DolibarrModules
//ODT template for Supplier Invoice
$src = DOL_DOCUMENT_ROOT.'/install/doctemplates/supplier_invoices/template_supplier_invoices.odt';
$dirodt = DOL_DATA_ROOT.'/doctemplates/supplier_invoices';
$dirodt = DOL_DATA_ROOT.($conf->entity > 1 ? '/'.$conf->entity : '').'/doctemplates/supplier_invoices';
$dest = $dirodt.'/template_supplier_invoices.odt';
if (file_exists($src) && !file_exists($dest)) {

View File

@@ -111,7 +111,7 @@ class modHoliday extends DolibarrModules
$this->const[$r][0] = "HOLIDAY_ADDON_PDF_ODT_PATH";
$this->const[$r][1] = "chaine";
$this->const[$r][2] = "DOL_DATA_ROOT/doctemplates/holiday";
$this->const[$r][2] = "DOL_DATA_ROOT".($conf->entity > 1 ? '/'.$conf->entity : '')."/doctemplates/holiday";
$this->const[$r][3] = "";
$this->const[$r][4] = 0;
$r++;
@@ -308,7 +308,7 @@ class modHoliday extends DolibarrModules
//ODT template
/*$src=DOL_DOCUMENT_ROOT.'/install/doctemplates/holiday/template_holiday.odt';
$dirodt=DOL_DATA_ROOT.'/doctemplates/holiday';
$dirodt=DOL_DATA_ROOT.($conf->entity > 1 ? '/'.$conf->entity : '').'/doctemplates/holiday';
$dest=$dirodt.'/template_order.odt';
if (file_exists($src) && ! file_exists($dest))

View File

@@ -138,7 +138,7 @@ class modMrp extends DolibarrModules
$this->const = array(
//1=>array('MRP_MO_ADDON_PDF', 'chaine', 'vinci', 'Name of default PDF model of MO', 0),
2=>array('MRP_MO_ADDON', 'chaine', 'mod_mo_standard', 'Name of numbering rules of MO', 0),
3=>array('MRP_MO_ADDON_PDF_ODT_PATH', 'chaine', 'DOL_DATA_ROOT/doctemplates/mrps', '', 0)
3=>array('MRP_MO_ADDON_PDF_ODT_PATH', 'chaine', 'DOL_DATA_ROOT'.($conf->entity > 1 ? '/'.$conf->entity : '').'/doctemplates/mrps', '', 0)
);
// Some keys to add into the overwriting translation tables
@@ -534,7 +534,7 @@ class modMrp extends DolibarrModules
// ODT template
$src = DOL_DOCUMENT_ROOT.'/install/doctemplates/mrps/template_mo.odt';
$dirodt = DOL_DATA_ROOT.'/doctemplates/mrps';
$dirodt = DOL_DATA_ROOT.($conf->entity > 1 ? '/'.$conf->entity : '').'/doctemplates/mrps';
$dest = $dirodt.'/template_mo.odt';
if (file_exists($src) && !file_exists($dest)) {

View File

@@ -92,7 +92,7 @@ class modProjet extends DolibarrModules
[
"PROJECT_ADDON_PDF_ODT_PATH",
"chaine",
"DOL_DATA_ROOT/doctemplates/projects",
"DOL_DATA_ROOT".($conf->entity > 1 ? '/'.$conf->entity : '')."/doctemplates/projects",
"",
0,
],
@@ -113,7 +113,7 @@ class modProjet extends DolibarrModules
[
"PROJECT_TASK_ADDON_PDF_ODT_PATH",
"chaine",
"DOL_DATA_ROOT/doctemplates/tasks",
"DOL_DATA_ROOT".($conf->entity > 1 ? '/'.$conf->entity : '')."/doctemplates/tasks",
"",
0,
],
@@ -380,7 +380,7 @@ class modProjet extends DolibarrModules
//ODT template for project
$src = DOL_DOCUMENT_ROOT.'/install/doctemplates/projects/template_project.odt';
$dirodt = DOL_DATA_ROOT.'/doctemplates/projects';
$dirodt = DOL_DATA_ROOT.($conf->entity > 1 ? '/'.$conf->entity : '').'/doctemplates/projects';
$dest = $dirodt.'/template_project.odt';
if (file_exists($src) && !file_exists($dest)) {
@@ -396,7 +396,7 @@ class modProjet extends DolibarrModules
//ODT template for tasks
$src = DOL_DOCUMENT_ROOT.'/install/doctemplates/tasks/template_task_summary.odt';
$dirodt = DOL_DATA_ROOT.'/doctemplates/tasks';
$dirodt = DOL_DATA_ROOT.($conf->entity > 1 ? '/'.$conf->entity : '').'/doctemplates/tasks';
$dest = $dirodt.'/template_task_summary.odt';
if (file_exists($src) && !file_exists($dest)) {

View File

@@ -92,7 +92,7 @@ class modReception extends DolibarrModules
$this->const[$r][0] = "RECEPTION_ADDON_PDF_ODT_PATH";
$this->const[$r][1] = "chaine";
$this->const[$r][2] = "DOL_DATA_ROOT/doctemplates/receptions";
$this->const[$r][2] = "DOL_DATA_ROOT".($conf->entity > 1 ? '/'.$conf->entity : '')."/doctemplates/receptions";
$this->const[$r][3] = "";
$this->const[$r][4] = 0;
$r++;
@@ -267,7 +267,7 @@ class modReception extends DolibarrModules
//ODT template
$src = DOL_DOCUMENT_ROOT.'/install/doctemplates/reception/template_reception.odt';
$dirodt = DOL_DATA_ROOT.'/doctemplates/reception';
$dirodt = DOL_DATA_ROOT.($conf->entity > 1 ? '/'.$conf->entity : '').'/doctemplates/reception';
$dest = $dirodt.'/template_reception.odt';
if (file_exists($src) && !file_exists($dest)) {

View File

@@ -102,7 +102,7 @@ class modSociete extends DolibarrModules
$this->const[$r][0] = "COMPANY_ADDON_PDF_ODT_PATH";
$this->const[$r][1] = "chaine";
$this->const[$r][2] = "DOL_DATA_ROOT/doctemplates/thirdparties";
$this->const[$r][2] = "DOL_DATA_ROOT".($conf->entity > 1 ? '/'.$conf->entity : '')."/doctemplates/thirdparties";
$this->const[$r][3] = "";
$this->const[$r][4] = 0;
$r++;
@@ -1034,7 +1034,7 @@ class modSociete extends DolibarrModules
//ODT template
$src = DOL_DOCUMENT_ROOT.'/install/doctemplates/thirdparties/template_thirdparty.odt';
$dirodt = DOL_DATA_ROOT.'/doctemplates/thirdparties';
$dirodt = DOL_DATA_ROOT.($conf->entity > 1 ? '/'.$conf->entity : '').'/doctemplates/thirdparties';
$dest = $dirodt.'/template_thirdparty.odt';
if (file_exists($src) && !file_exists($dest)) {

View File

@@ -97,14 +97,14 @@ class modStock extends DolibarrModules
$r++;
$this->const[$r][0] = "STOCK_ADDON_PDF_ODT_PATH";
$this->const[$r][1] = "chaine";
$this->const[$r][2] = "DOL_DATA_ROOT/doctemplates/stocks";
$this->const[$r][2] = "DOL_DATA_ROOT".($conf->entity > 1 ? '/'.$conf->entity : '')."/doctemplates/stocks";
$this->const[$r][3] = "";
$this->const[$r][4] = 0;
$r++;
$this->const[$r][0] = "MOUVEMENT_ADDON_PDF_ODT_PATH";
$this->const[$r][1] = "chaine";
$this->const[$r][2] = "DOL_DATA_ROOT/doctemplates/stocks/movements";
$this->const[$r][2] = "DOL_DATA_ROOT".($conf->entity > 1 ? '/'.$conf->entity : '')."/doctemplates/stocks/movements";
$this->const[$r][3] = "";
$this->const[$r][4] = 0;
@@ -554,7 +554,7 @@ class modStock extends DolibarrModules
//ODT template
$src = DOL_DOCUMENT_ROOT.'/install/doctemplates/stocks/template_warehouse.odt';
$dirodt = DOL_DATA_ROOT.'/doctemplates/stocks';
$dirodt = DOL_DATA_ROOT.($conf->entity > 1 ? '/'.$conf->entity : '').'/doctemplates/stocks';
$dest = $dirodt.'/template_warehouse.odt';
if (file_exists($src) && !file_exists($dest)) {

View File

@@ -93,7 +93,7 @@ class modSupplierProposal extends DolibarrModules
$this->const[$r][0] = "SUPPLIER_PROPOSAL_ADDON_PDF_ODT_PATH";
$this->const[$r][1] = "chaine";
$this->const[$r][2] = "DOL_DATA_ROOT/doctemplates/supplier_proposals";
$this->const[$r][2] = "DOL_DATA_ROOT".($conf->entity > 1 ? '/'.$conf->entity : '')."/doctemplates/supplier_proposals";
$this->const[$r][3] = "";
$this->const[$r][4] = 0;
@@ -164,7 +164,7 @@ class modSupplierProposal extends DolibarrModules
//ODT template
$src = DOL_DOCUMENT_ROOT.'/install/doctemplates/supplier_proposals/template_supplier_proposal.odt';
$dirodt = DOL_DATA_ROOT.'/doctemplates/supplier_proposals';
$dirodt = DOL_DATA_ROOT.($conf->entity > 1 ? '/'.$conf->entity : '').'/doctemplates/supplier_proposals';
$dest = $dirodt.'/template_supplier_proposal.odt';
if (file_exists($src) && !file_exists($dest)) {

View File

@@ -110,7 +110,7 @@ class modTicket extends DolibarrModules
$this->const = array(
1 => array('TICKET_ENABLE_PUBLIC_INTERFACE', 'chaine', '0', 'Enable ticket public interface', 0),
2 => array('TICKET_ADDON', 'chaine', 'mod_ticket_simple', 'Ticket ref module', 0),
3 => array('TICKET_ADDON_PDF_ODT_PATH', 'chaine', 'DOL_DATA_ROOT/doctemplates/tickets', 'Ticket templates ODT/ODS directory for templates', 0),
3 => array('TICKET_ADDON_PDF_ODT_PATH', 'chaine', 'DOL_DATA_ROOT'.($conf->entity > 1 ? '/'.$conf->entity : '').'/doctemplates/tickets', 'Ticket templates ODT/ODS directory for templates', 0),
4 => array('TICKET_AUTO_READ_WHEN_CREATED_FROM_BACKEND', 'chaine', 0, 'Automatically mark ticket as read when created from backend', 0),
5 => array('TICKET_DELAY_BEFORE_FIRST_RESPONSE', 'chaine', '0', 'Maximum wanted elapsed time before a first answer to a ticket (in hours). Display a warning in tickets list if not respected.', 0),
6 => array('TICKET_DELAY_SINCE_LAST_RESPONSE', 'chaine', '0', 'Maximum wanted elapsed time between two answers on the same ticket (in hours). Display a warning in tickets list if not respected.', 0),
@@ -414,7 +414,7 @@ class modTicket extends DolibarrModules
//ODT template
$src = DOL_DOCUMENT_ROOT.'/install/doctemplates/tickets/template_ticket.odt';
$dirodt = DOL_DATA_ROOT.'/doctemplates/tickets';
$dirodt = DOL_DATA_ROOT.($conf->entity > 1 ? '/'.$conf->entity : '').'/doctemplates/tickets';
$dest = $dirodt.'/template_ticket.odt';
if (file_exists($src) && !file_exists($dest)) {

View File

@@ -15,26 +15,22 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* Need to have the following variables defined:
* $conf
* $formmail
* $formwebsite (optional)
* $showlinktolayout='emailing', 'email', 'websitepage', ...
* $showlinktolayoutlabel='...'
* $showlinktoai ('' or 'textgeneration', 'textgenerationemail', 'textgenerationwebpage', ...)
* $showlinktoailabel='...'
* $htmlname
*/
/**
/**
* @var Conf $conf
* @var ?FormMail $formmail
* @var ?FormWebsite $formwebsite
* @var string $htmlname
* @var string $showlinktolayout
* @var string $showlinktolayoutlabel
* @var ?FormMail $formmail
* @var ?FormWebsite $formwebsite
* @var string $htmlname
* @var string $showlinktolayout 'emailing', 'email', 'websitepage', ...
* @var string $showlinktolayoutlabel '...'
* @var string $showlinktoai '' or 'textgeneration', 'textgenerationemail', 'textgenerationwebpage', ...
* @var string $showlinktoailabel '...'
* @var string $htmlname
* @var ?string $out
*/
// Protection to avoid direct call of template
//Protection to avoid direct call of template
if (empty($conf) || !is_object($conf)) {
print "Error, template page can't be called as URL";
exit(1);
@@ -59,11 +55,12 @@ if (empty($htmlname)) {
@phan-var-force ?string $out
';
if (!isset($out)) {
if (!isset($out)) { // Init to empty string if not defined
$out = '';
}
// Add link to add layout
if ($showlinktolayout) {
if ($showlinktolayout) { // May be set only if MAIN_EMAIL_USE_LAYOUT is set
$out .= '<a href="#" id="linkforlayouttemplates" class="notasortlink inline-block alink marginrightonly">';
$out .= img_picto($showlinktolayoutlabel, 'layout', 'class="paddingrightonly"');
$out .= $showlinktolayoutlabel.'...';
@@ -72,10 +69,10 @@ if ($showlinktolayout) {
$out .= '<script>
$(document).ready(function() {
$("#linkforlayouttemplates").click(function() {
console.log("We click on linkforlayouttemplates");
console.log("We click on linkforlayouttemplates, we toggle .template-selector");
event.preventDefault();
jQuery("#template-selector").toggle();
jQuery("#ai_input'.$htmlname.'").hide();
jQuery(".template-selector").toggle();
jQuery(".ai_input'.$htmlname.'").hide();
jQuery("#pageContent").show(); // May exists for website page only
});
});
@@ -92,10 +89,10 @@ if ($showlinktoai) {
$out .= '<script>
$(document).ready(function() {
$("#linkforaiprompt'.$showlinktoai.'").click(function() {
console.log("formlayoutai.tpl: We click on linkforaiprompt'.$showlinktoai.', we toggle #ai_input'.$showlinktoai.'");
console.log("formlayoutai.tpl: We click on linkforaiprompt'.$showlinktoai.', we toggle .ai_input'.$showlinktoai.'");
event.preventDefault();
jQuery("#ai_input'.$htmlname.'").toggle();
jQuery("#template-selector").hide();
jQuery(".ai_input'.$htmlname.'").toggle();
jQuery(".template-selector").hide();
jQuery(".email-layout-container").hide();
if (!jQuery("#ai_input'.$htmlname.'").is(":hidden")) {
console.log("Set focus on input field #ai_instructions'.$htmlname.'");

View File

@@ -2508,7 +2508,7 @@ UrlPublicInterfaceLabelAdmin=Alternative URL for public interface
UrlPublicInterfaceHelpAdmin=It is possible to define an alias to the web server and thus make available the public interface with another URL (the virtual host server must act as a proxy on the standard URL)
ExportUseForce=Use the parameter -f
ExportUseForceHelp=Force to continue the export even when an error is found (Backup may not be reliable)
CustomPrompt=Custom prompts
CustomPrompt=Custom prompts and models
AiDescription=AI (Artificial Intelligence) features
AiDescriptionLong=Provides AI (Artificial Intelligence) features in different parts of the application. Need external AI API.
AI_API_KEY=Key for AI api
@@ -2614,3 +2614,5 @@ Privileges=Privileges
FieldsLinked=Fields Linked
PDF_XXX_SHOW_PRICE_INCL_TAX=Show column Price including tax
AvailableWithSomePDFTemplatesOnly=Feature not supported on old PDF templates
SelectAService=Select the kind of service to use
SelectFeatureToTest=Select the feature to test

View File

@@ -283,6 +283,7 @@ ImportDataset_tax_contrib=Social/fiscal taxes
ImportDataset_tax_vat=VAT payments
ErrorBankAccountNotFound=Error: Bank account not found
FiscalPeriod=Accounting period
ErrorNoFiscalyearDefined=Error, no fiscal year defined (See %s - %s - %s)
FiscalYearSetAsDefault=Fiscal year (%s) defined as default
FiscalYearFromTo=Period from %s to %s
FiscalYearOpened=Fiscal year opened

View File

@@ -497,7 +497,7 @@ class modMyModule extends DolibarrModules
foreach ($myTmpObjects as $myTmpObjectKey => $myTmpObjectArray) {
if ($myTmpObjectArray['includerefgeneration']) {
$src = DOL_DOCUMENT_ROOT.'/install/doctemplates/'.$moduledir.'/template_myobjects.odt';
$dirodt = DOL_DATA_ROOT.'/doctemplates/'.$moduledir;
$dirodt = DOL_DATA_ROOT.($conf->entity > 1 ? '/'.$conf->entity : '').'/doctemplates/'.$moduledir;
$dest = $dirodt.'/template_myobjects.odt';
if (file_exists($src) && !file_exists($dest)) {

View File

@@ -655,7 +655,7 @@ if (isModEnabled('stock') && $user->hasRight('stock', 'mouvement', 'read')) {
$db->free($resql);
if (empty($num)) {
$colspan = 4;
$colspan = 5;
if (isModEnabled('productbatch')) {
$colspan++;
}

View File

@@ -208,13 +208,15 @@ class Task extends CommonObjectLine
* @var int|string
*/
public $timespent_datehour; // More accurate start date (same than timespent_date but includes hours, minutes and seconds)
/**
* @var int
*/
public $timespent_withhour; // 1 = we entered also start hours for timesheet line
public $timespent_withhour; // 0 or 1 = we have entered also start hours for timesheet line
/**
* @var int
*/
public $timespent_fk_user;
/**
* @var float
@@ -1656,10 +1658,9 @@ class Task extends CommonObjectLine
$timespent->fk_user = $this->timespent_fk_user;
$timespent->fk_product = $this->timespent_fk_product;
$timespent->note = $this->timespent_note;
$timespent->datec = $this->db->idate($now);
$timespent->datec = $now;
$result = $timespent->create($user);
if ($result > 0) {
$ret = $result;
$this->timespent_id = $result;

View File

@@ -189,7 +189,6 @@ if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x'
$search_year = '';
$search_note = '';
$search_duration = '';
$search_value = '';
$search_date_startday = '';
$search_date_startmonth = '';
$search_date_startyear = '';
@@ -261,6 +260,7 @@ if ($action == 'addtimespent' && $user->hasRight('projet', 'time')) {
$object->timespent_withhour = 1;
} else {
$object->timespent_date = dol_mktime(12, 0, 0, GETPOSTINT("timemonth"), GETPOSTINT("timeday"), GETPOSTINT("timeyear"));
$object->timespent_withhour = 0;
}
$object->timespent_fk_user = GETPOSTINT("userid");
$object->timespent_fk_product = GETPOSTINT("fk_product");
@@ -309,6 +309,7 @@ if (($action == 'updateline' || $action == 'updatesplitline') && !$cancel && $us
$object->timespent_withhour = 1;
} else {
$object->timespent_date = dol_mktime(12, 0, 0, GETPOSTINT("timelinemonth"), GETPOSTINT("timelineday"), GETPOSTINT("timelineyear"));
$object->timespent_withhour = 0;
}
$object->timespent_fk_user = GETPOSTINT("userid_line");
$object->timespent_fk_product = GETPOSTINT("fk_product");
@@ -338,6 +339,7 @@ if (($action == 'updateline' || $action == 'updatesplitline') && !$cancel && $us
$object->timespent_withhour = 1;
} else {
$object->timespent_date = dol_mktime(12, 0, 0, GETPOSTINT("timelinemonth"), GETPOSTINT("timelineday"), GETPOSTINT("timelineyear"));
$object->timespent_withhour = 0;
}
$object->timespent_fk_user = GETPOSTINT("userid_line");
$object->timespent_fk_product = GETPOSTINT("fk_product");

View File

@@ -52,7 +52,7 @@ if (empty($id) && empty($ref)) {
$id = $user->id;
}
$expand = $_COOKIE['virtualcard_expand'];
$expand = empty($_COOKIE['virtualcard_expand']) ? '' : $_COOKIE['virtualcard_expand'];
$object = new User($db);
if ($id > 0 || !empty($ref)) {

View File

@@ -3548,7 +3548,7 @@ if (!GETPOST('hide_websitemenu')) {
print '<span class="websiteselection">';
print '<input type="image" class="valignmiddle buttonwebsite" src="'.img_picto('', 'refresh', '', 0, 1).'" name="refreshpage" value="'.$langs->trans("Load").'"'.(($action != 'editsource') ? '' : ' disabled="disabled"').'>';
print '<input type="image" class="valignmiddle buttonwebsite hideonsmartphone" src="'.img_picto('', 'refresh', '', 0, 1).'" name="refreshpage" value="'.$langs->trans("Load").'"'.(($action != 'editsource') ? '' : ' disabled="disabled"').'>';
// Print nav arrows
$pagepreviousid = 0;