diff --git a/htdocs/ai/admin/setup.php b/htdocs/ai/admin/setup.php index 33d4954ea2d..00e3623dab5 100644 --- a/htdocs/ai/admin/setup.php +++ b/htdocs/ai/admin/setup.php @@ -67,8 +67,6 @@ foreach ($arrayofia as $ia) { $item->defaultFieldValue = ''; } -// Retrieve existing API Key -//$apiKey = dolibarr_get_const($db, 'MAIN_AI_CHATGPT_API_KEY'); $setupnotempty =+ count($formSetup->items); diff --git a/htdocs/ai/lib/generate_content.lib.php b/htdocs/ai/ajax/generate_content.php similarity index 82% rename from htdocs/ai/lib/generate_content.lib.php rename to htdocs/ai/ajax/generate_content.php index 898bd2205ca..c38ab4ef54b 100644 --- a/htdocs/ai/lib/generate_content.lib.php +++ b/htdocs/ai/ajax/generate_content.php @@ -41,30 +41,27 @@ if (!defined('NOREQUIRESOC')) { define('NOREQUIRESOC', '1'); } -require_once '../../main.inc.php'; +require '../../main.inc.php'; + require_once DOL_DOCUMENT_ROOT.'/ai/class/ai.class.php'; +top_httphead(); //get data from AJAX $rawData = file_get_contents('php://input'); $jsonData = json_decode($rawData, true); if (is_null($jsonData)) { - dol_print_error(null, 'data with format JSON valide.'); + dol_print_error('data with format JSON valide.'); } -$token = GETPOST('token'); -if ($token !== currentToken()) { // Remplacez 'newToken' par le nom de votre variable de session contenant le token - dol_print_error(null, 'CSRF token validation failed.'); - exit; -} -$chatGPT = new Ai('API_ENDPOINT', 'API_KEY'); +$chatGPT = new Ai($db); $instructions = dol_string_nohtmltag($jsonData['instructions'], 1, 'UTF-8'); $generatedContent = $chatGPT->generateContent($instructions); -if ($generatedContent) { - print $generatedContent; +if (is_array($generatedContent) && $generatedContent['error']) { + print "Error : " . $generatedContent['message']; } else { - dol_print_error(null, 'error!!'); + print $generatedContent; } diff --git a/htdocs/ai/class/ai.class.php b/htdocs/ai/class/ai.class.php index 3a1e5de3ad1..dd103c837e0 100644 --- a/htdocs/ai/class/ai.class.php +++ b/htdocs/ai/class/ai.class.php @@ -19,6 +19,7 @@ * along with this program. If not, see . * or see https://www.gnu.org/ */ +require_once DOL_DOCUMENT_ROOT."/core/lib/admin.lib.php"; /** * Class for AI @@ -43,33 +44,39 @@ class Ai /** * Constructor * - * @param string $apiEndpoint Endpoint of api - * @param string $apiKey key of api + * @param DoliDB $db Database handler + * */ - public function __construct($apiEndpoint, $apiKey) + public function __construct($db) { - $this->apiEndpoint = $apiEndpoint; - $this->apiKey = $apiKey; + $this->db = $db; + $this->apiEndpoint = dolibarr_get_const($this->db, 'AI_API_ENDPOINT'); + $this->apiKey = dolibarr_get_const($this->db, 'AI_KEY_API_CHATGPT'); } /** * Generate response of instructions * @param string $instructions instruction for generate content + * @param string $model model name (chat,text,image...) * @return mixed $response */ - public function generateContent($instructions) + public function generateContent($instructions, $model = 'gpt-3.5-turbo') { try { $ch = curl_init($this->apiEndpoint); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(['prompt' => $instructions])); + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([ + 'messages' => [ + ['role' => 'user', 'content' => $instructions] + ], + 'model' => $model + ])); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Authorization: Bearer ' . $this->apiKey, 'Content-Type: application/json' ]); $response = curl_exec($ch); - if (curl_errno($ch)) { throw new Exception('cURL error: ' . curl_error($ch)); } @@ -78,11 +85,15 @@ class Ai if ($statusCode != 200) { throw new Exception('API request failed with status code ' . $statusCode); } + // Decode JSON response + $decodedResponse = json_decode($response, true); - return $response; + // Extraction content + $generatedEmailContent = $decodedResponse['choices'][0]['message']['content']; + + return $generatedEmailContent; } catch (Exception $e) { - error_log($e->getMessage()); - return null; + return array('error' => true, 'message' => $e->getMessage()); } finally { curl_close($ch); } diff --git a/htdocs/ai/css/style.css b/htdocs/ai/css/style.css new file mode 100644 index 00000000000..29ed800d134 --- /dev/null +++ b/htdocs/ai/css/style.css @@ -0,0 +1,101 @@ +.template-container { + display: flex; + justify-content: space-between; + padding: 10px; + background: #f5f5f5; + border: 1px solid #d3d3d3; + border-radius: 5px; + margin-bottom: 15px; + } + + .template-option { + flex: 1; + text-align: center; + padding: 10px; + margin: 0 5px; + background: #e9e9e9; + border: 1px solid #ccc; + border-radius: 5px; + cursor: pointer; + height: 90px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + } + + .template-option:hover { + font-weight: bold; + background: var(--butactionbg); + color: var(--textbutaction); + border-radius: 8px; + border-collapse: collapse; + border: none; + } + + .template-option[data-template="ai"] { + background: #c5f7c5; + } + + .template-option[data-template="ai"]:hover { + font-weight: bold; + background: var(--butactionbg); + color: var(--textbutaction); + border-radius: 8px; + border-collapse: collapse; + border: none; + } + + .template-option.selected { + font-weight: bold; + background: var(--butactionbg); + color: var(--textbutaction); + border-radius: 8px; + border-collapse: collapse; + border: none; +} + + #template-selector { + width: 100%; + max-width: 80%; + height: auto; + padding: 10px; + border: 1px solid #d3d3d3; + border-radius: 5px; + margin-bottom: 10px; + margin-top: 10px; + } + + .template-option[data-template="ai"] i { + font-size: 42px; + display: block; + width: 80%; + max-height: 80px; + margin: 0 5px; + padding-top: 5px; + border-radius: 5px; + +} +.template-option[data-template="ai"] span { + padding-top: 30px; + font-size: 14px; + +} + +.template-option-text { + padding-top: 3px; + font-size: 14px; +} + +#ai_input { + display: none; +} + +.template-option img { + display: block; + width: 80%; + max-height: 80px; + margin: 0 5px; + padding-top: 5px; + border-radius: 7px; +} \ No newline at end of file diff --git a/htdocs/bom/tpl/objectline_create.tpl.php b/htdocs/bom/tpl/objectline_create.tpl.php index 9c59338fa69..49c4bb474bc 100644 --- a/htdocs/bom/tpl/objectline_create.tpl.php +++ b/htdocs/bom/tpl/objectline_create.tpl.php @@ -241,7 +241,6 @@ jQuery(document).ready(function() { }).done(function(data) { console.log(data); - var data = JSON.parse(data); $("#fk_unit").val(data).change(); }); @@ -254,9 +253,7 @@ jQuery(document).ready(function() { ,'idproduct' : idproduct } }).done(function(data) { - var data = JSON.parse(data); $('#idworkstations').val(data.defaultWk).select2(); - }); }); diff --git a/htdocs/compta/tva/class/tva.class.php b/htdocs/compta/tva/class/tva.class.php index f5ab07a82e8..1684bd9a96d 100644 --- a/htdocs/compta/tva/class/tva.class.php +++ b/htdocs/compta/tva/class/tva.class.php @@ -63,7 +63,7 @@ class Tva extends CommonObject public $num_payment; /** - * @var DateTime + * @var int Creation date */ public $datec; @@ -82,11 +82,6 @@ class Tva extends CommonObject */ public $rappro; - /** - * @var integer|string totalpaid - */ - public $totalpaid; - /** * @var string label */ @@ -426,9 +421,9 @@ class Tva extends CommonObject $this->amount = ''; $this->label = ''; $this->note = ''; - $this->fk_bank = ''; - $this->fk_user_creat = ''; - $this->fk_user_modif = ''; + $this->fk_bank = 0; + $this->fk_user_creat = 0; + $this->fk_user_modif = 0; } diff --git a/htdocs/core/class/ccountry.class.php b/htdocs/core/class/ccountry.class.php index 0a065041efe..179eaaf2edb 100644 --- a/htdocs/core/class/ccountry.class.php +++ b/htdocs/core/class/ccountry.class.php @@ -73,7 +73,7 @@ class Ccountry extends CommonDict $this->label = trim($this->label); } if (isset($this->active)) { - $this->active = trim($this->active); + $this->active = (int) $this->active; } // Check parameters @@ -195,7 +195,7 @@ class Ccountry extends CommonDict $this->label = trim($this->label); } if (isset($this->active)) { - $this->active = trim($this->active); + $this->active = (int) $this->active; } diff --git a/htdocs/core/class/cgenericdic.class.php b/htdocs/core/class/cgenericdic.class.php index f488ab75937..7e163c56acb 100644 --- a/htdocs/core/class/cgenericdic.class.php +++ b/htdocs/core/class/cgenericdic.class.php @@ -97,7 +97,7 @@ class CGenericDic extends CommonDict $this->label = trim($this->label); } if (isset($this->active)) { - $this->active = trim($this->active); + $this->active = (int) $this->active; } // Insert request @@ -321,7 +321,7 @@ class CGenericDic extends CommonDict $this->label = trim($this->label); } if (isset($this->active)) { - $this->active = trim($this->active); + $this->active = (int) $this->active; } // Check parameters diff --git a/htdocs/core/class/cregion.class.php b/htdocs/core/class/cregion.class.php index 758ae9842bc..358935bb2a9 100644 --- a/htdocs/core/class/cregion.class.php +++ b/htdocs/core/class/cregion.class.php @@ -94,7 +94,7 @@ class Cregion extends CommonDict $this->cheflieu = trim($this->cheflieu); } if (isset($this->active)) { - $this->active = trim($this->active); + $this->active = (int) $this->active; } // Check parameters @@ -224,7 +224,7 @@ class Cregion extends CommonDict $this->cheflieu = trim($this->cheflieu); } if (isset($this->active)) { - $this->active = trim($this->active); + $this->active = (int) $this->active; } diff --git a/htdocs/core/class/cstate.class.php b/htdocs/core/class/cstate.class.php index 99ad05c668a..8921689f2a1 100644 --- a/htdocs/core/class/cstate.class.php +++ b/htdocs/core/class/cstate.class.php @@ -84,7 +84,7 @@ class Cstate extends CommonDict $this->nom = trim($this->nom); } if (isset($this->active)) { - $this->active = trim($this->active); + $this->active = (int) $this->active; } // Check parameters @@ -194,7 +194,7 @@ class Cstate extends CommonDict $this->name = trim($this->name); } if (isset($this->active)) { - $this->active = trim($this->active); + $this->active = (int) $this->active; } // Check parameters diff --git a/htdocs/core/class/ctypent.class.php b/htdocs/core/class/ctypent.class.php index b1d88898338..26c5e07b2cf 100644 --- a/htdocs/core/class/ctypent.class.php +++ b/htdocs/core/class/ctypent.class.php @@ -64,7 +64,7 @@ class Ctypent extends CommonDict // Clean parameters if (isset($this->id)) { - $this->id = trim($this->id); + $this->id = (int) $this->id; } if (isset($this->code)) { $this->code = trim($this->code); @@ -73,7 +73,7 @@ class Ctypent extends CommonDict $this->libelle = trim($this->libelle); } if (isset($this->active)) { - $this->active = trim($this->active); + $this->active = (int) $this->active; } if (isset($this->module)) { $this->module = trim($this->module); @@ -193,7 +193,7 @@ class Ctypent extends CommonDict $this->libelle = trim($this->libelle); } if (isset($this->active)) { - $this->active = trim($this->active); + $this->active = (int) $this->active; } if (isset($this->module)) { $this->module = trim($this->module); diff --git a/htdocs/core/class/ctyperesource.class.php b/htdocs/core/class/ctyperesource.class.php index 2b1a8cc26f1..717b7c43ec6 100644 --- a/htdocs/core/class/ctyperesource.class.php +++ b/htdocs/core/class/ctyperesource.class.php @@ -80,7 +80,7 @@ class Ctyperesource extends CommonDict $this->label = trim($this->label); } if (isset($this->active)) { - $this->active = trim($this->active); + $this->active = (int) $this->active; } // Insert request @@ -277,7 +277,7 @@ class Ctyperesource extends CommonDict $this->label = trim($this->label); } if (isset($this->active)) { - $this->active = trim($this->active); + $this->active = (int) $this->active; } // Check parameters @@ -433,7 +433,7 @@ class Ctyperesource extends CommonDict $this->code = ''; $this->label = ''; - $this->active = ''; + $this->active = 0; } } diff --git a/htdocs/core/class/cunits.class.php b/htdocs/core/class/cunits.class.php index cd16e4edeac..4a5326d719e 100644 --- a/htdocs/core/class/cunits.class.php +++ b/htdocs/core/class/cunits.class.php @@ -88,7 +88,7 @@ class CUnits extends CommonDict $this->active = trim($this->unit_type); } if (isset($this->active)) { - $this->active = trim($this->active); + $this->active = (int) $this->active; } if (isset($this->scale)) { $this->scale = trim($this->scale); @@ -318,7 +318,7 @@ class CUnits extends CommonDict $this->scale = trim($this->scale); } if (isset($this->active)) { - $this->active = trim($this->active); + $this->active = (int) $this->active; } // Check parameters diff --git a/htdocs/core/class/html.formmail.class.php b/htdocs/core/class/html.formmail.class.php index 9592ed0a291..546b020a2f2 100644 --- a/htdocs/core/class/html.formmail.class.php +++ b/htdocs/core/class/html.formmail.class.php @@ -29,6 +29,8 @@ * \brief Fichier de la class permettant la generation du formulaire html d'envoi de mail unitaire */ require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/modelemail.lib.php'; + /** @@ -755,7 +757,7 @@ class FormMail extends Form if (!empty($this->withtoccuser) && is_array($this->withtoccuser) && getDolGlobalString('MAIN_MAIL_ENABLED_USER_DEST_SELECT')) { $out .= ''; $out .= $langs->trans("MailToCCUsers"); - $out .= ''; + $out .= ''; // multiselect array convert html entities into options tags, even if we don't want this, so we encode them a second time $tmparray = $this->withtoccuser; @@ -889,8 +891,8 @@ class FormMail extends Form //input prompt AI require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php'; - if (isModEnabled('ai') && getDolGlobalString('AI_CHATGPT_API_KEY')) { - $out .= $this->getHtmlForInstruction(); + if (isModEnabled('ai')) { + $out .= $this->getModelEmailTemplate(); } // Message if (!empty($this->withbody)) { @@ -1002,7 +1004,7 @@ class FormMail extends Form $out .= ''; if ($this->withbodyreadonly) { $out .= nl2br($defaultmessage); - $out .= ''; + $out .= ''; } else { if (!isset($this->ckeditortoolbar)) { $this->ckeditortoolbar = 'dolibarr_mailings'; @@ -1386,39 +1388,57 @@ class FormMail extends Form { global $langs, $form; - $baseUrl = dol_buildpath('/', 1); - - $out = ''; + $out = ''; $out .= ''; $out .= $form->textwithpicto($langs->trans('helpWithAi'), $langs->trans("YouCanMakeSomeInstructionForEmail")); $out .= ''; $out .= ''; - $out .= ''; - $out .= ''; - $out .= ''; + $out .= ''; + $out .= ''; $out .= "\n"; + + $out .= ""; + $out .= ""; + $out .= $this->getHtmlForInstruction(); + return $out; + } + /** diff --git a/htdocs/core/class/utils.class.php b/htdocs/core/class/utils.class.php index ec3c4d15505..3a1e6e34a96 100644 --- a/htdocs/core/class/utils.class.php +++ b/htdocs/core/class/utils.class.php @@ -31,15 +31,30 @@ class Utils { /** - * @var DoliDB Database handler. + * @var DoliDB Database handler */ public $db; + /** + * @var string Error message + * @see $errors + */ public $error; + + /** + * @var string[] Array of error messages + */ public $errors; - public $output; // Used by Cron method to return message - public $result; // Used by Cron method to return data + /** + * @var string Used by Cron method to return message + */ + public $output; + + /** + * @var array{'commandbackuplastdone': string, 'commandbackuptorun': string} Used by Cron method to return data + */ + public $result; /** * Constructor diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php index fe52c55fa2f..b0c22706cc7 100644 --- a/htdocs/core/lib/functions.lib.php +++ b/htdocs/core/lib/functions.lib.php @@ -11371,7 +11371,7 @@ function isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal */ function roundUpToNextMultiple($n, $x = 5) { - $result = (ceil($n) % $x === 0) ? ceil($n) : round(($n + $x / 2) / $x) * $x; + $result = (ceil($n) % $x === 0) ? ceil($n) : (round(($n + $x / 2) / $x) * $x); return (int) $result; } diff --git a/htdocs/core/lib/modelemail.lib.php b/htdocs/core/lib/modelemail.lib.php new file mode 100644 index 00000000000..c752658744b --- /dev/null +++ b/htdocs/core/lib/modelemail.lib.php @@ -0,0 +1,185 @@ + + * Copyright (C) 2004-2018 Laurent Destailleur + * Copyright (C) 2024 Alexandre Janniaux + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file htdocs/core/lib/emailmodel.lib.php + * + * \brief File for getting email html models + */ + +/** + * get empty html + * @return string $out html content + */ +function empty_template() +{ + $out = ''; + return $out; +} + +/** + * get basic html + * @return string $out html content + */ +function basic_template() +{ + $out = ' +
+
+ Gray rectangle +
+

Lorem, ipsum dolor sit amet consectetur adipisicing elit.

+

Lorem, ipsum dolor sit amet consectetur

+ + + + + + Gray rectangle + +
'; + return $out; +} + +/** + * get news html + * @return string $out html content + */ +function news_template() +{ + $out = ' +

Lorem, ipsum dolor sit amet consectetur adipisicing elit sit amet consectetur

+

Lorem, ipsum dolor sit amet consectetur adipisicing elitsit amet consectetur adipisicing

+ +
+
+

Lorem ipsum dolor sit amet, consectetur Lorem ipsum dolor sit amet, consectetur

+ + Lorem ipsum dolor sit amet, consectetur + adipiscing elit. Sed do eiusmod temxwshslkdsdsslpor incididunt ut labore et dolore magna
aliqua. Lorem, ipsum dolor sit amet consectetur adipisicing elit. Totam sit
autem nihil omnis! Odit ipsum repellat, voluptas accusantium dolores adipisci ut voluptates eius cumque dicta obcaecati + Gray rectangle + +
+
+ + Gray rectangle +
+
+ +
+
+

Lorem ipsum dolor sit amet, consectetur Lorem ipsum dolor sit amet, consectetur

+ + Lorem ipsum dolor sit amet, consectetur + adipiscing elit. Sed do eiusmod temxwshslkdsdsslpor incididunt ut labore et dolore magna
aliqua. Lorem, ipsum dolor sit amet consectetur adipisicing elit. Totam sit
autem nihil omnis! Odit ipsum repellat, voluptas accusantium dolores adipisci ut voluptates eius cumque dicta obcaecati + Gray rectangle + +
+
+ + Gray rectangle +
+
'; + return $out; +} + +/** + * get commerce html + * @return string $out html content + */ +function commerce_template() +{ + $out = ' +

