Pass Black on the files + pylint

Move test data to tests/data
This commit is contained in:
Tonow-c2c
2019-11-22 09:26:28 +01:00
committed by Florian da Costa
parent 5c8a0962da
commit 5bb8d7caa4
5 changed files with 230 additions and 157 deletions

View File

@@ -12,63 +12,67 @@ from odoo.exceptions import UserError, ValidationError
class AccountJournal(models.Model): class AccountJournal(models.Model):
_name = 'account.journal' _name = "account.journal"
_inherit = ['account.journal', 'mail.thread'] _inherit = ["account.journal", "mail.thread"]
_order = 'sequence' _order = "sequence"
used_for_import = fields.Boolean( used_for_import = fields.Boolean(string="Journal used for import")
string="Journal used for import")
commission_account_id = fields.Many2one( commission_account_id = fields.Many2one(
comodel_name='account.account', comodel_name="account.account", string="Commission account"
string='Commission account') )
import_type = fields.Selection( import_type = fields.Selection(
[('generic_csvxls_so', 'Generic .csv/.xls based on SO Name')], [("generic_csvxls_so", "Generic .csv/.xls based on SO Name")],
string='Type of import', string="Type of import",
default='generic_csvxls_so', default="generic_csvxls_so",
required=True, required=True,
help="Choose here the method by which you want to import account " help="Choose here the method by which you want to import account "
"moves for this journal.") "moves for this journal.",
)
last_import_date = fields.Datetime( last_import_date = fields.Datetime(string="Last Import Date")
string="Last Import Date")
partner_id = fields.Many2one( partner_id = fields.Many2one(
comodel_name='res.partner', comodel_name="res.partner",
string='Bank/Payment Office partner', string="Bank/Payment Office partner",
help="Put a partner if you want to have it on the commission move " help="Put a partner if you want to have it on the commission move "
"(and optionaly on the counterpart of the intermediate/" "(and optionaly on the counterpart of the intermediate/"
"banking move if you tick the corresponding checkbox).") "banking move if you tick the corresponding checkbox).",
)
receivable_account_id = fields.Many2one( receivable_account_id = fields.Many2one(
comodel_name='account.account', comodel_name="account.account",
string='Receivable/Payable Account', string="Receivable/Payable Account",
help="Choose a receivable/payable account to use as the default " help="Choose a receivable/payable account to use as the default "
"debit/credit account.") "debit/credit account.",
)
used_for_completion = fields.Boolean( used_for_completion = fields.Boolean(string="Journal used for completion")
string="Journal used for completion")
rule_ids = fields.Many2many( rule_ids = fields.Many2many(
comodel_name='account.move.completion.rule', comodel_name="account.move.completion.rule",
string='Auto-completion rules', string="Auto-completion rules",
relation='account_journal_completion_rule_rel') relation="account_journal_completion_rule_rel",
)
launch_import_completion = fields.Boolean( launch_import_completion = fields.Boolean(
string="Launch completion after import", string="Launch completion after import",
help="Tic that box to automatically launch the completion " help="Tic that box to automatically launch the completion "
"on each imported file using this journal.") "on each imported file using this journal.",
)
create_counterpart = fields.Boolean( create_counterpart = fields.Boolean(
string="Create Counterpart", string="Create Counterpart",
help="Tick that box to automatically create the move counterpart", help="Tick that box to automatically create the move counterpart",
default=True) default=True,
)
split_counterpart = fields.Boolean( split_counterpart = fields.Boolean(
string="Split Counterpart", string="Split Counterpart",
help="Two counterparts will be automatically created : one for " help="Two counterparts will be automatically created : one for "
"the refunds and one for the payments") "the refunds and one for the payments",
)
@api.multi @api.multi
def _prepare_counterpart_line(self, move, amount, date): def _prepare_counterpart_line(self, move, amount, date):
@@ -81,24 +85,24 @@ class AccountJournal(models.Model):
credit = -amount credit = -amount
debit = 0.0 debit = 0.0
counterpart_values = { counterpart_values = {
'date_maturity': date, "date_maturity": date,
'credit': credit, "credit": credit,
'debit': debit, "debit": debit,
'partner_id': self.partner_id.id, "partner_id": self.partner_id.id,
'move_id': move.id, "move_id": move.id,
'account_id': account_id, "account_id": account_id,
'already_completed': True, "already_completed": True,
'journal_id': self.id, "journal_id": self.id,
'company_id': self.company_id.id, "company_id": self.company_id.id,
'currency_id': self.currency_id.id, "currency_id": self.currency_id.id,
'company_currency_id': self.company_id.currency_id.id, "company_currency_id": self.company_id.currency_id.id,
'amount_residual': amount, "amount_residual": amount,
} }
return counterpart_values return counterpart_values
@api.multi @api.multi
def _create_counterpart(self, parser, move): def _create_counterpart(self, parser, move):
move_line_obj = self.env['account.move.line'] move_line_obj = self.env["account.move.line"]
refund = 0.0 refund = 0.0
payment = 0.0 payment = 0.0
transfer_lines = [] transfer_lines = []
@@ -114,16 +118,18 @@ class AccountJournal(models.Model):
total_amount = refund + payment total_amount = refund + payment
if total_amount: if total_amount:
transfer_lines.append(total_amount) transfer_lines.append(total_amount)
counterpart_date = parser.get_move_vals().get('date') or \ counterpart_date = (
fields.Date.today() parser.get_move_vals().get("date") or fields.Date.today()
)
transfer_line_count = len(transfer_lines) transfer_line_count = len(transfer_lines)
check_move_validity = False check_move_validity = False
for amount in transfer_lines: for amount in transfer_lines:
transfer_line_count -= 1 transfer_line_count -= 1
if not transfer_line_count: if not transfer_line_count:
check_move_validity = True check_move_validity = True
vals = self._prepare_counterpart_line(move, amount, vals = self._prepare_counterpart_line(
counterpart_date) move, amount, counterpart_date
)
move_line_obj.with_context( move_line_obj.with_context(
check_move_validity=check_move_validity check_move_validity=check_move_validity
).create(vals) ).create(vals)
@@ -142,48 +148,53 @@ class AccountJournal(models.Model):
statement ID statement ID
:param: context: global context :param: context: global context
""" """
move_line_obj = self.env['account.move.line'] move_line_obj = self.env["account.move.line"]
global_commission_amount = 0 global_commission_amount = 0
for row in parser.result_row_list: for row in parser.result_row_list:
global_commission_amount += float( global_commission_amount += float(
row.get('commission_amount', '0.0')) row.get("commission_amount", "0.0")
)
partner_id = self.partner_id.id partner_id = self.partner_id.id
# Commission line # Commission line
if global_commission_amount > 0.0: if global_commission_amount > 0.0:
raise UserError(_('Commission amount should not be positive.')) raise UserError(_("Commission amount should not be positive."))
elif global_commission_amount < 0.0: elif global_commission_amount < 0.0:
if not self.commission_account_id: if not self.commission_account_id:
raise UserError( raise UserError(
_('No commission account is set on the journal.')) _("No commission account is set on the journal.")
)
else: else:
commission_account_id = self.commission_account_id.id commission_account_id = self.commission_account_id.id
comm_values = { comm_values = {
'name': _('Commission line'), "name": _("Commission line"),
'date_maturity': parser.get_move_vals().get('date') or "date_maturity": (
fields.Date.today(), parser.get_move_vals().get("date")
'debit': -global_commission_amount, or fields.Date.today()
'partner_id': partner_id, ),
'move_id': move.id, "debit": -global_commission_amount,
'account_id': commission_account_id, "partner_id": partner_id,
'already_completed': True, "move_id": move.id,
"account_id": commission_account_id,
"already_completed": True,
} }
if (self.currency_id and if (
self.currency_id != self.company_id.currency_id): self.currency_id
and self.currency_id != self.company_id.currency_id
):
# the commission we are reading is in the currency of the # the commission we are reading is in the currency of the
# journal: use the amount in the amount_currency field, and # journal: use the amount in the amount_currency field, and
# set credit / debit to the value in company currency at # set credit / debit to the value in company currency at
# the date of the move. # the date of the move.
currency = self.currency_id.with_context(date=move.date) currency = self.currency_id.with_context(date=move.date)
company_currency = self.company_id.currency_id company_currency = self.company_id.currency_id
comm_values['amount_currency'] = comm_values['debit'] comm_values["amount_currency"] = comm_values["debit"]
comm_values['debit'] = currency.compute( comm_values["debit"] = currency.compute(
comm_values['debit'], comm_values["debit"], company_currency
company_currency
) )
comm_values['currency_id'] = currency.id comm_values["currency_id"] = currency.id
move_line_obj.with_context( move_line_obj.with_context(check_move_validity=False).create(
check_move_validity=False comm_values
).create(comm_values) )
@api.multi @api.multi
def write_logs_after_import(self, move, num_lines): def write_logs_after_import(self, move, num_lines):
@@ -195,8 +206,9 @@ class AccountJournal(models.Model):
:return: True :return: True
""" """
self.message_post( self.message_post(
body=_('Move %s have been imported with %s ' body=_("Move %s have been imported with %s " "lines.")
'lines.') % (move.name, num_lines)) % (move.name, num_lines)
)
return True return True
def prepare_move_line_vals(self, parser_vals, move): def prepare_move_line_vals(self, parser_vals, move):
@@ -211,40 +223,46 @@ class AccountJournal(models.Model):
:return: dict of vals that will be passed to create method of :return: dict of vals that will be passed to create method of
statement line. statement line.
""" """
move_line_obj = self.env['account.move.line'] move_line_obj = self.env["account.move.line"]
values = parser_vals values = parser_vals
if not values.get('account_id', False): if not values.get("account_id", False):
values['account_id'] = self.receivable_account_id.id values["account_id"] = self.receivable_account_id.id
account = self.env['account.account'].browse(values['account_id']) account = self.env["account.account"].browse(values["account_id"])
if (self.currency_id and if (
self.currency_id != self.company_id.currency_id): self.currency_id
and self.currency_id != self.company_id.currency_id
):
# the debit and credit we are reading are in the currency of the # the debit and credit we are reading are in the currency of the
# journal: use the amount in the amount_currency field, and set # journal: use the amount in the amount_currency field, and set
# credit / debit to the value in company currency at the date of # credit / debit to the value in company currency at the date of
# the move. # the move.
currency = self.currency_id.with_context(date=move.date) currency = self.currency_id.with_context(date=move.date)
company_currency = self.company_id.currency_id company_currency = self.company_id.currency_id
values['amount_currency'] = values['debit'] - values['credit'] values["amount_currency"] = values["debit"] - values["credit"]
values['debit'] = currency.compute(values['debit'], values["debit"] = currency.compute(
company_currency) values["debit"], company_currency
values['credit'] = currency.compute(values['credit'], )
company_currency) values["credit"] = currency.compute(
values["credit"], company_currency
)
if account.reconcile: if account.reconcile:
values['amount_residual'] = values['debit'] - values['credit'] values["amount_residual"] = values["debit"] - values["credit"]
else: else:
values['amount_residual'] = 0 values["amount_residual"] = 0
values.update({ values.update(
'company_id': self.company_id.id, {
'currency_id': self.currency_id.id, "company_id": self.company_id.id,
'company_currency_id': self.company_id.currency_id.id, "currency_id": self.currency_id.id,
'journal_id': self.id, "company_currency_id": self.company_id.currency_id.id,
'move_id': move.id, "journal_id": self.id,
'date': move.date, "move_id": move.id,
'balance': values['debit'] - values['credit'], "date": move.date,
'amount_residual_currency': 0, "balance": values["debit"] - values["credit"],
'user_type_id': account.user_type_id.id, "amount_residual_currency": 0,
'reconciled': False, "user_type_id": account.user_type_id.id,
}) "reconciled": False,
}
)
values = move_line_obj._add_missing_default_values(values) values = move_line_obj._add_missing_default_values(values)
return values return values
@@ -252,9 +270,11 @@ class AccountJournal(models.Model):
"""Hook to build the values of the statement from the parser and """Hook to build the values of the statement from the parser and
the profile. the profile.
""" """
vals = {'journal_id': self.id, vals = {
'currency_id': self.currency_id.id, "journal_id": self.id,
'import_partner_id': self.partner_id.id} "currency_id": self.currency_id.id,
"import_partner_id": self.partner_id.id,
}
vals.update(parser.get_move_vals()) vals.update(parser.get_move_vals())
return vals return vals
@@ -267,23 +287,23 @@ class AccountJournal(models.Model):
:param char: ftype represent the file extension (csv by default) :param char: ftype represent the file extension (csv by default)
:return: list: list of ids of the created account.bank.statement :return: list: list of ids of the created account.bank.statement
""" """
filename = self._context.get('file_name', None) filename = self._context.get("file_name", None)
if filename: if filename:
(filename, __) = os.path.splitext(filename) (filename, __) = os.path.splitext(filename)
parser = new_move_parser(self, ftype=ftype, move_ref=filename) parser = new_move_parser(self, ftype=ftype, move_ref=filename)
res = self.env['account.move'] res = self.env["account.move"]
for result_row_list in parser.parse(file_stream): for result_row_list in parser.parse(file_stream):
move = self._move_import( move = self._move_import(
parser, file_stream, parser,
file_stream,
result_row_list=result_row_list, result_row_list=result_row_list,
ftype=ftype ftype=ftype,
) )
res |= move res |= move
return res return res
def _move_import( def _move_import(
self, parser, file_stream, result_row_list=None, ftype="csv" self, parser, file_stream, result_row_list=None, ftype="csv"):
):
"""Create a bank statement with the given profile and parser. It will """Create a bank statement with the given profile and parser. It will
fulfill the bank statement with the values of the file provided, but fulfill the bank statement with the values of the file provided, but
will not complete data (like finding the partner, or the right will not complete data (like finding the partner, or the right
@@ -295,23 +315,26 @@ class AccountJournal(models.Model):
:param char: ftype represent the file extension (csv by default) :param char: ftype represent the file extension (csv by default)
:return: ID of the created account.bank.statement :return: ID of the created account.bank.statement
""" """
move_obj = self.env['account.move'] move_obj = self.env["account.move"]
move_line_obj = self.env['account.move.line'] move_line_obj = self.env["account.move.line"]
attachment_obj = self.env['ir.attachment'] attachment_obj = self.env["ir.attachment"]
if result_row_list is None: if result_row_list is None:
result_row_list = parser.result_row_list result_row_list = parser.result_row_list
# Check all key are present in account.bank.statement.line!! # Check all key are present in account.bank.statement.line!!
if not result_row_list: if not result_row_list:
raise UserError(_("Nothing to import: " raise UserError(_("Nothing to import: " "The file is empty"))
"The file is empty"))
parsed_cols = list( parsed_cols = list(
parser.get_move_line_vals(result_row_list[0]).keys() parser.get_move_line_vals(result_row_list[0]).keys()
) )
for col in parsed_cols: for col in parsed_cols:
if col not in move_line_obj._fields: if col not in move_line_obj._fields:
raise UserError( raise UserError(
_("Missing column! Column %s you try to import is not " _(
"present in the move line!") % col) "Missing column! Column %s you try to import is not "
"present in the move line!"
)
% col
)
move_vals = self.prepare_move_vals(result_row_list, parser) move_vals = self.prepare_move_vals(result_row_list, parser)
move = move_obj.create(move_vals) move = move_obj.create(move_vals)
try: try:
@@ -333,11 +356,11 @@ class AccountJournal(models.Model):
move._amount_compute() move._amount_compute()
# Attach data to the move # Attach data to the move
attachment_data = { attachment_data = {
'name': 'statement file', "name": "statement file",
'datas': file_stream, "datas": file_stream,
'datas_fname': "%s.%s" % (fields.Date.today(), ftype), "datas_fname": "%s.%s" % (fields.Date.today(), ftype),
'res_model': 'account.move', "res_model": "account.move",
'res_id': move.id, "res_id": move.id,
} }
attachment_obj.create(attachment_data) attachment_obj.create(attachment_data)
# If user ask to launch completion at end of import, do it! # If user ask to launch completion at end of import, do it!
@@ -351,9 +374,15 @@ class AccountJournal(models.Model):
except Exception: except Exception:
error_type, error_value, trbk = sys.exc_info() error_type, error_value, trbk = sys.exc_info()
st = "Error: %s\nDescription: %s\nTraceback:" % ( st = "Error: %s\nDescription: %s\nTraceback:" % (
error_type.__name__, error_value) error_type.__name__,
st += ''.join(traceback.format_tb(trbk, 30)) error_value,
)
st += "".join(traceback.format_tb(trbk, 30))
raise ValidationError( raise ValidationError(
_("Statement import error " _(
"The statement cannot be created: %s") % st) "Statement import error "
"The statement cannot be created: %s"
)
% st
)
return move return move

View File

@@ -32,8 +32,15 @@ class FileParser(AccountMoveImportParser):
format. format.
""" """
def __init__(self, journal, ftype='csv', extra_fields=None, header=None, def __init__(
dialect=None, move_ref=None, **kwargs): self,
journal,
ftype="csv",
extra_fields=None,
header=None,
dialect=None,
move_ref=None,
**kwargs):
""" """
:param char: parse_name: The name of the parser :param char: parse_name: The name of the parser
:param char: ftype: extension of the file (could be csv, xls or :param char: ftype: extension of the file (could be csv, xls or
@@ -44,11 +51,12 @@ class FileParser(AccountMoveImportParser):
header header
""" """
super().__init__(journal, **kwargs) super().__init__(journal, **kwargs)
if ftype in ('csv', 'xls', 'xlsx'): if ftype in ("csv", "xls", "xlsx"):
self.ftype = ftype[0:3] self.ftype = ftype[0:3]
else: else:
raise UserError( raise UserError(
_('Invalid file type %s. Please use csv, xls or xlsx') % ftype) _("Invalid file type %s. Please use csv, xls or xlsx") % ftype
)
self.conversion_dict = extra_fields self.conversion_dict = extra_fields
self.keys_to_validate = list(self.conversion_dict.keys()) self.keys_to_validate = list(self.conversion_dict.keys())
self.fieldnames = header self.fieldnames = header
@@ -73,7 +81,7 @@ class FileParser(AccountMoveImportParser):
given ftype given ftype
""" """
if self.parsed_file is None: if self.parsed_file is None:
if self.ftype == 'csv': if self.ftype == "csv":
self.parsed_file = self._parse_csv() self.parsed_file = self._parse_csv()
else: else:
self.parsed_file = self._parse_xls() self.parsed_file = self._parse_xls()
@@ -81,9 +89,8 @@ class FileParser(AccountMoveImportParser):
if len(self.parsed_file) <= self.current_line: if len(self.parsed_file) <= self.current_line:
return False return False
else: else:
print(self.current_line)
self.result_row_list = self.parsed_file[ self.result_row_list = self.parsed_file[
self.current_line:self.current_line + 1 self.current_line: self.current_line + 1
] ]
self.current_line += 1 self.current_line += 1
return True return True
@@ -101,7 +108,7 @@ class FileParser(AccountMoveImportParser):
parsed_cols = list(self.result_row_list[0].keys()) parsed_cols = list(self.result_row_list[0].keys())
for col in self.keys_to_validate: for col in self.keys_to_validate:
if col not in parsed_cols: if col not in parsed_cols:
raise UserError(_('Column %s not present in file') % col) raise UserError(_("Column %s not present in file") % col)
return True return True
def _post(self, *args, **kwargs): def _post(self, *args, **kwargs):
@@ -115,9 +122,10 @@ class FileParser(AccountMoveImportParser):
csv_file = tempfile.NamedTemporaryFile() csv_file = tempfile.NamedTemporaryFile()
csv_file.write(self.filebuffer) csv_file.write(self.filebuffer)
csv_file.flush() csv_file.flush()
with open(csv_file.name, 'rU') as fobj: with open(csv_file.name, "rU") as fobj:
reader = UnicodeDictReader(fobj, fieldnames=self.fieldnames, reader = UnicodeDictReader(
dialect=self.dialect) fobj, fieldnames=self.fieldnames, dialect=self.dialect
)
return list(reader) return list(reader)
def _parse_xls(self): def _parse_xls(self):
@@ -143,26 +151,41 @@ class FileParser(AccountMoveImportParser):
for rule in conversion_rules: for rule in conversion_rules:
if conversion_rules[rule] == datetime.datetime: if conversion_rules[rule] == datetime.datetime:
try: try:
date_string = line[rule].split(' ')[0] date_string = line[rule].split(" ")[0]
line[rule] = datetime.datetime.strptime(date_string, line[rule] = datetime.datetime.strptime(
'%Y-%m-%d') date_string, "%Y-%m-%d"
)
except ValueError as err: except ValueError as err:
raise UserError( raise UserError(
_("Date format is not valid." _(
" It should be YYYY-MM-DD for column: %s" "Date format is not valid."
" value: %s \n \n \n Please check the line with " " It should be YYYY-MM-DD for column: %s"
"ref: %s \n \n Detail: %s") % " value: %s \n \n \n Please check"
(rule, line.get(rule, _('Missing')), " the line with ref: %s \n \n Detail: %s"
line.get('ref', line), repr(err))) )
% (
rule,
line.get(rule, _("Missing")),
line.get("ref", line),
repr(err),
)
)
else: else:
try: try:
line[rule] = conversion_rules[rule](line[rule]) line[rule] = conversion_rules[rule](line[rule])
except Exception as err: except Exception as err:
raise UserError( raise UserError(
_("Value %s of column %s is not valid.\n Please " _(
"check the line with ref %s:\n \n Detail: %s") % "Value %s of column %s is not valid.\n Please "
(line.get(rule, _('Missing')), rule, "check the line with ref %s:\n \n Detail: %s"
line.get('ref', line), repr(err))) )
% (
line.get(rule, _("Missing")),
rule,
line.get("ref", line),
repr(err),
)
)
return result_set return result_set
def _from_xls(self, result_set, conversion_rules): def _from_xls(self, result_set, conversion_rules):
@@ -173,26 +196,41 @@ class FileParser(AccountMoveImportParser):
for rule in conversion_rules: for rule in conversion_rules:
if conversion_rules[rule] == datetime.datetime: if conversion_rules[rule] == datetime.datetime:
try: try:
t_tuple = xlrd.xldate_as_tuple(line[rule], t_tuple = xlrd.xldate_as_tuple(
self._datemode) line[rule], self._datemode
)
line[rule] = datetime.datetime(*t_tuple) line[rule] = datetime.datetime(*t_tuple)
except Exception as err: except Exception as err:
raise UserError( raise UserError(
_("Date format is not valid. " _(
"Please modify the cell formatting to date " "Date format is not valid. "
"format for column: %s value: %s\n Please check " "Please modify the cell formatting to date "
"the line with ref: %s\n \n Detail: %s") % "format for column: %s value: %s\n Please"
(rule, line.get(rule, _('Missing')), " check the line with ref: %s\n \n Detail: %s"
line.get('ref', line), repr(err))) )
% (
rule,
line.get(rule, _("Missing")),
line.get("ref", line),
repr(err),
)
)
else: else:
try: try:
line[rule] = conversion_rules[rule](line[rule]) line[rule] = conversion_rules[rule](line[rule])
except Exception as err: except Exception as err:
raise UserError( raise UserError(
_("Value %s of column %s is not valid.\n Please " _(
"check the line with ref %s:\n \n Detail: %s") % "Value %s of column %s is not valid.\n Please "
(line.get(rule, _('Missing')), rule, "check the line with ref %s:\n \n Detail: %s"
line.get('ref', line), repr(err))) )
% (
line.get(rule, _("Missing")),
rule,
line.get("ref", line),
repr(err),
)
)
return result_set return result_set
def _cast_rows(self, *args, **kwargs): def _cast_rows(self, *args, **kwargs):
@@ -200,6 +238,6 @@ class FileParser(AccountMoveImportParser):
providen. We call here _from_xls or _from_csv depending on the providen. We call here _from_xls or _from_csv depending on the
self.ftype variable. self.ftype variable.
""" """
func = getattr(self, '_from_%s' % self.ftype) func = getattr(self, "_from_%s" % self.ftype)
res = func(self.result_row_list, self.conversion_dict) res = func(self.result_row_list, self.conversion_dict)
return res return res

View File

@@ -53,7 +53,10 @@ class TestCodaImport(common.TransactionCase):
"""Test import from xls """Test import from xls
""" """
file_name = get_resource_path( file_name = get_resource_path(
'account_move_base_import', 'demo', 'statement.xls' 'account_move_base_import',
'tests',
'data',
'statement.xls'
) )
move = self._import_file(file_name) move = self._import_file(file_name)
self._validate_imported_move(move) self._validate_imported_move(move)
@@ -62,7 +65,10 @@ class TestCodaImport(common.TransactionCase):
"""Test import from csv """Test import from csv
""" """
file_name = get_resource_path( file_name = get_resource_path(
'account_move_base_import', 'demo', 'statement.csv' 'account_move_base_import',
'tests',
'data',
'statement.csv'
) )
move = self._import_file(file_name) move = self._import_file(file_name)
self._validate_imported_move(move) self._validate_imported_move(move)