Beispiel #1
0
class Discount(models.Model):
    """Discount in % or abs.val with possibly many months duration."""

    name = models.CharField(max_length=120)
    percent = models.SmallIntegerField(default=100,
                                       help_text=lazy_("Discount in %"))
    usages = models.SmallIntegerField(default=1,
                                      help_text=lazy_('Usages in months'))
    vendor = models.ForeignKey('market.Vendor',
                               related_name='tariff_discounts',
                               null=True)

    objects = DiscountManager()

    class Meta:
        """Explicitely mark the app_label."""
        app_label = "market"
        verbose_name = _("Discount")
        verbose_name_plural = _("Discounts")

    def __str__(self):
        return "{} {:d}% {} {:d} {}.".format(_("Discount"), self.percent,
                                             _("for next"), self.usages,
                                             _("paid months"))

    def use(self, total):
        """Use once this discount on `total`."""
        self.usages -= 1
        self.save()
        discount = Decimal("-0.01") * self.percent * total
        return (self.name, discount.to_integral_value(), app_settings.TAX)
Beispiel #2
0
class EmailUserAuthenticationForm(AuthenticationForm):

    username = forms.CharField(
        label=lazy_("Email/Username"),
        widget=forms.TextInput(
            attrs={
                'autofocus': True,
                'autocomplete': 'email',
                'autocapitalize': 'none',
                'placeholder': 'Email/Username'
            }),
    )

    password = forms.CharField(
        label=lazy_("Password"),
        strip=False,
        widget=forms.PasswordInput(attrs={
            'autocomplete': 'new-password',
            'placeholder': 'Password'
        }),
    )

    error_messages = {
        'invalid_login':
        lazy_("Please enter a correct %(username)s and password."),
        'inactive': lazy_("This account is inactive."),
    }

    def clean(self):
        username = self.cleaned_data.get('username')
        password = self.cleaned_data.get('password')

        if username is not None and password:
            self.user_cache = authenticate(self.request,
                                           username=username,
                                           password=password)
            if self.user_cache is None:
                try:
                    email_user = EmailUser.objects.get(username=username)
                    self.user_cache = authenticate(self.request,
                                                   username=email_user.email,
                                                   password=password)
                except EmailUser.DoesNotExist:
                    raise self.get_invalid_login_error()

            if self.user_cache is None:
                raise self.get_invalid_login_error()
            else:
                self.confirm_login_allowed(self.user_cache)

        return self.cleaned_data

    class Meta:
        model = EmailUser
Beispiel #3
0
 def create_user(self, email, password, username, **extra_fields):
     """
     Create and save a User with the given email and password.
     """
     if not email:
         raise ValueError(lazy_('The Email must be set.'))
     if not username:
         raise ValueError(lazy_('The Username must be set.'))
     email = self.normalize_email(email)
     user = self.model(email=email, username=username, **extra_fields)
     user.set_password(password)
     user.save()
     return user
Beispiel #4
0
    def create_superuser(self, email, password, username, **extra_fields):
        """
        Create and save a SuperUser with the given email and password.
        """
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)
        extra_fields.setdefault('is_active', True)

        if extra_fields.get('is_staff') is not True:
            raise ValueError(lazy_('Superuser must have is_staff=True.'))
        if extra_fields.get('is_superuser') is not True:
            raise ValueError(lazy_('Superuser must have is_superuser=True.'))
        return self.create_user(email, password, username, **extra_fields)
Beispiel #5
0
class EmailUserSetPasswordForm(SetPasswordForm):
    """
    A form that lets an email user change set their password without entering the old
    password, but does not require password confirmation.
    """
    new_password1 = forms.CharField(
        label=lazy_("Password"),
        strip=False,
        widget=forms.PasswordInput(attrs={
            'autocomplete': 'new-password',
            'placeholder': 'New Password'
        }),
        help_text=password_validation.password_validators_help_text_html(),
    )

    new_password2 = None

    def clean_password1(self):
        password = self.cleaned_data.get('password1')
        if password:
            password_validation.validate_password(password, self.user)
        return password

    class Meta:
        model = EmailUser
Beispiel #6
0
class EmailUserPasswordChangeForm(EmailUserSetPasswordForm):
    """
    A form that lets an email user change their password by entering their old
    password, but does not require password confirmation.
    """

    error_messages = {
        **SetPasswordForm.error_messages,
        'password_incorrect':
        lazy_(
            "Your old password was entered incorrectly. Please enter it again."
        ),
    }

    old_password = forms.CharField(
        label=lazy_("Old password"),
        strip=False,
        widget=forms.PasswordInput(
            attrs={
                'autocomplete': 'current-password',
                'placeholder': 'Old Password',
                'autofocus': True,
            }),
    )

    field_order = [
        'old_password',
        'new_password1',
    ]

    def clean_old_password(self):
        """
        Validate that the old_password field is correct.
        """
        old_password = self.cleaned_data["old_password"]
        if not self.user.check_password(old_password):
            raise forms.ValidationError(
                self.error_messages['password_incorrect'],
                code='password_incorrect',
            )
        return old_password
Beispiel #7
0
class EmailUser(AbstractUser):
    email = models.EmailField(
        lazy_('email address'),
        unique=True,
        help_text=lazy_('Required. Will be used for verification and password reset.'),
        error_messages={
            'unique': lazy_("A user with that email already exists."),
        }
    )

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

    objects = EmailUserManager()

    def save(self, *args, **kwargs):
        """
        Overriding save to prevent case insensitive duplication by
        calling the case insensitive filter method from the Manager.
        """
        if not self.pk:
            if self.__class__.objects.filter(username=self.username).exists():
                raise ValidationError("A user with that username already exists.")
            if self.__class__.objects.filter(email=self.email).exists():
                raise ValidationError("A user with that email already exists.")
        super().save(*args, **kwargs)

    def has_profile(self):
        """
        Determines if user has profile by counting
        if they have at least one SurveyTopicType
        """
        return self.survey_topic_types.count() > 0

    def __repr__(self):
        return "User(email={}, username={}, is_active={}, is_superuser={})".format(
            self.email, self.username, self.is_active, self.is_superuser
        )

    def __str__(self):
        return self.__repr__()
Beispiel #8
0
class EmailUserPasswordResetForm(PasswordResetForm):
    email = forms.EmailField(
        label=lazy_("Email"),
        max_length=254,
        widget=forms.EmailInput(
            attrs={
                'autofocus': True,
                'autocomplete': 'email',
                'autocapitalize': 'none',
                'placeholder': 'Email'
            }),
    )
Beispiel #9
0
class EmailUserCreationForm(UserCreationForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['email'].widget.attrs.update({'autofocus': False})

    password1 = forms.CharField(
        label=lazy_("Password"),
        strip=False,
        widget=forms.PasswordInput(attrs={
            'autocomplete': 'new-password',
            'placeholder': 'Password'
        }),
        help_text=password_validation.password_validators_help_text_html(),
    )

    password2 = None

    class Meta(UserCreationForm.Meta):
        model = EmailUser
        fields = (
            'email',
            'username',
        )
        widgets = {
            'email':
            forms.EmailInput(
                attrs={
                    'autocomplete': 'email',
                    'autocapitalize': 'none',
                    'placeholder': 'Email'
                }),
            'username':
            forms.TextInput(
                attrs={
                    'autocomplete': 'username',
                    'autocapitalize': 'none',
                    'placeholder': 'Username'
                }),
        }

    def clean_password1(self):
        password = self.cleaned_data.get('password1')
        if password:
            try:
                password_validation.validate_password(password, self.instance)
            except forms.ValidationError as error:
                self.add_error('password1', error)
        return password
Beispiel #10
0
 class Meta:
     app_label = "invoice"
     verbose_name = lazy_("invoice item")
     ordering = ['unit_price']
Beispiel #11
0
 class Meta:
     app_label = "invoice"
     verbose_name = lazy_('invoice')
     ordering = ('-date_issuance', 'id')
Beispiel #12
0
 class Meta:
     """State explicitely app_label."""
     app_label = "invoice"
     verbose_name = lazy_("Bank account")
     verbose_name_plural = lazy_("Bank accounts")
Beispiel #13
0
 class Meta:
     """State explicitely app_label."""
     app_label = "invoice"
     verbose_name = lazy_("Address")
     verbose_name_plural = lazy_("Addresses")
Beispiel #14
0
 class Meta:
     """State explicitely app_label and default ordering."""
     app_label = "invoice"
     verbose_name = lazy_('Invoice')
     verbose_name_plural = lazy_('Invoices')
     ordering = ('-date_issued', 'id')
Beispiel #15
0
class Billing(models.Model):
    """One billing per vendor. It keeps track of their setting of billing app.

    This model stores which vendor has chosen which billing period and when the last billing was
    performed. The :method:`bill` is periodically called by an external tool.
    """

    PERIODS = (
        (1, lazy_('monthly')),
        (3, lazy_('quartely')),
        (6, lazy_('half-yearly')),
        (12, lazy_('yearly')),
    )
    DEFAULT_PERIOD = 3

    vendor = models.OneToOneField('market.Vendor')

    period = models.PositiveIntegerField(default=DEFAULT_PERIOD,
                                         choices=PERIODS)
    next_period = models.PositiveIntegerField(lazy_("Next billing period"),
                                              default=DEFAULT_PERIOD,
                                              choices=PERIODS)
    last_billed = models.DateField(default=timezone.now)
    next_billing = models.DateField(blank=True,
                                    help_text=lazy_('Is set automatically'))
    active = models.BooleanField(default=True, db_index=True)

    class Meta:
        """Meta class to force ordering by date."""
        app_label = "market"
        verbose_name = _("Billing")
        verbose_name_plural = _("Billings")

    def __str__(self):
        return _("{0.name} next billing {1:%d.%m.%Y}").format(
            self.vendor, self.next_billing)

    def save(self, *args, **kwargs):
        """Sync times on save."""
        if self.next_period != self.period:
            if timezone.now().date() in (self.last_billed, self.next_billing):
                self.period = self.next_period
                self.next_billing = self.last_billed + monthdelta(self.period)

        self.next_billing = self.last_billed + monthdelta(self.period)
        return super(Billing, self).save(*args, **kwargs)

    @cached_property
    def tariff(self):
        """Current tariff based of statistics of a vendor."""
        return Statistics.objects.current(self.vendor).tariff

    def close(self):
        """Close the vendor and issue last billing."""
        self.active = False
        self.next_billing = timezone.now().date()
        return self.bill()

    @transaction.atomic
    def bill(self):
        """
        Issue a bill for a vendor.

        This method is called periodically by an external tool. It let all duties bill up and the
        executes all price modifiers such as discount encounters.

        We have to bill the vendor at the time of call. Only when the billing happened already
        today (or in the future) we will ignore that call.

        :rvalue:`tariff.Bill` the created bill (invoice)
        """
        if self.last_billed >= timezone.now().date():
            return Bill.objects.filter(period_end=timezone.now().date()).get()

        if self.next_billing > timezone.now().date():
            raise ValueError(
                "Can not bill before the end of billing period - close it instead."
            )

        contractor = defaults.vendor()
        bill = Bill.objects.create(
            vendor=self.vendor,
            logo=contractor.logo.path if contractor.logo else None,
            subscriber=self.vendor.address,
            period_start=self.last_billed,
            period_end=self.next_billing,
            contractor=contractor.address,
            contractor_bank=contractor.bank_account)

        months, rest = monthmod(self.last_billed, self.next_billing)
        # tranform date to datetime for billing useng StatisticsManager
        last_billed = datetime(self.last_billed.year, self.last_billed.month,
                               self.last_billed.day)
        # bill by months (because of Discounts and better visibility on the bill)
        for month in range(months.months):
            total = Decimal("0.00")
            for tariff, price in Statistics.objects.bill(
                    self.vendor, last_billed + monthdelta(month),
                    last_billed + monthdelta(month + 1)):
                bill.add_item(tariff, price, settings.TAX)
                total += price

            discount = Discount.objects.cut_the_price(self.vendor, total)
            if discount is not None:
                bill.add_item(*discount)

        # bill the remaining time (if there is some)
        if rest.days > 1:
            total = Decimal("0.00")
            for tariff, price in Statistics.objects.bill(
                    self.vendor, last_billed + months,
                    last_billed + months + rest):
                bill.add_item(tariff, price, settings.TAX)
                total += price

            discount = Discount.objects.cut_the_price(self.vendor, total)
            if discount is not None:
                bill.add_item(*discount)

        if bill.total < 0:
            bill.add_item(_("Rounding price to zero"), -1 * bill.total)

        self.last_billed = self.next_billing

        self.save()  # periods change is taken care of in .save() method
        bill.save()
        bill.send()
        return bill
Beispiel #16
0
 class Meta:
     app_label = "invoice"
     verbose_name = lazy_("Bank account")
     verbose_name_plural = lazy_("Bank accounts")
Beispiel #17
0
 class Meta:
     app_label = "invoice"
     verbose_name_plural = lazy_("Addresses")
Beispiel #18
0
 class Meta:
     app_label = lazy_("invoice")
     verbose_name = lazy_("bank account")
Beispiel #19
0
 class Meta:
     app_label = "invoice"
     verbose_name = lazy_('Invoice')
     verbose_name_plural = lazy_('Invoices')
     ordering = ('-date_issued', 'id')