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:
Frederik Jaeckel 2022-08-12 16:43:49 +02:00
parent 01825cc09c
commit 149baef174
10 changed files with 1099 additions and 64 deletions

65
line.py
View file

@ -78,7 +78,9 @@ class Line(Workflow, ModelSQL, ModelView):
credit = fields.Numeric(string='Credit', digits=(16, Eval('currency_digits', 2)),
required=True, readonly=True, depends=['currency_digits'])
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',
digits=(16, Eval('currency_digits', 2)),
@ -184,9 +186,12 @@ class Line(Workflow, ModelSQL, ModelView):
def get_rec_name(self, 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),
'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
@ -360,8 +365,23 @@ class Line(Workflow, ModelSQL, ModelView):
""" add debit/credit
"""
vlist = [x.copy() for x in vlist]
for vals in vlist:
vals.update(cls.get_debit_credit(vals))
for values in vlist:
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)
@classmethod
@ -373,14 +393,49 @@ class Line(Workflow, ModelSQL, ModelView):
to_write = []
for lines, values in zip(actions, actions):
for line in lines:
# deny write if chashbook is not open
if line.cashbook.state != 'open':
raise UserError(gettext(
'cashbook.msg_book_deny_write',
bookname = line.cashbook.rec_name,
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:
for line in lines:
values2 = {}

View file

@ -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'."
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"
msgid "Settings for this user alredy exists."
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."
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 #
@ -801,3 +833,11 @@ msgstr "Geschlossen"
msgctxt "selection:cashbook.recon,state_cashbook:"
msgid "Archive"
msgstr "Archiv"
msgctxt "field:cashbook.recon,start_amount:"
msgid "Start Amount"
msgstr "Anfangsbetrag"
msgctxt "field:cashbook.recon,end_amount:"
msgid "End Amount"
msgstr "Endbetrag"

View file

@ -23,8 +23,8 @@ msgid "The cashbook '%(bookname)s' cannot be deleted because it contains %(bookl
msgstr "The cashbook '%(bookname)s' cannot be deleted because it contains %(booklines)s lines and is not in the status 'Archive'."
msgctxt "model:ir.message,text:msg_book_deny_write"
msgid "The cash book '%(bookname)s' is '%(state_txt)s' and cannot be changed."
msgstr "The cash book '%(bookname)s' is '%(state_txt)s' and cannot be changed."
msgid "The cashbook '%(bookname)s' is '%(state_txt)s' and cannot be changed."
msgstr "The cashbook '%(bookname)s' is '%(state_txt)s' and cannot be changed."
msgctxt "model:ir.message,text:msg_line_deny_delete1"
msgid "The cashbook line '%(linetxt)s' cannot be deleted because the Cashbook '%(bookname)s' is in state '%(bookstate)s'."
@ -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'."
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"
msgid "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."
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"
msgid "Cashbook"
msgstr "Cashbook"
@ -74,6 +110,30 @@ msgctxt "model:ir.rule.group,name:rg_line_read"
msgid "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"
msgid "Cashbook"
msgstr "Cashbook"
@ -86,7 +146,7 @@ msgctxt "model:ir.ui.menu,name:menu_typeconfig"
msgid "Cashbook Type"
msgstr "Cashbook Type"
msgctxt "model:ir.ui.menu,name:menu_bookconfig"
msgctxt "model:ir.ui.menu,name:menu_booklist"
msgid "Cashbook"
msgstr "Cashbook"
@ -103,8 +163,8 @@ msgid "Category"
msgstr "Category"
msgctxt "model:ir.action,name:act_book_view"
msgid "Account"
msgstr "Account"
msgid "Cashbook"
msgstr "Cashbook"
msgctxt "model:ir.action,name:act_type_view"
msgid "Cashbook Type"
@ -122,6 +182,18 @@ msgctxt "model:ir.ui.menu,name:act_category_view"
msgid "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"
msgid "Edit"
msgstr "Edit"
@ -146,13 +218,29 @@ msgctxt "model:ir.model.button,string:book_wfarchive_button"
msgid "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:"
msgid "Cashbook"
msgstr "Cashbook"
msgctxt "view:cashbook.book:"
msgid "Owner & Authorizeds"
msgstr "Owner & Authorizeds"
msgid "Owner and Authorizeds"
msgstr "Owner and Authorizeds"
msgctxt "view:cashbook.book:"
msgid "Reconciliations"
msgstr "Reconciliations"
msgctxt "field:cashbook.book,name:"
msgid "Name"
@ -202,14 +290,50 @@ msgctxt "field:cashbook.book,account:"
msgid "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:"
msgid "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:"
msgid "Cashbook Line"
msgstr "Cashbook Line"
msgctxt "view:cashbook.line:"
msgid "Description"
msgstr "Description"
msgctxt "view:cashbook.line:"
msgid "State"
msgstr "State"
@ -246,17 +370,73 @@ msgctxt "field:cashbook.line,month:"
msgid "Month"
msgstr "Month"
msgctxt "model:ir.action.act_window.domain,name:act_line_domain_current"
msgid "Current Month"
msgstr "Current Month"
msgctxt "field:cashbook.line,category:"
msgid "Category"
msgstr "Category"
msgctxt "model:ir.action.act_window.domain,name:act_line_domain_last"
msgid "Last Month"
msgstr "Last Month"
msgctxt "field:cashbook.line,category_view:"
msgid "Category"
msgstr "Category"
msgctxt "model:ir.action.act_window.domain,name:act_line_domain_all"
msgid "All"
msgstr "All"
msgctxt "field:cashbook.line,bookingtype:"
msgid "Type"
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:"
msgid "Cashbook Type"
@ -270,17 +450,9 @@ msgctxt "field:cashbook.type,short:"
msgid "Abbreviation"
msgstr "Abbreviation"
msgctxt "model:cashbook.type,name:atype_cash"
msgid "Cash"
msgstr "Cash"
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 "field:cashbook.type,company:"
msgid "Company"
msgstr "Company"
msgctxt "model:cashbook.category,name:"
msgid "Category"
@ -330,6 +502,22 @@ msgctxt "field:cashbook.category,right:"
msgid "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:"
msgid "Open Cashbook"
msgstr "Open Cashbook"
@ -370,11 +558,11 @@ msgctxt "model:cashbook.open_lines,name:"
msgid "Open Cashbook"
msgstr "Open Cashbook"
msgctxt "wizard_button:cashbook.open_lines,start,end:"
msgctxt "wizard_button:cashbook.open_lines,askuser,end:"
msgid "Cancel"
msgstr "Cancel"
msgctxt "wizard_button:cashbook.open_lines,start,open_:"
msgctxt "wizard_button:cashbook.open_lines,askuser,open_:"
msgid "Open"
msgstr "Open"
@ -418,6 +606,10 @@ msgctxt "view:cashbook.configuration:"
msgid "Open Cashbook Wizard"
msgstr "Open Cashbook Wizard"
msgctxt "view:cashbook.configuration:"
msgid "Cashbook"
msgstr "Cashbook"
msgctxt "field:cashbook.configuration,date_from:"
msgid "Start Date"
msgstr "Start Date"
@ -430,10 +622,34 @@ msgctxt "field:cashbook.configuration,checked:"
msgid "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:"
msgid "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:"
msgid "User Configuration"
msgstr "User Configuration"
@ -450,3 +666,91 @@ msgctxt "field:cashbook.configuration_user,checked:"
msgid "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"

View file

@ -29,6 +29,18 @@ full copyright notices and license terms. -->
<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>
</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">
<field name="text">Settings for this user alredy exists.</field>
</record>
@ -44,6 +56,18 @@ full copyright notices and license terms. -->
<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>
</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>
</tryton>

View file

@ -3,12 +3,16 @@
# The COPYRIGHT file at the top level of this repository contains the
# 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.pyson import Eval, If, Or
from trytond.pool import Pool
from trytond.report import Report
from trytond.exceptions import UserError
from trytond.i18n import gettext
from decimal import Decimal
from sql.operators import Equal, Between
from sql import Literal, Null
from .book import sel_state_book
@ -52,11 +56,19 @@ class Reconciliation(Workflow, ModelSQL, ModelView):
()),
],
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',
model_name='cashbook.line', states=STATES,
depends=DEPENDS+['date_from', 'date_to', 'cashbook'],
add_remove=[
('cashbook', '=', Eval('cashbook')),
('state', 'in', ['check', 'done']),
('date', '>=', Eval('date_from')),
('date', '<=', Eval('date_to')),
],
@ -65,6 +77,11 @@ class Reconciliation(Workflow, ModelSQL, ModelView):
('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,
select=True, selection=sel_reconstate)
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
@ModelView.button
@Workflow.transition('edit')
def wfedit(cls, lines):
def wfedit(cls, reconciliations):
""" 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
@ModelView.button
@Workflow.transition('check')
def wfcheck(cls, lines):
""" is checked
def wfcheck(cls, reconciliations):
""" 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
@ModelView.button
@Workflow.transition('done')
def wfdone(cls, lines):
def wfdone(cls, reconciliations):
""" 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):
""" 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 '-',
'to': Report.format_date(self.date_to, None) if self.date_to is not None else '-',
'amount': Decimal('0.0'),
'symbol': getattr(getattr(self.cashbook, 'currency', None), 'symbol', '-'),
'start_amount': Report.format_number(self.start_amount or 0.0, None),
'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
def default_state(cls):
return 'edit'
@ -141,6 +287,22 @@ class Reconciliation(Workflow, ModelSQL, ModelView):
IrDate = Pool().get('ir.date')
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')
def on_change_with_state_cashbook(self, name=None):
""" get state of cashbook
@ -148,4 +310,63 @@ class Reconciliation(Workflow, ModelSQL, ModelView):
if self.cashbook:
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

