Merge remote-tracking branch 'inventree/master' into stock-item-forms

This commit is contained in:
Oliver 2021-11-04 17:24:30 +11:00
commit f0e44f0efd
10 changed files with 323 additions and 170 deletions

View File

@ -9,16 +9,16 @@ import decimal
import os import os
from datetime import datetime from datetime import datetime
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.validators import MinValueValidator
from django.urls import reverse
from django.db import models, transaction from django.db import models, transaction
from django.db.models import Sum, Q from django.db.models import Sum, Q
from django.db.models.functions import Coalesce from django.db.models.functions import Coalesce
from django.core.validators import MinValueValidator from django.db.models.signals import post_save
from django.dispatch.dispatcher import receiver
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
from markdownx.models import MarkdownxField from markdownx.models import MarkdownxField
@ -27,16 +27,17 @@ from mptt.exceptions import InvalidMove
from InvenTree.status_codes import BuildStatus, StockStatus, StockHistoryCode from InvenTree.status_codes import BuildStatus, StockStatus, StockHistoryCode
from InvenTree.helpers import increment, getSetting, normalize, MakeBarcode from InvenTree.helpers import increment, getSetting, normalize, MakeBarcode
from InvenTree.validators import validate_build_order_reference
from InvenTree.models import InvenTreeAttachment, ReferenceIndexingMixin from InvenTree.models import InvenTreeAttachment, ReferenceIndexingMixin
from InvenTree.validators import validate_build_order_reference
import common.models import common.models
import InvenTree.fields import InvenTree.fields
import InvenTree.helpers import InvenTree.helpers
import InvenTree.tasks
from stock import models as StockModels
from part import models as PartModels from part import models as PartModels
from stock import models as StockModels
from users import models as UserModels from users import models as UserModels
@ -1014,6 +1015,19 @@ class Build(MPTTModel, ReferenceIndexingMixin):
return self.status == BuildStatus.COMPLETE return self.status == BuildStatus.COMPLETE
@receiver(post_save, sender=Build, dispatch_uid='build_post_save_log')
def after_save_build(sender, instance: Build, created: bool, **kwargs):
"""
Callback function to be executed after a Build instance is saved
"""
if created:
# A new Build has just been created
# Run checks on required parts
InvenTree.tasks.offload_task('build.tasks.check_build_stock', instance)
class BuildOrderAttachment(InvenTreeAttachment): class BuildOrderAttachment(InvenTreeAttachment):
""" """
Model for storing file attachments against a BuildOrder object Model for storing file attachments against a BuildOrder object

96
InvenTree/build/tasks.py Normal file
View File

@ -0,0 +1,96 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from decimal import Decimal
import logging
from django.utils.translation import ugettext_lazy as _
from django.template.loader import render_to_string
from allauth.account.models import EmailAddress
import build.models
import InvenTree.helpers
import InvenTree.tasks
import part.models as part_models
logger = logging.getLogger('inventree')
def check_build_stock(build: build.models.Build):
"""
Check the required stock for a newly created build order,
and send an email out to any subscribed users if stock is low.
"""
# Iterate through each of the parts required for this build
lines = []
if not build:
logger.error("Invalid build passed to 'build.tasks.check_build_stock'")
return
try:
part = build.part
except part_models.Part.DoesNotExist:
# Note: This error may be thrown during unit testing...
logger.error("Invalid build.part passed to 'build.tasks.check_build_stock'")
return
for bom_item in part.get_bom_items():
sub_part = bom_item.sub_part
# The 'in stock' quantity depends on whether the bom_item allows variants
in_stock = sub_part.get_stock_count(include_variants=bom_item.allow_variants)
allocated = sub_part.allocation_count()
available = max(0, in_stock - allocated)
required = Decimal(bom_item.quantity) * Decimal(build.quantity)
if available < required:
# There is not sufficient stock for this part
lines.append({
'link': InvenTree.helpers.construct_absolute_url(sub_part.get_absolute_url()),
'part': sub_part,
'in_stock': in_stock,
'allocated': allocated,
'available': available,
'required': required,
})
if len(lines) == 0:
# Nothing to do
return
# Are there any users subscribed to these parts?
subscribers = build.part.get_subscribers()
emails = EmailAddress.objects.filter(
user__in=subscribers,
)
if len(emails) > 0:
logger.info(f"Notifying users of stock required for build {build.pk}")
context = {
'link': InvenTree.helpers.construct_absolute_url(build.get_absolute_url()),
'build': build,
'part': build.part,
'lines': lines,
}
# Render the HTML message
html_message = render_to_string('email/build_order_required_stock.html', context)
subject = "[InvenTree] " + _("Stock required for build order")
recipients = emails.values_list('email', flat=True)
InvenTree.tasks.send_email(subject, '', recipients, html_message=html_message)

View File

@ -160,171 +160,108 @@ def ExportBom(part, fmt='csv', cascade=False, max_levels=None, parameter_data=Fa
# Add stock columns to dataset # Add stock columns to dataset
add_columns_to_dataset(stock_cols, len(bom_items)) add_columns_to_dataset(stock_cols, len(bom_items))
if manufacturer_data and supplier_data: if manufacturer_data or supplier_data:
""" """
If requested, add extra columns for each SupplierPart and ManufacturerPart associated with each line item If requested, add extra columns for each SupplierPart and ManufacturerPart associated with each line item
""" """
# Expand dataset with manufacturer parts # Keep track of the supplier parts we have already exported
manufacturer_headers = [ supplier_parts_used = set()
_('Manufacturer'),
_('MPN'),
]
supplier_headers = [
_('Supplier'),
_('SKU'),
]
manufacturer_cols = {} manufacturer_cols = {}
for b_idx, bom_item in enumerate(bom_items): for bom_idx, bom_item in enumerate(bom_items):
# Get part instance # Get part instance
b_part = bom_item.sub_part b_part = bom_item.sub_part
# Filter manufacturer parts # Include manufacturer data for each BOM item
manufacturer_parts = ManufacturerPart.objects.filter(part__pk=b_part.pk) if manufacturer_data:
manufacturer_parts = manufacturer_parts.prefetch_related('supplier_parts')
# Process manufacturer part # Filter manufacturer parts
for manufacturer_idx, manufacturer_part in enumerate(manufacturer_parts): manufacturer_parts = ManufacturerPart.objects.filter(part__pk=b_part.pk).prefetch_related('supplier_parts')
if manufacturer_part and manufacturer_part.manufacturer: for mp_idx, mp_part in enumerate(manufacturer_parts):
manufacturer_name = manufacturer_part.manufacturer.name
else:
manufacturer_name = ''
if manufacturer_part: # Extract the "name" field of the Manufacturer (Company)
manufacturer_mpn = manufacturer_part.MPN if mp_part and mp_part.manufacturer:
else: manufacturer_name = mp_part.manufacturer.name
manufacturer_mpn = '' else:
manufacturer_name = ''
# Generate column names for this manufacturer # Extract the "MPN" field from the Manufacturer Part
k_man = manufacturer_headers[0] + "_" + str(manufacturer_idx) if mp_part:
k_mpn = manufacturer_headers[1] + "_" + str(manufacturer_idx) manufacturer_mpn = mp_part.MPN
else:
manufacturer_mpn = ''
try: # Generate a column name for this manufacturer
manufacturer_cols[k_man].update({b_idx: manufacturer_name}) k_man = f'{_("Manufacturer")}_{mp_idx}'
manufacturer_cols[k_mpn].update({b_idx: manufacturer_mpn}) k_mpn = f'{_("MPN")}_{mp_idx}'
except KeyError:
manufacturer_cols[k_man] = {b_idx: manufacturer_name}
manufacturer_cols[k_mpn] = {b_idx: manufacturer_mpn}
# Process supplier parts try:
for supplier_idx, supplier_part in enumerate(manufacturer_part.supplier_parts.all()): manufacturer_cols[k_man].update({bom_idx: manufacturer_name})
manufacturer_cols[k_mpn].update({bom_idx: manufacturer_mpn})
except KeyError:
manufacturer_cols[k_man] = {bom_idx: manufacturer_name}
manufacturer_cols[k_mpn] = {bom_idx: manufacturer_mpn}
if supplier_part.supplier and supplier_part.supplier: # We wish to include supplier data for this manufacturer part
supplier_name = supplier_part.supplier.name if supplier_data:
for sp_idx, sp_part in enumerate(mp_part.supplier_parts.all()):
supplier_parts_used.add(sp_part)
if sp_part.supplier and sp_part.supplier:
supplier_name = sp_part.supplier.name
else:
supplier_name = ''
if sp_part:
supplier_sku = sp_part.SKU
else:
supplier_sku = ''
# Generate column names for this supplier
k_sup = str(_("Supplier")) + "_" + str(mp_idx) + "_" + str(sp_idx)
k_sku = str(_("SKU")) + "_" + str(mp_idx) + "_" + str(sp_idx)
try:
manufacturer_cols[k_sup].update({bom_idx: supplier_name})
manufacturer_cols[k_sku].update({bom_idx: supplier_sku})
except KeyError:
manufacturer_cols[k_sup] = {bom_idx: supplier_name}
manufacturer_cols[k_sku] = {bom_idx: supplier_sku}
if supplier_data:
# Add in any extra supplier parts, which are not associated with a manufacturer part
for sp_idx, sp_part in enumerate(SupplierPart.objects.filter(part__pk=b_part.pk)):
if sp_part in supplier_parts_used:
continue
supplier_parts_used.add(sp_part)
if sp_part.supplier:
supplier_name = sp_part.supplier.name
else: else:
supplier_name = '' supplier_name = ''
if supplier_part: supplier_sku = sp_part.SKU
supplier_sku = supplier_part.SKU
else:
supplier_sku = ''
# Generate column names for this supplier # Generate column names for this supplier
k_sup = str(supplier_headers[0]) + "_" + str(manufacturer_idx) + "_" + str(supplier_idx) k_sup = str(_("Supplier")) + "_" + str(sp_idx)
k_sku = str(supplier_headers[1]) + "_" + str(manufacturer_idx) + "_" + str(supplier_idx) k_sku = str(_("SKU")) + "_" + str(sp_idx)
try: try:
manufacturer_cols[k_sup].update({b_idx: supplier_name}) manufacturer_cols[k_sup].update({bom_idx: supplier_name})
manufacturer_cols[k_sku].update({b_idx: supplier_sku}) manufacturer_cols[k_sku].update({bom_idx: supplier_sku})
except KeyError: except KeyError:
manufacturer_cols[k_sup] = {b_idx: supplier_name} manufacturer_cols[k_sup] = {bom_idx: supplier_name}
manufacturer_cols[k_sku] = {b_idx: supplier_sku} manufacturer_cols[k_sku] = {bom_idx: supplier_sku}
# Add manufacturer columns to dataset # Add supplier columns to dataset
add_columns_to_dataset(manufacturer_cols, len(bom_items))
elif manufacturer_data:
"""
If requested, add extra columns for each ManufacturerPart associated with each line item
"""
# Expand dataset with manufacturer parts
manufacturer_headers = [
_('Manufacturer'),
_('MPN'),
]
manufacturer_cols = {}
for b_idx, bom_item in enumerate(bom_items):
# Get part instance
b_part = bom_item.sub_part
# Filter supplier parts
manufacturer_parts = ManufacturerPart.objects.filter(part__pk=b_part.pk)
for idx, manufacturer_part in enumerate(manufacturer_parts):
if manufacturer_part:
manufacturer_name = manufacturer_part.manufacturer.name
else:
manufacturer_name = ''
manufacturer_mpn = manufacturer_part.MPN
# Add manufacturer data to the manufacturer columns
# Generate column names for this manufacturer
k_man = manufacturer_headers[0] + "_" + str(idx)
k_mpn = manufacturer_headers[1] + "_" + str(idx)
try:
manufacturer_cols[k_man].update({b_idx: manufacturer_name})
manufacturer_cols[k_mpn].update({b_idx: manufacturer_mpn})
except KeyError:
manufacturer_cols[k_man] = {b_idx: manufacturer_name}
manufacturer_cols[k_mpn] = {b_idx: manufacturer_mpn}
# Add manufacturer columns to dataset
add_columns_to_dataset(manufacturer_cols, len(bom_items))
elif supplier_data:
"""
If requested, add extra columns for each SupplierPart associated with each line item
"""
# Expand dataset with manufacturer parts
manufacturer_headers = [
_('Supplier'),
_('SKU'),
]
manufacturer_cols = {}
for b_idx, bom_item in enumerate(bom_items):
# Get part instance
b_part = bom_item.sub_part
# Filter supplier parts
supplier_parts = SupplierPart.objects.filter(part__pk=b_part.pk)
for idx, supplier_part in enumerate(supplier_parts):
if supplier_part.supplier:
supplier_name = supplier_part.supplier.name
else:
supplier_name = ''
supplier_sku = supplier_part.SKU
# Add manufacturer data to the manufacturer columns
# Generate column names for this supplier
k_sup = manufacturer_headers[0] + "_" + str(idx)
k_sku = manufacturer_headers[1] + "_" + str(idx)
try:
manufacturer_cols[k_sup].update({b_idx: supplier_name})
manufacturer_cols[k_sku].update({b_idx: supplier_sku})
except KeyError:
manufacturer_cols[k_sup] = {b_idx: supplier_name}
manufacturer_cols[k_sku] = {b_idx: supplier_sku}
# Add manufacturer columns to dataset
add_columns_to_dataset(manufacturer_cols, len(bom_items)) add_columns_to_dataset(manufacturer_cols, len(bom_items))
data = dataset.export(fmt) data = dataset.export(fmt)

View File

@ -1324,6 +1324,17 @@ class Part(MPTTModel):
return query return query
def get_stock_count(self, include_variants=True):
"""
Return the total "in stock" count for this part
"""
entries = self.stock_entries(in_stock=True, include_variants=include_variants)
query = entries.aggregate(t=Coalesce(Sum('quantity'), Decimal(0)))
return query['t']
@property @property
def total_stock(self): def total_stock(self):
""" Return the total stock quantity for this part. """ Return the total stock quantity for this part.
@ -1332,11 +1343,7 @@ class Part(MPTTModel):
- If this part is a "template" (variants exist) then these are counted too - If this part is a "template" (variants exist) then these are counted too
""" """
entries = self.stock_entries(in_stock=True) return self.get_stock_count()
query = entries.aggregate(t=Coalesce(Sum('quantity'), Decimal(0)))
return query['t']
def get_bom_item_filter(self, include_inherited=True): def get_bom_item_filter(self, include_inherited=True):
""" """
@ -2091,17 +2098,20 @@ class Part(MPTTModel):
Returns True if the total stock for this part is less than the minimum stock level Returns True if the total stock for this part is less than the minimum stock level
""" """
return self.total_stock <= self.minimum_stock return self.get_stock_count() < self.minimum_stock
@receiver(post_save, sender=Part, dispatch_uid='part_post_save_log') @receiver(post_save, sender=Part, dispatch_uid='part_post_save_log')
def after_save_part(sender, instance: Part, **kwargs): def after_save_part(sender, instance: Part, created, **kwargs):
""" """
Function to be executed after a Part is saved Function to be executed after a Part is saved
""" """
# Run this check in the background if not created:
InvenTree.tasks.offload_task('part.tasks.notify_low_stock_if_required', instance) # Check part stock only if we are *updating* the part (not creating it)
# Run this check in the background
InvenTree.tasks.offload_task('part.tasks.notify_low_stock_if_required', instance)
def attach_file(instance, filename): def attach_file(instance, filename):

View File

@ -50,7 +50,7 @@ def notify_low_stock(part: part.models.Part):
'link': InvenTree.helpers.construct_absolute_url(part.get_absolute_url()), 'link': InvenTree.helpers.construct_absolute_url(part.get_absolute_url()),
} }
subject = _(f'[InvenTree] {part.name} is low on stock') subject = "[InvenTree] " + _("Low stock notification")
html_message = render_to_string('email/low_stock_notification.html', context) html_message = render_to_string('email/low_stock_notification.html', context)
recipients = emails.values_list('email', flat=True) recipients = emails.values_list('email', flat=True)

View File

@ -42,11 +42,12 @@ from common.files import FileManager
from common.views import FileManagementFormView, FileManagementAjaxView from common.views import FileManagementFormView, FileManagementAjaxView
from common.forms import UploadFileForm, MatchFieldForm from common.forms import UploadFileForm, MatchFieldForm
from stock.models import StockLocation from stock.models import StockItem, StockLocation
import common.settings as inventree_settings import common.settings as inventree_settings
from . import forms as part_forms from . import forms as part_forms
from . import settings as part_settings
from .bom import MakeBomTemplate, ExportBom, IsValidBOMFormat from .bom import MakeBomTemplate, ExportBom, IsValidBOMFormat
from order.models import PurchaseOrderLineItem from order.models import PurchaseOrderLineItem
@ -245,6 +246,7 @@ class PartImport(FileManagementFormView):
'Category', 'Category',
'default_location', 'default_location',
'default_supplier', 'default_supplier',
'variant_of',
] ]
OPTIONAL_HEADERS = [ OPTIONAL_HEADERS = [
@ -256,6 +258,17 @@ class PartImport(FileManagementFormView):
'minimum_stock', 'minimum_stock',
'Units', 'Units',
'Notes', 'Notes',
'Active',
'base_cost',
'Multiple',
'Assembly',
'Component',
'is_template',
'Purchaseable',
'Salable',
'Trackable',
'Virtual',
'Stock',
] ]
name = 'part' name = 'part'
@ -284,6 +297,18 @@ class PartImport(FileManagementFormView):
'category': 'category', 'category': 'category',
'default_location': 'default_location', 'default_location': 'default_location',
'default_supplier': 'default_supplier', 'default_supplier': 'default_supplier',
'variant_of': 'variant_of',
'active': 'active',
'base_cost': 'base_cost',
'multiple': 'multiple',
'assembly': 'assembly',
'component': 'component',
'is_template': 'is_template',
'purchaseable': 'purchaseable',
'salable': 'salable',
'trackable': 'trackable',
'virtual': 'virtual',
'stock': 'stock',
} }
file_manager_class = PartFileManager file_manager_class = PartFileManager
@ -299,6 +324,8 @@ class PartImport(FileManagementFormView):
self.matches['default_location'] = ['name__contains'] self.matches['default_location'] = ['name__contains']
self.allowed_items['default_supplier'] = SupplierPart.objects.all() self.allowed_items['default_supplier'] = SupplierPart.objects.all()
self.matches['default_supplier'] = ['SKU__contains'] self.matches['default_supplier'] = ['SKU__contains']
self.allowed_items['variant_of'] = Part.objects.all()
self.matches['variant_of'] = ['name__contains']
# setup # setup
self.file_manager.setup() self.file_manager.setup()
@ -364,9 +391,29 @@ class PartImport(FileManagementFormView):
category=optional_matches['Category'], category=optional_matches['Category'],
default_location=optional_matches['default_location'], default_location=optional_matches['default_location'],
default_supplier=optional_matches['default_supplier'], default_supplier=optional_matches['default_supplier'],
variant_of=optional_matches['variant_of'],
active=str2bool(part_data.get('active', True)),
base_cost=part_data.get('base_cost', 0),
multiple=part_data.get('multiple', 1),
assembly=str2bool(part_data.get('assembly', part_settings.part_assembly_default())),
component=str2bool(part_data.get('component', part_settings.part_component_default())),
is_template=str2bool(part_data.get('is_template', part_settings.part_template_default())),
purchaseable=str2bool(part_data.get('purchaseable', part_settings.part_purchaseable_default())),
salable=str2bool(part_data.get('salable', part_settings.part_salable_default())),
trackable=str2bool(part_data.get('trackable', part_settings.part_trackable_default())),
virtual=str2bool(part_data.get('virtual', part_settings.part_virtual_default())),
) )
try: try:
new_part.save() new_part.save()
# add stock item if set
if part_data.get('stock', None):
stock = StockItem(
part=new_part,
location=new_part.default_location,
quantity=int(part_data.get('stock', 1)),
)
stock.save()
import_done += 1 import_done += 1
except ValidationError as _e: except ValidationError as _e:
import_error.append(', '.join(set(_e.messages))) import_error.append(', '.join(set(_e.messages)))

View File

@ -0,0 +1,39 @@
{% extends "email/email.html" %}
{% load i18n %}
{% load inventree_extras %}
{% block title %}
{% trans "Stock is required for the following build order" %}<br>
{% blocktrans with build=build.reference part=part.full_name quantity=build.quantity %}Build order {{ build }} - building {{ quantity }} x {{ part }}{% endblocktrans %}
<br>
<p>{% trans "Click on the following link to view this build order" %}: <a href='{{ link }}'>{{ link }}</a></p>
{% endblock title %}
{% block body %}
<tr colspan='100%' style='height: 2rem; text-align: center;'>{% trans "The following parts are low on required stock" %}</tr>
<tr style="height: 3rem; border-bottom: 1px solid">
<th>{% trans "Part" %}</th>
<th>{% trans "Required Quantity" %}</th>
<th>{% trans "Available" %}</th>
</tr>
{% for line in lines %}
<tr style="height: 2.5rem; border-bottom: 1px solid">
<td style='padding-left: 1em;'>
<a href='{{ line.link }}'>{{ line.part.full_name }}</a>{% if part.description %} - <em>{{ part.description }}</em>{% endif %}
</td>
<td style="text-align: center;">
{% decimal line.required %} {% if line.part.units %}{{ line.part.units }}{% endif %}
</td>
<td style="text-align: center;">{% decimal line.available %} {% if line.part.units %}{{ line.part.units }}{% endif %}</td>
</tr>
{% endfor %}
{% endblock body %}
{% block footer_prefix %}
<p><em>{% blocktrans with part=part.name %}You are receiving this email because you are subscribed to notifications for this part {% endblocktrans %}.</em></p>
{% endblock footer_prefix %}

View File

@ -8,15 +8,12 @@
{% if link %} {% if link %}
<p>{% trans "Click on the following link to view this part" %}: <a href="{{ link }}">{{ link }}</a></p> <p>{% trans "Click on the following link to view this part" %}: <a href="{{ link }}">{{ link }}</a></p>
{% endif %} {% endif %}
{% endblock %} {% endblock title %}
{% block subtitle %}
<p><em>{% blocktrans with part=part.name %}You are receiving this email because you are subscribed to notifications for this part {% endblocktrans %}.</em></p>
{% endblock %}
{% block body %} {% block body %}
<tr style="height: 3rem; border-bottom: 1px solid"> <tr style="height: 3rem; border-bottom: 1px solid">
<th>{% trans "Part Name" %}</th> <th>{% trans "Part" %}</th>
<th>{% trans "Total Stock" %}</th> <th>{% trans "Total Stock" %}</th>
<th>{% trans "Available" %}</th> <th>{% trans "Available" %}</th>
<th>{% trans "Minimum Quantity" %}</th> <th>{% trans "Minimum Quantity" %}</th>
@ -24,8 +21,12 @@
<tr style="height: 3rem"> <tr style="height: 3rem">
<td style="text-align: center;">{{ part.full_name }}</td> <td style="text-align: center;">{{ part.full_name }}</td>
<td style="text-align: center;">{{ part.total_stock }}</td> <td style="text-align: center;">{% decimal part.total_stock %}</td>
<td style="text-align: center;">{{ part.available_stock }}</td> <td style="text-align: center;">{% decimal part.available_stock %}</td>
<td style="text-align: center;">{{ part.minimum_stock }}</td> <td style="text-align: center;">{% decimal part.minimum_stock %}</td>
</tr> </tr>
{% endblock %} {% endblock body %}
{% block footer_prefix %}
<p><em>{% blocktrans with part=part.name %}You are receiving this email because you are subscribed to notifications for this part {% endblocktrans %}.</em></p>
{% endblock footer_prefix %}

