def test_refund_entire_order_with_restock(admin_user): shop = get_default_shop() supplier = get_simple_supplier() product = create_product("test-sku", shop=get_default_shop(), default_price=10, stock_behavior=StockBehavior.STOCKED) supplier.adjust_stock(product.id, 5) _check_stock_counts(supplier, product, 5, 5) order = create_order_with_product(product, supplier, 2, 200, shop=shop) order.payment_status = PaymentStatus.DEFERRED order.cache_prices() order.save() _check_stock_counts(supplier, product, 5, 3) assert not StockAdjustment.objects.filter(created_by=admin_user).exists() client = _get_client(admin_user) refund_url = "/api/wshop/order/%s/create_full_refund/" % order.id data = {"restock_products": True} response = client.post(refund_url, data, format="json") assert response.status_code == status.HTTP_201_CREATED order.refresh_from_db() # restock logical count _check_stock_counts(supplier, product, 5, 5) assert StockAdjustment.objects.filter(created_by=admin_user).exists()
def test_admin_form(rf, admin_user): supplier = get_simple_supplier() shop = get_default_shop() product = create_product("simple-test-product", shop, supplier) request = apply_request_middleware(rf.get("/"), user=admin_user) frm = SimpleSupplierForm(product=product, request=request) # Form contains 1 product even if the product is not stocked assert len(frm.products) == 1 assert not frm.products[0].is_stocked() product.stock_behavior = StockBehavior.STOCKED # Make product stocked product.save() # Now since product is stocked it should be in the form frm = SimpleSupplierForm(product=product, request=request) assert len(frm.products) == 1 # Add stocked children for product child_product = create_product("child-test-product", shop, supplier) child_product.stock_behavior = StockBehavior.STOCKED child_product.save() child_product.link_to_parent(product) # Admin form should now contain only child products for product frm = SimpleSupplierForm(product=product, request=request) assert len(frm.products) == 1 assert frm.products[0] == child_product
def test_shipment_with_insufficient_stock(): if "wshop.simple_supplier" not in settings.INSTALLED_APPS: pytest.skip("Need wshop.simple_supplier in INSTALLED_APPS") from wshop_tests.simple_supplier.utils import get_simple_supplier shop = get_default_shop() supplier = get_simple_supplier() order = _get_order(shop, supplier, stocked=True) product_line = order.lines.products().first() product = product_line.product assert product_line.quantity == 15 supplier.adjust_stock(product.pk, delta=10) stock_status = supplier.get_stock_status(product.pk) assert stock_status.physical_count == 10 order.create_shipment({product: 5}, supplier=supplier) assert order.shipping_status == ShippingStatus.PARTIALLY_SHIPPED assert order.shipments.all().count() == 1 with pytest.raises(Problem): order.create_shipment({product: 10}, supplier=supplier) # Should be fine after adding more stock supplier.adjust_stock(product.pk, delta=5) order.create_shipment({product: 10}, supplier=supplier)
def test_create_refund_amount(prices_include_tax): supplier = get_simple_supplier() order = _get_order(prices_include_tax, True, True) original_order_total = order.taxful_total_price num_order_lines = order.lines.count() # refund the discount lines first for line in order.lines.discounts(): order.create_refund([ {"line": "amount", "quantity": line.quantity, "amount": line.taxful_price.amount}]) # refund each line 1 by 1 for line in order.lines.products(): order.create_refund([ {"line": "amount", "quantity": 1, "amount": line.taxful_price.amount, "restock_products": True}]) assert order.has_refunds() #assert not order.can_create_refund() assert not order.taxful_total_price_value assert not order.taxless_total_price_value assert order.lines.refunds().count() == num_order_lines # we haven't refunded any quantity so the shipping status remains as-is assert order.shipping_status == ShippingStatus.NOT_SHIPPED assert order.payment_status == PaymentStatus.FULLY_PAID assert order.get_total_refunded_amount() == original_order_total.amount assert not order.get_total_unrefunded_amount().value for line in order.lines.products(): order.create_refund([ {"line": line, "quantity": line.quantity, "amount": Money(0, "EUR"), "restock_products": True}]) assert order.shipping_status == ShippingStatus.FULLY_SHIPPED
def test_order_source(rf, admin_user): """ Test order source validation with stocked products. """ shop = get_default_shop() supplier = get_simple_supplier() product = create_product("simple-test-product", shop, supplier) product.stock_behavior = StockBehavior.STOCKED product.save() quantity = 345 supplier.adjust_stock(product.pk, quantity) assert supplier.get_stock_statuses([product.id])[product.id].logical_count == quantity assert not list(supplier.get_orderability_errors(product.get_shop_instance(shop), quantity, customer=None)) assert list(supplier.get_orderability_errors(product.get_shop_instance(shop), quantity+1, customer=None)) source = seed_source(admin_user, shop) source.add_line( type=OrderLineType.PRODUCT, product=product, supplier=supplier, quantity=quantity, base_unit_price=source.create_price(10), ) assert not list(source.get_validation_errors()) source.add_line( type=OrderLineType.PRODUCT, product=product, supplier=supplier, quantity=quantity, base_unit_price=source.create_price(10), ) assert list(source.get_validation_errors())
def test_refund_entire_order(): shop = get_default_shop() supplier = get_simple_supplier() product = create_product( "test-sku", shop=get_default_shop(), default_price=10, stock_behavior=StockBehavior.STOCKED ) supplier.adjust_stock(product.id, 5) check_stock_counts(supplier, product, 5, 5) order = create_order_with_product(product, supplier, 2, 200, Decimal("0.24"), shop=shop) order.cache_prices() original_total_price = order.taxful_total_price check_stock_counts(supplier, product, 5, 3) # Create a full refund with `restock_products` set to False order.create_full_refund(restock_products=False) # Confirm the refund was created with correct amount assert order.taxless_total_price.amount.value == 0 assert order.taxful_total_price.amount.value == 0 refund_line = order.lines.order_by("ordering").last() assert refund_line.type == OrderLineType.REFUND assert refund_line.taxful_price == -original_total_price # Make sure logical count reflects refunded products check_stock_counts(supplier, product, 5, 3)
def test_create_refund_line_by_line(prices_include_tax): supplier = get_simple_supplier() order = _get_order(prices_include_tax, True, True) for line in order.lines.products(): check_stock_counts(supplier, line.product, INITIAL_PRODUCT_QUANTITY, INITIAL_PRODUCT_QUANTITY - line.quantity) original_order_total = order.taxful_total_price num_order_lines = order.lines.count() # refund the discount lines first for line in order.lines.discounts(): order.create_refund([ {"line": line, "quantity": line.quantity, "amount": line.taxful_price.amount}]) # refund each line 1 by 1 for line in order.lines.products(): order.create_refund([ {"line": line, "quantity": line.quantity, "amount": line.taxful_price.amount, "restock_products": True}]) for line in order.lines.products(): check_stock_counts(supplier, line.product, INITIAL_PRODUCT_QUANTITY, INITIAL_PRODUCT_QUANTITY) assert order.has_refunds() assert not order.can_create_refund() assert not order.taxful_total_price_value assert not order.taxless_total_price_value assert order.lines.refunds().count() == num_order_lines assert order.shipping_status == ShippingStatus.FULLY_SHIPPED assert order.get_total_refunded_amount() == original_order_total.amount assert not order.get_total_unrefunded_amount().value
def test_simple_supplier(rf): supplier = get_simple_supplier() shop = get_default_shop() product = create_product("simple-test-product", shop) ss = supplier.get_stock_status(product.pk) assert ss.product == product assert ss.logical_count == 0 num = random.randint(100, 500) supplier.adjust_stock(product.pk, +num) assert supplier.get_stock_status(product.pk).logical_count == num # Create order order = create_order_with_product(product, supplier, 10, 3, shop=shop) quantities = order.get_product_ids_and_quantities() pss = supplier.get_stock_status(product.pk) assert pss.logical_count == (num - quantities[product.pk]) assert pss.physical_count == num # Create shipment shipment = order.create_shipment_of_all_products(supplier) assert isinstance(shipment, Shipment) pss = supplier.get_stock_status(product.pk) assert pss.logical_count == (num - quantities[product.pk]) assert pss.physical_count == (num - quantities[product.pk]) # Delete shipment with pytest.raises(NotImplementedError): shipment.delete() shipment.soft_delete() assert shipment.is_deleted() pss = supplier.get_stock_status(product.pk) assert pss.logical_count == (num - quantities[product.pk]) assert pss.physical_count == (num)
def test_refund_without_shipment(restock): shop = get_default_shop() supplier = get_simple_supplier() product = create_product( "test-sku", shop=get_default_shop(), default_price=10, stock_behavior=StockBehavior.STOCKED ) # Start out with a supplier with quantity of 10 of a product supplier.adjust_stock(product.id, 10) check_stock_counts(supplier, product, physical=10, logical=10) order = create_order_with_product(product, supplier, 2, 200, shop=shop) order.cache_prices() check_stock_counts(supplier, product, physical=10, logical=8) # Restock value shouldn't matter if we don't have any shipments product_line = order.lines.first() order.create_refund([ {"line": product_line, "quantity": 2, "amount": Money(400, order.currency), "restock_products": restock}]) if restock: check_stock_counts(supplier, product, physical=10, logical=10) else: check_stock_counts(supplier, product, physical=10, logical=8) assert product_line.refunded_quantity == 2 assert order.get_total_tax_amount() == Money( order.taxful_total_price_value - order.taxless_total_price_value, order.currency)
def test_basket_package_product_orderability_change(rf): if "wshop.simple_supplier" not in settings.INSTALLED_APPS: pytest.skip("Need wshop.simple_supplier in INSTALLED_APPS") from wshop_tests.simple_supplier.utils import get_simple_supplier StoredBasket.objects.all().delete() shop = get_default_shop() supplier = get_simple_supplier() product, child = get_unstocked_package_product_and_stocked_child( shop, supplier, child_logical_quantity=2) request = rf.get("/") request.session = {} request.shop = shop apply_request_middleware(request) basket = get_basket(request) # Add the package parent basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1, force_new_line=True, extra={"foo": "foo"}) # Also add the child product separately basket.add_product(supplier=supplier, shop=shop, product=child, quantity=1, force_new_line=True, extra={"foo": "foo"}) # Should be stock for both assert len(basket.get_lines()) == 2 assert len(basket.get_unorderable_lines()) == 0 supplier.adjust_stock(child.id, -1) # Orderability is already cached, we need to uncache to force recheck basket.uncache() # After reducing stock to 1, should only be stock for one assert len(basket.get_lines()) == 1 assert len(basket.get_unorderable_lines()) == 1 supplier.adjust_stock(child.id, -1) basket.uncache() # After reducing stock to 0, should be stock for neither assert len(basket.get_lines()) == 0 assert len(basket.get_unorderable_lines()) == 2
def test_refund_with_shipment(restock): shop = get_default_shop() supplier = get_simple_supplier() product = create_product( "test-sku", shop=get_default_shop(), default_price=10, stock_behavior=StockBehavior.STOCKED ) # Start out with a supplier with quantity of 10 of a product supplier.adjust_stock(product.id, 10) check_stock_counts(supplier, product, physical=10, logical=10) # Order 4 products, make sure product counts are accurate order = create_order_with_product(product, supplier, 4, 200, shop=shop) order.cache_prices() check_stock_counts(supplier, product, physical=10, logical=6) product_line = order.lines.first() # Shipment should decrease physical count by 2, logical by none order.create_shipment({product_line.product: 2}, supplier=supplier) check_stock_counts(supplier, product, physical=8, logical=6) assert order.shipping_status == ShippingStatus.PARTIALLY_SHIPPED # Check correct refunded quantities assert not product_line.refunded_quantity # Create a refund that refunds from unshipped quantity first, then shipped quantity, check stocks check_stock_counts(supplier, product, physical=8, logical=6) order.create_refund([ {"line": product_line, "quantity": 3, "amount": Money(600, order.currency), "restock_products": restock}]) assert product_line.refunded_quantity == 3 assert order.shipping_status == ShippingStatus.FULLY_SHIPPED if restock: check_stock_counts(supplier, product, physical=9, logical=9) else: check_stock_counts(supplier, product, physical=8, logical=6) # Create a second refund that refunds the last shipped quantity, check stocks order.create_refund([ {"line": product_line, "quantity": 1, "amount": Money(200, order.currency), "restock_products": restock}]) assert product_line.refunded_quantity == 4 if restock: # Make sure we're not restocking more than maximum restockable quantity check_stock_counts(supplier, product, physical=10, logical=10) else: # Make sure maximum restockable quantity is not 0 check_stock_counts(supplier, product, physical=8, logical=6) assert order.get_total_tax_amount() == Money( order.taxful_total_price_value - order.taxless_total_price_value, order.currency)
def test_alert_limit_notification(rf, admin_user): supplier = get_simple_supplier() shop = get_default_shop() product = create_product("simple-test-product", shop, supplier) product.stock_behavior = StockBehavior.STOCKED product.save() sc = StockCount.objects.get(supplier=supplier, product=product) sc.alert_limit = 10 sc.save() # nothing in cache cache_key = AlertLimitReached.cache_key_fmt % (supplier.pk, product.pk) assert cache.get(cache_key) is None # put 11 units in stock supplier.adjust_stock(product.pk, +11) # still nothing in cache cache_key = AlertLimitReached.cache_key_fmt % (supplier.pk, product.pk) assert cache.get(cache_key) is None event = AlertLimitReached(product=product, supplier=supplier) assert event.variable_values["dispatched_last_24hs"] is False # stock should be 6, lower then the alert limit supplier.adjust_stock(product.pk, -5) last_run = cache.get(cache_key) assert last_run is not None event = AlertLimitReached(product=product, supplier=supplier) assert event.variable_values["dispatched_last_24hs"] is True # stock should be 1, lower then the alert limit supplier.adjust_stock(product.pk, -5) # last run should be updated assert cache.get(cache_key) != last_run event = AlertLimitReached(product=product, supplier=supplier) assert event.variable_values["dispatched_last_24hs"] is True # fake we have a cache with more than 24hrs cache.set(cache_key, time() - (24 * 60 * 60 * 2)) event = AlertLimitReached(product=product, supplier=supplier) assert event.variable_values["dispatched_last_24hs"] is False
def test_alert_limit_view(rf, admin_user): supplier = get_simple_supplier() shop = get_default_shop() product = create_product("simple-test-product", shop, supplier) sc = StockCount.objects.get(supplier=supplier, product=product) assert not sc.alert_limit test_alert_limit = decimal.Decimal(10) request = apply_request_middleware(rf.get("/"), user=admin_user) request.method = "POST" request.POST = { "alert_limit": test_alert_limit, } response = process_alert_limit(request, supplier.id, product.id) assert response.status_code == 200 sc = StockCount.objects.get(supplier=supplier, product=product) assert sc.alert_limit == test_alert_limit
def _get_custom_order(regular_user, **kwargs): prices_include_tax = kwargs.pop("prices_include_tax", False) include_basket_campaign = kwargs.pop("include_basket_campaign", False) include_catalog_campaign = kwargs.pop("include_catalog_campaign", False) shop = get_shop(prices_include_tax=prices_include_tax) supplier = get_simple_supplier() if include_basket_campaign: _add_basket_campaign(shop) if include_catalog_campaign: _add_catalog_campaign(shop) _add_taxes() contact = get_person_contact(regular_user) source = BasketishOrderSource(shop) source.status = get_initial_order_status() source.customer = contact ctx = get_pricing_module().get_context_from_data(shop, contact) for product_data in _get_product_data(): quantity = product_data.pop("quantity") product = create_product(sku=product_data.pop("sku"), shop=shop, supplier=supplier, stock_behavior=StockBehavior.STOCKED, tax_class=get_default_tax_class(), **product_data) shop_product = product.get_shop_instance(shop) shop_product.categories.add(get_default_category()) shop_product.save() supplier.adjust_stock(product.id, INITIAL_PRODUCT_QUANTITY) pi = product.get_price_info(ctx) source.add_line(type=OrderLineType.PRODUCT, product=product, supplier=supplier, quantity=quantity, base_unit_price=pi.base_unit_price, discount_amount=pi.discount_amount) oc = OrderCreator() order = oc.create_order(source) return order
def _get_order(prices_include_tax=False, include_basket_campaign=False, include_catalog_campaign=False): shop = get_shop(prices_include_tax=prices_include_tax) supplier = get_simple_supplier() if include_basket_campaign: _add_basket_campaign(shop) if include_catalog_campaign: _add_catalog_campaign(shop) _add_taxes() source = BasketishOrderSource(shop) source.status = get_initial_order_status() ctx = get_pricing_module().get_context_from_data(shop, AnonymousContact()) for product_data in _get_product_data(): quantity = product_data.pop("quantity") product = create_product( sku=product_data.pop("sku"), shop=shop, supplier=supplier, stock_behavior=StockBehavior.STOCKED, tax_class=get_default_tax_class(), **product_data) shop_product = product.get_shop_instance(shop) shop_product.categories.add(get_default_category()) shop_product.save() supplier.adjust_stock(product.id, INITIAL_PRODUCT_QUANTITY) pi = product.get_price_info(ctx) source.add_line( type=OrderLineType.PRODUCT, product=product, supplier=supplier, quantity=quantity, base_unit_price=pi.base_unit_price, discount_amount=pi.discount_amount ) oc = OrderCreator() order = oc.create_order(source) order.create_payment(Money("1", "EUR")) assert not order.has_refunds() assert order.can_create_refund() assert order.shipping_status == ShippingStatus.NOT_SHIPPED assert order.payment_status == PaymentStatus.PARTIALLY_PAID return order
def test_supplier_with_stock_counts_2(rf, admin_user, settings): with override_settings(WSHOP_HOME_CURRENCY="USD", WSHOP_ENABLE_MULTIPLE_SHOPS=False): supplier = get_simple_supplier() shop = get_default_shop() assert shop.prices_include_tax assert shop.currency != settings.WSHOP_HOME_CURRENCY product = create_product("simple-test-product", shop, supplier) quantity = random.randint(100, 600) supplier.adjust_stock(product.pk, quantity) adjust_quantity = random.randint(100, 600) request = apply_request_middleware(rf.get("/"), user=admin_user) request.POST = { "purchase_price": decimal.Decimal(32.00), "delta": adjust_quantity } response = process_stock_adjustment(request, supplier.id, product.id) assert response.status_code == 400 # Only POST is allowed request.method = "POST" response = process_stock_adjustment(request, supplier.id, product.id) assert response.status_code == 200 pss = supplier.get_stock_status(product.pk) # Product stock values should be adjusted assert pss.logical_count == (quantity + adjust_quantity) # test price properties sa = StockAdjustment.objects.first() assert sa.purchase_price.currency == shop.currency assert sa.purchase_price.includes_tax sc = StockCount.objects.first() assert sc.stock_value.currency == shop.currency assert sc.stock_value.includes_tax assert sc.stock_unit_price.currency == shop.currency assert sc.stock_unit_price.includes_tax settings.WSHOP_ENABLE_MULTIPLE_SHOPS = True sa = StockAdjustment.objects.first() # refetch to invalidate cache assert sa.purchase_price.currency != shop.currency assert sa.purchase_price.currency == settings.WSHOP_HOME_CURRENCY assert not sa.purchase_price.includes_tax sc = StockCount.objects.first() assert sc.stock_value.currency == settings.WSHOP_HOME_CURRENCY assert not sc.stock_value.includes_tax assert sc.stock_unit_price.currency == settings.WSHOP_HOME_CURRENCY assert not sc.stock_unit_price.includes_tax
def test_product_summary(): shop = get_default_shop() supplier = get_simple_supplier() product = create_product( "test-sku", shop=get_default_shop(), default_price=10, stock_behavior=StockBehavior.STOCKED ) supplier.adjust_stock(product.id, 5) # Order with 2 unshipped, non-refunded items and a shipping cost order = create_order_with_product(product, supplier, 2, 200, shop=shop) order.cache_prices() product_line = order.lines.first() shipping_line = order.lines.create(type=OrderLineType.SHIPPING, base_unit_price_value=5, quantity=1) # Make sure no invalid entries and check product quantities product_summary = order.get_product_summary() assert all(product_summary.keys()) summary = product_summary[product.id] assert_defaultdict_values(summary, ordered=2, shipped=0, refunded=0, unshipped=2) # Create a shipment for the other item, make sure status changes assert order.shipping_status == ShippingStatus.NOT_SHIPPED assert order.can_create_shipment() order.create_shipment(supplier=supplier, product_quantities={product: 1}) assert order.shipping_status == ShippingStatus.PARTIALLY_SHIPPED order.create_refund([{"line": shipping_line, "quantity": 1, "amount": Money(5, order.currency), "restock": False}]) product_summary = order.get_product_summary() assert all(product_summary.keys()) summary = product_summary[product.id] assert_defaultdict_values(summary, ordered=2, shipped=1, refunded=0, unshipped=1) # Create a refund for 2 items, we should get no negative values order.create_refund([{"line": product_line, "quantity": 2, "amount": Money(200, order.currency), "restock": False}]) product_summary = order.get_product_summary() assert all(product_summary.keys()) summary = product_summary[product.id] assert_defaultdict_values(summary, ordered=2, shipped=1, refunded=2, unshipped=0)
def test_simple_supplier(rf): supplier = get_simple_supplier() shop = get_default_shop() product = create_product("simple-test-product", shop) ss = supplier.get_stock_status(product.pk) assert ss.product == product assert ss.logical_count == 0 product_qty = 5 shipment = Shipment.objects.create(supplier=supplier, type=ShipmentType.IN) ShipmentProduct.objects.create(shipment=shipment, product=product, quantity=product_qty) assert shipment.status == ShipmentStatus.NOT_SENT shipment.set_received() assert shipment.status == ShipmentStatus.RECEIVED ss = supplier.get_stock_status(product.pk) assert ss.product == product assert ss.logical_count == 5
def test_create_full_refund(prices_include_tax): supplier = get_simple_supplier() order = _get_order(prices_include_tax, True, True) original_order_total = order.taxful_total_price num_order_lines = order.lines.count() order.create_full_refund(restock_products=True) for line in order.lines.products(): check_stock_counts(supplier, line.product, INITIAL_PRODUCT_QUANTITY, INITIAL_PRODUCT_QUANTITY) assert order.has_refunds() assert not order.can_create_refund() assert not order.taxful_total_price_value assert not order.taxless_total_price_value assert order.lines.refunds().count() == num_order_lines assert order.shipping_status == ShippingStatus.FULLY_SHIPPED assert order.payment_status == PaymentStatus.FULLY_PAID assert order.get_total_refunded_amount() == original_order_total.amount assert not order.get_total_unrefunded_amount().value assert not order.get_total_unrefunded_quantity()
def test_new_product_admin_form_renders(rf, client, admin_user): """ Make sure that no exceptions are raised when creating a new product with simple supplier enabled """ shop = get_default_shop() request = apply_request_middleware(rf.get("/"), user=admin_user) view = ProductEditView.as_view() supplier = get_simple_supplier() supplier.stock_managed = True supplier.save() # This should not raise an exception view(request).render() supplier.stock_managed = False supplier.save() # Nor should this view(request).render()
def test_refund_entire_order_without_restock(admin_user): shop = get_default_shop() supplier = get_simple_supplier() product = create_product("test-sku", shop=get_default_shop(), default_price=10, stock_behavior=StockBehavior.STOCKED) supplier.adjust_stock(product.id, 5) _check_stock_counts(supplier, product, 5, 5) order = create_order_with_product(product, supplier, 2, 200, Decimal("0.24"), shop=shop) order.payment_status = PaymentStatus.DEFERRED order.cache_prices() order.save() original_total_price = order.taxful_total_price _check_stock_counts(supplier, product, 5, 3) client = _get_client(admin_user) refund_url = "/api/wshop/order/%s/create_full_refund/" % order.id data = {"restock_products": False} response = client.post(refund_url, data, format="json") assert response.status_code == status.HTTP_201_CREATED order.refresh_from_db() # Confirm the refund was created with correct amount assert order.taxless_total_price.amount.value == 0 assert order.taxful_total_price.amount.value == 0 refund_line = order.lines.order_by("ordering").last() assert refund_line.type == OrderLineType.REFUND assert refund_line.taxful_price == -original_total_price # Make sure logical count reflects refunded products _check_stock_counts(supplier, product, 5, 3)
def test_partial_refund_limits(restock): shop = get_default_shop() supplier = get_simple_supplier() product = create_product( "test-sku", shop=get_default_shop(), default_price=10, stock_behavior=StockBehavior.STOCKED ) # Start out with a supplier with quantity of 10 of a product supplier.adjust_stock(product.id, 10) check_stock_counts(supplier, product, physical=10, logical=10) quantity = 2 order = create_order_with_product(product, supplier, quantity, 200, shop=shop) order.cache_prices() check_stock_counts(supplier, product, physical=10, logical=8) # try creating more partial refunds than possible product_line = order.lines.first() def create_refund(): order.create_refund([ {"line": product_line, "quantity": 1, "amount": Money(1, order.currency), "restock_products": restock}]) # create more refunds than available for index in range(quantity + 1): if index == quantity: with pytest.raises(RefundExceedsQuantityException): create_refund() else: create_refund() if restock: check_stock_counts(supplier, product, physical=10, logical=10) else: check_stock_counts(supplier, product, physical=10, logical=8) assert product_line.refunded_quantity == 2
def test_refund_entire_order_with_product_restock(): shop = get_default_shop() supplier = get_simple_supplier() product = create_product( "test-sku", shop=get_default_shop(), default_price=10, stock_behavior=StockBehavior.STOCKED ) supplier.adjust_stock(product.id, 5) check_stock_counts(supplier, product, 5, 5) order = create_order_with_product(product, supplier, 2, 200, shop=shop) order.cache_prices() check_stock_counts(supplier, product, 5, 3) # Create a full refund with `restock_products` set to True order.create_full_refund(restock_products=True) # restock logical count check_stock_counts(supplier, product, 5, 5)
def test_simple_supplier_out_of_stock(rf, anonymous): supplier = get_simple_supplier() shop = get_default_shop() product = create_product("simple-test-product", shop, supplier, stock_behavior=StockBehavior.STOCKED) if anonymous: customer = AnonymousContact() else: customer = create_random_person() ss = supplier.get_stock_status(product.pk) assert ss.product == product assert ss.logical_count == 0 num = random.randint(100, 500) supplier.adjust_stock(product.pk, +num) assert supplier.get_stock_status(product.pk).logical_count == num shop_product = product.get_shop_instance(shop) assert shop_product.is_orderable(supplier, customer, 1) # Create order order = create_order_with_product(product, supplier, num, 3, shop=shop) order.get_product_ids_and_quantities() pss = supplier.get_stock_status(product.pk) assert pss.logical_count == 0 assert pss.physical_count == num assert not shop_product.is_orderable(supplier, customer, 1) # Create shipment shipment = order.create_shipment_of_all_products(supplier) assert isinstance(shipment, Shipment) pss = supplier.get_stock_status(product.pk) assert pss.logical_count == 0 assert pss.physical_count == 0 assert not shop_product.is_orderable(supplier, customer, 1)
def test_can_create_shipment(): shop = get_default_shop() supplier = get_simple_supplier() product = create_product( "test-sku", shop=get_default_shop(), default_price=10, stock_behavior=StockBehavior.STOCKED ) supplier.adjust_stock(product.id, 10) order = create_order_with_product(product, supplier, 1, 200, shop=shop) assert order.can_create_shipment() # Fully shipped orders can't create shipments order.create_shipment_of_all_products(supplier) assert not order.can_create_shipment() order = create_order_with_product(product, supplier, 1, 200, shop=shop) assert order.can_create_shipment() # Canceled orders can't create shipments order.set_canceled() assert not order.can_create_shipment()
def test_simple_supplier(rf): supplier = get_simple_supplier() shop = get_default_shop() product = create_product("simple-test-product", shop) ss = supplier.get_stock_status(product.pk) assert ss.product == product assert ss.logical_count == 0 num = random.randint(100, 500) supplier.adjust_stock(product.pk, +num) assert supplier.get_stock_status(product.pk).logical_count == num # Create order ... order = create_order_with_product(product, supplier, 10, 3, shop=shop) quantities = order.get_product_ids_and_quantities() pss = supplier.get_stock_status(product.pk) assert pss.logical_count == (num - quantities[product.pk]) assert pss.physical_count == num # Create shipment ... order.create_shipment_of_all_products(supplier) pss = supplier.get_stock_status(product.pk) assert pss.physical_count == (num - quantities[product.pk]) # Cancel order... order.set_canceled() pss = supplier.get_stock_status(product.pk) assert pss.logical_count == (num)
def test_supplier_with_stock_counts(rf): supplier = get_simple_supplier() shop = get_default_shop() product = create_product("simple-test-product", shop, supplier) quantity = random.randint(100, 600) supplier.adjust_stock(product.pk, quantity) assert supplier.get_stock_statuses( [product.id])[product.id].logical_count == quantity # No orderability errors since product is not stocked assert not list( supplier.get_orderability_errors( product.get_shop_instance(shop), quantity + 1, customer=None)) product.stock_behavior = StockBehavior.STOCKED # Make product stocked product.save() assert not list( supplier.get_orderability_errors( product.get_shop_instance(shop), quantity, customer=None)) # Now since product is stocked we get orderability error with quantity + 1 assert list( supplier.get_orderability_errors(product.get_shop_instance(shop), quantity + 1, customer=None))
def test_get_listed_products_orderable_only(): context = get_jinja_context() shop = get_default_shop() simple_supplier = get_simple_supplier() n_products = 2 # Create product without stock product = create_product("test-sku", supplier=simple_supplier, shop=shop, stock_behavior=StockBehavior.STOCKED) assert len( general.get_listed_products(context, n_products, orderable_only=True)) == 0 assert len( general.get_listed_products(context, n_products, orderable_only=False)) == 1 # Increase stock on product quantity = product.get_shop_instance(shop).minimum_purchase_quantity simple_supplier.adjust_stock(product.id, quantity) assert len( general.get_listed_products(context, n_products, orderable_only=True)) == 1 assert len( general.get_listed_products(context, n_products, orderable_only=False)) == 1 # Decrease stock on product simple_supplier.adjust_stock(product.id, -quantity) assert len( general.get_listed_products(context, n_products, orderable_only=True)) == 0 assert len( general.get_listed_products(context, n_products, orderable_only=False)) == 1
def test_order_creator_with_package_product(rf, admin_user): if "wshop.simple_supplier" not in settings.INSTALLED_APPS: pytest.skip("Need wshop.simple_supplier in INSTALLED_APPS") from wshop_tests.simple_supplier.utils import get_simple_supplier shop = get_default_shop() supplier = get_simple_supplier() package_product = create_package_product("Package-Product-Test", shop=shop, supplier=supplier, children=2) shop_product = package_product.get_shop_instance(shop) quantity_map = package_product.get_package_child_to_quantity_map() product_1, product_2 = quantity_map.keys() product_1.stock_behavior = StockBehavior.STOCKED product_1.save() product_2.stock_behavior = StockBehavior.STOCKED product_2.save() assert quantity_map[product_1] == 1 assert quantity_map[product_2] == 2 supplier.adjust_stock(product_1.pk, 1) supplier.adjust_stock(product_2.pk, 2) assert supplier.get_stock_status(product_1.pk).logical_count == 1 assert supplier.get_stock_status(product_2.pk).logical_count == 2 creator = OrderCreator() # There should be no exception when creating order with only package product source = seed_source(admin_user) source.add_line( type=OrderLineType.PRODUCT, product=package_product, supplier=supplier, quantity=1, base_unit_price=source.create_price(10), ) order = creator.create_order(source) # However, there should not be enough stock for both package and child products source = seed_source(admin_user) source.add_line( type=OrderLineType.PRODUCT, product=package_product, supplier=supplier, quantity=1, base_unit_price=source.create_price(10), ) source.add_line( type=OrderLineType.PRODUCT, product=product_1, supplier=supplier, quantity=1, base_unit_price=source.create_price(10), ) # And a validation error should be raised with pytest.raises(ValidationError): order = creator.create_order(source)
def test_adjust_stock(admin_user): get_default_shop() sp = get_default_shop_product() sp.stock_behavior = StockBehavior.STOCKED sp.save() client = _get_client(admin_user) supplier1 = get_simple_supplier() supplier2 = get_default_supplier() # invalid type response = client.post("/api/wshop/supplier/%s/adjust_stock/" % supplier1.pk, format="json", data={ "product": sp.product.pk, "delta": 100, "type": 200 }) assert response.status_code == status.HTTP_400_BAD_REQUEST data = json.loads(response.content.decode("utf-8")) assert "type" in data # invalid supplier response = client.post("/api/wshop/supplier/%s/adjust_stock/" % 100, format="json", data={ "product": sp.product.pk, "delta": 100, "type": StockAdjustmentType.INVENTORY.value }) assert response.status_code == status.HTTP_404_NOT_FOUND # invalid product response = client.post("/api/wshop/supplier/%s/adjust_stock/" % supplier1.pk, format="json", data={ "product": 100, "delta": 100, "type": StockAdjustmentType.INVENTORY.value }) assert response.status_code == status.HTTP_400_BAD_REQUEST data = json.loads(response.content.decode("utf-8")) assert "product" in data # invalid delta response = client.post("/api/wshop/supplier/%s/adjust_stock/" % supplier1.pk, format="json", data={ "product": sp.product.pk, "delta": "not-a-number", "type": StockAdjustmentType.INVENTORY.value }) assert response.status_code == status.HTTP_400_BAD_REQUEST data = json.loads(response.content.decode("utf-8")) assert "delta" in data # adjust stock not implemented response = client.post("/api/wshop/supplier/%s/adjust_stock/" % supplier2.pk, format="json", data={ "product": sp.product.pk, "delta": 100, "type": StockAdjustmentType.INVENTORY.value }) assert response.status_code == status.HTTP_400_BAD_REQUEST # add 100 to inventory response = client.post("/api/wshop/supplier/%s/adjust_stock/" % supplier1.pk, format="json", data={ "product": sp.product.pk, "delta": 100, "type": StockAdjustmentType.RESTOCK.value }) assert response.status_code == status.HTTP_200_OK stock = supplier1.get_stock_status(sp.product.pk) assert stock.logical_count == 100 assert stock.physical_count == 100 # remove the stocks adjustments and calculate the stock again StockAdjustment.objects.all().delete() # update the stock, response = client.post("/api/wshop/supplier/%s/update_stocks/" % supplier1.pk, format="json", data={ "products": [sp.product.pk] }) assert response.status_code == status.HTTP_200_OK # everything should be zero stock = supplier1.get_stock_status(sp.product.pk) assert stock.logical_count == 0 assert stock.physical_count == 0