Merge remote-tracking branch 'inventree/master'

This commit is contained in:
Oliver Walters 2020-03-19 10:29:26 +11:00
commit 02ec1d4fa2
12 changed files with 132 additions and 22 deletions

View File

@ -8,6 +8,8 @@ from .validators import allowable_url_schemes
from django.forms.fields import URLField as FormURLField from django.forms.fields import URLField as FormURLField
from django.db import models as models from django.db import models as models
from django.core import validators from django.core import validators
from django import forms
from decimal import Decimal
class InvenTreeURLFormField(FormURLField): class InvenTreeURLFormField(FormURLField):
@ -25,3 +27,33 @@ class InvenTreeURLField(models.URLField):
return super().formfield(**{ return super().formfield(**{
'form_class': InvenTreeURLFormField 'form_class': InvenTreeURLFormField
}) })
def round_decimal(value, places):
"""
Round value to the specified number of places.
"""
if value is not None:
# see https://docs.python.org/2/library/decimal.html#decimal.Decimal.quantize for options
return value.quantize(Decimal(10) ** -places)
return value
class RoundingDecimalFormField(forms.DecimalField):
def to_python(self, value):
value = super(RoundingDecimalFormField, self).to_python(value)
return round_decimal(value, self.decimal_places)
class RoundingDecimalField(models.DecimalField):
def to_python(self, value):
value = super(RoundingDecimalField, self).to_python(value)
return round_decimal(value, self.decimal_places)
def formfield(self, **kwargs):
defaults = {
'form_class': RoundingDecimalFormField
}
defaults.update(kwargs)
return super(RoundingDecimalField, self).formfield(**kwargs)

View File

