SAP B1 - Component Item Cannot be Phantom Error - sapb1

I made an addon for MRP module of SAP B1 and I am trying to create a production order from order recommendation. I have an error that is "Component Item cannot be phantom Item".
The properties of my items are:
ItemCode : "M1" ,Inventory and Sales Item, Planning method: MRP - Make
ItemCode : "M2" ,it's only Sales Item, Planning method: MRP - Make
ItemCode : "M3" ,Inventory, Sales and Purchase Item, Planning method: MRP - Purchase
And the product tree is M1 --> M2 --> M3
What is the reason of this error? When I try to create production order of M1 on standard MIP without my addon it works succesfully.
Here is the code that is adding production order:
public void createWOrderFor(int id,
string itemCode,
string itemName,
int qty,
int satisSip,
string cardCode,
string cardName,
string releaseDate)
{
DataTable dataTable2 = new DataTable();
using (var connection = new SqlConnection("Server=" + server + ";Database=" + database + ";User Id=" + user + ";Password=" + pass + "; connection timeout=30;"))
{
connection.Open();
var comm = new SqlCommand("select Code, Quantity from ITT1 where Father='" + itemCode + "'", connection);
SqlDataReader dr = comm.ExecuteReader();
if (dr.HasRows)
dataTable2.Load(dr);
}
SAPbobsCOM.ProductionOrders BO_itemP;
BO_itemP = (SAPbobsCOM.ProductionOrders)getCompany().GetBusinessObject(SAPbobsCOM.BoObjectTypes.oProductionOrders);
SAPbobsCOM.ProductionOrders_Lines BO_item_lines = null;
BO_itemP.PostingDate = DateTime.Now;
BO_itemP.DueDate = DateTime.Parse(releaseDate);
BO_itemP.ItemNo = itemCode;
BO_itemP.PlannedQuantity = qty;
BO_itemP.ProductionOrderType = SAPbobsCOM.BoProductionOrderTypeEnum.bopotSpecial;
int count2 = 0;
foreach (DataRow row2 in dataTable2.Rows)
{
BO_item_lines = BO_itemP.Lines;
BO_itemP.Lines.ItemNo = row2["Code"].ToString();
BO_itemP.Lines.PlannedQuantity = miktar * qty;
BO_itemP.Lines.SetCurrentLine(count2);
count2++;
BO_itemP.Lines.BaseQuantity = miktar * qty;
BO_itemP.Lines.Warehouse = "1100";
BO_itemP.Lines.Add();
}
var retVal = BO_itemP.Add();
String err = base.getCompany().GetLastErrorDescription();
if (!err.Equals("")) { SAPbouiCOM.Framework.Application.SBO_Application.StatusBar.SetText("BO_item Hata: "+err, SAPbouiCOM.BoMessageTime.bmt_Long, SAPbouiCOM.BoStatusBarMessageType.smt_Error); }
}

You can't create a production order for a "Phantom" component. Your component 'M2' is flagged as sales item only which will set the "Phantom" Flag on the Item Master. If you alter your code to skip phantom items it should work.

Related

MS SQL Query in C# - poor performance

I calculate outstanding customers balance in C# Winforms. The code below works, but it's slow. Is there any way to improve its performance?
public DataTable GetOutStandingCustomers()
{
decimal Tot = 0;
DataTable table = new DataTable();
SqlConnection con = null;
try
{
table.Columns.Add("Code", typeof(Int32));
table.Columns.Add("Name", typeof(string));
table.Columns.Add("City", typeof(string));
table.Columns.Add("Tot", typeof(decimal));
string constr = ConfigHelper.GetConnectionString();
string query = "SELECT Code, Name,City FROM Chart WHERE LEFT(CODE,3)='401' AND Code > 401001 ";
string query0 = " SELECT(SELECT ISNULL( SUM(SalSum.Grand),'0' ) FROM SalSum WHERE SalSum.Code = #Code ) +( SELECT ISNULL(SUM(Journals.Amount),'0' ) FROM Journals WHERE Journals.DrCode = #Code ) -( SELECT ISNULL(SUM(RSalSum.Grand),'0' ) FROM RSalSum WHERE RSalSum.Code = #Code ) -( SELECT ISNULL(SUM(Journals.Amount),'0' ) FROM Journals WHERE Journals.CrCode = #Code )+(SELECT ISNULL(SUM(Chart.Debit),'0' ) FROM Chart WHERE Chart.Code = #Code) - (SELECT ISNULL(SUM(Chart.Credit), '0') FROM Chart WHERE Chart.Code = #Code)";
Person per = new Person();
con = new SqlConnection(constr);
SqlCommand com = new SqlCommand(query, con);
SqlCommand com0 = new SqlCommand(query0, con);
con.Open();
SqlDataReader r = com.ExecuteReader();
if (r.HasRows)
{
while (r.Read())
{
per.Name = Convert.ToString(r["Name"]);
per.City = Convert.ToString(r["City"]);
per.Code = Convert.ToString(r["Code"]);
com0.Parameters.Clear();
com0.Parameters.Add("#Code", SqlDbType.Int).Value = per.Code;
Tot = Convert.ToDecimal(com0.ExecuteScalar());
if (Tot != 0)
{
table.Rows.Add(per.Code, per.Name, per.City, Tot);
}
}
}
r.Close();
con.Close();
return table;
}
catch (Exception)
{
throw new Exception();
}
}
The performance problem is due to you retrieve all data from the server and filter data in the client using the complex computed expression that sum from seven tables:
if (Tot != 0)
{
table.Rows.Add(per.Code, per.Name, per.City, Tot);
}
This represent overhead over network plus you manually add the result to the datatable row by row.
The provided solution do filter in the server based on the computed expression using the CROSS APPLY
and auto create the datatable directly from the DataReader.
The benefit of CROSS APPLY is all columns are feasible to the main sql query, so you can filter on ToT column, filtering is done in the server (not the client).
public void SelctChart()
{
string sql2 = #"
select c.Code, c.Name,c.City ,oo.T
from chart c
cross apply
( select c.code,
(
(select ISNULL( SUM(SalSum.Grand),0 ) FROM SalSum WHERE SalSum.Code = c.code )
+( select ISNULL(SUM(j.Amount),0 ) FROM [dbo].[Jornals] j WHERE j.DrCode = c.code)
-( SELECT ISNULL(SUM(RSalSum.Grand),'0' ) FROM RSalSum WHERE RSalSum.Code = c.Code )
-( SELECT ISNULL(SUM(j.Amount),0 ) FROM [dbo].[Jornals] j WHERE j.CrCode = c.code )
+(SELECT ISNULL(SUM( c0.Debit),0 ) FROM [dbo].Chart c0 WHERE c0.Code = c.code)
- (SELECT ISNULL(SUM(c1.Credit), 0) FROM [dbo].Chart c1 WHERE c1.Code = c.code)
)T
) oo
where
oo.T >0
and LEFT(c.CODE,3)='401' AND c.Code > 401001
";
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlCommand command = new SqlCommand(sql2, connection);
//in case you pass #code as a a parameter
//command.Parameters.Add("#code", SqlDbType.Int);
//command.Parameters["#code"].Value = code;
try
{
connection.Open();
var reader = command.ExecuteReader();
while (!reader.IsClosed)
{
DataTable dt = new DataTable();
// Autoload datatable
dt.Load(reader);
Console.WriteLine(dt.Rows.Count);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
You can modify the method and pass code as a parameter
In this case it seems you're looping through and performing multiple queries using multiple Codes, you're also querying Chart twice. In this case you'd want to use a LEFT JOIN from Chart to your other tables.
ON Chart.Code = Salsum.Code
ON Chart.Code = Journal.Code
for example.
You will have to look at GROUP BY as well because you're aggregating some table columns by using SUM.
You may also need to make sure that Code is indexed on the tables you're querying. As long as Code is often queried like this and comparatively rarely Updated or Inserted to, then indexing the Code column on these tables is probably appropriate.
Left Joins : https://technet.microsoft.com/en-us/library/ms187518(v=sql.105).aspx
Indexing: https://technet.microsoft.com/en-us/library/jj835095(v=sql.110).aspx
Sorry I wrote a book on you here, but optimization often leads to a long answer (especially with SQL).
tldr;
Use a LEFT JOIN, grouping by Code
Index the Code columns

cant see gridview data that I want to see

In my Project I need that user will see the garages that in the same city of user .
I created users table that include - Id,UserName,Password,Email,CarModel,Year,City,
and another Table is GarageUsers that include -Id,UserName,Password,Email,Address,City,GarageName.
In Configure Data Source I insertes this code :
SELECT GargeUsers.GarageName, GargeUsers.Address,GargeUsers.City,GargeUsers.Email
FROM GargeUsers
INNER JOIN GarageuserCategory ON GargeUsers.Id = GarageuserCategory.UserId
I
WHERE (GarageuserCategory.CategoryId = 1) AND (GargeUsers.City LIKE Users.City)
(The GarageUserCategory is to show the data in the current category- its Ok ignore it).
In this code I see the all garages.
I add Session that save the user city when the user login.
But I cant see what I want in gridview. I need to know how to equal the session (USerCity) to garage city.
protected void LogIn_Click(object sender, EventArgs e)
{
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["RegistrationConnectionString"].ConnectionString);
conn.Open();
string checkuser = "select count(*) from Users where UserName= '" + UserName.Text + "'";
SqlCommand com = new SqlCommand(checkuser, conn);
int temp = Convert.ToInt32(com.ExecuteScalar().ToString());
conn.Close();
if (temp == 1)
{
conn.Open();
string checkPasswordQuery = "select Password from Users where UserName= '" + UserName.Text + "'";
SqlCommand passComm = new SqlCommand(checkPasswordQuery, conn);
string password = passComm.ExecuteScalar().ToString().Replace(" ", "");
string UserCityQuery = "select City from Users where UserName= '" + UserName.Text + "'";
SqlCommand cityComm = new SqlCommand(UserCityQuery, conn);
string UserCity = cityComm.ExecuteScalar().ToString().Replace(" ", "");
if (password == Password.Text)
{
Session["UserCity"] = UserCity;
Session["New"] = UserName.Text;
Response.Write("Password is correct");
Response.Redirect("HomePage.aspx");
}
else
{
Response.Write("Password is not correct");
}
}
else
{
Response.Write("User Name is not correct");
}
}
}
}
You say
I need that user will see the garages that in the same city of user.
That seem like you need a filter to select USER
Also you say
(The GarageUserCategory is to show the data in the current category- its Ok ignore it).
So i remove it. Next time try make the query simple with only the problem you have
SELECT
GargeUsers.GarageName,
GargeUsers.Address,
GargeUsers.City,
GargeUsers.Email
FROM
GargeUsers INNER JOIN
Users ON GargeUsers.City = Users.City
WHERE
Users.UserID = #userID <-- ADD THIS ONE
To handle variable in SQL check SELECT #local_variable (Transact-SQL)
To select the cities garage after user Select a city in the page
SELECT
GargeUsers.GarageName,
GargeUsers.Address,
GargeUsers.City,
GargeUsers.Email
FROM
GargeUsers
WHERE
GargeUsers.City = #cityID

Insert SQL statement is not working after checking against quantities from tables in database

I created a sale table which Insert function does not work properly. It shows the error message
Must declare the scalar variable "#iProductID"
at the statement
using (var sdRead = cmdOrder.ExecuteReader())
I am really stuck here. I also want to know how I can achieve for inserting SaleID with auto-increment without with any input field at the form. Every time I insert a new record, SaleID should be auto-generated and saved in the database.
My code below work like this. I checked available stocks from my Product table. If quantity order is greater than quantity from Product table, show error message.
Otherwise, proceed to inserting order information into Sale table. Any help is appreciated.
private void btnOrder_Click(object sender, EventArgs e)
{
int iQuantityDB;
int iCustomerID = Convert.ToInt32(txtCustomerID.Text);
int iProductID = Convert.ToInt32(txtProductID.Text);
decimal dPrice = Convert.ToDecimal(txtPrice.Text);
int iQuantity = Convert.ToInt32(txtQuantity.Text);
decimal dSubtotal = Convert.ToDecimal(txtSubTotal.Text);
decimal dGST = Convert.ToDecimal(txtGST.Text);
decimal dTotal = Convert.ToDecimal(txtTotal.Text);
string strConnectionString = #"Data Source = KK\SQLEXPRESS; Integrated Security = SSPI; Initial Catalog = JeanDB; MultipleActiveResultSets=True;";
using (var sqlconn = new SqlConnection(strConnectionString))
{
sqlconn.Open();
string querySelectQuantity = #"Select Quantity from dbo.JeanProduct WHERE ProductID = #iProductID";
using (var cmdOrder = new SqlCommand(querySelectQuantity, sqlconn))
{
using (var sdRead = cmdOrder.ExecuteReader())
{
sdRead.Read();
iQuantityDB = Convert.ToInt32(sdRead["Quantity"]);
}
}
if (iQuantityDB > iQuantity)
{
string InsertQuery = #"INSERT INTO Sale(CustomerID, ProductID, Price, Quantity, Subtotal, GST, Total)VALUES(#iCustomerID, #iProductID, #dPrice, #iQuantity, #dSubtotal, #dGST, #Total)";
using (var InsertCMD = new SqlCommand(InsertQuery, sqlconn))
{
InsertCMD.Connection = sqlconn;
InsertCMD.Parameters.AddWithValue("#iCustomerID", iCustomerID);
InsertCMD.Parameters.AddWithValue("#iProdcutID", iProductID);
InsertCMD.Parameters.AddWithValue("#dPrice", dPrice);
InsertCMD.Parameters.AddWithValue("#iQuantity", iQuantity);
InsertCMD.Parameters.AddWithValue("#dSubtotal", dSubtotal);
InsertCMD.Parameters.AddWithValue("#dGST", dGST);
InsertCMD.Parameters.AddWithValue("#dTotal", dTotal);
InsertCMD.ExecuteNonQuery();
LoadDataonTable();
}
}
else
{
MessageBox.Show("no more stock");
}
sqlconn.Close();
}
}
At the line using (var sdRead = cmdOrder.ExecuteReader()) your SQL SELECT query is using a parameter - WHERE ProductID = #iProductID - but this hasn't been set. Hence the error message Must declare the scalar variable "#iProductID"
Just add cmdOrder.Parameters.AddWithValue("#iProductID", iProductID) between defining the SQL and executing it, and that should clear that problem.
Moving on to the next one - you're using AddWithValue("#dTotal" but it's #Total in the SQL :)

Rdlc using MVC4 not working on IIS7

I am new to MVC4 with Entity Framework. Now i am doing RDLC report, here i am passing the parameters to the Report to Filter the data. It's working on local. When i am publishing on server it's not working. It's showing exception like ( An error occurred while processing your request). I am not able to find the exact error. Please help me to resolve this problem. Thanks in advance.
This is my Controller code:
var name = Convert.ToString(form["name"]);
var date = Convert.ToString(form["cdate"]);
var dateList = Convert.ToDateTime(form["cdate"]);
string id = Convert.ToString(form["value"]);
//id = "PDF";
LocalReport lr = new LocalReport();
string path = Path.Combine(Server.MapPath("../Reports"), "DailyReport.rdlc");
if (System.IO.File.Exists(path))
{
lr.ReportPath = path;
}
else
{
return View("Index");
}
//Assigning the parameters to the list
List<ReportParameter> lst = new List<ReportParameter>();
ReportParameter rptName = new ReportParameter("rptname", name);
ReportParameter rptDate = new ReportParameter("rptdate", date);
lst.Add(rptName);
lst.Add(rptDate);
lr.SetParameters(lst);
var rptData =
from d in db.Tbl_DailyReport
where (d.CreatedOn == dateList)
join u in db.Tbl_Users on d.CreatedBy equals u.UserID
where (u.UserName == name)
join c in db.Tbl_Mst_City on u.UserCity equals c.CityId
join ds in db.Tbl_Mst_Designation on u.UserDesignation equals ds.DesignationID
select new
{
u.UserName,
c.CityName,
ds.Designation,
d.DailyReport,
d.Achivement,
d.ReportTime,
d.Comment,
d.CreatedOn
};
//Passing the parameters to the report
ReportDataSource rd = new ReportDataSource("DataSet_dr", rptData);
lr.DataSources.Add(rd);
lr.Refresh();
string reportType = id;
string mimeType;
string encoding;
string fileNameExtension;
string deviceInfo =
"<DeviceInfo>" +
" <OutputFormat>" + id + "</OutputFormat>" +
" <PageWidth>8.5in</PageWidth>" +
" <PageHeight>11in</PageHeight>" +
" <MarginTop>0.5in</MarginTop>" +
" <MarginLeft>0.5in</MarginLeft>" +
" <MarginRight>0.5in</MarginRight>" +
" <MarginBottom>0.5in</MarginBottom>" +
"</DeviceInfo>";
Warning[] warnings;
string[] streams;
byte[] renderedBytes;
renderedBytes = lr.Render(
reportType,
deviceInfo,
out mimeType,
out encoding,
out fileNameExtension,
out streams,
out warnings);
return File(renderedBytes, mimeType);
Not sure what you want but try setting properties of RDLC report as following while publishing your website.
Build action = Content,
Copy to output Directory = Copy if newer.
and publish your website.
You can read more about publishing RDLC Here

IDataReader Issue in .Net 1.1 and .Net 4.0 for Sybase DB

I have a sybase DB which fetches results of a query properly as below...
select
S.ipoInternalID,
clientAccount,
clientPrice,
clientAccountType,
interestOnLoan =
CASE WHEN useHIBOR = 1 then
ROUND(financingAmount * (fixedRate + spreadRate) *
I.noOfDaysForInterest/365/100,2)
ELSE
ROUND(financingAmount * (I.fundingRate+ spreadRate) *
I.noOfDaysForInterest/365/100,2) END,
useHIBORSTR =
CASE WHEN useHIBOR = 1 then
"LOCK-IN RATE + SPREAD"
ELSE
"COST OF FUNDING + SPREAD" END,
from subscription S, iPO I, allocation A
where
S.ipoInternalID = #ipoInternalID and
I.ipoInternalID = #ipoInternalID and
A.ipoInternalID = #ipoInternalID and
S.ccassID *= A.ccassID
order by S.ccassID
Notice the way interestOnLoan field is calculated above.
Now when I run this query in SQL Advantage tool, it runs fine and gives me calculated values for interestOnLoan. When I run this query using .Net 1.1 API that loads this query via OleDB it runs fine...
myCommand.CommandText = myQuery;
myAdapter.SelectCommand = myCommand;
int i = myAdapter.Fill(resultSet);
My resultset fills ok.
But when I execute the above code in .net 4.0, the resultset errors out as
"Value was either too large or too small for a Decimal."
The value it has issues with is the interestOnLoan because I also executed the command via IDataReader as below...
using (var dr = myCommand.ExecuteReader())
{
resultSet.Tables.Add(ConvertDataReaderToTableManually(dr));
}
private static DataTable ConvertDataReaderToTableManually(IDataReader dr) {
var dt = new DataTable();
var dtSchema = dr.GetSchemaTable();
var listCols = new List<DataColumn>();
if (dtSchema != null) {
foreach (DataRow drow in dtSchema.Rows) {
var columnName = Convert.ToString(drow["ColumnName"]);
var t = (Type) (drow["DataType"]);
var column = new DataColumn(columnName, t);
column.Unique = (bool) drow["IsUnique"];
column.AllowDBNull = (bool) drow["AllowDBNull"];
column.AutoIncrement = (bool) drow["IsAutoIncrement"];
listCols.Add(column);
dt.Columns.Add(column);
}
}
// Read rows from DataReader and populate the DataTable
int j = 0;
while (dr.Read()) {
j++;
var dataRow = dt.NewRow();
for (int i = 0; i < listCols.Count; i++) {
try {
dataRow[((DataColumn)listCols[i])] = dr[i];
} catch (Exception ex1) { }
}
dt.Rows.Add(dataRow);
}
return dt;
}
Here it errors out at the dataRow[((DataColumn)listCols[i])] = dr[i] where it has issues reading from dr[i];
When observed the ith column is nothing but interestOnLoan.
So somehow .Net 4.0 is not able to read this value. It can read other decimal values correctly such as clientPrice.
Why could this be happening....
Also I wanted to ask is there any way I can load the values in the DataReader as Double (instead of Decimal) by default?
I didnt get the reason why .NET 4.0 had issues ith the above query but when I changed the query as below it worked in both (.Net 1.1 and 4.0)
select
S.ipoInternalID,
clientAccount,
clientPrice,
clientAccountType,
interestOnLoan = ROUND(
(CASE WHEN useHIBOR = 1 THEN
((financingAmount*(fixedRate + spreadRate) * .noOfDaysForInterest)/365.0)
ELSE
((financingAmount*(I.fundingRate+spreadRate)*I.noOfDaysForInterest)/365.0)
END) / 100.0, 2),
useHIBORSTR =
CASE WHEN useHIBOR = 1 then
"LOCK-IN RATE + SPREAD"
ELSE
"COST OF FUNDING + SPREAD" END,
from subscription S, iPO I , allocation A
where
S.ipoInternalID = #ipoInternalID and
I.ipoInternalID = #ipoInternalID and
A.ipoInternalID = #ipoInternalID and
S.ccassID *= A.ccassID
order by S.ccassID