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
8714d6876a
@ -6,7 +6,7 @@ Company database model definitions
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import os
|
||||
|
||||
import decimal
|
||||
import math
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
@ -566,12 +566,15 @@ class SupplierPart(models.Model):
|
||||
- If order multiples are to be observed, then we need to calculate based on that, too
|
||||
"""
|
||||
|
||||
price_breaks = self.price_breaks.filter(quantity__lte=quantity)
|
||||
price_breaks = self.price_breaks.all()
|
||||
|
||||
# No price break information available?
|
||||
if len(price_breaks) == 0:
|
||||
return None
|
||||
|
||||
# Check if quantity is fraction and disable multiples
|
||||
multiples = (quantity % 1 == 0)
|
||||
|
||||
# Order multiples
|
||||
if multiples:
|
||||
quantity = int(math.ceil(quantity / self.multiple) * self.multiple)
|
||||
@ -584,7 +587,12 @@ class SupplierPart(models.Model):
|
||||
# Default currency selection
|
||||
currency = common.models.InvenTreeSetting.get_setting('INVENTREE_DEFAULT_CURRENCY')
|
||||
|
||||
pb_min = None
|
||||
for pb in self.price_breaks.all():
|
||||
# Store smallest price break
|
||||
if not pb_min:
|
||||
pb_min = pb
|
||||
|
||||
# Ignore this pricebreak (quantity is too high)
|
||||
if pb.quantity > quantity:
|
||||
continue
|
||||
@ -598,6 +606,17 @@ class SupplierPart(models.Model):
|
||||
# Convert everything to the selected currency
|
||||
pb_cost = pb.convert_to(currency)
|
||||
|
||||
# Use smallest price break
|
||||
if not pb_found and pb_min:
|
||||
# Update price break information
|
||||
pb_quantity = pb_min.quantity
|
||||
pb_cost = pb_min.convert_to(currency)
|
||||
# Trigger cost calculation using smallest price break
|
||||
pb_found = True
|
||||
|
||||
# Convert quantity to decimal.Decimal format
|
||||
quantity = decimal.Decimal(f'{quantity}')
|
||||
|
||||
if pb_found:
|
||||
cost = pb_cost * quantity
|
||||
return normalize(cost + self.base_cost)
|
||||
|
@ -5,6 +5,7 @@ from django.test import TestCase
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
import os
|
||||
from decimal import Decimal
|
||||
|
||||
from .models import Company, Contact, ManufacturerPart, SupplierPart
|
||||
from .models import rename_company_image
|
||||
@ -103,8 +104,9 @@ class CompanySimpleTest(TestCase):
|
||||
self.assertEqual(p(100), 350)
|
||||
|
||||
p = self.acme0002.get_price
|
||||
self.assertEqual(p(1), None)
|
||||
self.assertEqual(p(2), None)
|
||||
self.assertEqual(p(0.5), 3.5)
|
||||
self.assertEqual(p(1), 7)
|
||||
self.assertEqual(p(2), 14)
|
||||
self.assertEqual(p(5), 35)
|
||||
self.assertEqual(p(45), 315)
|
||||
self.assertEqual(p(55), 68.75)
|
||||
@ -112,6 +114,7 @@ class CompanySimpleTest(TestCase):
|
||||
def test_part_pricing(self):
|
||||
m2x4 = Part.objects.get(name='M2x4 LPHS')
|
||||
|
||||
self.assertEqual(m2x4.get_price_info(5.5), "38.5 - 41.25")
|
||||
self.assertEqual(m2x4.get_price_info(10), "70 - 75")
|
||||
self.assertEqual(m2x4.get_price_info(100), "125 - 350")
|
||||
|
||||
@ -121,7 +124,8 @@ class CompanySimpleTest(TestCase):
|
||||
|
||||
m3x12 = Part.objects.get(name='M3x12 SHCS')
|
||||
|
||||
self.assertIsNone(m3x12.get_price_info(3))
|
||||
self.assertEqual(m3x12.get_price_info(0.3), Decimal('2.4'))
|
||||
self.assertEqual(m3x12.get_price_info(3), Decimal('24'))
|
||||
self.assertIsNotNone(m3x12.get_price_info(50))
|
||||
|
||||
def test_currency_validation(self):
|
||||
|
@ -1957,6 +1957,11 @@ class PartPricing(AjaxView):
|
||||
|
||||
role_required = ['sales_order.view', 'part.view']
|
||||
|
||||
def get_quantity(self):
|
||||
""" Return set quantity in decimal format """
|
||||
|
||||
return Decimal(self.request.POST.get('quantity', 1))
|
||||
|
||||
def get_part(self):
|
||||
try:
|
||||
return Part.objects.get(id=self.kwargs['pk'])
|
||||
@ -1965,12 +1970,12 @@ class PartPricing(AjaxView):
|
||||
|
||||
def get_pricing(self, quantity=1, currency=None):
|
||||
|
||||
try:
|
||||
quantity = int(quantity)
|
||||
except ValueError:
|
||||
quantity = 1
|
||||
# try:
|
||||
# quantity = int(quantity)
|
||||
# except ValueError:
|
||||
# quantity = 1
|
||||
|
||||
if quantity < 1:
|
||||
if quantity <= 0:
|
||||
quantity = 1
|
||||
|
||||
# TODO - Capacity for price comparison in different currencies
|
||||
@ -2000,16 +2005,19 @@ class PartPricing(AjaxView):
|
||||
min_buy_price /= scaler
|
||||
max_buy_price /= scaler
|
||||
|
||||
min_unit_buy_price = round(min_buy_price / quantity, 3)
|
||||
max_unit_buy_price = round(max_buy_price / quantity, 3)
|
||||
|
||||
min_buy_price = round(min_buy_price, 3)
|
||||
max_buy_price = round(max_buy_price, 3)
|
||||
|
||||
if 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_unit_buy_price
|
||||
|
||||
if max_buy_price:
|
||||
ctx['max_total_buy_price'] = max_buy_price
|
||||
ctx['max_unit_buy_price'] = max_buy_price / quantity
|
||||
ctx['max_unit_buy_price'] = max_unit_buy_price
|
||||
|
||||
# BOM pricing information
|
||||
if part.bom_count > 0:
|
||||
@ -2022,16 +2030,19 @@ class PartPricing(AjaxView):
|
||||
min_bom_price /= scaler
|
||||
max_bom_price /= scaler
|
||||
|
||||
min_unit_bom_price = round(min_bom_price / quantity, 3)
|
||||
max_unit_bom_price = round(max_bom_price / quantity, 3)
|
||||
|
||||
min_bom_price = round(min_bom_price, 3)
|
||||
max_bom_price = round(max_bom_price, 3)
|
||||
|
||||
if 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_unit_bom_price
|
||||
|
||||
if max_bom_price:
|
||||
ctx['max_total_bom_price'] = max_bom_price
|
||||
ctx['max_unit_bom_price'] = max_bom_price / quantity
|
||||
ctx['max_unit_bom_price'] = max_unit_bom_price
|
||||
|
||||
return ctx
|
||||
|
||||
@ -2043,10 +2054,11 @@ class PartPricing(AjaxView):
|
||||
|
||||
currency = None
|
||||
|
||||
try:
|
||||
quantity = int(self.request.POST.get('quantity', 1))
|
||||
except ValueError:
|
||||
quantity = 1
|
||||
quantity = self.get_quantity()
|
||||
|
||||
# Retain quantity value set by user
|
||||
form = self.form_class()
|
||||
form.fields['quantity'].initial = quantity
|
||||
|
||||
# TODO - How to handle pricing in different currencies?
|
||||
currency = None
|
||||
@ -2056,7 +2068,7 @@ class PartPricing(AjaxView):
|
||||
'form_valid': False,
|
||||
}
|
||||
|
||||
return self.renderJsonResponse(request, self.form_class(), data=data, context=self.get_pricing(quantity, currency))
|
||||
return self.renderJsonResponse(request, form, data=data, context=self.get_pricing(quantity, currency))
|
||||
|
||||
|
||||
class PartParameterTemplateCreate(AjaxCreateView):
|
||||
|
10
README.md
10
README.md
@ -51,15 +51,15 @@ To contribute to the translation effort, navigate to the [InvenTree crowdin proj
|
||||
|
||||
For InvenTree documentation, refer to the [InvenTree documentation website](https://inventree.readthedocs.io/en/latest/).
|
||||
|
||||
# Getting Started
|
||||
|
||||
Refer to the [getting started guide](https://inventree.readthedocs.io/en/latest/start/install/) for installation and setup instructions.
|
||||
|
||||
# Credits
|
||||
|
||||
The credits for all used packages are part of the [InvenTree documentation website](https://inventree.readthedocs.io/en/latest/credits/).
|
||||
|
||||
## Getting Started
|
||||
|
||||
Refer to the [getting started guide](https://inventree.readthedocs.io/en/latest/start/install/) for installation and setup instructions.
|
||||
|
||||
## Integration
|
||||
# Integration
|
||||
|
||||
InvenTree is designed to be extensible, and provides multiple options for integration with external applications or addition of custom plugins:
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user