@ -71,7 +71,7 @@ function loadAllocationTable(table, part_id, part, url, required, button) {
var count = 0; var count = 0;
for (var i = 0; i < results.length; i++) { for (var i = 0; i < results.length; i++) {
count += results[i].quantity; count += parseFloat(results[i].quantity);
} }
updateAllocationTotal(part_id, count, required); updateAllocationTotal(part_id, count, required);

View File

@ -18,7 +18,7 @@
<b>{% decimal item.sub_part.total_stock %}</b> <b>{% decimal item.sub_part.total_stock %}</b>
</div> </div>
<div class='col-sm-2'> <div class='col-sm-2'>
<b>{% multiply build.quantity item.quantity %}</b> <b>{% multiply build.quantity item.quantity %}{% if item.overage %} (+ {{ item.overage }}){% endif %}</b>
</div> </div>
<div class='col-sm-2'> <div class='col-sm-2'>
<b><span id='allocation-total-{{ item.sub_part.id }}'>{% part_allocation_count build item.sub_part %}</span></b> <b><span id='allocation-total-{{ item.sub_part.id }}'>{% part_allocation_count build item.sub_part %}</span></b>

View File

@ -6,6 +6,7 @@ Django Forms for interacting with Company app
from __future__ import unicode_literals from __future__ import unicode_literals
from InvenTree.forms import HelperForm from InvenTree.forms import HelperForm
from InvenTree.fields import RoundingDecimalFormField
from .models import Company from .models import Company
from .models import SupplierPart from .models import SupplierPart
@ -64,6 +65,10 @@ class EditSupplierPartForm(HelperForm):
class EditPriceBreakForm(HelperForm): class EditPriceBreakForm(HelperForm):
""" Form for creating / editing a supplier price break """ """ Form for creating / editing a supplier price break """
quantity = RoundingDecimalFormField(max_digits=10, decimal_places=5)
cost = RoundingDecimalFormField(max_digits=10, decimal_places=5)
class Meta: class Meta:
model = SupplierPriceBreak model = SupplierPriceBreak
fields = [ fields = [

View File

@ -0,0 +1,20 @@
# Generated by Django 2.2.9 on 2020-03-18 11:14
import InvenTree.fields
import django.core.validators
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('company', '0010_auto_20200201_1231'),
]
operations = [
migrations.AlterField(
model_name='supplierpricebreak',
name='cost',
field=InvenTree.fields.RoundingDecimalField(decimal_places=5, max_digits=10, validators=[django.core.validators.MinValueValidator(0)]),
),
]

View File

@ -0,0 +1,20 @@
# Generated by Django 2.2.9 on 2020-03-18 11:14
import InvenTree.fields
import django.core.validators
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('company', '0011_auto_20200318_1114'),
]
operations = [
migrations.AlterField(
model_name='supplierpricebreak',
name='quantity',
field=InvenTree.fields.RoundingDecimalField(decimal_places=5, default=1, max_digits=15, validators=[django.core.validators.MinValueValidator(1)]),
),
]

View File

@ -21,7 +21,7 @@ from django.conf import settings
from markdownx.models import MarkdownxField from markdownx.models import MarkdownxField
from InvenTree.fields import InvenTreeURLField from InvenTree.fields import InvenTreeURLField, RoundingDecimalField
from InvenTree.status_codes import OrderStatus from InvenTree.status_codes import OrderStatus
from common.models import Currency from common.models import Currency
@ -381,9 +381,9 @@ class SupplierPriceBreak(models.Model):
part = models.ForeignKey(SupplierPart, on_delete=models.CASCADE, related_name='pricebreaks') part = models.ForeignKey(SupplierPart, on_delete=models.CASCADE, related_name='pricebreaks')
quantity = models.DecimalField(max_digits=15, decimal_places=5, default=1, validators=[MinValueValidator(1)]) quantity = RoundingDecimalField(max_digits=15, decimal_places=5, default=1, validators=[MinValueValidator(1)])
cost = models.DecimalField(max_digits=10, decimal_places=5, validators=[MinValueValidator(0)]) cost = RoundingDecimalField(max_digits=10, decimal_places=5, validators=[MinValueValidator(0)])
currency = models.ForeignKey(Currency, blank=True, null=True, on_delete=models.SET_NULL) currency = models.ForeignKey(Currency, blank=True, null=True, on_delete=models.SET_NULL)

View File

@ -21,19 +21,49 @@ InvenTree | {% trans "Supplier Part" %}
</button> </button>
</div> </div>
</div> </div>
</div>
<div class='col-sm-6'>
<div class='media-left'> <div class='media-left'>
<img class='part-thumb' <img class='part-thumb'
{% if part.part.image %} {% if part.part.image %}
src='{{ part.part.image.url }}' src='{{ part.part.image.url }}'
{% else %} {% else %}
src="{% static 'img/blank_image.png' %}" src="{% static 'img/blank_image.png' %}"
{% endif %}/> {% endif %}/>
</div> </div>
</div> </div>
<div class='col-sm-6'>
<h4>{% trans "Supplier Part Details" %}</h4>
<table class="table table-striped table-condensed">
<tr>
<td>{% trans "Internal Part" %}</td>
<td>
{% if part.part %}
<a href="{% url 'part-suppliers' part.part.id %}">{{ part.part.full_name }}</a>
{% endif %}
</td>
</tr>
<tr><td>{% trans "Supplier" %}</td><td><a href="{% url 'company-detail-parts' part.supplier.id %}">{{ part.supplier.name }}</a></td></tr>
<tr><td>{% trans "SKU" %}</td><td>{{ part.SKU }}</tr></tr>
{% if part.URL %}
<tr><td>{% trans "URL" %}</td><td><a href="{{ part.URL }}">{{ part.URL }}</a></td></tr>
{% endif %}
{% if part.description %}
<tr><td>{% trans "Description" %}</td><td>{{ part.description }}</td></tr>
{% endif %}
{% if part.manufacturer %}
<tr><td>{% trans "Manufacturer" %}</td><td>{{ part.manufacturer }}</td></tr>
<tr><td>{% trans "MPN" %}</td><td>{{ part.MPN }}</td></tr>
{% endif %}
{% if part.note %}
<tr><td>{% trans "Note" %}</td><td>{{ part.note }}</td></tr>
{% endif %}
</table>
</div>
</div> </div>
<hr> <hr>
<div class='container-fluid'> <div class='container-fluid'>

View File

@ -1,9 +1,6 @@
{% load i18n %} {% load i18n %}
<ul class='nav nav-tabs'> <ul class='nav nav-tabs'>
<li{% if tab == 'details' %} class='active'{% endif %}>
<a href="{% url 'supplier-part-detail' part.id %}">{% trans "Details" %}</a>
</li>
<li{% if tab == 'pricing' %} class='active'{% endif %}> <li{% if tab == 'pricing' %} class='active'{% endif %}>
<a href="{% url 'supplier-part-pricing' part.id %}">{% trans "Pricing" %}</a> <a href="{% url 'supplier-part-pricing' part.id %}">{% trans "Pricing" %}</a>
</li> </li>

View File

@ -53,7 +53,7 @@ supplier_part_detail_urls = [
url(r'^orders/', views.SupplierPartDetail.as_view(template_name='company/supplier_part_orders.html'), name='supplier-part-orders'), url(r'^orders/', views.SupplierPartDetail.as_view(template_name='company/supplier_part_orders.html'), name='supplier-part-orders'),
url(r'^stock/', views.SupplierPartDetail.as_view(template_name='company/supplier_part_stock.html'), name='supplier-part-stock'), url(r'^stock/', views.SupplierPartDetail.as_view(template_name='company/supplier_part_stock.html'), name='supplier-part-stock'),
url('^.*$', views.SupplierPartDetail.as_view(), name='supplier-part-detail'), url('^.*$', views.SupplierPartDetail.as_view(template_name='company/supplier_part_pricing.html'), name='supplier-part-detail'),
] ]
supplier_part_urls = [ supplier_part_urls = [

View File

@ -1198,9 +1198,9 @@ class BomItem(models.Model):
overage = str(self.overage).strip() overage = str(self.overage).strip()
# Is the overage an integer value? # Is the overage a numerical value?
try: try:
ovg = int(overage) ovg = float(overage)
if ovg < 0: if ovg < 0:
ovg = 0 ovg = 0
@ -1223,7 +1223,7 @@ class BomItem(models.Model):
# Must be represented as a decimal # Must be represented as a decimal
percent = Decimal(percent) percent = Decimal(percent)
return int(percent * quantity) return float(percent * quantity)
except ValueError: except ValueError:
pass pass
@ -1245,7 +1245,12 @@ class BomItem(models.Model):
# Base quantity requirement # Base quantity requirement
base_quantity = self.quantity * build_quantity base_quantity = self.quantity * build_quantity
return base_quantity + self.get_overage_quantity(base_quantity) # Overage requiremet
ovrg_quantity = self.get_overage_quantity(base_quantity)
required = float(base_quantity) + float(ovrg_quantity)
return required
@property @property
def price_range(self): def price_range(self):

View File

@ -38,7 +38,7 @@ from InvenTree.views import AjaxView, AjaxCreateView, AjaxUpdateView, AjaxDelete
from InvenTree.views import QRCodeView from InvenTree.views import QRCodeView
from InvenTree.helpers import DownloadFile, str2bool from InvenTree.helpers import DownloadFile, str2bool
from InvenTree.status_codes import OrderStatus from InvenTree.status_codes import OrderStatus, BuildStatus
class PartIndex(ListView): class PartIndex(ListView):
@ -593,6 +593,7 @@ class PartDetail(DetailView):
context['disabled'] = not part.active context['disabled'] = not part.active
context['OrderStatus'] = OrderStatus context['OrderStatus'] = OrderStatus
context['BuildStatus'] = BuildStatus
return context return context