[MERGE] lp:~xrg/openobject-addons/trunk-patch18
[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([('modify', 'Modify'), ('refund', 'Refund'), ('cancel', 'Cancel')], "Refund Type", 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         if context is None:
45             context = {}
46         journal = obj_journal.search(cr, uid, [('type', '=', 'sale_refund')])
47         if context.get('type', False):
48             if context['type'] in ('in_invoice', 'in_refund'):
49                 journal = obj_journal.search(cr, uid, [('type', '=', 'purchase_refund')])
50         return journal and journal[0] or False
51
52     _defaults = {
53         'date': lambda *a: time.strftime('%Y-%m-%d'),
54         'journal_id': _get_journal,
55         'filter_refund': 'modify',
56     }
57
58     def fields_view_get(self, cr, uid, view_id=None, view_type=False, context=None, toolbar=False, submenu=False):
59         journal_obj = self.pool.get('account.journal')
60         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)
61         type = context.get('journal_type', 'sale_refund')
62         if type in ('sale', 'sale_refund'):
63             type = 'sale_refund'
64         else:
65             type = 'purchase_refund'
66         for field in res['fields']:
67             if field == 'journal_id':
68                 journal_select = journal_obj._name_search(cr, uid, '', [('type', '=', type)], context=context, limit=None, name_get_uid=1)
69                 res['fields'][field]['selection'] = journal_select
70         return res
71
72     def compute_refund(self, cr, uid, ids, mode='refund', context=None):
73         """
74         @param cr: the current row, from the database cursor,
75         @param uid: the current user’s ID for security checks,
76         @param ids: the account invoice refund’s ID or list of IDs
77
78         """
79         inv_obj = self.pool.get('account.invoice')
80         reconcile_obj = self.pool.get('account.move.reconcile')
81         account_m_line_obj = self.pool.get('account.move.line')
82         mod_obj = self.pool.get('ir.model.data')
83         act_obj = self.pool.get('ir.actions.act_window')
84         wf_service = netsvc.LocalService('workflow')
85         inv_tax_obj = self.pool.get('account.invoice.tax')
86         inv_line_obj = self.pool.get('account.invoice.line')
87         res_users_obj = self.pool.get('res.users')
88         if context is None:
89             context = {}
90
91         for form in  self.read(cr, uid, ids, context=context):
92             created_inv = []
93             date = False
94             period = False
95             description = False
96             company = res_users_obj.browse(cr, uid, uid, context=context).company_id
97             journal_id = form.get('journal_id', False)
98             for inv in inv_obj.browse(cr, uid, context.get('active_ids'), context=context):
99                 if inv.state in ['draft', 'proforma2', 'cancel']:
100                     raise osv.except_osv(_('Error !'), _('Can not %s draft/proforma/cancel invoice.') % (mode))
101                 if inv.reconciled and mode in ('cancel', 'modify'):
102                     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))
103                 if form['period']:
104                     period = form['period']
105                 else:
106                     period = inv.period_id and inv.period_id.id or False
107
108                 if not journal_id:
109                     journal_id = inv.journal_id.id
110
111                 if form['date']:
112                     date = form['date']
113                     if not form['period']:
114                             cr.execute("select name from ir_model_fields \
115                                             where model = 'account.period' \
116                                             and name = 'company_id'")
117                             result_query = cr.fetchone()
118                             if result_query:
119                                 cr.execute("""select p.id from account_fiscalyear y, account_period p where y.id=p.fiscalyear_id \
120                                     and date(%s) between p.date_start AND p.date_stop and y.company_id = %s limit 1""", (date, company.id,))
121                             else:
122                                 cr.execute("""SELECT id
123                                         from account_period where date(%s)
124                                         between date_start AND  date_stop  \
125                                         limit 1 """, (date,))
126                             res = cr.fetchone()
127                             if res:
128                                 period = res[0]
129                 else:
130                     date = inv.date_invoice
131                 if form['description']:
132                     description = form['description']
133                 else:
134                     description = inv.name
135
136                 if not period:
137                     raise osv.except_osv(_('Data Insufficient !'), \
138                                             _('No Period found on Invoice!'))
139
140                 refund_id = inv_obj.refund(cr, uid, [inv.id], date, period, description, journal_id)
141                 refund = inv_obj.browse(cr, uid, refund_id[0], context=context)
142                 inv_obj.write(cr, uid, [refund.id], {'date_due': date,
143                                                 'check_total': inv.check_total})
144                 inv_obj.button_compute(cr, uid, refund_id)
145
146                 created_inv.append(refund_id[0])
147                 if mode in ('cancel', 'modify'):
148                     movelines = inv.move_id.line_id
149                     to_reconcile_ids = {}
150                     for line in movelines:
151                         if line.account_id.id == inv.account_id.id:
152                             to_reconcile_ids[line.account_id.id] = [line.id]
153                         if type(line.reconcile_id) != osv.orm.browse_null:
154                             reconcile_obj.unlink(cr, uid, line.reconcile_id.id)
155                     wf_service.trg_validate(uid, 'account.invoice', \
156                                         refund.id, 'invoice_open', cr)
157                     refund = inv_obj.browse(cr, uid, refund_id[0], context=context)
158                     for tmpline in  refund.move_id.line_id:
159                         if tmpline.account_id.id == inv.account_id.id:
160                             to_reconcile_ids[tmpline.account_id.id].append(tmpline.id)
161                     for account in to_reconcile_ids:
162                         account_m_line_obj.reconcile(cr, uid, to_reconcile_ids[account],
163                                         writeoff_period_id=period,
164                                         writeoff_journal_id = inv.journal_id.id,
165                                         writeoff_acc_id=inv.account_id.id
166                                         )
167                     if mode == 'modify':
168                         invoice = inv_obj.read(cr, uid, [inv.id],
169                                     ['name', 'type', 'number', 'reference',
170                                     'comment', 'date_due', 'partner_id',
171                                     'address_contact_id', 'address_invoice_id',
172                                     'partner_insite', 'partner_contact',
173                                     'partner_ref', 'payment_term', 'account_id',
174                                     'currency_id', 'invoice_line', 'tax_line',
175                                     'journal_id', 'period_id'], context=context)
176                         invoice = invoice[0]
177                         del invoice['id']
178                         invoice_lines = inv_line_obj.read(cr, uid, invoice['invoice_line'], context=context)
179                         invoice_lines = inv_obj._refund_cleanup_lines(cr, uid, invoice_lines)
180                         tax_lines = inv_tax_obj.read(cr, uid, invoice['tax_line'], context=context)
181                         tax_lines = inv_obj._refund_cleanup_lines(cr, uid, tax_lines)
182                         invoice.update({
183                             'type': inv.type,
184                             'date_invoice': date,
185                             'state': 'draft',
186                             'number': False,
187                             'invoice_line': invoice_lines,
188                             'tax_line': tax_lines,
189                             'period_id': period,
190                             'name': description
191                         })
192                         for field in ('address_contact_id', 'address_invoice_id', 'partner_id',
193                                 'account_id', 'currency_id', 'payment_term', 'journal_id'):
194                                 invoice[field] = invoice[field] and invoice[field][0]
195                         inv_id = inv_obj.create(cr, uid, invoice, {})
196                         if inv.payment_term.id:
197                             data = inv_obj.onchange_payment_term_date_invoice(cr, uid, [inv_id], inv.payment_term.id, date)
198                             if 'value' in data and data['value']:
199                                 inv_obj.write(cr, uid, [inv_id], data['value'])
200                         created_inv.append(inv_id)
201             if inv.type in ('out_invoice', 'out_refund'):
202                 xml_id = 'action_invoice_tree3'
203             else:
204                 xml_id = 'action_invoice_tree4'
205             result = mod_obj.get_object_reference(cr, uid, 'account', xml_id)
206             id = result and result[1] or False
207             result = act_obj.read(cr, uid, id, context=context)
208             invoice_domain = eval(result['domain'])
209             invoice_domain.append(('id', 'in', created_inv))
210             result['domain'] = invoice_domain
211             return result
212
213     def invoice_refund(self, cr, uid, ids, context=None):
214         data_refund = self.read(cr, uid, ids, [],context=context)[0]['filter_refund']
215         return self.compute_refund(cr, uid, ids, data_refund, context=context)
216
217
218 account_invoice_refund()
219
220 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: