class OrderLineTax(MoneyPropped, ShoopModel, LineTax): order_line = models.ForeignKey(OrderLine, related_name='taxes', on_delete=models.PROTECT, verbose_name=_('order line')) tax = models.ForeignKey("Tax", related_name="order_line_taxes", on_delete=models.PROTECT, verbose_name=_('tax')) name = models.CharField(max_length=200, verbose_name=_('tax name')) amount = MoneyProperty('amount_value', 'order_line.order.currency') base_amount = MoneyProperty('base_amount_value', 'order_line.order.currency') amount_value = MoneyValueField(verbose_name=_('tax amount')) base_amount_value = MoneyValueField( verbose_name=_('base amount'), help_text=_('Amount that this tax is calculated from')) ordering = models.IntegerField(default=0, verbose_name=_('ordering')) class Meta: ordering = ["ordering"] def __str__(self): return "%s: %s on %s" % (self.name, self.amount, self.base_amount)
class Payment(MoneyPropped, models.Model): # TODO: Revise!!! order = models.ForeignKey("Order", related_name='payments', on_delete=models.PROTECT, verbose_name=_('order')) created_on = models.DateTimeField(auto_now_add=True, verbose_name=_('created on')) gateway_id = models.CharField(max_length=32, verbose_name=_('gateway ID')) # TODO: do we need this? payment_identifier = models.CharField(max_length=96, unique=True, verbose_name=_('identifier')) amount = MoneyProperty('amount_value', 'order.currency') foreign_amount = MoneyProperty('foreign_amount_value', 'foreign_currency') amount_value = MoneyValueField(verbose_name=_('amount')) foreign_amount_value = MoneyValueField(default=None, blank=True, null=True, verbose_name=_('foreign amount')) foreign_currency = CurrencyField(default=None, blank=True, null=True, verbose_name=_('foreign amount currency')) description = models.CharField(max_length=256, blank=True, verbose_name=_('description')) class Meta: verbose_name = _('payment') verbose_name_plural = _('payments')
class Tax(MoneyPropped, ImmutableMixin, TranslatableShoopModel): identifier_attr = 'code' immutability_message = _( "Cannot change business critical fields of Tax that is in use") unprotected_fields = ['enabled'] code = InternalIdentifierField(unique=True, editable=True, verbose_name=_("code"), help_text="") translations = TranslatedFields(name=models.CharField( max_length=64, verbose_name=_("name")), ) rate = models.DecimalField(max_digits=6, decimal_places=5, blank=True, null=True, verbose_name=_("tax rate"), help_text=_("The percentage rate of the tax.")) amount = MoneyProperty('amount_value', 'currency') amount_value = MoneyValueField( default=None, blank=True, null=True, verbose_name=_("tax amount value"), help_text=_("The flat amount of the tax. " "Mutually exclusive with percentage rates.")) currency = CurrencyField(default=None, blank=True, null=True, verbose_name=_("currency of tax amount")) enabled = models.BooleanField(default=True, verbose_name=_('enabled')) def clean(self): super(Tax, self).clean() if self.rate is None and self.amount is None: raise ValidationError(_('Either rate or amount is required')) if self.amount is not None and self.rate is not None: raise ValidationError(_('Cannot have both rate and amount')) if self.amount is not None and not self.currency: raise ValidationError( _("Currency is required if amount is specified")) def calculate_amount(self, base_amount): """ Calculate tax amount with this tax for given base amount. :type base_amount: shoop.utils.money.Money :rtype: shoop.utils.money.Money """ if self.amount is not None: return self.amount if self.rate is not None: return self.rate * base_amount raise ValueError("Improperly configured tax: %s" % self) def __str__(self): text = super(Tax, self).__str__() if self.rate is not None: text += " ({})".format(format_percent(self.rate, digits=3)) if self.amount is not None: text += " ({})".format(format_money(self.amount)) return text def _is_in_use(self): return self.order_line_taxes.exists() class Meta: verbose_name = _('tax') verbose_name_plural = _('taxes')
class Wallet(object): amount = MoneyProperty('value', 'currency') def __init__(self): self.value = 42 self.currency = 'EUR'