def test_update_company_from_dnb_partial_fields_multiple( self, formatted_dnb_company, adviser_callable, ): """ Test that update_company_from_dnb can update a subset of fields. """ duns_number = '123456789' company = CompanyFactory(duns_number=duns_number) original_company = Company.objects.get(id=company.id) adviser = adviser_callable() update_company_from_dnb( company, formatted_dnb_company, adviser, fields_to_update=['name', 'address'], ) company.refresh_from_db() assert company.global_ultimate_duns_number == original_company.global_ultimate_duns_number assert company.number_of_employees == original_company.number_of_employees assert company.name == formatted_dnb_company['name'] assert company.address_1 == formatted_dnb_company['address']['line_1'] assert company.address_2 == formatted_dnb_company['address']['line_2'] assert company.address_town == formatted_dnb_company['address']['town'] assert company.address_county == formatted_dnb_company['address'][ 'county'] assert company.address_postcode == formatted_dnb_company['address'][ 'postcode']
def test_rollback( self, formatted_dnb_company, fields, expected_fields, ): """ Test that rollback_dnb_company_update will roll back all DNB fields. """ with reversion.create_revision(): company = CompanyFactory( duns_number=formatted_dnb_company['duns_number']) original_company = Company.objects.get(id=company.id) update_company_from_dnb( company, formatted_dnb_company, update_descriptor='foo', ) rollback_dnb_company_update(company, 'foo', fields_to_update=fields) company.refresh_from_db() for field in expected_fields: assert getattr(company, field) == getattr(original_company, field) latest_version = Version.objects.get_for_object(company)[0] assert latest_version.revision.comment == 'Reverted D&B update from: foo'
def update_company_from_dnb_data(dnb_company_data, fields_to_update=None, update_descriptor=None): """ Update the company with the latest data from dnb-service. This task should be called when some other logic interacts with dnb-service to get the company data as the task itself will not interact with dnb-service. """ dnb_company = format_dnb_company(dnb_company_data) duns_number = dnb_company['duns_number'] logger.info(f'Updating company with duns_number: {duns_number}') try: dh_company = Company.objects.get(duns_number=duns_number) except Company.DoesNotExist: logger.error( 'Company matching duns_number was not found', extra={ 'duns_number': duns_number, 'dnb_company': dnb_company, }, ) raise if not update_descriptor: update_descriptor = 'celery:company_update' update_company_from_dnb( dh_company, dnb_company, fields_to_update=fields_to_update, update_descriptor=update_descriptor, ) return str(dh_company.pk)
def test_update_company_from_dnb_partial_fields_single( self, formatted_dnb_company, adviser_callable, ): """ Test that update_company_from_dnb can update a subset of fields. """ duns_number = '123456789' company = CompanyFactory(duns_number=duns_number) original_company = Company.objects.get(id=company.id) adviser = adviser_callable() update_company_from_dnb( company, formatted_dnb_company, adviser, fields_to_update=['global_ultimate_duns_number'], ) company.refresh_from_db() dnb_ultimate_duns = formatted_dnb_company[ 'global_ultimate_duns_number'] assert company.global_ultimate_duns_number == dnb_ultimate_duns assert company.name == original_company.name assert company.number_of_employees == original_company.number_of_employees
def update_from_dnb(model_admin, request, object_id): """ Tool to let admin users update company with a valid `duns_number` by pulling fresh data from D&B. The company record will be versioned after the update from D&B is applied. The `pending_dnb_investigation` field will be set to False. """ if not model_admin.has_change_permission(request): raise PermissionDenied() dh_company = model_admin.get_object(request, object_id) company_change_page = reverse( admin_urlname(model_admin.model._meta, 'change'), kwargs={'object_id': dh_company.pk}, ) if dh_company is None or dh_company.duns_number is None: raise SuspiciousOperation() try: dnb_company = get_company(dh_company.duns_number, request) except DNBServiceInvalidRequestError: message = 'No matching company found in D&B database.' raise AdminError([message], company_change_page) except DNBServiceBaseError: message = 'Something went wrong in an upstream service.' raise AdminError([message], company_change_page) if request.method == 'GET': return TemplateResponse( request, 'admin/company/company/update-from-dnb.html', { **model_admin.admin_site.each_context(request), 'media': model_admin.media, 'opts': model_admin.model._meta, 'object': dh_company, 'title': gettext_lazy('Confirm update from D&B'), 'diff': format_company_diff(dh_company, dnb_company), }, ) try: update_company_from_dnb(dh_company, dnb_company, request.user) return HttpResponseRedirect(company_change_page) except serializers.ValidationError: message = 'Data from D&B did not pass the Data Hub validation checks.' raise AdminError([message], company_change_page)
def _sync_company_with_dnb( company_id, fields_to_update, task, update_descriptor, retry_failures=True, ): dh_company = Company.objects.get(id=company_id) try: dnb_company = get_company(dh_company.duns_number) except DNBServiceError as exc: if is_server_error(exc.status_code) and retry_failures: raise task.retry(exc=exc, countdown=60) raise except (DNBServiceConnectionError, DNBServiceTimeoutError) as exc: if retry_failures: raise task.retry(exc=exc, countdown=60) raise update_company_from_dnb( dh_company, dnb_company, fields_to_update=fields_to_update, update_descriptor=update_descriptor, )
def test_post_dnb_data_invalid( self, formatted_dnb_company, ): """ Tests that ValidationError is raised when data returned by DNB is not valid for saving to a Data Hub Company. """ company = CompanyFactory(duns_number='123456789') adviser = AdviserFactory() formatted_dnb_company['name'] = None with pytest.raises(serializers.ValidationError) as excinfo: update_company_from_dnb(company, formatted_dnb_company, adviser) assert str(excinfo) == 'Data from D&B did not pass the Data Hub validation checks.'
def test_rollback_error( self, formatted_dnb_company, update_comment, error_message, ): """ Test that rollback_dnb_company_update will fail with the given error message when there is an issue in finding the version to revert to. """ company = CompanyFactory(duns_number=formatted_dnb_company['duns_number']) update_company_from_dnb( company, formatted_dnb_company, update_descriptor='foo', ) with pytest.raises(RevisionNotFoundError) as excinfo: rollback_dnb_company_update(company, update_comment) assert str(excinfo.value) == error_message
def test_run(s3_stubber, formatted_dnb_company): """ Test that the command successfully rolls back the specified records. """ with reversion.create_revision(): companies = [ CompanyFactory(duns_number='123456789'), CompanyFactory(duns_number='223456789'), ] original_companies = { company.id: Company.objects.get(id=company.id) for company in companies } update_descriptor = 'foobar' # Simulate updating the companies from DNB, which sets a revision with the specified # update descriptor for company in companies: formatted_dnb_company['duns_number'] = company.duns_number update_company_from_dnb( company, formatted_dnb_company, update_descriptor=update_descriptor, ) bucket = 'test_bucket' object_key = 'test_key' csv_content = f"""id 00000000-0000-0000-0000-000000000000 {companies[0].id} {companies[1].id} """ s3_stubber.add_response( 'get_object', { 'Body': BytesIO(bytes(csv_content, encoding='utf-8')), }, expected_params={ 'Bucket': bucket, 'Key': object_key, }, ) call_command( 'rollback_dnb_company_updates', bucket, object_key, update_descriptor=update_descriptor, ) # Ensure that the companies are reverted to their previous versions for company in companies: company.refresh_from_db() for field in ALL_DNB_UPDATED_MODEL_FIELDS: assert getattr(company, field) == getattr(original_companies[company.id], field) latest_version = Version.objects.get_for_object(company)[0] assert latest_version.revision.comment == f'Reverted D&B update from: {update_descriptor}'
def test_update_company_from_dnb_all_fields( self, formatted_dnb_company, base_company_dict, adviser_callable, update_descriptor, ): """ Test that update_company_from_dnb will update all fields when the fields kwarg is not specified. """ duns_number = '123456789' company = CompanyFactory(duns_number=duns_number, pending_dnb_investigation=True) original_company = Company.objects.get(id=company.id) adviser = adviser_callable() update_company_from_dnb( company, formatted_dnb_company, user=adviser, update_descriptor=update_descriptor, ) company.refresh_from_db() uk_country = Country.objects.get(iso_alpha2_code='GB') assert model_to_dict_company(company) == { **base_company_dict, 'address_1': 'Unit 10, Ockham Drive', 'address_2': '', 'address_country': uk_country.id, 'address_county': '', 'address_postcode': 'UB6 0F2', 'address_town': 'GREENFORD', 'archived_documents_url_path': original_company.archived_documents_url_path, 'business_type': original_company.business_type.id, 'company_number': '01261539', 'created_by': original_company.created_by.id, 'duns_number': '123456789', 'employee_range': original_company.employee_range.id, 'export_experience_category': original_company.export_experience_category.id, 'global_ultimate_duns_number': '291332174', 'id': original_company.id, 'is_number_of_employees_estimated': True, 'modified_by': adviser.id if adviser else original_company.modified_by.id, 'name': 'FOO BICYCLE LIMITED', 'number_of_employees': 260, 'sector': original_company.sector.id, 'turnover': 50651895, 'turnover_range': original_company.turnover_range.id, 'uk_region': original_company.uk_region.id, 'dnb_modified_on': now(), } versions = list(Version.objects.get_for_object(company)) assert len(versions) == 1 version = versions[0] if update_descriptor: assert version.revision.comment == f'Updated from D&B [{update_descriptor}]' else: assert version.revision.comment == 'Updated from D&B' assert version.revision.user == adviser if not adviser: assert company.modified_on == original_company.modified_on