mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
commit
a6ee3a59a0
@ -83,6 +83,7 @@ INSTALLED_APPS = [
|
||||
'django.contrib.staticfiles',
|
||||
|
||||
# InvenTree apps
|
||||
'common.apps.CommonConfig',
|
||||
'part.apps.PartConfig',
|
||||
'stock.apps.StockConfig',
|
||||
'company.apps.CompanyConfig',
|
||||
|
0
InvenTree/common/__init__.py
Normal file
0
InvenTree/common/__init__.py
Normal file
10
InvenTree/common/admin.py
Normal file
10
InvenTree/common/admin.py
Normal 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
5
InvenTree/common/apps.py
Normal file
@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class CommonConfig(AppConfig):
|
||||
name = 'common'
|
16
InvenTree/common/fixtures/currency.yaml
Normal file
16
InvenTree/common/fixtures/currency.yaml
Normal 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
|
26
InvenTree/common/migrations/0001_initial.py
Normal file
26
InvenTree/common/migrations/0001_initial.py
Normal 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')),
|
||||
],
|
||||
),
|
||||
]
|
17
InvenTree/common/migrations/0002_auto_20190902_2304.py
Normal file
17
InvenTree/common/migrations/0002_auto_20190902_2304.py
Normal 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'},
|
||||
),
|
||||
]
|
19
InvenTree/common/migrations/0003_auto_20190902_2310.py
Normal file
19
InvenTree/common/migrations/0003_auto_20190902_2310.py
Normal 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)]),
|
||||
),
|
||||
]
|
0
InvenTree/common/migrations/__init__.py
Normal file
0
InvenTree/common/migrations/__init__.py
Normal file
79
InvenTree/common/models.py
Normal file
79
InvenTree/common/models.py
Normal 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
19
InvenTree/common/tests.py
Normal 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)
|
1
InvenTree/common/views.py
Normal file
1
InvenTree/common/views.py
Normal file
@ -0,0 +1 @@
|
||||
# Create your views here.
|
@ -70,5 +70,6 @@ class EditPriceBreakForm(HelperForm):
|
||||
fields = [
|
||||
'part',
|
||||
'quantity',
|
||||
'cost'
|
||||
'cost',
|
||||
'currency',
|
||||
]
|
||||
|
@ -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'),
|
||||
),
|
||||
]
|
@ -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")
|
||||
|
||||
|
@ -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>
|
||||
|
5
Makefile
5
Makefile
@ -9,6 +9,7 @@ clean:
|
||||
|
||||
# Perform database migrations (after schema changes are made)
|
||||
migrate:
|
||||
python3 InvenTree/manage.py makemigrations common
|
||||
python3 InvenTree/manage.py makemigrations company
|
||||
python3 InvenTree/manage.py makemigrations part
|
||||
python3 InvenTree/manage.py makemigrations stock
|
||||
@ -40,12 +41,12 @@ style:
|
||||
# Run unit tests
|
||||
test:
|
||||
python3 InvenTree/manage.py check
|
||||
python3 InvenTree/manage.py test build company part stock order
|
||||
python3 InvenTree/manage.py test build common company order part stock
|
||||
|
||||
# Run code coverage
|
||||
coverage:
|
||||
python3 InvenTree/manage.py check
|
||||
coverage run InvenTree/manage.py test build company part stock order InvenTree
|
||||
coverage run InvenTree/manage.py test build common company order part stock InvenTree
|
||||
coverage html
|
||||
|
||||
# Install packages required to generate code docs
|
||||
|
Loading…
Reference in New Issue
Block a user