Odoo rpc query with join in C# - c#

I have this type of code to make a selection from Odoo Rpc:
XmlRpcClient client = new XmlRpcClient();
client.Url = LocalApplication.Url;
client.Path = "common";
// LOGIN BG
XmlRpcRequest requestLogin = new XmlRpcRequest("authenticate");
requestLogin.AddParams(LocalApplication.Db, LocalApplication.User, LocalApplication.Pass, XmlRpcParameter.EmptyStruct());
XmlRpcResponse responseLogin = client.Execute(requestLogin);
// READ
client.Path = "object";
// var x = client.Execute("select * from res_partner");
XmlRpcRequest requestSearch = new XmlRpcRequest("execute_kw");
requestSearch.AddParams(LocalApplication.Db, responseLogin.GetInt(),
LocalApplication.Pass, "res.partner", "search_read",
XmlRpcParameter.AsArray(
XmlRpcParameter.AsArray(
XmlRpcParameter.AsArray("is_agent", "=", "True".ToLower()))),
XmlRpcParameter.AsStruct(
XmlRpcParameter.AsMember("fields",
XmlRpcParameter.AsArray("name", "id", "phone", "email"))));
// SAVE
XmlRpcResponse responseSearch = client.Execute(requestSearch);
var agents = (responseSearch.GetObject() as List<object>);
Taswiq.Bussiness.Person p = new Person(LocalApplication.ConnectionString);
if (agents != null)
{
foreach (var a in agents)
{
Dictionary<string, object> agent = a as Dictionary<string, object>;
p.Name = agent["name"].ToString() == "False" ? "ND" : agent["name"].ToString();
p.Marca = Convert.ToInt32(agent["id"]);
p.Type = 1;
p.Phone = agent["phone"].ToString() == "False" ? "ND" : agent["phone"].ToString();
p.Email = agent["email"].ToString() == "False" ? "ND" : agent["email"].ToString();
MessageStruct result = p.Save();
if (result.HasErrors)
{
return result.Message;
}
}
return "Success";
}
All works fine, but now I need to make some join from this 2 tables like this:
Select *
from product_template as d
inner join product_product as r on r.product_tmpl_id = d.id;
I can't find anything but Python code.
Can anyone help me with that?

I think I found a solution. I do not know if is the best way but in my case it solved the problem.
I have made same list with every table that I need to go with join:
public List<object> lstStock = new List<object>();
public void PopulateStockList()
{
XmlRpcClient stock = new XmlRpcClient();
stock.Url = LocalApplication.Url;
stock.Path = "common";
//LOGIN BG
XmlRpcRequest requestLogin = new XmlRpcRequest("authenticate");
requestLogin.AddParams(LocalApplication.Db, LocalApplication.User,
LocalApplication.Pass,
XmlRpcParameter.EmptyStruct());
XmlRpcResponse responseLogin = stock.Execute(requestLogin);
//READ
stock.Path = "object";
XmlRpcRequest requestSearch = new XmlRpcRequest("execute_kw");
requestSearch.AddParams(LocalApplication.Db, responseLogin.GetInt(),
LocalApplication.Pass, "stock.quant", "search_read",
XmlRpcParameter.AsArray(
XmlRpcParameter.AsArray(
XmlRpcParameter.AsArray("id", ">", 0)
)
),
XmlRpcParameter.AsStruct(
XmlRpcParameter.AsMember("fields",
XmlRpcParameter.AsArray("product_id", "quantity", "in_date",
"owner_id"))
)
);
XmlRpcResponse responseSearch = stock.Execute(requestSearch);
lstStock = (responseSearch.GetObject() as List<object>);
}
Same way for every table that i need to join with.
And in the end, inside the foreach i made that join that i need to populate my sql table
public string DownloadRpcStock()
{
try
{
PopulateProdTemplate();
PopulateStockList();
PopulateProductList();
Stock s = new Stock(LocalApplication.ConnectionString);
foreach (var st in lstStock)
{
Dictionary<string, object> stc = st as Dictionary<string, object>;
var productId = stc["product_id"] as List<object>;
if (productId != null)
s.IdProduct = Convert.ToInt32(productId[0]); //Din lista care vine pe product_id imi iau valoarea dorita
s.IdStorage = 1;
s.StockQuantity = Convert.ToDouble(stc["quantity"]);
s.Rest = Convert.ToDouble(stc["quantity"]);
var prod = lstProduct.FirstOrDefault(
k => Convert.ToInt32((k as Dictionary<string, object>)["id"]) == Convert.ToInt32((stc["product_id"] as List<object>)[0])) as Dictionary<string, object>;
var prodTemplate =
lstProdTemplate.FirstOrDefault(
k => Convert.ToInt32((k as Dictionary<string, object>)["id"]) ==
Convert.ToInt32((prod["product_tmpl_id"] as List<object>)[0])) as Dictionary<string, object>;
if (prodTemplate != null)
s.Price = Convert.ToDouble(prodTemplate["list_price"]);
s.Batch = "NoBatch"; // Provizoriu
s.Date = Convert.ToDateTime(stc["in_date"]);
s.ExpireDate = Convert.ToDateTime(stc["in_date"]); // Provizoriu
s.IdCustomerSuplier = Convert.ToInt32(stc["owner_id"]);
s.Id = 0; //if (Id == 0) act = insert
MessageStruct result = s.Save();
if (result.HasErrors)
{
return result.Message;
}
}
return "Success";
}
catch (Exception ex)
{
return ex.Message;
}
}
I found many such questions on the internet without a solution.
I hope my solution will help someone else in the future.

Related

How do I refactor the code which contains foreach loop

I need to refactor the below code so that the deleted_at logic will be outside of the foreach (var app in data) loop. I tried to create the list guids and then add guids to it but its not working because model.resources is inside the loop and it is still deleting all the apps.
I need deleted_at logic outside because I'm trying to delete all apps which are in the database but are not in new data that I'm receiving from API.
If you have a better approach on my code I would love to see that, Thank you.
public async Task GetBuilds()
{
var data = new List<GetBuildTempClass>();
var guids = new List<GetBuildTempClass>();
using (var scope = _scopeFactory.CreateScope())
{
var _DBcontext = scope.ServiceProvider.GetRequiredService<PCFStatusContexts>();
foreach (var app in data)
{
var request = new HttpRequestMessage(HttpMethod.Get,
"apps/" + app.AppGuid + "/builds?per_page=200&order_by=updated_at");
var response = await _client_SB.SendAsync(request);
var json = await response.Content.ReadAsStringAsync();
BuildsClass.BuildsRootObject model =
JsonConvert.DeserializeObject<BuildsClass.BuildsRootObject>(json);
foreach (var item in model.resources)
{
var x = _DBcontext.Builds.FirstOrDefault(o =>
o.Guid == Guid.Parse(item.guid));
if (x == null)
{
_DBcontext.Builds.Add(new Builds
{
Guid = Guid.Parse(item.guid),
State = item.state,
CreatedAt = item.created_at,
UpdatedAt = item.updated_at,
Error = item.error,
CreatedByGuid = Guid.Parse(item.created_by.guid),
CreatedByName = item.created_by.name,
CreatedByEmail = item.created_by.email,
AppGuid = app.AppGuid,
AppName = app.AppName,
Foundation = 2,
Timestamp = DateTime.Now
});
}
else if (x.UpdatedAt != item.updated_at)
{
x.State = item.state;
x.UpdatedAt = item.updated_at;
x.Timestamp = DateTime.Now;
}
var sqlresult = new GetBuildTempClass
{
AppGuid = Guid.Parse(item.guid)
};
guids.Add(sqlresult);
}
//var guids = model.resources.Select(r => Guid.Parse(r.guid));
var builds = _DBcontext.Builds.Where(o =>
guids.Contains(o.Guid) == false &&
o.Foundation == 2 && o.DeletedAt == null);
foreach (var build_item in builds)
{
build_item.DeletedAt = DateTime.Now;
}
}
await _DBcontext.SaveChangesAsync();
}
}
I got it working I added var guids = new List < Guid > (); list to store data,
added guids.Add(Guid.Parse(item.guid)); to populate the list and finally wrote var builds = _DBcontext.Builds.Where(o = >guids.Contains(o.Guid) == false && o.Foundation == 2 && o.DeletedAt == null); outside the loop.
If anyone has a better suggestion please add a new answer.
public async Task GetBuilds() {
var data = new List < GetBuildTempClass > ();
var guids = new List < Guid > ();
using(var scope = _scopeFactory.CreateScope()) {
var _DBcontext = scope.ServiceProvider.GetRequiredService < PCFStatusContexts > ();
foreach(var app in data) {
var request = new HttpRequestMessage(HttpMethod.Get, "apps/" + app.AppGuid + "/builds?per_page=200&order_by=updated_at");
var response = await _client_SB.SendAsync(request);
var json = await response.Content.ReadAsStringAsync();
BuildsClass.BuildsRootObject model = JsonConvert.DeserializeObject < BuildsClass.BuildsRootObject > (json);
foreach(var item in model.resources) {
var x = _DBcontext.Builds.FirstOrDefault(o = >o.Guid == Guid.Parse(item.guid));
if (x == null) {
_DBcontext.Builds.Add(new Builds {
Guid = Guid.Parse(item.guid),
State = item.state,
CreatedAt = item.created_at,
UpdatedAt = item.updated_at,
Error = item.error,
CreatedByGuid = Guid.Parse(item.created_by.guid),
CreatedByName = item.created_by.name,
CreatedByEmail = item.created_by.email,
AppGuid = app.AppGuid,
AppName = app.AppName,
Foundation = 2,
Timestamp = DateTime.Now
});
}
else if (x.UpdatedAt != item.updated_at) {
x.State = item.state;
x.UpdatedAt = item.updated_at;
x.Timestamp = DateTime.Now;
}
guids.Add(Guid.Parse(item.guid));
}
}
var builds = _DBcontext.Builds.Where(o = >guids.Contains(o.Guid) == false && o.Foundation == 2 && o.DeletedAt == null);
foreach(var build_item in builds) {
build_item.DeletedAt = DateTime.Now;
}
await _DBcontext.SaveChangesAsync();
}
}

How to apply multiple optional filters in a LINQ query

I have a page where user can select any number of search filters to apply search
When user clicks on search, these parameters are passed to my GetIndicatorData method to perform the query. However, it doesn't seem to work for me.
Here is my code
public static List<tblindicators_data_custom> GetIndicatorsData(string status, int service_id, int operator_id, string year, string frequency)
{
var date = Convert.ToDateTime(year + "-01-01");
int[] numbers = status.Split(',').Select(n => int.Parse(n)).ToArray();
var ict = new ICT_indicatorsEntities();
var result = from ind in ict.tblindicators_data
join ser in ict.tblservices on ind.service_id equals ser.Id
join oper in ict.tbloperators on ind.operator_id equals oper.Id
where numbers.Contains(ind.status) && (ind.date_added.Year == date.Year)
select new
{
ind.Id,
ind.service_id,
ind.survey_id,
ind.operator_id,
ind.type,
ind.date_added,
ind.quater_start,
ind.quater_end,
ind.status,
ind.month,
service = ser.name,
operator_name = oper.name
};
List<tblindicators_data_custom> data = new List<tblindicators_data_custom>();
foreach (var item in result)
{
tblindicators_data_custom row = new tblindicators_data_custom();
row.Id = item.Id;
row.survey_id = item.survey_id;
row.service_id = item.service_id;
row.service_name = item.service;
row.operator_id = item.operator_id;
row.operator_name = item.operator_name;
row.date_added = item.date_added;
row.quater_start = item.quater_start;
row.type = item.type;
row.quater_end = item.quater_end;
row.month = item.month == null? DateTime.Now:item.month;
row.status = item.status;
data.Add(row);
}
return data;
}

How i can add Couchbase Documents in a list?

I'm experimenting with Couchbase + Xamarin.Forms trying to do a simple search, showing the results in a ListView but I've stuck. :(
Someone know how to add the rows/documents of a query in a list?
public List<Visitor> SearchRecord (string word)
{
var viewByName = db.GetView ("ByName");
viewByName.SetMap((doc, emit) => {
emit (new object[] {doc["first_name"], doc["last_name"]}, doc);
}, "2");
var visitorQuery = viewByName.CreateQuery();
visitorQuery.StartKey = new List<object> {word};
// visitorQuery.EndKey = new List<object> {word, new Dictionary<string, object>()};
visitorQuery.Limit = 100;
var visitors = visitorQuery.Run();
var visitorList = new List<Visitor> ();
foreach (var visitor in visitors) {
// visitorList.Add(visitor.Document); <-- Error.
System.Console.WriteLine(visitor.Key);
}
return visitorList;
}
I get the error messages:
Error CS1501: No overload for method Add' takes2' arguments
(CS1501) (Demo_Couchbase.Droid) Error CS1502: The best overloaded
method match for
System.Collections.Generic.List<Demo_Couchbase.Visitor>.Add(Demo_Couchbase.Visitor)'
has some invalid arguments (CS1502) (RegistroAgil_Couchbase.Droid)
Error CS1503: Argument#1' cannot convert Couchbase.Lite.Document'
expression to typeDemo_Couchbase.Visitor' (CS1503)
(Demo_Couchbase.Droid)
Thank you in advance for any help you can provide.
There is problem in your mapping part. You can directly cast documents on GetView.
You can try bellow code.
public List<Visitor> SearchRecord (string word)
{
var viewByName = db.GetView<Visitor>("ByName","ByName");
var visitorQuery = viewByName.CreateQuery();
visitorQuery.StartKey = new List<object> {word};
visitorQuery.Limit = 100;
var visitors = visitorQuery.Run();
var visitorList = new List<Visitor> ();
foreach (var visitor in visitors) {
visitorList.Add(visitor.Document);
System.Console.WriteLine(visitor.Key);
}
return visitorList;
}
I don't know if this is the most elegant solution though but my code works fine now.
Visitor ToRecord(Document d) {
var props = d.Properties;
return new Visitor {
Id = props["_id"].ToString(),
FirstName = (string)props["first_name"],
LastName = (string)props["last_name"],
Occupation = (string)props["occupation"],
Company = (string)props["company"],
Email = (string)props["email"],
Phone = (string)props["phone"],
Birthday = (string)props["birthday"],
LastVisit = (string)props["last_visit"],
LocalImagePath = (string)props["local_image_path"],
Type = (string)props["type"],
CreatedAt = (string)props["created_at"],
UpdatedAt = (string)props["updated_at"],
DeletedAt = (string)props["deleted_at"]
};
}
public List<Visitor> SearchRecord (string word)
{
var viewByName = db.GetView ("ByName");
viewByName.SetMap((doc, emit) => {
if ((doc.ContainsKey("type") && doc["type"].ToString() == "visitor") && (doc.ContainsKey("deleted_at") && doc["deleted_at"] == null))
emit (new [] {doc["first_name"], doc["last_name"]}, doc);
}, "2");
var visitorQuery = viewByName.CreateQuery();
visitorQuery.StartKey = word;
visitorQuery.Limit = 50;
var rows = visitorQuery.Run();
var visitorList = new List<Visitor> ();
for (int i = 0; i < rows.Count (); i++) {
var row = rows.GetRow (i);
var name = row.Document.GetProperty ("first_name").ToString ().ToLower () + " " + row.Document.GetProperty ("last_name").ToString ().ToLower ();
if (name.Contains (word))
visitorList.Add(ToRecord(row.Document));
}
return visitorList;
}

Multiple Array Insertion in C# using Foreach statement

private void Updated_ModRecFGA()
{
try
{
DRFGAModifiedRecord TABLE = new DRFGAModifiedRecord();
foreach (ArrayFunc Row in ArrayFunc.QueryResult)
{
foreach (ArrayFunc Row2 in ArrayFunc.QueryResult1)
{
TABLE.RecordDocID = Row.FGACol1;
TABLE.DRNo = Row.FGACol2;
TABLE.DDNum = Row.FGACol3;
TABLE.LineNumber = Row.FGACol4;
TABLE.Itemnmbr = Row2.updItemCode;
TABLE.Itemdesc = Row2.updItemDesc;
TABLE.Pallet = Row2.updPallets;
TABLE.BagsNo = Row2.updBagsNo;
TABLE.TotalLoaded = Row2.updTotalKgs;
TABLE.PostStat = Row2.updPostStat;
TABLE.ProdCode = Row2.updProdcode;
TABLE.VariantCode = Row2.updVariantCode;
TABLE.DateModify = DateTime.Now.ToShortDateString();
TABLE.TimeModify = DateTime.Now.ToShortTimeString();
TABLE.UserModify = "Mik";//GlobalvarClass.LogUser;
TABLE.ReasonModify = GlobalvarClass.GetModReasOnDR;
TABLE.FileType = "New";
saveREC(TABLE);
gfunc.MsgBox("Saved", 1);
}
}
ArrayFunc.QueryResult.Clear();
ArrayFunc.QueryResult1.Clear();
GlobalvarClass.GetModReasOnDR = string.Empty;
}
catch (Exception ex) { MessageBox.Show(ex.Message.ToString()); }
}
Is there another proper way to use this kind of code something like this to avoid duplicate? I know it duplicate because of two foreach statement
Just combine the two using linq. Remember to include using System.Linq; in your using clause.
var results = ArrayFunc.QueryResult.SelectMany(l1 => ArrayFunc.QueryResult1, (l1, l2) =>
new DRFGAModifiedRecord
{
RecordDocID = l1.FGACol1,
DRNo = l1.FGACol2,
DDNum = l1.FGACol3,
LineNumber = l1.FGACol4,
Itemnmbr = l2.updItemCode,
Itemdesc = l2.updItemDesc,
Pallet = l2.updPallets,
BagsNo = l2.updBagsNo,
TotalLoaded = l2.updTotalKgs,
PostStat = l2.updPostStat,
ProdCode = l2.updProdcode,
VariantCode = l2.updVariantCode,
DateModify = DateTime.Now.ToShortDateString(),
TimeModify = DateTime.Now.ToShortTimeString(),
UserModify = "Mik",//GlobalvarClass.LogUser
ReasonModify = GlobalvarClass.GetModReasOnDR,
FileType = "New"
}).ToList();
foreach (row in results)
{
saveREC(row);
gfunc.MsgBox("Saved", 1);
}
Alternatively you can do it with a query expression:
var result = (from l1 in ArrayFunc.QueryResult
from l2 in ArrayFunc.QueryResult1
select new DRFGAModifiedRecord { RecordDocID = l1.FGACol1 ... }).ToList();

What can be used instead of Datatable in LINQ

I have a SQL query that returns a Datatable:
var routesTable = _dbhelper.Select("SELECT [RouteId],[UserId],[SourceName],[CreationTime] FROM [Routes] WHERE UserId=#UserId AND RouteId=#RouteId", inputParams);
and then we can work with Datatable object of routesTable
if (routesTable.Rows.Count == 1)
{
result = new Route(routeId)
{
Name = (string)routesTable.Rows[0]["SourceName"],
Time = routesTable.Rows[0]["CreationTime"] is DBNull ? new DateTime() : Convert.ToDateTime(routesTable.Rows[0]["CreationTime"])
};
result.TrackPoints = GetTrackPointsForRoute(routeId);
}
I want to change this code to linq but I don't know how can I simulate Datatable in LINQ ,I wrote this part:
Route result = null;
aspnetdbDataContext aspdb = new aspnetdbDataContext();
var Result = from r in aspdb.RouteLinqs
where r.UserId == userId && r.RouteId==routeId
select r;
....
but I don't know how can I change this part:
if (routesTable.Rows.Count == 1)
{
result = new Route(routeId)
{
Name = (string)routesTable.Rows[0]["SourceName"],
Time = routesTable.Rows[0]["CreationTime"] is DBNull ? new DateTime() : Convert.ToDateTime(routesTable.Rows[0]["CreationTime"])
};
would you please tell me how can I do this?
EDIT
here you can see the whole block of code in original
public Route GetById(int routeId, Guid userId)
{
Route result = null;
var inputParams = new Dictionary<string, object>
{
{"UserId", userId},
{"RouteId", routeId}
};
var routesTable = _dbhelper.Select("SELECT [RouteId],[UserId],[SourceName],[CreationTime] FROM [Routes] WHERE UserId=#UserId AND RouteId=#RouteId", inputParams);
if (routesTable.Rows.Count == 1)
{
result = new Route(routeId)
{
Name = (string)routesTable.Rows[0]["SourceName"],
Time = routesTable.Rows[0]["CreationTime"] is DBNull ? new DateTime() : Convert.ToDateTime(routesTable.Rows[0]["CreationTime"])
};
result.TrackPoints = GetTrackPointsForRoute(routeId);
}
return result;
}
SELECT Function:
public DataTable Select(string query, Dictionary<string, object> parameters)
{
var dt = new DataTable();
using (_command = new SqlCommand(query, _connnection))
{
InitializeParametersAndConnection(parameters);
using (_adapter = new SqlDataAdapter(_command))
{
_adapter.Fill(dt);
}
}
return dt;
}
and the GetTrackPointsForRoute
private List<TrackPoint> GetTrackPointsForRoute(int routeId)
{
aspnetdbDataContext aspdb = new aspnetdbDataContext();
var result = new List<TrackPoint>();
var trackPointsTable = from t in aspdb.TrackPointlinqs
where t.RouteFK == routeId
select t;
foreach (var trackPointRow in trackPointsTable)
{
var trackPoint = new TrackPoint
{
Id = (int)trackPointRow.TrackPointId,
Elevation = Convert.ToSingle(trackPointRow.Elevation),
Latitude = Convert.ToDouble(trackPointRow.Latitude),
Longitude = Convert.ToDouble(trackPointRow.Longitude),
Time = trackPointRow.TrackTime is DBNull ? new DateTime() : (DateTime)trackPointRow.TrackTime
};
result.Add(trackPoint);
}
return result;
}
var firstRoute = aspdb.RouteLinqs
.Where(r => r.UserId == userId && r.RouteId == routeId)
.FirstOrDefault();
if (firstRoute == null)
{
return null;
}
else
{
return new Route(routeId)
{
Name = first.SourceName,
Time = first.CreationTime ?? new DateTime(),
TrackPoints = GetTrackPointsForRoute(routeId)
};
}
If this is LINQ to SQL you can simplify it further (this won't work with LINQ to Entity Framework though):
return aspdb.RouteLinqs
.Where(r => r.UserId == userId && r.RouteId == routeId)
.Select(r => new Route(routeId)
{
Name = r.SourceName,
Time = r.CreationTime ?? new DateTime(),
TrackPoints = GetTrackPointsForRoute(routeId)
})
.FirstOrDefault();
Note: You probably can replace GetTrackPointsForRoute with a join to the child table, meaning that the entire method can be done with a single call to the database, rather than one call to get the routes, and a second call to get the points. To do this you should learn about associations and joins in LINQ to SQL.

Categories