Example #1
0
def test_order_creator_account_manager():
    company = create_random_company()
    shop = get_shop(identifier="random-shop", enabled=True)
    source = seed_source(create_random_user(), shop)
    source.customer = company
    source.add_line(
        type=OrderLineType.PRODUCT,
        product=get_default_product(),
        supplier=get_default_supplier(),
        quantity=1,
        base_unit_price=source.create_price(10),
    )
    creator = OrderCreator()
    order = creator.create_order(source)
    assert order.account_manager is None  # Company contact doesn't have account manager field

    person = create_random_person()
    person.account_manager = create_random_person()
    person.save()

    source = seed_source(create_random_user(), shop)
    source.customer = person
    source.add_line(
        type=OrderLineType.PRODUCT,
        product=get_default_product(),
        supplier=get_default_supplier(),
        quantity=1,
        base_unit_price=source.create_price(10),
    )
    creator = OrderCreator()
    order = creator.create_order(source)
    assert order.account_manager is not None
    assert order.account_manager == person.account_manager
    with pytest.raises(ProtectedError):
        person.account_manager.delete()
Example #2
0
def test_product_hightlight_plugin(rf, highlight_type, orderable):
    shop = get_default_shop()
    p1 = create_product("p1", shop, get_default_supplier(), "10")
    p2 = create_product("p2", shop, get_default_supplier(), "20")
    p3 = create_product("p3", shop, get_default_supplier(), "30")
    p4 = create_product("p4", shop, get_default_supplier(), "40")

    sp4 = p4.get_shop_instance(shop)
    sp4.purchasable = False
    sp4.save()

    context = get_context(rf)
    plugin = ProductHighlightPlugin({
        "type": highlight_type,
        "count": 4,
        "orderable_only": orderable
    })
    context_products = plugin.get_context_data(context)["products"]

    assert p1 in context_products
    assert p2 in context_products
    assert p3 in context_products
    if orderable:
        assert p4 not in context_products
    else:
        assert p4 in context_products
def get_frontend_order_state(contact, valid_lines=True):
    """
    Get a dict structure mirroring what the frontend JavaScript would submit.
    :type contact: Contact|None
    """
    translation.activate("en")
    shop = get_default_shop()
    tax = Tax.objects.create(code="test_code", rate=decimal.Decimal("0.20"), name="Default")
    tax_class = TaxClass.objects.create(identifier="test_tax_class", name="Default")
    rule = TaxRule.objects.create(tax=tax)
    rule.tax_classes.add(tax_class)
    rule.save()
    product = create_product(
        sku=printable_gibberish(),
        supplier=get_default_supplier(),
        shop=shop
    )
    product.tax_class = tax_class
    product.save()
    if valid_lines:
        lines = [
            {"id": "x", "type": "product", "product": {"id": product.id}, "quantity": "32", "baseUnitPrice": 50},
            {"id": "y", "type": "other", "sku": "hello", "text": "A greeting", "quantity": 1, "unitPrice": "5.5"},
            {"id": "z", "type": "text", "text": "This was an order!", "quantity": 0},
        ]
    else:
        unshopped_product = create_product(sku=printable_gibberish(), supplier=get_default_supplier())
        not_visible_product = create_product(
            sku=printable_gibberish(),
            supplier=get_default_supplier(),
            shop=shop
        )
        not_visible_shop_product = not_visible_product.get_shop_instance(shop)
        not_visible_shop_product.visible = False
        not_visible_shop_product.save()
        lines = [
            {"id": "x", "type": "product"},  # no product?
            {"id": "x", "type": "product", "product": {"id": unshopped_product.id}},  # not in this shop?
            {"id": "y", "type": "product", "product": {"id": -product.id}},  # invalid product?
            {"id": "z", "type": "other", "quantity": 1, "unitPrice": "q"},  # what's that price?
            {"id": "rr", "type": "product", "quantity": 1, "product": {"id": not_visible_product.id}}  # not visible
        ]

    state = {
        "customer": {"id": contact.id if contact else None},
        "lines": lines,
        "methods": {
            "shippingMethod": {"id": get_default_shipping_method().id},
            "paymentMethod": {"id": get_default_payment_method().id},
        },
        "shop": {
            "selected": {
                "id": shop.id,
                "name": shop.name,
                "currency": shop.currency,
                "priceIncludeTaxes": shop.prices_include_tax
            }
        }
    }
    return state
Example #4
0
def test_processor_orderability(admin_user):
    source = OrderSource(Shop())
    processor = OrderProcessor()
    line = source.add_line(
        type=OrderLineType.PRODUCT,
        product=get_default_product(),
        supplier=get_default_supplier(),
        quantity=1,
        shop=get_default_shop(),
        base_unit_price=source.create_price(10),
    )
    line.order = Order(shop=get_default_shop())
    assert processor._check_orderability(line) is None

    unorderable_line = source.add_line(
        type=OrderLineType.PRODUCT,
        product=create_product("no-shop"),
        supplier=get_default_supplier(),
        quantity=1,
        shop=get_default_shop(),
        base_unit_price=source.create_price(20),
    )
    unorderable_line.order = Order(shop=get_default_shop())
    with pytest.raises(ValidationError) as exc:
        processor._check_orderability(unorderable_line)
    assert "Not available in" in exc.value.message
Example #5
0
def test_get_orderable_variation_children(rf):
    supplier = get_default_supplier()
    shop = get_default_shop()

    variable_name = "Color"
    parent = create_product("test-sku-1", shop=shop)
    variation_variable = ProductVariationVariable.objects.create(product=parent, identifier="color", name=variable_name)
    red_value = ProductVariationVariableValue.objects.create(variable=variation_variable, identifier="red", value="Red")
    blue_value =ProductVariationVariableValue.objects.create(variable=variation_variable, identifier="blue", value="Blue")
    combinations = list(parent.get_all_available_combinations())
    assert len(combinations) == 2
    for combo in combinations:
        assert not combo["result_product_pk"]
        child = create_product("xyz-%s" % combo["sku_part"], shop=shop, supplier=get_default_supplier(), default_price=20)
        child.link_to_parent(parent, combination_hash=combo["hash"])

    combinations = list(parent.get_all_available_combinations())
    assert len(combinations) == 2
    parent.refresh_from_db()
    assert parent.is_variation_parent()
    request = apply_request_middleware(rf.get("/"))

    cache.clear()
    for time in range(2):
        orderable_children, is_orderable = get_orderable_variation_children(parent, request, None)
        assert len(orderable_children)
        for var_variable, var_values in dict(orderable_children).items():
            assert var_variable == variation_variable
            assert red_value in var_values
            assert blue_value in var_values
def test_shipping_table_behavior(admin_user):
    service = get_custom_carrier_service()
    component = ShippingTableByModeBehaviorComponent.objects.create(
        mode=FetchTableMode.LOWEST_DELIVERY_TIME
    )
    service.behavior_components.add(component)
    source = get_source(admin_user, service)

    PRODUCT_WEIGHT = Decimal(700.0)  # in grams
    PRODUCT_WIDTH = Decimal(340)
    PRODUCT_DEPTH = Decimal(320)
    PRODUCT_HEIGHT = Decimal(180)

    product = get_default_product()
    product.gross_weight = PRODUCT_WEIGHT
    product.width = PRODUCT_WIDTH
    product.depth = PRODUCT_DEPTH
    product.height = PRODUCT_HEIGHT
    product.save()

    source.add_line(
        type=OrderLineType.PRODUCT,
        product=product,
        supplier=get_default_supplier(),
        quantity=1,
        base_unit_price=source.create_price(10),
    )

    assert abs((component.get_source_weight(source) * KG_TO_G) - PRODUCT_WEIGHT) < Decimal(0.0001)

    # configure cubic usage
    component.use_cubic_weight = True
    component.cubic_weight_factor = Decimal(6000.0)
    component.cubic_weight_exemption = Decimal(3.000)
    component.save()

    # not using cubic weight because of weight exemption
    assert abs((component.get_source_weight(source) * KG_TO_G) - PRODUCT_WEIGHT) < Decimal(0.0001)

    source.add_line(
        type=OrderLineType.PRODUCT,
        product=product,
        supplier=get_default_supplier(),
        quantity=7,
        base_unit_price=source.create_price(10),
    )

    cubic_weight = (PRODUCT_WIDTH * PRODUCT_DEPTH * (PRODUCT_HEIGHT * 8)) / component.cubic_weight_factor
    assert abs((component.get_source_weight(source) * KG_TO_G) - cubic_weight) < Decimal(0.0001)

    # set constraints
    component.max_package_width = Decimal(1000)
    component.max_package_height = Decimal(1000)
    component.max_package_length = Decimal(1000)
    component.max_package_edges_sum = Decimal(2000)
    component.max_package_weight = Decimal(10)
    component.save()

    cubic_weight = (PRODUCT_WIDTH * PRODUCT_DEPTH * (PRODUCT_HEIGHT * 8)) / component.cubic_weight_factor
    assert abs((component.get_source_weight(source) * KG_TO_G) - cubic_weight) < Decimal(0.0001)
Example #7
0
def test_copy_order_to_basket(admin_user, settings):
    configure(settings)
    shop = factories.get_default_shop()
    basket = factories.get_basket()
    p1 = factories.create_product("test", shop=shop, supplier=factories.get_default_supplier())
    order = factories.create_order_with_product(factories.get_default_product(), factories.get_default_supplier(), 2, 10, shop=shop)
    factories.add_product_to_order(order, factories.get_default_supplier(), p1, 2, 5)
    order.customer = get_person_contact(admin_user)
    order.save()
    client = _get_client(admin_user)
    payload = {
        "order": order.pk
    }
    response = client.post('/api/shuup/basket/{}-{}/add_from_order/'.format(shop.pk, basket.key), payload)
    assert response.status_code == status.HTTP_200_OK
    response_data = json.loads(response.content.decode("utf-8"))
    assert len(response_data["items"]) == 2
    assert not response_data["validation_errors"]
    basket.refresh_from_db()
    assert len(basket.data["lines"]) == 2

    # do it again, basket should clear first then read items
    payload = {
        "order": order.pk
    }
    response = client.post('/api/shuup/basket/{}-{}/add_from_order/'.format(shop.pk, basket.key), payload)
    assert response.status_code == status.HTTP_200_OK
    response_data = json.loads(response.content.decode("utf-8"))
    assert len(response_data["items"]) == 2
    assert not response_data["validation_errors"]
    basket.refresh_from_db()
    assert len(basket.data["lines"]) == 2
Example #8
0
def _create_order(shop, customer):
    p1 = factories.create_product("test", shop=shop, supplier=factories.get_default_supplier())
    order = factories.create_order_with_product(factories.get_default_product(), factories.get_default_supplier(), 2,
                                                10, shop=shop)
    factories.add_product_to_order(order, factories.get_default_supplier(), p1, 2, 5)
    order.customer = customer
    order.save()
    return order
Example #9
0
def test_refunds(browser, admin_user, live_server, settings):
    order = create_order_with_product(
        get_default_product(), get_default_supplier(), 10, decimal.Decimal("10"), n_lines=10,
        shop=get_default_shop())
    order2 = create_order_with_product(
        get_default_product(), get_default_supplier(), 10, decimal.Decimal("10"), n_lines=10,
        shop=get_default_shop())
    order2.create_payment(order2.taxful_total_price)
    initialize_admin_browser_test(browser, live_server, settings)
    _test_toolbar_visibility(browser, live_server, order)
    _test_create_full_refund(browser, live_server, order)
    _test_refund_view(browser, live_server, order2)
Example #10
0
def test_product_from_category_plugin(rf):
    shop = get_default_shop()
    category1 = get_default_category()
    category2 = CategoryFactory(status=CategoryStatus.VISIBLE)

    category1.shops.add(shop)
    category2.shops.add(shop)

    p1 = create_product("p1", shop, get_default_supplier(), "10")
    p2 = create_product("p2", shop, get_default_supplier(), "20")
    p3 = create_product("p3", shop, get_default_supplier(), "30")

    sp1 = p1.get_shop_instance(shop)
    sp2 = p2.get_shop_instance(shop)
    sp3 = p3.get_shop_instance(shop)

    sp1.categories.add(category1)
    sp2.categories.add(category1)
    sp3.categories.add(category2)

    context = get_context(rf)
    plugin = ProductsFromCategoryPlugin({
        "category": category1.pk
    })
    context_products = plugin.get_context_data(context)["products"]
    assert p1 in context_products
    assert p2 in context_products
    assert p3 not in context_products

    # test the plugin form
    with override_current_theme_class(None):
        theme = get_current_theme(get_default_shop())
        cell = LayoutCell(theme, ProductsFromCategoryPlugin.identifier, sizes={"md": 8})
        lcfg = LayoutCellFormGroup(layout_cell=cell, theme=theme, request=apply_request_middleware(rf.get("/")))
        assert not lcfg.is_valid()

        lcfg = LayoutCellFormGroup(
            data={
                "general-cell_width": "8",
                "general-cell_align": "pull-right",
                "plugin-count": 4,
                "plugin-category": category2.pk
            },
            layout_cell=cell,
            theme=theme,
            request=apply_request_middleware(rf.get("/"))
        )
        assert lcfg.is_valid()
        lcfg.save()
        assert cell.config["category"] == str(category2.pk)
def test_correios_delivery_time_2(rf, admin_user):
    with patch.object(CorreiosWS, 'get_preco_prazo', return_value=MOCKED_SUCCESS_RESULT):
        pac_carrier = get_correios_carrier_2()
        contact = get_person_contact(admin_user)
        p1 = create_product(sku='p1',
                            supplier=get_default_supplier(),
                            width=400,
                            depth=400,
                            height=400,
                            gross_weight=1250)

        # P2 é pesado pacas - é empacotado em uma caixa separada
        # só para dar problema na metade da entrega
        p2 = create_product(sku='p2',
                            supplier=get_default_supplier(),
                            width=400,
                            depth=400,
                            height=400,
                            gross_weight=31250)

        source = seed_source(admin_user)
        source.add_line(
            type=OrderLineType.PRODUCT,
            product=p1,
            supplier=get_default_supplier(),
            quantity=2,
            base_unit_price=source.create_price(10))

        source.add_line(
            type=OrderLineType.PRODUCT,
            product=p2,
            supplier=get_default_supplier(),
            quantity=1,
            base_unit_price=source.create_price(20))

        billing_address = get_address()
        shipping_address = get_address(name="My House", country='BR')
        shipping_address.postal_code = "89070210"
        source.billing_address = billing_address
        source.shipping_address = shipping_address
        source.customer = contact

        shipping = ShippingMethod.objects.filter(carrier=pac_carrier).first()

        bc = shipping.behavior_components.first()
        packages = bc._pack_source(source)
        assert len(packages) == 3

        results = bc._get_correios_results(source, packages)
        assert len(results) == 3
Example #12
0
def test_order_received(rf, regular_user):
    activate("en")
    get_default_product()
    get_default_supplier()
    get_test_script("test script", "order_received")

    template_data = STEP_DATA[0]["actions"][0]["template_data"]
    for lang in ["en", "fi"]:
        n_outbox_pre = len(mail.outbox)
        customer = create_random_person(locale=lang)
        create_random_order(customer)
        assert (len(mail.outbox) == n_outbox_pre + 1), "Sending email failed"
        latest_mail = mail.outbox[-1]
        assert latest_mail.subject == template_data[lang]["subject"], "Subject doesn't match"
        assert latest_mail.body == template_data[lang]["body"], "Body doesn't match"
Example #13
0
def test_complex_import():
    filename = "complex_import.xlsx"
    activate("en")
    shop = get_default_shop()
    get_default_tax_class()
    get_default_product_type()
    get_default_supplier()
    get_default_sales_unit()

    path = os.path.join(os.path.dirname(__file__), "data", "product", filename)
    transformed_data = transform_file(filename.split(".")[1], path)
    importer = ProductImporter(transformed_data, shop, "en")
    importer.process_data()

    assert len(importer.unmatched_fields) == 0
    importer.do_import(ImportMode.CREATE_UPDATE)
    products = importer.new_objects
    assert len(products) == 6
    assert ShopProduct.objects.count() == 6
    assert Category.objects.count() == 11
    assert Manufacturer.objects.count() == 4

    for idx, product in enumerate(Product.objects.all().order_by("sku")):
        shop_product = product.get_shop_instance(shop)

        data = PRODUCT_DATA[idx]
        assert product.sku == data["sku"]
        assert product.name == data["name"]

        assert shop_product.default_price_value == Decimal(data["price"])
        assert product.description == data["description"]

        if data.get("categories"):
            all_cats = set(data["categories"])
            all_cats.add(data["category"])

            for cat in shop_product.categories.all():
                assert cat.name in all_cats
            assert shop_product.categories.count() == len(all_cats)  # also add primary category

        if data.get("category"):
            assert shop_product.primary_category.name == data["category"]

        assert force_text(shop_product.visibility.label) == data["visibility"].lower()
        assert product.tax_class.name == data["tax_class"]

        if data.get("manufacturer"):
            assert product.manufacturer.name == data["manufacturer"]
Example #14
0
def test_basket_shipping_error(rf):
    StoredBasket.objects.all().delete()
    shop = get_default_shop()
    supplier = get_default_supplier()
    shipped_product = create_product(
        printable_gibberish(), shop=shop, supplier=supplier, default_price=50,
        shipping_mode=ShippingMode.SHIPPED
    )
    unshipped_product = create_product(
        printable_gibberish(), shop=shop, supplier=supplier, default_price=50,
        shipping_mode=ShippingMode.NOT_SHIPPED
    )

    request = rf.get("/")
    request.session = {}
    request.shop = shop
    apply_request_middleware(request)
    basket = get_basket(request)

    # With a shipped product but no shipping methods, we oughta get an error
    basket.add_product(supplier=supplier, shop=shop, product=shipped_product, quantity=1)
    assert any(ve.code == "no_common_shipping" for ve in basket.get_validation_errors())
    basket.clear_all()

    # But with an unshipped product, we should not
    basket.add_product(supplier=supplier, shop=shop, product=unshipped_product, quantity=1)
    assert not any(ve.code == "no_common_shipping" for ve in basket.get_validation_errors())
Example #15
0
def test_min_amount_is_not_included():
    shop = get_default_shop()
    supplier = get_default_supplier()
    person = create_random_person()
    initial_group_count = person.groups.count()
    sales_ranges = [
        ("silver", 0, 50),
        ("gold", 50, 100),
        ("diamond", 100, 1000),
        ("reverse_diamond", 1000, 100)
    ]
    for identifier, min, max in sales_ranges:
        create_sales_range(identifier, shop, min, max)

    payment = create_fully_paid_order(shop, person, supplier, "sku1", 50)
    assert get_total_sales(shop, person) == 50
    update_customers_groups(Payment, payment)
    assert person.groups.count() == (initial_group_count + 1)
    assert bool([group for group in person.groups.all() if group.identifier == "gold"])
    assert not bool([group for group in person.groups.all() if group.identifier in ["silver", "diamond"]])

    payment = create_fully_paid_order(shop, person, supplier, "sku2", 50)
    assert get_total_sales(shop, person) == 100
    update_customers_groups(Payment, payment)
    assert person.groups.count() == (initial_group_count + 1)
    assert bool([group for group in person.groups.all() if group.identifier == "diamond"])
    assert not bool([group for group in person.groups.all() if group.identifier == "reverse_diamond"])
    assert not bool([group for group in person.groups.all() if group.identifier in ["silver", "gold"]])
Example #16
0
def test_protected_fields():
    activate("en")
    shop = Shop.objects.create(
        name="testshop",
        identifier="testshop",
        status=ShopStatus.ENABLED,
        public_name="test shop",
        domain="derp",
        currency="EUR"
    )
    get_currency("EUR")
    get_currency("USD")
    assert shop.name == "testshop"
    assert shop.currency == "EUR"
    assert not ConfigurationItem.objects.filter(shop=shop, key="languages").exists()
    shop_form = ShopBaseForm(instance=shop, languages=settings.LANGUAGES)
    assert not shop_form._get_protected_fields()  # No protected fields just yet, right?
    data = get_form_data(shop_form, prepared=True)
    shop_form = ShopBaseForm(data=data, instance=shop, languages=settings.LANGUAGES)
    _test_cleanliness(shop_form)
    shop_form.save()

    # Now let's make it protected!
    create_product(printable_gibberish(), shop=shop, supplier=get_default_supplier())
    order = create_random_order(customer=create_random_person(), shop=shop)
    assert order.shop == shop

    # And try again...
    data["currency"] = "USD"
    shop_form = ShopBaseForm(data=data, instance=shop, languages=settings.LANGUAGES)
    assert shop_form._get_protected_fields()  # So protected!
    _test_cleanliness(shop_form)
    shop = shop_form.save()
    assert shop.currency == "EUR"  # But the shop form ignored the change . . .
Example #17
0
def test_sales_ranges_update_after_range_update():
    shop = get_default_shop()
    supplier = get_default_supplier()
    person = create_random_person()
    company = create_random_company()
    create_fully_paid_order(shop, person, supplier, "sku1", 50)
    create_fully_paid_order(shop, company, supplier, "sku2", 100)
    assert get_total_sales(shop, person) == 50
    assert get_total_sales(shop, company) == 100

    sales_range = create_sales_range("gold", shop, 10, 90)
    assert sales_range.group in person.groups.all()
    assert sales_range.group not in company.groups.all()

    sales_range.max_value = None
    sales_range.save()
    assert sales_range.group in person.groups.all()
    assert sales_range.group in company.groups.all()

    # Make sure customers is actually removed when range changes
    sales_range.max_value = 60
    sales_range.save()
    assert sales_range.group in person.groups.all()
    assert sales_range.group not in company.groups.all()

    # Inactive ranges shouldn't update group members
    sales_range.min_value = None
    sales_range.save()
    assert sales_range.group in person.groups.all()
    assert sales_range.group not in company.groups.all()
def test_best_selling_products_with_multiple_orders():
    context = get_jinja_context()
    supplier = get_default_supplier()
    shop = get_default_shop()
    n_products = 2
    price = 10

    product_1 = create_product("test-sku-1", supplier=supplier, shop=shop)
    product_2 = create_product("test-sku-2", supplier=supplier, shop=shop)
    create_order_with_product(product_1, supplier, quantity=1, taxless_base_unit_price=price, shop=shop)
    create_order_with_product(product_2, supplier, quantity=1, taxless_base_unit_price=price, shop=shop)
    cache.clear()
    # Two initial products sold
    assert product_1 in general.get_best_selling_products(context, n_products=n_products)
    assert product_2 in general.get_best_selling_products(context, n_products=n_products)

    product_3 = create_product("test-sku-3", supplier=supplier, shop=shop)
    create_order_with_product(product_3, supplier, quantity=2, taxless_base_unit_price=price, shop=shop)
    cache.clear()
    # Third product sold in greater quantity
    assert product_3 in general.get_best_selling_products(context, n_products=n_products)

    create_order_with_product(product_1, supplier, quantity=4, taxless_base_unit_price=price, shop=shop)
    create_order_with_product(product_2, supplier, quantity=4, taxless_base_unit_price=price, shop=shop)
    cache.clear()
    # Third product outsold by first two products
    assert product_3 not in general.get_best_selling_products(context, n_products=n_products)

    children = [create_product("SimpleVarChild-%d" % x, supplier=supplier, shop=shop) for x in range(5)]
    for child in children:
        child.link_to_parent(product_3)
        create_order_with_product(child, supplier, quantity=1, taxless_base_unit_price=price, shop=shop)
    cache.clear()
    # Third product now sold in greatest quantity
    assert product_3 == general.get_best_selling_products(context, n_products=n_products)[0]
Example #19
0
def test_sales_between_ranges():
    shop = get_default_shop()
    supplier = get_default_supplier()
    person = create_random_person()
    initial_group_count = person.groups.count()
    sales_ranges = [
        ("wood", 15, 0),
        ("silver", 0, 50),
        ("diamond", 100, None)
    ]
    for identifier, min, max in sales_ranges:
        create_sales_range(identifier, shop, min, max)

    payment = create_fully_paid_order(shop, person, supplier, "sku1", 10)
    assert get_total_sales(shop, person) == 10
    update_customers_groups(Payment, payment)
    assert person.groups.count() == (initial_group_count + 1)
    assert bool([group for group in person.groups.all() if group.identifier == "silver"])
    assert not bool([group for group in person.groups.all() if group.identifier == "wood"])

    payment = create_fully_paid_order(shop, person, supplier, "sku2", 50)
    assert get_total_sales(shop, person) == 60
    update_customers_groups(Payment, payment)
    assert person.groups.count() == initial_group_count
    assert not bool([group for group in person.groups.all() if group.identifier in ["silver", "gold", "diamond"]])

    payment = create_fully_paid_order(shop, person, supplier, "sku3", 200)
    assert get_total_sales(shop, person) == 260
    update_customers_groups(Payment, payment)
    assert person.groups.count() == (initial_group_count + 1)
    assert bool([group for group in person.groups.all() if group.identifier == "diamond"])
Example #20
0
def test_mass_edit_orders(rf, admin_user):
    shop = get_default_shop()
    supplier = get_default_supplier()
    contact1 = create_random_person()
    product1 = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price="50")
    product2 = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price="501")

    order = create_random_order(customer=contact1,
                                products=[product1, product2],
                                completion_probability=0)

    assert order.status.role != OrderStatusRole.CANCELED
    payload = {
        "action": CancelOrderAction().identifier,
        "values": [order.pk]
    }
    request = apply_request_middleware(rf.post(
        "/",
        user=admin_user,
    ))
    request._body = json.dumps(payload).encode("UTF-8")
    view = OrderListView.as_view()
    response = view(request=request)
    assert response.status_code == 200
    for order in Order.objects.all():
        assert order.status.role == OrderStatusRole.CANCELED
Example #21
0
def test_mass_edit_orders3(rf, admin_user):
    shop = get_default_shop()
    supplier = get_default_supplier()
    contact1 = create_random_person()
    product1 = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price="50")
    product2 = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price="501")

    order1 = create_random_order(customer=contact1,
                                products=[product1, product2],
                                completion_probability=0)

    order2 = create_random_order(customer=contact1,
                                products=[product1, product2],
                                completion_probability=0)
    assert order1.status.role != OrderStatusRole.CANCELED
    assert order2.status.role != OrderStatusRole.CANCELED

    payload = {
        "action": OrderConfirmationPdfAction().identifier,
        "values": [order1.pk, order2.pk]
    }
    request = apply_request_middleware(rf.post(
        "/",
        user=admin_user,
    ))
    request._body = json.dumps(payload).encode("UTF-8")
    view = OrderListView.as_view()
    response = view(request=request)
    assert response.status_code == 200
    if weasyprint:
        assert response['Content-Disposition'] == 'attachment; filename=order_confirmation_pdf.zip'
    else:
        assert response["content-type"] == "application/json"
Example #22
0
def test_complex_order_tax(include_taxes):
    tax = get_default_tax()
    quantities = [44, 23, 65]
    product = get_default_product()
    supplier = get_default_supplier()
    shop = get_default_shop()
    shop.prices_include_tax = include_taxes
    shop.save()

    order = create_empty_order(shop=shop)
    order.full_clean()
    order.save()

    pricing_context = get_pricing_module().get_context_from_data(
        shop=shop,
        customer=order.customer or AnonymousContact(),
    )

    total_price = Decimal("0")
    price = Decimal("50")

    for quantity in quantities:
        total_price += quantity * price
        add_product_to_order(order, supplier, product, quantity, price, tax.rate, pricing_context)
    order.cache_prices()
    order.save()

    currency = "EUR"
    summary = order.get_tax_summary()[0]

    assert summary.tax_rate == tax.rate
    assert summary.based_on == Money(total_price, currency)
    assert summary.tax_amount == Money(total_price * tax.rate, currency)
    assert summary.taxful == summary.based_on + summary.tax_amount
    assert order.get_total_tax_amount() == Money(total_price * tax.rate, currency)
Example #23
0
def get_package_product():
    """
    :rtype: shuup.core.models.Product
    """
    shop = get_default_shop()
    supplier = get_default_supplier()
    return create_package_product("PackageParent", shop=shop, supplier=supplier)
Example #24
0
def test_productfilter_works(rf):
    request, shop, group = initialize_test(rf, False)
    price = shop.create_price
    product_price = "100"
    discount_percentage = "0.30"

    supplier = get_default_supplier()
    product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=product_price)
    shop_product = product.get_shop_instance(shop)

    # create catalog campaign
    catalog_filter = ProductFilter.objects.create()
    catalog_filter.products.add(product)

    assert catalog_filter.matches(shop_product)

    catalog_campaign = CatalogCampaign.objects.create(shop=shop, active=True, name="test")
    catalog_campaign.filters.add(catalog_filter)
    cdp = ProductDiscountPercentage.objects.create(campaign=catalog_campaign, discount_percentage=discount_percentage)

    # add product to basket
    basket = get_basket(request)
    basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1)
    basket.shipping_method = get_shipping_method(shop=shop)
    basket.save()

    expected_total = price(product_price) - (Decimal(discount_percentage) * price(product_price))
    assert basket.total_price == expected_total
Example #25
0
def test_edit_object_view_errors(rf, admin_user):
    shop = factories.get_default_shop()
    view = EditObjectView.as_view()

    # missing params
    response = view(apply_request_middleware(rf.get(reverse("shuup_admin:edit")), user=admin_user, shop=shop))
    assert response.status_code == 400
    assert "Invalid object" in response.content.decode("utf-8")

    # invalid model
    response = _get_edit_object_view(rf, view, ".", None, admin_user, shop)
    assert response.status_code == 400
    assert "Invalid model" in response.content.decode("utf-8")

    # invalid object ID
    product = factories.create_product("p1", shop, factories.get_default_supplier())
    model = ".".join(ContentType.objects.get_for_model(product).natural_key())
    with pytest.raises(Http404) as error:
        _get_edit_object_view(rf, view, model, product.id + 10, admin_user, shop)
    assert "Object not found" in str(error)

    # object has no admin url
    from shuup.core.models import ConfigurationItem
    config = ConfigurationItem.objects.create(shop=shop, key="test", value={"value": 123})
    model = ".".join(ContentType.objects.get_for_model(config).natural_key())
    with pytest.raises(Http404) as error:
        _get_edit_object_view(rf, view, model, config.id, admin_user, shop)
    assert "Object not found" in str(error)
Example #26
0
def test_discounted_price(rf):
    shop = factories.get_default_shop()
    supplier = factories.get_default_supplier()
    request = rf.get("/")
    request.shop = shop
    apply_request_middleware(request)
    assert request.shop == shop

    original_price = 10
    product = factories.create_product("test1", shop=shop, supplier=supplier, default_price=original_price)
    shop_product = product.get_shop_instance(shop)

    # Set discount with discount amount for $2
    discount_amount = 2
    discount = Discount.objects.create(
        active=True, product=product, supplier=supplier, discount_amount_value=discount_amount)
    discount.shops = [shop]

    # Even though the supplier is matching with the product there is no discount
    # since the supplier is not in pricing context.
    assert not hasattr(request, "supplier")
    assert supplier in shop_product.suppliers.all()
    assert product.get_price_info(request).price == request.shop.create_price(10)

    setattr(request, "supplier", supplier)
    assert product.get_price_info(request).price == request.shop.create_price(8)

    # No discount once we change the discount supplier
    new_supplier = Supplier.objects.create(identifier="*")
    discount.supplier = new_supplier
    discount.save()
    assert product.get_price_info(request).price == request.shop.create_price(10)
Example #27
0
def test_refunds_with_quantities():
    shop = get_default_shop()
    supplier = get_default_supplier()
    product = create_product(
        "test-sku",
        shop=get_default_shop(),
        default_price=10,
        stock_behavior=StockBehavior.STOCKED
    )

    order = create_order_with_product(product, supplier, 3, 200, shop=shop)
    order.cache_prices()
    assert not order.lines.refunds()

    product_line = order.lines.first()
    refund_amount = Money(100, order.currency)
    order.create_refund([{"line": product_line, "quantity": 2, "amount": refund_amount}])
    assert len(order.lines.refunds()) == 2

    quantity_line = order.lines.refunds().filter(quantity=2).first()
    assert quantity_line
    amount_line = order.lines.refunds().filter(quantity=1).first()
    assert amount_line

    assert quantity_line.taxful_base_unit_price == -product_line.taxful_base_unit_price
    assert amount_line.taxful_price.amount == -refund_amount
Example #28
0
def test_create_full_refund_view(rf, admin_user):
    shop = get_default_shop()
    supplier = get_default_supplier()
    product = create_product(sku="test-sku", shop=shop, supplier=supplier, default_price=3.33)
    order = create_order_with_product(product, supplier, quantity=1, taxless_base_unit_price=1, shop=shop)
    order.cache_prices()

    original_total_price = order.taxful_total_price

    assert not order.has_refunds()
    assert len(order.lines.all()) == 1
    assert order.taxful_total_price.amount.value != 0

    data = {
        "restock_products": "on",
    }

    request = apply_request_middleware(rf.post("/", data=data), user=admin_user)
    view = OrderCreateFullRefundView.as_view()
    response = view(request, pk=order.pk)
    assert response.status_code == 302
    assert order.has_refunds()
    order.cache_prices()

    assert order.taxful_total_price.amount.value == 0
    refund_line = order.lines.filter(type=OrderLineType.REFUND).last()
    assert refund_line
    assert refund_line.taxful_price == -original_total_price
Example #29
0
def test_all_categories_view(rf, admin_user):
    shop = get_default_shop()
    supplier = get_default_supplier()
    category = get_default_category()
    product = get_default_product()
    request = apply_request_middleware(rf.get("/"))
    _check_product_count(request, 0)

    shop_product = product.get_shop_instance(shop)
    shop_product.categories.add(category)
    _check_product_count(request, 1)

    # Create few categories for better test results
    for i in range(10):
        cat = Category.objects.create(name=printable_gibberish())
        cat.shops.add(shop)

    new_product_count = random.randint(1, 3) + 1
    for i in range(1, new_product_count):
        product = create_product("sku-%s" % i, shop=shop, supplier=supplier, default_price=10)
        shop_product = product.get_shop_instance(shop)

        # Add random categories expect default category which we will make
        # hidden to make sure that products linked to hidden categories are
        # not listed
        shop_product.categories = Category.objects.exclude(id=category.pk).order_by("?")[:i]

    _check_product_count(request, new_product_count)

    category.status = CategoryStatus.INVISIBLE
    category.save()
    _check_product_count(request, new_product_count - 1)
Example #30
0
def test_coupon_amount_limit():
    coupon = Coupon.objects.create(code="TEST", active=True)
    get_default_campaign(coupon)

    contact = create_random_person()
    shop = get_default_shop()
    product = create_product("test", shop=shop, supplier=get_default_supplier(), default_price="12")
    order = create_random_order(customer=contact)

    for x in range(50):
       coupon.use(order)

    assert coupon.usages.count() == 50
    coupon.increase_usage_limit_by(5)
    coupon.save()

    assert coupon.usage_limit == 55
    assert coupon.can_use_code(contact)

    for x in range(5):
        coupon.use(order)

    assert coupon.usages.count() == 55

    assert not Coupon.is_usable(coupon.code, order.customer)
    assert coupon.usages.count() == 55  # no change, limit met
Example #31
0
def test_refunds(admin_user):
    shop = get_default_shop()
    supplier = get_default_supplier()
    product = create_product(
        "test-sku",
        shop=get_default_shop(),
        default_price=10,
    )
    tax_rate = Decimal("0.1")
    taxless_base_unit_price = shop.create_price(200)
    order = create_order_with_product(product, supplier, 3, taxless_base_unit_price, tax_rate, shop=shop)
    order.payment_status = PaymentStatus.DEFERRED
    order.cache_prices()
    order.save()

    assert len(order.lines.all()) == 1
    assert order.can_create_refund()
    assert not order.has_refunds()

    client = _get_client(admin_user)

    # Refund first and the only order line in 3 parts
    refund_url = "/api/shuup/order/%s/create_refund/" % order.id
    product_line = order.lines.first()
    data = {
        "refund_lines": [{
            "line": product_line.id,
            "quantity": 1,
            "amount": (product_line.taxful_price.amount.value / 3),
            "restock_products": False
        }]
    }

    # First refund
    response = client.post(refund_url, data, format="json")
    assert response.status_code == status.HTTP_201_CREATED
    order.refresh_from_db()
    assert order.lines.count() == 2
    assert order.has_refunds()

    # Second refund
    response = client.post(refund_url, data, format="json")
    assert response.status_code == status.HTTP_201_CREATED
    order.refresh_from_db()
    assert order.lines.count() == 3
    assert order.can_create_refund()

    # Third refund
    response = client.post(refund_url, data, format="json")
    assert response.status_code == status.HTTP_201_CREATED
    order.refresh_from_db()
    assert order.lines.count() == 4
    assert not order.can_create_refund()
    assert not order.taxful_total_price.amount
    assert order.get_total_tax_amount() == Money(
        (order.taxful_total_price_value - order.taxless_total_price_value),
        order.currency)

    # Test error message
    response = client.post(refund_url, data, format="json")
    assert response.status_code == status.HTTP_400_BAD_REQUEST
    error_msg = json.loads(response.content.decode("utf-8"))["error"]
    assert error_msg == "Order can not be refunded at the moment."
def test_bump_caches_signal(rf):
    """
    Test that prices are bumped when discount objects changes
    """
    initial_price = 10
    discounted_price = 5

    shop1 = factories.get_default_shop()
    shop2 = factories.get_shop(identifier="shop2", domain="shop2")

    product1 = factories.create_product(
        "product",
        shop=shop1,
        supplier=factories.get_default_supplier(),
        default_price=initial_price)

    product2 = factories.create_product(
        "product2",
        shop=shop2,
        supplier=factories.get_default_supplier(),
        default_price=20)

    now = datetime(2018, 1, 1, 9, 0, tzinfo=pytz.UTC)  # 01/01/2018 09:00 AM

    with patch("django.utils.timezone.now", new=lambda: now):
        discount = Discount.objects.create(
            name="discount",
            active=True,
            start_datetime=now - timedelta(days=10),
            end_datetime=now + timedelta(days=10),
            discounted_price_value=discounted_price)

        request = apply_request_middleware(rf.get("/"))
        request_shop2 = apply_request_middleware(
            rf.get("/", HTTP_HOST=shop2.domain))

        def assert_cache_product1(discounted=False):
            cache_price_info(request, product1, 1,
                             product1.get_price_info(request))
            if discounted:
                assert get_cached_price_info(
                    request, product1,
                    1).price == shop1.create_price(discounted_price)
            else:
                assert get_cached_price_info(
                    request, product1,
                    1).price == shop1.create_price(initial_price)

        def assert_product1_is_not_cached():
            assert get_cached_price_info(request, product1) is None

        def assert_product2_is_cached():
            assert get_cached_price_info(request_shop2, product2) is not None

        assert_product1_is_not_cached()
        assert_cache_product1()

        # cache bumped - the cache should be dropped - then, cache again
        discount.save()
        assert_product1_is_not_cached()
        assert_cache_product1()

        discount.shops.add(shop1)
        assert_product1_is_not_cached()
        assert_cache_product1(True)

        # cache product 2.. from now on, shop2 cache should never be bumped
        cache_price_info(request_shop2, product2, 1,
                         product2.get_price_info(request_shop2))
        assert_product2_is_cached()

        discount.product = product1
        discount.save()

        assert_product1_is_not_cached()
        assert_cache_product1(True)
        assert_product2_is_cached()

        availability_exception = AvailabilityException.objects.create(
            name="ae1",
            start_datetime=now + timedelta(days=20),
            end_datetime=now + timedelta(days=30),
        )
        availability_exception.discounts.add(discount)
        assert_product1_is_not_cached()
        assert_cache_product1(True)
        assert_product2_is_cached()

        availability_exception.save()
        assert_product1_is_not_cached()
        assert_cache_product1(True)
        assert_product2_is_cached()

        happy_hour = HappyHour.objects.create(name="hh 1")
        happy_hour.discounts.add(discount)
        assert_product1_is_not_cached()
        assert_cache_product1(True)
        assert_product2_is_cached()

        happy_hour.save()
        assert_product1_is_not_cached()
        assert_cache_product1(True)
        assert_product2_is_cached()

        time_range = TimeRange.objects.create(
            happy_hour=happy_hour,
            from_hour=(now - timedelta(hours=1)).time(),
            to_hour=(now + timedelta(hours=1)).time(),
            weekday=now.weekday())
        assert_product1_is_not_cached()
        assert_cache_product1(True)
        assert_product2_is_cached()

        time_range.save()
        assert_product1_is_not_cached()
        assert_cache_product1(True)
        assert_product2_is_cached()

        time_range.delete()
        assert_product1_is_not_cached()
        assert_cache_product1(True)
        assert_product2_is_cached()

        with pytest.raises(DiscountM2MChangeError):
            handle_generic_m2m_changed("test", time_range)
Example #33
0
                          user,
                          shop,
                          mode=None):
    data = {"model": model_name, "id": object_id}
    if mode:
        data["mode"] = mode
    request = apply_request_middleware(rf.get(reverse("shuup_admin:edit"),
                                              data),
                                       user=user,
                                       shop=shop)
    return view(request)


@pytest.mark.parametrize("creator_fn", [
    lambda: factories.create_product("sku", factories.get_default_shop(),
                                     factories.get_default_supplier()),
    lambda: factories.create_random_person(),
    lambda: factories.create_random_company(),
    lambda: factories.create_random_order(
        customer=factories.create_random_person(),
        products=[
            factories.create_product("p", factories.get_default_shop(),
                                     factories.get_default_supplier())
        ]),
    lambda: factories.create_random_user(),
])
@pytest.mark.django_db
def test_edit_object_view(rf, admin_user, creator_fn):
    shop = factories.get_default_shop()
    view = EditObjectView.as_view()
    object_instance = creator_fn()
Example #34
0
def test_serialize_data():
    """
    Test that contact data is serialized
    """
    activate("en")
    shop = factories.get_default_shop()

    customer = factories.create_random_person("en")
    user = factories.create_random_user("en")
    customer.user = user
    customer.default_billing_address = factories.create_random_address()
    customer.default_shipping_address = factories.create_random_address()
    customer.save()

    company = factories.create_random_company()
    company.default_billing_address = factories.create_random_address()
    company.default_shipping_address = factories.create_random_address()
    company.save()
    company.members.add(customer)

    product = factories.create_product("p1", shop,
                                       factories.get_default_supplier())

    orders = []
    core_baskets = []
    front_baskets = []

    for basket_customer in [customer, company]:
        orders.extend([
            factories.create_random_order(basket_customer, [product])
            for order in range(3)
        ])

        front_baskets.append(
            StoredBasket.objects.create(
                key=uuid4().hex,
                shop=shop,
                customer=basket_customer,
                orderer=customer,
                creator=customer.user,
                currency=shop.currency,
                data={"items": []},
                prices_include_tax=shop.prices_include_tax))
        core_baskets.append(
            Basket.objects.create(key=uuid4().hex,
                                  shop=shop,
                                  customer=basket_customer,
                                  orderer=customer,
                                  creator=customer.user,
                                  currency=shop.currency,
                                  data={"items": []},
                                  prices_include_tax=shop.prices_include_tax))

    person_data = GDPRPersonContactSerializer(customer).data
    assert person_data["name"] == customer.name
    assert person_data["phone"] == customer.phone
    assert person_data["default_billing_address"][
        "street"] == customer.default_billing_address.street
    assert person_data["default_shipping_address"][
        "street"] == customer.default_shipping_address.street
    assert person_data["user"]["id"] == customer.user.id
    assert person_data["user"]["username"] == customer.user.username
    assert person_data["company_memberships"][0]["name"] == company.name
    assert person_data["company_memberships"][0]["id"] == company.id
    assert len(person_data["orders"]) == 3
    assert len(person_data["saved_baskets"]) == 1
    assert len(person_data["baskets"]) == 1
Example #35
0
def test_variation_product_price_more_complex(client):
    shop = get_default_shop()
    supplier = get_default_supplier()

    product_data = {
        "supplier-1": {
            "sizes": ["S", "M", "XL"],
            "colors": ["Black", "Yellow", "Blue"],
            "material": ["leather", "cotton"]
        },
        "supplier-2": {
            "sizes": ["S", "XL", "XS", "XXL", "M"],
            "colors": ["Yellow", "Red"],
            "material": ["cotton"]
        },
    }
    parent = create_product("ComplexVarParent", shop=shop)
    shop_parent_product = parent.get_shop_instance(shop)
    for key, data in six.iteritems(product_data):
        supplier = Supplier.objects.create(identifier=key)
        for size in data["sizes"]:
            for color in data["colors"]:
                for material in data["material"]:
                    sku = "ComplexVarChild-%s-%s-%s" % (size, color, material)
                    shop_product = ShopProduct.objects.filter(
                        product__sku=sku).first()
                    if shop_product:
                        shop_product.suppliers.add(supplier)
                    else:
                        child = create_product(sku,
                                               shop=shop,
                                               supplier=supplier)
                        child.link_to_parent(parent,
                                             variables={
                                                 "size": size,
                                                 "color": color,
                                                 "material": material
                                             })

    assert parent.variation_children.count() == 25
    # We have 6 different combinations but only 5 combinations
    # has product in them.
    assert len(list(parent.get_all_available_combinations())) == 40

    # Lets test prices for suppliers
    for key, data in six.iteritems(product_data):
        supplier_id = Supplier.objects.get(identifier=key).id
        available_combinations = set()
        for size in data["sizes"]:
            for color in data["colors"]:
                for material in data["material"]:
                    available_combinations.add(
                        "color: %s, material: %s, size: %s" %
                        (color, material, size))

        expected_orderable_count = len(available_combinations)
        actual_orderable_count = 0
        had_at_least_one_not_orderable_in_this_test = False
        for combination in parent.get_all_available_combinations():
            product_result_pk = combination["result_product_pk"]
            if not product_result_pk:  # we can skip combinations without products
                continue

            variable_string = ""
            for key, value in six.iteritems(combination["variable_to_value"]):
                variable_string += "var_%s=%s&" % (key.pk, value.pk)

            response = client.get(
                reverse('shuup:xtheme_extra_view',
                        kwargs={'view': 'product_price'}) +
                "?id=%s&quantity=%s&%ssupplier=%s" %
                (parent.pk, 1, variable_string, supplier_id))
            assert response.context_data["product"] == Product.objects.filter(
                id=product_result_pk).first()
            content = response.content.decode("utf-8")

            is_orderable = combination[
                "text_description"] in available_combinations
            if is_orderable:
                actual_orderable_count += 1
                assert "form" in content
            else:
                had_at_least_one_not_orderable_in_this_test = True
                assert "Combination not available" in content

        assert actual_orderable_count == expected_orderable_count
        assert had_at_least_one_not_orderable_in_this_test
Example #36
0
def create_products(shop):
    supplier = get_default_supplier()
    for i in range(0, 200):
        sku = "sku-%d" % i
        create_product(sku, shop, supplier, default_price=i)
Example #37
0
def test_view_availability(admin_user, rf):
    supplier = get_default_supplier()
    shop_one = get_shop(True,
                        "USD",
                        enabled=True,
                        identifier="one",
                        name="Shop One")
    supplier.shops.add(shop_one)
    simone = create_random_user(username="******")
    simone.is_staff = True
    simone.save()
    shop_one.staff_members.add(simone)

    peter = create_random_user(username="******")
    peter.is_staff = True
    peter.save()
    shop_one.staff_members.add(peter)

    shop_two = get_shop(True,
                        "USD",
                        enabled=True,
                        identifier="two",
                        name="Shop Two")
    assert shop_one.pk != shop_two.pk
    supplier.shops.add(shop_two)
    calle = create_random_user(username="******")
    calle.is_staff = True
    calle.save()
    shop_two.staff_members.add(calle)

    product = create_product("simple-test-product", shop_one)
    order = create_order_with_product(product, supplier, 6, 6, shop=shop_one)

    # Simone and Peter should access to this order. Calle should get 404
    def test_view(view, order, shop, user, data=None):
        if data:
            request = apply_request_middleware(rf.post("/", data),
                                               user=user,
                                               shop=shop)
        else:
            request = apply_request_middleware(rf.get("/"),
                                               user=user,
                                               shop=shop)

        response = view.as_view()(request, pk=order.pk)

    # Gets
    for view in [
            OrderDetailView, OrderSetStatusView, OrderCreatePaymentView,
            OrderSetPaidView, OrderAddressEditView
    ]:
        test_view(view, order, shop_one, simone)
        test_view(view, order, shop_one, peter)
        with pytest.raises(Http404):
            test_view(view, order, shop_two, calle)

    test_view(NewLogEntryView, order, shop_one, simone,
              {"message": "message here"})
    test_view(NewLogEntryView, order, shop_one, peter,
              {"message": "message here"})
    with pytest.raises(Http404):
        test_view(NewLogEntryView, order, shop_two, calle,
                  {"message": "message here"})

    test_view(UpdateAdminCommentView, order, shop_one, simone,
              {"comment": "comment here"})
    test_view(UpdateAdminCommentView, order, shop_one, peter,
              {"comment": "comment here"})
    with pytest.raises(Http404):
        test_view(UpdateAdminCommentView, order, shop_two, calle,
                  {"comment": "comment here"})

    def test_shipment_view(order, shop, supplier, user):
        request = apply_request_middleware(rf.get("/"), user=user, shop=shop)
        response = OrderCreateShipmentView.as_view()(request,
                                                     pk=order.pk,
                                                     supplier_pk=supplier.pk)

    test_shipment_view(order, shop_one, supplier, simone)
    test_shipment_view(order, shop_one, supplier, peter)
    with pytest.raises(Http404):
        test_shipment_view(order, shop_two, supplier, calle)

    # Create shipment to test delete shipment view
    shipment = order.create_shipment_of_all_products(supplier)

    def test_shipment_delete_view(shipment, shop, user):
        request = apply_request_middleware(rf.post("/"), user=user, shop=shop)
        response = ShipmentDeleteView.as_view()(request,
                                                pk=shipment.pk,
                                                supplier_pk=supplier.pk)

    test_shipment_delete_view(shipment, shop_one, simone)
    test_shipment_delete_view(shipment, shop_one, peter)
    with pytest.raises(Http404):
        test_shipment_delete_view(shipment, shop_two, calle)

    # Create payment to test refund and delete payment view
    order.create_payment(order.taxful_total_price)
    payment = order.payments.first()
    assert payment is not None

    for view in [OrderCreateRefundView, OrderCreateFullRefundView]:
        test_view(view, order, shop_one, simone)
        test_view(view, order, shop_one, peter)
        with pytest.raises(Http404):
            test_view(view, order, shop_two, calle)

    def test_payment_delete_view(payment, shop, user):
        request = apply_request_middleware(rf.post("/"), user=user, shop=shop)
        response = OrderDeletePaymentView.as_view()(request, pk=payment.pk)

    test_payment_delete_view(payment, shop_one, simone)
    test_payment_delete_view(payment, shop_one, peter)
    with pytest.raises(Http404):
        test_payment_delete_view(payment, shop_two, calle)
Example #38
0
def test_broken_order(admin_user):
    """
    """
    quantities = [44, 23, 65]
    expected = sum(quantities) * 50
    expected_based_on = expected / 1.5

    # Shuup is calculating taxes per line so there will be some "errors"
    expected_based_on = ensure_decimal_places(
        Decimal("%s" % (expected_based_on + 0.01)))

    shop = get_default_shop()

    supplier = get_default_supplier()
    product1 = create_product("simple-test-product1", shop, supplier, 50)
    product2 = create_product("simple-test-product2", shop, supplier, 50)
    product3 = create_product("simple-test-product3", shop, supplier, 50)

    tax = get_default_tax()

    source = BasketishOrderSource(get_default_shop())
    billing_address = get_address(country="US")
    shipping_address = get_address(name="Test street", country="US")
    source.status = get_initial_order_status()
    source.billing_address = billing_address
    source.shipping_address = shipping_address
    source.customer = create_random_person()
    source.payment_method = get_default_payment_method()
    source.shipping_method = get_default_shipping_method()

    source.add_line(
        type=OrderLineType.PRODUCT,
        product=product1,
        supplier=get_default_supplier(),
        quantity=quantities[0],
        base_unit_price=source.create_price(50),
    )

    source.add_line(
        type=OrderLineType.PRODUCT,
        product=product2,
        supplier=get_default_supplier(),
        quantity=quantities[1],
        base_unit_price=source.create_price(50),
    )

    source.add_line(
        type=OrderLineType.PRODUCT,
        product=product3,
        supplier=get_default_supplier(),
        quantity=quantities[2],
        base_unit_price=source.create_price(50),
    )

    currency = "EUR"
    summary = source.get_tax_summary()

    assert len(summary) == 1
    summary = summary[0]

    assert summary.taxful == Money(expected, "EUR")
    assert summary.based_on == Money(expected_based_on, "EUR")

    # originally non-rounded value
    assert bankers_round(source.get_total_tax_amount()) == summary.tax_amount

    assert source.taxless_total_price.value == expected_based_on
    assert summary.taxful.value == source.taxful_total_price.value

    assert summary.tax_amount == Money(
        bankers_round(source.taxful_total_price.value -
                      source.taxless_total_price.value), currency)
    assert summary.taxful == summary.raw_based_on + summary.tax_amount

    assert summary.tax_rate == tax.rate
    assert summary.taxful.value == (
        summary.based_on + summary.tax_amount).value - Decimal("%s" % 0.01)

    # create order from basket
    creator = OrderCreator()
    order = creator.create_order(source)
    assert order.taxless_total_price.value == expected_based_on

    # originally non-rounded value
    assert bankers_round(order.get_total_tax_amount()) == summary.tax_amount
Example #39
0
def test_refunds():
    shop = get_default_shop()
    supplier = get_default_supplier()
    product = create_product(
        "test-sku",
        shop=get_default_shop(),
        default_price=10,
    )
    tax_rate = Decimal("0.1")
    taxless_base_unit_price = shop.create_price(200)
    order = create_order_with_product(product,
                                      supplier,
                                      3,
                                      taxless_base_unit_price,
                                      tax_rate,
                                      shop=shop)
    order.payment_status = PaymentStatus.DEFERRED
    order.cache_prices()
    order.save()

    assert order.get_total_refunded_amount().value == 0
    assert order.get_total_unrefunded_amount(
    ).value == order.taxful_total_price.value
    assert not order.can_edit()

    assert len(order.lines.all()) == 1

    product_line = order.lines.first()
    assert product_line.ordering == 0
    assert order.can_create_refund()
    assert not order.has_refunds()

    order.create_refund([{
        "line": product_line,
        "quantity": 1,
        "amount": (product_line.taxful_price.amount / 3)
    }])
    assert len(order.lines.all()) == 2
    assert order.lines.last().ordering == 1
    assert order.has_refunds()

    # Confirm the value of the refund
    assert order.lines.last().taxful_price == -product_line.base_unit_price
    assert order.lines.last().tax_amount == -(
        product_line.taxless_base_unit_price * tax_rate).amount

    # Create a refund with a parent line and amount
    order.create_refund([{
        "line": product_line,
        "quantity": 1,
        "amount": product_line.taxful_price.amount / 3
    }])
    assert len(order.lines.all()) == 3
    assert order.lines.last().ordering == 2

    assert order.lines.last(
    ).taxful_price.amount == -taxless_base_unit_price.amount * (1 + tax_rate)
    assert order.lines.last(
    ).tax_amount == -taxless_base_unit_price.amount * tax_rate

    assert order.taxless_total_price.amount == taxless_base_unit_price.amount
    assert order.taxful_total_price.amount == taxless_base_unit_price.amount * (
        1 + tax_rate)
    assert order.can_create_refund()
    assert order.get_total_tax_amount() == Money(
        (order.taxful_total_price_value - order.taxless_total_price_value),
        order.currency)

    # Try to refunding remaining amount without a parent line
    with pytest.raises(AssertionError):
        order.create_refund([{"amount": taxless_base_unit_price}])

    # refund remaining amount
    order.create_refund([{
        "line": product_line,
        "quantity": 1,
        "amount": product_line.taxful_price.amount / 3
    }])
    assert len(order.lines.all()) == 4
    assert order.lines.last().ordering == 3
    assert order.lines.last(
    ).taxful_price.amount == -taxless_base_unit_price.amount * (1 + tax_rate)

    assert not order.taxful_total_price.amount
    assert not order.can_create_refund()
    assert order.get_total_tax_amount() == Money(
        (order.taxful_total_price_value - order.taxless_total_price_value),
        order.currency)

    with pytest.raises(RefundExceedsAmountException):
        order.create_refund([{
            "line": product_line,
            "quantity": 1,
            "amount": taxless_base_unit_price.amount
        }])
def test_multiple_campaigns_cheapest_price():
    rf = RequestFactory()
    request, shop, group = initialize_test(rf, False)
    price = shop.create_price
    product_price = "100"
    discount_percentage = "0.30"
    discount_amount_value = "10"
    total_discount_amount = "50"

    expected_total = price(product_price) - (Decimal(discount_percentage) *
                                             price(product_price))
    matching_expected_total = price(product_price) - price(
        total_discount_amount)

    category = get_default_category()
    supplier = get_default_supplier()
    product = create_product(printable_gibberish(),
                             shop=shop,
                             supplier=supplier,
                             default_price=product_price)
    shop_product = product.get_shop_instance(shop)
    shop_product.categories.add(category)

    # create catalog campaign
    catalog_filter = ProductFilter.objects.create()
    catalog_filter.products.add(product)
    catalog_campaign = CatalogCampaign.objects.create(shop=shop,
                                                      active=True,
                                                      name="test")
    catalog_campaign.filters.add(catalog_filter)

    cdp = ProductDiscountPercentage.objects.create(
        campaign=catalog_campaign, discount_percentage=discount_percentage)

    # create basket campaign
    condition = CategoryProductsBasketCondition.objects.create(
        operator=ComparisonOperator.EQUALS, quantity=1)
    condition.categories.add(category)
    basket_campaign = BasketCampaign.objects.create(shop=shop,
                                                    public_name="test",
                                                    name="test",
                                                    active=True)
    basket_campaign.conditions.add(condition)

    effect = DiscountFromProduct.objects.create(
        campaign=basket_campaign, discount_amount=discount_amount_value)
    effect.products.add(product)

    # add product to basket
    basket = get_basket(request)
    basket.add_product(supplier=supplier,
                       shop=shop,
                       product=product,
                       quantity=1)
    basket.shipping_method = get_shipping_method(shop=shop)
    final_lines = basket.get_final_lines()
    assert len(final_lines) == 2
    assert basket.total_price == expected_total

    effect.discount_amount = total_discount_amount
    effect.save()
    basket.uncache()
    catalog_campaign.save()  # save to bump caches
    basket_campaign.save()  # save to bump caches

    assert basket.total_price == matching_expected_total  # discount is now bigger than the original

    effect.delete()  # remove effect
    basket.uncache()
    catalog_campaign.save()  # save to bump caches
    basket_campaign.save()  # save to bump caches

    assert BasketLineEffect.objects.count() == 0

    assert basket.total_price == expected_total
    # add new effect
    effect = DiscountFromCategoryProducts.objects.create(
        category=category,
        campaign=basket_campaign,
        discount_amount=discount_amount_value)
    assert basket.total_price == expected_total

    effect.discount_amount = total_discount_amount
    effect.save()
    basket.uncache()
    catalog_campaign.save()  # save to bump caches
    basket_campaign.save()  # save to bump caches
    assert basket.total_price == matching_expected_total  # discount is now bigger than the original
Example #41
0
def test_undiscounted_effects(rf, include_tax):
    request, shop, _ = initialize_test(rf, include_tax)

    basket = get_basket(request)
    supplier = get_default_supplier()

    single_product_price = Decimal(50)
    discounted_product_quantity = 4
    normal_priced_product_quantity = 2
    discount_percentage = Decimal(0.2)  # 20%
    discount_amount = basket.create_price(single_product_price *
                                          normal_priced_product_quantity *
                                          discount_percentage)

    category = CategoryFactory()

    discounted_product = create_product(printable_gibberish(),
                                        shop=shop,
                                        supplier=supplier,
                                        default_price=single_product_price)
    second_product = create_product(printable_gibberish(),
                                    shop=shop,
                                    supplier=supplier,
                                    default_price=single_product_price)

    ShopProduct.objects.get(
        shop=shop, product=discounted_product).categories.add(category)
    ShopProduct.objects.get(shop=shop,
                            product=second_product).categories.add(category)
    basket.add_product(supplier=supplier,
                       shop=shop,
                       product=discounted_product,
                       quantity=discounted_product_quantity)
    basket.add_product(supplier=supplier,
                       shop=shop,
                       product=second_product,
                       quantity=normal_priced_product_quantity)
    basket.shipping_method = get_shipping_method(shop=shop)
    basket.save()

    # Store basket price before any campaigns exists
    original_price = basket.total_price

    # CatalogCampaign
    catalog_campaign = CatalogCampaign.objects.create(active=True,
                                                      shop=shop,
                                                      name="test",
                                                      public_name="test")
    # Limit catalog campaign to "discounted_product"
    product_filter = ProductFilter.objects.create()
    product_filter.products.add(discounted_product)
    catalog_campaign.filters.add(product_filter)

    # BasketCampaign
    campaign = BasketCampaign.objects.create(active=True,
                                             shop=shop,
                                             name="test2",
                                             public_name="test2")

    final_lines = basket.get_final_lines()
    assert len(final_lines) == 3

    # Discount based on undiscounted product values
    DiscountPercentageFromUndiscounted.objects.create(
        campaign=campaign, discount_percentage=discount_percentage)

    basket.uncache()
    final_lines = basket.get_final_lines()
    assert len(final_lines) == 4

    discounted_basket_price = original_price - discount_amount
    assert basket.total_price.as_rounded(
    ) == discounted_basket_price.as_rounded()
Example #42
0
def test_suppliers_deleted():
    supplier = get_default_supplier()
    supplier.soft_delete()
    assert supplier.deleted is True
Example #43
0
def test_discount_no_limits(rf, include_tax):
    # check whether is it possible to earn money buying from us
    # adding lots of effects
    request, shop, _ = initialize_test(rf, include_tax)

    basket = get_basket(request)
    supplier = get_default_supplier()

    single_product_price = Decimal(50)
    quantity = 4
    discount_amount = (single_product_price * quantity * 2)
    discount_percentage = Decimal(1.9)  # 190%

    second_product = create_product(printable_gibberish(),
                                    shop=shop,
                                    supplier=supplier,
                                    default_price=single_product_price)

    # the expected discount amount should not be greater than the products
    expected_discount_amount = basket.create_price(
        single_product_price) * quantity

    category = CategoryFactory()

    # create basket rule that requires 2 products in basket
    product = create_product(printable_gibberish(),
                             shop=shop,
                             supplier=supplier,
                             default_price=single_product_price)
    ShopProduct.objects.get(shop=shop,
                            product=product).categories.add(category)
    basket.add_product(supplier=supplier,
                       shop=shop,
                       product=product,
                       quantity=quantity)
    basket.shipping_method = get_shipping_method(shop=shop)
    basket.save()

    rule = ProductsInBasketCondition.objects.create(quantity=2)
    rule.products.add(product)
    rule.save()

    # BasketCampaign
    campaign = BasketCampaign.objects.create(active=True,
                                             shop=shop,
                                             name="test",
                                             public_name="test")
    campaign.conditions.add(rule)

    # effect 1 - categories from products
    DiscountFromCategoryProducts.objects.create(
        campaign=campaign,
        discount_percentage=discount_percentage,
        category=category)

    # effect 2 - discount from products
    effect2 = DiscountFromProduct.objects.create(
        campaign=campaign, discount_amount=discount_amount)
    effect2.products.add(product)

    # effect 3 - basket discount
    BasketDiscountAmount.objects.create(campaign=campaign,
                                        discount_amount=discount_amount)

    # effect 4 - basket discount percetage
    BasketDiscountPercentage.objects.create(
        campaign=campaign, discount_percentage=discount_percentage)

    basket.uncache()

    final_lines = basket.get_final_lines()
    assert len(final_lines) == 3

    original_price = basket.create_price(single_product_price) * quantity
    line = final_lines[0]
    assert line.discount_amount == expected_discount_amount
    assert basket.total_price == original_price - expected_discount_amount

    # effect free - aaaww yes, it's free
    effect_free = FreeProductLine.objects.create(campaign=campaign, quantity=1)
    effect_free.products.add(second_product)

    basket.uncache()

    final_lines = basket.get_final_lines()
    assert len(final_lines) == 4
    line = final_lines[0]
    assert line.discount_amount == expected_discount_amount
    assert basket.total_price == original_price - expected_discount_amount

    # CatalogCampaign
    catalog_campaign = CatalogCampaign.objects.create(active=True,
                                                      shop=shop,
                                                      name="test2",
                                                      public_name="test")
    product_filter = ProductFilter.objects.create()
    product_filter.products.add(product)
    catalog_campaign.filters.add(product_filter)

    # effect 5 - ProductDiscountAmount
    ProductDiscountAmount.objects.create(campaign=catalog_campaign,
                                         discount_amount=discount_amount)

    # effct 6 - ProductDiscountPercentage
    ProductDiscountPercentage.objects.create(
        campaign=catalog_campaign, discount_percentage=discount_percentage)

    basket.uncache()

    final_lines = basket.get_final_lines()
    assert len(final_lines) == 4
    line = final_lines[0]
    assert line.discount_amount == expected_discount_amount
    assert basket.total_price == original_price - expected_discount_amount
def test_order_received_variables(rf, with_shop_contact):
    activate("en")
    shop = get_shop(True)
    contact_address = get_address(**SHOP_ADDRESS_DATA)
    contact_address.save()
    shop.contact_address = contact_address
    shop.save()

    get_default_product()
    get_default_supplier(shop)

    STEP_DATA = [{
        "cond_op": "all",
        "enabled": True,
        "next": "continue",
        "actions": [{
            "template_data": {
                "en": {
                    "body": "{{ customer_email }}",
                    "content_type": "plain",
                    "subject": "{{ customer_phone }}"
                }
            },
            "identifier": "send_email",
            "language": {
                "constant": "en"
            },
            "recipient": {
                "constant": "*****@*****.**"
            }
        }]
    }]

    if with_shop_contact:
        STEP_DATA[0]['actions'].insert(0, {
            "template_data": {
                "en": {
                    "body": "{{ shop_email }}",
                    "content_type": "plain",
                    "subject": "{{ shop_phone }}"
                }
            },
            "identifier": "send_email",
            "language": {
                "constant": "en"
            },
            "recipient": {
                "constant": "*****@*****.**"
            }
        })

    sc = Script.objects.create(
        name="variables script", event_identifier="order_received", enabled=True, shop=shop)
    sc.set_serialized_steps(STEP_DATA)
    sc.save()

    n_outbox_pre = len(mail.outbox)
    customer = create_random_person(locale='en')
    address = get_address(**DEFAULT_ADDRESS_DATA)
    address.save()
    customer.default_shipping_address = address
    customer.save()

    order = create_random_order(customer, shop=shop)
    assert (len(mail.outbox) == n_outbox_pre + (2 if with_shop_contact else 1)), "Sending email failed"

    latest_mail = mail.outbox[-1]
    assert latest_mail.subject == customer.default_shipping_address.phone
    assert latest_mail.body == customer.default_shipping_address.email

    if with_shop_contact:
        # shop email is sent first - we use insert(0, shop_step_data)
        penult_mail = mail.outbox[-2]
        assert penult_mail.subject == shop.contact_address.phone
        assert penult_mail.body == shop.contact_address.email
def test_product_total_sales_report(rf, admin_user, order_by):
    with override_provides("reports", [
            "shuup.default_reports.reports.product_total_sales:ProductSalesReport"
    ]):
        shop = get_default_shop()
        supplier = get_default_supplier()
        product1 = create_product("product1", supplier=supplier, shop=shop)
        product2 = create_product("product2", supplier=supplier, shop=shop)

        p1_qtd, p1_price, p1_tr, p1_lines = Decimal(3), Decimal(5), Decimal(
            0), 5
        p2_qtd, p2_price, p2_tr, p2_lines = Decimal(4), Decimal(5), Decimal(
            0.95), 3

        order = create_order_with_product(product=product1,
                                          supplier=supplier,
                                          quantity=p1_qtd,
                                          taxless_base_unit_price=p1_price,
                                          tax_rate=p1_tr,
                                          n_lines=p1_lines,
                                          shop=shop)
        order.create_payment(order.taxful_total_price.amount)

        order2 = create_order_with_product(product=product2,
                                           supplier=supplier,
                                           quantity=p2_qtd,
                                           taxless_base_unit_price=p2_price,
                                           tax_rate=p2_tr,
                                           n_lines=p2_lines,
                                           shop=shop)
        order2.create_payment(order2.taxful_total_price.amount)

        data = {
            "report": ProductSalesReport.get_name(),
            "shop": shop.pk,
            "date_range": DateRangeChoices.ALL_TIME.value,
            "writer": "json",
            "force_download": 1,
            "order_by": order_by
        }

        view = ReportView.as_view()
        request = apply_request_middleware(rf.post("/", data=data),
                                           user=admin_user)
        response = view(request)
        if hasattr(response, "render"):
            response.render()
        assert response.status_code == 200
        json_data = json.loads(response.content.decode("utf-8"))
        assert force_text(ProductSalesReport.title) in json_data.get("heading")

        data = json_data["tables"][0]["data"]
        assert len(data) == 2

        p1_total_qtd = p1_qtd * p1_lines
        p1_taxless_total = p1_total_qtd * p1_price
        p1_taxful_total = p1_taxless_total * (1 + p1_tr)

        p2_total_qtd = p2_qtd * p2_lines
        p2_taxless_total = p2_total_qtd * p2_price
        p2_taxful_total = p2_taxless_total * (1 + p2_tr)

        if order_by == "quantity":
            p1 = data[0]
            p2 = data[1]

        elif order_by == "taxless_total":
            p1 = data[0]
            p2 = data[1]

        else:  # order_by == "taxful_total":
            p1 = data[1]
            p2 = data[0]

        precision = Decimal('0.1')**2

        assert p1["product"] == product1.name
        assert Decimal(p1["quantity"]) == p1_total_qtd
        assert Decimal(
            p1["taxless_total"]) == p1_taxless_total.quantize(precision)
        assert Decimal(
            p1["taxful_total"]) == p1_taxful_total.quantize(precision)

        assert p2["product"] == product2.name
        assert Decimal(p2["quantity"]) == p2_total_qtd
        assert Decimal(
            p2["taxless_total"]) == p2_taxless_total.quantize(precision)
        assert Decimal(
            p2["taxful_total"]) == p2_taxful_total.quantize(precision)
Example #46
0
def get_frontend_order_state(contact, valid_lines=True):
    """
    Get a dict structure mirroring what the frontend JavaScript would submit.
    :type contact: Contact|None
    """
    translation.activate("en")
    shop = get_default_shop()
    tax = Tax.objects.create(code="test_code",
                             rate=decimal.Decimal("0.20"),
                             name="Default")
    tax_class = TaxClass.objects.create(identifier="test_tax_class",
                                        name="Default")
    rule = TaxRule.objects.create(tax=tax)
    rule.tax_classes.add(tax_class)
    rule.save()
    product = create_product(sku=printable_gibberish(),
                             supplier=get_default_supplier(),
                             shop=shop)
    product.tax_class = tax_class
    product.save()
    if valid_lines:
        lines = [
            {
                "id": "x",
                "type": "product",
                "product": {
                    "id": product.id
                },
                "quantity": "32",
                "baseUnitPrice": 50
            },
            {
                "id": "y",
                "type": "other",
                "sku": "hello",
                "text": "A greeting",
                "quantity": 1,
                "unitPrice": "5.5"
            },
            {
                "id": "z",
                "type": "text",
                "text": "This was an order!",
                "quantity": 0
            },
        ]
    else:
        unshopped_product = create_product(sku=printable_gibberish(),
                                           supplier=get_default_supplier())
        not_visible_product = create_product(sku=printable_gibberish(),
                                             supplier=get_default_supplier(),
                                             shop=shop)
        not_visible_shop_product = not_visible_product.get_shop_instance(shop)
        not_visible_shop_product.visible = False
        not_visible_shop_product.save()
        lines = [
            {
                "id": "x",
                "type": "product"
            },  # no product?
            {
                "id": "x",
                "type": "product",
                "product": {
                    "id": unshopped_product.id
                }
            },  # not in this shop?
            {
                "id": "y",
                "type": "product",
                "product": {
                    "id": -product.id
                }
            },  # invalid product?
            {
                "id": "z",
                "type": "other",
                "quantity": 1,
                "unitPrice": "q"
            },  # what's that price?
            {
                "id": "rr",
                "type": "product",
                "quantity": 1,
                "product": {
                    "id": not_visible_product.id
                }
            }  # not visible
        ]

    state = {
        "customer": {
            "id": contact.id if contact else None
        },
        "lines": lines,
        "methods": {
            "shippingMethod": {
                "id": get_default_shipping_method().id
            },
            "paymentMethod": {
                "id": get_default_payment_method().id
            },
        },
        "shop": {
            "selected": {
                "id": shop.id,
                "name": shop.name,
                "currency": shop.currency,
                "priceIncludeTaxes": shop.prices_include_tax
            }
        }
    }
    return state
Example #47
0
def test_basket_free_product(rf):
    request, shop, _ = initialize_test(rf, False)

    basket = get_basket(request)
    supplier = get_default_supplier()

    single_product_price = "50"
    original_quantity = 2
    # create basket rule that requires 2 products in basket
    product = create_product(printable_gibberish(),
                             shop=shop,
                             supplier=supplier,
                             default_price=single_product_price)
    basket.add_product(supplier=supplier,
                       shop=shop,
                       product=product,
                       quantity=2)
    basket.shipping_method = get_shipping_method(shop=shop)
    basket.save()

    second_product = create_product(printable_gibberish(),
                                    shop=shop,
                                    supplier=supplier,
                                    default_price=single_product_price)

    # no shop
    third_product = create_product(printable_gibberish(), supplier=supplier)

    rule = BasketTotalProductAmountCondition.objects.create(value="2")

    campaign = BasketCampaign.objects.create(active=True,
                                             shop=shop,
                                             name="test",
                                             public_name="test")
    campaign.conditions.add(rule)

    effect = FreeProductLine.objects.create(campaign=campaign, quantity=2)
    effect.products.add(second_product)
    discount_lines_count = len(effect.get_discount_lines(basket, []))
    assert discount_lines_count == 1

    # do not affect as there is no shop product for the product
    effect.products.add(third_product)
    assert len(effect.get_discount_lines(basket, [])) == discount_lines_count

    basket.uncache()
    final_lines = basket.get_final_lines()

    assert len(final_lines) == 3

    line_types = [l.type for l in final_lines]
    assert OrderLineType.DISCOUNT not in line_types

    for line in basket.get_final_lines():
        assert line.type in [OrderLineType.PRODUCT, OrderLineType.SHIPPING]
        if line.type == OrderLineType.SHIPPING:
            continue
        if line.product != product:
            assert line.product == second_product
            assert line.line_source == LineSource.DISCOUNT_MODULE
            assert line.quantity == original_quantity
        else:
            assert line.line_source == LineSource.CUSTOMER
Example #48
0
def test_product_catalog_cgp_with_variations():
    shop = factories.get_default_shop()
    supplier = factories.get_default_supplier()
    contact = factories.create_random_person()
    group = PersonContact.get_default_group()
    contact.groups.add(group)
    parent = factories.create_product("p1",
                                      shop=shop,
                                      supplier=supplier,
                                      default_price=Decimal("10"))
    child1 = factories.create_product("p2",
                                      shop=shop,
                                      supplier=supplier,
                                      default_price=Decimal("20"))
    child2 = factories.create_product("p3",
                                      shop=shop,
                                      supplier=supplier,
                                      default_price=Decimal("40"))
    child3 = factories.create_product("p4",
                                      shop=shop,
                                      supplier=supplier,
                                      default_price=Decimal("50"))

    child1.link_to_parent(parent)
    child2.link_to_parent(parent)
    child3.link_to_parent(parent)

    # set a price for child2
    CgpPrice.objects.create(shop=shop,
                            product=child2,
                            group=group,
                            price_value=Decimal("5"))
    # create a discount for child3
    CgpDiscount.objects.create(shop=shop,
                               product=child3,
                               group=group,
                               discount_amount_value=Decimal("35"))

    catalog = ProductCatalog(
        context=ProductCatalogContext(purchasable_only=False, contact=contact))
    ProductCatalog.index_product(parent)

    _assert_products_queryset(
        catalog,
        [
            (child2.pk, Decimal("5"), None),
            (parent.pk, Decimal("10"), None),
            (child1.pk, Decimal("20"), None),
            (child3.pk, Decimal("50"), Decimal("15")),
        ],
    )
    _assert_shop_products_queryset(
        catalog,
        [
            (child2.get_shop_instance(shop).pk, Decimal("5"), None),
            (parent.get_shop_instance(shop).pk, Decimal("10"), None),
            (child1.get_shop_instance(shop).pk, Decimal("20"), None),
            (child3.get_shop_instance(shop).pk, Decimal("50"), Decimal("15")),
        ],
    )
    # no customer
    _assert_price(parent, shop, Decimal("10"), Decimal("10"))
    _assert_price(child1, shop, Decimal("20"), Decimal("20"))
    _assert_price(child2, shop, Decimal("40"), Decimal("40"))
    _assert_price(child3, shop, Decimal("50"), Decimal("50"))
    # with the customer in the group
    _assert_price(parent, shop, Decimal("10"), Decimal("10"), customer=contact)
    _assert_price(child1, shop, Decimal("20"), Decimal("20"), customer=contact)
    _assert_price(child2, shop, Decimal("5"), Decimal("40"), customer=contact)
    _assert_price(child3, shop, Decimal("15"), Decimal("50"), customer=contact)
Example #49
0
def test_product_category_discount_amount_with_minimum_price(rf):
    # Buy X amount of Y get Z discount from Y
    request, shop, _ = initialize_test(rf, False)

    basket = get_basket(request)
    supplier = get_default_supplier()

    single_product_price = Decimal("50")
    single_product_min_price = Decimal("40")
    discount_amount_value = Decimal("200")  # will exceed the minimum price
    quantity = 2

    # the expected discount amount should not be greater than the products
    expected_discount_amount = basket.create_price(
        single_product_price) * quantity

    category = CategoryFactory()

    # create basket rule that requires 2 products in basket
    product = create_product(printable_gibberish(),
                             shop=shop,
                             supplier=supplier,
                             default_price=single_product_price)
    shop_product = ShopProduct.objects.get(shop=shop, product=product)
    shop_product.minimum_price_value = single_product_min_price
    shop_product.save()
    shop_product.categories.add(category)

    basket.add_product(supplier=supplier,
                       shop=shop,
                       product=product,
                       quantity=quantity)
    basket.shipping_method = get_shipping_method(shop=shop)
    basket.save()

    rule = ProductsInBasketCondition.objects.create(quantity=2)
    rule.products.add(product)
    rule.save()

    campaign = BasketCampaign.objects.create(active=True,
                                             shop=shop,
                                             name="test",
                                             public_name="test")
    campaign.conditions.add(rule)

    DiscountFromCategoryProducts.objects.create(
        campaign=campaign,
        discount_amount=discount_amount_value,
        category=category)
    assert rule.matches(basket, [])
    basket.uncache()

    # the discount amount should not exceed the minimum price. as the configued discount
    # will exceed, it should limit the discount amount
    final_lines = basket.get_final_lines()
    expected_discount_amount = basket.create_price(
        (single_product_price - single_product_min_price) * quantity)
    original_price = basket.create_price(single_product_price) * quantity
    line = final_lines[0]
    assert line.discount_amount == expected_discount_amount
    assert basket.total_price == original_price - expected_discount_amount
Example #50
0
def test_create_product_with_shop_product_and_attributes(admin_user):
    shop = get_default_shop()
    client = _get_client(admin_user)
    supplier = get_default_supplier()

    cat = Category.objects.create(status=CategoryStatus.VISIBLE,
                                  visibility=CategoryVisibility.VISIBLE_TO_ALL,
                                  identifier="test_category",
                                  name="Test")
    product_type = get_default_product_type()

    assert Attribute.objects.count() > 0

    assert Product.objects.count() == 0

    attributes_data = []

    expected_values = {
        "untranslated_string_value": "test value",
        "numeric_value": 12,
        "boolean_value": True,
        "timedelta_value": "200",  # seconds
        "datetime_value": "2017-01-01 01:00:00",
        "translated_string_value": "translated string value"
    }

    for spec in ATTR_SPECS:
        attr = Attribute.objects.get(identifier=spec["identifier"])
        attr_data = {
            "numeric_value": None,
            "datetime_value": None,
            "untranslated_string_value": "",
            "attribute": attr.pk,
            # "product": product.pk
        }

        if attr.is_stringy:
            if attr.is_translated:
                attr_data["translations"] = {
                    "en": {
                        "translated_string_value":
                        expected_values["translated_string_value"]
                    }
                }
            else:
                attr_data["untranslated_string_value"] = expected_values[
                    "untranslated_string_value"]
        elif attr.is_numeric:
            if attr.type == AttributeType.BOOLEAN:
                attr_data["numeric_value"] = int(
                    expected_values["boolean_value"])
            elif attr.type == AttributeType.TIMEDELTA:
                attr_data["numeric_value"] = int(
                    expected_values["timedelta_value"])
            else:
                attr_data["numeric_value"] = expected_values["numeric_value"]
        elif attr.is_temporal:
            attr_data["datetime_value"] = expected_values["datetime_value"]

        attributes_data.append(attr_data)

    data = _get_product_sample_data()
    data["shop_products"] = _get_sample_shop_product_data(shop, cat, supplier)
    data["attributes"] = attributes_data
    response = client.post("/api/shuup/product/",
                           content_type="application/json",
                           data=json.dumps(data))
    assert response.status_code == status.HTTP_201_CREATED

    # check all
    for lang in ("en", "pt-br"):
        activate(lang)
        product = Product.objects.first()
        _check_product_basic_data(product, data, lang)

    assert Product.objects.count() == 1
    assert ShopProduct.objects.count() == 1

    product = Product.objects.first()
    shop_product = ShopProduct.objects.first()

    assert product.get_shop_instance(shop) == shop_product
    assert supplier in shop_product.suppliers.all()
    assert cat in shop_product.categories.all()
    assert shop_product.primary_category == cat

    # validate attribute values
    for spec in ATTR_SPECS:
        attribute = Attribute.objects.get(identifier=spec["identifier"])
        attr = ProductAttribute.objects.get(product=product,
                                            attribute=attribute)
        if attribute.is_stringy:
            if attribute.is_translated:
                attr.set_current_language("en")
                assert attr.value == expected_values["translated_string_value"]
            else:
                assert attr.value == expected_values[
                    "untranslated_string_value"]
        elif attribute.is_numeric:
            if attribute.type == AttributeType.BOOLEAN:
                assert attr.value == expected_values["boolean_value"]
            elif attribute.type == AttributeType.TIMEDELTA:
                assert attr.value == datetime.timedelta(
                    seconds=int(expected_values["timedelta_value"]))
            else:
                assert attr.value == expected_values["numeric_value"]
        elif attribute.is_temporal:
            dt_value = expected_values["datetime_value"]
            parsed_dt = dt.strptime(dt_value, "%Y-%m-%d %H:%M:%S")
            assert attr.value.year == parsed_dt.year
            assert attr.value.month == parsed_dt.month
            assert attr.value.day == parsed_dt.day
Example #51
0
def test_order_create_without_shipping_or_billing_method(admin_user):
    create_default_order_statuses()
    shop = get_default_shop()
    contact = create_random_person(locale="en_US", minimum_name_comp_len=5)
    product = create_product(sku=printable_gibberish(),
                             supplier=get_default_supplier(),
                             shop=shop)
    assert not Order.objects.count()
    client = _get_client(admin_user)
    lines = [
        {
            "type": "product",
            "product": product.id,
            "quantity": "1",
            "base_unit_price_value": "5.00"
        },
        {
            "type": "product",
            "product": product.id,
            "quantity": "2",
            "base_unit_price_value": "1.00",
            "discount_amount_value": "0.50"
        },
        {
            "type": "other",
            "sku": "hello",
            "text": "A greeting",
            "quantity": 1,
            "base_unit_price_value": "3.5"
        },
        {
            "type": "text",
            "text": "This was an order!",
            "quantity": 0
        },
    ]
    response = client.post("/api/shuup/order/",
                           content_type="application/json",
                           data=json.dumps({
                               "shop": shop.pk,
                               "customer": contact.pk,
                               "lines": lines
                           }))
    assert response.status_code == 201
    assert Order.objects.count() == 1
    order = Order.objects.first()
    assert order.shop == shop
    assert order.shipping_method is None
    assert order.payment_method is None
    assert order.customer == contact
    assert order.creator == admin_user
    assert order.billing_address == contact.default_billing_address.to_immutable(
    )
    assert order.shipping_address == contact.default_shipping_address.to_immutable(
    )
    assert order.payment_status == PaymentStatus.NOT_PAID
    assert order.shipping_status == ShippingStatus.NOT_SHIPPED
    assert order.status == OrderStatus.objects.get_default_initial()
    assert order.taxful_total_price_value == decimal.Decimal(10)
    assert order.lines.count() == 4  # 2 product lines, 2 other lines
    for idx, line in enumerate(order.lines.all()[:4]):
        assert line.quantity == decimal.Decimal(lines[idx].get("quantity"))
        assert line.base_unit_price_value == decimal.Decimal(lines[idx].get(
            "base_unit_price_value", 0))
        assert line.discount_amount_value == decimal.Decimal(lines[idx].get(
            "discount_amount_value", 0))
Example #52
0
def test_product_catalog_discounted_price():
    shop = factories.get_default_shop()
    supplier = factories.get_default_supplier()
    contact = factories.create_random_person()
    group = PersonContact.get_default_group()
    contact.groups.add(group)
    product1 = factories.create_product("p1",
                                        shop=shop,
                                        supplier=supplier,
                                        default_price=Decimal("50"))
    product2 = factories.create_product("p2",
                                        shop=shop,
                                        supplier=supplier,
                                        default_price=Decimal("30"))

    # set price for product2
    CgpPrice.objects.create(shop=shop,
                            product=product2,
                            group=group,
                            price_value=Decimal(25))
    # create a discount for product2
    CgpDiscount.objects.create(shop=shop,
                               product=product2,
                               group=group,
                               discount_amount_value=Decimal(7))

    anon_catalog = ProductCatalog(context=ProductCatalogContext(
        purchasable_only=False))
    customer_catalog = ProductCatalog(
        context=ProductCatalogContext(purchasable_only=False, contact=contact))
    ProductCatalog.index_product(product1)
    ProductCatalog.index_product(product2)

    _assert_products_queryset(
        anon_catalog,
        [
            (product2.pk, Decimal("30"), None),
            (product1.pk, Decimal("50"), None),
        ],
    )
    _assert_products_queryset(
        customer_catalog,
        [
            (product2.pk, Decimal("25"), Decimal("23")),
            (product1.pk, Decimal("50"), None),
        ],
    )
    _assert_shop_products_queryset(
        anon_catalog,
        [
            (product2.get_shop_instance(shop).pk, Decimal("30"), None),
            (product1.get_shop_instance(shop).pk, Decimal("50"), None),
        ],
    )
    _assert_shop_products_queryset(
        customer_catalog,
        [
            (product2.get_shop_instance(shop).pk, Decimal("25"),
             Decimal("23")),
            (product1.get_shop_instance(shop).pk, Decimal("50"), None),
        ],
    )
    # no customer
    _assert_price(product1, shop, Decimal("50"), Decimal("50"))
    _assert_price(product2, shop, Decimal("30"), Decimal("30"))
    # with the customer in the group
    _assert_price(product1,
                  shop,
                  Decimal("50"),
                  Decimal("50"),
                  customer=contact)
    _assert_price(product2,
                  shop,
                  Decimal("18"),
                  Decimal("30"),
                  customer=contact)
Example #53
0
def test_filter_caching(rf):
    request, shop, group = initialize_test(rf, False)
    price = shop.create_price
    product_price = "100"
    discount_percentage = "0.30"

    supplier = get_default_supplier()
    product = create_product(printable_gibberish(),
                             shop=shop,
                             supplier=supplier,
                             default_price=product_price)
    product2 = create_product(printable_gibberish(),
                              shop=shop,
                              supplier=supplier,
                              default_price=product_price)

    assert product.pk != product2.pk  # ensure they're different

    # create catalog campaign
    catalog_filter = ProductFilter.objects.create()
    catalog_filter.products.add(product)

    catalog_campaign = CatalogCampaign.objects.create(shop=shop,
                                                      active=True,
                                                      name="test")
    catalog_campaign.filters.add(catalog_filter)
    assert CatalogFilterCachedShopProduct.objects.count() == 1
    catalog_campaign.save()
    assert CatalogFilterCachedShopProduct.objects.count() == 1

    entry = CatalogFilterCachedShopProduct.objects.first()
    assert entry.pk == get_matching_catalog_filters(
        product.get_shop_instance(shop))[0]

    # create another campaign
    catalog_filter2 = ProductFilter.objects.create()
    catalog_filter2.products.add(product2)
    catalog_campaign2 = CatalogCampaign.objects.create(shop=shop,
                                                       active=True,
                                                       name="test")
    catalog_campaign2.filters.add(catalog_filter2)
    assert CatalogFilterCachedShopProduct.objects.count() == 2
    catalog_campaign2.save()
    assert CatalogFilterCachedShopProduct.objects.count(
    ) == 2  # new cache for this product was created

    entry = CatalogFilterCachedShopProduct.objects.last()
    assert entry.pk == get_matching_catalog_filters(
        product2.get_shop_instance(shop))[0]

    # third campaign
    catalog_filter3 = ProductFilter.objects.create()
    catalog_filter3.products.add(product2)
    catalog_campaign3 = CatalogCampaign.objects.create(shop=shop,
                                                       active=True,
                                                       name="test")
    catalog_campaign3.filters.add(catalog_filter3)
    assert CatalogFilterCachedShopProduct.objects.count() == 3
    catalog_campaign3.save()
    assert CatalogFilterCachedShopProduct.objects.count(
    ) == 3  # new one for this filter again

    expected = get_matching_catalog_filters(product2.get_shop_instance(shop))
    for id in expected:
        assert id in [catalog_filter2.pk, catalog_filter3.pk]
Example #54
0
def test_create_order(admin_user, currency):
    create_default_order_statuses()
    shop = get_default_shop()
    shop.currency = currency
    tax = get_default_tax()
    Currency.objects.get_or_create(code=currency, decimal_places=2)
    shop.save()
    sm = get_default_shipping_method()
    pm = get_default_payment_method()
    contact = create_random_person(locale="en_US", minimum_name_comp_len=5)
    default_group = get_default_customer_group()
    default_group.members.add(contact)
    account_manager = create_random_person(locale="en_US",
                                           minimum_name_comp_len=5)
    contact.account_manager = account_manager
    contact.save()

    product = create_product(sku=printable_gibberish(),
                             supplier=get_default_supplier(),
                             shop=shop)
    assert not Order.objects.count()
    client = _get_client(admin_user)
    lines = [
        {
            "type": "product",
            "product": product.id,
            "quantity": "1",
            "base_unit_price_value": "5.00"
        },
        {
            "type": "product",
            "product": product.id,
            "quantity": "2",
            "base_unit_price_value": "1.00",
            "discount_amount_value": "0.50"
        },
        {
            "type": "other",
            "sku": "hello",
            "text": "A greeting",
            "quantity": 1,
            "base_unit_price_value": "3.5"
        },
        {
            "type": "text",
            "text": "This was an order!",
            "quantity": 0
        },
    ]
    response = client.post("/api/shuup/order/",
                           content_type="application/json",
                           data=json.dumps({
                               "shop": shop.pk,
                               "shipping_method": sm.pk,
                               "payment_method": pm.pk,
                               "customer": contact.pk,
                               "lines": lines
                           }))
    assert response.status_code == 201
    assert Order.objects.count() == 1
    order = Order.objects.first()
    assert order.shop == shop
    assert order.shipping_method == sm
    assert order.payment_method == pm
    assert order.customer == contact
    assert order.creator == admin_user
    assert order.billing_address == contact.default_billing_address.to_immutable(
    )
    assert order.shipping_address == contact.default_shipping_address.to_immutable(
    )
    assert order.payment_status == PaymentStatus.NOT_PAID
    assert order.shipping_status == ShippingStatus.NOT_SHIPPED
    assert order.status == OrderStatus.objects.get_default_initial()
    assert order.taxful_total_price_value == decimal.Decimal(10)
    assert order.lines.count(
    ) == 6  # shipping line, payment line, 2 product lines, 2 other lines
    assert order.currency == currency
    for idx, line in enumerate(order.lines.all()[:4]):
        assert line.quantity == decimal.Decimal(lines[idx].get("quantity"))
        assert line.base_unit_price_value == decimal.Decimal(lines[idx].get(
            "base_unit_price_value", 0))
        assert line.discount_amount_value == decimal.Decimal(lines[idx].get(
            "discount_amount_value", 0))

    # Test tax summary
    response_data = json.loads(response.content.decode("utf-8"))
    # Tax summary should not be present here
    assert "summary" not in response_data

    response = client.get('/api/shuup/order/{}/taxes/'.format(order.pk))
    assert response.status_code == status.HTTP_200_OK
    response_data = json.loads(response.content.decode("utf-8"))

    assert "lines" in response_data
    assert "summary" in response_data
    line_summary = response_data["lines"]
    summary = response_data["summary"]
    first_tax_summary = summary[0]

    assert int(first_tax_summary["tax_id"]) == tax.id
    assert first_tax_summary["tax_rate"] == tax.rate

    first_line_summary = line_summary[0]
    assert "tax" in first_line_summary

    response = client.get("/api/shuup/order/%s/" % order.id)
    assert response.status_code == status.HTTP_200_OK
    order_data = json.loads(response.content.decode("utf-8"))
    assert order_data.get("id") == order.id

    assert "available_shipping_methods" in order_data
    assert "available_payment_methods" in order_data

    assert order_data["available_payment_methods"][0]["id"] == pm.id
    assert order_data["available_shipping_methods"][0]["id"] == sm.id

    assert order.account_manager == account_manager
    assert order.customer_groups.count() == contact.groups.count()
    for group in order.customer_groups.all():
        assert contact.groups.filter(id=group.id).exists()

    assert order.tax_group is not None
    assert order.tax_group == contact.tax_group
def test_customer_sales_report(rf, order_by):
    shop = get_default_shop()
    supplier = get_default_supplier()
    product1 = create_product("p1", shop=shop, supplier=supplier)
    product2 = create_product("p2", shop=shop, supplier=supplier)
    product3 = create_product("p3", shop=shop, supplier=supplier)
    tax_rate = Decimal("0.3")

    # orders for person 1
    person1 = create_random_person()
    order1 = create_order_with_product(product=product1,
                                       supplier=supplier,
                                       quantity=2,
                                       taxless_base_unit_price="5",
                                       tax_rate=tax_rate,
                                       n_lines=1,
                                       shop=shop)
    order1.customer = person1
    order1.save()
    order2 = create_order_with_product(product=product2,
                                       supplier=supplier,
                                       quantity=1,
                                       taxless_base_unit_price="10",
                                       n_lines=1,
                                       shop=shop)
    order2.customer = person1
    order2.save()

    person1_taxful_total_sales = (order1.taxful_total_price +
                                  order2.taxful_total_price)
    person1_taxless_total_sales = (order1.taxless_total_price +
                                   order2.taxless_total_price)
    person1_avg_sales = (person1_taxful_total_sales / Decimal(2.0))

    # orders for person 2
    person2 = create_random_person()
    order3 = create_order_with_product(product=product1,
                                       supplier=supplier,
                                       quantity=2,
                                       taxless_base_unit_price="5",
                                       tax_rate=tax_rate,
                                       n_lines=1,
                                       shop=shop)
    order3.customer = person2
    order3.save()

    order4 = create_order_with_product(product=product2,
                                       supplier=supplier,
                                       quantity=2,
                                       taxless_base_unit_price="50",
                                       n_lines=1,
                                       shop=shop)
    order4.customer = person2
    order4.save()

    order5 = create_order_with_product(product=product3,
                                       supplier=supplier,
                                       quantity=2,
                                       taxless_base_unit_price="20",
                                       tax_rate=tax_rate,
                                       n_lines=1,
                                       shop=shop)
    order5.customer = person2
    order5.save()
    person2_taxful_total_sales = (order3.taxful_total_price +
                                  order4.taxful_total_price +
                                  order5.taxful_total_price)
    person2_taxless_total_sales = (order3.taxless_total_price +
                                   order4.taxless_total_price +
                                   order5.taxless_total_price)
    person2_avg_sales = (person2_taxful_total_sales / Decimal(3.0)).quantize(
        Decimal('0.01'))

    # pay orders
    [o.create_payment(o.taxful_total_price) for o in Order.objects.all()]

    data = {
        "report": CustomerSalesReport.get_name(),
        "shop": shop.pk,
        "date_range": DateRangeChoices.ALL_TIME,
        "writer": "json",
        "force_download": 1,
        "order_by": order_by
    }
    report = CustomerSalesReport(**data)
    writer = get_writer_instance(data["writer"])
    response = writer.get_response(report=report)
    if hasattr(response, "render"):
        response.render()
    json_data = json.loads(response.content.decode("utf-8"))
    assert force_text(CustomerSalesReport.title) in json_data.get("heading")
    data = json_data.get("tables")[0].get("data")

    assert len(data) == 2

    if order_by == "order_count":
        person1_data = data[1]
        person2_data = data[0]

    elif order_by == "average_sales":
        if person1_avg_sales > person2_avg_sales:
            person1_data = data[0]
            person2_data = data[1]
        else:
            person1_data = data[1]
            person2_data = data[0]

    elif order_by == "taxless_total":
        if person1_taxless_total_sales > person2_taxless_total_sales:
            person1_data = data[0]
            person2_data = data[1]
        else:
            person1_data = data[1]
            person2_data = data[0]

    elif order_by == "taxful_total":
        if person1_taxful_total_sales > person2_taxful_total_sales:
            person1_data = data[0]
            person2_data = data[1]
        else:
            person1_data = data[1]
            person2_data = data[0]

    assert person1_data["customer"] == person1.name
    assert person1_data["order_count"] == "2"
    assert person1_data["average_sales"] == str(person1_avg_sales.value)
    assert person1_data["taxless_total"] == str(
        person1_taxless_total_sales.value.quantize(Decimal("0.01")))
    assert person1_data["taxful_total"] == str(
        person1_taxful_total_sales.value.quantize(Decimal("0.01")))

    assert person2_data["customer"] == person2.name
    assert person2_data["order_count"] == "3"
    assert person2_data["average_sales"] == str(person2_avg_sales.value)
    assert person2_data["taxless_total"] == str(
        person2_taxless_total_sales.value.quantize(Decimal("0.01")))
    assert person2_data["taxful_total"] == str(
        person2_taxful_total_sales.value.quantize(Decimal("0.01")))
Example #56
0
def _create_total_sales(shop, day):
    product = create_product("test", shop=shop)
    supplier = get_default_supplier()
    order = create_order_with_product(product, supplier, 1, 10, shop=shop)
    order.order_date = day
    order.save()
def test_taxes_report(rf):
    shop = get_default_shop()
    supplier = get_default_supplier()
    product1 = create_product("p1", shop=shop, supplier=supplier)
    product2 = create_product("p2", shop=shop, supplier=supplier)
    create_product("p3", shop=shop, supplier=supplier)
    tax_rate1 = Decimal("0.3")
    tax_rate2 = Decimal("0.45")

    tax_rate1_instance = get_test_tax(tax_rate1)
    tax_rate2_instance = get_test_tax(tax_rate2)

    # orders for person 1
    person1 = create_random_person()
    order1 = create_order_with_product(product=product1,
                                       supplier=supplier,
                                       quantity=2,
                                       taxless_base_unit_price="5",
                                       tax_rate=tax_rate1,
                                       n_lines=1,
                                       shop=shop)
    order1.customer = person1
    order1.save()
    order2 = create_order_with_product(product=product2,
                                       supplier=supplier,
                                       quantity=1,
                                       taxless_base_unit_price="10",
                                       tax_rate=tax_rate1,
                                       n_lines=1,
                                       shop=shop)
    order2.customer = person1
    order2.save()

    # orders for person 2
    person2 = create_random_person()
    order3 = create_order_with_product(product=product1,
                                       supplier=supplier,
                                       quantity=1,
                                       taxless_base_unit_price="2",
                                       tax_rate=tax_rate2,
                                       n_lines=1,
                                       shop=shop)
    order3.customer = person2
    order3.save()

    order4 = create_order_with_product(product=product2,
                                       supplier=supplier,
                                       quantity=2,
                                       taxless_base_unit_price="8",
                                       tax_rate=tax_rate1,
                                       n_lines=1,
                                       shop=shop)
    order4.customer = person2
    order4.save()

    # pay orders
    [o.create_payment(o.taxful_total_price) for o in Order.objects.all()]

    data = {
        "report": TaxesReport.get_name(),
        "shop": shop.pk,
        "date_range": DateRangeChoices.ALL_TIME,
        "writer": "json",
        "force_download": 1,
    }
    report = TaxesReport(**data)
    writer = get_writer_instance(data["writer"])
    response = writer.get_response(report=report)
    if hasattr(response, "render"):
        response.render()
    json_data = json.loads(response.content.decode("utf-8"))
    assert force_text(TaxesReport.title) in json_data.get("heading")
    data = json_data.get("tables")[0].get("data")
    assert len(data) == 2

    tax1_rate1_total = (
        (order1.taxful_total_price_value - order1.taxless_total_price_value) +
        (order2.taxful_total_price_value - order2.taxless_total_price_value) +
        (order4.taxful_total_price_value - order4.taxless_total_price_value))
    tax1_pretax_total = (order1.taxless_total_price_value +
                         order2.taxless_total_price_value +
                         order4.taxless_total_price_value)
    tax1_total = (order1.taxful_total_price_value +
                  order2.taxful_total_price_value +
                  order4.taxful_total_price_value)
    tax2_rate2_total = (order3.taxful_total_price_value -
                        order3.taxless_total_price_value)

    # the report data order is the total charged ascending
    expected_result = [{
        "tax": tax_rate2_instance.name,
        "tax_rate": tax_rate2,
        "order_count": 1,
        "total_pretax_amount": order3.taxless_total_price_value,
        "total_tax_amount": tax2_rate2_total,
        "total": order3.taxful_total_price_value,
    }, {
        "tax": tax_rate1_instance.name,
        "tax_rate": tax_rate1,
        "order_count": 3,
        "total_pretax_amount": tax1_pretax_total,
        "total_tax_amount": tax1_rate1_total,
        "total": tax1_total,
    }]
    for ix, tax in enumerate(data):
        assert tax["tax"] == expected_result[ix]["tax"]
        assert Decimal(tax["tax_rate"]
                       ) == expected_result[ix]["tax_rate"] * Decimal(100.0)
        assert tax["order_count"] == str(expected_result[ix]["order_count"])
        assert tax["total_tax_amount"] == str(
            expected_result[ix]["total_tax_amount"])
        assert tax["total_pretax_amount"] == str(
            expected_result[ix]["total_pretax_amount"])
        assert tax["total"] == str(expected_result[ix]["total"])
Example #58
0
def test_refund_errors(admin_user):
    shop = get_default_shop()
    supplier = get_default_supplier()
    product = create_product(
        "test-sku",
        shop=get_default_shop(),
        default_price=10,
    )
    tax_rate = Decimal("0.1")
    taxless_base_unit_price = shop.create_price(200)
    order = create_order_with_product(product, supplier, 3, taxless_base_unit_price, tax_rate, shop=shop)
    order.payment_status = PaymentStatus.DEFERRED
    order.cache_prices()
    order.save()

    assert len(order.lines.all()) == 1
    assert order.can_create_refund()
    assert not order.has_refunds()

    client = _get_client(admin_user)

    refund_url = "/api/shuup/order/%s/create_refund/" % order.id
    product_line = order.lines.first()

    # error 1 - max refundable limit
    data = {
        "refund_lines": [{
            "line": product_line.id,
            "quantity": 1000,
            "amount": 1,
            "restock_products": False
        }]
    }
    response = client.post(refund_url, data, format="json")
    assert response.status_code == status.HTTP_400_BAD_REQUEST
    assert "Refund exceeds quantity." in response.data

    # error 2 - max amount
    data = {
        "refund_lines": [{
            "line": product_line.id,
            "quantity": 1,
            "amount": 100000000,
            "restock_products": False
        }]
    }
    response = client.post(refund_url, data, format="json")
    assert response.status_code == status.HTTP_400_BAD_REQUEST
    assert "Refund exceeds amount." in response.data

    # error 3 - invalid amount
    data = {
        "refund_lines": [{
            "line": product_line.id,
            "quantity": 1,
            "amount": -10,
            "restock_products": False
        }]
    }
    response = client.post(refund_url, data, format="json")
    assert response.status_code == status.HTTP_400_BAD_REQUEST
    assert "Invalid refund amount." in response.data

    # create partial refund
    data = {
        "refund_lines": [{
            "line": product_line.id,
            "quantity": 1,
            "amount": 1,
            "restock_products": False
        }]
    }
    response = client.post(refund_url, data, format="json")
    assert response.status_code == status.HTTP_201_CREATED

    # error 4 - can't create full refund
    data = {"restock_products": False}
    response = client.post("/api/shuup/order/%s/create_full_refund/" % order.id, data, format="json")
    assert response.status_code == status.HTTP_400_BAD_REQUEST
    assert "It is not possible to create the refund." in response.data
Example #59
0
def test_refunds_for_discounted_order_lines():
    shop = get_default_shop()
    supplier = get_default_supplier()
    product = create_product(
        "test-sku",
        shop=get_default_shop(),
        default_price=10,
    )

    order = create_order_with_product(product, supplier, 2, 200, shop=shop)
    discount_line = OrderLine(order_id=order.id,
                              type=OrderLineType.DISCOUNT,
                              quantity=1,
                              discount_amount_value=Decimal("0.54321"))
    discount_line.save()
    order.lines.add(discount_line)

    # Lines without quantity shouldn't affect refunds
    other_line = OrderLine(order=order,
                           type=OrderLineType.OTHER,
                           text="This random line for textual information",
                           quantity=0)
    other_line.save()
    order.lines.add(other_line)

    product_line = order.lines.filter(type=OrderLineType.PRODUCT).first()
    product_line.discount_amount = TaxfulPrice(100, order.currency)
    product_line.save()
    taxful_price_with_discount = product_line.taxful_price
    order.cache_prices()
    order.save()

    assert product_line.base_price == TaxfulPrice(400, order.currency)
    assert taxful_price_with_discount == TaxfulPrice(300, order.currency)

    # try to refund only the product line - should fail since this would result in a negative total
    with pytest.raises(RefundExceedsAmountException):
        order.create_refund([{
            "line": product_line,
            "quantity": 2,
            "amount": taxful_price_with_discount.amount
        }])

    # try to refund the product line with a negative amount
    with pytest.raises(InvalidRefundAmountException):
        order.create_refund([{
            "line": product_line,
            "quantity": 1,
            "amount": -taxful_price_with_discount.amount
        }])
    # try to refund the discount line with a positive amount
    with pytest.raises(InvalidRefundAmountException):
        order.create_refund([{
            "line": discount_line,
            "quantity": 1,
            "amount": -discount_line.taxful_price.amount
        }])

    order.create_refund([
        {
            "line": discount_line,
            "quantity": 1,
            "amount": discount_line.taxful_price.amount
        },
        {
            "line": product_line,
            "quantity": 2,
            "amount": taxful_price_with_discount.amount
        },
    ])
    assert product_line.max_refundable_amount.value == 0
    assert discount_line.max_refundable_amount.value == 0
    assert order.taxful_total_price.value == 0

    order = create_order_with_product(product, supplier, 2, 200, shop=shop)
    discount_line = OrderLine(order_id=order.id,
                              type=OrderLineType.DISCOUNT,
                              quantity=1,
                              discount_amount_value=Decimal("0.54321"))
    discount_line.save()
    order.lines.add(discount_line)
    product_line = order.lines.filter(type=OrderLineType.PRODUCT).first()
    product_line.discount_amount = TaxfulPrice(100, order.currency)
    product_line.save()

    # Lines without quantity shouldn't affect refunds
    other_line = OrderLine(order=order,
                           type=OrderLineType.OTHER,
                           text="This random line for textual information",
                           quantity=0)
    other_line.save()
    order.lines.add(other_line)

    order.cache_prices()
    order.save()

    order.create_full_refund(restock_products=False)
    assert order.taxful_total_price.value == 0
Example #60
0
def test_anonymize_contact():
    """
    Test that contact are anonymized
    """
    activate("en")
    shop = factories.get_default_shop()
    anonymizer = Anonymizer()

    customer = factories.create_random_person("en")
    user = factories.create_random_user("en")
    customer.user = user
    customer.default_billing_address = factories.create_random_address()
    customer.default_shipping_address = factories.create_random_address()
    customer.save()

    company = factories.create_random_company()
    company.default_billing_address = factories.create_random_address()
    company.default_shipping_address = factories.create_random_address()
    company.save()
    company.members.add(customer)

    product = factories.create_product("p1", shop,
                                       factories.get_default_supplier())

    orders = []
    core_baskets = []
    front_baskets = []

    for basket_customer in [customer, company]:
        orders.extend([
            factories.create_random_order(basket_customer, [product])
            for order in range(3)
        ])

        front_baskets.append(
            StoredBasket.objects.create(
                key=uuid4().hex,
                shop=shop,
                customer=basket_customer,
                orderer=customer,
                creator=customer.user,
                currency=shop.currency,
                data={"items": []},
                prices_include_tax=shop.prices_include_tax))
        core_baskets.append(
            Basket.objects.create(key=uuid4().hex,
                                  shop=shop,
                                  customer=basket_customer,
                                  orderer=customer,
                                  creator=customer.user,
                                  currency=shop.currency,
                                  data={"items": []},
                                  prices_include_tax=shop.prices_include_tax))

    anonymized_person = PersonContact.objects.get(id=customer.id)
    anonymizer.anonymize_person(anonymized_person)
    anonymized_person.refresh_from_db()
    assert anonymized_person.first_name != customer.first_name
    assert anonymized_person.last_name != customer.last_name
    assert anonymized_person.email != customer.email
    assert anonymized_person.phone != customer.phone
    assert anonymized_person.default_billing_address.street != customer.default_billing_address.street
    assert anonymized_person.default_billing_address.city != customer.default_billing_address.city

    anonymized_company = CompanyContact.objects.get(id=company.id)
    anonymizer.anonymize_company(anonymized_company)
    anonymized_company.refresh_from_db()
    assert anonymized_company.tax_number != company.tax_number
    assert anonymized_company.email != company.email
    assert anonymized_company.phone != company.phone
    assert anonymized_company.default_billing_address.street != company.default_billing_address.street
    assert anonymized_company.default_billing_address.city != company.default_billing_address.city

    for created_order in orders:
        order = Order.objects.get(id=created_order.id)
        assert order.phone != created_order.phone
        assert order.ip_address != created_order.ip_address
        assert order.shipping_address.street != created_order.shipping_address.street
        assert order.billing_address.street != created_order.billing_address.street

    for front_basket in front_baskets:
        stored_basket = StoredBasket.objects.get(id=front_basket.id)
        assert stored_basket.data is None

    for core_basket in core_baskets:
        basket = Basket.objects.get(id=core_basket.id)
        assert basket.data is None

    anonymized_user = get_user_model().objects.get(id=user.id)
    anonymizer.anonymize_user(anonymized_user)
    anonymized_user.refresh_from_db()
    assert user.username != anonymized_user.username
    assert user.first_name != anonymized_user.first_name
    assert user.last_name != anonymized_user.last_name