mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-29 12:06:44 +00:00
commit
a6ee3a59a0
@ -83,6 +83,7 @@ INSTALLED_APPS = [
|
|||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
|
|
||||||
# InvenTree apps
|
# InvenTree apps
|
||||||
|
'common.apps.CommonConfig',
|
||||||
'part.apps.PartConfig',
|
'part.apps.PartConfig',
|
||||||
'stock.apps.StockConfig',
|
'stock.apps.StockConfig',
|
||||||
'company.apps.CompanyConfig',
|
'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 = [
|
fields = [
|
||||||
'part',
|
'part',
|
||||||
'quantity',
|
'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 os
|
||||||
|
|
||||||
import math
|
import math
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
from django.core.validators import MinValueValidator
|
from django.core.validators import MinValueValidator
|
||||||
from django.db import models
|
from django.db import models
|
||||||
@ -19,6 +20,7 @@ from django.conf import settings
|
|||||||
from django.contrib.staticfiles.templatetags.staticfiles import static
|
from django.contrib.staticfiles.templatetags.staticfiles import static
|
||||||
|
|
||||||
from InvenTree.status_codes import OrderStatus
|
from InvenTree.status_codes import OrderStatus
|
||||||
|
from common.models import Currency
|
||||||
|
|
||||||
|
|
||||||
def rename_company_image(instance, filename):
|
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 this price-break quantity is the largest so far, use it!
|
||||||
if pb.quantity > pb_quantity:
|
if pb.quantity > pb_quantity:
|
||||||
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:
|
if pb_found:
|
||||||
cost = pb_cost * quantity
|
cost = pb_cost * quantity
|
||||||
@ -369,6 +372,7 @@ class SupplierPriceBreak(models.Model):
|
|||||||
part: Link to a SupplierPart object that this price break applies to
|
part: Link to a SupplierPart object that this price break applies to
|
||||||
quantity: Quantity required for price break
|
quantity: Quantity required for price break
|
||||||
cost: Cost at specified quantity
|
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')
|
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)])
|
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:
|
class Meta:
|
||||||
unique_together = ("part", "quantity")
|
unique_together = ("part", "quantity")
|
||||||
|
|
||||||
|
@ -88,7 +88,10 @@ InvenTree | {{ company.name }} - Parts
|
|||||||
{% for pb in part.price_breaks.all %}
|
{% for pb in part.price_breaks.all %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ pb.quantity }}</td>
|
<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;'>
|
<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='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>
|
<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)
|
# Perform database migrations (after schema changes are made)
|
||||||
migrate:
|
migrate:
|
||||||
|
python3 InvenTree/manage.py makemigrations common
|
||||||
python3 InvenTree/manage.py makemigrations company
|
python3 InvenTree/manage.py makemigrations company
|
||||||
python3 InvenTree/manage.py makemigrations part
|
python3 InvenTree/manage.py makemigrations part
|
||||||
python3 InvenTree/manage.py makemigrations stock
|
python3 InvenTree/manage.py makemigrations stock
|
||||||
@ -40,12 +41,12 @@ style:
|
|||||||
# Run unit tests
|
# Run unit tests
|
||||||
test:
|
test:
|
||||||
python3 InvenTree/manage.py check
|
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
|
# Run code coverage
|
||||||
coverage:
|
coverage:
|
||||||
python3 InvenTree/manage.py check
|
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
|
coverage html
|
||||||
|
|
||||||
# Install packages required to generate code docs
|
# Install packages required to generate code docs
|
||||||
|
Loading…
x
Reference in New Issue
Block a user