[FWD] [MERGE] Forward port of addons-v7 until rev. 8736.
bzr revid: tde@openerp.com-20130221105247-ezl63lpl4vdjxtys
This commit is contained in:
commit
e0372e8c6d
|
@ -343,7 +343,7 @@ class account_journal_cashbox_line(osv.osv):
|
|||
_rec_name = 'pieces'
|
||||
_columns = {
|
||||
'pieces': fields.float('Values', digits_compute=dp.get_precision('Account')),
|
||||
'journal_id' : fields.many2one('account.journal', 'Journal', required=True, select=1),
|
||||
'journal_id' : fields.many2one('account.journal', 'Journal', required=True, select=1, ondelete="cascade"),
|
||||
}
|
||||
|
||||
_order = 'pieces asc'
|
||||
|
|
|
@ -449,11 +449,15 @@ class account_invoice(osv.osv):
|
|||
context = {}
|
||||
invoices = self.read(cr, uid, ids, ['state','internal_number'], context=context)
|
||||
unlink_ids = []
|
||||
|
||||
for t in invoices:
|
||||
if t['state'] in ('draft', 'cancel') and t['internal_number']== False:
|
||||
unlink_ids.append(t['id'])
|
||||
if t['state'] not in ('draft', 'cancel'):
|
||||
raise openerp.exceptions.Warning(_('You cannot delete an invoice which is not draft or cancelled. You should refund it instead.'))
|
||||
elif t['internal_number']:
|
||||
raise openerp.exceptions.Warning(_('You cannot delete an invoice after it has been validated (and received a number). You can set it back to "Draft" state and modify its content, then re-confirm it.'))
|
||||
else:
|
||||
raise osv.except_osv(_('Invalid Action!'), _('You can not delete an invoice which is not cancelled. You should refund it instead.'))
|
||||
unlink_ids.append(t['id'])
|
||||
|
||||
osv.osv.unlink(self, cr, uid, unlink_ids, context=context)
|
||||
return True
|
||||
|
||||
|
@ -1147,6 +1151,11 @@ class account_invoice(osv.osv):
|
|||
return self.name_get(cr, user, ids, context)
|
||||
|
||||
def _refund_cleanup_lines(self, cr, uid, lines, context=None):
|
||||
"""Convert records to dict of values suitable for one2many line creation
|
||||
|
||||
:param list(browse_record) lines: records to convert
|
||||
:return: list of command tuple for one2many line creation [(0, 0, dict of valueis), ...]
|
||||
"""
|
||||
clean_lines = []
|
||||
for line in lines:
|
||||
clean_line = {}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
<field name="parent_id" invisible="1"/>
|
||||
<field name="state" invisible="1"/>
|
||||
<field name="type" invisible="1"/>
|
||||
<field name="template_id" invisible="1"/>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
</tree>
|
||||
</field>
|
||||
|
|
|
@ -56,7 +56,7 @@ class report_account_common(report_sxw.rml_parse, common_report_header):
|
|||
for report in self.pool.get('account.financial.report').browse(self.cr, self.uid, ids2, context=data['form']['used_context']):
|
||||
vals = {
|
||||
'name': report.name,
|
||||
'balance': report.balance * report.sign,
|
||||
'balance': report.balance * report.sign or 0.0,
|
||||
'type': 'report',
|
||||
'level': bool(report.style_overwrite) and report.style_overwrite or report.level,
|
||||
'account_type': report.type =='sum' and 'view' or False, #used to underline the financial report balances
|
||||
|
|
|
@ -180,9 +180,9 @@ class account_invoice_refund(osv.osv_memory):
|
|||
'journal_id', 'period_id'], context=context)
|
||||
invoice = invoice[0]
|
||||
del invoice['id']
|
||||
invoice_lines = inv_line_obj.read(cr, uid, invoice['invoice_line'], context=context)
|
||||
invoice_lines = inv_line_obj.browse(cr, uid, invoice['invoice_line'], context=context)
|
||||
invoice_lines = inv_obj._refund_cleanup_lines(cr, uid, invoice_lines)
|
||||
tax_lines = inv_tax_obj.read(cr, uid, invoice['tax_line'], context=context)
|
||||
tax_lines = inv_tax_obj.browse(cr, uid, invoice['tax_line'], context=context)
|
||||
tax_lines = inv_obj._refund_cleanup_lines(cr, uid, tax_lines)
|
||||
invoice.update({
|
||||
'type': inv.type,
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
##############################################################################
|
||||
|
||||
import account_analytic_analysis
|
||||
import cron_account_analytic_account
|
||||
import res_config
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -38,7 +38,6 @@ Adds menu to show relevant information to each manager.You can also view the rep
|
|||
'security/ir.model.access.csv',
|
||||
'security/account_analytic_analysis_security.xml',
|
||||
'account_analytic_analysis_view.xml',
|
||||
'account_analytic_analysis_menu.xml',
|
||||
'account_analytic_analysis_cron.xml',
|
||||
'res_config_view.xml',
|
||||
],
|
||||
|
|
|
@ -18,6 +18,9 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
import datetime
|
||||
import logging
|
||||
import time
|
||||
|
||||
from openerp.osv import osv, fields
|
||||
from openerp.osv.orm import intersect, except_orm
|
||||
|
@ -26,6 +29,7 @@ from openerp.tools.translate import _
|
|||
|
||||
from openerp.addons.decimal_precision import decimal_precision as dp
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
class account_analytic_account(osv.osv):
|
||||
_name = "account.analytic.account"
|
||||
|
@ -484,7 +488,48 @@ class account_analytic_account(osv.osv):
|
|||
res['value']['to_invoice'] = template.to_invoice.id
|
||||
res['value']['pricelist_id'] = template.pricelist_id.id
|
||||
return res
|
||||
account_analytic_account()
|
||||
|
||||
def cron_account_analytic_account(self, cr, uid, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
remind = {}
|
||||
|
||||
def fill_remind(key, domain, write_pending=False):
|
||||
base_domain = [
|
||||
('type', '=', 'contract'),
|
||||
('partner_id', '!=', False),
|
||||
('manager_id', '!=', False),
|
||||
('manager_id.email', '!=', False),
|
||||
]
|
||||
base_domain.extend(domain)
|
||||
|
||||
accounts_ids = self.search(cr, uid, base_domain, context=context, order='name asc')
|
||||
accounts = self.browse(cr, uid, accounts_ids, context=context)
|
||||
for account in accounts:
|
||||
if write_pending:
|
||||
account.write({'state' : 'pending'}, context=context)
|
||||
remind_user = remind.setdefault(account.manager_id.id, {})
|
||||
remind_type = remind_user.setdefault(key, {})
|
||||
remind_partner = remind_type.setdefault(account.partner_id, []).append(account)
|
||||
|
||||
# Already expired
|
||||
fill_remind("old", [('state', 'in', ['pending'])])
|
||||
|
||||
# Expires now
|
||||
fill_remind("new", [('state', 'in', ['draft', 'open']), '|', '&', ('date', '!=', False), ('date', '<=', time.strftime('%Y-%m-%d')), ('is_overdue_quantity', '=', True)], True)
|
||||
|
||||
# Expires in less than 30 days
|
||||
fill_remind("future", [('state', 'in', ['draft', 'open']), ('date', '!=', False), ('date', '<', (datetime.datetime.now() + datetime.timedelta(30)).strftime("%Y-%m-%d"))])
|
||||
|
||||
context['base_url'] = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url')
|
||||
context['action_id'] = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account_analytic_analysis', 'action_account_analytic_overdue_all')[1]
|
||||
template_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account_analytic_analysis', 'account_analytic_cron_email_template')[1]
|
||||
for user_id, data in remind.items():
|
||||
context["data"] = data
|
||||
_logger.debug("Sending reminder to uid %s", user_id)
|
||||
self.pool.get('email.template').send_mail(cr, uid, template_id, user_id, context=context)
|
||||
|
||||
return True
|
||||
|
||||
class account_analytic_account_summary_user(osv.osv):
|
||||
_name = "account_analytic_analysis.summary.user"
|
||||
|
@ -538,8 +583,6 @@ class account_analytic_account_summary_user(osv.osv):
|
|||
lu.user_id as "user",
|
||||
unit_amount
|
||||
from lu, mu)''')
|
||||
|
||||
account_analytic_account_summary_user()
|
||||
|
||||
class account_analytic_account_summary_month(osv.osv):
|
||||
_name = "account_analytic_analysis.summary.month"
|
||||
|
@ -600,6 +643,4 @@ class account_analytic_account_summary_month(osv.osv):
|
|||
'GROUP BY d.month, d.account_id ' \
|
||||
')')
|
||||
|
||||
|
||||
account_analytic_account_summary_month()
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -1,15 +1,77 @@
|
|||
<?xml version="1.0" encoding='UTF-8'?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<record id="account_analytic_cron_email_template" model="email.template">
|
||||
<field name="name">Contract expiration reminder</field>
|
||||
<field name="email_from">${object.email or ''}</field>
|
||||
<field name="subject">Contract expiration reminder ${user.company_id.name}</field>
|
||||
<field name="email_to">${object.email}</field>
|
||||
<field name="lang">${object.lang}</field>
|
||||
<field name="model_id" ref="base.model_res_users"/>
|
||||
<field name="auto_delete" eval="True"/>
|
||||
<field name="body_html"><![CDATA[
|
||||
Hello ${object.name},
|
||||
|
||||
% macro account_table(values):
|
||||
<table cellspacing="1" border="1" cellpadding="4">
|
||||
<tr>
|
||||
<th>Customer</th>
|
||||
<th>Contract</th>
|
||||
<th>Dates</th>
|
||||
<th>Prepaid Units</th>
|
||||
<th>Contact</th>
|
||||
</tr>
|
||||
% for partner, accounts in values:
|
||||
% for account in accounts:
|
||||
<tr>
|
||||
<td>${partner.name}</td>
|
||||
<td><a href="${ctx["base_url"]}/#action=${ctx["action_id"]}&id=${account.id}&view_type=form">${account.name}</a></td>
|
||||
<td>${account.date_start} to ${account.date and account.date or '???'}</td>
|
||||
<td>
|
||||
% if account.quantity_max != 0.0:
|
||||
${account.remaining_hours}/${account.quantity_max} units
|
||||
% endif
|
||||
</td>
|
||||
<td>${account.partner_id.phone or ''}, ${account.partner_id.email or ''}</td>
|
||||
</tr>
|
||||
% endfor
|
||||
% endfor
|
||||
</table>
|
||||
% endmacro
|
||||
|
||||
% if "new" in ctx["data"]:
|
||||
<h2>The following contracts just expired: </h2>
|
||||
${account_table(ctx["data"]["new"].iteritems())}
|
||||
% endif
|
||||
|
||||
% if "old" in ctx["data"]:
|
||||
<h2>The following expired contracts are still not processed: </h2>
|
||||
${account_table(ctx["data"]["old"].iteritems())}
|
||||
% endif
|
||||
|
||||
% if "future" in ctx["data"]:
|
||||
<h2>The following contracts will expire in less than one month: </h2>
|
||||
${account_table(ctx["data"]["future"].iteritems())}
|
||||
% endif
|
||||
|
||||
</pre>
|
||||
|
||||
]]></field>
|
||||
</record>
|
||||
<!--
|
||||
|
||||
-->
|
||||
|
||||
<record model="ir.cron" id="account_analytic_cron">
|
||||
<field name="name">Analytic Account Report for Sales</field>
|
||||
<field name="name">Contract expiration reminder</field>
|
||||
<field name="interval_number">1</field>
|
||||
<field name="interval_type">weeks</field>
|
||||
<field name="numbercall">-1</field>
|
||||
<field eval="False" name="doall"/>
|
||||
<field eval="'account.analytic.account'" name="model"/>
|
||||
<field eval="'cron_account_analytic_account'" name="function"/>
|
||||
<field eval="'()'" name="args"/>
|
||||
<field name="doall" eval="False"/>
|
||||
<field name="model" eval="'account.analytic.account'"/>
|
||||
<field name="function" eval="'cron_account_analytic_account'"/>
|
||||
<field name="args" eval="'()'" />
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
|
@ -1,104 +0,0 @@
|
|||
<openerp>
|
||||
<data>
|
||||
<menuitem id="base.menu_invoiced" name="Invoicing" parent="base.menu_base_partner" sequence="5"/>
|
||||
|
||||
<record id="action_hr_tree_invoiced_all" model="ir.actions.act_window">
|
||||
<field name="name">Time & Materials to Invoice</field>
|
||||
<field name="res_model">account.analytic.line</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">[('invoice_id','=',False)]</field>
|
||||
<field name="context">{'search_default_to_invoice': 1, 'search_default_sales': 1}</field>
|
||||
<field name="search_view_id" ref="account.view_account_analytic_line_filter"/>
|
||||
<field name="help" type="html">
|
||||
<p>
|
||||
You will find here timesheets and purchases you did for
|
||||
contracts that can be reinvoiced to the customer. If you want
|
||||
to record new activities to invoice, you should use the timesheet
|
||||
menu instead.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
<menuitem action="action_hr_tree_invoiced_all" id="menu_action_hr_tree_invoiced_all" parent="base.menu_invoiced" sequence="5"/>
|
||||
|
||||
<record id="view_account_analytic_account_overdue_search" model="ir.ui.view">
|
||||
<field name="name">account.analytic.account.search</field>
|
||||
<field name="model">account.analytic.account</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Contracts">
|
||||
<field name="name" filter_domain="['|', ('name','ilike',self),('code','ilike',self)]" string="Contract"/>
|
||||
<field name="date"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="manager_id"/>
|
||||
<field name="parent_id"/>
|
||||
<filter name="open" string="Open" domain="[('state','in',('open','draft'))]" help="Contracts in progress"/>
|
||||
<filter name="pending" string="Pending" domain="[('state','=','pending')]" help="Pending contracts to renew with your customer"/>
|
||||
<filter string="To Renew" domain="['|', '&', ('date', '!=', False), ('date', '<=', time.strftime('%%Y-%%m-%%d')), ('is_overdue_quantity', '=', True)]" name="renew"
|
||||
help="The contracts to be renewed because the deadline is passed or the working hours are higher than the allocated hours" />
|
||||
<separator/>
|
||||
<filter string="Customer Contracts" help="Contracts assigned to a customer." name="has_partner" domain="[('partner_id', '!=', False)]"/>
|
||||
<filter string="Contracts not assigned" help="Contracts that are not assigned to an account manager." domain="[('manager_id', '=', False)]"/>
|
||||
<separator/>
|
||||
<group expand="0" string="Group By...">
|
||||
<filter string="Status" domain="[]" context="{'group_by':'state'}"/>
|
||||
<filter string="Account Manager" domain="[]" context="{'group_by':'manager_id'}"/>
|
||||
<filter string="Partner" domain="[]" context="{'group_by':'partner_id'}"/>
|
||||
<filter string="Parent" domain="[]" context="{'group_by':'parent_id'}"/>
|
||||
<filter string="Start Date" domain="[]" context="{'group_by' : 'date_start'}" />
|
||||
<filter string="End Date" domain="[]" context="{'group_by' : 'date'}" />
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="action_account_analytic_overdue" model="ir.actions.act_window">
|
||||
<field name="name">Contracts to Renew</field>
|
||||
<field name="res_model">account.analytic.account</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="context">{'search_default_manager_id':uid, 'search_default_pending':1, 'search_default_renew':1}</field>
|
||||
<field name="domain">[('type','=','contract')]</field>
|
||||
<field name="search_view_id" ref="view_account_analytic_account_overdue_search"/>
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
Click to define a new contract.
|
||||
</p><p>
|
||||
You will find here the contracts to be renewed because the
|
||||
end date is passed or the working effort is higher than the
|
||||
maximum authorized one.
|
||||
</p><p>
|
||||
OpenERP automatically sets contracts to be renewed in a pending
|
||||
state. After the negociation, the salesman should close or renew
|
||||
pending contracts.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
<menuitem action="action_account_analytic_overdue" id="menu_action_account_analytic_overdue" sequence="50" parent="base.menu_invoiced"/>
|
||||
|
||||
<record id="action_account_analytic_overdue_all" model="ir.actions.act_window">
|
||||
<field name="name">Contracts</field>
|
||||
<field name="res_model">account.analytic.account</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="context">{'default_type':'contract', 'search_default_open':1, 'search_default_pending':1, 'default_manager_id':uid}</field>
|
||||
<field name="domain">[('type','=','contract')]</field>
|
||||
<field name="search_view_id" ref="view_account_analytic_account_overdue_search"/>
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
Click to create a new contract.
|
||||
</p><p>
|
||||
Use contracts to follow tasks, issues, timesheets or invoicing based on
|
||||
work done, expenses and/or sales orders. OpenERP will automatically manage
|
||||
the alerts for the renewal of the contracts to the right salesperson.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
<menuitem id="base.menu_sales" name="Sales"
|
||||
parent="base.menu_base_partner"
|
||||
sequence="1"/>
|
||||
<menuitem action="action_account_analytic_overdue_all" id="menu_action_account_analytic_overdue_all" sequence="7" parent="base.menu_sales"/>
|
||||
|
||||
|
||||
</data>
|
||||
</openerp>
|
|
@ -1,17 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<!--
|
||||
Analytic Account form
|
||||
-->
|
||||
|
||||
<record model="ir.actions.act_window" id="action_sales_order">
|
||||
<field name="name">Sales Orders</field>
|
||||
<field name="res_model">sale.order</field>
|
||||
<field name="src_model">account.analytic.account</field>
|
||||
</record>
|
||||
|
||||
<!-- Inherited Analytic Account form for contracts -->
|
||||
<record id="account_analytic_account_form_form" model="ir.ui.view">
|
||||
<field name="name">account.analytic.account.invoice.form.inherit</field>
|
||||
<field name="model">account.analytic.account</field>
|
||||
|
@ -142,19 +138,7 @@
|
|||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_account_analytic_account_tree_c2c_3" model="ir.ui.view">
|
||||
<field name="name">account.analytic.account.tree</field>
|
||||
<field name="model">account.analytic.account</field>
|
||||
<field name="inherit_id" ref="account.view_account_analytic_account_list"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="date" position="before">
|
||||
<field name="last_invoice_date"/>
|
||||
<field name="toinvoice_total"/>
|
||||
<field name="remaining_hours"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Inherited Analytic Account form for template required -->
|
||||
<record id="view_account_analytic_account_template_required" model="ir.ui.view">
|
||||
<field name="name">account.analytic.account.form.template.required</field>
|
||||
<field name="model">account.analytic.account</field>
|
||||
|
@ -167,6 +151,132 @@
|
|||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Inherited Analytic Account list for contracts -->
|
||||
<record id="view_account_analytic_account_tree_c2c_3" model="ir.ui.view">
|
||||
<field name="name">account.analytic.account.list.contract</field>
|
||||
<field name="model">account.analytic.account</field>
|
||||
<field name="inherit_id" ref="account.view_account_analytic_account_list"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="date" position="before">
|
||||
<field name="last_invoice_date"/>
|
||||
<field name="toinvoice_total"/>
|
||||
<field name="remaining_hours"/>
|
||||
<field name="pricelist_id" invisible="1"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Analytic Account search view for contract -->
|
||||
<record id="view_account_analytic_account_overdue_search" model="ir.ui.view">
|
||||
<field name="name">account.analytic.account.search</field>
|
||||
<field name="model">account.analytic.account</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Contracts">
|
||||
<field name="name" filter_domain="['|', ('name','ilike',self),('code','ilike',self)]" string="Contract"/>
|
||||
<field name="date"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="manager_id"/>
|
||||
<field name="parent_id"/>
|
||||
<filter name="open" string="In Progress" domain="[('state','in',('open','draft'))]" help="Contracts in progress (open, draft)"/>
|
||||
<filter name="pending" string="To Renew" domain="[('state','=','pending')]" help="Pending contracts"/>
|
||||
<filter name="closed" string="Closed" domain="[('state','=','pending')]" help="Closed contracts"/>
|
||||
<filter name="cancelled" string="Cancelled" domain="[('state','=','cancel')]" help="Cancelled contracts"/>
|
||||
<separator/>
|
||||
<filter
|
||||
string="Expired or consumed"
|
||||
domain="[('state','in',('open','draft','pending')), '|', '&', ('date', '!=', False), ('date', '<=', time.strftime('%%Y-%%m-%%d')), ('is_overdue_quantity', '=', True)]"
|
||||
help="End date passed or prepaid unit consumed" />
|
||||
<filter
|
||||
string="Expiring soon"
|
||||
domain="[('date', '!=', False), ('date', '<=', (context_today() + datetime.timedelta(30)).strftime('%%Y-%%m-%%d') )]"
|
||||
help="End date is in the next month" />
|
||||
<separator/>
|
||||
<filter string="Customer Contracts" help="Contracts assigned to a customer." name="has_partner" domain="[('partner_id', '!=', False)]"/>
|
||||
<filter string="Contracts not assigned" help="Contracts that are not assigned to an account manager." domain="[('manager_id', '=', False)]"/>
|
||||
<separator/>
|
||||
<group expand="0" string="Group By...">
|
||||
<filter string="Status" domain="[]" context="{'group_by':'state'}"/>
|
||||
<filter string="Account Manager" domain="[]" context="{'group_by':'manager_id'}"/>
|
||||
<filter string="Partner" domain="[]" context="{'group_by':'partner_id'}"/>
|
||||
<filter string="Parent" domain="[]" context="{'group_by':'parent_id'}"/>
|
||||
<filter string="Template" domain="[]" context="{'group_by':'template_id'}"/>
|
||||
<filter string="Start Date" domain="[]" context="{'group_by' : 'date_start'}" />
|
||||
<filter string="End Date" domain="[]" context="{'group_by' : 'date'}" />
|
||||
<filter string="Pricelist" domain="[]" context="{'group_by' : 'pricelist_id'}" />
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Action Sales/Sales/Contracts -->
|
||||
<record id="action_account_analytic_overdue_all" model="ir.actions.act_window">
|
||||
<field name="name">Contracts</field>
|
||||
<field name="res_model">account.analytic.account</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="context">{'default_type':'contract', 'search_default_open':1, 'search_default_pending':1, 'default_manager_id':uid}</field>
|
||||
<field name="domain">[('type','=','contract')]</field>
|
||||
<field name="search_view_id" ref="view_account_analytic_account_overdue_search"/>
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
Click to create a new contract.
|
||||
</p><p>
|
||||
Use contracts to follow tasks, issues, timesheets or invoicing based on
|
||||
work done, expenses and/or sales orders. OpenERP will automatically manage
|
||||
the alerts for the renewal of the contracts to the right salesperson.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
<menuitem id="base.menu_sales" name="Sales" parent="base.menu_base_partner" sequence="1"/>
|
||||
<menuitem action="action_account_analytic_overdue_all" id="menu_action_account_analytic_overdue_all" sequence="7" parent="base.menu_sales"/>
|
||||
|
||||
<!-- Action Sales/Invoicing/Time and Material to Invoice -->
|
||||
<record id="action_hr_tree_invoiced_all" model="ir.actions.act_window">
|
||||
<field name="name">Time & Materials to Invoice</field>
|
||||
<field name="res_model">account.analytic.line</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">[('invoice_id','=',False)]</field>
|
||||
<field name="context">{'search_default_to_invoice': 1, 'search_default_sales': 1}</field>
|
||||
<field name="search_view_id" ref="account.view_account_analytic_line_filter"/>
|
||||
<field name="help" type="html">
|
||||
<p>
|
||||
You will find here timesheets and purchases you did for
|
||||
contracts that can be reinvoiced to the customer. If you want
|
||||
to record new activities to invoice, you should use the timesheet
|
||||
menu instead.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
<menuitem id="base.menu_invoiced" name="Invoicing" parent="base.menu_base_partner" sequence="5"/>
|
||||
<menuitem action="action_hr_tree_invoiced_all" id="menu_action_hr_tree_invoiced_all" parent="base.menu_invoiced" sequence="5"/>
|
||||
|
||||
<!-- Action Sales/Invoicing/Contract to renew -->
|
||||
<record id="action_account_analytic_overdue" model="ir.actions.act_window">
|
||||
<field name="name">Contracts to Renew</field>
|
||||
<field name="res_model">account.analytic.account</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="context">{'search_default_manager_id':uid, 'search_default_pending':1, 'search_default_renew':1}</field>
|
||||
<field name="domain">[('type','=','contract')]</field>
|
||||
<field name="search_view_id" ref="view_account_analytic_account_overdue_search"/>
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
Click to define a new contract.
|
||||
</p><p>
|
||||
You will find here the contracts to be renewed because the
|
||||
end date is passed or the working effort is higher than the
|
||||
maximum authorized one.
|
||||
</p><p>
|
||||
OpenERP automatically sets contracts to be renewed in a pending
|
||||
state. After the negociation, the salesman should close or renew
|
||||
pending contracts.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
<menuitem action="action_account_analytic_overdue" id="menu_action_account_analytic_overdue" sequence="50" parent="base.menu_invoiced"/>
|
||||
|
||||
<!-- Action Sales/Configuration/Contract template -->
|
||||
<record id="template_of_contract_action" model="ir.actions.act_window">
|
||||
<field name="name">Contract Template</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
|
@ -184,8 +294,8 @@
|
|||
terms and conditions of the contract.
|
||||
</p>
|
||||
</field>
|
||||
|
||||
</record>
|
||||
<menuitem action="template_of_contract_action" id="menu_template_of_contract_action" parent="base.menu_base_config"/>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
|
@ -1,79 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
from mako.template import Template
|
||||
import time
|
||||
try:
|
||||
import cStringIO as StringIO
|
||||
except ImportError:
|
||||
import StringIO
|
||||
|
||||
from openerp import tools
|
||||
from openerp.osv import osv
|
||||
|
||||
MAKO_TEMPLATE = u"""Hello ${user.name},
|
||||
|
||||
Here is a list of contracts that have to be renewed for two
|
||||
possible reasons:
|
||||
- the end of contract date is passed
|
||||
- the customer consumed more hours than expected
|
||||
|
||||
Can you contact the customer in order to sell a new or renew its contract.
|
||||
The contract has been set with a pending state, can you update the status
|
||||
of the analytic account following this rule:
|
||||
- Set Done: if the customer does not want to renew
|
||||
- Set Open: if the customer purchased an extra contract
|
||||
|
||||
Here is the list of contracts to renew:
|
||||
% for partner, accounts in partners.iteritems():
|
||||
* ${partner.name}
|
||||
% for account in accounts:
|
||||
- Name: ${account.name}
|
||||
% if account.quantity_max != 0.0:
|
||||
- Quantity: ${account.quantity}/${account.quantity_max} hours
|
||||
% endif
|
||||
- Dates: ${account.date_start} to ${account.date and account.date or '???'}
|
||||
- Contacts:
|
||||
${account.partner_id.name}, ${account.partner_id.phone or ''}, ${account.partner_id.email or ''}
|
||||
|
||||
% endfor
|
||||
% endfor
|
||||
|
||||
You can use the report in the menu: Sales > Invoicing > Overdue Accounts
|
||||
|
||||
Regards,
|
||||
|
||||
--
|
||||
OpenERP
|
||||
"""
|
||||
|
||||
class analytic_account(osv.osv):
|
||||
_inherit = 'account.analytic.account'
|
||||
|
||||
def cron_account_analytic_account(self, cr, uid, context=None):
|
||||
domain = [
|
||||
('name', 'not ilike', 'maintenance'),
|
||||
('partner_id', '!=', False),
|
||||
('user_id', '!=', False),
|
||||
('user_id.email', '!=', False),
|
||||
('state', 'in', ('draft', 'open')),
|
||||
'|', ('date', '<', time.strftime('%Y-%m-%d')), ('date', '=', False),
|
||||
]
|
||||
|
||||
account_ids = self.search(cr, uid, domain, context=context, order='name asc')
|
||||
accounts = self.browse(cr, uid, account_ids, context=context)
|
||||
|
||||
users = dict()
|
||||
for account in accounts:
|
||||
users.setdefault(account.user_id, dict()).setdefault(account.partner_id, []).append(account)
|
||||
|
||||
account.write({'state' : 'pending'}, context=context)
|
||||
|
||||
for user, data in users.iteritems():
|
||||
subject = '[OPENERP] Reporting: Analytic Accounts'
|
||||
body = Template(MAKO_TEMPLATE).render_unicode(user=user, partners=data)
|
||||
tools.email_send('noreply@openerp.com', [user.email, ], subject, body)
|
||||
|
||||
return True
|
||||
|
||||
analytic_account()
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
@ -26,8 +26,6 @@ from openerp import tools
|
|||
from openerp.osv import fields, osv
|
||||
from openerp.tools.translate import _
|
||||
|
||||
from openerp import SUPERUSER_ID
|
||||
|
||||
class account_followup_stat_by_partner(osv.osv):
|
||||
_name = "account_followup.stat.by.partner"
|
||||
_description = "Follow-up Statistics by Partner"
|
||||
|
@ -315,13 +313,6 @@ class account_followup_print(osv.osv_memory):
|
|||
if stat_line_id not in partner_list:
|
||||
partner_list.append(stat_line_id)
|
||||
to_update[str(id)]= {'level': fups[followup_line_id][1], 'partner_id': stat_line_id}
|
||||
#Remove partners that are other companies in OpenERP
|
||||
comp_obj = self.pool.get("res.company")
|
||||
comp_ids = comp_obj.search(cr, SUPERUSER_ID, [], context=context)
|
||||
for comp in comp_obj.browse(cr, SUPERUSER_ID, comp_ids, context=context):
|
||||
company_partner_wiz_id = comp.partner_id.id * 10000 + company_id
|
||||
if company_partner_wiz_id in partner_list:
|
||||
partner_list.remove(company_partner_wiz_id)
|
||||
return {'partner_ids': partner_list, 'to_update': to_update}
|
||||
|
||||
account_followup_print()
|
||||
|
|
|
@ -196,7 +196,7 @@ class account_analytic_account(osv.osv):
|
|||
'date_start': fields.date('Start Date'),
|
||||
'date': fields.date('Date End', select=True),
|
||||
'company_id': fields.many2one('res.company', 'Company', required=False), #not required because we want to allow different companies to use the same chart of account, except for leaf accounts.
|
||||
'state': fields.selection([('template', 'Template'),('draft','New'),('open','In Progress'), ('cancelled', 'Cancelled'),('pending','To Renew'),('close','Closed')], 'Status', required=True, track_visibility='onchange'),
|
||||
'state': fields.selection([('template', 'Template'),('draft','New'),('open','In Progress'),('pending','To Renew'),('close','Closed'),('cancelled', 'Cancelled')], 'Status', required=True, track_visibility='onchange'),
|
||||
'currency_id': fields.function(_currency, fnct_inv=_set_company_currency, #the currency_id field is readonly except if it's a view account and if there is no company
|
||||
store = {
|
||||
'res.company': (_get_analytic_account, ['currency_id'], 10),
|
||||
|
@ -258,6 +258,9 @@ class account_analytic_account(osv.osv):
|
|||
(check_recursion, 'Error! You cannot create recursive analytic accounts.', ['parent_id']),
|
||||
]
|
||||
|
||||
def name_create(self, cr, uid, name, context=None):
|
||||
raise osv.except_osv(_('Warning'), _("Quick account creation disallowed."))
|
||||
|
||||
def copy(self, cr, uid, id, default=None, context=None):
|
||||
if not default:
|
||||
default = {}
|
||||
|
@ -349,6 +352,4 @@ class account_analytic_line(osv.osv):
|
|||
(_check_no_view, 'You cannot create analytic line on view account.', ['account_id']),
|
||||
]
|
||||
|
||||
account_analytic_line()
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -41,7 +41,7 @@ class res_users(osv.Model):
|
|||
state = simplejson.loads(params['state'])
|
||||
token = state.get('t')
|
||||
oauth_uid = validation['user_id']
|
||||
email = validation.get('email', 'provider_%d_user_%d' % (provider, oauth_uid))
|
||||
email = validation.get('email', 'provider_%s_user_%s' % (provider, oauth_uid))
|
||||
name = validation.get('name', email)
|
||||
values = {
|
||||
'name': name,
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
<!-- add Reset Password button -->
|
||||
<xpath expr="//div[@class='oe_right oe_button_box']" position="replace">
|
||||
<div class="oe_right oe_button_box">
|
||||
<button string="Reset Password" type="object" name="action_reset_password"
|
||||
help="Send an email to the user to (re)set their password."/>
|
||||
<button string="Send reset password instructions by email"
|
||||
type="object" name="action_reset_password" />
|
||||
</div>
|
||||
</xpath>
|
||||
</field>
|
||||
|
|
|
@ -587,27 +587,25 @@ class crm_lead(base_stage, format_address, osv.osv):
|
|||
return True
|
||||
|
||||
def _merge_opportunity_attachments(self, cr, uid, opportunity_id, opportunities, context=None):
|
||||
attachment = self.pool.get('ir.attachment')
|
||||
attach_obj = self.pool.get('ir.attachment')
|
||||
|
||||
# return attachments of opportunity
|
||||
def _get_attachments(opportunity_id):
|
||||
attachment_ids = attachment.search(cr, uid, [('res_model', '=', self._name), ('res_id', '=', opportunity_id)], context=context)
|
||||
return attachment.browse(cr, uid, attachment_ids, context=context)
|
||||
attachment_ids = attach_obj.search(cr, uid, [('res_model', '=', self._name), ('res_id', '=', opportunity_id)], context=context)
|
||||
return attach_obj.browse(cr, uid, attachment_ids, context=context)
|
||||
|
||||
count = 1
|
||||
first_attachments = _get_attachments(opportunity_id)
|
||||
#counter of all attachments to move. Used to make sure the name is different for all attachments
|
||||
count = 1
|
||||
for opportunity in opportunities:
|
||||
attachments = _get_attachments(opportunity.id)
|
||||
for first in first_attachments:
|
||||
for attachment in attachments:
|
||||
if attachment.name == first.name:
|
||||
values = dict(
|
||||
name = "%s (%s)" % (attachment.name, count,),
|
||||
res_id = opportunity_id,
|
||||
)
|
||||
attachment.write(values)
|
||||
count+=1
|
||||
|
||||
for attachment in attachments:
|
||||
values = {'res_id': opportunity_id,}
|
||||
for attachment_in_first in first_attachments:
|
||||
if attachment.name == attachment_in_first.name:
|
||||
name = "%s (%s)" % (attachment.name, count,),
|
||||
count+=1
|
||||
attachment.write(values)
|
||||
return True
|
||||
|
||||
def merge_opportunity(self, cr, uid, ids, context=None):
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
<openerp>
|
||||
<data>
|
||||
|
||||
|
||||
<!--
|
||||
CRM CASE STAGE
|
||||
-->
|
||||
|
@ -110,7 +109,7 @@
|
|||
string="Phone Calls"/>
|
||||
</div>
|
||||
<div class="oe_title">
|
||||
<label for="name" class="oe_edit_only" string="Lead Description"/>
|
||||
<label for="name" class="oe_edit_only"/>
|
||||
<h1><field name="name" placeholder="Describe the lead..."/></h1>
|
||||
</div>
|
||||
<group>
|
||||
|
@ -217,9 +216,8 @@
|
|||
<field name="arch" type="xml">
|
||||
<tree string="Leads" fonts="bold:message_unread==True" colors="grey:state in ('cancel', 'done')">
|
||||
<field name="date_deadline" invisible="1"/>
|
||||
<field name="create_date" groups="base.group_no_one"/>
|
||||
<field name="create_date"/>
|
||||
<field name="name"/>
|
||||
<field name="type"/>
|
||||
<field name="contact_name"/>
|
||||
<field name="country_id" invisible="context.get('invisible_country', True)"/>
|
||||
<field name="email_from"/>
|
||||
|
@ -390,8 +388,7 @@
|
|||
<button string="Meeting"
|
||||
name="action_makeMeeting"
|
||||
type="object"
|
||||
context="{'search_default_attendee_id': active_id, 'default_attendee_id' : active_id}"
|
||||
/>
|
||||
context="{'search_default_attendee_id': active_id, 'default_attendee_id' : active_id}"/>
|
||||
</div>
|
||||
<div class="oe_title">
|
||||
<label for="name" class="oe_edit_only"/>
|
||||
|
@ -512,7 +509,7 @@
|
|||
<field name="arch" type="xml">
|
||||
<tree string="Opportunities" fonts="bold:message_unread==True" colors="gray:state in ('cancel', 'done');red:date_deadline and (date_deadline < current_date)">
|
||||
<field name="date_deadline" invisible="1"/>
|
||||
<field name="create_date" groups="base.group_no_one"/>
|
||||
<field name="create_date"/>
|
||||
<field name="name" string="Opportunity"/>
|
||||
<field name="partner_id" string="Customer"/>
|
||||
<field name="country_id" invisible="context.get('invisible_country', True)"/>
|
||||
|
@ -527,8 +524,8 @@
|
|||
<field name="user_id"/>
|
||||
<field name="referred" invisible="1"/>
|
||||
<field name="priority" invisible="1"/>
|
||||
<field name="state" groups="base.group_no_one"/>
|
||||
<field name="message_unread" invisible="1"/>
|
||||
<field name="state" invisible="1"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
|
|
@ -45,8 +45,8 @@ class test_message_compose(TestMailBase):
|
|||
# Mail data
|
||||
_subject1 = 'Pigs'
|
||||
_subject2 = 'Bird'
|
||||
_body_html1 = '<div><p>Fans of Pigs, unite !\n</p><p>Admin</p></div>'
|
||||
_body_html2 = '<div><p>I am angry !\n</p><p>Admin</p></div>'
|
||||
_body_html1 = 'Fans of Pigs, unite !'
|
||||
_body_html2 = 'I am angry !'
|
||||
_attachments = [
|
||||
{'name': 'First', 'datas_fname': 'first.txt', 'datas': base64.b64encode('My first attachment')},
|
||||
{'name': 'Second', 'datas_fname': 'second.txt', 'datas': base64.b64encode('My second attachment')}
|
||||
|
@ -113,7 +113,7 @@ class test_message_compose(TestMailBase):
|
|||
partner_ids = self.res_partner.search(cr, uid, [('email', 'in', ['b@b.b', 'c@c.c', 'd@d.d'])])
|
||||
# Test: mail.compose.message: subject, body, partner_ids
|
||||
self.assertEqual(compose.subject, _subject1, 'mail.compose.message subject incorrect')
|
||||
self.assertEqual(compose.body, _body_html1, 'mail.compose.message body incorrect')
|
||||
self.assertIn(_body_html1, compose.body, 'mail.compose.message body incorrect')
|
||||
self.assertEqual(set(message_pids), set(partner_ids), 'mail.compose.message partner_ids incorrect')
|
||||
# Test: mail.compose.message: attachments
|
||||
# Test: mail.message: attachments
|
||||
|
@ -159,8 +159,8 @@ class test_message_compose(TestMailBase):
|
|||
# Test: subject, body
|
||||
self.assertEqual(message_pigs.subject, _subject1, 'mail.message subject on Pigs incorrect')
|
||||
self.assertEqual(message_bird.subject, _subject2, 'mail.message subject on Bird incorrect')
|
||||
self.assertEqual(message_pigs.body, _body_html1, 'mail.message body on Pigs incorrect')
|
||||
self.assertEqual(message_bird.body, _body_html2, 'mail.message body on Bird incorrect')
|
||||
self.assertIn(_body_html1, message_pigs.body, 'mail.message body on Pigs incorrect')
|
||||
self.assertIn(_body_html2, message_bird.body, 'mail.message body on Bird incorrect')
|
||||
# Test: partner_ids: p_a_id (default) + 3 newly created partners
|
||||
message_pigs_pids = [partner.id for partner in message_pigs.notified_partner_ids]
|
||||
message_bird_pids = [partner.id for partner in message_bird.notified_partner_ids]
|
||||
|
|
|
@ -150,8 +150,8 @@ class mail_compose_message(osv.TransientModel):
|
|||
values = {}
|
||||
# get values to return
|
||||
email_dict = super(mail_compose_message, self).render_message(cr, uid, wizard, res_id, context)
|
||||
email_dict.update(values)
|
||||
return email_dict
|
||||
values.update(email_dict)
|
||||
return values
|
||||
|
||||
def render_template(self, cr, uid, template, model, res_id, context=None):
|
||||
return self.pool.get('email.template').render_template(cr, uid, template, model, res_id, context=context)
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
</group>
|
||||
<group string="Actions to Perform on Incoming Mails">
|
||||
<field name="object_id" on_change="onchange_server_type(type, is_ssl, object_id)"/>
|
||||
<field name="action_id"/>
|
||||
<field name="action_id" groups="base.group_no_one"/>
|
||||
</group>
|
||||
<group attrs="{'invisible' : [('type', '!=', 'local')]}" string="Configuration">
|
||||
<field name="configuration" colspan="4"/>
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
</record>
|
||||
|
||||
<record id="hr.employee" model="hr.employee">
|
||||
<field name="product_id" ref="product.product_product_consultant"/>
|
||||
<field name="journal_id" ref="analytic_journal"/>
|
||||
</record>
|
||||
|
||||
|
|
|
@ -372,12 +372,25 @@ class lunch_order_line(osv.Model):
|
|||
cash_ids = [cash.id for cash in order_line.cashmove]
|
||||
cashmove_ref.unlink(cr, uid, cash_ids, context=context)
|
||||
return self._update_order_lines(cr, uid, ids, context=context)
|
||||
|
||||
def _get_line_order_ids(self, cr, uid, ids, context=None):
|
||||
"""
|
||||
return the list of lunch.order.lines ids to which belong the lunch.order 'ids'
|
||||
"""
|
||||
result = set()
|
||||
for lunch_order in self.browse(cr, uid, ids, context=context):
|
||||
for lines in lunch_order.order_line_ids:
|
||||
result.add(lines.id)
|
||||
return list(result)
|
||||
|
||||
_columns = {
|
||||
'name': fields.related('product_id', 'name', readonly=True),
|
||||
'order_id': fields.many2one('lunch.order', 'Order', ondelete='cascade'),
|
||||
'product_id': fields.many2one('lunch.product', 'Product', required=True),
|
||||
'date': fields.related('order_id', 'date', type='date', string="Date", readonly=True, store=True),
|
||||
'date': fields.related('order_id', 'date', type='date', string="Date", readonly=True, store={
|
||||
'lunch.order': (_get_line_order_ids, ['date'], 10),
|
||||
'lunch.order.line': (lambda self, cr, uid, ids, ctx: ids, [], 10),
|
||||
}),
|
||||
'supplier': fields.related('product_id', 'supplier', type='many2one', relation='res.partner', string="Supplier", readonly=True, store=True),
|
||||
'user_id': fields.related('order_id', 'user_id', type='many2one', relation='res.users', string='User', readonly=True, store=True),
|
||||
'note': fields.text('Note'),
|
||||
|
|
|
@ -157,7 +157,7 @@ class mail_mail(osv.Model):
|
|||
:param browse_record partner: specific recipient partner
|
||||
"""
|
||||
if force or (not mail.subject and mail.model and mail.res_id):
|
||||
return '%s posted on %s' % (mail.author_id.name, mail.record_name)
|
||||
return 'Re: %s' % (mail.record_name)
|
||||
return mail.subject
|
||||
|
||||
def send_get_mail_body(self, cr, uid, mail, partner=None, context=None):
|
||||
|
|
|
@ -592,7 +592,7 @@ class mail_thread(osv.AbstractModel):
|
|||
|
||||
thread_id = False
|
||||
for model, thread_id, custom_values, user_id in routes:
|
||||
if self._name != model:
|
||||
if self._name == 'mail.thread':
|
||||
context.update({'thread_model': model})
|
||||
if model:
|
||||
model_pool = self.pool.get(model)
|
||||
|
@ -772,7 +772,7 @@ class mail_thread(osv.AbstractModel):
|
|||
if author_ids:
|
||||
msg_dict['author_id'] = author_ids[0]
|
||||
else:
|
||||
msg_dict['email_from'] = message.get('from')
|
||||
msg_dict['email_from'] = decode(message.get('from'))
|
||||
partner_ids = self._message_find_partners(cr, uid, message, ['To', 'Cc'], context=context)
|
||||
msg_dict['partner_ids'] = [(4, partner_id) for partner_id in partner_ids]
|
||||
|
||||
|
@ -883,7 +883,12 @@ class mail_thread(osv.AbstractModel):
|
|||
if isinstance(thread_id, (list, tuple)):
|
||||
thread_id = thread_id and thread_id[0]
|
||||
mail_message = self.pool.get('mail.message')
|
||||
model = context.get('thread_model', self._name) if thread_id else False
|
||||
|
||||
# if we're processing a message directly coming from the gateway, the destination model was
|
||||
# set in the context.
|
||||
model = False
|
||||
if thread_id:
|
||||
model = context.get('thread_model', self._name) if self._name == 'mail.thread' else self._name
|
||||
|
||||
attachment_ids = kwargs.pop('attachment_ids', [])
|
||||
for name, content in attachments:
|
||||
|
@ -961,13 +966,12 @@ class mail_thread(osv.AbstractModel):
|
|||
mail_message_obj = self.pool.get('mail.message')
|
||||
ir_attachment = self.pool.get('ir.attachment')
|
||||
|
||||
# 1.A.1: add recipients of parent message
|
||||
# 1.A.1: add recipients of parent message (# TDE FIXME HACK: mail.thread -> private message)
|
||||
partner_ids = set([])
|
||||
if parent_id:
|
||||
if parent_id and self._name == 'mail.thread':
|
||||
parent_message = mail_message_obj.browse(cr, uid, parent_id, context=context)
|
||||
partner_ids |= set([(4, partner.id) for partner in parent_message.partner_ids])
|
||||
# TDE FIXME HACK: mail.thread -> private message
|
||||
if self._name == 'mail.thread' and parent_message.author_id.id:
|
||||
if parent_message.author_id.id:
|
||||
partner_ids.add((4, parent_message.author_id.id))
|
||||
|
||||
# 1.A.2: add specified recipients
|
||||
|
|
|
@ -206,21 +206,23 @@ openerp_mail_followers = function(session, mail) {
|
|||
|
||||
/** Fetch subtypes, only if current user is follower */
|
||||
fetch_subtypes: function () {
|
||||
var self = this;
|
||||
var subtype_list_ul = this.$('.oe_subtype_list').empty();
|
||||
if (! this.message_is_follower) return;
|
||||
var context = new session.web.CompoundContext(this.build_context(), {});
|
||||
this.ds_model.call('message_get_subscription_data', [[this.view.datarecord.id], context]).then(this.proxy('display_subtypes'));
|
||||
var id = this.view.datarecord.id;
|
||||
this.ds_model.call('message_get_subscription_data', [[id], new session.web.CompoundContext(this.build_context(), {})])
|
||||
.then(function (data) {self.display_subtypes(data, id);});
|
||||
},
|
||||
|
||||
/** Display subtypes: {'name': default, followed} */
|
||||
display_subtypes:function (data) {
|
||||
display_subtypes:function (data, id) {
|
||||
var self = this;
|
||||
var subtype_list_ul = this.$('.oe_subtype_list');
|
||||
var records = [];
|
||||
var nb_subtype = 0;
|
||||
subtype_list_ul.empty();
|
||||
if (this.view.datarecord.id) {
|
||||
records = data[this.view.datarecord.id].message_subtype_data;
|
||||
if (data[id]) {
|
||||
records = data[id].message_subtype_data;
|
||||
}
|
||||
_(records).each(function (record) {nb_subtype++;});
|
||||
if (nb_subtype > 1) {
|
||||
|
|
|
@ -339,7 +339,7 @@ class test_mail(TestMailBase):
|
|||
|
||||
# Mail data
|
||||
_subject = 'Pigs'
|
||||
_mail_subject = '%s posted on %s' % (user_raoul.name, group_pigs.name)
|
||||
_mail_subject = 'Re: %s' % (group_pigs.name)
|
||||
_body1 = '<p>Pigs rules</p>'
|
||||
_mail_body1 = '<p>Pigs rules</p>\n<div><p>Raoul</p></div>\n'
|
||||
_mail_bodyalt1 = 'Pigs rules\nRaoul\n'
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
id="base.marketing_menu"
|
||||
groups="marketing.group_marketing_user,marketing.group_marketing_manager"
|
||||
sequence="85"/>
|
||||
|
||||
<record id="view_crm_lead_form" model="ir.ui.view">
|
||||
<field name="name">crm.lead.inherit.form</field>
|
||||
<field name="model">crm.lead</field>
|
||||
|
@ -14,6 +15,7 @@
|
|||
<field name="arch" type="xml">
|
||||
<xpath expr="//group[@name='categorization']" position="attributes">
|
||||
<attribute name="string">Marketing</attribute>
|
||||
<attribute name="groups"></attribute>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='company_id']" position="after">
|
||||
<field name="type_id"/>
|
||||
|
@ -21,6 +23,7 @@
|
|||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_crm_opportunity_form" model="ir.ui.view">
|
||||
<field name="name">crm.lead.inherit.form</field>
|
||||
<field name="model">crm.lead</field>
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
<field name="arch" type="xml">
|
||||
<form string="Issue" version="7.0">
|
||||
<header>
|
||||
<button name="case_close" string="Done" type="object"
|
||||
<button name="case_close" string="Done" type="object"
|
||||
states="open" groups="base.group_user"/>
|
||||
<button name="case_close" string="Done" type="object"
|
||||
states="draft,pending" groups="base.group_user"/>
|
||||
|
@ -129,11 +129,11 @@
|
|||
<tree string="Issue Tracker Tree" fonts="bold:message_unread==True" colors="black:state=='open';blue:state=='pending';grey:state in ('cancel', 'done')">
|
||||
<field name="message_unread" invisible="1"/>
|
||||
<field name="id"/>
|
||||
<field name="create_date" groups="base.group_no_one"/>
|
||||
<field name="name"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="project_id"/>
|
||||
<field name="priority" string="Priority" groups="base.group_user"/>
|
||||
<field name="create_date"/>
|
||||
<field name="version_id" widget="selection"/>
|
||||
<field name="user_id"/>
|
||||
<field name="progress" widget="progressbar" attrs="{'invisible':[('task_id','=',False)]}"/>
|
||||
|
@ -157,7 +157,7 @@
|
|||
<filter string="New" icon="terp-document-new" domain="[('state','=','draft')]" help="New Issues"/>
|
||||
<filter string="To Do" domain="[('state','=','open')]" help="To Do Issues" icon="terp-check"/>
|
||||
<separator/>
|
||||
<filter string="Unassigned Issues" domain="[('user_id','=',False)]" help="Unassigned Issues" icon="terp-personal-" />
|
||||
<filter string="Unassigned Issues" domain="[('user_id','=',False)]" help="Unassigned Issues" icon="terp-personal-"/>
|
||||
<field name="user_id"/>
|
||||
<field name="project_id"/>
|
||||
<field name="categ_ids"/>
|
||||
|
|
|
@ -220,7 +220,7 @@ class WebKitParser(report_sxw):
|
|||
|
||||
if report_xml.report_file :
|
||||
path = addons.get_module_resource(*report_xml.report_file.split(os.path.sep))
|
||||
if os.path.exists(path) :
|
||||
if path and os.path.exists(path) :
|
||||
template = file(path).read()
|
||||
if not template and report_xml.report_webkit_data :
|
||||
template = report_xml.report_webkit_data
|
||||
|
|
|
@ -51,9 +51,9 @@ class sale_make_invoice(osv.osv_memory):
|
|||
if context is None:
|
||||
context = {}
|
||||
data = self.read(cr, uid, ids)[0]
|
||||
order_obj.action_invoice_create(cr, uid, context.get(('active_ids'), []), data['grouped'], date_inv = data['invoice_date'])
|
||||
order_obj.action_invoice_create(cr, uid, context.get(('active_ids'), []), data['grouped'], date_invoice = data['invoice_date'])
|
||||
order_obj.signal_manual_invoice(cr, uid, context.get(('active_ids'), []))
|
||||
|
||||
|
||||
for o in order_obj.browse(cr, uid, context.get(('active_ids'), []), context=context):
|
||||
for i in o.invoice_ids:
|
||||
newinv.append(i.id)
|
||||
|
|
|
@ -29,11 +29,14 @@ import openerp.addons.decimal_precision as dp
|
|||
class stock_return_picking_memory(osv.osv_memory):
|
||||
_name = "stock.return.picking.memory"
|
||||
_rec_name = 'product_id'
|
||||
|
||||
_columns = {
|
||||
'product_id' : fields.many2one('product.product', string="Product", required=True),
|
||||
'quantity' : fields.float("Quantity", digits_compute=dp.get_precision('Product Unit of Measure'), required=True),
|
||||
'wizard_id' : fields.many2one('stock.return.picking', string="Wizard"),
|
||||
'move_id' : fields.many2one('stock.move', "Move"),
|
||||
'prodlot_id': fields.related('move_id', 'prodlot_id', type='many2one', relation='stock.production.lot', string='Serial Number', readonly=True),
|
||||
|
||||
}
|
||||
|
||||
stock_return_picking_memory()
|
||||
|
@ -74,7 +77,7 @@ class stock_return_picking(osv.osv_memory):
|
|||
for line in pick.move_lines:
|
||||
qty = line.product_qty - return_history.get(line.id, 0)
|
||||
if qty > 0:
|
||||
result1.append({'product_id': line.product_id.id, 'quantity': qty,'move_id':line.id})
|
||||
result1.append({'product_id': line.product_id.id, 'quantity': qty,'move_id':line.id, 'prodlot_id': line.prodlot_id and line.prodlot_id.id or False})
|
||||
if 'product_return_moves' in fields:
|
||||
res.update({'product_return_moves': result1})
|
||||
return res
|
||||
|
|
|
@ -32,8 +32,8 @@
|
|||
<field name="arch" type="xml">
|
||||
<tree editable="bottom" string="Product Moves">
|
||||
<field name="product_id" />
|
||||
<field name="prodlot_id" groups="stock.group_production_lot"/>
|
||||
<field name="quantity" />
|
||||
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
@ -45,6 +45,7 @@
|
|||
<form string="Return Picking Memory" version="7.0">
|
||||
<group col="4">
|
||||
<field name="product_id" />
|
||||
<field name="prodlot_id" groups="stock.group_production_lot"/>
|
||||
<field name="quantity" />
|
||||
</group>
|
||||
</form>
|
||||
|
|
Loading…
Reference in New Issue