Dispensing Vet

This commit is contained in:
2026-04-26 16:02:42 -04:00
parent 9ec27e245a
commit 05a093afd3
6 changed files with 64 additions and 7 deletions
+50 -5
View File
@@ -318,6 +318,15 @@ function setupEventListeners() {
});
});
const vetInput = document.getElementById('dispenseVet');
if (vetInput) {
vetInput.addEventListener('input', () => {
const pos = vetInput.selectionStart;
vetInput.value = toTitleCase(vetInput.value);
vetInput.setSelectionRange(pos, pos);
});
}
if (variantForm) variantForm.addEventListener('submit', handleAddVariant);
if (editVariantForm) editVariantForm.addEventListener('submit', handleEditVariant);
if (dispenseForm) dispenseForm.addEventListener('submit', handleDispenseDrug);
@@ -1432,6 +1441,20 @@ async function updateBatchInfo() {
const drugOfVariant = allDrugs.find(d => (d.variants || []).some(v => v.id === variantId));
if (drugOfVariant) await ensureDrugDetailLoaded(drugOfVariant.id);
populateDispensePackSelect(getVariantById(variantId));
const vetLabel = document.getElementById('dispenseVetLabel');
const vetInputEl = document.getElementById('dispenseVet');
if (vetLabel && vetInputEl) {
const isControlled = drugOfVariant ? drugOfVariant.is_controlled : false;
vetLabel.textContent = isControlled ? 'Prescribing Vet *' : 'Prescribing Vet';
vetLabel.style.color = isControlled ? '#d32f2f' : '';
vetInputEl.placeholder = isControlled ? "Vet's name (required)" : "Vet's name";
}
} else {
const vetLabel = document.getElementById('dispenseVetLabel');
const vetInputEl = document.getElementById('dispenseVet');
if (vetLabel) { vetLabel.textContent = 'Prescribing Vet'; vetLabel.style.color = ''; }
if (vetInputEl) vetInputEl.placeholder = "Vet's name";
}
updateDispenseModeUi();
@@ -1817,6 +1840,7 @@ async function handleDispenseDrug(e) {
const requestedPackIdValue = document.getElementById('dispensePackSelect').value;
const requestedPackCountValue = document.getElementById('dispensePackCount').value;
const animalName = document.getElementById('dispenseAnimal').value;
const vetName = document.getElementById('dispenseVet')?.value.trim() || '';
const notes = document.getElementById('dispenseNotes').value;
const printEnabled = document.getElementById('dispensePrintEnabled')?.checked;
const dosage = document.getElementById('dispenseDosage')?.value.trim() || '';
@@ -1825,6 +1849,7 @@ async function handleDispenseDrug(e) {
const selectedPackId = requestedPackIdValue ? parseInt(requestedPackIdValue, 10) : null;
const selectedPackCount = requestedPackCountValue ? parseFloat(requestedPackCountValue) : null;
const variant = getVariantById(variantId);
const drugForVariant = allDrugs.find(d => (d.variants || []).some(v => v.id === variantId));
const legacyStockOnly = isLegacyDispenseSelected();
const selectedPack = variant && selectedPackId
? getActivePacksForVariant(variant).find(pack => pack.id === selectedPackId)
@@ -1940,10 +1965,17 @@ async function handleDispenseDrug(e) {
requested_pack_count: dispenseMode === 'pack' ? selectedPackCount : null,
dispense_source: dispenseSource,
animal_name: animalName || null,
prescribing_vet: vetName || null,
notes: notes || null,
allocations
};
if (drugForVariant && drugForVariant.is_controlled && !vetName) {
showToast('Prescribing vet name is required for controlled drugs.', 'warning');
document.getElementById('dispenseVet')?.focus();
return;
}
try {
const response = await apiCall('/dispense', {
method: 'POST',
@@ -2608,6 +2640,12 @@ async function showDrugHistory(drugId) {
<span class="history-label">User:</span>
<span class="history-value">${escapeHtml(item.user_name)}</span>
</div>
${item.prescribing_vet ? `
<div class="history-row">
<span class="history-label">Prescribing Vet:</span>
<span class="history-value">${escapeHtml(item.prescribing_vet)}</span>
</div>
` : ''}
${item.notes ? `
<div class="history-row">
<span class="history-label">Notes:</span>
@@ -2796,6 +2834,10 @@ function escapeHtml(text) {
return div.innerHTML;
}
function toTitleCase(str) {
return str.replace(/\S+/g, word => word.charAt(0).toUpperCase() + word.slice(1));
}
async function openReportsPage() {
const dropdown = document.getElementById('userDropdown');
if (dropdown) dropdown.style.display = 'none';
@@ -3600,10 +3642,10 @@ async function handleBarcodeScan(raw) {
return;
}
_applyBarcodeScanToLines(mapping, lot, expiryStr);
await _applyBarcodeScanToLines(mapping, lot, expiryStr);
}
function _applyBarcodeScanToLines(mapping, lot, expiryStr) {
async function _applyBarcodeScanToLines(mapping, lot, expiryStr) {
const container = document.getElementById('deliveryLinesContainer');
if (!container) return;
@@ -3637,17 +3679,17 @@ function _applyBarcodeScanToLines(mapping, lot, expiryStr) {
}) || null;
if (emptyLine) {
_populateDeliveryLine(emptyLine, mapping, lot, expiryStr);
await _populateDeliveryLine(emptyLine, mapping, lot, expiryStr);
return;
}
// 3. Append a new line
appendDeliveryLine();
const newLine = container.querySelector('.delivery-line:last-child');
if (newLine) _populateDeliveryLine(newLine, mapping, lot, expiryStr);
if (newLine) await _populateDeliveryLine(newLine, mapping, lot, expiryStr);
}
function _populateDeliveryLine(line, mapping, lot, expiryStr) {
async function _populateDeliveryLine(line, mapping, lot, expiryStr) {
const drugSelect = line.querySelector('.delivery-drug-select');
const variantSelect = line.querySelector('.delivery-variant-select');
const packSelect = line.querySelector('.delivery-pack-select');
@@ -3660,6 +3702,9 @@ function _populateDeliveryLine(line, mapping, lot, expiryStr) {
drugSelect.value = String(mapping.drug_id);
}
// Ensure drug detail (with packs) is loaded before trying to populate pack select
await ensureDrugDetailLoaded(mapping.drug_id);
if (variantSelect) {
const drug = allDrugs.find(d => d.id === mapping.drug_id) || null;
variantSelect.innerHTML = buildDeliveryVariantOptions(drug, mapping.drug_variant_id);
+5
View File
@@ -228,6 +228,11 @@
<input type="text" id="dispenseAnimal">
</div>
<div class="form-group">
<label for="dispenseVet" id="dispenseVetLabel">Prescribing Vet</label>
<input type="text" id="dispenseVet" placeholder="Vet's name">
</div>
<div class="form-group" style="margin-top: 18px; padding: 12px; background: #f9fafb; border: 1px solid #d9e2ec; border-radius: 6px;">
<label style="display: inline-flex; align-items: center; gap: 8px; margin-bottom: 0; font-weight: 600;">
<input type="checkbox" id="dispensePrintEnabled">
+4
View File
@@ -317,6 +317,7 @@ function renderDispensingTable(rows) {
const info = getVariantInfoById(row.drug_variant_id);
const quantityText = `${row.quantity} ${info.unit || 'units'}`;
const animal = row.animal_name || '-';
const vet = row.prescribing_vet || '-';
const notes = row.notes || '-';
const allocations = formatDispenseAllocation(row);
@@ -328,6 +329,7 @@ function renderDispensingTable(rows) {
<td>${escapeHtml(info.strength || '-')}</td>
<td>${escapeHtml(quantityText)}</td>
<td>${escapeHtml(animal)}</td>
<td>${escapeHtml(vet)}</td>
<td>${escapeHtml(allocations)}</td>
<td>${escapeHtml(notes)}</td>
</tr>
@@ -344,6 +346,7 @@ function renderDispensingTable(rows) {
<th>Strength</th>
<th>Quantity</th>
<th>Animal</th>
<th>Prescribing Vet</th>
<th>Batch Allocation</th>
<th>Notes</th>
</tr>
@@ -530,6 +533,7 @@ function applyCurrentFilters() {
info.drugName || '',
info.strength || '',
row.animal_name || '',
row.prescribing_vet || '',
row.notes || '',
formatDispenseAllocation(row)
].join(' ').toLowerCase();
-2
View File
@@ -710,8 +710,6 @@ footer {
}
#dispenseModal .form-actions {
position: sticky;
bottom: 0;
margin-top: 16px;
padding-top: 14px;
background: var(--white);