Remediate the 2026-09-03 feature-test findings
This commit is contained in:
@@ -13,7 +13,7 @@ from shopdb.api import db, success_response, error_response, paginated_response,
|
||||
from ..models import Notification, NotificationType
|
||||
from ..models.notification import _utc_iso
|
||||
|
||||
from shopdb.api import require_permission, Setting
|
||||
from shopdb.api import require_permission, Setting, intarg
|
||||
|
||||
notifications_bp = Blueprint('notifications', __name__)
|
||||
|
||||
@@ -355,8 +355,8 @@ def list_notifications():
|
||||
query = query.filter(Notification.isactive == True)
|
||||
|
||||
# Type filter
|
||||
if type_id := request.args.get('typeid', request.args.get('type_id')):
|
||||
query = query.filter(Notification.notificationtypeid == int(type_id))
|
||||
if (type_id := intarg('typeid', intarg('type_id'))) is not None:
|
||||
query = query.filter(Notification.notificationtypeid == type_id)
|
||||
|
||||
# Exact-match lookup for idempotent import. Notifications have no strong
|
||||
# natural key; ticketnumber is the best available when a ticket is set.
|
||||
|
||||
@@ -244,6 +244,7 @@
|
||||
import { ref, onMounted, computed, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { notificationsApi, applicationsApi, businessUnitsApi, employeesApi, settingsApi } from '@/api'
|
||||
import { resolveEmployeeName, fullname } from '@/composables/employeeResolve'
|
||||
import { apiError } from '@/utils/apiError'
|
||||
import { zonedInputFromUtc, utcFromZonedInput, DEFAULT_TZ } from '@/utils/datetime'
|
||||
|
||||
@@ -459,6 +460,30 @@ async function addCustomEmployee() {
|
||||
}
|
||||
selectedEmployees.value.push({ sso: token, name })
|
||||
} else {
|
||||
// Look the name up before treating it as free text. Typing a name in
|
||||
// full and pressing Enter used to produce `NAME:<text>`, linked to
|
||||
// nothing - no photo, no directory record - while picking the same
|
||||
// person off the suggestion list produced a real SSO. The chip looked
|
||||
// identical either way, which is why it went unnoticed.
|
||||
const { match, matches, failed } = await resolveEmployeeName(token)
|
||||
if (match) {
|
||||
const sso = String(match.SSO)
|
||||
if (selectedEmployees.value.some(e => e.sso === sso)) continue
|
||||
selectedEmployees.value.push({ sso, name: fullname(match) || token })
|
||||
continue
|
||||
}
|
||||
if (matches && matches.length) {
|
||||
// Several people match. Guessing is exactly wrong here, so show the
|
||||
// list and let the operator pick.
|
||||
employeeResults.value = matches
|
||||
error.value = `More than one employee matches "${token}" - pick one.`
|
||||
return
|
||||
}
|
||||
if (failed) {
|
||||
error.value = 'Could not reach the employee directory. Try again, or '
|
||||
+ 'pick from the list.'
|
||||
return
|
||||
}
|
||||
const key = `NAME:${token}`
|
||||
if (selectedEmployees.value.some(e => e.sso === key)) continue
|
||||
selectedEmployees.value.push({ sso: key, name: token })
|
||||
|
||||
Reference in New Issue
Block a user