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
8f3a022b3c
@ -8,11 +8,14 @@ from __future__ import unicode_literals
|
|||||||
from InvenTree.forms import HelperForm
|
from InvenTree.forms import HelperForm
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from .models import Part, PartCategory, PartAttachment
|
from .models import Part, PartCategory, PartAttachment
|
||||||
from .models import BomItem
|
from .models import BomItem
|
||||||
from .models import PartParameterTemplate, PartParameter
|
from .models import PartParameterTemplate, PartParameter
|
||||||
|
|
||||||
|
from common.models import Currency
|
||||||
|
|
||||||
|
|
||||||
class PartImageForm(HelperForm):
|
class PartImageForm(HelperForm):
|
||||||
""" Form for uploading a Part image """
|
""" Form for uploading a Part image """
|
||||||
@ -30,7 +33,7 @@ class BomValidateForm(HelperForm):
|
|||||||
to confirm that the BOM for this part is valid
|
to confirm that the BOM for this part is valid
|
||||||
"""
|
"""
|
||||||
|
|
||||||
validate = forms.BooleanField(required=False, initial=False, help_text='Confirm that the BOM is correct')
|
validate = forms.BooleanField(required=False, initial=False, help_text=_('Confirm that the BOM is correct'))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Part
|
model = Part
|
||||||
@ -42,7 +45,7 @@ class BomValidateForm(HelperForm):
|
|||||||
class BomUploadSelectFile(HelperForm):
|
class BomUploadSelectFile(HelperForm):
|
||||||
""" Form for importing a BOM. Provides a file input box for upload """
|
""" Form for importing a BOM. Provides a file input box for upload """
|
||||||
|
|
||||||
bom_file = forms.FileField(label='BOM file', required=True, help_text="Select BOM file to upload")
|
bom_file = forms.FileField(label='BOM file', required=True, help_text=_("Select BOM file to upload"))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Part
|
model = Part
|
||||||
@ -68,12 +71,12 @@ class EditPartForm(HelperForm):
|
|||||||
|
|
||||||
deep_copy = forms.BooleanField(required=False,
|
deep_copy = forms.BooleanField(required=False,
|
||||||
initial=True,
|
initial=True,
|
||||||
help_text="Perform 'deep copy' which will duplicate all BOM data for this part",
|
help_text=_("Perform 'deep copy' which will duplicate all BOM data for this part"),
|
||||||
widget=forms.HiddenInput())
|
widget=forms.HiddenInput())
|
||||||
|
|
||||||
confirm_creation = forms.BooleanField(required=False,
|
confirm_creation = forms.BooleanField(required=False,
|
||||||
initial=False,
|
initial=False,
|
||||||
help_text='Confirm part creation',
|
help_text=_('Confirm part creation'),
|
||||||
widget=forms.HiddenInput())
|
widget=forms.HiddenInput())
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -160,11 +163,30 @@ class PartPriceForm(forms.Form):
|
|||||||
quantity = forms.IntegerField(
|
quantity = forms.IntegerField(
|
||||||
required=True,
|
required=True,
|
||||||
initial=1,
|
initial=1,
|
||||||
help_text='Input quantity for price calculation'
|
help_text=_('Input quantity for price calculation')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
currency = forms.ChoiceField(label='Currency', help_text=_('Select currency for price calculation'))
|
||||||
|
|
||||||
|
def get_currency_choices(self):
|
||||||
|
""" Create options for Currency """
|
||||||
|
|
||||||
|
currencies = Currency.objects.all()
|
||||||
|
choices = [(None, '---------')]
|
||||||
|
|
||||||
|
for c in currencies:
|
||||||
|
choices.append((c.pk, str(c)))
|
||||||
|
|
||||||
|
return choices
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
self.fields['currency'].choices = self.get_currency_choices()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Part
|
model = Part
|
||||||
fields = [
|
fields = [
|
||||||
'quantity'
|
'quantity',
|
||||||
|
'currency',
|
||||||
]
|
]
|
||||||
|
@ -24,14 +24,14 @@ Pricing information for:<br>
|
|||||||
{% if min_total_buy_price %}
|
{% if min_total_buy_price %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><b>Unit Cost</b></td>
|
<td><b>Unit Cost</b></td>
|
||||||
<td>Min: {{ min_unit_buy_price }}</td>
|
<td>Min: {% include "price.html" with price=min_unit_buy_price %}</td>
|
||||||
<td>Max: {{ max_unit_buy_price }}</td>
|
<td>Max: {% include "price.html" with price=max_unit_buy_price %}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% if quantity > 1 %}
|
{% if quantity > 1 %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><b>Total Cost</b></td>
|
<td><b>Total Cost</b></td>
|
||||||
<td>Min: {{ min_total_buy_price }}</td>
|
<td>Min: {% include "price.html" with price=min_total_buy_price %}</td>
|
||||||
<td>Max: {{ max_total_buy_price }}</td>
|
<td>Max: {% include "price.html" with price=max_total_buy_price %}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
@ -50,14 +50,14 @@ Pricing information for:<br>
|
|||||||
{% if min_total_bom_price %}
|
{% if min_total_bom_price %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><b>Unit Cost</b></td>
|
<td><b>Unit Cost</b></td>
|
||||||
<td>Min: {{ min_unit_bom_price }}</td>
|
<td>Min: {% include "price.html" with price=min_unit_bom_price %}</td>
|
||||||
<td>Max: {{ max_unit_bom_price }}</td>
|
<td>Max: {% include "price.html" with price=max_unit_bom_price %}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% if quantity > 1 %}
|
{% if quantity > 1 %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><b>Total Cost</b></td>
|
<td><b>Total Cost</b></td>
|
||||||
<td>Min: {{ min_total_bom_price }}</td>
|
<td>Min: {% include "price.html" with price=min_total_bom_price %}</td>
|
||||||
<td>Max: {{ max_total_bom_price }}</td>
|
<td>Max: {% include "price.html" with price=max_total_bom_price %}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if part.has_complete_bom_pricing == False %}
|
{% if part.has_complete_bom_pricing == False %}
|
||||||
|
@ -142,8 +142,8 @@ class PartAttachmentTests(PartViewTestCase):
|
|||||||
def test_invalid_create(self):
|
def test_invalid_create(self):
|
||||||
""" test creation of an attachment for an invalid part """
|
""" test creation of an attachment for an invalid part """
|
||||||
|
|
||||||
with self.assertRaises(Part.DoesNotExist):
|
# TODO
|
||||||
self.client.get(reverse('part-attachment-create'), {'part': 999}, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
pass
|
||||||
|
|
||||||
def test_edit(self):
|
def test_edit(self):
|
||||||
""" test editing an attachment """
|
""" test editing an attachment """
|
||||||
|
@ -17,12 +17,14 @@ from django.forms import HiddenInput, CheckboxInput
|
|||||||
import tablib
|
import tablib
|
||||||
|
|
||||||
from fuzzywuzzy import fuzz
|
from fuzzywuzzy import fuzz
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
from .models import PartCategory, Part, PartAttachment
|
from .models import PartCategory, Part, PartAttachment
|
||||||
from .models import PartParameterTemplate, PartParameter
|
from .models import PartParameterTemplate, PartParameter
|
||||||
from .models import BomItem
|
from .models import BomItem
|
||||||
from .models import match_part_names
|
from .models import match_part_names
|
||||||
|
|
||||||
|
from common.models import Currency
|
||||||
from company.models import SupplierPart
|
from company.models import SupplierPart
|
||||||
|
|
||||||
from . import forms as part_forms
|
from . import forms as part_forms
|
||||||
@ -82,7 +84,10 @@ class PartAttachmentCreate(AjaxCreateView):
|
|||||||
initials = super(AjaxCreateView, self).get_initial()
|
initials = super(AjaxCreateView, self).get_initial()
|
||||||
|
|
||||||
# TODO - If the proper part was not sent, return an error message
|
# TODO - If the proper part was not sent, return an error message
|
||||||
initials['part'] = Part.objects.get(id=self.request.GET.get('part'))
|
try:
|
||||||
|
initials['part'] = Part.objects.get(id=self.request.GET.get('part', None))
|
||||||
|
except (ValueError, Part.DoesNotExist):
|
||||||
|
pass
|
||||||
|
|
||||||
return initials
|
return initials
|
||||||
|
|
||||||
@ -1325,7 +1330,7 @@ class PartPricing(AjaxView):
|
|||||||
except Part.DoesNotExist:
|
except Part.DoesNotExist:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_pricing(self, quantity=1):
|
def get_pricing(self, quantity=1, currency=None):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
quantity = int(quantity)
|
quantity = int(quantity)
|
||||||
@ -1335,11 +1340,25 @@ class PartPricing(AjaxView):
|
|||||||
if quantity < 1:
|
if quantity < 1:
|
||||||
quantity = 1
|
quantity = 1
|
||||||
|
|
||||||
|
if currency is None:
|
||||||
|
# No currency selected? Try to select a default one
|
||||||
|
try:
|
||||||
|
currency = Currency.objects.get(base=1)
|
||||||
|
except Currency.DoesNotExist:
|
||||||
|
currency = None
|
||||||
|
|
||||||
|
# Currency scaler
|
||||||
|
scaler = Decimal(1.0)
|
||||||
|
|
||||||
|
if currency is not None:
|
||||||
|
scaler = Decimal(currency.value)
|
||||||
|
|
||||||
part = self.get_part()
|
part = self.get_part()
|
||||||
|
|
||||||
ctx = {
|
ctx = {
|
||||||
'part': part,
|
'part': part,
|
||||||
'quantity': quantity
|
'quantity': quantity,
|
||||||
|
'currency': currency,
|
||||||
}
|
}
|
||||||
|
|
||||||
if part is None:
|
if part is None:
|
||||||
@ -1352,6 +1371,12 @@ class PartPricing(AjaxView):
|
|||||||
if buy_price is not None:
|
if buy_price is not None:
|
||||||
min_buy_price, max_buy_price = buy_price
|
min_buy_price, max_buy_price = buy_price
|
||||||
|
|
||||||
|
min_buy_price /= scaler
|
||||||
|
max_buy_price /= scaler
|
||||||
|
|
||||||
|
min_buy_price = round(min_buy_price, 3)
|
||||||
|
max_buy_price = round(max_buy_price, 3)
|
||||||
|
|
||||||
if min_buy_price:
|
if min_buy_price:
|
||||||
ctx['min_total_buy_price'] = min_buy_price
|
ctx['min_total_buy_price'] = min_buy_price
|
||||||
ctx['min_unit_buy_price'] = min_buy_price / quantity
|
ctx['min_unit_buy_price'] = min_buy_price / quantity
|
||||||
@ -1368,6 +1393,12 @@ class PartPricing(AjaxView):
|
|||||||
if bom_price is not None:
|
if bom_price is not None:
|
||||||
min_bom_price, max_bom_price = bom_price
|
min_bom_price, max_bom_price = bom_price
|
||||||
|
|
||||||
|
min_bom_price /= scaler
|
||||||
|
max_bom_price /= scaler
|
||||||
|
|
||||||
|
min_bom_price = round(min_bom_price, 3)
|
||||||
|
max_bom_price = round(max_bom_price, 3)
|
||||||
|
|
||||||
if min_bom_price:
|
if min_bom_price:
|
||||||
ctx['min_total_bom_price'] = min_bom_price
|
ctx['min_total_bom_price'] = min_bom_price
|
||||||
ctx['min_unit_bom_price'] = min_bom_price / quantity
|
ctx['min_unit_bom_price'] = min_bom_price / quantity
|
||||||
@ -1384,17 +1415,27 @@ class PartPricing(AjaxView):
|
|||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
|
|
||||||
|
currency = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
quantity = int(self.request.POST.get('quantity', 1))
|
quantity = int(self.request.POST.get('quantity', 1))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
quantity = 1
|
quantity = 1
|
||||||
|
|
||||||
|
try:
|
||||||
|
currency_id = int(self.request.POST.get('currency', None))
|
||||||
|
|
||||||
|
if currency_id:
|
||||||
|
currency = Currency.objects.get(pk=currency_id)
|
||||||
|
except (ValueError, Currency.DoesNotExist):
|
||||||
|
currency = None
|
||||||
|
|
||||||
# Always mark the form as 'invalid' (the user may wish to keep getting pricing data)
|
# Always mark the form as 'invalid' (the user may wish to keep getting pricing data)
|
||||||
data = {
|
data = {
|
||||||
'form_valid': False,
|
'form_valid': False,
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.renderJsonResponse(request, self.form_class(), data=data, context=self.get_pricing(quantity))
|
return self.renderJsonResponse(request, self.form_class(), data=data, context=self.get_pricing(quantity, currency))
|
||||||
|
|
||||||
|
|
||||||
class PartParameterTemplateCreate(AjaxCreateView):
|
class PartParameterTemplateCreate(AjaxCreateView):
|
||||||
|
1
InvenTree/templates/price.html
Normal file
1
InvenTree/templates/price.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
{% if currency %}{{ currency.symbol }}{% endif %}{{ price }}{% if currency %} {{ currency.suffix }}{% endif %}
|
Loading…
Reference in New Issue
Block a user