mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Add option to add a single-quantity price-break when creating a new SupplierPart object
- Add unit testing!
This commit is contained in:
parent
534f43872f
commit
47cbf3071d
@ -14,7 +14,6 @@ import django.forms
|
||||
import djmoney.settings
|
||||
from djmoney.forms.fields import MoneyField
|
||||
|
||||
from common.models import InvenTreeSetting
|
||||
import common.settings
|
||||
|
||||
from .models import Company
|
||||
|
@ -380,6 +380,25 @@ class SupplierPart(models.Model):
|
||||
def unit_pricing(self):
|
||||
return self.get_price(1)
|
||||
|
||||
def add_price_break(self, quantity, price):
|
||||
"""
|
||||
Create a new price break for this part
|
||||
|
||||
args:
|
||||
quantity - Numerical quantity
|
||||
price - Must be a Money object
|
||||
"""
|
||||
|
||||
# Check if a price break at that quantity already exists...
|
||||
if self.price_breaks.filter(quantity=quantity, part=self.pk).exists():
|
||||
return
|
||||
|
||||
SupplierPriceBreak.objects.create(
|
||||
part=self,
|
||||
quantity=quantity,
|
||||
price=price
|
||||
)
|
||||
|
||||
def get_price(self, quantity, moq=True, multiples=True, currency=None):
|
||||
""" Calculate the supplier price based on quantity price breaks.
|
||||
|
||||
|
@ -3,6 +3,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import json
|
||||
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
from django.contrib.auth import get_user_model
|
||||
@ -11,7 +13,7 @@ from django.contrib.auth.models import Group
|
||||
from .models import SupplierPart
|
||||
|
||||
|
||||
class CompanyViewTest(TestCase):
|
||||
class CompanyViewTestBase(TestCase):
|
||||
|
||||
fixtures = [
|
||||
'category',
|
||||
@ -47,14 +49,105 @@ class CompanyViewTest(TestCase):
|
||||
|
||||
self.client.login(username='username', password='password')
|
||||
|
||||
def test_company_index(self):
|
||||
""" Test the company index """
|
||||
|
||||
response = self.client.get(reverse('company-index'))
|
||||
class SupplierPartViewTests(CompanyViewTestBase):
|
||||
"""
|
||||
Tests for the SupplierPart views.
|
||||
"""
|
||||
|
||||
def post(self, data, valid=None):
|
||||
"""
|
||||
POST against this form and return the response (as a JSON object)
|
||||
"""
|
||||
url = reverse('supplier-part-create')
|
||||
|
||||
response = self.client.post(url, data, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
json_data = json.loads(response.content)
|
||||
|
||||
# If a particular status code is required
|
||||
if valid is not None:
|
||||
if valid:
|
||||
self.assertEqual(json_data['form_valid'], True)
|
||||
else:
|
||||
self.assertEqual(json_data['form_valid'], False)
|
||||
|
||||
form_errors = json.loads(json_data['form_errors'])
|
||||
|
||||
return json_data, form_errors
|
||||
|
||||
def test_supplier_part_create(self):
|
||||
"""
|
||||
Test the SupplierPartCreate view.
|
||||
|
||||
This view allows some additional functionality,
|
||||
specifically it allows the user to create a single-quantity price break
|
||||
automatically, when saving the new SupplierPart model.
|
||||
"""
|
||||
|
||||
url = reverse('supplier-part-create')
|
||||
|
||||
# First check that we can GET the form
|
||||
response = self.client.get(url, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# How many supplier parts are already in the database?
|
||||
n = SupplierPart.objects.all().count()
|
||||
|
||||
data = {
|
||||
'part': 1,
|
||||
'supplier': 1,
|
||||
}
|
||||
|
||||
# SKU is required! (form should fail)
|
||||
(response, errors) = self.post(data, valid=False)
|
||||
|
||||
self.assertIsNotNone(errors.get('SKU', None))
|
||||
|
||||
data['SKU'] = 'TEST-ME-123'
|
||||
|
||||
(response, errors) = self.post(data, valid=True)
|
||||
|
||||
# Check that the SupplierPart was created!
|
||||
self.assertEqual(n + 1, SupplierPart.objects.all().count())
|
||||
|
||||
# Check that it was created *without* a price-break
|
||||
supplier_part = SupplierPart.objects.get(pk=response['pk'])
|
||||
|
||||
self.assertEqual(supplier_part.price_breaks.count(), 0)
|
||||
|
||||
# Duplicate SKU is prohibited
|
||||
(response, errors) = self.post(data, valid=False)
|
||||
|
||||
self.assertIsNotNone(errors.get('__all__', None))
|
||||
|
||||
# Add with a different SKU, *and* a single-quantity price
|
||||
data['SKU'] = 'TEST-ME-1234'
|
||||
data['single_pricing_0'] = '123.4'
|
||||
data['single_pricing_1'] = 'CAD'
|
||||
|
||||
(response, errors) = self.post(data, valid=True)
|
||||
|
||||
pk = response.get('pk')
|
||||
|
||||
# Check that *another* SupplierPart was created
|
||||
self.assertEqual(n + 2, SupplierPart.objects.all().count())
|
||||
|
||||
supplier_part = SupplierPart.objects.get(pk=pk)
|
||||
|
||||
# Check that a price-break has been created!
|
||||
self.assertEqual(supplier_part.price_breaks.count(), 1)
|
||||
|
||||
price_break = supplier_part.price_breaks.first()
|
||||
|
||||
self.assertEqual(price_break.quantity, 1)
|
||||
|
||||
def test_supplier_part_delete(self):
|
||||
""" Test the SupplierPartDelete view """
|
||||
"""
|
||||
Test the SupplierPartDelete view
|
||||
"""
|
||||
|
||||
url = reverse('supplier-part-delete')
|
||||
|
||||
@ -80,3 +173,15 @@ class CompanyViewTest(TestCase):
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
self.assertEqual(n - 2, SupplierPart.objects.count())
|
||||
|
||||
|
||||
class CompanyViewTest(CompanyViewTestBase):
|
||||
"""
|
||||
Tests for various 'Company' views
|
||||
"""
|
||||
|
||||
def test_company_index(self):
|
||||
""" Test the company index """
|
||||
|
||||
response = self.client.get(reverse('company-index'))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
@ -271,6 +271,14 @@ class SupplierPartEdit(AjaxUpdateView):
|
||||
ajax_form_title = _('Edit Supplier Part')
|
||||
role_required = 'purchase_order.change'
|
||||
|
||||
def get_form(self):
|
||||
form = super().get_form()
|
||||
|
||||
# Hide the single-pricing field (only for creating a new SupplierPart!)
|
||||
form.fields['single_pricing'].widget = HiddenInput()
|
||||
|
||||
return form
|
||||
|
||||
|
||||
class SupplierPartCreate(AjaxCreateView):
|
||||
""" Create view for making new SupplierPart """
|
||||
@ -282,6 +290,30 @@ class SupplierPartCreate(AjaxCreateView):
|
||||
context_object_name = 'part'
|
||||
role_required = 'purchase_order.add'
|
||||
|
||||
def validate(self, part, form):
|
||||
|
||||
single_pricing = form.cleaned_data.get('single_pricing', None)
|
||||
|
||||
if single_pricing:
|
||||
# TODO - What validation steps can be performed on the single_pricing field?
|
||||
pass
|
||||
|
||||
def save(self, form):
|
||||
"""
|
||||
If single_pricing is defined, add a price break for quantity=1
|
||||
"""
|
||||
|
||||
# Save the supplier part object
|
||||
supplier_part = super().save(form)
|
||||
|
||||
single_pricing = form.cleaned_data.get('single_pricing', None)
|
||||
|
||||
if single_pricing:
|
||||
|
||||
supplier_part.add_price_break(1, single_pricing)
|
||||
|
||||
return supplier_part
|
||||
|
||||
def get_form(self):
|
||||
""" Create Form instance to create a new SupplierPart object.
|
||||
Hide some fields if they are not appropriate in context
|
||||
|
@ -8,6 +8,7 @@
|
||||
category: 8
|
||||
link: www.acme.com/parts/m2x4lphs
|
||||
tree_id: 0
|
||||
purchaseable: True
|
||||
level: 0
|
||||
lft: 0
|
||||
rght: 0
|
||||
|
Loading…
x
Reference in New Issue
Block a user