mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 05:05:42 +00:00 
			
		
		
		
	Update django money / py-moneyed dependencies (#5778)
* Replace moneyed format_money function * Update django-money to 3.3.0 and py-moneyed to 3.0 * Add CurrencyField migrations * Fix checking if decimal_places is set * Add currency formatting test * Test fixing deepsource test patterns * Revert "Test fixing deepsource test patterns" This reverts commit398ef93cd6. * FIx requirements.txt formatting * Revert "FIx requirements.txt formatting" This reverts commitbb554b0758.
This commit is contained in:
		| @@ -3,8 +3,14 @@ | ||||
| import re | ||||
| import string | ||||
|  | ||||
| from django.conf import settings | ||||
| from django.utils import translation | ||||
| from django.utils.translation import gettext_lazy as _ | ||||
|  | ||||
| from babel import Locale | ||||
| from babel.numbers import parse_pattern | ||||
| from djmoney.money import Money | ||||
|  | ||||
|  | ||||
| def parse_format_string(fmt_string: str) -> dict: | ||||
|     """Extract formatting information from the provided format string. | ||||
| @@ -160,3 +166,34 @@ def extract_named_group(name: str, value: str, fmt_string: str) -> str: | ||||
|     # And return the value we are interested in | ||||
|     # Note: This will raise an IndexError if the named group was not matched | ||||
|     return result.group(name) | ||||
|  | ||||
|  | ||||
| def format_money(money: Money, decimal_places: int = None, format: str = None) -> str: | ||||
|     """Format money object according to the currently set local | ||||
|  | ||||
|     Args: | ||||
|         decimal_places: Number of decimal places to use | ||||
|         format: Format pattern according LDML / the babel format pattern syntax (https://babel.pocoo.org/en/latest/numbers.html) | ||||
|  | ||||
|     Returns: | ||||
|         str: The formatted string | ||||
|  | ||||
|     Raises: | ||||
|         ValueError: format string is incorrectly specified | ||||
|     """ | ||||
|     language = None and translation.get_language() or settings.LANGUAGE_CODE | ||||
|     locale = Locale.parse(translation.to_locale(language)) | ||||
|     if format: | ||||
|         pattern = parse_pattern(format) | ||||
|     else: | ||||
|         pattern = locale.currency_formats["standard"] | ||||
|         if decimal_places is not None: | ||||
|             pattern.frac_prec = (decimal_places, decimal_places) | ||||
|  | ||||
|     return pattern.apply( | ||||
|         money.amount, | ||||
|         locale, | ||||
|         currency=money.currency.code, | ||||
|         currency_digits=decimal_places is None, | ||||
|         decimal_quantization=decimal_places is not None, | ||||
|     ) | ||||
|   | ||||
| @@ -10,7 +10,6 @@ from django.core.validators import URLValidator | ||||
| from django.db.utils import OperationalError, ProgrammingError | ||||
| from django.utils.translation import gettext_lazy as _ | ||||
|  | ||||
| import moneyed.localization | ||||
| import requests | ||||
| from djmoney.contrib.exchange.models import convert_money | ||||
| from djmoney.money import Money | ||||
| @@ -22,6 +21,7 @@ import InvenTree.helpers_model | ||||
| import InvenTree.version | ||||
| from common.notifications import (InvenTreeNotificationBodies, | ||||
|                                   NotificationBody, trigger_notification) | ||||
| from InvenTree.format import format_money | ||||
|  | ||||
| logger = logging.getLogger('inventree') | ||||
|  | ||||
| @@ -167,14 +167,13 @@ def download_image_from_url(remote_url, timeout=2.5): | ||||
|     return img | ||||
|  | ||||
|  | ||||
| def render_currency(money, decimal_places=None, currency=None, include_symbol=True, min_decimal_places=None, max_decimal_places=None): | ||||
| def render_currency(money, decimal_places=None, currency=None, min_decimal_places=None, max_decimal_places=None): | ||||
|     """Render a currency / Money object to a formatted string (e.g. for reports) | ||||
|  | ||||
|     Arguments: | ||||
|         money: The Money instance to be rendered | ||||
|         decimal_places: The number of decimal places to render to. If unspecified, uses the PRICING_DECIMAL_PLACES setting. | ||||
|         currency: Optionally convert to the specified currency | ||||
|         include_symbol: Render with the appropriate currency symbol | ||||
|         min_decimal_places: The minimum number of decimal places to render to. If unspecified, uses the PRICING_DECIMAL_PLACES_MIN setting. | ||||
|         max_decimal_places: The maximum number of decimal places to render to. If unspecified, uses the PRICING_DECIMAL_PLACES setting. | ||||
|     """ | ||||
| @@ -216,11 +215,7 @@ def render_currency(money, decimal_places=None, currency=None, include_symbol=Tr | ||||
|  | ||||
|     decimal_places = max(decimal_places, max_decimal_places) | ||||
|  | ||||
|     return moneyed.localization.format_money( | ||||
|         money, | ||||
|         decimal_places=decimal_places, | ||||
|         include_symbol=include_symbol, | ||||
|     ) | ||||
|     return format_money(money, decimal_places=decimal_places) | ||||
|  | ||||
|  | ||||
| def getModelsWithMixin(mixin_class) -> list: | ||||
|   | ||||
| @@ -330,6 +330,33 @@ class FormatTest(TestCase): | ||||
|                 "PO-###-{test}", | ||||
|             ) | ||||
|  | ||||
|     def test_currency_formatting(self): | ||||
|         """Test that currency formatting works correctly for multiple currencies""" | ||||
|  | ||||
|         test_data = ( | ||||
|             (Money(  3651.285718, "USD"), 4, "$3,651.2857"     ),  # noqa: E201,E202 | ||||
|             (Money(487587.849178, "CAD"), 5, "CA$487,587.84918"),  # noqa: E201,E202 | ||||
|             (Money(     0.348102, "EUR"), 1, "€0.3"            ),  # noqa: E201,E202 | ||||
|             (Money(     0.916530, "GBP"), 1, "£0.9"            ),  # noqa: E201,E202 | ||||
|             (Money(    61.031024, "JPY"), 3, "¥61.031"         ),  # noqa: E201,E202 | ||||
|             (Money( 49609.694602, "JPY"), 1, "¥49,609.7"       ),  # noqa: E201,E202 | ||||
|             (Money(155565.264777, "AUD"), 2, "A$155,565.26"    ),  # noqa: E201,E202 | ||||
|             (Money(     0.820437, "CNY"), 4, "CN¥0.8204"       ),  # noqa: E201,E202 | ||||
|             (Money(  7587.849178, "EUR"), 0, "€7,588"          ),  # noqa: E201,E202 | ||||
|             (Money(     0.348102, "GBP"), 3, "£0.348"          ),  # noqa: E201,E202 | ||||
|             (Money(     0.652923, "CHF"), 0, "CHF1"            ),  # noqa: E201,E202 | ||||
|             (Money(     0.820437, "CNY"), 1, "CN¥0.8"          ),  # noqa: E201,E202 | ||||
|             (Money(98789.5295680, "CHF"), 0, "CHF98,790"       ),  # noqa: E201,E202 | ||||
|             (Money(     0.585787, "USD"), 1, "$0.6"            ),  # noqa: E201,E202 | ||||
|             (Money(     0.690541, "CAD"), 3, "CA$0.691"        ),  # noqa: E201,E202 | ||||
|             (Money(   427.814104, "AUD"), 5, "A$427.81410"     ),  # noqa: E201,E202 | ||||
|         ) | ||||
|  | ||||
|         with self.settings(LANGUAGE_CODE="en-us"): | ||||
|             for value, decimal_places, expected_result in test_data: | ||||
|                 result = InvenTree.format.format_money(value, decimal_places=decimal_places) | ||||
|                 assert result == expected_result | ||||
|  | ||||
|  | ||||
| class TestHelpers(TestCase): | ||||
|     """Tests for InvenTree helper functions.""" | ||||
|   | ||||
| @@ -0,0 +1,19 @@ | ||||
| # Generated by Django 3.2.22 on 2023-10-24 16:44 | ||||
|  | ||||
| from django.db import migrations | ||||
| import djmoney.models.fields | ||||
|  | ||||
|  | ||||
| class Migration(migrations.Migration): | ||||
|  | ||||
|     dependencies = [ | ||||
|         ('company', '0066_auto_20230616_2059'), | ||||
|     ] | ||||
|  | ||||
|     operations = [ | ||||
|         migrations.AlterField( | ||||
|             model_name='supplierpricebreak', | ||||
|             name='price_currency', | ||||
|             field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), | ||||
|         ), | ||||
|     ] | ||||
							
								
								
									
										59
									
								
								InvenTree/order/migrations/0098_auto_20231024_1844.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								InvenTree/order/migrations/0098_auto_20231024_1844.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| # Generated by Django 3.2.22 on 2023-10-24 16:44 | ||||
|  | ||||
| from django.db import migrations | ||||
| import djmoney.models.fields | ||||
|  | ||||
|  | ||||
| class Migration(migrations.Migration): | ||||
|  | ||||
|     dependencies = [ | ||||
|         ('order', '0097_auto_20230529_0107'), | ||||
|     ] | ||||
|  | ||||
|     operations = [ | ||||
|         migrations.AlterField( | ||||
|             model_name='purchaseorder', | ||||
|             name='total_price_currency', | ||||
|             field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='purchaseorderextraline', | ||||
|             name='price_currency', | ||||
|             field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='purchaseorderlineitem', | ||||
|             name='purchase_price_currency', | ||||
|             field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='returnorder', | ||||
|             name='total_price_currency', | ||||
|             field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='returnorderextraline', | ||||
|             name='price_currency', | ||||
|             field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='returnorderlineitem', | ||||
|             name='price_currency', | ||||
|             field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='salesorder', | ||||
|             name='total_price_currency', | ||||
|             field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='salesorderextraline', | ||||
|             name='price_currency', | ||||
|             field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='salesorderlineitem', | ||||
|             name='sale_price_currency', | ||||
|             field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), | ||||
|         ), | ||||
|     ] | ||||
							
								
								
									
										114
									
								
								InvenTree/part/migrations/0118_auto_20231024_1844.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								InvenTree/part/migrations/0118_auto_20231024_1844.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,114 @@ | ||||
| # Generated by Django 3.2.22 on 2023-10-24 16:44 | ||||
|  | ||||
| from django.db import migrations | ||||
| import djmoney.models.fields | ||||
|  | ||||
|  | ||||
| class Migration(migrations.Migration): | ||||
|  | ||||
|     dependencies = [ | ||||
|         ('part', '0117_remove_part_responsible'), | ||||
|     ] | ||||
|  | ||||
|     operations = [ | ||||
|         migrations.AlterField( | ||||
|             model_name='partinternalpricebreak', | ||||
|             name='price_currency', | ||||
|             field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='partpricing', | ||||
|             name='bom_cost_max_currency', | ||||
|             field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='partpricing', | ||||
|             name='bom_cost_min_currency', | ||||
|             field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='partpricing', | ||||
|             name='internal_cost_max_currency', | ||||
|             field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='partpricing', | ||||
|             name='internal_cost_min_currency', | ||||
|             field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='partpricing', | ||||
|             name='overall_max_currency', | ||||
|             field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='partpricing', | ||||
|             name='overall_min_currency', | ||||
|             field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='partpricing', | ||||
|             name='purchase_cost_max_currency', | ||||
|             field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='partpricing', | ||||
|             name='purchase_cost_min_currency', | ||||
|             field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='partpricing', | ||||
|             name='sale_history_max_currency', | ||||
|             field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='partpricing', | ||||
|             name='sale_history_min_currency', | ||||
|             field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='partpricing', | ||||
|             name='sale_price_max_currency', | ||||
|             field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='partpricing', | ||||
|             name='sale_price_min_currency', | ||||
|             field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='partpricing', | ||||
|             name='supplier_price_max_currency', | ||||
|             field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='partpricing', | ||||
|             name='supplier_price_min_currency', | ||||
|             field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='partpricing', | ||||
|             name='variant_cost_max_currency', | ||||
|             field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='partpricing', | ||||
|             name='variant_cost_min_currency', | ||||
|             field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='partsellpricebreak', | ||||
|             name='price_currency', | ||||
|             field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='partstocktake', | ||||
|             name='cost_max_currency', | ||||
|             field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='partstocktake', | ||||
|             name='cost_min_currency', | ||||
|             field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), | ||||
|         ), | ||||
|     ] | ||||
| @@ -0,0 +1,19 @@ | ||||
| # Generated by Django 3.2.22 on 2023-10-24 16:44 | ||||
|  | ||||
| from django.db import migrations | ||||
| import djmoney.models.fields | ||||
|  | ||||
|  | ||||
| class Migration(migrations.Migration): | ||||
|  | ||||
|     dependencies = [ | ||||
|         ('stock', '0103_stock_location_types'), | ||||
|     ] | ||||
|  | ||||
|     operations = [ | ||||
|         migrations.AlterField( | ||||
|             model_name='stockitem', | ||||
|             name='purchase_price_currency', | ||||
|             field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), | ||||
|         ), | ||||
|     ] | ||||
| @@ -16,7 +16,7 @@ django-ical                             # iCal export for calendar views | ||||
| django-import-export>=3.3.1             # Data import / export for admin interface | ||||
| django-maintenance-mode                 # Shut down application while reloading etc. | ||||
| django-markdownify                      # Markdown rendering | ||||
| django-money<3.0.0                      # Django app for currency management  # FIXED 2022-06-26 to make sure py-moneyed is not conflicting | ||||
| django-money>=3.0.0                     # Django app for currency management | ||||
| django-mptt==0.11.0                     # Modified Preorder Tree Traversal | ||||
| django-redis>=5.0.0                     # Redis integration | ||||
| django-q2                               # Background task scheduling | ||||
| @@ -49,6 +49,3 @@ sentry-sdk                              # Error reporting (optional) | ||||
| setuptools                              # Standard dependency | ||||
| tablib[xls,xlsx,yaml]                   # Support for XLS and XLSX formats | ||||
| weasyprint==54.3                        # PDF generation | ||||
|  | ||||
| # Fixed sub-dependencies | ||||
| py-moneyed<2.0                          # For django-money # FIXED 2022-06-18 as we need `moneyed.localization` | ||||
|   | ||||
| @@ -113,7 +113,7 @@ django-maintenance-mode==0.19.0 | ||||
|     # via -r requirements.in | ||||
| django-markdownify==0.9.3 | ||||
|     # via -r requirements.in | ||||
| django-money==2.1.1 | ||||
| django-money==3.3.0 | ||||
|     # via -r requirements.in | ||||
| django-mptt==0.11.0 | ||||
|     # via -r requirements.in | ||||
| @@ -211,10 +211,8 @@ pillow==9.5.0 | ||||
|     #   weasyprint | ||||
| pint==0.21 | ||||
|     # via -r requirements.in | ||||
| py-moneyed==1.2 | ||||
|     # via | ||||
|     #   -r requirements.in | ||||
|     #   django-money | ||||
| py-moneyed==3.0 | ||||
|     # via django-money | ||||
| pycparser==2.21 | ||||
|     # via cffi | ||||
| pydyf==0.8.0 | ||||
| @@ -302,6 +300,7 @@ tinycss2==1.2.1 | ||||
| typing-extensions==4.8.0 | ||||
|     # via | ||||
|     #   asgiref | ||||
|     #   py-moneyed | ||||
|     #   qrcode | ||||
| uritemplate==4.1.1 | ||||
|     # via | ||||
|   | ||||
		Reference in New Issue
	
	Block a user