1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
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.
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.
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/>.
20 ##############################################################################
24 from openerp.osv import fields, osv
25 from openerp.tools.translate import _
27 class account_invoice_refund(osv.osv_memory):
31 _name = "account.invoice.refund"
32 _description = "Invoice Refund"
34 'date': fields.date('Date', help='This date will be used as the invoice date for credit note and period will be chosen accordingly!'),
35 'period': fields.many2one('account.period', 'Force period'),
36 '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.'),
37 'description': fields.char('Reason', required=True),
38 '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'),
41 def _get_journal(self, cr, uid, context=None):
42 obj_journal = self.pool.get('account.journal')
43 user_obj = self.pool.get('res.users')
46 inv_type = context.get('type', 'out_invoice')
47 company_id = user_obj.browse(cr, uid, uid, context=context).company_id.id
48 type = (inv_type == 'out_invoice') and 'sale_refund' or \
49 (inv_type == 'out_refund') and 'sale' or \
50 (inv_type == 'in_invoice') and 'purchase_refund' or \
51 (inv_type == 'in_refund') and 'purchase'
52 journal = obj_journal.search(cr, uid, [('type', '=', type), ('company_id','=',company_id)], limit=1, context=context)
53 return journal and journal[0] or False
55 def _get_reason(self, cr, uid, context=None):
56 active_id = context and context.get('active_id', False)
58 inv = self.pool.get('account.invoice').browse(cr, uid, active_id, context=context)
64 'date': lambda *a: time.strftime('%Y-%m-%d'),
65 'journal_id': _get_journal,
66 'filter_refund': 'refund',
67 'description': _get_reason,
70 def fields_view_get(self, cr, uid, view_id=None, view_type=False, context=None, toolbar=False, submenu=False):
71 journal_obj = self.pool.get('account.journal')
72 user_obj = self.pool.get('res.users')
73 # remove the entry with key 'form_view_ref', otherwise fields_view_get crashes
74 context = dict(context or {})
75 context.pop('form_view_ref', None)
76 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)
77 type = context.get('type', 'out_invoice')
78 company_id = user_obj.browse(cr, uid, uid, context=context).company_id.id
79 journal_type = (type == 'out_invoice') and 'sale_refund' or \
80 (type == 'out_refund') and 'sale' or \
81 (type == 'in_invoice') and 'purchase_refund' or \
82 (type == 'in_refund') and 'purchase'
83 for field in res['fields']:
84 if field == 'journal_id':
85 journal_select = journal_obj._name_search(cr, uid, '', [('type', '=', journal_type), ('company_id','child_of',[company_id])], context=context)
86 res['fields'][field]['selection'] = journal_select
89 def compute_refund(self, cr, uid, ids, mode='refund', context=None):
91 @param cr: the current row, from the database cursor,
92 @param uid: the current user’s ID for security checks,
93 @param ids: the account invoice refund’s ID or list of IDs
96 inv_obj = self.pool.get('account.invoice')
97 reconcile_obj = self.pool.get('account.move.reconcile')
98 account_m_line_obj = self.pool.get('account.move.line')
99 mod_obj = self.pool.get('ir.model.data')
100 act_obj = self.pool.get('ir.actions.act_window')
101 inv_tax_obj = self.pool.get('account.invoice.tax')
102 inv_line_obj = self.pool.get('account.invoice.line')
103 res_users_obj = self.pool.get('res.users')
107 for form in self.browse(cr, uid, ids, context=context):
112 company = res_users_obj.browse(cr, uid, uid, context=context).company_id
113 journal_id = form.journal_id.id
114 for inv in inv_obj.browse(cr, uid, context.get('active_ids'), context=context):
115 if inv.state in ['draft', 'proforma2', 'cancel']:
116 raise osv.except_osv(_('Error!'), _('Cannot %s draft/proforma/cancel invoice.') % (mode))
117 if inv.reconciled and mode in ('cancel', 'modify'):
118 raise osv.except_osv(_('Error!'), _('Cannot %s invoice which is already reconciled, invoice should be unreconciled first. You can only refund this invoice.') % (mode))
120 period = form.period.id
122 period = inv.period_id and inv.period_id.id or False
125 journal_id = inv.journal_id.id
129 if not form.period.id:
130 cr.execute("select name from ir_model_fields \
131 where model = 'account.period' \
132 and name = 'company_id'")
133 result_query = cr.fetchone()
135 cr.execute("""select p.id from account_fiscalyear y, account_period p where y.id=p.fiscalyear_id \
136 and date(%s) between p.date_start AND p.date_stop and y.company_id = %s limit 1""", (date, company.id,))
138 cr.execute("""SELECT id
139 from account_period where date(%s)
140 between date_start AND date_stop \
141 limit 1 """, (date,))
146 date = inv.date_invoice
148 description = form.description
150 description = inv.name
153 raise osv.except_osv(_('Insufficient Data!'), \
154 _('No period found on the invoice.'))
156 refund_id = inv_obj.refund(cr, uid, [inv.id], date, period, description, journal_id, context=context)
157 refund = inv_obj.browse(cr, uid, refund_id[0], context=context)
158 inv_obj.write(cr, uid, [refund.id], {'date_due': date,
159 'check_total': inv.check_total})
160 inv_obj.button_compute(cr, uid, refund_id)
162 created_inv.append(refund_id[0])
163 if mode in ('cancel', 'modify'):
164 movelines = inv.move_id.line_id
165 to_reconcile_ids = {}
166 for line in movelines:
167 if line.account_id.id == inv.account_id.id:
168 to_reconcile_ids.setdefault(line.account_id.id, []).append(line.id)
169 if line.reconcile_id:
170 line.reconcile_id.unlink()
171 refund.signal_workflow('invoice_open')
172 refund = inv_obj.browse(cr, uid, refund_id[0], context=context)
173 for tmpline in refund.move_id.line_id:
174 if tmpline.account_id.id == inv.account_id.id:
175 to_reconcile_ids[tmpline.account_id.id].append(tmpline.id)
176 for account in to_reconcile_ids:
177 account_m_line_obj.reconcile(cr, uid, to_reconcile_ids[account],
178 writeoff_period_id=period,
179 writeoff_journal_id = inv.journal_id.id,
180 writeoff_acc_id=inv.account_id.id
183 invoice = inv_obj.read(cr, uid, [inv.id],
184 ['name', 'type', 'number', 'reference',
185 'comment', 'date_due', 'partner_id',
186 'partner_insite', 'partner_contact',
187 'partner_ref', 'payment_term', 'account_id',
188 'currency_id', 'invoice_line', 'tax_line',
189 'journal_id', 'period_id'], context=context)
192 invoice_lines = inv_line_obj.browse(cr, uid, invoice['invoice_line'], context=context)
193 invoice_lines = inv_obj._refund_cleanup_lines(cr, uid, invoice_lines, context=context)
194 tax_lines = inv_tax_obj.browse(cr, uid, invoice['tax_line'], context=context)
195 tax_lines = inv_obj._refund_cleanup_lines(cr, uid, tax_lines, context=context)
198 'date_invoice': date,
201 'invoice_line': invoice_lines,
202 'tax_line': tax_lines,
206 for field in ('partner_id', 'account_id', 'currency_id',
207 'payment_term', 'journal_id'):
208 invoice[field] = invoice[field] and invoice[field][0]
209 inv_id = inv_obj.create(cr, uid, invoice, {})
210 if inv.payment_term.id:
211 data = inv_obj.onchange_payment_term_date_invoice(cr, uid, [inv_id], inv.payment_term.id, date)
212 if 'value' in data and data['value']:
213 inv_obj.write(cr, uid, [inv_id], data['value'])
214 created_inv.append(inv_id)
216 xml_id = (inv.type == 'out_refund') and 'action_invoice_tree1' or \
217 (inv.type == 'in_refund') and 'action_invoice_tree2' or \
218 (inv.type == 'out_invoice') and 'action_invoice_tree3' or \
219 (inv.type == 'in_invoice') and 'action_invoice_tree4'
220 result = mod_obj.get_object_reference(cr, uid, 'account', xml_id)
221 id = result and result[1] or False
223 result = act_obj.read(cr, uid, [id], context=context)[0]
224 invoice_domain = eval(result['domain'])
225 invoice_domain.append(('id', 'in', created_inv))
226 result['domain'] = invoice_domain
229 def invoice_refund(self, cr, uid, ids, context=None):
230 data_refund = self.read(cr, uid, ids, ['filter_refund'],context=context)[0]['filter_refund']
231 return self.compute_refund(cr, uid, ids, data_refund, context=context)
235 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: