Ejemplo n.º 1
0
class ExchangeRate(models.Model):
    class Meta:
        verbose_name = _('Exchange Rate')
        verbose_name_plural = _('Exchange Rates')

    source_currency = CurrencyField()
    target_currency = CurrencyField()
    rate = models.DecimalField(max_digits=12, decimal_places=6)
    datetime = models.DateTimeField()
Ejemplo n.º 2
0
class Sheaf(models.Model):
    account = models.ForeignKey(
        verbose_name=_('account'),
        to=Account,
        related_name='sheaves'
    )
    amount = models.DecimalField(
        verbose_name=_('sheaf'),
        max_digits=settings.MAX_DIGITS,
        decimal_places=settings.DECIMAL_PLACES
    )
    currency = CurrencyField(
        verbose_name=_('currency'),
        price_field='amount',
        default=settings.BASE_CURRENCY
    )

    def __str__(self):
        return '{amount} {currency} on {account}'.format(
            amount=self.amount,
            currency=self.currency,
            account=self.account
        )

    def __eq__(self, other):
        return type(other) == Sheaf and \
               self.account == other.account and \
               self.amount == other.amount and \
               self.currency == other.currency

    def __ne__(self, other):
        return not self.__eq__(other)

    class Meta:
        unique_together = ('account', 'currency')
Ejemplo n.º 3
0
class UserSettings(CommonInfo):
    """The user settings class."""

    objects = UserSettingsManager()

    language = LanguageField(
        verbose_name=_("language"),
        null=True,
        blank=True,
        default=settings.LANGUAGE_CODE,
        choices=settings.APP_LANGUAGES,
    )
    currency = CurrencyField(
        verbose_name=_("currency"),
        null=True,
        blank=True,
        choices=CURRENCY_CHOICES,
    )
    user = models.OneToOneField(
        User,
        on_delete=models.CASCADE,
        related_name="settings",
        verbose_name=_("user"),
    )
    is_last_name_hidden = models.BooleanField(
        default=False,
        help_text=_("Is the last name hidden from other users?"),
        verbose_name=_("is the last name hidden?"),
    )
    units = UnitsField(verbose_name=_("units"))

    def __str__(self) -> str:
        """Return the string representation."""
        return f"{self.user} settings"
Ejemplo n.º 4
0
class Organization(models.Model):
    name = models.CharField(max_length=255, default=None)
    subscription = models.CharField(max_length=1, choices=SUBSCRIPTION_TYPES)
    owner = models.ForeignKey(User, on_delete=models.PROTECT)
    number_scheme = models.CharField(max_length=1, choices=NUMBER_SCHEMES, default=NUMBER_SCHEME_SEMI_INTELLIGENT)
    number_class_code_len = models.PositiveIntegerField(default=NUMBER_CLASS_CODE_LEN_DEFAULT,
                                                        validators=[MinValueValidator(NUMBER_CLASS_CODE_LEN_MIN), MaxValueValidator(NUMBER_CLASS_CODE_LEN_MAX)])
    number_item_len = models.PositiveIntegerField(default=NUMBER_ITEM_LEN_DEFAULT,
                                                  validators=[MinValueValidator(NUMBER_ITEM_LEN_MIN), MaxValueValidator(NUMBER_ITEM_LEN_MAX)])
    number_variation_len = models.PositiveIntegerField(default=NUMBER_VARIATION_LEN_DEFAULT,
                                                       validators=[MinValueValidator(NUMBER_VARIATION_LEN_MIN), MaxValueValidator(NUMBER_VARIATION_LEN_MAX)])
    google_drive_parent = models.CharField(max_length=128, blank=True, default=None, null=True)
    currency = CurrencyField(max_length=3, choices=CURRENCY_CHOICES, default='USD')

    def number_cs(self):
        return "C" * self.number_class_code_len

    def number_ns(self):
        return "N" * self.number_item_len

    def number_vs(self):
        return "V" * self.number_variation_len

    def __str__(self):
        return u'%s' % self.name

    def seller_parts(self):
        return SellerPart.objects.filter(seller__organization=self)

    def save(self, *args, **kwargs):
        super(Organization, self).save()
        SellerPart.objects.filter(seller__organization=self).update(unit_cost_currency=self.currency, nre_cost_currency=self.currency)
Ejemplo n.º 5
0
class Rate(models.Model, metaclass=RateMetaclass):
    """
    Main entity for representing rates of currencies.
    """
    date_of_rate = models.DateTimeField(verbose_name='Rate date')
    base_currency = CurrencyField(default=settings.DEFAULT_CURRENCY,
                                  verbose_name='Default currency')
    created_at = models.DateTimeField(
        auto_now_add=True, verbose_name='Date of request to EU bank')

    @classmethod
    def sync(cls):
        """
        Method for updating rate from EU CENTRAL BANK information.
        """
        import xml.etree.ElementTree as ET
        response = requests.get(settings.EU_CENTRAL_BANK_URL)
        parsed = ET.fromstring(response.content)
        kwgs = {
            'base_currency': 'EUR',
            'EUR': 1,
            'date_of_rate': parsed[2][0].attrib['time']
        }
        for currency_info in parsed[2][0]:
            code = currency_info.attrib['currency']
            rate = currency_info.attrib['rate']
            if code in settings.CURRENCIES:
                kwgs[code] = rate

        cls.objects.create(**kwgs)

    def __str__(self):
        return f'Rate {self.date_of_rate.strftime("%d.%m.%Y %H:%M")}'
Ejemplo n.º 6
0
class Contribution(models.Model):
    _import = models.ForeignKey(
        ContributionImport,
        null=True,
        blank=True,
        on_delete=models.CASCADE,
        related_name="contributions",
    )
    _import.verbose_name = _("Import")
    person = models.ForeignKey(Person,
                               null=True,
                               on_delete=models.SET_NULL,
                               related_name="contributions")
    person.verbose_name = _("Person")

    date = models.DateField(_("Date"))
    note = models.CharField(_("Note"), max_length=255)
    debitaccount = models.CharField(_("Debit Account"), max_length=32)
    creditaccount = models.CharField(_("Credit Account"), max_length=32)
    amount = models.DecimalField(_("Amount"), max_digits=12, decimal_places=2)
    currency = CurrencyField(_("Currency"), choices=CURRENCY_CHOICES)
    currency.lazy_choices = lambda *args: settings.PREFERRED_CURRENCIES

    def amount_formatted(self):
        return Money(self.amount, self.currency)

    amount_formatted.verbose_name = _("Amount")

    def __str__(self):
        return f"{self.date}: {self.person} {self.amount_formatted()}"

    class Meta:
        verbose_name = _("Contribution")
        verbose_name_plural = _("Contributions")
        ordering = ["-date"]
Ejemplo n.º 7
0
class CurrencyRate(models.Model):
    class Meta:
        verbose_name = _('Currency Rate')
        verbose_name_plural = _('Currency Rates')

    source_currency = CurrencyField(_('source currency'), )
    target_currency = CurrencyField(
        _('target currency'),
        default=moneyed.EUR,
    )
    rate = models.DecimalField(
        _('rate'),
        max_digits=10,
        decimal_places=5,
    )
    uploaded = models.DateTimeField(_('uploaded'), )
Ejemplo n.º 8
0
class Provider(Model):
    name = CharField(max_length=100)
    email = EmailField(blank=True, null=True)
    phone = PhoneNumberField(blank=True, null=True)
    cur = CurrencyField(default='usd')

    def __str__(self):
        return "{}({})".format(self.name, self.id)
Ejemplo n.º 9
0
class Revision(models.Model):
    version = models.CharField(max_length=10)
    country = models.ForeignKey(Country, on_delete=models.CASCADE)
    pay_period = models.PositiveIntegerField(choices=PayPeriod.choices)
    currency = CurrencyField()
    date = models.DateField()

    def __str__(self):
        return self.version
Ejemplo n.º 10
0
class ServiceProvider(models.Model):
    name = models.CharField(max_length=100)
    email = models.EmailField()
    phone = PhoneNumberField()
    language = LanguageField(default='en')
    currency = CurrencyField(default='USD')

    def __str__(self):
        return self.name
Ejemplo n.º 11
0
class Provider(Dated):
    name = models.CharField(max_length=255)
    email = models.EmailField(blank=True)
    phone = PhoneNumberField(blank=True)
    language = LanguageField(default='en')
    currency = CurrencyField(default='USD')

    def __str__(self):
        return self.name

    class Meta:
        ordering = ['id']
Ejemplo n.º 12
0
class Provider(models.Model):
    """Model definition for Provider."""

    name = models.CharField("Provider's name.", max_length=50)
    phone = PhoneNumberField("Provider's phone number.")
    language = LanguageField("Provider's language.")
    currency = CurrencyField("Provider's currency.")
    user = models.OneToOneField(User, on_delete=models.CASCADE)

    def __str__(self):
        """Unicode representation of Provider."""
        return self.name
Ejemplo n.º 13
0
class Account(CodeNaturalKeyAbstractModel):
    name = models.CharField(max_length=100, null=True, blank=True)
    owner = models.ForeignKey(Actor,
                              on_delete=models.CASCADE,
                              related_name='accounts')
    currency = CurrencyField()
    description = models.CharField(max_length=200, null=True, blank=True)

    def __str__(self):
        return f'{self.name} ({self.currency}) of {self.owner}'

    def balance(self):
        return reduce(lambda x, item: x + item.amount, self.payments_in.all(), Money(0, self.currency)) \
               - reduce(lambda x, item: x + item.amount, self.payments_out.all(), Money(0, self.currency))
Ejemplo n.º 14
0
class Account(models.Model):

    type = models.IntegerField(choices=AccountType.choices)
    currency = CurrencyField()
    account_num = models.CharField(max_length=30)
    name = models.CharField(max_length=50)
    description = models.TextField()
    is_hidden = models.BooleanField(default=False)
    parent_id = models.IntegerField(null=True, blank=True)
    ending_balance_amt = models.DecimalField(
        max_digits=14,
        decimal_places=2,
    )
    bank_number = models.CharField(max_length=25)
Ejemplo n.º 15
0
class Provider(Dated):
    

name = models.CharField(max_length=255)
    
email = models.EmailField(blank=True)
    
phone = PhoneNumberField(blank=True)
    
language = LanguageField(default='en')
    
currency = CurrencyField(default='USD')

    

def __str__(self):
        
return self.name

    

class Meta:
        
ordering = ['id']




class ServiceArea(Dated):
    
provider = models.ForeignKey(Provider)
    
name = models.CharField(max_length=255)
    
poly = models.PolygonField()
    
price = MoneyField(max_digits=19, decimal_places=8)

    

def __str__(self):
        
return self.name

    

class Meta:
        
ordering = ['id']
Ejemplo n.º 16
0
class BankAccount(models.Model):
    """Bank account."""

    account_number = models.TextField(unique=True, verbose_name=_('Account number'))
    account_name = models.TextField(blank=True, verbose_name=_('Account name'))
    currency = CurrencyField()

    class Meta:
        """Meta class."""

        verbose_name = _('Bank account')
        verbose_name_plural = _('Bank accounts')

    def __str__(self):
        """Return string representation of bank account."""
        return '{} {}'.format(self.account_name, self.account_number)
Ejemplo n.º 17
0
class Provider(models.Model):
    """
    Provider's model
    """
    name = models.CharField(_('Name'), max_length=256)
    email = models.EmailField(_('Email'), unique=True)
    phone_number = PhoneNumberField()
    language = LanguageField()
    currency = CurrencyField()
    created_at = models.DateTimeField(_('Created at'), auto_now_add=True)

    class Meta:
        verbose_name = _('Provider')
        verbose_name_plural = _('Providers')

    def __str__(self):
        return f'{self.name} Provider'
Ejemplo n.º 18
0
class Country(Place):
    code = models.CharField(max_length=2, db_index=True)
    population = models.IntegerField()
    continent = models.CharField(max_length=2)
    tld = models.CharField(max_length=5)
    languages = models.ManyToManyField(Language, related_name="countries")
    currency = CurrencyField()

    class Meta:
        ordering = ['name']
        verbose_name_plural = "countries"

    @property
    def parent(self):
        return None

    def __unicode__(self):
        return force_unicode(self.name)
Ejemplo n.º 19
0
class UserProfile(models.Model):

    user = models.OneToOneField(User, on_delete=models.CASCADE)
    default_currency = CurrencyField(default=DEFAULT_CURRENCY,
                                     choices=CURRENCY_CHOICES)

    notif_invited_to_event = models.BooleanField(default=True)
    notif_requested_by_one_user = models.BooleanField(default=True)
    notif_upcoming_event = models.BooleanField(default=True)
    notif_edited_event = models.BooleanField(default=True)

    @classmethod
    def get_or_create(cls, user, *args, **kwargs):
        try:
            profile = cls.objects.get(user=user)
            return profile
        except cls.DoesNotExist:
            return cls(user=user, *args, **kwargs)
Ejemplo n.º 20
0
class Country(models.Model
              ):  # could expand on pypi.python.org/pypi/django-countries

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name = models.CharField(max_length=100, blank=True)
    code = models.CharField(max_length=3, blank=True)
    calling_code = models.CharField(max_length=3, blank=True)

    # assuming countries stick to one currency nationwide
    currency = CurrencyField(help_text='default currency for the country',
                             null=True,
                             blank=True)

    # MODEL PROPERTIES

    # MODEL FUNCTIONS
    def __str__(self):
        return str(self.code)

    class Meta:
        verbose_name_plural = 'countries'
Ejemplo n.º 21
0
class Account(Model):
    OPEN = 'OPEN'
    CLOSED = 'CLOSED'
    STATUS_CHOICES = (
        (OPEN, _('Open')),
        (CLOSED, _('Closed')),
    )
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    created = models.DateTimeField(auto_now_add=True, db_index=True)
    modified = models.DateTimeField(auto_now=True)
    owner = models.OneToOneField(settings.AUTH_USER_MODEL,
                                 related_name='billing_account',
                                 on_delete=PROTECT)
    currency = CurrencyField(db_index=True)
    status = FSMField(max_length=20,
                      choices=STATUS_CHOICES,
                      default=OPEN,
                      db_index=True)

    objects = AccountQuerySet.as_manager()

    def balance(self, as_of: date = None):
        charges = Charge.objects.filter(account=self)
        transactions = Transaction.successful.filter(account=self)
        if as_of is not None:
            charges = charges.filter(created__lte=as_of)
            transactions = transactions.filter(created__lte=as_of)
        return total_amount(transactions) - total_amount(charges)

    @transition(field=status, source=OPEN, target=CLOSED)
    def close(self):
        pass

    @transition(field=status, source=CLOSED, target=OPEN)
    def reopen(self):
        pass

    def __str__(self):
        return str(self.owner)
Ejemplo n.º 22
0
class User(AbstractBaseUser, PermissionsMixin):
    """Custom user model that support using email instead of username"""
    email = models.EmailField(max_length=255, unique=True)
    name = models.CharField(max_length=255)
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)

    main_currency = CurrencyField(verbose_name=_('Main currency'))

    objects = UserManager()

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ('name', )

    def save(self, *args, **kwargs):
        created = self._state.adding
        super(User, self).save(*args, **kwargs)

        if created:
            from .services import (create_initial_income_categories,
                                   create_initial_expense_categories)
            create_initial_income_categories(self)
            create_initial_expense_categories(self)
Ejemplo n.º 23
0
class Shop(Timestampable, Locatable, Contactable, Translatable, Permalinkable,
           models.Model):

    owner = models.ForeignKey(AUTH_USER_MODEL,
                              on_delete=models.PROTECT,
                              related_name="shop")
    name = models.CharField(max_length=50)
    description = models.TextField(default="", blank=True)
    logo_image = models.OneToOneField(
        'common.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='shop_as_logo',
        help_text=
        'if not square will be padded, like a social media profile pic')
    icon_image = models.OneToOneField(
        'common.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='shop_as_icon',
        help_text='version of the logo that fits in a small square')

    pushover_user_key = models.CharField(max_length=30, default="", blank=True)
    pushover_device_name = models.CharField(max_length=25,
                                            default="",
                                            blank=True)

    # SOCIAL ACCOUNTS
    facebook_href = models.URLField(default="", blank=True)
    instagram_href = models.URLField(default="", blank=True)
    google_maps_href = models.URLField(default="", blank=True)
    trip_advisor_href = models.URLField(default="", blank=True)

    # SETTINGS
    is_ghost_location = models.BooleanField(default=False)
    currency = CurrencyField(default='THB')

    # INTERFACES: BOTS AND WEBSITES
    # line_channel = line_app.LineChannel
    # website = website.Website

    gramables = models.ManyToManyField('common.Image', blank=True)

    # INHERITED FIELDS:
    # address, latitude, longitude
    # contact_name, contact_phone, contact_email
    # base_language, language

    # HISTORY MANAGER
    # history = HistoricalRecords()

    # MODEL PROPERTIES

    @property
    def customer_line_channel(self):
        from apps.line_app.models.line_channel import LineChannel, CUSTOMER_CHANNEL
        return LineChannel.objects.filter(
            shop=self, channel_type=CUSTOMER_CHANNEL).first()

    @property
    def orders(self):
        from apps.shop.models import Order
        return Order.objects.filter(
            line_channel_membership__line_channel__shop_id=self.id)

    @property
    def is_open(self) -> bool:
        return any(
            [schedule.is_on_now for schedule in self.menu.schedules.all()])

    @property
    def time_until_next_open(self) -> timedelta:
        return min([
            schedule.time_until_next_on
            for schedule in self.menu.schedules.all()
        ])

    # MODEL FUNCTIONS
    def setup_related_models(self):
        from apps.line_app.models.line_channel import LineChannel, CUSTOMER_CHANNEL
        line_channel, lc_created = LineChannel.objects.get_or_create(
            shop=self, channel_type=CUSTOMER_CHANNEL)
        from apps.shop.models import Menu, Schedule
        menu, m_created = Menu.objects.get_or_create(shop=self)
        if not Schedule.objects.filter(menu=menu).exists():
            schedule = Schedule.objects.create(menu=menu,
                                               available_weekdays=list(
                                                   range(7)))
            if not self.timezone:
                self.timezone = pytz.timezone('Asia/Bangkok')
                self.save()

    def __str__(self):
        return self.name or f"Shop {self.id}"

    def reset_slug(self):
        self.slug = slugify(self.name)

    # @models.permalink
    # def get_absolute_url(self):
    #     url_kwargs = self.get_url_kwargs(slug=self.slug)
    #     return (self.url_name, (), url_kwargs)

    def get_absolute_url(self):
        return reverse('shop:shop', kwargs={'shop_slug': self.slug})

    # META
    class Meta:
        ordering = ('name', )
        unique_together = ('owner', 'name')
Ejemplo n.º 24
0
class Order(TimeStampedModel, StatusModel):
    STATUS = Choices(
        'cart',
        'confirmed',
        'paid',
        'canceled',
        'completed',
        'refunded',
    )
    billing_address = models.ForeignKey(
        Address,
        null=True,
        blank=True,
        related_name='+',
    )
    shipping_address = models.ForeignKey(
        Address,
        null=True,
        blank=True,
        related_name='+',
    )
    currency = CurrencyField()
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        blank=True,
        null=True,
        verbose_name=_('user'),
        related_name='orders',
        on_delete=models.SET_NULL,
    )
    email = models.EmailField(
        _('email address'),
        blank=True,
        default='',
    )
    custom = pg_fields.JSONField(
        _('custom order data'),
        blank=True,
        default=dict,
    )

    def get_amount(self):
        zero = decimal.Decimal('0.00')
        return max((self.items.aggregate(sum=models.Sum(
            ((models.F('price') + models.F('vat') -
              models.Func(models.F('discount'), function='ABS')) *
             models.F('quantity')),
            output_field=models.DecimalField()))['sum'] or zero), zero)

    get_amount.short_description = _('amount')
    amount = property(get_amount)

    def get_order_no(self):
        return (self.id * 10) + int(luhn.calc_check_digit(self.id))

    get_order_no.short_description = _('order number')
    order_no = property(get_order_no)

    def get_remainder(self):
        return self.amount - (self.invoices.filter(
            status__in=('authorized', 'captured')).aggregate(
                sum=models.Sum(models.F('paid')))['sum']
                              or decimal.Decimal('0.00'))

    get_remainder.short_description = _('remainder')
    remainder = property(get_remainder)
Ejemplo n.º 25
0
class Order(Document):
    DOCUMENT_NUMBER_TEMPLATE = 'CO-{number_month}'

    class State(TextChoices):
        NEW = 'NEW', "Новый"
        WAITING = 'WAITING', "Ожидает"
        CONFIRMED = 'CONFIRMED', "Подтверждён"
        COMPLETED = 'COMPLETED', "Выполнен"
        PROCESSING = 'PROCESSING', "Исполняется"
        CANCELLED = 'CANCELLED', "Отменён"

    FINAL_STATES = (State.COMPLETED, State.CANCELLED)

    class FulfillOn(TextChoices):
        CREATED = 'CREATED', "когда заказ создан"
        CONFIRMED = 'CONFIRMED', "когда заказ подтверждён"
        ORDER_PAYED_FULL = 'ORDER_PAYED_FULL', "когда счет оплачен полностью"
        ORDER_PAYED_PARTLY = 'ORDER_PAYED_PARTLY', "когда счет оплачен частично"

    state = models.CharField(max_length=200,
                             choices=State.choices,
                             null=True,
                             blank=True)
    seller = models.ForeignKey(Actor,
                               on_delete=models.CASCADE,
                               null=True,
                               blank=True,
                               related_name='+')
    buyer = models.ForeignKey(Actor,
                              on_delete=models.CASCADE,
                              null=True,
                              blank=True,
                              related_name='+')
    currency = CurrencyField()
    valid_until = models.DateField(null=True, blank=True)

    comment = models.TextField(null=True, blank=True)
    fulfill_on = models.CharField(max_length=200,
                                  choices=FulfillOn.choices,
                                  null=True,
                                  blank=True)

    @property
    def amount(self):
        currency = self.currency
        if not currency:
            raise ValueError(f'No currency in order {self}')
        return reduce(lambda x, item: x + item.sum, self.items.all(),
                      Money(0, currency))

    def add_item(self, product: Product, quantity: int = 1):
        OrderItem.objects.create(product=product,
                                 order=self,
                                 price=product.price,
                                 quantity=quantity)

    def create_invoice(self) -> 'Invoice':
        return Invoice(
            order=self,
            amount=self.amount,
            seller=self.seller,
            buyer=self.buyer,
        )

    def fulfill(self):
        states = Order.State
        assert self.state in (
            None, states.NEW,
            states.CONFIRMED), f"Cannot fulfill from state {self.state}"
        self.set_state(states.PROCESSING)
Ejemplo n.º 26
0
class BankAccount(models.Model):
    """Bank account."""

    account_number = models.TextField(verbose_name=_('Account number'))
    account_name = models.TextField(blank=True, verbose_name=_('Account name'))
    currency = CurrencyField()
Ejemplo n.º 27
0
class ExchangeRate(models.Model):
    date = models.DateField()
    currency = CurrencyField()
    base_currency = CurrencyField()
    exchange_rate = models.FloatField()
Ejemplo n.º 28
0
class Transaction(models.Model):
    UNITS = (
        ('pcs', _('pieces')),
        ('kg', _('kilos')),
        ('g', _('grams')),
        ('l', _('liters')),
        ('gal', _('gallons')),
        ('p', _('pounds'))
    )

    date = models.DateField(
        verbose_name=_('date')
    )
    approved = models.BooleanField(
        verbose_name=_('approved'),
        default=True
    )

    account = models.ForeignKey(
        verbose_name=_('account'),
        to=Account,
        related_name='transactions'
    )

    amount = models.DecimalField(
        verbose_name=_('amount'),
        max_digits=settings.MAX_DIGITS,
        decimal_places=settings.DECIMAL_PLACES,
    )
    currency = CurrencyField(
        verbose_name=_('currency'),
        default=settings.BASE_CURRENCY,
        price_field=amount
    )

    quantity = models.DecimalField(
        verbose_name=_('good quantity'),
        max_digits=settings.MAX_DIGITS,
        decimal_places=settings.DECIMAL_PLACES,
        null=True,
        blank=True,
        default=None
    )
    unit = models.CharField(
        verbose_name=_('unit of measurement'),
        max_length=255,
        choices=UNITS,
        null=True,
        blank=True,
        default=None
    )

    invoice = models.ForeignKey(
        to=Invoice,
        related_name='transactions',
        blank=True, null=True, default=None
    )

    comment = models.TextField(
        verbose_name=_('comment'),
        blank=True
    )

    @property
    def price(self):
        if self.quantity:
            return self.amount / self.quantity

    def __str__(self):
        return ('{amount} {currency} @ {account} on {date} ({app}approved)'
                .format(amount=self.amount,
                        currency=self.currency,
                        account=self.account,
                        date=self.date,
                        app='not ' if not self.approved else ''))

    @transaction.atomic
    def save(self, *args, **kwargs):
        if not self.id:
            sheaf = self.account.sheaves.filter(currency=self.currency).first()
            if not sheaf:
                Sheaf.objects.create(account=self.account,
                                     currency=self.currency,
                                     amount=self.amount).save()
            else:
                sheaf.amount += self.amount
                sheaf.save()
            super(Transaction, self).save(*args, **kwargs)
        else:
            old_account = Transaction.objects.get(pk=self.pk).account
            super(Transaction, self).save(*args, **kwargs)
            self.account.recalculate_summary(atomic=False)
            if self.account != old_account:
                old_account.recalculate_summary(atomic=False)

    class Meta:
        ordering = ['-date']
Ejemplo n.º 29
0
class Task(models.Model):
    GOERLI = 'goerli'
    MUMBAI = 'mumbai'
    BINANCE_TESTNET = 'bsc_testnet'
    BINANCE_MAINNET = 'bsc_mainnet'
    CHAIN_CHOICES = (
        (GOERLI, 'Goerli (ETH)'),
        (MUMBAI, 'Mumbai (MATIC)'),
        (BINANCE_TESTNET, 'bsc_testnet (BNB)'),
        (BINANCE_MAINNET, 'bsc_mainnet (BNB)'),
    )

    title = models.CharField("Title", max_length=100)
    description = models.TextField("Description", max_length=100)
    chain = models.CharField("Chain", max_length=15, choices=CHAIN_CHOICES, default=GOERLI)
    uuid = models.UUIDField('Web3 Task Identifier', unique=True, null=True)
    website_link = models.CharField("Website Link", max_length=200, validators=[validators.URLValidator])
    website_image = models.ImageField("Website Image", upload_to="task_website_image", null=True)
    contract_address = models.CharField("Contract address", max_length=42)
    og_image_link = models.URLField("OpenGraph Image Path", max_length=200, blank=True, null=True)
    time_duration = models.DurationField("Time duration", default=datetime.timedelta(seconds=30))
    created = models.DateTimeField("Created", auto_now_add=True)  # No show
    modified = models.DateTimeField("Modified", auto_now=True)  # No show
    # XXX: rename `is_active` to `is_enabled`
    is_active = models.BooleanField("Is active", default=True)  # Read-only on API, manageable by admin
    is_active_web3 = models.BooleanField("Is active on Web3", default=True)  # Is active on Web 3
    initial_tx_hash = models.CharField("Initial transaction hash", max_length=66, blank=True, default='')
    user = models.ForeignKey(User, related_name='tasks', on_delete=models.DO_NOTHING)
    warning_message = models.CharField("Warning message", max_length=100, blank=True)

    currency = CurrencyField(
        choices=settings.CURRENCY_CHOICES,
        default=None,
        editable=False,
        max_length=5,
        null=True
    )
    reward_per_click = fields.MoneyField(
        "Reward per click",
        max_digits=18,
        decimal_places=10,
        default_currency=None,
        currency_field_name='currency'
    )
    remaining_balance = fields.MoneyField(
        "Remaining balance for task",
        max_digits=18,
        decimal_places=10,
        null=True,
        default=None,
        currency_field_name='currency'
    )
    initial_budget = fields.MoneyField(
        "Initial task budget",
        max_digits=18,
        decimal_places=10,
        null=True,
        default=None,
        currency_field_name='currency'
    )

    objects = managers.TaskManager()

    class Meta:
        verbose_name_plural = "Task"

    def __str__(self):  # pragma: no cover
        if self.title:
            return f"Task({self.title})"
        return "Task"

    def save(self, *args, **kwargs):
        if not self.currency or self.currency == 'USD':
            self.currency = self.chain_instance.currency
            for field in (
                'reward_per_click',
                'remaining_balance',
                'initial_budget',
            ):
                if getattr(self, field, False):
                    value = Money(getattr(self, field).amount, 'USD')
                    setattr(self, field, convert_money(value, self.chain_instance.currency))
        return super(Task, self).save(*args, **kwargs)

    @property
    def chain_instance(self) -> 'Web3Config':
        if self.chain in settings.WEB3_CONFIG:
            return settings.WEB3_CONFIG[self.chain]
        else:
            raise ImproperlyConfigured(f'{self.chain} not found in {list(settings.WEB3_CONFIG.keys())}')

    @property
    def reward_usd_per_click(self):
        return convert_money(self.reward_per_click, 'USD')

    @property
    def remaining_balance_usd(self):
        return convert_money(self.remaining_balance, 'USD')
Ejemplo n.º 30
0
class Account(Model):
    OPEN = 'OPEN'
    CLOSED = 'CLOSED'
    STATUS_CHOICES = (
        (OPEN, _('Open')),
        (CLOSED, _('Closed')),
    )
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    created = models.DateTimeField(auto_now_add=True, db_index=True)
    modified = models.DateTimeField(auto_now=True)
    owner = models.OneToOneField(settings.AUTH_USER_MODEL,
                                 related_name='billing_account',
                                 on_delete=PROTECT)
    currency = CurrencyField(db_index=True)
    status = FSMField(max_length=20,
                      choices=STATUS_CHOICES,
                      default=OPEN,
                      db_index=True)
    # An account is marked as delinquent when it is registered then when the user
    # registers a valid credit card it will be marked as compliant
    delinquent = models.BooleanField(default=True, db_index=True)

    objects = AccountQuerySet.as_manager()

    def __str__(self):
        return str(self.owner)

    @transition(field=status, source=OPEN, target=CLOSED)
    def close(self):
        pass

    @transition(field=status, source=CLOSED, target=OPEN)
    def reopen(self):
        pass

    def balance(self, as_of: date = None):
        charges = Charge.objects.filter(account=self)
        transactions = Transaction.successful.filter(account=self)
        if as_of is not None:
            charges = charges.filter(created__lte=as_of)
            transactions = transactions.filter(created__lte=as_of)
        return total_amount(transactions) - total_amount(charges)

    def is_solvent(
        self,
        currency_threshold_price_map: Dict[str, Decimal],
        account_valid_cc_map: Dict[UUID, bool] = None,
        account_balance_map: DefaultDict[UUID, DefaultDict[str,
                                                           Decimal]] = None):
        """
        Given a map of currency thresholds determines if the account is solvent

        An account is solvent when:
            * Has a valid and active credit card to pay OR
            * Has enough balance to pay

        In currency_threshold_price_map param you have to specify the amount threshold
        for each currency. If the account has enough of one of the currencies then
        is solvent. Ex:
        currency_threshold_price_map {
            'CHF': Decimal(10.83),
            'EUR': Decimal(10.),
            'NOK': Decimal(103.97)
        }

        Note: account_valid_cc_map and account_balance_map can be passed from outside
              in order to improve the efficiency when we require to know if several
              accounts are solvent
        """
        if not account_valid_cc_map:
            from .actions.accounts import get_account_valid_credit_card_map
            account_valid_cc_map = get_account_valid_credit_card_map(
                Account.objects.filter(id=self.id))

        if not account_balance_map:
            from .actions.accounts import get_account_balance_map
            accounts = Account.objects.filter(id=self.id)
            account_balance_map = get_account_balance_map(accounts)

        return (account_valid_cc_map[self.id] or self.has_enough_balance(
            account_balance_map, currency_threshold_price_map))

    def has_enough_balance(
            self, account_balance_map: DefaultDict[UUID, DefaultDict[str,
                                                                     Decimal]],
            currency_threshold_price_map: Dict[str, Decimal]) -> bool:
        for currency, balance in account_balance_map[self.id].items():
            if balance >= currency_threshold_price_map[currency]:
                return True
        return False

    def has_usable_credit_cards(self) -> bool:
        credit_cards = CreditCard.objects.filter(
            account=self, status=CreditCard.ACTIVE).valid()
        return credit_cards.exists()