2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-06-15 19:45:46 +00:00

Merge remote-tracking branch 'inventree/master'

This commit is contained in:
Oliver Walters
2022-11-28 20:19:36 +11:00
41 changed files with 29458 additions and 28173 deletions

View File

@ -49,12 +49,14 @@ jobs:
# Build the development docker image (using docker-compose.yml) # Build the development docker image (using docker-compose.yml)
run: | run: |
docker-compose build docker-compose build
- name: Run Unit Tests - name: Update Docker Image
run: | run: |
docker-compose run inventree-dev-server invoke update docker-compose run inventree-dev-server invoke update
docker-compose run inventree-dev-server invoke setup-dev docker-compose run inventree-dev-server invoke setup-dev
docker-compose up -d docker-compose up -d
docker-compose run inventree-dev-server invoke wait docker-compose run inventree-dev-server invoke wait
- name: Run Unit Tests
run: |
docker-compose run inventree-dev-server invoke test docker-compose run inventree-dev-server invoke test
docker-compose down docker-compose down
- name: Check Data Directory - name: Check Data Directory

View File

@ -7,8 +7,6 @@ import shutil
import string import string
from pathlib import Path from pathlib import Path
import yaml
logger = logging.getLogger('inventree') logger = logging.getLogger('inventree')
@ -61,6 +59,8 @@ def get_config_file(create=True) -> Path:
def load_config_data() -> map: def load_config_data() -> map:
"""Load configuration data from the config file.""" """Load configuration data from the config file."""
import yaml
cfg_file = get_config_file() cfg_file = get_config_file()
with open(cfg_file, 'r') as cfg: with open(cfg_file, 'r') as cfg:

View File

@ -584,7 +584,7 @@ if cache_host: # pragma: no cover
# database user sessions # database user sessions
SESSION_ENGINE = 'user_sessions.backends.db' SESSION_ENGINE = 'user_sessions.backends.db'
LOGOUT_REDIRECT_URL = 'index' LOGOUT_REDIRECT_URL = get_setting('INVENTREE_LOGOUT_REDIRECT_URL', 'logout_redirect_url', 'index')
SILENCED_SYSTEM_CHECKS = [ SILENCED_SYSTEM_CHECKS = [
'admin.E410', 'admin.E410',
] ]

View File

@ -777,6 +777,7 @@ class TestSettings(helpers.InvenTreeTestCase):
"""Test if install of plugins on startup works.""" """Test if install of plugins on startup works."""
from plugin import registry from plugin import registry
if not settings.DOCKER:
# Check an install run # Check an install run
response = registry.install_plugin_file() response = registry.install_plugin_file()
self.assertEqual(response, 'first_run') self.assertEqual(response, 'first_run')

View File

@ -258,7 +258,12 @@ class BaseInvenTreeSetting(models.Model):
""" """
setting = cls.get_setting_definition(key, **kwargs) setting = cls.get_setting_definition(key, **kwargs)
return setting.get('default', '') default = setting.get('default', '')
if callable(default):
return default()
else:
return default
@classmethod @classmethod
def get_setting_choices(cls, key, **kwargs): def get_setting_choices(cls, key, **kwargs):
@ -1100,6 +1105,27 @@ class InvenTreeSetting(BaseInvenTreeSetting):
'validator': bool, 'validator': bool,
}, },
'PRICING_PURCHASE_HISTORY_OVERRIDES_SUPPLIER': {
'name': _('Purchase History Override'),
'description': _('Historical purchase order pricing overrides supplier price breaks'),
'default': False,
'validator': bool,
},
'PRICING_USE_VARIANT_PRICING': {
'name': _('Use Variant Pricing'),
'description': _('Include variant pricing in overall pricing calculations'),
'default': True,
'validator': bool,
},
'PRICING_ACTIVE_VARIANTS': {
'name': _('Active Variants Only'),
'description': _('Only use active variant parts for calculating variant pricing'),
'default': False,
'validator': bool,
},
'PRICING_UPDATE_DAYS': { 'PRICING_UPDATE_DAYS': {
'name': _('Pricing Rebuild Time'), 'name': _('Pricing Rebuild Time'),
'description': _('Number of days before part pricing is automatically updated'), 'description': _('Number of days before part pricing is automatically updated'),
@ -1345,7 +1371,7 @@ class InvenTreeSetting(BaseInvenTreeSetting):
'PLUGIN_ON_STARTUP': { 'PLUGIN_ON_STARTUP': {
'name': _('Check plugins on startup'), 'name': _('Check plugins on startup'),
'description': _('Check that all plugins are installed on startup - enable in container environments'), 'description': _('Check that all plugins are installed on startup - enable in container environments'),
'default': False, 'default': settings.DOCKER,
'validator': bool, 'validator': bool,
'requires_restart': True, 'requires_restart': True,
}, },

View File

@ -71,9 +71,9 @@ class SupplierPartAdmin(ImportExportModelAdmin):
list_display = ('part', 'supplier', 'SKU') list_display = ('part', 'supplier', 'SKU')
search_fields = [ search_fields = [
'company__name', 'supplier__name',
'part__name', 'part__name',
'MPN', 'manufacturer_part__MPN',
'SKU', 'SKU',
] ]

View File

@ -171,6 +171,12 @@ login_default_protocol: http
remote_login_enabled: False remote_login_enabled: False
remote_login_header: HTTP_REMOTE_USER remote_login_header: HTTP_REMOTE_USER
# Logout redirect configuration
# This setting may be required if using remote / proxy login to redirect requests
# during the logout process (default is 'index'). Please read the docs for more details
# https://docs.djangoproject.com/en/stable/ref/settings/#logout-redirect-url
#logout_redirect_url: 'index'
# Permit custom authentication backends # Permit custom authentication backends
#authentication_backends: #authentication_backends:
# - 'django.contrib.auth.backends.ModelBackend' # - 'django.contrib.auth.backends.ModelBackend'

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -2571,10 +2571,17 @@ class PartPricing(models.Model):
variant_min = None variant_min = None
variant_max = None variant_max = None
active_only = InvenTreeSetting.get_setting('PRICING_ACTIVE_VARIANTS', False)
if self.part.is_template: if self.part.is_template:
variants = self.part.get_descendants(include_self=False) variants = self.part.get_descendants(include_self=False)
for v in variants: for v in variants:
if active_only and not v.active:
# Ignore inactive variant parts
continue
v_min = self.convert(v.pricing.overall_min) v_min = self.convert(v.pricing.overall_min)
v_max = self.convert(v.pricing.overall_max) v_max = self.convert(v.pricing.overall_max)
@ -2605,20 +2612,28 @@ class PartPricing(models.Model):
self.bom_cost_min, self.bom_cost_min,
self.purchase_cost_min, self.purchase_cost_min,
self.internal_cost_min, self.internal_cost_min,
self.variant_cost_min
] ]
max_costs = [ max_costs = [
self.bom_cost_max, self.bom_cost_max,
self.purchase_cost_max, self.purchase_cost_max,
self.internal_cost_max, self.internal_cost_max,
self.variant_cost_max
] ]
if InvenTreeSetting.get_setting('PRICING_USE_SUPPLIER_PRICING', True): purchase_history_override = InvenTreeSetting.get_setting('PRICING_PURCHASE_HISTORY_OVERRIDES_SUPPLIER', False, cache=False)
if InvenTreeSetting.get_setting('PRICING_USE_SUPPLIER_PRICING', True, cache=False):
# Add supplier pricing data, *unless* historical pricing information should override
if self.purchase_cost_min is None or not purchase_history_override:
min_costs.append(self.supplier_price_min) min_costs.append(self.supplier_price_min)
if self.purchase_cost_max is None or not purchase_history_override:
max_costs.append(self.supplier_price_max) max_costs.append(self.supplier_price_max)
if InvenTreeSetting.get_setting('PRICING_USE_VARIANT_PRICING', True, cache=False):
min_costs.append(self.variant_cost_min)
max_costs.append(self.variant_cost_max)
# Calculate overall minimum cost # Calculate overall minimum cost
for cost in min_costs: for cost in min_costs:
if cost is None: if cost is None:

View File

