You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
465 lines
15 KiB
465 lines
15 KiB
2 years ago
|
<?php
|
||
|
|
||
|
// Copyright (C) 2006-2021 Rod Roark <rod@sunsetsystems.com>
|
||
|
//
|
||
|
// This program is free software; you can redistribute it and/or
|
||
|
// modify it under the terms of the GNU General Public License
|
||
|
// as published by the Free Software Foundation; either version 2
|
||
|
// of the License, or (at your option) any later version.
|
||
|
|
||
|
require_once("../globals.php");
|
||
|
require_once("drugs.inc.php");
|
||
|
require_once("$srcdir/options.inc.php");
|
||
|
|
||
|
use OpenEMR\Common\Acl\AclMain;
|
||
|
use OpenEMR\Common\Twig\TwigContainer;
|
||
|
use OpenEMR\Core\Header;
|
||
|
|
||
|
// Check authorizations.
|
||
|
$auth_admin = AclMain::aclCheckCore('admin', 'drugs');
|
||
|
$auth_lots = $auth_admin ||
|
||
|
AclMain::aclCheckCore('inventory', 'lots') ||
|
||
|
AclMain::aclCheckCore('inventory', 'purchases') ||
|
||
|
AclMain::aclCheckCore('inventory', 'transfers') ||
|
||
|
AclMain::aclCheckCore('inventory', 'adjustments') ||
|
||
|
AclMain::aclCheckCore('inventory', 'consumption') ||
|
||
|
AclMain::aclCheckCore('inventory', 'destruction');
|
||
|
$auth_anything = $auth_lots ||
|
||
|
AclMain::aclCheckCore('inventory', 'sales') ||
|
||
|
AclMain::aclCheckCore('inventory', 'reporting');
|
||
|
if (!$auth_anything) {
|
||
|
echo (new TwigContainer(null, $GLOBALS['kernel']))->getTwig()->render('core/unauthorized.html.twig', ['pageTitle' => xl("Drug Inventory")]);
|
||
|
exit;
|
||
|
}
|
||
|
// Note if user is restricted to any facilities and/or warehouses.
|
||
|
$is_user_restricted = isUserRestricted();
|
||
|
|
||
|
// For each sorting option, specify the ORDER BY argument.
|
||
|
//
|
||
|
$ORDERHASH = array(
|
||
|
'prod' => 'd.name, d.drug_id, di.expiration, di.lot_number',
|
||
|
'act' => 'd.active, d.name, d.drug_id, di.expiration, di.lot_number',
|
||
|
'ndc' => 'd.ndc_number, d.name, d.drug_id, di.expiration, di.lot_number',
|
||
|
'con' => 'd.consumable, d.name, d.drug_id, di.expiration, di.lot_number',
|
||
|
'form' => 'lof.title, d.name, d.drug_id, di.expiration, di.lot_number',
|
||
|
'lot' => 'di.lot_number, d.name, d.drug_id, di.expiration',
|
||
|
'wh' => 'lo.title, d.name, d.drug_id, di.expiration, di.lot_number',
|
||
|
'fac' => 'f.name, d.name, d.drug_id, di.expiration, di.lot_number',
|
||
|
'qoh' => 'di.on_hand, d.name, d.drug_id, di.expiration, di.lot_number',
|
||
|
'exp' => 'di.expiration, d.name, d.drug_id, di.lot_number',
|
||
|
);
|
||
|
|
||
|
$form_facility = 0 + empty($_REQUEST['form_facility']) ? 0 : $_REQUEST['form_facility'];
|
||
|
$form_show_empty = empty($_REQUEST['form_show_empty']) ? 0 : 1;
|
||
|
$form_show_inactive = empty($_REQUEST['form_show_inactive']) ? 0 : 1;
|
||
|
$form_consumable = isset($_REQUEST['form_consumable']) ? intval($_REQUEST['form_consumable']) : 0;
|
||
|
|
||
|
// Incoming form_warehouse, if not empty is in the form "warehouse/facility".
|
||
|
// The facility part is an attribute used by JavaScript logic.
|
||
|
$form_warehouse = empty($_REQUEST['form_warehouse']) ? '' : $_REQUEST['form_warehouse'];
|
||
|
$tmp = explode('/', $form_warehouse);
|
||
|
$form_warehouse = $tmp[0];
|
||
|
|
||
|
// Get the order hash array value and key for this request.
|
||
|
$form_orderby = isset($ORDERHASH[$_REQUEST['form_orderby'] ?? '']) ? $_REQUEST['form_orderby'] : 'prod';
|
||
|
$orderby = $ORDERHASH[$form_orderby];
|
||
|
|
||
|
$binds = array();
|
||
|
$where = "WHERE 1 = 1";
|
||
|
if ($form_facility) {
|
||
|
$where .= " AND lo.option_value IS NOT NULL AND lo.option_value = ?";
|
||
|
$binds[] = $form_facility;
|
||
|
}
|
||
|
if ($form_warehouse) {
|
||
|
$where .= " AND di.warehouse_id IS NOT NULL AND di.warehouse_id = ?";
|
||
|
$binds[] = $form_warehouse;
|
||
|
}
|
||
|
if (!$form_show_inactive) {
|
||
|
$where .= " AND d.active = 1";
|
||
|
}
|
||
|
if ($form_consumable) {
|
||
|
if ($form_consumable == 1) {
|
||
|
$where .= " AND d.consumable = '1'";
|
||
|
} else {
|
||
|
$where .= " AND d.consumable != '1'";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$dion = $form_show_empty ? "" : "AND di.on_hand != 0";
|
||
|
|
||
|
// get drugs
|
||
|
$res = sqlStatement(
|
||
|
"SELECT d.*, " .
|
||
|
"di.inventory_id, di.lot_number, di.expiration, di.manufacturer, di.on_hand, " .
|
||
|
"di.warehouse_id, lo.title, lo.option_value AS facid, f.name AS facname " .
|
||
|
"FROM drugs AS d " .
|
||
|
"LEFT JOIN drug_inventory AS di ON di.drug_id = d.drug_id " .
|
||
|
"AND di.destroy_date IS NULL $dion " .
|
||
|
"LEFT JOIN list_options AS lo ON lo.list_id = 'warehouse' AND " .
|
||
|
"lo.option_id = di.warehouse_id AND lo.activity = 1 " .
|
||
|
"LEFT JOIN facility AS f ON f.id = lo.option_value " .
|
||
|
"LEFT JOIN list_options AS lof ON lof.list_id = 'drug_form' AND " .
|
||
|
"lof.option_id = d.form AND lof.activity = 1 " .
|
||
|
"$where ORDER BY d.active DESC, $orderby",
|
||
|
$binds
|
||
|
);
|
||
|
|
||
|
function generateEmptyTd($n)
|
||
|
{
|
||
|
$temp = '';
|
||
|
while ($n > 0) {
|
||
|
$temp .= "<td></td>";
|
||
|
$n--;
|
||
|
}
|
||
|
echo $temp;
|
||
|
}
|
||
|
function processData($data)
|
||
|
{
|
||
|
$data['inventory_id'] = [$data['inventory_id']];
|
||
|
$data['lot_number'] = [$data['lot_number']];
|
||
|
$data['facname'] = [$data['facname']];
|
||
|
$data['title'] = [$data['title']];
|
||
|
$data['on_hand'] = [$data['on_hand']];
|
||
|
$data['expiration'] = [$data['expiration']];
|
||
|
return $data;
|
||
|
}
|
||
|
function mergeData($d1, $d2)
|
||
|
{
|
||
|
$d1['inventory_id'] = array_merge($d1['inventory_id'], $d2['inventory_id']);
|
||
|
$d1['lot_number'] = array_merge($d1['lot_number'], $d2['lot_number']);
|
||
|
$d1['facname'] = array_merge($d1['facname'], $d2['facname']);
|
||
|
$d1['title'] = array_merge($d1['title'], $d2['title']);
|
||
|
$d1['on_hand'] = array_merge($d1['on_hand'], $d2['on_hand']);
|
||
|
$d1['expiration'] = array_merge($d1['expiration'], $d2['expiration']);
|
||
|
return $d1;
|
||
|
}
|
||
|
function mapToTable($row)
|
||
|
{
|
||
|
global $auth_admin, $auth_lots;
|
||
|
$today = date('Y-m-d');
|
||
|
if ($row) {
|
||
|
echo " <tr class='detail'>\n";
|
||
|
$lastid = $row['drug_id'];
|
||
|
if ($auth_admin) {
|
||
|
echo "<td title='" . xla('Click to edit') . "' onclick='dodclick(" . attr(addslashes($lastid)) . ")'>" .
|
||
|
"<a href='' onclick='return false'>" .
|
||
|
text($row['name']) . "</a></td>\n";
|
||
|
} else {
|
||
|
echo " <td>" . text($row['name']) . "</td>\n";
|
||
|
}
|
||
|
echo " <td>" . ($row['active'] ? xlt('Yes') : xlt('No')) . "</td>\n";
|
||
|
echo " <td>" . ($row['consumable'] ? xlt('Yes') : xlt('No')) . "</td>\n";
|
||
|
echo " <td>" . text($row['ndc_number']) . "</td>\n";
|
||
|
echo " <td>" .
|
||
|
generate_display_field(array('data_type' => '1','list_id' => 'drug_form'), $row['form']) .
|
||
|
"</td>\n";
|
||
|
echo " <td>" . text($row['size']) . "</td>\n";
|
||
|
echo " <td title='" . xla('Measurement Units') . "'>" .
|
||
|
generate_display_field(array('data_type' => '1','list_id' => 'drug_units'), $row['unit']) .
|
||
|
"</td>\n";
|
||
|
|
||
|
if ($auth_lots && $row['dispensable']) {
|
||
|
echo " <td onclick='doiclick(" . intval($lastid) . ",0)' title='" .
|
||
|
xla('Purchase or Transfer') . "' style='padding:0'>" .
|
||
|
"<input type='button' value='" . xla('Tran') . "'style='padding:0' /></td>\n";
|
||
|
} else {
|
||
|
echo " <td title='" . xla('Not applicable') . "'> </td>\n";
|
||
|
}
|
||
|
|
||
|
if (!empty($row['inventory_id'][0])) {
|
||
|
echo "<td>";
|
||
|
foreach ($row['inventory_id'] as $key => $value) {
|
||
|
if ($auth_lots) {
|
||
|
echo "<div title='" .
|
||
|
xla('Adjustment, Consumption, Return, or Edit') .
|
||
|
"' onclick='doiclick(" . intval($lastid) . "," .
|
||
|
intval($row['inventory_id'][$key]) . ")'>" .
|
||
|
"<a href='' onclick='return false'>" .
|
||
|
text($row['lot_number'][$key]) .
|
||
|
"</a></div>";
|
||
|
} else {
|
||
|
echo " <div>" . text($row['lot_number'][$key]) . "</div>\n";
|
||
|
}
|
||
|
}
|
||
|
echo "</td>\n<td>";
|
||
|
|
||
|
foreach ($row['facname'] as $value) {
|
||
|
$value = $value != null ? $value : "N/A";
|
||
|
echo "<div >" . text($value) . "</div>";
|
||
|
}
|
||
|
echo "</td>\n<td>";
|
||
|
|
||
|
foreach ($row['title'] as $value) {
|
||
|
$value = $value != null ? $value : "N/A";
|
||
|
echo "<div >" . text($value) . "</div>";
|
||
|
}
|
||
|
echo "</td>\n<td>";
|
||
|
|
||
|
foreach ($row['on_hand'] as $value) {
|
||
|
$value = $value != null ? $value : "N/A";
|
||
|
echo "<div >" . text($value) . "</div>";
|
||
|
}
|
||
|
echo "</td>\n<td>";
|
||
|
|
||
|
foreach ($row['expiration'] as $value) {
|
||
|
// Make the expiration date red if expired.
|
||
|
$expired = !empty($value) && strcmp($value, $today) <= 0;
|
||
|
$value = !empty($value) ? oeFormatShortDate($value) : xl('N/A');
|
||
|
echo "<div" . ($expired ? " style='color:red'" : "") . ">" . text($value) . "</div>";
|
||
|
}
|
||
|
echo "</td>\n";
|
||
|
} else {
|
||
|
generateEmptyTd(4);
|
||
|
}
|
||
|
echo " </tr>\n";
|
||
|
}
|
||
|
}
|
||
|
?>
|
||
|
<html>
|
||
|
|
||
|
<head>
|
||
|
|
||
|
<title><?php echo xlt('Drug Inventory'); ?></title>
|
||
|
|
||
|
<style>
|
||
|
a, a:visited, a:hover {
|
||
|
color: var(--primary);
|
||
|
}
|
||
|
#mymaintable thead .sorting::before,
|
||
|
#mymaintable thead .sorting_asc::before,
|
||
|
#mymaintable thead .sorting_asc::after,
|
||
|
#mymaintable thead .sorting_desc::before,
|
||
|
#mymaintable thead .sorting_desc::after,
|
||
|
#mymaintable thead .sorting::after {
|
||
|
display: none;
|
||
|
}
|
||
|
|
||
|
.dataTables_wrapper .dataTables_paginate .paginate_button {
|
||
|
padding: 0 !important;
|
||
|
margin: 0 !important;
|
||
|
border: 0 !important;
|
||
|
}
|
||
|
|
||
|
.paginate_button:hover {
|
||
|
background: transparent !important;
|
||
|
}
|
||
|
|
||
|
</style>
|
||
|
|
||
|
<?php Header::setupHeader(['datatables', 'datatables-dt', 'datatables-bs', 'report-helper']); ?>
|
||
|
|
||
|
<script>
|
||
|
|
||
|
// callback from add_edit_drug.php or add_edit_drug_inventory.php:
|
||
|
function refreshme() {
|
||
|
// Avoiding reload() here because it generates a browser warning about repeating a POST.
|
||
|
location.href = location.href;
|
||
|
}
|
||
|
|
||
|
// Process click on drug title.
|
||
|
function dodclick(id) {
|
||
|
dlgopen('add_edit_drug.php?drug=' + id, '_blank', 900, 600);
|
||
|
}
|
||
|
|
||
|
// Process click on drug QOO or lot.
|
||
|
function doiclick(id, lot) {
|
||
|
dlgopen('add_edit_lot.php?drug=' + id + '&lot=' + lot, '_blank', 600, 475);
|
||
|
}
|
||
|
|
||
|
// Enable/disable warehouse options depending on current facility.
|
||
|
function facchanged() {
|
||
|
var f = document.forms[0];
|
||
|
var facid = f.form_facility.value;
|
||
|
var theopts = f.form_warehouse.options;
|
||
|
for (var i = 1; i < theopts.length; ++i) {
|
||
|
var tmp = theopts[i].value.split('/');
|
||
|
var dis = facid && (tmp.length < 2 || tmp[1] != facid);
|
||
|
theopts[i].disabled = dis;
|
||
|
if (dis) {
|
||
|
theopts[i].selected = false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$(function () {
|
||
|
$('#mymaintable').DataTable({
|
||
|
stripeClasses:['stripe1','stripe2'],
|
||
|
orderClasses: false,
|
||
|
<?php // Bring in the translations ?>
|
||
|
<?php require($GLOBALS['srcdir'] . '/js/xl/datatables-net.js.php'); ?>
|
||
|
});
|
||
|
});
|
||
|
</script>
|
||
|
|
||
|
</head>
|
||
|
|
||
|
<body class="body_top">
|
||
|
<form method='post' action='drug_inventory.php' onsubmit='return top.restoreSession()'>
|
||
|
|
||
|
<table border='0' cellpadding='3' width='100%'>
|
||
|
<tr>
|
||
|
<td>
|
||
|
<b><?php echo xlt('Inventory Management'); ?></b>
|
||
|
</td>
|
||
|
<td align='right'>
|
||
|
<?php
|
||
|
// Build a drop-down list of facilities.
|
||
|
$query = "SELECT id, name FROM facility ORDER BY name";
|
||
|
$fres = sqlStatement($query);
|
||
|
echo " <select name='form_facility' onchange='facchanged()'>\n";
|
||
|
echo " <option value=''>-- " . xlt('All Facilities') . " --\n";
|
||
|
while ($frow = sqlFetchArray($fres)) {
|
||
|
$facid = $frow['id'];
|
||
|
if ($is_user_restricted && !isFacilityAllowed($facid)) {
|
||
|
continue;
|
||
|
}
|
||
|
echo " <option value='" . attr($facid) . "'";
|
||
|
if ($facid == $form_facility) {
|
||
|
echo " selected";
|
||
|
}
|
||
|
echo ">" . text($frow['name']) . "\n";
|
||
|
}
|
||
|
echo " </select>\n";
|
||
|
|
||
|
// Build a drop-down list of warehouses.
|
||
|
echo " ";
|
||
|
echo " <select name='form_warehouse'>\n";
|
||
|
echo " <option value=''>" . xlt('All Warehouses') . "</option>\n";
|
||
|
$lres = sqlStatement(
|
||
|
"SELECT * FROM list_options " .
|
||
|
"WHERE list_id = 'warehouse' ORDER BY seq, title"
|
||
|
);
|
||
|
while ($lrow = sqlFetchArray($lres)) {
|
||
|
$whid = $lrow['option_id'];
|
||
|
$facid = $lrow['option_value'];
|
||
|
if ($is_user_restricted && !isWarehouseAllowed($facid, $whid)) {
|
||
|
continue;
|
||
|
}
|
||
|
echo " <option value='" . attr("$whid/$facid") . "'";
|
||
|
echo " id='fac" . attr($facid) . "'";
|
||
|
if (strlen($form_warehouse) > 0 && $whid == $form_warehouse) {
|
||
|
echo " selected";
|
||
|
}
|
||
|
echo ">" . text(xl_list_label($lrow['title'])) . "</option>\n";
|
||
|
}
|
||
|
echo " </select>\n";
|
||
|
?>
|
||
|
|
||
|
<select name='form_consumable'>
|
||
|
<?php
|
||
|
foreach (
|
||
|
array(
|
||
|
'0' => xl('All Product Types'),
|
||
|
'1' => xl('Consumable Only'),
|
||
|
'2' => xl('Non-Consumable Only'),
|
||
|
) as $key => $value
|
||
|
) {
|
||
|
echo " <option value='" . attr($key) . "'";
|
||
|
if ($key == $form_consumable) {
|
||
|
echo " selected";
|
||
|
}
|
||
|
echo ">" . text($value) . "</option>\n";
|
||
|
}
|
||
|
?>
|
||
|
</select>
|
||
|
</td>
|
||
|
<td>
|
||
|
<input type='checkbox' name='form_show_empty' value='1'<?php if ($form_show_empty) {
|
||
|
echo " checked";} ?> />
|
||
|
<?php echo xlt('Show empty lots'); ?><br />
|
||
|
<input type='checkbox' name='form_show_inactive' value='1'<?php if ($form_show_inactive) {
|
||
|
echo " checked";} ?> />
|
||
|
<?php echo xlt('Show inactive'); ?>
|
||
|
</td>
|
||
|
<td>
|
||
|
<input type='submit' name='form_refresh' value="<?php echo xla('Refresh'); ?>" />
|
||
|
</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td height="1">
|
||
|
</td>
|
||
|
</tr>
|
||
|
</table>
|
||
|
|
||
|
<!-- TODO: Why are we not using the BS4 table class here? !-->
|
||
|
<table id='mymaintable' class="display table-striped">
|
||
|
<thead>
|
||
|
<tr class='head'>
|
||
|
<th>
|
||
|
<?php echo xlt('Name'); ?> </a>
|
||
|
</th>
|
||
|
<th>
|
||
|
<?php echo xlt('Act'); ?>
|
||
|
</th>
|
||
|
<th>
|
||
|
<?php echo xlt('Cons'); ?>
|
||
|
</th>
|
||
|
<th>
|
||
|
<?php echo xlt('NDC'); ?> </a>
|
||
|
</th>
|
||
|
<th>
|
||
|
<?php echo xlt('Form'); ?> </a>
|
||
|
</th>
|
||
|
<th>
|
||
|
<?php echo xlt('Size'); ?>
|
||
|
</th>
|
||
|
<th title='<?php echo xlt('Measurement Units'); ?>'>
|
||
|
<?php echo xlt('Unit'); ?>
|
||
|
</th>
|
||
|
<th title='<?php echo xla('Purchase or Transfer'); ?>'>
|
||
|
<?php echo xlt('Tran'); ?>
|
||
|
</th>
|
||
|
<th>
|
||
|
<?php echo xlt('Lot'); ?> </a>
|
||
|
</th>
|
||
|
<th>
|
||
|
<?php echo xlt('Facility'); ?> </a>
|
||
|
</th>
|
||
|
<th>
|
||
|
<?php echo xlt('Warehouse'); ?> </a>
|
||
|
</th>
|
||
|
<th>
|
||
|
<?php echo xlt('QOH'); ?> </a>
|
||
|
</th>
|
||
|
<th>
|
||
|
<?php echo xlt('Expires'); ?> </a>
|
||
|
</th>
|
||
|
</tr>
|
||
|
</thead>
|
||
|
<tbody>
|
||
|
<?php
|
||
|
$prevRow = '';
|
||
|
while ($row = sqlFetchArray($res)) {
|
||
|
if (!empty($row['inventory_id']) && $is_user_restricted && !isWarehouseAllowed($row['facid'], $row['warehouse_id'])) {
|
||
|
continue;
|
||
|
}
|
||
|
$row = processData($row);
|
||
|
if ($prevRow == '') {
|
||
|
$prevRow = $row;
|
||
|
continue;
|
||
|
}
|
||
|
if ($prevRow['drug_id'] == $row['drug_id']) {
|
||
|
$row = mergeData($prevRow, $row);
|
||
|
} else {
|
||
|
mapToTable($prevRow);
|
||
|
}
|
||
|
$prevRow = $row;
|
||
|
} // end while
|
||
|
mapToTable($prevRow);
|
||
|
?>
|
||
|
</tbody>
|
||
|
</table>
|
||
|
|
||
|
<input class="btn btn-primary btn-block w-25 mx-auto" type='button' value='<?php echo xla('Add Drug'); ?>' onclick='dodclick(0)' />
|
||
|
|
||
|
<input type="hidden" name="form_orderby" value="<?php echo attr($form_orderby) ?>" />
|
||
|
|
||
|
</form>
|
||
|
|
||
|
<script>
|
||
|
facchanged();
|
||
|
</script>
|
||
|
|
||
|
</body>
|
||
|
</html>
|