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', 'Refund'), ('cancel', 'Cancel'),('modify', 'Modify')], "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')
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 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)
66 type = context.get('type', 'out_invoice')
67 company_id = user_obj.browse(cr, uid, uid, context=context).company_id.id
68 journal_type = (type == 'out_invoice') and 'sale_refund' or \
69 (type == 'out_refund') and 'sale' or \
70 (type == 'in_invoice') and 'purchase_refund' or \
71 (type == 'in_refund') and 'purchase'
72 for field in res['fields']:
73 if field == 'journal_id':
74 journal_select = journal_obj._name_search(cr, uid, '', [('type', '=', journal_type), ('company_id','child_of',[company_id])], context=context)
75 res['fields'][field]['selection'] = journal_select
78 def compute_refund(self, cr, uid, ids, mode='refund', context=None):
80 @param cr: the current row, from the database cursor,
81 @param uid: the current user’s ID for security checks,
82 @param ids: the account invoice refund’s ID or list of IDs
85 inv_obj = self.pool.get('account.invoice')
86 reconcile_obj = self.pool.get('account.move.reconcile')
87 account_m_line_obj = self.pool.get('account.move.line')
88 mod_obj = self.pool.get('ir.model.data')
89 act_obj = self.pool.get('ir.actions.act_window')
90 wf_service = netsvc.LocalService('workflow')
91 inv_tax_obj = self.pool.get('account.invoice.tax')
92 inv_line_obj = self.pool.get('account.invoice.line')
93 res_users_obj = self.pool.get('res.users')
97 for form in self.browse(cr, uid, ids, context=context):
102 company = res_users_obj.browse(cr, uid, uid, context=context).company_id
103 journal_id = form.journal_id.id
104 for inv in inv_obj.browse(cr, uid, context.get('active_ids'), context=context):
105 if inv.state in ['draft', 'proforma2', 'cancel']:
106 raise osv.except_osv(_('Error !'), _('Can not %s draft/proforma/cancel invoice.') % (mode))
107 if inv.reconciled and mode in ('cancel', 'modify'):
108 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))
110 period = form.period.id
112 period = inv.period_id and inv.period_id.id or False
115 journal_id = inv.journal_id.id
119 if not form.period.id:
120 cr.execute("select name from ir_model_fields \
121 where model = 'account.period' \
122 and name = 'company_id'")
123 result_query = cr.fetchone()
125 cr.execute("""select p.id from account_fiscalyear y, account_period p where y.id=p.fiscalyear_id \
126 and date(%s) between p.date_start AND p.date_stop and y.company_id = %s limit 1""", (date, company.id,))
128 cr.execute("""SELECT id
129 from account_period where date(%s)
130 between date_start AND date_stop \
131 limit 1 """, (date,))
136 date = inv.date_invoice
138 description = form.description
140 description = inv.name
143 raise osv.except_osv(_('Data Insufficient !'), \
144 _('No Period found on Invoice!'))
146 refund_id = inv_obj.refund(cr, uid, [inv.id], date, period, description, journal_id)
147 refund = inv_obj.browse(cr, uid, refund_id[0], context=context)
148 inv_obj.write(cr, uid, [refund.id], {'date_due': date,
149 'check_total': inv.check_total})
150 inv_obj.button_compute(cr, uid, refund_id)
152 created_inv.append(refund_id[0])
153 if mode in ('cancel', 'modify'):
154 movelines = inv.move_id.line_id
155 to_reconcile_ids = {}
156 for line in movelines:
157 if line.account_id.id == inv.account_id.id:
158 to_reconcile_ids[line.account_id.id] = [line.id]
159 if type(line.reconcile_id) != osv.orm.browse_null:
160 reconcile_obj.unlink(cr, uid, line.reconcile_id.id)
161 wf_service.trg_validate(uid, 'account.invoice', \
162 refund.id, 'invoice_open', cr)
163 refund = inv_obj.browse(cr, uid, refund_id[0], context=context)
164 for tmpline in refund.move_id.line_id:
165 if tmpline.account_id.id == inv.account_id.id:
166 to_reconcile_ids[tmpline.account_id.id].append(tmpline.id)
167 for account in to_reconcile_ids:
168 account_m_line_obj.reconcile(cr, uid, to_reconcile_ids[account],
169 writeoff_period_id=period,
170 writeoff_journal_id = inv.journal_id.id,
171 writeoff_acc_id=inv.account_id.id
174 invoice = inv_obj.read(cr, uid, [inv.id],
175 ['name', 'type', 'number', 'reference',
176 'comment', 'date_due', 'partner_id',
177 'address_contact_id', 'address_invoice_id',
178 'partner_insite', 'partner_contact',
179 'partner_ref', 'payment_term', 'account_id',
180 'currency_id', 'invoice_line', 'tax_line',
181 'journal_id', 'period_id'], context=context)
184 invoice_lines = inv_line_obj.read(cr, uid, invoice['invoice_line'], context=context)
185 invoice_lines = inv_obj._refund_cleanup_lines(cr, uid, invoice_lines)
186 tax_lines = inv_tax_obj.read(cr, uid, invoice['tax_line'], context=context)
187 tax_lines = inv_obj._refund_cleanup_lines(cr, uid, tax_lines)
190 'date_invoice': date,
193 'invoice_line': invoice_lines,
194 'tax_line': tax_lines,
198 for field in ('address_contact_id', 'address_invoice_id', 'partner_id',
199 'account_id', 'currency_id', 'payment_term', 'journal_id'):
200 invoice[field] = invoice[field] and invoice[field][0]
201 inv_id = inv_obj.create(cr, uid, invoice, {})
202 if inv.payment_term.id:
203 data = inv_obj.onchange_payment_term_date_invoice(cr, uid, [inv_id], inv.payment_term.id, date)
204 if 'value' in data and data['value']:
205 inv_obj.write(cr, uid, [inv_id], data['value'])
206 created_inv.append(inv_id)
207 xml_id = (inv.type == 'out_refund') and 'action_invoice_tree1' or \
208 (inv.type == 'in_refund') and 'action_invoice_tree2' or \
209 (inv.type == 'out_invoice') and 'action_invoice_tree3' or \
210 (inv.type == 'in_invoice') and 'action_invoice_tree4'
211 result = mod_obj.get_object_reference(cr, uid, 'account', xml_id)
212 id = result and result[1] or False
213 result = act_obj.read(cr, uid, id, context=context)
214 invoice_domain = eval(result['domain'])
215 invoice_domain.append(('id', 'in', created_inv))
216 result['domain'] = invoice_domain
219 def invoice_refund(self, cr, uid, ids, context=None):
220 data_refund = self.read(cr, uid, ids, ['filter_refund'],context=context)[0]['filter_refund']
221 return self.compute_refund(cr, uid, ids, data_refund, context=context)
224 account_invoice_refund()
226 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: