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([('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'),
42 def _get_journal(self, cr, uid, context=None):
43 obj_journal = self.pool.get('account.journal')
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
53 'date': lambda *a: time.strftime('%Y-%m-%d'),
54 'journal_id': _get_journal,
55 'filter_refund': 'modify',
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'):
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
72 def compute_refund(self, cr, uid, ids, mode='refund', context=None):
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
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')
91 for form in self.read(cr, uid, ids, context=context):
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))
104 period = form['period']
106 period = inv.period_id and inv.period_id.id or False
109 journal_id = inv.journal_id.id
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()
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,))
122 cr.execute("""SELECT id
123 from account_period where date(%s)
124 between date_start AND date_stop \
125 limit 1 """, (date,))
130 date = inv.date_invoice
131 if form['description']:
132 description = form['description']
134 description = inv.name
137 raise osv.except_osv(_('Data Insufficient !'), \
138 _('No Period found on Invoice!'))
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)
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
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)
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)
184 'date_invoice': date,
187 'invoice_line': invoice_lines,
188 'tax_line': tax_lines,
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'
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
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)
218 account_invoice_refund()
220 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: