///<summary>(HQ Only) When we create, modify, or delete a non-sales tax adjustment that is attached to a procedure, we may also need to update ///sales tax for that procedure. Takes a non-sales tax adjustment and checks if the procedure already has a sales tax adjustment. If one already ///exists, calculate the new tax after the change to the passed in adjustment, and update the sales tax accordingly. If not, calculate and ///create a sales tax adjustment only if the procedure is taxable.</summary> public static void CreateOrUpdateSalesTaxIfNeeded(Adjustment modifiedAdj) { if (AvaTax.IsEnabled() && modifiedAdj.ProcNum > 0 && modifiedAdj.AdjType != AvaTax.SalesTaxAdjType && modifiedAdj.AdjType != AvaTax.SalesTaxReturnAdjType) { Adjustment taxAdjForProc = GetSalesTaxForProc(modifiedAdj.ProcNum); if (taxAdjForProc != null) { Procedure proc = Procedures.GetOneProc(modifiedAdj.ProcNum, false); CreateOrUpdateSalesTaxIfNeeded(proc, taxAdjForProc); } } }
///<summary>Sums all adjustments for a proc then returns that sum. Pass false to canIncludeTax in order to exclude sales tax from the end amount. ///</summary> public static double GetTotForProc(long procNum, bool canIncludeTax = true) { if (RemotingClient.RemotingRole == RemotingRole.ClientWeb) { return(Meth.GetDouble(MethodBase.GetCurrentMethod(), procNum, canIncludeTax)); } string command = "SELECT SUM(AdjAmt) FROM adjustment" + " WHERE ProcNum=" + POut.Long(procNum); if (AvaTax.IsEnabled() && !canIncludeTax) { command += " AND AdjType NOT IN (" + string.Join(",", POut.Long(AvaTax.SalesTaxAdjType), POut.Long(AvaTax.SalesTaxReturnAdjType)) + ")"; } return(PIn.Double(Db.GetScalar(command))); }
public static bool DoCreateReturnAdjustment(Procedure procedure, Adjustment lockedAdj, Adjustment returnAdj) { try { double procTotal = procedure.ProcFeeTotal + Adjustments.GetTotForProc(procedure.ProcNum, canIncludeTax: false); decimal taxEstNew = AvaTax.GetEstimate(procedure.CodeNum, procedure.PatNum, procTotal, hasExceptions: true); returnAdj.AdjAmt = (Adjustments.GetTotTaxForProc(procedure) - (double)taxEstNew) * (-1); if (returnAdj.AdjAmt == 0) { return(false); //no error and we would be refunding $0 to the customer, so no need to create a return adjustment } } catch (Exception e) { returnAdj.AdjNote = AvaTax.AddNote(returnAdj.AdjNote, "An error occurred processing the transaction: " + e.Message + " See local logs for more details."); } return(true); }
///<summary></summary> public static double GetTotTaxForProc(Procedure proc) { if (RemotingClient.RemotingRole == RemotingRole.ClientWeb) { return(Meth.GetDouble(MethodBase.GetCurrentMethod(), proc)); } if (!AvaTax.DoSendProcToAvalara(proc)) { return(0); } string command = "SELECT SUM(AdjAmt) FROM adjustment" + " WHERE ProcNum=" + POut.Long(proc.ProcNum) + " AND AdjType IN (" + string.Join(",", POut.Long(AvaTax.SalesTaxAdjType), POut.Long(AvaTax.SalesTaxReturnAdjType)) + ")"; return(PIn.Double(Db.GetScalar(command))); }
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>(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); }