[MERGE] merge with main addons

bzr revid: cha@tinyerp.com-20120524054256-lqep5fdpu631fbrp
This commit is contained in:
Ajay Chauhan (OpenERP) 2012-05-24 11:12:56 +05:30
commit edd6043b76
23 changed files with 554 additions and 598 deletions

View File

@ -234,65 +234,63 @@
</ul>
</t>
<t t-name="kanban-box">
<t t-if="record.date_deadline.raw_value and record.date_deadline.raw_value lt (new Date())" t-set="border">oe_kanban_color_red</t>
<div t-attf-class="#{kanban_color(record.color.raw_value)} #{border || ''}">
<div class="oe_kanban_box oe_kanban_color_border">
<table class="oe_kanban_table oe_kanban_box_header oe_kanban_color_bgdark oe_kanban_color_border oe_kanban_draghandle">
<tr>
<td align="left" valign="middle" width="16">
<a t-if="record.priority.raw_value == 1" icon="star-on" type="object" name="set_normal_priority"/>
<a t-if="record.priority.raw_value != 1" icon="star-off" type="object" name="set_high_priority" style="opacity:0.6; filter:alpha(opacity=60);"/>
</td>
<td align="left" valign="middle" class="oe_kanban_title" tooltip="lead_details">
<field name="partner_id"/>
<t t-if="record.planned_revenue.raw_value">
- <t t-esc="Math.round(record.planned_revenue.value)"/>
<field name="company_currency"/>
</t>
</td>
<td valign="top" width="22">
<img t-att-src="kanban_image('res.users', 'avatar', record.user_id.raw_value[0])" t-att-title="record.user_id.value"
width="22" height="22" class="oe_kanban_gravatar"/>
</td>
</tr>
</table>
<div class="oe_kanban_box_content oe_kanban_color_bglight oe_kanban_box_show_onclick_trigger">
<div>
<b>
<a t-if="record.partner_address_email.raw_value" t-attf-href="mailto:#{record.partner_address_email.raw_value}">
<field name="partner_address_name"/>
</a>
<field t-if="!record.partner_address_email.raw_value" name="partner_address_name"/>
</b>
</div>
<div>
<field name="name"/>
</div>
<div style="padding-left: 0.5em">
<i><field name="date_action"/><t t-if="record.date_action.raw_value"> : </t><field name="title_action"/></i>
</div>
<div t-attf-class="oe_kanban_color_#{kanban_getcolor(record.color.raw_value)} oe_kanban_card">
<a class="oe_kanban_menuaction oe_i">B</a>
<ul class="oe_kanban_menu">
<li><a type="edit" >Edit...</a></li>
<li><a type="delete">Delete</a></li>
<li><a name="%(mail.action_email_compose_message_wizard)d" type="action">Send New Email</a></li>
<li><a name="%(opportunity2phonecall_act)d" type="action">Log Call</a></li>
<li><a name="action_makeMeeting" type="object">Schedule Meeting</a></li>
<li><a name="%(crm.action_crm_add_note)d" context="{'model': 'crm.lead' }" type="action">Add Internal Note</a></li>
<li><ul class="oe_kanban_colorpicker" data-field="color"/></li>
</ul>
<div class="oe_kanban_content">
<a type="edit"><h3>
<field name="partner_id"/>
<t t-if="record.planned_revenue.raw_value">
- <t t-esc="Math.round(record.planned_revenue.value)"/>
<field name="company_currency"/>
</t>
</h3></a>
<div>
<b> <field name="partner_address_name"/> </b>
</div>
<div class="oe_kanban_buttons_set oe_kanban_color_border oe_kanban_color_bglight oe_kanban_box_show_onclick">
<div class="oe_kanban_left">
<a string="Edit" icon="gtk-edit" type="edit"/>
<a string="Change Color" icon="color-picker" type="color" name="color"/>
<a string="Send New Email" name="%(mail.action_email_compose_message_wizard)d" icon="terp-mail-message-new" type="action"/>
<a string="Log Call" name="%(opportunity2phonecall_act)d" icon="terp-call-start" type="action"/>
<a string="Schedule Meeting" name="action_makeMeeting" type="object" icon="stock_calendar"/>
<a string="Add Internal Note" name="%(crm.action_crm_add_note)d" context="{'model': 'crm.lead' }" icon="terp-document-new" type="action"/>
</div>
<div class="oe_kanban_right">
<a name="case_mark_lost" string="Mark Lost" states="open,pending" type="object" icon="kanban-stop" />
<a name="case_pending" string="Pending" states="draft,open" type="object" icon="kanban-pause" />
<a name="case_open" string="Open" states="pending" type="object" icon="gtk-media-play" />
<a name="case_mark_won" string="Mark Won" states="open,pending" type="object" icon="kanban-apply" />
</div>
<br class="oe_kanban_clear"/>
<div>
<field name="name"/>
</div>
<div style="padding-left: 0.5em">
<i>
<t t-if="record.date_deadline.raw_value and record.date_deadline.raw_value lt (new Date())" t-set="red">oe_kanban_text_red</t>
<span t-attf-class="#{red || ''}">
<field name="date_action"/>
</span>
<t t-if="record.date_action.raw_value"> : </t>
<field name="title_action"/>
</i>
</div>
<div class="oe_right">
<a t-if="record.priority.raw_value == 1" icon="star-on" type="object" name="set_normal_priority"/>
<a t-if="record.priority.raw_value != 1" icon="star-off" type="object" name="set_high_priority" style="opacity:0.7; filter:alpha(opacity=70);"/>
<!--
<t t-if="record.date_deadline.raw_value and record.date_deadline.raw_value lt (new Date())" t-set="red">oe_kaban_status_red</t>
<span t-attf-class="oe_kanban_status #{red}"> </span>
-->
<img t-att-src="kanban_image('res.users', 'avatar', record.user_id.raw_value[0])" t-att-title="record.user_id.value" width="24" height="24" class="oe_kanban_avatar"/>
</div>
<div class="oe_kanban_footer_left">
</div>
</div>
<div class="oe_clear"></div>
</div>
<!--
<div class="oe_kanban_right">
<a name="case_mark_lost" string="Mark Lost" states="open,pending" type="object" icon="kanban-stop" />
<a name="case_pending" string="Pending" states="draft,open" type="object" icon="kanban-pause" />
<a name="case_open" string="Open" states="pending" type="object" icon="gtk-media-play" />
<a name="case_mark_won" string="Mark Won" states="open,pending" type="object" icon="kanban-apply" />
</div>
-->
</t>
</templates>
</kanban>

View File

@ -1,20 +1,20 @@
<?xml version="1.0"?>
<openerp>
<data>
<record id="view_users_gogole_form" model="ir.ui.view">
<field name="name">res.users.google.form1</field>
<field name="model">res.users</field>
<field name="type">form</field>
<field name="inherit_id" ref="base.view_users_form"/>
<field name="arch" type="xml">
<xpath expr="//notebook[last()]" position="inside">
<page string=" Synchronization ">
<separator string="Google Account" colspan="4" />
<field name="gmail_user"/>
<field name="gmail_password" password="True"/>
</page>
</xpath>
</field>
</record>
<record id="view_users_gogole_form" model="ir.ui.view">
<field name="name">res.users.google.form1</field>
<field name="model">res.users</field>
<field name="type">form</field>
<field name="inherit_id" ref="base.view_users_form"/>
<field name="arch" type="xml">
<xpath expr="//notebook[last()]" position="inside">
<page string=" Synchronization ">
<separator string="Google Account" colspan="4"/>
<field name="gmail_user"/>
<field name="gmail_password" password="True"/>
</page>
</xpath>
</field>
</record>
</data>
</openerp>

View File

@ -37,16 +37,18 @@ class google_login(osv.osv_memory):
}
def google_login(self, user, password, type='', context=None):
if type == 'group':
if type == 'group':
gd_client = gdata.contacts.client.ContactsClient(source='OpenERP')
if type == 'contact' :
if type == 'contact':
gd_client = gdata.contacts.service.ContactsService()
if type == 'calendar':
gd_client = gdata.calendar.service.CalendarService()
if type =='docs_client':
gd_client = gdata.docs.client.DocsClient()
else:
gd_client = gdata.contacts.service.ContactsService()
try:
gd_client.ClientLogin(user, password,gd_client.source)
gd_client = gdata.contacts.service.ContactsService()
try:
gd_client.ClientLogin(user, password, gd_client.source)
except Exception:
return False
return gd_client
@ -60,7 +62,7 @@ class google_login(osv.osv_memory):
if 'password' in fields:
res.update({'password': user_obj.gmail_password})
return res
def login(self, cr, uid, ids, context=None):
data = self.read(cr, uid, ids)[0]
user = data['user']

View File

@ -0,0 +1 @@
import google_docs

View File

@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
{
'name': 'Google Docs integration',
'version': '0.2',
'author': 'OpenERP SA',
'website': 'http://openerp.com',
'category': 'Tools',
'installable': True,
'auto_install': False,
'web': True,
'js': ['static/src/js/gdocs.js'],
'update_xml': [
'res_config_user_view.xml'
],
'depends': ['google_base_account'],
'description': 'Module to attach a google document to any model.'
}

View File

