Skip to content

Commit e923e16

Browse files
author
=
committed
changes for #324
1 parent 89a99d5 commit e923e16

File tree

4 files changed

+231
-16
lines changed

4 files changed

+231
-16
lines changed

auctions/models.py

+6-7
Original file line numberDiff line numberDiff line change
@@ -1075,10 +1075,14 @@ def invoice_recalculate(self):
10751075
invoice.recalculate
10761076
invoice.save()
10771077

1078+
@property
1079+
def tos_qs(self):
1080+
return AuctionTOS.objects.filter(auction=self.pk).order_by("-createdon")
1081+
10781082
@property
10791083
def number_of_confirmed_tos(self):
10801084
"""How many people selected a pickup location in this auction"""
1081-
return AuctionTOS.objects.filter(auction=self.pk).count()
1085+
return self.tos_qs.count()
10821086

10831087
@property
10841088
def number_of_sellers(self):
@@ -1223,11 +1227,6 @@ def number_of_participants(self):
12231227
# sellers = User.objects.filter(lot__auction=self.pk, lot__winner__isnull=False).exclude(id__in=buyers).distinct()
12241228
return len(sellers) + len(buyers)
12251229

1226-
@property
1227-
def number_of_tos(self):
1228-
"""This will return users, ignoring any auctiontos without a user set"""
1229-
return AuctionTOS.objects.filter(auction=self.pk).count()
1230-
12311230
@property
12321231
def preregistered_users(self):
12331232
return AuctionTOS.objects.filter(auction=self.pk, manually_added=False).count()
@@ -1338,7 +1337,7 @@ def admin_checklist_joined(self):
13381337

13391338
@property
13401339
def admin_checklist_others_joined(self):
1341-
if self.number_of_tos > 1:
1340+
if self.number_of_confirmed_tos > 1:
13421341
return True
13431342
return False
13441343

auctions/templates/auction_stats.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@
158158
</thead>
159159
<tbody>
160160
<tr>
161-
<td>Total users</td><td>{{auction.number_of_tos}}</td>
161+
<td>Total users</td><td>{{auction.number_of_confirmed_tos}}</td>
162162
</tr>
163163
<tr>
164164
<td>Users who bought or sold at least one item</td><td>{{auction.number_of_participants}}</td>

auctions/urls.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -238,9 +238,9 @@
238238
name="auction_quick_checkout_htmx",
239239
),
240240
path(
241-
"api/auctions/<slug:slug>/validate-name/<path:filter>/",
242-
views.QuickCheckoutHTMX.as_view(),
243-
name="fixme",
241+
"api/auctions/<slug:slug>/validate-tos/",
242+
views.AuctionTOSValidation.as_view(),
243+
name="auctiontos_validation",
244244
),
245245
path("auctions/<slug:slug>/lots/<slug:custom_lot_number>/", views.ViewLot.as_view()),
246246
path(

auctions/views.py

+221-5
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
HttpResponseRedirect,
5252
JsonResponse,
5353
)
54+
from django.middleware.csrf import get_token
5455
from django.shortcuts import get_object_or_404, redirect
5556
from django.template.loader import render_to_string
5657
from django.urls import reverse
@@ -1322,6 +1323,78 @@ def post(self, request, *args, **kwargs):
13221323
return JsonResponse({"result": "success"})
13231324

13241325

1326+
class AuctionTOSValidation(AuctionViewMixin, APIPostView):
1327+
"""For real time validation on the auctiontos admin create form
1328+
See views.AuctionTOSAdmin for the corresponding js and view
1329+
qqq"""
1330+
1331+
def post(self, request, *args, **kwargs):
1332+
pk = request.POST.get("pk", None)
1333+
try:
1334+
pk = int(pk) if pk is not None else None
1335+
except ValueError:
1336+
pk = None
1337+
name = request.POST.get("name", None)
1338+
bidder_number = request.POST.get("bidder_number", None)
1339+
email = request.POST.get("email", None)
1340+
# note: be careful what you dump in result
1341+
# javascript will fill out any id on the form with this info
1342+
result = {
1343+
"id_bidder_number": "",
1344+
"id_name": "",
1345+
"id_email": "",
1346+
"id_address": "",
1347+
"id_is_club_member": "",
1348+
"id_phone_number": "",
1349+
"id_memo": "",
1350+
"name_tooltip": "",
1351+
"bidder_number_tooltip": "",
1352+
"email_tooltip": "",
1353+
}
1354+
base_qs = self.auction.tos_qs
1355+
if pk:
1356+
base_qs = base_qs.exclude(pk=pk)
1357+
if name and not email and not pk:
1358+
old_auctions = Auction.objects.filter(
1359+
Q(created_by=self.auction.created_by)
1360+
| Q(created_by=self.request.user)
1361+
| Q(auctiontos__is_admin=True, auctiontos__user=self.request.user)
1362+
)
1363+
qs = AuctionTOS.objects.filter(auction__in=old_auctions, email__isnull=False).order_by("-createdon")
1364+
old_tos = AuctionTOSFilter.generic(self, qs, name, match_names_only=True).first()
1365+
if old_tos:
1366+
bidder_number_used_in_this_auction = base_qs.filter(bidder_number=old_tos.bidder_number).first()
1367+
if not bidder_number_used_in_this_auction:
1368+
result["id_bidder_number"] = old_tos.bidder_number
1369+
result["id_name"] = old_tos.name
1370+
result["id_email"] = old_tos.email
1371+
result["id_address"] = old_tos.address
1372+
result["id_is_club_member"] = old_tos.is_club_member
1373+
result["id_phone_number"] = old_tos.phone_number
1374+
result["id_memo"] = old_tos.memo
1375+
else:
1376+
logger.info("no user found in older auctions with name %s", name)
1377+
if name:
1378+
existing_tos_in_this_auction = AuctionTOSFilter.generic(self, base_qs, name, match_names_only=True).first()
1379+
if existing_tos_in_this_auction:
1380+
result["name_tooltip"] = f"{existing_tos_in_this_auction.name} is already in this auction"
1381+
else:
1382+
logger.info("no user found in older auctions with name %s", name)
1383+
if email:
1384+
existing_tos_in_this_auction = base_qs.filter(email=email).first()
1385+
if existing_tos_in_this_auction:
1386+
result["email_tooltip"] = "Email is already in this auction"
1387+
else:
1388+
logger.info("no user found in this auction with email %s", email)
1389+
if bidder_number:
1390+
existing_tos_in_this_auction = base_qs.filter(bidder_number=bidder_number).first()
1391+
if existing_tos_in_this_auction:
1392+
result["bidder_number_tooltip"] = "Bidder number in use"
1393+
else:
1394+
logger.info("no user found in this auction with email %s", email)
1395+
return JsonResponse(result)
1396+
1397+
13251398
@login_required
13261399
def my_won_lot_csv(request):
13271400
"""CSV file showing won lots"""
@@ -1658,7 +1731,9 @@ def auctionLotList(request, slug):
16581731
first_row_fields.append(auction.custom_field_1_name)
16591732
writer.writerow(first_row_fields)
16601733
# lots = Lot.objects.exclude(is_deleted=True).filter(auction__slug=slug, auctiontos_winner__isnull=False).select_related('user', 'winner')
1661-
lots = auction.lots_qs.filter(winning_price__isnull=False).select_related("user", "winner")
1734+
lots = auction.lots_qs.filter(winning_price__isnull=False, auctiontos_winner__isnull=False).select_related(
1735+
"user", "winner"
1736+
)
16621737
lots = add_price_info(lots)
16631738
for lot in lots:
16641739
row = [
@@ -4001,10 +4076,151 @@ def get_context_data(self, **kwargs):
40014076
# for real time form validation
40024077
extra_script = "<script>"
40034078
if self.auctiontos:
4004-
extra_script += f"let pk={self.auctiontos.pk};"
4079+
extra_script += f"var pk={self.auctiontos.pk};"
40054080
else:
4006-
extra_script += "let pk=null;"
4007-
extra_script += "console.log(pk);</script>"
4081+
extra_script += "var pk=null;"
4082+
extra_script += f"""var validation_url = '{reverse('auctiontos_validation', kwargs={'slug': self.auction.slug})}';
4083+
var csrf_token = '{get_token(self.request)}';'qqq'"""
4084+
extra_script += """
4085+
4086+
function setFieldInvalid(fieldId, message, is_invalid) {
4087+
var field = document.getElementById(fieldId);
4088+
if (!field) return;
4089+
4090+
var feedbackId = fieldId + "_feedback";
4091+
var feedback = document.getElementById(feedbackId);
4092+
4093+
if (is_invalid) {
4094+
field.classList.add("is-invalid");
4095+
var existing_error = document.getElementById( "error_1_"+fieldId);
4096+
if (existing_error) {
4097+
existing_error.remove();
4098+
}
4099+
if (feedback) {
4100+
feedback.remove();
4101+
}
4102+
feedback = document.createElement("div");
4103+
feedback.id = feedbackId;
4104+
feedback.className = "invalid-feedback";
4105+
field.parentNode.appendChild(feedback);
4106+
4107+
feedback.textContent = message;
4108+
} else {
4109+
field.classList.remove("is-invalid");
4110+
if (feedback) {
4111+
feedback.remove();
4112+
}
4113+
}
4114+
}
4115+
4116+
function showAutocomplete(response, remove) {
4117+
var feedback = document.getElementById('id_name_feedback');
4118+
if (feedback) {
4119+
feedback.remove();
4120+
}
4121+
if (remove) {
4122+
return;
4123+
}
4124+
feedback = document.createElement("div");
4125+
feedback.id = "id_name_feedback";
4126+
feedback.className = "valid-feedback d-block cursor-pointer";
4127+
feedback.innerHTML = "<button role='button' class='btn btn-sm btn-info' id='autocompleteTosForm'>Click to use " + response.id_email + "</button>";
4128+
var autocomplete = response;
4129+
document.getElementById('id_name').parentNode.appendChild(feedback);
4130+
4131+
//setTimeout(function() {
4132+
var link = document.getElementById('autocompleteTosForm');
4133+
link.addEventListener('click', function(event) {
4134+
event.preventDefault();
4135+
4136+
for (var key in autocomplete) {
4137+
console.log(key);
4138+
if (autocomplete.hasOwnProperty(key)) {
4139+
var element = document.getElementById(key);
4140+
if (element) {
4141+
if (element.type !== "checkbox" && element.value === "") {
4142+
element.value = autocomplete[key] || '';
4143+
}
4144+
if (element.type === "checkbox") {
4145+
element.checked = autocomplete[key] === true;
4146+
}
4147+
}
4148+
}
4149+
}
4150+
4151+
});
4152+
link.focus();
4153+
//}, 40);
4154+
4155+
}
4156+
4157+
4158+
function showTooltip(element, message) {
4159+
var el = element;
4160+
4161+
// Destroy existing tooltip (if any)
4162+
if (el._tooltipInstance) {
4163+
el._tooltipInstance.dispose();
4164+
}
4165+
4166+
// Set tooltip attributes
4167+
el.setAttribute("data-bs-toggle", "tooltip");
4168+
el.setAttribute("data-bs-placement", "right");
4169+
el.setAttribute("title", message);
4170+
4171+
// Initialize and show Bootstrap tooltip
4172+
el._tooltipInstance = new bootstrap.Tooltip(el);
4173+
el._tooltipInstance.show();
4174+
}
4175+
4176+
function hideTooltip(element) {
4177+
if (element._tooltipInstance) {
4178+
element._tooltipInstance.dispose();
4179+
element._tooltipInstance = null;
4180+
}
4181+
}
4182+
4183+
function validateField() {
4184+
var nameInput = document.getElementById("id_name");
4185+
4186+
var data = {
4187+
pk: pk,
4188+
name: $("#id_name").val(),
4189+
bidder_number: $("#id_bidder_number").val(),
4190+
email: $("#id_email").val(),
4191+
};
4192+
4193+
$.ajax({
4194+
url: validation_url,
4195+
type: "POST",
4196+
data: data,
4197+
headers: { "X-CSRFToken": csrf_token },
4198+
success: function (response) {
4199+
if (response.name_tooltip) {
4200+
showTooltip(nameInput, response.name_tooltip);
4201+
showAutocomplete(response, true)
4202+
} else if (response.id_email) {
4203+
showAutocomplete(response)
4204+
} else {
4205+
hideTooltip(nameInput);
4206+
showAutocomplete(response, true)
4207+
}
4208+
if (response.email_tooltip) {
4209+
setFieldInvalid("id_email", response.email_tooltip, true);
4210+
} else {
4211+
setFieldInvalid("id_email", response.email_tooltip, false);
4212+
}
4213+
if (response.bidder_number_tooltip) {
4214+
setFieldInvalid("id_bidder_number", response.bidder_number_tooltip, true);
4215+
} else {
4216+
setFieldInvalid("id_bidder_number", response.bidder_number_tooltip, false);
4217+
}
4218+
}
4219+
});
4220+
}
4221+
4222+
$("#id_bidder_number, #id_name, #id_email").on("blur", validateField);
4223+
</script>"""
40084224
context["extra_script"] = mark_safe(extra_script)
40094225
return context
40104226

@@ -5157,7 +5373,7 @@ def get_queryset(self):
51575373

51585374
def dispatch(self, request, *args, **kwargs):
51595375
self.lot = get_object_or_404(Lot, pk=kwargs.pop("pk"), is_deleted=False)
5160-
self.filename = "label_" + self.lot.custom_lot_number
5376+
self.filename = f"label_{self.lot.lot_number_display}"
51615377
if self.lot.auctiontos_seller:
51625378
self.auction = self.lot.auctiontos_seller.auction
51635379
auth = False

0 commit comments

Comments
 (0)