# -*- coding: utf-8 -*- ############################################################################## # # OpenERP, Open Source Business Applications # Copyright (c) 2011 OpenERP S.A. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # ############################################################################## from osv import fields, osv, orm from edi import EDIMixin from edi.models import edi INVOICE_LINE_EDI_STRUCT = { 'name': True, 'origin': True, 'uos_id': True, 'product_id': True, 'price_unit': True, 'quantity': True, 'discount': True, # fields used for web preview only - discarded on import 'price_subtotal': True, } INVOICE_TAX_LINE_EDI_STRUCT = { 'name': True, 'base': True, 'amount': True, 'manual': True, 'sequence': True, 'base_amount': True, 'tax_amount': True, } INVOICE_EDI_STRUCT = { 'name': True, 'origin': True, 'company_id': True, # -> to be changed into partner 'type': True, # -> reversed at import 'internal_number': True, # -> reference at import 'comment': True, 'date_invoice': True, 'date_due': True, 'partner_id': True, 'payment_term': True, #custom: currency_id 'invoice_line': INVOICE_LINE_EDI_STRUCT, 'tax_line': INVOICE_TAX_LINE_EDI_STRUCT, # fields used for web preview only - discarded on import #custom: 'partner_ref' 'amount_total': True, 'amount_untaxed': True, 'amount_tax': True, } class account_invoice(osv.osv, EDIMixin): _inherit = 'account.invoice' def action_invoice_sent(self, cr, uid, ids, context=None): """"Override this method to add a link to mail""" if context is None: context = {} invoice_objs = self.browse(cr, uid, ids, context=context) edi_token = self.pool.get('edi.document').export_edi(cr, uid, invoice_objs, context = context)[0] web_root_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url') ctx = dict(context, edi_web_url_view=edi.EDI_VIEW_WEB_URL % (web_root_url, cr.dbname, edi_token)) return super(account_invoice, self).action_invoice_sent(cr, uid, ids, context=ctx) def edi_export(self, cr, uid, records, edi_struct=None, context=None): """Exports a supplier or customer invoice""" edi_struct = dict(edi_struct or INVOICE_EDI_STRUCT) res_company = self.pool.get('res.company') res_partner = self.pool.get('res.partner') edi_doc_list = [] for invoice in records: # generate the main report self._edi_generate_report_attachment(cr, uid, invoice, context=context) edi_doc = super(account_invoice,self).edi_export(cr, uid, [invoice], edi_struct, context)[0] edi_doc.update({ 'company_address': res_company.edi_export_address(cr, uid, invoice.company_id, context=context), 'company_paypal_account': invoice.company_id.paypal_account, 'partner_address': res_partner.edi_export(cr, uid, [invoice.partner_id], context=context)[0], 'currency': self.pool.get('res.currency').edi_export(cr, uid, [invoice.currency_id], context=context)[0], 'partner_ref': invoice.reference or False, }) edi_doc_list.append(edi_doc) return edi_doc_list def _edi_tax_account(self, cr, uid, invoice_type='out_invoice', context=None): #TODO/FIXME: should select proper Tax Account account_pool = self.pool.get('account.account') account_ids = account_pool.search(cr, uid, [('type','<>','view'),('type','<>','income'), ('type', '<>', 'closed')]) tax_account = False if account_ids: tax_account = account_pool.browse(cr, uid, account_ids[0]) return tax_account def _edi_invoice_account(self, cr, uid, partner_id, invoice_type, context=None): partner_pool = self.pool.get('res.partner') partner = partner_pool.browse(cr, uid, partner_id, context=context) if invoice_type in ('out_invoice', 'out_refund'): invoice_account = partner.property_account_receivable else: invoice_account = partner.property_account_payable return invoice_account def _edi_product_account(self, cr, uid, product_id, invoice_type, context=None): product_pool = self.pool.get('product.product') product = product_pool.browse(cr, uid, product_id, context=context) if invoice_type in ('out_invoice','out_refund'): account = product.property_account_income or product.categ_id.property_account_income_categ else: account = product.property_account_expense or product.categ_id.property_account_expense_categ return account def _edi_import_company(self, cr, uid, edi_document, context=None): # TODO: for multi-company setups, we currently import the document in the # user's current company, but we should perhaps foresee a way to select # the desired company among the user's allowed companies self._edi_requires_attributes(('company_id','company_address','type'), edi_document) res_partner = self.pool.get('res.partner') src_company_id, src_company_name = edi_document.pop('company_id') invoice_type = edi_document['type'] partner_value = {} if invoice_type in ('out_invoice', 'out_refund'): partner_value.update({'customer': True}) if invoice_type in ('in_invoice', 'in_refund'): partner_value.update({'supplier': True}) # imported company_address = new partner address address_info = edi_document.pop('company_address') if 'name' not in address_info: address_info['name'] = src_company_name address_info['type'] = 'invoice' address_info.update(partner_value) address_id = res_partner.edi_import(cr, uid, address_info, context=context) # modify edi_document to refer to new partner partner_address = res_partner.browse(cr, uid, address_id, context=context) address_edi_m2o = self.edi_m2o(cr, uid, partner_address, context=context) edi_document['partner_id'] = address_edi_m2o edi_document.pop('partner_address', False) # ignored return address_id def edi_import(self, cr, uid, edi_document, context=None): """ During import, invoices will import the company that is provided in the invoice as a new partner (e.g. supplier company for a customer invoice will be come a supplier record for the new invoice. Summary of tasks that need to be done: - import company as a new partner, if type==in then supplier=1, else customer=1 - partner_id field is modified to point to the new partner - company_address data used to add address to new partner - change type: out_invoice'<->'in_invoice','out_refund'<->'in_refund' - reference: should contain the value of the 'internal_number' - reference_type: 'none' - internal number: reset to False, auto-generated - journal_id: should be selected based on type: simply put the 'type' in the context when calling create(), will be selected correctly - payment_term: if set, create a default one based on name... - 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. - 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 on the tax config of the DB where it is imported. """ if context is None: context = {} self._edi_requires_attributes(('company_id','company_address','type','invoice_line','currency'), edi_document) # extract currency info res_currency = self.pool.get('res.currency') currency_info = edi_document.pop('currency') currency_id = res_currency.edi_import(cr, uid, currency_info, context=context) currency = res_currency.browse(cr, uid, currency_id) edi_document['currency_id'] = self.edi_m2o(cr, uid, currency, context=context) # change type: out_invoice'<->'in_invoice','out_refund'<->'in_refund' invoice_type = edi_document['type'] invoice_type = invoice_type.startswith('in_') and invoice_type.replace('in_','out_') or invoice_type.replace('out_','in_') edi_document['type'] = invoice_type #import company as a new partner partner_id = self._edi_import_company(cr, uid, edi_document, context=context) # Set Account invoice_account = self._edi_invoice_account(cr, uid, partner_id, invoice_type, context=context) edi_document['account_id'] = invoice_account and self.edi_m2o(cr, uid, invoice_account, context=context) or False # reference: should contain the value of the 'internal_number' edi_document['reference'] = edi_document.get('internal_number', False) # reference_type: 'none' edi_document['reference_type'] = 'none' # internal number: reset to False, auto-generated edi_document['internal_number'] = False # discard web preview fields, if present edi_document.pop('partner_ref', None) # journal_id: should be selected based on type: simply put the 'type' in the context when calling create(), will be selected correctly context.update(type=invoice_type) # 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. for edi_invoice_line in edi_document['invoice_line']: product_info = edi_invoice_line['product_id'] product_id = self.edi_import_relation(cr, uid, 'product.product', product_info[1], product_info[0], context=context) account = self._edi_product_account(cr, uid, product_id, invoice_type, context=context) # TODO: could be improved with fiscal positions perhaps # account = fpos_obj.map_account(cr, uid, fiscal_position_id, account.id) edi_invoice_line['account_id'] = self.edi_m2o(cr, uid, account, context=context) if account else False # discard web preview fields, if present edi_invoice_line.pop('price_subtotal', None) # 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 # on the tax config of the DB where it is imported. tax_account = self._edi_tax_account(cr, uid, context=context) tax_account_info = self.edi_m2o(cr, uid, tax_account, context=context) for edi_tax_line in edi_document.get('tax_line', []): edi_tax_line['account_id'] = tax_account_info edi_tax_line['manual'] = True return super(account_invoice,self).edi_import(cr, uid, edi_document, context=context) def _edi_record_display_action(self, cr, uid, id, context=None): """Returns an appropriate action definition dict for displaying the record with ID ``rec_id``. :param int id: database ID of record to display :return: action definition dict """ action = super(account_invoice,self)._edi_record_display_action(cr, uid, id, context=context) try: invoice = self.browse(cr, uid, id, context=context) if 'out_' in invoice.type: view_ext_id = 'invoice_form' journal_type = 'sale' else: view_ext_id = 'invoice_supplier_form' journal_type = 'purchase' ctx = "{'type': '%s', 'journal_type': '%s'}" % (invoice.type, journal_type) action.update(context=ctx) view_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account', view_ext_id)[1] action.update(views=[(view_id,'form'), (False, 'tree')]) except ValueError: # ignore if views are missing pass return action class account_invoice_line(osv.osv, EDIMixin): _inherit='account.invoice.line' class account_invoice_tax(osv.osv, EDIMixin): _inherit = "account.invoice.tax" # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: