Compare commits

...

9 commits

Author SHA1 Message Date
Frederik Jaeckel
491678da69 Version 6.0.2 2022-09-05 10:22:20 +02:00
Frederik Jaeckel
e9cde133c2 transaktionen: export + test 2022-09-05 10:13:20 +02:00
Frederik Jaeckel
8bb396f1d8 transaktion: import korrigiert 2022-09-03 20:36:16 +02:00
Frederik Jaeckel
78d02a60b7 import: party, transaction
übersetzung korrigiert
2022-09-02 14:33:12 +02:00
Frederik Jaeckel
a26e7119eb party: qif import 2022-09-01 17:13:55 +02:00
Frederik Jaeckel
be9c584fea import der transaktionen begonnen 2022-09-01 14:48:04 +02:00
Frederik Jaeckel
ac9400c085 kategorie: export als qif 2022-08-31 17:32:01 +02:00
Frederik Jaeckel
cdc0dfa49d Etikett ver 6.0.1 zum Änderungssatz 766463c8f9e1 hinzugefügt 2022-08-31 10:30:59 +02:00
Frederik Jaeckel
3eb4aebe0a Version 6.0.1 2022-08-31 10:30:49 +02:00
22 changed files with 1591 additions and 66 deletions

View file

@ -14,6 +14,15 @@ Requires
Changes
=======
*6.0.2 - 05.09.2022*
- category: qif-export
- party, transactions: qif-import/export
*6.0.1 - 31.08.2022*
- add: qif - category - import
*6.0.0 - 28.08.2022*
- init

View file

@ -5,16 +5,23 @@
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, QifBookExport
def register():
Pool.register(
QifTool,
Category,
Book,
ImportQifWizardStart,
ImportQifWizardInfo,
module='cashbook_dataexchange', type_='model')
Pool.register(
QifCategoryExport,
QifBookExport,
module='cashbook_dataexchange', type_='report')
Pool.register(
ImportQifWizard,
module='cashbook_dataexchange', type_='wizard')

46
book.py Normal file
View file

@ -0,0 +1,46 @@
# -*- 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, book):
""" export all transactions as QIF
"""
pool = Pool()
QifTool = pool.get('cashbook_dataexchange.qiftool')
return QifTool.qif_export_book(book)
@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, fail_cnt) = QifTool.convert_transactions_to_create(
QifTool.qif_read_transactions(qif_content['Bank'])
)
if fail_cnt == 0:
Book2.write(*[
[book],
{
'lines': [('create', to_create)],
}])
return [book]
return None
# end Category

View file

@ -10,6 +10,21 @@ from trytond.pool import Pool, PoolMeta
class Category(metaclass=PoolMeta):
__name__ = 'cashbook.category'
@classmethod
def export_as_qif(cls):
""" export all accessible categories 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, qifdata):
""" add categories from QIF-File-content
@ -18,46 +33,12 @@ class Category(metaclass=PoolMeta):
QifTool = pool.get('cashbook_dataexchange.qiftool')
Category2 = pool.get('cashbook.category')
def get_create(ctype, catdict, parent, do_search):
""" check if category exists, generate create-data
"""
result = []
for catname in catdict.keys():
if do_search == True:
c_lst = Category2.search([
('cattype', '=', ctype),
('name', '=', catname),
('parent', '=', None) if parent is None else ('parent.id', '=', parent.id),
])
else :
c_lst = []
if len(c_lst) == 0:
cat1 = {
'cattype': ctype,
'name': catname,
}
if parent is not None:
cat1['parent'] = parent.id
if len(catdict[catname]['childs']) > 0:
childs = get_create(ctype, catdict[catname]['childs'], None, False)
if len(childs) > 0:
cat1['childs'] = [('create', childs)]
result.append(cat1)
else :
if len(catdict[catname]['childs']) > 0:
result.extend(get_create(ctype, catdict[catname]['childs'], c_lst[0], True))
return result
type_data = QifTool.split_by_type(qifdata)
if not 'Cat' in type_data.keys():
return None
cat_tree = QifTool.qif_read_categories(type_data['Cat'])
to_create = []
for typ1 in ['in', 'out']:
to_create.extend(get_create(typ1, cat_tree[typ1], None, True))
to_create = QifTool.convert_categories_to_create(
QifTool.qif_read_categories(type_data['Cat']))
return Category2.create(to_create)
# end Category

View file

@ -10,6 +10,50 @@ 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:\nCredit: %(credit)s\nDebit: %(debit)s\nBalance: %(balance)s\nNumber of transactions: %(quantity)s"
msgstr "Die folgenden Transaktionen werden nun importiert:\nEinnahmen: %(credit)s\Ausgaben: %(debit)s\nSaldo: %(balance)s\nAnzahl Transactionen: %(quantity)s"
msgctxt "model:ir.message,text:msg_wiz_parties_found"
msgid "The following %(numparties)s parties are now imported:"
msgstr "Die folgenden %(numparties)s Parteien werden nun importiert:"
msgctxt "model:ir.message,text:msg_wiz_no_categories"
msgid "No categories were found in the file."
msgstr "In der Datei wurden keine Kategorien gefunden."
msgctxt "model:ir.message,text:msg_wiz_no_bank"
msgid "No transactions were found in the file."
msgstr "In der Datei wurden keine Transaktionen gefunden."
msgctxt "model:ir.message,text: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,text:mds_import_party_notfound"
msgid "The party '%(pname)s' was not found."
msgstr "Die Partei '%(pname)s' wurde nicht gefunden."
msgctxt "model:ir.message,text:mds_import_many_parties_found"
msgid "For the party '%(pname)s' of the import, several parties were found in the system. Use: '%(pname2)s'"
msgstr "Für die Partei '%(pname)s' des Imports wurden mehrere Parteien im System gefunden. Verwende: '%(pname2)s'"
msgctxt "model:ir.message,text: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,text:mds_import_checknumber"
msgid "Cheque No."
msgstr "Scheck-Nr:"
msgctxt "model:ir.message,text:mds_import_address"
msgid "Address"
msgstr "Adresse"
msgctxt "model:ir.message,text: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 #
@ -18,9 +62,13 @@ msgctxt "model:ir.action,name:act_import_qif_wizard"
msgid "Import QIF-File"
msgstr "QIF-Datei importieren"
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."
msgctxt "model:ir.action,name:qif_category_report"
msgid "Export QIF-File"
msgstr "QIF-Datei exportieren"
msgctxt "model:ir.action,name:qif_transaction_report"
msgid "Export QIF-File"
msgstr "QIF-Datei exportieren"
#####################################
@ -43,8 +91,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 importieren"
###########################################
@ -58,6 +106,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"
@ -78,10 +130,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"

View file

@ -2,10 +2,66 @@
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:\nCredit: %(credit)s\nDebit: %(debit)s\nBalance: %(balance)s\nNumber of transactions: %(quantity)s"
msgstr "The following transactionen are now imported:\nCredit: %(credit)s\nDebit: %(debit)s\nBalance: %(balance)s\nNumber of transactions: %(quantity)s"
msgctxt "model:ir.message,text:msg_wiz_parties_found"
msgid "The following %(numparties)s parties are now imported:"
msgstr "The following %(numparties)s parties are now imported:"
msgctxt "model:ir.message,text:msg_wiz_no_categories"
msgid "No categories were found in the file."
msgstr "No categories were found in the file."
msgctxt "model:ir.message,text:msg_wiz_no_bank"
msgid "No transactions were found in the file."
msgstr "No transactions were found in the file."
msgctxt "model:ir.message,text: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,text:mds_import_party_notfound"
msgid "The party '%(pname)s' was not found."
msgstr "The party '%(pname)s' was not found."
msgctxt "model:ir.message,text:mds_import_many_parties_found"
msgid "For the party '%(pname)s' of the import, several parties were found in the system. Use: '%(pname2)s'"
msgstr "For the party '%(pname)s' of the import, several parties were found in the system. Use: '%(pname2)s'"
msgctxt "model:ir.message,text: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,text:mds_import_checknumber"
msgid "Cheque No."
msgstr "Cheque No."
msgctxt "model:ir.message,text:mds_import_address"
msgid "Address"
msgstr "Address"
msgctxt "model:ir.message,text: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:ir.action,name:qif_transaction_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 +74,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 +90,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"

View file

@ -8,10 +8,39 @@ full copyright notices and license terms. -->
<record model="ir.message" id="msg_wiz_categories_found">
<field name="text">The following categories are now imported:\n%(categories)s</field>
</record>
<record model="ir.message" id="msg_wiz_no_categories">
<field name="text">No categories were found in the file.</field>
</record>
<record model="ir.message" id="msg_wiz_no_bank">
<field name="text">No transactions were found in the file.</field>
</record>
<record model="ir.message" id="msg_wiz_parties_found">
<field name="text">The following %(numparties)s parties are now imported:</field>
</record>
<record model="ir.message" id="msg_wiz_transactions_found">
<field name="text">The following transactionen are now imported:\nCredit: %(credit)s\nDebit: %(debit)s\nBalance: %(balance)s\nNumber of transactions: %(quantity)s</field>
</record>
<record model="ir.message" id="msg_wiz_transactions_error">
<field name="text">When reading the QIF file, there were the following problems:</field>
</record>
<record model="ir.message" id="mds_import_category_notfound">
<field name="text">The category '%(catname)s' (Type: %(cattype)s) was not found.</field>
</record>
<record model="ir.message" id="mds_import_party_notfound">
<field name="text">The party '%(pname)s' was not found.</field>
</record>
<record model="ir.message" id="mds_import_many_parties_found">
<field name="text">For the party '%(pname)s' of the import, several parties were found in the system. Use: '%(pname2)s'</field>
</record>
<record model="ir.message" id="mds_import_many_categories_found">
<field name="text">For the category '%(catname1)s' (type: '%(cattype)s') of the import, several categories were found in the system. Use: '%(catname2)s'</field>
</record>
<record model="ir.message" id="mds_import_checknumber">
<field name="text">Cheque No.</field>
</record>
<record model="ir.message" id="mds_import_address">
<field name="text">Address</field>
</record>
</data>
</tryton>

64
qif_export.py Normal file
View file

@ -0,0 +1,64 @@
# -*- 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.report import Report
from trytond.pool import Pool
from slugify import slugify
class QifCategoryExport(Report):
__name__ = 'cashbook_dataexchange.rep_category'
@classmethod
def execute(cls, ids, data):
""" filename for export
"""
pool = Pool()
IrDate = pool.get('ir.date')
Category = pool.get('cashbook.category')
return (
'qif',
Category.export_as_qif(),
False,
'%s-categories' % IrDate.today().isoformat().replace('-', ''))
# end QifCategoryExport
class QifBookExport(Report):
__name__ = 'cashbook_dataexchange.rep_book'
@classmethod
def execute(cls, ids, data):
""" filename for export
"""
pool = Pool()
IrDate = pool.get('ir.date')
Book = pool.get('cashbook.book')
books = Book.search([('id', '=', data.get('id', -1))])
if len(books) == 1:
return (
'qif',
Book.export_as_qif(books[0]),
False,
slugify('%(date)s-transactions-%(book)s' % {
'date': IrDate.today().isoformat().replace('-', ''),
'book': books[0].name,
}, max_length=100, word_boundary=True, save_order=True),
)
else :
return (
'txt',
'not cashbook found',
False,
'%(date)s-transactions-%(book)s' % {
'date': IrDate.today().isoformat().replace('-', ''),
'book': 'not-found',
})
# end QifBookExport

37
qif_export.xml Normal file
View file

@ -0,0 +1,37 @@
<?xml version="1.0"?>
<!-- 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. -->
<tryton>
<data>
<record model="ir.action.report" id="qif_category_report">
<field name="name">Export QIF-File</field>
<field name="model">cashbook.category</field>
<field name="report_name">cashbook_dataexchange.rep_category</field>
<field name="report">cashbook_dataexchange/report/export.fods</field>
<field name="template_extension">ods</field>
<field name="single" eval="False"/>
</record>
<record model="ir.action.keyword" id="qif_category_report-keyword">
<field name="keyword">form_action</field>
<field name="model">cashbook.category,-1</field>
<field name="action" ref="qif_category_report"/>
</record>
<record model="ir.action.report" id="qif_transaction_report">
<field name="name">Export QIF-File</field>
<field name="model">cashbook.book</field>
<field name="report_name">cashbook_dataexchange.rep_book</field>
<field name="report">cashbook_dataexchange/report/export.fods</field>
<field name="template_extension">ods</field>
<field name="single" eval="True"/>
</record>
<record model="ir.action.keyword" id="qif_transaction_report-keyword">
<field name="keyword">form_action</field>
<field name="model">cashbook.book,-1</field>
<field name="action" ref="qif_transaction_report"/>
</record>
</data>
</tryton>

View file

@ -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,43 +109,117 @@ class ImportQifWizard(Wizard):
""" read file, show number of objects
"""
pool = Pool()
QitTool = pool.get('cashbook_dataexchange.qiftool')
QifTool = pool.get('cashbook_dataexchange.qiftool')
Category = pool.get('cashbook.category')
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):
def get_catlist(record, cattype, parent_name=None):
""" generate list of categories
"""
names = []
for name1 in catlist.keys():
name_lst = []
if parent_name:
name_lst.append(parent_name)
name_lst.append(name1)
current_name = '/'.join(name_lst)
names.append(current_name)
names.extend(get_catlist(catlist[name1]['childs'], current_name))
if record['cattype'] != cattype:
return []
if 'parent' in record.keys():
parent_name = Category(record['parent']).rec_name
name_lst = []
if parent_name:
name_lst.append(parent_name)
name_lst.append(record['name'])
current_name = '/'.join(name_lst)
names.append(current_name)
if 'childs' in record.keys():
# record['childs']: [('create', [{}, ...]))]
for x in record['childs'][0][1]:
names.extend(get_catlist(x, cattype, current_name))
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'])
to_create = QifTool.convert_categories_to_create(QifTool.qif_read_categories(qif_content['Cat']))
in_categories = []
out_categories = []
for x in to_create:
in_categories.extend(get_catlist(x, 'in'))
out_categories.extend(get_catlist(x, 'out'))
self.showinfo.info = gettext(
'cashbook_dataexchange.msg_wiz_categories_found',
categories = '\n'.join(
[''] +
['%s (in)' % x for x in get_catlist(categories['in'], None)]+
['%s (in)' % x for x in in_categories]+
[''] +
['%s (out)' % x for x in get_catlist(categories['out'], None)]
['%s (out)' % x for x in out_categories]
)
)
if len(to_create) > 0:
self.showinfo.allowimport = True
else :
self.showinfo.info = gettext('cashbook_dataexchange.msg_wiz_no_categories')
elif model == 'party.party':
# read file content, extract parties
qif_content = QifTool.split_by_type(file_content)
if 'Bank' in qif_content.keys():
to_create = QifTool.convert_parties_to_create(
QifTool.qif_read_transactions(qif_content['Bank'])
)
self.showinfo.info = gettext(
'cashbook_dataexchange.msg_wiz_parties_found',
numparties = len(to_create),
) + '\n\n' + '\n'.join([x['name'] for x in to_create])
if len(to_create) > 0:
self.showinfo.allowimport = True
else :
self.showinfo.info = gettext('cashbook_dataexchange.msg_wiz_no_bank')
elif model == 'cashbook.book':
info_lst = []
# read file content, extract categories
qif_content = QifTool.split_by_type(file_content)
if 'Bank' in qif_content.keys():
(to_create, msg_list, fail_cnt) = QifTool.convert_transactions_to_create(
QifTool.qif_read_transactions(qif_content['Bank'])
)
if len(msg_list) > 0:
info_lst.append(gettext('cashbook_dataexchange.msg_wiz_transactions_error'))
info_lst.append('')
short_lst = []
for x in msg_list:
if x not in short_lst:
short_lst.append(x)
info_lst.extend(short_lst)
info_lst.append('')
# count
if fail_cnt == 0:
debit = sum([x['amount'] for x in to_create if x['bookingtype']=='out'])
credit = sum([x['amount'] for x in to_create if x['bookingtype']=='in'])
balance = credit - debit
if len(msg_list) > 0:
msg_list.append('')
info_lst.append(gettext(
'cashbook_dataexchange.msg_wiz_transactions_found',
quantity = len(to_create),
balance = Report.format_currency(balance, None, self.start.book.currency),
credit = Report.format_currency(credit, None, self.start.book.currency),
debit = Report.format_currency(debit, None, self.start.book.currency),
))
self.showinfo.allowimport = True
else :
info_lst.append(gettext('cashbook_dataexchange.msg_wiz_no_bank'))
self.showinfo.info = '\n'.join(info_lst)
return 'showinfo'
@ -119,6 +228,9 @@ class ImportQifWizard(Wizard):
"""
pool = Pool()
Category = pool.get('cashbook.category')
Book = pool.get('cashbook.book')
Party = pool.get('party.party')
QifTool = pool.get('cashbook_dataexchange.qiftool')
model = Transaction().context.get('active_model', '')
file_content = None
@ -128,7 +240,16 @@ 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)
elif model == 'party.party':
qif_content = QifTool.split_by_type(file_content)
if 'Bank' in qif_content.keys():
to_create = QifTool.convert_parties_to_create(
QifTool.qif_read_transactions(qif_content['Bank'])
)
Party.create(to_create)
return 'end'
# end ImportQifWizard

View file

@ -29,5 +29,19 @@ full copyright notices and license terms. -->
<field name="action" ref="act_import_qif_wizard"/>
</record>
<!-- import transactions -->
<record model="ir.action.keyword" id="act_import_qif_wizard-book-keyword">
<field name="keyword">form_action</field>
<field name="model">cashbook.book,-1</field>
<field name="action" ref="act_import_qif_wizard"/>
</record>
<!-- import parties -->
<record model="ir.action.keyword" id="act_import_qif_wizard-party-keyword">
<field name="keyword">form_action</field>
<field name="model">party.party,-1</field>
<field name="action" ref="act_import_qif_wizard"/>
</record>
</data>
</tryton>

View file

@ -5,6 +5,10 @@
from trytond.pool import Pool
from trytond.model import Model
from trytond.i18n import gettext
from trytond.report import Report
from decimal import Decimal
from datetime import datetime
class QifTool(Model):
@ -34,6 +38,371 @@ 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': <Decimal>,
'description': 'purpose',
'category': 'name of categroy',
},...],
'date': <date of transaction>,
'amount': <Decimal>,
'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 qif_export_book(cls, book):
""" export book
"""
result = ['!Type:Bank']
def get_amount_by_bookingstate(amount, line):
""" get amount with sign
"""
if line.bookingtype in ['in', 'spin', 'mvin']:
return amount
elif line.bookingtype in ['out', 'spout', 'mvout']:
return amount * Decimal('-1.0')
else :
raise ValueError('invalid bookingtype: %s' % line.bookingtype)
for line in book.lines:
# date
result.append('D%(date)s' % {
'date': Report.format_date(line.date, None),
})
# total
result.append('T%(total)s' % {
'total': Report.format_number(get_amount_by_bookingstate(line.amount, line), None),
})
# state
result.append('C%(state)s' % {
'state': 'X' if line.state in ['check', 'done'] else '*',
})
# party
if line.party:
result.append('P%(party)s' % {
'party': line.party.rec_name,
})
# address
p_address = line.party.address_get()
if p_address:
if len(p_address.full_address.strip()) > 0:
result.append('A%(address)s' % {
'address': p_address.full_address.replace('\n', ', ').strip(),
})
# category
if line.category:
result.append('L%(category)s' % {
'category': line.category.rec_name.replace('/', ':'),
})
# account
if line.booktransf:
result.append('L[%(account)s]' % {
'account': line.booktransf.name,
})
# description
if line.description:
result.append('M%(memo)s' % {
'memo': line.description.replace('\n', '; ')
})
# split-booking
for splitline in line.splitlines:
result.append('S%(category)s' % {
'category': splitline.category.rec_name.replace('/', ':'),
})
if splitline.description:
result.append('E%(memo)s' % {
'memo': splitline.description.replace('\n', '; ')
})
result.append('$%(total)s' % {
'total': Report.format_number(get_amount_by_bookingstate(splitline.amount, line), None),
})
result.append('^')
return '\n'.join(result)
@classmethod
def get_party_by_name(cls, partyname):
""" find party
"""
Party = Pool().get('party.party')
party_id = None
msg_txt = None
parties = Party.search([('rec_name', 'ilike', '%%%s%%' % partyname)])
if len(parties) == 0:
msg_txt = gettext(
'cashbook_dataexchange.mds_import_party_notfound',
pname = partyname,
)
elif len(parties) == 1:
party_id = parties[0].id
else :
party_id = parties[0].id
msg_txt = gettext(
'cashbook_dataexchange.mds_import_many_parties_found',
pname = partyname,
pname2 = parties[0].rec_name,
)
return (party_id, msg_txt)
@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_categories_to_create(cls, cat_tree):
""" cat_tree: result from cls.qif_read_categories()
"""
Category = Pool().get('cashbook.category')
def get_create(ctype, catdict, parent, do_search):
""" check if category exists, generate create-data
"""
result = []
for catname in catdict.keys():
if do_search == True:
c_lst = Category.search([
('cattype', '=', ctype),
('name', '=', catname),
('parent', '=', None) if parent is None else ('parent.id', '=', parent.id),
])
else :
c_lst = []
if len(c_lst) == 0:
cat1 = {
'cattype': ctype,
'name': catname,
}
if parent is not None:
cat1['parent'] = parent.id
if len(catdict[catname]['childs']) > 0:
childs = get_create(ctype, catdict[catname]['childs'], None, False)
if len(childs) > 0:
cat1['childs'] = [('create', childs)]
result.append(cat1)
else :
if len(catdict[catname]['childs']) > 0:
result.extend(get_create(ctype, catdict[catname]['childs'], c_lst[0], True))
return result
to_create = []
for typ1 in ['in', 'out']:
to_create.extend(get_create(typ1, cat_tree[typ1], None, True))
return to_create
@classmethod
def convert_parties_to_create(cls, transactions):
""" extract party from transaction, check if exist,
create 'to_create'
"""
Party = Pool().get('party.party')
to_create = []
party_cache = []
for transaction in transactions:
if 'party' in transaction.keys():
if transaction['party'] in party_cache:
continue
party_cache.append(transaction['party'])
if Party.search_count([
('rec_name', 'ilike', '%%%(pname)s%%' % {
'pname': transaction['party'],
})
]) == 0:
to_create.append({
'name': transaction['party'],
'addresses': [('create', [{
'street': transaction.get('address', None),
}])],
})
return to_create
@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 = []
fail_cnt = 0
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'))
# party
if 'party' in transaction.keys():
(party_id, msg_txt) = cls.get_party_by_name(transaction['party'])
if party_id is not None:
line['party'] = party_id
else :
fail_cnt += 1
if msg_txt is not None:
msg_list.append(msg_txt)
# 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)
fail_cnt += 1
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,
})
else :
fail_cnt += 1
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, fail_cnt)
@classmethod
def qif_read_categories(cls, catdata):
""" read categories from text
@ -78,4 +447,16 @@ class QifTool(Model):
categories[cattype] = add_category(categories[cattype], catname, cattype)
return categories
@classmethod
def qif_export_category(cls, record):
""" export single category as qif
"""
return '\n'.join([
'N%(cname)s' % {
'cname': record.rec_name.replace('/', ':'),
},
'E' if record.cattype == 'out' else 'I',
'^',
])
# end QifTool

240
report/exort.fods Normal file
View file

@ -0,0 +1,240 @@
<?xml version="1.0" encoding="UTF-8"?>
<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:presentation="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rpt="http://openoffice.org/2005/report" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:css3t="http://www.w3.org/TR/css3-text/" office:version="1.2" office:mimetype="application/vnd.oasis.opendocument.spreadsheet">
<office:meta><meta:creation-date>2017-10-20T23:41:04.964000000</meta:creation-date><meta:generator>LibreOffice/6.0.7.3$Linux_X86_64 LibreOffice_project/00m0$Build-3</meta:generator><meta:document-statistic meta:table-count="1" meta:cell-count="0" meta:object-count="0"/></office:meta>
<office:settings>
<config:config-item-set config:name="ooo:view-settings">
<config:config-item config:name="VisibleAreaTop" config:type="int">0</config:config-item>
<config:config-item config:name="VisibleAreaLeft" config:type="int">0</config:config-item>
<config:config-item config:name="VisibleAreaWidth" config:type="int">2257</config:config-item>
<config:config-item config:name="VisibleAreaHeight" config:type="int">451</config:config-item>
<config:config-item-map-indexed config:name="Views">
<config:config-item-map-entry>
<config:config-item config:name="ViewId" config:type="string">view1</config:config-item>
<config:config-item-map-named config:name="Tables">
<config:config-item-map-entry config:name="Sheet1">
<config:config-item config:name="CursorPositionX" config:type="int">0</config:config-item>
<config:config-item config:name="CursorPositionY" config:type="int">0</config:config-item>
<config:config-item config:name="HorizontalSplitMode" config:type="short">0</config:config-item>
<config:config-item config:name="VerticalSplitMode" config:type="short">0</config:config-item>
<config:config-item config:name="HorizontalSplitPosition" config:type="int">0</config:config-item>
<config:config-item config:name="VerticalSplitPosition" config:type="int">0</config:config-item>
<config:config-item config:name="ActiveSplitRange" config:type="short">2</config:config-item>
<config:config-item config:name="PositionLeft" config:type="int">0</config:config-item>
<config:config-item config:name="PositionRight" config:type="int">0</config:config-item>
<config:config-item config:name="PositionTop" config:type="int">0</config:config-item>
<config:config-item config:name="PositionBottom" config:type="int">0</config:config-item>
<config:config-item config:name="ZoomType" config:type="short">0</config:config-item>
<config:config-item config:name="ZoomValue" config:type="int">100</config:config-item>
<config:config-item config:name="PageViewZoomValue" config:type="int">60</config:config-item>
<config:config-item config:name="ShowGrid" config:type="boolean">true</config:config-item>
<config:config-item config:name="AnchoredTextOverflowLegacy" config:type="boolean">false</config:config-item>
</config:config-item-map-entry>
</config:config-item-map-named>
<config:config-item config:name="ActiveTable" config:type="string">Sheet1</config:config-item>
<config:config-item config:name="HorizontalScrollbarWidth" config:type="int">1828</config:config-item>
<config:config-item config:name="ZoomType" config:type="short">0</config:config-item>
<config:config-item config:name="ZoomValue" config:type="int">100</config:config-item>
<config:config-item config:name="PageViewZoomValue" config:type="int">60</config:config-item>
<config:config-item config:name="ShowPageBreakPreview" config:type="boolean">false</config:config-item>
<config:config-item config:name="ShowZeroValues" config:type="boolean">true</config:config-item>
<config:config-item config:name="ShowNotes" config:type="boolean">true</config:config-item>
<config:config-item config:name="ShowGrid" config:type="boolean">true</config:config-item>
<config:config-item config:name="GridColor" config:type="long">12632256</config:config-item>
<config:config-item config:name="ShowPageBreaks" config:type="boolean">true</config:config-item>
<config:config-item config:name="HasColumnRowHeaders" config:type="boolean">true</config:config-item>
<config:config-item config:name="HasSheetTabs" config:type="boolean">true</config:config-item>
<config:config-item config:name="IsOutlineSymbolsSet" config:type="boolean">true</config:config-item>
<config:config-item config:name="IsValueHighlightingEnabled" config:type="boolean">false</config:config-item>
<config:config-item config:name="IsSnapToRaster" config:type="boolean">false</config:config-item>
<config:config-item config:name="RasterIsVisible" config:type="boolean">false</config:config-item>
<config:config-item config:name="RasterResolutionX" config:type="int">1270</config:config-item>
<config:config-item config:name="RasterResolutionY" config:type="int">1270</config:config-item>
<config:config-item config:name="RasterSubdivisionX" config:type="int">1</config:config-item>
<config:config-item config:name="RasterSubdivisionY" config:type="int">1</config:config-item>
<config:config-item config:name="IsRasterAxisSynchronized" config:type="boolean">true</config:config-item>
<config:config-item config:name="AnchoredTextOverflowLegacy" config:type="boolean">false</config:config-item>
</config:config-item-map-entry>
</config:config-item-map-indexed>
</config:config-item-set>
<config:config-item-set config:name="ooo:configuration-settings">
<config:config-item config:name="SyntaxStringRef" config:type="short">7</config:config-item>
<config:config-item config:name="AllowPrintJobCancel" config:type="boolean">true</config:config-item>
<config:config-item config:name="SaveVersionOnClose" config:type="boolean">false</config:config-item>
<config:config-item config:name="IsKernAsianPunctuation" config:type="boolean">false</config:config-item>
<config:config-item config:name="CharacterCompressionType" config:type="short">0</config:config-item>
<config:config-item config:name="ApplyUserData" config:type="boolean">true</config:config-item>
<config:config-item config:name="PrinterSetup" config:type="base64Binary">owH+/0hQX0xhc2VySmV0X01GUF9NNDI2ZmRuX0IyNUM3Ml8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQ1VQUzpIUF9MYXNlckpldF9NRlBfTTQyNmZkbl9CMgAWAAMAxAAAAAAAAAAEAAhSAAAEdAAASm9iRGF0YSAxCnByaW50ZXI9SFBfTGFzZXJKZXRfTUZQX000MjZmZG5fQjI1QzcyXwpvcmllbnRhdGlvbj1Qb3J0cmFpdApjb3BpZXM9MQpjb2xsYXRlPWZhbHNlCm1hcmdpbmRhanVzdG1lbnQ9MCwwLDAsMApjb2xvcmRlcHRoPTI0CnBzbGV2ZWw9MApwZGZkZXZpY2U9MQpjb2xvcmRldmljZT0wClBQRENvbnRleERhdGEKUGFnZVNpemU6QTQAABIAQ09NUEFUX0RVUExFWF9NT0RFDwBEdXBsZXhNb2RlOjpPZmY=</config:config-item>
<config:config-item config:name="PrinterName" config:type="string">HP_LaserJet_MFP_M426fdn_B25C72_</config:config-item>
<config:config-item config:name="AutoCalculate" config:type="boolean">true</config:config-item>
<config:config-item config:name="LinkUpdateMode" config:type="short">3</config:config-item>
<config:config-item config:name="HasColumnRowHeaders" config:type="boolean">true</config:config-item>
<config:config-item config:name="LoadReadonly" config:type="boolean">false</config:config-item>
<config:config-item config:name="UpdateFromTemplate" config:type="boolean">true</config:config-item>
<config:config-item config:name="ShowZeroValues" config:type="boolean">true</config:config-item>
<config:config-item config:name="GridColor" config:type="long">12632256</config:config-item>
<config:config-item config:name="ShowPageBreaks" config:type="boolean">true</config:config-item>
<config:config-item config:name="ShowGrid" config:type="boolean">true</config:config-item>
<config:config-item config:name="IsOutlineSymbolsSet" config:type="boolean">true</config:config-item>
<config:config-item config:name="IsDocumentShared" config:type="boolean">false</config:config-item>
<config:config-item config:name="ShowNotes" config:type="boolean">true</config:config-item>
<config:config-item config:name="EmbedFonts" config:type="boolean">false</config:config-item>
<config:config-item config:name="HasSheetTabs" config:type="boolean">true</config:config-item>
<config:config-item config:name="RasterSubdivisionY" config:type="int">1</config:config-item>
<config:config-item config:name="RasterIsVisible" config:type="boolean">false</config:config-item>
<config:config-item config:name="RasterResolutionX" config:type="int">1270</config:config-item>
<config:config-item config:name="RasterResolutionY" config:type="int">1270</config:config-item>
<config:config-item config:name="IsSnapToRaster" config:type="boolean">false</config:config-item>
<config:config-item config:name="RasterSubdivisionX" config:type="int">1</config:config-item>
<config:config-item config:name="IsRasterAxisSynchronized" config:type="boolean">true</config:config-item>
</config:config-item-set>
</office:settings>
<office:scripts>
<office:script script:language="ooo:Basic">
<ooo:libraries xmlns:ooo="http://openoffice.org/2004/office" xmlns:xlink="http://www.w3.org/1999/xlink"/>
</office:script>
</office:scripts>
<office:font-face-decls>
<style:font-face style:name="Liberation Sans" svg:font-family="&apos;Liberation Sans&apos;" style:font-family-generic="swiss" style:font-pitch="variable"/>
<style:font-face style:name="DejaVu Sans" svg:font-family="&apos;DejaVu Sans&apos;" style:font-family-generic="system" style:font-pitch="variable"/>
<style:font-face style:name="Mangal" svg:font-family="Mangal" style:font-family-generic="system" style:font-pitch="variable"/>
<style:font-face style:name="Microsoft YaHei" svg:font-family="&apos;Microsoft YaHei&apos;" style:font-family-generic="system" style:font-pitch="variable"/>
<style:font-face style:name="Segoe UI" svg:font-family="&apos;Segoe UI&apos;" style:font-family-generic="system" style:font-pitch="variable"/>
<style:font-face style:name="Tahoma" svg:font-family="Tahoma" style:font-family-generic="system" style:font-pitch="variable"/>
</office:font-face-decls>
<office:styles>
<style:default-style style:family="table-cell">
<style:paragraph-properties style:tab-stop-distance="12.7mm"/>
<style:text-properties style:font-name="Liberation Sans" fo:language="en" fo:country="US" style:font-name-asian="Segoe UI" style:language-asian="zh" style:country-asian="CN" style:font-name-complex="Tahoma" style:language-complex="hi" style:country-complex="IN"/>
</style:default-style>
<number:number-style style:name="N0">
<number:number number:min-integer-digits="1"/>
</number:number-style>
<style:style style:name="Default" style:family="table-cell">
<style:text-properties style:font-name-asian="Microsoft YaHei" style:font-family-asian="&apos;Microsoft YaHei&apos;" style:font-family-generic-asian="system" style:font-pitch-asian="variable" style:font-name-complex="Mangal" style:font-family-complex="Mangal" style:font-family-generic-complex="system" style:font-pitch-complex="variable"/>
</style:style>
<style:style style:name="Heading" style:family="table-cell" style:parent-style-name="Default">
<style:text-properties fo:color="#000000" fo:font-size="24pt" fo:font-style="normal" fo:font-weight="bold"/>
</style:style>
<style:style style:name="Heading_20_1" style:display-name="Heading 1" style:family="table-cell" style:parent-style-name="Heading">
<style:text-properties fo:color="#000000" fo:font-size="18pt" fo:font-style="normal" fo:font-weight="normal"/>
</style:style>
<style:style style:name="Heading_20_2" style:display-name="Heading 2" style:family="table-cell" style:parent-style-name="Heading">
<style:text-properties fo:color="#000000" fo:font-size="12pt" fo:font-style="normal" fo:font-weight="normal"/>
</style:style>
<style:style style:name="Text" style:family="table-cell" style:parent-style-name="Default"/>
<style:style style:name="Note" style:family="table-cell" style:parent-style-name="Text">
<style:table-cell-properties fo:background-color="#ffffcc" style:diagonal-bl-tr="none" style:diagonal-tl-br="none" fo:border="0.74pt solid #808080"/>
<style:text-properties fo:color="#333333" fo:font-size="10pt" fo:font-style="normal" fo:font-weight="normal"/>
</style:style>
<style:style style:name="Footnote" style:family="table-cell" style:parent-style-name="Text">
<style:text-properties fo:color="#808080" fo:font-size="10pt" fo:font-style="italic" fo:font-weight="normal"/>
</style:style>
<style:style style:name="Status" style:family="table-cell" style:parent-style-name="Default"/>
<style:style style:name="Good" style:family="table-cell" style:parent-style-name="Status">
<style:table-cell-properties fo:background-color="#ccffcc"/>
<style:text-properties fo:color="#006600" fo:font-size="10pt" fo:font-style="normal" fo:font-weight="normal"/>
</style:style>
<style:style style:name="Neutral" style:family="table-cell" style:parent-style-name="Status">
<style:table-cell-properties fo:background-color="#ffffcc"/>
<style:text-properties fo:color="#996600" fo:font-size="10pt" fo:font-style="normal" fo:font-weight="normal"/>
</style:style>
<style:style style:name="Bad" style:family="table-cell" style:parent-style-name="Status">
<style:table-cell-properties fo:background-color="#ffcccc"/>
<style:text-properties fo:color="#cc0000" fo:font-size="10pt" fo:font-style="normal" fo:font-weight="normal"/>
</style:style>
<style:style style:name="Warning" style:family="table-cell" style:parent-style-name="Status">
<style:text-properties fo:color="#cc0000" fo:font-size="10pt" fo:font-style="normal" fo:font-weight="normal"/>
</style:style>
<style:style style:name="Error" style:family="table-cell" style:parent-style-name="Status">
<style:table-cell-properties fo:background-color="#cc0000"/>
<style:text-properties fo:color="#ffffff" fo:font-size="10pt" fo:font-style="normal" fo:font-weight="bold"/>
</style:style>
<style:style style:name="Accent" style:family="table-cell" style:parent-style-name="Default">
<style:text-properties fo:color="#000000" fo:font-size="10pt" fo:font-style="normal" fo:font-weight="bold"/>
</style:style>
<style:style style:name="Accent_20_1" style:display-name="Accent 1" style:family="table-cell" style:parent-style-name="Accent">
<style:table-cell-properties fo:background-color="#000000"/>
<style:text-properties fo:color="#ffffff" fo:font-size="10pt" fo:font-style="normal" fo:font-weight="normal"/>
</style:style>
<style:style style:name="Accent_20_2" style:display-name="Accent 2" style:family="table-cell" style:parent-style-name="Accent">
<style:table-cell-properties fo:background-color="#808080"/>
<style:text-properties fo:color="#ffffff" fo:font-size="10pt" fo:font-style="normal" fo:font-weight="normal"/>
</style:style>
<style:style style:name="Accent_20_3" style:display-name="Accent 3" style:family="table-cell" style:parent-style-name="Accent">
<style:table-cell-properties fo:background-color="#dddddd"/>
</style:style>
</office:styles>
<office:automatic-styles>
<style:style style:name="co1" style:family="table-column">
<style:table-column-properties fo:break-before="auto" style:column-width="22.58mm"/>
</style:style>
<style:style style:name="ro1" style:family="table-row">
<style:table-row-properties style:row-height="4.52mm" fo:break-before="auto" style:use-optimal-row-height="true"/>
</style:style>
<style:style style:name="ta1" style:family="table" style:master-page-name="Default">
<style:table-properties table:display="true" style:writing-mode="lr-tb"/>
</style:style>
<style:page-layout style:name="pm1">
<style:page-layout-properties style:writing-mode="lr-tb"/>
<style:header-style>
<style:header-footer-properties fo:min-height="7.5mm" fo:margin-left="0mm" fo:margin-right="0mm" fo:margin-bottom="2.5mm"/>
</style:header-style>
<style:footer-style>
<style:header-footer-properties fo:min-height="7.5mm" fo:margin-left="0mm" fo:margin-right="0mm" fo:margin-top="2.5mm"/>
</style:footer-style>
</style:page-layout>
<style:page-layout style:name="pm2">
<style:page-layout-properties style:writing-mode="lr-tb"/>
<style:header-style>
<style:header-footer-properties fo:min-height="7.5mm" fo:margin-left="0mm" fo:margin-right="0mm" fo:margin-bottom="2.5mm" fo:border="2.49pt solid #000000" fo:padding="0.18mm" fo:background-color="#c0c0c0">
<style:background-image/>
</style:header-footer-properties>
</style:header-style>
<style:footer-style>
<style:header-footer-properties fo:min-height="7.5mm" fo:margin-left="0mm" fo:margin-right="0mm" fo:margin-top="2.5mm" fo:border="2.49pt solid #000000" fo:padding="0.18mm" fo:background-color="#c0c0c0">
<style:background-image/>
</style:header-footer-properties>
</style:footer-style>
</style:page-layout>
</office:automatic-styles>
<office:master-styles>
<style:master-page style:name="Default" style:page-layout-name="pm1">
<style:header>
<text:p><text:sheet-name>???</text:sheet-name></text:p>
</style:header>
<style:header-left style:display="false"/>
<style:footer>
<text:p>Page <text:page-number>1</text:page-number></text:p>
</style:footer>
<style:footer-left style:display="false"/>
</style:master-page>
<style:master-page style:name="Report" style:page-layout-name="pm2">
<style:header>
<style:region-left>
<text:p><text:sheet-name>???</text:sheet-name><text:s/>(<text:title>???</text:title>)</text:p>
</style:region-left>
<style:region-right>
<text:p><text:date style:data-style-name="N2" text:date-value="2022-08-31">00.00.0000</text:date>, <text:time style:data-style-name="N2" text:time-value="17:16:57.122091415">00:00:00</text:time></text:p>
</style:region-right>
</style:header>
<style:header-left style:display="false"/>
<style:footer>
<text:p>Page <text:page-number>1</text:page-number><text:s/>/ <text:page-count>99</text:page-count></text:p>
</style:footer>
<style:footer-left style:display="false"/>
</style:master-page>
</office:master-styles>
<office:body>
<office:spreadsheet>
<table:calculation-settings table:automatic-find-labels="false" table:use-regular-expressions="false" table:use-wildcards="true"/>
<table:table table:name="Sheet1" table:style-name="ta1">
<table:table-column table:style-name="co1" table:default-cell-style-name="Default"/>
<table:table-row table:style-name="ro1">
<table:table-cell/>
</table:table-row>
</table:table>
<table:named-expressions/>
</office:spreadsheet>
</office:body>
</office:document>

View file

@ -42,7 +42,7 @@ with open(path.join(here, 'versiondep.txt'), encoding='utf-8') as f:
major_version = 6
minor_version = 0
requires = []
requires = ['python-slugify']
for dep in info.get('depends', []):
if not re.match(r'(ir|res|webdav)(\W|$)', dep):
if dep in modversion.keys():
@ -97,7 +97,8 @@ setup(name='%s_%s' % (PREFIX, MODULE),
package_data={
'trytond.modules.%s' % MODULE: (info.get('xml', [])
+ ['tryton.cfg', 'locale/*.po', 'tests/*.py',
'view/*.xml', 'versiondep.txt', 'README.rst']),
'report/*.fods', 'view/*.xml',
'versiondep.txt', 'README.rst']),
},
install_requires=requires,

View file

@ -5,12 +5,16 @@ import trytond.tests.test_tryton
import unittest
from trytond.modules.cashbook_dataexchange.tests.test_category import CategoryTestCase
from trytond.modules.cashbook_dataexchange.tests.test_party import PartyTestCase
from trytond.modules.cashbook_dataexchange.tests.test_transaction import TransactionTestCase
__all__ = ['suite']
class CashbookExchangeTestCase(\
CategoryTestCase,\
PartyTestCase,\
TransactionTestCase,\
):
'Test cashbook exchange module'
module = 'cashbook_dataexchange'

