1
# -*- coding: utf-8 -*-
2
##############################################################################
4
# OpenERP, Open Source Business Applications
5
# Copyright (c) 2011 OpenERP S.A. <http://openerp.com>
7
# This program is free software: you can redistribute it and/or modify
8
# it under the terms of the GNU Affero General Public License as
9
# published by the Free Software Foundation, either version 3 of the
10
# License, or (at your option) any later version.
12
# This program is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU Affero General Public License for more details.
17
# You should have received a copy of the GNU Affero General Public License
18
# along with this program. If not, see <http://www.gnu.org/licenses/>.
20
##############################################################################
22
from osv import fields, osv, orm
23
from edi import EDIMixin
25
INVOICE_LINE_EDI_STRUCT = {
35
# fields used for web preview only - discarded on import
36
'price_subtotal': True,
39
INVOICE_TAX_LINE_EDI_STRUCT = {
49
INVOICE_EDI_STRUCT = {
52
'company_id': True, # -> to be changed into partner
53
'type': True, # -> reversed at import
54
'internal_number': True, # -> reference at import
61
'invoice_line': INVOICE_LINE_EDI_STRUCT,
62
'tax_line': INVOICE_TAX_LINE_EDI_STRUCT,
64
# fields used for web preview only - discarded on import
65
#custom: 'partner_ref'
67
'amount_untaxed': True,
71
class account_invoice(osv.osv, EDIMixin):
72
_inherit = 'account.invoice'
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')
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]
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],
89
'currency': self.pool.get('res.currency').edi_export(cr, uid, [invoice.currency_id], context=context)[0],
90
'partner_ref': invoice.reference or False,
92
edi_doc_list.append(edi_doc)
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')])
101
tax_account = account_pool.browse(cr, uid, account_ids[0])
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
110
invoice_account = partner.property_account_payable
111
return invoice_account
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
119
account = product.property_account_expense or product.categ_id.property_account_expense_categ
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
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')
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']
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)
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)
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)
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
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.
182
self._edi_requires_attributes(('company_id','company_address','type','invoice_line','currency'), edi_document)
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)
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
196
#import company as a new partner
197
partner_id = self._edi_import_company(cr, uid, edi_document, context=context)
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
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'
208
# internal number: reset to False, auto-generated
209
edi_document['internal_number'] = False
211
# discard web preview fields, if present
212
edi_document.pop('partner_ref', None)
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)
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
227
# discard web preview fields, if present
228
edi_invoice_line.pop('price_subtotal', None)
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
238
return super(account_invoice,self).edi_import(cr, uid, edi_document, context=context)
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``.
245
:param int id: database ID of record to display
246
:return: action definition dict
248
action = super(account_invoice,self)._edi_record_display_action(cr, uid, id, context=context)
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'
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')])
262
# ignore if views are missing
267
class account_invoice_line(osv.osv, EDIMixin):
268
_inherit='account.invoice.line'
270
class account_invoice_tax(osv.osv, EDIMixin):
271
_inherit = "account.invoice.tax"
275
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: