How to write fields many2one in Odoo with XMLRPC - mapping

I want to use the xmlrpc for the model "product.category".
How to write the field "categ_id" with "read and search"in PYTHON ? I have always an error. I need to do that because i have to transfer this informations into another database Odoo.
import xmlrpc.client
import time
url_db1="http://localhost:8069"
db_1='DB_ONE'
username_db_1='username'
password_db_1='password'
common_1 = xmlrpc.client.ServerProxy('{}/xmlrpc/2/common'.format(url_db1))
models_1 = xmlrpc.client.ServerProxy('{}/xmlrpc/2/object'.format(url_db1))
version_db1 = common_1.version()
print("details..", version_db1)
url_db2="http://localhost:806972"
db_2='DB_2'
username_db_2='myemail'
password_db_2='mypassword'
common_2 = xmlrpc.client.ServerProxy('{}/xmlrpc/2/common'.format(url_db2))
models_2 = xmlrpc.client.ServerProxy('{}/xmlrpc/2/object'.format(url_db2))
version_db2 = common_2.version()
print("details..", version_db2)
uid_db1 = common_1.authenticate(db_1, username_db_1, password_db_1, {})
uid_db2 = common_2.authenticate(db_2, username_db_2, password_db_2, {})
db_1_categories = models_1.execute_kw(db_1, uid_db1, password_db_1, 'product.category', 'search_read', [[]], {'fields':['id','name', 'parent_id']})
total_count = 0
for category in db_1_categories:
print("category..", category)
total_count +=1
values = {}
values['id'] = category['id']
values['name'] = category['name']
if category['parent_id']:
values['parent_id'] = category['parent_id'][0]
new_lead = models_2.execute_kw(db_2, uid_db2, password_db_2, 'product.category', 'create', [values])
print("Total Created..", total_count)
This is the error :
ValidationError: ('The operation cannot be completed: another model requires the record being deleted. If possible, archive it instead.\n\nModel: Product Category (product.category), Constraint: product_category_parent_id_fkey', None)\n'>

The method name should be search_read
Example:
models.execute_kw(db, uid, password,
'product.template', 'search_read',
[],
{'fields': ['name', 'default_code', 'categ_id']})
It should return a list of dicts, the categ_id field value is a list of two values, the first is the id of the category and the second is the name of the category.
To write categ_id, you just need to provide the category ID to the write method.
Example:
product_template_data = [{'default_code': 'FURN_6666', 'categ_id': [8, 'All / Saleable / Office Furniture'], 'id': 23, 'name': 'Acoustic Bloc Screens'}, ...]
for ptd in product_template_data:
models.execute_kw(db, uid, password, 'product.template', 'write',
[[ptd['id']],
{
'categ_id': ptd['categ_id'][0],
...
}
])
You mentioned that you need to transfer data to another database, the product template is probably not present which means that you can't call the write method instead, you can call the create method.
Example:
id = models.execute_kw(db, uid, password, 'product.template', 'create', [{
'categ_id': ptd['categ_id'][0],
...
}])
Edit:
You will get an invalid syntax error using:
[product,'categ_id': product['categ_id'][0],]
To pass values to the create method, you need to pass args to the execute_kw method as a list then pass values as a dictionary inside that list.
Edit:
values = {}
values['name'] = product['name']
values['categ_id'] = product['categ_id'][0]
...
new_lead = models_2.execute_kw(db_2, uid_db2, password_db_2, 'product.template', 'create', [values])
Edit: Use the parent category id in the new database
When we call the create method it will create a new record and return its ID which is probably different the one passed through the values dictionary.
To avoid the ValidationError you can use a dictionary where the parent ID in the old database is the key and the new ID is the value then you have just to pass that value when creating a new category.
category_ids = {}
for category in db_1_categories:
print("category..", category)
total_count +=1
values = {}
# values['id'] = category['id']
values['name'] = category['name']
if category['parent_id']:
values['parent_id'] = category_ids[category['parent_id'][0]]
category_id = models_2.execute_kw(db_2, uid_db2, password_db_2, 'product.category', 'create', [values])
category_ids[category['id']] = category_id

first of all error in your code
This is your code
db_1_products = models_1.execute_kw(db_1, uid_db1, password_db_1, 'product.template', 'search_read', [[]], {'fields':['id','name', 'categ_id','type', 'default_code', 'list_price', 'website_url', 'inventory_availibility', 'website_description']})
total_count = 0
for product in db_1_products:
print("produt..", product)
total_count +=1
new_lead = models_2.execute_kw(db_2, uid_db2, password_db_2, 'product.template', 'create', [product,'categ_id': product['categ_id'][0],])
print("Total Created..", total_count)
This is updated code
db_1_products = models_1.execute_kw(db_1, uid_db1, password_db_1, 'product.template', 'search_read', [[]], {'fields':['id','name', 'categ_id','type', 'default_code', 'list_price', 'website_url', 'inventory_availibility', 'website_description']})
total_count = 0
for product in db_1_products:
print("produt..", product)
total_count +=1
new_lead = models_2.execute_kw(db_2, uid_db2, password_db_2, 'product.template', 'create', [{product,'categ_id': product['categ_id'][0]}])
print("Total Created..", total_count)
you need to pass dictionary when creating of any model in odoo.

Related

Adding values to a hash within/over multiple each loops

I have a concept called snapshot which basically stores a snapshot of how data looked at a certain period of time. What I'm building is a method that loops through the snapshots for each events, and builds a small hash outlining the ownership over time for a given shareholder.
def fetch_ownership_over_time(shareholder, captable)
#shareholder = Shareholder.find(shareholder.id)
#captable = Captable.find(captable.id)
#company = #captable.company.id
#ownership_over_time = []
#captable.events.collect(&:snapshot).each do |snapshot|
parsed_snapshot = JSON.parse(snapshot)
#ownership_over_time.push(parsed_snapshot["event"]["name"])
#ownership_over_time.push(parsed_snapshot["event"]["date"])
parsed_snapshot["shareholders"].each do |shareholder|
if shareholder["id"] == #shareholder.id
#ownership_over_time.push(shareholder["ownership_percentage"])
end
end
end
return #ownership_over_time
end
I then call this method in my view which successfully retrieves the correct values however they are not structured in any way:
["Event 1 ", "2018-11-19", "0.666666666666667", "Event 2 ", "2018-11-19", "0.333333333333333", "4th event ", "2018-11-19", "0.315789473684211"]
What I'd like to do now though is construct my hash so that each separate snapshot event contains a name, date and ownership_percentage.
Perhaps something like this:
ownership_over_time = [
{
event_name = "Event 1" #parsed_snapshot["event"]["name"]
event_date = "20180202" #parsed_snapshot["event"]["date"]
ownership_percentage = 0.37 #shareholder["ownership_percentage"]
},
{
event_name = "Event 2" #parsed_snapshot["event"]["name"]
event_date = "20180501" #parsed_snapshot["event"]["date"]
ownership_percentage = 0.60 #shareholder["ownership_percentage"]
}
]
My challenge though is that the ["event"]["name"] an ["event"]["date"] attributes I need to fetch when looping over my snapshots i.e. the first loop (.each do |snapshot|) whereas I get my ownership_percentage when looping over shareholders - the second loop (.each do |shareholder|).
So my question is - how can I build this hash in "two" places so I can return the hash with the 3 attributes?
Appreciative of guidance/help - thank you!
You have to create a new hash for the object and append that hash to the array of objects you are creating.
def fetch_ownership_over_time(shareholder, captable)
#shareholder = Shareholder.find(shareholder.id)
#captable = Captable.find(captable.id)
#company = #captable.company.id
#ownership_over_time = []
#captable.events.collect(&:snapshot).each do |snapshot|
parsed_snapshot = JSON.parse(snapshot)
shareholder = parsed_snapshot['shareholders'].select { |s| s['id'] == #shareholder.id }.first
local_snapshot = {
'event_name' => parsed_snapshot['event']['name'],
'event_date' => parsed_snapshot['event']['date'],
'ownership_percentage' => shareholder.try(:[], "ownership_percentage") || 0
}
#ownership_over_time.push local_snapshot
end
return #ownership_over_time
end
Notice that I changed your second loop to a select. As you currently have it, you risk on pushing two percentages if the id is found twice.
EDIT:
Added functionality to use a default value if no shareholder is found.

Generate a Key, Value JSON object from a Ruby Object Array

I have a Ruby array of students. Student class has attributes id, name and age.
students = [
{id:"id1",name:"name1",age:"age1"},
{id:"id2",name:"name2",age:"age2"},
{id:"id3",name:"name3",age:"age3"}
]
I want to create a JSON key value object from this array as follows.
json_object = {id1:name1, id2:name2, id3:name3}
input = [ {id:"id1",name:"name1",age:"age1"},
{id:"id2",name:"name2",age:"age2"},
{id:"id3",name:"name3",age:"age3"}]
require 'json'
JSON.dump(input.map { |hash| [hash[:id], hash[:name]] }.to_h)
#⇒ '{"id1":"name1","id2":"name2","id3":"name3"}'
Give this a go:
students = [
{id:"id1",name:"name1",age:"age1"},
{id:"id2",name:"name2",age:"age2"},
{id:"id3",name:"name3",age:"age3"}
]
json_object = students.each_with_object({}) do |hsh, returning|
returning[hsh[:id]] = hsh[:name]
end.to_json
In console:
puts json_object
=> {"id1":"name1","id2":"name2","id3":"name3"}
Your data is all identical, but if you wanted to generate a hash that took the value of students[n][:id] as keys and students[n][:name] as values you could do this:
student_ids_to_names = students.each_with_object({}) do |student, memo|
memo[student[:id]] = student[:name]
end
For your data, you'd end up with only one entry as the students are identical: { "id1" => "name1" }. If the data were different each key would be unique on :id.
Once you have a hash, you can call json_object = students_ids_to_names.to_json to get a JSON string.

Remove element from an array of objects and create array of hash

Rewriting the question with detailed example:
I have array of objects with three property, I would like to build array of harsh and have the resulting hash not have #name instance property. I have simulated the problem with an example.
example creation
class Employee
attr_accessor :name, :company, :duration
def initialize(name, company, duration)
#name = name
#company = company
#duration = duration
end
end
aSong1 = Employee.new("Fleck", "AMZ", 260)
aSong2 = Employee.new("Taylor", "EMC", 120)
aSong3 = Employee.new("Bob", "Adobe", 260)
aSong4 = Employee.new("Jack", "Google", 360)
final_array = [ ]
final_array.push(aSong1)
final_array.push(aSong2)
final_array.push(aSong3)
final_array.push(aSong4)
controller
puts final_array.length #4
final_array.each do | element |
puts element.is_a?(Object) #true
puts element.name #prints name
end
resulting array ( expected )
result = [{company: 'AMZ',duration: 260}, {company: 'EMC',duration: 120},{company: 'Adobe',duration: 260}, {company: 'Google',duration: 360} ]
Example: repl
You can delete the id keys using
a = [{"id":"21","company":"AMC","name":"Matt"},{"id":"22","company":"AMC","name":"Jon"},
{"id":"12","company":"XYZ","name":"Bob"}].each{|o| o.delete :id}
If you want to compare it with other hashes you can use the merge method assuming a2 is the second Hash
a.merge(a2)
This will return a new hash with any matching keys updated with new corresponding values from the second hash
final_array.collect {|x| { company: x.company, duration: x.duration }}
result:
[{:company=>"AMZ", :duration=>260}, {:company=>"EMC", :duration=>120}, {:company=>"Adobe", :duration=>260}, {:company=>"Google", :duration=>360}]
How about the following?
class Employee
def to_h
instance_variables.each_with_object({}) do |var, h|
k = var.to_s.tr('#','').to_sym
v = instance_variable_get(var)
h[k] = v
end
end
end
result = final_array.map{|e| e.to_h.delete_if{|k,v| k == :name}}

ActiveRecord query from an array of hash

I have an array of hash.
eg) array_of_hash = [ { id: 20, name: 'John' }, { id: 30, name: 'Doe'} ]
I would like to get records which match all the criteria in a particular hash.
So the query I want to get executed is
SELECT persons.* FROM persons WHERE persons.id = 20 AND persons.name = 'John' OR persons.id = 30 AND persons.name = 'Doe'
What is the best way to construct this query from an array of hash?
I think this is okay:
ids = array_of_hash.map { |h| h[:id] }
names = array_of_hash.map { |h| h[:name] }
Person.where(id: ids, name: names)
(although it's not super generic)
other attempt:
people = Person.all
array_of_hash.each do |h|
people = people.where(h)
end
people # => will generate a long long query.
Try to map all conditions to your ActiveRecord model independently and flatten the result array afterwards:
array_of_hash.map{ |where_clause| Person.where(where_clause) }.flatten

How to save multiple rows of data by looping through an array or a map?

Class Buyer {
String name
static constraints = {
}
}
Class Order {
String ref
static belongsTo = [buyer:Buyer]
static constraints = {
buyer(nullable:false)
}
}
In OrderController.groovy
...
def someAction = {
//working
def data1 = ["buyer.id": 2, "ref": "xyz"]
def ord = new Order(data1);
ord.save();
def data2 = ["buyer.id": 2, "ref": "234xyz"]
def ord2 = new Order(data2);
ord2.save();
//But in a loop - its not working
def items = ['abc', 'def', 'ghi', 'jkl']
def data2 = [:]
for(e in items) {
data2 = ["buyer.id": 2, "ref" : e.value] //keeping buyer id same
def ord = new Order(data2);
ord.save();
data2 = [:] //just emptying it?
}
}
As you would notice in "working" above, if I am able to save multiple rows by copy pasting and definging new maps but If I try to loop through an array, it doesnt work. Any ideas how do I save data by looping through an array or a map?
Any questions, please let know
Thanks
First, I'm not sure about ["buyer.id": 2, "ref" : e.value], I think it should be [buyer: Buyer.get(2), ref : e.value].
Second, I would recommend using cascading save to do the task. You can try something like this(You need to define a static hasMany = [orders: Order] relation in Buyer for Buyer-Order relationship.
Buyer b = new Buyer(name: "foo")
for(e in items) {
def ord = new Order(ref: e.value);
b.addToOrders(ord)
}
b.save(flush:true)
You should just be able to do:
def someAction = {
def items = ['abc', 'def', 'ghi', 'jkl']
items.each { item ->
def ord = new Order( [ 'buyer.id':2, ref:item ] )
ord.save()
}
}
What errors (if any) are you getting?
Also, why are you doing e.value? This will get you an array of Character rather than a String (which is what your first working example is using)

Resources