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 _
26 from openerp import netsvc
28 class account_invoice_refund(osv.osv_memory):
32 _name = "account.invoice.refund"
33 _description = "Invoice Refund"
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'),
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')
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
57 'date': lambda *a: time.strftime('%Y-%m-%d'),
58 'journal_id': _get_journal,
59 'filter_refund': 'refund',
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
81 def compute_refund(self, cr, uid, ids, mode='refund', context=None):
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
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')
100 for form in self.browse(cr, uid, ids, context=context):
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))
113 period = form.period.id
115 period = inv.period_id and inv.period_id.id or False
118 journal_id = inv.journal_id.id
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()
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,))
131 cr.execute("""SELECT id
132 from account_period where date(%s)
133 between date_start AND date_stop \
134 limit 1 """, (date,))
139 date = inv.date_invoice
141 description = form.description
143 description = inv.name
146 raise osv.except_osv(_('Insufficient Data!'), \
147 _('No period found on the invoice.'))
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)
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
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)
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)
192 'date_invoice': date,
195 'invoice_line': invoice_lines,
196 'tax_line': tax_lines,
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
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)
226 account_invoice_refund()
228 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: