2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-06-12 10:05:39 +00:00

Merge remote-tracking branch 'inventree/master'

This commit is contained in:
Oliver Walters
2019-09-03 10:42:14 +10:00
25 changed files with 273 additions and 22 deletions

View File

@ -83,6 +83,7 @@ INSTALLED_APPS = [
'django.contrib.staticfiles',
# InvenTree apps
'common.apps.CommonConfig',
'part.apps.PartConfig',
'stock.apps.StockConfig',
'company.apps.CompanyConfig',
@ -231,16 +232,20 @@ USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.10/howto/static-files/
# Web URL endpoint for served static files
STATIC_URL = '/static/'
# The filesystem location for served static files
STATIC_ROOT = CONFIG.get('static_root', os.path.join(BASE_DIR, 'static'))
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'InvenTree', 'static'),
]
# Web URL endpoint for served media files
MEDIA_URL = '/media/'
# The filesystem location for served static files
MEDIA_ROOT = CONFIG.get('media_root', os.path.join(BASE_DIR, 'media'))
# crispy forms use the bootstrap templates

View File

@ -4,7 +4,7 @@ Provides information on the current InvenTree version
import subprocess
INVENTREE_SW_VERSION = "0.0.3"
INVENTREE_SW_VERSION = "0.0.4"
def inventreeVersion():

View File

10
InvenTree/common/admin.py Normal file
View File

@ -0,0 +1,10 @@
from django.contrib import admin
from .models import Currency
class CurrencyAdmin(admin.ModelAdmin):
list_display = ('symbol', 'suffix', 'description', 'value', 'base')
admin.site.register(Currency, CurrencyAdmin)

5
InvenTree/common/apps.py Normal file
View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class CommonConfig(AppConfig):
name = 'common'

View File

@ -0,0 +1,16 @@
# Test fixtures for Currency objects
- model: common.currency
fields:
symbol: '$'
suffix: 'AUD'
description: 'Australian Dollars'
base: True
- model: common.currency
fields:
symbol: '$'
suffix: 'USD'
description: 'US Dollars'
base: False
value: 1.4

View File

@ -0,0 +1,26 @@
# Generated by Django 2.2.4 on 2019-09-02 23:02
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Currency',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('symbol', models.CharField(help_text='Currency Symbol e.g. $', max_length=10)),
('suffix', models.CharField(help_text='Currency Suffix e.g. AUD', max_length=10, unique=True)),
('description', models.CharField(help_text='Currency Description', max_length=100)),
('value', models.DecimalField(decimal_places=5, help_text='Currency Value', max_digits=10, validators=[django.core.validators.MinValueValidator(1e-05), django.core.validators.MaxValueValidator(100000)])),
('base', models.BooleanField(default=False, help_text='Use this currency as the base currency')),
],
),
]

View File

@ -0,0 +1,17 @@
# Generated by Django 2.2.4 on 2019-09-02 23:04
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('common', '0001_initial'),
]
operations = [
migrations.AlterModelOptions(
name='currency',
options={'verbose_name_plural': 'Currencies'},
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 2.2.4 on 2019-09-02 23:10
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('common', '0002_auto_20190902_2304'),
]
operations = [
migrations.AlterField(
model_name='currency',
name='value',
field=models.DecimalField(decimal_places=5, default=1.0, help_text='Currency Value', max_digits=10, validators=[django.core.validators.MinValueValidator(1e-05), django.core.validators.MaxValueValidator(100000)]),
),
]

View File

View File

@ -0,0 +1,79 @@
"""
Common database model definitions.
These models are 'generic' and do not fit a particular business logic object.
"""
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
from django.utils.translation import ugettext as _
from django.core.validators import MinValueValidator, MaxValueValidator
class Currency(models.Model):
"""
A Currency object represents a particular unit of currency.
Each Currency has a scaling factor which relates it to the base currency.
There must be one (and only one) currency which is selected as the base currency,
and each other currency is calculated relative to it.
Attributes:
symbol: Currency symbol e.g. $
suffix: Currency suffix e.g. AUD
description: Long-form description e.g. "Australian Dollars"
value: The value of this currency compared to the base currency.
base: True if this currency is the base currency
"""
symbol = models.CharField(max_length=10, blank=False, unique=False, help_text=_('Currency Symbol e.g. $'))
suffix = models.CharField(max_length=10, blank=False, unique=True, help_text=_('Currency Suffix e.g. AUD'))
description = models.CharField(max_length=100, blank=False, help_text=_('Currency Description'))
value = models.DecimalField(default=1.0, max_digits=10, decimal_places=5, validators=[MinValueValidator(0.00001), MaxValueValidator(100000)], help_text=_('Currency Value'))
base = models.BooleanField(default=False, help_text=_('Use this currency as the base currency'))
class Meta:
verbose_name_plural = 'Currencies'
def __str__(self):
""" Format string for currency representation """
s = "{sym} {suf} - {desc}".format(
sym=self.symbol,
suf=self.suffix,
desc=self.description
)
if self.base:
s += " (Base)"
else:
s += " = {v}".format(v=self.value)
return s
def save(self, *args, **kwargs):
""" Validate the model before saving
- Ensure that there is only one base currency!
"""
# If this currency is set as the base currency, ensure no others are
if self.base:
for cur in Currency.objects.filter(base=True).exclude(pk=self.pk):
cur.base = False
cur.save()
# If there are no currencies set as the base currency, set this as base
if not Currency.objects.filter(base=True).exists():
self.base = True
# If this is the base currency, ensure value is set to unity
if self.base:
self.value = 1.0
super().save(*args, **kwargs)

19
InvenTree/common/tests.py Normal file
View File

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.test import TestCase
from .models import Currency
class CurrencyTest(TestCase):
""" Tests for Currency model """
fixtures = [
'currency',
]
def test_currency(self):
# Simple test for now (improve this later!)
self.assertEqual(Currency.objects.count(), 2)

View File

@ -0,0 +1 @@
# Create your views here.

View File

@ -70,5 +70,6 @@ class EditPriceBreakForm(HelperForm):
fields = [
'part',
'quantity',
'cost'
'cost',
'currency',
]

View File

@ -0,0 +1,20 @@
# Generated by Django 2.2.4 on 2019-09-02 23:34
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('common', '0003_auto_20190902_2310'),
('company', '0005_auto_20190525_2356'),
]
operations = [
migrations.AddField(
model_name='supplierpricebreak',
name='currency',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='common.Currency'),
),
]

View File

@ -8,6 +8,7 @@ from __future__ import unicode_literals
import os
import math
from decimal import Decimal
from django.core.validators import MinValueValidator
from django.db import models
@ -19,6 +20,7 @@ from django.conf import settings
from django.contrib.staticfiles.templatetags.staticfiles import static
from InvenTree.status_codes import OrderStatus
from common.models import Currency
def rename_company_image(instance, filename):
@ -310,7 +312,8 @@ class SupplierPart(models.Model):
# If this price-break quantity is the largest so far, use it!
if pb.quantity > pb_quantity:
pb_quantity = pb.quantity
pb_cost = pb.cost
# Convert everything to base currency
pb_cost = pb.converted_cost
if pb_found:
cost = pb_cost * quantity
@ -369,6 +372,7 @@ class SupplierPriceBreak(models.Model):
part: Link to a SupplierPart object that this price break applies to
quantity: Quantity required for price break
cost: Cost at specified quantity
currency: Reference to the currency of this pricebreak (leave empty for base currency)
"""
part = models.ForeignKey(SupplierPart, on_delete=models.CASCADE, related_name='pricebreaks')
@ -377,6 +381,19 @@ class SupplierPriceBreak(models.Model):
cost = models.DecimalField(max_digits=10, decimal_places=5, validators=[MinValueValidator(0)])
currency = models.ForeignKey(Currency, blank=True, null=True, on_delete=models.SET_NULL)
@property
def converted_cost(self):
""" Return the cost of this price break, converted to the base currency """
scaler = Decimal(1.0)
if self.currency:
scaler = self.currency.value
return self.cost * scaler
class Meta:
unique_together = ("part", "quantity")

View File

@ -32,6 +32,8 @@ class CompanySerializer(InvenTreeModelSerializer):
url = serializers.CharField(source='get_absolute_url', read_only=True)
part_count = serializers.CharField(read_only=True)
image = serializers.CharField(source='get_image_url', read_only=True)
class Meta:
model = Company
fields = [

View File

@ -88,7 +88,10 @@ InvenTree | {{ company.name }} - Parts
{% for pb in part.price_breaks.all %}
<tr>
<td>{{ pb.quantity }}</td>
<td>{{ pb.cost }}
<td>
{% if pb.currency %}{{ pb.currency.symbol }}{% endif %}
{{ pb.cost }}
{% if pb.currency %}{{ pb.currency.suffix }}{% endif %}
<div class='btn-group' style='float: right;'>
<button title='Edit Price Break' class='btn btn-primary pb-edit-button btn-sm' type='button' url="{% url 'price-break-edit' pb.id %}"><span class='glyphicon glyphicon-small glyphicon-edit'></span></button>
<button title='Delete Price Break' class='btn btn-danger pb-delete-button btn-sm' type='button' url="{% url 'price-break-delete' pb.id %}"><span class='glyphicon glyphicon-small glyphicon-trash'></span></button>

View File

@ -28,6 +28,10 @@
<tr>
<td>View Code on GitHub</td><td><a href="{% inventree_github %}">{% inventree_github %}</a></td>
</tr>
<tr>
<td></td>
<td><a href='https://github.com/inventree/InvenTree/issues'><button class='btn btn-default'>Submit Bug Report</button></a></td>
</tr>
</table>
</div>