retrieve data using BQL in Acumatica - c#

I already create extension for APReleaseCheckProcess. I need to send RefNbr of document where DocType = 'REF' (send RefNbr of Vendor Refund) to another database.
I used this code below.
public static class APReleaseCheckProcess
{
public static void APPaymentRowPersisted(PXCache sender, PXRowPersistedEventArgs e)
{
string serverJade, dbJade, userJade, passJade;
serverJade = "BS-DEV64\\SQL2014"; //--- Server Jade : 192.168.10.13
dbJade = "SGL"; //--- DB Jade Live : SGL || DB Jade test : SGL_TEST
userJade = "sa"; //--- User ID : sa
passJade = "Admin1"; //--- Password : sa_091073
if (e.TranStatus == PXTranStatus.Completed && e.Operation == PXDBOperation.Update)
{
var doc = e.Row as APPayment;
#region Doc Type = Vendor Refund
if (doc != null && doc.Released == true && doc.DocType == "REF")
{
foreach (APAdjust oldadj in PXSelect<APAdjust,
Where<
APAdjust.adjgDocType, Equal<Required<APPayment.docType>>,
And<APAdjust.adjgRefNbr, Equal<Required<APPayment.refNbr>>,
And<APAdjust.adjNbr, Less<Required<APPayment.lineCntr>>>>>>
.Select(sender.Graph, doc.DocType, doc.RefNbr, doc.LineCntr))
{
string refNbr = oldadj.AdjdRefNbr;
string docType = oldadj.AdjdDocType;
// I need to retrieve InvoiceNbr from this query below using BQL statement:
string InvNbr = "select InvoiceNbr from APInvoice where CompanyID = 2 and RefNbr = refnbr";
// query to send to another database
using (SqlConnection conJade = new SqlConnection("server = " + serverJade + "; database = " + dbJade + "; user = " + userJade + "; password = " + passJade + ""))
{
string qRefund = "update b set b.cano = "+doc.RefNbr+"" +
"from evmaster as b " +
"inner join evmaster as a on a.svno = b.vchno " +
"where a.vchno = "+InvNbr+"";
conJade.Open();
using (SqlCommand comJade = new SqlCommand(qRefund, conJade))
{
SqlDataReader sdr = comJade.ExecuteReader();
sdr.Close();
}
}
}
}
#endregion
}
}
}
How to write the code to generate query above using BQL in Acumatica Customize project.

Assuming you want to run the query for the company of the logged in user, without error checking that would be:
((APInvoice)PXSelect<APInvoice, Where<APInvoice.refNbr, Equal<Required<APInvoice.refNbr>>>>.Select(sender.Graph, refNbr)).InvoiceNbr
If you need to run the query for a company other than the company of the logged in user, the recommended way is to put the data in a table without a CompanyID field.
The isolation of companies is strongly enforced by BQL, and you won't be able to retrieve data from another company unless you're logged into this company. The ORM also takes care of returning you the data from other company IDs if this data is split/shared with another company. For tables that don't contain a CompanyID field, the system returns all the data contained in this table.

Related

Cannot use SqlGeography as parameter with Dapper and ASP.NET core

I'm trying to insert a 'geography' datatype in SQL Server using Dapper 1.50.2 with ASP.NET Core 2.1.
I've read on several threads that it should be accepted by default since 1.32, yet I receive an exception when trying to insert the data type.
Note: I'm using a non .NET CORE data type in my entity. Microsoft.SqlServer.Types: 14.0.1016.290 since I couldn't find a good .NET core compatible geography datatype. (something with EF Core)
Entity:
public class Address : Entity{
/* .. */
public SqlGeography SpatialLocation { get; set; }
/* .. */
}
Insert method (standard):
public virtual TEntity Insert(TEntity entity){
if (string.IsNullOrEmpty(entity.CreationUser)){
entity.CreationUser = "UNKNOWN";
}
if (entity.EndDate == default(DateTime)){
entity.EndDate = DateTime.MaxValue;
}
return (TEntity) DapperExtensionsProxy.Insert(entity);
}
Insert method (specialized):
public override Address Insert(Address entity){
if (entity == null){
throw new ArgumentNullException(nameof(entity));
}
var sql = $"INSERT INTO [dbo].[Address]"
+ "([CreationDate]"
+ ",[StartDate]"
+ ",[UpdateDate]"
+ ",[EndDate]"
+ ",[CreationUser]"
+ ",[UpdateUser]"
+ ",[CityId]"
+ ",[Street]"
+ ",[Street2]"
+ ",[SpatialLocation]"
+ ",[Flags])"
+ "VALUES"
+ "(#creationDate"
+ ",#startDate"
+ ",#endDate"
+ ",#creationUser"
+ ",#cityId"
+ ",#street"
+ ",#street2"
+ ",#spatial"
+ ",#flags);";
DapperExtensionsProxy.Execute(sql, new
{
creationDate = entity.CreationDate,
startDate = entity.StartDate,
endDate = entity.EndDate,
creationUser = entity.CreationUser,
cityId = entity.CityId,
street = entity.Street,
street2 = entity.Street2,
flags = entity.Flags,
spatial = entity.SpatialLocation
});
/* should get ID back, check with SELECT SCOPE_IDENTITY() */
return entity;
}
I've also tried the Dynamic parameters approach with dapper (from the first post) but I'm unsure how to apply them together with different parameters
Exception
The member spatial of type Microsoft.SqlServer.Types.SqlGeography cannot be
used as a parameter value
UPDATE
I've solved this through a work-around with this piece of code. Getting the syntax right proved difficult.
public override Address Insert(Address entity){
if (entity == null){
throw new ArgumentNullException(nameof(entity));
}
var sql = $"INSERT INTO [dbo].[Address]"
+ "([CreationDate]"
+ ",[StartDate]"
+ ",[EndDate]"
+ ",[CreationUser]"
+ ",[CityId]"
+ ",[Street]"
+ ",[Street2]"
+ ",[SpatialLocation]"
+ ",[Flags])"
+ "VALUES"
+ "(#creationDate"
+ ",#startDate"
+ ",#endDate"
+ ",#creationUser"
+ ",#cityId"
+ ",#street"
+ ",#street2"
+ ",#spatial "
+ ",#flags); "
+ "SELECT SCOPE_IDENTITY()";
string lat = entity.SpatialLocation.Lat.Value.ToString(CultureInfo.InvariantCulture);
string longitude = entity.SpatialLocation.Long.Value.ToString(CultureInfo.InvariantCulture);
entity.Id = DapperExtensionsProxy.ExecuteScalar<int>(sql,new{
creationDate = entity.CreationDate,
startDate = entity.StartDate,
endDate = entity.EndDate,
creationUser = entity.CreationUser,
cityId = entity.CityId,
street = entity.Street,
street2 = entity.Street2,
flags = entity.Flags,
spatial = $"POINT({lat} {longitude} 4326)"
});
return entity;
}

How to check linq query result against Sql Server Database for data already exist or not?

