diff --git a/pyproject.toml b/pyproject.toml index 3b6468b327..8fbcccf748 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -113,7 +113,7 @@ invalid-argument-type="ignore" # 49 possibly-unbound-attribute="ignore" # 25 # https://github.com/astral-sh/ty/issues/164 unknown-argument="ignore" # 3 # need to wait for betterdjango field stubs invalid-assignment="ignore" # 17 # need to wait for betterdjango field stubs - +no-matching-overload="ignore" # 3 # need to wait for betterdjango field stubs [tool.coverage.run] source = ["src/backend/InvenTree", "InvenTree"] diff --git a/src/backend/InvenTree/InvenTree/fields.py b/src/backend/InvenTree/InvenTree/fields.py index c1495488f7..b7c09dbe12 100644 --- a/src/backend/InvenTree/InvenTree/fields.py +++ b/src/backend/InvenTree/InvenTree/fields.py @@ -15,6 +15,8 @@ from rest_framework.fields import URLField as RestURLField from rest_framework.fields import empty import InvenTree.helpers +import InvenTree.ready +from common.currency import currency_code_default from common.settings import get_global_setting from .validators import AllowedURLValidator, allowable_url_schemes @@ -59,7 +61,7 @@ class InvenTreeURLField(models.URLField): def money_kwargs(**kwargs): """Returns the database settings for MoneyFields.""" - from common.currency import currency_code_default, currency_code_mappings + from common.currency import currency_code_mappings # Default values (if not specified) if 'max_digits' not in kwargs: @@ -71,8 +73,14 @@ def money_kwargs(**kwargs): if 'currency_choices' not in kwargs: kwargs['currency_choices'] = currency_code_mappings() - if 'default_currency' not in kwargs: - kwargs['default_currency'] = currency_code_default() + if InvenTree.ready.isRunningMigrations(): + # During migrations, avoid setting a default currency + # This prevents issues related to early evaluation of the default currency value + kwargs['default_currency'] = '' + else: + # Override default currency with a callable function + # This ensures that the default currency is always up-to-date + kwargs['default_currency'] = currency_code_default return kwargs diff --git a/src/backend/InvenTree/build/api.py b/src/backend/InvenTree/build/api.py index b7dfac9904..a7332428ca 100644 --- a/src/backend/InvenTree/build/api.py +++ b/src/backend/InvenTree/build/api.py @@ -9,7 +9,7 @@ from django.db.models import F, Q from django.urls import include, path from django.utils.translation import gettext_lazy as _ -from django_filters import rest_framework as rest_filters +import django_filters.rest_framework.filters as rest_filters from django_filters.rest_framework.filterset import FilterSet from drf_spectacular.utils import extend_schema, extend_schema_field from rest_framework import serializers, status diff --git a/src/backend/InvenTree/common/currency.py b/src/backend/InvenTree/common/currency.py index 307e54d6d5..63699748bb 100644 --- a/src/backend/InvenTree/common/currency.py +++ b/src/backend/InvenTree/common/currency.py @@ -11,6 +11,7 @@ import structlog from moneyed import CURRENCIES import InvenTree.helpers +import InvenTree.ready logger = structlog.get_logger('inventree') @@ -19,6 +20,9 @@ def currency_code_default(create: bool = True): """Returns the default currency code (or USD if not specified).""" from common.settings import get_global_setting + if InvenTree.ready.isRunningMigrations(): + return '' # pragma: no cover + try: code = get_global_setting( 'INVENTREE_DEFAULT_CURRENCY', create=create, cache=True diff --git a/src/backend/InvenTree/company/migrations/0025_auto_20201110_1001.py b/src/backend/InvenTree/company/migrations/0025_auto_20201110_1001.py index f0db3f5f38..0c11004e74 100644 --- a/src/backend/InvenTree/company/migrations/0025_auto_20201110_1001.py +++ b/src/backend/InvenTree/company/migrations/0025_auto_20201110_1001.py @@ -3,7 +3,6 @@ from django.db import migrations, connection import djmoney.models.fields import common.currency -import common.settings class Migration(migrations.Migration): @@ -17,11 +16,11 @@ class Migration(migrations.Migration): migrations.AddField( model_name='supplierpricebreak', name='price', - field=djmoney.models.fields.MoneyField(decimal_places=4, default_currency=common.currency.currency_code_default(), help_text='Unit price at specified quantity', max_digits=19, null=True, verbose_name='Price'), + field=djmoney.models.fields.MoneyField(decimal_places=4, default_currency='', help_text='Unit price at specified quantity', max_digits=19, null=True, verbose_name='Price'), ), migrations.AddField( model_name='supplierpricebreak', name='price_currency', - field=djmoney.models.fields.CurrencyField(choices=common.currency.currency_code_mappings(), default=common.currency.currency_code_default(), editable=False, max_length=3), + field=djmoney.models.fields.CurrencyField(choices=common.currency.currency_code_mappings(), default='', editable=False, max_length=3), ), ] diff --git a/src/backend/InvenTree/company/migrations/0040_alter_company_currency.py b/src/backend/InvenTree/company/migrations/0040_alter_company_currency.py index 10c22b3367..0415241521 100644 --- a/src/backend/InvenTree/company/migrations/0040_alter_company_currency.py +++ b/src/backend/InvenTree/company/migrations/0040_alter_company_currency.py @@ -2,7 +2,6 @@ import InvenTree.validators import common.currency -import common.settings from django.db import migrations, models diff --git a/src/backend/InvenTree/order/migrations/0038_auto_20201112_1737.py b/src/backend/InvenTree/order/migrations/0038_auto_20201112_1737.py index efd157f5b6..e4014a00ed 100644 --- a/src/backend/InvenTree/order/migrations/0038_auto_20201112_1737.py +++ b/src/backend/InvenTree/order/migrations/0038_auto_20201112_1737.py @@ -3,7 +3,6 @@ from django.db import migrations import djmoney.models.fields import common.currency -import common.settings class Migration(migrations.Migration): @@ -17,11 +16,11 @@ class Migration(migrations.Migration): migrations.AddField( model_name='purchaseorderlineitem', name='purchase_price', - field=djmoney.models.fields.MoneyField(decimal_places=4, default_currency=common.currency.currency_code_default(), help_text='Unit purchase price', max_digits=19, null=True, verbose_name='Purchase Price'), + field=djmoney.models.fields.MoneyField(decimal_places=4, default_currency='', help_text='Unit purchase price', max_digits=19, null=True, verbose_name='Purchase Price'), ), migrations.AddField( model_name='purchaseorderlineitem', name='purchase_price_currency', - field=djmoney.models.fields.CurrencyField(choices=common.currency.currency_code_mappings(), default=common.currency.currency_code_default(), editable=False, max_length=3), + field=djmoney.models.fields.CurrencyField(choices=common.currency.currency_code_mappings(), default='', editable=False, max_length=3), ), ] diff --git a/src/backend/InvenTree/order/migrations/0039_auto_20201112_2203.py b/src/backend/InvenTree/order/migrations/0039_auto_20201112_2203.py index 0035f9a949..d9af677fa0 100644 --- a/src/backend/InvenTree/order/migrations/0039_auto_20201112_2203.py +++ b/src/backend/InvenTree/order/migrations/0039_auto_20201112_2203.py @@ -2,8 +2,6 @@ from django.db import migrations import djmoney.models.fields -import common.currency -import common.settings class Migration(migrations.Migration): @@ -16,6 +14,6 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='purchaseorderlineitem', name='purchase_price', - field=djmoney.models.fields.MoneyField(blank=True, decimal_places=4, default_currency=common.currency.currency_code_default(), help_text='Unit purchase price', max_digits=19, null=True, verbose_name='Purchase Price'), + field=djmoney.models.fields.MoneyField(blank=True, decimal_places=4, default_currency='', help_text='Unit purchase price', max_digits=19, null=True, verbose_name='Purchase Price'), ), ] diff --git a/src/backend/InvenTree/order/migrations/0045_auto_20210504_1946.py b/src/backend/InvenTree/order/migrations/0045_auto_20210504_1946.py index 4faadf2715..4366e6a4cb 100644 --- a/src/backend/InvenTree/order/migrations/0045_auto_20210504_1946.py +++ b/src/backend/InvenTree/order/migrations/0045_auto_20210504_1946.py @@ -2,7 +2,6 @@ from django.db import migrations import common.currency -import common.settings import djmoney.models.fields @@ -16,11 +15,11 @@ class Migration(migrations.Migration): migrations.AddField( model_name='salesorderlineitem', name='sale_price', - field=djmoney.models.fields.MoneyField(blank=True, decimal_places=4, default_currency=common.currency.currency_code_default(), help_text='Unit sale price', max_digits=19, null=True, verbose_name='Sale Price'), + field=djmoney.models.fields.MoneyField(blank=True, decimal_places=4, default_currency='', help_text='Unit sale price', max_digits=19, null=True, verbose_name='Sale Price'), ), migrations.AddField( model_name='salesorderlineitem', name='sale_price_currency', - field=djmoney.models.fields.CurrencyField(choices=common.currency.currency_code_mappings(), default=common.currency.currency_code_default(), editable=False, max_length=3), + field=djmoney.models.fields.CurrencyField(choices=common.currency.currency_code_mappings(), default='', editable=False, max_length=3), ), ] diff --git a/src/backend/InvenTree/part/migrations/0055_auto_20201110_1001.py b/src/backend/InvenTree/part/migrations/0055_auto_20201110_1001.py index 8bf472e4cb..a118b1da16 100644 --- a/src/backend/InvenTree/part/migrations/0055_auto_20201110_1001.py +++ b/src/backend/InvenTree/part/migrations/0055_auto_20201110_1001.py @@ -16,11 +16,11 @@ class Migration(migrations.Migration): migrations.AddField( model_name='partsellpricebreak', name='price', - field=djmoney.models.fields.MoneyField(decimal_places=4, default_currency=common.currency.currency_code_default(), help_text='Unit price at specified quantity', max_digits=19, null=True, verbose_name='Price'), + field=djmoney.models.fields.MoneyField(decimal_places=4, default_currency='', help_text='Unit price at specified quantity', max_digits=19, null=True, verbose_name='Price'), ), migrations.AddField( model_name='partsellpricebreak', name='price_currency', - field=djmoney.models.fields.CurrencyField(choices=common.currency.currency_code_mappings(), default=common.currency.currency_code_default(), editable=False, max_length=3), + field=djmoney.models.fields.CurrencyField(choices=common.currency.currency_code_mappings(), default='', editable=False, max_length=3), ), ] diff --git a/src/backend/InvenTree/part/migrations/0067_partinternalpricebreak.py b/src/backend/InvenTree/part/migrations/0067_partinternalpricebreak.py index d8dc4f3c56..c290bd6ec9 100644 --- a/src/backend/InvenTree/part/migrations/0067_partinternalpricebreak.py +++ b/src/backend/InvenTree/part/migrations/0067_partinternalpricebreak.py @@ -3,7 +3,6 @@ import InvenTree.fields import django.core.validators import common.currency -import common.settings from django.db import migrations, models import django.db.models.deletion import djmoney.models.fields @@ -21,8 +20,8 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('quantity', InvenTree.fields.RoundingDecimalField(decimal_places=5, default=1, help_text='Price break quantity', max_digits=15, validators=[django.core.validators.MinValueValidator(1)], verbose_name='Quantity')), - ('price_currency', djmoney.models.fields.CurrencyField(choices=common.currency.currency_code_mappings(), default=common.currency.currency_code_default(), editable=False, max_length=3)), - ('price', djmoney.models.fields.MoneyField(decimal_places=4, default_currency=common.currency.currency_code_default(), help_text='Unit price at specified quantity', max_digits=19, null=True, verbose_name='Price')), + ('price_currency', djmoney.models.fields.CurrencyField(choices=common.currency.currency_code_mappings(), default='', editable=False, max_length=3)), + ('price', djmoney.models.fields.MoneyField(decimal_places=4, default_currency='', help_text='Unit price at specified quantity', max_digits=19, null=True, verbose_name='Price')), ('part', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='internalpricebreaks', to='part.part', verbose_name='Part')), ], options={ diff --git a/src/backend/InvenTree/part/migrations/0089_auto_20221112_0128.py b/src/backend/InvenTree/part/migrations/0089_auto_20221112_0128.py index 865153ffe2..57cb415707 100644 --- a/src/backend/InvenTree/part/migrations/0089_auto_20221112_0128.py +++ b/src/backend/InvenTree/part/migrations/0089_auto_20221112_0128.py @@ -8,7 +8,7 @@ import djmoney.models.validators import InvenTree.fields import common.currency -import common.settings + class Migration(migrations.Migration): diff --git a/src/backend/InvenTree/stock/migrations/0053_auto_20201110_0513.py b/src/backend/InvenTree/stock/migrations/0053_auto_20201110_0513.py index a7106f8af2..695642522d 100644 --- a/src/backend/InvenTree/stock/migrations/0053_auto_20201110_0513.py +++ b/src/backend/InvenTree/stock/migrations/0053_auto_20201110_0513.py @@ -16,11 +16,11 @@ class Migration(migrations.Migration): migrations.AddField( model_name='stockitem', name='purchase_price', - field=djmoney.models.fields.MoneyField(decimal_places=4, default_currency=common.currency.currency_code_default(), help_text='Single unit purchase price at time of purchase', max_digits=19, null=True, verbose_name='Purchase Price'), + field=djmoney.models.fields.MoneyField(decimal_places=4, default_currency='', help_text='Single unit purchase price at time of purchase', max_digits=19, null=True, verbose_name='Purchase Price'), ), migrations.AddField( model_name='stockitem', name='purchase_price_currency', - field=djmoney.models.fields.CurrencyField(choices=common.currency.all_currency_codes(), default=common.currency.currency_code_default(), editable=False, max_length=3), + field=djmoney.models.fields.CurrencyField(choices=common.currency.all_currency_codes(), default='', editable=False, max_length=3), ), ] diff --git a/src/backend/InvenTree/stock/migrations/0055_auto_20201117_1453.py b/src/backend/InvenTree/stock/migrations/0055_auto_20201117_1453.py index 8aebb0c7c1..411e62e2c3 100644 --- a/src/backend/InvenTree/stock/migrations/0055_auto_20201117_1453.py +++ b/src/backend/InvenTree/stock/migrations/0055_auto_20201117_1453.py @@ -2,7 +2,6 @@ from django.db import migrations import djmoney.models.fields -import common.currency class Migration(migrations.Migration): @@ -15,6 +14,6 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='stockitem', name='purchase_price', - field=djmoney.models.fields.MoneyField(blank=True, decimal_places=4, default_currency=common.currency.currency_code_default(), help_text='Single unit purchase price at time of purchase', max_digits=19, null=True, verbose_name='Purchase Price'), + field=djmoney.models.fields.MoneyField(blank=True, decimal_places=4, default_currency='', help_text='Single unit purchase price at time of purchase', max_digits=19, null=True, verbose_name='Purchase Price'), ), ] diff --git a/src/backend/InvenTree/stock/tests.py b/src/backend/InvenTree/stock/tests.py index d2040cf3e5..ef400fb067 100644 --- a/src/backend/InvenTree/stock/tests.py +++ b/src/backend/InvenTree/stock/tests.py @@ -6,6 +6,8 @@ from django.core.exceptions import ValidationError from django.db.models import Sum from django.test import override_settings +from djmoney.money import Money + from build.models import Build from common.models import InvenTreeSetting from company.models import Company @@ -803,6 +805,27 @@ class StockTest(StockTestBase): self.assertTrue(check_func()) + def test_purchase_price(self): + """Test purchase price field.""" + from common.currency import currency_code_default + from common.settings import set_global_setting + + part = Part.objects.filter(virtual=False).first() + + for currency in ['AUD', 'USD', 'JPY']: + set_global_setting('INVENTREE_DEFAULT_CURRENCY', currency) + self.assertEqual(currency_code_default(), currency) + + # Create stock item, do not specify currency - should get default + item = StockItem.objects.create(part=part, quantity=10) + self.assertEqual(item.purchase_price_currency, currency) + + # Create stock item, specify currency + item = StockItem.objects.create( + part=part, quantity=10, purchase_price=Money(5, 'GBP') + ) + self.assertEqual(item.purchase_price_currency, 'GBP') + class StockBarcodeTest(StockTestBase): """Run barcode tests for the stock app.""" diff --git a/src/backend/InvenTree/users/api.py b/src/backend/InvenTree/users/api.py index 71456fa9dc..5cb0646719 100644 --- a/src/backend/InvenTree/users/api.py +++ b/src/backend/InvenTree/users/api.py @@ -12,8 +12,8 @@ from django.urls import include, path from django.views.decorators.csrf import ensure_csrf_cookie from django.views.generic.base import RedirectView +import django_filters.rest_framework.filters as rest_filters import structlog -from django_filters import rest_framework as rest_filters from django_filters.rest_framework.filterset import FilterSet from drf_spectacular.utils import OpenApiParameter, OpenApiResponse, extend_schema from rest_framework import exceptions diff --git a/src/frontend/src/forms/StockForms.tsx b/src/frontend/src/forms/StockForms.tsx index ae3fd0d475..89e9e8f7fb 100644 --- a/src/frontend/src/forms/StockForms.tsx +++ b/src/frontend/src/forms/StockForms.tsx @@ -124,10 +124,18 @@ export function useStockFields({ } }, [pricing, quantity]); + // Set the supplier part if provided useEffect(() => { if (supplierPartId && !supplierPart) setSupplierPart(supplierPartId); }, [partInstance, supplierPart, supplierPartId]); + // Set default currency from global settings + useEffect(() => { + setPurchasePriceCurrency( + globalSettings.getSetting('INVENTREE_DEFAULT_CURRENCY') + ); + }, [globalSettings]); + return useMemo(() => { const fields: ApiFormFieldSet = { part: { @@ -248,6 +256,7 @@ export function useStockFields({ }, purchase_price_currency: { icon: , + default: globalSettings.getSetting('INVENTREE_DEFAULT_CURRENCY'), value: purchasePriceCurrency, onValueChange: (value) => { setPurchasePriceCurrency(value);