mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge branch 'master' of https://github.com/inventree/InvenTree into price-history
This commit is contained in:
commit
1da1753b23
@ -44,3 +44,8 @@ class InvenTreeConfig(AppConfig):
|
|||||||
schedule_type=Schedule.MINUTES,
|
schedule_type=Schedule.MINUTES,
|
||||||
minutes=15
|
minutes=15
|
||||||
)
|
)
|
||||||
|
|
||||||
|
InvenTree.tasks.schedule_task(
|
||||||
|
'InvenTree.tasks.update_exchange_rates',
|
||||||
|
schedule_type=Schedule.DAILY,
|
||||||
|
)
|
||||||
|
@ -87,6 +87,11 @@ DEBUG = _is_true(get_setting(
|
|||||||
CONFIG.get('debug', True)
|
CONFIG.get('debug', True)
|
||||||
))
|
))
|
||||||
|
|
||||||
|
DOCKER = _is_true(get_setting(
|
||||||
|
'INVENTREE_DOCKER',
|
||||||
|
False
|
||||||
|
))
|
||||||
|
|
||||||
# Configure logging settings
|
# Configure logging settings
|
||||||
log_level = get_setting(
|
log_level = get_setting(
|
||||||
'INVENTREE_LOG_LEVEL',
|
'INVENTREE_LOG_LEVEL',
|
||||||
@ -508,7 +513,8 @@ CURRENCIES = CONFIG.get(
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
# TODO - Allow live web-based backends in the future
|
BASE_CURRENCY = CONFIG.get('base_currency', 'USD')
|
||||||
|
|
||||||
EXCHANGE_BACKEND = 'InvenTree.exchange.InvenTreeManualExchangeBackend'
|
EXCHANGE_BACKEND = 'InvenTree.exchange.InvenTreeManualExchangeBackend'
|
||||||
|
|
||||||
# Extract email settings from the config file
|
# Extract email settings from the config file
|
||||||
|
@ -901,6 +901,15 @@ input[type="submit"] {
|
|||||||
color: #e00;
|
color: #e00;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.info-messages {
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-messages .alert {
|
||||||
|
padding: 5px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.part-allocation {
|
.part-allocation {
|
||||||
padding: 3px 10px;
|
padding: 3px 10px;
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
|
@ -12,7 +12,7 @@ function attachClipboard(selector, containerselector, textElement) {
|
|||||||
return document.getElementById(textElement).textContent;
|
return document.getElementById(textElement).textContent;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
text = function() {
|
text = function(trigger) {
|
||||||
var content = trigger.parentElement.parentElement.textContent;return content.trim();
|
var content = trigger.parentElement.parentElement.textContent;return content.trim();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -22,7 +22,6 @@ function attachClipboard(selector, containerselector, textElement) {
|
|||||||
text: text,
|
text: text,
|
||||||
container: containerselector
|
container: containerselector
|
||||||
});
|
});
|
||||||
console.log(cis);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -81,7 +80,6 @@ function inventreeDocReady() {
|
|||||||
attachClipboard('.clip-btn');
|
attachClipboard('.clip-btn');
|
||||||
attachClipboard('.clip-btn', 'modal-about'); // modals
|
attachClipboard('.clip-btn', 'modal-about'); // modals
|
||||||
attachClipboard('.clip-btn-version', 'modal-about', 'about-copy-text'); // version-text
|
attachClipboard('.clip-btn-version', 'modal-about', 'about-copy-text'); // version-text
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function isFileTransfer(transfer) {
|
function isFileTransfer(transfer) {
|
||||||
|
@ -161,6 +161,34 @@ def check_for_updates():
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def update_exchange_rates():
|
||||||
|
"""
|
||||||
|
If an API key for fixer.io has been provided, attempt to update currency exchange rates
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
import common.models
|
||||||
|
from django.conf import settings
|
||||||
|
from djmoney.contrib.exchange.backends import FixerBackend
|
||||||
|
except AppRegistryNotReady:
|
||||||
|
# Apps not yet loaded!
|
||||||
|
return
|
||||||
|
|
||||||
|
fixer_api_key = common.models.InvenTreeSetting.get_setting('INVENTREE_FIXER_API_KEY', '').strip()
|
||||||
|
|
||||||
|
if not fixer_api_key:
|
||||||
|
# API key not provided
|
||||||
|
return
|
||||||
|
|
||||||
|
backend = FixerBackend(access_key=fixer_api_key)
|
||||||
|
|
||||||
|
currencies = ','.join(settings.CURRENCIES)
|
||||||
|
|
||||||
|
base = settings.BASE_CURRENCY
|
||||||
|
|
||||||
|
backend.update_rates(base_currency=base, symbols=currencies)
|
||||||
|
|
||||||
|
|
||||||
def send_email(subject, body, recipients, from_email=None):
|
def send_email(subject, body, recipients, from_email=None):
|
||||||
"""
|
"""
|
||||||
Send an email with the specified subject and body,
|
Send an email with the specified subject and body,
|
||||||
|
@ -10,8 +10,15 @@ import common.models
|
|||||||
|
|
||||||
INVENTREE_SW_VERSION = "0.2.2 pre"
|
INVENTREE_SW_VERSION = "0.2.2 pre"
|
||||||
|
|
||||||
# Increment this number whenever there is a significant change to the API that any clients need to know about
|
"""
|
||||||
INVENTREE_API_VERSION = 2
|
Increment thi API version number whenever there is a significant change to the API that any clients need to know about
|
||||||
|
|
||||||
|
v3 -> 2021-05-22:
|
||||||
|
- The updated StockItem "history tracking" now uses a different interface
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
INVENTREE_API_VERSION = 3
|
||||||
|
|
||||||
|
|
||||||
def inventreeInstanceName():
|
def inventreeInstanceName():
|
||||||
|
@ -9,22 +9,23 @@
|
|||||||
{% inventree_title %} | {% trans "Build Order" %} - {{ build }}
|
{% inventree_title %} | {% trans "Build Order" %} - {{ build }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block header_pre_content %}
|
{% block below_thumbnail %}
|
||||||
|
|
||||||
|
<div class='info-messages'>
|
||||||
{% if build.sales_order %}
|
{% if build.sales_order %}
|
||||||
<div class='alert alert-block alert-info'>
|
<div class='alert alert-block alert-info'>
|
||||||
{% object_link 'so-detail' build.sales_order.id build.sales_order as link %}
|
{% object_link 'so-detail' build.sales_order.id build.sales_order as link %}
|
||||||
{% blocktrans %}This Build Order is allocated to Sales Order {{link}}{% endblocktrans %}
|
{% blocktrans %}This Build Order is allocated to Sales Order {{link}}{% endblocktrans %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if build.parent %}
|
{% if build.parent %}
|
||||||
<div class='alert alert-block alert-info'>
|
<div class='alert alert-block alert-info'>
|
||||||
{% object_link 'build-detail' build.parent.id build.parent as link %}
|
{% object_link 'build-detail' build.parent.id build.parent as link %}
|
||||||
{% blocktrans %}This Build Order is a child of Build Order {{link}}{% endblocktrans %}
|
{% blocktrans %}This Build Order is a child of Build Order {{link}}{% endblocktrans %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block header_post_content %}
|
|
||||||
{% if build.active %}
|
{% if build.active %}
|
||||||
{% if build.can_complete %}
|
{% if build.can_complete %}
|
||||||
<div class='alert alert-block alert-success'>
|
<div class='alert alert-block alert-success'>
|
||||||
@ -47,6 +48,15 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block header_pre_content %}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block header_post_content %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block thumbnail %}
|
{% block thumbnail %}
|
||||||
|
@ -87,6 +87,12 @@ class InvenTreeSetting(models.Model):
|
|||||||
'choices': djmoney.settings.CURRENCY_CHOICES,
|
'choices': djmoney.settings.CURRENCY_CHOICES,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'INVENTREE_FIXER_API_KEY': {
|
||||||
|
'name': _('fixer.io API key'),
|
||||||
|
'description': _('API key for fixer.io currency conversion service'),
|
||||||
|
'default': '',
|
||||||
|
},
|
||||||
|
|
||||||
'INVENTREE_DOWNLOAD_FROM_URL': {
|
'INVENTREE_DOWNLOAD_FROM_URL': {
|
||||||
'name': _('Download from URL'),
|
'name': _('Download from URL'),
|
||||||
'description': _('Allow download of remote images and files from external URL'),
|
'description': _('Allow download of remote images and files from external URL'),
|
||||||
|
@ -449,8 +449,8 @@ class FileManagementFormView(MultiStepFormView):
|
|||||||
|
|
||||||
if guess:
|
if guess:
|
||||||
n = list(self.column_selections.values()).count(self.column_selections[col])
|
n = list(self.column_selections.values()).count(self.column_selections[col])
|
||||||
if n > 1:
|
if n > 1 and self.column_selections[col] not in duplicates:
|
||||||
duplicates.append(col)
|
duplicates.append(self.column_selections[col])
|
||||||
|
|
||||||
# Store extra context data
|
# Store extra context data
|
||||||
self.extra_context_data = {
|
self.extra_context_data = {
|
||||||
|
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
@ -13,6 +13,10 @@ from .models import SalesOrder, SalesOrderLineItem
|
|||||||
from .models import SalesOrderAllocation
|
from .models import SalesOrderAllocation
|
||||||
|
|
||||||
|
|
||||||
|
class PurchaseOrderLineItemInlineAdmin(admin.StackedInline):
|
||||||
|
model = PurchaseOrderLineItem
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderAdmin(ImportExportModelAdmin):
|
class PurchaseOrderAdmin(ImportExportModelAdmin):
|
||||||
|
|
||||||
list_display = (
|
list_display = (
|
||||||
@ -29,6 +33,10 @@ class PurchaseOrderAdmin(ImportExportModelAdmin):
|
|||||||
'description',
|
'description',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
inlines = [
|
||||||
|
PurchaseOrderLineItemInlineAdmin
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderAdmin(ImportExportModelAdmin):
|
class SalesOrderAdmin(ImportExportModelAdmin):
|
||||||
|
|
||||||
|
@ -826,6 +826,9 @@ class SalesOrderAllocation(models.Model):
|
|||||||
else:
|
else:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
def get_po(self):
|
||||||
|
return self.item.purchase_order
|
||||||
|
|
||||||
def complete_allocation(self, user):
|
def complete_allocation(self, user):
|
||||||
"""
|
"""
|
||||||
Complete this allocation (called when the parent SalesOrder is marked as "shipped"):
|
Complete this allocation (called when the parent SalesOrder is marked as "shipped"):
|
||||||
|
@ -235,6 +235,7 @@ class SalesOrderAllocationSerializer(InvenTreeModelSerializer):
|
|||||||
location_path = serializers.CharField(source='get_location_path')
|
location_path = serializers.CharField(source='get_location_path')
|
||||||
location_id = serializers.IntegerField(source='get_location')
|
location_id = serializers.IntegerField(source='get_location')
|
||||||
serial = serializers.CharField(source='get_serial')
|
serial = serializers.CharField(source='get_serial')
|
||||||
|
po = serializers.CharField(source='get_po')
|
||||||
quantity = serializers.FloatField()
|
quantity = serializers.FloatField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -247,6 +248,7 @@ class SalesOrderAllocationSerializer(InvenTreeModelSerializer):
|
|||||||
'quantity',
|
'quantity',
|
||||||
'location_id',
|
'location_id',
|
||||||
'location_path',
|
'location_path',
|
||||||
|
'po',
|
||||||
'item',
|
'item',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@
|
|||||||
<td>
|
<td>
|
||||||
{{ col }}
|
{{ col }}
|
||||||
{% for duplicate in duplicates %}
|
{% for duplicate in duplicates %}
|
||||||
{% if duplicate == col.name %}
|
{% if duplicate == col.value %}
|
||||||
<div class='alert alert-danger alert-block text-center' role='alert' style='padding:2px; margin-top:6px; margin-bottom:2px'>
|
<div class='alert alert-danger alert-block text-center' role='alert' style='padding:2px; margin-top:6px; margin-bottom:2px'>
|
||||||
<b>{% trans "Duplicate selection" %}</b>
|
<b>{% trans "Duplicate selection" %}</b>
|
||||||
</div>
|
</div>
|
||||||
|
@ -9,12 +9,14 @@
|
|||||||
{% inventree_title %} | {% trans "Sales Order" %}
|
{% inventree_title %} | {% trans "Sales Order" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block pre_content %}
|
{% block below_thumbnail %}
|
||||||
|
<div class='info-messages'>
|
||||||
{% if order.status == SalesOrderStatus.PENDING and not order.is_fully_allocated %}
|
{% if order.status == SalesOrderStatus.PENDING and not order.is_fully_allocated %}
|
||||||
<div class='alert alert-block alert-danger'>
|
<div class='alert alert-block alert-danger'>
|
||||||
{% trans "This Sales Order has not been fully allocated" %}
|
{% trans "This Sales Order has not been fully allocated" %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block thumbnail %}
|
{% block thumbnail %}
|
||||||
|
@ -87,6 +87,9 @@ function showAllocationSubTable(index, row, element) {
|
|||||||
return renderLink(row.location_path, `/stock/location/${row.location_id}/`);
|
return renderLink(row.location_path, `/stock/location/${row.location_id}/`);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
field: 'po'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
field: 'buttons',
|
field: 'buttons',
|
||||||
title: '{% trans "Actions" %}',
|
title: '{% trans "Actions" %}',
|
||||||
@ -161,7 +164,10 @@ function showFulfilledSubTable(index, row, element) {
|
|||||||
|
|
||||||
return renderLink(text, `/stock/item/${row.pk}/`);
|
return renderLink(text, `/stock/item/${row.pk}/`);
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
field: 'po'
|
||||||
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -271,6 +277,25 @@ $("#so-lines-table").inventreeTable({
|
|||||||
field: 'notes',
|
field: 'notes',
|
||||||
title: '{% trans "Notes" %}',
|
title: '{% trans "Notes" %}',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
field: 'po',
|
||||||
|
title: '{% trans "PO" %}',
|
||||||
|
formatter: function(value, row, index, field) {
|
||||||
|
var po_name = "";
|
||||||
|
if (row.allocated) {
|
||||||
|
row.allocations.forEach(function(allocation) {
|
||||||
|
if (allocation.po != po_name) {
|
||||||
|
if (po_name) {
|
||||||
|
po_name = "-";
|
||||||
|
} else {
|
||||||
|
po_name = allocation.po
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return `<div>` + po_name + `</div>`;
|
||||||
|
}
|
||||||
|
},
|
||||||
{% if order.status == SalesOrderStatus.PENDING %}
|
{% if order.status == SalesOrderStatus.PENDING %}
|
||||||
{
|
{
|
||||||
field: 'buttons',
|
field: 'buttons',
|
||||||
|
@ -90,7 +90,7 @@ class SalesOrderDetail(InvenTreeRoleMixin, DetailView):
|
|||||||
""" Detail view for a SalesOrder object """
|
""" Detail view for a SalesOrder object """
|
||||||
|
|
||||||
context_object_name = 'order'
|
context_object_name = 'order'
|
||||||
queryset = SalesOrder.objects.all().prefetch_related('lines')
|
queryset = SalesOrder.objects.all().prefetch_related('lines__allocations__item__purchase_order')
|
||||||
template_name = 'order/sales_order_detail.html'
|
template_name = 'order/sales_order_detail.html'
|
||||||
|
|
||||||
|
|
||||||
|
@ -12,13 +12,6 @@
|
|||||||
|
|
||||||
<div class='panel panel-default panel-inventree'>
|
<div class='panel panel-default panel-inventree'>
|
||||||
|
|
||||||
{% if part.variant_of %}
|
|
||||||
<div class='alert alert-info alert-block'>
|
|
||||||
{% object_link 'part-variants' part.variant_of.id part.variant_of.full_name as link %}
|
|
||||||
{% blocktrans %}This part is a variant of {{link}}{% endblocktrans %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
{% include "part/part_thumb.html" %}
|
{% include "part/part_thumb.html" %}
|
||||||
@ -107,6 +100,15 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class='info-messages'>
|
||||||
|
{% if part.variant_of %}
|
||||||
|
<div class='alert alert-info alert-block'>
|
||||||
|
{% object_link 'part-variants' part.variant_of.id part.variant_of.full_name as link %}
|
||||||
|
{% blocktrans %}This part is a variant of {{link}}{% endblocktrans %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
|
@ -1,8 +1,14 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
""" This module provides template tags for extra functionality
|
""" This module provides template tags for extra functionality
|
||||||
over and above the built-in Django tags.
|
over and above the built-in Django tags.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from django.conf import settings as djangosettings
|
||||||
|
|
||||||
from django import template
|
from django import template
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
@ -68,6 +74,33 @@ def part_allocation_count(build, part, *args, **kwargs):
|
|||||||
return InvenTree.helpers.decimal2string(build.getAllocatedQuantity(part))
|
return InvenTree.helpers.decimal2string(build.getAllocatedQuantity(part))
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_tag()
|
||||||
|
def inventree_in_debug_mode(*args, **kwargs):
|
||||||
|
""" Return True if the server is running in DEBUG mode """
|
||||||
|
|
||||||
|
return djangosettings.DEBUG
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_tag()
|
||||||
|
def inventree_docker_mode(*args, **kwargs):
|
||||||
|
""" Return True if the server is running as a Docker image """
|
||||||
|
|
||||||
|
return djangosettings.DOCKER
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_tag()
|
||||||
|
def inventree_db_engine(*args, **kwargs):
|
||||||
|
""" Return the InvenTree database backend e.g. 'postgresql' """
|
||||||
|
|
||||||
|
db = djangosettings.DATABASES['default']
|
||||||
|
|
||||||
|
engine = db.get('ENGINE', _('Unknown database'))
|
||||||
|
|
||||||
|
engine = engine.replace('django.db.backends.', '')
|
||||||
|
|
||||||
|
return engine
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag()
|
@register.simple_tag()
|
||||||
def inventree_instance_name(*args, **kwargs):
|
def inventree_instance_name(*args, **kwargs):
|
||||||
""" Return the InstanceName associated with the current database """
|
""" Return the InstanceName associated with the current database """
|
||||||
@ -86,6 +119,12 @@ def inventree_version(*args, **kwargs):
|
|||||||
return version.inventreeVersion()
|
return version.inventreeVersion()
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_tag()
|
||||||
|
def inventree_api_version(*args, **kwargs):
|
||||||
|
""" Return InvenTree API version """
|
||||||
|
return version.inventreeApiVersion()
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag()
|
@register.simple_tag()
|
||||||
def django_version(*args, **kwargs):
|
def django_version(*args, **kwargs):
|
||||||
""" Return Django version string """
|
""" Return Django version string """
|
||||||
|
@ -1018,6 +1018,9 @@ class StockTrackingList(generics.ListAPIView):
|
|||||||
for item in data:
|
for item in data:
|
||||||
deltas = item['deltas']
|
deltas = item['deltas']
|
||||||
|
|
||||||
|
if not deltas:
|
||||||
|
deltas = {}
|
||||||
|
|
||||||
# Add location detail
|
# Add location detail
|
||||||
if 'location' in deltas:
|
if 'location' in deltas:
|
||||||
try:
|
try:
|
||||||
|
@ -1370,6 +1370,12 @@ class StockItem(MPTTModel):
|
|||||||
if self.location:
|
if self.location:
|
||||||
s += ' @ {loc}'.format(loc=self.location.name)
|
s += ' @ {loc}'.format(loc=self.location.name)
|
||||||
|
|
||||||
|
if self.purchase_order:
|
||||||
|
s += " ({pre}{po})".format(
|
||||||
|
pre=helpers.getSetting("PURCHASEORDER_REFERENCE_PREFIX"),
|
||||||
|
po=self.purchase_order,
|
||||||
|
)
|
||||||
|
|
||||||
return s
|
return s
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
|
@ -14,68 +14,6 @@
|
|||||||
|
|
||||||
{% block pre_content %}
|
{% block pre_content %}
|
||||||
{% include 'stock/loc_link.html' with location=item.location %}
|
{% include 'stock/loc_link.html' with location=item.location %}
|
||||||
|
|
||||||
{% setting_object 'STOCK_OWNERSHIP_CONTROL' as owner_control %}
|
|
||||||
{% if owner_control.value == "True" %}
|
|
||||||
{% authorized_owners item.owner as owners %}
|
|
||||||
|
|
||||||
{% if not user in owners and not user.is_superuser %}
|
|
||||||
<div class='alert alert-block alert-info'>
|
|
||||||
{% trans "You are not in the list of owners of this item. This stock item cannot be edited." %}<br>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if item.is_building %}
|
|
||||||
<div class='alert alert-block alert-info'>
|
|
||||||
{% trans "This stock item is in production and cannot be edited." %}<br>
|
|
||||||
{% trans "Edit the stock item from the build view." %}<br>
|
|
||||||
|
|
||||||
{% if item.build %}
|
|
||||||
<a href="{% url 'build-detail' item.build.id %}">
|
|
||||||
<b>{{ item.build }}</b>
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if item.hasRequiredTests and not item.passedAllRequiredTests %}
|
|
||||||
<div class='alert alert-block alert-danger'>
|
|
||||||
{% trans "This stock item has not passed all required tests" %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% for allocation in item.sales_order_allocations.all %}
|
|
||||||
<div class='alert alert-block alert-info'>
|
|
||||||
{% object_link 'so-detail' allocation.line.order.id allocation.line.order as link %}
|
|
||||||
{% decimal allocation.quantity as qty %}
|
|
||||||
{% blocktrans %}This stock item is allocated to Sales Order {{ link }} (Quantity: {{ qty }}){% endblocktrans %}
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
{% for allocation in item.allocations.all %}
|
|
||||||
<div class='alert alert-block alert-info'>
|
|
||||||
{% object_link 'build-detail' allocation.build.id allocation.build %}
|
|
||||||
{% decimal allocation.quantity as qty %}
|
|
||||||
{% blocktrans %}This stock item is allocated to Build {{ link }} (Quantity: {{ qty }}){% endblocktrans %}
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
{% if item.serialized %}
|
|
||||||
<div class='alert alert-block alert-warning'>
|
|
||||||
{% trans "This stock item is serialized - it has a unique serial number and the quantity cannot be adjusted." %}
|
|
||||||
</div>
|
|
||||||
{% elif item.child_count > 0 %}
|
|
||||||
<div class='alert alert-block alert-warning'>
|
|
||||||
{% trans "This stock item cannot be deleted as it has child items" %}
|
|
||||||
</div>
|
|
||||||
{% elif item.delete_on_deplete and item.can_delete %}
|
|
||||||
<div class='alert alert-block alert-warning'>
|
|
||||||
{% trans "This stock item will be automatically deleted when all stock is depleted." %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block thumbnail %}
|
{% block thumbnail %}
|
||||||
@ -221,6 +159,73 @@
|
|||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block below_thumbnail %}
|
||||||
|
<div class='info-messages'>
|
||||||
|
|
||||||
|
{% setting_object 'STOCK_OWNERSHIP_CONTROL' as owner_control %}
|
||||||
|
{% if owner_control.value == "True" %}
|
||||||
|
{% authorized_owners item.owner as owners %}
|
||||||
|
|
||||||
|
{% if not user in owners and not user.is_superuser %}
|
||||||
|
<div class='alert alert-block alert-info'>
|
||||||
|
{% trans "You are not in the list of owners of this item. This stock item cannot be edited." %}<br>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if item.is_building %}
|
||||||
|
<div class='alert alert-block alert-info'>
|
||||||
|
{% trans "This stock item is in production and cannot be edited." %}<br>
|
||||||
|
{% trans "Edit the stock item from the build view." %}<br>
|
||||||
|
|
||||||
|
{% if item.build %}
|
||||||
|
<a href="{% url 'build-detail' item.build.id %}">
|
||||||
|
<b>{{ item.build }}</b>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if item.hasRequiredTests and not item.passedAllRequiredTests %}
|
||||||
|
<div class='alert alert-block alert-danger'>
|
||||||
|
{% trans "This stock item has not passed all required tests" %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% for allocation in item.sales_order_allocations.all %}
|
||||||
|
<div class='alert alert-block alert-info'>
|
||||||
|
{% object_link 'so-detail' allocation.line.order.id allocation.line.order as link %}
|
||||||
|
{% decimal allocation.quantity as qty %}
|
||||||
|
{% blocktrans %}This stock item is allocated to Sales Order {{ link }} (Quantity: {{ qty }}){% endblocktrans %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% for allocation in item.allocations.all %}
|
||||||
|
<div class='alert alert-block alert-info'>
|
||||||
|
{% object_link 'build-detail' allocation.build.id allocation.build %}
|
||||||
|
{% decimal allocation.quantity as qty %}
|
||||||
|
{% blocktrans %}This stock item is allocated to Build {{ link }} (Quantity: {{ qty }}){% endblocktrans %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% if item.serialized %}
|
||||||
|
<div class='alert alert-block alert-warning'>
|
||||||
|
{% trans "This stock item is serialized - it has a unique serial number and the quantity cannot be adjusted." %}
|
||||||
|
</div>
|
||||||
|
{% elif item.child_count > 0 %}
|
||||||
|
<div class='alert alert-block alert-warning'>
|
||||||
|
{% trans "This stock item cannot be deleted as it has child items" %}
|
||||||
|
</div>
|
||||||
|
{% elif item.delete_on_deplete and item.can_delete %}
|
||||||
|
<div class='alert alert-block alert-warning'>
|
||||||
|
{% trans "This stock item will be automatically deleted when all stock is depleted." %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block page_details %}
|
{% block page_details %}
|
||||||
<h4>{% trans "Stock Item Details" %}</h4>
|
<h4>{% trans "Stock Item Details" %}</h4>
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
{% include "InvenTree/settings/setting.html" with key="INVENTREE_BASE_URL" icon="fa-globe" %}
|
{% include "InvenTree/settings/setting.html" with key="INVENTREE_BASE_URL" icon="fa-globe" %}
|
||||||
{% include "InvenTree/settings/setting.html" with key="INVENTREE_COMPANY_NAME" icon="fa-building" %}
|
{% include "InvenTree/settings/setting.html" with key="INVENTREE_COMPANY_NAME" icon="fa-building" %}
|
||||||
{% include "InvenTree/settings/setting.html" with key="INVENTREE_DEFAULT_CURRENCY" icon="fa-dollar-sign" %}
|
{% include "InvenTree/settings/setting.html" with key="INVENTREE_DEFAULT_CURRENCY" icon="fa-dollar-sign" %}
|
||||||
|
{% include "InvenTree/settings/setting.html" with key="INVENTREE_FIXER_API_KEY" icon="fa-key" %}
|
||||||
{% include "InvenTree/settings/setting.html" with key="INVENTREE_DOWNLOAD_FROM_URL" icon="fa-cloud-download-alt" %}
|
{% include "InvenTree/settings/setting.html" with key="INVENTREE_DOWNLOAD_FROM_URL" icon="fa-cloud-download-alt" %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
@ -29,6 +29,11 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><span class='fas fa-code'></span></td>
|
||||||
|
<td>{% trans "API Version" %}</td>
|
||||||
|
<td>{% inventree_api_version %}{% include "clip.html" %}</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><span class='fas fa-hashtag'></span></td>
|
<td><span class='fas fa-hashtag'></span></td>
|
||||||
<td>{% trans "Django Version" %}</td>
|
<td>{% trans "Django Version" %}</td>
|
||||||
|
@ -89,6 +89,15 @@ function setFieldOptions(fieldName, optionList, options={}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function clearFieldOptions(fieldName) {
|
||||||
|
/**
|
||||||
|
* Clear (emtpy) the options list for a particular field
|
||||||
|
*/
|
||||||
|
|
||||||
|
setFieldOptions(fieldName, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function reloadFieldOptions(fieldName, options) {
|
function reloadFieldOptions(fieldName, options) {
|
||||||
/* Reload the options for a given field,
|
/* Reload the options for a given field,
|
||||||
* using an AJAX request.
|
* using an AJAX request.
|
||||||
|
@ -1017,6 +1017,11 @@ function loadStockTrackingTable(table, options) {
|
|||||||
formatter: function(details, row, index, field) {
|
formatter: function(details, row, index, field) {
|
||||||
var html = `<table class='table table-condensed' id='tracking-table-${row.pk}'>`;
|
var html = `<table class='table table-condensed' id='tracking-table-${row.pk}'>`;
|
||||||
|
|
||||||
|
if (!details) {
|
||||||
|
html += '</table>';
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
// Location information
|
// Location information
|
||||||
if (details.location) {
|
if (details.location) {
|
||||||
|
|
||||||
@ -1218,6 +1223,17 @@ function createNewStockItem(options) {
|
|||||||
field: 'part',
|
field: 'part',
|
||||||
action: function(value) {
|
action: function(value) {
|
||||||
|
|
||||||
|
if (!value) {
|
||||||
|
// No part chosen
|
||||||
|
|
||||||
|
clearFieldOptions('supplier_part');
|
||||||
|
enableField('serial_numbers', false);
|
||||||
|
enableField('purchase_price_0', false);
|
||||||
|
enableField('purchase_price_1', false);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Reload options for supplier part
|
// Reload options for supplier part
|
||||||
reloadFieldOptions(
|
reloadFieldOptions(
|
||||||
'supplier_part',
|
'supplier_part',
|
||||||
@ -1243,6 +1259,9 @@ function createNewStockItem(options) {
|
|||||||
enableField('serial_numbers', response.trackable);
|
enableField('serial_numbers', response.trackable);
|
||||||
clearField('serial_numbers');
|
clearField('serial_numbers');
|
||||||
|
|
||||||
|
enableField('purchase_price_0', response.purchaseable);
|
||||||
|
enableField('purchase_price_1', response.purchaseable);
|
||||||
|
|
||||||
// Populate the expiry date
|
// Populate the expiry date
|
||||||
if (response.default_expiry <= 0) {
|
if (response.default_expiry <= 0) {
|
||||||
// No expiry date
|
// No expiry date
|
||||||
|
@ -13,6 +13,27 @@
|
|||||||
<td>{% trans "Instance Name" %}</td>
|
<td>{% trans "Instance Name" %}</td>
|
||||||
<td>{% inventree_instance_name %}</td>
|
<td>{% inventree_instance_name %}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><span class='fas fa-database'></span></td>
|
||||||
|
<td>{% trans "Database" %}</td>
|
||||||
|
<td>{% inventree_db_engine %}</td>
|
||||||
|
</tr>
|
||||||
|
{% inventree_in_debug_mode as debug_mode %}
|
||||||
|
{% if debug_mode %}
|
||||||
|
<tr>
|
||||||
|
<td><span class='fas fa-laptop-code'></span></td>
|
||||||
|
<td>{% trans "Debug Mode" %}</td>
|
||||||
|
<td>{% trans "Server is running in debug mode" %}</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% inventree_docker_mode as docker_mode %}
|
||||||
|
{% if docker_mode %}
|
||||||
|
<tr>
|
||||||
|
<td><span class='fab fa-docker'></span></td>
|
||||||
|
<td>{% trans "Docker Mode" %}</td>
|
||||||
|
<td>{% trans "Server is deployed using docker" %}</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
{% if user.is_staff %}
|
{% if user.is_staff %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><span class='fas fa-server'></span></td>
|
<td><span class='fas fa-server'></span></td>
|
||||||
|
@ -27,6 +27,8 @@
|
|||||||
<!-- Data next to image goes here -->
|
<!-- Data next to image goes here -->
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
|
{% block below_thumbnail %}
|
||||||
|
{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
<div class='col-sm-6'>
|
<div class='col-sm-6'>
|
||||||
{% block page_details %}
|
{% block page_details %}
|
||||||
|
@ -3,3 +3,6 @@ InvenTree-Version: {% inventree_version %}
|
|||||||
Django Version: {% django_version %}
|
Django Version: {% django_version %}
|
||||||
{% inventree_commit_hash as hash %}{% if hash %}Commit Hash: {{ hash }}{% endif %}
|
{% inventree_commit_hash as hash %}{% if hash %}Commit Hash: {{ hash }}{% endif %}
|
||||||
{% inventree_commit_date as commit_date %}{% if commit_date %}Commit Date: {{ commit_date }}{% endif %}
|
{% inventree_commit_date as commit_date %}{% if commit_date %}Commit Date: {{ commit_date }}{% endif %}
|
||||||
|
Database: {% inventree_db_engine %}
|
||||||
|
Debug-Mode: {% inventree_in_debug_mode %}
|
||||||
|
Deployed using Docker: {% inventree_docker_mode %}
|
@ -1,3 +1,6 @@
|
|||||||
|
"commit_message": "Fix: New translations %original_file_name% from Crowdin"
|
||||||
|
"append_commit_message": false
|
||||||
|
|
||||||
files:
|
files:
|
||||||
- source: /InvenTree/locale/en/LC_MESSAGES/django.po
|
- source: /InvenTree/locale/en/LC_MESSAGES/django.po
|
||||||
translation: /InvenTree/locale/%two_letters_code%/LC_MESSAGES/%original_file_name%
|
translation: /InvenTree/locale/%two_letters_code%/LC_MESSAGES/%original_file_name%
|
||||||
|
@ -14,6 +14,7 @@ ENV INVENTREE_REPO="${repository}"
|
|||||||
ENV INVENTREE_BRANCH="${branch}"
|
ENV INVENTREE_BRANCH="${branch}"
|
||||||
|
|
||||||
ENV INVENTREE_LOG_LEVEL="INFO"
|
ENV INVENTREE_LOG_LEVEL="INFO"
|
||||||
|
ENV INVENTREE_DOCKER="true"
|
||||||
|
|
||||||
# InvenTree paths
|
# InvenTree paths
|
||||||
ENV INVENTREE_SRC_DIR="${INVENTREE_HOME}/src"
|
ENV INVENTREE_SRC_DIR="${INVENTREE_HOME}/src"
|
||||||
|
Loading…
Reference in New Issue
Block a user