[MERGE] merged the branch of jco with account_followup improvement, fixes and refactoring. It also includes a fix of the function search on credit field of res.partner that wasn't given proper results if a partner had no account.move.line

bzr revid: qdp-launchpad@openerp.com-20121207094559-q4o6p2gj2d334pad
This commit is contained in:
Quentin (OpenERP) 2012-12-07 10:45:59 +01:00
commit 354a55715e
11 changed files with 282 additions and 300 deletions

View File

@ -456,7 +456,7 @@ class account_move_line(osv.osv):
store = {
'account.move': (_get_move_lines, ['period_id'], 20)
}),
'blocked': fields.boolean('Litigation', help="You can check this box to mark this journal item as a litigation with the associated partner"),
'blocked': fields.boolean('No Follow-up', help="You can check this box to mark this journal item as a litigation with the associated partner"),
'partner_id': fields.many2one('res.partner', 'Partner', select=1, ondelete='restrict'),
'date_maturity': fields.date('Due date', select=True ,help="This field is used for payable and receivable journal entries. You can put the limit date for the payment of this line."),
'date': fields.related('move_id','date', string='Effective date', type='date', required=True, select=True,

View File

@ -134,18 +134,23 @@ class res_partner(osv.osv):
return []
having_values = tuple(map(itemgetter(2), args))
where = ' AND '.join(
map(lambda x: '(SUM(debit-credit) %(operator)s %%s)' % {
map(lambda x: '(SUM(bal2) %(operator)s %%s)' % {
'operator':x[1]},args))
query = self.pool.get('account.move.line')._query_get(cr, uid, context=context)
cr.execute(('SELECT partner_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 '+query+' '\
'AND partner_id IS NOT NULL '\
'GROUP BY partner_id HAVING '+where),
(type,) + having_values)
cr.execute(('SELECT pid AS partner_id, SUM(bal2) FROM ' \
'(SELECT CASE WHEN bal IS NOT NULL THEN bal ' \
'ELSE 0.0 END AS bal2, p.id as pid FROM ' \
'(SELECT (debit-credit) AS bal, partner_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 '+query+') AS l ' \
'RIGHT JOIN res_partner p ' \
'ON p.id = partner_id ) AS pl ' \
'GROUP BY pid HAVING ' + where),
(type,) + having_values)
res = cr.fetchall()
if not res:
return [('id','=','0')]

View File

@ -24,7 +24,6 @@ from lxml import etree
from tools.translate import _
class followup(osv.osv):
_name = 'account_followup.followup'
_description = 'Account Follow-up'
@ -74,7 +73,7 @@ class followup_line(osv.osv):
Exception made if there was a mistake of ours, it seems that the following amount stays unpaid. Please, take appropriate measures in order to carry out this payment in the next 8 days.
Would your payment have been carried out after this mail was sent, please ignore this message. Do not hesitate to contact our accounting department at (+32).10.68.94.39.
Would your payment have been carried out after this mail was sent, please ignore this message. Do not hesitate to contact our accounting department.
Best Regards,
""",
@ -118,12 +117,61 @@ class account_move_line(osv.osv):
class email_template(osv.osv):
_inherit = 'email.template'
# Adds current_date to the context. That way it can be used to put
# the account move lines in bold that are overdue in the email
def render_template(self, cr, uid, template, model, res_id, context=None):
context['current_date'] = fields.date.context_today(cr, uid, context)
return super(email_template, self).render_template(cr, uid, template, model, res_id, context=context)
def _get_followup_table_html(self, cr, uid, res_id, context=None):
'''
Build the html tables to be included in emails send to partners, when reminding them their
overdue invoices.
:param res_id: ID of the partner for whom we are building the tables
:rtype: string
'''
from report import account_followup_print
partner = self.pool.get('res.partner').browse(cr, uid, res_id, context=context)
followup_table = ''
if partner.unreconciled_aml_ids:
company = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id
current_date = fields.date.context_today(cr, uid, context)
rml_parse = account_followup_print.report_rappel(cr, uid, "followup_rml_parser")
final_res = rml_parse._lines_get_with_partner(partner, company.id)
for currency_dict in final_res:
currency = currency_dict.get('line', [{'currency_id': company.currency_id}])[0]['currency_id']
followup_table += '''
<table border="2" width=100%%>
<tr>
<td>Invoice date</td>
<td>Reference</td>
<td>Due date</td>
<td>Amount (%s)</td>
<td>Lit.</td>
</tr>
''' % (currency.symbol)
total = 0
for aml in currency_dict['line']:
block = aml['blocked'] and 'X' or ' '
total += aml['balance']
strbegin = "<TD>"
strend = "</TD>"
date = aml['date_maturity'] or aml['date']
if date <= current_date and aml['balance'] > 0:
strbegin = "<TD><B>"
strend = "</B></TD>"
followup_table +="<TR>" + strbegin + str(aml['date']) + strend + strbegin + aml['ref'] + strend + strbegin + str(date) + strend + strbegin + str(aml['balance']) + strend + strbegin + block + strend + "</TR>"
total = rml_parse.formatLang(total, dp='Account', currency_obj=currency)
followup_table += '''<tr> </tr>
</table>
<center>Amount due: %s </center>''' % (total)
return followup_table
def render_template(self, cr, uid, template, model, res_id, context=None):
if model == 'res.partner' and context.get('followup'):
context['followup_table'] = self._get_followup_table_html(cr, uid, res_id, context=context)
# Adds current_date to the context. That way it can be used to put
# the account move lines in bold that are overdue in the email
context['current_date'] = fields.date.context_today(cr, uid, context)
return super(email_template, self).render_template(cr, uid, template, model, res_id, context=context)
class res_partner(osv.osv):
@ -209,32 +257,36 @@ class res_partner(osv.osv):
}
def do_partner_mail(self, cr, uid, partner_ids, context=None):
if context is None:
context = {}
ctx = context.copy()
ctx['followup'] = True
#partner_ids are res.partner ids
# If not defined by latest follow-up level, it will be the default template if it can find it
mtp = self.pool.get('email.template')
unknown_mails = 0
for partner in self.browse(cr, uid, partner_ids, context=context):
for partner in self.browse(cr, uid, partner_ids, context=ctx):
if partner.email and partner.email.strip():
level = partner.latest_followup_level_id_without_lit
if level and level.send_email and level.email_template_id and level.email_template_id.id:
mtp.send_mail(cr, uid, level.email_template_id.id, partner.id, context=context)
mtp.send_mail(cr, uid, level.email_template_id.id, partner.id, context=ctx)
else:
mail_template_id = self.pool.get('ir.model.data').get_object_reference(cr, uid,
'account_followup', 'email_template_account_followup_default')
mtp.send_mail(cr, uid, mail_template_id[1], partner.id, context=context)
mtp.send_mail(cr, uid, mail_template_id[1], partner.id, context=ctx)
else:
unknown_mails = unknown_mails + 1
action_text = _("Email not sent because of email address of partner not filled in")
if partner.payment_next_action_date:
payment_action_date = min(fields.date.context_today(cr, uid, context), partner.payment_next_action_date)
payment_action_date = min(fields.date.context_today(cr, uid, ctx), partner.payment_next_action_date)
else:
payment_action_date = fields.date.context_today(cr, uid, context)
payment_action_date = fields.date.context_today(cr, uid, ctx)
if partner.payment_next_action:
payment_next_action = partner.payment_next_action + " + " + action_text
payment_next_action = partner.payment_next_action + " \n " + action_text
else:
payment_next_action = action_text
self.write(cr, uid, [partner.id], {'payment_next_action_date': payment_action_date,
'payment_next_action': payment_next_action}, context=context)
'payment_next_action': payment_next_action}, context=ctx)
return unknown_mails
def action_done(self, cr, uid, ids, context=None):
@ -256,21 +308,122 @@ class res_partner(osv.osv):
}
def _get_amounts_and_date(self, cr, uid, ids, name, arg, context=None):
'''
Function that computes values for the followup functional fields. Note that 'payment_amount_due'
is similar to 'credit' field on res.partner except it filters on user's company.
'''
res = {}
company = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id
current_date = fields.date.context_today(cr, uid, context)
for partner in self.browse(cr, uid, ids, context=context):
worst_due_date = False
amount_due = amount_overdue = 0.0
for aml in partner.unreconciled_aml_ids:
if (aml.company_id == company):
date_maturity = aml.date_maturity or aml.date
if not worst_due_date or date_maturity < worst_due_date:
worst_due_date = date_maturity
amount_due += aml.result
if (date_maturity <= current_date):
amount_overdue += aml.result
res[partner.id] = {'payment_amount_due': amount_due,
'payment_amount_overdue': amount_overdue,
'payment_earliest_due_date': worst_due_date}
return res
def _get_followup_overdue_query(self, cr, uid, args, overdue_only=False, context=None):
'''
This function is used to build the query and arguments to use when making a search on functional fields
* payment_amount_due
* payment_amount_overdue
Basically, the query is exactly the same except that for overdue there is an extra clause in the WHERE.
:param args: arguments given to the search in the usual domain notation (list of tuples)
:param overdue_only: option to add the extra argument to filter on overdue accounting entries or not
:returns: a tuple with
* the query to execute as first element
* the arguments for the execution of this query
:rtype: (string, [])
'''
company_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id
having_where_clause = ' AND '.join(map(lambda x: '(SUM(bal2) %s %%s)' % (x[1]), args))
having_values = [x[2] for x in args]
query = self.pool.get('account.move.line')._query_get(cr, uid, context=context)
overdue_only_str = overdue_only and 'AND date_maturity <= NOW()' or ''
return ('''SELECT pid AS partner_id, SUM(bal2) FROM
(SELECT CASE WHEN bal IS NOT NULL THEN bal
ELSE 0.0 END AS bal2, p.id as pid FROM
(SELECT (debit-credit) AS bal, partner_id
FROM account_move_line l
WHERE account_id IN
(SELECT id FROM account_account
WHERE type=\'receivable\' AND active)
''' + overdue_only_str + '''
AND reconcile_id IS NULL
AND company_id = %s
AND ''' + query + ''') AS l
RIGHT JOIN res_partner p
ON p.id = partner_id ) AS pl
GROUP BY pid HAVING ''' + having_where_clause, [company_id] + having_values)
def _payment_overdue_search(self, cr, uid, obj, name, args, context=None):
if not args:
return []
query, query_args = self._get_followup_overdue_query(cr, uid, args, overdue_only=True, context=context)
cr.execute(query, query_args)
res = cr.fetchall()
if not res:
return [('id','=','0')]
return [('id','in', [x[0] for x in res])]
def _payment_earliest_date_search(self, cr, uid, obj, name, args, context=None):
if not args:
return []
company_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id
having_where_clause = ' AND '.join(map(lambda x: '(MIN(l.date_maturity) %s %%s)' % (x[1]), args))
having_values = [x[2] for x in args]
query = self.pool.get('account.move.line')._query_get(cr, uid, context=context)
cr.execute('SELECT partner_id FROM account_move_line l '\
'WHERE account_id IN '\
'(SELECT id FROM account_account '\
'WHERE type=\'receivable\' AND active) '\
'AND l.company_id = %s '
'AND reconcile_id IS NULL '\
'AND '+query+' '\
'AND partner_id IS NOT NULL '\
'GROUP BY partner_id HAVING '+ having_where_clause,
[company_id] + having_values)
res = cr.fetchall()
if not res:
return [('id','=','0')]
return [('id','in', [x[0] for x in res])]
def _payment_due_search(self, cr, uid, obj, name, args, context=None):
if not args:
return []
query, query_args = self._get_followup_overdue_query(cr, uid, args, overdue_only=False, context=context)
cr.execute(query, query_args)
res = cr.fetchall()
if not res:
return [('id','=','0')]
return [('id','in', [x[0] for x in res])]
_inherit = "res.partner"
_columns = {
'payment_responsible_id':fields.many2one('res.users', ondelete='set null', string='Follow-up Responsible',
help="Responsible for making sure the action happens."),
help="Optionally you can assign a user to this field, which will make him responsible for the action."),
'payment_note':fields.text('Customer Payment Promise', help="Payment Note"),
'payment_next_action':fields.text('Next Action',
help="This is the next action to be taken by the user. It will automatically be set when the action fields are empty and the partner gets a follow-up level that requires a manual action. "),
'payment_next_action':fields.text('Next Action',
help="This is the next action to be taken. It will automatically be set when the partner gets a follow-up level that requires a manual action. "),
'payment_next_action_date':fields.date('Next Action Date',
help="This is when further follow-up is needed. The date will have been set to the current date if the action fields are empty and the partner gets a follow-up level that requires a manual action. "),
help="This is when the manual follow-up is needed. " \
"The date will be set to the current date when the partner gets a follow-up level that requires a manual action. Can be practical to set manually e.g. to see if he keeps his promises."),
'unreconciled_aml_ids':fields.one2many('account.move.line', 'partner_id', domain=['&', ('reconcile_id', '=', False), '&',
('account_id.active','=', True), '&', ('account_id.type', '=', 'receivable'), ('state', '!=', 'draft')]),
'latest_followup_date':fields.function(_get_latest, method=True, type='date', string="Latest Follow-up Date",
help="Latest date that the follow-up level of the partner was changed",
store=False,
multi="latest"),
store=False, multi="latest"),
'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",
@ -281,7 +434,19 @@ class res_partner(osv.osv):
help="The maximum follow-up level without taking into account the account move lines with litigation",
store=False,
multi="latest"),
'payment_amount_due':fields.related('credit', type='float', string="Total amount due", readonly=True),
'payment_amount_due':fields.function(_get_amounts_and_date,
type='float', string="Amount Due",
store = False, multi="followup",
fnct_search=_payment_due_search),
'payment_amount_overdue':fields.function(_get_amounts_and_date,
type='float', string="Amount Overdue",
store = False, multi="followup",
fnct_search = _payment_overdue_search),
'payment_earliest_due_date':fields.function(_get_amounts_and_date,
type='date',
string = "Worst Due Date",
multi="followup",
fnct_search=_payment_earliest_date_search),
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -16,7 +16,9 @@
<field name="user_id" invisible="1"/>
<field name="parent_id" invisible="1"/>
<field name="payment_responsible_id"/>
<field name="credit"/>
<field name="payment_earliest_due_date"/>
<field name="payment_amount_overdue"/>
<field name="payment_amount_due" />
</tree>
</field>
</record>
@ -28,10 +30,9 @@
<field name="arch" type="xml">
<search string="Search Partner" position="inside">
<group string="Follow-up">
<filter string="Partners with Credits" domain="[('credit', '>', 0.0)]" name="credits"/>
<filter string="Partners with Overdue Credits" domain="[('payment_amount_overdue', '>', 0.0)]" name="credits"/>
<separator/>
<filter string="Follow-ups To Do" domain="[('payment_next_action_date', '&lt;=', time.strftime('%%Y-%%m-%%d')), ('credit', '>', 0.0)]" name="todo"/>
<filter string="Future Follow-ups" domain="[('payment_next_action_date', '&gt;', time.strftime('%%Y-%%m-%%d')), ('credit', '>', 0.0)]"/>
<filter string="Follow-ups To Do" domain="[('payment_next_action_date', '&lt;=', time.strftime('%%Y-%%m-%%d')), ('payment_amount_overdue', '>', 0.0)]" name="todo"/>
<separator/>
<filter string="No Responsible" domain="[('payment_responsible_id', '=', False)]"/>
<filter string="My Follow-ups" domain="[('payment_responsible_id','=', uid)]"/>
@ -43,29 +44,7 @@
</field>
</record>
<record id="customer_followup_search_view2" model="ir.ui.view">
<field name="name">Search</field>
<field name="model">res.partner</field>
<field name="arch" type="xml">
<search string="Search view">
<field name="name"/>
<field name="payment_next_action"/>
<!--<filter string="Actions to be taken with overdue amount" domain="['&amp;', ('payment_amount_outstanding', '>', 0.0), ('payment_next_action_date', '&lt;=', time.strftime('%%Y-%%m-%%d'))]"/>
<separator/>-->
<filter string="Overdue amount" domain="[('credit', '>', 0.0)]"/>
<separator/>
<filter string="Follow-ups to do" domain="[('payment_next_action_date', '&lt;=', time.strftime('%%Y-%%m-%%d'))]"/>
<separator/>
<!--filter string="Future follow-ups" domain="['&', ('payment_next_action', '!=', ''), ('payment_next_action_date', '>', time.strftime('%%Y-%%m-%%d'))]"/>-->
<separator/>
<filter string="Without responsible" domain="[('payment_responsible_id', '=', False)]"/>
<filter string="I am responsible" domain="[('payment_responsible_id','=', uid)]"/>
<group expand="1" string="Group by">
<filter string="Responsible" context="{'group_by':'payment_responsible_id'}"/>
</group>
</search>
</field>
</record>
<record id="action_customer_followup" model="ir.actions.act_window">
<field name="name">Manual Follow-Ups</field>
@ -73,7 +52,7 @@
<field name="res_model">res.partner</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="context">{} </field>
<field name="domain">[('payment_amount_due', '>', 0.0)]</field>
<field name="context">{'Followupfirst':True, 'search_default_todo': True} </field>
<field name="search_view_id" ref="customer_followup_search_view"/>
</record>
@ -88,9 +67,9 @@
<page string="Payment Follow-up" groups="account.group_account_invoice" name="followup_tab">
<div class="oe_right oe_button_box" name="followup_button">
<button name="do_button_print" type="object" string="Print Overdue Payments" groups="account.group_account_user"
help="Print overdue payments report independent of follow-up line" attrs="{'invisible':[('credit', '&lt;=', 0.0)]}" />
help="Print overdue payments report independent of follow-up line" attrs="{'invisible':[('payment_amount_due', '&lt;=', 0.0)]}" />
<button name="do_partner_mail" type="object" string="Send Overdue Email" groups="account.group_account_user"
help="If not specified by the latest follow-up level, it will send from the default follow-up of overdue invoices template" attrs="{'invisible':[('credit', '&lt;=', 0.0)]}"/>
help="If not specified by the latest follow-up level, it will send from the default email template" attrs="{'invisible':[('payment_amount_due', '&lt;=', 0.0)]}"/>
</div>
<p attrs="{'invisible':[('latest_followup_date','=', False)]}">
The <field name="latest_followup_date" class = "oe_inline"/>, the latest payment follow-up
@ -105,22 +84,22 @@
help="Click to mark the action as done." class="oe_link"
attrs="{'invisible':[('payment_next_action_date','=', False)]}"
groups="base.group_partner_manager"/>
<field name="payment_next_action" placeholder="e.g. Give a phonecall, Check if it's paid, ..."/>
<field name="payment_next_action" placeholder="Action to be taken e.g. Give a phonecall, Check if it's paid, ..."/>
</div>
</group>
<label for="payment_note" class="oe_edit_only"/>
<field name="payment_note" placeholder="e.g. 50%% before 15th of May, balance before 1st of July."/>
<field name="payment_note" placeholder="He said the problem was temporary and promised to pay 50%% before 15th of May, balance before 1st of July."/>
<p class="oe_grey">
Below is the history of the transactions of this
customer. You can set an invoice in litigation in
order to not include it in the next payment
follow-ups.
customer. You can check "No Follow-up" in
order to exclude it from the next follow-up actions.
</p>
<field name="unreconciled_aml_ids">
<tree string="Account Move line" editable="bottom" create="false" delete="false" colors="red:(not date_maturity or date_maturity&lt;=current_date) and result&gt;0">
<field name="date" readonly="True"/>
<field name="company_id" readonly="True" groups="base.group_multi_company" />
<field name="move_id" readonly="True"/>
<field name="blocked" string="Litigation"/>
<field name="blocked"/>
<field name="date_maturity" readonly="True"/>
<field name="reconcile_partial_id" readonly="True"/>
<field name="result" readonly="True"/>

View File

@ -5,69 +5,36 @@
<data noupdate="1">
<!--Mail template level 0-->
<record id="email_template_account_followup_level0" model="email.template">
<field name="name">Follow-up of overdue invoices level 0</field>
<field name="email_from">${user.email or ''}</field>
<field name="subject">${user.company_id.name} Payment Follow-up</field>
<field name="email_to">${object.email}</field>
<field name="lang">${object.lang}</field>
<field name="name">First polite payment follow-up reminder email</field>
<field name="email_from">${user.email or '' | h}</field>
<field name="subject">${user.company_id.name | h} Payment Reminder</field>
<field name="email_to">${object.email | h}</field>
<field name="lang">${object.lang | h}</field>
<field name="model_id" ref="base.model_res_partner"/>
<field name="auto_delete" eval="True"/>
<field name="body_html"><![CDATA[
<div style="font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: rgb(255, 255, 255); ">
<p>Dear ${object.name},</p>
<p>Dear ${object.name | h},</p>
<p>
Exception made if there was a mistake of ours, it seems that the following amount stays unpaid. Please, take
appropriate measures in order to carry out this payment in the next 8 days.
Would your payment have been carried out after this mail was sent, please ignore this message. Do not hesitate to
contact our accounting department at (+32).10.68.94.39.
contact our accounting department.
</p>
<br/>
Best Regards,
<br/>
<br/>
${user.name}
${user.name | h}
<br/>
<br/>
<%
from openerp.addons.account_followup.report import account_followup_print
rml_parse = account_followup_print.report_rappel(object._cr, user.id, "followup_rml_parser")
final_res = rml_parse._lines_get_with_partner(object, user.company_id.id)
followup_table = ''
for currency_dict in final_res:
currency_symbol = currency_dict.get('line', [{'currency_id': user.company_id.currency_id}])[0]['currency_id'].symbol
followup_table += '''
<table border="2" width=100%%>
<tr>
<td>Invoice date</td>
<td>Reference</td>
<td>Due date</td>
<td>Amount (%s)</td>
<td>Lit.</td>
</tr>
''' % (currency_symbol)
total = 0
for aml in currency_dict['line']:
block = aml['blocked'] and 'X' or ' '
total += aml['balance']
strbegin = "<TD> "
strend = "</TD> "
date = aml['date_maturity'] or aml['date']
if date <= ctx['current_date'] and aml['balance'] > 0:
strbegin = "<TD><B>"
strend = "</B></TD>"
followup_table +="<TR>" + strbegin + str(aml['date']) + strend + strbegin + aml['ref'] + strend + strbegin + str(date) + strend + strbegin + str(aml['balance']) + strend + strbegin + block + strend + "</TR>"
total = rml_parse.formatLang(total, dp='Account', currency_obj=object.company_id.currency_id)
followup_table += '''<tr> </tr>
</table>
<center>Amount due: %s </center>''' % (total)
%>
${followup_table}
${ctx.get('followup_table','')}
<br/>
@ -79,72 +46,37 @@ ${followup_table}
<!--Mail template level 1 -->
<record id="email_template_account_followup_level1" model="email.template">
<field name="name">Follow-up of overdue invoices level 1</field>
<field name="email_from">${user.email or ''}</field>
<field name="subject">${user.company_id.name} Payment Follow-up</field>
<field name="email_to">${object.email}</field>
<field name="lang">${object.lang}</field>
<field name="name">A bit urging second payment follow-up reminder email</field>
<field name="email_from">${user.email or '' | h}</field>
<field name="subject">${user.company_id.name | h} Payment Reminder</field>
<field name="email_to">${object.email | h}</field>
<field name="lang">${object.lang | h}</field>
<field name="model_id" ref="base.model_res_partner"/>
<field name="auto_delete" eval="True"/>
<field name="body_html"><![CDATA[
<div style="font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: rgb(255, 255, 255); ">
<p>Dear ${object.name},</p>
<p>Dear ${object.name | h},</p>
<p>
We are disappointed to see that despite sending a reminder, that your account is now seriously overdue.
It is essential that immediate payment is made, otherwise we will have to consider placing a stop on your account
which means that we will no longer be able to supply your company with (goods/services).
Please, take appropriate measures in order to carry out this payment in the next 8 days.
If there is a problem with paying invoice that we are not aware of, do not hesitate to contact our accounting
department at (+32).10.68.94.39. so that we can resolve the matter quickly.
department. so that we can resolve the matter quickly.
Details of due payments is printed below.
</p>
<br/>
Best Regards,
<br/>
${user.name}
<br/>
${user.name | h}
<br/>
<br/>
<%
from openerp.addons.account_followup.report import account_followup_print
rml_parse = account_followup_print.report_rappel(object._cr, user.id, "followup_rml_parser")
final_res = rml_parse._lines_get_with_partner(object, user.company_id.id)
followup_table = ''
for currency_dict in final_res:
currency_symbol = currency_dict.get('line', [{'currency_id': user.company_id.currency_id}])[0]['currency_id'].symbol
followup_table += '''
<table border="2" width=100%%>
<tr>
<td>Invoice date</td>
<td>Reference</td>
<td>Due date</td>
<td>Amount (%s)</td>
<td>Lit.</td>
</tr>
''' % (currency_symbol)
total = 0
for aml in currency_dict['line']:
block = aml['blocked'] and 'X' or ' '
total += aml['balance']
strbegin = "<TD> "
strend = "</TD> "
date = aml['date_maturity'] or aml['date']
if date <= ctx['current_date'] and aml['balance'] > 0:
strbegin = "<TD><B>"
strend = "</B></TD>"
followup_table +="<TR>" + strbegin + str(aml['date']) + strend + strbegin + aml['ref'] + strend + strbegin + str(date) + strend + strbegin + str(aml['balance']) + strend + strbegin + block + strend + "</TR>"
total = rml_parse.formatLang(total, dp='Account', currency_obj=object.company_id.currency_id)
followup_table += '''<tr> </tr>
</table>
<center>Amount due: %s </center>''' % (total)
%>
${followup_table}
${ctx.get('followup_table','')}
<br/>
@ -153,71 +85,34 @@ ${followup_table}
</record>
<!--Email template -->
<record id="email_template_account_followup_level2" model="email.template">
<field name="name">Follow-up of overdue invoices level 2</field>
<field name="email_from">${user.email or ''}</field>
<field name="subject">${user.company_id.name} Payment Follow-up</field>
<field name="email_to">${object.email}</field>
<field name="lang">${object.lang}</field>
<field name="name">Urging payment follow-up reminder email</field>
<field name="email_from">${user.email or '' | h}</field>
<field name="subject">${user.company_id.name | h} Payment Reminder</field>
<field name="email_to">${object.email | h}</field>
<field name="lang">${object.lang | h}</field>
<field name="model_id" ref="base.model_res_partner"/>
<field name="auto_delete" eval="True"/>
<field name="body_html"><![CDATA[
<div style="font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: rgb(255, 255, 255); ">
<p>Dear ${object.name},</p>
<p>Dear ${object.name | h},</p>
<p>
Despite several reminders, your account is still not settled.
Unless full payment is made in next 8 days, legal action for the recovery of the debt will be taken without
further notice.
I trust that this action will prove unnecessary and details of due payments is printed below.
In case of any queries concerning this matter, do not hesitate to contact our accounting department at (+32).10.68.94.39.
In case of any queries concerning this matter, do not hesitate to contact our accounting department.
</p>
<br/>
Best Regards,
<br/>
${user.name}
<br/>
${user.name | h}
<br/>
<br/>
<%
from openerp.addons.account_followup.report import account_followup_print
rml_parse = account_followup_print.report_rappel(object._cr, user.id, "followup_rml_parser")
final_res = rml_parse._lines_get_with_partner(object, user.company_id.id)
followup_table = ''
for currency_dict in final_res:
currency_symbol = currency_dict.get('line', [{'currency_id': user.company_id.currency_id}])[0]['currency_id'].symbol
followup_table += '''
<table border="2" width=100%%>
<tr>
<td>Invoice date</td>
<td>Reference</td>
<td>Due date</td>
<td>Amount (%s)</td>
<td>Lit.</td>
</tr>
''' % (currency_symbol)
total = 0
for aml in currency_dict['line']:
block = aml['blocked'] and 'X' or ' '
total += aml['balance']
strbegin = "<TD> "
strend = "</TD> "
date = aml['date_maturity'] or aml['date']
if date <= ctx['current_date'] and aml['balance'] > 0:
strbegin = "<TD><B>"
strend = "</B></TD>"
followup_table +="<TR>" + strbegin + str(aml['date']) + strend + strbegin + aml['ref'] + strend + strbegin + str(date) + strend + strbegin + str(aml['balance']) + strend + strbegin + block + strend + "</TR>"
total = rml_parse.formatLang(total, dp='Account', currency_obj=object.company_id.currency_id)
followup_table += '''<tr> </tr>
</table>
<center>Amount due: %s </center>''' % (total)
%>
${followup_table}
${ctx.get('followup_table','')}
<br/>
@ -228,81 +123,37 @@ ${followup_table}
<!-- Default follow up message -->
<record id="email_template_account_followup_default" model="email.template">
<field name="name">Default follow-up of overdue invoices</field>
<field name="email_from">${user.email or ''}</field>
<field name="subject">${user.company_id.name} Payment Follow-up</field>
<field name="email_to">${object.email}</field>
<field name="lang">${object.lang}</field>
<field name="name">Default payment follow-up reminder e-mail</field>
<field name="email_from">${user.email or '' | h}</field>
<field name="subject">${user.company_id.name | h} Payment Reminder</field>
<field name="email_to">${object.email | h}</field>
<field name="lang">${object.lang | h}</field>
<field name="model_id" ref="base.model_res_partner"/>
<field name="auto_delete" eval="True"/>
<field name="body_html"><![CDATA[
<div style="font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: rgb(255, 255, 255); ">
<p>Dear ${object.name},</p>
<p>Dear ${object.name | h},</p>
<p>
Exception made if there was a mistake of ours, it seems that the following amount stays unpaid. Please, take
appropriate measures in order to carry out this payment in the next 8 days.
Would your payment have been carried out after this mail was sent, please ignore this message. Do not hesitate to
contact our accounting department at (+32).10.68.94.39.
contact our accounting department.
</p>
<br/>
Best Regards,
</br>
</br>
<br/>
${user.name}
<br/>
${user.name | h}
<br/>
<br/>
<%
from openerp.addons.account_followup.report import account_followup_print
rml_parse = account_followup_print.report_rappel(object._cr, user.id, "followup_rml_parser")
final_res = rml_parse._lines_get_with_partner(object, user.company_id.id)
followup_table = ''
for currency_dict in final_res:
currency_symbol = currency_dict.get('line', [{'currency_id': user.company_id.currency_id}])[0]['currency_id'].symbol
followup_table += '''
<table border="2" width=100%%>
<tr>
<td>Invoice date</td>
<td>Reference</td>
<td>Due date</td>
<td>Amount (%s)</td>
<td>Lit.</td>
</tr>
''' % (currency_symbol)
total = 0
for aml in currency_dict['line']:
block = aml['blocked'] and 'X' or ' '
total += aml['balance']
strbegin = "<TD> "
strend = "</TD> "
date = aml['date_maturity'] or aml['date']
if date <= ctx['current_date'] and aml['balance'] > 0:
strbegin = "<TD><B>"
strend = "</B></TD>"
followup_table +="<TR>" + strbegin + str(aml['date']) + strend + strbegin + aml['ref'] + strend + strbegin + str(date) + strend + strbegin + str(aml['balance']) + strend + strbegin + block + strend + "</TR>"
total = rml_parse.formatLang(total, dp='Account', currency_obj=object.company_id.currency_id)
followup_table += '''<tr> </tr>
</table>
<center>Amount due: %s </center>''' % (total)
%>
${followup_table}
<br/>
${ctx.get('followup_table','')}
<br/>
</div>
]]></field>
</record>
<record id="demo_followup1" model="account_followup.followup">
<field name="company_id" ref="base.main_company"/>
@ -319,7 +170,7 @@ Dear %(partner_name)s,
Exception made if there was a mistake of ours, it seems that the following amount stays unpaid. Please, take appropriate measures in order to carry out this payment in the next 8 days.
Would your payment have been carried out after this mail was sent, please ignore this message. Do not hesitate to contact our accounting department at (+32).10.68.94.39.
Would your payment have been carried out after this mail was sent, please ignore this message. Do not hesitate to contact our accounting department.
Best Regards,
</field>
@ -342,15 +193,13 @@ We are disappointed to see that despite sending a reminder, that your account is
It is essential that immediate payment is made, otherwise we will have to consider placing a stop on your account which means that we will no longer be able to supply your company with (goods/services).
Please, take appropriate measures in order to carry out this payment in the next 8 days.
If there is a problem with paying invoice that we are not aware of, do not hesitate to contact our accounting department at (+32).10.68.94.39. so that we can resolve the matter quickly.
If there is a problem with paying invoice that we are not aware of, do not hesitate to contact our accounting department, so that we can resolve the matter quickly.
Details of due payments is printed below.
Best Regards,
</field>
</record>
<record id="demo_followup_line3" model="account_followup.followup.line">
<field name="name">Call the customer on the phone</field>
<field name="sequence">3</field>
@ -369,7 +218,7 @@ Unless full payment is made in next 8 days, then legal action for the recovery o
I trust that this action will prove unnecessary and details of due payments is printed below.
In case of any queries concerning this matter, do not hesitate to contact our accounting department at (+32).10.68.94.39.
In case of any queries concerning this matter, do not hesitate to contact our accounting department.
Best Regards,
</field>

View File

@ -17,7 +17,7 @@ Unless full payment is made in next 8 days, then legal action for the recovery o
I trust that this action will prove unnecessary and details of due payments is printed below.
In case of any queries concerning this matter, do not hesitate to contact our accounting department at (+32).10.68.94.39.
In case of any queries concerning this matter, do not hesitate to contact our accounting department.
Best Regards,
</field>
@ -39,7 +39,7 @@ Unless full payment is made in next 8 days, then legal action for the recovery o
I trust that this action will prove unnecessary and details of due payments is printed below.
In case of any queries concerning this matter, do not hesitate to contact our accounting department at (+32).10.68.94.39.
In case of any queries concerning this matter, do not hesitate to contact our accounting department.
Best Regards,
</field>

View File

@ -69,7 +69,6 @@
<record id="view_account_followup_followup_form" model="ir.ui.view">
<field name="name">account_followup.followup.form</field>
<field name="model">account_followup.followup</field>
<!-- <field name="group_ids" groups="base.group_multi_company"/>-->
<field name="arch" type="xml">
<form string="Follow-up" version="7.0">
<h1><field name="company_id" widget="selection" class="oe_inline"/></h1>
@ -77,9 +76,11 @@
To remind customers of paying their invoices, you can
define different actions depending on how severely
overdue the customer is. These actions are bundled
into folow-up levels that are triggered when the due
date of the most overdue invoice has passed a certain
amount of days.
into follow-up levels that are triggered when the due
date of an invoice has passed a certain
number of days. If there are other overdue invoices for the
same customer, the actions of the most
overdue invoice will be executed.
</p>
<field name="followup_line"/>
</form>
@ -91,7 +92,7 @@
<field name="model">account_followup.followup</field>
<field name="arch" type="xml">
<tree string="Follow-up">
<field name="company_id" /> <!--groups="base.group_multi_company"-->
<field name="company_id" />
</tree>
</field>
</record>
@ -180,18 +181,5 @@
</field>
</field>
</record>
<!-- <record id="view_move_line_tree" model="ir.ui.view">
<field name="name">account.move.line.tree.followup</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="state" position="before">
<field name="followup_date"/>
<field name="followup_line_id"/>
</field>
</field>
</record> -->
</data>
</openerp>

View File

@ -25,6 +25,8 @@ import pooler
from report import report_sxw
class report_rappel(report_sxw.rml_parse):
_name = "account_followup.report.rappel"
def __init__(self, cr, uid, name, context=None):
super(report_rappel, self).__init__(cr, uid, name, context=context)
self.localcontext.update({
@ -79,7 +81,6 @@ class report_rappel(report_sxw.rml_parse):
final_res.append({'line': line_cur[cur]['line']})
return final_res
def _get_text(self, stat_line, followup_id, context=None):
if context is None:
context = {}
@ -108,7 +109,6 @@ class report_rappel(report_sxw.rml_parse):
'company_name': stat_line.company_id.name,
'user_signature': pooler.get_pool(self.cr.dbname).get('res.users').browse(self.cr, self.uid, self.uid, context).signature or '',
}
return text
report_sxw.report_sxw('report.account_followup.followup.print',

View File

@ -108,7 +108,7 @@ class TestAccountFollowup(TransactionCase):
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, [('credit', '>=', 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):
@ -153,7 +153,6 @@ class TestAccountFollowup(TransactionCase):
}, context={"followup_id": self.followup_id})
self.wizard.do_process(cr, uid, [self.wizard_id], context={"followup_id": self.followup_id})
partner_ref = self.partner.browse(cr, uid, self.partner_id)
print partner_ref.credit, partner_ref.payment_next_action_date, partner_ref.payment_responsible_id
self.assertEqual(0, self.partner.browse(cr, uid, self.partner_id).credit, "Credit != 0")
self.assertEqual(0, self.partner.browse(cr, uid, self.partner_id).payment_amount_due, "Amount Due != 0")
self.assertFalse(self.partner.browse(cr, uid, self.partner_id).payment_next_action_date, "Next action date not cleared")

View File

@ -152,7 +152,7 @@ class account_followup_print(osv.osv_memory):
if partner.max_followup_id.manual_action:
partner_obj.do_partner_manual_action(cr, uid, [partner.partner_id.id], context=context)
nbmanuals = nbmanuals + 1
key = partner.partner_id.payment_responsible_id.name or _("Nobody")
key = partner.partner_id.payment_responsible_id.name or _("Anybody")
if not key in manuals.keys():
manuals[key]= 1
else:
@ -198,15 +198,12 @@ class account_followup_print(osv.osv_memory):
ids = self.pool.get('res.partner').search(cr, uid, ['&', ('id', 'not in', partner_list_ids), '|',
('payment_responsible_id', '!=', False),
('payment_next_action_date', '!=', False)], context=context)
partners = self.pool.get('res.partner').browse(cr, uid, ids, context=context)
newids = []
for part in partners:
credit = 0
for aml in part.unreconciled_aml_ids:
credit +=aml.result
if credit <= 0:
newids.append(part.id)
self.pool.get('res.partner').action_done(cr, uid, newids, context=context)
partners_to_clear = []
for part in self.pool.get('res.partner').browse(cr, uid, ids, context=context):
if not part.unreconciled_aml_ids:
partners_to_clear.append(part.id)
self.pool.get('res.partner').action_done(cr, uid, partners_to_clear, context=context)
return len(ids)
def do_process(self, cr, uid, ids, context=None):

View File

@ -13,7 +13,7 @@
</group>
<p class ="oe_grey">
This action will send follow-up emails, print the letters and
set the manual actions per customers.
set the manual actions per customer, according to the follow-up levels defined.
</p>
<footer>
<button name="do_process" string="Send emails and generate letters" type="object" class="oe_highlight"/>