mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge branch 'master' into pui-plugins
This commit is contained in:
commit
0ea0ba1be5
4
.github/FUNDING.yml
vendored
4
.github/FUNDING.yml
vendored
@ -1,5 +1,3 @@
|
||||
github: inventree
|
||||
ko_fi: inventree
|
||||
patreon: inventree
|
||||
polar: inventree
|
||||
github: inventree
|
||||
custom: [paypal.me/inventree]
|
||||
|
2
.github/workflows/docker.yaml
vendored
2
.github/workflows/docker.yaml
vendored
@ -166,7 +166,7 @@ jobs:
|
||||
- name: Push Docker Images
|
||||
id: push-docker
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/build-push-action@16ebe778df0e7752d2cfcbd924afdbbd89c1a755 # pin@v6.6.1
|
||||
uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 # pin@v6.7.0
|
||||
with:
|
||||
context: .
|
||||
file: ./contrib/container/Dockerfile
|
||||
|
2
.github/workflows/scorecard.yaml
vendored
2
.github/workflows/scorecard.yaml
vendored
@ -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@eb055d739abdc2e8de2e5f4ba1a8b246daa779aa # v3.26.0
|
||||
uses: github/codeql-action/upload-sarif@883d8588e56d1753a8a58c1c86e88976f0c23449 # v3.26.3
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
@ -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 \
|
||||
|
@ -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
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -301,21 +301,21 @@ mkdocs-get-deps==0.2.0 \
|
||||
--hash=sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c \
|
||||
--hash=sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134
|
||||
# via mkdocs
|
||||
mkdocs-git-revision-date-localized-plugin==1.2.6 \
|
||||
--hash=sha256:e432942ce4ee8aa9b9f4493e993dee9d2cc08b3ea2b40a3d6b03ca0f2a4bcaa2 \
|
||||
--hash=sha256:f015cb0f3894a39b33447b18e270ae391c4e25275cac5a626e80b243784e2692
|
||||
mkdocs-git-revision-date-localized-plugin==1.2.7 \
|
||||
--hash=sha256:2f83b52b4dad642751a79465f80394672cbad022129286f40d36b03aebee490f \
|
||||
--hash=sha256:d2b30ccb74ec8e118298758d75ae4b4f02c620daf776a6c92fcbb58f2b78f19f
|
||||
# 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 \
|
||||
--hash=sha256:fe348d75f01c911f362b6d998c57b3d85b505876dde69db924f2c512c395c328
|
||||
# via -r docs/requirements.in
|
||||
mkdocs-material==9.5.31 \
|
||||
--hash=sha256:1b1f49066fdb3824c1e96d6bacd2d4375de4ac74580b47e79ff44c4d835c5fcb \
|
||||
--hash=sha256:31833ec664772669f5856f4f276bf3fdf0e642a445e64491eda459249c3a1ca8
|
||||
mkdocs-material==9.5.32 \
|
||||
--hash=sha256:38ed66e6d6768dde4edde022554553e48b2db0d26d1320b19e2e2b9da0be1120 \
|
||||
--hash=sha256:f3704f46b63d31b3cd35c0055a72280bed825786eccaf19c655b44e0cd2c6b3f
|
||||
# via -r docs/requirements.in
|
||||
mkdocs-material-extensions==1.3.1 \
|
||||
--hash=sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443 \
|
||||
@ -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
|
||||
|
@ -1,13 +1,29 @@
|
||||
"""InvenTree API version information."""
|
||||
|
||||
# InvenTree API version
|
||||
INVENTREE_API_VERSION = 238
|
||||
INVENTREE_API_VERSION = 242
|
||||
|
||||
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
|
||||
|
||||
|
||||
INVENTREE_API_TEXT = """
|
||||
|
||||
v242 - 2024-08-20 : https://github.com/inventree/InvenTree/pull/7932
|
||||
- Adds "level" attribute to BuildOrder serializer
|
||||
- Allow ordering of BuildOrder API by "level" attribute
|
||||
- Allow "parent" filter for BuildOrder API to have "cascade=True" option
|
||||
|
||||
v241 - 2024-08-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
|
||||
|
||||
|
@ -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
|
||||
)
|
||||
|
||||
|
||||
|
@ -34,10 +34,8 @@ class BuildFilter(rest_filters.FilterSet):
|
||||
"""Metaclass options."""
|
||||
model = Build
|
||||
fields = [
|
||||
'parent',
|
||||
'sales_order',
|
||||
'part',
|
||||
'issued_by',
|
||||
]
|
||||
|
||||
status = rest_filters.NumberFilter(label='Status')
|
||||
@ -50,6 +48,35 @@ class BuildFilter(rest_filters.FilterSet):
|
||||
return queryset.filter(status__in=BuildStatusGroups.ACTIVE_CODES)
|
||||
return queryset.exclude(status__in=BuildStatusGroups.ACTIVE_CODES)
|
||||
|
||||
cascade = rest_filters.BooleanFilter(label=_('Cascade'), method='filter_cascade')
|
||||
|
||||
def filter_cascade(self, queryset, name, value):
|
||||
"""Filter by whether or not the build is a 'cascade' build.
|
||||
|
||||
Note: this only applies when the 'parent' field filter is specified.
|
||||
"""
|
||||
|
||||
# No filtering here, see 'filter_parent'
|
||||
return queryset
|
||||
|
||||
parent = rest_filters.ModelChoiceFilter(
|
||||
queryset=Build.objects.all(),
|
||||
label=_('Parent Build'),
|
||||
field_name='parent',
|
||||
method='filter_parent'
|
||||
)
|
||||
|
||||
def filter_parent(self, queryset, name, parent):
|
||||
"""Filter by 'parent' build order."""
|
||||
|
||||
cascade = str2bool(self.data.get('cascade', False))
|
||||
|
||||
if cascade:
|
||||
builds = parent.get_descendants(include_self=False)
|
||||
return queryset.filter(pk__in=[b.pk for b in builds])
|
||||
|
||||
return queryset.filter(parent=parent)
|
||||
|
||||
overdue = rest_filters.BooleanFilter(label='Build is overdue', method='filter_overdue')
|
||||
|
||||
def filter_overdue(self, queryset, name, value):
|
||||
@ -58,7 +85,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 +101,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
|
||||
@ -150,6 +203,7 @@ class BuildList(DataExportViewMixin, BuildMixin, ListCreateAPI):
|
||||
'responsible',
|
||||
'project_code',
|
||||
'priority',
|
||||
'level',
|
||||
]
|
||||
|
||||
ordering_field_aliases = {
|
||||
@ -292,6 +346,7 @@ class BuildLineFilter(rest_filters.FilterSet):
|
||||
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')
|
||||
|
||||
|
@ -76,6 +76,7 @@ class BuildSerializer(NotesFieldMixin, DataImportExportSerializerMixin, InvenTre
|
||||
'responsible',
|
||||
'responsible_detail',
|
||||
'priority',
|
||||
'level',
|
||||
]
|
||||
|
||||
read_only_fields = [
|
||||
@ -84,8 +85,11 @@ class BuildSerializer(NotesFieldMixin, DataImportExportSerializerMixin, InvenTre
|
||||
'completion_data',
|
||||
'status',
|
||||
'status_text',
|
||||
'level',
|
||||
]
|
||||
|
||||
level = serializers.IntegerField(label=_('Build Level'), read_only=True)
|
||||
|
||||
url = serializers.CharField(source='get_absolute_url', read_only=True)
|
||||
|
||||
status_text = serializers.CharField(source='get_status_display', read_only=True)
|
||||
@ -1238,6 +1242,7 @@ class BuildLineSerializer(DataImportExportSerializerMixin, InvenTreeModelSeriali
|
||||
'reference',
|
||||
'consumable',
|
||||
'optional',
|
||||
'testable',
|
||||
'trackable',
|
||||
'inherited',
|
||||
'allow_variants',
|
||||
@ -1282,6 +1287,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 %}
|
||||
|
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
@ -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')
|
||||
)
|
||||
|
||||
|
||||
|
@ -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(
|
||||
|
18
src/backend/InvenTree/part/migrations/0128_part_testable.py
Normal file
18
src/backend/InvenTree/part/migrations/0128_part_testable.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.15 on 2024-08-15 02:12
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('part', '0127_remove_partcategory_icon_partcategory__icon'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='part',
|
||||
name='testable',
|
||||
field=models.BooleanField(default=False, help_text='Can this part have test results recorded against it?', verbose_name='Testable'),
|
||||
),
|
||||
]
|
@ -0,0 +1,37 @@
|
||||
# Generated by Django 4.2.15 on 2024-08-15 02:14
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def set_testable(apps, schema_editor):
|
||||
"""Set the 'testable' status to True for certain parts.
|
||||
|
||||
Prior to migration part.0128, the 'trackable' attribute
|
||||
was used to determine if parts could have tests associated with them.
|
||||
|
||||
However, 'trackable' comes with other restrictions
|
||||
(such as requiring a unique serial number).
|
||||
|
||||
So, we have added a new field 'testable' to the Part model,
|
||||
which is updated in this migration to match the value of the 'trackable' field.
|
||||
"""
|
||||
|
||||
Part = apps.get_model('part', 'Part')
|
||||
|
||||
# By default, 'testable' is False - so we only need to update parts marked as 'trackable'
|
||||
trackable_parts = Part.objects.filter(trackable=True)
|
||||
|
||||
if trackable_parts.count() > 0:
|
||||
print(f"\nMarking {trackable_parts.count()} Part objects as 'testable'")
|
||||
trackable_parts.update(testable=True)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('part', '0128_part_testable'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(set_testable, reverse_code=migrations.RunPython.noop)
|
||||
]
|
@ -403,6 +403,7 @@ class Part(
|
||||
component: Can this part be used to make other parts?
|
||||
purchaseable: Can this part be purchased from suppliers?
|
||||
trackable: Trackable parts can have unique serial numbers assigned, etc, etc
|
||||
testable: Testable parts can have test results recorded against their stock items
|
||||
active: Is this part active? Parts are deactivated instead of being deleted
|
||||
locked: This part is locked and cannot be edited
|
||||
virtual: Is this part "virtual"? e.g. a software product or similar
|
||||
@ -1166,6 +1167,12 @@ class Part(
|
||||
help_text=_('Does this part have tracking for unique items?'),
|
||||
)
|
||||
|
||||
testable = models.BooleanField(
|
||||
default=False,
|
||||
verbose_name=_('Testable'),
|
||||
help_text=_('Can this part have test results recorded against it?'),
|
||||
)
|
||||
|
||||
purchaseable = models.BooleanField(
|
||||
default=part_settings.part_purchaseable_default,
|
||||
verbose_name=_('Purchaseable'),
|
||||
|
@ -323,6 +323,7 @@ class PartBriefSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
||||
'is_template',
|
||||
'purchaseable',
|
||||
'salable',
|
||||
'testable',
|
||||
'trackable',
|
||||
'virtual',
|
||||
'units',
|
||||
@ -673,6 +674,7 @@ class PartSerializer(
|
||||
'salable',
|
||||
'starred',
|
||||
'thumbnail',
|
||||
'testable',
|
||||
'trackable',
|
||||
'units',
|
||||
'variant_of',
|
||||
|
@ -50,7 +50,7 @@
|
||||
{% trans "Stocktake" as text %}
|
||||
{% include "sidebar_item.html" with label="stocktake" text=text icon="fa-clipboard-check" %}
|
||||
{% endif %}
|
||||
{% if part.trackable %}
|
||||
{% if part.testable %}
|
||||
{% trans "Test Templates" as text %}
|
||||
{% include "sidebar_item.html" with label="test-templates" text=text icon="fa-vial" %}
|
||||
{% trans "Test Statistics" as text %}
|
||||
|
@ -8,7 +8,7 @@
|
||||
{% trans "Allocations" as text %}
|
||||
{% include "sidebar_item.html" with label="allocations" text=text icon="fa-bookmark" %}
|
||||
{% endif %}
|
||||
{% if item.part.trackable %}
|
||||
{% if item.part.testable %}
|
||||
{% trans "Test Data" as text %}
|
||||
{% include "sidebar_item.html" with label='test-data' text=text icon="fa-vial" %}
|
||||
{% endif %}
|
||||
|
@ -1297,7 +1297,7 @@ function loadBuildOutputTable(build_info, options={}) {
|
||||
});
|
||||
|
||||
// Request list of required tests for the part being assembled
|
||||
if (build_info.trackable) {
|
||||
if (build_info.testable) {
|
||||
inventreeGet(
|
||||
'{% url "api-part-test-template-list" %}',
|
||||
{
|
||||
|
@ -188,6 +188,9 @@ function partFields(options={}) {
|
||||
default: global_settings.PART_TEMPLATE,
|
||||
group: 'attributes',
|
||||
},
|
||||
testable: {
|
||||
group: 'attributes',
|
||||
},
|
||||
trackable: {
|
||||
default: global_settings.PART_TRACKABLE,
|
||||
group: 'attributes',
|
||||
|
@ -18,28 +18,27 @@
|
||||
*/
|
||||
|
||||
|
||||
// Construct a dynamic API filter for the "issued by" field
|
||||
function constructIssuedByFilter() {
|
||||
// Construct a dynamic API filter for an "owner"
|
||||
function constructOwnerFilter(title) {
|
||||
return {
|
||||
title: '{% trans "Issued By" %}',
|
||||
title: title,
|
||||
options: function() {
|
||||
let users = {};
|
||||
|
||||
inventreeGet('{% url "api-user-list" %}', {}, {
|
||||
var ownersList = {};
|
||||
inventreeGet('{% url "api-owner-list" %}', {}, {
|
||||
async: false,
|
||||
success: function(response) {
|
||||
for (let user of response) {
|
||||
users[user.pk] = {
|
||||
key: user.pk,
|
||||
value: user.username
|
||||
for (var key in response) {
|
||||
let owner = response[key];
|
||||
ownersList[owner.pk] = {
|
||||
key: owner.pk,
|
||||
value: `${owner.name} (${owner.label})`,
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return users;
|
||||
}
|
||||
}
|
||||
return ownersList;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Construct a dynamic API filter for the "project" field
|
||||
@ -142,6 +141,10 @@ function getVariantsTableFilters() {
|
||||
type: 'bool',
|
||||
title: '{% trans "Virtual" %}',
|
||||
},
|
||||
testable: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Testable" %}',
|
||||
},
|
||||
trackable: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Trackable" %}',
|
||||
@ -153,6 +156,10 @@ function getVariantsTableFilters() {
|
||||
// Return a dictionary of filters for the BOM table
|
||||
function getBOMTableFilters() {
|
||||
return {
|
||||
sub_part_testable: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Testable Part" %}',
|
||||
},
|
||||
sub_part_trackable: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Trackable Part" %}',
|
||||
@ -541,26 +548,8 @@ function getBuildTableFilters() {
|
||||
type: 'bool',
|
||||
title: '{% trans "Assigned to me" %}',
|
||||
},
|
||||
assigned_to: {
|
||||
title: '{% trans "Responsible" %}',
|
||||
options: function() {
|
||||
var ownersList = {};
|
||||
inventreeGet('{% url "api-owner-list" %}', {}, {
|
||||
async: false,
|
||||
success: function(response) {
|
||||
for (var key in response) {
|
||||
let owner = response[key];
|
||||
ownersList[owner.pk] = {
|
||||
key: owner.pk,
|
||||
value: `${owner.name} (${owner.label})`,
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
return ownersList;
|
||||
},
|
||||
},
|
||||
issued_by: constructIssuedByFilter(),
|
||||
assigned_to: constructOwnerFilter('{% trans "Responsible" %}'),
|
||||
issued_by: constructOwnerFilter('{% trans "Issued By" %}'),
|
||||
};
|
||||
|
||||
if (global_settings.PROJECT_CODES_ENABLED) {
|
||||
@ -785,6 +774,10 @@ function getPartTableFilters() {
|
||||
type: 'bool',
|
||||
title: '{% trans "Template" %}',
|
||||
},
|
||||
testable: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Testable" %}',
|
||||
},
|
||||
trackable: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Trackable" %}',
|
||||
|
@ -38,14 +38,14 @@
|
||||
"@mantine/spotlight": "^7.12.1",
|
||||
"@mantine/vanilla-extract": "^7.12.1",
|
||||
"@mdxeditor/editor": "^3.11.0",
|
||||
"@sentry/react": "^8.25.0",
|
||||
"@sentry/react": "^8.26.0",
|
||||
"@tabler/icons-react": "^3.12.0",
|
||||
"@tanstack/react-query": "^5.51.23",
|
||||
"@tanstack/react-query": "^5.51.24",
|
||||
"@uiw/codemirror-theme-vscode": "^4.23.0",
|
||||
"@uiw/react-codemirror": "^4.23.0",
|
||||
"@uiw/react-split": "^5.9.3",
|
||||
"@vanilla-extract/css": "^1.15.3",
|
||||
"axios": "^1.7.3",
|
||||
"@vanilla-extract/css": "^1.15.4",
|
||||
"axios": "^1.7.4",
|
||||
"clsx": "^2.1.0",
|
||||
"codemirror": ">=6.0.0",
|
||||
"dayjs": "^1.11.12",
|
||||
@ -59,12 +59,12 @@
|
||||
"react-grid-layout": "^1.4.4",
|
||||
"react-hook-form": "^7.52.2",
|
||||
"react-is": "^18.3.1",
|
||||
"react-router-dom": "^6.26.0",
|
||||
"react-router-dom": "^6.26.1",
|
||||
"react-select": "^5.8.0",
|
||||
"react-window": "^1.8.10",
|
||||
"recharts": "^2.12.4",
|
||||
"styled-components": "^6.1.12",
|
||||
"zustand": "^4.5.4"
|
||||
"zustand": "^4.5.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.25.2",
|
||||
@ -72,21 +72,21 @@
|
||||
"@babel/preset-typescript": "^7.24.7",
|
||||
"@lingui/cli": "^4.11.3",
|
||||
"@lingui/macro": "^4.11.3",
|
||||
"@playwright/test": "^1.46.0",
|
||||
"@types/node": "^22.2.0",
|
||||
"@playwright/test": "^1.46.1",
|
||||
"@types/node": "^22.4.1",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@types/react-grid-layout": "^1.3.5",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"@types/react-window": "^1.8.8",
|
||||
"@vanilla-extract/vite-plugin": "^4.0.13",
|
||||
"@vanilla-extract/vite-plugin": "^4.0.14",
|
||||
"@vitejs/plugin-react": "^4.3.1",
|
||||
"babel-plugin-macros": "^3.1.0",
|
||||
"nyc": "^17.0.0",
|
||||
"rollup-plugin-license": "^3.5.2",
|
||||
"typescript": "^5.5.4",
|
||||
"vite": "^5.4.0",
|
||||
"vite": "^5.4.1",
|
||||
"vite-plugin-babel-macros": "^1.0.6",
|
||||
"vite-plugin-istanbul": "^6.0.2"
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ import { ActionIcon, FloatingPosition, Group, Tooltip } from '@mantine/core';
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
import { identifierString } from '../../functions/conversion';
|
||||
import { notYetImplemented } from '../../functions/notifications';
|
||||
|
||||
export type ActionButtonProps = {
|
||||
icon?: ReactNode;
|
||||
@ -13,7 +12,7 @@ export type ActionButtonProps = {
|
||||
size?: number | string;
|
||||
radius?: number | string;
|
||||
disabled?: boolean;
|
||||
onClick?: any;
|
||||
onClick: (event?: any) => void;
|
||||
hidden?: boolean;
|
||||
tooltipAlignment?: FloatingPosition;
|
||||
};
|
||||
@ -42,7 +41,9 @@ export function ActionButton(props: ActionButtonProps) {
|
||||
aria-label={`action-button-${identifierString(
|
||||
props.tooltip ?? props.text ?? ''
|
||||
)}`}
|
||||
onClick={props.onClick ?? notYetImplemented}
|
||||
onClick={() => {
|
||||
props.onClick();
|
||||
}}
|
||||
variant={props.variant ?? 'transparent'}
|
||||
>
|
||||
<Group gap="xs" wrap="nowrap">
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { Button, Tooltip } from '@mantine/core';
|
||||
|
||||
import { InvenTreeIcon, InvenTreeIconType } from '../../functions/icons';
|
||||
import { notYetImplemented } from '../../functions/notifications';
|
||||
|
||||
/**
|
||||
* A "primary action" button for display on a page detail, (for example)
|
||||
@ -19,7 +18,7 @@ export default function PrimaryActionButton({
|
||||
icon?: InvenTreeIconType;
|
||||
color?: string;
|
||||
hidden?: boolean;
|
||||
onClick?: () => void;
|
||||
onClick: () => void;
|
||||
}) {
|
||||
if (hidden) {
|
||||
return null;
|
||||
@ -32,7 +31,7 @@ export default function PrimaryActionButton({
|
||||
color={color}
|
||||
radius="sm"
|
||||
p="xs"
|
||||
onClick={onClick ?? notYetImplemented}
|
||||
onClick={onClick}
|
||||
>
|
||||
{title}
|
||||
</Button>
|
||||
|
@ -92,7 +92,7 @@ export function PrintingActions({
|
||||
url: apiUrl(ApiEndpoints.label_print),
|
||||
title: t`Print Label`,
|
||||
fields: labelFields,
|
||||
timeout: (items.length + 1) * 1000,
|
||||
timeout: (items.length + 1) * 5000,
|
||||
onClose: () => {
|
||||
setPluginKey('');
|
||||
},
|
||||
@ -121,7 +121,7 @@ export function PrintingActions({
|
||||
const reportModal = useCreateApiFormModal({
|
||||
title: t`Print Report`,
|
||||
url: apiUrl(ApiEndpoints.report_print),
|
||||
timeout: (items.length + 1) * 1000,
|
||||
timeout: (items.length + 1) * 5000,
|
||||
fields: {
|
||||
template: {
|
||||
filters: {
|
||||
|
@ -2,15 +2,16 @@ import { t } from '@lingui/macro';
|
||||
import {
|
||||
Anchor,
|
||||
Badge,
|
||||
Group,
|
||||
Paper,
|
||||
Skeleton,
|
||||
Stack,
|
||||
Table,
|
||||
Text
|
||||
} from '@mantine/core';
|
||||
import { useSuspenseQuery } from '@tanstack/react-query';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { getValueAtPath } from 'mantine-datatable';
|
||||
import { Suspense, useCallback, useMemo } from 'react';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { api } from '../../App';
|
||||
@ -96,7 +97,7 @@ type FieldProps = {
|
||||
* Badge appends icon to describe type of Owner
|
||||
*/
|
||||
function NameBadge({ pk, type }: { pk: string | number; type: BadgeType }) {
|
||||
const { data } = useSuspenseQuery({
|
||||
const { data } = useQuery({
|
||||
queryKey: ['badge', type, pk],
|
||||
queryFn: async () => {
|
||||
let path: string = '';
|
||||
@ -111,6 +112,8 @@ function NameBadge({ pk, type }: { pk: string | number; type: BadgeType }) {
|
||||
case 'group':
|
||||
path = ApiEndpoints.group_list;
|
||||
break;
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
|
||||
const url = apiUrl(path, pk);
|
||||
@ -133,9 +136,13 @@ function NameBadge({ pk, type }: { pk: string | number; type: BadgeType }) {
|
||||
|
||||
const settings = useGlobalSettingsState();
|
||||
|
||||
if (!data || data.isLoading || data.isFetching) {
|
||||
return <Skeleton height={12} radius="md" />;
|
||||
}
|
||||
|
||||
// Rendering a user's rame for the badge
|
||||
function _render_name() {
|
||||
if (!data) {
|
||||
if (!data || !data.pk) {
|
||||
return '';
|
||||
} else if (type === 'user' && settings.isSet('DISPLAY_FULL_NAMES')) {
|
||||
if (data.first_name || data.last_name) {
|
||||
@ -151,18 +158,16 @@ function NameBadge({ pk, type }: { pk: string | number; type: BadgeType }) {
|
||||
}
|
||||
|
||||
return (
|
||||
<Suspense fallback={<Skeleton width={200} height={20} radius="xl" />}>
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Badge
|
||||
color="dark"
|
||||
variant="filled"
|
||||
style={{ display: 'flex', alignItems: 'center' }}
|
||||
>
|
||||
{data?.name ?? _render_name()}
|
||||
</Badge>
|
||||
<InvenTreeIcon icon={type === 'user' ? type : data.label} />
|
||||
</div>
|
||||
</Suspense>
|
||||
<Group wrap="nowrap" gap="sm" justify="right">
|
||||
<Badge
|
||||
color="dark"
|
||||
variant="filled"
|
||||
style={{ display: 'flex', alignItems: 'center' }}
|
||||
>
|
||||
{data?.name ?? _render_name()}
|
||||
</Badge>
|
||||
<InvenTreeIcon icon={type === 'user' ? type : data.label} />
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
|
||||
@ -195,15 +200,15 @@ function TableStringValue(props: Readonly<FieldProps>) {
|
||||
alignItems: 'flex-start'
|
||||
}}
|
||||
>
|
||||
<Suspense fallback={<Skeleton width={200} height={20} radius="xl" />}>
|
||||
<span>
|
||||
<Group wrap="nowrap" gap="xs" justify="space-apart">
|
||||
<Group wrap="nowrap" gap="xs" justify="left">
|
||||
{value ? value : props.field_data?.unit && '0'}{' '}
|
||||
{props.field_data.unit == true && props.unit}
|
||||
</span>
|
||||
</Suspense>
|
||||
{props.field_data.user && (
|
||||
<NameBadge pk={props.field_data?.user} type="user" />
|
||||
)}
|
||||
</Group>
|
||||
{props.field_data.user && (
|
||||
<NameBadge pk={props.field_data?.user} type="user" />
|
||||
)}
|
||||
</Group>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -215,7 +220,7 @@ function BooleanValue(props: Readonly<FieldProps>) {
|
||||
function TableAnchorValue(props: Readonly<FieldProps>) {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { data } = useSuspenseQuery({
|
||||
const { data } = useQuery({
|
||||
queryKey: ['detail', props.field_data.model, props.field_value],
|
||||
queryFn: async () => {
|
||||
if (!props.field_data?.model) {
|
||||
@ -260,6 +265,10 @@ function TableAnchorValue(props: Readonly<FieldProps>) {
|
||||
[detailUrl]
|
||||
);
|
||||
|
||||
if (!data || data.isLoading || data.isFetching) {
|
||||
return <Skeleton height={12} radius="md" />;
|
||||
}
|
||||
|
||||
if (props.field_data.external) {
|
||||
return (
|
||||
<Anchor
|
||||
@ -294,7 +303,7 @@ function TableAnchorValue(props: Readonly<FieldProps>) {
|
||||
}
|
||||
|
||||
return (
|
||||
<Suspense fallback={<Skeleton width={200} height={20} radius="xl" />}>
|
||||
<div>
|
||||
{make_link ? (
|
||||
<Anchor href="#" onClick={handleLinkClick}>
|
||||
<Text>{value}</Text>
|
||||
@ -302,7 +311,7 @@ function TableAnchorValue(props: Readonly<FieldProps>) {
|
||||
) : (
|
||||
<Text>{value}</Text>
|
||||
)}
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,11 @@ import { apiUrl } from '../../states/ApiState';
|
||||
import { TableColumn } from '../../tables/Column';
|
||||
import { TableFilter } from '../../tables/Filter';
|
||||
import { InvenTreeTable } from '../../tables/InvenTreeTable';
|
||||
import { RowDeleteAction, RowEditAction } from '../../tables/RowActions';
|
||||
import {
|
||||
RowAction,
|
||||
RowDeleteAction,
|
||||
RowEditAction
|
||||
} from '../../tables/RowActions';
|
||||
import { ActionButton } from '../buttons/ActionButton';
|
||||
import { YesNoButton } from '../buttons/YesNoButton';
|
||||
import { ApiFormFieldSet } from '../forms/fields/ApiFormField';
|
||||
@ -316,7 +320,7 @@ export default function ImporterDataSelector({
|
||||
}, [session]);
|
||||
|
||||
const rowActions = useCallback(
|
||||
(record: any) => {
|
||||
(record: any): RowAction[] => {
|
||||
return [
|
||||
{
|
||||
title: t`Accept`,
|
||||
|
@ -20,16 +20,15 @@ import { ReactNode, useMemo } from 'react';
|
||||
import { ModelType } from '../../enums/ModelType';
|
||||
import { identifierString } from '../../functions/conversion';
|
||||
import { InvenTreeIcon } from '../../functions/icons';
|
||||
import { notYetImplemented } from '../../functions/notifications';
|
||||
import { InvenTreeQRCode } from './QRCode';
|
||||
|
||||
export type ActionDropdownItem = {
|
||||
icon: ReactNode;
|
||||
name: string;
|
||||
icon?: ReactNode;
|
||||
name?: string;
|
||||
tooltip?: string;
|
||||
disabled?: boolean;
|
||||
hidden?: boolean;
|
||||
onClick?: () => void;
|
||||
onClick: (event?: any) => void;
|
||||
indicator?: Omit<IndicatorProps, 'children'>;
|
||||
};
|
||||
|
||||
@ -97,13 +96,7 @@ export function ActionDropdown({
|
||||
<Menu.Item
|
||||
aria-label={id}
|
||||
leftSection={action.icon}
|
||||
onClick={() => {
|
||||
if (action.onClick != undefined) {
|
||||
action.onClick();
|
||||
} else {
|
||||
notYetImplemented();
|
||||
}
|
||||
}}
|
||||
onClick={action.onClick}
|
||||
disabled={action.disabled}
|
||||
>
|
||||
{action.name}
|
||||
@ -159,131 +152,79 @@ export function ViewBarcodeAction({
|
||||
}
|
||||
|
||||
// Common action button for linking a custom barcode
|
||||
export function LinkBarcodeAction({
|
||||
hidden = false,
|
||||
onClick
|
||||
}: {
|
||||
hidden?: boolean;
|
||||
onClick?: () => void;
|
||||
}): ActionDropdownItem {
|
||||
export function LinkBarcodeAction(
|
||||
props: ActionDropdownItem
|
||||
): ActionDropdownItem {
|
||||
return {
|
||||
...props,
|
||||
icon: <IconLink />,
|
||||
name: t`Link Barcode`,
|
||||
tooltip: t`Link custom barcode`,
|
||||
onClick: onClick,
|
||||
hidden: hidden
|
||||
tooltip: t`Link custom barcode`
|
||||
};
|
||||
}
|
||||
|
||||
// Common action button for un-linking a custom barcode
|
||||
export function UnlinkBarcodeAction({
|
||||
hidden = false,
|
||||
onClick
|
||||
}: {
|
||||
hidden?: boolean;
|
||||
onClick?: () => void;
|
||||
}): ActionDropdownItem {
|
||||
export function UnlinkBarcodeAction(
|
||||
props: ActionDropdownItem
|
||||
): ActionDropdownItem {
|
||||
return {
|
||||
...props,
|
||||
icon: <IconUnlink />,
|
||||
name: t`Unlink Barcode`,
|
||||
tooltip: t`Unlink custom barcode`,
|
||||
onClick: onClick,
|
||||
hidden: hidden
|
||||
tooltip: t`Unlink custom barcode`
|
||||
};
|
||||
}
|
||||
|
||||
// Common action button for editing an item
|
||||
export function EditItemAction({
|
||||
hidden = false,
|
||||
tooltip,
|
||||
onClick
|
||||
}: {
|
||||
hidden?: boolean;
|
||||
tooltip?: string;
|
||||
onClick?: () => void;
|
||||
}): ActionDropdownItem {
|
||||
export function EditItemAction(props: ActionDropdownItem): ActionDropdownItem {
|
||||
return {
|
||||
...props,
|
||||
icon: <IconEdit color="blue" />,
|
||||
name: t`Edit`,
|
||||
tooltip: tooltip ?? `Edit item`,
|
||||
onClick: onClick,
|
||||
hidden: hidden
|
||||
tooltip: props.tooltip ?? t`Edit item`
|
||||
};
|
||||
}
|
||||
|
||||
// Common action button for deleting an item
|
||||
export function DeleteItemAction({
|
||||
hidden = false,
|
||||
disabled = false,
|
||||
tooltip,
|
||||
onClick
|
||||
}: {
|
||||
hidden?: boolean;
|
||||
disabled?: boolean;
|
||||
tooltip?: string;
|
||||
onClick?: () => void;
|
||||
}): ActionDropdownItem {
|
||||
export function DeleteItemAction(
|
||||
props: ActionDropdownItem
|
||||
): ActionDropdownItem {
|
||||
return {
|
||||
...props,
|
||||
icon: <IconTrash color="red" />,
|
||||
name: t`Delete`,
|
||||
tooltip: tooltip ?? t`Delete item`,
|
||||
onClick: onClick,
|
||||
hidden: hidden,
|
||||
disabled: disabled
|
||||
tooltip: props.tooltip ?? t`Delete item`
|
||||
};
|
||||
}
|
||||
|
||||
export function HoldItemAction({
|
||||
hidden = false,
|
||||
tooltip,
|
||||
onClick
|
||||
}: {
|
||||
hidden?: boolean;
|
||||
tooltip?: string;
|
||||
onClick?: () => void;
|
||||
}): ActionDropdownItem {
|
||||
export function HoldItemAction(props: ActionDropdownItem): ActionDropdownItem {
|
||||
return {
|
||||
...props,
|
||||
icon: <InvenTreeIcon icon="hold" iconProps={{ color: 'orange' }} />,
|
||||
name: t`Hold`,
|
||||
tooltip: tooltip ?? t`Hold`,
|
||||
onClick: onClick,
|
||||
hidden: hidden
|
||||
tooltip: props.tooltip ?? t`Hold`
|
||||
};
|
||||
}
|
||||
|
||||
export function CancelItemAction({
|
||||
hidden = false,
|
||||
tooltip,
|
||||
onClick
|
||||
}: {
|
||||
hidden?: boolean;
|
||||
tooltip?: string;
|
||||
onClick?: () => void;
|
||||
}): ActionDropdownItem {
|
||||
export function CancelItemAction(
|
||||
props: ActionDropdownItem
|
||||
): ActionDropdownItem {
|
||||
return {
|
||||
...props,
|
||||
icon: <InvenTreeIcon icon="cancel" iconProps={{ color: 'red' }} />,
|
||||
name: t`Cancel`,
|
||||
tooltip: tooltip ?? t`Cancel`,
|
||||
onClick: onClick,
|
||||
hidden: hidden
|
||||
tooltip: props.tooltip ?? t`Cancel`
|
||||
};
|
||||
}
|
||||
|
||||
// Common action button for duplicating an item
|
||||
export function DuplicateItemAction({
|
||||
hidden = false,
|
||||
tooltip,
|
||||
onClick
|
||||
}: {
|
||||
hidden?: boolean;
|
||||
tooltip?: string;
|
||||
onClick?: () => void;
|
||||
}): ActionDropdownItem {
|
||||
export function DuplicateItemAction(
|
||||
props: ActionDropdownItem
|
||||
): ActionDropdownItem {
|
||||
return {
|
||||
...props,
|
||||
icon: <IconCopy color="green" />,
|
||||
name: t`Duplicate`,
|
||||
tooltip: tooltip ?? t`Duplicate item`,
|
||||
onClick: onClick,
|
||||
hidden: hidden
|
||||
tooltip: props.tooltip ?? t`Duplicate item`
|
||||
};
|
||||
}
|
||||
|
@ -1,9 +1,18 @@
|
||||
import { Trans } from '@lingui/macro';
|
||||
import { Group, Menu, Skeleton, Text, UnstyledButton } from '@mantine/core';
|
||||
import {
|
||||
Group,
|
||||
Menu,
|
||||
Skeleton,
|
||||
Text,
|
||||
UnstyledButton,
|
||||
useMantineColorScheme
|
||||
} from '@mantine/core';
|
||||
import {
|
||||
IconChevronDown,
|
||||
IconLogout,
|
||||
IconMoonStars,
|
||||
IconSettings,
|
||||
IconSun,
|
||||
IconUserBolt,
|
||||
IconUserCog
|
||||
} from '@tabler/icons-react';
|
||||
@ -20,6 +29,7 @@ export function MainMenu() {
|
||||
state.user,
|
||||
state.username
|
||||
]);
|
||||
const { colorScheme, toggleColorScheme } = useMantineColorScheme();
|
||||
|
||||
return (
|
||||
<Menu width={260} position="bottom-end">
|
||||
@ -57,6 +67,15 @@ export function MainMenu() {
|
||||
<Trans>System Settings</Trans>
|
||||
</Menu.Item>
|
||||
)}
|
||||
<Menu.Item
|
||||
onClick={toggleColorScheme}
|
||||
leftSection={colorScheme === 'dark' ? <IconSun /> : <IconMoonStars />}
|
||||
c={
|
||||
colorScheme === 'dark' ? vars.colors.yellow[4] : vars.colors.blue[6]
|
||||
}
|
||||
>
|
||||
<Trans>Change Color Mode</Trans>
|
||||
</Menu.Item>
|
||||
{user?.is_staff && <Menu.Divider />}
|
||||
{user?.is_staff && (
|
||||
<Menu.Item
|
||||
|
@ -12,9 +12,9 @@ import {
|
||||
Text,
|
||||
Tooltip
|
||||
} from '@mantine/core';
|
||||
import { IconBellCheck, IconBellPlus } from '@tabler/icons-react';
|
||||
import { IconArrowRight, IconBellCheck } from '@tabler/icons-react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useMemo } from 'react';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
|
||||
import { api } from '../../App';
|
||||
@ -59,6 +59,19 @@ export function NotificationDrawer({
|
||||
return (notificationQuery.data?.results?.length ?? 0) > 0;
|
||||
}, [notificationQuery.data]);
|
||||
|
||||
const markAllAsRead = useCallback(() => {
|
||||
api
|
||||
.get(apiUrl(ApiEndpoints.notifications_readall), {
|
||||
params: {
|
||||
read: false
|
||||
}
|
||||
})
|
||||
.catch((_error) => {})
|
||||
.then((_response) => {
|
||||
notificationQuery.refetch();
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
opened={opened}
|
||||
@ -77,15 +90,29 @@ export function NotificationDrawer({
|
||||
title={
|
||||
<Group justify="space-between" wrap="nowrap">
|
||||
<StylishText size="lg">{t`Notifications`}</StylishText>
|
||||
<ActionIcon
|
||||
onClick={() => {
|
||||
onClose();
|
||||
navigate('/notifications/unread');
|
||||
}}
|
||||
variant="transparent"
|
||||
>
|
||||
<IconBellPlus />
|
||||
</ActionIcon>
|
||||
<Group justify="end" wrap="nowrap">
|
||||
<Tooltip label={t`Mark all as read`}>
|
||||
<ActionIcon
|
||||
variant="transparent"
|
||||
onClick={() => {
|
||||
markAllAsRead();
|
||||
}}
|
||||
>
|
||||
<IconBellCheck />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label={t`View all notifications`}>
|
||||
<ActionIcon
|
||||
onClick={() => {
|
||||
onClose();
|
||||
navigate('/notifications/unread');
|
||||
}}
|
||||
variant="transparent"
|
||||
>
|
||||
<IconArrowRight />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Group>
|
||||
</Group>
|
||||
}
|
||||
>
|
||||
|
@ -30,7 +30,7 @@ export function SettingsHeader({
|
||||
{shorthand && <Text c="dimmed">({shorthand})</Text>}
|
||||
</Group>
|
||||
<Group>
|
||||
<Text c="dimmed">{subtitle}</Text>
|
||||
{subtitle ? <Text c="dimmed">{subtitle}</Text> : null}
|
||||
{switch_text && switch_link && switch_condition && (
|
||||
<Anchor component={Link} to={switch_link}>
|
||||
<IconSwitch size={14} />
|
||||
|
@ -4,11 +4,13 @@ import { IconHome, IconLink, IconPointer } from '@tabler/icons-react';
|
||||
import { NavigateFunction } from 'react-router-dom';
|
||||
|
||||
import { useLocalState } from '../states/LocalState';
|
||||
import { useUserState } from '../states/UserState';
|
||||
import { aboutInvenTree, docLinks, licenseInfo, serverInfo } from './links';
|
||||
import { menuItems } from './menuItems';
|
||||
|
||||
export function getActions(navigate: NavigateFunction) {
|
||||
const setNavigationOpen = useLocalState((state) => state.setNavigationOpen);
|
||||
const { user } = useUserState();
|
||||
|
||||
const actions: SpotlightActionData[] = [
|
||||
{
|
||||
@ -62,5 +64,15 @@ export function getActions(navigate: NavigateFunction) {
|
||||
}
|
||||
];
|
||||
|
||||
// Staff actions
|
||||
user?.is_staff &&
|
||||
actions.push({
|
||||
id: 'admin-center',
|
||||
label: t`Admin Center`,
|
||||
description: t`Go to the Admin Center`,
|
||||
onClick: () => navigate(menuItems['settings-admin'].link),
|
||||
leftSection: <IconLink size="1.2rem" />
|
||||
});
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
@ -132,6 +132,7 @@ export enum ApiEndpoints {
|
||||
purchase_order_cancel = 'order/po/:id/cancel/',
|
||||
purchase_order_complete = 'order/po/:id/complete/',
|
||||
purchase_order_line_list = 'order/po-line/',
|
||||
purchase_order_extra_line_list = 'order/po-extra-line/',
|
||||
purchase_order_receive = 'order/po/:id/receive/',
|
||||
|
||||
sales_order_list = 'order/so/',
|
||||
@ -141,6 +142,7 @@ export enum ApiEndpoints {
|
||||
sales_order_ship = 'order/so/:id/ship/',
|
||||
sales_order_complete = 'order/so/:id/complete/',
|
||||
sales_order_line_list = 'order/so-line/',
|
||||
sales_order_extra_line_list = 'order/so-extra-line/',
|
||||
sales_order_allocation_list = 'order/so-allocation/',
|
||||
sales_order_shipment_list = 'order/so/shipment/',
|
||||
|
||||
@ -150,6 +152,7 @@ export enum ApiEndpoints {
|
||||
return_order_cancel = 'order/ro/:id/cancel/',
|
||||
return_order_complete = 'order/ro/:id/complete/',
|
||||
return_order_line_list = 'order/ro-line/',
|
||||
return_order_extra_line_list = 'order/ro-extra-line/',
|
||||
|
||||
// Template API endpoints
|
||||
label_list = 'label/template/',
|
||||
|
@ -19,3 +19,18 @@ export function customUnitsFields(): ApiFormFieldSet {
|
||||
symbol: {}
|
||||
};
|
||||
}
|
||||
|
||||
export function extraLineItemFields(): ApiFormFieldSet {
|
||||
return {
|
||||
order: {
|
||||
hidden: true
|
||||
},
|
||||
reference: {},
|
||||
description: {},
|
||||
quantity: {},
|
||||
price: {},
|
||||
price_currency: {},
|
||||
notes: {},
|
||||
link: {}
|
||||
};
|
||||
}
|
||||
|
@ -55,6 +55,7 @@ export function usePartFields({
|
||||
component: {},
|
||||
assembly: {},
|
||||
is_template: {},
|
||||
testable: {},
|
||||
trackable: {},
|
||||
purchaseable: {},
|
||||
salable: {},
|
||||
@ -140,7 +141,11 @@ export function partCategoryFields(): ApiFormFieldSet {
|
||||
return fields;
|
||||
}
|
||||
|
||||
export function usePartParameterFields(): ApiFormFieldSet {
|
||||
export function usePartParameterFields({
|
||||
editTemplate
|
||||
}: {
|
||||
editTemplate?: boolean;
|
||||
}): ApiFormFieldSet {
|
||||
// Valid field choices
|
||||
const [choices, setChoices] = useState<any[]>([]);
|
||||
|
||||
@ -155,6 +160,7 @@ export function usePartParameterFields(): ApiFormFieldSet {
|
||||
disabled: true
|
||||
},
|
||||
template: {
|
||||
disabled: editTemplate == false,
|
||||
onValueChange: (value: any, record: any) => {
|
||||
// Adjust the type of the "data" field based on the selected template
|
||||
if (record?.checkbox) {
|
||||
@ -194,5 +200,5 @@ export function usePartParameterFields(): ApiFormFieldSet {
|
||||
}
|
||||
}
|
||||
};
|
||||
}, [fieldType, choices]);
|
||||
}, [editTemplate, fieldType, choices]);
|
||||
}
|
||||
|
@ -27,6 +27,8 @@ import {
|
||||
IconCornerUpRightDouble,
|
||||
IconCurrencyDollar,
|
||||
IconDots,
|
||||
IconEdit,
|
||||
IconExclamationCircle,
|
||||
IconExternalLink,
|
||||
IconFileUpload,
|
||||
IconFlag,
|
||||
@ -109,7 +111,9 @@ const icons = {
|
||||
units: IconRulerMeasure,
|
||||
keywords: IconTag,
|
||||
status: IconInfoCircle,
|
||||
edit: IconEdit,
|
||||
info: IconInfoCircle,
|
||||
exclamation: IconExclamationCircle,
|
||||
details: IconInfoCircle,
|
||||
parameters: IconList,
|
||||
list: IconList,
|
||||
@ -130,6 +134,7 @@ const icons = {
|
||||
shipment: IconTruckDelivery,
|
||||
scheduling: IconCalendarStats,
|
||||
test_templates: IconTestPipe,
|
||||
test: IconTestPipe,
|
||||
related_parts: IconLayersLinked,
|
||||
attachments: IconPaperclip,
|
||||
note: IconNotes,
|
||||
|
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
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user