loss or profit move generation with confirmation wizard

16-gn-cash-refactor
Florian Roger 2 months ago
parent 5170cf70b2
commit fafb9e7d2a

@ -9,18 +9,23 @@ It is based on modules:
## How do we work with it:
1. Each day, users create operations they assume (like receive donation, etc.)
2. If time is short, they can also declare it on a simple solo statement line
3. At the end of the week, Odoo generates a statement with missing lines (i.e. lines with payment suspense accounts)
4. the accountant verifies cashbox balance and validate loss/profit
1. Each day, users create operations like receive donation, pay expenses, etc. The counterpart is their own property_account_payable_id (467 or 425)
2. When the accountant gives/receive money to/from user, the accountant registers the cash-out/deposit to/from user (i.e. user's property_account_payable_id vs cash_account_id)
3. The accountant can then:
1. Generates a cash statement
2. Verify cashbox and generate loss/profit move
## Whats Needs to Be Done:
- add validate cashbox and entry lines creation
- allow user to add statement lines 'on-the-fly'
- allow user to create Donation / Expense / Cash-In / Cash-Out operations
- create an action triggered by a button leading to a wizard that auto-generates cash statement from lines with payment suspense account.
- [x] add validate cashbox and entry lines creation
- [x] loss/profit validation
- [] use predefined cash units
- [] What to do when statement line generated has no label
- [] report cashbox to next statement
- [] compute cashbox from cash entries
- [] forbid statement unrelated lines OR add them in the next gen statement
- [] Check Cash IN/OUT
## Security ToDo

@ -23,11 +23,6 @@ class AccountJournal(models.Model):
self.ensure_one()
context = dict(self.env.context or {})
if context.get('journal_type', False) == 'cash':
#context['is_payment'] = True
#new_stmt = self.env['account.bank.statement'].write({'line_ids': None})
#_logger.warning("new statement id: %s", new_stmt.id)
_logger.warning("Date used for payment_ids search in create_cash statement: %s", fields.Date.context_today(self))
payment_ids = self.env['account.payment'].search([
('journal_id', '=', self.id),
('is_matched', '=', False),
@ -37,10 +32,8 @@ class AccountJournal(models.Model):
sequence = 1
stmt_lines_to_create = []
for payment in payment_ids:
_logger.warning("payment id to treat: %s (%s)", payment.id, payment.ref)
stmt_line_values, sequence = self.generate_statement_line_values_from_payment(sequence, payment.id)
stmt_lines_to_create.append(stmt_line_values)
_logger.warning("stmt_lines_to_create: %s", stmt_lines_to_create)
if len(stmt_lines_to_create) > 0:
last_line = self.env['account.bank.statement.line'].search([
('journal_id', '=', self.id),

@ -1,7 +1,9 @@
# -*- coding: utf-8 -*-
from odoo import api, fields, models, _
from odoo.exceptions import UserError, ValidationError
from odoo.exceptions import UserError, ValidationError, RedirectWarning
import logging
_logger = logging.getLogger(__name__)
class AccountCashboxLine(models.Model):
@ -83,33 +85,19 @@ class AccountBankStmtCashWizard(models.Model):
def _check_closing(self):
self.ensure_one()
context = dict(self.env.context or {})
stmt = self.end_bank_stmt_ids
difference = stmt.currency_id.compare_amounts(stmt.balance_end_real, stmt.balance_end)
return self.env['ir.actions.act_window']._for_xml_id('gn_cash.action_view_account_bnk_stmt_check')
stmt = self.end_bank_stmt_ids[0]
difference = stmt.currency_id.round(stmt.balance_end_real - stmt.balance_end)
if difference != 0:
return difference
def _validate_cashbox(self):
for cashbox in self:
context = dict(self.env.context or {})
if cashbox.start_bank_stmt_ids:
cashbox.start_bank_stmt_ids.write({'balance_start': cashbox.total})
cashbox.start_bank_stmt_ids[0].write({'balance_start': cashbox.total})
if cashbox.end_bank_stmt_ids:
cashbox.end_bank_stmt_ids.write({'balance_end_real': cashbox.total})
if cashbox.end_bank_stmt_ids.balance_end_real != cashbox.end_bank_stmt_ids.balance_end:
cashbox._check_closing(context)
# action = {
# 'name': _('Cash Difference'),
# 'view_mode': 'form',
# 'res_model': 'gn_cash.account.bank.statement.closebalance',
# 'view_id': self.env.ref('gn_cash.action_view_account_bnk_stmt_check').id,
# 'type': 'ir.actions.act_window',
# 'res_id': cashbox_id,
# 'context': context,
# 'target': 'new'
# }
# return action
#return self.env['ir.actions.act_window']._for_xml_id('gn_cash.action_view_account_bnk_stmt_check')
cashbox.end_bank_stmt_ids[0].write({'balance_end_real': cashbox.total})
class AccountBankStmtCloseCheck(models.TransientModel):
"""
@ -118,19 +106,16 @@ class AccountBankStmtCloseCheck(models.TransientModel):
_name = 'gn_cash.account.bank.statement.closebalance'
_description = 'Bank Statement Closing Balance'
def validate(self):
_logger.warning("context in gn_cash.account.bank.statement.closebalance: %s", dict(self.env.context) or {})
bnk_stmt_id = self.env.context.get('active_id', False)
stmt = self.env['account.bank.statement'].browse(bnk_stmt_id)
if not stmt.is_complete and difference > 0:
difference = stmt.currency_id.compare_amounts(stmt.balance_end, stmt.balance_end_real)
message = "We found difference + {difference} in the statement computation, compared to your computation.",
difference = fields.Char(default=lambda self: self.env.context.get('difference', False), store=False, readonly=True)
def action_confirm(self):
context = dict(self.env.context) or {}
if context.get('difference', False):
bnk_stmt_id = context['params'].get('id', False)
stmt = self.env['account.bank.statement'].browse(bnk_stmt_id)
if stmt:
stmt._create_closing_difference_line_values()
return {'type': 'ir.actions.act_window_close'}
values = self.env['account.bank.statement'].browse(bnk_stmt_id)._check_cash_balance_end_real_same_as_computed()
if values:
return self.env['ir.actions.act_window']._for_xml_id('account.action_view_account_bnk_stmt_check')
else:
return {'type': 'ir.actions.act_window_close'}
class BankStatement(models.Model):
_inherit = 'account.bank.statement'
@ -138,43 +123,45 @@ class BankStatement(models.Model):
cashbox_start_id = fields.Many2one('gn_cash.account.bank.statement.cashbox', string="Starting Cashbox")
cashbox_end_id = fields.Many2one('gn_cash.account.bank.statement.cashbox', string="Ending Cashbox")
def _check_cash_balance_end_real_same_as_computed(self):
def _create_closing_difference_line_values(self):
""" Check the balance_end_real (encoded manually by the user) is equals to the balance_end (computed by odoo).
For a cash statement, if there is a difference, the different is set automatically to a profit/loss account.
"""
for statement in self.filtered(lambda stmt: stmt.journal_type == 'cash'):
if not statement.currency_id.is_zero(statement.difference):
st_line_vals = {
'statement_id': statement.id,
'journal_id': statement.journal_id.id,
'amount': statement.difference,
'date': statement.date,
}
if statement.currency_id.compare_amounts(statement.difference, 0.0) < 0.0:
if not statement.journal_id.loss_account_id:
raise UserError(_(
"Please go on the %s journal and define a Loss Account. "
"This account will be used to record cash difference.",
statement.journal_id.name
))
st_line_vals['payment_ref'] = _("Cash difference observed during the counting (Loss)")
st_line_vals['counterpart_account_id'] = statement.journal_id.loss_account_id.id
else:
# statement.difference > 0.0
if not statement.journal_id.profit_account_id:
raise UserError(_(
"Please go on the %s journal and define a Profit Account. "
"This account will be used to record cash difference.",
statement.journal_id.name
))
st_line_vals['payment_ref'] = _("Cash difference observed during the counting (Profit)")
st_line_vals['counterpart_account_id'] = statement.journal_id.profit_account_id.id
self.env['account.bank.statement.line'].create(st_line_vals)
return True
for statement in self.filtered(lambda stmt: stmt.journal_id.type == 'cash'):
if not statement.is_complete:
difference = statement.currency_id.round(statement.balance_end_real - statement.balance_end)
if difference != 0:
st_line_vals = {
'statement_id': statement.id,
'journal_id': statement.journal_id.id,
'amount': difference,
'date': statement.date,
}
if difference < 0:
if not statement.journal_id.loss_account_id:
raise UserError(_(
"Please go on the %s journal and define a Loss Account. "
"This account will be used to record cash difference.",
statement.journal_id.name
))
st_line_vals['payment_ref'] = _("Cash difference observed during the counting (Loss)")
st_line_vals['counterpart_account_id'] = statement.journal_id.loss_account_id.id
else:
# statement.difference > 0.0
if not statement.journal_id.profit_account_id:
raise UserError(_(
"Please go on the %s journal and define a Profit Account. "
"This account will be used to record cash difference.",
statement.journal_id.name
))
st_line_vals['payment_ref'] = _("Cash difference observed during the counting (Profit)")
st_line_vals['counterpart_account_id'] = statement.journal_id.profit_account_id.id
self.env['account.bank.statement.line'].create(st_line_vals)
def open_cashbox_id(self):
self.ensure_one()
@ -199,4 +186,17 @@ class BankStatement(models.Model):
'target': 'new'
}
return action
return action
def button_validate(self):
for statement in self:
if not statement.is_complete:
action = self.env['ir.actions.act_window']._for_xml_id('gn_cash.action_view_account_bnk_stmt_check')
difference = statement.cashbox_end_id._check_closing()
difference = statement.currency_id.format(difference)
if difference:
context = dict(self.env.context or {})
context['difference'] = difference
action['context'] = context
_logger.warning("Context: %s", context)
return action

@ -1,3 +1,4 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_account_cashbox,account.bank.statement.cashbox,model_gn_cash_account_bank_statement_cashbox,account.group_account_user,1,1,1,1
access_account_cashbox_line,account.bank.statement.cashbox.line,model_gn_cash_account_cashbox_line,account.group_account_user,1,1,1,1
access_account_cashbox_line,account.bank.statement.cashbox.line,model_gn_cash_account_cashbox_line,account.group_account_user,1,1,1,1
access_account_bank_statement_closebalance,account.bank.statement.closebalance,model_gn_cash_account_bank_statement_closebalance,account.group_account_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_account_cashbox account.bank.statement.cashbox model_gn_cash_account_bank_statement_cashbox account.group_account_user 1 1 1 1
3 access_account_cashbox_line account.bank.statement.cashbox.line model_gn_cash_account_cashbox_line account.group_account_user 1 1 1 1
4 access_account_bank_statement_closebalance account.bank.statement.closebalance model_gn_cash_account_bank_statement_closebalance account.group_account_user 1 1 1 1

@ -52,10 +52,11 @@
<field name="arch" type="xml">
<form>
<div>
<p>The closing balance is different than the computed one!</p>
<p>The closing balance is different than the computed one! (<span> <field name="difference"/></span>)</p>
<p>Confirming this will create automatically a journal entry with the difference in the profit/loss account set on the cash journal.</p>
<footer>
<button string="Confirm" name="validate" type="object" class="btn-primary" data-hotkey="q"/>
<button string="Confirm" name="action_confirm" msg="test" type="object" class="btn-primary" data-hotkey="q"/>
<button string="Cancel" class="btn-secondary" special="cancel" data-hotkey="z"/>
</footer>
</div>
@ -65,8 +66,8 @@
<record id="gn_cash.action_view_account_bnk_stmt_check" model="ir.actions.act_window">
<field name="name">Check Closing Balance</field>
<field name="res_model">gn_cash.account.bank.statement.closebalance</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_account_bnk_stmt_check"/>
<field name="view_mode">form</field>
<field name="view_id" ref="gn_cash.view_account_bnk_stmt_check"/>
<field name="target">new</field>
</record>
</data>

@ -24,6 +24,12 @@
<field name="inherit_id" ref="account_statement_base.view_bank_statement_form"/>
<field name="priority">100</field>
<field name="arch" type="xml">
<xpath expr="//div[@role='alert']" position="before">
<header>
<field name="is_complete" invisible="1"/>
<button name="button_validate" invisible="context.get('journal_type', False) != 'cash'" attrs="{'invisible': [('is_complete', '=', True)]}" string="Clôturer" type="object" class="oe_highlight" />
</header>
</xpath>
<xpath expr="//field[@name='balance_start']" position="after" >
<button name="open_cashbox_id" invisible="context.get('journal_type', False) != 'cash'" string="&#8594; Count" type="object" class="oe_edit_only oe_link oe_inline" context="{'balance':'start'}"/>
</xpath>

Loading…
Cancel
Save