Merge branch 'master' into pr/ChristianSchindler/6305
This commit is contained in:
commit
7b88d0caa7
|
|
@ -1,5 +1,3 @@
|
|||
github: inventree
|
||||
ko_fi: inventree
|
||||
patreon: inventree
|
||||
polar: inventree
|
||||
github: inventree
|
||||
custom: [paypal.me/inventree]
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ tagged branch:
|
|||
|
||||
"""
|
||||
|
||||
import itertools
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
|
|
@ -198,10 +199,13 @@ if __name__ == '__main__':
|
|||
print(f"Version check passed for '{version}'!")
|
||||
print(f"Docker tags: '{docker_tags}'")
|
||||
|
||||
target_repos = [REPO.lower(), f'ghcr.io/{REPO.lower()}']
|
||||
|
||||
# Ref: https://getridbug.com/python/how-to-set-environment-variables-in-github-actions-using-python/
|
||||
with open(os.getenv('GITHUB_ENV'), 'a') as env_file:
|
||||
# Construct tag string
|
||||
tags = ','.join([f'{REPO.lower()}:{tag}' for tag in docker_tags])
|
||||
tag_list = [[f'{r}:{t}' for t in docker_tags] for r in target_repos]
|
||||
tags = ','.join(itertools.chain(*tag_list))
|
||||
|
||||
env_file.write(f'docker_tags={tags}\n')
|
||||
|
||||
|
|
|
|||
|
|
@ -127,17 +127,17 @@ jobs:
|
|||
uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # pin@v3.2.0
|
||||
- name: Set up Docker Buildx
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/setup-buildx-action@aa33708b10e362ff993539393ff100fa93ed6a27 # pin@v3.5.0
|
||||
uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # pin@v3.6.1
|
||||
- name: Set up cosign
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 # pin@v3.5.0
|
||||
uses: sigstore/cosign-installer@4959ce089c160fddf62f7b42464195ba1a56d382 # pin@v3.6.0
|
||||
- name: Check if Dockerhub login is required
|
||||
id: docker_login
|
||||
run: |
|
||||
if [ -z "${{ secrets.DOCKER_USERNAME }}" ]; then
|
||||
echo "skip_dockerhub_login=true" >> $GITHUB_ENV
|
||||
echo "skip_dockerhub_login=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "skip_dockerhub_login=false" >> $GITHUB_ENV
|
||||
echo "skip_dockerhub_login=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
- name: Login to Dockerhub
|
||||
if: github.event_name != 'pull_request' && steps.docker_login.outputs.skip_dockerhub_login != 'true'
|
||||
|
|
@ -166,7 +166,7 @@ jobs:
|
|||
- name: Push Docker Images
|
||||
id: push-docker
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/build-push-action@5176d81f87c23d6fc96624dfdbcd9f3830bbe445 # pin@v6.5.0
|
||||
uses: docker/build-push-action@16ebe778df0e7752d2cfcbd924afdbbd89c1a755 # pin@v6.6.1
|
||||
with:
|
||||
context: .
|
||||
file: ./contrib/container/Dockerfile
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ jobs:
|
|||
- name: Export API Documentation
|
||||
run: invoke schema --ignore-warnings --filename src/backend/InvenTree/schema.yml
|
||||
- name: Upload schema
|
||||
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # pin@v4.3.4
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # pin@v4.3.6
|
||||
with:
|
||||
name: schema.yml
|
||||
path: src/backend/InvenTree/schema.yml
|
||||
|
|
@ -177,7 +177,7 @@ jobs:
|
|||
echo "Downloaded api.yaml"
|
||||
- name: Running OpenAPI Spec diff action
|
||||
id: breaking_changes
|
||||
uses: oasdiff/oasdiff-action/diff@205ce7e2c5ae1511e720cbd307cae79fd7d4a909 # pin@main
|
||||
uses: oasdiff/oasdiff-action/diff@a2ff6682b27d175162a74c09ace8771bd3d512f8 # pin@main
|
||||
with:
|
||||
base: 'api.yaml'
|
||||
revision: 'src/backend/InvenTree/schema.yml'
|
||||
|
|
@ -535,7 +535,7 @@ jobs:
|
|||
- name: Run Playwright tests
|
||||
id: tests
|
||||
run: cd src/frontend && npx nyc playwright test
|
||||
- uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # pin@v4
|
||||
- uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # pin@v4
|
||||
if: ${{ !cancelled() && steps.tests.outcome == 'failure' }}
|
||||
with:
|
||||
name: playwright-report
|
||||
|
|
@ -573,7 +573,7 @@ jobs:
|
|||
run: |
|
||||
cd src/backend/InvenTree/web/static
|
||||
zip -r frontend-build.zip web/ web/.vite
|
||||
- uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # pin@v4.3.4
|
||||
- uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # pin@v4.3.6
|
||||
with:
|
||||
name: frontend-build
|
||||
path: src/backend/InvenTree/web/static/web
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ jobs:
|
|||
persist-credentials: false
|
||||
|
||||
- name: "Run analysis"
|
||||
uses: ossf/scorecard-action@dc50aa9510b46c811795eb24b2f1ba02a914e534 # v2.3.3
|
||||
uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0
|
||||
with:
|
||||
results_file: results.sarif
|
||||
results_format: sarif
|
||||
|
|
@ -59,7 +59,7 @@ jobs:
|
|||
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
||||
# format to the repository Actions tab.
|
||||
- name: "Upload artifact"
|
||||
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
with:
|
||||
name: SARIF file
|
||||
path: results.sarif
|
||||
|
|
@ -67,6 +67,6 @@ jobs:
|
|||
|
||||
# Upload the results to GitHub's code scanning dashboard.
|
||||
- name: "Upload to code-scanning"
|
||||
uses: github/codeql-action/upload-sarif@2d790406f505036ef40ecba973cc774a50395aac # v3.25.13
|
||||
uses: github/codeql-action/upload-sarif@eb055d739abdc2e8de2e5f4ba1a8b246daa779aa # v3.26.0
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
|
|
|||
|
|
@ -111,3 +111,4 @@ InvenTree/web/static
|
|||
docs/schema.yml
|
||||
docs/docs/api/*.yml
|
||||
docs/docs/api/schema/*.yml
|
||||
inventree_settings.json
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
ARG base_image=python:3.11-alpine3.18
|
||||
FROM ${base_image} AS inventree_base
|
||||
ARG base_image
|
||||
|
||||
# Build arguments for this image
|
||||
ARG commit_tag=""
|
||||
|
|
@ -48,13 +49,18 @@ ENV INVENTREE_BACKGROUND_WORKERS="4"
|
|||
ENV INVENTREE_WEB_ADDR=0.0.0.0
|
||||
ENV INVENTREE_WEB_PORT=8000
|
||||
|
||||
LABEL org.label-schema.schema-version="1.0" \
|
||||
org.label-schema.build-date=${DATE} \
|
||||
org.label-schema.vendor="inventree" \
|
||||
org.label-schema.name="inventree/inventree" \
|
||||
org.label-schema.url="https://hub.docker.com/r/inventree/inventree" \
|
||||
org.label-schema.vcs-url="https://github.com/inventree/InvenTree.git" \
|
||||
org.label-schema.vcs-ref=${commit_tag}
|
||||
LABEL org.opencontainers.image.created=${DATE} \
|
||||
org.opencontainers.image.vendor="inventree" \
|
||||
org.opencontainers.image.title="InvenTree backend server" \
|
||||
org.opencontainers.image.description="InvenTree is the open-source inventory management system" \
|
||||
org.opencontainers.image.url="https://inventree.org" \
|
||||
org.opencontainers.image.documentation="https://docs.inventree.org" \
|
||||
org.opencontainers.image.source="https://github.com/inventree/InvenTree" \
|
||||
org.opencontainers.image.revision=${commit_hash} \
|
||||
org.opencontainers.image.licenses="MIT" \
|
||||
org.opencontainers.image.base.name="docker.io/library/${base_image}" \
|
||||
org.opencontainers.image.version=${commit_tag}
|
||||
|
||||
|
||||
# Install required system level packages
|
||||
RUN apk add --no-cache \
|
||||
|
|
@ -68,8 +74,9 @@ RUN apk add --no-cache \
|
|||
# MySQL / MariaDB client
|
||||
mariadb-client mariadb-connector-c \
|
||||
&& \
|
||||
# fonts
|
||||
apk --update --upgrade --no-cache add fontconfig ttf-freefont font-noto terminus-font && fc-cache -f
|
||||
# font support
|
||||
apk --update --upgrade --no-cache add fontconfig ttf-freefont font-terminus font-noto font-noto-cjk font-noto-extra \
|
||||
&& fc-cache -f
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Packages needed for CI/packages
|
||||
requests==2.32.3
|
||||
pyyaml==6.0.1
|
||||
pyyaml==6.0.2
|
||||
jc==1.25.3
|
||||
|
|
|
|||
|
|
@ -108,58 +108,60 @@ pygments==2.17.2 \
|
|||
--hash=sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c \
|
||||
--hash=sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367
|
||||
# via jc
|
||||
pyyaml==6.0.1 \
|
||||
--hash=sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5 \
|
||||
--hash=sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc \
|
||||
--hash=sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df \
|
||||
--hash=sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741 \
|
||||
--hash=sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206 \
|
||||
--hash=sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27 \
|
||||
--hash=sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595 \
|
||||
--hash=sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62 \
|
||||
--hash=sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98 \
|
||||
--hash=sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696 \
|
||||
--hash=sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290 \
|
||||
--hash=sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9 \
|
||||
--hash=sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d \
|
||||
--hash=sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6 \
|
||||
--hash=sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867 \
|
||||
--hash=sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47 \
|
||||
--hash=sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486 \
|
||||
--hash=sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6 \
|
||||
--hash=sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3 \
|
||||
--hash=sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007 \
|
||||
--hash=sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938 \
|
||||
--hash=sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0 \
|
||||
--hash=sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c \
|
||||
--hash=sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735 \
|
||||
--hash=sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d \
|
||||
--hash=sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28 \
|
||||
--hash=sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4 \
|
||||
--hash=sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba \
|
||||
--hash=sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8 \
|
||||
--hash=sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef \
|
||||
--hash=sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5 \
|
||||
--hash=sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd \
|
||||
--hash=sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3 \
|
||||
--hash=sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0 \
|
||||
--hash=sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515 \
|
||||
--hash=sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c \
|
||||
--hash=sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c \
|
||||
--hash=sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924 \
|
||||
--hash=sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34 \
|
||||
--hash=sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43 \
|
||||
--hash=sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859 \
|
||||
--hash=sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673 \
|
||||
--hash=sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54 \
|
||||
--hash=sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a \
|
||||
--hash=sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b \
|
||||
--hash=sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab \
|
||||
--hash=sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa \
|
||||
--hash=sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c \
|
||||
--hash=sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585 \
|
||||
--hash=sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d \
|
||||
--hash=sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f
|
||||
pyyaml==6.0.2 \
|
||||
--hash=sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff \
|
||||
--hash=sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48 \
|
||||
--hash=sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086 \
|
||||
--hash=sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e \
|
||||
--hash=sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133 \
|
||||
--hash=sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5 \
|
||||
--hash=sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484 \
|
||||
--hash=sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee \
|
||||
--hash=sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5 \
|
||||
--hash=sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68 \
|
||||
--hash=sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a \
|
||||
--hash=sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf \
|
||||
--hash=sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99 \
|
||||
--hash=sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8 \
|
||||
--hash=sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85 \
|
||||
--hash=sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19 \
|
||||
--hash=sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc \
|
||||
--hash=sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a \
|
||||
--hash=sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1 \
|
||||
--hash=sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317 \
|
||||
--hash=sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c \
|
||||
--hash=sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631 \
|
||||
--hash=sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d \
|
||||
--hash=sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652 \
|
||||
--hash=sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5 \
|
||||
--hash=sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e \
|
||||
--hash=sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b \
|
||||
--hash=sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8 \
|
||||
--hash=sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476 \
|
||||
--hash=sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706 \
|
||||
--hash=sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563 \
|
||||
--hash=sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237 \
|
||||
--hash=sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b \
|
||||
--hash=sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083 \
|
||||
--hash=sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180 \
|
||||
--hash=sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425 \
|
||||
--hash=sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e \
|
||||
--hash=sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f \
|
||||
--hash=sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725 \
|
||||
--hash=sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183 \
|
||||
--hash=sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab \
|
||||
--hash=sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774 \
|
||||
--hash=sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725 \
|
||||
--hash=sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e \
|
||||
--hash=sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5 \
|
||||
--hash=sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d \
|
||||
--hash=sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290 \
|
||||
--hash=sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44 \
|
||||
--hash=sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed \
|
||||
--hash=sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4 \
|
||||
--hash=sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba \
|
||||
--hash=sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12 \
|
||||
--hash=sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4
|
||||
# via -r contrib/dev_reqs/requirements.in
|
||||
requests==2.32.3 \
|
||||
--hash=sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760 \
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 56 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 84 KiB |
|
|
@ -265,18 +265,16 @@ Build orders may (optionally) have a target complete date specified. If this dat
|
|||
- Builds can be filtered by overdue status in the build list
|
||||
- Overdue builds will be displayed on the home page
|
||||
|
||||
## Build Order Restrictions
|
||||
## Build Order Settings
|
||||
|
||||
There are a number of optional restrictions which can be applied to build orders, which may be enabled or disabled in the system settings:
|
||||
The following [global settings](../settings/global.md) are available for adjusting the behavior of build orders:
|
||||
|
||||
### Require Active Part
|
||||
|
||||
If this option is enabled, build orders can only be created for parts which are marked as [Active](../part/part.md#active-parts).
|
||||
|
||||
### Require Locked Part
|
||||
|
||||
If this option is enabled, build orders can only be created for parts which are marked as [Locked](../part/part.md#locked-parts).
|
||||
|
||||
### Require Valid BOM
|
||||
|
||||
If this option is enabled, build orders can only be created for parts which have a valid [Bill of Materials](./bom.md) defined.
|
||||
| Name | Description | Default | Units |
|
||||
| ---- | ----------- | ------- | ----- |
|
||||
{{ globalsetting("BUILDORDER_REFERENCE_PATTERN") }}
|
||||
{{ globalsetting("BUILDORDER_REQUIRE_RESPONSIBLE") }}
|
||||
{{ globalsetting("BUILDORDER_REQUIRE_ACTIVE_PART") }}
|
||||
{{ globalsetting("BUILDORDER_REQUIRE_LOCKED_PART") }}
|
||||
{{ globalsetting("BUILDORDER_REQUIRE_VALID_BOM") }}
|
||||
{{ globalsetting("BUILDORDER_REQUIRE_CLOSED_CHILDS") }}
|
||||
{{ globalsetting("PREVENT_BUILD_COMPLETION_HAVING_INCOMPLETED_TESTS") }}
|
||||
|
|
|
|||
|
|
@ -139,3 +139,14 @@ This view can be accessed externally as an ICS calendar using a URL like the fol
|
|||
`http://inventree.example.org/api/order/calendar/purchase-order/calendar.ics`
|
||||
|
||||
by default, completed orders are not exported. These can be included by appending `?include_completed=True` to the URL.
|
||||
|
||||
## Purchase Order Settings
|
||||
|
||||
The following [global settings](../settings/global.md) are available for purchase orders:
|
||||
|
||||
| Name | Description | Default | Units |
|
||||
| ---- | ----------- | ------- | ----- |
|
||||
{{ globalsetting("PURCHASEORDER_REFERENCE_PATTERN") }}
|
||||
{{ globalsetting("PURCHASEORDER_REQUIRE_RESPONSIBLE") }}
|
||||
{{ globalsetting("PURCHASEORDER_EDIT_COMPLETED_ORDERS") }}
|
||||
{{ globalsetting("PURCHASEORDER_AUTO_COMPLETE") }}
|
||||
|
|
|
|||
|
|
@ -121,3 +121,14 @@ This view can be accessed externally as an ICS calendar using a URL like the fol
|
|||
`http://inventree.example.org/api/order/calendar/return-order/calendar.ics`
|
||||
|
||||
by default, completed orders are not exported. These can be included by appending `?include_completed=True` to the URL.
|
||||
|
||||
## Return Order Settings
|
||||
|
||||
The following [global settings](../settings/global.md) are available for return orders:
|
||||
|
||||
| Name | Description | Default | Units |
|
||||
| ---- | ----------- | ------- | ----- |
|
||||
{{ globalsetting("RETURNORDER_ENABLED") }}
|
||||
{{ globalsetting("RETURNORDER_REFERENCE_PATTERN") }}
|
||||
{{ globalsetting("RETURNORDER_REQUIRE_RESPONSIBLE") }}
|
||||
{{ globalsetting("RETURNORDER_EDIT_COMPLETED_ORDERS") }}
|
||||
|
|
|
|||
|
|
@ -183,3 +183,15 @@ All these fields can be edited by the user:
|
|||
{% with id="edit-shipment", url="order/edit_shipment.png", description="Edit shipment" %}
|
||||
{% include "img.html" %}
|
||||
{% endwith %}
|
||||
|
||||
## Sales Order Settings
|
||||
|
||||
The following [global settings](../settings/global.md) are available for sales orders:
|
||||
|
||||
| Name | Description | Default | Units |
|
||||
| ---- | ----------- | ------- | ----- |
|
||||
{{ globalsetting("SALESORDER_REFERENCE_PATTERN") }}
|
||||
{{ globalsetting("SALESORDER_REQUIRE_RESPONSIBLE") }}
|
||||
{{ globalsetting("SALESORDER_DEFAULT_SHIPMENT") }}
|
||||
{{ globalsetting("SALESORDER_EDIT_COMPLETED_ORDERS") }}
|
||||
{{ globalsetting("SALESORDER_SHIP_COMPLETE") }}
|
||||
|
|
|
|||
|
|
@ -47,6 +47,10 @@ If a part is designated as an *Assembly* it can be created (or built) from other
|
|||
|
||||
If a part is designated as a *Component* it can be used as a sub-component of an *Assembly*. [Read further information about BOM management here](../build/bom.md)
|
||||
|
||||
### Testable
|
||||
|
||||
Testable parts can have test templates defined against the part, allowing test results to be recorded against any stock items for that part. For more information on testing, refer to the [testing documentation](./test.md).
|
||||
|
||||
### Trackable
|
||||
|
||||
Trackable parts can be assigned batch numbers or serial numbers which uniquely identify a particular stock item. Trackable parts also provide other features (and restrictions) in the InvenTree ecosystem.
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ title: Part Test Templates
|
|||
|
||||
## Part Test Templates
|
||||
|
||||
Parts which are designated as *trackable* (meaning they can be uniquely serialized) can define templates for tests which are to be performed against individual stock items corresponding to the part.
|
||||
Parts which are designated as [testable](./part.md#testable) can define templates for tests which are to be performed against individual stock items corresponding to the part.
|
||||
|
||||
A test template defines the parameters of the test; the individual stock items can then have associated test results which correspond to a test template.
|
||||
|
||||
|
|
|
|||
|
|
@ -131,9 +131,9 @@ The *Scheduling* tab provides an overview of the *predicted* future availability
|
|||
|
||||
The *Stocktake* tab provide historical stock level information, based on user-provided stocktake data. Refer to the [stocktake documentation](./stocktake.md) for further information.
|
||||
|
||||
### Tests
|
||||
### Test Templates
|
||||
|
||||
If a part is marked as *trackable*, the user can define tests which must be performed on any stock items which are instances of this part. [Read more about testing](./test.md).
|
||||
If a part is marked as *testable*, the user can define tests which must be performed on any stock items which are instances of this part. [Read more about testing](./test.md).
|
||||
|
||||
### Related Parts
|
||||
|
||||
|
|
|
|||
|
|
@ -24,12 +24,7 @@ If a different currency exchange backend is needed, or a custom implementation i
|
|||
|
||||
### Currency Settings
|
||||
|
||||
In the [settings screen](./global.md), under the *Pricing* section, the following currency settings are available:
|
||||
|
||||
| Setting | Description | Default Value |
|
||||
| --- | --- |
|
||||
| Default Currency | The selected *default* currency for the system. | USD |
|
||||
| Supported Currencies | The list of supported currencies for the system. | AUD, CAD, CNY, EUR, GBP, JPY, NZD, USD |
|
||||
Refer to the [global settings](./global.md#pricing-and-currency) documentation for more information on available currency settings.
|
||||
|
||||
#### Supported Currencies
|
||||
|
||||
|
|
|
|||
|
|
@ -17,120 +17,139 @@ Global settings are arranged in the following categories:
|
|||
|
||||
### Server Settings
|
||||
|
||||
Configuration of basic server settings.
|
||||
Configuration of basic server settings:
|
||||
|
||||
| Name | Description | Default | Units |
|
||||
| ---- | ----------- | ------- | ----- |
|
||||
{{ globalsetting("INVENTREE_BASE_URL") }}
|
||||
{{ globalsetting("INVENTREE_COMPANY_NAME") }}
|
||||
{{ globalsetting("INVENTREE_INSTANCE") }}
|
||||
{{ globalsetting("INVENTREE_INSTANCE_TITLE") }}
|
||||
{{ globalsetting("INVENTREE_RESTRICT_ABOUT") }}
|
||||
{{ globalsetting("DISPLAY_FULL_NAMES") }}
|
||||
{{ globalsetting("INVENTREE_UPDATE_CHECK_INTERVAL") }}
|
||||
{{ globalsetting("INVENTREE_DOWNLOAD_FROM_URL") }}
|
||||
{{ globalsetting("INVENTREE_DOWNLOAD_IMAGE_MAX_SIZE") }}
|
||||
{{ globalsetting("INVENTREE_DOWNLOAD_FROM_URL_USER_AGENT") }}
|
||||
{{ globalsetting("INVENTREE_REQUIRE_CONFIRM") }}
|
||||
{{ globalsetting("INVENTREE_STRICT_URLS") }}
|
||||
{{ globalsetting("INVENTREE_TREE_DEPTH") }}
|
||||
{{ globalsetting("INVENTREE_BACKUP_ENABLE") }}
|
||||
{{ globalsetting("INVENTREE_BACKUP_DAYS") }}
|
||||
{{ globalsetting("INVENTREE_DELETE_TASKS_DAYS") }}
|
||||
{{ globalsetting("INVENTREE_DELETE_ERRORS_DAYS") }}
|
||||
{{ globalsetting("INVENTREE_DELETE_NOTIFICATIONS_DAYS") }}
|
||||
|
||||
| Setting | Type | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| InvenTree Instance Name | String | String descriptor for the InvenTree server instance | InvenTree Server |
|
||||
| Use Instance Name | Boolean | Use instance name in title bars | False |
|
||||
| Restrict showing `about` | Boolean | Show the `about` modal only to superusers | False |
|
||||
| Base URL | String | Base URL for server instance | *blank* |
|
||||
| Company Name | String | Company name | My company name |
|
||||
| Download from URL | Boolean | Allow downloading of images from remote URLs | False |
|
||||
|
||||
### Login Settings
|
||||
|
||||
Change how logins, password-forgot, signups are handled.
|
||||
Change how logins, password-forgot, signups are handled:
|
||||
|
||||
| Setting | Type | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| Enable registration | Boolean | Enable self-registration for users on the login-pages | False |
|
||||
| Enable SSO | Boolean | Enable SSO on the login-pages | False |
|
||||
| Enable SSO registration | Boolean | Enable self-registration for users via SSO on the login-pages | False |
|
||||
| Enable SSO group sync | Boolean | Enable synchronizing InvenTree groups directly from the IdP | False |
|
||||
| SSO group key | String | The name of the groups claim attribute provided by the IdP | |
|
||||
| SSO group map | String (JSON) | A mapping from SSO groups to local InvenTree groups | {} |
|
||||
| Remove groups outside of SSO | Boolean | Whether groups assigned to the user should be removed if they are not backend by the IdP. Disabling this setting might cause security issues | True |
|
||||
| Enable password forgot | Boolean | Enable password forgot function on the login-pages.<br><br>This will let users reset their passwords on their own. For this feature to work you need to configure E-mail | True |
|
||||
| E-Mail required | Boolean | Require user to supply e-mail on signup.<br><br>Without a way (e-mail) to contact the user notifications and security features might not work! | False |
|
||||
| Enforce MFA | Boolean | Users must use multifactor security.<br><br>This forces each user to setup MFA and use it on each authentication | False |
|
||||
| Mail twice | Boolean | On signup ask users twice for their mail | False |
|
||||
| Password twice | Boolean | On signup ask users twice for their password | True |
|
||||
| Auto-fill SSO users | Boolean | Automatically fill out user-details from SSO account-data.<br><br>If this feature is enabled the user is only asked for their username, first- and surname if those values can not be gathered from their SSO profile. This might lead to unwanted usernames bleeding over. | True |
|
||||
| Allowed domains | String | Restrict signup to certain domains (comma-separated, starting with @) | |
|
||||
| Name | Description | Default | Units |
|
||||
| ---- | ----------- | ------- | ----- |
|
||||
{{ globalsetting("LOGIN_ENABLE_PWD_FORGOT") }}
|
||||
{{ globalsetting("LOGIN_MAIL_REQUIRED") }}
|
||||
{{ globalsetting("LOGIN_ENFORCE_MFA") }}
|
||||
{{ globalsetting("LOGIN_ENABLE_REG") }}
|
||||
{{ globalsetting("LOGIN_SIGNUP_MAIL_TWICE") }}
|
||||
{{ globalsetting("LOGIN_SIGNUP_PWD_TWICE") }}
|
||||
{{ globalsetting("SIGNUP_GROUP") }}
|
||||
{{ globalsetting("LOGIN_SIGNUP_MAIL_RESTRICTION") }}
|
||||
{{ globalsetting("LOGIN_ENABLE_SSO") }}
|
||||
{{ globalsetting("LOGIN_ENABLE_SSO_REG") }}
|
||||
{{ globalsetting("LOGIN_SIGNUP_SSO_AUTO") }}
|
||||
{{ globalsetting("LOGIN_ENABLE_SSO_GROUP_SYNC") }}
|
||||
{{ globalsetting("SSO_GROUP_MAP") }}
|
||||
{{ globalsetting("SSO_GROUP_KEY") }}
|
||||
{{ globalsetting("SSO_REMOVE_GROUPS") }}
|
||||
|
||||
#### Require User Email
|
||||
|
||||
If this setting is enabled, users must provide an email address when signing up. Note that some notification and security features require a valid email address.
|
||||
|
||||
#### Forgot Password
|
||||
|
||||
If this setting is enabled, users can reset their password via email. This requires a valid email address to be associated with the user account.
|
||||
|
||||
#### Enforce Multi-Factor Authentication
|
||||
|
||||
If this setting is enabled, users must have multi-factor authentication enabled to log in.
|
||||
|
||||
#### Auto Fil SSO Users
|
||||
|
||||
Automatically fill out user-details from SSO account-data. If this feature is enabled the user is only asked for their username, first- and surname if those values can not be gathered from their SSO profile. This might lead to unwanted usernames bleeding over.
|
||||
|
||||
### Barcodes
|
||||
|
||||
Configuration of barcode functionality
|
||||
Configuration of barcode functionality:
|
||||
|
||||
| Setting | Type | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| Barcode Support | Boolean | Enable barcode functionality in web interface | True |
|
||||
| Name | Description | Default | Units |
|
||||
| ---- | ----------- | ------- | ----- |
|
||||
{{ globalsetting("BARCODE_ENABLE") }}
|
||||
{{ globalsetting("BARCODE_INPUT_DELAY") }}
|
||||
{{ globalsetting("BARCODE_WEBCAM_SUPPORT") }}
|
||||
{{ globalsetting("BARCODE_SHOW_TEXT") }}
|
||||
{{ globalsetting("BARCODE_GENERATION_PLUGIN") }}
|
||||
|
||||
### Currencies
|
||||
### Pricing and Currency
|
||||
|
||||
Configuration of currency support
|
||||
Configuration of pricing data and currency support:
|
||||
|
||||
| Setting | Type | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| Default Currency | Currency | Default currency | USD |
|
||||
| Name | Description | Default | Units |
|
||||
| ---- | ----------- | ------- | ----- |
|
||||
{{ globalsetting("INVENTREE_DEFAULT_CURRENCY") }}
|
||||
{{ globalsetting("CURRENCY_CODES") }}
|
||||
{{ globalsetting("PART_INTERNAL_PRICE") }}
|
||||
{{ globalsetting("PART_BOM_USE_INTERNAL_PRICE") }}
|
||||
{{ globalsetting("PRICING_DECIMAL_PLACES_MIN") }}
|
||||
{{ globalsetting("PRICING_DECIMAL_PLACES") }}
|
||||
{{ globalsetting("PRICING_UPDATE_DAYS") }}
|
||||
{{ globalsetting("PRICING_USE_SUPPLIER_PRICING") }}
|
||||
{{ globalsetting("PRICING_PURCHASE_HISTORY_OVERRIDES_SUPPLIER") }}
|
||||
{{ globalsetting("PRICING_USE_STOCK_PRICING") }}
|
||||
{{ globalsetting("PRICING_STOCK_ITEM_AGE_DAYS") }}
|
||||
{{ globalsetting("PRICING_USE_VARIANT_PRICING") }}
|
||||
{{ globalsetting("PRICING_ACTIVE_VARIANTS") }}
|
||||
|
||||
### Reporting
|
||||
|
||||
Configuration of report generation
|
||||
Configuration of report generation:
|
||||
|
||||
| Setting | Type | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| Enable Reports | Boolean | Enable report generation | False |
|
||||
| Page Size | String | Default page size | A4 |
|
||||
| Debug Mode | Boolean | Generate reports in debug mode (HTML output) | False |
|
||||
| Test Reports | Boolean | Enable generation of test reports | False |
|
||||
| Name | Description | Default | Units |
|
||||
| ---- | ----------- | ------- | ----- |
|
||||
{{ globalsetting("REPORT_ENABLE") }}
|
||||
{{ globalsetting("REPORT_DEFAULT_PAGE_SIZE") }}
|
||||
{{ globalsetting("REPORT_DEBUG_MODE") }}
|
||||
{{ globalsetting("REPORT_LOG_ERRORS") }}
|
||||
{{ globalsetting("REPORT_ENABLE_TEST_REPORT") }}
|
||||
{{ globalsetting("REPORT_ATTACH_TEST_REPORT") }}
|
||||
|
||||
### Parts
|
||||
|
||||
#### Main Settings
|
||||
|
||||
| Setting | Type | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| IPN Regex | String | Regular expression pattern for matching Part IPN | *blank* |
|
||||
| Allow Duplicate IPN | Boolean | Allow multiple parts to share the same IPN | True |
|
||||
| Allow Editing IPN | Boolean | Allow changing the IPN value while editing a part | True |
|
||||
| Part Name Display Format | String | Format to display the part name | {% raw %}`{{ part.id if part.id }}{{ ' | ' if part.id }}{{ part.name }}{{ ' | ' if part.revision }}{{ part.revision if part.revision }}`{% endraw %} |
|
||||
| Show Price History | Boolean | Display historical pricing for Part | False |
|
||||
| Show Price in Forms | Boolean | Display part price in some forms | True |
|
||||
| Show Price in BOM | Boolean | Include pricing information in BOM tables | True |
|
||||
| Show related parts | Boolean | Display related parts for a part | True |
|
||||
| Create initial stock | Boolean | Create initial stock on part creation | True |
|
||||
|
||||
#### Creation Settings
|
||||
|
||||
| Setting | Type | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| Template | Boolean | Parts are templates by default | False |
|
||||
| Assembly | Boolean | Parts can be assembled from other components by default | False |
|
||||
| Component | Boolean | Parts can be used as sub-components by default | True |
|
||||
| Trackable | Boolean | Parts are trackable by default | False |
|
||||
| Purchaseable | Boolean | Parts are purchaseable by default | True |
|
||||
| Salable | Boolean | Parts are salable by default | False |
|
||||
| Virtual | Boolean | Parts are virtual by default | False |
|
||||
|
||||
#### Copy Settings
|
||||
|
||||
| Setting | Type | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| Copy Part BOM Data | Boolean | Copy BOM data by default when duplicating a part | True |
|
||||
| Copy Part Parameter Data | Boolean | Copy parameter data by default when duplicating a part | True |
|
||||
| Copy Part Test Data | Boolean | Copy test data by default when duplicating a part | True |
|
||||
| Copy Category Parameter Templates | Boolean | Copy category parameter templates when creating a part | True |
|
||||
|
||||
#### Internal Price Settings
|
||||
|
||||
| Setting | Type | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| Internal Prices | Boolean | Enable internal prices for parts | False |
|
||||
| Internal Price as BOM-Price | Boolean | Use the internal price (if set) in BOM-price calculations | False |
|
||||
|
||||
#### Part Import Setting
|
||||
|
||||
This section of the part settings allows staff users to:
|
||||
|
||||
- import parts to InvenTree clicking the <span class="badge inventree add"><span class='fas fa-plus-circle'></span> Import Part</span> button
|
||||
- enable the ["Import Parts" tab in the part category view](../part/part.md#part-import).
|
||||
|
||||
| Setting | Type | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| Show Import in Views | Boolean | Display the import wizard in some part views | True |
|
||||
| Name | Description | Default | Units |
|
||||
| ---- | ----------- | ------- | ----- |
|
||||
{{ globalsetting("PART_IPN_REGEX") }}
|
||||
{{ globalsetting("PART_ALLOW_DUPLICATE_IPN") }}
|
||||
{{ globalsetting("PART_ALLOW_EDIT_IPN") }}
|
||||
{{ globalsetting("PART_ALLOW_DELETE_FROM_ASSEMBLY") }}
|
||||
{{ globalsetting("PART_ENABLE_REVISION") }}
|
||||
{{ globalsetting("PART_REVISION_ASSEMBLY_ONLY") }}
|
||||
{{ globalsetting("PART_NAME_FORMAT") }}
|
||||
{{ globalsetting("PART_SHOW_RELATED") }}
|
||||
{{ globalsetting("PART_CREATE_INITIAL") }}
|
||||
{{ globalsetting("PART_CREATE_SUPPLIER") }}
|
||||
{{ globalsetting("PART_TEMPLATE") }}
|
||||
{{ globalsetting("PART_ASSEMBLY") }}
|
||||
{{ globalsetting("PART_COMPONENT") }}
|
||||
{{ globalsetting("PART_TRACKABLE") }}
|
||||
{{ globalsetting("PART_PURCHASEABLE") }}
|
||||
{{ globalsetting("PART_SALABLE") }}
|
||||
{{ globalsetting("PART_VIRTUAL") }}
|
||||
{{ globalsetting("PART_COPY_BOM") }}
|
||||
{{ globalsetting("PART_COPY_PARAMETERS") }}
|
||||
{{ globalsetting("PART_COPY_TESTS") }}
|
||||
{{ globalsetting("PART_CATEGORY_PARAMETERS") }}
|
||||
{{ globalsetting("PART_CATEGORY_DEFAULT_ICON") }}
|
||||
|
||||
#### Part Parameter Templates
|
||||
|
||||
|
|
@ -153,45 +172,48 @@ After a list of parameters is added to a part category and upon creation of a ne
|
|||
|
||||
Configuration of stock item options
|
||||
|
||||
| Setting | Type | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| Stock Expiry | Boolean | Enable stock expiry functionality | False |
|
||||
| Stock Stale Time | Days | Number of days stock items are considered stale before expiring | 90 |
|
||||
| Sell Expired Stock | Boolean | Allow sale of expired stock | False |
|
||||
| Build Expired Stock | Boolean | Allow building with expired stock | False |
|
||||
| Stock Ownership Control | Boolean | Enable ownership control functionality | False |
|
||||
| Name | Description | Default | Units |
|
||||
| ---- | ----------- | ------- | ----- |
|
||||
{{ globalsetting("SERIAL_NUMBER_GLOBALLY_UNIQUE") }}
|
||||
{{ globalsetting("SERIAL_NUMBER_AUTOFILL") }}
|
||||
{{ globalsetting("STOCK_DELETE_DEPLETED_DEFAULT") }}
|
||||
{{ globalsetting("STOCK_BATCH_CODE_TEMPLATE") }}
|
||||
{{ globalsetting("STOCK_ENABLE_EXPIRY") }}
|
||||
{{ globalsetting("STOCK_STALE_DAYS") }}
|
||||
{{ globalsetting("STOCK_ALLOW_EXPIRED_SALE") }}
|
||||
{{ globalsetting("STOCK_ALLOW_EXPIRED_BUILD") }}
|
||||
{{ globalsetting("STOCK_OWNERSHIP_CONTROL") }}
|
||||
{{ globalsetting("STOCK_LOCATION_DEFAULT_ICON") }}
|
||||
{{ globalsetting("STOCK_SHOW_INSTALLED_ITEMS") }}
|
||||
{{ globalsetting("STOCK_ENFORCE_BOM_INSTALLATION") }}
|
||||
{{ globalsetting("STOCK_ALLOW_OUT_OF_STOCK_TRANSFER") }}
|
||||
{{ globalsetting("TEST_STATION_DATA") }}
|
||||
|
||||
### Build Orders
|
||||
|
||||
Options for build orders
|
||||
|
||||
| Setting | Type | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| Reference Pattern | String | Pattern for defining Build Order reference values | {% raw %}BO-{ref:04d}{% endraw %} |
|
||||
Refer to the [build order settings](../build/build.md#build-order-settings).
|
||||
|
||||
### Purchase Orders
|
||||
|
||||
Options for purchase orders
|
||||
Refer to the [purchase order settings](../order/purchase_order.md#purchase-order-settings).
|
||||
|
||||
| Setting | Type | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| Reference Pattern | String | Pattern for defining Purchase Order reference values | {% raw %}PO-{ref:04d}{% endraw %} |
|
||||
### Sales Orders
|
||||
|
||||
### Sales orders
|
||||
Refer to the [sales order settings](../order/sales_order.md#sales-order-settings).
|
||||
|
||||
Options for sales orders
|
||||
### Return Orders
|
||||
|
||||
| Setting | Type | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| Reference Pattern | String | Pattern for defining Sales Order reference values | {% raw %}SO-{ref:04d}{% endraw %} |
|
||||
Refer to the [return order settings](../order/return_order.md#return-order-settings).
|
||||
|
||||
### Plugin Settings
|
||||
|
||||
Change into what parts plugins can integrate into.
|
||||
|
||||
| Setting | Type | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| Enable URL integration | Boolean | Enable plugins to add URL routes | False |
|
||||
| Enable navigation integration | Boolean | Enable plugins to integrate into navigation | False |
|
||||
| Enable setting integration | Boolean | Enable plugins to integrate into inventree settings | False |
|
||||
| Enable app integration | Boolean | Enable plugins to add apps | False |
|
||||
| Name | Description | Default | Units |
|
||||
| ---- | ----------- | ------- | ----- |
|
||||
{{ globalsetting("PLUGIN_ON_STARTUP") }}
|
||||
{{ globalsetting("PLUGIN_UPDATE_CHECK") }}
|
||||
{{ globalsetting("ENABLE_PLUGINS_URL") }}
|
||||
{{ globalsetting("ENABLE_PLUGINS_NAVIGATION") }}
|
||||
{{ globalsetting("ENABLE_PLUGINS_APP") }}
|
||||
{{ globalsetting("ENABLE_PLUGINS_SCHEDULE") }}
|
||||
{{ globalsetting("ENABLE_PLUGINS_EVENTS") }}
|
||||
|
|
|
|||
|
|
@ -32,24 +32,44 @@ This screen allows the user to customize display of items on the InvenTree home
|
|||
|
||||
### Search Settings
|
||||
|
||||
Customize settings for search results
|
||||
Customize settings for search results:
|
||||
|
||||
{% with id="user-search", url="settings/user_search.png", description="User Search Settings" %}
|
||||
{% include 'img.html' %}
|
||||
{% endwith %}
|
||||
| Name | Description | Default | Units |
|
||||
| ---- | ----------- | ------- | ----- |
|
||||
{{ usersetting("SEARCH_WHOLE") }}
|
||||
{{ usersetting("SEARCH_REGEX") }}
|
||||
{{ usersetting("SEARCH_PREVIEW_RESULTS") }}
|
||||
{{ usersetting("SEARCH_PREVIEW_SHOW_PARTS") }}
|
||||
{{ usersetting("SEARCH_HIDE_INACTIVE_PARTS") }}
|
||||
{{ usersetting("SEARCH_PREVIEW_SHOW_SUPPLIER_PARTS") }}
|
||||
{{ usersetting("SEARCH_PREVIEW_SHOW_MANUFACTURER_PARTS") }}
|
||||
{{ usersetting("SEARCH_PREVIEW_SHOW_CATEGORIES") }}
|
||||
{{ usersetting("SEARCH_PREVIEW_SHOW_STOCK") }}
|
||||
{{ usersetting("SEARCH_PREVIEW_HIDE_UNAVAILABLE_STOCK") }}
|
||||
{{ usersetting("SEARCH_PREVIEW_SHOW_LOCATIONS") }}
|
||||
{{ usersetting("SEARCH_PREVIEW_SHOW_COMPANIES") }}
|
||||
{{ usersetting("SEARCH_PREVIEW_SHOW_BUILD_ORDERS") }}
|
||||
{{ usersetting("SEARCH_PREVIEW_SHOW_PURCHASE_ORDERS") }}
|
||||
{{ usersetting("SEARCH_PREVIEW_EXCLUDE_INACTIVE_PURCHASE_ORDERS") }}
|
||||
{{ usersetting("SEARCH_PREVIEW_SHOW_SALES_ORDERS") }}
|
||||
{{ usersetting("SEARCH_PREVIEW_EXCLUDE_INACTIVE_SALES_ORDERS") }}
|
||||
{{ usersetting("SEARCH_PREVIEW_SHOW_RETURN_ORDERS") }}
|
||||
{{ usersetting("SEARCH_PREVIEW_EXCLUDE_INACTIVE_RETURN_ORDERS") }}
|
||||
|
||||
### Notifications
|
||||
|
||||
Settings related to notification messages
|
||||
Settings related to notification messages:
|
||||
|
||||
{% with id="user-notification", url="settings/user_notifications.png", description="User Notification Settings" %}
|
||||
{% include 'img.html' %}
|
||||
{% endwith %}
|
||||
| Name | Description | Default | Units |
|
||||
| ---- | ----------- | ------- | ----- |
|
||||
{{ usersetting("NOTIFICATION_ERROR_REPORT") }}
|
||||
|
||||
### Reporting
|
||||
|
||||
Settings for label printing and report generation
|
||||
Settings for label printing and report generation:
|
||||
|
||||
{% with id="user-reporting", url="settings/user_reporting.png", description="User Reporting Settings" %}
|
||||
{% include 'img.html' %}
|
||||
{% endwith %}
|
||||
| Name | Description | Default | Units |
|
||||
| ---- | ----------- | ------- | ----- |
|
||||
{{ usersetting("REPORT_INLINE") }}
|
||||
{{ usersetting("LABEL_INLINE") }}
|
||||
{{ usersetting("LABEL_DEFAULT_PRINTER") }}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ title: Stock Test Result
|
|||
|
||||
## Stock Test Result
|
||||
|
||||
Stock items which are associated with a *trackable* part can have associated test data - this is particularly useful for tracking unit testing / commissioning / acceptance data against a serialized stock item.
|
||||
Stock items which are associated with a [testable part](../part/part.md#testable) can have associated test data - this is particularly useful for tracking unit testing / commissioning / acceptance data against a serialized stock item.
|
||||
|
||||
The master "Part" record for the stock item can define multiple [test templates](../part/test.md), against which test data can be uploaded. Additionally, arbitrary test information can be assigned to the stock item.
|
||||
|
||||
|
|
|
|||
49
docs/main.py
49
docs/main.py
|
|
@ -1,5 +1,6 @@
|
|||
"""Main entry point for the documentation build process."""
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import textwrap
|
||||
|
|
@ -7,6 +8,20 @@ import textwrap
|
|||
import requests
|
||||
import yaml
|
||||
|
||||
# Cached settings dict values
|
||||
global GLOBAL_SETTINGS
|
||||
global USER_SETTINGS
|
||||
|
||||
# Read in the InvenTree settings file
|
||||
here = os.path.dirname(__file__)
|
||||
settings_file = os.path.join(here, 'inventree_settings.json')
|
||||
|
||||
with open(settings_file, 'r') as sf:
|
||||
settings = json.load(sf)
|
||||
|
||||
GLOBAL_SETTINGS = settings['global']
|
||||
USER_SETTINGS = settings['user']
|
||||
|
||||
|
||||
def get_repo_url(raw=False):
|
||||
"""Return the repository URL for the current project."""
|
||||
|
|
@ -219,3 +234,37 @@ def define_env(env):
|
|||
)
|
||||
|
||||
return includefile(fn, f'Template: {base}', format='html')
|
||||
|
||||
@env.macro
|
||||
def rendersetting(setting: dict):
|
||||
"""Render a provided setting object into a table row."""
|
||||
name = setting['name']
|
||||
description = setting['description']
|
||||
default = setting.get('default', None)
|
||||
units = setting.get('units', None)
|
||||
|
||||
return f'| {name} | {description} | {default if default is not None else ""} | {units if units is not None else ""} |'
|
||||
|
||||
@env.macro
|
||||
def globalsetting(key: str):
|
||||
"""Extract information on a particular global setting.
|
||||
|
||||
Arguments:
|
||||
- key: The name of the global setting to extract information for.
|
||||
"""
|
||||
global GLOBAL_SETTINGS
|
||||
setting = GLOBAL_SETTINGS[key]
|
||||
|
||||
return rendersetting(setting)
|
||||
|
||||
@env.macro
|
||||
def usersetting(key: str):
|
||||
"""Extract information on a particular user setting.
|
||||
|
||||
Arguments:
|
||||
- key: The name of the user setting to extract information for.
|
||||
"""
|
||||
global USER_SETTINGS
|
||||
setting = USER_SETTINGS[key]
|
||||
|
||||
return rendersetting(setting)
|
||||
|
|
|
|||
|
|
@ -305,9 +305,9 @@ mkdocs-git-revision-date-localized-plugin==1.2.6 \
|
|||
--hash=sha256:e432942ce4ee8aa9b9f4493e993dee9d2cc08b3ea2b40a3d6b03ca0f2a4bcaa2 \
|
||||
--hash=sha256:f015cb0f3894a39b33447b18e270ae391c4e25275cac5a626e80b243784e2692
|
||||
# via -r docs/requirements.in
|
||||
mkdocs-include-markdown-plugin==6.2.1 \
|
||||
--hash=sha256:46fc372886d48eec541d36138d1fe1db42afd08b976ef7c8d8d4ea6ee4d5d1e8 \
|
||||
--hash=sha256:8dfc3aee9435679b094cbdff023239e91d86cf357c40b0e99c28036449661830
|
||||
mkdocs-include-markdown-plugin==6.2.2 \
|
||||
--hash=sha256:d293950f6499d2944291ca7b9bc4a60e652bbfd3e3a42b564f6cceee268694e7 \
|
||||
--hash=sha256:f2bd5026650492a581d2fd44be6c22f90391910d76582b96a34c264f2d17875d
|
||||
# via -r docs/requirements.in
|
||||
mkdocs-macros-plugin==1.0.5 \
|
||||
--hash=sha256:f60e26f711f5a830ddf1e7980865bf5c0f1180db56109803cdd280073c1a050a \
|
||||
|
|
@ -335,9 +335,9 @@ mkdocstrings-python==1.10.2 \
|
|||
--hash=sha256:38a4fd41953defb458a107033440c229c7e9f98f35a24e84d888789c97da5a63 \
|
||||
--hash=sha256:e8e596b37f45c09b67bec253e035fe18988af5bbbbf44e0ccd711742eed750e5
|
||||
# via mkdocstrings
|
||||
neoteroi-mkdocs==1.0.5 \
|
||||
--hash=sha256:1f3b372dee79269157361733c0f45b3a89189077078e0e3224d829a144ef3579 \
|
||||
--hash=sha256:29875ef444b08aec5619a384142e16f1b4e851465cab4e380fb2b8ae730fe046
|
||||
neoteroi-mkdocs==1.1.0 \
|
||||
--hash=sha256:609aae655e781c7aec517ab14759c34ce896b8132d1df4b9c2e504779c2e48ef \
|
||||
--hash=sha256:9c59aebf83ca09d1d486bf8c0351e6ddfa912f09413d153ecabc5cd268a3155a
|
||||
# via -r docs/requirements.in
|
||||
packaging==24.0 \
|
||||
--hash=sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5 \
|
||||
|
|
@ -378,58 +378,60 @@ pytz==2024.1 \
|
|||
--hash=sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812 \
|
||||
--hash=sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319
|
||||
# via mkdocs-git-revision-date-localized-plugin
|
||||
pyyaml==6.0.1 \
|
||||
--hash=sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5 \
|
||||
--hash=sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc \
|
||||
--hash=sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df \
|
||||
--hash=sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741 \
|
||||
--hash=sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206 \
|
||||
--hash=sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27 \
|
||||
--hash=sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595 \
|
||||
--hash=sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62 \
|
||||
--hash=sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98 \
|
||||
--hash=sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696 \
|
||||
--hash=sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290 \
|
||||
--hash=sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9 \
|
||||
--hash=sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d \
|
||||
--hash=sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6 \
|
||||
--hash=sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867 \
|
||||
--hash=sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47 \
|
||||
--hash=sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486 \
|
||||
--hash=sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6 \
|
||||
--hash=sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3 \
|
||||
--hash=sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007 \
|
||||
--hash=sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938 \
|
||||
--hash=sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0 \
|
||||
--hash=sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c \
|
||||
--hash=sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735 \
|
||||
--hash=sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d \
|
||||
--hash=sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28 \
|
||||
--hash=sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4 \
|
||||
--hash=sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba \
|
||||
--hash=sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8 \
|
||||
--hash=sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef \
|
||||
--hash=sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5 \
|
||||
--hash=sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd \
|
||||
--hash=sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3 \
|
||||
--hash=sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0 \
|
||||
--hash=sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515 \
|
||||
--hash=sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c \
|
||||
--hash=sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c \
|
||||
--hash=sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924 \
|
||||
--hash=sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34 \
|
||||
--hash=sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43 \
|
||||
--hash=sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859 \
|
||||
--hash=sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673 \
|
||||
--hash=sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54 \
|
||||
--hash=sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a \
|
||||
--hash=sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b \
|
||||
--hash=sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab \
|
||||
--hash=sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa \
|
||||
--hash=sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c \
|
||||
--hash=sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585 \
|
||||
--hash=sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d \
|
||||
--hash=sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f
|
||||
pyyaml==6.0.2 \
|
||||
--hash=sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff \
|
||||
--hash=sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48 \
|
||||
--hash=sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086 \
|
||||
--hash=sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e \
|
||||
--hash=sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133 \
|
||||
--hash=sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5 \
|
||||
--hash=sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484 \
|
||||
--hash=sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee \
|
||||
--hash=sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5 \
|
||||
--hash=sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68 \
|
||||
--hash=sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a \
|
||||
--hash=sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf \
|
||||
--hash=sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99 \
|
||||
--hash=sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8 \
|
||||
--hash=sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85 \
|
||||
--hash=sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19 \
|
||||
--hash=sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc \
|
||||
--hash=sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a \
|
||||
--hash=sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1 \
|
||||
--hash=sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317 \
|
||||
--hash=sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c \
|
||||
--hash=sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631 \
|
||||
--hash=sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d \
|
||||
--hash=sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652 \
|
||||
--hash=sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5 \
|
||||
--hash=sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e \
|
||||
--hash=sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b \
|
||||
--hash=sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8 \
|
||||
--hash=sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476 \
|
||||
--hash=sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706 \
|
||||
--hash=sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563 \
|
||||
--hash=sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237 \
|
||||
--hash=sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b \
|
||||
--hash=sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083 \
|
||||
--hash=sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180 \
|
||||
--hash=sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425 \
|
||||
--hash=sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e \
|
||||
--hash=sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f \
|
||||
--hash=sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725 \
|
||||
--hash=sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183 \
|
||||
--hash=sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab \
|
||||
--hash=sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774 \
|
||||
--hash=sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725 \
|
||||
--hash=sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e \
|
||||
--hash=sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5 \
|
||||
--hash=sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d \
|
||||
--hash=sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290 \
|
||||
--hash=sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44 \
|
||||
--hash=sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed \
|
||||
--hash=sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4 \
|
||||
--hash=sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba \
|
||||
--hash=sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12 \
|
||||
--hash=sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4
|
||||
# via
|
||||
# essentials-openapi
|
||||
# mkdocs
|
||||
|
|
@ -593,7 +595,7 @@ wcmatch==8.5.2 \
|
|||
--hash=sha256:17d3ad3758f9d0b5b4dedc770b65420d4dac62e680229c287bf24c9db856a478 \
|
||||
--hash=sha256:a70222b86dea82fb382dd87b73278c10756c138bd6f8f714e2183128887b9eb2
|
||||
# via mkdocs-include-markdown-plugin
|
||||
zipp==3.19.2 \
|
||||
--hash=sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19 \
|
||||
--hash=sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c
|
||||
zipp==3.20.0 \
|
||||
--hash=sha256:0145e43d89664cfe1a2e533adc75adafed82fe2da404b4bbb6b026c0157bdb31 \
|
||||
--hash=sha256:58da6168be89f0be59beb194da1250516fdaa062ccebd30127ac65d30045e10d
|
||||
# via importlib-metadata
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ src = ["src/backend/InvenTree"]
|
|||
"__init__.py" = ["D104"]
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = ["A", "B", "C4", "D", "I", "N"]
|
||||
select = ["A", "B", "C4", "D", "I", "N", "F"]
|
||||
# Things that should be enabled in the future:
|
||||
# - LOG
|
||||
# - DJ # for Django stuff
|
||||
|
|
|
|||
|
|
@ -17,5 +17,6 @@ build:
|
|||
- echo "Generating API schema file"
|
||||
- pip install -U invoke
|
||||
- invoke migrate
|
||||
- invoke export-settings-definitions --filename docs/inventree_settings.json --overwrite
|
||||
- invoke schema --filename docs/schema.yml --ignore-warnings
|
||||
- python docs/extract_schema.py docs/schema.yml
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ from pathlib import Path
|
|||
from django.conf import settings
|
||||
from django.db import transaction
|
||||
from django.http import JsonResponse
|
||||
from django.urls import include, path
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from django_q.models import OrmQ
|
||||
|
|
@ -21,9 +20,7 @@ from rest_framework.views import APIView
|
|||
|
||||
import InvenTree.version
|
||||
import users.models
|
||||
from InvenTree.filters import SEARCH_ORDER_FILTER
|
||||
from InvenTree.mixins import ListCreateAPI
|
||||
from InvenTree.permissions import RolePermission
|
||||
from InvenTree.templatetags.inventree_extras import plugins_info
|
||||
from part.models import Part
|
||||
from plugin.serializers import MetadataSerializer
|
||||
|
|
|
|||
|
|
@ -1,13 +1,34 @@
|
|||
"""InvenTree API version information."""
|
||||
|
||||
# InvenTree API version
|
||||
INVENTREE_API_VERSION = 235
|
||||
INVENTREE_API_VERSION = 241
|
||||
|
||||
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
|
||||
|
||||
|
||||
INVENTREE_API_TEXT = """
|
||||
|
||||
v241 - 2024-09-18 : https://github.com/inventree/InvenTree/pull/7906
|
||||
- Adjusts required fields for the MeUserDetail endpoint
|
||||
|
||||
v240 - 2024-08-16 : https://github.com/inventree/InvenTree/pull/7900
|
||||
- Adjust "issued_by" filter for the BuildOrder list endpoint
|
||||
- Adjust "assigned_to" filter for the BuildOrder list endpoint
|
||||
|
||||
v239 - 2024-08-15 : https://github.com/inventree/InvenTree/pull/7888
|
||||
- Adds "testable" field to the Part model
|
||||
- Adds associated filters to various API endpoints
|
||||
|
||||
v238 - 2024-08-14 : https://github.com/inventree/InvenTree/pull/7874
|
||||
- Add "assembly" filter to BuildLine API endpoint
|
||||
|
||||
v237 - 2024-08-13 : https://github.com/inventree/InvenTree/pull/7863
|
||||
- Reimplement "bulk delete" operation for Attachment model
|
||||
- Fix permission checks for Attachment API endpoints
|
||||
|
||||
v236 - 2024-08-10 : https://github.com/inventree/InvenTree/pull/7844
|
||||
- Adds "supplier_name" to the PurchaseOrder API serializer
|
||||
|
||||
v235 - 2024-08-08 : https://github.com/inventree/InvenTree/pull/7837
|
||||
- Adds "on_order" quantity to SalesOrderLineItem serializer
|
||||
- Adds "building" quantity to SalesOrderLineItem serializer
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ from django.core.exceptions import AppRegistryNotReady
|
|||
from django.db import transaction
|
||||
from django.db.utils import IntegrityError, OperationalError
|
||||
|
||||
from allauth.socialaccount.signals import social_account_added, social_account_updated
|
||||
from allauth.socialaccount.signals import social_account_updated
|
||||
|
||||
import InvenTree.conversion
|
||||
import InvenTree.ready
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ def convert_physical_value(value: str, unit: str = None, strip_units=True):
|
|||
if unit:
|
||||
try:
|
||||
valid = unit in ureg
|
||||
except Exception as exc:
|
||||
except Exception:
|
||||
valid = False
|
||||
|
||||
if not valid:
|
||||
|
|
@ -196,7 +196,7 @@ def convert_physical_value(value: str, unit: str = None, strip_units=True):
|
|||
try:
|
||||
value = convert_value(attempt, unit)
|
||||
break
|
||||
except Exception as exc:
|
||||
except Exception:
|
||||
value = None
|
||||
|
||||
if value is None:
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import traceback
|
|||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ValidationError as DjangoValidationError
|
||||
from django.db.utils import IntegrityError, OperationalError
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
import rest_framework.views as drfviews
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
import datetime
|
||||
import hashlib
|
||||
import io
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import os.path
|
||||
|
|
@ -27,7 +26,6 @@ from bleach import clean
|
|||
from djmoney.money import Money
|
||||
from PIL import Image
|
||||
|
||||
import InvenTree.version
|
||||
from common.currency import currency_code_default
|
||||
|
||||
from .settings import MEDIA_URL, STATIC_URL
|
||||
|
|
|
|||
|
|
@ -36,8 +36,6 @@ def get_base_url(request=None):
|
|||
3. If settings.SITE_URL is set (e.g. in the Django settings), use that
|
||||
4. If the InvenTree setting INVENTREE_BASE_URL is set, use that
|
||||
"""
|
||||
import common.models
|
||||
|
||||
# Check if a request is provided
|
||||
if request:
|
||||
return request.build_absolute_uri('/')
|
||||
|
|
@ -104,8 +102,6 @@ def download_image_from_url(remote_url, timeout=2.5):
|
|||
ValueError: Server responded with invalid 'Content-Length' value
|
||||
TypeError: Response is not a valid image
|
||||
"""
|
||||
import common.models
|
||||
|
||||
# Check that the provided URL at least looks valid
|
||||
validator = URLValidator()
|
||||
validator(remote_url)
|
||||
|
|
@ -203,8 +199,6 @@ def render_currency(
|
|||
max_decimal_places: The maximum number of decimal places to render to. If unspecified, uses the PRICING_DECIMAL_PLACES setting.
|
||||
include_symbol: If True, include the currency symbol in the output
|
||||
"""
|
||||
import common.models
|
||||
|
||||
if money in [None, '']:
|
||||
return '-'
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
"""Custom management command to export settings definitions.
|
||||
|
||||
This is used to generate a JSON file which contains all of the settings,
|
||||
so that they can be introspected by the InvenTree documentation system.
|
||||
|
||||
This in turn allows settings to be documented in the InvenTree documentation,
|
||||
without having to manually duplicate the information in multiple places.
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""Extract settings information, and export to a JSON file."""
|
||||
|
||||
def add_arguments(self, parser):
|
||||
"""Add custom arguments for this command."""
|
||||
parser.add_argument(
|
||||
'filename', type=str, help='Output filename for settings definitions'
|
||||
)
|
||||
|
||||
def handle(self, *args, **kwargs):
|
||||
"""Export settings information to a JSON file."""
|
||||
from common.models import InvenTreeSetting, InvenTreeUserSetting
|
||||
|
||||
settings = {'global': {}, 'user': {}}
|
||||
|
||||
# Global settings
|
||||
for key, setting in InvenTreeSetting.SETTINGS.items():
|
||||
settings['global'][key] = {
|
||||
'name': str(setting['name']),
|
||||
'description': str(setting['description']),
|
||||
'default': str(InvenTreeSetting.get_setting_default(key)),
|
||||
'units': str(setting.get('units', '')),
|
||||
}
|
||||
|
||||
# User settings
|
||||
for key, setting in InvenTreeUserSetting.SETTINGS.items():
|
||||
settings['user'][key] = {
|
||||
'name': str(setting['name']),
|
||||
'description': str(setting['description']),
|
||||
'default': str(InvenTreeUserSetting.get_setting_default(key)),
|
||||
'units': str(setting.get('units', '')),
|
||||
}
|
||||
|
||||
filename = kwargs.get('filename', 'inventree_settings.json')
|
||||
|
||||
with open(filename, 'w') as f:
|
||||
json.dump(settings, f, indent=4)
|
||||
|
||||
print(f"Exported InvenTree settings definitions to '{filename}'")
|
||||
|
|
@ -93,7 +93,7 @@ class PluginValidationMixin(DiffMixin):
|
|||
return
|
||||
except ValidationError as exc:
|
||||
raise exc
|
||||
except Exception as exc:
|
||||
except Exception:
|
||||
# Log the exception to the database
|
||||
import InvenTree.exceptions
|
||||
|
||||
|
|
|
|||
|
|
@ -402,17 +402,17 @@ class UserSerializer(InvenTreeModelSerializer):
|
|||
model = User
|
||||
fields = ['pk', 'username', 'first_name', 'last_name', 'email']
|
||||
|
||||
read_only_fields = ['username']
|
||||
read_only_fields = ['username', 'email']
|
||||
|
||||
username = serializers.CharField(label=_('Username'), help_text=_('Username'))
|
||||
first_name = serializers.CharField(
|
||||
label=_('First Name'), help_text=_('First name of the user')
|
||||
label=_('First Name'), help_text=_('First name of the user'), allow_blank=True
|
||||
)
|
||||
last_name = serializers.CharField(
|
||||
label=_('Last Name'), help_text=_('Last name of the user')
|
||||
label=_('Last Name'), help_text=_('Last name of the user'), allow_blank=True
|
||||
)
|
||||
email = serializers.EmailField(
|
||||
label=_('Email'), help_text=_('Email address of the user')
|
||||
label=_('Email'), help_text=_('Email address of the user'), allow_blank=True
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ import django.conf.locale
|
|||
import django.core.exceptions
|
||||
from django.core.validators import URLValidator
|
||||
from django.http import Http404
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
import pytz
|
||||
from dotenv import load_dotenv
|
||||
|
|
@ -301,7 +300,7 @@ if (
|
|||
and get_boolean_setting('INVENTREE_DEBUG_SHELL', 'debug_shell', False)
|
||||
): # noqa
|
||||
try:
|
||||
import django_admin_shell
|
||||
import django_admin_shell # noqa: F401
|
||||
|
||||
INSTALLED_APPS.append('django_admin_shell')
|
||||
ADMIN_SHELL_ENABLE = True
|
||||
|
|
|
|||
|
|
@ -4,6 +4,6 @@ This file remains here for backwards compatibility,
|
|||
as external plugins may import status codes from this file.
|
||||
"""
|
||||
|
||||
from build.status_codes import *
|
||||
from order.status_codes import *
|
||||
from stock.status_codes import *
|
||||
from build.status_codes import * # noqa: F403
|
||||
from order.status_codes import * # noqa: F403
|
||||
from stock.status_codes import * # noqa: F403
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ from allauth.socialaccount.models import SocialAccount, SocialLogin
|
|||
from common.models import InvenTreeSetting
|
||||
from InvenTree import sso
|
||||
from InvenTree.forms import RegistratonMixin
|
||||
from InvenTree.unit_test import InvenTreeTestCase
|
||||
|
||||
|
||||
class Dummy:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
"""Test general functions and helpers."""
|
||||
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
from datetime import datetime, timedelta
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ def inventreeDocUrl():
|
|||
|
||||
def inventreeAppUrl():
|
||||
"""Return URL for InvenTree app site."""
|
||||
return f'https://docs.inventree.org/app/'
|
||||
return 'https://docs.inventree.org/app/'
|
||||
|
||||
|
||||
def inventreeCreditsUrl():
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ class BuildFilter(rest_filters.FilterSet):
|
|||
'parent',
|
||||
'sales_order',
|
||||
'part',
|
||||
'issued_by',
|
||||
]
|
||||
|
||||
status = rest_filters.NumberFilter(label='Status')
|
||||
|
|
@ -58,7 +57,10 @@ class BuildFilter(rest_filters.FilterSet):
|
|||
return queryset.filter(Build.OVERDUE_FILTER)
|
||||
return queryset.exclude(Build.OVERDUE_FILTER)
|
||||
|
||||
assigned_to_me = rest_filters.BooleanFilter(label='assigned_to_me', method='filter_assigned_to_me')
|
||||
assigned_to_me = rest_filters.BooleanFilter(
|
||||
label=_('Assigned to me'),
|
||||
method='filter_assigned_to_me'
|
||||
)
|
||||
|
||||
def filter_assigned_to_me(self, queryset, name, value):
|
||||
"""Filter by orders which are assigned to the current user."""
|
||||
|
|
@ -71,10 +73,33 @@ class BuildFilter(rest_filters.FilterSet):
|
|||
return queryset.filter(responsible__in=owners)
|
||||
return queryset.exclude(responsible__in=owners)
|
||||
|
||||
assigned_to = rest_filters.NumberFilter(label='responsible', method='filter_responsible')
|
||||
issued_by = rest_filters.ModelChoiceFilter(
|
||||
queryset=Owner.objects.all(),
|
||||
label=_('Issued By'),
|
||||
method='filter_issued_by'
|
||||
)
|
||||
|
||||
def filter_responsible(self, queryset, name, value):
|
||||
def filter_issued_by(self, queryset, name, owner):
|
||||
"""Filter by 'owner' which issued the order."""
|
||||
|
||||
if owner.label() == 'user':
|
||||
user = User.objects.get(pk=owner.owner_id)
|
||||
return queryset.filter(issued_by=user)
|
||||
elif owner.label() == 'group':
|
||||
group = User.objects.filter(groups__pk=owner.owner_id)
|
||||
return queryset.filter(issued_by__in=group)
|
||||
else:
|
||||
return queryset.none()
|
||||
|
||||
assigned_to = rest_filters.ModelChoiceFilter(
|
||||
queryset=Owner.objects.all(),
|
||||
field_name='responsible',
|
||||
label=_('Assigned To')
|
||||
)
|
||||
|
||||
def filter_responsible(self, queryset, name, owner):
|
||||
"""Filter by orders which are assigned to the specified owner."""
|
||||
|
||||
owners = list(Owner.objects.filter(pk=value))
|
||||
|
||||
# if we query by a user, also find all ownerships through group memberships
|
||||
|
|
@ -290,7 +315,9 @@ class BuildLineFilter(rest_filters.FilterSet):
|
|||
# Fields on related models
|
||||
consumable = rest_filters.BooleanFilter(label=_('Consumable'), field_name='bom_item__consumable')
|
||||
optional = rest_filters.BooleanFilter(label=_('Optional'), field_name='bom_item__optional')
|
||||
assembly = rest_filters.BooleanFilter(label=_('Assembly'), field_name='bom_item__sub_part__assembly')
|
||||
tracked = rest_filters.BooleanFilter(label=_('Tracked'), field_name='bom_item__sub_part__trackable')
|
||||
testable = rest_filters.BooleanFilter(label=_('Testable'), field_name='bom_item__sub_part__testable')
|
||||
|
||||
allocated = rest_filters.BooleanFilter(label=_('Allocated'), method='filter_allocated')
|
||||
|
||||
|
|
|
|||
|
|
@ -395,9 +395,9 @@ class Build(
|
|||
def sub_builds(self, cascade=True):
|
||||
"""Return all Build Order objects under this one."""
|
||||
if cascade:
|
||||
return Build.objects.filter(parent=self.pk)
|
||||
descendants = self.get_descendants(include_self=True)
|
||||
Build.objects.filter(parent__pk__in=[d.pk for d in descendants])
|
||||
return self.get_descendants(include_self=False)
|
||||
else:
|
||||
return self.get_children()
|
||||
|
||||
def sub_build_count(self, cascade=True):
|
||||
"""Return the number of sub builds under this one.
|
||||
|
|
@ -407,6 +407,11 @@ class Build(
|
|||
"""
|
||||
return self.sub_builds(cascade=cascade).count()
|
||||
|
||||
@property
|
||||
def has_open_child_builds(self):
|
||||
"""Return True if this build order has any open child builds."""
|
||||
return self.sub_builds().filter(status__in=BuildStatusGroups.ACTIVE_CODES).exists()
|
||||
|
||||
@property
|
||||
def is_overdue(self):
|
||||
"""Returns true if this build is "overdue".
|
||||
|
|
@ -576,6 +581,9 @@ class Build(
|
|||
- Untracked parts must be allocated
|
||||
"""
|
||||
|
||||
if get_global_setting('BUILDORDER_REQUIRE_CLOSED_CHILDS') and self.has_open_child_builds:
|
||||
return False
|
||||
|
||||
if self.status != BuildStatus.PRODUCTION.value:
|
||||
return False
|
||||
|
||||
|
|
@ -619,6 +627,10 @@ class Build(
|
|||
trim_allocated_stock = kwargs.pop('trim_allocated_stock', False)
|
||||
user = kwargs.pop('user', None)
|
||||
|
||||
# Prevent completion if there are open child builds
|
||||
if get_global_setting('BUILDORDER_REQUIRE_CLOSED_CHILDS') and self.has_open_child_builds:
|
||||
return
|
||||
|
||||
if self.incomplete_count > 0:
|
||||
return
|
||||
|
||||
|
|
@ -974,7 +986,10 @@ class Build(
|
|||
items_to_save = []
|
||||
items_to_delete = []
|
||||
|
||||
for build_line in self.untracked_line_items:
|
||||
lines = self.untracked_line_items
|
||||
lines = lines.prefetch_related('allocations')
|
||||
|
||||
for build_line in lines:
|
||||
|
||||
reduce_by = build_line.allocated_quantity() - build_line.quantity
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ from stock.serializers import StockItemSerializerBrief, LocationBriefSerializer
|
|||
|
||||
import common.models
|
||||
from common.serializers import ProjectCodeSerializer
|
||||
from common.settings import get_global_setting
|
||||
from importer.mixins import DataImportExportSerializerMixin
|
||||
import company.serializers
|
||||
import part.filters
|
||||
|
|
@ -765,6 +766,9 @@ class BuildCompleteSerializer(serializers.Serializer):
|
|||
"""Perform validation of this serializer prior to saving"""
|
||||
build = self.context['build']
|
||||
|
||||
if get_global_setting('BUILDORDER_REQUIRE_CLOSED_CHILDS') and build.has_open_child_builds:
|
||||
raise ValidationError(_("Build order has open child build orders"))
|
||||
|
||||
if build.status != BuildStatus.PRODUCTION.value:
|
||||
raise ValidationError(_("Build order must be in production state"))
|
||||
|
||||
|
|
@ -1234,6 +1238,7 @@ class BuildLineSerializer(DataImportExportSerializerMixin, InvenTreeModelSeriali
|
|||
'reference',
|
||||
'consumable',
|
||||
'optional',
|
||||
'testable',
|
||||
'trackable',
|
||||
'inherited',
|
||||
'allow_variants',
|
||||
|
|
@ -1278,6 +1283,7 @@ class BuildLineSerializer(DataImportExportSerializerMixin, InvenTreeModelSeriali
|
|||
reference = serializers.CharField(source='bom_item.reference', label=_('Reference'), read_only=True)
|
||||
consumable = serializers.BooleanField(source='bom_item.consumable', label=_('Consumable'), read_only=True)
|
||||
optional = serializers.BooleanField(source='bom_item.optional', label=_('Optional'), read_only=True)
|
||||
testable = serializers.BooleanField(source='bom_item.sub_part.testable', label=_('Testable'), read_only=True)
|
||||
trackable = serializers.BooleanField(source='bom_item.sub_part.trackable', label=_('Trackable'), read_only=True)
|
||||
inherited = serializers.BooleanField(source='bom_item.inherited', label=_('Inherited'), read_only=True)
|
||||
allow_variants = serializers.BooleanField(source='bom_item.allow_variants', label=_('Allow Variants'), read_only=True)
|
||||
|
|
|
|||
|
|
@ -332,7 +332,7 @@ src="{% static 'img/blank_image.png' %}"
|
|||
});
|
||||
});
|
||||
|
||||
{% if build.part.trackable > 0 %}
|
||||
{% if build.part.testable %}
|
||||
onPanelLoad("test-statistics", function() {
|
||||
prepareTestStatisticsTable('build', '{% url "api-test-statistics-by-build" build.pk %}')
|
||||
});
|
||||
|
|
|
|||
|
|
@ -388,6 +388,7 @@ onPanelLoad('outputs', function() {
|
|||
source_location: {{ build.take_from.pk }},
|
||||
{% endif %}
|
||||
tracked_parts: true,
|
||||
testable: {% js_bool build.part.testable %},
|
||||
trackable: {% js_bool build.part.trackable %}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
{% include "sidebar_item.html" with label='consumed' text=text icon="fa-tasks" %}
|
||||
{% trans "Child Build Orders" as text %}
|
||||
{% include "sidebar_item.html" with label='children' text=text icon="fa-sitemap" %}
|
||||
{% if build.part.trackable %}
|
||||
{% if build.part.testable %}
|
||||
{% trans "Test Statistics" as text %}
|
||||
{% include "sidebar_item.html" with label='test-statistics' text=text icon="fa-chart-line" %}
|
||||
{% endif %}
|
||||
|
|
|
|||
|
|
@ -1015,7 +1015,7 @@ class BuildOverallocationTest(BuildAPITest):
|
|||
'accept_overallocated': 'trim',
|
||||
},
|
||||
expected_code=201,
|
||||
max_query_count=555, # TODO: Come back and refactor this
|
||||
max_query_count=600, # TODO: Come back and refactor this
|
||||
)
|
||||
|
||||
self.build.refresh_from_db()
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import json
|
|||
|
||||
from django.conf import settings
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db.models import Q
|
||||
from django.http.response import HttpResponse
|
||||
from django.urls import include, path, re_path
|
||||
|
|
@ -706,7 +707,7 @@ class AttachmentFilter(rest_filters.FilterSet):
|
|||
return queryset.filter(Q(attachment=None) | Q(attachment='')).distinct()
|
||||
|
||||
|
||||
class AttachmentList(ListCreateAPI):
|
||||
class AttachmentList(BulkDeleteMixin, ListCreateAPI):
|
||||
"""List API endpoint for Attachment objects."""
|
||||
|
||||
queryset = common.models.Attachment.objects.all()
|
||||
|
|
@ -725,6 +726,24 @@ class AttachmentList(ListCreateAPI):
|
|||
attachment.upload_user = self.request.user
|
||||
attachment.save()
|
||||
|
||||
def validate_delete(self, queryset, request) -> None:
|
||||
"""Ensure that the user has correct permissions for a bulk-delete.
|
||||
|
||||
- Extract all model types from the provided queryset
|
||||
- Ensure that the user has correct 'delete' permissions for each model
|
||||
"""
|
||||
from common.validators import attachment_model_class_from_label
|
||||
from users.models import check_user_permission
|
||||
|
||||
model_types = queryset.values_list('model_type', flat=True).distinct()
|
||||
|
||||
for model_type in model_types:
|
||||
if model_class := attachment_model_class_from_label(model_type):
|
||||
if not check_user_permission(request.user, model_class, 'delete'):
|
||||
raise ValidationError(
|
||||
_('User does not have permission to delete these attachments')
|
||||
)
|
||||
|
||||
|
||||
class AttachmentDetail(RetrieveUpdateDestroyAPI):
|
||||
"""Detail API endpoint for Attachment objects."""
|
||||
|
|
|
|||
|
|
@ -1838,6 +1838,14 @@ class InvenTreeSetting(BaseInvenTreeSetting):
|
|||
'default': False,
|
||||
'validator': bool,
|
||||
},
|
||||
'BUILDORDER_REQUIRE_CLOSED_CHILDS': {
|
||||
'name': _('Require Closed Child Orders'),
|
||||
'description': _(
|
||||
'Prevent build order completion until all child orders are closed'
|
||||
),
|
||||
'default': False,
|
||||
'validator': bool,
|
||||
},
|
||||
'PREVENT_BUILD_COMPLETION_HAVING_INCOMPLETED_TESTS': {
|
||||
'name': _('Block Until Tests Pass'),
|
||||
'description': _(
|
||||
|
|
|
|||
|
|
@ -540,12 +540,17 @@ class AttachmentSerializer(InvenTreeModelSerializer):
|
|||
allow_null=False,
|
||||
)
|
||||
|
||||
def save(self):
|
||||
def save(self, **kwargs):
|
||||
"""Override the save method to handle the model_type field."""
|
||||
from InvenTree.models import InvenTreeAttachmentMixin
|
||||
from users.models import check_user_permission
|
||||
|
||||
model_type = self.validated_data.get('model_type', None)
|
||||
|
||||
if model_type is None:
|
||||
if self.instance:
|
||||
model_type = self.instance.model_type
|
||||
|
||||
# Ensure that the user has permission to attach files to the specified model
|
||||
user = self.context.get('request').user
|
||||
|
||||
|
|
@ -556,15 +561,18 @@ class AttachmentSerializer(InvenTreeModelSerializer):
|
|||
if not issubclass(target_model_class, InvenTreeAttachmentMixin):
|
||||
raise PermissionDenied(_('Invalid model type specified for attachment'))
|
||||
|
||||
permission_error_msg = _(
|
||||
'User does not have permission to create or edit attachments for this model'
|
||||
)
|
||||
|
||||
if not check_user_permission(user, target_model_class, 'change'):
|
||||
raise PermissionDenied(permission_error_msg)
|
||||
|
||||
# Check that the user has the required permissions to attach files to the target model
|
||||
if not target_model_class.check_attachment_permission('change', user):
|
||||
raise PermissionDenied(
|
||||
_(
|
||||
'User does not have permission to create or edit attachments for this model'
|
||||
)
|
||||
)
|
||||
raise PermissionDenied(_(permission_error_msg))
|
||||
|
||||
return super().save()
|
||||
return super().save(**kwargs)
|
||||
|
||||
|
||||
class IconSerializer(serializers.Serializer):
|
||||
|
|
|
|||
|
|
@ -157,6 +157,29 @@ class AttachmentTest(InvenTreeAPITestCase):
|
|||
# Upload should now work!
|
||||
response = self.post(url, data, expected_code=201)
|
||||
|
||||
pk = response.data['pk']
|
||||
|
||||
# Edit the attachment via API
|
||||
response = self.patch(
|
||||
reverse('api-attachment-detail', kwargs={'pk': pk}),
|
||||
{'comment': 'New comment'},
|
||||
expected_code=200,
|
||||
)
|
||||
|
||||
self.assertEqual(response.data['comment'], 'New comment')
|
||||
|
||||
attachment = Attachment.objects.get(pk=pk)
|
||||
self.assertEqual(attachment.comment, 'New comment')
|
||||
|
||||
# And check that we cannot edit the attachment without the correct permissions
|
||||
self.clearRoles()
|
||||
|
||||
self.patch(
|
||||
reverse('api-attachment-detail', kwargs={'pk': pk}),
|
||||
{'comment': 'New comment 2'},
|
||||
expected_code=403,
|
||||
)
|
||||
|
||||
# Try to delete the attachment via API (should fail)
|
||||
attachment = part.attachments.first()
|
||||
url = reverse('api-attachment-detail', kwargs={'pk': attachment.pk})
|
||||
|
|
@ -407,8 +430,6 @@ class SettingsTest(InvenTreeTestCase):
|
|||
@override_settings(SITE_URL=None, PLUGIN_TESTING=True, PLUGIN_TESTING_SETUP=True)
|
||||
def test_defaults(self):
|
||||
"""Populate the settings with default values."""
|
||||
N = len(InvenTreeSetting.SETTINGS.keys())
|
||||
|
||||
for key in InvenTreeSetting.SETTINGS.keys():
|
||||
value = InvenTreeSetting.get_setting_default(key)
|
||||
|
||||
|
|
@ -1103,7 +1124,6 @@ class CommonTest(InvenTreeAPITestCase):
|
|||
|
||||
def test_restart_flag(self):
|
||||
"""Test that the restart flag is reset on start."""
|
||||
import common.models
|
||||
from plugin import registry
|
||||
|
||||
# set flag true
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ def validate_attachment_model_type(value):
|
|||
"""Ensure that the provided attachment model is valid."""
|
||||
model_names = [el[0] for el in attachment_model_options()]
|
||||
if value not in model_names:
|
||||
raise ValidationError(f'Model type does not support attachments')
|
||||
raise ValidationError('Model type does not support attachments')
|
||||
|
||||
|
||||
def validate_notes_model_type(value):
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
"""Admin site specification for the 'importer' app."""
|
||||
|
||||
from django.contrib import admin
|
||||
from django.urls import path
|
||||
|
||||
import importer.models
|
||||
import importer.registry
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -1,7 +1,6 @@
|
|||
"""Django admin interface for the machine app."""
|
||||
|
||||
from django.contrib import admin
|
||||
from django.http.request import HttpRequest
|
||||
|
||||
from machine import models
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ logger = logging.getLogger('inventree')
|
|||
|
||||
|
||||
class MachineRegistry(
|
||||
get_shared_class_instance_state_mixin(lambda _x: f'machine:registry')
|
||||
get_shared_class_instance_state_mixin(lambda _x: 'machine:registry')
|
||||
):
|
||||
"""Machine registry class."""
|
||||
|
||||
|
|
@ -44,7 +44,7 @@ class MachineRegistry(
|
|||
"""Initialize the machine registry."""
|
||||
# clear cache for machines (only needed for global redis cache)
|
||||
if main and hasattr(cache, 'delete_pattern'): # pragma: no cover
|
||||
cache.delete_pattern(f'machine:*')
|
||||
cache.delete_pattern('machine:*')
|
||||
|
||||
self.discover_machine_types()
|
||||
self.discover_drivers()
|
||||
|
|
|
|||
|
|
@ -292,7 +292,7 @@ class TestLabelPrinterMachineType(TestMachineRegistryMixin, InvenTreeAPITestCase
|
|||
# test the single print label method calls
|
||||
self.assertEqual(self.print_label.call_count, 2)
|
||||
self.assertEqual(self.print_label.call_args.args[0], self.machine.machine)
|
||||
self.assertEqual(self.print_label.call_args.args[1], label)
|
||||
self.assertEqual(self.print_label.call_args.args[1], template)
|
||||
self.assertEqual(self.print_label.call_args.args[2], parts[1])
|
||||
self.assertIn('printing_options', self.print_labels.call_args.kwargs)
|
||||
self.assertEqual(
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ class OrderFilter(rest_filters.FilterSet):
|
|||
"""Base class for custom API filters for the OrderList endpoint."""
|
||||
|
||||
# Filter against order status
|
||||
status = rest_filters.NumberFilter(label='Order Status', method='filter_status')
|
||||
status = rest_filters.NumberFilter(label=_('Order Status'), method='filter_status')
|
||||
|
||||
def filter_status(self, queryset, name, value):
|
||||
"""Filter by integer status code."""
|
||||
|
|
@ -85,11 +85,11 @@ class OrderFilter(rest_filters.FilterSet):
|
|||
|
||||
# Exact match for reference
|
||||
reference = rest_filters.CharFilter(
|
||||
label='Filter by exact reference', field_name='reference', lookup_expr='iexact'
|
||||
label=_('Order Reference'), field_name='reference', lookup_expr='iexact'
|
||||
)
|
||||
|
||||
assigned_to_me = rest_filters.BooleanFilter(
|
||||
label='assigned_to_me', method='filter_assigned_to_me'
|
||||
label=_('Assigned to me'), method='filter_assigned_to_me'
|
||||
)
|
||||
|
||||
def filter_assigned_to_me(self, queryset, name, value):
|
||||
|
|
@ -113,7 +113,7 @@ class OrderFilter(rest_filters.FilterSet):
|
|||
return queryset.exclude(self.Meta.model.overdue_filter())
|
||||
|
||||
outstanding = rest_filters.BooleanFilter(
|
||||
label='outstanding', method='filter_outstanding'
|
||||
label=_('Outstanding'), method='filter_outstanding'
|
||||
)
|
||||
|
||||
def filter_outstanding(self, queryset, name, value):
|
||||
|
|
@ -123,11 +123,13 @@ class OrderFilter(rest_filters.FilterSet):
|
|||
return queryset.exclude(status__in=self.Meta.model.get_status_class().OPEN)
|
||||
|
||||
project_code = rest_filters.ModelChoiceFilter(
|
||||
queryset=common.models.ProjectCode.objects.all(), field_name='project_code'
|
||||
queryset=common.models.ProjectCode.objects.all(),
|
||||
field_name='project_code',
|
||||
label=_('Project Code'),
|
||||
)
|
||||
|
||||
has_project_code = rest_filters.BooleanFilter(
|
||||
label='has_project_code', method='filter_has_project_code'
|
||||
method='filter_has_project_code', label=_('Has Project Code')
|
||||
)
|
||||
|
||||
def filter_has_project_code(self, queryset, name, value):
|
||||
|
|
@ -137,7 +139,7 @@ class OrderFilter(rest_filters.FilterSet):
|
|||
return queryset.filter(project_code=None)
|
||||
|
||||
assigned_to = rest_filters.ModelChoiceFilter(
|
||||
queryset=Owner.objects.all(), field_name='responsible'
|
||||
queryset=Owner.objects.all(), field_name='responsible', label=_('Responsible')
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
"""Order model definitions."""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
|
|
@ -1162,7 +1161,8 @@ class SalesOrder(TotalPriceMixin, Order):
|
|||
|
||||
# Schedule pricing update for any referenced parts
|
||||
for line in self.lines.all():
|
||||
line.part.schedule_pricing_update(create=True)
|
||||
if line.part:
|
||||
line.part.schedule_pricing_update(create=True)
|
||||
|
||||
trigger_event('salesorder.completed', id=self.pk)
|
||||
|
||||
|
|
|
|||
|
|
@ -231,6 +231,7 @@ class PurchaseOrderSerializer(
|
|||
'supplier',
|
||||
'supplier_detail',
|
||||
'supplier_reference',
|
||||
'supplier_name',
|
||||
'total_price',
|
||||
'order_currency',
|
||||
])
|
||||
|
|
@ -278,6 +279,10 @@ class PurchaseOrderSerializer(
|
|||
|
||||
return queryset
|
||||
|
||||
supplier_name = serializers.CharField(
|
||||
source='supplier.name', read_only=True, label=_('Supplier Name')
|
||||
)
|
||||
|
||||
supplier_detail = CompanyBriefSerializer(
|
||||
source='supplier', many=False, read_only=True
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1126,9 +1126,9 @@ class PartFilter(rest_filters.FilterSet):
|
|||
# TODO: We should cache BOM checksums to make this process more efficient
|
||||
pks = []
|
||||
|
||||
for part in queryset:
|
||||
if part.is_bom_valid() == value:
|
||||
pks.append(part.pk)
|
||||
for item in queryset:
|
||||
if item.is_bom_valid() == value:
|
||||
pks.append(item.pk)
|
||||
|
||||
return queryset.filter(pk__in=pks)
|
||||
|
||||
|
|
@ -1157,6 +1157,8 @@ class PartFilter(rest_filters.FilterSet):
|
|||
|
||||
trackable = rest_filters.BooleanFilter()
|
||||
|
||||
testable = rest_filters.BooleanFilter()
|
||||
|
||||
purchaseable = rest_filters.BooleanFilter()
|
||||
|
||||
salable = rest_filters.BooleanFilter()
|
||||
|
|
@ -1748,20 +1750,28 @@ class BomFilter(rest_filters.FilterSet):
|
|||
|
||||
# Filters for linked 'part'
|
||||
part_active = rest_filters.BooleanFilter(
|
||||
label='Master part is active', field_name='part__active'
|
||||
label='Assembly part is active', field_name='part__active'
|
||||
)
|
||||
|
||||
part_trackable = rest_filters.BooleanFilter(
|
||||
label='Master part is trackable', field_name='part__trackable'
|
||||
label='Assembly part is trackable', field_name='part__trackable'
|
||||
)
|
||||
|
||||
part_testable = rest_filters.BooleanFilter(
|
||||
label=_('Assembly part is testable'), field_name='part__testable'
|
||||
)
|
||||
|
||||
# Filters for linked 'sub_part'
|
||||
sub_part_trackable = rest_filters.BooleanFilter(
|
||||
label='Sub part is trackable', field_name='sub_part__trackable'
|
||||
label='Component part is trackable', field_name='sub_part__trackable'
|
||||
)
|
||||
|
||||
sub_part_testable = rest_filters.BooleanFilter(
|
||||
label=_('Component part is testable'), field_name='sub_part__testable'
|
||||
)
|
||||
|
||||
sub_part_assembly = rest_filters.BooleanFilter(
|
||||
label='Sub part is an assembly', field_name='sub_part__assembly'
|
||||
label='Component part is an assembly', field_name='sub_part__assembly'
|
||||
)
|
||||
|
||||
available_stock = rest_filters.BooleanFilter(
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue