trytond-account_move_import/move.py

248 lines
7.9 KiB
Python
Raw Permalink Normal View History

2017-03-31 15:55:00 +02:00
# The COPYRIGHT file at the top level of this repository contains the full
# copyright notices and license terms.
import logging
2017-03-31 15:55:00 +02:00
from decimal import Decimal
import datetime
from trytond.transaction import Transaction
2017-03-31 15:55:00 +02:00
from trytond.pool import Pool
from trytond.model import fields, ModelSQL, ModelView, Workflow, Unique
2017-03-31 15:55:00 +02:00
from trytond.pyson import Eval
__all__ = ['AccountMoveImport', 'AccountMoveImportLine']
_logger = logging.getLogger(__name__)
2017-03-31 15:55:00 +02:00
class AccountMoveImport(Workflow, ModelSQL, ModelView):
'Account Move Import'
__name__ = 'account.move.import'
name = fields.Char(
'Name',
required=True,
2017-03-31 15:55:00 +02:00
states={
'readonly': Eval('state') == 'done',
},
)
journal = fields.Many2One(
'account.journal', 'Journal',
required=True,
2017-03-31 15:55:00 +02:00
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',
}
)
2017-03-31 15:55:00 +02:00
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'),
)
2017-03-31 15:55:00 +02:00
@staticmethod
def default_state():
return 'draft'
@staticmethod
def default_date_format():
return '%d/%m/%Y'
@staticmethod
def default_numeric_format():
return 'europe'
2017-03-31 15:55:00 +02:00
@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:
2017-03-31 15:55:00 +02:00
to_create_lines = []
default_journal = move_import.journal
2017-03-31 15:55:00 +02:00
current_account_move = None
account_move = None
current_move = None
for line_import in move_import.lines:
2017-03-31 15:55:00 +02:00
# 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
2017-03-31 15:55:00 +02:00
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
2017-04-04 12:00:05 +02:00
date = move_import.parse_datetime(line_import.date)
2017-03-31 15:55:00 +02:00
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()
)
2017-03-31 17:07:04 +02:00
2017-03-31 15:55:00 +02:00
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 """
2017-03-31 15:55:00 +02:00
if date:
return datetime.datetime.strptime(date, self.date_format)
2017-03-31 15:55:00 +02:00
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')
2017-03-31 15:55:00 +02:00
account_moves = fields.Char('Account Moves')
date = fields.Char('Date')
account = fields.Char('Account', required=True)
2017-03-31 15:55:00 +02:00
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)