~unifield-team/unifield-wm/us-826

« back to all changes in this revision

Viewing changes to account_override/invoice.py

  • Committer: Quentin THEURET
  • Date: 2016-03-04 12:15:00 UTC
  • Revision ID: qt@tempo-consulting.fr-20160304121500-u2ay8zrf83ih9fu3
US-826 [IMP] Change the way to check if products is not consistent on add multiple line wizard

Show diffs side-by-side

added added

removed removed

Lines of Context:
26
26
from osv import fields
27
27
from time import strftime
28
28
from tools.translate import _
29
 
import logging
 
29
from lxml import etree
 
30
import re
 
31
import netsvc
 
32
 
 
33
 
 
34
import decimal_precision as dp
30
35
 
31
36
class account_invoice(osv.osv):
32
37
    _name = 'account.invoice'
33
38
    _inherit = 'account.invoice'
34
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
 
35
210
    _columns = {
36
211
        'from_yml_test': fields.boolean('Only used to pass addons unit test', readonly=True, help='Never set this field to true !'),
37
212
        'sequence_id': fields.many2one('ir.sequence', string='Lines Sequence', ondelete='cascade',
38
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"),
39
245
    }
40
246
 
41
247
    _defaults = {
 
248
        'journal_id': _get_journal,
42
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,
43
258
    }
44
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
 
45
584
    def create_sequence(self, cr, uid, vals, context=None):
46
585
        """
47
586
        Create new entry sequence for every new invoice
66
605
        }
67
606
        return seq_pool.create(cr, uid, seq)
68
607
 
69
 
    def create(self, cr, uid, vals, context=None):
70
 
        """
71
 
        Filled in 'from_yml_test' to True if we come from tests
72
 
        """
73
 
        if not context:
74
 
            context = {}
75
 
        if context.get('update_mode') in ['init', 'update']:
76
 
            logging.getLogger('init').info('INV: set from yml test to True')
77
 
            vals['from_yml_test'] = True
78
 
        # Create a sequence for this new invoice
79
 
        res_seq = self.create_sequence(cr, uid, vals, context)
80
 
        vals.update({'sequence_id': res_seq,})
81
 
        return super(account_invoice, self).create(cr, uid, vals, context)
 
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
82
800
 
83
801
    def action_open_invoice(self, cr, uid, ids, context=None, *args):
84
802
        """
86
804
        """
87
805
        if not context:
88
806
            context = {}
 
807
        if isinstance(ids, (int, long)):
 
808
            ids = [ids]
89
809
        if not self.action_date_assign(cr, uid, ids, context, args):
90
810
            return False
91
811
        if not self.action_move_create(cr, uid, ids, context, args):
92
812
            return False
93
813
        if not self.action_number(cr, uid, ids, context):
94
814
            return False
95
 
        return True
96
 
 
97
 
    def _hook_period_id(self, cr, uid, inv, context=None):
98
 
        """
99
 
        Give matches period that are not draft and not HQ-closed from given date
100
 
        """
101
 
        # Some verifications
102
 
        if not context:
103
 
            context = {}
104
 
        if not inv:
105
 
            return False
106
 
        # NB: there is some period state. So we define that we choose only open period (so not draft and not done)
107
 
        res = self.pool.get('account.period').search(cr, uid, [('date_start','<=',inv.date_invoice or strftime('%Y-%m-%d')),
108
 
            ('date_stop','>=',inv.date_invoice or strftime('%Y-%m-%d')), ('state', 'not in', ['created', 'done']), 
109
 
            ('company_id', '=', inv.company_id.id)], context=context, order="date_start ASC, name ASC")
 
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
 
110
1155
        return res
111
1156
 
112
1157
account_invoice()
115
1160
    _name = 'account.invoice.line'
116
1161
    _inherit = 'account.invoice.line'
117
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
 
118
1214
    _columns = {
119
1215
        'from_yml_test': fields.boolean('Only used to pass addons unit test', readonly=True, help='Never set this field to true !'),
120
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),
121
1226
    }
122
1227
 
123
1228
    _defaults = {
 
1229
        'price_unit': lambda *a: 0.00,
124
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,
125
1233
    }
126
1234
 
127
1235
    _order = 'line_number'
128
1236
 
129
1237
    def create(self, cr, uid, vals, context=None):
130
1238
        """
131
 
        Filled in 'from_yml_test' to True if we come from tests.
132
1239
        Give a line_number to invoice line.
133
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
134
1244
        """
135
1245
        if not context:
136
1246
            context = {}
137
 
        if context.get('update_mode') in ['init', 'update']:
138
 
            logging.getLogger('init').info('INV: set from yml test to True')
139
 
            vals['from_yml_test'] = True
140
1247
        # Create new number with invoice sequence
141
1248
        if vals.get('invoice_id') and self._name in ['account.invoice.line']:
142
1249
            invoice = self.pool.get('account.invoice').browse(cr, uid, vals['invoice_id'])
143
1250
            if invoice and invoice.sequence_id:
144
1251
                sequence = invoice.sequence_id
145
 
                line = sequence.get_id(test='id', context=context)
 
1252
                line = sequence.get_id(code_or_id='id', context=context)
146
1253
                vals.update({'line_number': line})
147
1254
        return super(account_invoice_line, self).create(cr, uid, vals, context)
148
1255
 
150
1257
        """
151
1258
        Give a line_number in invoice_id in vals
152
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
153
1263
        """
 
1264
 
154
1265
        if not context:
155
1266
            context = {}
156
1267
        if isinstance(ids, (int, long)):
159
1270
            for il in self.browse(cr, uid, ids):
160
1271
                if not il.line_number and il.invoice_id.sequence_id:
161
1272
                    sequence = il.invoice_id.sequence_id
162
 
                    il_number = sequence.get_id(test='id', context=context)
 
1273
                    il_number = sequence.get_id(code_or_id='id', context=context)
163
1274
                    vals.update({'line_number': il_number})
164
 
        return super(account_invoice_line, self).write(cr, uid, ids, vals, context)
 
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)
165
1387
 
166
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
 
167
1459
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: