mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-29 12:06:44 +00:00
Remove common_currency model entirely
- A lot of views / pages / etc needed to be updated too - Now uses django-money fields entirely - Create a manual rate exchange backend (needs more work!)
This commit is contained in:
parent
1fc2ef5f18
commit
4dff18e4a6
21
InvenTree/InvenTree/exchange.py
Normal file
21
InvenTree/InvenTree/exchange.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
from djmoney.contrib.exchange.backends.base import BaseExchangeBackend
|
||||||
|
|
||||||
|
|
||||||
|
class InvenTreeManualExchangeBackend(BaseExchangeBackend):
|
||||||
|
"""
|
||||||
|
Backend for manually updating currency exchange rates
|
||||||
|
|
||||||
|
See the documentation for django-money: https://github.com/django-money/django-money
|
||||||
|
|
||||||
|
Specifically: https://github.com/django-money/django-money/tree/master/djmoney/contrib/exchange/backends
|
||||||
|
"""
|
||||||
|
|
||||||
|
name = "inventree"
|
||||||
|
url = None
|
||||||
|
|
||||||
|
def get_rates(self, **kwargs):
|
||||||
|
"""
|
||||||
|
Do not get any rates...
|
||||||
|
"""
|
||||||
|
|
||||||
|
return {}
|
@ -156,6 +156,7 @@ INSTALLED_APPS = [
|
|||||||
'django_tex', # LaTeX output
|
'django_tex', # LaTeX output
|
||||||
'django_admin_shell', # Python shell for the admin interface
|
'django_admin_shell', # Python shell for the admin interface
|
||||||
'djmoney', # django-money integration
|
'djmoney', # django-money integration
|
||||||
|
'djmoney.contrib.exchange', # django-money exchange rates
|
||||||
]
|
]
|
||||||
|
|
||||||
LOGGING = {
|
LOGGING = {
|
||||||
@ -360,6 +361,9 @@ CURRENCIES = CONFIG.get(
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# TODO - Allow live web-based backends in the future
|
||||||
|
EXCHANGE_BACKEND = 'InvenTree.exchange.InvenTreeManualExchangeBackend'
|
||||||
|
|
||||||
LOCALE_PATHS = (
|
LOCALE_PATHS = (
|
||||||
os.path.join(BASE_DIR, 'locale/'),
|
os.path.join(BASE_DIR, 'locale/'),
|
||||||
)
|
)
|
||||||
|
@ -74,7 +74,6 @@ settings_urls = [
|
|||||||
url(r'^theme/?', ColorThemeSelectView.as_view(), name='settings-theme'),
|
url(r'^theme/?', ColorThemeSelectView.as_view(), name='settings-theme'),
|
||||||
|
|
||||||
url(r'^global/?', SettingsView.as_view(template_name='InvenTree/settings/global.html'), name='settings-global'),
|
url(r'^global/?', SettingsView.as_view(template_name='InvenTree/settings/global.html'), name='settings-global'),
|
||||||
url(r'^currency/?', SettingsView.as_view(template_name='InvenTree/settings/currency.html'), name='settings-currency'),
|
|
||||||
url(r'^part/?', SettingsView.as_view(template_name='InvenTree/settings/part.html'), name='settings-part'),
|
url(r'^part/?', SettingsView.as_view(template_name='InvenTree/settings/part.html'), name='settings-part'),
|
||||||
url(r'^stock/?', SettingsView.as_view(template_name='InvenTree/settings/stock.html'), name='settings-stock'),
|
url(r'^stock/?', SettingsView.as_view(template_name='InvenTree/settings/stock.html'), name='settings-stock'),
|
||||||
url(r'^build/?', SettingsView.as_view(template_name='InvenTree/settings/build.html'), name='settings-build'),
|
url(r'^build/?', SettingsView.as_view(template_name='InvenTree/settings/build.html'), name='settings-build'),
|
||||||
|
@ -5,11 +5,7 @@ from django.contrib import admin
|
|||||||
|
|
||||||
from import_export.admin import ImportExportModelAdmin
|
from import_export.admin import ImportExportModelAdmin
|
||||||
|
|
||||||
from .models import Currency, InvenTreeSetting
|
from .models import InvenTreeSetting
|
||||||
|
|
||||||
|
|
||||||
class CurrencyAdmin(ImportExportModelAdmin):
|
|
||||||
list_display = ('symbol', 'suffix', 'description', 'value', 'base')
|
|
||||||
|
|
||||||
|
|
||||||
class SettingsAdmin(ImportExportModelAdmin):
|
class SettingsAdmin(ImportExportModelAdmin):
|
||||||
@ -17,5 +13,4 @@ class SettingsAdmin(ImportExportModelAdmin):
|
|||||||
list_display = ('key', 'value')
|
list_display = ('key', 'value')
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(Currency, CurrencyAdmin)
|
|
||||||
admin.site.register(InvenTreeSetting, SettingsAdmin)
|
admin.site.register(InvenTreeSetting, SettingsAdmin)
|
||||||
|
@ -5,35 +5,5 @@ Provides a JSON API for common components.
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from rest_framework import permissions, generics, filters
|
|
||||||
|
|
||||||
from django.conf.urls import url
|
|
||||||
|
|
||||||
from .models import Currency
|
|
||||||
from .serializers import CurrencySerializer
|
|
||||||
|
|
||||||
|
|
||||||
class CurrencyList(generics.ListCreateAPIView):
|
|
||||||
""" API endpoint for accessing a list of Currency objects.
|
|
||||||
|
|
||||||
- GET: Return a list of Currencies
|
|
||||||
- POST: Create a new currency
|
|
||||||
"""
|
|
||||||
|
|
||||||
queryset = Currency.objects.all()
|
|
||||||
serializer_class = CurrencySerializer
|
|
||||||
|
|
||||||
permission_classes = [
|
|
||||||
permissions.IsAuthenticated,
|
|
||||||
]
|
|
||||||
|
|
||||||
filter_backends = [
|
|
||||||
filters.OrderingFilter,
|
|
||||||
]
|
|
||||||
|
|
||||||
ordering_fields = ['suffix', 'value']
|
|
||||||
|
|
||||||
|
|
||||||
common_api_urls = [
|
common_api_urls = [
|
||||||
url(r'^currency/?$', CurrencyList.as_view(), name='api-currency-list'),
|
|
||||||
]
|
]
|
||||||
|
@ -7,21 +7,7 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
from InvenTree.forms import HelperForm
|
from InvenTree.forms import HelperForm
|
||||||
|
|
||||||
from .models import Currency, InvenTreeSetting
|
from .models import InvenTreeSetting
|
||||||
|
|
||||||
|
|
||||||
class CurrencyEditForm(HelperForm):
|
|
||||||
""" Form for creating / editing a currency object """
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Currency
|
|
||||||
fields = [
|
|
||||||
'symbol',
|
|
||||||
'suffix',
|
|
||||||
'description',
|
|
||||||
'value',
|
|
||||||
'base'
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class SettingEditForm(HelperForm):
|
class SettingEditForm(HelperForm):
|
||||||
|
18
InvenTree/common/migrations/0009_delete_currency.py
Normal file
18
InvenTree/common/migrations/0009_delete_currency.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 3.0.7 on 2020-11-10 11:40
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('company', '0027_remove_supplierpricebreak_currency'),
|
||||||
|
('part', '0057_remove_partsellpricebreak_currency'),
|
||||||
|
('common', '0008_remove_inventreesetting_description'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='Currency',
|
||||||
|
),
|
||||||
|
]
|
@ -7,16 +7,18 @@ These models are 'generic' and do not fit a particular business logic object.
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import decimal
|
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
import djmoney.settings
|
import djmoney.settings
|
||||||
from djmoney.models.fields import MoneyField
|
from djmoney.models.fields import MoneyField
|
||||||
|
from djmoney.money import Money
|
||||||
|
from djmoney.contrib.exchange.models import convert_money
|
||||||
|
from djmoney.contrib.exchange.exceptions import MissingRate
|
||||||
|
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.core.validators import MinValueValidator, MaxValueValidator
|
from django.core.validators import MinValueValidator
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
import InvenTree.helpers
|
import InvenTree.helpers
|
||||||
@ -455,74 +457,6 @@ class InvenTreeSetting(models.Model):
|
|||||||
return InvenTree.helpers.str2bool(self.value)
|
return InvenTree.helpers.str2bool(self.value)
|
||||||
|
|
||||||
|
|
||||||
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.exclude(pk=self.pk).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)
|
|
||||||
|
|
||||||
|
|
||||||
class PriceBreak(models.Model):
|
class PriceBreak(models.Model):
|
||||||
"""
|
"""
|
||||||
Represents a PriceBreak model
|
Represents a PriceBreak model
|
||||||
@ -533,10 +467,6 @@ class PriceBreak(models.Model):
|
|||||||
|
|
||||||
quantity = InvenTree.fields.RoundingDecimalField(max_digits=15, decimal_places=5, default=1, validators=[MinValueValidator(1)])
|
quantity = InvenTree.fields.RoundingDecimalField(max_digits=15, decimal_places=5, default=1, validators=[MinValueValidator(1)])
|
||||||
|
|
||||||
cost = InvenTree.fields.RoundingDecimalField(max_digits=10, decimal_places=5, validators=[MinValueValidator(0)])
|
|
||||||
|
|
||||||
currency = models.ForeignKey(Currency, blank=True, null=True, on_delete=models.SET_NULL)
|
|
||||||
|
|
||||||
price = MoneyField(
|
price = MoneyField(
|
||||||
max_digits=19,
|
max_digits=19,
|
||||||
decimal_places=4,
|
decimal_places=4,
|
||||||
@ -546,26 +476,21 @@ class PriceBreak(models.Model):
|
|||||||
help_text=_('Unit price at specified quantity'),
|
help_text=_('Unit price at specified quantity'),
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
def convert_to(self, currency_code):
|
||||||
def symbol(self):
|
|
||||||
return self.currency.symbol if self.currency else ''
|
|
||||||
|
|
||||||
@property
|
|
||||||
def suffix(self):
|
|
||||||
return self.currency.suffix if self.currency else ''
|
|
||||||
|
|
||||||
@property
|
|
||||||
def converted_cost(self):
|
|
||||||
"""
|
"""
|
||||||
Return the cost of this price break, converted to the base currency
|
Convert the unit-price at this price break to the specified currency code.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
currency_code - The currency code to convert to (e.g "USD" or "AUD")
|
||||||
"""
|
"""
|
||||||
|
|
||||||
scaler = decimal.Decimal(1.0)
|
try:
|
||||||
|
converted = convert_money(self.price, currency_code)
|
||||||
|
except MissingRate:
|
||||||
|
print(f"WARNING: No currency conversion rate available for {self.price_currency} -> {currency_code}")
|
||||||
|
return self.price.amount
|
||||||
|
|
||||||
if self.currency:
|
return converted.amount
|
||||||
scaler = self.currency.value
|
|
||||||
|
|
||||||
return self.cost * scaler
|
|
||||||
|
|
||||||
|
|
||||||
class ColorTheme(models.Model):
|
class ColorTheme(models.Model):
|
||||||
|
@ -1,22 +1,3 @@
|
|||||||
"""
|
"""
|
||||||
JSON serializers for common components
|
JSON serializers for common components
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from .models import Currency
|
|
||||||
|
|
||||||
from InvenTree.serializers import InvenTreeModelSerializer
|
|
||||||
|
|
||||||
|
|
||||||
class CurrencySerializer(InvenTreeModelSerializer):
|
|
||||||
""" Serializer for Currency object """
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Currency
|
|
||||||
fields = [
|
|
||||||
'pk',
|
|
||||||
'symbol',
|
|
||||||
'suffix',
|
|
||||||
'description',
|
|
||||||
'value',
|
|
||||||
'base'
|
|
||||||
]
|
|
||||||
|
@ -4,7 +4,7 @@ from __future__ import unicode_literals
|
|||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
|
|
||||||
from .models import Currency, InvenTreeSetting
|
from .models import InvenTreeSetting
|
||||||
|
|
||||||
|
|
||||||
class CurrencyTest(TestCase):
|
class CurrencyTest(TestCase):
|
||||||
|
@ -2,17 +2,5 @@
|
|||||||
URL lookup for common views
|
URL lookup for common views
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.conf.urls import url, include
|
|
||||||
|
|
||||||
from . import views
|
|
||||||
|
|
||||||
currency_urls = [
|
|
||||||
url(r'^new/', views.CurrencyCreate.as_view(), name='currency-create'),
|
|
||||||
|
|
||||||
url(r'^(?P<pk>\d+)/edit/', views.CurrencyEdit.as_view(), name='currency-edit'),
|
|
||||||
url(r'^(?P<pk>\d+)/delete/', views.CurrencyDelete.as_view(), name='currency-delete'),
|
|
||||||
]
|
|
||||||
|
|
||||||
common_urls = [
|
common_urls = [
|
||||||
url(r'currency/', include(currency_urls)),
|
|
||||||
]
|
]
|
||||||
|
@ -8,37 +8,13 @@ from __future__ import unicode_literals
|
|||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.forms import CheckboxInput, Select
|
from django.forms import CheckboxInput, Select
|
||||||
|
|
||||||
from InvenTree.views import AjaxCreateView, AjaxUpdateView, AjaxDeleteView
|
from InvenTree.views import AjaxUpdateView
|
||||||
from InvenTree.helpers import str2bool
|
from InvenTree.helpers import str2bool
|
||||||
|
|
||||||
from . import models
|
from . import models
|
||||||
from . import forms
|
from . import forms
|
||||||
|
|
||||||
|
|
||||||
class CurrencyCreate(AjaxCreateView):
|
|
||||||
""" View for creating a new Currency object """
|
|
||||||
|
|
||||||
model = models.Currency
|
|
||||||
form_class = forms.CurrencyEditForm
|
|
||||||
ajax_form_title = _('Create new Currency')
|
|
||||||
|
|
||||||
|
|
||||||
class CurrencyEdit(AjaxUpdateView):
|
|
||||||
""" View for editing an existing Currency object """
|
|
||||||
|
|
||||||
model = models.Currency
|
|
||||||
form_class = forms.CurrencyEditForm
|
|
||||||
ajax_form_title = _('Edit Currency')
|
|
||||||
|
|
||||||
|
|
||||||
class CurrencyDelete(AjaxDeleteView):
|
|
||||||
""" View for deleting an existing Currency object """
|
|
||||||
|
|
||||||
model = models.Currency
|
|
||||||
ajax_form_title = _('Delete Currency')
|
|
||||||
ajax_template_name = "common/delete_currency.html"
|
|
||||||
|
|
||||||
|
|
||||||
class SettingEdit(AjaxUpdateView):
|
class SettingEdit(AjaxUpdateView):
|
||||||
"""
|
"""
|
||||||
View for editing an InvenTree key:value settings object,
|
View for editing an InvenTree key:value settings object,
|
||||||
|
@ -13,7 +13,6 @@ from .models import SupplierPart
|
|||||||
from .models import SupplierPriceBreak
|
from .models import SupplierPriceBreak
|
||||||
|
|
||||||
from part.models import Part
|
from part.models import Part
|
||||||
from common.models import Currency
|
|
||||||
|
|
||||||
|
|
||||||
class CompanyResource(ModelResource):
|
class CompanyResource(ModelResource):
|
||||||
@ -75,8 +74,6 @@ class SupplierPriceBreakResource(ModelResource):
|
|||||||
|
|
||||||
part = Field(attribute='part', widget=widgets.ForeignKeyWidget(SupplierPart))
|
part = Field(attribute='part', widget=widgets.ForeignKeyWidget(SupplierPart))
|
||||||
|
|
||||||
currency = Field(attribute='currency', widget=widgets.ForeignKeyWidget(Currency))
|
|
||||||
|
|
||||||
supplier_id = Field(attribute='part__supplier__pk', readonly=True)
|
supplier_id = Field(attribute='part__supplier__pk', readonly=True)
|
||||||
|
|
||||||
supplier_name = Field(attribute='part__supplier__name', readonly=True)
|
supplier_name = Field(attribute='part__supplier__name', readonly=True)
|
||||||
@ -98,7 +95,7 @@ class SupplierPriceBreakAdmin(ImportExportModelAdmin):
|
|||||||
|
|
||||||
resource_class = SupplierPriceBreakResource
|
resource_class = SupplierPriceBreakResource
|
||||||
|
|
||||||
list_display = ('part', 'quantity', 'cost')
|
list_display = ('part', 'quantity', 'price')
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(Company, CompanyAdmin)
|
admin.site.register(Company, CompanyAdmin)
|
||||||
|
@ -7,21 +7,21 @@
|
|||||||
fields:
|
fields:
|
||||||
part: 1
|
part: 1
|
||||||
quantity: 1
|
quantity: 1
|
||||||
cost: 10
|
price: 10
|
||||||
|
|
||||||
- model: company.supplierpricebreak
|
- model: company.supplierpricebreak
|
||||||
pk: 2
|
pk: 2
|
||||||
fields:
|
fields:
|
||||||
part: 1
|
part: 1
|
||||||
quantity: 5
|
quantity: 5
|
||||||
cost: 7.50
|
price: 7.50
|
||||||
|
|
||||||
- model: company.supplierpricebreak
|
- model: company.supplierpricebreak
|
||||||
pk: 3
|
pk: 3
|
||||||
fields:
|
fields:
|
||||||
part: 1
|
part: 1
|
||||||
quantity: 25
|
quantity: 25
|
||||||
cost: 3.50
|
price: 3.50
|
||||||
|
|
||||||
# Price breaks for ACME0002
|
# Price breaks for ACME0002
|
||||||
- model: company.supplierpricebreak
|
- model: company.supplierpricebreak
|
||||||
@ -29,14 +29,14 @@
|
|||||||
fields:
|
fields:
|
||||||
part: 2
|
part: 2
|
||||||
quantity: 5
|
quantity: 5
|
||||||
cost: 7.00
|
price: 7.00
|
||||||
|
|
||||||
- model: company.supplierpricebreak
|
- model: company.supplierpricebreak
|
||||||
pk: 5
|
pk: 5
|
||||||
fields:
|
fields:
|
||||||
part: 2
|
part: 2
|
||||||
quantity: 50
|
quantity: 50
|
||||||
cost: 1.25
|
price: 1.25
|
||||||
|
|
||||||
# Price breaks for ZERGLPHS
|
# Price breaks for ZERGLPHS
|
||||||
- model: company.supplierpricebreak
|
- model: company.supplierpricebreak
|
||||||
@ -44,11 +44,11 @@
|
|||||||
fields:
|
fields:
|
||||||
part: 4
|
part: 4
|
||||||
quantity: 25
|
quantity: 25
|
||||||
cost: 8
|
price: 8
|
||||||
|
|
||||||
- model: company.supplierpricebreak
|
- model: company.supplierpricebreak
|
||||||
pk: 7
|
pk: 7
|
||||||
fields:
|
fields:
|
||||||
part: 4
|
part: 4
|
||||||
quantity: 100
|
quantity: 100
|
||||||
cost: 1.25
|
price: 1.25
|
@ -82,13 +82,10 @@ class EditPriceBreakForm(HelperForm):
|
|||||||
|
|
||||||
quantity = RoundingDecimalFormField(max_digits=10, decimal_places=5)
|
quantity = RoundingDecimalFormField(max_digits=10, decimal_places=5)
|
||||||
|
|
||||||
cost = RoundingDecimalFormField(max_digits=10, decimal_places=5)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = SupplierPriceBreak
|
model = SupplierPriceBreak
|
||||||
fields = [
|
fields = [
|
||||||
'part',
|
'part',
|
||||||
'quantity',
|
'quantity',
|
||||||
'cost',
|
'price',
|
||||||
'currency',
|
|
||||||
]
|
]
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
# Generated by Django 3.0.7 on 2020-11-10 11:40
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('company', '0026_auto_20201110_1011'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='supplierpricebreak',
|
||||||
|
name='currency',
|
||||||
|
),
|
||||||
|
]
|
@ -0,0 +1,17 @@
|
|||||||
|
# Generated by Django 3.0.7 on 2020-11-10 11:42
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('company', '0027_remove_supplierpricebreak_currency'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='supplierpricebreak',
|
||||||
|
name='cost',
|
||||||
|
),
|
||||||
|
]
|
@ -350,7 +350,7 @@ class SupplierPart(models.Model):
|
|||||||
def unit_pricing(self):
|
def unit_pricing(self):
|
||||||
return self.get_price(1)
|
return self.get_price(1)
|
||||||
|
|
||||||
def get_price(self, quantity, moq=True, multiples=True):
|
def get_price(self, quantity, moq=True, multiples=True, currency=None):
|
||||||
""" Calculate the supplier price based on quantity price breaks.
|
""" Calculate the supplier price based on quantity price breaks.
|
||||||
|
|
||||||
- Don't forget to add in flat-fee cost (base_cost field)
|
- Don't forget to add in flat-fee cost (base_cost field)
|
||||||
@ -372,6 +372,10 @@ class SupplierPart(models.Model):
|
|||||||
pb_quantity = -1
|
pb_quantity = -1
|
||||||
pb_cost = 0.0
|
pb_cost = 0.0
|
||||||
|
|
||||||
|
if currency is None:
|
||||||
|
# Default currency selection
|
||||||
|
currency = common.models.InvenTreeSetting.get_setting('INVENTREE_DEFAULT_CURRENCY')
|
||||||
|
|
||||||
for pb in self.price_breaks.all():
|
for pb in self.price_breaks.all():
|
||||||
# Ignore this pricebreak (quantity is too high)
|
# Ignore this pricebreak (quantity is too high)
|
||||||
if pb.quantity > quantity:
|
if pb.quantity > quantity:
|
||||||
@ -382,8 +386,9 @@ 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
|
||||||
# Convert everything to base currency
|
|
||||||
pb_cost = pb.converted_cost
|
# Convert everything to the selected currency
|
||||||
|
pb_cost = pb.convert_to(currency)
|
||||||
|
|
||||||
if pb_found:
|
if pb_found:
|
||||||
cost = pb_cost * quantity
|
cost = pb_cost * quantity
|
||||||
@ -462,7 +467,4 @@ class SupplierPriceBreak(common.models.PriceBreak):
|
|||||||
db_table = 'part_supplierpricebreak'
|
db_table = 'part_supplierpricebreak'
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "{mpn} - {cost} @ {quan}".format(
|
return f'{self.part.MPN} - {self.price} @ {self.quantity}'
|
||||||
mpn=self.part.MPN,
|
|
||||||
cost=self.cost,
|
|
||||||
quan=self.quantity)
|
|
||||||
|
@ -137,13 +137,9 @@ class SupplierPartSerializer(InvenTreeModelSerializer):
|
|||||||
class SupplierPriceBreakSerializer(InvenTreeModelSerializer):
|
class SupplierPriceBreakSerializer(InvenTreeModelSerializer):
|
||||||
""" Serializer for SupplierPriceBreak object """
|
""" Serializer for SupplierPriceBreak object """
|
||||||
|
|
||||||
symbol = serializers.CharField(read_only=True)
|
|
||||||
|
|
||||||
suffix = serializers.CharField(read_only=True)
|
|
||||||
|
|
||||||
quantity = serializers.FloatField()
|
quantity = serializers.FloatField()
|
||||||
|
|
||||||
cost = serializers.FloatField()
|
price = serializers.CharField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = SupplierPriceBreak
|
model = SupplierPriceBreak
|
||||||
@ -151,8 +147,5 @@ class SupplierPriceBreakSerializer(InvenTreeModelSerializer):
|
|||||||
'pk',
|
'pk',
|
||||||
'part',
|
'part',
|
||||||
'quantity',
|
'quantity',
|
||||||
'cost',
|
'price',
|
||||||
'currency',
|
|
||||||
'symbol',
|
|
||||||
'suffix',
|
|
||||||
]
|
]
|
||||||
|
@ -76,18 +76,11 @@ $('#price-break-table').inventreeTable({
|
|||||||
sortable: true,
|
sortable: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'cost',
|
field: 'price',
|
||||||
title: '{% trans "Price" %}',
|
title: '{% trans "Price" %}',
|
||||||
sortable: true,
|
sortable: true,
|
||||||
formatter: function(value, row, index) {
|
formatter: function(value, row, index) {
|
||||||
var html = '';
|
var html = value;
|
||||||
|
|
||||||
html += row.symbol || '';
|
|
||||||
html += value;
|
|
||||||
|
|
||||||
if (row.suffix) {
|
|
||||||
html += ' ' + row.suffix || '';
|
|
||||||
}
|
|
||||||
|
|
||||||
html += `<div class='btn-group float-right' role='group'>`
|
html += `<div class='btn-group float-right' role='group'>`
|
||||||
|
|
||||||
|
@ -6,6 +6,8 @@ from .models import Company, Contact, SupplierPart
|
|||||||
from .models import rename_company_image
|
from .models import rename_company_image
|
||||||
from part.models import Part
|
from part.models import Part
|
||||||
|
|
||||||
|
from InvenTree.exchange import InvenTreeManualExchangeBackend
|
||||||
|
from djmoney.contrib.exchange.models import Rate
|
||||||
|
|
||||||
class CompanySimpleTest(TestCase):
|
class CompanySimpleTest(TestCase):
|
||||||
|
|
||||||
@ -32,6 +34,14 @@ class CompanySimpleTest(TestCase):
|
|||||||
self.zerglphs = SupplierPart.objects.get(SKU='ZERGLPHS')
|
self.zerglphs = SupplierPart.objects.get(SKU='ZERGLPHS')
|
||||||
self.zergm312 = SupplierPart.objects.get(SKU='ZERGM312')
|
self.zergm312 = SupplierPart.objects.get(SKU='ZERGM312')
|
||||||
|
|
||||||
|
InvenTreeManualExchangeBackend().update_rates()
|
||||||
|
|
||||||
|
Rate.objects.create(
|
||||||
|
currency='AUD',
|
||||||
|
value='1.35',
|
||||||
|
backend_id='inventree',
|
||||||
|
)
|
||||||
|
|
||||||
def test_company_model(self):
|
def test_company_model(self):
|
||||||
c = Company.objects.get(name='ABC Co.')
|
c = Company.objects.get(name='ABC Co.')
|
||||||
self.assertEqual(c.name, 'ABC Co.')
|
self.assertEqual(c.name, 'ABC Co.')
|
||||||
|
@ -12,12 +12,12 @@ from django.views.generic import DetailView, ListView, UpdateView
|
|||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.forms import HiddenInput
|
from django.forms import HiddenInput
|
||||||
|
|
||||||
|
from moneyed import CURRENCIES
|
||||||
|
|
||||||
from InvenTree.views import AjaxCreateView, AjaxUpdateView, AjaxDeleteView
|
from InvenTree.views import AjaxCreateView, AjaxUpdateView, AjaxDeleteView
|
||||||
from InvenTree.helpers import str2bool
|
from InvenTree.helpers import str2bool
|
||||||
from InvenTree.views import InvenTreeRoleMixin
|
from InvenTree.views import InvenTreeRoleMixin
|
||||||
|
|
||||||
from common.models import Currency
|
|
||||||
|
|
||||||
from .models import Company
|
from .models import Company
|
||||||
from .models import SupplierPart
|
from .models import SupplierPart
|
||||||
from .models import SupplierPriceBreak
|
from .models import SupplierPriceBreak
|
||||||
@ -29,6 +29,8 @@ from .forms import CompanyImageForm
|
|||||||
from .forms import EditSupplierPartForm
|
from .forms import EditSupplierPartForm
|
||||||
from .forms import EditPriceBreakForm
|
from .forms import EditPriceBreakForm
|
||||||
|
|
||||||
|
import common.models
|
||||||
|
|
||||||
|
|
||||||
class CompanyIndex(InvenTreeRoleMixin, ListView):
|
class CompanyIndex(InvenTreeRoleMixin, ListView):
|
||||||
""" View for displaying list of companies
|
""" View for displaying list of companies
|
||||||
@ -435,12 +437,11 @@ class PriceBreakCreate(AjaxCreateView):
|
|||||||
|
|
||||||
initials['part'] = self.get_part()
|
initials['part'] = self.get_part()
|
||||||
|
|
||||||
# Pre-select the default currency
|
default_currency = common.models.InvenTreeSetting.get_setting('INVENTREE_DEFAULT_CURRENCY')
|
||||||
try:
|
currency = CURRENCIES.get(default_currency, None)
|
||||||
base = Currency.objects.get(base=True)
|
|
||||||
initials['currency'] = base
|
if currency is not None:
|
||||||
except Currency.DoesNotExist:
|
initials['price'] = [1.0, currency]
|
||||||
pass
|
|
||||||
|
|
||||||
return initials
|
return initials
|
||||||
|
|
||||||
|
@ -279,7 +279,7 @@ class PartSellPriceBreakAdmin(admin.ModelAdmin):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = PartSellPriceBreak
|
model = PartSellPriceBreak
|
||||||
|
|
||||||
list_display = ('part', 'quantity', 'cost', 'currency')
|
list_display = ('part', 'quantity', 'price',)
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(Part, PartAdmin)
|
admin.site.register(Part, PartAdmin)
|
||||||
|
@ -19,8 +19,6 @@ from .models import PartParameterTemplate, PartParameter
|
|||||||
from .models import PartTestTemplate
|
from .models import PartTestTemplate
|
||||||
from .models import PartSellPriceBreak
|
from .models import PartSellPriceBreak
|
||||||
|
|
||||||
from common.models import Currency
|
|
||||||
|
|
||||||
|
|
||||||
class PartModelChoiceField(forms.ModelChoiceField):
|
class PartModelChoiceField(forms.ModelChoiceField):
|
||||||
""" Extending string representation of Part instance with available stock """
|
""" Extending string representation of Part instance with available stock """
|
||||||
@ -298,13 +296,10 @@ class PartPriceForm(forms.Form):
|
|||||||
help_text=_('Input quantity for price calculation')
|
help_text=_('Input quantity for price calculation')
|
||||||
)
|
)
|
||||||
|
|
||||||
currency = forms.ModelChoiceField(queryset=Currency.objects.all(), label='Currency', help_text=_('Select currency for price calculation'))
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Part
|
model = Part
|
||||||
fields = [
|
fields = [
|
||||||
'quantity',
|
'quantity',
|
||||||
'currency',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -315,13 +310,10 @@ class EditPartSalePriceBreakForm(HelperForm):
|
|||||||
|
|
||||||
quantity = RoundingDecimalFormField(max_digits=10, decimal_places=5)
|
quantity = RoundingDecimalFormField(max_digits=10, decimal_places=5)
|
||||||
|
|
||||||
cost = RoundingDecimalFormField(max_digits=10, decimal_places=5)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = PartSellPriceBreak
|
model = PartSellPriceBreak
|
||||||
fields = [
|
fields = [
|
||||||
'part',
|
'part',
|
||||||
'quantity',
|
'quantity',
|
||||||
'cost',
|
'price',
|
||||||
'currency',
|
|
||||||
]
|
]
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
# Generated by Django 3.0.7 on 2020-11-10 11:40
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('part', '0056_auto_20201110_1125'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='partsellpricebreak',
|
||||||
|
name='currency',
|
||||||
|
),
|
||||||
|
]
|
@ -0,0 +1,17 @@
|
|||||||
|
# Generated by Django 3.0.7 on 2020-11-10 11:42
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('part', '0057_remove_partsellpricebreak_currency'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='partsellpricebreak',
|
||||||
|
name='cost',
|
||||||
|
),
|
||||||
|
]
|
@ -84,13 +84,9 @@ class PartSalePriceSerializer(InvenTreeModelSerializer):
|
|||||||
Serializer for sale prices for Part model.
|
Serializer for sale prices for Part model.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
symbol = serializers.CharField(read_only=True)
|
|
||||||
|
|
||||||
suffix = serializers.CharField(read_only=True)
|
|
||||||
|
|
||||||
quantity = serializers.FloatField()
|
quantity = serializers.FloatField()
|
||||||
|
|
||||||
cost = serializers.FloatField()
|
price = serializers.CharField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = PartSellPriceBreak
|
model = PartSellPriceBreak
|
||||||
@ -98,10 +94,7 @@ class PartSalePriceSerializer(InvenTreeModelSerializer):
|
|||||||
'pk',
|
'pk',
|
||||||
'part',
|
'part',
|
||||||
'quantity',
|
'quantity',
|
||||||
'cost',
|
'price',
|
||||||
'currency',
|
|
||||||
'symbol',
|
|
||||||
'suffix',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -10,7 +10,9 @@
|
|||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<div id='price-break-toolbar' class='btn-group'>
|
<div id='price-break-toolbar' class='btn-group'>
|
||||||
<button class='btn btn-primary' id='new-price-break' type='button'>{% trans "Add Price Break" %}</button>
|
<button class='btn btn-primary' id='new-price-break' type='button'>
|
||||||
|
<span class='fas fa-plus-circle'></span> {% trans "Add Price Break" %}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<table class='table table-striped table-condensed' id='price-break-table' data-toolbar='#price-break-toolbar'>
|
<table class='table table-striped table-condensed' id='price-break-table' data-toolbar='#price-break-toolbar'>
|
||||||
@ -81,18 +83,11 @@ $('#price-break-table').inventreeTable({
|
|||||||
sortable: true,
|
sortable: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'cost',
|
field: 'price',
|
||||||
title: '{% trans "Price" %}',
|
title: '{% trans "Price" %}',
|
||||||
sortable: true,
|
sortable: true,
|
||||||
formatter: function(value, row, index) {
|
formatter: function(value, row, index) {
|
||||||
var html = '';
|
var html = value;
|
||||||
|
|
||||||
html += row.symbol || '';
|
|
||||||
html += value;
|
|
||||||
|
|
||||||
if (row.suffix) {
|
|
||||||
html += ' ' + row.suffix || '';
|
|
||||||
}
|
|
||||||
|
|
||||||
html += `<div class='btn-group float-right' role='group'>`
|
html += `<div class='btn-group float-right' role='group'>`
|
||||||
|
|
||||||
|
@ -16,6 +16,8 @@ from django.forms.models import model_to_dict
|
|||||||
from django.forms import HiddenInput, CheckboxInput
|
from django.forms import HiddenInput, CheckboxInput
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
|
from moneyed import CURRENCIES
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from rapidfuzz import fuzz
|
from rapidfuzz import fuzz
|
||||||
@ -28,7 +30,7 @@ from .models import match_part_names
|
|||||||
from .models import PartTestTemplate
|
from .models import PartTestTemplate
|
||||||
from .models import PartSellPriceBreak
|
from .models import PartSellPriceBreak
|
||||||
|
|
||||||
from common.models import Currency, InvenTreeSetting
|
from common.models import InvenTreeSetting
|
||||||
from company.models import SupplierPart
|
from company.models import SupplierPart
|
||||||
|
|
||||||
from . import forms as part_forms
|
from . import forms as part_forms
|
||||||
@ -1860,19 +1862,12 @@ class PartPricing(AjaxView):
|
|||||||
if quantity < 1:
|
if quantity < 1:
|
||||||
quantity = 1
|
quantity = 1
|
||||||
|
|
||||||
if currency is None:
|
# TODO - Capacity for price comparison in different currencies
|
||||||
# No currency selected? Try to select a default one
|
currency = None
|
||||||
try:
|
|
||||||
currency = Currency.objects.get(base=1)
|
|
||||||
except Currency.DoesNotExist:
|
|
||||||
currency = None
|
|
||||||
|
|
||||||
# Currency scaler
|
# Currency scaler
|
||||||
scaler = Decimal(1.0)
|
scaler = Decimal(1.0)
|
||||||
|
|
||||||
if currency is not None:
|
|
||||||
scaler = Decimal(currency.value)
|
|
||||||
|
|
||||||
part = self.get_part()
|
part = self.get_part()
|
||||||
|
|
||||||
ctx = {
|
ctx = {
|
||||||
@ -1942,13 +1937,8 @@ class PartPricing(AjaxView):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
quantity = 1
|
quantity = 1
|
||||||
|
|
||||||
try:
|
# TODO - How to handle pricing in different currencies?
|
||||||
currency_id = int(self.request.POST.get('currency', None))
|
currency = None
|
||||||
|
|
||||||
if currency_id:
|
|
||||||
currency = Currency.objects.get(pk=currency_id)
|
|
||||||
except (ValueError, Currency.DoesNotExist):
|
|
||||||
currency = None
|
|
||||||
|
|
||||||
# Always mark the form as 'invalid' (the user may wish to keep getting pricing data)
|
# Always mark the form as 'invalid' (the user may wish to keep getting pricing data)
|
||||||
data = {
|
data = {
|
||||||
@ -2393,12 +2383,11 @@ class PartSalePriceBreakCreate(AjaxCreateView):
|
|||||||
|
|
||||||
initials['part'] = self.get_part()
|
initials['part'] = self.get_part()
|
||||||
|
|
||||||
# Pre-select the default currency
|
default_currency = InvenTreeSetting.get_setting('INVENTREE_DEFAULT_CURRENCY')
|
||||||
try:
|
currency = CURRENCIES.get(default_currency, None)
|
||||||
base = Currency.objects.get(base=True)
|
|
||||||
initials['currency'] = base
|
if currency is not None:
|
||||||
except Currency.DoesNotExist:
|
initials['price'] = [1.0, currency]
|
||||||
pass
|
|
||||||
|
|
||||||
return initials
|
return initials
|
||||||
|
|
||||||
|
@ -1,118 +0,0 @@
|
|||||||
{% extends "InvenTree/settings/settings.html" %}
|
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
{% block subtitle %}
|
|
||||||
{% trans "General Settings" %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block tabs %}
|
|
||||||
{% include "InvenTree/settings/tabs.html" with tab='currency' %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block settings %}
|
|
||||||
|
|
||||||
<h4>{% trans "Currencies" %}</h4>
|
|
||||||
|
|
||||||
<div id='currency-buttons'>
|
|
||||||
<button class='btn btn-success' id='new-currency'>
|
|
||||||
<span class='fas fa-plus-circle'></span> {% trans "New Currency" %}</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<table class='table table-striped table-condensed' id='currency-table' data-toolbar='#currency-buttons'>
|
|
||||||
</table>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block js_ready %}
|
|
||||||
{{ block.super }}
|
|
||||||
|
|
||||||
$("#currency-table").inventreeTable({
|
|
||||||
url: "{% url 'api-currency-list' %}",
|
|
||||||
queryParams: {
|
|
||||||
ordering: 'suffix'
|
|
||||||
},
|
|
||||||
formatNoMatches: function() { return "No currencies found"; },
|
|
||||||
rowStyle: function(row, index) {
|
|
||||||
if (row.base) {
|
|
||||||
return {classes: 'basecurrency'};
|
|
||||||
} else {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
columns: [
|
|
||||||
{
|
|
||||||
field: 'pk',
|
|
||||||
title: 'ID',
|
|
||||||
visible: false,
|
|
||||||
switchable: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'symbol',
|
|
||||||
title: 'Symbol',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'suffix',
|
|
||||||
title: 'Currency',
|
|
||||||
sortable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'description',
|
|
||||||
title: 'Description',
|
|
||||||
sortable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'value',
|
|
||||||
title: 'Value',
|
|
||||||
sortable: true,
|
|
||||||
formatter: function(value, row, index, field) {
|
|
||||||
if (row.base) {
|
|
||||||
return "Base Currency";
|
|
||||||
} else {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
formatter: function(value, row, index, field) {
|
|
||||||
|
|
||||||
var bEdit = "<button title='Edit Currency' class='cur-edit btn btn-default btn-glyph' type='button' pk='" + row.pk + "'><span class='fas fa-edit'></span></button>";
|
|
||||||
var bDel = "<button title='Delete Currency' class='cur-delete btn btn-default btn-glyph' type='button' pk='" + row.pk + "'><span class='fas fa-trash-alt icon-red'></span></button>";
|
|
||||||
|
|
||||||
var html = "<div class='btn-group' role='group'>" + bEdit + bDel + "</div>";
|
|
||||||
|
|
||||||
return html;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#currency-table").on('click', '.cur-edit', function() {
|
|
||||||
var button = $(this);
|
|
||||||
var url = "/common/currency/" + button.attr('pk') + "/edit/";
|
|
||||||
|
|
||||||
launchModalForm(url, {
|
|
||||||
success: function() {
|
|
||||||
$("#currency-table").bootstrapTable('refresh');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#currency-table").on('click', '.cur-delete', function() {
|
|
||||||
var button = $(this);
|
|
||||||
var url = "/common/currency/" + button.attr('pk') + "/delete/";
|
|
||||||
|
|
||||||
launchModalForm(url, {
|
|
||||||
success: function() {
|
|
||||||
$("#currency-table").bootstrapTable('refresh');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#new-currency").click(function() {
|
|
||||||
launchModalForm("{% url 'currency-create' %}", {
|
|
||||||
success: function() {
|
|
||||||
$("#currency-table").bootstrapTable('refresh');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
{% endblock %}
|
|
@ -15,9 +15,6 @@
|
|||||||
<li {% if tab == 'global' %} class='active' {% endif %}>
|
<li {% if tab == 'global' %} class='active' {% endif %}>
|
||||||
<a href='{% url "settings-global" %}'><span class='fas fa-globe'></span> {% trans "Global" %}</a>
|
<a href='{% url "settings-global" %}'><span class='fas fa-globe'></span> {% trans "Global" %}</a>
|
||||||
</li>
|
</li>
|
||||||
<li{% ifequal tab 'currency' %} class='active'{% endifequal %}>
|
|
||||||
<a href="{% url 'settings-currency' %}"><span class='fas fa-dollar-sign'></span> {% trans "Currency" %}</a>
|
|
||||||
</li>
|
|
||||||
<li{% ifequal tab 'part' %} class='active'{% endifequal %}>
|
<li{% ifequal tab 'part' %} class='active'{% endifequal %}>
|
||||||
<a href="{% url 'settings-part' %}"><span class='fas fa-shapes'></span> {% trans "Parts" %}</a>
|
<a href="{% url 'settings-part' %}"><span class='fas fa-shapes'></span> {% trans "Parts" %}</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -102,7 +102,6 @@ class RuleSet(models.Model):
|
|||||||
|
|
||||||
# Models which currently do not require permissions
|
# Models which currently do not require permissions
|
||||||
'common_colortheme',
|
'common_colortheme',
|
||||||
'common_currency',
|
|
||||||
'common_inventreesetting',
|
'common_inventreesetting',
|
||||||
'company_contact',
|
'company_contact',
|
||||||
'label_stockitemlabel',
|
'label_stockitemlabel',
|
||||||
|
@ -27,5 +27,6 @@ django-weasyprint==1.0.1 # HTML PDF export
|
|||||||
django-debug-toolbar==2.2 # Debug / profiling toolbar
|
django-debug-toolbar==2.2 # Debug / profiling toolbar
|
||||||
django-admin-shell==0.1.2 # Python shell for the admin interface
|
django-admin-shell==0.1.2 # Python shell for the admin interface
|
||||||
django-money==1.1 # Django app for currency management
|
django-money==1.1 # Django app for currency management
|
||||||
|
certifi # Certifi is (most likely) installed through one of the requirements above
|
||||||
|
|
||||||
inventree # Install the latest version of the InvenTree API python library
|
inventree # Install the latest version of the InvenTree API python library
|
||||||
|
Loading…
x
Reference in New Issue
Block a user