* Copyright (C) 2013-2026 Alexandre Spangaro * Copyright (C) 2014-2015 Florian Henry * Copyright (C) 2019 Frédéric France * Copyright (C) 2024-2026 MDW * * 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 . */ /** * \file htdocs/accountancy/admin/categories.php * \ingroup Accountancy (Double entries) * \brief Page to assign mass categories to accounts */ // Load Dolibarr environment require '../../main.inc.php'; /** * @var Conf $conf * @var DoliDB $db * @var HookManager $hookmanager * @var Translate $langs * @var User $user */ require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php'; require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountancycategory.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/html.formaccounting.class.php'; $error = 0; // Load translation files required by the page $langs->loadLangs(array("accountancy", "admin", "bills", "compta")); $action = GETPOST('action', 'aZ09'); $confirm = GETPOST('confirm', 'alpha'); $id = GETPOSTINT('id'); $catid = GETPOSTINT('catid'); // Alternative parameter name for consistency if (empty($id) && !empty($catid)) { $id = $catid; } $account_id = GETPOSTINT('account_id'); $cpt_id = GETPOSTINT('cpt'); // For compatibility with old system // Security check if (!$user->hasRight('accounting', 'chartofaccount')) { accessforbidden(); } // Initialize objects $accountingcategory = new AccountancyCategory($db); $form = new Form($db); $formaccounting = new FormAccounting($db); // Load category if ($id > 0) { $result = $accountingcategory->fetch($id); if ($result < 0) { setEventMessages($accountingcategory->error, $accountingcategory->errors, 'errors'); } } // Parameters for list (new system only) $limit = GETPOSTINT('limit') ? GETPOSTINT('limit') : $conf->liste_limit; $sortfield = GETPOST('sortfield', 'aZ09comma'); $sortorder = GETPOST('sortorder', 'aZ09comma'); $page = GETPOSTISSET('pageplusone') ? (GETPOSTINT('pageplusone') - 1) : GETPOSTINT("page"); if (empty($page) || $page < 0 || GETPOST('button_search', 'alpha') || GETPOST('button_removefilter', 'alpha')) { $page = 0; } $offset = $limit * $page; // Search filters (new system only) $search_account = GETPOST('search_account', 'alpha'); $search_label = GETPOST('search_label', 'alpha'); if (empty($sortfield)) { $sortfield = "aa.account_number"; } if (empty($sortorder)) { $sortorder = "ASC"; } // Use new multi report system $useNewSystem = (getDolGlobalInt('ACCOUNTING_ENABLE_MULTI_REPORT')); /* * Actions */ if ($useNewSystem) { // Remove filter if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x', 'alpha') || GETPOST('button_removefilter', 'alpha')) { $search_account = ''; $search_label = ''; } // Add account to category if ($action == 'add_account' && !empty($account_id)) { $result = $accountingcategory->addAccountToCategory($account_id); if ($result > 0) { setEventMessages($langs->trans("AccountAddedToCategory"), null, 'mesgs'); header("Location: ".$_SERVER["PHP_SELF"]."?id=".$id); exit; } elseif ($result == 0) { setEventMessages($langs->trans("AccountAlreadyInCategory"), null, 'warnings'); } else { setEventMessages($accountingcategory->error, $accountingcategory->errors, 'errors'); } } // Remove account from category if ($action == 'confirm_delete' && $confirm == 'yes' && !empty($account_id)) { $result = $accountingcategory->deleteAccountFromCategory($account_id); if ($result > 0) { setEventMessages($langs->trans("AccountRemovedFromCategory"), null, 'mesgs'); header("Location: ".$_SERVER["PHP_SELF"]."?id=".$id); exit; } else { setEventMessages($accountingcategory->error, $accountingcategory->errors, 'errors'); } } // Add multiple accounts if ($action == 'add_multiple' && GETPOST('accounts_to_add', 'array')) { $accounts_to_add = GETPOST('accounts_to_add', 'array'); $result = $accountingcategory->addMultipleAccountsToCategory($accounts_to_add); if ($result > 0) { setEventMessages($langs->trans("XAccountsAdded", $result), null, 'mesgs'); header("Location: ".$_SERVER["PHP_SELF"]."?id=".$id); exit; } elseif ($result < 0) { setEventMessages($accountingcategory->error, $accountingcategory->errors, 'errors'); } else { setEventMessages($langs->trans("NoAccountAdded"), null, 'warnings'); } } // Remove all accounts if ($action == 'confirm_remove_all' && $confirm == 'yes') { $result = $accountingcategory->removeAllAccountsFromCategory(); if ($result >= 0) { setEventMessages($langs->trans("XAccountsRemoved", $result), null, 'mesgs'); header("Location: ".$_SERVER["PHP_SELF"]."?id=".$id); exit; } else { setEventMessages($accountingcategory->error, $accountingcategory->errors, 'errors'); } } } else { // OLD SYSTEM: One-to-many via fk_accounting_category field if ($action == 'clean') { $result = $accountingcategory->deleteCptCat($cpt_id); if ($result >= 0) { header("Location: ".$_SERVER['PHP_SELF']."?id=".$id); exit; } else { setEventMessages($accountingcategory->error, $accountingcategory->errors, 'errors'); } } } /* * View */ $help_url = 'EN:Module_Double_Entry_Accounting#Setup|FR:Module_Comptabilité_en_Partie_Double#Configuration'; llxHeader('', $langs->trans("AccountingCategory"), $help_url, '', 0, 0, '', '', '', 'mod-accountancy page-admin-categories'); if ($useNewSystem) { // ============================================================================ // NEW SYSTEM VIEW // ============================================================================ // Confirmation dialogs if ($action == 'delete') { print $form->formconfirm( $_SERVER["PHP_SELF"].'?id='.$id.'&account_id='.$account_id, $langs->trans('RemoveAccountFromCategory'), $langs->trans('ConfirmRemoveAccountFromCategory'), 'confirm_delete', '', 0, 1 ); } if ($action == 'remove_all') { print $form->formconfirm( $_SERVER["PHP_SELF"].'?id='.$id, $langs->trans('RemoveAllAccountsFromCategory'), $langs->trans('ConfirmRemoveAllAccountsFromCategory'), 'confirm_remove_all', '', 0, 1 ); } // Page title $title = $langs->trans("AccountingCategory").': '.$accountingcategory->label; print load_fiche_titre($title, '', 'title_accountancy'); // Category information card print dol_get_fiche_head(); print ''; print ''; print ''; if (!empty($accountingcategory->range_account)) { print ''; } print ''; print '
'.$langs->trans("Code").''.$accountingcategory->code.'
'.$langs->trans("Label").''.$accountingcategory->label.'
'.$langs->trans("Comment").''.$accountingcategory->range_account.'
'.$langs->trans("AccountsLinked").''.$accountingcategory->countAccountsInCategory().'
'; print dol_get_fiche_end(); // Build SQL for linked accounts $sql = "SELECT aa.rowid, aa.account_number, aa.label"; $sql .= " FROM ".MAIN_DB_PREFIX."accounting_category_account as aca"; $sql .= " INNER JOIN ".MAIN_DB_PREFIX."accounting_account as aa ON aa.rowid = aca.fk_accounting_account"; $sql .= " WHERE aca.fk_accounting_category = ".((int) $id); $sql .= " AND aa.entity = ".$conf->entity; // Search filters if (!empty($search_account)) { $sql .= natural_search("aa.account_number", $search_account); } if (!empty($search_label)) { $sql .= natural_search("aa.label", $search_label); } // Count total $nbtotalofrecords = ''; if (!getDolGlobalInt('MAIN_DISABLE_FULL_SCANLIST')) { $resql = $db->query($sql); $nbtotalofrecords = $db->num_rows($resql); if (($page * $limit) > $nbtotalofrecords) { $page = 0; $offset = 0; } } // Add sort and limit $sql .= $db->order($sortfield, $sortorder); $sql .= $db->plimit($limit + 1, $offset); $resql = $db->query($sql); if (!$resql) { dol_print_error($db); exit; } $num = $db->num_rows($resql); // Parameters $param = '&id='.$id; if (!empty($search_account)) { $param .= '&search_account='.urlencode($search_account); } if (!empty($search_label)) { $param .= '&search_label='.urlencode($search_label); } // Form to add accounts print '
'; print ''; print ''; print ''; // Get available accounts $availableAccounts = $accountingcategory->getAvailableAccountsForCategory(); if (count($availableAccounts) > 0) { print ''; print ''; print '
'.$langs->trans("AddAccountsToCategory").": ".''; // Prepare array for multiselectarray $arrayofaccounts = array(); foreach ($availableAccounts as $account) { $arrayofaccounts[$account['id']] = $account['account_number'].' - '.$account['label']; } // Use Dolibarr native multiselect with search print $form->multiselectarray('accounts_to_add', $arrayofaccounts, array(), 0, 0, '', 0, '100%', '', '', $langs->trans("SelectAccountsToAdd")); print '
'; print '
'; print ''; print '   '; print ''; print '
'; } else { print '
'.$langs->trans("AllAccountsAlreadyLinked").'
'; } print '
'; // List of linked accounts print '
'; print ''; print ''; print ''; print ''; $massactionbutton = ''; $title = $langs->trans("AccountsLinked"); print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'accountancy', 0, '', '', $limit); print '
'; print ''; // Search filters row print ''; print ''; print ''; print ''; print ''; // Header row print ''; print_liste_field_titre("AccountNumber", $_SERVER["PHP_SELF"], "aa.account_number", "", $param, "", $sortfield, $sortorder); print_liste_field_titre("Label", $_SERVER["PHP_SELF"], "aa.label", "", $param, "", $sortfield, $sortorder); print_liste_field_titre("", $_SERVER["PHP_SELF"], "", "", $param, '', $sortfield, $sortorder, 'center '); print ''; // Data rows $i = 0; while ($i < min($num, $limit)) { $obj = $db->fetch_object($resql); print ''; // Account number print ''; // Label print ''; // Actions print ''; print ''; $i++; } if ($num == 0) { print ''; } print '
'; $searchpicto = $form->showFilterAndCheckAddButtons(0); print $searchpicto; print '
'; print ''; print $obj->account_number; print ''; print ''; print dol_escape_htmltag($obj->label); print ''; print 'rowid.'" class="reposition">'; print img_delete(); print ''; print '
'.$langs->trans("NoAccountLinked").'
'; print '
'; print '
'; // Button to remove all if ($num > 0) { print ''; } } else { // ============================================================================ // OLD SYSTEM VIEW (compatibility) // ============================================================================ $listcpt = $accountingcategory->display($id); $listcptNotIn = $accountingcategory->getAccountsWithNoCategory($id); print load_fiche_titre($langs->trans('AccountingCategory')." : ".$accountingcategory->label); print '
'; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print "\n"; if (is_array($listcpt) && count($listcpt) > 0) { foreach ($listcpt as $cpt) { print ''; print ''; print ''; print '"; print "\n"; } } else { print ''; } print '
'.$langs->trans("AccountNumber").''.$langs->trans("Label").'
'.length_accountg($cpt->account_number).''.$cpt->label.''; print ''; print img_picto($langs->trans("DeleteFromCat"), 'unlink', 'class="paddingleft"'); print "
'.$langs->trans("NoRecordFound").'
'; print '
'; } // End of page llxFooter(); $db->close();