///<summary>Add a sheet the the device check-in checklist.</summary> public static void CI_AddSheet(long patNum, long sheetNum) { Patient pat = Patients.GetPat(patNum); if (pat == null) { return; } List <MobileAppDevice> listMads = MobileAppDevices.GetAll(patNum); if (listMads.IsNullOrEmpty()) { return; } Sheet sheet = Sheets.GetOne(sheetNum); if (sheet == null) { return; } //See CI_AddSheet for input details. foreach (MobileAppDevice mad in listMads) { SendPushBackground(PushType.CI_AddSheet, mad.MobileAppDeviceNum, listPrimaryKeys: new List <long>() { pat.PatNum, sheet.SheetNum }); } }
///<summary>Delete the Kiosk or MobileDevice.</summary> public void Delete() { if (IsKiosk) { TerminalActives.DeleteForCmptrSessionAndId(_computerKiosk.ComputerName, _computerKiosk.SessionId, processId: _computerKiosk.ProcessId); } else { MobileAppDevices.Delete(_mobileDevice.MobileAppDeviceNum); } }
private void butAdd_Click(object sender, EventArgs e) { FormSheetPicker FormS = new FormSheetPicker(); FormS.SheetType = SheetTypeEnum.PatientForm; FormS.ShowDialog(); if (FormS.DialogResult != DialogResult.OK) { return; } SheetDef sheetDef; Sheet sheet = null; //only useful if not Terminal bool isPatUsingEClipboard = MobileAppDevices.PatientIsAlreadyUsingDevice(PatNum); for (int i = 0; i < FormS.SelectedSheetDefs.Count; i++) { sheetDef = FormS.SelectedSheetDefs[i]; if (FormS.TerminalSend && isPatUsingEClipboard && !sheetDef.HasMobileLayout) { if (!MsgBox.Show(MsgBoxButtons.YesNo, $"The patient is currently using an eClipboard to fill out forms, but the " + $"{sheetDef.Description} sheet does not have a mobile layout and cannot be used with eClipboard. " + $"If you add this form to the patient's list it will not be shown in eClipboard. Do you still want to add this form?")) { continue; } } sheet = SheetUtil.CreateSheet(sheetDef, PatNum); SheetParameter.SetParameter(sheet, "PatNum", PatNum); SheetFiller.FillFields(sheet); SheetUtil.CalculateHeights(sheet); if (FormS.TerminalSend) { sheet.InternalNote = ""; //because null not ok sheet.ShowInTerminal = (byte)(Sheets.GetBiggestShowInTerminal(PatNum) + 1); Sheets.SaveNewSheet(sheet); //save each sheet. //Push new sheet to eClipboard. if (isPatUsingEClipboard && sheetDef.HasMobileLayout) { OpenDentBusiness.WebTypes.PushNotificationUtils.CI_AddSheet(sheet.PatNum, sheet.SheetNum); } } } if (FormS.TerminalSend) { //do not show a dialog now. User will need to click the terminal button. FillGrid(); Signalods.SetInvalid(InvalidType.Kiosk); } else if (sheet != null) { FormSheetFillEdit.ShowForm(sheet, FormSheetFillEdit_FormClosing); } }
public static void ShowKioskManagerIfNeeded(Appointment oldAppt, long newConfirmed) { long clinicNum = PrefC.HasClinicsEnabled ? oldAppt.ClinicNum : 0; if (!MobileAppDevices.IsClinicSignedUpForEClipboard(clinicNum)) { return; } //If they have eClipboard and want to pop up the kiosk on check-in, show the kiosk manager if (ClinicPrefs.GetBool(PrefName.EClipboardPopupKioskOnCheckIn, clinicNum) && newConfirmed != oldAppt.Confirmed && newConfirmed == PrefC.GetLong(PrefName.AppointmentTimeArrivedTrigger)) { FormTerminalManager formTM = new FormTerminalManager(); formTM.ShowDialog(); } }
///<summary>Called when user clicks on use defaults for clinic, AuthorizeTab, clinicPicker.SelectedIndexChanged, and CheckEClipboardCreateMissingForms_Click. It sets various areas enabled or disabled. Doesn't change the checked values.</summary> private void SetUIEClipboardEnabled() { //bool isClinicSignedUp=EClipboardDisplayAsEnabled(); bool isClinicSignedUp = MobileAppDevices.IsClinicSignedUpForEClipboard(_clinicNumEClipboardTab); if (PrefC.HasClinicsEnabled && _clinicNumEClipboardTab == 0) { isClinicSignedUp = Clinics.GetForUserod(Security.CurUser).Any(x => MobileAppDevices.IsClinicSignedUpForEClipboard(x.ClinicNum)); } bool notUsingDefaults = _clinicNumEClipboardTab == 0 || !checkEClipboardUseDefaults.Checked; bool enableSheets = checkEClipboardCreateMissingForms.Checked; checkEClipboardUseDefaults.Enabled = _clinicNumEClipboardTab != 0 && isClinicSignedUp && _eClipboardAllowEdit; groupEClipboardRules.Enabled = isClinicSignedUp && _eClipboardAllowEdit && notUsingDefaults; groupEClipboardSheets.Enabled = isClinicSignedUp && _eClipboardAllowEdit && notUsingDefaults && enableSheets; gridMobileAppDevices.Enabled = isClinicSignedUp; labelEClipboardNotSignedUp.Visible = !isClinicSignedUp; }
///<summary>Sets the PatNum for the selected Kiosk or MobileDevice.</summary> public void SetPatNum(long patNum) { if (IsKiosk) { TerminalActives.SetPatNum(_computerKiosk.TerminalActiveNum, patNum); Signalods.SetInvalid(InvalidType.Kiosk, KeyType.ProcessId, Process.GetCurrentProcess().Id); //signal the terminal manager to refresh its grid } else { MobileAppDevices.SetPatNum(_mobileDevice.MobileAppDeviceNum, patNum); if (patNum > 0) { OpenDentBusiness.WebTypes.PushNotificationUtils.CI_CheckinPatient(patNum, _mobileDevice.MobileAppDeviceNum); } else { OpenDentBusiness.WebTypes.PushNotificationUtils.CI_GoToCheckin(_mobileDevice.MobileAppDeviceNum); } } }
private void gridMobileAppDevices_CellClick(object sender, ODGridClickEventArgs e) { int indexOfEnabledColumn; if (PrefC.HasClinicsEnabled) { indexOfEnabledColumn = 4; } else { indexOfEnabledColumn = 3; } if (e.Col != indexOfEnabledColumn) //They did not select the right column. { return; } MobileAppDevice mobileAppDevice = gridMobileAppDevices.SelectedTag <MobileAppDevice>(); //There is not a tag somehow. if (mobileAppDevice == null) { return; } if (mobileAppDevice.IsAllowed) { if (!MsgBox.Show(MsgBoxButtons.YesNo, "This will immediately make the device unavailable to all other workstations. Continue?")) { return; } } mobileAppDevice.IsAllowed = !mobileAppDevice.IsAllowed; //Flip the bit. //Update the device because the signal processing of this form isn't friendly to keeping an in-memory list that syncs when the form closes MobileAppDevices.Update(mobileAppDevice); OpenDentBusiness.WebTypes.PushNotificationUtils.CI_IsAllowedChanged(mobileAppDevice.MobileAppDeviceNum, mobileAppDevice.IsAllowed); FillGridMobileAppDevices(); //Fill the grid to show the changes. }
///<summary>Remove a sheet from a device check-in checklist.</summary> public static void CI_RemoveSheet(long patNum, long sheetNum) { Patient pat = Patients.GetPat(patNum); //Inentionally not getting the actual Sheet from the db here. It may have already been deleted, now time to get it off the device. if (pat == null) { return; } List <MobileAppDevice> listMads = MobileAppDevices.GetAll(patNum); if (listMads.IsNullOrEmpty()) { return; } //See CI_RemoveSheet for input details. foreach (MobileAppDevice mad in listMads) { SendPushBackground(PushType.CI_RemoveSheet, mad.MobileAppDeviceNum, listPrimaryKeys: new List <long>() { pat.PatNum, sheetNum }); } }
///<summary>Only call this method from SendPushBackground() or SendPushAlert(). See summary of those methods for documentation. ///If runAsync==true then spawns a worker thread and makes the web call. This method is passive and returns void so no reason to wait for return.</summary> private static void SendPush(PushType pushType, bool isAlert, long mobileAppDeviceNum = 0, long clinicNum = 0, long userNum = 0, List <long> listPrimaryKeys = null, List <string> listTags = null, string alertTitle = null, string alertMessage = null, bool runAsync = true) { void execute() { eServiceCode eService = eServiceCode.EClipboard; switch (pushType) { case PushType.CI_CheckinPatient: case PushType.CI_AddSheet: case PushType.CI_RemoveSheet: case PushType.CI_GoToCheckin: case PushType.CI_NewEClipboardPrefs: case PushType.CI_IsAllowedChanged: case PushType.CI_TreatmentPlan: case PushType.CI_RemoveTreatmentPlan: eService = eServiceCode.EClipboard; break; case PushType.ODM_NewTextMessage: case PushType.ODM_LogoutODUser: eService = eServiceCode.MobileWeb; break; case PushType.None: default: throw new Exception("Unsupported PushType: " + pushType.ToString()); } PushNotificationPayload payload = new PushNotificationPayload { IsAlert = isAlert, AlertMessage = alertMessage ?? "", AlertTitle = alertTitle ?? "", UserNum = userNum, PushNotificationActionJSON = JsonConvert.SerializeObject(new PushNotificationAction() { TypeOfPush = pushType, ListPrimaryKeys = listPrimaryKeys ?? new List <long>(), ListTags = listTags ?? new List <string>() }, typeof(PushNotificationAction), new JsonSerializerSettings()) }; if (mobileAppDeviceNum > 0) //Send to one exact device. { MobileAppDevice mad = MobileAppDevices.GetOne(mobileAppDeviceNum); if (mad == null) { throw new Exception("MobileAppDeviceNum not found: " + mobileAppDeviceNum.ToString()); } payload.DeviceId = mad.UniqueID; payload.ClinicNum = mad.ClinicNum; } else { payload.ClinicNum = clinicNum; } //Validate that this clinic is signed up for eClipboard if the push is related to eClipboard if (eService == eServiceCode.EClipboard && !MobileAppDevices.IsClinicSignedUpForEClipboard(payload.ClinicNum)) { throw new Exception($"ClinicNum {payload.ClinicNum} is not signed up for eClipboard."); } //Validate that this clinic is signed up for MobileWeb if the push is related to ODMobile else if (eService == eServiceCode.MobileWeb && !MobileAppDevices.IsClinicSignedUpForMobileWeb(payload.ClinicNum)) { if (clinicNum > -1 || pushType != PushType.ODM_LogoutODUser) //Logout is allowed to be sent to non-specific clinicNum. All others are not. { throw new Exception($"ClinicNum {payload.ClinicNum} is not signed up for ODMobile."); } } string jsonPayload = JsonConvert.SerializeObject(payload, typeof(PushNotificationPayload), new JsonSerializerSettings()); string result = WebServiceMainHQProxy.GetWebServiceMainHQInstance().SendPushNotification(PayloadHelper.CreatePayload("", eService), jsonPayload); if (result.ToLower() != "success") { throw new Exception(result); } } ODThread th = new ODThread(new ODThread.WorkerDelegate((o) => { execute(); })); th.AddExceptionHandler((e) => { if (MockExceptionHandler != null) { MockExceptionHandler(e); } else { Logger.WriteException(e, "PushNotifications"); } }); th.Name = "SendPush_" + pushType.ToString() + "_ClinicNum_" + clinicNum.ToString() + "_UserNum_" + userNum.ToString(); th.Start(); if (MockExceptionHandler != null || !runAsync) //Join back to main thread to cause this to be a blocking call. Unit tests will always block. { th.Join(Timeout.Infinite); } }
///<summary>Fills the big main grid.</summary> private void FillGridMobileAppDevices() { gridMobileAppDevices.BeginUpdate(); //Columns gridMobileAppDevices.ListGridColumns.Clear(); gridMobileAppDevices.AddColumn("Device Name", 0); gridMobileAppDevices.AddColumn("Last Attempt", 0); gridMobileAppDevices.AddColumn("Last Login", 0); if (PrefC.HasClinicsEnabled) { gridMobileAppDevices.AddColumn("Clinic", 0); } gridMobileAppDevices.ListGridColumns.Add(new GridColumn("Enabled", 50, HorizontalAlignment.Center)); if (_eClipboardAllowEdit) { gridMobileAppDevices.ListGridColumns.Add(new GridColumn("Delete", 45, HorizontalAlignment.Center)); } //Rows gridMobileAppDevices.ListGridRows.Clear(); List <MobileAppDevice> listDevicesToShow = MobileAppDevices.GetForUser(Security.CurUser); if (_clinicNumEClipboardTab > 0) { listDevicesToShow.RemoveAll(x => x.ClinicNum != _clinicNumEClipboardTab); } foreach (MobileAppDevice device in listDevicesToShow) { GridRow row = new GridRow(); row.Cells.Add(device.DeviceName + "\r\n(" + device.UniqueID + ")"); row.Cells.Add((device.LastAttempt.Year > 1880 ? device.LastAttempt.ToString() : "")); row.Cells.Add((device.LastLogin.Year > 1880 ? device.LastLogin.ToString() : "")); if (PrefC.HasClinicsEnabled) { row.Cells.Add((device.ClinicNum == 0 ? Clinics.GetPracticeAsClinicZero() : Clinics.GetClinic(device.ClinicNum)).Abbr); } row.Cells.Add((device.IsAllowed ? "X" : "")); if (_eClipboardAllowEdit) { #region Delete click handler void DeleteClick(object sender, EventArgs e) { if (device.PatNum > 0) { MsgBox.Show("A patient is currently using this device. Please clear the patient from the device using the Kiosk Manager" + " or wait until the patient is no longer using the device."); return; } if (!MsgBox.Show(MsgBoxButtons.YesNo, "This will immediately remove the device from the database and all other workstations." + " Continue?")) { return; } MobileAppDevices.Delete(device.MobileAppDeviceNum); FillGridMobileAppDevices(); } #endregion Delete click handler GridCell cell = new GridCell("Delete"); cell.ColorBackG = Color.LightGray; cell.ClickEvent = DeleteClick; row.Cells.Add(cell); } row.Tag = device; gridMobileAppDevices.ListGridRows.Add(row); } gridMobileAppDevices.EndUpdate(); }
private void FillGrid() { SheetDevice selected = new SheetDevice(new MobileAppDevice()); //just instantiate to something random that won't match any of our actual devices if (gridMain.GetSelectedIndex() > -1) { selected = (SheetDevice)gridMain.ListGridRows[gridMain.GetSelectedIndex()].Tag; } List <SheetDevice> listDevices = new List <SheetDevice>(); foreach (TerminalActive kiosk in TerminalActives.Refresh()) { listDevices.Add(new SheetDevice(kiosk)); } List <MobileAppDevice> listMobileDevices = new List <MobileAppDevice>(); if (PrefC.HasClinicsEnabled) { //Option "All" is selected and at least one clinic is signed up for the eClipboard feature if (contrClinicPicker.IsAllSelected && PrefC.GetString(PrefName.EClipboardClinicsSignedUp) != "") { listMobileDevices = MobileAppDevices.GetForUser(Security.CurUser).FindAll(x => x.IsAllowed); } //A specific clinic is selected and that is signed up for the eClipboard feature else if (MobileAppDevices.IsClinicSignedUpForEClipboard(contrClinicPicker.SelectedClinicNum)) { listMobileDevices = MobileAppDevices.GetForUser(Security.CurUser).FindAll(x => x.IsAllowed && x.ClinicNum == contrClinicPicker.SelectedClinicNum); } } //We aren't using clinics and the zero clinic is signed up else if (MobileAppDevices.IsClinicSignedUpForEClipboard(0)) { listMobileDevices = MobileAppDevices.GetForUser(Security.CurUser).FindAll(x => x.IsAllowed); } //Add the clinics we decided on the the d foreach (MobileAppDevice device in listMobileDevices) { listDevices.Add(new SheetDevice(device)); } int selectedIndex = -1; gridMain.BeginUpdate(); gridMain.ListGridColumns.Clear(); gridMain.AddColumn("Device Name", 150); gridMain.AddColumn("Session Name", 110); gridMain.AddColumn("Patient", 185); if (PrefC.HasClinicsEnabled) { gridMain.AddColumn("Clinic", 150); } gridMain.ListGridColumns.Add(new GridColumn("Action", 50) { TextAlign = HorizontalAlignment.Center }); if (_isSetupMode) { gridMain.ListGridColumns.Add(new GridColumn("Delete", 50) { TextAlign = HorizontalAlignment.Center }); } gridMain.ListGridRows.Clear(); foreach (SheetDevice device in listDevices) { GridRow row = new GridRow(); row.Tag = device; if (device.IsMobileAppDevice) { row.Cells.Add(new GridCell(device.Name + "\r\n(" + device.MobileDevice.UniqueID + ")")); } else { row.Cells.Add(new GridCell(device.Name)); } row.Cells.Add(new GridCell(device.SessionName)); row.Cells.Add(new GridCell(device.PatName)); if (PrefC.HasClinicsEnabled) { row.Cells.Add(new GridCell(device.ClinicDesc)); } #region Load/Clear click handler void CellClick(object sender, EventArgs e) { FillGrid(); if (device.PatNum == 0) //we are trying to load the patient { if (FormOpenDental.CurPatNum == 0) { MsgBox.Show(this, "There is currently no patient selected to send to the device. Select a patient in Open Dental " + "in order to continue."); return; } if (device.IsKiosk) //kiosk only { if (listSheets.Items.Count == 0) //eClipboard will allow to continue to load here in case we just want to take a photo { MsgBox.Show(this, "There are no sheets to send to the computer or device for the current patient."); return; } } else //eclipboard only { if (MobileAppDevices.PatientIsAlreadyUsingDevice(FormOpenDental.CurPatNum)) { MsgBox.Show(this, "The patient you have selected is already using another device. Select a patient who is not currently " + "using a device in order to continue."); return; } Appointment apptForToday = Appointments.GetAppointmentsForPat(FormOpenDental.CurPatNum).FirstOrDefault(x => x.AptDateTime.Date == DateTime.Today.Date); if (apptForToday == null) { MsgBox.Show(this, "The patient you have selected does not have an appointment today. Only patients with an " + "appointment on the same day can be sent to eClipboard."); return; } List <Sheet> listNonMobileSheets = listSheets.AllTags <Sheet>().FindAll(x => !x.HasMobileLayout); if (listNonMobileSheets.Count > 0) { if (!MsgBox.Show(MsgBoxButtons.YesNo, "The following sheets that have been queued for this patient cannot be " + $"loaded onto an eClipboard device because they do not have a mobile layout: \r\n" + $"{string.Join(", ",listNonMobileSheets.Select(x => x.Description))}. \r\nDo you still wish to continue?")) { return; } } //They are in setup mode (not normal workflow) and there are no sheets for this patient. They have not run the rules to generate //sheets as the patient has not been marked as arrived. When they push the patient to the device, they will not generate the sheets //from there either. Ask them if they want to generate the sheets in this case. if (_isSetupMode && listSheets.Items.Count == 0 && ClinicPrefs.GetBool(PrefName.EClipboardCreateMissingFormsOnCheckIn, apptForToday.ClinicNum)) { bool generateSheets = MsgBox.Show(MsgBoxButtons.YesNo, "This patient has no forms to load. Would you like to generate the " + "forms based on the eClipboard rules?"); if (generateSheets) { //We do not need to update the UI here. It will be updated at the end of this click method. Sheets.CreateSheetsForCheckIn(apptForToday); } } } device.SetPatNum(FormOpenDental.CurPatNum); } else //we are trying to clear the patient { if (!MsgBox.Show(this, true, "A patient is currently using the terminal. If you continue, they will lose the information that is on their " + "screen. Continue anyway?")) { return; } device.SetPatNum(0); } FillGrid(); } #endregion Load/Clear click handler GridCell cell = new GridCell(device.PatNum == 0?"Load":"Clear"); cell.ColorBackG = Color.LightGray; cell.ClickEvent = CellClick; row.Cells.Add(cell); if (_isSetupMode) { #region Delete click handler void DeleteClick(object sender, EventArgs e) { FillGrid(); if (!MsgBox.Show(this, MsgBoxButtons.OKCancel, "A row should not be deleted unless it is showing erroneously and there really is " + "nothing running on the computer or device shown. Continue anyway?")) { return; } device.Delete(); FillGrid(); } #endregion Delete click handler cell = new GridCell("Delete"); cell.ColorBackG = Color.LightGray; cell.ClickEvent = DeleteClick; row.Cells.Add(cell); } gridMain.ListGridRows.Add(row); if (selected != null && device.Matches(selected)) { selectedIndex = gridMain.ListGridRows.Count - 1; } } gridMain.EndUpdate(); gridMain.SetSelected(selectedIndex, true); //selectedIndex could be -1 if the selected term is not in the list, default to row 0 FillPat(); }