class Goal(models.Model):
    name = models.CharField(max_length=500)
    balance = MoneyField(max_digits=14,
                         decimal_places=2,
                         default=Money("0", "PHP"),
                         validators=[MinMoneyValidator(0)])
    target_amount = MoneyField(max_digits=14,
                               decimal_places=2,
                               default_currency='PHP',
                               validators=[MinMoneyValidator(1)])
    owner = models.ForeignKey(User, on_delete=models.CASCADE)
    target_date = models.DateField()

    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)
    opened = models.DateTimeField(null=True)

    @property
    def saving_progress(self):
        progress = Decimal(self.balance / self.target_amount)
        progress = round(progress * 100, 2)
        return progress if progress < 100 else 100

    @property
    def date_progress(self):
        date_interval = self.target_date - self.created.date()
        days_passed = datetime.now().date() - self.created.date()
        progress = Decimal(days_passed / date_interval)
        progress = round(progress * 100, 2)
        return progress if progress < 100 else 100
Beispiel #2
0
class Transaction(models.Model):
    balance = models.ForeignKey(Balance, on_delete=models.CASCADE)
    amount = MoneyField(max_digits=14,
                        decimal_places=2,
                        default_currency='USD',
                        validators=[
                            MinMoneyValidator(0),
                            MaxMoneyValidator(100000),
                            MinMoneyValidator({'USD': 0}),
                            MaxMoneyValidator({'USD': 100000}),
                        ])
    details = models.CharField(max_length=50)
    date_time = models.DateTimeField(auto_now=True)
Beispiel #3
0
class ValidatedMoneyModel(models.Model):
    money = MoneyField(
        max_digits=10,
        decimal_places=2,
        validators=[
            MinMoneyValidator({"EUR": 100, "USD": 50}),
            MaxMoneyValidator({"EUR": 1000, "USD": 500}),
            MinMoneyValidator(Money(500, "NOK")),
            MaxMoneyValidator(Money(900, "NOK")),
            MinMoneyValidator(10),
            MaxMoneyValidator(1500),
        ],
    )
class Wallet(models.Model):
    TYPE_CHOICES = [
        ('CH', 'Cash'),
        ('EM', 'E-Money'),
        ('CC', 'Credit Card'),
        ('DC', 'Debit Card'),
    ]
    name = models.CharField(max_length=500, null=False, blank=False)
    type = models.CharField(max_length=500,
                            null=False,
                            blank=False,
                            choices=TYPE_CHOICES)
    owner = models.ForeignKey(User, on_delete=models.CASCADE)
    balance = MoneyField(max_digits=14,
                         decimal_places=2,
                         default=Money("0", "PHP"),
                         validators=[MinMoneyValidator(0)])

    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)
    opened = models.DateTimeField(null=True)

    class Meta:
        unique_together = ('name', 'type', 'owner')

    def __str__(self):
        return '{}({})'.format(self.name, self.pk)
Beispiel #5
0
    def __init__(self, **kwargs):
        """Overwrite default values and validators."""
        # detect if creating migration
        if 'migrate' in sys.argv or 'makemigrations' in sys.argv:
            # remove currency information for a clean migration
            kwargs['default_currency'] = ''
            kwargs['currency_choices'] = []
        else:
            # set defaults
            kwargs.update(money_kwargs())

        # Set a minimum value validator
        validators = kwargs.get('validators', [])

        allow_negative = kwargs.pop('allow_negative', False)

        # If no validators are provided, add some "standard" ones
        if len(validators) == 0:

            if not allow_negative:
                validators.append(MinMoneyValidator(0), )

        kwargs['validators'] = validators

        super().__init__(**kwargs)
Beispiel #6
0
class Service(models.Model):
    Title = models.CharField(max_length=30)
    type = models.CharField(max_length=60, choices=Type_Choices, default='None', blank=True)
    description = models.TextField()
    rate_per_hour = MoneyField(max_digits=19, decimal_places=4, default_currency='KES',
                               validators=[MinMoneyValidator(1)])
    user = models.ForeignKey(User)
Beispiel #7
0
class Loan(models.Model):
    amount = MoneyField(max_digits=19,
                        decimal_places=4,
                        default_currency='CNY',
                        validators=[
                            MinMoneyValidator(0),
                        ])
    branch = models.ForeignKey(Branch,
                               related_name='loans',
                               on_delete=models.CASCADE)
    customers = models.ManyToManyField(Customer, related_name='loans')

    @property
    def status(self):
        released = self.released
        if released == 0:
            return 'empty'
        elif released >= self.amount:
            return 'full'
        else:
            return 'half'

    @property
    def released(self):
        return sum(payment.amount for payment in self.payments.all())

    @property
    def remained(self):
        released = self.released
        if released == 0:
            return self.amount
        else:
            return self.amount - released
Beispiel #8
0
class PositiveValidatedMoneyModel(models.Model):
    """Validated model with a field requiring a non-negative value."""
    money = MoneyField(max_digits=10,
                       decimal_places=2,
                       validators=[
                           MinMoneyValidator(0),
                       ])
class Price(CachedModel, CommonInfo, TimeStampedModel):
    """
    Price class
    """
    objects = PriceManager()

    price = MoneyField(max_digits=20,
                       decimal_places=2,
                       verbose_name=_('price'),
                       validators=[MinMoneyValidator(0)],
                       db_index=True)
    country = models.ForeignKey(Country,
                                on_delete=models.PROTECT,
                                verbose_name=_('country'),
                                null=True,
                                blank=True,
                                db_index=True)
    is_enabled = models.BooleanField(default=True,
                                     db_index=True,
                                     verbose_name=_('is enabled'))
    service = models.ForeignKey(Service,
                                on_delete=models.CASCADE,
                                verbose_name=_('service'),
                                db_index=True,
                                related_name='prices')
    period_from = models.PositiveIntegerField(
        db_index=True,
        null=True,
        blank=True,
        verbose_name=_('from'),
    )
    period_to = models.PositiveIntegerField(
        db_index=True,
        null=True,
        blank=True,
        verbose_name=_('to'),
    )
    for_unit = models.BooleanField(default=True,
                                   db_index=True,
                                   verbose_name=_('for unit'))

    def __str__(self):
        return '{}: {} {}-{} {}'.format(
            self.country if self.country else 'Base',
            self.price,
            self.period_from or '∞',
            self.period_to or '∞',
            'per unit' if self.for_unit else 'per period',
        )

    def clean(self):
        """
        Price validation
        """
        validate_price_periods(self)

    class Meta:
        ordering = ['country', 'period_from', 'period_to', 'price']
        unique_together = ('service', 'country', 'period_from', 'period_to')
Beispiel #10
0
class ValidatedMoneyModel(models.Model):
    money = MoneyField(max_digits=10,
                       decimal_places=2,
                       validators=[
                           MinMoneyValidator({
                               'EUR': 100,
                               'USD': 50
                           }),
                           MaxMoneyValidator({
                               'EUR': 1000,
                               'USD': 500
                           }),
                           MinMoneyValidator(Money(500, 'NOK')),
                           MaxMoneyValidator(Money(900, 'NOK')),
                           MinMoneyValidator(10),
                           MaxMoneyValidator(1500),
                       ])
Beispiel #11
0
 def __init__(self, *args, **kwargs):
     self.default_currency = kwargs.pop("default_currency", None)
     super(MoneyField, self).__init__(*args, **kwargs)
     # Rest Framework converts `min_value` / `max_value` to validators, that are not aware about `Money` class
     # We need to adjust them
     for idx, validator in enumerate(self.validators):
         if isinstance(validator, MinValueValidator):
             self.validators[idx] = MinMoneyValidator(self.min_value)
         elif isinstance(validator, MaxValueValidator):
             self.validators[idx] = MaxMoneyValidator(self.max_value)
Beispiel #12
0
class LoanPayment(models.Model):
    amount = MoneyField(max_digits=19,
                        decimal_places=4,
                        default_currency='CNY',
                        validators=[
                            MinMoneyValidator(0),
                        ])
    date = models.DateField(default=datetime.date.today, blank=True)
    loan = models.ForeignKey(Loan,
                             related_name='payments',
                             on_delete=models.CASCADE)
class Advertisement(models.Model):
    """Custom model for advertisements

    Attributes:
        user (AppUser): a user to which an ad belongs
        title (str): a title of an ad
        description (str): description for an ad
        photo (image): 1 photo for an ad
        address (str): address where to meet
        created_date (datetime): when an ad was added
        modified_date (datetime): the last time an ad was modified
        price (money): how much an ad's product costs
        tags(TaggableManager): M2M with Tag model from taggit lib

    Methods:
        get_absolute_url: The method that returns
        a unique link to an every advert instance

    """

    user = models.ForeignKey(AppUser,
                             on_delete=models.CASCADE,
                             null=True,
                             verbose_name=_('User'))
    title = models.CharField(max_length=100, verbose_name=_('Title'))
    description = models.TextField(max_length=3000,
                                   verbose_name=_('Description'))
    address = models.CharField(max_length=100,
                               null=True,
                               verbose_name=_('Address'))
    created_date = models.DateTimeField(auto_now_add=True,
                                        editable=False,
                                        verbose_name=_('Date of creation'))
    modified_date = models.DateTimeField(
        auto_now=True, verbose_name=_('Date of modification'))
    price = MoneyField(max_digits=14,
                       decimal_places=2,
                       default_currency='RUB',
                       verbose_name=_('Price'),
                       validators=[MinMoneyValidator(0)])
    favourite = models.ManyToManyField(AppUser, related_name='user_favourites')

    published = models.BooleanField(default=True, verbose_name=_('Published'))
    tags = TaggableManager(blank=True)

    def __str__(self):
        return f'{self.title} ({self.user})'

    def get_absolute_url(self):
        return reverse('info', args=[self.id])

    class Meta:
        verbose_name = _('Ad')
        verbose_name_plural = _('Ads')
Beispiel #14
0
class UserProfile(models.Model):
    user = models.OneToOneField(User, related_name='user', on_delete=models.PROTECT)
    photo = models.ImageField(upload_to='profile_image', blank=True)
    website = models.URLField(default='', blank=True)
    bio = models.TextField(default='', blank=True)
    phone = models.CharField(max_length=20, blank=True, default='')
    city = models.CharField(max_length=100, default='', blank=True)
    country = models.CharField(max_length=100, default='', blank=True)
    organization = models.CharField(max_length=100, default='', blank=True)
    Credit = MoneyField(
        max_digits=10,
        decimal_places=2,
        validators=[
            MinMoneyValidator(0),
            MaxMoneyValidator(1500),
            MinMoneyValidator(Money(0, 'NOK')),
            MaxMoneyValidator(Money(900, 'NOK')),
            MinMoneyValidator({'EUR': 100, 'USD': 0}),
            MaxMoneyValidator({'EUR': 1000, 'USD': 500}),
        ])

    def __str__(self):
        return str(self.user)
class PettycashReimbursementRequest(models.Model):
    dst = models.ForeignKey(
        User,
        help_text="Person to reemburse (usually you, yourself)",
        on_delete=models.CASCADE,
        related_name="isReimbursedTo",
    )

    date = models.DateField(help_text="Date of expense", default=timezone.now)

    submitted = models.DateTimeField(
        help_text="Date the request was submitted",
        default=timezone.now,
    )

    amount = MoneyField(
        max_digits=8,
        decimal_places=2,
        default_currency="EUR",
        validators=[
            MinMoneyValidator(0),
            MaxMoneyValidator(settings.MAX_PAY_REIMBURSE.amount),
        ],
        help_text=
        "This system will only accept reimbursement up to %s. Above that; contact the trustees directly (%s)"
        % (settings.MAX_PAY_REIMBURSE.amount, settings.TRUSTEES),
    )

    viaTheBank = models.BooleanField(
        default=False,
        help_text=
        "Check this box if you want to be paid via a IBAN/SEPA transfer; otherwise the amount will be credited to your Makerspace petty cash acount",
    )

    description = models.CharField(
        max_length=300,
        help_text="Description / omschrijving van waarvoor deze betaling is",
    )

    scan = StdImageField(
        upload_to=upload_to_pattern,
        delete_orphans=True,
        variations=settings.IMG_VARIATIONS,
        validators=settings.IMG_VALIDATORS,
        blank=True,
        null=True,
        help_text="Scan, photo or similar of the receipt",
    )
    history = HistoricalRecords()
Beispiel #16
0
class Expense(models.Model):
    """Expense to be reimbursed."""

    # TODO add attachments

    document_number = models.CharField('Document number',
                                       max_length=50,
                                       blank=True)
    requisition_number = models.IntegerField('Requisition number',
                                             blank=True,
                                             null=True)
    description = models.TextField("Description")
    value = MoneyField(
        "Amount",
        max_digits=11,
        decimal_places=2,
        default_currency="EUR",
        validators=[MinMoneyValidator(Money(0.01))],
    )
    eur_value = models.DecimalField('Amount in EUR',
                                    max_digits=11,
                                    decimal_places=2)

    is_social = models.BooleanField('It is a social expense', default=False)
    receipt = models.FileField('Receipt', upload_to=user_directory_path)
    receipt_date = models.DateField('Receipt date', null=True, default=None)
    reimbursement = models.ForeignKey(to="Reimbursement",
                                      on_delete=models.CASCADE,
                                      related_name="expenses")
    expensecode = models.ForeignKey(to='finance.ExpenseCode',
                                    on_delete=models.CASCADE,
                                    related_name="reimbursement_expenses",
                                    verbose_name='Expense code')

    class Meta:
        ordering = ('document_number', )
        verbose_name = 'Reimbursement expense'
        verbose_name_plural = 'Reimbursements expenses'

    def clean(self):

        if self.description is not None:
            # restrict number of lines
            nlines = self.description.count("\n")
            if nlines > 8 or len(self.description) > 600:
                raise ValidationError({"description": "Too much text."})

    def short_description(self):
        return textwrap.shorten(self.description, width=100, placeholder="...")
Beispiel #17
0
class AbstractPrice(SafeDeleteModel):
    """
    An abstract class that represents a price that was created at a given
    datetime.
    """

    class Meta:
        abstract = True

    price = MoneyField(
        decimal_places=2,
        max_digits=10,
        validators=[MinMoneyValidator(0)]
    )
    created_at = models.DateTimeField(auto_now_add=True)
Beispiel #18
0
class Dish(Model):
    name = CharField(max_length=64)

    restaurant = ForeignKey(Restaurant, on_delete=CASCADE)
    cost = MoneyField(
        max_digits=6,
        decimal_places=3,
        validators=[MinMoneyValidator(0)]
    )

    calories = IntegerField(
        validators=[MinValueValidator(0)]
    )
    grams = IntegerField(
        validators=[MinValueValidator(0)]
    )

    def __str__(self):
        return self.name
Beispiel #19
0
    def __init__(self, **kwargs):
        # detect if creating migration
        if 'migrate' in sys.argv or 'makemigrations' in sys.argv:
            # remove currency information for a clean migration
            kwargs['default_currency'] = ''
            kwargs['currency_choices'] = []
        else:
            # set defaults
            kwargs.update(money_kwargs())

        # Set a minimum value validator
        validators = kwargs.get('validators', [])

        if len(validators) == 0:
            validators.append(MinMoneyValidator(0), )

        kwargs['validators'] = validators

        super().__init__(**kwargs)
Beispiel #20
0
class Transaction(models.Model):
    STATUS = (('SUCCESS', 'Success'), ('FAILED', 'Failed'), ('PENDING',
                                                             'Pending'))
    SERVICES = (
        ('P2P', 'Pochi to Pochi'),
        ('DEPOSIT', 'Deposit'),
        ('WITHDRAW', 'Withdraw'),
        ('BONUS', 'Bonus'),
        ('FEES', 'Fees'),
    )
    full_timestamp = models.DateTimeField(auto_now_add=True)
    profile_id = models.CharField(max_length=10, db_index=True)
    account = models.CharField(max_length=13, db_index=True)
    msisdn = models.CharField(max_length=10, db_index=True, default='NA')
    trans_id = models.CharField(max_length=25, default='NA')
    service = models.CharField(max_length=8, db_index=True, choices=SERVICES)
    channel = models.CharField(max_length=25, db_index=True, default='NA')
    mode = models.CharField(max_length=6,
                            db_index=True,
                            choices=MODES,
                            default='POCHI')
    dst_account = models.CharField(max_length=25, db_index=True, default='NA')
    amount = MoneyField(max_digits=10,
                        decimal_places=2,
                        default_currency='TZS',
                        validators=[
                            MinMoneyValidator({
                                'TZS': 1000,
                                'USD': 50
                            }),
                            MaxMoneyValidator({
                                'TZS': 1000000000,
                                'USD': 1000000
                            }),
                        ])
    charge = MoneyField(max_digits=10,
                        decimal_places=2,
                        default_currency='TZS')
    reference = models.CharField(max_length=15, db_index=True, default='NA')
    status = models.CharField(max_length=7, choices=STATUS, default='PENDING')
    result_code = models.CharField(max_length=3, default='111', db_index=True)
    message = models.TextField(max_length=1024, default='NA')
    processed_timestamp = models.DateTimeField(null=True)
Beispiel #21
0
class RateClass(models.Model):
    name = models.CharField(verbose_name=_("name"),
                            max_length=150,
                            unique=True)
    description = models.CharField(verbose_name=_("description"),
                                   max_length=250,
                                   blank=True)
    rate = MoneyField(
        verbose_name=_("rate"),
        decimal_places=2,
        max_digits=6,
        default_currency="EUR",
        validators=[MinMoneyValidator(0)],
    )

    class Meta:
        verbose_name = _("rate class")
        verbose_name_plural = _("rate classes")

    def __str__(self):
        return self.name
Beispiel #22
0
class Product(models.Model):
    """
    Container model for a product
    which stores information common to all of its variations.
    """
    name = models.CharField(max_length=255)
    slug = models.SlugField(blank=True, unique=True)
    description = models.TextField(blank=True, null=True)

    product_template = models.ForeignKey(ProductTemplate,
                                         related_name="products",
                                         on_delete=models.CASCADE)
    min_price = MoneyField(
        max_digits=19,
        decimal_places=4,
        default=0,
        default_currency=settings.DEFAULT_CURRENCY,
        currency_choices=settings.CURRENCY_CHOICES,
        currency_max_length=settings.DEFAULT_CURRENCY_CODE_LENGTH,
        validators=[
            MinMoneyValidator(0),
        ])
    active = models.BooleanField(default=False)
    created = models.DateTimeField(editable=False, default=timezone.now)
    modified = models.DateTimeField(default=timezone.now)

    objects = ProductManager()

    def save(self, *args, **kwargs):
        """
        Update timestamps.
        """
        if not self.id:
            self.created = timezone.now()
        self.modified = timezone.now()
        return super().save(*args, **kwargs)

    def __str__(self):
        return self.name
Beispiel #23
0
class CashOut(models.Model):
    STATUS = (('SUCCESS', 'Success'), ('FAILED', 'Failed'), ('PENDING',
                                                             'Pending'))
    ext_entity = models.CharField(max_length=100)
    ext_acc_no = models.CharField(max_length=30, default='NA')
    amount = MoneyField(max_digits=10,
                        decimal_places=2,
                        default_currency='TZS',
                        validators=[
                            MinMoneyValidator({
                                'TZS': 1000,
                                'USD': 50
                            }),
                            MaxMoneyValidator({
                                'TZS': 1000000000,
                                'USD': 1000000
                            }),
                        ])
    status = models.CharField(max_length=8, choices=STATUS, default='PENDING')
    result = models.CharField(max_length=3, default='111', db_index=True)
    message = models.TextField(max_length=1024, default='NA')
    reference = models.CharField(max_length=15, db_index=True, default='NA')
    ext_trans_id = models.CharField(max_length=25, default='NA')
Beispiel #24
0
class Ledger(models.Model):
    TYPES = (('DEBIT', 'Debit'), ('CREDIT', 'Credit'))
    full_timestamp = models.DateTimeField(auto_now_add=True)
    profile_id = models.CharField(max_length=10, db_index=True)
    account = models.CharField(max_length=13, db_index=True)
    trans_type = models.CharField(max_length=10, choices=TYPES)
    mode = models.CharField(max_length=6,
                            db_index=True,
                            choices=MODES,
                            default='POCHI')
    amount = MoneyField(max_digits=10,
                        decimal_places=2,
                        default_currency='TZS',
                        validators=[
                            MinMoneyValidator({
                                'TZS': 1000,
                                'USD': 50
                            }),
                            MaxMoneyValidator({
                                'TZS': 1000000000,
                                'USD': 1000000
                            }),
                        ])
    trans_id = models.CharField(max_length=25, default='NA')
    reference = models.CharField(max_length=15, db_index=True, default='NA')
    available_o_bal = MoneyField(max_digits=10,
                                 decimal_places=2,
                                 default_currency='TZS')
    available_c_bal = MoneyField(max_digits=10,
                                 decimal_places=2,
                                 default_currency='TZS')
    current_o_bal = MoneyField(max_digits=10,
                               decimal_places=2,
                               default_currency='TZS')
    current_c_bal = MoneyField(max_digits=10,
                               decimal_places=2,
                               default_currency='TZS')
class Subscription(CommonInfo, TimeStampedModel):
    """
    The class represents client subscriptions
    """
    objects = SubscriptionManager()

    STATUSES = (
        ('enabled', _('enabled')),
        ('canceled', _('canceled')),
    )
    client = models.ForeignKey(Client,
                               on_delete=models.PROTECT,
                               db_index=True,
                               verbose_name=_('client'),
                               related_name='subscriptions')
    order = models.ForeignKey(Order,
                              on_delete=models.SET_NULL,
                              verbose_name=_('order'),
                              null=True,
                              blank=True,
                              db_index=True,
                              related_name='subscriptions')
    country = models.ForeignKey(Country,
                                on_delete=models.PROTECT,
                                verbose_name=_('country'),
                                db_index=True,
                                related_name='subscriptions')
    status = models.CharField(max_length=20,
                              default='enabled',
                              choices=STATUSES,
                              verbose_name=_('status'),
                              db_index=True)
    price = MoneyField(max_digits=20,
                       decimal_places=2,
                       blank=True,
                       verbose_name=_('price'),
                       validators=[MinMoneyValidator(0)],
                       db_index=True)
    period = models.PositiveIntegerField(
        verbose_name=_('period'),
        db_index=True,
        help_text=_('the billing period in months'))
    merchant = models.CharField(
        max_length=255,
        null=True,
        blank=True,
        db_index=True,
        verbose_name=_('merchant'),
    )
    customer = models.CharField(
        max_length=255,
        db_index=True,
        verbose_name=_('customer'),
    )
    subscription = models.CharField(
        max_length=255,
        db_index=True,
        verbose_name=_('subscription'),
    )

    def cancel(self):
        """
        Cancel the subscription
        """
        braintree = BraintreeGateway(self.country, 'sandbox')
        result = braintree.cancel_subscription(self.subscription)
        if (result):
            self.status = 'canceled'
            self.save()

    def save(self, *args, **kwargs):
        if self.status == 'enabled':
            for subscription in Subscription.objects.get_active(
                    self.client, self.pk):
                subscription.cancel()
        super().save(*args, **kwargs)

    class Meta:
        ordering = ['-created']
Beispiel #26
0
class Good(TimeStampedModel):
    class Meta:
        verbose_name = pgettext_lazy('product', 'Good')
        verbose_name_plural = _('Goods')
        unique_together = ('name', 'category')

    AVAILABLE = 'available'
    NOT_AVAILABLE = 'not_available'
    ON_REQUEST = 'on_request'

    AVAILABILITY_CHOICES = (
        (AVAILABLE, _('Available')),
        (NOT_AVAILABLE, _('Not available')),
        (ON_REQUEST, _('On request')),
    )

    name = models.CharField(verbose_name=pgettext_lazy('not person', 'Name'),
                            max_length=30,
                            null=False,
                            blank=False)
    description = models.TextField(verbose_name=_('Description'),
                                   blank=True,
                                   default='')
    category = models.ForeignKey(GoodsCategory,
                                 related_name='goods',
                                 on_delete=models.PROTECT,
                                 verbose_name=_('Category'))
    seller = models.ForeignKey(Store,
                               related_name='goods',
                               on_delete=models.CASCADE,
                               verbose_name=_('Seller'))
    price = MoneyField(verbose_name=_('Price'),
                       max_digits=14,
                       decimal_places=2,
                       default_currency='UAH',
                       validators=[MinMoneyValidator(0)])
    discount = models.PositiveIntegerField(default=0,
                                           validators=[MaxValueValidator(99)],
                                           verbose_name=_('Discount'))
    availability = models.CharField(max_length=20,
                                    choices=AVAILABILITY_CHOICES,
                                    default=AVAILABLE,
                                    verbose_name=_('Availability'))

    def __str__(self):
        return f'{self.name} ({self.category})'

    def categories(self):
        return self.category.categories_chain()

    def categories_names(self):
        return [category.name for category in self.categories()]

    categories_names.short_description = _('Categories names')

    def categories_names_chain(self):
        return ' > '.join([str(category) for category in self.categories()])

    categories_names_chain.short_description = _('Categories names chain')

    @property
    def categories_ids(self):
        return [category.id for category in self.categories()]

    @property
    def specifications(self):
        try:
            return self._specifications
        except GoodSpecifications.DoesNotExist:
            return None

    @property
    def final_price(self):
        return round(self.price * (1 - self.discount / 100))

    @property
    def main_image_url(self):
        image = self.images.filter(
            is_main=True).first() or self.images.order_by('id').first()
        return image.image_url if image else ''
Beispiel #27
0
class Product(models.Model):
    class Meta:
        verbose_name = "producto"
        verbose_name_plural = "productos"

    name = models.CharField(max_length=100, verbose_name="nombre")
    short_description = models.CharField(max_length=255, verbose_name="descripcion corta")
    description = tinymce_models.HTMLField(verbose_name="descripcion larga")
    price = MoneyField(max_digits=12, decimal_places=2, validators=[MinMoneyValidator(Decimal("0.01"))],
                       verbose_name="precio")
    discount = models.IntegerField(default=0, validators=[MinValueValidator(0), MaxValueValidator(100)], verbose_name="descuento")

    weight = models.IntegerField(validators=[MinValueValidator(0)], verbose_name="peso")
    weight_unit = models.CharField(max_length=100, choices=WEIGHT_UNITS, verbose_name="unidad del peso")
    width = models.IntegerField(validators=[MinValueValidator(0)], verbose_name="ancho")
    width_unit = models.CharField(max_length=100, choices=LENGTH_UNITS, verbose_name="unidad del ancho")
    height = models.IntegerField(validators=[MinValueValidator(0)], verbose_name="alto")
    height_unit = models.CharField(max_length=100, choices=LENGTH_UNITS, verbose_name="unidad del alto")
    depth = models.IntegerField(validators=[MinValueValidator(0)], verbose_name="profundidad")
    depth_unit = models.CharField(max_length=100, choices=LENGTH_UNITS, verbose_name="unidad de la profundidad")

    objects = ProductQuerySet.as_manager()

    def __str__(self):
        return self.name

    def id_as_string(self):
        return str(self.id)

    def rating_mean(self):
        if not self.ratings.values():
            return 0
        else:
            values = []
            for it in self.ratings.values():
                values.append(it['value'])
            resultado = statistics.mean(values)
            return round(resultado, 2)

    def cover_image(self):
        image = self.images.first()
        return image.url() if image else '/static/no_image.jpg'

    def price_with_discount(self):
        # da el precio con descuento en la moneda del producto
        return self.price * (100 - self.discount) / 100

    def sale_price(self):
        # da el precio con descuento en pesos argentinos

        if self.price.currency.code == 'ARS':
            return self.price_with_discount()

        # convertir dolares a pesos
        return Money(self.price_with_discount().amount * 150, "ARS")

    def shipping_price(self):
        ancho = convertir_a_metros(self.width, self.width_unit)
        alto = convertir_a_metros(self.height, self.height_unit)
        profundidad = convertir_a_metros(self.depth, self.depth_unit)
        peso = convertir_a_kilogramos(self.weight, self.weight_unit)
        multiplicador = max([1, ancho * alto * profundidad * peso])
        valor = 50
        return Money(multiplicador * valor, "ARS")

    def variants_with_values(self):
        from Products.models import ProductVariantValue
        return self.variants.filter(Exists(ProductVariantValue.objects.filter(variant__id=OuterRef('pk'))))
class PettycashTransaction(models.Model):
    dst = models.ForeignKey(
        User,
        help_text="Whom to pay the money to",
        on_delete=models.CASCADE,
        related_name="isReceivedBy",
        blank=True,
        null=True,
    )
    src = models.ForeignKey(
        User,
        help_text="Whom paid you",
        on_delete=models.CASCADE,
        related_name="isSentBy",
        blank=True,
        null=True,
    )

    date = models.DateTimeField(blank=True,
                                null=True,
                                help_text="Date of transaction")

    amount = MoneyField(
        max_digits=8,
        decimal_places=2,
        null=True,
        default_currency="EUR",
        validators=[MinMoneyValidator(0)],
    )
    description = models.CharField(
        max_length=300,
        blank=True,
        null=True,
        help_text="Description / omschrijving van waarvoor deze betaling is",
    )

    history = HistoricalRecords()

    def url(self):
        return settings.BASE + self.path()

    def path(self):
        return reverse("transactions", kwargs={"pk": self.id})

    def __str__(self):
        if self.dst == self.src:
            return "@%s BALANCE %s" % (self.date, self.amount)
        return "@%s %s->%s '%s' %s" % (
            self.date,
            self.src,
            self.dst,
            self.description,
            self.amount,
        )

    def delete(self, *args, **kwargs):
        rc = super(PettycashTransaction, self).delete(*args, **kwargs)
        try:
            adjust_balance_cache(self, self.src, self.amount)
            adjust_balance_cache(self, self.dst, -self.amount)
        except Exception as e:
            logger.error("Transaction cache failure on delete: %s" % (e))

        return rc

    def refund_booking(self):
        """
        Refund a booking by doing a new 'reverse' booking, this way all amounts stay positive
        """
        new_transaction = PettycashTransaction()
        new_transaction.src = self.dst
        new_transaction.dst = self.src
        new_transaction.amount = self.amount
        new_transaction.description = "refund %s (%d)" % (self.description,
                                                          self.pk)
        new_transaction.save()

    def save(self, *args, **kwargs):
        bypass = False

        if kwargs is not None and "bypass" in kwargs:
            bypass = kwargs["bypass"]
            del kwargs["bypass"]
        if self.pk:
            if not bypass:
                raise ValidationError(
                    "you may not edit an existing Transaction - instead create a new one"
                )
            logger.info("Bypass used on save of %s" % self)

        if not self.date:
            self.date = timezone.now()

        if self.amount < Money(0, EUR):
            if not bypass:
                raise ValidationError("Blocked negative transaction.")
            logger.info("Bypass for negative transaction used on save of %s" %
                        self)

        rc = super(PettycashTransaction, self).save(*args, **kwargs)
        try:
            adjust_balance_cache(self, self.src, -self.amount)
            adjust_balance_cache(self, self.dst, self.amount)
        except Exception as e:
            logger.error("Transaction cache failure: %s" % (e))

        return rc
Beispiel #29
0
 def clean(self):
     balance_validator = MinMoneyValidator(0 - self.credit_limit)
     try:
         balance_validator(self.balance)
     except ValidationError as e:
         raise ValidationError({'balance': e})
class Order(CommonInfo, TimeStampedModel):
    """
    Order class
    """
    STATUSES = (
        ('new', _('new')),
        ('processing', _('processing')),
        ('paid', _('paid')),
        ('canceled', _('canceled')),
        ('corrupted', _('corrupted')),
    )
    objects = OrderManager()
    tracker = FieldTracker()

    status = models.CharField(max_length=20,
                              default='new',
                              choices=STATUSES,
                              verbose_name=_('status'),
                              db_index=True)
    note = models.TextField(null=True,
                            blank=True,
                            db_index=True,
                            verbose_name=_('note'),
                            help_text=_('Clear to regenerate note'))
    price = MoneyField(max_digits=20,
                       decimal_places=2,
                       default=0,
                       verbose_name=_('price'),
                       validators=[MinMoneyValidator(0)],
                       db_index=True,
                       help_text=_('Set zero to recalculate price'))
    client = models.ForeignKey(Client,
                               on_delete=models.CASCADE,
                               db_index=True,
                               verbose_name=_('client'),
                               related_name='orders')
    expired_date = models.DateTimeField(db_index=True,
                                        blank=True,
                                        verbose_name=_('expired date'))
    paid_date = models.DateTimeField(db_index=True,
                                     null=True,
                                     blank=True,
                                     verbose_name=_('paid date'))
    payment_system = models.CharField(max_length=30,
                                      null=True,
                                      blank=True,
                                      choices=[
                                          (s, _(s))
                                          for s in settings.PAYMENT_SYSTEMS
                                      ],
                                      verbose_name=_('payment system'),
                                      db_index=True)
    client_services = models.ManyToManyField(
        'clients.ClientService',
        blank=True,
        verbose_name=_('client services'),
        through=ClientService.orders.through)
    discount = models.ForeignKey(ClientDiscount,
                                 on_delete=models.SET_NULL,
                                 null=True,
                                 blank=True,
                                 db_index=True,
                                 related_name='orders')

    @property
    def get_room_service(self):
        client_service = self.client_services.filter(
            service__type='rooms',
            service__period_units__in=('month', 'year')).first()
        return getattr(client_service, 'service', None)

    @property
    def client_services_by_category(self):
        """
        Grouped services
        """
        return self.client_services.get_order_services_by_category(self)

    def get_payer(self, client_filter=None, local=True):
        """
        Return payer
        """
        company = self.client.get_bill_company()
        client = self.client
        if client_filter:
            if not all([getattr(client, f, None) for f in client_filter]):
                client = None
        lang = get_lang(self.client.country.tld)
        local_company = getattr(company, lang, None)
        local_client = getattr(client, lang, None)

        if local_company:
            return company
        if local_client:
            return client
        if not local:
            return client

    def calc_price(self):
        """
        Calculate && return price
        """
        if self.status == 'corrupted':
            return Money(0, EUR)
        price = self.client_services.total(self.client_services)
        return self.apply_discount(price)

    def apply_discount(self, price):
        """
        Apply the client discount to the price
        """
        discount = getattr(self.client, 'discount', None)
        now = arrow.utcnow().datetime
        skip = False

        if not discount or not price:
            return price

        if not discount.remaining_uses:
            skip = True

        if discount.start_date and discount.start_date > now:
            skip = True

        if discount.end_date and discount.end_date < now:
            skip = True

        if discount == self.discount:
            skip = False

        if skip:
            return price

        price = price * (100 - discount.percentage_discount) / 100
        if discount != self.discount:
            discount.usage_count += 1
        self.discount = discount
        discount.save()

        return price

    def set_corrupted(self):
        """
        Set corrupted orders
        """
        if len(set([s.price.currency
                    for s in self.client_services.all()])) > 1:
            self.status = 'corrupted'
            self.price = self.calc_price()
            self.save()
            logger = logging.getLogger('billing')
            logger.error('Order corrupted #{}.'.format(self.pk))

    def set_paid(self, payment_system):
        """
        Set paid orders
        """
        self.status = 'paid'
        self.payment_system = payment_system
        self.paid_date = arrow.utcnow().datetime
        self.full_clean()
        self.save()

    @property
    def price_str(self):
        return '{} {}'.format(self.price.amount, self.price.currency)

    def generate_note(self):
        """
        Generate and return default order note
        """
        if self.client_services.count():
            return render_to_string('finances/order_note.md', {'order': self})
        return None

    def clean(self, *args, **kwargs):
        if self.status == 'paid' and \
           (not self.payment_system or not self.paid_date):
            raise ValidationError({
                'status':
                _('Can`t set "paid" status with empty payment system \
and paid date')
            })
        super(Order, self).clean(*args, **kwargs)

    def __str__(self):
        return '#{} - {} - {} - {} - {}'.format(
            self.id, self.status, self.client, self.price,
            self.expired_date.strftime('%c'))

    class Meta:
        ordering = (
            '-modified',
            '-created',
        )
        permissions = (('list_manager', _('Can see assigned entries')), )