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

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

CGDocument: On Delete Appointment, no update done by Document method. View responsible for this request.
CGView:

  1. Drag and Dropped code made to be the same as the rest. Call Document for updating appointments, but not redraw the grid. Drag Drop does not request a refresh directly; but rather, it shoots RPMS events which when they are raised back update the grid.
  2. Fixed bug in Drag and Drop where the source appointment resource is changed before passing it to Document.CreateAppointment() which saves it to the database. This causes the appointments maintained by Document to be temporarily corrupted if CreateAppointment fails during drag and drop.
  3. View calls refershdocumentasync now after Document.DeleteAppointment call.

Last but not least: new version of ClinicalScheduling.exe and BMXNet23.dll (release).

File size: 43.0 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 {
802 throw new Exception(sErrorID);
803 }
804 aCopy.AppointmentKey = nApptID;
805 this.m_appointments.AddAppointment(aCopy);
806
807
808 //Have make appointment from CGView responsible for requesting an update for the avialability.
809 //bool bRet = RefreshAvailabilitySchedule();
810
811 //Sam: don't think this is needed as it is called from CGView.
812 //Make CGView responsible for all drawing.
813 //UpdateAllViews();
814
815 return nApptID;
816 }
817
818 public void EditAppointment(CGAppointment pAppt, string sNote)
819 {
820 try
821 {
822 int nApptID = pAppt.AppointmentKey;
823 string sSql = "BSDX EDIT APPOINTMENT^" + nApptID.ToString() + "^" + sNote;
824
825 System.Data.DataTable dtAppt = m_DocManager.RPMSDataTable(sSql, "EditAppointment");
826
827 Debug.Assert(dtAppt.Rows.Count == 1);
828 DataRow r = dtAppt.Rows[0];
829 string sErrorID = r["ERRORID"].ToString();
830 if (sErrorID == "-1")
831 pAppt.Note = sNote;
832
833 if (this.m_appointments.AppointmentTable.ContainsKey(nApptID))
834 {
835 bool bRet = RefreshAvailabilitySchedule();
836 UpdateAllViews();
837 }
838 }
839 catch (Exception ex)
840 {
841 Debug.Write("CGDocument.EditAppointment Failed: " + ex.Message);
842 }
843 }
844
845 public void CheckInAppointment(int nApptID, DateTime dCheckIn)
846 {
847 string sCheckIn = FMDateTime.Create(dCheckIn).FMDateString;
848
849 string sSql = "BSDX CHECKIN APPOINTMENT^" + nApptID.ToString() + "^" + sCheckIn; // +"^";
850 //sSql += ClinicStopIEN + "^" + ProviderIEN + "^" + PrintRouteSlip + "^";
851 //sSql += PCCClinicIEN + "^" + PCCFormIEN + "^" + PCCOutGuide;
852
853 System.Data.DataTable dtAppt = m_DocManager.RPMSDataTable(sSql, "CheckInAppointment");
854
855 Debug.Assert(dtAppt.Rows.Count == 1);
856 DataRow r = dtAppt.Rows[0];
857 string sErrorID = r["ERRORID"].ToString();
858
859
860 }
861
862 public string DeleteAppointment(int nApptID)
863 {
864 return DeleteAppointment(nApptID, true, 0, "");
865 }
866
867 public string DeleteAppointment(int nApptID, bool bClinicCancelled, int nReason, string sRemarks)
868 {
869 //Returns "" if deletion successful
870 //Otherwise, returns reason for failure
871
872 string sClinicCancelled = (bClinicCancelled == true) ? "C" : "PC";
873 string sReasonID = nReason.ToString();
874 string sSql = "BSDX CANCEL APPOINTMENT^" + nApptID.ToString();
875 sSql += "^" + sClinicCancelled;
876 sSql += "^" + sReasonID;
877 sSql += "^" + sRemarks;
878 DataTable dtAppt = m_DocManager.RPMSDataTable(sSql, "DeleteAppointment");
879
880 Debug.Assert(dtAppt.Rows.Count == 1);
881 DataRow r = dtAppt.Rows[0];
882 string sErrorID = r["ERRORID"].ToString();
883 if (sErrorID != "")
884 return sErrorID;
885
886 if (this.m_appointments.AppointmentTable.ContainsKey(nApptID))
887 {
888 this.m_appointments.RemoveAppointment(nApptID);
889
890 // View responsible for deciding to redraw the grid; not the document now.
891 //bool bRet = RefreshAvailabilitySchedule();
892 //UpdateAllViews();
893 }
894 return "";
895 }
896
897 public string AutoRebook(CGAppointment a, int nSearchType, int nMinimumDays, int nMaximumDays, out CGAppointment aRebook)
898 {
899 //If successful Returns "1" and new start date and time returned in aRebook
900 //Otherwise, returns error message
901
902 CGAppointment aCopy = new CGAppointment();
903 aCopy.CreateAppointment(a.StartTime, a.EndTime, a.Note, 0, a.Resource);
904 aCopy.PatientID = a.PatientID;
905 aCopy.PatientName = a.PatientName;
906 aCopy.HealthRecordNumber = a.HealthRecordNumber;
907 aCopy.AccessTypeID = a.AccessTypeID;
908 aRebook = aCopy;
909
910 //Determine Rebook access type
911 //nSearchType = -1: use current, -2: use any non-zero type, >0 use this access type id
912 int nAVType = 0;
913
914 switch (nSearchType)
915 {
916 case -1:
917 nAVType = a.AccessTypeID;
918 break;
919 case -2:
920 nAVType = 0;
921 break;
922 default:
923 nAVType = nSearchType;
924 break;
925 }
926
927 int nSlots = 0;
928 string sSlots = "";
929 int nAccessTypeID; //To compare with nAVType
930
931 DateTime dResult = new DateTime(); //StartTime of access block to autorebook into
932
933 //Next two are empty, but needed to pass to CreateAvailabilitySchedule
934 ArrayList alAccessTypes = new ArrayList();
935 string sSearchInfo = "";
936
937 //Find the StartTime of first availability block of this type for this clinic
938 //between nMinimumDays and nMaximumDays
939
940 string sAVStart = a.StartTime.AddDays(nMinimumDays).ToString("M/d/yyyy@H:mm");
941
942 //dtAVEnd is the last day to search
943 DateTime dtAVEnd = a.StartTime.AddDays(nMinimumDays + nMaximumDays);
944 string sAVEnd = dtAVEnd.ToString("M/d/yyyy@H:mm");
945
946 //Increment start day to search a week (or so) at a time
947 //30 is a test increment. Need to test different values for performance
948 int nIncrement = (nMaximumDays < 30) ? nMaximumDays : 30;
949
950 //nCount and nCountEnd are the 'moving' counters
951 //that I add to start and end to get the bracket
952 //At the beginning of the DO loop, nCount and nCountEnd are already set
953 bool bFinished = false;
954 bool bFound = false;
955
956 DateTime dStart = a.StartTime.AddDays(nMinimumDays);
957 // v 1.3 i18n support - FM Date passed insated of American Date
958 string sStart = FMDateTime.Create(dStart).DateOnly.FMDateString;
959 DateTime dEnd = dStart.AddDays(nIncrement);
960 do
961 {
962 string sSql = "BSDX REBOOK NEXT BLOCK^" + sStart + "^" + a.Resource + "^" + nAVType.ToString();
963 DataTable dtNextBlock = this.DocManager.RPMSDataTable(sSql, "NextBlock");
964 Debug.Assert(dtNextBlock.Rows.Count == 1);
965 DataRow drNextBlockRow = dtNextBlock.Rows[0];
966
967 object oNextBlock;
968 oNextBlock = drNextBlockRow["NEXTBLOCK"];
969 if (oNextBlock.GetType() == typeof(System.DBNull))
970 break;
971 DateTime dNextBlock = (DateTime)drNextBlockRow["NEXTBLOCK"];
972 if (dNextBlock > dtAVEnd)
973 {
974 break;
975 }
976
977 dStart = dNextBlock;
978 dEnd = dStart.AddDays(nIncrement);
979 if (dEnd > dtAVEnd)
980 dEnd = dtAVEnd;
981
982 DataTable dtResult = CGSchedLib.CreateAvailabilitySchedule(m_DocManager, this.Resources, dStart, dEnd, alAccessTypes, ScheduleType.Resource, sSearchInfo);
983 //Loop thru dtResult looking for a slot having the required availability type.
984 //If found, set bFinished = true;
985 foreach (DataRow dr in dtResult.Rows)
986 {
987
988 sSlots = dr["SLOTS"].ToString();
989 if (sSlots == "")
990 sSlots = "0";
991 nSlots = Convert.ToInt16(sSlots);
992 if (nSlots > 0)
993 {
994 nAccessTypeID = 0; //holds the access type id of the availability block
995 if (dr["ACCESS_TYPE"].ToString() != "")
996 nAccessTypeID = Convert.ToInt16(dr["ACCESS_TYPE"].ToString());
997 if ((nSearchType == -2) && (nAccessTypeID > 0)) //Match on any non-zero type
998 {
999 bFinished = true;
1000 bFound = true;
1001 dResult = (DateTime)dr["START_TIME"];
1002 break;
1003 }
1004 if (nAccessTypeID == nAVType)
1005 {
1006 bFinished = true;
1007 bFound = true;
1008 dResult = (DateTime)dr["START_TIME"];
1009 break;
1010 }
1011 }
1012 }
1013 dStart = dEnd.AddDays(1);
1014 dEnd = dStart.AddDays(nIncrement);
1015 if (dEnd > dtAVEnd)
1016 dEnd = dtAVEnd;
1017 } while (bFinished == false);
1018
1019 if (bFound == true)
1020 {
1021 aCopy.StartTime = dResult;
1022 aCopy.EndTime = dResult.AddMinutes(a.Duration);
1023 //Create the appointment
1024 //Set the AUTOREBOOKED flag
1025 //and store the "AutoRebooked To DateTime"
1026 //in each autorebooked appointment
1027 this.CreateAppointment(aCopy);
1028 SetAutoRebook(a, dResult);
1029 return "1";
1030 }
1031 else
1032 {
1033 return "0";
1034 }
1035 }
1036
1037 private void SetAutoRebook(CGAppointment a, DateTime dtRebookedTo)
1038 {
1039 string sApptKey = a.AppointmentKey.ToString();
1040 //string sRebookedTo = dtRebookedTo.ToString("M/d/yyyy@HH:mm");
1041 // i18n
1042 string sRebookedTo = FMDateTime.Create(dtRebookedTo).FMDateString;
1043 string sSql = "BSDX REBOOK SET^" + sApptKey + "^" + sRebookedTo;
1044 System.Data.DataTable dtRebook = m_DocManager.RPMSDataTable(sSql, "AutoRebook");
1045
1046 }
1047
1048 public string AppointmentNoShow(int nApptID, bool bNoShow)
1049 {
1050 /*
1051 * BSDX NOSHOW RPC Returns 1 in ERRORID if successfully sets NOSHOW flag in BSDX APPOINTMENT and, if applicable, File 2
1052 *Otherwise, returns negative numbers for failure and errormessage in ERRORTXT
1053 *
1054 */
1055
1056 string sTest = bNoShow.ToString();
1057 string sNoShow = (bNoShow == true) ? "1" : "0";
1058 string sSql = "BSDX NOSHOW^" + nApptID.ToString();
1059 sSql += "^";
1060 sSql += sNoShow;
1061
1062 DataTable dtAppt = m_DocManager.RPMSDataTable(sSql, "AppointmentNoShow");
1063
1064 Debug.Assert(dtAppt.Rows.Count == 1);
1065 DataRow r = dtAppt.Rows[0];
1066 string sErrorID = r["ERRORID"].ToString();
1067 if (sErrorID != "1")
1068 {
1069 return r["ERRORTEXT"].ToString();
1070 }
1071
1072 bool bRet = RefreshSchedule();
1073
1074 return sErrorID;
1075 }
1076
1077 #endregion Methods
1078
1079 }//End class
1080}//End namespace
Note: See TracBrowser for help on using the repository browser.