示例#1
0
 def get_currency_or_default(self):
     if self.request and self.request.user.is_authenticated:
         client = self.request.user.clients.first()
         if client:
             currency = client.currency
         else:
             currency = get_default_currency()
     else:
         currency = get_default_currency()
     return currency
示例#2
0
 def validate(self, attrs):
     # NOTE(tomo): Validate cycle is unique
     value = attrs.get('value')
     option = attrs.get('option')
     cycle = attrs.get('cycle')
     cycle_multiplier = attrs.get('cycle_multiplier')
     currency = attrs.get('currency')
     if not currency:
         raise serializers.ValidationError(detail=_('Currency is required'))
     extra_attrs = {}
     if value:
         extra_attrs['value'] = value
     elif option:
         extra_attrs['option'] = option
     else:
         extra_attrs['value__isnull'] = True
         extra_attrs['option__isnull'] = True
     exist_query = ConfigurableOptionCycle.objects
     if self.instance:
         # We edit the same instance, exclude it from query
         exist_query = exist_query.exclude(pk=self.instance.pk)
     if exist_query.filter(cycle=cycle,
                           cycle_multiplier=cycle_multiplier,
                           currency=currency,
                           **extra_attrs).exists():
         raise serializers.ValidationError(detail='Similar cycle already exists')
     # End cycle unique validation
     relative_pricing = attrs.get('is_relative_price', False)
     if relative_pricing:
         # Check if relative pricing is sent, modify pricing if we have a similar cycle in default currency
         # or just raise
         default_currency = get_default_currency()
         if attrs['currency'] != default_currency:
             def_cycle = ConfigurableOptionCycle.objects.filter(
                 option=option,
                 cycle=cycle,
                 cycle_multiplier=cycle_multiplier,
                 currency=default_currency
             ).first()
             if def_cycle is None:
                 raise serializers.ValidationError(detail=_('Unable to auto calculate prices'))
             converted_price = utils.convert_currency(price=def_cycle.price,
                                                      from_currency=def_cycle.currency,
                                                      to_currency=attrs['currency'])
             attrs['price'] = utils.cdecimal(converted_price, q='.01')
             converted_setup_fee = utils.convert_currency(price=def_cycle.setup_fee,
                                                          from_currency=def_cycle.currency,
                                                          to_currency=attrs['currency'])
             attrs['setup_fee'] = utils.cdecimal(converted_setup_fee, q='.01')
     return attrs
示例#3
0
    def validate(self, attrs):
        attrs = super(StaffProductCycleSerializer, self).validate(attrs)
        cycle = attrs.get('cycle')
        cycle_multiplier = attrs.get('cycle_multiplier')
        auto_calculate_price = attrs.get('is_relative_price', False)
        cycle_currency_code = attrs.get('currency')
        cycle_product = attrs.get('product')
        if cycle_product:
            # do not allow one-time cycles to go with recurring cycles
            other_cycles = ProductCycle.objects.filter(product=cycle_product)
            if cycle == CyclePeriods.onetime:
                for other_cycle in other_cycles:  # type: ProductCycle
                    if other_cycle.cycle != CyclePeriods.onetime:
                        raise serializers.ValidationError(detail=_(
                            'Cannot add one time cycle if the product has a recurring cycle.'
                        ))
            else:  # treat the case when the new cycle is a recurring one
                for other_cycle in other_cycles:  # type: ProductCycle
                    if other_cycle.cycle == CyclePeriods.onetime:
                        raise serializers.ValidationError(detail=_(
                            'Cannot add recurring cycle if the product has a one time cycle.'
                        ))
        if auto_calculate_price and cycle_product:
            # auto calculate prices
            default_currency = get_default_currency()
            if default_currency.code != cycle_currency_code:
                def_cycle = ProductCycle.objects.filter(
                    product=cycle_product,
                    cycle=cycle,
                    cycle_multiplier=cycle_multiplier,
                    currency=default_currency).first()
                if def_cycle is None:
                    c_msg = _(
                        'A cycle with {} currency is required to auto calculate price'
                    ).format(default_currency)
                    raise serializers.ValidationError(detail=c_msg)

                converted_price = utils.convert_currency(
                    price=def_cycle.fixed_price,
                    from_currency=def_cycle.currency,
                    to_currency=cycle_currency_code)
                attrs['fixed_price'] = utils.cdecimal(converted_price, q='.01')
                converted_setup_fee = utils.convert_currency(
                    price=def_cycle.setup_fee,
                    from_currency=def_cycle.currency,
                    to_currency=cycle_currency_code)
                attrs['setup_fee'] = utils.cdecimal(converted_setup_fee,
                                                    q='.01')
        return attrs
def create_cart(request) -> FleioCart:
    cart_session_id = str(uuid4())
    cart_data = {}
    cart_metadata = OrderMetadata.from_request(request).to_json()
    if request.user.is_authenticated:
        cart_data['user'] = request.user.pk
        user_client = request.user.clients.first()
        if user_client:
            cart_data['client'] = user_client.pk
            cart_data['currency'] = user_client.currency.code

    if cart_data.get('currency') is None:
        cart_data['currency'] = get_default_currency().code
    cart_serializer = CartSerializer(data=cart_data)
    cart_serializer.is_valid(raise_exception=True)
    cart = cart_serializer.save(metadata=cart_metadata,
                                storage_id=cart_session_id)
    request.session[CART_SESSION_KEY] = cart_session_id
    return cart
示例#5
0
 def create_options(self, request):
     del request  # unused
     default_currency = get_default_currency()  # type: Currency
     create_options = {
         'countries':
         get_countries(),
         'currencies':
         [currency.code for currency in Currency.objects.all()],
         'default_currency':
         default_currency.code if default_currency else None,
         'custom_fields':
         ClientCustomFieldDefinition().definition,
         'max_email_attachment_size':
         settings.MAX_EMAIL_ATTACHMENT_SIZE,
         'email_variables': [
             '{}{}{}'.format('{{ ', name, ' }}')
             for name in self.get_email_variables()
         ],
     }
     return Response(create_options)
示例#6
0
def client_credit_post_save_callback(sender, instance: ClientCredit, **kwargs):
    # if a client credit is created or updated maybe credit is enough to resume client if it was suspended
    LOG.debug('Client credit post save callback invoked by {}'.format(sender))

    client = instance.client
    billing_settings = client.billing_settings

    created = kwargs.get('created', False)
    if created:
        if billing_settings.client_initial_credit > 0:
            default_currency = get_default_currency()
            monetary_amount = MonetaryAmount(
                value=Decimal(billing_settings.client_initial_credit),
                currency=default_currency,
            )
            destination_amount = monetary_amount.get_value_in_currency(currency=client.currency)
            client.add_credit(
                amount=destination_amount,
                currency=client.currency
            )
            Journal.objects.create(
                client_credit=instance,
                transaction=None,
                source_currency=default_currency,
                destination_currency=client.currency,
                source=JournalSources.staff,
                destination=JournalSources.credit,
                source_amount=billing_settings.client_initial_credit,
                destination_amount=destination_amount
            )

    if billing_settings.auto_resume_client_on_credit_update:
        client_operations = ClientOperations(client)
        client_operations.update_usage(skip_collecting=True)
        if client.status == ClientStatus.suspended:
            client_operations.evaluate_and_resume_if_enough_credit()
示例#7
0
    def pay_from_credit_balance(self, request, pk):
        invoice = self.get_object()
        if invoice.is_credit_invoice():
            # NOTE(tomo): do not allow payment from credit balance if this is a credit invoice
            return Response(
                {'detail': _('Unable to pay a credit invoice with credit')},
                status=HTTP_409_CONFLICT)
        if not invoice.is_unpaid():
            # NOTE(tomo): only unpaid invoices should be allowed
            return Response(
                {'detail': _('Only unpaid invoices can be paid from credit')},
                status=HTTP_409_CONFLICT)

        invoice_has_default_currency = True if invoice.currency.code == get_default_currency(
        ).code else False
        if not invoice_has_default_currency:
            try:
                credit_balance = invoice.client.credits.get(
                    currency=invoice.currency).amount
            except ClientCredit.DoesNotExist as e:
                LOG.error(e)
                credit_balance = 0
        else:
            credit_balance = invoice.client.uptodate_credit
        if credit_balance <= 0:
            return Response({'detail': _('Not enough credit')},
                            status=HTTP_409_CONFLICT)

        invoice_balance = invoice.balance
        # don't allow payment if the remaining credit (only for up to date credit in default currency) will be less
        # than the minimum specified in client's configuration after paying the invoice
        if invoice_has_default_currency:
            min_credit_to_be_left = invoice.client.billing_settings.minim_uptodate_credit_for_invoice_payment
            not_enough_credit_response = Response(data={
                'detail':
                _('You should have at least {} {} credit left after making a payment'
                  ).format(min_credit_to_be_left, invoice.client.currency)
            },
                                                  status=HTTP_409_CONFLICT)
            if credit_balance >= invoice_balance:
                if credit_balance - invoice_balance < min_credit_to_be_left:
                    return not_enough_credit_response
            elif min_credit_to_be_left > 0:
                return not_enough_credit_response

        amount = 0

        if invoice_balance >= credit_balance > 0:
            amount = credit_balance
        elif credit_balance > invoice_balance > 0:
            amount = invoice_balance

        currency_code = invoice.client.currency.code
        tasks.invoice_add_payment.delay(
            invoice.id,
            amount=amount,
            currency_code=currency_code,
            from_credit_balance=True,
            user_id=request.user.pk,
            create_todo=invoice.client.billing_settings.
            create_todo_on_invoice_payment,
        )

        return Response(
            {
                'detail':
                _('Adding {} {} to invoice'.format(amount, currency_code))
            },
            status=HTTP_202_ACCEPTED)
示例#8
0
    def get_domain_registrar_prices(domain: Domain,
                                    registrar: Registrar,
                                    years=None) -> SimpleNamespace or None:
        """Get the registrar prices for a domain"""
        # TODO(tomo): Check if domain is premium
        if years is None:
            years = domain.registration_period
        tld_name = domain.tld.name
        default_currency = get_default_currency()
        register_price = None
        transfer_price = None
        renew_price = None

        response = RegistrarPrices.objects.filter(
            tld_name=tld_name, connector=registrar.connector)
        if years > 1:
            # If we need a higher number of years, check if we have the answer cached
            # otherwise we need to calculate it
            response = response.filter(Q(years=1) | Q(years=years))
        # Prices can be in multiple currencies and with different years. At least for 1 year we should have the price
        price_currency_match = None
        for db_price in response:
            if db_price.currency == default_currency.code:
                if db_price.years == years:
                    price_currency_match = db_price
                elif db_price.years == 1 and not price_currency_match:
                    price_currency_match = db_price
        price_years_match = None
        if not price_currency_match:
            for db_price in response:
                if db_price.years == years:
                    price_years_match = db_price
                elif db_price.years == 1 and not price_years_match:
                    price_years_match = db_price
        if price_currency_match:
            if price_currency_match.years != years:
                register_price = cdecimal(price_currency_match.register_price *
                                          years)
                renew_price = cdecimal(price_currency_match.renew_price *
                                       years)
                transfer_price = cdecimal(price_currency_match.transfer_price
                                          )  # Transfers are on 1 year only
            else:
                register_price = cdecimal(price_currency_match.register_price)
                renew_price = cdecimal(price_currency_match.renew_price)
                transfer_price = cdecimal(price_currency_match.transfer_price)
        elif price_years_match:
            if price_years_match.years != years:
                pre_register_price = cdecimal(
                    price_years_match.register_price * years)
                pre_renew_price = cdecimal(price_years_match.renew_price *
                                           years)
                pre_transfer_price = cdecimal(price_years_match.transfer_price
                                              )  # Transfers are on 1 year only
            else:
                pre_register_price = cdecimal(price_years_match.register_price)
                pre_renew_price = cdecimal(price_years_match.register_price)
                pre_transfer_price = cdecimal(price_years_match.register_price)
            try:
                tld_currency = Currency.objects.get(
                    code=price_years_match.currency)
            except Currency.DoesNotExist:
                LOG.error(
                    'Registry currency {} does not exist in Fleio'.format(
                        price_years_match.currency))
                return None
            register_price = convert_currency(price=pre_register_price,
                                              from_currency=tld_currency,
                                              to_currency=default_currency)
            renew_price = convert_currency(price=pre_renew_price,
                                           from_currency=tld_currency,
                                           to_currency=default_currency)
            transfer_price = convert_currency(price=pre_transfer_price,
                                              from_currency=tld_currency,
                                              to_currency=default_currency)

        if register_price or renew_price or transfer_price:
            tld_prices = SimpleNamespace()
            tld_prices.register_price = cdecimal(register_price)
            tld_prices.renew_price = cdecimal(renew_price)
            tld_prices.transfer_price = cdecimal(transfer_price)
            tld_prices.currency = default_currency.code
            return tld_prices
        else:
            return None
示例#9
0
 def get_report_currency():
     def_c = get_default_currency()
     if not def_c:
         return 'USD'
     else:
         return def_c.code