示例#1
0
    def test_invoice_price_is_calculated_on_package_creation(self):
        with freeze_time('2016-11-04 12:00:00'):
            package = self.fixture.openstack_package

            days = (invoices_utils.get_current_month_end() - timezone.now()).days + 1
            expected_total = days * package.template.price

        with freeze_time(invoices_utils.get_current_month_end()):
            invoice = invoices_models.Invoice.objects.get(
                customer=package.tenant.service_project_link.project.customer
            )
            self.assertEqual(invoice.total, expected_total)
示例#2
0
    def _create_item(self, source, invoice, start, end):
        package = source
        overlapping_item = utils.get_openstack_items().filter(
            invoice=invoice,
            end__day=start.day,
            details__contains=package.tenant.name,
        ).order_by('-unit_price').first()

        daily_price = package.template.price
        product_code = package.template.product_code
        article_code = package.template.article_code
        if overlapping_item:
            """
            Notes:
            |- date -| - used during the date
            |- **** -| - used during the day
            |- ---- -| - was requested to use in the current day but will be moved to next or previous one.
            |-***?---| - was used for a half day and '?' stands for a conflict.

            If there is an item that overlaps with current one as shown below:
            |--03.01.2017-|-********-|-***?---|
                                     |----?**-|-06.01.2017-|-******-|
            we have to make next steps:
            1) If item is more expensive -> use it for price calculation
                and register new package starting from next day [-06.01.2017-]
            |--03.01.2017-|-********-|-*****-|
                                     |-------|-06.01.2017-|-******-|

            2) If old package item is more expensive and it is the end of the month
            extend package usage till the end of the day and set current package end date to start date,
            so that usage days is 0 but it is still registered in the invoice.
            |--29.01.2017-|-********-|-***31.01.2017***-|
                                     |----31.01.2017----|

            3) If item is cheaper do exactly the opposite and shift its end date to yesterday,
            so new package will be registered today
            |--03.01.2017-|-********-|-------|
                                     |-*****-|-06.01.2017-|-******-|
            """
            if overlapping_item.unit_price > daily_price:
                if overlapping_item.end.day == invoices_utils.get_current_month_end().day:
                    utils.extend_to_the_end_of_the_day(overlapping_item)
                    end = start
                else:
                    start = start + timezone.timedelta(days=1)
            else:
                utils.shift_backward(overlapping_item)

        invoices_models.GenericInvoiceItem.objects.create(
            scope=package,
            project=_get_project(package),
            unit_price=daily_price,
            unit=invoices_models.GenericInvoiceItem.Units.PER_DAY,
            product_code=product_code,
            article_code=article_code,
            invoice=invoice,
            start=start,
            end=end,
            details=self.get_details(package))
示例#3
0
def calculate_usage_for_current_month():
    start = invoice_utils.get_current_month_start()
    end = invoice_utils.get_current_month_end()
    scopes = []

    for customer in structure_models.Customer.objects.all():
        scopes.append(customer)
        for project in customer.projects.all():
            scopes.append(project)

    for scope in scopes:
        calculate_usage_for_scope(start, end, scope)
示例#4
0
 def create_or_update_component_item(cls, source, invoice, component_type, quantity):
     if invoice_models.InvoiceItem.objects.filter(
         resource=source,
         details__offering_component_type=component_type,
         invoice=invoice,
     ).exists():
         cls.update_component_item(source, component_type, invoice, quantity)
     else:
         start = timezone.now()
         end = get_current_month_end()
         plan_component = source.plan.components.get(component__type=component_type)
         cls.create_component_item(source, plan_component, invoice, start, end)
示例#5
0
    def test_invoice_item_usages_reset_for_new_month(self):
        self.create_package()
        with freeze_time('2020-05-31'):
            self.update_usage()
            invoice_items = self.get_invoice_items()
            self.assertEqual(len(invoice_items), self.get_component_number())

        with freeze_time('2020-06-01'):
            invoice_items = self.get_invoice_items().filter(
                start=invoices_utils.get_current_month_start(),
                end=invoices_utils.get_current_month_end(),
            )
            self.assertEqual(len(invoice_items), 0)  # Because new month starts
示例#6
0
 def create_invoice_item_for_total_limit(
     cls, resource, invoice, component_type, new_quantity, offering_component
 ):
     if resource.state != ResourceStates.OK:
         return
     related_invoice_items = invoice_models.InvoiceItem.objects.filter(
         resource=resource, details__offering_component_type=component_type,
     )
     if not related_invoice_items.exists():
         cls.create_or_update_component_item(
             resource, invoice, component_type, new_quantity
         )
     else:
         total = 0
         for invoice_item in related_invoice_items:
             if invoice_item.unit_price < 0:
                 total -= invoice_item.quantity
             else:
                 total += invoice_item.quantity
         diff = new_quantity - total
         if diff == 0:
             return
         plan_component = resource.plan.components.get(
             component__type=component_type
         )
         details = cls.get_component_details(resource, plan_component)
         start = timezone.now()
         end = get_current_month_end()
         invoice_models.InvoiceItem.objects.create(
             name=f'{RegistrationManager.get_name(resource)} / {cls.get_component_name(plan_component)}',
             resource=resource,
             project=Project.all_objects.get(id=resource.project_id),
             unit_price=plan_component.price if diff > 0 else -plan_component.price,
             unit=invoice_models.Units.QUANTITY,
             quantity=diff if diff > 0 else -diff,
             article_code=offering_component.article_code
             or resource.plan.article_code,
             invoice=invoice,
             start=start,
             end=end,
             details=details,
             measured_unit=offering_component.measured_unit,
         )
示例#7
0
 def update_component_item(cls, source, component_type, invoice, new_quantity):
     invoice_item = invoice_models.InvoiceItem.objects.get(
         resource=source,
         details__offering_component_type=component_type,
         invoice=invoice,
     )
     resource_limit_periods = invoice_item.details['resource_limit_periods']
     old_period = resource_limit_periods.pop()
     old_quantity = int(old_period['quantity'])
     old_start = parse_datetime(old_period['start'])
     today = timezone.now()
     new_quantity = cls.convert_quantity(new_quantity, component_type)
     if old_quantity == new_quantity:
         # Skip update if limit is the same
         return
     if old_quantity > new_quantity:
         old_end = today.replace(hour=23, minute=59, second=59)
         new_start = old_end + timedelta(seconds=1)
     else:
         new_start = today.replace(hour=0, minute=0, second=0)
         old_end = new_start - timedelta(seconds=1)
     old_period = utils.serialize_resource_limit_period(
         {'start': old_start, 'end': old_end, 'quantity': old_quantity}
     )
     new_period = utils.serialize_resource_limit_period(
         {
             'start': new_start,
             'end': get_current_month_end(),
             'quantity': new_quantity,
         }
     )
     resource_limit_periods.extend([old_period, new_period])
     plan_component = source.plan.components.get(component__type=component_type)
     invoice_item.quantity = sum(
         cls.get_total_quantity(
             plan_component.plan.unit,
             period['quantity'],
             parse_datetime(period['start']),
             parse_datetime(period['end']),
         )
         for period in resource_limit_periods
     )
     invoice_item.save(update_fields=['details', 'quantity'])