The product line view of the validated reception card used the product
master description (product->description) for the tooltip and the inline
description block, while the shipment card (expedition/card.php) and the
generic line template (core/tpl/objectline_view.tpl.php) use the document
line description (line->description).
Reception lines do carry their own description (copied from
commande_fournisseurdet.description in Reception::fetch_lines()), so the
card displayed the wrong text, or nothing at all when the line description
differed from the master one. Align reception/card.php on
expedition/card.php: use line->description, and compare the inline block
against the displayed label, like the generic template does. No default
behavior change (display still gated by PRODUIT_DESC_IN_FORM_ACCORDING_TO_DEVICE).
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Fix#38386 restore origin from db before reception line update handler
PR #36134 (standalone reception, commit dc4a28cd0b) changed the
$origin / $origin_id init at the top of htdocs/reception/card.php so
that an empty origin no longer falls back to 'reception' and the
origin_id fallbacks are gated on a non-empty $origin. The earlier block
that restored $origin from the loaded $object->origin (still on 22.0 at
line 136) was dropped at the same time, leaving the restoration only in
the render section that runs much later (around line 1953 on develop).
When the line edit form on an existing reception submits, it posts
action=updateline and id=N with no origin field, so $origin stays
empty when the action handler at line 762 is reached. The standalone
branch requires the global RECEPTION_STANDALONE to be set and the
origin branch requires $origin and $origin_id > 0, so a regular
reception falls through both branches and the line update (quantity,
comment, batch, extrafields) is silently dropped. Header data and
status changes still work because they use other handlers.
Re-introduce the post-fetch restoration right after
actions_fetchobject.inc.php so the action handler sees the same origin
context the page had when the form was rendered. The fix is minimal:
only restore when an id was loaded and the local variable is still
empty, so flows that genuinely carry an origin in the POST (the
create-from-supplier-order form) are untouched.
Reported by @AndiFoFFN on 23.0.3. Same regression on develop.
Signed-off-by: Dolicraft <contact@dolicraft.com>
* Fix#38386 address standalone-mode regression and refresh view after save
Address review feedback from @sonikf on PR #38558:
- Gate the post-fetch origin restore on $object->origin_id > 0 so a
standalone reception (origin_id = 0) is not affected. With the prior
version, Reception::fetch hydrated $object->origin to the literal
'commande' fallback (line 548 'For compatibility'), and the restore
block then assigned 'commande' to the local $origin, breaking the
standalone branch of the updateline handler.
- Prefer $object->origin_type over the deprecated $object->origin alias.
- Redirect after a successful updateline so the page leaves edit mode
and re-reads the saved values, matching the cancel path and the
header-data save handlers. Without it the view stayed on the edit
form even after the row was correctly written.
Signed-off-by: Dolicraft <contact@dolicraft.com>
* Fix#38386 drop redundant isset on origin_id to satisfy phpstan
CommonObject::$origin_id is declared @var int non-nullable, so the
isset() guard around the int comparison was redundant and phpstan
rejects 'Property in isset() is not nullable'. The > 0 test alone is
equivalent for the original intent (skip the restore for standalone
receptions, whose origin_id is 0).
Signed-off-by: Dolicraft <contact@dolicraft.com>
---------
Signed-off-by: Dolicraft <contact@dolicraft.com>
Co-authored-by: Dolicraft <contact@dolicraft.com>
Reception::fetch_lines_free(), addline(), update_line() and create() all
instantiate ReceptionLineBatch, but reception.class.php never includes its
receptionlinebatch.class.php file. It works in normal navigation only because
the entry pages (reception/card.php, ...) happen to require both classes
side-by-side.
Any code path that loads Reception through a generic helper - typically
fetchObjectByElement() from core/lib/functions.lib.php, called by hooks,
external modules, or some API/CLI tooling - triggers:
PHP Fatal error: Uncaught Error: Class "ReceptionLineBatch" not found
in htdocs/reception/class/reception.class.php on line 1230
Reproduced on production: index.php -> llxHeader -> main_area ->
HookManager::executeHooks -> ActionsUserNavHistory::printMainArea ->
UserNavHistory::fetchAll -> fetchObjectByElement() -> Reception::fetch() ->
Reception::fetch_lines_free() -> new ReceptionLineBatch() (fatal).
Adding the missing require_once at the top of reception.class.php makes the
class always available wherever Reception is loaded, consistent with how
other Dolibarr core classes pre-load their line/child classes.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When perweek.php / perday.php submit a filter form containing at least one
projet_task extrafield search input, the SQL produced by getTasksArray()
contains a phantom WHERE clause like:
AND (efpt.search_task_options_<field> LIKE '%-1%')
which triggers a DB_ERROR_NOSUCHFIELD.
Cause:
- perweek.php / perday.php build their search arrays via
$extrafields->getOptionalsFromPost('projet_task', '', 'search_task_')
which produces keys of the form 'search_task_options_<field>'.
- They also include /core/tpl/extrafields_list_search_sql.tpl.php with
$search_options_pattern = 'search_task_options_' (see e.g. perweek.php
lines 436 / 469 / 652), so the template's preg_replace correctly
strips the prefix and the resulting SQL column name is right.
- However, Task::getTasksArray() includes the same template at the end of
the SQL build but never sets $search_options_pattern. The template
then falls back to its default value 'search_options_'
(extrafields_list_search_sql.tpl.php line 48), which does not match
the actual key prefix. The preg_replace becomes a no-op and the SQL
is generated against the literal POST name instead of the column name.
Fix:
Add $search_options_pattern = 'search_task_options_' right before the
include, aligned with what perweek.php / perday.php already do.
Reproduction:
1. Enable a third-party module that adds an extrafield to the projet_task
element type (e.g. ATM doc2project adds fk_product).
2. Open /projet/activity/perweek.php and submit the form (any filter or
checkbox, including the project's own 'mode=mine' submit).
3. -> SQL error with phantom column efpt.search_task_options_<field>.
Verified affected versions: 20.0, 21.0, 22.0, 23.0, develop.
* Fix#38563 skip task-driven project load when allprojects view fetch returns no row
The updateline handler at htdocs/projet/tasks/time.php line 302 populates
$object via Task::fetch($id_temp) where $id_temp is the edited line's
task id. After the save, the page falls through to the render block at
line 937. In the all-projects view ($id=0, $ref=''), the outer if is
entered only because $allprojectforuser is set, then the elseif tests
$object->fetch($id, $ref) >= 0. Task::fetch returns 0 for no row but
leaves prior properties untouched, so $object->fk_project carries the
edited task's project. $projectstatic loads that project, $arrayofmassactions
gets filled, and the per-row toselect[] checkbox renders on every row
of the all-projects list, including rows for other projects.
Tighten the guard to > 0 so the body only runs when fetch actually
returned a row. The all-projects list then keeps the same render state
it had before the line edit: no projectstatic load, no massactions.
Reported by @priojk. Same code path on 23.0 and develop; 22.0 has a
different structure for this block and is not affected.
Signed-off-by: Dolicraft <contact@dolicraft.com>
* Update time.php
---------
Signed-off-by: Dolicraft <contact@dolicraft.com>
Co-authored-by: Dolicraft <contact@dolicraft.com>
Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
The timespent list inside a task page was scoped by the id query
parameter (/projet/tasks/time.php?id=X). The filter form right above
the list emits hidden inputs for projectid, withproject, tab and
page_y but no input for id, so submitting the search rewrites the URL
to /projet/tasks/time.php without id and the page falls back to the
global view, displaying entries from every task.
Add a hidden id input next to the others. The form already emits
projectid and withproject, so no other parameter is affected.
The same form on 21.0 and 22.0 still contained the id input; the
regression appeared on 23.0 (the input was dropped together with
unrelated layout changes in the v23 timespent refactor). PR targets
23.0 since 22.0 is unaffected.
Reported by @chris-siebert with a precise before/after URL trace and
a working hidden-input patch.
Signed-off-by: Dolicraft <contact@dolicraft.com>
Co-authored-by: Dolicraft <contact@dolicraft.com>
Webhooks targeting a host on a private/reserved range, on the local
network or on a known cloud metadata server have been blocked since
the GHSA-hh5p-m24x-fwx2 SSRF hardening landed in 23.0.2 (commit
b565d497f9). getURLContent() returns http_code 400 with a textual
explanation in $response['content'], but the webhook trigger only
logged "httpcode=400 curl_error_no=" without that content, so the
operator could not tell their webhook had been blocked on purpose
versus a generic network failure and could not discover the
$dolibarr_allow_localurl_for_webhooks flag that re-enables the
local-host case.
Append $response['content'] to the error message when the failure
matches the SSRF-block signature (curl_error_no empty, http_code
exactly 400, content non-empty). The textual hint emitted by
isIPAllowed() is then visible both in syslog and in the
WebhookHistory record.
The security fix itself is not changed.
Reported by @whoisbarry.
Signed-off-by: Dolicraft <contact@dolicraft.com>
Co-authored-by: Dolicraft <contact@dolicraft.com>
* Fix: API addtimespent crashes with "Illegal offset type" on PHP 8+
Fixes#38407
The @param annotations for $date (datetime|string) and $progress (int|null)
in Tasks::addTimeSpent() use union types, which Restler's Validator parses
into a PHP array. Then Validator::validate() at line 427 evaluates
isset(static::$preFilters[$info->type]) using that array as an array key,
which throws "Illegal offset type in isset or empty" on PHP 8+. Every call
to POST /tasks/{id}/addtimespent therefore returns HTTP 500 before the
endpoint code runs.
Use single-type annotations matching the convention already used by
Tasks::putTimeSpent in the same file: @param datetime + @phan-param string
for the date, and plain @param int for progress (its -1 default already
encodes "leave unchanged", so int|null is not needed).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Vincent Moreno <vincent.moreno@hupi.fr>
* Update parameter type for date in API documentation
* Update api_tasks.class.php
---------
Signed-off-by: Vincent Moreno <vincent.moreno@hupi.fr>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
- Fixed false positive error logging in `_recordActionResult`: an `update()` returning 0 (no rows affected) is no longer treated as an error (changed `<= 0` to `< 0`).
- Fixed `birth` date data type for Adherent anonymization: replaced the static string '1900-01-01' with a UNIX timestamp using `dol_mktime()` to comply with Dolibarr Core standards and prevent silent DAL failures.
OIDC was converted from a module to a config-level feature in v23 (#36051),
renaming the enable constant. No migration was added to update existing
llx_const rows, causing OIDC to appear disabled after a v22 -> v23 upgrade.
Add an inline edit pencil next to 'QtyToProduce' on the MO card and the
MO production view when the order is still in DRAFT status. On save:
1. The MO qty is updated through Mo::update(), which internally calls
Mo::updateProduction() to scale 'toconsume'/'toproduce' lines
proportionally (newQty/oldQty), respecting qty_frozen flags.
2. The 'toproduce' line for the MO's main product is then explicitly
realigned to match the MO qty exactly. This guards against legacy
inconsistent states and floating-point drift from successive ratio
scaling, while leaving BOM sub-product ratios and frozen lines
untouched.
The handler is placed BEFORE the include of
actions_addupdatedelete.inc.php, because that file has a generic
'set<key>' matcher that would otherwise intercept 'setqty', call
$object->fetch() + update() without setting $object->oldQty, and
silently skip the line scaling.
The edit is gated both client-side (pencil only shown when DRAFT) and
server-side (setqty handler checks STATUS_DRAFT) to prevent any
modification once the MO has been validated.
Note: the field type 'real' is temporarily overridden to 'numeric' only
while the edit pencil is active (action=editqty), because
Form::editfieldval() in html.form.class.php does not handle 'real' and
would render the Save/Cancel buttons with no <input> field in between.
Read-mode rendering (via price()) is preserved.
* Fix: avoid undefined method stdClass::isEmpty() on PHP 8.1
This fixes a fatal error when updating product prices on PHP 8.1 due to stdClass object handling.
* Update ProductCombination.class.php
---------
Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>