2009-10-13 05:58:37 +00:00
# -*- coding: utf-8 -*-
2006-12-07 13:41:40 +00:00
##############################################################################
2009-11-13 05:41:16 +00:00
#
2009-01-28 11:42:54 +00:00
# OpenERP, Open Source Management Solution
2010-01-12 09:18:39 +00:00
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
2008-06-16 11:00:21 +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
2009-11-13 05:41:16 +00:00
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2006-12-07 13:41:40 +00:00
#
##############################################################################
2010-08-16 11:57:03 +00:00
2006-12-07 13:41:40 +00:00
import datetime
2007-08-02 14:33:25 +00:00
import time
2006-12-07 13:41:40 +00:00
2008-06-02 13:19:37 +00:00
import tools
from osv import fields , osv
2008-07-08 08:13:12 +00:00
from tools . translate import _
2008-06-02 13:19:37 +00:00
2010-11-03 15:59:52 +00:00
class account_followup_stat_by_partner ( osv . osv ) :
_name = " account_followup.stat.by.partner "
2012-06-26 07:10:44 +00:00
_description = " Follow-up Statistics by Partner "
2010-11-03 15:59:52 +00:00
_rec_name = ' partner_id '
_auto = False
_columns = {
' partner_id ' : fields . many2one ( ' res.partner ' , ' Partner ' , readonly = True ) ,
' date_move ' : fields . date ( ' First move ' , readonly = True ) ,
' date_move_last ' : fields . date ( ' Last move ' , readonly = True ) ,
2012-06-26 07:10:44 +00:00
' date_followup ' : fields . date ( ' Latest follow-up ' , readonly = True ) ,
2010-11-03 15:59:52 +00:00
' max_followup_id ' : fields . many2one ( ' account_followup.followup.line ' ,
' Max Follow Up Level ' , readonly = True , ondelete = " cascade " ) ,
' balance ' : fields . float ( ' Balance ' , readonly = True ) ,
' company_id ' : fields . many2one ( ' res.company ' , ' Company ' , readonly = True ) ,
}
def init ( self , cr ) :
tools . drop_view_if_exists ( cr , ' account_followup_stat_by_partner ' )
2011-11-17 14:51:49 +00:00
# Here we don't have other choice but to create a virtual ID based on the concatenation
2012-02-10 08:58:25 +00:00
# of the partner_id and the company_id, because if a partner is shared between 2 companies,
2012-03-30 05:09:55 +00:00
# we want to see 2 lines for him in this table. It means that both company should be able
2012-06-26 07:10:44 +00:00
# to send him follow-ups separately . An assumption that the number of companies will not
2012-02-10 08:58:25 +00:00
# reach 10 000 records is made, what should be enough for a time.
2010-11-03 15:59:52 +00:00
cr . execute ( """
create or replace view account_followup_stat_by_partner as (
SELECT
2012-02-09 18:16:38 +00:00
l . partner_id * 10000 + l . company_id as id ,
2010-11-03 15:59:52 +00:00
l . partner_id AS partner_id ,
min ( l . date ) AS date_move ,
max ( l . date ) AS date_move_last ,
max ( l . followup_date ) AS date_followup ,
max ( l . followup_line_id ) AS max_followup_id ,
sum ( l . debit - l . credit ) AS balance ,
2010-11-05 05:05:51 +00:00
l . company_id as company_id
2010-11-03 15:59:52 +00:00
FROM
account_move_line l
LEFT JOIN account_account a ON ( l . account_id = a . id )
WHERE
a . active AND
a . type = ' receivable ' AND
l . reconcile_id is NULL AND
2012-11-06 17:15:22 +00:00
l . partner_id IS NOT NULL AND
( l . blocked = False )
2010-11-03 15:59:52 +00:00
GROUP BY
2010-11-10 08:20:11 +00:00
l . partner_id , l . company_id
2012-11-06 17:15:22 +00:00
) """ ) #Blocked is to take into account litigation
2010-11-03 15:59:52 +00:00
account_followup_stat_by_partner ( )
2012-11-15 08:16:34 +00:00
class account_followup_sending_results ( osv . osv_memory ) :
def do_report ( self , cr , uid , ids , context = None ) :
2012-11-30 16:24:01 +00:00
if context is None :
context = { }
return context . get ( ' report_data ' )
2012-11-20 12:40:26 +00:00
def do_done ( self , cr , uid , ids , context = None ) :
return { }
2012-11-30 16:24:01 +00:00
2012-11-15 08:16:34 +00:00
def _get_description ( self , cr , uid , context = None ) :
2012-11-30 16:24:01 +00:00
if context is None :
context = { }
return context . get ( ' description ' )
2012-11-20 12:40:26 +00:00
def _get_need_printing ( self , cr , uid , context = None ) :
2012-11-30 16:24:01 +00:00
if context is None :
context = { }
return context . get ( ' needprinting ' )
2012-11-27 16:53:17 +00:00
_name = ' account_followup.sending.results '
2012-11-15 08:16:34 +00:00
_description = ' Results from the sending of the different letters and emails '
_columns = {
2012-11-30 16:24:01 +00:00
' description ' : fields . text ( " Description " , readonly = True ) ,
' needprinting ' : fields . boolean ( " Needs Printing " )
}
2012-11-15 08:16:34 +00:00
_defaults = {
2012-11-30 16:24:01 +00:00
' needprinting ' : _get_need_printing ,
' description ' : _get_description ,
}
account_followup_sending_results ( )
2012-11-15 08:16:34 +00:00
2012-10-25 09:15:44 +00:00
class account_followup_print ( osv . osv_memory ) :
2012-11-27 16:53:17 +00:00
_name = ' account_followup.print '
2012-06-26 07:10:44 +00:00
_description = ' Print Follow-up & Send Mail to Customers '
2010-04-29 04:49:02 +00:00
_columns = {
2012-11-23 17:12:36 +00:00
' date ' : fields . date ( ' Follow-up Sending Date ' , required = True ,
help = " This field allow you to select a forecast date to plan your follow-ups " ) ,
2012-10-25 09:15:44 +00:00
' followup_id ' : fields . many2one ( ' account_followup.followup ' , ' Follow-Up ' , required = True , readonly = True ) ,
2012-11-23 17:12:36 +00:00
' partner_ids ' : fields . many2many ( ' account_followup.stat.by.partner ' , ' partner_stat_rel ' ,
' osv_memory_id ' , ' partner_id ' , ' Partners ' , required = True ) ,
2012-11-30 16:24:01 +00:00
' company_id ' : fields . related ( ' followup_id ' , ' company_id ' , type = ' many2one ' ,
relation = ' res.company ' , store = True , readonly = True ) ,
2012-06-26 09:16:51 +00:00
' email_conf ' : fields . boolean ( ' Send Email Confirmation ' ) ,
2010-04-29 04:49:02 +00:00
' email_subject ' : fields . char ( ' Email Subject ' , size = 64 ) ,
2012-11-30 16:24:01 +00:00
' partner_lang ' : fields . boolean ( ' Send Email in Partner Language ' ,
2012-11-23 17:12:36 +00:00
help = ' Do not change message text, if you want to send email in partner language, or configure from company ' ) ,
2012-06-26 09:16:51 +00:00
' email_body ' : fields . text ( ' Email Body ' ) ,
2012-10-26 11:06:21 +00:00
' summary ' : fields . text ( ' Summary ' , readonly = True ) ,
2012-11-23 17:12:36 +00:00
' test_print ' : fields . boolean ( ' Test Print ' ,
2012-11-30 16:24:01 +00:00
help = ' Check if you want to print follow-ups without changing follow-ups level. ' ) ,
2010-10-11 10:51:16 +00:00
}
2012-10-25 09:15:44 +00:00
def _get_followup ( self , cr , uid , context = None ) :
2010-07-03 09:30:47 +00:00
if context is None :
context = { }
2012-10-25 09:15:44 +00:00
if context . get ( ' active_model ' , ' ir.ui.menu ' ) == ' account_followup.followup ' :
return context . get ( ' active_id ' , False )
company_id = self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid , context = context ) . company_id . id
followp_id = self . pool . get ( ' account_followup.followup ' ) . search ( cr , uid , [ ( ' company_id ' , ' = ' , company_id ) ] , context = context )
return followp_id and followp_id [ 0 ] or False
2008-06-02 13:19:37 +00:00
2012-10-26 08:09:14 +00:00
def process_partners ( self , cr , uid , partner_ids , data , context = None ) :
partner_obj = self . pool . get ( ' res.partner ' )
partner_ids_to_print = [ ]
2012-11-15 08:16:34 +00:00
nbmanuals = 0
manuals = { }
nbmails = 0
2012-11-20 12:40:26 +00:00
nbunknownmails = 0
2012-11-15 08:16:34 +00:00
nbprints = 0
2012-11-20 12:40:26 +00:00
resulttext = " "
2012-10-26 08:09:14 +00:00
for partner in self . pool . get ( ' account_followup.stat.by.partner ' ) . browse ( cr , uid , partner_ids , context = context ) :
2012-11-22 09:22:19 +00:00
if partner . max_followup_id . manual_action :
2012-11-27 16:53:17 +00:00
partner_obj . do_partner_manual_action ( cr , uid , [ partner . partner_id . id ] , context = context )
2012-11-22 15:30:49 +00:00
nbmanuals = nbmanuals + 1
2012-11-21 14:49:32 +00:00
key = partner . partner_id . payment_responsible_id . name or _ ( " Nobody " )
2012-11-22 15:30:49 +00:00
if not key in manuals . keys ( ) :
2012-11-20 12:40:26 +00:00
manuals [ key ] = 1
else :
manuals [ key ] = manuals [ key ] + 1
2012-11-22 15:30:49 +00:00
if partner . max_followup_id . send_email :
2012-11-27 16:53:17 +00:00
nbunknownmails + = partner_obj . do_partner_mail ( cr , uid , [ partner . partner_id . id ] , context = context )
2012-11-20 12:40:26 +00:00
nbmails + = 1
2012-10-26 08:09:14 +00:00
if partner . max_followup_id . send_letter :
partner_ids_to_print . append ( partner . id )
2012-11-20 12:40:26 +00:00
nbprints + = 1
2012-11-23 15:42:33 +00:00
message = _ ( " Follow-up letter of " ) + " <I> " + partner . partner_id . latest_followup_level_id_without_lit . name + " </I> " + _ ( " will be sent " )
2012-11-22 15:30:49 +00:00
partner_obj . message_post ( cr , uid , [ partner . partner_id . id ] , body = message , context = context )
if nbunknownmails == 0 :
2012-11-23 17:12:36 +00:00
resulttext + = str ( nbmails ) + _ ( " email(s) sent " )
2012-11-22 15:30:49 +00:00
else :
2012-11-23 17:12:36 +00:00
resulttext + = str ( nbmails ) + _ ( " email(s) should have been sent, but " ) + str ( nbunknownmails ) + _ ( " had unknown email address(es) " ) + " \n <BR/> "
resulttext + = " <BR/> " + str ( nbprints ) + _ ( " letter(s) in report " ) + " \n <BR/> " + str ( nbmanuals ) + _ ( " manual action(s) assigned: " )
2012-11-20 12:40:26 +00:00
needprinting = False
if nbprints > 0 :
needprinting = True
2012-11-23 15:42:33 +00:00
resulttext + = " <p align= \" center \" > "
2012-11-22 15:30:49 +00:00
for item in manuals :
2012-11-22 09:22:19 +00:00
resulttext = resulttext + " <li> " + item + " : " + str ( manuals [ item ] ) + " \n </li> "
2012-11-23 15:42:33 +00:00
resulttext + = " </p> "
2012-11-20 12:40:26 +00:00
result = { }
2012-11-27 16:53:17 +00:00
action = partner_obj . do_partner_print ( cr , uid , partner_ids_to_print , data , context = context )
2012-11-20 12:40:26 +00:00
result [ ' needprinting ' ] = needprinting
result [ ' resulttext ' ] = resulttext
2012-11-20 15:29:59 +00:00
result [ ' action ' ] = action or { }
2012-11-20 12:40:26 +00:00
return result
2012-10-26 08:09:14 +00:00
def do_update_followup_level ( self , cr , uid , to_update , partner_list , date , context = None ) :
2012-11-23 15:42:33 +00:00
#update the follow-up level on account.move.line
2012-10-25 09:15:44 +00:00
for id in to_update . keys ( ) :
2012-10-26 08:09:14 +00:00
if to_update [ id ] [ ' partner_id ' ] in partner_list :
2012-11-28 13:12:21 +00:00
self . pool . get ( ' account.move.line ' ) . write ( cr , uid , [ int ( id ) ] , { ' followup_line_id ' : to_update [ id ] [ ' level ' ] ,
' followup_date ' : date } )
2012-10-25 09:15:44 +00:00
2012-11-27 16:53:17 +00:00
def clear_manual_actions ( self , cr , uid , partner_list , context = None ) :
2012-11-28 13:12:21 +00:00
# Partnerlist is list to exclude
2012-11-29 13:53:46 +00:00
# Will clear the actions of partners that have no due payments anymore
2012-11-27 16:53:17 +00:00
partner_list_ids = [ partner . partner_id . id for partner in self . pool . get ( ' account_followup.stat.by.partner ' ) . browse ( cr , uid , partner_list , context = context ) ]
2012-11-29 13:53:46 +00:00
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 )
2012-11-28 13:12:21 +00:00
return len ( ids )
2012-11-27 16:53:17 +00:00
2012-10-25 09:15:44 +00:00
def do_process ( self , cr , uid , ids , context = None ) :
2012-11-30 16:24:01 +00:00
if context is None :
context = { }
2012-11-27 16:53:17 +00:00
#Get partners
2012-10-25 09:15:44 +00:00
tmp = self . _get_partners_followp ( cr , uid , ids , context = context )
partner_list = tmp [ ' partner_ids ' ]
to_update = tmp [ ' to_update ' ]
2012-11-27 16:53:17 +00:00
date = self . browse ( cr , uid , ids , context = context ) [ 0 ] . date
data = self . read ( cr , uid , ids , [ ] , context = context ) [ 0 ]
2012-10-26 15:11:25 +00:00
data [ ' followup_id ' ] = data [ ' followup_id ' ] [ 0 ]
2012-11-30 16:24:01 +00:00
#Update partners
2012-10-26 08:09:14 +00:00
self . do_update_followup_level ( cr , uid , to_update , partner_list , date , context = context )
2012-11-30 16:24:01 +00:00
#process the partners (send mails...)
2012-11-20 12:40:26 +00:00
restot = self . process_partners ( cr , uid , partner_list , data , context = context )
2012-11-30 16:24:01 +00:00
#clear the manual actions if nothing is due anymore
2012-11-29 13:53:46 +00:00
nbactionscleared = self . clear_manual_actions ( cr , uid , partner_list , context = context )
if nbactionscleared > 0 :
2012-11-30 16:24:01 +00:00
restot [ ' resulttext ' ] = restot [ ' resulttext ' ] + " <li> " + _ ( " %s partners have no credits and as such the action is cleared " ) % ( str ( nbactionscleared ) ) + " </li> "
2012-11-27 16:53:17 +00:00
res = restot [ ' action ' ]
2012-11-30 16:24:01 +00:00
#return the next action
2012-11-15 08:16:34 +00:00
mod_obj = self . pool . get ( ' ir.model.data ' )
model_data_ids = mod_obj . search ( cr , uid , [ ( ' model ' , ' = ' , ' ir.ui.view ' ) , ( ' name ' , ' = ' , ' view_account_followup_sending_results ' ) ] , context = context )
resource_id = mod_obj . read ( cr , uid , model_data_ids , fields = [ ' res_id ' ] , context = context ) [ 0 ] [ ' res_id ' ]
2012-11-20 12:40:26 +00:00
context . update ( { ' description ' : restot [ ' resulttext ' ] , ' needprinting ' : restot [ ' needprinting ' ] , ' report_data ' : res } )
2012-11-15 08:16:34 +00:00
return {
2012-11-20 12:40:26 +00:00
' name ' : _ ( ' Send Letters and Emails: Actions Summary ' ) ,
2012-11-15 08:16:34 +00:00
' view_type ' : ' form ' ,
' context ' : context ,
' view_mode ' : ' tree,form ' ,
2012-11-28 13:12:21 +00:00
' res_model ' : ' account_followup.sending.results ' ,
2012-11-15 08:16:34 +00:00
' views ' : [ ( resource_id , ' form ' ) ] ,
' type ' : ' ir.actions.act_window ' ,
' target ' : ' new ' ,
}
2012-10-26 08:09:14 +00:00
2010-04-29 04:49:02 +00:00
def _get_msg ( self , cr , uid , context = None ) :
2012-11-20 12:40:26 +00:00
return self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid , context = context ) . company_id . follow_up_msg
2006-12-07 13:41:40 +00:00
2010-04-29 04:49:02 +00:00
_defaults = {
2012-10-26 11:34:54 +00:00
' date ' : lambda * a : time . strftime ( ' % Y- % m- %d ' ) ,
' followup_id ' : _get_followup ,
2012-11-06 17:15:22 +00:00
' email_body ' : " " ,
2012-10-26 11:06:21 +00:00
' email_subject ' : _ ( ' Invoices Reminder ' ) ,
' partner_lang ' : True ,
2012-10-26 11:23:31 +00:00
}
2012-10-25 09:15:44 +00:00
2010-04-29 04:49:02 +00:00
def _get_partners_followp ( self , cr , uid , ids , context = None ) :
2012-11-23 15:42:33 +00:00
data = { }
2012-10-26 08:09:14 +00:00
data = self . browse ( cr , uid , ids , context = context ) [ 0 ]
company_id = data . company_id . id
2012-10-23 12:23:24 +00:00
2010-04-29 04:49:02 +00:00
cr . execute (
" SELECT l.partner_id, l.followup_line_id,l.date_maturity, l.date, l.id " \
" FROM account_move_line AS l " \
" LEFT JOIN account_account AS a " \
" ON (l.account_id=a.id) " \
" WHERE (l.reconcile_id IS NULL) " \
" AND (a.type= ' receivable ' ) " \
" AND (l.state<> ' draft ' ) " \
" AND (l.partner_id is NOT NULL) " \
" AND (a.active) " \
2010-09-24 09:26:53 +00:00
" AND (l.debit > 0) " \
2012-11-06 17:15:22 +00:00
" AND (l.company_id = %s ) " \
2012-10-30 11:05:21 +00:00
" AND (l.blocked = False) " \
2012-11-07 16:51:30 +00:00
" ORDER BY l.date " , ( company_id , ) ) #l.blocked added to take litigation into account and it is not necessary to change follow-up level of account move lines without debit
2010-04-29 04:49:02 +00:00
move_lines = cr . fetchall ( )
old = None
fups = { }
2011-02-15 09:20:57 +00:00
fup_id = ' followup_id ' in context and context [ ' followup_id ' ] or data . followup_id . id
date = ' date ' in context and context [ ' date ' ] or data . date
2007-06-26 08:26:35 +00:00
2010-04-29 04:49:02 +00:00
current_date = datetime . date ( * time . strptime ( date ,
' % Y- % m- %d ' ) [ : 3 ] )
cr . execute (
" SELECT * " \
" FROM account_followup_followup_line " \
" WHERE followup_id= %s " \
2011-05-06 12:28:19 +00:00
" ORDER BY delay " , ( fup_id , ) )
2012-11-06 17:15:22 +00:00
#Create dictionary of tuples where first element is the date to compare with the due date and second element is the id of the next level
2010-04-29 04:49:02 +00:00
for result in cr . dictfetchall ( ) :
delay = datetime . timedelta ( days = result [ ' delay ' ] )
fups [ old ] = ( current_date - delay , result [ ' id ' ] )
old = result [ ' id ' ]
2012-11-30 16:24:01 +00:00
2010-04-29 04:49:02 +00:00
partner_list = [ ]
to_update = { }
2012-11-06 17:15:22 +00:00
#Fill dictionary of accountmovelines to_update with the partners that need to be updated
2010-04-29 04:49:02 +00:00
for partner_id , followup_line_id , date_maturity , date , id in move_lines :
if not partner_id :
continue
if followup_line_id not in fups :
continue
2011-11-17 14:51:49 +00:00
stat_line_id = partner_id * 10000 + company_id
2010-04-29 04:49:02 +00:00
if date_maturity :
if date_maturity < = fups [ followup_line_id ] [ 0 ] . strftime ( ' % Y- % m- %d ' ) :
2012-02-13 16:03:19 +00:00
if stat_line_id not in partner_list :
2011-11-17 14:51:49 +00:00
partner_list . append ( stat_line_id )
2012-02-13 16:03:19 +00:00
to_update [ str ( id ) ] = { ' level ' : fups [ followup_line_id ] [ 1 ] , ' partner_id ' : stat_line_id }
2010-04-29 04:49:02 +00:00
elif date and date < = fups [ followup_line_id ] [ 0 ] . strftime ( ' % Y- % m- %d ' ) :
2012-02-13 16:03:19 +00:00
if stat_line_id not in partner_list :
2011-11-17 14:51:49 +00:00
partner_list . append ( stat_line_id )
to_update [ str ( id ) ] = { ' level ' : fups [ followup_line_id ] [ 1 ] , ' partner_id ' : stat_line_id }
2010-04-29 04:49:02 +00:00
return { ' partner_ids ' : partner_list , ' to_update ' : to_update }
2008-06-02 13:19:37 +00:00
2012-10-26 11:06:21 +00:00
account_followup_print ( )
2012-10-25 09:15:44 +00:00
2010-11-03 15:59:52 +00:00
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: