def test_accept_offer_invalid_status(user_api_client, is_accepted,
                                     initial_status):
    customer = CustomerProfileFactory()
    due_date = date.today() + relativedelta(days=14)
    berth_switch_offer = BerthSwitchOfferFactory(
        customer=customer,
        due_date=due_date,
        status=initial_status,
        lease=BerthLeaseFactory(
            customer=customer,
            start_date=calculate_berth_lease_start_date(),
            end_date=calculate_berth_lease_end_date(),
            status=LeaseStatus.PAID,
        ),
    )

    variables = {
        # "offerId": to_global_id(BerthSwitchOfferNode, berth_switch_offer.id),
        "offerNumber": berth_switch_offer.offer_number,
        "isAccepted": is_accepted,
    }

    user_api_client.execute(ACCEPT_BERTH_SWITCH_OFFER_MUTATION,
                            input=variables)

    berth_switch_offer.refresh_from_db()
    berth_switch_offer.lease.refresh_from_db()

    assert berth_switch_offer.status == initial_status
    assert berth_switch_offer.lease.status == LeaseStatus.PAID
    new_lease = BerthLease.objects.exclude(
        id=berth_switch_offer.lease_id).first()
    assert new_lease is None
def test_reject_offer(user_api_client):
    customer = CustomerProfileFactory()
    due_date = date.today() + relativedelta(days=14)
    berth_switch_offer = BerthSwitchOfferFactory(
        customer=customer,
        due_date=due_date,
        status=OfferStatus.OFFERED,
        lease=BerthLeaseFactory(
            customer=customer,
            start_date=calculate_berth_lease_start_date(),
            end_date=calculate_berth_lease_end_date(),
            status=LeaseStatus.PAID,
        ),
    )

    variables = {
        # "offerId": to_global_id(BerthSwitchOfferNode, berth_switch_offer.id),
        "offerNumber": berth_switch_offer.offer_number,
        "isAccepted": False,
    }

    user_api_client.execute(ACCEPT_BERTH_SWITCH_OFFER_MUTATION,
                            input=variables)

    berth_switch_offer.refresh_from_db()
    berth_switch_offer.lease.refresh_from_db()

    assert berth_switch_offer.status == OfferStatus.REJECTED
    assert berth_switch_offer.lease.status == LeaseStatus.PAID
    assert berth_switch_offer.application.status == ApplicationStatus.REJECTED
    assert BerthLease.objects.all().count() == 1
def test_berth_switch_offer_current_season():
    berth_switch_offer = BerthSwitchOfferFactory(
        lease__start_date=calculate_berth_lease_start_date(),
        lease__end_date=calculate_berth_lease_end_date(),
    )

    assert berth_switch_offer.lease.start_date == date(2020, 6, 11)
def test_berth_is_not_available_renew_pending(date):
    with freeze_time(date):
        lease = BerthLeaseFactory(
            status=LeaseStatus.PAID,
            start_date=calculate_berth_lease_start_date() - relativedelta(years=1),
            end_date=calculate_berth_lease_end_date() - relativedelta(years=1),
        )

        # Need to fetch the berth from the DB to get the annotated value
        assert not Berth.objects.get(id=lease.berth_id).is_available
def test_berth_is_not_available_auto_renew_last_season(superuser_api_client, berth):
    start_date = calculate_berth_lease_start_date()
    end_date = calculate_berth_lease_end_date()

    start_date = start_date.replace(year=start_date.year - 1)
    end_date = end_date.replace(year=end_date.year - 1)
    BerthLeaseFactory(
        berth=berth, start_date=start_date, end_date=end_date, status=LeaseStatus.PAID,
    )
    assert not Berth.objects.get(id=berth.id).is_available
def test_berth_switch_offer_another_season():
    with pytest.raises(ValidationError) as exception:
        BerthSwitchOfferFactory(
            lease__start_date=calculate_berth_lease_start_date() -
            relativedelta(years=1),
            lease__end_date=calculate_berth_lease_end_date() -
            relativedelta(years=1),
        )

    assert "The exchanged lease has to be from the current season" in str(
        exception)
def test_berth_is_available_rejected_new_lease(date, inactive_status):
    with freeze_time(date):
        old_lease = BerthLeaseFactory(
            status=LeaseStatus.PAID,
            start_date=calculate_berth_lease_start_date() - relativedelta(years=1),
            end_date=calculate_berth_lease_end_date() - relativedelta(years=1),
        )
        BerthLeaseFactory(
            status=inactive_status, berth=old_lease.berth, customer=old_lease.customer
        )

        # Need to fetch the berth from the DB to get the annotated value
        assert Berth.objects.get(id=old_lease.berth_id).is_available
def test_berth_is_available_last_season_invalid_status(
    superuser_api_client, berth, status
):
    start_date = calculate_berth_lease_start_date()
    end_date = calculate_berth_lease_end_date()

    start_date = start_date.replace(year=start_date.year - 1)
    end_date = end_date.replace(year=end_date.year - 1)

    BerthLeaseFactory(
        berth=berth,
        start_date=start_date,
        end_date=end_date,
        status=LeaseStatus(status),
    )
    assert Berth.objects.get(id=berth.id).is_available
def test_order_winter_storage_lease_right_price_for_partial_year(
        winter_storage_area):
    services = {
        "summer_storage_for_docking_equipment": True,
        "summer_storage_for_trailers": random_bool(),
    }
    day_offset = 100
    for service, create in services.items():
        if create:
            # Using PriceUnits.AMOUNT to simplify testing
            AdditionalProductFactory(
                service=ProductServiceType(service),
                price_unit=PriceUnits.AMOUNT,
                period=PeriodType.YEAR,
            )

    section = WinterStorageSectionFactory(**services, area=winter_storage_area)
    lease = WinterStorageLeaseFactory(
        place=WinterStoragePlaceFactory(winter_storage_section=section),
        start_date=today(),
        end_date=today() + timedelta(days=day_offset),
    )
    order = OrderFactory(lease=lease)

    for service, create in services.items():
        if create:
            product = AdditionalProduct.objects.filter(
                service=ProductServiceType(service),
                price_unit=PriceUnits.AMOUNT,
                period=PeriodType.YEAR,
            ).first()
            OrderLine.objects.create(order=order, product=product)

    assert order.lease.start_date != calculate_berth_lease_start_date()
    assert order.lease.end_date != calculate_berth_lease_end_date()
    for service, created in services.items():
        if created:
            order_line = OrderLine.objects.filter(
                order=order,
                product__service=ProductServiceType(service)).first()
            partial_product_price = calculate_product_partial_year_price(
                order_line.product.price_value,
                order.lease.start_date,
                order.lease.end_date,
            )
            order_price = order_line.price
            assert partial_product_price == order_price
def test_berth_is_available_ends_during_season_after_lease_ends(
    superuser_api_client, berth
):
    start_date = calculate_berth_lease_start_date()
    end_date = calculate_berth_lease_end_date() - relativedelta(month=6, day=29)

    BerthLeaseFactory(
        berth=berth,
        start_date=start_date,
        end_date=end_date,
        # The lease is terminated at some point and set to end on end_date
        status=LeaseStatus.TERMINATED,
    )
    with mock.patch(
        "resources.models.calculate_season_start_date"
    ) as mock_start, mock.patch(
        "resources.models.calculate_berth_lease_end_date"
    ) as mock_end:
        mock_start.return_value = date(2020, 6, 30)
        mock_end.return_value = date(2020, 9, 15)
        assert Berth.objects.get(id=berth.id).is_available
def test_berth_is_not_available_ends_during_season_before_lease_ends(
    superuser_api_client, berth, status
):

    start_date = calculate_berth_lease_start_date()
    end_date = calculate_berth_lease_end_date() - relativedelta(month=6, day=29)

    BerthLeaseFactory(
        berth=berth,
        start_date=start_date,
        end_date=end_date,
        status=LeaseStatus(status),
    )
    with mock.patch(
        "leases.utils.calculate_berth_lease_start_date"
    ) as mock_start, mock.patch(
        "leases.utils.calculate_berth_lease_end_date"
    ) as mock_end:
        mock_start.return_value = date(2020, 6, 10)
        mock_end.return_value = date(2020, 9, 15)
        assert not Berth.objects.get(id=berth.id).is_available
Ejemplo n.º 12
0
    def get_queryset(self):
        """
        The QuerySet annotates whether a berth is available or not. For this,
        it considers the following criteria:
            - If there are leases associated to the berth
            - If any lease ends during the current or the last season (previous year)
                + If a lease ends during the current season:
                    * It needs to have a "valid" status (DRAFTED, OFFERED, PAID, ERROR)
                + If a lease ended during the last season:
                    * It should not have been renewed for the next season
                    * It needs to have a "valid" status (PAID)
        """
        from leases.models import BerthLease
        from payments.models import BerthSwitchOffer

        season_start = calculate_season_start_date()
        season_end = calculate_berth_lease_end_date()
        current_date = today().date()

        # If today is before the season ends but during the same year
        if current_date < season_end and current_date.year == season_end.year:
            last_year = current_date.year - 1
        else:
            last_year = current_date.year

        in_current_season = Q(
            # Check the lease starts at some point the during the season
            start_date__gte=season_start,
            # Check the lease ends earliest at the beginning of the season
            # (for leases terminated before the season started)
            end_date__gte=season_start,
            # Check the lease ends latest at the end of the season
            end_date__lte=season_end,
        )
        in_last_season = Q(end_date__year=last_year)

        active_current_status = Q(status__in=ACTIVE_LEASE_STATUSES)
        paid_status = Q(status=LeaseStatus.PAID)

        # In case the renewed leases for the upcoming season haven't been sent
        # or some of the leases that had to be fixed (also for the upcoming season)
        # are pending, we check for leases on the previous season that have already been paid,
        # which in most cases means that the customer will keep the berth for the next season as well.
        #
        # Pre-filter the leases for the upcoming/current season
        renewed_leases = BerthLease.objects.filter(
            in_current_season,
            berth=OuterRef("berth"),
            customer=OuterRef("customer"),
        ).values("pk")
        # Filter the leases from the previous season that have already been renewed
        previous_leases = (BerthLease.objects.exclude(
            Exists(renewed_leases)).filter(in_last_season,
                                           paid_status,
                                           berth=OuterRef("pk")).values("pk"))

        # For the leases that have been renewed or are valid during the current season.
        # Filter the leases on the current season that have not been rejected
        current_leases = BerthLease.objects.filter(
            in_current_season, active_current_status,
            berth=OuterRef("pk")).values("pk")

        # A berth is NOT available when it already has a lease on the current (or upcoming) season
        # or when the previous season lease has been paid and the new leases have not been sent.
        active_leases = ~Exists(previous_leases | current_leases)

        # Additionally, the berth is also NOT available when there is a switch offer drafted or offered
        # (this requires separate Exists clauses
        active_offers = ~Exists(
            BerthSwitchOffer.objects.filter(
                status__in=(OfferStatus.DRAFTED, OfferStatus.OFFERED),
                berth=OuterRef("pk"),
            ).values("pk"))

        # Need to explicitly mark the result of the AND as a BooleanField
        is_available = ExpressionWrapper(Q(active_leases & active_offers),
                                         output_field=BooleanField())

        return (super().get_queryset().annotate(
            is_available=is_available,
            _int_number=RawSQL(
                "CAST(substring(number FROM '^[0-9]+') AS INTEGER)",
                params=[],
                output_field=models.PositiveSmallIntegerField(),
            ),
        ).order_by("_int_number"))
def test_order_berth_lease_right_price_for_full_season(berth):
    lease = BerthLeaseFactory(berth=berth)
    order = OrderFactory(customer=lease.customer, lease=lease)

    assert order.lease.start_date == calculate_berth_lease_start_date()
    assert order.lease.end_date == calculate_berth_lease_end_date()