Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
f369a46
NEW massaction Clone eventattendees to event
Apr 28, 2026
c1bf0f3
cloning now works, missing notrigger question and reporting
Apr 28, 2026
1338184
Add cascading notrigger support when cloning eventattendee
Apr 29, 2026
a8ed728
Add verbose reporting and default notrigger when mass cloning eventat…
Apr 29, 2026
db33c07
add change reporting and use notrigger choice
Apr 29, 2026
e8fce56
Create object link when cloning eventattendees
Apr 29, 2026
233d485
make it user selectable during mass action if they want linked objects
Apr 29, 2026
e99201b
new function to get relationtype from objectlinks
Apr 29, 2026
810ff48
Print clone and source respectively for cloned eventattendee
Apr 29, 2026
0cdab61
pre-commit hook fixes
Apr 29, 2026
eb85872
phan and stan fixes
Apr 29, 2026
8fe8e38
phan fixes
Apr 29, 2026
4d7b25b
more phan fixes
Apr 29, 2026
0cbe415
phan fixes attendeeclone
Apr 29, 2026
e699c6e
improvements learned during developing the single clone eventattendee
May 2, 2026
23ffcd2
project is no longer a separate change
May 2, 2026
99debe2
NEW cloning attendee from attendee card with new parameters
Apr 30, 2026
c010c1e
cloning now creates with correct project, and status changes also run…
Apr 30, 2026
731664c
pre-commit fixes
Apr 30, 2026
dd59e0e
PHP-stan fixes
Apr 30, 2026
77eac75
PHAN fixes
Apr 30, 2026
f2a38d5
adjust indent
Apr 30, 2026
f0a4463
phan/phpstan fix
Apr 30, 2026
aaa0af7
more phan fix
Apr 30, 2026
dc94f3b
change to int to make phan happy
Apr 30, 2026
2a13ee9
removing keys with empty values
Apr 30, 2026
357bdc2
swithcing to numbers even though it does not look as good
Apr 30, 2026
84ec25b
suppressing this phan issue
Apr 30, 2026
c24969d
reorder phan supress
Apr 30, 2026
8096bd4
specify variable definition
Apr 30, 2026
a999739
writting phan suppress better
Apr 30, 2026
04440a7
Change key -1 to 655360 because using -1 would show the value text ve…
Apr 30, 2026
1b68c5a
setStatusCommon should only auto find the trigger by coder choice
May 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 43 additions & 5 deletions htdocs/core/class/commonobject.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -1443,16 +1443,17 @@ public function add_contact($fk_socpeople, $type_contact, $source = 'external',
/**
* Copy contact from one element to current
*
* @param CommonObject $objFrom Source element
* @param 'internal'|'external' $source Nature of contact ('internal' or 'external')
* @param CommonObject $objFrom Source element
* @param 'internal'|'external' $source Nature of contact ('internal' or 'external')
* @param int<0,1> $notrigger 0=launch triggers after, 1=disable triggers
* @return int >0 if OK, <0 if KO
*/
public function copy_linked_contact($objFrom, $source = 'internal')
public function copy_linked_contact($objFrom, $source = 'internal', $notrigger = 0)
{
// phpcs:enable
$contacts = $objFrom->liste_contact(-1, $source);
foreach ($contacts as $contact) {
if ($this->add_contact($contact['id'], $contact['fk_c_type_contact'], $contact['source']) < 0) {
if ($this->add_contact($contact['id'], $contact['fk_c_type_contact'], $contact['source'], $notrigger) < 0) {
return -1;
}
}
Expand Down Expand Up @@ -5189,6 +5190,39 @@ public function getCanvas($id = 0, $ref = '')
}
}

/**
* fetch object link Relationtype By Values, not id
*
* @param int $fk_source source id of object we link from
* @param string $sourcetype type of the source object
* @param int $fk_target target id of object we link to
* @param string $targettype type of the target object
* @return string|null Return integer <0 if KO, >0 if OK
*/
public function getRelationtypeByValues($fk_source, $sourcetype, $fk_target, $targettype)
{
$sql = "SELECT relationtype FROM";
$sql .= " ".MAIN_DB_PREFIX.'element_element';
$sql .= " WHERE fk_source=".((int) $fk_source);
$sql .= " AND sourcetype='".$this->db->escape($sourcetype)."'";
$sql .= " AND fk_target=".((int) $fk_target);
$sql .= " AND targettype='".$this->db->escape($targettype)."'";

dol_syslog(__METHOD__, LOG_DEBUG);
$result = $this->db->query($sql);
if ($result) {
$obj = $this->db->fetch_object($result);
if ($obj) {
return $obj->relationtype;
} else {
$this->error = $this->db->error();
return null;
}
} else {
$this->error = $this->db->error();
return null;
}
}

/**
* Get special code of a line
Expand Down Expand Up @@ -11556,7 +11590,7 @@ public function deleteLineCommon(User $user, $idline, $notrigger = 0)
* @param User $user Object user that modify
* @param int $status New status to set (often a constant like self::STATUS_XXX)
* @param int $notrigger 1=Does not execute triggers, 0=Execute triggers
* @param string $triggercode Trigger code to use
* @param string $triggercode Trigger code to use, 'auto' will make it try to find the right triggercode to use based on the status to change to.
* @return int Return integer <0 if KO, >0 if OK
* @see setStatut()
*/
Expand All @@ -11566,6 +11600,10 @@ public function setStatusCommon($user, $status, $notrigger = 0, $triggercode = '

$this->db->begin();

if ($triggercode == 'auto' && isset($this->list_possible_triggercode)) {
$triggercode = isset($this->list_possible_triggercode[$status]) ? $this->list_possible_triggercode[$status] : '';
dol_syslog(get_class($this).' change to status='.$status.' uses triggercode='.$triggercode, LOG_DEBUG);
}
$statusfield = 'status';
if (in_array($this->element, array('don', 'donation', 'shipping', 'project_task'))) {
$statusfield = 'fk_statut';
Expand Down
1 change: 1 addition & 0 deletions htdocs/core/class/html.form.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -10655,6 +10655,7 @@ public function showLinkedObjectBlock($object, $morehtmlright = '', $compatibleI

$nboftypesoutput = 0;

print '<!-- foreach linkedObjects -->';
foreach ($object->linkedObjects as $objecttype => $objects) {
$tplpath = $element = $subelement = $objecttype;

Expand Down
87 changes: 87 additions & 0 deletions htdocs/core/class/html.formprojet.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -934,6 +934,93 @@ public function selectInvoiceAndLine($selectedInvoiceId = 0, $selectedLineId = 0
return $out;
}

/**
* Output a form with Multi Selection of event organisation 4 cloning
*
* @param string $page Page - if empty no form tags are printed, else they are
* @param string $htmlname Name of HTML field
* @param string $title Text above the multi select form., default is '' and not shown, could be a <h4>...</h4>
* @param int[] $status_list List of event organisations
* @param int[] $toplist List of event organisations
* @param string $toptext Text in top/main optgroup (optional) - default is '' and not shown
* @param int $size How high the table should be in lines, default is 8
* @param string $morecss More css
* @param int $nooutput No print output. Return it only.
*
* @return string HTML
*/
public function formMultiSelectEventOrg4Clone($page, $htmlname, $title = '', $status_list = array(), $toplist = array(), $toptext = '', $size = 8, $morecss = '', $nooutput = 0)
{
dol_syslog(__METHOD__.'::', LOG_DEBUG);
global $langs;
$langs->load("mails");

$out = '';
if (!empty($page)) {
$out .= '<form action="'.$page.'">';
}
if (!empty($title)) {
$out .= $title;
}

$out .= '<div id="select_'.$htmlname.'">';
$out .= '<select class="select" name="select_'.$htmlname.'[]" id="select_'.$htmlname.'" multiple size="'.$size.'" style="'.$morecss.'">';

foreach ($toplist as $key => $value) {
$project_id = $value["rowid"];
$project_title = $value["title"];
$out .= ' <option class="option" value="'.$project_id.'">'.$project_title.'</option>';
}

$out .= '</select>';
$out .= '</div>';

$out .= '<br>';
$out .= '<input type="checkbox" id="verbosereporting" name="verbosereporting" value="1"><label for="verbosereporting">'.$langs->trans("Show").' '.$langs->trans("ListOf", $langs->trans("CloneOf", $langs->trans("Attendee"))).'</label><br>';
$out .= '<div id="oldobject_status">';
$out .= '<label for="oldobject_status"><h4>'.$langs->trans("Source").' &mdash; '.$langs->trans("SetToStatus").'</h4></label>';
$out .= '<select class="select" name="oldobject_status" id="oldobject_status" size="6">';
$out .= ' <option class="option" value="-1" selected>'.$langs->trans("IsBefore").'</option>';
$out .= ' <option class="option" value="" disabled>&mdash;&mdash;&mdash;</option>';
foreach ($status_list as $key => $value) {
$out .= ' <option class="option" value="'.$key.'">'.$value.'</option>';
}
$out .= '</select>';
$out .= '</div><br>';

$out .= '<div id="newobject_status">';
$out .= '<label for="newobject_status"><h4>'.$langs->trans("Clone").' &mdash; '.$langs->trans("SetToStatus").'</h4></label>';
$out .= '<select class="select" name="newobject_status" id="newobject_status" size="6">';
$out .= ' <option class="option" value="-1" selected>'.$langs->trans("Copy").'</option>';
$out .= ' <option class="option" value="" disabled>&mdash;&mdash;&mdash;</option>';
foreach ($status_list as $key => $value) {
$out .= ' <option class="option" value="'.$key.'">'.$value.'</option>';
}
$out .= '</select>';
$out .= '</div>';

$actionname = $htmlname;
$out .= '<div id="massmail_selection_buttons_'.$htmlname.'"><br>';
$out .= '<input type="hidden" name="massaction" value="confirm_preclone">';
$out .= '<input type="checkbox" id="notrigger" name="notrigger" value="1" checked><label for="notrigger">'.$langs->trans("NoTrigger").' '.$langs->trans("CloneOf", $langs->trans("Attendee")).'</label><br>';
$out .= '<input type="checkbox" id="objlink" name="objlink" value="1" checked><label for="objlink">'.$langs->trans("Create").' '.$langs->trans("LinkedObject").'</label><br><br>';
$out .= '<!-- 2 buttons Add and Cancel -->';
$out .= '<input type="submit" class="butAction button-add small reposition" id="button_clone_attendee" name="button_clone_attendee" value="'.$langs->trans("Clone").'">';
$out .= '<input type="submit" class="button button-cancel reposition" id="cancel" name="cancel" value="'.$langs->trans("Cancel").'" />';
$out .= '</div><br>';

if (!empty($page)) {
$out .= '</form>';
}

if (empty($nooutput)) {
print $out;
return '';
} else {
return $out;
}
}

/**
* Output html select to select opportunity status
*
Expand Down
12 changes: 10 additions & 2 deletions htdocs/core/class/objectlink.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ public function delete($user, $notrigger = 0)
*/
public function create($user, $fk_source, $sourcetype, $fk_target, $targettype, $relationtype = null, $notrigger = 0)
{
dol_syslog(__METHOD__, LOG_DEBUG);
global $conf, $langs;
$error = 0;

Expand Down Expand Up @@ -280,8 +281,8 @@ public function create($user, $fk_source, $sourcetype, $fk_target, $targettype,
} else {
$sql .= " (fk_source, sourcetype, fk_target, targettype )";
}
$sql .= " VALUES (".((int) $this->fk_source).", '".$this->db->escape($sourcetype)."', ";
$sql .= ((int) $this->fk_target).", '".$this->db->escape($targettype)."'";
$sql .= " VALUES (".((int) $fk_source).", '".$this->db->escape($sourcetype)."', ";
$sql .= ((int) $fk_target).", '".$this->db->escape($targettype)."'";
if ($relationtype) {
$sql .= ", '".$this->db->escape($relationtype)."'";
}
Expand Down Expand Up @@ -310,12 +311,19 @@ public function create($user, $fk_source, $sourcetype, $fk_target, $targettype,
*/
private function _makeobject($objectid, $objecttype)
{
dol_syslog(__METHOD__, LOG_DEBUG);
if ($objecttype == 'adherent') {
require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php';
$newobject = new Adherent($this->db);
$result = $newobject->fetch($objectid);
return $result;
}
if ($objecttype == 'conferenceorboothattendee') {
require_once DOL_DOCUMENT_ROOT.'/eventorganization/class/conferenceorboothattendee.class.php';
$newobject = new ConferenceOrBoothAttendee($this->db);
$result = $newobject->fetch($objectid);
return $result;
}
if ($objecttype == 'commande') {
require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
$newobject = new Commande($this->db);
Expand Down
40 changes: 40 additions & 0 deletions htdocs/core/tpl/massactions_pre.tpl.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
* @var string $action
* @var string $massaction
* @var string $modelmail
* @var int $projectid
* @var string $sendto
* @var string $topicmail
* @var string $trackid
Expand All @@ -65,6 +66,7 @@
@phan-var-force int[] $toselect
@phan-var-force ?string $uploaddir
@phan-var-force int<0,1> $withmaindocfilemail
@phan-var-force int $projectid
@phan-var-force string $sendto
@phan-var-force string $massaction
@phan-var-force int[] $arrayofselected
Expand Down Expand Up @@ -374,6 +376,44 @@
print dol_get_fiche_end();
}

if ($massaction == 'preclone') {
dol_syslog('massactions_pre.tpl.php::if massaction == preclone', LOG_DEBUG);
$langs->loadLangs(array("eventorganization", "admin", "projects"));
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php';
$formprojet = new FormProjets($db);
require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
$projectstatic = new Project($db);
if (!isset($projectid)) {
$projectid = 0;
}
if (isset($projectstatic->date_start_event)) {
$fromdate = $projectstatic->date_start_event;
} else {
$fpresult = $projectstatic->fetch($projectid);
if ($fpresult) {
$fromdate = $projectstatic->date_start_event;
} else {
setEventMessages($langs->trans("ErrorRefNotFound", $langs->trans("Project")), null, 'errors');
dol_syslog('massaction_pre.tpl.php::failed fetching project='.$projectid, LOG_ERR);
$fromdate = 0;
}
}

$project_list = $projectstatic->fetchEventOrgIds($user, 1, $fromdate, 1);
$status_list = array();
foreach ($objecttmp->list_possible_status as $statusid) {
$status_list[$statusid] = $objecttmp->LibStatut($statusid, 1);
}
$htmlname = 'eventorg';
$page = '';
$size = (int) round(3 + log(count($project_list)));
$toptext = $langs->trans("EventOrganization").' &mdash; '.$langs->trans("ExtrafieldCheckBoxFromList");
$title = '<h4><label for="eventOrg_selection_choices_'.$htmlname.'">'.$toptext.':</label></h4>';
$formprojet->formMultiSelectEventOrg4Clone($page, $htmlname, $title, $status_list, $project_list, $toptext, $size, 'width: 100%;');

print '<br>';
}

if ($massaction == 'edit_extrafields') {
require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
$elementtype = $objecttmp->element;
Expand Down
57 changes: 51 additions & 6 deletions htdocs/eventorganization/class/conferenceorboothattendee.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,16 @@ class ConferenceOrBoothAttendee extends CommonObject
const STATUS_USED = 5; // was present, presence confirmed, no more entrances can be done using this ticket
const STATUS_CANCELED = 9;

/**
* @var array<int, int> list of possible statuses for this object
*/
public $list_possible_status = [self::STATUS_DRAFT, self::STATUS_VALIDATED, self::STATUS_USED, self::STATUS_CANCELED];

/**
* @var array<int, string> list of possible triggercode for this object
*/
public $list_possible_triggercode = [self::STATUS_DRAFT => 'CONFERENCEORBOOTHATTENDEE_UNVALIDATE', self::STATUS_VALIDATED => 'CONFERENCEORBOOTHATTENDEE_VALIDATE', self::STATUS_USED => 'CONFERENCEORBOOTHATTENDEE_USED', self::STATUS_CANCELED => 'CONFERENCEORBOOTHATTENDEE_CANCEL'];

/**
* 'type' field format ('integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter]]', 'sellist:TableName:LabelFieldName[:KeyFieldName[:KeyFieldParent[:Filter]]]', 'varchar(x)', 'double(24,8)', 'real', 'price', 'text', 'text:none', 'html', 'date', 'datetime', 'timestamp', 'duration', 'mail', 'phone', 'url', 'password')
* Note: Filter can be a string like "(t.ref:like:'SO-%') or (t.date_creation:<:'20160101') or (t.nature:is:NULL)"
Expand Down Expand Up @@ -314,9 +324,12 @@ public function create(User $user, $notrigger = 0)
*
* @param User $user User that creates
* @param int $fromid Id of object to clone
* @return mixed New object created, <0 if KO
* @param int $notrigger 0=launch triggers after, 1=disable triggers
* @param int $nolink 0=make link between source and clone, 1=do not link
* @param array<string, mixed> $changes Default empty array else array[field] = value, naturally not id/rowid, but this could be used to create the clone in a different fk_project: $changes["fk_project"] = $newprojectid
* @return ConferenceOrBoothAttendee|int
*/
public function createFromClone(User $user, $fromid)
public function createFromClone(User $user, $fromid, $notrigger = 0, $nolink = 0, $changes = array())
{
global $langs, $extrafields;
$error = 0;
Expand All @@ -337,6 +350,24 @@ public function createFromClone(User $user, $fromid)
//foreach($this->lines as $line)
// $line->fetch_optionals();

// handle changes to fields
foreach ($changes as $key => $value) {
if ($key == "fields") {
dol_syslog('this->fields MUST NOT BE CHANGED', LOG_ERR);
$this->error = 'this->fields MUST NOT BE CHANGED';
return -2;
} elseif (array_key_exists((string) $key, $this->fields)) {
dol_syslog('key='.$key.' changed from '.$object->$key, LOG_DEBUG);
$object->$key = $value;
dol_syslog('key='.$key.' changed to '.$value, LOG_DEBUG);
} else {
$error++;
dol_syslog('key='.$key.' was not found in $this->fields', LOG_ERR);
$this->error = 'key='.$key.' was not found in $this->fields';
return -3;
}
}

// Reset some properties
unset($object->id);
unset($object->fk_user_creat);
Expand All @@ -362,26 +393,26 @@ public function createFromClone(User $user, $fromid)

// Create clone
$object->context['createfromclone'] = 'createfromclone';
$result = $object->createCommon($user);
$result = $object->createCommon($user, $notrigger);
if ($result < 0) {
$error++;
$this->setErrorsFromObject($object);
} else {
$object->ref = (string) $object->id;
$result = $object->update($user);
$result = $object->update($user, $notrigger);
}

if (!$error) {
// copy internal contacts
if ($this->copy_linked_contact($object, 'internal') < 0) {
if ($this->copy_linked_contact($object, 'internal', $notrigger) < 0) {
$error++;
}
}

if (!$error) {
// copy external contacts if same company
if (!empty($this->fk_soc) && $this->fk_soc == $object->socid) {
if ($this->copy_linked_contact($object, 'external') < 0) {
if ($this->copy_linked_contact($object, 'external', $notrigger) < 0) {
$error++;
}
}
Expand All @@ -392,6 +423,20 @@ public function createFromClone(User $user, $fromid)
// End
if (!$error) {
$this->db->commit();

require_once DOL_DOCUMENT_ROOT.'/core/class/objectlink.class.php';
$staticobjectlink = new ObjectLink($this->db);
if (!$nolink) {
$linkresult = $staticobjectlink->create($user, $this->id, $this->element, $object->id, $object->element, 'clone', $notrigger);
if ($linkresult) {
return $object;
} else {
dol_syslog('Failed to objectlink clone + source='.$this->id.' '.$staticobjectlink->error, LOG_ERR);
$this->db->rollback();
$this->error = $staticobjectlink->error;
return -1;
}
}
return $object;
} else {
$this->db->rollback();
Expand Down
Loading
Loading