public static bool IsTransworldEnabled(long clinicNum) { //No need to check RemotingRole;no call to db. Program progCur = Programs.GetCur(ProgramName.Transworld); if (progCur == null || !progCur.Enabled) { return(false); } Dictionary <long, List <ProgramProperty> > dictAllProps = ProgramProperties.GetForProgram(progCur.ProgramNum) .GroupBy(x => x.ClinicNum) .ToDictionary(x => x.Key, x => x.ToList()); if (dictAllProps.Count == 0) { return(false); } List <long> listDisabledClinicNums = new List <long>(); if (!PrefC.HasClinicsEnabled) { return(TsiTransLogs.ValidateClinicSftpDetails(dictAllProps[0], false)); } List <Clinic> listAllClinics = Clinics.GetDeepCopy(); listDisabledClinicNums.AddRange(dictAllProps.Where(x => !TsiTransLogs.ValidateClinicSftpDetails(x.Value, false)).Select(x => x.Key)); listDisabledClinicNums.AddRange(listAllClinics .FindAll(x => x.IsHidden || (listDisabledClinicNums.Contains(0) && !dictAllProps.ContainsKey(x.ClinicNum)))//if no props for HQ, skip other clinics without props .Select(x => x.ClinicNum) ); return(!listDisabledClinicNums.Contains(clinicNum)); }
///<summary>Creates and inserts a discount adjustment for the passed in procedure. Used by prepayment tool.</summary> public static void CreateDiscountAdjustment(Procedure proc, double discountPercentage, long adjType) { Adjustment adj = new Adjustment { DateEntry = DateTime.Today, AdjDate = DateTime.Today, ProcDate = proc.ProcDate, ProvNum = proc.ProvNum, ProcNum = proc.ProcNum, ClinicNum = proc.ClinicNum, PatNum = proc.PatNum, AdjType = adjType, AdjAmt = -(proc.ProcFee * discountPercentage) //Flip the sign to make it a negative adjustment. }; Adjustments.Insert(adj); TsiTransLogs.CheckAndInsertLogsIfAdjTypeExcluded(adj); }
///<summary>Creates a new discount adjustment for the given procedure.</summary> public static void CreateAdjustmentForDiscount(Procedure procedure) { //No need to check RemotingRole; no call to db. Adjustment AdjustmentCur = new Adjustment(); AdjustmentCur.DateEntry = DateTime.Today; AdjustmentCur.AdjDate = DateTime.Today; AdjustmentCur.ProcDate = procedure.ProcDate; AdjustmentCur.ProvNum = procedure.ProvNum; AdjustmentCur.PatNum = procedure.PatNum; AdjustmentCur.AdjType = PrefC.GetLong(PrefName.TreatPlanDiscountAdjustmentType); AdjustmentCur.ClinicNum = procedure.ClinicNum; AdjustmentCur.AdjAmt = -procedure.Discount; //Discount must be negative here. AdjustmentCur.ProcNum = procedure.ProcNum; Insert(AdjustmentCur); TsiTransLogs.CheckAndInsertLogsIfAdjTypeExcluded(AdjustmentCur); }
public static void CreateSalesTaxRefundIfNeeded(Procedure procedure, Adjustment adjustmentExistingLocked) { Adjustment adjustmentSalesTaxReturn = new Adjustment(); adjustmentSalesTaxReturn.DateEntry = DateTime.Today; adjustmentSalesTaxReturn.AdjDate = DateTime.Today; adjustmentSalesTaxReturn.ProcDate = procedure.ProcDate; adjustmentSalesTaxReturn.ProvNum = procedure.ProvNum; adjustmentSalesTaxReturn.PatNum = procedure.PatNum; adjustmentSalesTaxReturn.AdjType = AvaTax.SalesTaxReturnAdjType; adjustmentSalesTaxReturn.ClinicNum = procedure.ClinicNum; adjustmentSalesTaxReturn.ProcNum = procedure.ProcNum; if (AvaTax.DoCreateReturnAdjustment(procedure, adjustmentExistingLocked, adjustmentSalesTaxReturn)) { Insert(adjustmentSalesTaxReturn); TsiTransLogs.CheckAndInsertLogsIfAdjTypeExcluded(adjustmentSalesTaxReturn); } }
///<summary>Creates a new discount adjustment for the given procedure using the discount plan fee.</summary> public static void CreateAdjustmentForDiscountPlan(Procedure procedure) { //No need to check RemotingRole; no call to db. DiscountPlan discountPlan = DiscountPlans.GetPlan(Patients.GetPat(procedure.PatNum).DiscountPlanNum); if (discountPlan == null) { return; //No discount plan. } //Figure out how much the patient saved and make an adjustment for the difference so that the office find how much money they wrote off. double discountAmt = Fees.GetAmount(procedure.CodeNum, discountPlan.FeeSchedNum, procedure.ClinicNum, procedure.ProvNum); if (discountAmt == -1) { return; //No fee entered, don't make adjustment. } double adjAmt = procedure.ProcFee - discountAmt; if (adjAmt <= 0) { return; //We do not need to create adjustments for 0 dollars. } Adjustment adjustmentCur = new Adjustment(); adjustmentCur.DateEntry = DateTime.Today; adjustmentCur.AdjDate = DateTime.Today; adjustmentCur.ProcDate = procedure.ProcDate; adjustmentCur.ProvNum = procedure.ProvNum; adjustmentCur.PatNum = procedure.PatNum; adjustmentCur.AdjType = discountPlan.DefNum; adjustmentCur.ClinicNum = procedure.ClinicNum; adjustmentCur.AdjAmt = (-adjAmt); adjustmentCur.ProcNum = procedure.ProcNum; Insert(adjustmentCur); TsiTransLogs.CheckAndInsertLogsIfAdjTypeExcluded(adjustmentCur); SecurityLogs.MakeLogEntry(Permissions.AdjustmentCreate, procedure.PatNum, "Adjustment made for discount plan: " + adjustmentCur.AdjAmt.ToString("f")); }
///<summary>Sends an SFTP message to TSI to suspend the account for the guarantor passed in. Returns empty string if successful. ///Returns a translated error message that should be displayed to the user if anything goes wrong.</summary> public static string SuspendGuar(Patient guar) { PatAging patAging = Patients.GetAgingListFromGuarNums(new List <long>() { guar.PatNum }).FirstOrDefault(); if (patAging == null) //this would only happen if the patient was not in the db??, just in case { return(Lans.g("TsiTransLogs", "An error occurred when trying to send a suspend message to TSI.")); } long clinicNum = (PrefC.HasClinicsEnabled?guar.ClinicNum:0); Program prog = Programs.GetCur(ProgramName.Transworld); if (prog == null) //shouldn't be possible, the program link should always exist, just in case { return(Lans.g("TsiTransLogs", "The Transworld program link does not exist. Contact support.")); } Dictionary <long, List <ProgramProperty> > dictAllProps = ProgramProperties.GetForProgram(prog.ProgramNum) .GroupBy(x => x.ClinicNum) .ToDictionary(x => x.Key, x => x.ToList()); if (dictAllProps.Count == 0) //shouldn't be possible, there should always be a set of props for ClinicNum 0 even if disabled, just in case { return(Lans.g("TsiTransLogs", "The Transworld program link is not setup properly.")); } if (PrefC.HasClinicsEnabled && !dictAllProps.ContainsKey(clinicNum) && dictAllProps.ContainsKey(0)) { clinicNum = 0; } string clinicDesc = clinicNum == 0?"Headquarters":Clinics.GetDesc(clinicNum); if (!dictAllProps.ContainsKey(clinicNum) || !ValidateClinicSftpDetails(dictAllProps[clinicNum], true)) //the props should be valid, but this will test the connection using the props { return(Lans.g("TsiTransLogs", "The Transworld program link is not enabled") + " " + (PrefC.HasClinicsEnabled?(Lans.g("TsiTransLogs", "for the guarantor's clinic") + ", " + clinicDesc + ", "):"") + Lans.g("TsiTransLogs", "or is not setup properly.")); } List <ProgramProperty> listProps = dictAllProps[clinicNum]; long newBillType = PrefC.GetLong(PrefName.TransworldPaidInFullBillingType); if (newBillType == 0 || Defs.GetDef(DefCat.BillingTypes, newBillType) == null) { return(Lans.g("TsiTransLogs", "The default paid in full billing type is not set. An automated suspend message cannot be sent until the " + "default paid in full billing type is set in the Transworld program link") + (PrefC.HasClinicsEnabled?(" " + Lans.g("TsiTransLogs", "for the guarantor's clinic") + ", " + clinicDesc):"") + "."); } string clientId = ""; if (patAging.ListTsiLogs.Count > 0) { clientId = patAging.ListTsiLogs[0].ClientId; } if (string.IsNullOrEmpty(clientId)) { clientId = listProps.Find(x => x.PropertyDesc == "ClientIdAccelerator")?.PropertyValue; } if (string.IsNullOrEmpty(clientId)) { clientId = listProps.Find(x => x.PropertyDesc == "ClientIdCollection")?.PropertyValue; } if (string.IsNullOrEmpty(clientId)) { return(Lans.g("TsiTransLogs", "There is no client ID in the Transworld program link") + (PrefC.HasClinicsEnabled?(" " + Lans.g("TsiTransLogs", "for the guarantor's clinic") + ", " + clinicDesc):"") + "."); } string sftpAddress = listProps.Find(x => x.PropertyDesc == "SftpServerAddress")?.PropertyValue ?? ""; int sftpPort; if (!int.TryParse(listProps.Find(x => x.PropertyDesc == "SftpServerPort")?.PropertyValue ?? "", out sftpPort)) { sftpPort = 22; //default to port 22 } string userName = listProps.Find(x => x.PropertyDesc == "SftpUsername")?.PropertyValue ?? ""; string userPassword = listProps.Find(x => x.PropertyDesc == "SftpPassword")?.PropertyValue ?? ""; if (new[] { sftpAddress, userName, userPassword }.Any(x => string.IsNullOrEmpty(x))) { return(Lans.g("TsiTransLogs", "The SFTP address, username, or password for the Transworld program link") + " " + (PrefC.HasClinicsEnabled?(Lans.g("TsiTransLogs", "for the guarantor's clinic") + ", " + clinicDesc + ", "):"") + Lans.g("TsiTransLogs", "is blank.")); } string msg = TsiMsgConstructor.GenerateUpdate(patAging.PatNum, clientId, TsiTransType.SS, 0.00, patAging.AmountDue); try { byte[] fileContents = Encoding.ASCII.GetBytes(TsiMsgConstructor.GetUpdateFileHeader() + "\r\n" + msg); TaskStateUpload state = new Sftp.Upload(sftpAddress, userName, userPassword, sftpPort) { Folder = "/xfer/incoming", FileName = "TsiUpdates_" + DateTime.Now.ToString("yyyyMMddhhmmss") + ".txt", FileContent = fileContents, HasExceptions = true }; state.Execute(false); } catch (Exception ex) { return(Lans.g("TsiTransLogs", "There was an error sending the update message to Transworld") + (PrefC.HasClinicsEnabled?(" " + Lans.g("TsiTransLogs", "using the program properties for the guarantor's clinic") + ", " + clinicDesc):"") + ".\r\n" + ex.Message); } //Upload was successful TsiTransLog log = new TsiTransLog() { PatNum = patAging.PatNum, UserNum = Security.CurUser.UserNum, TransType = TsiTransType.SS, //TransDateTime=DateTime.Now,//set on insert, not editable by user //DemandType=TsiDemandType.Accelerator,//only valid for placement msgs //ServiceCode=TsiServiceCode.Diplomatic,//only valid for placement msgs ClientId = clientId, TransAmt = 0.00, AccountBalance = patAging.AmountDue, FKeyType = TsiFKeyType.None, //only used for account trans updates FKey = 0, //only used for account trans updates RawMsgText = msg, ClinicNum = clinicNum //,TransJson=""//only valid for placement msgs }; TsiTransLogs.Insert(log); //update family billing type to the paid in full billing type pref Patients.UpdateFamilyBillingType(newBillType, patAging.PatNum); return(""); }
///<summary>(HQ Only) Automatically creates or updates a sales tax adjustment for the passted in procedure. If an adjustment is passed in, we go ///ahead and update that adjustment, otherwise we check if there is already a sales tax adjustment for the given procedure and if not, we create ///a new one. Pass in false to doCalcTax if we have already called the AvaTax API to get the tax estimate recently to avoid redundant calls ///(currently only pre-payments uses this flag). ///isRepeatCharge indicates if the adjustment is being inserted by the repeat charge tool, currently only used to supress error messages ///in the Avatax API.</summary> public static void CreateOrUpdateSalesTaxIfNeeded(Procedure procedure, Adjustment salesTaxAdj = null, bool doCalcTax = true, bool isRepeatCharge = false) { if (!AvaTax.DoSendProcToAvalara(procedure, isRepeatCharge)) //tests isHQ { return; } //Check for middle tier as crud is called below if (RemotingClient.RemotingRole == RemotingRole.ClientWeb) { Meth.GetVoid(MethodBase.GetCurrentMethod(), procedure, salesTaxAdj, doCalcTax, isRepeatCharge); return; } if (salesTaxAdj == null) { salesTaxAdj = Adjustments.GetSalesTaxForProc(procedure.ProcNum); } //If we didn't find any existing adjustments to modify, create an adjustment instead if (salesTaxAdj == null) { salesTaxAdj = new Adjustment(); salesTaxAdj.DateEntry = DateTime.Today; salesTaxAdj.AdjDate = procedure.ProcDate; salesTaxAdj.ProcDate = procedure.ProcDate; salesTaxAdj.ProvNum = procedure.ProvNum; salesTaxAdj.PatNum = procedure.PatNum; salesTaxAdj.AdjType = AvaTax.SalesTaxAdjType; salesTaxAdj.ClinicNum = procedure.ClinicNum; salesTaxAdj.ProcNum = procedure.ProcNum; } //if the sales tax adjustment is locked, create a sales tax refund adjustment instead if (procedure.ProcDate <= AvaTax.TaxLockDate) { CreateSalesTaxRefundIfNeeded(procedure, salesTaxAdj); return; } if (!doCalcTax) //Should only ever happen for pre-payments, where we've already called the api to get the tax amount { salesTaxAdj.AdjAmt = procedure.TaxAmt; Insert(salesTaxAdj); } else if (AvaTax.DidUpdateAdjustment(procedure, salesTaxAdj)) { string note = DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToShortTimeString() + ": Tax amount changed from $" + procedure.TaxAmt.ToString("f2") + " to $" + salesTaxAdj.AdjAmt.ToString("f2"); if (!(procedure.TaxAmt - salesTaxAdj.AdjAmt).IsZero()) { procedure.TaxAmt = salesTaxAdj.AdjAmt; Crud.ProcedureCrud.Update(procedure); } if (salesTaxAdj.AdjNum == 0) { //The only way to get salesTaxAdj.AdjAmt=0 when AvaTax.DidUpdateAdjustment() returns true is if there was an error. if (isRepeatCharge && salesTaxAdj.AdjAmt == 0) //this is an error; we would normally not save a new adjustment with amt $0 { throw new ODException("Encountered an error communicating with AvaTax. Skip for repeating charges only. " + salesTaxAdj.AdjNote); } Insert(salesTaxAdj); //This could be an error or a new adjustment/repeating charge, either way we want to insert } else //updating an existing adjustment. We don't need to check isRepeatCharge because of { if (!string.IsNullOrWhiteSpace(salesTaxAdj.AdjNote)) { salesTaxAdj.AdjNote += Environment.NewLine; } salesTaxAdj.AdjNote += note; //If we are updating this adjustment, leave a note indicating what changed Update(salesTaxAdj); } } TsiTransLogs.CheckAndInsertLogsIfAdjTypeExcluded(salesTaxAdj); }