How to get Child elements in Karate framework - karate

I need to extract child elements from response of one api and then pass it dynamically to next api request.
Suppose i have the below xml:
* def foo =
"""
<records>
<record index="1">a</record>
<record index="2">b</record>
<record index="3" foo="bar">c</record>
</records>
"""
I want to extract only this:
<record index="1">a</record>
<record index="2">b</record>
<record index="3" foo="bar">c</record>
I tried below options, but none of then worked:
* def chk = foo//records/*
* def chk = foo//records/child::*
* def chk = foo//records/descendant::*
* print chk
After printing, I get the below, please suggest if i'm missing anything or any other way to do it.Thank you!
13:51:07.046 INFO - [print] {
"records": {
"record": [
{
"_": "a",
"#": {
"index": "1"
}
},
{
"_": "b",
"#": {
"index": "2"
}
},
{
"_": "c",
"#": {
"foo": "bar",
"index": "3"
}
}
]
}
}

EDIT: looks like a simple string replace would do the trick if all you need to do is take a set of elements and stuff them into another XML template:
(I'm leaving the original longer answer at the end of this answer for reference)
* def foo =
"""
<records>
<record index="1">a</record>
<record index="2">b</record>
<record index="3" foo="bar">c</record>
</records>
"""
* replace foo.<records> = ''
* replace foo.</records> = ''
# you can do def bar = read('template.txt') in your case
* text bar =
"""
<after new="namespace">
##foo##
</after>
"""
* replace bar.##foo## = foo
* xml bar = bar
* match bar ==
"""
<after new="namespace">
<record index="1">a</record>
<record index="2">b</record>
<record index="3" foo="bar">c</record>
</after>
"""
Yes, Karate does not support an XML node-list natively, typically you don't need it. And XML has this peculiarity where you always need a "parent" or "wrapping" element.
As you see above, you do have all the information needed. I'm still not clear what kind of XML you need to pass to the next API - but whatever the case, there is a way in Karate. Here are 2 options. First, iterating over the data and manually emitting XML:
* def foo =
"""
<records>
<record index="1">a</record>
<record index="2">b</record>
<record index="3" foo="bar">c</record>
</records>
"""
* json records = foo.records.record
* def temp = <bar></bar>
* def fun =
"""
function(r, i){
karate.set('temp', '/bar/record[' + (i+1) + ']', r._);
karate.set('temp', '/bar/record[' + (i+1) + ']/#index', r['#'].index);
}
"""
* eval karate.forEach(records, fun)
* match temp ==
"""
<bar>
<record index="1">a</record>
<record index="2">b</record>
<record index="3">c</record>
</bar>
"""
Note that karate.forEach is available only in the upcoming 0.8.0 but you can try 0.8.0.RC3
Here's another interesting option, if all you need to do is change the XML parent, a string replace will do the trick:
* replace foo.<records> = '<bar>'
* replace foo.</records> = '</bar>'
* xml bar = foo
* match bar ==
"""
<bar>
<record index="1">a</record>
<record index="2">b</record>
<record index="3" foo="bar">c</record>
</bar>
"""

Related

ir sequence increment issue in odoo

Hi guys Help me with this.
the check field doesnt increment.it stays blank
from odoo import api, fields, models, _
class check_print(models.Model):
_inherit = 'account.payment'
check = fields.Char("Check Number",readonly=True)
#api.model
def create(self,vals):
if vals.get('payment_method_code') == ('check_printing'):
vals['check'] = self.env['ir.sequence'].next_by_code('seq.ch.code')
res = super(check_print, self).create(vals)
return res
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<record id="seq_check_id" model="ir.sequence">
<field name="name">check</field>
<field name="code">seq.ch.code</field>
<field name="prefix">CH</field>
<field name="padding">10</field>
<field name="company_id" eval="False"/>
</record>
</data>
</odoo>
the check field is empty everytime i save the record
Seems that this line is not being evaluated to True:
if vals.get('payment_method_code') == ('check_printing'):
Also check your indentation of this lines that need to be outside the if block
res = super(check_print, self).create(vals)
return res

addition and subtraction sum attribute

I need to do an addition if the type_In == "In" and a subtraction if the type_In == "Out"
the sum attribute does just the addition
<field name="type_In"/>
<field name="Amount_In" sum="Amount total"/>
I don't think there is a better why only if you are good
in javascipt.
try to add a compute field.
Amount_value = fields....(compute="_get_amout_value") # same field type as Amount_In
#api.depends('Amount_In')
def get_amout_value(self):
for rec in self:
if rec.type_In == "In":
rec.Amount_value = rec.Amount_In
else :
rec.Amount_value = rec.Amount_In * -1
and if the tree:
<field name="type_In"/>
<field name="Amount_value" sum="Amount total"/>

Display multiple one2many field with different domain dynamically?

I have a model(modelA) with one2many field related to another model(modelB) and one of the fields in modelB is a category field, which is a many2one field. The requirement is to have a one2many field displayed for each category. So if there are 2 categories named 'category1' and 'category2', the form view of modelA should have 2 one2many fields, one which displays records of having category1 and another for category2(which could possibly done using domain).
For eg modelA and modelB has the following structure.
class classA(models.Model):
_name = 'modelA'
modelA_one2manyfield = fields.One2many('modelB', 'modelB_many2onefield')
class classB(models.Model):
_name = 'modelB'
name = fields.Char()
category = fields.Many2one('modelC')
modelB_many2onefield = fields.Many2one('modelA')
How would i go about implementing a form view for modelA so that for each category(which can be added by the user, hence there can be of any number of categories) there is a seperate one2many field.
What you are asking take a lot of time to give a very good answer one of the way that i think you need to try is override the fields_view_get because this is the method that retreive the view and here you can change the arch field to add a costum field take a look at this tutorial :
Tutorial for dynamic view
but i think you will have a problem, because even when you put the domain on the one2many field in XML, odoo will not filter
the record when the loading happen on the view :
<!-- here all record are shown but the expected behavior is the one2many should be empty -->
<field name="one2many_field_name" readonly="1" nolabel="1" domain="[('id', '=', False)]">
but when i add this field to the python declaration
# here no record will be shown on the view and that's what was expected
one2many_field_name = fields.One2many(..., domain=[('id', '=', False)])
so the question adding one2many field to arch via fields_view_get is easy but the problem is filtring data !!
It's technically not possible. Because you can't have 2 times the same field in the same view.
But you can create a specific widget to showing what you want. How you can see in the timesheet view (My Current timesheet menu).
This is a little tutorial to created a widget.
https://www.odoo.com/documentation/10.0/howtos/web.html#widgets-basics
This not an answer but you can say a tutorial example of dynamic view :
modul structur:
->dynamic_view
--> __ini__.py
--> models.py
--> views.xml
--> __manifest__.py
__manifest__.py :
# -*- coding: utf-8 -*-
{
'name' : 'Dynamic view',
'version' : '1.0',
'summary': 'Tutorial for Dynamic view',
'sequence': 30,
'description': """
This Module is for showing that you can update the code of the view
when it's called and even create new field without having to use python
code at all
""",
'category': 'StackOverFlow',
'depends' : ['base_setup',],
'data': [
'views.xml'
],
'installable': True,
'application': True,
'auto_install': False,
}
__init__.py :
# -*- coding: utf-8 -*-
from . import models
models.py :
# -*- coding: utf-8 -*-
from odoo import models, fields, api
class Person(models.Model):
_name = "training.person"
name = fields.Char("Full name")
class Car(models.Model):
_name = "training.car"
name = fields.Char("Car name")
mark_id = fields.Many2one(comodel_name="training.mark", string="Mark")
owner_id = fields.Many2one(comodel_name="training.person", string="Owner")
person_view_id = "dynamic_view.dgapr_form_person"
# here default arch value body in the view contains only
# name field but as we create new mark we add others field
person_view_arch = """
<group>
<field name="name"/>
</group>
"""
class Mark(models.Model):
_name = "training.mark"
name = fields.Char("Mark")
#api.model
def create(self, values):
"""
when we create a category we add one2many field to person view
TODO: when we unlink a category we need to remove the one2many
name of field is : x_mark_{id of deleted record}
"""
rec_id = super(Mark, self).create(values)
o2m_field = {
# fields created using orm method must start with x_
"name": "x_mark_%s"% rec_id.id,
"field_description": "Mark %s" % rec_id.name,
"ttype": "one2many",
"relation": "training.car",
"relation_field": "owner_id",
"stored": True,
"domain": "[('mark_id','=', %s)]"%rec_id.id,
"model_id": self.env.ref("dynamic_view.model_training_person").id,
}
# add on2many field to ir.model.fields
self.env["ir.model.fields"].create(o2m_field)
self.update_arch()
return rec_id
def update_arch(self):
"""
when ever we create or delete a mark record
we need to update the the view to add new one2many field
if we want to hide the one2many field in view that don't have
any record we should create compute field to use attrs features
"""
view_id = self.env.ref(person_view_id)
o2m_fields_ids = self.env['ir.model.fields'].search(
[
('model_id', '=', self.env.ref("dynamic_view.model_training_person").id),
('ttype', 'like', 'one2many'),
('relation_field', 'like', 'owner_id')
])
o2many_arch = ""
for o2m_id in o2m_fields_ids:
o2many_arch = o2many_arch + """
<group col="1" string="%s">
<field name="%s" noloable="1" />
</group>
""" % (o2m_id.field_description, o2m_id.name,)
arch_begin = """
<form>
<sheet>
"""
arch_close = """
</sheet>
</form>
"""
arch_body = person_view_arch + o2many_arch
new_arch = arch_begin + arch_body + arch_close
# update the arch of the view in database
view_id.arch = new_arch
views.xml:
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<data>
<record id="dgapr_form_car" model="ir.ui.view">
<field name="name">car.form</field>
<field name="model">training.car</field>
<field name="arch" type="xml">
<form >
<sheet>
<group>
<field name="name"/>
<field name="mark_id"/>
<field name="owner_id"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="dgapr_action_car" model="ir.actions.act_window">
<field name="name">Cars</field>
<field name="res_model">training.car</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem id="menu_root_training" name="Training"/>
<menuitem id="menu_ch_car" name="Cars" parent="menu_root_training" action="dgapr_action_car"/>
<record id="dgapr_form_person" model="ir.ui.view">
<field name="name">dgapr.form.person</field>
<field name="model">training.person</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<field name="name"/>
</group>
</sheet>
</form>
</field>
</record>
</data>
</odoo>
i found out that you can create field using ORM method even compute field. i think creating a widget is better but good to know that wen can create costum fields .
Hope this helps you
Note i didn't create a menu for person record but you can see the view by clicking on the owner_id in the car form if the new one2many field not shown just refresh the page.

Computed field result in odoo 10

I am working on student registration module. I want to to auto generate registration code and save it into database where as computed method having some issue.
1.. can't save Computed result in database.
2.. when i use store attribute with field and depends api then there is not increment in variable.
here is my code below.
reg_code = fields.Char(compute='code', string='Code', readonly=True)
#api.multi
def get_code(self):
count = 0
reg = "Reg #"
for record in self:
count += 1
record.reg_code = reg + " " + str(count)
if record.reg_code:
count += 1
record.reg_code = reg+" "+str(count)
else:
count += 1
record.reg_code = reg+" "+str(count)
You can use sequence in Odoo.
find sequence in
Enable Active developer mode
Settings --> Sequence
Then add this code in your module:
change field as
reg_code = fields.Char(string='Code',required=True, copy=False, readonly=True, index=True, default= lambda self: self.env['ir.sequence'].next_by_code('student.registration'))
Create another xml file like if you want to create sequence by code.
shipping_sequence.xml
and add this code
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<!-- Sequences for student.registration -->
<record id="seq_student_registration" model="ir.sequence">
<field name="name">Student Registration</field>
<field name="code">student.registration</field>
<field name="prefix">Code-</field>
<field name="padding">1</field>
<field name="company_id" eval="False"/>
</record>
</data>
</odoo>

Odoo Wizard writes to model but getting values from models doesn`t doesnt populate the wizard fields

I have a wizard below that writes to the database but doesnt read the values written to the database and display in the view.
I using the get_opinion to read the opinions and the get_notes to read the notes , but when i click on the buttons they just disappear with no error , where am i going wrong
Q2 , in this code which is part of the code below why am i getting model_name none
if context is None: context = {}
model_name=context.get('active_model')
print model_name #gives NONE why ?
Q3 How can i list all fields in context?
The module code is as below
class opinion(models.TransientModel):
_name='opinion'
opinion_emission = fields.Text(string='OPINION EMISSION')
notes = fields.Text(string='Additional Notes')
defaults={
'opinion_emission': lambda self : self.get_opinion(self),
'notes': lambda self : self.get_notes(self),
}
def save_it(self, cr, uid,ids , context=None):
# context = dict(self._context or {} )
if context is None: context = {}
active_id = context.get('active_id',False)
print 'active_id' , active_id
if active_id :
# op = self.env['opinion'].browse(active_id)
info = self.browse(cr,uid,ids)
self.pool.get('opinion').write(cr,uid,context['active_id'],{'opinion_emission': info[0].opinion_emission,'notes': info[0].notes})
return {
'type': 'ir.actions.act_window_close',
}
#functions that get the info stored in db
#api.one
def get_opinion(self):
ids=self._ids
cr = self._cr
uid = self._uid
context = self._context
if context is None: context = {}
active_id = context.get('active_id',False)
print 'active_id' , active_id
if active_id :
return self.env['opinion'].browse(cr, uid, context['active_id'], context).opinion_emission
#api.one
def get_notes(self,records=None):
ids=self._ids
cr = self._cr
uid = self._uid
context = self._context
if context is None: context = {}
model_name=context.get('active_model')
print model_name #gives NONE why ?
active_id = context.get('active_id',False)
print 'active_id' , active_id
if active_id :
print 'output',self.env['opinion'].browse(cr, uid, context['active_id'], context)[0].notes
return self.env['opinion'].browse(cr, uid, context['active_id'], context)[0].notes
The xml view code is :
<openerp>
<data>
<record id="view_opinion_wizard" model="ir.ui.view">
<field name="name">opinion_wizard.form</field>
<field name="model">opinion</field>
<field name="type">form</field>
<field name="arch" type="xml">
<!-- create a normal form view, with the fields you've created on your python file -->
<form string="OPINION" version="8.0">
<button icon="gtk-ok" name="get_notes" string='SET VAL' type="object" />
<group >
<separator string="Please insert OPINION" colspan="2"/>
<field name="opinion_emission" string="OPINION EMISSION "/>
<field name="notes" string="NOTES"/>
<newline/>
</group>
<div style="text-align:right">
<button icon="gtk-cancel" special="cancel" string="Cancel"/>
<button icon="gtk-ok" name="save_it" string="SAVE INFO" type="object" />
<button icon="gtk-ok" name="get_notes" string="GET NOTES" type="object" />
<button icon="gtk-ok" name="get_opinion" string="GET OPINION" type="object" />
</div>
</form>
</field>
</record>
<!-- your action window refers to the view_id you've just created -->
<record id="action_opinion" model="ir.actions.act_window">
<field name="name">OPINION</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">opinion</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="view_id" ref="view_opinion_wizard"/>
<field name="target">new</field>
</record>
<menuitem name="OPINION" id="menu_opinion" action="action_opinion" parent="timeorder_ratecard_rnd_menu"/>
</data>
This is new API code try this code:
class opinion(models.TransientModel):
_name = 'opinion'
opinion_emission = fields.Text(default=lambda self: self.get_opinion(self), string='OPINION EMISSION')
notes = fields.Text(default=lambda self: self.get_notes(self), string='Additional Notes')
#api.multi
def save_it(self):
active_id = self.env.context.get('active_id', False)
print 'active_id', active_id
if active_id:
op = self.env['opinion'].browse(active_id)
op.write({'opinion_emission': self.opinion_emission, 'notes': self.notes})
return {'type': 'ir.actions.act_window_close', }
#functions that get the info stored in db
#api.one
def get_opinion(self):
active_id = self.env.context.get('active_id', False)
print 'active_id', active_id
if active_id:
return self.env['opinion'].browse(active_id).opinion_emission
#api.one
def get_notes(self, records=None):
model_name = self.env.context.get('active_model')
print model_name # gives NONE why ?
active_id = self.env.context.get('active_id', False)
print 'active_id', active_id
if active_id:
print 'output', self.env['opinion'].browse(active_id).notes
return self.env['opinion'].browse(active_id).notes