def test_create_product_with_shop_product_and_attributes(admin_user): shop = get_default_shop() client = _get_client(admin_user) supplier = get_default_supplier() cat = Category.objects.create(status=CategoryStatus.VISIBLE, visibility=CategoryVisibility.VISIBLE_TO_ALL, identifier="test_category", name="Test") product_type = get_default_product_type() assert Attribute.objects.count() > 0 assert Product.objects.count() == 0 attributes_data = [] expected_values = { "untranslated_string_value": "test value", "numeric_value": 12, "boolean_value": True, "timedelta_value": "200", # seconds "datetime_value": "2017-01-01 01:00:00", "translated_string_value": "translated string value" } for spec in ATTR_SPECS: attr = Attribute.objects.get(identifier=spec["identifier"]) attr_data = { "numeric_value": None, "datetime_value": None, "untranslated_string_value": "", "attribute": attr.pk, # "product": product.pk } if attr.is_stringy: if attr.is_translated: attr_data["translations"] = { "en": { "translated_string_value": expected_values["translated_string_value"] } } else: attr_data["untranslated_string_value"] = expected_values[ "untranslated_string_value"] elif attr.is_numeric: if attr.type == AttributeType.BOOLEAN: attr_data["numeric_value"] = int( expected_values["boolean_value"]) elif attr.type == AttributeType.TIMEDELTA: attr_data["numeric_value"] = int( expected_values["timedelta_value"]) else: attr_data["numeric_value"] = expected_values["numeric_value"] elif attr.is_temporal: attr_data["datetime_value"] = expected_values["datetime_value"] attributes_data.append(attr_data) data = _get_product_sample_data() data["shop_products"] = _get_sample_shop_product_data(shop, cat, supplier) data["attributes"] = attributes_data response = client.post("/api/wshop/product/", content_type="application/json", data=json.dumps(data)) assert response.status_code == status.HTTP_201_CREATED # check all for lang in ("en", "pt-br"): activate(lang) product = Product.objects.first() _check_product_basic_data(product, data, lang) assert Product.objects.count() == 1 assert ShopProduct.objects.count() == 1 product = Product.objects.first() shop_product = ShopProduct.objects.first() assert product.get_shop_instance(shop) == shop_product assert supplier in shop_product.suppliers.all() assert cat in shop_product.categories.all() assert shop_product.primary_category == cat # validate attribute values for spec in ATTR_SPECS: attribute = Attribute.objects.get(identifier=spec["identifier"]) attr = ProductAttribute.objects.get(product=product, attribute=attribute) if attribute.is_stringy: if attribute.is_translated: attr.set_current_language("en") assert attr.value == expected_values["translated_string_value"] else: assert attr.value == expected_values[ "untranslated_string_value"] elif attribute.is_numeric: if attribute.type == AttributeType.BOOLEAN: assert attr.value == expected_values["boolean_value"] elif attribute.type == AttributeType.TIMEDELTA: assert attr.value == datetime.timedelta( seconds=int(expected_values["timedelta_value"])) else: assert attr.value == expected_values["numeric_value"] elif attribute.is_temporal: dt_value = expected_values["datetime_value"] parsed_dt = dt.strptime(dt_value, "%Y-%m-%d %H:%M:%S") assert attr.value.year == parsed_dt.year assert attr.value.month == parsed_dt.month assert attr.value.day == parsed_dt.day
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 get_frontend_order_state(contact, payment_method, product_price, valid_lines=True): """ Get a dict structure mirroring what the frontend JavaScript would submit. :type contact: Contact|None """ translation.activate("en") shop = get_default_shop() tax = Tax.objects.create(code="test_code", rate=decimal.Decimal("0.20"), name="Default") tax_class = TaxClass.objects.create(identifier="test_tax_class", name="Default") rule = TaxRule.objects.create(tax=tax) rule.tax_classes.add(tax_class) rule.save() product = create_product(sku=printable_gibberish(), supplier=get_default_supplier(), shop=shop) product.tax_class = tax_class product.save() if valid_lines: lines = [ { "id": "x", "type": "product", "product": { "id": product.id }, "quantity": "1", "baseUnitPrice": product_price }, { "id": "z", "type": "text", "text": "This was an order!", "quantity": 0 }, ] else: unshopped_product = create_product(sku=printable_gibberish(), supplier=get_default_supplier()) not_visible_product = create_product(sku=printable_gibberish(), supplier=get_default_supplier(), shop=shop) not_visible_shop_product = not_visible_product.get_shop_instance(shop) not_visible_shop_product.visibility = ShopProductVisibility.NOT_VISIBLE not_visible_shop_product.save() lines = [ { "id": "x", "type": "product" }, # no product? { "id": "x", "type": "product", "product": { "id": unshopped_product.id } }, # not in this shop? { "id": "y", "type": "product", "product": { "id": -product.id } }, # invalid product? { "id": "z", "type": "other", "quantity": 1, "unitPrice": "q" }, # what's that price? { "id": "rr", "type": "product", "quantity": 1, "product": { "id": not_visible_product.id } } # not visible ] state = { "customer": { "id": contact.id if contact else None, "billingAddress": encode_address(contact.default_billing_address) if contact else {}, "shippingAddress": encode_address(contact.default_shipping_address) if contact else {}, }, "lines": lines, "methods": { "shippingMethod": { "id": get_default_shipping_method().id }, "paymentMethod": { "id": payment_method.id }, }, "shop": { "selected": { "id": shop.id, "name": shop.name, "currency": shop.currency, "priceIncludeTaxes": shop.prices_include_tax } } } return state
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_basket_partial_quantity_update_all_product_counts(): shop = get_default_shop() supplier = get_default_supplier() request = get_request_with_basket() basket = request.basket pieces = SalesUnit.objects.create(identifier="pieces", decimals=0, name="Pieces", symbol='pc.') kilograms = SalesUnit.objects.create(identifier="kilograms", decimals=3, name="Kilograms", symbol='kg') cup = create_product(sku="COFFEE-CUP-123", sales_unit=pieces, shop=shop, supplier=supplier) beans = create_product(sku="COFFEEBEANS3", sales_unit=kilograms, shop=shop, supplier=supplier) beans_shop_product = beans.get_shop_instance(shop) beans_shop_product.minimum_purchase_quantity = Decimal('0.1') beans_shop_product.save() pears = create_product(sku="PEARS-27", sales_unit=kilograms, shop=shop, supplier=supplier) add = basket_commands.handle_add update = basket_commands.handle_update # Empty basket assert basket.product_count == 0 assert basket.smart_product_count == 0 assert basket.product_line_count == 0 # 1 cup add(request, basket, product_id=cup.pk, quantity=1) assert basket.product_count == 1 assert basket.smart_product_count == 1 assert basket.product_line_count == 1 # Basket update operations work by prefixing line id with operation qty_update_cup = 'q_' + basket.get_lines()[0].line_id delete_cup = 'delete_' + basket.get_lines()[0].line_id # 3 cups update(request, basket, **{qty_update_cup: "3"}) assert basket.product_count == 3 assert basket.smart_product_count == 3 assert basket.product_line_count == 1 # 3 cups + 0.5 kg beans add(request, basket, product_id=beans.pk, quantity='0.5') assert basket.product_count == Decimal('3.5') assert basket.smart_product_count == 4 assert basket.product_line_count == 2 qty_update_beans = 'q_' + basket.get_lines()[1].line_id delete_beans1 = 'delete_' + basket.get_lines()[1].line_id # 1 cup + 2.520 kg beans update(request, basket, **{qty_update_cup: "1.0"}) update(request, basket, **{qty_update_beans: "2.520"}) assert basket.product_count == Decimal('3.520') assert basket.smart_product_count == 2 assert basket.product_line_count == 2 # 42 cups + 2.520 kg beans update(request, basket, **{qty_update_cup: "42"}) assert basket.product_count == Decimal('44.520') assert basket.smart_product_count == 43 assert basket.product_line_count == 2 # 42 cups + 2.520 kg beans + 3.5 kg pears add(request, basket, product_id=pears.pk, quantity='3.5') assert basket.product_count == Decimal('48.020') assert basket.smart_product_count == 44 assert basket.product_line_count == 3 # 42 cups + 3.5 kg pears update(request, basket, **{delete_beans1: "1"}) assert basket.product_count == Decimal('45.5') assert basket.smart_product_count == 43 assert basket.product_line_count == 2 # 3.5 kg pears update(request, basket, **{delete_cup: "1"}) assert basket.product_count == Decimal('3.5') assert basket.smart_product_count == 1 assert basket.product_line_count == 1
def test_taxes_report(rf): shop = get_default_shop() supplier = get_default_supplier() product1 = create_product("p1", shop=shop, supplier=supplier) product2 = create_product("p2", shop=shop, supplier=supplier) create_product("p3", shop=shop, supplier=supplier) tax_rate1 = Decimal("0.3") tax_rate2 = Decimal("0.45") tax_rate1_instance = get_test_tax(tax_rate1) tax_rate2_instance = get_test_tax(tax_rate2) # orders for person 1 person1 = create_random_person() order1 = create_order_with_product(product=product1, supplier=supplier, quantity=2, taxless_base_unit_price="5", tax_rate=tax_rate1, n_lines=1, shop=shop) order1.customer = person1 order1.save() order2 = create_order_with_product(product=product2, supplier=supplier, quantity=1, taxless_base_unit_price="10", tax_rate=tax_rate1, n_lines=1, shop=shop) order2.customer = person1 order2.save() # orders for person 2 person2 = create_random_person() order3 = create_order_with_product(product=product1, supplier=supplier, quantity=1, taxless_base_unit_price="2", tax_rate=tax_rate2, n_lines=1, shop=shop) order3.customer = person2 order3.save() order4 = create_order_with_product(product=product2, supplier=supplier, quantity=2, taxless_base_unit_price="8", tax_rate=tax_rate1, n_lines=1, shop=shop) order4.customer = person2 order4.save() # pay orders [o.create_payment(o.taxful_total_price) for o in Order.objects.all()] data = { "report": TaxesReport.get_name(), "shop": shop.pk, "date_range": DateRangeChoices.ALL_TIME, "writer": "json", "force_download": 1, } report = TaxesReport(**data) writer = get_writer_instance(data["writer"]) response = writer.get_response(report=report) if hasattr(response, "render"): response.render() json_data = json.loads(response.content.decode("utf-8")) assert force_text(TaxesReport.title) in json_data.get("heading") data = json_data.get("tables")[0].get("data") assert len(data) == 2 tax1_rate1_total = ( (order1.taxful_total_price_value - order1.taxless_total_price_value) + (order2.taxful_total_price_value - order2.taxless_total_price_value) + (order4.taxful_total_price_value - order4.taxless_total_price_value)) tax1_pretax_total = (order1.taxless_total_price_value + order2.taxless_total_price_value + order4.taxless_total_price_value) tax1_total = (order1.taxful_total_price_value + order2.taxful_total_price_value + order4.taxful_total_price_value) tax2_rate2_total = (order3.taxful_total_price_value - order3.taxless_total_price_value) # the report data order is the total charged ascending expected_result = [{ "tax": tax_rate2_instance.name, "tax_rate": tax_rate2, "order_count": 1, "total_pretax_amount": order3.taxless_total_price_value, "total_tax_amount": tax2_rate2_total, "total": order3.taxful_total_price_value, }, { "tax": tax_rate1_instance.name, "tax_rate": tax_rate1, "order_count": 3, "total_pretax_amount": tax1_pretax_total, "total_tax_amount": tax1_rate1_total, "total": tax1_total, }] for ix, tax in enumerate(data): assert tax["tax"] == expected_result[ix]["tax"] assert Decimal(tax["tax_rate"] ) == expected_result[ix]["tax_rate"] * Decimal(100.0) assert tax["order_count"] == str(expected_result[ix]["order_count"]) assert tax["total_tax_amount"] == str( expected_result[ix]["total_tax_amount"]) assert tax["total_pretax_amount"] == str( expected_result[ix]["total_pretax_amount"]) assert tax["total"] == str(expected_result[ix]["total"])
def test_customer_sales_report(rf, order_by): shop = get_default_shop() supplier = get_default_supplier() product1 = create_product("p1", shop=shop, supplier=supplier) product2 = create_product("p2", shop=shop, supplier=supplier) product3 = create_product("p3", shop=shop, supplier=supplier) tax_rate = Decimal("0.3") # orders for person 1 person1 = create_random_person() order1 = create_order_with_product(product=product1, supplier=supplier, quantity=2, taxless_base_unit_price="5", tax_rate=tax_rate, n_lines=1, shop=shop) order1.customer = person1 order1.save() order2 = create_order_with_product(product=product2, supplier=supplier, quantity=1, taxless_base_unit_price="10", n_lines=1, shop=shop) order2.customer = person1 order2.save() person1_taxful_total_sales = (order1.taxful_total_price + order2.taxful_total_price) person1_taxless_total_sales = (order1.taxless_total_price + order2.taxless_total_price) person1_avg_sales = (person1_taxful_total_sales / Decimal(2.0)) # orders for person 2 person2 = create_random_person() order3 = create_order_with_product(product=product1, supplier=supplier, quantity=2, taxless_base_unit_price="5", tax_rate=tax_rate, n_lines=1, shop=shop) order3.customer = person2 order3.save() order4 = create_order_with_product(product=product2, supplier=supplier, quantity=2, taxless_base_unit_price="50", n_lines=1, shop=shop) order4.customer = person2 order4.save() order5 = create_order_with_product(product=product3, supplier=supplier, quantity=2, taxless_base_unit_price="20", tax_rate=tax_rate, n_lines=1, shop=shop) order5.customer = person2 order5.save() person2_taxful_total_sales = (order3.taxful_total_price + order4.taxful_total_price + order5.taxful_total_price) person2_taxless_total_sales = (order3.taxless_total_price + order4.taxless_total_price + order5.taxless_total_price) person2_avg_sales = (person2_taxful_total_sales / Decimal(3.0)).quantize( Decimal('0.01')) # pay orders [o.create_payment(o.taxful_total_price) for o in Order.objects.all()] data = { "report": CustomerSalesReport.get_name(), "shop": shop.pk, "date_range": DateRangeChoices.ALL_TIME, "writer": "json", "force_download": 1, "order_by": order_by } report = CustomerSalesReport(**data) writer = get_writer_instance(data["writer"]) response = writer.get_response(report=report) if hasattr(response, "render"): response.render() json_data = json.loads(response.content.decode("utf-8")) assert force_text(CustomerSalesReport.title) in json_data.get("heading") data = json_data.get("tables")[0].get("data") assert len(data) == 2 if order_by == "order_count": person1_data = data[1] person2_data = data[0] elif order_by == "average_sales": if person1_avg_sales > person2_avg_sales: person1_data = data[0] person2_data = data[1] else: person1_data = data[1] person2_data = data[0] elif order_by == "taxless_total": if person1_taxless_total_sales > person2_taxless_total_sales: person1_data = data[0] person2_data = data[1] else: person1_data = data[1] person2_data = data[0] elif order_by == "taxful_total": if person1_taxful_total_sales > person2_taxful_total_sales: person1_data = data[0] person2_data = data[1] else: person1_data = data[1] person2_data = data[0] assert person1_data["customer"] == person1.name assert person1_data["order_count"] == "2" assert person1_data["average_sales"] == str(person1_avg_sales.value) assert person1_data["taxless_total"] == str( person1_taxless_total_sales.value.quantize(Decimal("0.01"))) assert person1_data["taxful_total"] == str( person1_taxful_total_sales.value.quantize(Decimal("0.01"))) assert person2_data["customer"] == person2.name assert person2_data["order_count"] == "3" assert person2_data["average_sales"] == str(person2_avg_sales.value) assert person2_data["taxless_total"] == str( person2_taxless_total_sales.value.quantize(Decimal("0.01"))) assert person2_data["taxful_total"] == str( person2_taxful_total_sales.value.quantize(Decimal("0.01")))
def test_product_total_sales_report(rf, admin_user, order_by): with override_provides("reports", [ "wshop.default_reports.reports.product_total_sales:ProductSalesReport" ]): shop = get_default_shop() supplier = get_default_supplier() product1 = create_product("product1", supplier=supplier, shop=shop) product2 = create_product("product2", supplier=supplier, shop=shop) p1_qtd, p1_price, p1_tr, p1_lines = Decimal(3), Decimal(5), Decimal( 0), 5 p2_qtd, p2_price, p2_tr, p2_lines = Decimal(4), Decimal(5), Decimal( 0.95), 3 order = create_order_with_product(product=product1, supplier=supplier, quantity=p1_qtd, taxless_base_unit_price=p1_price, tax_rate=p1_tr, n_lines=p1_lines, shop=shop) order.create_payment(order.taxful_total_price.amount) order2 = create_order_with_product(product=product2, supplier=supplier, quantity=p2_qtd, taxless_base_unit_price=p2_price, tax_rate=p2_tr, n_lines=p2_lines, shop=shop) order2.create_payment(order2.taxful_total_price.amount) data = { "report": ProductSalesReport.get_name(), "shop": shop.pk, "date_range": DateRangeChoices.ALL_TIME.value, "writer": "json", "force_download": 1, "order_by": order_by } view = ReportView.as_view() request = apply_request_middleware(rf.post("/", data=data), user=admin_user) response = view(request) if hasattr(response, "render"): response.render() assert response.status_code == 200 json_data = json.loads(response.content.decode("utf-8")) assert force_text(ProductSalesReport.title) in json_data.get("heading") data = json_data["tables"][0]["data"] assert len(data) == 2 p1_total_qtd = p1_qtd * p1_lines p1_taxless_total = p1_total_qtd * p1_price p1_taxful_total = p1_taxless_total * (1 + p1_tr) p2_total_qtd = p2_qtd * p2_lines p2_taxless_total = p2_total_qtd * p2_price p2_taxful_total = p2_taxless_total * (1 + p2_tr) if order_by == "quantity": p1 = data[0] p2 = data[1] elif order_by == "taxless_total": p1 = data[0] p2 = data[1] else: # order_by == "taxful_total": p1 = data[1] p2 = data[0] precision = Decimal('0.1')**2 assert p1["product"] == product1.name assert Decimal(p1["quantity"]) == p1_total_qtd assert Decimal( p1["taxless_total"]) == p1_taxless_total.quantize(precision) assert Decimal( p1["taxful_total"]) == p1_taxful_total.quantize(precision) assert p2["product"] == product2.name assert Decimal(p2["quantity"]) == p2_total_qtd assert Decimal( p2["taxless_total"]) == p2_taxless_total.quantize(precision) assert Decimal( p2["taxful_total"]) == p2_taxful_total.quantize(precision)