~unifield-team/unifield-wm/us-671-homere

« back to all changes in this revision

Viewing changes to account_override/invoice.py

[UF-43] fix added noupdate to demo data

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/env python
2
 
#-*- encoding:utf-8 -*-
3
 
##############################################################################
4
 
#
5
 
#    OpenERP, Open Source Management Solution
6
 
#    Copyright (C) 2011 TeMPO Consulting, MSF. All Rights Reserved
7
 
#    Developer: Olivier DOSSMANN
8
 
#
9
 
#    This program is free software: you can redistribute it and/or modify
10
 
#    it under the terms of the GNU Affero General Public License as
11
 
#    published by the Free Software Foundation, either version 3 of the
12
 
#    License, or (at your option) any later version.
13
 
#
14
 
#    This program is distributed in the hope that it will be useful,
15
 
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 
#    GNU Affero General Public License for more details.
18
 
#
19
 
#    You should have received a copy of the GNU Affero General Public License
20
 
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 
#
22
 
##############################################################################
23
 
 
24
 
 
25
 
from osv import osv
26
 
from osv import fields
27
 
from time import strftime
28
 
from tools.translate import _
29
 
from lxml import etree
30
 
import re
31
 
import netsvc
32
 
 
33
 
 
34
 
import decimal_precision as dp
35
 
 
36
 
class account_invoice(osv.osv):
37
 
    _name = 'account.invoice'
38
 
    _inherit = 'account.invoice'
39
 
 
40
 
    def _get_invoice_report_name(self, cr, uid, ids, context=None):
41
 
        '''
42
 
        Returns the name of the invoice according to its type
43
 
        '''
44
 
        if isinstance(ids, list):
45
 
            ids = ids[0]
46
 
 
47
 
        inv = self.browse(cr, uid, ids, context=context)
48
 
        inv_name = inv.number or inv.name or 'No_description'
49
 
        prefix = 'STV_'
50
 
 
51
 
        if inv.type == 'out_refund': # Customer refund
52
 
            prefix = 'CR_'
53
 
        elif inv.type == 'in_refund': # Supplier refund
54
 
            prefix = 'SR_'
55
 
        elif inv.type == 'out_invoice':
56
 
            # Stock transfer voucher
57
 
            prefix = 'STV_'
58
 
            # Debit note
59
 
            if inv.is_debit_note and not inv.is_inkind_donation and not inv.is_intermission:
60
 
                prefix = 'DN_'
61
 
            # Intermission voucher OUT
62
 
            elif not inv.is_debit_note and not inv.is_inkind_donation and inv.is_intermission:
63
 
                prefix = 'IMO_'
64
 
        elif inv.type == 'in_invoice':
65
 
            # Supplier invoice
66
 
            prefix = 'SI_'
67
 
            # Intermission voucher IN
68
 
            if not inv.is_debit_note and not inv.is_inkind_donation and inv.is_intermission:
69
 
                prefix = 'IMI_'
70
 
            # Direct invoice
71
 
            elif inv.is_direct_invoice:
72
 
                prefix = 'DI_'
73
 
            # In-kind donation
74
 
            elif not inv.is_debit_note and inv.is_inkind_donation:
75
 
                prefix = 'DON_'
76
 
        return '%s%s' % (prefix, inv_name)
77
 
 
78
 
    def _get_journal(self, cr, uid, context=None):
79
 
        """
80
 
        WARNING: This method has been taken from account module from OpenERP
81
 
        """
82
 
        # @@@override@account.invoice.py
83
 
        if context is None:
84
 
            context = {}
85
 
        type_inv = context.get('type', 'out_invoice')
86
 
        user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
87
 
        company_id = context.get('company_id', user.company_id.id)
88
 
        type2journal = {'out_invoice': 'sale', 'in_invoice': 'purchase', 'out_refund': 'sale_refund', 'in_refund': 'purchase_refund'}
89
 
        refund_journal = {'out_invoice': False, 'in_invoice': False, 'out_refund': True, 'in_refund': True}
90
 
        args = [('type', '=', type2journal.get(type_inv, 'sale')),
91
 
                ('company_id', '=', company_id),
92
 
                ('refund_journal', '=', refund_journal.get(type_inv, False))]
93
 
        if user.company_id.instance_id:
94
 
            args.append(('is_current_instance','=',True))
95
 
        journal_obj = self.pool.get('account.journal')
96
 
        res = journal_obj.search(cr, uid, args, limit=1)
97
 
        return res and res[0] or False
98
 
 
99
 
    def _get_fake(self, cr, uid, ids, field_name=None, arg=None, context=None):
100
 
        """
101
 
        Fake method for 'ready_for_import_in_debit_note' field
102
 
        """
103
 
        res = {}
104
 
        for i in ids:
105
 
            res[i] = False
106
 
        return res
107
 
 
108
 
    def _search_ready_for_import_in_debit_note(self, cr, uid, obj, name, args, context=None):
109
 
        if not args:
110
 
            return []
111
 
        account_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.import_invoice_default_account and \
112
 
            self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.import_invoice_default_account.id or False
113
 
        if not account_id:
114
 
            raise osv.except_osv(_('Error'), _('No default account for import invoice on Debit Note!'))
115
 
        dom1 = [
116
 
            ('account_id','=',account_id),
117
 
            ('reconciled','=',False),
118
 
            ('state', '=', 'open'),
119
 
            ('type', '=', 'out_invoice'),
120
 
            ('journal_id.type', 'not in', ['migration']),
121
 
            ('partner_id.partner_type', '=', 'section'),
122
 
        ]
123
 
        return dom1+[('is_debit_note', '=', False)]
124
 
 
125
 
    def _get_fake_m2o_id(self, cr, uid, ids, field_name=None, arg=None, context=None):
126
 
        """
127
 
        Get many2one field content
128
 
        """
129
 
        res = {}
130
 
        name = field_name.replace("fake_", '')
131
 
        for i in self.browse(cr, uid, ids):
132
 
            if context and context.get('is_intermission', False):
133
 
                res[i.id] = False
134
 
                if name == 'account_id':
135
 
                    user = self.pool.get('res.users').browse(cr, uid, [uid], context=context)
136
 
                    if user[0].company_id.intermission_default_counterpart:
137
 
                        res[i.id] = user[0].company_id.intermission_default_counterpart.id
138
 
                elif name == 'journal_id':
139
 
                    int_journal_id = self.pool.get('account.journal').search(cr, uid, [('type', '=', 'intermission')], context=context)
140
 
                    if int_journal_id:
141
 
                        if isinstance(int_journal_id, (int, long)):
142
 
                            int_journal_id = [int_journal_id]
143
 
                        res[i.id] = int_journal_id[0]
144
 
                elif name == 'currency_id':
145
 
                    user = self.pool.get('res.users').browse(cr, uid, [uid], context=context)
146
 
                    if user[0].company_id.currency_id:
147
 
                        res[i.id] = user[0].company_id.currency_id.id
148
 
            else:
149
 
                res[i.id] = getattr(i, name, False) and getattr(getattr(i, name, False), 'id', False) or False
150
 
        return res
151
 
 
152
 
    def _get_have_donation_certificate(self, cr, uid, ids, field_name=None, arg=None, context=None):
153
 
        """
154
 
        If this invoice have a stock picking in which there is a Certificate of Donation, return True. Otherwise return False.
155
 
        """
156
 
        res = {}
157
 
        for i in self.browse(cr, uid, ids):
158
 
            res[i.id] = False
159
 
            if i.picking_id:
160
 
                a_ids = self.pool.get('ir.attachment').search(cr, uid, [('res_model', '=', 'stock.picking'), ('res_id', '=', i.picking_id.id), ('description', '=', 'Certificate of Donation')])
161
 
                if a_ids:
162
 
                    res[i.id] = True
163
 
        return res
164
 
 
165
 
    def _get_virtual_fields(self, cr, uid, ids, field_name=None, arg=None, context=None):
166
 
        """
167
 
        Get fields in order to transform them into 'virtual fields" (kind of field duplicity):
168
 
         - currency_id
169
 
         - account_id
170
 
         - supplier
171
 
        """
172
 
        res = {}
173
 
        for inv in self.browse(cr, uid, ids, context=context):
174
 
            res[inv.id] = {'virtual_currency_id': inv.currency_id.id or False, 'virtual_account_id': inv.account_id.id or False,
175
 
            'virtual_partner_id': inv.partner_id.id or False}
176
 
        return res
177
 
 
178
 
    def _get_vat_ok(self, cr, uid, ids, field_name, args, context=None):
179
 
        '''
180
 
        Return True if the system configuration VAT management is set to True
181
 
        '''
182
 
        vat_ok = self.pool.get('unifield.setup.configuration').get_config(cr, uid).vat_ok
183
 
        res = {}
184
 
        for id in ids:
185
 
            res[id] = vat_ok
186
 
 
187
 
        return res
188
 
 
189
 
    def _get_can_merge_lines(self, cr, uid, ids, field_name, args,
190
 
        context=None):
191
 
        res = {}
192
 
        if not ids:
193
 
            return res
194
 
        if isinstance(ids, (int, long, )):
195
 
            ids = [ids]
196
 
 
197
 
        for inv_br in self.browse(cr, uid, ids, context=context):
198
 
            # US-357: allow merge of line only for draft SI
199
 
            res[inv_br.id] = inv_br.state and inv_br.state == 'draft' \
200
 
                and inv_br.invoice_line \
201
 
                and inv_br.type == 'in_invoice' \
202
 
                and not inv_br.is_direct_invoice \
203
 
                and not inv_br.is_inkind_donation \
204
 
                and not inv_br.is_debit_note \
205
 
                and not inv_br.is_intermission \
206
 
                or False
207
 
 
208
 
        return res
209
 
 
210
 
    _columns = {
211
 
        'from_yml_test': fields.boolean('Only used to pass addons unit test', readonly=True, help='Never set this field to true !'),
212
 
        'sequence_id': fields.many2one('ir.sequence', string='Lines Sequence', ondelete='cascade',
213
 
            help="This field contains the information related to the numbering of the lines of this order."),
214
 
        'date_invoice': fields.date('Posting Date', states={'paid':[('readonly',True)], 'open':[('readonly',True)],
215
 
            'close':[('readonly',True)]}, select=True),
216
 
        'document_date': fields.date('Document Date', states={'paid':[('readonly',True)], 'open':[('readonly',True)],
217
 
            'close':[('readonly',True)]}, select=True),
218
 
        'is_debit_note': fields.boolean(string="Is a Debit Note?"),
219
 
        'is_inkind_donation': fields.boolean(string="Is an In-kind Donation?"),
220
 
        'is_intermission': fields.boolean(string="Is an Intermission Voucher?"),
221
 
        'ready_for_import_in_debit_note': fields.function(_get_fake, fnct_search=_search_ready_for_import_in_debit_note, type="boolean",
222
 
            method=True, string="Can be imported as invoice in a debit note?",),
223
 
        'imported_invoices': fields.one2many('account.invoice.line', 'import_invoice_id', string="Imported invoices", readonly=True),
224
 
        'partner_move_line': fields.one2many('account.move.line', 'invoice_partner_link', string="Partner move line", readonly=True),
225
 
        'fake_account_id': fields.function(_get_fake_m2o_id, method=True, type='many2one', relation="account.account", string="Account", readonly="True"),
226
 
        'fake_journal_id': fields.function(_get_fake_m2o_id, method=True, type='many2one', relation="account.journal", string="Journal", readonly="True"),
227
 
        'fake_currency_id': fields.function(_get_fake_m2o_id, method=True, type='many2one', relation="res.currency", string="Currency", readonly="True"),
228
 
        'have_donation_certificate': fields.function(_get_have_donation_certificate, method=True, type='boolean', string="Have a Certificate of donation?"),
229
 
        'purchase_list': fields.boolean(string='Purchase List ?', help='Check this box if the invoice comes from a purchase list', readonly=True, states={'draft':[('readonly',False)]}),
230
 
        'virtual_currency_id': fields.function(_get_virtual_fields, method=True, store=False, multi='virtual_fields', string="Currency",
231
 
            type='many2one', relation="res.currency", readonly=True),
232
 
        'virtual_account_id': fields.function(_get_virtual_fields, method=True, store=False, multi='virtual_fields', string="Account",
233
 
            type='many2one', relation="account.account", readonly=True),
234
 
        'virtual_partner_id': fields.function(_get_virtual_fields, method=True, store=False, multi='virtual_fields', string="Supplier",
235
 
            type='many2one', relation="res.partner", readonly=True),
236
 
        'register_line_ids': fields.one2many('account.bank.statement.line', 'invoice_id', string="Register Lines"),
237
 
        'is_direct_invoice': fields.boolean("Is direct invoice?", readonly=True),
238
 
        'address_invoice_id': fields.many2one('res.partner.address', 'Invoice Address', readonly=True, required=False,
239
 
            states={'draft':[('readonly',False)]}),
240
 
        'register_posting_date': fields.date(string="Register posting date for Direct Invoice", required=False),
241
 
        'vat_ok': fields.function(_get_vat_ok, method=True, type='boolean', string='VAT OK', store=False, readonly=True),
242
 
        'st_lines': fields.one2many('account.bank.statement.line', 'invoice_id', string="Register lines", readonly=True, help="Register lines that have a link to this invoice."),
243
 
        'can_merge_lines': fields.function(_get_can_merge_lines, method=True, type='boolean', string='Can merge lines ?'),
244
 
        'is_merged_by_account': fields.boolean("Is merged by account"),
245
 
    }
246
 
 
247
 
    _defaults = {
248
 
        'journal_id': _get_journal,
249
 
        'from_yml_test': lambda *a: False,
250
 
        'date_invoice': lambda *a: strftime('%Y-%m-%d'),
251
 
        'is_debit_note': lambda obj, cr, uid, c: c.get('is_debit_note', False),
252
 
        'is_inkind_donation': lambda obj, cr, uid, c: c.get('is_inkind_donation', False),
253
 
        'is_intermission': lambda obj, cr, uid, c: c.get('is_intermission', False),
254
 
        'is_direct_invoice': lambda *a: False,
255
 
        'vat_ok': lambda obj, cr, uid, context: obj.pool.get('unifield.setup.configuration').get_config(cr, uid).vat_ok,
256
 
        'can_merge_lines': lambda *a: False,
257
 
        'is_merged_by_account': lambda *a: False,
258
 
    }
259
 
 
260
 
    def onchange_company_id(self, cr, uid, ids, company_id, part_id, ctype, invoice_line, currency_id):
261
 
        """
262
 
        This is a method to redefine the journal_id domain with the current_instance taken into account
263
 
        """
264
 
        res = super(account_invoice, self).onchange_company_id(cr, uid, ids, company_id, part_id, ctype, invoice_line, currency_id)
265
 
        if company_id and ctype:
266
 
            res.setdefault('domain', {})
267
 
            res.setdefault('value', {})
268
 
            ass = {
269
 
                'out_invoice': 'sale',
270
 
                'in_invoice': 'purchase',
271
 
                'out_refund': 'sale_refund',
272
 
                'in_refund': 'purchase_refund',
273
 
            }
274
 
            journal_ids = self.pool.get('account.journal').search(cr, uid, [
275
 
                ('company_id','=',company_id), ('type', '=', ass.get(ctype, 'purchase')), ('is_current_instance', '=', True)
276
 
            ])
277
 
            if not journal_ids:
278
 
                raise osv.except_osv(_('Configuration Error !'), _('Can\'t find any account journal of %s type for this company.\n\nYou can create one in the menu: \nConfiguration\Financial Accounting\Accounts\Journals.') % (ass.get(type, 'purchase'), ))
279
 
            res['value']['journal_id'] = journal_ids[0]
280
 
            # TODO: it's very bad to set a domain by onchange method, no time to rewrite UniField !
281
 
            res['domain']['journal_id'] = [('id', 'in', journal_ids)]
282
 
        return res
283
 
 
284
 
    def onchange_partner_id(self, cr, uid, ids, ctype, partner_id,\
285
 
        date_invoice=False, payment_term=False, partner_bank_id=False, company_id=False, is_inkind_donation=False, is_intermission=False, is_debit_note=False, is_direct_invoice=False):
286
 
        """
287
 
        Update fake_account_id field regarding account_id result.
288
 
        Get default donation account for Donation invoices.
289
 
        Get default intermission account for Intermission Voucher IN/OUT invoices.
290
 
        Get default currency from partner if this one is linked to a pricelist.
291
 
        Ticket utp917 - added code to avoid currency cd change if a direct invoice
292
 
        """
293
 
        res = super(account_invoice, self).onchange_partner_id(cr, uid, ids, ctype, partner_id, date_invoice, payment_term, partner_bank_id, company_id)
294
 
        if is_inkind_donation and partner_id:
295
 
            partner = self.pool.get('res.partner').browse(cr, uid, partner_id)
296
 
            account_id = partner and partner.donation_payable_account and partner.donation_payable_account.id or False
297
 
            res['value']['account_id'] = account_id
298
 
        if is_intermission and partner_id:
299
 
            intermission_default_account = self.pool.get('res.users').browse(cr, uid, uid).company_id.intermission_default_counterpart
300
 
            account_id = intermission_default_account and intermission_default_account.id or False
301
 
            if not account_id:
302
 
                raise osv.except_osv(_('Error'), _('Please configure a default intermission account in Company configuration.'))
303
 
            res['value']['account_id'] = account_id
304
 
        if res.get('value', False) and 'account_id' in res['value']:
305
 
            res['value'].update({'fake_account_id': res['value'].get('account_id')})
306
 
        if partner_id and ctype:
307
 
            p = self.pool.get('res.partner').browse(cr, uid, partner_id)
308
 
            ai_direct_invoice = False
309
 
            if ids: #utp917
310
 
                ai = self.browse(cr, uid, ids)[0]
311
 
                ai_direct_invoice = ai.is_direct_invoice
312
 
            if p:
313
 
                c_id = False
314
 
                if ctype in ['in_invoice', 'out_refund'] and p.property_product_pricelist_purchase:
315
 
                    c_id = p.property_product_pricelist_purchase.currency_id.id
316
 
                elif ctype in ['out_invoice', 'in_refund'] and p.property_product_pricelist:
317
 
                    c_id = p.property_product_pricelist.currency_id.id
318
 
                # UFTP-121: regarding UTP-917, we have to change currency when changing partner, but not for direct invoices
319
 
                if c_id and (not is_direct_invoice and not ai_direct_invoice):
320
 
                    if not res.get('value', False):
321
 
                        res['value'] = {'currency_id': c_id}
322
 
                    else:
323
 
                        res['value'].update({'currency_id': c_id})
324
 
        # UFTP-168: If debit note, set account to False value
325
 
        if is_debit_note:
326
 
            res['value'].update({'account_id': False, 'fake_account_id': False})
327
 
        return res
328
 
 
329
 
    def _check_document_date(self, cr, uid, ids):
330
 
        """
331
 
        Check that document's date is done BEFORE posting date
332
 
        """
333
 
        if isinstance(ids, (int, long)):
334
 
            ids = [ids]
335
 
        for i in self.browse(cr, uid, ids):
336
 
            self.pool.get('finance.tools').check_document_date(cr, uid,
337
 
                i.document_date, i.date_invoice)
338
 
        return True
339
 
 
340
 
    def _check_invoice_merged_lines(self, cr, uid, ids, context=None):
341
 
        """
342
 
        US-357:
343
 
            merge of lines by account break lines descriptions (required field)
344
 
            => before next workflow stage from draft (validate, split)
345
 
               check that user has entered description on each line
346
 
               (force user to enter a custom description)
347
 
        """
348
 
        for self_br in self.browse(cr, uid, ids, context=context):
349
 
            if self_br.is_merged_by_account:
350
 
                if not all([ l.name for l in self_br.invoice_line ]):
351
 
                    raise osv.except_osv(
352
 
                        _('Error'),
353
 
                        _('Please enter a description in each merged line' \
354
 
                            ' before invoice validation')
355
 
                    )
356
 
 
357
 
    def _refund_cleanup_lines(self, cr, uid, lines):
358
 
        """
359
 
        Remove useless fields
360
 
        """
361
 
        for line in lines:
362
 
            if line.get('move_lines',False):
363
 
                del line['move_lines']
364
 
            if line.get('import_invoice_id',False):
365
 
                del line['import_invoice_id']
366
 
        res = super(account_invoice, self)._refund_cleanup_lines(cr, uid, lines)
367
 
        return res
368
 
 
369
 
    def check_po_link(self, cr, uid, ids, context=None):
370
 
        """
371
 
        Check that invoice (only supplier invoices) has no link with a PO. This is because of commitments presence.
372
 
        """
373
 
        if not context:
374
 
            context = {}
375
 
        if isinstance(ids, (int, long)):
376
 
            ids = [ids]
377
 
        purchase_obj = self.pool.get('purchase.order')
378
 
        commitment_obj = self.pool.get('account.commitment')
379
 
        for inv in self.read(cr, uid, ids, ['purchase_ids', 'type', 'is_inkind_donation', 'is_debit_note', 'state']):
380
 
            if inv.get('type', '') == 'in_invoice' and not inv.get('is_inkind_donation', False) and not inv.get('is_debit_note', False):
381
 
                if inv.get('purchase_ids', False):
382
 
                    # UTP-808: Allow draft invoice deletion. If commitment exists, set them as done.
383
 
                    if inv.get('state', '') != 'draft':
384
 
                        raise osv.except_osv(_('Warning'), _('You cannot cancel or delete a supplier invoice linked to a PO.'))
385
 
                    else:
386
 
                        for purchase in purchase_obj.browse(cr, uid, inv.get('purchase_ids', []), context=context):
387
 
                            commitment_obj.action_commitment_done(cr, uid, [x.id for x in purchase.commitment_ids])
388
 
        return True
389
 
 
390
 
    def _hook_period_id(self, cr, uid, inv, context=None):
391
 
        """
392
 
        Give matches period that are not draft and not HQ-closed from given date.
393
 
        Do not use special periods as period 13, 14 and 15.
394
 
        """
395
 
        # Some verifications
396
 
        if not context:
397
 
            context = {}
398
 
        if not inv:
399
 
            return False
400
 
        # NB: there is some period state. So we define that we choose only open period (so not draft and not done)
401
 
        res = self.pool.get('account.period').search(cr, uid, [('date_start','<=',inv.date_invoice or strftime('%Y-%m-%d')),
402
 
            ('date_stop','>=',inv.date_invoice or strftime('%Y-%m-%d')), ('state', 'not in', ['created', 'done']),
403
 
            ('company_id', '=', inv.company_id.id), ('special', '=', False)], context=context, order="date_start ASC, name ASC")
404
 
        return res
405
 
 
406
 
    def __hook_lines_before_pay_and_reconcile(self, cr, uid, lines):
407
 
        """
408
 
        Add document date to account_move_line before pay and reconcile
409
 
        """
410
 
        for line in lines:
411
 
            if line[2] and 'date' in line[2] and not line[2].get('document_date', False):
412
 
                line[2].update({'document_date': line[2].get('date')})
413
 
        return lines
414
 
 
415
 
    def fields_view_get(self, cr, uid, view_id=None, view_type=False, context=None, toolbar=False, submenu=False):
416
 
        """
417
 
        Rename Supplier/Customer to "Donor" if view_type == tree
418
 
        """
419
 
        if not context:
420
 
            context = {}
421
 
        res = super(account_invoice, self).fields_view_get(cr, uid, view_id, view_type, context=context, toolbar=toolbar, submenu=submenu)
422
 
        if view_type == 'tree' and (context.get('journal_type', False) == 'inkind' or context.get('journal_type', False) == 'intermission'):
423
 
            doc = etree.XML(res['arch'])
424
 
            nodes = doc.xpath("//field[@name='partner_id']")
425
 
            name = _('Donor')
426
 
            if context.get('journal_type') == 'intermission':
427
 
                name = _('Partner')
428
 
            for node in nodes:
429
 
                node.set('string', name)
430
 
            res['arch'] = etree.tostring(doc)
431
 
        elif view_type in ('tree', 'search') and context.get('type') in ['out_invoice', 'out_refund']:
432
 
            doc = etree.XML(res['arch'])
433
 
            nodes = doc.xpath("//field[@name='supplier_reference']")
434
 
            for node in nodes:
435
 
                node.getparent().remove(node)
436
 
            res['arch'] = etree.tostring(doc)
437
 
        return res
438
 
 
439
 
    def default_get(self, cr, uid, fields, context=None):
440
 
        """
441
 
        Fill in fake account and fake currency for intermission invoice (in context).
442
 
        """
443
 
        defaults = super(account_invoice, self).default_get(cr, uid, fields, context=context)
444
 
        if context and context.get('is_intermission', False):
445
 
            intermission_type = context.get('intermission_type', False)
446
 
            if intermission_type in ('in', 'out'):
447
 
                # UF-2270: manual intermission (in or out)
448
 
                if defaults is None:
449
 
                    defaults = {}
450
 
                user = self.pool.get('res.users').browse(cr, uid, [uid], context=context)
451
 
                if user and user[0] and user[0].company_id:
452
 
                    # get company default currency
453
 
                    if user[0].company_id.currency_id:
454
 
                        defaults['fake_currency_id'] = user[0].company_id.currency_id.id
455
 
                        defaults['currency_id'] = defaults['fake_currency_id']
456
 
                    # get 'intermission counter part' account
457
 
                    if user[0].company_id.intermission_default_counterpart:
458
 
                        defaults['fake_account_id'] = user[0].company_id.intermission_default_counterpart.id
459
 
                        defaults['account_id'] = defaults['fake_account_id']
460
 
                    else:
461
 
                        raise osv.except_osv("Error","Company Intermission Counterpart Account must be set")
462
 
                # 'INT' intermission journal
463
 
                int_journal_id = self.pool.get('account.journal').search(cr, uid, [('type', '=', 'intermission')], context=context)
464
 
                if int_journal_id:
465
 
                    if isinstance(int_journal_id, (int, long)):
466
 
                        int_journal_id = [int_journal_id]
467
 
                    defaults['fake_journal_id'] = int_journal_id[0]
468
 
                    defaults['journal_id'] = defaults['fake_journal_id']
469
 
        return defaults
470
 
 
471
 
    def copy(self, cr, uid, inv_id, default=None, context=None):
472
 
        """
473
 
        Delete period_id from invoice.
474
 
        Check context for splitting invoice.
475
 
        Reset register_line_ids.
476
 
        """
477
 
        # Some checks
478
 
        if context is None:
479
 
            context = {}
480
 
        if default is None:
481
 
            default = {}
482
 
        default.update({
483
 
            'period_id': False,
484
 
            'purchase_ids': False,  # UFTP-24 do not copy linked POs
485
 
            'purchase_list': False,  # UFTP-24 do not copy linked: reset of potential purchase list flag (from a PO direct purchase)
486
 
            'partner_move_line': False,
487
 
            'imported_invoices': False
488
 
        })
489
 
        # Reset register_line_ids if not given in default
490
 
        if 'register_line_ids' not in default:
491
 
            default['register_line_ids'] = []
492
 
        # US-267: Reset st_lines if not given in default, otherwise a new line in Reg will be added
493
 
        if 'st_lines' not in default:
494
 
            default['st_lines'] = []
495
 
        # Default behaviour
496
 
        new_id = super(account_invoice, self).copy(cr, uid, inv_id, default, context)
497
 
        # Case where you split an invoice
498
 
        if 'split_it' in context:
499
 
            purchase_obj = self.pool.get('purchase.order')
500
 
            sale_obj = self.pool.get('sale.order')
501
 
            if purchase_obj:
502
 
                # attach new invoice to PO
503
 
                purchase_ids = purchase_obj.search(cr, uid, [('invoice_ids', 'in', [inv_id])], context=context)
504
 
                if purchase_ids:
505
 
                    purchase_obj.write(cr, uid, purchase_ids, {'invoice_ids': [(4, new_id)]}, context=context)
506
 
            if sale_obj:
507
 
                # attach new invoice to SO
508
 
                sale_ids = sale_obj.search(cr, uid, [('invoice_ids', 'in', [inv_id])], context=context)
509
 
                if sale_ids:
510
 
                    sale_obj.write(cr, uid, sale_ids, {'invoice_ids': [(4, new_id)]}, context=context)
511
 
        return new_id
512
 
 
513
 
    def create(self, cr, uid, vals, context=None):
514
 
        """
515
 
        Filled in 'from_yml_test' to True if we come from tests
516
 
        """
517
 
        if not context:
518
 
            context = {}
519
 
        if 'document_date' in vals and 'date_invoice' in vals:
520
 
            self.pool.get('finance.tools').check_document_date(cr, uid,
521
 
                vals['document_date'], vals['date_invoice'], context=context)
522
 
 
523
 
        # Create a sequence for this new invoice
524
 
        res_seq = self.create_sequence(cr, uid, vals, context)
525
 
        vals.update({'sequence_id': res_seq,})
526
 
 
527
 
        # UTP-317 # Check that no inactive partner have been used to create this invoice
528
 
        if 'partner_id' in vals:
529
 
            partner_id = vals.get('partner_id')
530
 
            if isinstance(partner_id, (str)):
531
 
                partner_id = int(partner_id)
532
 
            partner = self.pool.get('res.partner').browse(cr, uid, [partner_id])
533
 
            if partner and partner[0] and not partner[0].active:
534
 
                raise osv.except_osv(_('Warning'), _("Partner '%s' is not active.") % (partner[0] and partner[0].name or '',))
535
 
 
536
 
        return super(account_invoice, self).create(cr, uid, vals, context)
537
 
 
538
 
    def write(self, cr, uid, ids, vals, context=None):
539
 
        """
540
 
        Check document_date
541
 
        """
542
 
        if context is None:
543
 
            context = {}
544
 
        if isinstance(ids, (int, long)):
545
 
            ids = [ids]
546
 
 
547
 
        # US_286: Forbit possibility to add include price tax
548
 
        # in bottom left corner
549
 
        if 'tax_line' in vals:
550
 
            tax_obj = self.pool.get('account.tax')
551
 
            for tax_line in vals['tax_line']:
552
 
                if tax_line[2]:
553
 
                    if 'account_tax_id' in tax_line[2]:
554
 
                        args = [('price_include', '=', '1'),
555
 
                                ('id', '=', tax_line[2]['account_tax_id'])]
556
 
                        tax_ids = tax_obj.search(cr, uid, args, limit=1,
557
 
                                order='NO_ORDER', context=context)
558
 
                        if tax_ids:
559
 
                            raise osv.except_osv(_('Error'),
560
 
                                                 _('Tax included in price can not be tied to the whole invoice.'))
561
 
 
562
 
        res = super(account_invoice, self).write(cr, uid, ids, vals, context=context)
563
 
        self._check_document_date(cr, uid, ids)
564
 
        return res
565
 
 
566
 
    def unlink(self, cr, uid, ids, context=None):
567
 
        """
568
 
        Delete register line if this invoice is a Direct Invoice.
569
 
        Don't delete an invoice that is linked to a PO. This is only for supplier invoices.
570
 
        """
571
 
        if not context:
572
 
            context = {}
573
 
        if isinstance(ids, (int, long)):
574
 
            ids = [ids]
575
 
        # Check register lines
576
 
        for inv in self.browse(cr, uid, ids):
577
 
            if inv.is_direct_invoice and inv.register_line_ids:
578
 
                if not context.get('from_register', False):
579
 
                    self.pool.get('account.bank.statement.line').unlink(cr, uid, [x.id for x in inv.register_line_ids], {'from_direct_invoice': True})
580
 
        # Check PO
581
 
        self.check_po_link(cr, uid, ids)
582
 
        return super(account_invoice, self).unlink(cr, uid, ids, context)
583
 
 
584
 
    def create_sequence(self, cr, uid, vals, context=None):
585
 
        """
586
 
        Create new entry sequence for every new invoice
587
 
        """
588
 
        seq_pool = self.pool.get('ir.sequence')
589
 
        seq_typ_pool = self.pool.get('ir.sequence.type')
590
 
 
591
 
        name = 'Invoice L' # For Invoice Lines
592
 
        code = 'account.invoice'
593
 
 
594
 
        types = {
595
 
            'name': name,
596
 
            'code': code
597
 
        }
598
 
        seq_typ_pool.create(cr, uid, types)
599
 
 
600
 
        seq = {
601
 
            'name': name,
602
 
            'code': code,
603
 
            'prefix': '',
604
 
            'padding': 0,
605
 
        }
606
 
        return seq_pool.create(cr, uid, seq)
607
 
 
608
 
    def log(self, cr, uid, inv_id, message, secondary=False, context=None):
609
 
        """
610
 
        Change first "Invoice" word from message into "Debit Note" if this invoice is a debit note.
611
 
        Change it to "In-kind donation" if this invoice is an In-kind donation.
612
 
        """
613
 
        if not context:
614
 
            context = {}
615
 
        local_ctx = context.copy()
616
 
        # Prepare some values
617
 
        # Search donation view and return it
618
 
        try:
619
 
            # try / except for runbot
620
 
            debit_res = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account_override', 'view_debit_note_form')
621
 
            inkind_res = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account_override', 'view_inkind_donation_form')
622
 
            intermission_res = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account_override', 'view_intermission_form')
623
 
            supplier_invoice_res = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account', 'invoice_supplier_form')
624
 
            customer_invoice_res = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account', 'invoice_form')
625
 
            supplier_direct_invoice_res = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'register_accounting', 'direct_supplier_invoice_form')
626
 
        except ValueError, e:
627
 
            return super(account_invoice, self).log(cr, uid, inv_id, message, secondary, context)
628
 
        debit_view_id = debit_res and debit_res[1] or False
629
 
        debit_note_ctx = {'view_id': debit_view_id, 'type':'out_invoice', 'journal_type': 'sale', 'is_debit_note': True}
630
 
        # Search donation view and return it
631
 
        inkind_view_id = inkind_res and inkind_res[1] or False
632
 
        inkind_ctx = {'view_id': inkind_view_id, 'type':'in_invoice', 'journal_type': 'inkind', 'is_inkind_donation': True}
633
 
        # Search intermission view
634
 
        intermission_view_id = intermission_res and intermission_res[1] or False
635
 
        intermission_ctx = {'view_id': intermission_view_id, 'journal_type': 'intermission', 'is_intermission': True}
636
 
        customer_view_id = customer_invoice_res[1] or False
637
 
        customer_ctx = {'view_id': customer_view_id, 'type': 'out_invoice', 'journal_type': 'sale'}
638
 
        message_changed = False
639
 
        pattern = re.compile('^(Invoice)')
640
 
        for el in [('is_debit_note', 'Debit Note', debit_note_ctx), ('is_inkind_donation', 'In-kind Donation', inkind_ctx), ('is_intermission', 'Intermission Voucher', intermission_ctx)]:
641
 
            if self.read(cr, uid, inv_id, [el[0]]).get(el[0], False) is True:
642
 
                m = re.match(pattern, message)
643
 
                if m and m.groups():
644
 
                    message = re.sub(pattern, el[1], message, 1)
645
 
                    message_changed = True
646
 
                local_ctx.update(el[2])
647
 
        # UF-1112: Give all customer invoices a name as "Stock Transfer Voucher".
648
 
        if not message_changed and self.read(cr, uid, inv_id, ['type']).get('type', False) == 'out_invoice':
649
 
            message = re.sub(pattern, 'Stock Transfer Voucher', message, 1)
650
 
 
651
 
            local_ctx.update(customer_ctx)
652
 
        # UF-1307: for supplier invoice log (from the incoming shipment), the context was not
653
 
        # filled with all the information; this leaded to having a "Sale" journal in the supplier
654
 
        # invoice if it was saved after coming from this link. Here's the fix.
655
 
        if local_ctx.get('type', False) == 'in_invoice':
656
 
            if not local_ctx.get('journal_type', False):
657
 
                supplier_view_id = supplier_invoice_res and supplier_invoice_res[1] or False
658
 
                local_ctx.update({'journal_type': 'purchase',
659
 
                                'view_id': supplier_view_id})
660
 
            elif local_ctx.get('direct_invoice_view', False): # UFTP-166: The wrong context saved in log
661
 
                supplier_view_id = supplier_direct_invoice_res and supplier_direct_invoice_res[1] or False
662
 
                local_ctx = {'journal_type': 'purchase',
663
 
                             'view_id': supplier_view_id}
664
 
        return super(account_invoice, self).log(cr, uid, inv_id, message, secondary, local_ctx)
665
 
 
666
 
    def invoice_open(self, cr, uid, ids, context=None):
667
 
        """
668
 
        No longer fills the date automatically, but requires it to be set
669
 
        """
670
 
        # Some verifications
671
 
        if not context:
672
 
            context = {}
673
 
        self._check_invoice_merged_lines(cr, uid, ids, context=context)
674
 
 
675
 
        # Prepare workflow object
676
 
        wf_service = netsvc.LocalService("workflow")
677
 
        for inv in self.browse(cr, uid, ids):
678
 
            values = {}
679
 
            curr_date = strftime('%Y-%m-%d')
680
 
            if not inv.date_invoice and not inv.document_date:
681
 
                values.update({'date': curr_date, 'document_date': curr_date, 'state': 'date'})
682
 
            elif not inv.date_invoice:
683
 
                values.update({'date': curr_date, 'document_date': inv.document_date, 'state': 'date'})
684
 
            elif not inv.document_date:
685
 
                values.update({'date': inv.date_invoice, 'document_date': curr_date, 'state': 'date'})
686
 
            if inv.type in ('in_invoice', 'in_refund') and abs(inv.check_total - inv.amount_total) >= (inv.currency_id.rounding/2.0):
687
 
                state = values and 'both' or 'amount'
688
 
                values.update({'check_total': inv.check_total , 'amount_total': inv.amount_total, 'state': state})
689
 
            if values:
690
 
                values['invoice_id'] = inv.id
691
 
                wiz_id = self.pool.get('wizard.invoice.date').create(cr, uid, values, context)
692
 
                return {
693
 
                    'name': "Missing Information",
694
 
                    'type': 'ir.actions.act_window',
695
 
                    'res_model': 'wizard.invoice.date',
696
 
                    'target': 'new',
697
 
                    'view_mode': 'form',
698
 
                    'view_type': 'form',
699
 
                    'res_id': wiz_id,
700
 
                    }
701
 
 
702
 
            wf_service.trg_validate(uid, 'account.invoice', inv.id, 'invoice_open', cr)
703
 
        return True
704
 
 
705
 
    def action_reconcile_imported_invoice(self, cr, uid, ids, context=None):
706
 
        """
707
 
        Reconcile each imported invoice with its attached invoice line
708
 
        """
709
 
        # some verifications
710
 
        if isinstance(ids, (int, long)):
711
 
            ids = [ids]
712
 
        # browse all given invoices
713
 
        for inv in self.browse(cr, uid, ids):
714
 
            for invl in inv.invoice_line:
715
 
                if not invl.import_invoice_id:
716
 
                    continue
717
 
                imported_invoice = invl.import_invoice_id
718
 
                # reconcile partner line from import invoice with this invoice line attached move line
719
 
                import_invoice_partner_move_lines = self.pool.get('account.move.line').search(cr, uid, [('invoice_partner_link', '=', imported_invoice.id)])
720
 
                invl_move_lines = [x.id or None for x in invl.move_lines]
721
 
                rec = self.pool.get('account.move.line').reconcile_partial(cr, uid, [import_invoice_partner_move_lines[0], invl_move_lines[0]], 'auto', context=context)
722
 
                if not rec:
723
 
                    return False
724
 
        return True
725
 
 
726
 
    def action_reconcile_direct_invoice(self, cr, uid, inv, context=None):
727
 
        """
728
 
        Reconcile move line if invoice is a Direct Invoice
729
 
        NB: In order to define that an invoice is a Direct Invoice, we need to have register_line_ids not null
730
 
        """
731
 
        # Verify that this invoice is linked to a register line and have a move
732
 
        if not inv:
733
 
            return False
734
 
        if inv.move_id and inv.register_line_ids:
735
 
            ml_obj = self.pool.get('account.move.line')
736
 
            # First search move line that becomes from invoice
737
 
            res_ml_ids = ml_obj.search(cr, uid, [
738
 
                ('move_id', '=', inv.move_id.id),
739
 
                ('account_id', '=', inv.account_id.id),
740
 
                ('invoice_line_id', '=', False),  # US-254: do not seek invoice line's JIs (if same account as header)
741
 
            ])
742
 
            if len(res_ml_ids) > 1:
743
 
                raise osv.except_osv(_('Error'), _('More than one journal items found for this invoice.'))
744
 
            invoice_move_line_id = res_ml_ids[0]
745
 
            # Then search move line that corresponds to the register line
746
 
            reg_line = inv.register_line_ids[0]
747
 
            reg_ml_ids = ml_obj.search(cr, uid, [('move_id', '=', reg_line.move_ids[0].id), ('account_id', '=', reg_line.account_id.id)])
748
 
            if len(reg_ml_ids) > 1:
749
 
                raise osv.except_osv(_('Error'), _('More than one journal items found for this register line.'))
750
 
            register_move_line_id = reg_ml_ids[0]
751
 
            # Finally do reconciliation
752
 
            ml_obj.reconcile_partial(cr, uid, [invoice_move_line_id, register_move_line_id])
753
 
        return True
754
 
 
755
 
    def action_cancel(self, cr, uid, ids, *args):
756
 
        """
757
 
        Reverse move if this object is a In-kind Donation. Otherwise do normal job: cancellation.
758
 
        Don't delete an invoice that is linked to a PO. This is only for supplier invoices.
759
 
        """
760
 
        to_cancel = []
761
 
        for i in self.browse(cr, uid, ids):
762
 
            if i.is_inkind_donation:
763
 
                move_id = i.move_id.id
764
 
                tmp_res = self.pool.get('account.move').reverse(cr, uid, [move_id], strftime('%Y-%m-%d'))
765
 
                # If success change invoice to cancel and detach move_id
766
 
                if tmp_res:
767
 
                    # Change invoice state
768
 
                    self.write(cr, uid, [i.id], {'state': 'cancel', 'move_id':False})
769
 
                continue
770
 
            to_cancel.append(i.id)
771
 
        # Check PO link
772
 
        self.check_po_link(cr, uid, ids)
773
 
        return super(account_invoice, self).action_cancel(cr, uid, to_cancel, args)
774
 
 
775
 
    def action_date_assign(self, cr, uid, ids, *args):
776
 
        """
777
 
        Check Document date.
778
 
        """
779
 
        # Prepare some values
780
 
        period_obj = self.pool.get('account.period')
781
 
        # Default behaviour to add date
782
 
        res = super(account_invoice, self).action_date_assign(cr, uid, ids, args)
783
 
        # Process invoices
784
 
        for i in self.browse(cr, uid, ids):
785
 
            if not i.date_invoice:
786
 
                self.write(cr, uid, i.id, {'date_invoice': strftime('%Y-%m-%d')})
787
 
                i = self.browse(cr, uid, i.id) # This permit to refresh the browse of this element
788
 
            if not i.document_date:
789
 
                raise osv.except_osv(_('Warning'), _('Document Date is a mandatory field for validation!'))
790
 
            # UFTP-105: Search period and raise an exeception if this one is not open
791
 
            period_ids = period_obj.get_period_from_date(cr, uid, i.date_invoice)
792
 
            if not period_ids:
793
 
                raise osv.except_osv(_('Error'), _('No period found for this posting date: %s') % (i.date_invoice))
794
 
            for period in period_obj.browse(cr, uid, period_ids):
795
 
                if period.state != 'draft':
796
 
                    raise osv.except_osv(_('Warning'), _('You cannot validate this document in the given period: %s because it\'s not open. Change the date of the document or open the period.') % (period.name))
797
 
        # Posting date should not be done BEFORE document date
798
 
        self._check_document_date(cr, uid, ids)
799
 
        return res
800
 
 
801
 
    def action_open_invoice(self, cr, uid, ids, context=None, *args):
802
 
        """
803
 
        Give function to use when changing invoice to open state
804
 
        """
805
 
        if not context:
806
 
            context = {}
807
 
        if isinstance(ids, (int, long)):
808
 
            ids = [ids]
809
 
        if not self.action_date_assign(cr, uid, ids, context, args):
810
 
            return False
811
 
        if not self.action_move_create(cr, uid, ids, context, args):
812
 
            return False
813
 
        if not self.action_number(cr, uid, ids, context):
814
 
            return False
815
 
        if not self.action_reconcile_imported_invoice(cr, uid, ids, context):
816
 
            return False
817
 
        return True
818
 
 
819
 
    def line_get_convert(self, cr, uid, x, part, date, context=None):
820
 
        """
821
 
        Add these field into invoice line:
822
 
        - invoice_line_id
823
 
        """
824
 
        if not context:
825
 
            context = {}
826
 
        res = super(account_invoice, self).line_get_convert(cr, uid, x, part, date, context)
827
 
        res.update({'invoice_line_id': x.get('invoice_line_id', False)})
828
 
        return res
829
 
 
830
 
    def finalize_invoice_move_lines(self, cr, uid, inv, line):
831
 
        """
832
 
        Hook that changes move line data before write them.
833
 
        Add a link between partner move line and invoice.
834
 
        Add invoice document date to data.
835
 
        """
836
 
        def is_partner_line(dico):
837
 
            if isinstance(dico, dict):
838
 
                if dico:
839
 
                    # In case where no amount_currency filled in, then take debit - credit for amount comparison
840
 
                    amount = dico.get('amount_currency', False) or (dico.get('debit', 0.0) - dico.get('credit', 0.0))
841
 
                    if amount == inv.amount_total and dico.get('partner_id', False) == inv.partner_id.id:
842
 
                        return True
843
 
            return False
844
 
        new_line = []
845
 
        for el in line:
846
 
            if el[2]:
847
 
                el[2].update({'document_date': inv.document_date})
848
 
            if el[2] and is_partner_line(el[2]):
849
 
                el[2].update({'invoice_partner_link': inv.id})
850
 
                new_line.append((el[0], el[1], el[2]))
851
 
            else:
852
 
                new_line.append(el)
853
 
        return super(account_invoice, self).finalize_invoice_move_lines(cr, uid, inv, new_line)
854
 
 
855
 
    def button_debit_note_import_invoice(self, cr, uid, ids, context=None):
856
 
        """
857
 
        Launch wizard that permits to import invoice on a debit note
858
 
        """
859
 
        # Some verifications
860
 
        if not context:
861
 
            context = {}
862
 
        if isinstance(ids, (int, long)):
863
 
            ids = [ids]
864
 
        # Browse all given invoices
865
 
        for inv in self.browse(cr, uid, ids):
866
 
            if inv.type != 'out_invoice' or inv.is_debit_note == False:
867
 
                raise osv.except_osv(_('Error'), _('You can only do import invoice on a Debit Note!'))
868
 
            w_id = self.pool.get('debit.note.import.invoice').create(cr, uid, {'invoice_id': inv.id, 'currency_id': inv.currency_id.id,
869
 
                'partner_id': inv.partner_id.id}, context=context)