@ -0,0 +1,152 @@
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2012 OpenERP SA (<http://www.openerp.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from osv import osv, fields
from tools.translate import _
try:
import gdata.docs.data
import gdata.docs.client
from gdata.client import RequestError
from gdata.docs.service import DOCUMENT_LABEL
import gdata.auth
except ImportError:
raise osv.except_osv(_('Google Docs Error!'), _('Please install gdata-python-client from http://code.google.com/p/gdata-python-client/downloads/list'))
class google_docs_ir_attachment(osv.osv):
_inherit = 'ir.attachment'
def _auth(self, cr, uid, context=None):
'''
Connexion with google base account
@return client object for connexion
'''
#pool the google.login in google_base_account
google_pool = self.pool.get('google.login')
#get gmail password and login. We use default_get() instead of a create() followed by a read() on the
# google.login object, because it is easier. The keys 'user' and 'password' ahve to be passed in the dict
# but the values will be replaced by the user gmail password and login.
user_config = google_pool.default_get( cr, uid, {'user' : '' , 'password' : ''}, context=context)
#login gmail account
client = google_pool.google_login( user_config['user'], user_config['password'], type='docs_client', context=context)
if not client:
raise osv.except_osv( _('Google Docs Error!'), _("Check your google configuration in users/synchronization"))
return client
def create_empty_google_doc(self, cr, uid, res_model, res_id, context=None):
'''Create a new google document, empty and with a default type (txt)
:param res_model: the object for which the google doc is created
:param res_id: the Id of the object for which the google doc is created
:return: the ID of the google document object created
'''
#login with the base account google module
client = self._auth(cr, uid, context=context)
# create the document in google docs
local_resource = gdata.docs.data.Resource(gdata.docs.data.DOCUMENT_LABEL)
#create a new doc in Google Docs
gdocs_resource = client.post(entry=local_resource, uri='https://docs.google.com/feeds/default/private/full/')
# create an ir.attachment into the db
self.create(cr, uid, {
'res_model': res_model,
'res_id': res_id,
'type': 'url',
'name': _('Google Doc'),
'url': gdocs_resource.get_alternate_link().href,
}, context=context)
return gdocs_resource.resource_id.text
def copy_gdoc(self, cr, uid, res_model, res_id, name_gdocs, gdoc_template_id, context=None):
'''
copy an existing document in google docs
:param res_model: the object for which the google doc is created
:param res_id: the Id of the object for which the google doc is created
:param name_gdocs: the name of the future ir.attachment that will be created. Based on the google doc template foun.
:param gdoc_template_id: the id of the google doc document to copy
:return: the ID of the google document object created
'''
#login with the base account google module
client = self._auth(cr, uid)
# fetch and copy the original document
try:
original_resource = client.get_resource_by_id(gdoc_template_id)
#copy the document you choose in the configuration
copy_resource = client.copy_resource(original_resource, 'copy_%s' % original_resource.title.text)
except:
raise osv.except_osv(_('Google Docs Error!'), _("Your resource id is not correct. You can find the id in the google docs URL"))
# create an ir.attachment
self.create(cr, uid, {
'res_model': res_model,
'res_id': res_id,
'type': 'url',
'name': name_gdocs,
'url': copy_resource.get_alternate_link().href
}, context=context)
return copy_resource.resource_id.text
def google_doc_get(self, cr, uid, res_model, ids, context=None):
'''
Function called by the js, when no google doc are yet associated with a record, with the aim to create one. It
will first seek for a google.docs.config associated with the model `res_model` to find out what's the template
of google doc to copy (this is usefull if you want to start with a non-empty document, a type or a name
different than the default values). If no config is associated with the `res_model`, then a blank text document
with a default name is created.
:param res_model: the object for which the google doc is created
:param ids: the list of ids of the objects for which the google doc is created. This list is supposed to have
a length of 1 element only (batch processing is not supported in the code, though nothing really prevent it)
:return: the google document object created
'''
assert len(ids) == 1, 'Creating google docs may only be done by one at a time'
res_id = ids[0]
pool_ir_attachment = self.pool.get('ir.attachment')
pool_gdoc_config = self.pool.get('google.docs.config')
name_gdocs = ''
model_fields_dic = self.pool.get(res_model).read(cr, uid, res_id, [], context=context)
# check if a model is configured with a template
google_docs_config = pool_gdoc_config.search(cr, uid, [('model_id', '=', res_model)], context=context)
if google_docs_config:
name_gdocs = pool_gdoc_config.browse(cr, uid, google_docs_config, context=context)[0].name_template
name_gdocs = name_gdocs % model_fields_dic
google_template_id = pool_gdoc_config.browse(cr, uid, google_docs_config[0], context=context).gdocs_resource_id
google_document = pool_ir_attachment.copy_gdoc(cr, uid, res_model, res_id, name_gdocs, google_template_id, context=context)
else:
google_document = pool_ir_attachment.create_empty_google_doc(cr, uid, res_model, res_id, context=context)
return google_document
class config(osv.osv):
_name = 'google.docs.config'
_description = "Google Docs templates config"
_columns = {
'model_id': fields.many2one('ir.model', 'Model'),
'gdocs_resource_id': fields.char('Google resource ID', size=64,help='''
This is the id of the template document, on google side. You can find it thanks to its URL:
*for a text document with url like `https://docs.google.com/a/openerp.com/document/d/123456789/edit`, the ID is `document:123456789`
*for a spreadsheet document with url like `https://docs.google.com/a/openerp.com/spreadsheet/ccc?key=123456789#gid=0`, the ID is `spreadsheet:123456789`
*for a presentation (slide show) document with url like `https://docs.google.com/a/openerp.com/presentation/d/123456789/edit#slide=id.p`, the ID is `presentation:123456789`
*for a drawing document with url like `https://docs.google.com/a/openerp.com/drawings/d/123456789/edit`, the ID is `drawings:123456789`
...
'''),
'name_template': fields.char('GDoc name template ', size=64, help='This is the name which appears on google side'),
}
_defaults = {
'name_template': 'gdoc_%(name)s',
}
config()

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!-- add google docs config field in user form -->
<record model="ir.ui.view" id="view_google_docs_config_tree">
<field name="name">google_docs.config.tree</field>
<field name="model">google.docs.config</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<group name="default_filters" position="inside">
<field name="model_id"/>
</group>
</field>
</record>
<record model="ir.ui.view" id="view_google_docs_config_form">
<field name="name">google_docs.config.form</field>
<field name="model">google.docs.config</field>
<field name="type">form</field>
<field name="arch" type="xml">
<group colspan='4'>
<field name="model_id"/>
<field name='gdocs_resource_id'/>
<field name='name_template'/>
</group>
</field>
</record>
<record model='ir.actions.act_window' id='action_google_docs_users_config'>
<field name='name'>Models configuration</field>
<field name='res_model'>google.docs.config</field>
<field name='type'>ir.actions.act_window</field>
<field name='view_type'>form</field>
<field name='view_id' ref='view_google_docs_config_tree'/>
</record>
<menuitem name='Google Docs configuration' id='menu_gdocs_config' parent='base.menu_administration'/>
<menuitem name='Models configuration' id='menu_gdocs_model_config' parent='menu_gdocs_config' action='action_google_docs_users_config'/>
</data>
</openerp>

View File

@ -0,0 +1,41 @@
openerp.google_docs = function(instance, m) {
var _t = instance.web._t;
instance.web.Sidebar = instance.web.Sidebar.extend({
on_attachments_loaded: function(attachments) {
self = this
self._super(attachments);
// if attachment contains a google doc url do nothing
// else display a button to create a google doc
var flag = false;
_.each(attachments, function(i) {
if (i.url && i.url.match('/docs.google.com/')) { flag = true; }
});
if (! flag) {
this.add_items('files', [
{ label: _t('Google Doc'), callback: self.on_google_doc },
]);
}
},
on_google_doc: function() {
var self = this;
var form = self.getParent();
form.sidebar_context().then(function (context) {
var ds = new instance.web.DataSet(this, 'ir.attachment', context);
ds.call('google_doc_get', [form.dataset.model, [form.datarecord.id], context], function(r) {
if (r == 'False') {
var params = {
error: response,
message: _t("The user google credentials are not set yet. Contact your administrator for help.")
}
$(openerp.web.qweb.render("DialogWarning", params)).dialog({
title: _t("User Google credentials are not yet set."),
modal: true,
});
}
form.reload();
});
})
}
})
}

File diff suppressed because one or more lines are too long

View File

@ -10,7 +10,7 @@
</table>
<div class="oe_mail_wall_left">
<div class="oe_mail_wall_act">
<textarea class="oe_mail oe_mail_wall_action_textarea" placeholder="Add a personnal message here..."/>
<textarea class="oe_mail oe_mail_wall_action_textarea" placeholder="What are you working on?"/>
<button class="oe_right oe_mail_wall_button_comment" type="button">Post comment</button>
</div>
<div class="oe_clear"></div>

View File

@ -70,7 +70,7 @@ Dashboard for project members that includes:
'auto_install': False,
'application': True,
'css': ['static/src/css/project.css'],
'js': ['static/src/js/dropdown.js','static/src/js/project.js'],
'js': ['static/src/js/project.js'],
'certificate': '0075116868317',
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -188,61 +188,39 @@
<field name="task_count"/>
<templates>
<t t-name="kanban-box">
<div class="project_vignettes">
<li t-attf-class="#{kanban_color(record.color.raw_value)} oe_project_kanban_vignette" id="oe_project_kanban_vignette">
<a href="#" class="oe_project_kanban_action dropdown-toggle"><span class="oe_i">B</span></a>
<ul class="dropdown-menu">
<li ><a type="edit" >Edit...</a></li>
<li ><a type="delete">Delete</a></li>
<li>
<ul class="color-chooser">
<li><a t-att-id="record.id.value" class="bgcolor"><span class="oe_kanban_color_0 square"></span></a></li>
<li><a t-att-id="record.id.value" class="bgcolor"><span class="oe_kanban_color_1 square"></span></a></li>
<li><a t-att-id="record.id.value" class="bgcolor"><span class="oe_kanban_color_2 square"></span></a></li>
<li><a t-att-id="record.id.value" class="bgcolor"><span class="oe_kanban_color_3 square"></span></a></li>
<li><a t-att-id="record.id.value" class="bgcolor"><span class="oe_kanban_color_4 square"></span></a></li>
<li><a t-att-id="record.id.value" class="bgcolor"><span class="oe_kanban_color_5 square"></span></a></li>
</ul>
</li>
</ul>
<h4><t t-esc="record.name.value.substr(0,33)"/><t t-if="record.name.value.length > 33">...</t></h4>
<div id="list">
<a t-if="record.use_tasks.raw_value" class="oe_project_buttons"
id="1" name="%(act_project_project_2_project_task_all)d" type="action">
<div t-attf-class="oe_kanban_color_#{kanban_getcolor(record.color.raw_value)} oe_kanban_card oe_kanban_project oe_kanban_auto_height">
<a class="oe_kanban_menuaction oe_i">B</a>
<ul class="oe_kanban_menu">
<li><a type="edit">Edit...</a></li>
<li><a type="delete">Delete</a></li>
<li><ul class="oe_kanban_colorpicker" data-field="color"/></li>
</ul>
<div class="oe_kanban_content">
<a type="edit"><h3 class="oe_kanban_ellipsis"><field name="name"/></h3></a>
<div class="oe_kanban_project_list">
<a t-if="record.use_tasks.raw_value"
name="%(act_project_project_2_project_task_all)d" type="action">
Tasks(<field name="task_count"/>)</a>
</div>
<br/>
<button class="click_button" name="dummy" type="object">
<table class="project_fields">
<tr id="deadline" t-if="record.date.raw_value">
<th align="left">Deadline</th>
<td align="left"><field name="date"/></td>
</tr>
<tr>
<th align="left" width="70px">Progress</th>
<td align="left">
<t t-esc="Math.round(record.effective_hours.raw_value)"/> / <t t-esc="Math.round(record.planned_hours.raw_value)"/> <field name="company_uom_id"/>
</td>
</tr>
</table>
<br/>
<div class="oe_kanban_project_fields oe_kanban_project_deadline" t-if="record.date.raw_value">
<div>Deadline</div>
<div><field name="date"/></div>
</div>
<div class="oe_kanban_project_fields oe_kanban_project_progress">
<div>Progress</div>
<div><t t-esc="Math.round(record.effective_hours.raw_value)"/> / <t t-esc="Math.round(record.planned_hours.raw_value)"/> <field name="company_uom_id"/></div>
</div>
<div class="oe_kanban_project_avatars">
<t t-foreach="record.members.raw_value" t-as="member">
<img t-att-src="kanban_image('res.users', 'avatar', member)" t-att-id="member" class="project_avatar"/>
<img t-att-src="kanban_image('res.users', 'avatar', member)" t-att-data-member_id="member"/>
</t>
</button>
</li>
</div>
</div>
</div>
<script type="text/javascript">
//open dropdwon when click on the icon.
$('.dropdown-toggle').dropdown();
//show and hide the dropdown icon when mouseover and mouseour.
$('.oe_project_kanban_vignette').mouseover(function() {
return $(this).find('.oe_project_kanban_action').show();
}).mouseout(function() {
return $(this).find('.oe_project_kanban_action').hide();
});
</script>
</t>
</templates>
</kanban>
@ -268,12 +246,16 @@
<field name="view_id" ref="view_project_kanban"/>
<field name="search_view_id" ref="view_project_project_filter"/>
<field name="context">{}</field>
<field name="help" type="xml"><p>You have no projects.</p>
<field name="help" type="xml">
<p>Click <i>'Create'</i> to <b>start a new project</b>.</p>
<p>Projects are used to organize your activities; plan tasks,
track issues, invoice timesheets. You can define internal
projects (R&amp;D, Improve Sales Process), private projects (My
Todos) or customer ones.</p>
<p>Click <i>'create'</i> to start a new project.</p>
<p>
You will be able collaborate with internal users on
projects or invite customers to share your activities.
</p>
</field>
</record>

View File

@ -1,246 +1,41 @@
.project_fields {
margin-top: 1px;
margin-bottom: 1px;
font-size: 11px;
padding-left: 0px;
.oe_kanban_project {
width: 300px;
}
.project_fields td {
border: none;
padding: 2px 0 2px 8px;
.oe_kanban_project_list {
margin: 8px 0 8px 0;
}
.project_fields th {
padding: 0;
/*.oe_kanban_project_list a:not(:first-child):before {
content: ' - ';
}*/
.oe_kanban_project_fields div {
display: inline-block;
width: 49%;
box-sizing: border-box;
padding: 2px 0 2px 0;
}
.oe_kanban_project_fields div:nth-child(odd) {
border-right: 1px solid #dddddd;
vertical-align: top;
margin-right: 8px;
padding-right: 8px;
}
.project_vignettes{
padding: 4px !important;
.oe_kanban_project_fields div:nth-child(even) {
padding-left: 8px;
color: #888888;
}
.project_vignettes li {
float: left;
.oe_kanban_project_avatars {
padding-top: 1em;
margin-top: 8px;
}
.project_vignettes .project_avatar {
.oe_kanban_project_avatars img {
width: 30px;
height: 30px;
padding-left: 0px;
}
.project_vignettes .project_fields {
width: 100%;
}
.project_vignettes .project_fields th {
width: 120px;
font-weight: normal;
}
.project_vignettes .project_fields td {
color: #888888;
}
.project_vignettes h4 a {
color: #4c4c4c;
}
.project_vignettes > li h4 {
margin-bottom: 2px;
padding-left: 2px;
}
.oe_project_buttons {
padding: 2px 2px !important;
background: none !important;
background-color: transparent !important;
border: hidden !important;
color: #8A89BA !important;
}
.oe_project_buttons:hover {
cursor: pointer;
color: #0000FF;
}
.project_avatar {
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
border-radius: 4px;
-moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}
.click_button {
background: none !important;
background-color: transparent !important;
border: hidden !important;
min-height: 155px;
min-width: 265px;
border: hidden;
/*margin-left: 6px !important;*/
margin-top: 3px !important;
text-align: left;
vertical-align: super;
font-size: 11px;
-webkit-box-align: baseline;
-moz-border-radius: 2px;
-webkit-border-radius: 2px;
border-radius: 2px;
-moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}
.click_button:hover {
cursor: default !important;
}
.dropdown-menu {
display: none;
position: absolute;
}
.dropdown {
position: relative;
}
.dropdown-toggle:after {
width: 0;
height: 0;
display: inline-block;
content: "&darr";
text-indent: -99999px;
vertical-align: top;
margin-top: 8px;
margin-left: 4px;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 4px solid white;
filter: alpha(opacity=50);
-khtml-opacity: 0.5;
-moz-opacity: 0.5;
opacity: 0.5;
}
.oe_project_kanban_vignette .dropdown-menu .color-chooser {
padding: 0 3px;
}
.oe_project_kanban_vignette .dropdown-menu .color-chooser li {
float: left;
}
.oe_project_kanban_vignette .dropdown-menu .color-chooser li a {
padding: 2px;
}
a.oe_project_kanban_action {
position: absolute;
right: 0;
display: none;
}
a.oe_project_kanban_action:hover {
text-decoration: none;
}
a.oe_project_kanban_action .oe_i {
color: #4c4c4c;
}
.square {
display: inline-block;
width: 18px;
height: 18px;
border:1px solid grey;
}
.oe_kanban_color_0 {
background: white;
}
.oe_kanban_color_1 {
background: #B1DCFE;
}
.oe_kanban_color_2 {
background: #FF8E8E;
}
.oe_kanban_color_3 {
background: khaki;
}
.oe_kanban_color_4 {
background: thistle;
}
.oe_kanban_color_5 {
background: orange;
}
.open {
display: block;
}
.open .dropdown-menu {
display: block;
}
a.oe_project_kanban_action {
position: absolute;
right: 0;
}
a.oe_project_kanban_action:hover {
text-decoration: none;
}
a.oe_project_kanban_action .eo_i {
color: #4c4c4c;
}
.oe_project_kanban_vignette {
position: relative;
min-height: 50px;
/*background: white;*/
border: 1px solid #d8d8d8;
border-bottom-color: #b9b9b9;
padding: 6px;
margin: 6px 0;
display: inline-block;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
border-radius: 4px;
}
.oe_project_kanban_vignette:last-child {
margin-bottom: 0;
}
.oe_project_kanban_vignette:hover {
-moz-box-shadow: 0 0 3px rgba(0, 0, 0, 0.6);
-webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.6);
-box-shadow: 0 0 3px rgba(0, 0, 0, 0.6);
}
.oe_project_kanban_vignette h4 {
margin: 0 0 2px;
}
.oe_project_kanban_vignette .dropdown-menu {
top: 30px;
right: -140px;
padding: 4px;
border: 1px solid #afafb6;
width: 160px;
overflow-x: hidden;
z-index: 900;
background: white;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
border-radius: 3px;
-moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
-webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
}
.oe_project_kanban_vignette .dropdown-menu p {
margin-left: 12px;
}
.oe_project_kanban_vignette .dropdown-menu li {
float: none;
display: block;
background-color: none;
}
.oe_project_kanban_vignette .dropdown-menu li a {
display: block;
padding: 3px 6px;
clear: both;
font-weight: normal;
line-height: 14px;
color: #4c4c4c;
text-decoration: none;
}
.oe_project_kanban_vignette .dropdown-menu li a:hover {
background: #f0f0fa;
background: -moz-linear-gradient(#f0f0fa, #eeeef6);
background: -webkit-gradient(linear, left top, left bottom, from(#f0f0fa), to(#eeeef6));
background: -webkit-linear-gradient(#f0f0fa, #eeeef6);
-moz-box-shadow: none;
-webkit-box-shadow: none;
-box-shadow: none;
}
.oe_project_kanban_vignette .dropdown-menu .color-chooser {
padding: 0 3px;
}
.oe_project_kanban_vignette .dropdown-menu .color-chooser li {
float: left;
}
.oe_project_kanban_vignette .dropdown-menu .color-chooser li a {
padding: 2px;
}

View File

@ -1,92 +0,0 @@
/* ============================================================
* bootstrap-dropdown.js v2.0.2
* http://twitter.github.com/bootstrap/javascript.html#dropdowns
* ============================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ============================================================ */
!function( $ ){
"use strict"
/* DROPDOWN CLASS DEFINITION
* ========================= */
var toggle = '[data-toggle="dropdown"]'
, Dropdown = function ( element ) {
var $el = $(element).on('click.dropdown.data-api', this.toggle)
$('html').on('click.dropdown.data-api', function () {
$el.parent().removeClass('open')
})
}
Dropdown.prototype = {
constructor: Dropdown
, toggle: function ( e ) {
var $this = $(this)
, selector = $this.attr('data-target')
, $parent
, isActive
if (!selector) {
selector = $this.attr('href')
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
}
$parent = $(selector)
$parent.length || ($parent = $this.parent())
isActive = $parent.hasClass('open')
clearMenus()
!isActive && $parent.toggleClass('open')
return false
}
}
function clearMenus() {
$(toggle).parent().removeClass('open')
}
/* DROPDOWN PLUGIN DEFINITION
* ========================== */
$.fn.dropdown = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('dropdown')
if (!data) $this.data('dropdown', (data = new Dropdown(this)))
if (typeof option == 'string') data[option].call($this)
})
}
$.fn.dropdown.Constructor = Dropdown
/* APPLY TO STANDARD DROPDOWN ELEMENTS
* =================================== */
$(function () {
$('html').on('click.dropdown.data-api', clearMenus)
$('body').on('click.dropdown.data-api', toggle, Dropdown.prototype.toggle)
})
}( window.jQuery );

View File

@ -1,46 +1,25 @@
openerp.project = function(openerp) {
openerp.web_kanban.ProjectKanban = openerp.web_kanban.KanbanRecord.include({
bind_events: function() {
openerp.web_kanban.KanbanView.include({
on_groups_started: function() {
var self = this;
self._super();
if (this.view.dataset.model == 'project.project') {
/*set avatar title for members.
In many2many fields, returns only list of ids.
we can implement return value of m2m fields like [(1,"Adminstration"),...].
self._super.apply(this, arguments);
if (this.dataset.model === 'project.project') {
/* Set avatar title for members.
In many2many fields, returns only list of ids.
we can implement return value of m2m fields like [(1,"Adminstration"),...].
*/
_.each($(this.$element).find('.project_avatar'), function(avatar) {
var dataset = new openerp.web.DataSetSearch(this, 'res.users', self.session.context, [['id','=',avatar.id]]);
dataset.read_slice(['name']).then(function(result) {
avatar.setAttribute("title",result[0].name);
var members_ids = [];
this.$element.find('.oe_kanban_project_avatars img').each(function() {
members_ids.push($(this).data('member_id'));
});
var dataset = new openerp.web.DataSetSearch(this, 'res.users', self.session.context, [['id', 'in', _.uniq(members_ids)]]);
dataset.read_slice(['id', 'name']).then(function(result) {
_.each(result, function(v, k) {
self.$element.find('.oe_kanban_project_avatars img[data-member_id=' + v.id + ']').attr('title', v.name).tipsy({
offset: 10
});
});
});
// set sequence like Tasks,Issues,Timesheets and Phases
var my_list = $("#list a");
my_list.sort(function (a, b) {
var aValue = parseInt(a.id, 10);
var bValue = parseInt(b.id, 10);
return aValue == bValue ? 0 : aValue < bValue ? -1 : 1;
});
$('#list').replaceWith(my_list);
// when vignette is clicked, it opens the first action in sequence
if (my_list.length !== 0) {
var click_button = $(this.$element).find('.click_button');
click_button.attr('data-name', my_list[0].getAttribute('data-name'));
click_button.attr('data-type', "action");
}
/* set background color.
we can do other way to implement new widget.
because we need to rpc call for that.
*/
this.$element.find('.bgcolor').click(function() {
var color = parseInt($(this).find('span').attr('class').split(' ')[0].substring(16), 10);
var color_class = $(this).find('span').attr('class').split(' ')[0];
$(this).closest('#oe_project_kanban_vignette').removeClass().addClass(color_class + ' oe_project_kanban_vignette');
self.view.dataset.write(parseInt(this.id, 10), {color:color});
});
}
}
});

View File

@ -372,9 +372,9 @@
<field name="use_issues"/>
<field name="issue_count" invisible="1"/>
</field>
<xpath expr="//div[@id='list']" position="inside">
<a t-if="record.use_issues.raw_value" class="oe_project_buttons"
id="2" name="%(act_project_project_2_project_issue_all)d" type="action">
<xpath expr="//div[contains(@class, 'oe_kanban_project_list')]" position="inside">
<a t-if="record.use_issues.raw_value"
name="%(act_project_project_2_project_issue_all)d" type="action">
Issues(<field name="issue_count"/>)</a>
</xpath>
</field>

View File

@ -129,9 +129,9 @@
<field name="use_phases"/>
<field name="phase_count"/>
</field>
<xpath expr="//div[@id='list']" position="inside">
<a t-if="record.use_phases.raw_value" class="oe_project_buttons"
id="4" name="%(act_project_phases)d" type="action">Phases(<field name="phase_count"/>)</a>
<xpath expr="//div[contains(@class, 'oe_kanban_project_list')]" position="inside">
<a t-if="record.use_phases.raw_value"
name="%(act_project_phases)d" type="action">Phases(<field name="phase_count"/>)</a>
</xpath>
</field>
</record>

View File

@ -36,25 +36,17 @@
<field name="currency_id"/>
<field name="partner_id"/>
</field>
<xpath expr="//div[@id='list']" position="inside">
<a t-if="record.use_timesheets.raw_value" class="oe_project_buttons"
id="3" name="open_timesheets" type="object">Timesheets(<field name="timesheet_count"/>)</a>
<xpath expr="//div[contains(@class, 'oe_kanban_project_list')]" position="inside">
<a t-if="record.use_timesheets.raw_value"
name="open_timesheets" type="object">Timesheets(<field name="timesheet_count"/>)</a>
</xpath>
<xpath expr="//tr[@id='deadline']" position="before">
<t t-if="record.partner_id.raw_value">
<tr>
<th align="left">Amount to invoice</th>
<td align="left">
<field name="amount_to_invoice"/> <t t-esc="record.currency_id.raw_value[1].split(' ')[1][1]"/>
</td>
</tr>
<tr>
<th align="left">Time to Invoice</th>
<td align="left">
<field name="time_to_invoice"/> <field name="company_uom_id"/>
</td>
</tr>
</t>
<xpath expr="//div[contains(@class, 'oe_kanban_project_deadline')]" position="before">
<div class="oe_kanban_project_fields oe_kanban_project_invoice" t-if="record.partner_id.raw_value">
<div>Amount to invoice</div>
<div><field name="amount_to_invoice"/> <t t-esc="record.currency_id.raw_value[1].split(' ')[1][1]"/></div>
<div>Time to Invoice</div>
<div><field name="time_to_invoice"/> <field name="company_uom_id"/></div>
</div>
</xpath>
</field>
</record>

View File

@ -1015,7 +1015,7 @@ class sale_order_line(osv.osv):
help="If 'on order', it triggers a procurement when the sale order is confirmed to create a task, purchase order or manufacturing order linked to this sale order line."),
'property_ids': fields.many2many('mrp.property', 'sale_order_line_property_rel', 'order_id', 'property_id', 'Properties', readonly=True, states={'draft': [('readonly', False)]}),
'address_allotment_id': fields.many2one('res.partner', 'Allotment Partner'),
'product_uom_qty': fields.float('Quantity (Unit of Measure)', digits_compute= dp.get_precision('Product UoS'), required=True, readonly=True, states={'draft': [('readonly', False)]}),
'product_uom_qty': fields.float('Quantity', digits_compute= dp.get_precision('Product UoS'), required=True, readonly=True, states={'draft': [('readonly', False)]}),
'product_uom': fields.many2one('product.uom', 'Unit of Measure ', required=True, readonly=True, states={'draft': [('readonly', False)]}),
'product_uos_qty': fields.float('Quantity (UoS)' ,digits_compute= dp.get_precision('Product UoS'), readonly=True, states={'draft': [('readonly', False)]}),
'product_uos': fields.many2one('product.uom', 'Product UoS'),

View File

@ -268,6 +268,7 @@
<field name="supply_method">produce</field>
<field name="uom_id" ref="product.uom_day"/>
<field name="uom_po_id" ref="product.uom_day"/>
<field name="company_id" eval="[]"/>
</record>
<record id="base.user_demo" model="res.users">

View File

@ -199,9 +199,9 @@
</form>
<tree string="Sales Order Lines">
<field colspan="4" name="name"/>
<field name="product_uom_qty" string="Qty(Unit of Measure)"/>
<field name="product_uom_qty" string="Quantity"/>
<field name="product_uom" string="Unit of Measure" groups="product.group_uom"/>
<field groups="product.group_uos" name="product_uos_qty" string="Qty(UoS)"/>
<field groups="product.group_uos" name="product_uos_qty" string="Quantity (UoS)"/>
<field groups="product.group_uos" name="product_uos" string="UoS"/>
<field name="discount" groups="sale.group_discount_per_so_line"/>
<field name="price_unit"/>

View File

@ -20,20 +20,42 @@
from osv import fields, osv
from tools.translate import _
import decimal_precision as dp
class sale_advance_payment_inv(osv.osv_memory):
_name = "sale.advance.payment.inv"
_description = "Sales Advance Payment Invoice"
def _default_product_id(self, cr, uid, context=None):
try:
product_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'sale', 'advance_product_0')
except ValueError:
#a ValueError is returned if the xml id given is not found in the table ir_model_data
return False
return product_id[1]
_columns = {
'product_id': fields.many2one('product.product', 'Advance Product', required=True,
help="Select a product of type service which is called 'Advance Product'. You may have to create it and set it as a default value on this field."),
'amount': fields.float('Advance Amount', digits=(16, 2), required=True, help="The amount to be invoiced in advance."),
'product_id': fields.many2one('product.product', 'Advance Product', help="Select a product of type service which is called 'Advance Product'. You may have to create it and set it as a default value on this field."),
'amount': fields.float('Advance Amount', digits_compute= dp.get_precision('Sale Price'), required=True, help="The amount to be invoiced in advance."),
'qtty': fields.float('Quantity', digits=(16, 2), required=True),
'advance_payment_method':fields.selection([('percentage','Percentage'), ('fixed','Fixed Price')], 'Type', required=True, help="Use Fixed Price if you want to give specific amound in Advance, Use Percentage if you want to give percentage of Total Invoice Amount."),
}
_defaults = {
'qtty': 1.0
'qtty': 1.0,
'advance_payment_method': 'fixed',
'product_id': _default_product_id,
}
def onchange_advance_payment_method(self, cr, uid, ids, advance_payment_method, product_id, context=None):
if advance_payment_method == 'percentage':
return {'value': {'amount':0, 'product_id':False }}
if not product_id:
return {'value': {'amount': 0}}
product = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
return {'value': {'amount': product.list_price}}
def create_invoices(self, cr, uid, ids, context=None):
"""
To create invoices.
@ -66,21 +88,55 @@ class sale_advance_payment_inv(osv.osv_memory):
val = obj_lines.product_id_change(cr, uid, [], sale_adv_obj.product_id.id,
uom = False, partner_id = sale.partner_id.id, fposition_id = sale.fiscal_position.id)
res = val['value']
if not sale_adv_obj.product_id.id :
prop = self.pool.get('ir.property').get(cr, uid,
'property_account_income_categ', 'product.category',
context=context)
account_id = prop and prop.id or False
account_id = self.pool.get('account.fiscal.position').map_account(cr, uid, sale.fiscal_position.id or False, account_id)
if not account_id:
raise osv.except_osv(_('Configuration Error !'),
_('There is no income account defined as global property.'))
res['account_id'] = account_id
if not res.get('account_id'):
raise osv.except_osv(_('Configuration Error !'),
_('There is no income account defined ' \
'for this product: "%s" (id:%d)') % \
(sale_adv_obj.product_id.name, sale_adv_obj.product_id.id,))
final_amount = 0
if sale_adv_obj.amount <= 0.00:
raise osv.except_osv(_('Data Insufficient !'),
_('Please check the Advance Amount, it should not be 0 or less!'))
if sale_adv_obj.advance_payment_method == 'percentage':
final_amount = sale.amount_total * sale_adv_obj.amount / 100
if not res.get('name'):
res['name'] = _("Advance of %s %%") % (sale_adv_obj.amount)
else:
final_amount = sale_adv_obj.amount
if not res.get('name'):
#TODO: should find a way to call formatLang() from rml_parse
if sale.pricelist_id.currency_id.position == 'after':
res['name'] = _("Advance of %s %s") % (final_amount, sale.pricelist_id.currency_id.symbol)
else:
res['name'] = _("Advance of %s %s") % (sale.pricelist_id.currency_id.symbol, final_amount)
if res.get('invoice_line_tax_id'):
res['invoice_line_tax_id'] = [(6, 0, res.get('invoice_line_tax_id'))]
else:
res['invoice_line_tax_id'] = False
line_id = obj_lines.create(cr, uid, {
'name': res.get('name'),
'account_id': res['account_id'],
'price_unit': sale_adv_obj.amount,
'quantity': sale_adv_obj.qtty,
'price_unit': final_amount,
'quantity': sale_adv_obj.qtty or 1.0,
'discount': False,
'uos_id': res.get('uos_id'),
'uos_id': res.get('uos_id', False),
'product_id': sale_adv_obj.product_id.id,
'invoice_line_tax_id': [(6, 0, res.get('invoice_line_tax_id'))],
'invoice_line_tax_id': res.get('invoice_line_tax_id'),
'account_analytic_id': sale.project_id.id or False,
#'note':'',
})
@ -112,49 +168,27 @@ class sale_advance_payment_inv(osv.osv_memory):
# If not, the advance will be deduced when generating the final invoice
#
if sale.order_policy == 'picking':
self.pool.get('sale.order.line').create(cr, uid, {
vals = {
'order_id': sale.id,
'name': res.get('name'),
'price_unit': -sale_adv_obj.amount,
'product_uom_qty': sale_adv_obj.qtty,
'product_uos_qty': sale_adv_obj.qtty,
'product_uos': res.get('uos_id'),
'product_uom': res.get('uos_id'),
'product_id': sale_adv_obj.product_id.id,
'price_unit': -final_amount,
'product_uom_qty': sale_adv_obj.qtty or 1.0,
'product_uos_qty': sale_adv_obj.qtty or 1.0,
'product_uos': res.get('uos_id', False),
'product_uom': res.get('uom_id', False),
'product_id': sale_adv_obj.product_id.id or False,
'discount': False,
'tax_id': [(6, 0, res.get('invoice_line_tax_id'))],
}, context)
'tax_id': res.get('invoice_line_tax_id'),
}
self.pool.get('sale.order.line').create(cr, uid, vals, context=context)
context.update({'invoice_id':list_inv})
return {
'name': 'Open Invoice',
'view_type': 'form',
'view_mode': 'form',
'res_model': 'sale.open.invoice',
'type': 'ir.actions.act_window',
'target': 'new',
'context': context
}
if context.get('open_invoices'):
return self.open_invoices( cr, uid, ids, context=context)
return {'type': 'ir.actions.act_window_close'}
sale_advance_payment_inv()
class sale_open_invoice(osv.osv_memory):
_name = "sale.open.invoice"
_description = "Sales Open Invoice"
def open_invoice(self, cr, uid, ids, context=None):
"""
To open invoice.
@param self: The object pointer.
@param cr: A database cursor
@param uid: ID of the user currently logged in
@param ids: the ID or list of IDs if we want more than one
@param context: A standard dictionary
@return:
"""
def open_invoices(self, cr, uid, ids, context=None):
if context is None:
context = {}
mod_obj = self.pool.get('ir.model.data')
@ -176,6 +210,6 @@ class sale_open_invoice(osv.osv_memory):
'type': 'ir.actions.act_window',
}
sale_open_invoice()
sale_advance_payment_inv()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -7,15 +7,19 @@
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Advance Invoice">
<field name="product_id"/>
<field name="advance_payment_method" on_change="onchange_advance_payment_method(advance_payment_method,product_id)"/>
<newline />
<field name="qtty" invisible="1"/>
<field name="amount"/>
<field name="product_id" on_change="onchange_advance_payment_method(advance_payment_method,product_id)" context="{'search_default_services':1}" attrs="{'invisible': [('advance_payment_method','=','percentage')]}" colspan="2"/>
<field name="amount" colspan="2"/>
<newline />
<separator string="" colspan="4"/>
<label string="" colspan="2" />
<button special="cancel" string="Cancel" icon="gtk-cancel"/>
<button name="create_invoices" string="Create Invoice" type="object" icon="gtk-go-forward"/>
<label string="" colspan="6" />
<group colspan="4">
<button special="cancel" string="Cancel" icon="gtk-cancel"/>
<button name="create_invoices" string="Create Invoice" type="object" icon="gtk-go-forward" context="{'open_invoices': False}"/>
<button name="create_invoices" string="Create and view Invoice" type="object" icon="terp-camera_test" context="{'open_invoices': True}" />
</group>
</form>
</field>
</record>
@ -28,31 +32,5 @@
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
<record id="view_sale_open_invoice" model="ir.ui.view">
<field name="name">Open Invoice</field>
<field name="model">sale.open.invoice</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Invoices">
<label string="You invoice has been successfully created!" />
<newline />
<separator string="" colspan="4"/>
<group colspan="4">
<button special="cancel" string="Close" icon="gtk-cancel"/>
<button name="open_invoice" string="Open Invoice" type="object" icon="gtk-go-forward"/>
</group>
</form>
</field>
</record>
<record id="action_view_sale_open_invoice" model="ir.actions.act_window">
<field name="name">Open Invoice</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">sale.open.invoice</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</data>
</openerp>