I am using active directory for getting all the departsment and filtering distinct departments using linq query, below is my code
private static DomainController GetDomainController(string domainpath)
{
var domainContext = new DirectoryContext(DirectoryContextType.Domain, domainpath);
var domain = Domain.GetDomain(domainContext);
var controller = domain.FindDomainController();
return controller;
}
private static MyMethod()
{
var domainController = GetDomainController(ActiveDirectorySettings.DomainPath);
// Lookup the information in AD
var ldapEntry = new DirectoryEntry(string.Format("LDAP://{0}", domainController)) { AuthenticationType = AuthenticationTypes.Secure | AuthenticationTypes.FastBind };
DirectorySearcher ds;
ds = new DirectorySearcher(ldapEntry)
{
SearchScope = SearchScope.Subtree,
Filter = "(&" + "(objectClass=user)" + "(department=" + departmentname + "*))"
};
ds.PropertiesToLoad.Add("department");
if (ds.FindAll().Count >= 1)
{
//DataSet du = DataReader.CheckAdUserExist();
var results = ds.FindAll();
var uniqueSearchResults = results.Cast<SearchResult>().Select(x => GetProperty(x,"department")).Distinct();
addUsersList.AddRange(uniqueSearchResults.Select(departmentName => new UsersAndDepartments
{
UserDepartment = departmentName
}));
}
}
I want to check the linq query result with the database whether department already exist or not, I am not sure how to do that?
If what you want is to create a simple database connection using SqlConnection you just need to query your DB using the department parameter you received from your AD request.
try{
SqlConnection connection = new SqlConnection("YourConnectionstring");
connection.Open();
//Your connection string can be found through your Server DB
//Now you go through your SearchResultCollection populated by SearchResult objects
foreach(SearchResult res in uniqueSearchResult){
SqlCommand cmd = new SqlCommand("Select * from yourTable where department=" +res.Properties["department"][0].ToString() + "", connection);
SqlDataReader reader = cmd.ExecuteReader();
//Here you verify if there are corresponding rows in your table
//with the request you have executed
if(!reader.HasRows()){
//If you have not found corresponding rows, then you add the department to your
//list
addUsersList.Add(res.Properties["department"][0].ToString());
}
}
connection.close();
}catch(Exception e){
Console.WriteLine("Exception caught : \n\n" + e.ToString();
}
This should work.
There are plenty of tutorials for this, but if you are making alot of requests I do not recommend using this connection method you will just lose too much time / organization, maybe try using a persistence Framework like Entity Framework :
https://www.codeproject.com/Articles/4416/Beginners-guide-to-accessing-SQL-Server-through-C
Hope this answers your question!
Here is my solution, I have solved it myself
private static DomainController GetDomainController(string domainpath)
{
var domainContext = new DirectoryContext(DirectoryContextType.Domain, domainpath);
var domain = Domain.GetDomain(domainContext);
var controller = domain.FindDomainController();
return controller;
}
private static MyMethod()
{
var domainController = GetDomainController(ActiveDirectorySettings.DomainPath);
// Lookup the information in AD
var ldapEntry = new DirectoryEntry(string.Format("LDAP://{0}", domainController)) { AuthenticationType = AuthenticationTypes.Secure | AuthenticationTypes.FastBind };
DirectorySearcher ds;
ds = new DirectorySearcher(ldapEntry)
{
SearchScope = SearchScope.Subtree,
Filter = "(&" + "(objectClass=user)" + "(department=" + departmentname + "*))"
};
ds.PropertiesToLoad.Add("department");
if (ds.FindAll().Count >= 1)
{
// getting list of all departments from the database
var departmentsList = AllDepartments();
// getting list of all departments from active directory
var results = ds.FindAll();
// filtering distinct departments from the result
var uniqueSearchResults = results.Cast<SearchResult>().Select(x => GetProperty(x,"department")).Distinct();
// here firstly i am getting the department list from the database and checking it for null, then using linq query i am comparing the result with ad department results
if (departmentsList != null)
{
addUsersList.AddRange(from sResultSet in uniqueSearchResults
where !departmentsList.Exists(u => u.UserDepartment == sResultSet)
select new UsersAndDepartments
{
UserDepartment = sResultSet
});
}
else
{
addUsersList.AddRange(uniqueSearchResults.Select(departmentName => new UsersAndDepartments
{
UserDepartment = departmentName
}));
}
}
}

Getting Linked Server Database Tables and Views via SMO

How do I get SQL Server databases that are on a Linked Server via SMO?
Server server = GetServer("server");
Database db = server.Databases["db"];
LinkedServer ls = server.LinkedServers["ls"];
The second line above returns a regular database. The third line returns a particular linked server, which provides access to the linked server connection, but not to its data. How can I get something like:
Database db1 = server.LinkedServers["ls"].Databases["db"];
? The reason I need this is that I will be looping through different objects within the linked database, such as tables or views.
UPDATE
For further clarification, I currently have this code:
public void GenerateViews(string objectName = null)
{
Server server = new Server("server");
//Database a = server.Databases["a"];
Database b = server.Databases["b"];
b.Tables.OfType<Table>().ToList().ForEach(o => ProcessSqlObject(o));
b.Views.OfType<View>().ToList().ForEach(o => ProcessSqlObject(o));
}
//takes all tables and views in database b that have a custom extended property "CreateView", and create a view for it in database a
private void ProcessSqlObject(dynamic o) //o MUST be an SMO table or view (since they don't implement a common interface, I'm using a dynamic)
{
Database ct = (Database)o.Parent;
Database a = ct.Parent.Databases["a"];
const string viewPrefix = "V_CTC_";
const string SourceIDColumnName = "SourceID";
string objectName = (string)o.Name; //name of table or view
objectName = objectName.StartsWith("V_", StringComparison.InvariantCultureIgnoreCase) ? objectName.Substring(2) : objectName;
string viewName = viewPrefix + objectName; //remove V_ from view, so that we don't have "V_V_".
ExtendedProperty ep = (ExtendedProperty)o.ExtendedProperties["CreateView"];
bool AlreadyExists = a.Views.OfType<View>().Any(v => v.Name == viewName);
if (ep != null && ep.Value.ToString() == "1") //there IS an extended property, and its value is 1, meaning, we want a view
{
if (!AlreadyExists) //we don't already have the view
{
//ProcessSqlObject(t, viewName, SourceIDColumnName, ct, a);
StringBuilder ws = new StringBuilder();
ws.AppendLine("SELECT");
ws.AppendLine("\t2 [" + SourceIDColumnName + "]");
((ColumnCollection)o.Columns).OfType<Column>().ToList().ForEach(c =>
{
ws.AppendLine("\t, [" + c.Name + "]");
});
string linkedServer = "[ls].";
ws.AppendLine("FROM " + linkedServer + "[" + ct.Name + "].[dbo].[" + o.Name + "] WITH(NOLOCK)");
string rt = ws.ToString();
rt = rt.Replace("wholesale", "retail");
rt = rt.Replace("2 [" + SourceIDColumnName + "]", "3 [" + SourceIDColumnName + "]");
StringBuilder sql = new StringBuilder();
sql.AppendLine("CREATE VIEW " + viewName + " AS");
sql.AppendLine();
sql.AppendLine(ws.ToString());
//sql.AppendLine();
sql.AppendLine("UNION ALL");
sql.AppendLine();
sql.AppendLine(rt);
Console.WriteLine(sql);
a.ExecuteNonQuery(sql.ToString());
}
}
else //we DON't want the view
{
a.Views.OfType<View>().Single(v => v.Name == viewName).Drop();
a.Refresh();
}
}
}
I am currently passing to the second function all tables and views in a given database. This is withOUT using a linked server. I want the ability to do the same thing but for a linked server, without having to rewrite the code.
Thanks.
You don't need to connect to the server to get the tables and views (if you just need their names). The LinkedServer class provide the EnumTables method for that.
The LinkedServer class has a DataSource property that you should be able to use as the name of the remote server. If you pass this to your GetServer() function, you should get back an SMO Server object.

How can I retrieve a list of workitems from TFS in C#?

I'm trying to write a project reporting tool in WPF / C#. I want to access all the project names on our TFS (Team Foundation Server), and then display statistics for each work item in a given project.
I've got the project names, but getting the actual work items is what's giving me a hard time. Here's what I've got so far:
public const string tfsLocation = "http://whatever";
// get the top list of project names from the team foundation server
public List<string> LoadProjectList()
{
var tpc = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(tfsLocation));
var workItemStore = new WorkItemStore(tpc);
var projects = (from Project project in workItemStore.Projects select project.Name).ToList();
return projects;
}
public string GetProjectInfo(string targetProject)
{
string info = String.Empty;
var tpc = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(tfsLocation));
var workItemStore = new WorkItemStore(tpc);
foreach (Project project in workItemStore.Projects)
{
if (project.Name == targetProject)
{
info += String.Format("Project: {0}\n\n", project.Name);
info += "Work Item Types:\n";
foreach (WorkItemType item in project.WorkItemTypes)
{
info += String.Format("- {0}\n", item.Name);
info += String.Format(" - Description: {0}\n", item.Description);
info += " - Field Definitions:\n";
foreach (FieldDefinition field in item.FieldDefinitions)
{
info += String.Format(" - {0}\n", field.Name);
}
info += "\n";
}
}
}
return info;
}
GetProjectInfo sends back some helpful info about what's in each project, but so far it looks like I'm only seeing the definitions of what the WorkItems consist of, and not the actual WorkItems themselves. I think the programming I've written is looking in the wrong place.
From Microsoft's definition of WorkItem,
(http://msdn.microsoft.com/en-us/library/microsoft.teamfoundation.workitemtracking.client.workitem.aspx)
it looks like it's inside WorkItemTracking.Client, but not inside the WorkItemStore, and I'm not sure where to go to access it.
FINAL VERSION:
Here's the updated version of my function, after referencing the below answer. This just returns a long string of the work item names with new lines between, for printing out, which is all I'm trying to get working (for now).
public string GetProjectInfo(string targetProject)
{
string info = String.Empty;
var tpc = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(tfsLocation));
WorkItemStore workItemStore = new WorkItemStore(tpc);
Query query = new Query(workItemStore, "SELECT * FROM WorkItems WHERE [System.TeamProject] = #project", new Dictionary<string, string>() { { "project", targetProject } });
WorkItemCollection wic = query.RunQuery();
foreach (WorkItem item in wic)
{
info += String.Format("{0}\n", item.Title);
}
return info;
}
You need to use WIQL queries to get actual work items you are interested in, e.g. to get all work items for a particular project:
using Microsoft.TeamFoundation.WorkItemTracking.Client;
Query query = new Query(
workItemStore,
"select * from issue where System.TeamProject = #project",
new Dictionary<string, string>() { { "project", project.Name } }
);
var workItemCollection = query.RunQuery();
foreach(Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem workItem in workItemCollection)
{
/*Get work item properties you are interested in*/
foreach(Microsoft.TeamFoundation.WorkItemTracking.Client.Field field in workItem.Fields)
{
/*Get field value*/
info += String.Format("Field name: {0} Value: {1}\n", field.Name, field.Value);
}
}
I do need to extract the linked Work Item (Testcases) as well with the Bug. I have created a query and it extracts both. But my issue is while i print the Work Items Fields, All of the prints separately, with no trace of which Bug is linked to which Testcase. How can I achieve that.
public async Task<IList<WorkItem>> QueryOpenBugs(string project)
{
var credentials = new VssBasicCredential(string.Empty, this.personalAccessToken);
// create a wiql object and build our query
var wiql = new Wiql()
{
// NOTE: Even if other columns are specified, only the ID & URL are available in the WorkItemReference
//Query = "Select [Id] " +
// "From WorkItems " +
// "Where [Work Item Type] = 'Bug' " +
// "And [System.TeamProject] = '" + project + "' " +
// "And [System.State] = 'Resolved' " +
// "Order By [State] Asc, [Changed Date] Desc",
Query = "Select [System.Id],[System.WorkItemType],[System.Title]" +
"From workitemLinks " +
"Where ([Source].[System.WorkItemType] = 'Bug' " +
"And [Source].[System.TeamProject] = '" + project + "' " +
"And [Source].[System.State] = 'Resolved' )" +
"And ([Target].[System.TeamProject] = '" + project + "' " +
"And [Target].[System.WorkItemType] = 'Test Case' )",
};
using (var httpClient = new WorkItemTrackingHttpClient(this.uri, credentials))
{
// execute the query to get the list of work items in the results
var result = await httpClient.QueryByWiqlAsync(wiql).ConfigureAwait(false);
var ids = result.WorkItemRelations.Select(item => item.Target.Id).ToArray();
// some error handling
if (ids.Length == 0)
{
return Array.Empty<WorkItem>();
}
// build a list of the fields we want to see
var fields = new[] { "System.Id", "System.Title", "System.State" , "System.IterationPath", "System.Tags", "Microsoft.VSTS.Common.StateChangeDate", "System.WorkItemType", "Microsoft.VSTS.TCM.AutomationStatus"};
// get work items for the ids found in query
return await httpClient.GetWorkItemsAsync(ids, fields, result.AsOf).ConfigureAwait(false);
}
}
/// <summary>
/// Execute a WIQL (Work Item Query Language) query to print a list of open bugs.
/// </summary>
/// <param name="project">The name of your project within your organization.</param>
/// <returns>An async task.</returns>
public async Task PrintOpenBugsAsync(string project)
{
var workItems = await this.QueryOpenBugs(project).ConfigureAwait(false);
Console.WriteLine("Query Results: {0} items found", workItems.Count);
// loop though work items and write to console
//Select - BugID , TestCaseID , TestSuiteID{} , ResolvedDate , AutomationStatus{}
foreach (var workItem in workItems)
{
string WorkItemType = (string)workItem.Fields["System.WorkItemType"];
if (WorkItemType == "Bug")
{
Console.WriteLine("The Bugs are:\n\n");
Console.WriteLine(
"{0}\t{1}\t{2}\t{3}\t{4}",
workItem.Id,
workItem.Fields["System.Title"],
workItem.Fields["System.State"],
// workItem.Fields["System.RelatedLinks"],
workItem.Fields["Microsoft.VSTS.Common.StateChangeDate"],
workItem.Fields["System.WorkItemType"]);
Console.WriteLine("\n");
}
else
{
Console.WriteLine("The TestCases are:\n\n");
Console.WriteLine(
"{0}\t{1}\t{2}\t{3}\t{4}",
workItem.Id,
workItem.Fields["System.Title"],
workItem.Fields["System.State"],
workItem.Fields["Microsoft.VSTS.TCM.AutomationStatus"],
workItem.Fields["System.WorkItemType"]);
Console.WriteLine("\n");
}

Add new customer to database

I am able to successfully create a token, then create a new customer enrolling them in a particular subscription plan.
I cannot figure out how to capture the customer_id that is created for them by Stripe. I need this in order to make changes later (change plan, update CC, etc.). The code I have for creating the customer is below (I use a reader to get fname, lname, etc. and excluded that here for brevity):
private StripeCustomer CreateCustomer()
{
NameValueCollection nvc = Request.Form;
string tokenID = nvc["stripeToken"];
if (tokenID != null)
{
var tokenService = new StripeTokenService();
StripeToken stripeToken = tokenService.Get(tokenID);
}
var myCustomer = new StripeCustomerCreateOptions();
myCustomer.Email = email;
myCustomer.Description = fname + " " + lname + " (" + email + ")";
myCustomer.TokenId = tokenID;
string plan = "basic";
myCustomer.PlanId = plan;
var customerService = new StripeCustomerService();
StripeCustomer CurrentCustomer = customerService.Create(myCustomer);
}
Maybe I am thinking about this incorrectly (must be), but I was looking for the token to provide the CustomerID initially. Here is what that provides:
id: tok_102znI2MdvjLMWitzgclEEcg
livemode: false
created: 1385241151
used: false
object: "token"
type: "card"
card:
id: card_102znI2MdvjLMWitq44B0MY7
object: "card"
last4: "4242"
type: "Visa"
exp_month: 12
exp_year: 2021
fingerprint: "V2WUOPIgMkP5DGGe"
customer: null
country: "US"
name: null
address_line1: null
address_line2: null
address_city: null
address_state: null
address_zip: null
address_country: null
The token is passed back to me before the customer is created, so that obviously doesn't work.
Chris F had the right idea.
After
StripeCustomer CurrentCustomer = customerService.Create(myCustomer);
I simply needed to add something like
string custid = CurrentCustomer.id;
Then add custid to my database.
You can do this also by :
StripeCustomer currentCustomer = customerService.Create(myCustomer);
currentCustomer.StripeCardList.StripeCards.FirstOrDefault().CustomerId;

Categories