870
 
            context.update({
871
 
                'active_id': inv.id,
872
 
                'active_ids': ids,
873
 
            })
874
 
            return {
875
 
                'type': 'ir.actions.act_window',
876
 
                'res_model': 'debit.note.import.invoice',
877
 
                'name': 'Import invoice',
878
 
                'view_type': 'form',
879
 
                'view_mode': 'form',
880
 
                'res_id': w_id,
881
 
                'context': context,
882
 
                'target': 'new',
883
 
            }
884
 
 
885
 
    def button_split_invoice(self, cr, uid, ids, context=None):
886
 
        """
887
 
        Launch the split invoice wizard to split an invoice in two elements.
888
 
        """
889
 
        # Some verifications
890
 
        if not context:
891
 
            context={}
892
 
        if isinstance(ids, (int, long)):
893
 
            ids = [ids]
894
 
        self._check_invoice_merged_lines(cr, uid, ids, context=context)
895
 
 
896
 
        # Prepare some value
897
 
        wiz_lines_obj = self.pool.get('wizard.split.invoice.lines')
898
 
        inv_lines_obj = self.pool.get('account.invoice.line')
899
 
        # Creating wizard
900
 
        wizard_id = self.pool.get('wizard.split.invoice').create(cr, uid, {'invoice_id': ids[0]}, context=context)
901
 
        # Add invoices_lines into the wizard
902
 
        invoice_line_ids = self.pool.get('account.invoice.line').search(cr, uid, [('invoice_id', '=', ids[0])], context=context)
903
 
        # Some other verifications
904
 
        if not len(invoice_line_ids):
905
 
            raise osv.except_osv(_('Error'), _('No invoice line in this invoice or not enough elements'))
906
 
        for invl in inv_lines_obj.browse(cr, uid, invoice_line_ids, context=context):
907
 
            wiz_lines_obj.create(cr, uid, {'invoice_line_id': invl.id, 'product_id': invl.product_id.id, 'quantity': invl.quantity,
908
 
                'price_unit': invl.price_unit, 'description': invl.name, 'wizard_id': wizard_id}, context=context)
909
 
        # Return wizard
910
 
        if wizard_id:
911
 
            return {
912
 
                'name': "Split Invoice",
913
 
                'type': 'ir.actions.act_window',
914
 
                'res_model': 'wizard.split.invoice',
915
 
                'target': 'new',
916
 
                'view_mode': 'form,tree',
917
 
                'view_type': 'form',
918
 
                'res_id': [wizard_id],
919
 
                'context':
920
 
                {
921
 
                    'active_id': ids[0],
922
 
                    'active_ids': ids,
923
 
                    'wizard_id': wizard_id,
924
 
                }
925
 
            }
926
 
        return False
927
 
 
928
 
    def button_donation_certificate(self, cr, uid, ids, context=None):
929
 
        """
930
 
        Open a view containing a list of all donation certificates linked to the given invoice.
931
 
        """
932
 
        for inv in self.browse(cr, uid, ids):
933
 
            pick_id = inv.picking_id and inv.picking_id.id or ''
934
 
            domain = "[('res_model', '=', 'stock.picking'), ('res_id', '=', " + str(pick_id) + "), ('description', '=', 'Certificate of Donation')]"
935
 
            view_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account_override', 'view_attachment_tree_2')
936
 
            view_id = view_id and view_id[1] or False
937
 
            search_view_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account_override', 'view_attachment_search_2')
938
 
            search_view_id = search_view_id and search_view_id[1] or False
939
 
            return {
940
 
                'name': "Certificate of Donation",
941
 
                'type': 'ir.actions.act_window',
942
 
                'res_model': 'ir.attachment',
943
 
                'view_type': 'form',
944
 
                'view_mode': 'tree,form',
945
 
                'view_id': [view_id],
946
 
                'search_view_id': search_view_id,
947
 
                'domain': domain,
948
 
                'context': context,
949
 
                'target': 'current',
950
 
            }
951
 
        return False
952
 
 
953
 
    def button_dummy_compute_total(self, cr, uid, ids, context=None):
954
 
        return True
955
 
 
956
 
    def button_merge_lines(self, cr, uid, ids, context=None):
957
 
        # US-357 merge lines (by account) button for draft SIs
958
 
        def check(inv_br):
959
 
            if not inv_br.can_merge_lines:
960
 
                raise osv.except_osv(_('Error'),
961
 
                    _("Invoice not eligible for lines merging"))
962
 
 
963
 
            account_iterations = {}
964
 
            for l in inv_br.invoice_line:
965
 
                account_iterations[l.account_id.id] = \
966
 
                    account_iterations.setdefault(l.account_id.id, 0) + 1
967
 
 
968
 
            any_to_merge = False
969
 
            if account_iterations:
970
 
                for a in account_iterations:
971
 
                    if account_iterations[a] > 1:
972
 
                        any_to_merge = True
973
 
                        break
974
 
 
975
 
            if not any_to_merge:
976
 
                raise osv.except_osv(_('Error'),
977
 
                    _("Invoice has no line to merge by account"))
978
 
 
979
 
        def compute_merge(inv_br):
980
 
            """
981
 
            :result:
982
 
                - A: lines vals by line number
983
 
                - B: and list of inv id to keep (1 line by account (not merged))
984
 
            :rtype : [dict, list]
985
 
 
986
 
            NOTES:
987
 
            - no impact on 'import_invoice_id', 'is_corrected' as the 
988
 
              invoice is draft so not imported, and no accounting entries
989
 
            - for order_line_id and sale_order_line_id these m2o are used
990
 
              for AD at line level but when merging we keep only AD from header
991
 
            """
992
 
            index = 1
993
 
            vals_template = {
994
 
                '_index_': index,  # internal merged line index
995
 
 
996
 
                'account_id': False,
997
 
                'company_id': inv_br.company_id.id,
998
 
                'discount': 0.,
999
 
                'invoice_id': inv_br.id,
1000
 
                'invoice_line_tax_id': None,  # m2m (None to distinguished False)
1001
 
                'name': '',
1002
 
                'partner_id': inv_br.partner_id.id,
1003
 
                'price_unit': 0.,
1004
 
                'quantity': 1.,
1005
 
            }
1006
 
 
1007
 
            by_account_vals = {}  # key: account_id
1008
 
            for l in inv_br.invoice_line:
1009
 
                # get current merge vals for account or create new
1010
 
                if l.account_id.id in by_account_vals:
1011
 
                    vals = by_account_vals[l.account_id.id]
1012
 
                else:
1013
 
                    # new account to merge
1014
 
                    vals = vals_template.copy()
1015
 
                    vals.update({
1016
 
                        '_index_': index,
1017
 
                        'account_id': l.account_id.id,
1018
 
                    })
1019
 
                    index += 1
1020
 
 
1021
 
                # merge line
1022
 
                vals['price_unit'] += l.price_subtotal  # qty 1 and price
1023
 
                if vals['invoice_line_tax_id'] is None:
1024
 
                    vals['invoice_line_tax_id'] = l.invoice_line_tax_id \
1025
 
                        and [ t.id for t in l.invoice_line_tax_id ] or False
1026
 
                else:
1027
 
                    # get rid of the product tax line if <> between merged lines
1028
 
                    if vals['invoice_line_tax_id'] is None:
1029
 
                        # first tax line browsed for the account
1030
 
                        if l.invoice_line_tax_id:
1031
 
                            vals['invoice_line_tax_id'] = [ 
1032
 
                                t.id for t in l.invoice_line_tax_id ]
1033
 
                        else:
1034
 
                            vals['invoice_line_tax_id'] = False
1035
 
                    elif vals['invoice_line_tax_id'] and l.invoice_line_tax_id:
1036
 
                        # track <> tax lines, if the case abort tax(es) in merge
1037
 
                        tax_ids = [ t.id for t in l.invoice_line_tax_id ]
1038
 
                        if cmp(vals['invoice_line_tax_id'], tax_ids) != 0:
1039
 
                            vals['invoice_line_tax_id'] = False
1040
 
                    else:
1041
 
                        # no tax(es) for this line,  abort tax(es) in merge
1042
 
                        vals['invoice_line_tax_id'] = False
1043
 
 
1044
 
                # update merge line
1045
 
                by_account_vals[l.account_id.id] = vals
1046
 
 
1047
 
                # internal merged lines ids
1048
 
                if not '_ids_' in by_account_vals[l.account_id.id]:
1049
 
                    by_account_vals[l.account_id.id]['_ids_'] = []
1050
 
                by_account_vals[l.account_id.id]['_ids_'].append(l.id)
1051
 
 
1052
 
            # result by index
1053
 
            res = [{}, []]
1054
 
            for a in by_account_vals:
1055
 
                if len(by_account_vals[a]['_ids_']) > 1:
1056
 
                    # more than 1 inv line by account
1057
 
                    index = by_account_vals[a]['_index_']
1058
 
                    del by_account_vals[a]['_index_']
1059
 
                    del by_account_vals[a]['_ids_']
1060
 
                    res[0][index] = by_account_vals[a]
1061
 
                else:
1062
 
                    res[1].append(by_account_vals[a]['_ids_'][0])
1063
 
            return res
1064
 
 
1065
 
        def delete_lines(inv_br, skip_ids):
1066
 
            # get ids to delete
1067
 
            ad_to_del_ids = []
1068
 
            line_to_del_ids = []
1069
 
 
1070
 
            for l in inv_br.invoice_line:
1071
 
                if l.id in skip_ids:
1072
 
                    continue  # line not to del (1 by account)
1073
 
                # delete AD
1074
 
                if l.analytic_distribution_id \
1075
 
                    and not l.analytic_distribution_id.id in ad_to_del_ids:
1076
 
                    ad_to_del_ids.append(l.analytic_distribution_id.id)
1077
 
                line_to_del_ids.append(l.id)
1078
 
 
1079
 
            # delete ADs
1080
 
            if ad_to_del_ids:
1081
 
                ad_obj.unlink(cr, uid, ad_to_del_ids, context=context)
1082
 
 
1083
 
            # delete lines
1084
 
            if line_to_del_ids:
1085
 
                ail_obj.unlink(cr, uid, line_to_del_ids, context=context)
1086
 
 
1087
 
        def do_merge(inv_br, lines_vals, not_merged_ids):
1088
 
            """
1089
 
            :param lines_vals: lines vals in order
1090
 
            :type lines_vals: dict
1091
 
            """
1092
 
            # the invoice is reviewed with merge lines
1093
 
            # => reset the line number sequence from 1
1094
 
            if inv_br.sequence_id:
1095
 
                inv_br.sequence_id.write({'number_next': 1}, context=context)
1096
 
 
1097
 
            # create merge lines
1098
 
            for ln in sorted(lines_vals.keys()):
1099
 
                vals = lines_vals[ln]
1100
 
 
1101
 
                # post encode tax m2m
1102
 
                vals['invoice_line_tax_id'] = vals['invoice_line_tax_id'] \
1103
 
                    and [(6, 0, vals['invoice_line_tax_id'])] or False
1104
 
 
1105
 
                # create merge line
1106
 
                if not self.pool.get('account.invoice.line').create(cr, uid,
1107
 
                    vals, context=context):
1108
 
                    break
1109
 
 
1110
 
            # recompute seq number for not merged lines
1111
 
            ail_obj = self.pool.get('account.invoice.line')
1112
 
            if not_merged_ids:
1113
 
                for lid in not_merged_ids:
1114
 
                    ln = inv_br.sequence_id.get_id(code_or_id='id')
1115
 
                    ail_obj.write(cr, uid, [lid], {
1116
 
                        'line_number': ln,
1117
 
                    })
1118
 
 
1119
 
        def merge_invoice(inv_br):
1120
 
            check(inv_br)
1121
 
            merge_res = compute_merge(inv_br)
1122
 
            delete_lines(inv_br, merge_res[1])
1123
 
            do_merge(inv_br, merge_res[0], merge_res[1])
1124
 
 
1125
 
            # set merged flag
1126
 
            inv_br.write({'is_merged_by_account': True}, context=context)
1127
 
 
1128
 
            # recompute taxes (reset not manual ones)
1129
 
            self.button_reset_taxes(cr, uid, [inv_br.id], context=context)
1130
 
 
1131
 
        def post_merge(inv_br):
1132
 
            inv_br.write({
1133
 
                # update check total for accurate check amount at validation
1134
 
                'check_total':
1135
 
                    inv_br.amount_total or inv_br.check_amount or 0.,
1136
 
            }, context=context)
1137
 
 
1138
 
        res = {}
1139
 
        if not ids:
1140
 
            return False
1141
 
        if isinstance(ids, (int, long, )):
1142
 
            ids = [ids]
1143
 
 
1144
 
        ail_obj = self.pool.get('account.invoice.line')
1145
 
        ad_obj = self.pool.get('analytic.distribution')
1146
 
 
1147
 
        # merging
1148
 
        for inv_br in self.browse(cr, uid, ids, context=context):
1149
 
            merge_invoice(inv_br)
1150
 
 
1151
 
        # post processing (reload invoices)
1152
 
        for inv_br in self.browse(cr, uid, ids, context=context):
1153
 
            post_merge(inv_br)
1154
 
 
1155
 
        return res
1156
 
 
1157
 
account_invoice()
1158
 
 
1159
 
class account_invoice_line(osv.osv):
1160
 
    _name = 'account.invoice.line'
1161
 
    _inherit = 'account.invoice.line'
1162
 
 
1163
 
    def _uom_constraint(self, cr, uid, ids, context=None):
1164
 
        for obj in self.browse(cr, uid, ids, context=context):
1165
 
            if not self.pool.get('uom.tools').check_uom(cr, uid, obj.product_id.id, obj.uos_id.id, context):
1166
 
                raise osv.except_osv(_('Error'), _('You have to select a product UOM in the same category than the purchase UOM of the product !'))
1167
 
        return True
1168
 
 
1169
 
    _constraints = [(_uom_constraint, 'Constraint error on Uom', [])]
1170
 
 
1171
 
    def _have_been_corrected(self, cr, uid, ids, name, args, context=None):
1172
 
        """
1173
 
        Return True if ALL elements are OK:
1174
 
         - a journal items is linked to this invoice line
1175
 
         - the journal items is linked to an analytic line that have been reallocated
1176
 
        """
1177
 
        if context is None:
1178
 
            context = {}
1179
 
        res = {}
1180
 
 
1181
 
        def has_ana_reallocated(move):
1182
 
            for ml in move.move_lines or []:
1183
 
                for al in ml.analytic_lines or []:
1184
 
                    if al.is_reallocated:
1185
 
                        return True
1186
 
            return False
1187
 
 
1188
 
        for il in self.browse(cr, uid, ids, context=context):
1189
 
            res[il.id] = has_ana_reallocated(il)
1190
 
        return res
1191
 
 
1192
 
    def _get_product_code(self, cr, uid, ids, field_name=None, arg=None, context=None):
1193
 
        """
1194
 
        Give product code for each invoice line
1195
 
        """
1196
 
        res = {}
1197
 
        for inv_line in self.browse(cr, uid, ids, context=context):
1198
 
            res[inv_line.id] = ''
1199
 
            if inv_line.product_id:
1200
 
                res[inv_line.id] = inv_line.product_id.default_code
1201
 
 
1202
 
        return res
1203
 
    def _get_vat_ok(self, cr, uid, ids, field_name, args, context=None):
1204
 
        '''
1205
 
        Return True if the system configuration VAT management is set to True
1206
 
        '''
1207
 
        vat_ok = self.pool.get('unifield.setup.configuration').get_config(cr, uid).vat_ok
1208
 
        res = {}
1209
 
        for id in ids:
1210
 
            res[id] = vat_ok
1211
 
 
1212
 
        return res
1213
 
 
1214
 
    _columns = {
1215
 
        'from_yml_test': fields.boolean('Only used to pass addons unit test', readonly=True, help='Never set this field to true !'),
1216
 
        'line_number': fields.integer(string='Line Number'),
1217
 
        'price_unit': fields.float('Unit Price', required=True, digits_compute= dp.get_precision('Account Computation')),
1218
 
        'import_invoice_id': fields.many2one('account.invoice', string="From an import invoice", readonly=True),
1219
 
        'move_lines':fields.one2many('account.move.line', 'invoice_line_id', string="Journal Item", readonly=True),
1220
 
        'is_corrected': fields.function(_have_been_corrected, method=True, string="Have been corrected?", type='boolean',
1221
 
            readonly=True, help="This informs system if this item have been corrected in analytic lines. Criteria: the invoice line is linked to a journal items that have analytic item which is reallocated.",
1222
 
            store=False),
1223
 
        'product_code': fields.function(_get_product_code, method=True, store=False, string="Product Code", type='char'),
1224
 
        'reference': fields.char(string="Reference", size=64),
1225
 
        'vat_ok': fields.function(_get_vat_ok, method=True, type='boolean', string='VAT OK', store=False, readonly=True),
1226
 
    }
1227
 
 
1228
 
    _defaults = {
1229
 
        'price_unit': lambda *a: 0.00,
1230
 
        'from_yml_test': lambda *a: False,
1231
 
        'is_corrected': lambda *a: False,
1232
 
        'vat_ok': lambda obj, cr, uid, context: obj.pool.get('unifield.setup.configuration').get_config(cr, uid).vat_ok,
1233
 
    }
1234
 
 
1235
 
    _order = 'line_number'
1236
 
 
1237
 
    def create(self, cr, uid, vals, context=None):
1238
 
        """
1239
 
        Give a line_number to invoice line.
1240
 
        NB: This appends only for account invoice line and not other object (for an example direct invoice line)
1241
 
        If invoice is a Direct Invoice and is in draft state:
1242
 
         - compute total amount (check_total field)
1243
 
         - write total to the register line
1244
 
        """
1245
 
        if not context:
1246
 
            context = {}
1247
 
        # Create new number with invoice sequence
1248
 
        if vals.get('invoice_id') and self._name in ['account.invoice.line']:
1249
 
            invoice = self.pool.get('account.invoice').browse(cr, uid, vals['invoice_id'])
1250
 
            if invoice and invoice.sequence_id:
1251
 
                sequence = invoice.sequence_id
1252
 
                line = sequence.get_id(code_or_id='id', context=context)
1253
 
                vals.update({'line_number': line})
1254
 
        return super(account_invoice_line, self).create(cr, uid, vals, context)
1255
 
 
1256
 
    def write(self, cr, uid, ids, vals, context=None):
1257
 
        """
1258
 
        Give a line_number in invoice_id in vals
1259
 
        NB: This appends only for account invoice line and not other object (for an example direct invoice line)
1260
 
        If invoice is a Direct Invoice and is in draft state:
1261
 
         - compute total amount (check_total field)
1262
 
         - write total to the register line
1263
 
        """
1264
 
 
1265
 
        if not context:
1266
 
            context = {}
1267
 
        if isinstance(ids, (int, long)):
1268
 
            ids = [ids]
1269
 
        if vals.get('invoice_id') and self._name in ['account.invoice.line']:
1270
 
            for il in self.browse(cr, uid, ids):
1271
 
                if not il.line_number and il.invoice_id.sequence_id:
1272
 
                    sequence = il.invoice_id.sequence_id
1273
 
                    il_number = sequence.get_id(code_or_id='id', context=context)
1274
 
                    vals.update({'line_number': il_number})
1275
 
        res = super(account_invoice_line, self).write(cr, uid, ids, vals, context)
1276
 
        for invl in self.browse(cr, uid, ids):
1277
 
            if invl.invoice_id and invl.invoice_id.is_direct_invoice and invl.invoice_id.state == 'draft':
1278
 
                amount = 0.0
1279
 
                for l in invl.invoice_id.invoice_line:
1280
 
                    amount += l.price_subtotal
1281
 
                self.pool.get('account.invoice').write(cr, uid, [invl.invoice_id.id], {'check_total': amount}, context)
1282
 
                self.pool.get('account.bank.statement.line').write(cr, uid, [x.id for x in invl.invoice_id.register_line_ids], {'amount': -1 * amount}, context)
1283
 
        return res
1284
 
 
1285
 
    def copy(self, cr, uid, inv_id, default=None, context=None):
1286
 
        """
1287
 
        Check context to see if we come from a split. If yes, we create the link between invoice and PO/FO.
1288
 
        """
1289
 
        if not context:
1290
 
            context = {}
1291
 
        if not default:
1292
 
            default = {}
1293
 
 
1294
 
        new_id = super(account_invoice_line, self).copy(cr, uid, inv_id, default, context)
1295
 
 
1296
 
        if 'split_it' in context:
1297
 
            purchase_lines_obj = self.pool.get('purchase.order.line')
1298
 
            sale_lines_obj = self.pool.get('sale.order.line')
1299
 
 
1300
 
            if purchase_lines_obj:
1301
 
                purchase_line_ids = purchase_lines_obj.search(cr, uid,
1302
 
                        [('invoice_lines', 'in', [inv_id])], order='NO_ORDER')
1303
 
                if purchase_line_ids:
1304
 
                    purchase_lines_obj.write(cr, uid, purchase_line_ids, {'invoice_lines': [(4, new_id)]})
1305
 
 
1306
 
            if sale_lines_obj:
1307
 
                sale_lines_ids =  sale_lines_obj.search(cr, uid,
1308
 
                        [('invoice_lines', 'in', [inv_id])], order='NO_ORDER')
1309
 
                if sale_lines_ids:
1310
 
                    sale_lines_obj.write(cr, uid,  sale_lines_ids, {'invoice_lines': [(4, new_id)]})
1311
 
 
1312
 
        return new_id
1313
 
 
1314
 
    def copy_data(self, cr, uid, inv_id, default=None, context=None):
1315
 
        """
1316
 
        Copy an invoice line without its move lines
1317
 
        """
1318
 
        if default is None:
1319
 
            default = {}
1320
 
        default.update({'move_lines': False,})
1321
 
        return super(account_invoice_line, self).copy_data(cr, uid, inv_id, default, context)
1322
 
 
1323
 
    def unlink(self, cr, uid, ids, context=None):
1324
 
        """
1325
 
        If invoice is a Direct Invoice and is in draft state:
1326
 
         - compute total amount (check_total field)
1327
 
         - write total to the register line
1328
 
        """
1329
 
        if not context:
1330
 
            context = {}
1331
 
        if isinstance(ids, (int, long)):
1332
 
            ids = [ids]
1333
 
        # Fetch all invoice_id to check
1334
 
        direct_invoice_ids = []
1335
 
        abst_obj = self.pool.get('account.bank.statement.line')
1336
 
        for invl in self.browse(cr, uid, ids):
1337
 
            if invl.invoice_id and invl.invoice_id.is_direct_invoice and invl.invoice_id.state == 'draft':
1338
 
                direct_invoice_ids.append(invl.invoice_id.id)
1339
 
                # find account_bank_statement_lines and used this to delete the account_moves and associated records
1340
 
                absl_ids = abst_obj.search(cr, uid,
1341
 
                        [('invoice_id','=',invl.invoice_id.id)],
1342
 
                        order='NO_ORDER')
1343
 
                if absl_ids:
1344
 
                    abst_obj.unlink_moves(cr, uid, absl_ids, context)
1345
 
        # Normal behaviour
1346
 
        res = super(account_invoice_line, self).unlink(cr, uid, ids, context)
1347
 
        # See all direct invoice
1348
 
        for inv in self.pool.get('account.invoice').browse(cr, uid, direct_invoice_ids):
1349
 
            amount = 0.0
1350
 
            for l in inv.invoice_line:
1351
 
                amount += l.price_subtotal
1352
 
            self.pool.get('account.invoice').write(cr, uid, [inv.id], {'check_total': amount}, context)
1353
 
            self.pool.get('account.bank.statement.line').write(cr, uid, [x.id for x in inv.register_line_ids], {'amount': -1 * amount}, context)
1354
 
        return res
1355
 
 
1356
 
    def move_line_get_item(self, cr, uid, line, context=None):
1357
 
        """
1358
 
        Add a link between move line and its invoice line
1359
 
        """
1360
 
        # some verification
1361
 
        if not context:
1362
 
            context = {}
1363
 
        # update default dict with invoice line ID
1364
 
        res = super(account_invoice_line, self).move_line_get_item(cr, uid, line, context=context)
1365
 
        res.update({'invoice_line_id': line.id})
1366
 
        return res
1367
 
 
1368
 
    def button_open_analytic_lines(self, cr, uid, ids, context=None):
1369
 
        """
1370
 
        Return analytic lines linked to this invoice line.
1371
 
        First we takes all journal items that are linked to this invoice line.
1372
 
        Then for all journal items, we take all analytic journal items.
1373
 
        Finally we display the result for "button_open_analytic_corrections" of analytic lines
1374
 
        """
1375
 
        # Some checks
1376
 
        if not context:
1377
 
            context = {}
1378
 
        # Prepare some values
1379
 
        al_ids = []
1380
 
        # Browse give invoice lines
1381
 
        for il in self.browse(cr, uid, ids, context=context):
1382
 
            if il.move_lines:
1383
 
                for ml in il.move_lines:
1384
 
                    if ml.analytic_lines:
1385
 
                        al_ids += [x.id for x in ml.analytic_lines]
1386
 
        return self.pool.get('account.analytic.line').button_open_analytic_corrections(cr, uid, al_ids, context=context)
1387
 
 
1388
 
account_invoice_line()
1389
 
 
1390
 
 
1391
 
class res_partner(osv.osv):
1392
 
    _description='Partner'
1393
 
    _inherit = "res.partner"
1394
 
 
1395
 
    def _get_fake(self, cr, uid, ids, name, args, context=None):
1396
 
        res = {}
1397
 
        if not ids:
1398
 
            return res
1399
 
        if isinstance(ids, (int, long)):
1400
 
            ids = [ids]
1401
 
        for id in ids:
1402
 
            res[id] = False
1403
 
        return res
1404
 
 
1405
 
    def _get_search_by_invoice_type(self, cr, uid, obj, name, args,
1406
 
        context=None):
1407
 
        res = []
1408
 
        if not len(args):
1409
 
            return res
1410
 
        if context is None:
1411
 
            context = {}
1412
 
        if len(args) != 1:
1413
 
            msg = _("Domain %s not suported") % (str(args), )
1414
 
            raise osv.except_osv(_('Error'), msg)
1415
 
        if args[0][1] != '=':
1416
 
            msg = _("Operator '%s' not suported") % (args[0][1], )
1417
 
            raise osv.except_osv(_('Error'), msg)
1418
 
        if not args[0][2]:
1419
 
            return res
1420
 
 
1421
 
        invoice_type = context.get('type', False)
1422
 
        if invoice_type:
1423
 
            if invoice_type in ('in_invoice', 'in_refund', ):
1424
 
                # in invoices: only supplier partner
1425
 
                res = [('supplier', '=', True)]
1426
 
            elif invoice_type in ('out_invoice', 'out_refund', ):
1427
 
                # out invoices: only customer partner
1428
 
                res = [('customer', '=', True)]
1429
 
 
1430
 
        return res
1431
 
 
1432
 
    _columns = {
1433
 
        'by_invoice_type': fields.function(_get_fake, type='boolean',
1434
 
            fnct_search=_get_search_by_invoice_type, method=True),
1435
 
    }
1436
 
 
1437
 
    def name_search(self, cr, uid, name='', args=None, operator='ilike',
1438
 
        context=None, limit=100):
1439
 
        # BKLG-50: IN/OUT invoice/refund partner autocompletion filter
1440
 
        # regarding supplier/customer
1441
 
        if context is None:
1442
 
            context = {}
1443
 
 
1444
 
        alternate_domain = False
1445
 
        invoice_type = context.get('type', False)
1446
 
        if invoice_type:
1447
 
            if invoice_type in ('in_invoice', 'in_refund', ):
1448
 
                alternate_domain = [('supplier', '=', True)]
1449
 
            elif invoice_type in ('out_invoice', 'out_refund', ):
1450
 
                alternate_domain = [('customer', '=', True)]
1451
 
        if alternate_domain:
1452
 
            args += alternate_domain
1453
 
 
1454
 
        return super(res_partner, self).name_search(cr, uid, name=name,
1455
 
            args=args, operator=operator, context=context, limit=limit)
1456
 
 
1457
 
res_partner()
1458
 
 
1459
 
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: