[MERGE]: Merge with lp:openobject-addons
bzr revid: mma@tinyerp.com-20120919124908-7z75tszx3onakuc9
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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
|
||||
# ----------------------------------------
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
=====================================================
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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
@@ -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">
|
||||
|
||||
@@ -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"
|
||||
#~ "Изберете валиден Модел"
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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:
|
||||
@@ -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
|
||||
|
||||
|
@@ -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
|
||||
/* ------------------------------------------------------------ */
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -142,11 +142,9 @@
|
||||
</li>
|
||||
<li><a t-attf-href="#model=res.partner&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>
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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','<>','make_to_order'),('supply_method','<>','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>
|
||||
|
||||
|
||||
+7
-5
@@ -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 """
|
||||
@@ -65,23 +66,24 @@ class note_note(osv.osv):
|
||||
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)
|
||||
text_note = html2plaintext(text_note)
|
||||
res[note.id] = text_note
|
||||
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)
|
||||
@@ -119,14 +121,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'
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<?xml version="1.0"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<menuitem name="Organizer" id="note_my_stuff" parent="mail.mail_feeds_main"/>
|
||||
|
||||
<!-- note Stage Form View -->
|
||||
@@ -51,7 +50,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 +64,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 +77,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 +103,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>
|
||||
@@ -139,9 +137,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,7 +182,7 @@
|
||||
<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"/>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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)]})
|
||||
|
||||
|
||||
|
||||
@@ -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' : [ '&' ,'!', '&', ('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' : [ '&' ,'!', '&', ('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'}"/>
|
||||
|
||||
@@ -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
@@ -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:
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
|
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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,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
|
||||
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
|
@@ -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>
|
||||
|
||||
@@ -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 "\
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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','<>','service'),('procure_method','<>','make_to_stock')]}">
|
||||
When you sell this service, nothing special will be trigered
|
||||
to deliver the customer.
|
||||
</p>
|
||||
<p attrs="{'invisible': [('type','<>','product'),('procure_method','<>','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','<>','consu'),('procure_method','<>','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"/>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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','<>','service'),('procure_method','<>','make_to_order'),('supply_method','<>','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>
|
||||
@@ -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
|
||||
|
||||
@@ -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','<>','service'),('procure_method','<>','make_to_order'),('supply_method','<>','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','<>','make_to_order'),('supply_method','<>','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'}"/>
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -9,9 +9,6 @@
|
||||
<field name="move_type" position="before">
|
||||
<field name="sale_id"/>
|
||||
</field>
|
||||
<label for="partner_id" position="replace">
|
||||
<label for="partner_id" class="oe_edit_only" string="Contact"/>
|
||||
</label>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
-
|
||||
I confirm the Quotation with "Deliver & invoice on demand".
|
||||
I confirm the Quotation with "On Demand" order policy.
|
||||
-
|
||||
!workflow {model: sale.order, action: order_confirm, ref: sale_order_2}
|
||||
-
|
||||
@@ -9,44 +9,34 @@
|
||||
sale_order = self.browse(cr, uid, ref("sale_order_2"))
|
||||
assert len(sale_order.invoice_ids) == False, "Invoice should not created."
|
||||
-
|
||||
I create advance invoice.
|
||||
I create advance invoice where type is 'Fixed Price'.
|
||||
-
|
||||
!python {model: sale.advance.payment.inv}: |
|
||||
ctx = context.copy()
|
||||
ctx.update({"active_model": 'sale.order', "active_ids": [ref("sale_order_2")], "active_id":ref("sale_order_2")})
|
||||
order_line = self.pool.get('sale.order.line').browse(cr, uid, ref("sale_order_line_4"), context=context)
|
||||
pay_id = self.create(cr, uid, {'advance_payment_method': 'fixed', 'product_id': order_line.product_id.id, 'amount': order_line.price_subtotal, 'qtty': order_line.product_uom_qty})
|
||||
pay_id = self.create(cr, uid, {'advance_payment_method': 'fixed', 'product_id': order_line.product_id.id, 'amount': order_line.price_unit})
|
||||
self.create_invoices(cr, uid, [pay_id], context=ctx)
|
||||
-
|
||||
I check Invoice which made advance
|
||||
I check Invoice which made advance.
|
||||
-
|
||||
!python {model: sale.order}: |
|
||||
order = self.browse(cr, uid, ref('sale_order_2'))
|
||||
assert order.invoice_ids, "Invoice should be created after make advance invoice."
|
||||
-
|
||||
I create Invoice from sale order line.
|
||||
I create advance invoice where type is 'Invoice all the Sale Order'.
|
||||
-
|
||||
!python {model: sale.order.line.make.invoice}: |
|
||||
!python {model: sale.advance.payment.inv}: |
|
||||
ctx = context.copy()
|
||||
ctx.update({"active_model": 'sale.order.line', "active_ids": [ref("sale_order_line_5")], "active_id":ref("sale_order_line_5")})
|
||||
self.make_invoices(cr, uid, [], context=ctx)
|
||||
ctx.update({"active_model": 'sale.order', "active_ids": [ref("sale_order_2")], "active_id":ref("sale_order_2")})
|
||||
pay_id = self.create(cr, uid, {'advance_payment_method': 'all'})
|
||||
self.create_invoices(cr, uid, [pay_id], context=ctx)
|
||||
-
|
||||
I check Invoice which made from sale order line.
|
||||
I check Invoice which made advance where type is 'Invoice all the Sale Order'.
|
||||
-
|
||||
!python {model: sale.order.line}: |
|
||||
line = self.browse(cr, uid, ref('sale_order_line_5'))
|
||||
assert line.invoiced, "Line is not invoiced."
|
||||
assert line.invoice_lines, "Invoice line should be created."
|
||||
-
|
||||
I create manual Invoice for order.
|
||||
-
|
||||
!record {model: sale.make.invoice, id: sale_make_invoice_1}:
|
||||
invoice_date: !eval time.strftime('%Y-%m-%d')
|
||||
-
|
||||
!python {model: sale.make.invoice}: |
|
||||
ctx = context.copy()
|
||||
ctx = ctx.update({"active_model": 'sale.order', "active_ids": [ref("sale_order_2")], "active_id":ref("sale_order_2")})
|
||||
self.make_invoices(cr, uid, [ref("sale_make_invoice_1")], context)
|
||||
!python {model: sale.order}: |
|
||||
order = self.browse(cr, uid, ref('sale_order_2'))
|
||||
assert order.invoice_ids, "Invoice should be created after make advance invoice where type is 'Invoice all the Sale Order'."
|
||||
-
|
||||
I open the Invoice.
|
||||
-
|
||||
@@ -57,7 +47,7 @@
|
||||
for invoice in so.invoice_ids:
|
||||
wf_service.trg_validate(uid, 'account.invoice', invoice.id, 'invoice_open', cr)
|
||||
-
|
||||
I pay the invoice
|
||||
I pay the invoice.
|
||||
-
|
||||
!python {model: account.invoice}: |
|
||||
sale_order = self.pool.get('sale.order')
|
||||
@@ -75,8 +65,9 @@
|
||||
!python {model: sale.order}: |
|
||||
sale_order = self.browse(cr, uid, ref("sale_order_2"))
|
||||
assert sale_order.invoice_ids, "Invoice should be created."
|
||||
assert sale_order.invoiced, "Order is not invoiced."
|
||||
assert sale_order.state == 'manual', 'Order should be in Manual.'
|
||||
assert sale_order.invoice_exists, "Order is not invoiced."
|
||||
assert sale_order.invoiced, "Order is not paid."
|
||||
assert sale_order.state == 'progress', 'Order should be in Progress.'
|
||||
|
||||
-
|
||||
I set order policy "Deliver & invoice on demand" as default policy.
|
||||
|
||||
@@ -25,13 +25,13 @@
|
||||
<record model="ir.ui.view" id="res_groups_search_sharing">
|
||||
<field name="name">res.groups.search.share</field>
|
||||
<field name="model">res.groups</field>
|
||||
<field name="priority" eval="8"/>
|
||||
<field name="inherit_id" ref="base.view_groups_search"/>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Groups">
|
||||
<field name="name" string="Group"/>
|
||||
<field name="share"/>
|
||||
<filter name="no_share" string="Regular groups only (no share groups)" icon="terp-partner" domain="[('share','=',False)]"/>
|
||||
</search>
|
||||
<field name="name" position="after">
|
||||
<filter name="share" string="Share Groups" domain="[('share','=',True)]"/>
|
||||
<filter name="no_share" string="Non-Share Groups" domain="[('share','=',False)]"/>
|
||||
<separator orientation="vertical"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
parent="base.menu_reporting_dashboard"
|
||||
action="open_board_warehouse"
|
||||
sequence="25"
|
||||
groups="group_stock_manager"/>
|
||||
groups="stock.group_stock_user"/>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
id="stock.next_id_61"
|
||||
name="Warehouse"
|
||||
sequence="15"
|
||||
parent="base.menu_reporting"/>
|
||||
parent="base.menu_reporting"
|
||||
groups="group_stock_manager"/>
|
||||
|
||||
<record id="view_stock_tree" model="ir.ui.view">
|
||||
<field name="name">report.stock.move.tree</field>
|
||||
|
||||
@@ -21,11 +21,11 @@ access_stock_production_lot_manager,stock.production.lot manager,model_stock_pro
|
||||
access_stock_production_lot_user,stock.production.lot user,model_stock_production_lot,stock.group_stock_user,1,1,1,1
|
||||
access_stock_production_lot_revision,stock.production.lot.revision,model_stock_production_lot_revision,stock.group_stock_user,1,1,1,1
|
||||
access_stock_move_manager,stock.move manager,model_stock_move,stock.group_stock_manager,1,1,0,0
|
||||
access_stock_move_user,stock.move user,model_stock_move,stock.group_stock_user,1,0,0,0
|
||||
access_stock_move_user,stock.move user,model_stock_move,stock.group_stock_user,1,1,1,0
|
||||
access_stock_move_warehouse_manager,stock.move.manager,model_stock_move,stock.group_stock_manager,1,1,1,1
|
||||
access_stock_inventory_user,stock.inventory user,model_stock_inventory,stock.group_stock_user,1,0,0,0
|
||||
access_stock_inventory_user,stock.inventory user,model_stock_inventory,stock.group_stock_user,1,1,1,0
|
||||
access_stock_inventory_manager,stock.inventory manager,model_stock_inventory,stock.group_stock_manager,1,1,1,1
|
||||
access_stock_inventory_line_user,stock.inventory.line user,model_stock_inventory_line,stock.group_stock_user,1,0,0,0
|
||||
access_stock_inventory_line_user,stock.inventory.line user,model_stock_inventory_line,stock.group_stock_user,1,1,1,0
|
||||
access_stock_inventory_line_manager,stock.inventory.line manager,model_stock_inventory_line,stock.group_stock_manager,1,1,1,1
|
||||
access_stock_report_prodlots,stock.report.prodlots,model_stock_report_prodlots,stock.group_stock_manager,1,0,0,0
|
||||
access_stock_location_sale_manager,stock.location sale manager,model_stock_location,base.group_sale_manager,1,0,0,0
|
||||
@@ -61,3 +61,4 @@ access_product_group_res_partner_stock_manager,res_partner group_stock_manager,b
|
||||
access_product_pricelist_version_stock_manager,product.pricelist.version stock_manager,product.model_product_pricelist_version,stock.group_stock_manager,1,1,1,1
|
||||
access_product_pricelist_item_stock_manager,product.pricelist.item stock_manager,product.model_product_pricelist_item,stock.group_stock_manager,1,1,1,1
|
||||
access_account_account_stock_manager,account.account stock manager,account.model_account_account,stock.group_stock_manager,1,0,0,0
|
||||
access_board_stock_user,board.board user,board.model_board_board,stock.group_stock_user,1,1,0,0
|
||||
|
||||
|
@@ -1659,6 +1659,7 @@ class stock_move(osv.osv):
|
||||
|
||||
# used for colors in tree views:
|
||||
'scrapped': fields.related('location_dest_id','scrap_location',type='boolean',relation='stock.location',string='Scrapped', readonly=True),
|
||||
'type': fields.related('picking_id', 'type', type='selection', selection=[('out', 'Sending Goods'), ('in', 'Getting Goods'), ('internal', 'Internal')], string='Shipping Type'),
|
||||
}
|
||||
def _check_location(self, cr, uid, ids, context=None):
|
||||
for record in self.browse(cr, uid, ids, context=context):
|
||||
@@ -1738,10 +1739,25 @@ class stock_move(osv.osv):
|
||||
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
|
||||
return user.company_id.partner_id.id
|
||||
|
||||
def _default_move_type(self, cr, uid, context=None):
|
||||
""" Gets default type of move
|
||||
@return: type
|
||||
"""
|
||||
if context is None:
|
||||
context = {}
|
||||
picking_type = context.get('picking_type')
|
||||
type = 'internal'
|
||||
if picking_type == 'in':
|
||||
type = 'in'
|
||||
elif picking_type == 'out':
|
||||
type = 'out'
|
||||
return type
|
||||
|
||||
_defaults = {
|
||||
'location_id': _default_location_source,
|
||||
'location_dest_id': _default_location_destination,
|
||||
'partner_id': _default_destination_address,
|
||||
'type': _default_move_type,
|
||||
'state': 'draft',
|
||||
'priority': '1',
|
||||
'product_qty': 1.0,
|
||||
@@ -1895,6 +1911,32 @@ class stock_move(osv.osv):
|
||||
result['location_dest_id'] = loc_dest_id
|
||||
return {'value': result}
|
||||
|
||||
def onchange_move_type(self, cr, uid, ids, type, context=None):
|
||||
""" On change of move type gives sorce and destination location.
|
||||
@param type: Move Type
|
||||
@return: Dictionary of values
|
||||
"""
|
||||
mod_obj = self.pool.get('ir.model.data')
|
||||
location_source_id = False
|
||||
location_dest_id = False
|
||||
if type == 'in':
|
||||
location_source_id = 'stock_location_suppliers'
|
||||
location_dest_id = 'stock_location_stock'
|
||||
elif type == 'out':
|
||||
location_source_id = 'stock_location_stock'
|
||||
location_dest_id = 'stock_location_customers'
|
||||
if location_source_id:
|
||||
try:
|
||||
location_model, location_source_id = mod_obj.get_object_reference(cr, uid, 'stock', location_source_id)
|
||||
except ValueError, e:
|
||||
location_source_id = False
|
||||
if location_dest_id:
|
||||
try:
|
||||
location_model, location_dest_id = mod_obj.get_object_reference(cr, uid, 'stock', location_dest_id)
|
||||
except ValueError, e:
|
||||
location_dest_id = False
|
||||
return {'value':{'location_id': location_source_id, 'location_dest_id': location_dest_id}}
|
||||
|
||||
def onchange_date(self, cr, uid, ids, date, date_expected, context=None):
|
||||
""" On change of Scheduled Date gives a Move date.
|
||||
@param date_expected: Scheduled Date
|
||||
|
||||
+158
-119
@@ -106,13 +106,16 @@
|
||||
<field name="arch" type="xml">
|
||||
<form string="Physical Inventory" version="7.0">
|
||||
<header>
|
||||
<button name="action_confirm" states="draft" string="Confirm Inventory" type="object" class="oe_highlight"/>
|
||||
<button name="action_done" states="confirm" string="Validate Inventory" type="object" class="oe_highlight"/>
|
||||
<button name="action_confirm" states="draft" string="Confirm Inventory" type="object" class="oe_highlight" groups="stock.group_stock_user"/>
|
||||
<button name="action_done" states="confirm" string="Validate Inventory" type="object" class="oe_highlight" groups="stock.group_stock_manager"/>
|
||||
<button name="action_cancel_draft" states="cancel" string="Set to Draft" type="object"/>
|
||||
<button name="action_cancel_inventory" states="draft,confirm,done" string="Cancel Inventory" type="object"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,confirm"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<div class="oe_right oe_button_box">
|
||||
<button name="%(action_view_stock_fill_inventory)d" string="Fill Inventory" type="action" />
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="name" placeholder="e.g. Annual inventory"/>
|
||||
@@ -231,9 +234,9 @@
|
||||
<field name="active"/>
|
||||
</group>
|
||||
<group groups="stock.group_tracking_lot" string="Traceability">
|
||||
<button name="action_traceability" icon="gtk-go-up" string="Upstream Traceability" type="object"
|
||||
<button name="action_traceability" string="Upstream Traceability" type="object"
|
||||
colspan="2"/>
|
||||
<button name="action_traceability" icon="gtk-go-down" string="Downstream Traceability" type="object"
|
||||
<button name="action_traceability" string="Downstream Traceability" type="object"
|
||||
context="{'type': 'move_history_ids'}" colspan="2"/>
|
||||
</group>
|
||||
</group>
|
||||
@@ -334,8 +337,8 @@
|
||||
<field name="arch" type="xml">
|
||||
<form string="Serial Number" version="7.0">
|
||||
<div class="oe_button_box oe_right">
|
||||
<button name="action_traceability" string="Upstream Traceability" type="object" context="{'type': '', 'field': 'prodlot_id'}" icon="gtk-go-up"/>
|
||||
<button name="action_traceability" string="Downstream Traceability" type="object" context="{'type': 'move_history_ids', 'field': 'prodlot_id'}" icon="gtk-go-down"/>
|
||||
<button name="action_traceability" string="Upstream Traceability" type="object" context="{'type': 'move_history_ids2', 'field': 'prodlot_id'}"/>
|
||||
<button name="action_traceability" string="Downstream Traceability" type="object" context="{'type': 'move_history_ids', 'field': 'prodlot_id'}"/>
|
||||
</div>
|
||||
<div class="oe_title">
|
||||
<label for="name" class="oe_edit_only"/>
|
||||
@@ -759,15 +762,10 @@
|
||||
<label for="name" class="oe_edit_only"/>
|
||||
<h1>
|
||||
<field name="name" class="oe_inline"/>
|
||||
<span attrs="{'invisible': [('origin','=',False)]}"> - </span>
|
||||
<field name="origin" placeholder="e.g. PO0032" class="oe_inline"/>
|
||||
</h1>
|
||||
<label for="partner_id" class="oe_edit_only"/>
|
||||
<h2>
|
||||
<field name="partner_id" on_change="onchange_partner_in(partner_id)"/>
|
||||
</h2>
|
||||
<group>
|
||||
<group>
|
||||
<field name="partner_id" on_change="onchange_partner_in(partner_id)"/>
|
||||
<field name="backorder_id" readonly="1"/>
|
||||
<field name="invoice_state" string="Invoice Control" groups="account.group_account_invoice" attrs="{'invisible':[('invoice_state', '=', 'none')]}"/>
|
||||
<field name="stock_journal_id" widget="selection" groups="account.group_account_user"/>
|
||||
@@ -775,119 +773,23 @@
|
||||
<group>
|
||||
<field name="date"/>
|
||||
<field name="min_date" readonly="1"/>
|
||||
<field name="origin" placeholder="e.g. PO0032" class="oe_inline"/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Products">
|
||||
<field name="move_lines" context="{'address_in_id': partner_id}">
|
||||
<tree colors="grey:scrapped == True" string="Stock Moves">
|
||||
<field name="product_id"/>
|
||||
<field name="product_qty" on_change="onchange_quantity(product_id, product_qty, product_uom, product_uos)"/>
|
||||
<field name="product_uom" string="Unit of Measure" groups="product.group_uom"/>
|
||||
<field name="product_uos" groups="product.group_uos"/>
|
||||
<button name="%(stock.move_scrap)d"
|
||||
string="Scrap Products" type="action"
|
||||
icon="gtk-convert" context="{'scrap': True}"
|
||||
states="draft,waiting,confirmed,assigned"
|
||||
groups="base.group_user"/>
|
||||
<field name="scrapped" invisible="1"/>
|
||||
<field name="prodlot_id" groups="stock.group_production_lot"/>
|
||||
<button
|
||||
name="%(stock.track_line)d"
|
||||
string="Split in Serial Number"
|
||||
groups="stock.group_production_lot"
|
||||
type="action" icon="terp-stock_effects-object-colorize"
|
||||
states="draft,waiting,confirmed,assigned"/>
|
||||
<field groups="stock.group_tracking_lot" name="tracking_id"/>
|
||||
<button name="setlast_tracking" string="Put in current pack" type="object"
|
||||
attrs="{'invisible': [('tracking_id','<>',False)]}"
|
||||
icon="terp-stock_effects-object-colorize"
|
||||
groups="stock.group_tracking_lot"
|
||||
states="draft,assigned,confirmed"/>
|
||||
<button name="%(split_into)d" string="Put in a new pack" type="action"
|
||||
groups="product.group_stock_packaging"
|
||||
icon="terp-stock_effects-object-colorize"
|
||||
states="draft,assigned,confirmed"/>
|
||||
<field name="location_dest_id" groups="stock.group_locations"/>
|
||||
<field name="state"/>
|
||||
</tree>
|
||||
<form string="Stock Moves" version="7.0">
|
||||
<header>
|
||||
<span groups="base.group_user">
|
||||
<button name="force_assign" states="confirmed" string="Force Availability" type="object" icon="gtk-jump-to"/>
|
||||
<button name="action_confirm" states="draft" string="Confirm" type="object" icon="gtk-apply"/>
|
||||
<button name="cancel_assign" states="assigned" string="Cancel Availability" type="object" icon="gtk-find"/>
|
||||
</span>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,assigned,done"/>
|
||||
</header>
|
||||
<group>
|
||||
<group>
|
||||
<field name="name" invisible="1"/>
|
||||
<field name="product_id" on_change="onchange_product_id(product_id,location_id,location_dest_id, parent.partner_id)"/>
|
||||
<label for="product_qty"/>
|
||||
<div>
|
||||
<field name="product_qty" on_change="onchange_quantity(product_id, product_qty, product_uom, product_uos)" class="oe_inline"/>
|
||||
<field name="product_uom" string="Unit Of Measure" groups="product.group_uom" class="oe_inline"/>
|
||||
<button name="%(stock.move_scrap)d"
|
||||
string="Scrap" type="action"
|
||||
icon="gtk-convert" context="{'scrap': True}"
|
||||
states="draft,waiting,confirmed,assigned"
|
||||
groups="base.group_user"/>
|
||||
</div>
|
||||
<label for="product_uos_qty" groups="product.group_uos"/>
|
||||
<div groups="product.group_uos">
|
||||
<field name="product_uos_qty" on_change="onchange_uos_quantity(product_id, product_uos_qty, product_uos, product_uom)" class="oe_inline"/>
|
||||
<field name="product_uos" on_change="onchange_quantity(product_id, product_qty, product_uom, product_uos)" class="oe_inline"/>
|
||||
</div>
|
||||
<field name="product_packaging" groups="product.group_stock_packaging" domain="[('product_id','=',product_id)]"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="create_date" invisible="1"/>
|
||||
<field name="date"/>
|
||||
<field name="date_expected" on_change="onchange_date(date,date_expected)"/>
|
||||
</group>
|
||||
|
||||
<group string="Locations" groups="stock.group_locations">
|
||||
<field name="location_id" domain="[('usage','<>','view')]"/>
|
||||
<field name="location_dest_id" domain="[('usage','=','internal')]" groups="stock.group_locations"/>
|
||||
</group>
|
||||
|
||||
<group groups="stock.group_tracking_lot" string="Traceability">
|
||||
<label for="tracking_id"/>
|
||||
<div>
|
||||
<field name="tracking_id" groups="stock.group_tracking_lot" class="oe_inline"/>
|
||||
<button name="%(split_into)d" string="New Pack" type="action"
|
||||
groups="product.group_stock_packaging"
|
||||
icon="terp-stock_effects-object-colorize"
|
||||
states="draft,assigned,confirmed"/>
|
||||
</div>
|
||||
|
||||
<label for="prodlot_id"/>
|
||||
<div>
|
||||
<field name="prodlot_id" groups="stock.group_production_lot"
|
||||
context="{'location_id':location_id, 'product_id':product_id}"
|
||||
domain="[('product_id','=?',product_id)]"
|
||||
on_change="onchange_lot_id(prodlot_id,product_qty, location_id, product_id, product_uom)" class="oe_inline"/>
|
||||
<button name="%(track_line)d"
|
||||
groups="stock.group_tracking_lot"
|
||||
states="draft,waiting,confirmed,assigned"
|
||||
string="Split" type="action" icon="terp-stock_effects-object-colorize"/>
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
<field name="move_lines" context="{'address_in_id': partner_id, 'form_view_ref':'view_move_picking_form', 'tree_view_ref':'view_move_picking_tree'}"/>
|
||||
</page>
|
||||
<page string="Additional Info">
|
||||
<group>
|
||||
<group>
|
||||
<field name="move_type"/>
|
||||
<field name="type" groups="base.group_user"/>
|
||||
<field name="type" groups="base.group_no_one"/>
|
||||
<field name="auto_picking" groups="base.group_user"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="company_id" groups="base.group_multi_company" widget="selection"/>
|
||||
<field name="date_done"/>
|
||||
<field name="date_done" groups="base.group_no_one"/>
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
@@ -972,7 +874,7 @@
|
||||
|
||||
<record id="view_picking_out_tree" model="ir.ui.view">
|
||||
<field name="name">stock.picking.out.tree</field>
|
||||
<field name="model">stock.picking</field>
|
||||
<field name="model">stock.picking.out</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree colors="blue:state == 'draft';grey:state == 'cancel';red:state not in ('cancel', 'done') and min_date < current_date" string="Delivery Orders">
|
||||
<field name="name"/>
|
||||
@@ -1002,13 +904,16 @@
|
||||
<button name="action_assign" states="confirmed" string="Check Availability" type="object" class="oe_highlight"/>
|
||||
</xpath>
|
||||
<xpath expr="/form/header//button[@name='action_process']" position="replace">
|
||||
<button name="action_process" states="assigned" string="Deliver" type="object" icon="gtk-go-forward" class="oe_highlight"/>
|
||||
<button name="action_process" states="assigned" string="Deliver" type="object" class="oe_highlight"/>
|
||||
</xpath>
|
||||
<xpath expr="/form/header//field[@name='state']" position="replace">
|
||||
<field name="state" nolabel="1" readonly="1" widget="statusbar" statusbar_visible="draft,confirmed,assigned,done" statusbar_colors='{"auto":"blue", "confirmed":"blue"}'/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='partner_id']" position="replace">
|
||||
<field name="partner_id" on_change="onchange_partner_in(partner_id)" colspan="4" string="Customer"/>
|
||||
<field name="partner_id" on_change="onchange_partner_in(partner_id)" string="Customer"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='move_lines']" position="replace">
|
||||
<field name="move_lines" context="{'address_in_id': partner_id, 'picking_type': 'out', 'form_view_ref':'view_move_picking_form', 'tree_view_ref':'view_move_picking_tree'}"/>
|
||||
</xpath>
|
||||
<xpath expr="/form/sheet" position="after">
|
||||
<div class="oe_chatter">
|
||||
@@ -1131,7 +1036,10 @@
|
||||
<button name="action_process" states="assigned" string="Receive" type="object" class="oe_highlight"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='partner_id']" position="replace">
|
||||
<field name="partner_id" on_change="onchange_partner_in(partner_id)" colspan="4" string="Supplier"/>
|
||||
<field name="partner_id" on_change="onchange_partner_in(partner_id)" string="Supplier"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='move_lines']" position="replace">
|
||||
<field name="move_lines" context="{'address_in_id': partner_id, 'picking_type': 'in', 'form_view_ref':'view_move_picking_form', 'tree_view_ref':'view_move_picking_tree'}"/>
|
||||
</xpath>
|
||||
<xpath expr="/form/sheet" position="after">
|
||||
<div class="oe_chatter">
|
||||
@@ -1207,7 +1115,7 @@
|
||||
</record>
|
||||
<menuitem action="action_picking_tree4" id="menu_action_picking_tree4" parent="menu_stock_warehouse_mgmt" sequence="1"/>
|
||||
|
||||
<menuitem action="action_picking_tree6" id="menu_action_picking_tree6" parent="menu_stock_warehouse_mgmt" sequence="2"/>
|
||||
<menuitem action="action_picking_tree6" id="menu_action_picking_tree6" parent="menu_stock_warehouse_mgmt" sequence="2" groups="stock.group_locations"/>
|
||||
|
||||
<record id="view_move_tree" model="ir.ui.view">
|
||||
<field name="name">stock.move.tree</field>
|
||||
@@ -1218,6 +1126,7 @@
|
||||
<field name="name"/>
|
||||
<field name="picking_id" string="Reference"/>
|
||||
<field name="origin"/>
|
||||
<field name="type" on_change="onchange_move_type(type)"/>
|
||||
<field name="create_date" invisible="1" groups="base.group_no_one"/>
|
||||
<field name="product_id" on_change="onchange_product_id(product_id,location_id,location_dest_id, False)"/>
|
||||
<field name="product_qty" on_change="onchange_quantity(product_id, product_qty, product_uom, product_uos)"/>
|
||||
@@ -1251,6 +1160,45 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_move_picking_tree" model="ir.ui.view">
|
||||
<field name="name">stock.move.tree</field>
|
||||
<field name="model">stock.move</field>
|
||||
<field eval="4" name="priority"/>
|
||||
<field name="arch" type="xml">
|
||||
<tree colors="grey:scrapped == True" string="Stock Moves">
|
||||
<field name="product_id"/>
|
||||
<field name="product_qty" on_change="onchange_quantity(product_id, product_qty, product_uom, product_uos)"/>
|
||||
<field name="product_uom" string="Unit of Measure" groups="product.group_uom"/>
|
||||
<field name="product_uos" groups="product.group_uos"/>
|
||||
<button name="%(stock.move_scrap)d"
|
||||
string="Scrap Products" type="action"
|
||||
icon="gtk-convert" context="{'scrap': True}"
|
||||
states="draft,waiting,confirmed,assigned"
|
||||
groups="base.group_user"/>
|
||||
<field name="scrapped" invisible="1"/>
|
||||
<field name="prodlot_id" groups="stock.group_production_lot"/>
|
||||
<button
|
||||
name="%(stock.track_line)d"
|
||||
string="Split in Serial Number"
|
||||
groups="stock.group_production_lot"
|
||||
type="action" icon="terp-stock_effects-object-colorize"
|
||||
states="draft,waiting,confirmed,assigned"/>
|
||||
<field groups="stock.group_tracking_lot" name="tracking_id"/>
|
||||
<button name="setlast_tracking" string="Put in current pack" type="object"
|
||||
attrs="{'invisible': [('tracking_id','<>',False)]}"
|
||||
icon="terp-stock_effects-object-colorize"
|
||||
groups="stock.group_tracking_lot"
|
||||
states="draft,assigned,confirmed"/>
|
||||
<button name="%(split_into)d" string="Put in a new pack" type="action"
|
||||
groups="product.group_stock_packaging"
|
||||
icon="terp-stock_effects-object-colorize"
|
||||
states="draft,assigned,confirmed"/>
|
||||
<field name="location_dest_id" groups="stock.group_locations"/>
|
||||
<field name="state"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_move_form" model="ir.ui.view">
|
||||
<field name="name">stock.move.form</field>
|
||||
<field name="model">stock.move</field>
|
||||
@@ -1292,6 +1240,7 @@
|
||||
</group>
|
||||
<group name="origin_grp" string="Origin">
|
||||
<field name="picking_id"/>
|
||||
<field name="type" on_change="onchange_move_type(type)"/>
|
||||
<field name="location_id" groups="stock.group_locations"/>
|
||||
<field name="create_date" groups="base.group_no_one"/>
|
||||
</group>
|
||||
@@ -1329,6 +1278,81 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_move_picking_form" model="ir.ui.view">
|
||||
<field name="name">stock.move.form</field>
|
||||
<field name="model">stock.move</field>
|
||||
<field eval="2" name="priority"/>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Stock Moves" version="7.0">
|
||||
<header>
|
||||
<span groups="base.group_user">
|
||||
<button name="force_assign" states="confirmed" string="Force Availability" type="object"/>
|
||||
<button name="action_confirm" states="draft" string="Confirm" type="object"/>
|
||||
<button name="cancel_assign" states="assigned" string="Cancel Availability" type="object"/>
|
||||
</span>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,assigned,done"/>
|
||||
</header>
|
||||
<group>
|
||||
<group>
|
||||
<field name="name" invisible="1"/>
|
||||
<field name="product_id" on_change="onchange_product_id(product_id,location_id,location_dest_id, parent.partner_id)"/>
|
||||
<field name="type" invisible="1"/>
|
||||
<label for="product_qty"/>
|
||||
<div>
|
||||
<field name="product_qty" on_change="onchange_quantity(product_id, product_qty, product_uom, product_uos)" class="oe_inline"/>
|
||||
<field name="product_uom" string="Unit Of Measure" groups="product.group_uom" class="oe_inline"/>
|
||||
<button name="%(stock.move_scrap)d"
|
||||
string="Scrap" type="action"
|
||||
icon="gtk-convert" context="{'scrap': True}"
|
||||
states="draft,waiting,confirmed,assigned"
|
||||
groups="base.group_user"/>
|
||||
</div>
|
||||
<label for="product_uos_qty" groups="product.group_uos"/>
|
||||
<div groups="product.group_uos">
|
||||
<field name="product_uos_qty" on_change="onchange_uos_quantity(product_id, product_uos_qty, product_uos, product_uom)" class="oe_inline"/>
|
||||
<field name="product_uos" on_change="onchange_quantity(product_id, product_qty, product_uom, product_uos)" class="oe_inline"/>
|
||||
</div>
|
||||
<field name="product_packaging" groups="product.group_stock_packaging" domain="[('product_id','=',product_id)]"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="create_date" invisible="1"/>
|
||||
<field name="date"/>
|
||||
<field name="date_expected" on_change="onchange_date(date,date_expected)"/>
|
||||
</group>
|
||||
|
||||
<group string="Locations" groups="stock.group_locations">
|
||||
<field name="location_id" domain="[('usage','<>','view')]"/>
|
||||
<field name="location_dest_id" domain="[('usage','=','internal')]" groups="stock.group_locations"/>
|
||||
</group>
|
||||
|
||||
<group groups="stock.group_tracking_lot" string="Traceability">
|
||||
<label for="tracking_id"/>
|
||||
<div>
|
||||
<field name="tracking_id" groups="stock.group_tracking_lot" class="oe_inline"/>
|
||||
<button name="%(split_into)d"
|
||||
string="New Pack" type="action"
|
||||
groups="product.group_stock_packaging"
|
||||
icon="terp-stock_effects-object-colorize"
|
||||
states="draft,assigned,confirmed"/>
|
||||
</div>
|
||||
|
||||
<label for="prodlot_id"/>
|
||||
<div>
|
||||
<field name="prodlot_id" groups="stock.group_production_lot"
|
||||
context="{'location_id':location_id, 'product_id':product_id}"
|
||||
domain="[('product_id','=?',product_id)]"
|
||||
on_change="onchange_lot_id(prodlot_id,product_qty, location_id, product_id, product_uom)" class="oe_inline"/>
|
||||
<button name="%(track_line)d"
|
||||
groups="stock.group_tracking_lot"
|
||||
states="draft,waiting,confirmed,assigned"
|
||||
string="Split" type="action" icon="terp-stock_effects-object-colorize"/>
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_move_search" model="ir.ui.view">
|
||||
<field name="name">stock.move.search</field>
|
||||
<field name="model">stock.move</field>
|
||||
@@ -1380,6 +1404,21 @@
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window.view" id="action_stock_move_tree_all">
|
||||
<field name="sequence" eval="1"/>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="view_id" ref="view_move_tree"/>
|
||||
<field name="act_window_id" ref="action_move_form2"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window.view" id="action_stock_move_form_all">
|
||||
<field name="sequence" eval="3"/>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="view_move_form"/>
|
||||
<field name="act_window_id" ref="action_move_form2"/>
|
||||
</record>
|
||||
|
||||
<menuitem action="action_move_form2" id="menu_action_move_form2" parent="menu_traceability" sequence="3"/>
|
||||
|
||||
<!--
|
||||
@@ -1453,7 +1492,7 @@
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">['|','&',('picking_id','=',False),('location_id.usage', 'in', ['customer','supplier']),'&',('picking_id','!=',False),('picking_id.type','=','in')]</field>
|
||||
<field name="view_id" ref="view_move_tree_reception_picking"/>
|
||||
<field name="context" eval="'{\'product_receive\' : True, \'default_location_id\':%d, \'default_location_dest_id\':%d, \'search_default_future\': 1}' % (ref('stock_location_suppliers'),ref('stock_location_stock') )"/>
|
||||
<field name="context">{'product_receive': True, 'search_default_future': True, 'picking_type': 'in'}</field>
|
||||
<field name="help" type="html">
|
||||
<p>
|
||||
Click to register a product reception.
|
||||
@@ -1588,7 +1627,7 @@
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">['|','&',('picking_id','=',False),('location_dest_id.usage', 'in', ['customer','supplier']),'&',('picking_id','!=',False),('picking_id.type','=','out')]</field>
|
||||
<field name="view_id" ref="view_move_tree_reception_picking"/>
|
||||
<field name="context" eval="'{\'default_location_id\':%d, \'default_location_dest_id\':%d, \'search_default_future\': 1}' % (ref('stock_location_stock'),ref('stock_location_customers'))"/>
|
||||
<field name="context">{'picking_type': 'out', 'search_default_future': True}</field>
|
||||
<field name="help" type="html">
|
||||
<p>
|
||||
You will find in this list all products you have to deliver to
|
||||
|
||||
@@ -25,11 +25,23 @@ from tools.translate import _
|
||||
class stock_fill_inventory(osv.osv_memory):
|
||||
_name = "stock.fill.inventory"
|
||||
_description = "Import Inventory"
|
||||
|
||||
def _default_location(self, cr, uid, ids, context=None):
|
||||
try:
|
||||
loc_model, location_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'stock', 'stock_location_stock')
|
||||
except ValueError, e:
|
||||
return False
|
||||
return location_id or False
|
||||
|
||||
_columns = {
|
||||
'location_id': fields.many2one('stock.location', 'Location', required=True),
|
||||
'recursive': fields.boolean("Include children",help="If checked, products contained in child locations of selected location will be included as well."),
|
||||
'set_stock_zero': fields.boolean("Set to zero",help="If checked, all product quantities will be set to zero to help ensure a real physical inventory is done"),
|
||||
}
|
||||
_defaults = {
|
||||
'location_id': _default_location,
|
||||
}
|
||||
|
||||
def view_init(self, cr, uid, fields_list, context=None):
|
||||
"""
|
||||
Creates view dynamically and adding fields at runtime.
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
<form string="Import Inventory" version="7.0">
|
||||
<separator string="Import current product inventory from the following location"/>
|
||||
<group>
|
||||
<field name="location_id"/>
|
||||
<field name="recursive"/>
|
||||
<field name="location_id" groups="stock.group_locations"/>
|
||||
<field name="recursive" groups="stock.group_locations"/>
|
||||
<field name="set_stock_zero"/>
|
||||
</group>
|
||||
<footer>
|
||||
@@ -21,14 +21,16 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<act_window name="Import Inventory"
|
||||
res_model="stock.fill.inventory"
|
||||
src_model="stock.inventory"
|
||||
view_mode="form"
|
||||
target="new"
|
||||
context="{'search_default_in_location':1}"
|
||||
key2="client_action_multi"
|
||||
id="action_view_stock_fill_inventory"/>
|
||||
|
||||
<record id="action_view_stock_fill_inventory" model="ir.actions.act_window">
|
||||
<field name="name">Fill Inventory</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">stock.fill.inventory</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="view_stock_fill_inventory"/>
|
||||
<field name="target">new</field>
|
||||
<field name="context">{'search_default_in_location':1}</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
@@ -78,7 +78,7 @@ class stock_partial_picking(osv.osv_memory):
|
||||
if context is None:
|
||||
context={}
|
||||
res = super(stock_partial_picking, self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu)
|
||||
type = context.get('active_model','').split('.')[-1]
|
||||
type = context.get('default_type', False)
|
||||
if type:
|
||||
doc = etree.XML(res['arch'])
|
||||
for node in doc.xpath("//button[@name='do_partial']"):
|
||||
|
||||
Reference in New Issue
Block a user