line/splitline: fremdwährung ok+test+migration

This commit is contained in:
Frederik Jaeckel 2022-10-04 16:47:14 +02:00
parent 0f6180ebdb
commit 25dcdde09b
10 changed files with 485 additions and 217 deletions

257
line.py
View file

@ -10,12 +10,12 @@ from trytond.transaction import Transaction
from trytond.report import Report from trytond.report import Report
from trytond.exceptions import UserError from trytond.exceptions import UserError
from trytond.i18n import gettext from trytond.i18n import gettext
from trytond.modules.currency.ir import rate_decimal
from decimal import Decimal from decimal import Decimal
from sql import Cast, Literal from sql import Literal
from sql.functions import DatePart from sql.functions import DatePart
from sql.conditionals import Case from sql.conditionals import Case
from .book import sel_state_book from .book import sel_state_book
from .mixin import SecondCurrencyMixin
sel_payee = [ sel_payee = [
@ -47,7 +47,7 @@ STATES = {
DEPENDS=['state', 'state_cashbook'] DEPENDS=['state', 'state_cashbook']
class Line(Workflow, ModelSQL, ModelView): class Line(SecondCurrencyMixin, Workflow, ModelSQL, ModelView):
'Cashbook Line' 'Cashbook Line'
__name__ = 'cashbook.line' __name__ = 'cashbook.line'
@ -121,23 +121,6 @@ class Line(Workflow, ModelSQL, ModelView):
payee = fields.Function(fields.Reference(string='Payee', readonly=True, payee = fields.Function(fields.Reference(string='Payee', readonly=True,
selection=sel_payee), 'on_change_with_payee', searcher='search_payee') selection=sel_payee), 'on_change_with_payee', searcher='search_payee')
amount_2nd_currency = fields.Numeric(string='Amount Second Currency',
digits=(16, Eval('currency2nd_digits', 2)),
states={
'readonly': STATES['readonly'],
'required': Bool(Eval('currency2nd')),
'invisible': ~Bool(Eval('currency2nd')),
}, depends=DEPENDS+['currency2nd_digits', 'currency2nd'])
rate_2nd_currency = fields.Function(fields.Numeric(string='Rate',
help='Exchange rate between the currencies of the participating cashbooks.',
digits=(rate_decimal * 2, rate_decimal),
states={
'readonly': STATES['readonly'],
'required': Bool(Eval('currency2nd')),
'invisible': ~Bool(Eval('currency2nd')),
}, depends=DEPENDS+['currency2nd_digits', 'currency2nd']),
'on_change_with_rate_2nd_currency', setter='set_rate_2nd_currency')
# link to lines created by this record # link to lines created by this record
reference = fields.Many2One(string='Reference', readonly=True, select=True, reference = fields.Many2One(string='Reference', readonly=True, select=True,
states={ states={
@ -180,10 +163,6 @@ class Line(Workflow, ModelSQL, ModelView):
string="Currency", readonly=True), 'on_change_with_currency') string="Currency", readonly=True), 'on_change_with_currency')
currency_digits = fields.Function(fields.Integer(string='Currency Digits', currency_digits = fields.Function(fields.Integer(string='Currency Digits',
readonly=True), 'on_change_with_currency_digits') readonly=True), 'on_change_with_currency_digits')
currency2nd = fields.Function(fields.Many2One(model_name='currency.currency',
string="2nd Currency", readonly=True), 'on_change_with_currency2nd')
currency2nd_digits = fields.Function(fields.Integer(string='2nd Currency Digits',
readonly=True), 'on_change_with_currency2nd_digits')
state = fields.Selection(string='State', required=True, readonly=True, state = fields.Selection(string='State', required=True, readonly=True,
select=True, selection=sel_linetype) select=True, selection=sel_linetype)
@ -203,6 +182,7 @@ class Line(Workflow, ModelSQL, ModelView):
table = cls.__table_handler__(module_name) table = cls.__table_handler__(module_name)
table.drop_constraint('amount_val') table.drop_constraint('amount_val')
cls.migrate_amount_2nd_currency()
@classmethod @classmethod
def __setup__(cls): def __setup__(cls):
@ -236,6 +216,48 @@ class Line(Workflow, ModelSQL, ModelView):
}, },
}) })
@classmethod
def migrate_amount_2nd_currency(cls):
""" add amount-2nd-currency
"""
pool = Pool()
Line2 = pool.get('cashbook.line')
Book = pool.get('cashbook.book')
Book2 = pool.get('cashbook.book')
tab_line = Line2.__table__()
tab_book = Book.__table__() # cashbook of line
tab_book2 = Book2.__table__() # transfer-target
cursor = Transaction().connection.cursor()
query = tab_line.join(tab_book,
condition=tab_line.cashbook == tab_book.id,
).join(tab_book2,
condition=tab_line.booktransf== tab_book2.id,
).select(tab_line.id,
where=tab_line.bookingtype.in_(['mvin', 'mvout']) & \
(tab_line.amount_2nd_currency == None) & \
(tab_book.currency != tab_book2.currency)
)
lines = Line2.search([('id', 'in', query)])
to_write = []
for line in lines:
values = Line2.add_2nd_currency({
'date': line.date,
'booktransf': line.booktransf.id,
'amount': line.amount,
}, line.currency)
if 'amount_2nd_currency' in values.keys():
values['id'] = line.id
to_write.append(values)
for line in to_write:
qu1 = tab_line.update(
columns = [tab_line.amount_2nd_currency],
values = [line['amount_2nd_currency']],
where = tab_line.id == line['id'],
)
cursor.execute(*qu1)
@classmethod @classmethod
@ModelView.button @ModelView.button
@Workflow.transition('edit') @Workflow.transition('edit')
@ -315,10 +337,10 @@ class Line(Workflow, ModelSQL, ModelView):
'booktransf': line.cashbook.id, 'booktransf': line.cashbook.id,
'reference': line.id, 'reference': line.id,
'amount': line.amount \ 'amount': line.amount \
if line.cashbook.currency.id == line.booktransf.currency.id \ if line.currency.id == line.booktransf.currency.id \
else line.amount_2nd_currency, else line.amount_2nd_currency,
'amount_2nd_currency': line.amount \ 'amount_2nd_currency': line.amount \
if line.cashbook.currency.id != line.booktransf.currency.id \ if line.currency.id != line.booktransf.currency.id \
else None, else None,
} }
values.update(cls.get_debit_credit(values)) values.update(cls.get_debit_credit(values))
@ -335,15 +357,17 @@ class Line(Workflow, ModelSQL, ModelView):
'description': sp_line.description, 'description': sp_line.description,
'booktransf': line.cashbook.id, 'booktransf': line.cashbook.id,
'reference': line.id, 'reference': line.id,
'amount': sp_line.amount \
if sp_line.currency.id == sp_line.booktransf.currency.id \
else sp_line.amount_2nd_currency,
'amount_2nd_currency': sp_line.amount \
if sp_line.currency.id != sp_line.booktransf.currency.id \
else None,
} }
if line.bookingtype.endswith('out'): if line.bookingtype.endswith('out'):
values['bookingtype'] = 'mvin' values['bookingtype'] = 'mvin'
else : else :
values['bookingtype'] = 'mvout' values['bookingtype'] = 'mvout'
values.update(line.get_amount_by_second_currency(
sp_line.booktransf.currency,
amount = sp_line.amount,
))
values.update(cls.get_debit_credit(values)) values.update(cls.get_debit_credit(values))
to_create_line.append(values) to_create_line.append(values)
@ -431,26 +455,6 @@ class Line(Workflow, ModelSQL, ModelView):
'type': gettext('cashbook.msg_line_bookingtype_%s' % self.bookingtype), 'type': gettext('cashbook.msg_line_bookingtype_%s' % self.bookingtype),
} }
def get_amount_by_second_currency(self, to_currency, amount=None):
""" get amount, calculate credit/debit from currency of current
cashbook to 'to_currency'
"""
Currency = Pool().get('currency.currency')
values = {
'amount': amount if amount is not None else self.amount,
}
if to_currency.id != self.cashbook.currency.id:
with Transaction().set_context({
'date': self.date,
}):
values['amount'] = Currency.compute(
self.cashbook.currency,
values['amount'],
to_currency)
return values
@staticmethod @staticmethod
def order_state(tables): def order_state(tables):
""" edit = 0, check/done = 1 """ edit = 0, check/done = 1
@ -541,7 +545,8 @@ class Line(Workflow, ModelSQL, ModelView):
""" """
self.amount = sum([x.amount for x in self.splitlines if x.amount is not None]) self.amount = sum([x.amount for x in self.splitlines if x.amount is not None])
@fields.depends('bookingtype', 'category', 'splitlines') @fields.depends('bookingtype', 'category', 'splitlines', 'booktransf',\
'currency2nd')
def on_change_bookingtype(self): def on_change_bookingtype(self):
""" clear category if not valid type """ clear category if not valid type
""" """
@ -567,98 +572,7 @@ class Line(Workflow, ModelSQL, ModelView):
else : # category else : # category
self.splitlines = [] self.splitlines = []
self.booktransf = None self.booktransf = None
self.currency2nd = self.on_change_with_currency2nd()
@fields.depends('booktransf', '_parent_booktransf.currency', 'date',\
'currency', 'amount', 'amount_2nd_currency')
def on_change_booktransf(self):
""" update amount_2nd_currency
"""
pool = Pool()
Currency = pool.get('currency.currency')
IrDate = pool.get('ir.date')
if (self.booktransf is None) or (self.currency is None):
return
if self.amount is not None:
if self.booktransf.currency.id != self.currency.id:
with Transaction().set_context({
'date': self.date or IrDate.today(),
}):
self.amount_2nd_currency = Currency.compute(
self.currency,
self.amount,
self.booktransf.currency
)
return
self.amount_2nd_currency = None
@fields.depends('currency', 'booktransf', '_parent_booktransf.currency', \
'amount', 'amount_2nd_currency', 'rate_2nd_currency')
def on_change_amount(self):
""" update amount_2nd_currency
"""
self.on_change_rate_2nd_currency()
@fields.depends('booktransf', '_parent_booktransf.currency', \
'amount', 'amount_2nd_currency', 'rate_2nd_currency')
def on_change_rate_2nd_currency(self):
""" update amount_2nd_currency by rate
"""
if (self.amount is None) or (self.rate_2nd_currency is None) or \
(self.booktransf is None):
return
if self.currency.id != self.booktransf.currency.id:
self.amount_2nd_currency = self.booktransf.currency.round(
self.amount * self.rate_2nd_currency
)
@classmethod
def set_rate_2nd_currency(cls, lines, name, value):
""" compute amount_2nd_currency, write to db
"""
Line2 = Pool().get('cashbook.line')
to_write = []
if not name == 'rate_2nd_currency':
return
for line in lines:
if line.booktransf is None:
continue
if line.cashbook.currency.id == line.booktransf.currency.id:
continue
to_write.extend([
[line],
{
'amount_2nd_currency': line.booktransf.currency.round(
line.amount * value),
}])
if len(to_write) > 0:
Line2.write(*to_write)
@fields.depends('amount', 'amount_2nd_currency', 'rate_2nd_currency')
def on_change_amount_2nd_currency(self):
""" update rate_2nd_currency by rate
"""
self.rate_2nd_currency = self.on_change_with_rate_2nd_currency()
@fields.depends('amount', 'amount_2nd_currency')
def on_change_with_rate_2nd_currency(self, name=None):
""" get current rate from amount
"""
Rate = Pool().get('currency.currency.rate')
if (self.amount is not None) and \
(self.amount_2nd_currency is not None):
if self.amount != Decimal('0.0'):
exp = Decimal(Decimal(1) / 10 ** Rate.rate.digits[1])
return (self.amount_2nd_currency / self.amount).quantize(exp)
@fields.depends('description') @fields.depends('description')
def on_change_with_descr_short(self, name=None): def on_change_with_descr_short(self, name=None):
@ -733,24 +647,6 @@ class Line(Workflow, ModelSQL, ModelView):
else: else:
return 2 return 2
@fields.depends('currency', 'booktransf', '_parent_booktransf.currency')
def on_change_with_currency2nd(self, name=None):
""" currency of transfer-target
"""
if self.booktransf:
if self.currency:
if self.currency.id != self.booktransf.currency.id:
return self.booktransf.currency.id
@fields.depends('currency', 'booktransf', '_parent_booktransf.currency')
def on_change_with_currency2nd_digits(self, name=None):
""" currency of transfer-target
"""
if self.booktransf:
return self.booktransf.currency.digits
else:
return 2
@fields.depends('id', 'date', 'cashbook', \ @fields.depends('id', 'date', 'cashbook', \
'_parent_cashbook.id', 'reconciliation', \ '_parent_cashbook.id', 'reconciliation', \
'_parent_reconciliation.start_amount',\ '_parent_reconciliation.start_amount',\
@ -872,38 +768,6 @@ class Line(Workflow, ModelSQL, ModelView):
raise ValueError('invalid "bookingtype"') raise ValueError('invalid "bookingtype"')
return {} return {}
@classmethod
def add_2nd_currency(cls, values):
""" add second currency amount if missing
"""
pool = Pool()
Currency = pool.get('currency.currency')
Cashbook = pool.get('cashbook.book')
IrDate = pool.get('ir.date')
cashbook = values.get('cashbook', None)
booktransf = values.get('booktransf', None)
amount = values.get('amount', None)
amount_2nd_currency = values.get('amount_2nd_currency', None)
if (cashbook is None) or (booktransf is None):
return values
if amount is not None:
if amount_2nd_currency is None:
cashbook = Cashbook(cashbook)
booktransf = Cashbook(booktransf)
if cashbook.currency.id != booktransf.currency.id:
with Transaction().set_context({
'date': values.get('date', IrDate.today()),
}):
values['amount_2nd_currency'] = Currency.compute(
cashbook.currency,
amount,
booktransf.currency,
)
return values
@classmethod @classmethod
def update_amount_by_splitlines(cls, lines): def update_amount_by_splitlines(cls, lines):
""" update amounts from split-lines """ update amounts from split-lines
@ -1028,11 +892,16 @@ class Line(Workflow, ModelSQL, ModelView):
def create(cls, vlist): def create(cls, vlist):
""" add debit/credit """ add debit/credit
""" """
Cashbook = Pool().get('cashbook.book')
vlist = [x.copy() for x in vlist] vlist = [x.copy() for x in vlist]
for values in vlist: for values in vlist:
values.update(cls.get_debit_credit(values)) values.update(cls.get_debit_credit(values))
values.update(cls.clear_by_bookingtype(values)) values.update(cls.clear_by_bookingtype(values))
values.update(cls.add_2nd_currency(values))
cashbook = values.get('cashbook', None)
if cashbook:
values.update(cls.add_2nd_currency(values, Cashbook(cashbook).currency))
# deny add to reconciliation if state is not 'check' or 'done' # deny add to reconciliation if state is not 'check' or 'done'
if values.get('reconciliation', None): if values.get('reconciliation', None):

View file

@ -582,6 +582,10 @@ msgctxt "view:cashbook.split:"
msgid "Description" msgid "Description"
msgstr "Beschreibung" msgstr "Beschreibung"
msgctxt "view:cashbook.split:"
msgid "Amount"
msgstr "Betrag"
msgctxt "field:cashbook.split,line:" msgctxt "field:cashbook.split,line:"
msgid "Line" msgid "Line"
msgstr "Zeile" msgstr "Zeile"
@ -698,6 +702,26 @@ msgctxt "field:cashbook.split,booktransf:"
msgid "Source/Dest" msgid "Source/Dest"
msgstr "Quelle/Ziel" msgstr "Quelle/Ziel"
msgctxt "field:cashbook.split,currency2nd:"
msgid "2nd Currency"
msgstr "Fremdwährung"
msgctxt "field:cashbook.split,currency2nd_digits:"
msgid "2nd Currency Digits"
msgstr "Nachkommastellen Fremdwährung"
msgctxt "field:cashbook.split,amount_2nd_currency:"
msgid "Amount Second Currency"
msgstr "Fremdwährungsbetrag"
msgctxt "field:cashbook.split,rate_2nd_currency:"
msgid "Rate"
msgstr "Kurs"
msgctxt "help:cashbook.split,rate_2nd_currency:"
msgid "Exchange rate between the currencies of the participating cashbooks."
msgstr "Wechselkurs zwischen der Währungen der beteiligten Kassenbücher."
################# #################
# cashbook.line # # cashbook.line #

View file

@ -542,6 +542,10 @@ msgctxt "view:cashbook.split:"
msgid "Description" msgid "Description"
msgstr "Description" msgstr "Description"
msgctxt "view:cashbook.split:"
msgid "Amount"
msgstr "Amount"
msgctxt "field:cashbook.split,line:" msgctxt "field:cashbook.split,line:"
msgid "Line" msgid "Line"
msgstr "Line" msgstr "Line"
@ -658,6 +662,26 @@ msgctxt "field:cashbook.split,booktransf:"
msgid "Source/Dest" msgid "Source/Dest"
msgstr "Source/Dest" msgstr "Source/Dest"
msgctxt "field:cashbook.split,currency2nd:"
msgid "2nd Currency"
msgstr "2nd Currency"
msgctxt "field:cashbook.split,currency2nd_digits:"
msgid "2nd Currency Digits"
msgstr "2nd Currency Digits"
msgctxt "field:cashbook.split,amount_2nd_currency:"
msgid "Amount Second Currency"
msgstr "Amount Second Currency"
msgctxt "field:cashbook.split,rate_2nd_currency:"
msgid "Rate"
msgstr "Rate"
msgctxt "help:cashbook.split,rate_2nd_currency:"
msgid "Exchange rate between the currencies of the participating cashbooks."
msgstr "Exchange rate between the currencies of the participating cashbooks."
msgctxt "model:cashbook.line,name:" msgctxt "model:cashbook.line,name:"
msgid "Cashbook Line" msgid "Cashbook Line"
msgstr "Cashbook Line" msgstr "Cashbook Line"

191
mixin.py Normal file
View file

@ -0,0 +1,191 @@
# -*- 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.model import ModelView, ModelSQL, fields
from trytond.pyson import Eval, Bool, Or
from trytond.pool import Pool
from trytond.modules.currency.ir import rate_decimal
from trytond.transaction import Transaction
from decimal import Decimal
STATES = {
'readonly': Or(
Eval('state', '') != 'edit',
Eval('state_cashbook', '') != 'open',
),
}
DEPENDS=['state', 'state_cashbook']
class SecondCurrencyMixin:
""" two fields for 2nd currency: amount + rate
"""
amount_2nd_currency = fields.Numeric(string='Amount Second Currency',
digits=(16, Eval('currency2nd_digits', 2)),
states={
'readonly': Or(
STATES['readonly'],
~Bool(Eval('currency2nd'))
),
'required': Bool(Eval('currency2nd')),
'invisible': ~Bool(Eval('currency2nd')),
}, depends=DEPENDS+['currency2nd_digits', 'currency2nd'])
rate_2nd_currency = fields.Function(fields.Numeric(string='Rate',
help='Exchange rate between the currencies of the participating cashbooks.',
digits=(rate_decimal * 2, rate_decimal),
states={
'readonly': Or(
STATES['readonly'],
~Bool(Eval('currency2nd'))
),
'required': Bool(Eval('currency2nd')),
'invisible': ~Bool(Eval('currency2nd')),
}, depends=DEPENDS+['currency2nd_digits', 'currency2nd']),
'on_change_with_rate_2nd_currency', setter='set_rate_2nd_currency')
currency2nd = fields.Function(fields.Many2One(model_name='currency.currency',
string="2nd Currency", readonly=True), 'on_change_with_currency2nd')
currency2nd_digits = fields.Function(fields.Integer(string='2nd Currency Digits',
readonly=True), 'on_change_with_currency2nd_digits')
@classmethod
def add_2nd_currency(cls, values, from_currency):
""" add second currency amount if missing
"""
pool = Pool()
Currency = pool.get('currency.currency')
Cashbook = pool.get('cashbook.book')
IrDate = pool.get('ir.date')
#cashbook = values.get('cashbook', None)
booktransf = values.get('booktransf', None)
amount = values.get('amount', None)
amount_2nd_currency = values.get('amount_2nd_currency', None)
if (amount is not None) and (booktransf is not None):
if amount_2nd_currency is None:
#cashbook = Cashbook(cashbook)
booktransf = Cashbook(booktransf)
if from_currency.id != booktransf.currency.id:
with Transaction().set_context({
'date': values.get('date', IrDate.today()),
}):
values['amount_2nd_currency'] = Currency.compute(
from_currency,
amount,
booktransf.currency,
)
return values
@fields.depends('booktransf', '_parent_booktransf.currency', \
'currency', 'amount', 'date', 'amount_2nd_currency', 'rate_2nd_currency')
def on_change_booktransf(self):
""" update amount_2nd_currency
"""
self.on_change_rate_2nd_currency()
@fields.depends('booktransf', '_parent_booktransf.currency', \
'currency', 'amount', 'date', 'amount_2nd_currency', 'rate_2nd_currency')
def on_change_amount(self):
""" update amount_2nd_currency
"""
self.on_change_rate_2nd_currency()
@fields.depends('booktransf', '_parent_booktransf.currency', \
'currency', 'amount', 'date', 'amount_2nd_currency', 'rate_2nd_currency')
def on_change_rate_2nd_currency(self):
""" update amount_2nd_currency + rate_2nd_currency
"""
pool = Pool()
IrDate = pool.get('ir.date')
Currency = pool.get('currency.currency')
if (self.amount is None) or (self.booktransf is None):
self.amount_2nd_currency = None
self.rate_2nd_currency = None
return
if self.rate_2nd_currency is None:
# no rate set, use current rate of target-currency
with Transaction().set_context({
'date': self.date or IrDate.today(),
}):
self.amount_2nd_currency = Currency.compute(
self.currency,
self.amount,
self.booktransf.currency
)
if self.amount != Decimal('0.0'):
self.rate_2nd_currency = self.amount_2nd_currency / self.amount
else :
self.amount_2nd_currency = self.booktransf.currency.round(
self.amount * self.rate_2nd_currency
)
@classmethod
def set_rate_2nd_currency(cls, lines, name, value):
""" compute amount_2nd_currency, write to db
"""
Line2 = Pool().get(cls.__name__)
to_write = []
if not name == 'rate_2nd_currency':
return
for line in lines:
if line.booktransf is None:
continue
if line.cashbook.currency.id == line.booktransf.currency.id:
continue
to_write.extend([
[line],
{
'amount_2nd_currency': line.booktransf.currency.round(
line.amount * value),
}])
if len(to_write) > 0:
Line2.write(*to_write)
@fields.depends('amount', 'amount_2nd_currency', 'rate_2nd_currency')
def on_change_amount_2nd_currency(self):
""" update rate_2nd_currency by rate
"""
self.rate_2nd_currency = self.on_change_with_rate_2nd_currency()
@fields.depends('amount', 'amount_2nd_currency')
def on_change_with_rate_2nd_currency(self, name=None):
""" get current rate from amount
"""
Rate = Pool().get('currency.currency.rate')
if (self.amount is not None) and \
(self.amount_2nd_currency is not None):
if self.amount != Decimal('0.0'):
exp = Decimal(Decimal(1) / 10 ** Rate.rate.digits[1])
return (self.amount_2nd_currency / self.amount).quantize(exp)
@fields.depends('currency', 'booktransf', '_parent_booktransf.currency')
def on_change_with_currency2nd(self, name=None):
""" currency of transfer-target
"""
if self.booktransf:
if self.currency:
if self.currency.id != self.booktransf.currency.id:
return self.booktransf.currency.id
@fields.depends('currency', 'booktransf', '_parent_booktransf.currency')
def on_change_with_currency2nd_digits(self, name=None):
""" currency of transfer-target
"""
if self.booktransf:
return self.booktransf.currency.digits
else:
return 2
# end SecondCurrencyMixin

View file

@ -12,6 +12,7 @@ from trytond.i18n import gettext
from trytond.transaction import Transaction from trytond.transaction import Transaction
from .line import sel_linetype, sel_bookingtype, STATES, DEPENDS from .line import sel_linetype, sel_bookingtype, STATES, DEPENDS
from .book import sel_state_book from .book import sel_state_book
from .mixin import SecondCurrencyMixin
sel_linetype = [ sel_linetype = [
@ -25,7 +26,7 @@ sel_target = [
] ]
class SplitLine(ModelSQL, ModelView): class SplitLine(SecondCurrencyMixin, ModelSQL, ModelView):
'Split booking line' 'Split booking line'
__name__ = 'cashbook.split' __name__ = 'cashbook.split'
@ -57,6 +58,7 @@ class SplitLine(ModelSQL, ModelView):
domain=[ domain=[
('owner.id', '=', Eval('owner_cashbook', -1)), ('owner.id', '=', Eval('owner_cashbook', -1)),
('id', '!=', Eval('cashbook', -1)), ('id', '!=', Eval('cashbook', -1)),
('btype', '!=', None),
], ],
states={ states={
'readonly': STATES['readonly'], 'readonly': STATES['readonly'],
@ -106,26 +108,6 @@ class SplitLine(ModelSQL, ModelView):
'type': gettext('cashbook.msg_line_bookingtype_%s' % self.line.bookingtype), 'type': gettext('cashbook.msg_line_bookingtype_%s' % self.line.bookingtype),
} }
def get_amount_by_second_currency(self, to_currency, amount=None):
""" get amount, calculate credit/debit from currency of current
cashbook to 'to_currency'
"""
Currency = Pool().get('currency.currency')
values = {
'amount': amount if amount is not None else self.amount,
}
if to_currency.id != self.line.cashbook.currency.id:
with Transaction().set_context({
'date': self.line.date,
}):
values['amount'] = Currency.compute(
self.line.cashbook.currency,
values['amount'],
to_currency)
return values
@fields.depends('splittype', 'category', 'booktransf') @fields.depends('splittype', 'category', 'booktransf')
def on_change_splittype(self): def on_change_splittype(self):
""" clear category if not valid type """ clear category if not valid type
@ -228,6 +210,11 @@ class SplitLine(ModelSQL, ModelView):
Line2 = Pool().get('cashbook.line') Line2 = Pool().get('cashbook.line')
vlist = [x.copy() for x in vlist] vlist = [x.copy() for x in vlist]
for values in vlist:
line = Line2(values.get('line', None))
if line:
values.update(cls.add_2nd_currency(values, line.cashbook.currency))
records = super(SplitLine, cls).create(vlist) records = super(SplitLine, cls).create(vlist)
to_update_line = [] to_update_line = []

View file

@ -70,6 +70,7 @@ class BookTestCase(ModuleTestCase):
(usd, euro) = self.prep_2nd_currency(company) (usd, euro) = self.prep_2nd_currency(company)
category = self.prep_category(cattype='in') category = self.prep_category(cattype='in')
self.assertEqual(company.currency.rec_name, 'Euro') self.assertEqual(company.currency.rec_name, 'Euro')
party = self.prep_party()
book, = Book.create([{ book, = Book.create([{
'name': 'Book 1', 'name': 'Book 1',
@ -84,6 +85,7 @@ class BookTestCase(ModuleTestCase):
'bookingtype': 'in', 'bookingtype': 'in',
'category': category.id, 'category': category.id,
'amount': Decimal('10.0'), 'amount': Decimal('10.0'),
'party': party.id,
}])], }])],
}]) }])
@ -118,6 +120,7 @@ class BookTestCase(ModuleTestCase):
(usd, euro) = self.prep_2nd_currency(company) (usd, euro) = self.prep_2nd_currency(company)
category = self.prep_category(cattype='in') category = self.prep_category(cattype='in')
self.assertEqual(company.currency.rec_name, 'Euro') self.assertEqual(company.currency.rec_name, 'Euro')
party = self.prep_party()
book, = Book.create([{ book, = Book.create([{
'name': 'Book 1', 'name': 'Book 1',
@ -136,6 +139,7 @@ class BookTestCase(ModuleTestCase):
'bookingtype': 'in', 'bookingtype': 'in',
'category': category.id, 'category': category.id,
'amount': Decimal('10.0'), 'amount': Decimal('10.0'),
'party': party.id,
}])], }])],
}])], }])],
}]) }])

View file

@ -156,6 +156,71 @@ class LineTestCase(ModuleTestCase):
self.assertEqual(books[0].lines[0].amount_2nd_currency, Decimal('10.5')) self.assertEqual(books[0].lines[0].amount_2nd_currency, Decimal('10.5'))
self.assertEqual(books[0].lines[0].amount, Decimal('12.0')) self.assertEqual(books[0].lines[0].amount, Decimal('12.0'))
@with_transaction()
def test_line_check_migrate_amount_2nd_currency(self):
""" create cashbook, lines, transfer
check migration
"""
pool = Pool()
Book = pool.get('cashbook.book')
Lines = pool.get('cashbook.line')
tab_line = Lines.__table__()
cursor = Transaction().connection.cursor()
types = self.prep_type()
company = self.prep_company()
# add EURO, set company-currency to EURO
(usd, euro) = self.prep_2nd_currency(company)
books = Book.create([{
'name': 'Book USD',
'btype': types.id,
'company': company.id,
'currency': usd.id,
'number_sequ': self.prep_sequence().id,
'start_date': date(2022, 5, 1),
}, {
'name': 'Book EURO',
'btype': types.id,
'company': company.id,
'currency': euro.id,
'number_sequ': self.prep_sequence().id,
'start_date': date(2022, 5, 1),
}])
self.assertEqual(len(books), 2)
self.assertEqual(books[0].rec_name, 'Book USD | 0.00 usd | Open')
self.assertEqual(books[1].rec_name, 'Book EURO | 0.00 € | Open')
Book.write(*[
[books[0]],
{
'lines': [('create', [{
'date': date(2022, 5, 5),
'description': 'Transfer USD --> EUR',
'bookingtype': 'mvout',
'amount': Decimal('10.0'),
'booktransf': books[1].id,
}])],
}])
self.assertEqual(len(books[0].lines), 1)
self.assertEqual(len(books[1].lines), 0)
self.assertEqual(books[0].lines[0].rec_name,
'05/05/2022|to|-10.00 usd|Transfer USD --> EUR [Book EURO | 0.00 € | Open]')
self.assertEqual(books[0].lines[0].amount_2nd_currency, Decimal('9.52'))
# clear field 'amount_2nd_currency' to prepare for migration
clear_field = tab_line.update(
columns = [tab_line.amount_2nd_currency],
values = [None],
where = (tab_line.id == books[0].lines[0].id),
)
cursor.execute(*clear_field)
# migrate
Lines.migrate_amount_2nd_currency()
self.assertEqual(books[0].lines[0].amount_2nd_currency, Decimal('9.52'))
@with_transaction() @with_transaction()
def test_line_check_transfer_2nd_currency_out(self): def test_line_check_transfer_2nd_currency_out(self):
""" create cashbook, lines, transfer amount between """ create cashbook, lines, transfer amount between

View file

@ -106,6 +106,103 @@ class SplitLineTestCase(ModuleTestCase):
self.assertEqual(len(books[0].lines[0].references), 0) self.assertEqual(len(books[0].lines[0].references), 0)
self.assertEqual(len(books[1].lines), 0) self.assertEqual(len(books[1].lines), 0)
@with_transaction()
def test_splitline_category_and_transfer_2ndcurrency(self):
""" add book, line, two split-lines,
category + transfer, target-cashbook in USD
"""
pool = Pool()
Book = pool.get('cashbook.book')
Line = pool.get('cashbook.line')
types = self.prep_type()
category1 = self.prep_category(cattype='out')
company = self.prep_company()
party = self.prep_party()
(usd, euro) = self.prep_2nd_currency(company)
books = Book.create([{
'name': 'Book 1',
'btype': types.id,
'company': company.id,
'currency': euro.id,
'number_sequ': self.prep_sequence().id,
'start_date': date(2022, 5, 1),
'lines': [('create', [{
'date': date(2022, 5, 1),
'description': 'Text 1',
'category': category1.id,
'bookingtype': 'out',
'amount': Decimal('1.0'),
'party': party.id,
}])],
}, {
'name': 'Book 2',
'btype': types.id,
'company': company.id,
'currency': usd.id,
'number_sequ': self.prep_sequence().id,
'start_date': date(2022, 5, 1),
}])
self.assertEqual(books[0].rec_name, 'Book 1 | -1.00 € | Open')
self.assertEqual(len(books[0].lines), 1)
self.assertEqual(books[0].lines[0].rec_name, '05/01/2022|Exp|-1.00 €|Text 1 [Cat1]')
self.assertEqual(books[1].rec_name, 'Book 2 | 0.00 usd | Open')
# EUR --> USD
Book.write(*[
[books[0]],
{
'lines': [('write', [books[0].lines[0]], {
'bookingtype': 'spout',
'splitlines': [('create', [{
'amount': Decimal('5.0'),
'splittype': 'cat',
'description': 'to category',
'category': category1.id,
}, {
'amount': Decimal('6.0'),
'splittype': 'tr',
'description': 'to book2',
'booktransf': books[1].id,
}])],
})]
}])
self.assertEqual(len(books[0].lines), 1)
self.assertEqual(books[0].lines[0].rec_name, '05/01/2022|Exp/Sp|-11.00 €|Text 1 [-]')
self.assertEqual(books[0].lines[0].category, None)
self.assertEqual(len(books[0].lines[0].splitlines), 2)
self.assertEqual(books[0].lines[0].splitlines[0].rec_name,
'Exp/Sp|5.00 €|to category [Cat1]')
self.assertEqual(books[0].lines[0].splitlines[0].amount_2nd_currency, None)
self.assertEqual(books[0].lines[0].splitlines[1].rec_name,
'Exp/Sp|6.00 €|to book2 [Book 2 | 0.00 usd | Open]')
self.assertEqual(books[0].lines[0].splitlines[1].amount_2nd_currency, Decimal('6.3'))
self.assertEqual(len(books[1].lines), 0)
# wf: edit -> check
Line.wfcheck(books[0].lines)
self.assertEqual(len(books[0].lines), 1)
self.assertEqual(books[0].lines[0].state, 'check')
self.assertEqual(books[0].lines[0].number, '1')
self.assertEqual(len(books[0].lines[0].references), 1)
self.assertEqual(books[0].lines[0].references[0].rec_name,
'05/01/2022|from|6.30 usd|to book2 [Book 1 | -11.00 € | Open]')
self.assertEqual(len(books[1].lines), 1)
self.assertEqual(books[1].lines[0].reference.rec_name,
'05/01/2022|Exp/Sp|-11.00 €|Text 1 [-]')
self.assertEqual(books[1].lines[0].rec_name,
'05/01/2022|from|6.30 usd|to book2 [Book 1 | -11.00 € | Open]')
self.assertEqual(books[1].lines[0].amount, Decimal('6.3'))
self.assertEqual(books[1].lines[0].amount_2nd_currency, Decimal('6.0'))
# wf: check --> edit
Line.wfedit(books[0].lines)
self.assertEqual(len(books[0].lines), 1)
self.assertEqual(len(books[0].lines[0].references), 0)
self.assertEqual(len(books[1].lines), 0)
@with_transaction() @with_transaction()
def test_splitline_check_clear_by_bookingtype(self): def test_splitline_check_clear_by_bookingtype(self):
""" add book, line, category, set line to 'in', """ add book, line, category, set line to 'in',

View file

@ -5,7 +5,7 @@ full copyright notices and license terms. -->
<form col="4"> <form col="4">
<label name="line"/> <label name="line"/>
<field name="line" colspan="3"/> <field name="line" colspan="5"/>
<label name="amount"/> <label name="amount"/>
<field name="amount" symbol="currency"/> <field name="amount" symbol="currency"/>
@ -17,9 +17,14 @@ full copyright notices and license terms. -->
<label name="booktransf"/> <label name="booktransf"/>
<field name="booktransf" colspan="3"/> <field name="booktransf" colspan="3"/>
<label name="amount_2nd_currency"/>
<field name="amount_2nd_currency" symbol="currency2nd"/>
<label name="rate_2nd_currency"/>
<field name="rate_2nd_currency"/>
<notebook colspan="4"> <notebook colspan="4">
<page name="description" col="1" string="Description"> <page name="description" col="1" string="Description">
<field name="description"/> <field name="description"/>
</page> </page>
</notebook> </notebook>
</form> </form>

View file

@ -10,4 +10,6 @@ full copyright notices and license terms. -->
<field name="description" expand="1"/> <field name="description" expand="1"/>
<field name="amount" sum="Amount"/> <field name="amount" sum="Amount"/>
<field name="currency"/> <field name="currency"/>
<field name="amount_2nd_currency"/>
<field name="currency2nd"/>
</tree> </tree>