Ejemplo n.º 1
0
    def test_first_visit_discount_zero_demand(self):
        current_demand = [0 for x in range(0, PRICE_BLOCK_SIZE)]

        DISCOUNT = 20

        discounts = DiscountSettings()
        discounts.weekday_discounts = {}
        discounts.first_visit_percentage = DISCOUNT
        discounts.revisit_within_1week_percentage = 0
        discounts.revisit_within_2week_percentage = 0
        discounts.maximum_discount = 20
        discounts.is_maximum_discount_enabled = False

        regular_price = 1000 * random()
        prices = calc_client_prices(self.tz, discounts, None, [
            regular_price,
        ], current_demand)

        assert len(prices) == PRICE_BLOCK_SIZE

        # Full discount on all days because of zero demand
        for price in prices:
            assert price.price == Decimal(regular_price *
                                          (1 - DISCOUNT / 100.0)).quantize(
                                              1, ROUND_HALF_UP)
            assert price.applied_discount == DiscountType.FIRST_BOOKING
Ejemplo n.º 2
0
    def test_no_applicable_discount(self):

        # Zero demand on all days
        current_demand = [0 for x in range(0, PRICE_BLOCK_SIZE)]

        DISCOUNT1 = 20
        DISCOUNT2 = 30
        DISCOUNT3 = 40

        discounts = DiscountSettings()
        discounts.weekday_discounts = {}
        discounts.first_visit_percentage = DISCOUNT1
        discounts.revisit_within_1week_percentage = DISCOUNT3
        discounts.revisit_within_2week_percentage = DISCOUNT2
        discounts.maximum_discount = 20
        discounts.is_maximum_discount_enabled = False

        last_visit_date = date(2018, 5, 25)
        regular_price = 1000 * random()
        prices = calc_client_prices(self.tz, discounts, last_visit_date, [
            regular_price,
        ], current_demand)

        assert len(prices) == PRICE_BLOCK_SIZE

        # No discount on any days
        for i in range(0, PRICE_BLOCK_SIZE):
            assert prices[i].price == Decimal(regular_price).quantize(
                1, ROUND_HALF_UP)
            assert prices[i].applied_discount is None
Ejemplo n.º 3
0
    def test_multiple_applicable_discount(self):

        # Zero demand on all days
        current_demand = [0 for x in range(0, PRICE_BLOCK_SIZE)]

        DISCOUNT1 = 30
        DISCOUNT2 = 40

        discounts = DiscountSettings()
        discounts.weekday_discounts = {Weekday.SATURDAY: DISCOUNT1}
        discounts.first_visit_percentage = DISCOUNT2
        discounts.revisit_within_1week_percentage = 0
        discounts.revisit_within_2week_percentage = 0
        discounts.maximum_discount = 20
        discounts.is_maximum_discount_enabled = False

        regular_price = 1000 * random()
        prices = calc_client_prices(self.tz, discounts, None, [
            regular_price,
        ], current_demand)

        assert len(prices) == PRICE_BLOCK_SIZE

        # First time discount on all days
        for i in range(0, PRICE_BLOCK_SIZE):
            assert prices[i].price == Decimal(
                regular_price * (1 - DISCOUNT2 / 100.0)).quantize(
                    1, ROUND_HALF_UP)
            assert prices[i].applied_discount == DiscountType.FIRST_BOOKING
Ejemplo n.º 4
0
def generate_prices_for_stylist_service(
        services: List[StylistService],
        client: Optional[Client],
        exclude_fully_booked: bool = False,
        exclude_unavailable_days: bool = False) -> Iterable[PriceOnDate]:
    """
    Generate prices for given stylist, client and service for PRICE_BLOCK_SIZE days ahead

    :param service: Service to generate prices for
    :param client: (optional) Client object, if omitted no client-specific discounts will apply
    :param exclude_fully_booked: whether or not remove fully booked dates
    :param exclude_unavailable_days: whether or not remove unavailable dates
    :return: Iterator over (date, CalculatedPrice, fully_booked boolean)
    """
    stylist = services[0].stylist

    last_visit_date = get_last_visit_date_for_client(
        stylist, client) if client else None

    today = stylist.get_current_now().date()
    dates_list = [
        today + datetime.timedelta(days=i) for i in range(0, PRICE_BLOCK_SIZE)
    ]
    demand_on_dates = generate_demand_list_for_stylist(stylist=stylist,
                                                       dates=dates_list)

    demand_list = [x.demand for x in demand_on_dates]

    discounts = generate_discount_settings_for_stylist(stylist)

    prices_list = calc_client_prices(
        stylist.salon.timezone, discounts, last_visit_date,
        [float(x.regular_price) for x in services], demand_list)
    is_fully_booked_list = [d.is_fully_booked for d in demand_on_dates]
    is_working_day_list = [d.is_working_day for d in demand_on_dates]
    prices_on_dates: Iterable[PriceOnDate] = [
        PriceOnDate._make(x) for x in zip(
            dates_list, prices_list, is_fully_booked_list, is_working_day_list)
    ]
    if exclude_fully_booked:
        # remove dates where demand is equal to COMPLETELY_BOOKED_DEMAND
        prices_on_dates = compress(prices_on_dates, is_fully_booked_list)

    if exclude_unavailable_days:
        # remove dates where demand is equal to UNAVAILABLE_DEMAND
        prices_on_dates = compress(prices_on_dates, is_working_day_list)

    return prices_on_dates
Ejemplo n.º 5
0
    def test_invalid_arguments(self):

        # Missing demand data
        with pytest.raises(ValueError):
            discounts = DiscountSettings()
            discounts.weekday_discounts = {}
            calc_client_prices(self.tz, discounts, None, [0.0], [])

        # Invalid demand values
        with pytest.raises(ValueError):
            discounts = DiscountSettings()
            discounts.weekday_discounts = {}
            discounts.first_visit_percentage = 0
            discounts.revisit_within_1week_percentage = 0
            discounts.revisit_within_2week_percentage = 0
            discounts.maximum_discount = 20
            discounts.is_maximum_discount_enabled = True
            current_demand = [2 for x in range(0, PRICE_BLOCK_SIZE)]  # Invalid
            calc_client_prices(self.tz, discounts, None, [0.0], current_demand)

        # Invalid discount values
        with pytest.raises(ValueError):
            discounts = DiscountSettings()
            discounts.weekday_discounts = {}
            discounts.first_visit_percentage = 200  # Invalid
            discounts.revisit_within_1week_percentage = 0
            discounts.revisit_within_2week_percentage = 0
            discounts.maximum_discount = 20
            discounts.is_maximum_discount_enabled = True
            current_demand = [0 for x in range(0, PRICE_BLOCK_SIZE)]
            calc_client_prices(self.tz, discounts, None, [0.0], current_demand)

        # Invalid discount values
        with pytest.raises(ValueError):
            discounts = DiscountSettings()
            discounts.weekday_discounts = {Weekday.MONDAY: -10}  # Invalid
            discounts.first_visit_percentage = 0
            discounts.revisit_within_1week_percentage = 0
            discounts.revisit_within_2week_percentage = 0
            discounts.maximum_discount = 20
            discounts.is_maximum_discount_enabled = True
            current_demand = [0 for x in range(0, PRICE_BLOCK_SIZE)]
            calc_client_prices(self.tz, discounts, None, [0.0], current_demand)
Ejemplo n.º 6
0
    def test_revisit_within_1and2week(self):

        # Zero demand on all days
        current_demand = [0 for x in range(0, PRICE_BLOCK_SIZE)]

        DISCOUNT1 = 50
        DISCOUNT2 = 40
        DISCOUNT3 = 30

        discounts = DiscountSettings()
        discounts.weekday_discounts = {}
        discounts.first_visit_percentage = DISCOUNT1
        discounts.revisit_within_1week_percentage = DISCOUNT2
        discounts.revisit_within_2week_percentage = DISCOUNT3
        discounts.maximum_discount = 20
        discounts.is_maximum_discount_enabled = False

        last_visit_date = date(2018, 6, 2)
        regular_price = 1000 * random()
        prices = calc_client_prices(self.tz, discounts, last_visit_date, [
            regular_price,
        ], current_demand)

        assert len(prices) == PRICE_BLOCK_SIZE

        # revisit_within_1week_percentage discount on first day
        assert prices[0].price == Decimal(regular_price *
                                          (1 - DISCOUNT2 / 100.0)).quantize(
                                              1, ROUND_HALF_UP)
        assert prices[0].applied_discount == DiscountType.REVISIT_WITHIN_1WEEK

        # revisit_within_2week_percentage discount on next 7 days
        for i in range(1, min(8, PRICE_BLOCK_SIZE)):
            assert prices[i].price == Decimal(
                regular_price * (1 - DISCOUNT3 / 100.0)).quantize(
                    1, ROUND_HALF_UP)
            assert prices[
                i].applied_discount is DiscountType.REVISIT_WITHIN_2WEEK

        # No discount for the rest of days
        for i in range(8, PRICE_BLOCK_SIZE):
            assert prices[i].price == Decimal(regular_price).quantize(
                1, ROUND_HALF_UP)
            assert prices[i].applied_discount is None
Ejemplo n.º 7
0
    def test_first_visit_discount_specific_day_demand(self):

        # Full demand on all days except first and second
        current_demand = [1 for x in range(0, PRICE_BLOCK_SIZE)]
        current_demand[0] = 0
        PARTIAL_DEMAND = 0.5
        current_demand[1] = PARTIAL_DEMAND

        DISCOUNT = 20
        discounts = DiscountSettings()
        discounts.weekday_discounts = {}
        discounts.first_visit_percentage = DISCOUNT
        discounts.revisit_within_1week_percentage = 0
        discounts.revisit_within_2week_percentage = 0
        discounts.maximum_discount = 20
        discounts.is_maximum_discount_enabled = False

        regular_price = 1000 * random()
        prices = calc_client_prices(self.tz, discounts, None, [
            regular_price,
        ], current_demand)

        assert len(prices) == PRICE_BLOCK_SIZE

        # Full discount on zero demand day
        assert prices[0].price == Decimal(
            regular_price * (1 - DISCOUNT / 100.0)).quantize(1, ROUND_HALF_UP)
        assert prices[0].applied_discount == DiscountType.FIRST_BOOKING

        # Partial discount on partial demand day
        assert prices[1].price == Decimal(
            regular_price * (1 - DISCOUNT / 100.0 * PARTIAL_DEMAND)).quantize(
                1, ROUND_HALF_UP)
        assert prices[1].applied_discount == DiscountType.FIRST_BOOKING

        # No discount on all other days
        for price in prices[2:]:
            assert price.price == Decimal(regular_price).quantize(
                1, ROUND_HALF_UP)
            assert price.applied_discount is None
Ejemplo n.º 8
0
    def test_no_input_discount(self):
        current_demand = [random() for x in range(0, PRICE_BLOCK_SIZE)]

        discounts = DiscountSettings()
        discounts.weekday_discounts = {}
        discounts.first_visit_percentage = 0
        discounts.revisit_within_1week_percentage = 0
        discounts.revisit_within_2week_percentage = 0
        discounts.maximum_discount = 20
        discounts.is_maximum_discount_enabled = True

        regular_price = 1000 * random()
        prices = calc_client_prices(self.tz, discounts, None, [
            regular_price,
        ], current_demand)

        assert len(prices) == PRICE_BLOCK_SIZE

        # No discount on any day because no discounts are defined
        for price in prices:
            assert price.price == Decimal(regular_price).quantize(
                1, ROUND_HALF_UP)
            assert price.applied_discount is None
Ejemplo n.º 9
0
    def test_partial_demand_with_max_discount(self):
        current_demand = [0] * PRICE_BLOCK_SIZE
        current_demand[0] = 0.11
        current_demand[1] = 0.22
        current_demand[2] = 0.33
        current_demand[3] = 0.44
        current_demand[4] = 0.55
        current_demand[5] = 0.95
        current_demand[6] = 1

        DISCOUNT = 20

        discounts = DiscountSettings()
        discounts.weekday_discounts = {}
        discounts.first_visit_percentage = DISCOUNT
        discounts.revisit_within_1week_percentage = 0
        discounts.revisit_within_2week_percentage = 0
        discounts.maximum_discount = 20
        discounts.is_maximum_discount_enabled = True

        regular_price = 1000 * random()
        prices = calc_client_prices(self.tz, discounts, None, [
            regular_price,
        ], current_demand)

        assert len(prices) == PRICE_BLOCK_SIZE

        assert prices[0].price == Decimal(
            _calculate_discount(regular_price, 0.89, DISCOUNT,
                                discounts.maximum_discount)).quantize(
                                    1, ROUND_HALF_UP)
        assert prices[1].price == Decimal(
            _calculate_discount(regular_price, 0.78, DISCOUNT,
                                discounts.maximum_discount)).quantize(
                                    1, ROUND_HALF_UP)
        assert prices[2].price == Decimal(
            _calculate_discount(regular_price, 0.67, DISCOUNT,
                                discounts.maximum_discount)).quantize(
                                    1, ROUND_HALF_UP)
        assert prices[3].price == Decimal(
            _calculate_discount(regular_price, 0.56, DISCOUNT,
                                discounts.maximum_discount)).quantize(
                                    1, ROUND_HALF_UP)
        assert prices[4].price == Decimal(
            _calculate_discount(regular_price, 0.45, DISCOUNT,
                                discounts.maximum_discount)).quantize(
                                    1, ROUND_HALF_UP)
        assert prices[5].price == Decimal(
            _calculate_discount(regular_price, 0.05, DISCOUNT,
                                discounts.maximum_discount)).quantize(
                                    1, ROUND_HALF_UP)
        assert prices[6].price == Decimal(
            _calculate_discount(regular_price, 0, DISCOUNT,
                                discounts.maximum_discount)).quantize(
                                    1, ROUND_HALF_UP)

        # Full discount on the rest of days because of zero demand
        for price in prices[7:]:
            assert price.price == Decimal(
                _calculate_discount(regular_price, 1, DISCOUNT,
                                    discounts.maximum_discount)).quantize(
                                        1, ROUND_HALF_UP)
            assert price.applied_discount == DiscountType.FIRST_BOOKING