Merge branch 'develop' into 23_accpicto2

This commit is contained in:
Alexandre SPANGARO
2025-11-28 14:58:27 +01:00
25 changed files with 1212 additions and 762 deletions

View File

@@ -40,7 +40,7 @@ require '../../main.inc.php';
* @var string $dolibarr_main_restrict_ip
* @var string $dolibarr_main_db_pass
* @var string $dolibarr_main_db_encrypted_pass
* @var string $dolibarr_main_stream_to_disable
* @var string|string[] $dolibarr_main_stream_to_disable
* @var string $dolibarr_nocsrfcheck
*/
require_once DOL_DOCUMENT_ROOT.'/core/lib/memory.lib.php';

View File

@@ -169,7 +169,7 @@ if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x'
include DOL_DOCUMENT_ROOT.'/core/actions_linkedfiles.inc.php';
if (GETPOST('action') == 'upload' && $user->hasRight('blockedlog', 'read')) { // read is read/upload for blockedlog
if (GETPOST('action') == 'export' && $user->hasRight('blockedlog', 'read')) { // read is read/export for blockedlog
$error = 0;
$previoushash = '';
@@ -184,16 +184,20 @@ if (GETPOST('action') == 'upload' && $user->hasRight('blockedlog', 'read')) { /
$error++;
}
$dates = dol_get_first_day(GETPOSTINT('yeartoexport'), GETPOSTINT('monthtoexport') ? GETPOSTINT('monthtoexport') : 1);
$datee = dol_get_last_day(GETPOSTINT('yeartoexport'), GETPOSTINT('monthtoexport') ? GETPOSTINT('monthtoexport') : 12);
if ($datee >= dol_now()) {
setEventMessages($langs->trans("ErrorPeriodMustBePastToAllowExport"), null, "errors");
$error++;
}
if (!$error) {
// Get the ID of the first line qualified
$sql = "SELECT rowid,date_creation,tms,user_fullname,action,amounts,element,fk_object,date_object,ref_object,signature,fk_user,object_data";
$sql .= " FROM ".MAIN_DB_PREFIX."blockedlog";
$sql .= " WHERE entity = ".((int) $conf->entity);
if (GETPOSTINT('monthtoexport') > 0 || GETPOSTINT('yeartoexport') > 0) {
$dates = dol_get_first_day(GETPOSTINT('yeartoexport'), GETPOSTINT('monthtoexport') ? GETPOSTINT('monthtoexport') : 1);
$datee = dol_get_last_day(GETPOSTINT('yeartoexport'), GETPOSTINT('monthtoexport') ? GETPOSTINT('monthtoexport') : 12);
$sql .= " AND date_creation BETWEEN '".$db->idate($dates)."' AND '".$db->idate($datee)."'";
}
$sql .= " AND date_creation BETWEEN '".$db->idate($dates)."' AND '".$db->idate($datee)."'";
$sql .= " ORDER BY rowid ASC"; // Required so we get the first one
$sql .= $db->plimit(1);
@@ -518,7 +522,7 @@ if ($action == 'deletefile') {
print '<form method="POST" id="exportArchives" action="'.$_SERVER["PHP_SELF"].'?output=file">';
print '<input type="hidden" name="token" value="'.newToken().'">';
print '<input type="hidden" name="action" value="upload">';
print '<input type="hidden" name="action" value="export">';
print '<div class="right">';

View File

@@ -100,11 +100,6 @@ $cancel = GETPOST('cancel', 'alpha');
$backtopage = GETPOST('backtopage', 'alpha'); // if not set, a default page will be used
$backtopageforcancel = GETPOST('backtopageforcancel', 'alpha'); // if not set, $backtopage will be used
$lineid = GETPOSTINT('lineid');
$userid = GETPOSTINT('userid');
$search_ref = GETPOST('sf_ref', 'alpha') ? GETPOST('sf_ref', 'alpha') : GETPOST('search_ref', 'alpha');
$search_societe = GETPOST('search_societe', 'alpha');
$search_montant_ht = GETPOST('search_montant_ht', 'alpha');
$search_montant_ttc = GETPOST('search_montant_ttc', 'alpha');
$origin = GETPOST('origin', 'alpha');
$originid = (GETPOSTINT('originid') ? GETPOSTINT('originid') : GETPOSTINT('origin_id')); // For backward compatibility
$fac_rec = GETPOSTINT('fac_rec');
@@ -123,8 +118,6 @@ $hideref = (GETPOSTINT('hideref') ? GETPOSTINT('hideref') : (getDolGlobalString(
// Number of lines for predefined product/service choices
$NBLINES = 4;
$usehm = getDolGlobalInt('MAIN_USE_HOURMIN_IN_DATE_RANGE');
$object = new Facture($db);
$extrafields = new ExtraFields($db);
@@ -1274,7 +1267,7 @@ if (empty($reshook)) {
$sourceinvoice = GETPOSTINT('fac_avoir');
if (!($sourceinvoice > 0) && !getDolGlobalString('INVOICE_CREDIT_NOTE_STANDALONE')) {
$error++;
setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("CorrectInvoice")), null, 'errors');
setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("InvoiceAvoirAskCombo")), null, 'errors');
$action = 'create';
}
@@ -4309,7 +4302,7 @@ if ($action == 'create') {
});
</script>';
$text = '<label>'.$tmp.$langs->transnoentities("InvoiceAvoirAsk").'</label> ';
$text .= '<select class="flat valignmiddle" name="fac_avoir" id="fac_avoir"';
$text .= '<select class="flat valignmiddle minwidth200" name="fac_avoir" id="fac_avoir"';
if (!$optionsav || $invoice_predefined->id > 0) {
$text .= ' disabled';
}
@@ -4325,9 +4318,10 @@ if ($action == 'create') {
print $desc;
print '<div id="credit_note_options" class="clearboth paddingtop marginbottomonly">';
print '<div class="marginleftlarge"><input type="checkbox" name="invoiceAvoirWithLines" id="invoiceAvoirWithLines" value="1" onclick="$(\'#credit_note_options input[type=checkbox]\').not(this).prop(\'checked\', false);" '.(GETPOSTINT('invoiceAvoirWithLines') > 0 ? 'checked' : '').' /> <label for="invoiceAvoirWithLines" class="small">'.$langs->trans('invoiceAvoirWithLines')."</label></div>";
//print '<br>';
print '<div class="marginleftlarge"><input type="checkbox" name="invoiceAvoirWithPaymentRestAmount" id="invoiceAvoirWithPaymentRestAmount" value="1" onclick="$(\'#credit_note_options input[type=checkbox]\').not(this).prop(\'checked\', false);" '.(GETPOSTINT('invoiceAvoirWithPaymentRestAmount') > 0 ? 'checked' : '').' /> <label for="invoiceAvoirWithPaymentRestAmount" class="small">'.$langs->trans('invoiceAvoirWithPaymentRestAmount')."</label></div>";
print '<div class="marginleftlargeondesktop"><input type="checkbox" name="invoiceAvoirWithLines" id="invoiceAvoirWithLines" value="1" onclick="$(\'#credit_note_options input[type=checkbox]\').not(this).prop(\'checked\', false);" '.(GETPOSTINT('invoiceAvoirWithLines') > 0 ? 'checked' : '').' /> <label for="invoiceAvoirWithLines" class="small">'.$langs->trans('invoiceAvoirWithLines')."</label></div>";
print '<div class="marginleftlargeondesktop"><input type="checkbox" name="invoiceAvoirWithPaymentRestAmount" id="invoiceAvoirWithPaymentRestAmount" value="1" onclick="$(\'#credit_note_options input[type=checkbox]\').not(this).prop(\'checked\', false);" '.(GETPOSTINT('invoiceAvoirWithPaymentRestAmount') > 0 ? 'checked' : '').' /> <label for="invoiceAvoirWithPaymentRestAmount" class="small">'.$langs->trans('invoiceAvoirWithPaymentRestAmount')."</label></div>";
// Adding a checkbox: "Automatically consume the credit note to close the corrected invoice" is better to be into
// the confirm popup when we validate the credit note
print '</div>';
print '</div></div>'."\n";

View File

@@ -2107,7 +2107,7 @@ class Form
dol_syslog(get_class($this) . "::select_remises", LOG_DEBUG);
$resql = $this->db->query($sql);
if ($resql) {
print '<select id="select_' . $htmlname . '" class="flat maxwidthonsmartphone" name="' . $htmlname . '">';
print '<select id="select_' . $htmlname . '" class="flat maxwidth200onsmartphone" name="' . $htmlname . '">';
$num = $this->db->num_rows($resql);
$qualifiedlines = $num;

View File

@@ -25,6 +25,7 @@
* \brief File of trigger for ticket module
*/
require_once DOL_DOCUMENT_ROOT.'/core/triggers/dolibarrtriggers.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
/**
@@ -164,14 +165,22 @@ class InterfaceTicketEmail extends DolibarrTriggers
$body_assignee = 'TicketAssignedEmailBody';
$see_ticket_assignee = 'SeeThisTicketIntomanagementInterface';
// We send files that were just uploaded because they were not moved to ticket document directory
//@see Ticket::copyFilesForTicket()
//@see ticket/card.php call to Ticket::copyFilesForTicket()
$formmail = new FormMail($this->db);
$formmail->trackid = ('');
$attachedfiles = $formmail->get_attached_files();
$filepaths = $attachedfiles['paths'];
$filenames = $attachedfiles['names'];
$mimetypes = $attachedfiles['mimes'];
// Send email to notification email
// Note: $object->context['disableticketemail'] is set to 1 by public interface at creation because email sending is already managed by page
// $object->context['createdfrompublicinterface'] may also be defined when creation done from public interface
if (getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO') && empty($object->context['disableticketemail'])) {
$sendto = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO');
// if ($sendto) { // already test, can't be empty
$this->composeAndSendAdminMessage($sendto, $subject_admin, $body_admin, $object, $langs);
// }
$this->composeAndSendAdminMessage($sendto, $subject_admin, $body_admin, $object, $langs, $filepaths, $mimetypes, $filenames);
}
// Send email to assignee if an assignee was set at creation
@@ -191,7 +200,7 @@ class InterfaceTicketEmail extends DolibarrTriggers
}
if (!empty($sendto)) {
$this->composeAndSendAssigneeMessage($sendto, $subject_assignee, $body_assignee, $see_ticket_assignee, $object, $langs);
$this->composeAndSendAssigneeMessage($sendto, $subject_assignee, $body_assignee, $see_ticket_assignee, $object, $langs, $filepaths, $mimetypes, $filenames);
}
if (!getDolUserString('TICKET_DISABLE_MAIL_AUTOCOPY_TO')) {
@@ -228,7 +237,7 @@ class InterfaceTicketEmail extends DolibarrTriggers
}
if ($sendto) {
$this->composeAndSendCustomerMessage($sendto, $subject_customer, $body_customer, $see_ticket_customer, $object, $langs);
$this->composeAndSendCustomerMessage($sendto, $subject_customer, $body_customer, $see_ticket_customer, $object, $langs, $filepaths, $mimetypes, $filenames);
}
}
@@ -355,17 +364,15 @@ class InterfaceTicketEmail extends DolibarrTriggers
* @param string $body email body (first line). Non-translated string.
* @param Ticket $object the ticket that the email refers to
* @param Translate $langs the translation object
* @param array<string> $filepaths File paths
* @param array<string> $mimetypes Mime types
* @param array<string> $filenames File names
* @return void
*/
private function composeAndSendAdminMessage($sendto, $base_subject, $body, Ticket $object, Translate $langs)
private function composeAndSendAdminMessage($sendto, $base_subject, $body, Ticket $object, Translate $langs, $filepaths = array(), $mimetypes = array(), $filenames = array())
{
global $conf, $mysoc;
// Init to avoid errors
$filepath = array();
$filename = array();
$mimetype = array();
$appli = $mysoc->name;
/* Send email to admin */
@@ -408,7 +415,7 @@ class InterfaceTicketEmail extends DolibarrTriggers
$conf->global->MAIN_MAIL_AUTOCOPY_TO = '';
}
include_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
$mailfile = new CMailFile($subject, $sendto, $from, $message_admin, $filepath, $mimetype, $filename, '', '', 0, -1, '', '', $trackid, '', 'ticket');
$mailfile = new CMailFile($subject, $sendto, $from, $message_admin, $filepaths, $mimetypes, $filenames, '', '', 0, -1, '', '', $trackid, '', 'ticket');
if ($mailfile->error) {
dol_syslog($mailfile->error, LOG_DEBUG);
} else {
@@ -428,17 +435,15 @@ class InterfaceTicketEmail extends DolibarrTriggers
* @param string $see_ticket string indicating the ticket public address
* @param Ticket $object the ticket that the email refers to
* @param Translate $langs the translation object
* @param array<string> $filepaths File paths
* @param array<string> $mimetypes Mime types
* @param array<string> $filenames File names
* @return void
*/
private function composeAndSendCustomerMessage($sendto, $base_subject, $body, $see_ticket, Ticket $object, Translate $langs)
private function composeAndSendCustomerMessage($sendto, $base_subject, $body, $see_ticket, Ticket $object, Translate $langs, $filepaths = array(), $mimetypes = array(), $filenames = array())
{
global $conf, $extrafields, $mysoc, $user;
// Init to avoid errors
$filepath = array();
$filename = array();
$mimetype = array();
$appli = $mysoc->name;
$subject = '['.$appli.'] '.$langs->transnoentities($base_subject);
@@ -501,7 +506,7 @@ class InterfaceTicketEmail extends DolibarrTriggers
}
include_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
$mailfile = new CMailFile($subject, $sendto, $from, $message_customer, $filepath, $mimetype, $filename, '', '', 0, -1, '', '', $trackid, '', 'ticket');
$mailfile = new CMailFile($subject, $sendto, $from, $message_customer, $filepaths, $mimetypes, $filenames, '', '', 0, -1, '', '', $trackid, '', 'ticket');
if ($mailfile->error) {
dol_syslog($mailfile->error, LOG_DEBUG);
} else {
@@ -527,17 +532,15 @@ class InterfaceTicketEmail extends DolibarrTriggers
* @param string $see_ticket string indicating the ticket public address
* @param Ticket $object the ticket that the email refers to
* @param Translate $langs the translation object
* @param array<string> $filepaths File paths
* @param array<string> $mimetypes Mime types
* @param array<string> $filenames File names
* @return void
*/
private function composeAndSendAssigneeMessage($sendto, $base_subject, $body, $see_ticket, Ticket $object, Translate $langs)
private function composeAndSendAssigneeMessage($sendto, $base_subject, $body, $see_ticket, Ticket $object, Translate $langs, $filepaths = array(), $mimetypes = array(), $filenames = array())
{
global $conf, $user, $mysoc;
// Init to avoid errors
$filepath = array();
$filename = array();
$mimetype = array();
// Send email to assigned user
$appli = $mysoc->name;
@@ -569,7 +572,7 @@ class InterfaceTicketEmail extends DolibarrTriggers
}
include_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
$mailfile = new CMailFile($subject, $sendto, $from, $message, $filepath, $mimetype, $filename, '', '', 0, -1);
$mailfile = new CMailFile($subject, $sendto, $from, $message, $filepaths, $mimetypes, $filenames, '', '', 0, -1);
if ($mailfile->error) {
setEventMessages($mailfile->error, $mailfile->errors, 'errors');
} else {

View File

@@ -142,7 +142,6 @@ $result = @include_once $conffile; // Keep @ because with some error reporting m
* @var ?string $dolibarr_main_url_root_alt
* @var ?string $dolibarr_main_document_root
* @var ?string $dolibarr_main_document_root_alt
* @var ?string $dolibarr_main_stream_to_disable
* @var ?string $dolibarr_main_instance_unique_id
* @var ?string $dolibarr_strict_mode
* @var ?string $dolibarr_main_data_root

View File

@@ -683,6 +683,7 @@ if (!GETPOST('action', 'aZ09') || preg_match('/upgrade/i', GETPOST('action', 'aZ
'MAIN_MODULE_BLOCKEDLOG' => 'noboxes',
'MAIN_MODULE_DON' => 'newboxdefonly',
'MAIN_MODULE_ECM' => 'newboxdefonly',
'MAIN_MODULE_EVENTORGANIZATION' => 'newboxdefonly',
'MAIN_MODULE_EXPENSEREPORT' => 'newboxdefonly',
'MAIN_MODULE_FACTURE' => 'newboxdefonly',
'MAIN_MODULE_FOURNISSEUR' => 'newboxdefonly',

View File

@@ -30,7 +30,7 @@ InvoiceReplacementShort=Replacement
InvoiceReplacementAsk=Replacement invoice for invoice
InvoiceReplacementDesc=<b>Replacement invoice</b> is used to completely replace an invoice with no payment already received.<br><br>Note: Only invoices with no payment on it can be replaced. If the invoice you replace is not yet closed, it will be automatically closed to 'abandoned'.
InvoiceAvoir=Credit note
InvoiceAvoirAsk=Credit note
InvoiceAvoirAsk=Credit note from
InvoiceAvoirAskCombo=Invoice to correct
InvoiceAvoirDesc=The <b>credit note</b> is a negative invoice used to correct the fact that an invoice shows an amount that differs from the amount actually paid (eg the customer paid too much by mistake, or will not pay the complete amount since some products were returned).
invoiceAvoirWithLines=Create Credit Note with lines from the origin invoice

View File

@@ -49,6 +49,7 @@ TotalTTCIfInvoiceSeeCompleteDataForDetail=Amount of event. Total including tax i
TypeOfEvent=Type of event
TotalForAction=Total for event %s
SecretKey=Secret key
ErrorPeriodMustBePastToAllowExport=Export into archives is allowed only if period is completely past
## logTypes
logBILL_DELETE=Customer invoice logically deleted

View File

@@ -62,7 +62,7 @@ require_once 'filefunc.inc.php';
* @var string $dolibarr_main_url_root
* @var string $dolibarr_main_url_root_alt
* @var string $dolibarr_main_document_root_alt
* @var string $dolibarr_main_stream_to_disable
* @var string|string[] $dolibarr_main_stream_to_disable
*/
'
@phan-var-force ?string $dolibarr_main_db_prefix

View File

@@ -0,0 +1,40 @@
<!-- file list-additional-filters.tpl.php -->
<?php
/* Copyright (C) 2025 Open-Dsi <support@open-dsi.fr>
*/
// Protection to avoid direct call of template
if (empty($context) || !is_object($context)) {
print "Error, template page can't be called as URL";
exit(1);
}
'@phan-var-force Context $context';
'@phan-var-force AbstractListController $this';
/**
* @var Conf $conf
* @var HookManager $hookmanager
* @var Translate $langs
* @var Context $context
* @var AbstractListController $this
* @var FormListWebPortal $formList
*/
$formList = &$this->formList;
// Filters
$moreforfilter = '';
$parameters = array();
$reshook = $hookmanager->executeHooks('printFieldPreListTitle', $parameters, $context);
if (empty($reshook)) {
$moreforfilter .= $hookmanager->resPrint;
} else {
$moreforfilter = $hookmanager->resPrint;
}
?>
<?php if (!empty($moreforfilter)) { ?>
<div id="webportal-<?php print dolPrintHTMLForAttribute($formList->object->element) ?>-additional-filters" class="centpercent">
<?php print $moreforfilter ?>
</div>
<?php } ?>

View File

@@ -0,0 +1,65 @@
<!-- file list-filters.tpl.php -->
<?php
/* Copyright (C) 2025 Open-Dsi <support@open-dsi.fr>
*/
// Protection to avoid direct call of template
if (empty($context) || !is_object($context)) {
print "Error, template page can't be called as URL";
exit(1);
}
'@phan-var-force Context $context';
'@phan-var-force AbstractListController $this';
/**
* @var Conf $conf
* @var HookManager $hookmanager
* @var Translate $langs
* @var Context $context
* @var AbstractListController $this
* @var FormListWebPortal $formList
*/
$formList = &$this->formList;
?>
<tr role="search-row">
<td data-col="row-checkbox">
<button class="btn-filter-icon btn-search-filters-icon" type="submit" name="button_search_x" value="x" aria-label="<?php print dolPrintHTMLForAttribute((string) $langs->trans('Search')) ?>"></button>
<button class="btn-filter-icon btn-remove-search-filters-icon" type="submit" name="button_removefilter_x" value="x" aria-label="<?php print dolPrintHTMLForAttribute((string) $langs->trans('RemoveSearchFilters')) ?>"></button>
</td>
<?php foreach ($formList->object->fields as $key => $val) {
$alias = $val['alias'] ?? 't.';
if (array_key_exists($alias . $key, $formList->arrayfields) && !empty($formList->arrayfields[$alias . $key]['checked'])) {
$cssforfield = $formList->getClasseCssList($key, $val);
if ($key == 'status') $cssforfield .= ' parentonrightofpage';
?>
<td <?php print empty($cssforfield) ? '' : 'class="' . dolPrintHTMLForAttribute($cssforfield) . '" ' ?>data-label="<?php print dolPrintHTMLForAttribute((string) $formList->arrayfields[$alias . $key]['label']) ?>" data-col="<?php print dolPrintHTMLForAttribute((string) $key) ?>">
<?php print $formList->printSearchInput($key, $val) ?>
</td>
<?php }
}
// Fields from hook
$parameters = array();
$reshook = $hookmanager->executeHooks('printFieldListOption', $parameters, $context);
print $hookmanager->resPrint;
// Remain to pay
if (array_key_exists('remain_to_pay', $formList->arrayfields) && !empty($formList->arrayfields['remain_to_pay']['checked'])) { ?>
<td data-label="<?php print dolPrintHTMLForAttribute((string) $formList->arrayfields['remain_to_pay']['label']) ?>">
</td>
<?php }
// Download link
if (array_key_exists('download_link', $formList->arrayfields) && !empty($formList->arrayfields['download_link']['checked'])) { ?>
<td data-label="<?php print dolPrintHTMLForAttribute((string) $formList->arrayfields['download_link']['label']) ?>">
</td>
<?php }
// Signature link
if (array_key_exists('signature_link', $formList->arrayfields) && !empty($formList->arrayfields['signature_link']['checked'])) { ?>
<td data-label="<?php print dolPrintHTMLForAttribute((string) $formList->arrayfields['signature_link']['label']) ?>">
</td>
<?php } ?>
</tr>

View File

@@ -0,0 +1,25 @@
<!-- file list-footer.tpl.php -->
<?php
/* Copyright (C) 2025 Open-Dsi <support@open-dsi.fr>
*/
// Protection to avoid direct call of template
if (empty($context) || !is_object($context)) {
print "Error, template page can't be called as URL";
exit(1);
}
'@phan-var-force Context $context';
'@phan-var-force AbstractListController $this';
/**
* @var Conf $conf
* @var HookManager $hookmanager
* @var Translate $langs
* @var Context $context
* @var AbstractListController $this
* @var FormListWebPortal $formList
*/
$formList = &$this->formList;
$parameters = array();
$reshook = $hookmanager->executeHooks('printFieldListFooter', $parameters, $context); // Note that $action and $object may have been modified by hook
print $hookmanager->resPrint;

View File

@@ -0,0 +1,113 @@
<!-- file list-lines.tpl.php -->
<?php
/* Copyright (C) 2025 Open-Dsi <support@open-dsi.fr>
*/
// Protection to avoid direct call of template
if (empty($context) || !is_object($context)) {
print "Error, template page can't be called as URL";
exit(1);
}
'@phan-var-force Context $context';
'@phan-var-force AbstractListController $this';
/**
* @var Conf $conf
* @var HookManager $hookmanager
* @var Translate $langs
* @var Context $context
* @var AbstractListController $this
* @var FormListWebPortal $formList
*/
$formList = &$this->formList;
if (!empty($formList->records)) {
$totalarray = [
'nbfield' => 0,
'totalizable' => [],
];
foreach ($formList->records as $i => $record) {
// Store properties in $object
$formList->setVarsFromFetchObj($record); ?>
<tr data-rowid="<?php print dolPrintHTMLForAttribute((string) $formList->object->id) ?>">
<td></td>
<?php $formList->setTotalValue('', [], $record, $i, $totalarray) ?>
<?php foreach ($formList->object->fields as $key => $val) {
$alias = $val['alias'] ?? 't.';
if (array_key_exists($alias . $key, $formList->arrayfields) && !empty($formList->arrayfields[$alias . $key]['checked'])) {
$cssforfield = $formList->getClasseCssList($key, $val, true);
if (preg_match('/tdoverflow/', $cssforfield)) $cssforfield .= ' classfortooltip';
$title = '';
if (preg_match('/tdoverflow/', $cssforfield) && !is_numeric($formList->object->$key)) {
$title = ' title="' . dolPrintHTMLForAttribute((string) $formList->object->$key) . '"';
}
?>
<td <?php print (empty($cssforfield) ? '' : 'class="' . dolPrintHTMLForAttribute($cssforfield) . '" '); print $title ?>data-label="<?php print dolPrintHTMLForAttribute((string) $formList->arrayfields[$alias . $key]['label']) ?>" data-col="<?php print dolPrintHTMLForAttribute((string) $key) ?>">
<?php print $formList->printValue($key, $val, $record, $i, $totalarray);
$formList->setTotalValue($key, $val, $record, $i, $totalarray) ?>
</td>
<?php }
}
// Fields from hook
$parameters = array('record' => $record, 'i' => $i, 'totalarray' => &$totalarray);
$reshook = $hookmanager->executeHooks('printFieldListValue', $parameters, $context);
print $hookmanager->resPrint;
// Remain to pay
if (array_key_exists('remain_to_pay', $formList->arrayfields) && !empty($formList->arrayfields['remain_to_pay']['checked'])) { ?>
<td class="nowraponall" data-label="<?php print dolPrintHTMLForAttribute((string) $formList->arrayfields['remain_to_pay']['label']) ?>" data-col="remain_to_pay">
<?php print $formList->printValue('remain_to_pay', [], $record, $i, $totalarray);
$formList->setTotalValue('remain_to_pay', [], $record, $i, $totalarray) ?>
</td>
<?php }
// Download link
if (array_key_exists('download_link', $formList->arrayfields) && !empty($formList->arrayfields['download_link']['checked'])) { ?>
<td data-label="<?php print dolPrintHTMLForAttribute((string) $formList->arrayfields['download_link']['label']) ?>" data-col="download_link">
<?php print $formList->printValue('download_link', [], $record, $i, $totalarray);
$formList->setTotalValue('download_link', [], $record, $i, $totalarray) ?>
</td>
<?php }
// Signature link
if (array_key_exists('signature_link', $formList->arrayfields) && !empty($formList->arrayfields['signature_link']['checked'])) { ?>
<td data-label="<?php print dolPrintHTMLForAttribute((string) $formList->arrayfields['signature_link']['label']) ?>" data-col="signature_link">
<?php print $formList->printValue('signature_link', [], $record, $i, $totalarray);
$formList->setTotalValue('signature_link', [], $record, $i, $totalarray) ?>
</td>
<?php } ?>
</tr>
<?php }
// Move fields of totalizable into the common array pos and val
if (!empty($totalarray['totalizable']) && is_array($totalarray['totalizable'])) {
foreach ($totalarray['totalizable'] as $keytotalizable => $valtotalizable) {
$totalarray['pos'][$valtotalizable['pos']] = $keytotalizable;
$totalarray['val'][$keytotalizable] = isset($valtotalizable['total']) ? $valtotalizable['total'] : 0;
}
}
// Show total line
if (isset($totalarray['pos'])) { ?>
<tr>
<?php $i = 0;
while ($i < $totalarray['nbfield']) {
$i++;
if (!empty($totalarray['pos'][$i])) { ?>
<td class="nowraponall essai">
<?php print price(!empty($totalarray['val'][$totalarray['pos'][$i]]) ? $totalarray['val'][$totalarray['pos'][$i]] : 0) ?>
</td>
<?php } else {
if ($i == 1) { ?>
<td><?php print $langs->trans("Total") ?></td>
<?php } else { ?>
<td></td>
<?php }
}
} ?>
</tr>
<?php }
} else { // If no record found ?>
<tr><td colspan="<?php print $formList->nbColumn ?>"><span class="opacitymedium"><?php print $langs->trans("NoRecordFound") ?></span></td></tr>
<?php } ?>

View File

@@ -0,0 +1,74 @@
<!-- file list-nav.tpl.php -->
<?php
/* Copyright (C) 2025 Open-Dsi <support@open-dsi.fr>
*/
// Protection to avoid direct call of template
if (empty($context) || !is_object($context)) {
print "Error, template page can't be called as URL";
exit(1);
}
'@phan-var-force Context $context';
'@phan-var-force AbstractListController $this';
/**
* @var Conf $conf
* @var HookManager $hookmanager
* @var Translate $langs
* @var Context $context
* @var AbstractListController $this
* @var FormListWebPortal $formList
*/
$formList = &$this->formList;
// Get nb pages
$nbPages = 0;
if ($formList->limit > 0) {
$nbPages = ceil($formList->nbtotalofrecords / $formList->limit);
}
if ($nbPages <= 0) {
$nbPages = 1;
}
$maxPaginationItem = min($nbPages, 5);
$minPageNum = max(1, $formList->page - 3);
$maxPageNum = min($nbPages, $formList->page + 3);
$params = $formList->params . '&amp;sortfield=' . $formList->sortfield . '&amp;sortorder=' . $formList->sortorder;
$params = preg_replace('/^(&|&amp;)/i', '', $params); // remove first & or &amp;
$url = $context->getControllerUrl($context->controller);
$url .= (preg_match('/\?/', $url) ? '&amp;' : '?') . $params;
?>
<input type="hidden" name="page" value="<?php print dolPrintHTMLForAttribute((string) $formList->page) ?>">
<nav id="webportal-<?php print dolPrintHTMLForAttribute($formList->object->element) ?>-pagination">
<ul>
<li><strong><?php print $langs->trans($formList->titleKey) ?></strong> (<?php print $formList->nbtotalofrecords ?>)</li>
</ul>
<?php if ($nbPages > 1) { ?>
<ul class="pages-nav-list">
<?php if ($formList->page > 1) { ?>
<li><a class="pages-nav-list__icon --prev" aria-label="<?php print dolPrintHTMLForAttribute((string) $langs->trans('AriaPrevPage')) ?>" href="<?php print $url . '&amp;page=' . ($formList->page - 1) ?>"<?php // print ($formList->page <= 1 ? ' disabled' : '') ?>></a></li>
<?php } ?>
<?php if ($minPageNum > 1) { ?>
<li><a class="pages-nav-list__link <?php print ($formList->page == 1 ? '--active' : '') ?>" aria-label="<?php print dolPrintHTMLForAttribute((string) $langs->trans('AriaPageX', 1)) ?>" href="<?php print $url . '&amp;page=1' ?>">1</a></li>
<li>&hellip;</li>
<?php } ?>
<?php for ($p = $minPageNum; $p <= $maxPageNum; $p++) { ?>
<li><a class="pages-nav-list__link <?php print ($formList->page === $p ? '--active' : '') ?>" aria-label="<?php print dolPrintHTMLForAttribute((string) $langs->trans('AriaPageX', $p)) ?>" href="<?php print $url . '&amp;page=' . $p ?>"><?php print $p ?></a></li>
<?php } ?>
<?php if ($maxPaginationItem < $nbPages) { ?>
<li>&hellip;</li>
<li><a class="pages-nav-list__link <?php print ($formList->page == $nbPages ? '--active' : '') ?>" aria-label="<?php print dolPrintHTMLForAttribute((string) $langs->trans('AriaPageX', $nbPages)) ?>" href="<?php print $url . '&amp;page=' . $nbPages ?>"><?php print $nbPages ?></a></li>
<?php } ?>
<?php if ($formList->page < $nbPages) { ?>
<li><a class="pages-nav-list__icon --next" aria-label="<?php print dolPrintHTMLForAttribute((string) $langs->trans('AriaNextPage')) ?>" href="<?php print $url . '&amp;page=' . ($formList->page + 1) ?>"<?php // print ($formList->page >= $nbPages ? ' disabled' : '') ?>></a></li>
<?php } ?>
</ul>
<?php } ?>
</nav>

View File

@@ -0,0 +1,75 @@
<!-- file list-titles.tpl.php -->
<?php
/* Copyright (C) 2025 Open-Dsi <support@open-dsi.fr>
*/
// Protection to avoid direct call of template
if (empty($context) || !is_object($context)) {
print "Error, template page can't be called as URL";
exit(1);
}
'@phan-var-force Context $context';
'@phan-var-force AbstractListController $this';
/**
* @var Conf $conf
* @var HookManager $hookmanager
* @var Translate $langs
* @var Context $context
* @var AbstractListController $this
* @var FormListWebPortal $formList
*/
$formList = &$this->formList;
$url = $context->getControllerUrl($context->controller);
$url .= (preg_match('/\?/', $url) ? '&amp;' : '?') . preg_replace('/^(&|&amp;)/i', '', $formList->params/* . "&amp;page=" . urlencode($formList->page)*/);
// Make array[sort field => sort order] for this list
$sortList = array_combine(explode(",", $formList->sortfield), explode(",", $formList->sortorder));
$formList->nbColumn = 0;
?>
<!-- Fields title label -->
<tr>
<th data-col="row-checkbox"></th>
<?php $formList->nbColumn++ ?>
<?php foreach ($formList->object->fields as $key => $val) {
$alias = $val['alias'] ?? 't.';
if (array_key_exists($alias . $key, $formList->arrayfields) && !empty($formList->arrayfields[$alias . $key]['checked'])) {
$order = array_key_exists($alias . $key, $sortList) ? strtolower(trim($sortList[$alias . $key])) : '';
$link_url = $url . '&amp;sortfield=' . urlencode($alias . $key) . '&amp;sortorder=' . ($order == 'desc' ? 'asc' : 'desc');
$cssforfield = $formList->getClasseCssList($key, $val);
$cssforfield = preg_replace('/small\s*/', '', $cssforfield);
?>
<th <?php print empty($cssforfield) ? '' : 'class="' . dolPrintHTMLForAttribute($cssforfield) . '" ' ?>data-col="<?php print dolPrintHTMLForAttribute((string) $key) ?>" scope="col"<?php print (!empty($order) ? ' table-order="' . dolPrintHTMLForAttribute($order) . '"' : '') ?>>
<a href="<?php print dolPrintHTMLForAttribute($link_url) ?>"><?php print $langs->trans((string) $formList->arrayfields[$alias . $key]['label']) ?></a>
</th>
<?php $formList->nbColumn++;
}
}
// Hook fields
$parameters = array('sortList' => $sortList);
$reshook = $hookmanager->executeHooks('printFieldListTitle', $parameters, $context);
print $hookmanager->resPrint;
// Remain to pay
if (array_key_exists('remain_to_pay', $formList->arrayfields) && !empty($formList->arrayfields['remain_to_pay']['checked'])) { ?>
<th scope="col"><?php print $langs->trans((string) $formList->arrayfields['remain_to_pay']['label']) ?></th>
<?php $formList->nbColumn++;
}
// Download link
if (array_key_exists('download_link', $formList->arrayfields) && !empty($formList->arrayfields['download_link']['checked'])) { ?>
<th scope="col"><?php print $langs->trans((string) $formList->arrayfields['download_link']['label']) ?></th>
<?php $formList->nbColumn++;
}
// Signature link
if (array_key_exists('signature_link', $formList->arrayfields) && !empty($formList->arrayfields['signature_link']['checked'])) { ?>
<th scope="col"><?php print $langs->trans((string) $formList->arrayfields['signature_link']['label']) ?></th>
<?php $formList->nbColumn++;
} ?>
</tr>

View File

@@ -0,0 +1,52 @@
<!-- file list.tpl.php -->
<?php
/* Copyright (C) 2025 Open-Dsi <support@open-dsi.fr>
*/
// Protection to avoid direct call of template
if (empty($context) || !is_object($context)) {
print "Error, template page can't be called as URL";
exit(1);
}
'@phan-var-force Context $context';
'@phan-var-force AbstractListController $this';
/**
* @var Conf $conf
* @var HookManager $hookmanager
* @var Translate $langs
* @var Context $context
* @var AbstractListController $this
* @var FormListWebPortal $formList
*/
$formList = &$this->formList;
?>
<form method="POST" id="searchFormList" action="<?php print $context->getControllerUrl($context->controller, '', false) ?>">
<?php print $context->getFormToken() ?>
<input type="hidden" name="formfilteraction" id="formfilteraction" value="list">
<input type="hidden" name="action" value="list">
<input type="hidden" name="sortfield" value="<?php print dolPrintHTMLForAttribute($formList->sortfield) ?>">
<input type="hidden" name="sortorder" value="<?php print dolPrintHTMLForAttribute($formList->sortorder) ?>">
<input type="hidden" name="contextpage" value="<?php print dolPrintHTMLForAttribute($formList->contextpage) ?>">
<?php $this->loadTemplate('list-nav') ?>
<?php $this->loadTemplate('list-additional-filters') ?>
<table id="webportal-<?php print dolPrintHTMLForAttribute($formList->object->element) ?>-list" responsive="scroll" role="grid">
<thead>
<?php $this->loadTemplate('list-filters') ?>
<?php $this->loadTemplate('list-titles') ?>
</thead>
<tbody>
<?php $this->loadTemplate('list-lines') ?>
</tbody>
<tfoot>
<?php $this->loadTemplate('list-footer') ?>
</tfoot>
</table>
</form>

View File

@@ -1241,6 +1241,9 @@ td.wordbreak img, td.wordbreakimp img {
.marginleftlarge {
margin-<?php print $left; ?>: 20px !important;
}
.marginleftlargeondesktop {
margin-<?php print $left; ?>: 20px;
}
.paddinglarge {
padding: 6px !important;
}
@@ -2280,6 +2283,10 @@ datalist {
font-size: <?php print is_numeric($fontsize) ? ((int) $fontsize + 3).'px' : $fontsize; ?> !important;
}
.marginleftlargeondesktop {
margin-<?php print $left; ?>: 0;
}
div#login_left, div#login_right {
min-width: 150px !important;
max-width: 240px !important;

View File

@@ -1397,6 +1397,9 @@ td.wordbreak img, td.wordbreakimp img {
.marginleftlarge {
margin-<?php print $left; ?>: 20px !important;
}
.marginleftlargeondesktop {
margin-<?php print $left; ?>: 20px;
}
.paddinglarge {
padding: 6px !important;
}
@@ -1587,8 +1590,12 @@ span.fa.fa-plus-circle.paddingleft {
margin-left: 1px;
}
.listofinvoicetype {
height: 28px;
min-height: 1.8em;
vertical-align: middle;
padding-top: 7px;
padding-bottom: 1px;
display: flex;
align-items: center;
}
.divsocialnetwork:not(:last-child) {
padding-<?php print $right; ?>: 20px;
@@ -2395,6 +2402,10 @@ select.widthcentpercentminusxx, span.widthcentpercentminusxx:not(.select2-select
font-size: <?php print is_numeric($fontsize) ? ($fontsize).'px' : $fontsize; ?> !important;
}
.marginleftlargeondesktop {
margin-<?php print $left; ?>: 0;
}
.login_vertical_align {
padding-left: 0;
}
@@ -2436,7 +2447,7 @@ select.widthcentpercentminusxx, span.widthcentpercentminusxx:not(.select2-select
height: 40px !important;
}
div.tabBar .listofinvoicetype table tr, div.tabBar .listofinvoicetype table tr td {
height: 28px !important;
height: 2.2em !important;
}
div.tabs div.tab a.tab {

View File

@@ -56,6 +56,11 @@ class Controller
*/
public $tplPath;
/**
* @var FormListWebPortal Form for list
*/
public $formList;
/**
* Constructor

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,82 @@
<?php
/* Copyright (C) 2023-2024 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2023-2024 Lionel Vessiller <lvessiller@easya.solutions>
*
* 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 <https://www.gnu.org/licenses/>.
*/
require_once __DIR__ . '/../class/controller.class.php';
/**
* \file htdocs/webportal/controllers/abstractlist.controller.class.php
* \ingroup webportal
* \brief This file is an abstract controller with shared logic to display a list
*/
/**
* Class for AbstractListController
*/
abstract class AbstractListController extends Controller
{
/**
* @var FormListWebPortal Form for list
*/
public $formList;
/**
* Set array fields
*
* @return void
*/
public function listSetArrayFields()
{
}
/**
* Set search values
*
* @param bool $clear Clear search values
* @return void
*/
public function listSetSearchValues($clear = false)
{
}
/**
* Called before print value for list
*
* @param string $field_key Field key
* @param array<string,mixed> $field_spec Field specification
* @param stdClass $record Contain data of object from database
* @return string HTML input
*/
public function listPrintValueBefore($field_key, $field_spec, &$record)
{
return '';
}
/**
* Called after print value for list
*
* @param string $field_key Field key
* @param array<string,mixed> $field_spec Field specification
* @param stdClass $record Contain data of object from database
* @param string $out Current HTML input
* @return string HTML input
*/
public function listPrintValueAfter($field_key, $field_spec, &$record, $out)
{
return $out;
}
}

View File

@@ -22,17 +22,14 @@
* \brief This file is a controller for invoice list
*/
require_once DOL_DOCUMENT_ROOT . '/webportal/class/html.formlistwebportal.class.php';
require_once DOL_DOCUMENT_ROOT . '/webportal/controllers/abstractlist.controller.class.php';
/**
* Class for InvoiceListController
*/
class InvoiceListController extends Controller
class InvoiceListController extends AbstractListController
{
/**
* @var FormListWebPortal Form for list
*/
public $formList;
/**
* Check current access to controller
*
@@ -60,8 +57,6 @@ class InvoiceListController extends Controller
return -1;
}
dol_include_once('/webportal/class/html.formlistwebportal.class.php');
// Load translation files required by the page
$langs->loadLangs(array('bills', 'companies', 'products', 'categories'));
@@ -70,16 +65,25 @@ class InvoiceListController extends Controller
$context->menu_active[] = 'invoice_list';
// set form list
$formListWebPortal = new FormListWebPortal($this->db);
$formListWebPortal->init('invoice');
$this->formList = new FormListWebPortal($this->db);
$this->formList->init($this, 'invoice');
// hook for action
$hookRes = $this->hookDoAction();
if (empty($hookRes)) {
$formListWebPortal->doActions();
$this->formList->doActions();
}
$this->formList = $formListWebPortal;
$this->formList->setSqlRequest();
// filter on logged third-party
$this->formList->sql_body .= " AND t.fk_soc = " . ((int) $context->logged_thirdparty->id);
// discard record with status draft
$this->formList->sql_body .= " AND t.fk_statut <> 0";
$this->formList->loadRecords();
$this->formList->setParams();
$this->formList->setColumnsVisibility();
return 1;
}
@@ -105,7 +109,7 @@ class InvoiceListController extends Controller
if (empty($hookRes)) {
print '<main class="container">';
//print '<figure>';
print $this->formList->elementList($context);
$this->loadTemplate('list');
//print '</figure>';
print '</main>';
}

View File

@@ -24,18 +24,13 @@
*/
require_once DOL_DOCUMENT_ROOT . '/webportal/class/html.formlistwebportal.class.php';
require_once DOL_DOCUMENT_ROOT . '/webportal/controllers/abstractlist.controller.class.php';
/**
* Class for OrderListController
*/
class OrderListController extends Controller
class OrderListController extends AbstractListController
{
/**
* @var FormListWebPortal Form for list
*/
public $formList;
/**
* Check current access to controller
*
@@ -71,16 +66,25 @@ class OrderListController extends Controller
$context->menu_active[] = 'order_list';
// set form list
$formListWebPortal = new FormListWebPortal($this->db);
$formListWebPortal->init('order');
$this->formList = new FormListWebPortal($this->db);
$this->formList->init($this, 'order');
// hook for action
$hookRes = $this->hookDoAction();
if (empty($hookRes)) {
$formListWebPortal->doActions();
$this->formList->doActions();
}
$this->formList = $formListWebPortal;
$this->formList->setSqlRequest();
// filter on logged third-party
$this->formList->sql_body .= " AND t.fk_soc = " . ((int) $context->logged_thirdparty->id);
// discard record with status draft
$this->formList->sql_body .= " AND t.fk_statut <> 0";
$this->formList->loadRecords();
$this->formList->setParams();
$this->formList->setColumnsVisibility();
return 1;
}
@@ -106,7 +110,7 @@ class OrderListController extends Controller
if (empty($hookRes)) {
print '<main class="container">';
//print '<figure>';
print $this->formList->elementList($context);
$this->loadTemplate('list');
//print '</figure>';
print '</main>';
}

View File

@@ -24,18 +24,13 @@
*/
require_once DOL_DOCUMENT_ROOT . '/webportal/class/html.formlistwebportal.class.php';
require_once DOL_DOCUMENT_ROOT . '/webportal/controllers/abstractlist.controller.class.php';
/**
* Class for PropalListController
*/
class PropalListController extends Controller
class PropalListController extends AbstractListController
{
/**
* @var FormListWebPortal Form for list
*/
public $formList;
/**
* Check current access to controller
*
@@ -74,16 +69,25 @@ class PropalListController extends Controller
$context->menu_active[] = 'propal_list';
// set form list
$formListWebPortal = new FormListWebPortal($this->db);
$formListWebPortal->init('propal');
$this->formList = new FormListWebPortal($this->db);
$this->formList->init($this, 'propal');
// hook for action
$hookRes = $this->hookDoAction();
if (empty($hookRes)) {
$formListWebPortal->doActions();
$this->formList->doActions();
}
$this->formList = $formListWebPortal;
$this->formList->setSqlRequest();
// filter on logged third-party
$this->formList->sql_body .= " AND t.fk_soc = " . ((int) $context->logged_thirdparty->id);
// discard record with status draft
$this->formList->sql_body .= " AND t.fk_statut <> 0";
$this->formList->loadRecords();
$this->formList->setParams();
$this->formList->setColumnsVisibility();
return 1;
}
@@ -109,7 +113,7 @@ class PropalListController extends Controller
if (empty($hookRes)) {
print '<main class="container">';
//print '<figure>';
print $this->formList->elementList($context);
$this->loadTemplate('list');
//print '</figure>';
print '</main>';
}