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
RevLine 
[614]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{
[1071]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
[614]31
[1071]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 }
[614]50
51
[1071]52 #region Properties
[614]53
[1071]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 }
[614]64
[1071]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 }
[614]79
[1071]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 }
[614]90
[1071]91 public CGDocumentManager DocManager
92 {
93 get
94 {
95 return m_DocManager;
96 }
97 set
98 {
99 m_DocManager = value;
100 }
101 }
[614]102
[1071]103 /// <summary>
104 /// Contains the hashtable of appointments
105 /// </summary>
106 public CGAppointments Appointments
107 {
108 get
109 {
110 return m_appointments;
111 }
112 }
[614]113
[1071]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 }
[614]128
[1071]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 }
[614]139
[1071]140 public string DocName
141 {
142 get
143 {
144 return this.m_sDocName;
145 }
146 set
147 {
148 this.m_sDocName = value;
149 }
150 }
[614]151
[1071]152 #endregion
[614]153
[1071]154 #region Methods
[614]155
[1071]156 public void UpdateAllViews()
157 {
158 //iterate through all views and call update.
159 Hashtable h = CGDocumentManager.Current.Views;
[614]160
[1071]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 }
[614]170
[1071]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
[821]199 //Nice to know that it gets set here!!!
[1071]200 m_dLastRefresh = DateTime.Now;
[614]201
[1071]202 this.m_appointments.ClearAllAppointments();
[614]203
[850]204 // calls RPC to get appointments
[1071]205 rAppointmentSchedule = CGSchedLib.CreateAppointmentSchedule(m_DocManager, m_sResourcesArray, this.m_dStartDate, this.m_dEndDate);
206
[821]207 // Datatable dumper into Debug Log (nice to know that this exists)
208 CGSchedLib.OutputArray(rAppointmentSchedule, "rAppointmentSchedule");
[614]209
210
[1071]211 foreach (DataRow r in rAppointmentSchedule.Rows)
212 {
[614]213
[1071]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();
[614]227
[1071]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;
[614]239
[1071]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);
[614]253
[1071]254 }
[614]255
[1071]256 return true;
257 }
258 catch (Exception Ex)
259 {
260 Debug.Write("CGDocument.RefreshDaysSchedule error: " + Ex.Message + "\n");
261 return false;
262 }
263 }
[614]264
265
[1071]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 }
[614]271
[1073]272 //sam: This is a test that duplicates RefreshDocument, but without the UpdateAllViews,
273 // as that has to be done synchornously.
[1083]274 //XXX: Needs to be refactored obviously, but now for testing.
275 //XXX: Tested extensively enough. Less refactoring now. 2011-01-26
[1073]276 public void RefreshDocumentAsync()
277 {
[1083]278 Debug.WriteLine("IN REFERSH DOCUMENT ASYNC\n\n");
279
[1073]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 }
[1071]295
[1073]296 bRet = RefreshSchedule();
297 }
298
299
[1071]300 public void RefreshDocument()
301 {
[1070]302 bool bRet = false;
[1071]303 if (m_sResourcesArray.Count == 0)
304 return;
[1070]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 }
[614]317
[1071]318 bRet = RefreshSchedule();
[1073]319
[1071]320 this.UpdateAllViews();
321 }
[614]322
[1071]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;
[614]330
[1071]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 }
[614]345
[1071]346 bRet = RefreshSchedule();
[614]347
[1071]348 CGView view = null;
349 //If this document already has a view, the use it
[1070]350 //SAM: Why do this again???
[1071]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 }
[614]365
[1071]366 //Otherwise, create new View
367 if (bReuseView == false)
368 {
369 view = new CGView();
[614]370
[1071]371 view.InitializeDocView(this,
372 this.DocManager,
373 m_dStartDate,
374 this.DocName);
[614]375
[1071]376 view.Show();
377 view.SyncTree();
[614]378
[1071]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 }
[614]391
[1065]392 /// <summary>
393 /// Refreshes Availablility and Schedules from RPMS.
394 /// </summary>
395 /// <returns>Success or Failure. Should be always Success.</returns>
[1071]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 }
[614]419
[1071]420 private bool RefreshAvailabilitySchedule()
421 {
422 try
423 {
424 if (this.m_DocManager.ConnectInfo.Connected == false)
425 {
426 m_DocManager.ConnectInfo.LoadConnectInfo();
427 }
[614]428
[1071]429 ArrayList saryApptTypes = new ArrayList();
430 int nApptTypeID = 0;
[614]431
[1071]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");
[614]436
[1071]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");
[614]455
[1071]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;
[614]466
[1083]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)
[1071]472 {
[1083]473 m_pAvArray.Clear();
[614]474
[1083]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"];
[614]480
[1083]481 //TODO: Fix this slots datatype problem
482 string sSlots = rTemp["SLOTS"].ToString();
483 nSlots = Convert.ToInt16(sSlots);
[614]484
[1083]485 sResourceList = rTemp["RESOURCE"].ToString();
486 sAccessRuleList = rTemp["ACCESS_TYPE"].ToString();
[614]487
[1083]488 string sNote = rTemp["NOTE"].ToString();
489
490 if ((nSlots < -1000) || (sAccessRuleList == ""))
[1071]491 {
[1083]492 nApptTypeID = 0;
493 }
494 else
495 {
496 foreach (DataRow rType in rTypeSchedule.Rows)
497 {
[614]498
[1083]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))
[1071]504 {
[1083]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 }
[1071]520 }
[1083]521 }//end foreach datarow rType
522 }
[614]523
524
[1083]525 AddAvailability(dStart, dEnd, nApptTypeID, nSlots, false, sResourceList, sAccessRuleList, sNote);
526 }//end foreach datarow rTemp
527 }//end lock
[1071]528 return true;
529 }
530 catch (Exception ex)
531 {
532 Debug.Write("CGDocument.RefreshAvailabilitySchedule error: " + ex.Message + "\n");
533 return false;
534 }
535 }
[614]536
[1071]537 private int GetTotalMinutes(DateTime dDate)
538 {
539 return ((dDate.Hour * 60) + dDate.Minute);
540 }
[614]541
[1083]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>
[1071]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
[614]558
[1071]559 CGAvailability pNewAv = new CGAvailability();
560 pNewAv.Create(StartTime, EndTime, nType, nSlots, sResourceList, sAccessRuleList);
[614]561
[1071]562 pNewAv.Note = sNote;
[614]563
[1071]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;
[614]583
[1071]584 string sName = dRow["ACCESS_TYPE_NAME"].ToString();
585 pNewAv.AccessTypeName = sName;
586 }
[614]587
[1071]588 int nIndex = 0;
589 nIndex = m_pAvArray.Add(pNewAv);
590 if (UpdateView == true)
591 {
592 this.UpdateAllViews();
593 }
594 return nIndex;
595 }
[614]596
[1071]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 }
[614]603
[1071]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;
[614]619
[1083]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)
[1071]631 {
[1083]632 //loop thru m_pAvArray
633 //Compare the start time and end time of eachblock
634 while (i < m_pAvArray.Count)
[1071]635 {
[1083]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)))
[1071]641 {
[1083]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)
[1071]647 {
[1083]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;
[614]659
[1083]660 }
661 }//end if
662 }//end if
663 i++;
664 }//end while
665 }//end lock
666
[1071]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.
[886]679 /// If 5 columns, begin on Second Day of Week
[1071]680 /// If 7 Columns, begin on First Day of Week
[886]681 /// (this is a change from the hardcoded behavior for US-based calendars)
[1071]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;
[908]691 // Week start based on thread locale
692 int nStartWeekDay = (int)System.Threading.Thread.CurrentThread.CurrentCulture.DateTimeFormat.FirstDayOfWeek;
[913]693 // Current Day
[1071]694 int nWeekDay = (int)SelectedDate.DayOfWeek; //0 == Sunday
[614]695
[1071]696 // this offset gets approrpriate day based on locale.
[886]697 int nOff = (nStartWeekDay + 1) % 7;
[1071]698 TimeSpan ts = new TimeSpan(nWeekDay - nOff, 0, 0, 0); //d,h,m,s
[614]699
[913]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.
[1071]702 if (ts < new TimeSpan()) ts = ts + new TimeSpan(7, 0, 0, 0);
[913]703
[1071]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;
[913]708 WeekEndDay = WeekStartDay + ts;
[1071]709 }
[913]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)
[1071]714 WeekStartDay = (nWeekDay == nStartWeekDay) ? SelectedDate + new TimeSpan(1, 0, 0, 0) : SelectedDate - ts;
[913]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.
[1071]723 WeekStartDay = (nWeekDay == nStartWeekDay) ? SelectedDate : SelectedDate - ts - new TimeSpan(1, 0, 0, 0);
[913]724 // End day calculation
725 int nEnd = 1;
726 ts = new TimeSpan((7 * nWeeks) - nEnd, 23, 59, 59);
727 WeekEndDay = WeekStartDay + ts;
728 }
729
[1071]730 bool bRet = ((WeekStartDay.Date != OldStartDay.Date) || (WeekEndDay.Date != OldEndDay.Date));
731 return bRet;
732 }
[614]733
[1071]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 }
[614]745
[1071]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;
[864]761
[1071]762 //sStart = rApptInfo.StartTime.ToString("M-d-yyyy@HH:mm");
763 //sEnd = rApptInfo.EndTime.ToString("M-d-yyyy@HH:mm");
764
[864]765 // i18n code -- Use culture neutral FMDates
766 sStart = FMDateTime.Create(rApptInfo.StartTime).FMDateString;
767 sEnd = FMDateTime.Create(rApptInfo.EndTime).FMDateString;
768
[1071]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 }
[614]782
[1071]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;
[1083]789 aCopy.WalkIn = bWalkin ? true : false;
[614]790
[1071]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;
[614]794
[1071]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))
[1084]801 {
[1071]802 throw new Exception(sErrorID);
[1084]803 }
[1071]804 aCopy.AppointmentKey = nApptID;
805 this.m_appointments.AddAppointment(aCopy);
[1083]806
807
808 //Have make appointment from CGView responsible for requesting an update for the avialability.
809 //bool bRet = RefreshAvailabilitySchedule();
[614]810
[1083]811 //Sam: don't think this is needed as it is called from CGView.
812 //Make CGView responsible for all drawing.
813 //UpdateAllViews();
[614]814
[1071]815 return nApptID;
816 }
[614]817
[1071]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;
[614]824
[1071]825 System.Data.DataTable dtAppt = m_DocManager.RPMSDataTable(sSql, "EditAppointment");
[614]826
[1071]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;
[614]832
[1071]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 }
[614]844
[1071]845 public void CheckInAppointment(int nApptID, DateTime dCheckIn)
846 {
847 string sCheckIn = FMDateTime.Create(dCheckIn).FMDateString;
848
[1062]849 string sSql = "BSDX CHECKIN APPOINTMENT^" + nApptID.ToString() + "^" + sCheckIn; // +"^";
[1071]850 //sSql += ClinicStopIEN + "^" + ProviderIEN + "^" + PrintRouteSlip + "^";
851 //sSql += PCCClinicIEN + "^" + PCCFormIEN + "^" + PCCOutGuide;
[614]852
[1071]853 System.Data.DataTable dtAppt = m_DocManager.RPMSDataTable(sSql, "CheckInAppointment");
[614]854
[1071]855 Debug.Assert(dtAppt.Rows.Count == 1);
856 DataRow r = dtAppt.Rows[0];
857 string sErrorID = r["ERRORID"].ToString();
[614]858
[1083]859
[1071]860 }
[614]861
[1071]862 public string DeleteAppointment(int nApptID)
863 {
864 return DeleteAppointment(nApptID, true, 0, "");
865 }
[614]866
[1071]867 public string DeleteAppointment(int nApptID, bool bClinicCancelled, int nReason, string sRemarks)
868 {
869 //Returns "" if deletion successful
870 //Otherwise, returns reason for failure
[614]871
[1071]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");
[614]879
[1071]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;
[614]885
[1071]886 if (this.m_appointments.AppointmentTable.ContainsKey(nApptID))
887 {
888 this.m_appointments.RemoveAppointment(nApptID);
[1084]889
890 // View responsible for deciding to redraw the grid; not the document now.
891 //bool bRet = RefreshAvailabilitySchedule();
892 //UpdateAllViews();
[1071]893 }
894 return "";
895 }
[614]896
[1071]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
[614]901
[1071]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;
[614]909
[1071]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;
[614]913
[1071]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 }
[614]926
[1071]927 int nSlots = 0;
928 string sSlots = "";
929 int nAccessTypeID; //To compare with nAVType
[614]930
[1071]931 DateTime dResult = new DateTime(); //StartTime of access block to autorebook into
[614]932
[1071]933 //Next two are empty, but needed to pass to CreateAvailabilitySchedule
934 ArrayList alAccessTypes = new ArrayList();
935 string sSearchInfo = "";
[614]936
[1071]937 //Find the StartTime of first availability block of this type for this clinic
938 //between nMinimumDays and nMaximumDays
[614]939
[1071]940 string sAVStart = a.StartTime.AddDays(nMinimumDays).ToString("M/d/yyyy@H:mm");
[614]941
[1071]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);
[864]957 // v 1.3 i18n support - FM Date passed insated of American Date
958 string sStart = FMDateTime.Create(dStart).DateOnly.FMDateString;
[1071]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];
[614]966
[1071]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 }
[614]976
[1071]977 dStart = dNextBlock;
978 dEnd = dStart.AddDays(nIncrement);
979 if (dEnd > dtAVEnd)
980 dEnd = dtAVEnd;
[614]981
[1071]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 {
[614]987
[1071]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);
[614]1018
[1071]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");
[864]1041 // i18n
1042 string sRebookedTo = FMDateTime.Create(dtRebookedTo).FMDateString;
[1071]1043 string sSql = "BSDX REBOOK SET^" + sApptKey + "^" + sRebookedTo;
1044 System.Data.DataTable dtRebook = m_DocManager.RPMSDataTable(sSql, "AutoRebook");
[614]1045
[1071]1046 }
[614]1047
[1071]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 */
[614]1055
[1071]1056 string sTest = bNoShow.ToString();
1057 string sNoShow = (bNoShow == true) ? "1" : "0";
1058 string sSql = "BSDX NOSHOW^" + nApptID.ToString();
1059 sSql += "^";
1060 sSql += sNoShow;
[614]1061
[1071]1062 DataTable dtAppt = m_DocManager.RPMSDataTable(sSql, "AppointmentNoShow");
[614]1063
[1071]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 }
[614]1071
[1071]1072 bool bRet = RefreshSchedule();
[614]1073
[1071]1074 return sErrorID;
1075 }
1076
1077 #endregion Methods
1078
1079 }//End class
[614]1080}//End namespace
Note: See TracBrowser for help on using the repository browser.