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 osv import fields, osv
25 from tools.translate import _
28 class account_invoice_refund(osv.osv_memory):
32 _name = "account.invoice.refund"
33 _description = "Invoice Refund"
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'),
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 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
80 def compute_refund(self, cr, uid, ids, mode='refund', context=None):
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
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')
99 for form in self.browse(cr, uid, ids, context=context):
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))
112 period = form.period.id
114 period = inv.period_id and inv.period_id.id or False
117 journal_id = inv.journal_id.id
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()
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,))
130 cr.execute("""SELECT id
131 from account_period where date(%s)
132 between date_start AND date_stop \
133 limit 1 """, (date,))
138 date = inv.date_invoice
140 description = form.description
142 description = inv.name
145 raise osv.except_osv(_('Data Insufficient !'), \
146 _('No Period found on Invoice!'))
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)
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
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)
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)
192 'date_invoice': date,
195 'invoice_line': invoice_lines,
196 'tax_line': tax_lines,
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
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: