public static void SIPProviderDeleted(SIPProvider sipProvider) { try { logger.Debug("SIPProviderBindingSynchroniser SIPProviderDeleted for " + sipProvider.Owner + " and " + sipProvider.ProviderName + "."); using (SIPSorceryEntities sipSorceryEntities = new SIPSorceryEntities()) { SIPProviderBinding existingBinding = (from binding in sipSorceryEntities.SIPProviderBindings where binding.ProviderID == sipProvider.ID select binding).FirstOrDefault(); if (existingBinding != null) { if (existingBinding.IsRegistered) { // Let the registration agent know the existing binding should be expired. existingBinding.BindingExpiry = 0; existingBinding.NextRegistrationTime = DateTime.UtcNow.ToString("o"); sipSorceryEntities.SaveChanges(); } else { sipSorceryEntities.SIPProviderBindings.DeleteObject(existingBinding); sipSorceryEntities.SaveChanges(); } } } } catch (Exception excp) { logger.Error("Exception SIPProviderBindingSynchroniser SIPProviderDeleted. " + excp.Message); } }
public void UpdateRealTimeCallControlCDRID(string oldCDRID, SIPCDR newCDR) { logger.Debug("UpdateRealTimeCallControlCDRID old CDR ID " + oldCDRID + ", new CDR ID " + newCDR.CDRId.ToString() + "."); using (var db = new SIPSorceryEntities()) { using (var trans = new TransactionScope()) { var realTimeCallControl = (from rtcc in db.RTCCs1 where rtcc.CDRID == oldCDRID select rtcc).FirstOrDefault(); if (realTimeCallControl == null) { logger.Error("No RTCC record could be found for CDR ID " + oldCDRID + "."); } else { //db.CDRs.AddObject(newCDR); realTimeCallControl.CDRID = newCDR.CDRId.ToString(); db.SaveChanges(); trans.Complete(); } } } }
/// <summary> /// Updates an existing customer account. /// </summary> /// <param name="customerAccount">The customer account to update.</param> public void Update(CustomerAccount customerAccount) { using (var db = new SIPSorceryEntities()) { var existingAccount = (from ca in db.CustomerAccounts where ca.ID == customerAccount.ID select ca).SingleOrDefault(); if (existingAccount == null) { throw new ApplicationException("The customer account to update could not be found"); } else if (existingAccount.Owner.ToLower() != customerAccount.Owner.ToLower()) { throw new ApplicationException("You are not authorised to update this customer account."); } else { CheckUniqueFields(customerAccount); logger.Debug("Updating customer account " + existingAccount.AccountName + " for " + existingAccount.Owner + "."); existingAccount.AccountName = customerAccount.AccountName; existingAccount.AccountNumber = customerAccount.AccountNumber; existingAccount.Credit = customerAccount.Credit; existingAccount.PIN = customerAccount.PIN; existingAccount.RatePlan = customerAccount.RatePlan; db.SaveChanges(); } } }
/// <summary> /// Adds a new CDR. /// </summary> /// <param name="rate">The rate record to add.</param> public void Add(SIPCDR cdr) { using (var sipSorceryEntities = new SIPSorceryEntities()) { var entityCDR = new CDR() { ID = cdr.CDRId.ToString(), Inserted = DateTimeOffset.UtcNow.ToString("o"), Created = cdr.Created.ToString("o"), Direction = cdr.CallDirection.ToString(), DstURI = cdr.Destination.ToString(), Dst = cdr.Destination.User, DstHost = cdr.Destination.Host, FromHeader = cdr.From.ToString(), FromName = cdr.From.FromName, FromUser = cdr.From.FromURI.User, CallID = cdr.CallId, Owner = cdr.Owner, AdminMemberID = cdr.AdminMemberId, RemoteSocket = (cdr.RemoteEndPoint != null) ? cdr.RemoteEndPoint.ToString() : null, LocalSocket = (cdr.LocalSIPEndPoint != null) ? cdr.LocalSIPEndPoint.ToString() : null }; sipSorceryEntities.CDRs.AddObject(entityCDR); sipSorceryEntities.SaveChanges(); } }
/// <summary> /// Deletes an existing rate. /// </summary> /// <param name="id">The ID of the rate record to delete.</param> public void Delete(string id) { using (var sipSorceryEntities = new SIPSorceryEntities()) { var rate = (from rt in sipSorceryEntities.Rates where rt.ID == id select rt).SingleOrDefault(); if (rate != null) { sipSorceryEntities.Rates.DeleteObject(rate); sipSorceryEntities.SaveChanges(); } } }
/// <summary> /// Adds a new customer account. /// </summary> /// <param name="customerAccount">The customer account to add.</param> public void Add(CustomerAccount customerAccount) { using (var sipSorceryEntities = new SIPSorceryEntities()) { customerAccount.AccountCode = Crypto.GetRandomString(ACCOUNT_CODE_RANDOM_STRING_PREFIX_LENGTH) + Crypto.GetRandomInt(ACCOUNT_CODE_RANDOM_NUMBER_SUFFIX_LENGTH).ToString(); CheckUniqueFields(customerAccount); customerAccount.ID = Guid.NewGuid().ToString(); customerAccount.Inserted = DateTimeOffset.UtcNow.ToString("o"); sipSorceryEntities.CustomerAccounts.AddObject(customerAccount); sipSorceryEntities.SaveChanges(); } }
public void SetCDRIsHangingUp(string rtccID) { using (var db = new SIPSorceryEntities()) { //var callCDR = (from cdr in db.CDRs where cdr.ID == cdrID select cdr).SingleOrDefault(); var callRTCC = (from rtcc in db.RTCCs1 where rtcc.ID == rtccID select rtcc).SingleOrDefault(); if (callRTCC == null) { logger.Debug("RTCC record could not be found for " + rtccID + " when attemping to set the IsHangingUp flag."); } else { callRTCC.IsHangingUp = true; db.SaveChanges(); } } }
/// <summary> /// Adds a new rate. /// </summary> /// <param name="rate">The rate record to add.</param> public void Add(Rate rate) { using (var sipSorceryEntities = new SIPSorceryEntities()) { if ((from rt in sipSorceryEntities.Rates where rt.Prefix == rate.Prefix && rt.Owner == rate.Owner select rt).Any()) { throw new ApplicationException("The rate prefix is already in use."); } else { rate.ID = Guid.NewGuid().ToString(); rate.Inserted = DateTimeOffset.UtcNow.ToString("o"); sipSorceryEntities.Rates.AddObject(rate); sipSorceryEntities.SaveChanges(); } } }
/// <summary> /// Adds a new rate. /// </summary> /// <param name="rate">The rate record to add.</param> public void Add(Rate rate) { using (var sipSorceryEntities = new SIPSorceryEntities()) { if ((from rt in sipSorceryEntities.Rates where rt.Prefix == rate.Prefix select rt).Any()) { throw new ApplicationException("The rate prefix is already in use."); } else { rate.ID = Guid.NewGuid().ToString(); rate.Inserted = DateTimeOffset.UtcNow.ToString("o"); sipSorceryEntities.Rates.AddObject(rate); sipSorceryEntities.SaveChanges(); } } }
/// <summary> /// Updates an existing CDR. /// </summary> /// <param name="cdr">The CDR to update.</param> public void Update(SIPCDR cdr) { using (var db = new SIPSorceryEntities()) { string cdrID = cdr.CDRId.ToString(); var existingCDR = (from cd in db.CDRs where cd.ID == cdrID select cd).SingleOrDefault(); if (existingCDR == null) { throw new ApplicationException("The CDR to update could not be found"); } else { // Fields that are not permitted to be updated. // ID // Inserted // Direction // Created // Destination // From // Call-ID existingCDR.Owner = cdr.Owner; existingCDR.AdminMemberID = cdr.AdminMemberId; existingCDR.BridgeID = (cdr.BridgeId != Guid.Empty) ? cdr.BridgeId.ToString() : null; existingCDR.InProgressTime = (cdr.ProgressTime != null) ? cdr.ProgressTime.Value.ToString("o") : null; existingCDR.InProgressStatus = cdr.ProgressStatus; existingCDR.InProgressReason = cdr.ProgressReasonPhrase; existingCDR.RingDuration = (cdr.ProgressTime != null && cdr.AnswerTime != null) ? Convert.ToInt32(cdr.AnswerTime.Value.Subtract(cdr.ProgressTime.Value).TotalSeconds) : 0; existingCDR.AnsweredTime = (cdr.AnswerTime != null) ? cdr.AnswerTime.Value.ToString("o") : null; existingCDR.AnsweredStatus = cdr.AnswerStatus; existingCDR.AnsweredReason = cdr.AnswerReasonPhrase; existingCDR.Duration = (cdr.AnswerTime != null && cdr.HangupTime != null) ? Convert.ToInt32(cdr.HangupTime.Value.Subtract(cdr.AnswerTime.Value).TotalSeconds) : 0;; existingCDR.HungupTime = (cdr.HangupTime != null) ? cdr.HangupTime.Value.ToString("o") : null; existingCDR.HungupReason = cdr.HangupReason; existingCDR.AnsweredAt = cdr.AnsweredAt; existingCDR.DialPlanContextID = (cdr.DialPlanContextID != Guid.Empty) ? cdr.DialPlanContextID.ToString() : null; existingCDR.RemoteSocket = (cdr.RemoteEndPoint != null) ? cdr.RemoteEndPoint.ToString() : null; existingCDR.LocalSocket = (cdr.LocalSIPEndPoint != null) ? cdr.LocalSIPEndPoint.ToString() : null; db.SaveChanges(); } } }
private static void AddNewBindingForProvider(SIPSorceryEntities sipSorceryEntities, SIPProvider sipProvider) { try { logger.Debug("AddNewBindingForProvider provider ID=" + sipProvider.ID + "."); SIPProviderBinding newBinding = sipSorceryEntities.SIPProviderBindings.CreateObject(); newBinding.SetProviderFields(sipProvider); newBinding.ID = Guid.NewGuid().ToString(); newBinding.NextRegistrationTime = DateTimeOffset.UtcNow.ToString("o"); newBinding.ProviderID = sipProvider.ID; newBinding.Owner = sipProvider.Owner; sipSorceryEntities.SIPProviderBindings.AddObject(newBinding); sipSorceryEntities.SaveChanges(); } catch (Exception excp) { logger.Error("Exception AddNewBindingForProvider. " + excp.Message); logger.Error(excp); } }
/// <summary> /// Updates an existing rate. /// </summary> /// <param name="rate">The rate to update.</param> public void Update(Rate rate) { using (var db = new SIPSorceryEntities()) { var existingRate = (from ra in db.Rates where ra.ID == rate.ID select ra).SingleOrDefault(); if (existingRate == null) { throw new ApplicationException("The rate to update could not be found"); } else if (existingRate.Owner.ToLower() != rate.Owner.ToLower()) { throw new ApplicationException("You are not authorised to update this rate."); } else { if (existingRate.Prefix != rate.Prefix) { if ((from rt in db.Rates where rt.Prefix == rate.Prefix select rt).Any()) { throw new ApplicationException("The rate prefix is already in use."); } } logger.Debug("Updating rate " + existingRate.Description + " for " + existingRate.Owner + "."); existingRate.Description = rate.Description; existingRate.Prefix = rate.Prefix; existingRate.Rate1 = rate.Rate1; existingRate.RateCode = rate.RateCode; existingRate.SetupCost = rate.SetupCost; existingRate.IncrementSeconds = rate.IncrementSeconds; existingRate.RatePlan = rate.RatePlan; db.SaveChanges(); } } }
public void SetCDRIsHangingUp(string cdrID) { using (var db = new SIPSorceryEntities()) { var callCDR = (from cdr in db.CDRs where cdr.ID == cdrID select cdr).SingleOrDefault(); if (callCDR == null) { logger.Debug("CDR could not be found for " + cdrID + " when attemping to set the IsHangingUp flag."); } else { callCDR.IsHangingUp = true; db.SaveChanges(); } } }
public void DeleteSIPAccount(string authUser, SIPAccount sipAccount) { using (var sipSorceryEntities = new SIPSorceryEntities()) { SIPAccount existingAccount = (from sa in sipSorceryEntities.SIPAccounts where sa.ID == sipAccount.ID && sa.Owner.ToLower() == authUser.ToLower() select sa).FirstOrDefault(); if (existingAccount == null) { throw new ApplicationException("The SIP account to delete could not be found."); } else if (existingAccount.Owner.ToLower() != authUser.ToLower()) { throw new ApplicationException("Not authorised to delete the SIP Account."); } sipSorceryEntities.SIPAccounts.DeleteObject(existingAccount); sipSorceryEntities.SaveChanges(); } }
public void UpdateSIPProvider(string authUser, SIPProvider sipProvider) { if (authUser.IsNullOrBlank()) { throw new ArgumentException("An authenticated user is required for InsertSIPProvider."); } SIPProvider existingAccount = null; using (var sipSorceryEntities = new SIPSorceryEntities()) { existingAccount = (from sp in sipSorceryEntities.SIPProviders where sp.ID == sipProvider.ID && sp.Owner == authUser.ToLower() select sp).FirstOrDefault(); if (existingAccount == null) { throw new ApplicationException("The SIP provider to update could not be found."); } else if (existingAccount.Owner.ToLower() != authUser.ToLower()) { throw new ApplicationException("Not authorised to update the SIP Provider."); } else if (existingAccount.IsReadOnly) { throw new ApplicationException("This SIP Provider is read-only. Please upgrade to a Premium service to enable it."); } existingAccount.CustomHeaders = sipProvider.CustomHeaders; existingAccount.GVCallbackNumber = sipProvider.GVCallbackNumber; existingAccount.GVCallbackPattern = sipProvider.GVCallbackPattern; existingAccount.GVCallbackType = sipProvider.GVCallbackType; existingAccount.LastUpdate = DateTimeOffset.UtcNow.ToString("o"); existingAccount.ProviderAuthUsername = sipProvider.ProviderAuthUsername; existingAccount.ProviderFrom = sipProvider.ProviderFrom; existingAccount.ProviderName = sipProvider.ProviderName; existingAccount.ProviderOutboundProxy = sipProvider.ProviderOutboundProxy; existingAccount.ProviderPassword = sipProvider.ProviderPassword; existingAccount.ProviderServer = sipProvider.ProviderServer; existingAccount.ProviderUsername = sipProvider.ProviderUsername; existingAccount.RegisterContact = sipProvider.RegisterContact; existingAccount.RegisterEnabled = sipProvider.RegisterEnabled; existingAccount.RegisterExpiry = sipProvider.RegisterExpiry; existingAccount.RegisterRealm = sipProvider.RegisterRealm; existingAccount.RegisterServer = sipProvider.RegisterServer; existingAccount.RegisterDisabledReason = sipProvider.RegisterDisabledReason; existingAccount.SendMWISubscribe = sipProvider.SendMWISubscribe; if (existingAccount.RegisterEnabled) { FixProviderRegisterDetails(existingAccount, authUser); } string validationError = SIPProvider.Validate(existingAccount); if (validationError != null) { throw new ApplicationException(validationError); } sipSorceryEntities.SaveChanges(); } SIPProviderBindingSynchroniser.SIPProviderUpdated(existingAccount); }
public void ChangeSIPDialPlanName(string authUser, string sipDialPlanID, string name) { if (authUser.IsNullOrBlank()) { throw new ArgumentException("An authenticated user is required for ChangeSIPDialPlanName."); } else if (name.IsNullOrBlank()) { throw new ArgumentNullException("The new name cannot be empty in ChangeSIPDialPlanName."); } using (var sipSorceryEntities = new SIPSorceryEntities()) { SIPDialPlan existingAccount = (from dp in sipSorceryEntities.SIPDialPlans where dp.ID == sipDialPlanID && dp.Owner.ToLower() == authUser.ToLower() select dp).FirstOrDefault(); if (existingAccount == null) { throw new ApplicationException("The SIP Dial Plan to change the name for could not be found."); } else if (existingAccount.Owner != authUser.ToLower()) { logger.Warn("User " + authUser + " was not authorised to change dial plan " + existingAccount.DialPlanName + " belonging to " + existingAccount.Owner + "."); throw new ApplicationException("Not authorised to change the SIP Dial Plan name."); } else if (existingAccount.IsReadOnly) { throw new ApplicationException("This Dial Plan is read-only. Please upgrade to a Premium service to enable it."); } logger.Debug("Changing the SIP dialplan " + existingAccount.DialPlanName + " for " + existingAccount.Owner + " to " + name + "."); if (sipSorceryEntities.SIPDialPlans.Any(x => x.DialPlanName.ToLower() == name.ToLower() && x.Owner.ToLower() == authUser.ToLower())) { throw new ApplicationException("There is already a dialplan with the same name. Please choose something different."); } // Need to update any SIP accounts that are using the old dialplan name. UpdateSIPAccountsDialPlanName(sipSorceryEntities, authUser, existingAccount.DialPlanName, name); existingAccount.DialPlanName = name; sipSorceryEntities.SaveChanges(); } }
public void DeleteSIPDialPlan(string authUser, string sipDialPlanID) { using (var sipSorceryEntities = new SIPSorceryEntities()) { SIPDialPlan existingAccount = (from dp in sipSorceryEntities.SIPDialPlans where dp.ID == sipDialPlanID && dp.Owner.ToLower() == authUser.ToLower() select dp).FirstOrDefault(); if (existingAccount == null) { throw new ApplicationException("The SIP Dial Plan to delete could not be found."); } else if (existingAccount.Owner != authUser.ToLower()) { throw new ApplicationException("Not authorised to delete the SIP Dial Plan."); } sipSorceryEntities.SIPDialPlans.DeleteObject(existingAccount); sipSorceryEntities.SaveChanges(); } }
public void DBWrite(string authUsername, string key, string value) { using (var entities = new SIPSorceryEntities()) { var existingEntry = entities.DialPlanData.Where(x => x.DataOwner == authUsername && x.DataKey == key).SingleOrDefault(); if (existingEntry != null) { existingEntry.DataValue = value; } else { var newEntry = new DialPlanDataEntry() { DataOwner = authUsername, DataKey = key, DataValue = value }; entities.AddToDialPlanData(newEntry); } entities.SaveChanges(); } }
/// <summary> /// This method should be called once a billable call has been completed. It will calculate the final cost of the call and return /// any usused credit back to the customer account. /// </summary> /// <param name="cdrID">The ID of the CDR the credit is being returned for.</param> /// <returns>The total cost of the completed call.</returns> public decimal ReturnUnusedCredit(string rtccID) { logger.Debug("ReturnUnusedCredit for RTCC ID " + rtccID + "."); decimal actualCallCost = Decimal.Zero; using (var db = new SIPSorceryEntities()) { using (var trans = new TransactionScope()) { var rtcc = (from rtc in db.RTCCs1.Include("cdr") where rtc.ID == rtccID select rtc).First(); if (rtcc.ReconciliationResult != null) { logger.Error("This CDR has already been reconciled, no further action will be taken."); } else { var callCDR = (from cdr in db.CDRs where cdr.ID == rtcc.CDRID select cdr).SingleOrDefault(); string reconciliationError = null; if (callCDR.Duration == null) { reconciliationError = "Error, the call duration was null."; logger.Warn("The unused credit could not be returned for " + rtcc.ID + " the CDR has not been hungup."); } else if (rtcc.Cost == null) { reconciliationError = "Error, the call cost was null."; logger.Warn("The unused credit could not be returned for " + rtcc.ID + " the call cost was empty."); } else if (rtcc.AccountCode.IsNullOrBlank()) { reconciliationError = "Error, the accountcode was null."; logger.Warn("The unused credit could not be returned for " + rtcc.ID + " due to the CDR having a blank accountcode."); } else if (rtcc.Rate <= 0) { reconciliationError = "Error, the rate was not set."; logger.Warn("The unused credit could not be returned for " + rtcc.ID + " due to the CDR having no rate."); } if (reconciliationError != null) { rtcc.ReconciliationResult = reconciliationError; db.SaveChanges(); } else { string accountCode = rtcc.AccountCode; var customerAccount = db.CustomerAccounts.Where(x => x.AccountCode == accountCode).SingleOrDefault(); logger.Debug("The pre-reconciliation balance for account " + accountCode + " was " + customerAccount.Credit + " when processing RTCC ID " + rtcc.ID + "."); if (customerAccount == null) { logger.Debug("The unused credit could not be returned for RTCC ID " + rtcc.ID + " due to the no matching accountcode."); rtcc.ReconciliationResult = "Error, no matching customer for " + accountCode + "."; db.SaveChanges(); } else { // Get the owning customer's RTCC billing increment. //int rtccIncrement = (from cust in db.Customers where cust.Name.ToLower() == callCDR.Owner.ToLower() select cust.RTCCBillingIncrement).Single(); int rtccIncrement = (rtcc.IncrementSeconds <= 0) ? DEFAULT_INCREMENT_SECONDS : rtcc.IncrementSeconds; int billableDuration = (callCDR.Duration.Value % rtccIncrement == 0) ? callCDR.Duration.Value : (callCDR.Duration.Value / rtccIncrement + 1) * rtccIncrement; if (billableDuration > 0) { actualCallCost = ((Convert.ToDecimal(billableDuration) / SECONDS_FOR_RATE) * rtcc.Rate.Value) + rtcc.SetupCost; } logger.Debug("RTCC billable duration " + billableDuration + " (increment " + rtccIncrement + "), actual call cost calculated at " + actualCallCost.ToString("0.#####") + " for call with cost of " + rtcc.Cost + "."); if (Math.Round(actualCallCost, 5) < Math.Round(rtcc.Cost.Value, 5)) { decimal returnCredit = Math.Round(rtcc.Cost.Value, 5) - Math.Round(actualCallCost, 5); if (returnCredit > 0) { // There is some credit to return to the customer account. rtcc.Cost = rtcc.Cost.Value - returnCredit; rtcc.ReconciliationResult = "ok"; customerAccount.Credit = customerAccount.Credit + returnCredit; rtcc.PostReconciliationBalance = customerAccount.Credit; logger.Debug("The billed call cost was " + actualCallCost.ToString("0.#####") + ", return credit amount " + returnCredit.ToString("0.#####") + ", post reconciliation balance " + customerAccount.Credit.ToString("0.#####") + "."); } else { // An error has occurred and the credit reserved was less than the cost of the call. rtcc.ReconciliationResult = "Error: Actual call cost calculated as " + actualCallCost.ToString("0.#####"); customerAccount.Credit = customerAccount.Credit + returnCredit; rtcc.PostReconciliationBalance = customerAccount.Credit; logger.Debug("Error, the billed call cost was " + actualCallCost.ToString("0.#####") + " which resulted in a negative return credit amount of " + returnCredit.ToString("0.#####") + ", post reconciliation balance " + customerAccount.Credit.ToString("0.#####") + "."); } db.SaveChanges(); } else if (Math.Round(actualCallCost, 5) > Math.Round(rtcc.Cost.Value, 5) && Math.Abs(callCDR.Duration.Value - rtcc.SecondsReserved.Value) <= SECONDS_LENIENCY_FOR_RECONCILIATION) { rtcc.ReconciliationResult = "ok"; rtcc.PostReconciliationBalance = customerAccount.Credit; logger.Debug("The billed call duration was in +/- " + SECONDS_LENIENCY_FOR_RECONCILIATION + " of actual call duration so a billable call cost of " + rtcc.Cost.Value.ToString("0.#####") + " was accepted for an actual call cost of " + actualCallCost.ToString("0.#####") + ", post reconciliation balance " + rtcc.PostReconciliationBalance.Value.ToString("0.#####") + "."); db.SaveChanges(); } else if (Math.Round(actualCallCost, 5) > Math.Round(rtcc.Cost.Value, 5)) { rtcc.ReconciliationResult = "Error, calculated cost of " + actualCallCost.ToString("0.#####") + " exceeded reserved cost of " + rtcc.Cost.Value.ToString("0.#####") + "."; rtcc.PostReconciliationBalance = customerAccount.Credit; logger.Debug("Error, calculated cost of " + actualCallCost.ToString("0.#####") + " exceeded reserved cost of " + rtcc.Cost.Value.ToString("0.#####") + ", post reconciliation balance " + rtcc.PostReconciliationBalance.Value.ToString("0.#####") + "."); db.SaveChanges(); } else { // Call cost exactly matches the reserved cost. rtcc.ReconciliationResult = "ok"; rtcc.PostReconciliationBalance = customerAccount.Credit; logger.Debug("The billed call duration was the exact amount reserved of " + rtcc.Cost.Value.ToString("0.#####") + ", post reconciliation balance " + rtcc.PostReconciliationBalance.Value.ToString("0.#####") + "."); db.SaveChanges(); } } } } trans.Complete(); } } return actualCallCost; }
/// <summary> /// This method attempts to reserve a the initial amount of credit for a call. /// </summary> /// <param name="accountCode">The accountCode the credit should be reserved against.</param> /// <param name="amount">The amount of credit to reserve.</param> /// <param name="rate">The rate for the call destination and the values that will be used for subsequent credit reservations.</param> /// <param name="initialSeconds">IF the reservation is successful this parameter will hold the number of seconds that were reserved for the initial reservation.</param> /// <returns>True if there was enough credit for the reservation otherwise false.</returns> public decimal ReserveInitialCredit(string accountCode, string rateID, SIPCDR cdr, out int initialSeconds) { try { logger.Debug("ReserveInitialCredit for " + accountCode + " and rate ID " + rateID + "."); initialSeconds = 0; using (var db = new SIPSorceryEntities()) { using (var trans = new TransactionScope()) { var rate = db.Rates.Where(x => x.ID == rateID).SingleOrDefault(); if (accountCode.IsNullOrBlank() || (rate == null || rate.Rate1 <= 0)) { return Decimal.MinusOne; } logger.Debug("ReserveInitialCredit for " + accountCode + ", rate " + rate.Rate1 + ", setup cost " + rate.SetupCost + ", increment seconds " + rate.IncrementSeconds + "."); var customerAccount = db.CustomerAccounts.Where(x => x.AccountCode == accountCode).SingleOrDefault(); if (customerAccount == null) { logger.Debug("The initial reservation for " + accountCode + " failed due to the no matching accountcode."); return Decimal.MinusOne; } // Get the owning customer's RTCC billing increment. //int rtccIncrement = (from cust in db.Customers where cust.Name.ToLower() == customerAccount.Owner.ToLower() select cust.RTCCBillingIncrement).Single(); initialSeconds = (rate.IncrementSeconds > MINIMUM_INITIAL_RESERVATION_SECONDS) ? rate.IncrementSeconds : MINIMUM_INITIAL_RESERVATION_SECONDS; decimal reservationCost = ((decimal)initialSeconds / (decimal)60 * rate.Rate1) + rate.SetupCost; if (customerAccount.Credit < reservationCost) { logger.Debug("The initial reservation for " + accountCode + ", duration " + initialSeconds + "s and " + reservationCost.ToString("0.#####") + " failed due to lack of credit."); return Decimal.MinusOne; } else { var rtccRecord = new RTCC() { ID = Guid.NewGuid().ToString(), CDRID = cdr.CDRId.ToString(), AccountCode = accountCode, SecondsReserved = initialSeconds, Cost = reservationCost, Rate = rate.Rate1, SetupCost = rate.SetupCost, IncrementSeconds = rate.IncrementSeconds, Inserted = DateTime.UtcNow }; db.RTCCs1.AddObject(rtccRecord); //var callCDR = (from cdr in db.CDRs where cdr.ID == cdrID select cdr).SingleOrDefault(); //if (callCDR == null) //{ // logger.Debug("The initial reservation for " + accountCode + " and " + reservationCost.ToString("0.#####") + " failed due to the no matching CDR for " + cdrID + "."); // return false; //} //else if (callCDR.HungupTime != null) //{ // logger.Debug("The initial reservation for " + accountCode + " and " + reservationCost.ToString("0.#####") + " failed due to the CDR already being hungup."); // return false; //} // The credit is available deduct it from the customer account balance and place it on the CDR. customerAccount.Credit = customerAccount.Credit - reservationCost; // Set the fields on the CDR. //callCDR.Rate = rate; //callCDR.SecondsReserved = seconds; //callCDR.AccountCode = accountCode; //callCDR.Cost = reservationCost; //db.CDRs.AddObject(cdr); db.SaveChanges(); trans.Complete(); logger.Debug("The initial reservation for " + accountCode + ", duration " + initialSeconds + "s and " + reservationCost.ToString("0.#####") + " was successful."); return reservationCost; } } } } catch (Exception excp) { logger.Error("Exception ReserveInitialCredit. " + excp); throw; } }
/// <summary> /// Updates an existing customer account. /// </summary> /// <param name="customerAccount">The customer account to update.</param> public void Update(CustomerAccount customerAccount) { using (var db = new SIPSorceryEntities()) { var existingAccount = (from ca in db.CustomerAccounts where ca.ID == customerAccount.ID select ca).SingleOrDefault(); if (existingAccount == null) { throw new ApplicationException("The customer account to update could not be found"); } else if (existingAccount.Owner.ToLower() != customerAccount.Owner.ToLower()) { throw new ApplicationException("You are not authorised to update this customer account."); } else { CheckUniqueFields(customerAccount); logger.Debug("Updating customer account " + existingAccount.AccountName + " for " + existingAccount.Owner + "."); existingAccount.AccountName = customerAccount.AccountName; existingAccount.AccountNumber = customerAccount.AccountNumber; existingAccount.Credit = customerAccount.Credit; existingAccount.PIN = customerAccount.PIN; db.SaveChanges(); } } }
public void DeleteRate(string authUser, string rateID) { using (var sipSorceryEntities = new SIPSorceryEntities()) { var existingRate = (from ra in sipSorceryEntities.Rates where ra.ID == rateID && ra.Owner.ToLower() == authUser.ToLower() select ra).FirstOrDefault(); if (existingRate == null) { throw new ApplicationException("The rate to delete could not be found."); } else if (existingRate.Owner != authUser.ToLower()) { throw new ApplicationException("Not authorised to delete the rate."); } sipSorceryEntities.Rates.DeleteObject(existingRate); sipSorceryEntities.SaveChanges(); } }
public void DeleteCustomerAccount(string authUser, string customerAccountID) { using (var sipSorceryEntities = new SIPSorceryEntities()) { var existingAccount = (from ca in sipSorceryEntities.CustomerAccounts where ca.ID == customerAccountID && ca.Owner.ToLower() == authUser.ToLower() select ca).FirstOrDefault(); if (existingAccount == null) { throw new ApplicationException("The customer account to delete could not be found."); } else if (existingAccount.Owner != authUser.ToLower()) { throw new ApplicationException("Not authorised to delete the customer account."); } sipSorceryEntities.CustomerAccounts.DeleteObject(existingAccount); sipSorceryEntities.SaveChanges(); } }
public void DBDelete(string authUsername, string key) { using (var entities = new SIPSorceryEntities()) { var existingEntry = entities.DialPlanData.Where(x => x.DataOwner == authUsername && x.DataKey == key).SingleOrDefault(); if (existingEntry != null) { entities.DeleteObject(existingEntry); entities.SaveChanges(); } } }
public void DeleteSIPProvider(string authUser, string sipProviderID) { using (var sipSorceryEntities = new SIPSorceryEntities()) { SIPProvider existingAccount = (from sp in sipSorceryEntities.SIPProviders where sp.ID == sipProviderID && sp.Owner.ToLower() == authUser.ToLower() select sp).FirstOrDefault(); if (existingAccount == null) { throw new ApplicationException("The SIP Provider to delete could not be found."); } else if (existingAccount.Owner.ToLower() != authUser.ToLower()) { throw new ApplicationException("Not authorised to delete the SIP Provider."); } sipSorceryEntities.SIPProviders.DeleteObject(existingAccount); sipSorceryEntities.SaveChanges(); } }
public void CustomerResetAPIKey(string authUser, string customerUsername) { if (authUser.IsNullOrBlank()) { throw new ArgumentException("An authenticated user is required for CustomerResetAPIKey."); } else if (authUser.ToLower() != customerUsername.ToLower()) { throw new ArgumentException("You are not authorised to reset the API key for " + customerUsername + "."); } using (var db = new SIPSorceryEntities()) { var existingCustomer = (from cust in db.Customers where cust.Name.ToLower() == customerUsername.ToLower() select cust).Single(); if (existingCustomer == null) { throw new ApplicationException("The customer record to reset the API key for could not be found."); } else { existingCustomer.APIKey = Crypto.GetRandomByteString(Customer.API_KEY_LENGTH / 2); db.SaveChanges(); } } }
/// <summary> /// Updates an existing rate. /// </summary> /// <param name="rate">The rate to update.</param> public void Update(Rate rate) { using (var db = new SIPSorceryEntities()) { var existingRate = (from ra in db.Rates where ra.ID == rate.ID select ra).SingleOrDefault(); if (existingRate == null) { throw new ApplicationException("The rate to update could not be found"); } else if (existingRate.Owner.ToLower() != rate.Owner.ToLower()) { throw new ApplicationException("You are not authorised to update this rate."); } else { if (existingRate.Prefix != rate.Prefix) { if ((from rt in db.Rates where rt.Prefix == rate.Prefix select rt).Any()) { throw new ApplicationException("The rate prefix is already in use."); } } logger.Debug("Updating rate " + existingRate.Description + " for " + existingRate.Owner + "."); existingRate.Description = rate.Description; existingRate.Prefix = rate.Prefix; existingRate.Rate1 = rate.Rate1; existingRate.RateCode = rate.RateCode; db.SaveChanges(); } } }
/// <summary> /// This method should be called once a billable call has been completed. It will calculate the final cost of the call and return /// any usused credit back to the customer account. /// </summary> /// <param name="cdrID">The ID of the CDR the credit is being returned for.</param> /// <returns>The total cost of the completed call.</returns> public decimal ReturnUnusedCredit(string rtccID) { logger.Debug("ReturnUnusedCredit for RTCC ID " + rtccID + "."); decimal actualCallCost = Decimal.Zero; using (var db = new SIPSorceryEntities()) { using (var trans = new TransactionScope()) { var rtcc = (from rtc in db.RTCCs1.Include("cdr") where rtc.ID == rtccID select rtc).First(); if (rtcc.ReconciliationResult != null) { logger.Error("This CDR has already been reconciled, no further action will be taken."); } else { var callCDR = (from cdr in db.CDRs where cdr.ID == rtcc.CDRID select cdr).SingleOrDefault(); string reconciliationError = null; if (callCDR.Duration == null) { reconciliationError = "Error, the call duration was null."; logger.Warn("The unused credit could not be returned for " + rtcc.ID + " the CDR has not been hungup."); } else if (rtcc.Cost == null) { reconciliationError = "Error, the call cost was null."; logger.Warn("The unused credit could not be returned for " + rtcc.ID + " the call cost was empty."); } else if (rtcc.AccountCode.IsNullOrBlank()) { reconciliationError = "Error, the accountcode was null."; logger.Warn("The unused credit could not be returned for " + rtcc.ID + " due to the CDR having a blank accountcode."); } else if (rtcc.Rate <= 0) { reconciliationError = "Error, the rate was not set."; logger.Warn("The unused credit could not be returned for " + rtcc.ID + " due to the CDR having no rate."); } if (reconciliationError != null) { rtcc.ReconciliationResult = reconciliationError; db.SaveChanges(); } else { string accountCode = rtcc.AccountCode; var customerAccount = db.CustomerAccounts.Where(x => x.AccountCode == accountCode).SingleOrDefault(); logger.Debug("The pre-reconciliation balance for account " + accountCode + " was " + customerAccount.Credit + " when processing RTCC ID " + rtcc.ID + "."); if (customerAccount == null) { logger.Debug("The unused credit could not be returned for RTCC ID " + rtcc.ID + " due to the no matching accountcode."); rtcc.ReconciliationResult = "Error, no matching customer for " + accountCode + "."; db.SaveChanges(); } else { // Get the owning customer's RTCC billing increment. //int rtccIncrement = (from cust in db.Customers where cust.Name.ToLower() == callCDR.Owner.ToLower() select cust.RTCCBillingIncrement).Single(); int rtccIncrement = (rtcc.IncrementSeconds <= 0) ? DEFAULT_INCREMENT_SECONDS : rtcc.IncrementSeconds; int billableDuration = (callCDR.Duration.Value % rtccIncrement == 0) ? callCDR.Duration.Value : (callCDR.Duration.Value / rtccIncrement + 1) * rtccIncrement; if (billableDuration > 0) { actualCallCost = ((Convert.ToDecimal(billableDuration) / SECONDS_FOR_RATE) * rtcc.Rate.Value) + rtcc.SetupCost; } logger.Debug("RTCC billable duration " + billableDuration + " (increment " + rtccIncrement + "), actual call cost calculated at " + actualCallCost.ToString("0.#####") + " for call with cost of " + rtcc.Cost + "."); if (Math.Round(actualCallCost, 5) < Math.Round(rtcc.Cost.Value, 5)) { decimal returnCredit = Math.Round(rtcc.Cost.Value, 5) - Math.Round(actualCallCost, 5); if (returnCredit > 0) { // There is some credit to return to the customer account. rtcc.Cost = rtcc.Cost.Value - returnCredit; rtcc.ReconciliationResult = "ok"; customerAccount.Credit = customerAccount.Credit + returnCredit; rtcc.PostReconciliationBalance = customerAccount.Credit; logger.Debug("The billed call cost was " + actualCallCost.ToString("0.#####") + ", return credit amount " + returnCredit.ToString("0.#####") + ", post reconciliation balance " + customerAccount.Credit.ToString("0.#####") + "."); } else { // An error has occurred and the credit reserved was less than the cost of the call. rtcc.ReconciliationResult = "Error: Actual call cost calculated as " + actualCallCost.ToString("0.#####"); customerAccount.Credit = customerAccount.Credit + returnCredit; rtcc.PostReconciliationBalance = customerAccount.Credit; logger.Debug("Error, the billed call cost was " + actualCallCost.ToString("0.#####") + " which resulted in a negative return credit amount of " + returnCredit.ToString("0.#####") + ", post reconciliation balance " + customerAccount.Credit.ToString("0.#####") + "."); } db.SaveChanges(); } else if (Math.Round(actualCallCost, 5) > Math.Round(rtcc.Cost.Value, 5) && Math.Abs(callCDR.Duration.Value - rtcc.SecondsReserved.Value) <= SECONDS_LENIENCY_FOR_RECONCILIATION) { rtcc.ReconciliationResult = "ok"; rtcc.PostReconciliationBalance = customerAccount.Credit; logger.Debug("The billed call duration was in +/- " + SECONDS_LENIENCY_FOR_RECONCILIATION + " of actual call duration so a billable call cost of " + rtcc.Cost.Value.ToString("0.#####") + " was accepted for an actual call cost of " + actualCallCost.ToString("0.#####") + ", post reconciliation balance " + rtcc.PostReconciliationBalance.Value.ToString("0.#####") + "."); db.SaveChanges(); } else if (Math.Round(actualCallCost, 5) > Math.Round(rtcc.Cost.Value, 5)) { rtcc.ReconciliationResult = "Error, calculated cost of " + actualCallCost.ToString("0.#####") + " exceeded reserved cost of " + rtcc.Cost.Value.ToString("0.#####") + "."; rtcc.PostReconciliationBalance = customerAccount.Credit; logger.Debug("Error, calculated cost of " + actualCallCost.ToString("0.#####") + " exceeded reserved cost of " + rtcc.Cost.Value.ToString("0.#####") + ", post reconciliation balance " + rtcc.PostReconciliationBalance.Value.ToString("0.#####") + "."); db.SaveChanges(); } else { // Call cost exactly matches the reserved cost. rtcc.ReconciliationResult = "ok"; rtcc.PostReconciliationBalance = customerAccount.Credit; logger.Debug("The billed call duration was the exact amount reserved of " + rtcc.Cost.Value.ToString("0.#####") + ", post reconciliation balance " + rtcc.PostReconciliationBalance.Value.ToString("0.#####") + "."); db.SaveChanges(); } } } } trans.Complete(); } } return(actualCallCost); }
public void UpdateSIPDialPlan(string authUser, SIPDialPlan sipDialPlan) { if (authUser.IsNullOrBlank()) { throw new ArgumentException("An authenticated user is required for UpdateSIPDialPlan."); } using (var sipSorceryEntities = new SIPSorceryEntities()) { SIPDialPlan existingAccount = (from dp in sipSorceryEntities.SIPDialPlans where dp.ID == sipDialPlan.ID && dp.Owner.ToLower() == authUser.ToLower() select dp).FirstOrDefault(); if (existingAccount == null) { throw new ApplicationException("The SIP Dial Plan to update could not be found."); } else if (existingAccount.Owner != authUser.ToLower()) { logger.Warn("User " + authUser + " was not authorised to update dial plan " + existingAccount.DialPlanName + " belonging to " + existingAccount.Owner + "."); throw new ApplicationException("Not authorised to update the SIP Dial Plan."); } else if (existingAccount.IsReadOnly) { throw new ApplicationException("This Dial Plan is read-only. Please upgrade to a Premium service to enable it."); } logger.Debug("Updating SIP dialplan " + existingAccount.DialPlanName + " for " + existingAccount.Owner + "."); existingAccount.DialPlanScript = sipDialPlan.DialPlanScript; existingAccount.LastUpdate = DateTimeOffset.UtcNow.ToString("o"); existingAccount.TraceEmailAddress = sipDialPlan.TraceEmailAddress; //existingAccount.ScriptTypeDescription = sipDialPlan.ScriptTypeDescription; existingAccount.AcceptNonInvite = sipDialPlan.AcceptNonInvite; if (existingAccount.DialPlanName != sipDialPlan.DialPlanName) { // Need to update the SIP accounts using the dial plan. string dialPlanName = existingAccount.DialPlanName; UpdateSIPAccountsDialPlanName(sipSorceryEntities, authUser, existingAccount.DialPlanName, sipDialPlan.DialPlanName); existingAccount.DialPlanName = sipDialPlan.DialPlanName; } //string validationError = SIPDialPlan.Validate(existingAccount); //if (validationError != null) //{ // throw new ApplicationException(validationError); //} sipSorceryEntities.SaveChanges(); } }
public void DeleteWebCallback(string authUser, WebCallback webCallback) { using (var sipSorceryEntities = new SIPSorceryEntities()) { var existingAccount = (from wc in sipSorceryEntities.WebCallbacks where wc.ID == webCallback.ID && wc.Owner == authUser.ToLower() select wc).FirstOrDefault(); if (existingAccount == null) { throw new ApplicationException("The web callback to delete could not be found."); } else if (existingAccount.Owner != authUser.ToLower()) { throw new ApplicationException("Not authorised to delete the web callback."); } sipSorceryEntities.WebCallbacks.DeleteObject(existingAccount); sipSorceryEntities.SaveChanges(); } }
/// <summary> /// This method attempts to reserve a the initial amount of credit for a call. /// </summary> /// <param name="accountCode">The accountCode the credit should be reserved against.</param> /// <param name="amount">The amount of credit to reserve.</param> /// <param name="rate">The rate for the call destination and the values that will be used for subsequent credit reservations.</param> /// <param name="initialSeconds">IF the reservation is successful this parameter will hold the number of seconds that were reserved for the initial reservation.</param> /// <returns>True if there was enough credit for the reservation otherwise false.</returns> public decimal ReserveInitialCredit(string accountCode, string rateID, SIPCDR cdr, out int initialSeconds) { try { logger.Debug("ReserveInitialCredit for " + accountCode + " and rate ID " + rateID + "."); initialSeconds = 0; using (var db = new SIPSorceryEntities()) { using (var trans = new TransactionScope()) { var rate = db.Rates.Where(x => x.ID == rateID).SingleOrDefault(); if (accountCode.IsNullOrBlank() || (rate == null || rate.Rate1 <= 0)) { return(Decimal.MinusOne); } logger.Debug("ReserveInitialCredit for " + accountCode + ", rate " + rate.Rate1 + ", setup cost " + rate.SetupCost + ", increment seconds " + rate.IncrementSeconds + "."); var customerAccount = db.CustomerAccounts.Where(x => x.AccountCode == accountCode).SingleOrDefault(); if (customerAccount == null) { logger.Debug("The initial reservation for " + accountCode + " failed due to the no matching accountcode."); return(Decimal.MinusOne); } // Get the owning customer's RTCC billing increment. //int rtccIncrement = (from cust in db.Customers where cust.Name.ToLower() == customerAccount.Owner.ToLower() select cust.RTCCBillingIncrement).Single(); initialSeconds = (rate.IncrementSeconds > MINIMUM_INITIAL_RESERVATION_SECONDS) ? rate.IncrementSeconds : MINIMUM_INITIAL_RESERVATION_SECONDS; decimal reservationCost = ((decimal)initialSeconds / (decimal)60 * rate.Rate1) + rate.SetupCost; if (customerAccount.Credit < reservationCost) { logger.Debug("The initial reservation for " + accountCode + ", duration " + initialSeconds + "s and " + reservationCost.ToString("0.#####") + " failed due to lack of credit."); return(Decimal.MinusOne); } else { var rtccRecord = new RTCC() { ID = Guid.NewGuid().ToString(), CDRID = cdr.CDRId.ToString(), AccountCode = accountCode, SecondsReserved = initialSeconds, Cost = reservationCost, Rate = rate.Rate1, SetupCost = rate.SetupCost, IncrementSeconds = rate.IncrementSeconds, Inserted = DateTime.UtcNow }; db.RTCCs1.AddObject(rtccRecord); //var callCDR = (from cdr in db.CDRs where cdr.ID == cdrID select cdr).SingleOrDefault(); //if (callCDR == null) //{ // logger.Debug("The initial reservation for " + accountCode + " and " + reservationCost.ToString("0.#####") + " failed due to the no matching CDR for " + cdrID + "."); // return false; //} //else if (callCDR.HungupTime != null) //{ // logger.Debug("The initial reservation for " + accountCode + " and " + reservationCost.ToString("0.#####") + " failed due to the CDR already being hungup."); // return false; //} // The credit is available deduct it from the customer account balance and place it on the CDR. customerAccount.Credit = customerAccount.Credit - reservationCost; // Set the fields on the CDR. //callCDR.Rate = rate; //callCDR.SecondsReserved = seconds; //callCDR.AccountCode = accountCode; //callCDR.Cost = reservationCost; //db.CDRs.AddObject(cdr); db.SaveChanges(); trans.Complete(); logger.Debug("The initial reservation for " + accountCode + ", duration " + initialSeconds + "s and " + reservationCost.ToString("0.#####") + " was successful."); return(reservationCost); } } } } catch (Exception excp) { logger.Error("Exception ReserveInitialCredit. " + excp); throw; } }
/// <summary> /// This method attempts to reserve a chunk of credit for to allow a call to continue. /// </summary> /// <param name="seconds">The number of seconds the reservation is being requested for.</param> /// <param name="cdrID">The CDR that the credit reservation will be applied to.</param> /// <returns>True if there was enough credit for the reservation otherwise false.</returns> public bool ReserveCredit(int seconds, string cdrID) { if (seconds <= 0 || cdrID.IsNullOrBlank()) { return false; } using (var db = new SIPSorceryEntities()) { using (var trans = new TransactionScope()) { var callCDR = (from cdr in db.CDRs where cdr.ID == cdrID select cdr).SingleOrDefault(); if (callCDR == null) { logger.Debug("The reservation for " + cdrID + " and " + seconds + "s failed due to the no matching CDR."); return false; } else { if (callCDR.HungupTime != null) { logger.Debug("The reservation for " + cdrID + " and " + seconds + "s failed due to the CDR already being hungup."); callCDR.ReservationError = "Error, call already hungup."; } else if (callCDR.AccountCode.IsNullOrBlank()) { logger.Debug("The reservation for " + cdrID + " and " + seconds + "s failed due to the CDR having a blank accountcode."); callCDR.ReservationError = "Error, empty accountcode."; } else if (callCDR.Rate <= 0) { logger.Debug("The reservation for " + cdrID + " and " + seconds + "s failed due to the CDR having no rate."); callCDR.ReservationError = "Error, empty rate."; } if (callCDR.ReservationError == null) { string accountCode = callCDR.AccountCode; var customerAccount = db.CustomerAccounts.Where(x => x.AccountCode == accountCode).SingleOrDefault(); if (customerAccount == null) { logger.Debug("The reservation for " + accountCode + " and " + seconds + "s failed due to the no matching accountcode."); callCDR.ReservationError = "Error, no customer for accountcode."; } else { decimal amount = (Convert.ToDecimal(seconds) / SECONDS_FOR_RATE) * callCDR.Rate.Value; if (customerAccount.Credit < amount) { logger.Debug("The reservation for " + accountCode + " and " + seconds + "s failed due to lack of credit."); callCDR.ReservationError = "Error, insufficient credit."; } else { // The credit is available deduct it from the customer account balance and place it on the CDR. customerAccount.Credit = customerAccount.Credit - amount; // Set the fields on the CDR. callCDR.SecondsReserved = callCDR.SecondsReserved + seconds; callCDR.Cost = callCDR.Cost + amount; } } } db.SaveChanges(); trans.Complete(); return callCDR.ReservationError == null; } } } }
/// <summary> /// This method attempts to reserve a chunk of credit for to allow a call to continue. /// </summary> /// <param name="seconds">The number of seconds the reservation is being requested for.</param> /// <param name="cdrID">The CDR that the credit reservation will be applied to.</param> /// <returns>True if there was enough credit for the reservation otherwise false.</returns> public bool ReserveCredit(int seconds, string rtccID) { if (seconds <= 0 || rtccID.IsNullOrBlank()) { return(false); } using (var db = new SIPSorceryEntities()) { using (var trans = new TransactionScope()) { var callRTCC = (from rtcc in db.RTCCs1 where rtcc.ID == rtccID select rtcc).SingleOrDefault(); if (callRTCC == null) { logger.Debug("The reservation for " + rtccID + " and " + seconds + "s failed due to the no matching RTCC record."); return(false); } else { var callCDR = (from cdr in db.CDRs where cdr.ID == callRTCC.CDRID select cdr).SingleOrDefault(); if (callCDR.HungupTime != null) { logger.Debug("The reservation for " + rtccID + " and " + seconds + "s failed due to the CDR already being hungup."); callRTCC.ReservationError = "Error, call already hungup."; } if (callRTCC.ReservationError == null) { string accountCode = callRTCC.AccountCode; var customerAccount = db.CustomerAccounts.Where(x => x.AccountCode == accountCode).SingleOrDefault(); if (customerAccount == null) { logger.Debug("The reservation for " + accountCode + " and " + seconds + "s failed due to the no matching accountcode."); callRTCC.ReservationError = "Error, no customer for accountcode."; } else { decimal amount = (Convert.ToDecimal(seconds) / SECONDS_FOR_RATE) * callRTCC.Rate.Value; if (customerAccount.Credit < amount) { logger.Debug("The reservation for " + accountCode + " and " + seconds + "s failed due to lack of credit."); callRTCC.ReservationError = "Error, insufficient credit."; } else { // The credit is available deduct it from the customer account balance and place it on the CDR. customerAccount.Credit = customerAccount.Credit - amount; // Set the fields on the CDR. callRTCC.SecondsReserved = callRTCC.SecondsReserved + seconds; callRTCC.Cost = callRTCC.Cost + amount; } } } db.SaveChanges(); trans.Complete(); return(callRTCC.ReservationError == null); } } } }
/// <summary> /// This method attempts to reserve a the initial amount of credit for a call. /// </summary> /// <param name="accountCode">The accountCode the credit should be reserved against.</param> /// <param name="amount">The amount of credit to reserve.</param> /// <param name="rate">The rate for the call destination and the values that will be used for subsequent credit reservations.</param> /// <param name="initialSeconds">IF the reservation is successful this parameter will hold the number of seconds that were reserved for the initial reservation.</param> /// <returns>True if there was enough credit for the reservation otherwise false.</returns> public decimal ReserveInitialCredit(string accountCode, decimal rate, out int initialSeconds) { logger.Debug("ReserveInitialCredit for " + accountCode + ", rate " + rate + "."); initialSeconds = 0; if (accountCode.IsNullOrBlank() || rate <= 0) { return Decimal.MinusOne; } using (var db = new SIPSorceryEntities()) { using (var trans = new TransactionScope()) { var customerAccount = db.CustomerAccounts.Where(x => x.AccountCode == accountCode).SingleOrDefault(); if (customerAccount == null) { logger.Debug("The initial reservation for " + accountCode + " failed due to the no matching accountcode."); return Decimal.MinusOne; } // Get the owning customer's RTCC billing increment. int rtccIncrement = (from cust in db.Customers where cust.Name.ToLower() == customerAccount.Owner.ToLower() select cust.RTCCBillingIncrement).Single(); initialSeconds = (rtccIncrement > MINIMUM_INITIAL_RESERVATION_SECONDS) ? rtccIncrement : MINIMUM_INITIAL_RESERVATION_SECONDS; decimal reservationCost = (decimal)initialSeconds / (decimal)60 * rate; if (customerAccount.Credit < reservationCost) { logger.Debug("The initial reservation for " + accountCode + ", duration " + initialSeconds + "s and " + reservationCost.ToString("0.#####") + " failed due to lack of credit."); return Decimal.MinusOne; } else { //var callCDR = (from cdr in db.CDRs where cdr.ID == cdrID select cdr).SingleOrDefault(); //if (callCDR == null) //{ // logger.Debug("The initial reservation for " + accountCode + " and " + reservationCost.ToString("0.#####") + " failed due to the no matching CDR for " + cdrID + "."); // return false; //} //else if (callCDR.HungupTime != null) //{ // logger.Debug("The initial reservation for " + accountCode + " and " + reservationCost.ToString("0.#####") + " failed due to the CDR already being hungup."); // return false; //} // The credit is available deduct it from the customer account balance and place it on the CDR. customerAccount.Credit = customerAccount.Credit - reservationCost; // Set the fields on the CDR. //callCDR.Rate = rate; //callCDR.SecondsReserved = seconds; //callCDR.AccountCode = accountCode; //callCDR.Cost = reservationCost; db.SaveChanges(); trans.Complete(); logger.Debug("The initial reservation for " + accountCode + ", duration " + initialSeconds + "s and " + reservationCost.ToString("0.#####") + " was successful."); return reservationCost; } } } }
public void UpdateWebCallback(string authUser, WebCallback webcallback) { if (authUser.IsNullOrBlank()) { throw new ArgumentException("An authenticated user is required for UpdateWebCallback."); } using (var sipSorceryEntities = new SIPSorceryEntities()) { var existingAccount = (from wc in sipSorceryEntities.WebCallbacks where wc.ID == webcallback.ID && wc.Owner == authUser.ToLower() select wc).FirstOrDefault(); if (existingAccount == null) { throw new ApplicationException("The web callback to update could not be found."); } else if (existingAccount.Owner != authUser.ToLower()) { throw new ApplicationException("Not authorised to update the web callback."); } logger.Debug("Updating web callback " + existingAccount.Description + " for " + existingAccount.Owner + "."); existingAccount.DialString1 = webcallback.DialString1; existingAccount.DialString2 = webcallback.DialString2; existingAccount.Description = webcallback.Description; sipSorceryEntities.SaveChanges(); } }
/// <summary> /// This method should be called once a billable call has been completed. It will calcualte the final cost of the call and return /// any usused credit back to the customer account. /// </summary> /// <param name="cdrID">The ID of the CDR the credit is being returned for.</param> public void ReturnUnusedCredit(string cdrID) { logger.Debug("ReturnUnusedCredit for CDR ID " + cdrID + "."); if (cdrID.NotNullOrBlank()) { using (var db = new SIPSorceryEntities()) { using (var trans = new TransactionScope()) { var callCDR = (from cdr in db.CDRs where cdr.ID == cdrID select cdr).SingleOrDefault(); if (callCDR == null) { logger.Error("The unused credit could not be returned for " + cdrID + " due to the no matching CDR."); } else { string reconciliationError = null; if (callCDR.Duration == null) { reconciliationError = "Error, the call duration was null."; logger.Warn("The unused credit could not be returned for " + cdrID + " the CDR has not been hungup."); } else if (callCDR.Cost == null) { reconciliationError = "Error, the call cost was null."; logger.Warn("The unused credit could not be returned for " + cdrID + " the call cost was empty."); } else if (callCDR.AccountCode.IsNullOrBlank()) { reconciliationError = "Error, the accountcode was null."; logger.Warn("The unused credit could not be returned for " + cdrID + " due to the CDR having a blank accountcode."); } else if (callCDR.Rate <= 0) { reconciliationError = "Error, the rate was not set."; logger.Warn("The unused credit could not be returned for " + cdrID + " due to the CDR having no rate."); } if (reconciliationError != null) { callCDR.ReconciliationResult = reconciliationError; db.SaveChanges(); } else { string accountCode = callCDR.AccountCode; var customerAccount = db.CustomerAccounts.Where(x => x.AccountCode == accountCode).SingleOrDefault(); if (customerAccount == null) { logger.Debug("The unused credit could not be returned for " + cdrID + " due to the no matching accountcode."); callCDR.ReconciliationResult = "Error, no matching customer for " + accountCode + "."; db.SaveChanges(); } else { // Get the owning customer's RTCC billing increment. int rtccIncrement = (from cust in db.Customers where cust.Name.ToLower() == callCDR.Owner.ToLower() select cust.RTCCBillingIncrement).Single(); int billableDuration = (callCDR.Duration.Value % rtccIncrement == 0) ? callCDR.Duration.Value : (callCDR.Duration.Value / rtccIncrement + 1) * rtccIncrement; decimal actualCallCost = (Convert.ToDecimal(billableDuration) / SECONDS_FOR_RATE) * callCDR.Rate.Value; logger.Debug("RTCC billable duration " + billableDuration + " (increment " + rtccIncrement + "), actual call cost calculated at " + actualCallCost.ToString("0.#####") + " for call with cost of " + callCDR.Cost + "."); if (Math.Round(actualCallCost, 5) < Math.Round(callCDR.Cost.Value, 5)) { decimal returnCredit = Math.Round(callCDR.Cost.Value, 5) - Math.Round(actualCallCost, 5); if (returnCredit > 0) { // There is some credit to return to the customer account. callCDR.Cost = callCDR.Cost.Value - returnCredit; callCDR.ReconciliationResult = "ok"; callCDR.PostReconciliationBalance = customerAccount.Credit + returnCredit; customerAccount.Credit = customerAccount.Credit + returnCredit; } else { // An error has occurred and the credit reserved was less than the cost of the call. callCDR.ReconciliationResult = "Error: Actual call cost calculated as " + actualCallCost.ToString("0.#####"); callCDR.PostReconciliationBalance = customerAccount.Credit + returnCredit; customerAccount.Credit = customerAccount.Credit + returnCredit; } db.SaveChanges(); } else if (Math.Round(actualCallCost, 5) > Math.Round(callCDR.Cost.Value, 5) && Math.Abs(callCDR.Duration.Value - callCDR.SecondsReserved.Value) <= SECONDS_LENIENCY_FOR_RECONCILIATION) { logger.Debug("The billed call duration was in +/- " + SECONDS_LENIENCY_FOR_RECONCILIATION + " of actual call duration so a a bille call cost of " + callCDR.Cost.Value.ToString("0.#####") + " was accepted for an actual call cost of " + actualCallCost.ToString("0.#####") + "."); callCDR.ReconciliationResult = "ok"; callCDR.PostReconciliationBalance = customerAccount.Credit; db.SaveChanges(); } else if (Math.Round(actualCallCost, 5) > Math.Round(callCDR.Cost.Value, 5)) { callCDR.ReconciliationResult = "Error, calculated cost of " + actualCallCost.ToString("0.#####") + " exceeded reserved cost of " + callCDR.Cost.Value.ToString("0.#####") + "."; callCDR.PostReconciliationBalance = customerAccount.Credit; db.SaveChanges(); } else { // Call cost exactly matches the reserved cost. callCDR.ReconciliationResult = "ok"; callCDR.PostReconciliationBalance = customerAccount.Credit; db.SaveChanges(); } } } } trans.Complete(); } } } }
public void CopySIPDialPlan(string authUser, string sipDialPlanID) { if (authUser.IsNullOrBlank()) { throw new ArgumentException("An authenticated user is required for CopySIPDialPlan."); } using (var sipSorceryEntities = new SIPSorceryEntities()) { SIPDialPlan existingAccount = (from dp in sipSorceryEntities.SIPDialPlans where dp.ID == sipDialPlanID && dp.Owner.ToLower() == authUser.ToLower() select dp).FirstOrDefault(); if (existingAccount == null) { throw new ApplicationException("The SIP Dial Plan to copy could not be found."); } else if (existingAccount.Owner != authUser.ToLower()) { logger.Warn("User " + authUser + " was not authorised to copy dial plan " + existingAccount.DialPlanName + " belonging to " + existingAccount.Owner + "."); throw new ApplicationException("Not authorised to copy the SIP Dial Plan."); } else if (existingAccount.IsReadOnly) { throw new ApplicationException("This Dial Plan is read-only. Please upgrade to a Premium service to enable it."); } logger.Debug("Copying SIP dialplan " + existingAccount.DialPlanName + " for " + existingAccount.Owner + "."); string newDialPlanName = existingAccount.DialPlanName + " Copy"; if (sipSorceryEntities.SIPDialPlans.Any(x => x.DialPlanName.ToLower() == newDialPlanName.ToLower() && x.Owner.ToLower() == authUser.ToLower())) { int attempts = 2; string newNameAttempt = newDialPlanName + " " + attempts.ToString(); while (sipSorceryEntities.SIPDialPlans.Any(x => x.DialPlanName.ToLower() == newNameAttempt.ToLower() && x.Owner.ToLower() == authUser.ToLower()) && attempts < 10) { attempts++; newNameAttempt = newDialPlanName + " " + attempts.ToString(); } if (attempts < 10) { newDialPlanName = newNameAttempt; } else { throw new ApplicationException("A new dial plan name could not be created for a dial plan copy operation."); } } SIPDialPlan copy = new SIPDialPlan(); copy.ID = Guid.NewGuid().ToString(); copy.Owner = authUser.ToLower(); copy.AdminMemberId = existingAccount.AdminMemberId; copy.Inserted = DateTimeOffset.UtcNow.ToString("o"); copy.LastUpdate = DateTimeOffset.UtcNow.ToString("o"); copy.MaxExecutionCount = SIPDialPlan.DEFAULT_MAXIMUM_EXECUTION_COUNT; copy.DialPlanName = newDialPlanName; copy.DialPlanScript = existingAccount.DialPlanScript; copy.ScriptTypeDescription = existingAccount.ScriptTypeDescription; copy.AuthorisedApps = existingAccount.AuthorisedApps; copy.AcceptNonInvite = existingAccount.AcceptNonInvite; sipSorceryEntities.SIPDialPlans.AddObject(copy); //sipSorceryEntities.SaveChanges(); logger.Debug("A new dial plan copy was created for " + existingAccount.DialPlanName + ", new dial plan name " + copy.DialPlanName + "."); if (existingAccount.ScriptType == SIPDialPlanScriptTypesEnum.SimpleWizard) { var simpleWizardRules = sipSorceryEntities.SimpleWizardRules.Where(x => x.Owner == authUser.ToLower() && x.DialPlanID == existingAccount.ID); if (simpleWizardRules != null && simpleWizardRules.Count() > 0) { foreach (var rule in simpleWizardRules) { SimpleWizardRule copiedRule = new SimpleWizardRule(); copiedRule.ID = Guid.NewGuid().ToString(); copiedRule.DialPlanID = copy.ID; copiedRule.Owner = authUser.ToLower(); copiedRule.ToMatchType = rule.ToMatchType; copiedRule.ToMatchParameter = rule.ToMatchParameter; copiedRule.Description = rule.Description; copiedRule.Command = rule.Command; copiedRule.CommandParameter1 = rule.CommandParameter1; copiedRule.CommandParameter2 = rule.CommandParameter2; copiedRule.CommandParameter3 = rule.CommandParameter3; copiedRule.CommandParameter4 = rule.CommandParameter4; copiedRule.Direction = rule.Direction; copiedRule.PatternType = rule.PatternType; copiedRule.Pattern = rule.Pattern; copiedRule.Priority = rule.Priority; copiedRule.IsDisabled = rule.IsDisabled; copiedRule.TimePattern = rule.TimePattern; copiedRule.ToSIPAccount = rule.ToSIPAccount; copiedRule.ToProvider = rule.ToProvider; sipSorceryEntities.SimpleWizardRules.AddObject(copiedRule); logger.Debug("Copied simple wizard rule priority " + rule.Priority + " to dial plan " + copy.DialPlanName + "."); } } } sipSorceryEntities.SaveChanges(); } }
public void DeleteSimpleWizardRule(string authUser, SimpleWizardRule rule) { using (var sipSorceryEntities = new SIPSorceryEntities()) { SimpleWizardRule existingRule = (from ru in sipSorceryEntities.SimpleWizardRules where ru.ID == rule.ID && ru.Owner == authUser.ToLower() select ru).FirstOrDefault(); if (existingRule == null) { throw new ApplicationException("The Simple Wizard Rule to delete could not be found."); } else if (existingRule.Owner != authUser.ToLower()) { throw new ApplicationException("Not authorised to delete the Simple Wizard Rule."); } sipSorceryEntities.SimpleWizardRules.DeleteObject(existingRule); sipSorceryEntities.SaveChanges(); } }