inventree-sl-guard 0.3.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 SL Guard contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,129 @@
1
+ Metadata-Version: 2.4
2
+ Name: inventree-sl-guard
3
+ Version: 0.3.1
4
+ Summary: SL Guard: protect InvenTree stock locations by type, with controlled relocation and bulk assignment
5
+ Author: SntPartizan
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/SntPartizan/inventree-sl-guard
8
+ Project-URL: Repository, https://github.com/SntPartizan/inventree-sl-guard
9
+ Project-URL: Issues, https://github.com/SntPartizan/inventree-sl-guard/issues
10
+ Keywords: inventree,plugin,stock-location,permissions,bulk-edit
11
+ Classifier: Framework :: Django
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Framework :: InvenTree
14
+ Requires-Python: >=3.11
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Dynamic: license-file
18
+
19
+ # InvenTree SL Guard
20
+
21
+ **Community-developed InvenTree plugin — v0.3.1 beta, verified on InvenTree 1.6.1.**
22
+
23
+ **A warehouse is a physical system, not an arbitrary tree of editable database records.** Rooms, aisles, racks and fixed shelves occupy real places. When their digital hierarchy changes without a matching change on the warehouse floor, the location map stops reflecting reality.
24
+
25
+ **A warehouse is not a nesting doll.** You cannot put the warehouse itself inside a cardboard box. A room does not belong in a bag; nor should a rack or fixed shelf suddenly become the child of a movable container in the digital location tree. **If the database says a shelf is inside a bag while the shelf is bolted to the wall, the database is wrong — not the physical world.**
26
+
27
+ SL Guard helps prevent such unauthorized structural rearrangements for **locations whose types an administrator has marked as protected**. It enforces those movement restrictions; it is not a general-purpose dimensional or physical-fit validator.
28
+
29
+ **SL Guard gives InvenTree's stock-location hierarchy a protected structural backbone.** Administrators designate types that represent fixed infrastructure. Unauthorized users cannot relocate those locations, change an existing protected type, or indirectly move protected descendants by moving an ancestor. Authorized staff can deliberately revise the layout, while everyday stock movements remain governed by normal InvenTree permissions.
30
+
31
+ **Keep the digital warehouse true to the physical one. Let inventory move — not the infrastructure around it.**
32
+
33
+ [![Support on Ko-fi](https://img.shields.io/badge/Support%20on-Ko--fi-FF5E5B?logo=kofi&logoColor=white)](https://ko-fi.com/sntpartizan)
34
+
35
+ SL Guard is free and open source. If you find it useful, you can [support continued development on Ko-fi](https://ko-fi.com/sntpartizan). Donations are entirely optional.
36
+
37
+ ### Features
38
+
39
+ - Choose protected `StockLocationType` records by **database ID**, not by name.
40
+ - Let normal users **assign** a protected type when they have regular InvenTree stock-location edit permissions.
41
+ - Block changing the parent or removing/replacing an *existing* protected type, except for superusers and groups explicitly allowed by the plugin, subject to normal InvenTree permissions and ownership checks.
42
+ - Prevent indirect relocation of protected descendants when moving an unprotected ancestor.
43
+ - Bulk-assign types from an embedded panel: full hierarchical paths, selectable search fields (name, path, current type, ID), optional JavaScript regex, checkboxes, preview/dry-run, transactional server-side commit.
44
+ - Retain ordinary editing of names/descriptions, normal stock-item moves, and moves of locations without protected descendants.
45
+ - Optional **SL Guard** navigation shortcut and **SL Guard** dashboard widget (matching shield symbol). Both are independently configurable by the superuser; hiding either does not disable protection or remove the management route.
46
+
47
+ > **Compatibility caution:** This beta is tested on **InvenTree 1.6.1**, not on every InvenTree release. It wraps an internal `LocationSerializer.validate` method at runtime to pass the authenticated request user to the model validation hook. This is a monkey patch, not a stable public plugin API. Review and test before upgrading InvenTree.
48
+
49
+ ## Installation from InvenTree (recommended)
50
+
51
+ This package is **not yet published on PyPI**. Install it directly from the public GitHub repository using InvenTree's built-in plugin installer (verified on InvenTree 1.6.1).
52
+
53
+ 1. Back up your database and configuration; test on a non-production instance first. Ensure external plugins are enabled (`plugins_enabled: true`).
54
+ 2. Sign in as an **InvenTree superuser**, then open **Settings → Admin → Plugins → Install Plugin**.
55
+ 3. Fill out the installer form:
56
+
57
+ | Field | Value |
58
+ | --- | --- |
59
+ | Package Name | `inventree-sl-guard` |
60
+ | Source URL | `git+https://github.com/SntPartizan/inventree-sl-guard.git` |
61
+ | Version | Leave blank |
62
+ | Confirm plugin installation | Enabled |
63
+
64
+ 4. Click **Install**. InvenTree runs pip in its own Python environment, discovers the plugin and collects its static files.
65
+ 5. In the plugin list, **activate SL Guard** (listed as `SL Guard`). Restart the InvenTree web process and background workers if needed so all processes load the new code. If the UI still shows old JavaScript, hard-refresh the browser; run `manage.py collectplugins` with the correct InvenTree configuration only if static-file collection needs troubleshooting.
66
+ 6. Configure **Protected StockLocationType IDs** and, optionally, **Authorized group IDs** in SL Guard settings. The management page is at `/web/plugin/sl-guard/manage` (relative to your InvenTree server).
67
+ 7. Test that an ordinary user cannot relocate a protected stock location and that an authorized administrator can.
68
+
69
+ **Dashboard widget:** SL Guard makes the widget *available*, but does not add it automatically. Add it through the normal **Dashboard → Add Widgets** interface. The navigation item and dashboard widget can be enabled or disabled independently in SL Guard settings; disabling these shortcuts does not disable protection.
70
+
71
+ ### Updating from GitHub
72
+
73
+ From **Settings → Admin → Plugins**, use the installed package's **Update** action. Supply the same **Source URL** (`git+https://github.com/SntPartizan/inventree-sl-guard.git`) again; InvenTree 1.6.1 pre-fills the package name but may not pre-fill the Git URL. Leave **Version** blank for the current repository branch. After upgrading, check the displayed version and verify the protection rules. Settings persist for upgrades of the same `sl-guard` plugin, but should still be verified.
74
+
75
+ For reproducible deployments, pin a tested Git tag in the URL (for example `git+https://github.com/SntPartizan/inventree-sl-guard.git@v0.3.1` **after creating that tag**). No PyPI-based install or update is available until this package is actually published there.
76
+
77
+ ### Alternative: manual wheel installation
78
+
79
+ For offline development or deployments where the GUI installer is unavailable, install a downloaded release wheel in the **same Python environment** as InvenTree:
80
+
81
+ ```bash
82
+ python -m pip install --no-deps --upgrade ./inventree_sl_guard-0.3.1-py3-none-any.whl
83
+ ```
84
+
85
+ Restart web and worker processes, activate the plugin and collect static files if needed. Do not run the command against an unrelated system Python environment.
86
+
87
+ The UI brand is **SL Guard** in every language. English and Russian panel translations are provided. The distribution is `inventree-sl-guard`, the plugin's display name is `SL Guard`, and its slug is `sl-guard`.
88
+
89
+ See [Russian instructions](README_RU.md), [localization notes](docs/localization.md), and [deployment notes](docs/deployment.md).
90
+
91
+ ## Permissions
92
+
93
+ | Operation | User with normal location-edit permission | Superuser / configured group (plus normal permissions) |
94
+ | --- | --- | --- |
95
+ | Assign a protected type to an unprotected location | Allowed | Allowed |
96
+ | Change an already-protected type | Blocked | Allowed |
97
+ | Move a protected location | Blocked | Allowed |
98
+ | Move an unprotected location containing protected descendants | Blocked | Allowed |
99
+ | Edit location name / description | Allowed | Allowed |
100
+
101
+ The settings (`PROTECTED_TYPE_IDS`, `ALLOWED_GROUP_IDS`) are administered by a superuser; a group ID alone does not replace InvenTree's own role and ownership checks.
102
+
103
+ ## Search and bulk assignment
104
+
105
+ The page shows each location's **full path** and unique ID. Search checks **name only by default**, so a query for `Rack` will not match boxes merely because their ancestor is a rack. Enable path/type/ID matching separately. Regex examples: `Rack|Shelf` (OR), `^(Rack|Shelf)` (start of name), `\d+` (digits). Selection is always by primary key, not the displayed path. Preview before applying a batch.
106
+
107
+ For large batches the UI has a separate 120-second HTTP timeout. A browser timeout does **not** prove rollback: reload the list and check current types before retrying.
108
+
109
+ ## Testing and packaging
110
+
111
+ ```bash
112
+ python -m pip install -e . --no-deps
113
+ python -m pip install pytest build twine
114
+ python -m pytest -q tests
115
+ node --test tests/js/*.test.mjs
116
+ python -m build
117
+ python -m twine check dist/*
118
+ ```
119
+
120
+ These tests cover policy, UI filtering and regression checks; they **do not** replace InvenTree/DB/API integration tests. [Required pre-release scenarios](docs/test-matrix.md).
121
+
122
+ ## Security and limitations
123
+
124
+ - Model `save()` validation cannot catch all direct SQL, `QuerySet.update()`, tree-bulk operations, or deletion of protected types/locations. Do not advertise this plugin as a comprehensive database-level access-control solution.
125
+ - The authenticated-user integration is tied to InvenTree 1.6.1 internals. Verify the bridge on every supported version.
126
+ - The default InvenTree barcode-scanning UI may hide the error reason and show a generic message. An **optional** developer-only script is in `tools/patch_scan_error_ui.py`; it **modifies InvenTree frontend source** and is **not part of the wheel**. Do not apply it automatically.
127
+ - The embedded admin UI supports **English (default) and Russian** based on the InvenTree frontend locale. Server-side API errors use the active Django language. Plugin metadata and static settings labels use English.
128
+
129
+ For responsible disclosure see [SECURITY.md](SECURITY.md). Licensed under [MIT](LICENSE). This is an independent community project, not an official InvenTree component.
@@ -0,0 +1,111 @@
1
+ # InvenTree SL Guard
2
+
3
+ **Community-developed InvenTree plugin — v0.3.1 beta, verified on InvenTree 1.6.1.**
4
+
5
+ **A warehouse is a physical system, not an arbitrary tree of editable database records.** Rooms, aisles, racks and fixed shelves occupy real places. When their digital hierarchy changes without a matching change on the warehouse floor, the location map stops reflecting reality.
6
+
7
+ **A warehouse is not a nesting doll.** You cannot put the warehouse itself inside a cardboard box. A room does not belong in a bag; nor should a rack or fixed shelf suddenly become the child of a movable container in the digital location tree. **If the database says a shelf is inside a bag while the shelf is bolted to the wall, the database is wrong — not the physical world.**
8
+
9
+ SL Guard helps prevent such unauthorized structural rearrangements for **locations whose types an administrator has marked as protected**. It enforces those movement restrictions; it is not a general-purpose dimensional or physical-fit validator.
10
+
11
+ **SL Guard gives InvenTree's stock-location hierarchy a protected structural backbone.** Administrators designate types that represent fixed infrastructure. Unauthorized users cannot relocate those locations, change an existing protected type, or indirectly move protected descendants by moving an ancestor. Authorized staff can deliberately revise the layout, while everyday stock movements remain governed by normal InvenTree permissions.
12
+
13
+ **Keep the digital warehouse true to the physical one. Let inventory move — not the infrastructure around it.**
14
+
15
+ [![Support on Ko-fi](https://img.shields.io/badge/Support%20on-Ko--fi-FF5E5B?logo=kofi&logoColor=white)](https://ko-fi.com/sntpartizan)
16
+
17
+ SL Guard is free and open source. If you find it useful, you can [support continued development on Ko-fi](https://ko-fi.com/sntpartizan). Donations are entirely optional.
18
+
19
+ ### Features
20
+
21
+ - Choose protected `StockLocationType` records by **database ID**, not by name.
22
+ - Let normal users **assign** a protected type when they have regular InvenTree stock-location edit permissions.
23
+ - Block changing the parent or removing/replacing an *existing* protected type, except for superusers and groups explicitly allowed by the plugin, subject to normal InvenTree permissions and ownership checks.
24
+ - Prevent indirect relocation of protected descendants when moving an unprotected ancestor.
25
+ - Bulk-assign types from an embedded panel: full hierarchical paths, selectable search fields (name, path, current type, ID), optional JavaScript regex, checkboxes, preview/dry-run, transactional server-side commit.
26
+ - Retain ordinary editing of names/descriptions, normal stock-item moves, and moves of locations without protected descendants.
27
+ - Optional **SL Guard** navigation shortcut and **SL Guard** dashboard widget (matching shield symbol). Both are independently configurable by the superuser; hiding either does not disable protection or remove the management route.
28
+
29
+ > **Compatibility caution:** This beta is tested on **InvenTree 1.6.1**, not on every InvenTree release. It wraps an internal `LocationSerializer.validate` method at runtime to pass the authenticated request user to the model validation hook. This is a monkey patch, not a stable public plugin API. Review and test before upgrading InvenTree.
30
+
31
+ ## Installation from InvenTree (recommended)
32
+
33
+ This package is **not yet published on PyPI**. Install it directly from the public GitHub repository using InvenTree's built-in plugin installer (verified on InvenTree 1.6.1).
34
+
35
+ 1. Back up your database and configuration; test on a non-production instance first. Ensure external plugins are enabled (`plugins_enabled: true`).
36
+ 2. Sign in as an **InvenTree superuser**, then open **Settings → Admin → Plugins → Install Plugin**.
37
+ 3. Fill out the installer form:
38
+
39
+ | Field | Value |
40
+ | --- | --- |
41
+ | Package Name | `inventree-sl-guard` |
42
+ | Source URL | `git+https://github.com/SntPartizan/inventree-sl-guard.git` |
43
+ | Version | Leave blank |
44
+ | Confirm plugin installation | Enabled |
45
+
46
+ 4. Click **Install**. InvenTree runs pip in its own Python environment, discovers the plugin and collects its static files.
47
+ 5. In the plugin list, **activate SL Guard** (listed as `SL Guard`). Restart the InvenTree web process and background workers if needed so all processes load the new code. If the UI still shows old JavaScript, hard-refresh the browser; run `manage.py collectplugins` with the correct InvenTree configuration only if static-file collection needs troubleshooting.
48
+ 6. Configure **Protected StockLocationType IDs** and, optionally, **Authorized group IDs** in SL Guard settings. The management page is at `/web/plugin/sl-guard/manage` (relative to your InvenTree server).
49
+ 7. Test that an ordinary user cannot relocate a protected stock location and that an authorized administrator can.
50
+
51
+ **Dashboard widget:** SL Guard makes the widget *available*, but does not add it automatically. Add it through the normal **Dashboard → Add Widgets** interface. The navigation item and dashboard widget can be enabled or disabled independently in SL Guard settings; disabling these shortcuts does not disable protection.
52
+
53
+ ### Updating from GitHub
54
+
55
+ From **Settings → Admin → Plugins**, use the installed package's **Update** action. Supply the same **Source URL** (`git+https://github.com/SntPartizan/inventree-sl-guard.git`) again; InvenTree 1.6.1 pre-fills the package name but may not pre-fill the Git URL. Leave **Version** blank for the current repository branch. After upgrading, check the displayed version and verify the protection rules. Settings persist for upgrades of the same `sl-guard` plugin, but should still be verified.
56
+
57
+ For reproducible deployments, pin a tested Git tag in the URL (for example `git+https://github.com/SntPartizan/inventree-sl-guard.git@v0.3.1` **after creating that tag**). No PyPI-based install or update is available until this package is actually published there.
58
+
59
+ ### Alternative: manual wheel installation
60
+
61
+ For offline development or deployments where the GUI installer is unavailable, install a downloaded release wheel in the **same Python environment** as InvenTree:
62
+
63
+ ```bash
64
+ python -m pip install --no-deps --upgrade ./inventree_sl_guard-0.3.1-py3-none-any.whl
65
+ ```
66
+
67
+ Restart web and worker processes, activate the plugin and collect static files if needed. Do not run the command against an unrelated system Python environment.
68
+
69
+ The UI brand is **SL Guard** in every language. English and Russian panel translations are provided. The distribution is `inventree-sl-guard`, the plugin's display name is `SL Guard`, and its slug is `sl-guard`.
70
+
71
+ See [Russian instructions](README_RU.md), [localization notes](docs/localization.md), and [deployment notes](docs/deployment.md).
72
+
73
+ ## Permissions
74
+
75
+ | Operation | User with normal location-edit permission | Superuser / configured group (plus normal permissions) |
76
+ | --- | --- | --- |
77
+ | Assign a protected type to an unprotected location | Allowed | Allowed |
78
+ | Change an already-protected type | Blocked | Allowed |
79
+ | Move a protected location | Blocked | Allowed |
80
+ | Move an unprotected location containing protected descendants | Blocked | Allowed |
81
+ | Edit location name / description | Allowed | Allowed |
82
+
83
+ The settings (`PROTECTED_TYPE_IDS`, `ALLOWED_GROUP_IDS`) are administered by a superuser; a group ID alone does not replace InvenTree's own role and ownership checks.
84
+
85
+ ## Search and bulk assignment
86
+
87
+ The page shows each location's **full path** and unique ID. Search checks **name only by default**, so a query for `Rack` will not match boxes merely because their ancestor is a rack. Enable path/type/ID matching separately. Regex examples: `Rack|Shelf` (OR), `^(Rack|Shelf)` (start of name), `\d+` (digits). Selection is always by primary key, not the displayed path. Preview before applying a batch.
88
+
89
+ For large batches the UI has a separate 120-second HTTP timeout. A browser timeout does **not** prove rollback: reload the list and check current types before retrying.
90
+
91
+ ## Testing and packaging
92
+
93
+ ```bash
94
+ python -m pip install -e . --no-deps
95
+ python -m pip install pytest build twine
96
+ python -m pytest -q tests
97
+ node --test tests/js/*.test.mjs
98
+ python -m build
99
+ python -m twine check dist/*
100
+ ```
101
+
102
+ These tests cover policy, UI filtering and regression checks; they **do not** replace InvenTree/DB/API integration tests. [Required pre-release scenarios](docs/test-matrix.md).
103
+
104
+ ## Security and limitations
105
+
106
+ - Model `save()` validation cannot catch all direct SQL, `QuerySet.update()`, tree-bulk operations, or deletion of protected types/locations. Do not advertise this plugin as a comprehensive database-level access-control solution.
107
+ - The authenticated-user integration is tied to InvenTree 1.6.1 internals. Verify the bridge on every supported version.
108
+ - The default InvenTree barcode-scanning UI may hide the error reason and show a generic message. An **optional** developer-only script is in `tools/patch_scan_error_ui.py`; it **modifies InvenTree frontend source** and is **not part of the wheel**. Do not apply it automatically.
109
+ - The embedded admin UI supports **English (default) and Russian** based on the InvenTree frontend locale. Server-side API errors use the active Django language. Plugin metadata and static settings labels use English.
110
+
111
+ For responsible disclosure see [SECURITY.md](SECURITY.md). Licensed under [MIT](LICENSE). This is an independent community project, not an official InvenTree component.
@@ -0,0 +1,129 @@
1
+ Metadata-Version: 2.4
2
+ Name: inventree-sl-guard
3
+ Version: 0.3.1
4
+ Summary: SL Guard: protect InvenTree stock locations by type, with controlled relocation and bulk assignment
5
+ Author: SntPartizan
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/SntPartizan/inventree-sl-guard
8
+ Project-URL: Repository, https://github.com/SntPartizan/inventree-sl-guard
9
+ Project-URL: Issues, https://github.com/SntPartizan/inventree-sl-guard/issues
10
+ Keywords: inventree,plugin,stock-location,permissions,bulk-edit
11
+ Classifier: Framework :: Django
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Framework :: InvenTree
14
+ Requires-Python: >=3.11
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Dynamic: license-file
18
+
19
+ # InvenTree SL Guard
20
+
21
+ **Community-developed InvenTree plugin — v0.3.1 beta, verified on InvenTree 1.6.1.**
22
+
23
+ **A warehouse is a physical system, not an arbitrary tree of editable database records.** Rooms, aisles, racks and fixed shelves occupy real places. When their digital hierarchy changes without a matching change on the warehouse floor, the location map stops reflecting reality.
24
+
25
+ **A warehouse is not a nesting doll.** You cannot put the warehouse itself inside a cardboard box. A room does not belong in a bag; nor should a rack or fixed shelf suddenly become the child of a movable container in the digital location tree. **If the database says a shelf is inside a bag while the shelf is bolted to the wall, the database is wrong — not the physical world.**
26
+
27
+ SL Guard helps prevent such unauthorized structural rearrangements for **locations whose types an administrator has marked as protected**. It enforces those movement restrictions; it is not a general-purpose dimensional or physical-fit validator.
28
+
29
+ **SL Guard gives InvenTree's stock-location hierarchy a protected structural backbone.** Administrators designate types that represent fixed infrastructure. Unauthorized users cannot relocate those locations, change an existing protected type, or indirectly move protected descendants by moving an ancestor. Authorized staff can deliberately revise the layout, while everyday stock movements remain governed by normal InvenTree permissions.
30
+
31
+ **Keep the digital warehouse true to the physical one. Let inventory move — not the infrastructure around it.**
32
+
33
+ [![Support on Ko-fi](https://img.shields.io/badge/Support%20on-Ko--fi-FF5E5B?logo=kofi&logoColor=white)](https://ko-fi.com/sntpartizan)
34
+
35
+ SL Guard is free and open source. If you find it useful, you can [support continued development on Ko-fi](https://ko-fi.com/sntpartizan). Donations are entirely optional.
36
+
37
+ ### Features
38
+
39
+ - Choose protected `StockLocationType` records by **database ID**, not by name.
40
+ - Let normal users **assign** a protected type when they have regular InvenTree stock-location edit permissions.
41
+ - Block changing the parent or removing/replacing an *existing* protected type, except for superusers and groups explicitly allowed by the plugin, subject to normal InvenTree permissions and ownership checks.
42
+ - Prevent indirect relocation of protected descendants when moving an unprotected ancestor.
43
+ - Bulk-assign types from an embedded panel: full hierarchical paths, selectable search fields (name, path, current type, ID), optional JavaScript regex, checkboxes, preview/dry-run, transactional server-side commit.
44
+ - Retain ordinary editing of names/descriptions, normal stock-item moves, and moves of locations without protected descendants.
45
+ - Optional **SL Guard** navigation shortcut and **SL Guard** dashboard widget (matching shield symbol). Both are independently configurable by the superuser; hiding either does not disable protection or remove the management route.
46
+
47
+ > **Compatibility caution:** This beta is tested on **InvenTree 1.6.1**, not on every InvenTree release. It wraps an internal `LocationSerializer.validate` method at runtime to pass the authenticated request user to the model validation hook. This is a monkey patch, not a stable public plugin API. Review and test before upgrading InvenTree.
48
+
49
+ ## Installation from InvenTree (recommended)
50
+
51
+ This package is **not yet published on PyPI**. Install it directly from the public GitHub repository using InvenTree's built-in plugin installer (verified on InvenTree 1.6.1).
52
+
53
+ 1. Back up your database and configuration; test on a non-production instance first. Ensure external plugins are enabled (`plugins_enabled: true`).
54
+ 2. Sign in as an **InvenTree superuser**, then open **Settings → Admin → Plugins → Install Plugin**.
55
+ 3. Fill out the installer form:
56
+
57
+ | Field | Value |
58
+ | --- | --- |
59
+ | Package Name | `inventree-sl-guard` |
60
+ | Source URL | `git+https://github.com/SntPartizan/inventree-sl-guard.git` |
61
+ | Version | Leave blank |
62
+ | Confirm plugin installation | Enabled |
63
+
64
+ 4. Click **Install**. InvenTree runs pip in its own Python environment, discovers the plugin and collects its static files.
65
+ 5. In the plugin list, **activate SL Guard** (listed as `SL Guard`). Restart the InvenTree web process and background workers if needed so all processes load the new code. If the UI still shows old JavaScript, hard-refresh the browser; run `manage.py collectplugins` with the correct InvenTree configuration only if static-file collection needs troubleshooting.
66
+ 6. Configure **Protected StockLocationType IDs** and, optionally, **Authorized group IDs** in SL Guard settings. The management page is at `/web/plugin/sl-guard/manage` (relative to your InvenTree server).
67
+ 7. Test that an ordinary user cannot relocate a protected stock location and that an authorized administrator can.
68
+
69
+ **Dashboard widget:** SL Guard makes the widget *available*, but does not add it automatically. Add it through the normal **Dashboard → Add Widgets** interface. The navigation item and dashboard widget can be enabled or disabled independently in SL Guard settings; disabling these shortcuts does not disable protection.
70
+
71
+ ### Updating from GitHub
72
+
73
+ From **Settings → Admin → Plugins**, use the installed package's **Update** action. Supply the same **Source URL** (`git+https://github.com/SntPartizan/inventree-sl-guard.git`) again; InvenTree 1.6.1 pre-fills the package name but may not pre-fill the Git URL. Leave **Version** blank for the current repository branch. After upgrading, check the displayed version and verify the protection rules. Settings persist for upgrades of the same `sl-guard` plugin, but should still be verified.
74
+
75
+ For reproducible deployments, pin a tested Git tag in the URL (for example `git+https://github.com/SntPartizan/inventree-sl-guard.git@v0.3.1` **after creating that tag**). No PyPI-based install or update is available until this package is actually published there.
76
+
77
+ ### Alternative: manual wheel installation
78
+
79
+ For offline development or deployments where the GUI installer is unavailable, install a downloaded release wheel in the **same Python environment** as InvenTree:
80
+
81
+ ```bash
82
+ python -m pip install --no-deps --upgrade ./inventree_sl_guard-0.3.1-py3-none-any.whl
83
+ ```
84
+
85
+ Restart web and worker processes, activate the plugin and collect static files if needed. Do not run the command against an unrelated system Python environment.
86
+
87
+ The UI brand is **SL Guard** in every language. English and Russian panel translations are provided. The distribution is `inventree-sl-guard`, the plugin's display name is `SL Guard`, and its slug is `sl-guard`.
88
+
89
+ See [Russian instructions](README_RU.md), [localization notes](docs/localization.md), and [deployment notes](docs/deployment.md).
90
+
91
+ ## Permissions
92
+
93
+ | Operation | User with normal location-edit permission | Superuser / configured group (plus normal permissions) |
94
+ | --- | --- | --- |
95
+ | Assign a protected type to an unprotected location | Allowed | Allowed |
96
+ | Change an already-protected type | Blocked | Allowed |
97
+ | Move a protected location | Blocked | Allowed |
98
+ | Move an unprotected location containing protected descendants | Blocked | Allowed |
99
+ | Edit location name / description | Allowed | Allowed |
100
+
101
+ The settings (`PROTECTED_TYPE_IDS`, `ALLOWED_GROUP_IDS`) are administered by a superuser; a group ID alone does not replace InvenTree's own role and ownership checks.
102
+
103
+ ## Search and bulk assignment
104
+
105
+ The page shows each location's **full path** and unique ID. Search checks **name only by default**, so a query for `Rack` will not match boxes merely because their ancestor is a rack. Enable path/type/ID matching separately. Regex examples: `Rack|Shelf` (OR), `^(Rack|Shelf)` (start of name), `\d+` (digits). Selection is always by primary key, not the displayed path. Preview before applying a batch.
106
+
107
+ For large batches the UI has a separate 120-second HTTP timeout. A browser timeout does **not** prove rollback: reload the list and check current types before retrying.
108
+
109
+ ## Testing and packaging
110
+
111
+ ```bash
112
+ python -m pip install -e . --no-deps
113
+ python -m pip install pytest build twine
114
+ python -m pytest -q tests
115
+ node --test tests/js/*.test.mjs
116
+ python -m build
117
+ python -m twine check dist/*
118
+ ```
119
+
120
+ These tests cover policy, UI filtering and regression checks; they **do not** replace InvenTree/DB/API integration tests. [Required pre-release scenarios](docs/test-matrix.md).
121
+
122
+ ## Security and limitations
123
+
124
+ - Model `save()` validation cannot catch all direct SQL, `QuerySet.update()`, tree-bulk operations, or deletion of protected types/locations. Do not advertise this plugin as a comprehensive database-level access-control solution.
125
+ - The authenticated-user integration is tied to InvenTree 1.6.1 internals. Verify the bridge on every supported version.
126
+ - The default InvenTree barcode-scanning UI may hide the error reason and show a generic message. An **optional** developer-only script is in `tools/patch_scan_error_ui.py`; it **modifies InvenTree frontend source** and is **not part of the wheel**. Do not apply it automatically.
127
+ - The embedded admin UI supports **English (default) and Russian** based on the InvenTree frontend locale. Server-side API errors use the active Django language. Plugin metadata and static settings labels use English.
128
+
129
+ For responsible disclosure see [SECURITY.md](SECURITY.md). Licensed under [MIT](LICENSE). This is an independent community project, not an official InvenTree component.
@@ -0,0 +1,21 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ inventree_sl_guard.egg-info/PKG-INFO
5
+ inventree_sl_guard.egg-info/SOURCES.txt
6
+ inventree_sl_guard.egg-info/dependency_links.txt
7
+ inventree_sl_guard.egg-info/entry_points.txt
8
+ inventree_sl_guard.egg-info/top_level.txt
9
+ sl_guard/__init__.py
10
+ sl_guard/i18n.py
11
+ sl_guard/plugin.py
12
+ sl_guard/policy.py
13
+ sl_guard/serializer_bridge.py
14
+ sl_guard/views.py
15
+ sl_guard/static/sl_guard_dashboard.js
16
+ sl_guard/static/sl_guard_panel.js
17
+ tests/test_brand_and_dashboard.py
18
+ tests/test_bulk_query_regression.py
19
+ tests/test_frontend_patch.py
20
+ tests/test_policy.py
21
+ tests/test_serializer_bridge.py
@@ -0,0 +1,2 @@
1
+ [inventree_plugins]
2
+ SLGuard = sl_guard.plugin:SLGuard
@@ -0,0 +1,32 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "inventree-sl-guard"
7
+ version = "0.3.1"
8
+ description = "SL Guard: protect InvenTree stock locations by type, with controlled relocation and bulk assignment"
9
+ readme = "README.md"
10
+ authors = [{ name = "SntPartizan" }]
11
+ requires-python = ">=3.11"
12
+ license = "MIT"
13
+ keywords = ["inventree", "plugin", "stock-location", "permissions", "bulk-edit"]
14
+ classifiers = [
15
+ "Framework :: Django",
16
+ "Programming Language :: Python :: 3",
17
+ "Framework :: InvenTree",
18
+ ]
19
+
20
+ [project.urls]
21
+ Homepage = "https://github.com/SntPartizan/inventree-sl-guard"
22
+ Repository = "https://github.com/SntPartizan/inventree-sl-guard"
23
+ Issues = "https://github.com/SntPartizan/inventree-sl-guard/issues"
24
+
25
+ [project.entry-points.inventree_plugins]
26
+ SLGuard = "sl_guard.plugin:SLGuard"
27
+
28
+ [tool.setuptools.packages.find]
29
+ include = ["sl_guard*"]
30
+
31
+ [tool.setuptools.package-data]
32
+ sl_guard = ["static/*.js"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ """SL Guard — InvenTree stock location protection."""
2
+
3
+ __version__ = "0.3.1"
@@ -0,0 +1,12 @@
1
+ """Small self-contained EN/RU translations for the Django plugin layer.
2
+
3
+ The UI uses InvenTree's plugin context.locale. Django API error messages use
4
+ Django's active request language, which can differ from the SPA preference.
5
+ """
6
+ from django.utils.translation import get_language
7
+
8
+
9
+ def tr(english: str, russian: str) -> str:
10
+ """Use English by default and Russian when Django activates ru locale."""
11
+ language = (get_language() or 'en').lower().split('-')[0].split('_')[0]
12
+ return russian if language == 'ru' else english