Compare commits

...

30 Commits
1.0.0 ... 1.0.3

Author SHA1 Message Date
github-actions[bot]
555b72305c Update docs regarding INVENTREE_AUTO_UPDATE (#10505) (#10507)
(cherry picked from commit 363c5fd2c6)

Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
2025-10-06 22:07:56 +11:00
Matthias Mair
9819442697 bump contianer / dev dependencies too (#10472) (#10496)
Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
2025-10-06 21:57:34 +11:00
github-actions[bot]
f901eec771 Fix rendering for custom status values (#10492) (#10494)
- Improve field lookup priority

(cherry picked from commit b370d54394)

Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
2025-10-05 22:39:04 +11:00
github-actions[bot]
3044cee011 Tweak docker setup (#10490) (#10493)
- Set default forwarding values

(cherry picked from commit 946d4358c3)

Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
2025-10-05 22:12:35 +11:00
github-actions[bot]
17be1fbb43 feat(ui/choice field): select old content on click, auto select first row (#10485) (#10491)
closes #10468, #10470

(cherry picked from commit 4635ea9443)

Co-authored-by: Hamza Ali <github+hhhapz@hamza.sh>
2025-10-05 20:00:08 +11:00
github-actions[bot]
d2aa15257b feat(backend): releax protocol check (#10454) (#10460)
* feat(backend): releax protocol check

* add changelog entry

* fix variable name

---------


(cherry picked from commit 7638092ab8)

Co-authored-by: Matthias Mair <code@mjmair.com>
Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
2025-10-02 21:56:10 +10:00
github-actions[bot]
6ff28da48f chore(deps): bump django from 4.2.24 to 4.2.25 in /src/backend (#10448) (#10455)
* chore(deps): bump django from 4.2.24 to 4.2.25 in /src/backend

Bumps [django](https://github.com/django/django) from 4.2.24 to 4.2.25.
- [Commits](https://github.com/django/django/compare/4.2.24...4.2.25)

---
updated-dependencies:
- dependency-name: django
  dependency-version: 4.2.25
  dependency-type: direct:production
...



* fix style

---------




(cherry picked from commit b8b1fabc50)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Matthias Mair <code@mjmair.com>
2025-10-02 20:43:23 +10:00
github-actions[bot]
bd62363b9a [UI] Fix total price for Purchase Order table (#10450) (#10452)
- Closes https://github.com/inventree/InvenTree/issues/10442

(cherry picked from commit bfd32c9864)

Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
2025-10-02 11:41:24 +10:00
github-actions[bot]
acc85672e1 Improve printing actions (#10430) (#10432)
- Ensure correct items are specified

(cherry picked from commit 867cd24088)

Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
2025-09-30 09:52:43 +10:00
Oliver
3e9fc94d63 Bump software version to 1.0.3 (#10425) 2025-09-29 15:22:52 +10:00
github-actions[bot]
1d2700fa6f [UI] Consume tracked (#10422) (#10424)
* Prevent manual consumption of tracked stock

* Prevent manual consuming of trackable items

(cherry picked from commit b0a60ed963)

Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
2025-09-29 14:01:43 +10:00
github-actions[bot]
9c170e6ed3 Fix locale formatting for calendar display (#10418) (#10420)
- Cannot accept underscore

(cherry picked from commit bcc386aecf)

Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
2025-09-29 11:31:49 +10:00
github-actions[bot]
542a75ce58 [UI] Enable printing of build lines (#10403) (#10410)
* [UI] Enable printing of build lines

- Closes https://github.com/inventree/InvenTree/issues/10402

* Prevent cell click action

(cherry picked from commit e897222e07)

Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
2025-09-27 14:30:43 +10:00
github-actions[bot]
06750358d6 [bug] Auto allocate bugfix (#10398) (#10407)
* Fix "unallocated_quantity" calculation

- Take "consumed" quantity into account also

* Account for consumed quantity in:

- build.is_fully_allocated
- build.is_overallocated

* Additional unit tests

- Ensure the new calculations work properly

* Adjust API filter

* Try splitting query

* Another fix

* Try ExpressionWrapper

* Change order of operations?

* Refactor

* Adjust filtering strategy

* Change ordering

* Use Max wrapper

* Add comments

(cherry picked from commit 6fdc6b3a8c)

Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
2025-09-27 10:23:06 +10:00
github-actions[bot]
6ccc4544be Fix typo (#10400) (#10401)
(cherry picked from commit 52be30eef5)

Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
2025-09-26 13:47:10 +10:00
github-actions[bot]
ac324cff14 Tweak build line table (#10397) (#10399)
- Show allocated quantity even if fully consumed
- Handles edge case where fully consumed but more stock allocated

(cherry picked from commit 1670523dab)

Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
2025-09-26 11:37:09 +10:00
github-actions[bot]
98b1678402 fix: correct user deletion (#10385) (#10386)
* Ensure all user sessions are cleared

* remove double warning text on user delete

(cherry picked from commit 4794d69687)

Co-authored-by: Matthias Mair <code@mjmair.com>
2025-09-24 10:30:29 +10:00
github-actions[bot]
da3ebacf6b Hide "consume" action when not viewed from build page (#10378) (#10379)
(cherry picked from commit a7b1b9d523)

Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
2025-09-23 21:08:54 +10:00
github-actions[bot]
24b2401f6a Handle null user case (#10362) (#10365)
(cherry picked from commit 2f357587bc)

Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
2025-09-20 13:32:45 +10:00
github-actions[bot]
d12c335032 Support import of "choice" fields (#10361) (#10364)
- Perform reverse lookup of display value

(cherry picked from commit bbfdcdce73)

Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
2025-09-20 12:56:53 +10:00
Oliver
eae580deaf Bump software version to 1.0.2 (#10360) 2025-09-20 10:15:49 +10:00
github-actions[bot]
6207aa6c32 Improved error handling (#10352) (#10354)
- Closes https://github.com/inventree/InvenTree/issues/10338

(cherry picked from commit f4333bd83f)

Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
2025-09-19 18:43:00 +10:00
github-actions[bot]
5ff7ce4703 fix(backend): better siteurl testing in middleware (#10335) (#10353)
* fix(backend): simplify siteurl testing

* add multi-site test

* pass off site_url check if more than one trusted origin is set

* split up testing

* add temporary debug info

* fix test enviorment

(cherry picked from commit 4b0acad518)

Co-authored-by: Matthias Mair <code@mjmair.com>
2025-09-19 18:42:33 +10:00
github-actions[bot]
0350623866 [UI] Display Stock link (#10350) (#10351)
- Display "link" for stock item

(cherry picked from commit 843dd92901)

Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
2025-09-19 10:50:31 +10:00
github-actions[bot]
bb576b16d8 fix bug I introduced with automatic EmailAddress creation for LDAP users (#10347) (#10349)
(cherry picked from commit fd57b5354b)

Co-authored-by: Jacob Felknor <jacobfelknor073@gmail.com>
2025-09-19 08:14:03 +10:00
github-actions[bot]
9705ea90c2 UI panels fix (#10341) (#10342)
* Tweak sample plugin

* Re-fetch panels when instance changes

* Unit test fix

(cherry picked from commit df0e27bed2)

Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
2025-09-18 13:31:43 +10:00
github-actions[bot]
39dc2b17fd Fix for RenderStockItem (#10336) (#10337)
- Handle case where serial number is empty string

(cherry picked from commit f057247fc1)

Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
2025-09-17 13:47:45 +10:00
github-actions[bot]
00646b0891 Exclude field from stock-item import (#10333) (#10334)
(cherry picked from commit a6e555708f)

Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
2025-09-17 10:05:27 +10:00
Oliver
2b7627a940 Bump dummy plugin version (#10330)
* Bump dummy plugin version

- Required, else newer versions fail CI

* Bump version number to 1.0.1
2025-09-16 11:45:33 +10:00
github-actions[bot]
048ece4797 Fix user defined radius (#10327) (#10328)
- Observe correct radius values
- Closes https://github.com/inventree/InvenTree/issues/10322

(cherry picked from commit 5727999d4d)

Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
2025-09-16 10:25:28 +10:00
37 changed files with 733 additions and 394 deletions

View File

@@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Changed site URL check to allow protocol mismatches if `INVENTREE_SITE_LAX_PROTOCOL` is set to `True` (default) in [#10454](https://github.com/inventree/InvenTree/pull/10454)
### Removed

View File

@@ -13,6 +13,11 @@ INVENTREE_SITE_URL="http://inventree.localhost"
#INVENTREE_SITE_URL="http://192.168.1.2" # You can specify a local IP address here
#INVENTREE_SITE_URL="https://inventree.my-domain.com" # Or a public domain name (which you control)
# InvenTree proxy forwarding settings
INVENTREE_USE_X_FORWARDED_HOST=True
INVENTREE_USE_X_FORWARDED_PORT=True
INVENTREE_USE_X_FORWARDED_PROTO=True
# Specify the location of the external data volume
# By default, placed in local directory 'inventree-data'
INVENTREE_EXT_VOLUME=./inventree-data
@@ -23,7 +28,8 @@ INVENTREE_LOG_LEVEL=WARNING
# Enable custom plugins?
INVENTREE_PLUGINS_ENABLED=True
# Run migrations automatically?
# Run database migrations automatically?
# Note: This does not negate the need to run "invoke update"
INVENTREE_AUTO_UPDATE=True
# InvenTree superuser account details

View File

@@ -1,12 +1,12 @@
# This file was autogenerated by uv via the following command:
# uv pip compile contrib/container/requirements.in -o contrib/container/requirements.txt --python-version=3.11 --no-strip-extras --generate-hashes -b src/backend/requirements.txt
asgiref==3.9.1 \
--hash=sha256:a5ab6582236218e5ef1648f242fd9f10626cfd4de8dc377db215d5d5098e3142 \
--hash=sha256:f3bba7092a48005b5f5bacd747d36ee4a5a61f4a269a6df590b43144355ebd2c
asgiref==3.9.2 \
--hash=sha256:0b61526596219d70396548fc003635056856dba5d0d086f86476f10b33c75960 \
--hash=sha256:a0249afacb66688ef258ffe503528360443e2b9a8d8c4581b6ebefa58c841ef1
# via django
django==4.2.23 \
--hash=sha256:42fdeaba6e6449d88d4f66de47871015097dc6f1b87910db00a91946295cfae4 \
--hash=sha256:dafbfaf52c2f289bd65f4ab935791cb4fb9a198f2a5ba9faf35d7338a77e9803
django==4.2.25 \
--hash=sha256:2391ab3d78191caaae2c963c19fd70b99e9751008da22a0adcc667c5a4f8d311 \
--hash=sha256:9584cf26b174b35620e53c2558b09d7eb180a655a3470474f513ff9acb494f8c
# via
# -r contrib/container/requirements.in
# django-auth-ldap
@@ -51,76 +51,73 @@ packaging==25.0 \
# via
# gunicorn
# mariadb
psycopg[binary, pool]==3.2.9 \
--hash=sha256:01a8dadccdaac2123c916208c96e06631641c0566b22005493f09663c7a8d3b6 \
--hash=sha256:2fbb46fcd17bc81f993f28c47f1ebea38d66ae97cc2dbc3cad73b37cefbff700
psycopg[binary, pool]==3.2.10 \
--hash=sha256:0bce99269d16ed18401683a8569b2c5abd94f72f8364856d56c0389bcd50972a \
--hash=sha256:ab5caf09a9ec42e314a21f5216dbcceac528e0e05142e42eea83a3b28b320ac3
# via -r contrib/container/requirements.in
psycopg-binary==3.2.9 \
--hash=sha256:001e986656f7e06c273dd4104e27f4b4e0614092e544d950c7c938d822b1a894 \
--hash=sha256:08bf9d5eabba160dd4f6ad247cf12f229cc19d2458511cab2eb9647f42fa6795 \
--hash=sha256:093a0c079dd6228a7f3c3d82b906b41964eaa062a9a8c19f45ab4984bf4e872b \
--hash=sha256:0e8aeefebe752f46e3c4b769e53f1d4ad71208fe1150975ef7662c22cca80fab \
--hash=sha256:14f64d1ac6942ff089fc7e926440f7a5ced062e2ed0949d7d2d680dc5c00e2d4 \
--hash=sha256:166acc57af5d2ff0c0c342aed02e69a0cd5ff216cae8820c1059a6f3b7cf5f78 \
--hash=sha256:18ac08475c9b971237fcc395b0a6ee4e8580bb5cf6247bc9b8461644bef5d9f4 \
--hash=sha256:1b2cf018168cad87580e67bdde38ff5e51511112f1ce6ce9a8336871f465c19a \
--hash=sha256:1ed2bab85b505d13e66a914d0f8cdfa9475c16d3491cf81394e0748b77729af2 \
--hash=sha256:1f1736d5b21f69feefeef8a75e8d3bf1f0a1e17c165a7488c3111af9d6936e91 \
--hash=sha256:2290bc146a1b6a9730350f695e8b670e1d1feb8446597bed0bbe7c3c30e0abcb \
--hash=sha256:24ddb03c1ccfe12d000d950c9aba93a7297993c4e3905d9f2c9795bb0764d523 \
--hash=sha256:2504e9fd94eabe545d20cddcc2ff0da86ee55d76329e1ab92ecfcc6c0a8156c4 \
--hash=sha256:25ab464bfba8c401f5536d5aa95f0ca1dd8257b5202eede04019b4415f491351 \
--hash=sha256:354dea21137a316b6868ee41c2ae7cce001e104760cf4eab3ec85627aed9b6cd \
--hash=sha256:387c87b51d72442708e7a853e7e7642717e704d59571da2f3b29e748be58c78a \
--hash=sha256:39a127e0cf9b55bd4734a8008adf3e01d1fd1cb36339c6a9e2b2cbb6007c50ee \
--hash=sha256:3db3ba3c470801e94836ad78bf11fd5fab22e71b0c77343a1ee95d693879937a \
--hash=sha256:413f9e46259fe26d99461af8e1a2b4795a4e27cc8ac6f7919ec19bcee8945074 \
--hash=sha256:418f52b77b715b42e8ec43ee61ca74abc6765a20db11e8576e7f6586488a266f \
--hash=sha256:4bfec4a73e8447d8fe8854886ffa78df2b1c279a7592241c2eb393d4499a17e2 \
--hash=sha256:4c1ab25e3134774f1e476d4bb9050cdec25f10802e63e92153906ae934578734 \
--hash=sha256:4df22ec17390ec5ccb38d211fb251d138d37a43344492858cea24de8efa15003 \
--hash=sha256:528239bbf55728ba0eacbd20632342867590273a9bacedac7538ebff890f1093 \
--hash=sha256:52e239cd66c4158e412318fbe028cd94b0ef21b0707f56dcb4bdc250ee58fd40 \
--hash=sha256:587a3f19954d687a14e0c8202628844db692dbf00bba0e6d006659bf1ca91cbe \
--hash=sha256:5918c0fab50df764812f3ca287f0d716c5c10bedde93d4da2cefc9d40d03f3aa \
--hash=sha256:5be8292d07a3ab828dc95b5ee6b69ca0a5b2e579a577b39671f4f5b47116dfd2 \
--hash=sha256:5d2c9fe14fe42b3575a0b4e09b081713e83b762c8dc38a3771dd3265f8f110e7 \
--hash=sha256:61d0a6ceed8f08c75a395bc28cb648a81cf8dee75ba4650093ad1a24a51c8724 \
--hash=sha256:6a76b4722a529390683c0304501f238b365a46b1e5fb6b7249dbc0ad6fea51a0 \
--hash=sha256:6afb3e62f2a3456f2180a4eef6b03177788df7ce938036ff7f09b696d418d186 \
--hash=sha256:72691a1615ebb42da8b636c5ca9f2b71f266be9e172f66209a361c175b7842c5 \
--hash=sha256:72fdbda5b4c2a6a72320857ef503a6589f56d46821592d4377c8c8604810342b \
--hash=sha256:76eddaf7fef1d0994e3d536ad48aa75034663d3a07f6f7e3e601105ae73aeff6 \
--hash=sha256:778588ca9897b6c6bab39b0d3034efff4c5438f5e3bd52fda3914175498202f9 \
--hash=sha256:791759138380df21d356ff991265fde7fe5997b0c924a502847a9f9141e68786 \
--hash=sha256:799fa1179ab8a58d1557a95df28b492874c8f4135101b55133ec9c55fc9ae9d7 \
--hash=sha256:7a838852e5afb6b4126f93eb409516a8c02a49b788f4df8b6469a40c2157fa21 \
--hash=sha256:7b617b81f08ad8def5edd110de44fd6d326f969240cc940c6f6b3ef21fe9c59f \
--hash=sha256:7e4660fad2807612bb200de7262c88773c3483e85d981324b3c647176e41fdc8 \
--hash=sha256:7fc2915949e5c1ea27a851f7a472a7da7d0a40d679f0a31e42f1022f3c562e87 \
--hash=sha256:95315b8c8ddfa2fdcb7fe3ddea8a595c1364524f512160c604e3be368be9dd07 \
--hash=sha256:96a551e4683f1c307cfc3d9a05fec62c00a7264f320c9962a67a543e3ce0d8ff \
--hash=sha256:98bbe35b5ad24a782c7bf267596638d78aa0e87abc7837bdac5b2a2ab954179e \
--hash=sha256:a1fa38a4687b14f517f049477178093c39c2a10fdcced21116f47c017516498f \
--hash=sha256:a3e0f89fe35cb03ff1646ab663dabf496477bab2a072315192dbaa6928862891 \
--hash=sha256:a4d76e28df27ce25dc19583407f5c6c6c2ba33b443329331ab29b6ef94c8736d \
--hash=sha256:ac2c04b6345e215e65ca6aef5c05cc689a960b16674eaa1f90a8f86dfaee8c04 \
--hash=sha256:ad280bbd409bf598683dda82232f5215cfc5f2b1bf0854e409b4d0c44a113b1d \
--hash=sha256:b2d7a6646d41228e9049978be1f3f838b557a1bde500b919906d54c4390f5086 \
--hash=sha256:b7e4e4dd177a8665c9ce86bc9caae2ab3aa9360b7ce7ec01827ea1baea9ff748 \
--hash=sha256:bb37ac3955d19e4996c3534abfa4f23181333974963826db9e0f00731274b695 \
--hash=sha256:bc75f63653ce4ec764c8f8c8b0ad9423e23021e1c34a84eb5f4ecac8538a4a4a \
--hash=sha256:be7d650a434921a6b1ebe3fff324dbc2364393eb29d7672e638ce3e21076974e \
--hash=sha256:cc19ed5c7afca3f6b298bfc35a6baa27adb2019670d15c32d0bb8f780f7d560d \
--hash=sha256:cf789be42aea5752ee396d58de0538d5fcb76795c85fb03ab23620293fb81b6f \
--hash=sha256:d9ac10a2ebe93a102a326415b330fff7512f01a9401406896e78a81d75d6eddc \
--hash=sha256:e0f05b9dafa5670a7503abc715af081dbbb176a8e6770de77bccaeb9024206c5 \
--hash=sha256:e4978c01ca4c208c9d6376bd585e2c0771986b76ff7ea518f6d2b51faece75e8 \
--hash=sha256:eac3a6e926421e976c1c2653624e1294f162dc67ac55f9addbe8f7b8d08ce603 \
--hash=sha256:f0d5b3af045a187aedbd7ed5fc513bd933a97aaff78e61c3745b330792c4345b \
--hash=sha256:f34e88940833d46108f949fdc1fcfb74d6b5ae076550cd67ab59ef47555dba95 \
--hash=sha256:fa5c80d8b4cbf23f338db88a7251cef8bb4b68e0f91cf8b6ddfa93884fdbb0c1 \
--hash=sha256:fb7599e436b586e265bea956751453ad32eb98be6a6e694252f4691c31b16edb
psycopg-binary==3.2.10 \
--hash=sha256:037dc92fc7d3f2adae7680e17216934c15b919d6528b908ac2eb52aecc0addcf \
--hash=sha256:0738320a8d405f98743227ff70ed8fac9670870289435f4861dc640cef4a61d3 \
--hash=sha256:0c23e88e048bbc33f32f5a35981707c9418723d469552dd5ac4e956366e58492 \
--hash=sha256:0c2b95e83fda70ed2b0b4fadd8538572e4a4d987b721823981862d1ab56cc760 \
--hash=sha256:14bcbcac0cab465d88b2581e43ec01af4b01c9833e663f1352e05cb41be19e44 \
--hash=sha256:183a59cbdcd7e156669577fd73a9e917b1ee664e620f1e31ae138d24c7714693 \
--hash=sha256:1b29285474e3339d0840e1b5079fdb0481914108f92ec62de0c87ae333c60b24 \
--hash=sha256:1dee2f4d2adc9adacbfecf8254bd82f6ac95cff707e1b9b99aa721cd1ef16b47 \
--hash=sha256:1f6982609b8ff8fcd67299b67cd5787da1876f3bb28fedd547262cfa8ddedf94 \
--hash=sha256:2028073fc12cd70ba003309d1439c0c4afab4a7eee7653b8c91213064fffe12b \
--hash=sha256:20384985fbc650c09a547a13c6d7f91bb42020d38ceafd2b68b7fc4a48a1f160 \
--hash=sha256:299834cce3eec0c48aae5a5207fc8f0c558fd65f2ceab1a36693329847da956b \
--hash=sha256:29b6bb87959515bc8b6abef10d8d23a9a681f03e48e9f0c8adb4b9fb7fa73f11 \
--hash=sha256:3234605839e7d7584bd0a20716395eba34d368a5099dafe7896c943facac98fc \
--hash=sha256:37b42b2f5f58df1f07a5df1b0c2bcc9bd3b9c105e2e988923bfa47aa4ae967da \
--hash=sha256:3bb4046973264ebc8cb7e20a83882d68577c1f26a6f8ad4fe52e4468cd9a8eee \
--hash=sha256:3e115930af2f38f4bbb5f1b61b598ceb802f091c1592c0fe0571c796b714b89a \
--hash=sha256:42ee399c2613b470a87084ed79b06d9d277f19b0457c10e03a4aef7059097abc \
--hash=sha256:43d803fb4e108a67c78ba58f3e6855437ca25d56504cae7ebbfbd8fce9b59247 \
--hash=sha256:447afc326cbc95ed67c0cd27606c0f81fa933b830061e096dbd37e08501cb3de \
--hash=sha256:470594d303928ab72a1ffd179c9c7bde9d00f76711d6b0c28f8a46ddf56d9807 \
--hash=sha256:484d2b1659afe0f8f1cef5ea960bb640e96fa864faf917086f9f833f5c7a8034 \
--hash=sha256:50130c0d1a2a01ec3d41631df86b6c1646c76718be000600a399dc1aad80b813 \
--hash=sha256:5334a61a00ccb722f0b28789e265c7a273cfd10d5a1ed6bf062686fbb71e7032 \
--hash=sha256:5369202e0e764193eac311b5a337d8cd58b1e23b822ddb7a559ed9f683d97623 \
--hash=sha256:55b14f2402be027fe1568bc6c4d75ac34628ff5442a70f74137dadf99f738e3b \
--hash=sha256:6220d6efd6e2df7b67d70ed60d653106cd3b70c5cb8cbe4e9f0a142a5db14015 \
--hash=sha256:62590dd113d10cd9c08251cb80b32e2e8aaf01ece04a700322e776b1d216959f \
--hash=sha256:646048f46192c8d23786cc6ef19f35b7488d4110396391e407eca695fdfe9dcd \
--hash=sha256:6fe450a98a0788b721b1b8302f0ba9be6eca82faf74bf7a86d794cd6484c7e27 \
--hash=sha256:70bb7f665587dfd79e69f48b34efe226149454d7aab138ed22d5431d703de2f6 \
--hash=sha256:725843fd444075cc6c9989f5b25ca83ac68d8d70b58e1f476fbb4096975e43cc \
--hash=sha256:764a5b9b40ad371c55dfdf95374d89e44a82fd62272d4fceebea0adb8930e2fb \
--hash=sha256:7950ff79df7a453ac8a7d7a74694055b6c15905b0a2b6e3c99eb59c51a3f9bf7 \
--hash=sha256:7fa1626225a162924d2da0ff4ef77869f7a8501d320355d2732be5bf2dda6138 \
--hash=sha256:810f65b9ef1fe9dddb5c05937884ea9563aaf4e1a2c3d138205231ed5f439511 \
--hash=sha256:8390db6d2010ffcaf7f2b42339a2da620a7125d37029c1f9b72dfb04a8e7be6f \
--hash=sha256:84f7e8c5e5031db342ae697c2e8fb48cd708ba56990573b33e53ce626445371d \
--hash=sha256:8923487c3898c65e1450847e15d734bb2e6adbd2e79d2d1dd5ad829a1306bdc0 \
--hash=sha256:89440355d1b163b11dc661ae64a5667578aab1b80bbf71ced90693d88e9863e1 \
--hash=sha256:8b45e65383da9c4a42a56f817973e521e893f4faae897fe9f1a971f9fe799742 \
--hash=sha256:8f4ae059c6c9e491cdc3f39f9fc4f09373ef281c6cc381499269dcff21abafc9 \
--hash=sha256:8fa2efaf5e2f8c289a185c91c80a624a8f97aa17fbedcbc68f373d089b332afd \
--hash=sha256:901729188b3fd5625970650ca1167786847dee0b92930c2858724d1a5e25dee1 \
--hash=sha256:9c9f2728488ac5848acdbf14bb4fde50f8ba783cbf3c19e9abd506741389fa7f \
--hash=sha256:a024b3ee539a475cbc59df877c8ecdd6f8552a1b522b69196935bc26dc6152fb \
--hash=sha256:a1d4e4d309049e3cb61269652a3ca56cb598da30ecd7eb8cea561e0d18bc1a43 \
--hash=sha256:a28f24a7b68456bd31209b027a5b04304d37eb1d622ef847bf8c47933218a738 \
--hash=sha256:a5a81104d88780018005fe17c37fa55b4afbb6dd3c205963cc56c025d5f1cc32 \
--hash=sha256:a92ff1c2cd79b3966d6a87e26ceb222ecd5581b5ae4b58961f126af806a861ed \
--hash=sha256:ab1c6d761c4ee581016823dcc02f29b16ad69177fcbba88a9074c924fc31813e \
--hash=sha256:ac0365398947879c9827b319217096be727da16c94422e0eb3cf98c930643162 \
--hash=sha256:b34c278a58aa79562afe7f45e0455b1f4cad5974fc3d5674cc5f1f9f57e97fc5 \
--hash=sha256:bd3676a04970cf825d2c771b0c147f91182c5a3653e0dbe958e12383668d0f79 \
--hash=sha256:bf30dcf6aaaa8d4779a20d2158bdf81cc8e84ce8eee595d748a7671c70c7b890 \
--hash=sha256:d2fe9eaa367f6171ab1a21a7dcb335eb2398be7f8bb7e04a20e2260aedc6f782 \
--hash=sha256:d557a94cd6d2e775b3af6cc0bd0ff0d9d641820b5cc3060ccf1f5ca2bf971217 \
--hash=sha256:d5c6a66a76022af41970bf19f51bc6bf87bd10165783dd1d40484bfd87d6b382 \
--hash=sha256:d7d05174276bb403b8a57e01b857d96b0ac2a6879c5ce06a5cac2d1115763081 \
--hash=sha256:d922fdd49ed17c558b6b2f9ae2054c3d0cced2a34e079ce5a41c86904d0203f7 \
--hash=sha256:db0eb06a19e4c64a08db0db80875ede44939af6a2afc281762c338fad5d6e547 \
--hash=sha256:e037aac8dc894d147ef33056fc826ee5072977107a3fdf06122224353a057598
# via psycopg
psycopg-pool==3.2.6 \
--hash=sha256:0f92a7817719517212fbfe2fd58b8c35c1850cdd2a80d36b581ba2085d9148e5 \
@@ -141,60 +138,80 @@ python-ldap==3.4.4 \
# via
# -r contrib/container/requirements.in
# django-auth-ldap
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
pyyaml==6.0.3 \
--hash=sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c \
--hash=sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a \
--hash=sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3 \
--hash=sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956 \
--hash=sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6 \
--hash=sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c \
--hash=sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65 \
--hash=sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a \
--hash=sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0 \
--hash=sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b \
--hash=sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1 \
--hash=sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6 \
--hash=sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7 \
--hash=sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e \
--hash=sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007 \
--hash=sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310 \
--hash=sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4 \
--hash=sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9 \
--hash=sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295 \
--hash=sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea \
--hash=sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0 \
--hash=sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e \
--hash=sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac \
--hash=sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9 \
--hash=sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7 \
--hash=sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35 \
--hash=sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb \
--hash=sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b \
--hash=sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69 \
--hash=sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5 \
--hash=sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b \
--hash=sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c \
--hash=sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369 \
--hash=sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd \
--hash=sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824 \
--hash=sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198 \
--hash=sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065 \
--hash=sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c \
--hash=sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c \
--hash=sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764 \
--hash=sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196 \
--hash=sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b \
--hash=sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00 \
--hash=sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac \
--hash=sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8 \
--hash=sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e \
--hash=sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28 \
--hash=sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3 \
--hash=sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5 \
--hash=sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4 \
--hash=sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b \
--hash=sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf \
--hash=sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5 \
--hash=sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702 \
--hash=sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8 \
--hash=sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788 \
--hash=sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da \
--hash=sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d \
--hash=sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc \
--hash=sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c \
--hash=sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba \
--hash=sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f \
--hash=sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917 \
--hash=sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5 \
--hash=sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26 \
--hash=sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f \
--hash=sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b \
--hash=sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be \
--hash=sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c \
--hash=sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3 \
--hash=sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6 \
--hash=sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926 \
--hash=sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0
# via -r contrib/container/requirements.in
setuptools==80.9.0 \
--hash=sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922 \
@@ -204,32 +221,32 @@ sqlparse==0.5.3 \
--hash=sha256:09f67787f56a0b16ecdbde1bfc7f5d9c3371ca683cfeaa8e6ff60b4807ec9272 \
--hash=sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca
# via django
typing-extensions==4.14.1 \
--hash=sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36 \
--hash=sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76
typing-extensions==4.15.0 \
--hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \
--hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548
# via
# psycopg
# psycopg-pool
uv==0.8.6 \
--hash=sha256:0b524de39f317bd8733c38cf100b6f8091d44e06b23f7752523ad1ad1454ede3 \
--hash=sha256:132e73f1e9fe05edc6c06c00416f7c721c48298786fd7293be6c584793170bbc \
--hash=sha256:1913f5627c57076c88dd38b0173bdb006ae9b8dbd92b1798a1acc9d744c1a7cc \
--hash=sha256:2ac28509db2e52613a59264bdb150d13274ed13e5b305f7e274da8cd83033985 \
--hash=sha256:37227aaf1e41c7eda3d7f0028e747a2a2eed3f3506b0adc121a4366e8281115b \
--hash=sha256:4d4e042f6bd9f143094051a05de758684028f451e563846cbc0c6f505b530cca \
--hash=sha256:4e81380549151e34ae96d56499438444ba58591ca9f2fc6ba0a867152601849e \
--hash=sha256:57a98367bfad38e870e1a8a6626464796ffcee6e937d429fbd7b25ddf46bb36f \
--hash=sha256:5b201ebc1c5c76c3a415fa4edcb25a0e06263d2255319d6d52275c775e926e23 \
--hash=sha256:6acdc77099906ba64bc1b725bef973c10905d7e9596d1b25f271db772bc9e8e4 \
--hash=sha256:7796acc3c5b84d5ee5e10cc6cf92eb61c19f6551855d0aa89ef5925e4a371fbf \
--hash=sha256:7c1f48279ff61940143c78b969094e13324988eabcfcd4799f4350d9d36c1d48 \
--hash=sha256:993af2c295856c5ca053678a8dadc11ce2f85485513ed1568c16e98d5dfa88bf \
--hash=sha256:c9de4adac36a62e4bddd959ce65fb4bb09b0cbfd95946d50390f2a9c186ecb9c \
--hash=sha256:d96ff3a1d06a6a00ed94dfb2996228153b3b5bfc892174b7556216ab872a91b1 \
--hash=sha256:deab2ce32d2dd7a1c0de459aa23470c60feb0ea24e67c9c5c5988d8bf4eb4a09 \
--hash=sha256:e35cc1ef79d3dce2b6aeffbfb280d02d5ad741d4ca07874bdf0a4d85c841d9de \
--hash=sha256:ee67acf1b211be2cfbeaec16cde13c8325810d32ff85963a9dedd1f9d7c61ef7 \
--hash=sha256:fdceb1ef554df0ddc620bfe83fdcf740829e489c62f78ba1f089abd62c71c63e
uv==0.8.22 \
--hash=sha256:2a436b941b6e79fe1e1065b705a5689d72210f4367cbe885e19910cbcde2e4a1 \
--hash=sha256:36c7aecdb0044caf15ace00da00af172759c49c832f0017b7433d80f46552cd3 \
--hash=sha256:4a982bdd5d239dd6dd2b4219165e209c75af1e1819730454ee46d65b3ccf77a3 \
--hash=sha256:58b6fb191a04b922dc3c8fea6660f58545a651843d7d0efa9ae69164fca9e05d \
--hash=sha256:6706b782ad75662df794e186d16b9ffa4946d57c88f21d0eadfd43425794d1b0 \
--hash=sha256:7350c5f82d9c38944e6466933edcf96a90e0cb85eae5c0e53a5bc716d6f62332 \
--hash=sha256:7378127cbd6ebce8ba6d9bdb88aa8ea995b579824abb5ec381c63b3a123a43be \
--hash=sha256:7e761ca7df8a0059b3fae6bc2c1db24583fa00b016e35bd22a5599d7084471a7 \
--hash=sha256:89944e99b04cc8542cb5931306f1c593f00c9d6f2b652fffc4d84d12b915f911 \
--hash=sha256:8ea724ae9f15c0cb4964e9e2e1b21df65c56ae02a54dc1d8a6ea44a52d819268 \
--hash=sha256:8efec4ef5acddc35f0867998c44e0b15fc4dace1e4c26d01443871a2fbb04bf6 \
--hash=sha256:9757f0b0c7d296f1e354db442ed0ce39721c06d11635ce4ee6638c5e809a9cb4 \
--hash=sha256:9eb3b4abfa25e07d7e1bb4c9bb8dbbdd51878356a37c3c4a2ece3d68d4286f28 \
--hash=sha256:aefa0cb27a86d2145ca9290a1e99c16a17ea26a4f14a89fb7336bc19388427cc \
--hash=sha256:b1fdffc2e71892ce648b66317e478fe8884d0007e20cfa582fff3dcea588a450 \
--hash=sha256:cda349c9ea53644d8d9ceae30db71616b733eb5330375ab4259765aef494b74e \
--hash=sha256:d6a33bd5309f8fb77d9fc249bb17f77a23426e6153e43b03ca1cd6640f0a423d \
--hash=sha256:e6e1289c411d43e0ca245f46e76457f3807de646d90b656591b6cf46348bed5c \
--hash=sha256:f6ded9bacb31441d788afca397b8b884ebc2e70f903bea0a38806194be4b249c
# via -r contrib/container/requirements.in
wheel==0.45.1 \
--hash=sha256:661e1abd9198507b1409a20c02106d9670b2576e916d58f520316666abca6729 \

View File

@@ -83,7 +83,7 @@ Date and time values are stored in the database in UTC format, and are converted
By default, the InvenTree server will not automatically apply database migrations. When the InvenTree installation is updated (*or a plugin is installed which requires database migrations*), database migrations must be applied manually by the system administrator.
With "auto update" enabled, the InvenTree server will automatically apply database migrations as required. To enable automatic database updates, set `INVENTREE_AUTO_UPDATE` to `True`.
With "auto update" enabled, the InvenTree server will automatically apply database migrations as required when plugins are changed. To enable automatic database updates, set `INVENTREE_AUTO_UPDATE` to `True`. However, this setting is not sufficient when updating your InvenTree installation - you must still ensure that you follow the required steps for updating InvenTree as per your installation method.
## Debugging and Logging Options
@@ -135,6 +135,7 @@ Depending on how your InvenTree installation is configured, you will need to pay
| INVENTREE_CORS_ORIGIN_WHITELIST | cors.whitelist | List of whitelisted CORS URLs. Refer to the [django-cors-headers documentation](https://github.com/adamchainz/django-cors-headers#cors_allowed_origins-sequencestr) | Uses the *INVENTREE_SITE_URL* parameter, if set. Otherwise, an empty list. |
| INVENTREE_CORS_ORIGIN_REGEX | cors.regex | List of regular expressions for CORS whitelisted URL patterns | *Empty list* |
| INVENTREE_CORS_ALLOW_CREDENTIALS | cors.allow_credentials | Allow cookies in cross-site requests | `True` |
| INVENTREE_SITE_LAX_PROTOCOL | site_lax_protocol | Ignore protocol mismatches on INVE-E7 site checks | `True` |
| INVENTREE_USE_X_FORWARDED_HOST | use_x_forwarded_host | Use forwarded host header | `False` |
| INVENTREE_USE_X_FORWARDED_PORT | use_x_forwarded_port | Use forwarded port header | `False` |
| INVENTREE_USE_X_FORWARDED_PROTO | use_x_forwarded_proto | Use forwarded protocol header | `False` |

View File

@@ -234,17 +234,31 @@ class InvenTreeHostSettingsMiddleware(MiddlewareMixin):
if path in urls or any(path.startswith(p) for p in paths_ignore):
return None
# Ensure that the settings are set correctly with the current request
# treat the accessed scheme and host
accessed_scheme = request._current_scheme_host
if accessed_scheme and not accessed_scheme.startswith(settings.SITE_URL):
msg = f'INVE-E7: The used path `{accessed_scheme}` does not match the SITE_URL `{settings.SITE_URL}`'
logger.error(msg)
return render(
request, 'config_error.html', {'error_message': msg}, status=500
)
referer = urlsplit(accessed_scheme)
# Ensure that the settings are set correctly with the current request
matches = (
(accessed_scheme and not accessed_scheme.startswith(settings.SITE_URL))
if not settings.SITE_LAX_PROTOCOL_CHECK
else not is_same_domain(referer.netloc, urlsplit(settings.SITE_URL).netloc)
)
if matches:
if (
isinstance(settings.CSRF_TRUSTED_ORIGINS, list)
and len(settings.CSRF_TRUSTED_ORIGINS) > 1
):
# The used url might not be the primary url - next check determines if in a trusted origins
pass
else:
msg = f'INVE-E7: The used path `{accessed_scheme}` does not match the SITE_URL `{settings.SITE_URL}`'
logger.error(msg)
return render(
request, 'config_error.html', {'error_message': msg}, status=500
)
# Check trusted origins
referer = urlsplit(accessed_scheme)
if not any(
is_same_domain(referer.netloc, host)
for host in [

View File

@@ -1052,6 +1052,9 @@ DATE_INPUT_FORMATS = ['%Y-%m-%d']
# Site URL can be specified statically, or via a run-time setting
SITE_URL = get_setting('INVENTREE_SITE_URL', 'site_url', None)
SITE_LAX_PROTOCOL_CHECK = get_boolean_setting(
'INVENTREE_SITE_LAX_PROTOCOL', 'site_lax_protocol', True
)
if SITE_URL:
SITE_URL = str(SITE_URL).strip().rstrip('/')

View File

@@ -1,5 +1,6 @@
"""Tests for middleware functions."""
from django.conf import settings
from django.http import Http404
from django.urls import reverse
@@ -87,36 +88,90 @@ class MiddlewareTests(InvenTreeTestCase):
log_error('testpath')
check(1)
def do_positive_test(self, response):
"""Helper function to check for positive test results."""
self.assertEqual(response.status_code, 200)
self.assertNotContains(response, 'INVE-E7')
self.assertContains(response, 'window.INVENTREE_SETTINGS')
def test_site_url_checks(self):
"""Test that the site URL check is correctly working."""
# correctly set
# simple setup
with self.settings(
SITE_URL='http://testserver', CSRF_TRUSTED_ORIGINS=['http://testserver']
):
response = self.client.get(reverse('web'))
self.assertEqual(response.status_code, 200)
self.assertNotContains(response, 'INVE-E7')
self.assertContains(response, 'window.INVENTREE_SETTINGS')
self.do_positive_test(response)
# wrongly set site URL
with self.settings(SITE_URL='https://example.com'):
# simple setup with wildcard
with self.settings(
SITE_URL='http://testserver', CSRF_TRUSTED_ORIGINS=['http://*.testserver']
):
response = self.client.get(reverse('web'))
self.assertEqual(response.status_code, 500)
self.assertContains(
response,
'INVE-E7: The used path `http://testserver` does not match',
status_code=500,
)
self.assertNotContains(
response, 'window.INVENTREE_SETTINGS', status_code=500
)
self.do_positive_test(response)
def test_site_lax_protocol(self):
"""Test that the site URL check is correctly working with/without lax protocol check."""
# Simple setup with proxy
with self.settings(
SITE_URL='https://testserver', CSRF_TRUSTED_ORIGINS=['https://testserver']
):
response = self.client.get(reverse('web'))
self.do_positive_test(response)
# No worky if strong security
with self.settings(
SITE_URL='https://testserver',
CSRF_TRUSTED_ORIGINS=['https://testserver'],
SITE_LAX_PROTOCOL_CHECK=False,
):
response = self.client.get(reverse('web'))
self.assertContains(response, 'INVE-E7: The used path', status_code=500)
def test_site_url_checks_multi(self):
"""Test that the site URL check is correctly working in a multi-site setup."""
# multi-site setup with trusted origins
with self.settings(
SITE_URL='https://testserver.example.com',
CSRF_TRUSTED_ORIGINS=[
'http://testserver',
'https://testserver.example.com',
],
):
# this will run with testserver as host by default
response = self.client.get(reverse('web'))
self.do_positive_test(response)
# Now test with the "outside" url name
response = self.client.get(
'https://testserver.example.com/web/',
SERVER_NAME='testserver.example.com',
)
self.do_positive_test(response)
# A non-trsuted origin must still fail in multi - origin setup
response = self.client.get(
'https://not-my-testserver.example.com/web/',
SERVER_NAME='not-my-testserver.example.com',
)
self.assertEqual(response.status_code, 500)
# Even if it is a subdomain
response = self.client.get(
'https://not-my.testserver.example.com/web/',
SERVER_NAME='not-my.testserver.example.com',
)
self.assertEqual(response.status_code, 500)
def test_site_url_checks_fails(self):
"""Test that the site URL check is correctly failing.
Important for security.
"""
# wrongly set but in debug -> is ignored
with self.settings(SITE_URL='https://example.com', DEBUG=True):
response = self.client.get(reverse('web'))
self.assertEqual(response.status_code, 200)
self.assertNotContains(response, 'INVE-E7')
self.assertContains(response, 'window.INVENTREE_SETTINGS')
self.do_positive_test(response)
# wrongly set cors
with self.settings(
@@ -125,7 +180,6 @@ class MiddlewareTests(InvenTreeTestCase):
CSRF_TRUSTED_ORIGINS=['https://example.com'],
):
response = self.client.get(reverse('web'))
self.assertEqual(response.status_code, 500)
self.assertContains(
response, 'is not in the TRUSTED_ORIGINS', status_code=500
)
@@ -133,10 +187,31 @@ class MiddlewareTests(InvenTreeTestCase):
response, 'window.INVENTREE_SETTINGS', status_code=500
)
# wrongly set site URL
with self.settings(
SITE_URL='http://testserver', CSRF_TRUSTED_ORIGINS=['http://*.testserver']
SITE_URL='https://example.com',
CSRF_TRUSTED_ORIGINS=['http://localhost:8000'],
):
response = self.client.get(reverse('web'))
self.assertEqual(response.status_code, 200)
self.assertNotContains(response, 'INVE-E7')
self.assertContains(response, 'window.INVENTREE_SETTINGS')
self.assertContains(
response, 'INVE-E7: The used path `http://testserver` ', status_code=500
)
self.assertNotContains(
response, 'window.INVENTREE_SETTINGS', status_code=500
)
# Log stuff # TODO remove
print(
'###DBG-TST###',
'site',
settings.SITE_URL,
'trusted',
settings.CSRF_TRUSTED_ORIGINS,
)
# Check that the correct step triggers the error message
self.assertContains(
response,
'INVE-E7: The used path `http://testserver` does not match',
status_code=500,
)

View File

@@ -18,7 +18,7 @@ from django.conf import settings
from .api_version import INVENTREE_API_TEXT, INVENTREE_API_VERSION
# InvenTree software version
INVENTREE_SW_VERSION = '1.0.0'
INVENTREE_SW_VERSION = '1.0.3'
logger = logging.getLogger('inventree')

View File

@@ -480,8 +480,8 @@ class BuildLineFilter(rest_filters.FilterSet):
def filter_allocated(self, queryset, name, value):
"""Filter by whether each BuildLine is fully allocated."""
if str2bool(value):
return queryset.filter(allocated__gte=F('quantity'))
return queryset.filter(allocated__lt=F('quantity'))
return queryset.filter(allocated__gte=F('quantity') - F('consumed'))
return queryset.filter(allocated__lt=F('quantity') - F('consumed'))
consumed = rest_filters.BooleanFilter(label=_('Consumed'), method='filter_consumed')

View File

@@ -1,21 +1,22 @@
"""Queryset filtering helper functions for the Build app."""
from django.db import models
from django.db.models import Q, Sum
from django.db.models.functions import Coalesce
from django.db.models import DecimalField, ExpressionWrapper, F, Max, Sum
from django.db.models.functions import Coalesce, Greatest
def annotate_allocated_quantity(queryset: Q) -> Q:
"""Annotate the 'allocated' quantity for each build item in the queryset.
Arguments:
queryset: The BuildLine queryset to annotate
"""
queryset = queryset.prefetch_related('allocations')
return queryset.annotate(
allocated=Coalesce(
Sum('allocations__quantity'), 0, output_field=models.DecimalField()
)
def annotate_required_quantity():
"""Annotate the 'required' quantity for each build item in the queryset."""
# Note: The use of Max() here is intentional, to avoid aggregation issues in MySQL
# Ref: https://github.com/inventree/InvenTree/pull/10398
return Greatest(
ExpressionWrapper(
Max(F('quantity')) - Max(F('consumed')), output_field=DecimalField()
),
0,
output_field=DecimalField(),
)
def annotate_allocated_quantity():
"""Annotate the 'allocated' quantity for each build item in the queryset."""
return Coalesce(Sum('allocations__quantity'), 0, output_field=DecimalField())

View File

@@ -29,7 +29,7 @@ import report.mixins
import stock.models
import users.models
from build.events import BuildEvents
from build.filters import annotate_allocated_quantity
from build.filters import annotate_allocated_quantity, annotate_required_quantity
from build.status_codes import BuildStatus, BuildStatusGroups
from build.validators import (
generate_next_build_reference,
@@ -1062,7 +1062,7 @@ class Build(
lines = self.untracked_line_items.all()
lines = lines.exclude(bom_item__consumable=True)
lines = annotate_allocated_quantity(lines)
lines = lines.annotate(allocated=annotate_allocated_quantity())
for build_line in lines:
reduce_by = build_line.allocated - build_line.quantity
@@ -1381,10 +1381,12 @@ class Build(
elif tracked is False:
lines = lines.filter(bom_item__sub_part__trackable=False)
lines = annotate_allocated_quantity(lines)
lines = lines.prefetch_related('allocations')
# Filter out any lines which have been fully allocated
lines = lines.filter(allocated__lt=F('quantity'))
lines = lines.annotate(
allocated=annotate_allocated_quantity(),
required=annotate_required_quantity(),
).filter(allocated__lt=F('required'))
return lines
@@ -1436,10 +1438,14 @@ class Build(
True if any BuildLine has been over-allocated.
"""
lines = self.build_lines.all().exclude(bom_item__consumable=True)
lines = annotate_allocated_quantity(lines)
lines = lines.prefetch_related('allocations')
# Find any lines which have been over-allocated
lines = lines.filter(allocated__gt=F('quantity'))
lines = lines.annotate(
allocated=annotate_allocated_quantity(),
required=annotate_required_quantity(),
).filter(allocated__gt=F('required'))
return lines.count() > 0
@@ -1644,19 +1650,30 @@ class BuildLine(report.mixins.InvenTreeReportMixin, InvenTree.models.InvenTreeMo
return allocated['q']
def unallocated_quantity(self):
"""Return the unallocated quantity for this BuildLine."""
return max(self.quantity - self.allocated_quantity(), 0)
"""Return the unallocated quantity for this BuildLine.
- Start with the required quantity
- Subtract the consumed quantity
- Subtract the allocated quantity
Return the remaining quantity (or zero if negative)
"""
return max(self.quantity - self.consumed - self.allocated_quantity(), 0)
def is_fully_allocated(self):
"""Return True if this BuildLine is fully allocated."""
if self.bom_item.consumable:
return True
return self.allocated_quantity() >= self.quantity
required = max(0, self.quantity - self.consumed)
return self.allocated_quantity() >= required
def is_overallocated(self):
"""Return True if this BuildLine is over-allocated."""
return self.allocated_quantity() > self.quantity
required = max(0, self.quantity - self.consumed)
return self.allocated_quantity() > required
def is_fully_consumed(self):
"""Return True if this BuildLine is fully consumed."""

View File

@@ -853,6 +853,78 @@ class AutoAllocationTests(BuildTestBase):
self.assertEqual(self.line_1.unallocated_quantity(), 0)
self.assertEqual(self.line_2.unallocated_quantity(), 0)
def test_allocate_consumed(self):
"""Test for auto-allocation against a build which has been fully consumed.
Steps:
1. Fully allocate the build (using the auto-allocate function)
2. Consume allocated stock
3. Ensure that all allocations are removed
4. Re-run the auto-allocate function
5. Check that no new allocations have been made
"""
self.assertEqual(self.build.allocated_stock.count(), 0)
self.assertFalse(self.build.is_fully_allocated(tracked=False))
# Auto allocate stock against the build order
self.build.auto_allocate_stock(
interchangeable=True, substitutes=True, optional_items=True
)
self.assertEqual(self.line_1.allocated_quantity(), 50)
self.assertEqual(self.line_2.allocated_quantity(), 30)
self.assertEqual(self.line_1.unallocated_quantity(), 0)
self.assertEqual(self.line_2.unallocated_quantity(), 0)
self.assertTrue(self.line_1.is_fully_allocated())
self.assertTrue(self.line_2.is_fully_allocated())
self.assertFalse(self.line_1.is_overallocated())
self.assertFalse(self.line_2.is_overallocated())
N = self.build.allocated_stock.count()
self.assertEqual(self.line_1.allocations.count(), 2)
self.assertEqual(self.line_2.allocations.count(), 6)
for item in self.line_1.allocations.all():
item.complete_allocation()
for item in self.line_2.allocations.all():
item.complete_allocation()
self.line_1.refresh_from_db()
self.line_2.refresh_from_db()
self.assertTrue(self.line_1.is_fully_allocated())
self.assertTrue(self.line_2.is_fully_allocated())
self.assertFalse(self.line_1.is_overallocated())
self.assertFalse(self.line_2.is_overallocated())
self.assertEqual(self.line_1.allocations.count(), 0)
self.assertEqual(self.line_2.allocations.count(), 0)
self.assertEqual(self.line_1.quantity, self.line_1.consumed)
self.assertEqual(self.line_2.quantity, self.line_2.consumed)
# Check that the "allocations" have been removed
self.assertEqual(self.build.allocated_stock.count(), N - 8)
# Now, try to auto-allocate again
self.build.auto_allocate_stock(
interchangeable=True, substitutes=True, optional_items=True
)
# Ensure that there are no "new" allocations (there should be none!)
self.assertEqual(self.line_1.allocated_quantity(), 0)
self.assertEqual(self.line_2.allocated_quantity(), 0)
self.assertEqual(self.line_1.unallocated_quantity(), 0)
self.assertEqual(self.line_2.unallocated_quantity(), 0)
self.assertEqual(self.build.allocated_stock.count(), N - 8)
class ExternalBuildTest(InvenTreeAPITestCase):
"""Unit tests for external build order functionality."""

View File

@@ -630,6 +630,38 @@ class DataImportRow(models.Model):
if value is None and field in default_values:
value = default_values[field]
# If the field provides a set of valid 'choices', use that as a lookup
if field_type == 'choice' and 'choices' in field_def:
choices = field_def.get('choices', None)
if callable(choices):
choices = choices()
# Try to match the provided value against the available choices
choice_value = None
for choice in choices:
primary_value = choice['value']
display_value = choice['display_name']
if primary_value == value:
choice_value = primary_value
# Break on first match against a primary choice value
break
if display_value == value:
choice_value = primary_value
elif (
str(display_value).lower().strip() == str(value).lower().strip()
and choice_value is None
):
# Case-insensitive match against display value
choice_value = primary_value
if choice_value is not None:
value = choice_value
data[field] = value
self.data = data

View File

@@ -111,7 +111,7 @@ class UserInterfaceMixinTests(InvenTreeAPITestCase):
# Request custom panel information for a part instance
response = self.get(url, data=query_data)
# There should be 4 active panels for the part by default
# There should be 3 active panels for the part by default
self.assertEqual(3, len(response.data))
_part.active = False
@@ -119,8 +119,8 @@ class UserInterfaceMixinTests(InvenTreeAPITestCase):
response = self.get(url, data=query_data)
# As the part is not active, only 3 panels left
self.assertEqual(3, len(response.data))
# As the part is not active, only 2 panels left
self.assertEqual(2, len(response.data))
# Disable the "ENABLE_PART_PANELS" setting, and try again
plugin.set_setting('ENABLE_PART_PANELS', False)

View File

@@ -93,13 +93,17 @@ class SampleUserInterfacePlugin(SettingsMixin, UserInterfaceMixin, InvenTreePlug
except (Part.DoesNotExist, ValueError):
part = None
panels.append({
'key': 'part-panel',
'title': _('Part Panel'),
'source': self.plugin_static_file('sample_panel.js:renderPartPanel'),
'icon': 'ti:package_outline',
'context': {'part_name': part.name if part else ''},
})
# Only display this panel for "active" parts
if part and part.active:
panels.append({
'key': 'part-panel',
'title': _('Part Panel'),
'source': self.plugin_static_file(
'sample_panel.js:renderPartPanel'
),
'icon': 'ti:package:outline',
'context': {'part_name': part.name if part else ''},
})
# Next, add a custom panel which will appear on the 'purchaseorder' page
if target_model == 'purchaseorder' and self.get_setting(

View File

@@ -10,4 +10,4 @@ class VersionPlugin(InvenTreePlugin):
NAME = 'Sample Version Plugin'
DESCRIPTION = 'A simple plugin which shows how to use the version limits'
MIN_VERSION = '0.1.0'
MAX_VERSION = '1.0.0'
MAX_VERSION = '2.0.0'

View File

@@ -4,7 +4,7 @@ import logging
import os
from django.apps import AppConfig
from django.core.exceptions import AppRegistryNotReady
from django.core.exceptions import AppRegistryNotReady, ValidationError
from django.core.files.base import ContentFile
from django.core.files.storage import default_storage
from django.db.utils import IntegrityError, OperationalError, ProgrammingError
@@ -52,6 +52,8 @@ class ReportConfig(AppConfig):
try:
self.create_default_labels()
self.create_default_reports()
except ValidationError:
logger.warning('Validation error when creating default templates')
except (
AppRegistryNotReady,
IntegrityError,
@@ -162,6 +164,10 @@ class ReportConfig(AppConfig):
**template, template=self.file_from_template('label', filename)
)
logger.info("Creating new label template: '%s'", template['name'])
except ValidationError:
logger.warning(
"Could not create label template: '%s'", template['name']
)
except Exception:
InvenTree.exceptions.log_error('create_default_labels', scope='init')
@@ -261,5 +267,9 @@ class ReportConfig(AppConfig):
**template, template=self.file_from_template('report', filename)
)
logger.info("Created new report template: '%s'", template['name'])
except ValidationError:
logger.warning(
"Could not create report template: '%s'", template['name']
)
except Exception:
InvenTree.exceptions.log_error('create_default_reports', scope='init')

View File

@@ -329,7 +329,7 @@ class StockItemSerializer(
'supplier_part_detail.MPN',
]
import_exclude_fields = ['use_pack_size', 'location_path']
import_exclude_fields = ['location_path', 'serial_numbers', 'use_pack_size']
class Meta:
"""Metaclass options."""
@@ -490,7 +490,7 @@ class StockItemSerializer(
def update(self, instance, validated_data):
"""Custom update method to pass the user information through to the instance."""
instance._user = self.context['user']
instance._user = self.context.get('user', None)
status_custom_key = validated_data.pop('status_custom_key', None)
status = validated_data.pop('status', None)

View File

@@ -144,6 +144,14 @@ class UserDetail(RetrieveUpdateDestroyAPI):
serializer_class = ExtendedUserSerializer
permission_classes = [InvenTree.permissions.StaffRolePermissionOrReadOnly]
def perform_destroy(self, instance):
"""Override destroy method to ensure sessions are deleted first."""
# Remove all sessions for this user
if sessions := instance.usersession_set.all():
sessions.delete()
# Normally delete the user
return super().perform_destroy(instance)
class UserDetailSetPassword(UpdateAPI):
"""Allows superusers to set the password for a user."""

View File

@@ -61,7 +61,10 @@ if settings.LDAP_AUTH:
user.save()
# if they got an email address from LDAP, create it now and make it the primary
if user.email:
if (
user.email
and not EmailAddress.objects.filter(user=user, email=user.email).exists()
):
EmailAddress.objects.create(user=user, email=user.email, primary=True)

View File

@@ -185,95 +185,111 @@ click==8.1.8 \
--hash=sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2 \
--hash=sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a
# via pip-tools
coverage[toml]==7.10.6 \
--hash=sha256:073711de3181b2e204e4870ac83a7c4853115b42e9cd4d145f2231e12d670930 \
--hash=sha256:081b98395ced0d9bcf60ada7661a0b75f36b78b9d7e39ea0790bb4ed8da14747 \
--hash=sha256:0de434f4fbbe5af4fa7989521c655c8c779afb61c53ab561b64dcee6149e4c65 \
--hash=sha256:0e93b1476b79eae849dc3872faeb0bf7948fd9ea34869590bc16a2a00b9c82a7 \
--hash=sha256:0f3f56e4cb573755e96a16501a98bf211f100463d70275759e73f3cbc00d4f27 \
--hash=sha256:0f7cb359a448e043c576f0da00aa8bfd796a01b06aa610ca453d4dde09cc1034 \
--hash=sha256:10356fdd33a7cc06e8051413140bbdc6f972137508a3572e3f59f805cd2832fd \
--hash=sha256:137921f2bac5559334ba66122b753db6dc5d1cf01eb7b64eb412bb0d064ef35b \
--hash=sha256:160c00a5e6b6bdf4e5984b0ef21fc860bc94416c41b7df4d63f536d17c38902e \
--hash=sha256:2195f8e16ba1a44651ca684db2ea2b2d4b5345da12f07d9c22a395202a05b23c \
--hash=sha256:282b1b20f45df57cc508c1e033403f02283adfb67d4c9c35a90281d81e5c52c5 \
--hash=sha256:28395ca3f71cd103b8c116333fa9db867f3a3e1ad6a084aa3725ae002b6583bc \
--hash=sha256:2904271c80898663c810a6b067920a61dd8d38341244a3605bd31ab55250dad5 \
--hash=sha256:2b38261034fda87be356f2c3f42221fdb4171c3ce7658066ae449241485390d5 \
--hash=sha256:2e4c33e6378b9d52d3454bd08847a8651f4ed23ddbb4a0520227bd346382bbc6 \
--hash=sha256:388d80e56191bf846c485c14ae2bc8898aa3124d9d35903fef7d907780477634 \
--hash=sha256:3e23dd5408fe71a356b41baa82892772a4cefcf758f2ca3383d2aa39e1b7a003 \
--hash=sha256:3fb99d0786fe17b228eab663d16bee2288e8724d26a199c29325aac4b0319b9b \
--hash=sha256:441c357d55f4936875636ef2cfb3bee36e466dcf50df9afbd398ce79dba1ebb7 \
--hash=sha256:4cec13817a651f8804a86e4f79d815b3b28472c910e099e4d5a0e8a3b6a1d4cb \
--hash=sha256:5aea98383463d6e1fa4e95416d8de66f2d0cb588774ee20ae1b28df826bcb619 \
--hash=sha256:5b15a87265e96307482746d86995f4bff282f14b027db75469c446da6127433b \
--hash=sha256:5b2dd6059938063a2c9fee1af729d4f2af28fd1a545e9b7652861f0d752ebcea \
--hash=sha256:5e75e37f23eb144e78940b40395b42f2321951206a4f50e23cfd6e8a198d3ceb \
--hash=sha256:6008a021907be8c4c02f37cdc3ffb258493bdebfeaf9a839f9e71dfdc47b018e \
--hash=sha256:61c950fc33d29c91b9e18540e1aed7d9f6787cc870a3e4032493bbbe641d12fc \
--hash=sha256:628055297f3e2aa181464c3808402887643405573eb3d9de060d81531fa79d32 \
--hash=sha256:675824a363cc05781b1527b39dc2587b8984965834a748177ee3c37b64ffeafb \
--hash=sha256:689920ecfd60f992cafca4f5477d55720466ad2c7fa29bb56ac8d44a1ac2b47a \
--hash=sha256:692d70ea725f471a547c305f0d0fc6a73480c62fb0da726370c088ab21aed282 \
--hash=sha256:6937347c5d7d069ee776b2bf4e1212f912a9f1f141a429c475e6089462fcecc5 \
--hash=sha256:6b3039e2ca459a70c79523d39347d83b73f2f06af5624905eba7ec34d64d80b5 \
--hash=sha256:6e31b8155150c57e5ac43ccd289d079eb3f825187d7c66e755a055d2c85794c6 \
--hash=sha256:70e7bfbd57126b5554aa482691145f798d7df77489a177a6bef80de78860a356 \
--hash=sha256:752a3005a1ded28f2f3a6e8787e24f28d6abe176ca64677bcd8d53d6fe2ec08a \
--hash=sha256:7d79dabc0a56f5af990cc6da9ad1e40766e82773c075f09cc571e2076fef882e \
--hash=sha256:7eb68d356ba0cc158ca535ce1381dbf2037fa8cb5b1ae5ddfc302e7317d04144 \
--hash=sha256:80b1695cf7c5ebe7b44bf2521221b9bb8cdf69b1f24231149a7e3eb1ae5fa2fb \
--hash=sha256:851430a9a361c7a8484a36126d1d0ff8d529d97385eacc8dfdc9bfc8c2d2cbe4 \
--hash=sha256:856986eadf41f52b214176d894a7de05331117f6035a28ac0016c0f63d887629 \
--hash=sha256:86b9b59f2b16e981906e9d6383eb6446d5b46c278460ae2c36487667717eccf1 \
--hash=sha256:8953746d371e5695405806c46d705a3cd170b9cc2b9f93953ad838f6c1e58612 \
--hash=sha256:8cdbe264f11afd69841bd8c0d83ca10b5b32853263ee62e6ac6a0ab63895f972 \
--hash=sha256:8dd5af36092430c2b075cee966719898f2ae87b636cefb85a653f1d0ba5d5393 \
--hash=sha256:8e0c38dc289e0508ef68ec95834cb5d2e96fdbe792eaccaa1bccac3966bbadcc \
--hash=sha256:90558c35af64971d65fbd935c32010f9a2f52776103a259f1dee865fe8259352 \
--hash=sha256:90cb5b1a4670662719591aa92d0095bb41714970c0b065b02a2610172dbf0af6 \
--hash=sha256:92be86fcb125e9bda0da7806afd29a3fd33fdf58fba5d60318399adf40bf37d0 \
--hash=sha256:92c4ecf6bf11b2e85fd4d8204814dc26e6a19f0c9d938c207c5cb0eadfcabbe3 \
--hash=sha256:95d91d7317cde40a1c249d6b7382750b7e6d86fad9d8eaf4fa3f8f44cf171e80 \
--hash=sha256:961834e2f2b863a0e14260a9a273aff07ff7818ab6e66d2addf5628590c628f9 \
--hash=sha256:9702b59d582ff1e184945d8b501ffdd08d2cee38d93a2206aa5f1365ce0b8d78 \
--hash=sha256:98cede73eb83c31e2118ae8d379c12e3e42736903a8afcca92a7218e1f2903b0 \
--hash=sha256:99c4283e2a0e147b9c9cc6bc9c96124de9419d6044837e9799763a0e29a7321a \
--hash=sha256:99e1a305c7765631d74b98bf7dbf54eeea931f975e80f115437d23848ee8c27c \
--hash=sha256:a517feaf3a0a3eca1ee985d8373135cfdedfbba3882a5eab4362bda7c7cf518d \
--hash=sha256:a80f7aef9535442bdcf562e5a0d5a5538ce8abe6bb209cfbf170c462ac2c2a32 \
--hash=sha256:ac765b026c9f33044419cbba1da913cfb82cca1b60598ac1c7a5ed6aac4621a0 \
--hash=sha256:acf36b8268785aad739443fa2780c16260ee3fa09d12b3a70f772ef100939d80 \
--hash=sha256:adec1d980fa07e60b6ef865f9e5410ba760e4e1d26f60f7e5772c73b9a5b0713 \
--hash=sha256:b0353b0f0850d49ada66fdd7d0c7cdb0f86b900bb9e367024fd14a60cecc1e27 \
--hash=sha256:b37201ce4a458c7a758ecc4efa92fa8ed783c66e0fa3c42ae19fc454a0792153 \
--hash=sha256:bf9a19f5012dab774628491659646335b1928cfc931bf8d97b0d5918dd58033c \
--hash=sha256:c61fc91ab80b23f5fddbee342d19662f3d3328173229caded831aa0bd7595460 \
--hash=sha256:c68018e4fc4e14b5668f1353b41ccf4bc83ba355f0e1b3836861c6f042d89ac1 \
--hash=sha256:c706db3cabb7ceef779de68270150665e710b46d56372455cd741184f3868d8f \
--hash=sha256:c83f6afb480eae0313114297d29d7c295670a41c11b274e6bca0c64540c1ce7b \
--hash=sha256:c8a3ec16e34ef980a46f60dc6ad86ec60f763c3f2fa0db6d261e6e754f72e945 \
--hash=sha256:c9a8b7a34a4de3ed987f636f71881cd3b8339f61118b1aa311fbda12741bff0b \
--hash=sha256:cd4b2b0707fc55afa160cd5fc33b27ccbf75ca11d81f4ec9863d5793fc6df56a \
--hash=sha256:d6b9ae13d5d3e8aeca9ca94198aa7b3ebbc5acfada557d724f2a1f03d2c0b0df \
--hash=sha256:d8fd7879082953c156d5b13c74aa6cca37f6a6f4747b39538504c3f9c63d043d \
--hash=sha256:d9369a23186d189b2fc95cc08b8160ba242057e887d766864f7adf3c46b2df21 \
--hash=sha256:db4a1d897bbbe7339946ffa2fe60c10cc81c43fab8b062d3fcb84188688174a4 \
--hash=sha256:df4ec1f8540b0bcbe26ca7dd0f541847cc8a108b35596f9f91f59f0c060bfdd2 \
--hash=sha256:e132b9152749bd33534e5bd8565c7576f135f157b4029b975e15ee184325f528 \
--hash=sha256:e3fb1fa01d3598002777dd259c0c2e6d9d5e10e7222976fc8e03992f972a2cba \
--hash=sha256:e41be6f0f19da64af13403e52f2dec38bbc2937af54df8ecef10850ff8d35301 \
--hash=sha256:ec98435796d2624d6905820a42f82149ee9fc4f2d45c2c5bc5a44481cc50db62 \
--hash=sha256:efeda443000aa23f276f4df973cb82beca682fd800bb119d19e80504ffe53ec2 \
--hash=sha256:f2a6a8e06bbda06f78739f40bfb56c45d14eb8249d0f0ea6d4b3d48e1f7c695d \
--hash=sha256:f32ff80e7ef6a5b5b606ea69a36e97b219cd9dc799bcf2963018a4d8f788cfbf \
--hash=sha256:f35ed9d945bece26553d5b4c8630453169672bea0050a564456eb88bdffd927e \
--hash=sha256:f644a3ae5933a552a29dbb9aa2f90c677a875f80ebea028e5a52a4f429044b90 \
--hash=sha256:f863c08f4ff6b64fa8045b1e3da480f5374779ef187f07b82e0538c68cb4ff8e \
--hash=sha256:fc53ba868875bfbb66ee447d64d6413c2db91fddcfca57025a0e7ab5b07d5862 \
--hash=sha256:ff8a991f70f4c0cf53088abf1e3886edcc87d53004c7bb94e78650b4d3dac3b5 \
--hash=sha256:ffea0575345e9ee0144dfe5701aa17f3ba546f8c3bb48db62ae101afb740e7d6
coverage[toml]==7.10.7 \
--hash=sha256:03ffc58aacdf65d2a82bbeb1ffe4d01ead4017a21bfd0454983b88ca73af94b9 \
--hash=sha256:097c1591f5af4496226d5783d036bf6fd6cd0cbc132e071b33861de756efb880 \
--hash=sha256:0b944ee8459f515f28b851728ad224fa2d068f1513ef6b7ff1efafeb2185f999 \
--hash=sha256:0ebbaddb2c19b71912c6f2518e791aa8b9f054985a0769bdb3a53ebbc765c6a1 \
--hash=sha256:10b24412692df990dbc34f8fb1b6b13d236ace9dfdd68df5b28c2e39cafbba13 \
--hash=sha256:10b6ba00ab1132a0ce4428ff68cf50a25efd6840a42cdf4239c9b99aad83be8b \
--hash=sha256:121da30abb574f6ce6ae09840dae322bef734480ceafe410117627aa54f76d82 \
--hash=sha256:18afb24843cbc175687225cab1138c95d262337f5473512010e46831aa0c2973 \
--hash=sha256:1b4fd784344d4e52647fd7857b2af5b3fbe6c239b0b5fa63e94eb67320770e0f \
--hash=sha256:1ca6db7c8807fb9e755d0379ccc39017ce0a84dcd26d14b5a03b78563776f681 \
--hash=sha256:1ef2319dd15a0b009667301a3f84452a4dc6fddfd06b0c5c53ea472d3989fbf0 \
--hash=sha256:2120043f147bebb41c85b97ac45dd173595ff14f2a584f2963891cbcc3091541 \
--hash=sha256:212f8f2e0612778f09c55dd4872cb1f64a1f2b074393d139278ce902064d5b32 \
--hash=sha256:240af60539987ced2c399809bd34f7c78e8abe0736af91c3d7d0e795df633d17 \
--hash=sha256:2a78cd46550081a7909b3329e2266204d584866e8d97b898cd7fb5ac8d888b1a \
--hash=sha256:2af88deffcc8a4d5974cf2d502251bc3b2db8461f0b66d80a449c33757aa9f40 \
--hash=sha256:2c8b9a0636f94c43cd3576811e05b89aa9bc2d0a85137affc544ae5cb0e4bfbd \
--hash=sha256:2fafd773231dd0378fdba66d339f84904a8e57a262f583530f4f156ab83863e6 \
--hash=sha256:314f2c326ded3f4b09be11bc282eb2fc861184bc95748ae67b360ac962770be7 \
--hash=sha256:33a5e6396ab684cb43dc7befa386258acb2d7fae7f67330ebb85ba4ea27938eb \
--hash=sha256:3445258bcded7d4aa630ab8296dea4d3f15a255588dd535f980c193ab6b95f3f \
--hash=sha256:35f5e3f9e455bb17831876048355dca0f758b6df22f49258cb5a91da23ef437d \
--hash=sha256:39508ffda4f343c35f3236fe8d1a6634a51f4581226a1262769d7f970e73bffe \
--hash=sha256:399a0b6347bcd3822be369392932884b8216d0944049ae22925631a9b3d4ba4c \
--hash=sha256:3a622ac801b17198020f09af3eaf45666b344a0d69fc2a6ffe2ea83aeef1d807 \
--hash=sha256:4376538f36b533b46f8971d3a3e63464f2c7905c9800db97361c43a2b14792ab \
--hash=sha256:4b583b97ab2e3efe1b3e75248a9b333bd3f8b0b1b8e5b45578e05e5850dfb2c2 \
--hash=sha256:4b6f236edf6e2f9ae8fcd1332da4e791c1b6ba0dc16a2dc94590ceccb482e546 \
--hash=sha256:4da86b6d62a496e908ac2898243920c7992499c1712ff7c2b6d837cc69d9467e \
--hash=sha256:50aa94fb1fb9a397eaa19c0d5ec15a5edd03a47bf1a3a6111a16b36e190cff65 \
--hash=sha256:567f5c155eda8df1d3d439d40a45a6a5f029b429b06648235f1e7e51b522b396 \
--hash=sha256:5a02d5a850e2979b0a014c412573953995174743a3f7fa4ea5a6e9a3c5617431 \
--hash=sha256:5e1e9802121405ede4b0133aa4340ad8186a1d2526de5b7c3eca519db7bb89fb \
--hash=sha256:5f33166f0dfcce728191f520bd2692914ec70fac2713f6bf3ce59c3deacb4699 \
--hash=sha256:606cc265adc9aaedcc84f1f064f0e8736bc45814f15a357e30fca7ecc01504e0 \
--hash=sha256:635adb9a4507c9fd2ed65f39693fa31c9a3ee3a8e6dc64df033e8fdf52a7003f \
--hash=sha256:65646bb0359386e07639c367a22cf9b5bf6304e8630b565d0626e2bdf329227a \
--hash=sha256:67f8c5cbcd3deb7a60b3345dffc89a961a484ed0af1f6f73de91705cc6e31235 \
--hash=sha256:69212fbccdbd5b0e39eac4067e20a4a5256609e209547d86f740d68ad4f04911 \
--hash=sha256:6b8b09c1fad947c84bbbc95eca841350fad9cbfa5a2d7ca88ac9f8d836c92e23 \
--hash=sha256:6be8ed3039ae7f7ac5ce058c308484787c86e8437e72b30bf5e88b8ea10f3c87 \
--hash=sha256:6e16e07d85ca0cf8bafe5f5d23a0b850064e8e945d5677492b06bbe6f09cc699 \
--hash=sha256:736f227fb490f03c6488f9b6d45855f8e0fd749c007f9303ad30efab0e73c05a \
--hash=sha256:73ab1601f84dc804f7812dc297e93cd99381162da39c47040a827d4e8dafe63b \
--hash=sha256:77eb4c747061a6af8d0f7bdb31f1e108d172762ef579166ec84542f711d90256 \
--hash=sha256:78a384e49f46b80fb4c901d52d92abe098e78768ed829c673fbb53c498bef73a \
--hash=sha256:7bb3b9ddb87ef7725056572368040c32775036472d5a033679d1fa6c8dc08417 \
--hash=sha256:7ea7c6c9d0d286d04ed3541747e6597cbe4971f22648b68248f7ddcd329207f0 \
--hash=sha256:7fe650342addd8524ca63d77b2362b02345e5f1a093266787d210c70a50b471a \
--hash=sha256:813922f35bd800dca9994c5971883cbc0d291128a5de6b167c7aa697fcf59360 \
--hash=sha256:83082a57783239717ceb0ad584de3c69cf581b2a95ed6bf81ea66034f00401c0 \
--hash=sha256:8421e088bc051361b01c4b3a50fd39a4b9133079a2229978d9d30511fd05231b \
--hash=sha256:86b0e7308289ddde73d863b7683f596d8d21c7d8664ce1dee061d0bcf3fbb4bb \
--hash=sha256:88127d40df529336a9836870436fc2751c339fbaed3a836d42c93f3e4bd1d0a2 \
--hash=sha256:8fb190658865565c549b6b4706856d6a7b09302c797eb2cf8e7fe9dabb043f0d \
--hash=sha256:912e6ebc7a6e4adfdbb1aec371ad04c68854cd3bf3608b3514e7ff9062931d8a \
--hash=sha256:925a1edf3d810537c5a3abe78ec5530160c5f9a26b1f4270b40e62cc79304a1e \
--hash=sha256:93c1b03552081b2a4423091d6fb3787265b8f86af404cff98d1b5342713bdd69 \
--hash=sha256:972b9e3a4094b053a4e46832b4bc829fc8a8d347160eb39d03f1690316a99c14 \
--hash=sha256:981a651f543f2854abd3b5fcb3263aac581b18209be49863ba575de6edf4c14d \
--hash=sha256:99e4aa63097ab1118e75a848a28e40d68b08a5e19ce587891ab7fd04475e780f \
--hash=sha256:9fa6e4dd51fe15d8738708a973470f67a855ca50002294852e9571cdbd9433f2 \
--hash=sha256:a0ec07fd264d0745ee396b666d47cef20875f4ff2375d7c4f58235886cc1ef0c \
--hash=sha256:a2d9a3b260cc1d1dbdb1c582e63ddcf5363426a1a68faa0f5da28d8ee3c722a0 \
--hash=sha256:a3cc8638b2480865eaa3926d192e64ce6c51e3d29c849e09d5b4ad95efae5399 \
--hash=sha256:a609f9c93113be646f44c2a0256d6ea375ad047005d7f57a5c15f614dc1b2f59 \
--hash=sha256:a62c6ef0d50e6de320c270ff91d9dd0a05e7250cac2a800b7784bae474506e63 \
--hash=sha256:a6442c59a8ac8b85812ce33bc4d05bde3fb22321fa8294e2a5b487c3505f611b \
--hash=sha256:a7b55a944a7f43892e28ad4bc0561dfd5f0d73e605d1aa5c3c976b52aea121d2 \
--hash=sha256:a8b6f03672aa6734e700bbcd65ff050fd19cddfec4b031cc8cf1c6967de5a68e \
--hash=sha256:affef7c76a9ef259187ef31599a9260330e0335a3011732c4b9effa01e1cd6e0 \
--hash=sha256:b06f260b16ead11643a5a9f955bd4b5fd76c1a4c6796aeade8520095b75de520 \
--hash=sha256:b1c81d0e5e160651879755c9c675b974276f135558cf4ba79fee7b8413a515df \
--hash=sha256:b281d5eca50189325cfe1f365fafade89b14b4a78d9b40b05ddd1fc7d2a10a9c \
--hash=sha256:b51dcd060f18c19290d9b8a9dd1e0181538df2ce0717f562fff6cf74d9fc0b5b \
--hash=sha256:b7b8288eb7cdd268b0304632da8cb0bb93fadcfec2fe5712f7b9cc8f4d487be2 \
--hash=sha256:b9be91986841a75042b3e3243d0b3cb0b2434252b977baaf0cd56e960fe1e46f \
--hash=sha256:ba58bbcd1b72f136080c0bccc2400d66cc6115f3f906c499013d065ac33a4b61 \
--hash=sha256:bb45474711ba385c46a0bfe696c695a929ae69ac636cda8f532be9e8c93d720a \
--hash=sha256:bc01f57ca26269c2c706e838f6422e2a8788e41b3e3c65e2f41148212e57cd59 \
--hash=sha256:bc91b314cef27742da486d6839b677b3f2793dfe52b51bbbb7cf736d5c29281c \
--hash=sha256:bda5e34f8a75721c96085903c6f2197dc398c20ffd98df33f866a9c8fd95f4bf \
--hash=sha256:c134869d5ffe34547d14e174c866fd8fe2254918cc0a95e99052903bc1543e07 \
--hash=sha256:c41e71c9cfb854789dee6fc51e46743a6d138b1803fab6cb860af43265b42ea6 \
--hash=sha256:c4e16bd7761c5e454f4efd36f345286d6f7c5fa111623c355691e2755cae3b9e \
--hash=sha256:c7315339eae3b24c2d2fa1ed7d7a38654cba34a13ef19fbcb9425da46d3dc594 \
--hash=sha256:c79124f70465a150e89340de5963f936ee97097d2ef76c869708c4248c63ca49 \
--hash=sha256:cac0fdca17b036af3881a9d2729a850b76553f3f716ccb0360ad4dbc06b3b843 \
--hash=sha256:cc87dd1b6eaf0b848eebb1c86469b9f72a1891cb42ac7adcfbce75eadb13dd14 \
--hash=sha256:cce2109b6219f22ece99db7644b9622f54a4e915dad65660ec435e89a3ea7cc3 \
--hash=sha256:d41213ea25a86f69efd1575073d34ea11aabe075604ddf3d148ecfec9e1e96a1 \
--hash=sha256:dc7c389dce432500273eaf48f410b37886be9208b2dd5710aaf7c57fd442c698 \
--hash=sha256:dd5e856ebb7bfb7672b0086846db5afb4567a7b9714b8a0ebafd211ec7ce6a15 \
--hash=sha256:e1ed71194ef6dea7ed2d5cb5f7243d4bcd334bfb63e59878519be558078f848d \
--hash=sha256:e201e015644e207139f7e2351980feb7040e6f4b2c2978892f3e3789d1c125e5 \
--hash=sha256:e28299d9f2e889e6d51b1f043f58d5f997c373cc12e6403b90df95b8b047c13e \
--hash=sha256:f3c887f96407cea3916294046fc7dab611c2552beadbed4ea901cbc6a40cc7a0 \
--hash=sha256:f49a05acd3dfe1ce9715b657e28d138578bc40126760efb962322c56e9ca344b \
--hash=sha256:f4ab143ab113be368a3e9b795f9cd7906c5ef407d6173fe9675a902e1fffc239 \
--hash=sha256:f51328ffe987aecf6d09f3cd9d979face89a617eacdaea43e7b3080777f647ba \
--hash=sha256:f57b2a3c8353d3e04acf75b3fed57ba41f5c0646bbf1d10c7c282291c97936b4 \
--hash=sha256:f7941f6f2fe6dd6807a1208737b8a0cbcf1cc6d7b07d24998ad2d63590868260 \
--hash=sha256:fc04cc7a3db33664e0c2d10eb8990ff6b3536f6842c9590ae8da4c614b9ed05a \
--hash=sha256:fff7b9c3f19957020cac546c70025331113d2e61537f6e2441bc7657913de7d3
# via -r src/backend/requirements-dev.in
cryptography==44.0.3 \
--hash=sha256:02f55fb4f8b79c1221b0961488eaae21015b69b210e18c386b69de182ebb1259 \
@@ -320,9 +336,9 @@ distlib==0.4.0 \
--hash=sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16 \
--hash=sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d
# via virtualenv
django==4.2.24 \
--hash=sha256:40cd7d3f53bc6cd1902eadce23c337e97200888df41e4a73b42d682f23e71d80 \
--hash=sha256:a6527112c58821a0dfc5ab73013f0bdd906539790a17196658e36e66af43c350
django==4.2.25 \
--hash=sha256:2391ab3d78191caaae2c963c19fd70b99e9751008da22a0adcc667c5a4f8d311 \
--hash=sha256:9584cf26b174b35620e53c2558b09d7eb180a655a3470474f513ff9acb494f8c
# via
# -c src/backend/requirements.txt
# django-slowtests
@@ -336,13 +352,13 @@ django-test-migrations==1.4.0 \
--hash=sha256:294dff98f6d43d020d4046b971bac5339e7c71458a35e9ad6450c388fe16ed6b \
--hash=sha256:f0c9c92864ed27d0c9a582e92056637e91227f54bd868a50cb9a1726668c563e
# via -r src/backend/requirements-dev.in
filelock==3.18.0 \
--hash=sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2 \
--hash=sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de
filelock==3.19.1 \
--hash=sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58 \
--hash=sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d
# via virtualenv
identify==2.6.12 \
--hash=sha256:ad9672d5a72e0d2ff7c5c8809b62dfa60458626352fb0eb7b55e69bdc45334a2 \
--hash=sha256:d8de45749f1efb108badef65ee8386f0f7bb19a7f26185f74de6367bffbaf0e6
identify==2.6.15 \
--hash=sha256:1181ef7608e00704db228516541eb83a88a9f94433a8c80bb9b5bd54b1d81757 \
--hash=sha256:e4f4864b96c6557ef2a1e1c951771838f4edc9df3a72ec7118b338801b11c7bf
# via pre-commit
importlib-metadata==8.7.0 \
--hash=sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000 \
@@ -350,9 +366,10 @@ importlib-metadata==8.7.0 \
# via
# -c src/backend/requirements.txt
# build
isort==6.0.1 \
--hash=sha256:1cb5df28dfbc742e490c5e41bad6da41b805b0a8be7bc93cd0fb2a8a890ac450 \
--hash=sha256:2dc5d7f65c9678d94c88dfc29161a320eec67328bc97aad576874cb4be1e9615
# isort
isort==6.1.0 \
--hash=sha256:58d8927ecce74e5087aef019f778d4081a3b6c98f15a80ba35782ca8a2097784 \
--hash=sha256:9b8f96a14cfee0677e78e941ff62f03769a06d412aabb9e2a90487b3b7e8d481
# via -r src/backend/requirements-dev.in
nodeenv==1.9.1 \
--hash=sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f \
@@ -372,9 +389,9 @@ pip==25.2 \
--hash=sha256:578283f006390f85bb6282dffb876454593d637f5d1be494b5202ce4877e71f2 \
--hash=sha256:6d67a2b4e7f14d8b31b8b52648866fa717f45a1eb70e83002f4331d07e953717
# via pip-tools
pip-tools==7.5.0 \
--hash=sha256:30639f50961bb09f49d22f4389e8d7d990709677c094ce1114186b1f2e9b5821 \
--hash=sha256:69758e4e5a65f160e315d74db46246fdbb30d549f1ed0c4236d057122c9b0f18
pip-tools==7.5.1 \
--hash=sha256:a051a94794ba52df9acad2d7c9b0b09ae001617db458a543f8287fea7b89c2cf \
--hash=sha256:f5ff803823529edc0e6e40c86b1aa7da7266fb1078093c8beea4e5b77877036a
# via -r src/backend/requirements-dev.in
platformdirs==4.3.8 \
--hash=sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc \
@@ -513,9 +530,10 @@ typing-extensions==4.14.1 \
# -c src/backend/requirements.txt
# asgiref
# django-test-migrations
virtualenv==20.33.1 \
--hash=sha256:07c19bc66c11acab6a5958b815cbcee30891cd1c2ccf53785a28651a0d8d8a67 \
--hash=sha256:1b44478d9e261b3fb8baa5e74a0ca3bc0e05f21aa36167bf9cbf850e542765b8
# virtualenv
virtualenv==20.34.0 \
--hash=sha256:341f5afa7eee943e4984a9207c025feedd768baff6753cd660c857ceb3e36026 \
--hash=sha256:44815b2c9dee7ed86e387b842a84f20b93f7f417f95886ca1996a72a4138eb1a
# via pre-commit
wheel==0.45.1 \
--hash=sha256:661e1abd9198507b1409a20c02106d9670b2576e916d58f520316666abca6729 \

View File

@@ -391,9 +391,9 @@ defusedxml==0.7.1 \
--hash=sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69 \
--hash=sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61
# via python3-openid
django==4.2.24 \
--hash=sha256:40cd7d3f53bc6cd1902eadce23c337e97200888df41e4a73b42d682f23e71d80 \
--hash=sha256:a6527112c58821a0dfc5ab73013f0bdd906539790a17196658e36e66af43c350
django==4.2.25 \
--hash=sha256:2391ab3d78191caaae2c963c19fd70b99e9751008da22a0adcc667c5a4f8d311 \
--hash=sha256:9584cf26b174b35620e53c2558b09d7eb180a655a3470474f513ff9acb494f8c
# via
# -r src/backend/requirements.in
# django-allauth

View File

@@ -1,4 +1,4 @@
import type { MantineSize } from '@mantine/core';
import type { MantineRadius, MantineSize } from '@mantine/core';
export type UiSizeType = MantineSize | string | number;
@@ -6,7 +6,7 @@ export interface UserTheme {
primaryColor: string;
whiteColor: string;
blackColor: string;
radius: UiSizeType;
radius: MantineRadius;
loader: string;
}

View File

@@ -62,6 +62,8 @@ export function PrintingActions({
id: reportId
});
const [itemIdList, setItemIdList] = useState<number[]>([]);
// Fetch available printing fields via OPTIONS request
const printingFields = useQuery({
enabled: labelPrintingEnabled,
@@ -89,13 +91,13 @@ export function PrintingActions({
filters: {
enabled: true,
model_type: modelType,
items: items.join(',')
items: itemIdList.join(',')
}
};
fields.items = {
...fields.items,
value: items,
value: itemIdList,
hidden: true
};
@@ -115,7 +117,7 @@ export function PrintingActions({
};
return fields;
}, [defaultLabelPlugin, pluginKey, printingFields.data, items]);
}, [defaultLabelPlugin, pluginKey, printingFields.data, itemIdList]);
const labelModal = useCreateApiFormModal({
url: apiUrl(ApiEndpoints.label_print),
@@ -123,6 +125,9 @@ export function PrintingActions({
modalId: 'print-labels',
fields: labelFields,
timeout: 5000,
onOpen: () => {
setItemIdList(items);
},
onClose: () => {
setPluginKey('');
},
@@ -134,24 +139,31 @@ export function PrintingActions({
}
});
const reportModal = useCreateApiFormModal({
url: apiUrl(ApiEndpoints.report_print),
title: t`Print Report`,
modalId: 'print-reports',
timeout: 5000,
fields: {
const reportFields: ApiFormFieldSet = useMemo(() => {
return {
template: {
autoFill: true,
filters: {
enabled: true,
model_type: modelType,
items: items.join(',')
items: itemIdList.join(',')
}
},
items: {
hidden: true,
value: items
value: itemIdList
}
};
}, [itemIdList, modelType]);
const reportModal = useCreateApiFormModal({
url: apiUrl(ApiEndpoints.report_print),
title: t`Print Report`,
modalId: 'print-reports',
timeout: 5000,
fields: reportFields,
onOpen: () => {
setItemIdList(items);
},
submitText: t`Print`,
successMessage: null,

View File

@@ -27,7 +27,7 @@ import {
IconDownload,
IconFilter
} from '@tabler/icons-react';
import { useCallback, useEffect, useState } from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useShallow } from 'zustand/react/shallow';
import type { CalendarState } from '../../hooks/UseCalendar';
import { useLocalState } from '../../states/LocalState';
@@ -59,6 +59,9 @@ export default function Calendar({
const [locale] = useLocalState(useShallow((s) => [s.language]));
// Ensure underscore is replaced with dash
const calendarLocale = useMemo(() => locale.replace('_', '-'), [locale]);
const selectMonth = useCallback(
(date: DateValue) => {
state.selectMonth(date);
@@ -186,7 +189,7 @@ export default function Calendar({
plugins={[dayGridPlugin, interactionPlugin]}
initialView='dayGridMonth'
locales={allLocales}
locale={locale}
locale={calendarLocale}
headerToolbar={false}
footerToolbar={false}
{...calendarProps}

View File

@@ -1,7 +1,7 @@
import type { ApiFormFieldType } from '@lib/types/Forms';
import { Select } from '@mantine/core';
import { useId } from '@mantine/hooks';
import { useCallback, useMemo } from 'react';
import { useCallback, useMemo, useRef } from 'react';
import type { FieldValues, UseControllerReturn } from 'react-hook-form';
/**
@@ -17,6 +17,7 @@ export function ChoiceField({
fieldName: string;
}>) {
const fieldId = useId();
const inputRef = useRef<HTMLInputElement>(null);
const {
field,
@@ -70,6 +71,8 @@ export function ChoiceField({
error={definition.error ?? error?.message}
radius='sm'
{...field}
ref={inputRef}
onDropdownOpen={() => inputRef?.current?.select()}
onChange={onChange}
data={choices}
value={choiceValue}
@@ -81,6 +84,7 @@ export function ChoiceField({
leftSection={definition.icon}
comboboxProps={{ withinPortal: true }}
searchable
selectFirstOptionOnChange
/>
);
}

View File

@@ -194,7 +194,10 @@ export function TableStatusRenderer(
accessor?: string
): ((record: any) => any) | undefined {
return (record: any) => {
const status = resolveItem(record, accessor ?? 'status');
const status =
resolveItem(record, accessor ?? 'status') ??
resolveItem(record, 'status_custom_key') ??
resolveItem(record, 'status');
return (
status && (

View File

@@ -88,7 +88,14 @@ export function RenderStockItem(
const allocated: number = Math.max(0, instance?.allocated ?? 0);
if (instance?.serial !== null && instance?.serial !== undefined) {
// Determine if this item is serialized
const serialized: boolean =
instance?.quantity == 1 &&
instance?.serial !== null &&
instance?.serial !== undefined &&
instance?.serial !== '';
if (serialized) {
quantity_string += `${t`Serial Number`}: ${instance.serial}`;
} else if (allocated > 0) {
const available: number = Math.max(0, instance.quantity - allocated);

View File

@@ -1,4 +1,4 @@
import type { MantineSize } from '@mantine/core';
import type { MantineRadius } from '@mantine/core';
export const emptyServerAPI = {
server: null,
@@ -26,7 +26,7 @@ export const emptyServerAPI = {
export interface SiteMarkProps {
value: number;
label: MantineSize;
label: MantineRadius;
}
export const SizeMarks: SiteMarkProps[] = [

View File

@@ -52,7 +52,7 @@ export function usePluginPanels({
// API query to fetch initial information on available plugin panels
const pluginQuery = useQuery({
enabled: pluginPanelsEnabled && !!model && id !== undefined,
queryKey: ['custom-plugin-panels', model, id],
queryKey: ['custom-plugin-panels', model, id, instance],
throwOnError: (error: any) => {
console.error('ERR: Failed to fetch plugin panels');
return false;

View File

@@ -40,19 +40,19 @@ export function UserTheme({ height }: Readonly<{ height: number }>) {
);
// radius
function getMark(value: number) {
function getRadiusFromValue(value: number) {
const obj = SizeMarks.find((mark) => mark.value === value);
if (obj) return obj;
return SizeMarks[0];
if (obj) return obj.label;
return 'sm';
}
function getDefaultRadius() {
const value = Number.parseInt(userTheme.radius.toString());
return SizeMarks.some((mark) => mark.value === value) ? value : 50;
}
const [radius, setRadius] = useState(getDefaultRadius());
const [radius, setRadius] = useState(25);
function changeRadius(value: number) {
const r = getRadiusFromValue(value);
setRadius(value);
setTheme([{ key: 'radius', value: value.toString() }]);
setTheme([{ key: 'radius', value: r.toString() }]);
}
return (
@@ -163,7 +163,7 @@ export function UserTheme({ height }: Readonly<{ height: number }>) {
</Table.Td>
<Table.Td>
<Slider
label={(val) => getMark(val).label}
label={(val) => getRadiusFromValue(val)}
defaultValue={50}
step={25}
marks={SizeMarks}

View File

@@ -173,17 +173,12 @@ export default function StockDetail() {
stockitem.status_custom_key == stockitem.status
},
{
type: 'text',
name: 'updated',
icon: 'calendar',
label: t`Last Updated`
},
{
type: 'text',
name: 'stocktake',
icon: 'calendar',
label: t`Last Stocktake`,
hidden: !stockitem.stocktake
type: 'link',
name: 'link',
label: t`Link`,
external: true,
copy: true,
hidden: !stockitem.link
}
];
@@ -415,6 +410,19 @@ export default function StockDetail() {
icon: 'part',
label: t`Packaging`,
hidden: !stockitem.packaging
},
{
type: 'text',
name: 'updated',
icon: 'calendar',
label: t`Last Updated`
},
{
type: 'text',
name: 'stocktake',
icon: 'calendar',
label: t`Last Stocktake`,
hidden: !stockitem.stocktake
}
];

View File

@@ -368,14 +368,14 @@ export type StatusColumnProps = TableColumnProps & {
};
export function StatusColumn(props: StatusColumnProps): TableColumn {
const accessor: string = props.accessor ?? 'status';
const accessor: string = props.accessor ?? 'status_custom_key';
return {
accessor: 'status',
sortable: true,
switchable: true,
minWidth: '50px',
render: TableStatusRenderer(props.model, accessor ?? 'status_custom_key'),
render: TableStatusRenderer(props.model, accessor),
...props
};
}

View File

@@ -183,9 +183,13 @@ export default function BuildAllocatedStockTable({
const [selectedItems, setSelectedItems] = useState<any[]>([]);
const itemsToConsume = useMemo(() => {
return selectedItems.filter((item) => !item.part_detail?.trackable);
}, [selectedItems]);
const consumeStock = useConsumeBuildItemsForm({
buildId: buildId ?? 0,
allocatedItems: selectedItems,
allocatedItems: itemsToConsume,
onFormSuccess: () => {
table.clearSelectedRecords();
table.refreshTable();
@@ -225,13 +229,16 @@ export default function BuildAllocatedStockTable({
const rowActions = useCallback(
(record: any): RowAction[] => {
const part = record.part_detail ?? {};
const trackable: boolean = part?.trackable ?? false;
return [
{
color: 'green',
icon: <IconCircleDashedCheck />,
title: t`Consume`,
tooltip: t`Consume Stock`,
hidden: !user.hasChangeRole(UserRoles.build),
hidden: !buildId || trackable || !user.hasChangeRole(UserRoles.build),
onClick: () => {
setSelectedItems([record]);
consumeStock.open();

View File

@@ -479,6 +479,7 @@ export default function BuildLineTable({
);
}
const allocated = record.allocatedQuantity ?? 0;
let required = Math.max(0, record.quantity - record.consumed);
if (output?.pk) {
@@ -486,7 +487,7 @@ export default function BuildLineTable({
required = record.bom_item_detail?.quantity;
}
if (required <= 0) {
if (allocated <= 0 && required <= 0) {
return (
<Group gap='xs' wrap='nowrap'>
<IconCircleCheck size={16} color='green' />
@@ -502,7 +503,7 @@ export default function BuildLineTable({
return (
<ProgressBar
progressLabel={true}
value={record.allocatedQuantity}
value={allocated}
maximum={required}
/>
);
@@ -664,9 +665,10 @@ export default function BuildLineTable({
(record: any): RowAction[] => {
const part = record.part_detail ?? {};
const in_production = build.status == buildStatus.PRODUCTION;
const consumable = record.bom_item_detail?.consumable ?? false;
const consumable: boolean = record.bom_item_detail?.consumable ?? false;
const trackable: boolean = part?.trackable ?? false;
const hasOutput = !!output?.pk;
const hasOutput: boolean = !!output?.pk;
const required = Math.max(
0,
@@ -677,6 +679,7 @@ export default function BuildLineTable({
const canConsume =
in_production &&
!consumable &&
!trackable &&
record.allocated > 0 &&
user.hasChangeRole(UserRoles.build);
@@ -952,6 +955,9 @@ export default function BuildLineTable({
dataFormatter: formatRecords,
enableDownload: true,
enableSelection: true,
enableLabels: true,
modelType: ModelType.buildline,
onCellClick: () => {},
rowExpansion: rowExpansion
}}
/>

View File

@@ -23,6 +23,7 @@ import type { TableColumn } from '@lib/types/Tables';
import { useNavigate } from 'react-router-dom';
import ImporterDrawer from '../../components/importer/ImporterDrawer';
import { RenderInstance } from '../../components/render/Instance';
import { formatCurrency } from '../../defaults/formatters';
import { dataImporterSessionFields } from '../../forms/ImporterForms';
import {
usePurchaseOrderLineItemFields,
@@ -249,11 +250,15 @@ export function PurchaseOrderLineItemTable({
accessor: 'purchase_price',
title: t`Unit Price`
}),
CurrencyColumn({
{
accessor: 'total_price',
currency_accessor: 'purchase_price_currency',
title: t`Total Price`
}),
title: t`Total Price`,
render: (record: any) =>
formatCurrency(record.purchase_price, {
currency: record.purchase_price_currency,
multiplier: record.quantity
})
},
TargetDateColumn({}),
LocationColumn({
accessor: 'destination_detail',

View File

@@ -358,6 +358,7 @@ export function UserTable({
title: t`Delete user`,
successMessage: t`User deleted`,
table: table,
preFormContent: <></>,
preFormWarning: t`Are you sure you want to delete this user?`
});