source: Scheduling/trunk/cs/bsdx0200GUISourceCode/CGAVDocument.cs@ 1731

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

CGAVDocument: Don't Update Grid. Leave that to CGAVView.
CGAVView: When updating grid, make a copy of appointments, and use the copy, so there won't be clashes when updating appointments in the background.
CGDocument: Minor Changes in AppointmentNoShow; New method AppointmentUndoCheckIn
CGDocumentManager: Resequenced load order b/c UserPreferences now talks to DB.
DAL: New Methods to save autoprint preferences; new delegate to enable some async stuff
DCheckIn & DAppointPage: _myCodeIsFiringIstheCheckBoxChangedEvent added to protect against firing event when setting checkbox in the code.
DPatientLetter: No changes.
UserPreferences: Now gets and saves user preferences from DB.

File size: 16.4 KB
Line 
1using System;
2using System.Collections;
3using System.Data;
4//using 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 array of availability blocks for a scheduling resource
14 /// </summary>
15 public class CGAVDocument : System.Object
16 {
17 public CGAVDocument()
18 {
19 m_AVBlocks = new CGAppointments();
20 m_sResourcesArray = new ArrayList();
21 }
22
23 #region Member Variables
24
25 public int m_nColumnCount; //todo: this should point to the view's member for column count
26 public int m_nTimeUnits;
27 public string m_sSecondary;
28 private string m_sDocName;
29 public ArrayList m_sResourcesArray;
30 public ScheduleType m_ScheduleType;
31 private DateTime m_dSelectedDate; //Holds the user's selection from the dtpicker
32 private DateTime m_dStartDate; //Beginning date of document data
33 private DateTime m_dEndDate; //Ending date of document data
34 public CGAppointments m_AVBlocks;
35 private CGDocumentManager m_DocManager;
36 private int m_nDocID; //Resource IEN in ^BSDXRES
37
38 #endregion
39
40 #region Properties
41
42 /// <summary>
43 /// Resource IEN in ^BSDXRES
44 /// </summary>
45 public int ResourceID
46 {
47 get
48 {
49 return m_nDocID;
50 }
51 set
52 {
53 m_nDocID = value;
54 }
55 }
56
57 /// <summary>
58 /// The list of Resource names
59 /// </summary>
60 public ArrayList Resources
61 {
62 get
63 {
64 return this.m_sResourcesArray;
65 }
66 set
67 {
68 this.m_sResourcesArray = value;
69 }
70 }
71
72 public CGDocumentManager DocManager
73 {
74 get
75 {
76 return m_DocManager;
77 }
78 set
79 {
80 m_DocManager = value;
81 }
82 }
83
84 /// <summary>
85 /// Contains the hashtable of Availability Blocks
86 /// </summary>
87 public CGAppointments AVBlocks
88 {
89 get
90 {
91 return m_AVBlocks;
92 }
93 set
94 {
95 m_AVBlocks = value;
96 }
97 }
98
99 /// <summary>
100 /// Holds the date selected by the user in CGView.dateTimePicker1
101 /// </summary>
102 public DateTime SelectedDate
103 {
104 get
105 {
106 return this.m_dSelectedDate;
107 }
108 set
109 {
110 this.m_dSelectedDate = value;
111 bool bRet = false;
112 if (m_ScheduleType == ScheduleType.Resource)
113 {
114 bRet = this.WeekNeedsRefresh(1, m_dSelectedDate, out this.m_dStartDate, out this.m_dEndDate);
115 }
116 else
117 {
118 this.m_dStartDate = m_dSelectedDate;
119 this.m_dEndDate = m_dSelectedDate;
120 this.m_dEndDate = this.m_dEndDate.AddHours(23);
121 this.m_dEndDate = this.m_dEndDate.AddMinutes(59);
122 this.m_dEndDate = this.m_dEndDate.AddSeconds(59);
123 }
124
125 bRet = this.RefreshDaysSchedule();
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 ChangeAppointmentTime(CGAppointment pAppt, DateTime dNewStart, DateTime dNewEnd, string sResource)
157 {
158 try
159 {
160 DateTime dOldStart = pAppt.StartTime;
161 DateTime dOldEnd = pAppt.EndTime;
162 if ((dOldStart == dNewStart) && (dOldEnd == dNewEnd))
163 { //no change in time
164 return;
165 }
166
167 foreach (CGAppointment a in m_AVBlocks.AppointmentTable.Values)
168 {
169 DateTime sStart2 = a.StartTime;
170 DateTime sEnd2 = a.EndTime;
171 if ((a.AppointmentKey != pAppt.AppointmentKey) && (CalendarGrid.TimesOverlap(dNewStart, dNewEnd, a.StartTime, a.EndTime)))
172 {
173 MessageBox.Show("Access blocks may not overlap.","Clinical Scheduling", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
174 return;
175 }
176 }
177
178 this.DeleteAvailability(pAppt.AppointmentKey);
179 pAppt.StartTime = dNewStart;
180 pAppt.EndTime = dNewEnd;
181 pAppt.Resource = sResource;
182 this.CreateAppointment(pAppt);
183
184 }
185 catch(Exception e)
186 {
187 MessageBox.Show("CGDocument::ChangeAppointmentTime failed: " + e.Message);
188 return;
189 }
190 }
191
192 public void DeleteAvailability(int nApptID)
193 {
194 if (this.m_AVBlocks.AppointmentTable.ContainsKey(nApptID))
195 {
196 string sSql = "BSDX CANCEL AVAILABILITY^" + nApptID.ToString();
197 DataTable dtAppt =m_DocManager.RPMSDataTable(sSql, "DeleteAvailability");
198 int nErrorID;
199 Debug.Assert(dtAppt.Rows.Count == 1);
200 DataRow r = dtAppt.Rows[0];
201 nErrorID = Convert.ToInt32(r["ERRORID"]);
202 this.m_AVBlocks.RemoveAppointment(nApptID);
203 UpdateAllViews();
204 }
205 }
206
207 /// <summary>
208 /// Called by LoadTemplate to create Access Block
209 /// Returns the IEN of the availability block in the RPMS BSDX AVAILABILITY file.
210 /// </summary>
211 public int CreateAppointmentAuto(CGAppointment rApptInfo)
212 {
213 try
214 {
215 string sStart;
216 string sEnd;
217 string sResource;
218 string sNote;
219 string sTypeID;
220 string sSlots;
221
222 //sStart = rApptInfo.StartTime.ToString("M-d-yyyy@HH:mm");
223 //sEnd = rApptInfo.EndTime.ToString("M-d-yyyy@HH:mm");
224 // i18n support
225 sStart = FMDateTime.Create(rApptInfo.StartTime).FMDateString;
226 sEnd = FMDateTime.Create(rApptInfo.EndTime).FMDateString;
227 sNote = rApptInfo.Note;
228 sResource = rApptInfo.Resource;
229 sTypeID = rApptInfo.AccessTypeID.ToString();
230 sSlots = rApptInfo.Slots.ToString();
231
232 CGAppointment aCopy = new CGAppointment();
233 aCopy.CreateAppointment(rApptInfo.StartTime, rApptInfo.EndTime, sNote, 0, sResource);
234 aCopy.AccessTypeID = rApptInfo.AccessTypeID;
235 aCopy.Slots = rApptInfo.Slots;
236 aCopy.IsAccessBlock = true;
237
238 string sSql = "BSDX ADD NEW AVAILABILITY^" + sStart + "^" + sEnd + "^" + sTypeID + "^" + sResource + "^" + sSlots + "^" + sNote;
239 DataTable dtAppt = m_DocManager.RPMSDataTable(sSql, "NewAvailability");
240
241 int nApptID;
242 int nErrorID;
243
244 Debug.Assert(dtAppt.Rows.Count == 1);
245 DataRow r = dtAppt.Rows[0];
246 nApptID =Convert.ToInt32(r["AVAILABILITYID"]);
247 nErrorID = Convert.ToInt32(r["ERRORID"]);
248 if (nErrorID != -1)
249 {
250 throw new Exception("VistA Error");
251 }
252 Debug.Write("CreateAvailabilityAuto -- new AV block created\n");
253
254 //UpdateAllViews();
255
256 aCopy.AppointmentKey = nApptID;
257 return nApptID;
258 }
259 catch (Exception ex)
260 {
261 Debug.Write("CGAVDocument.CreateAppointmentAuto Failed: " + ex.Message);
262 return -1;
263 }
264 }
265
266
267 public int CreateAppointment(CGAppointment rApptInfo)
268 {
269 try
270 {
271 string sStart;
272 string sEnd;
273 string sResource;
274 string sNote;
275 string sTypeID;
276 string sSlots;
277
278 //sStart = rApptInfo.StartTime.ToString("M-d-yyyy@HH:mm");
279 //sEnd = rApptInfo.EndTime.ToString("M-d-yyyy@HH:mm");
280 // i18n support
281 sStart = FMDateTime.Create(rApptInfo.StartTime).FMDateString;
282 sEnd = FMDateTime.Create(rApptInfo.EndTime).FMDateString;
283 sNote = rApptInfo.Note;
284 sResource = rApptInfo.Resource;
285 sTypeID = rApptInfo.AccessTypeID.ToString();
286 sSlots = rApptInfo.Slots.ToString();
287
288 CGAppointment aCopy = new CGAppointment();
289 aCopy.CreateAppointment(rApptInfo.StartTime, rApptInfo.EndTime, sNote, 0, sResource);
290 aCopy.AccessTypeID = rApptInfo.AccessTypeID;
291 aCopy.Slots = rApptInfo.Slots;
292 aCopy.IsAccessBlock = true;
293
294 aCopy.AccessTypeName = this.AccessNameFromID(aCopy.AccessTypeID);
295
296 foreach (CGAppointment a in this.m_AVBlocks.AppointmentTable.Values)
297 {
298 DateTime sStart2 = a.StartTime;
299 DateTime sEnd2 = a.EndTime;
300 if (CalendarGrid.TimesOverlap(aCopy.StartTime, aCopy.EndTime, a.StartTime, a.EndTime))
301 {
302 // throw new Exception("Access blocks may not overlap.");
303 MessageBox.Show("Access blocks may not overlap.","Clinical Scheduling", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
304 return -1;
305 }
306
307 }
308
309 string sSql = "BSDX ADD NEW AVAILABILITY^" + sStart + "^" + sEnd + "^" + sTypeID + "^" + sResource + "^" + sSlots + "^" + sNote;
310 DataTable dtAppt =m_DocManager.RPMSDataTable(sSql, "NewAvailability");
311
312 int nApptID;
313 int nErrorID;
314
315 Debug.Assert(dtAppt.Rows.Count == 1);
316 DataRow r = dtAppt.Rows[0];
317 nApptID =Convert.ToInt32(r["AVAILABILITYID"]);
318 nErrorID = Convert.ToInt32(r["ERRORID"]);
319 if (nErrorID != -1)
320 {
321 throw new Exception("VistA Error");
322 }
323 Debug.Write("CreateAvailability -- new AV block created\n");
324
325 aCopy.AppointmentKey = nApptID;
326
327 this.m_AVBlocks.AddAppointment(aCopy);
328
329 UpdateAllViews();
330
331 return nApptID;
332 }
333 catch (Exception ex)
334 {
335 Debug.Write("CGAVDocument.CreateAppointment Failed: " + ex.Message);
336 return -1;
337 }
338 }
339
340 private int GetTotalMinutes(DateTime dDate)
341 {
342 return ((dDate.Hour * 60) + dDate.Minute);
343 }
344
345 private string AccessNameFromID(int nAccessTypeID)
346 {
347 DataView dvTypes = new DataView(this.DocManager.GlobalDataSet.Tables["AccessTypes"]);
348 dvTypes.Sort = "BMXIEN ASC";
349 int nRow = dvTypes.Find(nAccessTypeID);
350 DataRowView drv = dvTypes[nRow];
351 string sAccessTypeName = drv["ACCESS_TYPE_NAME"].ToString();
352 return sAccessTypeName;
353 }
354
355
356 /// <summary>
357 /// Update availability block schedule based on info in RPMS
358 /// </summary>
359 public bool RefreshDaysSchedule()
360 {
361 DateTime dStart;
362 DateTime dEnd;
363 int nKeyID;
364 string sNote;
365 string sResource;
366 string sAccessType;
367 string sSlots;
368 CGAppointment pAppointment;
369 CGDocumentManager pApp = CGDocumentManager.Current;
370 DataTable rAppointmentSchedule;
371
372 this.m_AVBlocks.ClearAllAppointments();
373
374 ArrayList apptTypeIDs = new ArrayList();
375
376 rAppointmentSchedule = CGSchedLib.CreateAvailabilitySchedule(m_DocManager, m_sResourcesArray, this.m_dStartDate, this.m_dEndDate, apptTypeIDs,/* */ this.m_ScheduleType, "0");
377
378 foreach (DataRow r in rAppointmentSchedule.Rows)
379 {
380 nKeyID = Convert.ToInt32(r["AVAILABILITYID"].ToString());
381 if (nKeyID > 0)
382 {
383 dStart = (DateTime) r["START_TIME"];
384 dEnd = (DateTime) r["END_TIME"];
385 sNote = r["NOTE"].ToString();
386 sResource = r["RESOURCE"].ToString();
387 sAccessType = r["ACCESS_TYPE"].ToString();
388 sSlots = r["SLOTS"].ToString();
389
390 pAppointment = new CGAppointment();
391 pAppointment.CreateAppointment(dStart, dEnd, sNote, nKeyID, sResource);
392 pAppointment.AccessTypeID = Convert.ToInt16(sAccessType);
393 pAppointment.Slots = Convert.ToInt16(sSlots);
394 pAppointment.IsAccessBlock = true;
395 pAppointment.AccessTypeName = this.AccessNameFromID(pAppointment.AccessTypeID);
396
397 this.m_AVBlocks.AddAppointment(pAppointment);
398 }
399 }
400 return true;
401 }
402
403 /// <summary>
404 /// Given a selected date,
405 /// Calculates StartDay and End Day and returns them in output params.
406 /// nWeeks == number of Weeks to display
407 /// nColumnCount is number of days displayed per week.
408 /// If 5 columns, begin on Second Day of Week
409 /// If 7 Columns, begin on First Day of Week
410 /// (this is a change from the hardcoded behavior for US-based calendars)
411 ///
412 /// Returns TRUE if the document's data needs refreshing based on
413 /// this newly selected date.
414 /// </summary>
415 /// TODO: This is a duplicate of the method in CGDocument. We need to put them together.
416 public bool WeekNeedsRefresh(int nWeeks, DateTime SelectedDate,
417 out DateTime WeekStartDay, out DateTime WeekEndDay)
418 {
419 DateTime OldStartDay = m_dStartDate;
420 DateTime OldEndDay = m_dEndDate;
421 // Week start based on thread locale
422 int nStartWeekDay = (int)System.Threading.Thread.CurrentThread.CurrentCulture.DateTimeFormat.FirstDayOfWeek;
423 // Current Day
424 int nWeekDay = (int)SelectedDate.DayOfWeek; //0 == Sunday
425
426 // this offset gets approrpriate day based on locale.
427 int nOff = (nStartWeekDay + 1) % 7;
428 TimeSpan ts = new TimeSpan(nWeekDay - nOff, 0, 0, 0); //d,h,m,s
429
430 // if ts is negative, we will jump to the next week in the logic.
431 // to show the correct week, add 7. Confusing, I know.
432 if (ts < new TimeSpan()) ts = ts + new TimeSpan(7, 0, 0, 0);
433
434 if (m_nColumnCount == 1) // if one column start and end on the same day.
435 {
436 ts = new TimeSpan(0, 23, 59, 59);
437 WeekStartDay = SelectedDate;
438 WeekEndDay = WeekStartDay + ts;
439 }
440 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.
441 {
442 // if picked day is start of week (Sunday in US), start in the next day since that's the first day of work week
443 // else, just substract the calculated time span to get to the start of week (first work day)
444 WeekStartDay = (nWeekDay == nStartWeekDay) ? SelectedDate + new TimeSpan(1, 0, 0, 0) : SelectedDate - ts;
445 // End day calculation
446 int nEnd = 3;
447 ts = new TimeSpan((7 * nWeeks) - nEnd, 23, 59, 59);
448 WeekEndDay = WeekStartDay + ts;
449 }
450 else // if 7 column start at the 1st day of this week and end in 6:23:59:59 days.
451 {
452 // 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.
453 WeekStartDay = (nWeekDay == nStartWeekDay) ? SelectedDate : SelectedDate - ts - new TimeSpan(1, 0, 0, 0);
454 // End day calculation
455 int nEnd = 1;
456 ts = new TimeSpan((7 * nWeeks) - nEnd, 23, 59, 59);
457 WeekEndDay = WeekStartDay + ts;
458 }
459
460 bool bRet = ((WeekStartDay.Date != OldStartDay.Date) || (WeekEndDay.Date != OldEndDay.Date));
461 return bRet;
462 }
463
464 public void OnOpenDocument()
465 {
466 //Create new Document
467 // DateTime dStart;
468 // DateTime dEnd;
469
470 Debug.Assert(m_sResourcesArray.Count > 0);
471
472 m_sSecondary = "";
473
474 m_ScheduleType = (m_sResourcesArray.Count == 1) ? ScheduleType.Resource: ScheduleType.Clinic;
475
476 bool bRet = false;
477 //Set initial From and To dates based on current day
478 // DateTime dDate = new DateTime(2001,12,05); //test line
479 DateTime dDate = DateTime.Today;
480 if (m_ScheduleType == ScheduleType.Resource)
481 {
482 bRet = this.WeekNeedsRefresh(1,dDate, out this.m_dStartDate, out this.m_dEndDate);
483 }
484 else
485 {
486 this.m_dStartDate = dDate;
487 this.m_dEndDate = dDate;
488 this.m_dEndDate = this.m_dEndDate.AddHours(23);
489 this.m_dEndDate = this.m_dEndDate.AddMinutes(59);
490 this.m_dEndDate = this.m_dEndDate.AddSeconds(59);
491 }
492
493 bRet = this.RefreshDaysSchedule();
494
495 CGAVView view = null;
496 //If this document already has a view, the use it
497 Hashtable h = CGDocumentManager.Current.AvailabilityViews;
498 CGAVDocument d;
499 bool bReuseView = false;
500 foreach (CGAVView v in h.Keys)
501 {
502 d = (CGAVDocument) h[v];
503 if (d == this)
504 {
505 view = v;
506 bReuseView = true;
507 break;
508 }
509 }
510
511 //Otherwise, create new View
512 if (bReuseView == false)
513 {
514 view = new CGAVView();
515
516 view.DocManager = this.DocManager;
517 view.StartDate = m_dStartDate;
518
519 //Assign the document to the view
520 view.Document = this;
521
522 //Link the calendargrid's appointments table to the document's table
523 view.AVBlocks = this.AVBlocks;
524
525 view.Text = "Edit Availability - " + this.DocName;
526 view.Show();
527 }
528
529 this.UpdateAllViews();
530
531 }
532
533 public void AddResource(string sResource)
534 {
535 //TODO: Test that resource is not currently in list, that it IS a resource, etc
536 this.m_sResourcesArray.Add(sResource);
537 this.UpdateAllViews();
538 }
539
540 /// <summary>
541 /// Calls each AVview associated with this AVdocument and tells it to update itself
542 /// </summary>
543 public void UpdateAllViews()
544 {
545 //iterate through all views and call update.
546 Hashtable h = CGDocumentManager.Current.AvailabilityViews;
547
548 CGAVDocument d;
549 foreach (CGAVView v in h.Keys)
550 {
551 d = (CGAVDocument) h[v];
552 if (d == this)
553 {
554 v.UpdateArrays();
555 }
556 }
557 }
558
559 #endregion
560
561 }//End class
562}
Note: See TracBrowser for help on using the repository browser.