diff --git a/.github/workflows/windows-ci.yml b/.github/workflows/windows-ci.yml index 219658be32c..4497c798639 100644 --- a/.github/workflows/windows-ci.yml +++ b/.github/workflows/windows-ci.yml @@ -26,6 +26,7 @@ env: PHP_INI_SCAN_DIR: C:\myphpini CKEY: win-ci-2 GITHUB_JSON: ${{ toJSON(github) }} # Helps in debugging Github Action + GS_VERSION: "10.07.0" # Ghostscript version jobs: win-test: @@ -53,6 +54,27 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # phpts: ts # ts for thread-safe, default nts + # steps: + + - name: Restore cache Ghostscript (also stores cache at end of workflow) + uses: actions/cache@v5 + with: + path: | + C:\Program Files\gs + key: ${{ runner.os }}-imagick-ghostscript-${{ env.GS_VERSION }} + + - name: Install Ghostscript if not found + run: | + $gsBinDir = "C:\Program Files\gs\gs${{ env.GS_VERSION }}\bin" + echo "$gsBinDir" >> $env:GITHUB_PATH + $gsPath = "$gsBinDir\gswin64c.exe" + if (-not (Test-Path $gsPath)) { + choco install ghostscript -y --no-progress --version=${{ env.GS_VERSION }} + } + if (-not (Test-Path "$gsBinDir\gs.exe")) { + New-Item -ItemType SymbolicLink -Path "$gsBinDir\gs.exe" -Target $gsPath -Force + } + DIR "$gsBinDir" # Restore cache - name: Restore cache @@ -96,10 +118,13 @@ jobs: SET DATA_DIR=%CD%\documents SET TEST_DIR=%CD%\test SET INITDEMO_DIR=%CD%\dev\initdemo + SET SESSION_DIR=%TEMP%\php_sessions + mkdir %SESSION_DIR% ECHO "==== Create INI file to set open_basedir ===" echo [php] > %INIFILE% - echo open_basedir^="%HTDOCS_DIR%;%DATA_DIR%;%TEST_DIR%;%INITDEMO_DIR%;%PHPROOT%" >> %INIFILE% - REM Unset PHP_INI_SCAN_DIR to disable open_basedir restritions (to limit debug effort) + echo open_basedir^="%HTDOCS_DIR%;%DATA_DIR%;%TEST_DIR%;%INITDEMO_DIR%;%PHPROOT%;%SESSION_DIR%" >> %INIFILE% + echo session.save_path^="%SESSION_DIR%" >> %INIFILE% + REM Unset PHP_INI_SCAN_DIR to disable open_basedir restrictions (to limit debug effort) REM SET PHP_INI_SCAN_DIR= ECHO "==== Show contents of INI file to set open_basedir ===" type %INIFILE% diff --git a/dev/setup/phpunit/setup_conf.sh b/dev/setup/phpunit/setup_conf.sh index 248d51e3bff..99b211947bd 100755 --- a/dev/setup/phpunit/setup_conf.sh +++ b/dev/setup/phpunit/setup_conf.sh @@ -1,5 +1,5 @@ #!/usr/bin/bash -xv -# Copyright (C) 2024-2025 MDW +# Copyright (C) 2024-2026 MDW # shellcheck disable=2050,2089,2090,2086 ME=$(realpath "$0") @@ -184,9 +184,9 @@ if [ "$DB" = 'mysql' ] || [ "$DB" = 'mariadb' ] || [ "$DB" = 'postgresql' ]; the # Compute md5 based on install file contents, and on db prefix # filefunc.inc.php holds the version, so include it" # shellcheck disable=2046 - sum=$(md5sum "$ME" $(find "${TRAVIS_BUILD_DIR}/htdocs/install" -type f ; echo "${TRAVIS_BUILD_DIR}/filefunc.inc.php" ) | md5sum) + sum=$(md5sum "$ME" $(find "${TRAVIS_BUILD_DIR}/htdocs/install" -type f -not -path "${TRAVIS_BUILD_DIR}/htdocs/install/doctemplates/*" ; echo "${TRAVIS_BUILD_DIR}/filefunc.inc.php" ) | md5sum) # shellcheck disable=2046 - cnt=$(md5sum "$ME" $(find "${TRAVIS_BUILD_DIR}/htdocs/install" -type f) | wc) + cnt=$(md5sum "$ME" $(find "${TRAVIS_BUILD_DIR}/htdocs/install" -type f -not -path "${TRAVIS_BUILD_DIR}/htdocs/install/doctemplates/*") | wc) echo "MD5SUM $sum COUNT:$cnt" load_cache=0 if [ -r "$DB_CACHE_FILE".md5 ] && [ -r "$DB_CACHE_FILE" ] && [ -x "$(which "${MYSQLDUMP}")" ] ; then diff --git a/htdocs/ai/admin/server_mcp.php b/htdocs/ai/admin/server_mcp.php index 59827e32945..fe74416ee2c 100644 --- a/htdocs/ai/admin/server_mcp.php +++ b/htdocs/ai/admin/server_mcp.php @@ -166,6 +166,7 @@ if ($action == 'generate_key') { } } + /* * VIEW */ @@ -256,7 +257,7 @@ if (getDolGlobalString('AI_MCP_ENABLED')) { print '' . $langs->trans('ObfuscatePIIData') . ''; print ''; print ajax_constantonoff('AI_PRIVACY_REDACTION'); - print ' ' . $langs->trans("RedactionHelp") . ''; + print '   ' . $langs->trans("RedactionHelp") . ''; print ''; print ''; @@ -278,7 +279,7 @@ if (getDolGlobalString('AI_MCP_ENABLED')) { print '' . $langs->trans('EnableLogging') . ''; print ''; print ajax_constantonoff('AI_LOG_REQUESTS'); - print ' View Logs'; + print '   '.$langs->trans("ViewLogs").''; print ''; print ''; diff --git a/htdocs/ai/css/ai_assistant.css b/htdocs/ai/css/ai_assistant.css index a525d72552f..a3d46bdf2e2 100644 --- a/htdocs/ai/css/ai_assistant.css +++ b/htdocs/ai/css/ai_assistant.css @@ -1,7 +1,7 @@ /* Container */ .ai-chat-container { - max-width: 900px; - margin: 20px auto; + /* max-width: 900px; */ + /* margin: 20px auto; */ background: #fff; border: 1px solid #e0e0e0; border-radius: 8px; @@ -426,3 +426,79 @@ background-color: #6c757d; border-color: #6c757d; } + + +/* ------------------------------------------------------------------------- + * Popup mode (loaded via dol_openinpopup URL param from the top-toolbar + * AI icon -> dolButtonToOpenUrlInDialogPopup() iframe). + * + * Dolibarr core adds the body class "dol_openinpopup" when this param is + * set (htdocs/main.inc.php), so we can target popup-only tweaks here + * without touching index.php. + * + * Sizing strategy: + * The chat-container in standalone mode uses "height: 70vh; min-height: + * 650px" which is great on a full page but causes the dialog iframe to + * overflow on small windows. In popup mode we make the chat-container + * fill the iframe viewport exactly (height: 100% of body, body itself + * pinned to 100vh of iframe) and drop the 650px floor; .chat-history + * keeps its flex-grow:1 + overflow-y:auto so message scroll still works + * INTERNALLY without an iframe-level scrollbar. + * + * We also flatten the visual chrome (border, shadow, rounded corners, + * capped max-width, top margin) that is now redundant with the dialog + * frame, and hide the duplicate "AI Assistant" H2 (the dialog title bar + * already shows it). + * ------------------------------------------------------------------------- */ +body.dol_openinpopup { + margin: 0; + padding: 0; + height: 100vh; + overflow: hidden; +} + +/* In popup mode the DOM is: + * body > #id-container > .fiche > .ai-chat-container + * (top_menu and left_menu blocks are skipped by main.inc.php when + * dol_openinpopup is set, but #id-container and .fiche are always + * emitted). For "height: 100%" on .ai-chat-container to resolve to + * the iframe viewport, every ancestor in the chain must also be + * 100% high. */ +body.dol_openinpopup #id-container, +body.dol_openinpopup .fiche { + height: 100%; + margin: 0; + padding: 0; +} + +body.dol_openinpopup .ai-chat-container { + max-width: none; + margin: 0; + border: 0; + border-radius: 0; + box-shadow: none; + height: 100%; + min-height: 0; + max-height: 100%; +} + +/* Flexbox shrink fix: a flex-grow:1 child defaults to min-height:auto + * (content-based), which prevents the chat-history from shrinking below + * its message-content height and causes the WHOLE container to overflow + * the iframe after a long response. Forcing min-height:0 lets the + * chat-history shrink to fit the available column space and scroll + * INTERNALLY (overflow-y:auto is already on .chat-history). */ +body.dol_openinpopup .chat-history { + min-height: 0; +} + +/* The dialog title bar already shows "AI Assistant", so hide the redundant + * H2 inside .chat-header but keep .header-controls (Clear + mode select). */ +body.dol_openinpopup .chat-header { + border-radius: 0; + justify-content: flex-end; +} + +body.dol_openinpopup .chat-header h2 { + display: none; +} diff --git a/htdocs/core/lib/files.lib.php b/htdocs/core/lib/files.lib.php index f1a53d32f3a..e44a7245209 100644 --- a/htdocs/core/lib/files.lib.php +++ b/htdocs/core/lib/files.lib.php @@ -850,6 +850,52 @@ function dolReplaceInFile($srcfile, $arrayreplacement, $destfile = '', $newmask return 1; } +/** + * Removes content from a file that matches a given pattern. + * + * @param string $filePath Path to the file to be processed. + * @param string $pattern Regular expression pattern to identify the content to remove. + * @return bool Returns true if the operation was successful, false otherwise. + */ +function removePatternFromFile(string $filePath, string $pattern): bool +{ + // Check if the file exists + if (! file_exists($filePath)) { + dol_syslog("files.lib.php::removePatternFromFile: File $filePath does not exist", LOG_WARNING); + + return false; + } + + // Read the file content + $content = file_get_contents($filePath); + if ($content === false) { + dol_syslog("files.lib.php::removePatternFromFile: Unable to read the file $filePath", LOG_WARNING); + + return false; + } + + // Remove content matching the pattern + $updatedContent = preg_replace($pattern, '', $content); + if ($updatedContent === null) { + dol_syslog("files.lib.php::removePatternFromFile: Error while processing the file $filePath", LOG_WARNING); + + return false; + } + + // Write the updated content back to the file + $result = file_put_contents($filePath, $updatedContent); + if ($result === false) { + dol_syslog("files.lib.php::removePatternFromFile: Permission denied to overwrite the target file $filePath", LOG_WARNING); + + return false; + } + + dol_syslog("files.lib.php::removePatternFromFile: Content successfully removed in the file $filePath", LOG_INFO); + + return true; +} + + /** * Copy a file to another file. diff --git a/htdocs/core/modules/project/doc/doc_generic_project_odt.modules.php b/htdocs/core/modules/project/doc/doc_generic_project_odt.modules.php index 2f9d72ea3a3..706debb3be3 100644 --- a/htdocs/core/modules/project/doc/doc_generic_project_odt.modules.php +++ b/htdocs/core/modules/project/doc/doc_generic_project_odt.modules.php @@ -683,8 +683,8 @@ class doc_generic_project_odt extends ModelePDFProjects // Security check $socid = 0; - if (!empty($object->fk_soc)) { - $socid = $object->fk_soc; + if (!empty($object->socid)) { + $socid = $object->socid; } $tasksarray = $taskstatic->getTasksArray(null, null, $object->id, $socid, 0); diff --git a/htdocs/langs/en_US/admin.lang b/htdocs/langs/en_US/admin.lang index 607c2517949..5edb0131cbc 100644 --- a/htdocs/langs/en_US/admin.lang +++ b/htdocs/langs/en_US/admin.lang @@ -1497,10 +1497,13 @@ BrowserIsKO=You are using the %s web browser. This browser is known to be a bad PHPModuleLoaded=PHP component %s is loaded PreloadOPCode=Preloaded OPCode is used APCCacheInstalledButDisabled=APCCache installed but is disabled -AddRefInList=Display Customer/Vendor ref. into combo lists.
Third Parties will appear with a name format of "CC12345 - SC45678 - The Big Company corp." instead of "The Big Company corp". +AddRefInList=Display Customer/Vendor ref. into combo lists. +AddRefInListHelp=Third Parties will appear with a name format of "CC12345 - SC45678 - The Big Company corp." instead of "The Big Company corp". AddVatInList=Display Customer/Vendor VAT number into combo lists. -AddAdressInList=Display Customer/Vendor address into combo lists.
Third Parties will appear with a name format of "The Big Company corp. - 21 jump street 123456 Big town - USA" instead of "The Big Company corp". -AddEmailPhoneTownInContactList=Display Contact email (or phones if not defined) and town into combo lists.
Contacts will appear with a name format of "Dupond Durand - dupond.durand@example.com - Paris" or "Dupond Durand - 06 07 59 65 66 - Paris" instead of "Dupond Durand". +AddAdressInList=Display Customer/Vendor address into combo lists. +AddAdressInListHelp=Third Parties will appear with a name format of "The Big Company corp. - 21 jump street 123456 Big town - USA" instead of "The Big Company corp". +AddEmailPhoneTownInContactList=Display Contact email (or phones if not defined) and town into combo lists. +AddEmailPhoneTownInContactListHelp=Contacts will appear with a name format of "Dupond Durand - dupond.durand@example.com - Paris" or "Dupond Durand - 06 07 59 65 66 - Paris" instead of "Dupond Durand". AskForPreferredShippingMethod=Ask for preferred shipping method for Third Parties. FieldEdition=Modification of field %s FillThisOnlyIfRequired=Example: +2 (fill only if timezone offset problems are experienced) @@ -1539,7 +1542,8 @@ WatermarkOnDraft=Watermark on draft document JSOnPaimentBill=Activate feature to autofill payment lines on payment form CompanyIdProfChecker=Rules for Professional IDs MustBeUnique=Must be unique? -MustBeMandatory=Mandatory to create third parties (if VAT number or type of company defined) ? +MustBeMandatory=Mandatory to create third parties? +MustBeMandatory2=(if VAT number or type of company is defined) MustBeInvoiceMandatory=Mandatory to validate invoices? TechnicalServicesProvided=Technical services provided ##### WebDAV ##### diff --git a/htdocs/langs/en_US/modulebuilder.lang b/htdocs/langs/en_US/modulebuilder.lang index f79b7b980fb..ba852c68cf3 100644 --- a/htdocs/langs/en_US/modulebuilder.lang +++ b/htdocs/langs/en_US/modulebuilder.lang @@ -170,6 +170,8 @@ DefinePropertiesFromExistingTableDesc=If a table in the database (for the object DefinePropertiesFromExistingTableDesc2=Keep empty if the table does not exist yet. The code generator will use different kinds of fields to build an example of table that you can edit later. GeneratePermissions=I want to manage permissions on this object GeneratePermissionsHelp=If you check this, some code will be added to manage permissions to read, write and delete record of the objects +NoGenerateLines=I don't want to manage lines on this object +NoGenerateLinesHelp=If you check this, some code will be removed to manage lines of the objects PermissionDeletedSuccesfuly=Permission has been successfully removed PermissionUpdatedSuccesfuly=Permission has been successfully updated PermissionAddedSuccesfuly=Permission has been successfully added diff --git a/htdocs/main.inc.php b/htdocs/main.inc.php index cd6dc55dc06..d058eb9b2d9 100644 --- a/htdocs/main.inc.php +++ b/htdocs/main.inc.php @@ -2731,10 +2731,33 @@ function top_menu_ai() $html = ''; if (isModEnabled('ai')) { - $html .= ' - '; + // Open the AI Assistant in a popup overlay rather than navigating away, + // so the user keeps their current page context while interacting with + // the assistant. Uses the standard Dolibarr helper which builds an + // iframe-in-jQuery-UI-dialog (modal, 80% width, height-150) and auto- + // appends dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup=NAME + // so the embedded page renders without the surrounding chrome. + // JS-disabled fallback: the helper degrades to target="_blank". + // $label is used by dolButtonToOpenUrlInDialogPopup() both for the + // title="" tooltip on the AND for the jQuery UI dialog title. + // Include the keyboard-shortcut hint (matching the convention used + // e.g. by the PublicVirtualCardUrl call earlier in this file and by + // bookmark/quickadd/search) so the icon tooltip on hover reads e.g. + // "AI Assistant (Ctrl Alt a)" -- the dialog title shows the same. + $ailabel = $langs->trans('AIAssistant').' ('.$conf->browser->stringforfirstkey.' a)'; + $aibtn = dolButtonToOpenUrlInDialogPopup( + 'aiassistant', + $ailabel, + '', + '/ai/assistant/index.php', + '', + 'nofocusvisible', + '', + '', + 'a' + ); + $html .= ' + '; } return $html; diff --git a/htdocs/modulebuilder/index.php b/htdocs/modulebuilder/index.php index 1738429e070..aa2358c5f40 100644 --- a/htdocs/modulebuilder/index.php +++ b/htdocs/modulebuilder/index.php @@ -1656,6 +1656,29 @@ if ($dirins && $action == 'initobject' && $module && $objectname) { // Test on $filetogenerate[] = 'core/modules/mod'.$module.'.class.php'; } + if (! $error && GETPOST('nogeneratelines', 'aZ09')) { + $checkComment = checkExistComment($moduledescriptorfile, 0); + if ($checkComment < 0) { + $warning++; + setEventMessages($langs->trans("WarningCommentNotFound", $langs->trans("Menus"), basename($moduledescriptorfile)), null, 'warnings'); + } else { + // File path + $TFilePaths = [ + $destdir . '/class/' . strtolower($objectname) . '.class.php', + $destdir . '/class/api_' . strtolower($module) . '.class.php', + $destdir . '/' . strtolower($objectname) . '_card.php' + ]; + + // Pattern to remove everything between the tags + $pattern = '/\/\/BEGIN MODULEBUILDER LINES.*?\/\/END MODULEBUILDER LINES\s*/s'; + foreach ($TFilePaths as $filePath) { + if (! removePatternFromFile($filePath, $pattern)) { + $error++; + } + } + } + } + if (!$error) { // Edit PHP files to make replacement foreach ($filetogenerate as $destfile) { @@ -4150,6 +4173,7 @@ if ($module == 'initmodule') { print '
'; print '
'; print '
'; + print '
'; print '
'; print ''; print '
'; diff --git a/htdocs/modulebuilder/template/class/api_mymodule.class.php b/htdocs/modulebuilder/template/class/api_mymodule.class.php index 2074351fc21..6ec62448804 100644 --- a/htdocs/modulebuilder/template/class/api_mymodule.class.php +++ b/htdocs/modulebuilder/template/class/api_mymodule.class.php @@ -416,7 +416,7 @@ class MyModuleApi extends DolibarrApi unset($object->rowid); unset($object->canvas); - + //BEGIN MODULEBUILDER LINES // If object has lines, remove $db property if (isset($object->lines) && is_array($object->lines) && count($object->lines) > 0) { $nboflines = count($object->lines); @@ -427,7 +427,7 @@ class MyModuleApi extends DolibarrApi unset($object->lines[$i]->note); } } - + //END MODULEBUILDER LINES return $object; } } diff --git a/htdocs/modulebuilder/template/class/myobject.class.php b/htdocs/modulebuilder/template/class/myobject.class.php index 564564d2d20..5170570658c 100644 --- a/htdocs/modulebuilder/template/class/myobject.class.php +++ b/htdocs/modulebuilder/template/class/myobject.class.php @@ -205,43 +205,42 @@ class MyObject extends CommonObject public $import_key; // END MODULEBUILDER PROPERTIES - + //BEGIN MODULEBUILDER LINES // If this object has a subtable with lines - // /** - // * @var string Name of subtable line - // */ - // public $table_element_line = 'mymodule_myobjectline'; + /** + * @var string Name of subtable line + */ + public $table_element_line = 'mymodule_myobjectline'; - // /** - // * @var string Field name with ID of parent key if this object has a parent, Or Field name of in child tables to link to this record. - // */ - // public $fk_element = 'fk_myobject'; + /** + * @var string Field with ID of parent key if this object has a parent + */ + public $fk_element = 'fk_myobject'; - // /** - // * @var string Name of subtable class that manage subtable lines - // */ - // public $class_element_line = 'MyObjectline'; + /** + * @var string Name of subtable class that manage subtable lines + */ + public $class_element_line = 'MyObjectline'; - // /** - // * @var array List of child tables. To test if we can delete object. - // */ - // protected $childtables = array('mychildtable' => array('name'=>'MyObject', 'fk_element'=>'fk_myobject')); + /** + * @var array|string> List of child tables. To test if we can delete object. + */ + protected $childtables = array('mychildtable' => array('name'=>'MyObject', 'fk_element'=>'fk_myobject')); - // /** - // * @var array List of child tables. To know object to delete on cascade. - // * If name matches '@ClassName:FilePathClass:ParentFkFieldName' (the recommended mode) it will - // * call method ClassName->deleteByParentField(parentId, 'ParentFkFieldName') to fetch and delete child object. - // * Using an array like childtables should not be implemented because a child may have other child, so we must only use the method that call deleteByParentField(). - // */ - // protected $childtablesoncascade = array('mymodule_myobjectdet'); - - // /** - // * @var MyObjectLine[] Array of subtable lines - // */ - // public $lines = array(); + /** + * @var string[] List of child tables. To know object to delete on cascade. + * If name matches '@ClassNAme:FilePathClass;ParentFkFieldName' it will + * call method deleteByParentField(parentId, ParentFkFieldName) to fetch and delete child object + */ + protected $childtablesoncascade = array('mymodule_myobjectdet'); + /** + * @var MyObjectLine[] Array of subtable lines + */ + public $lines = array(); + //END MODULEBUILDER LINES /** * Constructor @@ -327,14 +326,15 @@ class MyObject extends CommonObject // Load source object $result = $object->fetchCommon($fromid); + //BEGIN MODULEBUILDER LINES if ($result > 0 && !empty($object->table_element_line)) { $object->fetchLines(); } // get lines so they will be clone - //foreach($this->lines as $line) - // $line->fetch_optionals(); - + foreach ($this->lines as $line) + $line->fetch_optionals(); + //END MODULEBUILDER LINES // Reset some properties unset($object->id); unset($object->fk_user_creat); @@ -418,9 +418,11 @@ class MyObject extends CommonObject public function fetch($id, $ref = null, $noextrafields = 0, $nolines = 0) { $result = $this->fetchCommon($id, $ref, '', $noextrafields); + //BEGIN MODULEBUILDER LINES if ($result > 0 && !empty($this->table_element_line) && empty($nolines)) { $this->fetchLines($noextrafields); } + //END MODULEBUILDER LINES return $result; } @@ -437,8 +439,7 @@ class MyObject extends CommonObject $result = $this->fetchLinesCommon('', $noextrafields); return $result; } - - + //END MODULEBUILDER LINES /** * Load list of objects in memory from the database. * Using a fetchAll() with limit = 0 is a very bad practice. Instead try to forge yourself an optimized SQL request with @@ -545,6 +546,7 @@ class MyObject extends CommonObject //return $this->deleteCommon($user, $notrigger, 1); } + //BEGIN MODULEBUILDER LINES /** * Delete a line of object in database * @@ -562,6 +564,7 @@ class MyObject extends CommonObject return $this->deleteLineCommon($user, $idline, $notrigger); } + //END MODULEBUILDER LINES /** @@ -1107,10 +1110,11 @@ class MyObject extends CommonObject return $this->initAsSpecimenCommon(); } + //BEGIN MODULEBUILDER LINES /** * Create an array of lines * - * @return CommonObjectLine[]|int array of lines if OK, <0 if KO + * @return array|int array of lines if OK, <0 if KO */ public function getLinesArray() { @@ -1123,10 +1127,12 @@ class MyObject extends CommonObject $this->setErrorsFromObject($objectline); return $result; } else { + /** @phpstan-ignore-next-line */ $this->lines = $result; return $this->lines; } } + //END MODULEBUILDER LINES /** * Returns the reference to the following non used object depending on the active numbering module. @@ -1273,7 +1279,7 @@ class MyObject extends CommonObject require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php'; - +//BEGIN MODULEBUILDER LINES /** * Class MyObjectLine. You can also remove this and generate a CRUD class for lines objects. */ @@ -1316,3 +1322,4 @@ class MyObjectLine extends CommonObjectLine $this->db = $db; } } +//END MODULEBUILDER LINES diff --git a/htdocs/modulebuilder/template/core/modules/mymodule/doc/pdf_standard_myobject.modules.php b/htdocs/modulebuilder/template/core/modules/mymodule/doc/pdf_standard_myobject.modules.php index 202bed7aa7e..eab91fc4b7e 100644 --- a/htdocs/modulebuilder/template/core/modules/mymodule/doc/pdf_standard_myobject.modules.php +++ b/htdocs/modulebuilder/template/core/modules/mymodule/doc/pdf_standard_myobject.modules.php @@ -37,6 +37,7 @@ dol_include_once('/mymodule/core/modules/mymodule/modules_myobject.php'); require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/class/commoninvoice.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/pdf.lib.php'; @@ -668,11 +669,11 @@ class pdf_standard_myobject extends ModelePDFMyObject $sign = 1; // Collection of totals by value of VAT in $this->tva["taux"]=total_tva $prev_progress = $object->lines[$i]->get_prev_progress($object->id); - if ($prev_progress > 0 && $object->lines instanceof CommonInvoiceLine && !empty($object->lines[$i]->situation_percent)) { // Compute progress from previous situation + if ($prev_progress > 0 && $object->lines[$i] instanceof CommonInvoiceLine && !empty($object->lines[$i]->situation_percent)) { // Compute progress from previous situation if (isModEnabled("multicurrency") && $object->multicurrency_tx != 1) { - $tvaligne = $sign * $object->lines[$i]->multicurrency_total_tva * ($object->lines[$i]->situation_percent - $prev_progress) / $object->lines[$i]->situation_percent; + $tvaligne = $sign * $object->lines[$i]->multicurrency_total_tva * ($object->lines[$i]->situation_percent - $prev_progress) / $object->lines[$i]->situation_percent; // @phan-suppress-current-line PhanUndeclaredProperty } else { - $tvaligne = $sign * $object->lines[$i]->total_tva * ($object->lines[$i]->situation_percent - $prev_progress) / $object->lines[$i]->situation_percent; + $tvaligne = $sign * $object->lines[$i]->total_tva * ($object->lines[$i]->situation_percent - $prev_progress) / $object->lines[$i]->situation_percent; // @phan-suppress-current-line PhanUndeclaredProperty } } else { if (isModEnabled("multicurrency") && $object->multicurrency_tx != 1) { @@ -684,10 +685,11 @@ class pdf_standard_myobject extends ModelePDFMyObject $localtax1ligne = $object->lines[$i]->total_localtax1; $localtax2ligne = $object->lines[$i]->total_localtax2; + // @phan-suppress-next-line PhanUndeclaredProperty $localtax1_rate = $object->lines[$i]->localtax1_tx; - $localtax2_rate = $object->lines[$i]->localtax2_tx; - $localtax1_type = $object->lines[$i]->localtax1_type; - $localtax2_type = $object->lines[$i]->localtax2_type; + $localtax2_rate = $object->lines[$i]->localtax2_tx; // @phan-suppress-current-line PhanUndeclaredProperty + $localtax1_type = $object->lines[$i]->localtax1_type; // @phan-suppress-current-line PhanUndeclaredProperty + $localtax2_type = $object->lines[$i]->localtax2_type; // @phan-suppress-current-line PhanUndeclaredProperty $vatrate = (string) $object->lines[$i]->tva_tx; @@ -724,7 +726,7 @@ class pdf_standard_myobject extends ModelePDFMyObject $this->tva[$vatrate] = 0; } $this->tva[$vatrate] += $tvaligne; - $vatcode = $object->lines[$i]->vat_src_code; + $vatcode = $object->lines[$i]->vat_src_code; // @phan-suppress-current-line PhanUndeclaredProperty if (empty($this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['amount'])) { $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['amount'] = 0; } @@ -761,7 +763,7 @@ class pdf_standard_myobject extends ModelePDFMyObject } } - if (isset($object->lines[$i + 1]->pagebreak) && $object->lines[$i + 1]->pagebreak) { + if (isset($object->lines[$i + 1]->pagebreak) && $object->lines[$i + 1]->pagebreak) { // @phan-suppress-current-line PhanUndeclaredProperty if ($pagenb == $pageposafter) { $this->_tableau($pdf, $tab_top, $this->page_hauteur - $tab_top - $heightforfooter, 0, $outputlangs, $hidetop, 1, $object->multicurrency_code, $outputlangsbis); } else { diff --git a/htdocs/modulebuilder/template/myobject_card.php b/htdocs/modulebuilder/template/myobject_card.php index 86c7fa3cdfe..05abb7f419f 100644 --- a/htdocs/modulebuilder/template/myobject_card.php +++ b/htdocs/modulebuilder/template/myobject_card.php @@ -211,8 +211,10 @@ if (empty($reshook)) { // Actions when printing a doc from card include DOL_DOCUMENT_ROOT.'/core/actions_printing.inc.php'; + //BEGIN MODULEBUILDER LINES // Action to move up and down lines of object - //include DOL_DOCUMENT_ROOT.'/core/actions_lineupdown.inc.php'; + include DOL_DOCUMENT_ROOT.'/core/actions_lineupdown.inc.php'; + //END MODULEBUILDER LINES // Action to build doc include DOL_DOCUMENT_ROOT.'/core/actions_builddoc.inc.php'; @@ -352,10 +354,12 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea if ($action == 'delete' || ($conf->use_javascript_ajax && empty($conf->dol_use_jmobile))) { $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('DeleteMyObject'), $langs->trans('ConfirmDeleteObject'), 'confirm_delete', '', 0, 'action-delete'); } + //BEGIN MODULEBUILDER LINES // Confirmation to delete line if ($action == 'deleteline') { $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id.'&lineid='.$lineid, $langs->trans('DeleteLine'), $langs->trans('ConfirmDeleteLine'), 'confirm_deleteline', '', 0, 1); } + //END MODULEBUILDER LINES // Clone confirmation if ($action == 'clone') { @@ -470,7 +474,7 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea print dol_get_fiche_end(); - + //BEGIN MODULEBUILDER LINES /* * Lines */ @@ -500,6 +504,7 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea $object->printObjectLines($action, $mysoc, null, GETPOSTINT('lineid'), 1); } + //BEGIN MODULEBUILDER LINES // Form to add new line if ($object->status == 0 && $permissiontoadd && $action != 'selectlines') { if ($action != 'editline') { @@ -515,6 +520,7 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea } } } + //END MODULEBUILDER LINES if (!empty($object->lines) || ($object->status == $object::STATUS_DRAFT && $permissiontoadd && $action != 'selectlines' && $action != 'editline')) { print ''; @@ -523,8 +529,7 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea print "\n"; } - - + //END MODULEBUILDER LINES // Buttons for actions if ($action != 'presend' && $action != 'editline') { diff --git a/htdocs/societe/admin/societe.php b/htdocs/societe/admin/societe.php index 788e6e8faa0..4a93b32714b 100644 --- a/htdocs/societe/admin/societe.php +++ b/htdocs/societe/admin/societe.php @@ -657,7 +657,7 @@ print ''; print ''.$langs->trans("Name").''; print ''.$langs->trans("Description").''; print ''.$langs->trans("MustBeUnique").''; -print ''.$langs->trans("MustBeMandatory").''; +print ''.$langs->trans("MustBeMandatory").'
'.$langs->trans("MustBeMandatory2").''; print ''.$langs->trans("MustBeInvoiceMandatory").''; print "\n"; @@ -857,9 +857,8 @@ if (!$conf->use_javascript_ajax) { print ''; - print ''; -print ''.$langs->trans("AddRefInList").''; +print ''.$form->textwithpicto($langs->trans("AddRefInList"), $langs->trans("AddRefInListHelp")).''; print ' '; print ''; if (getDolGlobalString('SOCIETE_ADD_REF_IN_LIST')) { @@ -887,7 +886,7 @@ print '
'; print ''; print ''; -print ''.$langs->trans("AddAdressInList").''; +print ''.$form->textwithpicto($langs->trans("AddAdressInList"), $langs->trans("AddAdressInListHelp")).''; print ' '; print ''; if (getDolGlobalString('COMPANY_SHOW_ADDRESS_SELECTLIST')) { @@ -901,7 +900,7 @@ print ''; print ''; print ''; -print ''.$langs->trans("AddEmailPhoneTownInContactList").''; +print ''.$form->textwithpicto($langs->trans("AddEmailPhoneTownInContactList"), $langs->trans("AddEmailPhoneTownInContactListHelp")).''; print ' '; print ''; if (getDolGlobalString('CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST')) {