OpenERP custom report filename - openerp-7

I would like to customize reports filename.
For instance, when I download an invoice, I will have something like 'Invoice.pdf' as filename. What I would like is something like 'invoice_number.pdf' but I don't know how to use dynamic file name ?

I found a way for 7.0 and current trunk:
For a quick fix, take a look at the Reports class in:
openerp-7.0\web\addons\web\controllers\main.py
The report name is set in a variable named file_name:
file_name = '%s.%s' % (file_name, report_struct['format'])
Find this line and insert this part before it:
'''
YP: Added proc. to create reports with real object names (the object must have a field named "name"):
eg: "PO00006.pdf" instead of "Request For Quotation.pdf" for a single object report
"PO00006-PO00002.pdf" instead of "Request For Quotation.pdf" for a multiple objects report
'''
# Try to get current object model and their ids from context
if action.has_key('context'):
action_context = action.get('context',{})
if action_context.has_key('active_model') and action_context.has_key('active_id'):
action_active_model = action_context.get('active_model','')
action_active_ids = action_context.get('active_ids', [])
if action_active_model and action_active_ids:
# Use built-in ORM method to get data from DB
m = req.session.model(action_active_model)
r = m.read(action_active_ids, False, context)
# Parse result to create a better filename
for i, item in enumerate(r):
if item.has_key('name'):
if i == 0:
file_name = ('%s') % (item['name'])
else:
file_name = ('%s-%s') % (file_name, item['name'])

For a quick fix, take a look at the Reports class in:
openerp-6.1\web\addons\web\controllers\main.py
The report name is set in a variable named header.
For my needs, I only want the report to be named by the object name (that's suitable in 99% of cases) so I added a new variable named report_name:
report_name = action['report_name']
if action.has_key('context'):
action_context = action.get('context',{})
if action_context.has_key('name'):
report_name = action_context['name']
return req.make_response(report,
headers=[
('Content-Disposition', 'attachment; filename="%s.%s"' % (report_name, report_struct['format'])),
('Content-Type', report_mimetype),
('Content-Length', len(report))],
cookies={'fileToken': int(token)})

I found a solution to set the default filename like the attach file name, using the expression in the attachment attribute in your xml report definition.
Found and modify this file: openerp7/openerp-web/addons/web/controllers/main.py.
Search this sentence:
file_name = '%s.%s' % (file_name, report_struct['format'])
Then add this code before:
'''Code added by Raul Paz to set the default file_name like the attach_name
The attach_name mach with the attachment expresion from your report_xml
<report
id="report_webkit.YourModule_report_id"
model="YourModule.model"
name="Your_report_name"
file="YOUR_MODULE/report/YOUR_MAKO_FILE.mako"
string="Your Text in the print button"
auto="False"
report_type="webkit"
attachment="'Valid_filename_expresion"
usage="default"
/>
And
to modify existing report sale_report.xml to manage the name:
create in your module a file : sale_report.xml
<?xml version="1.0" encoding="UTF-8"?>
<openerp>
<data>
<record id="sale.report_sale_order" model="ir.actions.report.xml">
<field name="attachment">(object.name or 'Sale Order')</field>
<field name="attachment_use" eval="True"/>
</record>
</data>
</openerp>
'''
try:
if action.get('attachment_use',False) and action['attachment']:
model = context['active_model']
cr = openerp.pooler.get_db(req.session._db).cursor()
uid = context['uid']
ids = context['active_ids']
objects=openerp.pooler.get_pool(req.session._db).get(model).browse(cr,uid,ids,context=context)
file_name=[eval(action['attachment'],{'object':x, 'time':time}) for x in objects][0]
except:
pass
#end code added
Good luky

Related

fill up binary field with default_get

I have two binary fields. In the sale order line tree view, I have a button to open my wizard. I chose and save file and it's perfectly saved in my label_file field of the selected sale order line.
The problem is that when I open the wizard I want to see it as a saved file, but it's not generated and only bytes are in the path.
first
Class SaleOrderLine(models.Model):
_inherit = 'sale.order.line'
label_file = fields.Binary('Saved Label')
Saved in SaleOrderLine
second
class OrderLineLabel(models.TransientModel):
_name = 'order.line.label'
label_file_show = fields.Binary('Label file')
#api.multi
def write_label_vals(self):
self.ensure_one()
sale_order_line = self.env['sale.order.line'].browse(self.env.context.get('active_ids'))
vals = {
'label_file': self.label_file,
}
sale_order_line.write(vals)
#api.model
def default_get(self, fields):
res = super(OrderLineLabel, self).default_get(fields)
order_line_id = self.env['sale.order.line'].browse(self.env.context.get('active_ids'))
status, headers, content = binary_content(model='sale.order.line', field='label_file', id=order_line_id.id,filename='test',download=True)
#tried like this
res['label_file_show'] = content
#or just like this
res['label_file_show'] = order_line_id.label_file
return res
this is how it looks when I open the wizard.
You need to add filename to the binary fields.
Declare a char field to hold the name:
label_file_name = fields.Char()
And use filename attribute to specify the file name to the binary field:
<field name="label_file_name" invisible="True"/>
<field name="label_file_show" filename="label_file_name"/>
In write_label_vals add one more line to save also the file name.
order_line_id.label_file_name = self.label_file_name
# vals = {'label_file': self.label_file, 'label_file_name': self.label_file_name}
Set the value of the filename in the wizard default_get method :
res['label_file_name'] = order_line_id.label_file_name

How to send a variable to a text file that calling to karate feature file...?

Step 01#: I am calling 'Request Date' from json file and saving as "RequestDate"
Background:
json req = read('classpath:XXX/XXX/API/02_Dataset/DataSet.json')
* def RequestDate = get req.GameEnq.RequestDate
Step 02#: I am also calling 'GameDetailsRequest' from json file which has the field called "RequestDate", I would like pass "RequestDate" into "GameDetailsRequest".
Scenario: GameEnq
Given request
"""
GameDetailsRequest
"""
* def GameDetailsRequest = read('classpath:XXX/XXX/API/02_Dataset/ServiceRequestData_GameEnq');
Note: I can able to print the "RequestDate" value correctly ,however i don't know how to call into "GameDetailsRequest"... Please assist me. Your suggestion highly appreciated
Kind Regards
Sudheer Bonam
I think you need to try replace for a text place holder replacement
Add a placeholder <PLACEHOLDER_NAME> in your text data in GameDetailsRequest where you want to insert RequestDate
eg:
* string GameDetailsRequest = "Game release data : <RequestDate>"
* replace GameDetailsRequest.RequestDate = "12-12-2020"
Now GameDetailsRequest will be "Game release data : 12-12-2020"
refer: karate doc for replace

In OpenERP, how can I force SQL to run when a module is installed?

I have a module built in OpenERP that adds some fields to existing models.
The definition is roughly as follows:
class res_partner(osv.osv):
_name = _inherit = 'res.partner'
_columns = {
'my_special_id': fields.integer('Special ID', required=False, readonly=True),
}
_defaults = {
'my_special_id': lambda *a: None
}
_sql_constraints = [
('special_id_uniq', 'UNIQUE (my_special_id)', 'Must be unique.'),
}
_sql = """CREATE UNIQUE INDEX special_id_limited_uniq
ON res_partner (my_special_id)
WHERE my_special_id != 0
"""
res_partner()
My goal is to have a field (special_id_uniq) that:
Defaults to NULL
Is an integer
Is unique, across all values, except for NULL
My first instinct was to create the _sql_constraint definition above, but this caused issues: apparently OpenERP doesn't have nullable integer fields, so 'None' is treated as 0. This breaks the SQL constraint, as now multiple values of 0 must be allowed.
To get around this, I removed the _sql_constraints option on the model and added the _sql code which creates a PostgreSQL partial index. To verify that it works, I ran the SQL myself on PostgreSQL and it acheives my desired effect. However, when I create a new DB via OpenERP and install my custom module, this SQL is not run. That is because the model is not being created:
def _auto_init(self, cr, context=None):
#...
create = not self._table_exist(cr)
#...
#Line 3046 of openerp/osv/orm.py version 6.1 function _auto_init
if create:
self._execute_sql(cr)
#...
So, how can I run some custom SQL at the point of module installation?
Actually, I might have just answered my own question: override _auto_init.
Edit:
Yes, the following works to run the SQL during install / upgrade:
def _auto_init(self, cr, context=None):
ret = super(res_partner, self)._auto_init(cr, context)
self._execute_sql(cr)
return ret
You can achieve this by adding a function entry in a data xml file of the module.
<openerp>
<data>
<!-- ... your init data records ...-->
<function model="res.partner" name="_my_init_mehod"/>
</data>
</openerp>
You can see a working example of this in the standard module document:
document_data.xml declares a call to the function _attach_parent_id to migrate all attachments to the new DMS structure at the end of the installation.

The ':' character, hexadecimal value 0x3A, cannot be included in a name

I saw this question already, but I didnt see an answer..
So I get this error:
The ':' character, hexadecimal value 0x3A, cannot be included in a name.
On this code:
XDocument XMLFeed = XDocument.Load("http://feeds.foxnews.com/foxnews/most-popular?format=xml");
XNamespace content = "http://purl.org/rss/1.0/modules/content/";
var feeds = from feed in XMLFeed.Descendants("item")
select new
{
Title = feed.Element("title").Value,
Link = feed.Element("link").Value,
pubDate = feed.Element("pubDate").Value,
Description = feed.Element("description").Value,
MediaContent = feed.Element(content + "encoded")
};
foreach (var f in feeds.Reverse())
{
....
}
An item looks like that:
<rss>
<channel>
....items....
<item>
<title>Pentagon confirms plan to create new spy agency</title>
<link>http://feeds.foxnews.com/~r/foxnews/most-popular/~3/lVUZwCdjVsc/</link>
<category>politics</category>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/" />
<pubDate>Tue, 24 Apr 2012 12:44:51 PDT</pubDate>
<guid isPermaLink="false">http://www.foxnews.com/politics/2012/04/24/pentagon-confirms-plan-to-create-new-spy-agency/</guid>
<content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[|http://global.fncstatic.com/static/managed/img/Politics/panetta_hearing_030712.jpg<img src="http://feeds.feedburner.com/~r/foxnews/most-popular/~4/lVUZwCdjVsc" height="1" width="1"/>]]></content:encoded>
<description>The Pentagon confirmed Tuesday that it is carving out a brand new spy agency expected to include several hundred officers focused on intelligence gathering around the world.&amp;#160;</description>
<dc:date xmlns:dc="http://purl.org/dc/elements/1.1/">2012-04-4T19:44:51Z</dc:date>
<feedburner:origLink>http://www.foxnews.com/politics/2012/04/24/pentagon-confirms-plan-to-create-new-spy-agency/</feedburner:origLink>
</item>
....items....
</channel>
</rss>
All I want is to get the "http://global.fncstatic.com/static/managed/img/Politics/panetta_hearing_030712.jpg", and before that check if content:encoded exists..
Thanks.
EDIT:
I've found a sample that I can show and edit the code that tries to handle it..
EDIT2:
I've done it in the ugly way:
text.Replace("content:encoded", "contentt").Replace("xmlns:content=\"http://purl.org/rss/1.0/modules/content/\"","");
and then get the element in the normal way:
MediaContent = feed.Element("contentt").Value
The following code
static void Main(string[] args)
{
var XMLFeed = XDocument.Parse(
#"<rss>
<channel>
....items....
<item>
<title>Pentagon confirms plan to create new spy agency</title>
<link>http://feeds.foxnews.com/~r/foxnews/most-popular/~3/lVUZwCdjVsc/</link>
<category>politics</category>
<dc:creator xmlns:dc='http://purl.org/dc/elements/1.1/' />
<pubDate>Tue, 24 Apr 2012 12:44:51 PDT</pubDate>
<guid isPermaLink='false'>http://www.foxnews.com/politics/2012/04/24/pentagon-confirms-plan-to-create-new-spy-agency/</guid>
<content:encoded xmlns:content='http://purl.org/rss/1.0/modules/content/'><![CDATA[|http://global.fncstatic.com/static/managed/img/Politics/panetta_hearing_030712.jpg<img src='http://feeds.feedburner.com/~r/foxnews/most-popular/~4/lVUZwCdjVsc' height='1' width='1'/>]]></content:encoded>
<description>The Pentagon confirmed Tuesday that it is carving out a brand new spy agency expected to include several hundred officers focused on intelligence gathering around the world.&amp;#160;</description>
<dc:date xmlns:dc='http://purl.org/dc/elements/1.1/'>2012-04-4T19:44:51Z</dc:date>
<!-- <feedburner:origLink>http://www.foxnews.com/politics/2012/04/24/pentagon-confirms-plan-to-create-new-spy-agency/</feedburner:origLink> -->
</item>
....items....
</channel>
</rss>");
XNamespace contentNs = "http://purl.org/rss/1.0/modules/content/";
var feeds = from feed in XMLFeed.Descendants("item")
select new
{
Title = (string)feed.Element("title"),
Link = (string)feed.Element("link"),
pubDate = (string)feed.Element("pubDate"),
Description = (string)feed.Element("description"),
MediaContent = GetMediaContent((string)feed.Element(contentNs + "encoded"))
};
foreach(var item in feeds)
{
Console.WriteLine(item);
}
}
private static string GetMediaContent(string content)
{
int imgStartPos = content.IndexOf("<img");
if(imgStartPos > 0)
{
int startPos = content[0] == '|' ? 1 : 0;
return content.Substring(startPos, imgStartPos - startPos);
}
return string.Empty;
}
results in:
{ Title = Pentagon confirms plan to create new spy agency, Link = http://feeds.f
oxnews.com/~r/foxnews/most-popular/~3/lVUZwCdjVsc/, pubDate = Tue, 24 Apr 2012 1
2:44:51 PDT, Description = The Pentagon confirmed Tuesday that it is carving out
a brand new spy agency expected to include several hundred officers focused on
intelligence gathering around the world. , MediaContent = http://global
.fncstatic.com/static/managed/img/Politics/panetta_hearing_030712.jpg }
Press any key to continue . . .
A few points:
You never want to treat Xml as text - in your case you removed the namespace declaration but actually if the namespace was declared inline (i.e. without binding to the prefix) or a different prefix would be defined your code would not work even though semantically both documents would be equivalent
Unless you know what's inside CDATA and how to treat it you always want to treat is as text. If you know it's something else you can treat it differently after parsing - see my elaborate on CDATA below for more details
To avoid NullReferenceExceptions if the element is missing I used explicit conversion operator (string) instead of invoking .Value
the Xml you posted was not a valid xml - there was missing namespace Uri for feedburner prefix
This is no longer related to the problem but may be helpful for some folks so I am leaving it
As far as the contents of the encode element is considered it is inside CDATA section. What's inside CDATA section is not an Xml but plain text. CDATA is usually used to not have to encode '<', '>', '&' characters (without CDATA they would have to be encoded as < > and & to not break the Xml document itself) but the Xml processor treat characters in the CDATA as if they were encoded (or to be more correct in encodes them). The CDATA is convenient if you want to embed html because textually the embedded content looks like the original yet it won't break your xml if the html is not a well-formed Xml. Since the CDATA content is not an Xml but text it is not possible to treat it as Xml. You will probably need to treat is as text and use for instance regular expressions. If you know it is a valid Xml you can load the contents to an XElement again and process it. In your case you have got mixed content so it is not easy to do unless you use a little dirty hack. Everything would be easy if you have just one top level element instead of mixed content. The hack is to add the element to avoid all the hassle. Inside the foreach look you can do something like this:
var mediaContentXml = XElement.Parse("<content>" + (string)item.MediaContent + "</content>");
Console.WriteLine((string)mediaContentXml.Element("img").Attribute("src"));
Again it's not pretty and it is a hack but it will work if the content of the encoded element is valid Xml. The more correct way of doing this is to us XmlReader with ConformanceLevel set to Fragment and recognize all kinds of nodes appropriately to create a corresponding Linq to Xml node.
You should use XNamespace:
XNamespace content = "...";
// later in your code ...
MediaContent = feed.Element(content + "encoded")
See more details here.
(Of course, you the string to be assigned to content is the same as in xmlns:content="...").

Adding a node to an existing XML file using inno setup

In my inno setup script there is a [code] section and I need to add some code to:
Open an xml file
then add a single node in a specific place
Save the file back to the hard drive
I need to be able to edit a file called config.xml in \documents\docotype
in the file there is some code like this:
<References>
<ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<string>System.dll</string>
<string>System.Core.dll</string>
<string>System.Drawing.dll</string>
<string>System.Windows.Forms.dll</string>
<string>System.XML.dll</string>
</ArrayOfString>
</References>
I need it to look like this:
<References>
<ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<string>System.dll</string>
<string>System.Core.dll</string>
<string>System.Drawing.dll</string>
<string>System.Windows.Forms.dll</string>
<string>System.XML.dll</string>
<string>C:\\bin\Custom\cutty109.dll</string>
</ArrayOfString>
</References>
So really I just need to add the following line into the file in the 'ArrayOfString' section
<string>C:\\bin\Custom\cutty109.dll</string>
I'm sure this must be possible but I have no clue how..
Thanks
Please refer the CodeAutomation.iss example provided along with inno install. And use this code instead the original code under the 'Modify the XML document' section.
{ Modify the XML document }
NewNode := XMLDoc.createElement('string');
XMLDoc.setProperty('SelectionLanguage', 'XPath');
RootNode := XMLDoc.selectSingleNode('//References/ArrayOfString');
RootNode.appendChild (NewNode);
RootNode.lastChild.text :='C:\\bin\Custom\cutty109.dll';
{ Save the XML document }
I assume you really need some dynamic way to add to this config file, if not then of course overriding the old one is the simplest method.
To dynamically add sections to a config file, you have some options:
You can create your own command line utility (exe or script) that does the file manipulation and call that utility in the [Run] section of your install script. This could look something like this:
In the [Files] section, you'll have one line for your utility:
Source: "myUtil.exe"; DestDir: "{app}"
In the [Run] section, you'll have one line for each manipulation you need to do in your config, like this:
FileName: "{app}\myUtil.exe"; Parameters: "/addSection:"
OR
You can use Pascal scripting to manipulate your config file. You can create a Pascal that uses CreateOleObject to call msxml.dll for XML the file manipulation. Then, in your [Files] section you can use AfterInstall to to call your Pascal function, like this:
Source: "myFileThatNeedsConfigManipulation.dll"; DestDir: ... ;
AfterInstall: MyPascalFunctionThatDoesTheManipulation
Try something like this:
Dim sXPath : sXPath = "/configuration/References/ArrayOfString"
Dim sAdd : sAdd = "C:\\bin\Custom\cutty109.dll"
Dim sElm : sElm = "string"
Dim sFSpec : sFSpec = resolvePath( "..\data\config.xml" )
Dim oXDoc : Set oXDoc = CreateObject( "Msxml2.DOMDocument" )
oXDoc.setProperty "SelectionLanguage", "XPath"
oXDoc.async = False
oXDoc.load sFSpec
If 0 = oXDoc.ParseError Then
WScript.Echo sFSpec, "looks ok"
Dim ndFnd : Set ndFnd = oXDoc.selectSingleNode( sXPath )
If ndFnd Is Nothing Then
WScript.Echo "|", sXPath, "| not found"
Else
WScript.Echo "found |" & ndFnd.tagName & "|"
Dim ndNew : Set ndNew = oXDoc.createElement( sElm )
ndNew.appendChild oXDoc.createTextNode( sAdd )
ndFnd.appendChild ndNew
WScript.Echo "After appending:"
WScript.Echo oXDoc.xml
oXDoc.Save Replace( sFSpec, ".xml", "-2.xml" )
End If
Else
WScript.Echo oXDoc.ParseError.Reason
End If
The steps:
create a Msxml2.DOMDocument
use XPath to find the node to change
create a now string element and append the text
append the new node to the found node
save the modified XML