phpmailer new version bug fix
[openemr.git] / interface / drugs / drugs.inc.php
blobaba5cfe3f1c496f68051becea41a339ab9cd671c
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 use PHPMailer\PHPMailer\PHPMailer;
17 // Decision was made in June 2013 that a sale line item in the Fee Sheet may
18 // come only from the specified warehouse. Set this to false if the decision
19 // is reversed.
20 $GLOBALS['SELL_FROM_ONE_WAREHOUSE'] = true;
22 $substitute_array = array('', xl('Allowed'), xl('Not Allowed'));
24 function send_drug_email($subject, $body)
26 $recipient = $GLOBALS['practice_return_email_path'];
27 if (empty($recipient)) {
28 return;
31 $mail = new PHPMailer();
32 $mail->From = $recipient;
33 $mail->FromName = 'In-House Pharmacy';
34 $mail->isMail();
35 $mail->Host = "localhost";
36 $mail->Mailer = "mail";
37 $mail->Body = $body;
38 $mail->Subject = $subject;
39 $mail->AddAddress($recipient);
40 if (!$mail->Send()) {
41 error_log("There has been a mail error sending to " . $recipient .
42 " " . $mail->ErrorInfo);
46 function sellDrug(
47 $drug_id,
48 $quantity,
49 $fee,
50 $patient_id = 0,
51 $encounter_id = 0,
52 $prescription_id = 0,
53 $sale_date = '',
54 $user = '',
55 $default_warehouse = '',
56 $testonly = false,
57 &$expiredlots = null,
58 $pricelevel = '',
59 $selector = ''
60 ) {
62 if (empty($patient_id)) {
63 $patient_id = $GLOBALS['pid'];
66 if (empty($sale_date)) {
67 $sale_date = date('Y-m-d');
70 if (empty($user)) {
71 $user = $_SESSION['authUser'];
74 // error_log("quantity = '$quantity'"); // debugging
76 if (empty($default_warehouse)) {
77 // Get the default warehouse, if any, for the user.
78 $rowuser = sqlQuery("SELECT default_warehouse FROM users WHERE username = ?", array($user));
79 $default_warehouse = $rowuser['default_warehouse'];
82 // Get relevant options for this product.
83 $rowdrug = sqlQuery("SELECT allow_combining, reorder_point, name, dispensable " .
84 "FROM drugs WHERE drug_id = ?", array($drug_id));
85 $allow_combining = $rowdrug['allow_combining'];
86 $dispensable = $rowdrug['dispensable'];
88 if (!$dispensable) {
89 // Non-dispensable is a much simpler case and does not touch inventory.
90 if ($testonly) {
91 return true;
94 $sale_id = sqlInsert(
95 "INSERT INTO drug_sales ( " .
96 "drug_id, inventory_id, prescription_id, pid, encounter, user, " .
97 "sale_date, quantity, fee ) VALUES ( " .
98 "?, 0, ?, ?, ?, ?, ?, ?, ?)",
99 array($drug_id, $prescription_id, $patient_id, $encounter_id, $user, $sale_date, $quantity, $fee)
101 return $sale_id;
104 // Combining is never allowed for prescriptions and will not work with
105 // dispense_drug.php.
106 if ($prescription_id) {
107 $allow_combining = 0;
110 $rows = array();
111 // $firstrow = false;
112 $qty_left = $quantity;
113 $bad_lot_list = '';
114 $total_on_hand = 0;
115 $gotexpired = false;
117 // If the user has a default warehouse, sort those lots first.
118 $orderby = ($default_warehouse === '') ?
119 "" : "di.warehouse_id != '$default_warehouse', ";
120 $orderby .= "lo.seq, di.expiration, di.lot_number, di.inventory_id";
122 // Retrieve lots in order of expiration date within warehouse preference.
123 $query = "SELECT di.*, lo.option_id, lo.seq " .
124 "FROM drug_inventory AS di " .
125 "LEFT JOIN list_options AS lo ON lo.list_id = 'warehouse' AND " .
126 "lo.option_id = di.warehouse_id AND lo.activity = 1 " .
127 "WHERE " .
128 "di.drug_id = ? AND di.destroy_date IS NULL ";
129 $sqlarr = array($drug_id);
130 if ($GLOBALS['SELL_FROM_ONE_WAREHOUSE'] && $default_warehouse) {
131 $query .= "AND di.warehouse_id = ? ";
132 $sqlarr[] = $default_warehouse;
135 $query .= "ORDER BY $orderby";
136 $res = sqlStatement($query, $sqlarr);
138 // First pass. Pick out lots to be used in filling this order, figure out
139 // if there is enough quantity on hand and check for lots to be destroyed.
140 while ($row = sqlFetchArray($res)) {
141 if ($row['warehouse_id'] != $default_warehouse) {
142 // Warehouses with seq > 99 are not available.
143 $seq = empty($row['seq']) ? 0 : $row['seq'] + 0;
144 if ($seq > 99) {
145 continue;
149 $on_hand = $row['on_hand'];
150 $expired = (!empty($row['expiration']) && $row['expiration'] <= $sale_date);
151 if ($expired || $on_hand < $quantity) {
152 $tmp = $row['lot_number'];
153 if (! $tmp) {
154 $tmp = '[missing lot number]';
157 if ($bad_lot_list) {
158 $bad_lot_list .= ', ';
161 $bad_lot_list .= $tmp;
164 if ($expired) {
165 $gotexpired = true;
166 continue;
169 /*****************************************************************
170 // Note the first row in case total quantity is insufficient and we are
171 // allowed to go negative.
172 if (!$firstrow) $firstrow = $row;
173 *****************************************************************/
175 $total_on_hand += $on_hand;
177 if ($on_hand > 0 && $qty_left > 0 && ($allow_combining || $on_hand >= $qty_left)) {
178 $rows[] = $row;
179 $qty_left -= $on_hand;
183 if ($expiredlots !== null) {
184 $expiredlots = $gotexpired;
187 if ($testonly) {
188 // Just testing inventory, so return true if OK, false if insufficient.
189 // $qty_left, if positive, is the amount requested that could not be allocated.
190 return $qty_left <= 0;
193 if ($bad_lot_list) {
194 send_drug_email(
195 "Possible lot destruction needed",
196 "The following lot(s) are expired or were too small to fill the " .
197 "order for patient $patient_id: $bad_lot_list\n"
201 /*******************************************************************
202 if (empty($firstrow)) return 0; // no suitable lots exist
203 // This can happen when combining is not allowed. We will use the
204 // first row and take it negative.
205 if (empty($rows)) {
206 $rows[] = $firstrow;
207 $qty_left -= $firstrow['on_hand'];
209 *******************************************************************/
211 // The above was an experiment in permitting a negative lot quantity.
212 // We decided that was a bad idea, so now we just error out if there
213 // is not enough on hand.
214 if ($qty_left > 0) {
215 return 0;
218 $sale_id = 0;
219 $qty_final = $quantity; // remaining unallocated quantity
220 $fee_final = $fee; // remaining unallocated fee
222 // Second pass. Update the database.
223 foreach ($rows as $row) {
224 $inventory_id = $row['inventory_id'];
226 /*****************************************************************
227 $thisqty = $row['on_hand'];
228 if ($qty_left > 0) {
229 $thisqty += $qty_left;
230 $qty_left = 0;
232 else if ($thisqty > $qty_final) {
233 $thisqty = $qty_final;
235 *****************************************************************/
236 $thisqty = min($qty_final, $row['on_hand']);
238 $qty_final -= $thisqty;
240 // Compute the proportional fee for this line item. For the last line
241 // item take the remaining unallocated fee to avoid round-off error.
242 if ($qty_final) {
243 $thisfee = sprintf('%0.2f', $fee * $thisqty / $quantity);
244 } else {
245 $thisfee = sprintf('%0.2f', $fee_final);
248 $fee_final -= $thisfee;
250 // Update inventory and create the sale line item.
251 sqlStatement("UPDATE drug_inventory SET " .
252 "on_hand = on_hand - ? " .
253 "WHERE inventory_id = ?", array($thisqty,$inventory_id));
254 $sale_id = sqlInsert(
255 "INSERT INTO drug_sales ( " .
256 "drug_id, inventory_id, prescription_id, pid, encounter, user, sale_date, quantity, fee, pricelevel, selector ) " .
257 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
258 array($drug_id, $inventory_id, $prescription_id, $patient_id, $encounter_id, $user,
259 $sale_date,
260 $thisqty,
261 $thisfee,
262 $pricelevel,
263 $selector)
266 // If this sale exhausted the lot then auto-destroy it if that is wanted.
267 if ($row['on_hand'] == $thisqty && !empty($GLOBALS['gbl_auto_destroy_lots'])) {
268 sqlStatement(
269 "UPDATE drug_inventory SET " .
270 "destroy_date = ?, destroy_method = ?, destroy_witness = ?, destroy_notes = ? " .
271 "WHERE drug_id = ? AND inventory_id = ?",
272 array($sale_date, xl('Automatic from sale'), $user, "sale_id = $sale_id",
273 $drug_id,
274 $inventory_id)
279 /*******************************************************************
280 // If appropriate, generate email to notify that re-order is due.
281 if (($total_on_hand - $quantity) <= $rowdrug['reorder_point']) {
282 send_drug_email("Product re-order required",
283 "Product '" . $rowdrug['name'] . "' has reached its reorder point.\n");
285 // TBD: If the above is un-commented, fix it to handle the case of
286 // $GLOBALS['gbl_min_max_months'] being true.
287 *******************************************************************/
289 // If combining is allowed then $sale_id will be just the last inserted ID,
290 // and it serves only to indicate that everything worked. Otherwise there
291 // can be only one inserted row and this is its ID.
292 return $sale_id;
295 // Determine if facility and warehouse restrictions are applicable for this user.
296 function isUserRestricted($userid = 0)
298 if (!$userid) {
299 $userid = $_SESSION['authId'];
302 $countrow = sqlQuery("SELECT count(*) AS count FROM users_facility WHERE " .
303 "tablename = 'users' AND table_id = ?", array($userid));
304 return !empty($countrow['count']);
307 // Check if the user has access to the given facility.
308 // Do not call this if user is not restricted!
309 function isFacilityAllowed($facid, $userid = 0)
311 if (!$userid) {
312 $userid = $_SESSION['authId'];
315 $countrow = sqlQuery(
316 "SELECT count(*) AS count FROM users_facility WHERE " .
317 "tablename = 'users' AND table_id = ? AND facility_id = ?",
318 array($userid, $facid)
320 if (empty($countrow['count'])) {
321 $countrow = sqlQuery(
322 "SELECT count(*) AS count FROM users WHERE " .
323 "id = ? AND facility_id = ?",
324 array($userid, $facid)
326 return !empty($countrow['count']);
329 return true;
332 // Check if the user has access to the given warehouse within the given facility.
333 // Do not call this if user is not restricted!
334 function isWarehouseAllowed($facid, $whid, $userid = 0)
336 if (!$userid) {
337 $userid = $_SESSION['authId'];
340 $countrow = sqlQuery(
341 "SELECT count(*) AS count FROM users_facility WHERE " .
342 "tablename = 'users' AND table_id = ? AND facility_id = ? AND " .
343 "(warehouse_id = ? OR warehouse_id = '')",
344 array($userid, $facid, $whid)
346 if (empty($countrow['count'])) {
347 $countrow = sqlQuery(
348 "SELECT count(*) AS count FROM users WHERE " .
349 "id = ? AND default_warehouse = ?",
350 array($userid, $whid)
352 return !empty($countrow['count']);
355 return true;