Lorem, ipsum dolor sit amet consectetur adipisicing elit sit amet consectetur

+

Lorem, ipsum dolor sit amet consectetur adipisicing elitsit amet consectetur adipisicing

+ +
+
+
+
+ Gray rectangle +
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit
Lorem ipsum dolor sit amet, consectetur adipiscing elit... +
+
+ +

+
+
+ Gray rectangle +
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit
Lorem ipsum dolor sit amet, consectetur adipiscing elit... +
+
+
+
+
+
+ Gray rectangle +
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit
Lorem ipsum dolor sit amet, consectetur adipiscing elit... +
+
+ +

+
+
+ Gray rectangle +
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit
Lorem ipsum dolor sit amet, consectetur adipiscing elit... +
+
+
+
+ + '; + return $out; +} + +/** + * get basic html + * @return string $out html content + */ +function text_template() +{ + $out = ' +

Lorem ipsum dolor sit amet consectetur adipisicing elit sit amet consectetur adipisicing elit.

+

Lorem ipsum dolor sit amet consectetur adipisicing elit sit amet consectetur adipisicing elit.

+

+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Commodi impedit molestias voluptatibus. Natus nulla sint totam illo? Hic name consequuntur id harum pariatur, quo illo quaerat minima tempore. + Lorem ipsum dolor sit amet consectetur adipisicing elit. Commodi impedit molestias voluptatibus. Natus nulla sint totam illo? Hic name consequuntur id harum pariatur, quo illo quaerat minima tempore. + Lorem ipsum dolor sit amet consectetur adipisicing elit. Commodi impedit molestias voluptatibus. Natus nulla sint totam illo? Hic name consequuntur id harum pariatur, quo illo quaerat minima tempore. + Lorem ipsum dolor sit amet consectetur adipisicing elit. Commodi impedit molestias voluptatibus. Natus nulla sint totam illo? Hic name consequuntur id harum pariatur, quo illo quaerat minima tempore. + Lorem ipsum dolor sit amet consectetur adipisicing elit. Commodi impedit molestias voluptatibus. Natus nulla sint totam illo? Hic name consequuntur id harum pariatur, quo illo quaerat minima tempore. + Lorem ipsum dolor sit amet consectetur adipisicing elit. Commodi impedit molestias voluptatibus. Natus nulla sint totam illo? Hic name consequuntur id harum pariatur, quo illo quaerat minima tempore. + Lorem ipsum dolor sit amet consectetur adipisicing elit. Commodi impedit molestias voluptatibus. Natus nulla sint totam illo? Hic name consequuntur id harum pariatur, quo illo quaerat minima tempore. +

