2009-10-13 05:58:37 +00:00
# -*- coding: utf-8 -*-
2006-12-07 13:41:40 +00:00
##############################################################################
2010-07-03 09:30:47 +00:00
#
2009-10-14 11:15:34 +00:00
# OpenERP, Open Source Management Solution
2010-01-12 09:18:39 +00:00
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
2006-12-07 13:41:40 +00:00
#
2008-11-03 19:18:56 +00:00
# This program is free software: you can redistribute it and/or modify
2009-10-14 11:15:34 +00:00
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
2006-12-07 13:41:40 +00:00
#
2008-11-03 19:18:56 +00:00
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2009-10-14 11:15:34 +00:00
# GNU Affero General Public License for more details.
2006-12-07 13:41:40 +00:00
#
2009-10-14 11:15:34 +00:00
# You should have received a copy of the GNU Affero General Public License
2010-07-03 09:30:47 +00:00
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2006-12-07 13:41:40 +00:00
#
##############################################################################
2014-07-06 14:44:26 +00:00
from openerp import api
2012-12-06 14:56:32 +00:00
from openerp . osv import fields , osv
2012-11-16 16:14:37 +00:00
from lxml import etree
2012-12-06 14:56:32 +00:00
from openerp . tools . translate import _
2012-11-08 14:14:57 +00:00
2006-12-07 13:41:40 +00:00
class followup ( osv . osv ) :
2008-07-22 15:11:28 +00:00
_name = ' account_followup.followup '
2012-06-27 06:28:21 +00:00
_description = ' Account Follow-up '
2012-11-15 08:16:34 +00:00
_rec_name = ' name '
2008-07-22 15:11:28 +00:00
_columns = {
2014-07-06 14:44:26 +00:00
' followup_line ' : fields . one2many ( ' account_followup.followup.line ' , ' followup_id ' , ' Follow-up ' , copy = True ) ,
2012-11-21 14:49:32 +00:00
' company_id ' : fields . many2one ( ' res.company ' , ' Company ' , required = True ) ,
2014-03-13 11:26:25 +00:00
' name ' : fields . related ( ' company_id ' , ' name ' , string = " Name " , readonly = True , type = " char " ) ,
2008-07-22 15:11:28 +00:00
}
2010-07-03 09:30:47 +00:00
_defaults = {
' company_id ' : lambda s , cr , uid , c : s . pool . get ( ' res.company ' ) . _company_default_get ( cr , uid , ' account_followup.followup ' , context = c ) ,
2010-10-11 10:51:16 +00:00
}
2012-11-22 08:32:24 +00:00
_sql_constraints = [ ( ' company_uniq ' , ' unique(company_id) ' , ' Only one follow-up per company is allowed ' ) ]
2012-11-21 14:49:32 +00:00
2012-11-12 17:38:57 +00:00
2012-11-21 14:49:32 +00:00
class followup_line ( osv . osv ) :
2012-11-06 17:15:22 +00:00
def _get_default_template ( self , cr , uid , ids , context = None ) :
2012-12-03 15:59:34 +00:00
try :
return self . pool . get ( ' ir.model.data ' ) . get_object_reference ( cr , uid , ' account_followup ' , ' email_template_account_followup_default ' ) [ 1 ]
except ValueError :
return False
2012-11-30 16:24:01 +00:00
2008-07-22 15:11:28 +00:00
_name = ' account_followup.followup.line '
2012-06-27 06:28:21 +00:00
_description = ' Follow-up Criteria '
2008-07-22 15:11:28 +00:00
_columns = {
2014-05-21 09:52:05 +00:00
' name ' : fields . char ( ' Follow-Up Action ' , required = True ) ,
2011-05-09 10:06:20 +00:00
' sequence ' : fields . integer ( ' Sequence ' , help = " Gives the sequence order when displaying a list of follow-up lines. " ) ,
2012-11-22 15:30:49 +00:00
' delay ' : fields . integer ( ' Due Days ' , help = " The number of days after the due date of the invoice to wait before sending the reminder. Could be negative if you want to send a polite alert beforehand. " , required = True ) ,
2008-07-22 15:11:28 +00:00
' followup_id ' : fields . many2one ( ' account_followup.followup ' , ' Follow Ups ' , required = True , ondelete = " cascade " ) ,
2009-01-28 14:43:31 +00:00
' description ' : fields . text ( ' Printed Message ' , translate = True ) ,
2012-11-21 14:49:32 +00:00
' send_email ' : fields . boolean ( ' Send an Email ' , help = " When processing, it will send an email " ) ,
' send_letter ' : fields . boolean ( ' Send a Letter ' , help = " When processing, it will print a letter " ) ,
2012-11-20 12:40:26 +00:00
' manual_action ' : fields . boolean ( ' Manual Action ' , help = " When processing, it will set the manual action to be taken for that customer. " ) ,
2012-11-21 14:49:32 +00:00
' manual_action_note ' : fields . text ( ' Action To Do ' , placeholder = " e.g. Give a phone call, check with others , ... " ) ,
' manual_action_responsible_id ' : fields . many2one ( ' res.users ' , ' Assign a Responsible ' , ondelete = ' set null ' ) ,
' email_template_id ' : fields . many2one ( ' email.template ' , ' Email Template ' , ondelete = ' set null ' ) ,
2010-10-11 10:51:16 +00:00
}
2012-11-06 17:15:22 +00:00
_order = ' delay '
2012-11-22 15:30:49 +00:00
_sql_constraints = [ ( ' days_uniq ' , ' unique(followup_id, delay) ' , ' Days of the follow-up levels must be different ' ) ]
2011-05-10 13:55:57 +00:00
_defaults = {
2012-10-19 08:07:37 +00:00
' send_email ' : True ,
2012-11-30 15:42:08 +00:00
' send_letter ' : True ,
2012-11-21 14:49:32 +00:00
' manual_action ' : False ,
2012-11-06 17:15:22 +00:00
' description ' : """
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 .
2012-12-06 16:40:06 +00:00
Would your payment have been carried out after this mail was sent , please ignore this message . Do not hesitate to contact our accounting department .
2011-09-24 10:42:25 +00:00
2012-11-06 17:15:22 +00:00
Best Regards ,
""" ,
' email_template_id ' : _get_default_template ,
2011-05-10 13:55:57 +00:00
}
2012-11-21 14:49:32 +00:00
2011-09-24 10:42:25 +00:00
def _check_description ( self , cr , uid , ids , context = None ) :
for line in self . browse ( cr , uid , ids , context = context ) :
if line . description :
try :
line . description % { ' partner_name ' : ' ' , ' date ' : ' ' , ' user_signature ' : ' ' , ' company_name ' : ' ' }
except :
return False
return True
_constraints = [
( _check_description , ' Your description is invalid, use the right legend or %% if you want to use the percent character. ' , [ ' description ' ] ) ,
]
2010-07-03 09:30:47 +00:00
2006-12-07 13:41:40 +00:00
class account_move_line ( osv . osv ) :
2012-11-21 14:49:32 +00:00
2012-11-07 16:51:30 +00:00
def _get_result ( self , cr , uid , ids , name , arg , context = None ) :
res = { }
2012-11-23 15:42:33 +00:00
for aml in self . browse ( cr , uid , ids , context = context ) :
2012-11-07 16:51:30 +00:00
res [ aml . id ] = aml . debit - aml . credit
return res
2012-11-21 14:49:32 +00:00
2008-07-22 15:11:28 +00:00
_inherit = ' account.move.line '
_columns = {
2012-11-22 15:30:49 +00:00
' followup_line_id ' : fields . many2one ( ' account_followup.followup.line ' , ' Follow-up Level ' ,
ondelete = ' restrict ' ) , #restrict deletion of the followup line
2011-01-17 11:20:56 +00:00
' followup_date ' : fields . date ( ' Latest Follow-up ' , select = True ) ,
2012-11-22 15:30:49 +00:00
' result ' : fields . function ( _get_result , type = ' float ' , method = True ,
string = " Balance " ) #'balance' field is not the same
2011-10-28 15:43:37 +00:00
}
2010-07-03 09:30:47 +00:00
2006-12-07 13:41:40 +00:00
2012-10-19 08:07:37 +00:00
class res_partner ( osv . osv ) :
2012-10-19 17:29:17 +00:00
2012-11-16 16:14:37 +00:00
def fields_view_get ( self , cr , uid , view_id = None , view_type = None , context = None , toolbar = False , submenu = False ) :
2012-11-21 14:49:32 +00:00
res = super ( res_partner , self ) . fields_view_get ( cr , uid , view_id = view_id , view_type = view_type , context = context ,
2012-11-16 16:14:37 +00:00
toolbar = toolbar , submenu = submenu )
2012-12-01 11:36:37 +00:00
context = context or { }
if view_type == ' form ' and context . get ( ' Followupfirst ' ) :
2012-11-16 16:14:37 +00:00
doc = etree . XML ( res [ ' arch ' ] , parser = None , base_url = None )
2012-12-01 11:36:37 +00:00
first_node = doc . xpath ( " //page[@name= ' followup_tab ' ] " )
2012-11-21 14:49:32 +00:00
root = first_node [ 0 ] . getparent ( )
2012-11-19 10:18:57 +00:00
root . insert ( 0 , first_node [ 0 ] )
2012-12-01 11:36:37 +00:00
res [ ' arch ' ] = etree . tostring ( doc , encoding = " utf-8 " )
2012-11-16 16:14:37 +00:00
return res
2012-11-21 14:49:32 +00:00
2012-11-27 16:53:17 +00:00
def _get_latest ( self , cr , uid , ids , names , arg , context = None , company_id = None ) :
2012-11-12 17:38:57 +00:00
res = { }
2012-11-23 15:42:33 +00:00
if company_id == None :
company = self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid , context = context ) . company_id
else :
company = self . pool . get ( ' res.company ' ) . browse ( cr , uid , company_id , context = context )
2012-11-22 15:30:49 +00:00
for partner in self . browse ( cr , uid , ids , context = context ) :
2012-11-23 15:42:33 +00:00
amls = partner . unreconciled_aml_ids
2012-11-12 17:38:57 +00:00
latest_date = False
latest_level = False
2012-11-20 15:29:59 +00:00
latest_days = False
2012-11-22 15:30:49 +00:00
latest_level_without_lit = False
2012-11-20 15:29:59 +00:00
latest_days_without_lit = False
2012-11-21 14:49:32 +00:00
for aml in amls :
2012-11-23 15:42:33 +00:00
if ( aml . company_id == company ) and ( aml . followup_line_id != False ) and ( not latest_days or latest_days < aml . followup_line_id . delay ) :
2012-11-22 15:30:49 +00:00
latest_days = aml . followup_line_id . delay
latest_level = aml . followup_line_id . id
2012-11-23 15:42:33 +00:00
if ( aml . company_id == company ) and ( not latest_date or latest_date < aml . followup_date ) :
2012-11-12 17:38:57 +00:00
latest_date = aml . followup_date
2012-11-23 15:42:33 +00:00
if ( aml . company_id == company ) and ( aml . blocked == False ) and ( aml . followup_line_id != False and
2012-11-22 15:30:49 +00:00
( not latest_days_without_lit or latest_days_without_lit < aml . followup_line_id . delay ) ) :
latest_days_without_lit = aml . followup_line_id . delay
2012-11-20 15:29:59 +00:00
latest_level_without_lit = aml . followup_line_id . id
res [ partner . id ] = { ' latest_followup_date ' : latest_date ,
2012-11-21 14:49:32 +00:00
' latest_followup_level_id ' : latest_level ,
2012-11-12 17:38:57 +00:00
' latest_followup_level_id_without_lit ' : latest_level_without_lit }
2012-11-06 17:15:22 +00:00
return res
2012-10-26 08:09:14 +00:00
2014-07-06 14:44:26 +00:00
@api.cr_uid_ids_context
2012-11-08 14:14:57 +00:00
def do_partner_manual_action ( self , cr , uid , partner_ids , context = None ) :
2012-11-23 15:42:33 +00:00
#partner_ids -> res.partner
for partner in self . browse ( cr , uid , partner_ids , context = context ) :
#Check action: check if the action was not empty, if not add
action_text = " "
if partner . payment_next_action :
action_text = ( partner . payment_next_action or ' ' ) + " \n " + ( partner . latest_followup_level_id_without_lit . manual_action_note or ' ' )
else :
action_text = partner . latest_followup_level_id_without_lit . manual_action_note or ' '
2012-11-30 16:24:01 +00:00
2013-04-04 09:48:16 +00:00
#Check date: only change when it did not exist already
action_date = partner . payment_next_action_date or fields . date . context_today ( self , cr , uid , context = context )
2012-11-30 16:24:01 +00:00
2012-11-23 15:42:33 +00:00
# Check responsible: if partner has not got a responsible already, take from follow-up
responsible_id = False
if partner . payment_responsible_id :
responsible_id = partner . payment_responsible_id . id
else :
p = partner . latest_followup_level_id_without_lit . manual_action_responsible_id
responsible_id = p and p . id or False
self . write ( cr , uid , [ partner . id ] , { ' payment_next_action_date ' : action_date ,
' payment_next_action ' : action_text ,
' payment_responsible_id ' : responsible_id } )
2012-11-21 14:49:32 +00:00
2012-11-23 15:42:33 +00:00
def do_partner_print ( self , cr , uid , wizard_partner_ids , data , context = None ) :
#wizard_partner_ids are ids from special view, not from res.partner
if not wizard_partner_ids :
2012-10-26 08:09:14 +00:00
return { }
2012-11-23 15:42:33 +00:00
data [ ' partner_ids ' ] = wizard_partner_ids
2012-10-26 08:09:14 +00:00
datas = {
2013-10-08 17:07:14 +00:00
' ids ' : wizard_partner_ids ,
2012-10-26 08:09:14 +00:00
' model ' : ' account_followup.followup ' ,
' form ' : data
}
2014-05-02 13:03:10 +00:00
return self . pool [ ' report ' ] . get_action ( cr , uid , [ ] , ' account_followup.report_followup ' , data = datas , context = context )
2012-10-26 08:09:14 +00:00
2014-07-06 14:44:26 +00:00
@api.cr_uid_ids_context
2012-10-26 08:09:14 +00:00
def do_partner_mail ( self , cr , uid , partner_ids , context = None ) :
2012-12-06 17:26:26 +00:00
if context is None :
context = { }
ctx = context . copy ( )
ctx [ ' followup ' ] = True
2012-11-08 14:14:57 +00:00
#partner_ids are res.partner ids
2012-11-23 15:42:33 +00:00
# If not defined by latest follow-up level, it will be the default template if it can find it
2012-10-26 08:09:14 +00:00
mtp = self . pool . get ( ' email.template ' )
2012-11-20 12:40:26 +00:00
unknown_mails = 0
2012-12-06 17:26:26 +00:00
for partner in self . browse ( cr , uid , partner_ids , context = ctx ) :
2012-11-30 15:42:08 +00:00
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 :
2012-12-06 17:26:26 +00:00
mtp . send_mail ( cr , uid , level . email_template_id . id , partner . id , context = ctx )
2012-11-23 15:42:33 +00:00
else :
mail_template_id = self . pool . get ( ' ir.model.data ' ) . get_object_reference ( cr , uid ,
' account_followup ' , ' email_template_account_followup_default ' )
2012-12-06 17:26:26 +00:00
mtp . send_mail ( cr , uid , mail_template_id [ 1 ] , partner . id , context = ctx )
2012-11-20 12:40:26 +00:00
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 :
2013-01-11 18:42:26 +00:00
payment_action_date = min ( fields . date . context_today ( self , cr , uid , context = ctx ) , partner . payment_next_action_date )
2012-11-20 12:40:26 +00:00
else :
2013-01-11 18:42:26 +00:00
payment_action_date = fields . date . context_today ( self , cr , uid , context = ctx )
2012-11-20 12:40:26 +00:00
if partner . payment_next_action :
2012-12-06 11:03:15 +00:00
payment_next_action = partner . payment_next_action + " \n " + action_text
2012-11-20 12:40:26 +00:00
else :
payment_next_action = action_text
2012-11-21 14:49:32 +00:00
self . write ( cr , uid , [ partner . id ] , { ' payment_next_action_date ' : payment_action_date ,
2012-12-06 17:26:26 +00:00
' payment_next_action ' : payment_next_action } , context = ctx )
2012-11-20 12:40:26 +00:00
return unknown_mails
2012-10-26 08:09:14 +00:00
2012-12-14 14:16:54 +00:00
def get_followup_table_html ( self , cr , uid , ids , context = None ) :
""" Build the html tables to be included in emails send to partners,
when reminding them their overdue invoices .
: param ids : [ id ] of the partner for whom we are building the tables
: rtype : string
"""
from report import account_followup_print
assert len ( ids ) == 1
2013-02-11 15:13:52 +00:00
if context is None :
context = { }
2012-12-14 14:16:54 +00:00
partner = self . browse ( cr , uid , ids [ 0 ] , context = context )
2013-02-11 15:13:52 +00:00
#copy the context to not change global context. Overwrite it because _() looks for the lang in local variable 'context'.
#Set the language to use = the partner language
context = dict ( context , lang = partner . lang )
2012-12-14 14:16:54 +00:00
followup_table = ' '
if partner . unreconciled_aml_ids :
company = self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid , context = context ) . company_id
2013-01-11 18:42:26 +00:00
current_date = fields . date . context_today ( self , cr , uid , context = context )
2012-12-14 14:16:54 +00:00
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 >
2013-02-11 15:13:52 +00:00
< td > ''' + _( " Invoice Date " ) + ''' < / td >
2013-01-24 15:39:45 +00:00
< td > ''' + _( " Description " ) + ''' < / td >
< td > ''' + _( " Reference " ) + ''' < / td >
2013-02-11 15:13:52 +00:00
< td > ''' + _( " Due Date " ) + ''' < / td >
2013-01-24 15:39:45 +00:00
< td > ''' + _( " Amount " ) + " ( %s ) " % (currency.symbol) + ''' < / td >
< td > ''' + _( " Lit. " ) + ''' < / td >
2012-12-14 14:16:54 +00:00
< / tr >
2013-01-24 15:39:45 +00:00
'''
2012-12-14 14:16:54 +00:00
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> "
2014-07-06 14:44:26 +00:00
followup_table + = " <TR> " + strbegin + str ( aml [ ' date ' ] ) + strend + strbegin + aml [ ' name ' ] + strend + strbegin + ( aml [ ' ref ' ] or ' ' ) + strend + strbegin + str ( date ) + strend + strbegin + str ( aml [ ' balance ' ] ) + strend + strbegin + block + strend + " </TR> "
2014-03-27 18:25:20 +00:00
total = reduce ( lambda x , y : x + y [ ' balance ' ] , currency_dict [ ' line ' ] , 0.00 )
2012-12-14 14:16:54 +00:00
total = rml_parse . formatLang ( total , dp = ' Account ' , currency_obj = currency )
followup_table + = ''' <tr> </tr>
< / table >
2013-01-24 15:39:45 +00:00
< center > ''' + _( " Amount due " ) + ''' : % s < / center > ''' % (total)
2012-12-14 14:16:54 +00:00
return followup_table
2013-01-21 09:54:26 +00:00
def write ( self , cr , uid , ids , vals , context = None ) :
if vals . get ( " payment_responsible_id " , False ) :
for part in self . browse ( cr , uid , ids , context = context ) :
if part . payment_responsible_id < > vals [ " payment_responsible_id " ] :
2013-01-23 15:29:23 +00:00
#Find partner_id of user put as responsible
responsible_partner_id = self . pool . get ( " res.users " ) . browse ( cr , uid , vals [ ' payment_responsible_id ' ] , context = context ) . partner_id . id
self . pool . get ( " mail.thread " ) . message_post ( cr , uid , 0 ,
2013-02-11 15:13:52 +00:00
body = _ ( " You became responsible to do the next action for the payment follow-up of " ) + " <b><a href= ' #id= " + str ( part . id ) + " &view_type=form&model=res.partner ' > " + part . name + " </a></b> " ,
type = ' comment ' ,
subtype = " mail.mt_comment " , context = context ,
model = ' res.partner ' , res_id = part . id ,
2013-03-04 13:19:34 +00:00
partner_ids = [ responsible_partner_id ] )
2013-02-11 15:13:52 +00:00
return super ( res_partner , self ) . write ( cr , uid , ids , vals , context = context )
2013-01-17 16:31:59 +00:00
2012-10-31 11:29:07 +00:00
def action_done ( self , cr , uid , ids , context = None ) :
2012-11-27 16:53:17 +00:00
return self . write ( cr , uid , ids , { ' payment_next_action_date ' : False , ' payment_next_action ' : ' ' , ' payment_responsible_id ' : False } , context = context )
2012-11-21 14:49:32 +00:00
2012-11-08 14:14:57 +00:00
def do_button_print ( self , cr , uid , ids , context = None ) :
2012-11-23 15:42:33 +00:00
assert ( len ( ids ) == 1 )
2013-01-03 09:48:18 +00:00
company_id = self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid , context = context ) . company_id . id
#search if the partner has accounting entries to print. If not, it may not be present in the
#psql view the report is based on, so we need to stop the user here.
if not self . pool . get ( ' account.move.line ' ) . search ( cr , uid , [
( ' partner_id ' , ' = ' , ids [ 0 ] ) ,
( ' account_id.type ' , ' = ' , ' receivable ' ) ,
( ' reconcile_id ' , ' = ' , False ) ,
( ' state ' , ' != ' , ' draft ' ) ,
( ' company_id ' , ' = ' , company_id ) ,
2015-04-29 15:39:58 +00:00
' | ' , ( ' date_maturity ' , ' = ' , False ) , ( ' date_maturity ' , ' <= ' , fields . date . context_today ( self , cr , uid ) ) ,
2013-01-03 09:48:18 +00:00
] , context = context ) :
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( " The partner does not have any accounting entries to print in the overdue report for the current company. " ) )
2012-11-08 14:14:57 +00:00
self . message_post ( cr , uid , [ ids [ 0 ] ] , body = _ ( ' Printed overdue payments report ' ) , context = context )
2013-01-03 09:48:18 +00:00
#build the id of this partner in the psql view. Could be replaced by a search with [('company_id', '=', company_id),('partner_id', '=', ids[0])]
wizard_partner_ids = [ ids [ 0 ] * 10000 + company_id ]
followup_ids = self . pool . get ( ' account_followup.followup ' ) . search ( cr , uid , [ ( ' company_id ' , ' = ' , company_id ) ] , context = context )
if not followup_ids :
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( " There is no followup plan defined for the current company. " ) )
data = {
' date ' : fields . date . today ( ) ,
' followup_id ' : followup_ids [ 0 ] ,
2012-11-06 17:15:22 +00:00
}
2013-01-03 09:48:18 +00:00
#call the print overdue report on this partner
return self . do_partner_print ( cr , uid , wizard_partner_ids , data , context = context )
2012-11-21 14:49:32 +00:00
2012-12-06 17:44:25 +00:00
def _get_amounts_and_date ( self , cr , uid , ids , name , arg , context = None ) :
2012-12-07 08:57:46 +00:00
'''
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.
'''
2012-12-05 14:41:47 +00:00
res = { }
2012-12-06 17:44:25 +00:00
company = self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid , context = context ) . company_id
2013-01-11 18:42:26 +00:00
current_date = fields . date . context_today ( self , cr , uid , context = context )
2012-12-05 14:41:47 +00:00
for partner in self . browse ( cr , uid , ids , context = context ) :
2012-12-06 17:44:25 +00:00
worst_due_date = False
amount_due = amount_overdue = 0.0
2012-12-05 14:41:47 +00:00
for aml in partner . unreconciled_aml_ids :
if ( aml . company_id == company ) :
2012-12-06 17:44:25 +00:00
date_maturity = aml . date_maturity or aml . date
2012-12-07 09:45:59 +00:00
if not worst_due_date or date_maturity < worst_due_date :
2012-12-06 17:44:25 +00:00
worst_due_date = date_maturity
2012-12-05 14:41:47 +00:00
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 ,
2012-12-06 17:44:25 +00:00
' payment_earliest_due_date ' : worst_due_date }
2012-12-05 14:41:47 +00:00
return res
2012-12-06 17:44:25 +00:00
2012-12-06 19:15:37 +00:00
def _get_followup_overdue_query ( self , cr , uid , args , overdue_only = False , context = None ) :
2012-12-06 23:09:18 +00:00
'''
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 , [ ] )
'''
2012-12-06 19:15:37 +00:00
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)
2012-12-05 14:41:47 +00:00
def _payment_overdue_search ( self , cr , uid , obj , name , args , context = None ) :
if not args :
2012-12-06 17:44:25 +00:00
return [ ]
2012-12-06 19:15:37 +00:00
query , query_args = self . _get_followup_overdue_query ( cr , uid , args , overdue_only = True , context = context )
cr . execute ( query , query_args )
2012-12-05 14:41:47 +00:00
res = cr . fetchall ( )
if not res :
return [ ( ' id ' , ' = ' , ' 0 ' ) ]
2012-12-06 23:09:18 +00:00
return [ ( ' id ' , ' in ' , [ x [ 0 ] for x in res ] ) ]
2012-12-06 17:44:25 +00:00
2012-12-06 11:03:15 +00:00
def _payment_earliest_date_search ( self , cr , uid , obj , name , args , context = None ) :
if not args :
return [ ]
2012-12-06 19:15:37 +00:00
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 ]
2012-12-06 11:03:15 +00:00
query = self . pool . get ( ' account.move.line ' ) . _query_get ( cr , uid , context = context )
2012-12-06 19:15:37 +00:00
cr . execute ( ' SELECT partner_id FROM account_move_line l ' \
2012-12-06 11:03:15 +00:00
' WHERE account_id IN ' \
' (SELECT id FROM account_account ' \
' WHERE type= \' receivable \' AND active) ' \
2012-12-06 19:15:37 +00:00
' AND l.company_id = %s '
2012-12-06 11:03:15 +00:00
' AND reconcile_id IS NULL ' \
' AND ' + query + ' ' \
' AND partner_id IS NOT NULL ' \
2012-12-06 19:15:37 +00:00
' GROUP BY partner_id HAVING ' + having_where_clause ,
[ company_id ] + having_values )
2012-12-06 11:03:15 +00:00
res = cr . fetchall ( )
if not res :
return [ ( ' id ' , ' = ' , ' 0 ' ) ]
2012-12-06 19:15:37 +00:00
return [ ( ' id ' , ' in ' , [ x [ 0 ] for x in res ] ) ]
2012-12-06 11:03:15 +00:00
2012-12-05 14:41:47 +00:00
def _payment_due_search ( self , cr , uid , obj , name , args , context = None ) :
if not args :
2012-12-06 19:15:37 +00:00
return [ ]
query , query_args = self . _get_followup_overdue_query ( cr , uid , args , overdue_only = False , context = context )
cr . execute ( query , query_args )
2012-12-05 14:41:47 +00:00
res = cr . fetchall ( )
if not res :
return [ ( ' id ' , ' = ' , ' 0 ' ) ]
2012-12-06 19:15:37 +00:00
return [ ( ' id ' , ' in ' , [ x [ 0 ] for x in res ] ) ]
2012-12-06 18:10:45 +00:00
2013-02-25 14:33:09 +00:00
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 )
2012-10-19 08:07:37 +00:00
_inherit = " res.partner "
_columns = {
2012-11-27 16:53:17 +00:00
' payment_responsible_id ' : fields . many2one ( ' res.users ' , ondelete = ' set null ' , string = ' Follow-up Responsible ' ,
2013-01-17 16:31:59 +00:00
help = " Optionally you can assign a user to this field, which will make him responsible for the action. " ,
2014-07-06 14:44:26 +00:00
track_visibility = " onchange " , copy = False ) ,
' payment_note ' : fields . text ( ' Customer Payment Promise ' , help = " Payment Note " , track_visibility = " onchange " , copy = False ) ,
' payment_next_action ' : fields . text ( ' Next Action ' , copy = False ,
2013-01-17 16:31:59 +00:00
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. " ,
track_visibility = " onchange " ) ,
2014-07-06 14:44:26 +00:00
' payment_next_action_date ' : fields . date ( ' Next Action Date ' , copy = False ,
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. " ) ,
2012-11-23 15:42:33 +00:00
' unreconciled_aml_ids ' : fields . one2many ( ' account.move.line ' , ' partner_id ' , domain = [ ' & ' , ( ' reconcile_id ' , ' = ' , False ) , ' & ' ,
( ' account_id.active ' , ' = ' , True ) , ' & ' , ( ' account_id.type ' , ' = ' , ' receivable ' ) , ( ' state ' , ' != ' , ' draft ' ) ] ) ,
2012-11-12 17:38:57 +00:00
' 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 " ,
2012-12-06 16:40:06 +00:00
store = False , multi = " latest " ) ,
2012-11-12 17:38:57 +00:00
' 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 " ,
2013-02-25 14:33:09 +00:00
store = {
' res.partner ' : ( lambda self , cr , uid , ids , c : ids , [ ] , 10 ) ,
' account.move.line ' : ( _get_partners , [ ' followup_line_id ' ] , 10 ) ,
} ,
2012-11-12 17:38:57 +00:00
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 " ,
2013-02-25 14:33:09 +00:00
store = {
' res.partner ' : ( lambda self , cr , uid , ids , c : ids , [ ] , 10 ) ,
' account.move.line ' : ( _get_partners , [ ' followup_line_id ' ] , 10 ) ,
} ,
2012-11-23 15:42:33 +00:00
multi = " latest " ) ,
2012-12-06 17:26:26 +00:00
' payment_amount_due ' : fields . function ( _get_amounts_and_date ,
2012-12-06 11:03:15 +00:00
type = ' float ' , string = " Amount Due " ,
2012-12-06 17:26:26 +00:00
store = False , multi = " followup " ,
2012-12-05 14:41:47 +00:00
fnct_search = _payment_due_search ) ,
2012-12-06 17:26:26 +00:00
' payment_amount_overdue ' : fields . function ( _get_amounts_and_date ,
2012-12-06 11:03:15 +00:00
type = ' float ' , string = " Amount Overdue " ,
2012-12-06 17:26:26 +00:00
store = False , multi = " followup " ,
2012-12-05 14:41:47 +00:00
fnct_search = _payment_overdue_search ) ,
2012-12-06 17:26:26 +00:00
' payment_earliest_due_date ' : fields . function ( _get_amounts_and_date ,
2012-12-05 14:41:47 +00:00
type = ' date ' ,
2012-12-06 11:03:15 +00:00
string = " Worst Due Date " ,
2012-12-06 17:26:26 +00:00
multi = " followup " ,
2012-12-06 11:03:15 +00:00
fnct_search = _payment_earliest_date_search ) ,
2012-11-21 14:49:32 +00:00
}
2014-04-22 09:49:26 +00:00
class account_config_settings ( osv . TransientModel ) :
2014-03-13 11:26:25 +00:00
_name = ' account.config.settings '
_inherit = ' account.config.settings '
def open_followup_level_form ( self , cr , uid , ids , context = None ) :
2014-04-22 10:38:02 +00:00
res_ids = self . pool . get ( ' account_followup.followup ' ) . search ( cr , uid , [ ] , context = context )
2014-03-13 12:46:45 +00:00
2014-04-22 09:49:26 +00:00
return {
' type ' : ' ir.actions.act_window ' ,
' name ' : ' Payment Follow-ups ' ,
' res_model ' : ' account_followup.followup ' ,
' res_id ' : res_ids and res_ids [ 0 ] or False ,
' view_mode ' : ' form,tree ' ,
}
2011-09-24 10:42:25 +00:00
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: