3 // Copyright (C) 2006-2021 Rod Roark <rod@sunsetsystems.com>
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 require_once("../globals.php");
11 require_once("drugs.inc.php");
12 require_once("$srcdir/options.inc.php");
14 use OpenEMR\Common\Acl\AclMain
;
15 use OpenEMR\Core\Header
;
17 // Check authorizations.
18 $auth_admin = AclMain
::aclCheckCore('admin', 'drugs');
19 $auth_lots = $auth_admin ||
20 AclMain
::aclCheckCore('inventory', 'lots') ||
21 AclMain
::aclCheckCore('inventory', 'purchases') ||
22 AclMain
::aclCheckCore('inventory', 'transfers') ||
23 AclMain
::aclCheckCore('inventory', 'adjustments') ||
24 AclMain
::aclCheckCore('inventory', 'consumption') ||
25 AclMain
::aclCheckCore('inventory', 'destruction');
26 $auth_anything = $auth_lots ||
27 AclMain
::aclCheckCore('inventory', 'sales') ||
28 AclMain
::aclCheckCore('inventory', 'reporting');
29 if (!$auth_anything) {
30 die(xlt('Not authorized'));
32 // Note if user is restricted to any facilities and/or warehouses.
33 $is_user_restricted = isUserRestricted();
35 // For each sorting option, specify the ORDER BY argument.
38 'prod' => 'd.name, d.drug_id, di.expiration, di.lot_number',
39 'act' => 'd.active, d.name, d.drug_id, di.expiration, di.lot_number',
40 'ndc' => 'd.ndc_number, d.name, d.drug_id, di.expiration, di.lot_number',
41 'con' => 'd.consumable, d.name, d.drug_id, di.expiration, di.lot_number',
42 'form' => 'lof.title, d.name, d.drug_id, di.expiration, di.lot_number',
43 'lot' => 'di.lot_number, d.name, d.drug_id, di.expiration',
44 'wh' => 'lo.title, d.name, d.drug_id, di.expiration, di.lot_number',
45 'fac' => 'f.name, d.name, d.drug_id, di.expiration, di.lot_number',
46 'qoh' => 'di.on_hand, d.name, d.drug_id, di.expiration, di.lot_number',
47 'exp' => 'di.expiration, d.name, d.drug_id, di.lot_number',
50 $form_facility = 0 +
empty($_REQUEST['form_facility']) ?
0 : $_REQUEST['form_facility'];
51 $form_show_empty = empty($_REQUEST['form_show_empty']) ?
0 : 1;
52 $form_show_inactive = empty($_REQUEST['form_show_inactive']) ?
0 : 1;
53 $form_consumable = isset($_REQUEST['form_consumable']) ?
intval($_REQUEST['form_consumable']) : 0;
55 // Incoming form_warehouse, if not empty is in the form "warehouse/facility".
56 // The facility part is an attribute used by JavaScript logic.
57 $form_warehouse = empty($_REQUEST['form_warehouse']) ?
'' : $_REQUEST['form_warehouse'];
58 $tmp = explode('/', $form_warehouse);
59 $form_warehouse = $tmp[0];
61 // Get the order hash array value and key for this request.
62 $form_orderby = isset($ORDERHASH[$_REQUEST['form_orderby'] ??
'']) ?
$_REQUEST['form_orderby'] : 'prod';
63 $orderby = $ORDERHASH[$form_orderby];
66 $where = "WHERE 1 = 1";
68 $where .= " AND lo.option_value IS NOT NULL AND lo.option_value = ?";
69 $binds[] = $form_facility;
71 if ($form_warehouse) {
72 $where .= " AND di.warehouse_id IS NOT NULL AND di.warehouse_id = ?";
73 $binds[] = $form_warehouse;
75 if (!$form_show_inactive) {
76 $where .= " AND d.active = 1";
78 if ($form_consumable) {
79 if ($form_consumable == 1) {
80 $where .= " AND d.consumable = '1'";
82 $where .= " AND d.consumable != '1'";
86 $dion = $form_show_empty ?
"" : "AND di.on_hand != 0";
91 "di.inventory_id, di.lot_number, di.expiration, di.manufacturer, di.on_hand, " .
92 "di.warehouse_id, lo.title, lo.option_value AS facid, f.name AS facname " .
94 "LEFT JOIN drug_inventory AS di ON di.drug_id = d.drug_id " .
95 "AND di.destroy_date IS NULL $dion " .
96 "LEFT JOIN list_options AS lo ON lo.list_id = 'warehouse' AND " .
97 "lo.option_id = di.warehouse_id AND lo.activity = 1 " .
98 "LEFT JOIN facility AS f ON f.id = lo.option_value " .
99 "LEFT JOIN list_options AS lof ON lof.list_id = 'drug_form' AND " .
100 "lof.option_id = d.form AND lof.activity = 1 " .
101 "$where ORDER BY d.active DESC, $orderby",
105 function generateEmptyTd($n)
109 $temp .= "<td></td>";
114 function processData($data)
116 $data['inventory_id'] = [$data['inventory_id']];
117 $data['lot_number'] = [$data['lot_number']];
118 $data['facname'] = [$data['facname']];
119 $data['title'] = [$data['title']];
120 $data['on_hand'] = [$data['on_hand']];
121 $data['expiration'] = [$data['expiration']];
124 function mergeData($d1, $d2)
126 $d1['inventory_id'] = array_merge($d1['inventory_id'], $d2['inventory_id']);
127 $d1['lot_number'] = array_merge($d1['lot_number'], $d2['lot_number']);
128 $d1['facname'] = array_merge($d1['facname'], $d2['facname']);
129 $d1['title'] = array_merge($d1['title'], $d2['title']);
130 $d1['on_hand'] = array_merge($d1['on_hand'], $d2['on_hand']);
131 $d1['expiration'] = array_merge($d1['expiration'], $d2['expiration']);
134 function mapToTable($row)
136 global $auth_admin, $auth_lots;
137 $today = date('Y-m-d');
139 echo " <tr class='detail'>\n";
140 $lastid = $row['drug_id'];
142 echo "<td title='" . xla('Click to edit') . "' onclick='dodclick(" . attr(addslashes($lastid)) . ")'>" .
143 "<a href='' onclick='return false'>" .
144 text($row['name']) . "</a></td>\n";
146 echo " <td>" . text($row['name']) . "</td>\n";
148 echo " <td>" . ($row['active'] ?
xlt('Yes') : xlt('No')) . "</td>\n";
149 echo " <td>" . ($row['consumable'] ?
xlt('Yes') : xlt('No')) . "</td>\n";
150 echo " <td>" . text($row['ndc_number']) . "</td>\n";
152 generate_display_field(array('data_type' => '1','list_id' => 'drug_form'), $row['form']) .
154 echo " <td>" . text($row['size']) . "</td>\n";
155 echo " <td title='" . xla('Measurement Units') . "'>" .
156 generate_display_field(array('data_type' => '1','list_id' => 'drug_units'), $row['unit']) .
159 if ($auth_lots && $row['dispensable']) {
160 echo " <td onclick='doiclick(" . intval($lastid) . ",0)' title='" .
161 xla('Purchase or Transfer') . "' style='padding:0'>" .
162 "<input type='button' value='" . xla('Tran') . "'style='padding:0' /></td>\n";
164 echo " <td title='" . xla('Not applicable') . "'> </td>\n";
167 if (!empty($row['inventory_id'][0])) {
169 foreach ($row['inventory_id'] as $key => $value) {
171 echo "<div title='" .
172 xla('Adjustment, Consumption, Return, or Edit') .
173 "' onclick='doiclick(" . intval($lastid) . "," .
174 intval($row['inventory_id'][$key]) . ")'>" .
175 "<a href='' onclick='return false'>" .
176 text($row['lot_number'][$key]) .
179 echo " <div>" . text($row['lot_number'][$key]) . "</div>\n";
184 foreach ($row['facname'] as $value) {
185 $value = $value != null ?
$value : "N/A";
186 echo "<div >" . text($value) . "</div>";
190 foreach ($row['title'] as $value) {
191 $value = $value != null ?
$value : "N/A";
192 echo "<div >" . text($value) . "</div>";
196 foreach ($row['on_hand'] as $value) {
197 $value = $value != null ?
$value : "N/A";
198 echo "<div >" . text($value) . "</div>";
202 foreach ($row['expiration'] as $value) {
203 // Make the expiration date red if expired.
204 $expired = !empty($value) && strcmp($value, $today) <= 0;
205 $value = !empty($value) ?
oeFormatShortDate($value) : xl('N/A');
206 echo "<div" . ($expired ?
" style='color:red'" : "") . ">" . text($value) . "</div>";
220 <title
><?php
echo xlt('Drug Inventory'); ?
></title
>
223 a
, a
:visited
, a
:hover
{
224 color
: var(--primary
);
226 #mymaintable thead .sorting::before,
227 #mymaintable thead .sorting_asc::before,
228 #mymaintable thead .sorting_asc::after,
229 #mymaintable thead .sorting_desc::before,
230 #mymaintable thead .sorting_desc::after,
231 #mymaintable thead .sorting::after {
235 .dataTables_wrapper
.dataTables_paginate
.paginate_button
{
236 padding
: 0 !important
;
237 margin
: 0 !important
;
238 border
: 0 !important
;
241 .paginate_button
:hover
{
242 background
: transparent
!important
;
247 <?php Header
::setupHeader(['datatables', 'datatables-dt', 'datatables-bs', 'report-helper']); ?
>
251 // callback from add_edit_drug.php or add_edit_drug_inventory.php:
252 function refreshme() {
253 // Avoiding reload() here because it generates a browser warning about repeating a POST.
254 location
.href
= location
.href
;
257 // Process click on drug title.
258 function dodclick(id
) {
259 dlgopen('add_edit_drug.php?drug=' + id
, '_blank', 900, 600);
262 // Process click on drug QOO or lot.
263 function doiclick(id
, lot
) {
264 dlgopen('add_edit_lot.php?drug=' + id +
'&lot=' + lot
, '_blank', 600, 475);
267 // Enable/disable warehouse options depending on current facility.
268 function facchanged() {
269 var f
= document
.forms
[0];
270 var facid
= f
.form_facility
.value
;
271 var theopts
= f
.form_warehouse
.options
;
272 for (var i
= 1; i
< theopts
.length
; ++i
) {
273 var tmp
= theopts
[i
].value
.split('/');
274 var dis
= facid
&& (tmp
.length
< 2 || tmp
[1] != facid
);
275 theopts
[i
].disabled
= dis
;
277 theopts
[i
].selected
= false;
283 $
('#mymaintable').DataTable({
284 stripeClasses
:['stripe1','stripe2'],
286 <?php
// Bring in the translations ?>
287 <?php
require($GLOBALS['srcdir'] . '/js/xl/datatables-net.js.php'); ?
>
294 <body
class="body_top">
295 <form method
='post' action
='drug_inventory.php' onsubmit
='return top.restoreSession()'>
297 <table border
='0' cellpadding
='3' width
='100%'>
300 <b
><?php
echo xlt('Inventory Management'); ?
></b
>
304 // Build a drop-down list of facilities.
305 $query = "SELECT id, name FROM facility ORDER BY name";
306 $fres = sqlStatement($query);
307 echo " <select name='form_facility' onchange='facchanged()'>\n";
308 echo " <option value=''>-- " . xlt('All Facilities') . " --\n";
309 while ($frow = sqlFetchArray($fres)) {
310 $facid = $frow['id'];
311 if ($is_user_restricted && !isFacilityAllowed($facid)) {
314 echo " <option value='" . attr($facid) . "'";
315 if ($facid == $form_facility) {
318 echo ">" . text($frow['name']) . "\n";
322 // Build a drop-down list of warehouses.
324 echo " <select name='form_warehouse'>\n";
325 echo " <option value=''>" . xlt('All Warehouses') . "</option>\n";
326 $lres = sqlStatement(
327 "SELECT * FROM list_options " .
328 "WHERE list_id = 'warehouse' ORDER BY seq, title"
330 while ($lrow = sqlFetchArray($lres)) {
331 $whid = $lrow['option_id'];
332 $facid = $lrow['option_value'];
333 if ($is_user_restricted && !isWarehouseAllowed($facid, $whid)) {
336 echo " <option value='" . attr("$whid/$facid") . "'";
337 echo " id='fac" . attr($facid) . "'";
338 if (strlen($form_warehouse) > 0 && $whid == $form_warehouse) {
341 echo ">" . text(xl_list_label($lrow['title'])) . "</option>\n";
346 <select name
='form_consumable'>
350 '0' => xl('All Product Types'),
351 '1' => xl('Consumable Only'),
352 '2' => xl('Non-Consumable Only'),
355 echo " <option value='" . attr($key) . "'";
356 if ($key == $form_consumable) {
359 echo ">" . text($value) . "</option>\n";
365 <input type
='checkbox' name
='form_show_empty' value
='1'<?php
if ($form_show_empty) {
366 echo " checked";} ?
> />
367 <?php
echo xl('Show empty lots'); ?
><br
/>
368 <input type
='checkbox' name
='form_show_inactive' value
='1'<?php
if ($form_show_inactive) {
369 echo " checked";} ?
> />
370 <?php
echo xl('Show inactive'); ?
>
373 <input type
='submit' name
='form_refresh' value
="<?php echo xla('Refresh'); ?>" />
382 <!-- TODO
: Why are we not using the BS4 table
class here?
!-->
383 <table id
='mymaintable' class="display table-striped">
387 <?php
echo xlt('Name'); ?
> </a
>
390 <?php
echo xlt('Act'); ?
>
393 <?php
echo xlt('Cons'); ?
>
396 <?php
echo xlt('NDC'); ?
> </a
>
399 <?php
echo xlt('Form'); ?
> </a
>
402 <?php
echo xlt('Size'); ?
>
404 <th title
='<?php echo xlt('Measurement Units
'); ?>'>
405 <?php
echo xlt('Unit'); ?
>
407 <th title
='<?php echo xla('Purchase
or Transfer
'); ?>'>
408 <?php
echo xlt('Tran'); ?
>
411 <?php
echo xlt('Lot'); ?
> </a
>
414 <?php
echo xlt('Facility'); ?
> </a
>
417 <?php
echo xlt('Warehouse'); ?
> </a
>
420 <?php
echo xlt('QOH'); ?
> </a
>
423 <?php
echo xlt('Expires'); ?
> </a
>
430 while ($row = sqlFetchArray($res)) {
431 if (!empty($row['inventory_id']) && $is_user_restricted && !isWarehouseAllowed($row['facid'], $row['warehouse_id'])) {
434 $row = processData($row);
435 if ($prevRow == '') {
439 if ($prevRow['drug_id'] == $row['drug_id']) {
440 $row = mergeData($prevRow, $row);
442 mapToTable($prevRow);
446 mapToTable($prevRow);
451 <input
class="btn btn-primary btn-block w-25 mx-auto" type
='button' value
='<?php echo xla('Add Drug
'); ?>' onclick
='dodclick(0)' />
453 <input type
="hidden" name
="form_orderby" value
="<?php echo attr($form_orderby) ?>" />