MappingEntityClassesException - Mapping definition neither have entities nor collection - shopware

I have written a product import which imports some product data via CLI command.
When i run it multiple times the defined product property (color) does not get overwritten but adden.
Example
First run - color: red
Second run: - color: green
The importer sums the properties up, So the color has then multiple values - red AND green.
I want to solve this by deleting the product properties before the importer runs by this code:
$items = $this->productPropertyRepository->search((new Criteria())->addFilter(new EqualsAnyFilter('productId',
$productIds)), Context::createDefaultContext());
$ids = [];
foreach ($items as $item) {
$ids[] = ['productId' => $item['product_id'], 'productVersionId' => $item['product_version_id']];
}
if ($ids) {
$this->productPropertyRepository->delete($ids, Context::createDefaultContext());
}
But this does not work because there is no ProductPropertyEntity class in Shopware 6.
I get an exception when execution the first line.
[Shopware\Core\Framework\DataAbstractionLayer\Exception\MappingEntityClassesException]
Mapping definition neither have entities nor collection.
Whats the best way to avoid keeping orphaned data in this case?

Related

In Core Data, how sort an NSFetchRequest depending on the sum of an attribute of a child entity? (SwiftUI)

I am building an iOS app in SwiftUI for which I have a Core Data model with two entities:
CategoryEntity with attribute: name (String)
ExpenseEntity with attributes: name (String) and amount (Double)
There is a To-many relationship between CategoryEntity and ExpenseEntity (A category can have many expenses).
I’m fetching the categories and showing them in a list together with the sum of the expenses for each category as follows: Link to app screenshot
I would like to add a sort to the fetch request so the categories appear in order depending on the total amount of their expenses. In the example of the previous picture, the order of appearance that I would like to get would be: Tech, Clothes, Food and Transport. I don’t know how to approach this problem. Any suggestions?
In my current implementation of the request, the sorted is done alphabetically:
// My current implementation for fetching the categories
func fetchCategories() {
let request = NSFetchRequest<CategoryEntity>(entityName: "CategoryEntity")
let sort = NSSortDescriptor(keyPath: \CategoryEntity.name, ascending: true)
request.sortDescriptors = [sort]
do {
fetchedCategories = try manager.context.fetch(request)
} catch let error {
print("Error fetching. \(error.localizedDescription)")
}
}
You don't have to make another FetchRequest, you can just sort in a computed property like this:
(I assume your fetched results come into a var called fetchedCategories.)
var sortedCategories: [CategoryEntity] {
return fetchedCategories.sorted(by: { cat1, cat2 in
cat1.expensesArray.reduce(0, { $0 + $1.amount }) >
cat2.expensesArray.reduce(0, { $0 + $1.amount })
})
}
So this sorts the fetchedCategories array by a comparing rule, that looks at the sum of all cat1.expenses and compares it with the sum of cat2.expenses. The >says we want the large sums first.
You put the computed var directly in the View where you use it!
And where you used fetchedCategories before in your view (e.g. a ForEach), you now use sortedCategories.
This will update in the same way as the fetched results do.
One approach would be to include a derived attribute in your CategoryEntity model description which keeps the totals for you. For example, to sum the relevant values from the amount column within an expenses relation:
That attribute should be updated whenever you save your managed object context. You'll then be able to sort it just as you would any other attribute, without the performance cost of calculating the expense sum for each category whenever you sort.
Note that this option only really works if you don't have to do any filtering on expenses; for example, if you're looking at sorting based on expenses just in 2022, but your core data store also has seconds in 2021, the derived attribute might not give you the sort order you want.

Where is my error with my join in acumatica?

I want to get all the attributes from my "Actual Item Inventry" (From Stock Items Form) so i have:
PXResultset<CSAnswers> res = PXSelectJoin<CSAnswers,
InnerJoin<InventoryItem,
On<CSAnswers.refNoteID, Equal<Current<InventoryItem.noteID>>>
>
>.Select(new PXGraph());
But, this returns me 0 rows.
Where is my error?
UPDATED:
My loop is like this:
foreach (PXResult<CSAnswers> record in res)
{
CSAnswers answers = (CSAnswers)record;
string refnoteid = answers.RefNoteID.ToString();
string value = answers.Value;
}
... but i can not go inside foreach.
Sorry for the English.
You should use an initialized graph rather than just "new PXGraph()" for the select. This can be as simple as "this" or "Base" depending on where this code is located. There are times that it is ok to initialize a new graph instance, but also times that it is not ok. Not knowing the context of your code sample, let's assume that "this" and "Base" were insufficient, and you need to initialize a new graph. If you need to work within another graph instance, this is how your code would look.
InventoryItemMaint graph = PXGraph<InventoryItemMaint>.CreateInstance<InventoryItemMaint>();
PXResultset<CSAnswers> res = PXSelectJoin<CSAnswers,
InnerJoin<InventoryItem, On<CSAnswers.refNoteID, Equal<Current<InventoryItem.noteID>>>>>
.Select(graph);
foreach (PXResult<CSAnswers> record in res)
{
CSAnswers answers = (CSAnswers)record;
string refnoteid = answers.RefNoteID.ToString();
string value = answers.Value;
}
However, since you should be initializing graph within a graph or graph extension, you should be able to use:
.Select(this) // To use the current graph containing this logic
or
.Select(Base) // To use the base graph that is being extended if in a graph extension
Since you are referring to:
Current<InventoryItem.noteID>
...but are using "new PXGraph()" then there is no "InventoryItem" to be in the current data cache of the generic base object PXGraph. Hence the need to reference a fully defined graph.
Another syntax for specifying exactly what value you want to pass in is to use a parameter like this:
var myNoteIdVariable = ...
InventoryItemMaint graph = PXGraph<InventoryItemMaint>.CreateInstance<InventoryItemMaint>();
PXResultset<CSAnswers> res = PXSelectJoin<CSAnswers,
InnerJoin<InventoryItem, On<CSAnswers.refNoteID, Equal<Required<InventoryItem.noteID>>>>>
.Select(graph, myNoteIdVariable);
foreach (PXResult<CSAnswers> record in res)
{
CSAnswers answers = (CSAnswers)record;
string refnoteid = answers.RefNoteID.ToString();
string value = answers.Value;
}
Notice the "Required" and the extra value in the Select() section. A quick and easy way to check if you have a value for your parameter is to use PXTrace to write to the Trace that you can check after refreshing the screen and performing whatever action would execute your code:
PXTrace.WriteInformation(myNoteIdVariable.ToString());
...to see if there is a value in myNoteIdVariable to retrieve a result set. Place that outside of the foreach block or you will only get a value in the trace when you actually get records... which is not happening in your case.
If you want to get deep into what SQL statements are being generated and executed, look for Request Profiler in the menus and enable SQL logging while you run a test. Then come back to check the results. (Remember to disable the SQL logging when done or you can generate a lot of unnecessary data.)

How to get Attributes of a associated child product(saleable) in magento2

As shown in below image a configurable product has only one associated product that is in stock. I want to get only those attributes of this simple product from which its associated with parent product.
I am not sure it is correct whatever I tried is below-
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$product = $objectManager->create('Magento\Catalog\Model\Product')->load(6); // 6 is product id of configurable product.
$_children = $product->getTypeInstance()->getUsedProducts($product);
foreach ($_children as $child){
foreach ($child->getAttributes() as $attribute) {
echo $attrCode = $attribute->getAttributeCode();
}
}
this $attrcode prints all attributes whether want to get only super attributes as shown in above image.

Creating a group of test objects with AutoMapper

I'm trying to create a repository of data that I can use for testing purposes for an emerging car production and design company.
Beginning Automapper Question:
In this project, I have 2 classes that share the same properties for the most part. I don't need the Id, so I am ignoring that.
My existing code looks like this:
Mapper.CreateMap<RaceCar, ProductionCar>()
.Ignore(d => d.fId) //ignore the ID
.ForMember(d=> d.ShowRoomName,
o=> o.MapFrom(s => s.FactoryName) //different property names but same thing really
//combine into my new test car
var testCarObject = Mapper.Map<RaceCar, ProductionCar>()
My main requirements are:
1) I need to create 100 of these test car objects
2) and that for every ProductionCar I use, it needs to have a corresponding RaceCar which are matched up by the name(ShowRoomName & FactoryName)
So is there a way of sticking this in some type of loop or array so that I can create the needed 100?
Also, is there a way to ensure that each new test car has the combined FactoryCar and RaceCar?
Thanks!
Use AutoMapper with AutoFixture:
var fixture = new Fixture();
var items = Enumerable.Range(1, 100)
.Select(i => fixture.Create<RaceCar>())
.Select(car => new { RaceCar = car, ProductionCar = Mapper.Map<RaceCar, ProductionCar>(car))
.ToList();
items.Profit()

Saving to a Entity that has Many To Many relationship

I have a set of tables (ConditionTemplate and KeyWord) that have a many to many relationship. In code I am trying to add a Keyword to a specific ConditionTemplate record. Unfortunately, when I think I'm adding a Keyword to a specific condition I'm getting an error as if it's adding a new Keyword without being associated to a condition.
An Image of my Model:
My Code:
Global Variables Creation:
EnterpriseEntities EE;
ConditionTemplate myConditionTemplate;
Load Global Variables:
EE = new EnterpriseEntities();
EE.Database.Connection.ConnectionString = Myapp.EnterpriseEntityConnectionString;
myConditionTemplate = EE.ConditionTemplates.Where(c => c.TemplateCode == "17D").FirstOrDefault();
The above code loads a single Condition with Many Keywords.
Available Keywords are in a listbox and the user pushed a button to select a keyword(s) to move to the condition. This is the code that handles that.
foreach (KeyWord SelectedKeyWord in ListBoxAvailableKeyWords.SelectedItems)
{
KeyWord NewKeyWord = new KeyWord
{
KeyWordID = SelectedKeyWord.KeyWordID,
ID = SelectedKeyWord.ID,
Word = SelectedKeyWord.Word
};
myConditionTemplate.KeyWords.Add(NewKeyWord);
}
Then the user pushes a button to save changes and I call
EE.SaveChanges
Then I get this error:
System.Data.UpdateException: An error occurred while updating the
entries. See the inner exception for details. --->
System.Data.SqlClient.SqlException: Violation of UNIQUE KEY constraint
'IX_KeyWord'. Cannot insert duplicate key in object 'dbo.KeyWord'. The
duplicate key value is (ADJUDICATION). The statement has been
terminated.
If I remove the code that sets the word property (Word = SelectedKeyWord.Word
) when I create the keyword object I get this error.
System.Data.Entity.Validation.DbEntityValidationException: Validation
failed for one or more entities. See 'EntityValidationErrors' property for more details.
Which tells me the word field is required.
In order to tell EF that the KeyWords you selected already exist in the database and to avoid the problem you must attach them to the context:
foreach (KeyWord SelectedKeyWord in ListBoxAvailableKeyWords.SelectedItems)
{
KeyWord NewKeyWord = new KeyWord
{
// You actually only need to set the primary key property here
ID = SelectedKeyWord.ID
};
EE.KeyWords.Attach(NewKeyWord);
myConditionTemplate.KeyWords.Add(NewKeyWord);
}
Edit
If the KeyWord entities are already attached to your context (because they have been loaded before with the same context for example) you can use instead:
foreach (KeyWord SelectedKeyWord in ListBoxAvailableKeyWords.SelectedItems)
{
KeyWord NewKeyWord = EE.KeyWords.Find(SelectedKeyWord.ID);
myConditionTemplate.KeyWords.Add(NewKeyWord);
}

Resources