View File

@ -1159,6 +1159,13 @@ function loadPartCategoryTable(table, options) {
filters = loadTableFilters(filterKey); filters = loadTableFilters(filterKey);
} }
var tree_view = options.allowTreeView && inventreeLoad('category-tree-view') == 1;
if (tree_view) {
params.cascade = true;
}
var original = {}; var original = {};
for (var key in params) { for (var key in params) {
@ -1168,8 +1175,6 @@ function loadPartCategoryTable(table, options) {
setupFilterList(filterKey, table, filterListElement); setupFilterList(filterKey, table, filterListElement);
var tree_view = options.allowTreeView && inventreeLoad('category-tree-view') == 1;
table.inventreeTable({ table.inventreeTable({
treeEnable: tree_view, treeEnable: tree_view,
rootParentId: tree_view ? options.params.parent : null, rootParentId: tree_view ? options.params.parent : null,

View File

@ -1679,6 +1679,12 @@ function loadStockLocationTable(table, options) {
var filterListElement = options.filterList || '#filter-list-location'; var filterListElement = options.filterList || '#filter-list-location';
var tree_view = options.allowTreeView && inventreeLoad('location-tree-view') == 1;
if (tree_view) {
params.cascade = true;
}
var filters = {}; var filters = {};
var filterKey = options.filterKey || options.name || 'location'; var filterKey = options.filterKey || options.name || 'location';
@ -1699,8 +1705,6 @@ function loadStockLocationTable(table, options) {
filters[key] = params[key]; filters[key] = params[key];
} }
var tree_view = options.allowTreeView && inventreeLoad('location-tree-view') == 1;
table.inventreeTable({ table.inventreeTable({
treeEnable: tree_view, treeEnable: tree_view,
rootParentId: tree_view ? options.params.parent : null, rootParentId: tree_view ? options.params.parent : null,