[MERGE] from trunk
bzr revid: chm@openerp.com-20130320125517-jv2ot85iypncoesj
This commit is contained in:
commit
ae30f8fff3
|
@ -1150,6 +1150,29 @@ class account_move(osv.osv):
|
|||
_description = "Account Entry"
|
||||
_order = 'id desc'
|
||||
|
||||
def account_move_prepare(self, cr, uid, journal_id, date=False, ref='', company_id=False, context=None):
|
||||
'''
|
||||
Prepares and returns a dictionary of values, ready to be passed to create() based on the parameters received.
|
||||
'''
|
||||
if not date:
|
||||
date = fields.date.today()
|
||||
period_obj = self.pool.get('account.period')
|
||||
if not company_id:
|
||||
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
|
||||
company_id = user.company_id.id
|
||||
if context is None:
|
||||
context = {}
|
||||
#put the company in context to find the good period
|
||||
ctx = context.copy()
|
||||
ctx.update({'company_id': company_id})
|
||||
return {
|
||||
'journal_id': journal_id,
|
||||
'date': date,
|
||||
'period_id': period_obj.find(cr, uid, date, context=ctx)[0],
|
||||
'ref': ref,
|
||||
'company_id': company_id,
|
||||
}
|
||||
|
||||
def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80):
|
||||
"""
|
||||
Returns a list of tupples containing id, name, as internally it is called {def name_get}
|
||||
|
@ -1850,6 +1873,13 @@ class account_tax(osv.osv):
|
|||
return result in the context
|
||||
Ex: result=round(price_unit*0.21,4)
|
||||
"""
|
||||
def copy_data(self, cr, uid, id, default=None, context=None):
|
||||
if default is None:
|
||||
default = {}
|
||||
name = self.read(cr, uid, id, ['name'], context=context)['name']
|
||||
default = default.copy()
|
||||
default.update({'name': name + _(' (Copy)')})
|
||||
return super(account_tax, self).copy_data(cr, uid, id, default=default, context=context)
|
||||
|
||||
def get_precision_tax():
|
||||
def change_digit_tax(cr):
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
import time
|
||||
from lxml import etree
|
||||
import openerp.addons.decimal_precision as dp
|
||||
import openerp.exceptions
|
||||
|
||||
from openerp import pooler
|
||||
from openerp.osv import fields, osv, orm
|
||||
|
@ -302,16 +303,7 @@ class account_invoice(osv.osv):
|
|||
('number_uniq', 'unique(number, company_id, journal_id, type)', 'Invoice Number must be unique per Company!'),
|
||||
]
|
||||
|
||||
def _find_partner(self, inv):
|
||||
'''
|
||||
Find the partner for which the accounting entries will be created
|
||||
'''
|
||||
#if the chosen partner is not a company and has a parent company, use the parent for the journal entries
|
||||
#because you want to invoice 'Agrolait, accounting department' but the journal items are for 'Agrolait'
|
||||
part = inv.partner_id
|
||||
if part.parent_id and not part.is_company:
|
||||
part = part.parent_id
|
||||
return part
|
||||
|
||||
|
||||
|
||||
def fields_view_get(self, cr, uid, view_id=None, view_type=False, context=None, toolbar=False, submenu=False):
|
||||
|
@ -981,7 +973,7 @@ class account_invoice(osv.osv):
|
|||
|
||||
date = inv.date_invoice or time.strftime('%Y-%m-%d')
|
||||
|
||||
part = self._find_partner(inv)
|
||||
part = self.pool.get("res.partner")._find_accounting_partner(inv.partner_id)
|
||||
|
||||
line = map(lambda x:(0,0,self.line_get_convert(cr, uid, x, part.id, date, context=ctx)),iml)
|
||||
|
||||
|
@ -1753,6 +1745,16 @@ class res_partner(osv.osv):
|
|||
'invoice_ids': fields.one2many('account.invoice.line', 'partner_id', 'Invoices', readonly=True),
|
||||
}
|
||||
|
||||
def _find_accounting_partner(self, part):
|
||||
'''
|
||||
Find the partner for which the accounting entries will be created
|
||||
'''
|
||||
#if the chosen partner is not a company and has a parent company, use the parent for the journal entries
|
||||
#because you want to invoice 'Agrolait, accounting department' but the journal items are for 'Agrolait'
|
||||
if part.parent_id and not part.is_company:
|
||||
part = part.parent_id
|
||||
return part
|
||||
|
||||
def copy(self, cr, uid, id, default=None, context=None):
|
||||
default = default or {}
|
||||
default.update({'invoice_ids' : []})
|
||||
|
|
|
@ -145,8 +145,7 @@
|
|||
<header>
|
||||
<button name="invoice_open" states="draft,proforma2" string="Validate" class="oe_highlight" groups="account.group_account_invoice"/>
|
||||
<button name="%(action_account_invoice_refund)d" type='action' string='Ask Refund' states='open,paid' groups="account.group_account_invoice"/>
|
||||
<button name="invoice_cancel" states="draft,proforma2" string="Cancel" groups="account.group_account_invoice"/>
|
||||
<button name="invoice_cancel" states="sale,open" string="Cancel" groups="base.group_no_one"/>
|
||||
<button name="invoice_cancel" states="draft,proforma2" string="Cancel Invoice" groups="account.group_account_invoice"/>
|
||||
<button name="action_cancel_draft" states="cancel" string="Set to Draft" type="object" groups="account.group_account_invoice"/>
|
||||
<button name='%(action_account_state_open)d' type='action' string='Re-Open' groups="account.group_account_invoice" attrs="{'invisible':['|', ('state','<>','paid'), ('reconciled', '=', True)]}" help="This button only appears when the state of the invoice is 'paid' (showing that it has been fully reconciled) and auto-computed boolean 'reconciled' is False (depicting that it's not the case anymore). In other words, the invoice has been dereconciled and it does not fit anymore the 'paid' state. You should press this button to re-open it and let it continue its normal process after having resolved the eventual exceptions it may have created."/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,open,paid" statusbar_colors='{"proforma":"blue","proforma2":"blue"}'/>
|
||||
|
@ -300,7 +299,7 @@
|
|||
<button name="invoice_open" states="proforma2" string="Validate" groups="base.group_user"/>
|
||||
<button name="invoice_proforma2" states="draft" string="PRO-FORMA" groups="account.group_proforma_invoices"/>
|
||||
<button name="%(action_account_invoice_refund)d" type='action' string='Refund Invoice' states='open,proforma2,paid' groups="base.group_user"/>
|
||||
<button name="invoice_cancel" states="draft,proforma2,open" string="Cancel" groups="base.group_no_one"/>
|
||||
<button name="invoice_cancel" states="draft,proforma2,open" string="Cancel Invoice" groups="base.group_no_one"/>
|
||||
<button name="action_cancel_draft" states="cancel" string="Reset to Draft" type="object" groups="base.group_user"/>
|
||||
<button name='%(action_account_state_open)d' type='action' string='Re-Open' groups="account.group_account_invoice" attrs="{'invisible':['|', ('state','<>','paid'), ('reconciled', '=', True)]}" help="This button only appears when the state of the invoice is 'paid' (showing that it has been fully reconciled) and auto-computed boolean 'reconciled' is False (depicting that it's not the case anymore). In other words, the invoice has been dereconciled and it does not fit anymore the 'paid' state. You should press this button to re-open it and let it continue its normal process after having resolved the eventual exceptions it may have created."/>
|
||||
<!--button name="%(account_invoices)d" string="Print Invoice" type="action" states="open,paid,proforma,sale,proforma2"/-->
|
||||
|
@ -437,7 +436,7 @@
|
|||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids" widget="mail_followers" groups="base.group_user"/>
|
||||
<field name="message_ids" widget="mail_thread" placeholder="Share a note..."/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
|
@ -448,11 +447,11 @@
|
|||
<field name="model">account.invoice</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Invoice">
|
||||
<field name="number" string="Invoice" filter_domain="['|','|', ('number','ilike',self), ('origin','ilike',self), ('supplier_invoice_number', 'ilike', self)]"/>
|
||||
<filter name="draft" icon="terp-document-new" string="Draft" domain="[('state','=','draft')]" help="Draft Invoices"/>
|
||||
<filter name="proforma" icon="terp-gtk-media-pause" string="Proforma" domain="[('state','=','proforma2')]" help="Proforma Invoices" groups="account.group_proforma_invoices"/>
|
||||
<filter name="invoices" icon="terp-dolar" string="Invoices" domain="[('state','not in',['draft','cancel'])]" help="Proforma/Open/Paid Invoices"/>
|
||||
<filter name="unpaid" icon="terp-dolar_ok!" string="Unpaid" domain="[('state','=','open')]" help="Unpaid Invoices"/>
|
||||
<field name="number" string="Invoice" filter_domain="['|','|','|', ('number','ilike',self), ('origin','ilike',self), ('supplier_invoice_number', 'ilike', self), ('partner_id', 'ilike', self)]"/>
|
||||
<filter name="draft" string="Draft" domain="[('state','=','draft')]" help="Draft Invoices"/>
|
||||
<filter name="proforma" string="Proforma" domain="[('state','=','proforma2')]" help="Proforma Invoices" groups="account.group_proforma_invoices"/>
|
||||
<filter name="invoices" string="Invoices" domain="[('state','not in',['draft','cancel'])]" help="Proforma/Open/Paid Invoices"/>
|
||||
<filter name="unpaid" string="Unpaid" domain="[('state','=','open')]" help="Unpaid Invoices"/>
|
||||
<separator/>
|
||||
<filter domain="[('user_id','=',uid)]" help="My Invoices" icon="terp-personal"/>
|
||||
<field name="partner_id"/>
|
||||
|
|
|
@ -780,7 +780,7 @@ class account_move_line(osv.osv):
|
|||
else:
|
||||
currency_id = line.company_id.currency_id
|
||||
if line.reconcile_id:
|
||||
raise osv.except_osv(_('Warning!'), _('Already reconciled.'))
|
||||
raise osv.except_osv(_('Warning'), _("Journal Item '%s' (id: %s), Move '%s' is already reconciled!") % (line.name, line.id, line.move_id.name))
|
||||
if line.reconcile_partial_id:
|
||||
for line2 in line.reconcile_partial_id.line_partial_ids:
|
||||
if not line2.reconcile_id:
|
||||
|
|
|
@ -161,7 +161,7 @@
|
|||
</p>
|
||||
</field>
|
||||
</record>
|
||||
<menuitem id="menu_action_account_period" action="action_account_period" parent="account.next_id_23" groups="base.group_no_one"/>
|
||||
<menuitem id="menu_action_account_period" action="action_account_period" parent="account.next_id_23"/>
|
||||
|
||||
<!-- Accounts -->
|
||||
<record id="view_account_form" model="ir.ui.view">
|
||||
|
@ -435,15 +435,15 @@
|
|||
<group string="Accounts">
|
||||
<field name="profit_account_id" domain="[('type','!=','view')]"/>
|
||||
<field name="loss_account_id" domain="[('type','!=','view')]"/>
|
||||
<field name="internal_account_id"/>
|
||||
<field name="internal_account_id" domain="[('type','!=','view')]"/>
|
||||
</group>
|
||||
<group string="Miscellaneous">
|
||||
<field name="with_last_closing_balance"/>
|
||||
<field name="cash_control"/>
|
||||
<field name="cash_control" attrs="{'invisible':[('type','not in', ('cash',))]}"/>
|
||||
</group>
|
||||
</group>
|
||||
<separator string="Available Coins" colspan="4" attrs="{'invisible' : [('cash_control', '=', False)] }"/>
|
||||
<field name="cashbox_line_ids" nolabel="1" string="Unit Of Currency Definition" colspan="4" attrs="{'invisible' : [('cash_control', '=', False)]}">
|
||||
<separator string="Available Coins" colspan="4" attrs="{'invisible' : ['|',('cash_control', '=', False),('type','not in', ('cash',))] }"/>
|
||||
<field name="cashbox_line_ids" nolabel="1" string="Unit Of Currency Definition" colspan="4" attrs="{'invisible' : ['|',('cash_control', '=', False),('type','not in', ('cash',))]}">
|
||||
<tree string="CashBox Lines" editable="bottom">
|
||||
<field name="pieces" />
|
||||
</tree>
|
||||
|
@ -552,7 +552,7 @@
|
|||
<header>
|
||||
<button name="button_confirm_bank" states="draft" string="Confirm" type="object" class="oe_highlight"/>
|
||||
<button name="button_dummy" states="draft" string="Compute" type="object" class="oe_highlight"/>
|
||||
<button name="button_cancel" states="confirm" string="Cancel" type="object"/>
|
||||
<button name="button_cancel" states="confirm" string="Cancel Statement" type="object"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,confirm"/>
|
||||
</header>
|
||||
<sheet>
|
||||
|
@ -1206,6 +1206,8 @@
|
|||
<field name="name"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="account_id"/>
|
||||
<field name="period_id" invisible="1"/>
|
||||
<field name="journal_id" invisible="1"/>
|
||||
<field name="reconcile_partial_id"/>
|
||||
<field name="state" invisible="1"/>
|
||||
<field name="debit" sum="Total debit"/>
|
||||
|
@ -1256,7 +1258,7 @@
|
|||
<form string="Account Entry" version="7.0">
|
||||
<header>
|
||||
<button name="button_validate" states="draft" string="Post" type="object" class="oe_highlight" groups="account.group_account_invoice"/>
|
||||
<button name="button_cancel" states="posted" string="Cancel" type="object" groups="account.group_account_invoice"/>
|
||||
<button name="button_cancel" states="posted" string="Cancel Entry" type="object" groups="account.group_account_invoice"/>
|
||||
<field name="state" widget="statusbar"/>
|
||||
</header>
|
||||
<sheet string="Journal Entries" >
|
||||
|
@ -2258,7 +2260,7 @@
|
|||
<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"/>
|
||||
<button name="button_cancel" states="confirm,open" string="Cancel CashBox" type="object"/>
|
||||
<field name="state" widget="statusbar" nolabel="1" statusbar_visible="draft,confirm"/>
|
||||
</header>
|
||||
<sheet string="Statement">
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
</record>
|
||||
|
||||
<record id="account_payment_term_line_15days" model="account.payment.term.line">
|
||||
<field name="name">15 Days</field>
|
||||
<field name="value">balance</field>
|
||||
<field eval="15" name="days"/>
|
||||
<field eval="0" name="days2"/>
|
||||
|
@ -48,7 +47,6 @@
|
|||
</record>
|
||||
|
||||
<record id="account_payment_term_line_net" model="account.payment.term.line">
|
||||
<field name="name">30 Net Days</field>
|
||||
<field name="value">balance</field>
|
||||
<field eval="30" name="days"/>
|
||||
<field eval="0" name="days2"/>
|
||||
|
|
|
@ -135,7 +135,6 @@
|
|||
<field name="note">30 Days End of Month</field>
|
||||
</record>
|
||||
<record id="account_payment_term_line" model="account.payment.term.line">
|
||||
<field name="name">30 Days End of Month</field>
|
||||
<field name="value">balance</field>
|
||||
<field eval="30" name="days"/>
|
||||
<field eval="-1" name="days2"/>
|
||||
|
@ -147,16 +146,13 @@
|
|||
<field name="note">30% Advance End 30 Days</field>
|
||||
</record>
|
||||
<record id="account_payment_term_line_advance1" model="account.payment.term.line">
|
||||
<field name="name">30% Advance</field>
|
||||
<field name="value">procent</field>
|
||||
<field eval="3" name="sequence"/>
|
||||
<field eval="0.300000" name="value_amount"/>
|
||||
<field eval="0" name="days"/>
|
||||
<field eval="0" name="days2"/>
|
||||
<field eval="account_payment_term_advance" name="payment_id"/>
|
||||
</record>
|
||||
<record id="account_payment_term_line_advance2" model="account.payment.term.line">
|
||||
<field name="name">Remaining Balance</field>
|
||||
<field name="value">balance</field>
|
||||
<field eval="30" name="days"/>
|
||||
<field eval="-1" name="days2"/>
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
<field name="name">Invoice - Send by Email</field>
|
||||
<field name="email_from">${object.user_id.email or object.company_id.email or 'noreply@localhost'}</field>
|
||||
<field name="subject">${object.company_id.name} Invoice (Ref ${object.number or 'n/a'})</field>
|
||||
<field name="email_recipients">${object.partner_id.id}</field>
|
||||
<field name="partner_to">${object.partner_id.id}</field>
|
||||
<field name="model_id" ref="account.model_account_invoice"/>
|
||||
<field name="auto_delete" eval="True"/>
|
||||
<field name="report_template" ref="account_invoices"/>
|
||||
|
|
|
@ -95,6 +95,7 @@
|
|||
</group>
|
||||
<field name="bank_ids" context="{'default_partner_id': active_id, 'form_view_ref': 'base.view_partner_bank_form'}">
|
||||
<tree string="Bank Details">
|
||||
<field name="state" invisible="1"/>
|
||||
<field name="sequence" invisible="1"/>
|
||||
<field name="acc_number"/>
|
||||
<field name="bank_name"/>
|
||||
|
|
|
@ -131,6 +131,7 @@
|
|||
<field name="name"/>
|
||||
<field name="account_id"/>
|
||||
<field name="journal_id"/>
|
||||
<field name="user_id"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="date"/>
|
||||
|
@ -169,6 +170,7 @@
|
|||
<field name="date"/>
|
||||
<field name="ref" invisible="context.get('to_invoice', False)"/>
|
||||
<field name="name"/>
|
||||
<field name="user_id"/>
|
||||
<field name="journal_id" invisible="context.get('to_invoice', False)"/>
|
||||
<field name="amount" sum="Total" invisible="context.get('to_invoice', False)"/>
|
||||
<field name="product_id" on_change="on_change_unit_amount(product_id, unit_amount, company_id, product_uom_id, journal_id)" invisible="not context.get('to_invoice', False)"/>
|
||||
|
@ -176,7 +178,6 @@
|
|||
<field name="product_uom_id" on_change="on_change_unit_amount(product_id, unit_amount, company_id, product_uom_id)" invisible="not context.get('to_invoice', False)"/>
|
||||
<field domain="[('type','=','normal')]" name="account_id"/>
|
||||
<field name="general_account_id" invisible="context.get('to_invoice', False)"/>
|
||||
<field name="user_id" invisible="1" />
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
</tree>
|
||||
</field>
|
||||
|
|
|
@ -65,7 +65,7 @@ class report_account_common(report_sxw.rml_parse, common_report_header):
|
|||
vals['debit'] = report.debit
|
||||
vals['credit'] = report.credit
|
||||
if data['form']['enable_filter']:
|
||||
vals['balance_cmp'] = self.pool.get('account.financial.report').browse(self.cr, self.uid, report.id, context=data['form']['comparison_context']).balance
|
||||
vals['balance_cmp'] = self.pool.get('account.financial.report').browse(self.cr, self.uid, report.id, context=data['form']['comparison_context']).balance * report.sign or 0.0
|
||||
lines.append(vals)
|
||||
account_ids = []
|
||||
if report.display_detail == 'no_detail':
|
||||
|
@ -97,7 +97,7 @@ class report_account_common(report_sxw.rml_parse, common_report_header):
|
|||
if not currency_obj.is_zero(self.cr, self.uid, account.company_id.currency_id, vals['balance']):
|
||||
flag = True
|
||||
if data['form']['enable_filter']:
|
||||
vals['balance_cmp'] = account_obj.browse(self.cr, self.uid, account.id, context=data['form']['comparison_context']).balance
|
||||
vals['balance_cmp'] = account_obj.browse(self.cr, self.uid, account.id, context=data['form']['comparison_context']).balance * report.sign or 0.0
|
||||
if not currency_obj.is_zero(self.cr, self.uid, account.company_id.currency_id, vals['balance_cmp']):
|
||||
flag = True
|
||||
if flag:
|
||||
|
|
|
@ -55,6 +55,19 @@ Hello ${object.name},
|
|||
${account_table(ctx["data"]["future"].iteritems())}
|
||||
% endif
|
||||
|
||||
<p>
|
||||
You can check all contracts to be renewed using the menu:
|
||||
</p>
|
||||
<ul>
|
||||
<li>Sales / Invoicing / Contracts to Renew</li>
|
||||
</ul>
|
||||
<p>
|
||||
Thanks,
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
--
|
||||
OpenERP Automatic Email
|
||||
</pre>
|
||||
|
||||
]]></field>
|
||||
|
|
|
@ -130,11 +130,11 @@
|
|||
</tr>
|
||||
</table>
|
||||
<group name='invoice_on_timesheets'>
|
||||
<p class="oe_grey oe_edit_only" colspan="2" attrs="{'invisible': [('invoice_on_timesheets','=',False)]}">
|
||||
When invoicing on timesheet, OpenERP uses the
|
||||
<p name='invoice_on_timesheets_label' class="oe_grey oe_edit_only" colspan="2" attrs="{'invisible': [('invoice_on_timesheets','=',False)]}">
|
||||
When reinvoicing costs, OpenERP uses the
|
||||
pricelist of the contract which uses the price
|
||||
defined on the product related to each employee to
|
||||
define the customer invoice price rate.
|
||||
defined on the product related (e.g timesheet
|
||||
products are defined on each employee).
|
||||
</p>
|
||||
<group>
|
||||
<field name="pricelist_id"
|
||||
|
@ -218,7 +218,7 @@
|
|||
<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="closed" string="Closed" domain="[('state','=','close')]" help="Closed contracts"/>
|
||||
<filter name="cancelled" string="Cancelled" domain="[('state','=','cancel')]" help="Cancelled contracts"/>
|
||||
<separator/>
|
||||
<filter
|
||||
|
@ -276,7 +276,7 @@
|
|||
<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="context">{'search_default_to_invoice': 1}</field>
|
||||
<field name="search_view_id" ref="account.view_account_analytic_line_filter"/>
|
||||
<field name="help" type="html">
|
||||
<p>
|
||||
|
|
|
@ -308,8 +308,8 @@ class account_invoice_line(osv.osv):
|
|||
res ['analytics_id'] = line.analytics_id and line.analytics_id.id or False
|
||||
return res
|
||||
|
||||
def product_id_change(self, cr, uid, ids, product, uom, qty=0, name='', type='out_invoice', partner_id=False, fposition_id=False, price_unit=False, currency_id=False, context=None, company_id=None):
|
||||
res_prod = super(account_invoice_line, self).product_id_change(cr, uid, ids, product, uom, qty, name, type, partner_id, fposition_id, price_unit, currency_id, context=context, company_id=company_id)
|
||||
def product_id_change(self, cr, uid, ids, product, uom_id, qty=0, name='', type='out_invoice', partner_id=False, fposition_id=False, price_unit=False, currency_id=False, context=None, company_id=None):
|
||||
res_prod = super(account_invoice_line, self).product_id_change(cr, uid, ids, product, uom_id, qty, name, type, partner_id, fposition_id, price_unit, currency_id, context=context, company_id=company_id)
|
||||
rec = self.pool.get('account.analytic.default').account_get(cr, uid, product, partner_id, uid, time.strftime('%Y-%m-%d'), context=context)
|
||||
if rec and rec.analytics_id:
|
||||
res_prod['value'].update({'analytics_id': rec.analytics_id.id})
|
||||
|
|
|
@ -25,6 +25,7 @@ from dateutil.relativedelta import relativedelta
|
|||
|
||||
from openerp.osv import fields, osv
|
||||
import openerp.addons.decimal_precision as dp
|
||||
from openerp.tools.translate import _
|
||||
|
||||
class account_asset_category(osv.osv):
|
||||
_name = 'account.asset.category'
|
||||
|
@ -75,6 +76,12 @@ class account_asset_asset(osv.osv):
|
|||
_name = 'account.asset.asset'
|
||||
_description = 'Asset'
|
||||
|
||||
def unlink(self, cr, uid, ids, context=None):
|
||||
for asset in self.browse(cr, uid, ids, context=context):
|
||||
if asset.account_move_line_ids:
|
||||
raise osv.except_osv(_('Error!'), _('You cannot delete an asset that contains posted depreciation lines.'))
|
||||
return super(account_account, self).unlink(cr, uid, ids, context=context)
|
||||
|
||||
def _get_period(self, cr, uid, context=None):
|
||||
periods = self.pool.get('account.period').find(cr, uid)
|
||||
if periods:
|
||||
|
@ -369,7 +376,7 @@ class account_asset_depreciation_line(osv.osv):
|
|||
_columns = {
|
||||
'name': fields.char('Depreciation Name', size=64, required=True, select=1),
|
||||
'sequence': fields.integer('Sequence', required=True),
|
||||
'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
|
||||
'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True, ondelete='cascade'),
|
||||
'parent_state': fields.related('asset_id', 'state', type='char', string='State of Asset'),
|
||||
'amount': fields.float('Current Depreciation', digits_compute=dp.get_precision('Account'), required=True),
|
||||
'remaining_value': fields.float('Next Period Depreciation', digits_compute=dp.get_precision('Account'),required=True),
|
||||
|
@ -454,7 +461,7 @@ account_asset_depreciation_line()
|
|||
class account_move_line(osv.osv):
|
||||
_inherit = 'account.move.line'
|
||||
_columns = {
|
||||
'asset_id': fields.many2one('account.asset.asset', 'Asset'),
|
||||
'asset_id': fields.many2one('account.asset.asset', 'Asset', ondelete="restrict"),
|
||||
'entry_ids': fields.one2many('account.move.line', 'asset_id', 'Entries', readonly=True, states={'draft':[('readonly',False)]}),
|
||||
|
||||
}
|
||||
|
|
|
@ -99,7 +99,7 @@
|
|||
<button string="Approve" name="validate" states="confirm" type="workflow" class="oe_highlight"/>
|
||||
<button string="Done" name="done" states="validate" type="workflow" class="oe_highlight"/>
|
||||
<button name="draft" states="cancel" string="Reset to Draft" type="workflow" />
|
||||
<button string="Cancel" name="cancel" states="confirm,validate" type="workflow"/>
|
||||
<button string="Cancel Budget" name="cancel" states="confirm,validate" type="workflow"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,confirm"/>
|
||||
</header>
|
||||
<sheet string="Budget">
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<field name="inherit_id" ref="account.invoice_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//button[@name='invoice_cancel']" position="replace">
|
||||
<button name="invoice_cancel" states="draft,proforma2,sale,open" string="Cancel" groups="account.group_account_invoice"/>
|
||||
<button name="invoice_cancel" states="draft,proforma2,sale,open" string="Cancel Invoice" groups="account.group_account_invoice"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
@ -29,7 +29,7 @@
|
|||
<field name="inherit_id" ref="account.invoice_supplier_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//button[@name='invoice_cancel']" position="replace">
|
||||
<button name="invoice_cancel" states="draft,proforma2,sale,open" string="Cancel" groups="account.group_account_invoice"/>
|
||||
<button name="invoice_cancel" states="draft,proforma2,sale,open" string="Cancel Invoice" groups="account.group_account_invoice"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
|
|
@ -292,8 +292,7 @@ class res_partner(osv.osv):
|
|||
type = 'comment',
|
||||
subtype = "mail.mt_comment", context = context,
|
||||
model = 'res.partner', res_id = part.id,
|
||||
notified_partner_ids = [(6, 0, [responsible_partner_id])],
|
||||
partner_ids = [(6, 0, [responsible_partner_id])])
|
||||
partner_ids = [responsible_partner_id])
|
||||
return super(res_partner, self).write(cr, uid, ids, vals, context=context)
|
||||
|
||||
def action_done(self, cr, uid, ids, context=None):
|
||||
|
@ -426,6 +425,14 @@ class res_partner(osv.osv):
|
|||
return [('id','=','0')]
|
||||
return [('id','in', [x[0] for x in res])]
|
||||
|
||||
def _get_partners(self, cr, uid, ids, context=None):
|
||||
#this function search for the partners linked to all account.move.line 'ids' that have been changed
|
||||
partners = set()
|
||||
for aml in self.browse(cr, uid, ids, context=context):
|
||||
if aml.partner_id:
|
||||
partners.add(aml.partner_id.id)
|
||||
return list(partners)
|
||||
|
||||
_inherit = "res.partner"
|
||||
_columns = {
|
||||
'payment_responsible_id':fields.many2one('res.users', ondelete='set null', string='Follow-up Responsible',
|
||||
|
@ -447,12 +454,18 @@ class res_partner(osv.osv):
|
|||
'latest_followup_level_id':fields.function(_get_latest, method=True,
|
||||
type='many2one', relation='account_followup.followup.line', string="Latest Follow-up Level",
|
||||
help="The maximum follow-up level",
|
||||
store=False,
|
||||
store={
|
||||
'res.partner': (lambda self, cr, uid, ids, c: ids,[],10),
|
||||
'account.move.line': (_get_partners, ['followup_line_id'], 10),
|
||||
},
|
||||
multi="latest"),
|
||||
'latest_followup_level_id_without_lit':fields.function(_get_latest, method=True,
|
||||
type='many2one', relation='account_followup.followup.line', string="Latest Follow-up Level without litigation",
|
||||
help="The maximum follow-up level without taking into account the account move lines with litigation",
|
||||
store=False,
|
||||
store={
|
||||
'res.partner': (lambda self, cr, uid, ids, c: ids,[],10),
|
||||
'account.move.line': (_get_partners, ['followup_line_id'], 10),
|
||||
},
|
||||
multi="latest"),
|
||||
'payment_amount_due':fields.function(_get_amounts_and_date,
|
||||
type='float', string="Amount Due",
|
||||
|
|
|
@ -17,12 +17,24 @@
|
|||
<field name="parent_id" invisible="1"/>
|
||||
<field name="payment_responsible_id"/>
|
||||
<field name="payment_earliest_due_date"/>
|
||||
<field name="latest_followup_level_id"/>
|
||||
<field name="payment_amount_overdue"/>
|
||||
<field name="payment_amount_due" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_partner_inherit_customer_followup_tree" model="ir.ui.view">
|
||||
<field name="name">res.partner.followup.inherit.tree</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="base.view_partner_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="name" position="after">
|
||||
<field name="payment_responsible_id" invisible="1"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="customer_followup_search_view" model="ir.ui.view">
|
||||
<field name="name">Search</field>
|
||||
<field name="model">res.partner</field>
|
||||
|
@ -39,6 +51,7 @@
|
|||
</group>
|
||||
<group expand="1" string="Group By...">
|
||||
<filter string="Follow-up Responsible" context="{'group_by':'payment_responsible_id'}"/>
|
||||
<filter string="Followup Level" context="{'group_by':'latest_followup_level_id'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
|
|
|
@ -52,10 +52,10 @@ class TestAccountFollowup(TransactionCase):
|
|||
def test_00_send_followup_after_3_days(self):
|
||||
""" Send follow up after 3 days and check nothing is done (as first follow-up level is only after 15 days)"""
|
||||
cr, uid = self.cr, self.uid
|
||||
current_date = datetime.datetime.now()
|
||||
current_date = datetime.datetime.utcnow()
|
||||
delta = datetime.timedelta(days=3)
|
||||
result = current_date + delta
|
||||
self.wizard_id = self.wizard.create(cr, uid, {'date':result.strftime("%Y-%m-%d"),
|
||||
self.wizard_id = self.wizard.create(cr, uid, {'date':result.strftime(tools.DEFAULT_SERVER_DATE_FORMAT),
|
||||
'followup_id': self.followup_id
|
||||
}, context={"followup_id": self.followup_id})
|
||||
self.wizard.do_process(cr, uid, [self.wizard_id], context={"followup_id": self.followup_id})
|
||||
|
@ -64,34 +64,33 @@ class TestAccountFollowup(TransactionCase):
|
|||
|
||||
def run_wizard_three_times(self):
|
||||
cr, uid = self.cr, self.uid
|
||||
current_date = datetime.datetime.now()
|
||||
current_date = datetime.datetime.utcnow()
|
||||
delta = datetime.timedelta(days=40)
|
||||
result = current_date + delta
|
||||
self.wizard_id = self.wizard.create(cr, uid, {'date':result.strftime("%Y-%m-%d"),
|
||||
self.wizard_id = self.wizard.create(cr, uid, {'date':result.strftime(tools.DEFAULT_SERVER_DATE_FORMAT),
|
||||
'followup_id': self.followup_id
|
||||
}, context={"followup_id": self.followup_id})
|
||||
self.wizard.do_process(cr, uid, [self.wizard_id], context={"followup_id": self.followup_id})
|
||||
self.wizard_id = self.wizard.create(cr, uid, {'date':result.strftime("%Y-%m-%d"),
|
||||
self.wizard.do_process(cr, uid, [self.wizard_id], context={"followup_id": self.followup_id, 'tz':'UTC'})
|
||||
self.wizard_id = self.wizard.create(cr, uid, {'date':result.strftime(tools.DEFAULT_SERVER_DATE_FORMAT),
|
||||
'followup_id': self.followup_id
|
||||
}, context={"followup_id": self.followup_id})
|
||||
self.wizard.do_process(cr, uid, [self.wizard_id], context={"followup_id": self.followup_id})
|
||||
self.wizard_id = self.wizard.create(cr, uid, {'date':result.strftime("%Y-%m-%d"),
|
||||
'followup_id': self.followup_id
|
||||
self.wizard.do_process(cr, uid, [self.wizard_id], context={"followup_id": self.followup_id, 'tz':'UTC'})
|
||||
self.wizard_id = self.wizard.create(cr, uid, {'date':result.strftime(tools.DEFAULT_SERVER_DATE_FORMAT),
|
||||
'followup_id': self.followup_id,
|
||||
}, context={"followup_id": self.followup_id})
|
||||
self.wizard.do_process(cr, uid, [self.wizard_id], context={"followup_id": self.followup_id})
|
||||
|
||||
self.wizard.do_process(cr, uid, [self.wizard_id], context={"followup_id": self.followup_id, 'tz':'UTC'})
|
||||
|
||||
def test_01_send_followup_later_for_upgrade(self):
|
||||
""" Send one follow-up after 15 days to check it upgrades to level 1"""
|
||||
cr, uid = self.cr, self.uid
|
||||
current_date = datetime.datetime.now()
|
||||
current_date = datetime.datetime.utcnow()
|
||||
delta = datetime.timedelta(days=15)
|
||||
result = current_date + delta
|
||||
self.wizard_id = self.wizard.create(cr, uid, {
|
||||
'date':result.strftime("%Y-%m-%d"),
|
||||
'date':result.strftime(tools.DEFAULT_SERVER_DATE_FORMAT),
|
||||
'followup_id': self.followup_id
|
||||
}, context={"followup_id": self.followup_id})
|
||||
self.wizard.do_process(cr, uid, [self.wizard_id], context={"followup_id": self.followup_id})
|
||||
self.wizard.do_process(cr, uid, [self.wizard_id], context={"followup_id": self.followup_id, 'tz':'UTC'})
|
||||
self.assertEqual(self.partner.browse(cr, uid, self.partner_id).latest_followup_level_id.id, self.first_followup_line_id,
|
||||
"Not updated to the correct follow-up level")
|
||||
|
||||
|
@ -102,12 +101,12 @@ class TestAccountFollowup(TransactionCase):
|
|||
self.assertEqual(self.partner.browse(cr, uid, self.partner_id).payment_next_action,
|
||||
"Call the customer on the phone! ", "Manual action not set")
|
||||
self.assertEqual(self.partner.browse(cr, uid, self.partner_id).payment_next_action_date,
|
||||
datetime.datetime.now().strftime("%Y-%m-%d"))
|
||||
|
||||
datetime.datetime.utcnow().strftime(tools.DEFAULT_SERVER_DATE_FORMAT))
|
||||
|
||||
def test_03_filter_on_credit(self):
|
||||
""" Check the partners can be filtered on having credits """
|
||||
cr, uid = self.cr, self.uid
|
||||
ids = self.partner.search(cr, uid, [('payment_amount_due', '>=', 0.0)])
|
||||
ids = self.partner.search(cr, uid, [('payment_amount_due', '>', 0.0)])
|
||||
self.assertIn(self.partner_id, ids)
|
||||
|
||||
def test_04_action_done(self):
|
||||
|
@ -139,7 +138,7 @@ class TestAccountFollowup(TransactionCase):
|
|||
"""Run wizard until manual action, pay the invoice and check that partner has no follow-up level anymore and after running the wizard the action is empty"""
|
||||
cr, uid = self.cr, self.uid
|
||||
self.test_02_check_manual_action()
|
||||
current_date = datetime.datetime.now()
|
||||
current_date = datetime.datetime.utcnow()
|
||||
delta = datetime.timedelta(days=1)
|
||||
result = current_date + delta
|
||||
self.invoice.pay_and_reconcile(cr, uid, [self.invoice_id], 1000.0, self.pay_account_id,
|
||||
|
@ -147,7 +146,7 @@ class TestAccountFollowup(TransactionCase):
|
|||
self.period_id, self.journal_id,
|
||||
name = "Payment for test customer invoice follow-up")
|
||||
self.assertFalse(self.partner.browse(cr, uid, self.partner_id).latest_followup_level_id, "Level not empty")
|
||||
self.wizard_id = self.wizard.create(cr, uid, {'date':result.strftime("%Y-%m-%d"),
|
||||
self.wizard_id = self.wizard.create(cr, uid, {'date':result.strftime(tools.DEFAULT_SERVER_DATE_FORMAT),
|
||||
'followup_id': self.followup_id
|
||||
}, context={"followup_id": self.followup_id})
|
||||
self.wizard.do_process(cr, uid, [self.wizard_id], context={"followup_id": self.followup_id})
|
||||
|
|
|
@ -53,7 +53,6 @@ have a new option to import payment orders as bank statement lines.
|
|||
'account_payment_view.xml',
|
||||
'account_payment_workflow.xml',
|
||||
'account_payment_sequence.xml',
|
||||
'account_invoice_view.xml',
|
||||
'account_payment_report.xml',
|
||||
],
|
||||
'demo': ['account_payment_demo.xml'],
|
||||
|
|
|
@ -19,9 +19,8 @@
|
|||
#
|
||||
##############################################################################
|
||||
|
||||
from datetime import datetime
|
||||
from openerp.tools.translate import _
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.osv import osv
|
||||
|
||||
class Invoice(osv.osv):
|
||||
_inherit = 'account.invoice'
|
||||
|
@ -43,28 +42,6 @@ class Invoice(osv.osv):
|
|||
raise osv.except_osv(_('Error!'), _("You cannot cancel an invoice which has already been imported in a payment order. Remove it from the following payment order : %s."%(payment_order_name)))
|
||||
return super(Invoice, self).action_cancel(cr, uid, ids, context=context)
|
||||
|
||||
def _amount_to_pay(self, cursor, user, ids, name, args, context=None):
|
||||
'''Return the amount still to pay regarding all the payment orders'''
|
||||
if not ids:
|
||||
return {}
|
||||
res = {}
|
||||
for invoice in self.browse(cursor, user, ids, context=context):
|
||||
res[invoice.id] = 0.0
|
||||
if invoice.move_id:
|
||||
for line in invoice.move_id.line_id:
|
||||
if not line.date_maturity or \
|
||||
datetime.strptime(line.date_maturity, '%Y-%m-%d') \
|
||||
< datetime.today():
|
||||
res[invoice.id] += line.amount_to_pay
|
||||
return res
|
||||
|
||||
_columns = {
|
||||
'amount_to_pay': fields.function(_amount_to_pay,
|
||||
type='float', string='Amount to be paid',
|
||||
help='The amount which should be paid at the current date\n' \
|
||||
'minus the amount which is already in payment order'),
|
||||
}
|
||||
|
||||
Invoice()
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<record id="invoice_supplier_form" model="ir.ui.view">
|
||||
<field name="name">account.invoice.supplier.form.inherit</field>
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="inherit_id" ref="account.invoice_supplier_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="partner_bank_id" position="before">
|
||||
<field name="amount_to_pay"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
|
@ -19,65 +19,12 @@
|
|||
#
|
||||
##############################################################################
|
||||
|
||||
from operator import itemgetter
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.osv import osv
|
||||
from openerp.tools.translate import _
|
||||
|
||||
class account_move_line(osv.osv):
|
||||
_inherit = "account.move.line"
|
||||
|
||||
def amount_to_pay(self, cr, uid, ids, name, arg=None, context=None):
|
||||
""" Return the amount still to pay regarding all the payemnt orders
|
||||
(excepting cancelled orders)"""
|
||||
if not ids:
|
||||
return {}
|
||||
cr.execute("""SELECT ml.id,
|
||||
CASE WHEN ml.amount_currency < 0
|
||||
THEN - ml.amount_currency
|
||||
ELSE ml.credit
|
||||
END -
|
||||
(SELECT coalesce(sum(amount_currency),0)
|
||||
FROM payment_line pl
|
||||
INNER JOIN payment_order po
|
||||
ON (pl.order_id = po.id)
|
||||
WHERE move_line_id = ml.id
|
||||
AND po.state != 'cancel') AS amount
|
||||
FROM account_move_line ml
|
||||
WHERE id IN %s""", (tuple(ids),))
|
||||
r = dict(cr.fetchall())
|
||||
return r
|
||||
|
||||
def _to_pay_search(self, cr, uid, obj, name, args, context=None):
|
||||
if not args:
|
||||
return []
|
||||
line_obj = self.pool.get('account.move.line')
|
||||
query = line_obj._query_get(cr, uid, context={})
|
||||
where = ' and '.join(map(lambda x: '''(SELECT
|
||||
CASE WHEN l.amount_currency < 0
|
||||
THEN - l.amount_currency
|
||||
ELSE l.credit
|
||||
END - coalesce(sum(pl.amount_currency), 0)
|
||||
FROM payment_line pl
|
||||
INNER JOIN payment_order po ON (pl.order_id = po.id)
|
||||
WHERE move_line_id = l.id
|
||||
AND po.state != 'cancel'
|
||||
) %(operator)s %%s ''' % {'operator': x[1]}, args))
|
||||
sql_args = tuple(map(itemgetter(2), args))
|
||||
|
||||
cr.execute(('''SELECT id
|
||||
FROM account_move_line l
|
||||
WHERE account_id IN (select id
|
||||
FROM account_account
|
||||
WHERE type=%s AND active)
|
||||
AND reconcile_id IS null
|
||||
AND credit > 0
|
||||
AND ''' + where + ' and ' + query), ('payable',)+sql_args )
|
||||
|
||||
res = cr.fetchall()
|
||||
if not res:
|
||||
return [('id', '=', '0')]
|
||||
return [('id', 'in', map(lambda x:x[0], res))]
|
||||
|
||||
def line2bank(self, cr, uid, ids, payment_type=None, context=None):
|
||||
"""
|
||||
Try to return for each Ledger Posting line a corresponding bank
|
||||
|
@ -110,11 +57,6 @@ class account_move_line(osv.osv):
|
|||
raise osv.except_osv(_('Error!'), _('There is no partner defined on the entry line.'))
|
||||
return line2bank
|
||||
|
||||
_columns = {
|
||||
'amount_to_pay': fields.function(amount_to_pay,
|
||||
type='float', string='Amount to pay', fnct_search=_to_pay_search),
|
||||
}
|
||||
|
||||
account_move_line()
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -352,7 +352,7 @@ class payment_line(osv.osv):
|
|||
|
||||
if move_line_id:
|
||||
line = move_line_obj.browse(cr, uid, move_line_id, context=context)
|
||||
data['amount_currency'] = line.amount_to_pay
|
||||
data['amount_currency'] = line.amount_residual_currency
|
||||
|
||||
res = self.onchange_amount(cr, uid, ids, data['amount_currency'], currency,
|
||||
company_currency, context)
|
||||
|
|
|
@ -2,29 +2,6 @@
|
|||
<openerp>
|
||||
<data>
|
||||
|
||||
<!-- View used in the wizard -->
|
||||
<record id="view_move_line_form" model="ir.ui.view">
|
||||
<field name="name">account.move.line.form.inherit</field>
|
||||
<field name="model">account.move.line</field>
|
||||
<field name="inherit_id" ref="account.view_move_line_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="reconcile_partial_id" position="after">
|
||||
<field name="amount_to_pay"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- <record model="ir.ui.view" id="view_move_line_tree_wiz">
|
||||
<field name="name">account.move.line.tree</field>
|
||||
<field name="model">account.move.line</field>
|
||||
<field name="inherit_id" ref="account.view_move_line_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="reconcile" position="after">
|
||||
<field name="amount_to_pay"/>
|
||||
</field>
|
||||
</field>
|
||||
</record> -->
|
||||
|
||||
<menuitem id="menu_main_payment" name="Payment" parent="account.menu_finance" sequence="7"/>
|
||||
|
||||
<record id="view_payment_mode_search" model="ir.ui.view">
|
||||
|
@ -88,7 +65,7 @@
|
|||
<button name="open" states="draft" string="Confirm Payments" class="oe_highlight"/>
|
||||
<button name="set_done" states="open" string="Make Payments" type="object" class="oe_highlight"/>
|
||||
<button name="set_to_draft" states="cancel" string="Set to draft" type="object"/>
|
||||
<button name="cancel" states="draft,open" string="Cancel"/>
|
||||
<button name="cancel" states="draft,open" string="Cancel Payments"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,open"/>
|
||||
</header>
|
||||
<sheet string="Payment order">
|
||||
|
@ -116,7 +93,7 @@
|
|||
<notebook>
|
||||
<page string="Payment">
|
||||
<group col="4">
|
||||
<field name="move_line_id" on_change="onchange_move_line(move_line_id,parent.mode,parent.date_prefered,parent.date_scheduled,currency,company_currency)" domain="[('reconcile_id','=', False), ('credit', '>',0),('amount_to_pay','>',0)] "/>
|
||||
<field name="move_line_id" on_change="onchange_move_line(move_line_id,parent.mode,parent.date_prefered,parent.date_scheduled,currency,company_currency)" domain="[('reconcile_id','=', False), ('credit', '>',0),('amount_residual','>',0)] "/>
|
||||
<separator colspan="4" string="Transaction Information"/>
|
||||
<field name="date"/>
|
||||
<label for="amount_currency" groups="base.group_multi_currency"/>
|
||||
|
@ -242,7 +219,7 @@
|
|||
<page string="Payment">
|
||||
<group col="4">
|
||||
<field name="order_id"/>
|
||||
<field name="move_line_id" on_change="onchange_move_line(move_line_id, False, currency, company_currency)" domain="[('reconcile_id','=', False), ('credit', '>',0),('amount_to_pay','>',0)]"/>
|
||||
<field name="move_line_id" on_change="onchange_move_line(move_line_id, False, currency, company_currency)" domain="[('reconcile_id','=', False), ('credit', '>',0),('amount_residual','>',0)]"/>
|
||||
<separator colspan="4" string="Transaction Information"/>
|
||||
<field name="date"/>
|
||||
<label for="amount_currency" groups="base.group_multi_currency"/>
|
||||
|
|
|
@ -82,7 +82,7 @@ class payment_order_create(osv.osv_memory):
|
|||
date_to_pay = payment.date_scheduled
|
||||
payment_obj.create(cr, uid,{
|
||||
'move_line_id': line.id,
|
||||
'amount_currency': line.amount_to_pay,
|
||||
'amount_currency': line.amount_residual_currency,
|
||||
'bank_id': line2bank.get(line.id),
|
||||
'order_id': payment.id,
|
||||
'partner_id': line.partner_id and line.partner_id.id or False,
|
||||
|
@ -102,7 +102,7 @@ class payment_order_create(osv.osv_memory):
|
|||
# payment = self.pool.get('payment.order').browse(cr, uid, context['active_id'], context=context)
|
||||
|
||||
# Search for move line to pay:
|
||||
domain = [('reconcile_id', '=', False), ('account_id.type', '=', 'payable'), ('amount_to_pay', '>', 0)]
|
||||
domain = [('reconcile_id', '=', False), ('account_id.type', '=', 'payable'), ('amount_residual', '>', 0)]
|
||||
domain = domain + ['|', ('date_maturity', '<=', search_due_date), ('date_maturity', '=', False)]
|
||||
line_ids = line_obj.search(cr, uid, domain, context=context)
|
||||
context.update({'line_ids': line_ids})
|
||||
|
|
|
@ -43,8 +43,8 @@
|
|||
<form string="Accounting Voucher" version="7.0">
|
||||
<header>
|
||||
<button name="proforma_voucher" string="Post" states="draft" class="oe_highlight"/>
|
||||
<button name="cancel_voucher" string="Cancel" type="object" states="posted" confirm="Are you sure to unreconcile this record?"/>
|
||||
<button name="cancel_voucher" string="Cancel" states="draft,proforma" />
|
||||
<button name="cancel_voucher" string="Cancel Voucher" type="object" states="posted" confirm="Are you sure you want to unreconcile this record?"/>
|
||||
<button name="cancel_voucher" string="Cancel Voucher" states="draft,proforma" />
|
||||
<button name="action_cancel_draft" type="object" states="cancel" string="Set to Draft"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,posted" statusbar_colors='{"proforma":"blue"}'/>
|
||||
</header>
|
||||
|
|
|
@ -41,7 +41,7 @@ class invoice(osv.osv):
|
|||
'target': 'new',
|
||||
'domain': '[]',
|
||||
'context': {
|
||||
'default_partner_id': self._find_partner(inv).id,
|
||||
'default_partner_id': self.pool.get('res.partner')._find_accounting_partner(inv.partner_id).id,
|
||||
'default_amount': inv.type in ('out_refund', 'in_refund') and -inv.residual or inv.residual,
|
||||
'default_reference': inv.name,
|
||||
'close_after_process': True,
|
||||
|
|
|
@ -126,7 +126,7 @@
|
|||
<form string="Voucher Payment" version="7.0">
|
||||
<header>
|
||||
<button name="proforma_voucher" string="Validate" states="draft" invisible="context.get('line_type', False)" class="oe_highlight"/>
|
||||
<button name="cancel_voucher" string="Cancel" states="draft,proforma" invisible="context.get('line_type', False)"/>
|
||||
<button name="cancel_voucher" string="Cancel Voucher" states="draft,proforma" invisible="context.get('line_type', False)"/>
|
||||
<button name="cancel_voucher" string="Unreconcile" type="object" states="posted" invisible="context.get('line_type', False)" confirm="Are you sure to unreconcile and cancel this record ?"/>
|
||||
<button name="action_cancel_draft" type="object" states="cancel" string="Set to Draft" invisible="context.get('line_type', False)"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,posted" statusbar_colors='{"proforma":"blue"}'/>
|
||||
|
@ -394,7 +394,7 @@
|
|||
<form string="Receipt" version="7.0">
|
||||
<header invisible="context.get('line_type', False)">
|
||||
<button name="proforma_voucher" string="Validate" states="draft" class="oe_highlight"/>
|
||||
<button name="cancel_voucher" string="Cancel" states="draft,proforma"/>
|
||||
<button name="cancel_voucher" string="Cancel Receipt" states="draft,proforma"/>
|
||||
<button name="cancel_voucher" string="Unreconcile" type="object" states="posted" confirm="Are you sure to unreconcile and cancel this record ?"/>
|
||||
<button name="action_cancel_draft" type="object" states="cancel" string="Set to Draft"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,posted" statusbar_colors='{"proforma":"blue"}'/>
|
||||
|
|
|
@ -59,17 +59,17 @@
|
|||
<field name="name">account.voucher.sale.form</field>
|
||||
<field name="model">account.voucher</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Sale voucher" version="7.0">
|
||||
<form string="Sales Receipt" version="7.0">
|
||||
<header>
|
||||
<button name="proforma_voucher" string="Validate" states="draft" class="oe_highlight"/>
|
||||
<button name="%(act_pay_voucher)d" context="{'narration':narration, 'title':'Customer Payment', 'type':'receipt', 'partner_id':partner_id, 'reference':reference, 'amount':amount}" type="action" string="Validate Payment" attrs="{'invisible':['|',('pay_now','=','pay_now'),'|',('state','=','draft'), ('paid','=',True)]}" class="oe_highlight"/>
|
||||
<button name="cancel_voucher" string="Cancel" states="draft,proforma" />
|
||||
<button name="cancel_voucher" string="Cancel" type="object" states="posted" confirm="Are you sure you want to cancel this receipt?"/>
|
||||
<button name="cancel_voucher" string="Cancel Receipt" states="draft,proforma" />
|
||||
<button name="cancel_voucher" string="Cancel Receipt" type="object" states="posted" confirm="Are you sure you want to cancel this receipt?"/>
|
||||
<button name="action_cancel_draft" type="object" states="cancel" string="Set to Draft"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,posted" statusbar_colors='{"proforma":"blue"}'/>
|
||||
</header>
|
||||
<sheet string="Sales Receipts" >
|
||||
<h1><label for="number" string="Sale Receipt"/> <field name="number" class="oe_inline" readonly="1"/></h1>
|
||||
<sheet string="Sales Receipt" >
|
||||
<h1><label for="number" string="Sales Receipt"/> <field name="number" class="oe_inline" readonly="1"/></h1>
|
||||
<group>
|
||||
<group>
|
||||
<field name="type" invisible="True"/>
|
||||
|
@ -208,8 +208,8 @@
|
|||
<header>
|
||||
<button name="proforma_voucher" string="Validate" states="draft" class="oe_highlight"/>
|
||||
<button name="%(act_pay_bills)d" context="{'narration':narration, 'title':'Bill Payment', 'type':'payment', 'partner_id': partner_id, 'reference':reference}" type="action" string="Pay Bill" attrs="{'invisible':['|',('pay_now','=','pay_now'),'|',('state','=','draft'), ('paid','=',True)]}" class="oe_highlight"/>
|
||||
<button name="cancel_voucher" string="Cancel" states="draft,proforma" />
|
||||
<button name="cancel_voucher" string="Cancel" type="object" states="posted" confirm="Are you sure you want to cancel this receipt?"/>
|
||||
<button name="cancel_voucher" string="Cancel Voucher" states="draft,proforma" />
|
||||
<button name="cancel_voucher" string="Cancel Voucher" type="object" states="posted" confirm="Are you sure you want to cancel this receipt?"/>
|
||||
<button name="action_cancel_draft" type="object" states="cancel" string="Set to Draft"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,posted" statusbar_colors='{"proforma":"blue"}'/>
|
||||
</header>
|
||||
|
|
|
@ -40,6 +40,16 @@
|
|||
</td>
|
||||
</tr>
|
||||
</xpath>
|
||||
<xpath expr="//p[@name='invoice_on_timesheets_label']" position="attributes">
|
||||
<attribute name="attrs">{'invisible': [('invoice_on_timesheets','=',False),('charge_expenses','=',False)]}</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='pricelist_id']" position="attributes">
|
||||
<attribute name="attrs">{'required': ['|',('invoice_on_timesheets','=',True),('charge_expenses','=',True)], 'invisible':[('invoice_on_timesheets','=',False), ('charge_expenses','=',False)]}</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='to_invoice']" position="attributes">
|
||||
<attribute name="attrs">{'required': ['|',('invoice_on_timesheets','=',True),('charge_expenses','=',True)]}</attribute>
|
||||
<attribute name="string">Expenses and Timesheet Invoicing Ratio</attribute>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
|
|
@ -1,25 +1,15 @@
|
|||
.login .pane {
|
||||
width: 260px;
|
||||
height: 175px;
|
||||
}
|
||||
|
||||
.login .pane input[name='openid_url'] {
|
||||
.oe_login .oe_login_pane input[name='openid_url'] {
|
||||
background: #fff url(../img/login-bg.gif) no-repeat 1px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.auth_choice {
|
||||
position: static;
|
||||
display: none;
|
||||
.openerp .oe_login .openid_providers {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.openid_providers {
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
float: right;
|
||||
.openerp .oe_login .openid_providers ul {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.openid_providers li {
|
||||
.openerp .oe_login .openid_providers ul li {
|
||||
display: block;
|
||||
float: left;
|
||||
margin: 0 1px 3px 2px;
|
||||
|
@ -29,7 +19,6 @@
|
|||
display: block;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border: 1px solid #ddd;
|
||||
background: #fff url(../img/openid_16.png) no-repeat 50%;
|
||||
text-indent: -9999px;
|
||||
overflow: hidden;
|
||||
|
@ -37,20 +26,16 @@
|
|||
}
|
||||
|
||||
.openid_providers a.selected {
|
||||
border-color: #9A0404;
|
||||
background-color: #DC5F59;
|
||||
}
|
||||
|
||||
.openid_providers a[title="Password"] { background-image: url(../img/textfield_key.png); }
|
||||
.openid_providers a[title="AOL"] { background-image: url(../img/aol.png); }
|
||||
.openid_providers a[title="ClaimID"] { background-image: url(../img/claimid.png); }
|
||||
.openid_providers a[title="Google"] { background-image: url(../img/googlefav.png); }
|
||||
.openid_providers a[title="Google Apps"] { background-image: url(../img/marketplace.gif); }
|
||||
.openid_providers a[title="MyOpenID"] { background-image: url(../img/myopenid.png); }
|
||||
.openid_providers a[title="VeriSign"] { background-image: url(../img/verisign.png); }
|
||||
.openid_providers a[title="Yahoo!"] { background-image: url(../img/yahoo.png); }
|
||||
.openid_providers a[title="Launchpad"] { background-image: url(../img/launchpad.png); }
|
||||
.openid_providers a[data-provider="Password"] { background-image: url(../img/textfield_key.png); }
|
||||
.openid_providers a[data-provider="AOL"] { background-image: url(../img/aol.png); }
|
||||
.openid_providers a[data-provider="ClaimID"] { background-image: url(../img/claimid.png); }
|
||||
.openid_providers a[data-provider="Google"] { background-image: url(../img/googlefav.png); }
|
||||
.openid_providers a[data-provider="Google Apps"] { background-image: url(../img/marketplace.gif); }
|
||||
.openid_providers a[data-provider="MyOpenID"] { background-image: url(../img/myopenid.png); }
|
||||
.openid_providers a[data-provider="VeriSign"] { background-image: url(../img/verisign.png); }
|
||||
.openid_providers a[data-provider="Yahoo!"] { background-image: url(../img/yahoo.png); }
|
||||
.openid_providers a[data-provider="Launchpad"] { background-image: url(../img/launchpad.png); }
|
||||
|
||||
|
||||
li.auth_choice.selected {
|
||||
display: table-row;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,16 @@ instance.web.Login = instance.web.Login.extend({
|
|||
self.$openid_selected_input = $();
|
||||
self.$openid_selected_provider = null;
|
||||
|
||||
|
||||
// Hook auth_signup events. noop if module is not installed.
|
||||
self.on('change:login_mode', self, function() {
|
||||
var mode = self.get('login_mode') || 'default';
|
||||
if (mode !== 'default') {
|
||||
return;
|
||||
}
|
||||
self.do_openid_select(self.$openid_selected_button, self.$openid_selected_provider, true);
|
||||
});
|
||||
|
||||
|
||||
var openIdProvider = null;
|
||||
if (self.has_local_storage && self.remember_credentials) {
|
||||
|
@ -21,12 +31,10 @@ instance.web.Login = instance.web.Login.extend({
|
|||
}
|
||||
|
||||
if (openIdProvider) {
|
||||
$openid_selected_provider = openIdProvider;
|
||||
self.$openid_selected_provider = openIdProvider;
|
||||
self.do_openid_select('a[href="#' + openIdProvider + '"]', openIdProvider, true);
|
||||
|
||||
if (self.has_local_storage && self.remember_credentials) {
|
||||
self.$openid_selected_input.find('input').val(localStorage.getItem('openid-login'));
|
||||
}
|
||||
self.$openid_selected_input.find('input').val(localStorage.getItem('openid-login') || '');
|
||||
}
|
||||
else {
|
||||
self.do_openid_select('a[data-url=""]', 'login,password', true);
|
||||
|
@ -49,11 +57,12 @@ instance.web.Login = instance.web.Login.extend({
|
|||
do_openid_select: function (button, provider, noautosubmit) {
|
||||
var self = this;
|
||||
|
||||
self.$('li[data-provider]').hide();
|
||||
self.$openid_selected_button.add(self.$openid_selected_input).removeClass('selected');
|
||||
self.$openid_selected_button = self.$el.find(button).addClass('selected');
|
||||
|
||||
var input = _(provider.split(',')).map(function(p) { return 'li[data-provider="'+p+'"]'; }).join(',');
|
||||
self.$openid_selected_input = self.$el.find(input).addClass('selected');
|
||||
self.$openid_selected_input = self.$el.find(input).show();
|
||||
|
||||
self.$openid_selected_input.find('input:first').focus();
|
||||
self.$openid_selected_provider = (self.$openid_selected_button.attr('href') || '').substr(1);
|
||||
|
@ -62,7 +71,7 @@ instance.web.Login = instance.web.Login.extend({
|
|||
localStorage.setItem('openid-provider', self.$openid_selected_provider);
|
||||
}
|
||||
|
||||
if (!noautosubmit && self.$openid_selected_input.length == 0) {
|
||||
if (!noautosubmit && self.$openid_selected_input.length === 0) {
|
||||
self.$el.find('form').submit();
|
||||
}
|
||||
|
||||
|
|
|
@ -4,44 +4,53 @@
|
|||
|
||||
<t t-extend="Login">
|
||||
<t t-jquery=".oe_login .oe_login_logo" t-operation="after">
|
||||
<ul class="openid_providers oe_semantic_html_override">
|
||||
<li><a href="#login,password" title="Password" data-url="" id="btn_password">Password</a></li>
|
||||
<li><a href="#google" title="Google" data-url="https://www.google.com/accounts/o8/id">Google</a></li>
|
||||
<li><a href="#googleapps" title="Google Apps" data-url="https://www.google.com/accounts/o8/site-xrds?hd={id}">Google</a></li>
|
||||
<li><a href="#launchpad" title="Launchpad" data-url="https://launchpad.net/~{id}">Launchpad</a></li>
|
||||
<li><a href="#openid_url" title="OpenID" data-url="{id}">OpenID</a></li>
|
||||
</ul>
|
||||
<div class="openid_providers" data-modes="default openid"><ul>
|
||||
<li><a href="#login,password" data-provider='Password' title="Password" data-url="" id="btn_password">Password</a></li>
|
||||
<li><a href="#google" data-provider='Google' title="Google" data-url="https://www.google.com/accounts/o8/id">Google</a></li>
|
||||
<li><a href="#googleapps" data-provider='Google Apps' title="Google Apps" data-url="https://www.google.com/accounts/o8/site-xrds?hd={id}">Google</a></li>
|
||||
<li><a href="#launchpad" data-provider='Launchpad' title="Launchpad" data-url="https://launchpad.net/~{id}">Launchpad</a></li>
|
||||
<li><a href="#openid_url" data-provider='OpenID' title="OpenID" data-url="{id}">OpenID</a></li>
|
||||
</ul></div>
|
||||
</t>
|
||||
</t>
|
||||
<t t-extend="Login">
|
||||
<t t-jquery=".oe_login .oe_login_pane form ul li:nth-child(4)" t-operation="after">
|
||||
<li>
|
||||
<t t-jquery=".oe_login .oe_login_pane form ul li:last-child()" t-operation="before">
|
||||
<li data-modes="openid" data-provider='googleapps'>
|
||||
Google Apps Domain
|
||||
</li>
|
||||
<li>
|
||||
<li data-modes="openid" data-provider='googleapps'>
|
||||
<input type="text" name="googleapps" />
|
||||
</li>
|
||||
<li>
|
||||
<li data-modes="openid" data-provider='launchpad'>
|
||||
Username
|
||||
</li>
|
||||
<li>
|
||||
<li data-modes="openid" data-provider='launchpad'>
|
||||
<input type="text" name="launchpad" />
|
||||
</li>
|
||||
<li>
|
||||
<li data-modes="openid" data-provider='openid_url'>
|
||||
OpenID URL
|
||||
</li>
|
||||
<li>
|
||||
<li data-modes="openid" data-provider='openid_url'>
|
||||
<input type="text" name="openid_url" />
|
||||
</li>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<t t-extend="Login">
|
||||
<t t-jquery=".oe_login .oe_login_pane form ul li:has(input)">
|
||||
<t t-jquery=".oe_login .oe_login_pane form ul li:has(input[name=password])">
|
||||
this.each(function() {
|
||||
var $i = $(this);
|
||||
$i.add($i.prev()).attr('data-provider', 'password');
|
||||
});
|
||||
</t>
|
||||
<t t-jquery=".oe_login .oe_login_pane form ul li:has(input[name=login])">
|
||||
this.each(function() {
|
||||
var $i = $(this),
|
||||
dp = $i.find('input').attr('name');
|
||||
$i.add($i.prev()).attr('class', 'auth_choice').attr('data-provider', dp);
|
||||
dp = $i.find('input').attr('name'),
|
||||
$p = $i.prev();
|
||||
// $p may not be the correct label when auth_signup is installed.
|
||||
while(($p.attr('data-modes') || 'default') !== 'default') { $p = $p.prev(); }
|
||||
$i.add($p).attr('data-provider', dp);
|
||||
});
|
||||
</t>
|
||||
</t>
|
||||
|
|
|
@ -42,4 +42,5 @@ Allow users to sign up and reset their password
|
|||
],
|
||||
'js': ['static/src/js/auth_signup.js'],
|
||||
'qweb': ['static/src/xml/auth_signup.xml'],
|
||||
'bootstrap': True,
|
||||
}
|
||||
|
|
|
@ -19,9 +19,6 @@
|
|||
#
|
||||
##############################################################################
|
||||
import logging
|
||||
import urllib
|
||||
|
||||
import werkzeug
|
||||
|
||||
import openerp
|
||||
from openerp.modules.registry import RegistryManager
|
||||
|
@ -54,9 +51,8 @@ class Controller(openerp.addons.web.http.Controller):
|
|||
return user_info
|
||||
|
||||
@openerp.addons.web.http.jsonrequest
|
||||
def signup(self, req, dbname, token, name, login, password):
|
||||
def signup(self, req, dbname, token, **values):
|
||||
""" sign up a user (new or existing)"""
|
||||
values = {'name': name, 'login': login, 'password': password}
|
||||
try:
|
||||
self._signup_with_values(req, dbname, token, values)
|
||||
except SignupError, e:
|
||||
|
@ -69,7 +65,7 @@ class Controller(openerp.addons.web.http.Controller):
|
|||
res_users = registry.get('res.users')
|
||||
res_users.signup(cr, openerp.SUPERUSER_ID, values, token)
|
||||
|
||||
@openerp.addons.web.http.httprequest
|
||||
@openerp.addons.web.http.jsonrequest
|
||||
def reset_password(self, req, dbname, login):
|
||||
""" retrieve user, and perform reset password """
|
||||
registry = RegistryManager.get(dbname)
|
||||
|
@ -78,12 +74,10 @@ class Controller(openerp.addons.web.http.Controller):
|
|||
res_users = registry.get('res.users')
|
||||
res_users.reset_password(cr, openerp.SUPERUSER_ID, login)
|
||||
cr.commit()
|
||||
message = 'An email has been sent with credentials to reset your password'
|
||||
except Exception as e:
|
||||
# signup error
|
||||
_logger.exception('error when resetting password')
|
||||
message = e.message
|
||||
params = [('action', 'login'), ('error_message', message)]
|
||||
return werkzeug.utils.redirect("/#" + urllib.urlencode(params))
|
||||
raise(e)
|
||||
return True
|
||||
|
||||
# vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -161,14 +161,12 @@ class res_users(osv.Model):
|
|||
def _get_state(self, cr, uid, ids, name, arg, context=None):
|
||||
res = {}
|
||||
for user in self.browse(cr, uid, ids, context):
|
||||
res[user.id] = ('reset' if user.signup_valid else
|
||||
'active' if user.login_date else
|
||||
'new')
|
||||
res[user.id] = ('active' if user.login_date else 'new')
|
||||
return res
|
||||
|
||||
_columns = {
|
||||
'state': fields.function(_get_state, string='Status', type='selection',
|
||||
selection=[('new', 'New'), ('active', 'Active'), ('reset', 'Resetting Password')]),
|
||||
selection=[('new', 'Never Connected'), ('active', 'Activated')]),
|
||||
}
|
||||
|
||||
def signup(self, cr, uid, values, token=None, context=None):
|
||||
|
@ -270,16 +268,7 @@ class res_users(osv.Model):
|
|||
if mail_state and mail_state['state'] == 'exception':
|
||||
raise self.pool.get('res.config.settings').get_config_warning(cr, _("Cannot send email: no outgoing email server configured.\nYou can configure it under %(menu:base_setup.menu_general_configuration)s."), context)
|
||||
else:
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'name': '_(Server Notification)',
|
||||
'tag': 'action_notify',
|
||||
'params': {
|
||||
'title': 'Mail Sent to: %s' % user.name,
|
||||
'text': 'You can reset the password by yourself using this <a href=%s>link</a>' % user.partner_id.signup_url,
|
||||
'sticky': True,
|
||||
}
|
||||
}
|
||||
return True
|
||||
|
||||
def create(self, cr, uid, values, context=None):
|
||||
# overridden to automatically invite user to sign up
|
||||
|
|
|
@ -17,13 +17,25 @@
|
|||
<header>
|
||||
<field name="state" widget="statusbar"/>
|
||||
</header>
|
||||
<div class="oe_form_box_info oe_text_center" attrs="{'invisible': [('signup_valid', '!=', True)]}">
|
||||
<p attrs="{'invisible': [('state', '!=', 'active')]}">
|
||||
<b>A password reset has been requested for this user. An email containing the following link has been sent:</b>
|
||||
</p>
|
||||
<p attrs="{'invisible': [('state', '!=', 'new')]}">
|
||||
<b>An invitation email containing the following subscription link has been sent:</b>
|
||||
</p>
|
||||
<p><field class="oe_inline" name="signup_url" widget="url"/></p>
|
||||
<field name="signup_valid" invisible="1"/>
|
||||
</div>
|
||||
</xpath>
|
||||
<!-- add Reset Password button -->
|
||||
<xpath expr="//div[@class='oe_right oe_button_box']" position="replace">
|
||||
<div class="oe_right oe_button_box">
|
||||
<xpath expr="//div[@class='oe_right oe_button_box']//button" position="replace">
|
||||
<button string="Send Reset Password Instructions"
|
||||
type="object" name="action_reset_password" />
|
||||
</div>
|
||||
type="object" name="action_reset_password"
|
||||
attrs="{'invisible': [('state', '!=', 'active')]}"/>
|
||||
<button string="Send an Invitation Email"
|
||||
type="object" name="action_reset_password" context="{'create_user': 1}"
|
||||
attrs="{'invisible': [('state', '!=', 'new')]}"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
|
|
@ -5,32 +5,33 @@ openerp.auth_signup = function(instance) {
|
|||
instance.web.Login.include({
|
||||
start: function() {
|
||||
var self = this;
|
||||
var d = this._super();
|
||||
d.done(function() {
|
||||
self.$(".oe_signup_show").hide();
|
||||
this.signup_enabled = false;
|
||||
this.reset_password_enabled = false;
|
||||
return this._super().then(function() {
|
||||
|
||||
// Switches the login box to the select mode whith mode == [default|signup|reset]
|
||||
self.on('change:login_mode', self, function() {
|
||||
var mode = self.get('login_mode') || 'default';
|
||||
self.$('*[data-modes]').each(function() {
|
||||
var modes = $(this).data('modes').split(/\s+/);
|
||||
$(this).toggle(modes.indexOf(mode) > -1);
|
||||
});
|
||||
self.$('a.oe_signup_signup:visible').toggle(self.signup_enabled);
|
||||
self.$('a.oe_signup_reset_password:visible').toggle(self.reset_password_enabled);
|
||||
});
|
||||
|
||||
// to switch between the signup and regular login form
|
||||
self.$('a.oe_signup_signup').click(function(ev) {
|
||||
if (ev) {
|
||||
ev.preventDefault();
|
||||
}
|
||||
self.$el.addClass("oe_login_signup");
|
||||
self.$(".oe_signup_show").show();
|
||||
self.$(".oe_signup_hide").hide();
|
||||
self.set('login_mode', 'signup');
|
||||
return false;
|
||||
});
|
||||
self.$('a.oe_signup_back').click(function(ev) {
|
||||
if (ev) {
|
||||
ev.preventDefault();
|
||||
}
|
||||
self.$el.removeClass("oe_login_signup");
|
||||
self.$(".oe_signup_show").hide();
|
||||
self.$(".oe_signup_hide").show();
|
||||
self.set('login_mode', 'default');
|
||||
delete self.params.token;
|
||||
return false;
|
||||
});
|
||||
|
||||
var dblist = self.db_list || [];
|
||||
var dbname = self.params.db || (dblist.length === 1 ? dblist[0] : null);
|
||||
var dbname = self.selected_db;
|
||||
|
||||
// if there is an error message in params, show it then forget it
|
||||
if (self.params.error_message) {
|
||||
|
@ -42,7 +43,7 @@ openerp.auth_signup = function(instance) {
|
|||
if (dbname && self.params.token) {
|
||||
self.rpc("/auth_signup/retrieve", {dbname: dbname, token: self.params.token})
|
||||
.done(self.on_token_loaded)
|
||||
.fail(self.on_token_failed)
|
||||
.fail(self.on_token_failed);
|
||||
}
|
||||
if (dbname && self.params.login) {
|
||||
self.$("form input[name=login]").val(self.params.login);
|
||||
|
@ -51,23 +52,21 @@ openerp.auth_signup = function(instance) {
|
|||
// bind reset password link
|
||||
self.$('a.oe_signup_reset_password').click(self.do_reset_password);
|
||||
|
||||
// make signup link and reset password link visible only when enabled
|
||||
self.$('a.oe_signup_signup').hide();
|
||||
self.$('a.oe_signup_reset_password').hide();
|
||||
if (dbname) {
|
||||
self.rpc("/auth_signup/get_config", {dbname: dbname})
|
||||
.done(function(result) {
|
||||
if (result.signup) {
|
||||
self.$('a.oe_signup_signup').show();
|
||||
}
|
||||
if (result.reset_password) {
|
||||
self.$('a.oe_signup_reset_password').show();
|
||||
}
|
||||
});
|
||||
self.rpc("/auth_signup/get_config", {dbname: dbname}).done(function(result) {
|
||||
self.signup_enabled = result.signup;
|
||||
self.reset_password_enabled = result.reset_password;
|
||||
if (!self.signup_enabled || self.$("form input[name=login]").val()){
|
||||
self.set('login_mode', 'default');
|
||||
} else {
|
||||
self.set('login_mode', 'signup');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// TODO: support multiple database mode
|
||||
self.set('login_mode', 'default');
|
||||
}
|
||||
});
|
||||
|
||||
return d;
|
||||
},
|
||||
|
||||
on_token_loaded: function(result) {
|
||||
|
@ -76,9 +75,7 @@ openerp.auth_signup = function(instance) {
|
|||
this.on_db_loaded([result.db]);
|
||||
if (result.token) {
|
||||
// switch to signup mode, set user name and login
|
||||
this.$el.addClass("oe_login_signup");
|
||||
self.$(".oe_signup_show").show();
|
||||
self.$(".oe_signup_hide").hide();
|
||||
this.set('login_mode', (this.params.type === 'reset' ? 'reset' : 'signup'));
|
||||
this.$("form input[name=name]").val(result.name).attr("readonly", "readonly");
|
||||
if (result.login) {
|
||||
this.$("form input[name=login]").val(result.login).attr("readonly", "readonly");
|
||||
|
@ -88,6 +85,7 @@ openerp.auth_signup = function(instance) {
|
|||
} else {
|
||||
// remain in login mode, set login if present
|
||||
delete this.params.token;
|
||||
this.set('login_mode', 'default');
|
||||
this.$("form input[name=login]").val(result.login || "");
|
||||
}
|
||||
},
|
||||
|
@ -99,43 +97,52 @@ openerp.auth_signup = function(instance) {
|
|||
this.show_error(_t("Invalid signup token"));
|
||||
delete this.params.db;
|
||||
delete this.params.token;
|
||||
this.set('login_mode', 'default');
|
||||
},
|
||||
|
||||
get_params: function(){
|
||||
// signup user (or reset password)
|
||||
var db = this.$("form [name=db]").val();
|
||||
var name = this.$("form input[name=name]").val();
|
||||
var login = this.$("form input[name=login]").val();
|
||||
var password = this.$("form input[name=password]").val();
|
||||
var confirm_password = this.$("form input[name=confirm_password]").val();
|
||||
if (!db) {
|
||||
this.do_warn(_t("Login"), _t("No database selected !"));
|
||||
return false;
|
||||
} else if (!name) {
|
||||
this.do_warn(_t("Login"), _t("Please enter a name."));
|
||||
return false;
|
||||
} else if (!login) {
|
||||
this.do_warn(_t("Login"), _t("Please enter a username."));
|
||||
return false;
|
||||
} else if (!password || !confirm_password) {
|
||||
this.do_warn(_t("Login"), _t("Please enter a password and confirm it."));
|
||||
return false;
|
||||
} else if (password !== confirm_password) {
|
||||
this.do_warn(_t("Login"), _t("Passwords do not match; please retype them."));
|
||||
return false;
|
||||
}
|
||||
var params = {
|
||||
dbname : db,
|
||||
token: this.params.token || "",
|
||||
name: name,
|
||||
login: login,
|
||||
password: password,
|
||||
};
|
||||
return params;
|
||||
},
|
||||
|
||||
on_submit: function(ev) {
|
||||
if (ev) {
|
||||
ev.preventDefault();
|
||||
}
|
||||
if (this.$el.hasClass("oe_login_signup")) {
|
||||
// signup user (or reset password)
|
||||
var db = this.$("form [name=db]").val();
|
||||
var name = this.$("form input[name=name]").val();
|
||||
var login = this.$("form input[name=login]").val();
|
||||
var password = this.$("form input[name=password]").val();
|
||||
var confirm_password = this.$("form input[name=confirm_password]").val();
|
||||
if (!db) {
|
||||
this.do_warn(_t("Login"), _t("No database selected !"));
|
||||
return false;
|
||||
} else if (!name) {
|
||||
this.do_warn(_t("Login"), _t("Please enter a name."));
|
||||
return false;
|
||||
} else if (!login) {
|
||||
this.do_warn(_t("Login"), _t("Please enter a username."));
|
||||
return false;
|
||||
} else if (!password || !confirm_password) {
|
||||
this.do_warn(_t("Login"), _t("Please enter a password and confirm it."));
|
||||
return false;
|
||||
} else if (password !== confirm_password) {
|
||||
this.do_warn(_t("Login"), _t("Passwords do not match; please retype them."));
|
||||
var login_mode = this.get('login_mode');
|
||||
if (login_mode === 'signup' || login_mode === 'reset') {
|
||||
var params = this.get_params();
|
||||
if (_.isEmpty(params)){
|
||||
return false;
|
||||
}
|
||||
var params = {
|
||||
dbname : db,
|
||||
token: this.params.token || "",
|
||||
name: name,
|
||||
login: login,
|
||||
password: password,
|
||||
};
|
||||
|
||||
var self = this,
|
||||
super_ = this._super;
|
||||
this.rpc('/auth_signup/signup', params)
|
||||
|
@ -156,21 +163,23 @@ openerp.auth_signup = function(instance) {
|
|||
if (ev) {
|
||||
ev.preventDefault();
|
||||
}
|
||||
var self = this;
|
||||
var db = this.$("form [name=db]").val();
|
||||
var login = this.$("form input[name=login]").val();
|
||||
if (!db) {
|
||||
this.do_warn(_t("Login"), _t("No database selected !"));
|
||||
return false;
|
||||
return $.Deferred().reject();
|
||||
} else if (!login) {
|
||||
this.do_warn(_t("Login"), _t("Please enter a username or email address."))
|
||||
return false;
|
||||
this.do_warn(_t("Login"), _t("Please enter a username or email address."));
|
||||
return $.Deferred().reject();
|
||||
}
|
||||
var params = {
|
||||
dbname : db,
|
||||
login: login,
|
||||
};
|
||||
var url = "/auth_signup/reset_password?" + $.param(params);
|
||||
window.location = url;
|
||||
return self.rpc("/auth_signup/reset_password", { dbname: db, login: login }).done(function(result) {
|
||||
self.show_error(_t("An email has been sent with credentials to reset your password"));
|
||||
self.set('login_mode', 'default');
|
||||
}).fail(function(result, ev) {
|
||||
ev.preventDefault();
|
||||
self.show_error(result.message);
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
|
|
@ -5,26 +5,35 @@
|
|||
|
||||
<t t-extend="Login">
|
||||
<t t-jquery="form ul:first li:contains('Username')" t-operation="before">
|
||||
<li class="oe_signup_show">Name</li>
|
||||
<li class="oe_signup_show"><input name="name" type="text"/></li>
|
||||
<li data-modes="signup reset">Name</li>
|
||||
<li data-modes="signup reset"><input name="name" type="text"/></li>
|
||||
</t>
|
||||
<t t-jquery="form ul:first li:contains('Username')" t-operation="replace">
|
||||
<li class="oe_signup_hide">Username</li>
|
||||
<li class="oe_signup_show">Username (Email)</li>
|
||||
<li data-modes="default">Username</li>
|
||||
<li data-modes="signup reset">Username (Email)</li>
|
||||
</t>
|
||||
<t t-jquery="form ul:first li:has(input[name=login], input[name=password]), form ul:first li:contains('Password')">
|
||||
this.attr('data-modes', 'default signup reset');
|
||||
</t>
|
||||
<t t-jquery="form ul:first li:has(input[name=password])" t-operation="after">
|
||||
<li class="oe_signup_show">Confirm Password</li>
|
||||
<li class="oe_signup_show"><input name="confirm_password" type="password"/></li>
|
||||
<li data-modes="signup reset">Confirm Password</li>
|
||||
<li data-modes="signup reset"><input name="confirm_password" type="password"/></li>
|
||||
</t>
|
||||
<t t-jquery="form ul:first li:has(button[name=submit])" t-operation="replace">
|
||||
<li>
|
||||
<button class="oe_signup_hide" name="submit">Log in</button>
|
||||
<button class="oe_signup_show" name="submit">Sign up</button>
|
||||
<a class="oe_signup_hide oe_signup_signup" href="#">Sign Up</a>
|
||||
<a class="oe_signup_show oe_signup_back" href="#">Back to Login</a>
|
||||
<a class="oe_signup_reset_password" href="#">Reset password</a>
|
||||
<button name="submit">
|
||||
<span data-modes="default">Log in</span>
|
||||
<span data-modes="signup">Sign Up</span>
|
||||
<span data-modes="reset">Reset password</span>
|
||||
</button>
|
||||
<a class="oe_signup_signup" data-modes="default" href="#">Sign Up</a>
|
||||
<a class="oe_signup_back" data-modes="signup reset" href="#">Back to Login</a>
|
||||
<a class="oe_signup_reset_password" data-modes="default" href="#">Reset password</a>
|
||||
</li>
|
||||
</t>
|
||||
<t t-jquery=".oe_login_manage_db">
|
||||
this.attr('data-modes', 'default');
|
||||
</t>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
|
|
|
@ -214,6 +214,13 @@ class base_action_rule(osv.osv):
|
|||
self._register_hook(cr, ids)
|
||||
return True
|
||||
|
||||
def onchange_model_id(self, cr, uid, ids, model_id, context=None):
|
||||
data = {'model': False, 'filter_pre_id': False, 'filter_id': False}
|
||||
if model_id:
|
||||
model = self.pool.get('ir.model').browse(cr, uid, model_id, context=context)
|
||||
data.update({'model': model.model})
|
||||
return {'value': data}
|
||||
|
||||
def _check(self, cr, uid, automatic=False, use_new_cursor=False, context=None):
|
||||
""" This Function is called by scheduler. """
|
||||
context = context or {}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<h1><field name="name"/></h1>
|
||||
<group>
|
||||
<group>
|
||||
<field name="model_id"/>
|
||||
<field name="model_id" on_change="onchange_model_id(model_id, context)"/>
|
||||
<field name="model" invisible="1"/>
|
||||
</group>
|
||||
<group>
|
||||
|
|
|
@ -70,7 +70,7 @@
|
|||
<header>
|
||||
<button name="do_confirm" string="Confirm" states="tentative,cancelled" type="object" class="oe_highlight"/>
|
||||
<button name="do_tentative" states="confirmed,cancelled" string="Uncertain" type="object" class="oe_highlight"/>
|
||||
<button name="do_cancel" string="Cancel" states="tentative,confirmed" type="object"/>
|
||||
<button name="do_cancel" string="Cancel Event" states="tentative,confirmed" type="object"/>
|
||||
<field name="state" widget="statusbar"
|
||||
statusbar_visible="tentative,confirmed" statusbar_colors='{"proforma":"blue"}'/>
|
||||
</header>
|
||||
|
|
|
@ -19,10 +19,11 @@
|
|||
#
|
||||
##############################################################################
|
||||
|
||||
import base64
|
||||
import openerp.modules.registry
|
||||
from openerp.osv import osv
|
||||
from openerp_sxw2rml import sxw2rml
|
||||
from StringIO import StringIO
|
||||
import base64
|
||||
from openerp import pooler
|
||||
from openerp import addons
|
||||
|
||||
|
@ -55,7 +56,12 @@ class report_xml(osv.osv):
|
|||
'report_sxw_content': base64.decodestring(file_sxw),
|
||||
'report_rml_content': str(sxw2rml(sxwval, xsl=fp.read())),
|
||||
})
|
||||
|
||||
# FIXME: this should be moved to an override of the ir.actions.report_xml.create() method
|
||||
cr.commit()
|
||||
pool.get('ir.actions.report.xml').register_all(cr)
|
||||
openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname)
|
||||
|
||||
return True
|
||||
|
||||
def report_get(self, cr, uid, report_id, context=None):
|
||||
|
|
Binary file not shown.
|
@ -92,9 +92,9 @@ class ExportToRML( unohelper.Base, XJobExecutor ):
|
|||
if docinfo.getUserFieldValue(2) == "":
|
||||
ErrorDialog("Please Save this file on server","Use Send To Server Option in OpenERP Report Menu","Error")
|
||||
exit(1)
|
||||
filename = self.GetAFileName()
|
||||
if not filename:
|
||||
exit(1)
|
||||
filename = self.GetAFileName()
|
||||
if not filename:
|
||||
exit(1)
|
||||
global passwd
|
||||
self.password = passwd
|
||||
try:
|
||||
|
@ -118,7 +118,7 @@ class ExportToRML( unohelper.Base, XJobExecutor ):
|
|||
initPath = tempfile.gettempdir()
|
||||
oUcb = createUnoService("com.sun.star.ucb.SimpleFileAccess")
|
||||
if oUcb.exists(initPath):
|
||||
oFileDialog.setDisplayDirectory('file://' + ( os.name == 'nt' and '/' or '' ) + initPath )
|
||||
oFileDialog.setDisplayDirectory('file://' + ( os.name == 'nt' and '/' or '' ) + initPath )
|
||||
|
||||
oFileDialog.setDefaultName(f_path )
|
||||
|
||||
|
|
|
@ -209,13 +209,13 @@ class Fields(unohelper.Base, XJobExecutor ):
|
|||
key.sort()
|
||||
myval=None
|
||||
if not sVar.find("/")==-1:
|
||||
myval=sVar[:sVar.find("/")]
|
||||
myval=sVar[:sVar.find("/")]
|
||||
else:
|
||||
myval=sVar
|
||||
if myval in key:
|
||||
if (res[myval]['type'] in ['many2one']):
|
||||
sObject = res[myval]['relation']
|
||||
return self.getRes(sock,res[myval]['relation'], sVar[sVar.find("/")+1:])
|
||||
return self.getRes(sock,res[myval]['relation'], sVar[sVar.find("/")+1:])
|
||||
else:
|
||||
return sObject
|
||||
|
||||
|
|
|
@ -166,33 +166,33 @@ class RepeatIn( unohelper.Base, XJobExecutor ):
|
|||
self.sValue= "objects"
|
||||
else:
|
||||
sItem=""
|
||||
for anObject in self.aObjectList:
|
||||
if anObject[:anObject.find("(")] == sObject:
|
||||
sItem = anObject
|
||||
self.insVariable.setText( sItem )
|
||||
for anObject in self.aObjectList:
|
||||
if anObject[:anObject.find("(")] == sObject:
|
||||
sItem = anObject
|
||||
self.insVariable.setText( sItem )
|
||||
|
||||
genTree(
|
||||
sItem[sItem.find("(")+1:sItem.find(")")],
|
||||
self.aListRepeatIn,
|
||||
self.insField,
|
||||
self.sMyHost,
|
||||
2,
|
||||
ending=['one2many','many2many'],
|
||||
recur=['one2many','many2many']
|
||||
)
|
||||
genTree(
|
||||
sItem[sItem.find("(")+1:sItem.find(")")],
|
||||
self.aListRepeatIn,
|
||||
self.insField,
|
||||
self.sMyHost,
|
||||
2,
|
||||
ending=['one2many','many2many'],
|
||||
recur=['one2many','many2many']
|
||||
)
|
||||
|
||||
self.sValue= self.win.getListBoxItem("lstFields",self.aListRepeatIn.index(sFields))
|
||||
|
||||
for var in self.aVariableList:
|
||||
|
||||
if var[:8] <> 'List of ':
|
||||
self.model_ids = self.sock.execute(database, uid, self.password, 'ir.model' , 'search', [('model','=',var[var.find("(")+1:var.find(")")])])
|
||||
if var[:8] <> 'List of ':
|
||||
self.model_ids = self.sock.execute(database, uid, self.password, 'ir.model' , 'search', [('model','=',var[var.find("(")+1:var.find(")")])])
|
||||
else:
|
||||
self.model_ids = self.sock.execute(database, uid, self.password, 'ir.model' , 'search', [('model','=',var[8:])])
|
||||
self.model_ids = self.sock.execute(database, uid, self.password, 'ir.model' , 'search', [('model','=',var[8:])])
|
||||
fields=['name','model']
|
||||
self.model_res = self.sock.execute(database, uid, self.password, 'ir.model', 'read', self.model_ids,fields)
|
||||
if self.model_res <> []:
|
||||
if var[:8]<>'List of ':
|
||||
if var[:8]<>'List of ':
|
||||
self.insVariable.addItem(var[:var.find("(")+1] + self.model_res[0]['name'] + ")" ,self.insVariable.getItemCount())
|
||||
else:
|
||||
self.insVariable.addItem('List of ' + self.model_res[0]['name'] ,self.insVariable.getItemCount())
|
||||
|
@ -212,8 +212,8 @@ class RepeatIn( unohelper.Base, XJobExecutor ):
|
|||
self.win.setEditText("txtName", self.sGVariable)
|
||||
self.win.setEditText("txtUName",self.sGDisplayName)
|
||||
else:
|
||||
self.win.setEditText("txtName",sMain[sMain.rfind("/")+1:])
|
||||
self.win.setEditText("txtUName","|-."+sItem[sItem.rfind("/")+1:]+".-|")
|
||||
self.win.setEditText("txtName",sMain[sMain.rfind("/")+1:])
|
||||
self.win.setEditText("txtUName","|-."+sItem[sItem.rfind("/")+1:]+".-|")
|
||||
|
||||
def cmbVariable_selected(self, oItemEvent):
|
||||
|
||||
|
@ -225,15 +225,15 @@ class RepeatIn( unohelper.Base, XJobExecutor ):
|
|||
self.win.removeListBoxItems("lstFields", 0, self.win.getListBoxItemCount("lstFields"))
|
||||
sItem=self.win.getComboBoxText("cmbVariable")
|
||||
for var in self.aVariableList:
|
||||
if var[:8]=='List of ':
|
||||
if var[:8]==sItem[:8]:
|
||||
if var[:8]=='List of ':
|
||||
if var[:8]==sItem[:8]:
|
||||
sItem = var
|
||||
elif var[:var.find("(")+1] == sItem[:sItem.find("(")+1]:
|
||||
elif var[:var.find("(")+1] == sItem[:sItem.find("(")+1]:
|
||||
sItem = var
|
||||
self.aListRepeatIn=[]
|
||||
|
||||
data = ( sItem[sItem.rfind(" ") + 1:] == docinfo.getUserFieldValue(3) ) and docinfo.getUserFieldValue(3) or sItem[sItem.find("(")+1:sItem.find(")")]
|
||||
genTree( data, self.aListRepeatIn, self.insField, self.sMyHost, 2, ending=['one2many','many2many'], recur=['one2many','many2many'] )
|
||||
data = ( sItem[sItem.rfind(" ") + 1:] == docinfo.getUserFieldValue(3) ) and docinfo.getUserFieldValue(3) or sItem[sItem.find("(")+1:sItem.find(")")]
|
||||
genTree( data, self.aListRepeatIn, self.insField, self.sMyHost, 2, ending=['one2many','many2many'], recur=['one2many','many2many'] )
|
||||
|
||||
self.win.selectListBoxItemPos("lstFields", 0, True )
|
||||
|
||||
|
|
|
@ -123,6 +123,7 @@ class SendtoServer(unohelper.Base, XJobExecutor):
|
|||
self.win.addFixedText("lblReportName", 2, 30, 50, 15, "Technical Name :")
|
||||
self.win.addEdit("txtReportName", -5, 25, 123, 15,report_name)
|
||||
self.win.addCheckBox("chkHeader", 51, 45, 70 ,15, "Corporate Header")
|
||||
self.win.setCheckBoxState("chkHeader", True)
|
||||
self.win.addFixedText("lblResourceType", 2 , 60, 50, 15, "Select Rpt. Type :")
|
||||
self.win.addComboListBox("lstResourceType", -5, 58, 123, 15,True,itemListenerProc=self.lstbox_selected)
|
||||
self.lstResourceType = self.win.getControl( "lstResourceType" )
|
||||
|
@ -190,7 +191,6 @@ class SendtoServer(unohelper.Base, XJobExecutor):
|
|||
#sock = xmlrpclib.ServerProxy(docinfo.getUserFieldValue(0) +'/xmlrpc/object')
|
||||
|
||||
file_type = oDoc2.getURL()[7:].split(".")[-1]
|
||||
res = self.sock.execute(database, uid, self.password, 'ir.actions.report.xml', 'upload_report', int(docinfo.getUserFieldValue(2)),base64.encodestring(data),file_type,{})
|
||||
params = {
|
||||
'name': self.win.getEditText("txtName"),
|
||||
'model': docinfo.getUserFieldValue(3),
|
||||
|
@ -200,7 +200,12 @@ class SendtoServer(unohelper.Base, XJobExecutor):
|
|||
}
|
||||
if self.win.getListBoxSelectedItem("lstResourceType")=='OpenOffice':
|
||||
params['report_type']=file_type
|
||||
res = self.sock.execute(database, uid, self.password, 'ir.actions.report.xml', 'write', int(docinfo.getUserFieldValue(2)), params)
|
||||
self.sock.execute(database, uid, self.password, 'ir.actions.report.xml', 'write', int(docinfo.getUserFieldValue(2)), params)
|
||||
|
||||
# Call upload_report as the *last* step, as it will call register_all() and cause the report service
|
||||
# to be loaded - which requires all the data to be correct in the database
|
||||
self.sock.execute(database, uid, self.password, 'ir.actions.report.xml', 'upload_report', int(docinfo.getUserFieldValue(2)),base64.encodestring(data),file_type,{})
|
||||
|
||||
self.logobj.log_write('SendToServer',LOG_INFO, ':Report %s successfully send using %s'%(params['name'],database))
|
||||
self.win.endExecute()
|
||||
else:
|
||||
|
|
|
@ -112,43 +112,43 @@ class AddLang(unohelper.Base, XJobExecutor ):
|
|||
text=cursor.getText()
|
||||
tcur=text.createTextCursorByRange(cursor)
|
||||
|
||||
self.aVariableList.extend( filter( lambda obj: obj[:obj.find("(")] == "Objects", self.aObjectList ) )
|
||||
self.aVariableList.extend( filter( lambda obj: obj[:obj.find("(")] == "Objects", self.aObjectList ) )
|
||||
|
||||
for i in range(len(self.aItemList)):
|
||||
anItem = self.aItemList[i][1]
|
||||
component = self.aComponentAdd[i]
|
||||
anItem = self.aItemList[i][1]
|
||||
component = self.aComponentAdd[i]
|
||||
|
||||
if component == "Document":
|
||||
sLVal = anItem[anItem.find(",'") + 2:anItem.find("')")]
|
||||
self.aVariableList.extend( filter( lambda obj: obj[:obj.find("(")] == sLVal, self.aObjectList ) )
|
||||
sLVal = anItem[anItem.find(",'") + 2:anItem.find("')")]
|
||||
self.aVariableList.extend( filter( lambda obj: obj[:obj.find("(")] == sLVal, self.aObjectList ) )
|
||||
|
||||
if tcur.TextSection:
|
||||
getRecersiveSection(tcur.TextSection,self.aSectionList)
|
||||
if component in self.aSectionList:
|
||||
sLVal = anItem[anItem.find(",'") + 2:anItem.find("')")]
|
||||
self.aVariableList.extend( filter( lambda obj: obj[:obj.find("(")] == sLVal, self.aObjectList ) )
|
||||
sLVal = anItem[anItem.find(",'") + 2:anItem.find("')")]
|
||||
self.aVariableList.extend( filter( lambda obj: obj[:obj.find("(")] == sLVal, self.aObjectList ) )
|
||||
|
||||
if tcur.TextTable:
|
||||
if not component == "Document" and component[component.rfind(".") + 1:] == tcur.TextTable.Name:
|
||||
if not component == "Document" and component[component.rfind(".") + 1:] == tcur.TextTable.Name:
|
||||
VariableScope(tcur,self.insVariable,self.aObjectList,self.aComponentAdd,self.aItemList,component)
|
||||
|
||||
self.bModify=bFromModify
|
||||
if self.bModify==True:
|
||||
sItem=""
|
||||
for anObject in self.aObjectList:
|
||||
if anObject[:anObject.find("(")] == sVariable:
|
||||
sItem = anObject
|
||||
self.insVariable.setText( sItem )
|
||||
genTree(sItem[sItem.find("(")+1:sItem.find(")")],self.aListFields, self.insField,self.sMyHost,2,ending_excl=['one2many','many2one','many2many','reference'], recur=['many2one'])
|
||||
for anObject in self.aObjectList:
|
||||
if anObject[:anObject.find("(")] == sVariable:
|
||||
sItem = anObject
|
||||
self.insVariable.setText( sItem )
|
||||
genTree(sItem[sItem.find("(")+1:sItem.find(")")],self.aListFields, self.insField,self.sMyHost,2,ending_excl=['one2many','many2one','many2many','reference'], recur=['many2one'])
|
||||
self.sValue= self.win.getListBoxItem("lstFields",self.aListFields.index(sFields))
|
||||
|
||||
for var in self.aVariableList:
|
||||
|
||||
self.model_ids = self.sock.execute(database, uid, self.password, 'ir.model' , 'search', [('model','=',var[var.find("(")+1:var.find(")")])])
|
||||
self.model_ids = self.sock.execute(database, uid, self.password, 'ir.model' , 'search', [('model','=',var[var.find("(")+1:var.find(")")])])
|
||||
fields=['name','model']
|
||||
self.model_res = self.sock.execute(database, uid, self.password, 'ir.model', 'read', self.model_ids,fields)
|
||||
if self.model_res <> []:
|
||||
self.insVariable.addItem(var[:var.find("(")+1] + self.model_res[0]['name'] + ")" ,self.insVariable.getItemCount())
|
||||
self.insVariable.addItem(var[:var.find("(")+1] + self.model_res[0]['name'] + ")" ,self.insVariable.getItemCount())
|
||||
else:
|
||||
self.insVariable.addItem(var ,self.insVariable.getItemCount())
|
||||
|
||||
|
@ -165,15 +165,15 @@ class AddLang(unohelper.Base, XJobExecutor ):
|
|||
docinfo=doc.getDocumentInfo()
|
||||
sItem= self.win.getComboBoxText("cmbVariable")
|
||||
for var in self.aVariableList:
|
||||
if var[:var.find("(")+1]==sItem[:sItem.find("(")+1]:
|
||||
if var[:var.find("(")+1]==sItem[:sItem.find("(")+1]:
|
||||
sItem = var
|
||||
sMain=self.aListFields[self.win.getListBoxSelectedItemPos("lstFields")]
|
||||
t=sMain.rfind('/lang')
|
||||
if t!=-1:
|
||||
sObject=self.getRes(self.sock,sItem[sItem.find("(")+1:-1],sMain[1:])
|
||||
sObject=self.getRes(self.sock,sItem[sItem.find("(")+1:-1],sMain[1:])
|
||||
ids = self.sock.execute(database, uid, self.password, sObject , 'search', [])
|
||||
res = self.sock.execute(database, uid, self.password, sObject , 'read',[ids[0]])
|
||||
self.win.setEditText("txtUName",res[0][sMain[sMain.rfind("/")+1:]])
|
||||
self.win.setEditText("txtUName",res[0][sMain[sMain.rfind("/")+1:]])
|
||||
else:
|
||||
ErrorDialog("Please select a language.")
|
||||
|
||||
|
@ -192,13 +192,13 @@ class AddLang(unohelper.Base, XJobExecutor ):
|
|||
key.sort()
|
||||
myval=None
|
||||
if not sVar.find("/")==-1:
|
||||
myval=sVar[:sVar.find("/")]
|
||||
myval=sVar[:sVar.find("/")]
|
||||
else:
|
||||
myval=sVar
|
||||
if myval in key:
|
||||
if (res[myval]['type'] in ['many2one']):
|
||||
sObject = res[myval]['relation']
|
||||
return self.getRes(sock,res[myval]['relation'], sVar[sVar.find("/")+1:])
|
||||
return self.getRes(sock,res[myval]['relation'], sVar[sVar.find("/")+1:])
|
||||
else:
|
||||
return sObject
|
||||
|
||||
|
@ -213,18 +213,18 @@ class AddLang(unohelper.Base, XJobExecutor ):
|
|||
self.aListFields=[]
|
||||
tempItem = self.win.getComboBoxText("cmbVariable")
|
||||
for var in self.aVariableList:
|
||||
if var[:var.find("(")] == tempItem[:tempItem.find("(")]:
|
||||
if var[:var.find("(")] == tempItem[:tempItem.find("(")]:
|
||||
sItem=var
|
||||
|
||||
genTree(
|
||||
sItem[ sItem.find("(") + 1:sItem.find(")")],
|
||||
self.aListFields,
|
||||
self.insField,
|
||||
self.sMyHost,
|
||||
2,
|
||||
ending_excl=['one2many','many2one','many2many','reference'],
|
||||
recur=['many2one']
|
||||
)
|
||||
genTree(
|
||||
sItem[ sItem.find("(") + 1:sItem.find(")")],
|
||||
self.aListFields,
|
||||
self.insField,
|
||||
self.sMyHost,
|
||||
2,
|
||||
ending_excl=['one2many','many2one','many2many','reference'],
|
||||
recur=['many2one']
|
||||
)
|
||||
|
||||
except:
|
||||
import traceback;traceback.print_exc()
|
||||
|
|
|
@ -87,19 +87,19 @@ def genTree(object, aList, insField, host, level=3, ending=None, ending_excl=Non
|
|||
|
||||
def VariableScope(oTcur, insVariable, aObjectList, aComponentAdd, aItemList, sTableName=""):
|
||||
if sTableName.find(".") != -1:
|
||||
for i in range(len(aItemList)):
|
||||
for i in range(len(aItemList)):
|
||||
if aComponentAdd[i]==sTableName:
|
||||
sLVal=aItemList[i][1][aItemList[i][1].find(",'")+2:aItemList[i][1].find("')")]
|
||||
sLVal=aItemList[i][1][aItemList[i][1].find(",'")+2:aItemList[i][1].find("')")]
|
||||
for j in range(len(aObjectList)):
|
||||
if aObjectList[j][:aObjectList[j].find("(")] == sLVal:
|
||||
if aObjectList[j][:aObjectList[j].find("(")] == sLVal:
|
||||
insVariable.append(aObjectList[j])
|
||||
VariableScope(oTcur,insVariable,aObjectList,aComponentAdd,aItemList, sTableName[:sTableName.rfind(".")])
|
||||
VariableScope(oTcur,insVariable,aObjectList,aComponentAdd,aItemList, sTableName[:sTableName.rfind(".")])
|
||||
else:
|
||||
for i in range(len(aItemList)):
|
||||
for i in range(len(aItemList)):
|
||||
if aComponentAdd[i]==sTableName:
|
||||
sLVal=aItemList[i][1][aItemList[i][1].find(",'")+2:aItemList[i][1].find("')")]
|
||||
sLVal=aItemList[i][1][aItemList[i][1].find(",'")+2:aItemList[i][1].find("')")]
|
||||
for j in range(len(aObjectList)):
|
||||
if aObjectList[j][:aObjectList[j].find("(")] == sLVal and sLVal!="":
|
||||
if aObjectList[j][:aObjectList[j].find("(")] == sLVal and sLVal!="":
|
||||
insVariable.append(aObjectList[j])
|
||||
|
||||
def getList(aObjectList, host, count):
|
||||
|
@ -145,8 +145,8 @@ def getRelation(sRelName, sItem, sObjName, aObjectList, host):
|
|||
if k == sItem:
|
||||
aObjectList.append(sObjName + "(" + res[k]['relation'] + ")")
|
||||
return 0
|
||||
if k == sItem[:sItem.find(".")]:
|
||||
getRelation(res[k]['relation'], sItem[sItem.find(".")+1:], sObjName,aObjectList,host)
|
||||
if k == sItem[:sItem.find(".")]:
|
||||
getRelation(res[k]['relation'], sItem[sItem.find(".")+1:], sObjName,aObjectList,host)
|
||||
|
||||
|
||||
def getPath(sPath, sMain):
|
||||
|
@ -157,13 +157,13 @@ def getPath(sPath, sMain):
|
|||
oPar = oParEnum.nextElement()
|
||||
if oPar.supportsService("com.sun.star.text.TextField.DropDown"):
|
||||
sItem=oPar.Items[1]
|
||||
if sPath[:sPath.find(".")] == sMain:
|
||||
if sPath[:sPath.find(".")] == sMain:
|
||||
break;
|
||||
else:
|
||||
res = re.findall('\\[\\[ *([a-zA-Z0-9_\.]+) *\\]\\]',sPath)
|
||||
if len(res) <> 0:
|
||||
if sItem[sItem.find(",'")+2:sItem.find("')")] == sPath[:sPath.find(".")]:
|
||||
sPath = sItem[sItem.find("(")+1:sItem.find(",")] + sPath[sPath.find("."):]
|
||||
if sItem[sItem.find(",'")+2:sItem.find("')")] == sPath[:sPath.find(".")]:
|
||||
sPath = sItem[sItem.find("(")+1:sItem.find(",")] + sPath[sPath.find("."):]
|
||||
getPath(sPath, sMain)
|
||||
return sPath
|
||||
|
||||
|
|
|
@ -22,21 +22,21 @@
|
|||
import urllib
|
||||
|
||||
def get_absolute_file_path(url):
|
||||
url_unquoted = urllib.unquote(url)
|
||||
return os.name == 'nt' and url_unquoted[1:] or url_unquoted
|
||||
url_unquoted = urllib.unquote(url)
|
||||
return os.name == 'nt' and url_unquoted[1:] or url_unquoted
|
||||
|
||||
# This function reads the content of a file and return it to the caller
|
||||
def read_data_from_file(filename):
|
||||
fp = file( filename, "rb" )
|
||||
data = fp.read()
|
||||
fp.close()
|
||||
return data
|
||||
fp = file( filename, "rb" )
|
||||
data = fp.read()
|
||||
fp.close()
|
||||
return data
|
||||
|
||||
# This function writes the content to a file
|
||||
def write_data_to_file(filename, data):
|
||||
fp = file( filename, 'wb' )
|
||||
fp.write( data )
|
||||
fp.close()
|
||||
fp = file( filename, 'wb' )
|
||||
fp.write( data )
|
||||
fp.close()
|
||||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -75,42 +75,42 @@ class modify(unohelper.Base, XJobExecutor ):
|
|||
self.sMyHost= docinfo.getUserFieldValue(0)
|
||||
else:
|
||||
ErrorDialog(
|
||||
"Please insert user define field Field-1",
|
||||
"Just go to File->Properties->User Define \n"
|
||||
"Field-1 E.g. http://localhost:8069"
|
||||
)
|
||||
"Please insert user define field Field-1",
|
||||
"Just go to File->Properties->User Define \n"
|
||||
"Field-1 E.g. http://localhost:8069"
|
||||
)
|
||||
exit(1)
|
||||
|
||||
# Check weather Field-4 is available or not otherwise exit from application
|
||||
if not docinfo.getUserFieldValue(3) == "" and not docinfo.getUserFieldValue(0)=="":
|
||||
if self.oVC.TextField:
|
||||
self.oCurObj=self.oVC.TextField
|
||||
item = self.oCurObj.Items[0]
|
||||
item = self.oCurObj.Items[0]
|
||||
|
||||
kind, group1, group2 = self.getOperation(self.oCurObj.Items[1] )
|
||||
kind, group1, group2 = self.getOperation(self.oCurObj.Items[1] )
|
||||
|
||||
start_group1 = group1[:group1.find(".")]
|
||||
stop_group1 = group1[group1.find("."):].replace(".", "/")
|
||||
start_group1 = group1[:group1.find(".")]
|
||||
stop_group1 = group1[group1.find("."):].replace(".", "/")
|
||||
|
||||
if kind == "field":
|
||||
Fields( start_group1, stop_group1, item, True )
|
||||
Fields( start_group1, stop_group1, item, True )
|
||||
elif kind == "expression":
|
||||
Expression( group1, item, True )
|
||||
elif kind == "repeatIn":
|
||||
RepeatIn( start_group1, group2, stop_group1, item, True )
|
||||
RepeatIn( start_group1, group2, stop_group1, item, True )
|
||||
else:
|
||||
ErrorDialog(
|
||||
"Please place your cursor at beginning of field that you want to modify.",""
|
||||
)
|
||||
)
|
||||
|
||||
else:
|
||||
ErrorDialog(
|
||||
"Please insert user define field Field-1 or Field-4",
|
||||
"Just go to File->Properties->User Define \n"
|
||||
"Field-1 E.g. http://localhost:8069 \n"
|
||||
"OR \n"
|
||||
"Field-4 E.g. account.invoice"
|
||||
)
|
||||
"Please insert user define field Field-1 or Field-4",
|
||||
"Just go to File->Properties->User Define \n"
|
||||
"Field-1 E.g. http://localhost:8069 \n"
|
||||
"OR \n"
|
||||
"Field-4 E.g. account.invoice"
|
||||
)
|
||||
exit(1)
|
||||
|
||||
def getOperation(self, str):
|
||||
|
@ -121,14 +121,14 @@ class modify(unohelper.Base, XJobExecutor ):
|
|||
method2 = lambda x: (u'field', x.group(1), None)
|
||||
method3 = lambda x: (u'expression', x.group(1), None)
|
||||
regexes = [
|
||||
('\\[\\[ *repeatIn\\( *(.+)*, *\'([a-zA-Z0-9_]+)\' *\\) *\\]\\]', method1),
|
||||
('\\[\\[ *([a-zA-Z0-9_\.]+) *\\]\\]', method2),
|
||||
('\\[\\[ *(.+) *\\]\\]', method3)
|
||||
('\\[\\[ *repeatIn\\( *(.+)*, *\'([a-zA-Z0-9_]+)\' *\\) *\\]\\]', method1),
|
||||
('\\[\\[ *([a-zA-Z0-9_\.]+) *\\]\\]', method2),
|
||||
('\\[\\[ *(.+) *\\]\\]', method3)
|
||||
]
|
||||
for (rule,method) in regexes:
|
||||
res = re.match(rule, str)
|
||||
if res:
|
||||
return method(res)
|
||||
res = re.match(rule, str)
|
||||
if res:
|
||||
return method(res)
|
||||
|
||||
if __name__<>"package":
|
||||
modify(None)
|
||||
|
|
|
@ -11,23 +11,23 @@ import time
|
|||
sock = xmlrpclib.ServerProxy('http://localhost:8069/xmlrpc/object')
|
||||
|
||||
def get(object, level=3, ending=None, ending_excl=None, recur=None, root=''):
|
||||
if ending is None:
|
||||
ending = []
|
||||
if ending_excl is None:
|
||||
ending_excl = []
|
||||
if recur is None:
|
||||
recur = []
|
||||
res = sock.execute('terp', 3, 'admin', 'account.invoice', 'fields_get')
|
||||
key = res.keys()
|
||||
key.sort()
|
||||
for k in key:
|
||||
if (not ending or res[k]['type'] in ending) and ((not ending_excl) or not (res[k]['type'] in ending_excl)):
|
||||
print root+'/'+k
|
||||
if ending is None:
|
||||
ending = []
|
||||
if ending_excl is None:
|
||||
ending_excl = []
|
||||
if recur is None:
|
||||
recur = []
|
||||
res = sock.execute('terp', 3, 'admin', 'account.invoice', 'fields_get')
|
||||
key = res.keys()
|
||||
key.sort()
|
||||
for k in key:
|
||||
if (not ending or res[k]['type'] in ending) and ((not ending_excl) or not (res[k]['type'] in ending_excl)):
|
||||
print root+'/'+k
|
||||
|
||||
if res[k]['type'] in recur:
|
||||
print root+'/'+k
|
||||
if (res[k]['type'] in recur) and (level>0):
|
||||
get(res[k]['relation'], level-1, ending, ending_excl, recur, root+'/'+k)
|
||||
if res[k]['type'] in recur:
|
||||
print root+'/'+k
|
||||
if (res[k]['type'] in recur) and (level>0):
|
||||
get(res[k]['relation'], level-1, ending, ending_excl, recur, root+'/'+k)
|
||||
|
||||
print 'Field selection for a rields', '='*40
|
||||
get('account.invoice', level=0, ending_excl=['one2many','many2one','many2many','reference'], recur=['many2one'])
|
||||
|
|
|
@ -177,8 +177,10 @@ instance.web.form.DashBoard = instance.web.form.FormWidget.extend({
|
|||
view_mode = action_attrs.view_mode;
|
||||
|
||||
// evaluate action_attrs context and domain
|
||||
action_attrs.context_string = action_attrs.context;
|
||||
action_attrs.context = instance.web.pyeval.eval(
|
||||
'context', action_attrs.context || {});
|
||||
action_attrs.domain_string = action_attrs.domain;
|
||||
action_attrs.domain = instance.web.pyeval.eval(
|
||||
'domain', action_attrs.domain || [], action_attrs.context);
|
||||
if (action_attrs.context['dashboard_merge_domains_contexts'] === false) {
|
||||
|
@ -301,9 +303,9 @@ instance.web.form.DashBoard = instance.web.form.FormWidget.extend({
|
|||
instance.web.form.DashBoardLegacy = instance.web.form.DashBoard.extend({
|
||||
renderElement: function() {
|
||||
if (this.node.tag == 'hpaned') {
|
||||
this.node.attrs.style = '2-1';
|
||||
this.node.attrs.layout = '2-1';
|
||||
} else if (this.node.tag == 'vpaned') {
|
||||
this.node.attrs.style = '1';
|
||||
this.node.attrs.layout = '1';
|
||||
}
|
||||
this.node.tag = 'board';
|
||||
_.each(this.node.children, function(child) {
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<span> Change Layout </span>
|
||||
</button>
|
||||
</div>
|
||||
<table t-att-data-layout="node.attrs.style" t-attf-class="oe_dashboard oe_dashboard_layout_#{node.attrs.style}" cellspacing="0" cellpadding="0" border="0">
|
||||
<table t-att-data-layout="node.attrs.layout" t-attf-class="oe_dashboard oe_dashboard_layout_#{node.attrs.layout}" cellspacing="0" cellpadding="0" border="0">
|
||||
<tr>
|
||||
<td t-foreach="node.children" t-as="column" t-if="column.tag == 'column'"
|
||||
t-att-id="view.element_id + '_column_' + column_index" t-attf-class="oe_dashboard_column index_#{column_index}">
|
||||
|
|
|
@ -72,20 +72,4 @@
|
|||
</record>
|
||||
</data>
|
||||
|
||||
<!-- Mail template is done in a NOUPDATE block
|
||||
so users can freely customize/delete them -->
|
||||
<data noupdate="1">
|
||||
|
||||
<!--Definition of an email template with an empty body that will be used in opportunity mailing. Used to give a
|
||||
basis for email recipients, name and to ease the definition of a further elaborated template. -->
|
||||
<record id="email_template_opportunity_mail" model="email.template">
|
||||
<field name="name">Opportunity - Send Emails</field>
|
||||
<field name="email_from">${object.user_id.email or ''}</field>
|
||||
<field name="subject">Opportunity ${object.name | h})</field>
|
||||
<field name="model_id" ref="crm.model_crm_lead"/>
|
||||
<field name="auto_delete" eval="True"/>
|
||||
<field name="email_recipients">${object.partner_id.id}</field>
|
||||
<field name="body_html"></field>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
|
@ -273,7 +273,9 @@ class crm_lead(base_stage, format_address, osv.osv):
|
|||
'channel_id': fields.many2one('crm.case.channel', 'Channel', help="Communication channel (mail, direct, phone, ...)"),
|
||||
'contact_name': fields.char('Contact Name', size=64),
|
||||
'partner_name': fields.char("Customer Name", size=64,help='The name of the future partner company that will be created while converting the lead into opportunity', select=1),
|
||||
'opt_out': fields.boolean('Opt-Out', oldname='optout', help="If opt-out is checked, this contact has refused to receive emails or unsubscribed to a campaign."),
|
||||
'opt_out': fields.boolean('Opt-Out', oldname='optout',
|
||||
help="If opt-out is checked, this contact has refused to receive emails for mass mailing and marketing campaign. "
|
||||
"Filter 'Available for Mass Mailing' allows users to filter the leads when performing mass mailing."),
|
||||
'type':fields.selection([ ('lead','Lead'), ('opportunity','Opportunity'), ],'Type', help="Type is used to separate Leads and Opportunities"),
|
||||
'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority', select=True),
|
||||
'date_closed': fields.datetime('Closed', readonly=True),
|
||||
|
@ -725,7 +727,7 @@ class crm_lead(base_stage, format_address, osv.osv):
|
|||
'parent_id': parent_id,
|
||||
'phone': lead.phone,
|
||||
'mobile': lead.mobile,
|
||||
'email': lead.email_from and tools.email_split(lead.email_from)[0],
|
||||
'email': tools.email_split(lead.email_from) and tools.email_split(lead.email_from)[0] or False,
|
||||
'fax': lead.fax,
|
||||
'title': lead.title and lead.title.id or False,
|
||||
'function': lead.function,
|
||||
|
@ -944,7 +946,7 @@ class crm_lead(base_stage, format_address, osv.osv):
|
|||
try:
|
||||
compose_form_id = ir_model_data.get_object_reference(cr, uid, 'mail', 'email_compose_message_wizard_form')[1]
|
||||
except ValueError:
|
||||
compose_form_id = False
|
||||
compose_form_id = False
|
||||
if context is None:
|
||||
context = {}
|
||||
ctx = context.copy()
|
||||
|
@ -975,13 +977,22 @@ class crm_lead(base_stage, format_address, osv.osv):
|
|||
return [lead.section_id.message_get_reply_to()[0] if lead.section_id else False
|
||||
for lead in self.browse(cr, uid, ids, context=context)]
|
||||
|
||||
def message_get_suggested_recipients(self, cr, uid, ids, context=None):
|
||||
recipients = super(crm_lead, self).message_get_suggested_recipients(cr, uid, ids, context=context)
|
||||
for lead in self.browse(cr, uid, ids, context=context):
|
||||
if lead.partner_id:
|
||||
self._message_add_suggested_recipient(cr, uid, recipients, lead, partner=lead.partner_id, reason=_('Customer'))
|
||||
elif lead.email_from:
|
||||
self._message_add_suggested_recipient(cr, uid, recipients, lead, email=lead.email_from, reason=_('Customer Email'))
|
||||
return recipients
|
||||
|
||||
def message_new(self, cr, uid, msg, custom_values=None, context=None):
|
||||
""" Overrides mail_thread message_new that is called by the mailgateway
|
||||
through message_process.
|
||||
This override updates the document according to the email.
|
||||
"""
|
||||
if custom_values is None: custom_values = {}
|
||||
|
||||
if custom_values is None:
|
||||
custom_values = {}
|
||||
desc = html2plaintext(msg.get('body')) if msg.get('body') else ''
|
||||
defaults = {
|
||||
'name': msg.get('subject') or _("No Subject"),
|
||||
|
@ -1029,9 +1040,12 @@ class crm_lead(base_stage, format_address, osv.osv):
|
|||
|
||||
def schedule_phonecall_send_note(self, cr, uid, ids, phonecall_id, action, context=None):
|
||||
phonecall = self.pool.get('crm.phonecall').browse(cr, uid, [phonecall_id], context=context)[0]
|
||||
if action == 'log': prefix = 'Logged'
|
||||
else: prefix = 'Scheduled'
|
||||
message = _("<b>%s a call</b> for the <em>%s</em>.") % (prefix, phonecall.date)
|
||||
if action == 'log':
|
||||
prefix = 'Logged'
|
||||
else:
|
||||
prefix = 'Scheduled'
|
||||
suffix = ' %s' % phonecall.description
|
||||
message = _("%s a call for %s.%s") % (prefix, phonecall.date, suffix)
|
||||
return self.message_post(cr, uid, ids, body=message, context=context)
|
||||
|
||||
def onchange_state(self, cr, uid, ids, state_id, context=None):
|
||||
|
|
|
@ -222,5 +222,17 @@
|
|||
<field name="relation_field">section_id</field>
|
||||
</record>
|
||||
|
||||
<!--Definition of an email template with an empty body that will be used in opportunity mailing.
|
||||
Used to give a basis for email recipients, name and to ease the definition of a further
|
||||
elaborated template. -->
|
||||
<record id="email_template_opportunity_mail" model="email.template">
|
||||
<field name="name">Lead/Opportunity Mass Mail</field>
|
||||
<field name="model_id" ref="crm.model_crm_lead"/>
|
||||
<field name="auto_delete" eval="True"/>
|
||||
<field name="partner_to">${object.partner_id and object.partner_id.id}</field>
|
||||
<field name="email_to">${not object.partner_id and object.email_from}</field>
|
||||
<field name="body_html"></field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
|
@ -97,16 +97,16 @@
|
|||
states="draft,open,pending" help="Convert to Opportunity" class="oe_highlight"/>
|
||||
<button name="case_reset" string="Reset" type="object"
|
||||
states="cancel"/>
|
||||
<button name="case_cancel" string="Cancel" type="object"
|
||||
<button name="case_cancel" string="Cancel Case" type="object"
|
||||
states="draft,open,pending"/>
|
||||
<field name="stage_id" widget="statusbar" clickable="True"
|
||||
on_change="onchange_stage_id(stage_id)"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<div class="oe_right oe_button_box" name="buttons">
|
||||
<button type="action"
|
||||
name="%(act_crm_opportunity_crm_phonecall_new)d"
|
||||
string="Phone Calls"/>
|
||||
<button string="Schedule/Log Call"
|
||||
name="%(opportunity2phonecall_act)d"
|
||||
type="action"/>
|
||||
</div>
|
||||
<div class="oe_title">
|
||||
<label for="name" class="oe_edit_only"/>
|
||||
|
@ -151,7 +151,8 @@
|
|||
-->
|
||||
</group>
|
||||
<group>
|
||||
<field name="user_id" on_change="onchange_user_id(section_id, user_id)"/>
|
||||
<field name="user_id" on_change="onchange_user_id(section_id, user_id)"
|
||||
context="{'default_groups_ref': ['base.group_user', 'base.group_sale_salesman_all_leads']}"/>
|
||||
<label for="section_id"/>
|
||||
<div>
|
||||
<field name="section_id"/>
|
||||
|
@ -163,7 +164,9 @@
|
|||
<field name="priority"/>
|
||||
<field name="categ_ids"
|
||||
widget="many2many_tags"
|
||||
domain="[('object_id.model','=','crm.lead')]"/>
|
||||
domain="[('object_id.model','=','crm.lead')]"
|
||||
context="{'object_name': 'crm.lead'}"
|
||||
/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook colspan="4">
|
||||
|
@ -339,18 +342,21 @@
|
|||
<filter string="Assigned to My Team(s)"
|
||||
domain="[('section_id.member_ids', 'in', [uid])]" context="{'invisible_section': False}"
|
||||
help="Leads that are assigned to any sales teams I am member of"/>
|
||||
<separator/>
|
||||
<separator />
|
||||
<filter string="Available for mass mailing"
|
||||
name='not_opt_out' domain="[('opt_out', '=', False)]"
|
||||
help="Leads that did not ask not to be included in mass mailing campaigns"/>
|
||||
<separator />
|
||||
<group expand="0" string="Group By...">
|
||||
<filter string="Salesperson" domain="[]" context="{'group_by':'user_id'}"/>
|
||||
<filter string="Team" domain="[]" context="{'group_by':'section_id'}"/>
|
||||
<filter string="Stage" domain="[]" context="{'group_by':'stage_id'}"/>
|
||||
<filter string="Customer" help="Partner" domain="[]" context="{'group_by':'partner_id'}"/>
|
||||
<filter string="Country" domain="[]" context="{'group_by':'country_id'}"/>
|
||||
<filter string="Stage" domain="[]" context="{'group_by':'stage_id'}"/>
|
||||
<filter string="Referrer" domain="[]" context="{'group_by':'referred'}"/>
|
||||
<filter string="Campaign" domain="[]" context="{'group_by':'type_id'}"/>
|
||||
<filter string="Channel" domain="[]" context="{'group_by':'channel_id'}"/>
|
||||
<filter string="Creation" domain="[]" context="{'group_by':'create_date'}" groups="base.group_no_one"/>
|
||||
<filter string="Creation" domain="[]" context="{'group_by':'create_date'}"/>
|
||||
</group>
|
||||
<group string="Display">
|
||||
<filter string="Show Countries" context="{'invisible_country': False}" help="Show Countries"/>
|
||||
|
@ -377,7 +383,6 @@
|
|||
states="draft,open,pending" class="oe_highlight"/>
|
||||
<button name="case_mark_lost" string="Mark Lost" type="object"
|
||||
states="draft,open" class="oe_highlight"/>
|
||||
<button name="new_mail_send" string="Send Mail" type="object" states="draft,open,pending"/>
|
||||
<field name="stage_id" widget="statusbar" clickable="True"/>
|
||||
</header>
|
||||
<sheet>
|
||||
|
@ -422,7 +427,7 @@
|
|||
</group>
|
||||
|
||||
<group>
|
||||
<field name="user_id" on_change="onchange_user_id(section_id, user_id)"/>
|
||||
<field name="user_id" on_change="onchange_user_id(section_id, user_id)" context="{'default_groups_ref': ['base.group_user', 'base.group_sale_salesman_all_leads']}"/>
|
||||
<label for="section_id"/>
|
||||
<div>
|
||||
<field name="section_id" widget="selection"/>
|
||||
|
@ -432,7 +437,7 @@
|
|||
<group>
|
||||
<field name="categ_ids"
|
||||
string="Categories" widget="many2many_tags"
|
||||
context = "{'object_name': 'crm.lead'}"
|
||||
context="{'object_name': 'crm.lead'}"
|
||||
domain="[('object_id.model', '=', 'crm.lead')]"/>
|
||||
|
||||
</group>
|
||||
|
@ -567,7 +572,7 @@
|
|||
<filter string="Referrer" domain="[]" context="{'group_by':'referred'}"/>
|
||||
<filter string="Campaign" domain="[]" context="{'group_by':'type_id'}"/>
|
||||
<filter string="Channel" domain="[]" context="{'group_by':'channel_id'}"/>
|
||||
<filter string="Creation" domain="[]" context="{'group_by':'create_date'}" groups="base.group_no_one"/>
|
||||
<filter string="Creation" domain="[]" context="{'group_by':'create_date'}"/>
|
||||
</group>
|
||||
<group string="Display">
|
||||
<filter string="Show Sales Team" context="{'invisible_section': False}" domain="[]" help="Show Sales Team"/>
|
||||
|
@ -577,5 +582,29 @@
|
|||
</field>
|
||||
</record>
|
||||
|
||||
<!--
|
||||
MASS MAILING
|
||||
-->
|
||||
<act_window name="Lead/Opportunity Mass Mail"
|
||||
res_model="mail.compose.message"
|
||||
src_model="crm.lead"
|
||||
view_mode="form"
|
||||
multi="True"
|
||||
target="new"
|
||||
key2="client_action_multi"
|
||||
id="crm.action_lead_mass_mail"
|
||||
context="{
|
||||
'default_composition_mode': 'mass_mail',
|
||||
'default_email_to':'{$object.email or \'\'}',
|
||||
'default_use_template': True,
|
||||
'default_template_id': ref('crm.email_template_opportunity_mail'),
|
||||
}"/>
|
||||
|
||||
<!--Update of email_template defined in crm_lead_data, to add ref_ir_act_window
|
||||
allowing to have a well formed email template (context action considered as set). -->
|
||||
<record id="email_template_opportunity_mail" model="email.template">
|
||||
<field name="ref_ir_act_window" ref="crm.action_lead_mass_mail"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
|
@ -73,7 +73,7 @@
|
|||
states="open,pending"/>
|
||||
<button name="case_reset" string="Reset to Todo" type="object"
|
||||
states="cancel"/>
|
||||
<button name="case_cancel" string="Cancel" type="object"
|
||||
<button name="case_cancel" string="Cancel Call" type="object"
|
||||
states="draft,open,pending"/>
|
||||
<field name="state" widget="statusbar" nolabel="1" statusbar_visible="open,done"/>
|
||||
</header>
|
||||
|
|
|
@ -52,8 +52,6 @@ class crm_opportunity2phonecall(osv.osv_memory):
|
|||
res.update({'categ_id': categ_id})
|
||||
if 'partner_id' in fields:
|
||||
res.update({'partner_id': opp.partner_id and opp.partner_id.id or False})
|
||||
if 'note' in fields:
|
||||
res.update({'note': opp.description})
|
||||
if 'contact_name' in fields:
|
||||
res.update({'contact_name': opp.partner_id and opp.partner_id.name or False})
|
||||
if 'phone' in fields:
|
||||
|
|
|
@ -13,14 +13,14 @@
|
|||
<group>
|
||||
<field name="action"/>
|
||||
<field name="name"/>
|
||||
<field name="categ_id" string="Type"
|
||||
widget="selection"
|
||||
domain="[('object_id.model', '=', 'crm.phonecall')]"
|
||||
groups="base.group_no_one"/>
|
||||
<field name="date" string="Planned Date" attrs="{'invisible': [('action','=','log')]}"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="partner_id" readonly="True"/>
|
||||
<field name="categ_id" string="Type"
|
||||
widget="selection"
|
||||
domain="[('object_id.model', '=', 'crm.phonecall')]"
|
||||
groups="base.group_no_one"/>
|
||||
<field name="phone"/>
|
||||
<field name="user_id" attrs="{'invisible': [('action','=','log')]}"/>
|
||||
<field name="section_id" widget="selection" attrs="{'invisible': [('action','=','log')]}"/>
|
||||
|
|
|
@ -168,7 +168,7 @@
|
|||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids" widget="mail_followers" groups="base.group_user"/>
|
||||
<field name="message_ids" widget="mail_thread" placeholder="Share a note..."/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
|
|
|
@ -31,8 +31,8 @@
|
|||
<header>
|
||||
<button name="case_open" string="Open" type="object" class="oe_highlight"
|
||||
states="draft,pending"/>
|
||||
<button name="case_close" string="Close" type="object" states="draft,pending"/>
|
||||
<button name="case_close" string="Close" type="object" states="open" class="oe_highlight"/>
|
||||
<button name="case_close" string="Close Case" type="object" states="draft,pending"/>
|
||||
<button name="case_close" string="Close Case" type="object" states="open" class="oe_highlight"/>
|
||||
<button name="case_pending" string="Pending" type="object"
|
||||
states="draft"/>
|
||||
<button name="case_pending" string="Pending" type="object"
|
||||
|
@ -41,7 +41,7 @@
|
|||
states="cancel,done"/>
|
||||
<button name="case_escalate" string="Escalate" type="object"
|
||||
states="open,draft,pending"/>
|
||||
<button name="case_cancel" string="Cancel" type="object"
|
||||
<button name="case_cancel" string="Cancel Case" type="object"
|
||||
states="draft,open,pending"/>
|
||||
<field name="state" nolabel="1" widget="statusbar" statusbar_visible="draft,open,done" statusbar_colors='{"pending":"blue"}'/>
|
||||
</header>
|
||||
|
@ -95,7 +95,7 @@
|
|||
</notebook>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_ids" widget="mail_thread" placeholder="Share a note..."/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
#
|
||||
##############################################################################
|
||||
|
||||
import partner_geo_assign
|
||||
import crm_partner_assign
|
||||
import wizard
|
||||
import report
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ You can also use the geolocalization without using the GPS coordinates.
|
|||
'res_partner_view.xml',
|
||||
'wizard/crm_forward_to_partner_view.xml',
|
||||
'crm_lead_view.xml',
|
||||
'crm_partner_assign_data.xml',
|
||||
'report/crm_lead_report_view.xml',
|
||||
'report/crm_partner_report_view.xml',
|
||||
],
|
||||
|
|
|
@ -71,7 +71,6 @@ class res_partner_grade(osv.osv):
|
|||
_defaults = {
|
||||
'active': lambda *args: 1
|
||||
}
|
||||
res_partner_grade()
|
||||
|
||||
class res_partner_activation(osv.osv):
|
||||
_name = 'res.partner.activation'
|
||||
|
@ -82,7 +81,6 @@ class res_partner_activation(osv.osv):
|
|||
'name' : fields.char('Name', size=32, required=True),
|
||||
}
|
||||
|
||||
res_partner_activation()
|
||||
|
||||
class res_partner(osv.osv):
|
||||
_inherit = "res.partner"
|
||||
|
@ -120,7 +118,6 @@ class res_partner(osv.osv):
|
|||
'date_localization': fields.date.context_today(self,cr,uid,context=context)
|
||||
}, context=context)
|
||||
return True
|
||||
res_partner()
|
||||
|
||||
class crm_lead(osv.osv):
|
||||
_inherit = "crm.lead"
|
||||
|
@ -164,8 +161,9 @@ class crm_lead(osv.osv):
|
|||
self.assign_geo_localize(cr, uid, [lead.id], lead.partner_latitude, lead.partner_longitude, context=context)
|
||||
partner = res_partner.browse(cr, uid, partner_id, context=context)
|
||||
if partner.user_id:
|
||||
salesteam_id = partner.section_id and partner.section_id.id or False
|
||||
for lead_id in ids:
|
||||
self.allocate_salesman(cr, uid, [lead_id], [partner.user_id.id], context=context)
|
||||
self.allocate_salesman(cr, uid, [lead_id], [partner.user_id.id], team_id=salesteam_id, context=context)
|
||||
self.write(cr, uid, [lead.id], {'date_assign': fields.date.context_today(self,cr,uid,context=context), 'partner_assigned_id': partner_id}, context=context)
|
||||
return res
|
||||
|
||||
|
@ -260,7 +258,7 @@ class crm_lead(osv.osv):
|
|||
res_partner_ids[lead.id] = partner_id
|
||||
break
|
||||
return res_partner_ids
|
||||
crm_lead()
|
||||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<?xml version="1.0" encoding='UTF-8'?>
|
||||
<openerp>
|
||||
<data>
|
||||
<record id="crm_partner_assign_email_template" model="email.template">
|
||||
<field name="name">Lead forward</field>
|
||||
<field name="email_from"></field>
|
||||
<field name="subject">Fwd: Lead: ${object.name}</field>
|
||||
<field name="email_to"></field>
|
||||
<field name="lang"></field>
|
||||
<field name="model_id" ref="crm.model_crm_lead"/>
|
||||
<field name="auto_delete" eval="True"/>
|
||||
<field name="body_html"><![CDATA[
|
||||
Hello ${object.partner_assigned_id.name},
|
||||
<p>
|
||||
Here is a lead that might interest you.
|
||||
</p>
|
||||
<p>
|
||||
Please keep me informed about your actions about it so that I can keep an
|
||||
accurate follow-up of it and help you in the sale cycle.
|
||||
</p>
|
||||
<p>
|
||||
Your account manager,<br/>
|
||||
${object.user_id.name},<br/>
|
||||
${object.user_id.email}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
${ctx["mail_body"].replace('\n','<br>') | safe}
|
||||
</p>
|
||||
|
||||
% if ctx["history_mode"] in ('whole'):
|
||||
% for message in object.message_ids:
|
||||
---- Original Message (${message.date or ''}) ----<br/>
|
||||
${message.body | safe}
|
||||
% endfor
|
||||
% endif
|
||||
% if ctx['history_mode'] == 'latest':
|
||||
---- Original Message (${object.message_ids[0].date or ''}) ----<br/>
|
||||
${object.message_ids[0].body | safe}
|
||||
% endif
|
||||
]]></field>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
|
@ -77,6 +77,7 @@
|
|||
<field name="inherit_id" ref="base.view_partner_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="user_id" position="after">
|
||||
<field name="date_review_next"/>
|
||||
<field name="grade_id"/>
|
||||
<field name="activation"/>
|
||||
</field>
|
||||
|
@ -104,17 +105,19 @@
|
|||
<field name="arch" type="xml">
|
||||
<xpath expr="//notebook[last()]" position="inside">
|
||||
<page string="Geo Localization">
|
||||
<group colspan="2" col="2">
|
||||
<separator string="Partner Activation" colspan="2"/>
|
||||
<field name="grade_id" widget="selection"/>
|
||||
<field name="activation" widget="selection"/>
|
||||
<field name="partner_weight"/>
|
||||
</group>
|
||||
<group colspan="2" col="2">
|
||||
<separator string="Partner Review" colspan="2"/>
|
||||
<field name="date_review"/>
|
||||
<field name="date_review_next"/>
|
||||
<field name="date_partnership"/>
|
||||
<group>
|
||||
<group>
|
||||
<separator string="Partner Activation" colspan="2"/>
|
||||
<field name="grade_id" widget="selection"/>
|
||||
<field name="activation" widget="selection"/>
|
||||
<field name="partner_weight"/>
|
||||
</group>
|
||||
<group>
|
||||
<separator string="Partner Review" colspan="2"/>
|
||||
<field name="date_review"/>
|
||||
<field name="date_review_next"/>
|
||||
<field name="date_partnership"/>
|
||||
</group>
|
||||
</group>
|
||||
<group colspan="2" col="2">
|
||||
<separator string="Geo Localization" colspan="2"/>
|
||||
|
|
|
@ -20,30 +20,15 @@
|
|||
#
|
||||
##############################################################################
|
||||
|
||||
import re
|
||||
import time
|
||||
from openerp import tools
|
||||
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.tools.translate import _
|
||||
|
||||
class crm_lead_forward_to_partner(osv.osv_memory):
|
||||
|
||||
class crm_lead_forward_to_partner(osv.TransientModel):
|
||||
""" Forward info history to partners. """
|
||||
_name = 'crm.lead.forward.to.partner'
|
||||
_inherit = "mail.compose.message"
|
||||
|
||||
def default_get(self, cr, uid, fields, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
# set as comment, perform overrided document-like action that calls get_record_data
|
||||
old_mode = context.get('default_composition_mode', 'forward')
|
||||
context['default_composition_mode'] = 'comment'
|
||||
res = super(crm_lead_forward_to_partner, self).default_get(cr, uid, fields, context=context)
|
||||
# back to forward mode
|
||||
context['default_composition_mode'] = old_mode
|
||||
res['composition_mode'] = context['default_composition_mode']
|
||||
return res
|
||||
|
||||
def _get_composition_mode_selection(self, cr, uid, context=None):
|
||||
composition_mode = super(crm_lead_forward_to_partner, self)._get_composition_mode_selection(cr, uid, context=context)
|
||||
composition_mode.append(('forward', 'Forward'))
|
||||
|
@ -56,37 +41,54 @@ class crm_lead_forward_to_partner(osv.osv_memory):
|
|||
'attachment_ids': fields.many2many('ir.attachment',
|
||||
'lead_forward_to_partner_attachment_rel',
|
||||
'wizard_id', 'attachment_id', 'Attachments'),
|
||||
'history_mode': fields.selection([('info', 'Case Information'),
|
||||
'history_mode': fields.selection([('info', 'Internal notes'),
|
||||
('latest', 'Latest email'), ('whole', 'Whole Story')],
|
||||
'Send history', required=True),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'history_mode': 'latest',
|
||||
'history_mode': 'info',
|
||||
}
|
||||
|
||||
def default_get(self, cr, uid, fields, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
# set as comment, perform overrided document-like action that calls get_record_data
|
||||
old_mode = context.get('default_composition_mode', 'forward')
|
||||
context['default_composition_mode'] = 'comment'
|
||||
res = super(crm_lead_forward_to_partner, self).default_get(cr, uid, fields, context=context)
|
||||
# back to forward mode
|
||||
context['default_composition_mode'] = old_mode
|
||||
res['composition_mode'] = context['default_composition_mode']
|
||||
return res
|
||||
|
||||
def get_record_data(self, cr, uid, model, res_id, context=None):
|
||||
""" Override of mail.compose.message, to add default values coming
|
||||
form the related lead.
|
||||
"""
|
||||
if context is None:
|
||||
context = {}
|
||||
res = super(crm_lead_forward_to_partner, self).get_record_data(cr, uid, model, res_id, context=context)
|
||||
if model not in ('crm.lead') or not res_id:
|
||||
return res
|
||||
lead_obj = self.pool.get(model)
|
||||
lead = lead_obj.browse(cr, uid, res_id, context=context)
|
||||
subject = '%s: %s - %s' % (_('Fwd'), _('Lead forward'), lead.name)
|
||||
body = self._get_message_body(cr, uid, lead, 'info', context=context)
|
||||
res.update({
|
||||
'subject': subject,
|
||||
'body': body,
|
||||
})
|
||||
template_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'crm_partner_assign', 'crm_partner_assign_email_template')[1]
|
||||
context['history_mode'] = context.get('history_mode','whole')
|
||||
mail_body_fields = ['partner_id', 'partner_name', 'title', 'function', 'street', 'street2', 'zip', 'city', 'country_id', 'state_id', 'email_from', 'phone', 'fax', 'mobile', 'description']
|
||||
lead = self.pool.get('crm.lead').browse(cr, uid, res_id, context=context)
|
||||
context['mail_body'] = self.pool.get('crm.lead')._mail_body(cr, uid, lead, mail_body_fields, context=context)
|
||||
template = self.generate_email_for_composer(cr, uid, template_id, res_id, context)
|
||||
res['subject'] = template['subject']
|
||||
res['body'] = template['body']
|
||||
return res
|
||||
|
||||
def on_change_history_mode(self, cr, uid, ids, history_mode, model, res_id, context=None):
|
||||
""" Update body when changing history_mode """
|
||||
if context is None:
|
||||
context = {}
|
||||
if model and model == 'crm.lead' and res_id:
|
||||
lead = self.pool.get(model).browse(cr, uid, res_id, context=context)
|
||||
body = self._get_message_body(cr, uid, lead, history_mode, context=context)
|
||||
context['history_mode'] = history_mode
|
||||
body = self.get_record_data(cr, uid, 'crm.lead', res_id, context=context)['body']
|
||||
return {'value': {'body': body}}
|
||||
|
||||
def create(self, cr, uid, values, context=None):
|
||||
|
@ -116,56 +118,7 @@ class crm_lead_forward_to_partner(osv.osv_memory):
|
|||
value = self.default_get(cr, uid, ['body', 'email_to', 'email_cc', 'subject', 'history_mode'], context=context)
|
||||
self.write(cr, uid, ids, value, context=context)
|
||||
|
||||
self.send_mail(cr, uid, ids, context=context)
|
||||
# for case in lead.browse(cr, uid, lead_ids, context=context):
|
||||
# TODO: WHAT TO DO WITH THAT ?
|
||||
# if (this.send_to == 'partner' and this.partner_id):
|
||||
# lead.assign_partner(cr, uid, [case.id], this.partner_id.id, context=context)
|
||||
|
||||
# if this.send_to == 'user':
|
||||
# lead.allocate_salesman(cr, uid, [case.id], [this.user_id.id], context=context)
|
||||
return res
|
||||
|
||||
def _get_info_body(self, cr, uid, lead, context=None):
|
||||
field_names = []
|
||||
proxy = self.pool.get(lead._name)
|
||||
if lead.type == 'opportunity':
|
||||
field_names += ['partner_id']
|
||||
field_names += [
|
||||
'partner_name' , 'title', 'function', 'street', 'street2',
|
||||
'zip', 'city', 'country_id', 'state_id', 'email_from',
|
||||
'phone', 'fax', 'mobile', 'categ_id', 'description',
|
||||
]
|
||||
return proxy._mail_body(cr, uid, lead, field_names, context=context)
|
||||
|
||||
def _get_message_body(self, cr, uid, lead, history_mode='whole', context=None):
|
||||
""" This function gets whole communication history and returns as top
|
||||
posting style
|
||||
#1: form a body, based on the lead
|
||||
#2: append to the body the communication history, based on the
|
||||
history_mode parameter
|
||||
|
||||
- info: Forward the case information
|
||||
- latest: Send the latest history
|
||||
- whole: Send the whole history
|
||||
|
||||
:param lead: browse_record on crm.lead
|
||||
:param history_mode: 'whole' or 'latest'
|
||||
"""
|
||||
mail_message = self.pool.get('mail.message')
|
||||
body = self._get_info_body(cr, uid, lead, context=context)
|
||||
if history_mode not in ('whole', 'latest'):
|
||||
return body or ''
|
||||
for message in lead.message_ids:
|
||||
header = '-------- Original Message --------'
|
||||
sentdate = 'Date: %s' % (message.date or '')
|
||||
desc = '\n%s'%(message.body)
|
||||
original = [header, sentdate, desc, '\n']
|
||||
original = '\n'.join(original)
|
||||
body += original
|
||||
if history_mode == 'latest':
|
||||
break
|
||||
return body or ''
|
||||
return self.send_mail(cr, uid, ids, context=context)
|
||||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -15,8 +15,7 @@
|
|||
<field name="history_mode" colspan="4"
|
||||
on_change="on_change_history_mode(history_mode, model, res_id)"/>
|
||||
<field name="subject" colspan="4"/>
|
||||
<field name="partner_ids" colspan="4" widget="many2many_tags"
|
||||
on_change="onchange_partner_ids(partner_ids)"/>
|
||||
<field name="partner_ids" colspan="4" widget="many2many_tags_email"/>
|
||||
<notebook colspan="4">
|
||||
<page string="Body">
|
||||
<field name="body"/>
|
||||
|
|
|
@ -181,11 +181,11 @@ class contentIndex(object):
|
|||
res = (mime, fobj.indexContent(content,filename,fname or realfname) )
|
||||
else:
|
||||
_logger.debug("Have no object, return (%s, None).", mime)
|
||||
res = (mime, None )
|
||||
res = (mime, '')
|
||||
except Exception:
|
||||
_logger.exception("Cannot index file %s (%s).",
|
||||
filename, fname or realfname)
|
||||
res = None
|
||||
res = (mime, '')
|
||||
|
||||
# If we created a tmp file, unlink it now
|
||||
if not realfname and fname:
|
||||
|
|
|
@ -105,7 +105,7 @@ class DocIndex(indexer):
|
|||
|
||||
_logger.warning("Failed attempt to execute antiword (MS Word reader). Antiword is necessary to index the file %s of MIME type %s. Detailed error available at DEBUG level.", fname, self._getMimeTypes()[0])
|
||||
_logger.debug("Trace of the failed file indexing attempt.", exc_info=True)
|
||||
return False
|
||||
return u''
|
||||
|
||||
cntIndex.register(DocIndex())
|
||||
|
||||
|
@ -166,9 +166,14 @@ class PdfIndex(indexer):
|
|||
return ['.pdf']
|
||||
|
||||
def _doIndexFile(self, fname):
|
||||
pop = Popen(['pdftotext', '-enc', 'UTF-8', '-nopgbrk', fname, '-'], shell=False, stdout=PIPE)
|
||||
(data, _) = pop.communicate()
|
||||
return _to_unicode(data)
|
||||
try:
|
||||
pop = Popen(['pdftotext', '-enc', 'UTF-8', '-nopgbrk', fname, '-'], shell=False, stdout=PIPE)
|
||||
(data, _) = pop.communicate()
|
||||
return _to_unicode(data)
|
||||
except OSError:
|
||||
_logger.warning("Failed attempt to execute pdftotext. This program is necessary to index the file %s of MIME type %s. Detailed error available at DEBUG level.", fname, self._getMimeTypes()[0])
|
||||
_logger.debug("Trace of the failed file indexing attempt.", exc_info=True)
|
||||
return u''
|
||||
|
||||
cntIndex.register(PdfIndex())
|
||||
|
||||
|
|
|
@ -65,6 +65,7 @@ class email_template(osv.osv):
|
|||
"Templates for sending email"
|
||||
_name = "email.template"
|
||||
_description = 'Email Templates'
|
||||
_order = 'name'
|
||||
|
||||
def render_template(self, cr, uid, template, model, res_id, context=None):
|
||||
"""Render the given template text, replace mako expressions ``${expr}``
|
||||
|
@ -141,9 +142,13 @@ class email_template(osv.osv):
|
|||
help="If checked, the user's signature will be appended to the text version "
|
||||
"of the message"),
|
||||
'subject': fields.char('Subject', translate=True, help="Subject (placeholders may be used here)",),
|
||||
'email_from': fields.char('From', help="Sender address (placeholders may be used here)"),
|
||||
'email_from': fields.char('From',
|
||||
help="Sender address (placeholders may be used here). If not set, the default "
|
||||
"value will be the author's email alias if configured, or email address."),
|
||||
'email_to': fields.char('To (Emails)', help="Comma-separated recipient addresses (placeholders may be used here)"),
|
||||
'email_recipients': fields.char('To (Partners)', help="Comma-separated ids of recipient partners (placeholders may be used here)"),
|
||||
'partner_to': fields.char('To (Partners)',
|
||||
help="Comma-separated ids of recipient partners (placeholders may be used here)",
|
||||
oldname='email_recipients'),
|
||||
'email_cc': fields.char('Cc', help="Carbon copy recipients (placeholders may be used here)"),
|
||||
'reply_to': fields.char('Reply-To', help="Preferred response address (placeholders may be used here)"),
|
||||
'mail_server_id': fields.many2one('ir.mail_server', 'Outgoing Mail Server', readonly=False,
|
||||
|
@ -311,7 +316,7 @@ class email_template(osv.osv):
|
|||
template = self.get_email_template(cr, uid, template_id, res_id, context)
|
||||
values = {}
|
||||
for field in ['subject', 'body_html', 'email_from',
|
||||
'email_to', 'email_recipients', 'email_cc', 'reply_to']:
|
||||
'email_to', 'partner_to', 'email_cc', 'reply_to']:
|
||||
values[field] = self.render_template(cr, uid, getattr(template, field),
|
||||
template.model, res_id, context=context) \
|
||||
or False
|
||||
|
@ -371,7 +376,7 @@ class email_template(osv.osv):
|
|||
values = self.generate_email(cr, uid, template_id, res_id, context=context)
|
||||
assert 'email_from' in values, 'email_from is missing or empty after template rendering, send_mail() cannot proceed'
|
||||
attachments = values.pop('attachments') or {}
|
||||
del values['email_recipients'] # TODO Properly use them.
|
||||
del values['partner_to'] # TODO Properly use them.
|
||||
msg_id = mail_mail.create(cr, uid, values, context=context)
|
||||
# link attachments
|
||||
attachment_ids = []
|
||||
|
|
|
@ -28,9 +28,9 @@
|
|||
<page string="Email Details">
|
||||
<group>
|
||||
<group string="Addressing">
|
||||
<field name="email_from" required="1"/>
|
||||
<field name="email_from"/>
|
||||
<field name="email_to"/>
|
||||
<field name="email_recipients"/>
|
||||
<field name="partner_to"/>
|
||||
<field name="email_cc"/>
|
||||
<field name="reply_to"/>
|
||||
<field name="user_signature"/>
|
||||
|
@ -43,7 +43,7 @@
|
|||
<field name="copyvalue"/>
|
||||
</group>
|
||||
<group string="Contents" colspan="2">
|
||||
<field name="subject" required="1"/>
|
||||
<field name="subject"/>
|
||||
<field name="body_html" width="250" height="450" nolabel="1" colspan="2" placeholder="Email contents (in raw HTML format)"/>
|
||||
</group>
|
||||
</group>
|
||||
|
@ -78,7 +78,7 @@
|
|||
<field name="subject"/>
|
||||
<field name="email_from"/>
|
||||
<field name="email_to"/>
|
||||
<field name="email_recipients"/>
|
||||
<field name="partner_to"/>
|
||||
<field name="report_name"/>
|
||||
</tree>
|
||||
</field>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp.osv import fields,osv
|
||||
from openerp.osv import fields, osv
|
||||
|
||||
class res_partner(osv.osv):
|
||||
"""Inherit res.partner to add a generic opt-out field that can be used
|
||||
|
@ -28,8 +28,9 @@ class res_partner(osv.osv):
|
|||
_inherit = 'res.partner'
|
||||
|
||||
_columns = {
|
||||
'opt_out': fields.boolean('Opt-Out', help="If checked, this partner will not receive any automated email " \
|
||||
"notifications, such as the availability of invoices."),
|
||||
'opt_out': fields.boolean('Opt-Out',
|
||||
help="If opt-out is checked, this contact has refused to receive emails for mass mailing and marketing campaign. "
|
||||
"Filter 'Available for Mass Mailing' allows users to filter the partners when performing mass mailing."),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
|
|
|
@ -11,5 +11,49 @@
|
|||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="res_partner_opt_out_search">
|
||||
<field name="name">res.partner.opt_out.search</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="base.view_res_partner_filter"/>
|
||||
<field name="arch" type="xml">
|
||||
<filter string="Suppliers" position="after">
|
||||
<separator />
|
||||
<filter string="Available for mass mailing"
|
||||
name='not_opt_out' domain="[('opt_out', '=', False)]"
|
||||
help="Partners that did not ask not to be included in mass mailing campaigns" />
|
||||
</filter>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!--Definition of an email template with an empty body that will be used in partner mailing. Used to give a
|
||||
basis for email recipients, name and to ease the definition of a further elaborated template. -->
|
||||
<record id="email_template_partner" model="email.template">
|
||||
<field name="name">Partner Mass Mail</field>
|
||||
<field name="model_id" ref="base.model_res_partner"/>
|
||||
<field name="auto_delete" eval="True"/>
|
||||
<field name="partner_to">${object.id}</field>
|
||||
</record>
|
||||
|
||||
<!-- Replace the default mass-mailing wizard in base with the composition wizard -->
|
||||
<act_window name="Partner Mass Mailing"
|
||||
res_model="mail.compose.message"
|
||||
src_model="res.partner"
|
||||
view_mode="form"
|
||||
multi="True"
|
||||
target="new"
|
||||
key2="client_action_multi"
|
||||
id="base.action_partner_mass_mail"
|
||||
context="{
|
||||
'default_composition_mode': 'mass_mail',
|
||||
'default_partner_to': '${object.id or \'\'}',
|
||||
'default_use_template': True,
|
||||
'default_template_id': ref('email_template_partner'),
|
||||
}"/>
|
||||
|
||||
<record id="email_template_partner" model="email.template">
|
||||
<field name="ref_ir_act_window" ref="base.action_partner_mass_mail"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
|
@ -130,6 +130,7 @@ class test_message_compose(TestMailBase):
|
|||
# 1. Mass_mail on pigs and bird, with a default_partner_ids set to check he is correctly added
|
||||
context = {
|
||||
'default_composition_mode': 'mass_mail',
|
||||
'default_notify': True,
|
||||
'default_model': 'mail.group',
|
||||
'default_res_id': self.group_pigs_id,
|
||||
'default_template_id': email_template_id,
|
||||
|
@ -170,20 +171,20 @@ class test_message_compose(TestMailBase):
|
|||
self.assertEqual(set(message_bird_pids), set(partner_ids), 'mail.message on bird notified_partner_ids incorrect')
|
||||
|
||||
# ----------------------------------------
|
||||
# CASE4: test newly introduced email_recipients field
|
||||
# CASE4: test newly introduced partner_to field
|
||||
# ----------------------------------------
|
||||
|
||||
# get already-created partners back
|
||||
p_b_id = self.res_partner.search(cr, uid, [('email', '=', 'b@b.b')])[0]
|
||||
p_c_id = self.res_partner.search(cr, uid, [('email', '=', 'c@c.c')])[0]
|
||||
p_d_id = self.res_partner.search(cr, uid, [('email', '=', 'd@d.d')])[0]
|
||||
# modify template: use email_recipients, use template and email address in email_to to test all features together
|
||||
# modify template: use partner_to, use template and email address in email_to to test all features together
|
||||
user_model_id = self.registry('ir.model').search(cr, uid, [('model', '=', 'res.users')])[0]
|
||||
email_template.write(cr, uid, [email_template_id], {
|
||||
'model_id': user_model_id,
|
||||
'body_html': '${object.login}',
|
||||
'email_to': '${object.email} c@c',
|
||||
'email_recipients': '%i,%i' % (p_b_id, p_c_id),
|
||||
'partner_to': '%i,%i' % (p_b_id, p_c_id),
|
||||
'email_cc': 'd@d',
|
||||
})
|
||||
# patner by email + partner by id (no double)
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<group>
|
||||
<field name="email_from" readonly="1"/>
|
||||
<field name="email_to" readonly="1"/>
|
||||
<field name="email_recipients" readonly="1"/>
|
||||
<field name="partner_to" readonly="1"/>
|
||||
<field name="email_cc" readonly="1" attrs="{'invisible':[('email_cc','=',False)]}"/>
|
||||
<field name="reply_to" readonly="1" attrs="{'invisible':[('reply_to','=',False)]}"/>
|
||||
<field name="subject" readonly="1"/>
|
||||
|
|
|
@ -59,14 +59,21 @@ class mail_compose_message(osv.TransientModel):
|
|||
_columns = {
|
||||
# incredible hack of the day: size=-1 means we want an int db column instead of an str one
|
||||
'template_id': fields.selection(_get_templates, 'Template', size=-1),
|
||||
'partner_to': fields.char('To (Partner IDs)',
|
||||
help="Comma-separated list of recipient partners ids (placeholders may be used here)"),
|
||||
'email_to': fields.char('To (Emails)',
|
||||
help="Comma-separated recipient addresses (placeholders may be used here)",),
|
||||
'email_cc': fields.char('Cc (Emails)',
|
||||
help="Carbon copy recipients (placeholders may be used here)"),
|
||||
}
|
||||
|
||||
def onchange_template_id(self, cr, uid, ids, template_id, composition_mode, model, res_id, context=None):
|
||||
""" - mass_mailing: we cannot render, so return the template values
|
||||
- normal mode: return rendered values """
|
||||
if template_id and composition_mode == 'mass_mail':
|
||||
values = self.pool.get('email.template').read(cr, uid, template_id, ['subject', 'body_html'], context)
|
||||
values.pop('id')
|
||||
fields = ['subject', 'body_html', 'email_from', 'email_to', 'partner_to', 'email_cc', 'reply_to']
|
||||
template_values = self.pool.get('email.template').read(cr, uid, template_id, fields, context)
|
||||
values = dict((field, template_values[field]) for field in fields if template_values.get(field))
|
||||
elif template_id:
|
||||
# FIXME odo: change the mail generation to avoid attachment duplication
|
||||
values = self.generate_email_for_composer(cr, uid, template_id, res_id, context=context)
|
||||
|
@ -80,11 +87,11 @@ class mail_compose_message(osv.TransientModel):
|
|||
'datas_fname': attach_fname,
|
||||
'res_model': model,
|
||||
'res_id': res_id,
|
||||
'type': 'binary', # override default_type from context, possibly meant for another model!
|
||||
'type': 'binary', # override default_type from context, possibly meant for another model!
|
||||
}
|
||||
values['attachment_ids'].append(ir_attach_obj.create(cr, uid, data_attach, context=context))
|
||||
else:
|
||||
values = self.default_get(cr, uid, ['body', 'subject', 'partner_ids', 'attachment_ids'], context=context)
|
||||
values = self.default_get(cr, uid, ['subject', 'body', 'email_from', 'email_to', 'email_cc', 'partner_to', 'reply_to', 'attachment_ids'], context=context)
|
||||
|
||||
if values.get('body_html'):
|
||||
values['body'] = values.pop('body_html')
|
||||
|
@ -117,27 +124,35 @@ class mail_compose_message(osv.TransientModel):
|
|||
# Wizard validation and send
|
||||
#------------------------------------------------------
|
||||
|
||||
def _get_or_create_partners_from_values(self, cr, uid, rendered_values, context=None):
|
||||
""" Check for email_to, email_cc, partner_to """
|
||||
partner_ids = []
|
||||
mails = tools.email_split(rendered_values.pop('email_to', '') + ' ' + rendered_values.pop('email_cc', ''))
|
||||
for mail in mails:
|
||||
partner_id = self.pool.get('res.partner').find_or_create(cr, uid, mail, context=context)
|
||||
partner_ids.append(partner_id)
|
||||
partner_to = rendered_values.pop('partner_to', '')
|
||||
if partner_to:
|
||||
for partner_id in partner_to.split(','):
|
||||
if partner_id: # placeholders could generate '', 3, 2 due to some empty field values
|
||||
partner_ids.append(int(partner_id))
|
||||
return partner_ids
|
||||
|
||||
def generate_email_for_composer(self, cr, uid, template_id, res_id, context=None):
|
||||
""" Call email_template.generate_email(), get fields relevant for
|
||||
mail.compose.message, transform email_cc and email_to into partner_ids """
|
||||
template_values = self.pool.get('email.template').generate_email(cr, uid, template_id, res_id, context=context)
|
||||
# filter template values
|
||||
fields = ['body_html', 'subject', 'email_to', 'email_recipients', 'email_cc', 'attachments']
|
||||
fields = ['subject', 'body_html', 'email_from', 'email_to', 'partner_to', 'email_cc', 'reply_to', 'attachments']
|
||||
values = dict((field, template_values[field]) for field in fields if template_values.get(field))
|
||||
values['body'] = values.pop('body_html', '')
|
||||
|
||||
# transform email_to, email_cc into partner_ids
|
||||
values['partner_ids'] = []
|
||||
|
||||
mails = tools.email_split(values.pop('email_to', '') + ' ' + values.pop('email_cc', ''))
|
||||
for mail in mails:
|
||||
partner_id = self.pool.get('res.partner').find_or_create(cr, uid, mail, context=context)
|
||||
values['partner_ids'].append(partner_id)
|
||||
email_recipients = values.pop('email_recipients', '')
|
||||
if email_recipients:
|
||||
for partner_id in email_recipients.split(','):
|
||||
values['partner_ids'].append(int(partner_id))
|
||||
|
||||
values['partner_ids'] = list(set(values['partner_ids']))
|
||||
partner_ids = self._get_or_create_partners_from_values(cr, uid, values, context=context)
|
||||
# legacy template behavior: void values do not erase existing values and the
|
||||
# related key is removed from the values dict
|
||||
if partner_ids:
|
||||
values['partner_ids'] = list(partner_ids)
|
||||
|
||||
return values
|
||||
|
||||
|
@ -150,6 +165,11 @@ 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)
|
||||
# those values are not managed; they are readonly
|
||||
email_dict.pop('email_to', None)
|
||||
email_dict.pop('email_cc', None)
|
||||
email_dict.pop('partner_to', None)
|
||||
# update template values by wizard values
|
||||
values.update(email_dict)
|
||||
return values
|
||||
|
||||
|
|
|
@ -7,6 +7,22 @@
|
|||
<field name="model">mail.compose.message</field>
|
||||
<field name="inherit_id" ref="mail.email_compose_message_wizard_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='subject']" position="after">
|
||||
<label string="Template Recipients" for="partner_to"
|
||||
groups="base.group_no_one"
|
||||
attrs="{'invisible':[('composition_mode', '!=', 'mass_mail')]}"/>
|
||||
<div groups="base.group_no_one"
|
||||
attrs="{'invisible':[('composition_mode', '!=', 'mass_mail')]}">
|
||||
<group class="oe_grey">
|
||||
<!-- <label string="Partners" for="partner_to"/> -->
|
||||
<field name="partner_to" readonly="1"/>
|
||||
<!-- <label string="Email To" for="email_to"/> -->
|
||||
<field name="email_to" readonly="1"/>
|
||||
<!-- <label string="Email CC" for="email_cc"/> -->
|
||||
<field name="email_cc" readonly="1"/>
|
||||
</group>
|
||||
</div>
|
||||
</xpath>
|
||||
<xpath expr="//footer" position="inside">
|
||||
<group class="oe_right" col="1">
|
||||
<div>Use template
|
||||
|
|
|
@ -168,7 +168,7 @@
|
|||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids" widget="mail_followers" groups="base.group_user"/>
|
||||
<field name="message_ids" widget="mail_thread" placeholder="Share a note..."/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
|
@ -405,7 +405,7 @@
|
|||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="message_ids" widget="mail_thread" placeholder="Share a note..."/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
|
|
|
@ -78,7 +78,7 @@ class fetchmail_server(osv.osv):
|
|||
'priority': fields.integer('Server Priority', readonly=True, states={'draft':[('readonly', False)]}, help="Defines the order of processing, "
|
||||
"lower values mean higher priority"),
|
||||
'message_ids': fields.one2many('mail.mail', 'fetchmail_server_id', 'Messages', readonly=True),
|
||||
'configuration' : fields.text('Configuration'),
|
||||
'configuration' : fields.text('Configuration', readonly=True),
|
||||
'script' : fields.char('Script', readonly=True, size=64),
|
||||
}
|
||||
_defaults = {
|
||||
|
@ -113,7 +113,16 @@ class fetchmail_server(osv.osv):
|
|||
conf['model']=r[0]['model']
|
||||
values['configuration'] = """Use the below script with the following command line options with your Mail Transport Agent (MTA)
|
||||
|
||||
openerp_mailgate.py -u %(uid)d -p PASSWORD -o %(model)s -d %(dbname)s --host=HOSTNAME --port=PORT
|
||||
openerp_mailgate.py --host=HOSTNAME --port=PORT -u %(uid)d -p PASSWORD -d %(dbname)s
|
||||
|
||||
Example configuration for the postfix mta running locally:
|
||||
|
||||
/etc/postfix/virtual_aliases:
|
||||
@youdomain openerp_mailgate@localhost
|
||||
|
||||
/etc/aliases:
|
||||
openerp_mailgate: "|/path/to/openerp-mailgate.py --host=localhost -u %(uid)d -p PASSWORD -d %(dbname)s"
|
||||
|
||||
""" % conf
|
||||
|
||||
return {'value':values}
|
||||
|
|
|
@ -49,7 +49,8 @@
|
|||
</group>
|
||||
<group>
|
||||
<field name="company_id" groups="base.group_multi_company" on_change="onchange_company(company_id)"/>
|
||||
<field name="user_id" on_change="onchange_user(user_id)" string="Related User"/>
|
||||
<field name="user_id" on_change="onchange_user(user_id)" string="Related User"
|
||||
context="{'default_groups_ref': ['base.group_user']}"/>
|
||||
</group>
|
||||
</group>
|
||||
<field name="notes" placeholder="Other Information ..." colspan="4"/>
|
||||
|
|
|
@ -160,7 +160,7 @@
|
|||
<button name="button_final_validation" string="Validate Appraisal" states="wait" type="object" class="oe_highlight"/>
|
||||
<button name="button_done" string="Done" states="progress" type="object" class="oe_highlight"/>
|
||||
<button name="button_draft" string="Reset to Draft" states="cancel" type="object" />
|
||||
<button name="button_cancel" string="Cancel" states="draft,wait,progress" type="object"/>
|
||||
<button name="button_cancel" string="Cancel Appraisal" states="draft,wait,progress" type="object"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,progress,wait,done" statusbar_colors='{"progress":"blue"}'/>
|
||||
</header>
|
||||
<sheet>
|
||||
|
@ -282,7 +282,7 @@
|
|||
<field name="arch" type="xml">
|
||||
<form string="Interview Appraisal" version="7.0">
|
||||
<header>
|
||||
<button string="Cancel" name="survey_req_cancel" type="object"
|
||||
<button string="Cancel Survey" name="survey_req_cancel" type="object"
|
||||
states="draft,waiting_answer" class="oe_left"/>
|
||||
<button string="Print Survey" name="action_print_survey" type="object"
|
||||
states="draft" context="{'survey_id': survey_id, 'response_id': [response], 'response_no':0}"
|
||||
|
|
|
@ -39,14 +39,14 @@ The whole flow is implemented as:
|
|||
* Draft expense
|
||||
* Confirmation of the sheet by the employee
|
||||
* Validation by his manager
|
||||
* Validation by the accountant and receipt creation
|
||||
* Validation by the accountant and accounting entries creation
|
||||
|
||||
This module also uses analytic accounting and is compatible with the invoice on timesheet module so that you are able to automatically re-invoice your customers' expenses if your work by project.
|
||||
""",
|
||||
'author': 'OpenERP SA',
|
||||
'website': 'http://www.openerp.com',
|
||||
'images': ['images/hr_expenses_analysis.jpeg', 'images/hr_expenses.jpeg'],
|
||||
'depends': ['hr', 'account_voucher'],
|
||||
'depends': ['hr', 'account_accountant'],
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'hr_expense_data.xml',
|
||||
|
|
|
@ -40,7 +40,7 @@ class hr_expense_expense(osv.osv):
|
|||
if context is None:
|
||||
context = {}
|
||||
if not default: default = {}
|
||||
default.update({'voucher_id': False, 'date_confirm': False, 'date_valid': False, 'user_valid': False})
|
||||
default.update({'date_confirm': False, 'date_valid': False, 'user_valid': False})
|
||||
return super(hr_expense_expense, self).copy(cr, uid, id, default, context=context)
|
||||
|
||||
def _amount(self, cr, uid, ids, field_name, arg, context=None):
|
||||
|
@ -85,7 +85,6 @@ class hr_expense_expense(osv.osv):
|
|||
'line_ids': fields.one2many('hr.expense.line', 'expense_id', 'Expense Lines', readonly=True, states={'draft':[('readonly',False)]} ),
|
||||
'note': fields.text('Note'),
|
||||
'amount': fields.function(_amount, string='Total Amount', digits_compute=dp.get_precision('Account')),
|
||||
'voucher_id': fields.many2one('account.voucher', "Employee's Receipt"),
|
||||
'currency_id': fields.many2one('res.currency', 'Currency', required=True, readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
|
||||
'department_id':fields.many2one('hr.department','Department', readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
|
||||
'company_id': fields.many2one('res.company', 'Company', required=True),
|
||||
|
@ -144,92 +143,233 @@ class hr_expense_expense(osv.osv):
|
|||
def expense_canceled(self, cr, uid, ids, context=None):
|
||||
return self.write(cr, uid, ids, {'state': 'cancelled'}, context=context)
|
||||
|
||||
def action_receipt_create(self, cr, uid, ids, context=None):
|
||||
property_obj = self.pool.get('ir.property')
|
||||
sequence_obj = self.pool.get('ir.sequence')
|
||||
analytic_journal_obj = self.pool.get('account.analytic.journal')
|
||||
account_journal = self.pool.get('account.journal')
|
||||
voucher_obj = self.pool.get('account.voucher')
|
||||
currency_obj = self.pool.get('res.currency')
|
||||
def account_move_get(self, cr, uid, expense_id, context=None):
|
||||
'''
|
||||
This method prepare the creation of the account move related to the given expense.
|
||||
|
||||
:param expense_id: Id of expense for which we are creating account_move.
|
||||
:return: mapping between fieldname and value of account move to create
|
||||
:rtype: dict
|
||||
'''
|
||||
journal_obj = self.pool.get('account.journal')
|
||||
expense = self.browse(cr, uid, expense_id, context=context)
|
||||
company_id = expense.company_id.id
|
||||
date = expense.date_confirm
|
||||
ref = expense.name
|
||||
journal_id = False
|
||||
if expense.journal_id:
|
||||
journal_id = expense.journal_id.id
|
||||
else:
|
||||
journal_id = journal_obj.search(cr, uid, [('type', '=', 'purchase'), ('company_id', '=', company_id)])
|
||||
if not journal_id:
|
||||
raise osv.except_osv(_('Error!'), _("No expense journal found. Please make sure you have a journal with type 'purchase' configured."))
|
||||
journal_id = journal_id[0]
|
||||
return self.pool.get('account.move').account_move_prepare(cr, uid, journal_id, date=date, ref=ref, company_id=company_id, context=context)
|
||||
|
||||
def line_get_convert(self, cr, uid, x, part, date, context=None):
|
||||
partner_id = self.pool.get('res.partner')._find_accounting_partner(part).id
|
||||
return {
|
||||
'date_maturity': x.get('date_maturity', False),
|
||||
'partner_id': partner_id,
|
||||
'name': x['name'][:64],
|
||||
'date': date,
|
||||
'debit': x['price']>0 and x['price'],
|
||||
'credit': x['price']<0 and -x['price'],
|
||||
'account_id': x['account_id'],
|
||||
'analytic_lines': x.get('analytic_lines', False),
|
||||
'amount_currency': x['price']>0 and abs(x.get('amount_currency', False)) or -abs(x.get('amount_currency', False)),
|
||||
'currency_id': x.get('currency_id', False),
|
||||
'tax_code_id': x.get('tax_code_id', False),
|
||||
'tax_amount': x.get('tax_amount', False),
|
||||
'ref': x.get('ref', False),
|
||||
'quantity': x.get('quantity',1.00),
|
||||
'product_id': x.get('product_id', False),
|
||||
'product_uom_id': x.get('uos_id', False),
|
||||
'analytic_account_id': x.get('account_analytic_id', False),
|
||||
}
|
||||
|
||||
def compute_expense_totals(self, cr, uid, exp, company_currency, ref, account_move_lines, context=None):
|
||||
'''
|
||||
internal method used for computation of total amount of an expense in the company currency and
|
||||
in the expense currency, given the account_move_lines that will be created. It also do some small
|
||||
transformations at these account_move_lines (for multi-currency purposes)
|
||||
|
||||
:param account_move_lines: list of dict
|
||||
:rtype: tuple of 3 elements (a, b ,c)
|
||||
a: total in company currency
|
||||
b: total in hr.expense currency
|
||||
c: account_move_lines potentially modified
|
||||
'''
|
||||
cur_obj = self.pool.get('res.currency')
|
||||
if context is None:
|
||||
context = {}
|
||||
for exp in self.browse(cr, uid, ids, context=context):
|
||||
company_id = exp.company_id.id
|
||||
lines = []
|
||||
total = 0.0
|
||||
ctx = context.copy()
|
||||
ctx.update({'date': exp.date})
|
||||
journal = False
|
||||
if exp.journal_id:
|
||||
journal = exp.journal_id
|
||||
context={}
|
||||
context.update({'date': exp.date_confirm or time.strftime('%Y-%m-%d')})
|
||||
total = 0.0
|
||||
total_currency = 0.0
|
||||
for i in account_move_lines:
|
||||
if exp.currency_id.id != company_currency:
|
||||
i['currency_id'] = exp.currency_id.id
|
||||
i['amount_currency'] = i['price']
|
||||
i['price'] = cur_obj.compute(cr, uid, exp.currency_id.id,
|
||||
company_currency, i['price'],
|
||||
context=context)
|
||||
else:
|
||||
journal_id = voucher_obj._get_journal(cr, uid, context={'type': 'purchase', 'company_id': company_id})
|
||||
if journal_id:
|
||||
journal = account_journal.browse(cr, uid, journal_id, context=context)
|
||||
if not journal:
|
||||
raise osv.except_osv(_('Error!'), _("No expense journal found. Please make sure you have a journal with type 'purchase' configured."))
|
||||
for line in exp.line_ids:
|
||||
if line.product_id:
|
||||
acc = line.product_id.property_account_expense
|
||||
if not acc:
|
||||
acc = line.product_id.categ_id.property_account_expense_categ
|
||||
else:
|
||||
acc = property_obj.get(cr, uid, 'property_account_expense_categ', 'product.category', context={'force_company': company_id})
|
||||
if not acc:
|
||||
raise osv.except_osv(_('Error!'), _('Please configure Default Expense account for Product purchase: `property_account_expense_categ`.'))
|
||||
total_amount = line.total_amount
|
||||
if journal.currency:
|
||||
if exp.currency_id != journal.currency:
|
||||
total_amount = currency_obj.compute(cr, uid, exp.currency_id.id, journal.currency.id, total_amount, context=ctx)
|
||||
elif exp.currency_id != exp.company_id.currency_id:
|
||||
total_amount = currency_obj.compute(cr, uid, exp.currency_id.id, exp.company_id.currency_id.id, total_amount, context=ctx)
|
||||
lines.append((0, False, {
|
||||
'name': line.name,
|
||||
'account_id': acc.id,
|
||||
'account_analytic_id': line.analytic_account.id,
|
||||
'amount': total_amount,
|
||||
'type': 'dr'
|
||||
}))
|
||||
total += total_amount
|
||||
i['amount_currency'] = False
|
||||
i['currency_id'] = False
|
||||
total -= i['price']
|
||||
total_currency -= i['amount_currency'] or i['price']
|
||||
return total, total_currency, account_move_lines
|
||||
|
||||
def action_move_create(self, cr, uid, ids, context=None):
|
||||
'''
|
||||
main function that is called when trying to create the accounting entries related to an expense
|
||||
'''
|
||||
move_obj = self.pool.get('account.move')
|
||||
for exp in self.browse(cr, uid, ids, context=context):
|
||||
if not exp.employee_id.address_home_id:
|
||||
raise osv.except_osv(_('Error!'), _('The employee must have a home address.'))
|
||||
company_currency = exp.company_id.currency_id.id
|
||||
diff_currency_p = exp.currency_id.id <> company_currency
|
||||
|
||||
#create the move that will contain the accounting entries
|
||||
move_id = move_obj.create(cr, uid, self.account_move_get(cr, uid, exp.id, context=context), context=context)
|
||||
|
||||
#one account.move.line per expense line (+taxes..)
|
||||
eml = self.move_line_get(cr, uid, exp.id, context=context)
|
||||
|
||||
#create one more move line, a counterline for the total on payable account
|
||||
total, total_currency, eml = self.compute_expense_totals(cr, uid, exp, company_currency, exp.name, eml, context=context)
|
||||
acc = exp.employee_id.address_home_id.property_account_payable.id
|
||||
voucher = {
|
||||
'name': exp.name or '/',
|
||||
'reference': sequence_obj.get(cr, uid, 'hr.expense.invoice'),
|
||||
'account_id': acc,
|
||||
'type': 'purchase',
|
||||
'partner_id': exp.employee_id.address_home_id.id,
|
||||
'company_id': company_id,
|
||||
'line_ids': lines,
|
||||
'amount': total,
|
||||
'journal_id': journal.id,
|
||||
}
|
||||
if journal and not journal.analytic_journal_id:
|
||||
analytic_journal_ids = analytic_journal_obj.search(cr, uid, [('type','=','purchase')], context=context)
|
||||
if analytic_journal_ids:
|
||||
account_journal.write(cr, uid, [journal.id], {'analytic_journal_id': analytic_journal_ids[0]}, context=context)
|
||||
voucher_id = voucher_obj.create(cr, uid, voucher, context=context)
|
||||
self.write(cr, uid, [exp.id], {'voucher_id': voucher_id, 'state': 'done'}, context=context)
|
||||
eml.append({
|
||||
'type': 'dest',
|
||||
'name': '/',
|
||||
'price': total,
|
||||
'account_id': acc,
|
||||
'date_maturity': exp.date_confirm,
|
||||
'amount_currency': diff_currency_p and total_currency or False,
|
||||
'currency_id': diff_currency_p and exp.currency_id.id or False,
|
||||
'ref': exp.name
|
||||
})
|
||||
|
||||
#convert eml into an osv-valid format
|
||||
lines = map(lambda x:(0,0,self.line_get_convert(cr, uid, x, exp.employee_id.address_home_id, exp.date_confirm, context=context)), eml)
|
||||
move_obj.write(cr, uid, [move_id], {'line_id': lines}, context=context)
|
||||
self.write(cr, uid, ids, {'account_move_id': move_id, 'state': 'done'}, context=context)
|
||||
return True
|
||||
|
||||
def action_view_receipt(self, cr, uid, ids, context=None):
|
||||
|
||||
def move_line_get(self, cr, uid, expense_id, context=None):
|
||||
res = []
|
||||
tax_obj = self.pool.get('account.tax')
|
||||
cur_obj = self.pool.get('res.currency')
|
||||
if context is None:
|
||||
context = {}
|
||||
exp = self.browse(cr, uid, expense_id, context=context)
|
||||
company_currency = exp.company_id.currency_id.id
|
||||
|
||||
for line in exp.line_ids:
|
||||
mres = self.move_line_get_item(cr, uid, line, context)
|
||||
if not mres:
|
||||
continue
|
||||
res.append(mres)
|
||||
tax_code_found= False
|
||||
|
||||
#Calculate tax according to default tax on product
|
||||
|
||||
taxes = []
|
||||
#Taken from product_id_onchange in account.invoice
|
||||
if line.product_id:
|
||||
fposition_id = False
|
||||
fpos_obj = self.pool.get('account.fiscal.position')
|
||||
fpos = fposition_id and fpos_obj.browse(cr, uid, fposition_id, context=context) or False
|
||||
product = line.product_id
|
||||
taxes = product.supplier_taxes_id
|
||||
#If taxes are not related to the product, maybe they are in the account
|
||||
if not taxes:
|
||||
a = product.property_account_expense.id #Why is not there a check here?
|
||||
if not a:
|
||||
a = product.categ_id.property_account_expense_categ.id
|
||||
a = fpos_obj.map_account(cr, uid, fpos, a)
|
||||
taxes = a and self.pool.get('account.account').browse(cr, uid, a, context=context).tax_ids or False
|
||||
tax_id = fpos_obj.map_tax(cr, uid, fpos, taxes)
|
||||
if not taxes:
|
||||
continue
|
||||
#Calculating tax on the line and creating move?
|
||||
for tax in tax_obj.compute_all(cr, uid, taxes,
|
||||
line.unit_amount ,
|
||||
line.unit_quantity, line.product_id,
|
||||
exp.user_id.partner_id)['taxes']:
|
||||
tax_code_id = tax['base_code_id']
|
||||
tax_amount = line.total_amount * tax['base_sign']
|
||||
if tax_code_found:
|
||||
if not tax_code_id:
|
||||
continue
|
||||
res.append(self.move_line_get_item(cr, uid, line, context))
|
||||
res[-1]['price'] = 0.0
|
||||
res[-1]['account_analytic_id'] = False
|
||||
elif not tax_code_id:
|
||||
continue
|
||||
tax_code_found = True
|
||||
res[-1]['tax_code_id'] = tax_code_id
|
||||
res[-1]['tax_amount'] = cur_obj.compute(cr, uid, exp.currency_id.id, company_currency, tax_amount, context={'date': exp.date_confirm})
|
||||
|
||||
#Will create the tax here as we don't have the access
|
||||
assoc_tax = {
|
||||
'type':'tax',
|
||||
'name':tax['name'],
|
||||
'price_unit': tax['price_unit'],
|
||||
'quantity': 1,
|
||||
'price': tax['amount'] * tax['base_sign'] or 0.0,
|
||||
'account_id': tax['account_collected_id'],
|
||||
'tax_code_id': tax['tax_code_id'],
|
||||
'tax_amount': tax['amount'] * tax['base_sign'],
|
||||
}
|
||||
res.append(assoc_tax)
|
||||
return res
|
||||
|
||||
def move_line_get_item(self, cr, uid, line, context=None):
|
||||
company = line.expense_id.company_id
|
||||
property_obj = self.pool.get('ir.property')
|
||||
if line.product_id:
|
||||
acc = line.product_id.property_account_expense
|
||||
if not acc:
|
||||
acc = line.product_id.categ_id.property_account_expense_categ
|
||||
else:
|
||||
acc = property_obj.get(cr, uid, 'property_account_expense_categ', 'product.category', context={'force_company': company.id})
|
||||
if not acc:
|
||||
raise osv.except_osv(_('Error!'), _('Please configure Default Expense account for Product purchase: `property_account_expense_categ`.'))
|
||||
return {
|
||||
'type':'src',
|
||||
'name': line.name.split('\n')[0][:64],
|
||||
'price_unit':line.unit_amount,
|
||||
'quantity':line.unit_quantity,
|
||||
'price':line.total_amount,
|
||||
'account_id':acc.id,
|
||||
'product_id':line.product_id.id,
|
||||
'uos_id':line.uom_id.id,
|
||||
'account_analytic_id':line.analytic_account.id,
|
||||
}
|
||||
|
||||
def action_view_move(self, cr, uid, ids, context=None):
|
||||
'''
|
||||
This function returns an action that display existing receipt of given expense ids.
|
||||
This function returns an action that display existing account.move of given expense ids.
|
||||
'''
|
||||
assert len(ids) == 1, 'This option should only be used for a single id at a time'
|
||||
voucher_id = self.browse(cr, uid, ids[0], context=context).voucher_id.id
|
||||
res = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account_voucher', 'view_purchase_receipt_form')
|
||||
expense = self.browse(cr, uid, ids[0], context=context)
|
||||
assert expense.account_move_id
|
||||
try:
|
||||
dummy, view_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account', 'view_move_form')
|
||||
except ValueError, e:
|
||||
view_id = False
|
||||
result = {
|
||||
'name': _('Expense Receipt'),
|
||||
'name': _('Expense Account Move'),
|
||||
'view_type': 'form',
|
||||
'view_mode': 'form',
|
||||
'view_id': res and res[1] or False,
|
||||
'res_model': 'account.voucher',
|
||||
'view_id': view_id,
|
||||
'res_model': 'account.move',
|
||||
'type': 'ir.actions.act_window',
|
||||
'nodestroy': True,
|
||||
'target': 'current',
|
||||
'res_id': voucher_id,
|
||||
'res_id': expense.account_move_id.id,
|
||||
}
|
||||
return result
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@
|
|||
<button name="refuse" states="confirm,accepted" string="Refuse" type="workflow" groups="base.group_hr_user" />
|
||||
<button name="draft" states="confirm,cancelled" string="Set to Draft" type="workflow" groups="base.group_hr_user" />
|
||||
<button name="done" states="accepted" string="Generate Accounting Entries" type="workflow" groups="account.group_account_invoice" class="oe_highlight"/>
|
||||
<button name="action_view_receipt" states="done" string="Open Receipt" type="object"/>
|
||||
<button name="action_view_move" states="done" string="Open Accounting Entries" type="object" groups="account.group_account_invoice"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,confirm,accepted,done" statusbar_colors='{"confirm":"blue","cancelled":"red"}'/>
|
||||
</header>
|
||||
<sheet>
|
||||
|
@ -133,7 +133,7 @@
|
|||
<group>
|
||||
<group string="Accounting Data">
|
||||
<field name="journal_id" widget="selection" domain="[('type', '=', 'purchase')]"/>
|
||||
<field name="voucher_id" context="{'form_view_ref': 'account_voucher.view_purchase_receipt_form'}"/>
|
||||
<field name="account_move_id"/>
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
<field name="wkf_id" ref="wkf_expenses"/>
|
||||
<field name="name">done</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">action_receipt_create()</field>
|
||||
<field name="action">action_move_create()</field>
|
||||
</record>
|
||||
|
||||
<record id="t1" model="workflow.transition">
|
||||
|
|
|
@ -43,7 +43,6 @@ class hr_expense_report(osv.osv):
|
|||
'employee_id': fields.many2one('hr.employee', "Employee's Name", readonly=True),
|
||||
'date_confirm': fields.date('Confirmation Date', readonly=True),
|
||||
'date_valid': fields.date('Validation Date', readonly=True),
|
||||
'voucher_id': fields.many2one('account.voucher', 'Receipt', readonly=True),
|
||||
'department_id':fields.many2one('hr.department','Department', readonly=True),
|
||||
'company_id':fields.many2one('res.company', 'Company', readonly=True),
|
||||
'user_id':fields.many2one('res.users', 'Validation User', readonly=True),
|
||||
|
@ -77,7 +76,6 @@ class hr_expense_report(osv.osv):
|
|||
s.currency_id,
|
||||
to_date(to_char(s.date_confirm, 'dd-MM-YYYY'),'dd-MM-YYYY') as date_confirm,
|
||||
to_date(to_char(s.date_valid, 'dd-MM-YYYY'),'dd-MM-YYYY') as date_valid,
|
||||
s.voucher_id,
|
||||
s.user_valid as user_id,
|
||||
s.department_id,
|
||||
to_char(date_trunc('day',s.create_date), 'YYYY') as year,
|
||||
|
@ -107,7 +105,6 @@ class hr_expense_report(osv.osv):
|
|||
to_date(to_char(s.date_valid, 'dd-MM-YYYY'),'dd-MM-YYYY'),
|
||||
l.product_id,
|
||||
l.analytic_account,
|
||||
s.voucher_id,
|
||||
s.currency_id,
|
||||
s.user_valid,
|
||||
s.department_id,
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
<field name="year" invisible="1"/>
|
||||
<field name="month" invisible="1"/>
|
||||
<field name="day" invisible="1"/>
|
||||
<field name="voucher_id" invisible="1"/>
|
||||
<field name="analytic_account" invisible="1" groups="analytic.group_analytic_accounting"/>
|
||||
<field name="department_id" invisible="1"/>
|
||||
<field name="company_id" invisible="1"/>
|
||||
|
|
|
@ -26,11 +26,6 @@
|
|||
!python {model: hr.expense.expense}: |
|
||||
sep_expenses = self.browse(cr, uid, ref("sep_expenses"), context=context)
|
||||
assert sep_expenses.state == 'done', "Expense should be in 'Done' state."
|
||||
assert sep_expenses.voucher_id, "Expense should have link of Purchase Receipt."
|
||||
assert sep_expenses.voucher_id.type == 'purchase', "Receipt type is not purchase receipt."
|
||||
assert sep_expenses.voucher_id.amount == sep_expenses.amount,"Receipt total amount is not correspond with expense total."
|
||||
assert len(sep_expenses.voucher_id.line_dr_ids) == len(sep_expenses.line_ids),"Lines of Receipt and expense line are not correspond."
|
||||
|
||||
-
|
||||
I duplicate the expenses and cancel duplicated.
|
||||
-
|
||||
|
|
|
@ -374,6 +374,7 @@
|
|||
<field name="model">hr.holidays.status</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Leave Type" version="7.0">
|
||||
<sheet string="Leave Type">
|
||||
<group col="4">
|
||||
<field name="name"/>
|
||||
<field name="categ_id"/>
|
||||
|
@ -392,6 +393,7 @@
|
|||
</group>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
|
|
@ -217,7 +217,7 @@
|
|||
<button string="Refund" name="refund_sheet" states="confirm,done" type='object' />
|
||||
<button string="Set to Draft" name="draft" states="cancel"/>
|
||||
<button string="Compute Sheet" name="compute_sheet" type="object" states="draft" class="oe_highlight"/>
|
||||
<button string="Cancel" name="cancel_sheet" states="draft,hr_check,confirm,verify"/>
|
||||
<button string="Cancel Payslip" name="cancel_sheet" states="draft,hr_check,confirm,verify"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,confirm"/>
|
||||
</header>
|
||||
<sheet>
|
||||
|
|
|
@ -340,6 +340,15 @@ class hr_applicant(base_stage, osv.Model):
|
|||
value = self.pool.get("survey").action_print_survey(cr, uid, ids, context=context)
|
||||
return value
|
||||
|
||||
def message_get_suggested_recipients(self, cr, uid, ids, context=None):
|
||||
recipients = super(hr_applicant, self).message_get_suggested_recipients(cr, uid, ids, context=context)
|
||||
for applicant in self.browse(cr, uid, ids, context=context):
|
||||
if applicant.partner_id:
|
||||
self._message_add_suggested_recipient(cr, uid, recipients, applicant, partner=applicant.partner_id, reason=_('Contact'))
|
||||
elif applicant.email_from:
|
||||
self._message_add_suggested_recipient(cr, uid, recipients, applicant, email=applicant.email_from, reason=_('Contact Email'))
|
||||
return recipients
|
||||
|
||||
def message_new(self, cr, uid, msg, custom_values=None, context=None):
|
||||
""" Overrides mail_thread message_new that is called by the mailgateway
|
||||
through message_process.
|
||||
|
|
|
@ -107,7 +107,8 @@
|
|||
<field name="type_id" placeholder="Degree"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="user_id"/>
|
||||
<field name="user_id"
|
||||
context="{'default_groups_ref': ['base.group_user', 'base.group_hr_manager']}"/>
|
||||
<label for="title_action"/>
|
||||
<div>
|
||||
<field name="date_action"/>
|
||||
|
@ -151,7 +152,7 @@
|
|||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="message_ids" widget="mail_thread" placeholder="Share a message..."/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
|
@ -237,6 +238,7 @@
|
|||
<field name="title_action"/>
|
||||
<field name="department_id"/>
|
||||
<field name="categ_ids"/>
|
||||
<field name="message_summary"/>
|
||||
<templates>
|
||||
<t t-name="kanban-tooltip">
|
||||
<ul class="oe_kanban_tooltip">
|
||||
|
@ -282,6 +284,7 @@
|
|||
|
||||
</div>
|
||||
<div class="oe_kanban_footer_left" style="margin-top:5px;">
|
||||
<t t-raw="record.message_summary.raw_value"/>
|
||||
<field name="categ_ids"/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -343,6 +346,7 @@
|
|||
<field name="model">hr.recruitment.stage</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Stage" version="7.0">
|
||||
<sheet>
|
||||
<group string="Stage Definition">
|
||||
<group>
|
||||
<field name="name"/>
|
||||
|
@ -356,6 +360,7 @@
|
|||
</group>
|
||||
<separator string="Requirements"/>
|
||||
<field name="requirements"/>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
@ -401,10 +406,12 @@
|
|||
<field name="model">hr.recruitment.degree</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Degree" version="7.0">
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="name"/>
|
||||
<field name="sequence" groups="base.group_no_one"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
@ -438,8 +445,12 @@
|
|||
<field name="name">hr.recruitment.source.form</field>
|
||||
<field name="model">hr.recruitment.source</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Sources of Applicants">
|
||||
<field name="name"/>
|
||||
<form string="Sources of Applicants" version="7.0">
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="name"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
<field name="arch" type="xml">
|
||||
<tree editable="top" string="Timesheet Activities">
|
||||
<field name="date" on_change="on_change_date(date)"/>
|
||||
<field name="user_id" on_change="on_change_user_id(user_id)" required="1" options='{"no_open": True}'/>
|
||||
<field name="user_id" on_change="on_change_user_id(user_id)" required="1" options='{"no_open": True}'
|
||||
context="{'default_groups_ref': [base.group_user']}"/>
|
||||
<field name="name"/>
|
||||
<field domain="[('type','=','normal'),('use_timesheets','=',1)]" name="account_id" context="{'default_use_timesheets': 1, 'default_type': 'contract'}"/>
|
||||
<field name="unit_amount" string="Duration" on_change="on_change_unit_amount(product_id, unit_amount, False, product_uom_id,journal_id)" sum="Total time" widget="float_time"/>
|
||||
|
@ -29,7 +30,8 @@
|
|||
<group>
|
||||
<group>
|
||||
<field name="name"/>
|
||||
<field name="user_id" on_change="on_change_user_id(user_id)" required="1"/>
|
||||
<field name="user_id" on_change="on_change_user_id(user_id)" required="1"
|
||||
context="{'default_groups_ref': [base.group_user']}"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="date" on_change="on_change_date(date)"/>
|
||||
|
|
|
@ -213,6 +213,7 @@ class hr_timesheet_sheet(osv.osv):
|
|||
|
||||
def onchange_employee_id(self, cr, uid, ids, employee_id, context=None):
|
||||
department_id = False
|
||||
user_id = False
|
||||
if employee_id:
|
||||
empl_id = self.pool.get('hr.employee').browse(cr, uid, employee_id, context=context)
|
||||
department_id = empl_id.department_id.id
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue