~ubuntu-branches/ubuntu/quantal/openerp6.1/quantal

« back to all changes in this revision

Viewing changes to openerp/addons/account/edi/invoice.py

  • Committer: Package Import Robot
  • Author(s): Yolanda Robla
  • Date: 2012-09-20 15:29:00 UTC
  • Revision ID: package-import@ubuntu.com-20120920152900-woyy3yww8z6acmsk
Tags: upstream-6.1-1+dfsg
ImportĀ upstreamĀ versionĀ 6.1-1+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*-
 
2
##############################################################################
 
3
#
 
4
#    OpenERP, Open Source Business Applications
 
5
#    Copyright (c) 2011 OpenERP S.A. <http://openerp.com>
 
6
#
 
7
#    This program is free software: you can redistribute it and/or modify
 
8
#    it under the terms of the GNU Affero General Public License as
 
9
#    published by the Free Software Foundation, either version 3 of the
 
10
#    License, or (at your option) any later version.
 
11
#
 
12
#    This program is distributed in the hope that it will be useful,
 
13
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
15
#    GNU Affero General Public License for more details.
 
16
#
 
17
#    You should have received a copy of the GNU Affero General Public License
 
18
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
19
#
 
20
##############################################################################
 
21
 
 
22
from osv import fields, osv, orm
 
23
from edi import EDIMixin
 
24
 
 
25
INVOICE_LINE_EDI_STRUCT = {
 
26
    'name': True,
 
27
    'origin': True,
 
28
    'uos_id': True,
 
29
    'product_id': True,
 
30
    'price_unit': True,
 
31
    'quantity': True,
 
32
    'discount': True,
 
33
    'note': True,
 
34
 
 
35
    # fields used for web preview only - discarded on import
 
36
    'price_subtotal': True,
 
37
}
 
38
 
 
39
INVOICE_TAX_LINE_EDI_STRUCT = {
 
40
    'name': True,
 
41
    'base': True,
 
42
    'amount': True,
 
43
    'manual': True,
 
44
    'sequence': True,
 
45
    'base_amount': True,
 
46
    'tax_amount': True,
 
47
}
 
48
 
 
49
INVOICE_EDI_STRUCT = {
 
50
    'name': True,
 
51
    'origin': True,
 
52
    'company_id': True, # -> to be changed into partner
 
53
    'type': True, # -> reversed at import
 
54
    'internal_number': True, # -> reference at import
 
55
    'comment': True,
 
56
    'date_invoice': True,
 
57
    'date_due': True,
 
58
    'partner_id': True,
 
59
    'payment_term': True,
 
60
    #custom: currency_id
 
61
    'invoice_line': INVOICE_LINE_EDI_STRUCT,
 
62
    'tax_line': INVOICE_TAX_LINE_EDI_STRUCT,
 
63
 
 
64
    # fields used for web preview only - discarded on import
 
65
    #custom: 'partner_ref'
 
66
    'amount_total': True,
 
67
    'amount_untaxed': True,
 
68
    'amount_tax': True,
 
69
}
 
70
 
 
71
class account_invoice(osv.osv, EDIMixin):
 
72
    _inherit = 'account.invoice'
 
73
 
 
74
    def edi_export(self, cr, uid, records, edi_struct=None, context=None):
 
75
        """Exports a supplier or customer invoice"""
 
76
        edi_struct = dict(edi_struct or INVOICE_EDI_STRUCT)
 
77
        res_company = self.pool.get('res.company')
 
78
        res_partner_address = self.pool.get('res.partner.address')
 
79
        edi_doc_list = []
 
80
        for invoice in records:
 
81
            # generate the main report
 
82
            self._edi_generate_report_attachment(cr, uid, invoice, context=context)
 
83
            edi_doc = super(account_invoice,self).edi_export(cr, uid, [invoice], edi_struct, context)[0]
 
84
            edi_doc.update({
 
85
                    'company_address': res_company.edi_export_address(cr, uid, invoice.company_id, context=context),
 
86
                    'company_paypal_account': invoice.company_id.paypal_account,
 
87
                    'partner_address': res_partner_address.edi_export(cr, uid, [invoice.address_invoice_id], context=context)[0],
 
88
 
 
89
                    'currency': self.pool.get('res.currency').edi_export(cr, uid, [invoice.currency_id], context=context)[0],
 
90
                    'partner_ref': invoice.reference or False,
 
91
            })
 
92
            edi_doc_list.append(edi_doc)
 
93
        return edi_doc_list
 
94
 
 
95
    def _edi_tax_account(self, cr, uid, invoice_type='out_invoice', context=None):
 
96
        #TODO/FIXME: should select proper Tax Account
 
97
        account_pool = self.pool.get('account.account')
 
98
        account_ids = account_pool.search(cr, uid, [('type','<>','view'),('type','<>','income'), ('type', '<>', 'closed')])
 
99
        tax_account = False
 
100
        if account_ids:
 
101
            tax_account = account_pool.browse(cr, uid, account_ids[0])
 
102
        return tax_account
 
103
 
 
104
    def _edi_invoice_account(self, cr, uid, partner_id, invoice_type, context=None):
 
105
        partner_pool = self.pool.get('res.partner')
 
106
        partner = partner_pool.browse(cr, uid, partner_id, context=context)
 
107
        if invoice_type in ('out_invoice', 'out_refund'):
 
108
            invoice_account = partner.property_account_receivable
 
109
        else:
 
110
            invoice_account = partner.property_account_payable
 
111
        return invoice_account
 
112
 
 
113
    def _edi_product_account(self, cr, uid, product_id, invoice_type, context=None):
 
114
        product_pool = self.pool.get('product.product')
 
115
        product = product_pool.browse(cr, uid, product_id, context=context)
 
116
        if invoice_type in ('out_invoice','out_refund'):
 
117
            account = product.property_account_income or product.categ_id.property_account_income_categ
 
118
        else:
 
119
            account = product.property_account_expense or product.categ_id.property_account_expense_categ
 
120
        return account
 
121
 
 
122
    def _edi_import_company(self, cr, uid, edi_document, context=None):
 
123
        # TODO: for multi-company setups, we currently import the document in the
 
124
        #       user's current company, but we should perhaps foresee a way to select
 
125
        #       the desired company among the user's allowed companies
 
126
 
 
127
        self._edi_requires_attributes(('company_id','company_address','type'), edi_document)
 
128
        res_partner_address = self.pool.get('res.partner.address')
 
129
        res_partner = self.pool.get('res.partner')
 
130
 
 
131
        # imported company = new partner
 
132
        src_company_id, src_company_name = edi_document.pop('company_id')
 
133
        partner_id = self.edi_import_relation(cr, uid, 'res.partner', src_company_name,
 
134
                                              src_company_id, context=context)
 
135
        invoice_type = edi_document['type']
 
136
        partner_value = {}
 
137
        if invoice_type in ('out_invoice', 'out_refund'):
 
138
            partner_value.update({'customer': True})
 
139
        if invoice_type in ('in_invoice', 'in_refund'):
 
140
            partner_value.update({'supplier': True})
 
141
        res_partner.write(cr, uid, [partner_id], partner_value, context=context)
 
142
 
 
143
        # imported company_address = new partner address
 
144
        address_info = edi_document.pop('company_address')
 
145
        address_info['partner_id'] = (src_company_id, src_company_name)
 
146
        address_info['type'] = 'invoice'
 
147
        address_id = res_partner_address.edi_import(cr, uid, address_info, context=context)
 
148
 
 
149
        # modify edi_document to refer to new partner
 
150
        partner_address = res_partner_address.browse(cr, uid, address_id, context=context)
 
151
        edi_document['partner_id'] = (src_company_id, src_company_name)
 
152
        edi_document.pop('partner_address', False) # ignored
 
153
        edi_document['address_invoice_id'] = self.edi_m2o(cr, uid, partner_address, context=context)
 
154
 
 
155
        return partner_id
 
156
 
 
157
 
 
158
    def edi_import(self, cr, uid, edi_document, context=None):
 
159
        """ During import, invoices will import the company that is provided in the invoice as
 
160
            a new partner (e.g. supplier company for a customer invoice will be come a supplier
 
161
            record for the new invoice.
 
162
            Summary of tasks that need to be done:
 
163
                - import company as a new partner, if type==in then supplier=1, else customer=1
 
164
                - partner_id field is modified to point to the new partner
 
165
                - company_address data used to add address to new partner
 
166
                - change type: out_invoice'<->'in_invoice','out_refund'<->'in_refund'
 
167
                - reference: should contain the value of the 'internal_number'
 
168
                - reference_type: 'none'
 
169
                - internal number: reset to False, auto-generated
 
170
                - journal_id: should be selected based on type: simply put the 'type'
 
171
                    in the context when calling create(), will be selected correctly
 
172
                - payment_term: if set, create a default one based on name...
 
173
                - for invoice lines, the account_id value should be taken from the
 
174
                    product's default, i.e. from the default category, as it will not
 
175
                    be provided.
 
176
                - for tax lines, we disconnect from the invoice.line, so all tax lines
 
177
                    will be of type 'manual', and default accounts should be picked based
 
178
                    on the tax config of the DB where it is imported.
 
179
        """
 
180
        if context is None:
 
181
            context = {}
 
182
        self._edi_requires_attributes(('company_id','company_address','type','invoice_line','currency'), edi_document)
 
183
 
 
184
        # extract currency info
 
185
        res_currency = self.pool.get('res.currency')
 
186
        currency_info = edi_document.pop('currency')
 
187
        currency_id = res_currency.edi_import(cr, uid, currency_info, context=context)
 
188
        currency = res_currency.browse(cr, uid, currency_id)
 
189
        edi_document['currency_id'] =  self.edi_m2o(cr, uid, currency, context=context)
 
190
 
 
191
        # change type: out_invoice'<->'in_invoice','out_refund'<->'in_refund'
 
192
        invoice_type = edi_document['type']
 
193
        invoice_type = invoice_type.startswith('in_') and invoice_type.replace('in_','out_') or invoice_type.replace('out_','in_')
 
194
        edi_document['type'] = invoice_type
 
195
 
 
196
        #import company as a new partner
 
197
        partner_id = self._edi_import_company(cr, uid, edi_document, context=context)
 
198
 
 
199
        # Set Account
 
200
        invoice_account = self._edi_invoice_account(cr, uid, partner_id, invoice_type, context=context)
 
201
        edi_document['account_id'] = invoice_account and self.edi_m2o(cr, uid, invoice_account, context=context) or False
 
202
 
 
203
        # reference: should contain the value of the 'internal_number'
 
204
        edi_document['reference'] = edi_document.get('internal_number', False)
 
205
        # reference_type: 'none'
 
206
        edi_document['reference_type'] = 'none'
 
207
 
 
208
        # internal number: reset to False, auto-generated
 
209
        edi_document['internal_number'] = False
 
210
 
 
211
        # discard web preview fields, if present
 
212
        edi_document.pop('partner_ref', None)
 
213
 
 
214
        # journal_id: should be selected based on type: simply put the 'type' in the context when calling create(), will be selected correctly
 
215
        context.update(type=invoice_type)
 
216
 
 
217
        # for invoice lines, the account_id value should be taken from the product's default, i.e. from the default category, as it will not be provided.
 
218
        for edi_invoice_line in edi_document['invoice_line']:
 
219
            product_info = edi_invoice_line['product_id']
 
220
            product_id = self.edi_import_relation(cr, uid, 'product.product', product_info[1],
 
221
                                                  product_info[0], context=context)
 
222
            account = self._edi_product_account(cr, uid, product_id, invoice_type, context=context)
 
223
            # TODO: could be improved with fiscal positions perhaps
 
224
            # account = fpos_obj.map_account(cr, uid, fiscal_position_id, account.id)
 
225
            edi_invoice_line['account_id'] = self.edi_m2o(cr, uid, account, context=context) if account else False
 
226
 
 
227
            # discard web preview fields, if present
 
228
            edi_invoice_line.pop('price_subtotal', None)
 
229
 
 
230
        # for tax lines, we disconnect from the invoice.line, so all tax lines will be of type 'manual', and default accounts should be picked based
 
231
        # on the tax config of the DB where it is imported.
 
232
        tax_account = self._edi_tax_account(cr, uid, context=context)
 
233
        tax_account_info = self.edi_m2o(cr, uid, tax_account, context=context)
 
234
        for edi_tax_line in edi_document.get('tax_line', []):
 
235
            edi_tax_line['account_id'] = tax_account_info
 
236
            edi_tax_line['manual'] = True
 
237
 
 
238
        return super(account_invoice,self).edi_import(cr, uid, edi_document, context=context)
 
239
 
 
240
 
 
241
    def _edi_record_display_action(self, cr, uid, id, context=None):
 
242
        """Returns an appropriate action definition dict for displaying
 
243
           the record with ID ``rec_id``.
 
244
 
 
245
           :param int id: database ID of record to display
 
246
           :return: action definition dict
 
247
        """
 
248
        action = super(account_invoice,self)._edi_record_display_action(cr, uid, id, context=context)
 
249
        try:
 
250
            invoice = self.browse(cr, uid, id, context=context)
 
251
            if 'out_' in invoice.type:
 
252
                view_ext_id = 'invoice_form'
 
253
                journal_type = 'sale'
 
254
            else:
 
255
                view_ext_id = 'invoice_supplier_form'
 
256
                journal_type = 'purchase'
 
257
            ctx = "{'type': '%s', 'journal_type': '%s'}" % (invoice.type, journal_type)
 
258
            action.update(context=ctx)
 
259
            view_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account', view_ext_id)[1]
 
260
            action.update(views=[(view_id,'form'), (False, 'tree')])
 
261
        except ValueError:
 
262
            # ignore if views are missing
 
263
            pass
 
264
        return action
 
265
 
 
266
 
 
267
class account_invoice_line(osv.osv, EDIMixin):
 
268
    _inherit='account.invoice.line'
 
269
 
 
270
class account_invoice_tax(osv.osv, EDIMixin):
 
271
    _inherit = "account.invoice.tax"
 
272
 
 
273
 
 
274
 
 
275
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: