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
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()