[IMP] account: improve invoice UX and bank addition

When using the wizard to add a bank account, it should be added to
an existing journal only if the journal has no bank account set
and no journal entries.

We also want to be able to remove a set bank account.

When using the wizard to add a bank account (Create it button ;
not Connect button), it should be added to an existing journal
only if the journal has no bank account set and no journal entries.

We remove an unwanted constraint on the bank journals that
prevented the removal of an Account Number (IBAN).

Some UX changes. In the Journal Items list view, specific views
open depending on their move type. This commit also changes
the "View" button of the invoice payment widget on the Journal Entry's
form view in order to align behaviours.

The core open_move method is also moved from the account move line
model to the account move's one for more flexibility.

The "1 Statement" button of the payment's form view now links to the
new reconciliation widget.

The Journal Items lists accessed from the Journal report now show
the partner by default.

The "Unreconciled" filter now filter out partial reconciliations which
have a residual amount != 0.

The Partner column should almost always be shown by default in a
Journal Items list view.

task-2893208

closes odoo/odoo#94923

Related: odoo/enterprise#29092
Signed-off-by: Florian Gilbert (flg) <flg@odoo.com>
This commit is contained in:
Pierrot (prro)
2022-07-27 16:33:51 +02:00
parent ef5a9612f7
commit 157ae1c708
7 changed files with 47 additions and 80 deletions
+1 -3
View File
@@ -538,9 +538,7 @@ class AccountJournal(models.Model):
if journal.bank_account_id:
journal.bank_account_id.currency_id = vals['currency_id']
if 'bank_account_id' in vals:
if not vals.get('bank_account_id'):
raise UserError(_('You cannot remove the bank account from the journal once set.'))
else:
if vals.get('bank_account_id'):
bank_account = self.env['res.partner.bank'].browse(vals['bank_account_id'])
if bank_account.partner_id != company.partner_id:
raise UserError(_("The partners of the journal's company and the related bank account mismatch."))
+13 -18
View File
@@ -1818,7 +1818,6 @@ class AccountMove(models.Model):
'digits': [69, reconciled_partial['currency'].decimal_places],
'position': reconciled_partial['currency'].position,
'date': counterpart_line.date,
'payment_id': counterpart_line.id,
'partial_id': reconciled_partial['partial_id'],
'account_payment_id': counterpart_line.payment_id.id,
'payment_method_name': counterpart_line.payment_id.payment_method_line_id.name,
@@ -2946,6 +2945,18 @@ class AccountMove(models.Model):
'views': [(self.env.ref('account.view_move_tree').id, 'tree'), (False, 'form')],
}
def open_move(self):
if self.payment_id:
return self.open_payment_view()
else:
return {
'type': 'ir.actions.act_window',
'res_model': 'account.move',
'view_mode': 'form',
'res_id': self.id,
'views': [(False, 'form')],
}
def post(self):
warnings.warn(
"RedirectWarning method 'post()' is a deprecated alias to 'action_post()' or _post()",
@@ -6068,23 +6079,7 @@ class AccountMoveLine(models.Model):
return action
def open_move(self):
if self.payment_id:
return {
'type': 'ir.actions.act_window',
'res_model': 'account.payment',
'view_mode': 'form',
'res_id': self.payment_id.id,
'views': [(False, 'form')],
}
else:
return {
'type': 'ir.actions.act_window',
'res_model': 'account.move',
'view_mode': 'form',
'res_id': self.move_id.id,
'views': [(False, 'form')],
}
return self.move_id.open_move()
def action_automatic_entry(self):
action = self.env['ir.actions.act_window']._for_xml_id('account.account_automatic_entry_wizard_action')
@@ -88,28 +88,16 @@ var ShowPaymentLineWidget = AbstractField.extend({
* @param {MouseEvent} event
*/
_onOpenPaymentOrMove: function (event) {
var paymentId = parseInt($(event.target).attr('payment-id'));
var moveId = parseInt($(event.target).attr('move-id'));
var resModel;
var id;
if (paymentId !== undefined && !isNaN(paymentId)){
resModel = "account.payment";
id = paymentId;
} else if (moveId !== undefined && !isNaN(moveId)){
resModel = "account.move";
id = moveId;
}
//Open form view of account.move with id = move_id
//viewAlreadyopened is a flag to prevent the user from clicking on another account.move/account.payment
//while the first one he clicked on is loading
if (!this.viewAlreadyOpened && resModel && id) {
if (!this.viewAlreadyOpened && moveId !== undefined && !isNaN(moveId)) {
this.viewAlreadyOpened = true;
this.do_action({
type: 'ir.actions.act_window',
res_model: resModel,
res_id: id,
views: [[false, 'form']],
target: 'current'
var self = this;
this._rpc({
model: 'account.move',
method: 'open_move',
args: [moveId],
}).then(function (actionData) {
return self.do_action(actionData);
});
}
},
@@ -103,7 +103,7 @@
</table>
</div>
<button class="btn btn-sm btn-primary js_unreconcile_payment float-start" t-att-partial-id="partial_id" t-att-payment-id="payment_id" t-att-move-id="move_id" style="margin-top:5px; margin-bottom:5px;" groups="account.group_account_invoice">Unreconcile</button>
<button class="btn btn-sm btn-secondary js_open_payment float-end" t-att-payment-id="account_payment_id" t-att-move-id="move_id" style="margin-top:5px; margin-bottom:5px;">View</button>
<button class="btn btn-sm btn-secondary js_open_payment float-end" t-att-move-id="move_id" style="margin-top:5px; margin-bottom:5px;">View</button>
</t>
</templates>
@@ -25,7 +25,7 @@ QUnit.module('account', {
}, function () {
QUnit.module('Reconciliation');
QUnit.test('Reconciliation form field', async function (assert) {
QUnit.test('Reconciliation form field [REQUIRES FOCUS]', async function (assert) {
assert.expect(5);
var form = await createView({
@@ -46,37 +46,31 @@ QUnit.module('account', {
assert.deepEqual(args.args, [4, 20], "should call js_assign_outstanding_line {warning: required focus}");
return Promise.resolve();
}
if (args.method === 'open_move') {
assert.deepEqual(args.args, [10], "should call open_move {warning: required focus}");
return Promise.resolve();
}
return this._super.apply(this, arguments);
},
intercepts: {
do_action: function (event) {
assert.deepEqual(event.data.action, {
'type': 'ir.actions.act_window',
'res_model': 'account.move',
'res_id': 10,
'views': [[false, 'form']],
'target': 'current'
},
"should open the form view");
},
},
});
assert.strictEqual(form.$('.o_field_widget[name="payments_widget"]').text().replace(/[\s\n\r]+/g, ' '),
" Paid on 04/25/2017 $ 555.00 ",
"should display payment information");
form.$('.o_field_widget[name="outstanding_credits_debits_widget"] .outstanding_credit_assign').trigger('click');
await testUtils.dom.click(form.$('.o_field_widget[name="outstanding_credits_debits_widget"] .outstanding_credit_assign'));
assert.strictEqual(form.$('.o_field_widget[name="outstanding_credits_debits_widget"]').text().replace(/[\s\n\r]+/g, ' '),
" Outstanding credits Add INV/2017/0004 $ 100.00 ",
"should display outstanding information");
form.$('.o_field_widget[name="payments_widget"] .js_payment_info').trigger('focus');
form.$('.popover .js_open_payment').trigger('click');
form.$('.o_field_widget[name="payments_widget"] .js_payment_info').focus();
await testUtils.nextTick();
await testUtils.dom.click(form.$('.popover .js_open_payment'));
form.$('.o_field_widget[name="payments_widget"] .js_payment_info').trigger('focus');
form.$('.popover .js_unreconcile_payment').trigger('click');
form.$('.o_field_widget[name="payments_widget"] .js_payment_info').focus();
await testUtils.nextTick();
await testUtils.dom.click(form.$('.popover .js_unreconcile_payment'));
form.destroy();
});
+7 -18
View File
@@ -166,9 +166,9 @@
<field name="date" readonly="1"/>
<field name="company_id" groups="base.group_multi_company" optional="hide"/>
<field name="journal_id" options='{"no_open":True}' optional="hide"/>
<field name="move_name" string="Journal Entry" optional="show" widget="open_move_widget"/>
<field name="move_name" string="Journal Entry" widget="open_move_widget"/>
<field name="account_id" options="{'no_open': True, 'no_create': True}" domain="[('company_id', '=', company_id)]" groups="account.group_account_readonly"/>
<field name="partner_id" optional="hide" attrs="{'readonly':[('account_type','in', ('liability_payable','asset_receivable'))]}"/>
<field name="partner_id" optional="show" attrs="{'readonly':[('account_type','in', ('liability_payable','asset_receivable'))]}"/>
<field name="ref" optional="hide"/>
<field name="product_id" optional="hide"/>
<field name="name" optional="show"/>
@@ -184,8 +184,8 @@
<field name="balance" sum="Total Balance" optional="hide" readonly="1"/>
<field name="cumulated_balance" optional="hide"/>
<field name="matching_number" optional="show"/>
<field name="amount_residual" string="Residual" optional="hide" attrs="{'invisible':[('account_type', 'not in', ('liability_payable','asset_receivable'))]}"/>
<field name="amount_residual_currency" string="Residual in Currency" optional="hide" attrs="{'invisible':['|', ('is_same_currency', '=', True), ('account_type', 'not in', ('liability_payable','asset_receivable'))]}"/>
<field name="amount_residual" sum="Total Residual" string="Residual" optional="hide" attrs="{'invisible':[('is_account_reconcile', '=', False)]}"/>
<field name="amount_residual_currency" sum="Total Residual in Currency" string="Residual in Currency" optional="hide" attrs="{'invisible':['|', ('is_same_currency', '=', True), ('account_type', 'not in', ('liability_payable','asset_receivable'))]}"/>
<field name="parent_state" invisible="1"/>
<field name="account_type" invisible="1"/>
@@ -194,6 +194,7 @@
<field name="reconcile_model_id" invisible="1"/>
<field name="reconciled" invisible="1"/>
<field name="is_same_currency" invisible="1"/>
<field name="is_account_reconcile" invisible="1"/>
<groupby name="partner_id">
<button name="edit" type="edit" icon="fa-edit" title="Edit"/>
</groupby>
@@ -208,7 +209,6 @@
<field name="inherit_id" ref="account.view_move_line_tree"/>
<field name="arch" type="xml">
<field name="date" position="attributes"><attribute name="optional">hide</attribute></field>
<field name="move_name" position="attributes"><attribute name="optional">hide</attribute></field>
<field name="tax_tag_ids" position="attributes"><attribute name="optional">show</attribute></field>
</field>
</record>
@@ -220,7 +220,6 @@
<field name="inherit_id" ref="account.view_move_line_tree"/>
<field name="arch" type="xml">
<field name="date" position="attributes"><attribute name="optional">hide</attribute></field>
<field name="move_name" position="attributes"><attribute name="optional">hide</attribute></field>
</field>
</record>
@@ -231,7 +230,6 @@
<field name="inherit_id" ref="account.view_move_line_tree"/>
<field name="arch" type="xml">
<field name="date" position="attributes"><attribute name="optional">hide</attribute></field>
<field name="move_name" position="attributes"><attribute name="optional">hide</attribute></field>
</field>
</record>
@@ -267,12 +265,10 @@
<field name="mode">primary</field>
<field name="inherit_id" ref="account.view_move_line_tree"/>
<field name="arch" type="xml">
<field name="move_name" position="replace"/>
<field name="matching_number" position="replace">
<field name="tax_line_id" string="Tax"/>
<field name="tax_base_amount" sum="Total Base Amount"/>
<field name="tax_audit"/>
<field name="move_name"/>
</field>
<field name="analytic_account_id" position="attributes">
<attribute name="optional">hide</attribute>
@@ -286,9 +282,6 @@
<field name="journal_id" position="attributes">
<attribute name="optional">show</attribute>
</field>
<field name="move_name" position="attributes">
<attribute name="optional">hide</attribute>
</field>
</field>
</record>
@@ -330,7 +323,7 @@
<separator/>
<filter string="To Check" name="to_check" domain="[('move_id.to_check', '=', True)]"/>
<separator/>
<filter string="Unreconciled" domain="[('full_reconcile_id', '=', False), ('balance', '!=', 0), ('account_id.reconcile', '=', True)]" help="Journal items where matching number isn't set" name="unreconciled"/>
<filter string="Unreconciled" domain="[('amount_residual', '!=', 0), ('account_id.reconcile', '=', True)]" help="Journal items where matching number isn't set" name="unreconciled"/>
<separator/>
<filter string="Sales" name="sales" domain="[('journal_id.type', '=', 'sale')]" context="{'default_journal_type': 'sale'}"/>
<filter string="Purchases" name="purchases" domain="[('journal_id.type', '=', 'purchase')]" context="{'default_journal_type': 'purchase'}"/>
@@ -476,10 +469,6 @@
<xpath expr="//field[@name='ref']" position="attributes">
<attribute name="optional">show</attribute>
</xpath>
<xpath expr="//field[@name='payment_state']" position="attributes">
<attribute name="optional">hide</attribute>
</xpath>
</field>
</record>
@@ -672,7 +661,7 @@
</div>
<sheet>
<div name="button_box" class="oe_button_box">
<button name="open_bank_statement_view"
<button name="open_move"
class="oe_stat_button"
icon="fa-bars"
type="object"
+5 -2
View File
@@ -116,8 +116,11 @@ class SetupBarBankConfigWizard(models.TransientModel):
record.linked_journal_id = record.journal_id and record.journal_id[0] or record.default_linked_journal_id()
def default_linked_journal_id(self):
default = self.env['account.journal'].search([('type', '=', 'bank'), ('bank_account_id', '=', False)], limit=1)
return default[:1].id
for journal_id in self.env['account.journal'].search([('type', '=', 'bank'), ('bank_account_id', '=', False)]):
empty_journal_count = self.env['account.move'].search_count([('journal_id', '=', journal_id.id)])
if empty_journal_count == 0:
return journal_id.id
return False
def set_linked_journal_id(self):
""" Called when saving the wizard.