def get_data(self, **kwargs): data = [] coupons_usages = self.get_objects() for coupon_usage in coupons_usages: total_discount = self.shop.create_price(0) for discount in coupon_usage.order.lines.discounts(): total_discount += discount.taxful_price data.append({ "date": format_date(coupon_usage.order.order_date, locale=get_current_babel_locale()), "coupon": coupon_usage.coupon.code, "order": coupon_usage.order, "taxful_total": coupon_usage.order.taxful_total_price.as_rounded().value, "taxful_subtotal": (coupon_usage.order.taxful_total_price - total_discount).as_rounded().value, "total_discount": total_discount.as_rounded().value }) return self.get_return_data(data)
def __init__(self, title, data_type=ChartDataType.NUMBER, locale=None, currency=None, options=None): """ :param str title: the title of the chart :param ChartDataType data_type: the data type of values The chart will format the output labels according to this parameter :param str locale: the locale to render values If not set, the locale will be fetched from Babel :param str currency: the ISO-4217 code for the currency This is necessary when the data_type is CURRENCY :param dict options: a dicionaty with options for Chartjs """ self.title = title self.datasets = [] self.options = options self.data_type = data_type self.currency = currency if locale: self.locale = locale else: self.locale = get_current_babel_locale() if data_type == ChartDataType.CURRENCY and not currency: raise AttributeError( "You should also set currency for this data type")
def get_data(self): orders = self.get_objects().order_by("-order_date") data = [] for order_date, orders_group in itertools.groupby(orders, key=self.extract_date): taxless_total = TaxlessPrice(0, currency=self.shop.currency) taxful_total = TaxfulPrice(0, currency=self.shop.currency) paid_total = TaxfulPrice(0, currency=self.shop.currency) product_count = 0 order_count = 0 for order in orders_group: taxless_total += order.taxless_total_price taxful_total += order.taxful_total_price product_count += sum(order.get_product_ids_and_quantities().values()) order_count += 1 if order.payment_date: paid_total += order.taxful_total_price data.append({ "date": format_date(order_date, format="short", locale=get_current_babel_locale()), "order_count": order_count, "product_count": int(product_count), "taxless_total": taxless_total, "taxful_total": taxful_total, }) return self.get_return_data(data)
def _render_report(self, report): if not report.rendered: report_data = report.get_data() self.write_heading("{title} {start} - {end}".format( title=report.title, start=format_datetime(report_data["start"], format="short", locale=get_current_babel_locale()), end=format_datetime(report_data["end"], format="short", locale=get_current_babel_locale()))) report.ensure_texts() self.write_data_table(report, report_data["data"], has_totals=report_data["has_totals"]) return self.get_rendered_output()
def test_parse_date_range_presets(): locale = i18n.get_current_babel_locale() def local_now(): return datetime.datetime(2017, 12, 4, 17, 1, tzinfo=pytz.UTC) with mock.patch("wshop.utils.dates.local_now", side_effect=local_now): start, end = parse_date_range_preset(DateRangeChoices.TODAY) assert start == local_now().replace(hour=0, minute=0, second=0) assert end == local_now() start, end = parse_date_range_preset(DateRangeChoices.RUNNING_WEEK) assert start == local_now().replace(month=11, day=27, hour=0, minute=0, second=0) assert end == local_now() start, end = parse_date_range_preset(DateRangeChoices.RUNNING_MONTH) assert start == local_now().replace(month=11, day=4, hour=0, minute=0, second=0) assert end == local_now() assert local_now().weekday() == locale.first_week_day start, end = parse_date_range_preset(DateRangeChoices.THIS_WEEK) assert start == local_now().replace(hour=0, minute=0, second=0) assert end == local_now() start, end = parse_date_range_preset(DateRangeChoices.THIS_MONTH) assert start == local_now().replace(month=12, day=1, hour=0, minute=0, second=0) assert end == local_now() start, end = parse_date_range_preset(DateRangeChoices.THIS_YEAR) assert start == local_now().replace(month=1, day=1, hour=0, minute=0, second=0) assert end == local_now() start, end = parse_date_range_preset(DateRangeChoices.ALL_TIME) assert start == local_now().replace(year=2000, hour=0, minute=0, second=0) assert end == local_now()
def get_first_day_of_the_current_week(today_start): locale = i18n.get_current_babel_locale() first_day_of_the_week = today_start if today_start.weekday() == locale.first_week_day: return first_day_of_the_week def get_prospect(i): return (today_start - datetime.timedelta(days=i)) return iterables.first([ get_prospect(i) for i in range(1, 7) if get_prospect(i).weekday() == locale.first_week_day ])
def get_dashboard_blocks(self, request): if not self.check_demo_optin(request): return locale = get_current_babel_locale() n = now() weekday = format_date(n, "EEEE", locale=locale) today = format_date(n, locale=locale) yield DashboardValueBlock(id="test-x", color="blue", title="Happy %s!" % weekday, value=today, icon="fa fa-calendar") yield DashboardNumberBlock(id="test-x", color="red", title="Visitors Yesterday", value=random.randint(2200, 10000), icon="fa fa-globe") yield DashboardNumberBlock(id="test-x", color="gray", title="Registered Users", value=1240, icon="fa fa-user") yield DashboardNumberBlock(id="test-x", color="orange", title="Orders", value=32, icon="fa fa-inbox") yield DashboardMoneyBlock(id="test-x", color="green", title="Open Orders Value", value=32000, currency="USD", icon="fa fa-line-chart") yield DashboardNumberBlock(id="test-x", color="yellow", title="Current Visitors", value=6, icon="fa fa-users") yield DashboardMoneyBlock(id="test-x", color="none", title="Sales this week", value=430.30, currency="USD", icon="fa fa-dollar") yield DashboardValueBlock(id="test-1", value="\u03C0", title="The most delicious number", color="purple", icon="fa fa-smile-o")
def datetime(value, kind="datetime", format="medium", tz=True): """ Format a datetime for human consumption. The currently active locale's formatting rules are used. The output of this function is probably not machine-parseable. :param value: datetime object to format :type value: datetime.datetime :param kind: Format as 'datetime', 'date' or 'time' :type kind: str :param format: Format specifier or one of 'full', 'long', 'medium' or 'short' :type format: str :param tz: Convert to current or given timezone. Accepted values are: True (default) convert to currently active timezone (as reported by :func:`django.utils.timezone.get_current_timezone`) False (or other false value like empty string) do no convert to any timezone (use UTC) Other values (as str) convert to given timezone (e.g. ``"US/Hawaii"``) :type tz: bool|str """ locale = get_current_babel_locale() if type(value) is date: # Not using isinstance, since `datetime`s are `date` too. # We can't do any TZ manipulation for dates, so just use `format_date` always return format_date(value, format=format, locale=locale) if tz: value = localtime(value, (None if tz is True else tz)) if kind == "datetime": return format_datetime(value, format=format, locale=locale) elif kind == "date": return format_date(value, format=format, locale=locale) elif kind == "time": return format_time(value, format=format, locale=locale) else: raise ValueError("Unknown `datetime` kind: %r" % kind)
def encode_line(line): if line.base_unit_price.amount.value != 0: discount_percent = ( line.discount_amount.amount.value / (line.base_unit_price.amount.value * line.quantity) ) else: discount_percent = 0 return { "sku": line.sku, "text": line.text, "quantity": format_decimal(line.quantity, locale=get_current_babel_locale()), "unitPrice": format_money(line.base_unit_price.amount), "discountedUnitPrice": format_money(line.discounted_unit_price.amount), "discountAmount": format_money(line.discount_amount.amount), "discountPercent": format_percent(discount_percent, 2), "taxlessTotal": format_money(line.taxless_price.amount), "taxPercentage": format_percent(line.tax_rate, 2), "taxfulTotal": format_money(line.taxful_price.amount) }
def address_as_string_list(self, address, locale=None): assert issubclass(type(address), Address) locale = locale or get_current_babel_locale() country = address.country.code.upper() base_lines = [ address.company_name, address.full_name, address.name_ext, address.street, address.street2, address.street3, "%s %s %s" % (address.postal_code, address.city, address.region_code or address.region), locale.territories.get(country, country) if not address.is_home else None ] stripped_lines = [force_text(line).strip() for line in base_lines if line] return [s for s in stripped_lines if (s and len(s) > 1)]
def handle_customer_details(self, request): customer_id = request.GET["id"] customer = Contact.objects.get(pk=customer_id) companies = [] if isinstance(customer, PersonContact): companies = sorted(customer.company_memberships.all(), key=(lambda x: force_text(x))) recent_orders = customer.customer_orders.valid().order_by('-id')[:10] order_summary = [] for dt in customer.customer_orders.valid().datetimes('order_date', 'year'): summary = customer.customer_orders.filter(order_date__year=dt.year).aggregate( total=Sum('taxful_total_price_value') ) order_summary.append({ 'year': dt.year, 'total': format_currency( summary['total'], currency=recent_orders[0].currency, locale=get_current_babel_locale() ) }) return { "customer_info": { "name": customer.full_name, "phone_no": customer.phone, "email": customer.email, "tax_number": getattr(customer, "tax_number", ""), "companies": [force_text(company) for company in companies] if len(companies) else None, "groups": [force_text(group) for group in customer.groups.all()], "merchant_notes": customer.merchant_notes }, "order_summary": order_summary, "recent_orders": [ { "order_date": get_locally_formatted_datetime(order.order_date), "total": format_money(order.taxful_total_price), "status": order.get_status_display(), "payment_status": force_text(order.payment_status.label), "shipment_status": force_text(order.shipping_status.label) } for order in recent_orders ] }
def get_data(self): orders = self.get_objects().order_by("-order_date") data = [] # TODO: maybe make raw sql query in future for order_date, orders_group in itertools.groupby(orders, key=self.extract_date): taxless_total = TaxlessPrice(0, currency=self.shop.currency) taxful_total = TaxfulPrice(0, currency=self.shop.currency) product_count = 0 order_count = 0 for order in orders_group: taxless_total += order.taxless_total_price taxful_total += order.taxful_total_price product_count += sum(order.get_product_ids_and_quantities().values()) order_count += 1 data.append({ "date": format_date(order_date, locale=get_current_babel_locale()), "order_count": order_count, "product_count": int(product_count), "taxless_total": taxless_total.as_rounded().value, "taxful_total": taxful_total.as_rounded().value, }) return self.get_return_data(data)
def test_sales_report_timezone(server_timezone): with override_settings(TIME_ZONE=server_timezone): """ TIME TABLE | identifier | ISO 8859-1 | UTC | America/Los_Angeles | America/Sao_Paulo | | first_date | 2017-10-01T23:50:00+03:00 | 2017-10-01 20:50:00 | 2017-10-01 13:50:00 | 2017-10-01 17:50:00 | | second_date | 2017-10-02T17:13:00+10:00 | 2017-10-02 07:13:00 | 2017-10-02 00:13:00 | 2017-10-02 04:13:00 | | third_date | 2017-10-02T22:04:44-01:00 | 2017-10-02 23:04:44 | 2017-10-02 16:04:44 | 2017-10-02 20:04:44 | | forth_date | 2017-10-02T23:04:44-05:00 | 2017-10-03 04:04:44 | 2017-10-02 21:04:44 | 2017-10-03 01:04:44 | """ first_date = parse_datetime("2017-10-01T23:50:00+03:00") second_date = parse_datetime("2017-10-02T17:13:00+10:00") third_date = parse_datetime("2017-10-02T22:04:44-01:00") forth_date = parse_datetime("2017-10-02T23:04:44-05:00") inited_data = create_orders_for_dates( [first_date, second_date, third_date, forth_date], as_paid=True) assert Order.objects.count() == 4 first_date_local = first_date.astimezone(timezone(server_timezone)) second_date_local = second_date.astimezone(timezone(server_timezone)) third_date_local = third_date.astimezone(timezone(server_timezone)) forth_date_local = forth_date.astimezone(timezone(server_timezone)) data = { "report": SalesReport.get_name(), "shop": inited_data["shop"].pk, "start_date": first_date_local, "end_date": second_date_local, } report = SalesReport(**data) report_data = report.get_data()["data"] assert len(report_data) == 2 # the orders should be rendered as localtime assert report_data[0]["date"] == format_date( second_date_local, locale=get_current_babel_locale()) assert report_data[1]["date"] == format_date( first_date_local, locale=get_current_babel_locale()) # includes the 3rd order data.update({ "start_date": first_date_local, "end_date": third_date_local }) report = SalesReport(**data) report_data = report.get_data()["data"] assert len(report_data) == 2 assert report_data[0]["date"] == format_date( second_date_local, locale=get_current_babel_locale()) assert report_data[1]["date"] == format_date( first_date_local, locale=get_current_babel_locale()) # includes the 4th order - here the result is different for Los_Angeles and Sao_Paulo data.update({ "start_date": first_date_local, "end_date": forth_date_local }) report = SalesReport(**data) report_data = report.get_data()["data"] if server_timezone == "America/Los_Angeles": assert len(report_data) == 2 assert report_data[0]["date"] == format_date( second_date_local, locale=get_current_babel_locale()) assert report_data[1]["date"] == format_date( first_date_local, locale=get_current_babel_locale()) else: assert len(report_data) == 3 assert report_data[0]["date"] == format_date( forth_date_local, locale=get_current_babel_locale()) assert report_data[1]["date"] == format_date( second_date_local, locale=get_current_babel_locale()) assert report_data[2]["date"] == format_date( first_date_local, locale=get_current_babel_locale()) # Using strings as start or end date should raise TypeError. # Only date or datetime objects should be accepted. data.update({ "start_date": first_date_local.isoformat(), "end_date": forth_date_local.isoformat() }) with pytest.raises(TypeError): report = SalesReport(**data) report_data = report.get_data()["data"] # Using different date types in start and end date should raise TypeError. data.update({ "start_date": first_date_local, "end_date": forth_date_local.date() }) with pytest.raises(TypeError): report = SalesReport(**data) report_data = report.get_data()["data"]
def get_symbol(self, currency): return babel.numbers.get_currency_symbol(currency.code, get_current_babel_locale())
def get_chart(self): if self.cached_chart is not None: return self.cached_chart chart_options = { "scales": { "yAxes": [{ "ticks": { "beginAtZero": True } }] } } today = date.today() chart_start_date = today - timedelta(days=365) orders = get_orders_for_shop(self.request) sum_sales_data = group_by_period( orders.valid().since((today - chart_start_date).days), "order_date", "month", sum=Sum("taxful_total_price_value") ) for (month, year) in month_iter(chart_start_date, today): sales_date = date(year, month, 1) if sales_date not in sum_sales_data: sum_sales_data[sales_date] = {"sum": Decimal(0)} # sort and recreated the ordered dict since we've put new items into sum_sales_data = OrderedDict(sorted(six.iteritems(sum_sales_data), key=lambda x: x[0])) locale = get_current_babel_locale() labels = [ format_date(k, format=get_year_and_month_format(locale), locale=locale) for k in sum_sales_data ] mixed_chart = MixedChart(title=_("Sales per Month (past 12 months)"), labels=labels, data_type=ChartDataType.CURRENCY, options=chart_options, currency=self.currency, locale=locale) cumulative_sales = [] average_sales = [] # only calculate cumulative and average if there are at least 3 months if len(sum_sales_data) >= 3: count = 0 total = Decimal() for month_sale in sum_sales_data.values(): total = total + month_sale["sum"] cumulative_sales.append(total) average_sales.append(total / (count+1)) count = count + 1 # this will be on top of all bars if average_sales: mixed_chart.add_data(_("Average Sales"), [v for v in average_sales], ChartType.LINE) # this will be under the cummulative bars mixed_chart.add_data(_("Sales"), [v["sum"] for v in sum_sales_data.values()], ChartType.BAR) # this will be under all others charts if cumulative_sales: mixed_chart.add_data(_("Cumulative Total Sales"), [v for v in cumulative_sales], ChartType.BAR) self.cached_chart = mixed_chart return mixed_chart
def __init__(self, id, value, title, **kwargs): value = parse_decimal_string(value) if int(value) == value: value = int(value) value = format_number(value, locale=get_current_babel_locale()) super(DashboardNumberBlock, self).__init__(id, value, title, **kwargs)
def number(value): return format_decimal(value, locale=get_current_babel_locale())
def _formatted_datetime(self, dt): return format_datetime(localtime(dt), locale=get_current_babel_locale())
def get_currency_choices(): locale = get_current_babel_locale() currencies = Currency.objects.all().order_by("code") return [(currency.code, locale.currencies.get(currency.code, currency)) for currency in currencies]
def __init__(self, id, value, title, currency, **kwargs): self.currency = currency value = parse_decimal_string(value) value = format_currency(value, currency=self.currency, locale=get_current_babel_locale()) super(DashboardMoneyBlock, self).__init__(id, value, title, **kwargs)
def test_shop_api(admin_user, currency, currency_decimals): activate("en") default_shop = get_default_shop() client = APIClient() client.force_authenticate(user=admin_user) Currency.objects.get_or_create(code=currency, decimal_places=currency_decimals) shop_data = { "domain": "wshop.com", "status": ShopStatus.ENABLED.value, "owner": create_random_person().pk, "currency": currency, "prices_include_tax": True, "maintenance_mode": False, "translations": { "en": { "name": "Store 1", "public_name": "Public Store 1" } } } response = client.post("/api/wshop/shop/", content_type="application/json", data=json.dumps(shop_data)) assert response.status_code == status.HTTP_201_CREATED shop = Shop.objects.exclude(pk=default_shop.pk).first() assert shop.domain == shop_data["domain"] assert shop.status.value == shop_data["status"] assert shop.owner.pk == shop_data["owner"] assert shop.currency == shop_data["currency"] assert shop.maintenance_mode == shop_data["maintenance_mode"] assert shop.prices_include_tax == shop_data["prices_include_tax"] assert shop.name == shop_data["translations"]["en"]["name"] assert shop.public_name == shop_data["translations"]["en"]["public_name"] shop_data["domain"] = "cloud.wshop.com" response = client.put("/api/wshop/shop/%d/" % shop.id, content_type="application/json", data=json.dumps(shop_data)) assert response.status_code == status.HTTP_200_OK shop = Shop.objects.exclude(pk=default_shop.pk).first() assert shop.domain == shop_data["domain"] response = client.get("/api/wshop/shop/%d/" % shop.id) assert response.status_code == status.HTTP_200_OK data = json.loads(response.content.decode("utf-8")) assert shop.domain == data["domain"] assert shop.status.value == data["status"] assert shop.owner.pk == data["owner"] assert shop.currency == data["currency"]["code"] assert data["currency"]["symbol"] == babel.numbers.get_currency_symbol( shop.currency, get_current_babel_locale()) assert data["currency"]["decimal_places"] == currency_decimals assert shop.maintenance_mode == data["maintenance_mode"] assert shop.prices_include_tax == data["prices_include_tax"] assert shop.name == data["translations"]["en"]["name"] assert shop.public_name == data["translations"]["en"]["public_name"] response = client.get("/api/wshop/shop/") assert response.status_code == status.HTTP_200_OK data = json.loads(response.content.decode("utf-8")) assert shop.domain == data[1]["domain"] response = client.delete("/api/wshop/shop/%d/" % shop.id) assert response.status_code == status.HTTP_204_NO_CONTENT assert Shop.objects.count() == 1 # shouldn't be possible to delete a shop with a related order product = create_product("product1", shop=default_shop, supplier=get_default_supplier()) create_random_order(create_random_person(), [product], completion_probability=1, shop=default_shop) response = client.delete("/api/wshop/shop/%d/" % default_shop.id) assert response.status_code == status.HTTP_400_BAD_REQUEST assert "This object can not be deleted because it is referenced by" in response.content.decode( "utf-8") assert Shop.objects.count() == 1
def test_coupons_usage_report(rf): shop = get_default_shop() tax_class = get_default_tax_class() creator = OrderCreator() coupon1 = Coupon.objects.create(code="coupon1", active=True) coupon2 = Coupon.objects.create(code="coupon2", active=True) campaign1 = get_default_campaign(coupon1, "10") campaign2 = get_default_campaign(coupon2, "25") source1 = seed_source(coupon1) source2 = seed_source(coupon1) source3 = seed_source(coupon1) source4 = seed_source(coupon2) creator.create_order(source1) creator.create_order(source2) creator.create_order(source3) creator.create_order(source4) # pay orders [o.create_payment(o.taxful_total_price) for o in Order.objects.all()] data = { "report": CouponsUsageReport.get_name(), "shop": shop.pk, "date_range": DateRangeChoices.ALL_TIME, "writer": "json", "force_download": 1, } report = CouponsUsageReport(**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(CouponsUsageReport.title) in json_data.get("heading") data = json_data.get("tables")[0].get("data") assert len(data) == Order.objects.count() expected_data = [] orders = Order.objects.all().order_by("order_date") for order in orders: discount = order.shop.create_price(0) for dt in order.lines.discounts(): discount += dt.taxful_price expected_data.append({ "date": format_date(order.order_date, locale=get_current_babel_locale()), "coupon": order.codes[0], "order": str(order), "taxful_total": str(order.taxful_total_price.as_rounded().value), "taxful_subtotal": str((order.taxful_total_price - discount).as_rounded().value), "total_discount": str(discount.as_rounded().value) }) assert len(expected_data) == len(data) for ix, d in enumerate(data): for k, v in d.items(): assert expected_data[ix][k] == v
def get_currency_display(self, instance): locale = get_current_babel_locale() return locale.currencies.get(instance.code, instance.code)