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

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

New exe and dll
calendarGrid: Minor documentation updates.
CGAppointments: Object now supports Deep cloning
CGDocument:

  1. Major changes on how m_pAvArrays is handled. Now it is locked whenever it is updated or queried. Some refactoring to make sure there are no db calls during the locks so that the locks won't be expensive.
  2. Removed ClearResources, an unused method.
  3. Appointment aCopy walkin property is set to true if the appointment is a walkin. This makes sure that the grid draws it correctly between it is added to the appointment array and we fetch new data from the server.
  4. Create appointment is not responsible anymore for requesting updates from the server. All requests to update data must be done through CGView, as it is the only party interested in displaying accurate data on the grid. Just send the create appt event to the server.
  5. CheckInAppointment: Same thing. Now responsible for requesting updates from the server. Just send the checkin event to the server.

CGDocumentManager: Removed tracing. Done in BMX Library only now.
CGView:

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