Skip to content

Commit cbed5b1

Browse files
committed
Use a server-driven datatable for stock type search page
1 parent b0b782f commit cbed5b1

4 files changed

Lines changed: 253 additions & 90 deletions

File tree

quicktill/tillweb/datatable.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,12 @@
2222
Department,
2323
Delivery,
2424
Supplier,
25+
Unit,
2526
StockType,
2627
StockItem,
2728
StockAnnotation,
2829
AnnotationType,
30+
StockTake,
2931
Barcode,
3032
RefusalsLog,
3133
zero,
@@ -681,6 +683,101 @@ def stockitems(request, info):
681683
})
682684

683685

686+
@tillweb_view
687+
def stocktypes(request, info):
688+
columns = {
689+
'id': StockType.id,
690+
'manufacturer': StockType.manufacturer,
691+
'name': StockType.name,
692+
'fullname': StockType.fullname,
693+
'abv': StockType.abv,
694+
'department': StockType.dept_id,
695+
'unit': Unit.description,
696+
'saleprice': StockType.saleprice,
697+
'stocktake': StockType.stocktake_id,
698+
}
699+
700+
search_value = request.GET.get("search[value]")
701+
q = td.s.query(StockType)\
702+
.join(Department)\
703+
.join(Unit)\
704+
.join(StockTake, isouter=True)\
705+
.options(contains_eager(StockType.department),
706+
contains_eager(StockType.unit),
707+
contains_eager(StockType.stocktake))
708+
709+
# Apply filters from parameters, before counting the size of the table
710+
try:
711+
department_pre_filter = int(request.GET.get("department_pre_filter"))
712+
q = q.filter(StockType.dept_id == department_pre_filter)
713+
except (ValueError, TypeError):
714+
pass
715+
716+
fq = q
717+
718+
# Apply filters from parameters, after counting the size of the table
719+
include_archived = request.GET.get("include_archived", "no") == "yes"
720+
if not include_archived:
721+
fq = fq.filter(StockType.archived == False)
722+
723+
manufacturer = request.GET.get("manufacturer")
724+
if manufacturer:
725+
fq = fq.filter(StockType.manufacturer.ilike(f"%{manufacturer}%"))
726+
727+
name = request.GET.get("name")
728+
if name:
729+
fq = fq.filter(StockType.name.ilike(f"%{name}%"))
730+
731+
try:
732+
department = int(request.GET.get("department"))
733+
fq = fq.filter(StockType.dept_id == department)
734+
except (ValueError, TypeError):
735+
pass
736+
737+
# Apply filters from search value
738+
if search_value:
739+
try:
740+
intsearch = int(search_value)
741+
except ValueError:
742+
intsearch = None
743+
try:
744+
decsearch = Decimal(search_value)
745+
except decimal.InvalidOperation:
746+
decsearch = None
747+
qs = [
748+
columns['manufacturer'].ilike(f'%{search_value}%'),
749+
columns['name'].ilike(f'{search_value}%'),
750+
columns['fullname'].ilike(f'%{search_value}%'),
751+
]
752+
if intsearch:
753+
qs.append(columns['id'] == intsearch)
754+
if decsearch:
755+
qs.append(columns['abv'] == decsearch)
756+
qs.append(columns['saleprice'] == decsearch)
757+
fq = fq.filter(or_(*qs))
758+
759+
# 'st' is a StockType in the lambda below
760+
return _datatables_json(
761+
request, q, fq, columns, lambda st: {
762+
'manufacturer': st.manufacturer,
763+
'name': st.name,
764+
'department': st.department.description,
765+
'department_url': st.department.get_absolute_url(),
766+
'abv': st.abv,
767+
'saleprice': st.saleprice,
768+
'manufacturer_url': reverse(
769+
"tillweb-stocktype-search",
770+
query=[("manufacturer", st.manufacturer)]),
771+
'stocktype_url': st.get_absolute_url(),
772+
'unit': st.unit.description,
773+
'unit_url': st.unit.get_absolute_url(),
774+
'stocktake': str(st.stocktake) if st.stocktake else None,
775+
'stocktake_url': st.stocktake.get_absolute_url() if st.stocktake
776+
else None,
777+
'DT_RowClass': "table-danger" if st.archived else None,
778+
})
779+
780+
684781
@tillweb_view
685782
def annotations(request, info):
686783
columns = {

quicktill/tillweb/templates/tillweb/stocktypesearch.html

Lines changed: 133 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,60 @@
55

66
{% block tillcontent %}
77

8-
<form class="mb-2" action="" method="get">
9-
{% include "form-horizontal.html" %}
10-
<div class="row justify-content-center">
11-
<button class="btn btn-primary w-75" type="submit">Find</button>
8+
<form class="mb-2" action="" method="post">{% csrf_token %}
9+
<div class="row mb-3">
10+
<label for="manufacturerInput" class="col-sm-2 col-form-label">Manufacturer</label>
11+
<div class="col-sm-10">
12+
<input type="search" class="form-control" id="manufacturerInput" name="manufacturer">
13+
</div>
1214
</div>
15+
16+
<div class="row mb-3">
17+
<label for="nameInput" class="col-sm-2 col-form-label">Name</label>
18+
<div class="col-sm-10">
19+
<input type="search" class="form-control" id="nameInput" name="name">
20+
</div>
21+
</div>
22+
23+
<div class="row mb-3">
24+
<label for="departmentInput" class="col-sm-2 col-form-label">Department</label>
25+
<div class="col-sm-10">
26+
<select class="col-sm-10 form-select" id="departmentInput" aria-label="Department" name="department">
27+
<option value="" selected>Any</option>
28+
{% for d in departments %}
29+
<option value="{{d.id}}">{{d.description}}</option>
30+
{% endfor %}
31+
</select>
32+
</div>
33+
</div>
34+
35+
<div class="row mb-3 form-check">
36+
<div class="col-sm-10 offset-sm-2">
37+
<input class="form-check-input" type="checkbox" value="" id="checkArchived" name="include_archived">
38+
<label class="form-check-label" for="checkArchived">
39+
Include archived?
40+
</label>
41+
</div>
42+
</div>
43+
44+
{% if create_form %}
45+
<button class="btn btn-secondary mb-2" type="button" data-bs-toggle="modal" data-bs-target="#createModal">
46+
Add new stock type
47+
</button>
48+
{% endif %}
49+
50+
{% if candidate_stocktakes %}
51+
{% for st in candidate_stocktakes %}
52+
<button class="btn btn-secondary mb-2 candidate-stocktake-button" type="submit" name="submit_add_to_{{st.id}}" disabled>
53+
Add to scope for stock take {{st.id}} ({{st.description}})
54+
</button>
55+
{% endfor %}
56+
<p>N.B. stock types already assigned to a stock take won't be assigned
57+
to a different one.</p>
58+
{% endif %}
1359
</form>
1460

1561
{% if create_form %}
16-
<button class="btn btn-secondary mb-2" type="button" data-bs-toggle="modal" data-bs-target="#createModal">
17-
Add new stock type
18-
</button>
19-
2062
{% modal "createModal" dialog_class="modal-lg" title="Add new stock type" %}
2163
<form action="" method="post">{% csrf_token %}
2264
<datalist id="manufacturers">
@@ -46,18 +88,6 @@
4688

4789
{% endif %}
4890

49-
{% if stocktypes %}
50-
{% if candidate_stocktakes %}
51-
<form action="" method="post">{% csrf_token %}
52-
{% for st in candidate_stocktakes %}
53-
<button class="btn btn-secondary mb-2" type="submit" name="submit_add_to_{{st.id}}">
54-
Add to scope for stock take {{st.id}} ({{st.description}})
55-
</button>
56-
{% endfor %}
57-
</form>
58-
<p>N.B. stock types already assigned to a stock take won't be assigned
59-
to a different one.</p>
60-
{% endif %}
6191
<table class="table table-striped table-hover table-sm" id="stocktypelist">
6292
<thead class="table-light">
6393
<tr>
@@ -71,26 +101,92 @@
71101
</tr>
72102
</thead>
73103
<tbody>
74-
{% for t in stocktypes %}
75-
<tr{% if t.archived %} class="table-danger"{% endif %}>
76-
<td><a href="?manufacturer={{t.manufacturer|urlencode}}">{{t.manufacturer}}</a></td>
77-
<td><a href="{{t.get_absolute_url}}">{{t.name}}</a></td>
78-
<td data-sort="{{t.abv|default:0.00}}">{{t.abvstr}}</td>
79-
<td><a href="{{t.department.get_absolute_url}}">{{t.department}}</a></td>
80-
<td>{{t.unit.description}}</td>
81-
{% if t.saleprice %}<td class="money" data-sort="{{t.saleprice}}">{{money}}{{t.pricestr}}</td>
82-
{% else %}<td data-sort="0.00"></td>{% endif %}
83-
<td>{% if t.stocktake %}<a href="{{t.stocktake.get_absolute_url}}">{{t.stocktake}}</a>{% endif %}</td>
84-
</tr>
85-
{% endfor %}
86104
</tbody>
87105
</table>
106+
88107
<script type="text/javascript">
89-
$(document).ready(function(){
90-
$("#stocktypelist").DataTable();
91-
});
92-
</script>
108+
$(document).ready(function() {
109+
let include_archived = false;
110+
let manufacturer = null;
111+
let name = null;
112+
let department = null;
93113

94-
{% endif %}
114+
function update_candidate_stocktakes() {
115+
const valid = (!!manufacturer || !!name || !!department);
116+
if (valid) {
117+
$(".candidate-stocktake-button").removeAttr('disabled');
118+
} else {
119+
$(".candidate-stocktake-button").attr('disabled', 'disabled');
120+
}
121+
}
95122

123+
const stocktype_api = $("#stocktypelist").DataTable({
124+
ajax: {
125+
url: '{% url "tillweb-datatable-stocktypes" %}',
126+
dataSrc: 'data',
127+
data: function (d) {
128+
if (manufacturer) {
129+
d.manufacturer = manufacturer;
130+
}
131+
if (name) {
132+
d.name = name;
133+
}
134+
if (department) {
135+
d.department = department;
136+
}
137+
d.include_archived = include_archived ? 'yes': 'no';
138+
},
139+
},
140+
columns: [
141+
{ data: 'manufacturer',
142+
render: render_link('manufacturer_url', DataTable.render.text()),
143+
},
144+
{ data: 'name',
145+
render: render_link('stocktype_url', DataTable.render.text()),
146+
},
147+
{ data: 'abv',
148+
render: render_abv(),
149+
},
150+
{ data: 'department',
151+
render: render_link('department_url', DataTable.render.text()),
152+
},
153+
{ data: 'unit',
154+
render: render_link('unit_url', DataTable.render.text()),
155+
},
156+
{ data: 'saleprice',
157+
render: render_money(),
158+
},
159+
{ data: 'stocktake',
160+
render: render_link('stocktake_url', DataTable.render.text()),
161+
},
162+
],
163+
order: [[0, 'asc'], [1, 'asc']],
164+
searching: false,
165+
paging: true,
166+
serverSide: true,
167+
});
168+
const reloadDebounce = DataTable.util.debounce(function() {
169+
stocktype_api.ajax.reload(null, true);});
170+
171+
$("#manufacturerInput").on("input", function() {
172+
manufacturer = this.value;
173+
reloadDebounce();
174+
update_candidate_stocktakes();
175+
});
176+
$("#nameInput").on("input", function() {
177+
name = this.value;
178+
reloadDebounce();
179+
update_candidate_stocktakes();
180+
});
181+
$("#departmentInput").change(function() {
182+
department = this.value;
183+
stocktype_api.ajax.reload(null, true);
184+
update_candidate_stocktakes();
185+
});
186+
$("#checkArchived").change(function() {
187+
include_archived = this.checked;
188+
stocktype_api.ajax.reload(null, true);
189+
});
190+
});
191+
</script>
96192
{% endblock %}

quicktill/tillweb/urls.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@
137137
name="tillweb-datatable-deliveries"),
138138
path('datatable/stockitems.json', datatable.stockitems,
139139
name="tillweb-datatable-stockitems"),
140+
path('datatable/stocktypes.json', datatable.stocktypes,
141+
name="tillweb-datatable-stocktypes"),
140142
path('datatable/annotations.json', datatable.annotations,
141143
name="tillweb-datatable-annotations"),
142144
path('datatable/barcodes.json', datatable.barcodes,

0 commit comments

Comments
 (0)