diff --git a/ChangeLog.md b/ChangeLog.md index c482a95..59545f5 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,6 +1,9 @@ # Changelog for massaction ## Unreleased + +## Release 1.7 +- FIX : Compat v23 - *02/12/2025* - 1.7.1 - NEW : Add mass action to create a new supplier proposal from a customer proposal or order - *09/10/2025* - 1.7.0 ## Release 1.6 diff --git a/admin/massaction_setup.php b/admin/massaction_setup.php old mode 100755 new mode 100644 index 5b58e56..7055ca8 --- a/admin/massaction_setup.php +++ b/admin/massaction_setup.php @@ -22,51 +22,44 @@ * Put some comments here */ // Dolibarr environment -$res = @include("../../main.inc.php"); // From htdocs directory +$res = @include "../../main.inc.php"; // From htdocs directory if (! $res) { - $res = @include("../../../main.inc.php"); // From "custom" directory + $res = @include "../../../main.inc.php"; // From "custom" directory } // Libraries require_once DOL_DOCUMENT_ROOT . "/core/lib/admin.lib.php"; require_once '../lib/massaction.lib.php'; -dol_include_once('abricot/includes/lib/admin.lib.php'); +require_once dirname(__DIR__) .'/class/toolsMassactions.class.php'; + // Translations $langs->load("massaction@massaction"); // Access control if (! $user->admin) { - accessforbidden(); + accessforbidden(); } // Parameters $action = GETPOST('action', 'aZ09'); /* * Actions */ -if (preg_match('/set_(.*)/',$action,$reg)) -{ - $code=$reg[1]; - if (dolibarr_set_const($db, $code, GETPOST($code, 'aZ09'), 'chaine', 0, '', $conf->entity) > 0) - { - header("Location: ".$_SERVER["PHP_SELF"]); - exit; - } - else - { - dol_print_error($db); - } +if (preg_match('/set_(.*)/', $action, $reg)) { + $code=$reg[1]; + if (dolibarr_set_const($db, $code, GETPOST($code, 'aZ09'), 'chaine', 0, '', $conf->entity) > 0) { + header("Location: ".$_SERVER["PHP_SELF"]); + exit; + } else { + dol_print_error($db); + } } -if (preg_match('/del_(.*)/',$action,$reg)) -{ - $code=$reg[1]; - if (dolibarr_del_const($db, $code, 0) > 0) - { - Header("Location: ".$_SERVER["PHP_SELF"]); - exit; - } - else - { - dol_print_error($db); - } +if (preg_match('/del_(.*)/', $action, $reg)) { + $code=$reg[1]; + if (dolibarr_del_const($db, $code, 0) > 0) { + Header("Location: ".$_SERVER["PHP_SELF"]); + exit; + } else { + dol_print_error($db); + } } /* * View @@ -75,33 +68,28 @@ llxHeader('', $langs->trans($page_name)); // Subheader $linkback = '' - . $langs->trans("BackToModuleList") . ''; + . $langs->trans("BackToModuleList") . ''; print load_fiche_titre($langs->trans($page_name), $linkback); // Configuration header $head = massactionAdminPrepareHead(); dol_fiche_head( - $head, - 'settings', - $langs->trans("ModuleMassActionName"), - -1, - "massaction@massaction" + $head, + 'settings', + $langs->trans("ModuleMassActionName"), + -1, + "massaction@massaction" ); // Setup page goes here $form = new Form($db); $var = false; print ''; -if(!function_exists('setup_print_title')){ - print '
'.$langs->trans('AbricotNeedUpdate').' : Wiki
'; - exit; -} -setup_print_title("Parameters"); - +toolsMassactions::setupPrintTitle("Parameters"); $predefinedPrice = getDolGlobalInt('SUPPLIER_PROPOSAL_WITH_PREDEFINED_PRICES_ONLY') ? $langs->trans('MassActionSetupCreateProposalSupplierWithConf') : null; // Example with a yes / no select -setup_print_on_off('MASSACTION_AUTO_DOWNLOAD', $langs->trans('SetupAutoDownloadTitle') , $langs->trans('SetupAutoDownloadDesc')); -setup_print_on_off('MASSACTION_AUTO_SEND_SUPPLIER_PROPOSAL', $langs->trans('MassActionSetupAutoSendSupplierProposal'), $predefinedPrice); -setup_print_on_off('MASSACTION_CREATE_SUPPLIER_PROPOSAL_TO_ZERO', $langs->trans('MassActionSetupCreateProposalSupplierToZero')); +toolsMassactions::setupPrintOnOff('MASSACTION_AUTO_DOWNLOAD', $langs->trans('SetupAutoDownloadTitle'), $langs->trans('SetupAutoDownloadDesc')); +toolsMassactions::setupPrintOnOff('MASSACTION_AUTO_SEND_SUPPLIER_PROPOSAL', $langs->trans('MassActionSetupAutoSendSupplierProposal'), $predefinedPrice); +toolsMassactions::setupPrintOnOff('MASSACTION_CREATE_SUPPLIER_PROPOSAL_TO_ZERO', $langs->trans('MassActionSetupCreateProposalSupplierToZero')); print '
'; dol_fiche_end(-1); llxFooter(); diff --git a/class/actions_massaction.class.php b/class/actions_massaction.class.php index d11ec44..d6ba7b3 100644 --- a/class/actions_massaction.class.php +++ b/class/actions_massaction.class.php @@ -59,13 +59,27 @@ class Actionsmassaction extends \massaction\RetroCompatCommonHookActions public $errors = array(); /** - * Constructor + * Constructor. + * + * @param DoliDB $db Database handler. */ public function __construct($db) { $this->db = $db; } - + /** + * Prepares and displays confirmation modals for specific mass actions. + * + * Handles the 'linktomailing' (select draft campaign) and 'linksalesperson' + * (assign sales rep) actions by generating the required form parameters + * and injecting the confirmation dialog into the output. + * + * @param array $parameters Hook parameters containing context. + * @param object $object The current object. + * @param string $action The current action. + * @param HookManager $hookmanager The hook manager instance. + * @return void Sets $this->resprints with the form HTML. + */ public function doPreMassActions($parameters, &$object, &$action, $hookmanager) { global $conf, $user, $massaction, $langs; @@ -74,12 +88,11 @@ public function doPreMassActions($parameters, &$object, &$action, $hookmanager) $toprint = ''; // Action en masse d'ajout de destinataires à un e-mailing, choix de l'e-mailing - if($massaction == 'linktomailing' && isModEnabled('mailing') && $user->hasRight('mailing', 'creer') + if ($massaction == 'linktomailing' && isModEnabled('mailing') && $user->hasRight('mailing', 'creer') && (in_array('thirdpartylist', $TContext) || in_array('contactlist', $TContext) || in_array('memberlist', $TContext) - || in_array('userlist', $TContext))) - { + || in_array('userlist', $TContext))) { //Selection du mailing concerné $TMailings = array(); @@ -110,9 +123,8 @@ public function doPreMassActions($parameters, &$object, &$action, $hookmanager) } // Action en masse d'affectation de commerciaux aux tiers, choix des options - if($massaction == 'linksalesperson' && $user->hasRight('societe', 'creer') - && in_array('thirdpartylist', $TContext)) - { + if ($massaction == 'linksalesperson' && $user->hasRight('societe', 'creer') + && in_array('thirdpartylist', $TContext)) { $form = new Form($this->db); $userList = $form->select_dolusers('', 'select_salesperson', 1, '', 0, '', '', 0, 0, 0, '', 0, '', '', 0, 0, 1); @@ -138,13 +150,12 @@ public function doPreMassActions($parameters, &$object, &$action, $hookmanager) $this->resprints = $toprint; } - /** * Overloading the doMassActions function : replacing the parent's function with the one below * - * @param array() $parameters Hook metadatas (context, etc...) - * @param CommonObject &$object The object to process (an invoice if you are in invoice module, a propale in propale's module, etc...) - * @param string &$action Current action (if set). Generally create or edit or null + * @param array $parameters Hook metadatas (context, etc...) + * @param CommonObject $object The object to process (an invoice if you are in invoice module, a propale in propale's module, etc...) + * @param string $action Current action (if set). Generally create or edit or null * @param HookManager $hookmanager Hook manager propagated to allow calling another hook * @return int < 0 on error, 0 on success, 1 to replace standard code */ @@ -153,7 +164,7 @@ public function doMassActions($parameters, &$object, &$action, $hookmanager) global $conf, $user, $langs, $db, $massaction, $diroutputmassaction; $langs->load('massaction@massaction'); - if(empty($massaction) && GETPOSTISSET('massaction')) $massaction = GETPOST('massaction', 'alphanohtml'); + if (empty($massaction) && GETPOSTISSET('massaction')) $massaction = GETPOST('massaction', 'alphanohtml'); $TContext = explode(":", $parameters['context']); $confirm = GETPOST('confirm', 'alphanohtml'); @@ -162,8 +173,7 @@ public function doMassActions($parameters, &$object, &$action, $hookmanager) $errormsg = ''; // Error message // Action en masse "Génération archive zip" - if ($massaction == 'generate_zip' && strpos($parameters['context'], 'list') !== 0) - { + if ($massaction == 'generate_zip' && strpos($parameters['context'], 'list') !== 0) { // @TODO Ask for compression format and filename /*if($massaction == 'generate_zip') { @@ -177,13 +187,12 @@ public function doMassActions($parameters, &$object, &$action, $hookmanager) }*/ - if (empty($diroutputmassaction) || empty($parameters['uploaddir'])) - { + if (empty($diroutputmassaction) || empty($parameters['uploaddir'])) { $error++; $errormsg = $langs->trans('NoDirectoryAvailable'); } - if(!$error) { + if (!$error) { // Lists all file to add in the zip archive require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php'; $formfile = new FormFile($db); @@ -191,7 +200,7 @@ public function doMassActions($parameters, &$object, &$action, $hookmanager) foreach ($parameters['toselect'] as $objectid) { $object->fetch($objectid); $ref = dol_sanitizeFileName($object->ref); - if($object->element == 'invoice_supplier') $subdir = get_exdir($object->id, 2, 0, 0, $object, 'invoice_supplier').$ref; + if ($object->element == 'invoice_supplier') $subdir = get_exdir($object->id, 2, 0, 0, $object, 'invoice_supplier').$ref; else $subdir = $ref; $filedir = $parameters['uploaddir'] . '/' . $subdir; @@ -212,7 +221,7 @@ public function doMassActions($parameters, &$object, &$action, $hookmanager) } } - if(!$error) { + if (!$error) { //$compressmode = GETPOST('compress_mode', 'alphanohtml'); $compressmode = 'zip'; //$filename = GETPOST('filename'); @@ -234,18 +243,16 @@ public function doMassActions($parameters, &$object, &$action, $hookmanager) dol_delete_dir_recursive($tempdir); // Auto Download if (file_exists($diroutputmassaction.'/'.$filename)) { - if (getDolGlobalString('MASSACTION_AUTO_DOWNLOAD')){ + if (getDolGlobalString('MASSACTION_AUTO_DOWNLOAD')) { header('Content-Type: application/zip'); header('Content-Disposition: attachment; filename="'.$filename.'"'); header('Content-Length: ' . filesize($diroutputmassaction.'/'.$filename)); readfile($diroutputmassaction.'/'.$filename); - } - else { + } else { setEventMessage($langs->trans('MassActionZIPGenerated', count($toarchive))); } - } - else{ - setEventMessage($langs->trans('MassActionErrorGeneration'),'errors'); + } else { + setEventMessage($langs->trans('MassActionErrorGeneration'), 'errors'); } header('Location:'.$_SERVER['PHP_SELF']); exit; @@ -257,8 +264,7 @@ public function doMassActions($parameters, &$object, &$action, $hookmanager) (in_array('thirdpartylist', $TContext) || in_array('contactlist', $TContext) || in_array('memberlist', $TContext) - || in_array('userlist', $TContext))) - { + || in_array('userlist', $TContext))) { $mailing_selected = GETPOST('select_mailings', 'int'); $toselect = GETPOST('toselect', 'array'); @@ -304,8 +310,7 @@ public function doMassActions($parameters, &$object, &$action, $hookmanager) // Action en masse d'affectation de commercial if ($action == 'confirm_linksalesperson' && $confirm == 'yes' && $user->hasRight('societe', 'creer') && - in_array('thirdpartylist', $TContext)) - { + in_array('thirdpartylist', $TContext)) { $toselect = GETPOST('toselect', 'array'); $select_salesperson = GETPOST('select_salesperson', 'array'); // Option : 0 = ajout, 1 = remplacement, 2 = retrait @@ -313,20 +318,20 @@ public function doMassActions($parameters, &$object, &$action, $hookmanager) $societe = new Societe($this->db); - foreach($toselect as $thirdparty_id) { + foreach ($toselect as $thirdparty_id) { $res = $societe->fetch($thirdparty_id); - if($res) { - if($salesperson_option == 2) { - foreach($select_salesperson as $id_salesperson) { + if ($res) { + if ($salesperson_option == 2) { + foreach ($select_salesperson as $id_salesperson) { $res = $societe->del_commercial($user, $id_salesperson); - if($res < 0) { + if ($res < 0) { $error++; $errormsg = $societe->error; } } } else { $res = $societe->setSalesRep($select_salesperson, ($salesperson_option == 0)); - if($res < 0) { + if ($res < 0) { $error++; $errormsg = $societe->error; } @@ -337,7 +342,7 @@ public function doMassActions($parameters, &$object, &$action, $hookmanager) } } - if(!$error) { + if (!$error) { setEventMessage($langs->trans('MassActionLinkSalesPersonSuccess')); } } @@ -369,24 +374,21 @@ public function addMoreMassActions($parameters, &$object, &$action, $hookmanager $TContext = explode(":", $parameters['context']); // Ajout de l'action en masse "Génération archive zip" sur les listes - if (strpos($parameters['context'], 'list') !== false) - { + if (strpos($parameters['context'], 'list') !== false) { $label = ' ' . $langs->trans("MassActionGenerateZIP"); $this->resprints = ''; } // Ajout de l'action en masse "Envoi par e-mail" sur la liste des factures fournisseur - if (in_array('supplierinvoicelist', $TContext)) - { + if (in_array('supplierinvoicelist', $TContext)) { $this->resprints .= ''; } // Ajout de l'action en masse d'ajout de destinataires à un e-mailing - if(in_array('thirdpartylist', $TContext) + if (in_array('thirdpartylist', $TContext) || in_array('contactlist', $TContext) || in_array('memberlist', $TContext) || in_array('userlist', $TContext)) { - if (isModEnabled('mailing') && $user->hasRight('mailing', 'creer')) { $label = ' ' . $langs->trans("MassActionLinktoMailing"); $this->resprints .= ''; @@ -394,7 +396,7 @@ public function addMoreMassActions($parameters, &$object, &$action, $hookmanager } // Ajout de l'action en masse d'affectation de commerciaux aux tiers - if(in_array('thirdpartylist', $TContext)){ + if (in_array('thirdpartylist', $TContext)) { if ($user->hasRight('societe', 'creer')) { $label = ' ' . $langs->trans("MassActionLinkSalesperson"); $this->resprints .= ''; @@ -408,8 +410,16 @@ public function addMoreMassActions($parameters, &$object, &$action, $hookmanager return -1; } } - - function selectCompression() { + /** + * Generates an HTML dropdown to select a compression method. + * + * Checks for server support (e.g., Gzip, Bzip2) and automatically disables + * options if the required PHP functions are missing. + * + * @return string The HTML '; - foreach($compression as $key => $val) - { - if (! $val['function'] || function_exists($val['function'])) // Enabled export format - { + foreach ($compression as $key => $val) { + if (! $val['function'] || function_exists($val['function'])) { // Enabled export format $select.= ''; - } - else // Disabled export format + } else // Disabled export format { $select.= ''; } @@ -432,16 +439,20 @@ function selectCompression() { return $select; } - - /** - * @param $parameters - * @param $object - * @param $action - * @param $hookmanager - * @return int + * Processes mass actions on document lines. + * + * Handles logic for deleting lines, updating quantities/margins, and creating + * supplier price requests within Proposals, Orders, and Invoices contexts. + * + * @param array $parameters Hook parameters containing context. + * @param object $object The current business object. + * @param string $action The current action being performed. + * @param HookManager $hookmanager The hook manager instance. + * @return int Returns 0. */ - public function doActions($parameters, &$object, &$action, $hookmanager) : int { + public function doActions($parameters, &$object, &$action, $hookmanager) : int + { global $langs, $user, $conf; $TContexts = explode(':', $parameters['context']); @@ -470,7 +481,7 @@ public function doActions($parameters, &$object, &$action, $hookmanager) : int { $massAction->deleteLine($index, $selectedLine); } - if(!empty($massAction->TErrors)) { + if (!empty($massAction->TErrors)) { $this->db->rollback(); } else { $this->db->commit(); @@ -479,7 +490,6 @@ public function doActions($parameters, &$object, &$action, $hookmanager) : int { $massAction->handleErrors($TSelectedLines, $massAction->TErrors, $action); $action = ''; - } if (($action == 'edit_quantity' || $action == 'edit_margin') && $confirm == 'yes') { @@ -503,11 +513,10 @@ public function doActions($parameters, &$object, &$action, $hookmanager) : int { $index = array_search(intval($selectedLine), $TRowIds); $massAction->updateLine($index, $quantity, $marge_tx); - } } - if(!empty($massAction->TErrors)) { + if (!empty($massAction->TErrors)) { $this->db->rollback(); } else { $this->db->commit(); @@ -516,7 +525,6 @@ public function doActions($parameters, &$object, &$action, $hookmanager) : int { $massAction->handleErrors($TSelectedLines, $errors, $action); $action = ''; - } // Supplier price request management if ($action == 'createSupplierPrice') { @@ -527,14 +535,18 @@ public function doActions($parameters, &$object, &$action, $hookmanager) : int { } return 0; } - - /** - * @param $parameters - * @param Propal|Facture|Commande $object - * @param $action - * @param $hookmanager - * @return int + * Injects checkboxes and mass action buttons into document line tables. + * + * Checks permissions and object status, then adds a form with mass action options + * (e.g., Delete, Edit Margin) and JavaScript to handle row selection in the + * proposal, order, or invoice card. + * + * @param array $parameters Hook context (e.g., 'propalcard'). + * @param object $object The current business object. + * @param string $action The current action state. + * @param HookManager $hookmanager The hook manager instance. + * @return int Returns 0. */ public function addMoreActionsButtons($parameters, &$object, &$action, $hookmanager) { @@ -566,7 +578,7 @@ public function addMoreActionsButtons($parameters, &$object, &$action, $hookmana $status = $object->status; $enableCheckboxes = 0; - if( + if ( ( ($object->element == 'propal' && $status == Propal::STATUS_DRAFT) || ($object->element == 'facture' && $status == Facture::STATUS_DRAFT) || @@ -699,37 +711,38 @@ function updateSelectedLines() { load('massaction@massaction'); - $TContexts = explode(':',$parameters['context']); - - if(in_array('ordercard', $TContexts) || in_array('propalcard', $TContexts) || in_array('invoicecard', $TContexts)) { + $TContexts = explode(':', $parameters['context']); - $rightCreate = method_exists($user,'hasRight') ? $user->hasRight($object->element,'create') : $user->rights->{$object->element}->creer; - $rightWrite = method_exists($user,'hasRight') ? $user->hasRight($object->element,'write') : $user->rights->{$object->element}->write; + if (in_array('ordercard', $TContexts) || in_array('propalcard', $TContexts) || in_array('invoicecard', $TContexts)) { + $rightCreate = method_exists($user, 'hasRight') ? $user->hasRight($object->element, 'create') : $user->rights->{$object->element}->creer; + $rightWrite = method_exists($user, 'hasRight') ? $user->hasRight($object->element, 'write') : $user->rights->{$object->element}->write; $displayButton = ($object->statut == 0 && ($rightCreate || $rightWrite)); - if($action == 'cut') { + if ($action == 'cut') { $selectedLines = GETPOST('selectedLines', 'alpha'); } @@ -751,13 +764,13 @@ function formObjectOptions($parameters, &$object, &$action, $hookmanager) } if ($displayButton) { - if($object->element=='facture') $idvar = 'facid'; + if ($object->element=='facture') $idvar = 'facid'; else $idvar = 'id'; - if($object->element == 'propal') { + if ($object->element == 'propal') { $fiche = '/comm/propal/card.php'; - } else if($object->element == 'commande') { + } elseif ($object->element == 'commande') { $fiche = '/commande/card.php'; - } else if($object->element == 'facture') { + } elseif ($object->element == 'facture') { $fiche = '/compta/facture/card.php'; } @@ -770,7 +783,7 @@ function formObjectOptions($parameters, &$object, &$action, $hookmanager) $('#pop-split').remove(); $('body').append('
'); - $.get('id.'&element='.$object->element.'&selectedLines='.$selectedLines ?>', function(data) { + $.get('id.'&element='.$object->element.'&selectedLines='.$selectedLines ?>', function(data) { $('#pop-split').html(data) $('#pop-split').dialog({ @@ -857,5 +870,4 @@ function formObjectOptions($parameters, &$object, &$action, $hookmanager) } return 0; } - } diff --git a/class/massaction.class.php b/class/massaction.class.php index 39bbdf6..29529b1 100644 --- a/class/massaction.class.php +++ b/class/massaction.class.php @@ -1,8 +1,27 @@ . + */ include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php'; -class MassAction { +/** + * Class for MassAction + */ +class MassAction +{ public $TErrors = []; @@ -11,7 +30,10 @@ class MassAction { public $db; /** - * @param $object + * Constructor. + * + * @param DoliDB $db Database handler. + * @param object $object The target business object to process. */ public function __construct($db, $object) { @@ -21,10 +43,15 @@ public function __construct($db, $object) /** - * @param int $index - * @param float $quantity - * @param float|null $marge - * @return int + * Updates a specific line within the current object. + * + * Recalculates the unit price if a margin is provided, preserves existing line data, + * and routes the update to the correct object-specific method (Propal, Order, or Invoice). + * + * @param int $index The array index of the line to update. + * @param float|null $quantity The new quantity (null to keep existing). + * @param float|null $marge The new margin rate to recalculate price (null to keep existing price). + * @return int Returns >0 on success, <0 on failure. */ public function updateLine(int $index, ?float $quantity, ?float $marge = null): int { @@ -56,7 +83,7 @@ public function updateLine(int $index, ?float $quantity, ?float $marge = null): $ref_ext = $this->object->lines[$index]->ref_ext; $rang = $this->object->lines[$index]->rang; - if($marge !== null && $marge !== '') { + if ($marge !== null && $marge !== '') { $marge = floatval($marge); $pu_ht = MassAction::getPuByMargin($this->object, $index, $marge, $pa_ht); } else $pu_ht = $subprice; @@ -79,7 +106,7 @@ public function updateLine(int $index, ?float $quantity, ?float $marge = null): $rowid, $desc, $pu_ht, $quantity, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, $price_base_type, $info_bits, $date_start, $date_end, $type, $fk_parent_line, $skip_update_total, - $fk_fournprice, $pa_ht, $label, $special_code, $array_options, $fk_unit, 0, $notrigger, $ref_ext,$rang + $fk_fournprice, $pa_ht, $label, $special_code, $array_options, $fk_unit, 0, $notrigger, $ref_ext, $rang ); break; case "facture": @@ -87,25 +114,29 @@ public function updateLine(int $index, ?float $quantity, ?float $marge = null): $rowid, $desc, $pu_ht, $quantity, $remise_percent, $date_start, $date_end, $txtva, $txlocaltax1, $txlocaltax2, $price_base_type, $info_bits, $type, $fk_parent_line, $skip_update_total, - $fk_fournprice, $pa_ht, $label, $special_code, $array_options, $situation_percent, $fk_unit, 0, $notrigger, $ref_ext,$rang + $fk_fournprice, $pa_ht, $label, $special_code, $array_options, $situation_percent, $fk_unit, 0, $notrigger, $ref_ext, $rang ); break; default: break; } - if ($resUpdate < 0){ + if ($resUpdate < 0) { $this->TErrors[] = $langs->trans('ErrorUpdateLine', $index + 1); } return $resUpdate; - } /** - * @param int $index - * @param int $selectedLine - * @return int + * Deletes a specific line from the current object. + * + * Routes the deletion request to the appropriate method based on the object type + * (Order, Invoice, or Proposal), handling differences in method signatures. + * + * @param int $index The visual index of the line (used for error reporting). + * @param int $selectedLine The unique database ID (rowid) of the line to delete. + * @return int Returns >0 on success, <0 on failure. */ public function deleteLine(int $index, int $selectedLine): int { @@ -131,11 +162,16 @@ public function deleteLine(int $index, int $selectedLine): int } /** - * @param CommonObject $object - * @param int $index - * @param float $marge_tx - * @param float $pa_ht - * @return float + * Calculates the new unit price based on a desired margin rate. + * + * Applies the margin to the buying price (cost) and adjusts for any existing + * discount on the line to ensure the final margin is respected. + * + * @param CommonObject $object The business object. + * @param int $index The line index. + * @param float $marge_tx The desired margin rate (%). + * @param float $pa_ht The buying price (cost price). + * @return float The calculated unit price (HT), or -1 if cost price is invalid. */ private function getPuByMargin(CommonObject $object, int $index, float $marge_tx, float $pa_ht): float { @@ -156,7 +192,16 @@ private function getPuByMargin(CommonObject $object, int $index, float $marge_tx return $pu_ht; } - + /** + * Validates input values for mass update actions. + * + * Checks if the provided value is numeric and enforces non-negative constraints + * specifically for quantity updates. + * + * @param string $action The action type (e.g., 'edit_quantity', 'edit_margin'). + * @param mixed $field The input value to validate. + * @return array An array of error messages, or empty if valid. + */ public static function checkFields($action, $field) { @@ -172,21 +217,24 @@ public static function checkFields($action, $field) // Convertir en float seulement si la validation est réussie $field = floatval($field); - if($action == 'edit_quantity' && $field < 0) { + if ($action == 'edit_quantity' && $field < 0) { $TErrors[] = $langs->trans('ErrorQtyNegative'); } } return $TErrors; - } /** - * @param array $TSelectedLines - * @param array $TErrors - * @param string $action + * Finalizes mass action execution by handling user feedback and navigation. + * + * Displays a success message and redirects on success, or logs errors + * and displays distinct error notifications on failure. + * + * @param array $TSelectedLines List of processed line IDs. + * @param array $TErrors List of errors encountered during execution. + * @param string $action The action type (e.g., 'delete_lines', 'edit_quantity'). * @return void - * @throws Exception */ public function handleErrors(array $TSelectedLines, array $TErrors, string $action): void { @@ -205,7 +253,7 @@ public function handleErrors(array $TSelectedLines, array $TErrors, string $acti setEventMessage($confirmMsg); header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $this->object->id); } else { - if(!empty($this->db->lasterror())) { + if (!empty($this->db->lasterror())) { $this->TErrors[] = $this->db->lasterror(); } setEventMessages('Errors', $this->TErrors, 'errors'); @@ -215,11 +263,16 @@ public function handleErrors(array $TSelectedLines, array $TErrors, string $acti } /** - * @param string $action - * @param array $TSelectedLines - * @param int $id - * @param Form $form - * @return string + * Generates the HTML for a mass action confirmation modal. + * + * Configures the dialog title, question, and input fields based on the + * requested action (delete, edit quantity, supplier select, etc.). + * + * @param string $action The triggered action (e.g., 'predelete'). + * @param array $TSelectedLines Array of selected line IDs. + * @param int $id The parent object ID (used for form URL). + * @param Form $form The Dolibarr Form handler. + * @return string The rendered HTML of the confirmation modal. */ public static function getFormConfirm(string $action, array $TSelectedLines, int $id, Form $form): string { @@ -234,14 +287,11 @@ public static function getFormConfirm(string $action, array $TSelectedLines, int $page = $_SERVER["PHP_SELF"] . '?id=' . $id; if ($action == 'predelete') { - $actionInFormConfirm = 'delete_lines'; $title = $langs->trans("ConfirmMassDeletion"); $question = $langs->trans("ConfirmMassDeletionQuestion", $nbrOfSelectedLines); $formQuestion = null; - } elseif ($action == "preeditquantity") { - $actionInFormConfirm = 'edit_quantity'; $title = $langs->trans('MassActionConfirmEdit'); @@ -252,9 +302,7 @@ public static function getFormConfirm(string $action, array $TSelectedLines, int 'name' => 'quantity' ) ); - } elseif ($action == 'preeditmargin') { - $actionInFormConfirm = 'edit_margin'; $title = $langs->trans('MassActionConfirmEdit'); $question = $langs->trans('MassActionConfirmEditMargin', $nbrOfSelectedLines); @@ -274,7 +322,7 @@ public static function getFormConfirm(string $action, array $TSelectedLines, int 'label' => $langs->trans('MassActionSelectSupplier'), 'type' => 'other', 'name' => 'supplierPrice', - 'value' => $form->select_company('', 'supplierid', '(s.fournisseur:IN:' . SOCIETE::SUPPLIER .')' , 1, 1, 0, [], 0, 'minwidth100', '', '', 1, [], true), + 'value' => $form->select_company('', 'supplierid', '(s.fournisseur:IN:' . SOCIETE::SUPPLIER .')', 1, 1, 0, [], 0, 'minwidth100', '', '', 1, [], true), ), ); @@ -286,10 +334,9 @@ public static function getFormConfirm(string $action, array $TSelectedLines, int 'value' => $form->selectModelMail("", 'supplier_proposal_send', 0, 0, ''), ); } - } - if(empty($actionInFormConfirm) || empty($title) ) { + if (empty($actionInFormConfirm) || empty($title) ) { return ''; } @@ -305,8 +352,13 @@ public static function getFormConfirm(string $action, array $TSelectedLines, int } /** - * @param Form $form - * @return string + * Generates the HTML selector for available mass actions. + * + * Builds the list of actions (Split, Edit Margin/Qty, Delete, Supplier Request) + * based on the current Dolibarr version, user permissions, and module settings. + * + * @param Form $form The Dolibarr Form handler. + * @return string The rendered HTML of the mass action selector. */ public static function getMassActionButton(Form $form): string { @@ -339,7 +391,7 @@ public static function getMassActionButton(Form $form): string * @param array $TSelectedLines The lines to process. * @param array $supplierIds The supplier to send the supplier proposal * @param int $templateId The email template to use for sending the supplier proposal - * * @return void This function does not return a value but outputs messages. + * @return void This function does not return a value but outputs messages. */ public static function handleCreateSupplierPriceAction(CommonObject $object, array $TSelectedLines, array $supplierIds, int $templateId): void { @@ -422,7 +474,6 @@ public static function processSingleSupplier(int $supplierId, array $selectedLin $db->commit(); return $successLog; - } catch (Exception $e) { $db->rollback(); throw new Exception($langs->trans("MassActionErrorProcessingSupplier", $supplier->name) . ': ' . $e->getMessage()); @@ -432,12 +483,12 @@ public static function processSingleSupplier(int $supplierId, array $selectedLin /** * Creates and populates a supplier price request. * - * * @param int $supplierId Id of the supplier - * * @param array $lines Array of lines to process - * * @param CommonObject $object The original source object (e.g., Order, Proposal) - * * @return SupplierProposal The created supplier proposal request. - * * @throws Exception if creation fails. - */ + * @param int $supplierId Id of the supplier + * @param array $lines Array of lines to process + * @param CommonObject $object The original source object (e.g., Order, Proposal) + * @return SupplierProposal The created supplier proposal request. + * @throws Exception if creation fails. + */ public static function createSupplierProposal(int $supplierId, array $lines, CommonObject $object): SupplierProposal { global $db, $user, $langs; @@ -474,6 +525,7 @@ public static function createSupplierProposal(int $supplierId, array $lines, Com * Generates the PDF document for a price request. * * @param SupplierProposal $proposal The price request to generate the PDF for. + * @return void * @throws Exception if PDF generation fails. */ public static function generateProposalPdf(SupplierProposal $proposal): void @@ -491,6 +543,7 @@ public static function generateProposalPdf(SupplierProposal $proposal): void * @param SupplierProposal $supplierProposal The supplier price request to send. * @param Societe $supplier The supplier to send the email to. * @param int $templateId The email template to use for sending the email. + * @return void * @throws Exception if the email sending fails. */ public static function sendProposalByEmail(SupplierProposal $supplierProposal, Societe $supplier, int $templateId): void @@ -519,7 +572,6 @@ public static function sendProposalByEmail(SupplierProposal $supplierProposal, S $errorDetails = is_array($supplier->error) ? implode(', ', $supplier->error) : $supplier->error; throw new Exception($langs->trans("MassActionFailedToSendEmail"). $errorDetails); } - } /** @@ -554,5 +606,4 @@ public static function getSelectedLineDetails(CommonObject $object, array $selec } return $details; } - } diff --git a/class/toolsMassactions.class.php b/class/toolsMassactions.class.php new file mode 100644 index 0000000..222131d --- /dev/null +++ b/class/toolsMassactions.class.php @@ -0,0 +1,90 @@ +. + */ + + +/** + * Class for MassAction + */ +class toolsMassactions +{ + /** + * Renders a configuration table row with an On/Off toggle switch. + * + * Displays the setting label (with optional tooltip and description) and generates + * either an AJAX toggle or a link that forces a page reload to change the setting. + * + * @param string $confkey The global configuration key (DB constant name). + * @param string|false $title Label text. If false, the $confkey translation is used. + * @param string $desc Description text displayed below the label. + * @param string|false $help Tooltip text or translation key. + * @param int $width Width of the toggle column (default: 300). + * @param bool $forcereload If true, reloads the page on click. If false, uses AJAX. + * @param array $ajaxConstantOnOffInput Additional parameters for the AJAX toggle. + * @return void Output is sent directly to the buffer. + */ + public static function setupPrintOnOff($confkey, $title = false, $desc = '', $help = false, $width = 300, $forcereload = false, $ajaxConstantOnOffInput = array()) + { + global $var, $bc, $langs, $conf, $form; + $var=!$var; + + print ''; + print ''; + + + if (empty($help) && !empty($langs->tab_translate[$confkey . '_HELP'])) { + $help = $confkey . '_HELP'; + } + + if (!empty($help)) { + print $form->textwithtooltip(($title?$title:$langs->trans($confkey)), $langs->trans($help), 2, 1, img_help(1, '')); + } else { + print $title?$title:$langs->trans($confkey); + } + + if (!empty($desc)) { + print '
'.$langs->trans($desc).''; + } + print ''; + print ' '; + print ''; + + if ($forcereload) { + $link = $_SERVER['PHP_SELF'].'?action=set_'.$confkey.'&token='. newToken() .'&'.$confkey.'='.intval((empty($conf->global->{$confkey}))); + $toggleClass = empty($conf->global->{$confkey})?'fa-toggle-off':'fa-toggle-on font-status4'; + print ''; + } else { + print ajax_constantonoff($confkey, $ajaxConstantOnOffInput); + } + print ''; + } + /** + * Generates the HTML header row for a configuration table. + * + * @param string $title Translation key or label for the first column (default: "Parameter"). + * @return string Returns the HTML string. + */ + public static function setupPrintTitle($title = 'Parameter') :string + { + global $langs; + $out = ''; + $out .= ''.$langs->trans($title) . ''; + $out .= ' '; + $out .= ''.$langs->trans('Value').''; + $out .= ''; + return $out; + } +} diff --git a/core/modules/modmassaction.class.php b/core/modules/modmassaction.class.php index d3df002..6eca11b 100644 --- a/core/modules/modmassaction.class.php +++ b/core/modules/modmassaction.class.php @@ -38,11 +38,11 @@ class modmassaction extends DolibarrModules * * @param DoliDB $db Database handler */ - function __construct($db) + public function __construct($db) { - global $langs,$conf; + global $langs,$conf; - $this->db = $db; + $this->db = $db; $this->editor_name = 'ATM Consulting'; $this->editor_url = 'https://www.atm-consulting.fr'; @@ -61,7 +61,7 @@ function __construct($db) // Module description, used if translation string 'ModuleXXXDesc' not found (where XXX is value of numeric property 'numero' of module) $this->description = "ModuleMassActionDesc"; // Possible values for version are: 'development', 'experimental', 'dolibarr' or version - $this->version = '1.7.0'; + $this->version = '1.7.1'; // Key used in llx_const table to save module status enabled/disabled (where MYMODULE is value of property name of module in uppercase) $this->const_name = 'MAIN_MODULE_'.strtoupper($this->name); // Where to store the module in setup page (0=common,1=interface,2=others,3=very specific) @@ -101,19 +101,18 @@ function __construct($db) // ); $this->const = array(); - $this->tabs = array(); + $this->tabs = array(); - // Dictionaries - if (! isModEnabled('massaction')) - { - $conf->massaction=new stdClass(); - $conf->massaction->enabled=0; - } + // Dictionaries + if (! isModEnabled('massaction')) { + $conf->massaction=new stdClass(); + $conf->massaction->enabled=0; + } $this->dictionaries=array(); - // Boxes + // Boxes // Add here list of php file(s) stored in core/boxes that contains class to show a box. - $this->boxes = array(); // List of boxes + $this->boxes = array(); // List of boxes // Permissions $this->rights = array(); // Permission array used by this module @@ -122,7 +121,6 @@ function __construct($db) // Main menu entries $this->menu = array(); // List of menus to add $r=0; - } /** @@ -130,14 +128,14 @@ function __construct($db) * The init function add constants, boxes, permissions and menus (defined in constructor) into Dolibarr database. * It also creates data directories * - * @param string $options Options when enabling module ('', 'noboxes') + * @param string $options Options when enabling module ('', 'noboxes') * @return int 1 if OK, 0 if KO */ - function init($options='') + public function init($options = '') { $sql = array(); - define('INC_FROM_DOLIBARR',true); + define('INC_FROM_DOLIBARR', true); $this->addEmailTemplateForSupplierProposal(); @@ -149,10 +147,10 @@ function init($options='') * Remove from database constants, boxes and permissions from Dolibarr database. * Data directories are not deleted * - * @param string $options Options when enabling module ('', 'noboxes') + * @param string $options Options when enabling module ('', 'noboxes') * @return int 1 if OK, 0 if KO */ - function remove($options='') + public function remove($options = '') { $sql = array(); return $this->_remove($sql, $options); @@ -177,7 +175,6 @@ private function addEmailTemplateForSupplierProposal() : void $obj = $this->db->fetch_object($res); if ($obj->total == 0) { - $topic = $langs->transnoentities('MassActionSupplierPriceRequest') . ' __REF__'; $content = $langs->transnoentities('MassActionHello') . " __THIRDPARTY_NAME__,\n\n"; $content .= $langs->transnoentities('MassActionPleaseFindAttachedOurPriceRequest') . " __REF__.\n\n"; @@ -196,6 +193,4 @@ private function addEmailTemplateForSupplierProposal() : void $this->db->query($sql); } } - - } diff --git a/script/showLines.php b/script/showLines.php index ec5e2f8..d53951c 100644 --- a/script/showLines.php +++ b/script/showLines.php @@ -1,4 +1,19 @@ . + */ if (! defined('NOTOKENRENEWAL')) define('NOTOKENRENEWAL', '1'); // Do not roll the Anti CSRF token (used if MAIN_SECURITY_CSRF_WITH_TOKEN is on) //if (! defined('NOSTYLECHECK')) define('NOSTYLECHECK', '1'); // Do not check style html tag into posted data if (! defined('NOREQUIREMENU')) define('NOREQUIREMENU', '1'); // If there is no need to load and show top and left menu @@ -14,9 +29,9 @@ //if (! defined('CSRFCHECK_WITH_TOKEN')) define('CSRFCHECK_WITH_TOKEN', '1'); // Force use of CSRF protection with tokens even for GET //if (! defined('NOBROWSERNOTIF')) define('NOBROWSERNOTIF', '1'); // Disable browser notification - if(is_file('../main.inc.php')) include("../main.inc.php"); - else if(is_file('../../../main.inc.php')) include("../../../main.inc.php"); - else include("../../main.inc.php"); + if (is_file('../main.inc.php')) include "../main.inc.php"; +elseif (is_file('../../../main.inc.php')) include "../../../main.inc.php"; +else include "../../main.inc.php"; require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php'; require_once DOL_DOCUMENT_ROOT.'/comm/propal/class/propal.class.php'; @@ -39,7 +54,7 @@ $token = function_exists('newToken')?newToken():$_SESSION['newtoken']; - ?> +?>
@@ -53,12 +68,12 @@ $form=new Form($db); if ((float) DOL_VERSION < 18.0) { echo $form->select_company($object->socid, 'socid', '(s.client=1 OR s.client=2 OR s.client=3)'); - }else{ + } else { echo $form->select_company($object->socid, 'socid', '( (s.client:=:1) OR (s.client:=:2) OR (s.client:=:3) )'); } - if(!empty($mc)) { + if (!empty($mc)) { echo ' - '. $langs->trans('EntityTo').' : '. $mc->select_entities($conf->entity, 'split_entity'); } @@ -83,7 +98,7 @@ $TRowIds = array_column($object->lines, 'rowid'); - $TSelectedLines = explode(',',$selectedLines); + $TSelectedLines = explode(',', $selectedLines); if (!empty($TSelectedLines)) { foreach ($TSelectedLines as $selectedLine) { @@ -93,15 +108,14 @@ $line = $object->lines[$index]; - if($line->fk_product>0) { + if ($line->fk_product>0) { $prod=new Product($db); $prod->fetch($line->fk_product); $text = $prod->getNomUrl(1).' - '.$prod->label; $desc = dol_htmlentitiesbr($line->desc); - $label = $form->textwithtooltip($text,$desc,3); - } - else{ + $label = $form->textwithtooltip($text, $desc, 3); + } else { $label = !empty($line->desc) ? $line->desc : $line->label; } @@ -110,29 +124,26 @@ $class=($class=='impair') ? 'pair':'impair'; - if($line->product_type==9) { + if ($line->product_type==9) { ?> - tva_tx,2) ?>% - subprice)?$line->price:$line->subprice,0,'',1,-1,-1,$conf->currency); ?> + tva_tx, 2) ?>% + subprice)?$line->price:$line->subprice, 0, '', 1, -1, -1, $conf->currency); ?> qty ?> - remise_percent,2) ?>% - total_ht,0,'',1,-1,-1,$conf->currency); ?> + remise_percent, 2) ?>% + total_ht, 0, '', 1, -1, -1, $conf->currency); ?> . + */ if (! defined('NOTOKENRENEWAL')) define('NOTOKENRENEWAL', '1'); // Do not roll the Anti CSRF token (used if MAIN_SECURITY_CSRF_WITH_TOKEN is on) //if (! defined('NOSTYLECHECK')) define('NOSTYLECHECK', '1'); // Do not check style html tag into posted data if (! defined('NOREQUIREMENU')) define('NOREQUIREMENU', '1'); // If there is no need to load and show top and left menu @@ -14,9 +29,9 @@ //if (! defined('CSRFCHECK_WITH_TOKEN')) define('CSRFCHECK_WITH_TOKEN', '1'); // Force use of CSRF protection with tokens even for GET //if (! defined('NOBROWSERNOTIF')) define('NOBROWSERNOTIF', '1'); // Disable browser notification - if(is_file('../main.inc.php')) include("../main.inc.php"); - else if(is_file('../../../main.inc.php')) include("../../../main.inc.php"); - else include("../../main.inc.php"); + if (is_file('../main.inc.php')) include "../main.inc.php"; +elseif (is_file('../../../main.inc.php')) include "../../../main.inc.php"; +else include "../../main.inc.php"; require_once DOL_DOCUMENT_ROOT.'/comm/propal/class/propal.class.php'; require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php'; @@ -40,199 +55,183 @@ $entity = GETPOST('split_entity', 'int'); $TMoveLine = GETPOST('TMoveLine', 'array'); $langs->load('massaction@massaction'); - if(empty($TMoveLine)){ - $json->result = -1; // 0 nothing, 1 ok, -x errors - $json->errorMessage = $langs->trans('EmptyTMoveLine'); - print json_encode($json); - exit; - } +if (empty($TMoveLine)) { + $json->result = -1; // 0 nothing, 1 ok, -x errors + $json->errorMessage = $langs->trans('EmptyTMoveLine'); + print json_encode($json); + exit; +} $classname = $element; global $id_origin_line; $object = new $classname($db); $object->fetch($id); - if(empty($entity))$entity=$conf->entity; + if (empty($entity))$entity=$conf->entity; - if($action == 'split' || $action=='copy') { - $json->result = 1; // vu que à la base il n'y a pas de gestion d'erreur sur ce script il faut partir du postula que c'est ok, perso ça me rends fou, mais on pourra le passer à -1 si erreurs plus tard... mais franchement c'est moche... +if ($action == 'split' || $action=='copy') { + $json->result = 1; // vu que à la base il n'y a pas de gestion d'erreur sur ce script il faut partir du postula que c'est ok, perso ça me rends fou, mais on pourra le passer à -1 si erreurs plus tard... mais franchement c'est moche... - $fk_target = GETPOST('fk_element_split', 'int'); - if ($fk_target > 0) - { - $new_object = new $classname($db); - $new_object->fetch($fk_target); - - // copie des coefs de la propal source si la propal de destination en est dépourvu - - if(!empty($conf->nomenclature) && isModEnabled('nomenclature') && in_array($element, array('propal', 'commande'))) { - dol_include_once('/nomenclature/class/nomenclature.class.php'); - $PDOdb = new TPDOdb; - - if (!empty($TMoveLine)) // on ne copie les coefs que si y a des lignes à copier... - { - $coef = new TNomenclatureCoefObject; - $TCoef = $coef->loadCoefObject($PDOdb, $object, $element, $object->id); - if (!empty($TCoef)) - { - $sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.'nomenclature_coef_object - WHERE fk_object = '.(int)$new_object->id.' + $fk_target = GETPOST('fk_element_split', 'int'); + if ($fk_target > 0) { + $new_object = new $classname($db); + $new_object->fetch($fk_target); + + // copie des coefs de la propal source si la propal de destination en est dépourvu + + if (!empty($conf->nomenclature) && isModEnabled('nomenclature') && in_array($element, array('propal', 'commande'))) { + dol_include_once('/nomenclature/class/nomenclature.class.php'); + $PDOdb = new TPDOdb; + + if (!empty($TMoveLine)) { // on ne copie les coefs que si y a des lignes à copier... + $coef = new TNomenclatureCoefObject; + $TCoef = $coef->loadCoefObject($PDOdb, $object, $element, $object->id); + if (!empty($TCoef)) { + $sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.'nomenclature_coef_object + WHERE fk_object = '.(int) $new_object->id.' AND type_object = "'.$element.'"'; - $PDOdb->Execute($sql); - $TRes = $PDOdb->Get_All(); - - if (empty($TRes)) // la nomenclature cible n'a pas de coef, on copie les coef de la source - { - foreach ($TCoef as $label => $coef) - { - $coef->fk_object = $new_object->id; - $coef->{OBJETSTD_MASTERKEY} = 0; // le champ id est toujours def - $coef->{OBJETSTD_DATECREATE}=time(); // ces champs dates aussi - $coef->{OBJETSTD_DATEUPDATE}=time(); -// var_dump($new_object->id, $coef); exit; - $coef->save($PDOdb); - } + $PDOdb->Execute($sql); + $TRes = $PDOdb->Get_All(); + + if (empty($TRes)) { // la nomenclature cible n'a pas de coef, on copie les coef de la source + foreach ($TCoef as $label => $coef) { + $coef->fk_object = $new_object->id; + $coef->{OBJETSTD_MASTERKEY} = 0; // le champ id est toujours def + $coef->{OBJETSTD_DATECREATE}=time(); // ces champs dates aussi + $coef->{OBJETSTD_DATEUPDATE}=time(); + // var_dump($new_object->id, $coef); exit; + $coef->save($PDOdb); } } } - } + } - foreach ($TMoveLine as $k => $line) - { - $line = $object->lines[$k]; + foreach ($TMoveLine as $k => $line) { + $line = $object->lines[$k]; - $id_origin_line = $line->id; - if($object->element == 'propal') { - /** @var Propal $new_object */ - $newLineId = $new_object->addline($line->desc, $line->subprice, $line->qty, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, $line->fk_product, $line->remise_percent, 'HT', 0, 0, $line->product_type, -1, $line->special_code, 0, 0, $line->pa_ht, $line->label, $line->date_start, $line->date_end, $line->array_options, $line->fk_unit, '', 0, 0, $line->fk_remise_except); - } - elseif($object->element == 'commande') { - /** @var Commande $new_object */ - $newLineId = $new_object->addline($line->desc, $line->subprice, $line->qty, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, $line->fk_product, $line->remise_percent, 0, $line->fk_remise_except, 'HT', 0, $line->date_start, $line->date_end, $line->product_type, -1, $line->special_code, 0, 0, $line->pa_ht, $line->label, $line->array_options, $line->fk_unit); - } - else { - /** @var Facture $new_object */ - $newLineId = $new_object->addline($line->desc, $line->subprice, $line->qty, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, $line->fk_product, $line->remise_percent, $line->date_start, $line->date_end, 0, 0, $line->fk_remise_except, 'HT', 0, $line->product_type, -1, $line->special_code, 0, 0, 0, 0, $line->pa_ht, $line->label, $line->array_options, 100, 0, $line->fk_unit); - } + $id_origin_line = $line->id; + if ($object->element == 'propal') { + /** @var Propal $new_object */ + $newLineId = $new_object->addline($line->desc, $line->subprice, $line->qty, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, $line->fk_product, $line->remise_percent, 'HT', 0, 0, $line->product_type, -1, $line->special_code, 0, 0, $line->pa_ht, $line->label, $line->date_start, $line->date_end, $line->array_options, $line->fk_unit, '', 0, 0, $line->fk_remise_except); + } elseif ($object->element == 'commande') { + /** @var Commande $new_object */ + $newLineId = $new_object->addline($line->desc, $line->subprice, $line->qty, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, $line->fk_product, $line->remise_percent, 0, $line->fk_remise_except, 'HT', 0, $line->date_start, $line->date_end, $line->product_type, -1, $line->special_code, 0, 0, $line->pa_ht, $line->label, $line->array_options, $line->fk_unit); + } else { + /** @var Facture $new_object */ + $newLineId = $new_object->addline($line->desc, $line->subprice, $line->qty, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, $line->fk_product, $line->remise_percent, $line->date_start, $line->date_end, 0, 0, $line->fk_remise_except, 'HT', 0, $line->product_type, -1, $line->special_code, 0, 0, 0, 0, $line->pa_ht, $line->label, $line->array_options, 100, 0, $line->fk_unit); + } - if ($newLineId < 0) { - dol_syslog('splitLines::error - addline. NEW line id : ' . $newLineId, LOG_ERR); - } + if ($newLineId < 0) { + dol_syslog('splitLines::error - addline. NEW line id : ' . $newLineId, LOG_ERR); + } - if(!empty($conf->nomenclature) && isModEnabled('nomenclature') && in_array($element, array('propal', 'commande'))) { - // nomenclature de la ligne source - $n = new TNomenclature; - $n->loadByObjectId($PDOdb, $line->id, $element, true, $line->fk_product, $line->qty, $object->id); + if (!empty($conf->nomenclature) && isModEnabled('nomenclature') && in_array($element, array('propal', 'commande'))) { + // nomenclature de la ligne source + $n = new TNomenclature; + $n->loadByObjectId($PDOdb, $line->id, $element, true, $line->fk_product, $line->qty, $object->id); - if($n->rowid == 0 && (count($n->TNomenclatureDet) + count($n->TNomenclatureWorkstation)) > 0) { - // Le cas d'une nomenclature non chargée : ça ne sert à rien de copier la Nomenclature... - continue; - } + if ($n->rowid == 0 && (count($n->TNomenclatureDet) + count($n->TNomenclatureWorkstation)) > 0) { + // Le cas d'une nomenclature non chargée : ça ne sert à rien de copier la Nomenclature... + continue; } } } - else - { - - if ($object->element == 'commande' || $object->element == 'propal' ){ - $id_new = $object->createFromClone($user, (int)GETPOST('socid')); - }else{ - $id_new = $object->createFromClone($user, $object->id); - } - - - if ($id_new > 0) - { - $json->newObjectId = $id_new; - - $json->log[] = "création $id_new"; - $new_object = new $classname($db); - $new_object->fetch($id_new); - // var_dump($TMoveLine,$new_object->lines); - - foreach($new_object->lines as $k=>$line) { + } else { + if ($object->element == 'commande' || $object->element == 'propal' ) { + $id_new = $object->createFromClone($user, (int) GETPOST('socid')); + } else { + $id_new = $object->createFromClone($user, $object->id); + } - $lineid = empty($line->id) ? $line->rowid : $line->id; - if(!isset($TMoveLine[$k])) { - $json->log[] = "Suppresion ligne $k $lineid"; - if ($object->element == 'commande' ){ - // commande - $resDelete = $new_object->deleteline($user,$lineid); - } else { - //propal || facture - $resDelete = $new_object->deleteline($lineid); - } + if ($id_new > 0) { + $json->newObjectId = $id_new; - if($resDelete < 0) { - dol_syslog('splitLines::error - deleteline. RES delete : ' . $resDelete, LOG_ERR); - } + $json->log[] = "création $id_new"; + $new_object = new $classname($db); + $new_object->fetch($id_new); + // var_dump($TMoveLine,$new_object->lines); + + foreach ($new_object->lines as $k=>$line) { + $lineid = empty($line->id) ? $line->rowid : $line->id; + + if (!isset($TMoveLine[$k])) { + $json->log[] = "Suppresion ligne $k $lineid"; + if ($object->element == 'commande' ) { + // commande + $resDelete = $new_object->deleteline($user, $lineid); + } else { + //propal || facture + $resDelete = $new_object->deleteline($lineid); } - else{ - $json->log[] = "ok $k $lineid"; + + if ($resDelete < 0) { + dol_syslog('splitLines::error - deleteline. RES delete : ' . $resDelete, LOG_ERR); } + } else { + $json->log[] = "ok $k $lineid"; } - } else { - dol_syslog('splitLines::error - createFromClone. ID New : ' . $id_new, LOG_ERR); } + } else { + dol_syslog('splitLines::error - createFromClone. ID New : ' . $id_new, LOG_ERR); } + } - if($entity!=$conf->entity) { - $db->query("UPDATE ".MAIN_DB_PREFIX.$new_object->table_element." SET entity=".$entity." WHERE rowid=".$new_object->id ); - } - - $linkToDocument = ''; - if (!empty($new_object) && is_object($new_object) && method_exists($new_object, 'getNomUrl')){ - $linkToDocument = $new_object->getNomUrl(); - } + if ($entity!=$conf->entity) { + $db->query("UPDATE ".MAIN_DB_PREFIX.$new_object->table_element." SET entity=".$entity." WHERE rowid=".$new_object->id); + } - if (!empty($linkToDocument)) $linkToDocument = '- '.$linkToDocument; - - if ($action == 'split') { - // coté js il y a une redirection, mais avec CSRF CHECK l'ancienne redirection avec get ne marche plus - $json->msg = $langs->trans('SplitOk'); // dû a un bug de l'espace avec les set event messages j'ai découpé mais il faut normalement utilisé $langs->trans('SplitOk', $linkToDocument) - setEventMessage($langs->trans('SplitOk')); - setEventMessage($linkToDocument); - } elseif ($action == 'copy') { - // coté js il y a une redirection, mais avec CSRF CHECK l'ancienne redirection avec get ne marche plus - $json->msg = $langs->trans('SplitCopyOk'); // dû a un bug de l'espace avec les set event messages j'ai découpé mais il faut normalement utilisé $langs->trans('SplitCopyOk', $linkToDocument) - setEventMessage($json->msg); - setEventMessage($linkToDocument); - } + $linkToDocument = ''; + if (!empty($new_object) && is_object($new_object) && method_exists($new_object, 'getNomUrl')) { + $linkToDocument = $new_object->getNomUrl(); } - if ($action == 'split' || $action == 'delete' ) - { - $errors = 0; - foreach($object->lines as $k=>$line) { - $lineid = empty($line->id) ? $line->rowid : $line->id; - if(isset($TMoveLine[$k])) { - $json->log[] = "Suppresion ligne old $lineid"; - - if ($object->element == 'commande' ){ - // commande - $resDel = $object->deleteline($user,$lineid); - } else { - //propal || facture - $resDel = $object->deleteline($lineid); - } + if (!empty($linkToDocument)) $linkToDocument = '- '.$linkToDocument; + + if ($action == 'split') { + // coté js il y a une redirection, mais avec CSRF CHECK l'ancienne redirection avec get ne marche plus + $json->msg = $langs->trans('SplitOk'); // dû a un bug de l'espace avec les set event messages j'ai découpé mais il faut normalement utilisé $langs->trans('SplitOk', $linkToDocument) + setEventMessage($langs->trans('SplitOk')); + setEventMessage($linkToDocument); + } elseif ($action == 'copy') { + // coté js il y a une redirection, mais avec CSRF CHECK l'ancienne redirection avec get ne marche plus + $json->msg = $langs->trans('SplitCopyOk'); // dû a un bug de l'espace avec les set event messages j'ai découpé mais il faut normalement utilisé $langs->trans('SplitCopyOk', $linkToDocument) + setEventMessage($json->msg); + setEventMessage($linkToDocument); + } +} + +if ($action == 'split' || $action == 'delete' ) { + $errors = 0; + foreach ($object->lines as $k=>$line) { + $lineid = empty($line->id) ? $line->rowid : $line->id; + if (isset($TMoveLine[$k])) { + $json->log[] = "Suppresion ligne old $lineid"; + + if ($object->element == 'commande' ) { + // commande + $resDel = $object->deleteline($user, $lineid); + } else { + //propal || facture + $resDel = $object->deleteline($lineid); + } - if ($resDel < 0) { - $errors++; - dol_syslog('splitLines::error - split or delete. Res delete : ' . $resDelete, LOG_ERR); - } + if ($resDel < 0) { + $errors++; + dol_syslog('splitLines::error - split or delete. Res delete : ' . $resDelete, LOG_ERR); } } + } - if(empty($errors) && $action == 'delete' ) { - // coté js il y a une redirection, mais avec CSRF CHECK l'ancienne redirection avec get ne marche plus - $json->msg = $langs->trans('SplitDeleteOk'); - setEventMessage($json->msg); - } + if (empty($errors) && $action == 'delete' ) { + // coté js il y a une redirection, mais avec CSRF CHECK l'ancienne redirection avec get ne marche plus + $json->msg = $langs->trans('SplitDeleteOk'); + setEventMessage($json->msg); } +} print json_encode($json);