[MERGE] Merged trunk.

bzr revid: jra@tinyerp.com-20120920080935-2y2cfc18e65g7a4y
This commit is contained in:
Jiten (OpenERP)
2012-09-20 13:39:35 +05:30
112 changed files with 3263 additions and 1030 deletions
+1 -1
View File
@@ -304,7 +304,7 @@ class account_bank_statement(osv.osv):
'date': st_line.date,
'ref': st_line.ref,
'move_id': move_id,
'partner_id': partner_id,
'partner_id': par_id,
'account_id': acc_id,
'credit': credit,
'debit': debit,
+1 -11
View File
@@ -252,16 +252,6 @@
</tree>
</field>
</record>
<record id="view_treasory_graph" model="ir.ui.view">
<field name="name">account.treasury.graph</field>
<field name="model">account.account</field>
<field name="arch" type="xml">
<graph string="Treasury Analysis" type="bar">
<field name="name"/>
<field name="balance" operator="+"/>
</graph>
</field>
</record>
<record id="action_account_form" model="ir.actions.act_window">
<field name="name">Accounts</field>
<field name="res_model">account.account</field>
@@ -2537,7 +2527,7 @@ action = pool.get('res.config').next(cr, uid, [], context)
<header>
<button name="button_confirm_cash" states="open" string="Close CashBox" type="object" class="oe_highlight"/>
<button name="button_open" states="draft" string="Open CashBox" type="object" class="oe_highlight"/>
<button name="button_cancel" states="confirm,open" string="Cancel" type="object" groups="base.group_extended"/>
<button name="button_cancel" states="confirm,open" string="Cancel" type="object"/>
<field name="state" widget="statusbar" nolabel="1" statusbar_visible="draft,confirm"/>
</header>
<sheet string="Statement">
-13
View File
@@ -12,16 +12,6 @@
<field name="domain">[('year','=',time.strftime('%Y'))]</field>
</record>
<record id="action_treasory_graph" model="ir.actions.act_window">
<field name="name">Treasury</field>
<field name="res_model">account.account</field>
<field name="view_type">form</field>
<field name="view_mode">graph,tree</field>
<field name="domain">[('type','=','liquidity')]</field>
<field name="context">{'default_type': 'liquidity'}</field>
<field name="view_id" ref="account.view_treasory_graph"/>
</record>
<record id="board_account_form" model="ir.ui.view">
<field name="name">board.account.form</field>
<field name="model">board.board</field>
@@ -31,9 +21,6 @@
<column>
<action name="%(action_company_analysis_tree)d" string="Company Analysis"/>
</column>
<column>
<action name="%(action_treasory_graph)d" string="Treasury"/>
</column>
</board>
</form>
</field>
@@ -92,14 +92,14 @@ class account_move_journal(osv.osv_memory):
journal = False
if journal_id:
journal = journal_pool.read(cr, uid, [journal_id], ['name'])[0]['name']
journal = journal_pool.read(cr, uid, journal_id, ['name'], context=context).name
journal_string = _("Journal: %s") % tools.ustr(journal)
else:
journal_string = _("Journal: All")
period = False
if period_id:
period = period_pool.browse(cr, uid, [period_id], ['name'])[0]['name']
period = period_pool.browse(cr, uid, period_id, context=context).name
period_string = _("Period: %s") % tools.ustr(period)
open_string = _("Open")
+9 -2
View File
@@ -64,11 +64,13 @@ class ir_model_fields_anonymization(osv.osv):
state = 'anonymized' # all fields are anonymized
else:
state = 'unstable' # fields are mixed: this should be fixed
return state
def _check_write(self, cr, uid, context=None):
# check that the field is created from the menu and not from an database update
# otherwise the database update can crash:
if context.get('manual'):
global_state = self._get_global_state(cr, uid, context=context)
if global_state == 'anonymized':
@@ -100,12 +102,16 @@ class ir_model_fields_anonymization(osv.osv):
# check field state: all should be clear before we can add a new field to anonymize:
self._check_write(cr, uid, context=context)
global_state = self._get_global_state(cr, uid, context=context)
if 'field_name' in vals and vals['field_name'] and 'model_name' in vals and vals['model_name']:
vals['model_id'], vals['field_id'] = self._get_model_and_field_ids(cr, uid, vals, context=context)
# check not existing fields:
if not vals.get('field_id'):
vals['state'] = 'not_existing'
else:
vals['state'] = global_state
res = super(ir_model_fields_anonymization, self).create(cr, uid, vals, context=context)
@@ -340,8 +346,9 @@ class ir_model_fields_anonymize_wizard(osv.osv_memory):
# remove the placeholer:
eview.remove(placeholder)
else:
# unstable ?
raise
msg = "The database anonymization is currently in an unstable state. Some fields are anonymized," + \
" while some fields are not anonymized. You should try to solve this problem before trying to do anything else."
raise osv.except_osv('Error !', msg)
res['arch'] = etree.tostring(eview)
+1 -1
View File
@@ -13,7 +13,7 @@
<field name="groups_id" eval="[(5,)]"/>
</record>
<record id="default_template_user" model="ir.config_parameter">
<record id="default_template_user_config" model="ir.config_parameter">
<field name="key">auth_signup.template_user_id</field>
<field name="value" ref="default_template_user"/>
</record>
+2 -1
View File
@@ -20,7 +20,7 @@
##############################################################################
{
'name': 'Calendar Layer',
'name': 'Calendar',
'version': '1.0',
'depends': ['base', 'base_status', 'mail', 'base_action_rule'],
'description': """
@@ -51,6 +51,7 @@ If you need to manage your meetings, you should install the CRM module.
],
'test' : ['test/base_calendar_test.yml'],
'installable': True,
'application': True,
'auto_install': False,
'certificate': '00694071962960352821',
'images': ['images/base_calendar1.jpeg','images/base_calendar2.jpeg','images/base_calendar3.jpeg','images/base_calendar4.jpeg',],
+4 -1
View File
@@ -1431,7 +1431,10 @@ rule or repeating pattern of time to exclude from the recurring rule."),
if r['class']=='private':
for f in r.keys():
if f not in ('id','date','date_deadline','duration','user_id','state'):
r[f] = False
if isinstance(r[f], list):
r[f] = []
else:
r[f] = False
if f=='name':
r[f] = _('Busy')
+2 -1
View File
@@ -418,7 +418,7 @@
<field name="type">ir.actions.act_window</field>
<field name="res_model">calendar.event</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form,calendar</field>
<field name="view_mode">calendar,tree,form</field>
<field name="search_view_id" ref="view_calendar_event_filter"/>
</record>
@@ -428,5 +428,6 @@
name="Events" parent="base.menu_calendar_configuration"
sequence="15" action="action_view_event"/>
<menuitem name="Agenda" id="mail_menu_agenda" parent="mail.mail_my_stuff" sequence="10" action="action_view_event"/>
</data>
</openerp>
+6 -1
View File
@@ -53,7 +53,7 @@ class crm_meeting(base_state, osv.Model):
'partner_ids': fields.many2many('res.partner', 'crm_meeting_partner_rel', 'meeting_id','partner_id',
string='Attendees', states={'done': [('readonly', True)]}),
'state': fields.selection(
[('draft', 'Unconfirmed'), ('open', 'Confirmed'), ('cancel', 'Cancelled'), ('done', 'Done')],
[('draft', 'Unconfirmed'), ('open', 'Confirmed')],
string='Status', size=16, readonly=True),
# Meeting fields
'name': fields.char('Meeting Subject', size=128, required=True, states={'done': [('readonly', True)]}),
@@ -66,6 +66,11 @@ class crm_meeting(base_state, osv.Model):
'state': 'open',
}
def copy(self, cr, uid, id, default=None, context=None):
default = default or {}
default['attendee_ids'] = False
return super(crm_meeting, self).copy(cr, uid, id, default, context)
# ----------------------------------------
# OpenChatter
# ----------------------------------------
+1 -3
View File
@@ -69,9 +69,7 @@
<field name="model">crm.meeting</field>
<field name="arch" type="xml">
<form string="Meetings" version="7.0">
<header>
<field name="state" invisible="True"/>
</header>
<field name="state" invisible="True"/>
<sheet>
<div class="oe_title">
<div class="oe_edit_only">
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

+2 -5
View File
@@ -9,14 +9,11 @@
<field name="inherit_id" ref="base.res_partner_kanban_view"/>
<field name="arch" type="xml">
<xpath expr="//div[@class='oe_kanban_partner_categories']" position="inside">
<span class="oe_kanban_list_many2many" data-model="res.partner.category">
<t t-foreach="record.category_id.raw_value" t-as="category">
<span class="oe_tag" t-att-data-list_id="category"/>
</t>
<span class="oe_kanban_list_many2many">
<field name="category_id"/>
</span>
</xpath>
</field>
</record>
</data>
</openerp>
+15 -5
View File
@@ -69,7 +69,7 @@ class base_stage(object):
return False
return uid
def onchange_partner_address_id(self, cr, uid, ids, add, email=False):
def onchange_partner_address_id(self, cr, uid, ids, add, email=False, context=None):
""" This function returns value of partner email based on Partner Address
:param add: Id of Partner's address
:param email: Partner's email ID
@@ -77,10 +77,20 @@ class base_stage(object):
data = {'value': {'email_from': False, 'phone':False}}
if add:
address = self.pool.get('res.partner').browse(cr, uid, add)
data['value'] = {'email_from': address and address.email or False ,
'phone': address and address.phone or False}
if 'phone' not in self._columns:
del data['value']['phone']
data['value'] = {'partner_name': address and address.name or False,
'email_from': address and address.email or False,
'phone': address and address.phone or False,
'street': address and address.street or False,
'street2': address and address.street2 or False,
'city': address and address.city or False,
'state_id': address.state_id and address.state_id.id or False,
'zip': address and address.zip or False,
'country_id': address.country_id and address.country_id.id or False,
}
fields = self.fields_get(cr, uid, context=context or {})
for key in data['value'].keys():
if key not in fields:
del data['value'][key]
return data
def onchange_partner_id(self, cr, uid, ids, part, email=False):
+33
View File
@@ -1,3 +1,36 @@
.openerp .oe_dashboard_layout_1 .oe_dashboard_column.index_0 {
width: 100%;
}
.openerp .oe_dashboard_layout_1 .oe_dashboard_column.index_1, .openerp .oe_dashboard_layout_1 .oe_dashboard_column.index_2 {
display: none;
}
.openerp .oe_dashboard_layout_1-1 .oe_dashboard_column {
width: 50%;
}
.openerp .oe_dashboard_layout_1-1 .oe_dashboard_column.index_2 {
display: none;
}
.openerp .oe_dashboard_layout_1-1-1 .oe_dashboard_column {
width: 33%;
}
.openerp .oe_dashboard_layout_2-1 .oe_dashboard_column.index_0 {
width: 70%;
}
.openerp .oe_dashboard_layout_2-1 .oe_dashboard_column.index_1 {
width: 30%;
}
.openerp .oe_dashboard_layout_2-1 .oe_dashboard_column.index_2 {
display: none;
}
.openerp .oe_dashboard_layout_1-2 .oe_dashboard_column.index_0 {
width: 30%;
}
.openerp .oe_dashboard_layout_1-2 .oe_dashboard_column.index_1 {
width: 70%;
}
.openerp .oe_dashboard_layout_1-2 .oe_dashboard_column.index_2 {
display: none;
}
.openerp .oe_dashboard_layout_selector ul {
white-space: nowrap;
}
@@ -9,6 +9,31 @@
box-shadow: $bsval
.openerp
.oe_dashboard_layout_1 .oe_dashboard_column
&.index_0
width: 100%
&.index_1, &.index_2
display: none
.oe_dashboard_layout_1-1 .oe_dashboard_column
width: 50%
&.index_2
display: none
.oe_dashboard_layout_1-1-1 .oe_dashboard_column
width: 33%
.oe_dashboard_layout_2-1 .oe_dashboard_column
&.index_0
width: 70%
&.index_1
width: 30%
&.index_2
display: none
.oe_dashboard_layout_1-2 .oe_dashboard_column
&.index_0
width: 30%
&.index_1
width: 70%
&.index_2
display: none
.oe_dashboard_layout_selector
ul
white-space: nowrap
@@ -98,3 +123,4 @@
> tbody
tr:nth-child(odd)
background: transparent
+4 -2
View File
@@ -24,6 +24,10 @@ instance.web.form.DashBoard = instance.web.form.FormWidget.extend({
scroll: false
}).bind('sortstop', self.do_save_dashboard);
var old_title = this.__parentedParent.get('title');
this.__parentedParent.on_record_loaded.add_last(function(){
self.__parentedParent.set({ 'title' : old_title});
});
// Events
this.$el.find('.oe_dashboard_link_reset').click(this.on_reset);
this.$el.find('.oe_dashboard_link_change_layout').click(this.on_change_layout);
@@ -165,8 +169,6 @@ instance.web.form.DashBoard = instance.web.form.FormWidget.extend({
this.rpc('/web/view/add_custom', {
view_id: this.view.fields_view.view_id,
arch: arch
}, function() {
self.$el.find('.oe_dashboard_link_reset').show();
});
},
on_load_action: function(result, index, action_attrs) {
+1 -1
View File
@@ -1,7 +1,7 @@
<template>
<t t-name="DashBoard">
<div class="oe_dashboard_links">
<button type="button" class="button oe_dashboard_link_reset" title="Reset Layout.." t-att-style="view.fields_view.custom_view_id ? null : 'display: none'">
<button type="button" class="button oe_dashboard_link_reset" title="Reset Layout.." t-att-style="view.fields_view.custom_view_id || session.debug ? null : 'display: none'">
<img src="/board/static/src/img/layout_2-1.png" width="16" height="16"/>
<span> Reset </span>
</button>
+3 -1
View File
@@ -30,7 +30,9 @@ Create a claim from a delivery order.
Adds a Claim link to the delivery order.
""",
'data' : ['claim_delivery_view.xml'],
'data' : [
'claim_delivery_view.xml',
'claim_delivery_data.xml',],
'auto_install': False,
'installable': True,
'certificate' : '001101649349223746957',
@@ -0,0 +1,11 @@
<?xml version="1.0"?>
<openerp>
<data>
<record model="res.request.link" id="request_link_claim_from_delivery">
<field name="name">Delivery Order</field>
<field name="object">stock.picking.out</field>
</record>
</data>
</openerp>
@@ -1,11 +1,27 @@
<openerp>
<data>
<act_window id="action_claim_from_delivery" name="Claim"
domain="[]" target="current"
context="{'default_partner_id': partner_id}"
view_mode="form" res_model="crm.claim"
src_model="stock.picking" />
<record id="action_claim_from_delivery" model="ir.actions.act_window">
<field name="name">Claim From Delivery</field>
<field name="res_model">crm.claim</field>
<field name="view_type">form</field>
<field name="view_id" ref="crm_claim.crm_case_claims_tree_view"/>
<field name="context">{'default_ref': 'stock.picking.out,'+str(context.get('active_id', False))}</field>
<field name="domain">[('ref','=','stock.picking.out,'+str(context.get('active_id',False)))]</field>
</record>
<record id="crm_claim_from_delivery" model="ir.ui.view">
<field name="name">crm.claim.from_delivery.form</field>
<field name="model">stock.picking.out</field>
<field name="inherit_id" ref="stock.view_picking_out_form"/>
<field name="arch" type="xml">
<xpath expr="/form/sheet/h1" position="before">
<div class="oe_right oe_button_box">
<button name="%(action_claim_from_delivery)d" string="Claims" type="action"/>
</div>
</xpath>
</field>
</record>
</data>
</openerp>
+21
View File
@@ -0,0 +1,21 @@
# -*- 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/>.
#
##############################################################################
+44
View File
@@ -0,0 +1,44 @@
# -*- 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': 'Address Book',
'version': '1.0',
'category': 'Tools',
'description': """
This module gives you a quick view of your address book, accessible from your home page.
You can track your suppliers, customers and other contacts.
""",
'author': 'OpenERP SA',
'website': 'http://openerp.com',
'summary': 'Contacts, People and Companies',
'depends': [
'mail',
],
'data': [
'contacts_view.xml',
],
'installable': True,
'application': True,
'auto_install': False,
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+53
View File
@@ -0,0 +1,53 @@
<?xml version="1.0" ?>
<openerp>
<data>
<record id="action_contacts" model="ir.actions.act_window">
<field name="name">Contacts</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">res.partner</field>
<field name="view_type">form</field>
<field name="view_mode">kanban,tree,form</field>
<field name="search_view_id" ref="base.view_res_partner_filter"/>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to add a contact in your address book.
</p><p>
OpenERP helps you easily track all activities related to
a customer; discussions, history of business opportunities,
documents, etc.
</p>
</field>
</record>
<record id="action_contacts_view_kanban" model="ir.actions.act_window.view">
<field eval="0" name="sequence"/>
<field name="view_mode">kanban</field>
<field name="view_id" ref="base.res_partner_kanban_view"/>
<field name="act_window_id" ref="action_contacts"/>
</record>
<record id="action_contacts_view_tree" model="ir.actions.act_window.view">
<field name="sequence" eval="1"/>
<field name="view_mode">tree</field>
<field name="view_id" ref="base.view_partner_tree"/>
<field name="act_window_id" ref="action_contacts"/>
</record>
<record id="action_contacts_view_form" model="ir.actions.act_window.view">
<field eval="2" name="sequence"/>
<field name="view_mode">form</field>
<field name="view_id" ref="base.view_partner_form"/>
<field name="act_window_id" ref="action_contacts"/>
</record>
<menuitem name="Contacts"
id="menu_contacts"
parent="mail.mail_my_stuff"
sequence="11"
action="action_contacts"
groups="base.group_user,base.group_partner_manager"/>
</data>
</openerp>
Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

+1 -1
View File
@@ -25,7 +25,7 @@
'version': '1.0',
'category': 'Customer Relationship Management',
'sequence': 2,
'summary': 'Leads, Opportunities, Agenda',
'summary': 'Leads, Opportunities, Phone Calls',
'description': """
The generic OpenERP Customer Relationship Management
=====================================================
+2 -1
View File
@@ -92,9 +92,10 @@ class crm_case_stage(osv.osv):
_defaults = {
'sequence': lambda *args: 1,
'probability': lambda *args: 0.0,
'state': 'draft',
'state': 'open',
'fold': False,
'type': 'both',
'case_default': True,
}
class crm_case_section(osv.osv):
+41 -42
View File
@@ -104,56 +104,55 @@
<field name="arch" type="xml">
<form string="Phone Call" version="7.0">
<header>
<button name="case_close" string="Held" type="object" class="oe_highlight"
<button name="case_close" string="Call Done" type="object" class="oe_highlight"
states="open,pending"/>
<button name="case_reset" string="Reset to Todo" type="object"
states="cancel"/>
<button string="Convert to Opportunity"
name="%(phonecall2opportunity_act)d"
type="action"
attrs="{'invisible':[ '|', ('opportunity_id','!=',False), ('state','!=', 'open')]}"/>
<button string="Convert to Opportunity" class="oe_highlight"
name="%(phonecall2opportunity_act)d"
type="action"
attrs="{'invisible':[ '|', ('opportunity_id','!=',False), ('state','!=', 'done')]}"/>
<button string="Schedule Other Call"
name="%(phonecall_to_phonecall_act)d"
type="action"/>
<button string="Schedule a Meeting" name="action_make_meeting" type="object"/>
<button name="case_cancel" string="Cancel" type="object"
states="draft,open,pending"/>
<field name="state" widget="statusbar" nolabel="1" statusbar_visible="open,done"/>
</header>
<sheet string="Phone Call">
<div class="oe_title">
<div class="oe_edit_only">
<label for="name" string="Title"/>
</div>
<h1><field name="name" required="1"/></h1>
<div class="oe_edit_only">
<label for="partner_phone" string="Phone"/>
</div>
<h2><field name="partner_phone"/></h2>
</div>
<group col="4">
<field name="date"/>
<field name="user_id"/>
<field name="duration" widget="float_time"/>
<field name="section_id" colspan="1" widget="selection"/>
<field name="partner_id" on_change="onchange_partner_id(partner_id)"/>
<field name="categ_id" widget="selection"
domain="[('object_id.model', '=', 'crm.phonecall')]"/>
<field name="partner_mobile"/>
<field name="priority"/>
<field name="opportunity_id"/>
</group>
<sheet string="Phone Call">
<div class="oe_right">
<button string="Convert to Opportunity"
name="%(phonecall2opportunity_act)d"
type="action"
attrs="{'invisible':[ '|', ('opportunity_id','!=',False), ('state', 'not in', ('open', 'done'))]}"/>
<button string="Schedule Other Call"
name="%(phonecall_to_phonecall_act)d"
type="action"/>
<button string="Schedule a Meeting" name="action_make_meeting" type="object"/>
</div>
<div class="oe_title">
<div class="oe_edit_only">
<label for="name" string="Title"/>
</div>
<h1><field name="name" required="1"/></h1>
<div class="oe_edit_only">
<label for="partner_phone" string="Phone"/>
</div>
<h2><field name="partner_phone"/></h2>
</div>
<group col="4">
<field name="date"/>
<field name="user_id"/>
<field name="duration" widget="float_time"/>
<field name="section_id" colspan="1" widget="selection"/>
<field name="partner_id" on_change="onchange_partner_id(partner_id)"/>
<field name="categ_id" widget="selection"
domain="[('object_id.model', '=', 'crm.phonecall')]"/>
<field name="partner_mobile"/>
<field name="priority"/>
<field name="opportunity_id"/>
</group>
<field name="description" placeholder="Description..."/>
</sheet>
<div class="oe_chatter">
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
<field name="description" placeholder="Description..."/>
</sheet>
<div class="oe_chatter">
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
</form>
</field>
</record>
+2 -2
View File
@@ -130,10 +130,10 @@
<field name="arch" type="xml">
<xpath expr="//div[@name='buttons']" position="inside">
<button type="action"
string="Schedule a Meeting"
string="Meetings"
name="%(base_calendar.action_crm_meeting)d"
context="{'search_default_partner_ids': active_id, 'default_partner_ids' : [active_id]}"/>
<button type="action" string="Schedule a Call"
<button type="action" string="Calls"
name="%(crm.crm_case_categ_phone_create_partner)d"
context="{'search_default_partner_id': active_id, 'default_duration': 1.0}" />
<button type="action" string="Opportunities" attrs="{'invisible': [('customer', '=', False)]}"
+4 -4
View File
@@ -259,10 +259,10 @@
<record id="view_picking_withweight_internal_move_form" model="ir.ui.view">
<field name="name">stock.picking_withweight.internal.move.form.view</field>
<field name="model">stock.picking</field>
<field name="inherit_id" ref="stock.view_picking_form"/>
<field name="model">stock.move</field>
<field name="inherit_id" ref="stock.view_move_picking_tree"/>
<field name="arch" type="xml">
<xpath expr="/form/sheet/notebook/page/field[@name='move_lines']/tree/field[@name='product_uom']" position="after">
<xpath expr="//field[@name='product_uom']" position="after">
<field name="weight"/>
<field name="weight_net" groups="base.group_no_one"/>
</xpath>
@@ -306,7 +306,7 @@
<record id="view_delivery_order_inherit_stock" model="ir.ui.view">
<field name="name">stock.picking.out.form</field>
<field name="model">stock.picking</field>
<field name="model">stock.picking.out</field>
<field name="inherit_id" ref="stock.view_picking_out_form"/>
<field name="arch" type="xml">
<xpath expr="/form/header//button[@string='Create Invoice/Refund']" position="after">
+8 -2
View File
@@ -207,6 +207,8 @@ class event_event(osv.osv):
'main_speaker_id': fields.many2one('res.partner','Main Speaker', readonly=False, states={'done': [('readonly', True)]}, help="Speaker who will be giving speech at the event."),
'address_id': fields.many2one('res.partner','Location Address', readonly=False, states={'done': [('readonly', True)]}),
'street': fields.related('address_id','street',type='char',string='Street'),
'street2': fields.related('address_id','street2',type='char',string='Street2'),
'state_id': fields.related('address_id','state_id',type='many2one', relation="res.country.state", string='State'),
'zip': fields.related('address_id','zip',type='char',string='zip'),
'city': fields.related('address_id','city',type='char',string='city'),
'speaker_confirmed': fields.boolean('Speaker Confirmed', readonly=False, states={'done': [('readonly', True)]}),
@@ -265,16 +267,20 @@ class event_event(osv.osv):
def on_change_address_id(self, cr, uid, ids, address_id, context=None):
values = {
'street' : False,
'street2' : False,
'city' : False,
'zip' : False,
'country_id' : False,
'state_id' : False,
}
if isinstance(address_id, (long, int)):
address = self.pool.get('res.partner').browse(cr, uid, address_id, context=context)
values.update({
'street' : address.street,
'street2' : address.street2,
'city' : address.city,
'country_id' : address.country_id and address.country_id.id,
'state_id' : address.state_id and address.state_id.id,
'zip' : address.zip,
})
+23 -21
View File
@@ -101,7 +101,7 @@
<field name="arch" type="xml">
<form string="Events" version="7.0">
<header>
<span groups="base.user_group">
<span groups="base.group_user">
<button string="Confirm Event" name="button_confirm" states="draft" type="object" class="oe_highlight"/>
<button string="Event Ended" name="button_done" states="confirm" type="object" class="oe_highlight"/>
<button string="Set To Draft" name="button_draft" states="cancel,done" type="object" />
@@ -111,52 +111,54 @@
</header>
<sheet>
<div class="oe_right oe_button_box">
<button name="%(event.act_event_list_register_event)d" type="action" string="Registration" icon="gtk-index" help="Register with this event"/>
<button name="%(event.act_event_list_register_event)d" type="action" string="Registrations" help="Register with this event"/>
</div>
<div class="oe_title">
<label for="name" class="oe_edit_only"/>
<h1><field name="name"/></h1>
</div>
<div>
<h2>From <field name="date_begin" class="oe_inline" /> to <field name="date_end" class="oe_inline"/>
</h2>
<group>
<group>
<label for="address_id" string="Location Address"/>
<label for="address_id" string="Location"/>
<div>
<field name="address_id" widget="many2one_address_google_map" widget_option="{'placeholder':'.oe_google_map'}" on_change="on_change_address_id(address_id)" />
<field name="address_id" on_change="on_change_address_id(address_id)" />
<field name="street" placeholder="Street..."/>
<div>
<field name="zip" class="oe_inline" placeholder="Zip"/>
<field name="city" class="oe_inline" placeholder="City"/>
<field name="street2"/>
<div class="address_format">
<field name="city" placeholder="City" style="width: 40%%"/>
<field name="state_id" class="oe_no_button" placeholder="State" style="width: 37%%" options='{"no_open": true}'/>
<field name="zip" placeholder="ZIP" style="width: 20%%"/>
</div>
<field name="country_id" placeholder="Country" class="oe_no_button" options='{"no_open": true}'/>
</div>
</group>
<group>
<field name="type" on_change="onchange_event_type(type,context)"/>
<field name="register_min"/>
<field name="register_max"/>
<label for="register_current" string="Current Registrations" groups="base.group_user"/>
<div groups="base.group_user">
Confirmed: <field name="register_current" class="oe_inline"/>, Unconfirmed: <field name="register_prospect" class="oe_inline"/>
</div>
<field name="date_begin"/>
<field name="date_end"/>
</group>
</group>
</div>
<div class="oe_google_map" style="height: 200px"></div>
<notebook>
<page string="Event Description">
<field name="note" colspan="4" nolabel="1"/>
</page>
<page string="Internal Data">
<page string="Registrations">
<group>
<group>
<field name="register_min"/>
<field name="register_max"/>
<label for="register_current" string="Current Registrations" groups="base.group_user"/>
<div groups="base.group_user">
<field name="register_prospect" class="oe_inline"/> (confirmed: <field name="register_current" class="oe_inline"/>)
</div>
<field name="register_attended"/>
</group>
<group>
<field name="main_speaker_id" domain="[('speaker','=',True)]" context="{'default_speaker':1}"/>
<field name="speaker_confirmed"/>
</group>
<group>
<field name="register_attended"/>
</group>
</group>
<field name="registration_ids" colspan="4" nolabel="1" groups="event.group_event_manager,event.group_event_user">
<tree string="Registration" editable="top">
@@ -166,7 +168,7 @@
<field name="nb_register" />
<field name="state"/>
<button name="registration_open" string="Confirm Registration" states="draft" type="object" icon="gtk-apply"/>
<button name="button_reg_close" string="Attented the Event" states="open" type="object" icon="gtk-jump-to"/>
<button name="button_reg_close" string="Attended the Event" states="open" type="object" icon="gtk-jump-to"/>
<button name="button_reg_cancel" string="Cancel Registration" states="draft,open" type="object" icon="gtk-cancel"/>
</tree>
<form string="Registration">
+333
View File
@@ -0,0 +1,333 @@
# Macedonian translation for openobject-addons
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-02-08 00:36+0000\n"
"PO-Revision-Date: 2012-09-18 16:07+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Macedonian <mk@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-09-19 04:37+0000\n"
"X-Generator: Launchpad (build 15966)\n"
#. module: fetchmail
#: selection:fetchmail.server,state:0
msgid "Confirmed"
msgstr "Потврдено"
#. module: fetchmail
#: field:fetchmail.server,server:0
msgid "Server Name"
msgstr ""
#. module: fetchmail
#: field:fetchmail.server,script:0
msgid "Script"
msgstr ""
#. module: fetchmail
#: help:fetchmail.server,priority:0
msgid "Defines the order of processing, lower values mean higher priority"
msgstr ""
#. module: fetchmail
#: help:fetchmail.server,is_ssl:0
msgid ""
"Connections are encrypted with SSL/TLS through a dedicated port (default: "
"IMAPS=993, POP3S=995)"
msgstr ""
#. module: fetchmail
#: field:fetchmail.server,attach:0
msgid "Keep Attachments"
msgstr ""
#. module: fetchmail
#: help:fetchmail.server,original:0
msgid ""
"Whether a full original copy of each email should be kept for referenceand "
"attached to each processed message. This will usually double the size of "
"your message database."
msgstr ""
#. module: fetchmail
#: field:fetchmail.server,priority:0
msgid "Server Priority"
msgstr ""
#. module: fetchmail
#: field:fetchmail.server,state:0
msgid "State"
msgstr "Состојба"
#. module: fetchmail
#: view:fetchmail.server:0
msgid "POP"
msgstr "POP"
#. module: fetchmail
#: view:fetchmail.server:0
msgid "Fetch Now"
msgstr ""
#. module: fetchmail
#: model:ir.actions.act_window,name:fetchmail.action_email_server_tree
#: model:ir.ui.menu,name:fetchmail.menu_action_fetchmail_server_tree
msgid "Incoming Mail Servers"
msgstr ""
#. module: fetchmail
#: field:fetchmail.server,port:0
msgid "Port"
msgstr ""
#. module: fetchmail
#: view:fetchmail.server:0
msgid "POP/IMAP Servers"
msgstr "POP/IMAP сервери"
#. module: fetchmail
#: selection:fetchmail.server,type:0
msgid "Local Server"
msgstr ""
#. module: fetchmail
#: field:fetchmail.server,user:0
msgid "Username"
msgstr ""
#. module: fetchmail
#: model:ir.model,name:fetchmail.model_fetchmail_server
msgid "POP/IMAP Server"
msgstr ""
#. module: fetchmail
#: view:fetchmail.server:0
msgid "Reset Confirmation"
msgstr ""
#. module: fetchmail
#: view:fetchmail.server:0
msgid "SSL"
msgstr ""
#. module: fetchmail
#: model:ir.model,name:fetchmail.model_mail_message
msgid "Email Message"
msgstr ""
#. module: fetchmail
#: field:fetchmail.server,date:0
msgid "Last Fetch Date"
msgstr ""
#. module: fetchmail
#: help:fetchmail.server,action_id:0
msgid ""
"Optional custom server action to trigger for each incoming mail, on the "
"record that was created or updated by this mail"
msgstr ""
#. module: fetchmail
#: view:fetchmail.server:0
msgid "# of emails"
msgstr ""
#. module: fetchmail
#: field:fetchmail.server,original:0
msgid "Keep Original"
msgstr ""
#. module: fetchmail
#: code:addons/fetchmail/fetchmail.py:155
#, python-format
msgid ""
"Here is what we got instead:\n"
" %s"
msgstr ""
#. module: fetchmail
#: view:fetchmail.server:0
#: field:fetchmail.server,configuration:0
msgid "Configuration"
msgstr ""
#. module: fetchmail
#: view:fetchmail.server:0
msgid "Incoming Mail Server"
msgstr ""
#. module: fetchmail
#: code:addons/fetchmail/fetchmail.py:155
#, python-format
msgid "Connection test failed!"
msgstr ""
#. module: fetchmail
#: help:fetchmail.server,server:0
msgid "Hostname or IP of the mail server"
msgstr ""
#. module: fetchmail
#: view:fetchmail.server:0
msgid "Server type IMAP."
msgstr ""
#. module: fetchmail
#: field:fetchmail.server,name:0
msgid "Name"
msgstr ""
#. module: fetchmail
#: field:fetchmail.server,is_ssl:0
msgid "SSL/TLS"
msgstr ""
#. module: fetchmail
#: view:fetchmail.server:0
msgid "Test & Confirm"
msgstr ""
#. module: fetchmail
#: field:fetchmail.server,action_id:0
msgid "Server Action"
msgstr ""
#. module: fetchmail
#: field:mail.message,fetchmail_server_id:0
msgid "Inbound Mail Server"
msgstr ""
#. module: fetchmail
#: field:fetchmail.server,message_ids:0
#: model:ir.actions.act_window,name:fetchmail.act_server_history
msgid "Messages"
msgstr ""
#. module: fetchmail
#: view:fetchmail.server:0
msgid "Search Incoming Mail Servers"
msgstr ""
#. module: fetchmail
#: field:fetchmail.server,active:0
msgid "Active"
msgstr ""
#. module: fetchmail
#: help:fetchmail.server,attach:0
msgid ""
"Whether attachments should be downloaded. If not enabled, incoming emails "
"will be stripped of any attachments before being processed"
msgstr ""
#. module: fetchmail
#: view:fetchmail.server:0
msgid "Advanced Options"
msgstr ""
#. module: fetchmail
#: selection:fetchmail.server,type:0
msgid "IMAP Server"
msgstr ""
#. module: fetchmail
#: view:fetchmail.server:0
msgid "IMAP"
msgstr ""
#. module: fetchmail
#: view:fetchmail.server:0
msgid "Server type POP."
msgstr ""
#. module: fetchmail
#: field:fetchmail.server,password:0
msgid "Password"
msgstr ""
#. module: fetchmail
#: view:fetchmail.server:0
msgid "Actions to Perform on Incoming Mails"
msgstr ""
#. module: fetchmail
#: field:fetchmail.server,type:0
msgid "Server Type"
msgstr ""
#. module: fetchmail
#: view:fetchmail.server:0
msgid "Login Information"
msgstr ""
#. module: fetchmail
#: view:fetchmail.server:0
msgid "Server Information"
msgstr ""
#. module: fetchmail
#: view:fetchmail.server:0
msgid "If SSL required."
msgstr ""
#. module: fetchmail
#: view:fetchmail.server:0
msgid "Advanced"
msgstr ""
#. module: fetchmail
#: view:fetchmail.server:0
msgid "Server & Login"
msgstr ""
#. module: fetchmail
#: help:fetchmail.server,object_id:0
msgid ""
"Process each incoming mail as part of a conversation corresponding to this "
"document type. This will create new documents for new conversations, or "
"attach follow-up emails to the existing conversations (documents)."
msgstr ""
#. module: fetchmail
#: field:fetchmail.server,object_id:0
msgid "Create a New Record"
msgstr ""
#. module: fetchmail
#: selection:fetchmail.server,state:0
msgid "Not Confirmed"
msgstr "Непотврдено"
#. module: fetchmail
#: selection:fetchmail.server,type:0
msgid "POP Server"
msgstr ""
#. module: fetchmail
#: view:mail.message:0
msgid "Mail Server"
msgstr ""
#~ msgid "Confirm"
#~ msgstr "Потврди"
#~ msgid "Group By..."
#~ msgstr "Групирај по..."
#~ msgid "Type"
#~ msgstr "Вид"
#~ msgid ""
#~ "Warning! Record for selected Model can not be created\n"
#~ "Please choose valid Model"
#~ msgstr ""
#~ "Предупредување! Записот за избраниот Модел не може да се креира\n"
#~ "Изберете валиден Модел"
+33
View File
@@ -0,0 +1,33 @@
# Icelandic translation for openobject-addons
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-02-08 00:36+0000\n"
"PO-Revision-Date: 2012-09-19 10:00+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Icelandic <is@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-09-20 04:43+0000\n"
"X-Generator: Launchpad (build 15985)\n"
#. module: google_map
#: view:res.partner:0
msgid "Map"
msgstr "Landakort"
#. module: google_map
#: model:ir.model,name:google_map.model_res_partner_address
msgid "Partner Addresses"
msgstr "Heimilisfang viðskiptaaðila"
#. module: google_map
#: view:res.partner:0
msgid "Street2 : "
msgstr "Gata2 : "
+1 -1
View File
@@ -55,7 +55,7 @@ class hr_attendance(osv.osv):
'name': fields.datetime('Date', required=True, select=1),
'action': fields.selection([('sign_in', 'Sign In'), ('sign_out', 'Sign Out'), ('action','Action')], 'Action', required=True),
'action_desc': fields.many2one("hr.action.reason", "Action Reason", domain="[('action_type', '=', action)]", help='Specifies the reason for Signing In/Signing Out in case of extra hours.'),
'employee_id': fields.many2one('hr.employee', "Employee's Name", required=True, select=True),
'employee_id': fields.many2one('hr.employee', "Employee", required=True, select=True),
'day': fields.function(_day_compute, type='char', string='Day', store=True, select=1, size=32),
}
_defaults = {
@@ -314,9 +314,7 @@
</div>
<div class="oe_kanban_footer_left" style="margin-top:5px;">
<div class="oe_left oe_tags">
<t t-foreach="record.categ_ids.raw_value" t-as="categ_id">
<span class="oe_tag" t-att-data-categ_id="categ_id"></span>
</t>
<field name="categ_ids"/>
</div>
</div>
</div>
@@ -13,7 +13,7 @@
product_id: product.product_product_consultant
journal_id: hr_timesheet.analytic_journal
-
I create my current timesheet for employee "Quentin Paolinon".
I create a timesheet for employee "Quentin Paolinon".
-
!record {model: hr_timesheet_sheet.sheet, id: hr_timesheet_sheet_sheet_deddk0}:
date_current: !eval time.strftime('%Y-%m-%d')
@@ -23,13 +23,17 @@
user_id: base.user_demo
employee_id: 'hr.employee_qdp'
-
Now, at the time of login, I create Attendances and perform "Sign In" action.
Quentin "Sign In" at around 9am.
-
!python {model: hr_timesheet_sheet.sheet}: |
self.attendance_action_change(cr, uid, [ref('hr_timesheet_sheet_sheet_deddk0')], {})
task_work = self.search(cr, uid, [("name","=","Quentin Paolinon")],context)[0]
task_ids = self.browse(cr, uid, task_work, context)
assert task_ids.state == "draft", "I State In Timesheet"
!record {model: hr.attendance, id: hr_attendance_1}:
action: sign_in
employee_id: 'hr.employee_qdp'
name: !eval datetime.now().strftime('%Y-%m-%d 09:%M:%S')
-
I test that Quentin in signed in
-
!assert {model: hr.employee, id: hr.employee_qdp}:
- state == 'present'
-
I want to check attendance and work of yesterday. I click on <- button.
-
@@ -57,10 +61,10 @@
-
At the time of logout, I create attendance and perform "Sign Out".
-
!record {model: hr.attendance, id: hr_attendance_1}:
!record {model: hr.attendance, id: hr_attendance_2}:
action: sign_out
employee_id: 'hr.employee_qdp'
name: !eval (datetime.now() + timedelta(minutes=1)).strftime('%Y-%m-%d %H:%M:%S')
name: !eval datetime.now().strftime('%Y-%m-%d 17:30:00')
-
I create Timesheet Entry for time spend on today work.
-
@@ -103,10 +107,10 @@
general_account_id: account.a_expense
journal_id: hr_timesheet.analytic_journal
-
I set an allowed difference of 25 hours
I set an allowed difference of 1 hour
-
!record {model: res.company, id: base.main_company}:
timesheet_max_difference: 25.00
timesheet_max_difference: 1.00
-
I tried again to confirm the timesheet after modification.
-
+1 -3
View File
@@ -444,11 +444,9 @@ class sugar_import(import_framework):
import Tasks
"""
task_state = {
'Completed' : 'done',
'Completed' : 'open',
'Not Started':'draft',
'In Progress': 'open',
'Pending Input': 'draft',
'deferred': 'cancel'
}
def import_task(self, val):
+1 -1
View File
@@ -27,7 +27,7 @@ This is the base module to manage the accounting chart for Ecuador in OpenERP.
Accounting chart and localization for Ecuador.
""",
'author': 'OpenERP SA',
'author': 'Gnuthink Co.Ltd.',
'depends': [
'account',
'base_vat',
+1
View File
@@ -25,6 +25,7 @@ import mail_message
import mail_mail
import mail_thread
import mail_group
import mail_vote
import res_partner
import res_users
import report
+1 -1
View File
@@ -7,7 +7,7 @@
<field name="description">Discussion about best sales practices and deals.</field>
</record>
<record model="mail.group" id="group_all_employees">
<field name="name">All Employees</field>
<field name="name">Whole Company</field>
<field name="group_ids" eval="[(4, ref('base.group_user'))]"/>
<field name="description">Discussion about best sales practices and deals.</field>
</record>
+27
View File
@@ -130,6 +130,8 @@ class mail_message(osv.Model):
'unread': fields.function(_get_unread, fnct_search=_search_unread,
type='boolean', string='Unread',
help='Functional field to search for unread messages linked to uid'),
'vote_user_ids': fields.many2many('res.users', 'mail_vote', 'message_id', 'user_id', string='Votes',
help='Users that voted for this message'),
}
def _needaction_domain_get(self, cr, uid, context=None):
@@ -147,12 +149,35 @@ class mail_message(osv.Model):
'body': '',
}
#------------------------------------------------------
# Vote/Like
#------------------------------------------------------
def vote_toggle(self, cr, uid, ids, user_ids=None, context=None):
''' Toggles voting '''
if not user_ids:
user_ids = [uid]
for message in self.read(cr, uid, ids, ['vote_user_ids'], context=context):
for user_id in user_ids:
has_voted = user_id in message.get('vote_user_ids')
if not has_voted:
self.write(cr, uid, message.get('id'), {'vote_user_ids': [(4, user_id)]}, context=context)
else:
self.write(cr, uid, message.get('id'), {'vote_user_ids': [(3, user_id)]}, context=context)
return True
#------------------------------------------------------
# Message loading for web interface
#------------------------------------------------------
def _message_dict_get(self, cr, uid, msg, context=None):
""" Return a dict representation of the message browse record. """
has_voted = False
vote_ids = self.pool.get('res.users').name_get(cr, uid, [user.id for user in msg.vote_user_ids], context=context)
for vote in vote_ids:
if vote[0] == uid:
has_voted = True
break
attachment_ids = [{'id': attach[0], 'name': attach[1]} for attach in self.pool.get('ir.attachment').name_get(cr, uid, [x.id for x in msg.attachment_ids], context=context)]
author_id = self.pool.get('res.partner').name_get(cr, uid, [msg.author_id.id], context=context)[0]
author_user_id = self.pool.get('res.users').name_get(cr, uid, [msg.author_id.user_ids[0].id], context=context)[0]
@@ -171,6 +196,8 @@ class mail_message(osv.Model):
'author_user_id': author_user_id,
'partner_ids': partner_ids,
'child_ids': [],
'vote_user_ids': vote_ids,
'has_voted': has_voted
}
def message_read_tree_flatten(self, cr, uid, messages, current_level, level, context=None):
+2 -1
View File
@@ -9,7 +9,8 @@
sequence="10"/>
<!-- Left-side menu: Feeds -->
<menuitem id="mail_feeds" name="Feeds" parent="mail_feeds_main" groups="base.group_user" sequence="10"/>
<menuitem id="mail_feeds" name="Feeds" parent="mail.mail_feeds_main" groups="base.group_user" sequence="10"/>
<menuitem id="mail_my_stuff" name="Organizer" parent="mail.mail_feeds_main"/>
<record id="mail_wallfeeds" model="ir.ui.menu">
<field name="name">My Feeds</field>
+39
View File
@@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2012-Today 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
class mail_vote(osv.Model):
''' Mail vote feature allow users to like and unlike messages attached
to a document. This allows for example to build a ranking-based
displaying of messages, for FAQ. '''
_name = 'mail.vote'
_description = 'Mail Vote'
_columns = {
'message_id': fields.many2one('mail.message', 'Message', select=1,
ondelete='cascade', required=True),
'user_id': fields.many2one('res.users', 'User', select=1,
ondelete='cascade', required=True),
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+1
View File
@@ -10,3 +10,4 @@ access_mail_alias_user,mail.alias,model_mail_alias,base.group_user,1,1,1,0
access_mail_alias_system,mail.alias,model_mail_alias,base.group_system,1,1,1,1
access_mail_mail_user,mail.mail,model_mail_mail,base.group_user,1,1,1,0
access_mail_mail_manager,mail.mail,model_mail_mail,group_mail_manager,1,1,1,1
access_mail_vote_all,mail.vote.all,model_mail_vote,,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
10 access_mail_alias_system mail.alias model_mail_alias base.group_system 1 1 1 1
11 access_mail_mail_user mail.mail model_mail_mail base.group_user 1 1 1 0
12 access_mail_mail_manager mail.mail model_mail_mail group_mail_manager 1 1 1 1
13 access_mail_vote_all mail.vote.all model_mail_vote 1 1 1 1
+25
View File
@@ -260,6 +260,31 @@
display: none;
}
/*--------------------------------------------------------------*/
/* mail.vote
/*--------------------------------------------------------------*/
.openerp .oe_mail_msg_content button.oe_mail_msg_vote {
height:21px;
width: 30px;
padding: 1px;
background: #8A89BA;
margin-top: -4px;
}
.openerp .oe_mail_msg_content button.oe_mail_msg_vote_true {
background:#DC5F59;
}
.openerp .oe_mail_msg_content button.oe_mail_msg_vote span {
color: white;
}
.openerp .oe_mail_msg_content span.oe_mail_vote_count{
color: #807FB4;
}
/* ------------------------------------------------------------ */
/* mail.compose.message form view & OpenERP hacks
/* ------------------------------------------------------------ */
+32
View File
@@ -291,6 +291,7 @@ openerp.mail = function(session) {
truncate_limit: options.truncate_limit || 250,
}
// datasets and internal vars
this.records = {};
this.ds_thread = new session.web.DataSetSearch(this, this.context.default_model);
this.ds_notification = new session.web.DataSetSearch(this, 'mail.notification');
this.ds_message = new session.web.DataSetSearch(this, 'mail.message');
@@ -355,6 +356,8 @@ openerp.mail = function(session) {
'default_parent_id': parseInt(msg_id),
'default_content_subtype': 'html'} );
});
// event: click on 'Vote' button
this.$el.on('click', 'button.oe_mail_msg_vote', this.on_vote);
},
on_message_delete: function (event) {
@@ -373,6 +376,16 @@ openerp.mail = function(session) {
return this.ds_notification.call('set_message_read', [parseInt(msg_id)]);
},
on_vote: function (event) {
event.stopPropagation();
var self = this;
var message_id = $(event.srcElement).parent().data().msg_id;
var vote_node = $(event.srcElement).parents('li').eq(0);
if (! message_id) { return false; }
return this.ds_message.call('vote_toggle', [[parseInt(message_id)]]).pipe(
self.toggle_vote(message_id, vote_node));
},
/**
* Override-hack of do_action: automatically reload the chatter.
* Normally it should be called only when clicking on 'Post/Send'
@@ -492,6 +505,8 @@ openerp.mail = function(session) {
attach['url'] = mail.ChatterUtils.get_attachment_url(this.session, attach);
}
record.is_author = mail.ChatterUtils.is_author(this, record.author_user_id[0]);
// add to internal storage
this.records[record.id] = record;
// render, add the expand feature
var rendered = session.web.qweb.render('mail.thread.message', {'record': record, 'thread': this, 'options': this.options});
$(rendered).appendTo(this.$el.children('div.oe_mail_thread_display:first'));
@@ -505,6 +520,23 @@ openerp.mail = function(session) {
});
},
// Render vote Display template.
toggle_vote: function (message_id, vote_node) {
var self = this;
var record = this.records[message_id];
if (record.has_voted) {
var idx = _.map(record.vote_user_ids, function (x) { return x[0]; }).indexOf(message_id);
record.vote_user_ids.splice(idx, 1);
}
else {
record.vote_user_ids.push([this.session.uid, 'You']);
}
record.has_voted = ! record.has_voted;
var vote_element = session.web.qweb.render('mail.thread.message.vote', {'record': record});
vote_node.empty();
vote_node.html(vote_element);
},
/** Display 'show more' button */
update_fetch_more: function (new_value) {
if (new_value) {
+14 -3
View File
@@ -142,11 +142,9 @@
</li>
<li><a t-attf-href="#model=res.partner&amp;id=#{record.author_id[0]}"><t t-raw="record.author_id[1]"/></a></li>
<li><span t-att-title="record.date"><t t-raw="record.timerelative"/></span></li>
<t t-call="mail.thread.message.vote"/>
<li t-if="options.show_reply"><a class="oe_mail_msg_reply">Reply</a></li>
<li t-if="options.show_reply_by_email"><a class="oe_mail_msg_reply_by_email" t-attf-data-msg_id="{record.id}">Reply</a></li>
<!-- uncomment when merging vote
<li><a href="#">Like</a></li>
-->
<li t-if="record.attachment_ids.length > 0">
<a class="oe_mail_msg_view_attachments">
<t t-if="record.attachment_ids.length == 1">1 Attachment</t>
@@ -180,4 +178,17 @@
</ul>
</t>
<!-- mail.thread.message.vote
Template used to display Like/Unlike in a mail.message
-->
<li t-name="mail.thread.message.vote">
<t t-if='record.vote_user_ids.length > 0'>
<span class="oe_left oe_mail_vote_count"><t t-esc="record.vote_user_ids.length"/> votes</span>
</t>
<button t-attf-class="oe_mail_msg_vote oe_mail_msg_vote_#{record.has_voted}" t-attf-data-msg_id="{record.id}">
<t t-if="! record.has_voted"><span>+1</span></t>
<t t-if="record.has_voted"><span>-1</span></t>
</button>
</li>
</template>
+30
View File
@@ -647,3 +647,33 @@ class test_mail(TestMailMockups):
msg1.refresh()
self.assertEqual(5, len(group_pigs.message_ids), 'group should contain 5 messages')
self.assertEqual(2, len(msg1.child_ids), 'msg1 should have 2 children now')
def test_60_vote(self):
""" Test designed for the vote/unvote feature. """
cr, uid = self.cr, self.uid
group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id)
user_admin = self.res_users.browse(cr, uid, uid)
msg1 = group_pigs.message_post(body='My Body', subject='1')
msg1 = self.mail_message.browse(cr, uid, msg1)
# Create user Bert Tartopoils
user_bert_id = self.res_users.create(cr, uid, {'name': 'Bert', 'login': 'bert'})
user_bert = self.res_users.browse(cr, uid, user_bert_id)
# Test: msg1 and msg2 have void vote_user_ids
self.assertFalse(msg1.vote_user_ids, 'newly created message msg1 has not void vote_user_ids')
# Do: Admin vote for msg1
self.mail_message.vote_toggle(cr, uid, [msg1.id])
msg1.refresh()
# Test: msg1 has Admin as voter
self.assertEqual(set(msg1.vote_user_ids), set([user_admin]), 'after voting, Admin is not the voter')
# Do: Bert vote for msg1
self.mail_message.vote_toggle(cr, uid, [msg1.id], [user_bert_id])
msg1.refresh()
# Test: msg1 has Admin and Bert as voters
self.assertEqual(set(msg1.vote_user_ids), set([user_admin, user_bert]), 'after voting, Admin and Bert are not the voters')
# Do: Admin unvote for msg1
self.mail_message.vote_toggle(cr, uid, [msg1.id])
msg1.refresh()
# Test: msg1 has Bert as voter
self.assertEqual(set(msg1.vote_user_ids), set([user_bert]), 'after unvoting for Admin, Bert is not the voter')
+2 -1
View File
@@ -30,7 +30,8 @@
icon="terp-graph"
id="menu_board_manufacturing"
parent="base.menu_reporting_dashboard"
sequence="30"/>
sequence="30"
groups="group_mrp_manager"/>
</data>
</openerp>
+9 -2
View File
@@ -956,9 +956,16 @@
<field name="model">product.product</field>
<field name="inherit_id" ref="product.product_normal_form_view"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='procure_method']" position="after">
<field name="supply_method" groups="base.group_user"/>
<xpath expr="//field[@name='supply_method']" position="attributes">
<attribute name="invisible">False</attribute>
</xpath>
<group name="procurement_help" position="inside">
<p attrs="{'invisible': [('type','=','service'),('procure_method','&lt;&gt;','make_to_order'),('supply_method','&lt;&gt;','produce')]}">
When you sell this product, OpenERP will trigger <b>a manufacturing
order</b> using the bill of materials assigned to this product.
The delivery order will be ready once the production is done.
</p>
</group>
</field>
</record>
+3 -3
View File
@@ -20,7 +20,7 @@
##############################################################################
{
'name': 'Memos',
'name': 'Notes',
'version': '1.0',
'category': 'Tools',
'description': """
@@ -28,12 +28,12 @@ This module allows users to create their own notes inside OpenERP
=================================================================
Use notes to write meeting minutes, organize ideas, organize personnal todo
lists, etc. Each user manages his own personnal memos. Notes are available to
lists, etc. Each user manages his own personnal Notes. Notes are available to
their authors only, but they can share notes to others users so that several
people can work on the same note in real time. It's very efficient to share
meeting minutes.
Memos can be found in the 'Home' menu.
Notes can be found in the 'Home' menu.
""",
'author': 'OpenERP SA',
'website': 'http://openerp.com',
+10 -10
View File
@@ -22,6 +22,7 @@
from openerp.osv import osv, fields
from tools.translate import _
import re
from openerp.tools.misc import html2plaintext
class note_stage(osv.osv):
""" Category of Note """
@@ -62,26 +63,24 @@ class note_note(osv.osv):
def _get_note_first_line(self, cr, uid, ids, name="", args={}, context=None):
res = {}
for note in self.browse(cr, uid, ids, context=context):
text_note = (note.memo or '').strip().split('\n')[0]
text_note = re.sub(r'(\S?)(<br[ /]*>|<[/]?p>|<[/]?div>|<table>)[\s\S]*',r'\1',text_note)
text_note = re.sub(r'<[^>]+>','',text_note)
res[note.id] = text_note
res[note.id] = (note.memo and html2plaintext(note.memo) or "").strip().replace('*','').split("\n")[0]
return res
#unactivate a sticky note and record the date
def onclick_note_is_done(self, cr, uid, ids, context=None):
self.write(cr, uid, ids, { 'active' : False, 'date_done' : fields.date.today() })
self.write(cr, uid, ids, { 'open' : False, 'date_done' : fields.date.today() })
self.message_post(cr, uid, ids[0], body='Note is done.', subject=False,
type='notification', parent_id=False, attachments=None, context=context)
return False
#activate a note
def onclick_note_not_done(self, cr, uid, ids, context=None):
self.write(cr, uid, ids, {'active' : True})
self.write(cr, uid, ids, {'open' : True})
self.message_post(cr, uid, ids[0], body='Note has been activated.', subject=False,
type='notification', parent_id=False, attachments=None, context=context)
return False
#used for undisplay the follower if it's the current user
def _get_my_current_partner(self, cr, uid, ids, name, args, context=None):
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
@@ -96,7 +95,8 @@ class note_note(osv.osv):
def _set_stage_per_user(self, cr, uid, id, name, value, args=None, context=None):
note = self.browse(cr, uid, id, context=context)
if not value: return False
return self.write(cr, uid, [id], {'stage_ids': [(4, value)]}, context=context)
stage_ids = [value] + [stage.id for stage in note.stage_ids if stage.user_id.id != uid ]
return self.write(cr, uid, [id], {'stage_ids': [(6, 0, stage_ids)]}, context=context)
def _get_stage_per_user(self, cr, uid, ids, name, args, context=None):
result = dict.fromkeys(ids, False)
@@ -119,14 +119,14 @@ class note_note(osv.osv):
type='many2one',
relation='note.stage'),
'stage_ids': fields.many2many('note.stage','note_stage_rel','note_id','stage_id','Stages of Users'),
'active': fields.boolean('Active'),
'open': fields.boolean('Active'),
'date_done': fields.date('Date done'),
'color': fields.integer('Color Index'),
'tag_ids' : fields.many2many('note.tag','note_tags_rel','note_id','tag_id','Tags'),
'current_partner_id' : fields.function(_get_my_current_partner),
}
_defaults = {
'active' : 1,
'open' : 1,
'stage_id' : _get_default_stage_id,
}
_order = 'sequence'
+11 -20
View File
@@ -28,7 +28,7 @@
<record id="note_1" model="note.note">
<field name="name">Customer report #349872</field>
<field name="memo"><![CDATA[Customer report #349872
<field name="memo"><![CDATA[<b>Customer report #349872</b>
<br/><br/>* Calendar app in Home
<br/>* The base_calendar module should create a menu in Home, like described above.
<br/>* This module should become a main application (in the first screen at installation)
@@ -40,7 +40,7 @@
</record>
<record id="note_2" model="note.note">
<field name="memo"><![CDATA[Call Fabien
<field name="memo"><![CDATA[<b>Call Fabien</b>
<br/><br/>* Followed by the telephone conversation and mail about D.544.3
]]>
</field>
@@ -48,21 +48,21 @@
</record>
<record id="note_3" model="note.note">
<field name="memo"><![CDATA[Call Marc
<field name="memo"><![CDATA[<b>Call Marc</b>
<br/><br/>]]>
</field>
<field name="stage_id" ref="note_stage_01"/>
</record>
<record id="note_4" model="note.note">
<field name="memo"><![CDATA[Project N.947.5
<field name="memo"><![CDATA[<b>Project N.947.5</b>
<br/><br/>]]>
</field>
<field name="stage_id" ref="note_stage_02"/>
</record>
<record id="note_5" model="note.note">
<field name="memo"><![CDATA[Shop for family dinner
<field name="memo"><![CDATA[<b>Shop for family dinner</b>
<br/>* stuffed turkey
<br/>* wine
]]>
@@ -71,7 +71,7 @@
</record>
<record id="note_6" model="note.note">
<field name="memo"><![CDATA[Idea to develop
<field name="memo"><![CDATA[<b>Idea to develop</b>
<br/><br/>* Create a module note_pad
it transforms the html editable memo text field into widget='pad', similar to project_pad depends on 'memo' and 'pad' modules
]]>
@@ -80,7 +80,7 @@
</record>
<record id="note_7" model="note.note">
<field name="memo"><![CDATA[Read some documentation about OpenERP before diving into the code
<field name="memo"><![CDATA[<b>Read some documentation about OpenERP before diving into the code</b>
<br/><br/>* Open ERP: a modern approach to integrated business management
<br/>* Open ERP for Retail and Industrial Management
]]>
@@ -89,7 +89,7 @@
</record>
<record id="note_8" model="note.note">
<field name="memo"><![CDATA[New computer specs
<field name="memo"><![CDATA[<b>New computer specs</b>
<br/><br/>* Motherboard
according to processor
<br/>* Processor
@@ -111,7 +111,7 @@
</record>
<record id="note_9" model="note.note">
<field name="memo"><![CDATA[Read those books
<field name="memo"><![CDATA[<b>Read those books</b>
<br/><br/>* Open ERP: a modern approach to integrated business management
<br/>* Open ERP for Retail and Industrial Management
]]>
@@ -120,7 +120,7 @@
</record>
<record id="note_10" model="note.note">
<field name="memo"><![CDATA[Read some documentation about OpenERP before diving into the code
<field name="memo"><![CDATA[<b>Read some documentation about OpenERP before diving into the code</b>
<br/><br/>* Open ERP: a modern approach to integrated business management
<br/>* Open ERP for Retail and Industrial Management
]]>
@@ -129,7 +129,7 @@
</record>
<record id="note_12" model="note.note">
<field name="memo"><![CDATA[Read some documentation about OpenERP before diving into the code
<field name="memo"><![CDATA[<b>Read some documentation about OpenERP before diving into the code</b>
<br/><br/>* Open ERP: a modern approach to integrated business management
<br/>* Open ERP for Retail and Industrial Management
]]>
@@ -138,14 +138,5 @@
<field name="color">7</field>
</record>
<record id="note_13" model="note.note">
<field name="memo"><![CDATA[Read those books
<br/><br/>* Open ERP: a modern approach to integrated business management
<br/>* Open ERP for Retail and Industrial Management
]]>
</field>
<field name="stage_id" ref="note_stage_03"/>
</record>
</data>
</openerp>
+12 -20
View File
@@ -1,9 +1,6 @@
<?xml version="1.0"?>
<openerp>
<data>
<menuitem name="Organizer" id="note_my_stuff" parent="mail.mail_feeds_main"/>
<!-- note Stage Form View -->
<record model="ir.ui.view" id="view_note_stage_form">
<field name="name">note.stage.form</field>
@@ -39,8 +36,6 @@
<field name="domain">[('user_id','=',uid)]</field>
</record>
<menuitem name="Categories" id="menu_notes_stage" parent="note_my_stuff" action="action_note_stage" sequence="40" groups="base.group_no_one"/>
<!-- New note Kanban View -->
<record model="ir.ui.view" id="view_note_note_kanban">
<field name="name">note.note.kanban</field>
@@ -51,7 +46,7 @@
<field name="sequence"/>
<field name="name"/>
<field name="stage_id"/>
<field name="active"/>
<field name="open"/>
<field name="memo"/>
<field name="date_done"/>
<field name="current_partner_id"/>
@@ -65,8 +60,8 @@
<div t-attf-class="oe_kanban_color_#{kanban_getcolor(record.color.raw_value)} oe_kanban_global_click_edit oe_semantic_html_override oe_kanban_card #{record.group_fancy==1 ? 'oe_kanban_card_fancy' : ''}">
<div class="oe_dropdown_kanban">
<span>
<a name="onclick_note_is_done" type="object" t-if="record.active.raw_value" class="oe_e">W</a>
<a name="onclick_note_not_done" type="object" t-if="!record.active.raw_value" class="oe_e">è</a>
<a name="onclick_note_is_done" type="object" t-if="record.open.raw_value" class="oe_e">W</a>
<a name="onclick_note_not_done" type="object" t-if="!record.open.raw_value" class="oe_e">è</a>
</span>
<!-- dropdown menu -->
@@ -78,13 +73,12 @@
</ul>
</div>
</div>
<!-- kanban note -->
<div t-attf-class="oe_kanban_content #{record.active.raw_value ? '' : 'note_text_line_through'}">
<div t-attf-class="oe_kanban_content #{record.open.raw_value ? '' : 'note_text_line_through'}">
<!-- title -->
<field name="name"/>
</div>
<div widget="many2many_tags" t-att-data="record.tag_ids.raw_value" model="note.tag"/>
<field name="tag_ids"/>
<div class="oe_right">
<t t-foreach="record.message_follower_ids.raw_value" t-as="follower">
<img t-if="record.current_partner_id.raw_value!=follower" t-att-src="kanban_image('res.partner', 'image_small', follower)" width="24" height="24" class="oe_kanban_avatar" t-att-data-member_id="follower"/>
@@ -105,7 +99,7 @@
<field name="arch" type="xml">
<tree string="Stages">
<field name="name"/>
<field name="active"/>
<field name="open"/>
<field name="stage_id"/>
<field name="tag_ids" widget="many2many_tags" groups="note.group_note_tags"/>
</tree>
@@ -119,12 +113,10 @@
<field name="arch" type="xml">
<form string="Note" version="7.0">
<header>
<field name="tag_ids" widget="many2many_tags" class="oe_inline" placeholder="Tags"/>
<field name="stage_id" domain="[('user_id','=',uid)]" widget="statusbar" clickable="1"/>
</header>
<field name="memo" widget="html"/><!-- editor_width="100%%" editor_height="60%%" -->
<group groups="note.group_note_tags" col="2">
<field name="tag_ids" widget="many2many_tags"/>
</group>
<field name="message_follower_ids" widget="mail_followers"/>
<field class="oe_chatter" name="message_ids" widget="mail_thread"/>
</form>
@@ -139,9 +131,8 @@
<search string="Notes">
<field name="memo" string="Note"/>
<field name="tag_ids" groups="note.group_note_tags"/>
<field name="stage_id"/>
<filter name="active_true" string="Active" domain="['|',('active', '=', True),('date_done','=',time.strftime('%%Y-%%m-%%d'))]"/>
<filter name="active_false" string="Old" domain="[('active', '=', False)]"/>
<filter name="open_true" string="Active" domain="['|',('open', '=', True),('date_done','=',time.strftime('%%Y-%%m-%%d'))]"/>
<filter name="open_false" string="Archive" domain="[('open', '=', False)]"/>
<group expand="0" string="Group By...">
<filter icon="terp-stock_symbol-selection" string="Stage" help="By sticky note Category" context="{'group_by':'stage_id'}"/>
</group>
@@ -185,10 +176,11 @@
<field name="view_type">form</field>
<field name="view_mode">kanban,tree,form</field>
<field name="search_view_id" ref="view_note_note_filter"/>
<field name="context">{'search_default_active_true':True}</field>
<field name="context">{'search_default_open_true':True}</field>
</record>
<menuitem name="Notes" id="note_notes" parent="note_my_stuff" sequence="20" action="action_note_note"/>
<menuitem name="Notes" id="menu_note_notes" parent="mail.mail_my_stuff" sequence="20" action="note.action_note_note"/>
<menuitem name="Categories" id="menu_notes_stage" parent="mail.mail_my_stuff" action="note.action_note_stage" sequence="21" groups="base.group_no_one"/>
</data>
</openerp>
+13 -6
View File
@@ -1,12 +1,19 @@
<?xml version="1.0"?>
<openerp>
<data>
<record id="note_note_rule_global" model="ir.rule">
<field name="name">Only followers can access a sticky notes</field>
<field model="ir.model" ref="model_note_note" name="model_id"/>
<field name="domain_force">['|',('message_follower_ids','=',False),('message_follower_ids','=',user.partner_id.id)]</field>
<field eval="True" name="global"/>
</record>
<record id="note_note_rule_global" model="ir.rule">
<field name="name">Only followers can access a sticky notes</field>
<field model="ir.model" ref="model_note_note" name="model_id"/>
<field name="domain_force">[('message_follower_ids','=',user.partner_id.id)]</field>
<field eval="True" name="global"/>
</record>
<record id="note_stage_rule_global" model="ir.rule">
<field name="name">Each user have his stage name</field>
<field model="ir.model" ref="model_note_stage" name="model_id"/>
<field name="domain_force">['|',('user_id','=',False),('user_id','=',user.id)]</field>
<field eval="True" name="global"/>
</record>
</data>
</openerp>
+2 -8
View File
@@ -31,11 +31,5 @@ class note_pad_note(osv.osv):
_pad_fields = ['note_pad']
_columns = {
'note_pad_url': fields.char('Pad Url',
pad_content_field='memo',
size=250 ),
}
#_defaults = {
# 'note_pad_url': lambda self, cr, uid, context: self.pad_generate_url(cr, uid, context),
#}
'note_pad_url': fields.char('Pad Url', pad_content_field='memo'),
}
+11 -12
View File
@@ -21,7 +21,7 @@ class pad_common(osv.osv_memory):
s = string.ascii_uppercase + string.digits
salt = ''.join([s[random.randint(0, len(s) - 1)] for i in range(10)])
# contruct the url
url = '%s/p/%s-%s-%s' % (pad_server, cr.dbname, self._name, salt)
url = '%s/p/%s-%s-%s' % (pad_server, cr.dbname.replace('_','-'), self._name, salt)
return url
def pad_get_content(self, cr, uid, url, context=None):
@@ -35,22 +35,21 @@ class pad_common(osv.osv_memory):
# TODO
# reverse engineer protocol to be setHtml without using the api key
# override read and copy to generate url and store the content if empty
def default_get(self, cr, uid, fields, context=None):
data = super(pad_common, self).default_get(cr, uid, fields, context)
for k in fields:
field = self._all_columns[k].column
if hasattr(field,'pad_content_field'):
data[k] = self.pad_generate_url(cr, uid, context=context)
return data
def write(self, cr, uid, ids, vals, context=None):
self._set_pad_value(cr, uid, vals, context)
return super(pad_common, self).write(cr, uid, ids, vals, context=context)
def create(self, cr, uid, vals, context=None):
self._set_pad_value(cr, uid, vals, context)
return super(pad_common, self).create(cr, uid, vals, context=context)
# Set the pad content in vals
def _set_pad_value(self, cr, uid, vals, context=None):
for k,v in vals.items():
field = self._all_columns[k].column
if hasattr(field,'pad_content_field'):
vals[field.pad_content_field] = self.pad_get_content(cr, uid, v, context=context)
return super(pad_common, self).write(cr, uid, ids, vals, context=context)
vals[field.pad_content_field] = self.pad_get_content(cr, uid, v, context=context)
def copy(self, cr, uid, id, default=None, context=None):
if not default:
+8 -1
View File
@@ -16,8 +16,15 @@ instance.web.form.FieldPad = instance.web.form.AbstractField.extend({
var self = this;
var _super = self._super;
_super.apply(self,[val]);
if (val === false || val === "") {
self.field_manager.dataset.call('pad_generate_url').then(function(r) {
_super.apply(self,[r]);
self.render_value();
});
} else {
self.render_value();
}
this._dirty_flag = true;
self.render_value();
},
render_value: function() {
console.log("display");
+109
View File
@@ -0,0 +1,109 @@
# Icelandic translation for openobject-addons
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-02-09 00:36+0000\n"
"PO-Revision-Date: 2012-09-19 10:08+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Icelandic <is@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-09-20 04:43+0000\n"
"X-Generator: Launchpad (build 15985)\n"
#. module: plugin_thunderbird
#: field:plugin_thunderbird.installer,pdf_file:0
msgid "Installation Manual"
msgstr "Uppsetningarhandbók"
#. module: plugin_thunderbird
#: field:plugin_thunderbird.installer,description:0
msgid "Description"
msgstr "Lýsing"
#. module: plugin_thunderbird
#: view:plugin_thunderbird.installer:0
msgid "title"
msgstr "titill"
#. module: plugin_thunderbird
#: model:ir.model,name:plugin_thunderbird.model_plugin_thunderbird_installer
msgid "plugin_thunderbird.installer"
msgstr "plugin_thunderbird.installer"
#. module: plugin_thunderbird
#: field:plugin_thunderbird.installer,config_logo:0
msgid "Image"
msgstr "Mynd"
#. module: plugin_thunderbird
#: field:plugin_thunderbird.installer,plugin_file:0
#: field:plugin_thunderbird.installer,thunderbird:0
msgid "Thunderbird Plug-in"
msgstr "Thunderbird viðbót"
#. module: plugin_thunderbird
#: view:plugin_thunderbird.installer:0
msgid ""
"This plug-in allows you to link your email to OpenERP's documents. You can "
"attach it to any existing one in OpenERP or create a new one."
msgstr ""
#. module: plugin_thunderbird
#: help:plugin_thunderbird.installer,thunderbird:0
msgid ""
"Allows you to select an object that you would like to add to your email and "
"its attachments."
msgstr ""
"Gerir þér kleift að velja hlut sem þú vilt bæta við póstinn og viðhengi hans."
#. module: plugin_thunderbird
#: help:plugin_thunderbird.installer,pdf_file:0
msgid "The documentation file :- how to install Thunderbird Plug-in."
msgstr "Handbókin :- hvernig á að setja inn Thunderbird viðbót."
#. module: plugin_thunderbird
#: view:plugin_thunderbird.installer:0
msgid "_Close"
msgstr "_Loka"
#. module: plugin_thunderbird
#: view:plugin_thunderbird.installer:0
msgid "Installation and Configuration Steps"
msgstr ""
#. module: plugin_thunderbird
#: field:plugin_thunderbird.installer,name:0
#: field:plugin_thunderbird.installer,pdf_name:0
msgid "File name"
msgstr "Skrárheiti"
#. module: plugin_thunderbird
#: help:plugin_thunderbird.installer,plugin_file:0
msgid ""
"Thunderbird plug-in file. Save as this file and install this plug-in in "
"thunderbird."
msgstr ""
"Skrá með Thunderbird viðbót. Vistaðu skrána og settu viðbótina inn í "
"Thunderbird."
#. module: plugin_thunderbird
#: model:ir.actions.act_window,name:plugin_thunderbird.action_thunderbird_installer
#: model:ir.ui.menu,name:plugin_thunderbird.menu_base_config_plugins_thunderbird
#: view:plugin_thunderbird.installer:0
msgid "Install Thunderbird Plug-In"
msgstr "Setja inn Thunderbird viðbót"
#~ msgid ""
#~ "This plug-in allows you to link your e-mail to OpenERP's documents. You can "
#~ "attach it to any existing one in OpenERP or create a new one."
#~ msgstr ""
#~ "Þessi viðbót gerir þér kleift að tengja tölvupóst við OpenERP skjöl. Þú "
#~ "getur viðhengt póstinn við skjöl sem þegar eru til í OpenERP eða búið til "
#~ "nýtt."
+6 -2
View File
@@ -314,9 +314,13 @@ class pos_session(osv.osv):
# define some cash journal if no payment method exists
if not pos_config.journal_ids:
cashids = self.pool.get('account.journal').search(cr, uid, [('journal_user', '=', True), ('type','=','cash')], context=context)
journal_proxy = self.pool.get('account.journal')
cashids = journal_proxy.search(cr, uid, [('journal_user', '=', True), ('type','=','cash')], context=context)
if not cashids:
cashids = self.pool.get('account.journal').search(cr, uid, [('journal_user','=',True)], context=context)
cashids = journal_proxy.search(cr, uid, [('type', '=', 'cash')], context=context)
if not cashids:
cashids = journal_proxy.search(cr, uid, [('journal_user','=',True)], context=context)
jobj.write(cr, uid, [pos_config.id], {'journal_ids': [(6,0, cashids)]})
+7 -6
View File
@@ -896,8 +896,8 @@
<field name="stop_at" attrs="{'invisible' : [('state', '!=', 'closed')]}"/>
</group>
<newline/>
<group string="Opening Cash Control" attrs="{'invisible' : ['|', ('cash_control', '=', False),('state', 'in', ('opened', 'closing_control'))]}">
<group string="Opening Cash Control" attrs="{'invisible' : [ '&amp;' ,'!', '&amp;', ('state', '=', 'opening_control'), ('cash_control', '=', True), ('state', '!=',
'closed')] }" >
<field name="opening_details_ids" nolabel="1" colspan="2" attrs="{'readonly' : [('state', 'not in', ('opening_control',))]}">
<tree string="Opening Cashbox Lines" editable="bottom">
<field name="pieces" readonly="1" />
@@ -906,7 +906,8 @@
</tree>
</field>
</group>
<group string="Closing Cash Control" attrs="{'invisible': ['|', ('cash_control', '=', False), ('state', 'in', ('opening_control', 'opened'))]}">
<group string="Closing Cash Control" attrs="{'invisible' : [ '&amp;' ,'!', '&amp;', ('state', '=', 'closing_control'), ('cash_control', '=', True), ('state', '!=',
'closed')] }" >
<field name="details_ids" nolabel="1" colspan="2">
<tree string="Cashbox Lines" editable="bottom">
<field name="pieces" readonly="1" />
@@ -916,7 +917,7 @@
</field>
</group>
<div attrs="{'invisible' : ['|', ('cash_control', '=', False),('state', 'in', ('opened', 'closing_control'))]}">
<div attrs="{'invisible' : [('state', '!=', 'closed')]}">
<group class="oe_subtotal_footer oe_right">
<field name="cash_register_balance_start" readonly="1" string="Opening Balance" class="oe_subtotal_footer_separator"/>
<field name="cash_register_total_entry_encoding" attrs="{'invisible' : [('state', '=', 'opening_control')]}" string="+ Transactions"/>
@@ -933,7 +934,7 @@
</div>
<group class="oe_subtotal_footer oe_right" attrs="{'invisible': ['|', ('cash_control', '=', False), ('state', 'in', ('opening_control', 'opened'))]}">
<group class="oe_subtotal_footer oe_right" attrs="{'invisible': [('state', '!=', 'closed')]}">
<field name="cash_register_balance_end_real" class="oe_subtotal_footer_separator"/>
<field name="cash_register_difference" class="oe_subtotal_footer_separator"/>
</group>
@@ -1035,7 +1036,7 @@
<filter icon="gtk-convert" string="Posted" domain="[('state','=','done')]"/>
<field name="user_id"/>
<field name="session_id"/>
<group expand="0" string="Group By..." groups="base.group_extended">
<group expand="0" string="Group By...">
<filter string="Customer" icon="terp-personal" domain="[]" context="{'group_by':'partner_id'}"/>
<filter string="Salesman" icon="terp-personal" domain="[]" context="{'group_by':'user_id'}"/>
<filter string="Session" icon="terp-personal" domain="[]" context="{'group_by':'session_id'}"/>
+1 -1
View File
@@ -43,7 +43,7 @@ class mail_mail_portal(osv.Model):
:param partner: browse_record of the specific recipient partner
"""
if partner:
portal_ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'portal', 'portal')
portal_ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'portal', 'group_portal')
portal_id = portal_ref and portal_ref[1] or False
url = self._generate_signin_url(cr, uid, partner.id, portal_id, 1234, context=context)
body = tools.append_content_to_html(mail.body_html, url)
+4 -186
View File
@@ -20,196 +20,14 @@
##############################################################################
from osv import osv, fields
from tools.translate import _
class portal(osv.osv):
""" A portal is simply a group of users with the flag 'is_portal' set to True.
The flag 'is_portal' makes a user group usable as a portal.
"""
A portal is a group of users with specific menu, widgets, and typically
restricted access rights.
"""
_name = 'res.portal'
_description = 'Portal'
_inherits = {'res.groups': 'group_id'}
_inherit = 'res.groups'
_columns = {
'group_id': fields.many2one('res.groups', required=True, ondelete='cascade',
string='Group',
help='The group corresponding to this portal'),
'url': fields.char('URL',
help="The url where portal users can connect to the server"),
'home_action_id': fields.many2one('ir.actions.actions',
string='Home Action',
help="if set, replaces the standard home action (first screen after loggin) for the portal's users"),
'menu_action_id': fields.many2one('ir.actions.act_window', readonly=True,
# ISSUE: 'ondelete' constraints do not seem effective on this field...
string='Menu Action',
help="If set, replaces the standard menu for the portal's users"),
'parent_menu_id': fields.many2one('ir.ui.menu', ondelete='restrict',
string='Parent Menu',
help='The menu action opens the submenus of this menu item'),
'widget_ids': fields.one2many('res.portal.widget', 'portal_id',
string='Widgets',
help='Widgets assigned to portal users'),
'is_portal': fields.boolean('Portal', help="If checked, this group is usable as a portal."),
}
def copy(self, cr, uid, id, values, context=None):
""" override copy(): menu_action_id must be different """
values['menu_action_id'] = None
return super(portal, self).copy(cr, uid, id, values, context)
def create(self, cr, uid, values, context=None):
""" extend create() to assign the portal menu to users """
if context is None:
context = {}
# create portal (admin should not be included)
context['noadmin'] = True
portal_id = super(portal, self).create(cr, uid, values, context)
# assign menu action and widgets to users
if values.get('users') or values.get('menu_action_id'):
self._assign_menu(cr, uid, [portal_id], context)
if values.get('users') or values.get('widget_ids'):
self._assign_widgets(cr, uid, [portal_id], context)
return portal_id
def write(self, cr, uid, ids, values, context=None):
""" extend write() to reflect changes on users """
# first apply portal changes
super(portal, self).write(cr, uid, ids, values, context)
# assign menu action and widgets to users
if values.get('users') or values.get('menu_action_id'):
self._assign_menu(cr, uid, ids, context)
if values.get('users') or values.get('widget_ids'):
self._assign_widgets(cr, uid, ids, context)
# if parent_menu_id has changed, apply the change on menu_action_id
if 'parent_menu_id' in values:
act_window_obj = self.pool.get('ir.actions.act_window')
portals = self.browse(cr, uid, ids, context)
action_ids = [p.menu_action_id.id for p in portals if p.menu_action_id]
if action_ids:
action_values = {'domain': [('parent_id', '=', values['parent_menu_id'])]}
act_window_obj.write(cr, uid, action_ids, action_values, context)
return True
def _assign_menu(self, cr, uid, ids, context=None):
""" assign portal_menu_settings to users of portals (ids) """
user_obj = self.pool.get('res.users')
for p in self.browse(cr, uid, ids, context):
# user menu action = portal menu action if set in portal
if p.menu_action_id:
user_ids = [u.id for u in p.users if u.id != 1]
user_values = {'menu_id': p.menu_action_id.id}
user_obj.write(cr, uid, user_ids, user_values, context)
def _assign_widgets(self, cr, uid, ids, context=None):
""" assign portal widgets to users of portals (ids) """
widget_user_obj = self.pool.get('res.widget.user')
for p in self.browse(cr, uid, ids, context):
for w in p.widget_ids:
values = {'sequence': w.sequence, 'widget_id': w.widget_id.id}
for u in p.users:
if u.id == 1: continue
values['user_id'] = u.id
widget_user_obj.create(cr, uid, values, context)
def _res_xml_id(self, cr, uid, module, xml_id):
""" return the resource id associated to the given xml_id """
data_obj = self.pool.get('ir.model.data')
data_id = data_obj._get_id(cr, uid, module, xml_id)
return data_obj.browse(cr, uid, data_id).res_id
portal()
class portal_override_menu(osv.osv):
"""
Extend res.portal with a boolean field 'Override Users Menu', that
triggers the creation or removal of menu_action_id
"""
_name = 'res.portal'
_inherit = 'res.portal'
def _get_override_menu(self, cr, uid, ids, field_name, arg, context=None):
assert field_name == 'override_menu'
result = {}
for p in self.browse(cr, uid, ids, context):
result[p.id] = bool(p.menu_action_id)
return result
def _set_override_menu(self, cr, uid, id, field_name, field_value, arg, context=None):
assert field_name == 'override_menu'
if field_value:
self.create_menu_action(cr, uid, id, context)
else:
self.write(cr, uid, [id], {'menu_action_id': False}, context)
def create_menu_action(self, cr, uid, id, context=None):
""" create, if necessary, a menu action that opens the menu items below
parent_menu_id """
p = self.browse(cr, uid, id, context)
if not p.menu_action_id:
actions_obj = self.pool.get('ir.actions.act_window')
parent_id = p.parent_menu_id.id if p.parent_menu_id else False
action_values = {
'name': _('%s Menu') % p.name,
'type': 'ir.actions.act_window',
'usage': 'menu',
'res_model': 'ir.ui.menu',
'view_type': 'tree',
'view_id': self._res_xml_id(cr, uid, 'base', 'view_menu'),
'domain': [('parent_id', '=', parent_id)],
}
action_id = actions_obj.create(cr, uid, action_values, context)
self.write(cr, uid, [id], {'menu_action_id': action_id}, context)
_columns = {
'override_menu': fields.function(
_get_override_menu, fnct_inv=_set_override_menu,
type='boolean', string='Override Menu Action of Users',
help='Enable this option to override the Menu Action of portal users'),
}
portal_override_menu()
class portal_widget(osv.osv):
"""
Similar to res.widget.user (res_widget.py), but with a portal instead.
New users in a portal are assigned the portal's widgets.
"""
_name='res.portal.widget'
_description = 'Portal Widgets'
_order = 'sequence'
_columns = {
'sequence': fields.integer('Sequence'),
'portal_id': fields.many2one('res.portal', select=1, ondelete='cascade',
string='Portal'),
'widget_id': fields.many2one('res.widget', required=True, ondelete='cascade',
string='Widget'),
}
def create(self, cr, uid, values, context=None):
domain = [('portal_id', '=', values.get('portal_id')),
('widget_id', '=', values.get('widget_id'))]
existing = self.search(cr, uid, domain, context=context)
if existing:
res = existing[0]
else:
res = super(portal_widget, self).create(cr, uid, values, context=context)
return res
portal_widget()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
-8
View File
@@ -2,14 +2,6 @@
<openerp>
<data noupdate="1">
<record id="portal" model="res.portal">
<field name="name">Portal</field>
<field name="group_id" ref="group_portal_member"/>
<field name="override_menu" eval="False"/>
<!-- Insert the template user from auth_signup in the portal members group -->
<field name="users" eval="[(4,ref('auth_signup.default_template_user'))]"/>
</record>
<!-- Mail group for the company's news -->
<record id="company_news_feed" model="mail.group">
<field name="name">Company's news</field>
+1 -1
View File
@@ -13,7 +13,7 @@
</record>
<!-- Add the demo user to the portal (and therefore to the portal member group) -->
<record id="portal" model="res.portal">
<record id="group_portal" model="res.groups">
<field name="users" eval="[(4,ref('demo_user0'))]"/>
</record>
+17 -57
View File
@@ -5,7 +5,7 @@
<!-- Top menu item -->
<menuitem name="Portal"
id="portal_menu"
groups="base.group_no_one,portal.group_portal_member,auth_anonymous.group_anonymous"
groups="base.group_no_one,portal.group_portal,auth_anonymous.group_anonymous"
sequence="20"/>
<menuitem name="Our company" id="portal_company" parent="portal_menu" sequence="10"/>
@@ -21,71 +21,31 @@
<menuitem name="Projects" id="portal_projects" parent="portal_menu" sequence="40"/>
<menuitem name="After Sale Services" id="portal_after_sales" parent="portal_menu" sequence="50"/>
<!-- portal tree view -->
<record id="portal_list_view" model="ir.ui.view">
<field name="name">Portal List</field>
<field name="model">res.portal</field>
<!-- extend res.groups search view -->
<record id="group_search_view" model="ir.ui.view">
<field name="name">Group Search</field>
<field name="model">res.groups</field>
<field name="inherit_id" ref="base.view_groups_search"/>
<field name="arch" type="xml">
<tree string="Portals">
<field name="name" string="Portal Name"/>
</tree>
<field name="name" position="after">
<filter name="portal" string="Portal Groups" domain="[('is_portal','=',True)]"/>
<filter name="no_portal" string="Non-Portal Groups" domain="[('is_portal','=',False)]"/>
<separator orientation="vertical"/>
</field>
</field>
</record>
<!-- portal form view -->
<record id="portal_form_view" model="ir.ui.view">
<!-- extend res.groups form view -->
<record id="group_form_view" model="ir.ui.view">
<field name="name">Portal Form</field>
<field name="model">res.portal</field>
<field name="model">res.groups</field>
<field name="inherit_id" ref="base.view_groups_form"/>
<field name="arch" type="xml">
<page string="Users" position="before">
<page string="Portal">
<group>
<group>
<field name="url" widget="url" string="Portal URL"/>
<field name="home_action_id"/>
</group>
<group>
<field name="override_menu"/>
<field name="parent_menu_id"
context="{'ir.ui.menu.full_list': True}"/>
<label for="partent_menu_id" colspan="1"/>
</group>
<separator string="Widgets Assigned to Users" colspan="2"/>
<field name="widget_ids" nolabel="1" colspan="2">
<tree string="Widgets" editable="bottom">
<field name="sequence"/>
<field name="widget_id"/>
</tree>
</field>
</group>
</page>
</page>
<field name="name" position="after">
<field name="is_portal"/>
</field>
</field>
</record>
<!-- action Administration/Portals/Portals -->
<record id="portal_list_action" model="ir.actions.act_window">
<field name="name">Portals</field>
<field name="res_model">res.portal</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<!-- a context is necessary to get the right portal form view -->
<field name="context">{'form_view_ref': 'portal.portal_form_view'}</field>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to create a new portal.
</p><p>
A portal helps defining specific views and rules for a group of
users (the portal group). A portal menu, widgets and specific
groups may be assigned to the portal's users.
</p>
</field>
</record>
<!-- menu Administration/Portals/Portals -->
<menuitem name="Portals Settings" id="portal_menu_settings" parent="base.menu_administration" groups="group_portal_manager"/>
<menuitem name="Portals" id="portal_list_menu" parent="portal_menu_settings" action="portal_list_action"/>
</data>
</openerp>
+7 -11
View File
@@ -1,12 +1,8 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_all,access.portal.all,model_res_portal,,1,0,0,0
access_widget_all,access.portal.widget.all,model_res_portal_widget,,1,0,0,0
access_manager,access.portal.manager,model_res_portal,group_portal_manager,1,1,1,1
access_widget_manager,access.portal.widget.manager,model_res_portal_widget,group_portal_manager,1,1,1,1
access_mail_message,mail.message,mail.model_mail_message,group_portal_member,1,0,1,1
access_mail_message_all,mail.message.all,mail.model_mail_message,group_portal_member,1,0,0,0
access_mail_thread,mail.thread,mail.model_mail_thread,group_portal_member,1,0,0,0
access_mail_followers,mail.followers,mail.model_mail_followers,group_portal_member,1,0,1,1
access_mail_notification,mail.notification,mail.model_mail_notification,group_portal_member,1,0,1,0
access_mail_group,mail.group,mail.model_mail_group,group_portal_member,1,0,0,0
access_mail_alias,mail.alias,mail.model_mail_alias,group_portal_member,1,0,0,0
access_mail_message,mail.message,mail.model_mail_message,group_portal,1,0,1,1
access_mail_message_all,mail.message.all,mail.model_mail_message,group_portal,1,0,0,0
access_mail_thread,mail.thread,mail.model_mail_thread,group_portal,1,0,0,0
access_mail_followers,mail.followers,mail.model_mail_followers,group_portal,1,0,1,1
access_mail_notification,mail.notification,mail.model_mail_notification,group_portal,1,0,1,0
access_mail_group,mail.group,mail.model_mail_group,group_portal,1,0,0,0
access_mail_alias,mail.alias,mail.model_mail_alias,group_portal,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_all access_mail_message access.portal.all mail.message model_res_portal mail.model_mail_message group_portal 1 0 0 1 0 1
3 access_widget_all access_mail_message_all access.portal.widget.all mail.message.all model_res_portal_widget mail.model_mail_message group_portal 1 0 0 0
4 access_manager access_mail_thread access.portal.manager mail.thread model_res_portal mail.model_mail_thread group_portal_manager group_portal 1 1 0 1 0 1 0
5 access_widget_manager access_mail_followers access.portal.widget.manager mail.followers model_res_portal_widget mail.model_mail_followers group_portal_manager group_portal 1 1 0 1 1
6 access_mail_message access_mail_notification mail.message mail.notification mail.model_mail_message mail.model_mail_notification group_portal_member group_portal 1 0 1 1 0
7 access_mail_message_all access_mail_group mail.message.all mail.group mail.model_mail_message mail.model_mail_group group_portal_member group_portal 1 0 0 0
8 access_mail_thread access_mail_alias mail.thread mail.alias mail.model_mail_thread mail.model_mail_alias group_portal_member group_portal 1 0 0 0
access_mail_followers mail.followers mail.model_mail_followers group_portal_member 1 0 1 1
access_mail_notification mail.notification mail.model_mail_notification group_portal_member 1 0 1 0
access_mail_group mail.group mail.model_mail_group group_portal_member 1 0 0 0
access_mail_alias mail.alias mail.model_mail_alias group_portal_member 1 0 0 0
+7 -24
View File
@@ -2,31 +2,14 @@
<openerp>
<data noupdate="1">
<!-- Don't use any implied_ids here; this group's behavior is particular -->
<record id="group_portal_member" model="res.groups">
<field name="name">Portal Member</field>
<field name="comment">Portal members can access information through the portal menu. Also, they are chrooted in this specific menu.</field>
</record>
<record id="module_category_portal" model="ir.module.category">
<!-- The portal group -->
<record id="group_portal" model="res.groups">
<field name="name">Portal</field>
<field name="parent_id" ref="base.module_category_tools"/>
<field name="sequence">25</field>
</record>
<!-- No implied ids with 'member' since this group is highly restrictive (chrooted menu) -->
<record id="group_portal_officer" model="res.groups">
<field name="name">Officer</field>
<field name="comment">Portal officers can create new portal users with the portal wizard.</field>
<field name="category_id" ref="module_category_portal"/>
</record>
<record id="group_portal_manager" model="res.groups">
<field name="name">Manager</field>
<field name="comment">Portal managers have access to the portal definitions, and can easily configure the users, access rights and menus of portal users.</field>
<field name="category_id" ref="module_category_portal"/>
<field name="implied_ids" eval="[(4, ref('group_portal_officer'))]"/>
<field name="users" eval="[(4, ref('base.user_root'))]"/>
<field name="comment">Portal members have specific access rights (such as record rules and restricted menus).
They usually do not belong to the usual OpenERP groups.</field>
<field name="is_portal" eval="True"/>
<!-- Insert the template user from auth_signup in the portal members group -->
<field name="users" eval="[(4,ref('auth_signup.default_template_user'))]"/>
</record>
</data>
+1 -1
View File
@@ -42,7 +42,7 @@ class test_portal(test_mail.TestMailMockups):
user_admin = self.res_users.browse(cr, uid, uid)
self.mail_invite = self.registry('mail.wizard.invite')
base_url = self.registry('ir.config_parameter').get_param(cr, uid, 'web.base.url', default='')
portal_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'portal', 'portal')
portal_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'portal', 'group_portal')
portal_id = portal_ref and portal_ref[1] or False
# 0 - Admin
+161 -147
View File
@@ -23,33 +23,43 @@ import logging
import random
from osv import osv, fields
from tools.misc import email_re
from tools.translate import _
from tools.misc import email_re
from openerp import SUPERUSER_ID
from base.res.res_partner import _lang_get
_logger = logging.getLogger(__name__)
# welcome email sent to new portal users (note that calling tools.translate._
# has no effect except exporting those strings for translation)
# welcome/goodbye email sent to portal users
# (note that calling '_' has no effect except exporting those strings for translation)
WELCOME_EMAIL_SUBJECT = _("Your OpenERP account at %(company)s")
WELCOME_EMAIL_BODY = _("""Dear %(name)s,
You have been created an OpenERP account at %(url)s.
You have been given access to %(portal)s at %(url)s.
Your login account data is:
Database: %(db)s
User: %(login)s
Password: %(password)s
%(message)s
%(welcome_message)s
--
OpenERP - Open Source Business Applications
http://www.openerp.com
""")
ROOT_UID = 1
GOODBYE_EMAIL_SUBJECT = _("Your OpenERP account at %(company)s")
GOODBYE_EMAIL_BODY = _("""Dear %(name)s,
Your access to %(portal)s has been withdrawn.
%(goodbye_message)s
--
OpenERP - Open Source Business Applications
http://www.openerp.com
""")
# character sets for passwords, excluding 0, O, o, 1, I, l
_PASSU = 'ABCDEFGHIJKLMNPQRSTUVWXYZ'
@@ -71,169 +81,173 @@ def extract_email(email):
class wizard(osv.osv_memory):
"""
A wizard to create portal users from instances of 'res.partner'. The purpose
is to provide an OpenERP database access to customers or suppliers.
A wizard to manage the creation/removal of portal users.
"""
_name = 'res.portal.wizard'
_description = 'Portal Wizard'
_name = 'portal.wizard'
_description = 'Portal Access Management'
_columns = {
'portal_id': fields.many2one('res.portal', required=True,
string='Portal',
help="The portal in which new users must be added"),
'user_ids': fields.one2many('res.portal.wizard.user', 'wizard_id',
string='Users'),
'message': fields.text(string='Invitation message',
help="This text is included in the welcome email sent to the users"),
'portal_id': fields.many2one('res.groups', domain=[('is_portal', '=', True)], required=True,
string='Portal', help="The portal that users can be added in or removed from."),
'user_ids': fields.one2many('portal.wizard.user', 'wizard_id', string='Users'),
'welcome_message': fields.text(string='Invitation Message',
help="This text is included in the email sent to new users of the portal."),
'goodbye_message': fields.text(string='Withdrawal Message',
help="This text is included in the email sent to users withdrawn from the portal."),
}
def _default_user_ids(self, cr, uid, context):
""" determine default user_ids from the active records """
def create_user_from_address(address):
if isinstance(address, int):
res_partner_obj = self.pool.get('res.partner')
address = res_partner_obj.browse(cr, uid, address, context=context)
lang = address.id and address.lang or 'en_US'
partner_id = address.id
else:
lang = address.parent_id and address.parent_id.lang or 'en_US'
partner_id = address.parent_id and address.parent_id.id
return{
'name': address.name,
'email': extract_email(address.email),
'lang': lang,
'partner_id': partner_id,
}
user_ids = []
if context.get('active_model') == 'res.partner':
partner_obj = self.pool.get('res.partner')
partner_ids = context.get('active_ids', [])
partners = partner_obj.browse(cr, uid, partner_ids, context)
for p in partners:
# add one user per contact, or one user if no contact
if p.child_ids:
user_ids.extend(map(create_user_from_address, p.child_ids))
elif p.is_company == False and p.customer == True:
user_ids.extend(map(create_user_from_address, [p.id]))
else:
user_ids.append({'lang': p.lang or 'en_US', 'parent_id': p.id})
return user_ids
def _default_portal(self, cr, uid, context):
portal_ids = self.pool.get('res.groups').search(cr, uid, [('is_portal', '=', True)])
return portal_ids and portal_ids[0] or False
_defaults = {
'user_ids': _default_user_ids
'portal_id': _default_portal,
}
def action_create(self, cr, uid, ids, context=None):
""" create new users in portal(s), and notify them by email """
# we copy the context to change the language for translating emails
context0 = context or {}
context0['noshortcut'] = True # prevent shortcut creation
context = context0.copy()
user_obj = self.pool.get('res.users')
user = user_obj.browse(cr, ROOT_UID, uid, context0)
if not user.email:
raise osv.except_osv(_('Email required'),
_('You must have an email address in your User Preferences'
' to send emails.'))
portal_obj = self.pool.get('res.portal')
for wiz in self.browse(cr, uid, ids, context):
# determine existing users
login_cond = [('login', 'in', [u.email for u in wiz.user_ids])]
existing_uids = user_obj.search(cr, ROOT_UID, login_cond)
existing_users = user_obj.browse(cr, ROOT_UID, existing_uids)
existing_logins = [u.login for u in existing_users]
# create new users in portal (skip existing logins)
new_users_data = [ {
'name': u.name,
'login': u.email,
'password': random_password(),
'email': u.email,
'lang': u.lang,
'share': True,
'action_id': wiz.portal_id.home_action_id and wiz.portal_id.home_action_id.id or False,
'partner_id': u.partner_id and u.partner_id.id,
'groups_id': [(6, 0, [])],
} for u in wiz.user_ids if u.email not in existing_logins ]
portal_obj.write(cr, ROOT_UID, [wiz.portal_id.id],
{'users': [(0, 0, data) for data in new_users_data]}, context0)
# send email to all users (translated in their language)
data = {
'company': user.company_id.name,
'message': wiz.message or "",
'url': wiz.portal_id.url or _("(missing url)"),
'db': cr.dbname,
}
mail_mail_obj = self.pool.get('mail.mail')
dest_uids = user_obj.search(cr, ROOT_UID, login_cond)
dest_users = user_obj.browse(cr, ROOT_UID, dest_uids)
for dest_user in dest_users:
context['lang'] = dest_user.lang
data['login'] = dest_user.login
data['password'] = dest_user.password
data['name'] = dest_user.name
email_from = user.email
email_to = dest_user.email
subject = _(WELCOME_EMAIL_SUBJECT) % data
body = _(WELCOME_EMAIL_BODY) % data
mail_id = mail_mail_obj.create(cr, uid, {
'email_from': email_from ,
'email_to': email_to,
'subject': subject,
'state': 'outgoing',
'body_html': '<pre>%s</pre>' % body}, context=context)
def onchange_portal_id(self, cr, uid, ids, portal_id, context=None):
# for each partner, determine corresponding portal.wizard.user records
res_partner = self.pool.get('res.partner')
partner_ids = context and context.get('active_ids') or []
contact_ids = set()
user_changes = []
for partner in res_partner.browse(cr, SUPERUSER_ID, partner_ids, context):
for contact in (partner.child_ids or [partner]):
# make sure that each contact appears at most once in the list
if contact.id not in contact_ids:
contact_ids.add(contact.id)
in_portal = False
if contact.user_ids:
in_portal = portal_id in [g.id for g in contact.user_ids[0].groups_id]
user_changes.append((0, 0, {
'partner_id': contact.id,
'email': contact.email,
'in_portal': in_portal,
}))
return {'value': {'user_ids': user_changes}}
def action_apply(self, cr, uid, ids, context=None):
wizard = self.browse(cr, uid, ids[0], context)
portal_user_ids = [user.id for user in wizard.user_ids]
self.pool.get('portal.wizard.user').action_apply(cr, uid, portal_user_ids, context)
return {'type': 'ir.actions.act_window_close'}
wizard()
class wizard_user(osv.osv_memory):
"""
A model to configure users in the portal wizard.
"""
_name = 'res.portal.wizard.user'
_name = 'portal.wizard.user'
_description = 'Portal User Config'
_columns = {
'wizard_id': fields.many2one('res.portal.wizard', required=True,
string='Wizard'),
'name': fields.char(size=64, required=True,
string='User Name',
help="The user's real name"),
'email': fields.char(size=64, required=True,
string='Email',
help="Will be used as user login. "
"Also necessary to send the account information to new users"),
'lang': fields.selection(_lang_get, required=True,
string='Language',
help="The language for the user's user interface"),
'partner_id': fields.many2one('res.partner',
string='Partner'),
'wizard_id': fields.many2one('portal.wizard', string='Wizard', required=True),
'partner_id': fields.many2one('res.partner', string='Contact', required=True, readonly=True),
'email': fields.char(size=240, string='Email'),
'in_portal': fields.boolean('In Portal'),
}
def _check_email(self, cr, uid, ids):
""" check syntax of email address """
for wuser in self.browse(cr, uid, ids):
if not email_re.match(wuser.email): return False
return True
def create(self, cr, uid, values, context=None):
""" overridden to update the partner's email (if necessary) """
id = super(wizard_user, self).create(cr, uid, values, context)
wuser = self.browse(cr, uid, id, context)
if wuser.partner_id.email != wuser.email:
wuser.partner_id.write({'email': wuser.email})
return id
_constraints = [
(_check_email, 'Invalid email address', ['email']),
]
def action_apply(self, cr, uid, ids, context=None):
res_users = self.pool.get('res.users')
for wizard_user in self.browse(cr, SUPERUSER_ID, ids, context):
portal = wizard_user.wizard_id.portal_id
user = self._retrieve_user(cr, SUPERUSER_ID, wizard_user, context)
if wizard_user.in_portal:
# create a user if necessary, and make sure it is in the portal group
if not user:
user = self._create_user(cr, SUPERUSER_ID, wizard_user, context)
if (not user.active) or (portal not in user.groups_id):
user.write({'active': True, 'groups_id': [(4, portal.id)]})
wizard_user = self.browse(cr, SUPERUSER_ID, wizard_user.id, context)
self._send_email(cr, uid, wizard_user, context)
else:
# remove the user (if it exists) from the portal group
if user and (portal in user.groups_id):
# if user belongs to portal only, deactivate it
if len(user.groups_id) <= 1:
user.write({'groups_id': [(3, portal.id)], 'active': False})
else:
user.write({'groups_id': [(3, portal.id)]})
wizard_user = self.browse(cr, SUPERUSER_ID, wizard_user.id, context)
self._send_email(cr, uid, wizard_user, context)
wizard_user()
def _retrieve_user(self, cr, uid, wizard_user, context=None):
""" retrieve the (possibly inactive) user corresponding to wizard_user.partner_id
@param wizard_user: browse record of model portal.wizard.user
@return: browse record of model res.users
"""
if wizard_user.partner_id.user_ids:
return wizard_user.partner_id.user_ids[0]
# the user may be inactive, search for it
res_users = self.pool.get('res.users')
domain = [('partner_id', '=', wizard_user.partner_id.id), ('active', '=', False)]
user_ids = res_users.search(cr, uid, domain)
return user_ids and res_users.browse(cr, uid, user_ids[0], context) or False
def _create_user(self, cr, uid, wizard_user, context=None):
""" create a new user for wizard_user.partner_id
@param wizard_user: browse record of model portal.wizard.user
@return: browse record of model res.users
"""
res_users = self.pool.get('res.users')
create_context = dict(context or {}, noshortcut=True) # to prevent shortcut creation
values = {
'login': extract_email(wizard_user.email),
'password': random_password(),
'partner_id': wizard_user.partner_id.id,
'groups_id': [(6, 0, [])],
'share': True,
}
user_id = res_users.create(cr, uid, values, context=create_context)
return res_users.browse(cr, uid, user_id, context)
def _send_email(self, cr, uid, wizard_user, context=None):
""" send notification email to a new/former portal user
@param wizard_user: browse record of model portal.wizard.user
@return: the id of the created mail.mail record
"""
this_context = context
this_user = self.pool.get('res.users').browse(cr, SUPERUSER_ID, uid, context)
if not this_user.email:
raise osv.except_osv(_('Email required'),
_('You must have an email address in your User Preferences to send emails.'))
# determine subject and body in the portal user's language
url = self.pool.get('ir.config_parameter').get_param(cr, SUPERUSER_ID, 'web.base.url', context=this_context)
user = self._retrieve_user(cr, SUPERUSER_ID, wizard_user, context)
context = dict(this_context or {}, lang=user.lang)
data = {
'company': this_user.company_id.name,
'portal': wizard_user.wizard_id.portal_id.name,
'welcome_message': wizard_user.wizard_id.welcome_message or "",
'goodbye_message': wizard_user.wizard_id.goodbye_message or "",
'url': url or _("(missing url)"),
'db': cr.dbname,
'login': user.login,
'password': user.password,
'name': user.name
}
if wizard_user.in_portal:
subject = _(WELCOME_EMAIL_SUBJECT) % data
body = _(WELCOME_EMAIL_BODY) % data
else:
subject = _(GOODBYE_EMAIL_SUBJECT) % data
body = _(GOODBYE_EMAIL_BODY) % data
mail_mail = self.pool.get('mail.mail')
mail_values = {
'email_from': this_user.email,
'email_to': user.email,
'subject': subject,
'body_html': '<pre>%s</pre>' % body,
'state': 'outgoing',
}
return mail_mail.create(cr, uid, mail_values, context=this_context)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+23 -34
View File
@@ -3,28 +3,34 @@
<data>
<!-- wizard action on res.partner -->
<act_window id="partner_wizard_action"
name="Add Portal Access"
name="Portal Access Management"
src_model="res.partner"
res_model="res.portal.wizard"
res_model="portal.wizard"
view_type="form" view_mode="form"
key2="client_action_multi" target="new"
groups="group_portal_officer"/>
groups="base.group_partner_manager"/>
<!-- wizard view -->
<record id="wizard_view" model="ir.ui.view">
<field name="name">Add Portal Access</field>
<field name="model">res.portal.wizard</field>
<field name="name">Portal Access Management</field>
<field name="model">portal.wizard</field>
<field name="arch" type="xml">
<form string="Add Portal Access" version="7.0">
<group col="4">
<field name="portal_id" widget="selection"/>
<form string="Portal Access Management" version="7.0">
<group>
<field name="portal_id" widget="selection" on_change="onchange_portal_id(portal_id, context)"/>
</group>
<div>
Select which contacts should belong to the portal in the list below.
The email address of each selected contact must be valid and unique.
If necessary, you can fix any contact's email address directly in the list.
</div>
<field name="user_ids"/>
<label string="The following text will be included in the welcome email sent to users."/>
<field name="message"/>
<field name="welcome_message"
placeholder="This text is included in the email sent to new portal users."/>
<field name="goodbye_message"
placeholder="This text is included in the email sent to users withdrawn from the portal."/>
<footer>
<button string="Send Invitations"
name="action_create" type="object" class="oe_highlight" />
<button string="Apply" name="action_apply" type="object" class="oe_highlight"/>
or
<button string="Cancel" class="oe_link" special="cancel" />
</footer>
@@ -35,32 +41,15 @@
<!-- wizard user list view -->
<record id="wizard_user_tree_view" model="ir.ui.view">
<field name="name">Portal Users</field>
<field name="model">res.portal.wizard.user</field>
<field name="model">portal.wizard.user</field>
<field name="arch" type="xml">
<!-- the attribute 'editable' is set below to make the elements
editable in the web client 6.0 -->
<tree string="Portal Users" editable="bottom">
<field name="name"/>
<field name="email"/>
<!-- the contact list is editable, but one cannot add or delete rows -->
<tree string="Contacts" editable="bottom" create="false" delete="false">
<field name="partner_id"/>
<field name="email"/>
<field name="in_portal"/>
</tree>
</field>
</record>
<!-- wizard user form view -->
<record id="wizard_user_form_view" model="ir.ui.view">
<field name="name">Portal User</field>
<field name="model">res.portal.wizard.user</field>
<field name="arch" type="xml">
<form string="Portal User" version="7.0">
<group colspan="2" col="2">
<field name="name"/>
<field name="email"/>
<field name="lang"/>
<field name="partner_id"/>
</group>
</form>
</field>
</record>
</data>
</openerp>
+3 -8
View File
@@ -44,9 +44,6 @@ class share_wizard_portal(osv.TransientModel):
'group_ids': fields.many2many('res.groups', 'share_wizard_res_group_rel', 'share_id', 'group_id', 'Existing groups', domain=[('share', '=', False)]),
}
def is_portal_manager(self, cr, uid, context=None):
return self.has_group(cr, uid, module='portal', group_xml_id='group_portal_manager', context=context)
def _check_preconditions(self, cr, uid, wizard_data, context=None):
if wizard_data.user_type == 'existing':
self._assert(wizard_data.user_ids,
@@ -122,16 +119,14 @@ class share_wizard_portal(osv.TransientModel):
# alter the rules of the groups so they can see the shared data
if wizard_data.group_ids:
# get the list of portals and the related groups to install their menus.
Portals = self.pool.get('res.portal')
all_portals = Portals.browse(cr, UID_ROOT, Portals.search(cr, UID_ROOT, [])) #no context!
all_portal_group_ids = [p.group_id.id for p in all_portals]
res_groups = self.pool.get('res.groups')
all_portal_group_ids = res_groups.search(cr, UID_ROOT, [('is_portal', '=', True)])
# populate result lines with the users of each group and
# setup the menu for portal groups
for group in wizard_data.group_ids:
if group.id in all_portal_group_ids:
portal = all_portals[all_portal_group_ids.index(group.id)]
self._create_shared_data_menu(cr, uid, wizard_data, portal, context=context)
self._create_shared_data_menu(cr, uid, wizard_data, group.id, context=context)
for user in group.users:
new_line = {'user_id': user.id,
+3 -3
View File
@@ -36,12 +36,12 @@
<field name="inherit_id" ref="share.share_step2_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='result_line_ids']//field[@name='login']" position="after">
<field name="share_url" groups="portal.group_portal_manager"/>
<field name="share_url" groups="base.group_no_one"/>
</xpath>
<xpath expr="//field[@name='result_line_ids']" position="after">
<newline/>
<group string="Details">
<field name="share_root_url" groups="portal.group_portal_manager"/>
<group string="Details" groups="base.group_no_one">
<field name="share_root_url"/>
</group>
</xpath>
</field>
@@ -1,3 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_crm_claim,crm.claim,crm_claim.model_crm_claim,portal.group_portal_member,1,0,0,0
access_crm_claim_stage,crm.claim.stage,crm_claim.model_crm_claim_stage,portal.group_portal_member,1,0,0,0
access_crm_claim,crm.claim,crm_claim.model_crm_claim,portal.group_portal,1,0,0,0
access_crm_claim_stage,crm.claim.stage,crm_claim.model_crm_claim_stage,portal.group_portal,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_crm_claim crm.claim crm_claim.model_crm_claim portal.group_portal_member portal.group_portal 1 0 0 0
3 access_crm_claim_stage crm.claim.stage crm_claim.model_crm_claim_stage portal.group_portal_member portal.group_portal 1 0 0 0
@@ -6,7 +6,7 @@
<field name="name">Portal Personal Claims</field>
<field ref="crm_claim.model_crm_claim" name="model_id"/>
<field name="domain_force">[('partner_id','child_of',user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('portal.group_portal_member'))]"/>
<field name="groups" eval="[(4, ref('portal.group_portal'))]"/>
</record>
</data>
@@ -1,3 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_event,event,event.model_event_event,portal.group_portal_member,1,0,0,0
access_registration,registration,event.model_event_registration,portal.group_portal_member,1,0,0,0
access_event,event,event.model_event_event,portal.group_portal,1,0,0,0
access_registration,registration,event.model_event_registration,portal.group_portal,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_event event event.model_event_event portal.group_portal_member portal.group_portal 1 0 0 0
3 access_registration registration event.model_event_registration portal.group_portal_member portal.group_portal 1 0 0 0
@@ -6,14 +6,14 @@
<field name="name">Portal Visible Events</field>
<field ref="event.model_event_event" name="model_id"/>
<field name="domain_force">[('visibility', '=', 'public')]</field>
<field name="groups" eval="[(4, ref('portal.group_portal_member'))]"/>
<field name="groups" eval="[(4, ref('portal.group_portal'))]"/>
</record>
<record id="portal_registration_rule" model="ir.rule">
<field name="name">Portal Personal Registrations</field>
<field ref="event.model_event_registration" name="model_id"/>
<field name="domain_force">[('partner_id','child_of',user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('portal.group_portal_member'))]"/>
<field name="groups" eval="[(4, ref('portal.group_portal'))]"/>
</record>
</data>
@@ -1,6 +1,6 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_hr_employee_user,hr.employee user,hr.model_hr_employee,portal.group_portal_member,1,0,0,0
access_res_partner,res.partner,base.model_res_partner,portal.group_portal_member,1,0,0,0
access_res_partner_address,res.partner_address,base.model_res_partner_address,portal.group_portal_member,1,0,0,0
access_res_partner_category,res.partner_category,base.model_res_partner_category,portal.group_portal_member,1,0,0,0
access_res_partner_title,res.partner_title,base.model_res_partner_title,portal.group_portal_member,1,0,0,0
access_hr_employee_user,hr.employee user,hr.model_hr_employee,portal.group_portal,1,0,0,0
access_res_partner,res.partner,base.model_res_partner,portal.group_portal,1,0,0,0
access_res_partner_address,res.partner_address,base.model_res_partner_address,portal.group_portal,1,0,0,0
access_res_partner_category,res.partner_category,base.model_res_partner_category,portal.group_portal,1,0,0,0
access_res_partner_title,res.partner_title,base.model_res_partner_title,portal.group_portal,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_hr_employee_user hr.employee user hr.model_hr_employee portal.group_portal_member portal.group_portal 1 0 0 0
3 access_res_partner res.partner base.model_res_partner portal.group_portal_member portal.group_portal 1 0 0 0
4 access_res_partner_address res.partner_address base.model_res_partner_address portal.group_portal_member portal.group_portal 1 0 0 0
5 access_res_partner_category res.partner_category base.model_res_partner_category portal.group_portal_member portal.group_portal 1 0 0 0
6 access_res_partner_title res.partner_title base.model_res_partner_title portal.group_portal_member portal.group_portal 1 0 0 0
@@ -1,5 +1,5 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_task,tasks,project.model_project_task,portal.group_portal_member,1,0,0,0
access_task_type,task_type,project.model_project_task_type,portal.group_portal_member,1,0,0,0
access_task_work,task_work,project.model_project_task_work,portal.group_portal_member,1,0,0,0
access_project_category,project_category,project.model_project_category,portal.group_portal_member,1,0,0,0
access_task,tasks,project.model_project_task,portal.group_portal,1,0,0,0
access_task_type,task_type,project.model_project_task_type,portal.group_portal,1,0,0,0
access_task_work,task_work,project.model_project_task_work,portal.group_portal,1,0,0,0
access_project_category,project_category,project.model_project_category,portal.group_portal,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_task tasks project.model_project_task portal.group_portal_member portal.group_portal 1 0 0 0
3 access_task_type task_type project.model_project_task_type portal.group_portal_member portal.group_portal 1 0 0 0
4 access_task_work task_work project.model_project_task_work portal.group_portal_member portal.group_portal 1 0 0 0
5 access_project_category project_category project.model_project_category portal.group_portal_member portal.group_portal 1 0 0 0
@@ -6,7 +6,7 @@
<field name="name">Portal Personal Task</field>
<field ref="project.model_project_task" name="model_id"/>
<field name="domain_force">[('partner_id','child_of',user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('portal.group_portal_member'))]"/>
<field name="groups" eval="[(4, ref('portal.group_portal'))]"/>
</record>
</data>
@@ -27,6 +27,7 @@
<t t-foreach="record.categ_ids.raw_value" t-as="categ_id">
<span class="oe_tag" t-att-data-categ_id="categ_id"></span>
</t>
<field name="categ_ids"/>
</div>
<div class="oe_right">
Creation: <field name="create_date"/>
@@ -1,3 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_issues,project_issue,project_issue.model_project_issue,portal.group_portal_member,1,0,0,0
access_case_section,crm_case_section,crm.model_crm_case_section,portal.group_portal_member,1,0,0,0
access_issues,project_issue,project_issue.model_project_issue,portal.group_portal,1,0,0,0
access_case_section,crm_case_section,crm.model_crm_case_section,portal.group_portal,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_issues project_issue project_issue.model_project_issue portal.group_portal_member portal.group_portal 1 0 0 0
3 access_case_section crm_case_section crm.model_crm_case_section portal.group_portal_member portal.group_portal 1 0 0 0
@@ -6,7 +6,7 @@
<field name="name">Portal Personal Issues</field>
<field ref="project_issue.model_project_issue" name="model_id"/>
<field name="domain_force">[('partner_id','child_of',user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('portal.group_portal_member'))]"/>
<field name="groups" eval="[(4, ref('portal.group_portal'))]"/>
</record>
</data>
+32 -32
View File
@@ -1,33 +1,33 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_sale_order,sale.order,sale.model_sale_order,portal.group_portal_member,1,0,0,0
access_sale_order_line,sale.order.line,sale.model_sale_order_line,portal.group_portal_member,1,0,0,0
access_stock_picking,stock.picking,stock.model_stock_picking,portal.group_portal_member,1,0,0,0
access_stock_picking.out,stock.picking.out,stock.model_stock_picking_out,portal.group_portal_member,1,0,0,0
access_stock_move,stock.move,stock.model_stock_move,portal.group_portal_member,1,0,0,0
access_stock_warehouse_orderpoint,stock.warehouse.orderpoint,procurement.model_stock_warehouse_orderpoint,portal.group_portal_member,1,0,0,0
access_account_invoice,account.invoice,account.model_account_invoice,portal.group_portal_member,1,0,0,0
access_account_invoice_tax,account.invoice.tax,account.model_account_invoice_tax,portal.group_portal_member,1,0,0,0
access_account_invoice_line,account.invoice.line,account.model_account_invoice_line,portal.group_portal_member,1,0,0,0
access_account_journal,account.journal,account.model_account_journal,portal.group_portal_member,1,0,0,0
access_account_voucher,account.voucher,account_voucher.model_account_voucher,portal.group_portal_member,1,0,0,0
access_account_voucher_line,account.voucher.line,account_voucher.model_account_voucher_line,portal.group_portal_member,1,0,0,0
access_account_move,account.move,account.model_account_move,portal.group_portal_member,1,0,0,0
access_account_move_line,account.move.line,account.model_account_move_line,portal.group_portal_member,1,0,0,0
access_account_move_reconcile,account.move.reconcile,account.model_account_move_reconcile,portal.group_portal_member,1,0,0,0
access_account_fiscalyear,account.sequence.fiscalyear,account.model_account_sequence_fiscalyear,portal.group_portal_member,1,0,0,0
access_sale_shop,sale.shop,sale.model_sale_shop,portal.group_portal_member,1,0,0,0
access_product_list,product.pricelist,product.model_product_pricelist,portal.group_portal_member,1,0,0,0
access_product,product.product,product.model_product_product,portal.group_portal_member,1,0,0,0
access_res_partner,res.partner,base.model_res_partner,portal.group_portal_member,1,0,0,0
access_product_uom,product.uom,product.model_product_uom,portal.group_portal_member,1,0,0,0
access_account_tax,account.tax,account.model_account_tax,portal.group_portal_member,1,0,0,0
access_mrp_property,mrp.property,procurement.model_mrp_property,portal.group_portal_member,1,0,0,0
access_product_template,product.template,product.model_product_template,portal.group_portal_member,1,0,0,0
access_stock_warehouse,stock.warehouse,stock.model_stock_warehouse,portal.group_portal_member,1,0,0,0
access_stock_location,stock.location,stock.model_stock_location,portal.group_portal_member,1,0,0,0
access_account_fiscalyear,account.fiscalyear,account.model_account_fiscalyear,portal.group_portal_member,1,0,0,0
access_res_partner_category,res.partner.category,base.model_res_partner_category,portal.group_portal_member,1,0,0,0
access_product_supplierinfo,product.supplierinfo,product.model_product_supplierinfo,portal.group_portal_member,1,0,0,0
access_product_packaging,product.packaging,product.model_product_packaging,portal.group_portal_member,1,0,0,0
access_account_period,account.period,account.model_account_period,portal.group_portal_member,1,0,0,0
access_account_account,account.account,account.model_account_account,portal.group_portal_member,1,0,0,0
access_sale_order,sale.order,sale.model_sale_order,portal.group_portal,1,0,0,0
access_sale_order_line,sale.order.line,sale.model_sale_order_line,portal.group_portal,1,0,0,0
access_stock_picking,stock.picking,stock.model_stock_picking,portal.group_portal,1,0,0,0
access_stock_picking.out,stock.picking.out,stock.model_stock_picking_out,portal.group_portal,1,0,0,0
access_stock_move,stock.move,stock.model_stock_move,portal.group_portal,1,0,0,0
access_stock_warehouse_orderpoint,stock.warehouse.orderpoint,procurement.model_stock_warehouse_orderpoint,portal.group_portal,1,0,0,0
access_account_invoice,account.invoice,account.model_account_invoice,portal.group_portal,1,0,0,0
access_account_invoice_tax,account.invoice.tax,account.model_account_invoice_tax,portal.group_portal,1,0,0,0
access_account_invoice_line,account.invoice.line,account.model_account_invoice_line,portal.group_portal,1,0,0,0
access_account_journal,account.journal,account.model_account_journal,portal.group_portal,1,0,0,0
access_account_voucher,account.voucher,account_voucher.model_account_voucher,portal.group_portal,1,0,0,0
access_account_voucher_line,account.voucher.line,account_voucher.model_account_voucher_line,portal.group_portal,1,0,0,0
access_account_move,account.move,account.model_account_move,portal.group_portal,1,0,0,0
access_account_move_line,account.move.line,account.model_account_move_line,portal.group_portal,1,0,0,0
access_account_move_reconcile,account.move.reconcile,account.model_account_move_reconcile,portal.group_portal,1,0,0,0
access_account_fiscalyear,account.sequence.fiscalyear,account.model_account_sequence_fiscalyear,portal.group_portal,1,0,0,0
access_sale_shop,sale.shop,sale.model_sale_shop,portal.group_portal,1,0,0,0
access_product_list,product.pricelist,product.model_product_pricelist,portal.group_portal,1,0,0,0
access_product,product.product,product.model_product_product,portal.group_portal,1,0,0,0
access_res_partner,res.partner,base.model_res_partner,portal.group_portal,1,0,0,0
access_product_uom,product.uom,product.model_product_uom,portal.group_portal,1,0,0,0
access_account_tax,account.tax,account.model_account_tax,portal.group_portal,1,0,0,0
access_mrp_property,mrp.property,procurement.model_mrp_property,portal.group_portal,1,0,0,0
access_product_template,product.template,product.model_product_template,portal.group_portal,1,0,0,0
access_stock_warehouse,stock.warehouse,stock.model_stock_warehouse,portal.group_portal,1,0,0,0
access_stock_location,stock.location,stock.model_stock_location,portal.group_portal,1,0,0,0
access_account_fiscalyear,account.fiscalyear,account.model_account_fiscalyear,portal.group_portal,1,0,0,0
access_res_partner_category,res.partner.category,base.model_res_partner_category,portal.group_portal,1,0,0,0
access_product_supplierinfo,product.supplierinfo,product.model_product_supplierinfo,portal.group_portal,1,0,0,0
access_product_packaging,product.packaging,product.model_product_packaging,portal.group_portal,1,0,0,0
access_account_period,account.period,account.model_account_period,portal.group_portal,1,0,0,0
access_account_account,account.account,account.model_account_account,portal.group_portal,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_sale_order sale.order sale.model_sale_order portal.group_portal_member portal.group_portal 1 0 0 0
3 access_sale_order_line sale.order.line sale.model_sale_order_line portal.group_portal_member portal.group_portal 1 0 0 0
4 access_stock_picking stock.picking stock.model_stock_picking portal.group_portal_member portal.group_portal 1 0 0 0
5 access_stock_picking.out stock.picking.out stock.model_stock_picking_out portal.group_portal_member portal.group_portal 1 0 0 0
6 access_stock_move stock.move stock.model_stock_move portal.group_portal_member portal.group_portal 1 0 0 0
7 access_stock_warehouse_orderpoint stock.warehouse.orderpoint procurement.model_stock_warehouse_orderpoint portal.group_portal_member portal.group_portal 1 0 0 0
8 access_account_invoice account.invoice account.model_account_invoice portal.group_portal_member portal.group_portal 1 0 0 0
9 access_account_invoice_tax account.invoice.tax account.model_account_invoice_tax portal.group_portal_member portal.group_portal 1 0 0 0
10 access_account_invoice_line account.invoice.line account.model_account_invoice_line portal.group_portal_member portal.group_portal 1 0 0 0
11 access_account_journal account.journal account.model_account_journal portal.group_portal_member portal.group_portal 1 0 0 0
12 access_account_voucher account.voucher account_voucher.model_account_voucher portal.group_portal_member portal.group_portal 1 0 0 0
13 access_account_voucher_line account.voucher.line account_voucher.model_account_voucher_line portal.group_portal_member portal.group_portal 1 0 0 0
14 access_account_move account.move account.model_account_move portal.group_portal_member portal.group_portal 1 0 0 0
15 access_account_move_line account.move.line account.model_account_move_line portal.group_portal_member portal.group_portal 1 0 0 0
16 access_account_move_reconcile account.move.reconcile account.model_account_move_reconcile portal.group_portal_member portal.group_portal 1 0 0 0
17 access_account_fiscalyear account.sequence.fiscalyear account.model_account_sequence_fiscalyear portal.group_portal_member portal.group_portal 1 0 0 0
18 access_sale_shop sale.shop sale.model_sale_shop portal.group_portal_member portal.group_portal 1 0 0 0
19 access_product_list product.pricelist product.model_product_pricelist portal.group_portal_member portal.group_portal 1 0 0 0
20 access_product product.product product.model_product_product portal.group_portal_member portal.group_portal 1 0 0 0
21 access_res_partner res.partner base.model_res_partner portal.group_portal_member portal.group_portal 1 0 0 0
22 access_product_uom product.uom product.model_product_uom portal.group_portal_member portal.group_portal 1 0 0 0
23 access_account_tax account.tax account.model_account_tax portal.group_portal_member portal.group_portal 1 0 0 0
24 access_mrp_property mrp.property procurement.model_mrp_property portal.group_portal_member portal.group_portal 1 0 0 0
25 access_product_template product.template product.model_product_template portal.group_portal_member portal.group_portal 1 0 0 0
26 access_stock_warehouse stock.warehouse stock.model_stock_warehouse portal.group_portal_member portal.group_portal 1 0 0 0
27 access_stock_location stock.location stock.model_stock_location portal.group_portal_member portal.group_portal 1 0 0 0
28 access_account_fiscalyear account.fiscalyear account.model_account_fiscalyear portal.group_portal_member portal.group_portal 1 0 0 0
29 access_res_partner_category res.partner.category base.model_res_partner_category portal.group_portal_member portal.group_portal 1 0 0 0
30 access_product_supplierinfo product.supplierinfo product.model_product_supplierinfo portal.group_portal_member portal.group_portal 1 0 0 0
31 access_product_packaging product.packaging product.model_product_packaging portal.group_portal_member portal.group_portal 1 0 0 0
32 access_account_period account.period account.model_account_period portal.group_portal_member portal.group_portal 1 0 0 0
33 access_account_account account.account account.model_account_account portal.group_portal_member portal.group_portal 1 0 0 0
@@ -7,42 +7,42 @@
<field name="name">Portal Personal Quotations/Sales Orders</field>
<field ref="sale.model_sale_order" name="model_id"/>
<field name="domain_force">[('partner_id','child_of',user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('portal.group_portal_member'))]"/>
<field name="groups" eval="[(4, ref('portal.group_portal'))]"/>
</record>
<record id="portal_stock_picking_user_rule" model="ir.rule">
<field name="name">Portal Personal Delivery Orders</field>
<field ref="stock.model_stock_picking" name="model_id"/>
<field name="domain_force">[('partner_id','child_of',[user.partner_id.id])]</field>
<field name="groups" eval="[(4, ref('portal.group_portal_member'))]"/>
<field name="groups" eval="[(4, ref('portal.group_portal'))]"/>
</record>
<record id="portal_stock_picking_user_rule" model="ir.rule">
<field name="name">Portal Personal Delivery Orders Out</field>
<field ref="stock.model_stock_picking_out" name="model_id"/>
<field name="domain_force">[('partner_id','child_of',[user.partner_id.id])]</field>
<field name="groups" eval="[(4, ref('portal.group_portal_member'))]"/>
<field name="groups" eval="[(4, ref('portal.group_portal'))]"/>
</record>
<record id="portal_account_invoice_user_rule" model="ir.rule">
<field name="name">Portal Personal Account Invoices</field>
<field ref="account.model_account_invoice" name="model_id"/>
<field name="domain_force">[('partner_id','child_of',user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('portal.group_portal_member'))]"/>
<field name="groups" eval="[(4, ref('portal.group_portal'))]"/>
</record>
<record id="portal_personal_payment" model="ir.rule">
<field name="name">Portal Personal Payments</field>
<field ref="account_voucher.model_account_voucher" name="model_id"/>
<field name="domain_force">[('partner_id','child_of',user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('portal.group_portal_member'))]"/>
<field name="groups" eval="[(4, ref('portal.group_portal'))]"/>
</record>
<record id="portal_personal_contact" model="ir.rule">
<field name="name">Portal Personal Contacts</field>
<field ref="base.model_res_partner" name="model_id"/>
<field name="domain_force">[('id','child_of',user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('portal.group_portal_member'))]"/>
<field name="groups" eval="[(4, ref('portal.group_portal'))]"/>
</record>
</data>
+1 -1
View File
@@ -554,7 +554,7 @@ class stock_warehouse_orderpoint(osv.osv):
'logic': fields.selection([('max','Order to Max'),('price','Best price (not yet active!)')], 'Reordering Mode', required=True),
'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse', required=True, ondelete="cascade"),
'location_id': fields.many2one('stock.location', 'Location', required=True, ondelete="cascade"),
'product_id': fields.many2one('product.product', 'Product', required=True, ondelete='cascade', domain=[('type','=','product')]),
'product_id': fields.many2one('product.product', 'Product', required=True, ondelete='cascade', domain=[('type','!=','service')]),
'product_uom': fields.many2one('product.uom', 'Product Unit of Measure', required=True),
'product_min_qty': fields.float('Minimum Quantity', required=True,
help="When the virtual stock goes below the Min Quantity specified for this field, OpenERP generates "\
+5 -1
View File
@@ -166,7 +166,11 @@
<field name="view_mode">tree,form</field>
<field name="context">{'search_default_perm_exceptions':1}</field>
<field name="search_view_id" ref="view_procurement_filter"/>
<field name="help">Procurement Orders represent the need for a certain quantity of products, at a given time, in a given location. Sales Orders are one typical source of Procurement Orders (but these are distinct documents). Depending on the procurement parameters and the product configuration, the procurement engine will attempt to satisfy the need by reserving products from stock, ordering products from a supplier, or passing a manufacturing order, etc. A Procurement Exception occurs when the system cannot find a way to fulfill a procurement. Some exceptions will resolve themselves automatically, but others require manual intervention (those are identified by a specific error message).</field>
<field name="help" type="html">
<p>
Procurement Orders represent the need for a certain quantity of products, at a given time, in a given location. Sales Orders are one typical source of Procurement Orders (but these are distinct documents). Depending on the procurement parameters and the product configuration, the procurement engine will attempt to satisfy the need by reserving products from stock, ordering products from a supplier, or passing a manufacturing order, etc. A Procurement Exception occurs when the system cannot find a way to fulfill a procurement. Some exceptions will resolve themselves automatically, but others require manual intervention (those are identified by a specific error message).
</p>
</field>
</record>
<record id="procurement_action5" model="ir.actions.act_window">
+22 -2
View File
@@ -106,11 +106,31 @@
<group name="procurement">
<group>
<field name="procure_method" groups="base.group_user"/>
<field name="supply_method" groups="base.group_user" invisible="1"/>
<field name="cost_method" groups="product.group_costing_method"/>
<field name="standard_price" attrs="{'readonly':[('cost_method','=','average')]}"/>
</group>
<group>
<field name="uom_po_id" groups="product.group_uom"/>
<group name="procurement_help" class="oe_grey" col="1">
<p attrs="{'invisible': [('type','&lt;&gt;','service'),('procure_method','&lt;&gt;','make_to_stock')]}">
When you sell this service, nothing special will be trigered
to deliver the customer.
</p>
<p attrs="{'invisible': [('type','&lt;&gt;','product'),('procure_method','&lt;&gt;','make_to_stock')]}">
When you sell this product, OpenERP will <b>use the available inventory</b>
for the delivery order.
<br/>
If there are not enough quantities available, the delivery order
will wait for new products. You should create others rules
(orderpoints, manual purchase orders) to fulfill the inventory.
</p>
<p attrs="{'invisible': [('type','&lt;&gt;','consu'),('procure_method','&lt;&gt;','make_to_stock')]}">
When you sell this product, a delivery order will be created.
OpenERP will consider that the <b>required quantities are always
available</b> as it's a consumable (the stock on hand may become negative).
</p>
</group>
<group groups="product.group_uom">
<field name="uom_po_id"/>
</group>
</group>
<separator string="Notes for Suppliers"/>
+1 -3
View File
@@ -269,9 +269,7 @@
</div>
<div class="oe_kanban_footer_left">
<div class="oe_left oe_tags">
<t t-foreach="record.categ_ids.raw_value" t-as="categ_id">
<span class="oe_tag" t-att-data-categ_id="categ_id"></span>
</t>
<field name="categ_ids"/>
</div>
<div class="oe_right">
<span class="oe_kanban_highlight">
+9 -2
View File
@@ -36,9 +36,16 @@
<field name="model">product.product</field>
<field name="inherit_id" ref="product.product_normal_form_view"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='procure_method']" position="after">
<field name="supply_method" groups="base.group_user"/>
<xpath expr="//field[@name='supply_method']" position="attributes">
<attribute name="invisible">False</attribute>
</xpath>
<group name="procurement_help" position="inside">
<p attrs="{'invisible': [('type','&lt;&gt;','service'),('procure_method','&lt;&gt;','make_to_order'),('supply_method','&lt;&gt;','produce')]}">
When you sell this service to a customer, <b>a task</b> will be
created to follow up the job to do. This task will appear
in the project related to the contract of the sale order.
</p>
</group>
</field>
</record>
</data>
@@ -1,20 +0,0 @@
<?xml version="1.0" ?>
<openerp>
<data noupdate="1">
<!-- <record id="base.menu_project_management_time_tracking" model="ir.ui.menu">
<field name="name">Time Tracking</field>
<field eval="5" name="sequence"/>
<field name="parent_id" ref="base.menu_main_pm"/>
</record>
<record id="menu_act_project_management_timesheet_sheet_form" model="ir.ui.menu">
<field name="name">My Timesheet</field>
<field eval="5" name="sequence"/>
<field name="parent_id" ref="base.menu_project_management_time_tracking"/>
<field name="icon">STOCK_JUSTIFY_FILL</field>
<field name="action" ref="hr_timesheet_sheet.ir_actions_server_timsheet_sheet"/>
</record> -->
</data>
</openerp>
+1
View File
@@ -548,6 +548,7 @@ class purchase_order(osv.osv):
'partner_id': order.dest_address_id.id or order.partner_id.id,
'move_dest_id': order_line.move_dest_id.id,
'state': 'draft',
'type':'in',
'purchase_line_id': order_line.id,
'company_id': order.company_id.id,
'price_unit': order_line.price_unit
+14
View File
@@ -572,6 +572,20 @@
<separator string="Suppliers"/>
<field name="seller_ids" context="{'uom_id': uom_id}"/>
</group>
<group name="procurement_help" position="inside">
<p attrs="{'invisible': [('type','&lt;&gt;','service'),('procure_method','&lt;&gt;','make_to_order'),('supply_method','&lt;&gt;','buy')]}">
When you sell this service to a customer, <b>a draft purchase order</b>
will be created in order to subcontract the job
<i attrs="{'invisible': [('seller_id','=',False)]}">to
<field name="seller_id" class="oe_inline"/></i>.
</p>
<p attrs="{'invisible': [('type','=','service'),('procure_method','&lt;&gt;','make_to_order'),('supply_method','&lt;&gt;','buy')]}">
When you sell this product, OpenERP will trigger <b>a draft
purchase order</b> to buy the required quantities to the supplier.
The delivery order will be ready after having received the
products.
</p>
</group>
</field>
</record>
</data>
@@ -72,7 +72,7 @@
<filter string="Product" name="group_product_id" icon="terp-accessories-archiver" context="{'group_by':'product_id'}"/>
<filter string="Category" name="group_category_id" icon="terp-stock_symbol-selection" context="{'group_by':'category_id'}"/>
<filter string="Reference Unit of Measure" name="group_product_uom" icon="terp-mrp" context="{'group_by':'product_uom'}"/>
<filter string="Warehouse" icon="terp-go-home" context="{'group_by':'warehouse_id'}" groups="base.group_extended"/>
<filter string="Warehouse" icon="terp-go-home" context="{'group_by':'warehouse_id'}"/>
<filter string="Reference UOM" name="group_product_uom" icon="terp-mrp" context="{'group_by':'product_uom'}"/>
<filter string="Warehouse" icon="terp-go-home" context="{'group_by':'warehouse_id'}"/>
<filter string="Destination" icon="terp-gtk-jump-to-ltr" context="{'group_by':'location_id'}"/>
+1 -1
View File
@@ -31,7 +31,7 @@
<field name="purchase_id"/>
</xpath>
<xpath expr="//field[@name='company_id']" position="after">
<field name="warehouse_id"/>
<field name="warehouse_id" groups="stock.group_locations"/>
</xpath>
</field>
</record>
@@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-09-17 04:38+0000\n"
"X-Generator: Launchpad (build 15944)\n"
"X-Launchpad-Export-Date: 2012-09-18 04:48+0000\n"
"X-Generator: Launchpad (build 15966)\n"
#. module: purchase_double_validation
#: view:purchase.double.validation.installer:0
+28 -40
View File
@@ -145,6 +145,14 @@ class sale_order(osv.osv):
res[sale.id] = 0.0
return res
def _invoice_exists(self, cursor, user, ids, name, arg, context=None):
res = {}
for sale in self.browse(cursor, user, ids, context=context):
res[sale.id] = False
if sale.invoice_ids:
res[sale.id] = True
return res
def _invoiced(self, cursor, user, ids, name, arg, context=None):
res = {}
for sale in self.browse(cursor, user, ids, context=context):
@@ -156,7 +164,7 @@ class sale_order(osv.osv):
if invoice.state != 'paid':
res[sale.id] = False
break
if not invoice_existence:
if not invoice_existence or sale.state == 'manual':
res[sale.id] = False
return res
@@ -245,6 +253,8 @@ class sale_order(osv.osv):
'invoiced_rate': fields.function(_invoiced_rate, string='Invoiced', type='float'),
'invoiced': fields.function(_invoiced, string='Paid',
fnct_search=_invoiced_search, type='boolean', help="It indicates that an invoice has been paid."),
'invoice_exists': fields.function(_invoice_exists, string='Invoiced',
fnct_search=_invoiced_search, type='boolean', help="It indicates that sale order has at least one invoice."),
'note': fields.text('Terms and conditions'),
'amount_untaxed': fields.function(_amount_all, digits_compute= dp.get_precision('Account'), string='Untaxed Amount',
@@ -518,68 +528,45 @@ class sale_order(osv.osv):
This function returns an action that display existing invoices of given sale order ids. It can either be a in a list or in a form view, if there is only one invoice to show.
'''
mod_obj = self.pool.get('ir.model.data')
result = {
'name': _('Cutomer Invoice'),
'view_type': 'form',
'res_model': 'account.invoice',
'context': "{'type':'out_invoice', 'journal_type': 'sale'}",
'type': 'ir.actions.act_window',
'nodestroy': True,
'target': 'current',
}
act_obj = self.pool.get('ir.actions.act_window')
result = mod_obj.get_object_reference(cr, uid, 'account', 'action_invoice_tree1')
id = result and result[1] or False
result = act_obj.read(cr, uid, [id], context=context)[0]
#compute the number of invoices to display
inv_ids = []
for so in self.browse(cr, uid, ids, context=context):
inv_ids += [invoice.id for invoice in so.invoice_ids]
#choose the view_mode accordingly
if len(inv_ids)>1:
res = mod_obj.get_object_reference(cr, uid, 'account', 'invoice_tree')
result.update({
'view_mode': 'tree,form',
'res_id': inv_ids or False
})
result['domain'] = "[('id','in',["+','.join(map(str, inv_ids))+"])]"
else:
res = mod_obj.get_object_reference(cr, uid, 'account', 'invoice_form')
result.update({
'view_mode': 'form',
'res_id': inv_ids and inv_ids[0] or False,
})
result.update(view_id = res and res[1] or False)
result['views'] = [(res and res[1] or False, 'form')]
result['res_id'] = inv_ids and inv_ids[0] or False
return result
def action_view_delivery(self, cr, uid, ids, context=None):
'''
This function returns an action that display existing delivery orders of given sale order ids. It can either be a in a list or in a form view, if there is only one delivery order to show.
'''
mod_obj = self.pool.get('ir.model.data')
result = {
'name': _('Delivery Order'),
'view_type': 'form',
'res_model': 'stock.picking',
'context': "{'type':'out'}",
'type': 'ir.actions.act_window',
'nodestroy': True,
'target': 'current',
}
act_obj = self.pool.get('ir.actions.act_window')
result = mod_obj.get_object_reference(cr, uid, 'stock', 'action_picking_tree')
id = result and result[1] or False
result = act_obj.read(cr, uid, [id], context=context)[0]
#compute the number of delivery orders to display
pick_ids = []
for so in self.browse(cr, uid, ids, context=context):
pick_ids += [picking.id for picking in so.picking_ids]
#choose the view_mode accordingly
if len(pick_ids) > 1:
res = mod_obj.get_object_reference(cr, uid, 'stock', 'view_picking_out_tree')
result.update({
'view_mode': 'tree,form',
'res_id': pick_ids or False
})
result['domain'] = "[('id','in',["+','.join(map(str, pick_ids))+"])]"
else:
res = mod_obj.get_object_reference(cr, uid, 'stock', 'view_picking_out_form')
result.update({
'view_mode': 'form',
'res_id': pick_ids and pick_ids[0] or False,
})
result.update(view_id = res and res[1] or False)
result['views'] = [(res and res[1] or False, 'form')]
result['res_id'] = pick_ids and pick_ids[0] or False
return result
def test_no_product(self, cr, uid, order, context):
@@ -883,6 +870,7 @@ class sale_order(osv.osv):
'sale_line_id': line.id,
'tracking_id': False,
'state': 'draft',
'type':'out',
#'state': 'waiting',
'note': '\n'.join(line.name.split('\n')[1:]),
'company_id': order.company_id.id,
+2 -1
View File
@@ -163,7 +163,7 @@
<button name="action_button_confirm" states="draft" string="Confirm" type="object"/>
<button name="action_button_confirm" states="sent" string="Confirm" class="oe_highlight" type="object"/>
<button name="action_view_invoice" string="View Invoice" type="object" class="oe_highlight"
attrs="{'invisible': ['|','|',('state', '!=','progress'), ('invoiced', '=', True),('order_policy','=','picking')]}"/>
attrs="{'invisible': [('invoice_exists', '=', False)]}"/>
<button name="action_view_delivery" string="View Delivery Order" type="object" class="oe_highlight"
attrs="{'invisible': ['|','|','|',('picking_ids','=',False),('picking_ids','=',[]), ('state', 'not in', ('progress','manual')),('shipped','=',True)]}"/>
<button name="%(action_view_sale_advance_payment_inv)d" string="Create Invoice"
@@ -320,6 +320,7 @@
<group>
<field name="invoiced"/>
<field name="shipped"/>
<field name="invoice_exists" invisible="1"/>
</group>
</group>
</page>

Some files were not shown because too many files have changed in this diff Show More