Compare commits

...

11 Commits
23.0 ... 18.0

Author SHA1 Message Date
Laurent Destailleur
2b3ae6c2d7 Add logs 2026-02-06 13:16:16 +01:00
Laurent Destailleur
da0ece345f Fix label 2026-02-06 13:06:16 +01:00
Laurent Destailleur
173c6865cb Test CI 2026-02-06 13:05:21 +01:00
Eric Seigne
07a3d0e4b7 auto assign PR on 18.0 / Unable to resolve action cli/gh-action, repository not found 2026-02-05 09:43:02 +01:00
Benjamin Falière
72ed29a599 FIX(API, thirdparties): get fixed amount discounts (#37068)
Co-authored-by: Benjamin Falière <benjamin.faliere@altairis.fr>
2026-02-04 14:50:21 +01:00
Sylvain Legrand
70b3be5e7f Fix from V22 (#35559)
Using a packaging with a float value less than 1 (0.1, 0.5, etc.) generates a 500 error (division by 0)

Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
Co-authored-by: Eric - CAP-REL <1468823+rycks@users.noreply.github.com>
2026-01-29 13:02:25 +01:00
Eric Seigne
07ff579416 auto assign PR to 18 thanks to copilot 2026-01-29 10:01:29 +01:00
Laurent Destailleur
53dbc754f6 Merge branch '17.0' of git@github.com:Dolibarr/dolibarr.git into 18.0 2026-01-27 10:43:41 +01:00
Laurent Destailleur
a9dcecbc2f Merge branch '16.0' of git@github.com:/Dolibarr/dolibarr.git into 17.0 2026-01-27 10:43:20 +01:00
Laurent Destailleur
5d524f7973 Merge branch '15.0' of git@github.com:Dolibarr/dolibarr.git into 16.0 2026-01-27 10:43:01 +01:00
Laurent Destailleur
1b2f16b01a Fix ci 2026-01-27 10:42:37 +01:00
5 changed files with 241 additions and 68 deletions

82
.github/workflows/pr-18-testldr.yaml vendored Normal file
View File

@@ -0,0 +1,82 @@
# Action to prepare the github action
# Go on Dolibarr - Settings - Developer settings - Enter a name + webhook to disable + Permissions (see app test) + Can install by any account
# Click on generate the private keys
# Click on Install application - choose user of the Organization
# Go on Organisation - Secret and variables and create a secret PR18_SECRET_KEY and copy the content of received private key. Choose the repository access to "Repository Dolibarr".
# Go on Organisation - Secret and variables and create a variable PR18_APP_ID and copy the ID of the previously create ID. Choose the repository access to "Repository Dolibarr".
#
name: Set reviewer and label for v18 (test ldr)
on:
pull_request_target:
types: [opened, synchronize, reopened]
branches:
- "18.0"
push:
branches:
- "18.0"
permissions:
pull-requests: write
issues: write
jobs:
assign-and-label-v18:
runs-on: ubuntu-latest
steps:
#- name: Install GitHub CLI
# run: sudo apt-get install gh
- name: Debug GitHub Event
run: cat $GITHUB_EVENT_PATH
- name: Generate a token
id: generate-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ vars.PR18_APP_ID }}
private-key: ${{ secrets.PR18_SECRET_KEY }}
- name: Checkout repository
uses: actions/checkout@v4
- name: Assign Tag
env:
GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }}
prid: ${{ github.event.pull_request.number }}
prid2: ${{ github.event.pull_request_target.number }}
url: ${{ github.event.pull_request.html_url }}
url2: ${{ github.event.pull_request_target.html_url }}
run: |
echo "env.prid=${{env.prid}}"
echo "env.prid=${{env.prid2}}"
echo "env.url=${{env.url}}"
echo "env.url2=${{env.url2}}"
gh pr edit "${{env.prid}}" --add-label "Issue for v18 maintenance Team"
- name: Set reviewers except PR author
id: set-reviewers
run: |
# Liste des reviewers à ajuster selon équipe
REVIEWERS=("lvessiller-opendsi" "rycks")
AUTHOR="${{ github.event.pull_request.user.login }}"
FINAL_REVIEWERS=()
for reviewer in "${REVIEWERS[@]}"; do
if [ "$reviewer" != "$AUTHOR" ]; then
FINAL_REVIEWERS+=("$reviewer")
fi
done
echo "AUTHOR=$AUTHOR"
echo "reviewers=$(IFS=, ; echo "${FINAL_REVIEWERS[*]}")" >> $GITHUB_OUTPUT
- name: Assign reviewers
if: steps.set-reviewers.outputs.reviewers != ''
env:
GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }}
prid: ${{ github.event.pull_request.number }}
url: ${{ github.event.pull_request.html_url }}
reviewers: ${{ steps.set-reviewers.outputs.reviewers }}
run: |
echo "Assigning reviewers: ${{env.reviewers}}"
gh pr edit "${{env.prid}}" --add-reviewer "${{env.reviewers}}"

View File

@@ -1,11 +1,17 @@
# Action to prepare the github action
# Go on Dolibarr - Settings - Developer settings - Enter a name + webhook to disable + Permissions (see app test) + Can install by any account
# Click on generate the private keys
# Click on Install application - choose user of the Organization
# Go on Organisation - Secret and variables and create a secret PR18_SECRET_KEY and copy the content of received private key. Choose the repository access to "Repository Dolibarr".
# Go on Organisation - Secret and variables and create a variable PR18_APP_ID and copy the ID of the previously create ID. Choose the repository access to "Repository Dolibarr".
# Action to prepare the GitHub Action
# Prerequisites (create in the organization / repo):
# - Secret: PR18_SECRET_KEY (private key generated for the GitHub App)
# - Variable: PR18_APP_ID (GitHub App ID)
#
# Behavior:
# - On pull_request_target (opened, synchronize, reopened) against branch 18.0:
# - Generate a GitHub App token
# - Add the label "Issue for v18 maintenance Team" to the PR (error-tolerant)
# - Assign the reviewers listed below, excluding the PR author
# -> attempt per reviewer (if a reviewer fails, log and continue)
# -> the step fails only if no reviewer could be added
# - On push to 18.0: workflow runs but PR-specific actions are skipped
#
name: Set reviewer and label for v18
on:
pull_request_target:
@@ -24,54 +30,137 @@ jobs:
assign-and-label-v18:
runs-on: ubuntu-latest
# Mergeers / reviewers list: edit here as needed (comma-separated)
env:
REVIEWERS: "lvessiller-opendsi,rycks"
# Label name to apply
V18_LABEL: "Issue for v18 maintenance Team"
steps:
#- name: Install GitHub CLI
# run: sudo apt-get install gh
# 1) Generate a GitHub App token (via actions/create-github-app-token)
- name: Generate GitHub App token
id: generate-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ vars.PR18_APP_ID }}
private-key: ${{ secrets.PR18_SECRET_KEY }}
- name: Generate a token
id: generate-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ vars.PR18_APP_ID }}
private-key: ${{ secrets.PR18_SECRET_KEY }}
# 2) Checkout repository (useful if repo content is needed later)
- name: Checkout repository
uses: actions/checkout@v4
- name: Checkout repository
uses: actions/checkout@v4
# 3) Install / configure GitHub CLI (gh) on the runner
#- name: Setup GitHub CLI (gh)
# uses: cli/gh-action@v3
# -> Error: Unable to resolve action cli/gh-action, repository not found
- name: Setup GitHub CLI (gh) (install locally)
run: |
set -euo pipefail
sudo apt-get update
sudo apt-get install -y gh || {
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg
sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null
sudo apt-get update
sudo apt-get install -y gh
}
gh --version
- name: Assign Tag
env:
GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }}
prid: ${{ github.event.pull_request.number }}
url: ${{ github.event.pull_request.html_url }}
url2: ${{ github.event.pull_request_target.html_url }}
run: |
echo "env.prid=${{env.prid}}"
echo "env.url=${{env.url}}"
echo "env.url2=${{env.url2}}"
gh pr edit "${{env.prid}}" --add-label "Issue for v18 maintenance Team"
# Debug information (useful for diagnostics)
- name: Debug info
run: |
echo "Event: $GITHUB_EVENT_NAME"
echo "Ref: $GITHUB_REF"
echo "Run id: $GITHUB_RUN_ID"
echo "Reviewers configured: $REVIEWERS"
- name: Set reviewers except PR author
id: set-reviewers
run: |
# Liste des reviewers à ajuster selon équipe
REVIEWERS=("lvessiller-opendsi" "rycks")
AUTHOR="${{ github.event.pull_request.user.login }}"
FINAL_REVIEWERS=()
for reviewer in "${REVIEWERS[@]}"; do
if [ "$reviewer" != "$AUTHOR" ]; then
FINAL_REVIEWERS+=("$reviewer")
# 4) Add the label to the PR (PR events only)
# -> tolerant to errors: log on failure but do not fail the job
- name: Add label to PR (pull_request events only)
if: ${{ github.event_name == 'pull_request' }}
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
V18_LABEL: ${{ env.V18_LABEL }}
run: |
set -euo pipefail
echo "Adding label '$V18_LABEL' to PR #${PR_NUMBER}"
if gh pr edit "$PR_NUMBER" --add-label "$V18_LABEL"; then
echo "Label added successfully."
else
echo "Warning: failed to add label '$V18_LABEL' to PR #${PR_NUMBER}. Continuing."
fi
done
echo "AUTHOR=$AUTHOR"
echo "reviewers=$(IFS=, ; echo "${FINAL_REVIEWERS[*]}")" >> $GITHUB_OUTPUT
- name: Assign reviewers
if: steps.set-reviewers.outputs.reviewers != ''
env:
GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }}
prid: ${{ github.event.pull_request.number }}
url: ${{ github.event.pull_request.html_url }}
reviewers: ${{ steps.set-reviewers.outputs.reviewers }}
run: |
echo "Assigning reviewers: ${{env.reviewers}}"
gh pr edit "${{env.prid}}" --add-reviewer "${{env.reviewers}}"
# 5) Compute final reviewers list excluding the PR author
- name: Compute reviewers (exclude PR author)
if: ${{ github.event_name == 'pull_request' }}
id: set-reviewers
run: |
set -euo pipefail
IFS=',' read -ra ALL_REVIEWERS <<< "${REVIEWERS}"
AUTHOR="${{ github.event.pull_request.user.login }}"
FINAL=()
for r in "${ALL_REVIEWERS[@]}"; do
r_trimmed="$(echo "$r" | xargs)"
if [ -z "$r_trimmed" ]; then
continue
fi
if [ "$r_trimmed" != "$AUTHOR" ]; then
FINAL+=("$r_trimmed")
fi
done
if [ ${#FINAL[@]} -eq 0 ]; then
echo "reviewers=" >> $GITHUB_OUTPUT
else
reviewers_csv="$(IFS=, ; echo "${FINAL[*]}")"
echo "reviewers=${reviewers_csv}" >> $GITHUB_OUTPUT
fi
echo "author=$AUTHOR" >> $GITHUB_OUTPUT
echo "Computed reviewers: ${reviewers_csv:-<none>}"
# 6) Assign reviewers one-by-one with fine-grained error handling
# - try each reviewer, track successes and failures
# - fail the step only if none could be added
# - succeed if at least one was added (but log failures)
- name: Assign reviewers on PR (per-reviewer, tolerant errors)
if: ${{ github.event_name == 'pull_request' && steps.set-reviewers.outputs.reviewers != '' }}
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
REVIEWERS_CSV: ${{ steps.set-reviewers.outputs.reviewers }}
run: |
# Note: avoid 'set -e' to handle per-reviewer failures manually
set -uo pipefail
IFS=',' read -ra TO_ADD <<< "${REVIEWERS_CSV}"
SUCCESS=0
FAILED=()
for r in "${TO_ADD[@]}"; do
r_trimmed="$(echo "$r" | xargs)"
if [ -z "$r_trimmed" ]; then
continue
fi
echo "Attempting to add reviewer: $r_trimmed"
if gh pr edit "$PR_NUMBER" --add-reviewer "$r_trimmed"; then
echo "Added reviewer: $r_trimmed"
SUCCESS=$((SUCCESS+1))
else
echo "Warning: failed to add reviewer: $r_trimmed"
echo "Debug: PR view:"
gh pr view "$PR_NUMBER" --json number,title,author,reviewRequests || true
FAILED+=("$r_trimmed")
fi
done
if [ $SUCCESS -eq 0 ]; then
echo "Error: none of the configured reviewers could be added: ${FAILED[*]:-<none>}"
# Fail the step because no reviewer was added
exit 1
else
echo "Reviewers added: ${SUCCESS}. Failed to add: ${FAILED[*]:-none}"
# Step succeeds even if some reviewers failed
fi
# 7) Push event notice (no PR-specific actions performed)
- name: Push event notice
if: ${{ github.event_name == 'push' }}
run: |
echo "Triggered by push on branch 18.0. No PR-specific actions performed."

View File

@@ -916,8 +916,8 @@ class modProduct extends DolibarrModules
$this->import_regex_array[$r] = array(
'pr.datec'=>'^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]$',
'pr.recuperableonly'=>'^[0|1]$'
);
$this->import_convertvalue_array[$r] = array(
);
$this->import_convertvalue_array[$r] = array(
'pr.fk_product'=>array('rule'=>'fetchidfromref', 'classfile'=>'/product/class/product.class.php', 'class'=>'Product', 'method'=>'fetch', 'element'=>'Product')
);
$this->import_examplevalues_array[$r] = array('pr.fk_product'=>"ref:PRODUCT_REF or id:123456",

View File

@@ -616,7 +616,7 @@ class CommandeFournisseur extends CommonOrder
$objsearchpackage = $this->db->fetch_object($resqlsearchpackage);
if ($objsearchpackage) {
$line->fk_fournprice = $objsearchpackage->rowid;
$line->packaging = $objsearchpackage->packaging;
$line->packaging = (float) $objsearchpackage->packaging;
}
} else {
$this->error = $this->db->lasterror();
@@ -1979,16 +1979,16 @@ class CommandeFournisseur extends CommonOrder
}
// Predefine quantity according to packaging
if (!empty($conf->global->PRODUCT_USE_SUPPLIER_PACKAGING)) {
if (getDolGlobalString('PRODUCT_USE_SUPPLIER_PACKAGING')) {
$prod = new Product($this->db);
$prod->get_buyprice($fk_prod_fourn_price, $qty, $fk_product, 'none', (empty($this->fk_soc) ? $this->socid : $this->fk_soc));
$prod->get_buyprice($fk_prod_fourn_price, (float) $qty, $fk_product, 'none', (empty($this->fk_soc) ? $this->socid : $this->fk_soc));
if ($qty < $prod->packaging) {
$qty = $prod->packaging;
$qty = (float) $prod->packaging;
} else {
if (!empty($prod->packaging) && ($qty % $prod->packaging) > 0) {
$coeff = intval($qty / $prod->packaging) + 1;
$qty = $prod->packaging * $coeff;
if (!empty($prod->packaging) && (fmod((float) $qty, (float) $prod->packaging) > 0.000001)) {
$coeff = intval((float) $qty / $prod->packaging) + 1;
$qty = (float) $prod->packaging * $coeff;
setEventMessages($langs->trans('QtyRecalculatedWithPackaging'), null, 'mesgs');
}
}
@@ -2962,11 +2962,11 @@ class CommandeFournisseur extends CommonOrder
$this->line->desc = $desc;
// redefine quantity according to packaging
if (!empty($conf->global->PRODUCT_USE_SUPPLIER_PACKAGING)) {
if (getDolGlobalString('PRODUCT_USE_SUPPLIER_PACKAGING')) {
if ($qty < $this->line->packaging) {
$qty = $this->line->packaging;
} else {
if (!empty($this->line->packaging) && ($qty % $this->line->packaging) > 0) {
if (!empty($this->line->packaging) && is_numeric($this->line->packaging) && (float) $this->line->packaging > 0 && (fmod((float) $qty, $this->line->packaging) > 0)) {
$coeff = intval($qty / $this->line->packaging) + 1;
$qty = $this->line->packaging * $coeff;
setEventMessage($langs->trans('QtyRecalculatedWithPackaging'), 'mesgs');
@@ -3869,7 +3869,7 @@ class CommandeFournisseurLigne extends CommonOrderLine
$objsearchpackage = $this->db->fetch_object($resqlsearchpackage);
if ($objsearchpackage) {
$this->fk_fournprice = $objsearchpackage->rowid;
$this->packaging = $objsearchpackage->packaging;
$this->packaging = (float) $objsearchpackage->packaging;
}
} else {
$this->error = $this->db->lasterror();

View File

@@ -1,8 +1,9 @@
<?php
/* Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
* Copyright (C) 2018 Pierre Chéné <pierre.chene44@gmail.com>
* Copyright (C) 2019 Cedric Ancelin <icedo.anc@gmail.com>
/* Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
* Copyright (C) 2018 Pierre Chéné <pierre.chene44@gmail.com>
* Copyright (C) 2019 Cedric Ancelin <icedo.anc@gmail.com>
* Copyright (C) 2020-2021 Frédéric France <frederic.france@netlogic.fr>
* Copyright (C) 2026 Benjamin Falière <benjamin@faliere.com>
*
* 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
@@ -1052,8 +1053,9 @@ class Thirdparties extends DolibarrApi
$sql = "SELECT f.ref, f.type as factype, re.fk_facture_source, re.rowid, re.amount_ht, re.amount_tva, re.amount_ttc, re.description, re.fk_facture, re.fk_facture_line";
$sql .= " FROM ".MAIN_DB_PREFIX."societe_remise_except as re, ".MAIN_DB_PREFIX."facture as f";
$sql .= " WHERE f.rowid = re.fk_facture_source AND re.fk_soc = ".((int) $id);
$sql .= " FROM ".MAIN_DB_PREFIX."societe_remise_except as re";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."facture as f ON f.rowid = re.fk_facture_source";
$sql .= " WHERE re.fk_soc = ".((int) $id);
if ($filter == "available") {
$sql .= " AND re.fk_facture IS NULL AND re.fk_facture_line IS NULL";
}