[MERGE] lp881356
[odoo/odoo.git] / addons / account / wizard / account_invoice_refund.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
6 #
7 #    This program is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU Affero General Public License as
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU Affero General Public License for more details.
16 #
17 #    You should have received a copy of the GNU Affero General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20 ##############################################################################
21
22 import time
23
24 from osv import fields, osv
25 from tools.translate import _
26 import netsvc
27
28 class account_invoice_refund(osv.osv_memory):
29
30     """Refunds invoice"""
31
32     _name = "account.invoice.refund"
33     _description = "Invoice Refund"
34     _columns = {
35        'date': fields.date('Operation date', help='This date will be used as the invoice date for Refund Invoice and Period will be chosen accordingly!'),
36        'period': fields.many2one('account.period', 'Force period'),
37        'journal_id': fields.many2one('account.journal', 'Refund Journal', help='You can select here the journal to use for the refund invoice that will be created. If you leave that field empty, it will use the same journal as the current invoice.'),
38        'description': fields.char('Description', size=128, required=True),
39        'filter_refund': fields.selection([('refund', 'Create a draft Refund'), ('cancel', 'Cancel: refund invoice and reconcile'),('modify', 'Modify: refund invoice, reconcile and create a new draft invoice')], "Refund Method", required=True, help='Refund invoice base on this type. You can not Modify and Cancel if the invoice is already reconciled'),
40     }
41
42     def _get_journal(self, cr, uid, context=None):
43         obj_journal = self.pool.get('account.journal')
44         user_obj = self.pool.get('res.users')
45         if context is None:
46             context = {}
47         inv_type = context.get('type', 'out_invoice')
48         company_id = user_obj.browse(cr, uid, uid, context=context).company_id.id
49         type = (inv_type == 'out_invoice') and 'sale_refund' or \
50                (inv_type == 'out_refund') and 'sale' or \
51                (inv_type == 'in_invoice') and 'purchase_refund' or \
52                (inv_type == 'in_refund') and 'purchase'
53         journal = obj_journal.search(cr, uid, [('type', '=', type), ('company_id','=',company_id)], limit=1, context=context)
54         return journal and journal[0] or False
55
56     _defaults = {
57         'date': lambda *a: time.strftime('%Y-%m-%d'),
58         'journal_id': _get_journal,
59         'filter_refund': 'refund',
60     }
61
62     def fields_view_get(self, cr, uid, view_id=None, view_type=False, context=None, toolbar=False, submenu=False):
63         journal_obj = self.pool.get('account.journal')
64         user_obj = self.pool.get('res.users')
65         # remove the entry with key 'form_view_ref', otherwise fields_view_get crashes
66         context.pop('form_view_ref', None)
67         res = super(account_invoice_refund,self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu)
68         type = context.get('type', 'out_invoice')
69         company_id = user_obj.browse(cr, uid, uid, context=context).company_id.id
70         journal_type = (type == 'out_invoice') and 'sale_refund' or \
71                        (type == 'out_refund') and 'sale' or \
72                        (type == 'in_invoice') and 'purchase_refund' or \
73                        (type == 'in_refund') and 'purchase'
74         for field in res['fields']:
75             if field == 'journal_id':
76                 journal_select = journal_obj._name_search(cr, uid, '', [('type', '=', journal_type), ('company_id','child_of',[company_id])], context=context)
77                 res['fields'][field]['selection'] = journal_select
78         return res
79
80     def compute_refund(self, cr, uid, ids, mode='refund', context=None):
81         """
82         @param cr: the current row, from the database cursor,
83         @param uid: the current user’s ID for security checks,
84         @param ids: the account invoice refund’s ID or list of IDs
85
86         """
87         inv_obj = self.pool.get('account.invoice')
88         reconcile_obj = self.pool.get('account.move.reconcile')
89         account_m_line_obj = self.pool.get('account.move.line')
90         mod_obj = self.pool.get('ir.model.data')
91         act_obj = self.pool.get('ir.actions.act_window')
92         wf_service = netsvc.LocalService('workflow')
93         inv_tax_obj = self.pool.get('account.invoice.tax')
94         inv_line_obj = self.pool.get('account.invoice.line')
95         res_users_obj = self.pool.get('res.users')
96         if context is None:
97             context = {}
98
99         for form in self.browse(cr, uid, ids, context=context):
100             created_inv = []
101             date = False
102             period = False
103             description = False
104             company = res_users_obj.browse(cr, uid, uid, context=context).company_id
105             journal_id = form.journal_id.id
106             for inv in inv_obj.browse(cr, uid, context.get('active_ids'), context=context):
107                 if inv.state in ['draft', 'proforma2', 'cancel']:
108                     raise osv.except_osv(_('Error !'), _('Can not %s draft/proforma/cancel invoice.') % (mode))
109                 if inv.reconciled and mode in ('cancel', 'modify'):
110                     raise osv.except_osv(_('Error !'), _('Can not %s invoice which is already reconciled, invoice should be unreconciled first. You can only Refund this invoice') % (mode))
111                 if form.period.id:
112                     period = form.period.id
113                 else:
114                     period = inv.period_id and inv.period_id.id or False
115
116                 if not journal_id:
117                     journal_id = inv.journal_id.id
118
119                 if form.date:
120                     date = form.date
121                     if not form.period.id:
122                             cr.execute("select name from ir_model_fields \
123                                             where model = 'account.period' \
124                                             and name = 'company_id'")
125                             result_query = cr.fetchone()
126                             if result_query:
127                                 cr.execute("""select p.id from account_fiscalyear y, account_period p where y.id=p.fiscalyear_id \
128                                     and date(%s) between p.date_start AND p.date_stop and y.company_id = %s limit 1""", (date, company.id,))
129                             else:
130                                 cr.execute("""SELECT id
131                                         from account_period where date(%s)
132                                         between date_start AND  date_stop  \
133                                         limit 1 """, (date,))
134                             res = cr.fetchone()
135                             if res:
136                                 period = res[0]
137                 else:
138                     date = inv.date_invoice
139                 if form.description:
140                     description = form.description
141                 else:
142                     description = inv.name
143
144                 if not period:
145                     raise osv.except_osv(_('Data Insufficient !'), \
146                                             _('No Period found on Invoice!'))
147
148                 refund_id = inv_obj.refund(cr, uid, [inv.id], date, period, description, journal_id)
149                 refund = inv_obj.browse(cr, uid, refund_id[0], context=context)
150                 inv_obj.write(cr, uid, [refund.id], {'date_due': date,
151                                                 'check_total': inv.check_total})
152                 inv_obj.button_compute(cr, uid, refund_id)
153
154                 created_inv.append(refund_id[0])
155                 if mode in ('cancel', 'modify'):
156                     movelines = inv.move_id.line_id
157                     to_reconcile_ids = {}
158                     for line in movelines:
159                         if line.account_id.id == inv.account_id.id:
160                             to_reconcile_ids[line.account_id.id] = [line.id]
161                         if type(line.reconcile_id) != osv.orm.browse_null:
162                             reconcile_obj.unlink(cr, uid, line.reconcile_id.id)
163                     wf_service.trg_validate(uid, 'account.invoice', \
164                                         refund.id, 'invoice_open', cr)
165                     refund = inv_obj.browse(cr, uid, refund_id[0], context=context)
166                     for tmpline in  refund.move_id.line_id:
167                         if tmpline.account_id.id == inv.account_id.id:
168                             to_reconcile_ids[tmpline.account_id.id].append(tmpline.id)
169                     for account in to_reconcile_ids:
170                         account_m_line_obj.reconcile(cr, uid, to_reconcile_ids[account],
171                                         writeoff_period_id=period,
172                                         writeoff_journal_id = inv.journal_id.id,
173                                         writeoff_acc_id=inv.account_id.id
174                                         )
175                     if mode == 'modify':
176                         invoice = inv_obj.read(cr, uid, [inv.id],
177                                     ['name', 'type', 'number', 'reference',
178                                     'comment', 'date_due', 'partner_id',
179                                     'address_contact_id', 'address_invoice_id',
180                                     'partner_insite', 'partner_contact',
181                                     'partner_ref', 'payment_term', 'account_id',
182                                     'currency_id', 'invoice_line', 'tax_line',
183                                     'journal_id', 'period_id'], context=context)
184                         invoice = invoice[0]
185                         del invoice['id']
186                         invoice_lines = inv_line_obj.read(cr, uid, invoice['invoice_line'], context=context)
187                         invoice_lines = inv_obj._refund_cleanup_lines(cr, uid, invoice_lines)
188                         tax_lines = inv_tax_obj.read(cr, uid, invoice['tax_line'], context=context)
189                         tax_lines = inv_obj._refund_cleanup_lines(cr, uid, tax_lines)
190                         invoice.update({
191                             'type': inv.type,
192                             'date_invoice': date,
193                             'state': 'draft',
194                             'number': False,
195                             'invoice_line': invoice_lines,
196                             'tax_line': tax_lines,
197                             'period_id': period,
198                             'name': description
199                         })
200                         for field in ('address_contact_id', 'address_invoice_id', 'partner_id',
201                                 'account_id', 'currency_id', 'payment_term', 'journal_id'):
202                                 invoice[field] = invoice[field] and invoice[field][0]
203                         inv_id = inv_obj.create(cr, uid, invoice, {})
204                         if inv.payment_term.id:
205                             data = inv_obj.onchange_payment_term_date_invoice(cr, uid, [inv_id], inv.payment_term.id, date)
206                             if 'value' in data and data['value']:
207                                 inv_obj.write(cr, uid, [inv_id], data['value'])
208                         created_inv.append(inv_id)
209             xml_id = (inv.type == 'out_refund') and 'action_invoice_tree1' or \
210                      (inv.type == 'in_refund') and 'action_invoice_tree2' or \
211                      (inv.type == 'out_invoice') and 'action_invoice_tree3' or \
212                      (inv.type == 'in_invoice') and 'action_invoice_tree4'
213             result = mod_obj.get_object_reference(cr, uid, 'account', xml_id)
214             id = result and result[1] or False
215             result = act_obj.read(cr, uid, id, context=context)
216             invoice_domain = eval(result['domain'])
217             invoice_domain.append(('id', 'in', created_inv))
218             result['domain'] = invoice_domain
219             return result
220
221     def invoice_refund(self, cr, uid, ids, context=None):
222         data_refund = self.read(cr, uid, ids, ['filter_refund'],context=context)[0]['filter_refund']
223         return self.compute_refund(cr, uid, ids, data_refund, context=context)
224
225
226 account_invoice_refund()
227
228 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: