Changeset View
Changeset View
Standalone View
Standalone View
looper/models.py
| Show First 20 Lines • Show All 992 Lines • ▼ Show 20 Lines | |||||
| default=0, | default=0, | ||||
| blank=True, | blank=True, | ||||
| editable=False, | editable=False, | ||||
| help_text='How often an automatic collection attempt was made.', | help_text='How often an automatic collection attempt was made.', | ||||
| ) | ) | ||||
| paid_at = models.DateTimeField( | paid_at = models.DateTimeField( | ||||
| null=True, blank=True, help_text='When the order was paid, if at all.' | null=True, blank=True, help_text='When the order was paid, if at all.' | ||||
| ) | ) | ||||
| refunded_at = models.DateTimeField(null=True, help_text='Date of latest refund, if any.') | |||||
| refunded: Money = MoneyField(blank=True, default=0, help_text='Refunded total, if any.') | |||||
| tax_refunded: Money = MoneyField(blank=True, default=0, help_text='Refunded tax, if any.') | |||||
| retry_after = models.DateTimeField( | retry_after = models.DateTimeField( | ||||
| null=True, blank=True, help_text='When automatic collection should be retried.' | null=True, blank=True, help_text='When automatic collection should be retried.' | ||||
| ) | ) | ||||
| number = models.CharField( | number = models.CharField( | ||||
| null=True, | null=True, | ||||
| unique=True, | unique=True, | ||||
| ▲ Show 20 Lines • Show All 125 Lines • ▼ Show 20 Lines | |||||
| ) | ) | ||||
| if save: | if save: | ||||
| transaction.save() | transaction.save() | ||||
| return transaction | return transaction | ||||
| def __str__(self) -> str: | def __str__(self) -> str: | ||||
| return f'Order {self.number or self.pk} for Subscription {self.subscription_id}' | return f'Order {self.number or self.pk} for Subscription {self.subscription_id}' | ||||
| def total_refunded(self) -> Money: | def get_total_refunded(self) -> Money: | ||||
| """Compute the total refunded amount for this order. | """Compute the total refunded amount for this order. | ||||
| Since an order can have multiple transactions that all have a refunded | Since an order can have multiple transactions that all have a refunded | ||||
| amount, we have to sum them all. | amount, we have to sum them all. | ||||
| Note that this function assumes that all transactions have the same | Note that this function assumes that all transactions have the same | ||||
| currency as the order. | currency as the order. | ||||
| """ | """ | ||||
| result = self.transaction_set.all().aggregate(models.Sum('amount_refunded')) | result = self.transaction_set.all().aggregate(models.Sum('amount_refunded')) | ||||
| # Prevent None, which happens when an order has no transactions. | # Prevent None, which happens when an order has no transactions. | ||||
| refunded_sum = result['amount_refunded__sum'] or 0 | refunded_sum = result['amount_refunded__sum'] or 0 | ||||
| return Money(self.currency, refunded_sum) | return Money(self.currency, refunded_sum) | ||||
| def get_tax_refunded(self) -> Money: | |||||
| """Compute the amount of tax refunded for this order.""" | |||||
| if self.tax._cents == 0: | |||||
| return Money(self.currency, 0) | |||||
| total_refunded = self.get_total_refunded() | |||||
| if total_refunded._cents == 0: | |||||
| return Money(self.currency, 0) | |||||
| amount = round(total_refunded._cents * self.tax_rate / 100) | |||||
| return Money(self.currency, amount) | |||||
| def attach_log_entry(self, message: str) -> None: | def attach_log_entry(self, message: str) -> None: | ||||
| """Attach an admin history log entry to this Order and its Subscription.""" | """Attach an admin history log entry to this Order and its Subscription.""" | ||||
| admin_log.attach_log_entry(self, message) | admin_log.attach_log_entry(self, message) | ||||
| admin_url = reverse('admin:looper_order_change', kwargs={'object_id': self.id}) | admin_url = reverse('admin:looper_order_change', kwargs={'object_id': self.id}) | ||||
| subs: Subscription = self.subscription # make PyCharm happy | subs: Subscription = self.subscription # make PyCharm happy | ||||
| subs.attach_log_entry( | subs.attach_log_entry( | ||||
| format_html('{} for <a href="{}">order #{}</a>', message, admin_url, self.id) | format_html('{} for <a href="{}">order #{}</a>', message, admin_url, self.id) | ||||
| ▲ Show 20 Lines • Show All 210 Lines • ▼ Show 20 Lines | |||||
| ) | ) | ||||
| try: | try: | ||||
| self.payment_method.gateway.provider.refund(self.transaction_id, amount) | self.payment_method.gateway.provider.refund(self.transaction_id, amount) | ||||
| except looper.exceptions.GatewayError: | except looper.exceptions.GatewayError: | ||||
| raise | raise | ||||
| self.log.info( | self.log.info( | ||||
| 'Transaction pk=%d was succesful, storing transaction ID %r', | 'Refund of transaction pk=%d, ID %r for amount=%r was succesful', | ||||
| self.pk, | self.pk, | ||||
| self.transaction_id, | self.transaction_id, | ||||
| amount, | |||||
| ) | ) | ||||
| self.refunded_at = django.utils.timezone.now() | self.refunded_at = django.utils.timezone.now() | ||||
| self.amount_refunded = self.amount_refunded + amount | self.amount_refunded = self.amount_refunded + amount | ||||
| self.save(update_fields={'refunded_at', 'amount_refunded'}) | self.save(update_fields={'refunded_at', 'amount_refunded'}) | ||||
| self.attach_log_entry(f'Refund of {amount} was successful') | self.attach_log_entry(f'Refund of {amount} was successful') | ||||
| # Update order's refunded amount as well | |||||
| order = self.order | |||||
| order.refunded = order.get_total_refunded() | |||||
| order.refunded_at = self.refunded_at | |||||
| order.tax_refunded = order.get_tax_refunded() | |||||
| order.save(update_fields={'refunded', 'tax_refunded', 'refunded_at'}) | |||||
| order.attach_log_entry( | |||||
| f'Updated refunded amount to {order.refunded} (refunded tax {order.tax_refunded})' | |||||
| ) | |||||
| def attach_log_entry(self, message: str) -> None: | def attach_log_entry(self, message: str) -> None: | ||||
| """Attach an admin history log entry to this Transaction and its Order + Subscription.""" | """Attach an admin history log entry to this Transaction and its Order + Subscription.""" | ||||
| admin_log.attach_log_entry(self, message) | admin_log.attach_log_entry(self, message) | ||||
| admin_url = reverse('admin:looper_transaction_change', kwargs={'object_id': self.id}) | admin_url = reverse('admin:looper_transaction_change', kwargs={'object_id': self.id}) | ||||
| order: Order = self.order # make PyCharm happy | order: Order = self.order # make PyCharm happy | ||||
| order.attach_log_entry( | order.attach_log_entry( | ||||
| format_html('{} for <a href="{}">transaction #{}</a>', message, admin_url, self.id) | format_html('{} for <a href="{}">transaction #{}</a>', message, admin_url, self.id) | ||||
| ) | ) | ||||
| # class Coupon(models.Model): | # class Coupon(models.Model): | ||||
| # """Discounts! Implement later.""" | # """Discounts! Implement later.""" | ||||
| # pass | # pass | ||||
| # | # | ||||
| # | # | ||||
| Context not available. | |||||