mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge remote-tracking branch 'inventree/master'
This commit is contained in:
commit
669f27011e
4
.github/ISSUE_TEMPLATE/install.yaml
vendored
4
.github/ISSUE_TEMPLATE/install.yaml
vendored
@ -4,6 +4,8 @@ labels: ["question", "triage:not-checked", "setup"]
|
||||
body:
|
||||
- type: checkboxes
|
||||
id: deployment
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: "Deployment Method"
|
||||
options:
|
||||
@ -12,6 +14,8 @@ body:
|
||||
- label: "Docker Production"
|
||||
- label: "Bare metal Development"
|
||||
- label: "Bare metal Production"
|
||||
- label: "Digital Ocean image"
|
||||
- label: "Other (please provide a link `Steps to Reproduce`"
|
||||
- type: textarea
|
||||
id: description
|
||||
validations:
|
||||
|
45
.pkgr.yml
45
.pkgr.yml
@ -15,33 +15,20 @@ env:
|
||||
- INVENTREE_PLUGIN_FILE=/opt/inventree/plugins.txt
|
||||
- INVENTREE_CONFIG_FILE=/opt/inventree/config.yaml
|
||||
after_install: contrib/packager.io/postinstall.sh
|
||||
dependencies:
|
||||
- curl
|
||||
- python3
|
||||
- python3-venv
|
||||
- python3-pip
|
||||
- python3-cffi
|
||||
- python3-brotli
|
||||
- python3-wheel
|
||||
- libpango-1.0-0
|
||||
- libharfbuzz0b
|
||||
- libpangoft2-1.0-0
|
||||
- gettext
|
||||
- nginx
|
||||
- jq
|
||||
targets:
|
||||
ubuntu-20.04:
|
||||
dependencies:
|
||||
- curl
|
||||
- python3
|
||||
- python3-venv
|
||||
- python3-pip
|
||||
- python3-cffi
|
||||
- python3-brotli
|
||||
- python3-wheel
|
||||
- libpango-1.0-0
|
||||
- libharfbuzz0b
|
||||
- libpangoft2-1.0-0
|
||||
- gettext
|
||||
- nginx
|
||||
- jq
|
||||
debian-11:
|
||||
dependencies:
|
||||
- curl
|
||||
- python3
|
||||
- python3-venv
|
||||
- python3-pip
|
||||
- python3-cffi
|
||||
- python3-brotli
|
||||
- python3-wheel
|
||||
- libpango-1.0-0
|
||||
- libpangoft2-1.0-0
|
||||
- gettext
|
||||
- nginx
|
||||
- jq
|
||||
ubuntu-20.04: true
|
||||
debian-11: true
|
||||
|
@ -2,11 +2,14 @@
|
||||
|
||||
|
||||
# InvenTree API version
|
||||
INVENTREE_API_VERSION = 82
|
||||
INVENTREE_API_VERSION = 83
|
||||
|
||||
"""
|
||||
Increment this API version number whenever there is a significant change to the API that any clients need to know about
|
||||
|
||||
v83 -> 2022-11-19 : https://github.com/inventree/InvenTree/pull/3949
|
||||
- Add support for structural Stock locations
|
||||
|
||||
v82 -> 2022-11-16 : https://github.com/inventree/InvenTree/pull/3931
|
||||
- Add support for structural Part categories
|
||||
|
||||
|
@ -744,6 +744,7 @@ SOCIALACCOUNT_STORE_TOKENS = True
|
||||
# settings for allauth
|
||||
ACCOUNT_EMAIL_CONFIRMATION_EXPIRE_DAYS = get_setting('INVENTREE_LOGIN_CONFIRM_DAYS', 'login_confirm_days', 3, typecast=int)
|
||||
ACCOUNT_LOGIN_ATTEMPTS_LIMIT = get_setting('INVENTREE_LOGIN_ATTEMPTS', 'login_attempts', 5, typecast=int)
|
||||
ACCOUNT_DEFAULT_HTTP_PROTOCOL = get_setting('INVENTREE_LOGIN_DEFAULT_HTTP_PROTOCOL', 'login_default_protocol', 'http')
|
||||
ACCOUNT_LOGOUT_ON_PASSWORD_CHANGE = True
|
||||
ACCOUNT_PREVENT_ENUMERATION = True
|
||||
|
||||
|
@ -458,7 +458,7 @@ main {
|
||||
}
|
||||
|
||||
.part-thumb-container:hover .part-thumb-overlay {
|
||||
opacity: 1;
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
.part-thumb-overlay {
|
||||
@ -467,8 +467,6 @@ main {
|
||||
left: 0;
|
||||
opacity: 0;
|
||||
transition: .25s ease;
|
||||
padding-top: 10px;
|
||||
padding-left: 25px;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@ import re
|
||||
import warnings
|
||||
from dataclasses import dataclass
|
||||
from datetime import timedelta
|
||||
from typing import Callable
|
||||
from typing import Callable, List
|
||||
|
||||
from django.conf import settings
|
||||
from django.core import mail as django_mail
|
||||
@ -153,7 +153,7 @@ class ScheduledTask:
|
||||
|
||||
class TaskRegister:
|
||||
"""Registery for periodicall tasks."""
|
||||
task_list: list[ScheduledTask] = []
|
||||
task_list: List[ScheduledTask] = []
|
||||
|
||||
def register(self, task, schedule, minutes: int = None):
|
||||
"""Register a task with the que."""
|
||||
|
@ -3,23 +3,7 @@
|
||||
from django import forms
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from InvenTree.forms import HelperForm
|
||||
|
||||
from .files import FileManager
|
||||
from .models import InvenTreeSetting
|
||||
|
||||
|
||||
class SettingEditForm(HelperForm):
|
||||
"""Form for creating / editing a settings object."""
|
||||
|
||||
class Meta:
|
||||
"""Metaclassoptions for SettingEditForm."""
|
||||
|
||||
model = InvenTreeSetting
|
||||
|
||||
fields = [
|
||||
'value'
|
||||
]
|
||||
|
||||
|
||||
class UploadFileForm(forms.Form):
|
||||
|
@ -58,6 +58,9 @@
|
||||
{% if allow_download %}
|
||||
<button type='button' class='btn btn-outline-secondary' title="{% trans 'Download image from URL' %}" id='company-image-url'><span class='fas fa-cloud-download-alt'></span></button>
|
||||
{% endif %}
|
||||
{% if company.image %}
|
||||
<button type='button' class='btn btn-outline-secondary' title='{% trans "Delete image" %}' id='company-image-delete'><span class='fas fa-trash-alt icon-red'></span></button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -194,10 +197,37 @@
|
||||
$('#company-image').click(function() {
|
||||
showModalImage('{{ company.image.url }}');
|
||||
});
|
||||
|
||||
$('#company-image-delete').click(function(event) {
|
||||
event.stopPropagation();
|
||||
showQuestionDialog(
|
||||
'{% trans "Remove Image" %}',
|
||||
'{% trans "Remove associated image from this company" %}',
|
||||
{
|
||||
accept_text: '{% trans "Remove" %}',
|
||||
submitClass: 'danger',
|
||||
accept: function() {
|
||||
inventreePut(
|
||||
'{% url "api-company-detail" company.pk %}',
|
||||
{
|
||||
'image': null,
|
||||
},
|
||||
{
|
||||
method: 'PATCH',
|
||||
success: function() {
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
{% endif %}
|
||||
|
||||
$("#company-image-upload").click(function() {
|
||||
|
||||
$("#company-image-upload").click(function(event) {
|
||||
event.stopPropagation();
|
||||
constructForm(
|
||||
'{% url "api-company-detail" company.pk %}',
|
||||
{
|
||||
|
@ -161,6 +161,7 @@ background:
|
||||
# Login configuration
|
||||
login_confirm_days: 3
|
||||
login_attempts: 5
|
||||
login_default_protocol: http
|
||||
|
||||
# Remote / proxy login
|
||||
# These settings can introduce security problems if configured incorrectly. Please read
|
||||
|
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
@ -14,6 +14,6 @@ class Migration(migrations.Migration):
|
||||
migrations.AddField(
|
||||
model_name='partcategory',
|
||||
name='structural',
|
||||
field=models.BooleanField(default=False, help_text="Parts may not be directly assigned to a structural category, but may be assigned to it's child categories.", verbose_name='Structural'),
|
||||
field=models.BooleanField(default=False, help_text="Parts may not be directly assigned to a structural category, but may be assigned to child categories.", verbose_name='Structural'),
|
||||
),
|
||||
]
|
||||
|
@ -122,7 +122,7 @@ class PartCategory(MetadataMixin, InvenTreeTree):
|
||||
verbose_name=_('Structural'),
|
||||
help_text=_(
|
||||
'Parts may not be directly assigned to a structural category, '
|
||||
'but may be assigned to it\'s child categories.'),
|
||||
'but may be assigned to child categories.'),
|
||||
)
|
||||
|
||||
default_keywords = models.CharField(null=True, blank=True, max_length=250, verbose_name=_('Default keywords'), help_text=_('Default keywords for parts in this category'))
|
||||
|
@ -502,8 +502,34 @@
|
||||
});
|
||||
});
|
||||
|
||||
$("#part-image-upload").click(function() {
|
||||
$('#part-image-delete').click(function(event) {
|
||||
event.stopPropagation();
|
||||
showQuestionDialog(
|
||||
'{% trans "Remove Image" %}',
|
||||
'{% trans "Remove associated image from this part" %}',
|
||||
{
|
||||
accept_text: '{% trans "Remove" %}',
|
||||
submitClass: 'danger',
|
||||
accept: function() {
|
||||
inventreePut(
|
||||
'{% url "api-part-detail" part.pk %}',
|
||||
{
|
||||
'image': null,
|
||||
},
|
||||
{
|
||||
method: 'PATCH',
|
||||
success: function(data) {
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
$("#part-image-upload").click(function(event) {
|
||||
event.stopPropagation();
|
||||
constructForm(
|
||||
'{% url "api-part-detail" part.pk %}',
|
||||
{
|
||||
@ -576,12 +602,12 @@
|
||||
});
|
||||
}
|
||||
|
||||
$("#part-image-select").click(function() {
|
||||
launchModalForm("{% url 'part-image-select' part.id %}",
|
||||
{
|
||||
reload: true,
|
||||
after_render: onSelectImage
|
||||
});
|
||||
$("#part-image-select").click(function(event) {
|
||||
event.stopPropagation();
|
||||
launchModalForm("{% url 'part-image-select' part.id %}", {
|
||||
reload: true,
|
||||
after_render: onSelectImage
|
||||
});
|
||||
});
|
||||
|
||||
$("#part-edit").click(function() {
|
||||
|
@ -13,6 +13,9 @@
|
||||
{% if allow_download %}
|
||||
<button type='button' class='btn btn-outline-secondary' title="{% trans 'Download image from URL' %}" id='part-image-url'><span class='fas fa-cloud-download-alt'></span></button>
|
||||
{% endif %}
|
||||
{% if part.image %}
|
||||
<button type='button' class='btn btn-outline-secondary' title='{% trans "Delete image" %}' id='part-image-delete'><span class='fas fa-trash-alt icon-red'></span></button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
@ -309,6 +309,8 @@ class StockLocationList(APIDownloadMixin, ListCreateAPI):
|
||||
]
|
||||
|
||||
filterset_fields = [
|
||||
'name',
|
||||
'structural'
|
||||
]
|
||||
|
||||
search_fields = [
|
||||
|
18
InvenTree/stock/migrations/0090_stocklocation_structural.py
Normal file
18
InvenTree/stock/migrations/0090_stocklocation_structural.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 3.2.16 on 2022-11-18 15:49
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('stock', '0089_alter_stockitem_purchase_price'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='stocklocation',
|
||||
name='structural',
|
||||
field=models.BooleanField(default=False, help_text="Stock items may not be directly located into a structural stock locations, but may be located to child locations.", verbose_name='Structural'),
|
||||
),
|
||||
]
|
@ -107,6 +107,14 @@ class StockLocation(InvenTreeBarcodeMixin, MetadataMixin, InvenTreeTree):
|
||||
help_text=_('Select Owner'),
|
||||
related_name='stock_locations')
|
||||
|
||||
structural = models.BooleanField(
|
||||
default=False,
|
||||
verbose_name=_('Structural'),
|
||||
help_text=_(
|
||||
'Stock items may not be directly located into a structural stock locations, '
|
||||
'but may be located to child locations.'),
|
||||
)
|
||||
|
||||
def get_location_owner(self):
|
||||
"""Get the closest "owner" for this location.
|
||||
|
||||
@ -139,6 +147,17 @@ class StockLocation(InvenTreeBarcodeMixin, MetadataMixin, InvenTreeTree):
|
||||
|
||||
return user in owner.get_related_owners(include_group=True)
|
||||
|
||||
def clean(self):
|
||||
"""Custom clean action for the StockLocation model:
|
||||
|
||||
- Ensure stock location can't be made structural if stock items already located to them
|
||||
"""
|
||||
if self.pk and self.structural and self.item_count > 0:
|
||||
raise ValidationError(
|
||||
_("You cannot make this stock location structural because some stock items "
|
||||
"are already located into it!"))
|
||||
super().clean()
|
||||
|
||||
def get_absolute_url(self):
|
||||
"""Return url for instance."""
|
||||
return reverse('stock-location-detail', kwargs={'pk': self.id})
|
||||
@ -496,8 +515,14 @@ class StockItem(InvenTreeBarcodeMixin, MetadataMixin, MPTTModel):
|
||||
- The 'part' and 'supplier_part.part' fields cannot point to the same Part object
|
||||
- The 'part' is not virtual
|
||||
- The 'part' does not belong to itself
|
||||
- The location is not structural
|
||||
- Quantity must be 1 if the StockItem has a serial number
|
||||
"""
|
||||
|
||||
if self.location is not None and self.location.structural:
|
||||
raise ValidationError(
|
||||
{'location': _("Stock items cannot be located into structural stock locations!")})
|
||||
|
||||
super().clean()
|
||||
|
||||
# Strip serial number field
|
||||
|
@ -606,6 +606,7 @@ class LocationSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
||||
'items',
|
||||
'owner',
|
||||
'icon',
|
||||
'structural',
|
||||
]
|
||||
|
||||
read_only_fields = [
|
||||
|
@ -6,6 +6,7 @@ from datetime import datetime, timedelta
|
||||
from enum import IntEnum
|
||||
|
||||
import django.http
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.urls import reverse
|
||||
|
||||
import tablib
|
||||
@ -225,6 +226,71 @@ class StockLocationTest(StockAPITestCase):
|
||||
child.refresh_from_db()
|
||||
self.assertEqual(child.parent, parent_stock_location)
|
||||
|
||||
def test_stock_location_structural(self):
|
||||
"""Test the effectiveness of structural stock locations
|
||||
|
||||
Make sure:
|
||||
- Stock items cannot be created in structural locations
|
||||
- Stock items cannot be located to structural locations
|
||||
- Check that stock location change to structural fails if items located into it
|
||||
"""
|
||||
|
||||
# Create our structural stock location
|
||||
structural_location = StockLocation.objects.create(
|
||||
name='Structural stock location',
|
||||
description='This is the structural stock location',
|
||||
parent=None,
|
||||
structural=True
|
||||
)
|
||||
|
||||
stock_item_count_before = StockItem.objects.count()
|
||||
|
||||
# Make sure that we get an error if we try to create a stock item in the structural location
|
||||
with self.assertRaises(ValidationError):
|
||||
item = StockItem.objects.create(
|
||||
batch="Stock item which shall not be created",
|
||||
location=structural_location
|
||||
)
|
||||
|
||||
# Ensure that the stock item really did not get created in the structural location
|
||||
self.assertEqual(stock_item_count_before, StockItem.objects.count())
|
||||
|
||||
# Create a non-structural location for test stock location change
|
||||
non_structural_location = StockLocation.objects.create(
|
||||
name='Non-structural category',
|
||||
description='This is a non-structural category',
|
||||
parent=None,
|
||||
structural=False
|
||||
)
|
||||
|
||||
# Construct a part for stock item creation
|
||||
part = Part.objects.create(
|
||||
name='Part for stock item creation', description='Part for stock item creation',
|
||||
category=None,
|
||||
is_template=False,
|
||||
)
|
||||
|
||||
# Create the test stock item located to a non-structural category
|
||||
item = StockItem.objects.create(
|
||||
batch="Item which will be tried to relocated to a structural location",
|
||||
location=non_structural_location,
|
||||
part=part
|
||||
)
|
||||
|
||||
# Try to relocate it to a structural location
|
||||
item.location = structural_location
|
||||
with self.assertRaises(ValidationError):
|
||||
item.save()
|
||||
|
||||
# Ensure that the item did not get saved to the DB
|
||||
item.refresh_from_db()
|
||||
self.assertEqual(item.location.pk, non_structural_location.pk)
|
||||
|
||||
# Try to change the non-structural location to structural while items located into it
|
||||
non_structural_location.structural = True
|
||||
with self.assertRaises(ValidationError):
|
||||
non_structural_location.full_clean()
|
||||
|
||||
|
||||
class StockItemListTest(StockAPITestCase):
|
||||
"""Tests for the StockItem API LIST endpoint."""
|
||||
|
@ -60,9 +60,15 @@ function buildFormFields() {
|
||||
},
|
||||
take_from: {
|
||||
icon: 'fa-sitemap',
|
||||
filters: {
|
||||
structural: false,
|
||||
}
|
||||
},
|
||||
destination: {
|
||||
icon: 'fa-sitemap',
|
||||
filters: {
|
||||
structural: false,
|
||||
}
|
||||
},
|
||||
link: {
|
||||
icon: 'fa-link',
|
||||
@ -524,7 +530,11 @@ function completeBuildOutputs(build_id, outputs, options={}) {
|
||||
preFormContent: html,
|
||||
fields: {
|
||||
status: {},
|
||||
location: {},
|
||||
location: {
|
||||
filters: {
|
||||
structural: false,
|
||||
},
|
||||
},
|
||||
notes: {},
|
||||
accept_incomplete_allocation: {},
|
||||
},
|
||||
@ -2391,7 +2401,7 @@ function autoAllocateStockToBuild(build_id, bom_items=[], options={}) {
|
||||
<strong>{% trans "Automatic Stock Allocation" %}</strong><br>
|
||||
{% trans "Stock items will be automatically allocated to this build order, according to the provided guidelines" %}:
|
||||
<ul>
|
||||
<li>{% trans "If a location is specifed, stock will only be allocated from that location" %}</li>
|
||||
<li>{% trans "If a location is specified, stock will only be allocated from that location" %}</li>
|
||||
<li>{% trans "If stock is considered interchangeable, it will be allocated from the first location it is found" %}</li>
|
||||
<li>{% trans "If substitute stock is allowed, it will be used where stock of the primary part cannot be found" %}</li>
|
||||
</ul>
|
||||
@ -2401,6 +2411,9 @@ function autoAllocateStockToBuild(build_id, bom_items=[], options={}) {
|
||||
var fields = {
|
||||
location: {
|
||||
value: options.location,
|
||||
filters: {
|
||||
structural: false,
|
||||
}
|
||||
},
|
||||
exclude_location: {},
|
||||
interchangeable: {
|
||||
|
@ -621,11 +621,11 @@ function showQuestionDialog(title, content, options={}) {
|
||||
* cancel - Functino to run if the user presses 'Cancel'
|
||||
*/
|
||||
|
||||
var modal = createNewModal({
|
||||
title: title,
|
||||
submitText: options.accept_text || '{% trans "Accept" %}',
|
||||
closeText: options.cancel_text || '{% trans "Cancel" %}',
|
||||
});
|
||||
options.title = title;
|
||||
options.submitText = options.accept_text || '{% trans "Accept" %}';
|
||||
options.closeText = options.cancel_text || '{% trans "Cancel" %}';
|
||||
|
||||
var modal = createNewModal(options);
|
||||
|
||||
modalSetContent(modal, content);
|
||||
|
||||
|
@ -888,7 +888,11 @@ function poLineItemFields(options={}) {
|
||||
purchase_price: {},
|
||||
purchase_price_currency: {},
|
||||
target_date: {},
|
||||
destination: {},
|
||||
destination: {
|
||||
filters: {
|
||||
structural: false,
|
||||
}
|
||||
},
|
||||
notes: {},
|
||||
};
|
||||
|
||||
@ -1688,7 +1692,11 @@ function receivePurchaseOrderItems(order_id, line_items, options={}) {
|
||||
constructForm(`/api/order/po/${order_id}/receive/`, {
|
||||
method: 'POST',
|
||||
fields: {
|
||||
location: {},
|
||||
location: {
|
||||
filters: {
|
||||
structural: false,
|
||||
}
|
||||
},
|
||||
},
|
||||
preFormContent: html,
|
||||
confirm: true,
|
||||
|
@ -99,6 +99,9 @@ function partFields(options={}) {
|
||||
icon: 'fa-link',
|
||||
},
|
||||
default_location: {
|
||||
filters: {
|
||||
structural: false,
|
||||
}
|
||||
},
|
||||
default_supplier: {
|
||||
filters: {
|
||||
@ -297,7 +300,11 @@ function categoryFields() {
|
||||
},
|
||||
name: {},
|
||||
description: {},
|
||||
default_location: {},
|
||||
default_location: {
|
||||
filters: {
|
||||
structural: false,
|
||||
}
|
||||
},
|
||||
default_keywords: {
|
||||
icon: 'fa-key',
|
||||
},
|
||||
|
@ -80,6 +80,9 @@ function serializeStockItem(pk, options={}) {
|
||||
},
|
||||
destination: {
|
||||
icon: 'fa-sitemap',
|
||||
filters: {
|
||||
structural: false,
|
||||
}
|
||||
},
|
||||
notes: {},
|
||||
};
|
||||
@ -114,6 +117,7 @@ function stockLocationFields(options={}) {
|
||||
name: {},
|
||||
description: {},
|
||||
owner: {},
|
||||
structural: {},
|
||||
icon: {
|
||||
help_text: `{% trans "Icon (optional) - Explore all available icons on" %} <a href="https://fontawesome.com/v5/search?s=solid" target="_blank" rel="noopener noreferrer">Font Awesome</a>.`,
|
||||
placeholder: 'fas fa-box',
|
||||
@ -280,6 +284,9 @@ function stockItemFields(options={}) {
|
||||
},
|
||||
location: {
|
||||
icon: 'fa-sitemap',
|
||||
filters: {
|
||||
structural: false,
|
||||
},
|
||||
},
|
||||
quantity: {
|
||||
help_text: '{% trans "Enter initial quantity for this stock item" %}',
|
||||
@ -838,6 +845,9 @@ function mergeStockItems(items, options={}) {
|
||||
location: {
|
||||
value: location,
|
||||
icon: 'fa-sitemap',
|
||||
filters: {
|
||||
structural: false,
|
||||
}
|
||||
},
|
||||
notes: {},
|
||||
allow_mismatched_suppliers: {},
|
||||
@ -1106,7 +1116,11 @@ function adjustStock(action, items, options={}) {
|
||||
var extraFields = {};
|
||||
|
||||
if (specifyLocation) {
|
||||
extraFields.location = {};
|
||||
extraFields.location = {
|
||||
filters: {
|
||||
structural: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (action != 'delete') {
|
||||
@ -2810,6 +2824,9 @@ function uninstallStockItem(installed_item_id, options={}) {
|
||||
fields: {
|
||||
location: {
|
||||
icon: 'fa-sitemap',
|
||||
filters: {
|
||||
structural: false,
|
||||
}
|
||||
},
|
||||
note: {},
|
||||
},
|
||||
|
@ -132,8 +132,6 @@ There are several options to deploy InvenTree.
|
||||
<div align="center"><h4>
|
||||
<a href="https://inventree.readthedocs.io/en/latest/start/docker/">Docker</a>
|
||||
<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>
|
||||
</h4></div>
|
||||
|
||||
|
@ -2,16 +2,12 @@
|
||||
# This script was generated by bashly 0.8.9 (https://bashly.dannyb.co)
|
||||
# Modifying it manually is not recommended
|
||||
|
||||
# :wrapper.bash3_bouncer
|
||||
if [[ "${BASH_VERSINFO:-0}" -lt 4 ]]; then
|
||||
printf "bash version 4 or higher is required\n" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# :command.master_script
|
||||
# :command.root_command
|
||||
root_command() {
|
||||
# src/root_command.sh
|
||||
# Settings
|
||||
source_url=${args[source]}
|
||||
publisher=${args[publisher]}
|
||||
@ -59,25 +55,26 @@ root_command() {
|
||||
# Check if os and version is supported
|
||||
get_distribution
|
||||
echo "### Detected distribution: $OS $VER"
|
||||
NOT_SUPPORTED=false
|
||||
SUPPORTED=true
|
||||
case "$OS" in
|
||||
Ubuntu)
|
||||
if [[ $VER != "20.04" ]]; then
|
||||
NOT_SUPPORTED=true
|
||||
SUPPORTED=false
|
||||
fi
|
||||
;;
|
||||
Debian | Raspbian)
|
||||
"Debian GNU/Linux" | Raspbian)
|
||||
if [[ $VER != "11" ]]; then
|
||||
NOT_SUPPORTED=true
|
||||
SUPPORTED=false
|
||||
fi
|
||||
OS=Debian
|
||||
;;
|
||||
*)
|
||||
echo "### Distribution not supported"
|
||||
NOT_SUPPORTED=true
|
||||
SUPPORTED=false
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ $NOT_SUPPORTED ]]; then
|
||||
if [[ $SUPPORTED != "true" ]]; then
|
||||
echo "This OS is currently not supported"
|
||||
echo "please install manually using https://inventree.readthedocs.io/en/stable/start/install/"
|
||||
echo "or check https://github.com/inventree/InvenTree/issues/3836 for packaging for your OS."
|
||||
@ -96,11 +93,10 @@ root_command() {
|
||||
fi
|
||||
done
|
||||
|
||||
echo "### Adding key and package source"
|
||||
# Add key
|
||||
do_call "wget -qO- https://dl.packager.io/srv/$publisher/InvenTree/key | sudo apt-key add -"
|
||||
# Add packagelist
|
||||
do_call "sudo wget -O /etc/apt/sources.list.d/inventree.list https://dl.packager.io/srv/$publisher/InvenTree/$source_url/installer/${lsb_dist}/${dist_version}.repo"
|
||||
echo "### Getting and adding key"
|
||||
wget -qO- https://dl.packager.io/srv/$publisher/InvenTree/key | sudo apt-key add -
|
||||
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"
|
||||
|
||||
echo "### Updateing package lists"
|
||||
do_call "sudo apt-get update"
|
||||
@ -118,34 +114,30 @@ root_command() {
|
||||
|
||||
}
|
||||
|
||||
# :command.version_command
|
||||
version_command() {
|
||||
echo "$version"
|
||||
}
|
||||
|
||||
# :command.usage
|
||||
install_usage() {
|
||||
install.sh_usage() {
|
||||
if [[ -n $long_usage ]]; then
|
||||
printf "install - Interactive installer for InvenTree\n"
|
||||
printf "install.sh - Interactive installer for InvenTree\n"
|
||||
echo
|
||||
|
||||
else
|
||||
printf "install - Interactive installer for InvenTree\n"
|
||||
printf "install.sh - Interactive installer for InvenTree\n"
|
||||
echo
|
||||
|
||||
fi
|
||||
|
||||
printf "Usage:\n"
|
||||
printf " install [SOURCE] [PUBLISHER] [OPTIONS]\n"
|
||||
printf " install --help | -h\n"
|
||||
printf " install --version | -v\n"
|
||||
printf " install.sh [SOURCE] [PUBLISHER] [OPTIONS]\n"
|
||||
printf " install.sh --help | -h\n"
|
||||
printf " install.sh --version | -v\n"
|
||||
echo
|
||||
|
||||
# :command.long_usage
|
||||
if [[ -n $long_usage ]]; then
|
||||
printf "Options:\n"
|
||||
|
||||
# :command.usage_fixed_flags
|
||||
echo " --help, -h"
|
||||
printf " Show this help\n"
|
||||
echo
|
||||
@ -153,34 +145,27 @@ install_usage() {
|
||||
printf " Show version number\n"
|
||||
echo
|
||||
|
||||
# :command.usage_flags
|
||||
# :flag.usage
|
||||
echo " --no-call, -n"
|
||||
printf " Do not call outside APIs (only functionally needed)\n"
|
||||
echo
|
||||
|
||||
# :flag.usage
|
||||
echo " --dry-run, -d"
|
||||
printf " Dry run (do not install anything)\n"
|
||||
echo
|
||||
|
||||
# :command.usage_args
|
||||
printf "Arguments:\n"
|
||||
|
||||
# :argument.usage
|
||||
echo " SOURCE"
|
||||
printf " Package source that should be used\n"
|
||||
printf " Allowed: stable, master, main\n"
|
||||
printf " Default: stable\n"
|
||||
echo
|
||||
|
||||
# :argument.usage
|
||||
echo " PUBLISHER"
|
||||
printf " Publisher that should be used\n"
|
||||
printf " Default: inventree\n"
|
||||
echo
|
||||
|
||||
# :command.usage_examples
|
||||
printf "Examples:\n"
|
||||
printf " install\n"
|
||||
printf " install master --no-call\n"
|
||||
@ -190,7 +175,6 @@ install_usage() {
|
||||
fi
|
||||
}
|
||||
|
||||
# :command.normalize_input
|
||||
normalize_input() {
|
||||
local arg flags
|
||||
|
||||
@ -214,7 +198,7 @@ normalize_input() {
|
||||
shift
|
||||
done
|
||||
}
|
||||
# :command.inspect_args
|
||||
|
||||
inspect_args() {
|
||||
readarray -t sorted_keys < <(printf '%s\n' "${!args[@]}" | sort)
|
||||
if (( ${#args[@]} )); then
|
||||
@ -234,11 +218,8 @@ inspect_args() {
|
||||
fi
|
||||
}
|
||||
|
||||
# :command.command_functions
|
||||
|
||||
# :command.parse_requirements
|
||||
parse_requirements() {
|
||||
# :command.fixed_flags_filter
|
||||
|
||||
case "${1:-}" in
|
||||
--version | -v )
|
||||
version_command
|
||||
@ -247,31 +228,26 @@ parse_requirements() {
|
||||
|
||||
--help | -h )
|
||||
long_usage=yes
|
||||
install_usage
|
||||
install.sh_usage
|
||||
exit
|
||||
;;
|
||||
|
||||
esac
|
||||
|
||||
# :command.command_filter
|
||||
action="root"
|
||||
|
||||
# :command.parse_requirements_while
|
||||
while [[ $# -gt 0 ]]; do
|
||||
key="$1"
|
||||
case "$key" in
|
||||
# :flag.case
|
||||
|
||||
--no-call | -n )
|
||||
|
||||
# :flag.case_no_arg
|
||||
args[--no-call]=1
|
||||
shift
|
||||
;;
|
||||
|
||||
# :flag.case
|
||||
--dry-run | -d )
|
||||
|
||||
# :flag.case_no_arg
|
||||
args[--dry-run]=1
|
||||
shift
|
||||
;;
|
||||
@ -282,8 +258,7 @@ parse_requirements() {
|
||||
;;
|
||||
|
||||
* )
|
||||
# :command.parse_requirements_case
|
||||
# :command.parse_requirements_case_simple
|
||||
|
||||
if [[ -z ${args[source]+x} ]]; then
|
||||
|
||||
args[source]=$1
|
||||
@ -302,11 +277,9 @@ parse_requirements() {
|
||||
esac
|
||||
done
|
||||
|
||||
# :command.default_assignments
|
||||
[[ -n ${args[source]:-} ]] || args[source]="stable"
|
||||
[[ -n ${args[publisher]:-} ]] || args[publisher]="inventree"
|
||||
|
||||
# :command.whitelist_filter
|
||||
if [[ ! ${args[source]} =~ ^(stable|master|main)$ ]]; then
|
||||
printf "%s\n" "source must be one of: stable, master, main" >&2
|
||||
exit 1
|
||||
@ -314,17 +287,14 @@ parse_requirements() {
|
||||
|
||||
}
|
||||
|
||||
# :command.initialize
|
||||
initialize() {
|
||||
version="2.0"
|
||||
long_usage=''
|
||||
set -e
|
||||
|
||||
# src/initialize.sh
|
||||
|
||||
}
|
||||
|
||||
# :command.run
|
||||
run() {
|
||||
declare -A args=()
|
||||
declare -a other_args=()
|
||||
|
@ -1,341 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# This script was generated by bashly 0.8.9 (https://bashly.dannyb.co)
|
||||
# Modifying it manually is not recommended
|
||||
|
||||
# :wrapper.bash3_bouncer
|
||||
if [[ "${BASH_VERSINFO:-0}" -lt 4 ]]; then
|
||||
printf "bash version 4 or higher is required\n" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# :command.master_script
|
||||
# :command.root_command
|
||||
root_command() {
|
||||
# src/root_command.sh
|
||||
# Settings
|
||||
source_url=${args[source]}
|
||||
publisher=${args[publisher]}
|
||||
# Flags
|
||||
no_call=${args[--no-call]}
|
||||
dry_run=${args[--dry-run]}
|
||||
|
||||
REQS="wget apt-transport-https"
|
||||
|
||||
function do_call() {
|
||||
if [[ $dry_run ]]; then
|
||||
echo -e "### DRY RUN: \n$1"
|
||||
else
|
||||
$1
|
||||
fi
|
||||
}
|
||||
|
||||
function get_distribution {
|
||||
if [ -f /etc/os-release ]; then
|
||||
. /etc/os-release
|
||||
OS=$NAME
|
||||
VER=$VERSION_ID
|
||||
elif type lsb_release >/dev/null 2>&1; then
|
||||
OS=$(lsb_release -si)
|
||||
VER=$(lsb_release -sr)
|
||||
elif [ -f /etc/lsb-release ]; then
|
||||
. /etc/lsb-release
|
||||
OS=$DISTRIB_ID
|
||||
VER=$DISTRIB_RELEASE
|
||||
elif [ -f /etc/debian_version ]; then
|
||||
OS=Debian
|
||||
VER=$(cat /etc/debian_version)
|
||||
elif [ -f /etc/SuSe-release ]; then
|
||||
OS=SEL
|
||||
elif [ -f /etc/redhat-release ]; then
|
||||
OS=RedHat
|
||||
else
|
||||
OS=$(uname -s)
|
||||
VER=$(uname -r)
|
||||
fi
|
||||
}
|
||||
|
||||
echo "### Installer for InvenTree - source: $publisher/$source_url"
|
||||
|
||||
# Check if os and version is supported
|
||||
get_distribution
|
||||
echo "### Detected distribution: $OS $VER"
|
||||
NOT_SUPPORTED=false
|
||||
case "$OS" in
|
||||
Ubuntu)
|
||||
if [[ $VER != "20.04" ]]; then
|
||||
NOT_SUPPORTED=true
|
||||
fi
|
||||
;;
|
||||
Debian | Raspbian)
|
||||
if [[ $VER != "11" ]]; then
|
||||
NOT_SUPPORTED=true
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "### Distribution not supported"
|
||||
NOT_SUPPORTED=true
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ $NOT_SUPPORTED ]]; then
|
||||
echo "This OS is currently not supported"
|
||||
echo "please install manually using https://inventree.readthedocs.io/en/stable/start/install/"
|
||||
echo "or check https://github.com/inventree/InvenTree/issues/3836 for packaging for your OS."
|
||||
echo "If you think this is a bug please file an issue at"
|
||||
echo "https://github.com/inventree/InvenTree/issues/new?template=install.yaml"
|
||||
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "### Installing required packages for download"
|
||||
for pkg in $REQS; do
|
||||
if dpkg-query -W -f'${Status}' "$pkg" 2>/dev/null | grep -q "ok installed"; then
|
||||
true
|
||||
else
|
||||
do_call "sudo apt-get -yqq install $pkg"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "### Adding key and package source"
|
||||
# Add key
|
||||
do_call "wget -qO- https://dl.packager.io/srv/$publisher/InvenTree/key | sudo apt-key add -"
|
||||
# Add packagelist
|
||||
do_call "sudo wget -O /etc/apt/sources.list.d/inventree.list https://dl.packager.io/srv/$publisher/InvenTree/$source_url/installer/${lsb_dist}/${dist_version}.repo"
|
||||
|
||||
echo "### Updateing package lists"
|
||||
do_call "sudo apt-get update"
|
||||
|
||||
# Set up environment for install
|
||||
echo "### Setting installer args"
|
||||
if [[ $no_call ]]; then
|
||||
do_call "export NO_CALL=true"
|
||||
fi
|
||||
|
||||
echo "### Installing InvenTree"
|
||||
do_call "sudo apt-get install inventree -y"
|
||||
|
||||
echo "### Install done!"
|
||||
|
||||
}
|
||||
|
||||
# :command.version_command
|
||||
version_command() {
|
||||
echo "$version"
|
||||
}
|
||||
|
||||
# :command.usage
|
||||
install_usage() {
|
||||
if [[ -n $long_usage ]]; then
|
||||
printf "install - Interactive installer for InvenTree\n"
|
||||
echo
|
||||
|
||||
else
|
||||
printf "install - Interactive installer for InvenTree\n"
|
||||
echo
|
||||
|
||||
fi
|
||||
|
||||
printf "Usage:\n"
|
||||
printf " install [SOURCE] [PUBLISHER] [OPTIONS]\n"
|
||||
printf " install --help | -h\n"
|
||||
printf " install --version | -v\n"
|
||||
echo
|
||||
|
||||
# :command.long_usage
|
||||
if [[ -n $long_usage ]]; then
|
||||
printf "Options:\n"
|
||||
|
||||
# :command.usage_fixed_flags
|
||||
echo " --help, -h"
|
||||
printf " Show this help\n"
|
||||
echo
|
||||
echo " --version, -v"
|
||||
printf " Show version number\n"
|
||||
echo
|
||||
|
||||
# :command.usage_flags
|
||||
# :flag.usage
|
||||
echo " --no-call, -n"
|
||||
printf " Do not call outside APIs (only functionally needed)\n"
|
||||
echo
|
||||
|
||||
# :flag.usage
|
||||
echo " --dry-run, -d"
|
||||
printf " Dry run (do not install anything)\n"
|
||||
echo
|
||||
|
||||
# :command.usage_args
|
||||
printf "Arguments:\n"
|
||||
|
||||
# :argument.usage
|
||||
echo " SOURCE"
|
||||
printf " Package source that should be used\n"
|
||||
printf " Allowed: stable, master, main\n"
|
||||
printf " Default: stable\n"
|
||||
echo
|
||||
|
||||
# :argument.usage
|
||||
echo " PUBLISHER"
|
||||
printf " Publisher that should be used\n"
|
||||
printf " Default: inventree\n"
|
||||
echo
|
||||
|
||||
# :command.usage_examples
|
||||
printf "Examples:\n"
|
||||
printf " install\n"
|
||||
printf " install master --no-call\n"
|
||||
printf " install master matmair --dry-run\n"
|
||||
echo
|
||||
|
||||
fi
|
||||
}
|
||||
|
||||
# :command.normalize_input
|
||||
normalize_input() {
|
||||
local arg flags
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
arg="$1"
|
||||
if [[ $arg =~ ^(--[a-zA-Z0-9_\-]+)=(.+)$ ]]; then
|
||||
input+=("${BASH_REMATCH[1]}")
|
||||
input+=("${BASH_REMATCH[2]}")
|
||||
elif [[ $arg =~ ^(-[a-zA-Z0-9])=(.+)$ ]]; then
|
||||
input+=("${BASH_REMATCH[1]}")
|
||||
input+=("${BASH_REMATCH[2]}")
|
||||
elif [[ $arg =~ ^-([a-zA-Z0-9][a-zA-Z0-9]+)$ ]]; then
|
||||
flags="${BASH_REMATCH[1]}"
|
||||
for (( i=0 ; i < ${#flags} ; i++ )); do
|
||||
input+=("-${flags:i:1}")
|
||||
done
|
||||
else
|
||||
input+=("$arg")
|
||||
fi
|
||||
|
||||
shift
|
||||
done
|
||||
}
|
||||
# :command.inspect_args
|
||||
inspect_args() {
|
||||
readarray -t sorted_keys < <(printf '%s\n' "${!args[@]}" | sort)
|
||||
if (( ${#args[@]} )); then
|
||||
echo args:
|
||||
for k in "${sorted_keys[@]}"; do echo "- \${args[$k]} = ${args[$k]}"; done
|
||||
else
|
||||
echo args: none
|
||||
fi
|
||||
|
||||
if (( ${#other_args[@]} )); then
|
||||
echo
|
||||
echo other_args:
|
||||
echo "- \${other_args[*]} = ${other_args[*]}"
|
||||
for i in "${!other_args[@]}"; do
|
||||
echo "- \${other_args[$i]} = ${other_args[$i]}"
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
# :command.command_functions
|
||||
|
||||
# :command.parse_requirements
|
||||
parse_requirements() {
|
||||
# :command.fixed_flags_filter
|
||||
case "${1:-}" in
|
||||
--version | -v )
|
||||
version_command
|
||||
exit
|
||||
;;
|
||||
|
||||
--help | -h )
|
||||
long_usage=yes
|
||||
install_usage
|
||||
exit
|
||||
;;
|
||||
|
||||
esac
|
||||
|
||||
# :command.command_filter
|
||||
action="root"
|
||||
|
||||
# :command.parse_requirements_while
|
||||
while [[ $# -gt 0 ]]; do
|
||||
key="$1"
|
||||
case "$key" in
|
||||
# :flag.case
|
||||
--no-call | -n )
|
||||
|
||||
# :flag.case_no_arg
|
||||
args[--no-call]=1
|
||||
shift
|
||||
;;
|
||||
|
||||
# :flag.case
|
||||
--dry-run | -d )
|
||||
|
||||
# :flag.case_no_arg
|
||||
args[--dry-run]=1
|
||||
shift
|
||||
;;
|
||||
|
||||
-?* )
|
||||
printf "invalid option: %s\n" "$key" >&2
|
||||
exit 1
|
||||
;;
|
||||
|
||||
* )
|
||||
# :command.parse_requirements_case
|
||||
# :command.parse_requirements_case_simple
|
||||
if [[ -z ${args[source]+x} ]]; then
|
||||
|
||||
args[source]=$1
|
||||
shift
|
||||
elif [[ -z ${args[publisher]+x} ]]; then
|
||||
|
||||
args[publisher]=$1
|
||||
shift
|
||||
else
|
||||
printf "invalid argument: %s\n" "$key" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
;;
|
||||
|
||||
esac
|
||||
done
|
||||
|
||||
# :command.default_assignments
|
||||
[[ -n ${args[source]:-} ]] || args[source]="stable"
|
||||
[[ -n ${args[publisher]:-} ]] || args[publisher]="inventree"
|
||||
|
||||
# :command.whitelist_filter
|
||||
if [[ ! ${args[source]} =~ ^(stable|master|main)$ ]]; then
|
||||
printf "%s\n" "source must be one of: stable, master, main" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
# :command.initialize
|
||||
initialize() {
|
||||
version="2.0"
|
||||
long_usage=''
|
||||
set -e
|
||||
|
||||
# src/initialize.sh
|
||||
|
||||
}
|
||||
|
||||
# :command.run
|
||||
run() {
|
||||
declare -A args=()
|
||||
declare -a other_args=()
|
||||
declare -a input=()
|
||||
normalize_input "$@"
|
||||
parse_requirements "${input[@]}"
|
||||
|
||||
if [[ $action == "root" ]]; then
|
||||
root_command
|
||||
fi
|
||||
}
|
||||
|
||||
initialize
|
||||
run "$@"
|
32
contrib/installer/settings.yml
Normal file
32
contrib/installer/settings.yml
Normal file
@ -0,0 +1,32 @@
|
||||
# All settings are optional (with their default values provided below), and
|
||||
# can also be set with an environment variable with the same name, capitalized
|
||||
# and prefixed by `BASHLY_` - for example: BASHLY_SOURCE_DIR
|
||||
#
|
||||
# When setting environment variables, you can use:
|
||||
# - "0", "false" or "no" to represent false
|
||||
# - "1", "true" or "yes" to represent true
|
||||
|
||||
# The path containing the bashly configuration and source files
|
||||
source_dir: src
|
||||
|
||||
# The path to use for creating the bash script
|
||||
target_dir: ..
|
||||
|
||||
# The path to use for upgrading library files, relative to the source dir
|
||||
lib_dir: lib
|
||||
|
||||
# When true, enable bash strict mode (set -euo pipefail)
|
||||
strict: false
|
||||
|
||||
# When true, the generated script will use tab indentation instead of spaces
|
||||
# (every 2 leading spaces will be converted to a tab character)
|
||||
tab_indent: false
|
||||
|
||||
# When true, the generated script will consider any argument in the form of
|
||||
# `-abc` as if it is `-a -b -c`.
|
||||
compact_short_flags: true
|
||||
|
||||
# Set to 'production' or 'development':
|
||||
# - production generate a smaller script, without file markers
|
||||
# - development generate with file markers
|
||||
env: production
|
@ -1,4 +1,4 @@
|
||||
name: install
|
||||
name: install.sh
|
||||
help: Interactive installer for InvenTree
|
||||
version: 2.0
|
||||
|
||||
|
@ -45,25 +45,26 @@ echo "### Installer for InvenTree - source: $publisher/$source_url"
|
||||
# Check if os and version is supported
|
||||
get_distribution
|
||||
echo "### Detected distribution: $OS $VER"
|
||||
NOT_SUPPORTED=false
|
||||
SUPPORTED=true
|
||||
case "$OS" in
|
||||
Ubuntu)
|
||||
if [[ $VER != "20.04" ]]; then
|
||||
NOT_SUPPORTED=true
|
||||
SUPPORTED=false
|
||||
fi
|
||||
;;
|
||||
Debian | Raspbian)
|
||||
"Debian GNU/Linux" | Raspbian)
|
||||
if [[ $VER != "11" ]]; then
|
||||
NOT_SUPPORTED=true
|
||||
SUPPORTED=false
|
||||
fi
|
||||
OS=Debian
|
||||
;;
|
||||
*)
|
||||
echo "### Distribution not supported"
|
||||
NOT_SUPPORTED=true
|
||||
SUPPORTED=false
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ $NOT_SUPPORTED ]]; then
|
||||
if [[ $SUPPORTED != "true" ]]; then
|
||||
echo "This OS is currently not supported"
|
||||
echo "please install manually using https://inventree.readthedocs.io/en/stable/start/install/"
|
||||
echo "or check https://github.com/inventree/InvenTree/issues/3836 for packaging for your OS."
|
||||
@ -82,11 +83,10 @@ for pkg in $REQS; do
|
||||
fi
|
||||
done
|
||||
|
||||
echo "### Adding key and package source"
|
||||
# Add key
|
||||
do_call "wget -qO- https://dl.packager.io/srv/$publisher/InvenTree/key | sudo apt-key add -"
|
||||
# Add packagelist
|
||||
do_call "sudo wget -O /etc/apt/sources.list.d/inventree.list https://dl.packager.io/srv/$publisher/InvenTree/$source_url/installer/${lsb_dist}/${dist_version}.repo"
|
||||
echo "### Getting and adding key"
|
||||
wget -qO- https://dl.packager.io/srv/$publisher/InvenTree/key | sudo apt-key add -
|
||||
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"
|
||||
|
||||
echo "### Updateing package lists"
|
||||
do_call "sudo apt-get update"
|
||||
|
@ -84,7 +84,7 @@ function detect_envs() {
|
||||
|
||||
echo "# Setting base environment variables"
|
||||
|
||||
export INVENTREE_CONFIG_FILE=${CONF_DIR}/config.yaml
|
||||
export INVENTREE_CONFIG_FILE=${INVENTREE_CONFIG_FILE:-${CONF_DIR}/config.yaml}
|
||||
|
||||
if test -f "${INVENTREE_CONFIG_FILE}"; then
|
||||
echo "# Using existing config file: ${INVENTREE_CONFIG_FILE}"
|
||||
@ -93,22 +93,22 @@ function detect_envs() {
|
||||
pip install jc -q
|
||||
|
||||
# Load config
|
||||
local conf=$(cat ${INVENTREE_CONFIG_FILE} | jc --yaml)
|
||||
local CONF=$(cat ${INVENTREE_CONFIG_FILE} | jc --yaml)
|
||||
|
||||
# Parse the config file
|
||||
export INVENTREE_MEDIA_ROOT=$conf | jq '.[].media_root'
|
||||
export INVENTREE_STATIC_ROOT=$conf | jq '.[].static_root'
|
||||
export INVENTREE_BACKUP_DIR=$conf | jq '.[].backup_dir'
|
||||
export INVENTREE_PLUGINS_ENABLED=$conf | jq '.[].plugins_enabled'
|
||||
export INVENTREE_PLUGIN_FILE=$conf | jq '.[].plugin_file'
|
||||
export INVENTREE_SECRET_KEY_FILE=$conf | jq '.[].secret_key_file'
|
||||
export INVENTREE_MEDIA_ROOT=$(jq -r '.[].media_root' <<< ${CONF})
|
||||
export INVENTREE_STATIC_ROOT=$(jq -r '.[].static_root' <<< ${CONF})
|
||||
export INVENTREE_BACKUP_DIR=$(jq -r '.[].backup_dir' <<< ${CONF})
|
||||
export INVENTREE_PLUGINS_ENABLED=$(jq -r '.[].plugins_enabled' <<< ${CONF})
|
||||
export INVENTREE_PLUGIN_FILE=$(jq -r '.[].plugin_file' <<< ${CONF})
|
||||
export INVENTREE_SECRET_KEY_FILE=$(jq -r '.[].secret_key_file' <<< ${CONF})
|
||||
|
||||
export INVENTREE_DB_ENGINE=$conf | jq '.[].database.ENGINE'
|
||||
export INVENTREE_DB_NAME=$conf | jq '.[].database.NAME'
|
||||
export INVENTREE_DB_USER=$conf | jq '.[].database.USER'
|
||||
export INVENTREE_DB_PASSWORD=$conf | jq '.[].database.PASSWORD'
|
||||
export INVENTREE_DB_HOST=$conf | jq '.[].database.HOST'
|
||||
export INVENTREE_DB_PORT=$conf | jq '.[].database.PORT'
|
||||
export INVENTREE_DB_ENGINE=$(jq -r '.[].database.ENGINE' <<< ${CONF})
|
||||
export INVENTREE_DB_NAME=$(jq -r '.[].database.NAME' <<< ${CONF})
|
||||
export INVENTREE_DB_USER=$(jq -r '.[].database.USER' <<< ${CONF})
|
||||
export INVENTREE_DB_PASSWORD=$(jq -r '.[].database.PASSWORD' <<< ${CONF})
|
||||
export INVENTREE_DB_HOST=$(jq -r '.[].database.HOST' <<< ${CONF})
|
||||
export INVENTREE_DB_PORT=$(jq -r '.[].database.PORT' <<< ${CONF})
|
||||
else
|
||||
echo "# No config file found: ${INVENTREE_CONFIG_FILE}, using envs or defaults"
|
||||
|
||||
@ -160,7 +160,8 @@ function create_initscripts() {
|
||||
echo "# python enviroment already present - skipping"
|
||||
else
|
||||
echo "# Setting up python enviroment"
|
||||
sudo -u ${APP_USER} --preserve-env=$SETUP_ENVS bash -c "cd ${APP_HOME} && python3 -m venv env && pip install invoke"
|
||||
sudo -u ${APP_USER} --preserve-env=$SETUP_ENVS bash -c "cd ${APP_HOME} && ${SETUP_PYTHON} -m venv env"
|
||||
sudo -u ${APP_USER} --preserve-env=$SETUP_ENVS bash -c "cd ${APP_HOME} && env/bin/pip install invoke wheel"
|
||||
|
||||
if [ -n "${SETUP_EXTRA_PIP}" ]; then
|
||||
echo "# Installing extra pip packages"
|
||||
|
@ -11,7 +11,7 @@ PATH=${APP_HOME}/env/bin:${APP_HOME}/:/sbin:/bin:/usr/sbin:/usr/bin:
|
||||
. ${APP_HOME}/contrib/packager.io/functions.sh
|
||||
|
||||
# Envs that should be passed to setup commands
|
||||
export SETUP_ENVS=PATH,APP_HOME,INVENTREE_MEDIA_ROOT,INVENTREE_STATIC_ROOT,INVENTREE_PLUGINS_ENABLED,INVENTREE_PLUGIN_FILE,INVENTREE_CONFIG_FILE,INVENTREE_SECRET_KEY_FILE,INVENTREE_DB_ENGINE,INVENTREE_DB_NAME,INVENTREE_DB_USER,INVENTREE_DB_PASSWORD,INVENTREE_DB_HOST,INVENTREE_DB_PORT,INVENTREE_ADMIN_USER,INVENTREE_ADMIN_EMAIL,INVENTREE_ADMIN_PASSWORD,SETUP_NGINX_FILE,SETUP_ADMIN_PASSWORD_FILE,SETUP_NO_CALLS,SETUP_DEBUG,SETUP_EXTRA_PIP
|
||||
export SETUP_ENVS=PATH,APP_HOME,INVENTREE_MEDIA_ROOT,INVENTREE_STATIC_ROOT,INVENTREE_BACKUP_DIR,INVENTREE_PLUGINS_ENABLED,INVENTREE_PLUGIN_FILE,INVENTREE_CONFIG_FILE,INVENTREE_SECRET_KEY_FILE,INVENTREE_DB_ENGINE,INVENTREE_DB_NAME,INVENTREE_DB_USER,INVENTREE_DB_PASSWORD,INVENTREE_DB_HOST,INVENTREE_DB_PORT,INVENTREE_ADMIN_USER,INVENTREE_ADMIN_EMAIL,INVENTREE_ADMIN_PASSWORD,SETUP_NGINX_FILE,SETUP_ADMIN_PASSWORD_FILE,SETUP_NO_CALLS,SETUP_DEBUG,SETUP_EXTRA_PIP,SETUP_PYTHON
|
||||
|
||||
# Get the envs
|
||||
detect_local_env
|
||||
@ -23,8 +23,10 @@ export DATA_DIR=${APP_HOME}/data
|
||||
export SETUP_NGINX_FILE=${SETUP_NGINX_FILE:-/etc/nginx/sites-enabled/inventree.conf}
|
||||
export SETUP_ADMIN_PASSWORD_FILE=${CONF_DIR}/admin_password.txt
|
||||
export SETUP_NO_CALLS=${SETUP_NO_CALLS:-false}
|
||||
export SETUP_PYTHON=${SETUP_PYTHON:-python3}
|
||||
# SETUP_DEBUG can be set to get debug info
|
||||
# SETUP_EXTRA_PIP can be set to install extra pip packages
|
||||
# SETUP_PYTHON can be set to use a different python version
|
||||
|
||||
# get base info
|
||||
detect_envs
|
||||
|
Loading…
x
Reference in New Issue
Block a user