Draw a dual-bay secondary where it actually is, and make two counts add up
Three map and aggregation defects. The first is a behaviour change you asked for; the other two are the 2026-09-03 run's findings. A dualpath secondary bay was excluded from /api/assets/map unconditionally while the collapse setting is on, so positioning it was a silent no-op: the form took the pin, stored mapx/mapy, and the map never drew it. One marker per pair is right when the pair sits in one place, and that stays the default - but a dual-bay machine whose second bay is physically elsewhere is exactly what positioning it separately means. Now: a secondary with its own position draws its own marker; one without is represented by the primary's, which carries the combined '2007 / 2008' label. Nothing extra is needed for the fallback, because the feed already requires mapx/mapy - an unpositioned bay never reaches the loop. The combined label is now suppressed when the partner draws its own marker. Labelling two separate pins '2007 / 2008' would say one machine is in two places, which is worse than either bay being unlabelled. Verified against a live instance: unpositioned secondary gives one marker labelled with both bays; positioned gives two markers each with its own name; clearing the position puts it back to one. That third case matters - a one-way door would be its own bug. machines/dashboard/summary applied the dualpath exclusion to total and bytype but not to bystatus, so the status bars summed to more than the headline above them, by exactly the number of secondary bays. A breakdown that does not add up to its own total is an afternoon of reconciling. The measuring-tool calibrationstatus filter ran AFTER pagination. It is a derived value so it cannot be a SQL filter, but applying it to the current page meant asking for 'overdue' with one overdue tool in sixteen returned an empty page 1, a total of 16, eight pages, and the row itself on page 3. When that filter is in play the matching set is now built and filtered before paging - the same tradeoff the old comment named, spent on correct answers instead of a cheaper query. Seven tests. The pagination one verified against the committed version, which fails it. Scope: product
This commit is contained in:
@@ -586,3 +586,61 @@ def test_an_edit_is_saved_even_when_an_audited_field_holds_a_date(
|
||||
headers=auth_headers).get_json()['data']
|
||||
assert after['name'] == 'After rename'
|
||||
assert after['measuringtool']['nextcalibrationdate'] == '2028-01-15'
|
||||
|
||||
|
||||
# --- a derived filter must page over the MATCHES ----------------------------
|
||||
#
|
||||
# calibrationstatus was applied to the rows of the current page, so a request
|
||||
# for 'overdue' returned whatever matched on that page while the total counted
|
||||
# every tool. With one overdue tool in sixteen: page 1 empty, total 16, eight
|
||||
# pages, and the row itself on page 3. The filter hid matching rows and
|
||||
# miscounted the rest.
|
||||
|
||||
def test_a_calibration_filter_pages_over_the_matches_not_the_page(
|
||||
client, auth_headers):
|
||||
from datetime import date, timedelta
|
||||
|
||||
overdue = (date.today() - timedelta(days=5)).isoformat()
|
||||
current = (date.today() + timedelta(days=400)).isoformat()
|
||||
|
||||
# One overdue tool, well past the first page of a small page size.
|
||||
for index in range(6):
|
||||
created = client.post('/api/measuringtools', headers=auth_headers, json={
|
||||
'assetnumber': 'MT-PAGE-%02d' % index,
|
||||
'name': 'Paged tool %d' % index,
|
||||
'statusid': _status_id(client),
|
||||
'nextcalibrationdate': overdue if index == 5 else current,
|
||||
})
|
||||
assert created.status_code == 201, created.get_json()
|
||||
|
||||
# Scoped with search as well, so earlier tests in this module cannot
|
||||
# change the expected set - the point is paging over matches, not how many
|
||||
# overdue tools the whole fixture happens to hold.
|
||||
response = client.get(
|
||||
'/api/measuringtools?calibrationstatus=overdue&search=MT-PAGE&perpage=2',
|
||||
headers=auth_headers)
|
||||
assert response.status_code == 200, response.get_json()
|
||||
body = response.get_json()
|
||||
rows = body['data']
|
||||
pagination = body['meta']['pagination']
|
||||
|
||||
assert [row['assetnumber'] for row in rows] == ['MT-PAGE-05'], \
|
||||
'the overdue tool must be on page 1 of a filtered list'
|
||||
assert pagination['total'] == 1, \
|
||||
'the total counts the MATCHES, not every tool'
|
||||
assert pagination['totalpages'] == 1
|
||||
|
||||
|
||||
def test_an_unfiltered_list_still_pages_normally(client, auth_headers):
|
||||
for index in range(3):
|
||||
client.post('/api/measuringtools', headers=auth_headers, json={
|
||||
'assetnumber': 'MT-PLAIN-%02d' % index,
|
||||
'name': 'Plain tool %d' % index,
|
||||
'statusid': _status_id(client),
|
||||
})
|
||||
|
||||
response = client.get('/api/measuringtools?perpage=2', headers=auth_headers)
|
||||
assert response.status_code == 200
|
||||
pagination = response.get_json()['meta']['pagination']
|
||||
assert pagination['perpage'] == 2
|
||||
assert pagination['total'] >= 3
|
||||
|
||||
Reference in New Issue
Block a user