I want to print all the reports that belong to a particular client.
I already have my own form of report.
I do not know how to add "print_all" button for print(or just save to pdf) all invoices
If someone knows where I can look for similar solutions, please help.
If I was not clear enough or if you needed more informatino, please let me know.
There is no need to write your own function in order to print all report related to customer, under customer form there is smart button "invoiced", this will open the customer specific invoices and you can print as answered by #WaKo.
You can add a button to the ListView and use JavaScript to download files separately (call a python method to get report data as base64 string).
To add the button you need to override the ListView Qweb template.
Qweb:
<?xml version="1.0" encoding="UTF-8"?>
<templates id="sync_template" xml:space="preserve">
<t t-extend="ListView.buttons">
<t t-jquery="button.oe_list_add" t-operation="after">
<t t-if="widget.model == 'account.invoice'">
<button class="btn btn-sm btn-default oe_print_all" type="button">Print All</button>
</t>
</t>
</t>
</templates>
JavaScript:
I included download.js to be able to call download function from js.
openerp.print_all = function(instance) {
instance.web.ListView.include({
load_list: function(data) {
this._super(data);
if (this.$buttons) {
this.$buttons.find('.oe_print_all').off().click(this.proxy('print_all')) ;
}
},
print_all: function () {
var report_obj = new instance.web.Model("report")
var dataset = this.dataset;
new instance.web.Model("account.invoice")
.call("get_report_data", [this.groups.get_selection().ids])
.done(function (datas) {
console.log(datas);
$.each(datas, function( index, data ) {
download('data:application/pdf;base64,' + data[0], "Invoice_" + data[1] + '.pdf','application/pdf');
});
});
}
});
}
I used get_report_data method wich returns a list of tuples [(invoice_data, name), ...]
Python
class AccountInvoice(models.Model):
_inherit = "account.invoice"
#api.model
def get_report_data(self, ids):
report_obj = self.env['report']
return [(base64.b64encode(report_obj.get_pdf(invoice, "account.report_invoice")),
invoice.number.replace('/', '-') if invoice.number else '')
for invoice in self.browse(ids)]
Related
I am learning mongodb with meteor js so dont have much knowledge of both mongodb and meteor.
Below is the js code:
import { Template } from 'meteor/templating';
import { Dbs } from '../lib/collections.js';
import './main.html';
Template.body.helpers({
/*temp1:[
{text:'my data1'}'
{text:'my data1'}
]*/
dbs(){
return Dbs.find({'user_id':'p123'});
}
});
Basically i just want to pass user id in a textbox and based on it,i want to display the other details of user.In above code i am passing it manually and its working.anyone suggest me what should i do here ?
You can attach to the template instance a ReactiveVar that you will update with an event.
Then use it inside the helper, so the helper will re-execute everytime the value change:
Template.myTemplate.onCreated(function() {
this.currentTextBox = new ReactiveVar();
});
Template.myTemplate.events({
"keyup .js-my-textbox"(event, instance) {
// This event is executed when you type in an input with the class "js-my-textbox"
instance.currentTextBox.set(event.target.value);
},
});
Template.myTemplate.helpers({
dbs() {
const instance = Template.instance();
return Dbs.find({'user_id': instance.currentTextBox.get() });
},
});
EDIT:
Example of what could be the html part:
<template name="myTemplate">
<input type="text" placeholder="Search" class="js-my-textbox">
{{#each db in dbs}}
<-- Do what you want, example: -->
<p>{{db.myField}}</p>
{{/each}}
</template>
First You Create Event Function Template.body.events({}) to access text .Then store variable to use Meteor Session. Then Variable easily access helpers.If session changes variable will be changed.
I have a simple <firebase-query> tag, and I'd like to manipulate some of the data before having it displayed through a <dom-repeat>. For example, I need to turn some fields into links, and also parse some dates.
So, I need to get the data once it's ready, loop through each item, and change some of the values.
To do that, I have an observer on the data to detect when it's ready. However, I can't figure out how to loop through the data from that JavaScript function. For some reason, for(var i in items) doesn't work, although the items do exist.
Here is the component:
<dom-module id="cool-stuff">
<template>
<firebase-query id="query" path="/items" data="{{items}}"></firebase-query>
<template is="dom-repeat" items="{{items}}" as="item">
[[item.name]]<br />
[[item.date]]<br />
</template>
</template>
<script>
Polymer({
is: 'ix-table',
properties: {
items: {type: Object, observer: "_itemsChanged"},
}
itemsChanged: function(data) {
// how do I loop through the data received from firebase-query?
console.log(data);
}
});
</script>
</dom-module>
Ideally, all I'd want to do in the observer function is something like:
for(var i in data) {
obj = data[i];
obj.name = '<a href="/item/"+obj.key>'+ojb.name+'</a>';
}
But I can't seem to be able to loop through the data.
Inside the observer function, console.log(data) returns some weird stuff like this:
Array[o]
0: Object (which contains a proper item)
1: Object (same)
2: Object (same)
Update:
Here is a screenshot of what console.log(data) returns (from inside the observer):
The array seems to be populated with all the objects, but it shows as Array[0]. So it won't let me loop through them.
Update 2:
Thanks to arfost here is the solution:
<script>
Polymer({
is: 'ix-table',
properties: {
items: {type: Object},
}
observers: [
'_itemsChanged(items.splices)'
],
_itemsChanged: function(changeRecord) {
if (changeRecord) {
changeRecord.indexSplices.forEach(function(s) {
for (var i=0; i<s.addedCount; i++) {
var index = s.index + i;
var item = s.object[index];
console.log('Item ' + item.name + ' added at index ' + index);
// do whatever needed with the item here:
this.items[index].name = "New name";
}
}, this);
}
},
});
</script>
<firebase-query> results
Note that <firebase-query> results in an array of objects. Let's say your database contained the following items under /notes/<USER_ID>/:
Your <firebase-query> would look similar to this:
<firebase-query
id="query"
app-name="notes"
path="/notes/[[user.uid]]"
data="{{notes}}">
</firebase-query>
(where user is bound to <firebase-auth>.user).
Assuming the user is logged in, <firebase-query> would then populate its data property (i.e., bound to notes) with the following array:
Note how each object contains a $key property, which corresponds to the item's key seen in the Firebase console's Database view.
You could then iterate notes directly with <dom-repeat>:
<template is="dom-repeat" items="[[notes]]">
<li>
<div>key: [[item.$key]]</div>
<div>body: [[item.body]]</div>
<div>title: [[item.title]]</div>
</li>
</template>
Binding to HTML strings
You should be aware that the string data bindingsĀ are rendered literally in this case, so attempting to set name to obj.name = '<a href="...">' would render the literal string instead of an anchor. Instead, you should declare the tags in your template, and bind the key and name properties inside those tags. So, your observer could be replaced with this:
<template is="dom-repeat" items="{{items}}" as="item">
<a href$="/item/[[item.key]]">[[item.name]]</a><br />
[[item.date]]<br />
</template>
Iterating an array
The following note is only relevant if you prefer to mutate the data before displaying it...
When iterating an array, you should avoid for..in because it doesn't guarantee order of iteration, and because it may iterate over enumerable properties you might not necessarily care about. Instead, you could use for..of (assuming ES6 is available to your app):
for (let note of notes) {
note.title += ' ...';
}
or Array.prototype.forEach():
notes.forEach(function(note) {
note.title += ' ...';
});
I thinks I have run into the same issue as you.
It come from the way firebase query is getting the array, the way polymer obersvers works, and is hidden by the fact that the javascript console is reference based when it show the objects.
In fact what really happen here, is that firebase query is creating an empty array, which trigger your polymer observer.
So your function is called as soon as the array is created, but still empty and you can't iterate through, since it's empty. You then log it, where the primitives sub-properties are correctly displayed (array[0])
Then firebase begin to populate the array with the datas. The arrays reference stay the same, so polymer won't fire the observer again, and in the console, when it try to display the array it display the array referenced in the log, which now contains the datas.
I recommend that you use a array mutation observer in place of your simple one as follow
`properties: {
items: {type: Object},
},
,
observers: [
'_itemsChanged(items.splices)'
],`
It will fire every time an object is added to your array, and you would be able to do the work you need :)
I had the link for the documentation on array mutation observer :)
polymer array mutation observer
I hope this will solve your issue,
have a good day.
i don't think i can think of a scenario where you'd need to mutate the data by looping through the array rather than just using computed bindings. like this:
<template is="dom-repeat" items="{{items}}" as="item">
<child-el date="{{_computeDate(item.date)}}"></child-el><br />
<child-el attr1="{{_someOtherConversion(item.prop1)}}"></child-el><br />
<child-el attr2="{{_iPromiseAnyConversionCanBeDoneLikeThis(item.prop2)}}"></child-el><br />
</template>
<script>
_computeDate: function(item) {
//do date converstion
}
I have a custom FileArgument class that I'm using to store information about an uploaded file:
export class FileArgument {
name: string;
path: string;
file: File;
}
My upload works fine and the server then returns the path where the file was uploaded. I then want to store this path in a dictionary using a previously set fileArgument.name as key. Below is a simplified overview of my component. onSubmit() is where the action is happening:
export class InputModuleComponent {
private vsmodule: InputModule;
private moduleArguments = {};
private fileArgument: FileArgument = new FileArgument();
#Input()
module: InputModule;
constructor(private inputModuleService: InputModuleService) {}
onSubmit(): void {
this.inputModuleService.postFile(this.fileArgument.file).subscribe(
response => {
this.moduleArguments[this.fileArgument.name] = response.filename;
console.log(this.moduleArguments);
},
error => {}
);
}
onFileChange(event): void {
this.fileArgument.file = event.originalTarget.files[0];
this.fileArgument.name = event.originalTarget.id;
}
}
Line 14 above (this.moduleArguments[this.fileArgument.name] = response.filename;) causes the following error in Firefox:
EXCEPTION: Uncaught (in promise): SecurityError: The operation is insecure.
and in Chrome:
core.umd.js:5995 EXCEPTION: Uncaught (in promise): InvalidStateError: Failed to set the 'value' property on 'HTMLInputElement': This input element accepts a filename, which may only be programmatically set to the empty string.
If I replace that line with, for example:
this.moduleArguments['hello'] = response.filename;
I don't get any errors. The error clearly comes from using fileArgument.name as a dict key, but I have no idea why.
EDIT: The postFile() method from my service is below:
postFile (file: File): Observable<any> {
console.log('input-module.service - postFile()');
var url = this.uploadURL;
return Observable.create(observer => {
var formData: FormData = new FormData()
var xhr: XMLHttpRequest = new XMLHttpRequest();
formData.append("upload", file, file.name);
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
observer.next(JSON.parse(xhr.response));
observer.complete();
} else {
observer.error(xhr.response);
}
}
};
xhr.open('POST', url, true);
xhr.send(formData);
});
}
The component HTML:
<a (click)="modal.open()">
{{vsmodule.displayName}}
</a>
<modal #modal>
<form (ngSubmit)="onSubmit()">
<modal-header [show-close]="true">
<h4 class="modal-title">Input Module - {{vsmodule.displayName}}</h4>
</modal-header>
<modal-body>
<p>{{vsmodule.description}}</p>
<hr>
<ul>
<li *ngFor="let arg of vsmodule.args; let i = index">
<fieldset *ngIf="arg.type == 'file'">
<label>{{ arg.displayName }}</label>
<input
name="{{arg.name}}"
id="{{ arg.name }}"
type="file"
[(ngModel)]="moduleArguments[arg.name]"
(change)="onFileChange($event)"
>
<p>{{ arg.description }}<p>
</fieldset>
</li>
</ul>
</modal-body>
<modal-footer>
<button type="button" class="btn btn-default" data-dismiss="modal" (click)="modal.dismiss()">Dismiss</button>
<button type="submit" class="btn btn-primary">Run</button>
</modal-footer>
</form>
</modal>
In onChange, fileArgument.name is set to the value of event.originalTarget.id - the id of an actual HTML element in the page
And chrome error is saying:
Failed to set the 'value' property on 'HTMLInputElement'
Edit since you added the html - you have bound the 'moduleArguements' property to the file input element's ngmodel - as a result, changing that value will cause angular to try and modify the value property on the file input which is not permitted.
What is the purpose of updating that value? Is it just to feedback to the user?
If you remove the ngModel binding from the input element it should work - you are using the onFileChange event to capture the new filename anyway (although in the controller it is just onChange?)
Short Answer: You cannot cannot actually change the value of the this.moduleArguments[this.fileArgument.name]as it would be a security issue.
Explaination: You would be changing the actual value of this.fileArgument.name to something else, which would allow other people with ill intent to do the same. Essentially, an attacker could change that name to redirect any attempts to use that file to another file. So, Java (or Flash, or any other programming language) could not programmatically change that due to security reasons.
With your work-around, you are not actually setting a File data member, therefore JS does not see this as a security hazard.
Remember, [almost] anything you can do involving website/server code (or code interacting with either), an attacker can do as well. That's why JS, in this case, blocks people from changing the content of this specific standard File object. Hope this helps!
have you tried this:
var fileName = this.fileArgument.name;
this.moduleArguments[fileName] = response.filename;
and also if you are somewhere in JS changing the 'value' of your
tag, you will get that error, please refer to:
https://stackoverflow.com/a/29721020/2071008
I want to get the property name of spacebar Ex.
<template name="example">
{{#with object}}
{{level1.level2}}
{{permission.post.create}}
{{permission.post.read}}
{{/with}}
</template>
After I click on it I want to get the name of property
Template.example.events({
"click <selector>" : function(event,template){
//Question how to get name "level1.level2" ,"permission.post.create" ,"permission.post.read"
var name1 = ??? // I want name1 = "level1.level2" (string not it value)
//After I get the property name I can use it in MongoDB query like this
var key = {}
key[name1] = "newvalue" // now I got { 'level1.level2' : 'newvalue'}
Model.update({_id : "abc"}, key)
}
})
It very useful when you have to update deep nested document Ex.. table of permission list with checkbox
what about to add data- properties to the html elements, and then when you receive the event, you can do something like var -id = $(event.target).attr("data-id"); I didnt understand completely your question neither have access to you data structure but assuming that you want to get an information from the object that you render in a html element. My suggestion its the follow:
<template name="mytemplate">
{{ object.name }}
</template>
Template.<name>.events({
"click <selector>" : function(event,template){
//Question how to get name "level1.level2" ,"permission.post.create" ,"permission.post.read"
var name1 = $(event.target).attr("data-name");
//do whatever with you variable
}
})
Sorry if this its not what are you looking.
ncubica your answer was close that what I want.
I use
<template name="mytemplate">
{{level1.level2}}
</template>
Then I can use it like this
"click .toggle-checked" : function(event,template){
var pName = $(event.target).attr("data-name")
var updateAttr ={}
updateAttr[pName] = "theNewValue"
Collection.update({_id : this._id},{ $set : updateAttr})
}
Thank you for your hint.
I want to include a template (or use a helper, I don't care) that could be clicked to edit in place. This view MUST be reusable, and so can't rely on the Session variable or any other variables that aren't contained by the view instance.
In the display mode it would look like this:
<div class="editable">{{content}}</div>
which would change to the edit mode when you clicked on it, which would look like this:
<input type="text" value="{{content}}" />
and you could revert back to display mode (persisting it's changes appropriately) by either hitting enter or pressing a button.
It seems meteor doesn't make this incredibly easy, since my first attempts with html:
<template name="editable">
{{#if editing}}
<input type="text" value={{this}} />
{{else}}
<div class="edit-thing">{{this}}</div>
{{/if}}
</template>
// In the appropriate display template.
{{> editable stuff}}
and js:
Template.user.stuff = "yo yo yo";
Template.editable.events({
'click .edit-thing': function(e) {
this.isEditing = true;
}
});
Template.editable.helpers({
editing: function() {
return !!this.isEditing;
}
});
have had problems with not being reactive, which using the Deps library didn't solve. (This version just wouldn't change when you clicked it, since this.isEditing isn't reactive and doesn't trigger a change in the editing helper.)
Ask for more information if you like! Thanks!
That's a typical use case for Deps, did you remember to use both depend and changed? The js code may look like this:
Template.editable.created = function() {
this.data.isEditing = false;
this.data.isEditingDep = new Deps.Dependency();
};
Template.editable.events({
'... whatever to start edit mode ...': function(e, t) {
t.data.isEditing = true;
t.data.isEditingDep.changed();
},
'... whatever to close edit mode ...': function(e, t) {
t.data.isEditing = false;
t.data.isEditingDep.changed();
},
});
Template.editable.editing = function() {
this.isEditingDep.depend();
return this.isEditing;
};