How to make component item change owner automatically with its parent? - api

I'm learning hyperledger composer and I don't know how to modify chaincode to make following model work:
This is my cto:
asset Item identified by itemId{
o String itemId
o String name
o String idgId
o String serialNumber
o String comment
--> BU owner
--> Item [] items optional
}
abstract participant BU identified by buId{
o String buId
o String name
o String country
o String city
}
participant Manufacturer extends BU{
}
participant Assembler extends BU{
}
And chaincode:
function tradeCommodity(trade) {
trade.item.owner = trade.newOwner;
return getAssetRegistry('org.dps.track.Item')
.then(function (assetRegistry) {
var tradeNotification = getFactory().newEvent('org.dps.track', 'TradeNotification');
tradeNotification.item = trade.item;
emit(tradeNotification);
// persist the state of the commodity
return assetRegistry.update(trade.item);
});
}
then i make two items: I1 and I2 - which will be components of third item I3
like this:
{
"$class": "org.dps.track.Item",
"itemId": "I1",
"name": "c1",
"idgId": "123",
"serialNumber": "123",
"comment": "component1",
"owner": "resource:org.dps.track.Assembler#BU2"
},
{
"$class": "org.dps.track.Item",
"itemId": "I2",
"name": "c2",
"idgId": "456",
"serialNumber": "456",
"comment": "component2",
"owner": "resource:org.dps.track.Assembler#BU2"
},
{
"$class": "org.dps.track.Item",
"itemId": "I3",
"name": "complex",
"idgId": "789",
"serialNumber": "789",
"comment": "item consists of items",
"owner": "resource:org.dps.track.Assembler#BU2",
"items": [
"resource:org.dps.track.Item#I1",
"resource:org.dps.track.Item#I2"
]
}
Then if i make a transaction I3 changes owner but its components have still previous owner. How can i make I1 and I2 change owner automatically when I3 changes the owner. Is it possible to achieve this? I will be thankful for any help or guidance.

something like this (note that this is sample code, there may be more efficient ways to do it eg. aggregate all the 'related Items' that are affected by change of ownership, and then do one single updateAll() call to update the Item registry - example here) - also note I used async / await (available in Node 8 as its far more readable).
note my transaction model is:
transaction Trade {
--> Item item
--> Assembler newOwner
}
UPDATED: you ALSO need your asset Item to have --> Assembler owner as the resource class (to build a relationship instance with) and not -->BU owner` which is an abstract class.
in the model file (ie Assembler is a participant resource)
/**
* Sample transaction processor function.
* #param {org.dps.track.Trade } trade The sample transaction instance.
* #transaction
*/
async function tradeCommodity(trade) {
const factory = getFactory();
trade.item.owner = trade.newOwner;
var list = [];
if (trade.item.items && trade.item.items.length > 0) {
trade.item.items.forEach((asset) => {
list.push(asset);
});
}
const assetRegistry = await getAssetRegistry('org.dps.track.Item');
// persist the state of the current ITEM
await assetRegistry.update(trade.item);
for (var i = 0; i < list.length; ++i) {
let res = await assetRegistry.get(list[i].getIdentifier());
res.owner = factory.newRelationship('org.dps.track', 'Assembler', trade.newOwner.getIdentifier());
// persist the state of the ITEM with new owner as a relationship
await assetRegistry.update(res);
}
}

Related

Filter products by category in a Shopware 6 Import/Export Profile

I created a Profile in shopware 6 with the api.
The body of the request is:
{
"id": "my99id2cb2784069938089a8a77ctest",
"label": "Label test",
"name": "name test",
"sourceEntity": "product",
"fileType": "text/csv",
"delimiter": ";",
"enclosure": "\"",
"createdAt": "2022-11-10T10:15:22Z",
"mapping": [{"key":"id","mappedKey":"id","position":0,"id":"someid4f7c6c478b8041cfd5fe0ae0c5"},{"key":"cover.media.url","mappedKey":"cover","position":1,"id":"someid0ffa7f48e6b135f6e754d6b93c"}],
"config": {"createEntities": false, "updateEntities": true}
}
Is there a way to specify that i want only product from certain categories? Not all of them
No, this currently isn't possible with just the profile and existing endpoints. You'll have to implement a custom api endpoint.
If you are able to do that follow the steps from this answer until you first retrieve a logId. Then instead of starting the process using the existing endpoint, request your new custom endpoint. The implementation could look similar to this:
/**
* #Route(defaults={"_routeScope"={"api"}})
*/
class CustomExportApiController extends AbstractController
{
private ImportExportFactory $importExportFactory;
public function __construct(ImportExportFactory $importExportFactory)
{
$this->importExportFactory = $importExportFactory;
}
/**
* #Route("/api/_action/custom/export/{logId}/{categoryId}", name="api.action.custom.export", methods={"POST"})
*/
public function customProductExport(string $logId, string $categoryId, Context $context): JsonResponse
{
$importExport = $this->importExportFactory->create($logId, 50, 50);
$logEntity = $importExport->getLogEntity();
if ($logEntity->getState() === Progress::STATE_ABORTED) {
return new JsonResponse(['success' => false]);
}
$criteria = new Criteria();
$criteria->addFilter(new EqualsAnyFilter('categoryTree', [$categoryId]));
$offset = 0;
do {
$progress = $importExport->export($context, $criteria, $offset);
$offset = $progress->getOffset();
} while (!$progress->isFinished());
return new JsonResponse(['success' => true]);
}
}
After calling your custom endpoint proceed with the steps as described in the answer linked above.

How to Merge two array of object with the same keys that have the same values and the different value will be stored in an array?

I got data from database like this:
[
{
id_user:1,
expense:3000
},
{
id_user:1,
expense:5000
},
{
id_user:2,
expense:35000
},
{
id_user:3,
expense:50100
}
]
How can i convert to json like this ?
[
{
id_user:1,
expense:[3000,5000]
},
{
id_user:2,
expense:35000
},
{
id_user:3,
expense:50100
}
]
Reduce & Map is way to go. And it's way better to have all expense fields to be an array, so you don't have to check weather it's an array or not
const sample = [{
id_user: 1, expense: 3000
}, {
id_user: 1, expense: 5000
}, {
id_user: 2, expense: 35000
}, {
id_user: 3, expense: 50100
}]
const parseData = data => {
return [...data.reduce((acc, { id_user, expense }) => {
const currentUser = acc.get(id_user)
const newExpense = currentUser ? currentUser.expense : []
newExpense.push(expense)
acc.set(id_user, { id_user, expense: newExpense })
return acc
}, new Map()).values()]
}
console.log(parseData(sample))
You would just need to write a program to do it. This is how I'd approach it:
// phase 1
const combined = {};
for (const item of data) {
if (!combined[item.id_user]) {
combined[item.id_user] = [];
}
combined[item.id_user].push(item.expense);
}
// phase 2
const newData = [];
for (const id of Object.keys(combined)) {
newData.push({id_user: id, expense: combined[id]});
}
The approach is 2 phases. The first phase goes through the data and combines expense values for the same user into an array. I build it into an Object since it's efficient to look up entries by their key this way. If there are a lot of records to process this will be faster than pushing into an Array and using .includes to find entries that already exist.
The 2nd phase is building the new array with the combined expense arrays. This is done by pushing each new object into the output Array.
Note that I chose to make the new expense always be an array even if there's only one expense. It's cleaner to have an attribute have a consistent type rather than having to check if it's an Array when accessing the data.

How can I use Ember Data's FixtureAdapter with real API content

I want to populate my Ember Data fixtures with real content taken from an API data dump instead of the standard fixtures. I don't want to use the API directly.
I want to do this so I can imitate a local instance of the API data.
How can I streamline this and also how might I configure the adapter to allow this?
Consider this default FIXTURE:
App.Comment = DS.Model.extend({
article: DS.belongsTo('article'),
author: DS.belongsTo('user'),
dateCreated: DS.attr('date', {readOnly: true}),
dateModified: DS.attr('date', {readOnly: true}),
description: DS.attr('string')
});
App.Comment.FIXTURES = [{
id: 1,
temp: 1,
author: 1,
dateCreated: 'Mon Jul 28 2014 12:00:00 GMT+1000 (EST)',
dateModified: null,
description: 'lorem ipsum'
}];
Consider this API response:
{"comments": [
{
"articleID": 1,
"description": "I am a comment",
"authorID": 1,
"dateCreated": "2014-09-04T02:39:00",
"createdBy": "Elise Chant",
"dateModified": "2014-09-04T02:39:00",
"id": 1
},
{
"articleID": 1,
"description": "I am another comment",
"authorID": 1,
"dateCreated": "2014-09-04T02:48:00",
"createdBy": "Elise Chant",
"dateModified": "2014-09-04T02:48:00",
"id": 2
}
]}
Get your backend to provide a data dump method from the API with the resources, such as:
http://myapi/dump
The method shall return a zipped folder of files representing the site's resources, such as Comment.js, User.js, Article.js.
Each of these files should return the JSON resource wrapped by DS.Model.FIXTURES array, such as
App.Comment.FIXTURES =
[
{
"articleID": 1,
"userID": 1,
"description": "I am a comment.",
"category": 0,
"authorID": 1,
"dateCreated": "2014-09-04T02:39:00",
"id": 1
},
// ...
];
Remember to make sure that each .js file is an included script tag in html and appears after its corresponding model. Check this is available in the Browser's Developer Console.
Finally, In order to correctly connect asynchronous relationships such as articleID and authorID, property names need to be normalised like that would be if you were using normal fixture data. So configure the adapter to strip 'ID' from the end of any belongsTo relationships by parsing the FIXTURES payload:
if (App.ENV === 'DEV') {
App.ApplicationSerializer = DS.JSONSerializer.extend({
// access to the payload
extractArray: function(store, type, payload) {
var self = this;
return payload.map(function(item) {
// normalise incoming data
return self.normalize(type, item);
});
},
normalize: function(type, hash) {
if (!hash) { return hash; }
var normalizedHash = {},
normalizedProp;
for (var prop in hash) {
if (!!prop.match(/ID$/)) {
// belongs to / has Many attribute
// remove 'ID' from the end of the property name
normalizedProp = prop.substr(0, prop.length-2);
} else { // regular attribute
normalizedProp = prop;
}
normalizedHash[normalizedProp] = hash[prop];
}
this.normalizeId(normalizedHash);
this.normalizeUsingDeclaredMapping(type, normalizedHash);
this.applyTransforms(type, normalizedHash);
return normalizedHash;
}
});
App.ApplicationAdapter = DS.FixtureAdapter.extend({
simulateRemoteResponse: true,
latency: 1000,
// This "unsets" serializer so that the store will lookup the proper serializer
// #see https://github.com/emberjs/data/issues/1333
serializer: function() {
return;
}.property()
});
} else {
DS.CustomRESTSerializer = DS.RESTSerializer.extend({
keyForRelationship: function(key, kind) {
if (kind === 'belongsTo') {
return key + 'ID';
} else {
return key;
}
}
});
App.ApplicationAdapter = DS.RESTAdapter.extend({
host: App.HOST,
namespace: App.NAMESPACE,
ajax: function(url, method, hash) {
hash = hash || {}; // hash may be undefined
hash.crossDomain = true;
hash.xhrFields = {withCredentials: true};
return this._super(url, method, hash);
},
defaultSerializer: 'DS/customREST'
});
}

NetSuite: obtaining invoice balance

I search hi and lo but could not find any decent reference how to get paid and outstanding amounts for the invoice in NetSuite using SuiteTalk API.
Documentation is non-existent and so are the samples so I'm poking in the dark here.
When invoice is retrieved using get method, I kind of expected these to be specified (fields amountPaid and amountRemaining) but they are not.
The next port of call, unfortunately, is to search for customer payments that have been applied to the target invoice. Search works but, to make the matter worse, each payment is returned without any apply details so extra call is required to get all apply details and figure out the total payment amount applied to the invoice.
Is that REALLY the only way to do it?!
(And I probably would have to consider deposits and deposit applications as they are separate record types. Sigh)
Any help is appreciated
George
UPDATE: Working code sample
long internalInvoiceId = 42;
TransactionSearchAdvanced tsa = new TransactionSearchAdvanced()
{
columns = new TransactionSearchRow()
{
basic = new TransactionSearchRowBasic()
{
total = new SearchColumnDoubleField[] { new SearchColumnDoubleField() },
amount = new SearchColumnDoubleField[] { new SearchColumnDoubleField() },
amountPaid = new SearchColumnDoubleField[] { new SearchColumnDoubleField() },
amountRemaining = new SearchColumnDoubleField[] { new SearchColumnDoubleField() }
}
},
criteria = new TransactionSearch()
{
basic = new TransactionSearchBasic()
{
mainLine = new SearchBooleanField()
{
searchValue = true,
searchValueSpecified = true
},
type = new SearchEnumMultiSelectField()
{
#operator = SearchEnumMultiSelectFieldOperator.anyOf,
operatorSpecified = true,
searchValue = new string[] { "_invoice" }
},
internalIdNumber = new SearchLongField()
{
#operator = SearchLongFieldOperator.equalTo,
operatorSpecified = true,
searchValue = internalInvoiceId,
searchValueSpecified = true
}
}
}
};
SearchResult sr = nss.search(tsa);
Do a transaction search.
Criteria:
Main Line: Yes
Type: Invoice
(Optional) Internal ID: "The internal id of your Invoice"
Result/Columns:
Amount Paid
Amount Remaining
Translate the above to SuiteTalk API (TransactionSearchAdvanced) and you should be able to get what you want.
Here's a function using the NetSuite ruby bindings that retrieves the amount remaining (due) on an invoice:
def amount_due_for_invoice(ns_invoice)
search = NetSuite::Records::Invoice.search(
criteria: {
basic: [
{
field: 'type',
operator: 'anyOf',
value: %w(_invoice)
},
{
field: 'mainLine',
value: true
},
{
field: 'internalIdNumber',
operator: 'equalTo',
value: ns_invoice.internal_id
}
]
},
columns: {
'tranSales:basic' => {
'platformCommon:internalId/' => {},
'platformCommon:amountRemaining' => {}
}
}
)
if search.results.size > 1
fail "invoice search on internalId should never return more than a single result"
end
search.results.first.attributes[:amount_remaining].to_f
end
An alternative solution here is to create a custom transaction body formula field which pulls the value of the invoice's amountremainingtotalbox field.

RavenDB Patch API: updating a nested collection

I am trying to update a nested collection using the Patch API. More specifically, consider the following example - a Posts collection:
{
"Title": "Hello RavenDB",
"Category": "RavenDB",
"Content": "This is a blog about RavenDB",
"Comments": [
{
"Title": "Unrealistic",
"Content": "This example is unrealistic"
},
{
"Title": "Nice",
"Content": "This example is nice"
}
]
}
I used the Patch API and Set-based operation docs at http://ravendb.net/docs/client-api/partial-document-updates and http://ravendb.net/docs/client-api/set-based-operations as well as several stackoverflow questions as resources to do a bulk update using set operations and a static index. A requirement is to update the "Title" of a comment only when the previous value was "Nice" and if so, update it to "Bad".
The static index "NicePosts" is defined as:
Map = posts => from post in posts
where post.Comments.Any(comment => comment.Title == "Nice")
select new {post.Title, post.Category}
The bulk patch update command is:
documentStore.DatabaseCommands.UpdateByIndex("NicePosts",
new IndexQuery(),
new[] { new PatchRequest
{ Type = PatchCommandType.Modify,
Name = "Comments",
PrevVal = RavenJObject.Parse(#"{ ""Title"": ""Nice""}"),
Nested = new[]
{
new PatchRequest {Type = PatchCommandType.Set, Name = "Title", Value = new RavenJValue("Bad") },
} }, allowStale: true);
I have some questions regarding this:
1) Is my structure/syntax for the update command correct?
2) I would like the update to be performed on all the records in the collection. Hence I haven't defined the query filter in the IndexQuery Query because the "NicePosts" index already returns the appropriate set. However running this command doesn't update the collection.
3) If I set "allowStale:false" I get a "stale index" error. Before opening my document store session I instantiate the index class and Execute it to persist it to the ravenDB instance. Any ideas whats going wrong here?
Thanks,
EDIT:
Based on ayende's recommendation changed Patch command to:
documentStore.DatabaseCommands.UpdateByIndex("NicePosts",
new IndexQuery(),
new[] {
new PatchRequest {
Type = PatchCommandType.Modify,
Name = "Comments",
Position = 0,
Nested = new[] {
new PatchRequest {Type = PatchCommandType.Set, Name = "Title", Value = new RavenJValue("Bad")},
}
}
}, allowStale: false);
This can now be done using the scripted patch request:
string oldTitle = "Nice";
string newTitle = "Bad";
documentStore.DatabaseCommands.UpdateByIndex("NicePosts",
new IndexQuery(),
new ScriptedPatchRequest
{
Script = #"for (var i = 0; i < this.Comments.length; i++)
if (this.Comments[i].Title == oldTitle)
this.Comments[i].Title = newTitle;",
Values =
{
{ "oldTitle", oldTitle },
{ "newTitle", newTitle },
},
}
);
You can't use the patch command to update values based on the existing value in the array.
You need to specify the actual position.