def test_basic_order_flow(with_company): cache.clear() create_default_order_statuses() n_orders_pre = Order.objects.count() populate_if_required() c = SmartClient() product_ids = _populate_client_basket(c) addresses_path = reverse("wshop:checkout", kwargs={"phase": "addresses"}) addresses_soup = c.soup(addresses_path) inputs = fill_address_inputs(addresses_soup, with_company=with_company) response = c.post(addresses_path, data=inputs) assert response.status_code == 302 # Should redirect forth methods_path = reverse("wshop:checkout", kwargs={"phase": "methods"}) methods_soup = c.soup(methods_path) assert c.post(methods_path, data=extract_form_fields( methods_soup)).status_code == 302 # Should redirect forth confirm_path = reverse("wshop:checkout", kwargs={"phase": "confirm"}) confirm_soup = c.soup(confirm_path) Product.objects.get(pk=product_ids[0]).soft_delete() assert c.post(confirm_path, data=extract_form_fields( confirm_soup)).status_code == 200 # user needs to reconfirm data = extract_form_fields(confirm_soup) data['product_ids'] = ','.join(product_ids[1:]) assert c.post(confirm_path, data=data).status_code == 302 # Should redirect forth n_orders_post = Order.objects.count() assert n_orders_post > n_orders_pre, "order was created"
def test_order_statuses(admin_user): create_default_order_statuses() source = seed_source(admin_user) source.add_line( type=OrderLineType.PRODUCT, product=get_default_product(), supplier=get_default_supplier(), quantity=1, base_unit_price=source.create_price(10), ) source.add_line( type=OrderLineType.OTHER, quantity=1, base_unit_price=source.create_price(10), require_verification=True, ) creator = OrderCreator() order = creator.create_order(source) # new order, status/role is new/initial assert order.status.identifier == DefaultOrderStatus.INITIAL.value assert order.status.role == OrderStatusRole.INITIAL # FUTURE: order gets payment the status changes to processing/processing total = order.taxful_total_price.amount order.create_payment(total) assert order.status.identifier == DefaultOrderStatus.INITIAL.value assert order.status.role == OrderStatusRole.INITIAL # FUTURE: order is fully shipped the status changes to complete/complete order.create_shipment_of_all_products() assert order.status.identifier == DefaultOrderStatus.INITIAL.value assert order.status.role == OrderStatusRole.INITIAL
def test_create_without_a_contact(admin_user): create_default_order_statuses() shop = get_default_shop() sm = get_default_shipping_method() pm = get_default_payment_method() assert not Order.objects.count() client = _get_client(admin_user) lines = [ { "type": "other", "sku": "hello", "text": "A greeting", "quantity": 1, "base_unit_price_value": "3.5" }, ] response = client.post("/api/wshop/order/", content_type="application/json", data=json.dumps({ "shop": shop.pk, "customer": None, "shipping_method": sm.pk, "payment_method": pm.pk, "lines": lines })) assert response.status_code == 201 assert Order.objects.count() == 1 order = Order.objects.first() assert order.shop == shop assert order.customer == None assert order.creator == admin_user assert order.shipping_method == sm assert order.payment_method == pm assert order.billing_address == None assert order.shipping_address == None assert order.payment_status == PaymentStatus.NOT_PAID assert order.shipping_status == ShippingStatus.NOT_SHIPPED assert order.status == OrderStatus.objects.get_default_initial() assert order.taxful_total_price_value == decimal.Decimal(3.5) assert order.lines.count( ) == 3 # shipping line, payment line, 2 product lines, 2 other lines for idx, line in enumerate(order.lines.all()[:1]): assert line.quantity == decimal.Decimal(lines[idx].get("quantity")) assert line.base_unit_price_value == decimal.Decimal(lines[idx].get( "base_unit_price_value", 0)) assert line.discount_amount_value == decimal.Decimal(lines[idx].get( "discount_amount_value", 0))
def test_checkout_empty_basket(rf): cache.clear() create_default_order_statuses() n_orders_pre = Order.objects.count() populate_if_required() c = SmartClient() product_ids = _populate_client_basket(c) addresses_path = reverse("wshop:checkout", kwargs={"phase": "addresses"}) addresses_soup = c.soup(addresses_path) inputs = fill_address_inputs(addresses_soup) for product_id in product_ids: Product.objects.get(pk=product_id).soft_delete() response, soup = c.response_and_soup(addresses_path, data=inputs, method="post") assert response.status_code == 200 # Should redirect forth assert b"Your shopping cart is empty." in soup.renderContents()
def test_get_by_status(admin_user): create_default_order_statuses() shop = get_default_shop() cancelled_status = OrderStatus.objects.get_default_canceled() for i in range(1, 10): order = create_empty_order(shop=shop) order.status = cancelled_status order.save() order = create_empty_order(shop=shop) order.save() client = _get_client(admin_user) response = client.get("/api/wshop/order/", data={"status": order.status.id}) assert response.status_code == status.HTTP_200_OK order_data = json.loads(response.content.decode("utf-8")) assert len(order_data) == 1 assert order_data[0].get("id") == order.id assert order_data[0].get("identifier") == order.identifier response = client.get("/api/wshop/order/", data={"status": cancelled_status.id}) assert response.status_code == status.HTTP_200_OK order_data = json.loads(response.content.decode("utf-8")) assert len(order_data) == 9 assert order.can_set_complete() old_status = order.status order.status = OrderStatus.objects.get_default_complete() order.save() assert old_status != order.status response = client.get("/api/wshop/order/", data={"status": old_status.id}) assert response.status_code == status.HTTP_200_OK order_data = json.loads(response.content.decode("utf-8")) assert len(order_data) == 0 response = client.get("/api/wshop/order/", data={"status": order.status.id}) assert response.status_code == status.HTTP_200_OK order_data = json.loads(response.content.decode("utf-8")) assert len(order_data) == 1
def test_single_default_status_for_role(): create_default_order_statuses() new_default_cancel = OrderStatus.objects.create( identifier="foo", role=OrderStatusRole.CANCELED, name="foo", default=True) assert new_default_cancel.default assert OrderStatus.objects.get_default_canceled() == new_default_cancel new_default_cancel.delete() # We can use this weird moment to cover the "no default" case, yay with pytest.raises(ObjectDoesNotExist): OrderStatus.objects.get_default_canceled() old_cancel = OrderStatus.objects.get( identifier=DefaultOrderStatus.CANCELED.value) assert not old_cancel.default # This will have been reset when another status became the default old_cancel.default = True old_cancel.save()
def test_order_statuses_are_translatable(): create_default_order_statuses() assert OrderStatus.objects.translated( get_language()).count() == OrderStatus.objects.count()
def test_create_order(admin_user, target_customer): with override_settings(**REQUIRED_SETTINGS): set_configuration() factories.create_default_order_statuses() shop = factories.get_default_shop() client = _get_client(admin_user) # Create basket for target customer payload = {"shop": shop.pk} target = orderer = get_person_contact(admin_user) if target_customer == "other_person": target = orderer = factories.create_random_person() payload["customer"] = target.pk elif target_customer == "company": target = factories.create_random_company() orderer = factories.create_random_person() payload.update({"customer": target.pk, "orderer": orderer.pk}) target.members.add(orderer) response = client.post("/api/wshop/basket/new/", payload) assert response.status_code == status.HTTP_201_CREATED basket_data = json.loads(response.content.decode("utf-8")) basket = Basket.objects.first() assert basket.key == basket_data["uuid"].split("-")[1] assert basket.customer == target assert basket.orderer == orderer shop_product = factories.get_default_shop_product() shop_product.default_price = TaxfulPrice(1, shop.currency) shop_product.save() # Add shop product to basket payload = {"shop_product": shop_product.id} response = client.post( "/api/wshop/basket/{}-{}/add/".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"]) == 1 # Create order from basket response = client.post( "/api/wshop/basket/{}-{}/create_order/".format( shop.pk, basket.key), payload) assert response.status_code == status.HTTP_400_BAD_REQUEST response_data = json.loads(response.content.decode("utf-8")) assert "errors" in response_data factories.get_default_payment_method() factories.get_default_shipping_method() response = client.post( "/api/wshop/basket/{}-{}/create_order/".format( shop.pk, basket.key), payload) assert response.status_code == status.HTTP_201_CREATED response_data = json.loads(response.content.decode("utf-8")) basket.refresh_from_db() assert basket.finished order = Order.objects.get( reference_number=response_data["reference_number"]) assert order.status == OrderStatus.objects.get_default_initial() assert order.payment_status == PaymentStatus.NOT_PAID assert order.shipping_status == ShippingStatus.NOT_SHIPPED assert not order.payment_method assert not order.shipping_method assert float(order.taxful_total_price_value) == 1 assert order.customer == target assert order.orderer == orderer assert order.creator == admin_user assert not order.billing_address assert not order.shipping_address
def test_order_create_without_shipping_or_billing_method(admin_user): create_default_order_statuses() shop = get_default_shop() contact = create_random_person(locale="en_US", minimum_name_comp_len=5) product = create_product(sku=printable_gibberish(), supplier=get_default_supplier(), shop=shop) assert not Order.objects.count() client = _get_client(admin_user) lines = [ { "type": "product", "product": product.id, "quantity": "1", "base_unit_price_value": "5.00" }, { "type": "product", "product": product.id, "quantity": "2", "base_unit_price_value": "1.00", "discount_amount_value": "0.50" }, { "type": "other", "sku": "hello", "text": "A greeting", "quantity": 1, "base_unit_price_value": "3.5" }, { "type": "text", "text": "This was an order!", "quantity": 0 }, ] response = client.post("/api/wshop/order/", content_type="application/json", data=json.dumps({ "shop": shop.pk, "customer": contact.pk, "lines": lines })) assert response.status_code == 201 assert Order.objects.count() == 1 order = Order.objects.first() assert order.shop == shop assert order.shipping_method is None assert order.payment_method is None assert order.customer == contact assert order.creator == admin_user assert order.billing_address == contact.default_billing_address.to_immutable( ) assert order.shipping_address == contact.default_shipping_address.to_immutable( ) assert order.payment_status == PaymentStatus.NOT_PAID assert order.shipping_status == ShippingStatus.NOT_SHIPPED assert order.status == OrderStatus.objects.get_default_initial() assert order.taxful_total_price_value == decimal.Decimal(10) assert order.lines.count() == 4 # 2 product lines, 2 other lines for idx, line in enumerate(order.lines.all()[:4]): assert line.quantity == decimal.Decimal(lines[idx].get("quantity")) assert line.base_unit_price_value == decimal.Decimal(lines[idx].get( "base_unit_price_value", 0)) assert line.discount_amount_value == decimal.Decimal(lines[idx].get( "discount_amount_value", 0))
def test_create_order(admin_user, currency): create_default_order_statuses() shop = get_default_shop() shop.currency = currency tax = get_default_tax() Currency.objects.get_or_create(code=currency, decimal_places=2) shop.save() sm = get_default_shipping_method() pm = get_default_payment_method() contact = create_random_person(locale="en_US", minimum_name_comp_len=5) default_group = get_default_customer_group() default_group.members.add(contact) account_manager = create_random_person(locale="en_US", minimum_name_comp_len=5) contact.account_manager = account_manager contact.save() product = create_product(sku=printable_gibberish(), supplier=get_default_supplier(), shop=shop) assert not Order.objects.count() client = _get_client(admin_user) lines = [ { "type": "product", "product": product.id, "quantity": "1", "base_unit_price_value": "5.00" }, { "type": "product", "product": product.id, "quantity": "2", "base_unit_price_value": "1.00", "discount_amount_value": "0.50" }, { "type": "other", "sku": "hello", "text": "A greeting", "quantity": 1, "base_unit_price_value": "3.5" }, { "type": "text", "text": "This was an order!", "quantity": 0 }, ] response = client.post("/api/wshop/order/", content_type="application/json", data=json.dumps({ "shop": shop.pk, "shipping_method": sm.pk, "payment_method": pm.pk, "customer": contact.pk, "lines": lines })) assert response.status_code == 201 assert Order.objects.count() == 1 order = Order.objects.first() assert order.shop == shop assert order.shipping_method == sm assert order.payment_method == pm assert order.customer == contact assert order.creator == admin_user assert order.billing_address == contact.default_billing_address.to_immutable( ) assert order.shipping_address == contact.default_shipping_address.to_immutable( ) assert order.payment_status == PaymentStatus.NOT_PAID assert order.shipping_status == ShippingStatus.NOT_SHIPPED assert order.status == OrderStatus.objects.get_default_initial() assert order.taxful_total_price_value == decimal.Decimal(10) assert order.lines.count( ) == 6 # shipping line, payment line, 2 product lines, 2 other lines assert order.currency == currency for idx, line in enumerate(order.lines.all()[:4]): assert line.quantity == decimal.Decimal(lines[idx].get("quantity")) assert line.base_unit_price_value == decimal.Decimal(lines[idx].get( "base_unit_price_value", 0)) assert line.discount_amount_value == decimal.Decimal(lines[idx].get( "discount_amount_value", 0)) # Test tax summary response_data = json.loads(response.content.decode("utf-8")) # Tax summary should not be present here assert "summary" not in response_data response = client.get('/api/wshop/order/{}/taxes/'.format(order.pk)) assert response.status_code == status.HTTP_200_OK response_data = json.loads(response.content.decode("utf-8")) assert "lines" in response_data assert "summary" in response_data line_summary = response_data["lines"] summary = response_data["summary"] first_tax_summary = summary[0] assert int(first_tax_summary["tax_id"]) == tax.id assert first_tax_summary["tax_rate"] == tax.rate first_line_summary = line_summary[0] assert "tax" in first_line_summary response = client.get("/api/wshop/order/%s/" % order.id) assert response.status_code == status.HTTP_200_OK order_data = json.loads(response.content.decode("utf-8")) assert order_data.get("id") == order.id assert "available_shipping_methods" in order_data assert "available_payment_methods" in order_data assert order_data["available_payment_methods"][0]["id"] == pm.id assert order_data["available_shipping_methods"][0]["id"] == sm.id assert order.account_manager == account_manager assert order.customer_groups.count() == contact.groups.count() for group in order.customer_groups.all(): assert contact.groups.filter(id=group.id).exists() assert order.tax_group is not None assert order.tax_group == contact.tax_group
def test_order_flow_with_phases(get_shipping_method, shipping_data, get_payment_method, payment_data): cache.clear() create_default_order_statuses() populate_if_required() c = SmartClient() _populate_client_basket(c) # Create methods shipping_method = get_shipping_method() payment_method = get_payment_method() # Resolve paths addresses_path = reverse("wshop:checkout", kwargs={"phase": "addresses"}) methods_path = reverse("wshop:checkout", kwargs={"phase": "methods"}) shipping_path = reverse("wshop:checkout", kwargs={"phase": "shipping"}) payment_path = reverse("wshop:checkout", kwargs={"phase": "payment"}) confirm_path = reverse("wshop:checkout", kwargs={"phase": "confirm"}) # Phase: Addresses addresses_soup = c.soup(addresses_path) inputs = fill_address_inputs(addresses_soup, with_company=False) response = c.post(addresses_path, data=inputs) assert response.status_code == 302, "Address phase should redirect forth to methods" # Phase: Methods response = c.get(methods_path) assert response.status_code == 200 response = c.post(methods_path, data={ "shipping_method": shipping_method.pk, "payment_method": payment_method.pk }) assert response.status_code == 302, "Methods phase should redirect forth" if isinstance(shipping_method.carrier, CarrierWithCheckoutPhase): # Phase: Shipping response = c.get(shipping_path) assert response.status_code == 200 response = c.post(shipping_path, data=shipping_data) assert response.status_code == 302, "Payments phase should redirect forth" if isinstance(payment_method.payment_processor, PaymentWithCheckoutPhase): # Phase: payment response = c.get(payment_path) assert response.status_code == 200 response = c.post(payment_path, data=payment_data) assert response.status_code == 302, "Payments phase should redirect forth" # Phase: Confirm assert Order.objects.count() == 0 confirm_soup = c.soup(confirm_path) response = c.post(confirm_path, data=extract_form_fields(confirm_soup)) assert response.status_code == 302, "Confirm should redirect forth" order = Order.objects.first() if isinstance(shipping_method.carrier, CarrierWithCheckoutPhase): assert order.shipping_data.get("input_value") == "20540" if isinstance(payment_method.payment_processor, PaymentWithCheckoutPhase): assert order.payment_data.get("input_value") assert order.payment_status == PaymentStatus.NOT_PAID # Resolve order specific paths (payment and complete) process_payment_path = reverse("wshop:order_process_payment", kwargs={ "pk": order.pk, "key": order.key }) process_payment_return_path = reverse( "wshop:order_process_payment_return", kwargs={ "pk": order.pk, "key": order.key }) order_complete_path = reverse("wshop:order_complete", kwargs={ "pk": order.pk, "key": order.key }) # Check confirm redirection to payment page assert response.url.endswith(process_payment_path), ( "Confirm should have redirected to payment page") # Visit payment page response = c.get(process_payment_path) assert response.status_code == 302, "Payment page should redirect forth" assert response.url.endswith(process_payment_return_path) # Check payment return response = c.get(process_payment_return_path) assert response.status_code == 302, "Payment return should redirect forth" assert response.url.endswith(order_complete_path) # Check payment status has changed to DEFERRED order = Order.objects.get(pk=order.pk) # reload assert order.payment_status == PaymentStatus.DEFERRED