View file

@ -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_config import ConfigTestCase
from trytond.modules.cashbook.tests.test_category import CategoryTestCase
from trytond.modules.cashbook.tests.test_reconciliation import ReconTestCase
__all__ = ['suite']
class CashbookTestCase(\
ReconTestCase,\
CategoryTestCase,\
ConfigTestCase,\
LineTestCase,

View file

@ -51,10 +51,10 @@ class LineTestCase(ModuleTestCase):
self.assertEqual(book.state, 'open')
self.assertEqual(len(book.lines), 2)
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[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 1a')]), 0)
@ -67,9 +67,9 @@ class LineTestCase(ModuleTestCase):
# sorting: date -> state -> id
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[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')
# set to same date
@ -80,17 +80,17 @@ class LineTestCase(ModuleTestCase):
}])
# check again
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[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')
# set to 'check', will sort first
Lines.wfcheck([book.lines[1]])
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[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')
@with_transaction()
@ -127,6 +127,24 @@ class LineTestCase(ModuleTestCase):
self.assertEqual(book.state, 'open')
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])
self.assertEqual(book.state, 'closed')
@ -521,7 +539,7 @@ class LineTestCase(ModuleTestCase):
self.assertEqual(book.lines[0].state, 'check')
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,
[book.lines[0]])
@ -586,14 +604,14 @@ class LineTestCase(ModuleTestCase):
lines = Line.search([])
self.assertEqual(len(lines), 1)
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(*[
lines,
{
'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()
def test_line_permission_reviewer(self):
@ -659,25 +677,25 @@ class LineTestCase(ModuleTestCase):
self.assertEqual(len(lines), 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].rec_name, '05/01/2022 Test 1')
self.assertEqual(lines[0].rec_name, '05/01/2022|1.00 usd|Test 1 [Cat1]')
Line.write(*[
lines,
{
'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
with Transaction().set_user(usr_lst[0].id):
lines = Line.search([])
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(*[
lines,
{
'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()
def test_line_permission_observer(self):
@ -743,7 +761,7 @@ class LineTestCase(ModuleTestCase):
self.assertEqual(len(lines), 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].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,
'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):
lines = Line.search([])
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(*[
lines,
{
'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

View 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

View file

@ -22,7 +22,11 @@ full copyright notices and license terms. -->
<button name="wfdone"/>
</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"/>
<field name="date"/>
<newline/>

View file

@ -7,6 +7,8 @@ full copyright notices and license terms. -->
<field name="date"/>
<field name="date_from"/>
<field name="date_to"/>
<field name="start_amount"/>
<field name="end_amount"/>
<field name="lines"/>
<field name="state"/>
</tree>