You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

116 lines
4.4 KiB

  1. # -*- coding: utf-8 -*-
  2. # Copyright 2015 Odoo S. A.
  3. # Copyright 2015 Laurent Mignon <laurent.mignon@acsone.eu>
  4. # Copyright 2015 Ronald Portier <rportier@therp.nl>
  5. # Copyright 2016 Pedro M. Baeza <pedro.baeza@tecnativa.com>
  6. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
  7. import StringIO
  8. import dateutil.parser
  9. from odoo.tools.translate import _
  10. from odoo import api, models
  11. from odoo.exceptions import UserError
  12. import logging
  13. _logger = logging.getLogger(__name__)
  14. try:
  15. import chardet
  16. except (ImportError, IOError) as err:
  17. chardet = False
  18. _logger.debug(err)
  19. class AccountBankStatementImport(models.TransientModel):
  20. _inherit = "account.bank.statement.import"
  21. @api.model
  22. def _check_qif(self, data_file):
  23. return data_file.strip().startswith('!Type:')
  24. def _get_qif_encoding(self, data_file):
  25. if chardet:
  26. return chardet.detect(data_file)['encoding']
  27. else:
  28. return u'utf-8'
  29. def _parse_qif_date(self, date_str):
  30. return dateutil.parser.parse(date_str, fuzzy=True).date()
  31. def _parse_file(self, data_file):
  32. if not self._check_qif(data_file):
  33. return super(AccountBankStatementImport, self)._parse_file(
  34. data_file)
  35. encoding = self._get_qif_encoding(data_file)
  36. data_file = data_file.decode(encoding)
  37. try:
  38. file_data = ""
  39. for line in StringIO.StringIO(data_file).readlines():
  40. file_data += line
  41. if '\r' in file_data:
  42. data_list = file_data.split('\r')
  43. else:
  44. data_list = file_data.split('\n')
  45. header = data_list[0].strip()
  46. header = header.split(":")[1]
  47. except:
  48. raise UserError(_('Could not decipher the QIF file.'))
  49. transactions = []
  50. vals_line = {}
  51. total = 0
  52. if header in ("Bank", "CCard"):
  53. vals_bank_statement = {}
  54. for line in data_list:
  55. line = line.strip()
  56. if not line:
  57. continue
  58. if line[0] == 'D': # date of transaction
  59. vals_line['date'] = self._parse_qif_date(line[1:])
  60. elif line[0] == 'T': # Total amount
  61. total += float(line[1:].replace(',', ''))
  62. vals_line['amount'] = float(line[1:].replace(',', ''))
  63. elif line[0] == 'N': # Check number
  64. vals_line['ref'] = line[1:]
  65. elif line[0] == 'P': # Payee
  66. vals_line['name'] = (
  67. 'name' in vals_line and
  68. line[1:] + ': ' + vals_line['name'] or line[1:]
  69. )
  70. elif line[0] == 'M': # Memo
  71. vals_line['name'] = ('name' in vals_line and
  72. vals_line['name'] + ': ' + line[1:] or
  73. line[1:])
  74. elif line[0] == '^': # end of item
  75. transactions.append(vals_line)
  76. vals_line = {}
  77. elif line[0] == '\n':
  78. transactions = []
  79. else:
  80. pass
  81. else:
  82. raise UserError(_('This file is either not a bank statement or is '
  83. 'not correctly formed.'))
  84. vals_bank_statement.update({
  85. 'balance_end_real': total,
  86. 'transactions': transactions
  87. })
  88. return None, None, [vals_bank_statement]
  89. def _complete_stmts_vals(self, stmt_vals, journal_id, account_number):
  90. """Match partner_id if hasn't been deducted yet."""
  91. res = super(AccountBankStatementImport, self)._complete_stmts_vals(
  92. stmt_vals, journal_id, account_number,
  93. )
  94. # Since QIF doesn't provide account numbers (normal behaviour is to
  95. # provide 'account_number', which the generic module uses to find
  96. # the partner), we have to find res.partner through the name
  97. partner_obj = self.env['res.partner']
  98. for statement in res:
  99. for line_vals in statement['transactions']:
  100. if not line_vals.get('partner_id') and line_vals.get('name'):
  101. partner = partner_obj.search(
  102. [('name', 'ilike', line_vals['name'])], limit=1,
  103. )
  104. line_vals['partner_id'] = partner.id
  105. return res