def test_shipment_with_unshippable_products(): shop = get_default_shop() supplier = get_default_supplier() product = create_product( "unshippable", shop=shop, supplier=supplier, default_price=5.55) product.shipping_mode = ShippingMode.NOT_SHIPPED product.save() order = _get_order(shop, supplier, stocked=False) initial_product_line_count = order.lines.products().count() add_product_to_order(order, supplier, product, quantity=4, taxless_base_unit_price=3) order.cache_prices() order.check_all_verified() order.save() assert order.shipments.count() == 0 assert order.can_create_shipment() assert not order.can_set_complete() order.create_shipment_of_all_products(supplier=supplier) assert (order.lines.products().count() == initial_product_line_count + 1) assert order.shipments.count() == 1 assert ShipmentProduct.objects.filter(shipment__order_id=order.id).count() == initial_product_line_count assert order.shipping_status == ShippingStatus.FULLY_SHIPPED assert order.can_set_complete()
def test_computing_simple_product_relations(rf): shop = get_default_shop() supplier = get_default_supplier() product = create_product("simple-test-product", shop) related_product = create_product("simple-related-product", shop) quantities = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] for quantity in quantities: order = create_order_with_product(product, supplier, quantity=1, taxless_base_unit_price=6, shop=shop) add_product_to_order(order, supplier, related_product, quantity=quantity, taxless_base_unit_price=6) assert ProductCrossSell.objects.count() == 0 add_bought_with_relations_for_product(product.pk) assert ProductCrossSell.objects.count() == 1 cross_sell_product = ProductCrossSell.objects.filter( product1=product).first() assert cross_sell_product.product2 == related_product assert cross_sell_product.weight == sum(quantities) add_bought_with_relations_for_product(related_product.id) assert ProductCrossSell.objects.count() == 2 cross_sell_product = ProductCrossSell.objects.filter( product1=related_product).first() assert cross_sell_product.product2 == product assert cross_sell_product.weight == len(quantities)
def test_rounding_with_taxes(prices): shop = get_default_shop() supplier = get_default_supplier() order = create_empty_order(shop=shop) order.save() product = create_product("test_sku", shop=shop, supplier=supplier) tax_rate = Decimal("0.22222") for x, price in enumerate(prices): add_product_to_order( order, supplier, product, quantity=Decimal("2.22"), taxless_base_unit_price=Decimal(price), tax_rate=tax_rate, ) order.cache_prices() for x, order_line in enumerate(order.lines.all().order_by("ordering")): # Check that total prices calculated from priceful parts still matches assert _get_taxless_price(order_line) == order_line.taxless_price assert _get_taxful_price(order_line) == order_line.taxful_price assert order_line.price == ( order_line.base_unit_price * order_line.quantity - order_line.discount_amount)
def test_order_with_only_unshippable_products(): shop = get_default_shop() supplier = get_default_supplier() order = create_empty_order(shop=shop) order.full_clean() order.save() product = create_product("unshippable", shop=shop, supplier=supplier, default_price=5.55) product.shipping_mode = ShippingMode.NOT_SHIPPED product.save() add_product_to_order(order, supplier, product, quantity=4, taxless_base_unit_price=3) order.cache_prices() order.check_all_verified() order.save() assert not order.can_create_shipment() assert order.can_set_complete()
def _init_test_with_variations(): shop = get_default_shop() supplier = get_default_supplier() product_data = { "t-shirt": { "colors": ["black", "yellow"], }, "hoodie": { "colors": ["black"], } } for key, data in six.iteritems(product_data): parent = create_product(key, shop=shop) shop_parent_product = parent.get_shop_instance(shop) for color in data["colors"]: sku = "%s-%s" % (key, color) 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={"color": color}) assert Product.objects.count() == 5 black_t_shirt = Product.objects.filter(sku="t-shirt-black").first() black_hoodie = Product.objects.filter(sku="hoodie-black").first() order = create_order_with_product(black_t_shirt, supplier, quantity=1, taxless_base_unit_price=6, shop=shop) add_product_to_order(order, supplier, black_hoodie, quantity=1, taxless_base_unit_price=6) return black_t_shirt.variation_parent, black_hoodie.variation_parent
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)
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
def test_shipment_with_unshippable_products(): shop = get_default_shop() supplier = get_default_supplier() product = create_product( "unshippable", shop=shop, supplier=supplier, default_price=5.55) product.shipping_mode = ShippingMode.NOT_SHIPPED product.save() order = _get_order(shop, supplier, stocked=False) initial_product_line_count = order.lines.products().count() add_product_to_order(order, supplier, product, quantity=4, taxless_base_unit_price=3) order.cache_prices() order.check_all_verified() order.save() assert order.shipments.count() == 0 assert order.can_create_shipment() assert not order.can_set_complete() order.create_shipment_of_all_products(supplier=supplier) assert (order.lines.products().count() == initial_product_line_count + 1) assert order.shipments.count() == 1 assert ShipmentProduct.objects.filter(shipment__order_id=order.id).count() == initial_product_line_count assert order.shipping_status == ShippingStatus.FULLY_SHIPPED assert order.can_set_complete()
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)
def _init_test_with_variations(): shop = get_default_shop() supplier = get_default_supplier() product_data = { "t-shirt": { "colors": ["black", "yellow"], }, "hoodie": { "colors": ["black"], }, } for key, data in six.iteritems(product_data): parent = create_product(key, shop=shop) shop_parent_product = parent.get_shop_instance(shop) for color in data["colors"]: sku = "%s-%s" % (key, color) 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={"color": color}) assert Product.objects.count() == 5 black_t_shirt = Product.objects.filter(sku="t-shirt-black").first() black_hoodie = Product.objects.filter(sku="hoodie-black").first() order = create_order_with_product(black_t_shirt, supplier, quantity=1, taxless_base_unit_price=6, shop=shop) add_product_to_order(order, supplier, black_hoodie, quantity=1, taxless_base_unit_price=6) return black_t_shirt, black_hoodie
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
def _add_product_to_order(order, sku, quantity, shop, supplier): product = create_product( sku=sku, shop=shop, supplier=supplier, default_price=3.33, ) add_product_to_order(order, supplier, product, quantity=quantity, taxless_base_unit_price=1)
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
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
def test_order_shipments(rf, admin_user): shop = get_default_shop() supplier = get_supplier(module_identifier="simple_supplier", identifier="1", name="supplier") supplier.shops.add(shop) product = create_product("sku1", shop=shop, default_price=10) shop_product = product.get_shop_instance(shop=shop) shop_product.suppliers.set([supplier]) product_quantities = { supplier.pk: {product.pk: 20}, } def get_quantity(supplier, product): return product_quantities[supplier.pk][product.pk] order = create_empty_order(shop=shop) order.full_clean() order.save() order.change_status(OrderStatus.objects.get_default_processing()) # Let's test the order order status history section for superuser request = apply_request_middleware(rf.get("/"), user=admin_user, shop=shop) # Add product to order for supplier add_product_to_order(order, supplier, product, get_quantity(supplier, product), 8) context = OrderHistorySection.get_context_data(order, request) assert len(context) == 2 # Let's create staff user without any permissions staff_user = create_random_user(is_staff=True) group = get_default_permission_group() staff_user.groups.add(group) shop.staff_members.add(staff_user) request = apply_request_middleware(rf.get("/"), user=staff_user, shop=shop) context = OrderHistorySection.get_context_data(order, request) assert len(context) == 2 # works fine while rendering rendered_content = loader.render_to_string( OrderHistorySection.template, context={ OrderHistorySection.identifier: context, "order_status_history": OrderStatusHistory.objects.filter(order=order).order_by("-created_on"), }, ) assert force_text(OrderStatus.objects.get_default_initial().name) in rendered_content assert force_text(OrderStatus.objects.get_default_processing().name) in rendered_content client = Client() client.force_login(admin_user) # We should see the order status section assert OrderHistorySection.visible_for_object(order, request)
def test_order_refunds_with_other_lines(rf, admin_user): shop = get_default_shop() supplier = get_default_supplier() supplier.shops.add(shop) product = create_product("sku", shop=shop, default_price=10) shop_product = product.get_shop_instance(shop=shop) shop_product.suppliers = [supplier] order = create_empty_order(shop=shop) order.full_clean() order.save() add_product_to_order(order, supplier, product, 4, 5) # 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) # Lines with quantity again should be able to be refunded normally. other_line_with_quantity = OrderLine(order=order, type=OrderLineType.OTHER, text="Special service 100$/h", quantity=1, base_unit_price_value=100) other_line_with_quantity.save() order.lines.add(other_line_with_quantity) assert other_line_with_quantity.max_refundable_quantity == 1 assert other_line_with_quantity.max_refundable_amount.value == 100 order.cache_prices() order.create_payment(order.taxful_total_price) assert order.is_paid() assert order.taxful_total_price_value == 120 # 100 + 4 * 20 def get_refund_view_content(): request = apply_request_middleware(rf.get("/"), user=admin_user) view = OrderCreateRefundView.as_view() response = view(request, pk=order.pk) return BeautifulSoup(response.render().content) refund_soup = get_refund_view_content() refund_options = refund_soup.find( id="id_form-0-line_number").findAll("option") assert len( refund_options) == 4 # 1 empty line, 1 for arbitrary and 2 for lines assert len([ option for option in refund_options if "Special service 100$/h" in force_text(option) ]) == 1
def _get_order(shop, supplier, stocked=False): order = create_empty_order(shop=shop) order.full_clean() order.save() for product_data in _get_product_data(stocked): quantity = product_data.pop("quantity") product = create_product( sku=product_data.pop("sku"), shop=shop, supplier=supplier, default_price=3.33, **product_data ) add_product_to_order(order, supplier, product, quantity=quantity, taxless_base_unit_price=1) order.cache_prices() order.check_all_verified() order.save() return order
def _get_order(shop, supplier, stocked=False): order = create_empty_order(shop=shop) order.full_clean() order.save() for product_data in _get_product_data(stocked): quantity = product_data.pop("quantity") product = create_product( sku=product_data.pop("sku"), shop=shop, supplier=supplier, default_price=3.33, **product_data) add_product_to_order(order, supplier, product, quantity=quantity, taxless_base_unit_price=1) order.cache_prices() order.check_all_verified() order.save() return order
def test_product_relations_max_quantity(rf): shop = get_default_shop() supplier = get_default_supplier() product = create_product("simple-test-product", shop) quantities = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] for i, quantity in enumerate(quantities): order = create_order_with_product(product, supplier, quantity=1, taxless_base_unit_price=6, shop=shop) add_product_to_order( order, supplier, create_product("product-%s" % i, shop), quantity=quantity, taxless_base_unit_price=6 ) assert ProductCrossSell.objects.count() == 0 add_bought_with_relations_for_product(product.pk, max_quantity=5) assert ProductCrossSell.objects.count() == 5 # Test that ordering is ok assert not ProductCrossSell.objects.filter(weight=1).exists() assert ProductCrossSell.objects.filter(weight=11).exists()
def test_rounding_with_taxes(prices): shop = get_default_shop() supplier = get_default_supplier() order = create_empty_order(shop=shop) order.save() product = create_product("test_sku", shop=shop, supplier=supplier) tax_rate = Decimal("0.22222") for x, price in enumerate(prices): add_product_to_order( order, supplier, product, quantity=Decimal("2.22"), taxless_base_unit_price=Decimal(price), tax_rate=tax_rate) order.cache_prices() for x, order_line in enumerate(order.lines.all().order_by("ordering")): # Check that total prices calculated from priceful parts still matches assert _get_taxless_price(order_line) == order_line.taxless_price assert _get_taxful_price(order_line) == order_line.taxful_price assert order_line.price == (order_line.base_unit_price * order_line.quantity - order_line.discount_amount)
def _get_order(shop, supplier, has_price): order = create_empty_order(shop=shop) order.full_clean() order.save() for product_data in _get_product_data(has_price): quantity = product_data.pop("quantity") tax_rate = product_data.pop("tax_rate") product = create_product( sku=product_data.pop("sku"), shop=shop, supplier=supplier, **product_data) add_product_to_order( order, supplier, product, quantity=quantity, taxless_base_unit_price=product_data["default_price"], tax_rate=tax_rate) order.cache_prices() order.check_all_verified() order.save() return order
def _get_order(shop, supplier, has_price): order = create_empty_order(shop=shop) order.full_clean() order.save() for product_data in _get_product_data(has_price): quantity = product_data.pop("quantity") tax_rate = product_data.pop("tax_rate") product = create_product( sku=product_data.pop("sku"), shop=shop, supplier=supplier, **product_data) add_product_to_order( order, supplier, product, quantity=quantity, taxless_base_unit_price=product_data["default_price"], tax_rate=tax_rate) order.cache_prices() order.check_all_verified() order.save() return order
def test_order_refunds_with_other_lines(): shop = get_default_shop() supplier = Supplier.objects.create(identifier="1", name="supplier1") supplier.shops.add(shop) product = create_product("sku", shop=shop, default_price=10) shop_product = product.get_shop_instance(shop=shop) shop_product.suppliers.set([supplier]) order = create_empty_order(shop=shop) order.full_clean() order.save() add_product_to_order(order, supplier, product, 4, 5) # 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) # Lines with quantity again should be able to be refunded normally. other_line_with_quantity = OrderLine(order=order, type=OrderLineType.OTHER, text="Special service 100$/h", quantity=1, base_unit_price_value=100) other_line_with_quantity.save() order.lines.add(other_line_with_quantity) assert other_line_with_quantity.max_refundable_quantity == 1 assert other_line_with_quantity.max_refundable_amount.value == 100 order.cache_prices() order.create_payment(order.taxful_total_price) assert order.is_paid() assert order.taxful_total_price_value == 120 # 100 + 4 * 20 order.create_full_refund() assert order.taxful_total_price_value == 0
def test_product_relations_max_quantity(rf): shop = get_default_shop() supplier = get_default_supplier() product = create_product("simple-test-product", shop) quantities = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] for i, quantity in enumerate(quantities): order = create_order_with_product(product, supplier, quantity=1, taxless_base_unit_price=6, shop=shop) add_product_to_order( order, supplier, create_product("product-%s" % i, shop), quantity=quantity, taxless_base_unit_price=6 ) assert ProductCrossSell.objects.count() == 0 add_bought_with_relations_for_product(product.pk, max_quantity=5) assert ProductCrossSell.objects.count() == 5 # Test that ordering is ok assert not ProductCrossSell.objects.filter(weight=1).exists() assert ProductCrossSell.objects.filter(weight=11).exists()
def test_order_with_only_unshippable_products(): shop = get_default_shop() supplier = get_default_supplier() order = create_empty_order(shop=shop) order.full_clean() order.save() product = create_product( "unshippable", shop=shop, supplier=supplier, default_price=5.55) product.shipping_mode = ShippingMode.NOT_SHIPPED product.save() add_product_to_order(order, supplier, product, quantity=4, taxless_base_unit_price=3) order.cache_prices() order.check_all_verified() order.save() assert not order.can_create_shipment() assert order.can_set_complete()
def test_computing_simple_product_relations(rf): shop = get_default_shop() supplier = get_default_supplier() product = create_product("simple-test-product", shop) related_product = create_product("simple-related-product", shop) quantities = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] for quantity in quantities: order = create_order_with_product(product, supplier, quantity=1, taxless_base_unit_price=6, shop=shop) add_product_to_order(order, supplier, related_product, quantity=quantity, taxless_base_unit_price=6) assert ProductCrossSell.objects.count() == 0 add_bought_with_relations_for_product(product.pk) assert ProductCrossSell.objects.count() == 1 cross_sell_product = ProductCrossSell.objects.filter(product1=product).first() assert cross_sell_product.product2 == related_product assert cross_sell_product.weight == sum(quantities) add_bought_with_relations_for_product(related_product.id) assert ProductCrossSell.objects.count() == 2 cross_sell_product = ProductCrossSell.objects.filter(product1=related_product).first() assert cross_sell_product.product2 == product assert cross_sell_product.weight == len(quantities)
def create_multi_supplier_order_to_review(shop_product, customer): order = factories.create_empty_order(shop=shop_product.shop) order.full_clean() order.save() pricing_context = factories._get_pricing_context(order.shop, order.customer) for supplier in shop_product.suppliers.all(): factories.add_product_to_order(order, supplier, shop_product.product, 1, shop_product.default_price_value, 0, pricing_context) order.cache_prices() order.save() order.customer = customer order.create_payment(order.taxful_total_price) for supplier in shop_product.suppliers.all(): order.create_shipment_of_all_products(supplier) order.status = OrderStatus.objects.get_default_complete() order.save() return order
def test_order_shipment_section(rf, admin_user): shop = get_default_shop() supplier1 = Supplier.objects.create(identifier="1", name="supplier1") supplier1.shops.add(shop) supplier2 = Supplier.objects.create(identifier="2") supplier2.shops.add(shop) product1 = create_product("sku1", shop=shop, default_price=10) shop_product1 = product1.get_shop_instance(shop=shop) shop_product1.suppliers.set([supplier1]) product2 = create_product("sku3", shop=shop, default_price=10, shipping_mode=ShippingMode.NOT_SHIPPED) shop_product2 = product1.get_shop_instance(shop=shop) shop_product2.suppliers.set([supplier2]) product_quantities = { supplier1.pk: { product1.pk: 20 }, supplier2.pk: { product2.pk: 10 } } def get_quantity(supplier, product): return product_quantities[supplier.pk][product.pk] order = create_empty_order(shop=shop) order.full_clean() order.save() # Let's test the order shipment section for superuser request = apply_request_middleware(rf.get("/"), user=admin_user, shop=shop) # Add product 3 to order for supplier 2 add_product_to_order(order, supplier2, product2, get_quantity(supplier2, product2), 8) # Product is not shippable so order section should not be available assert not ShipmentSection.visible_for_object(order, request) # Add product 2 to order for supplier 1 add_product_to_order(order, supplier1, product1, get_quantity(supplier1, product1), 7) # Now we should see the shipment section assert ShipmentSection.visible_for_object(order, request) # Make order fully paid so we can start creting shipments and refunds order.cache_prices() order.check_all_verified() order.create_payment(order.taxful_total_price) assert order.is_paid() product_summary = order.get_product_summary() assert product_summary[product1.pk]["unshipped"] == 20 assert product_summary[product2.pk]["unshipped"] == 0 assert product_summary[product2.pk]["ordered"] == 10 # Fully ship the order order.create_shipment({product1: 5}, supplier=supplier1) order.create_shipment({product1: 5}, supplier=supplier1) order.create_shipment({product1: 10}, supplier=supplier1) assert not order.get_unshipped_products() assert order.is_fully_shipped() context = ShipmentSection.get_context_data(order, request) assert len(context["suppliers"]) == 2 assert len(context["create_urls"].keys()) == 2 # One for each supplier assert len(context["delete_urls"].keys()) == 3 # One for each shipment # Let's create staff user without any permissions staff_user = create_random_user(is_staff=True) group = get_default_permission_group() staff_user.groups.add(group) shop.staff_members.add(staff_user) request = apply_request_middleware(rf.get("/"), user=staff_user, shop=shop) context = ShipmentSection.get_context_data(order, request) assert len(context["suppliers"]) == 2 assert len(context["create_urls"].keys()) == 0 assert len(context["delete_urls"].keys()) == 0 set_permissions_for_group(group, ["order.create-shipment"]) request = apply_request_middleware(rf.get("/"), user=staff_user, shop=shop) context = ShipmentSection.get_context_data(order, request) assert len(context["suppliers"]) == 2 assert len(context["create_urls"].keys()) == 2 assert len(context["delete_urls"].keys()) == 0 set_permissions_for_group(group, ["order.create-shipment", "order.delete-shipment"]) request = apply_request_middleware(rf.get("/"), user=staff_user, shop=shop) context = ShipmentSection.get_context_data(order, request) assert len(context["suppliers"]) == 2 assert len(context["create_urls"].keys()) == 2 assert len(context["delete_urls"].keys()) == 3 # Make product1 unshipped product1.shipping_mode = ShippingMode.NOT_SHIPPED product1.save() # We still should see the order shipment section since existing shipments assert ShipmentSection.visible_for_object(order, request) # Let's delete all shipments since both products is unshipped and we # don't need those. for shipment in order.shipments.all(): shipment.soft_delete() assert not ShipmentSection.visible_for_object(order, request)
def test_order_refunds_with_multiple_suppliers(): shop = get_default_shop() supplier1 = Supplier.objects.create(identifier="1", name="supplier1") supplier1.shops.add(shop) supplier2 = Supplier.objects.create(identifier="2") supplier2.shops.add(shop) supplier3 = Supplier.objects.create(identifier="3", name="s") supplier3.shops.add(shop) product1 = create_product("sku1", shop=shop, default_price=10) shop_product1 = product1.get_shop_instance(shop=shop) shop_product1.suppliers = [supplier1, supplier2, supplier3] product2 = create_product("sku2", shop=shop, default_price=10) shop_product2 = product1.get_shop_instance(shop=shop) shop_product2.suppliers = [supplier1, supplier2] product3 = create_product("sku3", shop=shop, default_price=10, shipping_mode=ShippingMode.NOT_SHIPPED) shop_product3 = product1.get_shop_instance(shop=shop) shop_product3.suppliers = [supplier3] product_quantities = { supplier1: { product1: 5, product2: 6 }, supplier2: { product1: 3, product2: 13 }, supplier3: { product1: 1, product3: 50 } } def get_quantity(supplier, product): return product_quantities[supplier.pk][product.pk] order = create_empty_order(shop=shop) order.full_clean() order.save() for supplier, product_data in six.iteritems(product_quantities): for product, quantity in six.iteritems(product_data): add_product_to_order(order, supplier, product, quantity, 5) order.cache_prices() order.create_payment(order.taxful_total_price) assert order.is_paid() # All supplier should be able to refund the order assert order.can_create_refund() assert order.can_create_refund(supplier1) assert order.can_create_refund(supplier2) assert order.can_create_refund(supplier3) assert order.get_total_unrefunded_amount(supplier1).value == Decimal("55") # 11 * 5 assert order.get_total_unrefunded_quantity(supplier1) == Decimal("11") # 5 x product1 and 6 x product2 with pytest.raises(RefundExceedsAmountException): order.create_refund([{ "line": "amount", "quantity": 1, "amount": order.shop.create_price(60) }], supplier=supplier1) # Supplier 1 refunds the order order.create_refund(_get_refund_data(order, supplier1)) assert order.get_total_refunded_amount(supplier1).value == Decimal("55") # 11 * 5 assert order.get_total_unrefunded_amount(supplier1).value == Decimal("0") assert not order.can_create_refund(supplier1) assert order.can_create_refund() assert order.can_create_refund(supplier2) assert order.can_create_refund(supplier3) assert order.get_total_unrefunded_amount(supplier2).value == Decimal("80") # 16 * 5 assert order.get_total_unrefunded_quantity(supplier2) == Decimal("16") # 3 x product1 and 13 x product2 with pytest.raises(RefundExceedsAmountException): order.create_refund([{ "line": "amount", "quantity": 1, "amount": order.shop.create_price(81) }], supplier=supplier2) # Supplier 2 refunds the order order.create_refund(_get_refund_data(order, supplier2)) assert order.get_total_refunded_amount(supplier2).value == Decimal("80") # 11 * 5 assert order.get_total_unrefunded_amount(supplier2).value == Decimal("0") assert not order.can_create_refund(supplier1) assert not order.can_create_refund(supplier2) assert order.can_create_refund() assert order.can_create_refund(supplier3) assert order.get_total_unrefunded_amount(supplier3).value == Decimal("255") # 51 * 5 assert order.get_total_unrefunded_quantity(supplier3) == Decimal("51") # 3 x product1 and 13 x product2 order.create_refund([{ "line": "amount", "quantity": 1, "amount": order.shop.create_price(200) }], supplier=supplier3) assert OrderLine.objects.filter(order=order, supplier=supplier3, type=OrderLineType.REFUND).exists() # Supplier 3 refunds the order order.create_refund(_get_refund_data(order, supplier3)) assert order.get_total_refunded_amount(supplier3).value == Decimal("255") # 11 * 5 assert order.get_total_unrefunded_amount(supplier3).value == Decimal("0") assert not order.can_create_refund(supplier1) assert not order.can_create_refund(supplier2) assert not order.can_create_refund(supplier3) assert not order.can_create_refund()
def test_refunds_with_multiple_suppliers(rf, admin_user): shop = get_default_shop() supplier1 = Supplier.objects.create(identifier="1", name="supplier1") supplier1.shops.add(shop) supplier2 = Supplier.objects.create(identifier="2") supplier2.shops.add(shop) supplier3 = Supplier.objects.create(identifier="3", name="s") supplier3.shops.add(shop) product1 = create_product("sku1", shop=shop, default_price=10) shop_product1 = product1.get_shop_instance(shop=shop) shop_product1.suppliers = [supplier1, supplier2, supplier3] product2 = create_product("sku2", shop=shop, default_price=10) shop_product2 = product1.get_shop_instance(shop=shop) shop_product2.suppliers = [supplier1, supplier2] product3 = create_product("sku3", shop=shop, default_price=10, shipping_mode=ShippingMode.NOT_SHIPPED) shop_product3 = product1.get_shop_instance(shop=shop) shop_product3.suppliers = [supplier3] product_quantities = { supplier1: { product1: 5, product2: 6 }, supplier2: { product1: 3, product2: 13 }, supplier3: { product1: 1, product3: 50 } } def get_quantity(supplier, product): return product_quantities[supplier.pk][product.pk] order = create_empty_order(shop=shop) order.full_clean() order.save() for supplier, product_data in six.iteritems(product_quantities): for product, quantity in six.iteritems(product_data): add_product_to_order(order, supplier, product, quantity, 5) order.cache_prices() order.create_payment(order.taxful_total_price) assert order.is_paid() # All supplier should be able to refund the order assert order.can_create_refund() assert order.can_create_refund(supplier1) assert order.can_create_refund(supplier2) assert order.can_create_refund(supplier3) assert not order.has_refunds() assert len(order.lines.all()) == 6 product_line = order.lines.first() data = { "form-0-line_number": 0, "form-0-quantity": 1, "form-0-amount": 1, "form-0-restock_products": False, "form-INITIAL_FORMS": 0, "form-MAX_NUM_FORMS": 1000, "form-TOTAL_FORMS": 1, "form-MIN_NUM_FORMS": 0, } supplier_provider = "shuup.testing.supplier_provider.RequestSupplierProvider" with override_settings(SHUUP_ADMIN_SUPPLIER_PROVIDER_SPEC=supplier_provider): # Test refund view content as superuser request = apply_request_middleware(rf.get("/", data=data), user=admin_user) view = OrderCreateRefundView.as_view() response = view(request, pk=order.pk) assert response.status_code == 200 if hasattr(response, "render"): response.render() soup = BeautifulSoup(response.content) _assert_order_table_row_count(soup, 6) _assert_order_mobile_list_row_count(soup, 6) assert _get_create_full_refund_button(soup) is not None # Test refund view content as supplier 1 request = apply_request_middleware(rf.get("/", data=data), user=admin_user) request.supplier = supplier1 assert get_supplier(request) == supplier1 view = OrderCreateRefundView.as_view() response = view(request, pk=order.pk) assert response.status_code == 200 if hasattr(response, "render"): response.render() soup = BeautifulSoup(response.content) _assert_order_table_row_count(soup, 2) _assert_order_mobile_list_row_count(soup, 2) assert _get_create_full_refund_button(soup) is None # Test refund view content as supplier 2 user request = apply_request_middleware(rf.get("/", data=data), user=admin_user) request.supplier = supplier2 assert get_supplier(request) == supplier2 view = OrderCreateRefundView.as_view() response = view(request, pk=order.pk) assert response.status_code == 200 if hasattr(response, "render"): response.render() soup = BeautifulSoup(response.content) _assert_order_table_row_count(soup, 2) _assert_order_mobile_list_row_count(soup, 2) assert _get_create_full_refund_button(soup) is None
def test_refunds_with_multiple_suppliers(rf, admin_user): shop = get_default_shop() supplier1 = Supplier.objects.create(identifier="1", name="supplier1") supplier1.shops.add(shop) supplier2 = Supplier.objects.create(identifier="2") supplier2.shops.add(shop) supplier3 = Supplier.objects.create(identifier="3", name="s") supplier3.shops.add(shop) product1 = create_product("sku1", shop=shop, default_price=10) shop_product1 = product1.get_shop_instance(shop=shop) shop_product1.suppliers = [supplier1, supplier2, supplier3] product2 = create_product("sku2", shop=shop, default_price=10) shop_product2 = product1.get_shop_instance(shop=shop) shop_product2.suppliers = [supplier1, supplier2] product3 = create_product("sku3", shop=shop, default_price=10, shipping_mode=ShippingMode.NOT_SHIPPED) shop_product3 = product1.get_shop_instance(shop=shop) shop_product3.suppliers = [supplier3] product_quantities = { supplier1: { product1: 5, product2: 6 }, supplier2: { product1: 3, product2: 13 }, supplier3: { product1: 1, product3: 50 } } def get_quantity(supplier, product): return product_quantities[supplier.pk][product.pk] order = create_empty_order(shop=shop) order.full_clean() order.save() for supplier, product_data in six.iteritems(product_quantities): for product, quantity in six.iteritems(product_data): add_product_to_order(order, supplier, product, quantity, 5) order.cache_prices() order.create_payment(order.taxful_total_price) assert order.is_paid() # All supplier should be able to refund the order assert order.can_create_refund() assert order.can_create_refund(supplier1) assert order.can_create_refund(supplier2) assert order.can_create_refund(supplier3) assert not order.has_refunds() assert len(order.lines.all()) == 6 product_line = order.lines.first() data = { "form-0-line_number": 0, "form-0-quantity": 1, "form-0-amount": 1, "form-0-restock_products": False, "form-INITIAL_FORMS": 0, "form-MAX_NUM_FORMS": 1000, "form-TOTAL_FORMS": 1, "form-MIN_NUM_FORMS": 0, } supplier_provider = "shuup.testing.supplier_provider.RequestSupplierProvider" with override_settings( SHUUP_ADMIN_SUPPLIER_PROVIDER_SPEC=supplier_provider): # Test refund view content as superuser request = apply_request_middleware(rf.get("/", data=data), user=admin_user) view = OrderCreateRefundView.as_view() response = view(request, pk=order.pk) assert response.status_code == 200 if hasattr(response, "render"): response.render() soup = BeautifulSoup(response.content) _assert_order_table_row_count(soup, 6) _assert_order_mobile_list_row_count(soup, 6) assert _get_create_full_refund_button(soup) is not None # Test refund view content as supplier 1 request = apply_request_middleware(rf.get("/", data=data), user=admin_user) request.supplier = supplier1 assert get_supplier(request) == supplier1 view = OrderCreateRefundView.as_view() response = view(request, pk=order.pk) assert response.status_code == 200 if hasattr(response, "render"): response.render() soup = BeautifulSoup(response.content) _assert_order_table_row_count(soup, 2) _assert_order_mobile_list_row_count(soup, 2) assert _get_create_full_refund_button(soup) is None # Test refund view content as supplier 2 user request = apply_request_middleware(rf.get("/", data=data), user=admin_user) request.supplier = supplier2 assert get_supplier(request) == supplier2 view = OrderCreateRefundView.as_view() response = view(request, pk=order.pk) assert response.status_code == 200 if hasattr(response, "render"): response.render() soup = BeautifulSoup(response.content) _assert_order_table_row_count(soup, 2) _assert_order_mobile_list_row_count(soup, 2) assert _get_create_full_refund_button(soup) is None
def test_order_shipments(rf, admin_user): shop = get_default_shop() supplier1 = Supplier.objects.create(identifier="1", name="supplier1") supplier1.shops.add(shop) supplier2 = Supplier.objects.create(identifier="2") supplier2.shops.add(shop) product1 = create_product("sku1", shop=shop, default_price=10) shop_product1 = product1.get_shop_instance(shop=shop) shop_product1.suppliers.set([supplier1]) product2 = create_product("sku3", shop=shop, default_price=10, shipping_mode=ShippingMode.NOT_SHIPPED) shop_product2 = product1.get_shop_instance(shop=shop) shop_product2.suppliers.set([supplier2]) product_quantities = { supplier1.pk: { product1.pk: 20 }, supplier2.pk: { product2.pk: 10 } } def get_quantity(supplier, product): return product_quantities[supplier.pk][product.pk] order = create_empty_order(shop=shop) order.full_clean() order.save() # Let's test the order shipment section for superuser request = apply_request_middleware(rf.get("/"), user=admin_user, shop=shop) # Add product 3 to order for supplier 2 add_product_to_order(order, supplier2, product2, get_quantity(supplier2, product2), 8) # Product is not shippable so order section should not be available assert not ShipmentSection.visible_for_object(order, request) # Add product 2 to order for supplier 1 add_product_to_order(order, supplier1, product1, get_quantity(supplier1, product1), 7) # Now we should see the shipment section assert ShipmentSection.visible_for_object(order, request) # Make order fully paid so we can start creting shipments and refunds order.cache_prices() order.check_all_verified() order.create_payment(order.taxful_total_price) assert order.is_paid() product_summary = order.get_product_summary() assert product_summary[product1.pk]["unshipped"] == 20 assert product_summary[product2.pk]["unshipped"] == 0 assert product_summary[product2.pk]["ordered"] == 10 # Fully ship the order order.create_shipment({product1: 5}, supplier=supplier1) order.create_shipment({product1: 5}, supplier=supplier1) order.create_shipment({product1: 10}, supplier=supplier1) assert not order.get_unshipped_products() assert order.is_fully_shipped() context = ShipmentSection.get_context_data(order, request) assert len(context["suppliers"]) == 2 assert len(context["create_urls"].keys()) == 2 # One for each supplier assert len(context["delete_urls"].keys()) == 3 # One for each shipment # Let's create staff user without any permissions staff_user = create_random_user(is_staff=True) group = get_default_permission_group() staff_user.groups.add(group) shop.staff_members.add(staff_user) request = apply_request_middleware(rf.get("/"), user=staff_user, shop=shop) context = ShipmentSection.get_context_data(order, request) assert len(context["suppliers"]) == 2 assert len(context["create_urls"].keys()) == 0 assert len(context["delete_urls"].keys()) == 0 assert len(context["set_sent_urls"].keys()) == 0 set_permissions_for_group(group, ["order.create-shipment"]) request = apply_request_middleware(rf.get("/"), user=staff_user, shop=shop) context = ShipmentSection.get_context_data(order, request) assert len(context["suppliers"]) == 2 assert len(context["create_urls"].keys()) == 2 assert len(context["delete_urls"].keys()) == 0 assert len(context["set_sent_urls"].keys()) == 0 set_permissions_for_group(group, [ "order.create-shipment", "order.delete-shipment", "order.set-shipment-sent" ]) request = apply_request_middleware(rf.get("/"), user=staff_user, shop=shop) context = ShipmentSection.get_context_data(order, request) assert len(context["suppliers"]) == 2 assert len(context["create_urls"].keys()) == 2 assert len(context["delete_urls"].keys()) == 3 assert len(context["set_sent_urls"].keys()) == 3 # works fine while rendering rendered_content = loader.render_to_string(ShipmentSection.template, context={ ShipmentSection.identifier: context, "order": order, }) all_urls = list(context["delete_urls"].values()) all_urls.extend(list(context["set_sent_urls"].values())) for url in all_urls: assert url in rendered_content assert order.get_sent_shipments().count() == 0 order.shipments.filter(status=ShipmentStatus.NOT_SENT) == 3 client = Client() client.force_login(admin_user) # mark all shipments as sent! for mark_sent_url in context["set_sent_urls"].values(): response = client.post(mark_sent_url) assert response.status_code == 302 assert order.get_sent_shipments().count() == 3 order.shipments.filter(status=ShipmentStatus.NOT_SENT) == 0 # Make product1 unshipped product1.shipping_mode = ShippingMode.NOT_SHIPPED product1.save() # We still should see the order shipment section since existing shipments assert ShipmentSection.visible_for_object(order, request) # list all shipments in shipments list view response = client.get("{}?jq={}".format( reverse("shuup_admin:order.shipments.list"), json.dumps({ "perPage": 10, "page": 1 }))) assert response.status_code == 200 data = json.loads(response.content) assert len(data["items"]) == 3 for item in data["items"]: assert item["status"] == "Sent" # Let's delete all shipments since both products is unshipped and we # don't need those. for shipment in order.shipments.all(): shipment.soft_delete() assert not ShipmentSection.visible_for_object(order, request)
def test_order_product_summary_with_multiple_suppliers(): shop = get_default_shop() supplier1 = Supplier.objects.create(identifier="1", name="supplier1") supplier1.shops.add(shop) supplier2 = Supplier.objects.create(identifier="2") supplier2.shops.add(shop) supplier3 = Supplier.objects.create(identifier="3", name="s") supplier3.shops.add(shop) product1 = create_product("sku1", shop=shop, default_price=10) shop_product1 = product1.get_shop_instance(shop=shop) shop_product1.suppliers = [supplier1, supplier2, supplier3] product2 = create_product("sku2", shop=shop, default_price=10) shop_product2 = product1.get_shop_instance(shop=shop) shop_product2.suppliers = [supplier1, supplier2] product3 = create_product("sku3", shop=shop, default_price=10, shipping_mode=ShippingMode.NOT_SHIPPED) shop_product3 = product1.get_shop_instance(shop=shop) shop_product3.suppliers = [supplier3] product_quantities = { supplier1.pk: { product1.pk: 5, product2.pk: 6 }, supplier2.pk: { product1.pk: 3, product2.pk: 13 }, supplier3.pk: { product1.pk: 1, product3.pk: 50 } } def get_quantity(supplier, product): return product_quantities[supplier.pk][product.pk] order = create_empty_order(shop=shop) order.full_clean() order.save() # Add product 3 to order for supplier 3 add_product_to_order(order, supplier3, product3, get_quantity(supplier3, product3), 8) assert order.get_product_ids_and_quantities()[product3.pk] == 50 assert not order.has_products_requiring_shipment() assert not order.has_products_requiring_shipment(supplier3) # Add product 2 to order for supplier 1 add_product_to_order(order, supplier1, product2, get_quantity(supplier1, product2), 7) assert order.get_product_ids_and_quantities()[product2.pk] == 6 assert order.has_products_requiring_shipment() assert order.has_products_requiring_shipment(supplier1) assert not order.has_products_requiring_shipment(supplier3) # Add product 2 to order for supplier 2 add_product_to_order(order, supplier2, product2, get_quantity(supplier2, product2), 6) assert order.get_product_ids_and_quantities()[product2.pk] == 19 assert order.has_products_requiring_shipment() assert order.has_products_requiring_shipment(supplier1) assert order.has_products_requiring_shipment(supplier2) assert not order.has_products_requiring_shipment(supplier3) # Add product 1 to order for supplier 3 add_product_to_order(order, supplier3, product1, get_quantity(supplier3, product1), 5) assert order.get_product_ids_and_quantities()[product1.pk] == 1 assert order.has_products_requiring_shipment() assert order.has_products_requiring_shipment(supplier1) assert order.has_products_requiring_shipment(supplier2) assert order.has_products_requiring_shipment(supplier3) # Add product 1 for supplier 1 and 3 add_product_to_order(order, supplier1, product1, get_quantity(supplier1, product1), 4) add_product_to_order(order, supplier2, product1, get_quantity(supplier2, product1), 3) assert order.get_product_ids_and_quantities()[product1.pk] == 9 product_summary = order.get_product_summary() _assert_product_summary(product_summary, product1.pk, 9, 9, 0, 0) _assert_product_summary(product_summary, product2.pk, 19, 19, 0, 0) _assert_product_summary(product_summary, product3.pk, 50, 0, 0, 0) # Test product summary per supplier product_summary = order.get_product_summary(supplier1) _assert_product_summary(product_summary, product1.pk, 5, 5, 0, 0) _assert_product_summary(product_summary, product2.pk, 6, 6, 0, 0) _assert_product_summary(product_summary, product3.pk, 0, 0, 0, 0) product_summary = order.get_product_summary(supplier2.pk) _assert_product_summary(product_summary, product1.pk, 3, 3, 0, 0) _assert_product_summary(product_summary, product2.pk, 13, 13, 0, 0) _assert_product_summary(product_summary, product3.pk, 0, 0, 0, 0) product_summary = order.get_product_summary(supplier3.pk) _assert_product_summary(product_summary, product1.pk, 1, 1, 0, 0) _assert_product_summary(product_summary, product2.pk, 0, 0, 0, 0) _assert_product_summary(product_summary, product3.pk, 50, 0, 0, 0) # Make order fully paid so we can start creting shipments and refunds order.cache_prices() order.check_all_verified() order.create_payment(order.taxful_total_price) assert order.is_paid() # Let's make suer all good with unshipped products unshipped_products = order.get_unshipped_products() assert unshipped_products[product1.pk]["unshipped"] == 9 assert unshipped_products[product2.pk]["unshipped"] == 19 assert unshipped_products.get(product3.pk) is None unshipped_products = order.get_unshipped_products(supplier1) assert unshipped_products[product1.pk]["unshipped"] == 5 assert unshipped_products[product2.pk]["unshipped"] == 6 unshipped_products = order.get_unshipped_products(supplier2) assert unshipped_products[product1.pk]["unshipped"] == 3 assert unshipped_products[product2.pk]["unshipped"] == 13 unshipped_products = order.get_unshipped_products(supplier3) assert unshipped_products[product1.pk]["unshipped"] == 1 assert unshipped_products.get(product2.pk) is None assert unshipped_products.get(product3.pk) is None # Refund product3 line_to_refund = order.lines.filter(product_id=product3.pk).first() order.create_refund([{ "line": line_to_refund, "quantity": 10, "amount": shop.create_price("10").amount }]) product_summary = order.get_product_summary() _assert_product_summary(product_summary, product3.pk, 50, 0, 0, 10) product_summary = order.get_product_summary(supplier3.pk) _assert_product_summary(product_summary, product3.pk, 50, 0, 0, 10) order.create_refund([{ "line": line_to_refund, "quantity": 40, "amount": shop.create_price("20").amount }]) product_summary = order.get_product_summary() _assert_product_summary(product_summary, product3.pk, 50, 0, 0, 50) product_summary = order.get_product_summary(supplier3.pk) _assert_product_summary(product_summary, product3.pk, 50, 0, 0, 50) # Then ship product 1 for all suppliers one by one order.create_shipment({product1: 1}, supplier=supplier3) unshipped_products = order.get_unshipped_products(supplier3) assert unshipped_products.get(product1.pk) is None unshipped_products = order.get_unshipped_products() assert unshipped_products[product1.pk]["unshipped"] == 8 order.create_shipment({product1: 3}, supplier=supplier2) unshipped_products = order.get_unshipped_products(supplier2) assert unshipped_products.get(product1.pk) is None unshipped_products = order.get_unshipped_products() assert unshipped_products[product1.pk]["unshipped"] == 5 order.create_shipment({product1: 5}, supplier=supplier1) unshipped_products = order.get_unshipped_products(supplier1) assert unshipped_products.get(product1.pk) is None unshipped_products = order.get_unshipped_products() assert unshipped_products.get(product1.pk) is None # Then ship product 2 for all suppliers with a twist order.create_shipment({product2: 13}, supplier=supplier2) unshipped_products = order.get_unshipped_products(supplier2) assert unshipped_products.get(product2.pk) is None unshipped_products = order.get_unshipped_products() assert unshipped_products[product2.pk]["unshipped"] == 6 order.create_shipment({product2: 5}, supplier=supplier1) unshipped_products = order.get_unshipped_products(supplier1) assert unshipped_products[product2.pk]["unshipped"] == 1 unshipped_products = order.get_unshipped_products() assert unshipped_products[product2.pk]["unshipped"] == 1 product_summary = order.get_product_summary() _assert_product_summary(product_summary, product2.pk, 19, 1, 18, 0) # Refund the last product and see all falling in place line_to_refund = order.lines.filter(product_id=product2.pk, supplier=supplier1).first() order.create_refund([{ "line": line_to_refund, "quantity": 1, "amount": shop.create_price("1").amount }]) assert not order.get_unshipped_products() assert order.is_fully_shipped() # Verify product summary product_summary = order.get_product_summary() _assert_product_summary(product_summary, product1.pk, 9, 0, 9, 0, suppliers=[supplier1, supplier2, supplier3]) _assert_product_summary(product_summary, product2.pk, 19, 0, 18, 1, suppliers=[supplier1, supplier2]) _assert_product_summary(product_summary, product3.pk, 50, 0, 0, 50, suppliers=[supplier3]) # Order prodcuts and quantities still match, right? order_products_and_quantities = order.get_product_ids_and_quantities() assert order_products_and_quantities[product1.pk] == 9 assert order_products_and_quantities[product2.pk] == 19 assert order_products_and_quantities[product3.pk] == 50 # Order still has products requiring shipments, right? assert order.has_products_requiring_shipment() assert order.has_products_requiring_shipment(supplier1) assert order.has_products_requiring_shipment(supplier2) assert order.has_products_requiring_shipment(supplier3)
def test_order_refunds_with_multiple_suppliers(): shop = get_default_shop() supplier1 = Supplier.objects.create(identifier="1", name="supplier1") supplier1.shops.add(shop) supplier2 = Supplier.objects.create(identifier="2") supplier2.shops.add(shop) supplier3 = Supplier.objects.create(identifier="3", name="s") supplier3.shops.add(shop) product1 = create_product("sku1", shop=shop, default_price=10) shop_product1 = product1.get_shop_instance(shop=shop) shop_product1.suppliers.set([supplier1, supplier2, supplier3]) product2 = create_product("sku2", shop=shop, default_price=10) shop_product2 = product1.get_shop_instance(shop=shop) shop_product2.suppliers.set([supplier1, supplier2]) product3 = create_product("sku3", shop=shop, default_price=10, shipping_mode=ShippingMode.NOT_SHIPPED) shop_product3 = product1.get_shop_instance(shop=shop) shop_product3.suppliers.set([supplier3]) product_quantities = { supplier1: {product1: 5, product2: 6}, supplier2: {product1: 3, product2: 13}, supplier3: {product1: 1, product3: 50}, } def get_quantity(supplier, product): return product_quantities[supplier.pk][product.pk] order = create_empty_order(shop=shop) order.full_clean() order.save() for supplier, product_data in six.iteritems(product_quantities): for product, quantity in six.iteritems(product_data): add_product_to_order(order, supplier, product, quantity, 5) # 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.create_payment(order.taxful_total_price) assert order.is_paid() # All supplier should be able to refund the order assert order.can_create_refund() assert order.can_create_refund(supplier1) assert order.can_create_refund(supplier2) assert order.can_create_refund(supplier3) assert order.get_total_unrefunded_amount(supplier1).value == Decimal("55") # 11 * 5 assert order.get_total_unrefunded_quantity(supplier1) == Decimal("11") # 5 x product1 and 6 x product2 with pytest.raises(RefundExceedsAmountException): order.create_refund( [{"line": "amount", "quantity": 1, "amount": order.shop.create_price(60)}], supplier=supplier1 ) # Supplier 1 refunds the order order.create_refund(_get_refund_data(order, supplier1)) assert order.get_total_refunded_amount(supplier1).value == Decimal("55") # 11 * 5 assert order.get_total_unrefunded_amount(supplier1).value == Decimal("0") assert not order.can_create_refund(supplier1) assert order.can_create_refund() assert order.can_create_refund(supplier2) assert order.can_create_refund(supplier3) assert order.get_total_unrefunded_amount(supplier2).value == Decimal("80") # 16 * 5 assert order.get_total_unrefunded_quantity(supplier2) == Decimal("16") # 3 x product1 and 13 x product2 with pytest.raises(RefundExceedsAmountException): order.create_refund( [{"line": "amount", "quantity": 1, "amount": order.shop.create_price(81)}], supplier=supplier2 ) # Supplier 2 refunds the order order.create_refund(_get_refund_data(order, supplier2)) assert order.get_total_refunded_amount(supplier2).value == Decimal("80") # 11 * 5 assert order.get_total_unrefunded_amount(supplier2).value == Decimal("0") assert not order.can_create_refund(supplier1) assert not order.can_create_refund(supplier2) assert order.can_create_refund() assert order.can_create_refund(supplier3) assert order.get_total_unrefunded_amount(supplier3).value == Decimal("255") # 51 * 5 assert order.get_total_unrefunded_quantity(supplier3) == Decimal("51") # 3 x product1 and 13 x product2 with override_settings(SHUUP_ALLOW_ARBITRARY_REFUNDS=False): with pytest.raises(RefundArbitraryRefundsNotAllowedException): order.create_refund( [{"line": "amount", "quantity": 1, "amount": order.shop.create_price(200)}], supplier=supplier3 ) order.create_refund([{"line": "amount", "quantity": 1, "amount": order.shop.create_price(200)}], supplier=supplier3) assert OrderLine.objects.filter(order=order, supplier=supplier3, type=OrderLineType.REFUND).exists() # Supplier 3 refunds the order order.create_refund(_get_refund_data(order, supplier3)) assert order.get_total_refunded_amount(supplier3).value == Decimal("255") # 11 * 5 assert order.get_total_unrefunded_amount(supplier3).value == Decimal("0") assert not order.can_create_refund(supplier1) assert not order.can_create_refund(supplier2) assert not order.can_create_refund(supplier3) assert not order.can_create_refund()
def test_order_shipment_section(rf, admin_user): shop = get_default_shop() supplier1 = Supplier.objects.create(identifier="1", name="supplier1") supplier1.shops.add(shop) supplier2 = Supplier.objects.create(identifier="2") supplier2.shops.add(shop) product1 = create_product("sku1", shop=shop, default_price=10) shop_product1 = product1.get_shop_instance(shop=shop) shop_product1.suppliers = [supplier1] product2 = create_product("sku3", shop=shop, default_price=10, shipping_mode=ShippingMode.NOT_SHIPPED) shop_product2 = product1.get_shop_instance(shop=shop) shop_product2.suppliers = [supplier2] product_quantities = { supplier1.pk: { product1.pk: 20 }, supplier2.pk: { product2.pk: 10 } } def get_quantity(supplier, product): return product_quantities[supplier.pk][product.pk] order = create_empty_order(shop=shop) order.full_clean() order.save() # Let's test the order shipment section for superuser request = apply_request_middleware(rf.get("/"), user=admin_user, shop=shop) # Add product 3 to order for supplier 2 add_product_to_order(order, supplier2, product2, get_quantity(supplier2, product2), 8) # Product is not shippable so order section should not be available assert not ShipmentSection.visible_for_object(order, request) # Add product 2 to order for supplier 1 add_product_to_order(order, supplier1, product1, get_quantity(supplier1, product1), 7) # Now we should see the shipment section assert ShipmentSection.visible_for_object(order, request) # Make order fully paid so we can start creting shipments and refunds order.cache_prices() order.check_all_verified() order.create_payment(order.taxful_total_price) assert order.is_paid() product_summary = order.get_product_summary() assert product_summary[product1.pk]["unshipped"] == 20 assert product_summary[product2.pk]["unshipped"] == 0 assert product_summary[product2.pk]["ordered"] == 10 # Fully ship the order order.create_shipment({product1: 5}, supplier=supplier1) order.create_shipment({product1: 5}, supplier=supplier1) order.create_shipment({product1: 10}, supplier=supplier1) assert not order.get_unshipped_products() assert order.is_fully_shipped() context = ShipmentSection.get_context_data(order, request) assert len(context["suppliers"]) == 2 assert len(context["create_urls"].keys()) == 2 # One for each supplier assert len(context["delete_urls"].keys()) == 3 # One for each shipment # Let's create staff user without any permissions staff_user = create_random_user(is_staff=True) group = get_default_permission_group() staff_user.groups.add(group) shop.staff_members.add(staff_user) request = apply_request_middleware(rf.get("/"), user=staff_user, shop=shop) context = ShipmentSection.get_context_data(order, request) assert len(context["suppliers"]) == 2 assert len(context["create_urls"].keys()) == 0 assert len(context["delete_urls"].keys()) == 0 set_permissions_for_group(group, ["order.create-shipment"]) request = apply_request_middleware(rf.get("/"), user=staff_user, shop=shop) context = ShipmentSection.get_context_data(order, request) assert len(context["suppliers"]) == 2 assert len(context["create_urls"].keys()) == 2 assert len(context["delete_urls"].keys()) == 0 set_permissions_for_group(group, ["order.create-shipment", "order.delete-shipment"]) request = apply_request_middleware(rf.get("/"), user=staff_user, shop=shop) context = ShipmentSection.get_context_data(order, request) assert len(context["suppliers"]) == 2 assert len(context["create_urls"].keys()) == 2 assert len(context["delete_urls"].keys()) == 3 # Make product1 unshipped product1.shipping_mode = ShippingMode.NOT_SHIPPED product1.save() # We still should see the order shipment section since existing shipments assert ShipmentSection.visible_for_object(order, request) # Let's delete all shipments since both products is unshipped and we # don't need those. for shipment in order.shipments.all(): shipment.soft_delete() assert not ShipmentSection.visible_for_object(order, request)
def test_get_best_selling_products(admin_user): shop1 = get_default_shop() shop2 = get_shop(True) person1 = create_random_person() person1.user = admin_user person1.save() supplier = create_simple_supplier("supplier1") client = _get_client(admin_user) # list best selling products response = client.get("/api/shuup/front/shop_products/best_selling/", {"shop": shop2.pk, "limit": 20}) assert response.status_code == status.HTTP_200_OK products = json.loads(response.content.decode("utf-8")) assert len(products["results"]) == 0 # THIS IS IMPORTANT! cache.clear() products = [create_product("Standard-%d" % x, supplier=supplier, shop=shop2) for x in range(10)] # create 1 product with 4 variations parent_product = create_product("ParentProduct1", supplier=supplier, shop=shop2) children = [create_product("SimpleVarChild-%d" % x, supplier=supplier, shop=shop2) for x in range(4)] for child in children: child.link_to_parent(parent_product) best_selling = defaultdict(int) # create orders with standard products for p_index in range(len(products)): order = create_empty_order(shop=shop2) order.save() qty = (len(products)-p_index) add_product_to_order(order, supplier, products[p_index], qty, Decimal(1.0)) order.create_shipment_of_all_products() order.status = OrderStatus.objects.get_default_complete() order.save(update_fields=("status",)) best_selling[products[p_index].id] = qty # create orders with variation products - the parent product is counted instead of its children for p_index in range(2): variation = random.choice(children) qty = 5 order = create_empty_order(shop=shop2) order.save() add_product_to_order(order, supplier, variation, qty, Decimal(1.0)) order.create_shipment_of_all_products() order.status = OrderStatus.objects.get_default_complete() order.save(update_fields=("status",)) best_selling[parent_product.id] = best_selling[parent_product.id] + qty # get the top 100 best selling products response = client.get("/api/shuup/front/shop_products/best_selling/", {"shop": shop2.pk, "limit": 100}) assert response.status_code == status.HTTP_200_OK products = json.loads(response.content.decode("utf-8")) assert len(products["results"]) == len(best_selling) # as we added less then 100, this must be true assert products["next"] is None # check the if all IDS are part of best selling for ix in range(len(products)): assert products["results"][ix]["product_id"] in best_selling.keys() # get the top 5 best selling products (we should get paginated results) response = client.get("/api/shuup/front/shop_products/best_selling/", {"shop": shop2.pk, "limit": 5}) assert response.status_code == status.HTTP_200_OK products = json.loads(response.content.decode("utf-8")) assert len(products["results"]) == 5 assert products["count"] == len(best_selling) assert products["next"] is not None sorted_best_selling_ids = [prod[0] for prod in sorted(best_selling.items(), key=lambda prod: -prod[1])][:5] # check the if all the 5 best sellers are part of best selling for ix in range(len(products)): assert products["results"][ix]["product_id"] in sorted_best_selling_ids
def test_order_arbitrary_refunds_with_multiple_suppliers(): shop = get_default_shop() supplier1 = Supplier.objects.create(identifier="1", name="supplier1") supplier1.shops.add(shop) supplier2 = Supplier.objects.create(identifier="2") supplier2.shops.add(shop) supplier3 = Supplier.objects.create(identifier="3", name="s") supplier3.shops.add(shop) product1 = create_product("sku1", shop=shop, default_price=10) shop_product1 = product1.get_shop_instance(shop=shop) shop_product1.suppliers.set([supplier1, supplier2, supplier3]) product2 = create_product("sku2", shop=shop, default_price=10) shop_product2 = product1.get_shop_instance(shop=shop) shop_product2.suppliers.set([supplier1, supplier2]) product3 = create_product("sku3", shop=shop, default_price=10, shipping_mode=ShippingMode.NOT_SHIPPED) shop_product3 = product1.get_shop_instance(shop=shop) shop_product3.suppliers.set([supplier3]) product_quantities = { supplier1: {product1: 5, product2: 6}, supplier2: {product1: 3, product2: 13}, supplier3: {product1: 1, product3: 50}, } def get_quantity(supplier, product): return product_quantities[supplier.pk][product.pk] order = create_empty_order(shop=shop) order.full_clean() order.save() for supplier, product_data in six.iteritems(product_quantities): for product, quantity in six.iteritems(product_data): add_product_to_order(order, supplier, product, quantity, 5) # 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.create_payment(order.taxful_total_price) assert order.is_paid() # All supplier should be able to refund the order assert order.can_create_refund() assert order.can_create_refund(supplier1) assert order.can_create_refund(supplier2) assert order.can_create_refund(supplier3) # Step by step refund lines for supplier1 assert order.can_create_refund() assert order.get_total_unrefunded_amount(supplier1).value == Decimal("55") # 11 * 5 assert order.get_total_unrefunded_amount().value == Decimal("390") # 55 + 80 + 255 proudct1_line_for_supplier1 = order.lines.filter(supplier=supplier1, product=product1).first() supplier1_refund_data = [ { "line": proudct1_line_for_supplier1, "quantity": proudct1_line_for_supplier1.quantity, "amount": order.shop.create_price(20).amount, # Line total is 5 * 5 = 25 "restock_products": True, } ] order.create_refund(supplier1_refund_data) assert order.get_total_unrefunded_amount(supplier1).value == Decimal("35") order.create_refund( [{"line": "amount", "quantity": 1, "amount": order.shop.create_price(30).amount}], supplier=supplier1 ) assert order.get_total_unrefunded_amount(supplier1).value == Decimal("5") order.create_refund( [{"line": "amount", "quantity": 1, "amount": order.shop.create_price(5).amount}], supplier=supplier1 ) assert order.get_total_unrefunded_amount(supplier1).value == Decimal("0") assert order.can_create_refund(supplier1) # Some quantity still left to refund proudct2_line_for_supplier1 = order.lines.filter(supplier=supplier1, product=product2).first() supplier1_restock_refund_data = [ { "line": proudct2_line_for_supplier1, "quantity": proudct2_line_for_supplier1.quantity, "amount": order.shop.create_price(0).amount, # Line total is 5 * 5 = 25 "restock_products": True, } ] order.create_refund(supplier1_restock_refund_data) assert not order.can_create_refund(supplier1) # Step by step refund lines for supplier2 assert order.can_create_refund() assert order.get_total_unrefunded_amount(supplier2).value == Decimal("80") # 16 * 5 assert order.get_total_unrefunded_amount().value == Decimal("335") # 80 + 255 proudct2_line_for_supplier2 = order.lines.filter(supplier=supplier2, product=product2).first() supplier2_refund_data = [ { "line": proudct2_line_for_supplier2, "quantity": 10, "amount": order.shop.create_price(50).amount, # Line total is 13 * 5 = 65 "restock_products": True, } ] order.create_refund(supplier2_refund_data) assert order.get_total_unrefunded_amount(supplier2).value == Decimal("30") order.create_refund( [{"line": "amount", "quantity": 1, "amount": order.shop.create_price(5).amount}], supplier=supplier2 ) assert order.get_total_unrefunded_amount(supplier2).value == Decimal("25") order.create_refund( [{"line": "amount", "quantity": 1, "amount": order.shop.create_price(25).amount}], supplier=supplier2 ) assert order.get_total_unrefunded_amount(supplier2).value == Decimal("0") assert order.can_create_refund(supplier2) # Some quantity still left to refund supplier2_restock_refund_data = [ { "line": proudct2_line_for_supplier2, "quantity": 3, "amount": order.shop.create_price(0).amount, # Line total is 5 * 5 = 25 "restock_products": True, } ] order.create_refund(supplier2_restock_refund_data) proudct1_line_for_supplier2 = order.lines.filter(supplier=supplier2, product=product1).first() supplier1_restock_refund_data = [ { "line": proudct1_line_for_supplier2, "quantity": proudct1_line_for_supplier2.quantity, "amount": order.shop.create_price(0).amount, # Line total is 5 * 5 = 25 "restock_products": True, } ] order.create_refund(supplier1_restock_refund_data) assert not order.can_create_refund(supplier2) # Step by step refund lines for supplier3 assert order.can_create_refund() assert order.get_total_unrefunded_amount(supplier3).value == Decimal("255") # 51 * 5 assert order.get_total_unrefunded_amount().value == Decimal("255") # 255 order.create_refund( [{"line": "amount", "quantity": 1, "amount": order.shop.create_price(55).amount}], supplier=supplier3 ) assert order.get_total_unrefunded_amount(supplier3).value == Decimal("200") proudct3_line_for_supplier3 = order.lines.filter(supplier=supplier3, product=product3).first() supplier3_refund_data = [ { "line": proudct3_line_for_supplier3, "quantity": 50, "amount": order.shop.create_price(200).amount, # Line total is 13 * 5 = 65 "restock_products": True, } ] order.create_refund(supplier3_refund_data) assert order.get_total_unrefunded_amount(supplier2).value == Decimal("0") assert order.get_total_unrefunded_amount().value == Decimal("0") assert order.can_create_refund(supplier3) # Some quantity still left to refund proudct1_line_for_supplier3 = order.lines.filter(supplier=supplier3, product=product1).first() supplier3_restock_refund_data = [ { "line": proudct1_line_for_supplier3, "quantity": proudct1_line_for_supplier3.quantity, "amount": order.shop.create_price(0).amount, # Line total is 5 * 5 = 25 "restock_products": True, } ] order.create_refund(supplier3_restock_refund_data) assert not order.can_create_refund(supplier3) assert not order.can_create_refund()
def test_get_best_selling_products(admin_user): shop1 = get_default_shop() shop2 = get_shop(True) person1 = create_random_person() person1.user = admin_user person1.save() supplier = create_simple_supplier("supplier1") client = _get_client(admin_user) # list best selling products response = client.get("/api/shuup/front/shop_products/best_selling/", { "shop": shop2.pk, "limit": 20 }) assert response.status_code == status.HTTP_200_OK products = json.loads(response.content.decode("utf-8")) assert len(products["results"]) == 0 # THIS IS IMPORTANT! cache.clear() products = [ create_product("Standard-%d" % x, supplier=supplier, shop=shop2) for x in range(10) ] # create 1 product with 4 variations parent_product = create_product("ParentProduct1", supplier=supplier, shop=shop2) children = [ create_product("SimpleVarChild-%d" % x, supplier=supplier, shop=shop2) for x in range(4) ] for child in children: child.link_to_parent(parent_product) best_selling = defaultdict(int) # create orders with standard products for p_index in range(len(products)): order = create_empty_order(shop=shop2) order.save() qty = (len(products) - p_index) add_product_to_order(order, supplier, products[p_index], qty, Decimal(1.0)) order.create_shipment_of_all_products() order.status = OrderStatus.objects.get_default_complete() order.save(update_fields=("status", )) best_selling[products[p_index].id] = qty # create orders with variation products - the parent product is counted instead of its children for p_index in range(2): variation = random.choice(children) qty = 5 order = create_empty_order(shop=shop2) order.save() add_product_to_order(order, supplier, variation, qty, Decimal(1.0)) order.create_shipment_of_all_products() order.status = OrderStatus.objects.get_default_complete() order.save(update_fields=("status", )) best_selling[parent_product.id] = best_selling[parent_product.id] + qty # get the top 100 best selling products response = client.get("/api/shuup/front/shop_products/best_selling/", { "shop": shop2.pk, "limit": 100 }) assert response.status_code == status.HTTP_200_OK products = json.loads(response.content.decode("utf-8")) assert len(products["results"]) == len( best_selling) # as we added less then 100, this must be true assert products["next"] is None # check the if all IDS are part of best selling for ix in range(len(products)): assert products["results"][ix]["product_id"] in best_selling.keys() # get the top 5 best selling products (we should get paginated results) response = client.get("/api/shuup/front/shop_products/best_selling/", { "shop": shop2.pk, "limit": 5 }) assert response.status_code == status.HTTP_200_OK products = json.loads(response.content.decode("utf-8")) assert len(products["results"]) == 5 assert products["count"] == len(best_selling) assert products["next"] is not None sorted_best_selling_ids = [ prod[0] for prod in sorted(best_selling.items(), key=lambda prod: -prod[1]) ][:5] # check the if all the 5 best sellers are part of best selling for ix in range(len(products)): assert products["results"][ix]["product_id"] in sorted_best_selling_ids