trytond-account_move_import/move.py

248 lines
7.9 KiB
Python

# The COPYRIGHT file at the top level of this repository contains the full
# copyright notices and license terms.
import logging
from decimal import Decimal
import datetime
from trytond.transaction import Transaction
from trytond.pool import Pool
from trytond.model import fields, ModelSQL, ModelView, Workflow, Unique
from trytond.pyson import Eval
__all__ = ['AccountMoveImport', 'AccountMoveImportLine']
_logger = logging.getLogger(__name__)
class AccountMoveImport(Workflow, ModelSQL, ModelView):
'Account Move Import'
__name__ = 'account.move.import'
name = fields.Char(
'Name',
required=True,
states={
'readonly': Eval('state') == 'done',
},
)
journal = fields.Many2One(
'account.journal', 'Journal',
required=True,
states={
'readonly': Eval('state') == 'done',
}
)
date_format = fields.Selection(
[
('%d/%m/%Y', 'DD/MM/YYYY'),
('%d-%m-%Y', 'DD-MM-YYYY'),
],
'Date Format',
sort=False,
required=True,
states={
'readonly': Eval('state') == 'done',
}
)
numeric_format = fields.Selection(
[
('europe', '1.000,00'),
('usa', '1,000.00'),
],
'Numeric Format',
sort=False,
required=True,
states={
'readonly': Eval('state') == 'done',
}
)
lines = fields.One2Many('account.move.import.line', 'account_import',
'Lines', states={
'readonly': Eval('state') == 'done',
})
state = fields.Selection([
('draft', 'Draft'),
('done', 'Done'),
], 'State', required=True, readonly=True)
@classmethod
def __setup__(cls):
super(AccountMoveImport, cls).__setup__()
cls._buttons.update({
'process': {
'readonly': ~Eval('lines') | ~Eval('journal')
| (Eval('state') == 'done')
}
})
cls._error_messages.update({
'incorrect_data': ('Import error: %s'),
'not_found_period': ('Not found period: %s'),
})
cls._transitions |= set((
('draft', 'done'),
))
t = cls.__table__()
cls._sql_constraints.append(
('name_uniq', Unique(t, t.name),
'Move Import name must be unique'),
)
@staticmethod
def default_state():
return 'draft'
@staticmethod
def default_date_format():
return '%d/%m/%Y'
@staticmethod
def default_numeric_format():
return 'europe'
@classmethod
@ModelView.button
@Workflow.transition('done')
def process(cls, records):
pool = Pool()
AccountMove = pool.get('account.move')
Period = pool.get('account.period')
to_create = []
for move_import in records:
to_create_lines = []
default_journal = move_import.journal
current_account_move = None
account_move = None
current_move = None
for line_import in move_import.lines:
# There might nor be a number for the account, then assume
# we are working on the same account move
current_account_move = line_import.account_moves or account_move
if current_account_move != account_move:
account_move = current_account_move
if to_create_lines:
current_move.lines = to_create_lines
to_create.append(current_move)
to_create_lines = []
current_move = AccountMove()
current_move.journal = default_journal
date = move_import.parse_datetime(line_import.date)
period_name = '%s-%02d' % (date.year, date.month)
periods = Period.search([
('name', '=', period_name)], limit=1)
if not periods:
cls.raise_user_error('not_found_period', period_name)
period, = periods
current_move.period = period
to_create_lines.append(
line_import.build_account_move_line()
)
if to_create_lines:
current_move.lines = to_create_lines
to_create.append(current_move)
if to_create:
AccountMove.create([x._save_values for x in to_create])
def parse_datetime(self, date):
""" Converts a formatted date to a datetime object """
if date:
return datetime.datetime.strptime(date, self.date_format)
return None
class AccountMoveImportLine(ModelSQL, ModelView):
'Account Move Import Line'
__name__ = 'account.move.import.line'
account_import = fields.Many2One(
'account.move.import', 'Move Import',
required=True, ondelete='CASCADE')
account_moves = fields.Char('Account Moves')
date = fields.Char('Date')
account = fields.Char('Account', required=True)
party = fields.Char('Party')
debit = fields.Char('Debit')
credit = fields.Char('Credit')
account_description = fields.Char('Account Description')
@classmethod
def __setup__(cls):
super(AccountMoveImportLine, cls).__setup__()
cls._error_messages.update({
'incorrect_data': ('Import error: %s'),
})
@classmethod
def import_data(cls, fields_names, data):
import_id = Transaction().context.get('import_id')
if import_id and 'account_import' not in fields_names:
fields_names, data = cls.preappend_move_import(
fields_names, data, import_id)
return super(AccountMoveImportLine, cls).import_data(
fields_names, data)
@staticmethod
def preappend_move_import(fields_names, data, import_id):
move_import = Pool().get('account.move.import')(import_id)
fields_names = ['account_import'] + fields_names
# This relies on account_import rec_name being unique!
data = [
[move_import.rec_name] + line
for line in data
]
return fields_names, data
def build_account_move_line(self):
"Parses an account.move.line.import object "
"into a new account.move.line instance"
pool = Pool()
AccountMoveLine = pool.get('account.move.line')
account_move_line = AccountMoveLine(
account=self.find_account(self.account),
party=self.find_party(self.party),
date=self.account_import.parse_datetime(self.date),
debit=self.parse_decimal(self.debit),
credit=self.parse_decimal(self.credit),
description=self.account_description,
)
return account_move_line
def find_account(self, account_name):
pool = Pool()
Account = pool.get('account.account')
accounts = Account.search([('code', '=', account_name)], limit=1)
if not accounts:
self.raise_user_error(
'incorrect_data', 'No account named %s' % account_name)
return accounts[0]
def find_party(self, party_name):
if not party_name:
return None
Party = Pool().get('party.party')
parties = Party.search([('name', '=', party_name)], limit=1)
if not parties:
self.raise_user_error(
'incorrect_data', 'No party named %s' % party_name)
return parties[0]
def parse_decimal(self, decimal):
""" Converts a string to Decimal having thousand separators in mind """
if decimal:
if self.account_import.numeric_format == 'europe':
decimal = decimal.replace('.', '').replace(',', '.')
else:
decimal = decimal.replace(',', '')
else:
decimal = '00.00'
return Decimal(decimal)