Example #1
0
    def test_notify_on_order_paid(self):
        """Test that a notification is triggered when an order is marked as paid."""
        order = OrderWithAcceptedQuoteFactory(assignees=[])
        OrderAssigneeFactory.create_batch(1, order=order)
        OrderSubscriberFactory.create_batch(2, order=order)

        notify.client.reset_mock()

        order.mark_as_paid(
            by=AdviserFactory(),
            payments_data=[
                {
                    'amount': order.total_cost,
                    'received_on': dateutil_parse('2017-01-02').date(),
                },
            ],
        )

        #  1 = customer, 3 = assignees/subscribers
        assert len(
            notify.client.send_email_notification.call_args_list) == (3 + 1)

        templates_called = [
            data[1]['template_id']
            for data in notify.client.send_email_notification.call_args_list
        ]
        assert templates_called == [
            Template.order_paid_for_customer.value,
            Template.order_paid_for_adviser.value,
            Template.order_paid_for_adviser.value,
            Template.order_paid_for_adviser.value,
        ]
Example #2
0
    def test_notify_on_order_cancelled(self):
        """Test that a notification is triggered when an order is cancelled."""
        order = OrderFactory(assignees=[])
        OrderAssigneeFactory.create_batch(1, order=order, is_lead=True)
        OrderSubscriberFactory.create_batch(2, order=order)

        notify.client.reset_mock()

        order.cancel(by=AdviserFactory(),
                     reason=CancellationReason.objects.first())

        #  1 = customer, 3 = assignees/subscribers
        assert len(
            notify.client.send_email_notification.call_args_list) == (3 + 1)

        templates_called = [
            data[1]['template_id']
            for data in notify.client.send_email_notification.call_args_list
        ]
        assert templates_called == [
            Template.order_cancelled_for_customer.value,
            Template.order_cancelled_for_adviser.value,
            Template.order_cancelled_for_adviser.value,
            Template.order_cancelled_for_adviser.value,
        ]
Example #3
0
    def test_notify_on_quote_cancelled(self, mocked_notify_client):
        """Test that a notification is triggered when a quote is cancelled."""
        order = OrderWithOpenQuoteFactory(assignees=[])
        OrderAssigneeFactory.create_batch(1, order=order)
        OrderSubscriberFactory.create_batch(2, order=order)

        mocked_notify_client.reset_mock()

        order.reopen(by=AdviserFactory())

        #  1 = customer, 3 = assignees/subscribers
        assert len(
            mocked_notify_client.send_email_notification.call_args_list) == (
                3 + 1)

        templates_called = [
            data[1]['template_id'] for data in
            mocked_notify_client.send_email_notification.call_args_list
        ]
        assert templates_called == [
            Template.quote_cancelled_for_customer.value,
            Template.quote_cancelled_for_adviser.value,
            Template.quote_cancelled_for_adviser.value,
            Template.quote_cancelled_for_adviser.value,
        ]
    def test_set_team_country_on_create(self):
        """
        Tests that when creating a new OrderAssignee, the `team` and `country`
        properties get populated automatically.
        """
        # adviser belonging to a team with a country
        team = TeamFactory(country_id=constants.Country.france.value.id)
        adviser = AdviserFactory(dit_team=team)
        assignee = OrderAssigneeFactory(adviser=adviser)

        assert assignee.team == team
        assert str(assignee.country_id) == constants.Country.france.value.id

        # adviser belonging to a team without country
        team = TeamFactory(country=None)
        adviser = AdviserFactory(dit_team=team)
        assignee = OrderAssigneeFactory(adviser=adviser)

        assert assignee.team == team
        assert not assignee.country

        # adviser not belonging to any team
        adviser = AdviserFactory(dit_team=None)
        assignee = OrderAssigneeFactory(adviser=adviser)

        assert not assignee.team
        assert not assignee.country
    def test_400_if_readonly_fields_changed(self):
        """
        Test that the `actual_time` field cannot be set when the order is in draft.
        """
        order = OrderFactory(assignees=[])
        OrderAssigneeFactory(order=order, estimated_time=100, is_lead=True)
        assignee2 = OrderAssigneeFactory(order=order,
                                         estimated_time=250,
                                         is_lead=False)

        url = reverse(
            'api-v3:omis:order:assignee',
            kwargs={'order_pk': order.id},
        )
        response = self.api_client.patch(
            url,
            [
                {
                    'adviser': {
                        'id': assignee2.adviser.id
                    },
                    'actual_time': 200,
                },
            ],
        )

        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert response.json() == [
            {
                'actual_time': [
                    'This field cannot be changed at this stage.',
                ],
            },
        ]
 def test_with_lead_assignee(self):
     """
     Test that get_lead_assignee() returns the lead assignee if present.
     """
     order = OrderFactory(assignees=[])
     lead_assignee = OrderAssigneeFactory(order=order, is_lead=True)
     OrderAssigneeFactory(order=order, is_lead=False)
     assert order.get_lead_assignee() == lead_assignee
    def test_cannot_change_adviser_after_creation(self):
        """After creating an OrderAssignee, the related adviser cannot be changed."""
        adviser = AdviserFactory()
        assignee = OrderAssigneeFactory(adviser=adviser)

        with pytest.raises(ValueError):
            assignee.adviser = AdviserFactory()
            assignee.save()
Example #8
0
def setup_data(setup_es):
    """Sets up data for the tests."""
    with freeze_time('2017-01-01 13:00:00'):
        company = CompanyFactory(name='Mercury trading', alias='Uranus supplies')
        contact = ContactFactory(company=company, first_name='John', last_name='Doe')
        order = OrderFactory(
            reference='abcd',
            primary_market_id=constants.Country.japan.value.id,
            uk_region_id=constants.UKRegion.channel_islands.value.id,
            assignees=[],
            status=OrderStatus.draft,
            company=company,
            contact=contact,
            discount_value=0,
            delivery_date=dateutil_parse('2018-01-01').date(),
            vat_verified=False,
        )
        OrderSubscriberFactory(
            order=order,
            adviser=AdviserFactory(dit_team_id=constants.Team.healthcare_uk.value.id),
        )
        OrderAssigneeFactory(
            order=order,
            adviser=AdviserFactory(dit_team_id=constants.Team.tees_valley_lep.value.id),
            estimated_time=60,
        )

    with freeze_time('2017-02-01 13:00:00'):
        company = CompanyFactory(name='Venus Ltd', alias='Earth outsourcing')
        contact = ContactFactory(company=company, first_name='Jenny', last_name='Cakeman')
        order = OrderWithAcceptedQuoteFactory(
            reference='efgh',
            primary_market_id=constants.Country.france.value.id,
            uk_region_id=constants.UKRegion.east_midlands.value.id,
            assignees=[],
            status=OrderStatus.quote_awaiting_acceptance,
            company=company,
            contact=contact,
            discount_value=0,
            delivery_date=dateutil_parse('2018-02-01').date(),
            vat_verified=False,
        )
        OrderSubscriberFactory(
            order=order,
            adviser=AdviserFactory(dit_team_id=constants.Team.td_events_healthcare.value.id),
        )
        OrderAssigneeFactory(
            order=order,
            adviser=AdviserFactory(dit_team_id=constants.Team.food_from_britain.value.id),
            estimated_time=120,
        )

        setup_es.indices.refresh()
    def test_non_empty(self):
        """
        Test that calling GET returns the list of advisers assigned to the order.
        """
        advisers = AdviserFactory.create_batch(3)
        order = OrderFactory(assignees=[])
        for i, adviser in enumerate(advisers[:2]):
            OrderAssigneeFactory(
                order=order,
                adviser=adviser,
                estimated_time=(120 * i),
            )

        url = reverse(
            'api-v3:omis:order:assignee',
            kwargs={'order_pk': order.id},
        )
        response = self.api_client.get(url)

        assert response.status_code == status.HTTP_200_OK
        assert response.json() == [{
            'adviser': {
                'id': str(adviser.id),
                'first_name': adviser.first_name,
                'last_name': adviser.last_name,
                'name': adviser.name,
            },
            'estimated_time': (120 * i),
            'actual_time': None,
            'is_lead': False,
        } for i, adviser in enumerate(advisers[:2])]
    def test_400_if_readonly_fields_changed(self, data):
        """
        Test that estimated_time and is_lead cannot be set at this stage.
        """
        order = OrderPaidFactory(assignees=[])
        assignee = OrderAssigneeFactory(order=order)

        url = reverse(
            'api-v3:omis:order:assignee',
            kwargs={'order_pk': order.id},
        )
        response = self.api_client.patch(
            url,
            [
                {
                    'adviser': {
                        'id': assignee.adviser.id
                    },
                    **data,
                },
            ],
        )

        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert response.json() == [
            {
                list(data)[0]: [
                    'This field cannot be changed at this stage.',
                ],
            },
        ]
Example #11
0
    def test_advisers_notified(self):
        """
        Test that calling `quote_cancelled` sends an email to all advisers notifying them that
        the quote has been cancelled.
        """
        order = OrderFactory(assignees=[])
        assignees = OrderAssigneeFactory.create_batch(2, order=order)
        subscribers = OrderSubscriberFactory.create_batch(2, order=order)
        canceller = AdviserFactory()

        notify.client.reset_mock()

        notify.quote_cancelled(order, by=canceller)

        assert notify.client.send_email_notification.called
        # 1 = customer, 4 = assignees/subscribers
        assert len(
            notify.client.send_email_notification.call_args_list) == (4 + 1)

        calls_by_email = {
            data['email_address']: {
                'template_id': data['template_id'],
                'personalisation': data['personalisation'],
            }
            for _, data in notify.client.send_email_notification.call_args_list
        }
        for item in itertools.chain(assignees, subscribers):
            call = calls_by_email[item.adviser.get_current_email()]
            assert call[
                'template_id'] == Template.quote_cancelled_for_adviser.value
            assert call['personalisation'][
                'recipient name'] == item.adviser.name
            assert call['personalisation'][
                'embedded link'] == order.get_datahub_frontend_url()
            assert call['personalisation']['canceller'] == canceller.name
 def test_without_lead_assignee(self):
     """
     Test that get_lead_assignee() returns None if there are assignees
     but none of them is a lead.
     """
     order = OrderFactory(assignees=[])
     OrderAssigneeFactory(order=order, is_lead=False)
     assert not order.get_lead_assignee()
    def test_validation_error_if_not_all_actual_time_set(self):
        """
        Test that if not all assignee actual time fields have been set,
        a validation error is raised and the call fails.
        """
        order = OrderPaidFactory(assignees=[])
        OrderAssigneeCompleteFactory(order=order)
        OrderAssigneeFactory(order=order)

        with pytest.raises(ValidationError):
            order.complete(by=None)
    def test_pricing_update_on_assignee_created(self):
        """Test that if a new assignee is added, the pricing on the order changes."""
        order = OrderFactory(discount_value=0)
        assert order.total_cost > 0
        pre_update_total_cost = order.total_cost

        OrderAssigneeFactory(order=order)

        order.refresh_from_db()
        assert order.total_cost > 0
        post_update_total_cost = order.total_cost

        assert pre_update_total_cost != post_update_total_cost
    def test_400_if_assignee_deleted(self):
        """
        Test that assignees cannot be deleted at this stage.
        Given an order with the following assignees:
            [
                {
                    "adviser": {"id": 1},
                    "estimated_time": 100,
                    "is_lead": true
                },
                {
                    "adviser": {"id": 2},
                    "estimated_time": 250,
                    "is_lead": false
                },
            ]

        if I pass the following data with force_delete == True
            [
                {
                    "adviser": {"id": 1},
                },
            ]

        then:
            the response returns a validation error as no assignee can be deleted.
        """
        order = OrderPaidFactory(assignees=[])
        assignee = OrderAssigneeFactory(order=order)

        url = reverse(
            'api-v3:omis:order:assignee',
            kwargs={'order_pk': order.id},
        )
        response = self.api_client.patch(
            f'{url}?{AssigneeView.FORCE_DELETE_PARAM}=1',
            [
                {
                    'adviser': {
                        'id': assignee.adviser.id
                    },
                },
            ],
        )

        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert response.json() == {
            'non_field_errors': [
                'You cannot delete any assignees at this stage.',
            ],
        }
Example #16
0
    def test_content(self):
        """Test that the quote content is populated as expected."""
        hourly_rate = HourlyRateFactory(rate_value=1250,
                                        vat_value=Decimal(17.5))
        company = CompanyFactory(
            name='My *Coorp',
            registered_address_1='line 1',
            registered_address_2='*line 2',
            registered_address_town='London',
            registered_address_county='County',
            registered_address_postcode='SW1A 1AA',
            registered_address_country_id=Country.united_kingdom.value.id,
            company_number='123456789',
        )
        contact = ContactFactory(
            company=company,
            first_name='John',
            last_name='*Doe',
        )
        order = OrderFactory(
            delivery_date=dateutil_parse('2017-06-20'),
            company=company,
            contact=contact,
            reference='ABC123',
            primary_market_id=Country.france.value.id,
            description='lorem *ipsum',
            discount_value=100,
            hourly_rate=hourly_rate,
            assignees=[],
            vat_status=VATStatus.uk,
            contact_email='*****@*****.**',
        )
        OrderAssigneeFactory(
            order=order,
            adviser=AdviserFactory(
                first_name='Foo',
                last_name='*Bar',
            ),
            estimated_time=150,
            is_lead=True,
        )

        content = generate_quote_content(
            order=order,
            expires_on=dateutil_parse('2017-05-18').date(),
        )
        with open(COMPILED_QUOTE_TEMPLATE, 'r', encoding='utf-8') as f:
            expected_content = f.read()

        assert content == expected_content
Example #17
0
    def test_notify_on_order_assignee_added(self):
        """
        Test that a notification is sent to the adviser when they get assigned to an order.
        """
        order = OrderFactory(assignees=[])

        notify.client.reset_mock()

        assignee = OrderAssigneeFactory(order=order)
        assert notify.client.send_email_notification.called
        call_args = notify.client.send_email_notification.call_args_list[0][1]
        assert call_args['email_address'] == assignee.adviser.contact_email
        assert call_args[
            'template_id'] == Template.you_have_been_added_for_adviser.value
Example #18
0
    def test_notify_on_quote_accepted(self):
        """Test that a notification is triggered when a quote is accepted."""
        order = OrderWithOpenQuoteFactory(assignees=[])
        OrderAssigneeFactory.create_batch(1, order=order, is_lead=True)
        OrderSubscriberFactory.create_batch(2, order=order)

        notify.client.reset_mock()

        order.accept_quote(by=None)

        #  1 = customer, 3 = assignees/subscribers
        assert len(
            notify.client.send_email_notification.call_args_list) == (3 + 1)

        templates_called = [
            data[1]['template_id']
            for data in notify.client.send_email_notification.call_args_list
        ]
        assert templates_called == [
            Template.quote_accepted_for_customer.value,
            Template.quote_accepted_for_adviser.value,
            Template.quote_accepted_for_adviser.value,
            Template.quote_accepted_for_adviser.value,
        ]
Example #19
0
    def test_notify_on_order_assignee_deleted(self, mocked_notify_client):
        """
        Test that a notification is sent to the adviser when they get removed from an order.
        """
        order = OrderFactory(assignees=[])
        assignee = OrderAssigneeFactory(order=order)

        mocked_notify_client.reset_mock()

        order.assignees.all().delete()

        assert mocked_notify_client.send_email_notification.called
        call_args = mocked_notify_client.send_email_notification.call_args_list[
            0][1]
        assert call_args['email_address'] == assignee.adviser.contact_email
        assert call_args[
            'template_id'] == Template.you_have_been_removed_for_adviser.value
    def test_without_applied_vat(self, fields):
        """Test when the VAT status doesn't require the VAT to be applied."""
        hourly_rate = HourlyRateFactory(rate_value=110,
                                        vat_value=Decimal(19.5))
        order = OrderFactory(
            **fields,
            discount_value=100,
            hourly_rate=hourly_rate,
            assignees=[],
        )
        OrderAssigneeFactory(order=order, estimated_time=140)

        pricing = calculate_order_pricing(order)

        assert pricing.net_cost == 257
        assert pricing.subtotal_cost == 157
        assert pricing.vat_cost == 0
        assert pricing.total_cost == 157
def test_adding_assignees_syncs_order_to_es(setup_es):
    """
    Test that when an assignee is added to an order,
    the linked order gets synced to ES.
    """
    order = OrderFactory(assignees=[])
    assignees = OrderAssigneeFactory.create_batch(2, order=order)
    setup_es.indices.refresh()

    result = setup_es.get(
        index=OrderSearchApp.es_model.get_write_index(),
        doc_type=OrderSearchApp.name,
        id=order.pk,
    )

    indexed = {str(assignee['id']) for assignee in result['_source']['assignees']}
    expected = {str(assignee.adviser.pk) for assignee in assignees}
    assert indexed == expected
    assert len(indexed) == 2
Example #22
0
def test_adding_assignees_syncs_order_to_opensearch(opensearch_with_signals):
    """
    Test that when an assignee is added to an order,
    the linked order gets synced to OpenSearch.
    """
    order = OrderFactory(assignees=[])
    assignees = OrderAssigneeFactory.create_batch(2, order=order)
    opensearch_with_signals.indices.refresh()

    result = opensearch_with_signals.get(
        index=OrderSearchApp.search_model.get_write_index(),
        id=order.pk,
    )

    indexed = {
        str(assignee['id'])
        for assignee in result['_source']['assignees']
    }
    expected = {str(assignee.adviser.pk) for assignee in assignees}
    assert indexed == expected
    assert len(indexed) == 2
Example #23
0
    def test_pricing_format(self):
        """Test that the pricing is formatted as expected (xx.yy)"""
        hourly_rate = HourlyRateFactory(rate_value=1250, vat_value=Decimal(20))
        order = OrderFactory(
            discount_value=0,
            hourly_rate=hourly_rate,
            assignees=[],
            vat_status=VATStatus.uk,
        )
        OrderAssigneeFactory(
            order=order,
            estimated_time=120,
            is_lead=True,
        )

        content = generate_quote_content(
            order=order,
            expires_on=dateutil_parse('2017-05-18').date(),
        )

        assert '25.00' in content
    def test_team_country_dont_change_after_creation(self):
        """
        Tests that after creating an OrderAssignee, the `team` and `country`
        properties don't change with further updates.
        """
        team_france = TeamFactory(country_id=constants.Country.france.value.id)
        adviser = AdviserFactory(dit_team=team_france)
        assignee = OrderAssigneeFactory(adviser=adviser)

        # the adviser moves to another team
        adviser.dit_team = TeamFactory(
            country_id=constants.Country.italy.value.id)
        adviser.save()

        assignee.estimated_time = 1000
        assignee.save()
        assignee.refresh_from_db()

        # the assignee is still linking to the original team and country
        assert assignee.team == team_france
        assert str(assignee.country_id) == constants.Country.france.value.id
Example #25
0
def setup_subtotal_cost_data(es_with_collector):
    """
    Setup Order data for total subtotal cost test.

    Order will have a reference that is a string representation of the resulting subtotal_cost.
    Subtotal cost must be three-digit because the test is choosing orders by their
    reference. The reference filter is built with a trigram so this way we can
    avoid ambiguity.
    """
    subtotal_costs = (100, 200, 300, 400, 500)

    for subtotal_cost in subtotal_costs:
        order = OrderFactory(
            reference=str(subtotal_cost),
            discount_value=0,
            assignees=[],
        )
        OrderAssigneeFactory(
            order=order,
            estimated_time=subtotal_cost * 60 // order.hourly_rate.rate_value,
        )

    es_with_collector.flush_and_refresh()
    def test_ok_if_assignee_removed(self):
        """
        Test that assignees can be removed passing the `force_delete` flag.
        Given an order with the following assignees:
            [
                {
                    "adviser": {
                        "id": 1,
                        "first_name": "Joe",
                        "last_name": "Doe"
                    },
                    "estimated_time": 100,
                    "is_lead": true
                },
                {
                    "adviser": {
                        "id": 2,
                        "first_name": "Rebecca",
                        "last_name": "Bah"
                    },
                    "estimated_time": 250,
                    "is_lead": false
                },
            ]

        if I pass the following data with force_delete True:
            [
                {
                    "adviser": {"id": 1},
                    "estimated_time": 200
                },
                {
                    "adviser": {"id": 3},
                    "estimated_time": 250
                },
            ]

        then:
            adviser 2 gets removed
        """
        order = OrderFactory()
        adviser1 = AdviserFactory()
        adviser2 = AdviserFactory()
        adviser3 = AdviserFactory()

        OrderAssigneeFactory(order=order, adviser=adviser1)
        OrderAssigneeFactory(order=order, adviser=adviser2)

        url = reverse(
            'api-v3:omis:order:assignee',
            kwargs={'order_pk': order.id},
        )
        response = self.api_client.patch(
            f'{url}?{AssigneeView.FORCE_DELETE_PARAM}=1',
            [
                {
                    'adviser': {
                        'id': adviser1.id
                    },
                    'estimated_time': 200,
                },
                {
                    'adviser': {
                        'id': adviser3.id
                    },
                    'estimated_time': 250,
                },
            ],
        )

        assert response.status_code == status.HTTP_200_OK
        returned_advisers = {item['adviser']['id'] for item in response.json()}
        assert returned_advisers == {str(adviser1.id), str(adviser3.id)}
    def test_set_actual_time(self):
        """
        Test that actual_time for any assignee can be set.
        Given an order with the following assignees:
            [
                {
                    "adviser": {"id": 1},
                    "estimated_time": 100,
                    "is_lead": true
                },
                {
                    "adviser": {"id": 2},
                    "estimated_time": 250,
                    "is_lead": false
                },
            ]

        if I pass the following data:
            [
                {
                    "adviser": {"id": 2},
                    "actual_time": 220
                },
            ]

        then:
            adviser 2 gets updated
        """
        order = OrderPaidFactory(assignees=[])
        assignee1 = OrderAssigneeFactory(order=order,
                                         estimated_time=100,
                                         is_lead=True)
        assignee2 = OrderAssigneeFactory(order=order,
                                         estimated_time=250,
                                         is_lead=False)

        url = reverse(
            'api-v3:omis:order:assignee',
            kwargs={'order_pk': order.id},
        )
        response = self.api_client.patch(
            url,
            [
                {
                    'adviser': {
                        'id': assignee2.adviser.id
                    },
                    'actual_time': 220,
                },
            ],
        )

        assert response.status_code == status.HTTP_200_OK
        assert response.json() == [
            {
                'adviser': {
                    'id': str(assignee1.adviser.id),
                    'first_name': assignee1.adviser.first_name,
                    'last_name': assignee1.adviser.last_name,
                    'name': assignee1.adviser.name,
                },
                'estimated_time': assignee1.estimated_time,
                'actual_time': None,
                'is_lead': assignee1.is_lead,
            },
            {
                'adviser': {
                    'id': str(assignee2.adviser.id),
                    'first_name': assignee2.adviser.first_name,
                    'last_name': assignee2.adviser.last_name,
                    'name': assignee2.adviser.name,
                },
                'estimated_time': assignee2.estimated_time,
                'actual_time': 220,
                'is_lead': assignee2.is_lead,
            },
        ]
    def test_only_one_lead_allowed(self):
        """
        Test that only one lead is allowed and you have to set the old lead to False
        if you want to promote a different adviser.

        Given an order with the following assignees:
            [
                {
                    "adviser": {"id": 1},
                    ...
                    "is_lead": true
                },
                {
                    "adviser": {"id": 2},
                    ...
                    "is_lead": false
                },
                {
                    "adviser": {"id": 3},
                    ...
                    "is_lead": false
                },
            ]

        if I pass the following data:
            [
                {
                    "adviser": {"id": 2},
                    "is_lead": true
                },
                {
                    "adviser": {"id": 3},
                    "estimated_time": 0
                },
                {
                    "adviser": {"id": 4},
                    "estimated_time": 0
                },
            ]

        then:
            the response returns a validation error as adviser 1 and 2 are both marked as lead.
        """
        order = OrderFactory()
        adviser1 = AdviserFactory()
        adviser2 = AdviserFactory()
        adviser3 = AdviserFactory()
        adviser4 = AdviserFactory()

        OrderAssigneeFactory(order=order,
                             adviser=adviser1,
                             estimated_time=100,
                             is_lead=True)
        OrderAssigneeFactory(order=order,
                             adviser=adviser2,
                             estimated_time=250,
                             is_lead=False)
        OrderAssigneeFactory(order=order,
                             adviser=adviser3,
                             estimated_time=300,
                             is_lead=False)

        url = reverse(
            'api-v3:omis:order:assignee',
            kwargs={'order_pk': order.id},
        )
        response = self.api_client.patch(
            url,
            [
                {
                    'adviser': {
                        'id': adviser2.id
                    },
                    'is_lead': True,
                },
                {
                    'adviser': {
                        'id': adviser3.id
                    },
                    'estimated_time': 0,
                },
                {
                    'adviser': {
                        'id': adviser4.id
                    },
                    'estimated_time': 0,
                },
            ],
        )

        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert response.json() == {
            'non_field_errors': ['Only one lead allowed.']
        }
    def test_400_doesnt_commit_changes(self):
        """
        Test that in case of errors, changes are not saved.
        Given an order with the following assignees:
            [
                {
                    "adviser": {
                        "id": 1,
                        "first_name": "Joe",
                        "last_name": "Doe"
                    },
                    "estimated_time": 100,
                    "is_lead": true
                },
                {
                    "adviser": {
                        "id": 2,
                        "first_name": "Rebecca",
                        "last_name": "Bah"
                    },
                    "estimated_time": 250,
                    "is_lead": false
                },
            ]

        if I pass the following data:
            [
                {
                    "adviser": {"id": 1},
                    "estimated_time": 200
                },
                {
                    "adviser": {"id": 3},
                    "estimated_time": 250
                },
                {
                    "adviser": {"id": non-existent},
                    "estimated_time": 250
                },
            ]

        then:
            1. the response returns a validation error as the new adviser doesn't exist
            2. adviser 1 doesn't get updated
            3. adviser 3 doesn't get added
        """
        order = OrderFactory(assignees=[])
        adviser1 = AdviserFactory()
        adviser2 = AdviserFactory()
        adviser3 = AdviserFactory()

        OrderAssigneeFactory(order=order,
                             adviser=adviser1,
                             estimated_time=100,
                             is_lead=True)
        OrderAssigneeFactory(order=order,
                             adviser=adviser2,
                             estimated_time=250,
                             is_lead=False)

        url = reverse(
            'api-v3:omis:order:assignee',
            kwargs={'order_pk': order.id},
        )
        response = self.api_client.patch(
            url,
            [
                {
                    'adviser': {
                        'id': adviser1.id
                    },
                    'estimated_time': 200,
                    'is_lead': False,
                },
                {
                    'adviser': {
                        'id': adviser3.id
                    },
                    'estimated_time': 250,
                    'is_lead': True,
                },
                {
                    'adviser': {
                        'id': '00000000-0000-0000-0000-000000000000'
                    },
                    'estimated_time': 300,
                },
            ],
        )

        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert response.json() == [
            {},
            {},
            {
                'adviser': [
                    'Invalid pk "00000000-0000-0000-0000-000000000000" - object does not exist.',
                ],
            },
        ]

        # check db consistency
        adviser1.refresh_from_db()
        adviser2.refresh_from_db()

        qs = order.assignees
        ad_ids = set(qs.values_list('adviser_id', flat=True))
        assert ad_ids == {adviser1.id, adviser2.id}

        assignee1 = qs.get(adviser_id=adviser1.id)
        assert assignee1.estimated_time == 100
        assert assignee1.is_lead
    def test_without_changing_any_values(self):
        """
        Test that if I patch an assignee without changing any values,
        the db record doesn't get changed (and therefore modified_by stays the same).
        Given an order with the following assignees:
            [
                {
                    "adviser": {
                        "id": 1,
                        "first_name": "Joe",
                        "last_name": "Doe"
                    },
                    "estimated_time": 100,
                    "is_lead": true
                },
                {
                    "adviser": {
                        "id": 2,
                        "first_name": "Rebecca",
                        "last_name": "Bah"
                    },
                    "estimated_time": 250,
                    "is_lead": false
                },
            ]

        if I pass the following data:
            [
                {
                    "adviser": {"id": 1},
                    "estimated_time": 100,
                    "is_lead": true
                },
            ]

        then:
            adviser 1 doesn't get changed
        """
        created_by = AdviserFactory()
        order = OrderFactory()
        adviser1 = AdviserFactory()
        adviser2 = AdviserFactory()

        assignee1 = OrderAssigneeFactory(
            order=order,
            adviser=adviser1,
            created_by=created_by,
            modified_by=created_by,
        )
        OrderAssigneeFactory(order=order, adviser=adviser2)

        url = reverse(
            'api-v3:omis:order:assignee',
            kwargs={'order_pk': order.id},
        )
        response = self.api_client.patch(
            url,
            [
                {
                    'adviser': {
                        'id': adviser1.id
                    },
                    'estimated_time': assignee1.estimated_time,
                    'is_lead': assignee1.is_lead,
                },
            ],
        )

        assert response.status_code == status.HTTP_200_OK
        assignee1.refresh_from_db()

        assert assignee1.created_by == created_by
        assert assignee1.modified_by == created_by