diff --git a/ChangeLog b/ChangeLog index 0f568ff5359..030650d5863 100644 --- a/ChangeLog +++ b/ChangeLog @@ -15,7 +15,7 @@ NEW: Add option to disable globaly some notifications emails. NEW: #18326 Workflow: Close order on shipment closing. NEW: #18401 Add __NEWREF__ subtitute to get new object reference. NEW: #18403 Add __URL_SHIPMENT__ substitute to get the URL of a shipment -NEW: #18689 REST API module: add api key generate / modify right. +NEW: #18689 REST API module: add api key generate / modify pemrission. NEW: #18663 Make "L'Annuaire des Entreprises" the default provider for SIREN verification for French thirdparties. NEW: #18046 Add tags on ticket/categories NEW: #18326 Workflow: Close order on shipment closing. @@ -123,6 +123,7 @@ NEW: Introduce method hasRight NEW: Can use textarea field into a confirm popup. NEW: Can use the result_mode of mysqli driver. Save memory for list count NEW: #18319 REST API - Shipment: Add 'close' action / endpoint / POST method. +NEW: Add API /approve and /makeOrder for purchase orders. NEW: add action trigger for member excluded NEW: add option MAIN_IBAN_IS_NEVER_MANDATORY, MAIN_IBAN_NOT_MANDATORY, PROPAL_NOT_BILLABLE, PROPAL_REOPEN_UNSIGNED_ONLY, PROPOSAL_ARE_NOT_BILLABLE, TICKETS_MESSAGE_FORCE_MAIL NEW: Add code codebar column on serial/lot structure diff --git a/htdocs/admin/dolistore/css/dolistore.css b/htdocs/admin/dolistore/css/dolistore.css index fb4dc2d5bb1..3d8f163caf2 100644 --- a/htdocs/admin/dolistore/css/dolistore.css +++ b/htdocs/admin/dolistore/css/dolistore.css @@ -1,6 +1,5 @@ div.divsearchfield { - float: left; margin: 4px 12px 4px 2px; padding-left: 2px; } diff --git a/htdocs/admin/modules.php b/htdocs/admin/modules.php index f720e411717..29d2335a0f9 100644 --- a/htdocs/admin/modules.php +++ b/htdocs/admin/modules.php @@ -535,11 +535,11 @@ if ($mode == 'common' || $mode == 'commonkanban') { //$moreforfilter .= '
'.$moreinfo.' '.$moreinfo2.'
'; - $moreforfilter .= '
'; - $moreforfilter .= '
'; + $moreforfilter .= '
'; + $moreforfilter .= '
'; $moreforfilter .= img_picto($langs->trans("Filter"), 'filter', 'class="paddingright opacityhigh hideonsmartphone"').''; $moreforfilter .= '
'; - $moreforfilter .= '
'; + $moreforfilter .= '
'; $moreforfilter .= $form->selectarray('search_nature', $arrayofnatures, dol_escape_htmltag($search_nature), $langs->trans('Origin'), 0, 0, '', 0, 0, 0, '', 'maxwidth250', 1); $moreforfilter .= '
'; if (getDolGlobalInt('MAIN_FEATURES_LEVEL')) { @@ -553,16 +553,16 @@ if ($mode == 'common' || $mode == 'commonkanban') { if ($conf->global->MAIN_FEATURES_LEVEL > 1) { $array_version['development'] = $langs->trans("Development"); } - $moreforfilter .= '
'; + $moreforfilter .= '
'; $moreforfilter .= $form->selectarray('search_version', $array_version, $search_version, $langs->trans('Version'), 0, 0, '', 0, 0, 0, '', 'maxwidth150', 1); $moreforfilter .= '
'; } - $moreforfilter .= '
'; + $moreforfilter .= '
'; $moreforfilter .= $form->selectarray('search_status', array('active'=>$langs->transnoentitiesnoconv("Enabled"), 'disabled'=>$langs->transnoentitiesnoconv("Disabled")), $search_status, $langs->trans('Status'), 0, 0, '', 0, 0, 0, '', 'maxwidth150', 1); $moreforfilter .= '
'; $moreforfilter .= ' '; - $moreforfilter .= '
'; - $moreforfilter .= ''; + $moreforfilter .= '
'; + $moreforfilter .= ''; if ($search_keyword || ($search_nature && $search_nature != '-1') || ($search_version && $search_version != '-1') || ($search_status && $search_status != '-1')) { $moreforfilter .= ' '; $moreforfilter .= ''; @@ -1139,7 +1139,7 @@ if ($mode == 'deploy') { } if ($allowfromweb == 1) { - print $langs->trans("UnpackPackageInModulesRoot", $dirins).'
'; + print ''.$langs->trans("UnpackPackageInModulesRoot", $dirins).'
'; print '
'; diff --git a/htdocs/api/class/api_documents.class.php b/htdocs/api/class/api_documents.class.php index 3cd8d3e0774..28dbef6887e 100644 --- a/htdocs/api/class/api_documents.class.php +++ b/htdocs/api/class/api_documents.class.php @@ -624,6 +624,9 @@ class Documents extends DolibarrApi } elseif ($modulepart == 'expensereport') { require_once DOL_DOCUMENT_ROOT.'/expensereport/class/expensereport.class.php'; $object = new ExpenseReport($this->db); + } elseif ($modulepart == 'fichinter') { + require_once DOL_DOCUMENT_ROOT.'/fichinter/class/fichinter.class.php'; + $object = new Fichinter($this->db); } elseif ($modulepart == 'adherent' || $modulepart == 'member') { $modulepart = 'adherent'; require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php'; diff --git a/htdocs/core/class/html.formticket.class.php b/htdocs/core/class/html.formticket.class.php index 81912207f57..6866bfa8356 100644 --- a/htdocs/core/class/html.formticket.class.php +++ b/htdocs/core/class/html.formticket.class.php @@ -1261,7 +1261,7 @@ class FormTicket // Zone to select its email template if (count($modelmail_array) > 0) { print '
'."\n"; - print $langs->trans('SelectMailModel').': '.$formmail->selectarray('modelmailselected', $modelmail_array, $this->param['models_id'], 1); + print $langs->trans('SelectMailModel').': '.$formmail->selectarray('modelmailselected', $modelmail_array, $this->param['models_id'], 1, 0, "", "", 0, 0, 0, '', 'minwidth200'); if ($user->admin) { print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1); } diff --git a/htdocs/fourn/class/api_supplier_orders.class.php b/htdocs/fourn/class/api_supplier_orders.class.php index 01c0079171e..e21eb3436dc 100644 --- a/htdocs/fourn/class/api_supplier_orders.class.php +++ b/htdocs/fourn/class/api_supplier_orders.class.php @@ -369,6 +369,189 @@ class SupplierOrders extends DolibarrApi ); } + /** + * Approve an order + * + * @param int $id Order ID + * @param int $idwarehouse Warehouse ID + * @param int $secondlevel 1=Does not execute triggers, 0= execute triggers + * + * @url POST {id}/approve + * + * @return array + * FIXME An error 403 is returned if the request has an empty body. + * Error message: "Forbidden: Content type `text/plain` is not supported." + * Workaround: send this in the body + * { + * "idwarehouse": 0, + * "secondlevel": 0 + * } + */ + public function approve($id, $idwarehouse = 0, $secondlevel = 0) + { + if (empty(DolibarrApiAccess::$user->rights->fournisseur->commande->creer) && empty(DolibarrApiAccess::$user->rights->supplier_order->creer)) { + throw new RestException(401); + } + $result = $this->order->fetch($id); + if (!$result) { + throw new RestException(404, 'Order not found'); + } + + if (!DolibarrApi::_checkAccessToResource('fournisseur', $this->order->id, 'commande_fournisseur', 'commande')) { + throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login); + } + + $result = $this->order->approve(DolibarrApiAccess::$user, $idwarehouse, $secondlevel); + if ($result == 0) { + throw new RestException(304, 'Error nothing done. May be object is already approved'); + } + if ($result < 0) { + throw new RestException(500, 'Error when approve Order: '.$this->order->error); + } + + return array( + 'success' => array( + 'code' => 200, + 'message' => 'Order approved (Ref='.$this->order->ref.')' + ) + ); + } + + + /** + * Sends an order to the vendor + * + * @param int $id Order ID + * @param integer $date Date (unix timestamp in sec) + * @param int $method Method + * @param string $comment Comment + * + * @url POST {id}/makeorder + * + * @return array + * FIXME An error 403 is returned if the request has an empty body. + * Error message: "Forbidden: Content type `text/plain` is not supported." + * Workaround: send this in the body + * { + * "date": 0, + * "method": 0, + * "comment": "" + * } + */ + public function makeOrder($id, $date, $method, $comment = '') + { + if (empty(DolibarrApiAccess::$user->rights->fournisseur->commande->creer) && empty(DolibarrApiAccess::$user->rights->supplier_order->creer)) { + throw new RestException(401); + } + $result = $this->order->fetch($id); + if (!$result) { + throw new RestException(404, 'Order not found'); + } + + if (!DolibarrApi::_checkAccessToResource('fournisseur', $this->order->id, 'commande_fournisseur', 'commande')) { + throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login); + } + + $result = $this->order->commande(DolibarrApiAccess::$user, $date, $method, $comment); + if ($result == 0) { + throw new RestException(304, 'Error nothing done. May be object is already sent'); + } + if ($result < 0) { + throw new RestException(500, 'Error when sending Order: '.$this->order->error); + } + + return array( + 'success' => array( + 'code' => 200, + 'message' => 'Order sent (Ref='.$this->order->ref.')' + ) + ); + } + + /** + * Receives the order, dispatches products. + * + * Example: + * { + * "closeopenorder": 1, + * "comment": "", + * "lines": [{ + * "id": 14, + * "fk_product": 112, + * "qty": 18, + * "warehouse": 1, + * "price": 114, + * "comment": "", + * "eatby": 0, + * "sellby": 0, + * "batch": 0, + * "notrigger": 0 + * }] + * } + * + * @param int $id Order ID + * @param integer $closeopenorder Close order if everything is received {@required false} + * @param string $comment Comment {@required false} + * @param array $lines Array of product dispatches + * + * @url POST {id}/receive + * + * @return array + * FIXME An error 403 is returned if the request has an empty body. + * Error message: "Forbidden: Content type `text/plain` is not supported." + * + */ + public function receiveOrder($id, $closeopenorder, $comment, $lines) + { + if (empty(DolibarrApiAccess::$user->rights->fournisseur->commande->creer) && empty(DolibarrApiAccess::$user->rights->supplier_order->creer)) { + throw new RestException(401); + } + $result = $this->order->fetch($id); + if (!$result) { + throw new RestException(404, 'Order not found'); + } + + if (!DolibarrApi::_checkAccessToResource('fournisseur', $this->order->id, 'commande_fournisseur', 'commande')) { + throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login); + } + + foreach ($lines as $line) { + $lineObj =(object) $line; + + $result=$this->order->dispatchProduct(DolibarrApiAccess::$user, + $lineObj->fk_product, + $lineObj->qty, + $lineObj->warehouse, + $lineObj->price, + $lineObj->comment, + $lineObj->eatby, + $lineObj->sellby, + $lineObj->batch, + $lineObj->id, + $lineObj->notrigger); + + if ($result < 0) { + throw new RestException(500, 'Error dispatch order line '.$line->id.': '.$this->order->error); + } + } + + $result = $this->order->calcAndSetStatusDispatch(DolibarrApiAccess::$user, $closeopenorder, $comment); + + if ($result == 0) { + throw new RestException(304, 'Error nothing done. May be object is already dispatched'); + } + if ($result < 0) { + throw new RestException(500, 'Error when receivce order: '.$this->order->error); + } + + return array( + 'success' => array( + 'code' => 200, + 'message' => 'Order received (Ref='.$this->order->ref.')' + ) + ); + } + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore /** * Clean sensible object datas diff --git a/htdocs/langs/en_US/admin.lang b/htdocs/langs/en_US/admin.lang index e8a64b3f72b..47daec0851b 100644 --- a/htdocs/langs/en_US/admin.lang +++ b/htdocs/langs/en_US/admin.lang @@ -343,7 +343,7 @@ StepNb=Step %s FindPackageFromWebSite=Find a package that provides the features you need (for example on the official web site %s). DownloadPackageFromWebSite=Download package (for example from the official web site %s). UnpackPackageInDolibarrRoot=Unpack/unzip the packaged files into your Dolibarr server directory: %s -UnpackPackageInModulesRoot=To deploy/install an external module, unpack/unzip the packaged files into the server directory dedicated to external modules:
%s +UnpackPackageInModulesRoot=To deploy/install an external module, you must unpack/unzip the archive file into the server directory dedicated to external modules:
%s SetupIsReadyForUse=Module deployment is finished. You must however enable and setup the module in your application by going to the page setup modules: %s. NotExistsDirect=The alternative root directory is not defined to an existing directory.
InfDirAlt=Since version 3, it is possible to define an alternative root directory. This allows you to store, into a dedicated directory, plug-ins and custom templates.
Just create a directory at the root of Dolibarr (eg: custom).
diff --git a/htdocs/theme/eldy/dropdown.inc.php b/htdocs/theme/eldy/dropdown.inc.php index 81734c817b8..5c62a703f67 100644 --- a/htdocs/theme/eldy/dropdown.inc.php +++ b/htdocs/theme/eldy/dropdown.inc.php @@ -7,6 +7,11 @@ if (!defined('ISLOADEDBYSTEELSHEET')) { * Dropdown of user popup */ +.bookmark-footer a.top-menu-dropdown-link { + white-space: normal; + word-break: break-word; +} + button.dropdown-item.global-search-item { outline: none; } @@ -15,6 +20,12 @@ button.dropdown-item.global-search-item { display: block; } +#topmenu-bookmark-dropdown .dropdown-menu { + min-width: 300px; + max-width: 360px; +} + + .dropdown-search { border-color: #eee; diff --git a/htdocs/theme/eldy/global.inc.php b/htdocs/theme/eldy/global.inc.php index a830002d197..deb7cdadf9e 100644 --- a/htdocs/theme/eldy/global.inc.php +++ b/htdocs/theme/eldy/global.inc.php @@ -261,7 +261,7 @@ input:invalid, select:invalid, input.--error , select.--error { div.tabBar textarea:focus { border: 1px solid #aaa !important; } -input:focus:not(.select2-search__field), select:focus, .select2-container--open .select2-selection--single { +input:focus:not(.button):not(.select2-search__field):not(#top-bookmark-search-input), select:focus, .select2-container--open .select2-selection--single { /* div.tabBar input:focus, div.tabBar select:focus { */ border-bottom: 1px solid #666 !important; border-bottom-left-radius: 0 !important; @@ -985,12 +985,18 @@ span.fa.fa-plus-circle.paddingleft { padding-: 20px; } div.divsearchfield { - float: ; + /* float: ; */ + display: inline-block; margin-: 12px; margin-: 2px; margin-top: 4px; margin-bottom: 4px; - padding-left: 2px; + padding-left: 2px; +} +.divfilteralone { + background-color: rgba(0, 0, 0, 0.08); + border-radius: 5px; + padding-left: 5px; } .divsearchfieldfilter { text-overflow: clip; @@ -2808,8 +2814,8 @@ div.login a:hover { div.login_block_user { display: inline-block; vertical-align: middle; - line-height: px; - height: px; + line-height: px; + height: px; } div.login_block_other { display: inline-block; @@ -3396,6 +3402,7 @@ input.buttonreset { padding: 8px 15px; text-decoration: underline; color: var(--colortextlink); + background-color: transparent; cursor: pointer; } .nopaddingleft { diff --git a/htdocs/theme/md/dropdown.inc.php b/htdocs/theme/md/dropdown.inc.php index 1aec654410f..ff7285a2772 100644 --- a/htdocs/theme/md/dropdown.inc.php +++ b/htdocs/theme/md/dropdown.inc.php @@ -2,7 +2,6 @@ if (!defined('ISLOADEDBYSTEELSHEET')) { die('Must be call by steelsheet'); } ?> - /*