@ -16,6 +16,10 @@
{% include "InvenTree/settings/setting.html" with key="PRICING_DECIMAL_PLACES" %} {% include "InvenTree/settings/setting.html" with key="PRICING_DECIMAL_PLACES" %}
{% include "InvenTree/settings/setting.html" with key="PRICING_UPDATE_DAYS" icon='fa-calendar-alt' %} {% include "InvenTree/settings/setting.html" with key="PRICING_UPDATE_DAYS" icon='fa-calendar-alt' %}
{% include "InvenTree/settings/setting.html" with key="PRICING_USE_SUPPLIER_PRICING" icon='fa-check-circle' %} {% include "InvenTree/settings/setting.html" with key="PRICING_USE_SUPPLIER_PRICING" icon='fa-check-circle' %}
{% include "InvenTree/settings/setting.html" with key="PRICING_PURCHASE_HISTORY_OVERRIDES_SUPPLIER" icon='fa-shopping-cart' %}
{% include "InvenTree/settings/setting.html" with key="PRICING_USE_VARIANT_PRICING" icon='fa-check-circle' %}
{% include "InvenTree/settings/setting.html" with key="PRICING_ACTIVE_VARIANTS" %}
</tbody> </tbody>
</table> </table>
</div> </div>

View File

@ -957,10 +957,10 @@ function loadBomTable(table, options={}) {
} }
}); });
var total = `${top_total}`; var total = `${formatDecimal(top_total)}`;
if (top_total != all_total) { if (top_total != all_total) {
total += ` / ${all_total}`; total += ` / ${formatDecimal(all_total)}`;
} }
return total; return total;

View File

@ -695,6 +695,11 @@ function loadVariantPricingChart(options={}) {
options.params.ancestor = part; options.params.ancestor = part;
if (global_settings.PRICING_ACTIVE_VARIANTS) {
// Filter variants by "active" status
options.params.active = true;
}
table.inventreeTable({ table.inventreeTable({
url: '{% url "api-part-list" %}', url: '{% url "api-part-list" %}',
name: 'variantpricingtable', name: 'variantpricingtable',

View File

@ -130,6 +130,10 @@ function getAvailableTableFilters(tableKey) {
title: '{% trans "Include sublocations" %}', title: '{% trans "Include sublocations" %}',
description: '{% trans "Include locations" %}', description: '{% trans "Include locations" %}',
}, },
structural: {
type: 'bool',
title: '{% trans "Structural" %}',
},
}; };
} }
@ -141,6 +145,10 @@ function getAvailableTableFilters(tableKey) {
title: '{% trans "Include subcategories" %}', title: '{% trans "Include subcategories" %}',
description: '{% trans "Include subcategories" %}', description: '{% trans "Include subcategories" %}',
}, },
structural: {
type: 'bool',
title: '{% trans "Structural" %}',
},
starred: { starred: {
type: 'bool', type: 'bool',
title: '{% trans "Subscribed" %}', title: '{% trans "Subscribed" %}',

View File

@ -132,6 +132,8 @@ There are several options to deploy InvenTree.
<div align="center"><h4> <div align="center"><h4>
<a href="https://inventree.readthedocs.io/en/latest/start/docker/">Docker</a> <a href="https://inventree.readthedocs.io/en/latest/start/docker/">Docker</a>
<span> · </span> <span> · </span>
<a href="https://marketplace.digitalocean.com/apps/inventree?refcode=d6172576d014"><img src="https://www.deploytodo.com/do-btn-blue-ghost.svg" alt="Deploy to DO" width="auto" height="40" /></a>
<span> · </span>
<a href="https://inventree.readthedocs.io/en/latest/start/install/">Bare Metal</a> <a href="https://inventree.readthedocs.io/en/latest/start/install/">Bare Metal</a>
</h4></div> </h4></div>

View File

@ -55,10 +55,21 @@ root_command() {
# Check if os and version is supported # Check if os and version is supported
get_distribution get_distribution
echo "### Detected distribution: $OS $VER" echo "### Detected distribution: $OS $VER"
SUPPORTED=true SUPPORTED=true # is thos OS supported?
NEEDS_LIBSSL1_1=false # does this OS need libssl1.1?
DIST_OS=${OS,,}
DIST_VER=$VER
case "$OS" in case "$OS" in
Ubuntu) Ubuntu)
if [[ $VER != "20.04" ]]; then if [[ $VER == "22.04" ]]; then
SUPPORTED=true
NEEDS_LIBSSL1_1=true
DIST_VER="20.04"
elif [[ $VER == "20.04" ]]; then
SUPPORTED=true
else
SUPPORTED=false SUPPORTED=false
fi fi
;; ;;
@ -93,10 +104,19 @@ root_command() {
fi fi
done done
if [[ $NEEDS_LIBSSL1_1 == "true" ]]; then
echo "### Pathching for libssl1.1"
echo "deb http://security.ubuntu.com/ubuntu focal-security main" | sudo tee /etc/apt/sources.list.d/focal-security.list
do_call "sudo apt-get update"
do_call "sudo apt-get install libssl1.1"
sudo rm /etc/apt/sources.list.d/focal-security.list
fi
echo "### Getting and adding key" echo "### Getting and adding key"
wget -qO- https://dl.packager.io/srv/$publisher/InvenTree/key | sudo apt-key add - wget -qO- https://dl.packager.io/srv/$publisher/InvenTree/key | sudo apt-key add -
echo "### Adding package source" echo "### Adding package source"
do_call "sudo wget -O /etc/apt/sources.list.d/inventree.list https://dl.packager.io/srv/$publisher/InvenTree/$source_url/installer/${OS,,}/${VER}.repo" do_call "sudo wget -O /etc/apt/sources.list.d/inventree.list https://dl.packager.io/srv/$publisher/InvenTree/$source_url/installer/$DIST_OS/$DIST_VER.repo"
echo "### Updateing package lists" echo "### Updateing package lists"
do_call "sudo apt-get update" do_call "sudo apt-get update"

View File

@ -45,10 +45,21 @@ echo "### Installer for InvenTree - source: $publisher/$source_url"
# Check if os and version is supported # Check if os and version is supported
get_distribution get_distribution
echo "### Detected distribution: $OS $VER" echo "### Detected distribution: $OS $VER"
SUPPORTED=true SUPPORTED=true # is thos OS supported?
NEEDS_LIBSSL1_1=false # does this OS need libssl1.1?
DIST_OS=${OS,,}
DIST_VER=$VER
case "$OS" in case "$OS" in
Ubuntu) Ubuntu)
if [[ $VER != "20.04" ]]; then if [[ $VER == "22.04" ]]; then
SUPPORTED=true
NEEDS_LIBSSL1_1=true
DIST_VER="20.04"
elif [[ $VER == "20.04" ]]; then
SUPPORTED=true
else
SUPPORTED=false SUPPORTED=false
fi fi
;; ;;
@ -83,10 +94,19 @@ for pkg in $REQS; do
fi fi
done done
if [[ $NEEDS_LIBSSL1_1 == "true" ]]; then
echo "### Pathching for libssl1.1"
echo "deb http://security.ubuntu.com/ubuntu focal-security main" | sudo tee /etc/apt/sources.list.d/focal-security.list
do_call "sudo apt-get update"
do_call "sudo apt-get install libssl1.1"
sudo rm /etc/apt/sources.list.d/focal-security.list
fi
echo "### Getting and adding key" echo "### Getting and adding key"
wget -qO- https://dl.packager.io/srv/$publisher/InvenTree/key | sudo apt-key add - wget -qO- https://dl.packager.io/srv/$publisher/InvenTree/key | sudo apt-key add -
echo "### Adding package source" echo "### Adding package source"
do_call "sudo wget -O /etc/apt/sources.list.d/inventree.list https://dl.packager.io/srv/$publisher/InvenTree/$source_url/installer/${OS,,}/${VER}.repo" do_call "sudo wget -O /etc/apt/sources.list.d/inventree.list https://dl.packager.io/srv/$publisher/InvenTree/$source_url/installer/$DIST_OS/$DIST_VER.repo"
echo "### Updateing package lists" echo "### Updateing package lists"
do_call "sudo apt-get update" do_call "sudo apt-get update"