class Shippable(TimestampedModel): """Add this to every shippable item""" name = models.CharField(max_length=255) name_receipt = models.CharField( _('Name for receipts'), max_length=255, help_text= '«посещение мастер-класса по TDD» или «Доступ к записи курсов кройки и шитья»' ) full_name = models.CharField( _('Full name for letters'), max_length=255, help_text= 'Билет на мастер-класс о TDD или «запись курсов кройки и шитья»', ) slug = models.SlugField() price = models.DecimalField(max_digits=8, decimal_places=2) old_price = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True) tinkoff_credit_promo_code = models.CharField( _('Fixed promo code for tinkoff credit'), max_length=64, blank=True, help_text=_('Used in tinkoff credit only')) class Meta: abstract = True def get_price_display(self): return format_price(self.price) def get_old_price_display(self): return format_price(self.old_price) def get_formatted_price_display(self): return format_old_price(self.old_price, self.price) def ship(self, to: User, order: Optional[Order] = None): return ShippingFactory.ship(self, to=to, order=order) def get_price(self, promocode=None) -> Decimal: promocode = apps.get_model('orders.PromoCode').objects.get_or_nothing( name=promocode) if promocode is not None: return promocode.apply(self.price) return self.price def get_template_id(self): """Get custom per-item template_id""" if not hasattr(self, 'template_id'): return if self.template_id is not None and len(self.template_id): return self.template_id
class Recipe(TimestampedModel): name = models.CharField(_("Name"), max_length=255, db_index=True, unique=True) cement_weight = models.DecimalField(_("Cement, kg"), max_digits=10, decimal_places=3, default=0) sand_weight = models.DecimalField(_("Sand, kg"), max_digits=10, decimal_places=3, default=0) gravel_weight = models.DecimalField(_("Gravel, kg"), max_digits=10, decimal_places=3, default=0) water_weight = models.DecimalField(_("Water, kg"), max_digits=10, decimal_places=3, default=0) admixture_weight = models.DecimalField(_("Admixture, kg"), max_digits=10, decimal_places=3, default=0) class Meta: verbose_name = _("Recipe") verbose_name_plural = _("Recipes") def __str__(self): return self.name
class Shippable(TimestampedModel): """Add this to every shippable item""" name = models.CharField(max_length=255) name_receipt = models.CharField( _('Name for receipts'), max_length=255, help_text= '«посещение мастер-класса по TDD» или «Доступ к записи курсов кройки и шитья»' ) full_name = models.CharField( _('Full name for letters'), max_length=255, help_text= 'Билет на мастер-класс о TDD или «запись курсов кройки и шитья»', ) slug = models.SlugField() price = models.DecimalField(max_digits=8, decimal_places=2) old_price = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True) class Meta: abstract = True def get_price_display(self): return format_price(self.price) def get_old_price_display(self): return format_price(self.old_price) def get_formatted_price_display(self): return format_old_price(self.old_price, self.price) def ship(self, to): return ShippingFactory.ship(self, to=to) def get_template_id(self): """Get custom per-item template_id""" if not hasattr(self, 'template_id'): return if self.template_id is not None and len(self.template_id): return self.template_id
class CreditNotification(TimestampedModel): """Notification for credit order by TinkoffCredit""" STATUSES_CHOICES = ( ('approved', _('Approved')), ('rejected', _('Rejected')), ('canceled', _('Canceled')), ('signed', _('Signed')), ) order_id = models.IntegerField() status = models.CharField(max_length=32, choices=STATUSES_CHOICES) bank_created = models.DateTimeField() first_payment = models.DecimalField(max_digits=10, decimal_places=2) order_amount = models.DecimalField(max_digits=10, decimal_places=2) credit_amount = models.DecimalField(max_digits=10, decimal_places=2) product = models.CharField(max_length=128) term = models.IntegerField() monthly_payment = models.DecimalField(max_digits=10, decimal_places=2) phone = models.CharField(max_length=64, null=True, blank=True) first_name = models.CharField(max_length=128, null=True, blank=True) last_name = models.CharField(max_length=128, null=True, blank=True) middle_name = models.CharField(max_length=32, blank=True, null=True) loan_number = models.CharField(max_length=128, blank=True, null=True) email = models.CharField(max_length=128, null=True, blank=True)
class PaymentNotification(TimestampedModel): STATUS_CHOICES = ( ('AUTHORIZED', _('Authorized')), ('CONFIRMED', _('Confirmed')), ('REVERSED', _('Reversed')), ('REFUNDED', _('Refunded')), ('PARTIAL_REFUNDED', _('Partial refunded')), ('REJECTED', _('Rejected')), ) terminal_key = models.CharField(max_length=512) order_id = models.IntegerField() success = models.BooleanField() status = models.CharField(max_length=128, choices=STATUS_CHOICES) payment_id = models.BigIntegerField() error_code = models.CharField(max_length=512, null=True) amount = models.DecimalField(decimal_places=2, max_digits=9) rebill_id = models.BigIntegerField(null=True) card_id = models.CharField(null=True, max_length=64) pan = models.CharField(max_length=128, null=True) data = models.TextField(null=True) token = models.CharField(max_length=512) exp_date = models.CharField(max_length=32, null=True)
class Batch(TimestampedModel): recipe = models.ForeignKey("lab.recipe", verbose_name=_("Recipe"), on_delete=models.PROTECT) volume = models.DecimalField(_("Volume, m3"), max_digits=10, decimal_places=3, default=0) cement_weight = models.DecimalField(_("Cement, kg"), max_digits=10, decimal_places=3, default=0) sand_weight = models.DecimalField(_("Sand, kg"), max_digits=10, decimal_places=3, default=0) gravel_weight = models.DecimalField(_("Gravel, kg"), max_digits=10, decimal_places=3, default=0) water_weight = models.DecimalField(_("Water, kg"), max_digits=10, decimal_places=3, default=0) admixture_weight = models.DecimalField(_("Admixture, kg"), max_digits=10, decimal_places=3, default=0) class Meta: verbose_name = _("Batch") verbose_name_plural = _("Batches") def __str__(self): return f"{self.recipe.name}: {self.volume} м3"
class Order(TimestampedModel): objects = OrderQuerySet.as_manager() # type: OrderQuerySet user = models.ForeignKey('users.User', on_delete=models.PROTECT) price = models.DecimalField(max_digits=9, decimal_places=2) promocode = models.ForeignKey('orders.PromoCode', verbose_name=_('Promo Code'), blank=True, null=True, on_delete=models.PROTECT) paid = models.DateTimeField( _('Date when order got paid'), null=True, blank=True, help_text=_('If set during creation, order automaticaly gets shipped'), ) shipped = models.DateTimeField(_('Date when order was shipped'), null=True, blank=True) course = ItemField('courses.Course', null=True, blank=True, on_delete=models.PROTECT) record = ItemField('courses.Record', null=True, blank=True, on_delete=models.PROTECT) bundle = ItemField('courses.Bundle', null=True, blank=True, on_delete=models.PROTECT) class Meta: ordering = ['-id'] verbose_name = _('Order') verbose_name_plural = _('Orders') def __str__(self): return f'Order #{self.pk}' @property def item(self): """Find the attached item. Simple replacement for ContentType framework """ for field in self.__class__._meta.get_fields(): if getattr(field, '_is_item', False): if getattr(self, f'{field.name}_id', None) is not None: return getattr(self, field.name) @classmethod def _iterate_items(cls) -> Iterable[models.fields.Field]: for field in cls._meta.get_fields(): if getattr(field, '_is_item', False): yield field @classmethod def get_item_foreignkey(cls, item) -> Optional[models.fields.Field]: """ Given an item model, returns the ForeignKey to it""" for field in cls._iterate_items(): if field.related_model == item.__class__: return field.name def reset_items(self): for field in self._iterate_items(): setattr(self, field.name, None) def set_item(self, item): foreign_key = self.__class__.get_item_foreignkey(item) if foreign_key is not None: self.reset_items() setattr(self, foreign_key, item) return raise UnknownItemException('There is not foreignKey for {}'.format( item.__class__)) def set_paid(self): is_already_paid = self.paid is not None self.paid = timezone.now() self.save() if not is_already_paid and self.item is not None: self.ship() def ship(self): """Ship the order. Better call it asynchronously""" self.item.ship(to=self.user) self.shipped = timezone.now() self.save() order_got_shipped.send( sender=self.__class__, order=self, )
class Order(TimestampedModel): objects = OrderQuerySet.as_manager() # type: OrderQuerySet user = models.ForeignKey('users.User', verbose_name=_('User'), on_delete=models.PROTECT) price = models.DecimalField(_('Price'), max_digits=9, decimal_places=2) promocode = models.ForeignKey('orders.PromoCode', verbose_name=_('Promo Code'), blank=True, null=True, on_delete=models.PROTECT) paid = models.DateTimeField( _('Date when order got paid'), null=True, blank=True, help_text=_('If set during creation, order automaticaly gets shipped'), ) shipped = models.DateTimeField(_('Date when order was shipped'), null=True, blank=True) desired_bank = models.CharField(_('User-requested bank string'), blank=True, max_length=32) course = ItemField(to='products.Course', verbose_name=_('Course'), null=True, blank=True, on_delete=models.PROTECT) record = ItemField(to='products.Record', verbose_name=_('Record'), null=True, blank=True, on_delete=models.PROTECT) bundle = ItemField(to='products.Bundle', verbose_name=_('Bundle'), null=True, blank=True, on_delete=models.PROTECT) giver = models.ForeignKey('users.User', verbose_name=_('Giver'), null=True, blank=True, on_delete=models.SET_NULL, related_name='created_gifts') desired_shipment_date = models.DateTimeField( _('Date when the gift should be shipped'), null=True, blank=True) gift_message = models.TextField(_('Gift message'), default='', blank=True) notification_to_giver_is_sent = models.BooleanField(default=False) class Meta: ordering = ['-id'] verbose_name = _('Order') verbose_name_plural = _('Orders') def __str__(self): return f'Order #{self.pk}' @property def item(self): """Find the attached item. Simple replacement for ContentType framework """ for field in self.__class__._meta.get_fields(): if getattr(field, '_is_item', False): if getattr(self, f'{field.name}_id', None) is not None: return getattr(self, field.name) @classmethod def _iterate_items(cls) -> Iterable[models.fields.Field]: for field in cls._meta.get_fields(): if getattr(field, '_is_item', False): yield field @classmethod def get_item_foreignkey(cls, item) -> Optional[models.fields.Field]: """ Given an item model, returns the ForeignKey to it""" for field in cls._iterate_items(): if field.related_model == item.__class__: return field.name def reset_items(self): for field in self._iterate_items(): setattr(self, field.name, None) def set_item(self, item): foreign_key = self.__class__.get_item_foreignkey(item) if foreign_key is not None: self.reset_items() setattr(self, foreign_key, item) return raise UnknownItemException('There is not foreignKey for {}'.format( item.__class__)) def set_paid(self, silent=False): from orders.services.order_is_paid_setter import Griphook Griphook(self, silent=silent)() def ship(self, silent: bool = False): """Ship the order. Better call it asynchronously""" from orders.services.order_shipper import Pigwidgeon Pigwidgeon(self, silent=silent)()