mirror of
https://github.com/Dolibarr/dolibarr.git
synced 2026-03-18 16:13:59 +01:00
Compare commits
512 Commits
phpstan-ba
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5f5b4570c4 | ||
|
|
0e5170e491 | ||
|
|
812b51a0b7 | ||
|
|
66ac556770 | ||
|
|
d548dfe042 | ||
|
|
1b47fa3bd8 | ||
|
|
74a202ed17 | ||
|
|
0a07d73ed4 | ||
|
|
2c8bb1eac2 | ||
|
|
5c3acbcc60 | ||
|
|
270f719e8a | ||
|
|
c32f0cae0c | ||
|
|
449f0cbd23 | ||
|
|
2985c8bd21 | ||
|
|
a222fe5360 | ||
|
|
e98c441c4c | ||
|
|
638fd27af2 | ||
|
|
f9c6562de9 | ||
|
|
8ede2e75f5 | ||
|
|
86bb0b4cc5 | ||
|
|
4098eafe24 | ||
|
|
5294805890 | ||
|
|
5915aa4846 | ||
|
|
9db76503e4 | ||
|
|
fe4da5614a | ||
|
|
e5135ed8ab | ||
|
|
a33b97b311 | ||
|
|
10d780ad60 | ||
|
|
c783b305dc | ||
|
|
ae5d7b6fb6 | ||
|
|
559454e4d4 | ||
|
|
a81ba1847d | ||
|
|
209323598a | ||
|
|
88b66741fc | ||
|
|
6505e41b4d | ||
|
|
a4bd180c72 | ||
|
|
2605d33dbc | ||
|
|
9e13496975 | ||
|
|
d33e0dd302 | ||
|
|
cb96c5fbed | ||
|
|
7c399bd78b | ||
|
|
22804ef1d6 | ||
|
|
0d50af64a2 | ||
|
|
6a3300348e | ||
|
|
57812a1272 | ||
|
|
39f3abec0c | ||
|
|
5760b67393 | ||
|
|
558064b9be | ||
|
|
46c764c689 | ||
|
|
6f4c1ef972 | ||
|
|
a9d846dfd6 | ||
|
|
bc29bc1a8a | ||
|
|
4908f4acba | ||
|
|
ce9f993856 | ||
|
|
2a3982f2fd | ||
|
|
e77af9c26c | ||
|
|
1917a9bffc | ||
|
|
0f6c62856b | ||
|
|
e8a02f169a | ||
|
|
ac1a56fd00 | ||
|
|
e0ceff086e | ||
|
|
28b77bc599 | ||
|
|
84eae7147b | ||
|
|
fbd80c35d8 | ||
|
|
2f5a74e34c | ||
|
|
cad1d11aee | ||
|
|
15ddf8b2ad | ||
|
|
72d6859473 | ||
|
|
a0ddd26b63 | ||
|
|
0f2d81203b | ||
|
|
7753db3704 | ||
|
|
5a54415266 | ||
|
|
06629610bf | ||
|
|
27f22cf073 | ||
|
|
50af72468b | ||
|
|
402af2a929 | ||
|
|
ea86d67269 | ||
|
|
38d1b12bfe | ||
|
|
6aea3f6e16 | ||
|
|
9400c2b358 | ||
|
|
4b3443cd77 | ||
|
|
d3de4529a7 | ||
|
|
6bd574c307 | ||
|
|
324930d438 | ||
|
|
fea51e5b92 | ||
|
|
2680ec9652 | ||
|
|
a6e877c36e | ||
|
|
d1e45ecf1a | ||
|
|
639a8cd789 | ||
|
|
dc1fc47118 | ||
|
|
f90e121650 | ||
|
|
699718ab35 | ||
|
|
14dd9abcc1 | ||
|
|
ec67efed5f | ||
|
|
c332faa956 | ||
|
|
53c90968a2 | ||
|
|
e3d526db4b | ||
|
|
561cbbe958 | ||
|
|
54c5141d16 | ||
|
|
55636c2519 | ||
|
|
acf6ce15d0 | ||
|
|
992731c8ca | ||
|
|
138c1bf5b3 | ||
|
|
82dfa6e189 | ||
|
|
99bc406c49 | ||
|
|
934123eef5 | ||
|
|
482b8e8248 | ||
|
|
b4ab809e97 | ||
|
|
5a1ea120e6 | ||
|
|
c531628ae3 | ||
|
|
4039e17b89 | ||
|
|
5d83e6feb3 | ||
|
|
b9f5895c68 | ||
|
|
c76d781e30 | ||
|
|
488366cf06 | ||
|
|
584e103ebf | ||
|
|
058c8ee9b0 | ||
|
|
f86955fcf9 | ||
|
|
1582f1afb9 | ||
|
|
5475df50bf | ||
|
|
cbf2adc238 | ||
|
|
093d9634c8 | ||
|
|
0e62a44ea1 | ||
|
|
a12da79340 | ||
|
|
f23b1801e5 | ||
|
|
c057d46a4c | ||
|
|
677ec352d9 | ||
|
|
531c55a9fd | ||
|
|
86f08b429b | ||
|
|
f188ad64eb | ||
|
|
30e28d5f8a | ||
|
|
ffcbe0263d | ||
|
|
95579849dc | ||
|
|
904f201de3 | ||
|
|
94d7642b2f | ||
|
|
69f75928e7 | ||
|
|
a4b8cb7008 | ||
|
|
e29b741958 | ||
|
|
b02f1a4ebf | ||
|
|
30c7e86687 | ||
|
|
70346cb3bb | ||
|
|
ff204097e3 | ||
|
|
e62d193997 | ||
|
|
b1e693a1ac | ||
|
|
f0d6ae5b3f | ||
|
|
f01cfa9fad | ||
|
|
502bc82815 | ||
|
|
5e8bb7be8e | ||
|
|
93c6cb922c | ||
|
|
fd71eba41e | ||
|
|
ea60298eeb | ||
|
|
6a70193549 | ||
|
|
b73d055cbb | ||
|
|
3016301ae8 | ||
|
|
2065dee731 | ||
|
|
6689f5bb52 | ||
|
|
d8bce4e1e4 | ||
|
|
cd0a4016ff | ||
|
|
ad16be87eb | ||
|
|
8172916804 | ||
|
|
f5e7d983b3 | ||
|
|
bf77993fc9 | ||
|
|
4f289effa3 | ||
|
|
d615300b7a | ||
|
|
a8f7b6eb05 | ||
|
|
1ee077f797 | ||
|
|
f1433d9234 | ||
|
|
ab108d2b80 | ||
|
|
d2bbba5a21 | ||
|
|
2176229ffb | ||
|
|
8bb0a5d5d8 | ||
|
|
170a782a87 | ||
|
|
1f952b6969 | ||
|
|
ca957917e5 | ||
|
|
5079e49023 | ||
|
|
89eb9115b3 | ||
|
|
d61ffe5872 | ||
|
|
45d862c0e8 | ||
|
|
02885b4a5b | ||
|
|
dac22586ba | ||
|
|
7762154f6d | ||
|
|
6f1b27ddad | ||
|
|
432361304a | ||
|
|
f8a37ca704 | ||
|
|
59f2abede8 | ||
|
|
4522f558df | ||
|
|
75ca8e04b4 | ||
|
|
156fedd87a | ||
|
|
8e68db2f58 | ||
|
|
a8b4e5d141 | ||
|
|
92151198bf | ||
|
|
e4d8d3047e | ||
|
|
d2e1cb18cb | ||
|
|
ddfb7c1274 | ||
|
|
8ba2977d58 | ||
|
|
ce19a9df01 | ||
|
|
f84ed50a8e | ||
|
|
d004cf01fc | ||
|
|
0f2ee5e5c2 | ||
|
|
6c621b182e | ||
|
|
f4aac1bd9a | ||
|
|
acb525524e | ||
|
|
054720a474 | ||
|
|
70db1d57b9 | ||
|
|
7e7a11cef1 | ||
|
|
7e203114eb | ||
|
|
4ad6d1b5b3 | ||
|
|
6944c5584f | ||
|
|
d1e9781403 | ||
|
|
ab3a6b37cc | ||
|
|
f597cf9310 | ||
|
|
dfd9e93bbc | ||
|
|
e74a333862 | ||
|
|
aa10d6062b | ||
|
|
767ddd34a2 | ||
|
|
b3b02c98a0 | ||
|
|
345a92c7db | ||
|
|
62cf38f227 | ||
|
|
a9db2f6dba | ||
|
|
3206535346 | ||
|
|
391df54913 | ||
|
|
e58458efc6 | ||
|
|
7fe6c0c873 | ||
|
|
435935be01 | ||
|
|
f6ccc8664c | ||
|
|
dde1fa3837 | ||
|
|
1969fcf195 | ||
|
|
a1aa7f8eb6 | ||
|
|
208d960dab | ||
|
|
68c8eded6c | ||
|
|
8688ee5866 | ||
|
|
676f6574f9 | ||
|
|
2cf8e3809f | ||
|
|
bfa1c0e874 | ||
|
|
4d2359e1de | ||
|
|
b9ef1caa46 | ||
|
|
7e123903cf | ||
|
|
42900e2c8c | ||
|
|
6b9d5a59d9 | ||
|
|
b31856eaf8 | ||
|
|
62c9dbe689 | ||
|
|
bbf9ff2c4c | ||
|
|
d704f6f532 | ||
|
|
cf11ecd085 | ||
|
|
f4109abe56 | ||
|
|
baa1359021 | ||
|
|
6db0b14702 | ||
|
|
ddf784c0a7 | ||
|
|
4646cf8417 | ||
|
|
d165a8a557 | ||
|
|
c723aec696 | ||
|
|
f23d590749 | ||
|
|
2a14bdc20f | ||
|
|
a3d5c0b30b | ||
|
|
f4278b2c6f | ||
|
|
bc44247cd1 | ||
|
|
a7afc55fde | ||
|
|
c449bcf9f4 | ||
|
|
dea8c0cf0e | ||
|
|
ba9f70dc92 | ||
|
|
015c9859cc | ||
|
|
e3a0d9fda4 | ||
|
|
9b54dcf4af | ||
|
|
be2957816a | ||
|
|
2070883cfd | ||
|
|
8e1477a6c8 | ||
|
|
6caa4cd194 | ||
|
|
14de760244 | ||
|
|
3ab609726e | ||
|
|
0eb3c9e68d | ||
|
|
6a76c6c0ed | ||
|
|
d97351526d | ||
|
|
35eb6853fd | ||
|
|
6ba163880a | ||
|
|
03d1fe4e4f | ||
|
|
a3530f0329 | ||
|
|
e2a1544dd6 | ||
|
|
73fb69abca | ||
|
|
e049222877 | ||
|
|
c428e079e3 | ||
|
|
d3252f0846 | ||
|
|
0dd56ce3bd | ||
|
|
8d97afc6ed | ||
|
|
e596db45e8 | ||
|
|
418f0c2e30 | ||
|
|
2a6d7739ba | ||
|
|
9587c1302b | ||
|
|
e71464d907 | ||
|
|
d5fab0b625 | ||
|
|
500bd9c1f2 | ||
|
|
ce3609ce83 | ||
|
|
3dfce11ae8 | ||
|
|
7a8e0ace2e | ||
|
|
3a15740c73 | ||
|
|
7bb2713bd9 | ||
|
|
96205a6a09 | ||
|
|
e6f6231c48 | ||
|
|
9bc6dd0294 | ||
|
|
1650d96091 | ||
|
|
a0b7852d65 | ||
|
|
cf0ab8d300 | ||
|
|
01a94ed23f | ||
|
|
641333e2ce | ||
|
|
9c0cdee7c6 | ||
|
|
5da7a05d01 | ||
|
|
6b73193846 | ||
|
|
8cb84700e2 | ||
|
|
23716a217e | ||
|
|
92f76fcef4 | ||
|
|
9940db94d9 | ||
|
|
3e7805acd7 | ||
|
|
dc794ff2fb | ||
|
|
0e566ac49f | ||
|
|
f852915171 | ||
|
|
1f09296717 | ||
|
|
8f8c70922e | ||
|
|
d072d1fc0a | ||
|
|
cc9b9e06b6 | ||
|
|
aaf64cf696 | ||
|
|
17857b271c | ||
|
|
eaf2faebdb | ||
|
|
fc19612af0 | ||
|
|
6a12f5c6d5 | ||
|
|
4213a06ad8 | ||
|
|
4406dbe2c4 | ||
|
|
6fb8b21471 | ||
|
|
6f12d1dfcc | ||
|
|
a4e077a0f7 | ||
|
|
749b45a16d | ||
|
|
f1c2ab19af | ||
|
|
f7151c3cd9 | ||
|
|
fffbf75d33 | ||
|
|
b9c244ab05 | ||
|
|
89d3afe517 | ||
|
|
497a3ac3a2 | ||
|
|
b956ea5257 | ||
|
|
293b5a0778 | ||
|
|
61c935b827 | ||
|
|
343edad341 | ||
|
|
864191e881 | ||
|
|
6ca6462466 | ||
|
|
5077058df5 | ||
|
|
b4d326e30b | ||
|
|
8bb8553fbd | ||
|
|
939930dade | ||
|
|
fcb11df3b8 | ||
|
|
cd74b34708 | ||
|
|
b73291cbca | ||
|
|
603e1ff877 | ||
|
|
eb778141d6 | ||
|
|
a3fd7e57f9 | ||
|
|
38142b31d1 | ||
|
|
2454b651d4 | ||
|
|
e0e77b203c | ||
|
|
175e677f2a | ||
|
|
e85fd10a2b | ||
|
|
82d3d3ba92 | ||
|
|
57a1f05d49 | ||
|
|
913d732d7f | ||
|
|
bcedf595f3 | ||
|
|
0818f97b96 | ||
|
|
9273cbd919 | ||
|
|
894c4d8f9e | ||
|
|
d928b816d7 | ||
|
|
1ea2a2b0fd | ||
|
|
848c5b9482 | ||
|
|
29aceee503 | ||
|
|
19f8fe8e63 | ||
|
|
2cbde4df2a | ||
|
|
50eeab9211 | ||
|
|
7c2e2f4c82 | ||
|
|
115b1e27b0 | ||
|
|
26c38c52f9 | ||
|
|
743c22e57c | ||
|
|
4bfe5d2d4a | ||
|
|
58852eaa57 | ||
|
|
5f99494fed | ||
|
|
8976231f35 | ||
|
|
1febc39a56 | ||
|
|
6f2adf3457 | ||
|
|
d1a78bf3ac | ||
|
|
5756ba2369 | ||
|
|
6ba0c37ccd | ||
|
|
78b1259b51 | ||
|
|
3e95f2b456 | ||
|
|
5133603afd | ||
|
|
518f16aea3 | ||
|
|
d44ab2f9b5 | ||
|
|
2e56f40c39 | ||
|
|
f7ba251a35 | ||
|
|
91e5b19541 | ||
|
|
454c688403 | ||
|
|
f6241de8a0 | ||
|
|
5d1c39440d | ||
|
|
fc13435730 | ||
|
|
53ecd7de15 | ||
|
|
271af9f8f7 | ||
|
|
ec8898ac50 | ||
|
|
011eb736b8 | ||
|
|
3a84044b6d | ||
|
|
1af0a8db4f | ||
|
|
060d7d7a60 | ||
|
|
0f777b1cd8 | ||
|
|
3aaa626fd6 | ||
|
|
97f1bbc219 | ||
|
|
2f13c73c20 | ||
|
|
e051c6df2a | ||
|
|
66132ee94b | ||
|
|
c99e556ba8 | ||
|
|
c73cc82294 | ||
|
|
965709a2d0 | ||
|
|
047a91d205 | ||
|
|
dbcac5bb33 | ||
|
|
51ff4766e8 | ||
|
|
4810534025 | ||
|
|
3ff4fedab1 | ||
|
|
38590d0357 | ||
|
|
302dcd4aa0 | ||
|
|
58909659f0 | ||
|
|
5aee214f75 | ||
|
|
5ae08ab240 | ||
|
|
342abeaa70 | ||
|
|
a9c85c78e2 | ||
|
|
189b762162 | ||
|
|
e30a4376af | ||
|
|
5322020005 | ||
|
|
6a458223b1 | ||
|
|
ad90eb7a7c | ||
|
|
c65c7678b1 | ||
|
|
dc12867364 | ||
|
|
a1e255a846 | ||
|
|
3daff81110 | ||
|
|
dc12e93f6e | ||
|
|
ac5cf30583 | ||
|
|
2112d6a1b5 | ||
|
|
a424b368a0 | ||
|
|
6e2da14e0e | ||
|
|
01ac580441 | ||
|
|
ce02010542 | ||
|
|
4ad18d1219 | ||
|
|
2e02df9002 | ||
|
|
23f9aeded0 | ||
|
|
cd4833e6de | ||
|
|
e522839aa0 | ||
|
|
cfb6fa89d9 | ||
|
|
bd3754dc8b | ||
|
|
3beb29eb0a | ||
|
|
392badd940 | ||
|
|
d0860270ac | ||
|
|
40414d35a3 | ||
|
|
7cfa8be4a5 | ||
|
|
448f06fe5b | ||
|
|
e72b89b117 | ||
|
|
90e54882ef | ||
|
|
07e0d4e992 | ||
|
|
c326ab68f0 | ||
|
|
023061e370 | ||
|
|
fce2f218f5 | ||
|
|
6e73f627d6 | ||
|
|
4817b4e235 | ||
|
|
37cc867537 | ||
|
|
4349cc83e4 | ||
|
|
6282af73bf | ||
|
|
f09e8916c3 | ||
|
|
7acb0e0d4d | ||
|
|
77ba059635 | ||
|
|
1642d20d46 | ||
|
|
ec97514bdb | ||
|
|
e1dedd638b | ||
|
|
82ed704060 | ||
|
|
c2a3ea6f12 | ||
|
|
2b0696080c | ||
|
|
edbd58b102 | ||
|
|
ec2f4146c3 | ||
|
|
cd1c327173 | ||
|
|
b2b079fc5b | ||
|
|
a25a60ad55 | ||
|
|
4108515fc6 | ||
|
|
e30cffc425 | ||
|
|
2e7a691371 | ||
|
|
15dac27fac | ||
|
|
b7e2749312 | ||
|
|
3c0c7af552 | ||
|
|
6b0e63f9e5 | ||
|
|
5879acbedf | ||
|
|
000c855038 | ||
|
|
c5b4d149b5 | ||
|
|
0b920df95a | ||
|
|
57451d9970 | ||
|
|
486f5ad65e | ||
|
|
c1dcded268 | ||
|
|
ba8ab407fe | ||
|
|
9cd7af2648 | ||
|
|
f92d77cea5 | ||
|
|
8540a02561 | ||
|
|
2cc352cacd | ||
|
|
5fdf0f8094 | ||
|
|
2796ffa9af | ||
|
|
bde08e0868 | ||
|
|
9459d72590 | ||
|
|
b93a2cbf04 | ||
|
|
82af80d411 | ||
|
|
f85c6d6495 | ||
|
|
11e25b25fb | ||
|
|
125caf1d4b | ||
|
|
7b2761790f | ||
|
|
0a5b75582a | ||
|
|
65490c00c9 | ||
|
|
d6f4bf8f03 | ||
|
|
a9aea129e7 | ||
|
|
16b2735ca4 | ||
|
|
eaee0a952d |
2
.github/workflows/ci-on-release.yml
vendored
2
.github/workflows/ci-on-release.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
steps:
|
||||
- name: Generate a token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@v2
|
||||
uses: actions/create-github-app-token@v3
|
||||
with:
|
||||
app-id: ${{ vars.RELEASE_DOCKER_ID }}
|
||||
private-key: ${{ secrets.RELEASE_DOCKER_SECRET }}
|
||||
|
||||
2
.github/workflows/phan.yml
vendored
2
.github/workflows/phan.yml
vendored
@@ -90,7 +90,7 @@ jobs:
|
||||
cs2pr --prepend-filename --prepend-source --notices-as-warnings _phan.xml
|
||||
|
||||
- name: Provide phan log as artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
if: ${{ always() }}
|
||||
with:
|
||||
name: phan-srcrt
|
||||
|
||||
2
.github/workflows/phpstan.yml
vendored
2
.github/workflows/phpstan.yml
vendored
@@ -122,7 +122,7 @@ jobs:
|
||||
key: phpstan-cache-${{ matrix.php-version }}-${{ env.CACHE_KEY_PART }}-${{
|
||||
github.run_id }}
|
||||
- name: Provide phpstan log as artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
if: ${{ always() }}
|
||||
with:
|
||||
name: phpstan-srcrt
|
||||
|
||||
158
.github/workflows/pr-18.yml
vendored
Normal file
158
.github/workflows/pr-18.yml
vendored
Normal file
@@ -0,0 +1,158 @@
|
||||
# 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:
|
||||
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
|
||||
|
||||
# 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:
|
||||
# 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@v3
|
||||
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@v6
|
||||
|
||||
# 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"
|
||||
|
||||
# 3) 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_target' }}
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
V18_LABEL: ${{ env.V18_LABEL }}
|
||||
REPO: ${{ github.repository }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
echo "Adding label '$V18_LABEL' to PR #${PR_NUMBER}"
|
||||
response=$(curl -s -o /dev/null -w "%{http_code}" -X POST \
|
||||
-H "Authorization: Bearer ${GH_TOKEN}" \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
"https://api.github.com/repos/${REPO}/issues/${PR_NUMBER}/labels" \
|
||||
-d "{\"labels\": [\"${V18_LABEL}\"]}")
|
||||
|
||||
if [ "$response" -eq 200 ]; then
|
||||
echo "Label added successfully."
|
||||
else
|
||||
echo "Warning: failed to add label '$V18_LABEL' to PR #${PR_NUMBER} (HTTP $response). Continuing."
|
||||
fi
|
||||
|
||||
# 4) Compute final reviewers list excluding the PR author
|
||||
- name: Compute reviewers (exclude PR author)
|
||||
if: ${{ github.event_name == 'pull_request_target' }}
|
||||
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>}"
|
||||
|
||||
# 5) 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_target' && 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 }}
|
||||
REPO: ${{ github.repository }}
|
||||
run: |
|
||||
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"
|
||||
response=$(curl -s -o /dev/null -w "%{http_code}" -X POST \
|
||||
-H "Authorization: Bearer ${GH_TOKEN}" \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
"https://api.github.com/repos/${REPO}/pulls/${PR_NUMBER}/requested_reviewers" \
|
||||
-d "{\"reviewers\": [\"${r_trimmed}\"]}")
|
||||
|
||||
if [ "$response" -eq 201 ] || [ "$response" -eq 200 ]; then
|
||||
echo "Added reviewer: $r_trimmed"
|
||||
SUCCESS=$((SUCCESS+1))
|
||||
else
|
||||
echo "Warning: failed to add reviewer: $r_trimmed (HTTP $response)"
|
||||
FAILED+=("$r_trimmed")
|
||||
fi
|
||||
done
|
||||
if [ $SUCCESS -eq 0 ]; then
|
||||
echo "Error: none of the configured reviewers could be added: ${FAILED[*]:-<none>}"
|
||||
exit 1
|
||||
else
|
||||
echo "Reviewers added: ${SUCCESS}. Failed to add: ${FAILED[*]:-none}"
|
||||
fi
|
||||
|
||||
# 6) 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."
|
||||
2
.github/workflows/pre-commit.yml
vendored
2
.github/workflows/pre-commit.yml
vendored
@@ -169,7 +169,7 @@ jobs:
|
||||
key: pre-commit-4|${{ env.pythonLocation }}|${{ hashFiles('.pre-commit-config.yaml') }}
|
||||
# Upload result log files of precommit into the Artifact shared store
|
||||
- name: Provide log as artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
if: ${{ ! cancelled() }}
|
||||
with:
|
||||
name: precommit-logs
|
||||
|
||||
2
.github/workflows/windows-ci.yml
vendored
2
.github/workflows/windows-ci.yml
vendored
@@ -173,7 +173,7 @@ jobs:
|
||||
in: ${{ env.PHPUNIT_LOG }}
|
||||
|
||||
- name: Provide dolibarr and phpunit logs as artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
if: ${{ ! cancelled() }}
|
||||
with:
|
||||
name: win-ci-logs
|
||||
|
||||
@@ -157,6 +157,7 @@ install:
|
||||
#sudo apt install composer
|
||||
composer -V
|
||||
sudo composer -n config -g vendor-dir htdocs/includes
|
||||
sudo composer -n config audit.block-insecure false
|
||||
sudo chmod -R a+rwx /home/travis/.config/composer
|
||||
echo
|
||||
|
||||
|
||||
10
COPYRIGHT
10
COPYRIGHT
@@ -12,11 +12,16 @@ These dependencies are listed in the bottom of this file.
|
||||
The Dolibarr images resources (available in the doc directory) is distributed under the Creative Commons Attribution 4.0 International license (CC BY 4.0).
|
||||
|
||||
|
||||
Trademark usage
|
||||
---------------
|
||||
|
||||
The name Dolibarr is a trademark initially registered by Laurent Destailleur and ceased to the Dolibarr foundation. You can use the name Dolibarr
|
||||
for your own need as long as you follow the rules defined on the page https://wiki.dolibarr.org/index.php/Rules_to_use_the_brand_name_%22Dolibarr%22
|
||||
The use of the name DoliStore is also restricted to the same rules defined on https://wiki.dolibarr.org/index.php/Rules_to_use_the_brand_name_%22Dolibarr%22
|
||||
|
||||
|
||||
License of third party libraries
|
||||
--------------------------------
|
||||
|
||||
Licence of dependencies of third-party components used by Dolibarr (all compatible with the Licence of Dolibarr):
|
||||
|
||||
@@ -39,7 +44,6 @@ PHP-Iban 4.1.1 LGPL-3+ Yes
|
||||
PHP-Imap 2.7.2 MIT License Yes Library to use IMAP with OAuth
|
||||
PHPoAuthLib 0.8.2 MIT License Yes Library to provide oauth1 and oauth2 to different service
|
||||
PHPPrintIPP 1.3 GPL-2+ Yes Library to send print IPP requests
|
||||
PrestaShop-WS-Lib 94feb5f OSL-3.0 No Library providing API client for Prestashop.
|
||||
PSR/Logs 1.0 MIT License Yes Library for logs (used by DebugBar)
|
||||
PSR/simple-cache ? MIT License Yes Library for cache (used by PHPSpreadSheet)
|
||||
Restler 3.1.1 LGPL-3+ Yes Library to develop REST Web services (+ swagger-ui js lib into dir explorer)
|
||||
@@ -67,13 +71,9 @@ jQuery jPicker 1.1.6 GPL and MIT License Yes
|
||||
jQuery jqueryFileTree 1.0.1 GPL and MIT License Yes JS library for filetree
|
||||
jQuery jquerytreeview 1.4.1 MIT License Yes JS library for filetree
|
||||
jQuery TableDnD 0.6 GPL and MIT License Yes JS library plugin TableDnD (to reorder table rows)
|
||||
jQuery Timepicker 1.1.0 GPL and MIT License Yes JS library Timepicker addon for Datepicker
|
||||
jsGanttImproved 2.8.10 BSD License Yes JS library (to build Gantt reports)
|
||||
SwaggerUI 2.2.10 GPL-2+ Yes JS library to offer the REST API explorer
|
||||
|
||||
Image libraries:
|
||||
Octicons 8.1 MIT Yes
|
||||
|
||||
Font libraries:
|
||||
Fontawesome 5.13 Font Awesome Free Licence Yes
|
||||
|
||||
|
||||
30
ChangeLog
30
ChangeLog
@@ -2,6 +2,28 @@
|
||||
English Dolibarr ChangeLog
|
||||
--------------------------------------------------------------
|
||||
|
||||
|
||||
***** ChangeLog for 23.0.1 compared to 23.0.0 *****
|
||||
|
||||
FIX: Removed SQL error on install process.
|
||||
FIX: #37412 Better fix
|
||||
FIX: Added user filtering for displaying leave in the calendar (#37385)
|
||||
FIX: Bad value when entering price with multicurrency included tax.
|
||||
FIX: Better compatibility for module using condition object-> in tabs
|
||||
FIX: CSS
|
||||
FIX: Fetch of lead status
|
||||
FIX: Fix doc preview in comm card
|
||||
FIX: height of confirm popup
|
||||
FIX: option MAIN_USE_TITLE_FOR_USER was on update and not on create
|
||||
FIX: Order API: delete order returns wrong http response in case order could not be deleted (#37472)
|
||||
FIX: Pb with import of agendaevents. Date and import id not visible.
|
||||
FIX: read_supplier_price filter for stock complement (#37417)
|
||||
FIX: Reload page after check holiday for save param (#37410)
|
||||
FIX: Several trouble with demo docker packages. More secured way to use install.force.php file
|
||||
FIX: translation on multiselect with rich label - Fix CSS public ticket
|
||||
FIX: update COPYRIGHT file to reflect removed libraries in v23.0.0
|
||||
|
||||
|
||||
***** ChangeLog for 23.0.0 compared to 22.0 *****
|
||||
|
||||
For users:
|
||||
@@ -205,18 +227,18 @@ The following changes may create regressions for some external modules, but were
|
||||
* Because of the new TRIGGER_PREFIX property, the triggers SUPPLIER_PRODUCT_BUYPRICE_XXX are renamed into PRODUCT_BUYPRICE_XXX.
|
||||
* Function img_pdf() has been removed. Replace it with img_picto('', 'pdf') if you were using it.
|
||||
* The method run_trigger() was deprecated 10+ years ago in favor of runTrigger(). It has been removed. Change your trigger file if you still use it.
|
||||
* Property ->picto of module descriptors must contains the image extension if it is not a font awesome tag. Example: $this->picto="mymoduleimg.png";
|
||||
* Property ->picto of module descriptors or menu entries must contains the image extension if it is not a font awesome tag. Example: $this->picto="mymoduleimg.png";
|
||||
* Stock movement API GET method output variable names has been harmonized with POST input parameter names
|
||||
* Concatenation into computed property of extrafields is off by default. You can enable it from conf.php file by adding dol_concat to list of allowed function in $dolibarr_main_restrict_eval_methods,
|
||||
For example: $dolibarr_main_restrict_eval_methods='getDolGlobalString,getDolGlobalInt,getDolCurrency,fetchNoCompute,hasRight,isModEnabled,isStringVarMatching,abs,min,max,round,dol_now,dol_concat,preg_match';
|
||||
* Old variable $obj and $object are no more allowed into on the fly evaluated strings like computed or conditions on extrafields. Use $objectoffield to get current object
|
||||
Also if you were using temporary variables int a computed extrafields, the nameot temporary variable must match $var123.
|
||||
Also if you were using temporary variables in a computed extrafield, the name of the temporary variable must match $var123.
|
||||
* The hidden constant MAIN_ALLOW_UNSECURED_SPECIAL_CHARS_IN_DOL_EVAL in database has been replaced with the
|
||||
variable $dolibarr_main_allow_unsecured_special_chars_in_dol_eval into file conf/conf.php
|
||||
* $conf use into "computed formulae" of etrafields is now deprecated (not yet forbidden). You can replace use of $conf->currency by the new method getCurrency() and $conf->global->xxx by getDolGlobalString('xxx').
|
||||
* $conf use into "computed formulae" of extrafields is now deprecated (not yet forbidden). You can replace use of $conf->currency by the new method getCurrency() and $conf->global->xxx by getDolGlobalString('xxx').
|
||||
* $user->rights->module->perms is also deprecated. You can use $user->hasRight() instead.
|
||||
* The API endpoint /proposals/{id}/contact/{contactid}/{type}/{source} is now {id}/contact/{contactid}/{type} to match same behaviour than order and invoices.
|
||||
* The API endpoint /interventions/{id}/reopen hase been removed in favor of /interventions/{id}/settodraft
|
||||
* The API endpoint /interventions/{id}/reopen has been removed in favor of /interventions/{id}/settodraft
|
||||
* If the module geoipmaxmind is used, you must resubmit the geoip data file as it is now stored differently.
|
||||
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ Security report are valid only on any current stable version for the last 5 majo
|
||||
|
||||
To report a vulnerability, for a private report, you can:
|
||||
|
||||
- Send your report as an issue on https://github.com/Dolibarr/dolibarr/issues or on GitHub Vulnerability Disclosure Program tool (VDP): https://github.com/Dolibarr/dolibarr/security/advisories (recommended)
|
||||
- Send your report as an issue on https://github.com/Dolibarr/dolibarr/issues or on GitHub Vulnerability Disclosure Program tool (VDP): https://github.com/Dolibarr/dolibarr/security/advisories (recommended). Do 1 report only per vulnerability. Reports combining several vulnerabilities, as well as reports generated using IA will be rejected.
|
||||
<!--
|
||||
- Send your report on Vulnerability Disclosure Program (VDP) [https://app.yogosha.com/cvd/dolibarr/10VxeNx6Ui3rSEhAgX63US](https://app.yogosha.com/cvd/dolibarr/10VxeNx6Ui3rSEhAgX63US) (recommended for everybody)
|
||||
- Or if you have permissions, use GitHub security advisory at [https://github.com/Dolibarr/dolibarr/security/advisories/new](https://github.com/Dolibarr/dolibarr/security/advisories/new)
|
||||
|
||||
@@ -3,19 +3,30 @@
|
||||
# See README.md to know how to create a Dolibarr env with docker
|
||||
set -ex
|
||||
|
||||
if [ "${PHP_INI_DIR}" == "" ]; then
|
||||
echo
|
||||
echo This script must not be run directly. It is used by the Dockerfile
|
||||
echo See README.md
|
||||
echo
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ "${HOST_USER_ID}" == "" ]; then
|
||||
echo "Define HOST_USER_ID to your user ID before starting"
|
||||
echo "Define HOST_USER_ID to your user ID before starting (example: www-data)"
|
||||
exit 1
|
||||
fi
|
||||
if [ "${HOST_GROUP_ID}" == "" ]; then
|
||||
echo "Define HOST_GROUP_ID to your group ID before starting (example: www-data)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
usermod -u "${HOST_USER_ID}" www-data
|
||||
groupmod -g "${HOST_USER_ID}" www-data
|
||||
groupmod -g "${HOST_GROUP_ID}" www-data
|
||||
|
||||
echo "[docker-run] => update '${PHP_INI_DIR}/conf.d/dolibarr-php.ini'"
|
||||
cat <<EOF > "${PHP_INI_DIR}/conf.d/dolibarr-php.ini"
|
||||
date.timezone = ${PHP_INI_DATE_TIMEZONE:-UTC}
|
||||
memory_limit = ${PHP_INI_MEMORY_LIMIT:-256M}
|
||||
display_errors = Off
|
||||
EOF
|
||||
|
||||
exec apache2-foreground
|
||||
|
||||
@@ -1,24 +1,51 @@
|
||||
# How to use or run Dolibarr with Docker ?
|
||||
# How to build and run your local version of Dolibarr, quickly with Docker ?
|
||||
|
||||
|
||||
## For a fast run of a demo of the local version, you can build the docker image from the source repository by running
|
||||
## For a fast run of a demo of the local version, you can build a docker image pointing to your source repository
|
||||
|
||||
This process using the Dockerfile and docker-compose.yml file is a config file to use to build and run Dolibarr in the
|
||||
current workspace with Docker. This docker image is intended for **development usage** (See next chapter for a production usage).
|
||||
|
||||
Warning: There is no persistency of database. This process is for dev purpose only.
|
||||
|
||||
|
||||
Build the containers:
|
||||
|
||||
git clone https://github.com/Dolibarr/dolibarr.git dolibarr
|
||||
|
||||
cd dolibarr/docker
|
||||
cd dolibarr/dev/build/docker
|
||||
|
||||
sudo docker-compose build
|
||||
sudo docker-compose build [--no-cache]
|
||||
or
|
||||
sudo docker compose build [--no-cache]
|
||||
|
||||
|
||||
Launch the containers:
|
||||
|
||||
sudo -s
|
||||
|
||||
export HOST_USER_ID=$(id -u)
|
||||
export HOST_GROUP_ID=$(id -g)
|
||||
export MYSQL_ROOT_PWD=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 13; echo)
|
||||
|
||||
docker-compose up -d
|
||||
or
|
||||
docker compose up -d
|
||||
|
||||
Check that server process is on:
|
||||
|
||||
docker compose ps
|
||||
|
||||
Warning: There is no persistency of data. This process is for dev purpose only.
|
||||
Check also logs of starting container:
|
||||
|
||||
docker logs dolibarr-web-dev
|
||||
|
||||
The URL to go to the installed Dolibarr is :
|
||||
|
||||
http://0.0.0.0:8080
|
||||
|
||||
To delete processes:
|
||||
|
||||
docker compose kill stop
|
||||
|
||||
|
||||
## For a more robust or a production usage
|
||||
|
||||
@@ -1,28 +1,27 @@
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
mariadb-prod:
|
||||
container_name: dolibarr-mariadb-prod
|
||||
mariadb-dev:
|
||||
container_name: dolibarr-mariadb-dev
|
||||
image: mariadb:latest
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: $MYSQL_ROOT_PWD
|
||||
MYSQL_DATABASE: "dolibarr-prod"
|
||||
MARIADB_ROOT_PASSWORD: $MYSQL_ROOT_PWD
|
||||
MARIADB_DATABASE: "dolibarr-dev"
|
||||
|
||||
web:
|
||||
container_name: dolibarr-web-prod
|
||||
container_name: dolibarr-web-dev
|
||||
build: .
|
||||
|
||||
volumes:
|
||||
- ../../htdocs:/var/www/html/
|
||||
- ../../documents:/var/www/html/documents
|
||||
- ../../../htdocs:/var/www/html/
|
||||
- ../../../documents:/var/www/documents
|
||||
|
||||
depends_on:
|
||||
- mariadb-prod
|
||||
- mariadb-dev
|
||||
environment:
|
||||
HOST_USER_ID: $HOST_USER_ID
|
||||
HOST_GROUP_ID: $HOST_GROUP_ID
|
||||
DOLI_ROOT_PASSWORD: $MYSQL_ROOT_PWD
|
||||
DOLI_DATABASE: "dolibarr-prod"
|
||||
DOLI_DB_SERVER: "mariadb-prod"
|
||||
DOLI_DATABASE: "dolibarr-dev"
|
||||
DOLI_DB_SERVER: "mariadb-dev"
|
||||
DOLI_DB_USER: "root"
|
||||
DOLI_DB_PASSWORD: $MYSQL_ROOT_PWD
|
||||
ports:
|
||||
- "8080:80"
|
||||
|
||||
@@ -10,6 +10,16 @@ if [ "${PHP_INI_DIR}" == "" ]; then
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ "${HOST_USER_ID}" == "" ]; then
|
||||
echo "Define HOST_USER_ID to your user ID before starting (example: www-data)"
|
||||
exit 1
|
||||
fi
|
||||
if [ "${HOST_GROUP_ID}" == "" ]; then
|
||||
echo "Define HOST_GROUP_ID to your group ID before starting (example: www-data)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
usermod -u "${HOST_USER_ID}" www-data
|
||||
groupmod -g "${HOST_GROUP_ID}" www-data
|
||||
|
||||
@@ -29,6 +39,7 @@ date.timezone = ${PHP_INI_DATE_TIMEZONE:-UTC}
|
||||
memory_limit = ${PHP_INI_MEMORY_LIMIT:-256M}
|
||||
EOF
|
||||
|
||||
cp /var/www/html/install/install.forced.docker.php /var/www/html/install/install.forced.php
|
||||
echo cp /var/www/html/install/install.forced.sample.php /var/www/html/install/install.forced.php
|
||||
cp /var/www/html/install/install.forced.sample.php /var/www/html/install/install.forced.php
|
||||
|
||||
exec apache2-foreground
|
||||
|
||||
@@ -60,9 +60,19 @@ my %ALTERNATEPATH = (
|
||||
);
|
||||
|
||||
my $RPMSUBVERSION = "auto"; # auto use value found into BUILD
|
||||
my $RPMDIR = "$ENV{HOME}/rpmbuild"; # by default
|
||||
if ( -d "/usr/src/redhat" ) { $RPMDIR = "/usr/src/redhat"; } # redhat
|
||||
if ( -d "/usr/src/packages" ) { $RPMDIR = "/usr/src/packages"; } # opensuse
|
||||
if ( -d "/usr/src/RPM" ) { $RPMDIR = "/usr/src/RPM"; } # mandrake
|
||||
|
||||
if ( ! -d "$RPMDIR/SOURCES" ) {
|
||||
mkdir("$RPMDIR/SOURCES");
|
||||
if ( ! -d "$RPMDIR/SOURCES" ) {
|
||||
print "Failed to create dir $RPMDIR/SOURCES\n";
|
||||
sleep 2;
|
||||
exit 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
use vars qw/ $REVISION $VERSION /;
|
||||
my $VERSION = "4.0";
|
||||
@@ -203,10 +213,10 @@ while (<$IN>) {
|
||||
}
|
||||
}
|
||||
close $IN;
|
||||
open( my $IN2, "<", $SOURCE . "/htdocs/filefunc.inc.php" )
|
||||
open( my $IN2, "<", $SOURCE . "/htdocs/version.inc.php" )
|
||||
or die "Error: Can't open version file "
|
||||
. $SOURCE
|
||||
. "/htdocs/filefunc.inc.php\n";
|
||||
. "/htdocs/version.inc.php\n";
|
||||
while (<$IN2>) {
|
||||
if ( $_ =~ /define\('DOL_MINOR_VERSION',\s*'([\d\.a-z\-]+)'\)/ ) {
|
||||
$MINORVERSION = $1;
|
||||
@@ -625,10 +635,8 @@ if ($nboftargetok) {
|
||||
. $BUILD . '"' . "\n";
|
||||
$ret =
|
||||
`git tag -a -f -m "$MAJOR.$MINOR.$BUILD" "$MAJOR.$MINOR.$BUILD"`;
|
||||
print 'Run git push '
|
||||
. $GITREMOTENAME
|
||||
. ' -f "$MAJOR.$MINOR.$BUILD"' . "\n";
|
||||
$ret = `git push $GITREMOTENAME -f -"$MAJOR.$MINOR.$BUILD"`;
|
||||
print "Run git push $GITREMOTENAME -f '$MAJOR.$MINOR.$BUILD'\n";
|
||||
$ret = `git push $GITREMOTENAME -f "$MAJOR.$MINOR.$BUILD"`;
|
||||
|
||||
#$ret=`git push -f origin "$MAJOR.$MINOR.$BUILD"`;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
diff -up htdocs/filefunc.inc.php.patch htdocs/filefunc.inc.php
|
||||
--- htdocs/filefunc.inc.php.patch 2011-09-03 02:32:48.666952000 +0200
|
||||
+++ htdocs/filefunc.inc.php 2011-09-03 02:33:00.510952001 +0200
|
||||
@@ -63,8 +63,8 @@
|
||||
@@ -128,8 +128,8 @@
|
||||
$conffile = "conf/conf.php";
|
||||
$conffiletoshow = "htdocs/conf/conf.php";
|
||||
// For debian/redhat like systems
|
||||
|
||||
@@ -189,7 +189,6 @@ done >>%{name}.lang
|
||||
%_datadir/dolibarr/htdocs/expedition
|
||||
%_datadir/dolibarr/htdocs/expensereport
|
||||
%_datadir/dolibarr/htdocs/exports
|
||||
%_datadir/dolibarr/htdocs/externalsite
|
||||
%_datadir/dolibarr/htdocs/fichinter
|
||||
%_datadir/dolibarr/htdocs/fourn
|
||||
%_datadir/dolibarr/htdocs/ftp
|
||||
|
||||
@@ -270,7 +270,6 @@ done >>%{name}.lang
|
||||
%_datadir/dolibarr/htdocs/expedition
|
||||
%_datadir/dolibarr/htdocs/expensereport
|
||||
%_datadir/dolibarr/htdocs/exports
|
||||
%_datadir/dolibarr/htdocs/externalsite
|
||||
%_datadir/dolibarr/htdocs/fichinter
|
||||
%_datadir/dolibarr/htdocs/fourn
|
||||
%_datadir/dolibarr/htdocs/ftp
|
||||
|
||||
@@ -187,7 +187,6 @@ done >>%{name}.lang
|
||||
%_datadir/dolibarr/htdocs/expedition
|
||||
%_datadir/dolibarr/htdocs/expensereport
|
||||
%_datadir/dolibarr/htdocs/exports
|
||||
%_datadir/dolibarr/htdocs/externalsite
|
||||
%_datadir/dolibarr/htdocs/fichinter
|
||||
%_datadir/dolibarr/htdocs/fourn
|
||||
%_datadir/dolibarr/htdocs/ftp
|
||||
|
||||
@@ -197,7 +197,6 @@ done >>%{name}.lang
|
||||
%_datadir/dolibarr/htdocs/expedition
|
||||
%_datadir/dolibarr/htdocs/expensereport
|
||||
%_datadir/dolibarr/htdocs/exports
|
||||
%_datadir/dolibarr/htdocs/externalsite
|
||||
%_datadir/dolibarr/htdocs/fichinter
|
||||
%_datadir/dolibarr/htdocs/fourn
|
||||
%_datadir/dolibarr/htdocs/ftp
|
||||
|
||||
@@ -184,7 +184,7 @@ if ($confirm == 'regenerate') {
|
||||
|
||||
print "For ROWID ".$obj->rowid." - Previous hash = ".$previoushash."\n";
|
||||
print "Signature in db: ".$obj->signature." - New calculated: ".$signature['calculatedsignature']."\n";
|
||||
if ($obj->signature != $signature['calculatedsignature']) {
|
||||
if (!empty($signature['calculatedsignature']) && $obj->signature != $signature['calculatedsignature']) {
|
||||
$tmpsql = "UPDATE ".MAIN_DB_PREFIX."blockedlog SET signature = '".$db->escape($signature['calculatedsignature'])."'";
|
||||
$tmpsql .= " WHERE rowid = ".((int) $obj->rowid);
|
||||
|
||||
|
||||
125
dev/pullmerge.sh
Executable file
125
dev/pullmerge.sh
Executable file
@@ -0,0 +1,125 @@
|
||||
#!/bin/bash
|
||||
|
||||
param1="$1"
|
||||
|
||||
|
||||
# Configuration des versions
|
||||
START=14
|
||||
END=23
|
||||
DEV_DIR="dolibarr_dev"
|
||||
DEV_BRANCH="develop"
|
||||
|
||||
if [ "x$2" != "x" ]; then
|
||||
START=$2
|
||||
fi
|
||||
|
||||
if [[ "$param1" != 'pull' && "$param1" != 'merge' && "$param1" != 'all' ]]; then
|
||||
echo "Usage: $0 <pull|merge|all> [xx]"
|
||||
echo " where xx is version to start from (14, 18, ...)"
|
||||
echo
|
||||
echo "Example:"
|
||||
echo "$0 pull # For pull only"
|
||||
echo "$0 merge 20 # For merge only"
|
||||
echo "$0 all # For pull then merge"
|
||||
echo
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "x$param1" = "xall" ] || [ "x$param1" = "xpull" ]; then
|
||||
echo "--- STEP Pull : Update directories (git pull) ---"
|
||||
|
||||
for (( i=$START; i<=$END; i++ ))
|
||||
do
|
||||
DIR="dolibarr_$i.0"
|
||||
|
||||
if [ -d "$DIR" ]; then
|
||||
echo "*** Update of $DIR..."
|
||||
cd "$DIR" || exit
|
||||
|
||||
# We want to be sure we are on good brnahc and we pull
|
||||
git checkout "$i.0" && git pull
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "ERREUR : The pull on $DIR has failed. We stop here."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd ..
|
||||
else
|
||||
echo "Warning : The directory $DIR does not exists, switch to next one."
|
||||
fi
|
||||
done
|
||||
|
||||
# Update branch dev
|
||||
if [ -d "$DEV_DIR" ]; then
|
||||
echo "*** Update of $DEV_DIR..."
|
||||
cd "$DEV_DIR" || exit
|
||||
git checkout "$DEV_BRANCH" && git pull || { echo "Error pull $DEV_DIR"; exit 1; }
|
||||
cd ..
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
if [ "x$param1" = "xall" ] || [ "x$param1" = "xmerge" ]; then
|
||||
echo -e "\n--- STEP Merge : Cascade of Merges (xx-1 -> xx) ---"
|
||||
|
||||
for (( i=$((START+1)); i<=$END; i++ ))
|
||||
do
|
||||
PREV=$((i-1))
|
||||
CURRENT=$i
|
||||
DIR_CURRENT="dolibarr_$CURRENT.0"
|
||||
DIR_PREV="dolibarr_$PREV.0"
|
||||
|
||||
if [ -d "$DIR_CURRENT" ] && [ -d "$DIR_PREV" ]; then
|
||||
echo "*** Merge of dolibarr_$PREV to dolibarr_$CURRENT..."
|
||||
|
||||
echo "Go on dir $DIR_CURRENT"
|
||||
cd "$DIR_CURRENT" || exit
|
||||
|
||||
# Add previous directory to a local remote for the merge
|
||||
# This allow to merge without the need of a common remote repository, which may not be the case here as we have only local clones.
|
||||
REMOTE_NAME="local_prev"
|
||||
git remote remove $REMOTE_NAME 2>/dev/null
|
||||
git remote add $REMOTE_NAME "../$DIR_PREV"
|
||||
git fetch $REMOTE_NAME
|
||||
|
||||
# Try to merge
|
||||
git merge "$REMOTE_NAME/$PREV.0" -m "Automated merge from $PREV.0 to $CURRENT.0 by tool pullmerge.sh"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Success on merge. Try to push..."
|
||||
git push
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "ERROR : The push of $DIR_CURRENT has failed. We stop here."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "ERROR : Conflict or error on merge into $DIR_CURRENT. We stop here."
|
||||
# Optionnel : git merge --abort pour laisser le repo propre
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Nettoyage du remote temporaire
|
||||
git remote remove $REMOTE_NAME
|
||||
cd ..
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -d "$DEV_DIR" ] && [ -d "dolibarr_$END.0" ]; then
|
||||
echo "*** Merge final of dolibarr_$END.0 vers $DEV_DIR..."
|
||||
cd "$DEV_DIR" || exit
|
||||
|
||||
REMOTE_NAME="local_prev"
|
||||
git remote remove $REMOTE_NAME 2>/dev/null
|
||||
git remote add $REMOTE_NAME "../dolibarr_$END.0"
|
||||
git fetch $REMOTE_NAME
|
||||
|
||||
git merge "$REMOTE_NAME/$END.0" -m "Automated merge from $END.0 to $DEV_BRANCH" && git push || exit 1
|
||||
|
||||
git remote remove $REMOTE_NAME
|
||||
cd ..
|
||||
fi
|
||||
fi
|
||||
|
||||
echo -e "\nSuccess !"
|
||||
@@ -47,7 +47,7 @@ function save_db_cache() (
|
||||
# Get the target version from the version.inc.php file
|
||||
target_version=$(sed -n "s/.*define('DOL_\\(MAJOR_\\)\\?VERSION',[[:space:]]*'\\([0-9.]*\\).*/\\2/p" ../version.inc.php) ; echo $target_version
|
||||
# Default in case that failed
|
||||
target_version=${target_version:=22.0.0}
|
||||
target_version=${target_version:=23.0.0}
|
||||
|
||||
# Sequence of versions for upgrade process (to be completed)
|
||||
VERSIONS=("3.5.0" "3.6.0" "3.7.0" "3.8.0" "3.9.0")
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
$sql .= " AND cs.periode >= '".$db->idate($search_date_limit_start)."'";
|
||||
$sql .= " OR (cs.periode IS NULL AND cs.date_ech between '".$db->idate(dol_get_first_day($year))."' AND '".$db->idate(dol_get_last_day($year))."')";
|
||||
$sql .= " SET reponses = '".$db->escape($nouveauchoix)."'";
|
||||
$sql .= " cs.rowid, cs.libelle, cs.fk_type as type, cs.periode as period, cs.date_ech, cs.amount as total,";
|
||||
$sql .= " cs.rowid, cs.libelle as label, cs.fk_type as type, cs.periode as period, cs.date_ech, cs.amount as total,";
|
||||
$sql.= " ".MAIN_DB_PREFIX."notify_def as nd,";
|
||||
$sql.= " AND nd.fk_action = ad.rowid";
|
||||
$sql.= " WHERE u.rowid = nd.fk_user";
|
||||
|
||||
@@ -274,7 +274,6 @@ return [
|
||||
'disableremove' => 'int<0,1>',
|
||||
'dolibarr_main_authentication' => 'string',
|
||||
'dolibarr_main_data_root' => 'string',
|
||||
'dolibarr_main_data_root' => 'string',
|
||||
'dolibarr_main_db_encrypted_pass' => 'string',
|
||||
'dolibarr_main_db_host' => 'string',
|
||||
'dolibarr_main_db_pass' => 'string',
|
||||
@@ -346,6 +345,7 @@ return [
|
||||
'htdocs/includes/',
|
||||
'htdocs/install/doctemplates/websites/',
|
||||
'htdocs/core/class/lessc.class.php', // External library
|
||||
'htdocs/admin/tools/ui/',
|
||||
PHAN_DIR . '/stubs/',
|
||||
],
|
||||
//'exclude_file_regex' => '@^vendor/.*/(tests?|Tests?)/@',
|
||||
@@ -451,6 +451,7 @@ return [
|
||||
|
||||
'PhanCompatibleNegativeStringOffset', // return false positive
|
||||
'PhanPluginConstantVariableBool', // a lot of false positive, in most cases, we want to keep the code as it is
|
||||
'PhanPluginConstantVariableNull', // a lot of false positive, in most cases, we want to keep the code as it is
|
||||
// 'PhanPluginUnknownArrayPropertyType', // Helps find missing array keys or mismatches, remaining occurrences are likely unused properties
|
||||
'PhanTypeArraySuspiciousNullable', // About 440 occurrences
|
||||
// 'PhanTypeInvalidDimOffset', // Helps identify missing array indexes in types or reference to unset indexes
|
||||
|
||||
@@ -68,6 +68,7 @@ $config['suppress_issue_types'] = [
|
||||
|
||||
'PhanCompatibleNegativeStringOffset', // return false positive
|
||||
'PhanPluginConstantVariableBool', // a lot of false positive, in most cases, we want to keep the code as it is
|
||||
'PhanPluginConstantVariableNull', // a lot of false positive, in most cases, we want to keep the code as it is
|
||||
// 'PhanPluginUnknownArrayPropertyType', // Helps find missing array keys or mismatches, remaining occurrences are likely unused properties
|
||||
'PhanTypeArraySuspiciousNullable', // About 400 cases
|
||||
// 'PhanTypeInvalidDimOffset', // Helps identify missing array indexes in types or reference to unset indexes
|
||||
|
||||
@@ -100,7 +100,7 @@ if (!$sortorder) {
|
||||
$object = new AccountingAccount($db);
|
||||
|
||||
$arrayfields = array(
|
||||
'aa.account_number' => array('label' => "AccountNumber", 'checked' => '1'),
|
||||
'aa.account_number' => array('label' => "AccountNumber", 'checked' => '1', 'csslist' => 'maxwidth50'),
|
||||
'aa.label' => array('label' => "Label", 'checked' => '1'),
|
||||
'aa.labelshort' => array('label' => "ShortLabel", 'checked' => '1'),
|
||||
'aa.account_parent' => array('label' => "Accountparent", 'checked' => '1'),
|
||||
@@ -451,6 +451,8 @@ if ($resql) {
|
||||
|
||||
include DOL_DOCUMENT_ROOT.'/core/tpl/massactions_pre.tpl.php';
|
||||
|
||||
print '<div class="neutral">';
|
||||
|
||||
// Box to select active chart of account
|
||||
print $langs->trans("Selectchartofaccounts")." : ";
|
||||
print '<select class="flat minwidth200" name="chartofaccounts" id="chartofaccounts">';
|
||||
@@ -485,7 +487,7 @@ if ($resql) {
|
||||
print ajax_combobox("chartofaccounts");
|
||||
print '<input type="'.(empty($conf->use_javascript_ajax) ? 'submit' : 'button').'" class="button button-edit small" name="change_chart" id="change_chart" value="'.dol_escape_htmltag($langs->trans("ChangeAndLoad")).'">';
|
||||
|
||||
print '<br>';
|
||||
print '</div>';
|
||||
|
||||
$parameters = array('chartofaccounts' => $chartofaccounts, 'permissiontoadd' => $permissiontoadd, 'permissiontodelete' => $permissiontodelete);
|
||||
$reshook = $hookmanager->executeHooks('formObjectOptions', $parameters, $accounting, $action); // Note that $action and $object may have been modified by hook
|
||||
@@ -530,10 +532,10 @@ if ($resql) {
|
||||
print '</td>';
|
||||
}
|
||||
if (!empty($arrayfields['aa.account_number']['checked'])) {
|
||||
print '<td class="liste_titre"><input type="text" class="flat width100" name="search_account" value="'.$search_account.'"></td>';
|
||||
print '<td class="liste_titre"><input type="text" class="flat width75" name="search_account" value="'.$search_account.'"></td>';
|
||||
}
|
||||
if (!empty($arrayfields['aa.label']['checked'])) {
|
||||
print '<td class="liste_titre"><input type="text" class="flat width150" name="search_label" value="'.$search_label.'"></td>';
|
||||
print '<td class="liste_titre"><input type="text" class="flat width100" name="search_label" value="'.$search_label.'"></td>';
|
||||
}
|
||||
if (!empty($arrayfields['aa.labelshort']['checked'])) {
|
||||
print '<td class="liste_titre"><input type="text" class="flat width100" name="search_labelshort" value="'.$search_labelshort.'"></td>';
|
||||
|
||||
@@ -27,9 +27,6 @@
|
||||
|
||||
// Load Dolibarr environment
|
||||
require '../../main.inc.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
|
||||
|
||||
/**
|
||||
* @var Conf $conf
|
||||
* @var DoliDB $db
|
||||
@@ -37,13 +34,14 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
|
||||
* @var Translate $langs
|
||||
* @var User $user
|
||||
*/
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
|
||||
|
||||
// Load translation files required by the page
|
||||
$langs->loadLangs(array("accountancy", "admin", "bills", "compta", "errors", "hrm", "salaries"));
|
||||
|
||||
$action = GETPOST('action', 'aZ09');
|
||||
$cancel = GETPOST('cancel', 'alpha');
|
||||
|
||||
$massaction = GETPOST('massaction', 'aZ09');
|
||||
$optioncss = GETPOST('optioncss', 'alpha');
|
||||
$mode = GETPOST('mode', 'aZ'); // The output mode ('list', 'kanban', 'hierarchy', 'calendar', ...)
|
||||
@@ -350,6 +348,7 @@ if ($resql) {
|
||||
print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, '', $num, $nbtotalofrecords, 'accounting_account', 0, '', '', $limit, 0, 0, 1);
|
||||
|
||||
print '<div class="info">'.$langs->trans("WarningCreateSubAccounts").'</div>';
|
||||
print '<br>';
|
||||
|
||||
$varpage = empty($contextpage) ? $_SERVER["PHP_SELF"] : $contextpage;
|
||||
$htmlofselectarray = $form->multiSelectArrayWithCheckbox('selectedfields', $arrayfields, $varpage, getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')); // This also change content of $arrayfields with user setup
|
||||
|
||||
@@ -450,7 +450,19 @@ if (empty($reshook)) {
|
||||
if ($result < 0) {
|
||||
setEventMessages($object->error, $object->errors, 'errors');
|
||||
} else {
|
||||
header("Location: " . $backtopage . "?sortfield=t.piece_num&sortorder=asc" . ($type ? '&type='.$type : ''));
|
||||
// Retrieve the actual final part number (not the temporary number)
|
||||
$piece_num = $object->piece_num;
|
||||
|
||||
$linkEntry = '<a href="'.$_SERVER["PHP_SELF"].'?piece_num='.(int) $piece_num.'">'.$langs->trans('NumMvts').': '.(int) $piece_num.'</a>';
|
||||
setEventMessages($langs->trans('RecordSaved').' - '.$linkEntry, null, 'mesgs', '', 1);
|
||||
|
||||
if (getDolGlobalInt('ACCOUNTANCY_VALID_REDIRECT_TO_CARD')) {
|
||||
// Redirection to the validated entry record
|
||||
header("Location: ".$_SERVER["PHP_SELF"]."?piece_num=".(int) $piece_num);
|
||||
} else {
|
||||
// Default behavior: return on journal view
|
||||
header("Location: ".$backtopage."?sortfield=t.piece_num&sortorder=asc".($type ? '&type='.$type : ''));
|
||||
}
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -846,30 +846,50 @@ if ($action == 'export_file') {
|
||||
}
|
||||
|
||||
// add documents in an archive for some accountancy export format
|
||||
if (getDolGlobalString('ACCOUNTING_EXPORT_MODELCSV') == AccountancyExport::$EXPORT_TYPE_QUADRATUS
|
||||
|| getDolGlobalString('ACCOUNTING_EXPORT_MODELCSV') == AccountancyExport::$EXPORT_TYPE_FEC
|
||||
|| getDolGlobalString('ACCOUNTING_EXPORT_MODELCSV') == AccountancyExport::$EXPORT_TYPE_FEC2
|
||||
) {
|
||||
$except = array();
|
||||
if (getDolGlobalInt('ACCOUNTING_EXPORT_REMOVE_INVOICE_SOURCE_FILE')) {
|
||||
$except[] = $langs->trans('Invoice');
|
||||
}
|
||||
if (getDolGlobalInt('ACCOUNTING_EXPORT_REMOVE_EXPENSEREPORT_SOURCE_FILE')) {
|
||||
$except[] = $langs->trans('ExpenseReport');
|
||||
}
|
||||
if (getDolGlobalInt('ACCOUNTING_EXPORT_REMOVE_SUPPLIERINVOICE_SOURCE_FILE')) {
|
||||
$except[] = $langs->trans('SupplierInvoice');
|
||||
}
|
||||
$exportTypesWithDocs = array(
|
||||
AccountancyExport::$EXPORT_TYPE_QUADRATUS,
|
||||
AccountancyExport::$EXPORT_TYPE_FEC,
|
||||
AccountancyExport::$EXPORT_TYPE_FEC2
|
||||
);
|
||||
|
||||
$form_question['notifiedexportfull'] = array(
|
||||
'name' => 'notifiedexportfull',
|
||||
'type' => 'checkbox',
|
||||
'label' => $langs->trans('NotifiedExportFull').(empty($except) ? '' : ' <spanc class="opacitymedium">('.$langs->trans("except").' '.implode(', ', $except).')</span>'),
|
||||
'value' => 'false',
|
||||
);
|
||||
$except = array();
|
||||
if (getDolGlobalInt('ACCOUNTING_EXPORT_REMOVE_INVOICE_SOURCE_FILE')) {
|
||||
$except[] = $langs->trans('Invoice');
|
||||
}
|
||||
if (getDolGlobalInt('ACCOUNTING_EXPORT_REMOVE_EXPENSEREPORT_SOURCE_FILE')) {
|
||||
$except[] = $langs->trans('ExpenseReport');
|
||||
}
|
||||
if (getDolGlobalInt('ACCOUNTING_EXPORT_REMOVE_SUPPLIERINVOICE_SOURCE_FILE')) {
|
||||
$except[] = $langs->trans('SupplierInvoice');
|
||||
}
|
||||
|
||||
$form_question['notifiedexportfull'] = array(
|
||||
'name' => 'notifiedexportfull',
|
||||
'type' => 'checkbox',
|
||||
'label' => $langs->trans('NotifiedExportFull').(empty($except) ? '' : ' <spanc class="opacitymedium">('.$langs->trans("except").' '.implode(', ', $except).')</span>'),
|
||||
'value' => 'false',
|
||||
);
|
||||
|
||||
$formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?'.$param, $langs->trans("ExportFilteredList").'...', $langs->trans('ConfirmExportFile'), 'export_fileconfirm', $form_question, '', 1, 500, 700);
|
||||
|
||||
$formconfirm .= '<script>
|
||||
jQuery(document).ready(function() {
|
||||
const exportTypesWithDocs = ['.implode(',', $exportTypesWithDocs).'];
|
||||
const $formatExport = jQuery("#formatexport");
|
||||
|
||||
function toggleExportFull() {
|
||||
const $checkbox = jQuery("#notifiedexportfull");
|
||||
const show = exportTypesWithDocs.indexOf(parseInt($formatExport.val())) !== -1;
|
||||
$checkbox.closest(".tagtr").toggle(show);
|
||||
if (!show) {
|
||||
$checkbox.prop("checked", false); // remove checked if hidden
|
||||
}
|
||||
}
|
||||
|
||||
$formatExport.on("change", toggleExportFull);
|
||||
toggleExportFull();
|
||||
});
|
||||
</script>';
|
||||
}
|
||||
|
||||
// Print form confirm
|
||||
|
||||
@@ -750,7 +750,8 @@ if (!empty($socid)) {
|
||||
print '</div>';
|
||||
print dol_get_fiche_end();
|
||||
|
||||
print info_admin($langs->trans("WarningThisPageContainsOnlyEntriesTransferredInAccounting")).'';
|
||||
print info_admin($langs->trans("WarningThisPageContainsOnlyEntriesTransferredInAccounting"));
|
||||
print '<br>';
|
||||
|
||||
// Choice of mode (customer / supplier)
|
||||
if (!empty($conf->dol_use_jmobile)) {
|
||||
|
||||
@@ -1204,7 +1204,12 @@ class AccountancyExport
|
||||
|
||||
$tab['end_line'] = $end_line;
|
||||
|
||||
print implode('|', $tab);
|
||||
$output = implode('|', $tab);
|
||||
if ($exportFile) {
|
||||
fwrite($exportFile, $output);
|
||||
} else {
|
||||
print $output;
|
||||
}
|
||||
|
||||
$index++;
|
||||
}
|
||||
|
||||
@@ -2258,6 +2258,9 @@ class BookKeeping extends CommonObject
|
||||
$error++;
|
||||
}
|
||||
|
||||
// Store the new piece_num so caller can use it after transformTransaction()
|
||||
$this->piece_num = $next_piecenum;
|
||||
|
||||
if (!$error) {
|
||||
// Delete if there is an empty line
|
||||
$sql = 'DELETE FROM '.$this->db->prefix().$this->table_element.'_tmp WHERE piece_num = '.((int) $piece_num).' AND entity = ' .((int) $conf->entity)." AND numero_compte IS NULL AND debit = 0 AND credit = 0";
|
||||
|
||||
@@ -132,6 +132,11 @@ class BookkeepingTemplateLine extends CommonObject
|
||||
*/
|
||||
public $subledger_label;
|
||||
|
||||
/**
|
||||
* @var ?string label operation
|
||||
*/
|
||||
public $operation_label;
|
||||
|
||||
/**
|
||||
* @var string Debit amount (stored as string for precision)
|
||||
*/
|
||||
|
||||
@@ -155,8 +155,8 @@ if (empty($reshook)) {
|
||||
}
|
||||
} elseif ($action == 'confirm_step_2' && $confirm == "yes" && $user->hasRight('accounting', 'fiscalyear', 'write')) {
|
||||
$new_fiscal_period_id = GETPOSTINT('new_fiscal_period_id');
|
||||
$separate_auxiliary_account = GETPOSTINT('separate_auxiliary_account');
|
||||
$generate_bookkeeping_records = GETPOSTINT('generate_bookkeeping_records');
|
||||
$separate_auxiliary_account = (!empty(GETPOST('separate_auxiliary_account')) ? 1 : 0);
|
||||
$generate_bookkeeping_records = (!empty(GETPOST('generate_bookkeeping_records')) ? 1 : 0);
|
||||
|
||||
$error = 0;
|
||||
if ($generate_bookkeeping_records) {
|
||||
|
||||
@@ -29,12 +29,6 @@
|
||||
|
||||
// Load Dolibarr environment
|
||||
require '../../main.inc.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingaccount.class.php';
|
||||
|
||||
/**
|
||||
* @var Conf $conf
|
||||
* @var DoliDB $db
|
||||
@@ -43,6 +37,11 @@ require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingaccount.class.php';
|
||||
* @var Translate $langs
|
||||
* @var User $user
|
||||
*/
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingaccount.class.php';
|
||||
|
||||
// Load translation files required by the page
|
||||
$langs->loadLangs(array("compta", "bills", "other", "accountancy"));
|
||||
@@ -292,6 +291,7 @@ if ($action == 'validatehistory' && $user->hasRight('accounting', 'bind', 'write
|
||||
/*
|
||||
* View
|
||||
*/
|
||||
|
||||
$help_url = 'EN:Module_Double_Entry_Accounting|FR:Module_Comptabilité_en_Partie_Double#Liaisons_comptables';
|
||||
|
||||
llxHeader('', $langs->trans("CustomersVentilation"), $help_url, '', 0, 0, '', '', '', 'mod-accountancy accountancy-customer page-index');
|
||||
@@ -302,12 +302,14 @@ $textnextyear = ' <a href="'.$_SERVER["PHP_SELF"].'?year='.($year_current +
|
||||
|
||||
print load_fiche_titre($langs->trans("CustomersVentilation")." ".$textprevyear." ".$langs->trans("Year")." ".$year_start." ".$textnextyear, '', 'title_accountancy');
|
||||
|
||||
print '<span class="opacitymedium">'.$langs->trans("DescVentilCustomer").'</span><br>';
|
||||
print '<span class="opacitymedium hideonsmartphone">'.$langs->trans("DescVentilMore", $langs->transnoentitiesnoconv("ValidateHistory"), $langs->transnoentitiesnoconv("ToBind")).'<br>';
|
||||
print '</span><br>';
|
||||
print '<div class="info">';
|
||||
print '<span class="">'.$langs->trans("DescVentilCustomer").'</span><br>';
|
||||
print '<span class="hideonsmartphone">'.$langs->trans("DescVentilMore", $langs->transnoentitiesnoconv("ValidateHistory"), $langs->transnoentitiesnoconv("ToBind")).'<br>';
|
||||
print '</span>';
|
||||
print '</div>';
|
||||
|
||||
if (getDolGlobalInt('INVOICE_USE_SITUATION') == 1) {
|
||||
print info_admin($langs->trans("SorryThisModuleIsNotCompatibleWithTheExperimentalFeatureOfSituationInvoices"));
|
||||
print info_admin($langs->trans("SorryThisModuleIsNotCompatibleWithTheExperimentalFeatureOfSituationInvoices"), 0, 0, 'warning');
|
||||
print "<br>";
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,14 @@
|
||||
*/
|
||||
|
||||
require '../../main.inc.php';
|
||||
/**
|
||||
* @var Conf $conf
|
||||
* @var DoliDB $db
|
||||
* @var HookManager $hookmanager
|
||||
* @var Societe $mysoc
|
||||
* @var Translate $langs
|
||||
* @var User $user
|
||||
*/
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formaccounting.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php';
|
||||
@@ -37,15 +45,6 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
|
||||
|
||||
/**
|
||||
* @var Conf $conf
|
||||
* @var DoliDB $db
|
||||
* @var HookManager $hookmanager
|
||||
* @var Societe $mysoc
|
||||
* @var Translate $langs
|
||||
* @var User $user
|
||||
*/
|
||||
|
||||
// Load translation files required by the page
|
||||
$langs->loadLangs(array("bills", "compta", "accountancy", "productbatch", "products"));
|
||||
|
||||
@@ -441,11 +440,12 @@ if ($result) {
|
||||
print '<input type="hidden" name="page" value="'.$page.'">';
|
||||
|
||||
// @phan-suppress-next-line PhanPluginSuspiciousParamOrder
|
||||
print_barre_liste($langs->trans("InvoiceLinesDone").'<br><span class="opacitymedium small">'.$langs->trans("DescVentilDoneCustomer").'</span>', $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, '', $num_lines, $nbtotalofrecords, 'title_accountancy', 0, '', '', $limit, 0, 0, 1);
|
||||
print_barre_liste($langs->trans("InvoiceLinesDone").'<br><span class="opacityhigh small">'.$langs->trans("DescVentilDoneCustomer").'</span>', $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, '', $num_lines, $nbtotalofrecords, 'title_accountancy', 0, '', '', $limit, 0, 0, 1);
|
||||
|
||||
print '<br>'.$langs->trans("ChangeAccount").' <div class="inline-block paddingbottom marginbottomonly">';
|
||||
print '<br>'.$langs->trans("ChangeAccount").' <div class="inline-block paddingbottom paddingtop">';
|
||||
print $formaccounting->select_account($account_parent, 'account_parent', 2, array(), 0, 0, 'maxwidth300 maxwidthonsmartphone valignmiddle');
|
||||
print '<input type="submit" class="button small smallpaddingimp valignmiddle" value="'.$langs->trans("ChangeBinding").'"/></div>';
|
||||
print '<br><br>';
|
||||
|
||||
$moreforfilter = '';
|
||||
|
||||
@@ -700,7 +700,7 @@ if ($result) {
|
||||
}
|
||||
// Ref Product
|
||||
if (!empty($arrayfields['p.ref']['checked'])) {
|
||||
print '<td class="tdoverflowmax100">';
|
||||
print '<td class="tdoverflowmax100 cell2linesheight">';
|
||||
if ($productstatic->id > 0) {
|
||||
print $productstatic->getNomUrl(1);
|
||||
} else {
|
||||
@@ -766,9 +766,10 @@ if ($result) {
|
||||
}
|
||||
// Country
|
||||
if (!empty($arrayfields['co.label']['checked'])) {
|
||||
print '<td class="tdoverflowmax125" title="'.dolPrintHTML($langs->trans("Country".$objp->country_code).' ('.$objp->country_code.')').'">';
|
||||
print '<td class="tdoverflowmax125" title="'.dolPrintHTMLForAttribute($langs->trans("Country".$objp->country_code).' ('.$objp->country_code.')').'">';
|
||||
if ($objp->country_code) {
|
||||
print $langs->trans("Country".$objp->country_code).' ('.$objp->country_code.')';
|
||||
print $langs->trans("Country".$objp->country_code);
|
||||
//print ' ('.$objp->country_code.')';
|
||||
}
|
||||
print '</td>';
|
||||
$totalarray['nbfield']++;
|
||||
|
||||
@@ -111,6 +111,8 @@ if (!$sortorder) {
|
||||
// Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
|
||||
$hookmanager->initHooks(array($contextpage));
|
||||
|
||||
$object = new AccountingAccount($db);
|
||||
|
||||
$formaccounting = new FormAccounting($db);
|
||||
$accountingAccount = new AccountingAccount($db);
|
||||
|
||||
@@ -254,7 +256,6 @@ if (GETPOST('sortfield') == 'f.datef, f.ref, l.rowid') {
|
||||
*/
|
||||
|
||||
$form = new Form($db);
|
||||
$formother = new FormOther($db);
|
||||
|
||||
$help_url = 'EN:Module_Double_Entry_Accounting|FR:Module_Comptabilité_en_Partie_Double#Liaisons_comptables';
|
||||
|
||||
@@ -503,7 +504,8 @@ if ($result) {
|
||||
print '<input type="hidden" name="page" value="'.$page.'">';
|
||||
|
||||
// @phan-suppress-next-line PhanPluginSuspiciousParamOrder
|
||||
print_barre_liste($langs->trans("InvoiceLines").'<br><span class="opacitymedium small">'.$langs->trans("DescVentilTodoCustomer").'</span>', $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, (string) $massactionbutton, $num_lines, $nbtotalofrecords, 'title_accountancy', 0, '', '', $limit, 0, 0, 1);
|
||||
print_barre_liste($langs->trans("InvoiceLines").'<br><span class="opacityhigh small">'.$langs->trans("DescVentilTodoCustomer").'</span>', $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, (string) $massactionbutton, $num_lines, $nbtotalofrecords, 'title_accountancy', 0, '', '', $limit, 0, 0, 1);
|
||||
print '<br>';
|
||||
|
||||
if ($massaction == 'set_default_account') {
|
||||
$formquestion = array();
|
||||
@@ -826,7 +828,7 @@ if ($result) {
|
||||
}
|
||||
// Ref Invoice
|
||||
if (!empty($arrayfields['f.ref']['checked'])) {
|
||||
print '<td class="nowraponall">'.$facture_static->getNomUrl(1).'</td>';
|
||||
print '<td class="nowraponall cell2linesheight">'.$facture_static->getNomUrl(1).'</td>';
|
||||
$totalarray['nbfield']++;
|
||||
}
|
||||
// Invoice date
|
||||
@@ -836,7 +838,7 @@ if ($result) {
|
||||
}
|
||||
// Ref Product
|
||||
if (!empty($arrayfields['p.ref']['checked'])) {
|
||||
print '<td class="tdoverflowmax125">';
|
||||
print '<td class="tdoverflowmax125 cell2linesheight">';
|
||||
if ($product_static->id > 0) {
|
||||
print $product_static->getNomUrl(1);
|
||||
}
|
||||
@@ -889,8 +891,8 @@ if ($result) {
|
||||
if ($product_static->tva_tx !== $facture_static_det->tva_tx && price2num($product_static->tva_tx) && price2num($facture_static_det->tva_tx)) { // Note: having a vat rate of 0 is often the normal case when sells is intra b2b or to export
|
||||
$code_vat_differ = 'warning bold';
|
||||
}
|
||||
print '<td class="right'.($code_vat_differ ? ' '.$code_vat_differ : '').'">';
|
||||
print vatrate($facture_static_det->tva_tx.($facture_static_det->vat_src_code ? ' ('.$facture_static_det->vat_src_code.')' : ''));
|
||||
print '<td class="right cell2linesheight'.($code_vat_differ ? ' '.$code_vat_differ : '').'">';
|
||||
print vatrate($facture_static_det->tva_tx.($facture_static_det->vat_src_code ? ' ('.$facture_static_det->vat_src_code.')' : ''), false, 0, 0, 1);
|
||||
print '</td>';
|
||||
$totalarray['nbfield']++;
|
||||
}
|
||||
@@ -914,7 +916,7 @@ if ($result) {
|
||||
}
|
||||
// Found accounts
|
||||
if (!empty($arrayfields['aa.data_suggest']['checked'])) {
|
||||
print '<td class="small">';
|
||||
print '<td class="small cell2linesheight">';
|
||||
// First show default account for any products
|
||||
$s = '1. '.(($facture_static_det->product_type == 1) ? $langs->trans("DefaultForService") : $langs->trans("DefaultForProduct")).': ';
|
||||
$shelp = '';
|
||||
|
||||
@@ -28,10 +28,6 @@
|
||||
|
||||
// Load Dolibarr environment
|
||||
require '../../main.inc.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/expensereport/class/expensereport.class.php';
|
||||
|
||||
/**
|
||||
* @var Conf $conf
|
||||
* @var DoliDB $db
|
||||
@@ -39,6 +35,9 @@ require_once DOL_DOCUMENT_ROOT.'/expensereport/class/expensereport.class.php';
|
||||
* @var Translate $langs
|
||||
* @var User $user
|
||||
*/
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/expensereport/class/expensereport.class.php';
|
||||
|
||||
// Load translation files required by the page
|
||||
$langs->loadLangs(array("compta", "bills", "other", "accountancy"));
|
||||
@@ -85,6 +84,7 @@ if (!$user->hasRight('accounting', 'bind', 'write')) {
|
||||
/*
|
||||
* Actions
|
||||
*/
|
||||
|
||||
$error = 0;
|
||||
|
||||
if (($action == 'clean' || $action == 'validatehistory') && $user->hasRight('accounting', 'bind', 'write')) {
|
||||
@@ -197,10 +197,11 @@ $textnextyear = ' <a href="'.$_SERVER["PHP_SELF"].'?year='.($year_current +
|
||||
|
||||
print load_fiche_titre($langs->trans("ExpenseReportsVentilation")." ".$textprevyear." ".$langs->trans("Year")." ".$year_start." ".$textnextyear, '', 'title_accountancy');
|
||||
|
||||
print '<span class="opacitymedium">'.$langs->trans("DescVentilExpenseReport").'</span><br>';
|
||||
print '<span class="opacitymedium hideonsmartphone">'.$langs->trans("DescVentilExpenseReportMore", $langs->transnoentitiesnoconv("ValidateHistory"), $langs->transnoentitiesnoconv("ToBind")).'<br>';
|
||||
print '</span><br>';
|
||||
|
||||
print '<div class="info">';
|
||||
print '<span class="">'.$langs->trans("DescVentilExpenseReport").'</span><br>';
|
||||
print '<span class="hideonsmartphone">'.$langs->trans("DescVentilExpenseReportMore", $langs->transnoentitiesnoconv("ValidateHistory"), $langs->transnoentitiesnoconv("ToBind")).'<br>';
|
||||
print '</span>';
|
||||
print '</div>';
|
||||
|
||||
$y = $year_current;
|
||||
|
||||
|
||||
@@ -27,14 +27,6 @@
|
||||
*/
|
||||
|
||||
require '../../main.inc.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formaccounting.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/expensereport/class/expensereport.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingaccount.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php';
|
||||
|
||||
/**
|
||||
* @var Conf $conf
|
||||
* @var DoliDB $db
|
||||
@@ -42,6 +34,13 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php';
|
||||
* @var Translate $langs
|
||||
* @var User $user
|
||||
*/
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formaccounting.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/expensereport/class/expensereport.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingaccount.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php';
|
||||
|
||||
// Load translation files required by the page
|
||||
$langs->loadLangs(array("compta", "bills", "other", "accountancy", "trips", "productbatch", "hrm"));
|
||||
@@ -372,11 +371,12 @@ if ($result) {
|
||||
print '<input type="hidden" name="page" value="'.$page.'">';
|
||||
|
||||
// @phan-suppress-next-line PhanPluginSuspiciousParamOrder
|
||||
print_barre_liste($langs->trans("ExpenseReportLinesDone").'<br><span class="opacitymedium small">'.$langs->trans("DescVentilDoneExpenseReport").'</span>', $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, '', $num_lines, $nbtotalofrecords, 'title_accountancy', 0, '', '', $limit, 0, 0, 1);
|
||||
print_barre_liste($langs->trans("ExpenseReportLinesDone").'<br><span class="opacityhigh small">'.$langs->trans("DescVentilDoneExpenseReport").'</span>', $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, '', $num_lines, $nbtotalofrecords, 'title_accountancy', 0, '', '', $limit, 0, 0, 1);
|
||||
|
||||
print '<br>'.$langs->trans("ChangeAccount").' <div class="inline-block paddingbottom marginbottomonly">';
|
||||
print '<br>'.$langs->trans("ChangeAccount").' <div class="inline-block paddingbottom paddingtop">';
|
||||
print $formaccounting->select_account($account_parent, 'account_parent', 2, array(), 0, 0, 'maxwidth300 maxwidthonsmartphone valignmiddle');
|
||||
print '<input type="submit" class="button small smallpaddingimp valignmiddle" value="'.$langs->trans("ChangeBinding").'"/></div>';
|
||||
print '<br><br>';
|
||||
|
||||
$moreforfilter = '';
|
||||
|
||||
@@ -639,7 +639,7 @@ if ($result) {
|
||||
}
|
||||
// Accounting account affected
|
||||
if (!empty($arrayfields['aa.account_number']['checked'])) {
|
||||
print '<td class="tdoverflowmax200" title="'.dol_escape_htmltag($accountingaccountstatic->label).'">';
|
||||
print '<td class="tdoverflowmax200" title="'.dolPrintHTMLForAttribute($accountingaccountstatic->label).'">';
|
||||
print '<a class="editfielda reposition marginleftonly marginrightonly" href="./card.php?id='.$objp->rowid.'&backtopage='.urlencode($_SERVER["PHP_SELF"].($param ? '?'.$param : '')).'">';
|
||||
print img_edit();
|
||||
print '</a> ';
|
||||
|
||||
@@ -421,7 +421,9 @@ if ($result) {
|
||||
print '<input type="hidden" name="page" value="'.$page.'">';
|
||||
|
||||
// @phan-suppress-next-line PhanPluginSuspiciousParamOrder
|
||||
print_barre_liste($langs->trans("ExpenseReportLines").'<br><span class="opacitymedium small">'.$langs->trans("DescVentilTodoExpenseReport").'</span>', $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, (string) $massactionbutton, $num_lines, $nbtotalofrecords, 'title_accountancy', 0, '', '', $limit, 0, 0, 1);
|
||||
print_barre_liste($langs->trans("ExpenseReportLines").'<br><span class="opacityhigh small">'.$langs->trans("DescVentilTodoExpenseReport").'</span>', $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, (string) $massactionbutton, $num_lines, $nbtotalofrecords, 'title_accountancy', 0, '', '', $limit, 0, 0, 1);
|
||||
|
||||
print '<br>';
|
||||
|
||||
if (!empty($msg)) {
|
||||
print $msg.'<br>';
|
||||
|
||||
@@ -26,10 +26,6 @@
|
||||
|
||||
// Load Dolibarr environment
|
||||
require '../main.inc.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
|
||||
|
||||
/**
|
||||
* @var Conf $conf
|
||||
* @var DoliDB $db
|
||||
@@ -38,6 +34,9 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
|
||||
* @var Translate $langs
|
||||
* @var User $user
|
||||
*/
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
|
||||
|
||||
// Load translation files required by the page
|
||||
$langs->loadLangs(array("compta", "bills", "other", "accountancy", "loans", "banks", "admin", "dict"));
|
||||
@@ -147,12 +146,13 @@ if (isModEnabled('accounting')) {
|
||||
|
||||
print '<div class="'.($helpisexpanded ? '' : 'hideobject').' idfaq">'; // hideobject is to start hidden
|
||||
print "<br>\n";
|
||||
print '<span class="opacitymedium">'.$langs->trans("AccountancyAreaDescIntro")."</span><br>\n";
|
||||
//print '<span class="opacitymedium">'.$langs->trans("AccountancyAreaDescIntro")."</span><br>\n";
|
||||
if ($user->hasRight('accounting', 'chartofaccount')) {
|
||||
print '<br>';
|
||||
//print '<br>';
|
||||
//print '<br>';
|
||||
print load_fiche_titre('<span class="fa fa-calendar"></span> '.$langs->trans("AccountancyAreaDescActionOnce"), '', '', 0, '', 'nomarginbottom')."\n";
|
||||
print '<hr>';
|
||||
print "<br>\n";
|
||||
|
||||
print '<div class="info">';
|
||||
|
||||
// STEPS
|
||||
$step++;
|
||||
@@ -199,13 +199,15 @@ if (isModEnabled('accounting')) {
|
||||
print $s;
|
||||
print "<br>\n";
|
||||
}
|
||||
print '</div>';
|
||||
|
||||
|
||||
print "<br>\n";
|
||||
print $langs->trans("AccountancyAreaDescActionOnceBis");
|
||||
print "<br>\n";
|
||||
print '<span class="opacitymedium">'.$langs->trans("AccountancyAreaDescActionOnceBis").'</span>';
|
||||
print "<br>\n";
|
||||
|
||||
print '<div class="info">';
|
||||
|
||||
$step++;
|
||||
$s = img_picto('', 'puce').' '.$langs->trans("AccountancyAreaDescDefault", $step, '{s}');
|
||||
$s = str_replace('{s}', '<a href="'.DOL_URL_ROOT.'/accountancy/admin/defaultaccounts.php?leftmenu=accountancy_admin" target="setupaccountancy"><strong>'.$langs->transnoentitiesnoconv("Setup").' - '.$langs->transnoentitiesnoconv("MenuDefaultAccounts").'</strong></a>', $s);
|
||||
@@ -247,6 +249,8 @@ if (isModEnabled('accounting')) {
|
||||
print $s;
|
||||
print "<br>\n";
|
||||
|
||||
print '</div>';
|
||||
|
||||
print '<br>';
|
||||
}
|
||||
|
||||
@@ -254,12 +258,13 @@ if (isModEnabled('accounting')) {
|
||||
|
||||
print "<br>\n";
|
||||
print load_fiche_titre('<span class="fa fa-calendar"></span> '.$langs->trans("AccountancyAreaDescActionFreq"), '', '', 0, '', 'nomarginbottom')."\n";
|
||||
print '<hr>';
|
||||
print "<br>\n";
|
||||
|
||||
$step = 0;
|
||||
|
||||
$langs->loadLangs(array('bills', 'trips'));
|
||||
|
||||
print '<div class="info">';
|
||||
|
||||
$step++;
|
||||
$s = img_picto('', 'puce').' '.$langs->trans("AccountancyAreaDescBind", chr(64 + $step), $langs->transnoentitiesnoconv("BillsCustomers"), '{s}')."\n";
|
||||
$s = str_replace('{s}', '<a href="'.DOL_URL_ROOT.'/accountancy/customer/index.php" target="setupaccountancy"><strong>'.$langs->transnoentitiesnoconv("TransferInAccounting").' - '.$langs->transnoentitiesnoconv("CustomersVentilation").'</strong></a>', $s);
|
||||
@@ -293,6 +298,9 @@ if (isModEnabled('accounting')) {
|
||||
$s = img_picto('', 'puce').' '.$langs->trans("AccountancyAreaDescClosePeriod", chr(64 + $step))."<br>\n";
|
||||
print $s;
|
||||
|
||||
print '</div>';
|
||||
|
||||
|
||||
if (!empty($resultboxes['boxlista']) || !empty($resultboxes['boxlistb'])) {
|
||||
print "<br>\n";
|
||||
print '<br>';
|
||||
|
||||
@@ -28,12 +28,6 @@
|
||||
|
||||
// Load Dolibarr environment
|
||||
require '../../main.inc.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingaccount.class.php';
|
||||
|
||||
/**
|
||||
* @var Conf $conf
|
||||
* @var DoliDB $db
|
||||
@@ -42,6 +36,11 @@ require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingaccount.class.php';
|
||||
* @var Translate $langs
|
||||
* @var User $user
|
||||
*/
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingaccount.class.php';
|
||||
|
||||
// Load translation files required by the page
|
||||
$langs->loadLangs(array("compta", "bills", "other", "accountancy"));
|
||||
@@ -298,6 +297,7 @@ if ($action == 'validatehistory' && $user->hasRight('accounting', 'bind', 'write
|
||||
/*
|
||||
* View
|
||||
*/
|
||||
|
||||
$help_url = 'EN:Module_Double_Entry_Accounting|FR:Module_Comptabilité_en_Partie_Double#Liaisons_comptables';
|
||||
|
||||
llxHeader('', $langs->trans("SuppliersVentilation"), $help_url, '', 0, 0, '', '', '', 'mod-accountancy accountancy-supplier page-index');
|
||||
@@ -307,9 +307,11 @@ $textnextyear = ' <a href="'.$_SERVER["PHP_SELF"].'?year='.($year_current +
|
||||
|
||||
print load_fiche_titre($langs->trans("SuppliersVentilation")." ".$textprevyear." ".$langs->trans("Year")." ".$year_start." ".$textnextyear, '', 'title_accountancy');
|
||||
|
||||
print '<span class="opacitymedium">'.$langs->trans("DescVentilSupplier").'</span><br>';
|
||||
print '<span class="opacitymedium hideonsmartphone">'.$langs->trans("DescVentilMore", $langs->transnoentitiesnoconv("ValidateHistory"), $langs->transnoentitiesnoconv("ToBind")).'<br>';
|
||||
print '</span><br>';
|
||||
print '<div class="info">';
|
||||
print '<span class="">'.$langs->trans("DescVentilSupplier").'</span><br>';
|
||||
print '<span class="hideonsmartphone">'.$langs->trans("DescVentilMore", $langs->transnoentitiesnoconv("ValidateHistory"), $langs->transnoentitiesnoconv("ToBind")).'<br>';
|
||||
print '</span>';
|
||||
print '</div>';
|
||||
|
||||
$y = $year_current;
|
||||
|
||||
|
||||
@@ -26,7 +26,14 @@
|
||||
* \brief Page of detail of the lines of ventilation of invoices suppliers
|
||||
*/
|
||||
require '../../main.inc.php';
|
||||
|
||||
/**
|
||||
* @var Conf $conf
|
||||
* @var DoliDB $db
|
||||
* @var HookManager $hookmanager
|
||||
* @var Societe $mysoc
|
||||
* @var Translate $langs
|
||||
* @var User $user
|
||||
*/
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formaccounting.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php';
|
||||
@@ -38,18 +45,10 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
|
||||
|
||||
/**
|
||||
* @var Conf $conf
|
||||
* @var DoliDB $db
|
||||
* @var HookManager $hookmanager
|
||||
* @var Societe $mysoc
|
||||
* @var Translate $langs
|
||||
* @var User $user
|
||||
*/
|
||||
|
||||
// Load translation files required by the page
|
||||
$langs->loadLangs(array("compta", "bills", "other", "accountancy", "productbatch", "products"));
|
||||
|
||||
$action = GETPOST('action', 'aZ');
|
||||
$optioncss = GETPOST('optioncss', 'aZ'); // Option for the css output (always '' except when 'print')
|
||||
|
||||
$account_parent = GETPOST('account_parent');
|
||||
@@ -102,6 +101,8 @@ if (!$sortorder) {
|
||||
$contextpage = 'accountancysupplierlines';
|
||||
$hookmanager->initHooks([$contextpage]);
|
||||
|
||||
$object = new AccountingAccount($db);
|
||||
|
||||
// Security check
|
||||
if (!isModEnabled('accounting')) {
|
||||
accessforbidden();
|
||||
@@ -121,7 +122,7 @@ $arrayfields = array(
|
||||
'f.libelle' => array('label' => "InvoiceLabel", 'position' => 1, 'checked' => '1', 'enabled' => '1'),
|
||||
'f.datef' => array('label' => "Date", 'position' => 1, 'checked' => '1', 'enabled' => '1'), // f.datef, f.ref, l.rowid
|
||||
'p.ref' => array('label' => "ProductRef", 'position' => 1, 'checked' => '1', 'enabled' => '1'),
|
||||
'l.description' => array('label' => "ProductDescription", 'position' => 1, 'checked' => '1', 'enabled' => '1'),
|
||||
'l.description' => array('label' => "ProductDescription", 'position' => 1, 'checked' => '-1', 'enabled' => '1'),
|
||||
'l.total_ht' => array('label' => "Amount", 'position' => 1, 'checked' => '1', 'enabled' => '1'),
|
||||
'l.tva_tx' => array('label' => "VATRate", 'position' => 1, 'checked' => '1', 'enabled' => '1'),
|
||||
's.nom' => array('label' => "ThirdParty", 'position' => 1, 'checked' => '1', 'enabled' => '1'),
|
||||
@@ -441,11 +442,12 @@ if ($result) {
|
||||
print '<input type="hidden" name="page" value="'.$page.'">';
|
||||
|
||||
// @phan-suppress-next-line PhanPluginSuspiciousParamOrder
|
||||
print_barre_liste($langs->trans("InvoiceLinesDone").'<br><span class="opacitymedium small">'.$langs->trans("DescVentilDoneSupplier").'</span>', $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, '', $num_lines, $nbtotalofrecords, 'title_accountancy', 0, '', '', $limit, 0, 0, 1);
|
||||
print_barre_liste($langs->trans("InvoiceLinesDone").'<br><span class="opacityhigh small">'.$langs->trans("DescVentilDoneSupplier").'</span>', $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, '', $num_lines, $nbtotalofrecords, 'title_accountancy', 0, '', '', $limit, 0, 0, 1);
|
||||
|
||||
print '<br>'.$langs->trans("ChangeAccount").' <div class="inline-block paddingbottom marginbottomonly">';
|
||||
print '<br>'.$langs->trans("ChangeAccount").' <div class="inline-block paddingbottom paddingtop">';
|
||||
print $formaccounting->select_account($account_parent, 'account_parent', 2, array(), 0, 0, 'maxwidth300 maxwidthonsmartphone valignmiddle');
|
||||
print '<input type="submit" class="button small smallpaddingimp valignmiddle" value="'.$langs->trans("ChangeBinding").'"/></div>';
|
||||
print '<br><br>';
|
||||
|
||||
$moreforfilter = '';
|
||||
|
||||
@@ -703,7 +705,7 @@ if ($result) {
|
||||
}
|
||||
// Ref Invoice
|
||||
if (!empty($arrayfields['f.ref']['checked'])) {
|
||||
print '<td class="nowraponall tdoverflowmax125">';
|
||||
print '<td class="nowraponall tdoverflowmax125 cell2linesheight">';
|
||||
print $facturefournisseur_static->getNomUrl(1);
|
||||
if ($objp->ref_supplier) {
|
||||
print '<br><span class="opacitymedium small">'.dol_escape_htmltag($objp->ref_supplier).'</span>';
|
||||
@@ -731,7 +733,7 @@ if ($result) {
|
||||
}
|
||||
// Ref Product
|
||||
if (!empty($arrayfields['p.ref']['checked'])) {
|
||||
print '<td class="tdoverflowmax100">';
|
||||
print '<td class="tdoverflowmax100 cell2linesheight">';
|
||||
if ($productstatic->id > 0) {
|
||||
print $productstatic->getNomUrl(1);
|
||||
}
|
||||
@@ -739,7 +741,7 @@ if ($result) {
|
||||
print '<br>';
|
||||
}
|
||||
if ($objp->product_label) {
|
||||
print '<span class="opacitymedium">'.$objp->product_label.'</span>';
|
||||
print '<span class="opacitymedium">'.dolPrintHTML($objp->product_label).'</span>';
|
||||
}
|
||||
print '</td>';
|
||||
$totalarray['nbfield']++;
|
||||
@@ -747,7 +749,7 @@ if ($result) {
|
||||
// Description
|
||||
if (!empty($arrayfields['l.description']['checked'])) {
|
||||
$text = dolGetFirstLineOfText(dol_string_nohtmltag($objp->description, 1));
|
||||
print '<td class="tdoverflowmax200 small" title="'.dol_escape_htmltag($text).'">';
|
||||
print '<td class="tdoverflowmax200 small" title="'.dolPrintHTMLForAttribute($text).'">';
|
||||
$trunclength = getDolGlobalInt('ACCOUNTING_LENGTH_DESCRIPTION', 32);
|
||||
print $form->textwithtooltip(dol_trunc($text, $trunclength), $objp->description);
|
||||
print '</td>';
|
||||
@@ -770,21 +772,22 @@ if ($result) {
|
||||
}
|
||||
// Country
|
||||
if (!empty($arrayfields['co.label']['checked'])) {
|
||||
print '<td class="tdoverflowmax100">';
|
||||
print '<td class="tdoverflowmax100" title="'.dolPrintHTMLForAttribute($langs->trans("Country".$objp->country_code).' ('.$objp->country_code.')').'">';
|
||||
if ($objp->country_code) {
|
||||
print $langs->trans("Country".$objp->country_code).' ('.$objp->country_code.')';
|
||||
print $langs->trans("Country".$objp->country_code);
|
||||
//print ' ('.$objp->country_code.')';
|
||||
}
|
||||
print '</td>';
|
||||
$totalarray['nbfield']++;
|
||||
}
|
||||
// TVA Intracom
|
||||
if (!empty($arrayfields['s.tva_intra']['checked'])) {
|
||||
print '<td class="tdoverflowmax80" title="'.dol_escape_htmltag($objp->tva_intra).'">'.dol_escape_htmltag($objp->tva_intra).'</td>';
|
||||
print '<td class="tdoverflowmax80" title="'.dolPrintHTMLForAttribute($objp->tva_intra).'">'.dol_escape_htmltag($objp->tva_intra).'</td>';
|
||||
$totalarray['nbfield']++;
|
||||
}
|
||||
// Account
|
||||
if (!empty($arrayfields['aa.account_number']['checked'])) {
|
||||
print '<td class="tdoverflowmax200" title="'.dol_escape_htmltag($accountingaccountstatic->label).'">';
|
||||
print '<td class="tdoverflowmax200" title="'.dolPrintHTMLForAttribute($accountingaccountstatic->label).'">';
|
||||
print '<a class="editfielda" href="./card.php?id='.$objp->rowid.'&backtopage='.urlencode($_SERVER["PHP_SELF"].($param ? '?'.$param : '')).'">';
|
||||
print img_edit();
|
||||
print '</a> ';
|
||||
|
||||
@@ -29,7 +29,14 @@
|
||||
* \brief Ventilation page from suppliers invoices
|
||||
*/
|
||||
require '../../main.inc.php';
|
||||
|
||||
/**
|
||||
* @var Conf $conf
|
||||
* @var DoliDB $db
|
||||
* @var HookManager $hookmanager
|
||||
* @var Societe $mysoc
|
||||
* @var Translate $langs
|
||||
* @var User $user
|
||||
*/
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formaccounting.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php';
|
||||
@@ -41,15 +48,6 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
|
||||
|
||||
/**
|
||||
* @var Conf $conf
|
||||
* @var DoliDB $db
|
||||
* @var HookManager $hookmanager
|
||||
* @var Societe $mysoc
|
||||
* @var Translate $langs
|
||||
* @var User $user
|
||||
*/
|
||||
|
||||
// Load translation files required by the page
|
||||
$langs->loadLangs(array("bills", "companies", "compta", "accountancy", "other", "productbatch", "products"));
|
||||
|
||||
@@ -516,7 +514,8 @@ if ($result) {
|
||||
print '<input type="hidden" name="page" value="'.$page.'">';
|
||||
|
||||
// @phan-suppress-next-line PhanPluginSuspiciousParamOrder
|
||||
print_barre_liste($langs->trans("InvoiceLines").'<br><span class="opacitymedium small">'.$langs->trans("DescVentilTodoCustomer").'</span>', $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, (string) $massactionbutton, $num_lines, $nbtotalofrecords, 'title_accountancy', 0, '', '', $limit, 0, 0, 1);
|
||||
print_barre_liste($langs->trans("InvoiceLines").'<br><span class="opacityhigh small">'.$langs->trans("DescVentilTodoCustomer").'</span>', $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, (string) $massactionbutton, $num_lines, $nbtotalofrecords, 'title_accountancy', 0, '', '', $limit, 0, 0, 1);
|
||||
print '<br>';
|
||||
|
||||
if ($massaction == 'set_default_account') {
|
||||
$formquestion = array();
|
||||
@@ -850,7 +849,7 @@ if ($result) {
|
||||
}
|
||||
// Ref Invoice
|
||||
if (!empty($arrayfields['f.ref']['checked'])) {
|
||||
print '<td class="nowraponall">'.$facturefourn_static->getNomUrl(1);
|
||||
print '<td class="nowraponall cell2linesheight">'.$facturefourn_static->getNomUrl(1);
|
||||
if ($objp->ref_supplier) {
|
||||
print '<br><span class="opacitymedium small">'.dol_escape_htmltag($objp->ref_supplier).'</span>';
|
||||
}
|
||||
@@ -866,7 +865,7 @@ if ($result) {
|
||||
// Supplier invoice label
|
||||
if (!empty($arrayfields['f.libelle']['checked'])) {
|
||||
print '<td class="tdoverflowmax100 small" title="'.dol_escape_htmltag($objp->invoice_label).'">';
|
||||
print dol_escape_htmltag($objp->invoice_label);
|
||||
print dolPrintHTML($objp->invoice_label);
|
||||
print '</td>';
|
||||
$totalarray['nbfield']++;
|
||||
}
|
||||
@@ -877,7 +876,7 @@ if ($result) {
|
||||
}
|
||||
// Ref Product
|
||||
if (!empty($arrayfields['p.ref']['checked'])) {
|
||||
print '<td class="tdoverflowmax100">';
|
||||
print '<td class="tdoverflowmax100 cell2linesheight">';
|
||||
if ($product_static->id > 0) {
|
||||
print $product_static->getNomUrl(1);
|
||||
}
|
||||
@@ -908,7 +907,7 @@ if ($result) {
|
||||
//if ($objp->vat_tx_l != $objp->vat_tx_p && price2num($objp->vat_tx_p) && price2num($objp->vat_tx_l)) { // Note: having a vat rate of 0 is often the normal case when sells is intra b2b or to export
|
||||
// $code_vat_differ = 'warning bold';
|
||||
//}
|
||||
print '<td class="right'.($code_vat_differ ? ' '.$code_vat_differ : '').'">';
|
||||
print '<td class="right cell2linesheight'.($code_vat_differ ? ' '.$code_vat_differ : '').'">';
|
||||
print vatrate($facturefourn_static_det->tva_tx.($facturefourn_static_det->vat_src_code ? ' ('.$facturefourn_static_det->vat_src_code.')' : ''), false, 0, 0, 1);
|
||||
print '</td>';
|
||||
$totalarray['nbfield']++;
|
||||
@@ -933,7 +932,7 @@ if ($result) {
|
||||
}
|
||||
// Found accounts
|
||||
if (!empty($arrayfields['aa.data_suggest']['checked'])) {
|
||||
print '<td class="small">';
|
||||
print '<td class="small cell2linesheight">';
|
||||
$s = '1. '.(($facturefourn_static_det->product_type == 1) ? $langs->trans("DefaultForService") : $langs->trans("DefaultForProduct")).': ';
|
||||
$shelp = '';
|
||||
$ttype = 'help';
|
||||
|
||||
@@ -1189,9 +1189,12 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) {
|
||||
print '<tr><td id="tdcompany">'.$langs->trans("Company").' / '.$langs->trans("Organization").'</td><td>'.img_picto('', 'company', 'class="pictofixedwidth"').'<input type="text" name="societe" class="minwidth300" maxlength="128" value="'.(GETPOSTISSET('societe') ? GETPOST('societe', 'alphanohtml') : $soc->name).'"></td></tr>';
|
||||
|
||||
// Civility
|
||||
print '<tr><td>'.$langs->trans("UserTitle").'</td><td>';
|
||||
print $formcompany->select_civility(GETPOSTISSET('civility_code') ? GETPOST('civility_code') : $object->civility_code, 'civility_code', 'maxwidth150', 1).'</td>';
|
||||
print '</tr>';
|
||||
if (getDolGlobalString('MAIN_USE_TITLE_FOR_MEMBER')) {
|
||||
print '<tr><td>'.$langs->trans("UserTitle").'</td><td>';
|
||||
print $formcompany->select_civility(GETPOSTISSET('civility_code') ? GETPOST('civility_code', "aZ09") : $object->civility_code, 'civility_code', 'maxwidth150', 1);
|
||||
print '</td>';
|
||||
print '</tr>';
|
||||
}
|
||||
|
||||
// Lastname
|
||||
print '<tr><td id="tdlastname">'.$langs->trans("Lastname").'</td><td><input type="text" name="lastname" class="minwidth300" maxlength="50" value="'.(GETPOSTISSET('lastname') ? GETPOST('lastname', 'alphanohtml') : $object->lastname).'"></td>';
|
||||
@@ -1415,18 +1418,20 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) {
|
||||
$morphys["mor"] = $langs->trans("Moral");
|
||||
$checkednature = (GETPOSTISSET("morphy") ? GETPOST("morphy", 'alpha') : $object->morphy);
|
||||
print '<tr><td><span class="fieldrequired">'.$langs->trans("MemberNature").'</span></td><td>';
|
||||
print '<span id="spannature1" class="member-individual-back spannature paddinglarge marginrightonly"><label for="phisicalinput" class="valignmiddle">'.$morphys["phy"].'<input id="phisicalinput" class="flat checkforselect marginleftonly valignmiddle" type="radio" name="morphy" value="phy"'.($checkednature == "phy" ? ' checked="checked"' : '').'></label></span>';
|
||||
print '<span id="spannature1" class="member-company-back spannature paddinglarge marginrightonly"><label for="moralinput" class="valignmiddle">'.$morphys["mor"].'<input id="moralinput" class="flat checkforselect marginleftonly valignmiddle" type="radio" name="morphy" value="mor"'.($checkednature == "mor" ? ' checked="checked"' : '').'></label></span>';
|
||||
print '<span id="spannature1" class="member-individual-back spannature paddinglarge marginrightonly"><label for="phisicalinput" class="">'.$morphys["phy"].'<input id="phisicalinput" class="flat checkforselect marginleftonly valignmiddle" type="radio" name="morphy" value="phy"'.($checkednature == "phy" ? ' checked="checked"' : '').'></label></span>';
|
||||
print '<span id="spannature1" class="member-company-back spannature paddinglarge marginrightonly"><label for="moralinput" class="">'.$morphys["mor"].'<input id="moralinput" class="flat checkforselect marginleftonly valignmiddle" type="radio" name="morphy" value="mor"'.($checkednature == "mor" ? ' checked="checked"' : '').'></label></span>';
|
||||
print "</td></tr>";
|
||||
|
||||
// Company
|
||||
print '<tr><td id="tdcompany">'.$langs->trans("Company").'</td><td>'.img_picto('', 'company', 'class="pictofixedwidth"').'<input type="text" name="societe" class="minwidth300" maxlength="128" value="'.(GETPOSTISSET("societe") ? GETPOST("societe", 'alphanohtml', 2) : $object->company).'"></td></tr>';
|
||||
|
||||
// Civility
|
||||
print '<tr><td>'.$langs->trans("UserTitle").'</td><td>';
|
||||
print $formcompany->select_civility(GETPOSTISSET("civility_code") ? GETPOST("civility_code", 'alpha') : $object->civility_code, 'civility_code', 'maxwidth150', 1);
|
||||
print '</td>';
|
||||
print '</tr>';
|
||||
if (getDolGlobalString('MAIN_USE_TITLE_FOR_MEMBER')) {
|
||||
print '<tr><td>'.$langs->trans("UserTitle").'</td><td>';
|
||||
print $formcompany->select_civility(GETPOSTISSET("civility_code") ? GETPOST("civility_code", 'aZ09') : $object->civility_code, 'civility_code', 'maxwidth150', 1);
|
||||
print '</td>';
|
||||
print '</tr>';
|
||||
}
|
||||
|
||||
// Lastname
|
||||
print '<tr><td id="tdlastname">'.$langs->trans("Lastname").'</td><td><input type="text" name="lastname" class="minwidth300" maxlength="50" value="'.(GETPOSTISSET("lastname") ? GETPOST("lastname", 'alphanohtml', 2) : $object->lastname).'"></td>';
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* Copyright (C) 2016 Charlie Benke <charlie@patas-monkey.com>
|
||||
* Copyright (C) 2018-2019 Thibault Foucart <support@ptibogxiv.net>
|
||||
* Copyright (C) 2021 Waël Almoman <info@almoman.com>
|
||||
* Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
|
||||
* Copyright (C) 2024-2026 Frédéric France <frederic.france@free.fr>
|
||||
* Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -305,9 +305,10 @@ class AdherentType extends CommonObject
|
||||
* Update or add a translation for this member type
|
||||
*
|
||||
* @param User $user Object user making update
|
||||
* @param int $notrigger Do not execute trigger
|
||||
* @return int Return integer <0 if KO, >0 if OK
|
||||
*/
|
||||
public function setMultiLangs($user)
|
||||
public function setMultiLangs($user, $notrigger = 0)
|
||||
{
|
||||
global $langs;
|
||||
|
||||
@@ -375,13 +376,15 @@ class AdherentType extends CommonObject
|
||||
}
|
||||
}
|
||||
|
||||
// Call trigger
|
||||
$result = $this->call_trigger('MEMBER_TYPE_SET_MULTILANGS', $user);
|
||||
if ($result < 0) {
|
||||
$this->error = $this->db->lasterror();
|
||||
return -1;
|
||||
if (empty($notrigger)) {
|
||||
// Call trigger
|
||||
$result = $this->call_trigger('MEMBER_TYPE_SET_MULTILANGS', $user);
|
||||
if ($result < 0) {
|
||||
$this->error = $this->db->lasterror();
|
||||
return -1;
|
||||
}
|
||||
// End call triggers
|
||||
}
|
||||
// End call triggers
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -391,9 +394,10 @@ class AdherentType extends CommonObject
|
||||
*
|
||||
* @param string $langtodelete Language code to delete
|
||||
* @param User $user Object user making delete
|
||||
* @return int Return integer <0 if KO, >0 if OK
|
||||
* @param int $notrigger Do not execute trigger
|
||||
* @return int Return integer <0 if KO, >0 if OK
|
||||
*/
|
||||
public function delMultiLangs($langtodelete, $user)
|
||||
public function delMultiLangs($langtodelete, $user, $notrigger = 0)
|
||||
{
|
||||
$sql = "DELETE FROM ".MAIN_DB_PREFIX."adherent_type_lang";
|
||||
$sql .= " WHERE fk_type = ".((int) $this->id)." AND lang = '".$this->db->escape($langtodelete)."'";
|
||||
@@ -401,14 +405,16 @@ class AdherentType extends CommonObject
|
||||
dol_syslog(get_class($this).'::delMultiLangs', LOG_DEBUG);
|
||||
$result = $this->db->query($sql);
|
||||
if ($result) {
|
||||
// Call trigger
|
||||
$result = $this->call_trigger('MEMBER_TYPE_DEL_MULTILANGS', $user);
|
||||
if ($result < 0) {
|
||||
$this->error = $this->db->lasterror();
|
||||
dol_syslog(get_class($this).'::delMultiLangs error='.$this->error, LOG_ERR);
|
||||
return -1;
|
||||
if (empty($notrigger)) {
|
||||
// Call trigger
|
||||
$result = $this->call_trigger('MEMBER_TYPE_DEL_MULTILANGS', $user);
|
||||
if ($result < 0) {
|
||||
$this->error = $this->db->lasterror();
|
||||
dol_syslog(get_class($this).'::delMultiLangs error='.$this->error, LOG_ERR);
|
||||
return -1;
|
||||
}
|
||||
// End call triggers
|
||||
}
|
||||
// End call triggers
|
||||
return 1;
|
||||
} else {
|
||||
$this->error = $this->db->lasterror();
|
||||
@@ -523,7 +529,7 @@ class AdherentType extends CommonObject
|
||||
|
||||
// Multilangs
|
||||
if (getDolGlobalInt('MAIN_MULTILANGS')) {
|
||||
if ($this->setMultiLangs($user) < 0) {
|
||||
if ($this->setMultiLangs($user, $notrigger) < 0) {
|
||||
$this->error = $langs->trans("Error")." : ".$this->db->error()." - ".$sql;
|
||||
return -2;
|
||||
}
|
||||
@@ -927,23 +933,68 @@ class AdherentType extends CommonObject
|
||||
* Return translated label by the nature of a adherent (physical or moral)
|
||||
*
|
||||
* @param string $morphy Nature of the adherent (physical or moral)
|
||||
* @param int<0,2> $addbadge Add badge (1=Full label, 2=First letters only)
|
||||
* @return string Label
|
||||
*/
|
||||
public function getmorphylib($morphy = '')
|
||||
public function getmorphylib($morphy = '', $addbadge = 0)
|
||||
{
|
||||
global $langs;
|
||||
|
||||
$s = '';
|
||||
|
||||
if ($morphy == 'phy') {
|
||||
return $langs->trans("Physical");
|
||||
$s = $langs->trans("Physical");
|
||||
} elseif ($morphy == 'mor') {
|
||||
return $langs->trans("Moral");
|
||||
$s = $langs->trans("Moral");
|
||||
} else {
|
||||
return $langs->trans("MorAndPhy");
|
||||
$s = $langs->trans("MorAndPhy");
|
||||
}
|
||||
//return $morphy;
|
||||
|
||||
if ($addbadge) {
|
||||
$labeltoshowm = $langs->trans("Moral");
|
||||
$labeltoshowp = $langs->trans("Physical");
|
||||
|
||||
$labeltoshow = $s;
|
||||
if ($morphy === 'phy') {
|
||||
if ($addbadge == 2) {
|
||||
$labeltoshow = dol_strtoupper(dolGetFirstLetters($labeltoshowp));
|
||||
if ($labeltoshow == dol_strtoupper(dolGetFirstLetters($labeltoshow))) {
|
||||
$labeltoshow = dol_strtoupper(dolGetFirstLetters($labeltoshow, 2));
|
||||
}
|
||||
}
|
||||
$s = '<span class="member-individual-back paddingleftimp paddingrightimp" title="'.$langs->trans("Physical").'">'.$labeltoshow.'</span>';
|
||||
}
|
||||
if ($morphy === 'mor') {
|
||||
if ($addbadge == 2) {
|
||||
$labeltoshow = dol_strtoupper(dolGetFirstLetters($labeltoshowm));
|
||||
if ($labeltoshow == dol_strtoupper(dolGetFirstLetters($labeltoshow))) {
|
||||
$labeltoshow = dol_strtoupper(dolGetFirstLetters($labeltoshow, 2));
|
||||
}
|
||||
}
|
||||
$s = '<span class="member-company-back paddingleftimp paddingrightimp" title="'.$langs->trans("Moral").'">'.$labeltoshow.'</span>';
|
||||
}
|
||||
if ($morphy === '') {
|
||||
if ($addbadge == 2) {
|
||||
$labeltoshow1 = dol_strtoupper(dolGetFirstLetters($labeltoshowp));
|
||||
if ($labeltoshow1 == dol_strtoupper(dolGetFirstLetters($labeltoshow1))) {
|
||||
$labeltoshow1 = dol_strtoupper(dolGetFirstLetters($labeltoshow1, 2));
|
||||
}
|
||||
$labeltoshow2 = dol_strtoupper(dolGetFirstLetters($labeltoshowm));
|
||||
if ($labeltoshow2 == dol_strtoupper(dolGetFirstLetters($labeltoshow2))) {
|
||||
$labeltoshow2 = dol_strtoupper(dolGetFirstLetters($labeltoshow2, 2));
|
||||
}
|
||||
$labeltoshow = $labeltoshow1.' '.$langs->trans("or").' '.$labeltoshow2;
|
||||
}
|
||||
$s = '<span class="member-individual-company-back paddingleftimp paddingrightimp" title="'.$langs->trans("MorAndPhy").'">'.$labeltoshow.'</span>';
|
||||
}
|
||||
}
|
||||
|
||||
return $s;
|
||||
}
|
||||
|
||||
/**
|
||||
* getTooltipContentArray
|
||||
*
|
||||
* @param array<string,mixed> $params params to construct tooltip data
|
||||
* @since v18
|
||||
* @return array{picto?:string,ref?:string,refsupplier?:string,label?:string,date?:string,date_echeance?:string,amountht?:string,total_ht?:string,totaltva?:string,amountlt1?:string,amountlt2?:string,amountrevenustamp?:string,totalttc?:string}|array{optimize:string}
|
||||
|
||||
@@ -31,12 +31,6 @@
|
||||
|
||||
// Load Dolibarr environment
|
||||
require '../main.inc.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent_type.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/adherents/class/subscription.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
|
||||
|
||||
|
||||
/**
|
||||
* @var Conf $conf
|
||||
* @var DoliDB $db
|
||||
@@ -44,6 +38,10 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
|
||||
* @var Translate $langs
|
||||
* @var User $user
|
||||
*/
|
||||
require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent_type.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/adherents/class/subscription.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
|
||||
|
||||
// Load translation files required by the page
|
||||
$langs->loadLangs(array("companies", "members"));
|
||||
@@ -108,7 +106,7 @@ if ($conf->use_javascript_ajax) {
|
||||
|
||||
$boxgraph .= '<div class="div-table-responsive-no-min">';
|
||||
$boxgraph .= '<table class="noborder nohover centpercent">';
|
||||
$boxgraph .= '<tr class="liste_titre"><th colspan="2">'.$langs->trans("Statistics").($numberyears ? ' ('.($year - $numberyears).' - '.$year.')' : '').'</th></tr>';
|
||||
$boxgraph .= '<tr class="liste_titre"><th colspan="2">'.$langs->trans("Statistics").' - '.$langs->trans("Status").' '.($numberyears ? ' ('.($year - $numberyears).' - '.$year.')' : '').'</th></tr>';
|
||||
$boxgraph .= '<tr><td class="center" colspan="2">';
|
||||
|
||||
require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherentstats.class.php';
|
||||
|
||||
@@ -1334,7 +1334,7 @@ while ($i < $imaxinloop) {
|
||||
}
|
||||
// Firstname
|
||||
if (!empty($arrayfields['d.firstname']['checked'])) {
|
||||
print '<td class="tdoverflowmax125" title="'.dol_escape_htmltag($obj->firstname).'">';
|
||||
print '<td class="tdoverflowmax125">';
|
||||
print $memberstatic->getNomUrl(0, 0, 'card', 'firstname');
|
||||
//print $obj->firstname;
|
||||
print "</td>\n";
|
||||
@@ -1344,7 +1344,7 @@ while ($i < $imaxinloop) {
|
||||
}
|
||||
// Lastname
|
||||
if (!empty($arrayfields['d.lastname']['checked'])) {
|
||||
print '<td class="tdoverflowmax125" title="'.dol_escape_htmltag($obj->lastname).'">';
|
||||
print '<td class="tdoverflowmax125">';
|
||||
print $memberstatic->getNomUrl(0, 0, 'card', 'lastname');
|
||||
//print $obj->lastname;
|
||||
print "</td>\n";
|
||||
@@ -1365,7 +1365,7 @@ while ($i < $imaxinloop) {
|
||||
}
|
||||
// Company
|
||||
if (!empty($arrayfields['d.societe']['checked'])) {
|
||||
print '<td class="tdoverflowmax125" title="'.dolPrintHTMLForAttribute((string) $companyname).'">';
|
||||
print '<td class="tdoverflowmax125">';
|
||||
print $companynametoshow;
|
||||
print "</td>\n";
|
||||
}
|
||||
|
||||
@@ -44,10 +44,6 @@ $mode = GETPOST('mode') ? GETPOST('mode') : '';
|
||||
|
||||
|
||||
// Security check
|
||||
if ($user->socid > 0) {
|
||||
$action = '';
|
||||
$socid = $user->socid;
|
||||
}
|
||||
restrictedArea($user, 'adherent', '', '', 'cotisation');
|
||||
|
||||
$year = (int) dol_print_date(dol_now('gmt'), "%Y", 'gmt');
|
||||
@@ -187,7 +183,10 @@ foreach ($data as $val) {
|
||||
$nbactive = $val['nbactive'];
|
||||
|
||||
print '<tr class="oddeven">';
|
||||
print '<td>'.$memberstatic->getmorphylib($val['label']).'</td>';
|
||||
print '<td>';
|
||||
|
||||
print $memberstatic->getmorphylib($val['label'], 1);
|
||||
print '</td>';
|
||||
print '<td class="right">'.$nb.'</td>';
|
||||
print '<td class="right">'.$nbactive.'</td>';
|
||||
print '<td class="center">'.dol_print_date($val['lastdate'], 'dayhour', 'auto', null, false, 1).'</td>';
|
||||
|
||||
@@ -473,13 +473,7 @@ if (!$rowid && $action != 'create' && $action != 'edit') {
|
||||
}
|
||||
if (!empty($arrayfields['t.morphy']['checked'])) {
|
||||
print '<td class="center">';
|
||||
if ($objp->morphy == 'phy') {
|
||||
print $langs->trans("Physical");
|
||||
} elseif ($objp->morphy == 'mor') {
|
||||
print $langs->trans("Moral");
|
||||
} else {
|
||||
print $langs->trans("MorAndPhy");
|
||||
}
|
||||
print $membertype->getmorphylib($objp->morphy, 1);
|
||||
print '</td>';
|
||||
}
|
||||
if (!empty($arrayfields['t.duration']['checked'])) {
|
||||
@@ -502,8 +496,12 @@ if (!$rowid && $action != 'create' && $action != 'edit') {
|
||||
if (!empty($arrayfields['t.caneditamount']['checked'])) {
|
||||
print '<td class="center">'.yn($objp->caneditamount).'</td>';
|
||||
}
|
||||
// Minimum amount
|
||||
if (!empty($arrayfields['t.minimumamount']['checked'])) {
|
||||
print '<td class="center">'.price($objp->minimumamount).'</td>';
|
||||
print '<td class="center">';
|
||||
$minimumamount = ((is_null($objp->minimumamount) || $objp->minimumamount === '') ? '' : price($objp->minimumamount));
|
||||
print $minimumamount;
|
||||
print '</td>';
|
||||
}
|
||||
if (!empty($arrayfields['t.amount']['checked'])) {
|
||||
print '<td class="center">';
|
||||
@@ -603,7 +601,7 @@ if ($action == 'create') {
|
||||
print '</td></tr>';
|
||||
|
||||
print '<tr><td>'.$langs->trans("MinimumAmountShort").'</td><td>';
|
||||
print '<input name="minimumamount" size="5" value="'.(GETPOSTISSET('minimumamount') ? GETPOST('minimumamount') : price($minimumamount)).'">';
|
||||
print '<input name="minimumamount" size="5" value="'.(GETPOSTISSET('minimumamount') ? GETPOST('minimumamount') : ($minimumamount ? price($minimumamount): '')).'">';
|
||||
print '</td></tr>';
|
||||
|
||||
print '<tr><td>'.$langs->trans("RecommendedAmount").'</td><td>';
|
||||
@@ -676,7 +674,9 @@ if ($rowid > 0) {
|
||||
print '<table class="tableforfield border centpercent">';
|
||||
|
||||
// Morphy
|
||||
print '<tr><td>'.$langs->trans("MembersNature").'</td><td class="valeur" >'.$object->getmorphylib($object->morphy).'</td>';
|
||||
print '<tr><td>'.$langs->trans("MembersNature").'</td><td class="valeur" >';
|
||||
print $object->getmorphylib($object->morphy, 1);
|
||||
print '</td>';
|
||||
print '</tr>';
|
||||
|
||||
print '<tr><td>'.$form->textwithpicto($langs->trans("SubscriptionRequired"), $langs->trans("SubscriptionRequiredDesc")).'</td><td>';
|
||||
|
||||
@@ -30,11 +30,6 @@
|
||||
|
||||
// Load Dolibarr environment
|
||||
require '../main.inc.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formadmin.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/class/defaultvalues.class.php';
|
||||
|
||||
/**
|
||||
* @var Conf $conf
|
||||
* @var DoliDB $db
|
||||
@@ -42,6 +37,10 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/defaultvalues.class.php';
|
||||
* @var Translate $langs
|
||||
* @var User $user
|
||||
*/
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formadmin.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/class/defaultvalues.class.php';
|
||||
|
||||
// Load translation files required by the page
|
||||
$langs->loadLangs(array('companies', 'products', 'admin', 'sms', 'other', 'errors'));
|
||||
@@ -52,6 +51,7 @@ if (!$user->admin) {
|
||||
|
||||
$id = GETPOSTINT('rowid');
|
||||
$action = GETPOST('action', 'aZ09');
|
||||
$contextpage = GETPOST('contextpage', 'aZ09');
|
||||
$optioncss = GETPOST('optionscss', 'alphanohtml');
|
||||
|
||||
$mode = GETPOST('mode', 'aZ09') ? GETPOST('mode', 'aZ09') : 'createform'; // 'createform', 'filters', 'sortorder', 'focus'
|
||||
@@ -215,18 +215,22 @@ llxHeader('', $langs->trans("Setup"), $wikihelp, '', 0, 0, '', '', '', 'mod-admi
|
||||
|
||||
$param = '&mode='.$mode;
|
||||
|
||||
$enabledisablehtml = $langs->trans("EnableDefaultValues").' ';
|
||||
$enabledisablehtml = '<span class="divfilteralone">';
|
||||
if (!getDolGlobalString('MAIN_ENABLE_DEFAULT_VALUES')) {
|
||||
// Button off, click to enable
|
||||
$enabledisablehtml .= '<a class="reposition valignmiddle" href="'.$_SERVER["PHP_SELF"].'?action=setMAIN_ENABLE_DEFAULT_VALUES&token='.newToken().'&value=1'.$param.'">';
|
||||
$enabledisablehtml .= img_picto($langs->trans("Disabled"), 'switch_off');
|
||||
$enabledisablehtml .= '</a>';
|
||||
$enabledisablehtml .= '<a class="reposition valignmiddle nounderlineimp" href="'.$_SERVER["PHP_SELF"].'?action=setMAIN_ENABLE_DEFAULT_VALUES&token='.newToken().'&value=1'.$param.'">';
|
||||
} else {
|
||||
// Button on, click to disable
|
||||
$enabledisablehtml .= '<a class="reposition valignmiddle" href="'.$_SERVER["PHP_SELF"].'?action=setMAIN_ENABLE_DEFAULT_VALUES&token='.newToken().'&value=0'.$param.'">';
|
||||
$enabledisablehtml .= img_picto($langs->trans("Activated"), 'switch_on');
|
||||
$enabledisablehtml .= '</a>';
|
||||
$enabledisablehtml .= '<a class="reposition valignmiddle nounderlineimp" href="'.$_SERVER["PHP_SELF"].'?action=setMAIN_ENABLE_DEFAULT_VALUES&token='.newToken().'&value=0'.$param.'">';
|
||||
}
|
||||
$enabledisablehtml .= $langs->trans("EnableDefaultValues");
|
||||
if (!getDolGlobalString('MAIN_ENABLE_DEFAULT_VALUES')) {
|
||||
$enabledisablehtml .= img_picto($langs->trans("Disabled"), 'switch_off', 'class="paddingleft valignmiddle"');
|
||||
} else {
|
||||
$enabledisablehtml .= img_picto($langs->trans("Activated"), 'switch_on', 'class="paddingleft valignmiddle"');
|
||||
}
|
||||
$enabledisablehtml .= '</a>';
|
||||
$enabledisablehtml .= '</span>';
|
||||
|
||||
print load_fiche_titre($langs->trans("DefaultValues"), $enabledisablehtml, 'title_setup');
|
||||
|
||||
|
||||
@@ -805,6 +805,18 @@ if ($conf->use_javascript_ajax) {
|
||||
print '</td>';
|
||||
print '</tr>';
|
||||
|
||||
// EMAILCOLLECTOR_ISNOTANSWER_USE_REFERENCES: Consider generic References as answer for "isnotanswer" filter.
|
||||
print '<tr class="oddeven"><td>'.$form->textwithpicto($langs->trans("EmailCollectorIsNotAnswerUseReferences"), $langs->transnoentitiesnoconv("EmailCollectorIsNotAnswerUseReferencesHelp")).'</td>';
|
||||
print '<td class="left">';
|
||||
if ($conf->use_javascript_ajax) {
|
||||
print ajax_constantonoff('EMAILCOLLECTOR_ISNOTANSWER_USE_REFERENCES');
|
||||
} else {
|
||||
$arrval = array('0' => $langs->trans("No"), '1' => $langs->trans("Yes"));
|
||||
print $form->selectarray("EMAILCOLLECTOR_ISNOTANSWER_USE_REFERENCES", $arrval, getDolGlobalInt('EMAILCOLLECTOR_ISNOTANSWER_USE_REFERENCES', 0));
|
||||
}
|
||||
print '</td>';
|
||||
print '</tr>';
|
||||
|
||||
print '</table>';
|
||||
print '</div>';
|
||||
|
||||
|
||||
@@ -156,7 +156,7 @@ if (empty($reshook)) {
|
||||
}
|
||||
|
||||
if (!$error) {
|
||||
header('Location: ' . $_SERVER['PHP_SELF']);
|
||||
header('Location: '.DOL_URL_ROOT.'/admin/expensereport_rules.php');
|
||||
exit;
|
||||
} else {
|
||||
$action = '';
|
||||
@@ -170,7 +170,7 @@ if (empty($reshook)) {
|
||||
dol_print_error($object->db);
|
||||
}
|
||||
|
||||
header('Location: ' . $_SERVER['PHP_SELF']);
|
||||
header('Location: ' . DOL_URL_ROOT.'/admin/expensereport_rules.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
@@ -122,8 +122,8 @@ print '<br><br>';
|
||||
print $langs->trans("SetupDescription3b");
|
||||
if (!empty($setupcompanynotcomplete)) {
|
||||
$langs->load("errors");
|
||||
$warnpicto = img_warning($langs->trans("WarningMandatorySetupNotComplete"), 'style="padding-right: 6px;"');
|
||||
print '<br><div class="warning"><a href="'.DOL_URL_ROOT.'/admin/company.php?mainmenu=home&action=edit&token='.newToken().'">'.$warnpicto.' '.$langs->trans("WarningMandatorySetupNotComplete").'</a></div>';
|
||||
$warnpicto = img_warning($langs->trans("WarningMandatorySetupNotComplete"), 'style="padding-right: 10px;"');
|
||||
print '<br><div class="warning marginrightonly"><a href="'.DOL_URL_ROOT.'/admin/company.php?mainmenu=home&action=edit&token='.newToken().'">'.$warnpicto.$langs->trans("WarningMandatorySetupNotComplete").'</a></div>';
|
||||
}
|
||||
|
||||
print '</a>';
|
||||
@@ -132,7 +132,6 @@ print '</section>';
|
||||
print '<br>';
|
||||
print '<br>';
|
||||
|
||||
|
||||
// Show info setup modules
|
||||
|
||||
print '<section class="setupsection setupmodules cursorpointer">';
|
||||
@@ -153,8 +152,8 @@ print '<a class="nounderlineimp fontsize-1-1" href="'.DOL_URL_ROOT.'/admin/modul
|
||||
print '<br><br>'.$langs->trans("SetupDescription4b");
|
||||
if ($nbmodulesnotautoenabled < getDolGlobalInt('MAIN_MIN_NB_ENABLED_MODULE_FOR_WARNING', 1)) { // If only minimal initial modules enabled
|
||||
$langs->load("errors");
|
||||
$warnpicto = img_warning($langs->trans("WarningEnableYourModulesApplications"), 'style="padding-right: 6px;"');
|
||||
print '<br><div class="warning"><a href="'.DOL_URL_ROOT.'/admin/modules.php?mainmenu=home">'.$warnpicto.$langs->trans("WarningEnableYourModulesApplications").'</a></div>';
|
||||
$warnpicto = img_warning($langs->trans("WarningEnableYourModulesApplications"), 'style="padding-right: 10px;"');
|
||||
print '<br><div class="warning marginrightonly"><a href="'.DOL_URL_ROOT.'/admin/modules.php?mainmenu=home">'.$warnpicto.$langs->trans("WarningEnableYourModulesApplications").'</a></div>';
|
||||
}
|
||||
|
||||
print '</section>';
|
||||
|
||||
@@ -27,16 +27,16 @@
|
||||
|
||||
// Load Dolibarr environment
|
||||
require '../main.inc.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
|
||||
|
||||
/**
|
||||
* @var Conf $conf
|
||||
* @var DoliDB $db
|
||||
* @var HookManager $hookmanager
|
||||
* @var Societe $mysoc
|
||||
* @var Translate $langs
|
||||
* @var User $user
|
||||
*/
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
|
||||
|
||||
// Load translation files required by the page
|
||||
$langs->loadLangs(array('companies', 'products', 'admin'));
|
||||
|
||||
@@ -775,7 +775,7 @@ if ($mode == 'common' || $mode == 'commonkanban') {
|
||||
$moreforfilter .= dolGetButtonTitle($langs->trans('ViewKanban'), '', 'fa fa-th-list imgforviewmode', $_SERVER["PHP_SELF"].'?mode=commonkanban'.$param, '', ($mode == 'commonkanban' ? 2 : 1), array('morecss' => 'reposition'));
|
||||
$moreforfilter .= '</li></ul></div>';
|
||||
|
||||
$moreforfilter .= '<div class="divfilteralone colorbacktimesheet float valignmiddle">';
|
||||
$moreforfilter .= '<div class="divfilteralone colorbacktimesheet float valignmiddle nopaddingtopimp nopaddingbottomimp">';
|
||||
$moreforfilter .= '<div class="divsearchfield paddingtop paddingbottom valignmiddle inline-block">';
|
||||
$moreforfilter .= img_picto($langs->trans("Filter"), 'filter', 'class="paddingright opacityhigh hideonsmartphone"').'<input type="text" id="search_keyword" name="search_keyword" class="maxwidth125" value="'.dol_escape_htmltag($search_keyword).'" spellcheck="false" placeholder="'.dol_escape_htmltag($langs->trans('Keyword')).'">';
|
||||
$moreforfilter .= '</div>';
|
||||
|
||||
@@ -101,7 +101,9 @@ if ($action == 'update') {
|
||||
}
|
||||
}
|
||||
if (GETPOSTISSET($constvalue.'_URL')) {
|
||||
if (!dolibarr_set_const($db, $newconstvalue.'_URL', GETPOST($constvalue.'_URL'), 'chaine', 0, '', $conf->entity)) {
|
||||
$cleanurl = GETPOST($constvalue.'_URL');
|
||||
$cleanurl = preg_replace('/\/$/', '', $cleanurl);
|
||||
if (!dolibarr_set_const($db, $newconstvalue.'_URL', $cleanurl, 'chaine', 0, '', $conf->entity)) {
|
||||
$error++;
|
||||
}
|
||||
}
|
||||
@@ -443,7 +445,7 @@ if (count($listinsetup) > 0) {
|
||||
print '</tr>';
|
||||
|
||||
// Tenant
|
||||
if ($keybeforeprovider == 'MICROSOFT' || $keybeforeprovider == 'MICROSOFT2') {
|
||||
if ($keybeforeprovider == 'MICROSOFT' || $keybeforeprovider == 'MICROSOFT2' || $keybeforeprovider == 'MICROSOFT3') {
|
||||
print '<tr class="oddeven value">';
|
||||
print '<td><label for="'.$key[2].'">'.$langs->trans("OAUTH_TENANT").'</label></td>';
|
||||
print '<td><input type="text" size="100" id="OAUTH_'.$keybeforeprovider.($keyforprovider ? '-'.$keyforprovider : '').'_TENANT" name="OAUTH_'.$keybeforeprovider.($keyforprovider ? '-'.$keyforprovider : '').'_TENANT" value="'.getDolGlobalString('OAUTH_'.$keybeforeprovider.($keyforprovider ? '-'.$keyforprovider : '').'_TENANT').'">';
|
||||
|
||||
@@ -66,7 +66,6 @@ if (!$user->admin) {
|
||||
accessforbidden();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Action
|
||||
*/
|
||||
@@ -433,7 +432,7 @@ if ($mode == 'setup' && $user->admin) {
|
||||
print '<td>';
|
||||
if ($keyforprovider != 'Login') {
|
||||
if (is_object($tokenobj)) {
|
||||
print $form->textwithpicto(yn(1), $langs->trans("HasAccessToken").' : '.dol_print_date($storage->date_modification, 'dayhour').' state='.dol_escape_htmltag($storage->state));
|
||||
print $form->textwithpicto(yn(1), $langs->trans("HasAccessToken").' : '.dol_print_date($storage->date_modification, 'dayhour').'<br>Scopes saved into field state='.dol_escape_htmltag($storage->state));
|
||||
} else {
|
||||
print '<span class="opacitymedium">'.$langs->trans("NoAccessToken").'</span>';
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ require '../main.inc.php';
|
||||
require_once DOL_DOCUMENT_ROOT . '/core/lib/admin.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT . '/core/lib/openid_connect.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT . '/core/class/html.form.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT . '/core/lib/openid_connect.lib.php';
|
||||
|
||||
/**
|
||||
* @var Conf $conf
|
||||
@@ -118,6 +117,41 @@ if ($action == 'set') {
|
||||
$errors[] = $db->lasterror();
|
||||
$error++;
|
||||
}
|
||||
|
||||
$value = GETPOST('MAIN_AUTHENTICATION_OIDC_DEFAULT_GROUP', 'int');
|
||||
$res = dolibarr_set_const($db, 'MAIN_AUTHENTICATION_OIDC_DEFAULT_GROUP', $value, 'chaine', 0, '', 0);
|
||||
if (!$res > 0) {
|
||||
$errors[] = $db->lasterror();
|
||||
$error++;
|
||||
}
|
||||
|
||||
$value = GETPOSTINT('MAIN_AUTHENTICATION_OIDC_DEFAULT_CREATOR');
|
||||
$res = dolibarr_set_const($db, 'MAIN_AUTHENTICATION_OIDC_DEFAULT_CREATOR', $value, 'chaine', 0, '', 0);
|
||||
if (!$res > 0) {
|
||||
$errors[] = $db->lasterror();
|
||||
$error++;
|
||||
}
|
||||
|
||||
$value = GETPOST('MAIN_AUTHENTICATION_OIDC_CLAIM_FIRSTNAME', 'alpha');
|
||||
$res = dolibarr_set_const($db, 'MAIN_AUTHENTICATION_OIDC_CLAIM_FIRSTNAME', $value, 'chaine', 0, '', 0);
|
||||
if (!$res > 0) {
|
||||
$errors[] = $db->lasterror();
|
||||
$error++;
|
||||
}
|
||||
|
||||
$value = GETPOST('MAIN_AUTHENTICATION_OIDC_CLAIM_LASTNAME', 'alpha');
|
||||
$res = dolibarr_set_const($db, 'MAIN_AUTHENTICATION_OIDC_CLAIM_LASTNAME', $value, 'chaine', 0, '', 0);
|
||||
if (!$res > 0) {
|
||||
$errors[] = $db->lasterror();
|
||||
$error++;
|
||||
}
|
||||
|
||||
$value = GETPOST('MAIN_AUTHENTICATION_OIDC_CLAIM_EMAIL', 'alpha');
|
||||
$res = dolibarr_set_const($db, 'MAIN_AUTHENTICATION_OIDC_CLAIM_EMAIL', $value, 'chaine', 0, '', 0);
|
||||
if (!$res > 0) {
|
||||
$errors[] = $db->lasterror();
|
||||
$error++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($action != '') {
|
||||
@@ -300,8 +334,70 @@ if (getDolGlobalString('MAIN_AUTHENTICATION_OIDC_ON')) {
|
||||
print '<input name="MAIN_AUTHENTICATION_OPENID_URL_IMG" id="MAIN_AUTHENTICATION_OPENID_URL_IMG" class="minwidth400 centpercent" value="' . dol_escape_htmltag((GETPOSTISSET('MAIN_AUTHENTICATION_OPENID_URL_IMG') ? GETPOST('MAIN_AUTHENTICATION_OPENID_URL_IMG', 'nohtml') : (getDolGlobalString('MAIN_AUTHENTICATION_OPENID_URL_IMG') ? getDolGlobalString("MAIN_AUTHENTICATION_OPENID_URL_IMG") : ''))) . '">';
|
||||
print '</td></tr>' . "\n";
|
||||
|
||||
print '</table>' . "\n";
|
||||
print '</div>';
|
||||
// --- User Auto-Creation Settings ---
|
||||
print '</table></div>' . "\n";
|
||||
|
||||
$langs->load("errors");
|
||||
global $dolibarr_main_authentication_autocreateuser;
|
||||
if (empty($dolibarr_main_authentication_autocreateuser)) {
|
||||
print info_admin($langs->trans("OIDCAutocreateUserDisabled"), 0, 0, 1, 'warning');
|
||||
} else {
|
||||
print info_admin($langs->trans("OIDCAutocreateUserEnabled"), 0, 0, 1, 'success');
|
||||
}
|
||||
|
||||
if (!empty($dolibarr_main_authentication_autocreateuser)) {
|
||||
print '<div class="div-table-responsive-no-min">';
|
||||
print '<table class="tagtable noborder liste nobottomiftotal">';
|
||||
print '<tr class="liste_titre">';
|
||||
print '<th class="liste_titre" colspan="3">' . $langs->trans("MainAuthenticationOidcAutoCreateTitle") . '</th>' . "\n";
|
||||
print "</tr>\n";
|
||||
|
||||
// MAIN_AUTHENTICATION_OIDC_DEFAULT_CREATOR
|
||||
$form = new Form($db);
|
||||
print '<tr class="oddeven">' . "\n";
|
||||
print '<td>' . $langs->trans("MainAuthenticationOidcDefaultCreatorName") . '</td>' . "\n";
|
||||
print '<td>' . $langs->trans("MainAuthenticationOidcDefaultCreatorDesc") . '</td>' . "\n";
|
||||
print '<td align="right">' . "\n";
|
||||
$creator_val = GETPOSTISSET('MAIN_AUTHENTICATION_OIDC_DEFAULT_CREATOR') ? GETPOSTINT('MAIN_AUTHENTICATION_OIDC_DEFAULT_CREATOR') : getDolGlobalInt('MAIN_AUTHENTICATION_OIDC_DEFAULT_CREATOR');
|
||||
print $form->select_dolusers($creator_val, 'MAIN_AUTHENTICATION_OIDC_DEFAULT_CREATOR', 1, null, 0, '', '', '', 0, 0, '(admin:=:1) AND (statut:=:1)', 0, '', 'minwidth200 maxwidth500');
|
||||
print '</td></tr>' . "\n";
|
||||
|
||||
// MAIN_AUTHENTICATION_OIDC_DEFAULT_GROUP
|
||||
print '<tr class="oddeven">' . "\n";
|
||||
print '<td>' . $langs->trans("MainAuthenticationOidcDefaultGroupName") . '</td>' . "\n";
|
||||
print '<td>' . $langs->trans("MainAuthenticationOidcDefaultGroupDesc") . '</td>' . "\n";
|
||||
print '<td align="right">' . "\n";
|
||||
$defaultgroup_val = GETPOSTISSET('MAIN_AUTHENTICATION_OIDC_DEFAULT_GROUP') ? GETPOSTINT('MAIN_AUTHENTICATION_OIDC_DEFAULT_GROUP') : getDolGlobalInt('MAIN_AUTHENTICATION_OIDC_DEFAULT_GROUP');
|
||||
$form->select_dolgroups($defaultgroup_val, 'MAIN_AUTHENTICATION_OIDC_DEFAULT_GROUP', 1);
|
||||
print '</td></tr>' . "\n";
|
||||
|
||||
// MAIN_AUTHENTICATION_OIDC_CLAIM_FIRSTNAME
|
||||
print '<tr class="oddeven">' . "\n";
|
||||
print '<td>' . $langs->trans("MainAuthenticationOidcClaimFirstnameName") . '</td>' . "\n";
|
||||
print '<td>' . $langs->trans("MainAuthenticationOidcClaimFirstnameDesc") . '</td>' . "\n";
|
||||
print '<td align="right">' . "\n";
|
||||
print '<input name="MAIN_AUTHENTICATION_OIDC_CLAIM_FIRSTNAME" id="MAIN_AUTHENTICATION_OIDC_CLAIM_FIRSTNAME" class="minwidth400 centpercent" value="' . dol_escape_htmltag((GETPOSTISSET('MAIN_AUTHENTICATION_OIDC_CLAIM_FIRSTNAME') ? GETPOST('MAIN_AUTHENTICATION_OIDC_CLAIM_FIRSTNAME', 'nohtml') : (getDolGlobalString('MAIN_AUTHENTICATION_OIDC_CLAIM_FIRSTNAME') ? getDolGlobalString("MAIN_AUTHENTICATION_OIDC_CLAIM_FIRSTNAME") : ''))) . '" placeholder="given_name">';
|
||||
print '</td></tr>' . "\n";
|
||||
|
||||
// MAIN_AUTHENTICATION_OIDC_CLAIM_LASTNAME
|
||||
print '<tr class="oddeven">' . "\n";
|
||||
print '<td>' . $langs->trans("MainAuthenticationOidcClaimLastnameName") . '</td>' . "\n";
|
||||
print '<td>' . $langs->trans("MainAuthenticationOidcClaimLastnameDesc") . '</td>' . "\n";
|
||||
print '<td align="right">' . "\n";
|
||||
print '<input name="MAIN_AUTHENTICATION_OIDC_CLAIM_LASTNAME" id="MAIN_AUTHENTICATION_OIDC_CLAIM_LASTNAME" class="minwidth400 centpercent" value="' . dol_escape_htmltag((GETPOSTISSET('MAIN_AUTHENTICATION_OIDC_CLAIM_LASTNAME') ? GETPOST('MAIN_AUTHENTICATION_OIDC_CLAIM_LASTNAME', 'nohtml') : (getDolGlobalString('MAIN_AUTHENTICATION_OIDC_CLAIM_LASTNAME') ? getDolGlobalString("MAIN_AUTHENTICATION_OIDC_CLAIM_LASTNAME") : ''))) . '" placeholder="family_name">';
|
||||
print '</td></tr>' . "\n";
|
||||
|
||||
// MAIN_AUTHENTICATION_OIDC_CLAIM_EMAIL
|
||||
print '<tr class="oddeven">' . "\n";
|
||||
print '<td>' . $langs->trans("MainAuthenticationOidcClaimEmailName") . '</td>' . "\n";
|
||||
print '<td>' . $langs->trans("MainAuthenticationOidcClaimEmailDesc") . '</td>' . "\n";
|
||||
print '<td align="right">' . "\n";
|
||||
print '<input name="MAIN_AUTHENTICATION_OIDC_CLAIM_EMAIL" id="MAIN_AUTHENTICATION_OIDC_CLAIM_EMAIL" class="minwidth400 centpercent" value="' . dol_escape_htmltag((GETPOSTISSET('MAIN_AUTHENTICATION_OIDC_CLAIM_EMAIL') ? GETPOST('MAIN_AUTHENTICATION_OIDC_CLAIM_EMAIL', 'nohtml') : (getDolGlobalString('MAIN_AUTHENTICATION_OIDC_CLAIM_EMAIL') ? getDolGlobalString("MAIN_AUTHENTICATION_OIDC_CLAIM_EMAIL") : ''))) . '" placeholder="email">';
|
||||
print '</td></tr>' . "\n";
|
||||
|
||||
print '</table>' . "\n";
|
||||
print '</div>';
|
||||
} // end if autocreateuser
|
||||
|
||||
print '<br>';
|
||||
print '<div align="center">';
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
# PrestaShop-webservice-lib
|
||||
|
||||
Source updated from:
|
||||
|
||||
https://github.com/PrestaShop/PrestaShop-webservice-lib/blob/master/PSWebServiceLibrary.php
|
||||
|
||||
## License compatibility analysis
|
||||
|
||||
https://www.gnu.org/licenses/license-list.html#OSL
|
||||
|
||||
|
||||
## Local changes
|
||||
|
||||
- Change `executeRequest` to public method because
|
||||
`htdocs/admin/remotestore/ajax/image.php#` uses it.
|
||||
@@ -8,6 +8,7 @@
|
||||
* Copyright (C) 2011-2018 Philippe Grand <philippe.grand@atoo-net.com>
|
||||
* Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
|
||||
* Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
|
||||
* Copyright (C) 2026 Pierre Ardoin <developpeur@lesmetiersdubatiment.fr>
|
||||
*
|
||||
* 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
|
||||
@@ -499,6 +500,12 @@ print '</td><td class="right">';
|
||||
print '<input type="submit" class="button button-edit" value="'.$langs->trans("Modify").'">';
|
||||
print "</td></tr>\n";
|
||||
|
||||
// Allow external download
|
||||
print '<tr class="oddeven">';
|
||||
print '<td>'.$langs->trans("AllowExternalDownload").'</td>';
|
||||
print '<td class="left" colspan="2">';
|
||||
print ajax_constantonoff('SUPPLIER_INVOICE_ALLOW_EXTERNAL_DOWNLOAD', array(), null, 0, 0, 0, 2, 0, 1);
|
||||
print '</td></tr>';
|
||||
|
||||
// Notifications
|
||||
print '<tr class="oddeven">';
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
* Copyright (C) 2011-2018 Philippe Grand <philippe.grand@atoo-net.com>
|
||||
* Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
|
||||
* Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
|
||||
* Copyright (C) 2026 Pierre Ardoin <developpeur@lesmetiersdubatiment.fr>
|
||||
*
|
||||
* 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
|
||||
@@ -538,6 +539,12 @@ print '<td colspan="2">';
|
||||
print ajax_constantonoff('SUPPLIER_ORDER_DISABLE_CLASSIFY_BILLED_FROM_SUPPLIER_ORDER');
|
||||
print '</td></tr>';
|
||||
|
||||
// Allow external download
|
||||
print '<tr class="oddeven">';
|
||||
print '<td>'.$langs->trans("AllowExternalDownload").'</td>';
|
||||
print '<td class="left" colspan="2">';
|
||||
print ajax_constantonoff('SUPPLIER_ORDER_ALLOW_EXTERNAL_DOWNLOAD', array(), null, 0, 0, 0, 2, 0, 1);
|
||||
print '</td></tr>';
|
||||
|
||||
// Notifications
|
||||
print '<tr class="oddeven">';
|
||||
|
||||
@@ -421,7 +421,7 @@ if (empty($dolibarr_main_restrict_eval_methods)) {
|
||||
} else {
|
||||
print $dolibarr_main_restrict_eval_methods;
|
||||
}
|
||||
print ' <span class="opacitymedium">('.$langs->trans("RecommendedValueIs", 'getDolGlobalString, getDolGlobalInt, getDolCurrency, getDolEntity, getDolDBType, fetchNoCompute, hasRight, isAdmin, isModEnabled, isStringVarMatching, abs, min, max, round, dol_now, dol_concat, preg_match').')</span>';
|
||||
print ' <span class="opacitymedium">('.$langs->trans("RecommendedValueIs", 'getDolGlobalString, getDolGlobalInt, getDolCurrency, getDolEntity, getDolDBType, fetchNoCompute, hasRight, isAdmin, isExternalUser, isModEnabled, isStringVarMatching, abs, min, max, round, dol_now, dol_concat, preg_match').')</span>';
|
||||
print '<br>';
|
||||
|
||||
|
||||
|
||||
@@ -708,7 +708,7 @@ print '<tr class="oddeven"><td class="titlefieldmiddle"><label for="TICKET_SEND_
|
||||
print $form->textwithpicto($langs->trans("TicketSendToInternalCC"), $langs->trans("TicketSendToInternalCCHelp")).'</label></td>';
|
||||
print '<td>';
|
||||
print img_picto('', 'email', 'class="pictofixedwidth"');
|
||||
print '<input class="flat width300" name="TICKET_SEND_INTERNAL_CC" value="'.getDolGlobalString('TICKET_SEND_INTERNAL_CC').'">';
|
||||
print '<input class="minwidth200" name="TICKET_SEND_INTERNAL_CC" value="'.getDolGlobalString('TICKET_SEND_INTERNAL_CC').'">';
|
||||
print '</td>';
|
||||
print '<td></td>';
|
||||
print '</tr>';
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file htdocs/admint/tools/ui/class/uidoc.class.php
|
||||
* \file htdocs/admint/tools/ui/class/documentation.class.php
|
||||
* \ingroup ui
|
||||
* \brief File of class to manage UI documentation
|
||||
*/
|
||||
@@ -155,6 +155,7 @@ class Documentation
|
||||
'summary' => array(
|
||||
'DocBasicUsage' => '#seteventmessagesection-basicusage',
|
||||
'DocSetEventMessageContextualVariations' => '#seteventmessagesection-contextvariations',
|
||||
'DocSetEventMessageJsContext' => '#titlesection-tool-seteventmessage',
|
||||
)
|
||||
),
|
||||
'Inputs' => array(
|
||||
@@ -251,6 +252,7 @@ class Documentation
|
||||
'JSDolibarrhooks' => '#titlesection-hooks',
|
||||
'JSDolibarrhooksReadyVsInit' => '#titlesection-event-init-vs-ready',
|
||||
'JSDolibarrAwaitHooks' => '#titlesection-await-hooks',
|
||||
'JSDolibarrhooksAjaxSpecial' => '#titlesection-dom-initnewcontent',
|
||||
'ExampleOfCreatingNewContextTool' => '#titlesection-create-tool-example',
|
||||
'SetEventMessageTool' => '#titlesection-tool-seteventmessage',
|
||||
'SetAndUseContextVars' => '#titlesection-contextvars',
|
||||
|
||||
@@ -90,7 +90,7 @@ $documentation->showSidebar(); ?>
|
||||
'<h5>Example heading <span class="badge badge-secondary">New</span></h5>',
|
||||
'<h6>Example heading <span class="badge badge-secondary">New</span></h6>'
|
||||
);
|
||||
echo $documentation->showCode($lines); ?>
|
||||
$documentation->showCode($lines); ?>
|
||||
|
||||
<p class="documentation-text"><?php echo $langs->trans('DocBadgeUseOnLinksOrButtons'); ?></p>
|
||||
<div class="documentation-example">
|
||||
@@ -105,7 +105,7 @@ $documentation->showSidebar(); ?>
|
||||
' Notifications <span class="badge badge-primary">4</span>',
|
||||
'</button>',
|
||||
);
|
||||
echo $documentation->showCode($lines); ?>
|
||||
$documentation->showCode($lines); ?>
|
||||
|
||||
<div class="warning">
|
||||
<p class="documentation-text"><?php echo $langs->trans('DocBadgeWarningAriaHidden1'); ?></p>
|
||||
@@ -127,7 +127,7 @@ $documentation->showSidebar(); ?>
|
||||
' <span class="sr-only">unread messages</span>',
|
||||
'</button>',
|
||||
);
|
||||
echo $documentation->showCode($lines); ?>
|
||||
$documentation->showCode($lines); ?>
|
||||
</div>
|
||||
|
||||
<!-- Contextual variations -->
|
||||
@@ -155,7 +155,7 @@ $documentation->showSidebar(); ?>
|
||||
'<span class="badge badge-light">Light</span>',
|
||||
'<span class="badge badge-dark">Dark</span>',
|
||||
);
|
||||
echo $documentation->showCode($lines); ?>
|
||||
$documentation->showCode($lines); ?>
|
||||
<div class="warning">
|
||||
<p class="documentation-text"><strong><?php echo $langs->trans('DocBadgeContextualVariationsWarning1'); ?></strong></p>
|
||||
<p class="documentation-text"><?php echo $langs->trans('DocBadgeContextualVariationsWarning2'); ?></p>
|
||||
@@ -176,7 +176,7 @@ $documentation->showSidebar(); ?>
|
||||
for ($i = 0; $i <= 9; $i++) :
|
||||
$lines[] = '<span class="badge badge-status'.$i.'">status-'.$i.'</span>';
|
||||
endfor;
|
||||
echo $documentation->showCode($lines); ?>
|
||||
$documentation->showCode($lines); ?>
|
||||
</div>
|
||||
|
||||
<!-- Pill badges -->
|
||||
@@ -210,7 +210,7 @@ $documentation->showSidebar(); ?>
|
||||
for ($i = 0; $i <= 9; $i++) :
|
||||
$lines[] = '<span class="badge badge-pill badge-status'.$i.'">status-'.$i.'</span>';
|
||||
endfor;
|
||||
echo $documentation->showCode($lines); ?>
|
||||
$documentation->showCode($lines); ?>
|
||||
</div>
|
||||
|
||||
<!-- Dot badges -->
|
||||
@@ -244,7 +244,7 @@ $documentation->showSidebar(); ?>
|
||||
for ($i = 0; $i <= 9; $i++) :
|
||||
$lines[] = '<span class="badge badge-dot badge-status'.$i.'"></span>';
|
||||
endfor;
|
||||
echo $documentation->showCode($lines); ?>
|
||||
$documentation->showCode($lines); ?>
|
||||
</div>
|
||||
|
||||
<!-- Links -->
|
||||
@@ -278,7 +278,7 @@ $documentation->showSidebar(); ?>
|
||||
for ($i = 0; $i <= 9; $i++) :
|
||||
$lines[] = '<a href="#" class="badge badge-status'.$i.'" >status'.$i.'</a>';
|
||||
endfor;
|
||||
echo $documentation->showCode($lines); ?>
|
||||
$documentation->showCode($lines); ?>
|
||||
</div>
|
||||
|
||||
<!-- Use badge helper function -->
|
||||
@@ -308,7 +308,7 @@ $documentation->showSidebar(); ?>
|
||||
"print dolGetBadge('your label for accessibility', 'your label <u>with</u> <em>html</em>', 'danger', 'pill');",
|
||||
"print dolGetBadge('your label for accessibility', 'your label', 'warning', 'dot');",
|
||||
);
|
||||
echo $documentation->showCode($lines, 'php'); ?>
|
||||
$documentation->showCode($lines, 'php'); ?>
|
||||
</div>
|
||||
|
||||
<!-- -->
|
||||
|
||||
@@ -135,7 +135,7 @@ $documentation->showSidebar(); ?>
|
||||
' */',
|
||||
'print dolGetButtonAction($label, $html, $actionType, $url, $id, $userRight, $params);',
|
||||
);
|
||||
echo $documentation->showCode($lines, 'php'); ?>
|
||||
$documentation->showCode($lines, 'php'); ?>
|
||||
</div>
|
||||
|
||||
<!-- Example of modal usage -->
|
||||
@@ -202,7 +202,7 @@ $documentation->showSidebar(); ?>
|
||||
'',
|
||||
'print dolGetButtonAction($label, $html, $actionType, $url, $id, $userRight, $params);',
|
||||
);
|
||||
echo $documentation->showCode($lines, 'php'); ?>
|
||||
$documentation->showCode($lines, 'php'); ?>
|
||||
</div>
|
||||
|
||||
<!-- Example of subbutton usage -->
|
||||
@@ -259,7 +259,7 @@ $documentation->showSidebar(); ?>
|
||||
');',
|
||||
'print dolGetButtonAction($label, $html, $actionType, $url, $id, $userRight, $params);'
|
||||
);
|
||||
echo $documentation->showCode($lines, 'php'); ?>
|
||||
$documentation->showCode($lines, 'php'); ?>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -286,7 +286,7 @@ $documentation->showSidebar(); ?>
|
||||
'print \' <button class="btn-low-emphasis --btn-icon" title="\'.dol_escape_htmltag($btnLabel).\'" aria-label="\'.dol_escape_htmltag($btnLabel).\'" >\'.img_picto($btnLabel, \'eraser\', \'aria-hidden="true"\', 0, 0, 1).\'</button>\';',
|
||||
|
||||
);
|
||||
echo $documentation->showCode($lines, 'php'); ?>
|
||||
$documentation->showCode($lines, 'php'); ?>
|
||||
</div>
|
||||
|
||||
<!-- Example of subbutton usage -->
|
||||
@@ -322,7 +322,7 @@ $documentation->showSidebar(); ?>
|
||||
'print dolGetButtonTitle($btnLabel, \'\', \'fa fa-file\', \'#\', \'\', $status);',
|
||||
);
|
||||
|
||||
echo $documentation->showCode($lines, 'php'); ?><div class="documentation-example">
|
||||
$documentation->showCode($lines, 'php'); ?><div class="documentation-example">
|
||||
<?php
|
||||
|
||||
$btnLabel = $langs->trans('Label', 'php');
|
||||
@@ -350,7 +350,7 @@ $documentation->showSidebar(); ?>
|
||||
'$status = -2; // Disabled without info',
|
||||
'print dolGetButtonTitle($btnLabel, \'\', \'fa fa-download\', \'#\', \'\', $status, [\'forcenohideoftext\'=>1]);',
|
||||
);
|
||||
echo $documentation->showCode($lines, 'php'); ?>
|
||||
$documentation->showCode($lines, 'php'); ?>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -127,7 +127,7 @@ $documentation->showSidebar(); ?>
|
||||
'setEventMessages("message", null);',
|
||||
'setEventMessages(null, messages[]);',
|
||||
);
|
||||
echo $documentation->showCode($lines, 'php'); ?>
|
||||
$documentation->showCode($lines, 'php'); ?>
|
||||
</div>
|
||||
|
||||
<!-- Contextual variations -->
|
||||
@@ -136,6 +136,10 @@ $documentation->showSidebar(); ?>
|
||||
<p class="documentation-text"><?php echo $langs->trans('DocSetEventMessageContextualVariationsDescription'); ?></p>
|
||||
<div class="documentation-example">
|
||||
<?php
|
||||
$params = [
|
||||
'attr' => []
|
||||
];
|
||||
|
||||
$label = 'My action label used for accessibility visually for impaired people';
|
||||
$user_right = 1;
|
||||
$html = '<span class="fa fa-comment paddingright"></span>'.$langs->trans('DocSetEventMessageDisplayOKMessage');
|
||||
@@ -167,9 +171,18 @@ $documentation->showSidebar(); ?>
|
||||
'setEventMessages("message", null, "warnings");',
|
||||
'setEventMessages("message", null, "errors");'
|
||||
);
|
||||
echo $documentation->showCode($lines, 'php'); ?>
|
||||
$documentation->showCode($lines, 'php'); ?>
|
||||
</div>
|
||||
<!-- -->
|
||||
|
||||
|
||||
|
||||
<?php
|
||||
$setEventMessageJsContextTitle = $langs->trans('DocSetEventMessageJsContext');
|
||||
include __DIR__ . '/../dolibarr-context/inc_seteventmessage.php';
|
||||
?>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -84,7 +84,6 @@ if (!GETPOST('hidenavmenu')) {
|
||||
<div class="documentation-section" id="img-picto-section-list">
|
||||
|
||||
<?php
|
||||
|
||||
$iconFileName = 'shims.json';
|
||||
$iconFilePath = DOL_DOCUMENT_ROOT . '/theme/common/fontawesome-5/metadata';
|
||||
|
||||
@@ -169,18 +168,27 @@ if (!GETPOST('hidenavmenu')) {
|
||||
<div class="documentation-section" id="icon-section-list">
|
||||
|
||||
<?php
|
||||
|
||||
$iconFileName = 'shims.json';
|
||||
$iconFileFa = 'icons.json';
|
||||
$iconFilePath = DOL_DOCUMENT_ROOT . '/theme/common/fontawesome-5/metadata';
|
||||
|
||||
$fontAwesomeIconRaw = file_get_contents($iconFilePath. '/' .$iconFileName);
|
||||
if ($fontAwesomeIconRaw === false) {
|
||||
dol_print_error($db, 'Error missing file '. $iconFilePath . '/' . $iconFileName);
|
||||
}
|
||||
// Load the full FontAwesome 5 icons JSON
|
||||
$allIconsRaw = file_get_contents($iconFilePath . '/' . $iconFileFa);
|
||||
$fontAwesomeIcons = []; // This will be the output array in shims.json format
|
||||
|
||||
$fontAwesomeIcons = json_decode($fontAwesomeIconRaw);
|
||||
if ($fontAwesomeIcons === null) {
|
||||
dol_print_error($db, 'Error decoding '. $iconFilePath . '/' . $iconFileName);
|
||||
if ($allIconsRaw === false) {
|
||||
dol_print_error($db, 'Error: missing file ' . $iconFilePath . '/' . $iconFileFa);
|
||||
} else {
|
||||
$allIcons = json_decode($allIconsRaw, true);
|
||||
if ($allIcons === null) {
|
||||
dol_print_error($db, 'Error: cannot decode JSON from ' . $iconFilePath . '/' . $iconFileFa);
|
||||
} else {
|
||||
foreach ($allIcons as $iconName => $iconData) {
|
||||
// Determine prefix: 'fab' for brands, 'fas' or 'far' can be added later if needed
|
||||
$prefix = in_array('brands', $iconData['styles']) ? 'fab' : null;
|
||||
// Format: [ "icon-name", "prefix if any", null ]
|
||||
$fontAwesomeIcons[] = [$iconName, $prefix, null]; // null reserved for future alias
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -208,7 +216,7 @@ if (!GETPOST('hidenavmenu')) {
|
||||
<div class="documentation-fontawesome-icon-list">
|
||||
<?php
|
||||
$alreadyDisplay = [];
|
||||
if ($fontAwesomeIcons && is_array($fontAwesomeIcons)) {
|
||||
if ($fontAwesomeIcons) {
|
||||
foreach ($fontAwesomeIcons as $iconData) {
|
||||
$class= $iconData[1]??'fa';
|
||||
if (!empty($iconData[2])) {
|
||||
@@ -223,6 +231,7 @@ if (!GETPOST('hidenavmenu')) {
|
||||
|
||||
$alreadyDisplay[] = $class;
|
||||
$iconCode = '<span class="'.$class.'" ></span>';
|
||||
$iconLabel = !empty($iconData[2]) ? $iconData[2] : $iconData[0];
|
||||
|
||||
if ($displayMode == 'kanban') {
|
||||
print '<div class="info-box ">
|
||||
@@ -230,7 +239,7 @@ if (!GETPOST('hidenavmenu')) {
|
||||
' . $iconCode . '
|
||||
</span>
|
||||
<div class="info-box-content">
|
||||
<div class="info-box-title" >' . ($iconData[2] ?? ($iconData[0] ?? '')) . '</div>
|
||||
<div class="info-box-title" >' . $iconLabel . '</div>
|
||||
<div class="info-box-lines">
|
||||
<div class="info-box-line spanoverflow nowrap">
|
||||
<div class="inline-block nowraponall">
|
||||
|
||||
@@ -90,7 +90,7 @@ $documentation->showSidebar(); ?>
|
||||
'Disabled Input',
|
||||
'<input id="label" name="label" class="minwidth200" maxlength="255" value="" disabled>',
|
||||
);
|
||||
echo $documentation->showCode($lines); ?>
|
||||
$documentation->showCode($lines); ?>
|
||||
|
||||
<!-- Checkbox input -->
|
||||
<p class="documentation-text"><?php echo $langs->trans('DocCheckboxInputsDescription'); ?></p>
|
||||
@@ -105,7 +105,7 @@ $documentation->showSidebar(); ?>
|
||||
'<span class="spannature paddinglarge marginrightonly nonature-back valignmiddle"><label for="customerinput" class="valignmiddle"><span class="valignmiddle">Customer</span><input id="customerinput" class="flat checkforselect marginleftonly valignmiddle" type="checkbox" name="customer" value="1" checked></label></span>',
|
||||
'<span class="spannature paddinglarge marginrightonly nonature-back valignmiddle"><label for="supplierinput" class="valignmiddle"><span class="valignmiddle">Supplier</span><input id="supplierinput" class="flat checkforselect marginleftonly valignmiddle" type="checkbox" name="supplier" value="1" checked></label></span>',
|
||||
);
|
||||
echo $documentation->showCode($lines); ?>
|
||||
$documentation->showCode($lines); ?>
|
||||
|
||||
<!-- Radio input -->
|
||||
<p class="documentation-text"><?php echo $langs->trans('DocRadioInputsDescription'); ?></p>
|
||||
@@ -118,7 +118,7 @@ $documentation->showSidebar(); ?>
|
||||
'<input type="radio" id="idforradioinput1" name="radioinput" value="value1"><label for="idforradioinput1" class="marginrightonly"> Radio Input 1</label>',
|
||||
'<input type="radio" id="idforradioinput2" name="radioinput" value="value2"><label for="idforradioinput2" class="marginrightonly"> Radio Input 2</label>'
|
||||
);
|
||||
echo $documentation->showCode($lines); ?>
|
||||
$documentation->showCode($lines); ?>
|
||||
</div>
|
||||
|
||||
<!-- Helper functions -->
|
||||
@@ -174,7 +174,7 @@ $documentation->showSidebar(); ?>
|
||||
'print $form->selectarray(\'htmlnameselectwithinemptyvalue\', $values, \'idnameselectwithinemptyvalue\', 0,0, 0, \'\', 0, 0, 0, \'\', \'minwidth200\');',
|
||||
|
||||
);
|
||||
echo $documentation->showCode($lines, 'php'); ?>
|
||||
$documentation->showCode($lines, 'php'); ?>
|
||||
|
||||
<!-- Multiselect input -->
|
||||
<p class="documentation-text"><?php echo $langs->trans('DocMultiSelectInputsDescription'); ?></p>
|
||||
@@ -212,7 +212,7 @@ $documentation->showSidebar(); ?>
|
||||
'// Multiselect',
|
||||
'print $form->multiselectarray(\'categories\', $values, GETPOST(\'categories\', \'array\'), 0, 0, \'minwidth200\', 0, 0);'
|
||||
);
|
||||
echo $documentation->showCode($lines, 'php'); ?>
|
||||
$documentation->showCode($lines, 'php'); ?>
|
||||
|
||||
<!-- Date input -->
|
||||
<p class="documentation-text"><?php echo $langs->trans('DocDateSelectInputsDescription'); ?></p>
|
||||
@@ -269,7 +269,7 @@ $documentation->showSidebar(); ?>
|
||||
'// Date Select with hours',
|
||||
'print $form->selectDate(\'\', \'re2\', 1, 1, 1);'
|
||||
);
|
||||
echo $documentation->showCode($lines, 'php'); ?>
|
||||
$documentation->showCode($lines, 'php'); ?>
|
||||
|
||||
|
||||
<!-- Editor input -->
|
||||
@@ -306,7 +306,7 @@ $documentation->showSidebar(); ?>
|
||||
'$doleditor = new DolEditor(\'desc\', GETPOST(\'desc\', \'restricthtml\'), \'\', 160, \'dolibarr_details\', \'\', false, true, getDolGlobalString(\'FCKEDITOR_ENABLE_DETAILS\'), ROWS_4, \'90%\');',
|
||||
'print $form->multiselectarray(\'categories\', $values, GETPOST(\'categories\', \'array\'), 0, 0, \'minwidth200\', 0, 0);'
|
||||
);
|
||||
echo $documentation->showCode($lines, 'php'); ?>
|
||||
$documentation->showCode($lines, 'php'); ?>
|
||||
</div>
|
||||
|
||||
<!-- Search Filter Tool Input -->
|
||||
@@ -376,7 +376,7 @@ $documentation->showSidebar(); ?>
|
||||
'</div>',
|
||||
|
||||
);
|
||||
echo $documentation->showCode($lines, 'php');
|
||||
$documentation->showCode($lines, 'php');
|
||||
|
||||
$lines = array(
|
||||
'<?php',
|
||||
@@ -403,7 +403,7 @@ $documentation->showSidebar(); ?>
|
||||
'</div>',
|
||||
|
||||
);
|
||||
echo $documentation->showCode($lines, 'php');
|
||||
$documentation->showCode($lines, 'php');
|
||||
|
||||
?>
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ $documentation->showSidebar(); ?>
|
||||
' <div class="progress-bar" role="progressbar" style="width: 80%" aria-valuenow="80" aria-valuemin="0" aria-valuemax="100"></div>',
|
||||
'</div>',
|
||||
);
|
||||
echo $documentation->showCode($lines); ?>
|
||||
$documentation->showCode($lines); ?>
|
||||
|
||||
<p class="documentation-text"><?php echo $langs->trans('DocProgressCanBeSpaced'); ?></p>
|
||||
<div class="documentation-example">
|
||||
@@ -128,7 +128,7 @@ $documentation->showSidebar(); ?>
|
||||
' <div class="progress-bar" role="progressbar" style="width: 40%" aria-valuenow="40" aria-valuemin="0" aria-valuemax="100"></div>',
|
||||
'</div>',
|
||||
);
|
||||
echo $documentation->showCode($lines); ?>
|
||||
$documentation->showCode($lines); ?>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -172,7 +172,7 @@ $documentation->showSidebar(); ?>
|
||||
'',
|
||||
|
||||
);
|
||||
echo $documentation->showCode($lines); ?>
|
||||
$documentation->showCode($lines); ?>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -216,7 +216,7 @@ $documentation->showSidebar(); ?>
|
||||
'',
|
||||
|
||||
);
|
||||
echo $documentation->showCode($lines); ?>
|
||||
$documentation->showCode($lines); ?>
|
||||
</div>
|
||||
<!-- -->
|
||||
|
||||
@@ -258,7 +258,7 @@ $documentation->showSidebar(); ?>
|
||||
'',
|
||||
|
||||
);
|
||||
echo $documentation->showCode($lines); ?>
|
||||
$documentation->showCode($lines); ?>
|
||||
</div>
|
||||
<!-- -->
|
||||
|
||||
|
||||
@@ -164,7 +164,7 @@ $documentation->showSidebar(); ?>
|
||||
'',
|
||||
'</table>',
|
||||
);
|
||||
echo $documentation->showCode($lines, 'php'); ?>
|
||||
$documentation->showCode($lines, 'php'); ?>
|
||||
</div>
|
||||
|
||||
<!-- Table with filters -->
|
||||
@@ -283,7 +283,7 @@ $documentation->showSidebar(); ?>
|
||||
' </table>',
|
||||
'</form>',
|
||||
);
|
||||
echo $documentation->showCode($lines); ?>
|
||||
$documentation->showCode($lines); ?>
|
||||
</div>
|
||||
|
||||
<!-- Add a row before filters -->
|
||||
@@ -386,7 +386,7 @@ $documentation->showSidebar(); ?>
|
||||
' <!-- Total -->',
|
||||
'</table>',
|
||||
);
|
||||
echo $documentation->showCode($lines); ?>
|
||||
$documentation->showCode($lines); ?>
|
||||
</div>
|
||||
|
||||
<!-- CSS classes for tables -->
|
||||
|
||||
@@ -101,7 +101,7 @@ $documentation->showSidebar(); ?>
|
||||
'// Title with custom image icon, 4th parameter must be 1',
|
||||
'print load_fiche_titre("'.$langs->trans('DocMyPageTitle').'", "", "IMAGE_URL", 1);',
|
||||
);
|
||||
echo $documentation->showCode($lines, 'php'); ?>
|
||||
$documentation->showCode($lines, 'php'); ?>
|
||||
|
||||
<p class="documentation-text"><?php echo $langs->trans('DocTitleMoreContentDescription'); ?></p>
|
||||
<div class="documentation-example">
|
||||
@@ -133,7 +133,7 @@ $documentation->showSidebar(); ?>
|
||||
'$moreContent = \'MyHTMLContent\';',
|
||||
'print load_fiche_titre("'.$langs->trans('DocMyPageTitle').'", $moreHtmlRight, "fa-rocket_fas_#b0bb39", 0, $tableID, $moreclass, $moreContent);',
|
||||
);
|
||||
echo $documentation->showCode($lines, 'php'); ?>
|
||||
$documentation->showCode($lines, 'php'); ?>
|
||||
</div>
|
||||
|
||||
<!-- Table with filters -->
|
||||
@@ -196,7 +196,7 @@ $documentation->showSidebar(); ?>
|
||||
' */',
|
||||
'print_barre_liste($title, $page, $file, $options, $sortfield, $sortorder, $morehtmlcenter, $num, $totalnboflines, $picto, $pictoisfullpath, $morehtmlright, $morecss, $limit, $selectlimitsuffix, $hidenavigation, $pagenavastextinput, $morehtmlrightbeforearrow);',
|
||||
);
|
||||
echo $documentation->showCode($lines, 'php'); ?>
|
||||
$documentation->showCode($lines, 'php'); ?>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @var string $setEventMessageJsContextTitle
|
||||
* @var Documentation $documentation
|
||||
*
|
||||
* @phan-var string|null $setEventMessageJsContextTitle
|
||||
* @phan-var Documentation $documentation
|
||||
*/
|
||||
|
||||
if (!defined('DOL_VERSION')) {die();}
|
||||
|
||||
global $documentation, $setEventMessageJsContextTitle;
|
||||
|
||||
if ($documentation === null || !($documentation instanceof Documentation)) { return; }
|
||||
|
||||
|
||||
?>
|
||||
|
||||
<div class="documentation-section">
|
||||
<h2 id="titlesection-tool-seteventmessage" class="documentation-title"><?php print $setEventMessageJsContextTitle ?? 'Set event message tool'; ?></h2>
|
||||
|
||||
<p>
|
||||
Instead of calling JNotify directly in your code, use Dolibarr’s setEventMessage tool.
|
||||
Dolibarr provides the configuration option DISABLE_JQUERY_JNOTIFY, which disables the jQuery JNotify system, usually because another notification library will be used instead.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you rely on Dolibarr.tools.setEventMessage(), your code remains compatible even if the underlying notification system changes.
|
||||
The setEventMessage tool can be replaced internally without requiring any changes in your modules or custom scripts.
|
||||
</p>
|
||||
<p>
|
||||
This means all developers can write features without worrying about frontend compatibility or future library replacements. Enjoy!
|
||||
|
||||
</p>
|
||||
<?php
|
||||
$lines = array(
|
||||
'<script nonce="<?php print getNonce() ?>" >',
|
||||
' document.addEventListener(\'Dolibarr:Ready\', function(e) {',
|
||||
'',
|
||||
' document.getElementById(\'setEventMessage-success\').addEventListener(\'click\', function(e) {',
|
||||
' Dolibarr.tools.setEventMessage(\'Success Test\');',
|
||||
' });',
|
||||
'',
|
||||
' document.getElementById(\'setEventMessage-error\').addEventListener(\'click\', function(e) {',
|
||||
' Dolibarr.tools.setEventMessage(\'Error Test\', \'errors\');',
|
||||
' });',
|
||||
'',
|
||||
' document.getElementById(\'setEventMessage-error-sticky\').addEventListener(\'click\', function(e) {',
|
||||
' Dolibarr.tools.setEventMessage(\'Error Test\', \'errors\', true);',
|
||||
' });',
|
||||
'',
|
||||
' document.getElementById(\'setEventMessage-warning\').addEventListener(\'click\', function(e) {',
|
||||
' Dolibarr.tools.setEventMessage(\'Warning Test\', \'warnings\');',
|
||||
' });',
|
||||
'',
|
||||
' });',
|
||||
'</script>',
|
||||
);
|
||||
$documentation->showCode($lines, 'php'); ?>
|
||||
<div class="documentation-example">
|
||||
|
||||
<script nonce="<?php print getNonce() ?>" >
|
||||
document.addEventListener('Dolibarr:Ready', function(e) {
|
||||
|
||||
document.getElementById('setEventMessage-success').addEventListener('click', function(e) {
|
||||
Dolibarr.tools.setEventMessage('Success Test')
|
||||
});
|
||||
|
||||
document.getElementById('setEventMessage-error').addEventListener('click', function(e) {
|
||||
Dolibarr.tools.setEventMessage('Error Test', 'errors');
|
||||
});
|
||||
|
||||
document.getElementById('setEventMessage-error-sticky').addEventListener('click', function(e) {
|
||||
Dolibarr.tools.setEventMessage('Error Test', 'errors', true);
|
||||
});
|
||||
|
||||
document.getElementById('setEventMessage-warning').addEventListener('click', function(e) {
|
||||
Dolibarr.tools.setEventMessage('Warning Test', 'warnings');
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
<button id="setEventMessage-success" class="button">Alert success</button>
|
||||
<button id="setEventMessage-error" class="button">Alert error</button>
|
||||
<button id="setEventMessage-error-sticky" class="button">Alert error sticky</button>
|
||||
<button id="setEventMessage-warning" class="button">Alert warning</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -40,8 +40,8 @@ $langs->load('uxdocumentation');
|
||||
|
||||
//
|
||||
$documentation = new Documentation($db);
|
||||
$group = 'ExperimentalUx';
|
||||
$experimentName = 'UxDolibarrContext';
|
||||
$group = 'UxDolibarrContext';
|
||||
$experimentName = 'UxDolibarrContextHowItWork';
|
||||
|
||||
$js = [
|
||||
'/includes/ace/src/ace.js',
|
||||
@@ -65,7 +65,7 @@ $documentation->showSidebar(); ?>
|
||||
|
||||
<div class="doc-content-wrapper">
|
||||
|
||||
<h1 class="documentation-title"><?php echo $langs->trans($experimentName); ?> : <?php echo $langs->trans('UxDolibarrContextHowItWork'); ?></h1>
|
||||
<h1 class="documentation-title"><?php echo $langs->trans($group); ?> : <?php echo $langs->trans('UxDolibarrContextHowItWork'); ?></h1>
|
||||
|
||||
<?php $documentation->showSummary(); ?>
|
||||
|
||||
@@ -137,7 +137,6 @@ $documentation->showSidebar(); ?>
|
||||
When enabled, <code>Dolibarr.log()</code> behaves like <code>console.log()</code>.
|
||||
</p>
|
||||
|
||||
<div class="documentation-example">
|
||||
<?php
|
||||
$lines = array(
|
||||
'<script>',
|
||||
@@ -153,7 +152,6 @@ $documentation->showSidebar(); ?>
|
||||
);
|
||||
$documentation->showCode($lines, 'php');
|
||||
?>
|
||||
</div>
|
||||
|
||||
<h3>Summary</h3>
|
||||
<ul>
|
||||
@@ -187,7 +185,6 @@ $documentation->showSidebar(); ?>
|
||||
<code>e.detail</code> and the event name is prefixed by <code>Dolibarr:</code> so for a hook named A event name is <code>Dolibarr:A</code>
|
||||
</p>
|
||||
|
||||
<div class="documentation-example">
|
||||
<?php
|
||||
$lines = array(
|
||||
'<script>',
|
||||
@@ -203,7 +200,7 @@ $documentation->showSidebar(); ?>
|
||||
'</script>',
|
||||
);
|
||||
$documentation->showCode($lines, 'php'); ?>
|
||||
</div>
|
||||
|
||||
|
||||
<h3>Practical usage</h3>
|
||||
<p>
|
||||
@@ -212,7 +209,6 @@ $documentation->showSidebar(); ?>
|
||||
are valid, but <code>Dolibarr.on()</code> is simpler and more convenient because you get the
|
||||
data directly.
|
||||
</p>
|
||||
<div class="documentation-example">
|
||||
<?php
|
||||
$lines = array(
|
||||
'<script>',
|
||||
@@ -246,6 +242,7 @@ $documentation->showSidebar(); ?>
|
||||
);
|
||||
$documentation->showCode($lines, 'php'); ?>
|
||||
|
||||
<div class="documentation-example">
|
||||
Open your console <code>F12</code> and click on <button class="button" id="try-event-yourCustomHookName">try</button>
|
||||
<script nonce="<?php print getNonce() ?>" >
|
||||
document.addEventListener('Dolibarr:Ready', function(e) {
|
||||
@@ -309,7 +306,6 @@ $documentation->showSidebar(); ?>
|
||||
</p>
|
||||
|
||||
<h3>Examples of usage</h3>
|
||||
<div class="documentation-example">
|
||||
<?php
|
||||
$lines = array(
|
||||
'<script>',
|
||||
@@ -334,7 +330,7 @@ $documentation->showSidebar(); ?>
|
||||
'</script>',
|
||||
);
|
||||
$documentation->showCode($lines, 'php'); ?>
|
||||
</div>
|
||||
|
||||
|
||||
<p>
|
||||
In summary:
|
||||
@@ -364,7 +360,6 @@ $documentation->showSidebar(); ?>
|
||||
This means you can <code>await</code> their results in your code, and any asynchronous operations inside a hook (e.g., API calls, timers) will be handled correctly before moving to the next hook.
|
||||
</p>
|
||||
|
||||
<div class="documentation-example">
|
||||
<?php
|
||||
$lines = array(
|
||||
'<script nonce="<?php print getNonce() ?>">',
|
||||
@@ -402,6 +397,7 @@ $documentation->showSidebar(); ?>
|
||||
);
|
||||
$documentation->showCode($lines, 'php'); ?>
|
||||
|
||||
<div class="documentation-example">
|
||||
Open your console <code>F12</code> and click on <button class="button" id="try-event-yourCustomAwaitHookName">try</button>
|
||||
|
||||
<script nonce="<?php print getNonce() ?>">
|
||||
@@ -439,6 +435,232 @@ $documentation->showSidebar(); ?>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="documentation-section">
|
||||
<h2 id="titlesection-dom-initnewcontent" class="documentation-title">
|
||||
initNewContent event system
|
||||
</h2>
|
||||
|
||||
<p>
|
||||
The <strong>initNewContent</strong> event is a standardized Dolibarr mechanism
|
||||
to re-initialize UI components on dynamically added content.
|
||||
Use it whenever you inject new DOM elements via AJAX, templates, or other dynamic updates.
|
||||
</p>
|
||||
|
||||
<h3 id="titlesection-usecase-tooltips" class="documentation-title">
|
||||
Use Case example: Dynamic Tooltips
|
||||
</h3>
|
||||
|
||||
<p>
|
||||
In a typical Dolibarr page, tooltips are initialized on page load for all elements
|
||||
that have the class <code>.classfortooltip</code>. This works perfectly for static content.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
However, when a section of the page is dynamically recreated or loaded via AJAX,
|
||||
the new elements with <code>.classfortooltip</code> do not automatically have tooltips,
|
||||
because the initialization script has already run on the initial DOM and is not rerun for the new elements.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The <strong>initNewContent</strong> mechanism solves this problem by providing a standardized hook
|
||||
to re-initialize all interactive components on newly added DOM elements.
|
||||
Developers can listen to <code>initNewContent</code> and re-run tooltip initialization
|
||||
(or any other dynamic behavior) only on the new elements or their children, ensuring consistency and avoiding duplication.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
This approach guarantees that tooltips, dialogs, and other interactive components
|
||||
remain functional even when content is injected or updated asynchronously.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
In addition to <code>document.ready</code> or <code>$(document).ready()</code>,
|
||||
listen to <strong>initNewContent</strong> to ensure that tooltips, dialogs, or other interactive components
|
||||
are properly initialized on any new DOM fragment added dynamically.
|
||||
</p>
|
||||
|
||||
|
||||
<div class="documentation-example">
|
||||
<p>
|
||||
<button class="button" id="try-no-initNewContent">Test without event</button>
|
||||
<button class="button" id="try-initNewContent">Test with initNewContent event</button>
|
||||
</p>
|
||||
<div id="initNewContent-test-container"></div>
|
||||
|
||||
<style>
|
||||
/* Animation for highlighting new content */
|
||||
@keyframes highlightfortest {
|
||||
from { background-color: #fffa8d; }
|
||||
to { background-color: transparent; }
|
||||
}
|
||||
.highlightfortest {
|
||||
padding: 10px;
|
||||
animation: highlightfortest 1s ease-out;
|
||||
}
|
||||
</style>
|
||||
<div id="idfortooltiponclick_doc-event-dialog-test" class="classfortooltiponclicktext" title="The title" style="display: none" >Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce nec elit venenatis, bibendum dui in, tristique dolor. In hac habitasse platea dictumst. Vestibulum consectetur quam non felis fringilla mollis pretium vel nibh. Pellentesque congue risus et laoreet blandit. Aliquam orci ipsum, gravida id leo eget, molestie pulvinar sem. Nulla sed felis et lacus tristique finibus. Cras ornare tincidunt. Aenean hendrerit volutpat efficitur. Integer vestibulum dui eget lectus pulvinar, vel mattis odio facilisis. Etiam convallis scelerisque lobortis. Mauris tristique, quam dignissim sollicitudin sodales, elit ligula venenatis neque, sit amet interdum lacus tellus id tellus. Mauris eu pretium turpis. Proin porta sem eget nisl vulputate vehicula.</div>
|
||||
<script nonce="<?php print getNonce() ?>">
|
||||
document.addEventListener('Dolibarr:Ready', function(e) {
|
||||
const container = document.getElementById('initNewContent-test-container');
|
||||
|
||||
document.getElementById('try-no-initNewContent').addEventListener('click', function() {
|
||||
container.innerHTML = `<span class="classfortooltip highlightfortest" title="this is the title">A text with a tooltip but the tooltip isn't load</span>
|
||||
and <span class="classfortooltiponclick highlightfortest" dolid="doc-event-dialog-test" style="cursor: pointer;" >A text with a tooltip to click but the tooltip isn't load</span>`;
|
||||
});
|
||||
|
||||
document.getElementById('try-initNewContent').addEventListener('click', function() {
|
||||
container.innerHTML = `<span class="classfortooltip highlightfortest" title="this is the title">A text with a tooltip and the tooltip is loaded</span>
|
||||
And <span class="classfortooltiponclick highlightfortest" dolid="doc-event-dialog-test" style="cursor: pointer;" >A text with a tooltip to click and the tooltip is loaded</span>`;
|
||||
Dolibarr.initNewContent(container);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
</div>
|
||||
<?php
|
||||
$lines = array(
|
||||
'<div id="idfortooltiponclick_doc-event-dialog-test" class="classfortooltiponclicktext" title="The title" >Lorem ipsum .....</div>',
|
||||
'<script nonce="<?php print getNonce() ?>">',
|
||||
'document.addEventListener("Dolibarr:Ready", function(e) {',
|
||||
' const container = document.getElementById("initNewContent-test-container");',
|
||||
'',
|
||||
' // Insert content without calling initNewContent',
|
||||
' document.getElementById("try-no-initNewContent").addEventListener("click", function() {',
|
||||
' container.innerHTML = `<span class="classfortooltip highlightfortest" title="this is the title">A text with a tooltip but the tooltip isn\'t load</span>',
|
||||
' and <span class="classfortooltiponclick highlightfortest" dolid="doc-event-dialog-test" style="cursor: pointer;">A text with a tooltip to click but the tooltip isn\'t load</span>`;',
|
||||
' });',
|
||||
'',
|
||||
' // Insert content and trigger initNewContent',
|
||||
' document.getElementById("try-initNewContent").addEventListener("click", function() {',
|
||||
' container.innerHTML = `<span class="classfortooltip highlightfortest" title="this is the title">A text with a tooltip and the tooltip is loaded</span>',
|
||||
' and <span class="classfortooltiponclick highlightfortest" dolid="doc-event-dialog-test" style="cursor: pointer;">A text with a tooltip to click and the tooltip is loaded</span>`;',
|
||||
' Dolibarr.initNewContent(container);',
|
||||
' });',
|
||||
'});',
|
||||
'</script>',
|
||||
);
|
||||
$documentation->showCode($lines, 'php'); ?>
|
||||
|
||||
<h3 id="titlesection-usecase-tooltips" class="documentation-title">
|
||||
How to use it
|
||||
</h3>
|
||||
|
||||
<p>
|
||||
The event handler receives an object with the property <code>targets</code>,
|
||||
which is an array of DOM elements or jQuery collections to initialize. Each element can be:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>a container with child elements to initialize</li>
|
||||
<li>or a direct element that needs initialization</li>
|
||||
</ul>
|
||||
|
||||
<h4>Example: Trigger initNewContent manually on a container</h4>
|
||||
|
||||
<?php
|
||||
$lines = array(
|
||||
'<script nonce="<?php print getNonce() ?>">',
|
||||
' document.addEventListener(\'Dolibarr:Ready\', function(e) {',
|
||||
' /* [... code that dynamically reloads part of the DOM ...] */',
|
||||
'',
|
||||
' /**',
|
||||
' * true: only include the children of each target element',
|
||||
' * false: include the target elements themselves',
|
||||
' */',
|
||||
' const applyToChildrenOnly = true;',
|
||||
'',
|
||||
' // Trigger initNewContent manually on a jQuery container',
|
||||
' Dolibarr.initNewContent($("#myContainer"), applyToChildrenOnly);',
|
||||
'',
|
||||
' // Trigger initNewContent manually on a vanilla JS element',
|
||||
' const element = document.getElementById("myContainer");',
|
||||
' Dolibarr.initNewContent(element, applyToChildrenOnly);',
|
||||
' });',
|
||||
'</script>',
|
||||
);
|
||||
$documentation->showCode($lines, 'php'); ?>
|
||||
|
||||
<p>
|
||||
Dolibarr provides a custom event system to properly initialize dynamic content.
|
||||
Always use <strong>initNewContent</strong> when working with AJAX-injected fragments or
|
||||
dynamically created elements instead of relying solely on jQuery document ready.
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
<h4>Example in pure JS style: listening to initNewContent</h4>
|
||||
<?php
|
||||
$lines = array(
|
||||
'<script nonce="<?php print getNonce() ?>">',
|
||||
' Dolibarr.on("initNewContent", ({ targets }) => {',
|
||||
' targets.forEach(root => {',
|
||||
'',
|
||||
' // Array to store all matching dialog elements',
|
||||
' const dialogs = [];',
|
||||
'',
|
||||
' // Include the root element if it matches the selector',
|
||||
' if (root.matches(".classfortooltiponclicktext")) {',
|
||||
' dialogs.push(root);',
|
||||
' }',
|
||||
'',
|
||||
' // Add all descendants matching the selector',
|
||||
' dialogs.push(...root.querySelectorAll(".classfortooltiponclicktext"));',
|
||||
'',
|
||||
' // Initialize each dialog element',
|
||||
' dialogs.forEach(el => {',
|
||||
' // Your code to initialize the tooltip/dialog or other stuff',
|
||||
' });',
|
||||
' });',
|
||||
' });',
|
||||
'</script>',
|
||||
);
|
||||
$documentation->showCode($lines, 'php');
|
||||
?>
|
||||
|
||||
<h4>Compact version in pure JS</h4>
|
||||
<?php
|
||||
$lines = array(
|
||||
'<script nonce="<?php print getNonce() ?>">',
|
||||
' Dolibarr.on("initNewContent", ({ targets }) => {',
|
||||
' targets.forEach(root => {',
|
||||
' const dialogs = [',
|
||||
' ...(root.matches(".classfortooltiponclicktext") ? [root] : []),',
|
||||
' ...root.querySelectorAll(".classfortooltiponclicktext")',
|
||||
' ];',
|
||||
' dialogs.forEach(el => {',
|
||||
' // Initialize tooltip/dialog or other stuff here',
|
||||
' });',
|
||||
' });',
|
||||
' });',
|
||||
'</script>',
|
||||
);
|
||||
$documentation->showCode($lines, 'php');
|
||||
?>
|
||||
|
||||
<h4>Example in jQuery style</h4>
|
||||
<?php
|
||||
$lines = array(
|
||||
'<script nonce="<?php print getNonce() ?>">',
|
||||
' Dolibarr.on("initNewContent", ({ targets }) => {',
|
||||
' targets.forEach($root => {',
|
||||
' const $dialogs = $root',
|
||||
' .filter(".classfortooltiponclicktext")',
|
||||
' .add($root.find(".classfortooltiponclicktext"));',
|
||||
' $dialogs.each(function () {',
|
||||
' const $el = $(this);',
|
||||
' // Initialize tooltip/dialog behavior or other stuff here',
|
||||
' });',
|
||||
' });',
|
||||
' });',
|
||||
'</script>',
|
||||
);
|
||||
$documentation->showCode($lines, 'php');
|
||||
?>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="documentation-section">
|
||||
<h2 id="titlesection-create-tool-example" class="documentation-title">Example of creating a new context tool</h2>
|
||||
@@ -450,7 +672,6 @@ $documentation->showSidebar(); ?>
|
||||
<p>See also <code>dolibarr-context.mock.js</code> for defining all standard Dolibarr tools and creating mock implementations to improve code completion and editor support.</p>
|
||||
<p><b>Note :</b> a tool can be a class not only a function</p>
|
||||
|
||||
<div class="documentation-example">
|
||||
<?php
|
||||
$lines = array(
|
||||
'<script>',
|
||||
@@ -467,14 +688,12 @@ $documentation->showSidebar(); ?>
|
||||
'</script>',
|
||||
);
|
||||
$documentation->showCode($lines, 'php'); ?>
|
||||
</div>
|
||||
|
||||
<h3>Protected Tools</h3>
|
||||
<p>
|
||||
Once a tool is defined on overwrite false, it cannot be replaced. Attempting to redefine it without overwrite will throw an error:
|
||||
</p>
|
||||
|
||||
<div class="documentation-example">
|
||||
<?php
|
||||
$lines = array(
|
||||
'<script>',
|
||||
@@ -486,14 +705,12 @@ $documentation->showSidebar(); ?>
|
||||
'</script>',
|
||||
);
|
||||
$documentation->showCode($lines, 'php'); ?>
|
||||
</div>
|
||||
|
||||
<h3>Reading Tools</h3>
|
||||
<p>
|
||||
You can read the list of available tools using <code>Dolibarr.tools</code>. It returns a frozen copy:
|
||||
</p>
|
||||
|
||||
<div class="documentation-example">
|
||||
<?php
|
||||
$lines = array(
|
||||
'<script>',
|
||||
@@ -502,82 +719,10 @@ $documentation->showSidebar(); ?>
|
||||
'</script>',
|
||||
);
|
||||
$documentation->showCode($lines, 'php'); ?>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="documentation-section">
|
||||
<h2 id="titlesection-tool-seteventmessage" class="documentation-title">Set event message tool</h2>
|
||||
|
||||
<p>
|
||||
Instead of calling JNotify directly in your code, use Dolibarr’s setEventMessage tool.
|
||||
Dolibarr provides the configuration option DISABLE_JQUERY_JNOTIFY, which disables the jQuery JNotify system, usually because another notification library will be used instead.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you rely on Dolibarr.tools.setEventMessage(), your code remains compatible even if the underlying notification system changes.
|
||||
The setEventMessage tool can be replaced internally without requiring any changes in your modules or custom scripts.
|
||||
</p>
|
||||
<p>
|
||||
This means all developers can write features without worrying about frontend compatibility or future library replacements. Enjoy!
|
||||
|
||||
</p>
|
||||
|
||||
<div class="documentation-example">
|
||||
<?php
|
||||
$lines = array(
|
||||
'<script nonce="<?php print getNonce() ?>" >',
|
||||
' document.addEventListener(\'Dolibarr:Ready\', function(e) {',
|
||||
'',
|
||||
' document.getElementById(\'setEventMessage-success\').addEventListener(\'click\', function(e) {',
|
||||
' Dolibarr.tools.setEventMessage(\'Success Test\');',
|
||||
' });',
|
||||
'',
|
||||
' document.getElementById(\'setEventMessage-error\').addEventListener(\'click\', function(e) {',
|
||||
' Dolibarr.tools.setEventMessage(\'Error Test\', \'errors\');',
|
||||
' });',
|
||||
'',
|
||||
' document.getElementById(\'setEventMessage-error-sticky\').addEventListener(\'click\', function(e) {',
|
||||
' Dolibarr.tools.setEventMessage(\'Error Test\', \'errors\', true);',
|
||||
' });',
|
||||
'',
|
||||
' document.getElementById(\'setEventMessage-warning\').addEventListener(\'click\', function(e) {',
|
||||
' Dolibarr.tools.setEventMessage(\'Warning Test\', \'warnings\');',
|
||||
' });',
|
||||
'',
|
||||
' });',
|
||||
'</script>',
|
||||
);
|
||||
$documentation->showCode($lines, 'php'); ?>
|
||||
<script nonce="<?php print getNonce() ?>" >
|
||||
document.addEventListener('Dolibarr:Ready', function(e) {
|
||||
|
||||
document.getElementById('setEventMessage-success').addEventListener('click', function(e) {
|
||||
Dolibarr.tools.setEventMessage('Success Test')
|
||||
});
|
||||
|
||||
document.getElementById('setEventMessage-error').addEventListener('click', function(e) {
|
||||
Dolibarr.tools.setEventMessage('Error Test', 'errors');
|
||||
});
|
||||
|
||||
document.getElementById('setEventMessage-error-sticky').addEventListener('click', function(e) {
|
||||
Dolibarr.tools.setEventMessage('Error Test', 'errors', true);
|
||||
});
|
||||
|
||||
document.getElementById('setEventMessage-warning').addEventListener('click', function(e) {
|
||||
Dolibarr.tools.setEventMessage('Warning Test', 'warnings');
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
<button id="setEventMessage-success" class="button">Alert success</button>
|
||||
<button id="setEventMessage-error" class="button">Alert error</button>
|
||||
<button id="setEventMessage-error-sticky" class="button">Alert error sticky</button>
|
||||
<button id="setEventMessage-warning" class="button">Alert warning</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<?php include __DIR__ . '/inc_seteventmessage.php'; ?>
|
||||
|
||||
|
||||
<div class="documentation-section">
|
||||
@@ -598,7 +743,6 @@ $documentation->showSidebar(); ?>
|
||||
</p>
|
||||
|
||||
<h3>Add context var (overridable or not)</h3>
|
||||
<div class="documentation-example">
|
||||
<?php
|
||||
$lines = array(
|
||||
'<script nonce="<?php print getNonce() ?>" >',
|
||||
@@ -613,11 +757,9 @@ $documentation->showSidebar(); ?>
|
||||
);
|
||||
$documentation->showCode($lines, 'php');
|
||||
?>
|
||||
</div>
|
||||
|
||||
|
||||
<h3>Add multiple context vars (overridable or not)</h3>
|
||||
<div class="documentation-example">
|
||||
<?php
|
||||
$lines = array(
|
||||
'<?php',
|
||||
@@ -644,10 +786,8 @@ $documentation->showSidebar(); ?>
|
||||
);
|
||||
$documentation->showCode($lines, 'php');
|
||||
?>
|
||||
</div>
|
||||
|
||||
<h3>Get context var</h3>
|
||||
<div class="documentation-example">
|
||||
<?php
|
||||
$lines = array(
|
||||
'<script nonce="<?php print getNonce() ?>" >',
|
||||
@@ -659,7 +799,6 @@ $documentation->showSidebar(); ?>
|
||||
);
|
||||
$documentation->showCode($lines, 'php');
|
||||
?>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -191,7 +191,7 @@ $documentation->showSidebar(); ?>
|
||||
'$documentation->docFooter();',
|
||||
'?>',
|
||||
);
|
||||
echo $documentation->showCode($lines, 'php'); ?>
|
||||
$documentation->showCode($lines, 'php'); ?>
|
||||
</div>
|
||||
|
||||
<!-- Second Step -->
|
||||
@@ -231,7 +231,7 @@ $documentation->showSidebar(); ?>
|
||||
' )',
|
||||
');',
|
||||
);
|
||||
echo $documentation->showCode($lines, 'php'); ?>
|
||||
$documentation->showCode($lines, 'php'); ?>
|
||||
</div>
|
||||
|
||||
<!-- Third Step -->
|
||||
@@ -247,7 +247,7 @@ $documentation->showSidebar(); ?>
|
||||
'// Set view for menu and breadcrumb',
|
||||
'$documentation->view = array(\'Components\', \'MyComponent\');',
|
||||
);
|
||||
echo $documentation->showCode($lines, 'php'); ?>
|
||||
$documentation->showCode($lines, 'php'); ?>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -25,10 +25,6 @@
|
||||
|
||||
// Load Dolibarr environment
|
||||
require '../main.inc.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formadmin.class.php';
|
||||
|
||||
/**
|
||||
* @var Conf $conf
|
||||
* @var DoliDB $db
|
||||
@@ -36,6 +32,9 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/html.formadmin.class.php';
|
||||
* @var Translate $langs
|
||||
* @var User $user
|
||||
*/
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formadmin.class.php';
|
||||
|
||||
// Load translation files required by the page
|
||||
$langs->loadLangs(array("companies", "products", "admin", "sms", "other", "errors"));
|
||||
@@ -231,19 +230,23 @@ llxHeader('', $title, $wikihelp, '', 0, 0, '', '', '', 'mod-admin page-translati
|
||||
|
||||
$param = '&mode='.urlencode($mode);
|
||||
|
||||
$enabledisablehtml = '';
|
||||
$enabledisablehtml .= $langs->trans("EnableOverwriteTranslation").' ';
|
||||
$enabledisablehtml = '<span class="divfilteralone">';
|
||||
if (!getDolGlobalString('MAIN_ENABLE_OVERWRITE_TRANSLATION')) {
|
||||
// Button off, click to enable
|
||||
$enabledisablehtml .= '<a class="reposition valignmiddle" href="'.$_SERVER["PHP_SELF"].'?action=setMAIN_ENABLE_OVERWRITE_TRANSLATION&token='.newToken().'&value=1'.$param.'">';
|
||||
$enabledisablehtml .= img_picto($langs->trans("Disabled"), 'switch_off');
|
||||
$enabledisablehtml .= '</a>';
|
||||
$enabledisablehtml .= '<a class="reposition valignmiddle nounderlineimp" href="'.$_SERVER["PHP_SELF"].'?action=setMAIN_ENABLE_OVERWRITE_TRANSLATION&token='.newToken().'&value=1'.$param.'">';
|
||||
} else {
|
||||
$enabledisablehtml .= '<a class="reposition valignmiddle nounderlineimp" href="'.$_SERVER["PHP_SELF"].'?action=setMAIN_ENABLE_OVERWRITE_TRANSLATION&token='.newToken().'&value=0'.$param.'">';
|
||||
}
|
||||
$enabledisablehtml .= $langs->trans("EnableOverwriteTranslation");
|
||||
if (!getDolGlobalString('MAIN_ENABLE_OVERWRITE_TRANSLATION')) {
|
||||
// Button off, click to enable
|
||||
$enabledisablehtml .= img_picto($langs->trans("Disabled"), 'switch_off', 'class="paddingleft valignmiddle"');
|
||||
} else {
|
||||
// Button on, click to disable
|
||||
$enabledisablehtml .= '<a class="reposition valignmiddle" href="'.$_SERVER["PHP_SELF"].'?action=setMAIN_ENABLE_OVERWRITE_TRANSLATION&token='.newToken().'&value=0'.$param.'">';
|
||||
$enabledisablehtml .= img_picto($langs->trans("Activated"), 'switch_on');
|
||||
$enabledisablehtml .= '</a>';
|
||||
$enabledisablehtml .= img_picto($langs->trans("Activated"), 'switch_on', 'class="paddingleft valignmiddle"');
|
||||
}
|
||||
$enabledisablehtml .= '</a>';
|
||||
$enabledisablehtml .= '</span>';
|
||||
|
||||
$current_language_code = $langs->defaultlang;
|
||||
$s = picto_from_langcode($current_language_code);
|
||||
@@ -580,37 +583,46 @@ if ($mode == 'searchkey') {
|
||||
print '<div class="div-table-responsive-no-min">';
|
||||
print '<table class="noborder centpercent">';
|
||||
|
||||
print '<tr class="liste_titre liste_titre_filter"><td>';
|
||||
print '<tr class="liste_titre liste_titre_filter">';
|
||||
// Action column
|
||||
if ($conf->main_checkbox_left_column) {
|
||||
print '<td class="center nowraponall">';
|
||||
$searchpicto = $form->showFilterAndCheckAddButtons(!empty($massactionbutton) ? 1 : 0, 'checkforselect', 1);
|
||||
print $searchpicto;
|
||||
print '</td>';
|
||||
}
|
||||
print '<td>';
|
||||
print $formadmin->select_language($langcode, 'langcode', 0, array(), 0, 0, 0, 'minwidth100 maxwidth250', 1);
|
||||
print '</td>'."\n";
|
||||
print '<td>';
|
||||
print '<input type="text" class="flat maxwidthonsmartphone" name="transkey" value="'.dol_escape_htmltag($transkey).'">';
|
||||
print '</td><td>';
|
||||
print '<input type="text" class="quatrevingtpercent" name="transvalue" value="'.dol_escape_htmltag($transvalue).'">';
|
||||
// Limit to superadmin
|
||||
/*if (isModEnabled('multicompany') && !$user->entity)
|
||||
{
|
||||
print '</td><td>';
|
||||
print '<input type="text" class="flat" size="1" name="entitysearch" value="'.$conf->entity.'">';
|
||||
}
|
||||
else
|
||||
{*/
|
||||
print '<input type="hidden" name="entitysearch" value="'.$conf->entity.'">';
|
||||
//}
|
||||
print '</td>';
|
||||
print '<td></td>';
|
||||
// Action column
|
||||
print '<td class="right nowraponall">';
|
||||
$searchpicto = $form->showFilterAndCheckAddButtons(!empty($massactionbutton) ? 1 : 0, 'checkforselect', 1);
|
||||
print $searchpicto;
|
||||
print '</td>';
|
||||
if (!$conf->main_checkbox_left_column) {
|
||||
print '<td class="right nowraponall">';
|
||||
$searchpicto = $form->showFilterAndCheckAddButtons(!empty($massactionbutton) ? 1 : 0, 'checkforselect', 1);
|
||||
print $searchpicto;
|
||||
print '</td>';
|
||||
}
|
||||
print '</tr>';
|
||||
|
||||
print '<tr class="liste_titre">';
|
||||
// Action column
|
||||
if ($conf->main_checkbox_left_column) {
|
||||
print_liste_field_titre("");
|
||||
}
|
||||
print_liste_field_titre("Language_en_US_es_MX_etc", $_SERVER["PHP_SELF"], 'lang,transkey', '', $param, '', $sortfield, $sortorder);
|
||||
print_liste_field_titre("TranslationKey", $_SERVER["PHP_SELF"], 'transkey', '', $param, '', $sortfield, $sortorder);
|
||||
print_liste_field_titre("CurrentTranslationString", $_SERVER["PHP_SELF"], 'transvalue', '', $param, '', $sortfield, $sortorder);
|
||||
//if (isModEnabled('multicompany') && !$user->entity) print_liste_field_titre("Entity", $_SERVER["PHP_SELF"], 'entity,transkey', '', $param, '', $sortfield, $sortorder);
|
||||
print '<td align="center"></td>';
|
||||
print_liste_field_titre("");
|
||||
// Action column
|
||||
if (!$conf->main_checkbox_left_column) {
|
||||
print_liste_field_titre("");
|
||||
}
|
||||
print "</tr>\n";
|
||||
|
||||
|
||||
@@ -637,7 +649,15 @@ if ($mode == 'searchkey') {
|
||||
if ($limit && $i > ($offset + $limit)) {
|
||||
break;
|
||||
}
|
||||
print '<tr class="oddeven"><td>'.dolPrintHTML($langcode).'</td>';
|
||||
|
||||
print '<tr class="oddeven">';
|
||||
// Action column
|
||||
if ($conf->main_checkbox_left_column) {
|
||||
print '<td class="center nowraponall">';
|
||||
print '</td>';
|
||||
}
|
||||
// Code lang
|
||||
print '<td>'.dolPrintHTML($langcode).'</td>';
|
||||
// Key
|
||||
print '<td class="" title="'.dolPrintHTMLForAttribute($key).'">'.dolPrintHTML($key).'</td>';
|
||||
print '<td class="tdoverflowmax300 small">';
|
||||
@@ -656,6 +676,7 @@ if ($mode == 'searchkey') {
|
||||
}
|
||||
print '</span>';
|
||||
print '</td>';
|
||||
|
||||
print '<td class="right nowraponall">';
|
||||
if (!empty($newlangfileonly->tab_translate[$key])) {
|
||||
if ($val != $newlangfileonly->tab_translate[$key]) {
|
||||
@@ -713,11 +734,13 @@ if ($mode == 'searchkey') {
|
||||
print $form->textwithpicto('', $htmltext, 1, 'warning');
|
||||
}
|
||||
}
|
||||
/*if (isModEnabled('multicompany') && !$user->entity)
|
||||
{
|
||||
print '<td>'.$val.'</td>';
|
||||
}*/
|
||||
print '</td></tr>'."\n";
|
||||
print '</td>';
|
||||
|
||||
// Action column
|
||||
if (!$conf->main_checkbox_left_column) {
|
||||
print'<td></td>';
|
||||
}
|
||||
print '</tr>'."\n";
|
||||
}
|
||||
|
||||
if (empty($recordtoshow)) {
|
||||
|
||||
@@ -57,14 +57,17 @@ $modulepart = GETPOST('modulepart', 'aZ09'); // Used by actions_setmoduleoptions
|
||||
|
||||
$type = 'group';
|
||||
|
||||
|
||||
/*
|
||||
* Action
|
||||
*/
|
||||
|
||||
include DOL_DOCUMENT_ROOT.'/core/actions_setmoduleoptions.inc.php';
|
||||
|
||||
$reg = array();
|
||||
|
||||
if ($action == 'set_default') {
|
||||
$ret = addDocumentModel($value, $type, $label, $scandir);
|
||||
$ret = addDocumentModel($value, $type, '', '');
|
||||
$res = true;
|
||||
} elseif ($action == 'del_default') {
|
||||
$ret = delDocumentModel($value, $type);
|
||||
@@ -85,7 +88,7 @@ if ($action == 'set_default') {
|
||||
// On active le modele
|
||||
$ret = delDocumentModel($value, $type);
|
||||
if ($ret > 0) {
|
||||
$ret = addDocumentModel($value, $type, $label, $scandir);
|
||||
$ret = addDocumentModel($value, $type, '', '');
|
||||
}
|
||||
$res = true;
|
||||
} elseif (preg_match('/set_([a-z0-9_\-]+)/i', $action, $reg)) {
|
||||
|
||||
@@ -75,19 +75,24 @@ if (!class_exists('FormSetup')) {
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formsetup.class.php';
|
||||
}
|
||||
|
||||
$form = new Form($db);
|
||||
$formSetup = new FormSetup($db);
|
||||
$aiservice = getDolGlobalString('AI_API_SERVICE', 'chatgpt');
|
||||
|
||||
// Setup conf for AI model
|
||||
$formSetup->formHiddenInputs['action'] = "updatefeaturemodel";
|
||||
foreach ($arrayofaifeatures as $featurekey => $feature) {
|
||||
$newkey = $featurekey;
|
||||
$newfeaturekey = $featurekey;
|
||||
if (preg_match('/^text/', $featurekey)) {
|
||||
$newkey = 'textgeneration';
|
||||
$newfeaturekey = 'textgeneration';
|
||||
}
|
||||
$item = $formSetup->newItem('AI_API_'.strtoupper($aiservice).'_MODEL_'.$feature["function"]); // Name of constant must end with _KEY so it is encrypted when saved into database.
|
||||
if ($arrayofai[$aiservice][$newkey] != 'na') {
|
||||
$item->nameText = $langs->trans("AI_API_MODEL_".$feature["function"]).' <span class="opacitymedium">('.$langs->trans("Default").' = '.$arrayofai[$aiservice][$newkey].')</span>';
|
||||
if (!empty($arrayofai[$aiservice][$newfeaturekey]['default']) && $arrayofai[$aiservice][$newfeaturekey]['default'] != 'na') {
|
||||
$item->nameText = '<span class="valignmiddle">'.$langs->trans("AI_API_MODEL_".$feature["function"]).' </span><span class="opacitymedium valignmiddle">('.$langs->trans("Default").' = '.$arrayofai[$aiservice][$newfeaturekey]['default'].')</span>';
|
||||
if (!empty($arrayofai[$aiservice][$newfeaturekey]['examples'])) {
|
||||
$htmltooltip = $langs->trans("Example").': '.$arrayofai[$aiservice][$newfeaturekey]['examples'];
|
||||
$item->nameText .= $form->textwithpicto('', $htmltooltip);
|
||||
}
|
||||
} else {
|
||||
$item->nameText = $langs->trans("AI_API_MODEL_".$feature["function"]).' <span class="opacitymedium">('.$langs->trans("None").')</span>';
|
||||
}
|
||||
@@ -205,7 +210,6 @@ if ($action == 'confirm_deleteproperty' && GETPOST('confirm') == 'yes') {
|
||||
* View
|
||||
*/
|
||||
|
||||
$form = new Form($db);
|
||||
$formai = new FormAI($db);
|
||||
|
||||
$help_url = '';
|
||||
@@ -245,10 +249,10 @@ if ($action == 'deleteproperty') {
|
||||
print $formconfirm;
|
||||
}
|
||||
|
||||
print '<br>';
|
||||
|
||||
if ($action == 'create') {
|
||||
$out = '<div class="addcustomprompt">';
|
||||
$out = '<br>';
|
||||
|
||||
$out .= '<div class="addcustomprompt">';
|
||||
|
||||
$out .= '<form action="'.$_SERVER["PHP_SELF"].'" method="POST">';
|
||||
$out .= '<input type="hidden" name="token" value="'.newToken().'">';
|
||||
@@ -335,17 +339,23 @@ if ($action == 'create') {
|
||||
$out .= $form->buttonsSaveCancel("Add", "");
|
||||
$out .= '</form>';
|
||||
|
||||
$out .= '<br><br><br>';
|
||||
$out .= '</div>';
|
||||
|
||||
print $out;
|
||||
}
|
||||
|
||||
|
||||
if ($action == 'edit' || $action == 'create' || $action == 'deleteproperty') {
|
||||
$out = '';
|
||||
|
||||
if (!empty($currentConfigurations)) {
|
||||
if (empty($currentConfigurations)) {
|
||||
print '<span class="opacitymedium">'.$langs->trans("None").'</span>';
|
||||
print '<br>';
|
||||
print '<br>';
|
||||
print '<br>';
|
||||
} else {
|
||||
print '<br>';
|
||||
print '<br>';
|
||||
print '<br>';
|
||||
foreach ($currentConfigurations as $confkey => $config) {
|
||||
if (!empty($confkey) && !preg_match('/^[a-z]+$/i', $confkey)) { // Ignore empty saved setup
|
||||
continue;
|
||||
|
||||
529
htdocs/ai/admin/log_viewer.php
Normal file
529
htdocs/ai/admin/log_viewer.php
Normal file
@@ -0,0 +1,529 @@
|
||||
<?php
|
||||
/* Copyright (C) 2004-2017 Laurent Destailleur <eldy@users.sourceforge.net>
|
||||
* Copyright (C) 2026 Nick Fragoulis
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file htdocs/ai/admin/log_viewer.php
|
||||
* \ingroup ai
|
||||
* \brief AI Request Log Viewer with Payload Inspection
|
||||
*/
|
||||
|
||||
/**
|
||||
* @var Conf $conf
|
||||
* @var DoliDB $db
|
||||
* @var HookManager $hookmanager
|
||||
* @var Translate $langs
|
||||
* @var User $user
|
||||
* @var Form $form
|
||||
*/
|
||||
|
||||
require '../../main.inc.php';
|
||||
require_once DOL_DOCUMENT_ROOT . '/core/lib/admin.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT . '/core/class/html.form.class.php';
|
||||
|
||||
|
||||
// Access Control
|
||||
if (!$user->admin) {
|
||||
accessforbidden();
|
||||
}
|
||||
|
||||
// Load translations
|
||||
$langs->loadLangs(array("admin", "other"));
|
||||
|
||||
// Parameters
|
||||
$action = GETPOST('action', 'aZ09');
|
||||
$massaction = GETPOST('massaction', 'alpha');
|
||||
$confirm = GETPOST('confirm', 'alpha');
|
||||
$toselect = GETPOST('toselect', 'array');
|
||||
$contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : 'ailoglist';
|
||||
$optioncss = GETPOST('optioncss', 'alpha');
|
||||
$mode = GETPOST('mode', 'alpha');
|
||||
|
||||
// Search parameters for all columns
|
||||
$search_date_start = dol_mktime(0, 0, 0, GETPOSTINT('search_date_startmonth'), GETPOSTINT('search_date_startday'), GETPOSTINT('search_date_startyear'));
|
||||
$search_date_end = dol_mktime(23, 59, 59, GETPOSTINT('search_date_endmonth'), GETPOSTINT('search_date_endday'), GETPOSTINT('search_date_endyear'));
|
||||
$search_user = GETPOST('search_user', 'alpha');
|
||||
$search_query = GETPOST('search_query', 'alpha');
|
||||
$search_tool = GETPOST('search_tool', 'alpha');
|
||||
$search_provider = GETPOST('search_provider', 'alpha');
|
||||
$search_time_min = GETPOST('search_time_min', 'alpha');
|
||||
$search_time_max = GETPOST('search_time_max', 'alpha');
|
||||
$search_status = GETPOST('search_status', 'alpha');
|
||||
|
||||
// Pagination parameters
|
||||
$limit = GETPOST('limit', 'int') ? GETPOST('limit', 'int') : $conf->liste_limit;
|
||||
$sortfield = GETPOST('sortfield', 'alpha');
|
||||
$sortorder = GETPOST('sortorder', 'alpha');
|
||||
$page = GETPOSTINT("page");
|
||||
if (empty($page) || $page == -1) {
|
||||
$page = 0;
|
||||
}
|
||||
$offset = $limit * $page;
|
||||
if (!$sortfield) $sortfield = "l.date_request";
|
||||
if (!$sortorder) $sortorder = "DESC";
|
||||
|
||||
// Initialize array of search criteria
|
||||
$search_array = array(
|
||||
'search_date_start' => $search_date_start,
|
||||
'search_date_end' => $search_date_end,
|
||||
'search_user' => $search_user,
|
||||
'search_query' => $search_query,
|
||||
'search_tool' => $search_tool,
|
||||
'search_provider' => $search_provider,
|
||||
'search_time_min' => $search_time_min,
|
||||
'search_time_max' => $search_time_max,
|
||||
'search_status' => $search_status
|
||||
);
|
||||
|
||||
/*
|
||||
* Actions
|
||||
*/
|
||||
|
||||
$error = '';
|
||||
if ($action == 'purge' && $confirm == 'yes') {
|
||||
$db->begin();
|
||||
|
||||
$sql = "DELETE FROM " . MAIN_DB_PREFIX . "ai_request_log";
|
||||
$sql .= " WHERE entity IN (" . getEntity('airequestlog') . ")";
|
||||
|
||||
$resql = $db->query($sql);
|
||||
|
||||
if ($resql) {
|
||||
$nbDeleted = $db->affected_rows($resql);
|
||||
$db->commit();
|
||||
setEventMessages($langs->trans("LogsCleared") . " (" . $nbDeleted . ")", null, 'mesgs');
|
||||
} else {
|
||||
$db->rollback();
|
||||
setEventMessages($db->lasterror(), null, 'errors');
|
||||
}
|
||||
|
||||
header('Location: ' . $_SERVER["PHP_SELF"]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Purge selection
|
||||
if ($massaction == 'purge' && !empty($toselect) && is_array($toselect)) {
|
||||
$db->begin();
|
||||
|
||||
foreach ($toselect as $id) {
|
||||
$sql = "DELETE FROM " . MAIN_DB_PREFIX . "ai_request_log";
|
||||
$sql .= " WHERE rowid = " . ((int) $id);
|
||||
$sql .= " AND entity IN (" . getEntity('airequestlog') . ")";
|
||||
|
||||
$resql = $db->query($sql);
|
||||
if (!$resql) {
|
||||
$error++;
|
||||
$db->rollback();
|
||||
setEventMessages($db->lasterror(), null, 'errors');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$error) {
|
||||
$db->commit();
|
||||
setEventMessages($langs->trans("SelectedLogsDeleted"), null, 'mesgs');
|
||||
} else {
|
||||
$db->rollback();
|
||||
}
|
||||
|
||||
$action = 'list';
|
||||
$massaction = '';
|
||||
}
|
||||
|
||||
// Clear filter action
|
||||
if (GETPOST('button_removefilter', 'alpha') || GETPOST('button_removefilter_x', 'alpha')) {
|
||||
$search_date_start = '';
|
||||
$search_date_end = '';
|
||||
$search_user = '';
|
||||
$search_query = '';
|
||||
$search_tool = '';
|
||||
$search_provider = '';
|
||||
$search_time_min = '';
|
||||
$search_time_max = '';
|
||||
$search_status = '';
|
||||
// Reset page
|
||||
$page = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* View
|
||||
*/
|
||||
|
||||
// Initialize array of search criteria for the view
|
||||
$param = '';
|
||||
if ($contextpage != $_SERVER["PHP_SELF"]) {
|
||||
$param .= '&contextpage='.urlencode($contextpage);
|
||||
}
|
||||
if ($limit > 0 && $limit != $conf->liste_limit) {
|
||||
$param .= '&limit='.urlencode($limit);
|
||||
}
|
||||
foreach ($search_array as $key => $val) {
|
||||
if (!empty($val) || $val === '0') {
|
||||
$param .= '&' . $key . '=' . urlencode($val);
|
||||
}
|
||||
}
|
||||
|
||||
llxHeader('', $langs->trans("AIRequestLogs"), '');
|
||||
|
||||
|
||||
// Build WHERE clause
|
||||
$where = array();
|
||||
|
||||
$where[] = "l.entity IN (" . getEntity('airequestlog') . ")";
|
||||
|
||||
if ($search_date_start) {
|
||||
$where[] = "l.date_request >= '" . $db->escape(date('Y-m-d H:i:s', $search_date_start)) . "'";
|
||||
}
|
||||
if ($search_date_end) {
|
||||
$where[] = "l.date_request <= '" . $db->escape(date('Y-m-d H:i:s', $search_date_end)) . "'";
|
||||
}
|
||||
if ($search_user) {
|
||||
$where[] = "u.login LIKE '%" . $db->escape($search_user) . "%'";
|
||||
}
|
||||
if ($search_query) {
|
||||
$where[] = "l.query_text LIKE '%" . $db->escape($search_query) . "%'";
|
||||
}
|
||||
if ($search_tool) {
|
||||
$where[] = "l.tool_name LIKE '%" . $db->escape($search_tool) . "%'";
|
||||
}
|
||||
if ($search_provider) {
|
||||
$where[] = "l.provider LIKE '%" . $db->escape($search_provider) . "%'";
|
||||
}
|
||||
if ($search_time_min) {
|
||||
$where[] = "l.execution_time >= " . floatval($search_time_min);
|
||||
}
|
||||
if ($search_time_max) {
|
||||
$where[] = "l.execution_time <= " . floatval($search_time_max);
|
||||
}
|
||||
if ($search_status) {
|
||||
$where[] = "l.status = '" . $db->escape($search_status) . "'";
|
||||
}
|
||||
|
||||
$whereSQL = '';
|
||||
if (!empty($where)) {
|
||||
$whereSQL = ' WHERE ' . implode(' AND ', $where);
|
||||
}
|
||||
|
||||
// Get total count for pagination
|
||||
$sqlCount = "SELECT COUNT(*) as total
|
||||
FROM " . MAIN_DB_PREFIX . "ai_request_log as l
|
||||
LEFT JOIN " . MAIN_DB_PREFIX . "user as u ON l.fk_user = u.rowid
|
||||
$whereSQL";
|
||||
$resCount = $db->query($sqlCount);
|
||||
$totalRecords = $resCount ? $db->fetch_object($resCount)->total : 0;
|
||||
|
||||
|
||||
$sql = "SELECT l.*, u.login
|
||||
FROM " . MAIN_DB_PREFIX . "ai_request_log as l
|
||||
LEFT JOIN " . MAIN_DB_PREFIX . "user as u ON l.fk_user = u.rowid
|
||||
$whereSQL
|
||||
ORDER BY $sortfield $sortorder
|
||||
LIMIT " . $offset . ", " . $limit;
|
||||
|
||||
$res = $db->query($sql);
|
||||
$num = $db->num_rows($res);
|
||||
|
||||
// Create object for list
|
||||
$object = new stdClass();
|
||||
$object->total = $totalRecords;
|
||||
|
||||
$title = $langs->trans("AIRequestLogs");
|
||||
print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, '', $num, $totalRecords, 'title_ai', 0, '', '', $limit, 1, 0, 0, '');
|
||||
|
||||
print '<form method="POST" action="' . $_SERVER["PHP_SELF"] . '" name="limitform">';
|
||||
print '<input type="hidden" name="token" value="' . newToken() . '">';
|
||||
print '<input type="hidden" name="action" value="list">';
|
||||
|
||||
// Add all search parameters to preserve them when changing the limit
|
||||
foreach ($search_array as $key => $val) {
|
||||
if (!empty($val) || $val === '0') {
|
||||
print '<input type="hidden" name="' . $key . '" value="' . dol_escape_htmltag($val) . '">';
|
||||
}
|
||||
}
|
||||
|
||||
print '<div class="div-table-responsive-no-min">';
|
||||
print '<table class="noborder" width="100%">';
|
||||
print '<tr>';
|
||||
print '<td class="right">';
|
||||
print $langs->trans("Show") . ': ';
|
||||
print '<input type="hidden" name="contextpage" value="'.$contextpage.'">';
|
||||
print '<input type="hidden" name="sortfield" value="'.$sortfield.'">';
|
||||
print '<input type="hidden" name="sortorder" value="'.$sortorder.'">';
|
||||
print '<input type="hidden" name="page" value="'.$page.'">';
|
||||
|
||||
// Create array of options for limit
|
||||
$arrayoflimit = array(5, 10, 20, 50, 100, 500, 1000);
|
||||
print '<select class="flat" name="limit" onchange="this.form.submit()">';
|
||||
foreach ($arrayoflimit as $val) {
|
||||
print '<option value="'.$val.'"';
|
||||
if ($limit == $val) print ' selected';
|
||||
print '>'.$val.'</option>';
|
||||
}
|
||||
|
||||
print '</select>';
|
||||
print ' ' . $langs->trans("Entries");
|
||||
print '</td>';
|
||||
print '</tr>';
|
||||
print '</table>';
|
||||
print '</div>';
|
||||
print '</form>';
|
||||
|
||||
// Display form for filters
|
||||
print '<form method="POST" action="' . $_SERVER["PHP_SELF"] . '" name="search_form">';
|
||||
print '<input type="hidden" name="token" value="' . newToken() . '">';
|
||||
print '<input type="hidden" name="formfilteraction" id="formfilteraction" value="list">';
|
||||
print '<input type="hidden" name="sortfield" value="' . $sortfield . '">';
|
||||
print '<input type="hidden" name="sortorder" value="' . $sortorder . '">';
|
||||
print '<input type="hidden" name="page" value="' . $page . '">';
|
||||
print '<input type="hidden" name="contextpage" value="'.$contextpage.'">';
|
||||
print '<input type="hidden" name="page_y" value="">';
|
||||
print '<input type="hidden" name="mode" value="'.$mode.'">';
|
||||
|
||||
print '<div class="div-table-responsive">';
|
||||
print '<table class="tagtable liste listwithfilterbefore">'."\n";
|
||||
|
||||
// Fields title
|
||||
print '<tr class="liste_titre">';
|
||||
print_liste_field_titre("Date", $_SERVER["PHP_SELF"], "l.date_request", "", $param, '', $sortfield, $sortorder);
|
||||
print_liste_field_titre("User", $_SERVER["PHP_SELF"], "u.login", "", $param, '', $sortfield, $sortorder);
|
||||
print_liste_field_titre("Query", $_SERVER["PHP_SELF"], "l.query_text", "", $param, '', $sortfield, $sortorder);
|
||||
print_liste_field_titre("MCPTool", $_SERVER["PHP_SELF"], "l.tool_name", "", $param, '', $sortfield, $sortorder);
|
||||
print_liste_field_titre("Provider", $_SERVER["PHP_SELF"], "l.provider", "", $param, '', $sortfield, $sortorder);
|
||||
print_liste_field_titre("Time", $_SERVER["PHP_SELF"], "l.execution_time", "", $param, 'align="center"', $sortfield, $sortorder);
|
||||
print_liste_field_titre("Status", $_SERVER["PHP_SELF"], "l.status", "", $param, 'align="center"', $sortfield, $sortorder);
|
||||
print_liste_field_titre('', $_SERVER["PHP_SELF"], "", "", $param, 'align="center"');
|
||||
print '</tr>';
|
||||
// Search row
|
||||
print '<tr class="liste_titre_filter">';
|
||||
// Date search
|
||||
print '<td class="liste_titre">';
|
||||
print $form->selectDate($search_date_start, 'search_date_start', 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("From"));
|
||||
print ' - ';
|
||||
print $form->selectDate($search_date_end, 'search_date_end', 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("To"));
|
||||
print '</td>';
|
||||
// User search
|
||||
print '<td class="liste_titre"><input type="text" name="search_user" value="' . dol_escape_htmltag($search_user) . '" class="maxwidth100"></td>';
|
||||
// Query search
|
||||
print '<td class="liste_titre"><input type="text" name="search_query" value="' . dol_escape_htmltag($search_query) . '" class="maxwidth150"></td>';
|
||||
// Tool search
|
||||
print '<td class="liste_titre"><input type="text" name="search_tool" value="' . dol_escape_htmltag($search_tool) . '" class="maxwidth100"></td>';
|
||||
// Provider search
|
||||
print '<td class="liste_titre"><input type="text" name="search_provider" value="' . dol_escape_htmltag($search_provider) . '" class="maxwidth100"></td>';
|
||||
// Time search
|
||||
print '<td class="liste_titre center">';
|
||||
print '<input type="text" name="search_time_min" value="' . dol_escape_htmltag($search_time_min) . '" size="3" placeholder="' . dol_escape_htmltag($langs->trans('Min')) . '">';
|
||||
print '<input type="text" name="search_time_max" value="' . dol_escape_htmltag($search_time_max) . '" size="3" placeholder="' . dol_escape_htmltag($langs->trans('Max')) . '">';
|
||||
// Status search
|
||||
print '<td class="liste_titre center">';
|
||||
$status_options = array('' => $langs->trans("All"), 'success' => $langs->trans("Success"), 'confirm' => $langs->trans("Confirm"), 'error' => $langs->trans("Error"));
|
||||
print $form->selectarray('search_status', $status_options, $search_status, 0, 0, 0, '', 1); // @phan-suppress-current-line PhanPluginSuspiciousParamOrder
|
||||
print '</td>';
|
||||
// Search buttons
|
||||
print '<td class="liste_titre center">';
|
||||
$searchpicto = img_picto($langs->trans("Search"), 'search.png', '', 0, 1);
|
||||
print '<input type="image" class="liste_titre" name="button_search" src="' . $searchpicto . '" value="' . dol_escape_htmltag($langs->trans("Search")) . '" title="' . dol_escape_htmltag($langs->trans("Search")) . '">';
|
||||
$clearpicto = img_picto($langs->trans("RemoveFilter"), 'searchclear.png', '', 0, 1);
|
||||
print '<input type="image" class="liste_titre" name="button_removefilter" src="' . $clearpicto . '" value="' . dol_escape_htmltag($langs->trans("RemoveFilter")) . '" title="' . dol_escape_htmltag($langs->trans("RemoveFilter")) . '">';
|
||||
print '</td>';
|
||||
print '</tr>';
|
||||
|
||||
// Mass action buttons
|
||||
print '<tr class="liste_titre">';
|
||||
print '<td class="liste_titre" colspan="8">';
|
||||
print '<div class="center">';
|
||||
print '<div class="inline-block divButAction"><a class="butAction" href="'.$_SERVER["PHP_SELF"].'?action=purge&token='.newToken().'" onclick="return confirm(\''.$langs->trans("ConfirmDeleteAllLogs").'\');">'.$langs->trans("ClearAllLogs").'</a></div>';
|
||||
print '</div>';
|
||||
print '</td>';
|
||||
print '</tr>';
|
||||
|
||||
if ($res && $db->num_rows($res) > 0) {
|
||||
$i = 0;
|
||||
while ($obj = $db->fetch_object($res)) {
|
||||
print '<tr class="oddeven">';
|
||||
|
||||
// Date
|
||||
print '<td>' . dol_print_date($db->jdate($obj->date_request), 'dayhour') . '</td>';
|
||||
|
||||
// User
|
||||
print '<td>' . ($obj->login ? dol_escape_htmltag($obj->login) : $langs->trans("Unknown")) . '</td>';
|
||||
|
||||
// Query - properly escaped
|
||||
$shortQuery = dol_trunc($obj->query_text, 60);
|
||||
print '<td title="' . dol_escape_htmltag($obj->query_text) . '">' . dol_escape_htmltag($shortQuery) . '</td>';
|
||||
|
||||
// Tool
|
||||
print '<td>' . dol_escape_htmltag($obj->tool_name) . '</td>';
|
||||
|
||||
// Provider
|
||||
print '<td>' . dol_escape_htmltag($obj->provider) . '</td>';
|
||||
|
||||
// Time
|
||||
$timeColor = ($obj->execution_time > 5) ? 'color:red;' : '';
|
||||
print '<td style="' . $timeColor . '" align="center">' . round($obj->execution_time, 2) . 's</td>';
|
||||
|
||||
// Status
|
||||
$badge = 'badge-status0';
|
||||
if ($obj->status == $langs->transnoentitiesnoconv("Success")) {
|
||||
$badge = 'badge-status4'; // Green
|
||||
}
|
||||
if ($obj->status == $langs->transnoentitiesnoconv("Confirm")) {
|
||||
$badge = 'badge-status3'; // Yellow
|
||||
}
|
||||
if ($obj->status == $langs->transnoentitiesnoconv('Error')) {
|
||||
$badge = 'badge-status8'; // Red
|
||||
}
|
||||
print '<td align="center"><span class="badge ' . $badge . '">' . dol_escape_htmltag($obj->status) . '</span></td>';
|
||||
|
||||
// Details Button (Triggers Modal)
|
||||
// We embed data attributes securely with proper UTF-8 handling
|
||||
$reqSafe = base64_encode($obj->raw_request_payload);
|
||||
$resSafe = base64_encode($obj->raw_response_payload);
|
||||
$errSafe = base64_encode($obj->error_msg);
|
||||
|
||||
print '<td align="center">';
|
||||
print '<a href="#" class="button button-small" onclick="openLogModal(this)"
|
||||
data-req="' . dol_escape_htmltag($reqSafe) . '"
|
||||
data-res="' . dol_escape_htmltag($resSafe) . '"
|
||||
data-err="' . dol_escape_htmltag($errSafe) . '">';
|
||||
print '<span class="fa fa-search-plus"></span> ' . $langs->trans("View");
|
||||
print '</a>';
|
||||
print '</td>';
|
||||
|
||||
print '</tr>';
|
||||
$i++;
|
||||
}
|
||||
} else {
|
||||
$colspan = 8;
|
||||
print '<tr><td colspan="' . $colspan . '" class="opacitymedium">' . $langs->trans("NoLogsFound");
|
||||
if (!empty($where)) {
|
||||
print ' ' . $langs->trans("MatchingSearchCriteria");
|
||||
}
|
||||
print '. ' . $langs->trans("TryAskingAI") . '.</td></tr>';
|
||||
}
|
||||
print '</table></div>';
|
||||
|
||||
print '</form>';
|
||||
|
||||
// --- MODAL HTML & JS ---
|
||||
?>
|
||||
<div id="logModal" style="display:none; position:fixed; z-index:9999; left:0; top:0; width:100%; height:100%; overflow:auto; background-color:rgba(0,0,0,0.5);">
|
||||
<div style="background-color:#fff; margin:5% auto; padding:20px; border:1px solid #888; width:80%; max-width:900px; border-radius:8px; box-shadow:0 4px 8px rgba(0,0,0,0.2);">
|
||||
<span style="float:right; font-size:28px; font-weight:bold; cursor:pointer;" onclick="document.getElementById('logModal').style.display='none'">×</span>
|
||||
<h2><?php echo $langs->trans("LogDetails"); ?></h2>
|
||||
|
||||
<h3><?php echo $langs->trans("ErrorWarning"); ?></h3>
|
||||
<div id="modalError" style="background:#fff0f0; border:1px solid #ffcdd2; color:#d32f2f; padding:10px; border-radius:4px; display:none;"></div>
|
||||
|
||||
<div style="display:flex; gap:20px; margin-top:15px;">
|
||||
<div style="flex:1;">
|
||||
<h3><?php echo $langs->trans("RequestPayload"); ?></h3>
|
||||
<textarea id="modalReq" style="width:100%; height:300px; font-family:monospace; font-size:12px; border:1px solid #ccc;" readonly></textarea>
|
||||
</div>
|
||||
<div style="flex:1;">
|
||||
<h3><?php echo $langs->trans("ResponsePayload"); ?></h3>
|
||||
<textarea id="modalRes" style="width:100%; height:300px; font-family:monospace; font-size:12px; border:1px solid #ccc;" readonly></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="text-align:right; margin-top:15px;">
|
||||
<button class="button" onclick="document.getElementById('logModal').style.display='none'"><?php echo $langs->trans("Close"); ?></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// UTF-8 safe base64 decoding function
|
||||
function base64ToUtf8(str) {
|
||||
// Going backwards: from bytestream, to percent-encoding, to original string.
|
||||
return decodeURIComponent(atob(str).split('').map(function(c) {
|
||||
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
|
||||
}).join(''));
|
||||
}
|
||||
|
||||
// Function to decode Unicode escape sequences in JSON strings
|
||||
function decodeUnicodeEscapes(str) {
|
||||
// First, try to parse as JSON to handle escaped Unicode properly
|
||||
try {
|
||||
// If it's a JSON string, parse and stringify to decode escapes
|
||||
const parsed = JSON.parse(str);
|
||||
return JSON.stringify(parsed, null, 2);
|
||||
} catch (e) {
|
||||
// If not valid JSON, try to decode Unicode escapes in the string
|
||||
return str.replace(/\\u([0-9a-fA-F]{4})/g, function(match, p1) {
|
||||
return String.fromCharCode(parseInt(p1, 16));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Sanitize HTML to prevent XSS
|
||||
function escapeHtml(text) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
function openLogModal(btn) {
|
||||
// Decode Base64 safely with UTF-8 support
|
||||
const reqBase64 = btn.getAttribute('data-req') || '';
|
||||
const resBase64 = btn.getAttribute('data-res') || '';
|
||||
const errBase64 = btn.getAttribute('data-err') || '';
|
||||
|
||||
let req = '';
|
||||
let res = '';
|
||||
let err = '';
|
||||
|
||||
try {
|
||||
// Decode Unicode escape sequences
|
||||
if (reqBase64) {
|
||||
req = base64ToUtf8(reqBase64);
|
||||
req = decodeUnicodeEscapes(req);
|
||||
}
|
||||
if (resBase64) {
|
||||
res = base64ToUtf8(resBase64);
|
||||
res = decodeUnicodeEscapes(res);
|
||||
}
|
||||
if (errBase64) {
|
||||
err = base64ToUtf8(errBase64);
|
||||
err = decodeUnicodeEscapes(err);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error decoding base64:', e);
|
||||
// Fallback to regular atob if UTF-8 decoding fails
|
||||
req = reqBase64 ? atob(reqBase64) : '';
|
||||
res = resBase64 ? atob(resBase64) : '';
|
||||
err = errBase64 ? atob(errBase64) : '';
|
||||
}
|
||||
|
||||
// Sanitize content before setting it
|
||||
document.getElementById('modalReq').value = req || '(<?php echo $langs->trans("NoRequestPayload"); ?>)';
|
||||
document.getElementById('modalRes').value = res || '(<?php echo $langs->trans("NoResponsePayload"); ?>)';
|
||||
|
||||
const errDiv = document.getElementById('modalError');
|
||||
if (err) {
|
||||
errDiv.innerText = err;
|
||||
errDiv.style.display = 'block';
|
||||
} else {
|
||||
errDiv.style.display = 'none';
|
||||
}
|
||||
|
||||
document.getElementById('logModal').style.display = 'block';
|
||||
}
|
||||
</script>
|
||||
|
||||
<?php
|
||||
llxFooter();
|
||||
@@ -4,107 +4,48 @@
|
||||
* Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
|
||||
* Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
|
||||
* Coryright (C) 2024 Alexandre Spangaro <alexandre@inovea-conseil.com>
|
||||
* Copyright (C) 2026 Nick Fragoulis
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* 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
|
||||
* 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
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file htdocs/ai/admin/server_mcp.php
|
||||
* \file htdocs/ai/admin/server_mcp.php
|
||||
* \ingroup ai
|
||||
* \brief Ai setup page.
|
||||
* \brief MCP Server & Assistant Configuration Page
|
||||
*/
|
||||
|
||||
// Load Dolibarr environment
|
||||
require '../../main.inc.php';
|
||||
require_once DOL_DOCUMENT_ROOT."/core/lib/admin.lib.php";
|
||||
require_once DOL_DOCUMENT_ROOT."/core/class/doleditor.class.php";
|
||||
require_once DOL_DOCUMENT_ROOT."/ai/lib/ai.lib.php";
|
||||
|
||||
/**
|
||||
* @var Conf $conf
|
||||
* @var DoliDB $db
|
||||
* @var HookManager $hookmanager
|
||||
* @var Translate $langs
|
||||
* @var User $user
|
||||
* @var Form $form
|
||||
*/
|
||||
|
||||
require '../../main.inc.php';
|
||||
require_once DOL_DOCUMENT_ROOT . "/core/lib/admin.lib.php";
|
||||
require_once DOL_DOCUMENT_ROOT . "/core/class/html.form.class.php";
|
||||
require_once DOL_DOCUMENT_ROOT . "/core/class/doleditor.class.php";
|
||||
require_once DOL_DOCUMENT_ROOT . "/ai/lib/ai.lib.php";
|
||||
require_once DOL_DOCUMENT_ROOT . "/core/lib/functions2.lib.php";
|
||||
require_once DOL_DOCUMENT_ROOT . "/core/lib/security.lib.php";
|
||||
|
||||
|
||||
$langs->loadLangs(array("admin", "website", "other"));
|
||||
|
||||
|
||||
// Parameters
|
||||
$action = GETPOST('action', 'aZ09');
|
||||
$backtopage = GETPOST('backtopage', 'alpha');
|
||||
$modulepart = GETPOST('modulepart', 'aZ09'); // Used by actions_setmoduleoptions.inc.php
|
||||
|
||||
if (empty($action)) {
|
||||
$action = 'edit';
|
||||
}
|
||||
|
||||
$content = GETPOST('content');
|
||||
|
||||
$error = 0;
|
||||
$setupnotempty = 0;
|
||||
|
||||
|
||||
// Set this to 1 to use the factory to manage constants. Warning, the generated module will be compatible with version v15+ only
|
||||
$useFormSetup = 1;
|
||||
|
||||
if (!class_exists('FormSetup')) {
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formsetup.class.php';
|
||||
}
|
||||
|
||||
$formSetup = new FormSetup($db);
|
||||
|
||||
// List all available IA
|
||||
$arrayofai = getListOfAIServices();
|
||||
|
||||
// List all available features
|
||||
$arrayofaifeatures = getListOfAIFeatures();
|
||||
|
||||
$item = $formSetup->newItem('AI_API_SERVICE'); // Name of constant must end with _KEY so it is encrypted when saved into database.
|
||||
$item->setAsSelect($arrayofai);
|
||||
$item->cssClass = 'minwidth150';
|
||||
|
||||
foreach ($arrayofai as $ia => $iarecord) {
|
||||
$ialabel = $iarecord['label'];
|
||||
// Setup conf AI_PUBLIC_INTERFACE_TOPIC
|
||||
/*$item = $formSetup->newItem('AI_API_'.strtoupper($ia).'_ENDPOINT'); // Name of constant must end with _KEY so it is encrypted when saved into database.
|
||||
$item->defaultFieldValue = '';
|
||||
$item->cssClass = 'minwidth500';*/
|
||||
|
||||
$item = $formSetup->newItem('AI_API_'.strtoupper($ia).'_KEY')->setAsSecureKey(); // Name of constant must end with _KEY so it is encrypted when saved into database.
|
||||
$item->nameText = $langs->trans("AI_API_KEY").' ('.$ialabel.')';
|
||||
$item->defaultFieldValue = '';
|
||||
$item->fieldParams['hideGenerateButton'] = 1;
|
||||
$item->fieldParams['trClass'] = 'iaservice '.$ia;
|
||||
$item->cssClass = 'minwidth500 text-security input'.$ia;
|
||||
|
||||
$item = $formSetup->newItem('AI_API_'.strtoupper($ia).'_URL'); // Name of constant must end with _KEY so it is encrypted when saved into database.
|
||||
$item->nameText = $langs->trans("AI_API_URL").' ('.$ialabel.')';
|
||||
$item->defaultFieldValue = '';
|
||||
$item->fieldParams['trClass'] = 'iaservice iaurl '.$ia;
|
||||
$item->cssClass = 'minwidth500 input'.$ia;
|
||||
if ($ia == 'custom') {
|
||||
$item->fieldAttr['placeholder'] = 'https://domainofapi.com/v1/';
|
||||
}
|
||||
}
|
||||
|
||||
$setupnotempty = + count($formSetup->items);
|
||||
|
||||
|
||||
$dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
|
||||
|
||||
// Access control
|
||||
if (!$user->admin) {
|
||||
accessforbidden();
|
||||
@@ -113,35 +54,349 @@ if (!isModEnabled('ai')) {
|
||||
accessforbidden('Module AI not activated.');
|
||||
}
|
||||
|
||||
// Parameters
|
||||
$action = GETPOST('action', 'aZ09');
|
||||
|
||||
/*
|
||||
* Actions
|
||||
* ACTIONS
|
||||
*/
|
||||
|
||||
include DOL_DOCUMENT_ROOT.'/core/actions_setmoduleoptions.inc.php';
|
||||
// Main Settings
|
||||
if ($action == 'update') {
|
||||
$error = 0;
|
||||
|
||||
$action = 'edit';
|
||||
if (GETPOSTISSET('AI_ASK_FOR_CONFIRMATION')) {
|
||||
$res = dolibarr_set_const($db, "AI_ASK_FOR_CONFIRMATION", GETPOSTINT("AI_ASK_FOR_CONFIRMATION"), 'int', 0, '', $conf->entity);
|
||||
if ($res <= 0) $error++;
|
||||
}
|
||||
|
||||
if (GETPOSTISSET('AI_LOG_RETENTION')) {
|
||||
$res = dolibarr_set_const($db, "AI_LOG_RETENTION", GETPOST("AI_LOG_RETENTION"), 'int', 0, '', $conf->entity);
|
||||
if ($res <= 0) {
|
||||
$error++;
|
||||
}
|
||||
}
|
||||
|
||||
if (GETPOSTISSET('AI_DEFAULT_INPUT_MODE')) {
|
||||
$res = dolibarr_set_const($db, "AI_DEFAULT_INPUT_MODE", GETPOST("AI_DEFAULT_INPUT_MODE"), 'chaine', 0, '', $conf->entity);
|
||||
if ($res <= 0) {
|
||||
$error++;
|
||||
}
|
||||
}
|
||||
|
||||
if (GETPOSTISSET('AI_INTENT_PROMPT')) {
|
||||
$res = dolibarr_set_const($db, "AI_INTENT_PROMPT", GETPOST("AI_INTENT_PROMPT"), 'chaine', 0, '', $conf->entity);
|
||||
if ($res <= 0) {
|
||||
$error++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($error) {
|
||||
setEventMessages($langs->trans("ErrorSavingSettings"), null, 'errors');
|
||||
} else {
|
||||
setEventMessages($langs->trans("SetupSaved"), null, 'mesgs');
|
||||
}
|
||||
|
||||
header("Location: ".$_SERVER["PHP_SELF"]."?mainmenu=home");
|
||||
exit;
|
||||
}
|
||||
|
||||
// Test connection
|
||||
if ($action == 'test_provider') {
|
||||
$service = GETPOST('service_key', 'aZ09');
|
||||
if ($service) {
|
||||
$credential = getDolGlobalString('AI_API_' . strtoupper($service) . '_KEY');
|
||||
$url = getDolGlobalString('AI_API_' . strtoupper($service) . '_URL');
|
||||
|
||||
// Decrypt if needed
|
||||
if (preg_match('/^crypted:/', $credential)) {
|
||||
$credential = dol_decode(substr($credential, 8));
|
||||
} elseif (preg_match('/^dolcrypt:/', $credential)) {
|
||||
$credential = dolDecrypt($credential, '');
|
||||
}
|
||||
|
||||
// Only proceed if the key is valid (decrypted or not encrypted)
|
||||
if ($credential !== null) {
|
||||
$res = testAIConnection($service, $credential, $url);
|
||||
|
||||
if ($res['success']) {
|
||||
setEventMessages($langs->trans("ConnectionSuccessful") . $res['message'], null, 'mesgs');
|
||||
} else {
|
||||
setEventMessages($langs->trans("ConnectionFailed") . $res['message'], null, 'errors');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// External Access Settings
|
||||
if ($action == 'update_external') {
|
||||
$error = 0;
|
||||
|
||||
$user_id = GETPOSTINT("AI_MCP_USER_ID");
|
||||
if (GETPOSTISSET('AI_MCP_USER_ID')) {
|
||||
$res = dolibarr_set_const($db, "AI_MCP_USER_ID", $user_id, 'int', 0, '', $conf->entity);
|
||||
if ($res <= 0) {
|
||||
$error++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($error) {
|
||||
setEventMessages($langs->trans("ErrorSavingSettings"), null, 'errors');
|
||||
} else {
|
||||
setEventMessages($langs->trans("SetupSaved"), null, 'mesgs');
|
||||
}
|
||||
|
||||
header("Location: ".$_SERVER["PHP_SELF"]."?mainmenu=home");
|
||||
exit;
|
||||
}
|
||||
|
||||
// Generate New API Key
|
||||
if ($action == 'generate_key') {
|
||||
$newKey = dolGetRandomBytes(64);
|
||||
|
||||
if (empty($newKey)) {
|
||||
setEventMessages($langs->trans("KeyGenerationFailed"), null, 'errors');
|
||||
} else {
|
||||
if (dolibarr_set_const($db, 'AI_MCP_API_KEY', $newKey, 'chaine', 0, '', $conf->entity) > 0) {
|
||||
setEventMessages($langs->trans("KeyGenerationSuccessfull"), null, 'mesgs');
|
||||
} else {
|
||||
setEventMessages($langs->trans("KeySaveFailed"), null, 'errors');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* View
|
||||
* VIEW
|
||||
*/
|
||||
|
||||
$help_url = '';
|
||||
$title = "AiSetup";
|
||||
|
||||
$title = "AIMCPConfig";
|
||||
llxHeader('', $langs->trans($title), $help_url, '', 0, 0, '', '', '', 'mod-ai page-admin');
|
||||
|
||||
// Subheader
|
||||
$linkback = '<a href="'.($backtopage ? $backtopage : DOL_URL_ROOT.'/admin/modules.php?restore_lastsearch_values=1').'">'.img_picto($langs->trans("BackToModuleList"), 'back', 'class="pictofixedwidth"').'<span class="hideonsmartphone">'.$langs->trans("BackToModuleList").'</span></a>';
|
||||
|
||||
print load_fiche_titre($langs->trans($title), $linkback, 'title_setup');
|
||||
$linkback = '<a href="'.DOL_URL_ROOT.'/admin/modules.php?restore_lastsearch_values=1">'.$langs->trans("BackToModuleList").'</a>';
|
||||
print load_fiche_titre($title, $linkback, 'title_setup');
|
||||
|
||||
|
||||
// Configuration header
|
||||
$head = aiAdminPrepareHead();
|
||||
print dol_get_fiche_head($head, 'servermcp', $langs->trans($title), -1, "ai");
|
||||
print dol_get_fiche_head($head, 'servermcp', "MCP Server", -1, "ai");
|
||||
|
||||
print 'There is no MCP server available yet.';
|
||||
print '<span class="opacitymedium">' . $langs->trans("ConfigHelp") . '</span><br><br>';
|
||||
|
||||
$form = new Form($db);
|
||||
|
||||
// Load Current Values
|
||||
$apiKey = getDolGlobalString('AI_MCP_API_KEY');
|
||||
|
||||
// Default Prompt Logic
|
||||
$defaultPromptText = " ROLE: Dolibarr ERP AI.
|
||||
GOAL: Map user intent to specific JSON commands.
|
||||
|
||||
CONSTRAINTS:
|
||||
1. OUTPUT: Single valid JSON object ONLY. No Markdown. No text.
|
||||
Format: {\"tool\": \"tool_name\", \"arguments\": {\"argument_name\": \"argument_value\", ...}}
|
||||
2. TOOLS: Use ONLY provided tools. If no tool or thirdparty fits, you must first use 'respond_to_user' to inform user.
|
||||
3. ARGS: strict adherence to schema. Do not invent parameters.
|
||||
4. MISSING INFO: If a required argument (like an ID) is missing, use 'ask_for_clarification'.
|
||||
5. SAFETY: For DELETE/UPDATE actions, you MUST use 'ask_for_confirmation'.";
|
||||
|
||||
$currentPrompt = getDolGlobalString('AI_INTENT_PROMPT', $defaultPromptText);
|
||||
|
||||
// Settings
|
||||
print '<div class="div-table-responsive-no-min">';
|
||||
print '<table class="noborder centpercent">';
|
||||
|
||||
// Enable/Disable
|
||||
print '<tr class="oddeven">';
|
||||
print '<td class="titlefield" width="30%">' . $langs->trans('EnableMCPServer') . '</td>';
|
||||
print '<td>';
|
||||
print ajax_constantonoff('AI_MCP_ENABLED');
|
||||
print ' <span class="opacitymedium">' . $langs->trans('DisableMCPAI') . '</span>';
|
||||
print '</td>';
|
||||
print '</tr>';
|
||||
|
||||
print '</table>';
|
||||
print '</div>';
|
||||
|
||||
print load_fiche_titre($langs->trans("PrivateModeTitle"), '', 'fas fa-lock');
|
||||
|
||||
print '<form method="POST" action="'.$_SERVER["PHP_SELF"].'">';
|
||||
print '<input type="hidden" name="token" value="'.newToken().'">';
|
||||
print '<input type="hidden" name="action" value="update">';
|
||||
|
||||
print '<div class="div-table-responsive-no-min">';
|
||||
print '<table class="noborder centpercent">';
|
||||
|
||||
// Input Mode
|
||||
print '<tr class="oddeven">';
|
||||
print '<td>Default Interface Mode</td>';
|
||||
print '<td>';
|
||||
$input_modes = [
|
||||
'text' => '💬 ' . $langs->trans('OptionTextOnly'),
|
||||
'native' => '☁️ ' . $langs->trans('OptionCloudFast') . ' - ' . $langs->trans('OptionCloudFasthelp'),
|
||||
'whisper' => '🔒 ' . $langs->trans('OptionWhisperLocal') . ' - ' . $langs->trans('OptionWhisperLocalhelp')
|
||||
];
|
||||
print $form->selectarray('AI_DEFAULT_INPUT_MODE', $input_modes, getDolGlobalString('AI_DEFAULT_INPUT_MODE'), 0, 0, 0);
|
||||
print '<br><span class="opacitymedium small">' . $langs->trans("InputMethodHelp") . '</span>';
|
||||
print '</td>';
|
||||
print '</tr>';
|
||||
|
||||
// Enhanced Privacy control
|
||||
print '<tr class="oddeven">';
|
||||
print '<td class="titlefield" width="30%">' . $langs->trans('ObfuscatePIIData') . '</td>';
|
||||
print '<td>';
|
||||
print ajax_constantonoff('AI_PRIVACY_REDACTION');
|
||||
print ' <span class="opacitymedium">' . $langs->trans("RedactionHelp") . '</span>';
|
||||
print '</td>';
|
||||
print '</tr>';
|
||||
|
||||
// Confirmation Level
|
||||
print '<tr class="oddeven">';
|
||||
print '<td class="titlefield" width="30%">' . $langs->trans("AskConfirmation");
|
||||
print ' <span class="fa fa-info-circle" title="' . $langs->trans("AskConfirmationHelp") . '"></span></td>';
|
||||
print '<td>';
|
||||
$confirmation_options = [
|
||||
'0' => $langs->trans("ConfirmNever"),
|
||||
'1' => $langs->trans("ConfirmWriteOnly"),
|
||||
'2' => $langs->trans("ConfirmAlways")
|
||||
];
|
||||
print $form->selectarray('AI_ASK_FOR_CONFIRMATION', $confirmation_options, getDolGlobalInt('AI_ASK_FOR_CONFIRMATION', 1), 0, 0, 0);
|
||||
print '</td>';
|
||||
print '</tr>';
|
||||
|
||||
// Logging
|
||||
print '<tr class="oddeven">';
|
||||
print '<td class="titlefield" width="30%">' . $langs->trans('EnableLogging') . '</td>';
|
||||
print '<td>';
|
||||
print ajax_constantonoff('AI_LOG_REQUESTS');
|
||||
print ' <a href="' . DOL_URL_ROOT . '/ai/admin/log_viewer.php" target="_blank" class="button" style="padding-top: 4px; padding-bottom: 4px;">View Logs</a>';
|
||||
print '</td>';
|
||||
print '</tr>';
|
||||
|
||||
print '<tr class="oddeven">';
|
||||
print '<td>' . $langs->trans("LogRetention") . '</td>';
|
||||
print '<td><input type="number" name="AI_LOG_RETENTION" value="' . getDolGlobalInt('AI_LOG_RETENTION', 30) . '" size="5"> (0 = Forever)</td>';
|
||||
print '</tr>';
|
||||
|
||||
// System Prompt
|
||||
print '<tr class="oddeven">';
|
||||
print '<td colspan="2">';
|
||||
print '<strong>' . $langs->trans("SystemPrompt") . '</strong><br>';
|
||||
print '<span class="opacitymedium small">' . $langs->trans("SystemPromptHelp") . '</span><br>';
|
||||
$doleditor = new DolEditor('AI_INTENT_PROMPT', $currentPrompt, '', 250, 'dolibarr_notes', 'In', false, false, true, ROWS_8, '90%');
|
||||
$doleditor->Create();
|
||||
print '</td>';
|
||||
print '</tr>';
|
||||
|
||||
print '</table>';
|
||||
print '</div>';
|
||||
|
||||
print '<div class="center"><input type="submit" class="button" value="'.$langs->trans("Save").'"></div>';
|
||||
print '</form>';
|
||||
|
||||
|
||||
|
||||
// AI Provider Config and Connection testing
|
||||
$services = getListOfAIServices();
|
||||
$currentService = getDolGlobalString('AI_API_SERVICE');
|
||||
|
||||
print load_fiche_titre($langs->trans("AIProviderConfigTitle"), '', 'fa fa-plug');
|
||||
|
||||
if ((string) $currentService == '-1') {
|
||||
print '<div class="warning">'.$langs->trans("NoAIProviderSelected").' <a href="'.dol_buildpath('/ai/admin/setup.php', 1).'">'.$langs->trans("ConfigureHere").'</a></div>';
|
||||
} else {
|
||||
print '<div class="div-table-responsive-no-min">';
|
||||
print '<table class="noborder centpercent">';
|
||||
|
||||
print '<tr class="oddeven"><td class="titlefield">'.$langs->trans("AIProvider").'</td><td>'.$services[$currentService]['label'].'</td></tr>';
|
||||
|
||||
$prefix = 'AI_API_'.strtoupper($currentService);
|
||||
$modelVal = getDolGlobalString($prefix.'_MODEL', $services[$currentService]['textgeneration']);
|
||||
|
||||
print '<tr class="oddeven"><td>'.$langs->trans("AI_API_MODEL").'</td><td>'.$modelVal.'</td></tr>';
|
||||
print '</table></div>';
|
||||
|
||||
print '<div class="center">';
|
||||
|
||||
if ($currentService && $currentService !== '-1') {
|
||||
print ' <a href="'.$_SERVER["PHP_SELF"].'?action=test_provider&service_key='.$currentService.'" class="button">Test Connection</a>';
|
||||
}
|
||||
|
||||
print '</div>';
|
||||
}
|
||||
print '</div>';
|
||||
print '</form>';
|
||||
|
||||
// External Access Configuration
|
||||
print load_fiche_titre($langs->trans("AiMcpExternalAccess"), '', 'fas fa-lock-open text-danger');
|
||||
|
||||
print '<form method="POST" action="'.$_SERVER["PHP_SELF"].'">';
|
||||
print '<input type="hidden" name="token" value="'.newToken().'">';
|
||||
print '<input type="hidden" name="action" value="update_external">';
|
||||
|
||||
print '<div class="div-table-responsive-no-min">';
|
||||
print '<table class="noborder centpercent">';
|
||||
|
||||
// Service User
|
||||
print '<tr class="oddeven">';
|
||||
print '<td>Service User <span class="fa fa-info-circle" title="' . $langs->trans("UserPermissionsTooltip") . '"></span></td>';
|
||||
print '<td>';
|
||||
print '<div style="display: flex; align-items: center;">';
|
||||
print $form->select_dolusers(getDolGlobalInt('AI_MCP_USER_ID'), 'AI_MCP_USER_ID', 1);
|
||||
print ' <input type="submit" class="button" value="'.$langs->trans("Save").'" style="margin-left: 20px;">';
|
||||
print '</div>';
|
||||
print '<span class="opacitymedium small">' . $langs->trans("DedicatedUserRecommendation") . '</span>';
|
||||
print '</td>';
|
||||
print '</tr>';
|
||||
|
||||
print '<tr class="oddeven">';
|
||||
print '<td width="30%">API Key</td>';
|
||||
print '<td>';
|
||||
if ($apiKey) {
|
||||
print '<input type="text" id="apikey" value="'.$apiKey.'" readonly style="width:400px; padding:6px; background:#f4f4f4; border:1px solid #ccc; color:#555;">';
|
||||
print ' <button type="button" class="button small" onclick="navigator.clipboard.writeText(document.getElementById(\'apikey\').value)">' . $langs->trans("Copy") . '</button>';
|
||||
print ' <a class="button" href="'.$_SERVER["PHP_SELF"].'?action=generate_key&token='.newToken().'">Generate New Key</a>';
|
||||
} else {
|
||||
print '<span class="opacitymedium">' . $langs->trans("NoKeyWarning") . '</span>';
|
||||
print ' <a class="button" href="' . $_SERVER["PHP_SELF"] . '?action=generate_key&token=' . newToken() . '">' . $langs->trans("GenerateKey") . '</a>';
|
||||
}
|
||||
print '</td>';
|
||||
print '</tr>';
|
||||
|
||||
print '<tr class="oddeven">';
|
||||
print '<td>Endpoint URL</td>';
|
||||
print '<td>';
|
||||
$endpoint = dol_buildpath('/ai/server/mcp_server.php', 2);
|
||||
print '<input type="text" value="'.$endpoint.'" readonly style="width:600px; border:none; background:transparent;">';
|
||||
print '</td>';
|
||||
print '</tr>';
|
||||
|
||||
print '</table>';
|
||||
print '</div>';
|
||||
|
||||
print '</form>';
|
||||
|
||||
// Configuration Examples
|
||||
print '<br>';
|
||||
print '<div style="background:#fcfcfc; border:1px solid #eee; padding:15px; border-radius:5px;">';
|
||||
print '<strong>' . $langs->trans("ClaudeDesktopConfig") . '</strong><br>';
|
||||
print '<pre style="background:#333; color:#fff; padding:10px; border-radius:4px; overflow:auto; margin-top:10px;">';
|
||||
echo htmlspecialchars('{
|
||||
"mcpServers": {
|
||||
"dolibarr": {
|
||||
"command": "node",
|
||||
"args": ["/path/to/mcp-bridge.js"],
|
||||
"env": {
|
||||
"DOLIBARR_URL": "'.$endpoint.'",
|
||||
"DOLIBARR_API_KEY": "'.($apiKey ? $apiKey : "YOUR_KEY_HERE").'"
|
||||
}
|
||||
}
|
||||
}
|
||||
}');
|
||||
print '</pre>';
|
||||
print '</div>';
|
||||
|
||||
print dol_get_fiche_end();
|
||||
|
||||
llxFooter();
|
||||
$db->close();
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
/* Copyright (C) 2004-2017 Laurent Destailleur <eldy@users.sourceforge.net>
|
||||
* Copyright (C) 2022 Lamrani Abdel
|
||||
* Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
|
||||
* Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
|
||||
* Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
|
||||
* Coryright (C) 2024 Alexandre Spangaro <alexandre@inovea-conseil.com>
|
||||
* Copyright (C) 2026 Nick Fragoulis
|
||||
*
|
||||
* 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
|
||||
@@ -11,7 +12,7 @@
|
||||
* (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
|
||||
* 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.
|
||||
*
|
||||
@@ -27,10 +28,6 @@
|
||||
|
||||
// Load Dolibarr environment
|
||||
require '../../main.inc.php';
|
||||
require_once DOL_DOCUMENT_ROOT."/core/lib/admin.lib.php";
|
||||
require_once DOL_DOCUMENT_ROOT."/core/class/doleditor.class.php";
|
||||
require_once DOL_DOCUMENT_ROOT."/ai/lib/ai.lib.php";
|
||||
|
||||
/**
|
||||
* @var Conf $conf
|
||||
* @var DoliDB $db
|
||||
@@ -38,6 +35,9 @@ require_once DOL_DOCUMENT_ROOT."/ai/lib/ai.lib.php";
|
||||
* @var Translate $langs
|
||||
* @var User $user
|
||||
*/
|
||||
require_once DOL_DOCUMENT_ROOT."/core/lib/admin.lib.php";
|
||||
require_once DOL_DOCUMENT_ROOT."/core/class/doleditor.class.php";
|
||||
require_once DOL_DOCUMENT_ROOT."/ai/lib/ai.lib.php";
|
||||
|
||||
$langs->loadLangs(array("admin", "website", "other"));
|
||||
|
||||
@@ -66,23 +66,29 @@ if (!class_exists('FormSetup')) {
|
||||
|
||||
$formSetup = new FormSetup($db);
|
||||
|
||||
// List all available IA
|
||||
// List all available AI
|
||||
$arrayofai = getListOfAIServices();
|
||||
|
||||
// List all available features
|
||||
$arrayofaifeatures = getListOfAIFeatures();
|
||||
|
||||
// Main Service Selection
|
||||
$item = $formSetup->newItem('AI_API_SERVICE'); // Name of constant must end with _KEY so it is encrypted when saved into database.
|
||||
$item->setAsSelect($arrayofai);
|
||||
$item->cssClass = 'minwidth150';
|
||||
|
||||
// Loop for Provider Configs
|
||||
foreach ($arrayofai as $ia => $iarecord) {
|
||||
if ($ia == '-1') {
|
||||
continue;
|
||||
}
|
||||
$ialabel = $iarecord['label'];
|
||||
// Setup conf AI_PUBLIC_INTERFACE_TOPIC
|
||||
/*$item = $formSetup->newItem('AI_API_'.strtoupper($ia).'_ENDPOINT'); // Name of constant must end with _KEY so it is encrypted when saved into database.
|
||||
$item->defaultFieldValue = '';
|
||||
$item->cssClass = 'minwidth500';*/
|
||||
|
||||
// API Key
|
||||
$item = $formSetup->newItem('AI_API_'.strtoupper($ia).'_KEY')->setAsSecureKey(); // Name of constant must end with _KEY so it is encrypted when saved into database.
|
||||
$item->nameText = $langs->trans("AI_API_KEY").' ('.$ialabel.')';
|
||||
$item->defaultFieldValue = '';
|
||||
@@ -91,14 +97,23 @@ foreach ($arrayofai as $ia => $iarecord) {
|
||||
$item->cssClass = 'minwidth500 text-security input'.$ia;
|
||||
$item->helpText = '<span class="helptoshow">HelpToShow</span>';
|
||||
|
||||
// API URL
|
||||
$item = $formSetup->newItem('AI_API_'.strtoupper($ia).'_URL'); // Name of constant must end with _KEY so it is encrypted when saved into database.
|
||||
$item->nameText = $langs->trans("AI_API_URL").' ('.$ialabel.')';
|
||||
$item->defaultFieldValue = '';
|
||||
$item->defaultFieldValue = $iarecord['url'];
|
||||
$item->fieldParams['trClass'] = 'iaservice iaurl '.$ia;
|
||||
$item->cssClass = 'minwidth500 input'.$ia;
|
||||
if ($ia == 'custom') {
|
||||
$item->fieldAttr['placeholder'] = 'https://domainofapi.com/v1/';
|
||||
}
|
||||
|
||||
// Generic Model Field
|
||||
$item = $formSetup->newItem('AI_API_'.strtoupper($ia).'_MODEL');
|
||||
$item->nameText = $langs->trans("AI_API_MODEL").' ('.$ialabel.')';
|
||||
$item->defaultFieldValue = $iarecord['textgeneration'];
|
||||
$item->fieldParams['trClass'] = 'iaservice '.$ia;
|
||||
$item->cssClass = 'minwidth500 input'.$ia;
|
||||
$item->helpText = $langs->trans("AI_API_MODEL_HELP");
|
||||
}
|
||||
|
||||
$setupnotempty = + count($formSetup->items);
|
||||
|
||||
@@ -153,18 +153,18 @@ class Ai
|
||||
if (in_array($function, array('file', 'assistant', 'thread'))) {
|
||||
$model = '';
|
||||
} elseif ($function == 'imagegeneration') {
|
||||
$model = getDolGlobalString('AI_API_'.strtoupper($this->apiService).'_MODEL_IMAGE', $arrayofai[$this->apiService][$function]);
|
||||
$model = getDolGlobalString('AI_API_'.strtoupper($this->apiService).'_MODEL_IMAGE', $arrayofai[$this->apiService][$function]['default']);
|
||||
} elseif ($function == 'audiogeneration') {
|
||||
$model = getDolGlobalString('AI_API_'.strtoupper($this->apiService).'_MODEL_AUDIO', $arrayofai[$this->apiService][$function]);
|
||||
$model = getDolGlobalString('AI_API_'.strtoupper($this->apiService).'_MODEL_AUDIO', $arrayofai[$this->apiService][$function]['default']);
|
||||
} elseif ($function == 'transcription') {
|
||||
$model = getDolGlobalString('AI_API_'.strtoupper($this->apiService).'_MODEL_TRANSCRIPT', $arrayofai[$this->apiService][$function]);
|
||||
$model = getDolGlobalString('AI_API_'.strtoupper($this->apiService).'_MODEL_TRANSCRIPT', $arrayofai[$this->apiService][$function]['default']);
|
||||
} elseif ($function == 'translation') {
|
||||
$model = getDolGlobalString('AI_API_'.strtoupper($this->apiService).'_MODEL_TRANSLATE', $arrayofai[$this->apiService][$function]);
|
||||
$model = getDolGlobalString('AI_API_'.strtoupper($this->apiService).'_MODEL_TRANSLATE', $arrayofai[$this->apiService][$function]['default']);
|
||||
} elseif ($function == 'docparsing') {
|
||||
$model = getDolGlobalString('AI_API_'.strtoupper($this->apiService).'_MODEL_DOCPARSING', $arrayofai[$this->apiService][$function]);
|
||||
$model = getDolGlobalString('AI_API_'.strtoupper($this->apiService).'_MODEL_DOCPARSING', $arrayofai[$this->apiService][$function]['default']);
|
||||
} else {
|
||||
// else 'textgenerationemail', 'textgenerationwebpage', 'textgeneration', 'texttranslation', 'textsummarize'
|
||||
$model = getDolGlobalString('AI_API_'.strtoupper($this->apiService).'_MODEL_TEXT', $arrayofai[$this->apiService]['textgeneration']);
|
||||
$model = getDolGlobalString('AI_API_'.strtoupper($this->apiService).'_MODEL_TEXT', $arrayofai[$this->apiService]['textgeneration']['default']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,7 +297,7 @@ class Ai
|
||||
} else {
|
||||
fwrite($fp, "Call endpoint ".$this->apiEndpoint." with POST and the following message:\n");
|
||||
fwrite($fp, $fullInstructions."\n");
|
||||
fwrite($fp, "Prepompt\n");
|
||||
fwrite($fp, "And prepompt:\n");
|
||||
fwrite($fp, $prePrompt."\n");
|
||||
}
|
||||
fwrite($fp, "HTTP Header\n");
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<?php
|
||||
/* Copyright (C) 2022 Alice Adminson <aadminson@example.com>
|
||||
* Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
|
||||
* Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
|
||||
/* Copyright (C) 2022 Alice Adminson <aadminson@example.com>
|
||||
* Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
|
||||
* Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
|
||||
* Copyright (C) 2026 Nick Fragoulis
|
||||
*
|
||||
* 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
|
||||
@@ -9,7 +10,7 @@
|
||||
* (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
|
||||
* 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.
|
||||
*
|
||||
@@ -51,8 +52,7 @@ function getListOfAIFeatures()
|
||||
'audiogeneration' => array('label' => 'AudioGeneration', 'picto' => '', 'status' => 'notused', 'function' => 'AUDIO'),
|
||||
'transcription' => array('label' => 'AudioTranscription', 'picto' => '', 'status' => 'notused', 'function' => 'TRANSCRIPT'),
|
||||
'translation' => array('label' => 'AudioTranslation', 'picto' => '', 'status' => 'notused', 'function' => 'TRANSLATE'),
|
||||
|
||||
'docparsing' => array('label' => 'DocumentPArsing', 'picto' => '', 'status' => 'experimental', 'function' => 'DOCPARSING')
|
||||
'docparsing' => array('label' => 'DocumentParsing', 'picto' => '', 'status' => 'experimental', 'function' => 'DOCPARSING')
|
||||
);
|
||||
|
||||
return $arrayofaifeatures;
|
||||
@@ -70,63 +70,296 @@ function getListOfAIServices()
|
||||
$arrayofai = array(
|
||||
'-1' => array('label' => $langs->trans('SelectAService')),
|
||||
'chatgpt' => array(
|
||||
'label' => 'ChatGPT',
|
||||
'url' => 'https://api.openai.com/v1/',
|
||||
'setup' => 'https://platform.openai.com/account/api-keys',
|
||||
'textgeneration' => 'gpt-4.1-turbo', // a lot of text transformation like: 'textgenerationemail', 'textgenerationwebpage', 'textgeneration', 'texttranslation', 'textsummarize'
|
||||
'imagegeneration' => 'dall-e-3',
|
||||
'audiogeneration' => 'tts-1',
|
||||
'videogeneration' => 'na',
|
||||
'transcription' => 'whisper-1', // audio to text
|
||||
'translation' => 'whisper-1', // audio to text into another language
|
||||
'docparsing' => 'na',
|
||||
'label' => 'ChatGPT (OpenAI)',
|
||||
'url' => 'https://api.openai.com/v1/',
|
||||
'setup' => 'https://platform.openai.com/account/api-keys',
|
||||
'textgeneration' => array('default' => 'gpt-5.2'), // Flagship model released late 2025, updated Feb 2026
|
||||
'imagegeneration' => array('default' => 'gpt-image-1.5'), // Replaced DALL-E 3; 4x faster and native to GPT-5
|
||||
'audiogeneration' => array('default' => 'gpt-audio-1.5'), // New Feb 23, 2026 release for high-fidelity audio out
|
||||
'videogeneration' => array('default' => 'sora-2'), // OpenAI's standard API video model
|
||||
'transcription' => array('default' => 'whisper-large-v3-turbo'), // The current speed/accuracy benchmark for ASR
|
||||
'translation' => array('default' => 'whisper-large-v3-turbo'), // Still the best for multi-language audio translation
|
||||
'docparsing' => array('default' => 'gpt-5.2'), // Uses the new Responses API / Vision capabilities
|
||||
'adapter_type' => 'openai'
|
||||
),
|
||||
'groq' => array(
|
||||
'label' => 'Groq',
|
||||
'url' => 'https://api.groq.com/openai/',
|
||||
'setup' => 'https://platform.groq.com/signup',
|
||||
'textgeneration' => 'mixtral-8x7b-32768', // 'llama3-8b-8192', 'gemma-7b-it'
|
||||
'imagegeneration' => 'na',
|
||||
'audiogeneration' => 'na',
|
||||
'videogeneration' => 'na',
|
||||
'transcription' => 'na',
|
||||
'translation' => 'na',
|
||||
'docparsing' => 'na',
|
||||
'label' => 'Groq (LPU Inference)',
|
||||
'url' => 'https://api.groq.com/openai/v1/',
|
||||
'setup' => 'https://console.groq.com/keys',
|
||||
'textgeneration' => array('default' => 'llama-4-8b-instant'), // February 2026 flagship for extreme speed (1,000+ t/s)
|
||||
'imagegeneration' => array('default' => 'na'),
|
||||
'audiogeneration' => array('default' => 'na'),
|
||||
'videogeneration' => array('default' => 'na'),
|
||||
'transcription' => array('default' => 'whisper-large-v3-turbo'), // Groq's specialized high-speed Whisper implementation
|
||||
'translation' => array('default' => 'whisper-large-v3-turbo'), // High-speed audio translation to English
|
||||
'docparsing' => array('default' => 'llama-4-70b-versatile'), // Best for structured data extraction from text
|
||||
'adapter_type' => 'openai'
|
||||
),
|
||||
'mistral' => array(
|
||||
'label' => 'Mistral',
|
||||
'label' => 'Mistral AI',
|
||||
'url' => 'https://api.mistral.ai/v1/',
|
||||
'setup' => 'https://console.mistral.ai/',
|
||||
'textgeneration' => 'open-mistral-7b',
|
||||
'imagegeneration' => 'na',
|
||||
'audiogeneration' => 'na',
|
||||
'videogeneration' => 'na',
|
||||
'transcription' => 'na',
|
||||
'translation' => 'na',
|
||||
'docparsing' => 'open-mistral-7b',
|
||||
'setup' => 'https://console.mistral.ai/api-keys/',
|
||||
'textgeneration' => array('default' => 'mistral-small-latest', 'examples' => 'mistral-tiny-latest, mistral-small-latest, mistral-medium-latest, mistral-large-latest'), // Points to Mistral Small 3 (updated Feb 2026)
|
||||
'imagegeneration' => array('default' => 'na'),
|
||||
'audiogeneration' => array('default' => 'na'),
|
||||
'videogeneration' => array('default' => 'na'),
|
||||
'transcription' => array('default' => 'na'),
|
||||
'translation' => array('default' => 'na'),
|
||||
'docparsing' => array('default' => 'pixtral-12b-latest'), // Mistral's native vision/doc model
|
||||
'adapter_type' => 'openai'
|
||||
),
|
||||
'deepseek' => array(
|
||||
'label' => 'DeepSeek',
|
||||
'url' => 'https://api.deepseek.com',
|
||||
'setup' => 'https://platform.deepseek.com/api_keys',
|
||||
'textgeneration' => array('default' => 'deepseek-v4'), // Released Feb 2026, flagship MoE model
|
||||
'imagegeneration' => array('default' => 'deepseek-janus-2'), // DeepSeek's latest multimodal vision/gen model
|
||||
'audiogeneration' => array('default' => 'na'),
|
||||
'videogeneration' => array('default' => 'na'),
|
||||
'transcription' => array('default' => 'na'),
|
||||
'translation' => array('default' => 'na'),
|
||||
'docparsing' => array('default' => 'deepseek-v4'), // Massive 1M context support for parsing
|
||||
'adapter_type' => 'openai'
|
||||
),
|
||||
'perplexity' => array(
|
||||
'label' => 'Perplexity (Sonar)',
|
||||
'url' => 'https://api.perplexity.ai',
|
||||
'setup' => 'https://www.perplexity.ai/settings/api',
|
||||
'textgeneration' => array('default' => 'sonar-pro'), // Flagship search model as of Feb 2026
|
||||
'imagegeneration' => array('default' => 'na'),
|
||||
'audiogeneration' => array('default' => 'na'),
|
||||
'videogeneration' => array('default' => 'na'),
|
||||
'transcription' => array('default' => 'na'),
|
||||
'translation' => array('default' => 'na'),
|
||||
'docparsing' => array('default' => 'sonar-reasoning'), // Best for analyzing search-grounded docs
|
||||
'adapter_type' => 'openai'
|
||||
),
|
||||
'zai' => array(
|
||||
'label' => 'Zhipu AI (GLM)',
|
||||
'url' => 'https://api.z.ai/api/paas/v4',
|
||||
'setup' => 'https://docs.z.ai/guides/overview/quick-start',
|
||||
'textgeneration' => array('default' => 'glm-5'), // Flagship released February 11, 2026
|
||||
'imagegeneration' => array('default' => 'cogview-4'), // Zhipu's latest SOTA image generator
|
||||
'audiogeneration' => array('default' => 'cogvlm2-audio'), // High-fidelity conversational audio
|
||||
'videogeneration' => array('default' => 'cogvideox-2'), // Flagship API video model
|
||||
'transcription' => array('default' => 'na'),
|
||||
'translation' => array('default' => 'na'),
|
||||
'docparsing' => array('default' => 'glm-5'), // Top-tier agentic document processing
|
||||
'adapter_type' => 'openai'
|
||||
),
|
||||
'custom' => array(
|
||||
'label' => 'Custom',
|
||||
'url' => 'https://domainofapi.com/v1/',
|
||||
'setup' => 'Ask your AI provider how to get your API key',
|
||||
'textgeneration' => 'tinyllama-1.1b',
|
||||
'imagegeneration' => 'mixtral-8x7b-32768',
|
||||
'audiogeneration' => 'mixtral-8x7b-32768',
|
||||
'videogeneration' => 'na',
|
||||
'transcription' => 'mixtral-8x7b-32768',
|
||||
'translation' => 'mixtral-8x7b-32768',
|
||||
'docparsing' => 'na',
|
||||
'textgeneration' => array('default' => 'tinyllama-1.1b'),
|
||||
'imagegeneration' => array('default' => 'mixtral-8x7b-32768'),
|
||||
'audiogeneration' => array('default' => 'mixtral-8x7b-32768'),
|
||||
'videogeneration' => array('default' => 'na'),
|
||||
'transcription' => array('default' => 'mixtral-8x7b-32768'),
|
||||
'translation' => array('default' => 'mixtral-8x7b-32768'),
|
||||
'docparsing' => array('default' => 'na'),
|
||||
'adapter_type' => 'openai'
|
||||
),
|
||||
// --- SPECIALIZED ADAPTERS ---
|
||||
'anthropic' => array(
|
||||
'label' => 'Anthropic (Claude)',
|
||||
'url' => 'https://api.anthropic.com/v1/',
|
||||
'setup' => 'https://console.anthropic.com/',
|
||||
'textgeneration' => array('default' => 'claude-opus-4-6'), // Released Feb 2026; features a 1M context window
|
||||
'imagegeneration' => array('default' => 'na'), // Anthropic remains focused on text/code logic
|
||||
'audiogeneration' => array('default' => 'na'),
|
||||
'videogeneration' => array('default' => 'na'),
|
||||
'transcription' => array('default' => 'na'),
|
||||
'translation' => array('default' => 'na'),
|
||||
'docparsing' => array('default' => 'claude-opus-4-6'), // Leading model for "Computer Use" and PDF analysis
|
||||
'adapter_type' => 'anthropic'
|
||||
),
|
||||
'google' => array(
|
||||
'label' => 'Google Gemini',
|
||||
'url' => 'https://generativelanguage.googleapis.com/v1beta/',
|
||||
'setup' => 'https://aistudio.google.com/',
|
||||
'textgeneration' => array('default' => 'gemini-3.1-pro-preview'), // Flagship reasoning model released Feb 19, 2026
|
||||
'imagegeneration' => array('default' => 'nano-banana-pro'), // Latest SOTA image model (Gemini 3 Pro Image)
|
||||
'audiogeneration' => array('default' => 'gemini-2.5-pro-tts'), // High-fidelity native speech synthesis
|
||||
'videogeneration' => array('default' => 'veo-3.1'), // Google's flagship cinematic video API
|
||||
'transcription' => array('default' => 'gemini-3.1-pro-preview'), // Native multi-modal audio reasoning
|
||||
'translation' => array('default' => 'gemini-3.1-pro-preview'), // Native audio-to-text translation
|
||||
'docparsing' => array('default' => 'gemini-3.1-pro-preview'), // Massive 2M+ context window for full repo parsing
|
||||
'adapter_type' => 'google'
|
||||
)
|
||||
//'gemini' => array(
|
||||
// 'label' => 'Gemini',
|
||||
//)
|
||||
);
|
||||
|
||||
return $arrayofai;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the connection to an AI service using its API key and URL.
|
||||
*
|
||||
* This function supports multiple AI providers (Google Gemini, Anthropic Claude, and OpenAI-compatible APIs like
|
||||
* Mistral, Groq, and DeepSeek). It constructs a minimal, provider-specific request payload and sends it
|
||||
* to the given endpoint to verify that the API key is valid and the service is reachable.
|
||||
*
|
||||
* @param string $service The identifier of the AI service (e.g., 'google', 'anthropic', 'openai', 'mistral').
|
||||
* @param string $key The API key for the service.
|
||||
* @param string $url The base URL of the AI service's API endpoint.
|
||||
*
|
||||
* @return array{success: bool, message: string} An associative array indicating the result of the test.
|
||||
* - 'success' is true on a successful connection (HTTP 2xx), false otherwise.
|
||||
* - 'message' provides details, such as "OK (HTTP 200)" or an error description.
|
||||
*/
|
||||
function testAIConnection(string $service, string $key, string $url): array
|
||||
{
|
||||
if (empty($key)) {
|
||||
return ['success' => false, 'message' => 'API Key is empty'];
|
||||
}
|
||||
|
||||
// Load Defaults (Ensure this function exists or handle the error)
|
||||
if (!function_exists('getListOfAIServices')) {
|
||||
return ['success' => false, 'message' => 'Configuration helper function missing.'];
|
||||
}
|
||||
|
||||
$list = getListOfAIServices();
|
||||
$defUrl = $list[$service]['url'] ?? '';
|
||||
// Use model from config, fallback to hardcoded if necessary
|
||||
$defaultModel = $list[$service]['model'] ?? 'unknown';
|
||||
|
||||
// Normalize URL
|
||||
if (empty($url)) {
|
||||
$url = $defUrl;
|
||||
}
|
||||
$url = rtrim($url, '/');
|
||||
|
||||
$data = [];
|
||||
$headers = ["Content-Type: application/json"];
|
||||
|
||||
$model = '';
|
||||
if (empty($model)) {
|
||||
$model = getDolGlobalString('AI_API_' . strtoupper($service) . '_MODEL');
|
||||
}
|
||||
|
||||
$data = [];
|
||||
$headers = ["Content-Type: application/json"];
|
||||
|
||||
// GOOGLE
|
||||
if ($service == 'google' || strpos($url, 'googleapis') !== false) {
|
||||
if (strpos($url, ':generateContent') === false) {
|
||||
if (strpos($url, 'models') === false) {
|
||||
$url .= "/models/$model:generateContent";
|
||||
} else {
|
||||
$url .= "/$model:generateContent";
|
||||
}
|
||||
}
|
||||
$url .= "?key=" . $key;
|
||||
$data = ["contents" => [ ["parts" => [ ["text" => "Hello"] ] ] ], "generationConfig" => ["maxOutputTokens" => 5]];
|
||||
} elseif ($service == 'anthropic' || strpos($url, 'anthropic') !== false) { // ANTHROPIC
|
||||
if (strpos($url, 'messages') === false) $url .= '/messages';
|
||||
$headers[] = "x-api-key: $key";
|
||||
$headers[] = "anthropic-version: 2023-06-01";
|
||||
$data = [
|
||||
"model" => $model, // Uses Configured Model
|
||||
"messages" => [["role" => "user", "content" => "Hello"]],
|
||||
"max_tokens" => 5
|
||||
];
|
||||
} else {
|
||||
if (strpos($url, '/chat/completions') === false) $url .= '/chat/completions';
|
||||
$headers[] = "Authorization: Bearer $key";
|
||||
|
||||
$data = [
|
||||
"model" => $model, // Uses Configured Model (from Priority Chain)
|
||||
"messages" => [["role" => "user", "content" => "Hello"]],
|
||||
"max_tokens" => 5
|
||||
];
|
||||
}
|
||||
|
||||
// Execute cURL
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
||||
// Optional: Add SSL verification if behind a proxy with self-signed certs
|
||||
// curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
|
||||
$result = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$err = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ($err) {
|
||||
return ['success' => false, 'message' => "Curl Error: $err"];
|
||||
}
|
||||
|
||||
if ($httpCode >= 200 && $httpCode < 300) {
|
||||
return ['success' => true, 'message' => "OK (HTTP $httpCode)."];
|
||||
} else {
|
||||
$json = json_decode($result, true);
|
||||
// Attempt to find the error message in various common structures
|
||||
$msg = $json['error']['message'] ?? $json['message'] ?? substr($result, 0, 150);
|
||||
return ['success' => false, 'message' => "HTTP $httpCode. Error: $msg"];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log AI Request with Raw Payloads
|
||||
*
|
||||
* @param DoliDB $db Database object
|
||||
* @param User $user User object
|
||||
* @param string $query The query sent to the AI
|
||||
* @param array<string, mixed> $response The full response from the AI
|
||||
* @param string $provider The AI provider (e.g., 'OpenAI', 'Anthropic')
|
||||
* @param float $time Execution time in seconds
|
||||
* @param float $confidence Confidence score from the AI (if any)
|
||||
* @param string $status Status of the request (e.g., 'success', 'error')
|
||||
* @param string $error Error message, if any
|
||||
* @param string $rawReq Raw request payload
|
||||
* @param string $rawRes Raw response payload
|
||||
* @return void
|
||||
*/
|
||||
function ai_log_request($db, $user, $query, array $response, $provider, float $time, float $confidence, $status, $error = '', $rawReq = '', $rawRes = ''): void
|
||||
{
|
||||
global $conf;
|
||||
|
||||
if (!getDolGlobalInt('AI_LOG_REQUESTS')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tool = isset($response['tool']) ? (string) $response['tool'] : '';
|
||||
|
||||
if (dol_strlen($rawReq) > 60000) {
|
||||
$rawReq = dol_substr($rawReq, 0, 60000) . '... [Truncated]';
|
||||
}
|
||||
|
||||
$rawResStr = (string) $rawRes;
|
||||
if (dol_strlen($rawResStr) > 60000) {
|
||||
$rawResStr = dol_substr($rawResStr, 0, 60000) . '... [Truncated]';
|
||||
}
|
||||
|
||||
$sql = "INSERT INTO " . MAIN_DB_PREFIX . "ai_request_log (";
|
||||
$sql .= "entity, date_request, fk_user, query_text, tool_name, provider, ";
|
||||
$sql .= "execution_time, confidence, status, error_msg, raw_request_payload, raw_response_payload";
|
||||
$sql .= ") VALUES (";
|
||||
$sql .= ((int) $conf->entity) . ", ";
|
||||
$sql .= "'" . $db->idate(dol_now()) . "', ";
|
||||
$sql .= ((int) $user->id) . ", ";
|
||||
$sql .= "'" . $db->escape($query) . "', ";
|
||||
$sql .= "'" . $db->escape($tool) . "', ";
|
||||
$sql .= "'" . $db->escape($provider) . "', ";
|
||||
$sql .= ((float) $time) . ", ";
|
||||
$sql .= ((float) $confidence) . ", ";
|
||||
$sql .= "'" . $db->escape($status) . "', ";
|
||||
$sql .= "'" . $db->escape($error) . "', ";
|
||||
$sql .= "'" . $db->escape($rawReq) . "', ";
|
||||
$sql .= "'" . $db->escape($rawResStr) . "'";
|
||||
$sql .= ")";
|
||||
|
||||
$resql = $db->query($sql);
|
||||
|
||||
if (!$resql) {
|
||||
dol_print_error($db);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list for AI summarize
|
||||
|
||||
@@ -111,6 +111,23 @@ class Documents extends DolibarrApi
|
||||
throw new RestException(403);
|
||||
}
|
||||
|
||||
if (DolibarrApiAccess::$user->socid > 0) {
|
||||
if ($sqlprotectagainstexternals) {
|
||||
$resql = $this->db->query($sqlprotectagainstexternals);
|
||||
if ($resql) {
|
||||
$num = $this->db->num_rows($resql);
|
||||
$i = 0;
|
||||
while ($i < $num) {
|
||||
$obj = $this->db->fetch_object($resql);
|
||||
if (DolibarrApiAccess::$user->socid != $obj->fk_soc) {
|
||||
throw new RestException(403, 'Not allowed to download documents with such a ref');
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$filename = basename($original_file);
|
||||
$original_file_osencoded = dol_osencode($original_file); // New file name encoded in OS encoding charset
|
||||
|
||||
@@ -197,6 +214,23 @@ class Documents extends DolibarrApi
|
||||
throw new RestException(403);
|
||||
}
|
||||
|
||||
if (DolibarrApiAccess::$user->socid > 0) {
|
||||
if ($sqlprotectagainstexternals) {
|
||||
$resql = $this->db->query($sqlprotectagainstexternals);
|
||||
if ($resql) {
|
||||
$num = $this->db->num_rows($resql);
|
||||
$i = 0;
|
||||
while ($i < $num) {
|
||||
$obj = $this->db->fetch_object($resql);
|
||||
if (DolibarrApiAccess::$user->socid != $obj->fk_soc) {
|
||||
throw new RestException(403, 'Not allowed to download documents with such a ref');
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Generates the document
|
||||
$hidedetails = !getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DETAILS') ? 0 : 1;
|
||||
$hidedesc = !getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DESC') ? 0 : 1;
|
||||
@@ -1283,6 +1317,23 @@ class Documents extends DolibarrApi
|
||||
throw new RestException(403);
|
||||
}
|
||||
|
||||
if (DolibarrApiAccess::$user->socid > 0) {
|
||||
if ($sqlprotectagainstexternals) {
|
||||
$resql = $this->db->query($sqlprotectagainstexternals);
|
||||
if ($resql) {
|
||||
$num = $this->db->num_rows($resql);
|
||||
$i = 0;
|
||||
while ($i < $num) {
|
||||
$obj = $this->db->fetch_object($resql);
|
||||
if (DolibarrApiAccess::$user->socid != $obj->fk_soc) {
|
||||
throw new RestException(403, 'Not allowed to download documents with such a ref');
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$filename = basename($original_file);
|
||||
$original_file_osencoded = dol_osencode($original_file); // New file name encoded in OS encoding charset
|
||||
|
||||
|
||||
@@ -1473,6 +1473,7 @@ class Setup extends DolibarrApi
|
||||
$list[$tab->elementtype][$tab->name]['perms'] = $tab->perms;
|
||||
$list[$tab->elementtype][$tab->name]['list'] = $tab->list;
|
||||
$list[$tab->elementtype][$tab->name]['printable'] = $tab->printable;
|
||||
$list[$tab->elementtype][$tab->name]['showintooltip'] = $tab->showintooltip;
|
||||
$list[$tab->elementtype][$tab->name]['totalizable'] = $tab->totalizable;
|
||||
$list[$tab->elementtype][$tab->name]['langs'] = $tab->langs;
|
||||
$list[$tab->elementtype][$tab->name]['help'] = $tab->help;
|
||||
@@ -1558,7 +1559,7 @@ class Setup extends DolibarrApi
|
||||
}
|
||||
|
||||
$sql = "SELECT t.rowid as id, t.name, t.entity, t.elementtype, t.label, t.type, t.size, t.fieldcomputed, t.fielddefault,";
|
||||
$sql .= " t.fieldunique, t.fieldrequired, t.perms, t.enabled, t.pos, t.alwayseditable, t.param, t.list, t.printable,";
|
||||
$sql .= " t.fieldunique, t.fieldrequired, t.perms, t.enabled, t.pos, t.alwayseditable, t.param, t.list, t.printable, t.showintooltip,";
|
||||
$sql .= " t.totalizable, t.langs, t.help, t.css, t.cssview, t.csslist, t.fk_user_author, t.fk_user_modif, t.datec, t.tms";
|
||||
$sql .= " FROM ".MAIN_DB_PREFIX."extrafields as t";
|
||||
$sql .= " WHERE t.entity IN (".getEntity('extrafields').")";
|
||||
@@ -1585,6 +1586,7 @@ class Setup extends DolibarrApi
|
||||
$answer[$tab->elementtype][$tab->name]['perms'] = $tab->perms;
|
||||
$answer[$tab->elementtype][$tab->name]['list'] = $tab->list;
|
||||
$answer[$tab->elementtype][$tab->name]['printable'] = $tab->printable;
|
||||
$answer[$tab->elementtype][$tab->name]['showintooltip'] = $tab->showintooltip;
|
||||
$answer[$tab->elementtype][$tab->name]['totalizable'] = $tab->totalizable;
|
||||
$answer[$tab->elementtype][$tab->name]['langs'] = $tab->langs;
|
||||
$answer[$tab->elementtype][$tab->name]['help'] = $tab->help;
|
||||
@@ -1658,6 +1660,7 @@ class Setup extends DolibarrApi
|
||||
$default_value = $request_data['default'];
|
||||
$totalizable = $request_data['totalizable'];
|
||||
$printable = $request_data['printable'];
|
||||
$showintooltip = $request_data['showintooltip'];
|
||||
$required = $request_data['required'];
|
||||
$langfile = $request_data['langfile'];
|
||||
$computed = $request_data['computed'];
|
||||
@@ -1743,6 +1746,7 @@ class Setup extends DolibarrApi
|
||||
$default_value = $request_data['default'];
|
||||
$totalizable = $request_data['totalizable'];
|
||||
$printable = $request_data['printable'];
|
||||
$showintooltip = $request_data['showintooltip'];
|
||||
$required = $request_data['required'];
|
||||
$langfile = $request_data['langfile'];
|
||||
$computed = $request_data['computed'];
|
||||
|
||||
@@ -122,10 +122,6 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
|
||||
$conf->global->MAIN_DISALLOW_UNSECURED_SELECT_INTO_EXTRAFIELDS_FILTER = 1;
|
||||
|
||||
|
||||
// In API context, we force the protection to avoid forging of criteria including bind SQL injection
|
||||
$conf->global->MAIN_DISALLOW_UNSECURED_SELECT_INTO_EXTRAFIELDS_FILTER = 1;
|
||||
|
||||
|
||||
$url = $_SERVER['PHP_SELF'];
|
||||
if (preg_match('/api\/index\.php$/', $url)) { // sometimes $_SERVER['PHP_SELF'] is 'api\/index\.php' instead of 'api\/index\.php/explorer.php' or 'api\/index\.php/method'
|
||||
$url = $_SERVER['PHP_SELF'].(empty($_SERVER['PATH_INFO']) ? $_SERVER['ORIG_PATH_INFO'] : $_SERVER['PATH_INFO']);
|
||||
|
||||
@@ -25,10 +25,6 @@
|
||||
|
||||
// Load Dolibarr environment
|
||||
require '../../main.inc.php';
|
||||
require_once DOL_DOCUMENT_ROOT . '/core/lib/asset.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT . '/asset/class/assetmodel.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT . '/asset/class/assetaccountancycodes.class.php';
|
||||
|
||||
/**
|
||||
* @var Conf $conf
|
||||
* @var DoliDB $db
|
||||
@@ -36,6 +32,9 @@ require_once DOL_DOCUMENT_ROOT . '/asset/class/assetaccountancycodes.class.php';
|
||||
* @var Translate $langs
|
||||
* @var User $user
|
||||
*/
|
||||
require_once DOL_DOCUMENT_ROOT . '/core/lib/asset.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT . '/asset/class/assetmodel.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT . '/asset/class/assetaccountancycodes.class.php';
|
||||
|
||||
// Load translation files required by the page
|
||||
$langs->loadLangs(array("assets", "companies"));
|
||||
@@ -46,6 +45,7 @@ $ref = GETPOST('ref', 'alpha');
|
||||
$action = GETPOST('action', 'aZ09');
|
||||
$cancel = GETPOST('cancel', 'alpha');
|
||||
$backtopage = GETPOST('backtopage', 'alpha');
|
||||
$backtopageforcancel = GETPOST('backtopageforcancel', 'alpha');
|
||||
|
||||
// Initialize a technical objects
|
||||
$object = new AssetModel($db);
|
||||
|
||||
@@ -115,7 +115,7 @@ $block_static = new BlockedLog($db);
|
||||
$block_static->loadTrackedEvents();
|
||||
|
||||
// Access Control
|
||||
if ((!$user->admin && !$user->hasRight('blockedlog', 'read')) || !isModEnabled('blockedlog')) {
|
||||
if (((!$user->admin && !$user->hasRight('blockedlog', 'read')) || !isModEnabled('blockedlog')) && !userIsTaxAuditor()) {
|
||||
accessforbidden();
|
||||
}
|
||||
|
||||
@@ -415,6 +415,10 @@ if ($action == 'export' && $user->hasRight('blockedlog', 'read')) { // read is
|
||||
|
||||
$i++;
|
||||
}
|
||||
|
||||
if ($i == 0) {
|
||||
fwrite($fh, ";\n");
|
||||
}
|
||||
} else {
|
||||
$error++;
|
||||
setEventMessages($db->lasterror, null, 'errors');
|
||||
@@ -524,159 +528,57 @@ if ($action == 'export' && $user->hasRight('blockedlog', 'read')) { // read is
|
||||
.csvClean($statusofrecord).';'."\n");
|
||||
|
||||
|
||||
// Get lifetime amount of all invoices validated and payments created/deleted.
|
||||
// We do not use $totalamountalllines because it is only for the period, but we want lifetime amount since the first record to now.
|
||||
$totalamountlifetime = array('BILL_VALIDATE' => 0, 'PAYMENT_CUSTOMER_CREATE' => 0, 'PAYMENT_CUSTOMER_DELETE' => 0);
|
||||
$totalhtamountlifetime = array('BILL_VALIDATE' => 0, 'PAYMENT_CUSTOMER_CREATE' => 0, 'PAYMENT_CUSTOMER_DELETE' => 0);
|
||||
|
||||
// Calculate lifetime totals (with date of first record)
|
||||
$sql = "SELECT action, module_source, object_format, MIN(date_creation) as datemin, SUM(amounts_taxexcl) as sumamounts_taxexcl, SUM(amounts) as sumamounts";
|
||||
$sql .= " FROM ".MAIN_DB_PREFIX."blockedlog";
|
||||
$sql .= " WHERE entity = ".((int) $conf->entity);
|
||||
//$sql .= " AND action IN ('BILL_VALIDATE', 'BILL_SENTBYMAIL', 'PAYMENT_CUSTOMER_CREATE', 'CASHCONTROL_CLOSE', 'PAYMENT_CUSTOMER_DELETE', 'DOC_DOWNLOAD', 'DOC_PREVIEW')";
|
||||
$sql .= " AND action IN ('BILL_VALIDATE', 'PAYMENT_CUSTOMER_CREATE', 'PAYMENT_CUSTOMER_DELETE')"; // Only event into lifetime total
|
||||
$sql .= " AND date_creation < '".$db->idate(dol_get_last_day(GETPOSTINT('yeartoexport'), GETPOSTINT('monthtoexport') > 0 ? GETPOSTINT('monthtoexport') : 12))."'";
|
||||
$sql .= " GROUP BY action, module_source, object_format";
|
||||
|
||||
$foundoldformat = 0;
|
||||
$firstrecorddatearray = array();
|
||||
$firstrecorddate = 0;
|
||||
$resql = $db->query($sql);
|
||||
if ($resql) {
|
||||
while ($obj = $db->fetch_object($resql)) {
|
||||
// First record date per action code and module
|
||||
if (!empty($firstrecorddatearray[$obj->action][$obj->module_source])) {
|
||||
$firstrecorddatearray[$obj->action] = min($firstrecorddatearray[$obj->action][$obj->module_source], $db->jdate($obj->datemin, 'gmt'));
|
||||
} else {
|
||||
$firstrecorddatearray[$obj->action] = $db->jdate($obj->datemin, 'gmt');
|
||||
}
|
||||
// First record for all actions code
|
||||
if (!empty($firstrecorddate)) {
|
||||
$firstrecorddate = min($firstrecorddate, $db->jdate($obj->datemin, 'gmt'));
|
||||
} else {
|
||||
$firstrecorddate = $obj->datemin;
|
||||
}
|
||||
|
||||
if (!isset($totalamountlifetime[$obj->action])) {
|
||||
$totalamountlifetime[$obj->action] = 0;
|
||||
}
|
||||
|
||||
// Total per action code and module
|
||||
$totalamountlifetime[$obj->action] += $obj->sumamounts;
|
||||
|
||||
// If format of line is old, the sumamounts_taxexcl was not recorded. So we flag this case.
|
||||
if (empty($obj->object_format) || $obj->object_format == 'V1') {
|
||||
$foundoldformat = 1;
|
||||
} else {
|
||||
$totalhtamountlifetime[$obj->action] += $obj->sumamounts_taxexcl;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$error++;
|
||||
setEventMessages($db->lasterror, null, 'errors');
|
||||
}
|
||||
|
||||
// Now calculate cumulative total of all invoices validated
|
||||
if (array_key_exists('BILL_VALIDATE', $totalhtamount)) {
|
||||
foreach ($totalhtamount['BILL_VALIDATE'] as $val) { // Loop on each module
|
||||
$totalhtamountalllines['BILL_VALIDATE'] += $val;
|
||||
}
|
||||
foreach ($totalvatamount['BILL_VALIDATE'] as $val) {
|
||||
$totalvatamountalllines['BILL_VALIDATE'] += $val;
|
||||
}
|
||||
foreach ($totalamount['BILL_VALIDATE'] as $val) {
|
||||
$totalamountalllines['BILL_VALIDATE'] += $val;
|
||||
}
|
||||
}
|
||||
if (array_key_exists('PAYMENT_CUSTOMER', $totalhtamount)) {
|
||||
foreach ($totalhtamount['PAYMENT_CUSTOMER'] as $val) {
|
||||
$totalhtamountalllines['PAYMENT_CUSTOMER'] += $val;
|
||||
}
|
||||
foreach ($totalvatamount['PAYMENT_CUSTOMER'] as $val) {
|
||||
$totalvatamountalllines['PAYMENT_CUSTOMER'] += $val;
|
||||
}
|
||||
foreach ($totalamount['PAYMENT_CUSTOMER'] as $val) {
|
||||
$totalamountalllines['PAYMENT_CUSTOMER'] += $val;
|
||||
}
|
||||
}
|
||||
include_once DOL_DOCUMENT_ROOT.'/blockedlog/admin/lifetimeamount.inc.php';
|
||||
|
||||
|
||||
// Add a final line with perpetual total for invoice validations
|
||||
$block_static->id = 0;
|
||||
$block_static->date_creation = '';
|
||||
$block_static->action = '';
|
||||
$block_static->module_source = '*';
|
||||
$block_static->pos_source = '*';
|
||||
$block_static->amounts_taxexcl = '';
|
||||
$block_static->amounts = '';
|
||||
$block_static->ref_object = '';
|
||||
$block_static->date_object = 0;
|
||||
$block_static->user_fullname = '';
|
||||
$block_static->linktoref = '';
|
||||
$block_static->linktype = '';
|
||||
$block_static->object_version = '';
|
||||
$block_static->object_format = '';
|
||||
$block_static->signature = '';
|
||||
|
||||
$statusofrecord = '';
|
||||
|
||||
fwrite($fh, 'SUMMARY LIFETIME BILLED - '.$langs->transnoentitiesnoconv("Invoices").' : '.$totalhtamountalllines['BILL_VALIDATE'].' '.$langs->trans("HT")." - ".($foundoldformat ? '' : ($totalamountalllines['BILL_VALIDATE'] - $totalhtamountalllines['BILL_VALIDATE']).' '.$langs->transnoentitiesnoconv("VAT")).' - '.$totalamountalllines['BILL_VALIDATE'].' '.$langs->trans("TTC").";"
|
||||
fwrite($fh, 'SUMMARY LIFETIME BILLED - '.$langs->transnoentitiesnoconv("Invoices").' : '.$totalhtamountlifetime['BILL_VALIDATE'].' '.$langs->trans("HT")." - ".($foundoldformat ? '' : ($totalamountlifetime['BILL_VALIDATE'] - $totalhtamountlifetime['BILL_VALIDATE']).' '.$langs->transnoentitiesnoconv("VAT")).' - '.$totalamountlifetime['BILL_VALIDATE'].' '.$langs->trans("TTC").";"
|
||||
.csvClean('').';'
|
||||
.csvClean('').';'
|
||||
.csvClean('').';'
|
||||
.csvClean('').';'
|
||||
.csvClean('').';'
|
||||
.csvClean('').';'
|
||||
.csvClean('').';'
|
||||
.csvClean('').';'
|
||||
.csvClean('').';'
|
||||
.csvClean('').';'
|
||||
.csvClean('').';'
|
||||
.csvClean('').';'
|
||||
.csvClean('').';'
|
||||
.csvClean('').';'
|
||||
.csvClean('').';'
|
||||
.csvClean('').';'
|
||||
.csvClean($block_static->date_creation).';'
|
||||
.csvClean($block_static->action).';'
|
||||
.csvClean($block_static->module_source).';'
|
||||
.csvClean($block_static->pos_source).';'
|
||||
.csvClean($block_static->amounts_taxexcl).';' // Can be 1.20000000 with 8 digits. TODO Clean to have 8 digits in V1
|
||||
.csvClean($block_static->amounts).';' // Can be 1.20000000 with 8 digits. TODO Clean to have 8 digits in V1
|
||||
.csvClean($block_static->ref_object).';'
|
||||
.csvClean('').';'
|
||||
.csvClean($block_static->user_fullname).';'
|
||||
.csvClean($block_static->linktoref).';'
|
||||
.csvClean($block_static->linktype).';'
|
||||
.csvClean('').';' // We must use the string (so $obj->object_data) and not the array decoded with dolDecodeBlockedData
|
||||
.csvClean($block_static->object_version).';'
|
||||
.csvClean($block_static->object_format).';'
|
||||
.csvClean($block_static->signature).';'
|
||||
.csvClean($statusofrecord).';'
|
||||
.csvClean('>= '.dol_print_date($firstrecorddate, 'standard')).";\n");
|
||||
|
||||
|
||||
// Add a final line with perpetual total for customer payments
|
||||
$block_static->id = 0;
|
||||
$block_static->date_creation = '';
|
||||
$block_static->action = '';
|
||||
$block_static->module_source = '*';
|
||||
$block_static->pos_source = '*';
|
||||
$block_static->amounts_taxexcl = '';
|
||||
$block_static->amounts = '';
|
||||
$block_static->ref_object = '';
|
||||
$block_static->date_object = 0;
|
||||
$block_static->user_fullname = '';
|
||||
$block_static->linktoref = '';
|
||||
$block_static->linktype = '';
|
||||
$block_static->object_version = '';
|
||||
$block_static->object_format = '';
|
||||
$block_static->signature = '';
|
||||
|
||||
$statusofrecord = '';
|
||||
|
||||
fwrite($fh, 'SUMMARY LIFETIME PAID - '.$langs->transnoentitiesnoconv("Payments").' : '.($totalamountlifetime['PAYMENT_CUSTOMER_CREATE'] + $totalamountlifetime['PAYMENT_CUSTOMER_DELETE']).";"
|
||||
.csvClean('').';'
|
||||
.csvClean($block_static->date_creation).';'
|
||||
.csvClean($block_static->action).';'
|
||||
.csvClean($block_static->module_source).';'
|
||||
.csvClean($block_static->pos_source).';'
|
||||
.csvClean($block_static->amounts_taxexcl).';' // Can be 1.20000000 with 8 digits. TODO Clean to have 8 digits in V1
|
||||
.csvClean($block_static->amounts).';' // Can be 1.20000000 with 8 digits. TODO Clean to have 8 digits in V1
|
||||
.csvClean($block_static->ref_object).';'
|
||||
.csvClean('').';'
|
||||
.csvClean($block_static->user_fullname).';'
|
||||
.csvClean($block_static->linktoref).';'
|
||||
.csvClean($block_static->linktype).';'
|
||||
.csvClean('').';' // We must use the string (so $obj->object_data) and not the array decoded with dolDecodeBlockedData
|
||||
.csvClean($block_static->object_version).';'
|
||||
.csvClean($block_static->object_format).';'
|
||||
.csvClean($block_static->signature).';'
|
||||
.csvClean('').';'
|
||||
.csvClean('').';'
|
||||
.csvClean('').';'
|
||||
.csvClean('').';'
|
||||
.csvClean('').';'
|
||||
.csvClean('').';'
|
||||
.csvClean('').';'
|
||||
.csvClean('').';'
|
||||
.csvClean('').';'
|
||||
.csvClean('').';'
|
||||
.csvClean('').';'
|
||||
.csvClean('').';'
|
||||
.csvClean('').';'
|
||||
.csvClean('').';'
|
||||
.csvClean('>= '.dol_print_date($firstrecorddate, 'standard')).";\n");
|
||||
|
||||
// End of file, we will calculate global signature on it now, before adding last line.
|
||||
fclose($fh);
|
||||
|
||||
// Calculate the signature of the file (the last line has a return line)
|
||||
@@ -687,43 +589,50 @@ if ($action == 'export' && $user->hasRight('blockedlog', 'read')) { // read is
|
||||
// Now add a signature to check integrity at end of file
|
||||
file_put_contents($tmpfile, 'END - sha256='.$sha256.' - hmac_sha256='.$hmacsha256, FILE_APPEND);
|
||||
dolChmod($tmpfile);
|
||||
}
|
||||
|
||||
if (!$error) {
|
||||
if ($periodnotcomplete) {
|
||||
setEventMessages($langs->trans("ErrorPeriodMustBePastToAllowExport"), null, "warnings");
|
||||
} else {
|
||||
// We record the export as a new line into the unalterable logs
|
||||
require_once DOL_DOCUMENT_ROOT.'/blockedlog/class/blockedlog.class.php';
|
||||
$b = new BlockedLog($db);
|
||||
|
||||
$object = new stdClass();
|
||||
$object->id = 0;
|
||||
$object->element = 'module';
|
||||
$object->ref = 'systemevent';
|
||||
$object->entity = $conf->entity;
|
||||
$object->date = dol_now();
|
||||
$object->fullname = $user->getFullName($langs);
|
||||
if (!$error) {
|
||||
if ($periodnotcomplete) {
|
||||
setEventMessages($langs->trans("ErrorPeriodMustBePastToAllowExport"), null, "warnings");
|
||||
} else {
|
||||
// We record the export as a new line into the unalterable logs
|
||||
require_once DOL_DOCUMENT_ROOT.'/blockedlog/class/blockedlog.class.php';
|
||||
$b = new BlockedLog($db);
|
||||
|
||||
$object->label = 'Export unalterable logs';
|
||||
$object->period = 'year='.GETPOSTINT('yeartoexport').(GETPOSTINT('monthtoexport') ? ' month='.GETPOSTINT('monthtoexport') : '');
|
||||
$object = new stdClass();
|
||||
$object->id = 0;
|
||||
$object->element = 'module';
|
||||
$object->ref = 'systemevent';
|
||||
$object->entity = $conf->entity;
|
||||
$object->date = dol_now();
|
||||
$object->fullname = $user->getFullName($langs);
|
||||
|
||||
$action = 'BLOCKEDLOG_EXPORT';
|
||||
$object->label = 'Export unalterable logs';
|
||||
|
||||
$result = $b->setObjectData($object, $action, 0, $user, 0);
|
||||
$object->total_billed = $totalhtamountalllines['BILL_VALIDATE'].' '.$langs->trans("HT").' - '.$totalvatamountalllines['BILL_VALIDATE'].' '.$langs->trans("VAT").' - '.$totalamountalllines['BILL_VALIDATE'].' '.$langs->trans("HT");
|
||||
$object->total_collected = $totalamountalllines['PAYMENT_CUSTOMER'];
|
||||
$object->totallifetime_billed = $totalhtamountlifetime['BILL_VALIDATE'].' '.$langs->trans("HT")." - ".($foundoldformat ? '' : ($totalamountlifetime['BILL_VALIDATE'] - $totalhtamountlifetime['BILL_VALIDATE']).' '.$langs->transnoentitiesnoconv("VAT")).' - '.$totalamountlifetime['BILL_VALIDATE'].' '.$langs->trans("HT");
|
||||
$object->totallifetime_collected = ($totalamountlifetime['PAYMENT_CUSTOMER_CREATE'] + $totalamountlifetime['PAYMENT_CUSTOMER_DELETE']);
|
||||
|
||||
if ($result < 0) {
|
||||
setEventMessages('Failed to insert the export into the unalterable log. Export canceled: '.$b->error, null, 'errors');
|
||||
dol_delete_file($tmpfile);
|
||||
$error++;
|
||||
}
|
||||
$object->period = 'year='.GETPOSTINT('yeartoexport').(GETPOSTINT('monthtoexport') ? ' month='.GETPOSTINT('monthtoexport') : '');
|
||||
|
||||
$res = $b->create($user);
|
||||
$action = 'BLOCKEDLOG_EXPORT';
|
||||
|
||||
if ($res < 0) {
|
||||
setEventMessages('Failed to insert the export into the unalterable log. Export canceled: '.$b->error, null, 'errors');
|
||||
dol_delete_file($tmpfile);
|
||||
$error++;
|
||||
$result = $b->setObjectData($object, $action, 0, $user, 0);
|
||||
|
||||
if ($result < 0) {
|
||||
setEventMessages('Failed to insert the export into the unalterable log. Export canceled: '.$b->error, null, 'errors');
|
||||
dol_delete_file($tmpfile);
|
||||
$error++;
|
||||
}
|
||||
|
||||
$res = $b->create($user);
|
||||
|
||||
if ($res < 0) {
|
||||
setEventMessages('Failed to insert the export into the unalterable log. Export canceled: '.$b->error, null, 'errors');
|
||||
dol_delete_file($tmpfile);
|
||||
$error++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -768,11 +677,14 @@ if ($withtab) {
|
||||
}
|
||||
|
||||
$morehtmlcenter = '';
|
||||
$texttop = '';
|
||||
|
||||
$registrationnumber = getHashUniqueIdOfRegistration();
|
||||
$texttop = '<small class="opacitymedium">'.$langs->trans("RegistrationNumber").':</small> <small>'.dol_trunc($registrationnumber, 10).'</small>';
|
||||
if (!isRegistrationDataSavedAndPushed()) {
|
||||
$texttop = '';
|
||||
if (!userIsTaxAuditor()) {
|
||||
$texttop = '<small class="opacitymedium">'.$langs->trans("RegistrationNumber").':</small> <small>'.dol_trunc($registrationnumber, 10).'</small>';
|
||||
if (!isRegistrationDataSavedAndPushed()) {
|
||||
$texttop = '';
|
||||
}
|
||||
}
|
||||
|
||||
print load_fiche_titre($title.'<br>'.$texttop, $linkback, 'blockedlog', 0, '', '', $morehtmlcenter);
|
||||
@@ -785,9 +697,11 @@ print dol_get_fiche_head($head, 'archives', '', -1);
|
||||
//print '<br><br>';
|
||||
|
||||
print '<div class="opacitymedium hideonsmartphone justify">';
|
||||
|
||||
print $langs->trans("ArchivesDesc")."<br>";
|
||||
|
||||
if (!userIsTaxAuditor()) {
|
||||
print $langs->trans("ArchivesDesc")."<br>";
|
||||
} else {
|
||||
print $langs->trans("ArchivesAuditorDesc")."<br>";
|
||||
}
|
||||
print "</div>\n";
|
||||
|
||||
|
||||
@@ -827,6 +741,7 @@ if ($action == 'check' || $action == 'checkconfirmed') {
|
||||
|
||||
$registrationnumber = getHashUniqueIdOfRegistration();
|
||||
$secretkey = $registrationnumber;
|
||||
$inputregistrationnumber = '';
|
||||
|
||||
// Prepare to create a temporary file
|
||||
$fullpathtmp = $upload_dir.'/temp/'.GETPOST('urlfile').'.tmp';
|
||||
@@ -838,15 +753,39 @@ if ($action == 'check' || $action == 'checkconfirmed') {
|
||||
removeLastLine($fullpathtmp);
|
||||
|
||||
print $langs->trans("FileHasBeenEncodedWithASecretKeyStartingWith").' : '.$regnumber.'...<br>';
|
||||
if (preg_match('/^'.$regnumber.'/', $secretkey)) {
|
||||
|
||||
if (preg_match('/^'.$regnumber.'/', $secretkey) && !userIsTaxAuditor()) {
|
||||
print 'As this matches the 10 first characters of the full registration number of this instance, we will use the full registration number to control the archive file...';
|
||||
} else {
|
||||
print 'This archive file was not generated by this instance. The control of authenticity is possible only if you know the full registration number.';
|
||||
print '<input type="text" name="inputregistrationnumber" placeholder="'.$langs->trans("FullRegistrationNumber").'">';
|
||||
if (!userIsTaxAuditor() || !isModEnabled('captureserver')) { // @phan-suppress-current-line UnknownModuleName
|
||||
print 'This archive file was not generated by this instance.';
|
||||
print 'The control of authenticity is possible only if you know the full registration number.';
|
||||
$inputregistrationnumber = '';
|
||||
|
||||
print $langs->trans("PleaseEnterFullRegistrationNumber").' ';
|
||||
} else {
|
||||
// Here module "captureserver" is on. We can search the full registration number and prefill value
|
||||
$sql = "SELECT registerid from ".MAIN_DB_PREFIX."captureserver_captureserver";
|
||||
$sql .= " WHERE registerid LIKE '".$db->escape($regnumber)."%'";
|
||||
$sql .= " AND type = 'dolibarrregistration'";
|
||||
$sql .= " LIMIT 1";
|
||||
|
||||
$resql = $db->query($sql);
|
||||
if ($resql) {
|
||||
$obj = $db->fetch_object($resql);
|
||||
if ($obj) {
|
||||
$inputregistrationnumber = $obj->registerid;
|
||||
}
|
||||
}
|
||||
|
||||
print $langs->trans("WeFoundThisFullRegistrationNumberForThisKey").' ';
|
||||
}
|
||||
print '<input type="text" name="inputregistrationnumber" class="width300" placeholder="'.$langs->trans("FullRegistrationNumber").'"value="'.$inputregistrationnumber.'">';
|
||||
}
|
||||
print '<br><br>';
|
||||
print '<center><a class="button small nomarginleft" href="'.$_SERVER["PHP_SELF"].'?action=checkconfirmed&urlfile='.urlencode(GETPOST('urlfile')).'">'.$langs->trans("ControlFile").'</a></center>';
|
||||
|
||||
|
||||
//<input type="text" name="inputregistrationnumber" placeholder="'.$langs->trans("RegistrationNumber").'">';
|
||||
|
||||
print '</div>';
|
||||
@@ -868,6 +807,13 @@ if ($action == 'check' || $action == 'checkconfirmed') {
|
||||
$nbLinesModifiedInExportButKo = 0;
|
||||
$nbLinesModifiedBeforeExport = 0;
|
||||
|
||||
$amounthtlifetime = array();
|
||||
$amountvatlifetime = array();
|
||||
$amountttclifetime = array();
|
||||
$amounthtlifetime['BILL_VALIDATE'] = $amounthtlifetime['PAYMENT_CUSTOMER'] = null;
|
||||
$amountvatlifetime['BILL_VALIDATE'] = $amountvatlifetime['PAYMENT_CUSTOMER'] = null;
|
||||
$amountttclifetime['BILL_VALIDATE'] = $amountttclifetime['PAYMENT_CUSTOMER'] = null;
|
||||
|
||||
$handle = fopen($fullpath, "r");
|
||||
if ($handle) {
|
||||
$numline = 0;
|
||||
@@ -926,7 +872,7 @@ if ($action == 'check' || $action == 'checkconfirmed') {
|
||||
$block_static->action = $lineactioncode = (string) $line[3];
|
||||
$block_static->module_source = (string) $line[4];
|
||||
$block_static->pos_source = (string) $line[5];
|
||||
$block_static->amounts_taxexcl = $lineamountht = ($line[6] === '' ? null : (float) $line[5]);
|
||||
$block_static->amounts_taxexcl = $lineamountht = ($line[6] === '' ? null : (float) $line[6]);
|
||||
$block_static->amounts = $lineamountttc = (float) $line[7];
|
||||
$block_static->ref_object = $lineref = (string) $line[8];
|
||||
$block_static->date_object = (int) $line[9];
|
||||
@@ -1006,10 +952,35 @@ if ($action == 'check' || $action == 'checkconfirmed') {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Test if line is a summary line
|
||||
if (preg_match('/^SUMMARY /', (string) $line[0])) {
|
||||
// We are on a line for summary information
|
||||
$lineanalyzed = 1;
|
||||
/*
|
||||
if (preg_match('/^SUMMARY TURNOVER BILLED/', (string) $line[0])) {
|
||||
// Do nothing, we recalculate amount from previous lines
|
||||
}
|
||||
if (preg_match('/^SUMMARY TURNOVER PAID/', (string) $line[0])) {
|
||||
// Do nothing, we recalculate amount from previous lines
|
||||
}
|
||||
*/
|
||||
if (preg_match('/^SUMMARY LIFETIME BILLED/', (string) $line[0])) {
|
||||
// We load data from line
|
||||
$amountstring = (string) $line[0];
|
||||
if (preg_match('/^SUMMARY LIFETIME BILLED[^\d]*\s:\s([\d\.]+)\s[^\d]+\s([\d\.]+)\s[^\d]+\s([\d\.]+)\s[^\d]+/', $amountstring, $reg)) {
|
||||
$amounthtlifetime['BILL_VALIDATE'] = (float) $reg[1];
|
||||
$amountvatlifetime['BILL_VALIDATE'] = (float) $reg[2];
|
||||
$amountttclifetime['BILL_VALIDATE'] = (float) $reg[3];
|
||||
}
|
||||
}
|
||||
if (preg_match('/^SUMMARY LIFETIME PAID/', (string) $line[0])) {
|
||||
$amountstring = (string) $line[0];
|
||||
if (preg_match('/^SUMMARY LIFETIME PAID[^\d]*\s:\s([\d\.]+)/', $amountstring, $reg)) {
|
||||
$amounthtlifetime['PAYMENT_CUSTOMER'] = (float) $reg[1];
|
||||
$amountvatlifetime['PAYMENT_CUSTOMER'] = 0.0;
|
||||
$amountttclifetime['PAYMENT_CUSTOMER'] = (float) $reg[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (preg_match('/END - ([a-z0-9_]+)=([a-z0-9]+) - ([a-z0-9_]+)=([a-z0-9]+)$/', (string) $line[0], $reg)) {
|
||||
@@ -1040,7 +1011,7 @@ if ($action == 'check' || $action == 'checkconfirmed') {
|
||||
|
||||
print '<br><br>';
|
||||
|
||||
if ($recalculatedhashsign && $recalculatedhashsign == $hashsign) {
|
||||
if ($recalculatedhashsign && hash_equals($recalculatedhashsign, $hashsign)) {
|
||||
print img_picto('', 'tick', 'class="valignmiddle pictofixedwidth"');
|
||||
print '<b>'.$langs->trans("FileIntegrity").'</b> ';
|
||||
print ' '.$form->textwithpicto('', $langs->trans("FileContentMatchSignature").'<br><br>'.$algosign.' = '.$recalculatedhashsign);
|
||||
@@ -1051,12 +1022,12 @@ if ($action == 'check' || $action == 'checkconfirmed') {
|
||||
}
|
||||
print '<br><br>';
|
||||
|
||||
if ($recalculatedhashauth && $recalculatedhashauth == $hashauth) {
|
||||
if ($recalculatedhashauth && hash_equals($recalculatedhashauth, $hashauth)) {
|
||||
print img_picto('', 'tick', 'class="valignmiddle pictofixedwidth"');
|
||||
print '<b>'.$langs->trans("FileAuthenticity").'</b> ';
|
||||
print ' - <span class="opacitymedium">'.$langs->trans("FileWasGeneratedByThisInstance").'</span>';
|
||||
print ' '.$form->textwithpicto('', $langs->trans("FileContentMatchSignature").'<br><br>'.$algoauth.' = '.$recalculatedhashauth);
|
||||
} elseif ($recalculatedhashsign == $hashsign) {
|
||||
} elseif ($recalculatedhashsign && hash_equals($recalculatedhashsign, $hashsign)) {
|
||||
print img_picto('', 'cross', 'class="error valignmiddle pictofixedwidth"');
|
||||
print '<b>'.$langs->trans("FileAuthenticity").'</b> ';
|
||||
print ' '.$form->textwithpicto('', $langs->trans("FileNotFromInstance").'<br><br>Recalculated '.$recalculatedhashauth.' != Found in file '.$hashauth);
|
||||
@@ -1067,17 +1038,19 @@ if ($action == 'check' || $action == 'checkconfirmed') {
|
||||
}
|
||||
print '<br><br>';
|
||||
|
||||
if ($nbLinesModifiedInExportButKo) {
|
||||
print img_picto('', 'cross', 'class="error valignmiddle pictofixedwidth"');
|
||||
print '<b>'.$langs->trans("nbLinesModifiedInExportButKo").'</b>: ';
|
||||
//print ' '.$form->textwithpicto('', $langs->trans("FileHasBeenCorrupted").'<br>Recalculated '.$recalculatedhashsign.' != Found in file '.$hashsign);
|
||||
print '<br><br>';
|
||||
/*
|
||||
if (!userIsTaxAuditor()) {
|
||||
if ($nbLinesModifiedInExportButKo) {
|
||||
print img_picto('', 'cross', 'class="error valignmiddle pictofixedwidth"');
|
||||
print '<b>'.$langs->trans("nbLinesModifiedInExportButKo").'</b>: ';
|
||||
print '<br><br>';
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
if ($nbLinesModifiedBeforeExport) {
|
||||
print img_picto('', 'warning', 'class="error valignmiddle pictofixedwidth"');
|
||||
print '<b>'.$langs->trans("nbLinesModifiedBeforeExport").'</b>';
|
||||
//print ' '.$form->textwithpicto('', $langs->trans("FileHasBeenCorrupted").'<br>Recalculated '.$recalculatedhashsign.' != Found in file '.$hashsign);
|
||||
print '<br><br>';
|
||||
}
|
||||
|
||||
@@ -1092,7 +1065,7 @@ if ($action == 'check' || $action == 'checkconfirmed') {
|
||||
|
||||
print '<hr>';
|
||||
|
||||
$arraykeys = array('BILL_VALIDATE', 'PAYMENT_CUSTOMER_CREATE');
|
||||
$arraykeys = array('BILL_VALIDATE', 'PAYMENT_CUSTOMER');
|
||||
foreach ($arraykeys as $key) {
|
||||
$totalhttoshow = $totalhtamountforaction[$key];
|
||||
$totalvattoshow = $totalvatamountforaction[$key];
|
||||
@@ -1101,12 +1074,12 @@ if ($action == 'check' || $action == 'checkconfirmed') {
|
||||
print '<b>'.dolPrintHTML($langs->trans("TotalForAction").' '.$langs->trans('log'.$key)).'</b>';
|
||||
if ($key == 'BILL_VALIDATE') {
|
||||
print ' <span class="opacitymedium">('.$langs->trans("Turnover").')</span>';
|
||||
} elseif ($key == 'PAYMENT_CUSTOMER_CREATE') {
|
||||
} elseif ($key == 'PAYMENT_CUSTOMER') {
|
||||
print ' <span class="opacitymedium">('.$langs->trans("TurnoverCollected").')</span>';
|
||||
}
|
||||
print ': ';
|
||||
|
||||
if ($key == 'PAYMENT_CUSTOMER_CREATE') {
|
||||
if ($key == 'PAYMENT_CUSTOMER') {
|
||||
print '<span class="amount">'.price($totaltoshow, 0, $langs, 1, -1, -1, getDolCurrency()).'</span>';
|
||||
} else {
|
||||
print $langs->trans("HT").': ';
|
||||
@@ -1125,6 +1098,44 @@ if ($action == 'check' || $action == 'checkconfirmed') {
|
||||
print '<br>';
|
||||
}
|
||||
|
||||
// Now print the value for lifetime amounts.
|
||||
$arraykeys = array('BILL_VALIDATE', 'PAYMENT_CUSTOMER');
|
||||
foreach ($arraykeys as $key) {
|
||||
if (is_null($amounthtlifetime[$key])) { // If not entry found, we discard
|
||||
continue;
|
||||
}
|
||||
$totalhttoshow = $amounthtlifetime[$key];
|
||||
$totalvattoshow = $amountvatlifetime[$key];
|
||||
$totaltoshow = $amountttclifetime[$key];
|
||||
|
||||
print '<b>'.dolPrintHTML($langs->trans("LifetimeAmountShort").' '.$langs->trans('log'.$key)).'</b>';
|
||||
if ($key == 'BILL_VALIDATE') {
|
||||
print ' <span class="opacitymedium">('.$langs->trans("Turnover").')</span>';
|
||||
} elseif ($key == 'PAYMENT_CUSTOMER') {
|
||||
print ' <span class="opacitymedium">('.$langs->trans("TurnoverCollected").')</span>';
|
||||
}
|
||||
print ': ';
|
||||
|
||||
if ($key == 'PAYMENT_CUSTOMER') {
|
||||
print '<span class="amount">'.price($totaltoshow, 0, $langs, 1, -1, -1, getDolCurrency()).'</span>';
|
||||
} else {
|
||||
print $langs->trans("HT").': ';
|
||||
print '<span class="amount">'.price($totalhttoshow, 0, $langs, 1, -1, -1, getDolCurrency()).'</span>';
|
||||
|
||||
print ' - ';
|
||||
|
||||
print $langs->trans("VAT").': ';
|
||||
print '<span class="amount">'.price($totalvattoshow, 0, $langs, 1, -1, -1, getDolCurrency()).'</span>';
|
||||
|
||||
print ' - ';
|
||||
|
||||
print $langs->trans("TTC").': ';
|
||||
print '<span class="amount">'.price($totaltoshow, 0, $langs, 1, -1, -1, getDolCurrency()).'</span>';
|
||||
}
|
||||
print '<br>';
|
||||
}
|
||||
|
||||
|
||||
print '<br>';
|
||||
|
||||
$text = $langs->trans("IfIntegrityAuthenticityIsOkYouCanGetdetailByOpeningTheFile");
|
||||
@@ -1142,17 +1153,20 @@ if ($action == 'check' || $action == 'checkconfirmed') {
|
||||
if ($action != 'check' && $action != 'checkconfirmed') {
|
||||
$htmltext = '';
|
||||
|
||||
$htmltext .= $langs->trans("UnalterableLogTool2", $langs->transnoentities("Archives"))."<br>";
|
||||
if ($mysoc->country_code == 'FR') {
|
||||
$htmltext .= '<br>'.$langs->trans("UnalterableLogTool1FR").'<br>';
|
||||
if (!userIsTaxAuditor()) {
|
||||
$htmltext .= $langs->trans("UnalterableLogTool2", $langs->transnoentities("Archives"))."<br>";
|
||||
if ($mysoc->country_code == 'FR') {
|
||||
$htmltext .= '<br>'.$langs->trans("UnalterableLogTool1FR").'<br>';
|
||||
}
|
||||
//$htmltext .= $langs->trans("UnalterableLogTool1");
|
||||
//$htmltext .= $langs->trans("UnalterableLogTool3")."<br>";
|
||||
|
||||
print info_admin($htmltext, 0, 0, 'warning');
|
||||
|
||||
print '<br>';
|
||||
} else {
|
||||
print '<br>';
|
||||
}
|
||||
//$htmltext .= $langs->trans("UnalterableLogTool1");
|
||||
//$htmltext .= $langs->trans("UnalterableLogTool3")."<br>";
|
||||
|
||||
print info_admin($htmltext, 0, 0, 'warning');
|
||||
|
||||
|
||||
print '<br>';
|
||||
|
||||
$param = '';
|
||||
if ($contextpage != getDolDefaultContextPage(__FILE__)) {
|
||||
@@ -1218,33 +1232,34 @@ if ($action != 'check' && $action != 'checkconfirmed') {
|
||||
}
|
||||
|
||||
|
||||
print '<form method="POST" id="exportArchives" action="'.$_SERVER["PHP_SELF"].'?output=file">';
|
||||
print '<input type="hidden" name="token" value="'.newToken().'">';
|
||||
print '<input type="hidden" name="action" value="export">';
|
||||
if (!userIsTaxAuditor()) {
|
||||
print '<form method="POST" id="exportArchives" action="'.$_SERVER["PHP_SELF"].'?output=file">';
|
||||
print '<input type="hidden" name="token" value="'.newToken().'">';
|
||||
print '<input type="hidden" name="action" value="export">';
|
||||
|
||||
print '<div class="right">';
|
||||
print '<div class="right">';
|
||||
|
||||
print '<span class="hideonsmartphone">'.$langs->trans("RestrictYearToExport").': </span>';
|
||||
// Month
|
||||
print $formother->select_month((string) GETPOSTINT('monthtoexport'), 'monthtoexport', $langs->trans("Month"), 0, 'minwidth50 maxwidth75imp valignmiddle', true);
|
||||
print '<input type="text" name="yeartoexport" class="valignmiddle maxwidth75imp" value="'.GETPOST('yeartoexport').'" placeholder="'.$langs->trans("Year").'">';
|
||||
print '<span class="hideonsmartphone">'.$langs->trans("RestrictYearToExport").': </span>';
|
||||
// Month
|
||||
print $formother->select_month((string) GETPOSTINT('monthtoexport'), 'monthtoexport', $langs->trans("Month"), 0, 'minwidth50 maxwidth75imp valignmiddle', true);
|
||||
print '<input type="text" name="yeartoexport" class="valignmiddle maxwidth75imp" value="'.GETPOST('yeartoexport').'" placeholder="'.$langs->trans("Year").'">';
|
||||
|
||||
print ' ';
|
||||
print ' ';
|
||||
|
||||
// Disabled, we will use the getHashUniqueIdOfRegistration() as secret HMAC
|
||||
//print '<input type="text" name="hmacexportkey" class="valignmiddle minwidth150imp maxwidth300imp" required value="'.GETPOST('hmacexportkey').'" placeholder="'.$langs->trans("Password").'">';
|
||||
// Disabled, we will use the getHashUniqueIdOfRegistration() as secret HMAC
|
||||
//print '<input type="text" name="hmacexportkey" class="valignmiddle minwidth150imp maxwidth300imp" required value="'.GETPOST('hmacexportkey').'" placeholder="'.$langs->trans("Password").'">';
|
||||
|
||||
print ' ';
|
||||
print ' ';
|
||||
|
||||
print '<input type="hidden" name="withtab" value="'.GETPOST('withtab', 'alpha').'">';
|
||||
print '<input type="submit" name="downloadcsv" class="button" value="'.$langs->trans('DownloadLogCSV').'">';
|
||||
/*if (getDolGlobalString('BLOCKEDLOG_USE_REMOTE_AUTHORITY')) {
|
||||
print ' | <a href="?action=downloadblockchain'.(GETPOST('withtab', 'alpha') ? '&withtab='.GETPOST('withtab', 'alpha') : '').'">'.$langs->trans('DownloadBlockChain').'</a>';
|
||||
}*/
|
||||
print ' </div><br>';
|
||||
|
||||
print '</form>';
|
||||
print '<input type="hidden" name="withtab" value="'.GETPOST('withtab', 'alpha').'">';
|
||||
print '<input type="submit" name="downloadcsv" class="button" value="'.$langs->trans('DownloadLogCSV').'">';
|
||||
/*if (getDolGlobalString('BLOCKEDLOG_USE_REMOTE_AUTHORITY')) {
|
||||
print ' | <a href="?action=downloadblockchain'.(GETPOST('withtab', 'alpha') ? '&withtab='.GETPOST('withtab', 'alpha') : '').'">'.$langs->trans('DownloadBlockChain').'</a>';
|
||||
}*/
|
||||
print ' </div><br>';
|
||||
|
||||
print '</form>';
|
||||
}
|
||||
|
||||
/*
|
||||
print '<form method="POST" id="searchFormList" action="'.dolBuildUrl($_SERVER["PHP_SELF"]).'">';
|
||||
|
||||
@@ -157,6 +157,12 @@ if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x'
|
||||
$search_array_options = array();
|
||||
}
|
||||
|
||||
if (userIsTaxAuditor()) {
|
||||
// When this hidden option is on, open another tab as the tab by default
|
||||
header("Location: ".DOL_URL_ROOT."/blockedlog/admin/blockedlog_archives.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* View
|
||||
@@ -191,11 +197,14 @@ if (GETPOST('withtab', 'alpha')) {
|
||||
}
|
||||
|
||||
$morehtmlcenter = '';
|
||||
$texttop = '';
|
||||
|
||||
$registrationnumber = getHashUniqueIdOfRegistration();
|
||||
$texttop = '<small class="opacitymedium">'.$langs->trans("RegistrationNumber").':</small> <small>'.dol_trunc($registrationnumber, 10).'</small>';
|
||||
if (!isRegistrationDataSavedAndPushed()) {
|
||||
$texttop = '';
|
||||
if (userIsTaxAuditor()) {
|
||||
$texttop = '<small class="opacitymedium">'.$langs->trans("RegistrationNumber").':</small> <small>'.dol_trunc($registrationnumber, 10).'</small>';
|
||||
if (!isRegistrationDataSavedAndPushed()) {
|
||||
$texttop = '';
|
||||
}
|
||||
}
|
||||
|
||||
print load_fiche_titre($title.'<br>'.$texttop, $linkback, 'blockedlog', 0, '', '', $morehtmlcenter);
|
||||
@@ -599,10 +608,11 @@ if (is_array($blocks)) {
|
||||
}
|
||||
print '<tr><td colspan="'.$colspan.'"><span class="opacitymedium">'.$langs->trans("NoRecordFound").'</span></td></tr>';
|
||||
} else {
|
||||
ksort($totalamount);
|
||||
foreach ($totalamount as $key => $totalamountperref) {
|
||||
if ($key == 'BILL_VALIDATE' || $key == 'PAYMENT_CUSTOMER') {
|
||||
// Total
|
||||
print '<tr class="liste_total">';
|
||||
print '<tr class="liste_total totalblockedlog">';
|
||||
|
||||
// Action column
|
||||
if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
|
||||
@@ -614,31 +624,17 @@ if (is_array($blocks)) {
|
||||
print '<td colspan="4">';
|
||||
print dolPrintHTML($langs->trans("TotalForAction").' '.$langs->trans('log'.$key));
|
||||
if ($key == 'BILL_VALIDATE') {
|
||||
print ' <span class="opacitymedium">('.$langs->trans("Turnover").')</span>';
|
||||
print ' <span class="opacitylow">('.$langs->trans("Turnover").')</span>';
|
||||
} elseif ($key == 'PAYMENT_CUSTOMER') {
|
||||
print ' <span class="opacitymedium">('.$langs->trans("TurnoverCollected").')</span>';
|
||||
print ' <span class="opacitylow">('.$langs->trans("TurnoverCollected").')</span>';
|
||||
}
|
||||
print '</td>';
|
||||
|
||||
// Date
|
||||
//print '<td class="nowraponall"></td>';
|
||||
|
||||
// User
|
||||
//print '<td class="tdoverflowmax200">';
|
||||
//print '</td>';
|
||||
|
||||
// Module source
|
||||
//print '<td></td>';
|
||||
|
||||
// Action
|
||||
print '<td></td>';
|
||||
|
||||
// Ref
|
||||
//print '<td class="nowraponall">';
|
||||
//print '</td>';
|
||||
|
||||
// Amount (HT)
|
||||
print '<td class="right nowraponall" colspan="2">';
|
||||
print '<td class="right nowraponall" colspan="3">';
|
||||
$totalhttoshow = 0;
|
||||
foreach ($totalhtamount[$key] as $value) { // Loop on each module
|
||||
$totalhttoshow += $value;
|
||||
@@ -652,21 +648,22 @@ if (is_array($blocks)) {
|
||||
$totaltoshow += $value;
|
||||
}
|
||||
|
||||
print $langs->trans("HT").': ';
|
||||
print price($totalhttoshow);
|
||||
if ($key == 'BILL_VALIDATE') {
|
||||
print price($totalhttoshow);
|
||||
print ' '.$langs->trans("HT");
|
||||
|
||||
//print '<br>';
|
||||
print ' ';
|
||||
print ' - ';
|
||||
|
||||
print $langs->trans("VAT").': ';
|
||||
print price($totalvattoshow);
|
||||
print price($totalvattoshow);
|
||||
print ' '.$langs->trans("VAT");
|
||||
|
||||
//print '<br>';
|
||||
print ' ';
|
||||
print ' - ';
|
||||
}
|
||||
|
||||
print $langs->trans("TTC").': ';
|
||||
print price($totaltoshow);
|
||||
|
||||
if ($key == 'BILL_VALIDATE') {
|
||||
print ' '.$langs->trans("TTC");
|
||||
}
|
||||
print '</td>';
|
||||
|
||||
// Details link
|
||||
@@ -695,6 +692,129 @@ if (is_array($blocks)) {
|
||||
print '</tr>';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO Show the lifetime payment only if we click on a link.
|
||||
$afilterexists = ($search_id || ($search_fk_user > 0) || $search_ref || $search_amount || $search_signature || !empty($search_module_source) || $search_pos_source);
|
||||
|
||||
if (! $afilterexists) {
|
||||
// Get lifetime amount of all invoices validated and payments created/deleted.
|
||||
// We do not use $totalamountalllines because it is only for the period, but we want lifetime amount since the first record to now.
|
||||
|
||||
$totalamountlifetime = array('BILL_VALIDATE' => 0, 'PAYMENT_CUSTOMER_CREATE' => 0, 'PAYMENT_CUSTOMER_DELETE' => 0);
|
||||
$totalhtamountlifetime = array('BILL_VALIDATE' => 0, 'PAYMENT_CUSTOMER_CREATE' => 0, 'PAYMENT_CUSTOMER_DELETE' => 0);
|
||||
$foundoldformat = 0;
|
||||
$firstrecorddate = 0;
|
||||
if (empty($search_end) || $search_end == -1) {
|
||||
$search_end = dol_now();
|
||||
}
|
||||
include_once DOL_DOCUMENT_ROOT.'/blockedlog/admin/lifetimeamount.inc.php';
|
||||
|
||||
if (empty($search_code) || in_array('BILL_VALIDATE', $search_code)) {
|
||||
// Total
|
||||
print '<tr class="liste_total totalblockedlog">';
|
||||
|
||||
// Action column
|
||||
if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
|
||||
print '<td></td>';
|
||||
}
|
||||
|
||||
// ID
|
||||
print '<td colspan="4">';
|
||||
print dolPrintHTML($langs->trans("TotalForAction").' '.$langs->trans('logBILL_VALIDATE'));
|
||||
print ' <span class="opacitylow">('.$langs->trans("Turnover").')';
|
||||
print '<br>'.$langs->trans("LifetimeAmountShort").': '.dol_print_date($firstrecorddate, 'dayhour', 'tzuserrel');
|
||||
if ($search_end && $search_end != -1) {
|
||||
print ' - '.dol_print_date($search_end, 'dayhoursec', 'tzuserrel');
|
||||
} else {
|
||||
print ' - '.$langs->trans("Now");
|
||||
}
|
||||
print '</span>';
|
||||
print '</td>';
|
||||
|
||||
// Action
|
||||
print '<td></td>';
|
||||
|
||||
// Amount (HT)
|
||||
print '<td class="right nowraponall" colspan="3">';
|
||||
print $totalhtamountlifetime['BILL_VALIDATE'].' '.$langs->trans("HT")." - ".($foundoldformat ? '' : ($totalamountlifetime['BILL_VALIDATE'] - $totalhtamountlifetime['BILL_VALIDATE']).' '.$langs->transnoentitiesnoconv("VAT")).' - '.$totalamountlifetime['BILL_VALIDATE'].' '.$langs->trans("TTC");
|
||||
print '</td>';
|
||||
|
||||
// Details link
|
||||
print '<td class="center"></td>';
|
||||
|
||||
// Fingerprint
|
||||
print '<td class="nowraponall"></td>';
|
||||
|
||||
// Status
|
||||
print '<td class="center"></td>';
|
||||
|
||||
// Link to debug information object
|
||||
if (getDolGlobalString("BLOCKEDLOG_DEBUG")) { // If in experimental or develop mode, we add some debug information. It may help developers to find origin of bugs.
|
||||
print '<td class="tdoverflowmax150"'.(preg_match('/<a/', $object_link) ? '' : 'title="'.dol_escape_htmltag(dol_string_nohtmltag($object_link.($object_link_title ? ' - '.$object_link_title : ''))).'"').'>';
|
||||
print '</td>';
|
||||
}
|
||||
|
||||
// Action column
|
||||
if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
|
||||
print '<td class="liste_titre"></td>';
|
||||
}
|
||||
|
||||
print '</tr>';
|
||||
}
|
||||
if (empty($search_code) || in_array('PAYMENT_CUSTOMER_CREATE', $search_code) || in_array('PAYMENT_CUSTOMER_DELETE', $search_code)) {
|
||||
// Total
|
||||
print '<tr class="liste_total totalblockedlog">';
|
||||
|
||||
// Action column
|
||||
if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
|
||||
print '<td></td>';
|
||||
}
|
||||
|
||||
// ID
|
||||
print '<td colspan="4">';
|
||||
print dolPrintHTML($langs->trans("TotalForAction").' '.$langs->trans('logPAYMENT_CUSTOMER'));
|
||||
print ' <span class="opacitylow">('.$langs->trans("TurnoverCollected").')';
|
||||
print '<br>'.$langs->trans("LifetimeAmountShort").': '.dol_print_date($firstrecorddate, 'dayhour', 'tzuserrel');
|
||||
if ($search_end && $search_end != -1) {
|
||||
print ' - '.dol_print_date($search_end, 'dayhoursec', 'tzuserrel');
|
||||
} else {
|
||||
print ' - '.$langs->trans("Now");
|
||||
}
|
||||
print '</span>';
|
||||
print '</td>';
|
||||
|
||||
// Action
|
||||
print '<td></td>';
|
||||
|
||||
// Amount (HT)
|
||||
print '<td class="right nowraponall" colspan="3">';
|
||||
print ($totalamountlifetime['PAYMENT_CUSTOMER_CREATE'] + $totalamountlifetime['PAYMENT_CUSTOMER_DELETE']);
|
||||
print '</td>';
|
||||
|
||||
// Details link
|
||||
print '<td class="center"></td>';
|
||||
|
||||
// Fingerprint
|
||||
print '<td class="nowraponall"></td>';
|
||||
|
||||
// Status
|
||||
print '<td class="center"></td>';
|
||||
|
||||
// Link to debug information object
|
||||
if (getDolGlobalString("BLOCKEDLOG_DEBUG")) { // If in experimental or develop mode, we add some debug information. It may help developers to find origin of bugs.
|
||||
print '<td class="tdoverflowmax150"'.(preg_match('/<a/', $object_link) ? '' : 'title="'.dol_escape_htmltag(dol_string_nohtmltag($object_link.($object_link_title ? ' - '.$object_link_title : ''))).'"').'>';
|
||||
print '</td>';
|
||||
}
|
||||
|
||||
// Action column
|
||||
if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
|
||||
print '<td class="liste_titre"></td>';
|
||||
}
|
||||
|
||||
print '</tr>';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ $origin = GETPOST('origin');
|
||||
$mode = GETPOST('mode');
|
||||
|
||||
// Access Control
|
||||
if (!$user->admin) {
|
||||
if (!$user->admin && !userIsTaxAuditor()) {
|
||||
accessforbidden();
|
||||
}
|
||||
|
||||
@@ -93,11 +93,14 @@ if (GETPOST('withtab', 'alpha')) {
|
||||
}
|
||||
|
||||
$morehtmlcenter = '';
|
||||
$texttop = '';
|
||||
|
||||
$registrationnumber = getHashUniqueIdOfRegistration();
|
||||
$texttop = '<small class="opacitymedium">'.$langs->trans("RegistrationNumber").':</small> <small>'.dol_trunc($registrationnumber, 10).'</small>';
|
||||
if ((!isRegistrationDataSavedAndPushed() || !isModEnabled('blockedlog')) && $mode != "forceregistration") {
|
||||
$texttop = '';
|
||||
if (!userIsTaxAuditor()) {
|
||||
$texttop = '<small class="opacitymedium">'.$langs->trans("RegistrationNumber").':</small> <small>'.dol_trunc($registrationnumber, 10).'</small>';
|
||||
if ((!isRegistrationDataSavedAndPushed() || !isModEnabled('blockedlog')) && $mode != "forceregistration") {
|
||||
$texttop = '';
|
||||
}
|
||||
}
|
||||
|
||||
print load_fiche_titre($title.'<br>'.$texttop, $linkback, 'blockedlog', 0, '', '', $morehtmlcenter);
|
||||
@@ -109,8 +112,9 @@ if ($withtab) {
|
||||
print '<br>';
|
||||
}
|
||||
|
||||
print '<span class="opacitymedium">'.$langs->trans("BlockedLogDesc")."</span><br>\n";
|
||||
|
||||
if (!userIsTaxAuditor()) {
|
||||
print '<span class="opacitymedium">'.$langs->trans("BlockedLogDesc")."</span><br>\n";
|
||||
}
|
||||
|
||||
// Version
|
||||
$versionbadge = '<span class="badge-text badge-secondary">'.getBlockedLogVersionToShow().'</span>';
|
||||
@@ -133,7 +137,7 @@ if ($mysoc->country_code == 'FR') {
|
||||
}
|
||||
|
||||
// Show generic message (for countries that need registration) to explain we need registration to collect data and why
|
||||
if (in_array($mysoc->country_code, array('FR'))) {
|
||||
if (in_array($mysoc->country_code, array('FR')) && !userIsTaxAuditor()) {
|
||||
$organization_for_ping = getDolGlobalString('MAIN_ORGANIZATION_FOR_PING', "Association Dolibarr");
|
||||
$dataprivacy_url = getDolGlobalString('MAIN_ORGANIZATION_URL_PRIVACY', "https://www.dolibarr.org/legal-privacy-gdpr.php");
|
||||
|
||||
|
||||
95
htdocs/blockedlog/admin/lifetimeamount.inc.php
Normal file
95
htdocs/blockedlog/admin/lifetimeamount.inc.php
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
/* Copyright (C) 2026 Laurent Destailleur <eldy@destailleur.fr>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file htdocs/blockedlog/admin/lifetimeamount.inc.php
|
||||
* \ingroup blockedlog
|
||||
* \brief Code to calculate lifetime amount
|
||||
*/
|
||||
|
||||
/**
|
||||
* @var DoliDB $db
|
||||
* @var Conf $conf
|
||||
* @var CommonObject $object
|
||||
*
|
||||
* @var array<string,float> $totalamountlifetime
|
||||
* @var array<string,float> $totalhtamountlifetime
|
||||
* @var int $foundoldformat
|
||||
* @var int $firstrecorddate
|
||||
* @var int $error
|
||||
* @var ?int $search_end
|
||||
*/
|
||||
'@phan-var-force array<string,float> $totalamountlifetime';
|
||||
'@phan-var-force array<string,float> $totalhtamountlifetime';
|
||||
|
||||
// Protection to avoid direct call of template
|
||||
if (empty($conf) || !is_object($conf)) {
|
||||
print "Error, template page ".basename(__FILE__)." can't be called with no conf defined.";
|
||||
exit;
|
||||
}
|
||||
|
||||
$firstrecorddatearray = array();
|
||||
if (!empty($search_end) && $search_end > 0) {
|
||||
$dateend = $search_end;
|
||||
} else {
|
||||
$dateend = dol_get_last_day(GETPOSTINT('yeartoexport'), GETPOSTINT('monthtoexport') > 0 ? GETPOSTINT('monthtoexport') : 12);
|
||||
}
|
||||
|
||||
|
||||
// Calculate lifetime totals (with date of first record)
|
||||
$sql = "SELECT action, module_source, object_format, MIN(date_creation) as datemin, SUM(amounts_taxexcl) as sumamounts_taxexcl, SUM(amounts) as sumamounts";
|
||||
$sql .= " FROM ".MAIN_DB_PREFIX."blockedlog";
|
||||
$sql .= " WHERE entity = ".((int) $conf->entity);
|
||||
//$sql .= " AND action IN ('BILL_VALIDATE', 'BILL_SENTBYMAIL', 'PAYMENT_CUSTOMER_CREATE', 'CASHCONTROL_CLOSE', 'PAYMENT_CUSTOMER_DELETE', 'DOC_DOWNLOAD', 'DOC_PREVIEW')";
|
||||
$sql .= " AND action IN ('BILL_VALIDATE', 'PAYMENT_CUSTOMER_CREATE', 'PAYMENT_CUSTOMER_DELETE')"; // Only event into lifetime total
|
||||
$sql .= " AND date_creation < '".$db->idate($dateend)."'";
|
||||
$sql .= " GROUP BY action, module_source, object_format";
|
||||
|
||||
$resql = $db->query($sql);
|
||||
if ($resql) {
|
||||
while ($obj = $db->fetch_object($resql)) {
|
||||
// First record date per action code and module
|
||||
if (!empty($firstrecorddatearray[$obj->action][$obj->module_source])) {
|
||||
$firstrecorddatearray[$obj->action] = min($firstrecorddatearray[$obj->action][$obj->module_source], $db->jdate($obj->datemin, 'gmt'));
|
||||
} else {
|
||||
$firstrecorddatearray[$obj->action] = $db->jdate($obj->datemin, 'gmt');
|
||||
}
|
||||
// First record for all actions code
|
||||
if (!empty($firstrecorddate)) {
|
||||
$firstrecorddate = min($firstrecorddate, $db->jdate($obj->datemin, 'gmt'));
|
||||
} else {
|
||||
$firstrecorddate = $obj->datemin;
|
||||
}
|
||||
|
||||
if (!isset($totalamountlifetime[$obj->action])) {
|
||||
$totalamountlifetime[$obj->action] = 0;
|
||||
}
|
||||
|
||||
// Total per action code and module
|
||||
$totalamountlifetime[$obj->action] += $obj->sumamounts;
|
||||
|
||||
// If format of line is old, the sumamounts_taxexcl was not recorded. So we flag this case.
|
||||
if (empty($obj->object_format) || $obj->object_format === 'V1') {
|
||||
$foundoldformat = 1;
|
||||
} else {
|
||||
$totalhtamountlifetime[$obj->action] += $obj->sumamounts_taxexcl;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$error++;
|
||||
setEventMessages($db->lasterror, null, 'errors');
|
||||
}
|
||||
@@ -224,11 +224,14 @@ if (GETPOST('withtab', 'alpha')) {
|
||||
}
|
||||
|
||||
$morehtmlcenter = '';
|
||||
$texttop = '';
|
||||
|
||||
$registrationnumber = getHashUniqueIdOfRegistration();
|
||||
$texttop = '<small class="opacitymedium">'.$langs->trans("RegistrationNumber").':</small> <small>'.dol_trunc($registrationnumber, 10).'</small>';
|
||||
if ((!isRegistrationDataSavedAndPushed() || !isModEnabled('blockedlog')) && $mode != "forceregistration") {
|
||||
$texttop = '';
|
||||
if (!userIsTaxAuditor()) {
|
||||
$texttop = '<small class="opacitymedium">'.$langs->trans("RegistrationNumber").':</small> <small>'.dol_trunc($registrationnumber, 10).'</small>';
|
||||
if ((!isRegistrationDataSavedAndPushed() || !isModEnabled('blockedlog')) && $mode != "forceregistration") {
|
||||
$texttop = '';
|
||||
}
|
||||
}
|
||||
|
||||
print load_fiche_titre($title.'<br>'.$texttop, $linkback, 'blockedlog', 0, '', '', $morehtmlcenter);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
/* Copyright (C) 2017 Laurent Destailleur <eldy@users.sourceforge.net>
|
||||
* Copyright (C) 2017 ATM Consulting <contact@atm-consulting.fr>
|
||||
* Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
|
||||
/* Copyright (C) 2017-2026 Laurent Destailleur <eldy@users.sourceforge.net>
|
||||
* Copyright (C) 2017 ATM Consulting <contact@atm-consulting.fr>
|
||||
* Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
|
||||
*
|
||||
* 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
|
||||
@@ -75,6 +75,7 @@ if ($element === 'facture') { // Test on permission done in top of page
|
||||
if ($facture->fetch($id) > 0) {
|
||||
//print 'Object '.$element.' logged with action code = '.$action." pos_print_counter is currently ".$facture->pos_print_counter;
|
||||
|
||||
/* Increase of counter is now managed inside the document.php file
|
||||
if ($facture->status > Facture::STATUS_DRAFT) {
|
||||
// Increase counter by 1
|
||||
$sql = "UPDATE ".MAIN_DB_PREFIX."facture SET pos_print_counter = pos_print_counter + 1";
|
||||
@@ -84,6 +85,7 @@ if ($element === 'facture') { // Test on permission done in top of page
|
||||
$facture->pos_print_counter += 1;
|
||||
// $facture->update($user, 1); // disabled update, we did a direct sql update before. We disable trigger here because we already call the trigger $action = DOC_PREVIEW or DOC_DOWNLOAD just after.
|
||||
}
|
||||
*/
|
||||
|
||||
$facture->call_trigger($action, $user);
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ if ((!$user->admin && !$user->hasRight('blockedlog', 'read')) || empty($conf->bl
|
||||
accessforbidden();
|
||||
}
|
||||
|
||||
$langs->loadLangs(array("admin", "bills", "blockedlog", "cashdesk", "companies", "mails", "members", "products"));
|
||||
$langs->loadLangs(array("admin", "bills", "blockedlog", "cashdesk", "companies", "compta", "mails", "members", "products"));
|
||||
|
||||
|
||||
/*
|
||||
@@ -163,7 +163,7 @@ function formatObject($objtoshow, $prefix, $parentelement = '')
|
||||
'vat_src_code' => 'VATCode',
|
||||
'multicurrency_code' => 'Currency',
|
||||
'qty' => 'Quantity',
|
||||
'remise_percent' => 'DiscountInPercent',
|
||||
'remise_percent' => $langs->transnoentitiesnoconv('Discount').' (%)',
|
||||
'nom' => 'Name',
|
||||
'name' => 'Name',
|
||||
'email' => 'Email',
|
||||
@@ -190,15 +190,19 @@ function formatObject($objtoshow, $prefix, $parentelement = '')
|
||||
'special_code' => 'Special line (WEEE line, option, id of module...)',
|
||||
'status' => 'Status',
|
||||
'cash' => 'PaymentTypeLIQ',
|
||||
'cash_declared' => $langs->transnoentities('PaymentTypeLIQ').' - '.$langs->transnoentities("AmuntCountedByUserShort"),
|
||||
'cash_lifetime' => $langs->transnoentities('LifetimeAmount', $langs->transnoentities('PaymentTypeLIQ')),
|
||||
'cash_declared' => $langs->transnoentitiesnoconv('PaymentTypeLIQ').' - '.$langs->transnoentitiesnoconv("AmuntCountedByUserShort"),
|
||||
'cash_lifetime' => $langs->transnoentitiesnoconv('LifetimeAmount', $langs->transnoentitiesnoconv('PaymentTypeLIQ')),
|
||||
'card' => 'PaymentTypeCB',
|
||||
'card_declared' => $langs->transnoentities('PaymentTypeCB').' - '.$langs->transnoentities("AmuntCountedByUserShort"),
|
||||
'card_lifetime' => $langs->transnoentities('LifetimeAmount', $langs->transnoentities('PaymentTypeCB')),
|
||||
'card_declared' => $langs->transnoentitiesnoconv('PaymentTypeCB').' - '.$langs->transnoentitiesnoconv("AmuntCountedByUserShort"),
|
||||
'card_lifetime' => $langs->transnoentitiesnoconv('LifetimeAmount', $langs->transnoentitiesnoconv('PaymentTypeCB')),
|
||||
'cheque' => 'PaymentTypeCHQ',
|
||||
'cheque_declared' => $langs->transnoentities('PaymentTypeCHQ').' - '.$langs->transnoentities("AmuntCountedByUserShort"),
|
||||
'cheque_lifetime' => $langs->transnoentities('LifetimeAmount', $langs->transnoentities('PaymentTypeCHQ')),
|
||||
'cheque_declared' => $langs->transnoentitiesnoconv('PaymentTypeCHQ').' - '.$langs->transnoentitiesnoconv("AmuntCountedByUserShort"),
|
||||
'cheque_lifetime' => $langs->transnoentitiesnoconv('LifetimeAmount', $langs->transnoentitiesnoconv('PaymentTypeCHQ')),
|
||||
'lifetime_start' => 'LifetimeStatDate',
|
||||
'total_billed' => 'Turnover',
|
||||
'total_collected' => 'TurnoverCollected',
|
||||
'totallifetime_billed' => $langs->transnoentitiesnoconv('Turnover').' - '.$langs->transnoentitiesnoconv('LifetimeAmountShort'),
|
||||
'totallifetime_collected' => $langs->transnoentitiesnoconv('TurnoverCollected').' - '.$langs->transnoentitiesnoconv('LifetimeAmountShort'),
|
||||
'email_from' => 'MailFrom',
|
||||
'email_to' => 'MailTo',
|
||||
'email_msgid' => 'EmailMsgID',
|
||||
@@ -281,6 +285,8 @@ function formatObject($objtoshow, $prefix, $parentelement = '')
|
||||
'cash_declared', 'cheque_declared', 'card_declared', 'cash_lifetime', 'cheque_lifetime', 'card_lifetime'
|
||||
)) || (isset($arrayoffields[$key]['type']) && in_array($arrayoffields[$key]['type'], array('price')))) {
|
||||
$s .= '<span class="amount">'.price($val, 0, $langs, 1, 0, -2).'</span>';
|
||||
} elseif (in_array($key, array('total_billed', 'total_collected', 'totallifetime_billed', 'totallifetime_collected'))) {
|
||||
$s .= '<span class="amount">'.$val.'</span>';
|
||||
} else {
|
||||
$s .= $val;
|
||||
}
|
||||
|
||||
@@ -58,19 +58,23 @@ function blockedlogadmin_prepare_head($withtabsetup)
|
||||
$h = 0;
|
||||
$head = array();
|
||||
|
||||
$head[$h][0] = DOL_URL_ROOT."/blockedlog/admin/registration.php".$param;
|
||||
$head[$h][1] = $langs->trans("UserRegistration");
|
||||
$head[$h][2] = 'registration';
|
||||
$h++;
|
||||
|
||||
$b = new BlockedLog($db);
|
||||
$head[$h][0] = DOL_URL_ROOT."/blockedlog/admin/blockedlog_list.php".$param;
|
||||
$head[$h][1] = $langs->trans("BrowseBlockedLog");
|
||||
if ($b->alreadyUsed()) {
|
||||
$head[$h][1] .= (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') ? '<span class="badge marginleftonlyshort">...</span>' : '');
|
||||
if (!userIsTaxAuditor()) {
|
||||
$head[$h][0] = DOL_URL_ROOT."/blockedlog/admin/registration.php".$param;
|
||||
$head[$h][1] = $langs->trans("UserRegistration");
|
||||
$head[$h][2] = 'registration';
|
||||
$h++;
|
||||
}
|
||||
|
||||
if (!userIsTaxAuditor()) {
|
||||
$b = new BlockedLog($db);
|
||||
$head[$h][0] = DOL_URL_ROOT."/blockedlog/admin/blockedlog_list.php".$param;
|
||||
$head[$h][1] = $langs->trans("BrowseBlockedLog");
|
||||
if ($b->alreadyUsed()) {
|
||||
$head[$h][1] .= (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') ? '<span class="badge marginleftonlyshort">...</span>' : '');
|
||||
}
|
||||
$head[$h][2] = 'fingerprints';
|
||||
$h++;
|
||||
}
|
||||
$head[$h][2] = 'fingerprints';
|
||||
$h++;
|
||||
|
||||
$head[$h][0] = DOL_URL_ROOT."/blockedlog/admin/blockedlog_archives.php".$param;
|
||||
$head[$h][1] = $langs->trans("Archives");
|
||||
@@ -85,7 +89,7 @@ function blockedlogadmin_prepare_head($withtabsetup)
|
||||
$h++;
|
||||
}
|
||||
|
||||
if ($withtabsetup) {
|
||||
if ($withtabsetup && !userIsTaxAuditor()) {
|
||||
$head[$h][0] = DOL_URL_ROOT."/blockedlog/admin/blockedlog.php".$param;
|
||||
$head[$h][1] = $langs->trans("TechnicalInformation");
|
||||
$head[$h][2] = 'technicalinfo';
|
||||
@@ -384,13 +388,16 @@ function callApiToPushCounter($id, $signature, $test, $previousid, $previoussign
|
||||
$url_for_ping = getDolGlobalString('MAIN_URL_FOR_PING', "https://ping.dolibarr.org/");
|
||||
|
||||
$algo = 'sha256';
|
||||
$hash_unique_id = getHashUniqueIdOfRegistration($algo);
|
||||
$hash_unique_id = getHashUniqueIdOfRegistration($algo); // The hash of the unique IDof instance
|
||||
|
||||
$t = microtime(true);
|
||||
$micro = sprintf("%06d", (int) ($t - floor($t)) * 1000000);
|
||||
|
||||
$data = '';
|
||||
$data .= 'hash_algo=dol_hash-'.urlencode($algo);
|
||||
$data .= '&hash_unique_id='.urlencode($hash_unique_id);
|
||||
$data .= '&action=dolibarrpushcounter';
|
||||
$data .= '&datesys='.urlencode(dol_print_date(dol_now(), 'standard', 'gmt'));
|
||||
$data .= '&datesys='.urlencode(dol_print_date(dol_now('gmt'), 'standard', 'gmt').'.'.$micro);
|
||||
$data .= '&version='.(float) DOL_VERSION;
|
||||
$data .= '&version_full='.urlencode(DOL_VERSION);
|
||||
$data .= '&versionblockedlog='.(float) getBlockedLogVersionToShow();
|
||||
@@ -445,3 +452,15 @@ function callApiToPushCounter($id, $signature, $test, $previousid, $previoussign
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if user is a ta auditor
|
||||
*
|
||||
* @return int Return > 0 if user is an external user so must be restricted to archive control feature
|
||||
*/
|
||||
function userIsTaxAuditor()
|
||||
{
|
||||
global $user;
|
||||
|
||||
return ((getDolGlobalString('BLOCKEDLOG_FOR_TAX_AUDITOR') && $user->socid) ? 1 : 0);
|
||||
}
|
||||
|
||||
@@ -258,39 +258,57 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea
|
||||
|
||||
print '</thead>';
|
||||
print '<tbody>';
|
||||
$tablerows = array();
|
||||
$position = '';
|
||||
$levelposition = '';
|
||||
$lineposition = 0;
|
||||
if (count($TChildBom) > 0) {
|
||||
if ($action == 'treeview') {
|
||||
foreach ($TChildBom as $fk_bom => $TProduct) {
|
||||
$repeatChar = ' ';
|
||||
if (!empty($TProduct['bom'])) {
|
||||
// we make position string in format 'lineposition.bomlevel.childposition'
|
||||
if (!empty($TProduct['position']) && $TProduct['parentid'] == $object->id) {
|
||||
// define lineposition
|
||||
$lineposition = $TProduct['position'];
|
||||
}
|
||||
// define lineposition.bomlevel
|
||||
$position = sprintf('%d.%d', $lineposition, $TProduct['level']);
|
||||
// memorize level position for products in bom
|
||||
$levelposition = $position;
|
||||
if (!empty($TProduct['parentid']) && $TProduct['parentid'] != $object->id && empty($TProduct['product'])) {
|
||||
// define lineposition.bomlevel.childposition
|
||||
$position = sprintf('%s.%d', $position, $TProduct['position']);
|
||||
}
|
||||
$prod = new Product($db);
|
||||
$prod->fetch($TProduct['bom']->fk_product);
|
||||
if ($TProduct['parentid'] != $object->id) {
|
||||
print '<tr class="sub_bom_lines oddeven" parentid="'.$TProduct['parentid'].'">';
|
||||
$tablerows[$position] = '<tr class="sub_bom_lines oddeven" parentid="'.$TProduct['parentid'].'">';
|
||||
} else {
|
||||
print '<tr class="oddeven">';
|
||||
$tablerows[$position] = '<tr class="oddeven">';
|
||||
}
|
||||
if ($action == 'treeview') {
|
||||
print '<td class="linecoldescription">'.str_repeat($repeatChar, $TProduct['level']).$prod->getNomUrl(1);
|
||||
} else {
|
||||
print '<td class="linecoldescription">'.str_repeat($repeatChar, $TProduct['level']).$TProduct['bom']->getNomUrl(1);
|
||||
}
|
||||
print ' <a class="collapse_bom" id="collapse-'.$fk_bom.'" href="#">';
|
||||
print img_picto('', 'folder-open');
|
||||
print '</a>';
|
||||
print '</td>';
|
||||
if ($action == 'treeview') {
|
||||
print '<td class="left">'.$TProduct['bom']->getNomUrl(1).'</td>';
|
||||
}
|
||||
print '<td class="linecolqty right">'.price(price2num($TProduct['qty'], 'MS')).'</td>';
|
||||
print '<td>';
|
||||
print '</td>';
|
||||
print '<td class="linecolstock right"></td>';
|
||||
print '<td class="linecoltheoricalstock right"></td>';
|
||||
print '</tr>';
|
||||
$tablerows[$position] .= '<td class="linecoldescription">'.str_repeat($repeatChar, $TProduct['level']).$prod->getNomUrl(1);
|
||||
$tablerows[$position] .= ' <a class="collapse_bom" id="collapse-'.$fk_bom.'" href="#">';
|
||||
$tablerows[$position] .= img_picto('', 'folder-open');
|
||||
$tablerows[$position] .= '</a>';
|
||||
$tablerows[$position] .= '</td>';
|
||||
$tablerows[$position] .= '<td class="left">'.$TProduct['bom']->getNomUrl(1).'</td>';
|
||||
$tablerows[$position] .= '<td class="linecolqty right">'.price(price2num($TProduct['qty'], 'MS')).'</td>';
|
||||
$tablerows[$position] .= '<td>';
|
||||
$tablerows[$position] .= '</td>';
|
||||
$tablerows[$position] .= '<td class="linecolstock right"></td>';
|
||||
$tablerows[$position] .= '<td class="linecoltheoricalstock right"></td>';
|
||||
$tablerows[$position] .= '</tr>';
|
||||
}
|
||||
if (!empty($TProduct['product'])) {
|
||||
foreach ($TProduct['product'] as $fk_product => $TInfos) {
|
||||
if (!empty($TInfos['position']) && $fk_bom == $object->id) {
|
||||
// top level position
|
||||
$position = $TInfos['position'];
|
||||
} else {
|
||||
// sublevel position
|
||||
$position = sprintf('%s.%d', $levelposition, $TInfos['position']);
|
||||
}
|
||||
$prod = new Product($db);
|
||||
$prod->fetch($fk_product);
|
||||
$prod->load_virtual_stock();
|
||||
@@ -298,23 +316,27 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea
|
||||
$prod->stock_reel = 0;
|
||||
}
|
||||
if ($fk_bom != $object->id) {
|
||||
print '<tr class="sub_bom_lines oddeven" parentid="'.$fk_bom.'">';
|
||||
$tablerows[$position] = '<tr class="sub_bom_lines oddeven" parentid="'.$fk_bom.'">'; // sub bom on same position
|
||||
} else {
|
||||
print '<tr class="oddeven">';
|
||||
$tablerows[$position] = '<tr class="oddeven">';
|
||||
}
|
||||
print '<td class="linecoldescription">'.str_repeat($repeatChar, $TInfos['level']).$prod->getNomUrl(1).'</td>';
|
||||
if ($action == 'treeview') {
|
||||
print '<td></td>';
|
||||
}
|
||||
print '<td class="linecolqty right">'.price(price2num($TInfos['qty'], 'MS')).'</td>';
|
||||
print '<td>';
|
||||
print '</td>';
|
||||
print '<td class="linecolstock right">'.price2num($prod->stock_reel, 'MS').'</td>';
|
||||
print '<td class="linecoltheoricalstock right">'.$prod->stock_theorique.'</td>';
|
||||
print '</tr>';
|
||||
$tablerows[$position] .= '<td class="linecoldescription">'.str_repeat($repeatChar, (int) $TInfos['level']).$prod->getNomUrl(1).'</td>';
|
||||
$tablerows[$position] .= '<td></td>';
|
||||
$tablerows[$position] .= '<td class="linecolqty right">'.price(price2num((float) $TInfos['qty'], 'MS')).'</td>';
|
||||
$tablerows[$position] .= '<td>';
|
||||
$tablerows[$position] .= '</td>';
|
||||
$tablerows[$position] .= '<td class="linecolstock right">'.price2num($prod->stock_reel, 'MS').'</td>';
|
||||
$tablerows[$position] .= '<td class="linecoltheoricalstock right">'.$prod->stock_theorique.'</td>';
|
||||
$tablerows[$position] .= '</tr>';
|
||||
}
|
||||
}
|
||||
}
|
||||
// Sort the rows numeric by position string
|
||||
ksort($tablerows, SORT_NUMERIC);
|
||||
// Print the rows
|
||||
foreach ($tablerows as $position => $row) {
|
||||
print $row;
|
||||
}
|
||||
} else {
|
||||
foreach ($TChildBom as $fk_product => $elem) {
|
||||
$prod = new Product($db);
|
||||
|
||||
@@ -1558,7 +1558,7 @@ class BOM extends CommonObject
|
||||
/**
|
||||
* Get/add Net needs Tree by product or bom
|
||||
*
|
||||
* @param array<int,array{product:array,bom:BOM,parentid:int,qty:float,level:int,fk_unit:?int}> $TNetNeeds Array of ChildBom and infos linked to
|
||||
* @param array<int,array{product:array,bom:BOM,parentid:int,qty:float,level:int,position:int,fk_unit:?int}> $TNetNeeds Array of ChildBom and infos linked to
|
||||
* @param float $qty qty needed (used as a factor to produce 1 unit)
|
||||
* @param int<0,1000> $level level of recursivity
|
||||
* @return void
|
||||
@@ -1576,6 +1576,7 @@ class BOM extends CommonObject
|
||||
//$TNetNeeds[$childBom->id]['fk_unit'] = $line->fk_unit;
|
||||
$TNetNeeds[$childBom->id]['qty'] = $line->qty * $qty;
|
||||
$TNetNeeds[$childBom->id]['level'] = $level;
|
||||
$TNetNeeds[$childBom->id]['position'] = $line->position;
|
||||
$childBom->getNetNeedsTree($TNetNeeds, $line->qty * $qty / $childBom->qty, $level + 1);
|
||||
}
|
||||
} else {
|
||||
@@ -1593,6 +1594,7 @@ class BOM extends CommonObject
|
||||
}
|
||||
$TNetNeeds[$this->id]['product'][$line->fk_product]['qty'] += $line->qty * $qty;
|
||||
$TNetNeeds[$this->id]['product'][$line->fk_product]['level'] = $level;
|
||||
$TNetNeeds[$this->id]['product'][$line->fk_product]['position'] = $line->position;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -425,7 +425,6 @@ while ($i < $imaxinloop) {
|
||||
print img_picto('', 'url', 'class="pictofixedwidth"');
|
||||
print '<a class="" href="'.$obj->url.'"'.($obj->target ? ' target="newlink" rel="noopener"' : '').'>';
|
||||
} else {
|
||||
//print img_picto('', 'rightarrow', 'class="pictofixedwidth"');
|
||||
print '<a class="" href="'.$obj->url.'">';
|
||||
}
|
||||
print $link;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user