line: änderungesperre bei diversen wf-zuständen + test,
abstimmung: datum/betrag anfang/ende korrekt + test für beträge muß noch
This commit is contained in:
parent
01825cc09c
commit
149baef174
10 changed files with 1099 additions and 64 deletions
65
line.py
65
line.py
|
@ -78,7 +78,9 @@ class Line(Workflow, ModelSQL, ModelView):
|
||||||
credit = fields.Numeric(string='Credit', digits=(16, Eval('currency_digits', 2)),
|
credit = fields.Numeric(string='Credit', digits=(16, Eval('currency_digits', 2)),
|
||||||
required=True, readonly=True, depends=['currency_digits'])
|
required=True, readonly=True, depends=['currency_digits'])
|
||||||
reconciliation = fields.Many2One(string='Reconciliation', readonly=True,
|
reconciliation = fields.Many2One(string='Reconciliation', readonly=True,
|
||||||
model_name='cashbook.recon', ondelete='SET NULL')
|
model_name='cashbook.recon', ondelete='SET NULL',
|
||||||
|
domain=[('cashbook.id', '=', Eval('cashbook'))],
|
||||||
|
depends=['cashbook'])
|
||||||
|
|
||||||
balance = fields.Function(fields.Numeric(string='Balance',
|
balance = fields.Function(fields.Numeric(string='Balance',
|
||||||
digits=(16, Eval('currency_digits', 2)),
|
digits=(16, Eval('currency_digits', 2)),
|
||||||
|
@ -184,9 +186,12 @@ class Line(Workflow, ModelSQL, ModelView):
|
||||||
def get_rec_name(self, name):
|
def get_rec_name(self, name):
|
||||||
""" short + name
|
""" short + name
|
||||||
"""
|
"""
|
||||||
return '%(date)s %(desc)s' % {
|
return '%(date)s|%(amount)s %(symbol)s|%(desc)s [%(category)s]' % {
|
||||||
'date': Report.format_date(self.date),
|
'date': Report.format_date(self.date),
|
||||||
'desc': (self.description or '-')[:40],
|
'desc': (self.description or '-')[:40],
|
||||||
|
'amount': Report.format_number(self.amount or 0.0, None),
|
||||||
|
'symbol': getattr(self.currency, 'symbol', '-'),
|
||||||
|
'category': self.category_view,
|
||||||
}
|
}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -360,8 +365,23 @@ class Line(Workflow, ModelSQL, ModelView):
|
||||||
""" add debit/credit
|
""" add debit/credit
|
||||||
"""
|
"""
|
||||||
vlist = [x.copy() for x in vlist]
|
vlist = [x.copy() for x in vlist]
|
||||||
for vals in vlist:
|
for values in vlist:
|
||||||
vals.update(cls.get_debit_credit(vals))
|
values.update(cls.get_debit_credit(values))
|
||||||
|
|
||||||
|
# deny add to reconciliation if state is not 'check' or 'done'
|
||||||
|
if values.get('reconciliation', None):
|
||||||
|
if not values.get('state', '-') in ['check', 'done']:
|
||||||
|
date_txt = '-'
|
||||||
|
if values.get('date', None):
|
||||||
|
date_txt = Report.format_date(values.get('date', None))
|
||||||
|
raise UserError(gettext(
|
||||||
|
'cashbook.msg_line_deny_recon_by_state',
|
||||||
|
recname = '%(date)s|%(descr)s' % {
|
||||||
|
'date': date_txt,
|
||||||
|
'descr': values.get('description', '-'),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
|
||||||
return super(Line, cls).create(vlist)
|
return super(Line, cls).create(vlist)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -373,14 +393,49 @@ class Line(Workflow, ModelSQL, ModelView):
|
||||||
to_write = []
|
to_write = []
|
||||||
for lines, values in zip(actions, actions):
|
for lines, values in zip(actions, actions):
|
||||||
for line in lines:
|
for line in lines:
|
||||||
|
# deny write if chashbook is not open
|
||||||
if line.cashbook.state != 'open':
|
if line.cashbook.state != 'open':
|
||||||
raise UserError(gettext(
|
raise UserError(gettext(
|
||||||
'cashbook.msg_book_deny_write',
|
'cashbook.msg_book_deny_write',
|
||||||
bookname = line.cashbook.rec_name,
|
bookname = line.cashbook.rec_name,
|
||||||
state_txt = line.cashbook.state_string,
|
state_txt = line.cashbook.state_string,
|
||||||
))
|
))
|
||||||
|
if line.reconciliation:
|
||||||
|
# deny state-change to 'edit' if line is linked to reconciliation
|
||||||
|
if values.get('state', '-') == 'edit':
|
||||||
|
raise UserError(gettext(
|
||||||
|
'cashbook.msg_line_deny_stateedit_with_recon',
|
||||||
|
recname = line.rec_name,
|
||||||
|
))
|
||||||
|
|
||||||
# debit / credit
|
# deny write if reconciliation is 'check' or 'done'
|
||||||
|
if line.reconciliation.state == 'done':
|
||||||
|
raise UserError(gettext(
|
||||||
|
'cashbook.msg_line_deny_write_by_reconciliation',
|
||||||
|
recname = line.rec_name,
|
||||||
|
reconame = line.reconciliation.rec_name,
|
||||||
|
))
|
||||||
|
# deny write if line is not 'Edit'
|
||||||
|
if line.state != 'edit':
|
||||||
|
# allow state-update, if its the only action
|
||||||
|
if not ((len(set({'state', 'reconciliation'}).intersection(values.keys())) > 0) \
|
||||||
|
and (len(values.keys()) == 1)):
|
||||||
|
raise UserError(gettext(
|
||||||
|
'cashbook.msg_line_deny_write',
|
||||||
|
recname = line.rec_name,
|
||||||
|
state_txt = line.state_string,
|
||||||
|
))
|
||||||
|
|
||||||
|
# deny add to reconciliation if state is not 'check' or 'done'
|
||||||
|
if values.get('reconciliation', None):
|
||||||
|
for line in lines:
|
||||||
|
if not line.state in ['check', 'done']:
|
||||||
|
raise UserError(gettext(
|
||||||
|
'cashbook.msg_line_deny_recon_by_state',
|
||||||
|
recname = line.rec_name
|
||||||
|
))
|
||||||
|
|
||||||
|
# update debit / credit
|
||||||
if len(set(values.keys()).intersection(set({'amount', 'bookingtype'}))) > 0:
|
if len(set(values.keys()).intersection(set({'amount', 'bookingtype'}))) > 0:
|
||||||
for line in lines:
|
for line in lines:
|
||||||
values2 = {}
|
values2 = {}
|
||||||
|
|
40
locale/de.po
40
locale/de.po
|
@ -38,6 +38,22 @@ msgctxt "model:ir.message,text:msg_line_deny_delete2"
|
||||||
msgid "The cashbook line '%(linetxt)s' cannot be deleted, its in state '%(linestate)s'."
|
msgid "The cashbook line '%(linetxt)s' cannot be deleted, its in state '%(linestate)s'."
|
||||||
msgstr "Die Kassenbuchzeile '%(linetxt)s' kann nicht gelöscht werden, da sie im Status '%(linestate)s' ist."
|
msgstr "Die Kassenbuchzeile '%(linetxt)s' kann nicht gelöscht werden, da sie im Status '%(linestate)s' ist."
|
||||||
|
|
||||||
|
msgctxt "model:ir.message,text:msg_line_deny_stateedit_with_recon"
|
||||||
|
msgid "The status cannot be changed to 'Edit' as long as the line '%(recname)s' is associated with a reconciliation."
|
||||||
|
msgstr "Der Status kann nicht in 'Bearbeiten' geändert werden, solange die Zeile '%(recname)s' mit einer Abstimmung verbunden ist."
|
||||||
|
|
||||||
|
msgctxt "model:ir.message,text:msg_line_deny_write"
|
||||||
|
msgid "The cashbook line '%(recname)s' is '%(state_txt)s' and cannot be changed."
|
||||||
|
msgstr "Die Kassenbuchzeile '%(recname)s' ist '%(state_txt)s' und kann nicht geändert werden."
|
||||||
|
|
||||||
|
msgctxt "model:ir.message,text:msg_recon_deny_delete1"
|
||||||
|
msgid "The reconciliation '%(recontxt)s' cannot be deleted because the Cashbook '%(bookname)s' is in state '%(bookstate)s'."
|
||||||
|
msgstr "Die Abstimmung '%(recontxt)s' kann nicht gelöscht werden, weil das Kassenbuch '%(bookname)s' im Status '%(bookstate)s' ist."
|
||||||
|
|
||||||
|
msgctxt "model:ir.message,text:msg_recon_deny_delete2"
|
||||||
|
msgid "The reconciliation '%(recontxt)s' cannot be deleted, its in state '%(reconstate)s'."
|
||||||
|
msgstr "Die Abstimmung '%(recontxt)s' kann nicht gelöscht werden, da sie im Status '%(reconstate)s' ist."
|
||||||
|
|
||||||
msgctxt "model:ir.message,text:msg_setting_already_exists"
|
msgctxt "model:ir.message,text:msg_setting_already_exists"
|
||||||
msgid "Settings for this user alredy exists."
|
msgid "Settings for this user alredy exists."
|
||||||
msgstr "Einstellungen für diesen Benutzer sind bereits vorhanden."
|
msgstr "Einstellungen für diesen Benutzer sind bereits vorhanden."
|
||||||
|
@ -58,6 +74,22 @@ msgctxt "model:ir.message,text:msg_book_err_startamount_with_lines"
|
||||||
msgid "The initial amount of the cash book '%(bookname)s' cannot be changed because it already contains bookings."
|
msgid "The initial amount of the cash book '%(bookname)s' cannot be changed because it already contains bookings."
|
||||||
msgstr "Der Anfangsbetrag des Kassenbuchs '%(bookname)s' kann nicht geändert werden, da es bereits Buchungen enthält."
|
msgstr "Der Anfangsbetrag des Kassenbuchs '%(bookname)s' kann nicht geändert werden, da es bereits Buchungen enthält."
|
||||||
|
|
||||||
|
msgctxt "model:ir.message,text:msg_line_deny_recon_by_state"
|
||||||
|
msgid "For reconciliation, the line '%(recname)s' must be in the status 'Check' or 'Done'."
|
||||||
|
msgstr "Für die Abstimmung muss die Zeile '%(recname)s' im Status 'Prüfen' oder 'Fertig' sein."
|
||||||
|
|
||||||
|
msgctxt "model:ir.message,text:mds_recon_deny_line_not_check"
|
||||||
|
msgid "For the reconciliation '%(reconame)s' of the cashbook '%(bookname)s', all lines in the date range from '%(datefrom)s' to '%(dateto)s' must be in the 'Check' state."
|
||||||
|
msgstr "Für die Abstimmung '%(reconame)s' des Kassenbuchs '%(bookname)s' müssen alle Zeilen im Datumsbereich von '%(datefrom)s' bis '%(dateto)s' im Zustand 'Prüfen' sein."
|
||||||
|
|
||||||
|
msgctxt "model:ir.message,text:msg_line_deny_write_by_reconciliation"
|
||||||
|
msgid "The line '%(recname)s' cannot be changed because the reconciliation '%(reconame)s'is 'Done'."
|
||||||
|
msgstr "Die Zeile '%(recname)s' kann nicht geändert werden, da die Abstimmung '%(reconame)s' ist."
|
||||||
|
|
||||||
|
msgctxt "model:ir.message,text:msg_recon_err_overlap"
|
||||||
|
msgid "The date range overlaps with another reconciliation."
|
||||||
|
msgstr "Der Datumsbereich überschneidet sich mit einer anderen Abstimmung."
|
||||||
|
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# res.group #
|
# res.group #
|
||||||
|
@ -801,3 +833,11 @@ msgstr "Geschlossen"
|
||||||
msgctxt "selection:cashbook.recon,state_cashbook:"
|
msgctxt "selection:cashbook.recon,state_cashbook:"
|
||||||
msgid "Archive"
|
msgid "Archive"
|
||||||
msgstr "Archiv"
|
msgstr "Archiv"
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.recon,start_amount:"
|
||||||
|
msgid "Start Amount"
|
||||||
|
msgstr "Anfangsbetrag"
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.recon,end_amount:"
|
||||||
|
msgid "End Amount"
|
||||||
|
msgstr "Endbetrag"
|
||||||
|
|
358
locale/en.po
358
locale/en.po
|
@ -34,6 +34,18 @@ msgctxt "model:ir.message,text:msg_line_deny_delete2"
|
||||||
msgid "The cashbook line '%(linetxt)s' cannot be deleted, its in state '%(linestate)s'."
|
msgid "The cashbook line '%(linetxt)s' cannot be deleted, its in state '%(linestate)s'."
|
||||||
msgstr "The cashbook line '%(linetxt)s' cannot be deleted, its in state '%(linestate)s'."
|
msgstr "The cashbook line '%(linetxt)s' cannot be deleted, its in state '%(linestate)s'."
|
||||||
|
|
||||||
|
msgctxt "model:ir.message,text:msg_line_deny_write"
|
||||||
|
msgid "The cashbook line '%(recname)s' is '%(state_txt)s' and cannot be changed."
|
||||||
|
msgstr "The cashbook line '%(recname)s' is '%(state_txt)s' and cannot be changed."
|
||||||
|
|
||||||
|
msgctxt "model:ir.message,text:msg_recon_deny_delete1"
|
||||||
|
msgid "The reconciliation '%(recontxt)s' cannot be deleted because the Cashbook '%(bookname)s' is in state '%(bookstate)s'."
|
||||||
|
msgstr "The reconciliation '%(recontxt)s' cannot be deleted because the Cashbook '%(bookname)s' is in state '%(bookstate)s'."
|
||||||
|
|
||||||
|
msgctxt "model:ir.message,text:msg_recon_deny_delete2"
|
||||||
|
msgid "The reconciliation '%(recontxt)s' cannot be deleted, its in state '%(reconstate)s'."
|
||||||
|
msgstr "The reconciliation '%(recontxt)s' cannot be deleted, its in state '%(reconstate)s'."
|
||||||
|
|
||||||
msgctxt "model:ir.message,text:msg_setting_already_exists"
|
msgctxt "model:ir.message,text:msg_setting_already_exists"
|
||||||
msgid "Settings for this user alredy exists."
|
msgid "Settings for this user alredy exists."
|
||||||
msgstr "Settings for this user alredy exists."
|
msgstr "Settings for this user alredy exists."
|
||||||
|
@ -42,6 +54,30 @@ msgctxt "model:ir.message,text:msg_category_name_unique"
|
||||||
msgid "The category name already exists at this level."
|
msgid "The category name already exists at this level."
|
||||||
msgstr "The category name already exists at this level."
|
msgstr "The category name already exists at this level."
|
||||||
|
|
||||||
|
msgctxt "model:ir.message,text:msg_category_account_unique"
|
||||||
|
msgid "The account is already in use for a category."
|
||||||
|
msgstr "The account is already in use for a category."
|
||||||
|
|
||||||
|
msgctxt "model:ir.message,text:msg_category_type_not_like_parent"
|
||||||
|
msgid "The type of the current category '%(catname)s' must be equal to the type of the parent category '%(parentname)s'."
|
||||||
|
msgstr "The type of the current category '%(catname)s' must be equal to the type of the parent category '%(parentname)s'."
|
||||||
|
|
||||||
|
msgctxt "model:ir.message,text:msg_book_err_startamount_with_lines"
|
||||||
|
msgid "The initial amount of the cash book '%(bookname)s' cannot be changed because it already contains bookings."
|
||||||
|
msgstr "The initial amount of the cash book '%(bookname)s' cannot be changed because it already contains bookings."
|
||||||
|
|
||||||
|
msgctxt "model:ir.message,text:msg_line_deny_recon_by_state"
|
||||||
|
msgid "For reconciliation, the line '%(recname)s' must be in the status 'Check' or 'Done'."
|
||||||
|
msgstr "For reconciliation, the line '%(recname)s' must be in the status 'Check' or 'Done'."
|
||||||
|
|
||||||
|
msgctxt "model:ir.message,text:mds_recon_deny_line_not_check"
|
||||||
|
msgid "For the reconciliation '%(reconame)s' of the cashbook '%(bookname)s', all lines in the date range from '%(datefrom)s' to '%(dateto)s' must be in the 'Check' state."
|
||||||
|
msgstr "For the reconciliation '%(reconame)s' of the cashbook '%(bookname)s', all lines in the date range from '%(datefrom)s' to '%(dateto)s' must be in the 'Check' state."
|
||||||
|
|
||||||
|
msgctxt "model:ir.message,text:msg_line_deny_write_by_reconciliation"
|
||||||
|
msgid "The line '%(recname)s' cannot be changed because the reconciliation '%(reconame)s'is 'Done'."
|
||||||
|
msgstr "The line '%(recname)s' cannot be changed because the reconciliation '%(reconame)s'is 'Done'."
|
||||||
|
|
||||||
msgctxt "model:res.group,name:group_cashbook"
|
msgctxt "model:res.group,name:group_cashbook"
|
||||||
msgid "Cashbook"
|
msgid "Cashbook"
|
||||||
msgstr "Cashbook"
|
msgstr "Cashbook"
|
||||||
|
@ -74,6 +110,30 @@ msgctxt "model:ir.rule.group,name:rg_line_read"
|
||||||
msgid "Observer: Cashbook line read"
|
msgid "Observer: Cashbook line read"
|
||||||
msgstr "Observer: Cashbook line read"
|
msgstr "Observer: Cashbook line read"
|
||||||
|
|
||||||
|
msgctxt "model:ir.rule.group,name:rg_line_read"
|
||||||
|
msgid "User in companies"
|
||||||
|
msgstr "User in companies"
|
||||||
|
|
||||||
|
msgctxt "model:ir.rule.group,name:rg_type_companies"
|
||||||
|
msgid "User in companies"
|
||||||
|
msgstr "User in companies"
|
||||||
|
|
||||||
|
msgctxt "model:ir.rule.group,name:rg_book_companies"
|
||||||
|
msgid "User in companies"
|
||||||
|
msgstr "User in companies"
|
||||||
|
|
||||||
|
msgctxt "model:ir.rule.group,name:rg_recon_companies"
|
||||||
|
msgid "User in companies"
|
||||||
|
msgstr "User in companies"
|
||||||
|
|
||||||
|
msgctxt "model:ir.rule.group,name:rg_recon_write_adm"
|
||||||
|
msgid "Administrators: Reconciliation read/write"
|
||||||
|
msgstr "Administrators: Reconciliation read/write"
|
||||||
|
|
||||||
|
msgctxt "model:ir.rule.group,name:rg_recon_write"
|
||||||
|
msgid "Owners and reviewers: Reconciliation write"
|
||||||
|
msgstr "Owners and reviewers: Reconciliation write"
|
||||||
|
|
||||||
msgctxt "model:ir.ui.menu,name:menu_cashbook"
|
msgctxt "model:ir.ui.menu,name:menu_cashbook"
|
||||||
msgid "Cashbook"
|
msgid "Cashbook"
|
||||||
msgstr "Cashbook"
|
msgstr "Cashbook"
|
||||||
|
@ -86,7 +146,7 @@ msgctxt "model:ir.ui.menu,name:menu_typeconfig"
|
||||||
msgid "Cashbook Type"
|
msgid "Cashbook Type"
|
||||||
msgstr "Cashbook Type"
|
msgstr "Cashbook Type"
|
||||||
|
|
||||||
msgctxt "model:ir.ui.menu,name:menu_bookconfig"
|
msgctxt "model:ir.ui.menu,name:menu_booklist"
|
||||||
msgid "Cashbook"
|
msgid "Cashbook"
|
||||||
msgstr "Cashbook"
|
msgstr "Cashbook"
|
||||||
|
|
||||||
|
@ -103,8 +163,8 @@ msgid "Category"
|
||||||
msgstr "Category"
|
msgstr "Category"
|
||||||
|
|
||||||
msgctxt "model:ir.action,name:act_book_view"
|
msgctxt "model:ir.action,name:act_book_view"
|
||||||
msgid "Account"
|
msgid "Cashbook"
|
||||||
msgstr "Account"
|
msgstr "Cashbook"
|
||||||
|
|
||||||
msgctxt "model:ir.action,name:act_type_view"
|
msgctxt "model:ir.action,name:act_type_view"
|
||||||
msgid "Cashbook Type"
|
msgid "Cashbook Type"
|
||||||
|
@ -122,6 +182,18 @@ msgctxt "model:ir.ui.menu,name:act_category_view"
|
||||||
msgid "Category"
|
msgid "Category"
|
||||||
msgstr "Category"
|
msgstr "Category"
|
||||||
|
|
||||||
|
msgctxt "model:ir.action.act_window.domain,name:act_line_domain_current"
|
||||||
|
msgid "Current Month"
|
||||||
|
msgstr "Current Month"
|
||||||
|
|
||||||
|
msgctxt "model:ir.action.act_window.domain,name:act_line_domain_last"
|
||||||
|
msgid "Last Month"
|
||||||
|
msgstr "Last Month"
|
||||||
|
|
||||||
|
msgctxt "model:ir.action.act_window.domain,name:act_line_domain_all"
|
||||||
|
msgid "All"
|
||||||
|
msgstr "All"
|
||||||
|
|
||||||
msgctxt "model:ir.model.button,string:line_wfedit_button"
|
msgctxt "model:ir.model.button,string:line_wfedit_button"
|
||||||
msgid "Edit"
|
msgid "Edit"
|
||||||
msgstr "Edit"
|
msgstr "Edit"
|
||||||
|
@ -146,13 +218,29 @@ msgctxt "model:ir.model.button,string:book_wfarchive_button"
|
||||||
msgid "Archive"
|
msgid "Archive"
|
||||||
msgstr "Archive"
|
msgstr "Archive"
|
||||||
|
|
||||||
|
msgctxt "model:ir.model.button,string:recon_wfedit_button"
|
||||||
|
msgid "Edit"
|
||||||
|
msgstr "Edit"
|
||||||
|
|
||||||
|
msgctxt "model:ir.model.button,string:recon_wfcheck_button"
|
||||||
|
msgid "Check"
|
||||||
|
msgstr "Check"
|
||||||
|
|
||||||
|
msgctxt "model:ir.model.button,string:recon_wfdone_button"
|
||||||
|
msgid "Done"
|
||||||
|
msgstr "Done"
|
||||||
|
|
||||||
msgctxt "model:cashbook.book,name:"
|
msgctxt "model:cashbook.book,name:"
|
||||||
msgid "Cashbook"
|
msgid "Cashbook"
|
||||||
msgstr "Cashbook"
|
msgstr "Cashbook"
|
||||||
|
|
||||||
msgctxt "view:cashbook.book:"
|
msgctxt "view:cashbook.book:"
|
||||||
msgid "Owner & Authorizeds"
|
msgid "Owner and Authorizeds"
|
||||||
msgstr "Owner & Authorizeds"
|
msgstr "Owner and Authorizeds"
|
||||||
|
|
||||||
|
msgctxt "view:cashbook.book:"
|
||||||
|
msgid "Reconciliations"
|
||||||
|
msgstr "Reconciliations"
|
||||||
|
|
||||||
msgctxt "field:cashbook.book,name:"
|
msgctxt "field:cashbook.book,name:"
|
||||||
msgid "Name"
|
msgid "Name"
|
||||||
|
@ -202,14 +290,50 @@ msgctxt "field:cashbook.book,account:"
|
||||||
msgid "Account"
|
msgid "Account"
|
||||||
msgstr "Account"
|
msgstr "Account"
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.book,company:"
|
||||||
|
msgid "Company"
|
||||||
|
msgstr "Company"
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.book,currency:"
|
||||||
|
msgid "Currency"
|
||||||
|
msgstr "Currency"
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.book,start_balance:"
|
||||||
|
msgid "Initial Amount"
|
||||||
|
msgstr "Initial Amount"
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.book,balance:"
|
||||||
|
msgid "Balance"
|
||||||
|
msgstr "Balance"
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.book,reconciliations:"
|
||||||
|
msgid "Reconciliations"
|
||||||
|
msgstr "Reconciliations"
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.book,lines:"
|
||||||
|
msgid "Lines"
|
||||||
|
msgstr "Lines"
|
||||||
|
|
||||||
msgctxt "model:cashbook.line,name:"
|
msgctxt "model:cashbook.line,name:"
|
||||||
msgid "Cashbook Line"
|
msgid "Cashbook Line"
|
||||||
msgstr "Cashbook Line"
|
msgstr "Cashbook Line"
|
||||||
|
|
||||||
|
msgctxt "view:cashbook.line:"
|
||||||
|
msgid "Credit"
|
||||||
|
msgstr "Credit"
|
||||||
|
|
||||||
|
msgctxt "view:cashbook.line:"
|
||||||
|
msgid "Debit"
|
||||||
|
msgstr "Debit"
|
||||||
|
|
||||||
msgctxt "view:cashbook.line:"
|
msgctxt "view:cashbook.line:"
|
||||||
msgid "Cashbook Line"
|
msgid "Cashbook Line"
|
||||||
msgstr "Cashbook Line"
|
msgstr "Cashbook Line"
|
||||||
|
|
||||||
|
msgctxt "view:cashbook.line:"
|
||||||
|
msgid "Description"
|
||||||
|
msgstr "Description"
|
||||||
|
|
||||||
msgctxt "view:cashbook.line:"
|
msgctxt "view:cashbook.line:"
|
||||||
msgid "State"
|
msgid "State"
|
||||||
msgstr "State"
|
msgstr "State"
|
||||||
|
@ -246,17 +370,73 @@ msgctxt "field:cashbook.line,month:"
|
||||||
msgid "Month"
|
msgid "Month"
|
||||||
msgstr "Month"
|
msgstr "Month"
|
||||||
|
|
||||||
msgctxt "model:ir.action.act_window.domain,name:act_line_domain_current"
|
msgctxt "field:cashbook.line,category:"
|
||||||
msgid "Current Month"
|
msgid "Category"
|
||||||
msgstr "Current Month"
|
msgstr "Category"
|
||||||
|
|
||||||
msgctxt "model:ir.action.act_window.domain,name:act_line_domain_last"
|
msgctxt "field:cashbook.line,category_view:"
|
||||||
msgid "Last Month"
|
msgid "Category"
|
||||||
msgstr "Last Month"
|
msgstr "Category"
|
||||||
|
|
||||||
msgctxt "model:ir.action.act_window.domain,name:act_line_domain_all"
|
msgctxt "field:cashbook.line,bookingtype:"
|
||||||
msgid "All"
|
msgid "Type"
|
||||||
msgstr "All"
|
msgstr "Type"
|
||||||
|
|
||||||
|
msgctxt "help:cashbook.line,bookingtype:"
|
||||||
|
msgid "Type of Booking"
|
||||||
|
msgstr "Type of Booking"
|
||||||
|
|
||||||
|
msgctxt "selection:cashbook.line,bookingtype:"
|
||||||
|
msgid "Revenue"
|
||||||
|
msgstr "Revenue"
|
||||||
|
|
||||||
|
msgctxt "selection:cashbook.line,bookingtype:"
|
||||||
|
msgid "Expense"
|
||||||
|
msgstr "Expense"
|
||||||
|
|
||||||
|
msgctxt "selection:cashbook.line,bookingtype:"
|
||||||
|
msgid "Transfer from"
|
||||||
|
msgstr "Transfer from"
|
||||||
|
|
||||||
|
msgctxt "selection:cashbook.line,bookingtype:"
|
||||||
|
msgid "Transfer to"
|
||||||
|
msgstr "Transfer to"
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.line,company:"
|
||||||
|
msgid "Company"
|
||||||
|
msgstr "Company"
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.line,amount:"
|
||||||
|
msgid "Amount"
|
||||||
|
msgstr "Amount"
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.line,debit:"
|
||||||
|
msgid "Debit"
|
||||||
|
msgstr "Debit"
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.line,credit:"
|
||||||
|
msgid "Credit"
|
||||||
|
msgstr "Credit"
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.line,currency:"
|
||||||
|
msgid "Currency"
|
||||||
|
msgstr "Currency"
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.line,currency_digits:"
|
||||||
|
msgid "Currency Digits"
|
||||||
|
msgstr "Currency Digits"
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.line,balance:"
|
||||||
|
msgid "Balance"
|
||||||
|
msgstr "Balance"
|
||||||
|
|
||||||
|
msgctxt "help:cashbook.line,balance:"
|
||||||
|
msgid "Balance of the cash book up to the current line, if the default sorting applies."
|
||||||
|
msgstr "Balance of the cash book up to the current line, if the default sorting applies."
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.line,reconciliation:"
|
||||||
|
msgid "Reconciliation"
|
||||||
|
msgstr "Reconciliation"
|
||||||
|
|
||||||
msgctxt "model:cashbook.type,name:"
|
msgctxt "model:cashbook.type,name:"
|
||||||
msgid "Cashbook Type"
|
msgid "Cashbook Type"
|
||||||
|
@ -270,17 +450,9 @@ msgctxt "field:cashbook.type,short:"
|
||||||
msgid "Abbreviation"
|
msgid "Abbreviation"
|
||||||
msgstr "Abbreviation"
|
msgstr "Abbreviation"
|
||||||
|
|
||||||
msgctxt "model:cashbook.type,name:atype_cash"
|
msgctxt "field:cashbook.type,company:"
|
||||||
msgid "Cash"
|
msgid "Company"
|
||||||
msgstr "Cash"
|
msgstr "Company"
|
||||||
|
|
||||||
msgctxt "model:cashbook.type,name:atype_giro"
|
|
||||||
msgid "Giro"
|
|
||||||
msgstr "Giro"
|
|
||||||
|
|
||||||
msgctxt "model:cashbook.type,name:atype_fixtermdep"
|
|
||||||
msgid "Fixed-term deposit"
|
|
||||||
msgstr "Fixed-term deposit"
|
|
||||||
|
|
||||||
msgctxt "model:cashbook.category,name:"
|
msgctxt "model:cashbook.category,name:"
|
||||||
msgid "Category"
|
msgid "Category"
|
||||||
|
@ -330,6 +502,22 @@ msgctxt "field:cashbook.category,right:"
|
||||||
msgid "Right"
|
msgid "Right"
|
||||||
msgstr "Right"
|
msgstr "Right"
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.category,cattype:"
|
||||||
|
msgid "Type"
|
||||||
|
msgstr "Type"
|
||||||
|
|
||||||
|
msgctxt "help:cashbook.category,cattype:"
|
||||||
|
msgid "Type of Category"
|
||||||
|
msgstr "Type of Category"
|
||||||
|
|
||||||
|
msgctxt "selection:cashbook.category,cattype:"
|
||||||
|
msgid "Revenue"
|
||||||
|
msgstr "Revenue"
|
||||||
|
|
||||||
|
msgctxt "selection:cashbook.category,cattype:"
|
||||||
|
msgid "Expense"
|
||||||
|
msgstr "Expense"
|
||||||
|
|
||||||
msgctxt "model:cashbook.open_lines.start,name:"
|
msgctxt "model:cashbook.open_lines.start,name:"
|
||||||
msgid "Open Cashbook"
|
msgid "Open Cashbook"
|
||||||
msgstr "Open Cashbook"
|
msgstr "Open Cashbook"
|
||||||
|
@ -370,11 +558,11 @@ msgctxt "model:cashbook.open_lines,name:"
|
||||||
msgid "Open Cashbook"
|
msgid "Open Cashbook"
|
||||||
msgstr "Open Cashbook"
|
msgstr "Open Cashbook"
|
||||||
|
|
||||||
msgctxt "wizard_button:cashbook.open_lines,start,end:"
|
msgctxt "wizard_button:cashbook.open_lines,askuser,end:"
|
||||||
msgid "Cancel"
|
msgid "Cancel"
|
||||||
msgstr "Cancel"
|
msgstr "Cancel"
|
||||||
|
|
||||||
msgctxt "wizard_button:cashbook.open_lines,start,open_:"
|
msgctxt "wizard_button:cashbook.open_lines,askuser,open_:"
|
||||||
msgid "Open"
|
msgid "Open"
|
||||||
msgstr "Open"
|
msgstr "Open"
|
||||||
|
|
||||||
|
@ -418,6 +606,10 @@ msgctxt "view:cashbook.configuration:"
|
||||||
msgid "Open Cashbook Wizard"
|
msgid "Open Cashbook Wizard"
|
||||||
msgstr "Open Cashbook Wizard"
|
msgstr "Open Cashbook Wizard"
|
||||||
|
|
||||||
|
msgctxt "view:cashbook.configuration:"
|
||||||
|
msgid "Cashbook"
|
||||||
|
msgstr "Cashbook"
|
||||||
|
|
||||||
msgctxt "field:cashbook.configuration,date_from:"
|
msgctxt "field:cashbook.configuration,date_from:"
|
||||||
msgid "Start Date"
|
msgid "Start Date"
|
||||||
msgstr "Start Date"
|
msgstr "Start Date"
|
||||||
|
@ -430,10 +622,34 @@ msgctxt "field:cashbook.configuration,checked:"
|
||||||
msgid "Checked"
|
msgid "Checked"
|
||||||
msgstr "Checked"
|
msgstr "Checked"
|
||||||
|
|
||||||
|
msgctxt "help:cashbook.configuration,checked:"
|
||||||
|
msgid "Show cashbook lines in Checked-state."
|
||||||
|
msgstr "Show cashbook lines in Checked-state."
|
||||||
|
|
||||||
msgctxt "field:cashbook.configuration,done:"
|
msgctxt "field:cashbook.configuration,done:"
|
||||||
msgid "Done"
|
msgid "Done"
|
||||||
msgstr "Done"
|
msgstr "Done"
|
||||||
|
|
||||||
|
msgctxt "help:cashbook.configuration,done:"
|
||||||
|
msgid "Show cashbook lines in Done-state."
|
||||||
|
msgstr "Show cashbook lines in Done-state."
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.configuration,catnamelong:"
|
||||||
|
msgid "Category: Show long name"
|
||||||
|
msgstr "Category: Show long name"
|
||||||
|
|
||||||
|
msgctxt "help:cashbook.configuration,catnamelong:"
|
||||||
|
msgid "Shows the long name of the category in the Category field of a cash book line."
|
||||||
|
msgstr "Shows the long name of the category in the Category field of a cash book line."
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.configuration,cataccno:"
|
||||||
|
msgid "Category: Show account number"
|
||||||
|
msgstr "Category: Show account number"
|
||||||
|
|
||||||
|
msgctxt "help:cashbook.configuration,cataccno:"
|
||||||
|
msgid "Shows the number of the linked account in the name of a category."
|
||||||
|
msgstr "Shows the number of the linked account in the name of a category."
|
||||||
|
|
||||||
msgctxt "model:cashbook.configuration_user,name:"
|
msgctxt "model:cashbook.configuration_user,name:"
|
||||||
msgid "User Configuration"
|
msgid "User Configuration"
|
||||||
msgstr "User Configuration"
|
msgstr "User Configuration"
|
||||||
|
@ -450,3 +666,91 @@ msgctxt "field:cashbook.configuration_user,checked:"
|
||||||
msgid "Checked"
|
msgid "Checked"
|
||||||
msgstr "Checked"
|
msgstr "Checked"
|
||||||
|
|
||||||
|
msgctxt "help:cashbook.configuration_user,checked:"
|
||||||
|
msgid "Show cashbook lines in Checked-state."
|
||||||
|
msgstr "Show cashbook lines in Checked-state."
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.configuration_user,done:"
|
||||||
|
msgid "Done"
|
||||||
|
msgstr "Done"
|
||||||
|
|
||||||
|
msgctxt "help:cashbook.configuration_user,done:"
|
||||||
|
msgid "Show cashbook lines in Done-state."
|
||||||
|
msgstr "Show cashbook lines in Done-state."
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.configuration_user,catnamelong:"
|
||||||
|
msgid "Category: Show long name"
|
||||||
|
msgstr "Category: Show long name"
|
||||||
|
|
||||||
|
msgctxt "help:cashbook.configuration_user,catnamelong:"
|
||||||
|
msgid "Shows the long name of the category in the Category field of a cash book line."
|
||||||
|
msgstr "Shows the long name of the category in the Category field of a cash book line."
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.configuration_user,cataccno:"
|
||||||
|
msgid "Category: Show account number"
|
||||||
|
msgstr "Category: Show account number"
|
||||||
|
|
||||||
|
msgctxt "help:cashbook.configuration_user,cataccno:"
|
||||||
|
msgid "Shows the number of the linked account in the name of a category."
|
||||||
|
msgstr "Shows the number of the linked account in the name of a category."
|
||||||
|
|
||||||
|
msgctxt "model:cashbook.recon,name:"
|
||||||
|
msgid "Cashbook Reconciliation"
|
||||||
|
msgstr "Cashbook Reconciliation"
|
||||||
|
|
||||||
|
msgctxt "view:cashbook.recon:"
|
||||||
|
msgid "Reconciliation period"
|
||||||
|
msgstr "Reconciliation period"
|
||||||
|
|
||||||
|
msgctxt "view:cashbook.recon:"
|
||||||
|
msgid "State"
|
||||||
|
msgstr "State"
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.recon,cashbook:"
|
||||||
|
msgid "Cashbook"
|
||||||
|
msgstr "Cashbook"
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.recon,date:"
|
||||||
|
msgid "Date"
|
||||||
|
msgstr "Date"
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.recon,date_from:"
|
||||||
|
msgid "Start Date"
|
||||||
|
msgstr "Start Date"
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.recon,date_to:"
|
||||||
|
msgid "End Date"
|
||||||
|
msgstr "End Date"
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.recon,state:"
|
||||||
|
msgid "State"
|
||||||
|
msgstr "State"
|
||||||
|
|
||||||
|
msgctxt "selection:cashbook.recon,state:"
|
||||||
|
msgid "Edit"
|
||||||
|
msgstr "Edit"
|
||||||
|
|
||||||
|
msgctxt "selection:cashbook.recon,state:"
|
||||||
|
msgid "Check"
|
||||||
|
msgstr "Check"
|
||||||
|
|
||||||
|
msgctxt "selection:cashbook.recon,state:"
|
||||||
|
msgid "Done"
|
||||||
|
msgstr "Done"
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.recon,lines:"
|
||||||
|
msgid "Lines"
|
||||||
|
msgstr "Lines"
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.recon,state_cashbook:"
|
||||||
|
msgid "State of Cashbook"
|
||||||
|
msgstr "State of Cashbook"
|
||||||
|
|
||||||
|
msgctxt "selection:cashbook.recon,state_cashbook:"
|
||||||
|
msgid "Open"
|
||||||
|
msgstr "Open"
|
||||||
|
|
||||||
|
msgctxt "selection:cashbook.recon,state_cashbook:"
|
||||||
|
msgid "Closed"
|
||||||
|
msgstr "Closed"
|
||||||
|
|
||||||
|
|
24
message.xml
24
message.xml
|
@ -29,6 +29,18 @@ full copyright notices and license terms. -->
|
||||||
<record model="ir.message" id="msg_line_deny_delete2">
|
<record model="ir.message" id="msg_line_deny_delete2">
|
||||||
<field name="text">The cashbook line '%(linetxt)s' cannot be deleted, its in state '%(linestate)s'.</field>
|
<field name="text">The cashbook line '%(linetxt)s' cannot be deleted, its in state '%(linestate)s'.</field>
|
||||||
</record>
|
</record>
|
||||||
|
<record model="ir.message" id="msg_line_deny_write">
|
||||||
|
<field name="text">The cashbook line '%(recname)s' is '%(state_txt)s' and cannot be changed.</field>
|
||||||
|
</record>
|
||||||
|
<record model="ir.message" id="msg_line_deny_stateedit_with_recon">
|
||||||
|
<field name="text">The status cannot be changed to 'Edit' as long as the line '%(recname)s' is associated with a reconciliation.</field>
|
||||||
|
</record>
|
||||||
|
<record model="ir.message" id="msg_recon_deny_delete1">
|
||||||
|
<field name="text">The reconciliation '%(recontxt)s' cannot be deleted because the Cashbook '%(bookname)s' is in state '%(bookstate)s'.</field>
|
||||||
|
</record>
|
||||||
|
<record model="ir.message" id="msg_recon_deny_delete2">
|
||||||
|
<field name="text">The reconciliation '%(recontxt)s' cannot be deleted, its in state '%(reconstate)s'.</field>
|
||||||
|
</record>
|
||||||
<record model="ir.message" id="msg_setting_already_exists">
|
<record model="ir.message" id="msg_setting_already_exists">
|
||||||
<field name="text">Settings for this user alredy exists.</field>
|
<field name="text">Settings for this user alredy exists.</field>
|
||||||
</record>
|
</record>
|
||||||
|
@ -44,6 +56,18 @@ full copyright notices and license terms. -->
|
||||||
<record model="ir.message" id="msg_book_err_startamount_with_lines">
|
<record model="ir.message" id="msg_book_err_startamount_with_lines">
|
||||||
<field name="text">The initial amount of the cash book '%(bookname)s' cannot be changed because it already contains bookings.</field>
|
<field name="text">The initial amount of the cash book '%(bookname)s' cannot be changed because it already contains bookings.</field>
|
||||||
</record>
|
</record>
|
||||||
|
<record model="ir.message" id="msg_line_deny_recon_by_state">
|
||||||
|
<field name="text">For reconciliation, the line '%(recname)s' must be in the status 'Check' or 'Done'.</field>
|
||||||
|
</record>
|
||||||
|
<record model="ir.message" id="mds_recon_deny_line_not_check">
|
||||||
|
<field name="text">For the reconciliation '%(reconame)s' of the cashbook '%(bookname)s', all lines in the date range from '%(datefrom)s' to '%(dateto)s' must be in the 'Check' state.</field>
|
||||||
|
</record>
|
||||||
|
<record model="ir.message" id="msg_line_deny_write_by_reconciliation">
|
||||||
|
<field name="text">The line '%(recname)s' cannot be changed because the reconciliation '%(reconame)s'is 'Done'.</field>
|
||||||
|
</record>
|
||||||
|
<record model="ir.message" id="msg_recon_err_overlap">
|
||||||
|
<field name="text">The date range overlaps with another reconciliation.</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
</data>
|
</data>
|
||||||
</tryton>
|
</tryton>
|
||||||
|
|
|
@ -3,12 +3,16 @@
|
||||||
# The COPYRIGHT file at the top level of this repository contains the
|
# The COPYRIGHT file at the top level of this repository contains the
|
||||||
# full copyright notices and license terms.
|
# full copyright notices and license terms.
|
||||||
|
|
||||||
from trytond.model import Workflow, ModelView, ModelSQL, fields, Unique
|
from trytond.model import Workflow, ModelView, ModelSQL, fields
|
||||||
from trytond.transaction import Transaction
|
from trytond.transaction import Transaction
|
||||||
from trytond.pyson import Eval, If, Or
|
from trytond.pyson import Eval, If, Or
|
||||||
from trytond.pool import Pool
|
from trytond.pool import Pool
|
||||||
from trytond.report import Report
|
from trytond.report import Report
|
||||||
|
from trytond.exceptions import UserError
|
||||||
|
from trytond.i18n import gettext
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
from sql.operators import Equal, Between
|
||||||
|
from sql import Literal, Null
|
||||||
from .book import sel_state_book
|
from .book import sel_state_book
|
||||||
|
|
||||||
|
|
||||||
|
@ -52,11 +56,19 @@ class Reconciliation(Workflow, ModelSQL, ModelView):
|
||||||
()),
|
()),
|
||||||
],
|
],
|
||||||
states=STATES, depends=DEPENDS+['date_from'])
|
states=STATES, depends=DEPENDS+['date_from'])
|
||||||
|
start_amount = fields.Numeric(string='Start Amount', required=True,
|
||||||
|
readonly=True, digits=(16, Eval('currency_digits', 2)),
|
||||||
|
depends=['currency_digits'])
|
||||||
|
end_amount = fields.Numeric(string='End Amount', required=True,
|
||||||
|
readonly=True, digits=(16, Eval('currency_digits', 2)),
|
||||||
|
depends=['currency_digits'])
|
||||||
|
|
||||||
lines = fields.One2Many(string='Lines', field='reconciliation',
|
lines = fields.One2Many(string='Lines', field='reconciliation',
|
||||||
model_name='cashbook.line', states=STATES,
|
model_name='cashbook.line', states=STATES,
|
||||||
depends=DEPENDS+['date_from', 'date_to', 'cashbook'],
|
depends=DEPENDS+['date_from', 'date_to', 'cashbook'],
|
||||||
add_remove=[
|
add_remove=[
|
||||||
('cashbook', '=', Eval('cashbook')),
|
('cashbook', '=', Eval('cashbook')),
|
||||||
|
('state', 'in', ['check', 'done']),
|
||||||
('date', '>=', Eval('date_from')),
|
('date', '>=', Eval('date_from')),
|
||||||
('date', '<=', Eval('date_to')),
|
('date', '<=', Eval('date_to')),
|
||||||
],
|
],
|
||||||
|
@ -65,6 +77,11 @@ class Reconciliation(Workflow, ModelSQL, ModelView):
|
||||||
('date', '<=', Eval('date_to')),
|
('date', '<=', Eval('date_to')),
|
||||||
])
|
])
|
||||||
|
|
||||||
|
currency = fields.Function(fields.Many2One(model_name='currency.currency',
|
||||||
|
string="Currency"), 'on_change_with_currency')
|
||||||
|
currency_digits = fields.Function(fields.Integer(string='Currency Digits'),
|
||||||
|
'on_change_with_currency_digits')
|
||||||
|
|
||||||
state = fields.Selection(string='State', required=True, readonly=True,
|
state = fields.Selection(string='State', required=True, readonly=True,
|
||||||
select=True, selection=sel_reconstate)
|
select=True, selection=sel_reconstate)
|
||||||
state_string = state.translated('state')
|
state_string = state.translated('state')
|
||||||
|
@ -96,40 +113,169 @@ class Reconciliation(Workflow, ModelSQL, ModelView):
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def check_overlap_dates(cls, date_from, date_to, id_cashbook, record=None):
|
||||||
|
""" deny overlap of date_from/date_to between records of same cashbook
|
||||||
|
allow: date_to=date_from
|
||||||
|
"""
|
||||||
|
Recon = Pool().get('cashbook.recon')
|
||||||
|
|
||||||
|
query = [
|
||||||
|
('cashbook.id', '=', id_cashbook),
|
||||||
|
['OR',
|
||||||
|
[ # 'start' is inside of other record
|
||||||
|
('date_from', '<=', date_from),
|
||||||
|
('date_to', '>', date_from),
|
||||||
|
],
|
||||||
|
[ # 'end' is inside of other record
|
||||||
|
('date_from', '<', date_to),
|
||||||
|
('date_to', '>=', date_to),
|
||||||
|
],
|
||||||
|
[ # enclose other record
|
||||||
|
('date_from', '>=', date_from),
|
||||||
|
('date_to', '<=', date_to),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]
|
||||||
|
|
||||||
|
# avoid finding ourselves
|
||||||
|
if record:
|
||||||
|
query.append(('id', '!=', record.id))
|
||||||
|
|
||||||
|
if Recon.search_count(query) > 0:
|
||||||
|
raise UserError(gettext('cashbook.msg_recon_err_overlap'))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def check_lines_not_checked(cls, reconciliations):
|
||||||
|
""" deny lines in date-range not 'checked', w/o records at date-limit
|
||||||
|
"""
|
||||||
|
Line = Pool().get('cashbook.line')
|
||||||
|
|
||||||
|
for reconciliation in reconciliations:
|
||||||
|
if Line.search_count([
|
||||||
|
('date', '>', reconciliation.date_from),
|
||||||
|
('date', '<', reconciliation.date_to),
|
||||||
|
('cashbook.id', '=', reconciliation.cashbook.id),
|
||||||
|
('state', '!=', 'check'),
|
||||||
|
]) > 0:
|
||||||
|
raise UserError(gettext(
|
||||||
|
'cashbook.mds_recon_deny_line_not_check',
|
||||||
|
bookname = reconciliation.cashbook.rec_name,
|
||||||
|
reconame = reconciliation.rec_name,
|
||||||
|
datefrom = Report.format_date(reconciliation.date_from),
|
||||||
|
dateto = Report.format_date(reconciliation.date_to),
|
||||||
|
))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ModelView.button
|
@ModelView.button
|
||||||
@Workflow.transition('edit')
|
@Workflow.transition('edit')
|
||||||
def wfedit(cls, lines):
|
def wfedit(cls, reconciliations):
|
||||||
""" edit
|
""" edit
|
||||||
"""
|
"""
|
||||||
pass
|
Recon = Pool().get('cashbook.recon')
|
||||||
|
|
||||||
|
to_write = []
|
||||||
|
for reconciliation in reconciliations:
|
||||||
|
values = {
|
||||||
|
'start_amount': Decimal('0.0'),
|
||||||
|
'end_amount': Decimal('0.0'),
|
||||||
|
}
|
||||||
|
|
||||||
|
# unlink lines from reconciliation
|
||||||
|
if len(reconciliation.lines) > 0:
|
||||||
|
values['lines'] = [('remove', [x.id for x in reconciliation.lines])]
|
||||||
|
|
||||||
|
to_write.extend([[reconciliation], values])
|
||||||
|
|
||||||
|
if len(to_write) > 0:
|
||||||
|
Recon.write(*to_write)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ModelView.button
|
@ModelView.button
|
||||||
@Workflow.transition('check')
|
@Workflow.transition('check')
|
||||||
def wfcheck(cls, lines):
|
def wfcheck(cls, reconciliations):
|
||||||
""" is checked
|
""" checked: add lines of book in date-range to reconciliation,
|
||||||
|
state of lines must be 'checked'
|
||||||
"""
|
"""
|
||||||
pass
|
pool = Pool()
|
||||||
|
Line = pool.get('cashbook.line')
|
||||||
|
Recon = pool.get('cashbook.recon')
|
||||||
|
|
||||||
|
cls.check_lines_not_checked(reconciliations)
|
||||||
|
|
||||||
|
to_write = []
|
||||||
|
for reconciliation in reconciliations:
|
||||||
|
values = {}
|
||||||
|
|
||||||
|
# get start_amount: end_amount of predecessor
|
||||||
|
pre_recon = Recon.search([
|
||||||
|
('cashbook.id', '=', reconciliation.cashbook.id),
|
||||||
|
('date_to', '<=', reconciliation.date_from),
|
||||||
|
('state', 'in', ['check', 'done']),
|
||||||
|
], order=[('date_to', 'DESC')], limit=1)
|
||||||
|
if len(pre_recon) > 0:
|
||||||
|
values['start_amount'] = pre_recon[0].end_amount
|
||||||
|
else :
|
||||||
|
# not found, use 'start_balance' of cashbook
|
||||||
|
values['start_amount'] = reconciliation.cashbook.start_balance
|
||||||
|
values['end_amount'] = values['start_amount']
|
||||||
|
|
||||||
|
# add 'checked'-lines to reconciliation
|
||||||
|
lines = Line.search([
|
||||||
|
('date', '>=', reconciliation.date_from),
|
||||||
|
('date', '<=', reconciliation.date_to),
|
||||||
|
('cashbook.id', '=', reconciliation.cashbook.id),
|
||||||
|
('reconciliation', '=', None),
|
||||||
|
('state', '=', 'check'),
|
||||||
|
])
|
||||||
|
if len(lines) > 0:
|
||||||
|
values['lines'] = [('add', [x.id for x in lines])]
|
||||||
|
|
||||||
|
# add amounts of new lines
|
||||||
|
values['end_amount'] += sum([x.credit - x.debit for x in lines])
|
||||||
|
# add amounts of already linked lines
|
||||||
|
values['end_amount'] += sum([x.credit - x.debit for x in reconciliation.lines])
|
||||||
|
|
||||||
|
to_write.extend([[reconciliation], values])
|
||||||
|
|
||||||
|
if len(to_write) > 0:
|
||||||
|
Recon.write(*to_write)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ModelView.button
|
@ModelView.button
|
||||||
@Workflow.transition('done')
|
@Workflow.transition('done')
|
||||||
def wfdone(cls, lines):
|
def wfdone(cls, reconciliations):
|
||||||
""" is done
|
""" is done
|
||||||
"""
|
"""
|
||||||
pass
|
Line = Pool().get('cashbook.line')
|
||||||
|
|
||||||
|
to_wfdone_line = []
|
||||||
|
for reconciliation in reconciliations:
|
||||||
|
to_wfdone_line.extend(list(reconciliation.lines))
|
||||||
|
|
||||||
|
if len(to_wfdone_line) > 0:
|
||||||
|
Line.wfdone(to_wfdone_line)
|
||||||
|
|
||||||
def get_rec_name(self, name):
|
def get_rec_name(self, name):
|
||||||
""" short + name
|
""" short + name
|
||||||
"""
|
"""
|
||||||
return '%(from)s - %(to)s: %(amount)s %(symbol)s' % {
|
return '%(from)s - %(to)s | %(start_amount)s %(symbol)s - %(start_amount)s %(symbol)s [%(num)s]' % {
|
||||||
'from': Report.format_date(self.date_from, None) if self.date_from is not None else '-',
|
'from': Report.format_date(self.date_from, None) if self.date_from is not None else '-',
|
||||||
'to': Report.format_date(self.date_to, None) if self.date_to is not None else '-',
|
'to': Report.format_date(self.date_to, None) if self.date_to is not None else '-',
|
||||||
'amount': Decimal('0.0'),
|
'start_amount': Report.format_number(self.start_amount or 0.0, None),
|
||||||
'symbol': getattr(getattr(self.cashbook, 'currency', None), 'symbol', '-'),
|
'end_amount': Report.format_number(self.end_amount or 0.0, None),
|
||||||
|
'symbol': getattr(self.currency, 'symbol', '-'),
|
||||||
|
'num': len(self.lines),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def default_start_amount(cls):
|
||||||
|
return Decimal('0.0')
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def default_end_amount(cls):
|
||||||
|
return Decimal('0.0')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def default_state(cls):
|
def default_state(cls):
|
||||||
return 'edit'
|
return 'edit'
|
||||||
|
@ -141,6 +287,22 @@ class Reconciliation(Workflow, ModelSQL, ModelView):
|
||||||
IrDate = Pool().get('ir.date')
|
IrDate = Pool().get('ir.date')
|
||||||
return IrDate.today()
|
return IrDate.today()
|
||||||
|
|
||||||
|
@fields.depends('cashbook', '_parent_cashbook.currency')
|
||||||
|
def on_change_with_currency(self, name=None):
|
||||||
|
""" currency of cashbook
|
||||||
|
"""
|
||||||
|
if self.cashbook:
|
||||||
|
return self.cashbook.currency.id
|
||||||
|
|
||||||
|
@fields.depends('cashbook', '_parent_cashbook.currency')
|
||||||
|
def on_change_with_currency_digits(self, name=None):
|
||||||
|
""" currency of cashbook
|
||||||
|
"""
|
||||||
|
if self.cashbook:
|
||||||
|
return self.cashbook.currency.digits
|
||||||
|
else:
|
||||||
|
return 2
|
||||||
|
|
||||||
@fields.depends('cashbook', '_parent_cashbook.state')
|
@fields.depends('cashbook', '_parent_cashbook.state')
|
||||||
def on_change_with_state_cashbook(self, name=None):
|
def on_change_with_state_cashbook(self, name=None):
|
||||||
""" get state of cashbook
|
""" get state of cashbook
|
||||||
|
@ -148,4 +310,63 @@ class Reconciliation(Workflow, ModelSQL, ModelView):
|
||||||
if self.cashbook:
|
if self.cashbook:
|
||||||
return self.cashbook.state
|
return self.cashbook.state
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create(cls, vlist):
|
||||||
|
""" add debit/credit
|
||||||
|
"""
|
||||||
|
for values in vlist:
|
||||||
|
cls.check_overlap_dates(
|
||||||
|
values.get('date_from', None),
|
||||||
|
values.get('date_to', None),
|
||||||
|
values.get('cashbook', None))
|
||||||
|
return super(Reconciliation, cls).create(vlist)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def write(cls, *args):
|
||||||
|
""" deny update if cashbook.line!='open',
|
||||||
|
add or update debit/credit
|
||||||
|
"""
|
||||||
|
actions = iter(args)
|
||||||
|
for reconciliations, values in zip(actions, actions):
|
||||||
|
# deny write if chashbook is not open
|
||||||
|
for reconciliation in reconciliations:
|
||||||
|
|
||||||
|
# deny overlap
|
||||||
|
if len(set({'date_from', 'date_to'}).intersection(set(values.keys()))) > 0:
|
||||||
|
cls.check_overlap_dates(
|
||||||
|
values.get('date_from', reconciliation.date_from),
|
||||||
|
values.get('date_to', reconciliation.date_to),
|
||||||
|
reconciliation.cashbook.id,
|
||||||
|
reconciliation)
|
||||||
|
|
||||||
|
if reconciliation.cashbook.state != 'open':
|
||||||
|
raise UserError(gettext(
|
||||||
|
'cashbook.msg_book_deny_write',
|
||||||
|
bookname = reconciliation.cashbook.rec_name,
|
||||||
|
state_txt = reconciliation.cashbook.state_string,
|
||||||
|
))
|
||||||
|
|
||||||
|
super(Reconciliation, cls).write(*args)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def delete(cls, reconciliations):
|
||||||
|
""" deny delete if book is not 'open' or wf is not 'edit'
|
||||||
|
"""
|
||||||
|
for reconciliation in reconciliations:
|
||||||
|
if reconciliation.cashbook.state == 'closed':
|
||||||
|
raise UserError(gettext(
|
||||||
|
'cashbook.msg_line_deny_delete1',
|
||||||
|
linetxt = reconciliation.rec_name,
|
||||||
|
bookname = reconciliation.cashbook.rec_name,
|
||||||
|
bookstate = reconciliation.cashbook.state_string,
|
||||||
|
))
|
||||||
|
if reconciliation.state != 'edit':
|
||||||
|
raise UserError(gettext(
|
||||||
|
'cashbook.msg_recon_deny_delete2',
|
||||||
|
recontxt = reconciliation.rec_name,
|
||||||
|
reconstate = reconciliation.state_string,
|
||||||
|
))
|
||||||
|
|
||||||
|
return super(Reconciliation, cls).delete(reconciliations)
|
||||||
|
|
||||||
# end Type
|
# end Type
|
||||||
|
|
|
@ -9,11 +9,13 @@ from trytond.modules.cashbook.tests.test_book import BookTestCase
|
||||||
from trytond.modules.cashbook.tests.test_line import LineTestCase
|
from trytond.modules.cashbook.tests.test_line import LineTestCase
|
||||||
from trytond.modules.cashbook.tests.test_config import ConfigTestCase
|
from trytond.modules.cashbook.tests.test_config import ConfigTestCase
|
||||||
from trytond.modules.cashbook.tests.test_category import CategoryTestCase
|
from trytond.modules.cashbook.tests.test_category import CategoryTestCase
|
||||||
|
from trytond.modules.cashbook.tests.test_reconciliation import ReconTestCase
|
||||||
|
|
||||||
__all__ = ['suite']
|
__all__ = ['suite']
|
||||||
|
|
||||||
|
|
||||||
class CashbookTestCase(\
|
class CashbookTestCase(\
|
||||||
|
ReconTestCase,\
|
||||||
CategoryTestCase,\
|
CategoryTestCase,\
|
||||||
ConfigTestCase,\
|
ConfigTestCase,\
|
||||||
LineTestCase,
|
LineTestCase,
|
||||||
|
|
|
@ -51,10 +51,10 @@ class LineTestCase(ModuleTestCase):
|
||||||
self.assertEqual(book.state, 'open')
|
self.assertEqual(book.state, 'open')
|
||||||
self.assertEqual(len(book.lines), 2)
|
self.assertEqual(len(book.lines), 2)
|
||||||
self.assertEqual(book.lines[0].date, date(2022, 5, 1))
|
self.assertEqual(book.lines[0].date, date(2022, 5, 1))
|
||||||
self.assertEqual(book.lines[0].rec_name, '05/01/2022 Text 1')
|
self.assertEqual(book.lines[0].rec_name, '05/01/2022|1.00 usd|Text 1 [Cat1]')
|
||||||
self.assertEqual(book.lines[0].state_cashbook, 'open')
|
self.assertEqual(book.lines[0].state_cashbook, 'open')
|
||||||
self.assertEqual(book.lines[1].date, date(2022, 5, 2))
|
self.assertEqual(book.lines[1].date, date(2022, 5, 2))
|
||||||
self.assertEqual(book.lines[1].rec_name, '05/02/2022 Text 2')
|
self.assertEqual(book.lines[1].rec_name, '05/02/2022|1.00 usd|Text 2 [Cat1]')
|
||||||
|
|
||||||
self.assertEqual(Lines.search_count([('rec_name', '=', 'Text 1')]), 1)
|
self.assertEqual(Lines.search_count([('rec_name', '=', 'Text 1')]), 1)
|
||||||
self.assertEqual(Lines.search_count([('rec_name', '=', 'Text 1a')]), 0)
|
self.assertEqual(Lines.search_count([('rec_name', '=', 'Text 1a')]), 0)
|
||||||
|
@ -67,9 +67,9 @@ class LineTestCase(ModuleTestCase):
|
||||||
|
|
||||||
# sorting: date -> state -> id
|
# sorting: date -> state -> id
|
||||||
self.assertEqual(len(book.lines), 2)
|
self.assertEqual(len(book.lines), 2)
|
||||||
self.assertEqual(book.lines[0].rec_name, '05/01/2022 Text 1')
|
self.assertEqual(book.lines[0].rec_name, '05/01/2022|1.00 usd|Text 1 [Cat1]')
|
||||||
self.assertEqual(book.lines[0].state, 'edit')
|
self.assertEqual(book.lines[0].state, 'edit')
|
||||||
self.assertEqual(book.lines[1].rec_name, '05/02/2022 Text 2')
|
self.assertEqual(book.lines[1].rec_name, '05/02/2022|1.00 usd|Text 2 [Cat1]')
|
||||||
self.assertEqual(book.lines[1].state, 'edit')
|
self.assertEqual(book.lines[1].state, 'edit')
|
||||||
|
|
||||||
# set to same date
|
# set to same date
|
||||||
|
@ -80,17 +80,17 @@ class LineTestCase(ModuleTestCase):
|
||||||
}])
|
}])
|
||||||
# check again
|
# check again
|
||||||
book, = Book.search([])
|
book, = Book.search([])
|
||||||
self.assertEqual(book.lines[0].rec_name, '05/01/2022 Text 1')
|
self.assertEqual(book.lines[0].rec_name, '05/01/2022|1.00 usd|Text 1 [Cat1]')
|
||||||
self.assertEqual(book.lines[0].state, 'edit')
|
self.assertEqual(book.lines[0].state, 'edit')
|
||||||
self.assertEqual(book.lines[1].rec_name, '05/01/2022 Text 2')
|
self.assertEqual(book.lines[1].rec_name, '05/01/2022|1.00 usd|Text 2 [Cat1]')
|
||||||
self.assertEqual(book.lines[1].state, 'edit')
|
self.assertEqual(book.lines[1].state, 'edit')
|
||||||
|
|
||||||
# set to 'check', will sort first
|
# set to 'check', will sort first
|
||||||
Lines.wfcheck([book.lines[1]])
|
Lines.wfcheck([book.lines[1]])
|
||||||
book, = Book.search([])
|
book, = Book.search([])
|
||||||
self.assertEqual(book.lines[0].rec_name, '05/01/2022 Text 2')
|
self.assertEqual(book.lines[0].rec_name, '05/01/2022|1.00 usd|Text 2 [Cat1]')
|
||||||
self.assertEqual(book.lines[0].state, 'check')
|
self.assertEqual(book.lines[0].state, 'check')
|
||||||
self.assertEqual(book.lines[1].rec_name, '05/01/2022 Text 1')
|
self.assertEqual(book.lines[1].rec_name, '05/01/2022|1.00 usd|Text 1 [Cat1]')
|
||||||
self.assertEqual(book.lines[1].state, 'edit')
|
self.assertEqual(book.lines[1].state, 'edit')
|
||||||
|
|
||||||
@with_transaction()
|
@with_transaction()
|
||||||
|
@ -127,6 +127,24 @@ class LineTestCase(ModuleTestCase):
|
||||||
self.assertEqual(book.state, 'open')
|
self.assertEqual(book.state, 'open')
|
||||||
self.assertEqual(len(book.lines), 2)
|
self.assertEqual(len(book.lines), 2)
|
||||||
|
|
||||||
|
Line.write(*[
|
||||||
|
[book.lines[0]],
|
||||||
|
{
|
||||||
|
'description': 'works',
|
||||||
|
}])
|
||||||
|
Line.wfcheck([book.lines[0]])
|
||||||
|
self.assertEqual(book.lines[0].state, 'check')
|
||||||
|
|
||||||
|
self.assertRaisesRegex(UserError,
|
||||||
|
"The cashbook line '05/01/2022|1.00 usd|works [Cat1]' is 'Checked' and cannot be changed.",
|
||||||
|
Line.write,
|
||||||
|
*[
|
||||||
|
[book.lines[0]],
|
||||||
|
{
|
||||||
|
'description': 'denied by line.state',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
Book.wfclosed([book])
|
Book.wfclosed([book])
|
||||||
self.assertEqual(book.state, 'closed')
|
self.assertEqual(book.state, 'closed')
|
||||||
|
|
||||||
|
@ -521,7 +539,7 @@ class LineTestCase(ModuleTestCase):
|
||||||
self.assertEqual(book.lines[0].state, 'check')
|
self.assertEqual(book.lines[0].state, 'check')
|
||||||
|
|
||||||
self.assertRaisesRegex(UserError,
|
self.assertRaisesRegex(UserError,
|
||||||
"The cashbook line '05/01/2022 Text 1' cannot be deleted, its in state 'Checked'.",
|
"The cashbook line '05/01/2022|1.00 usd|Test 1 [Cat1]' cannot be deleted, its in state 'Checked'.",
|
||||||
Lines.delete,
|
Lines.delete,
|
||||||
[book.lines[0]])
|
[book.lines[0]])
|
||||||
|
|
||||||
|
@ -586,14 +604,14 @@ class LineTestCase(ModuleTestCase):
|
||||||
lines = Line.search([])
|
lines = Line.search([])
|
||||||
self.assertEqual(len(lines), 1)
|
self.assertEqual(len(lines), 1)
|
||||||
self.assertEqual(lines[0].cashbook.rec_name, 'Fridas book | 1.00 usd | Open')
|
self.assertEqual(lines[0].cashbook.rec_name, 'Fridas book | 1.00 usd | Open')
|
||||||
self.assertEqual(lines[0].rec_name, '05/01/2022 Test 1')
|
self.assertEqual(lines[0].rec_name, '05/01/2022|1.00 usd|Test 1 [Cat1]')
|
||||||
|
|
||||||
Line.write(*[
|
Line.write(*[
|
||||||
lines,
|
lines,
|
||||||
{
|
{
|
||||||
'description': 'Test 2',
|
'description': 'Test 2',
|
||||||
}])
|
}])
|
||||||
self.assertEqual(lines[0].rec_name, '05/01/2022 Test 2')
|
self.assertEqual(lines[0].rec_name, '05/01/2022|1.00 usd|Test 2 [Cat1]')
|
||||||
|
|
||||||
@with_transaction()
|
@with_transaction()
|
||||||
def test_line_permission_reviewer(self):
|
def test_line_permission_reviewer(self):
|
||||||
|
@ -659,25 +677,25 @@ class LineTestCase(ModuleTestCase):
|
||||||
self.assertEqual(len(lines), 1)
|
self.assertEqual(len(lines), 1)
|
||||||
self.assertEqual(len(lines[0].cashbook.reviewer.users), 1)
|
self.assertEqual(len(lines[0].cashbook.reviewer.users), 1)
|
||||||
self.assertEqual(lines[0].cashbook.reviewer.users[0].rec_name, 'Diego')
|
self.assertEqual(lines[0].cashbook.reviewer.users[0].rec_name, 'Diego')
|
||||||
self.assertEqual(lines[0].rec_name, '05/01/2022 Test 1')
|
self.assertEqual(lines[0].rec_name, '05/01/2022|1.00 usd|Test 1 [Cat1]')
|
||||||
Line.write(*[
|
Line.write(*[
|
||||||
lines,
|
lines,
|
||||||
{
|
{
|
||||||
'description': 'Test 2',
|
'description': 'Test 2',
|
||||||
}])
|
}])
|
||||||
self.assertEqual(lines[0].rec_name, '05/01/2022 Test 2')
|
self.assertEqual(lines[0].rec_name, '05/01/2022|1.00 usd|Test 2 [Cat1]')
|
||||||
|
|
||||||
# change to user 'frida' read/write line
|
# change to user 'frida' read/write line
|
||||||
with Transaction().set_user(usr_lst[0].id):
|
with Transaction().set_user(usr_lst[0].id):
|
||||||
lines = Line.search([])
|
lines = Line.search([])
|
||||||
self.assertEqual(len(lines), 1)
|
self.assertEqual(len(lines), 1)
|
||||||
self.assertEqual(lines[0].rec_name, '05/01/2022 Test 2')
|
self.assertEqual(lines[0].rec_name, '05/01/2022|1.00 usd|Test 2 [Cat1]')
|
||||||
Line.write(*[
|
Line.write(*[
|
||||||
lines,
|
lines,
|
||||||
{
|
{
|
||||||
'description': 'Test 3',
|
'description': 'Test 3',
|
||||||
}])
|
}])
|
||||||
self.assertEqual(lines[0].rec_name, '05/01/2022 Test 3')
|
self.assertEqual(lines[0].rec_name, '05/01/2022|1.00 usd|Test 3 [Cat1]')
|
||||||
|
|
||||||
@with_transaction()
|
@with_transaction()
|
||||||
def test_line_permission_observer(self):
|
def test_line_permission_observer(self):
|
||||||
|
@ -743,7 +761,7 @@ class LineTestCase(ModuleTestCase):
|
||||||
self.assertEqual(len(lines), 1)
|
self.assertEqual(len(lines), 1)
|
||||||
self.assertEqual(len(lines[0].cashbook.observer.users), 1)
|
self.assertEqual(len(lines[0].cashbook.observer.users), 1)
|
||||||
self.assertEqual(lines[0].cashbook.observer.users[0].rec_name, 'Diego')
|
self.assertEqual(lines[0].cashbook.observer.users[0].rec_name, 'Diego')
|
||||||
self.assertEqual(lines[0].rec_name, '05/01/2022 Test 1')
|
self.assertEqual(lines[0].rec_name, '05/01/2022|1.00 usd|Test 1 [Cat1]')
|
||||||
|
|
||||||
self.assertRaisesRegex(UserError,
|
self.assertRaisesRegex(UserError,
|
||||||
'You are not allowed to write to records "[0-9]{1,}" of "Cashbook Line" because of at least one of these rules:\nOwners and reviewers: Cashbook line write - ',
|
'You are not allowed to write to records "[0-9]{1,}" of "Cashbook Line" because of at least one of these rules:\nOwners and reviewers: Cashbook line write - ',
|
||||||
|
@ -759,12 +777,12 @@ class LineTestCase(ModuleTestCase):
|
||||||
with Transaction().set_user(usr_lst[0].id):
|
with Transaction().set_user(usr_lst[0].id):
|
||||||
lines = Line.search([])
|
lines = Line.search([])
|
||||||
self.assertEqual(len(lines), 1)
|
self.assertEqual(len(lines), 1)
|
||||||
self.assertEqual(lines[0].rec_name, '05/01/2022 Test 1')
|
self.assertEqual(lines[0].rec_name, '05/01/2022|1.00 usd|Test 1 [Cat1]')
|
||||||
Line.write(*[
|
Line.write(*[
|
||||||
lines,
|
lines,
|
||||||
{
|
{
|
||||||
'description': 'Test 2',
|
'description': 'Test 2',
|
||||||
}])
|
}])
|
||||||
self.assertEqual(lines[0].rec_name, '05/01/2022 Test 2')
|
self.assertEqual(lines[0].rec_name, '05/01/2022|1.00 usd|Test 2 [Cat1]')
|
||||||
|
|
||||||
# end LineTestCase
|
# end LineTestCase
|
||||||
|
|
365
tests/test_reconciliation.py
Normal file
365
tests/test_reconciliation.py
Normal file
|
@ -0,0 +1,365 @@
|
||||||
|
# -*- 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.exceptions import UserError
|
||||||
|
from datetime import date
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
|
|
||||||
|
class ReconTestCase(ModuleTestCase):
|
||||||
|
'Test cashbook reconciliation module'
|
||||||
|
module = 'cashbook'
|
||||||
|
|
||||||
|
@with_transaction()
|
||||||
|
def test_recon_check_overlap(self):
|
||||||
|
""" create, check deny of overlap date
|
||||||
|
"""
|
||||||
|
pool = Pool()
|
||||||
|
Book = pool.get('cashbook.book')
|
||||||
|
Reconciliation = pool.get('cashbook.recon')
|
||||||
|
|
||||||
|
types = self.prep_type()
|
||||||
|
category = self.prep_category(cattype='in')
|
||||||
|
company = self.prep_company()
|
||||||
|
book, = Book.create([{
|
||||||
|
'name': 'Book 1',
|
||||||
|
'btype': types.id,
|
||||||
|
'company': company.id,
|
||||||
|
'currency': company.currency.id,
|
||||||
|
}])
|
||||||
|
|
||||||
|
recon1, = Reconciliation.create([{
|
||||||
|
'date': date(2022, 5, 1),
|
||||||
|
'date_from': date(2022, 5, 1),
|
||||||
|
'date_to': date(2022, 5, 31),
|
||||||
|
'cashbook': book.id,
|
||||||
|
}])
|
||||||
|
self.assertEqual(recon1.rec_name, '05/01/2022 - 05/31/2022 | 0.00 usd - 0.00 usd [0]')
|
||||||
|
|
||||||
|
recon2, = Reconciliation.create([{
|
||||||
|
'date': date(2022, 6, 1),
|
||||||
|
'date_from': date(2022, 6, 1),
|
||||||
|
'date_to': date(2022, 6, 30),
|
||||||
|
'cashbook': book.id,
|
||||||
|
}])
|
||||||
|
self.assertEqual(recon2.rec_name, '06/01/2022 - 06/30/2022 | 0.00 usd - 0.00 usd [0]')
|
||||||
|
|
||||||
|
# same day for end of '0' and start of '1'
|
||||||
|
Reconciliation.write(*[
|
||||||
|
[recon2],
|
||||||
|
{
|
||||||
|
'date_from': date(2022, 5, 31),
|
||||||
|
},
|
||||||
|
])
|
||||||
|
self.assertEqual(recon1.rec_name, '05/01/2022 - 05/31/2022 | 0.00 usd - 0.00 usd [0]')
|
||||||
|
self.assertEqual(recon2.rec_name, '05/31/2022 - 06/30/2022 | 0.00 usd - 0.00 usd [0]')
|
||||||
|
|
||||||
|
# 'date_from' inside of other record
|
||||||
|
self.assertRaisesRegex(UserError,
|
||||||
|
'The date range overlaps with another reconciliation.',
|
||||||
|
Reconciliation.write,
|
||||||
|
*[
|
||||||
|
[recon2],
|
||||||
|
{
|
||||||
|
'date_from': date(2022, 5, 30),
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
# 'date_from' inside of other record
|
||||||
|
self.assertRaisesRegex(UserError,
|
||||||
|
'The date range overlaps with another reconciliation.',
|
||||||
|
Reconciliation.write,
|
||||||
|
*[
|
||||||
|
[recon2],
|
||||||
|
{
|
||||||
|
'date_from': date(2022, 5, 2),
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
# 'date_from' same date_from like other record
|
||||||
|
self.assertRaisesRegex(UserError,
|
||||||
|
'The date range overlaps with another reconciliation.',
|
||||||
|
Reconciliation.write,
|
||||||
|
*[
|
||||||
|
[recon2],
|
||||||
|
{
|
||||||
|
'date_from': date(2022, 5, 1),
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
# enclose other record
|
||||||
|
self.assertRaisesRegex(UserError,
|
||||||
|
'The date range overlaps with another reconciliation.',
|
||||||
|
Reconciliation.write,
|
||||||
|
*[
|
||||||
|
[recon2],
|
||||||
|
{
|
||||||
|
'date_from': date(2022, 4, 30),
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
# within other record
|
||||||
|
self.assertRaisesRegex(UserError,
|
||||||
|
'The date range overlaps with another reconciliation.',
|
||||||
|
Reconciliation.write,
|
||||||
|
*[
|
||||||
|
[recon2],
|
||||||
|
{
|
||||||
|
'date_from': date(2022, 5, 2),
|
||||||
|
'date_to': date(2022, 5, 12),
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
# from after to
|
||||||
|
self.assertRaisesRegex(UserError,
|
||||||
|
'The value for field "Start Date" in "Cashbook Reconciliation" is not valid according to its domain.',
|
||||||
|
Reconciliation.write,
|
||||||
|
*[
|
||||||
|
[recon2],
|
||||||
|
{
|
||||||
|
'date_from': date(2022, 4, 15),
|
||||||
|
'date_to': date(2022, 4, 10),
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
# 'date_to' at 'date_from' of other record - allowed
|
||||||
|
Reconciliation.write(*[
|
||||||
|
[recon2],
|
||||||
|
{
|
||||||
|
'date_from': date(2022, 4, 15),
|
||||||
|
'date_to': date(2022, 5, 1),
|
||||||
|
}])
|
||||||
|
|
||||||
|
# 'date_to' after other date_from
|
||||||
|
self.assertRaisesRegex(UserError,
|
||||||
|
'The date range overlaps with another reconciliation.',
|
||||||
|
Reconciliation.write,
|
||||||
|
*[
|
||||||
|
[recon2],
|
||||||
|
{
|
||||||
|
'date_to': date(2022, 5, 2),
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
# overlap at create
|
||||||
|
self.assertRaisesRegex(UserError,
|
||||||
|
'The date range overlaps with another reconciliation.',
|
||||||
|
Reconciliation.create,
|
||||||
|
[{
|
||||||
|
'date': date(2022, 5, 1),
|
||||||
|
'date_from': date(2022, 4, 1),
|
||||||
|
'date_to': date(2022, 4, 16),
|
||||||
|
'cashbook': book.id,
|
||||||
|
}])
|
||||||
|
|
||||||
|
recon3, = Reconciliation.create([{
|
||||||
|
'date': date(2022, 4, 17),
|
||||||
|
'date_from': date(2022, 4, 1),
|
||||||
|
'date_to': date(2022, 4, 15),
|
||||||
|
'cashbook': book.id,
|
||||||
|
}])
|
||||||
|
self.assertEqual(recon1.rec_name, '05/01/2022 - 05/31/2022 | 0.00 usd - 0.00 usd [0]')
|
||||||
|
self.assertEqual(recon2.rec_name, '04/15/2022 - 05/01/2022 | 0.00 usd - 0.00 usd [0]')
|
||||||
|
self.assertEqual(recon3.rec_name, '04/01/2022 - 04/15/2022 | 0.00 usd - 0.00 usd [0]')
|
||||||
|
|
||||||
|
@with_transaction()
|
||||||
|
def test_recon_create_check_line_add_to_recon(self):
|
||||||
|
""" create, booklines, add reconciliation
|
||||||
|
"""
|
||||||
|
pool = Pool()
|
||||||
|
Book = pool.get('cashbook.book')
|
||||||
|
Lines = pool.get('cashbook.line')
|
||||||
|
Reconciliation = pool.get('cashbook.recon')
|
||||||
|
|
||||||
|
types = self.prep_type()
|
||||||
|
category = self.prep_category(cattype='in')
|
||||||
|
company = self.prep_company()
|
||||||
|
book, = Book.create([{
|
||||||
|
'name': 'Book 1',
|
||||||
|
'btype': types.id,
|
||||||
|
'company': company.id,
|
||||||
|
'currency': company.currency.id,
|
||||||
|
'lines': [('create', [{
|
||||||
|
'date': date(2022, 5, 1),
|
||||||
|
'description': 'Text 1',
|
||||||
|
'category': category.id,
|
||||||
|
'bookingtype': 'in',
|
||||||
|
'amount': Decimal('1.0'),
|
||||||
|
}, {
|
||||||
|
'date': date(2022, 5, 5),
|
||||||
|
'description': 'Text 2',
|
||||||
|
'category': category.id,
|
||||||
|
'bookingtype': 'in',
|
||||||
|
'amount': Decimal('1.0'),
|
||||||
|
}])],
|
||||||
|
'reconciliations': [('create', [{
|
||||||
|
'date': date(2022, 5, 28),
|
||||||
|
'date_from': date(2022, 5, 1),
|
||||||
|
'date_to': date(2022, 5, 31),
|
||||||
|
}])],
|
||||||
|
}])
|
||||||
|
self.assertEqual(book.name, 'Book 1')
|
||||||
|
self.assertEqual(book.state, 'open')
|
||||||
|
self.assertEqual(len(book.lines), 2)
|
||||||
|
self.assertEqual(book.lines[0].rec_name, '05/01/2022|1.00 usd|Text 1 [Cat1]')
|
||||||
|
self.assertEqual(book.lines[1].rec_name, '05/05/2022|1.00 usd|Text 2 [Cat1]')
|
||||||
|
self.assertEqual(len(book.reconciliations), 1)
|
||||||
|
self.assertEqual(book.reconciliations[0].rec_name, '05/01/2022 - 05/31/2022 | 0.00 usd - 0.00 usd [0]')
|
||||||
|
self.assertEqual(len(book.reconciliations[0].lines), 0)
|
||||||
|
|
||||||
|
self.assertRaisesRegex(UserError,
|
||||||
|
"For reconciliation, the line '05/01/2022|1.00 usd|Text 1 [Cat1]' must be in the status 'Check' or 'Done'.",
|
||||||
|
Lines.write,
|
||||||
|
*[
|
||||||
|
[book.lines[0]],
|
||||||
|
{
|
||||||
|
'reconciliation': book.reconciliations[0].id,
|
||||||
|
}
|
||||||
|
])
|
||||||
|
Lines.wfcheck(book.lines)
|
||||||
|
|
||||||
|
Lines.write(*[
|
||||||
|
list(book.lines),
|
||||||
|
{
|
||||||
|
'reconciliation': book.reconciliations[0].id,
|
||||||
|
}])
|
||||||
|
self.assertEqual(len(book.reconciliations[0].lines), 2)
|
||||||
|
|
||||||
|
self.assertRaisesRegex(UserError,
|
||||||
|
"The status cannot be changed to 'Edit' as long as the line '05/01/2022|1.00 usd|Text 1 [Cat1]' is associated with a reconciliation.",
|
||||||
|
Lines.wfedit,
|
||||||
|
[book.lines[0]])
|
||||||
|
|
||||||
|
@with_transaction()
|
||||||
|
def test_recon_check_deny_delete(self):
|
||||||
|
""" create, booklines, reconciliation, try delete
|
||||||
|
"""
|
||||||
|
pool = Pool()
|
||||||
|
Book = pool.get('cashbook.book')
|
||||||
|
Lines = pool.get('cashbook.line')
|
||||||
|
Reconciliation = pool.get('cashbook.recon')
|
||||||
|
|
||||||
|
types = self.prep_type()
|
||||||
|
category = self.prep_category(cattype='in')
|
||||||
|
company = self.prep_company()
|
||||||
|
book, = Book.create([{
|
||||||
|
'name': 'Book 1',
|
||||||
|
'btype': types.id,
|
||||||
|
'company': company.id,
|
||||||
|
'currency': company.currency.id,
|
||||||
|
'lines': [('create', [{
|
||||||
|
'date': date(2022, 5, 1),
|
||||||
|
'description': 'Text 1',
|
||||||
|
'category': category.id,
|
||||||
|
'bookingtype': 'in',
|
||||||
|
'amount': Decimal('1.0'),
|
||||||
|
}])],
|
||||||
|
'reconciliations': [('create', [{
|
||||||
|
'date': date(2022, 5, 28),
|
||||||
|
'date_from': date(2022, 5, 1),
|
||||||
|
'date_to': date(2022, 5, 31),
|
||||||
|
}])],
|
||||||
|
}])
|
||||||
|
self.assertEqual(book.name, 'Book 1')
|
||||||
|
self.assertEqual(book.state, 'open')
|
||||||
|
|
||||||
|
Lines.wfcheck(list(book.lines))
|
||||||
|
Reconciliation.wfcheck(list(book.reconciliations))
|
||||||
|
self.assertEqual(len(book.reconciliations), 1)
|
||||||
|
self.assertEqual(len(book.reconciliations[0].lines), 1)
|
||||||
|
|
||||||
|
self.assertRaisesRegex(UserError,
|
||||||
|
"The reconciliation '05/01/2022 - 05/31/2022 | 0.00 - 0.00 usd [0]' cannot be deleted, its in state 'Check'.",
|
||||||
|
Reconciliation.delete,
|
||||||
|
list(book.reconciliations))
|
||||||
|
|
||||||
|
Book.wfclosed([book])
|
||||||
|
|
||||||
|
self.assertRaisesRegex(UserError,
|
||||||
|
"The cashbook line '05/01/2022 - 05/31/2022: 0.00 usd' cannot be deleted because the Cashbook 'Book 1 | 1.00 usd | Closed' is in state 'Closed'.",
|
||||||
|
Reconciliation.delete,
|
||||||
|
list(book.reconciliations))
|
||||||
|
|
||||||
|
self.assertRaisesRegex(UserError,
|
||||||
|
"The cash book 'Book 1 | 1.00 usd | Closed' is 'Closed' and cannot be changed.",
|
||||||
|
Reconciliation.write,
|
||||||
|
*[
|
||||||
|
list(book.reconciliations),
|
||||||
|
{
|
||||||
|
'date': date(2022, 5, 29),
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
@with_transaction()
|
||||||
|
def test_recon_check_wf_edit_to_check(self):
|
||||||
|
""" create, booklines, add reconciliation
|
||||||
|
"""
|
||||||
|
pool = Pool()
|
||||||
|
Book = pool.get('cashbook.book')
|
||||||
|
Lines = pool.get('cashbook.line')
|
||||||
|
Reconciliation = pool.get('cashbook.recon')
|
||||||
|
|
||||||
|
types = self.prep_type()
|
||||||
|
category = self.prep_category(cattype='in')
|
||||||
|
company = self.prep_company()
|
||||||
|
book, = Book.create([{
|
||||||
|
'name': 'Book 1',
|
||||||
|
'btype': types.id,
|
||||||
|
'company': company.id,
|
||||||
|
'currency': company.currency.id,
|
||||||
|
'lines': [('create', [{
|
||||||
|
'date': date(2022, 5, 1),
|
||||||
|
'description': 'Text 1',
|
||||||
|
'category': category.id,
|
||||||
|
'bookingtype': 'in',
|
||||||
|
'amount': Decimal('1.0'),
|
||||||
|
}, {
|
||||||
|
'date': date(2022, 5, 5),
|
||||||
|
'description': 'Text 2',
|
||||||
|
'category': category.id,
|
||||||
|
'bookingtype': 'in',
|
||||||
|
'amount': Decimal('1.0'),
|
||||||
|
}])],
|
||||||
|
'reconciliations': [('create', [{
|
||||||
|
'date': date(2022, 5, 28),
|
||||||
|
'date_from': date(2022, 5, 1),
|
||||||
|
'date_to': date(2022, 5, 31),
|
||||||
|
}])],
|
||||||
|
}])
|
||||||
|
self.assertEqual(book.name, 'Book 1')
|
||||||
|
self.assertEqual(book.state, 'open')
|
||||||
|
self.assertEqual(len(book.lines), 2)
|
||||||
|
self.assertEqual(book.lines[0].rec_name, '05/01/2022|1.00 usd|Text 1 [Cat1]')
|
||||||
|
self.assertEqual(book.lines[1].rec_name, '05/05/2022|1.00 usd|Text 2 [Cat1]')
|
||||||
|
self.assertEqual(book.lines[0].reconciliation, None)
|
||||||
|
self.assertEqual(book.lines[1].reconciliation, None)
|
||||||
|
self.assertEqual(len(book.reconciliations), 1)
|
||||||
|
self.assertEqual(book.reconciliations[0].rec_name, '05/01/2022 - 05/31/2022 | 0.00 usd - 0.00 usd [0]')
|
||||||
|
self.assertEqual(len(book.reconciliations[0].lines), 0)
|
||||||
|
|
||||||
|
# run wf, fail with lines not 'checked'
|
||||||
|
self.assertRaisesRegex(UserError,
|
||||||
|
"For the reconciliation '05/01/2022 - 05/31/2022 | 0.00 usd - 0.00 usd [0]' of the cashbook 'Book 1 | 2.00 usd | Open', all lines in the date range from '05/01/2022' to '05/31/2022' must be in the 'Check' state.",
|
||||||
|
Reconciliation.wfcheck,
|
||||||
|
list(book.reconciliations),
|
||||||
|
)
|
||||||
|
|
||||||
|
# edit --> check
|
||||||
|
Lines.wfcheck(book.lines)
|
||||||
|
Reconciliation.wfcheck(list(book.reconciliations))
|
||||||
|
self.assertEqual(len(book.reconciliations[0].lines), 2)
|
||||||
|
self.assertEqual(book.lines[0].reconciliation.rec_name, '05/01/2022 - 05/31/2022 | 0.00 usd - 0.00 usd [2]')
|
||||||
|
self.assertEqual(book.lines[1].reconciliation.rec_name, '05/01/2022 - 05/31/2022 | 0.00 usd - 0.00 usd [2]')
|
||||||
|
|
||||||
|
# check --> edit
|
||||||
|
Reconciliation.wfedit(list(book.reconciliations))
|
||||||
|
self.assertEqual(len(book.reconciliations[0].lines), 0)
|
||||||
|
self.assertEqual(book.lines[0].reconciliation, None)
|
||||||
|
self.assertEqual(book.lines[1].reconciliation, None)
|
||||||
|
|
||||||
|
# end ReconTestCase
|
|
@ -22,7 +22,11 @@ full copyright notices and license terms. -->
|
||||||
<button name="wfdone"/>
|
<button name="wfdone"/>
|
||||||
</group>
|
</group>
|
||||||
|
|
||||||
<label id="lab1" string=" " colspan="4"/>
|
<label name="start_amount"/>
|
||||||
|
<field name="start_amount"/>
|
||||||
|
<label name="end_amount"/>
|
||||||
|
<field name="end_amount"/>
|
||||||
|
|
||||||
<label name="date"/>
|
<label name="date"/>
|
||||||
<field name="date"/>
|
<field name="date"/>
|
||||||
<newline/>
|
<newline/>
|
||||||
|
|
|
@ -7,6 +7,8 @@ full copyright notices and license terms. -->
|
||||||
<field name="date"/>
|
<field name="date"/>
|
||||||
<field name="date_from"/>
|
<field name="date_from"/>
|
||||||
<field name="date_to"/>
|
<field name="date_to"/>
|
||||||
|
<field name="start_amount"/>
|
||||||
|
<field name="end_amount"/>
|
||||||
<field name="lines"/>
|
<field name="lines"/>
|
||||||
<field name="state"/>
|
<field name="state"/>
|
||||||
</tree>
|
</tree>
|
||||||
|
|
Loading…
Reference in a new issue