bye jquery 1.11.1 (#849)
[openemr.git] / interface / drugs / drugs.inc.php
blob5c95250a21f7048c422a63804ff86d8e73d847f3
1 <?php
2 // Copyright (C) 2006-2016 Rod Roark <rod@sunsetsystems.com>
3 //
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License
6 // as published by the Free Software Foundation; either version 2
7 // of the License, or (at your option) any later version.
8 //
9 // Modified 7-2009 by BM in order to migrate using the form,
10 // unit, route, and interval lists with the
11 // functions in openemr/library/options.inc.php .
12 // These lists are based on the constants found in the
13 // openemr/library/classes/Prescription.class.php file.
15 // Decision was made in June 2013 that a sale line item in the Fee Sheet may
16 // come only from the specified warehouse. Set this to false if the decision
17 // is reversed.
18 $GLOBALS['SELL_FROM_ONE_WAREHOUSE'] = true;
20 $substitute_array = array('', xl('Allowed'), xl('Not Allowed'));
22 function send_drug_email($subject, $body) {
23 $recipient = $GLOBALS['practice_return_email_path'];
24 if (empty($recipient)) return;
25 $mail = new PHPMailer();
26 $mail->From = $recipient;
27 $mail->FromName = 'In-House Pharmacy';
28 $mail->isMail();
29 $mail->Host = "localhost";
30 $mail->Mailer = "mail";
31 $mail->Body = $body;
32 $mail->Subject = $subject;
33 $mail->AddAddress($recipient);
34 if(!$mail->Send()) {
35 error_log("There has been a mail error sending to " . $recipient .
36 " " . $mail->ErrorInfo);
40 function sellDrug($drug_id, $quantity, $fee, $patient_id=0, $encounter_id=0,
41 $prescription_id=0, $sale_date='', $user='', $default_warehouse='',
42 $testonly=false, &$expiredlots=null, $pricelevel='', $selector='') {
44 if (empty($patient_id)) $patient_id = $GLOBALS['pid'];
45 if (empty($sale_date)) $sale_date = date('Y-m-d');
46 if (empty($user)) $user = $_SESSION['authUser'];
48 // error_log("quantity = '$quantity'"); // debugging
50 if (empty($default_warehouse)) {
51 // Get the default warehouse, if any, for the user.
52 $rowuser = sqlQuery("SELECT default_warehouse FROM users WHERE username = ?", array($user));
53 $default_warehouse = $rowuser['default_warehouse'];
56 // Get relevant options for this product.
57 $rowdrug = sqlQuery("SELECT allow_combining, reorder_point, name, dispensable " .
58 "FROM drugs WHERE drug_id = ?", array($drug_id));
59 $allow_combining = $rowdrug['allow_combining'];
60 $dispensable = $rowdrug['dispensable'];
62 if (!$dispensable) {
63 // Non-dispensable is a much simpler case and does not touch inventory.
64 if ($testonly) return true;
65 $sale_id = sqlInsert("INSERT INTO drug_sales ( " .
66 "drug_id, inventory_id, prescription_id, pid, encounter, user, " .
67 "sale_date, quantity, fee ) VALUES ( " .
68 "?, 0, ?, ?, ?, ?, ?, ?, ?)",
69 array($drug_id, $prescription_id, $patient_id, $encounter_id, $user, $sale_date, $quantity, $fee));
70 return $sale_id;
73 // Combining is never allowed for prescriptions and will not work with
74 // dispense_drug.php.
75 if ($prescription_id) $allow_combining = 0;
77 $rows = array();
78 // $firstrow = false;
79 $qty_left = $quantity;
80 $bad_lot_list = '';
81 $total_on_hand = 0;
82 $gotexpired = false;
84 // If the user has a default warehouse, sort those lots first.
85 $orderby = ($default_warehouse === '') ?
86 "" : "di.warehouse_id != '$default_warehouse', ";
87 $orderby .= "lo.seq, di.expiration, di.lot_number, di.inventory_id";
89 // Retrieve lots in order of expiration date within warehouse preference.
90 $query = "SELECT di.*, lo.option_id, lo.seq " .
91 "FROM drug_inventory AS di " .
92 "LEFT JOIN list_options AS lo ON lo.list_id = 'warehouse' AND " .
93 "lo.option_id = di.warehouse_id AND lo.activity = 1 " .
94 "WHERE " .
95 "di.drug_id = ? AND di.destroy_date IS NULL ";
96 $sqlarr = array($drug_id);
97 if ($GLOBALS['SELL_FROM_ONE_WAREHOUSE'] && $default_warehouse) {
98 $query .= "AND di.warehouse_id = ? ";
99 $sqlarr[] = $default_warehouse;
101 $query .= "ORDER BY $orderby";
102 $res = sqlStatement($query, $sqlarr);
104 // First pass. Pick out lots to be used in filling this order, figure out
105 // if there is enough quantity on hand and check for lots to be destroyed.
106 while ($row = sqlFetchArray($res)) {
107 if ($row['warehouse_id'] != $default_warehouse) {
108 // Warehouses with seq > 99 are not available.
109 $seq = empty($row['seq']) ? 0 : $row['seq'] + 0;
110 if ($seq > 99) continue;
112 $on_hand = $row['on_hand'];
113 $expired = (!empty($row['expiration']) && $row['expiration'] <= $sale_date);
114 if ($expired || $on_hand < $quantity) {
115 $tmp = $row['lot_number'];
116 if (! $tmp) $tmp = '[missing lot number]';
117 if ($bad_lot_list) $bad_lot_list .= ', ';
118 $bad_lot_list .= $tmp;
120 if ($expired) {
121 $gotexpired = true;
122 continue;
125 /*****************************************************************
126 // Note the first row in case total quantity is insufficient and we are
127 // allowed to go negative.
128 if (!$firstrow) $firstrow = $row;
129 *****************************************************************/
131 $total_on_hand += $on_hand;
133 if ($on_hand > 0 && $qty_left > 0 && ($allow_combining || $on_hand >= $qty_left)) {
134 $rows[] = $row;
135 $qty_left -= $on_hand;
139 if ($expiredlots !== null) $expiredlots = $gotexpired;
141 if ($testonly) {
142 // Just testing inventory, so return true if OK, false if insufficient.
143 // $qty_left, if positive, is the amount requested that could not be allocated.
144 return $qty_left <= 0;
147 if ($bad_lot_list) {
148 send_drug_email("Possible lot destruction needed",
149 "The following lot(s) are expired or were too small to fill the " .
150 "order for patient $patient_id: $bad_lot_list\n");
153 /*******************************************************************
154 if (empty($firstrow)) return 0; // no suitable lots exist
155 // This can happen when combining is not allowed. We will use the
156 // first row and take it negative.
157 if (empty($rows)) {
158 $rows[] = $firstrow;
159 $qty_left -= $firstrow['on_hand'];
161 *******************************************************************/
163 // The above was an experiment in permitting a negative lot quantity.
164 // We decided that was a bad idea, so now we just error out if there
165 // is not enough on hand.
166 if ($qty_left > 0) return 0;
168 $sale_id = 0;
169 $qty_final = $quantity; // remaining unallocated quantity
170 $fee_final = $fee; // remaining unallocated fee
172 // Second pass. Update the database.
173 foreach ($rows as $row) {
174 $inventory_id = $row['inventory_id'];
176 /*****************************************************************
177 $thisqty = $row['on_hand'];
178 if ($qty_left > 0) {
179 $thisqty += $qty_left;
180 $qty_left = 0;
182 else if ($thisqty > $qty_final) {
183 $thisqty = $qty_final;
185 *****************************************************************/
186 $thisqty = min($qty_final, $row['on_hand']);
188 $qty_final -= $thisqty;
190 // Compute the proportional fee for this line item. For the last line
191 // item take the remaining unallocated fee to avoid round-off error.
192 if ($qty_final)
193 $thisfee = sprintf('%0.2f', $fee * $thisqty / $quantity);
194 else
195 $thisfee = sprintf('%0.2f', $fee_final);
196 $fee_final -= $thisfee;
198 // Update inventory and create the sale line item.
199 sqlStatement("UPDATE drug_inventory SET " .
200 "on_hand = on_hand - ? " .
201 "WHERE inventory_id = ?", array($thisqty,$inventory_id));
202 $sale_id = sqlInsert("INSERT INTO drug_sales ( " .
203 "drug_id, inventory_id, prescription_id, pid, encounter, user, sale_date, quantity, fee, pricelevel, selector ) " .
204 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
205 array($drug_id, $inventory_id, $prescription_id, $patient_id, $encounter_id, $user,
206 $sale_date, $thisqty, $thisfee, $pricelevel, $selector));
208 // If this sale exhausted the lot then auto-destroy it if that is wanted.
209 if ($row['on_hand'] == $thisqty && !empty($GLOBALS['gbl_auto_destroy_lots'])) {
210 sqlStatement("UPDATE drug_inventory SET " .
211 "destroy_date = ?, destroy_method = ?, destroy_witness = ?, destroy_notes = ? " .
212 "WHERE drug_id = ? AND inventory_id = ?",
213 array($sale_date, xl('Automatic from sale'), $user, "sale_id = $sale_id",
214 $drug_id, $inventory_id));
218 /*******************************************************************
219 // If appropriate, generate email to notify that re-order is due.
220 if (($total_on_hand - $quantity) <= $rowdrug['reorder_point']) {
221 send_drug_email("Product re-order required",
222 "Product '" . $rowdrug['name'] . "' has reached its reorder point.\n");
224 // TBD: If the above is un-commented, fix it to handle the case of
225 // $GLOBALS['gbl_min_max_months'] being true.
226 *******************************************************************/
228 // If combining is allowed then $sale_id will be just the last inserted ID,
229 // and it serves only to indicate that everything worked. Otherwise there
230 // can be only one inserted row and this is its ID.
231 return $sale_id;
234 // Determine if facility and warehouse restrictions are applicable for this user.
235 function isUserRestricted($userid=0) {
236 if (!$userid) $userid = $_SESSION['authId'];
237 $countrow = sqlQuery("SELECT count(*) AS count FROM users_facility WHERE " .
238 "tablename = 'users' AND table_id = ?", array($userid));
239 return !empty($countrow['count']);
242 // Check if the user has access to the given facility.
243 // Do not call this if user is not restricted!
244 function isFacilityAllowed($facid, $userid=0) {
245 if (!$userid) $userid = $_SESSION['authId'];
246 $countrow = sqlQuery("SELECT count(*) AS count FROM users_facility WHERE " .
247 "tablename = 'users' AND table_id = ? AND facility_id = ?",
248 array($userid, $facid));
249 if (empty($countrow['count'])) {
250 $countrow = sqlQuery("SELECT count(*) AS count FROM users WHERE " .
251 "id = ? AND facility_id = ?",
252 array($userid, $facid));
253 return !empty($countrow['count']);
255 return true;
258 // Check if the user has access to the given warehouse within the given facility.
259 // Do not call this if user is not restricted!
260 function isWarehouseAllowed($facid, $whid, $userid=0) {
261 if (!$userid) $userid = $_SESSION['authId'];
262 $countrow = sqlQuery("SELECT count(*) AS count FROM users_facility WHERE " .
263 "tablename = 'users' AND table_id = ? AND facility_id = ? AND " .
264 "(warehouse_id = ? OR warehouse_id = '')",
265 array($userid, $facid, $whid));
266 if (empty($countrow['count'])) {
267 $countrow = sqlQuery("SELECT count(*) AS count FROM users WHERE " .
268 "id = ? AND default_warehouse = ?",
269 array($userid, $whid));
270 return !empty($countrow['count']);
272 return true;