<commit_before># -*- coding: utf-8 -*-
# This file is part of Shuup.
#
# Copyright (c) 2012-2021, Shuup Commerce Inc. All rights reserved.
#
# This source code is licensed under the OSL-3.0 license found in the
# LICENSE file in the root directory of this source tree.
from __future__ import unicode_literals

import warnings
from decimal import ROUND_HALF_UP, Decimal
from django.core.exceptions import ValidationError
from django.db import models
from django.db.models.signals import post_save
from django.utils.encoding import python_2_unicode_compatible
from django.utils.functional import cached_property
from django.utils.translation import pgettext, ugettext_lazy as _
from functools import lru_cache
from parler.models import TranslatedField, TranslatedFields, TranslatedFieldsModel

from shuup.core import cache
from shuup.core.fields import InternalIdentifierField, QuantityField
from shuup.utils.django_compat import force_text
from shuup.utils.i18n import format_number
from shuup.utils.numbers import bankers_round, parse_decimal_string

from ._base import TranslatableShuupModel


@lru_cache()
def get_display_unit(sales_unit):
    cache_key = "display_unit:sales_unit_{}_default_display_unit".format(sales_unit.pk)
    default_display_unit = cache.get(cache_key)
    if default_display_unit is None:
        default_display_unit = sales_unit.display_units.filter(default=True).first()
        # Set 0 to cache to prevent None values, which will not be a valid cache value
        # 0 will be invalid below, hence we prevent another query here
        cache.set(cache_key, default_display_unit or 0)
    return default_display_unit


# TODO: (3.0) Remove deprecated SalesUnit.short_name
class _ShortNameToSymbol(object):
    def __init__(self, *args, **kwargs):
        if "short_name" in kwargs:
            self._issue_deprecation_warning()
            kwargs.setdefault("symbol", kwargs.pop("short_name"))
        super(_ShortNameToSymbol, self).__init__(*args, **kwargs)

    @property
    def short_name(self):
        self._issue_deprecation_warning()
        return self.symbol

    @short_name.setter
    def short_name(self, value):
        self._issue_deprecation_warning()
        self.symbol = value

    def _issue_deprecation_warning(self):
        warnings.warn("Warning! `short_name` is deprecated, use `symbol` instead.", DeprecationWarning)


@python_2_unicode_compatible
class SalesUnit(_ShortNameToSymbol, TranslatableShuupModel):
    identifier = InternalIdentifierField(unique=True)
    decimals = models.PositiveSmallIntegerField(
        default=0,
        verbose_name=_("allowed decimal places"),
        help_text=_(
            "The number of decimal places allowed by this sales unit."
            "Set this to a value greater than zero if products with "
            "this sales unit can be sold in fractional quantities."
        ),
    )

    name = TranslatedField()
    symbol = TranslatedField()

    class Meta:
        verbose_name = _("sales unit")
        verbose_name_plural = _("sales units")

    def __str__(self):
        return force_text(self.safe_translation_getter("name", default=self.identifier) or "")

    @property
    def allow_fractions(self):
        return self.decimals > 0

    @cached_property
    def quantity_step(self):
        """
        Get the quantity increment for the amount of decimals this unit allows.

        For zero decimals, this will be 1; for one decimal, 0.1; etc.

        :return: Decimal in (0..1].
        :rtype: Decimal
        """

        # This particular syntax (`10 ^ -n`) is the same that `bankers_round` uses
        # to figure out the quantizer.

        return Decimal(10) ** (-int(self.decimals))

    def round(self, value):
        return bankers_round(parse_decimal_string(value), self.decimals)

    @property
    def display_unit(self):
        """
        Default display unit of this sales unit.

        Get a `DisplayUnit` object, which has this sales unit as its
        internal unit and is marked as a default, or if there is no
        default display unit for this sales unit, then a proxy object.
        The proxy object has the same display unit interface and mirrors
        the properties of the sales unit, such as symbol and decimals.

        :rtype: DisplayUnit
        """
        return (get_display_unit(self) if self.pk else None) or SalesUnitAsDisplayUnit(self)


class SalesUnitTranslation(_ShortNameToSymbol, TranslatedFieldsModel):
    master = models.ForeignKey(
        on_delete=models.CASCADE, to=SalesUnit, related_name="translations", null=True, editable=False
    )
    name = models.CharField(
        max_length=128,
        verbose_name=_("name"),
        help_text=_(
            "The sales unit name to use for products (e.g. "
            "'pieces' or 'units'). Sales units can be set individually for each "
            "product through the product editor view."<commit_msg>Core: fix sales unit FK field<commit_after># -*- coding: utf-8 -*-
# This file is part of Shuup.
#
# Copyright (c) 2012-2021, Shuup Commerce Inc. All rights reserved.
#
# This source code is licensed under the OSL-3.0 license found in the
# LICENSE file in the root directory of this source tree.
from __future__ import unicode_literals

import warnings
from decimal import ROUND_HALF_UP, Decimal
from django.core.exceptions import ValidationError
from django.db import models
from django.db.models.signals import post_save
from django.utils.encoding import python_2_unicode_compatible
from django.utils.functional import cached_property
from django.utils.translation import pgettext, ugettext_lazy as _
from functools import lru_cache
from parler.fields import TranslationsForeignKey
from parler.models import TranslatedField, TranslatedFields, TranslatedFieldsModel

from shuup.core import cache
from shuup.core.fields import InternalIdentifierField, QuantityField
from shuup.utils.django_compat import force_text
from shuup.utils.i18n import format_number
from shuup.utils.numbers import bankers_round, parse_decimal_string

from ._base import TranslatableShuupModel


@lru_cache()
def get_display_unit(sales_unit):
    cache_key = "display_unit:sales_unit_{}_default_display_unit".format(sales_unit.pk)
    default_display_unit = cache.get(cache_key)
    if default_display_unit is None:
        default_display_unit = sales_unit.display_units.filter(default=True).first()
        # Set 0 to cache to prevent None values, which will not be a valid cache value
        # 0 will be invalid below, hence we prevent another query here
        cache.set(cache_key, default_display_unit or 0)
    return default_display_unit


# TODO: (3.0) Remove deprecated SalesUnit.short_name
class _ShortNameToSymbol(object):
    def __init__(self, *args, **kwargs):
        if "short_name" in kwargs:
            self._issue_deprecation_warning()
            kwargs.setdefault("symbol", kwargs.pop("short_name"))
        super(_ShortNameToSymbol, self).__init__(*args, **kwargs)

    @property
    def short_name(self):
        self._issue_deprecation_warning()
        return self.symbol

    @short_name.setter
    def short_name(self, value):
        self._issue_deprecation_warning()
        self.symbol = value

    def _issue_deprecation_warning(self):
        warnings.warn("Warning! `short_name` is deprecated, use `symbol` instead.", DeprecationWarning)


@python_2_unicode_compatible
class SalesUnit(_ShortNameToSymbol, TranslatableShuupModel):
    identifier = InternalIdentifierField(unique=True)
    decimals = models.PositiveSmallIntegerField(
        default=0,
        verbose_name=_("allowed decimal places"),
        help_text=_(
            "The number of decimal places allowed by this sales unit."
            "Set this to a value greater than zero if products with "
            "this sales unit can be sold in fractional quantities."
        ),
    )

    name = TranslatedField()
    symbol = TranslatedField()

    class Meta:
        verbose_name = _("sales unit")
        verbose_name_plural = _("sales units")

    def __str__(self):
        return force_text(self.safe_translation_getter("name", default=self.identifier) or "")

    @property
    def allow_fractions(self):
        return self.decimals > 0

    @cached_property
    def quantity_step(self):
        """
        Get the quantity increment for the amount of decimals this unit allows.

        For zero decimals, this will be 1; for one decimal, 0.1; etc.

        :return: Decimal in (0..1].
        :rtype: Decimal
        """

        # This particular syntax (`10 ^ -n`) is the same that `bankers_round` uses
        # to figure out the quantizer.

        return Decimal(10) ** (-int(self.decimals))

    def round(self, value):
        return bankers_round(parse_decimal_string(value), self.decimals)

    @property
    def display_unit(self):
        """
        Default display unit of this sales unit.

        Get a `DisplayUnit` object, which has this sales unit as its
        internal unit and is marked as a default, or if there is no
        default display unit for this sales unit, then a proxy object.
        The proxy object has the same display unit interface and mirrors
        the properties of the sales unit, such as symbol and decimals.

        :rtype: DisplayUnit
        """
        return (get_display_unit(self) if self.pk else None) or SalesUnitAsDisplayUnit(self)


class SalesUnitTranslation(_ShortNameToSymbol, TranslatedFieldsModel):
    master = TranslationsForeignKey(
        on_delete=models.CASCADE, to=SalesUnit, related_name="translations", null=True, editable=False
    )
    name = models.CharField(
        max_length=128,
        verbose_name=_("name"),
        help_text=_(
            "The sales unit name to use for products (e.g. "
            "'pieces' or 'units'). Sales units can be set individually for each "
            "product through the product editor view."
{'fraction_non_alphanumeric': 0.07641965364478454, 'fraction_numerical': 0.00684655658477648, 'mean_word_length': 3.326219512195122, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2616304', 'n_tokens_mistral': 2881, 'n_tokens_neox': 2733, 'n_words': 900}