1
# -*- coding: utf-8 -*-
2
##############################################################################
4
# OpenERP, Open Source Business Applications
5
# Copyright (c) 2011-2012 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
##############################################################################
21
from openerp.osv import osv, fields
22
from openerp.addons.edi import EDIMixin
24
from urllib import urlencode
26
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 = self.pool.get('res.partner')
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.edi_export(cr, uid, [invoice.partner_id], context=context)[0],
88
'currency': self.pool.get('res.currency').edi_export(cr, uid, [invoice.currency_id], context=context)[0],
89
'partner_ref': invoice.reference or False,
91
edi_doc_list.append(edi_doc)
94
def _edi_tax_account(self, cr, uid, invoice_type='out_invoice', context=None):
95
#TODO/FIXME: should select proper Tax Account
96
account_pool = self.pool.get('account.account')
97
account_ids = account_pool.search(cr, uid, [('type','<>','view'),('type','<>','income'), ('type', '<>', 'closed')])
100
tax_account = account_pool.browse(cr, uid, account_ids[0])
103
def _edi_invoice_account(self, cr, uid, partner_id, invoice_type, context=None):
104
res_partner = self.pool.get('res.partner')
105
partner = res_partner.browse(cr, uid, partner_id, context=context)
106
if invoice_type in ('out_invoice', 'out_refund'):
107
invoice_account = partner.property_account_receivable
109
invoice_account = partner.property_account_payable
110
return invoice_account
112
def _edi_product_account(self, cr, uid, product_id, invoice_type, context=None):
113
product_pool = self.pool.get('product.product')
114
product = product_pool.browse(cr, uid, product_id, context=context)
115
if invoice_type in ('out_invoice','out_refund'):
116
account = product.property_account_income or product.categ_id.property_account_income_categ
118
account = product.property_account_expense or product.categ_id.property_account_expense_categ
121
def _edi_import_company(self, cr, uid, edi_document, context=None):
122
# TODO: for multi-company setups, we currently import the document in the
123
# user's current company, but we should perhaps foresee a way to select
124
# the desired company among the user's allowed companies
126
self._edi_requires_attributes(('company_id','company_address','type'), edi_document)
127
res_partner = self.pool.get('res.partner')
129
xid, company_name = edi_document.pop('company_id')
130
# Retrofit address info into a unified partner info (changed in v7 - used to keep them separate)
131
company_address_edi = edi_document.pop('company_address')
132
company_address_edi['name'] = company_name
133
company_address_edi['is_company'] = True
134
company_address_edi['__import_model'] = 'res.partner'
135
company_address_edi['__id'] = xid # override address ID, as of v7 they should be the same anyway
136
if company_address_edi.get('logo'):
137
company_address_edi['image'] = company_address_edi.pop('logo')
139
invoice_type = edi_document['type']
140
if invoice_type.startswith('out_'):
141
company_address_edi['customer'] = True
143
company_address_edi['supplier'] = True
144
partner_id = res_partner.edi_import(cr, uid, company_address_edi, context=context)
146
# modify edi_document to refer to new partner
147
partner = res_partner.browse(cr, uid, partner_id, context=context)
148
partner_edi_m2o = self.edi_m2o(cr, uid, partner, context=context)
149
edi_document['partner_id'] = partner_edi_m2o
150
edi_document.pop('partner_address', None) # ignored, that's supposed to be our own address!
154
def edi_import(self, cr, uid, edi_document, context=None):
155
""" During import, invoices will import the company that is provided in the invoice as
156
a new partner (e.g. supplier company for a customer invoice will be come a supplier
157
record for the new invoice.
158
Summary of tasks that need to be done:
159
- import company as a new partner, if type==in then supplier=1, else customer=1
160
- partner_id field is modified to point to the new partner
161
- company_address data used to add address to new partner
162
- change type: out_invoice'<->'in_invoice','out_refund'<->'in_refund'
163
- reference: should contain the value of the 'internal_number'
164
- reference_type: 'none'
165
- internal number: reset to False, auto-generated
166
- journal_id: should be selected based on type: simply put the 'type'
167
in the context when calling create(), will be selected correctly
168
- payment_term: if set, create a default one based on name...
169
- for invoice lines, the account_id value should be taken from the
170
product's default, i.e. from the default category, as it will not
172
- for tax lines, we disconnect from the invoice.line, so all tax lines
173
will be of type 'manual', and default accounts should be picked based
174
on the tax config of the DB where it is imported.
178
self._edi_requires_attributes(('company_id','company_address','type','invoice_line','currency'), edi_document)
180
# extract currency info
181
res_currency = self.pool.get('res.currency')
182
currency_info = edi_document.pop('currency')
183
currency_id = res_currency.edi_import(cr, uid, currency_info, context=context)
184
currency = res_currency.browse(cr, uid, currency_id)
185
edi_document['currency_id'] = self.edi_m2o(cr, uid, currency, context=context)
187
# change type: out_invoice'<->'in_invoice','out_refund'<->'in_refund'
188
invoice_type = edi_document['type']
189
invoice_type = invoice_type.startswith('in_') and invoice_type.replace('in_','out_') or invoice_type.replace('out_','in_')
190
edi_document['type'] = invoice_type
192
# import company as a new partner
193
partner_id = self._edi_import_company(cr, uid, edi_document, context=context)
196
invoice_account = self._edi_invoice_account(cr, uid, partner_id, invoice_type, context=context)
197
edi_document['account_id'] = invoice_account and self.edi_m2o(cr, uid, invoice_account, context=context) or False
199
# reference: should contain the value of the 'internal_number'
200
edi_document['reference'] = edi_document.get('internal_number', False)
201
# reference_type: 'none'
202
edi_document['reference_type'] = 'none'
204
# internal number: reset to False, auto-generated
205
edi_document['internal_number'] = False
207
# discard web preview fields, if present
208
edi_document.pop('partner_ref', None)
210
# journal_id: should be selected based on type: simply put the 'type' in the context when calling create(), will be selected correctly
211
context.update(type=invoice_type)
213
# 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.
214
for edi_invoice_line in edi_document['invoice_line']:
215
product_info = edi_invoice_line['product_id']
216
product_id = self.edi_import_relation(cr, uid, 'product.product', product_info[1],
217
product_info[0], context=context)
218
account = self._edi_product_account(cr, uid, product_id, invoice_type, context=context)
219
# TODO: could be improved with fiscal positions perhaps
220
# account = fpos_obj.map_account(cr, uid, fiscal_position_id, account.id)
221
edi_invoice_line['account_id'] = self.edi_m2o(cr, uid, account, context=context) if account else False
223
# discard web preview fields, if present
224
edi_invoice_line.pop('price_subtotal', None)
226
# 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
227
# on the tax config of the DB where it is imported.
228
tax_account = self._edi_tax_account(cr, uid, context=context)
229
tax_account_info = self.edi_m2o(cr, uid, tax_account, context=context)
230
for edi_tax_line in edi_document.get('tax_line', []):
231
edi_tax_line['account_id'] = tax_account_info
232
edi_tax_line['manual'] = True
234
return super(account_invoice,self).edi_import(cr, uid, edi_document, context=context)
237
def _edi_record_display_action(self, cr, uid, id, context=None):
238
"""Returns an appropriate action definition dict for displaying
239
the record with ID ``rec_id``.
241
:param int id: database ID of record to display
242
:return: action definition dict
244
action = super(account_invoice,self)._edi_record_display_action(cr, uid, id, context=context)
246
invoice = self.browse(cr, uid, id, context=context)
247
if 'out_' in invoice.type:
248
view_ext_id = 'invoice_form'
249
journal_type = 'sale'
251
view_ext_id = 'invoice_supplier_form'
252
journal_type = 'purchase'
253
ctx = "{'type': '%s', 'journal_type': '%s'}" % (invoice.type, journal_type)
254
action.update(context=ctx)
255
view_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account', view_ext_id)[1]
256
action.update(views=[(view_id,'form'), (False, 'tree')])
258
# ignore if views are missing
262
def _edi_paypal_url(self, cr, uid, ids, field, arg, context=None):
263
res = dict.fromkeys(ids, False)
264
for inv in self.browse(cr, uid, ids, context=context):
265
if inv.type == 'out_invoice' and inv.company_id.paypal_account:
268
"business": inv.company_id.paypal_account,
269
"item_name": inv.company_id.name + " Invoice " + inv.number,
270
"invoice": inv.number,
271
"amount": inv.residual,
272
"currency_code": inv.currency_id.name,
273
"button_subtype": "services",
275
"bn": "OpenERP_Invoice_PayNow_" + inv.currency_id.name,
277
res[inv.id] = "https://www.paypal.com/cgi-bin/webscr?" + urlencode(params)
281
'paypal_url': fields.function(_edi_paypal_url, type='char', string='Paypal Url'),
285
class account_invoice_line(osv.osv, EDIMixin):
286
_inherit='account.invoice.line'
288
class account_invoice_tax(osv.osv, EDIMixin):
289
_inherit = "account.invoice.tax"
293
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: