Skip to content

Commit 4f2b949

Browse files
committed
Merge branch 'release/v1.0.2'
2 parents 7b315de + ee46c86 commit 4f2b949

37 files changed

+1454
-227
lines changed

CHANGELOG.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
All notable changes to this project will be documented in this file.
44

5+
## [1.0.2] - 2023-08-31
6+
- Add a demo project for exploration and also containing all documentation code for proofing.
7+
- Revise and Enhancing Tutorial , Group by and Time series documentation.
8+
- Fix issue with error on dev console on report page due to resources duplication
9+
- Fix issue with Custom querysets not being correctly connected in the view
10+
- Fix issue with time series custom dates
11+
- Fix issue with Crosstab on traversing fields
12+
513

614
## [1.0.1] - 2023-07-03
715

@@ -11,7 +19,7 @@ All notable changes to this project will be documented in this file.
1119
## [1.0.0] - 2023-07-03
1220

1321
- Added crosstab_ids_custom_filters to allow custom filters on crosstab ids
14-
- Added ``group_by_querysets`` to allow custom querysets as group
22+
- Added ``group_by_custom_querysets`` to allow custom querysets as group
1523
- Added ability to have crosstab report in a time series report
1624
- Enhanced Docs content and structure.
1725

README.rst

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -61,26 +61,30 @@ You can simply use a code like this
6161
6262
6363
class TotalProductSales(ReportView):
64-
65-
report_model = MySalesItems
66-
date_field = "date_placed"
64+
report_model = SalesTransaction
65+
date_field = "date"
6766
group_by = "product"
6867
columns = [
69-
"title",
70-
SlickReportField.create(Sum, "quantity"),
71-
SlickReportField.create(Sum, "value", name="sum__value"),
68+
"name",
69+
SlickReportField.create(Sum, "quantity", verbose_name="Total quantity sold", is_summable=False),
70+
SlickReportField.create(Sum, "value", name="sum__value", verbose_name="Total Value sold $"),
7271
]
7372
7473
chart_settings = [
7574
Chart(
7675
"Total sold $",
7776
Chart.BAR,
78-
data_source="value__sum",
79-
title_source="title",
77+
data_source=["sum__value"],
78+
title_source=["name"],
79+
),
80+
Chart(
81+
"Total sold $ [PIE]",
82+
Chart.PIE,
83+
data_source=["sum__value"],
84+
title_source=["name"],
8085
),
8186
]
8287
83-
8488
To get something this
8589

8690
.. image:: https://i.ibb.co/SvxTM23/Selection-294.png
@@ -190,16 +194,38 @@ You can interact with the `ReportGenerator` using same syntax as used with the `
190194
my_report.get_report_data() # -> [{'title':'Product 1', '__total__: 56}, {'title':'Product 2', '__total__: 43}, ]
191195
192196
193-
This is just a scratch, for more please visit the documentation
197+
This is just a scratch of what you can do and customize.
198+
199+
Demo site
200+
---------
201+
202+
Available on `Django Slick Reporting <https://django-slick-reporting.com/>`_
203+
204+
205+
You can also use locally
206+
207+
.. code-block:: console
208+
209+
# clone the repo
210+
# create a virtual environment, activate it, then
211+
cd django-slick-reporting/demo_proj
212+
pip install requirements.txt
213+
python manage.py migrate
214+
python manage.py create_entries
215+
python manage.py runserver
216+
217+
the ``create_entries`` command will generate data for the demo app
218+
194219

195220
Batteries Included
196221
------------------
197222

198223
Slick Reporting comes with
199224

200-
* A Bootstrap Filter Form
201-
* Charting support `Chart.js <https://www.chartjs.org/>`_
202-
* Powerful tables `datatables.net <https://datatables.net/>`_
225+
* An auto-generated, bootstrap-ready Filter Form
226+
* Carts.js Charting support `Chart.js <https://www.chartjs.org/>`_
227+
* Highcharts.js Charting support `Highcharts.js <https://www.highcharts.com//>`_
228+
* Datatables `datatables.net <https://datatables.net/>`_
203229

204230
A Preview:
205231

@@ -208,11 +234,6 @@ A Preview:
208234
:alt: Shipped in View Page
209235

210236

211-
Demo site
212-
---------
213-
214-
Available on `Django Slick Reporting <https://django-slick-reporting.com/>`_
215-
216237
Documentation
217238
-------------
218239

@@ -221,11 +242,8 @@ Available on `Read The Docs <https://django-slick-reporting.readthedocs.io/en/la
221242
Road Ahead
222243
----------
223244

224-
This project is young and can use your support.
225-
226-
Some of the ideas / features that ought be added
227-
228-
* Support Other backends like SQL Alchemy & Pandas
245+
* Continue on enriching the demo project
246+
* Add the dashboard capabilities
229247

230248

231249
Running tests
@@ -264,6 +282,6 @@ If you like this package, chances are you may like those packages too!
264282

265283
`Django Tabular Permissions <https://github.com/RamezIssac/django-tabular-permissions>`_ Display Django permissions in a HTML table that is translatable and easy customized.
266284

267-
`Django Ra ERP Framework <https://github.com/ra-systems/RA>`_ A framework to build business solutions with ease.
285+
`Django ERP Framework <https://github.com/ra-systems/RA>`_ A framework to build business solutions with ease.
268286

269287
If you find this project useful or promising , You can support us by a github ⭐

demo_proj/demo_app/__init__.py

Whitespace-only changes.

demo_proj/demo_app/admin.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from django.contrib import admin
2+
3+
# Register your models here.

demo_proj/demo_app/apps.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from django.apps import AppConfig
2+
3+
4+
class DemoAppConfig(AppConfig):
5+
default_auto_field = "django.db.models.BigAutoField"
6+
name = "demo_app"

demo_proj/demo_app/forms.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
from django import forms
2+
from django.db.models import Q
3+
from slick_reporting.forms import BaseReportForm
4+
5+
6+
class TotalSalesFilterForm(BaseReportForm, forms.Form):
7+
PRODUCT_SIZE_CHOICES = (
8+
("all", "All"),
9+
("big-only", "Big Only"),
10+
("small-only", "Small Only"),
11+
("medium-only", "Medium Only"),
12+
("all-except-extra-big", "All except extra Big"),
13+
)
14+
start_date = forms.DateField(
15+
required=False,
16+
label="Start Date",
17+
widget=forms.DateInput(attrs={"type": "date"}),
18+
)
19+
end_date = forms.DateField(
20+
required=False, label="End Date", widget=forms.DateInput(attrs={"type": "date"})
21+
)
22+
product_size = forms.ChoiceField(
23+
choices=PRODUCT_SIZE_CHOICES, required=False, label="Product Size", initial="all"
24+
)
25+
26+
def get_filters(self):
27+
# return the filters to be used in the report
28+
# Note: the use of Q filters and kwargs filters
29+
kw_filters = {}
30+
q_filters = []
31+
if self.cleaned_data["product_size"] == "big-only":
32+
kw_filters["product__size__in"] = ["extra_big", "big"]
33+
elif self.cleaned_data["product_size"] == "small-only":
34+
kw_filters["product__size__in"] = ["extra_small", "small"]
35+
elif self.cleaned_data["product_size"] == "medium-only":
36+
kw_filters["product__size__in"] = ["medium"]
37+
elif self.cleaned_data["product_size"] == "all-except-extra-big":
38+
q_filters.append(~Q(product__size__in=["extra_big", "big"]))
39+
return q_filters, kw_filters
40+
41+
def get_start_date(self):
42+
return self.cleaned_data["start_date"]
43+
44+
def get_end_date(self):
45+
return self.cleaned_data["end_date"]
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import datetime
2+
import random
3+
from datetime import timedelta
4+
5+
from django.contrib.auth import get_user_model
6+
from django.core.management.base import BaseCommand
7+
8+
# from expense.models import Expense, ExpenseTransaction
9+
from ...models import Client, Product, SalesTransaction, ProductCategory
10+
11+
User = get_user_model()
12+
13+
14+
def date_range(start_date, end_date):
15+
for i in range((end_date - start_date).days + 1):
16+
yield start_date + timedelta(i)
17+
18+
19+
class Command(BaseCommand):
20+
help = "Create Sample entries for the demo app"
21+
22+
def handle(self, *args, **options):
23+
# create clients
24+
models_list = [
25+
Client,
26+
Product,
27+
]
28+
client_countries = [
29+
"US",
30+
"DE",
31+
"EG",
32+
"IN",
33+
"KW",
34+
"RA"
35+
]
36+
product_category = [
37+
"extra_big",
38+
"big",
39+
"medium",
40+
"small",
41+
"extra-small"
42+
]
43+
SalesTransaction.objects.all().delete()
44+
Client.objects.all().delete()
45+
Product.objects.all().delete()
46+
ProductCategory.objects.all().delete()
47+
User.objects.filter(is_superuser=False).delete()
48+
for i in range(10):
49+
User.objects.create_user(username=f"user {i}", password="password")
50+
51+
users_id = list(User.objects.values_list("id", flat=True))
52+
for i in range(1, 4):
53+
ProductCategory.objects.create(name=f"Product Category {i}")
54+
55+
product_category_ids = list(ProductCategory.objects.values_list("id", flat=True))
56+
for i in range(1, 10):
57+
Client.objects.create(name=f"Client {i}",
58+
country=random.choice(client_countries),
59+
# owner_id=random.choice(users_id)
60+
)
61+
clients_ids = list(Client.objects.values_list("pk", flat=True))
62+
# create products
63+
for i in range(1, 10):
64+
Product.objects.create(name=f"Product {i}",
65+
product_category_id=random.choice(product_category_ids),
66+
size=random.choice(product_category))
67+
products_ids = list(Product.objects.values_list("pk", flat=True))
68+
69+
current_year = datetime.datetime.today().year
70+
start_date = datetime.datetime(current_year, 1, 1)
71+
end_date = datetime.datetime(current_year + 1, 1, 1)
72+
73+
for date in date_range(start_date, end_date):
74+
for i in range(1, 10):
75+
SalesTransaction.objects.create(
76+
client_id=random.choice(clients_ids),
77+
product_id=random.choice(products_ids),
78+
quantity=random.randint(1, 10),
79+
price=random.randint(1, 100),
80+
date=date,
81+
number=f"Sale {date.strftime('%Y-%m-%d')} #{i}",
82+
)
83+
# ExpenseTransaction.objects.create(
84+
# expense_id=random.choice(expense_ids),
85+
# value=random.randint(1, 100),
86+
# date=date,
87+
# number=f"Expense {date.strftime('%Y-%m-%d')} #{i}",
88+
# )
89+
90+
self.stdout.write(self.style.SUCCESS("Entries Created Successfully"))
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# Generated by Django 4.2 on 2023-08-02 09:14
2+
3+
from django.db import migrations, models
4+
import django.db.models.deletion
5+
6+
7+
class Migration(migrations.Migration):
8+
initial = True
9+
10+
dependencies = []
11+
12+
operations = [
13+
migrations.CreateModel(
14+
name="Client",
15+
fields=[
16+
(
17+
"id",
18+
models.BigAutoField(
19+
auto_created=True,
20+
primary_key=True,
21+
serialize=False,
22+
verbose_name="ID",
23+
),
24+
),
25+
("name", models.CharField(max_length=100, verbose_name="Client Name")),
26+
],
27+
options={
28+
"verbose_name": "Client",
29+
"verbose_name_plural": "Clients",
30+
},
31+
),
32+
migrations.CreateModel(
33+
name="Product",
34+
fields=[
35+
(
36+
"id",
37+
models.BigAutoField(
38+
auto_created=True,
39+
primary_key=True,
40+
serialize=False,
41+
verbose_name="ID",
42+
),
43+
),
44+
("name", models.CharField(max_length=100, verbose_name="Product Name")),
45+
],
46+
options={
47+
"verbose_name": "Product",
48+
"verbose_name_plural": "Products",
49+
},
50+
),
51+
migrations.CreateModel(
52+
name="SalesTransaction",
53+
fields=[
54+
(
55+
"id",
56+
models.BigAutoField(
57+
auto_created=True,
58+
primary_key=True,
59+
serialize=False,
60+
verbose_name="ID",
61+
),
62+
),
63+
(
64+
"number",
65+
models.CharField(
66+
max_length=100, verbose_name="Sales Transaction #"
67+
),
68+
),
69+
("date", models.DateTimeField()),
70+
("notes", models.TextField(blank=True, null=True)),
71+
("value", models.DecimalField(decimal_places=2, max_digits=9)),
72+
(
73+
"client",
74+
models.ForeignKey(
75+
on_delete=django.db.models.deletion.PROTECT,
76+
to="demo_app.client",
77+
verbose_name="Client",
78+
),
79+
),
80+
(
81+
"product",
82+
models.ForeignKey(
83+
on_delete=django.db.models.deletion.PROTECT,
84+
to="demo_app.product",
85+
verbose_name="Product",
86+
),
87+
),
88+
],
89+
options={
90+
"verbose_name": "Sales Transaction",
91+
"verbose_name_plural": "Sales Transactions",
92+
},
93+
),
94+
]

0 commit comments

Comments
 (0)