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

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

Remove CGSchedLib.OutputArray everywhere. Used to be needed in VS 2003; not anymore as you can see tables now.
CalendarGrid:

  • Make MinSince80 and TimesOverlap static functions for use by other classes since they are done over and over again.

CGAppointments:

  • Documentation updates

CGAVDocument:

CGDocument:

CGDocumentManager:

CGSchedLib: Lots of Changes

CGView:

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