Launchpad automatic translations update.
[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 openerp.osv import fields, osv
25 from openerp.tools.translate import _
26 from openerp 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('Date', help='This date will be used as the invoice date for credit note 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 credit note that will be created. If you leave that field empty, it will use the same journal as the current invoice.'),
38        'description': fields.char('Reason', size=128, required=True),
39        'filter_refund': fields.selection([('refund', 'Create a draft refund'), ('cancel', 'Cancel: create refund and reconcile'),('modify', 'Modify: create refund, reconcile and create a new draft invoice')], "Refund Method", required=True, help='Refund 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         if context is None:context = {}
64         journal_obj = self.pool.get('account.journal')
65         user_obj = self.pool.get('res.users')
66         # remove the entry with key 'form_view_ref', otherwise fields_view_get crashes
67         context.pop('form_view_ref', None)
68         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)
69         type = context.get('type', 'out_invoice')
70         company_id = user_obj.browse(cr, uid, uid, context=context).company_id.id
71         journal_type = (type == 'out_invoice') and 'sale_refund' or \
72                        (type == 'out_refund') and 'sale' or \
73                        (type == 'in_invoice') and 'purchase_refund' or \
74                        (type == 'in_refund') and 'purchase'
75         for field in res['fields']:
76             if field == 'journal_id':
77                 journal_select = journal_obj._name_search(cr, uid, '', [('type', '=', journal_type), ('company_id','child_of',[company_id])], context=context)
78                 res['fields'][field]['selection'] = journal_select
79         return res
80
81     def compute_refund(self, cr, uid, ids, mode='refund', context=None):
82         """
83         @param cr: the current row, from the database cursor,
84         @param uid: the current user’s ID for security checks,
85         @param ids: the account invoice refund’s ID or list of IDs
86
87         """
88         inv_obj = self.pool.get('account.invoice')
89         reconcile_obj = self.pool.get('account.move.reconcile')
90         account_m_line_obj = self.pool.get('account.move.line')
91         mod_obj = self.pool.get('ir.model.data')
92         act_obj = self.pool.get('ir.actions.act_window')
93         wf_service = netsvc.LocalService('workflow')
94         inv_tax_obj = self.pool.get('account.invoice.tax')
95         inv_line_obj = self.pool.get('account.invoice.line')
96         res_users_obj = self.pool.get('res.users')
97         if context is None:
98             context = {}
99
100         for form in self.browse(cr, uid, ids, context=context):
101             created_inv = []
102             date = False
103             period = False
104             description = False
105             company = res_users_obj.browse(cr, uid, uid, context=context).company_id
106             journal_id = form.journal_id.id
107             for inv in inv_obj.browse(cr, uid, context.get('active_ids'), context=context):
108                 if inv.state in ['draft', 'proforma2', 'cancel']:
109                     raise osv.except_osv(_('Error!'), _('Cannot %s draft/proforma/cancel invoice.') % (mode))
110                 if inv.reconciled and mode in ('cancel', 'modify'):
111                     raise osv.except_osv(_('Error!'), _('Cannot %s invoice which is already reconciled, invoice should be unreconciled first. You can only refund this invoice.') % (mode))
112                 if form.period.id:
113                     period = form.period.id
114                 else:
115                     period = inv.period_id and inv.period_id.id or False
116
117                 if not journal_id:
118                     journal_id = inv.journal_id.id
119
120                 if form.date:
121                     date = form.date
122                     if not form.period.id:
123                             cr.execute("select name from ir_model_fields \
124                                             where model = 'account.period' \
125                                             and name = 'company_id'")
126                             result_query = cr.fetchone()
127                             if result_query:
128                                 cr.execute("""select p.id from account_fiscalyear y, account_period p where y.id=p.fiscalyear_id \
129                                     and date(%s) between p.date_start AND p.date_stop and y.company_id = %s limit 1""", (date, company.id,))
130                             else:
131                                 cr.execute("""SELECT id
132                                         from account_period where date(%s)
133                                         between date_start AND  date_stop  \
134                                         limit 1 """, (date,))
135                             res = cr.fetchone()
136                             if res:
137                                 period = res[0]
138                 else:
139                     date = inv.date_invoice
140                 if form.description:
141                     description = form.description
142                 else:
143                     description = inv.name
144
145                 if not period:
146                     raise osv.except_osv(_('Insufficient Data!'), \
147                                             _('No period found on the invoice.'))
148
149                 refund_id = inv_obj.refund(cr, uid, [inv.id], date, period, description, journal_id, context=context)
150                 refund = inv_obj.browse(cr, uid, refund_id[0], context=context)
151                 inv_obj.write(cr, uid, [refund.id], {'date_due': date,
152                                                 'check_total': inv.check_total})
153                 inv_obj.button_compute(cr, uid, refund_id)
154
155                 created_inv.append(refund_id[0])
156                 if mode in ('cancel', 'modify'):
157                     movelines = inv.move_id.line_id
158                     to_reconcile_ids = {}
159                     for line in movelines:
160                         if line.account_id.id == inv.account_id.id:
161                             to_reconcile_ids[line.account_id.id] = [line.id]
162                         if line.reconcile_id:
163                             line.reconcile_id.unlink()
164                     wf_service.trg_validate(uid, 'account.invoice', \
165                                         refund.id, 'invoice_open', cr)
166                     refund = inv_obj.browse(cr, uid, refund_id[0], context=context)
167                     for tmpline in  refund.move_id.line_id:
168                         if tmpline.account_id.id == inv.account_id.id:
169                             to_reconcile_ids[tmpline.account_id.id].append(tmpline.id)
170                     for account in to_reconcile_ids:
171                         account_m_line_obj.reconcile(cr, uid, to_reconcile_ids[account],
172                                         writeoff_period_id=period,
173                                         writeoff_journal_id = inv.journal_id.id,
174                                         writeoff_acc_id=inv.account_id.id
175                                         )
176                     if mode == 'modify':
177                         invoice = inv_obj.read(cr, uid, [inv.id],
178                                     ['name', 'type', 'number', 'reference',
179                                     'comment', 'date_due', 'partner_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.browse(cr, uid, invoice['invoice_line'], context=context)
187                         invoice_lines = inv_obj._refund_cleanup_lines(cr, uid, invoice_lines, context=context)
188                         tax_lines = inv_tax_obj.browse(cr, uid, invoice['tax_line'], context=context)
189                         tax_lines = inv_obj._refund_cleanup_lines(cr, uid, tax_lines, context=context)
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 ('partner_id', 'account_id', 'currency_id',
201                                          '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: