diff --git a/addons/account/models/account_journal.py b/addons/account/models/account_journal.py index 13c418cc099..0bab630145f 100644 --- a/addons/account/models/account_journal.py +++ b/addons/account/models/account_journal.py @@ -107,19 +107,41 @@ class AccountJournal(models.Model): sequence_override_regex = fields.Text(help="Technical field used to enforce complex sequence composition that the system would normally misunderstand.\n"\ "This is a regex that can include all the following capture groups: prefix1, year, prefix2, month, prefix3, seq, suffix.\n"\ "The prefix* groups are the separators between the year, month and the actual increasing sequence number (seq).\n"\ - + "e.g: ^(?P.*?)(?P\d{4})(?P\D*?)(?P\d{2})(?P\D+?)(?P\d+)(?P\D*?)$") - inbound_payment_method_ids = fields.Many2many('account.payment.method', 'account_journal_inbound_payment_method_rel', 'journal_id', 'inbound_payment_method', - domain=[('payment_type', '=', 'inbound')], string='Inbound Payment Methods', default=lambda self: self._default_inbound_payment_methods(), - help="Manual: Get paid by cash, check or any other method outside of Odoo.\n"\ - "Electronic: Get paid automatically through a payment acquirer by requesting a transaction on a card saved by the customer when buying or subscribing online (payment token).\n"\ - "Batch Deposit: Encase several customer checks at once by generating a batch deposit to submit to your bank. When encoding the bank statement in Odoo,you are suggested to reconcile the transaction with the batch deposit. Enable this option from the settings.") - outbound_payment_method_ids = fields.Many2many('account.payment.method', 'account_journal_outbound_payment_method_rel', 'journal_id', 'outbound_payment_method', - domain=[('payment_type', '=', 'outbound')], string='Outbound Payment Methods', default=lambda self: self._default_outbound_payment_methods(), - help="Manual:Pay bill by cash or any other method outside of Odoo.\n"\ - "Check:Pay bill by check and print it from Odoo.\n"\ - "SEPA Credit Transfer: Pay bill from a SEPA Credit Transfer file you submit to your bank. Enable this option from the settings.") + inbound_payment_method_ids = fields.Many2many( + comodel_name='account.payment.method', + relation='account_journal_inbound_payment_method_rel', + column1='journal_id', + column2='inbound_payment_method', + domain=[('payment_type', '=', 'inbound')], + string='Inbound Payment Methods', + compute='_compute_inbound_payment_method_ids', + store=True, + readonly=False, + help="Manual: Get paid by cash, check or any other method outside of Odoo.\n" + "Electronic: Get paid automatically through a payment acquirer by requesting a transaction" + " on a card saved by the customer when buying or subscribing online (payment token).\n" + "Batch Deposit: Encase several customer checks at once by generating a batch deposit to" + " submit to your bank. When encoding the bank statement in Odoo,you are suggested to" + " reconcile the transaction with the batch deposit. Enable this option from the settings." + ) + outbound_payment_method_ids = fields.Many2many( + comodel_name='account.payment.method', + relation='account_journal_outbound_payment_method_rel', + column1='journal_id', + column2='outbound_payment_method', + domain=[('payment_type', '=', 'outbound')], + string='Outbound Payment Methods', + compute='_compute_outbound_payment_method_ids', + store=True, + readonly=False, + help="Manual:Pay bill by cash or any other method outside of Odoo.\n" + "Check:Pay bill by check and print it from Odoo.\n" + "SEPA Credit Transfer: Pay bill from a SEPA Credit Transfer file you submit to your" + " bank. Enable this option from the settings." + ) at_least_one_inbound = fields.Boolean(compute='_methods_compute', store=True) at_least_one_outbound = fields.Boolean(compute='_methods_compute', store=True) profit_account_id = fields.Many2one( @@ -190,6 +212,22 @@ class AccountJournal(models.Model): else: journal.default_account_type = False + @api.depends('type') + def _compute_outbound_payment_method_ids(self): + for journal in self: + if journal.type in ('bank', 'cash'): + journal.outbound_payment_method_ids = self._default_outbound_payment_methods() + else: + journal.outbound_payment_method_ids = False + + @api.depends('type') + def _compute_inbound_payment_method_ids(self): + for journal in self: + if journal.type in ('bank', 'cash'): + journal.inbound_payment_method_ids = self._default_inbound_payment_methods() + else: + journal.inbound_payment_method_ids = False + @api.depends('company_id', 'type') def _compute_suspense_account_id(self): for journal in self: diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index 221518dae56..7a7f2824efe 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -171,8 +171,13 @@ class AccountMove(models.Model): country_code = fields.Char(related='company_id.country_id.code', readonly=True) user_id = fields.Many2one(string='User', related='invoice_user_id', help='Technical field used to fit the generic behavior in mail templates.') - is_move_sent = fields.Boolean(readonly=True, default=False, copy=False, - help="It indicates that the invoice/payment has been sent.") + is_move_sent = fields.Boolean( + readonly=True, + default=False, + copy=False, + tracking=True, + help="It indicates that the invoice/payment has been sent.", + ) partner_bank_id = fields.Many2one('res.partner.bank', string='Recipient Bank', help='Bank Account Number to which the invoice will be paid. A Company bank account if this is a Customer Invoice or Vendor Credit Note, otherwise a Partner bank account number.', check_company=True) diff --git a/addons/account/models/account_payment.py b/addons/account/models/account_payment.py index 66c5e5df5c8..40c4c035e1a 100644 --- a/addons/account/models/account_payment.py +++ b/addons/account/models/account_payment.py @@ -109,6 +109,11 @@ class AccountPayment(models.Model): help="Invoices whose journal items have been reconciled with these payments.") reconciled_invoices_count = fields.Integer(string="# Reconciled Invoices", compute="_compute_stat_buttons_from_reconciliation") + reconciled_bill_ids = fields.Many2many('account.move', string="Reconciled Bills", + compute='_compute_stat_buttons_from_reconciliation', + help="Invoices whose journal items have been reconciled with these payments.") + reconciled_bills_count = fields.Integer(string="# Reconciled Bills", + compute="_compute_stat_buttons_from_reconciliation") reconciled_statement_ids = fields.Many2many('account.move', string="Reconciled Statements", compute='_compute_stat_buttons_from_reconciliation', help="Statements matched to this payment") @@ -422,6 +427,8 @@ class AccountPayment(models.Model): if not stored_payments: self.reconciled_invoice_ids = False self.reconciled_invoices_count = 0 + self.reconciled_bill_ids = False + self.reconciled_bills_count = 0 self.reconciled_statement_ids = False self.reconciled_statements_count = 0 return @@ -433,7 +440,8 @@ class AccountPayment(models.Model): self._cr.execute(''' SELECT payment.id, - ARRAY_AGG(DISTINCT invoice.id) AS invoice_ids + ARRAY_AGG(DISTINCT invoice.id) AS invoice_ids, + invoice.move_type FROM account_payment payment JOIN account_move move ON move.id = payment.move_id JOIN account_move_line line ON line.move_id = move.id @@ -448,16 +456,24 @@ class AccountPayment(models.Model): JOIN account_move invoice ON invoice.id = counterpart_line.move_id JOIN account_account account ON account.id = line.account_id WHERE account.internal_type IN ('receivable', 'payable') + AND payment.id IN %(payment_ids)s AND line.id != counterpart_line.id AND invoice.move_type in ('out_invoice', 'out_refund', 'in_invoice', 'in_refund', 'out_receipt', 'in_receipt') - GROUP BY payment.id - ''') - query_res = dict((payment_id, invoice_ids) for payment_id, invoice_ids in self._cr.fetchall()) - - for pay in self: - invoice_ids = query_res.get(pay.id, []) - pay.reconciled_invoice_ids = [(6, 0, invoice_ids)] - pay.reconciled_invoices_count = len(invoice_ids) + GROUP BY payment.id, invoice.move_type + ''', { + 'payment_ids': tuple(stored_payments.ids) + }) + query_res = self._cr.dictfetchall() + self.reconciled_invoice_ids = self.reconciled_invoices_count = False + self.reconciled_bill_ids = self.reconciled_bills_count = False + for res in query_res: + pay = self.browse(res['id']) + if res['move_type'] in self.env['account.move'].get_sale_types(True): + pay.reconciled_invoice_ids += self.env['account.move'].browse(res.get('invoice_ids', [])) + pay.reconciled_invoices_count = len(res.get('invoice_ids', [])) + else: + pay.reconciled_bill_ids += self.env['account.move'].browse(res.get('invoice_ids', [])) + pay.reconciled_bills_count = len(res.get('invoice_ids', [])) self._cr.execute(''' SELECT @@ -477,10 +493,13 @@ class AccountPayment(models.Model): OR part.credit_move_id = counterpart_line.id WHERE (account.id = journal.payment_debit_account_id OR account.id = journal.payment_credit_account_id) + AND payment.id IN %(payment_ids)s AND line.id != counterpart_line.id AND counterpart_line.statement_id IS NOT NULL GROUP BY payment.id - ''') + ''', { + 'payment_ids': tuple(stored_payments.ids) + }) query_res = dict((payment_id, statement_ids) for payment_id, statement_ids in self._cr.fetchall()) for pay in self: @@ -748,6 +767,30 @@ class AccountPayment(models.Model): }) return action + def button_open_bills(self): + ''' Redirect the user to the bill(s) paid by this payment. + :return: An action on account.move. + ''' + self.ensure_one() + + action = { + 'name': _("Paid Bills"), + 'type': 'ir.actions.act_window', + 'res_model': 'account.move', + 'context': {'create': False}, + } + if len(self.reconciled_bill_ids) == 1: + action.update({ + 'view_mode': 'form', + 'res_id': self.reconciled_bill_ids.id, + }) + else: + action.update({ + 'view_mode': 'list,form', + 'domain': [('id', 'in', self.reconciled_bill_ids.ids)], + }) + return action + def button_open_statements(self): ''' Redirect the user to the statement line(s) reconciled to this payment. :return: An action on account.move. diff --git a/addons/account/security/ir.model.access.csv b/addons/account/security/ir.model.access.csv index 2013d5dbd4b..cddbb5372f2 100644 --- a/addons/account/security/ir.model.access.csv +++ b/addons/account/security/ir.model.access.csv @@ -118,7 +118,7 @@ access_account_payment_term_partner_manager,account.payment.term partner manager access_account_payment_term_manager,account.payment.term,model_account_payment_term,account.group_account_manager,1,1,1,1 access_account_payment_term_line_partner_manager,account.payment.term.line partner manager,model_account_payment_term_line,base.group_user,1,0,0,0 access_account_payment_term_line_manager,account.payment.term.line,model_account_payment_term_line,account.group_account_manager,1,1,1,1 -access_account_payment_method_readonly,account.payment.method,model_account_payment_method,account.group_account_readonly,1,0,0,0 +access_account_payment_method_readonly,account.payment.method,model_account_payment_method,base.group_user,1,0,0,0 access_account_payment_method,account.payment.method,model_account_payment_method,account.group_account_invoice,1,1,1,1 access_account_payment_readonly,account.payment,model_account_payment,account.group_account_readonly,1,0,0,0 access_account_payment,account.payment,model_account_payment,account.group_account_invoice,1,1,1,1 diff --git a/addons/account/views/account_payment_view.xml b/addons/account/views/account_payment_view.xml index 18894102b42..2e4a05b74a8 100644 --- a/addons/account/views/account_payment_view.xml +++ b/addons/account/views/account_payment_view.xml @@ -171,6 +171,15 @@ Invoices + + +