2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-11-13 19:36:46 +00:00

Default stock currency (#10641) (#10644)

* Default stock currency (#10641)

* Fix for useStockFields

- Use default currency

* Ensure default currency is observed

* Specify field default

* Improve import (for ty)

* Update migration files

- Point currency fields to the correct default method

* Unit tests

- Ensure stock item gets correct default currency

* Cleaner generation of default currency value

- Return empty string during migratoins

* Update existing migrations

* Reduce noise

* Ignore "no-matching-overload" rule for ty

* Tweak money_kwargs

* Remove conflicting code

* Fix import

* Tweak currency_code_default
This commit is contained in:
Oliver
2025-10-21 16:29:24 +11:00
committed by GitHub
parent f8bcc3ec17
commit 0dcb706f4c
14 changed files with 52 additions and 27 deletions

View File

@@ -15,6 +15,8 @@ from rest_framework.fields import URLField as RestURLField
from rest_framework.fields import empty from rest_framework.fields import empty
import InvenTree.helpers import InvenTree.helpers
import InvenTree.ready
from common.currency import currency_code_default
from common.settings import get_global_setting from common.settings import get_global_setting
from .validators import AllowedURLValidator, allowable_url_schemes from .validators import AllowedURLValidator, allowable_url_schemes
@@ -59,7 +61,7 @@ class InvenTreeURLField(models.URLField):
def money_kwargs(**kwargs): def money_kwargs(**kwargs):
"""Returns the database settings for MoneyFields.""" """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) # Default values (if not specified)
if 'max_digits' not in kwargs: if 'max_digits' not in kwargs:
@@ -71,8 +73,14 @@ def money_kwargs(**kwargs):
if 'currency_choices' not in kwargs: if 'currency_choices' not in kwargs:
kwargs['currency_choices'] = currency_code_mappings() kwargs['currency_choices'] = currency_code_mappings()
if 'default_currency' not in kwargs: if InvenTree.ready.isRunningMigrations():
kwargs['default_currency'] = currency_code_default() # 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 return kwargs

View File

@@ -11,6 +11,7 @@ import structlog
from moneyed import CURRENCIES from moneyed import CURRENCIES
import InvenTree.helpers import InvenTree.helpers
import InvenTree.ready
logger = structlog.get_logger('inventree') logger = structlog.get_logger('inventree')

View File

@@ -3,7 +3,6 @@
from django.db import migrations, connection from django.db import migrations, connection
import djmoney.models.fields import djmoney.models.fields
import common.currency import common.currency
import common.settings
class Migration(migrations.Migration): class Migration(migrations.Migration):
@@ -17,11 +16,11 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='supplierpricebreak', model_name='supplierpricebreak',
name='price', 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( migrations.AddField(
model_name='supplierpricebreak', model_name='supplierpricebreak',
name='price_currency', 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),
), ),
] ]

View File

@@ -2,7 +2,6 @@
import InvenTree.validators import InvenTree.validators
import common.currency import common.currency
import common.settings
from django.db import migrations, models from django.db import migrations, models

View File

@@ -3,7 +3,6 @@
from django.db import migrations from django.db import migrations
import djmoney.models.fields import djmoney.models.fields
import common.currency import common.currency
import common.settings
class Migration(migrations.Migration): class Migration(migrations.Migration):
@@ -17,11 +16,11 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='purchaseorderlineitem', model_name='purchaseorderlineitem',
name='purchase_price', 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( migrations.AddField(
model_name='purchaseorderlineitem', model_name='purchaseorderlineitem',
name='purchase_price_currency', 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),
), ),
] ]

View File

@@ -2,8 +2,6 @@
from django.db import migrations from django.db import migrations
import djmoney.models.fields import djmoney.models.fields
import common.currency
import common.settings
class Migration(migrations.Migration): class Migration(migrations.Migration):
@@ -16,6 +14,6 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='purchaseorderlineitem', model_name='purchaseorderlineitem',
name='purchase_price', 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'),
), ),
] ]

View File

@@ -2,7 +2,6 @@
from django.db import migrations from django.db import migrations
import common.currency import common.currency
import common.settings
import djmoney.models.fields import djmoney.models.fields
@@ -16,11 +15,11 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='salesorderlineitem', model_name='salesorderlineitem',
name='sale_price', 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( migrations.AddField(
model_name='salesorderlineitem', model_name='salesorderlineitem',
name='sale_price_currency', 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),
), ),
] ]

View File

@@ -16,11 +16,11 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='partsellpricebreak', model_name='partsellpricebreak',
name='price', 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( migrations.AddField(
model_name='partsellpricebreak', model_name='partsellpricebreak',
name='price_currency', 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),
), ),
] ]

View File

@@ -3,7 +3,6 @@
import InvenTree.fields import InvenTree.fields
import django.core.validators import django.core.validators
import common.currency import common.currency
import common.settings
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
import djmoney.models.fields import djmoney.models.fields
@@ -21,8 +20,8 @@ class Migration(migrations.Migration):
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('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')), ('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_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=common.currency.currency_code_default(), help_text='Unit price at specified quantity', max_digits=19, null=True, verbose_name='Price')), ('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')), ('part', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='internalpricebreaks', to='part.part', verbose_name='Part')),
], ],
options={ options={

View File

@@ -8,7 +8,7 @@ import djmoney.models.validators
import InvenTree.fields import InvenTree.fields
import common.currency import common.currency
import common.settings
class Migration(migrations.Migration): class Migration(migrations.Migration):

View File

@@ -16,11 +16,11 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='stockitem', model_name='stockitem',
name='purchase_price', 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( migrations.AddField(
model_name='stockitem', model_name='stockitem',
name='purchase_price_currency', 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),
), ),
] ]

View File

@@ -2,7 +2,6 @@
from django.db import migrations from django.db import migrations
import djmoney.models.fields import djmoney.models.fields
import common.currency
class Migration(migrations.Migration): class Migration(migrations.Migration):
@@ -15,6 +14,6 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='stockitem', model_name='stockitem',
name='purchase_price', 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'),
), ),
] ]

View File

@@ -6,6 +6,8 @@ from django.core.exceptions import ValidationError
from django.db.models import Sum from django.db.models import Sum
from django.test import override_settings from django.test import override_settings
from djmoney.money import Money
from build.models import Build from build.models import Build
from common.models import InvenTreeSetting from common.models import InvenTreeSetting
from company.models import Company from company.models import Company
@@ -803,6 +805,27 @@ class StockTest(StockTestBase):
self.assertTrue(check_func()) 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): class StockBarcodeTest(StockTestBase):
"""Run barcode tests for the stock app.""" """Run barcode tests for the stock app."""

View File

@@ -212,7 +212,8 @@ export function useStockFields({
icon: <IconCurrencyDollar /> icon: <IconCurrencyDollar />
}, },
purchase_price_currency: { purchase_price_currency: {
icon: <IconCoins /> icon: <IconCoins />,
default: globalSettings.getSetting('INVENTREE_DEFAULT_CURRENCY')
}, },
packaging: { packaging: {
icon: <IconPackage /> icon: <IconPackage />