LBF custom template (nation notes) fancybox replace.
[openemr.git] / library / encounter_events.inc.php
blobb70f4bc13e04a7d060b6520ae0c708b02b830787
1 <?php
2 // +-----------------------------------------------------------------------------+
3 // Copyright (C) 2010 Z&H Consultancy Services Private Limited <sam@zhservices.com>
4 //
5 //
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
18 // A copy of the GNU General Public License is included along with this program:
19 // openemr/interface/login/GnuGPL.html
20 // For more information write to the Free Software
21 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 //
23 // Author: Eldho Chacko <eldho@zhservices.com>
24 // Paul Simon K <paul@zhservices.com>
26 // +------------------------------------------------------------------------------+
28 require_once(dirname(__FILE__) . '/calendar.inc');
29 require_once(dirname(__FILE__) . '/patient_tracker.inc.php');
32 //===============================================================================
33 //This section handles the events of payment screen.
34 //===============================================================================
35 define('REPEAT_EVERY_DAY', 0);
36 define('REPEAT_EVERY_WEEK', 1);
37 define('REPEAT_EVERY_MONTH', 2);
38 define('REPEAT_EVERY_YEAR', 3);
39 define('REPEAT_EVERY_WORK_DAY', 4);
40 define('REPEAT_DAYS_EVERY_WEEK', 6);
41 //===============================================================================
42 $today=date('Y-m-d');
43 //===============================================================================
44 //Create event in calender as arrived
45 function calendar_arrived($form_pid)
47 $Today=date('Y-m-d');
48 //Take all recurring events relevent for today.
49 $result_event=sqlStatement(
50 "SELECT * FROM openemr_postcalendar_events WHERE pc_recurrtype != '0' and pc_pid = ? and pc_endDate != '0000-00-00'
51 and pc_eventDate < ? and pc_endDate >= ? ",
52 array($form_pid,$Today,$Today)
54 if (sqlNumRows($result_event)==0) {//no repeating appointment
55 $result_event=sqlStatement(
56 "SELECT * FROM openemr_postcalendar_events WHERE pc_pid =? and pc_eventDate = ?",
57 array($form_pid,$Today)
59 if (sqlNumRows($result_event)==0) {//no appointment
60 echo "<br><br><br>".htmlspecialchars(xl('Sorry No Appointment is Fixed'), ENT_QUOTES).". ".htmlspecialchars(xl('No Encounter could be created'), ENT_QUOTES).".";
61 die;
62 } else //one appointment
64 $enc = todaysEncounterCheck($form_pid);//create encounter
65 $zero_enc=0;
66 sqlStatement(
67 "UPDATE openemr_postcalendar_events SET pc_apptstatus ='@' WHERE pc_pid =? and pc_eventDate = ?",
68 array($form_pid,$Today)
71 } else //repeating appointment set
73 while ($row_event=sqlFetchArray($result_event)) {
74 $pc_eid = $row_event['pc_eid'];
75 $pc_eventDate = $row_event['pc_eventDate'];
76 $pc_recurrspec_array = unserialize($row_event['pc_recurrspec']);
77 while (1) {
78 if ($pc_eventDate==$Today) {//Matches so insert.
79 if (!$exist_eid=check_event_exist($pc_eid)) {
80 update_event($pc_eid);
81 } else {
82 sqlStatement(
83 "UPDATE openemr_postcalendar_events SET pc_apptstatus = '@' WHERE pc_eid = ?",
84 array($exist_eid)
88 $enc = todaysEncounterCheck($form_pid);//create encounter
89 $zero_enc=0;
90 break;
91 } elseif ($pc_eventDate>$Today) {//the frequency does not match today,no need to increment furthur.
92 echo "<br><br><br>".htmlspecialchars(xl('Sorry No Appointment is Fixed'), ENT_QUOTES).". ".htmlspecialchars(xl('No Encounter could be created'), ENT_QUOTES).".";
93 die;
94 break;
97 // Added by Rod to handle repeats on nth or last given weekday of a month:
98 if ($row_event['pc_recurrtype'] == 2) {
99 $my_repeat_on_day = $pc_recurrspec_array['event_repeat_on_day'];
100 $my_repeat_on_num = $pc_recurrspec_array['event_repeat_on_num'];
101 $adate = getdate(strtotime($pc_eventDate));
102 $adate['mon'] += 1;
103 if ($adate['mon'] > 12) {
104 $adate['year'] += 1;
105 $adate['mon'] -= 12;
108 if ($my_repeat_on_num < 5) { // not last
109 $adate['mday'] = 1;
110 $dow = jddayofweek(cal_to_jd(CAL_GREGORIAN, $adate['mon'], $adate['mday'], $adate['year']));
111 if ($dow > $my_repeat_on_day) {
112 $dow -= 7;
115 $adate['mday'] += ($my_repeat_on_num - 1) * 7 + $my_repeat_on_day - $dow;
116 } else { // last weekday of month
117 $adate['mday'] = cal_days_in_month(CAL_GREGORIAN, $adate['mon'], $adate['year']);
118 $dow = jddayofweek(cal_to_jd(CAL_GREGORIAN, $adate['mon'], $adate['mday'], $adate['year']));
119 if ($dow < $my_repeat_on_day) {
120 $dow += 7;
123 $adate['mday'] += $my_repeat_on_day - $dow;
126 $pc_eventDate = date('Y-m-d', mktime(0, 0, 0, $adate['mon'], $adate['mday'], $adate['year']));
127 } // end recurrtype 2
129 else { // pc_recurrtype is 1
130 $pc_eventDate_array = explode('-', $pc_eventDate);
131 // Find the next day as per the frequency definition.
132 $pc_eventDate =& __increment(
133 $pc_eventDate_array[2],
134 $pc_eventDate_array[1],
135 $pc_eventDate_array[0],
136 $pc_recurrspec_array['event_repeat_freq'],
137 $pc_recurrspec_array['event_repeat_freq_type']
144 return $enc;
146 //===============================================================================
147 // Checks for the patient's encounter ID for today, creating it if there is none.
149 function todaysEncounterCheck($patient_id, $enc_date = '', $reason = '', $fac_id = '', $billing_fac = '', $provider = '', $cat = '', $return_existing = true)
151 global $today;
152 $encounter = todaysEncounterIf($patient_id);
153 if ($encounter) {
154 if ($return_existing) {
155 return $encounter;
156 } else {
157 return 0;
161 if (is_array($provider)) {
162 $visit_provider = (int)$provider[0];
163 } elseif ($provider) {
164 $visit_provider = (int)$provider;
165 } else {
166 $visit_provider = '(NULL)';
169 $dos = $enc_date ? $enc_date : $today;
170 $visit_reason = $reason ? $reason : xl('Please indicate visit reason');
171 $tmprow = sqlQuery("SELECT username, facility, facility_id FROM users WHERE id = ?", array($_SESSION["authUserID"]));
172 $username = $tmprow['username'];
173 $facility = $tmprow['facility'];
174 $facility_id = $fac_id ? (int)$fac_id : $tmprow['facility_id'];
175 $billing_facility = $billing_fac ? (int)$billing_fac : $tmprow['facility_id'];
176 $visit_cat = $cat ? $cat : '(NULL)';
177 $conn = $GLOBALS['adodb']['db'];
178 $encounter = $conn->GenID("sequences");
179 addForm(
180 $encounter,
181 "New Patient Encounter",
182 sqlInsert(
183 "INSERT INTO form_encounter SET " .
184 "date = ?, " .
185 "reason = ?, " .
186 "facility = ?, " .
187 "facility_id = ?, " .
188 "billing_facility = ?, " .
189 "provider_id = ?, " .
190 "pid = ?, " .
191 "encounter = ?," .
192 "pc_catid = ?",
193 array($dos,$visit_reason,$facility,$facility_id,$billing_facility,$visit_provider,$patient_id,$encounter,$visit_cat)
195 "newpatient",
196 $patient_id,
197 "1",
198 "NOW()",
199 $username
201 return $encounter;
204 //===============================================================================
205 // Checks for the group's encounter ID for today, creating it if there is none.
207 function todaysTherapyGroupEncounterCheck($group_id, $enc_date = '', $reason = '', $fac_id = '', $billing_fac = '', $provider = '', $cat = '', $return_existing = true, $eid = null)
209 global $today;
210 $encounter = todaysTherapyGroupEncounterIf($group_id);
211 if ($encounter) {
212 if ($return_existing) {
213 return $encounter;
214 } else {
215 return 0;
219 if (is_array($provider)) {
220 $visit_provider = (int)$provider[0];
221 $counselors = implode(',', $provider);
222 } elseif ($provider) {
223 $visit_provider = $counselors = (int)$provider;
224 } else {
225 $visit_provider = $counselors = null;
228 $dos = $enc_date ? $enc_date : $today;
229 $visit_reason = $reason ? $reason : xl('Please indicate visit reason');
230 $tmprow = sqlQuery("SELECT username, facility, facility_id FROM users WHERE id = ?", array($_SESSION["authUserID"]));
231 $username = $tmprow['username'];
232 $facility = $tmprow['facility'];
233 $facility_id = $fac_id ? (int)$fac_id : $tmprow['facility_id'];
234 $billing_facility = $billing_fac ? (int)$billing_fac : $tmprow['facility_id'];
235 $visit_cat = $cat ? $cat : '(NULL)';
236 $conn = $GLOBALS['adodb']['db'];
237 $encounter = $conn->GenID("sequences");
238 addForm(
239 $encounter,
240 "New Therapy Group Encounter",
241 sqlInsert(
242 "INSERT INTO form_groups_encounter SET " .
243 "date = ?, " .
244 "reason = ?, " .
245 "facility = ?, " .
246 "facility_id = ?, " .
247 "billing_facility = ?, " .
248 "provider_id = ?, " .
249 "group_id = ?, " .
250 "encounter = ?," .
251 "pc_catid = ? ," .
252 "appt_id = ? ," .
253 "counselors = ? ",
254 array($dos,$visit_reason,$facility,$facility_id,$billing_facility,$visit_provider,$group_id,$encounter,$visit_cat, $eid, $counselors)
256 "newGroupEncounter",
257 null,
258 "1",
259 "NOW()",
260 $username,
262 $group_id
264 return $encounter;
266 //===============================================================================
267 // Get the patient's encounter ID for today, if it exists.
268 // In the case of more than one encounter today, pick the last one.
270 function todaysEncounterIf($patient_id)
272 global $today;
273 $tmprow = sqlQuery("SELECT encounter FROM form_encounter WHERE " .
274 "pid = ? AND date = ? " .
275 "ORDER BY encounter DESC LIMIT 1", array($patient_id,"$today 00:00:00"));
276 return empty($tmprow['encounter']) ? 0 : $tmprow['encounter'];
278 //===============================================================================
279 // Get the group's encounter ID for today, if it exists.
280 // In the case of more than one encounter today, pick the last one.
282 function todaysTherapyGroupEncounterIf($group_id)
284 global $today;
285 $tmprow = sqlQuery("SELECT encounter FROM form_groups_encounter WHERE " .
286 "group_id = ? AND date = ? " .
287 "ORDER BY encounter DESC LIMIT 1", array($group_id,"$today 00:00:00"));
288 return empty($tmprow['encounter']) ? 0 : $tmprow['encounter'];
290 //===============================================================================
292 // Get the patient's encounter ID for today, creating it if there is none.
294 function todaysEncounter($patient_id, $reason = '')
296 global $today, $userauthorized;
298 if (empty($reason)) {
299 $reason = xl('Please indicate visit reason');
302 // Was going to use the existing encounter for today if there is one, but
303 // decided it's right to always create a new one. Leaving the code here
304 // (and corresponding function above) in case it is ever wanted later.
305 /*******************************************************************
306 $encounter = todaysEncounterIf($patient_id);
307 if ($encounter) return $encounter;
308 *******************************************************************/
310 $tmprow = sqlQuery("SELECT username, facility, facility_id FROM users " .
311 "WHERE id = ?", array($_SESSION["authUserID"]));
312 $username = $tmprow['username'];
313 $facility = $tmprow['facility'];
314 $facility_id = $tmprow['facility_id'];
315 $conn = $GLOBALS['adodb']['db'];
316 $encounter = $conn->GenID("sequences");
317 $provider_id = $userauthorized ? $_SESSION['authUserID'] : 0;
318 addForm(
319 $encounter,
320 "New Patient Encounter",
321 sqlInsert(
322 "INSERT INTO form_encounter SET date = ?, onset_date = ?, " .
323 "reason = ?, facility = ?, facility_id = ?, pid = ?, encounter = ?, " .
324 "provider_id = ?",
325 array($today, $today, $reason, $facility, $facility_id, $patient_id,
326 $encounter, $provider_id)
328 "newpatient",
329 $patient_id,
330 $userauthorized,
331 "NOW()",
332 $username
334 return $encounter;
336 //===============================================================================
337 // get the original event's repeat specs
338 function update_event($eid)
340 $origEventRes = sqlStatement("SELECT * FROM openemr_postcalendar_events WHERE pc_eid = ?", array($eid));
341 $origEvent=sqlFetchArray($origEventRes);
342 $oldRecurrspec = unserialize($origEvent['pc_recurrspec']);
343 $duration=$origEvent['pc_duration'];
344 $starttime=$origEvent['pc_startTime'];
345 $endtime=$origEvent['pc_endTime'];
346 $selected_date = date("Ymd");
347 if ($oldRecurrspec['exdate'] != "") {
348 $oldRecurrspec['exdate'] .= ",".$selected_date;
349 } else {
350 $oldRecurrspec['exdate'] .= $selected_date;
353 // mod original event recur specs to exclude this date
354 sqlStatement("UPDATE openemr_postcalendar_events SET pc_recurrspec = ? WHERE pc_eid = ?", array(serialize($oldRecurrspec),$eid));
355 // specify some special variables needed for the INSERT
356 // no recurr specs, this is used for adding a new non-recurring event
357 $noRecurrspec = array("event_repeat_freq" => "",
358 "event_repeat_freq_type" => "",
359 "event_repeat_on_num" => "1",
360 "event_repeat_on_day" => "0",
361 "event_repeat_on_freq" => "0",
362 "exdate" => ""
364 // Useless garbage that we must save.
365 $locationspecs = array("event_location" => "",
366 "event_street1" => "",
367 "event_street2" => "",
368 "event_city" => "",
369 "event_state" => "",
370 "event_postal" => ""
372 $locationspec = serialize($locationspecs);
373 $args['event_date'] = date('Y-m-d');
374 $args['duration'] = $duration;
375 // this event is forced to NOT REPEAT
376 $args['form_repeat'] = "0";
377 $args['recurrspec'] = $noRecurrspec;
378 $args['form_enddate'] = "0000-00-00";
379 $args['starttime'] = $starttime;
380 $args['endtime'] = $endtime;
381 $args['locationspec'] = $locationspec;
382 $args['form_category']=$origEvent['pc_catid'];
383 $args['new_multiple_value']=$origEvent['pc_multiple'];
384 $args['form_provider']=$origEvent['pc_aid'];
385 $args['form_pid']=$origEvent['pc_pid'];
386 $args['form_title']=$origEvent['pc_title'];
387 $args['form_allday']=$origEvent['pc_alldayevent'];
388 $args['form_apptstatus']='@';
389 $args['form_prefcat']=$origEvent['pc_prefcatid'];
390 $args['facility']=$origEvent['pc_facility'];
391 $args['billing_facility']=$origEvent['pc_billing_location'];
392 InsertEvent($args, 'payment');
394 //===============================================================================
395 // check if event exists
396 function check_event_exist($eid)
398 $origEventRes = sqlStatement("SELECT * FROM openemr_postcalendar_events WHERE pc_eid = ?", array($eid));
399 $origEvent=sqlFetchArray($origEventRes);
400 $pc_catid=$origEvent['pc_catid'];
401 $pc_aid=$origEvent['pc_aid'];
402 $pc_pid=$origEvent['pc_pid'];
403 $pc_eventDate=date('Y-m-d');
404 $pc_startTime=$origEvent['pc_startTime'];
405 $pc_endTime=$origEvent['pc_endTime'];
406 $pc_facility=$origEvent['pc_facility'];
407 $pc_billing_location=$origEvent['pc_billing_location'];
408 $pc_recurrspec_array = unserialize($origEvent['pc_recurrspec']);
409 $origEvent = sqlStatement(
410 "SELECT * FROM openemr_postcalendar_events WHERE pc_eid != ? and pc_catid=? and pc_aid=? ".
411 "and pc_pid=? and pc_eventDate=? and pc_startTime=? and pc_endTime=? and pc_facility=? and pc_billing_location=?",
412 array($eid,$pc_catid,$pc_aid,$pc_pid,$pc_eventDate,$pc_startTime,$pc_endTime,$pc_facility,$pc_billing_location)
414 if (sqlNumRows($origEvent)>0) {
415 $origEventRow=sqlFetchArray($origEvent);
416 return $origEventRow['pc_eid'];
417 } else {
418 if (strpos($pc_recurrspec_array['exdate'], date('Ymd')) === false) {//;'20110228'
419 return false;
420 } else {//this happens in delete case
421 return true;
425 //===============================================================================
426 // insert an event
427 // $args is mainly filled with content from the POST http var
428 function InsertEvent($args, $from = 'general')
430 $pc_recurrtype = '0';
431 if ($args['form_repeat'] || $args['days_every_week']) {
432 if ($args['recurrspec']['event_repeat_freq_type'] == "6") {
433 $pc_recurrtype = 3;
434 } else {
435 $pc_recurrtype = $args['recurrspec']['event_repeat_on_freq'] ? '2' : '1';
439 $form_pid = empty($args['form_pid']) ? '' : $args['form_pid'];
440 $form_room = empty($args['form_room']) ? '' : $args['form_room'];
441 $form_gid = empty($args['form_gid']) ? '' : $args['form_gid'];
443 if ($from == 'general') {
444 $pc_eid = sqlInsert(
445 "INSERT INTO openemr_postcalendar_events ( " .
446 "pc_catid, pc_multiple, pc_aid, pc_pid, pc_gid, pc_title, pc_time, pc_hometext, " .
447 "pc_informant, pc_eventDate, pc_endDate, pc_duration, pc_recurrtype, " .
448 "pc_recurrspec, pc_startTime, pc_endTime, pc_alldayevent, " .
449 "pc_apptstatus, pc_prefcatid, pc_location, pc_eventstatus, pc_sharing, pc_facility,pc_billing_location,pc_room " .
450 ") VALUES (?,?,?,?,?,?,NOW(),?,?,?,?,?,?,?,?,?,?,?,?,?,1,1,?,?,?)",
451 array($args['form_category'],(isset($args['new_multiple_value']) ? $args['new_multiple_value'] : ''),$args['form_provider'],$form_pid,$form_gid,
452 $args['form_title'],$args['form_comments'],$_SESSION['authUserID'],$args['event_date'],
453 fixDate($args['form_enddate']),$args['duration'],$pc_recurrtype,serialize($args['recurrspec']),
454 $args['starttime'],$args['endtime'],$args['form_allday'],$args['form_apptstatus'],$args['form_prefcat'],
455 $args['locationspec'],(int)$args['facility'],(int)$args['billing_facility'],$form_room)
458 //Manage tracker status.
459 if (!empty($form_pid)) {
460 manage_tracker_status($args['event_date'], $args['starttime'], $pc_eid, $form_pid, $_SESSION['authUser'], $args['form_apptstatus'], $args['form_room']);
463 $GLOBALS['temporary-eid-for-manage-tracker'] = $pc_eid; //used by manage tracker module to set correct encounter in tracker when check in
465 return $pc_eid;
466 } elseif ($from == 'payment') {
467 sqlStatement(
468 "INSERT INTO openemr_postcalendar_events ( " .
469 "pc_catid, pc_multiple, pc_aid, pc_pid, pc_title, pc_time, " .
470 "pc_eventDate, pc_endDate, pc_duration, pc_recurrtype, " .
471 "pc_recurrspec, pc_startTime, pc_endTime, pc_alldayevent, " .
472 "pc_apptstatus, pc_prefcatid, pc_location, pc_eventstatus, pc_sharing, pc_facility,pc_billing_location " .
473 ") VALUES (?,?,?,?,?,NOW(),?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
474 array($args['form_category'],$args['new_multiple_value'],$args['form_provider'],$form_pid,$args['form_title'],
475 $args['event_date'],$args['form_enddate'],$args['duration'],$pc_recurrtype,serialize($args['recurrspec']),
476 $args['starttime'],$args['endtime'],$args['form_allday'],$args['form_apptstatus'],$args['form_prefcat'], $args['locationspec'],
479 (int)$args['facility'],
480 (int)$args['billing_facility'])
484 //================================================================================================================
486 * __increment()
487 * returns the next valid date for an event based on the
488 * current day,month,year,freq and type
489 * @private
490 * @returns string YYYY-MM-DD
492 function &__increment($d, $m, $y, $f, $t)
495 if ($t == REPEAT_EVERY_DAY) {
496 return date('Y-m-d', mktime(0, 0, 0, $m, ($d+$f), $y));
497 } elseif ($t == REPEAT_EVERY_WORK_DAY) {
498 // a workday is defined as Mon,Tue,Wed,Thu,Fri
499 // repeating on every or Nth work day means to not include
500 // weekends (Sat/Sun) in the increment... tricky
502 // ugh, a day-by-day loop seems necessary here, something where
503 // we can check to see if the day is a Sat/Sun and increment
504 // the frequency count so as to ignore the weekend. hmmmm....
505 $orig_freq = $f;
506 for ($daycount=1; $daycount<=$orig_freq; $daycount++) {
507 $nextWorkDOW = date('w', mktime(0, 0, 0, $m, ($d+$daycount), $y));
508 if (is_weekend_day($nextWorkDOW)) {
509 $f++;
513 // and finally make sure we haven't landed on a end week days
514 // adjust as necessary
515 $nextWorkDOW = date('w', mktime(0, 0, 0, $m, ($d+$f), $y));
516 if (count($GLOBALS['weekend_days']) === 2) {
517 if ($nextWorkDOW == $GLOBALS['weekend_days'][0]) {
518 $f+=2;
519 } elseif ($nextWorkDOW == $GLOBALS['weekend_days'][1]) {
520 $f++;
522 } elseif (count($GLOBALS['weekend_days']) === 1 && $nextWorkDOW === $GLOBALS['weekend_days'][0]) {
523 $f++;
526 return date('Y-m-d', mktime(0, 0, 0, $m, ($d+$f), $y));
527 } elseif ($t == REPEAT_EVERY_WEEK) {
528 return date('Y-m-d', mktime(0, 0, 0, $m, ($d+(7*$f)), $y));
529 } elseif ($t == REPEAT_EVERY_MONTH) {
530 return date('Y-m-d', mktime(0, 0, 0, ($m+$f), $d, $y));
531 } elseif ($t == REPEAT_EVERY_YEAR) {
532 return date('Y-m-d', mktime(0, 0, 0, $m, $d, ($y+$f)));
533 } elseif ($t == REPEAT_DAYS_EVERY_WEEK) {
534 $old_appointment_date = date('Y-m-d', mktime(0, 0, 0, $m, $d, $y));
535 $next_appointment_date = getTheNextAppointment($old_appointment_date, $f);
536 return $next_appointment_date;
540 function getTheNextAppointment($appointment_date, $freq)
542 $day_arr = explode(",", $freq);
543 $date_arr = array();
544 foreach ($day_arr as $day) {
545 $day = getDayName($day);
546 $date = date('Y-m-d', strtotime("next " . $day, strtotime($appointment_date)));
547 array_push($date_arr, $date);
550 $next_appointment = getEarliestDate($date_arr);
551 return $next_appointment;
554 function getDayName($day_num)
556 if ($day_num == "1") {
557 return "sunday";
560 if ($day_num == "2") {
561 return "monday";
564 if ($day_num == "3") {
565 return "tuesday";
568 if ($day_num == "4") {
569 return "wednesday";
572 if ($day_num == "5") {
573 return "thursday";
576 if ($day_num == "6") {
577 return "friday";
580 if ($day_num == "7") {
581 return "saturday";
586 function getEarliestDate($date_arr)
588 $earliest = ($date_arr[0]);
589 foreach ($date_arr as $date) {
590 if (strtotime($date) < strtotime($earliest)) {
591 $earliest = $date;
595 return $earliest;
597 //================================================================================================================