Build order rules (#7842)

* Add new global setting

* Check if there are open children before completing a build

* Adds management command to export settings definition

* Fix settings export

* Extract settings data into documentation

* Add global settings spec

* User settings

* Revert strict mode

* Tweak unit test

* Remove unreachable code

* Always export settings first

* Remove unused macro

* Remove old images

* Re-add missing docs strings

* Tweak docs

* Remove unused import
This commit is contained in:
Oliver 2024-08-10 09:26:03 +10:00 committed by GitHub
parent 556a3161e8
commit 42183a3a3f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 375 additions and 158 deletions

1
.gitignore vendored
View File

@ -111,3 +111,4 @@ InvenTree/web/static
docs/schema.yml docs/schema.yml
docs/docs/api/*.yml docs/docs/api/*.yml
docs/docs/api/schema/*.yml docs/docs/api/schema/*.yml
inventree_settings.json

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

View File

@ -265,18 +265,16 @@ Build orders may (optionally) have a target complete date specified. If this dat
- Builds can be filtered by overdue status in the build list - Builds can be filtered by overdue status in the build list
- Overdue builds will be displayed on the home page - Overdue builds will be displayed on the home page
## Build Order Restrictions ## Build Order Settings
There are a number of optional restrictions which can be applied to build orders, which may be enabled or disabled in the system settings: The following [global settings](../settings/global.md) are available for adjusting the behavior of build orders:
### Require Active Part | Name | Description | Default | Units |
| ---- | ----------- | ------- | ----- |
If this option is enabled, build orders can only be created for parts which are marked as [Active](../part/part.md#active-parts). {{ globalsetting("BUILDORDER_REFERENCE_PATTERN") }}
{{ globalsetting("BUILDORDER_REQUIRE_RESPONSIBLE") }}
### Require Locked Part {{ globalsetting("BUILDORDER_REQUIRE_ACTIVE_PART") }}
{{ globalsetting("BUILDORDER_REQUIRE_LOCKED_PART") }}
If this option is enabled, build orders can only be created for parts which are marked as [Locked](../part/part.md#locked-parts). {{ globalsetting("BUILDORDER_REQUIRE_VALID_BOM") }}
{{ globalsetting("BUILDORDER_REQUIRE_CLOSED_CHILDS") }}
### Require Valid BOM {{ globalsetting("PREVENT_BUILD_COMPLETION_HAVING_INCOMPLETED_TESTS") }}
If this option is enabled, build orders can only be created for parts which have a valid [Bill of Materials](./bom.md) defined.

View File

@ -139,3 +139,14 @@ This view can be accessed externally as an ICS calendar using a URL like the fol
`http://inventree.example.org/api/order/calendar/purchase-order/calendar.ics` `http://inventree.example.org/api/order/calendar/purchase-order/calendar.ics`
by default, completed orders are not exported. These can be included by appending `?include_completed=True` to the URL. by default, completed orders are not exported. These can be included by appending `?include_completed=True` to the URL.
## Purchase Order Settings
The following [global settings](../settings/global.md) are available for purchase orders:
| Name | Description | Default | Units |
| ---- | ----------- | ------- | ----- |
{{ globalsetting("PURCHASEORDER_REFERENCE_PATTERN") }}
{{ globalsetting("PURCHASEORDER_REQUIRE_RESPONSIBLE") }}
{{ globalsetting("PURCHASEORDER_EDIT_COMPLETED_ORDERS") }}
{{ globalsetting("PURCHASEORDER_AUTO_COMPLETE") }}

View File

@ -121,3 +121,14 @@ This view can be accessed externally as an ICS calendar using a URL like the fol
`http://inventree.example.org/api/order/calendar/return-order/calendar.ics` `http://inventree.example.org/api/order/calendar/return-order/calendar.ics`
by default, completed orders are not exported. These can be included by appending `?include_completed=True` to the URL. by default, completed orders are not exported. These can be included by appending `?include_completed=True` to the URL.
## Return Order Settings
The following [global settings](../settings/global.md) are available for return orders:
| Name | Description | Default | Units |
| ---- | ----------- | ------- | ----- |
{{ globalsetting("RETURNORDER_ENABLED") }}
{{ globalsetting("RETURNORDER_REFERENCE_PATTERN") }}
{{ globalsetting("RETURNORDER_REQUIRE_RESPONSIBLE") }}
{{ globalsetting("RETURNORDER_EDIT_COMPLETED_ORDERS") }}

View File

@ -183,3 +183,15 @@ All these fields can be edited by the user:
{% with id="edit-shipment", url="order/edit_shipment.png", description="Edit shipment" %} {% with id="edit-shipment", url="order/edit_shipment.png", description="Edit shipment" %}
{% include "img.html" %} {% include "img.html" %}
{% endwith %} {% endwith %}
## Sales Order Settings
The following [global settings](../settings/global.md) are available for sales orders:
| Name | Description | Default | Units |
| ---- | ----------- | ------- | ----- |
{{ globalsetting("SALESORDER_REFERENCE_PATTERN") }}
{{ globalsetting("SALESORDER_REQUIRE_RESPONSIBLE") }}
{{ globalsetting("SALESORDER_DEFAULT_SHIPMENT") }}
{{ globalsetting("SALESORDER_EDIT_COMPLETED_ORDERS") }}
{{ globalsetting("SALESORDER_SHIP_COMPLETE") }}

View File

@ -24,12 +24,7 @@ If a different currency exchange backend is needed, or a custom implementation i
### Currency Settings ### Currency Settings
In the [settings screen](./global.md), under the *Pricing* section, the following currency settings are available: Refer to the [global settings](./global.md#pricing-and-currency) documentation for more information on available currency settings.
| Setting | Description | Default Value |
| --- | --- |
| Default Currency | The selected *default* currency for the system. | USD |
| Supported Currencies | The list of supported currencies for the system. | AUD, CAD, CNY, EUR, GBP, JPY, NZD, USD |
#### Supported Currencies #### Supported Currencies

View File

@ -17,120 +17,139 @@ Global settings are arranged in the following categories:
### Server Settings ### Server Settings
Configuration of basic server settings. Configuration of basic server settings:
| Name | Description | Default | Units |
| ---- | ----------- | ------- | ----- |
{{ globalsetting("INVENTREE_BASE_URL") }}
{{ globalsetting("INVENTREE_COMPANY_NAME") }}
{{ globalsetting("INVENTREE_INSTANCE") }}
{{ globalsetting("INVENTREE_INSTANCE_TITLE") }}
{{ globalsetting("INVENTREE_RESTRICT_ABOUT") }}
{{ globalsetting("DISPLAY_FULL_NAMES") }}
{{ globalsetting("INVENTREE_UPDATE_CHECK_INTERVAL") }}
{{ globalsetting("INVENTREE_DOWNLOAD_FROM_URL") }}
{{ globalsetting("INVENTREE_DOWNLOAD_IMAGE_MAX_SIZE") }}
{{ globalsetting("INVENTREE_DOWNLOAD_FROM_URL_USER_AGENT") }}
{{ globalsetting("INVENTREE_REQUIRE_CONFIRM") }}
{{ globalsetting("INVENTREE_STRICT_URLS") }}
{{ globalsetting("INVENTREE_TREE_DEPTH") }}
{{ globalsetting("INVENTREE_BACKUP_ENABLE") }}
{{ globalsetting("INVENTREE_BACKUP_DAYS") }}
{{ globalsetting("INVENTREE_DELETE_TASKS_DAYS") }}
{{ globalsetting("INVENTREE_DELETE_ERRORS_DAYS") }}
{{ globalsetting("INVENTREE_DELETE_NOTIFICATIONS_DAYS") }}
| Setting | Type | Description | Default |
| --- | --- | --- | --- |
| InvenTree Instance Name | String | String descriptor for the InvenTree server instance | InvenTree Server |
| Use Instance Name | Boolean | Use instance name in title bars | False |
| Restrict showing `about` | Boolean | Show the `about` modal only to superusers | False |
| Base URL | String | Base URL for server instance | *blank* |
| Company Name | String | Company name | My company name |
| Download from URL | Boolean | Allow downloading of images from remote URLs | False |
### Login Settings ### Login Settings
Change how logins, password-forgot, signups are handled. Change how logins, password-forgot, signups are handled:
| Setting | Type | Description | Default | | Name | Description | Default | Units |
| --- | --- | --- | --- | | ---- | ----------- | ------- | ----- |
| Enable registration | Boolean | Enable self-registration for users on the login-pages | False | {{ globalsetting("LOGIN_ENABLE_PWD_FORGOT") }}
| Enable SSO | Boolean | Enable SSO on the login-pages | False | {{ globalsetting("LOGIN_MAIL_REQUIRED") }}
| Enable SSO registration | Boolean | Enable self-registration for users via SSO on the login-pages | False | {{ globalsetting("LOGIN_ENFORCE_MFA") }}
| Enable SSO group sync | Boolean | Enable synchronizing InvenTree groups directly from the IdP | False | {{ globalsetting("LOGIN_ENABLE_REG") }}
| SSO group key | String | The name of the groups claim attribute provided by the IdP | | {{ globalsetting("LOGIN_SIGNUP_MAIL_TWICE") }}
| SSO group map | String (JSON) | A mapping from SSO groups to local InvenTree groups | {} | {{ globalsetting("LOGIN_SIGNUP_PWD_TWICE") }}
| Remove groups outside of SSO | Boolean | Whether groups assigned to the user should be removed if they are not backend by the IdP. Disabling this setting might cause security issues | True | {{ globalsetting("SIGNUP_GROUP") }}
| Enable password forgot | Boolean | Enable password forgot function on the login-pages.<br><br>This will let users reset their passwords on their own. For this feature to work you need to configure E-mail | True | {{ globalsetting("LOGIN_SIGNUP_MAIL_RESTRICTION") }}
| E-Mail required | Boolean | Require user to supply e-mail on signup.<br><br>Without a way (e-mail) to contact the user notifications and security features might not work! | False | {{ globalsetting("LOGIN_ENABLE_SSO") }}
| Enforce MFA | Boolean | Users must use multifactor security.<br><br>This forces each user to setup MFA and use it on each authentication | False | {{ globalsetting("LOGIN_ENABLE_SSO_REG") }}
| Mail twice | Boolean | On signup ask users twice for their mail | False | {{ globalsetting("LOGIN_SIGNUP_SSO_AUTO") }}
| Password twice | Boolean | On signup ask users twice for their password | True | {{ globalsetting("LOGIN_ENABLE_SSO_GROUP_SYNC") }}
| Auto-fill SSO users | Boolean | Automatically fill out user-details from SSO account-data.<br><br>If this feature is enabled the user is only asked for their username, first- and surname if those values can not be gathered from their SSO profile. This might lead to unwanted usernames bleeding over. | True | {{ globalsetting("SSO_GROUP_MAP") }}
| Allowed domains | String | Restrict signup to certain domains (comma-separated, starting with @) | | {{ globalsetting("SSO_GROUP_KEY") }}
{{ globalsetting("SSO_REMOVE_GROUPS") }}
#### Require User Email
If this setting is enabled, users must provide an email address when signing up. Note that some notification and security features require a valid email address.
#### Forgot Password
If this setting is enabled, users can reset their password via email. This requires a valid email address to be associated with the user account.
#### Enforce Multi-Factor Authentication
If this setting is enabled, users must have multi-factor authentication enabled to log in.
#### Auto Fil SSO Users
Automatically fill out user-details from SSO account-data. If this feature is enabled the user is only asked for their username, first- and surname if those values can not be gathered from their SSO profile. This might lead to unwanted usernames bleeding over.
### Barcodes ### Barcodes
Configuration of barcode functionality Configuration of barcode functionality:
| Setting | Type | Description | Default | | Name | Description | Default | Units |
| --- | --- | --- | --- | | ---- | ----------- | ------- | ----- |
| Barcode Support | Boolean | Enable barcode functionality in web interface | True | {{ globalsetting("BARCODE_ENABLE") }}
{{ globalsetting("BARCODE_INPUT_DELAY") }}
{{ globalsetting("BARCODE_WEBCAM_SUPPORT") }}
{{ globalsetting("BARCODE_SHOW_TEXT") }}
{{ globalsetting("BARCODE_GENERATION_PLUGIN") }}
### Currencies ### Pricing and Currency
Configuration of currency support Configuration of pricing data and currency support:
| Setting | Type | Description | Default | | Name | Description | Default | Units |
| --- | --- | --- | --- | | ---- | ----------- | ------- | ----- |
| Default Currency | Currency | Default currency | USD | {{ globalsetting("INVENTREE_DEFAULT_CURRENCY") }}
{{ globalsetting("CURRENCY_CODES") }}
{{ globalsetting("PART_INTERNAL_PRICE") }}
{{ globalsetting("PART_BOM_USE_INTERNAL_PRICE") }}
{{ globalsetting("PRICING_DECIMAL_PLACES_MIN") }}
{{ globalsetting("PRICING_DECIMAL_PLACES") }}
{{ globalsetting("PRICING_UPDATE_DAYS") }}
{{ globalsetting("PRICING_USE_SUPPLIER_PRICING") }}
{{ globalsetting("PRICING_PURCHASE_HISTORY_OVERRIDES_SUPPLIER") }}
{{ globalsetting("PRICING_USE_STOCK_PRICING") }}
{{ globalsetting("PRICING_STOCK_ITEM_AGE_DAYS") }}
{{ globalsetting("PRICING_USE_VARIANT_PRICING") }}
{{ globalsetting("PRICING_ACTIVE_VARIANTS") }}
### Reporting ### Reporting
Configuration of report generation Configuration of report generation:
| Setting | Type | Description | Default | | Name | Description | Default | Units |
| --- | --- | --- | --- | | ---- | ----------- | ------- | ----- |
| Enable Reports | Boolean | Enable report generation | False | {{ globalsetting("REPORT_ENABLE") }}
| Page Size | String | Default page size | A4 | {{ globalsetting("REPORT_DEFAULT_PAGE_SIZE") }}
| Debug Mode | Boolean | Generate reports in debug mode (HTML output) | False | {{ globalsetting("REPORT_DEBUG_MODE") }}
| Test Reports | Boolean | Enable generation of test reports | False | {{ globalsetting("REPORT_LOG_ERRORS") }}
{{ globalsetting("REPORT_ENABLE_TEST_REPORT") }}
{{ globalsetting("REPORT_ATTACH_TEST_REPORT") }}
### Parts ### Parts
#### Main Settings | Name | Description | Default | Units |
| ---- | ----------- | ------- | ----- |
| Setting | Type | Description | Default | {{ globalsetting("PART_IPN_REGEX") }}
| --- | --- | --- | --- | {{ globalsetting("PART_ALLOW_DUPLICATE_IPN") }}
| IPN Regex | String | Regular expression pattern for matching Part IPN | *blank* | {{ globalsetting("PART_ALLOW_EDIT_IPN") }}
| Allow Duplicate IPN | Boolean | Allow multiple parts to share the same IPN | True | {{ globalsetting("PART_ALLOW_DELETE_FROM_ASSEMBLY") }}
| Allow Editing IPN | Boolean | Allow changing the IPN value while editing a part | True | {{ globalsetting("PART_ENABLE_REVISION") }}
| Part Name Display Format | String | Format to display the part name | {% raw %}`{{ part.id if part.id }}{{ ' | ' if part.id }}{{ part.name }}{{ ' | ' if part.revision }}{{ part.revision if part.revision }}`{% endraw %} | {{ globalsetting("PART_REVISION_ASSEMBLY_ONLY") }}
| Show Price History | Boolean | Display historical pricing for Part | False | {{ globalsetting("PART_NAME_FORMAT") }}
| Show Price in Forms | Boolean | Display part price in some forms | True | {{ globalsetting("PART_SHOW_RELATED") }}
| Show Price in BOM | Boolean | Include pricing information in BOM tables | True | {{ globalsetting("PART_CREATE_INITIAL") }}
| Show related parts | Boolean | Display related parts for a part | True | {{ globalsetting("PART_CREATE_SUPPLIER") }}
| Create initial stock | Boolean | Create initial stock on part creation | True | {{ globalsetting("PART_TEMPLATE") }}
{{ globalsetting("PART_ASSEMBLY") }}
#### Creation Settings {{ globalsetting("PART_COMPONENT") }}
{{ globalsetting("PART_TRACKABLE") }}
| Setting | Type | Description | Default | {{ globalsetting("PART_PURCHASEABLE") }}
| --- | --- | --- | --- | {{ globalsetting("PART_SALABLE") }}
| Template | Boolean | Parts are templates by default | False | {{ globalsetting("PART_VIRTUAL") }}
| Assembly | Boolean | Parts can be assembled from other components by default | False | {{ globalsetting("PART_COPY_BOM") }}
| Component | Boolean | Parts can be used as sub-components by default | True | {{ globalsetting("PART_COPY_PARAMETERS") }}
| Trackable | Boolean | Parts are trackable by default | False | {{ globalsetting("PART_COPY_TESTS") }}
| Purchaseable | Boolean | Parts are purchaseable by default | True | {{ globalsetting("PART_CATEGORY_PARAMETERS") }}
| Salable | Boolean | Parts are salable by default | False | {{ globalsetting("PART_CATEGORY_DEFAULT_ICON") }}
| Virtual | Boolean | Parts are virtual by default | False |
#### Copy Settings
| Setting | Type | Description | Default |
| --- | --- | --- | --- |
| Copy Part BOM Data | Boolean | Copy BOM data by default when duplicating a part | True |
| Copy Part Parameter Data | Boolean | Copy parameter data by default when duplicating a part | True |
| Copy Part Test Data | Boolean | Copy test data by default when duplicating a part | True |
| Copy Category Parameter Templates | Boolean | Copy category parameter templates when creating a part | True |
#### Internal Price Settings
| Setting | Type | Description | Default |
| --- | --- | --- | --- |
| Internal Prices | Boolean | Enable internal prices for parts | False |
| Internal Price as BOM-Price | Boolean | Use the internal price (if set) in BOM-price calculations | False |
#### Part Import Setting
This section of the part settings allows staff users to:
- import parts to InvenTree clicking the <span class="badge inventree add"><span class='fas fa-plus-circle'></span> Import Part</span> button
- enable the ["Import Parts" tab in the part category view](../part/part.md#part-import).
| Setting | Type | Description | Default |
| --- | --- | --- | --- |
| Show Import in Views | Boolean | Display the import wizard in some part views | True |
#### Part Parameter Templates #### Part Parameter Templates
@ -153,45 +172,48 @@ After a list of parameters is added to a part category and upon creation of a ne
Configuration of stock item options Configuration of stock item options
| Setting | Type | Description | Default | | Name | Description | Default | Units |
| --- | --- | --- | --- | | ---- | ----------- | ------- | ----- |
| Stock Expiry | Boolean | Enable stock expiry functionality | False | {{ globalsetting("SERIAL_NUMBER_GLOBALLY_UNIQUE") }}
| Stock Stale Time | Days | Number of days stock items are considered stale before expiring | 90 | {{ globalsetting("SERIAL_NUMBER_AUTOFILL") }}
| Sell Expired Stock | Boolean | Allow sale of expired stock | False | {{ globalsetting("STOCK_DELETE_DEPLETED_DEFAULT") }}
| Build Expired Stock | Boolean | Allow building with expired stock | False | {{ globalsetting("STOCK_BATCH_CODE_TEMPLATE") }}
| Stock Ownership Control | Boolean | Enable ownership control functionality | False | {{ globalsetting("STOCK_ENABLE_EXPIRY") }}
{{ globalsetting("STOCK_STALE_DAYS") }}
{{ globalsetting("STOCK_ALLOW_EXPIRED_SALE") }}
{{ globalsetting("STOCK_ALLOW_EXPIRED_BUILD") }}
{{ globalsetting("STOCK_OWNERSHIP_CONTROL") }}
{{ globalsetting("STOCK_LOCATION_DEFAULT_ICON") }}
{{ globalsetting("STOCK_SHOW_INSTALLED_ITEMS") }}
{{ globalsetting("STOCK_ENFORCE_BOM_INSTALLATION") }}
{{ globalsetting("STOCK_ALLOW_OUT_OF_STOCK_TRANSFER") }}
{{ globalsetting("TEST_STATION_DATA") }}
### Build Orders ### Build Orders
Options for build orders Refer to the [build order settings](../build/build.md#build-order-settings).
| Setting | Type | Description | Default |
| --- | --- | --- | --- |
| Reference Pattern | String | Pattern for defining Build Order reference values | {% raw %}BO-{ref:04d}{% endraw %} |
### Purchase Orders ### Purchase Orders
Options for purchase orders Refer to the [purchase order settings](../order/purchase_order.md#purchase-order-settings).
| Setting | Type | Description | Default | ### Sales Orders
| --- | --- | --- | --- |
| Reference Pattern | String | Pattern for defining Purchase Order reference values | {% raw %}PO-{ref:04d}{% endraw %} |
### Sales orders Refer to the [sales order settings](../order/sales_order.md#sales-order-settings).
Options for sales orders ### Return Orders
| Setting | Type | Description | Default | Refer to the [return order settings](../order/return_order.md#return-order-settings).
| --- | --- | --- | --- |
| Reference Pattern | String | Pattern for defining Sales Order reference values | {% raw %}SO-{ref:04d}{% endraw %} |
### Plugin Settings ### Plugin Settings
Change into what parts plugins can integrate into.
| Setting | Type | Description | Default | | Name | Description | Default | Units |
| --- | --- | --- | --- | | ---- | ----------- | ------- | ----- |
| Enable URL integration | Boolean | Enable plugins to add URL routes | False | {{ globalsetting("PLUGIN_ON_STARTUP") }}
| Enable navigation integration | Boolean | Enable plugins to integrate into navigation | False | {{ globalsetting("PLUGIN_UPDATE_CHECK") }}
| Enable setting integration | Boolean | Enable plugins to integrate into inventree settings | False | {{ globalsetting("ENABLE_PLUGINS_URL") }}
| Enable app integration | Boolean | Enable plugins to add apps | False | {{ globalsetting("ENABLE_PLUGINS_NAVIGATION") }}
{{ globalsetting("ENABLE_PLUGINS_APP") }}
{{ globalsetting("ENABLE_PLUGINS_SCHEDULE") }}
{{ globalsetting("ENABLE_PLUGINS_EVENTS") }}

View File

@ -32,24 +32,44 @@ This screen allows the user to customize display of items on the InvenTree home
### Search Settings ### Search Settings
Customize settings for search results Customize settings for search results:
{% with id="user-search", url="settings/user_search.png", description="User Search Settings" %} | Name | Description | Default | Units |
{% include 'img.html' %} | ---- | ----------- | ------- | ----- |
{% endwith %} {{ usersetting("SEARCH_WHOLE") }}
{{ usersetting("SEARCH_REGEX") }}
{{ usersetting("SEARCH_PREVIEW_RESULTS") }}
{{ usersetting("SEARCH_PREVIEW_SHOW_PARTS") }}
{{ usersetting("SEARCH_HIDE_INACTIVE_PARTS") }}
{{ usersetting("SEARCH_PREVIEW_SHOW_SUPPLIER_PARTS") }}
{{ usersetting("SEARCH_PREVIEW_SHOW_MANUFACTURER_PARTS") }}
{{ usersetting("SEARCH_PREVIEW_SHOW_CATEGORIES") }}
{{ usersetting("SEARCH_PREVIEW_SHOW_STOCK") }}
{{ usersetting("SEARCH_PREVIEW_HIDE_UNAVAILABLE_STOCK") }}
{{ usersetting("SEARCH_PREVIEW_SHOW_LOCATIONS") }}
{{ usersetting("SEARCH_PREVIEW_SHOW_COMPANIES") }}
{{ usersetting("SEARCH_PREVIEW_SHOW_BUILD_ORDERS") }}
{{ usersetting("SEARCH_PREVIEW_SHOW_PURCHASE_ORDERS") }}
{{ usersetting("SEARCH_PREVIEW_EXCLUDE_INACTIVE_PURCHASE_ORDERS") }}
{{ usersetting("SEARCH_PREVIEW_SHOW_SALES_ORDERS") }}
{{ usersetting("SEARCH_PREVIEW_EXCLUDE_INACTIVE_SALES_ORDERS") }}
{{ usersetting("SEARCH_PREVIEW_SHOW_RETURN_ORDERS") }}
{{ usersetting("SEARCH_PREVIEW_EXCLUDE_INACTIVE_RETURN_ORDERS") }}
### Notifications ### Notifications
Settings related to notification messages Settings related to notification messages:
{% with id="user-notification", url="settings/user_notifications.png", description="User Notification Settings" %} | Name | Description | Default | Units |
{% include 'img.html' %} | ---- | ----------- | ------- | ----- |
{% endwith %} {{ usersetting("NOTIFICATION_ERROR_REPORT") }}
### Reporting ### Reporting
Settings for label printing and report generation Settings for label printing and report generation:
{% with id="user-reporting", url="settings/user_reporting.png", description="User Reporting Settings" %} | Name | Description | Default | Units |
{% include 'img.html' %} | ---- | ----------- | ------- | ----- |
{% endwith %} {{ usersetting("REPORT_INLINE") }}
{{ usersetting("LABEL_INLINE") }}
{{ usersetting("LABEL_DEFAULT_PRINTER") }}

View File

@ -1,5 +1,6 @@
"""Main entry point for the documentation build process.""" """Main entry point for the documentation build process."""
import json
import os import os
import subprocess import subprocess
import textwrap import textwrap
@ -7,6 +8,20 @@ import textwrap
import requests import requests
import yaml import yaml
# Cached settings dict values
global GLOBAL_SETTINGS
global USER_SETTINGS
# Read in the InvenTree settings file
here = os.path.dirname(__file__)
settings_file = os.path.join(here, 'inventree_settings.json')
with open(settings_file, 'r') as sf:
settings = json.load(sf)
GLOBAL_SETTINGS = settings['global']
USER_SETTINGS = settings['user']
def get_repo_url(raw=False): def get_repo_url(raw=False):
"""Return the repository URL for the current project.""" """Return the repository URL for the current project."""
@ -219,3 +234,37 @@ def define_env(env):
) )
return includefile(fn, f'Template: {base}', format='html') return includefile(fn, f'Template: {base}', format='html')
@env.macro
def rendersetting(setting: dict):
"""Render a provided setting object into a table row."""
name = setting['name']
description = setting['description']
default = setting.get('default', None)
units = setting.get('units', None)
return f'| {name} | {description} | {default if default is not None else ""} | {units if units is not None else ""} |'
@env.macro
def globalsetting(key: str):
"""Extract information on a particular global setting.
Arguments:
- key: The name of the global setting to extract information for.
"""
global GLOBAL_SETTINGS
setting = GLOBAL_SETTINGS[key]
return rendersetting(setting)
@env.macro
def usersetting(key: str):
"""Extract information on a particular user setting.
Arguments:
- key: The name of the user setting to extract information for.
"""
global USER_SETTINGS
setting = USER_SETTINGS[key]
return rendersetting(setting)

View File

@ -17,5 +17,6 @@ build:
- echo "Generating API schema file" - echo "Generating API schema file"
- pip install -U invoke - pip install -U invoke
- invoke migrate - invoke migrate
- invoke export-settings-definitions --filename docs/inventree_settings.json --overwrite
- invoke schema --filename docs/schema.yml --ignore-warnings - invoke schema --filename docs/schema.yml --ignore-warnings
- python docs/extract_schema.py docs/schema.yml - python docs/extract_schema.py docs/schema.yml

View File

@ -0,0 +1,53 @@
"""Custom management command to export settings definitions.
This is used to generate a JSON file which contains all of the settings,
so that they can be introspected by the InvenTree documentation system.
This in turn allows settings to be documented in the InvenTree documentation,
without having to manually duplicate the information in multiple places.
"""
import json
from django.core.management.base import BaseCommand
class Command(BaseCommand):
"""Extract settings information, and export to a JSON file."""
def add_arguments(self, parser):
"""Add custom arguments for this command."""
parser.add_argument(
'filename', type=str, help='Output filename for settings definitions'
)
def handle(self, *args, **kwargs):
"""Export settings information to a JSON file."""
from common.models import InvenTreeSetting, InvenTreeUserSetting
settings = {'global': {}, 'user': {}}
# Global settings
for key, setting in InvenTreeSetting.SETTINGS.items():
settings['global'][key] = {
'name': str(setting['name']),
'description': str(setting['description']),
'default': str(InvenTreeSetting.get_setting_default(key)),
'units': str(setting.get('units', '')),
}
# User settings
for key, setting in InvenTreeUserSetting.SETTINGS.items():
settings['user'][key] = {
'name': str(setting['name']),
'description': str(setting['description']),
'default': str(InvenTreeUserSetting.get_setting_default(key)),
'units': str(setting.get('units', '')),
}
filename = kwargs.get('filename', 'inventree_settings.json')
with open(filename, 'w') as f:
json.dump(settings, f, indent=4)
print(f"Exported InvenTree settings definitions to '{filename}'")

View File

@ -395,9 +395,9 @@ class Build(
def sub_builds(self, cascade=True): def sub_builds(self, cascade=True):
"""Return all Build Order objects under this one.""" """Return all Build Order objects under this one."""
if cascade: if cascade:
return Build.objects.filter(parent=self.pk) return self.get_descendants(include_self=False)
descendants = self.get_descendants(include_self=True) else:
Build.objects.filter(parent__pk__in=[d.pk for d in descendants]) return self.get_children()
def sub_build_count(self, cascade=True): def sub_build_count(self, cascade=True):
"""Return the number of sub builds under this one. """Return the number of sub builds under this one.
@ -407,6 +407,11 @@ class Build(
""" """
return self.sub_builds(cascade=cascade).count() return self.sub_builds(cascade=cascade).count()
@property
def has_open_child_builds(self):
"""Return True if this build order has any open child builds."""
return self.sub_builds().filter(status__in=BuildStatusGroups.ACTIVE_CODES).exists()
@property @property
def is_overdue(self): def is_overdue(self):
"""Returns true if this build is "overdue". """Returns true if this build is "overdue".
@ -576,6 +581,9 @@ class Build(
- Untracked parts must be allocated - Untracked parts must be allocated
""" """
if get_global_setting('BUILDORDER_REQUIRE_CLOSED_CHILDS') and self.has_open_child_builds:
return False
if self.status != BuildStatus.PRODUCTION.value: if self.status != BuildStatus.PRODUCTION.value:
return False return False
@ -619,6 +627,10 @@ class Build(
trim_allocated_stock = kwargs.pop('trim_allocated_stock', False) trim_allocated_stock = kwargs.pop('trim_allocated_stock', False)
user = kwargs.pop('user', None) user = kwargs.pop('user', None)
# Prevent completion if there are open child builds
if get_global_setting('BUILDORDER_REQUIRE_CLOSED_CHILDS') and self.has_open_child_builds:
return
if self.incomplete_count > 0: if self.incomplete_count > 0:
return return
@ -974,7 +986,10 @@ class Build(
items_to_save = [] items_to_save = []
items_to_delete = [] items_to_delete = []
for build_line in self.untracked_line_items: lines = self.untracked_line_items
lines = lines.prefetch_related('allocations')
for build_line in lines:
reduce_by = build_line.allocated_quantity() - build_line.quantity reduce_by = build_line.allocated_quantity() - build_line.quantity

View File

@ -27,6 +27,7 @@ from stock.serializers import StockItemSerializerBrief, LocationBriefSerializer
import common.models import common.models
from common.serializers import ProjectCodeSerializer from common.serializers import ProjectCodeSerializer
from common.settings import get_global_setting
from importer.mixins import DataImportExportSerializerMixin from importer.mixins import DataImportExportSerializerMixin
import company.serializers import company.serializers
import part.filters import part.filters
@ -765,6 +766,9 @@ class BuildCompleteSerializer(serializers.Serializer):
"""Perform validation of this serializer prior to saving""" """Perform validation of this serializer prior to saving"""
build = self.context['build'] build = self.context['build']
if get_global_setting('BUILDORDER_REQUIRE_CLOSED_CHILDS') and build.has_open_child_builds:
raise ValidationError(_("Build order has open child build orders"))
if build.status != BuildStatus.PRODUCTION.value: if build.status != BuildStatus.PRODUCTION.value:
raise ValidationError(_("Build order must be in production state")) raise ValidationError(_("Build order must be in production state"))

View File

@ -1015,7 +1015,7 @@ class BuildOverallocationTest(BuildAPITest):
'accept_overallocated': 'trim', 'accept_overallocated': 'trim',
}, },
expected_code=201, expected_code=201,
max_query_count=555, # TODO: Come back and refactor this max_query_count=600, # TODO: Come back and refactor this
) )
self.build.refresh_from_db() self.build.refresh_from_db()

View File

@ -1838,6 +1838,14 @@ class InvenTreeSetting(BaseInvenTreeSetting):
'default': False, 'default': False,
'validator': bool, 'validator': bool,
}, },
'BUILDORDER_REQUIRE_CLOSED_CHILDS': {
'name': _('Require Closed Child Orders'),
'description': _(
'Prevent build order completion until all child orders are closed'
),
'default': False,
'validator': bool,
},
'PREVENT_BUILD_COMPLETION_HAVING_INCOMPLETED_TESTS': { 'PREVENT_BUILD_COMPLETION_HAVING_INCOMPLETED_TESTS': {
'name': _('Block Until Tests Pass'), 'name': _('Block Until Tests Pass'),
'description': _( 'description': _(

View File

@ -17,6 +17,7 @@
{% include "InvenTree/settings/setting.html" with key="BUILDORDER_REQUIRE_ACTIVE_PART" %} {% include "InvenTree/settings/setting.html" with key="BUILDORDER_REQUIRE_ACTIVE_PART" %}
{% include "InvenTree/settings/setting.html" with key="BUILDORDER_REQUIRE_LOCKED_PART" %} {% include "InvenTree/settings/setting.html" with key="BUILDORDER_REQUIRE_LOCKED_PART" %}
{% include "InvenTree/settings/setting.html" with key="BUILDORDER_REQUIRE_VALID_BOM" %} {% include "InvenTree/settings/setting.html" with key="BUILDORDER_REQUIRE_VALID_BOM" %}
{% include "InvenTree/settings/setting.html" with key="BUILDORDER_REQUIRE_CLOSED_CHILDS" %}
{% include "InvenTree/settings/setting.html" with key="PREVENT_BUILD_COMPLETION_HAVING_INCOMPLETED_TESTS" %} {% include "InvenTree/settings/setting.html" with key="PREVENT_BUILD_COMPLETION_HAVING_INCOMPLETED_TESTS" %}
</tbody> </tbody>
</table> </table>

View File

@ -244,6 +244,7 @@ export default function SystemSettings() {
'BUILDORDER_REQUIRE_ACTIVE_PART', 'BUILDORDER_REQUIRE_ACTIVE_PART',
'BUILDORDER_REQUIRE_LOCKED_PART', 'BUILDORDER_REQUIRE_LOCKED_PART',
'BUILDORDER_REQUIRE_VALID_BOM', 'BUILDORDER_REQUIRE_VALID_BOM',
'BUILDORDER_REQUIRE_CLOSED_CHILDS',
'PREVENT_BUILD_COMPLETION_HAVING_INCOMPLETED_TESTS' 'PREVENT_BUILD_COMPLETION_HAVING_INCOMPLETED_TESTS'
]} ]}
/> />

View File

@ -1074,6 +1074,16 @@ def schema(
print('Schema export completed:', filename) print('Schema export completed:', filename)
@task
def export_settings_definitions(c, filename='inventree_settings.json', overwrite=False):
"""Export settings definition to a JSON file."""
filename = Path(filename).resolve()
check_file_existance(filename, overwrite)
print(f"Exporting settings definition to '{filename}'...")
manage(c, f'export_settings_definitions {filename}', pty=True)
@task(default=True) @task(default=True)
def version(c): def version(c):
"""Show the current version of InvenTree.""" """Show the current version of InvenTree."""
@ -1406,6 +1416,11 @@ via your signed in browser, or consider using a point release download via invok
) )
def docs_server(c, address='localhost:8080', compile_schema=False): def docs_server(c, address='localhost:8080', compile_schema=False):
"""Start a local mkdocs server to view the documentation.""" """Start a local mkdocs server to view the documentation."""
# Extract settings definitions
export_settings_definitions(
c, filename='docs/inventree_settings.json', overwrite=True
)
if compile_schema: if compile_schema:
# Build the schema docs first # Build the schema docs first
schema(c, ignore_warnings=True, overwrite=True, filename='docs/schema.yml') schema(c, ignore_warnings=True, overwrite=True, filename='docs/schema.yml')