예제 #1
0
    def create_reseller_service(self, request, pk):
        del pk  # unused

        # TODO - #1019: implement proper creation of service here
        serializer = StaffCreateServiceSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        client = self.get_object()
        reseller_resources = client_reseller_resources(client=client)

        if reseller_resources is not None:
            raise ForbiddenException(
                {'detail': _('Client already has a reseller service')})

        reseller_product = Product.objects.get(
            id=serializer.validated_data['product_id'])
        reseller_product_cycle = reseller_product.cycles.filter(
            id=serializer.validated_data['product_cycle_id'])[0]

        service = Service.objects.create(
            client=client,
            product=reseller_product,
            cycle=reseller_product_cycle,
            display_name='Reseller service',
            status=ServiceStatus.active,
        )

        module_factory.get_module_instance(service=service).create(
            service=service)

        return Response({'detail': _('Ok')})
예제 #2
0
    def update_usage(self,
                     skip_collecting: bool = False,
                     skip_compute_current: bool = False):
        if not skip_collecting:
            usage_settings = UsageSettings(
                billing_settings=self.billing_settings)

            # compute total unpaid usage for all services associated with the client
            LOG.debug('Updating usage for client {}'.format(self.client))
            for service in self.client.services.all():
                billing_module = module_factory.get_module_instance(
                    service=service)
                billing_module.collect_usage(service=service,
                                             usage_settings=usage_settings)

        if not skip_compute_current:
            # compute current client usage
            self.__client_usage = self.__compute_client_usage()

        # update uptodate credit for client
        uptodate_credit = cdecimal(
            self.client.get_remaining_credit(self.client_usage.unpaid_usage,
                                             self.client.currency.code))
        self.client.set_uptodate_credit(uptodate_credit=uptodate_credit)

        self.update_outofcredit_status()

        # log to summary
        self.summary.update_uptodate_credit(self.uptodate_credit)
예제 #3
0
def create_service_for_item(item):
    try:
        if not validate_services_limit():
            raise APIBadRequest(
                _('Failed to create service, please contact support'), )
        with transaction.atomic():
            service = Service.objects.create(client=item.order.client,
                                             display_name=item.name,
                                             product=item.product,
                                             cycle=item.cycle,
                                             status=ServiceStatus.pending,
                                             next_due_date=utcnow(),
                                             plugin_data=item.plugin_data,
                                             domain_name=item.domain_name)
            for config_option in item.configurable_options.all():
                service.configurable_options.create(
                    option=config_option.option,
                    option_value=config_option.option_value,
                    quantity=config_option.quantity,
                    has_price=config_option.has_price,
                    taxable=config_option.taxable,
                    price=config_option.price,
                    unit_price=config_option.unit_price,
                    setup_fee=config_option.setup_fee)
            billing_module = module_factory.get_module_instance(
                service=service)
            if not billing_module.initialize(service=service):
                raise BillingError(_('Failed to initialize service'))
    except BillingError:
        # failed to initialize service
        # TODO: see what action should we take here
        pass
    else:
        item.service = service
        item.save(update_fields=['service'])
예제 #4
0
def renew_service(self, service_id, **kwargs):
    del self, kwargs  # unused

    service = Service.objects.get(pk=service_id)
    service_module = module_factory.get_module_instance(service=service)
    result = service_module.renew(service=service)
    return result
예제 #5
0
def change_product_service(service_id, product_id, cycle_id, configurable_options):
    product = Product.objects.get(pk=product_id)
    cycle = ProductCycle.objects.get(pk=cycle_id)
    with transaction.atomic():
        service = Service.objects.select_for_update().get(pk=service_id)
        previous_due_date = service.get_previous_due_date()
        service_module = module_factory.get_module_instance(service=service)
        # Update configurable options
        for opt in configurable_options:
            try:
                opt['option'] = ConfigurableOption.objects.get(pk=opt['option'])
            except ConfigurableOption.DoesNotExist:
                continue
            existing_opt = service.configurable_options.filter(option=opt['option']).first()
            if not existing_opt:
                service.configurable_options.create(**opt)
            else:
                if opt['option'].widget == 'yesno' and opt['option_value'] == 'no':
                    service.configurable_options.filter(option=opt['option']).delete()
                else:
                    service.configurable_options.filter(option=opt['option']).update(**opt)
        service.product = product
        service.cycle = cycle
        service.override_price = None  # Disable overridden price if it exists since we upgraded the product
        service.display_name = product.name
        service.update_next_invoice_date(previous_due_date=previous_due_date)
        service.update_next_due_date(previous_due_date=previous_due_date)
        service.save()
        # Set configurable options here
        result = service_module.change_product(service=service)
        service.task = None
        service.save(update_fields=['task'])
    return result
예제 #6
0
 def get_dynamic_price_for_service(service: Service, end_datetime: datetime) -> Decimal:
     LOG.info('Getting dynamic price for service {}'.format(service))
     try:
         billing_module = module_factory.get_module_instance(service=service)
         unsettled_usage = billing_module.get_unsettled_usage(service, end_datetime)
         return cdecimal(unsettled_usage.total_cost)
     except ModuleNotFoundException:
         return Decimal(0)
예제 #7
0
def create_service(self, service_id, **kwargs):
    del self, kwargs  # unused

    service = Service.objects.get(pk=service_id)
    service_module = module_factory.get_module_instance(service=service)
    result = service_module.create(service=service)
    if result:
        service.set_active()
    return service.id
예제 #8
0
    def reset_usage(self):
        # call reset usage for all services
        for service in self.client.services.all():
            billing_module = module_factory.get_module_instance(
                service=service)
            billing_module.reset_usage(service=service)

        # recalculate usage after reset
        self.update_usage()
예제 #9
0
 def get_unsettled_periods(client: Client):
     services_unsettled_periods = []
     for service in client.services.all():
         billing_module = module_factory.get_module_instance(
             service=service)
         service_unsettled_periods = billing_module.get_service_unsettled_periods(
             service=service)
         services_unsettled_periods = services_unsettled_periods + service_unsettled_periods
     return ClientUnsettledHistorySerializer(services_unsettled_periods,
                                             many=True).data
예제 #10
0
 def destroy(self, request, *args, **kwargs):
     service = self.get_object()
     if service.client.billing_settings.suspend_instead_of_terminate or reseller_suspend_instead_of_terminate(
         client=service.client,
     ):
         if self.request.query_params.get('delete_associated_resources', 'false') == 'true':
             tasks.suspend_service.delay(
                 service_id=service.id,
                 reason=ServiceSuspendType.SUSPEND_REASON_TERMINATE_DISABLED,
                 suspend_type=ServiceSuspendType.staff,
             )
             return Response(
                 {
                     'detail': _('Suspend instead of terminate is enabled for the client. Suspending service.')
                 },
                 status=status.HTTP_202_ACCEPTED
             )
         else:
             return Response(
                 {'detail': _('Suspend instead of terminate is enabled for the client, service not deleted.')},
                 status=status.HTTP_403_FORBIDDEN
             )
     else:
         if self.request.query_params.get('delete_associated_resources', 'false') == 'true':
             billing_module = module_factory.get_module_instance(service=service)
             delete_task = billing_module.prepare_delete_task(service=service, user_id=request.user.id)
             celery.chain(
                 delete_task,
                 tasks.delete_service_from_database.si(service_id=service.id)
             ).apply_async()
             return Response(
                 {'detail': _('Delete in progress.')},
                 status=status.HTTP_202_ACCEPTED
             )
         else:
             if service.status in [
                 ServiceStatus.terminated,
                 ServiceStatus.canceled,
                 ServiceStatus.pending,
                 ServiceStatus.fraud
             ]:
                 tasks.delete_service_from_database.si(service_id=service.id).apply_async()
                 return Response(
                     {'detail': _('Delete in progress.')},
                     status=status.HTTP_202_ACCEPTED
                 )
             else:
                 return Response(
                     {
                         'detail': _('Cannot delete service with active resources, terminate service first.')
                     },
                     status=status.HTTP_403_FORBIDDEN
                 )
예제 #11
0
    def get_cloud_summary(self, request, pk):
        del request, pk  # unused
        client = self.get_object()
        if client.first_project is None:
            return Response({})

        # TODO - #1020: iterate over all services and get usage summary for all
        service = client.first_project.service
        billing_module = module_factory.get_module_instance(service=service)
        summary = billing_module.get_usage_summary(service=service)

        return Response(summary)
예제 #12
0
    def reset_usage(self, service: Service) -> bool:
        LOG.debug('{} reset_usage called for service {}:{}'.format(
            self.module_name, service.id, service))

        for reseller_client in filter_queryset_for_client(
                queryset=Client.objects, client=service.client).all():
            for client_service in reseller_client.services.all():
                service_module = module_factory.get_module_instance(
                    service=client_service, reseller_usage=True)
                service_module.reset_usage()

        return True
예제 #13
0
def suspend_service(self, service_id, reason, suspend_type=None, **kwargs):
    del self, kwargs  # unused

    service = Service.objects.get(pk=service_id)
    LOG.debug('Suspending service {} for client {} ({})'.format(
        service_id, service.client.name, service.client.id,
    ))

    service_module = module_factory.get_module_instance(service=service)
    result = service_module.suspend(service=service, reason=reason)
    if result:
        service.set_suspended(reason=reason, suspend_type=suspend_type)
    return result
예제 #14
0
    def get_unpaid_usage(self, service: Service) -> ServiceUsage:
        LOG.debug('{} get_unpaid_usage called for service {}:{}'.format(
            self.module_name, service.id, service))

        service_usage = ServiceUsage(total_cost=Decimal('0.00'))
        for reseller_client in filter_queryset_for_client(
                queryset=Client.objects, client=service.client).all():
            for client_service in reseller_client.services.all():
                service_module = module_factory.get_module_instance(
                    service=client_service, reseller_usage=True)
                service_usage = service_usage + service_module.get_unpaid_usage(
                    service=client_service, )

        return service_usage
예제 #15
0
 def update_billing_plan(self, request, pk):
     del pk  # unused
     if not staff_active_features.is_enabled('openstack.plans'):
         raise APIBadRequest(detail=_('Cannot update os plan because openstack plans feature is disabled'))
     service = self.get_object()  # type: Service
     if service.status in [
         ServiceStatus.terminated,
         ServiceStatus.canceled,
         ServiceStatus.fraud
     ]:
         raise APIBadRequest(_('Cannot change pricing plan for service in this state.'))
     new_plan_id = request.data.get('plan')
     billing_module = module_factory.get_module_instance(service=service)
     billing_module.change_pricing_plan(service=service, new_plan_id=new_plan_id)
     return Response({'detail': _('Pricing plan updated')})
예제 #16
0
 def mark_billing_histories_as_invoiced(self, request):
     """Method called from external module (fleio-whmcs) to mark billing histories as invoiced"""
     client_external_billing_id = request.data.get('client_external_billing_id', None)
     if not client_external_billing_id:
         raise APIBadRequest(_('Client external billing id is required to fulfill this request.'))
     try:
         client = Client.objects.get(external_billing_id=client_external_billing_id)
     except Client.DoesNotExist:
         raise APIBadRequest(_('Could not find client related to given external billing id.'))
     for service in client.services.all():
         billing_module = module_factory.get_module_instance(service=service)
         # this will mark unsettled service dynamic usage histories to invoiced
         billing_module.get_unsettled_usage(service=service, end_datetime=utcnow())
     return Response({
         'detail': _('Successfully marked client {} billing histories states as invoiced.').format(client.id)
     })
예제 #17
0
    def get_estimated_usage(self, service: Service,
                            usage_settings: UsageSettings) -> EstimatedUsage:
        LOG.debug('{} get_estimated_usage called for service {}:{}'.format(
            self.module_name, service.id, service))

        estimated_usage = EstimatedUsage()
        for reseller_client in filter_queryset_for_client(
                queryset=Client.objects, client=service.client).all():
            for client_service in reseller_client.services.all():
                service_module = module_factory.get_module_instance(
                    service=client_service, reseller_usage=True)
                estimated_usage = estimated_usage + service_module.get_estimated_usage(
                    service=client_service,
                    usage_settings=usage_settings,
                )

        return estimated_usage
예제 #18
0
    def __compute_client_usage(self) -> ClientUsage:
        # compute total unpaid usage for all services associated with the client
        LOG.debug('Computing total usage for client {}'.format(self.client))
        total_unpaid_usage = Decimal(0)
        total_estimated_usage = EstimatedUsage()
        usage_settings = UsageSettings(billing_settings=self.billing_settings)
        for service in self.client.services.all():
            billing_module = module_factory.get_module_instance(
                service=service)

            # get unpaid usage from billing module
            unpaid_usage = billing_module.get_unpaid_usage(service)
            total_unpaid_usage += unpaid_usage.total_cost

            if service.status == ServiceStatus.active:
                # get unpaid usage from service
                if service.next_due_date is not None and service.next_due_date < self.reference_datetime:
                    total_unpaid_usage += service.get_fixed_price()

                # see if we need to get estimated usage from billing module
                if not service.is_price_overridden:
                    # get estimated usage from billing module
                    estimated_usage = billing_module.get_estimated_usage(
                        service, usage_settings=usage_settings)
                    total_estimated_usage += estimated_usage

                # add service static price if needed
                if service.get_fixed_price() > 0:
                    service_fixed_usage = EstimatedUsage.create_for_fixed_price(
                        fixed_price=service.get_fixed_price(),
                        cycle_end_date=service.next_due_date,
                        get_next_end_date=lambda previous_end_date, s=service:
                        s.get_next_due_date(previous_end_date),
                        usage_settings=usage_settings)

                    total_estimated_usage += service_fixed_usage

        return ClientUsage(total_unpaid_usage, total_estimated_usage)
예제 #19
0
 def accept(self, request: Request, pk) -> Response:
     del pk  # unused
     order = self.get_object()
     can_accept = True
     messages = []
     for item in order.items.all():
         if item.service:
             billing_module = module_factory.get_module_instance(service=item.service)
             can_accept_service, message = billing_module.can_accept_order(service=item.service)
             can_accept = can_accept and can_accept_service
             if not can_accept_service:
                 messages.append(message)
     if can_accept:
         order_accept.delay(order.id, user_id=request.user.pk)
         return Response({'detail': 'Accepted'}, status=HTTP_202_ACCEPTED)
     else:
         return Response(
             {
                 'detail': 'Cannot accept order',
                 'messages': messages
             },
             status=HTTP_422_UNPROCESSABLE_ENTITY,
         )
예제 #20
0
def terminate_client(self,
                     client_id: int,
                     user_id: Optional[int] = None,
                     delete_all_resources: Optional[bool] = False):
    del self  # unused
    client = Client.objects.get(id=client_id)  # type: Client

    if client.billing_settings.suspend_instead_of_terminate or reseller_suspend_instead_of_terminate(
            client=client):
        raise Exception(
            'Terminate client called when suspend instead of terminate is true'
        )

    # delete all services for client
    delete_services_tasks = []

    for service in client.services.all():
        billing_module = module_factory.get_module_instance(service=service)
        if delete_all_resources:
            delete_services_tasks.append(
                celery.chain(
                    billing_module.prepare_delete_task(service=service,
                                                       user_id=user_id),
                    service_tasks.delete_service_from_database.si(
                        service_id=service.id)))
        else:
            delete_services_tasks.append(
                service_tasks.delete_service_from_database.si(
                    service_id=service.id))

    if len(delete_services_tasks):
        final_chord = celery.group(
            delete_services_tasks) | delete_client_from_database.si(
                client_id=client_id)
        celery.chain(final_chord).apply_async()
    else:
        delete_client_from_database.delay(client_id=client_id)
예제 #21
0
def terminate_service(self, service_id, cancellation_request_id=None, **kwargs):
    del self, kwargs  # unused

    service = Service.objects.get(pk=service_id)
    service_module = module_factory.get_module_instance(service=service)
    if service.client.billing_settings.suspend_instead_of_terminate or reseller_suspend_instead_of_terminate(
        client=service.client,
    ):
        LOG.info('Suspend instead of terminate is active, will suspend the service {}'.format(service_id))
        result = service_module.suspend(
            service=service,
            reason=ServiceSuspendType.SUSPEND_REASON_TERMINATE_DISABLED,
            suspend_type=ServiceSuspendType.staff
        )
        if result:
            service.set_suspended(
                reason=ServiceSuspendType.SUSPEND_REASON_TERMINATE_DISABLED,
                suspend_type=ServiceSuspendType.staff
            )
    else:
        LOG.info('Service will be terminated: {}'.format(service_id))
        service_module.prepare_delete_task(service=service).apply()
        service.set_terminated(cancellation_request_id=cancellation_request_id)
    return service.id
예제 #22
0
    def get_client_revenue(client: Client, start_date: datetime.datetime,
                           end_date: datetime.datetime):
        """Get all client revenue that should be included in the report"""
        services_report = {}
        report = {
            'client': client.id,
            'client_display_name': client.long_name,
            'services_report': services_report,
            'credit_in': decimal.Decimal('0.00'),
            'credit_out': decimal.Decimal('0.00'),
            'credit_available': decimal.Decimal('0.00')
        }
        client_main_credit_account = client.credits.filter(
            currency=client.currency).first()

        if client_main_credit_account:
            # set the last available credit for the given period
            last_journal_entry = Journal.objects.filter(
                Q(date_added__lt=end_date)
                & (Q(client_credit=client_main_credit_account)
                   | Q(invoice__client=client))).order_by('date_added').last()
            if (last_journal_entry and last_journal_entry.client_credit_left
                    and last_journal_entry.client_credit_left_currency.code
                    == client_main_credit_account.currency.code):
                report[
                    'credit_available'] = last_journal_entry.client_credit_left
            else:
                # TODO(manu): This conditional branch will not work when re-generating for older months if a fleio
                #   installation exists from a longer time (there are no journal entries that report the credit_
                #   available after that journal entry). To fix this, calculate client_credit_left for each journal
                #   entry since beginning of time in a migration
                report['credit_available'] = client_main_credit_account.amount
        # Add each service to the report and it's usage details given by it's billing module
        # if available.
        for service in client.services.filter(
                Q(terminated_at__isnull=True) | Q(terminated_at__lt=end_date)):
            fixed_monthly_price = cdecimal(
                service.get_fixed_price(),
                q='.01')  # returns fixed or overriden price
            services_report[service.id] = {
                'service_name':
                service.display_name,
                'service_id':
                service.id,
                'service_type':
                service.product.product_type,
                'service_last_cycle':
                JournalReport._get_next_due_date(service=service,
                                                 until_date=end_date),
                'entries': [],
                'fixed_monthly_price':
                fixed_monthly_price,  # fixed or overriden price
                'price_overridden':
                service.is_price_overridden,
                'total_revenue':
                decimal.Decimal('0.00'),
                'total_from_credit':
                decimal.Decimal('0.00')
            }

            # Get the report module for each service if it exists, in order to get a detailed location usage
            service_module = module_factory.get_module_instance(
                service=service)
            services_report[service.id][
                'usage_details'] = service_module.get_service_report(
                    service, start_date, end_date)
        # Gather journal entries and split out credit and direct service payments through invoices
        if client_main_credit_account:
            # All credit entries that need to appear on the report
            credit_qs = Journal.objects.filter(
                date_added__gte=start_date,
                date_added__lt=end_date,
                client_credit=client_main_credit_account)
            credit_in_qs = credit_qs.filter(destination=JournalSources.credit,
                                            source__in=[
                                                JournalSources.external,
                                                JournalSources.transaction
                                            ])
            credit_in_qs = credit_in_qs.aggregate(
                dest_amount=Coalesce(models.Sum('destination_amount'), 0))
            credit_amount_in = credit_in_qs.get('dest_amount', 0)
            credit_out_qs = credit_qs.filter(source=JournalSources.credit,
                                             destination__in=[
                                                 JournalSources.external,
                                                 JournalSources.transaction
                                             ])
            credit_out_qs = credit_out_qs.aggregate(
                source_amount=Coalesce(models.Sum('source_amount'), 0))
            credit_amount_out = credit_out_qs.get('source_amount', 0)
            report['credit_in'] += credit_amount_in
            report['credit_out'] += credit_amount_out
        # Revenue from invoices:
        invoice_journal_qs = Journal.objects.filter(
            invoice__client=client,
            date_added__gte=start_date,
            date_added__lt=end_date).order_by('date_added')
        invoice_journal_qs = invoice_journal_qs.filter(
            Q(source=JournalSources.invoice,
              destination__in=[
                  JournalSources.external, JournalSources.transaction
              ]) | Q(destination=JournalSources.invoice,
                     source__in=[
                         JournalSources.external,
                         JournalSources.transaction,
                         JournalSources.staff,
                     ])).all()
        for journal_entry in invoice_journal_qs:
            invoice = journal_entry.invoice
            items_percent = JournalReport.get_invoice_items_percent(
                invoice=invoice)
            for item in invoice.items.all():
                if item.service:
                    if item.service.id in services_report:
                        if items_percent[item.id]['percent'] != 0:
                            amount = items_percent[item.id][
                                'percent'] / 100 * journal_entry.destination_amount
                            taxamt = items_percent[item.id][
                                'taxes_percent'] / 100 * journal_entry.destination_amount
                            amount -= taxamt
                            if journal_entry.destination in [
                                    JournalSources.transaction,
                                    JournalSources.external
                            ]:
                                amount = -1 * amount
                            amount = cdecimal(amount, q='.01')
                            taxamt = cdecimal(taxamt, q='.01')
                            services_report[item.service.id]['entries'].append(
                                {
                                    'amount':
                                    amount,
                                    'item_type':
                                    item.item_type,
                                    'from_credit':
                                    False,
                                    'taxes_amount':
                                    taxamt,
                                    'taxes_percent':
                                    items_percent[item.id]['taxes_percent'],
                                    'source':
                                    journal_entry.source,
                                    'date':
                                    str(journal_entry.date_added)
                                })
                            services_report[
                                item.service.id]['total_revenue'] += amount
                    else:
                        amount = items_percent[item.id][
                            'percent'] / 100 * journal_entry.destination_amount
                        taxamt = items_percent[item.id][
                            'taxes_percent'] / 100 * journal_entry.destination_amount
                        amount -= taxamt
                        if journal_entry.destination in [
                                JournalSources.transaction,
                                JournalSources.external
                        ]:
                            amount = -1 * amount
                        amount = cdecimal(amount, q='.01')
                        taxamt = cdecimal(taxamt, q='.01')
                        services_report[item.service.id] = {
                            'entries': [{
                                'amount':
                                amount,
                                'item_type':
                                item.item_type,
                                'from_credit':
                                False,
                                'taxes_amount':
                                taxamt,
                                'taxes_percent':
                                items_percent[item.id]['taxes_percent'],
                                'source':
                                journal_entry.source,
                                'date':
                                str(journal_entry.date_added)
                            }],
                            'service':
                            item.service.display_name,
                            'total_revenue':
                            amount,
                            'total_from_credit':
                            decimal.Decimal('0.00')
                        }

        # Credit entries for each service
        cservice_qs = Journal.objects.filter(
            date_added__gte=start_date,
            date_added__lt=end_date,
            client_credit=client_main_credit_account)
        cservice_qs = cservice_qs.filter(
            Q(source=JournalSources.credit, destination=JournalSources.invoice)
            | Q(source=JournalSources.invoice,
                destination=JournalSources.credit)).all()

        for journal_entry in cservice_qs:
            invoice = journal_entry.invoice
            items_percent = JournalReport.get_invoice_items_percent(
                invoice=invoice)
            for item in invoice.items.all():
                if item.service:
                    if item.service.id in services_report:
                        if items_percent[item.id]['percent'] != 0:
                            amount = items_percent[item.id][
                                'percent'] / 100 * journal_entry.destination_amount
                            taxamt = items_percent[item.id][
                                'taxes_percent'] / 100 * journal_entry.destination_amount
                            amount -= taxamt
                            if journal_entry.destination == JournalSources.credit:
                                amount = -1 * amount
                            amount = cdecimal(amount, q='.01')
                            taxamt = cdecimal(taxamt, q='.01')
                            credit_entries = services_report[
                                item.service.id]['entries']
                            credit_entries.append({
                                'amount':
                                amount,
                                'item_type':
                                item.item_type,
                                'from_credit':
                                True,
                                'taxes_amount':
                                taxamt,
                                'taxes_percent':
                                items_percent[item.id]['taxes_percent'],
                                'source':
                                journal_entry.source,
                                'date':
                                str(journal_entry.date_added)
                            })
                            services_report[
                                item.service.id]['total_revenue'] += amount
                            services_report[
                                item.service.id]['total_from_credit'] += (
                                    amount + taxamt)
                    else:
                        amount = items_percent[item.id][
                            'percent'] / 100 * journal_entry.destination_amount
                        taxamt = items_percent[item.id][
                            'taxes_percent'] / 100 * journal_entry.destination_amount
                        amount -= taxamt
                        if journal_entry.destination == JournalSources.credit:
                            amount = -1 * amount
                        amount = cdecimal(amount, q='.01')
                        taxamt = cdecimal(taxamt, q='.01')
                        credit_entries = [{
                            'amount':
                            amount,
                            'item_type':
                            item.item_type,
                            'from_credit':
                            True,
                            'taxes_amount':
                            taxamt,
                            'taxes_percent':
                            items_percent[item.id]['taxes_percent'],
                            'source':
                            journal_entry.source,
                            'date':
                            str(journal_entry.date_added)
                        }]
                        services_report[item.service.id] = {
                            'entries': credit_entries,
                            'service': item.service.display_name,
                            'total_revenue': decimal.Decimal('0.00'),
                            'total_from_credit': amount + taxamt
                        }
                elif item.item_type == BillingItemTypes.credit:
                    if journal_entry.destination == JournalSources.invoice:
                        report['credit_out'] += item.amount
                    else:
                        report['credit_in'] += item.amount
        # Gether all credit used by services
        # The credit is split proportional to each service, based on it's usage report
        client_available_credit = report['credit_available']
        total_still_required_cost = decimal.Decimal('0.00')
        total_required_cost = decimal.Decimal('0.00')
        total_debt = decimal.Decimal('0.00')
        total_credit_alloted = decimal.Decimal('0.00')
        for service_id, service_report in services_report.items():
            price_overridden = service_report[
                'price_overridden'] if 'price_overridden' in service_report else False
            total_revenue = service_report['total_revenue']
            if price_overridden:
                # If pirice is overridden, the service total cost is the fixed price one - entries for it
                service_required_cost = service_report['fixed_monthly_price']
                service_report['service_required_cost'] = cdecimal(
                    service_required_cost, q='.01')
                cost_still_required = service_required_cost - total_revenue
                cost_still_required = cdecimal(cost_still_required, q='.01')
                service_report['cost_still_required'] = cost_still_required
            else:
                # Add here logic for dynamic but minimum fixed
                fixed_monthly_price = (service_report['fixed_monthly_price'] if
                                       'fixed_monthly_price' in service_report
                                       else decimal.Decimal('0.00'))
                usage_details = (service_report['usage_details']
                                 if 'usage_details' in service_report else {})
                service_required_cost = (
                    fixed_monthly_price +
                    usage_details.get('total_cost', decimal.Decimal('0.00')))
                cost_still_required = service_required_cost - total_revenue
                cost_still_required = cdecimal(cost_still_required, q='.01')
                service_report['service_required_cost'] = cdecimal(
                    service_required_cost, q='.01')
                service_report['cost_still_required'] = cost_still_required
            # Create a total amount so we can deduct from credit available and calculate the percentage
            total_required_cost += service_required_cost
            total_still_required_cost += cost_still_required

        # Calculate the percentage, debts, credit alloted of each service
        services_report, report = JournalReport._calculate_amount_for_services(
            client=client,
            services_report=services_report,
            report=report,
            total_still_required_cost=total_still_required_cost,
            client_available_credit=client_available_credit,
            total_debt=total_debt,
            total_credit_alloted=total_credit_alloted,
            end_date=end_date)
        # Go through each service to get the totals per region
        revenue_per_location = {}
        default_location = JournalReport.get_default_location()
        for service_id, service_report in services_report.items():
            # Set an equal percent for each location
            usage_details = service_report.get('usage_details', {})
            if type(usage_details) is dict and usage_details.keys():
                # Deal with services that have usage_details
                location_cost = usage_details.get('location_cost')
                total_revenue = service_report['alloted_from_credit']
                service_location_alloted = JournalReport.get_percent_per_location(
                    location_cost, total_revenue)
                for location_name, costs in service_location_alloted.items():
                    if location_name not in revenue_per_location:
                        revenue_per_location[location_name] = decimal.Decimal(
                            '0.00')
                    revenue_per_location[location_name] += costs['alloted']
            else:
                if default_location not in revenue_per_location:
                    revenue_per_location[default_location] = decimal.Decimal(
                        '0.00')
                total_revenue = service_report['alloted_from_credit']
                revenue_per_location[default_location] += total_revenue
        report['revenue_per_location'] = [{
            'name': name,
            'revenue': cdecimal(revenue, q='.01')
        } for name, revenue in revenue_per_location.items()]

        return report
예제 #23
0
 def settle_dynamic_price_for_service(service: Service, issue_date: datetime):
     try:
         billing_module = module_factory.get_module_instance(service=service)
         billing_module.settle_usage(service, issue_date)
     except ModuleNotFoundException:
         pass