View file

@ -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
@ -12,7 +14,7 @@ from .qifdata import qif_category, qif_types
class CategoryTestCase(CashbookTestCase):
'Test cashbook categoy module'
module = 'CashbookExchangeTestCase'
module = 'cashbook_dataexchange'
@with_transaction()
def test_wiz_import_category(self):
@ -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)
@ -123,6 +125,54 @@ Lebensmittel (out)""")
self.assertEqual(records[13].rec_name, 'Telekommunikation/Telefon')
self.assertEqual(records[14].rec_name, 'Telekommunikation/Telefon/Test1')
result = Category.export_as_qif()
self.assertEqual(result, """!Type:Cat
NFernsehen
E
^
NFernsehen:GEZ
E
^
NFernsehen:TV-Company
E
^
NLebensmittel
E
^
NTelefon
E
^
NTelefon:Telco1-Tablett
E
^
NTelefon:Telco2-Handy
E
^
NTelefon:Telco3
E
^
NTelekommunikation
E
^
NTelekommunikation:Fernsehen
E
^
NTelekommunikation:Online-Dienste
E
^
NTelekommunikation:Telefon
E
^
NTelekommunikation:Telefon:Test1
E
^
NGehalt
I
^
NGehalt:Zulagen
I
^""")
@with_transaction()
def test_category_create_by_qif_existing_categories(self):
""" create categories by import a qif-file,
@ -190,6 +240,194 @@ Lebensmittel (out)""")
'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')
Party = pool.get('party.party')
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')
parties = Party.create([{
'name': 'Opening Balance',
'addresses': [('create', [{}])],
}, {
'name': 'GA NR00002168 BLZ10000000 0',
'addresses': [('create', [{}])],
}, {
'name': 'Foodshop Zehlendorf',
'addresses': [('create', [{}])],
}, {
'name': 'real,- Teltow',
'addresses': [('create', [{}])],
}])
categories = 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, fail_cnt) = 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',
'party': parties[0].id,
'category': categories[4].id,
}, {
'date': date(2013, 12, 5),
'amount': Decimal('290.00'),
'description': '05.12/06.42UHR TT TELTOW',
'state': 'check',
'bookingtype': 'in',
'party': parties[1].id,
'category': categories[3].id,
}, {
'date': date(2013, 12, 5),
'amount': Decimal('56.37'),
'description': 'some food',
'state': 'check',
'bookingtype': 'out',
'party': parties[2].id,
'category': categories[0].id,
}, {
'date': date(2020, 10, 22),
'amount': Decimal('55.84'),
'description': 'Lebensmittel',
'state': 'edit',
'bookingtype': 'spout',
'category': categories[0].id,
'party': parties[3].id,
'splitlines': [
('create', [{
'amount': Decimal('49.36'),
'description': 'Lebensmittel',
'category': categories[0].id,
}, {
'amount': Decimal('2.99'),
'description': 'Klopapier',
'category': categories[2].id,
}, {
'amount': Decimal('3.49'),
'description': 'Sagrotan',
'category': categories[1].id,
}],
)],
}])
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

71
tests/test_party.py Normal file
View file

@ -0,0 +1,71 @@
# -*- 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 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
from .qifdata import qif_types
class PartyTestCase(ModuleTestCase):
'Test cashbook party module'
module = 'cashbook_dataexchange'
@with_transaction()
def test_wiz_import_party(self):
""" create parties by run wizard
"""
pool = Pool()
Party = pool.get('party.party')
ImportWiz = pool.get('cashbook_dataexchange.qif_imp_wiz', type='wizard')
company = self.prep_company()
with Transaction().set_context({
'company': company.id,
'active_model': 'party.party',
}):
(sess_id, start_state, end_state) = ImportWiz.create()
w_obj = ImportWiz(sess_id)
self.assertEqual(start_state, 'start')
self.assertEqual(end_state, 'end')
# run start
result = ImportWiz.execute(sess_id, {}, start_state)
self.assertEqual(list(result.keys()), ['view'])
self.assertEqual(result['view']['defaults']['company'], company.id)
r1 = {}
r1['file_'] = qif_types.encode('utf8')
r1['company'] = company.id
w_obj.start.file_ = r1['file_']
w_obj.start.company = company.id
result = ImportWiz.execute(sess_id, {'start': r1}, 'readf')
self.assertEqual(list(result.keys()), ['view'])
self.assertEqual(result['view']['defaults']['company'], company.id)
self.assertEqual(result['view']['defaults']['info'],
"""The following 3 parties are now imported:\n
Opening Balance
GA NR00002168 BLZ10000000 0
Foodshop Zehlendorf""")
r1 = {'company': company.id}
result = ImportWiz.execute(sess_id, {'showinfo': r1}, 'importf')
self.assertEqual(list(result.keys()), [])
ImportWiz.delete(sess_id)
records = Party.search([], order=[('name', 'ASC')])
self.assertEqual(len(records), 4)
self.assertEqual(records[0].rec_name, 'Foodshop Zehlendorf')
self.assertEqual(records[1].rec_name, 'GA NR00002168 BLZ10000000 0')
self.assertEqual(records[2].rec_name, 'm-ds')
self.assertEqual(records[3].rec_name, 'Opening Balance')
# end PartyTestCase

132
tests/test_transaction.py Normal file
View file

@ -0,0 +1,132 @@
# -*- 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 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
from .qifdata import qif_types
class TransactionTestCase(ModuleTestCase):
'Test cashbook transaction module'
module = 'cashbook_dataexchange'
@with_transaction()
def test_wiz_import_transactions(self):
""" create transactions by run wizard
"""
pool = Pool()
Party = pool.get('party.party')
Category = pool.get('cashbook.category')
Book = pool.get('cashbook.book')
ImportWiz = pool.get('cashbook_dataexchange.qif_imp_wiz', type='wizard')
company = self.prep_company()
with Transaction().set_context({
'company': company.id,
'active_model': 'cashbook.book',
}):
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'),
}])
Party.create([{
'name': 'GA NR00002168 BLZ10000000 0',
'addresses':[('create', [{}])],
}, {
'name': 'Foodshop Zehlendorf',
'addresses':[('create', [{}])],
}, {
'name': 'Opening Balance',
'addresses':[('create', [{}])],
}])
Category.create([{
'name':'Bargeld',
'cattype': 'in',
}, {
'name': 'S-Giro',
'cattype': 'in',
}, {
'name': 'Lebensmittel',
'cattype': 'out',
}])
(sess_id, start_state, end_state) = ImportWiz.create()
w_obj = ImportWiz(sess_id)
self.assertEqual(start_state, 'start')
self.assertEqual(end_state, 'end')
# run start
result = ImportWiz.execute(sess_id, {}, start_state)
self.assertEqual(list(result.keys()), ['view'])
self.assertEqual(result['view']['defaults']['company'], company.id)
r1 = {}
r1['file_'] = qif_types.encode('utf8')
r1['company'] = company.id
r1['book'] = book.id
w_obj.start.file_ = r1['file_']
w_obj.start.company = company.id
w_obj.start.book = book.id
result = ImportWiz.execute(sess_id, {'start': r1}, 'readf')
self.assertEqual(list(result.keys()), ['view'])
self.assertEqual(result['view']['defaults']['company'], company.id)
self.assertEqual(result['view']['defaults']['info'],
"""The following transactionen are now imported:
Credit: usd297.12
Debit: usd56.37
Balance: usd240.75
Number of transactions: 3""")
r1 = {
'company': company.id,
'book': book.id,
}
result = ImportWiz.execute(sess_id, {'showinfo': r1}, 'importf')
self.assertEqual(list(result.keys()), [])
ImportWiz.delete(sess_id)
self.assertEqual(len(book.lines), 3)
self.assertEqual(book.lines[0].rec_name, '12/04/2013|Rev|7.12 usd|- [Bargeld]')
self.assertEqual(book.lines[1].rec_name, '12/05/2013|Rev|290.00 usd|05.12/06.42UHR TT TELTOW [S-Giro]')
self.assertEqual(book.lines[2].rec_name, '12/05/2013|Exp|-56.37 usd|some food [Lebensmittel]')
self.assertEqual(Book.export_as_qif(book), """!Type:Bank
D12/04/2013
T7.12
CX
POpening Balance
LBargeld
^
D12/05/2013
T290.00
CX
PGA NR00002168 BLZ10000000 0
LS-Giro
M05.12/06.42UHR TT TELTOW
^
D12/05/2013
T-56.37
CX
PFoodshop Zehlendorf
LLebensmittel
Msome food
^""")
# end PartyTestCase

View file

@ -1,7 +1,8 @@
[tryton]
version=6.0.1
version=6.0.2
depends:
cashbook
xml:
message.xml
qif_import_wiz.xml
qif_export.xml

View file

@ -1 +1 @@
cashbook;6.0.1;6.0.999;mds
cashbook;6.0.4;6.0.999;mds

View file

@ -5,9 +5,12 @@ full copyright notices and license terms. -->
<form col="2">
<label name="company"/>
<field name="company"/>
<label name="book"/>
<field name="book"/>
<group col="1" name="info" colspan="2" string="Information" yexpand="1">
<field name="info"/>
</group>
<field name="allowimport"/>
</form>

View file

@ -5,6 +5,8 @@ full copyright notices and license terms. -->
<form col="2">
<label name="company"/>
<field name="company"/>
<label name="book"/>
<field name="book"/>
<label name="file_"/>
<field name="file_"/>