If your adding several related entities at once, should you call SaveChanges after each, or can it be done once? Im getting an error such as "Violation of PRIMARY KEY constraint".
Orders has a navigation property to an entity Records.
I want to add Records to an order, then call savechanges.
When I do, I get an error complaining about the primary key on Records.
I want to do something like this
Order o = new Order{.......}
Record r1 = new Record{...}
Record r2 = new Record{....}
o.Records.Add(r1)
o.Records.Add(r2)
Context.Orders.Add(o);
Context.SaveChanges()
Isnt this the right approach?
...
//populate order entity
Order o = new Order { AccountKey = iAccnt, OfficeKey = iOffice, UserKey = iUser, CreatedDate = dtCreateDate };
//save
ctx.Orders.Add(o);
//locations
Location origin = new Location
{
Order = o,
LocationTypeKey = ctx.LocationTypes.Where(a => a.TypeName == "Origin").First().LocationTypeId,
Address1 = " test address 1",
Address2 = " test Address 2",
City = oi.OriginCity,
StateKey = ctx.States.Where(a => a.StateName == oi.OriginState).First().StateId,
CountryKey = ctx.Countries.Where(a => a.CountryName == oi.OriginCountry).First().CountryId
};
Location destn = new Location
{
Order = o,
LocationTypeKey = ctx.LocationTypes.Where(a => a.TypeName == "Destination").First().LocationTypeId,
Address1 = " test address 1",
Address2 = " test Address 2",
City = oi.OriginCity,
StateKey = ctx.States.Where(a => a.StateName == oi.DestinationState).First().StateId,
CountryKey = ctx.Countries.Where(a => a.CountryName == oi.DestinationCountry).First().CountryId
};
try
{
int rows = ctx.SaveChanges();
if (rows == 0)
{
retval = false;
}
}
catch (DbEntityValidationException ex)
{
string exst = ex.ToString();
}
Related
When i update value of specify product, i cannot update product.name because it has unique contain
builder.HasAlternateKey(x => x.Name);
when I call _context.SaveChange(); it cause this error message :"
The property 'Product.Name' is part of a key and so cannot be modified or marked as modified. To change the principal of an existing entity with an identifying foreign key, first delete the dependent and invoke 'SaveChanges', and then associate the dependent with the new principal"
how can I update productName field ? I leave my update code below:
try
{
var product = await _context.Products.FindAsync(request.Id);
if (product == null) throw new ProjectException($"Cannot find a product with id: {request.Id}");
product.Name = request.Name;
product.Price = request.Price;
product.Stock = request.Stock;
product.Description = request.Description;
product.productStatus = request.productStatus;
//Save image
if (request.ThumbnailImage != null)
{
var thumbnailImage = await _context.ProductImages.FirstOrDefaultAsync(i => i.IsDefault == true && i.ProductId == request.Id);
if (thumbnailImage != null)
{
thumbnailImage.FileSize = request.ThumbnailImage.Length;
thumbnailImage.ImagePath = await this.SaveFile(request.ThumbnailImage);
_context.ProductImages.Update(thumbnailImage);
}
else
{
product.ProductImages = new List<ProductImage>()
{
new ProductImage()
{
Caption = "Thumbnail image",
DateCreated = DateTime.Now,
FileSize = request.ThumbnailImage.Length,
ImagePath = await this.SaveFile(request.ThumbnailImage),
IsDefault = true,
SortOrder = 1
}
};
}
}
await _context.SaveChangesAsync();
return true;
}
catch (Exception e)
{
var xx = e.Message;//get message in debuging
var x = 1;//this line make me have time to read error message :(
return false;
}
I've managed to create a payment using the C# .NET SDK, however it keeps showing up as 'unapplied' when I check in QBO.
I am providing the Invoice ID and tried to follow their developer API documentation but I been at this so long now that maybe I am missing something?
The following code creates the payment but doesn't 'receive' the payment towards the invoice, is there something I missed or need to do in order for the two to be linked together?
Payment payment = new Payment
{
ProcessPayment = false,
CustomerRef = new ReferenceType { name = customer.DisplayName, Value = customer.Id },
CurrencyRef = new ReferenceType { type = "Currency", Value = "CAD" },
TotalAmt = amount,
TotalAmtSpecified = true
};
if (method == PaymentTypes.Cash)
{
var paymentType = paymentMethods.FirstOrDefault(o => o.Id == "1");
if (paymentType != null)
{
payment.PaymentMethodRef = new ReferenceType()
{name = paymentType.Name, Value = paymentType.Id};
}
}
if (method == PaymentTypes.DebitCard)
{
var paymentType = paymentMethods.FirstOrDefault(o => o.Id == "9");
if (paymentType != null)
{
payment.PaymentMethodRef = new ReferenceType()
{ name = paymentType.Name, Value = paymentType.Id };
}
}
if (method == PaymentTypes.CreditCard)
{
var paymentType = paymentMethods.FirstOrDefault(o => o.Id == "8");
if (paymentType != null)
{
payment.PaymentMethodRef = new ReferenceType()
{ name = paymentType.Name, Value = paymentType.Id };
}
}
List<LinkedTxn> linkedTxns = new List<LinkedTxn>
{
new LinkedTxn()
{
TxnId = invoice.Id,
TxnType = TxnTypeEnum.Invoice.ToString()
},
};
foreach (Line line in invoice.Line)
{
//line.Amount = amount;
//line.AmountSpecified = true;
line.Received = amount;
line.ReceivedSpecified = true;
line.DetailType = LineDetailTypeEnum.PaymentLineDetail;
line.DetailTypeSpecified = true;
line.LinkedTxn = linkedTxns.ToArray();
}
payment.DepositToAccountRef = new ReferenceType() { Value = "5" };
payment.Line = invoice.Line;
payment.PaymentRefNum = reference;
DataService dataService = new DataService(serviceContext);
dataService.Add<Payment>(payment);
This is not an answer. However there's too much to add to the comments. I'm hoping this will be a helpful starting point (if not I'll remove it later).
Firstly I'd suggest refactoring your code and parameterise your variables. Doing so you should be able to preform repeatable testing in isolation.
When you preform the dataService.Add<Payment>(payment), store the response object as it may offer clues on how the request was processed. Alternatively if this is suppressing error messages, you might want to try an use Postman to send HTTP requests. This may help determine what's parameters are missing/ incorrect.
Avoid creating objects that are partially assigned it makes it a lot easier to read 7 work out which properties need to be assigned.
Also if you have a look at Full update a payment section on https://developer.intuit.com/app/developer/qbo/docs/api/accounting/all-entities/payment
the json payload has an additional Line with the property TxnType set to CreditMemo. I would assume you'll need to add something like ReceivePayment or CreditCardPayment?
To refactor your code consider:
// TODO - Set variables for testing purposes.
// This can be added as params to a method.
decimal amount = 100;
string reference = "";
string invoiceId = ""; // invoice.Id
string customerDisplayName = ""; //customer.DisplayName
string customerId = ""; //customer.Id
string paymentName = "Cash"; // paymentType.Name
string paymentId = "1"; // paymentType.Id
List<Line> lines = new List<Line>(); // invoice.Line
if(lines.Count() == 0)
{
// TODO: You might want to check there are lines?
throw new ArgumentException("No invoice.");
}
Line[] invoiceLines = lines.Select(m => new Line()
{
AnyIntuitObject = m.AnyIntuitObject,
Amount = m.Amount,
AmountSpecified = m.AmountSpecified,
CustomField = m.CustomField,
Id = m.Id,
LineNum = m.LineNum,
Description = m.Description,
DetailType = LineDetailTypeEnum.PaymentLineDetail, //m.DetailType,
DetailTypeSpecified = true, //m.DetailTypeSpecified,
LinkedTxn = new List<LinkedTxn>
{
new LinkedTxn()
{
TxnId = invoiceId,
TxnType = TxnTypeEnum.Invoice.ToString() // TODO: Should this be ReceivePayment?
},
}.ToArray(), //m.LinkedTxn,
LineEx = m.LineEx,
Received = amount, //m.Received,
ReceivedSpecified = true // m.ReceivedSpecified
}).ToArray();
Payment payment = new Payment
{
ProcessPayment = false,
CustomerRef = new ReferenceType { name = customerDisplayName, Value = customerId },
CurrencyRef = new ReferenceType { type = "Currency", Value = "CAD" },
TotalAmt = amount,
TotalAmtSpecified = true,
DepositToAccountRef = new ReferenceType() { Value = "5" },
Line = invoiceLines, // Variable is for debugging purposes - it should be inline or call a method.
PaymentRefNum = reference,
PaymentMethodRef = new ReferenceType()
{
name = paymentName,
Value = paymentId,
}
};
DataService dataService = new DataService(serviceContext);
Payment results = dataService.Add<Payment>(payment);
var json = JsonConvert.SerializeObject(results);
Console.Write(json); // TODO - Use break point/ or write response somewhere for clues.
I am getting the list of Invoice details by Invoice Id.
Now i want to update AmountDue by respective Invoice Id.
i tried by below code:
ByInvoiceId.AmountDue = Convert.ToDecimal(100.00);
public_app_api.Invoices.Update(ByInvoiceId);
but..Error as
"A validation exception occurred"
What's the reason behind and How to solve this?
There are a number of ways to change the amount due on an invoice, however changing the value of the property directly is not one of them.
The amount due on an invoice is driven by the totals of the line items on the invoice minus the total of payments and allocated credit. One way to change the amount due is to change the values of your line items or add/remove some line items. You won't be able to change the invoice if it has been paid/partially paid.
Another way you could change the amount due on an invoice is to add a payment against the invoice or allocate credit from a credit note, prepayment, or overpayment to the invoice
In addition to MJMortimer's answer.
You can't change the line amounts on an AUTHORISED invoice via the c# API. You have to VOID the invoice and create a new one. You can however update DRAFT and SUBMITTED ones by updating the line items.
EDIT: Here is some code to help you. This is create invoice code, but amending one is essentially the same.
public XeroTransferResult CreateInvoices(IEnumerable<InvoiceModel> invoices, string user, string status)
{
_user = XeroApiHelper.User(user);
var invoicestatus = InvoiceStatus.Draft;
if (status == "SUBMITTED")
{
invoicestatus = InvoiceStatus.Submitted;
}
else if (status == "AUTHORISED")
{
invoicestatus = InvoiceStatus.Authorised;
}
var api = XeroApiHelper.CoreApi();
var xinvs = new List<Invoice>();
foreach (var inv in invoices)
{
var items = new List<LineItem>();
foreach (var line in inv.Lines)
{
decimal discount = 0;
if (line.PriceBeforeDiscount != line.Price)
{
discount = (decimal)(1 - line.Price / line.PriceBeforeDiscount) * 100;
}
items.Add(new LineItem
{
AccountCode = line.AccountCode,
Description = line.PublicationName != "N/A" ? line.PublicationName + " - " + line.Description : line.Description,
TaxAmount = (decimal)line.TaxAmount,
Quantity = 1,
UnitAmount = (decimal)line.PriceBeforeDiscount,
DiscountRate = discount,
TaxType = line.XeroCode,
ItemCode = line.ItemCode
});
}
var person = inv.Company.People.FirstOrDefault(p => p.IsAccountContact);
if (person == null)
{
person = inv.Company.People.FirstOrDefault(p => p.IsPrimaryContact);
}
var ninv = new Invoice
{
Number = inv.ClientInvoiceId,
Type = InvoiceType.AccountsReceivable,
Status = invoicestatus,
Reference = inv.Reference,
Contact = new Contact
{
Name = inv.Company.OrganisationName,
//ContactNumber = "MM" + inv.Company.CompanyId.ToString(),
FirstName = person.FirstName,
LastName = person.LastName,
EmailAddress = person.Email,
Phones = new List<Phone>()
{
new Phone {PhoneNumber = person.Telephone, PhoneType = PhoneType.Default},
new Phone {PhoneNumber = person.Mobile, PhoneType = PhoneType.Mobile}
},
Addresses = new List<Address>
{ new Address
{
//AttentionTo = inv.Company.People.FirstOrDefault(p => p.IsAccountContact) == null
//? inv.Company.People.FirstOrDefault(p=> p.IsPrimaryContact).FullName
//: inv.Company.People.FirstOrDefault(p => p.IsAccountContact).FullName,
//AddressLine1 = inv.Company.OrganisationName,
AddressLine1 = inv.Company.Address.Address1,
AddressLine2 = inv.Company.Address.Address2 ?? "",
AddressLine3 = inv.Company.Address.Address3 ?? "",
Region = inv.Company.Address.CountyState,
City = inv.Company.Address.TownCity,
PostalCode = inv.Company.Address.PostCode,
}
}
},
AmountDue = (decimal)inv.TotalAmount,
Date = inv.InvoiceDate,
DueDate = inv.DueDate,
LineItems = items,
LineAmountTypes = LineAmountType.Exclusive
};
if (SessionContext.TransferContactDetailsToXero == false)
{
ninv.Contact = new Contact
{
Id = inv.Company.XeroId ?? Guid.Empty,
Name = inv.Company.OrganisationName
};
}
xinvs.Add(ninv);
}
var success = true;
var xinvresult = new List<Invoice>();
try
{
api.SummarizeErrors(false);
xinvresult = api.Invoices.Create(xinvs).ToList();
}
catch (ValidationException ex)
{
// Something's gone wrong
}
foreach (var inv in xinvresult)
{
var mminvoice = invoices.FirstOrDefault(i => i.ClientInvoiceId == inv.Number);
if (inv.Errors != null && inv.Errors.Count > 0)
{
success = false;
if (mminvoice != null)
{
var errors = new List<XeroError>();
foreach (var err in inv.Errors)
{
errors.Add(new XeroError { ErrorDescription = err.Message });
}
mminvoice.XeroErrors = errors;
}
}
else
{
mminvoice.XeroTransferDate = DateTime.Now;
mminvoice.XeroId = inv.Id;
mminvoice.XeroErrors = new List<XeroError>();
}
}
return new XeroTransferResult
{
Invoices = invoices,
Success = success
};
}
I have the problem that I have a MariaDB database with HeidiSQL. There are four tables and im using Linq to insert new data. One of the tables isn´t always necessary. So i marked the column with the foreign key in one of the a other tables for can be NULL. The problem is that when i create the new objects to insert into database it creates the new data in the database but the foreign key in the other table keeps emtpy.
When i undo the Null option in the column and want to insert a standardvalue instead, it throws an UpdateEntityException.
What i should mention is, that i cerated the database first in HeidiSQL and created then the code in Visual Studio with EntityFramework 5.0.
Or might the mistake caused by building and adding the database object in an if-clause?
There are some code examples of my code, i hope it will help.
DateTime aktuellesDatum = DateTime.Now;
int proId = getProjectIdByProjectnumber(zeichnungen[0].Projektnummer);
int tagId = getTagIdByTag(zeichnungen[0].Tag, zeichnungen[0].Projektnummer);
string hauptzeichnung = "";
int gruppeId = -1;
//Noch kein Projekt vorhanden
if(proId == -1)
{
using (DMSContext db = new DMSContext())
{
foreach (ZeichnungInDB zeichnungInDB in zeichnungen)
{
zeichnungInDB.Volante_Index = getVolCountByDrawingNumber(zeichnungInDB.Zeichnungsnummer) + 1;
var zeichnung = new zeichnung()
{
Zeichnung_ID = zeichnungInDB.Dateiname + "_" + zeichnungInDB.Index + "_VOL_" + zeichnungInDB.Volante_Index + "_" + aktuellesDatum.ToShortDateString(),
Zeichnungsnummer = zeichnungInDB.Zeichnungsnummer,
Index = zeichnungInDB.Index,
Zeitstempel = aktuellesDatum,
Dateiname_Org = zeichnungInDB.Dateiname,
Aenderung_Ext = zeichnungInDB.Aenderung_Ext,
Aenderung_Int = "AE_" + zeichnungInDB.Projektnummer + "_" + aktuellesDatum.Year + "-" + aktuellesDatum.Month + "-" + aktuellesDatum.Day + " " + aktuellesDatum.Hour + ":" + aktuellesDatum.Minute,
Dokumententyp = zeichnungInDB.DokumentenTyp,
Dateiendung = zeichnungInDB.Extension,
Volante_Index = zeichnungInDB.Volante_Index,
MMS_Sachmerkmal = zeichnungInDB.Mms_Sachmerkmal,
Status = zeichnungInDB.Status,
Aenderung_Bemerkung_Txt = zeichnungInDB.Aenderung_Bemerkung_Text,
Einzel_Bemerkung_Txt = zeichnungInDB.Einzel_Bemerkung,
Ahang_Link = zeichnungInDB.Anhang_Link,
Einzel_Link = zeichnungInDB.Einzel_Link,
};
db.zeichnungs.Add(zeichnung);
if(zeichnungInDB.Baugruppe_Hauptzeichnung == true)
{
hauptzeichnung = zeichnungInDB.Zeichnungsnummer;
}
}
var projekt = new projekt()
{
Projektnummer = zeichnungen[0].Projektnummer,
};
var tag = new tag()
{
Tag1 = zeichnungen[0].Tag,
};
if (!hauptzeichnung.Equals(""))
{
var baugruppe = new baugruppe
{
Hauptzeichnung = hauptzeichnung,
};
db.baugruppes.Add(baugruppe);
}
db.projekts.Add(projekt);
db.tags.Add(tag);
try
{
db.SaveChanges();
}
catch (System.Data.Entity.Validation.DbEntityValidationException dbEx)
{
Exception raise = dbEx;
foreach (var validationErrors in dbEx.EntityValidationErrors)
{
foreach (var validationError in validationErrors.ValidationErrors)
{
string message = string.Format("{0}:{1}",
validationErrors.Entry.Entity.ToString(),
validationError.ErrorMessage);
// raise a new exception nesting
// the current instance as InnerException
raise = new InvalidOperationException(message, raise);
}
}
throw raise;
}
}
This is only a short e.g. from my code because the whole cs would be to long and nobody would spend the time on so much code.
One other thing i would like to ask is. If the following code works correct for update a string in a field?
private static void updateHauptzeichnung(int baugruppeId, string zeichnungsnummer)
{
using (var context = new DMSContext())
{
var query = context.baugruppes
.Where(b => b.Baugruppe_ID == baugruppeId)
.Select(g => new { g.Hauptzeichnung })
.SingleOrDefault();
if (query != null)
{
query.Hauptzeichnung.Replace(query.Hauptzeichnung, zeichnungsnummer);
}
context.SaveChanges();
}
}
I solved my problem. I changed the foreignkey field from can be NULL to is necessary, added in the foreign table a costum dataset with ID is 0 and I give new data this ID as its foreign ID when they have no offical link to the foreign table. It might not be the best solution, but it fixed my problem.
I'm trying to specify a Product/Service list item for invoice line items of invoices that I am importing to QuickBooks Online (QBO) company file and I am getting an error.
Error I receive is:
Intuit.Ipp.Exception.IdsException: InternalServerError ---> Intuit.Ipp.Exception.EndpointNotFoundException: Ids service endpoint was not found.
The exception doesn't give any further breakdown as to if what I'm doing is valid or not.
My unit test method:
[TestMethod()]
public void CreateTest()
{
Entities.Invoice invoice = new Entities.Invoice();
invoice.ReferenceId = Guid.NewGuid().ToString("N").Substring(0, 10);
invoice.CreatedDate = DateTime.Now;
invoice.CustomerId = 1;
invoice.LineItems.Add(new InvoiceLine() { ItemName = "Initial Funding", Description = "Initial Funding", Amount = 5500 });
invoice.LineItems.Add(new InvoiceLine() { ItemName = "Lien Fee", Description = "Lien Fee", Amount = 100 });
IPPRestProfile restProfile = new IPPRestProfile(realmId, accessToken, accessTokenSecret, Intuit.Ipp.Core.IntuitServicesType.QBO, consumerKey, consumerSecret);
IPP.Invoices target = new IPP.Invoices(restProfile);
Intuit.Ipp.Data.Invoice actual = target.Create(invoice);
if (actual != null)
{
Console.WriteLine("QB Invoice ID: {0}", actual.Id);
Console.WriteLine("QB Sync Token: {0}", actual.SyncToken);
Console.WriteLine("================================================");
ObjectDumper.Write(actual, 4);
}
}
The method that the unit test calls:
public Intuit.Ipp.Data.Invoice Create(Entities.Invoice invoice)
{
// Check pre-conditions
if (invoice == null) { throw new ArgumentException("Invoice object is required.", "invoice"); }
var qboInvoice = new Intuit.Ipp.Data.Invoice();
BuildInvoiceEntity(qboInvoice, invoice);
return _Service.Add(qboInvoice) as Intuit.Ipp.Data.Invoice;
}
And finally the build invoice method:
private void BuildInvoiceEntity(Intuit.Ipp.Data.Invoice qboInvoice, Entities.Invoice invoice)
{
if (qboInvoice != null && invoice != null)
{
IQuickBooksHeader header = invoice as IQuickBooksHeader;
if (String.IsNullOrEmpty(header.Id))
{
qboInvoice.DocNumber = invoice.ReferenceId;
qboInvoice.TxnDate = invoice.CreatedDate;
qboInvoice.TxnDateSpecified = true;
// Customer
qboInvoice.CustomerRef = new ReferenceType()
{
type = objectNameEnumType.Customer.ToString(),
Value = invoice.CustomerId.ToString()
};
// AR Account
qboInvoice.ARAccountRef = new ReferenceType()
{
type = objectNameEnumType.Account.ToString(),
name = "Accounts Receivable"
};
}
if (invoice.LineItems.Count > 0)
{
Intuit.Ipp.Data.Line[] invoiceLineCollection = new Intuit.Ipp.Data.Line[invoice.LineItems.Count];
for (int i = 0; i < invoice.LineItems.Count; i++)
{
var line = invoice.LineItems[i];
var qboInvoiceLine = new Intuit.Ipp.Data.Line()
{
Amount = line.Amount,
AmountSpecified = true,
Description = line.Description,
DetailType = LineDetailTypeEnum.SalesItemLineDetail,
DetailTypeSpecified = true,
AnyIntuitObject = new SalesItemLineDetail()
{
ItemRef = new ReferenceType()
{
name = line.ItemName,
},
ItemElementName = ItemChoiceType.UnitPrice,
AnyIntuitObject = line.Amount
}
};
invoiceLineCollection[i] = qboInvoiceLine;
}
qboInvoice.Line = invoiceLineCollection;
}
}
}
If I remove this piece of code from my build method:
ItemRef = new ReferenceType()
{
name = line.ItemName,
},
the invoice is successfully added with the default "Services" list item for the Product/Service of the invoice line items.
The online documentation for the IPP .NET SDK V3 is vague on what to specify for ReferenceType. What is wrong about just specifying the name of the list item? If I'm wrong about how I'm trying to specify a Product/Service list item for invoice line items, what is the correct way?
After days of researching, I never did find an answer as to why I can't just use the name like I wanted to, even though it works that way when specifying an AccountRef. But I digress, here is my solution:
// Hold a collection of QBO items
private ReadOnlyCollection<Item> _Items;
// I set the collection in the constructor only once
public Invoices(Entities.IPPRestProfile restProfile)
{
if (restProfile == null)
throw new ArgumentException("IPPRestProfile object is required.", "restProfile");
OAuthRequestValidator oAuthValidator = new OAuthRequestValidator(restProfile.OAuthAccessToken, restProfile.OAuthAccessTokenSecret,
restProfile.ConsumerKey, restProfile.ConsumerSecret);
ServiceContext context = new ServiceContext(restProfile.RealmId, restProfile.DataSource, oAuthValidator);
_Service = new DataService(context);
_Items = (new QueryService<Item>(context)).ExecuteIdsQuery("SELECT * FROM Item", QueryOperationType.query);
}
Whenever I build my invoice I query the collection for the Id of the item by name:
private void BuildInvoiceEntity(Intuit.Ipp.Data.Invoice qboInvoice, Entities.Invoice invoice)
{
...
// Get the Id value of the item by name
string itemTypeId = _Items.Where(o => o.Name == line.ItemName).FirstOrDefault().Id;
// Specify the Id value in the item reference of the SalesItemLineDetail
var qboInvoiceLine = new Intuit.Ipp.Data.Line()
{
Amount = (decimal)amount,
AmountSpecified = true,
Description = line.Description,
DetailType = LineDetailTypeEnum.SalesItemLineDetail,
DetailTypeSpecified = true,
AnyIntuitObject = new SalesItemLineDetail()
{
ItemRef = new ReferenceType() { Value = itemTypeId },
AnyIntuitObject = (decimal)line.Rate,
ItemElementName = ItemChoiceType.UnitPrice
}
};
...
}
Hopefully this helps point someone in the right direction to a possible better solution.