source: Scheduling/trunk/cs/bsdx0200GUISourceCode/CGDocument.cs@ 1122

Last change on this file since 1122 was 1122, checked in by Sam Habiel, 13 years ago

Added (maybe) better uncaught Exception handling. We'll see if it works at all.
Release exes, again.

File size: 55.4 KB
Line 
1using System;
2using System.Collections;
3using System.Data;
4using System.Data.OleDb;
5using System.Diagnostics;
6using System.Drawing;
7using System.Windows.Forms;
8using IndianHealthService.BMXNet;
9using System.Linq;
10using System.Collections.Generic;
11
12namespace IndianHealthService.ClinicalScheduling
13{
14 /// <summary>
15 /// Contains the array of appointments and availabily that make up the document class
16 /// </summary>
17 public class CGDocument
18 {
19 #region Member Variables
20 public int m_nColumnCount; //todo: this should point to the view's member for column count
21 public int m_nTimeUnits; //?
22 private string m_sDocName; //Document Name ?
23 public ArrayList m_sResourcesArray; //keeps the resources
24 public ScheduleType m_ScheduleType; //Either a Resource or a Clinic (Group of Resources)
25 private DateTime m_dSelectedDate; //Holds the user's selection from the dtpicker
26 private DateTime m_dStartDate; //Beginning date of document data
27 private DateTime m_dEndDate; //Ending date of document data
28 public CGAppointments m_appointments; //Appointment List
29 private ArrayList m_pAvArray; //Availability List
30 private CGDocumentManager m_DocManager; //Holds a reference to the document manager
31 private DateTime m_dLastRefresh = DateTime.Now; //Holds last refersh date
32 #endregion
33
34 /// <summary>
35 /// Constructor. Initialize State Data:
36 /// 3 Arrays (Appointments, Availabilities, and Resources)
37 /// Schedule Type is Resource not Clinic
38 /// Selected Date is Today
39 /// Start Date is Today
40 /// End Date is 7 days from Today
41 /// </summary>
42 public CGDocument()
43 {
44 m_appointments = new CGAppointments(); // Holds Appointments
45 m_pAvArray = new ArrayList(); // Holds Availabilites
46 m_sResourcesArray = new ArrayList(); // Holds Resources
47 m_ScheduleType = ScheduleType.Resource; // Default Schedule Type is a Resource
48 m_dSelectedDate = DateTime.Today; // Default Selected Date is Today
49 m_dStartDate = DateTime.Today; // Default Start Date is Today
50 m_dEndDate = DateTime.Today.AddDays(7); // Default End Date is 7 days from Today.
51 }
52
53
54 #region Properties
55
56 /// <summary>
57 /// Returns the latest refresh time for this document
58 /// </summary>
59 public DateTime LastRefreshed
60 {
61 get
62 {
63 return m_dLastRefresh;
64 }
65 }
66
67 /// <summary>
68 /// The list of Resource names
69 /// </summary>
70 public ArrayList Resources
71 {
72 get
73 {
74 return this.m_sResourcesArray;
75 }
76 set
77 {
78 this.m_sResourcesArray = value;
79 }
80 }
81
82 /// <summary>
83 /// The array of CGAvailabilities that contains appt type and slots
84 /// </summary>
85 public ArrayList AvailabilityArray
86 {
87 get
88 {
89 return this.m_pAvArray;
90 }
91 }
92
93 public CGDocumentManager DocManager
94 {
95 get
96 {
97 return m_DocManager;
98 }
99 set
100 {
101 m_DocManager = value;
102 }
103 }
104
105 /// <summary>
106 /// Contains the hashtable of appointments
107 /// </summary>
108 public CGAppointments Appointments
109 {
110 get
111 {
112 return m_appointments;
113 }
114 }
115
116 /// <summary>
117 /// Holds the date selected by the user in CGView.dateTimePicker1
118 /// </summary>
119 public DateTime SelectedDate
120 {
121 get
122 {
123 return this.m_dSelectedDate;
124 }
125 set
126 {
127 this.m_dSelectedDate = value;
128 }
129 }
130
131 /// <summary>
132 /// Contains the beginning date of the appointment document
133 /// </summary>
134 public DateTime StartDate
135 {
136 get
137 {
138 return this.m_dStartDate;
139 }
140 }
141
142 public string DocName
143 {
144 get
145 {
146 return this.m_sDocName;
147 }
148 set
149 {
150 this.m_sDocName = value;
151 }
152 }
153
154 #endregion
155
156 #region Methods
157
158 public void UpdateAllViews()
159 {
160 //iterate through all views and call update.
161 Hashtable h = CGDocumentManager.Current.Views;
162
163 CGDocument d;
164 foreach (CGView v in h.Keys)
165 {
166 d = (CGDocument)h[v];
167 if (d == this)
168 {
169 v.UpdateArrays();
170 }
171 }
172
173 }
174
175 /// <summary>
176 /// Update schedule based on info in RPMS
177 /// <returns>Clears and repopluates m_appointments</returns>
178 /// </summary>
179 private bool RefreshAppointments()
180 {
181 try
182 {
183 string sPatientName;
184 string sPatientID;
185 DateTime dStart;
186 DateTime dEnd;
187 DateTime dCheckIn;
188 DateTime dAuxTime;
189 int nKeyID;
190 string sNote;
191 string sResource;
192 bool bNoShow = false;
193 string sNoShow = "0";
194 string sHRN = "";
195 int nAccessTypeID; //used in autorebook
196 string sWalkIn = "0";
197 bool bWalkIn;
198 CGAppointment pAppointment;
199 CGDocumentManager pApp = CGDocumentManager.Current;
200 DataTable rAppointmentSchedule;
201
202 //Nice to know that it gets set here!!!
203 m_dLastRefresh = DateTime.Now;
204
205 //Clear appointments associated with this document
206 this.m_appointments.ClearAllAppointments();
207
208 // calls RPC to get appointments
209 rAppointmentSchedule = CGSchedLib.CreateAppointmentSchedule(m_DocManager, m_sResourcesArray, this.m_dStartDate, this.m_dEndDate);
210
211 // loop through datatable: Create CGAppointment and add to CGAppointments
212 foreach (DataRow r in rAppointmentSchedule.Rows)
213 {
214
215 if (r["APPOINTMENTID"].ToString() == "0")
216 {
217 string sMsg = r["NOTE"].ToString();
218 throw new BMXNetException(sMsg);
219 }
220 nKeyID = Convert.ToInt32(r["APPOINTMENTID"].ToString());
221 sResource = r["RESOURCENAME"].ToString();
222 sPatientName = r["PATIENTNAME"].ToString();
223 sPatientID = r["PATIENTID"].ToString();
224 dStart = (DateTime)r["START_TIME"];
225 dEnd = (DateTime)r["END_TIME"];
226 dCheckIn = new DateTime();
227 dAuxTime = new DateTime();
228
229 if (r["CHECKIN"].GetType() != typeof(System.DBNull))
230 dCheckIn = (DateTime)r["CHECKIN"];
231 if (r["AUXTIME"].GetType() != typeof(System.DBNull))
232 dCheckIn = (DateTime)r["AUXTIME"];
233 sNote = r["NOTE"].ToString();
234 sNoShow = r["NOSHOW"].ToString();
235 bNoShow = (sNoShow == "1") ? true : false;
236 sHRN = r["HRN"].ToString();
237 nAccessTypeID = (int)r["ACCESSTYPEID"];
238 sWalkIn = r["WALKIN"].ToString();
239 bWalkIn = (sWalkIn == "1") ? true : false;
240
241 Patient pt = new Patient()
242 {
243 DFN = Convert.ToInt32(sPatientID),
244 Name = sPatientName,
245 DOB = (DateTime)r["DOB"],
246 ID = r["PID"].ToString(),
247 HRN = sHRN,
248 Sex = r["SEX"].ToString() == "MALE" ? Sex.Male : Sex.Female
249 };
250
251 pAppointment = new CGAppointment();
252 pAppointment.Patient = pt;
253 pAppointment.CreateAppointment(dStart, dEnd, sNote, nKeyID, sResource);
254 pAppointment.PatientName = sPatientName;
255 pAppointment.PatientID = Convert.ToInt32(sPatientID);
256 if (dCheckIn.Ticks > 0)
257 pAppointment.CheckInTime = dCheckIn;
258 if (dAuxTime.Ticks > 0)
259 pAppointment.AuxTime = dAuxTime;
260 pAppointment.NoShow = bNoShow;
261 pAppointment.HealthRecordNumber = sHRN;
262 pAppointment.AccessTypeID = nAccessTypeID;
263 pAppointment.WalkIn = bWalkIn;
264 this.m_appointments.AddAppointment(pAppointment);
265
266 }
267
268 return true;
269 }
270 catch (Exception Ex)
271 {
272 Debug.Write("CGDocument.RefreshDaysSchedule error: " + Ex.Message + "\n");
273 return false;
274 }
275 }
276
277
278 public bool IsRefreshNeeded()
279 {
280 if (m_sResourcesArray.Count == 0) return false;
281 return this.WeekNeedsRefresh(1, m_dSelectedDate, out this.m_dStartDate, out this.m_dEndDate);
282 }
283
284 //sam: This is a test that duplicates RefreshDocument, but without the UpdateAllViews,
285 // as that has to be done synchornously.
286 //XXX: Needs to be refactored obviously, but now for testing.
287 //XXX: Tested extensively enough. Less refactoring now. 2011-01-26
288 public void RefreshDocumentAsync()
289 {
290 Debug.WriteLine("IN REFERSH DOCUMENT ASYNC\n\n");
291
292 bool bRet = false;
293 if (m_sResourcesArray.Count == 0)
294 return;
295 if (m_sResourcesArray.Count == 1)
296 {
297 bRet = this.WeekNeedsRefresh(1, m_dSelectedDate, out this.m_dStartDate, out this.m_dEndDate);
298 }
299 else
300 {
301 this.m_dStartDate = m_dSelectedDate;
302 this.m_dEndDate = m_dSelectedDate;
303 this.m_dEndDate = this.m_dEndDate.AddHours(23);
304 this.m_dEndDate = this.m_dEndDate.AddMinutes(59);
305 this.m_dEndDate = this.m_dEndDate.AddSeconds(59);
306 }
307
308 bRet = RefreshSchedule();
309 }
310
311
312 public void RefreshDocument()
313 {
314 bool bRet = false;
315 if (m_sResourcesArray.Count == 0)
316 return;
317 if (m_sResourcesArray.Count == 1)
318 {
319 bRet = this.WeekNeedsRefresh(1, m_dSelectedDate, out this.m_dStartDate, out this.m_dEndDate);
320 }
321 else
322 {
323 this.m_dStartDate = m_dSelectedDate;
324 this.m_dEndDate = m_dSelectedDate;
325 this.m_dEndDate = this.m_dEndDate.AddHours(23);
326 this.m_dEndDate = this.m_dEndDate.AddMinutes(59);
327 this.m_dEndDate = this.m_dEndDate.AddSeconds(59);
328 }
329
330 bRet = RefreshSchedule();
331
332 this.UpdateAllViews();
333 }
334
335 public void OnOpenDocument(DateTime dDate)
336 {
337 try
338 {
339 //Create new Document
340 m_ScheduleType = (m_sResourcesArray.Count == 1) ? ScheduleType.Resource : ScheduleType.Clinic;
341 bool bRet = false;
342
343 if (m_ScheduleType == ScheduleType.Resource)
344 {
345 bRet = this.WeekNeedsRefresh(1, dDate, out this.m_dStartDate, out this.m_dEndDate);
346 }
347 else
348 {
349 this.m_dStartDate = dDate;
350 this.m_dEndDate = dDate;
351 this.m_dEndDate = this.m_dEndDate.AddHours(23);
352 this.m_dEndDate = this.m_dEndDate.AddMinutes(59);
353 this.m_dEndDate = this.m_dEndDate.AddSeconds(59);
354 }
355
356 bRet = RefreshSchedule();
357
358 CGView view = null;
359 //If this document already has a view, the use it
360 //SAM: Why do this again???
361 Hashtable h = CGDocumentManager.Current.Views;
362 CGDocument d;
363 bool bReuseView = false;
364 foreach (CGView v in h.Keys)
365 {
366 d = (CGDocument)h[v];
367 if (d == this)
368 {
369 view = v;
370 bReuseView = true;
371 v.InitializeDocView(this.DocName);
372 break;
373 }
374 }
375
376 //Otherwise, create new View
377 if (bReuseView == false)
378 {
379 view = new CGView();
380
381 view.InitializeDocView(this,
382 this.DocManager,
383 m_dStartDate,
384 this.DocName);
385
386 view.Show();
387 view.SyncTree();
388
389 }
390 this.UpdateAllViews();
391 }
392 catch (BMXNetException bmxEx)
393 {
394 throw bmxEx;
395 }
396 catch (Exception ex)
397 {
398 throw new BMXNet.BMXNetException("ClinicalScheduling.OnOpenDocument error: " + ex.Message);
399 }
400 }
401
402 /// <summary>
403 /// Refreshes Availablility and Schedules from RPMS.
404 /// </summary>
405 /// <returns>Success or Failure. Should be always Success.</returns>
406 private bool RefreshSchedule()
407 {
408 this.RefreshAvailabilitySchedule();
409 this.RefreshAppointments();
410 return true;
411 }
412
413 private bool RefreshAvailabilitySchedule()
414 {
415 try
416 {
417 ArrayList saryApptTypes = new ArrayList();
418
419 //Refresh Availability schedules
420 DataTable rAvailabilitySchedule;
421 rAvailabilitySchedule = CGSchedLib.CreateAvailabilitySchedule(m_DocManager, m_sResourcesArray, this.m_dStartDate, this.m_dEndDate, saryApptTypes,/**/ m_ScheduleType, "0");
422
423 ////NEW
424 //NOTE: This lock makes sure that availabilities aren't queried for slots when the array is an intermediate
425 //state. The other place that has this lock is SlotsAvailable function.
426 lock (this.m_pAvArray)
427 {
428 m_pAvArray.Clear();
429 foreach (DataRow rTemp in rAvailabilitySchedule.Rows)
430 {
431 DateTime dStart = (DateTime)rTemp["START_TIME"];
432 DateTime dEnd = (DateTime)rTemp["END_TIME"];
433
434 //TODO: Fix this slots datatype problem
435 string sSlots = rTemp["SLOTS"].ToString();
436 int nSlots = Convert.ToInt16(sSlots);
437
438 string sResourceList = rTemp["RESOURCE"].ToString();
439 string sAccessRuleList = rTemp["ACCESS_TYPE"].ToString();
440 string sNote = rTemp["NOTE"].ToString();
441
442 int nApptTypeID;
443
444 if ((nSlots < -1000) || (sAccessRuleList == ""))
445 {
446 nApptTypeID = 0;
447 }
448 else
449 {
450 nApptTypeID = Int32.Parse(rTemp["ACCESS_TYPE"].ToString());
451 }
452
453 AddAvailability(dStart, dEnd, nApptTypeID, nSlots, sResourceList, sAccessRuleList, sNote);
454 }
455 }
456 return true;
457
458 /* NOT USED
459 //Refresh Type Schedule
460 string sResourceName = "";
461 DataTable rTypeSchedule = new DataTable(); ;
462 for (int j = 0; j < m_sResourcesArray.Count; j++)
463 {
464 sResourceName = m_sResourcesArray[j].ToString();
465 DataTable dtTemp = CGSchedLib.CreateAssignedTypeSchedule(m_DocManager, sResourceName, this.m_dStartDate, this.m_dEndDate, m_ScheduleType);
466
467 if (j == 0)
468 {
469 rTypeSchedule = dtTemp;
470 }
471 else
472 {
473 rTypeSchedule = CGSchedLib.UnionBlocks(rTypeSchedule, dtTemp);
474 }
475 }
476
477 DateTime dStart;
478 DateTime dEnd;
479 DateTime dTypeStart;
480 DateTime dTypeEnd;
481 int nSlots;
482 Rectangle crRectA = new Rectangle(0, 0, 1, 0);
483 Rectangle crRectB = new Rectangle(0, 0, 1, 0);
484 bool bIsect;
485 string sResourceList;
486 string sAccessRuleList;
487
488 //smh: moved clear availabilities down here.
489 //smh: Temporary solution to make sure that people don't touch the availability table at the same time!!!
490 //NOTE: This lock makes sure that availabilities aren't queried for slots when the array is an intermediate
491 //state. The other place that has this lock is SlotsAvailable function.
492 lock (this.m_pAvArray)
493 {
494 m_pAvArray.Clear();
495
496 foreach (DataRow rTemp in rAvailabilitySchedule.Rows)
497 {
498 //get StartTime, EndTime and Slots
499 dStart = (DateTime)rTemp["START_TIME"];
500 dEnd = (DateTime)rTemp["END_TIME"];
501
502 //TODO: Fix this slots datatype problem
503 string sSlots = rTemp["SLOTS"].ToString();
504 nSlots = Convert.ToInt16(sSlots);
505
506 sResourceList = rTemp["RESOURCE"].ToString();
507 sAccessRuleList = rTemp["ACCESS_TYPE"].ToString();
508
509 string sNote = rTemp["NOTE"].ToString();
510
511 if ((nSlots < -1000) || (sAccessRuleList == ""))
512 {
513 nApptTypeID = 0;
514 }
515 else
516 {
517 foreach (DataRow rType in rTypeSchedule.Rows)
518 {
519
520 dTypeStart = (DateTime)rType["StartTime"];
521 dTypeEnd = (DateTime)rType["EndTime"];
522 //if start & end times overlap, then
523 string sTypeResource = rType["ResourceName"].ToString();
524 if ((dTypeStart.DayOfYear == dStart.DayOfYear) && (sResourceList == sTypeResource))
525 {
526 crRectA.Y = GetTotalMinutes(dStart);
527 crRectA.Height = GetTotalMinutes(dEnd) - crRectA.Top;
528 crRectB.Y = GetTotalMinutes(dTypeStart);
529 crRectB.Height = GetTotalMinutes(dTypeEnd) - crRectB.Top;
530 bIsect = crRectA.IntersectsWith(crRectB);
531 if (bIsect == true)
532 {
533 //TODO: This code:
534 // nApptTypeID = (int) rType["AppointmentTypeID"];
535 //Causes this exception:
536 //Unhandled Exception: System.InvalidCastException: Specified cast is not valid.
537 string sTemp = rType["AppointmentTypeID"].ToString();
538 nApptTypeID = Convert.ToInt16(sTemp);
539 break;
540 }
541 }
542 }//end foreach datarow rType
543 }
544
545
546 //AddAvailability(dStart, dEnd, nApptTypeID, nSlots, sResourceList, sAccessRuleList, sNote);
547 }//end foreach datarow rTemp
548 }//end lock
549 return true;
550 */
551 }
552 catch (Exception ex)
553 {
554 Debug.Write("CGDocument.RefreshAvailabilitySchedule error: " + ex.Message + "\n");
555 return false;
556 }
557 }
558
559 private int GetTotalMinutes(DateTime dDate)
560 {
561 return ((dDate.Hour * 60) + dDate.Minute);
562 }
563
564 /// <summary>
565 /// Adds Availability to Availability Array held by document
566 /// </summary>
567 /// <param name="StartTime">Self-Explan</param>
568 /// <param name="EndTime">Self-Explan</param>
569 /// <param name="nType"></param>
570 /// <param name="nSlots"></param>
571 /// <param name="UpdateView"></param>
572 /// <param name="sResourceList"></param>
573 /// <param name="sAccessRuleList"></param>
574 /// <param name="sNote"></param>
575 /// <returns></returns>
576 public int AddAvailability(DateTime StartTime, DateTime EndTime, int nType, int nSlots, string sResourceList, string sAccessRuleList, string sNote)
577 {
578 //adds it to the object array
579 //Returns the index in the array
580
581 CGAvailability pNewAv = new CGAvailability();
582 pNewAv.Create(StartTime, EndTime, nType, nSlots, sResourceList, sAccessRuleList);
583
584 pNewAv.Note = sNote;
585
586 //Look up the color and type name using the AppointmentTypes datatable
587 DataTable dtType = this.m_DocManager.GlobalDataSet.Tables["AccessTypes"];
588 DataRow dRow = dtType.Rows.Find(nType.ToString());
589 if (dRow != null)
590 {
591 string sColor = dRow["DISPLAY_COLOR"].ToString();
592 pNewAv.DisplayColor = sColor;
593 string sTemp = dRow["RED"].ToString();
594 sTemp = (sTemp == "") ? "0" : sTemp;
595 int nRed = Convert.ToInt16(sTemp);
596 pNewAv.Red = nRed;
597 sTemp = dRow["GREEN"].ToString();
598 sTemp = (sTemp == "") ? "0" : sTemp;
599 int nGreen = Convert.ToInt16(sTemp);
600 pNewAv.Green = nGreen;
601 sTemp = dRow["BLUE"].ToString();
602 sTemp = (sTemp == "") ? "0" : sTemp;
603 int nBlue = Convert.ToInt16(sTemp);
604 pNewAv.Blue = nBlue;
605
606 string sName = dRow["ACCESS_TYPE_NAME"].ToString();
607 pNewAv.AccessTypeName = sName;
608 }
609
610 int nIndex = 0;
611 nIndex = m_pAvArray.Add(pNewAv);
612
613 return nIndex;
614 }
615
616
617 public void AddResource(string sResource)
618 {
619 //TODO: Test that resource is not currently in list, that it IS a resource, etc
620 this.m_sResourcesArray.Add(sResource);
621 }
622
623 /// <summary>
624 /// Gets number of slots left in a certain selection on the grid. Used in Multiple Places.
625 /// </summary>
626 /// <param name="dSelStart">Selection Start Date</param>
627 /// <param name="dSelEnd">Selection End Date</param>
628 /// <param name="sResource">Resource Name</param>
629 /// <param name="sAccessType">Out: Name of Access Type</param>
630 /// <param name="sAvailabilityMessage">Out: Access Note</param>
631 /// <returns>Number of slots</returns>
632 public int SlotsAvailable(DateTime dSelStart, DateTime dSelEnd, string sResource, int viewTimeScale, out CGAvailability resultantAV)
633 {
634
635 resultantAV = null;
636
637 double slotsAvailable = 0; //defalut return value
638
639 double effectiveSlotsAvailable = 0; //Slots available based on the time scale.
640
641
642 //NOTE: What's this lock? This lock makes sure that nobody is editing the availability array
643 //when we are looking at it. Since the availability array could potentially be updated on
644 //a different thread, we are can be potentially left stuck with an empty array.
645 //
646 //The other place that uses this lock is the RefershAvailabilitySchedule method
647 //
648 //This is a temporary fix until I figure out how to divorce the availbilities here from those drawn
649 //on the calendar. Appointments are cloned b/c they are in an object that supports that; and b/c I
650 //don't need to suddenly query them at runtime like I do with Availabilities.
651
652 //Let's Try Linq
653 lock (this.m_pAvArray)
654 {
655 //This foreach loop looks for an availability that overlaps where the user clicked.
656 //Availabilites cannot overlap each other (enforced at the DB level)
657 //If selection hits multiple blocks, get the block with the most slots (reflected by the sorting here)
658 List<CGAvailability> lstAV = (from avail in this.m_pAvArray.Cast<CGAvailability>()
659 where (sResource == avail.ResourceList && CalendarGrid.TimesOverlap(dSelStart, dSelEnd, avail.StartTime, avail.EndTime))
660 select avail).ToList();
661
662 //if we don't have any availabilities, then return with zero slots.
663 if (lstAV.Count == 0) return 0;
664
665 CGAvailability pAV;
666
667 //if there is just one, that's it.
668 if (lstAV.Count == 1) pAV = lstAV.First();
669 //otherwise...
670 else
671 {
672 //if availabilities are contigous to each other, need to join them together.
673
674 //First, are they the same?
675 string firsttype = lstAV.First().AccessTypeName;
676 bool bAllSameType = lstAV.All(av => av.AccessTypeName == firsttype);
677
678 //Second are they ALL contigous?
679 DateTime lastEndTime = DateTime.Today; //bogus value to please compiler who wants it assigned.
680 int index = 0;
681
682 bool bContigous = lstAV.OrderBy(av => av.StartTime)
683 .All(av =>
684 {
685 index++;
686 if (index == 1)
687 {
688 lastEndTime = av.EndTime;
689 return true;
690 }
691 if (av.StartTime == lastEndTime)
692 {
693 lastEndTime = av.EndTime;
694 return true;
695 }
696
697 return false;
698 });
699
700
701
702 if (bContigous && bAllSameType)
703 {
704 var enumAVOrdered = lstAV.OrderBy(av => av.StartTime);
705
706 pAV = new CGAvailability
707 {
708 StartTime = enumAVOrdered.First().StartTime,
709 EndTime = enumAVOrdered.Last().EndTime,
710 Slots = enumAVOrdered.Sum(av => av.Slots),
711 AccessTypeName = enumAVOrdered.First().AccessTypeName,
712 Note = enumAVOrdered.First().Note
713 };
714 }
715 else
716 {
717 pAV = lstAV.OrderByDescending(av => av.Slots).First();
718 }
719 }
720
721 resultantAV = pAV; // out var
722
723 slotsAvailable = pAV.Slots;
724
725 //Subtract total slots current appointments take up.
726 slotsAvailable -= (from appt in this.Appointments.AppointmentTable.Values.Cast<CGAppointment>()
727 //If the resource is the same and the user selection overlaps, then...
728 where (sResource == appt.Resource && CalendarGrid.TimesOverlap(pAV.StartTime, pAV.EndTime, appt.StartTime, appt.EndTime))
729 // if appt starttime is before avail start time, only count against the avail starting from the availability start time
730 let startTimeToCountAgainstBlock = appt.StartTime < pAV.StartTime ? pAV.StartTime : appt.StartTime
731 // if appt endtime is after the avail ends, only count against the avail up to where the avail ends
732 let endTimeToCountAgainstBlock = appt.EndTime > pAV.EndTime ? pAV.EndTime : appt.EndTime
733 // theoretical minutes per slot for the availability
734 let minPerSlot = (pAV.EndTime - pAV.StartTime).TotalMinutes / pAV.Slots
735 // how many minutes does this appointment take away from the slot
736 let minPerAppt = (endTimeToCountAgainstBlock - startTimeToCountAgainstBlock).TotalMinutes
737 // how many slots the appointment takes up using this availability's scale
738 let slotsConsumed = minPerAppt / minPerSlot
739 select slotsConsumed)
740 // add up SlotsConsumed to substract from slotsAvailable
741 .Sum();
742
743 //theoretical minutes per slot for the availability
744 double minPerSlot2 = (pAV.EndTime - pAV.StartTime).TotalMinutes / pAV.Slots;
745
746 //Convert it to the view's time scale.
747 effectiveSlotsAvailable = (minPerSlot2 / viewTimeScale) * slotsAvailable;
748
749 }
750
751 //round it down.
752 return (int)effectiveSlotsAvailable;
753
754 /* OLD ALGOTHRIM 2
755
756 lock (this.m_pAvArray)
757 {
758 //This foreach loop looks for an availability that overlaps where the user clicked.
759 //There can only be one, as availabilites cannot overlap each other (enforced at the DB level)
760 //Therefore, we loop, and once we find it, we break.
761 foreach (CGAvailability pAV in this.m_pAvArray)
762 {
763 //If the resource is the same and the user selection overlaps, then...
764 if (sResource == pAV.ResourceList && CalendarGrid.TimesOverlap(dSelStart, dSelEnd, pAV.StartTime, pAV.EndTime))
765 {
766 slotsAvailable = pAV.Slots; //variable now holds the total number of slots
767 sAccessType = pAV.AccessTypeName; //Access Name
768 sAvailabilityMessage = pAV.Note; //Access Block Note
769
770 //Here we substract each appointment weight in slots from slotsAvailable
771 foreach (DictionaryEntry apptDict in this.m_appointments.AppointmentTable)
772 {
773 CGAppointment appt = (CGAppointment)apptDict.Value;
774 //If the appointment is in the same resource and overlaps with this availablity
775 if (sResource == appt.Resource && CalendarGrid.TimesOverlap(pAV.StartTime, pAV.EndTime, appt.StartTime, appt.EndTime))
776 {
777 // if appt starttime is before avail start time, only count against the avail starting from the availability start time
778 DateTime startTimeToCountAgainstBlock = appt.StartTime < pAV.StartTime ? pAV.StartTime : appt.StartTime;
779 // if appt endtime is after the avail ends, only count against the avail up to where the avail ends
780 DateTime endTimeToCountAgainstBlock = appt.EndTime > pAV.EndTime ? pAV.EndTime : appt.EndTime;
781 // theoretical minutes per slot for the availability
782 double minPerSlot = (pAV.EndTime - pAV.StartTime).TotalMinutes/pAV.Slots;
783 // how many minutes does this appointment take away from the slot
784 double minPerAppt = (endTimeToCountAgainstBlock - startTimeToCountAgainstBlock).TotalMinutes;
785 // how many slots the appointment takes up using this availability's scale
786 double slotsConsumed = minPerAppt / minPerSlot;
787 // subscract that from the total slots for the availability
788 slotsAvailable -= slotsConsumed;
789 } //end if
790 //now go to the next appointment in foreach
791 }
792 // As I said above, we can match only one availability. Once we found it and substracted from it; we are done.
793 break;
794 }
795 }
796 // We return the integer portion of the variable for display to the user or for calculations.
797 // That means, if 2.11 slots are left, the user sees 2. If 2.66, still 2.
798 return (int)slotsAvailable;
799 }
800 */
801
802 /* ORIGINAL ALGORITHM
803 sAccessType = "";
804 sAvailabilityMessage = "";
805 DateTime dStart;
806 DateTime dEnd;
807 int nAvailableSlots = 999;
808 int nSlots = 0;
809 int i = 0;
810 CGAvailability pAv;
811 Rectangle crRectA = new Rectangle(0, 0, 1, 0);
812 Rectangle crRectB = new Rectangle(0, 0, 1, 0);
813 bool bIsect;
814 crRectB.Y = GetTotalMinutes(dSelStart);
815 crRectB.Height = GetTotalMinutes(dSelEnd) - crRectB.Y;
816
817 //NOTE: What's this lock? This lock makes sure that nobody is editing the availability array
818 //when we are looking at it. Since the availability array could potentially be updated on
819 //a different thread, we are can be potentially left stuck with an empty array.
820 //
821 //The other place that uses this lock is the RefershAvailabilitySchedule method
822 //
823 //This is a temporary fix until I figure out how to divorce the availbilities here from those drawn
824 //on the calendar. Appointments are cloned b/c they are in an object that supports that; and b/c I
825 //don't need to suddenly query them at runtime like I do with Availabilities.
826
827 lock (this.m_pAvArray)
828 {
829 //loop thru m_pAvArray
830 //Compare the start time and end time of eachblock
831 while (i < m_pAvArray.Count)
832 {
833 pAv = (CGAvailability)m_pAvArray[i];
834 dStart = pAv.StartTime;
835 dEnd = pAv.EndTime;
836 if ((sResource == pAv.ResourceList) &&
837 ((dSelStart.Date == dStart.Date) || (dSelStart.Date == dEnd.Date)))
838 {
839 crRectA.Y = (dStart.Date < dSelStart.Date) ? 0 : GetTotalMinutes(dStart);
840 crRectA.Height = (dEnd.Date > dSelEnd.Date) ? 1440 : GetTotalMinutes(dEnd);
841 crRectA.Height = crRectA.Height - crRectA.Y;
842 bIsect = crRectA.IntersectsWith(crRectB);
843 if (bIsect != false)
844 {
845 nSlots = pAv.Slots;
846 if (nSlots < 1)
847 {
848 nAvailableSlots = 0;
849 break;
850 }
851 if (nSlots < nAvailableSlots)
852 {
853 nAvailableSlots = nSlots;
854 sAccessType = pAv.AccessTypeName;
855 sAvailabilityMessage = pAv.Note;
856
857 }
858 }//end if
859 }//end if
860 i++;
861 }//end while
862 }//end lock
863
864 if (nAvailableSlots == 999)
865 {
866 nAvailableSlots = 0;
867 }
868 return nAvailableSlots;
869 */
870 }
871
872 /// <summary>
873 /// Given a selected date,
874 /// Calculates StartDay and End Day and returns them in output params.
875 /// nWeeks == number of Weeks to display
876 /// nColumnCount is number of days displayed per week.
877 /// If 5 columns, begin on Second Day of Week
878 /// If 7 Columns, begin on First Day of Week
879 /// (this is a change from the hardcoded behavior for US-based calendars)
880 ///
881 /// Returns TRUE if the document's data needs refreshing based on
882 /// this newly selected date.
883 /// </summary>
884 public bool WeekNeedsRefresh(int nWeeks, DateTime SelectedDate,
885 out DateTime WeekStartDay, out DateTime WeekEndDay)
886 {
887 DateTime OldStartDay = m_dStartDate;
888 DateTime OldEndDay = m_dEndDate;
889 // Week start based on thread locale
890 int nStartWeekDay = (int)System.Threading.Thread.CurrentThread.CurrentCulture.DateTimeFormat.FirstDayOfWeek;
891 // Current Day
892 int nWeekDay = (int)SelectedDate.DayOfWeek; //0 == Sunday
893
894 // this offset gets approrpriate day based on locale.
895 int nOff = (nStartWeekDay + 1) % 7;
896 TimeSpan ts = new TimeSpan(nWeekDay - nOff, 0, 0, 0); //d,h,m,s
897
898 // if ts is negative, we will jump to the next week in the logic.
899 // to show the correct week, add 7. Confusing, I know.
900 if (ts < new TimeSpan()) ts = ts + new TimeSpan(7, 0, 0, 0);
901
902 if (m_nColumnCount == 1) // if one column start and end on the same day.
903 {
904 ts = new TimeSpan(0, 23, 59, 59);
905 WeekStartDay = SelectedDate;
906 WeekEndDay = WeekStartDay + ts;
907 }
908 else if (m_nColumnCount == 5 || m_nColumnCount == 0) // if 5 column start (or default) at the 2nd day of this week and end in 4:23:59:59 days.
909 {
910 // if picked day is start of week (Sunday in US), start in the next day since that's the first day of work week
911 // else, just substract the calculated time span to get to the start of week (first work day)
912 WeekStartDay = (nWeekDay == nStartWeekDay) ? SelectedDate + new TimeSpan(1, 0, 0, 0) : SelectedDate - ts;
913 // End day calculation
914 int nEnd = 3;
915 ts = new TimeSpan((7 * nWeeks) - nEnd, 23, 59, 59);
916 WeekEndDay = WeekStartDay + ts;
917 }
918 else // if 7 column start at the 1st day of this week and end in 6:23:59:59 days.
919 {
920 // if picked day is start of week, use that. Otherwise, go to the fist work day and substract one to get to start of week.
921 WeekStartDay = (nWeekDay == nStartWeekDay) ? SelectedDate : SelectedDate - ts - new TimeSpan(1, 0, 0, 0);
922 // End day calculation
923 int nEnd = 1;
924 ts = new TimeSpan((7 * nWeeks) - nEnd, 23, 59, 59);
925 WeekEndDay = WeekStartDay + ts;
926 }
927
928 bool bRet = ((WeekStartDay.Date != OldStartDay.Date) || (WeekEndDay.Date != OldEndDay.Date));
929 return bRet;
930 }
931
932 /// <summary>
933 /// Calls RPMS to create appointment then
934 /// adds appointment to the m_appointments collection
935 /// Returns the IEN of the appointment in the RPMS BSDX APPOINTMENT file.
936 /// </summary>
937 /// <param name="rApptInfo"></param>
938 /// <returns></returns>
939 public int CreateAppointment(CGAppointment rApptInfo)
940 {
941 return CreateAppointment(rApptInfo, false);
942 }
943
944 /// <summary>
945 /// Use this overload to create a walkin appointment
946 /// </summary>
947 /// <param name="rApptInfo"></param>
948 /// <param name="bWalkin"></param>
949 /// <returns></returns>
950 public int CreateAppointment(CGAppointment rApptInfo, bool bWalkin)
951 {
952 string sStart;
953 string sEnd;
954 string sPatID;
955 string sResource;
956 string sNote;
957 string sLen;
958 string sApptID;
959
960 //sStart = rApptInfo.StartTime.ToString("M-d-yyyy@HH:mm");
961 //sEnd = rApptInfo.EndTime.ToString("M-d-yyyy@HH:mm");
962
963 // i18n code -- Use culture neutral FMDates
964 sStart = FMDateTime.Create(rApptInfo.StartTime).FMDateString;
965 sEnd = FMDateTime.Create(rApptInfo.EndTime).FMDateString;
966
967 TimeSpan sp = rApptInfo.EndTime - rApptInfo.StartTime;
968 sLen = sp.TotalMinutes.ToString();
969 sPatID = rApptInfo.PatientID.ToString();
970 sNote = rApptInfo.Note;
971 sResource = rApptInfo.Resource;
972 if (bWalkin == true)
973 {
974 sApptID = "WALKIN";
975 }
976 else
977 {
978 sApptID = rApptInfo.AccessTypeID.ToString();
979 }
980
981 CGAppointment aCopy = new CGAppointment();
982 aCopy.CreateAppointment(rApptInfo.StartTime, rApptInfo.EndTime, sNote, 0, sResource);
983 aCopy.PatientID = rApptInfo.PatientID;
984 aCopy.PatientName = rApptInfo.PatientName;
985 aCopy.HealthRecordNumber = rApptInfo.HealthRecordNumber;
986 aCopy.AccessTypeID = rApptInfo.AccessTypeID;
987 aCopy.WalkIn = bWalkin ? true : false;
988
989 string sSql = "BSDX ADD NEW APPOINTMENT^" + sStart + "^" + sEnd + "^" + sPatID + "^" + sResource + "^" + sLen + "^" + sNote + "^" + sApptID;
990 System.Data.DataTable dtAppt = m_DocManager.RPMSDataTable(sSql, "NewAppointment");
991 int nApptID;
992
993 Debug.Assert(dtAppt.Rows.Count == 1);
994 DataRow r = dtAppt.Rows[0];
995 nApptID = Convert.ToInt32(r["APPOINTMENTID"]);
996 string sErrorID;
997 sErrorID = r["ERRORID"].ToString();
998 if ((sErrorID != "") || (nApptID < 1))
999 {
1000 throw new Exception(sErrorID);
1001 }
1002 aCopy.AppointmentKey = nApptID;
1003 this.m_appointments.AddAppointment(aCopy);
1004
1005 //TODO: Improve
1006 //Yucky hack for now... haven't investigated why we need to create a new CGAppointment beyond
1007 //the one that we pass.
1008 rApptInfo.AppointmentKey = nApptID;
1009
1010 //Have make appointment from CGView responsible for requesting an update for the avialability.
1011 //bool bRet = RefreshAvailabilitySchedule();
1012
1013 //Sam: don't think this is needed as it is called from CGView.
1014 //Make CGView responsible for all drawing.
1015 //UpdateAllViews();
1016
1017 return nApptID;
1018 }
1019
1020 public void EditAppointment(CGAppointment pAppt, string sNote)
1021 {
1022 try
1023 {
1024 int nApptID = pAppt.AppointmentKey;
1025 string sSql = "BSDX EDIT APPOINTMENT^" + nApptID.ToString() + "^" + sNote;
1026
1027 System.Data.DataTable dtAppt = m_DocManager.RPMSDataTable(sSql, "EditAppointment");
1028
1029 Debug.Assert(dtAppt.Rows.Count == 1);
1030 DataRow r = dtAppt.Rows[0];
1031 string sErrorID = r["ERRORID"].ToString();
1032 if (sErrorID == "-1")
1033 pAppt.Note = sNote;
1034
1035 if (this.m_appointments.AppointmentTable.ContainsKey(nApptID))
1036 {
1037 bool bRet = RefreshAvailabilitySchedule();
1038 UpdateAllViews();
1039 }
1040 }
1041 catch (Exception ex)
1042 {
1043 Debug.Write("CGDocument.EditAppointment Failed: " + ex.Message);
1044 }
1045 }
1046
1047 public void CheckInAppointment(int nApptID, DateTime dCheckIn)
1048 {
1049 string sCheckIn = FMDateTime.Create(dCheckIn).FMDateString;
1050
1051 string sSql = "BSDX CHECKIN APPOINTMENT^" + nApptID.ToString() + "^" + sCheckIn; // +"^";
1052 //sSql += ClinicStopIEN + "^" + ProviderIEN + "^" + PrintRouteSlip + "^";
1053 //sSql += PCCClinicIEN + "^" + PCCFormIEN + "^" + PCCOutGuide;
1054
1055 System.Data.DataTable dtAppt = m_DocManager.RPMSDataTable(sSql, "CheckInAppointment");
1056
1057 Debug.Assert(dtAppt.Rows.Count == 1);
1058 DataRow r = dtAppt.Rows[0];
1059 string sErrorID = r["ERRORID"].ToString();
1060 }
1061
1062 public string DeleteAppointment(int nApptID)
1063 {
1064 return DeleteAppointment(nApptID, true, 0, "");
1065 }
1066
1067 public string DeleteAppointment(int nApptID, bool bClinicCancelled, int nReason, string sRemarks)
1068 {
1069 //Returns "" if deletion successful
1070 //Otherwise, returns reason for failure
1071
1072 string sClinicCancelled = (bClinicCancelled == true) ? "C" : "PC";
1073 string sReasonID = nReason.ToString();
1074 string sSql = "BSDX CANCEL APPOINTMENT^" + nApptID.ToString();
1075 sSql += "^" + sClinicCancelled;
1076 sSql += "^" + sReasonID;
1077 sSql += "^" + sRemarks;
1078 DataTable dtAppt = m_DocManager.RPMSDataTable(sSql, "DeleteAppointment");
1079
1080 Debug.Assert(dtAppt.Rows.Count == 1);
1081 DataRow r = dtAppt.Rows[0];
1082 string sErrorID = r["ERRORID"].ToString();
1083 if (sErrorID != "")
1084 return sErrorID;
1085
1086 if (this.m_appointments.AppointmentTable.ContainsKey(nApptID))
1087 {
1088 this.m_appointments.RemoveAppointment(nApptID);
1089
1090 // View responsible for deciding to redraw the grid; not the document now.
1091 //bool bRet = RefreshAvailabilitySchedule();
1092 //UpdateAllViews();
1093 }
1094 return "";
1095 }
1096
1097 public string AutoRebook(CGAppointment a, int nSearchType, int nMinimumDays, int nMaximumDays, out CGAppointment aRebook)
1098 {
1099 //If successful Returns "1" and new start date and time returned in aRebook
1100 //Otherwise, returns error message
1101
1102 CGAppointment aCopy = new CGAppointment();
1103 aCopy.CreateAppointment(a.StartTime, a.EndTime, a.Note, 0, a.Resource);
1104 aCopy.PatientID = a.PatientID;
1105 aCopy.PatientName = a.PatientName;
1106 aCopy.HealthRecordNumber = a.HealthRecordNumber;
1107 aCopy.AccessTypeID = a.AccessTypeID;
1108 aRebook = aCopy;
1109
1110 //Determine Rebook access type
1111 //nSearchType = -1: use current, -2: use any non-zero type, >0 use this access type id
1112 int nAVType = 0;
1113
1114 switch (nSearchType)
1115 {
1116 case -1:
1117 nAVType = a.AccessTypeID;
1118 break;
1119 case -2:
1120 nAVType = 0;
1121 break;
1122 default:
1123 nAVType = nSearchType;
1124 break;
1125 }
1126
1127 int nSlots = 0;
1128 string sSlots = "";
1129 int nAccessTypeID; //To compare with nAVType
1130
1131 DateTime dResult = new DateTime(); //StartTime of access block to autorebook into
1132
1133 //Next two are empty, but needed to pass to CreateAvailabilitySchedule
1134 ArrayList alAccessTypes = new ArrayList();
1135 string sSearchInfo = "";
1136
1137 //Find the StartTime of first availability block of this type for this clinic
1138 //between nMinimumDays and nMaximumDays
1139
1140 string sAVStart = a.StartTime.AddDays(nMinimumDays).ToString("M/d/yyyy@H:mm");
1141
1142 //dtAVEnd is the last day to search
1143 DateTime dtAVEnd = a.StartTime.AddDays(nMinimumDays + nMaximumDays);
1144 string sAVEnd = dtAVEnd.ToString("M/d/yyyy@H:mm");
1145
1146 //Increment start day to search a week (or so) at a time
1147 //30 is a test increment. Need to test different values for performance
1148 int nIncrement = (nMaximumDays < 30) ? nMaximumDays : 30;
1149
1150 //nCount and nCountEnd are the 'moving' counters
1151 //that I add to start and end to get the bracket
1152 //At the beginning of the DO loop, nCount and nCountEnd are already set
1153 bool bFinished = false;
1154 bool bFound = false;
1155
1156 DateTime dStart = a.StartTime.AddDays(nMinimumDays);
1157 // v 1.3 i18n support - FM Date passed insated of American Date
1158 string sStart = FMDateTime.Create(dStart).DateOnly.FMDateString;
1159 DateTime dEnd = dStart.AddDays(nIncrement);
1160 do
1161 {
1162 string sSql = "BSDX REBOOK NEXT BLOCK^" + sStart + "^" + a.Resource + "^" + nAVType.ToString();
1163 DataTable dtNextBlock = this.DocManager.RPMSDataTable(sSql, "NextBlock");
1164 Debug.Assert(dtNextBlock.Rows.Count == 1);
1165 DataRow drNextBlockRow = dtNextBlock.Rows[0];
1166
1167 object oNextBlock;
1168 oNextBlock = drNextBlockRow["NEXTBLOCK"];
1169 if (oNextBlock.GetType() == typeof(System.DBNull))
1170 break;
1171 DateTime dNextBlock = (DateTime)drNextBlockRow["NEXTBLOCK"];
1172 if (dNextBlock > dtAVEnd)
1173 {
1174 break;
1175 }
1176
1177 dStart = dNextBlock;
1178 dEnd = dStart.AddDays(nIncrement);
1179 if (dEnd > dtAVEnd)
1180 dEnd = dtAVEnd;
1181
1182 DataTable dtResult = CGSchedLib.CreateAvailabilitySchedule(m_DocManager, this.Resources, dStart, dEnd, alAccessTypes, ScheduleType.Resource, sSearchInfo);
1183 //Loop thru dtResult looking for a slot having the required availability type.
1184 //If found, set bFinished = true;
1185 foreach (DataRow dr in dtResult.Rows)
1186 {
1187
1188 sSlots = dr["SLOTS"].ToString();
1189 if (sSlots == "")
1190 sSlots = "0";
1191 nSlots = Convert.ToInt16(sSlots);
1192 if (nSlots > 0)
1193 {
1194 nAccessTypeID = 0; //holds the access type id of the availability block
1195 if (dr["ACCESS_TYPE"].ToString() != "")
1196 nAccessTypeID = Convert.ToInt16(dr["ACCESS_TYPE"].ToString());
1197 if ((nSearchType == -2) && (nAccessTypeID > 0)) //Match on any non-zero type
1198 {
1199 bFinished = true;
1200 bFound = true;
1201 dResult = (DateTime)dr["START_TIME"];
1202 break;
1203 }
1204 if (nAccessTypeID == nAVType)
1205 {
1206 bFinished = true;
1207 bFound = true;
1208 dResult = (DateTime)dr["START_TIME"];
1209 break;
1210 }
1211 }
1212 }
1213 dStart = dEnd.AddDays(1);
1214 dEnd = dStart.AddDays(nIncrement);
1215 if (dEnd > dtAVEnd)
1216 dEnd = dtAVEnd;
1217 } while (bFinished == false);
1218
1219 if (bFound == true)
1220 {
1221 aCopy.StartTime = dResult;
1222 aCopy.EndTime = dResult.AddMinutes(a.Duration);
1223 //Create the appointment
1224 //Set the AUTOREBOOKED flag
1225 //and store the "AutoRebooked To DateTime"
1226 //in each autorebooked appointment
1227 this.CreateAppointment(aCopy);
1228 SetAutoRebook(a, dResult);
1229 return "1";
1230 }
1231 else
1232 {
1233 return "0";
1234 }
1235 }
1236
1237 private void SetAutoRebook(CGAppointment a, DateTime dtRebookedTo)
1238 {
1239 string sApptKey = a.AppointmentKey.ToString();
1240 //string sRebookedTo = dtRebookedTo.ToString("M/d/yyyy@HH:mm");
1241 // i18n
1242 string sRebookedTo = FMDateTime.Create(dtRebookedTo).FMDateString;
1243 string sSql = "BSDX REBOOK SET^" + sApptKey + "^" + sRebookedTo;
1244 System.Data.DataTable dtRebook = m_DocManager.RPMSDataTable(sSql, "AutoRebook");
1245
1246 }
1247
1248 public string AppointmentNoShow(CGAppointment a, bool bNoShow)
1249 {
1250 /*
1251 * BSDX NOSHOW RPC Returns 1 in ERRORID if successfully sets NOSHOW flag in BSDX APPOINTMENT and, if applicable, File 2
1252 *Otherwise, returns negative numbers for failure and errormessage in ERRORTXT
1253 *
1254 */
1255 int nApptID = a.AppointmentKey;
1256 string sTest = bNoShow.ToString();
1257 string sNoShow = (bNoShow == true) ? "1" : "0";
1258 string sSql = "BSDX NOSHOW^" + nApptID.ToString();
1259 sSql += "^";
1260 sSql += sNoShow;
1261
1262 DataTable dtAppt = m_DocManager.RPMSDataTable(sSql, "AppointmentNoShow");
1263
1264 Debug.Assert(dtAppt.Rows.Count == 1);
1265 DataRow r = dtAppt.Rows[0];
1266 string sErrorID = r["ERRORID"].ToString();
1267 if (sErrorID != "1")
1268 {
1269 return r["ERRORTEXT"].ToString();
1270 }
1271
1272 //All okay over here... then set appointment noshow or not no show...
1273 a.NoShow = bNoShow ? true : false;
1274
1275 //bool bRet = RefreshSchedule();
1276
1277 return sErrorID;
1278 }
1279
1280 public bool AppointmentUndoCheckin(CGAppointment a, out string msg)
1281 {
1282 msg = "";
1283 //zero good, anything else bad
1284 DataTable dt = CGDocumentManager.Current.DAL.RemoveCheckIn(a.AppointmentKey);
1285 if (dt.Rows[0][0].ToString() == "0")
1286 {
1287 a.CheckInTime = DateTime.MinValue; // remove check-in time.
1288 return true;
1289 }
1290
1291 else
1292 {
1293 msg = dt.Rows[0][0].ToString();
1294 return false;
1295 }
1296 }
1297
1298 //DON'T USE OBVIOUSLY. JUST FOR TESTING.
1299 public void ThrowException()
1300 {
1301 throw new Exception("Hello World. I am an Exception.");
1302 }
1303
1304 #endregion Methods
1305
1306 }//End class
1307}//End namespace
Note: See TracBrowser for help on using the repository browser.