Compare commits
29 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
217c992662 | ||
![]() |
a2a761c979 | ||
![]() |
ca3b9de32c | ||
![]() |
5931736b16 | ||
![]() |
761083cbbe | ||
![]() |
da211efd93 | ||
![]() |
b4a02d92d6 | ||
![]() |
978823faf7 | ||
![]() |
492befca04 | ||
![]() |
0c2bae0663 | ||
![]() |
70c4741c9d | ||
![]() |
eb8c1baa1b | ||
![]() |
803262e202 | ||
![]() |
c5b3277622 | ||
![]() |
3796c7b7bc | ||
![]() |
5544433b81 | ||
![]() |
e56b4f0cf8 | ||
![]() |
40ee9ed169 | ||
![]() |
63108a34eb | ||
![]() |
5216cecba7 | ||
![]() |
491678da69 | ||
![]() |
e9cde133c2 | ||
![]() |
8bb396f1d8 | ||
![]() |
78d02a60b7 | ||
![]() |
a26e7119eb | ||
![]() |
be9c584fea | ||
![]() |
ac9400c085 | ||
![]() |
cdc0dfa49d | ||
![]() |
3eb4aebe0a |
25 changed files with 2522 additions and 361 deletions
22
README.rst
22
README.rst
|
@ -14,6 +14,28 @@ Requires
|
|||
Changes
|
||||
=======
|
||||
|
||||
*6.0.5 - 06.06.2023*
|
||||
|
||||
- optimize code
|
||||
|
||||
*6.0.4 - 14.02.2023*
|
||||
|
||||
- fix: test
|
||||
|
||||
*6.0.3 - 13.09.2022*
|
||||
|
||||
- add: import of split-bookings
|
||||
- updt: transfers
|
||||
|
||||
*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
|
||||
|
|
11
__init__.py
11
__init__.py
|
@ -5,16 +5,25 @@
|
|||
|
||||
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_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
46
book.py
Normal 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.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 'Bank' not in qif_content.keys():
|
||||
return None
|
||||
|
||||
(to_create, msg_list, fail_cnt) = \
|
||||
QifTool.convert_transactions_to_create(
|
||||
book,
|
||||
QifTool.qif_read_transactions(qif_content['Bank']))
|
||||
if fail_cnt == 0:
|
||||
Book2.write(*[
|
||||
[book],
|
||||
{
|
||||
'lines': [('create', to_create)],
|
||||
}])
|
||||
return [book]
|
||||
return None
|
||||
|
||||
# end Category
|
57
category.py
57
category.py
|
@ -3,13 +3,28 @@
|
|||
# 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 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():
|
||||
if 'Cat' not 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
|
||||
|
|
94
locale/de.po
94
locale/de.po
|
@ -10,6 +10,74 @@ 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\nAusgaben: %(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' was not found."
|
||||
msgstr "Die Kategorie '%(catname)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' of the import, several categories were found in the system. Use: '%(catname2)s'"
|
||||
msgstr "Für die Kategorie '%(catname1)s' des Imports wurden mehrere Kategorien im System gefunden. Verwende: '%(catname2)s'"
|
||||
|
||||
msgctxt "model:ir.message,text:mds_import_many_books_found"
|
||||
msgid "For the cashbook '%(bookname1)s' of the import, several cashbooks were found in the system. Use: '%(bookname2)s'"
|
||||
msgstr "Für das Kassenbuch '%(bookname1)s' des Imports wurden mehrere Kassenbücher im System gefunden. Verwende: '%(bookname2)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:"
|
||||
|
||||
msgctxt "model:ir.message,text:mds_import_no_category"
|
||||
msgid "No category has been assigned for transaction '%(trdata)s'."
|
||||
msgstr "Für die Transaktion '%(trdata)s' wurde keine Kategorie zugeordnet."
|
||||
|
||||
msgctxt "model:ir.message,text:mds_import_no_account"
|
||||
msgid "No cashbook has been assigned for transaction '%(trdata)s'."
|
||||
msgstr "Für die Transaktion '%(trdata)s' wurde kein Kassenbuch zugeordnet."
|
||||
|
||||
msgctxt "model:ir.message,text:mds_import_book_notfound"
|
||||
msgid "The cashbook '%(bookname)s' was not found."
|
||||
msgstr "Das Kassenbuch '%(bookname)s' wurde nicht gefunden."
|
||||
|
||||
msgctxt "model:ir.message,text:mds_import_category_not_match"
|
||||
msgid "The category '%(catname)s' of the split booking does not match the bookingtype '%(bktype)s' (data: '%(data)s')"
|
||||
msgstr "Die Kategorie '%(catname)s' der Splitbuchung paßt nicht zum Buchungstyp '%(bktype)s' (Daten: '%(data)s')"
|
||||
|
||||
msgctxt "model:ir.message,text:msg_ignore_null_booking"
|
||||
msgid "Ignore empty booking, no category, amount zero: %(trinfo)s"
|
||||
msgstr "Ignoriere leere Buchung, keine Kategorie, Betrag Null: %(trinfo)s"
|
||||
|
||||
|
||||
#############
|
||||
# ir.action #
|
||||
|
@ -18,9 +86,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 +115,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 +130,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 +154,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"
|
||||
|
|
104
locale/en.po
104
locale/en.po
|
@ -2,10 +2,82 @@
|
|||
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' was not found."
|
||||
msgstr "The category '%(catname)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' of the import, several categories were found in the system. Use: '%(catname2)s'"
|
||||
msgstr "For the category '%(catname1)s' of the import, several categories were found in the system. Use: '%(catname2)s'"
|
||||
|
||||
msgctxt "model:ir.message,text:mds_import_many_books_found"
|
||||
msgid "For the cashbook '%(bookname1)s' of the import, several cashbooks were found in the system. Use: '%(bookname2)s'"
|
||||
msgstr "For the cashbook '%(bookname1)s' of the import, several cashbooks were found in the system. Use: '%(bookname2)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.message,text:mds_import_no_category"
|
||||
msgid "No category has been assigned for transaction '%(trdata)s'."
|
||||
msgstr "No category has been assigned for transaction '%(trdata)s'."
|
||||
|
||||
msgctxt "model:ir.message,text:mds_import_no_account"
|
||||
msgid "No cashbook has been assigned for transaction '%(trdata)s'."
|
||||
msgstr "No cashbook has been assigned for transaction '%(trdata)s'."
|
||||
|
||||
msgctxt "model:ir.message,text:mds_import_book_notfound"
|
||||
msgid "The cashbook '%(bookname)s' was not found."
|
||||
msgstr "The cashbook '%(bookname)s' was not found."
|
||||
|
||||
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 +90,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 +106,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"
|
||||
|
||||
|
|
50
message.xml
50
message.xml
|
@ -8,10 +8,58 @@ 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_book_notfound">
|
||||
<field name="text">The cashbook '%(bookname)s' was not found.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="mds_import_category_notfound">
|
||||
<field name="text">The category '%(catname)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' of the import, several categories were found in the system. Use: '%(catname2)s'</field>
|
||||
</record>
|
||||
<record model="ir.message" id="mds_import_many_books_found">
|
||||
<field name="text">For the cashbook '%(bookname1)s' of the import, several cashbooks were found in the system. Use: '%(bookname2)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>
|
||||
<record model="ir.message" id="mds_import_no_category">
|
||||
<field name="text">No category has been assigned for transaction '%(trdata)s'.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="mds_import_no_account">
|
||||
<field name="text">No cashbook has been assigned for transaction '%(trdata)s'.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="mds_import_category_not_match">
|
||||
<field name="text">The category '%(catname)s' of the split booking does not match the bookingtype '%(bktype)s' (data: '%(data)s')</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_ignore_null_booking">
|
||||
<field name="text">Ignore empty booking, no category, amount zero: %(trinfo)s</field>
|
||||
</record>
|
||||
|
||||
|
||||
</data>
|
||||
</tryton>
|
||||
|
|
63
qif_export.py
Normal file
63
qif_export.py
Normal file
|
@ -0,0 +1,63 @@
|
|||
# -*- 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
37
qif_export.xml
Normal 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>
|
|
@ -7,18 +7,26 @@ from trytond.transaction import Transaction
|
|||
from trytond.pool import Pool
|
||||
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
|
||||
|
||||
|
||||
class ImportQifWizardStart(ModelView):
|
||||
'Import QIF-File'
|
||||
__name__ = 'cashbook_dataexchange.qif_imp_wiz.start'
|
||||
|
||||
company = fields.Many2One(model_name='company.company',
|
||||
company = fields.Many2One(
|
||||
model_name='company.company',
|
||||
string="Company", required=True,
|
||||
states={'invisible': True})
|
||||
file_ = fields.Binary(string="QIF-File", required=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')
|
||||
|
||||
@classmethod
|
||||
|
@ -32,8 +40,15 @@ class ImportQifWizardInfo(ModelView):
|
|||
'Import QIF-File'
|
||||
__name__ = 'cashbook_dataexchange.qif_imp_wiz.info'
|
||||
|
||||
company = fields.Many2One(model_name='company.company',
|
||||
string="Company", required=True,
|
||||
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)
|
||||
|
||||
|
@ -45,28 +60,49 @@ class ImportQifWizard(Wizard):
|
|||
__name__ = 'cashbook_dataexchange.qif_imp_wiz'
|
||||
|
||||
start_state = 'start'
|
||||
start = StateView(model_name='cashbook_dataexchange.qif_imp_wiz.start', \
|
||||
view='cashbook_dataexchange.qif_imp_wiz_start_form', \
|
||||
start = StateView(
|
||||
model_name='cashbook_dataexchange.qif_imp_wiz.start',
|
||||
view='cashbook_dataexchange.qif_imp_wiz_start_form',
|
||||
buttons=[
|
||||
Button(string='Cancel', state='end', icon='tryton-cancel'),
|
||||
Button(string='Read File', state='readf', icon='tryton-forward', default=True),
|
||||
])
|
||||
showinfo = StateView(model_name='cashbook_dataexchange.qif_imp_wiz.info', \
|
||||
view='cashbook_dataexchange.qif_imp_wiz_info_form', \
|
||||
Button(
|
||||
string='Read File', state='readf',
|
||||
icon='tryton-forward', default=True)])
|
||||
showinfo = StateView(
|
||||
model_name='cashbook_dataexchange.qif_imp_wiz.info',
|
||||
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)})])
|
||||
|
||||
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 +110,127 @@ 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():
|
||||
|
||||
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(name1)
|
||||
name_lst.append(record['name'])
|
||||
current_name = '/'.join(name_lst)
|
||||
names.append(current_name)
|
||||
names.extend(get_catlist(catlist[name1]['childs'], 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')
|
||||
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(
|
||||
self.start.book,
|
||||
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'] in ['out', 'mvout', 'spout']])
|
||||
credit = sum([
|
||||
x['amount'] for x in to_create
|
||||
if x['bookingtype'] in ['in', 'mvin', 'spin']])
|
||||
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 +239,10 @@ class ImportQifWizard(Wizard):
|
|||
"""
|
||||
pool = Pool()
|
||||
Category = pool.get('cashbook.category')
|
||||
Book = pool.get('cashbook.book')
|
||||
Line = pool.get('cashbook.line')
|
||||
Party = pool.get('party.party')
|
||||
QifTool = pool.get('cashbook_dataexchange.qiftool')
|
||||
|
||||
model = Transaction().context.get('active_model', '')
|
||||
file_content = None
|
||||
|
@ -127,9 +251,22 @@ class ImportQifWizard(Wizard):
|
|||
|
||||
if model == 'cashbook.category':
|
||||
if file_content:
|
||||
records = Category.create_from_qif(file_content)
|
||||
|
||||
Category.create_from_qif(file_content)
|
||||
elif model == 'cashbook.book':
|
||||
if file_content:
|
||||
Book.create_from_qif(self.showinfo.book, file_content)
|
||||
lines = Line.search([
|
||||
('cashbook.id', '=', self.showinfo.book.id),
|
||||
('state', '=', 'edit')])
|
||||
if len(lines) > 0:
|
||||
Line.wfcheck(lines)
|
||||
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
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
557
qiftool.py
557
qiftool.py
|
@ -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):
|
||||
|
@ -26,7 +30,7 @@ class QifTool(Model):
|
|||
if current_type is None:
|
||||
continue
|
||||
|
||||
if not current_type in blocks.keys():
|
||||
if current_type not in blocks.keys():
|
||||
blocks[current_type] = []
|
||||
blocks[current_type].append(line.strip())
|
||||
|
||||
|
@ -34,6 +38,542 @@ 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, account
|
||||
if line_txt.startswith('[') and line_txt.endswith(']'):
|
||||
booking['split'].append({
|
||||
'account': line_txt[1:-1],
|
||||
})
|
||||
else:
|
||||
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,
|
||||
digits=book.currency.digits),
|
||||
})
|
||||
# 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,
|
||||
digits=book.currency.digits),
|
||||
})
|
||||
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_account_by_name(cls, book, account_name):
|
||||
""" find cashbook
|
||||
"""
|
||||
Book = Pool().get('cashbook.book')
|
||||
|
||||
book_obj = None
|
||||
msg_txt = None
|
||||
books = Book.search([
|
||||
('name', '=', account_name),
|
||||
('owner.id', '=', book.owner.id),
|
||||
('id', '!=', book.id),
|
||||
])
|
||||
if len(books) == 1:
|
||||
book_obj = books[0]
|
||||
elif len(books) == 0:
|
||||
msg_txt = gettext(
|
||||
'cashbook_dataexchange.mds_import_book_notfound',
|
||||
bookname=account_name,
|
||||
)
|
||||
else:
|
||||
msg_txt = gettext(
|
||||
'cashbook_dataexchange.mds_import_many_books_found',
|
||||
bookname1=account_name,
|
||||
bookname2=books[0].rec_name,
|
||||
)
|
||||
book_obj = books[0]
|
||||
return (book_obj, msg_txt)
|
||||
|
||||
@classmethod
|
||||
def get_category_by_name(cls, company, catname):
|
||||
""" find category
|
||||
"""
|
||||
Category = Pool().get('cashbook.category')
|
||||
|
||||
cat_obj = None
|
||||
msg_txt = None
|
||||
categories = Category.search([
|
||||
('rec_name', '=', catname.replace(':', '/')),
|
||||
('company.id', '=', company.id),
|
||||
])
|
||||
if len(categories) == 1:
|
||||
cat_obj = categories[0]
|
||||
elif len(categories) == 0:
|
||||
msg_txt = gettext(
|
||||
'cashbook_dataexchange.mds_import_category_notfound',
|
||||
catname=catname,
|
||||
)
|
||||
else:
|
||||
msg_txt = gettext(
|
||||
'cashbook_dataexchange.mds_import_many_categories_found',
|
||||
catname1=catname,
|
||||
catname2='%(name)s [%(type)s]' % {
|
||||
'name': categories[0].rec_name,
|
||||
'type': categories[0].cattype,
|
||||
},
|
||||
)
|
||||
cat_obj = categories[0]
|
||||
return (cat_obj, 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 is 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 check_counter_transaction(cls, book, line):
|
||||
""" check if planned transaction was already inserted by
|
||||
import to target-cashbook
|
||||
"""
|
||||
Line = Pool().get('cashbook.line')
|
||||
|
||||
if Line.search_count([
|
||||
('cashbook.id', '=', book.id),
|
||||
('booktransf.id', '=', line['booktransf']),
|
||||
('date', '=', line['date']),
|
||||
# ('description', '=', line['description']),
|
||||
('amount', '=', line['amount']),
|
||||
('bookingtype', '=', line['bookingtype']),
|
||||
]) > 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def get_category_account(cls, book, transaction):
|
||||
""" get category or account
|
||||
"""
|
||||
cat_name = transaction.get('category', None)
|
||||
account_name = transaction.get('account', None)
|
||||
msg_list = []
|
||||
fail_cnt = 0
|
||||
category = account = None
|
||||
if cat_name is not None:
|
||||
(category, msg_txt) = cls.get_category_by_name(
|
||||
book.company, cat_name)
|
||||
if category is None:
|
||||
msg_list.append(msg_txt)
|
||||
fail_cnt += 1
|
||||
elif account_name is not None:
|
||||
(account, msg_txt) = cls.get_account_by_name(book, account_name)
|
||||
if account is None:
|
||||
msg_list.append(msg_txt)
|
||||
fail_cnt += 1
|
||||
return (category, account, msg_list, fail_cnt)
|
||||
|
||||
@classmethod
|
||||
def convert_transactions_to_create(
|
||||
cls, book, transactions, split2edit=True):
|
||||
""" convert read transactions to create-command
|
||||
split2edit: True = split-bookings are 'edit', False = dont change
|
||||
"""
|
||||
def updt_description(descr_txt):
|
||||
""" repair line breaks
|
||||
"""
|
||||
if descr_txt is None:
|
||||
return None
|
||||
return descr_txt.replace('\\n', '\n')
|
||||
|
||||
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 'description' in line.keys():
|
||||
line['description'] = updt_description(line['description'])
|
||||
|
||||
(category, account, msg_lst2, fail_cnt2) = \
|
||||
cls.get_category_account(book, transaction)
|
||||
msg_list.extend(msg_lst2)
|
||||
if fail_cnt2 > 0:
|
||||
fail_cnt += fail_cnt2
|
||||
continue
|
||||
|
||||
if category:
|
||||
cat_type = category.cattype
|
||||
line['category'] = category.id
|
||||
|
||||
if cat_type == 'out':
|
||||
# amount of usually out-transaction are negative in QIF,
|
||||
# if its a return-transaction it should be positive
|
||||
line['amount'] = line['amount'].copy_negate()
|
||||
|
||||
line['bookingtype'] = cat_type
|
||||
if len(transaction['split']) > 0:
|
||||
line['bookingtype'] = {
|
||||
'in': 'spin',
|
||||
'out': 'spout',
|
||||
}[cat_type]
|
||||
elif account:
|
||||
if line['amount'] < Decimal('0.0'):
|
||||
line['bookingtype'] = 'mvout'
|
||||
line['amount'] = line['amount'].copy_negate()
|
||||
else:
|
||||
line['bookingtype'] = 'mvin'
|
||||
|
||||
line['booktransf'] = account.id
|
||||
descr_lst = [transaction.get('party', '-')]
|
||||
if 'description' in line.keys():
|
||||
descr_lst.append(line['description'])
|
||||
if 'party' in transaction.keys():
|
||||
del transaction['party']
|
||||
line['description'] = '; '.join(descr_lst)
|
||||
line['state'] = 'edit'
|
||||
if cls.check_counter_transaction(book, line) is True:
|
||||
# counter-transaction already exists
|
||||
continue
|
||||
else:
|
||||
# transaction: no category, no account - ignore?
|
||||
if line.get('amount', Decimal('0.0')) == Decimal('0.0'):
|
||||
# no amount --> ignore!
|
||||
tr_info = {'trdate': '-', 'amount': '-'}
|
||||
if 'date' in transaction.keys():
|
||||
tr_info['trdate'] = Report.format_date(
|
||||
transaction['date'], None)
|
||||
if 'amount' in transaction.keys():
|
||||
tr_info['amount'] = Report.format_currency(
|
||||
transaction['amount'],
|
||||
None,
|
||||
book.currency)
|
||||
tr_info['descr'] = transaction.get('description', '-')
|
||||
msg_list.append(gettext(
|
||||
'cashbook_dataexchange.msg_ignore_null_booking',
|
||||
trinfo='%(trdate)s, %(amount)s, %(descr)s' % tr_info,
|
||||
))
|
||||
continue
|
||||
|
||||
# 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)
|
||||
|
||||
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']:
|
||||
(category, account, msg_lst2, fail_cnt2) = \
|
||||
cls.get_category_account(book, sp_line)
|
||||
msg_list.extend(msg_lst2)
|
||||
if fail_cnt2 > 0:
|
||||
fail_cnt += fail_cnt2
|
||||
continue
|
||||
|
||||
split_line = {
|
||||
'amount': sp_line['amount']
|
||||
if line['bookingtype'].endswith('in')
|
||||
else sp_line['amount'].copy_negate(),
|
||||
'description': updt_description(
|
||||
sp_line.get('description', None)),
|
||||
}
|
||||
|
||||
if category:
|
||||
# category match to bookingtype?
|
||||
if ((category.cattype == 'in') and
|
||||
line['bookingtype'].endswith('out')) or \
|
||||
((category.cattype == 'out') and
|
||||
line['bookingtype'].endswith('in')):
|
||||
msg_list.append(gettext(
|
||||
'cashbook_dataexchange.' +
|
||||
'mds_import_category_not_match',
|
||||
catname='%s [%s]' % (
|
||||
category.rec_name, category.cattype),
|
||||
bktype=line['bookingtype'],
|
||||
data=str(transaction)))
|
||||
fail_cnt += 1
|
||||
continue
|
||||
split_line['splittype'] = 'cat'
|
||||
split_line['category'] = category.id
|
||||
elif account:
|
||||
split_line['splittype'] = 'tr'
|
||||
split_line['booktransf'] = account.id
|
||||
else:
|
||||
continue
|
||||
|
||||
split_lines.append(split_line)
|
||||
|
||||
if len(split_lines) > 0:
|
||||
line['splitlines'] = [('create', split_lines)]
|
||||
|
||||
if split2edit is True:
|
||||
if 'splitlines' in line.keys():
|
||||
line['state'] = 'edit'
|
||||
|
||||
# check data
|
||||
if line['bookingtype'] in ['in', 'out']:
|
||||
if line.get('category', None) is None:
|
||||
msg_list.append(gettext(
|
||||
'cashbook_dataexchange.mds_import_no_category',
|
||||
trdata=str(transaction)))
|
||||
fail_cnt += 1
|
||||
elif line['bookingtype'] in ['mvin', 'mvout']:
|
||||
if line.get('booktransf', None) is None:
|
||||
msg_list.append(gettext(
|
||||
'cashbook_dataexchange.mds_import_no_account',
|
||||
trdata=str(transaction)))
|
||||
fail_cnt += 1
|
||||
|
||||
to_create.append(line)
|
||||
return (to_create, msg_list, fail_cnt)
|
||||
|
||||
@classmethod
|
||||
def qif_read_categories(cls, catdata):
|
||||
""" read categories from text
|
||||
|
@ -75,7 +615,20 @@ class QifTool(Model):
|
|||
cattype = 'in'
|
||||
else:
|
||||
raise ValueError('invalid line: %s (%s)' % (line, cattxt))
|
||||
categories[cattype] = add_category(categories[cattype], catname, cattype)
|
||||
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
240
report/exort.fods
Normal 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="'Liberation Sans'" style:font-family-generic="swiss" style:font-pitch="variable"/>
|
||||
<style:font-face style:name="DejaVu Sans" svg:font-family="'DejaVu Sans'" 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="'Microsoft YaHei'" style:font-family-generic="system" style:font-pitch="variable"/>
|
||||
<style:font-face style:name="Segoe UI" svg:font-family="'Segoe UI'" 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="'Microsoft YaHei'" 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>
|
36
setup.py
36
setup.py
|
@ -2,7 +2,7 @@
|
|||
"""
|
||||
|
||||
# Always prefer setuptools over distutils
|
||||
from setuptools import setup, find_packages
|
||||
from setuptools import setup
|
||||
# To use a consistent encoding
|
||||
from codecs import open
|
||||
from os import path
|
||||
|
@ -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():
|
||||
|
@ -51,23 +51,27 @@ for dep in info.get('depends', []):
|
|||
prefix = modversion[dep]['prefix']
|
||||
|
||||
if len(modversion[dep]['max']) > 0:
|
||||
requires.append('%s_%s >= %s, <= %s' %
|
||||
(prefix, dep, modversion[dep]['min'], modversion[dep]['max']))
|
||||
requires.append('%s_%s >= %s, <= %s' % (
|
||||
prefix, dep, modversion[dep]['min'],
|
||||
modversion[dep]['max']))
|
||||
else:
|
||||
requires.append('%s_%s >= %s' %
|
||||
(prefix, dep, modversion[dep]['min']))
|
||||
requires.append('%s_%s >= %s' % (
|
||||
prefix, dep, modversion[dep]['min']))
|
||||
else:
|
||||
requires.append('%s_%s >= %s.%s, < %s.%s' %
|
||||
('trytond', dep, major_version, minor_version,
|
||||
requires.append('%s_%s >= %s.%s, < %s.%s' % (
|
||||
'trytond', dep, major_version, minor_version,
|
||||
major_version, minor_version + 1))
|
||||
requires.append('trytond >= %s.%s, < %s.%s' %
|
||||
(major_version, minor_version, major_version, minor_version + 1))
|
||||
requires.append('trytond >= %s.%s, < %s.%s' % (
|
||||
major_version, minor_version, major_version, minor_version + 1))
|
||||
|
||||
setup(name='%s_%s' % (PREFIX, MODULE),
|
||||
setup(
|
||||
name='%s_%s' % (PREFIX, MODULE),
|
||||
version=info.get('version', '0.0.1'),
|
||||
description='Tryton module to add import/export to cashbook.',
|
||||
long_description=long_description,
|
||||
long_description_content_type='text/x-rst',
|
||||
url='https://www.m-ds.de/',
|
||||
download_url='https://scmdev.m-ds.de/Tryton/Extra/cashbook_dataexchange',
|
||||
author='martin-data services',
|
||||
author_email='service@m-ds.de',
|
||||
license='GPL-3',
|
||||
|
@ -87,19 +91,19 @@ setup(name='%s_%s' % (PREFIX, MODULE),
|
|||
'License :: OSI Approved :: GNU General Public License (GPL)',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
'Programming Language :: Python :: 3.8',
|
||||
'Programming Language :: Python :: 3.9',
|
||||
],
|
||||
|
||||
keywords='tryton cashbook import export',
|
||||
package_dir={'trytond.modules.%s' % MODULE: '.'},
|
||||
packages=[
|
||||
'trytond.modules.%s' % MODULE,
|
||||
],
|
||||
package_data={
|
||||
'trytond.modules.%s' % MODULE: (info.get('xml', [])
|
||||
+ ['tryton.cfg', 'locale/*.po', 'tests/*.py',
|
||||
'view/*.xml', 'versiondep.txt', 'README.rst']),
|
||||
'trytond.modules.%s' % MODULE: (info.get('xml', []) + [
|
||||
'tryton.cfg', 'locale/*.po', 'tests/*.py',
|
||||
'report/*.fods', 'view/*.xml',
|
||||
'versiondep.txt', 'README.rst']),
|
||||
},
|
||||
|
||||
install_requires=requires,
|
||||
zip_safe=False,
|
||||
entry_points="""
|
||||
|
|
|
@ -1,23 +1,18 @@
|
|||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
# -*- 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.
|
||||
|
||||
import trytond.tests.test_tryton
|
||||
import unittest
|
||||
|
||||
from trytond.modules.cashbook_dataexchange.tests.test_category import CategoryTestCase
|
||||
from .test_module import CashbookExchangeTestCase
|
||||
|
||||
__all__ = ['suite']
|
||||
|
||||
|
||||
class CashbookExchangeTestCase(\
|
||||
CategoryTestCase,\
|
||||
):
|
||||
'Test cashbook exchange module'
|
||||
module = 'cashbook_dataexchange'
|
||||
|
||||
# end CashbookExchangeTestCase
|
||||
|
||||
def suite():
|
||||
suite = trytond.tests.test_tryton.suite()
|
||||
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(CashbookExchangeTestCase))
|
||||
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(
|
||||
CashbookExchangeTestCase))
|
||||
return suite
|
||||
|
|
507
tests/category.py
Normal file
507
tests/category.py
Normal file
|
@ -0,0 +1,507 @@
|
|||
# -*- 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 with_transaction
|
||||
from trytond.pool import Pool
|
||||
from trytond.transaction import Transaction
|
||||
from .qifdata import qif_types
|
||||
|
||||
|
||||
class CategoryTestCase(object):
|
||||
'Test cashbook categoy module'
|
||||
module = 'cashbook_dataexchange'
|
||||
|
||||
@with_transaction()
|
||||
def test_wiz_import_category(self):
|
||||
""" create categories by run wizard
|
||||
"""
|
||||
pool = Pool()
|
||||
Category = pool.get('cashbook.category')
|
||||
ImportWiz = pool.get(
|
||||
'cashbook_dataexchange.qif_imp_wiz', type='wizard')
|
||||
|
||||
company = self.prep_company()
|
||||
with Transaction().set_context({
|
||||
'company': company.id,
|
||||
'active_model': 'cashbook.category'}):
|
||||
(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 categories are now imported:\n
|
||||
Gehalt (in)
|
||||
Gehalt/Zulagen (in)
|
||||
|
||||
Telekommunikation (out)
|
||||
Telekommunikation/Online-Dienste (out)
|
||||
Telekommunikation/Telefon (out)
|
||||
Telekommunikation/Telefon/Test1 (out)
|
||||
Telekommunikation/Fernsehen (out)
|
||||
Telefon (out)
|
||||
Telefon/Telco1-Tablett (out)
|
||||
Telefon/Telco2-Handy (out)
|
||||
Telefon/Telco3 (out)
|
||||
Fernsehen (out)
|
||||
Fernsehen/TV-Company (out)
|
||||
Fernsehen/GEZ (out)
|
||||
Lebensmittel (out)""")
|
||||
|
||||
r1 = {'company': company.id}
|
||||
result = ImportWiz.execute(sess_id, {'showinfo': r1}, 'importf')
|
||||
self.assertEqual(list(result.keys()), [])
|
||||
|
||||
ImportWiz.delete(sess_id)
|
||||
|
||||
records = Category.search([], order=[('rec_name', 'ASC')])
|
||||
self.assertEqual(len(records), 15)
|
||||
|
||||
self.assertEqual(records[0].rec_name, 'Fernsehen')
|
||||
self.assertEqual(records[1].rec_name, 'Fernsehen/GEZ')
|
||||
self.assertEqual(records[2].rec_name, 'Fernsehen/TV-Company')
|
||||
self.assertEqual(records[3].rec_name, 'Gehalt')
|
||||
self.assertEqual(records[4].rec_name, 'Gehalt/Zulagen')
|
||||
self.assertEqual(records[5].rec_name, 'Lebensmittel')
|
||||
self.assertEqual(records[6].rec_name, 'Telefon')
|
||||
self.assertEqual(records[7].rec_name, 'Telefon/Telco1-Tablett')
|
||||
self.assertEqual(records[8].rec_name, 'Telefon/Telco2-Handy')
|
||||
self.assertEqual(records[9].rec_name, 'Telefon/Telco3')
|
||||
self.assertEqual(records[10].rec_name, 'Telekommunikation')
|
||||
self.assertEqual(
|
||||
records[11].rec_name, 'Telekommunikation/Fernsehen')
|
||||
self.assertEqual(
|
||||
records[12].rec_name, 'Telekommunikation/Online-Dienste')
|
||||
self.assertEqual(
|
||||
records[13].rec_name, 'Telekommunikation/Telefon')
|
||||
self.assertEqual(
|
||||
records[14].rec_name, 'Telekommunikation/Telefon/Test1')
|
||||
|
||||
@with_transaction()
|
||||
def test_category_create_by_qif_emptydb(self):
|
||||
""" create categories by import a qif-file
|
||||
"""
|
||||
pool = Pool()
|
||||
Category = pool.get('cashbook.category')
|
||||
|
||||
company = self.prep_company()
|
||||
with Transaction().set_context({
|
||||
'company': company.id}):
|
||||
records = Category.create_from_qif(qif_types)
|
||||
|
||||
records = Category.search([], order=[('rec_name', 'ASC')])
|
||||
self.assertEqual(len(records), 15)
|
||||
|
||||
self.assertEqual(records[0].rec_name, 'Fernsehen')
|
||||
self.assertEqual(records[1].rec_name, 'Fernsehen/GEZ')
|
||||
self.assertEqual(records[2].rec_name, 'Fernsehen/TV-Company')
|
||||
self.assertEqual(records[3].rec_name, 'Gehalt')
|
||||
self.assertEqual(records[4].rec_name, 'Gehalt/Zulagen')
|
||||
self.assertEqual(records[5].rec_name, 'Lebensmittel')
|
||||
self.assertEqual(records[6].rec_name, 'Telefon')
|
||||
self.assertEqual(records[7].rec_name, 'Telefon/Telco1-Tablett')
|
||||
self.assertEqual(records[8].rec_name, 'Telefon/Telco2-Handy')
|
||||
self.assertEqual(records[9].rec_name, 'Telefon/Telco3')
|
||||
self.assertEqual(records[10].rec_name, 'Telekommunikation')
|
||||
self.assertEqual(
|
||||
records[11].rec_name, 'Telekommunikation/Fernsehen')
|
||||
self.assertEqual(
|
||||
records[12].rec_name, 'Telekommunikation/Online-Dienste')
|
||||
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,
|
||||
some categories exists already
|
||||
"""
|
||||
pool = Pool()
|
||||
Category = pool.get('cashbook.category')
|
||||
|
||||
company = self.prep_company()
|
||||
with Transaction().set_context({
|
||||
'company': company.id}):
|
||||
cat1, = Category.create([{
|
||||
'name': 'Telekommunikation',
|
||||
'cattype': 'out',
|
||||
'childs': [('create', [{
|
||||
'cattype': 'out',
|
||||
'name': 'Telefon',
|
||||
}])],
|
||||
}])
|
||||
|
||||
records = Category.search([])
|
||||
self.assertEqual(len(records), 2)
|
||||
self.assertEqual(records[0].rec_name, 'Telekommunikation')
|
||||
self.assertEqual(records[1].rec_name, 'Telekommunikation/Telefon')
|
||||
|
||||
Category.create_from_qif(qif_types)
|
||||
|
||||
records = Category.search([], order=[('rec_name', 'ASC')])
|
||||
self.assertEqual(len(records), 15)
|
||||
|
||||
self.assertEqual(records[0].rec_name, 'Fernsehen')
|
||||
self.assertEqual(records[1].rec_name, 'Fernsehen/GEZ')
|
||||
self.assertEqual(records[2].rec_name, 'Fernsehen/TV-Company')
|
||||
self.assertEqual(records[3].rec_name, 'Gehalt')
|
||||
self.assertEqual(records[4].rec_name, 'Gehalt/Zulagen')
|
||||
self.assertEqual(records[5].rec_name, 'Lebensmittel')
|
||||
self.assertEqual(records[6].rec_name, 'Telefon')
|
||||
self.assertEqual(records[7].rec_name, 'Telefon/Telco1-Tablett')
|
||||
self.assertEqual(records[8].rec_name, 'Telefon/Telco2-Handy')
|
||||
self.assertEqual(records[9].rec_name, 'Telefon/Telco3')
|
||||
self.assertEqual(records[10].rec_name, 'Telekommunikation')
|
||||
self.assertEqual(
|
||||
records[11].rec_name, 'Telekommunikation/Fernsehen')
|
||||
self.assertEqual(
|
||||
records[12].rec_name, 'Telekommunikation/Online-Dienste')
|
||||
self.assertEqual(
|
||||
records[13].rec_name, 'Telekommunikation/Telefon')
|
||||
self.assertEqual(
|
||||
records[14].rec_name, 'Telekommunikation/Telefon/Test1')
|
||||
|
||||
@with_transaction()
|
||||
def test_qiftool_split_types(self):
|
||||
""" split file-content by types
|
||||
"""
|
||||
QifTool = Pool().get('cashbook_dataexchange.qiftool')
|
||||
|
||||
result = QifTool.split_by_type(qif_types)
|
||||
self.assertEqual(len(result.keys()), 2)
|
||||
self.assertEqual(
|
||||
result['Cat'],
|
||||
'NGehalt\nI\n^\nNGehalt:Zulagen\nI\n^\nNTelekommunikation' +
|
||||
'\nE\n^\nNTelekommunikation:Online-Dienste\n' +
|
||||
'E\n^\nNTelekommunikation:Telefon\nE\n^\nN' +
|
||||
'Telekommunikation:Telefon:Test1\n' +
|
||||
'E\n^\nNTelefon:Telco1-Tablett\n' +
|
||||
'E\n^\nNTelefon:Telco2-Handy\nE\n^\nNTelefon:Telco3\nE\n^\n' +
|
||||
'NTelekommunikation:Fernsehen\nE\n^\nNFernsehen:TV-Company\nE\n' +
|
||||
'^\nNFernsehen:GEZ\nE\n^\nNLebensmittel\nE\n^')
|
||||
self.assertEqual(
|
||||
result['Bank'],
|
||||
'D04.12.2013\nT7,12\nCX\n' +
|
||||
'POpening Balance\nL[Bargeld]\n^\nD05.12.2013\nCX\nM05.12/' +
|
||||
'06.42UHR TT TELTOW\nT-29,00\n' +
|
||||
'PGA NR00002168 BLZ10000000 0\nL[S-Giro]\n^\nD05.12.2013' +
|
||||
'\nCX\nMsome food\nT-56,37\nPFoodshop Zehlendorf\n' +
|
||||
'LLebensmittel\n^\nD06.12.2013\nCX\nMreturn of bottles\n' +
|
||||
'T1,45\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()
|
||||
books = 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),
|
||||
}, {
|
||||
'name': 'S-Giro',
|
||||
'btype': types.id,
|
||||
'company': company.id,
|
||||
'currency': company.currency.id,
|
||||
'number_sequ': self.prep_sequence().id,
|
||||
'start_date': date(2010, 1, 1),
|
||||
}, {
|
||||
'name': 'Bargeld',
|
||||
'btype': types.id,
|
||||
'company': company.id,
|
||||
'currency': company.currency.id,
|
||||
'number_sequ': self.prep_sequence().id,
|
||||
'start_date': date(2010, 1, 1),
|
||||
}])
|
||||
self.assertEqual(books[0].name, 'Cash Book')
|
||||
self.assertEqual(books[1].name, 'S-Giro')
|
||||
self.assertEqual(books[2].name, 'Bargeld')
|
||||
self.assertEqual(books[0].btype.rec_name, 'CAS - Cash')
|
||||
self.assertEqual(books[1].btype.rec_name, 'CAS - Cash')
|
||||
self.assertEqual(books[2].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,
|
||||
}])
|
||||
|
||||
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\nT-29,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\n' +
|
||||
'EKlopapier\n' +
|
||||
'$-2,99\nSHaushaltschemie\nESagrotan\n$-3,49\n' +
|
||||
'S[S-Giro]\nEtransfer out\n$-3,49\n^\n')
|
||||
|
||||
(to_create, msg_txt, fail_cnt) = \
|
||||
QifTool.convert_transactions_to_create(books[0], tr_list)
|
||||
self.assertEqual(msg_txt, [])
|
||||
self.assertEqual(to_create, [{
|
||||
'date': date(2013, 12, 4),
|
||||
'amount': Decimal('7.12'),
|
||||
'state': 'edit',
|
||||
'bookingtype': 'mvin',
|
||||
'booktransf': books[2].id,
|
||||
'description': 'Opening Balance',
|
||||
}, {
|
||||
'date': date(2013, 12, 5),
|
||||
'amount': Decimal('29.00'),
|
||||
'state': 'edit',
|
||||
'bookingtype': 'mvout',
|
||||
'booktransf': books[1].id,
|
||||
'description':
|
||||
'GA NR00002168 BLZ10000000 0; 05.12/06.42UHR ' +
|
||||
'TT TELTOW',
|
||||
}, {
|
||||
'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', [{
|
||||
'splittype': 'cat',
|
||||
'amount': Decimal('49.36'),
|
||||
'description': 'Lebensmittel',
|
||||
'category': categories[0].id,
|
||||
}, {
|
||||
'splittype': 'cat',
|
||||
'amount': Decimal('2.99'),
|
||||
'description': 'Klopapier',
|
||||
'category': categories[2].id,
|
||||
}, {
|
||||
'splittype': 'cat',
|
||||
'amount': Decimal('3.49'),
|
||||
'description': 'Sagrotan',
|
||||
'category': categories[1].id,
|
||||
}, {
|
||||
'splittype': 'tr',
|
||||
'amount': Decimal('3.49'),
|
||||
'description': 'transfer out',
|
||||
'booktransf': books[1].id,
|
||||
}])],
|
||||
}])
|
||||
Book.write(*[
|
||||
[books[0]],
|
||||
{
|
||||
'lines': [('create', to_create)],
|
||||
}])
|
||||
self.assertEqual(len(books[0].lines), 4)
|
||||
self.assertEqual(books[0].balance, Decimal('-137.58'))
|
||||
|
||||
@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
|
||||
"""
|
||||
QifTool = Pool().get('cashbook_dataexchange.qiftool')
|
||||
|
||||
result = QifTool.qif_read_categories(
|
||||
'NGehalt\nI\n^\nNGehalt:Zulagen\nI\n^' +
|
||||
'NTelekommunikation\nE\n^\nNTelekommunikation:' +
|
||||
'Online-Dienste\nE\n^')
|
||||
self.assertEqual(result, {
|
||||
'in': {
|
||||
'Gehalt': {
|
||||
'type': 'in',
|
||||
'childs': {
|
||||
'Zulagen': {
|
||||
'type': 'in',
|
||||
'childs': {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'out': {
|
||||
'Telekommunikation': {
|
||||
'type': 'out',
|
||||
'childs': {
|
||||
'Online-Dienste': {
|
||||
'type': 'out',
|
||||
'childs': {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
# end CategoryTestCase
|
71
tests/party.py
Normal file
71
tests/party.py
Normal 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 trytond.tests.test_tryton import with_transaction
|
||||
from trytond.pool import Pool
|
||||
from trytond.transaction import Transaction
|
||||
from .qifdata import qif_types
|
||||
|
||||
|
||||
class PartyTestCase(object):
|
||||
'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
|
|
@ -54,7 +54,7 @@ L[Bargeld]
|
|||
D05.12.2013
|
||||
CX
|
||||
M05.12/06.42UHR TT TELTOW
|
||||
T290,00
|
||||
T-29,00
|
||||
PGA NR00002168 BLZ10000000 0
|
||||
L[S-Giro]
|
||||
^
|
||||
|
@ -65,6 +65,13 @@ T-56,37
|
|||
PFoodshop Zehlendorf
|
||||
LLebensmittel
|
||||
^
|
||||
D06.12.2013
|
||||
CX
|
||||
Mreturn of bottles
|
||||
T1,45
|
||||
PFoodshop Zehlendorf
|
||||
LLebensmittel
|
||||
^
|
||||
"""
|
||||
|
||||
qif_category = """!Type:Cat
|
||||
|
|
|
@ -1,226 +0,0 @@
|
|||
# -*- 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.tests.test_tryton import ModuleTestCase, with_transaction
|
||||
from trytond.pool import Pool
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.modules.cashbook.tests import CashbookTestCase
|
||||
from .qifdata import qif_category, qif_types
|
||||
|
||||
|
||||
class CategoryTestCase(CashbookTestCase):
|
||||
'Test cashbook categoy module'
|
||||
module = 'CashbookExchangeTestCase'
|
||||
|
||||
@with_transaction()
|
||||
def test_wiz_import_category(self):
|
||||
""" create categories by run wizard
|
||||
"""
|
||||
pool = Pool()
|
||||
Category = pool.get('cashbook.category')
|
||||
ImportWiz = pool.get('cashbook_dataexchange.qif_imp_wiz', type='wizard')
|
||||
|
||||
company = self.prep_company()
|
||||
with Transaction().set_context({
|
||||
'company': company.id,
|
||||
'active_model': 'cashbook.category',
|
||||
}):
|
||||
(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 categories are now imported:\\n
|
||||
Gehalt (in)
|
||||
Gehalt/Zulagen (in)
|
||||
|
||||
Telekommunikation (out)
|
||||
Telekommunikation/Online-Dienste (out)
|
||||
Telekommunikation/Telefon (out)
|
||||
Telekommunikation/Telefon/Test1 (out)
|
||||
Telekommunikation/Fernsehen (out)
|
||||
Telefon (out)
|
||||
Telefon/Telco1-Tablett (out)
|
||||
Telefon/Telco2-Handy (out)
|
||||
Telefon/Telco3 (out)
|
||||
Fernsehen (out)
|
||||
Fernsehen/TV-Company (out)
|
||||
Fernsehen/GEZ (out)
|
||||
Lebensmittel (out)""")
|
||||
|
||||
r1 = {'company': company.id}
|
||||
result = ImportWiz.execute(sess_id, {'showinfo': r1}, 'importf')
|
||||
self.assertEqual(list(result.keys()), [])
|
||||
|
||||
ImportWiz.delete(sess_id)
|
||||
|
||||
records = Category.search([], order=[('rec_name', 'ASC')])
|
||||
self.assertEqual(len(records), 15)
|
||||
|
||||
self.assertEqual(records[0].rec_name, 'Fernsehen')
|
||||
self.assertEqual(records[1].rec_name, 'Fernsehen/GEZ')
|
||||
self.assertEqual(records[2].rec_name, 'Fernsehen/TV-Company')
|
||||
self.assertEqual(records[3].rec_name, 'Gehalt')
|
||||
self.assertEqual(records[4].rec_name, 'Gehalt/Zulagen')
|
||||
self.assertEqual(records[5].rec_name, 'Lebensmittel')
|
||||
self.assertEqual(records[6].rec_name, 'Telefon')
|
||||
self.assertEqual(records[7].rec_name, 'Telefon/Telco1-Tablett')
|
||||
self.assertEqual(records[8].rec_name, 'Telefon/Telco2-Handy')
|
||||
self.assertEqual(records[9].rec_name, 'Telefon/Telco3')
|
||||
self.assertEqual(records[10].rec_name, 'Telekommunikation')
|
||||
self.assertEqual(records[11].rec_name, 'Telekommunikation/Fernsehen')
|
||||
self.assertEqual(records[12].rec_name, 'Telekommunikation/Online-Dienste')
|
||||
self.assertEqual(records[13].rec_name, 'Telekommunikation/Telefon')
|
||||
self.assertEqual(records[14].rec_name, 'Telekommunikation/Telefon/Test1')
|
||||
|
||||
@with_transaction()
|
||||
def test_category_create_by_qif_emptydb(self):
|
||||
""" create categories by import a qif-file
|
||||
"""
|
||||
pool = Pool()
|
||||
Category = pool.get('cashbook.category')
|
||||
|
||||
company = self.prep_company()
|
||||
with Transaction().set_context({
|
||||
'company': company.id,
|
||||
}):
|
||||
records = Category.create_from_qif(qif_types)
|
||||
|
||||
records = Category.search([], order=[('rec_name', 'ASC')])
|
||||
self.assertEqual(len(records), 15)
|
||||
|
||||
self.assertEqual(records[0].rec_name, 'Fernsehen')
|
||||
self.assertEqual(records[1].rec_name, 'Fernsehen/GEZ')
|
||||
self.assertEqual(records[2].rec_name, 'Fernsehen/TV-Company')
|
||||
self.assertEqual(records[3].rec_name, 'Gehalt')
|
||||
self.assertEqual(records[4].rec_name, 'Gehalt/Zulagen')
|
||||
self.assertEqual(records[5].rec_name, 'Lebensmittel')
|
||||
self.assertEqual(records[6].rec_name, 'Telefon')
|
||||
self.assertEqual(records[7].rec_name, 'Telefon/Telco1-Tablett')
|
||||
self.assertEqual(records[8].rec_name, 'Telefon/Telco2-Handy')
|
||||
self.assertEqual(records[9].rec_name, 'Telefon/Telco3')
|
||||
self.assertEqual(records[10].rec_name, 'Telekommunikation')
|
||||
self.assertEqual(records[11].rec_name, 'Telekommunikation/Fernsehen')
|
||||
self.assertEqual(records[12].rec_name, 'Telekommunikation/Online-Dienste')
|
||||
self.assertEqual(records[13].rec_name, 'Telekommunikation/Telefon')
|
||||
self.assertEqual(records[14].rec_name, 'Telekommunikation/Telefon/Test1')
|
||||
|
||||
@with_transaction()
|
||||
def test_category_create_by_qif_existing_categories(self):
|
||||
""" create categories by import a qif-file,
|
||||
some categories exists already
|
||||
"""
|
||||
pool = Pool()
|
||||
Category = pool.get('cashbook.category')
|
||||
|
||||
company = self.prep_company()
|
||||
with Transaction().set_context({
|
||||
'company': company.id,
|
||||
}):
|
||||
cat1, = Category.create([{
|
||||
'name': 'Telekommunikation',
|
||||
'cattype': 'out',
|
||||
'childs': [('create', [{
|
||||
'cattype': 'out',
|
||||
'name': 'Telefon',
|
||||
}])],
|
||||
}])
|
||||
|
||||
records = Category.search([])
|
||||
self.assertEqual(len(records), 2)
|
||||
self.assertEqual(records[0].rec_name, 'Telekommunikation')
|
||||
self.assertEqual(records[1].rec_name, 'Telekommunikation/Telefon')
|
||||
|
||||
records1 = Category.create_from_qif(qif_types)
|
||||
|
||||
records = Category.search([], order=[('rec_name', 'ASC')])
|
||||
self.assertEqual(len(records), 15)
|
||||
|
||||
self.assertEqual(records[0].rec_name, 'Fernsehen')
|
||||
self.assertEqual(records[1].rec_name, 'Fernsehen/GEZ')
|
||||
self.assertEqual(records[2].rec_name, 'Fernsehen/TV-Company')
|
||||
self.assertEqual(records[3].rec_name, 'Gehalt')
|
||||
self.assertEqual(records[4].rec_name, 'Gehalt/Zulagen')
|
||||
self.assertEqual(records[5].rec_name, 'Lebensmittel')
|
||||
self.assertEqual(records[6].rec_name, 'Telefon')
|
||||
self.assertEqual(records[7].rec_name, 'Telefon/Telco1-Tablett')
|
||||
self.assertEqual(records[8].rec_name, 'Telefon/Telco2-Handy')
|
||||
self.assertEqual(records[9].rec_name, 'Telefon/Telco3')
|
||||
self.assertEqual(records[10].rec_name, 'Telekommunikation')
|
||||
self.assertEqual(records[11].rec_name, 'Telekommunikation/Fernsehen')
|
||||
self.assertEqual(records[12].rec_name, 'Telekommunikation/Online-Dienste')
|
||||
self.assertEqual(records[13].rec_name, 'Telekommunikation/Telefon')
|
||||
self.assertEqual(records[14].rec_name, 'Telekommunikation/Telefon/Test1')
|
||||
|
||||
@with_transaction()
|
||||
def test_qiftool_split_types(self):
|
||||
""" split file-content by types
|
||||
"""
|
||||
QifTool = Pool().get('cashbook_dataexchange.qiftool')
|
||||
|
||||
result = QifTool.split_by_type(qif_types)
|
||||
self.assertEqual(len(result.keys()), 2)
|
||||
self.assertEqual(result['Cat'], 'NGehalt\nI\n^\nNGehalt:Zulagen\n'+
|
||||
'I\n^\nNTelekommunikation\nE\n^\nNTelekommunikation:Online-Dienste\n'+
|
||||
'E\n^\nNTelekommunikation:Telefon\nE\n^\nNTelekommunikation:Telefon:Test1\n'+
|
||||
'E\n^\nNTelefon:Telco1-Tablett\n'+
|
||||
'E\n^\nNTelefon:Telco2-Handy\nE\n^\nNTelefon:Telco3\nE\n^\n'+
|
||||
'NTelekommunikation:Fernsehen\nE\n^\nNFernsehen:TV-Company\nE\n'+
|
||||
'^\nNFernsehen:GEZ\nE\n^\nNLebensmittel\nE\n^')
|
||||
self.assertEqual(result['Bank'], 'D04.12.2013\nT7,12\nCX\nPOpening Balance\n'+
|
||||
'L[Bargeld]\n^\nD05.12.2013\nCX\nM05.12/06.42UHR TT TELTOW\nT290,00\n'+
|
||||
'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_read_categories(self):
|
||||
""" read category-data from text
|
||||
"""
|
||||
QifTool = Pool().get('cashbook_dataexchange.qiftool')
|
||||
|
||||
result = QifTool.qif_read_categories('NGehalt\nI\n^\nNGehalt:Zulagen\nI\n^'+
|
||||
'NTelekommunikation\nE\n^\nNTelekommunikation:Online-Dienste\nE\n^')
|
||||
self.assertEqual(result, {
|
||||
'in': {
|
||||
'Gehalt': {
|
||||
'type': 'in',
|
||||
'childs': {
|
||||
'Zulagen': {
|
||||
'type': 'in',
|
||||
'childs': {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'out': {
|
||||
'Telekommunikation': {
|
||||
'type': 'out',
|
||||
'childs': {
|
||||
'Online-Dienste': {
|
||||
'type': 'out',
|
||||
'childs': {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
# end CategoryTestCase
|
26
tests/test_module.py
Normal file
26
tests/test_module.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
# -*- 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.tests.test_tryton import ModuleTestCase
|
||||
from trytond.modules.cashbook.tests.test_module import CashbookTestCase
|
||||
from .category import CategoryTestCase
|
||||
from .party import PartyTestCase
|
||||
from .transaction import TransactionTestCase
|
||||
|
||||
|
||||
class CashbookExchangeTestCase(
|
||||
CashbookTestCase,
|
||||
CategoryTestCase,
|
||||
PartyTestCase,
|
||||
TransactionTestCase,
|
||||
ModuleTestCase):
|
||||
'Test cashbook exchange module'
|
||||
module = 'cashbook_dataexchange'
|
||||
|
||||
# end CashbookExchangeTestCase
|
||||
|
||||
|
||||
del ModuleTestCase
|
437
tests/transaction.py
Normal file
437
tests/transaction.py
Normal file
|
@ -0,0 +1,437 @@
|
|||
# -*- 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 with_transaction
|
||||
from trytond.pool import Pool
|
||||
from trytond.transaction import Transaction
|
||||
from .qifdata import qif_types
|
||||
|
||||
|
||||
class TransactionTestCase(object):
|
||||
'Test cashbook transaction module'
|
||||
module = 'cashbook_dataexchange'
|
||||
|
||||
@with_transaction()
|
||||
def test_func_check_counter_transaction(self):
|
||||
""" check function 'check_counter_transaction'
|
||||
"""
|
||||
pool = Pool()
|
||||
Book = pool.get('cashbook.book')
|
||||
Line = pool.get('cashbook.line')
|
||||
QifTool = pool.get('cashbook_dataexchange.qiftool')
|
||||
|
||||
company = self.prep_company()
|
||||
with Transaction().set_context({
|
||||
'company': company.id}):
|
||||
types = self.prep_type()
|
||||
books = 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),
|
||||
}, {
|
||||
'name': 'S-Giro',
|
||||
'btype': types.id,
|
||||
'company': company.id,
|
||||
'currency': company.currency.id,
|
||||
'number_sequ': self.prep_sequence().id,
|
||||
'start_date': date(2010, 1, 1),
|
||||
}])
|
||||
|
||||
Book.write(*[
|
||||
[books[0]],
|
||||
{
|
||||
'lines': [('create', [{
|
||||
'date': date(2022, 6, 1),
|
||||
'bookingtype': 'mvout',
|
||||
'amount': Decimal('10.0'),
|
||||
'booktransf': books[1].id,
|
||||
'description': 'transfer',
|
||||
}])],
|
||||
}])
|
||||
self.assertEqual(len(books[0].lines), 1)
|
||||
self.assertEqual(
|
||||
books[0].lines[0].rec_name,
|
||||
'06/01/2022|to|-10.00 usd|transfer [S-Giro | 0.00 usd | Open]')
|
||||
self.assertEqual(len(books[1].lines), 0)
|
||||
|
||||
Line.wfcheck(books[0].lines)
|
||||
self.assertEqual(len(books[1].lines), 1)
|
||||
self.assertEqual(
|
||||
books[1].lines[0].rec_name,
|
||||
'06/01/2022|from|10.00 usd|transfer [Cash Book ' +
|
||||
'| -10.00 usd | Open]')
|
||||
|
||||
self.assertEqual(QifTool.check_counter_transaction(books[1], {
|
||||
'booktransf': books[0].id,
|
||||
'date': date(2022, 6, 1),
|
||||
'amount': Decimal('10.0'),
|
||||
'description': 'transfer',
|
||||
'bookingtype': 'mvin',
|
||||
}), True)
|
||||
|
||||
@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()
|
||||
books = 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),
|
||||
}, {
|
||||
'name': 'S-Giro',
|
||||
'btype': types.id,
|
||||
'company': company.id,
|
||||
'currency': company.currency.id,
|
||||
'number_sequ': self.prep_sequence().id,
|
||||
'start_date': date(2010, 1, 1),
|
||||
}, {
|
||||
'name': 'Bargeld',
|
||||
'btype': types.id,
|
||||
'company': company.id,
|
||||
'currency': company.currency.id,
|
||||
'number_sequ': self.prep_sequence().id,
|
||||
'start_date': date(2010, 1, 1),
|
||||
}])
|
||||
|
||||
Party.create([{
|
||||
'name': 'GA NR00002168 BLZ10000000 0',
|
||||
'addresses': [('create', [{}])],
|
||||
}, {
|
||||
'name': 'Foodshop Zehlendorf',
|
||||
'addresses': [('create', [{}])],
|
||||
}, {
|
||||
'name': 'Opening Balance',
|
||||
'addresses': [('create', [{}])],
|
||||
}])
|
||||
|
||||
Category.create([{
|
||||
'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'] = books[0].id
|
||||
w_obj.start.file_ = r1['file_']
|
||||
w_obj.start.company = company.id
|
||||
w_obj.start.book = books[0].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: usd7.12
|
||||
Debit: usd83.92
|
||||
Balance: -usd76.80
|
||||
Number of transactions: 4""")
|
||||
|
||||
r1 = {
|
||||
'company': company.id,
|
||||
'book': books[0].id,
|
||||
}
|
||||
result = ImportWiz.execute(sess_id, {'showinfo': r1}, 'importf')
|
||||
self.assertEqual(list(result.keys()), [])
|
||||
|
||||
ImportWiz.delete(sess_id)
|
||||
|
||||
self.assertEqual(len(books[0].lines), 4)
|
||||
|
||||
self.assertEqual(
|
||||
books[0].lines[0].rec_name,
|
||||
'12/04/2013|from|7.12 usd|Opening Balance ' +
|
||||
'[Bargeld | -7.12 usd | Open]')
|
||||
self.assertEqual(
|
||||
books[0].lines[1].rec_name,
|
||||
'12/05/2013|to|-29.00 usd|GA NR00002168 BLZ10000000 ' +
|
||||
'0; 05.12/06.42 [S-Giro | 29.00 usd | Open]')
|
||||
self.assertEqual(
|
||||
books[0].lines[2].rec_name,
|
||||
'12/05/2013|Exp|-56.37 usd|some food [Lebensmittel]')
|
||||
self.assertEqual(
|
||||
books[0].lines[3].rec_name,
|
||||
'12/06/2013|Exp|1.45 usd|return of bottles [Lebensmittel]')
|
||||
|
||||
self.assertEqual(Book.export_as_qif(books[0]), """!Type:Bank
|
||||
D12/04/2013
|
||||
T7.12
|
||||
CX
|
||||
L[Bargeld]
|
||||
MOpening Balance
|
||||
^
|
||||
D12/05/2013
|
||||
T-29.00
|
||||
CX
|
||||
L[S-Giro]
|
||||
MGA NR00002168 BLZ10000000 0; 05.12/06.42UHR TT TELTOW
|
||||
^
|
||||
D12/05/2013
|
||||
T-56.37
|
||||
CX
|
||||
PFoodshop Zehlendorf
|
||||
LLebensmittel
|
||||
Msome food
|
||||
^
|
||||
D12/06/2013
|
||||
T1.45
|
||||
CX
|
||||
PFoodshop Zehlendorf
|
||||
LLebensmittel
|
||||
Mreturn of bottles
|
||||
^""")
|
||||
|
||||
@with_transaction()
|
||||
def test_wiz_import_transactions_transfer(self):
|
||||
""" create transactions by run wizard,
|
||||
handle transfers
|
||||
"""
|
||||
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()
|
||||
books = Book.create([{
|
||||
'name': 'From Book',
|
||||
'btype': types.id,
|
||||
'company': company.id,
|
||||
'currency': company.currency.id,
|
||||
'number_sequ': self.prep_sequence().id,
|
||||
'start_date': date(2010, 1, 1),
|
||||
}, {
|
||||
'name': 'To Book',
|
||||
'btype': types.id,
|
||||
'company': company.id,
|
||||
'currency': company.currency.id,
|
||||
'number_sequ': self.prep_sequence().id,
|
||||
'start_date': date(2010, 1, 1),
|
||||
}])
|
||||
|
||||
Party.create([{
|
||||
'name': 'Foodshop Zehlendorf',
|
||||
'addresses': [('create', [{}])],
|
||||
}])
|
||||
|
||||
Category.create([{
|
||||
'name': 'Lebensmittel',
|
||||
'cattype': 'out',
|
||||
}, {
|
||||
'name': 'Fee',
|
||||
'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_'] = """!Type:Bank
|
||||
D05.12.2013
|
||||
CX
|
||||
Msome food
|
||||
T-50,25
|
||||
PFoodshop Zehlendorf
|
||||
LLebensmittel
|
||||
^
|
||||
D04.12.2013
|
||||
T-7,30
|
||||
CX
|
||||
PTransfer to book
|
||||
L[To Book]
|
||||
^""".encode('utf8')
|
||||
r1['company'] = company.id
|
||||
r1['book'] = books[0].id
|
||||
w_obj.start.file_ = r1['file_']
|
||||
w_obj.start.company = company.id
|
||||
w_obj.start.book = books[0].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: usd0.00
|
||||
Debit: usd57.55
|
||||
Balance: -usd57.55
|
||||
Number of transactions: 2""")
|
||||
|
||||
r1 = {
|
||||
'company': company.id,
|
||||
'book': books[0].id,
|
||||
}
|
||||
result = ImportWiz.execute(sess_id, {'showinfo': r1}, 'importf')
|
||||
self.assertEqual(list(result.keys()), [])
|
||||
|
||||
ImportWiz.delete(sess_id)
|
||||
|
||||
self.assertEqual(len(books[0].lines), 2)
|
||||
self.assertEqual(len(books[1].lines), 1)
|
||||
|
||||
self.assertEqual(
|
||||
books[0].lines[0].rec_name,
|
||||
'12/04/2013|to|-7.30 usd|Transfer to book ' +
|
||||
'[To Book | 7.30 usd | Open]')
|
||||
self.assertEqual(books[0].lines[0].state, 'check')
|
||||
self.assertEqual(
|
||||
books[0].lines[1].rec_name,
|
||||
'12/05/2013|Exp|-50.25 usd|some food [Lebensmittel]')
|
||||
self.assertEqual(books[0].lines[1].state, 'check')
|
||||
self.assertEqual(
|
||||
books[1].lines[0].rec_name,
|
||||
'12/04/2013|from|7.30 usd|Transfer to book ' +
|
||||
'[From Book | -57.55 usd | Open]')
|
||||
self.assertEqual(books[1].lines[0].state, 'check')
|
||||
|
||||
# run wizard again - import to 'To Book'
|
||||
(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_'] = """!Type:Bank
|
||||
D10.12.2013
|
||||
CX
|
||||
Msome food
|
||||
T-10,00
|
||||
PFoodshop Zehlendorf
|
||||
LLebensmittel
|
||||
^
|
||||
D04.12.2013
|
||||
T7,30
|
||||
CX
|
||||
PTransfer to book
|
||||
L[From Book]
|
||||
^
|
||||
D06.12.2013
|
||||
T-10,00
|
||||
CX
|
||||
PFoodshop Zehlendorf
|
||||
MSplitbooking with category and account
|
||||
LFee
|
||||
SFee
|
||||
EFee for transfer
|
||||
$-3,00
|
||||
S[From Book]
|
||||
ETransfer to From-Book
|
||||
$-7,00
|
||||
^
|
||||
""".encode('utf8')
|
||||
r1['company'] = company.id
|
||||
r1['book'] = books[1].id
|
||||
w_obj.start.file_ = r1['file_']
|
||||
w_obj.start.company = company.id
|
||||
w_obj.start.book = books[1].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: usd0.00
|
||||
Debit: usd20.00
|
||||
Balance: -usd20.00
|
||||
Number of transactions: 2""")
|
||||
|
||||
r1 = {
|
||||
'company': company.id,
|
||||
'book': books[1].id,
|
||||
}
|
||||
result = ImportWiz.execute(sess_id, {'showinfo': r1}, 'importf')
|
||||
self.assertEqual(list(result.keys()), [])
|
||||
|
||||
ImportWiz.delete(sess_id)
|
||||
|
||||
self.assertEqual(len(books[0].lines), 3)
|
||||
self.assertEqual(len(books[1].lines), 3)
|
||||
|
||||
self.assertEqual(
|
||||
books[0].lines[0].rec_name,
|
||||
'12/04/2013|to|-7.30 usd|Transfer to book ' +
|
||||
'[To Book | -12.70 usd | Open]')
|
||||
self.assertEqual(
|
||||
books[0].lines[0].state, 'check')
|
||||
self.assertEqual(
|
||||
books[0].lines[1].rec_name,
|
||||
'12/05/2013|Exp|-50.25 usd|some food [Lebensmittel]')
|
||||
self.assertEqual(
|
||||
books[0].lines[1].state, 'check')
|
||||
self.assertEqual(
|
||||
books[0].lines[2].rec_name,
|
||||
'12/06/2013|from|7.00 usd|Transfer to From-Book ' +
|
||||
'[To Book | -12.70 usd | Open]')
|
||||
self.assertEqual(
|
||||
books[0].lines[2].state, 'check')
|
||||
|
||||
self.assertEqual(
|
||||
books[1].lines[0].rec_name,
|
||||
'12/04/2013|from|7.30 usd|Transfer to book [From Book ' +
|
||||
'| -50.55 usd | Open]')
|
||||
self.assertEqual(books[1].lines[0].state, 'check')
|
||||
self.assertEqual(
|
||||
books[1].lines[1].rec_name,
|
||||
'12/06/2013|Exp/Sp|-10.00 usd|Splitbooking with category' +
|
||||
' and account [-]')
|
||||
self.assertEqual(books[1].lines[1].state, 'check')
|
||||
self.assertEqual(
|
||||
books[1].lines[2].rec_name,
|
||||
'12/10/2013|Exp|-10.00 usd|some food [Lebensmittel]')
|
||||
self.assertEqual(books[1].lines[2].state, 'check')
|
||||
|
||||
# end PartyTestCase
|
|
@ -1,7 +1,8 @@
|
|||
[tryton]
|
||||
version=6.0.1
|
||||
version=6.0.5
|
||||
depends:
|
||||
cashbook
|
||||
xml:
|
||||
message.xml
|
||||
qif_import_wiz.xml
|
||||
qif_export.xml
|
||||
|
|
|
@ -1 +1 @@
|
|||
cashbook;6.0.1;6.0.999;mds
|
||||
cashbook;6.0.28;6.0.999;mds
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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_"/>
|
||||
|
|
Loading…
Reference in a new issue