'; + + return $out; +} diff --git a/htdocs/core/modules/modCommande.class.php b/htdocs/core/modules/modCommande.class.php index eb276ea8c3d..005ae6490be 100644 --- a/htdocs/core/modules/modCommande.class.php +++ b/htdocs/core/modules/modCommande.class.php @@ -200,7 +200,7 @@ class modCommande extends DolibarrModules $this->export_label[$r] = 'CustomersOrdersAndOrdersLines'; // Translation key (used only if key ExportDataset_xxx_z not found) $this->export_permission[$r] = array(array("commande", "commande", "export")); $this->export_fields_array[$r] = array( - 's.rowid'=>"IdCompany", 's.nom'=>'CompanyName', 'ps.nom'=>'ParentCompany', 's.address'=>'Address', 's.zip'=>'Zip', 's.town'=>'Town', 'd.nom'=>'State', 'co.label'=>'Country', + 's.rowid'=>"IdCompany", 's.nom'=>'CompanyName', 'ps.nom'=>'ParentCompany', 's.code_client'=>'CustomerCode', 's.address'=>'Address', 's.zip'=>'Zip', 's.town'=>'Town', 'd.nom'=>'State', 'co.label'=>'Country', 'co.code'=>"CountryCode", 's.phone'=>'Phone', 's.siren'=>'ProfId1', 's.siret'=>'ProfId2', 's.ape'=>'ProfId3', 's.idprof4'=>'ProfId4', 'c.rowid'=>"Id", 'c.ref'=>"Ref", 'c.ref_client'=>"RefCustomer", 'c.fk_soc'=>"IdCompany", 'c.date_creation'=>"DateCreation", 'c.date_commande'=>"OrderDate", 'c.date_livraison'=>"DateDeliveryPlanned", 'c.amount_ht'=>"Amount", 'c.total_ht'=>"TotalHT", @@ -233,7 +233,7 @@ class modCommande extends DolibarrModules // 'p.rowid'=>'List:product:ref','p.ref'=>'Text','p.label'=>'Text' //); $this->export_TypeFields_array[$r] = array( - 's.nom'=>'Text', 'ps.nom'=>'Text', 's.address'=>'Text', 's.zip'=>'Text', 's.town'=>'Text', 'co.label'=>'List:c_country:label:label', 'co.code'=>'Text', 's.phone'=>'Text', + 's.nom'=>'Text', 'ps.nom'=>'Text', 's.code_client'=>'Text', 's.address'=>'Text', 's.zip'=>'Text', 's.town'=>'Text', 'co.label'=>'List:c_country:label:label', 'co.code'=>'Text', 's.phone'=>'Text', 's.siren'=>'Text', 's.siret'=>'Text', 's.ape'=>'Text', 's.idprof4'=>'Text', 'c.ref'=>"Text", 'c.ref_client'=>"Text", 'c.date_creation'=>"Date", 'c.date_commande'=>"Date", 'c.date_livraison'=>"Date", 'sm.code'=>"Text", 'c.amount_ht'=>"Numeric", 'c.total_ht'=>"Numeric", 'c.total_ttc'=>"Numeric", 'c.facture'=>"Boolean", 'c.fk_statut'=>'Status', 'c.note_public'=>"Text", 'pj.ref'=>'Text', @@ -242,7 +242,7 @@ class modCommande extends DolibarrModules 'c.entity'=>'List:entity:label:rowid', ); $this->export_entities_array[$r] = array( - 's.rowid'=>"company", 's.nom'=>'company', 'ps.nom'=>'company', 's.address'=>'company', 's.zip'=>'company', 's.town'=>'company', 'd.nom'=>'company', 'co.label'=>'company', + 's.rowid'=>"company", 's.nom'=>'company', 'ps.nom'=>'company', 's.code_client'=>'company', 's.address'=>'company', 's.zip'=>'company', 's.town'=>'company', 'd.nom'=>'company', 'co.label'=>'company', 'co.code'=>'company', 's.phone'=>'company', 's.siren'=>'company', 's.ape'=>'company', 's.idprof4'=>'company', 's.siret'=>'company', 'c.rowid'=>"order", 'c.ref'=>"order", 'c.ref_client'=>"order", 'c.fk_soc'=>"order", 'c.date_creation'=>"order", 'c.date_commande'=>"order", 'c.amount_ht'=>"order", 'c.total_ht'=>"order", 'c.total_ttc'=>"order", 'c.facture'=>"order", 'c.fk_statut'=>"order", 'c.note'=>"order", diff --git a/htdocs/cron/class/cronjob.class.php b/htdocs/cron/class/cronjob.class.php index 8a57372927b..9c57f274779 100644 --- a/htdocs/cron/class/cronjob.class.php +++ b/htdocs/cron/class/cronjob.class.php @@ -110,7 +110,7 @@ class Cronjob extends CommonObject public $datelastresult = ''; /** - * @var string Last result from end job execution + * @var int Last result from end job execution */ public $lastresult; @@ -263,10 +263,10 @@ class Cronjob extends CommonObject $this->unitfrequency = trim($this->unitfrequency); } if (isset($this->frequency)) { - $this->frequency = trim($this->frequency); + $this->frequency = (int) $this->frequency; } if (isset($this->status)) { - $this->status = trim($this->status); + $this->status = (int) $this->status; } if (isset($this->note_private)) { $this->note_private = trim($this->note_private); @@ -710,10 +710,10 @@ class Cronjob extends CommonObject $this->unitfrequency = trim($this->unitfrequency); } if (isset($this->frequency)) { - $this->frequency = trim($this->frequency); + $this->frequency = (int) $this->frequency; } if (isset($this->status)) { - $this->status = trim($this->status); + $this->status = (int) $this->status; } if (isset($this->note_private)) { $this->note_private = trim($this->note_private); @@ -945,7 +945,7 @@ class Cronjob extends CommonObject public function initAsSpecimen() { $this->id = 0; - $this->ref = 0; + $this->ref = ''; $this->entity = 0; $this->tms = ''; $this->datec = ''; @@ -967,7 +967,7 @@ class Cronjob extends CommonObject $this->lastoutput = ''; $this->lastresult = ''; $this->unitfrequency = ''; - $this->frequency = ''; + $this->frequency = 0; $this->status = 0; $this->processing = 0; $this->pid = null; @@ -975,7 +975,7 @@ class Cronjob extends CommonObject $this->fk_user_author = 0; $this->fk_user_mod = 0; $this->note_private = ''; - $this->nbrun = ''; + $this->nbrun = 0; $this->maxrun = 100; $this->libname = ''; } diff --git a/htdocs/ecm/class/ecmfiles.class.php b/htdocs/ecm/class/ecmfiles.class.php index aa0b89ff9b0..5e1c0107cfa 100644 --- a/htdocs/ecm/class/ecmfiles.class.php +++ b/htdocs/ecm/class/ecmfiles.class.php @@ -662,7 +662,7 @@ class EcmFiles extends CommonObject $this->share = trim($this->share); } if (isset($this->entity)) { - $this->entity = trim($this->entity); + $this->entity = (int) $this->entity; } if (isset($this->filename)) { $this->filename = preg_replace('/\.noexe$/', '', trim($this->filename)); @@ -690,7 +690,7 @@ class EcmFiles extends CommonObject $this->extraparams = trim($this->extraparams); } if (isset($this->fk_user_m)) { - $this->fk_user_m = trim($this->fk_user_m); + $this->fk_user_m = (int) $this->fk_user_m; } if (isset($this->acl)) { $this->acl = trim($this->acl); @@ -979,7 +979,7 @@ class EcmFiles extends CommonObject $this->date_c = (dol_now() - 3600 * 24 * 10); $this->date_m = ''; $this->fk_user_c = $user->id; - $this->fk_user_m = ''; + $this->fk_user_m = $user->id; $this->acl = ''; $this->src_object_type = 'product'; $this->src_object_id = 1; diff --git a/htdocs/expedition/card.php b/htdocs/expedition/card.php index decb5ab0cf3..6bf40733ed9 100644 --- a/htdocs/expedition/card.php +++ b/htdocs/expedition/card.php @@ -758,8 +758,8 @@ if (empty($reshook)) { $stockLocation = 0; $qty = "qtyl".$line_id; $line->id = $line_id; - $line->entrepot_id = GETPOST($stockLocation, 'int'); - $line->qty = GETPOST($qty, 'int'); + $line->entrepot_id = GETPOSTINT($stockLocation); + $line->qty = GETPOSTFLOAT($qty); if ($line->update($user) < 0) { setEventMessages($line->error, $line->errors, 'errors'); $error++; diff --git a/htdocs/langs/en_US/mails.lang b/htdocs/langs/en_US/mails.lang index 2de7dbbbdc3..aaddd278084 100644 --- a/htdocs/langs/en_US/mails.lang +++ b/htdocs/langs/en_US/mails.lang @@ -184,5 +184,7 @@ EmailOptedOut=Email owner has requested to not contact him with this email anymo EvenUnsubscribe=Include opt-out emails EvenUnsubscribeDesc=Include opt-out emails when you select emails as targets. Useful for mandatory service emails for example. XEmailsDoneYActionsDone=%s emails pre-qualified, %s emails successfully processed (for %s record/actions done) -helpWithAi=Add instructions -YouCanMakeSomeInstructionForEmail=You can add some instructions for your email (Example: generate image in email template...) +helpWithAi=Generate message from AI +YouCanMakeSomeInstructionForEmail=You Can Make Some Instruction For your Email (Exemple: generate image in email template...) +ModelTemplate=Email template +YouCanChooseAModelForYouMailContent= You can shoose one of models or generate one with Ai \ No newline at end of file diff --git a/htdocs/projet/tasks/stats/index.php b/htdocs/projet/tasks/stats/index.php index eb720c69f74..2c402efac39 100644 --- a/htdocs/projet/tasks/stats/index.php +++ b/htdocs/projet/tasks/stats/index.php @@ -45,7 +45,7 @@ if ($user->socid > 0) { $socid = $user->socid; } $nowyear = dol_print_date(dol_now('gmt'), "%Y", 'gmt'); -$year = GETPOST('year') > 0 ? GETPOST('year') : $nowyear; +$year = GETPOSTINT('year') > 0 ? GETPOSTINT('year') : $nowyear; $startyear = $year - (!getDolGlobalString('MAIN_STATS_GRAPHS_SHOW_N_YEARS') ? 2 : max(1, min(10, getDolGlobalString('MAIN_STATS_GRAPHS_SHOW_N_YEARS')))); $endyear = $year; diff --git a/htdocs/projet/tasks/task.php b/htdocs/projet/tasks/task.php index 59b260fc142..f71b0fbfeb2 100644 --- a/htdocs/projet/tasks/task.php +++ b/htdocs/projet/tasks/task.php @@ -111,7 +111,7 @@ if ($action == 'update' && !GETPOST("cancel") && $user->hasRight('projet', 'cree $object->date_start = dol_mktime(GETPOST('date_starthour', 'int'), GETPOST('date_startmin', 'int'), 0, GETPOST('date_startmonth', 'int'), GETPOST('date_startday', 'int'), GETPOST('date_startyear', 'int')); $object->date_end = dol_mktime(GETPOST('date_endhour', 'int'), GETPOST('date_endmin', 'int'), 0, GETPOST('date_endmonth', 'int'), GETPOST('date_endday', 'int'), GETPOST('date_endyear', 'int')); $object->progress = price2num(GETPOST('progress', 'alphanohtml')); - $object->budget_amount = price2num(GETPOST('budget_amount', 'alphanohtml')); + $object->budget_amount = GETPOSTFLOAT('budget_amount'); // Fill array 'array_options' with data from add form $ret = $extrafields->setOptionalsFromPost(null, $object, '@GETPOSTISSET'); diff --git a/htdocs/societe/class/societe.class.php b/htdocs/societe/class/societe.class.php index 15e88771d24..1acdb619d76 100644 --- a/htdocs/societe/class/societe.class.php +++ b/htdocs/societe/class/societe.class.php @@ -19,6 +19,7 @@ * Copyright (C) 2020 Open-Dsi * Copyright (C) 2022 ButterflyOfFire * Copyright (C) 2023 Alexandre Janniaux + * Copyright (C) 2024 William Mead * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -310,8 +311,7 @@ class Societe extends CommonObject public $status = 1; /** - * Id of region - * @var int + * @var string region code */ public $region_code; @@ -954,7 +954,7 @@ class Societe extends CommonObject if ($this->code_client == -1 || $this->code_client === 'auto') { $this->get_codeclient($this, 0); } - if ($this->code_fournisseur == -1 || $this->code_fournisseur === 'auto') { + if ($this->code_fournisseur == '-1' || $this->code_fournisseur === 'auto') { $this->get_codefournisseur($this, 1); } @@ -1358,17 +1358,17 @@ class Societe extends CommonObject $now = dol_now(); // Clean parameters - $this->id = $id; - $this->entity = ((isset($this->entity) && is_numeric($this->entity)) ? $this->entity : $conf->entity); - $this->name = $this->name ? trim($this->name) : trim($this->nom); - $this->nom = $this->name; // For backward compatibility - $this->name_alias = trim($this->name_alias); + $this->id = $id; + $this->entity = ((isset($this->entity) && is_numeric($this->entity)) ? $this->entity : $conf->entity); + $this->name = $this->name ? trim($this->name) : trim($this->nom); + $this->nom = $this->name; // For backward compatibility + $this->name_alias = trim($this->name_alias); $this->ref_ext = trim($this->ref_ext); - $this->address = $this->address ? trim($this->address) : trim($this->address); - $this->zip = $this->zip ? trim($this->zip) : trim($this->zip); - $this->town = $this->town ? trim($this->town) : trim($this->town); - $this->state_id = trim($this->state_id); - $this->country_id = ($this->country_id > 0) ? $this->country_id : 0; + $this->address = trim($this->address); + $this->zip = trim($this->zip); + $this->town = trim($this->town); + $this->state_id = (is_numeric($this->state_id)) ? (int) trim($this->state_id) : 0; + $this->country_id = ($this->country_id > 0) ? $this->country_id : 0; $this->phone = trim($this->phone); $this->phone = preg_replace("/\s/", "", $this->phone); $this->phone = preg_replace("/\./", "", $this->phone); @@ -1385,14 +1385,14 @@ class Societe extends CommonObject $this->idprof4 = trim($this->idprof4); $this->idprof5 = (!empty($this->idprof5) ? trim($this->idprof5) : ''); $this->idprof6 = (!empty($this->idprof6) ? trim($this->idprof6) : ''); - $this->prefix_comm = trim($this->prefix_comm); + $this->prefix_comm = trim($this->prefix_comm); $this->outstanding_limit = price2num($this->outstanding_limit); $this->order_min_amount = price2num($this->order_min_amount); $this->supplier_order_min_amount = price2num($this->supplier_order_min_amount); - $this->tva_assuj = trim($this->tva_assuj); + $this->tva_assuj = (is_numeric($this->tva_assuj)) ? (int) trim($this->tva_assuj) : 0; $this->tva_intra = dol_sanitizeFileName($this->tva_intra, ''); - $this->vat_reverse_charge = empty($this->vat_reverse_charge) ? '0' : '1'; + $this->vat_reverse_charge = empty($this->vat_reverse_charge) ? 0 : 1; if (empty($this->status)) { $this->status = 0; } @@ -1412,12 +1412,7 @@ class Societe extends CommonObject $this->localtax1_value = trim($this->localtax1_value); $this->localtax2_value = trim($this->localtax2_value); - if ($this->capital != '') { - $this->capital = price2num(trim($this->capital)); - } - if (!is_numeric($this->capital)) { - $this->capital = ''; // '' = undef - } + $this->capital = ($this->capital != '') ? (float) price2num(trim($this->capital)) : null; $this->effectif_id = trim($this->effectif_id); $this->forme_juridique_code = trim($this->forme_juridique_code); @@ -1429,7 +1424,7 @@ class Societe extends CommonObject if ($this->code_client == -1 || $this->code_client === 'auto') { $this->get_codeclient($this, 0); } - if ($this->code_fournisseur == -1 || $this->code_fournisseur === 'auto') { + if ($this->code_fournisseur == '-1' || $this->code_fournisseur === 'auto') { $this->get_codefournisseur($this, 1); } @@ -1574,7 +1569,7 @@ class Societe extends CommonObject $sql .= ",localtax2_value =0.000"; } - $sql .= ",capital = ".($this->capital == '' ? "null" : $this->capital); + $sql .= ",capital = ".($this->capital === null ? "null" : $this->capital); $sql .= ",prefix_comm = ".(!empty($this->prefix_comm) ? "'".$this->db->escape($this->prefix_comm)."'" : "null"); @@ -1937,7 +1932,7 @@ class Societe extends CommonObject $this->state_id = $obj->state_id; $this->state_code = $obj->state_code; $this->region_id = $obj->region_id; - $this->region_code = $obj->region_code; + $this->region_code = $obj->region_code; $this->state = ($obj->state != '-' ? $obj->state : ''); $transcode = $langs->trans('StatusProspect'.$obj->fk_stcomm); @@ -4018,30 +4013,27 @@ class Societe extends CommonObject } /** - * Return if third party is a company (Business) or an end user (Consumer) + * Check if third party is a company (Business) or an end user (Consumer) * - * @return boolean true=is a company, false=a and user + * @return boolean if a company: true || if a user: false */ public function isACompany() { - global $conf; - // Define if third party is treated as company (or not) when nature is unknown - $isacompany = !getDolGlobalString('MAIN_UNKNOWN_CUSTOMERS_ARE_COMPANIES') ? 0 : 1; // 0 by default + $isACompany = getDolGlobalInt('MAIN_UNKNOWN_CUSTOMERS_ARE_COMPANIES'); if (!empty($this->tva_intra)) { - $isacompany = 1; + $isACompany = 1; } elseif (!empty($this->idprof1) || !empty($this->idprof2) || !empty($this->idprof3) || !empty($this->idprof4) || !empty($this->idprof5) || !empty($this->idprof6)) { - $isacompany = 1; + $isACompany = 1; } elseif (!empty($this->typent_code) && $this->typent_code != 'TE_UNKNOWN') { // TODO Add a field is_a_company into dictionary if (preg_match('/^TE_PRIVATE/', $this->typent_code)) { - $isacompany = 0; + $isACompany = 0; } else { - $isacompany = 1; + $isACompany = 1; } } - - return $isacompany; + return boolval($isACompany); } /** @@ -4262,7 +4254,7 @@ class Societe extends CommonObject $this->client = 1; // A member is a customer by default $this->code_client = ($customercode ? $customercode : -1); - $this->code_fournisseur = -1; + $this->code_fournisseur = '-1'; $this->typent_code = ($member->morphy == 'phy' ? 'TE_PRIVATE' : 0); $this->typent_id = $this->typent_code ? dol_getIdFromCode($this->db, $this->typent_code, 'c_typent', 'id', 'code') : 0; @@ -4336,10 +4328,11 @@ class Societe extends CommonObject $this->note_private = getDolGlobalString('MAIN_INFO_SOCIETE_NOTE'); // We define country_id, country_code and country - $country_id = $country_code = $country_label = ''; + $country_id = 0; + $country_code = $country_label = ''; if (getDolGlobalString('MAIN_INFO_SOCIETE_COUNTRY')) { $tmp = explode(':', getDolGlobalString('MAIN_INFO_SOCIETE_COUNTRY')); - $country_id = $tmp[0]; + $country_id = (is_numeric($tmp[0])) ? (int) $tmp[0] : 0; if (!empty($tmp[1])) { // If $conf->global->MAIN_INFO_SOCIETE_COUNTRY is "id:code:label" $country_code = $tmp[1]; $country_label = $tmp[2]; @@ -4422,7 +4415,7 @@ class Societe extends CommonObject $this->idprof6 = getDolGlobalString('MAIN_INFO_PROFID6'); $this->tva_intra = getDolGlobalString('MAIN_INFO_TVAINTRA'); // VAT number, not necessarily INTRA. $this->managers = getDolGlobalString('MAIN_INFO_SOCIETE_MANAGERS'); - $this->capital = getDolGlobalString('MAIN_INFO_CAPITAL'); + $this->capital = is_numeric(getDolGlobalString('MAIN_INFO_CAPITAL')) ? (float) price2num(getDolGlobalString('MAIN_INFO_CAPITAL')) : 0; $this->forme_juridique_code = getDolGlobalString('MAIN_INFO_SOCIETE_FORME_JURIDIQUE'); $this->email = getDolGlobalString('MAIN_INFO_SOCIETE_MAIL'); $this->default_lang = getDolGlobalString('MAIN_LANG_DEFAULT', 'auto'); @@ -4434,7 +4427,7 @@ class Societe extends CommonObject $this->logo_squarred_mini = getDolGlobalString('MAIN_INFO_SOCIETE_LOGO_SQUARRED_MINI'); // Define if company use vat or not - $this->tva_assuj = getDolGlobalString('FACTURE_TVAOPTION'); + $this->tva_assuj = getDolGlobalInt('FACTURE_TVAOPTION'); // Define if company use local taxes $this->localtax1_assuj = ((isset($conf->global->FACTURE_LOCAL_TAX1_OPTION) && (getDolGlobalString('FACTURE_LOCAL_TAX1_OPTION') == '1' || getDolGlobalString('FACTURE_LOCAL_TAX1_OPTION') == 'localtax1on')) ? 1 : 0); diff --git a/htdocs/theme/common/mailtemplate/basic.png b/htdocs/theme/common/mailtemplate/basic.png new file mode 100644 index 00000000000..a96a07e7d76 Binary files /dev/null and b/htdocs/theme/common/mailtemplate/basic.png differ diff --git a/htdocs/theme/common/mailtemplate/commerce.png b/htdocs/theme/common/mailtemplate/commerce.png new file mode 100644 index 00000000000..9a5dd14c3ad Binary files /dev/null and b/htdocs/theme/common/mailtemplate/commerce.png differ diff --git a/htdocs/theme/common/mailtemplate/empty.png b/htdocs/theme/common/mailtemplate/empty.png new file mode 100644 index 00000000000..458ad61630f Binary files /dev/null and b/htdocs/theme/common/mailtemplate/empty.png differ diff --git a/htdocs/theme/common/mailtemplate/news.png b/htdocs/theme/common/mailtemplate/news.png new file mode 100644 index 00000000000..8fc4146f509 Binary files /dev/null and b/htdocs/theme/common/mailtemplate/news.png differ diff --git a/htdocs/theme/common/mailtemplate/text.png b/htdocs/theme/common/mailtemplate/text.png new file mode 100644 index 00000000000..8b8966e8117 Binary files /dev/null and b/htdocs/theme/common/mailtemplate/text.png differ diff --git a/htdocs/website/class/website.class.php b/htdocs/website/class/website.class.php index 16e2625ddb6..fb711c31302 100644 --- a/htdocs/website/class/website.class.php +++ b/htdocs/website/class/website.class.php @@ -2078,7 +2078,7 @@ class Website extends CommonObject $userId = fileowner($desfFile); if ($userId !== false) { // Obtain user information from the ID - if (function_exists('posix_getpwuid')&& function_exists('posix_getpwuid')) { + if (function_exists('posix_geteuid') && function_exists('posix_getpwuid')) { $uid = posix_geteuid(); $userInfoM = posix_getpwuid($uid); diff --git a/test/phpunit/FunctionsLibTest.php b/test/phpunit/FunctionsLibTest.php index ce34604cc32..b28e945e28d 100644 --- a/test/phpunit/FunctionsLibTest.php +++ b/test/phpunit/FunctionsLibTest.php @@ -1797,4 +1797,29 @@ class FunctionsLibTest extends PHPUnit\Framework\TestCase return true; } + + + /** + * testRoundUpToNextMultiple + * + * @return void; + */ + public function testRoundUpToNextMultiple() + { + $this->assertEquals(roundUpToNextMultiple(39.5), 40); + $this->assertEquals(roundUpToNextMultiple(40), 40); + $this->assertEquals(roundUpToNextMultiple(40.4), 45); + $this->assertEquals(roundUpToNextMultiple(40.5), 45); + $this->assertEquals(roundUpToNextMultiple(44.5), 45); + + $this->assertEquals(roundUpToNextMultiple(39.5, 10), 40); + $this->assertEquals(roundUpToNextMultiple(40, 10), 40); + $this->assertEquals(roundUpToNextMultiple(40.5, 10), 50); + $this->assertEquals(roundUpToNextMultiple(44.5, 10), 50); + + $this->assertEquals(roundUpToNextMultiple(39.5, 6), 42); + $this->assertEquals(roundUpToNextMultiple(40, 6), 42); + $this->assertEquals(roundUpToNextMultiple(40.5, 6), 42); + $this->assertEquals(roundUpToNextMultiple(44.5, 6), 48); + } }