* FIX#37826 country_code must be an ISO code, not a label
On a proposal/order, the 2nd+ line got VAT 0% with the error "no vat rates
defined for country 'France'/'Suisse'". Root cause: the seller country_code
held a translated country label instead of the ISO code, so the
"c.code IN (...)" lookup in Form::load_cache_vatrates() matched nothing.
- Societe::setMysoc(): the canonical MAIN_INFO_SOCIETE_COUNTRY is
"id:code:label"; a legacy "id:label" value put the label into
country_code. Only treat the full 3-token form as "id:code:label" and
otherwise rebuild code/label from the authoritative country id.
- Form::load_tva(): defensively recover the seller ISO code from its
country id when country_code is not a known c_country code, so a bad
value can never silently force the VAT rate to 0%.
Signed-off-by: Jam Balaya <jambalaya.pyoncafe@outlook.jp>
* FIX#37826 avoid useless SQL per page: recover country ISO code from id only when code is not a 2-char ISO code
---------
Signed-off-by: Jam Balaya <jambalaya.pyoncafe@outlook.jp>
* fix: accounting export reexport date not updated and fiscal year filter incorrectly applied on reexport
* Update date_export only if it is empty or ACCOUNTING_REEXPORT is active
---------
Co-authored-by: Zakaria Boushaba <z.boushaba@vold.africa>
Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
* FIX: Margin calculation on situation invoices uses cumulative percent instead of delta, causing inflated amounts across all margin views
* Fix
* use the 'as' between table name and alias name
---------
Co-authored-by: Zakaria Boushaba <z.boushaba@vold.africa>
Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
htdocs/fourn/paiement/document.php called
restrictedArea($user, $object->element, $object->id, 'paiementfourn', '')
before $object was instantiated (the 'Load object' block sat after the
security check), so the permission check ran on an undefined $object (null
element / null id) and PHPStan level 10 flagged 'Variable $object might not
be defined'.
The sibling pages fourn/paiement/card.php and info.php instantiate the
PaiementFourn object before the same restrictedArea() call. Move the object
load ahead of the security check so the check runs against the real object.
In htdocs/comm/action/index.php the month and week views call
print_actions_filter(..., $actioncode, $usergroupids, ...) with
$usergroupids (array<int>, built at the top from the comma-separated
$usergroup), but the day view passed the raw $usergroup string instead.
print_actions_filter() forwards this to select_dolgroups() which expects an
array, so a multi-group filter (e.g. '3,5') is not preselected in the day view.
Pass $usergroupids to match the two sibling calls.
PHPStan level 10 flagged: Parameter #16 $usergroupid of function
print_actions_filter expects array<int>, string given.
Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
htdocs/paypal/lib/paypal.lib.php passed a boolean to CURLOPT_SSL_VERIFYHOST:
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, ($ssl_verifypeer ? true : false));
CURLOPT_SSL_VERIFYHOST expects 0, 1 or 2 (not a bool). 'true' is cast to int 1,
which only checks that a Common Name is present, not that it matches the host
(value 1 is also deprecated / a no-op on some libcurl builds) - weakening TLS
host verification on PayPal API calls. Use 2 (full host check) / 0.
PHPStan level 10 flagged: Parameter #3 $value of function curl_setopt expects
0|2, bool given.
Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
htdocs/core/class/openid.class.php OpenId::GetOpenIDServer() calls:
$response = getURLContent(...); // array{http_code,content,...}
list($servers, $delegates) = $this->HTML2OpenIDServer($response);
but HTML2OpenIDServer($content) runs preg_match_all(..., $content), i.e. it
expects the HTML string, not the whole getURLContent() array. On PHP 8 this
throws a TypeError (preg_match_all subject must be string), breaking OpenID
server discovery; on PHP 7 it silently matches nothing. Pass $response['content'].
PHPStan level 10 flagged: Parameter #1 $content of method
SimpleOpenID::HTML2OpenIDServer() expects string, array given.
Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
* qual: Update branch protection rules in AGENTS.md
Updated the branch protection rules in AGENTS.md to reflect the new policy of never committing directly to the `develop` branch or version branches matching the regex `^\d+\.\d+$`.
* Fix: Rename .claude directory to .agents
Renamed the .claude directory and its contents to .agents.
.agents is AI provider agnostic.
The legacy FCKeditor file manager connector (config.inc.php, also included
by browser/default/browser.php and connectors/php/connector.php) only called
main.inc.php and never checked any module right on 21.0 and 22.0, so any
authenticated user, even one with no permission, could reach the connector,
browse the medias directory and upload files.
The permission check already present on 23.0 and develop was never backported.
This adds the same guard so a non-admin without website write right is refused.
Reported by Abderrahmane Aksoum.
Signed-off-by: Dolicraft <contact@dolicraft.com>
Co-authored-by: Dolicraft <contact@dolicraft.com>
Extrafield labels are stored HTML encoded in llx_extrafields.label
because they are saved through Translate::trans(), which runs the
string through htmlentities(). write_title() wrote the label as-is,
so the CSV header line showed raw entities (Données instead of
Données) while the data cells were fine. With a ; separator the
trailing ; of each entity also wrapped the label in double quotes.
Decode entities and remove HTML with dol_string_nohtmltag() before
csvClean(), consistent with the TSV and Excel export drivers. For the
derived _label column of Select extrafields, append the suffix before
csvClean() so the column stays valid CSV when the decoded label
contains the separator or a quote.
Signed-off-by: Gregory Aliot <greg.aliot@gmail.com>
Co-authored-by: Gregory Aliot <greg.aliot@gmail.com>
Since PHP 8.3 ldap_connect() takes a single URI argument. connectBind()
concatenated host and port as "host:port", which is a malformed URI for a
scheme-less host (localhost, 192.168.0.2) since the host is then parsed as the
URI scheme, so the following bind returns false. It also appended ":port" to an
already qualified ldaps:// URI. Prefix scheme-less hosts with ldap:// and leave
ldap(s):// URIs untouched.
Signed-off-by: Dolicraft <clement@pixodeo.com>
The double-space removal loop guarded its condition with the truthiness
of strpos():
while (strpos($temp, " ")) {
strpos() returns int 0 when the first double space is at position 0 of
the string, and 0 is falsy, so the loop body never runs. As a result NO
double spaces are collapsed at all in that string, including interior
ones. This happens for any input that begins with two spaces, e.g. text
whose leading "<br><br>" is turned into two leading spaces by the
removelinefeed step just above. The trailing trim() hides the leading
spaces but the interior double spaces survive, producing wrong output
(e.g. "Total amount due" instead of "Total amount due").
Fix by comparing against false, the standard PHP idiom used elsewhere in
this codebase. Strings that do not start with a double space were already
handled correctly, so behaviour is unchanged for them. Also corrected the
neighbouring comment ("double quotes" -> "double spaces").
Signed-off-by: TowyTowy <towy@airreps.link>
Co-authored-by: Claude <noreply@anthropic.com>
* FIX Invoice creation from template ignores extra fields set in form
* Fix Strict comparison using === between mixed and null will always evaluate to false
---------
Co-authored-by: Zakaria Boushaba <z.boushaba@vold.africa>
# Qual: Update logToCheckStyle (now extracts PHP Lint error msg)
logToCheckStyle was updated to extract PHP Lint (php -l) errors.
So they will show in the summary report and as annotations.