From 0287452fe8023db85f509fdb88d27d1d5d08b0e3 Mon Sep 17 00:00:00 2001 From: Frederik Jaeckel Date: Thu, 1 Sep 2022 14:48:04 +0200 Subject: [PATCH] import der transaktionen begonnen --- __init__.py | 2 + book.py | 52 +++++++++ locale/de.po | 52 +++++++-- locale/en.po | 72 ++++++++++++ message.xml | 22 +++- qif_import_wiz.py | 81 ++++++++++++- qif_import_wiz.xml | 7 ++ qiftool.py | 186 ++++++++++++++++++++++++++++++ tests/test_category.py | 173 ++++++++++++++++++++++++++- view/wiz_qifimport_info_form.xml | 3 + view/wiz_qifimport_start_form.xml | 2 + 11 files changed, 637 insertions(+), 15 deletions(-) create mode 100644 book.py diff --git a/__init__.py b/__init__.py index d703d42..ed4330c 100644 --- a/__init__.py +++ b/__init__.py @@ -5,6 +5,7 @@ from trytond.pool import Pool from .category import Category +from .book import Book from .qiftool import QifTool from .qif_import_wiz import ImportQifWizard, ImportQifWizardStart, ImportQifWizardInfo from .qif_export import QifCategoryExport @@ -13,6 +14,7 @@ def register(): Pool.register( QifTool, Category, + Book, ImportQifWizardStart, ImportQifWizardInfo, module='cashbook_dataexchange', type_='model') diff --git a/book.py b/book.py new file mode 100644 index 0000000..261cb64 --- /dev/null +++ b/book.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +# This file is part of the cashbook-module from m-ds for Tryton. +# The COPYRIGHT file at the top level of this repository contains the +# full copyright notices and license terms. + +from trytond.transaction import Transaction +from trytond.pool import Pool, PoolMeta + + +class Book(metaclass=PoolMeta): + __name__ = 'cashbook.book' + + # ~ @classmethod + # ~ def export_as_qif(cls): + # ~ """ export all transactions as QIF + # ~ """ + # ~ pool = Pool() + # ~ Category2 = pool.get('cashbook.category') + # ~ QifTool = pool.get('cashbook_dataexchange.qiftool') + + # ~ categories = Category2.search([], + # ~ order=[('cattype', 'ASC'), ('rec_name', 'ASC')]) + + # ~ export = ['!Type:Cat'] + # ~ export.extend([QifTool.qif_export_category(x) for x in categories]) + # ~ return '\n'.join(export) + + @classmethod + def create_from_qif(cls, book, qifdata): + """ add transactions from QIF-File-content + """ + pool = Pool() + QifTool = pool.get('cashbook_dataexchange.qiftool') + Book2 = pool.get('cashbook.book') + + qif_content = QifTool.split_by_type(qifdata) + if not 'Bank' in qif_content.keys(): + return None + + (to_create, msg_list) = QifTool.convert_transactions_to_create( + QifTool.qif_read_transactions(qif_content['Bank']) + ) + if msg_list == []: + Book2.write(*[ + [book], + { + 'lines': [('create', to_create)], + }]) + return [book] + return None + +# end Category diff --git a/locale/de.po b/locale/de.po index 730f94e..124fc7b 100644 --- a/locale/de.po +++ b/locale/de.po @@ -10,6 +10,38 @@ msgctxt "model:ir.message,text:msg_wiz_categories_found" msgid "The following categories are now imported:\n%(categories)s" msgstr "Die folgenden Kategorien werden nun importiert:\n%(categories)s" +msgctxt "model:ir.message,text:msg_wiz_transactions_found" +msgid "The following transactionen are now imported:\nBalance: %(balance)s\nNumber of transactions: %(quantity)s" +msgstr "Die folgenden Transaktionen werden nun importiert:\nSaldo: %(balance)s\nAnzahl Transactionen: %(quantity)s" + +msgctxt "model:ir.message,name:msg_wiz_no_categories" +msgid "No categories were found in the file." +msgstr "In der Datei wurden keine Kategorien gefunden." + +msgctxt "model:ir.message,name:msg_wiz_no_bank" +msgid "No transactions were found in the file." +msgstr "In der Datei wurden keine Transaktionen gefunden." + +msgctxt "model:ir.message,name:mds_import_category_notfound" +msgid "The category '%(catname)s' (Type: %(cattype)s) was not found." +msgstr "Die Kategorie '%(catname)s' (Typ: %(cattype)s) wurde nicht gefunden." + +msgctxt "model:ir.message,name:mds_import_many_categories_found" +msgid "For the category '%(catname1)s' (type: '%(cattype)s') of the import, several categories were found in the system. Use: '%(catname2)s'" +msgstr "Für die Kategorie '%(catname1)s' (Typ: '%(cattype)s') des Imports wurden mehrere Kategorien im System gefunden. Verwende: '%(catname2)s'" + +msgctxt "model:ir.message,name:mds_import_checknumber" +msgid "Cheque No." +msgstr "Scheck-Nr:" + +msgctxt "model:ir.message,name:mds_import_address" +msgid "Address" +msgstr "Adresse" + +msgctxt "model:ir.message,name:msg_wiz_transactions_error" +msgid "When reading the QIF file, there were the following problems:" +msgstr "Beim Einlesen der QIF-Datei gab es folgende Probleme:" + ############# # ir.action # @@ -22,10 +54,6 @@ msgctxt "model:ir.action,name:qif_category_report" msgid "Export QIF-File" msgstr "QIF-Datei exportieren" -msgctxt "model:ir.action,name:msg_wiz_no_categories" -msgid "No categories were found in the file." -msgstr "In der Datei wurden keine Kategorien gefunden." - ##################################### # cashbook_dataexchange.qif_imp_wiz # @@ -47,8 +75,8 @@ msgid "Cancel" msgstr "Abbruch" msgctxt "wizard_button:cashbook_dataexchange.qif_imp_wiz,showinfo,importf:" -msgid "Import Categories" -msgstr "Kategorien importieren" +msgid "Import Data" +msgstr "Daten Kategorien" ########################################### @@ -62,6 +90,10 @@ msgctxt "field:cashbook_dataexchange.qif_imp_wiz.start,company:" msgid "Company" msgstr "Unternehmen" +msgctxt "field:cashbook_dataexchange.qif_imp_wiz.start,book:" +msgid "Cashbook" +msgstr "Kassenbuch" + msgctxt "field:cashbook_dataexchange.qif_imp_wiz.start,file_:" msgid "QIF-File" msgstr "QIF-Datei" @@ -82,10 +114,14 @@ msgctxt "view:cashbook_dataexchange.qif_imp_wiz.info:" msgid "Information" msgstr "Information" -msgctxt "field:cashbook_dataexchange.qif_imp_wiz.start,company:" +msgctxt "field:cashbook_dataexchange.qif_imp_wiz.info,book:" +msgid "Cashbook" +msgstr "Kassenbuch" + +msgctxt "field:cashbook_dataexchange.qif_imp_wiz.info,company:" msgid "Company" msgstr "Unternehmen" -msgctxt "field:cashbook_dataexchange.qif_imp_wiz.start,info:" +msgctxt "field:cashbook_dataexchange.qif_imp_wiz.info,info:" msgid "Information" msgstr "Information" diff --git a/locale/en.po b/locale/en.po index df5fa3e..1bc2211 100644 --- a/locale/en.po +++ b/locale/en.po @@ -2,10 +2,50 @@ msgid "" msgstr "Content-Type: text/plain; charset=utf-8\n" +msgctxt "model:ir.message,text:msg_wiz_categories_found" +msgid "The following categories are now imported:\n%(categories)s" +msgstr "The following categories are now imported:\n%(categories)s" + +msgctxt "model:ir.message,text:msg_wiz_transactions_found" +msgid "The following transactionen are now imported:\nBalance: %(balance)s\nNumber of transactions: %(quantity)s" +msgstr "The following transactionen are now imported:\nBalance: %(balance)s\nNumber of transactions: %(quantity)s" + +msgctxt "model:ir.message,name:msg_wiz_no_categories" +msgid "No categories were found in the file." +msgstr "No categories were found in the file." + +msgctxt "model:ir.message,name:msg_wiz_no_bank" +msgid "No transactions were found in the file." +msgstr "No transactions were found in the file." + +msgctxt "model:ir.message,name:mds_import_category_notfound" +msgid "The category '%(catname)s' (Type: %(cattype)s) was not found." +msgstr "The category '%(catname)s' (Type: %(cattype)s) was not found." + +msgctxt "model:ir.message,name:mds_import_many_categories_found" +msgid "For the category '%(catname1)s' (type: '%(cattype)s') of the import, several categories were found in the system. Use: '%(catname2)s'" +msgstr "For the category '%(catname1)s' (type: '%(cattype)s') of the import, several categories were found in the system. Use: '%(catname2)s'" + +msgctxt "model:ir.message,name:mds_import_checknumber" +msgid "Cheque No." +msgstr "Cheque No." + +msgctxt "model:ir.message,name:mds_import_address" +msgid "Address" +msgstr "Address" + +msgctxt "model:ir.message,name:msg_wiz_transactions_error" +msgid "When reading the QIF file, there were the following problems:" +msgstr "When reading the QIF file, there were the following problems:" + msgctxt "model:ir.action,name:act_import_qif_wizard" msgid "Import QIF-File" msgstr "Import QIF-File" +msgctxt "model:ir.action,name:qif_category_report" +msgid "Export QIF-File" +msgstr "Export QIF-File" + msgctxt "model:cashbook_dataexchange.qif_imp_wiz,name:" msgid "Import QIF-File" msgstr "Import QIF-File" @@ -18,6 +58,14 @@ msgctxt "wizard_button:cashbook_dataexchange.qif_imp_wiz,start,readf:" msgid "Read File" msgstr "Read File" +msgctxt "wizard_button:cashbook_dataexchange.qif_imp_wiz,showinfo,end:" +msgid "Cancel" +msgstr "Cancel" + +msgctxt "wizard_button:cashbook_dataexchange.qif_imp_wiz,showinfo,importf:" +msgid "Import Data" +msgstr "Import Data" + msgctxt "model:cashbook_dataexchange.qif_imp_wiz.start,name:" msgid "Import QIF-File" msgstr "Import QIF-File" @@ -26,7 +74,31 @@ msgctxt "field:cashbook_dataexchange.qif_imp_wiz.start,company:" msgid "Company" msgstr "Company" +msgctxt "field:cashbook_dataexchange.qif_imp_wiz.start,book:" +msgid "Cashbook" +msgstr "Cashbook" + msgctxt "field:cashbook_dataexchange.qif_imp_wiz.start,file_:" msgid "QIF-File" msgstr "QIF-File" +msgctxt "help:cashbook_dataexchange.qif_imp_wiz.start,file_:" +msgid "Quicken Interchange Format" +msgstr "Quicken Interchange Format" + +msgctxt "model:cashbook_dataexchange.qif_imp_wiz.info,name:" +msgid "Import QIF-File" +msgstr "Import QIF-File" + +msgctxt "view:cashbook_dataexchange.qif_imp_wiz.info:" +msgid "Information" +msgstr "Information" + +msgctxt "field:cashbook_dataexchange.qif_imp_wiz.info,book:" +msgid "Cashbook" +msgstr "Cashbook" + +msgctxt "field:cashbook_dataexchange.qif_imp_wiz.info,company:" +msgid "Company" +msgstr "Company" + diff --git a/message.xml b/message.xml index fe4a54e..b03a6a2 100644 --- a/message.xml +++ b/message.xml @@ -8,10 +8,30 @@ full copyright notices and license terms. --> The following categories are now imported:\n%(categories)s - No categories were found in the file. + + No transactions were found in the file. + + + The following transactionen are now imported:\nBalance: %(balance)s\nNumber of transactions: %(quantity)s + + + When reading the QIF file, there were the following problems: + + + The category '%(catname)s' (Type: %(cattype)s) was not found. + + + For the category '%(catname1)s' (type: '%(cattype)s') of the import, several categories were found in the system. Use: '%(catname2)s' + + + Cheque No. + + + Address + diff --git a/qif_import_wiz.py b/qif_import_wiz.py index 0687533..ef50af0 100644 --- a/qif_import_wiz.py +++ b/qif_import_wiz.py @@ -9,6 +9,9 @@ from trytond.model import ModelView, fields from trytond.wizard import Wizard, StateTransition, StateView, Button from trytond.transaction import Transaction from trytond.i18n import gettext +from trytond.pyson import Eval, Bool +from trytond.report import Report +from decimal import Decimal class ImportQifWizardStart(ModelView): @@ -18,6 +21,11 @@ class ImportQifWizardStart(ModelView): company = fields.Many2One(model_name='company.company', string="Company", required=True, states={'invisible': True}) + book = fields.Many2One(string='Cashbook', readonly=True, + model_name='cashbook.book', + states={ + 'invisible': ~Bool(Eval('book')), + }) file_ = fields.Binary(string="QIF-File", required=True, help='Quicken Interchange Format') @@ -35,6 +43,13 @@ class ImportQifWizardInfo(ModelView): company = fields.Many2One(model_name='company.company', string="Company", required=True, states={'invisible': True}) + book = fields.Many2One(string='Cash Book', readonly=True, + model_name='cashbook.book', + states={ + 'invisible': ~Bool(Eval('book')), + }) + allowimport = fields.Boolean(string='Import Enabled', + states={'invisible': True}) info = fields.Text(string='Information', readonly=True) # end ImportQifWizardInfo @@ -55,18 +70,38 @@ class ImportQifWizard(Wizard): view='cashbook_dataexchange.qif_imp_wiz_info_form', \ buttons=[ Button(string='Cancel', state='end', icon='tryton-cancel'), - Button(string='Import Categories', state='importf', icon='tryton-import', default=True), + Button(string='Import Data', state='importf', icon='tryton-import', default=True, + states={ + 'readonly': Eval('allowimport', False) == False, + }), ]) readf = StateTransition() importf = StateTransition() + def default_start(self, fields): + """ show book, company + """ + context = Transaction().context + + values = { + 'company': Transaction().context.get('company'), + 'book': None, + } + + model = context.get('active_model', '') + if model == 'cashbook.book': + values['book'] = context.get('active_id', None) + return values + def default_showinfo(self, fields): """ show import-info """ values = { 'company': self.start.company.id, 'info': getattr(self.showinfo, 'info', None), + 'book': getattr(getattr(self.start, 'book', None), 'id', None), + 'allowimport': getattr(self.showinfo, 'allowimport', False), } return values @@ -74,13 +109,14 @@ class ImportQifWizard(Wizard): """ read file, show number of objects """ pool = Pool() - QitTool = pool.get('cashbook_dataexchange.qiftool') + QifTool = pool.get('cashbook_dataexchange.qiftool') model = Transaction().context.get('active_model', '') file_content = None if isinstance(self.start.file_, bytes): file_content = self.start.file_.decode('utf8') + self.showinfo.allowimport = False if model == 'cashbook.category': def get_catlist(catlist, parent_name=None): """ generate list of categories @@ -97,9 +133,9 @@ class ImportQifWizard(Wizard): return names # read file content, extract categories - qif_content = QitTool.split_by_type(file_content) + qif_content = QifTool.split_by_type(file_content) if 'Cat' in qif_content.keys(): - categories = QitTool.qif_read_categories(qif_content['Cat']) + categories = QifTool.qif_read_categories(qif_content['Cat']) self.showinfo.info = gettext( 'cashbook_dataexchange.msg_wiz_categories_found', categories = '\n'.join( @@ -109,8 +145,40 @@ class ImportQifWizard(Wizard): ['%s (out)' % x for x in get_catlist(categories['out'], None)] ) ) + self.showinfo.allowimport = True else : self.showinfo.info = gettext('cashbook_dataexchange.msg_wiz_no_categories') + elif model == 'cashbook.book': + # read file content, extract categories + qif_content = QifTool.split_by_type(file_content) + if 'Bank' in qif_content.keys(): + (to_create, msg_list) = QifTool.convert_transactions_to_create( + QifTool.qif_read_transactions(qif_content['Bank']) + ) + if len(msg_list) > 0: + short_lst = [] + for x in msg_list: + if x not in short_lst: + short_lst.append(x) + + self.showinfo.info = '%s\n\n%s' % ( + gettext('cashbook_dataexchange.msg_wiz_transactions_error'), + '\n'.join(short_lst), + ) + else : + # count + balance = sum([ + x['amount'] \ + if x['bookingtype'] in ['in', 'spin'] else x['amount'].copy_sign(Decimal('-1.0')) \ + for x in to_create]) + self.showinfo.info = gettext( + 'cashbook_dataexchange.msg_wiz_transactions_found', + quantity = len(to_create), + balance = Report.format_currency(balance, None, self.start.book.currency), + ) + self.showinfo.allowimport = True + else : + self.showinfo.info = gettext('cashbook_dataexchange.msg_wiz_no_bank') return 'showinfo' @@ -119,6 +187,7 @@ class ImportQifWizard(Wizard): """ pool = Pool() Category = pool.get('cashbook.category') + Book = pool.get('cashbook.book') model = Transaction().context.get('active_model', '') file_content = None @@ -128,7 +197,9 @@ class ImportQifWizard(Wizard): if model == 'cashbook.category': if file_content: records = Category.create_from_qif(file_content) - + elif model == 'cashbook.book': + if file_content: + Book.create_from_qif(self.showinfo.book, file_content) return 'end' # end ImportQifWizard diff --git a/qif_import_wiz.xml b/qif_import_wiz.xml index f354c18..cb345e2 100644 --- a/qif_import_wiz.xml +++ b/qif_import_wiz.xml @@ -29,5 +29,12 @@ full copyright notices and license terms. --> + + + form_action + cashbook.book,-1 + + + diff --git a/qiftool.py b/qiftool.py index c00bdd2..29b454d 100644 --- a/qiftool.py +++ b/qiftool.py @@ -5,6 +5,9 @@ from trytond.pool import Pool from trytond.model import Model +from trytond.i18n import gettext +from decimal import Decimal +from datetime import datetime class QifTool(Model): @@ -34,6 +37,189 @@ class QifTool(Model): blocks[block] = '\n'.join(blocks[block]) return blocks + @classmethod + def get_amount_from_txt(cls, amount_txt): + """ convert text to Decimal + """ + if (',' in amount_txt) and (amount_txt[-3] == '.'): + # '.' = decimal, ',' = tousand + amount_txt = amount_txt.replace(',', '.') + elif ('.' in amount_txt) and (amount_txt[-3] == ','): + # ',' = decimal, '.' = tousand + amount_txt = amount_txt.replace('.', '') + amount_txt = amount_txt.replace(',', '.') + elif ',' in amount_txt: + amount_txt = amount_txt.replace(',', '.') + return Decimal(amount_txt) + + @classmethod + def qif_read_transactions(cls, transactiondata): + """ read transactions from text + result: [{ + 'split': [{ + 'amount': , + 'description': 'purpose', + 'category': 'name of categroy', + },...], + 'date': , + 'amount': , + 'party': 'name of party', + 'address': 'address of party', + 'checknumber': 'number', + 'description': 'purpose', + 'state': 'check|edit', + 'account': 'name of cashbook', + 'category': 'name of category', + }, ...] + """ + result = [] + for booktxt in transactiondata.split('^'): + if len(booktxt.strip()) == 0: + continue + + booking = {'split': []} + for line in booktxt.strip().split('\n'): + line_txt = line[1:].strip() + if line.startswith('D'): # date + booking['date'] = datetime.strptime(line_txt, '%d.%m.%Y').date() + elif line.startswith('T'): # total + booking['amount'] = cls.get_amount_from_txt(line_txt) + elif line.startswith('U'): # total + booking['amount'] = cls.get_amount_from_txt(line_txt) + elif line.startswith('P'): # party + booking['party'] = line_txt + elif line.startswith('A'): # address + booking['address'] = line_txt + elif line.startswith('N'): # address + booking['checknumber'] = line_txt + elif line.startswith('M'): # memo + booking['description'] = line_txt + elif line.startswith('C'): # state + booking['state'] = { + 'X': 'check', + '*': 'edit', + }.get(line_txt, 'edit') + elif line.startswith('L'): # category, account + if line_txt.startswith('[') and line_txt.endswith(']'): + booking['account'] = line_txt[1:-1] + else : + booking['category'] = line_txt + elif line.startswith('S'): # split: category + booking['split'].append({ + 'category': line_txt, + }) + elif line.startswith('E'): # split: memo + booking['split'][-1]['description'] = line_txt + elif line.startswith('$'): # split: amount + booking['split'][-1]['amount'] = cls.get_amount_from_txt(line_txt) + elif line.startswith('£'): # split: amount + booking['split'][-1]['amount'] = cls.get_amount_from_txt(line_txt) + else : + raise ValueError('unknown line-code: %s' % (line)) + result.append(booking) + return result + + @classmethod + def get_category_by_name(cls, catname, cattype): + """ find category + """ + Category = Pool().get('cashbook.category') + + cat_id = None + msg_txt = None + categories = Category.search([ + ('cattype', '=', cattype), + ('rec_name', '=', catname.replace(':', '/')), + ]) + if len(categories) == 1: + cat_id = categories[0].id + elif len(categories) == 0: + msg_txt = gettext( + 'cashbook_dataexchange.mds_import_category_notfound', + catname = catname, + cattype = cattype, + ) + else : + msg_txt = gettext( + 'cashbook_dataexchange.mds_import_many_categories_found', + catname1 = catname, + catname2 = categories[0].rec_name, + cattype = cattype, + ) + cat_id = categories[0].id + return (cat_id, msg_txt) + + @classmethod + def convert_transactions_to_create(cls, transactions, split2edit=True): + """ convert read transactions to create-command + split2edit: True = split-bokings are 'edit', False = dont change + """ + to_create = [] + msg_list = [] + for transaction in transactions: + line = {x:transaction[x] for x in [ + 'date', 'amount', 'description', 'state', + ] if x in transaction.keys()} + + if len(transaction['split']) > 0: + if line['amount'] >= Decimal('0.0'): + line['bookingtype'] = 'spin' + else : + line['bookingtype'] = 'spout' + line['amount'] = line['amount'].copy_sign(Decimal('1.0')) + else : + if line['amount'] >= Decimal('0.0'): + line['bookingtype'] = 'in' + else : + line['bookingtype'] = 'out' + line['amount'] = line['amount'].copy_sign(Decimal('1.0')) + + # store 'account' like 'category' + cat_name = transaction.get('category', transaction.get('account', None)) + cat_type = 'in' if line['bookingtype'] in ['in', 'spin'] else 'out' + if cat_name is not None: + (cat_id, msg_txt) = cls.get_category_by_name(cat_name, cat_type) + if cat_id is None: + msg_list.append(msg_txt) + continue + else : + line['category'] = cat_id + if msg_txt is not None: + msg_list.append(msg_txt) + + for x in ['address', 'checknumber']: + if x in transaction.keys(): + line['description'] = ', '.join([ + line.get('description', ''), + '%s %s' % ( + gettext('cashbook_dataexchange.mds_import_%s' % x), + transaction[x] + ), + ]) + + split_lines = [] + for sp_line in transaction['split']: + (cat_id, msg_txt) = cls.get_category_by_name(sp_line['category'], cat_type) + + if msg_txt is not None: + msg_list.append(msg_txt) + + if cat_id is not None: + split_lines.append({ + 'amount': sp_line['amount'].copy_sign(Decimal('1.0')), + 'description': sp_line.get('description', None), + 'category': cat_id, + }) + if len(split_lines) > 0: + line['splitlines'] = [('create', split_lines)] + + if split2edit == True: + if 'splitlines' in line.keys(): + line['state'] = 'edit' + + to_create.append(line) + return (to_create, msg_list) + @classmethod def qif_read_categories(cls, catdata): """ read categories from text diff --git a/tests/test_category.py b/tests/test_category.py index 1487cc9..7cb1b65 100644 --- a/tests/test_category.py +++ b/tests/test_category.py @@ -3,6 +3,8 @@ # The COPYRIGHT file at the top level of this repository contains the # full copyright notices and license terms. +from datetime import date +from decimal import Decimal from trytond.tests.test_tryton import ModuleTestCase, with_transaction from trytond.pool import Pool from trytond.transaction import Transaction @@ -48,7 +50,7 @@ class CategoryTestCase(CashbookTestCase): self.assertEqual(list(result.keys()), ['view']) self.assertEqual(result['view']['defaults']['company'], company.id) self.assertEqual(result['view']['defaults']['info'], -"""The following categories are now imported:\\n +"""The following categories are now imported:\n Gehalt (in) Gehalt/Zulagen (in) @@ -238,6 +240,175 @@ I 'PGA NR00002168 BLZ10000000 0\nL[S-Giro]\n^\nD05.12.2013\nCX\nMsome food\n'+ 'T-56,37\nPFoodshop Zehlendorf\nLLebensmittel\n^\n') + @with_transaction() + def test_qiftool_convert_transactions(self): + """ convert_transactions_to_create + """ + pool = Pool() + QifTool = pool.get('cashbook_dataexchange.qiftool') + Category = pool.get('cashbook.category') + Book = pool.get('cashbook.book') + + company = self.prep_company() + with Transaction().set_context({ + 'company': company.id, + }): + types = self.prep_type() + book, = Book.create([{ + 'name': 'Cash Book', + 'btype': types.id, + 'company': company.id, + 'currency': company.currency.id, + 'number_sequ': self.prep_sequence().id, + 'start_date': date(2010, 1, 1), + 'start_balance': Decimal('0.0'), + }]) + self.assertEqual(book.name, 'Cash Book') + self.assertEqual(book.btype.rec_name, 'CAS - Cash') + + Category.create([{ + 'name': 'Lebensmittel', + 'cattype': 'out', + 'company': company.id, + }, { + 'name': 'Haushaltschemie', + 'cattype': 'out', + 'company': company.id, + }, { + 'name': 'Kosmetik', + 'cattype': 'out', + 'company': company.id, + }, { + 'name': 'S-Giro', + 'cattype': 'in', + 'company': company.id, + }, { + 'name': 'Bargeld', + 'cattype': 'in', + 'company': company.id, + }, ]) + + tr_list = QifTool.qif_read_transactions('D04.12.2013\nT7,12\nCX\n'+ + 'POpening Balance\nL[Bargeld]\n^\nD05.12.2013\nCX\n'+ + 'M05.12/06.42UHR TT TELTOW\nT290,00\nPGA NR00002168 BLZ10000000 0\n'+ + 'L[S-Giro]\n^\nD05.12.2013\nCX\nMsome food\nT-56,37\n'+ + 'PFoodshop Zehlendorf\nLLebensmittel\n^\nD22.10.2020\n'+ + 'CX\nMLebensmittel\nT-55,84\nPreal,- Teltow\nLLebensmittel\n'+ + 'SLebensmittel\nELebensmittel\n$-49,36\nSKosmetik\nEKlopapier\n'+ + '$-2,99\nSHaushaltschemie\nESagrotan\n$-3,49\n^\n') + + (to_create, msg_txt) = QifTool.convert_transactions_to_create(tr_list) + self.assertEqual(msg_txt, []) + self.assertEqual(to_create, [{ + 'date': date(2013, 12, 4), + 'amount': Decimal('7.12'), + 'state': 'check', + 'bookingtype': 'in', + 'category': 74, + }, { + 'date': date(2013, 12, 5), + 'amount': Decimal('290.00'), + 'description': '05.12/06.42UHR TT TELTOW', + 'state': 'check', + 'bookingtype': 'in', + 'category': 73, + }, { + 'date': date(2013, 12, 5), + 'amount': Decimal('56.37'), + 'description': 'some food', + 'state': 'check', + 'bookingtype': 'out', + 'category': 70, + }, { + 'date': date(2020, 10, 22), + 'amount': Decimal('55.84'), + 'description': 'Lebensmittel', + 'state': 'edit', + 'bookingtype': 'spout', + 'category': 70, + 'splitlines': [ + ('create', [{ + 'amount': Decimal('49.36'), + 'description': 'Lebensmittel', + 'category': 70, + }, { + 'amount': Decimal('2.99'), + 'description': 'Klopapier', + 'category': 72, + }, { + 'amount': Decimal('3.49'), + 'description': 'Sagrotan', + 'category': 71, + }], + )], + }]) + Book.write(*[ + [book], + { + 'lines': [('create', to_create)], + }]) + self.assertEqual(len(book.lines), 4) + self.assertEqual(book.balance, Decimal('184.91')) + + @with_transaction() + def test_qiftool_read_transactions(self): + """ read transaction data from text + """ + QifTool = Pool().get('cashbook_dataexchange.qiftool') + + result = QifTool.qif_read_transactions('D04.12.2013\nT7,12\nCX\n'+ + 'POpening Balance\nL[Bargeld]\n^\nD05.12.2013\nCX\n'+ + 'M05.12/06.42UHR TT TELTOW\nT290,00\nPGA NR00002168 BLZ10000000 0\n'+ + 'L[S-Giro]\n^\nD05.12.2013\nCX\nMsome food\nT-56,37\n'+ + 'PFoodshop Zehlendorf\nLLebensmittel\n^\nD22.10.2020\n'+ + 'CX\nMLebensmittel\nT-55,84\nPreal,- Teltow\nLLebensmittel\n'+ + 'SLebensmittel\nELebensmittel\n$-49,36\nSKosmetik\nEKlopapier\n'+ + '$-2,99\nSHaushaltschemie\nESagrotan\n$-3,49\n^\n') + self.assertEqual(result, [{ + 'split': [], + 'date': date(2013, 12, 4), + 'amount': Decimal('7.12'), + 'state': 'check', + 'party': 'Opening Balance', + 'account': 'Bargeld', + }, { + 'split': [], + 'date': date(2013, 12, 5), + 'state': 'check', + 'description': '05.12/06.42UHR TT TELTOW', + 'amount': Decimal('290.00'), + 'party': 'GA NR00002168 BLZ10000000 0', + 'account': 'S-Giro', + }, { + 'split': [], + 'date': date(2013, 12, 5), + 'state': 'check', + 'description': 'some food', + 'amount': Decimal('-56.37'), + 'party': 'Foodshop Zehlendorf', + 'category': 'Lebensmittel', + }, { + 'split': [{ + 'category': 'Lebensmittel', + 'description': 'Lebensmittel', + 'amount': Decimal('-49.36'), + }, { + 'category': 'Kosmetik', + 'description': 'Klopapier', + 'amount': Decimal('-2.99'), + }, { + 'category': 'Haushaltschemie', + 'description': 'Sagrotan', + 'amount': Decimal('-3.49'), + }], + 'date': date(2020, 10, 22), + 'state': 'check', + 'description': 'Lebensmittel', + 'amount': Decimal('-55.84'), + 'party': 'real,- Teltow', + 'category': 'Lebensmittel', + }]) + @with_transaction() def test_qiftool_read_categories(self): """ read category-data from text diff --git a/view/wiz_qifimport_info_form.xml b/view/wiz_qifimport_info_form.xml index caff918..9b38048 100644 --- a/view/wiz_qifimport_info_form.xml +++ b/view/wiz_qifimport_info_form.xml @@ -5,9 +5,12 @@ full copyright notices and license terms. -->