Merge branch 'develop' of git@github.com:Dolibarr/dolibarr.git into develop
This commit is contained in:
commit
bc7ca4a3cb
15 changed files with 288 additions and 74 deletions
29
.github/workflows/windows-ci.yml
vendored
29
.github/workflows/windows-ci.yml
vendored
|
|
@ -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%
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/bash -xv
|
||||
# Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
|
||||
# Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
|
||||
# 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
|
||||
|
|
|
|||
|
|
@ -166,6 +166,7 @@ if ($action == 'generate_key') {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* VIEW
|
||||
*/
|
||||
|
|
@ -256,7 +257,7 @@ if (getDolGlobalString('AI_MCP_ENABLED')) {
|
|||
print '<td>' . $langs->trans('ObfuscatePIIData') . '</td>';
|
||||
print '<td>';
|
||||
print ajax_constantonoff('AI_PRIVACY_REDACTION');
|
||||
print ' <span class="opacitymedium">' . $langs->trans("RedactionHelp") . '</span>';
|
||||
print ' <span class="opacitymedium">' . $langs->trans("RedactionHelp") . '</span>';
|
||||
print '</td>';
|
||||
print '</tr>';
|
||||
|
||||
|
|
@ -278,7 +279,7 @@ if (getDolGlobalString('AI_MCP_ENABLED')) {
|
|||
print '<td>' . $langs->trans('EnableLogging') . '</td>';
|
||||
print '<td>';
|
||||
print ajax_constantonoff('AI_LOG_REQUESTS');
|
||||
print ' <a href="' . DOL_URL_ROOT . '/ai/admin/log_viewer.php" target="_blank" class="button" style="padding-top: 4px; padding-bottom: 4px;">View Logs</a>';
|
||||
print ' <a href="' . DOL_URL_ROOT . '/ai/admin/log_viewer.php" target="_blank" style="padding-top: 4px; padding-bottom: 4px;">'.$langs->trans("ViewLogs").'</a>';
|
||||
print '</td>';
|
||||
print '</tr>';
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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.<br>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.<br>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.<br>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 #####
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -2731,10 +2731,33 @@ function top_menu_ai()
|
|||
$html = '';
|
||||
|
||||
if (isModEnabled('ai')) {
|
||||
$html .= '<!-- div for quick ai link -->
|
||||
<div id="topmenu-tool" class="atoplogin dropdown inline-block">
|
||||
<a accesskey="a" class="nofocusvisible" href="'.DOL_URL_ROOT.'/ai/assistant/index.php" title="'.$langs->trans('AIAssistant').' ('.$conf->browser->stringforfirstkey.' a)"><i class="fa fa-magic"></i></a>
|
||||
</div>';
|
||||
// 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 <a> 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,
|
||||
'<i class="fa fa-magic"></i>',
|
||||
'/ai/assistant/index.php',
|
||||
'',
|
||||
'nofocusvisible',
|
||||
'',
|
||||
'',
|
||||
'a'
|
||||
);
|
||||
$html .= '<!-- div for quick ai link (opens AI Assistant in popup) -->
|
||||
<div id="topmenu-tool" class="atoplogin dropdown inline-block">'.$aibtn.'</div>';
|
||||
}
|
||||
|
||||
return $html;
|
||||
|
|
|
|||
|
|
@ -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 '<input type="checkbox" name="includerefgeneration" id="includerefgeneration" value="includerefgeneration"> <label class="margintoponly" for="includerefgeneration">'.$form->textwithpicto($langs->trans("IncludeRefGeneration"), $langs->trans("IncludeRefGenerationHelp")).'</label><br>';
|
||||
print '<input type="checkbox" name="includedocgeneration" id="includedocgeneration" value="includedocgeneration"> <label for="includedocgeneration">'.$form->textwithpicto($langs->trans("IncludeDocGeneration"), $langs->trans("IncludeDocGenerationHelp")).'</label><br>';
|
||||
print '<input type="checkbox" name="generatepermissions" id="generatepermissions" value="generatepermissions"> <label for="generatepermissions">'.$form->textwithpicto($langs->trans("GeneratePermissions"), $langs->trans("GeneratePermissionsHelp")).'</label><br>';
|
||||
print '<input type="checkbox" name="nogeneratelines" id="nogeneratelines" value="nogeneratelines"> <label for="nogeneratelines">'.$form->textwithpicto($langs->trans("NoGenerateLines"), $langs->trans("NoGenerateLinesHelp")).'</label><br>';
|
||||
print '<br>';
|
||||
print '<input type="submit" class="button small" name="create" value="'.dol_escape_htmltag($langs->trans("GenerateCode")).'"'.($dirins ? '' : ' disabled="disabled"').'>';
|
||||
print '<br>';
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<array<string>|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<CommonObjectLine>|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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 '</table>';
|
||||
|
|
@ -523,8 +529,7 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea
|
|||
|
||||
print "</form>\n";
|
||||
}
|
||||
|
||||
|
||||
//END MODULEBUILDER LINES
|
||||
// Buttons for actions
|
||||
|
||||
if ($action != 'presend' && $action != 'editline') {
|
||||
|
|
|
|||
|
|
@ -657,7 +657,7 @@ print '<tr class="liste_titre">';
|
|||
print '<td>'.$langs->trans("Name").'</td>';
|
||||
print '<td>'.$langs->trans("Description").'</td>';
|
||||
print '<td class="center">'.$langs->trans("MustBeUnique").'</td>';
|
||||
print '<td class="center">'.$langs->trans("MustBeMandatory").'</td>';
|
||||
print '<td class="center">'.$langs->trans("MustBeMandatory").'<br><span class="opacitymedium">'.$langs->trans("MustBeMandatory2").'</span></td>';
|
||||
print '<td class="center">'.$langs->trans("MustBeInvoiceMandatory").'</td>';
|
||||
print "</tr>\n";
|
||||
|
||||
|
|
@ -857,9 +857,8 @@ if (!$conf->use_javascript_ajax) {
|
|||
print '</tr>';
|
||||
|
||||
|
||||
|
||||
print '<tr class="oddeven">';
|
||||
print '<td width="80%">'.$langs->trans("AddRefInList").'</td>';
|
||||
print '<td width="80%">'.$form->textwithpicto($langs->trans("AddRefInList"), $langs->trans("AddRefInListHelp")).'</td>';
|
||||
print '<td> </td>';
|
||||
print '<td class="center">';
|
||||
if (getDolGlobalString('SOCIETE_ADD_REF_IN_LIST')) {
|
||||
|
|
@ -887,7 +886,7 @@ print '</a></td>';
|
|||
print '</tr>';
|
||||
|
||||
print '<tr class="oddeven">';
|
||||
print '<td width="80%">'.$langs->trans("AddAdressInList").'</td>';
|
||||
print '<td width="80%">'.$form->textwithpicto($langs->trans("AddAdressInList"), $langs->trans("AddAdressInListHelp")).'</td>';
|
||||
print '<td> </td>';
|
||||
print '<td class="center">';
|
||||
if (getDolGlobalString('COMPANY_SHOW_ADDRESS_SELECTLIST')) {
|
||||
|
|
@ -901,7 +900,7 @@ print '</a></td>';
|
|||
print '</tr>';
|
||||
|
||||
print '<tr class="oddeven">';
|
||||
print '<td width="80%">'.$langs->trans("AddEmailPhoneTownInContactList").'</td>';
|
||||
print '<td width="80%">'.$form->textwithpicto($langs->trans("AddEmailPhoneTownInContactList"), $langs->trans("AddEmailPhoneTownInContactListHelp")).'</td>';
|
||||
print '<td> </td>';
|
||||
print '<td class="center">';
|
||||
if (getDolGlobalString('CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST')) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue