def create_usage(cluster): try: resource = marketplace_models.Resource.objects.get(scope=cluster) except django_exceptions.ObjectDoesNotExist: logger.debug( 'Skipping node usage synchronization because this ' 'marketplace.Resource does not exist.' 'Cluster ID: %s', cluster.id, ) return date = datetime.date.today() usage = cluster.node_set.filter( state=core_models.StateMixin.States.OK).count() for component in manager.get_components(PLUGIN_NAME): try: offering_component = marketplace_models.OfferingComponent.objects.get( offering=resource.offering, type=component.type) plan_period = (marketplace_models.ResourcePlanPeriod.objects. filter(Q(start__lte=date) | Q(start__isnull=True)). filter(Q(end__gt=date) | Q(end__isnull=True)).get( resource=resource)) try: component_usage = marketplace_models.ComponentUsage.objects.get( resource=resource, component=offering_component, billing_period=month_start(date), plan_period=plan_period, ) component_usage.usage = max(usage, component_usage.usage) component_usage.save() except django_exceptions.ObjectDoesNotExist: marketplace_models.ComponentUsage.objects.create( resource=resource, component=offering_component, usage=usage, date=date, billing_period=month_start(date), plan_period=plan_period, ) except marketplace_models.OfferingComponent.DoesNotExist: logger.warning( 'Skipping node usage synchronization because this ' 'marketplace.OfferingComponent does not exist.' 'Cluster ID: %s', cluster.id, ) except marketplace_models.ResourcePlanPeriod.DoesNotExist: logger.warning( 'Skipping node usage synchronization because this ' 'marketplace.ResourcePlanPeriod does not exist.' 'Cluster ID: %s', cluster.id, )
def validate(self, attrs): resource = attrs['resource'] date = attrs['date'] plan = resource.plan if resource.state == models.Resource.States.TERMINATED: raise rf_exceptions.ValidationError({ 'resource': _('Resource is terminated.') }) if not plan: raise rf_exceptions.ValidationError({ 'resource': _('Resource does not have billing plan.') }) if date > datetime.date.today(): raise rf_exceptions.ValidationError({'date': _('Invalid date value.')}) if invoices_models.Invoice.objects.filter(customer=resource.project.customer, year=date.year, month=date.month). \ filter(state__in=[invoices_models.Invoice.States.CREATED, invoices_models.Invoice.States.PAID]).exists(): # If an invoice exists, and invoice state is created or paid then a billing period is closed. raise rf_exceptions.ValidationError({ 'date': _('Cannot update usage information. Billing period is closed.') }) if plan.unit == UnitPriceMixin.Units.PER_MONTH: attrs['date'] = core_utils.month_start(date) if plan.unit == UnitPriceMixin.Units.PER_HALF_MONTH: if date.day < 16: attrs['date'] = core_utils.month_start(date) else: attrs['date'] = datetime.date(year=date.year, month=date.month, day=16) for usage in attrs['usages']: component_type = usage['type'] offering = resource.plan.offering try: component = models.OfferingComponent.objects.get( offering=offering, type=component_type, billing_type=models.OfferingComponent.BillingTypes.USAGE, ) usage['component'] = component except models.OfferingComponent.DoesNotExist: raise rf_exceptions.ValidationError(_('Component "%s" is not found.') % component_type) return attrs
def create_monthly_invoices(): """ - For every customer change state of the invoices for previous months from "pending" to "billed" and freeze their items. - Create new invoice for every customer in current month if not created yet. """ date = timezone.now() old_invoices = models.Invoice.objects.filter( Q(state=models.Invoice.States.PENDING, year__lt=date.year) | Q(state=models.Invoice.States.PENDING, year=date.year, month__lt=date.month) ) for invoice in old_invoices: invoice.set_created() customers = structure_models.Customer.objects.all() if settings.WALDUR_CORE['ENABLE_ACCOUNTING_START_DATE']: customers = customers.filter(accounting_start_date__lt=timezone.now()) for customer in customers.iterator(): registrators.RegistrationManager.get_or_create_invoice( customer, core_utils.month_start(date) ) if settings.WALDUR_INVOICES['INVOICE_REPORTING']['ENABLE']: send_invoice_report.delay() if settings.WALDUR_INVOICES['SEND_CUSTOMER_INVOICES']: chain(create_pdf_for_new_invoices.si(), send_new_invoices_notification.si())() else: create_pdf_for_new_invoices.delay()
def test_handler(self): component = factories.OfferingComponentFactory( offering=self.resource.offering, billing_type=models.OfferingComponent.BillingTypes.USAGE, type='storage', ) self._create_item() usage = factories.ComponentUsageFactory( resource=self.resource, billing_period=core_utils.month_start(timezone.now()), component=component, ) self.client.force_authenticate(self.fixture.staff) result = self.client.get(self.url, { 'start': '2020-03', 'end': '2020-03' }) self.assertEqual( result.data, [{ 'period': '2020-03', 'components': { 'cpu': 1, usage.component.type: usage.usage }, }], )
def test_event_log_is_created_if_component_usage_has_been_created( self, mock_logger): self.client.force_authenticate(self.fixture.staff) usage_data = self.get_usage_data() response = self.client.post( '/api/marketplace-component-usages/set_usage/', usage_data) self.assertEqual(response.status_code, status.HTTP_201_CREATED) mock_logger.info.assert_called_with( 'Usage has been created for %s, component: ram, value: 5' % self.resource) date = timezone.now() billing_period = core_utils.month_start(date) component_usage = models.ComponentUsage.objects.get( resource=self.resource, component=self.offering_component, date=date, billing_period=billing_period, ) logging_models.Event.objects.get( message='Marketplace component usage %s has been created.' % component_usage.uuid) usage_data['usages'][0]['amount'] = 8 self.client.post('/api/marketplace-component-usages/set_usage/', usage_data) logging_models.Event.objects.get( message='Marketplace component usage %s has been updated.' % component_usage.uuid)
def test_event_log_creates_if_component_usage_has_been_created( self, mock_logger): self.client.force_authenticate(self.fixture.staff) usage_data = self.get_usage_data() response = self.client.post( '/api/marketplace-component-usages/set_usage/', usage_data) self.assertEqual(response.status_code, status.HTTP_201_CREATED) mock_logger.info.assert_called_once_with( 'Usage has been created. Data: {\'plan_period\': \'%s\',' ' \'usages\': [{\'type\': \'cpu\', \'amount\': 5, \'description\': \'\'}]}.' % self.plan_period.uuid.hex) date = timezone.now() billing_period = core_utils.month_start(date) component_usage = models.ComponentUsage.objects.get( resource=self.resource, component=self.offering_component, date=date, billing_period=billing_period, ) logging_models.Event.objects.get( message='Marketplace component usage %s has been created.' % component_usage.uuid) usage_data['usages'][0]['amount'] = 8 self.client.post('/api/marketplace-component-usages/set_usage/', usage_data) logging_models.Event.objects.get( message='Marketplace component usage %s has been updated.' % component_usage.uuid)
class ResourcePlanPeriodFactory(factory.DjangoModelFactory): class Meta: model = models.ResourcePlanPeriod resource = factory.SubFactory(ResourceFactory) plan = factory.SubFactory(PlanFactory) start = core_utils.month_start(timezone.now())
def component_usage_register(component_usage): from waldur_mastermind.slurm_invoices.registrators import AllocationRegistrator plan_period = component_usage.plan_period if not plan_period: logger.warning( 'Skipping processing of component usage with ID %s because ' 'plan period is not defined.', component_usage.id, ) return try: plan = plan_period.plan plan_component = plan.components.get(component=component_usage.component) allocation = component_usage.resource.scope customer = allocation.project.customer invoice, created = registrators.RegistrationManager.get_or_create_invoice( customer, component_usage.date ) details = AllocationRegistrator().get_component_details( allocation, plan_component ) details['plan_period_id'] = plan_period.id offering_component = plan_component.component month_start = core_utils.month_start(component_usage.date) month_end = core_utils.month_end(component_usage.date) start = ( month_start if not component_usage.plan_period.start else max(component_usage.plan_period.start, month_start) ) end = ( month_end if not component_usage.plan_period.end else min(component_usage.plan_period.end, month_end) ) invoice_models.InvoiceItem.objects.create( content_type=ContentType.objects.get_for_model(allocation), object_id=allocation.id, project=allocation.project, invoice=invoice, start=start, end=end, details=details, unit_price=plan_component.price, quantity=component_usage.usage, unit=common_mixins.UnitPriceMixin.Units.QUANTITY, product_code=offering_component.product_code or plan.product_code, article_code=offering_component.article_code or plan.article_code, ) except marketplace_models.PlanComponent.DoesNotExist: logger.warning( 'Plan component for usage component %s is not found.', component_usage.id )
def test_set_recurring_to_false_for_other_usages_in_this_period(self): self.client.force_authenticate(self.fixture.staff) payload = self.get_usage_data() payload['usages'][0]['recurring'] = True response = self.client.post( '/api/marketplace-component-usages/set_usage/', payload) self.assertEqual(response.status_code, status.HTTP_201_CREATED) date = timezone.now() billing_period = core_utils.month_start(date) usage = models.ComponentUsage.objects.get( resource=self.resource, component=self.offering_component, date=date, billing_period=billing_period, ) self.assertTrue(usage.recurring) new_plan_period = models.ResourcePlanPeriod.objects.create( resource=self.plan_period.resource, plan=self.plan_period.plan, ) self.plan_period = new_plan_period payload = self.get_usage_data() response = self.client.post( '/api/marketplace-component-usages/set_usage/', payload) self.assertEqual(response.status_code, status.HTTP_201_CREATED) usage.refresh_from_db() self.assertFalse(usage.recurring)
def get_info_about_missing_usage_reports(): now = timezone.now() billing_period = core_utils.month_start(now) offering_ids = models.OfferingComponent.objects.filter( billing_type=models.OfferingComponent.BillingTypes.USAGE ).values_list('offering_id', flat=True) resource_with_usages = models.ComponentUsage.objects.filter( billing_period=billing_period ).values_list('resource', flat=True) resources_without_usages = models.Resource.objects.filter( state=models.Resource.States.OK, offering_id__in=offering_ids ).exclude(id__in=resource_with_usages) result = [] for resource in resources_without_usages: rows = list( filter(lambda x: x['customer'] == resource.offering.customer, result) ) if rows: rows[0]['resources'].append(resource) else: result.append( {'customer': resource.offering.customer, 'resources': [resource],} ) return result
def test_item_details(self): component = factories.OfferingComponentFactory( offering=self.resource.offering, billing_type=models.OfferingComponent.BillingTypes.USAGE, type='storage', ) usage = factories.ComponentUsageFactory( resource=self.resource, billing_period=core_utils.month_start(timezone.now()), component=component, ) item = self._create_item() self.assertEqual( item.details, { 'limits': self.resource.limits, 'usages': { usage.component.type: usage.usage }, 'scope_uuid': item.scope.uuid.hex, 'offering_name': self.offering.name, 'offering_type': PACKAGE_TYPE, 'offering_uuid': self.offering.uuid.hex, }, )
def create_recurring_usage_if_invoice_has_been_created( sender, instance, created=False, **kwargs ): if not created: return invoice = instance now = timezone.now() prev_month = (now.replace(day=1) - datetime.timedelta(days=1)).date() prev_month_start = prev_month.replace(day=1) usages = marketplace_models.ComponentUsage.objects.filter( resource__project__customer=invoice.customer, recurring=True, billing_period__gte=prev_month_start, ).exclude(resource__state=marketplace_models.Resource.States.TERMINATED) if not usages: return for usage in usages: marketplace_models.ComponentUsage.objects.create( resource=usage.resource, component=usage.component, usage=usage.usage, description=usage.description, date=now, plan_period=usage.plan_period, recurring=usage.recurring, billing_period=core_utils.month_start(now), )
def on_resource_post_save(cls, sender, instance, created=False, **kwargs): resource = instance if resource.offering.type != cls.plugin_name: return if created: return if ( resource.state == ResourceStates.OK and resource.tracker.previous('state') == ResourceStates.CREATING ): registrators.RegistrationManager.register( resource, timezone.now(), order_type=OrderTypes.CREATE ) if ( resource.state == ResourceStates.TERMINATED and instance.tracker.previous('state') == ResourceStates.TERMINATING ): registrators.RegistrationManager.terminate(resource, timezone.now()) if resource.tracker.has_changed('plan_id'): registrators.RegistrationManager.terminate(resource, timezone.now()) registrators.RegistrationManager.register( resource, timezone.now(), order_type=OrderTypes.UPDATE ) if resource.tracker.has_changed('limits'): today = timezone.now() invoice, _ = registrators.RegistrationManager.get_or_create_invoice( resource.project.customer, core_utils.month_start(today) ) valid_limits = set( resource.offering.components.filter( billing_type=BillingTypes.LIMIT ).values_list('type', flat=True) ) for component_type, new_quantity in resource.limits.items(): if component_type not in valid_limits: continue offering_component = resource.offering.components.get( type=component_type ) if ( offering_component.billing_type == BillingTypes.LIMIT and offering_component.limit_period == LimitPeriods.TOTAL ): cls.create_invoice_item_for_total_limit( resource, invoice, component_type, new_quantity, offering_component, ) else: cls.create_or_update_component_item( resource, invoice, component_type, new_quantity )
def component_usage_register(component_usage): from waldur_mastermind.support_invoices.registrators import OfferingRegistrator plan_period = component_usage.plan_period if not plan_period: logger.warning('Skipping processing of component usage with ID %s because ' 'plan period is not defined.', component_usage.id) return plan = plan_period.plan try: plan_component = plan.components.get(component=component_usage.component) item = invoice_models.InvoiceItem.objects.get(scope=component_usage.resource.scope, details__plan_period_id=plan_period.id, details__plan_component_id=plan_component.id) item.quantity = component_usage.usage item.unit_price = plan_component.price item.save() except invoice_models.InvoiceItem.DoesNotExist: offering = component_usage.resource.scope customer = offering.project.customer invoice, created = registrators.RegistrationManager.get_or_create_invoice(customer, component_usage.date) details = OfferingRegistrator().get_component_details(offering, plan_component) details['plan_period_id'] = plan_period.id offering_component = plan_component.component month_start = core_utils.month_start(component_usage.date) month_end = core_utils.month_end(component_usage.date) start = month_start if not component_usage.plan_period.start else \ max(component_usage.plan_period.start, month_start) end = month_end if not component_usage.plan_period.end else \ min(component_usage.plan_period.end, month_end) invoice_models.InvoiceItem.objects.create( content_type=ContentType.objects.get_for_model(offering), object_id=offering.id, project=offering.project, invoice=invoice, start=start, end=end, details=details, unit_price=plan_component.price, quantity=component_usage.usage, unit=common_mixins.UnitPriceMixin.Units.QUANTITY, product_code=offering_component.product_code or plan.product_code, article_code=offering_component.article_code or plan.article_code, ) except marketplace_models.PlanComponent.DoesNotExist: logger.warning('Plan component for usage component %s is not found.', component_usage.id) except invoice_models.InvoiceItem.MultipleObjectsReturned: logger.warning('Skipping the invoice item unit price update ' 'because multiple GenericInvoiceItem objects found. Scope: %s %s, date: %s.', component_usage.resource.content_type, component_usage.resource.object_id, component_usage.date)
class ComponentUsageFactory(factory.DjangoModelFactory): class Meta: model = models.ComponentUsage resource = factory.SubFactory(ResourceFactory) component = factory.SubFactory(OfferingComponentFactory) usage = 1 date = timezone.now() billing_period = core_utils.month_start(timezone.now())
def update_component_quota(sender, instance, created=False, **kwargs): if created: return if not set(instance.tracker.changed()) & COMPONENT_FIELDS: return allocation = instance try: resource = marketplace_models.Resource.objects.get(scope=allocation) except django_exceptions.ObjectDoesNotExist: return for component in manager.get_components(PLUGIN_NAME): usage = getattr(allocation, component.type + '_usage') limit = getattr(allocation, component.type + '_limit') try: offering_component = marketplace_models.OfferingComponent.objects.get( offering=resource.offering, type=component.type ) except marketplace_models.OfferingComponent.DoesNotExist: logger.warning( 'Skipping Allocation synchronization because this ' 'marketplace.OfferingComponent does not exist.' 'Allocation ID: %s', allocation.id, ) else: marketplace_models.ComponentQuota.objects.update_or_create( resource=resource, component=offering_component, defaults={'limit': limit, 'usage': usage}, ) try: plan_period = marketplace_models.ResourcePlanPeriod.objects.get( resource=resource, end=None ) except (ObjectDoesNotExist, MultipleObjectsReturned): logger.warning( 'Skipping component usage synchronization because valid' 'ResourcePlanPeriod is not found.' 'Allocation ID: %s', allocation.id, ) else: date = timezone.now() marketplace_models.ComponentUsage.objects.update_or_create( resource=resource, component=offering_component, billing_period=month_start(date), plan_period=plan_period, defaults={'usage': usage, 'date': date}, )
def test_create_usage(self): response = self.submit_usage() self.assertEqual(response.status_code, status.HTTP_201_CREATED) date = timezone.now() billing_period = core_utils.month_start(date) self.assertTrue( models.ComponentUsage.objects.filter( resource=self.resource, component=self.offering_component, date=date, billing_period=billing_period).exists())
def _create_historical_estimates(resource, configuration): """ Create consumption details and price estimates for past months. Usually we need to update historical values on resource import. """ today = timezone.now() month_start = core_utils.month_start(today) while month_start > resource.created: month_start -= relativedelta(months=1) models.PriceEstimate.create_historical( resource, configuration, max(month_start, resource.created))
def shift_backward(self): """ Adjust old invoice item end field to the end of previous unit Adjust new invoice item field to the start of current unit. """ end = self.old_item.end if self.old_item.unit != self.unit and self.unit == Units.PER_MONTH: start = core_utils.month_start(end) elif self.old_item.unit != self.unit and self.unit == Units.PER_HALF_MONTH: if end.day < 15: start = core_utils.month_start(end) else: start = end.replace(day=15) elif self.unit == Units.PER_HOUR: start = end.replace(minute=0, second=0) else: start = end.replace(hour=0, minute=0, second=0) end = start - timedelta(seconds=1) return start, end
def test_total_amount_exceeds_total_limit(self): self.offering_component.limit_period = models.OfferingComponent.LimitPeriods.TOTAL self.offering_component.limit_amount = 7 self.offering_component.save() models.ComponentUsage.objects.create( resource=self.resource, component=self.offering_component, date=core_utils.month_start(datetime.date.today()), usage=5, ) response = self.submit_usage() self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
def handle(self, *args, **options): today = timezone.now() with transaction.atomic(): # Delete current month price estimates models.PriceEstimate.objects.filter(month=today.month, year=today.year).delete() # Create new estimates for resources and ancestors for resource_model in CostTrackingRegister.registered_resources: for resource in resource_model.objects.all(): configuration = CostTrackingRegister.get_configuration(resource) date = max(core_utils.month_start(today), resource.created) models.PriceEstimate.create_historical(resource, configuration, date) # recalculate consumed estimate tasks.recalculate_estimate()
def test_invoice_price_includes_usage_components(self): invoice = invoices_models.Invoice.objects.get(customer=self.allocation.service_project_link.project.customer) self.assertEqual(invoice.price, 0) marketplace_models.ComponentUsage.objects.create( resource=self.resource, component=self.resource.plan.components.first().component, usage=1, date=datetime.date.today(), billing_period=month_start(datetime.date.today()), plan_period=self.plan_period ) invoice.refresh_from_db() self.assertEqual(invoice.price, 3)
def test_item_details(self): sp = factories.ServiceProviderFactory( customer=self.resource.offering.customer) component = factories.OfferingComponentFactory( offering=self.resource.offering, billing_type=models.OfferingComponent.BillingTypes.LIMIT, type='storage', ) factories.ComponentUsageFactory( resource=self.resource, billing_period=core_utils.month_start(timezone.now()), component=component, ) item = self._create_items().first() self.assertDictEqual( item.details, { 'resource_name': item.resource.name, 'resource_uuid': item.resource.uuid.hex, 'service_provider_name': self.resource.offering.customer.name, 'service_provider_uuid': sp.uuid.hex, 'offering_name': self.offering.name, 'offering_type': TENANT_TYPE, 'offering_uuid': self.offering.uuid.hex, 'plan_name': self.resource.plan.name, 'plan_uuid': self.resource.plan.uuid.hex, 'plan_component_id': self.plan_component.id, 'offering_component_type': self.plan_component.component.type, 'offering_component_name': self.plan_component.component.name, 'resource_limit_periods': [{ 'end': '2020-03-31T23:59:59.999999+00:00', 'start': '2020-03-01T00:00:00+00:00', 'total': '31', 'quantity': 1, 'billing_periods': 31, }], }, )
def test_authenticated_user_can_submit_usage_via_api(self, role): self.client.force_authenticate(getattr(self.fixture, role)) response = self.client.post( '/api/marketplace-component-usages/set_usage/', self.get_usage_data()) self.assertEqual(response.status_code, status.HTTP_201_CREATED) date = timezone.now() billing_period = core_utils.month_start(date) self.assertTrue( models.ComponentUsage.objects.filter( resource=self.resource, component=self.offering_component, date=date, billing_period=billing_period).exists())
def _create_usage(self, date=None, **kwargs): date = date or datetime.date.today() plan_period = marketplace_models.ResourcePlanPeriod.objects. \ filter(Q(start__lte=date) | Q(start__isnull=True)). \ filter(Q(end__gt=date) | Q(end__isnull=True)). \ get(resource=self.resource) option = dict(resource=self.resource, component=self.fixture.offering_component_cpu, usage=10, date=date, billing_period=core_utils.month_start(date), plan_period=plan_period) option.update(kwargs) return marketplace_models.ComponentUsage.objects.create(**option)
def test_plan_period_linking(self): response = self.submit_usage() self.assertEqual(response.status_code, status.HTTP_201_CREATED) date = timezone.now() billing_period = core_utils.month_start(date) usage = models.ComponentUsage.objects.get( resource=self.resource, component=self.offering_component, date=date, billing_period=billing_period) plan_period = models.ResourcePlanPeriod.objects.get( resource=self.resource, start=datetime.date(2017, 1, 10), end__isnull=True) self.assertEqual(usage.plan_period, plan_period)
def validate_amount(self, resource, amount, date): if not self.limit_period or not self.limit_amount: return usages = ComponentUsage.objects.filter(resource=resource, component=self) if self.limit_period == OfferingComponent.LimitPeriods.MONTH: usages = usages.filter(date=core_utils.month_start(date)) total = usages.aggregate(models.Sum('usage'))['usage__sum'] or 0 if total + amount > self.limit_amount: raise rf_exceptions.ValidationError( _('Total amount exceeds exceeds limit. Total amount: %s, limit: %s.' ) % (total + amount, self.limit_amount))
def create(self, price_estimate): """ Take configuration from previous month, it it exists. Set last_update_time equals to the beginning of the month. """ kwargs = {} try: previous_price_estimate = price_estimate.get_previous() except ObjectDoesNotExist: pass else: configuration = previous_price_estimate.consumption_details.configuration kwargs['configuration'] = configuration month_start = core_utils.month_start( datetime.date(price_estimate.year, price_estimate.month, 1)) kwargs['last_update_time'] = month_start return super(ConsumptionDetailsQuerySet, self).create(price_estimate=price_estimate, **kwargs)
def get_common_data(self, start=None, end=None, quantity=100): now = timezone.now() if start is None: start = month_start(now) if end is None: end = month_end(now) return { 'unit': 'sample-unit', 'name': 'Fake invoice item', 'measured_unit': 'sample-m-unit', 'article_code': '', 'unit_price': 2.0, 'details': {}, 'quantity': quantity, 'start': start.isoformat(), 'end': end.isoformat(), }
def _create_slurm_usage(instance): allocation_usage = instance allocation = allocation_usage.allocation try: resource = marketplace_models.Resource.objects.get(scope=allocation) except django_exceptions.ObjectDoesNotExist: return date = datetime.date(year=allocation_usage.year, month=allocation_usage.month, day=1) for component in manager.get_components(PLUGIN_NAME): usage = getattr(allocation_usage, component.type + '_usage') try: plan_component = marketplace_models.OfferingComponent.objects.get( offering=resource.offering, type=component.type) plan_period = (marketplace_models.ResourcePlanPeriod.objects. filter(Q(start__lte=date) | Q(start__isnull=True)). filter(Q(end__gt=date) | Q(end__isnull=True)).get( resource=resource)) marketplace_models.ComponentUsage.objects.create( resource=resource, component=plan_component, usage=usage, date=date, billing_period=month_start(date), plan_period=plan_period, ) except django_exceptions.ObjectDoesNotExist: logger.warning( 'Skipping AllocationUsage synchronization because this ' 'marketplace.OfferingComponent does not exist.' 'AllocationUsage ID: %s', allocation_usage.id, ) except IntegrityError: logger.warning( 'Skipping AllocationUsage synchronization because this marketplace.ComponentUsage exists.' 'AllocationUsage ID: %s', allocation_usage.id, exc_info=True, )