mirror of
https://github.com/OCA/bank-payment.git
synced 2025-02-02 10:37:31 +02:00
@@ -1,7 +1,7 @@
|
||||
# Copyright 2014 Compassion CH - Cyril Sester <csester@compassion.ch>
|
||||
# Copyright 2014 Tecnativa - Pedro M. Baeza
|
||||
# Copyright 2015-2020 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
# Copyright 2017 Tecnativa - Carlos Dauden <carlos.dauden@tecnativa.com>
|
||||
# Copyright 2017 Tecnativa - Carlos Dauden
|
||||
# Copyright 2014-2022 Tecnativa - Pedro M. Baeza
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
{
|
||||
@@ -23,7 +23,6 @@
|
||||
"views/account_payment_line.xml",
|
||||
"views/res_partner_bank_view.xml",
|
||||
"views/res_partner.xml",
|
||||
"views/bank_payment_line_view.xml",
|
||||
"data/mandate_reference_sequence.xml",
|
||||
"security/mandate_security.xml",
|
||||
"security/ir.model.access.csv",
|
||||
|
||||
@@ -4,5 +4,4 @@ from . import account_move
|
||||
from . import res_partner_bank
|
||||
from . import res_partner
|
||||
from . import account_payment_line
|
||||
from . import bank_payment_line
|
||||
from . import account_move_line
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
# Copyright 2014 Compassion CH - Cyril Sester <csester@compassion.ch>
|
||||
# Copyright 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza
|
||||
# Copyright 2015-16 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class BankPaymentLine(models.Model):
|
||||
_inherit = "bank.payment.line"
|
||||
|
||||
mandate_id = fields.Many2one(
|
||||
comodel_name="account.banking.mandate",
|
||||
string="Direct Debit Mandate",
|
||||
related="payment_line_ids.mandate_id",
|
||||
check_company=True,
|
||||
)
|
||||
|
||||
@api.model
|
||||
def same_fields_payment_line_and_bank_payment_line(self):
|
||||
res = super().same_fields_payment_line_and_bank_payment_line()
|
||||
res.append("mandate_id")
|
||||
return res
|
||||
@@ -1,4 +1,5 @@
|
||||
# Copyright 2017 Creu Blanca
|
||||
# Copyright 2017-2022 Tecnativa - Pedro M. Baeza
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from odoo import fields
|
||||
@@ -9,27 +10,16 @@ from odoo.tests.common import TransactionCase
|
||||
class TestInvoiceMandate(TransactionCase):
|
||||
def test_post_invoice_01(self):
|
||||
self.invoice._onchange_partner_id()
|
||||
|
||||
prev_orders = self.env["account.payment.order"].search([])
|
||||
self.assertEqual(self.invoice.mandate_id, self.mandate)
|
||||
|
||||
self.invoice.action_post()
|
||||
|
||||
payable_move_lines = self.invoice.line_ids.filtered(
|
||||
lambda s: s.account_id == self.invoice_account
|
||||
)
|
||||
if payable_move_lines:
|
||||
self.assertEqual(payable_move_lines[0].move_id.mandate_id, self.mandate)
|
||||
|
||||
self.env["account.invoice.payment.line.multi"].with_context(
|
||||
active_model="account.move", active_ids=self.invoice.ids
|
||||
).create({}).run()
|
||||
|
||||
payment_order = self.env["account.payment.order"].search([])
|
||||
payment_order = self.env["account.payment.order"].search([]) - prev_orders
|
||||
self.assertEqual(len(payment_order.ids), 1)
|
||||
payment_order.payment_mode_id_change()
|
||||
payment_order.draft2open()
|
||||
payment_order.open2generated()
|
||||
payment_order.generated2uploaded()
|
||||
self.assertEqual(self.mandate.payment_line_ids_count, 1)
|
||||
|
||||
def test_post_invoice_02(self):
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!--
|
||||
© 2015-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
|
||||
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
-->
|
||||
<odoo>
|
||||
<record id="bank_payment_line_form" model="ir.ui.view">
|
||||
<field name="name">banking.mandate.bank.payment.line.form</field>
|
||||
<field name="model">bank.payment.line</field>
|
||||
<field name="inherit_id" ref="account_payment_order.bank_payment_line_form" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="partner_bank_id" position="after">
|
||||
<field
|
||||
name="mandate_id"
|
||||
invisible="context.get('default_payment_type')!='inbound'"
|
||||
/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
<record id="bank_payment_line_tree" model="ir.ui.view">
|
||||
<field name="name">banking.mandate.bank.payment.line.tree</field>
|
||||
<field name="model">bank.payment.line</field>
|
||||
<field name="inherit_id" ref="account_payment_order.bank_payment_line_tree" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="partner_bank_id" position="after">
|
||||
<field
|
||||
name="mandate_id"
|
||||
string="Mandate"
|
||||
invisible="context.get('default_payment_type')!='inbound'"
|
||||
/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -1,7 +1,7 @@
|
||||
# Copyright 2013-2016 Akretion - Alexis de Lattre
|
||||
# Copyright 2014-2017 Tecnativa - Pedro M. Baeza
|
||||
# Copyright 2016 Tecnativa - Antonio Espinosa
|
||||
# Copyright 2021 Tecnativa - Carlos Roca
|
||||
# Copyright 2014-2022 Tecnativa - Pedro M. Baeza
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
{
|
||||
@@ -18,7 +18,6 @@
|
||||
"security/security.xml",
|
||||
"views/account_payment_line.xml",
|
||||
"views/account_payment_order.xml",
|
||||
"views/bank_payment_line_view.xml",
|
||||
"views/account_payment_mode.xml",
|
||||
"views/res_config_settings.xml",
|
||||
"views/account_payment_method.xml",
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from . import account_payment_line
|
||||
from . import account_payment_order
|
||||
from . import bank_payment_line
|
||||
from . import account_payment_mode
|
||||
from . import res_company
|
||||
from . import res_config_settings
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# Copyright 2013-2016 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
# Copyright 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza
|
||||
# Copyright 2021 Tecnativa - Carlos Roca
|
||||
# Copyright 2014-2022 Tecnativa - Pedro M. Baeza
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from odoo import fields, models
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class AccountPaymentLine(models.Model):
|
||||
@@ -173,3 +173,10 @@ class AccountPaymentLine(models.Model):
|
||||
communication_type = fields.Selection(
|
||||
selection_add=[("ISO", "ISO")], ondelete={"ISO": "cascade"}
|
||||
)
|
||||
|
||||
@api.model
|
||||
def _get_payment_line_grouping_fields(self):
|
||||
"""Add specific PAIN fields to the grouping criteria."""
|
||||
res = super()._get_payment_line_grouping_fields()
|
||||
res += ["priority", "local_instrument", "category_purpose", "purpose"]
|
||||
return res
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Copyright 2013-2016 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
# Copyright 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza
|
||||
# Copyright 2016 Antiun Ingenieria S.L. - Antonio Espinosa
|
||||
# Copyright 2021 Tecnativa - Carlos Roca
|
||||
# Copyright 2014-2022 Tecnativa - Pedro M. Baeza
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
import logging
|
||||
@@ -610,11 +610,12 @@ class AccountPaymentOrder(models.Model):
|
||||
@api.model
|
||||
def generate_remittance_info_block(self, parent_node, line, gen_args):
|
||||
remittance_info = etree.SubElement(parent_node, "RmtInf")
|
||||
if line.communication_type == "normal":
|
||||
communication_type = line.payment_line_ids[:1].communication_type
|
||||
if communication_type == "normal":
|
||||
remittance_info_unstructured = etree.SubElement(remittance_info, "Ustrd")
|
||||
remittance_info_unstructured.text = self._prepare_field(
|
||||
"Remittance Unstructured Information",
|
||||
"line.communication",
|
||||
"line.payment_reference",
|
||||
{"line": line},
|
||||
140,
|
||||
gen_args=gen_args,
|
||||
@@ -636,7 +637,7 @@ class AccountPaymentOrder(models.Model):
|
||||
creditor_ref_info_type_issuer = etree.SubElement(
|
||||
creditor_ref_info_type, "Issr"
|
||||
)
|
||||
creditor_ref_info_type_issuer.text = line.communication_type
|
||||
creditor_ref_info_type_issuer.text = communication_type
|
||||
creditor_reference = etree.SubElement(
|
||||
creditor_ref_information, "CdtrRef"
|
||||
)
|
||||
@@ -655,13 +656,13 @@ class AccountPaymentOrder(models.Model):
|
||||
creditor_ref_info_type_issuer = etree.SubElement(
|
||||
creditor_ref_info_type, "Issr"
|
||||
)
|
||||
creditor_ref_info_type_issuer.text = line.communication_type
|
||||
creditor_ref_info_type_issuer.text = communication_type
|
||||
|
||||
creditor_reference = etree.SubElement(creditor_ref_information, "Ref")
|
||||
|
||||
creditor_reference.text = self._prepare_field(
|
||||
"Creditor Structured Reference",
|
||||
"line.communication",
|
||||
"line.payment_reference",
|
||||
{"line": line},
|
||||
35,
|
||||
gen_args=gen_args,
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
# Copyright 2013-2016 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
# Copyright 2021 Tecnativa - Carlos Roca
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class BankPaymentLine(models.Model):
|
||||
_inherit = "bank.payment.line"
|
||||
|
||||
priority = fields.Selection(related="payment_line_ids.priority", string="Priority")
|
||||
local_instrument = fields.Selection(
|
||||
related="payment_line_ids.local_instrument", string="Local Instrument"
|
||||
)
|
||||
category_purpose = fields.Selection(
|
||||
related="payment_line_ids.category_purpose", string="Category Purpose"
|
||||
)
|
||||
purpose = fields.Selection(related="payment_line_ids.purpose")
|
||||
|
||||
@api.model
|
||||
def same_fields_payment_line_and_bank_payment_line(self):
|
||||
res = super().same_fields_payment_line_and_bank_payment_line()
|
||||
res += ["priority", "local_instrument", "category_purpose", "purpose"]
|
||||
return res
|
||||
@@ -1,31 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!--
|
||||
© 2015-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
|
||||
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
-->
|
||||
<odoo>
|
||||
<record id="bank_payment_line_form" model="ir.ui.view">
|
||||
<field name="name">pain.base.bank.payment.line.form</field>
|
||||
<field name="model">bank.payment.line</field>
|
||||
<field name="inherit_id" ref="account_payment_order.bank_payment_line_form" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="partner_bank_id" position="after">
|
||||
<field name="priority" />
|
||||
<field name="local_instrument" />
|
||||
<field name="category_purpose" />
|
||||
<field name="purpose" />
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
<record id="bank_payment_line_tree" model="ir.ui.view">
|
||||
<field name="name">pain.base.bank.payment.line.tree</field>
|
||||
<field name="model">bank.payment.line</field>
|
||||
<field name="inherit_id" ref="account_payment_order.bank_payment_line_tree" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="communication" position="after">
|
||||
<field name="priority" optional="hide" />
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -1,5 +1,6 @@
|
||||
# Copyright 2010-2020 Akretion (www.akretion.com)
|
||||
# Copyright 2016-2020 Tecnativa - Antonio Espinosa and Pedro M. Baeza
|
||||
# Copyright 2016 Tecnativa - Antonio Espinosa
|
||||
# Copyright 2016-2022 Tecnativa - Pedro M. Baeza
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html)
|
||||
|
||||
{
|
||||
@@ -7,7 +8,7 @@
|
||||
"summary": "Create SEPA XML files for Credit Transfers",
|
||||
"version": "14.0.1.2.1",
|
||||
"license": "AGPL-3",
|
||||
"author": "Akretion, " "Tecnativa, " "Odoo Community Association (OCA)",
|
||||
"author": "Akretion, Tecnativa, Odoo Community Association (OCA)",
|
||||
"website": "https://github.com/OCA/bank-payment",
|
||||
"category": "Banking addons",
|
||||
"conflicts": ["account_sepa"],
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Copyright 2010-2020 Akretion (www.akretion.com)
|
||||
# Copyright 2014-2020 Tecnativa - Pedro M. Baeza
|
||||
# Copyright 2014-2022 Tecnativa - Pedro M. Baeza
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from lxml import etree
|
||||
@@ -78,10 +78,11 @@ class AccountPaymentOrder(models.Model):
|
||||
lines_per_group = {}
|
||||
# key = (requested_date, priority, local_instrument, categ_purpose)
|
||||
# values = list of lines as object
|
||||
for line in self.bank_line_ids:
|
||||
priority = line.priority
|
||||
local_instrument = line.local_instrument
|
||||
categ_purpose = line.category_purpose
|
||||
for line in self.payment_ids:
|
||||
payment_line = line.payment_line_ids[:1]
|
||||
priority = payment_line.priority
|
||||
local_instrument = payment_line.local_instrument
|
||||
categ_purpose = payment_line.category_purpose
|
||||
# The field line.date is the requested payment date
|
||||
# taking into account the 'date_prefered' setting
|
||||
# cf account_banking_payment_export/models/account_payment.py
|
||||
@@ -145,7 +146,7 @@ class AccountPaymentOrder(models.Model):
|
||||
)
|
||||
instruction_identification.text = self._prepare_field(
|
||||
"Instruction Identification",
|
||||
"line.name",
|
||||
"str(line.move_id.id)",
|
||||
{"line": line},
|
||||
35,
|
||||
gen_args=gen_args,
|
||||
@@ -155,7 +156,7 @@ class AccountPaymentOrder(models.Model):
|
||||
)
|
||||
end2end_identification.text = self._prepare_field(
|
||||
"End to End Identification",
|
||||
"line.name",
|
||||
"str(line.move_id.id)",
|
||||
{"line": line},
|
||||
35,
|
||||
gen_args=gen_args,
|
||||
@@ -171,9 +172,9 @@ class AccountPaymentOrder(models.Model):
|
||||
instructed_amount = etree.SubElement(
|
||||
amount, "InstdAmt", Ccy=currency_name
|
||||
)
|
||||
instructed_amount.text = "%.2f" % line.amount_currency
|
||||
amount_control_sum_a += line.amount_currency
|
||||
amount_control_sum_b += line.amount_currency
|
||||
instructed_amount.text = "%.2f" % line.amount
|
||||
amount_control_sum_a += line.amount
|
||||
amount_control_sum_b += line.amount
|
||||
if not line.partner_bank_id:
|
||||
raise UserError(
|
||||
_(
|
||||
@@ -190,9 +191,10 @@ class AccountPaymentOrder(models.Model):
|
||||
gen_args,
|
||||
line,
|
||||
)
|
||||
if line.purpose:
|
||||
line_purpose = line.payment_line_ids[:1].purpose
|
||||
if line_purpose:
|
||||
purpose = etree.SubElement(credit_transfer_transaction_info, "Purp")
|
||||
etree.SubElement(purpose, "Cd").text = line.purpose
|
||||
etree.SubElement(purpose, "Cd").text = line_purpose
|
||||
self.generate_remittance_info_block(
|
||||
credit_transfer_transaction_info, line, gen_args
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Copyright 2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
|
||||
# Copyright 2018 Tecnativa - Pedro M. Baeza
|
||||
# Copyright 2020 Sygel Technology - Valentin Vinagre
|
||||
# Copyright 2018-2022 Tecnativa - Pedro M. Baeza
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
import base64
|
||||
@@ -21,7 +21,6 @@ class TestSCT(SavepointCase):
|
||||
cls.journal_model = cls.env["account.journal"]
|
||||
cls.payment_order_model = cls.env["account.payment.order"]
|
||||
cls.payment_line_model = cls.env["account.payment.line"]
|
||||
cls.bank_line_model = cls.env["bank.payment.line"]
|
||||
cls.partner_bank_model = cls.env["res.partner.bank"]
|
||||
cls.attachment_model = cls.env["ir.attachment"]
|
||||
cls.invoice_model = cls.env["account.move"]
|
||||
@@ -194,20 +193,16 @@ class TestSCT(SavepointCase):
|
||||
self.payment_order.draft2open()
|
||||
self.assertEqual(self.payment_order.state, "open")
|
||||
self.assertEqual(self.payment_order.sepa, True)
|
||||
bank_lines = self.bank_line_model.search(
|
||||
[("partner_id", "=", self.partner_agrolait.id)]
|
||||
)
|
||||
self.assertEqual(len(bank_lines), 1)
|
||||
agrolait_bank_line = bank_lines[0]
|
||||
self.assertTrue(self.payment_order.payment_ids)
|
||||
agrolait_bank_line = self.payment_order.payment_ids[0]
|
||||
self.assertEqual(agrolait_bank_line.currency_id, self.eur_currency)
|
||||
self.assertEqual(
|
||||
agrolait_bank_line.currency_id.compare_amounts(
|
||||
agrolait_bank_line.amount_currency, 49.0
|
||||
agrolait_bank_line.amount, 49.0
|
||||
),
|
||||
0,
|
||||
)
|
||||
self.assertEqual(agrolait_bank_line.communication_type, "normal")
|
||||
self.assertEqual(agrolait_bank_line.communication, "F1341-F1342-A1301")
|
||||
self.assertEqual(agrolait_bank_line.payment_reference, "F1341-F1342-A1301")
|
||||
self.assertEqual(agrolait_bank_line.partner_bank_id, invoice1.partner_bank_id)
|
||||
|
||||
action = self.payment_order.open2generated()
|
||||
@@ -286,20 +281,14 @@ class TestSCT(SavepointCase):
|
||||
self.payment_order.draft2open()
|
||||
self.assertEqual(self.payment_order.state, "open")
|
||||
self.assertEqual(self.payment_order.sepa, False)
|
||||
bank_lines = self.bank_line_model.search(
|
||||
[("partner_id", "=", self.partner_asus.id)]
|
||||
)
|
||||
self.assertEqual(len(bank_lines), 1)
|
||||
asus_bank_line = bank_lines[0]
|
||||
self.assertEqual(self.payment_order.payment_count, 1)
|
||||
asus_bank_line = self.payment_order.payment_ids[0]
|
||||
self.assertEqual(asus_bank_line.currency_id, self.usd_currency)
|
||||
self.assertEqual(
|
||||
asus_bank_line.currency_id.compare_amounts(
|
||||
asus_bank_line.amount_currency, 3054.0
|
||||
),
|
||||
asus_bank_line.currency_id.compare_amounts(asus_bank_line.amount, 3054.0),
|
||||
0,
|
||||
)
|
||||
self.assertEqual(asus_bank_line.communication_type, "normal")
|
||||
self.assertEqual(asus_bank_line.communication, "Inv9032-Inv9033")
|
||||
self.assertEqual(asus_bank_line.payment_reference, "Inv9032-Inv9033")
|
||||
self.assertEqual(asus_bank_line.partner_bank_id, invoice1.partner_bank_id)
|
||||
|
||||
action = self.payment_order.open2generated()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# Copyright 2013-2020 Akretion (www.akretion.com)
|
||||
# Copyright 2014-2020 Tecnativa - Pedro M. Baeza & Antonio Espinosa
|
||||
# Copyright 2016 Tecnativa - Antonio Espinosa
|
||||
# Copyright 2014-2022 Tecnativa - Pedro M. Baeza
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
{
|
||||
@@ -7,7 +8,7 @@
|
||||
"summary": "Create SEPA files for Direct Debit",
|
||||
"version": "14.0.1.3.3",
|
||||
"license": "AGPL-3",
|
||||
"author": "Akretion, " "Tecnativa, " "Odoo Community Association (OCA)",
|
||||
"author": "Akretion, Tecnativa, Odoo Community Association (OCA)",
|
||||
"website": "https://github.com/OCA/bank-payment",
|
||||
"category": "Banking addons",
|
||||
"depends": ["account_banking_pain_base", "account_banking_mandate"],
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from . import res_company
|
||||
from . import account_banking_mandate
|
||||
from . import bank_payment_line
|
||||
from . import account_payment_mode
|
||||
from . import account_payment_method
|
||||
from . import account_payment_order
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Copyright 2020 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
|
||||
# Copyright 2018 Tecnativa - Pedro M. Baeza
|
||||
# Copyright 2018-2022 Tecnativa - Pedro M. Baeza
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from lxml import etree
|
||||
@@ -72,16 +72,17 @@ class AccountPaymentOrder(models.Model):
|
||||
lines_per_group = {}
|
||||
# key = (requested_date, priority, sequence type)
|
||||
# value = list of lines as objects
|
||||
for line in self.bank_line_ids:
|
||||
for line in self.payment_ids:
|
||||
transactions_count_a += 1
|
||||
priority = line.priority
|
||||
categ_purpose = line.category_purpose
|
||||
scheme = line.mandate_id.scheme
|
||||
if line.mandate_id.type == "oneoff":
|
||||
payment_line = line.payment_line_ids[:1]
|
||||
priority = payment_line.priority
|
||||
categ_purpose = payment_line.category_purpose
|
||||
scheme = payment_line.mandate_id.scheme
|
||||
if payment_line.mandate_id.type == "oneoff":
|
||||
seq_type = "OOFF"
|
||||
elif line.mandate_id.type == "recurrent":
|
||||
elif payment_line.mandate_id.type == "recurrent":
|
||||
seq_type_map = {"recurring": "RCUR", "first": "FRST", "final": "FNAL"}
|
||||
seq_type_label = line.mandate_id.recurrent_sequence_type
|
||||
seq_type_label = payment_line.mandate_id.recurrent_sequence_type
|
||||
assert seq_type_label is not False
|
||||
seq_type = seq_type_map[seq_type_label]
|
||||
else:
|
||||
@@ -90,7 +91,7 @@ class AccountPaymentOrder(models.Model):
|
||||
"Invalid mandate type in '%s'. Valid ones are 'Recurrent' "
|
||||
"or 'One-Off'"
|
||||
)
|
||||
% line.mandate_id.unique_mandate_reference
|
||||
% payment_line.mandate_id.unique_mandate_reference
|
||||
)
|
||||
# The field line.date is the requested payment date
|
||||
# taking into account the 'date_preferred' setting
|
||||
@@ -165,7 +166,7 @@ class AccountPaymentOrder(models.Model):
|
||||
)
|
||||
instruction_identification.text = self._prepare_field(
|
||||
"Instruction Identification",
|
||||
"line.name",
|
||||
"str(line.move_id.id)",
|
||||
{"line": line},
|
||||
35,
|
||||
gen_args=gen_args,
|
||||
@@ -175,7 +176,7 @@ class AccountPaymentOrder(models.Model):
|
||||
)
|
||||
end2end_identification.text = self._prepare_field(
|
||||
"End to End Identification",
|
||||
"line.name",
|
||||
"str(line.move_id.id)",
|
||||
{"line": line},
|
||||
35,
|
||||
gen_args=gen_args,
|
||||
@@ -190,18 +191,19 @@ class AccountPaymentOrder(models.Model):
|
||||
instructed_amount = etree.SubElement(
|
||||
dd_transaction_info, "InstdAmt", Ccy=currency_name
|
||||
)
|
||||
instructed_amount.text = "%.2f" % line.amount_currency
|
||||
amount_control_sum_a += line.amount_currency
|
||||
amount_control_sum_b += line.amount_currency
|
||||
instructed_amount.text = "%.2f" % line.amount
|
||||
amount_control_sum_a += line.amount
|
||||
amount_control_sum_b += line.amount
|
||||
dd_transaction = etree.SubElement(dd_transaction_info, "DrctDbtTx")
|
||||
mandate_related_info = etree.SubElement(dd_transaction, "MndtRltdInf")
|
||||
mandate_identification = etree.SubElement(
|
||||
mandate_related_info, "MndtId"
|
||||
)
|
||||
mandate = line.payment_line_ids[:1].mandate_id
|
||||
mandate_identification.text = self._prepare_field(
|
||||
"Unique Mandate Reference",
|
||||
"line.mandate_id.unique_mandate_reference",
|
||||
{"line": line},
|
||||
"mandate.unique_mandate_reference",
|
||||
{"mandate": mandate},
|
||||
35,
|
||||
gen_args=gen_args,
|
||||
)
|
||||
@@ -211,16 +213,11 @@ class AccountPaymentOrder(models.Model):
|
||||
mandate_signature_date.text = self._prepare_field(
|
||||
"Mandate Signature Date",
|
||||
"signature_date",
|
||||
{
|
||||
"line": line,
|
||||
"signature_date": fields.Date.to_string(
|
||||
line.mandate_id.signature_date
|
||||
),
|
||||
},
|
||||
{"signature_date": fields.Date.to_string(mandate.signature_date)},
|
||||
10,
|
||||
gen_args=gen_args,
|
||||
)
|
||||
if sequence_type == "FRST" and line.mandate_id.last_debit_date:
|
||||
if sequence_type == "FRST" and mandate.last_debit_date:
|
||||
amendment_indicator = etree.SubElement(
|
||||
mandate_related_info, "AmdmntInd"
|
||||
)
|
||||
@@ -252,10 +249,10 @@ class AccountPaymentOrder(models.Model):
|
||||
gen_args,
|
||||
line,
|
||||
)
|
||||
|
||||
if line.purpose:
|
||||
line_purpose = line.payment_line_ids[:1].purpose
|
||||
if line_purpose:
|
||||
purpose = etree.SubElement(dd_transaction_info, "Purp")
|
||||
etree.SubElement(purpose, "Cd").text = line.purpose
|
||||
etree.SubElement(purpose, "Cd").text = line_purpose
|
||||
|
||||
self.generate_remittance_info_block(dd_transaction_info, line, gen_args)
|
||||
|
||||
@@ -281,18 +278,19 @@ class AccountPaymentOrder(models.Model):
|
||||
to_expire_mandates = abmo.browse([])
|
||||
first_mandates = abmo.browse([])
|
||||
all_mandates = abmo.browse([])
|
||||
for bline in order.bank_line_ids:
|
||||
if bline.mandate_id in all_mandates:
|
||||
for payment in order.payment_ids:
|
||||
mandate = payment.payment_line_ids.mandate_id
|
||||
if mandate in all_mandates:
|
||||
continue
|
||||
all_mandates += bline.mandate_id
|
||||
if bline.mandate_id.type == "oneoff":
|
||||
to_expire_mandates += bline.mandate_id
|
||||
elif bline.mandate_id.type == "recurrent":
|
||||
seq_type = bline.mandate_id.recurrent_sequence_type
|
||||
all_mandates += mandate
|
||||
if mandate.type == "oneoff":
|
||||
to_expire_mandates += mandate
|
||||
elif mandate.type == "recurrent":
|
||||
seq_type = mandate.recurrent_sequence_type
|
||||
if seq_type == "final":
|
||||
to_expire_mandates += bline.mandate_id
|
||||
to_expire_mandates += mandate
|
||||
elif seq_type == "first":
|
||||
first_mandates += bline.mandate_id
|
||||
first_mandates += mandate
|
||||
all_mandates.write({"last_debit_date": order.date_generated})
|
||||
to_expire_mandates.write({"state": "expired"})
|
||||
first_mandates.write({"recurrent_sequence_type": "recurring"})
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
# Copyright 2020 Akretion - Alexis de Lattre
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from odoo import models
|
||||
|
||||
|
||||
class BankPaymentLine(models.Model):
|
||||
_inherit = "bank.payment.line"
|
||||
|
||||
def move_line_offsetting_account_hashcode(self):
|
||||
"""
|
||||
From my experience, even when you ask several direct debits
|
||||
at the same date with enough delay, you will have several credits
|
||||
on your bank statement: one for each mandate types.
|
||||
So we split the transfer move lines by mandate type, so easier
|
||||
reconciliation of the bank statement.
|
||||
"""
|
||||
hashcode = super().move_line_offsetting_account_hashcode()
|
||||
hashcode += "-" + str(self.mandate_id.recurrent_sequence_type)
|
||||
return hashcode
|
||||
@@ -1,5 +1,5 @@
|
||||
# Copyright 2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
|
||||
# Copyright 2018-2020 Tecnativa - Pedro M. Baeza
|
||||
# Copyright 2018-2022 Tecnativa - Pedro M. Baeza
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
import base64
|
||||
@@ -42,7 +42,6 @@ class TestSDDBase(SavepointCase):
|
||||
cls.payment_order_model = cls.env["account.payment.order"]
|
||||
cls.payment_line_model = cls.env["account.payment.line"]
|
||||
cls.mandate_model = cls.env["account.banking.mandate"]
|
||||
cls.bank_line_model = cls.env["bank.payment.line"]
|
||||
cls.partner_bank_model = cls.env["res.partner.bank"]
|
||||
cls.attachment_model = cls.env["ir.attachment"]
|
||||
cls.invoice_model = cls.env["account.move"]
|
||||
@@ -255,22 +254,14 @@ class TestSDDBase(SavepointCase):
|
||||
payment_order.draft2open()
|
||||
self.assertEqual(payment_order.state, "open")
|
||||
self.assertEqual(payment_order.sepa, True)
|
||||
# Check bank payment line
|
||||
bank_lines = self.bank_line_model.search(
|
||||
[("partner_id", "=", self.partner_agrolait.id)]
|
||||
)
|
||||
self.assertEqual(len(bank_lines), 1)
|
||||
agrolait_bank_line = bank_lines[0]
|
||||
# Check account payment
|
||||
agrolait_bank_line = payment_order.payment_ids[0]
|
||||
self.assertEqual(agrolait_bank_line.currency_id, self.eur_currency)
|
||||
self.assertEqual(
|
||||
float_compare(
|
||||
agrolait_bank_line.amount_currency, 42.0, precision_digits=accpre
|
||||
),
|
||||
float_compare(agrolait_bank_line.amount, 42.0, precision_digits=accpre),
|
||||
0,
|
||||
)
|
||||
self.assertEqual(agrolait_bank_line.communication_type, "normal")
|
||||
self.assertEqual(agrolait_bank_line.communication, invoice1.name)
|
||||
self.assertEqual(agrolait_bank_line.mandate_id, invoice1.mandate_id)
|
||||
self.assertEqual(agrolait_bank_line.payment_reference, invoice1.name)
|
||||
self.assertEqual(
|
||||
agrolait_bank_line.partner_bank_id, invoice1.mandate_id.partner_bank_id
|
||||
)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# © 2009 EduSense BV (<http://www.edusense.nl>)
|
||||
# © 2011-2013 Therp BV (<https://therp.nl>)
|
||||
# © 2013-2014 ACSONE SA (<https://acsone.eu>).
|
||||
# © 2014-2016 Tecnativa - Pedro M. Baeza
|
||||
# © 2016 Akretion (<https://www.akretion.com>).
|
||||
# © 2016 Aselcis (<https://www.aselcis.com>).
|
||||
# © 2014-2022 Tecnativa - Pedro M. Baeza
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
{
|
||||
@@ -29,7 +29,6 @@
|
||||
"views/account_payment_mode.xml",
|
||||
"views/account_payment_order.xml",
|
||||
"views/account_payment_line.xml",
|
||||
"views/bank_payment_line.xml",
|
||||
"views/account_move_line.xml",
|
||||
"views/ir_attachment.xml",
|
||||
"views/account_invoice_view.xml",
|
||||
|
||||
@@ -1,18 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!--
|
||||
© 2015-2016 Akretion (https://www.akretion.com/)
|
||||
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
Copyright 2019 Tecnativa - Pedro M. Baeza
|
||||
Copyright 2015-2016 Akretion - Alexis de Lattre
|
||||
Copyright 2019-2022 Tecnativa - Pedro M. Baeza
|
||||
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
-->
|
||||
<odoo noupdate="1">
|
||||
<record id="bank_payment_line_seq" model="ir.sequence">
|
||||
<field name="name">Bank Payment Line</field>
|
||||
<field name="code">bank.payment.line</field>
|
||||
<field name="prefix">L</field>
|
||||
<field name="padding">5</field>
|
||||
<field name="company_id" eval="False" />
|
||||
</record>
|
||||
<record id="account_payment_line_seq" model="ir.sequence">
|
||||
<field name="name">Payment Line</field>
|
||||
<field name="code">account.payment.line</field>
|
||||
|
||||
174
account_payment_order/migrations/14.0.2.0.0/post-migration.py
Normal file
174
account_payment_order/migrations/14.0.2.0.0/post-migration.py
Normal file
@@ -0,0 +1,174 @@
|
||||
# Copyright 2022 Tecnativa - Pedro M. Baeza
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
import logging
|
||||
|
||||
from openupgradelib import openupgrade
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _insert_account_payments(env):
|
||||
openupgrade.logged_query(
|
||||
env.cr, "ALTER TABLE account_payment ADD old_bank_payment_line_id INT4"
|
||||
)
|
||||
# Create an account.payment record for each bank.payment.line
|
||||
openupgrade.logged_query(
|
||||
env.cr,
|
||||
"""
|
||||
INSERT INTO account_payment (
|
||||
create_date, create_uid, write_date, write_uid, old_bank_payment_line_name,
|
||||
payment_order_id, partner_id, amount, currency_id,
|
||||
payment_method_id, old_bank_payment_line_id, payment_type,
|
||||
partner_type,
|
||||
destination_account_id, payment_reference, move_id
|
||||
)
|
||||
SELECT
|
||||
bpl.create_date, bpl.create_uid, bpl.write_date, bpl.write_uid, bpl.name,
|
||||
bpl.order_id, bpl.partner_id, bpl.amount_currency, 1,
|
||||
apm.payment_method_id, bpl.id, apo.payment_type,
|
||||
CASE WHEN apo.payment_type = 'inbound' THEN 'customer' ELSE 'supplier' END,
|
||||
aml.account_id, bpl.communication, aml.move_id
|
||||
FROM bank_payment_line bpl
|
||||
JOIN account_payment_order apo ON apo.id = bpl.order_id
|
||||
JOIN account_payment_mode apm ON apm.id = apo.payment_mode_id
|
||||
LEFT JOIN account_move_line aml ON aml.bank_payment_line_id = bpl.id
|
||||
""",
|
||||
)
|
||||
# As the information is asymmetric: N payment lines > 1 bank payment line, but there
|
||||
# are some related non-stored fields to payment lines, we need a second query to
|
||||
# update some of the fields
|
||||
openupgrade.logged_query(
|
||||
env.cr,
|
||||
"""
|
||||
UPDATE account_payment ap
|
||||
SET currency_id = apl.currency_id,
|
||||
partner_bank_id = apl.partner_bank_id
|
||||
FROM account_payment_line apl
|
||||
WHERE apl.bank_line_id = ap.old_bank_payment_line_id
|
||||
""",
|
||||
)
|
||||
|
||||
|
||||
def _create_hooks(env):
|
||||
"""Avoid errors due to locked dates, overriding involved methods."""
|
||||
|
||||
def _check_fiscalyear_lock_date(self):
|
||||
return True
|
||||
|
||||
def _check_tax_lock_date(self):
|
||||
return True
|
||||
|
||||
def _check_reconciliation(self):
|
||||
return True
|
||||
|
||||
# create hooks
|
||||
_check_fiscalyear_lock_date._original_method = type(
|
||||
env["account.move"]
|
||||
)._check_fiscalyear_lock_date
|
||||
type(env["account.move"])._check_fiscalyear_lock_date = _check_fiscalyear_lock_date
|
||||
_check_tax_lock_date._original_method = type(
|
||||
env["account.move.line"]
|
||||
)._check_tax_lock_date
|
||||
type(env["account.move.line"])._check_tax_lock_date = _check_tax_lock_date
|
||||
_check_reconciliation._original_method = type(
|
||||
env["account.move.line"]
|
||||
)._check_reconciliation
|
||||
type(env["account.move.line"])._check_reconciliation = _check_reconciliation
|
||||
|
||||
|
||||
def create_moves_from_orphan_account_payments(env):
|
||||
"""Recreate missing journal entries on the newly created account payments."""
|
||||
env.cr.execute(
|
||||
"""
|
||||
SELECT ap.id, MIN(apl.date), MIN(bpl.company_id), MIN(apo.name),
|
||||
MIN(apo.journal_id), MIN(apl.currency_id), MIN(apo.state), MIN(apo.id)
|
||||
FROM bank_payment_line bpl
|
||||
JOIN account_payment ap ON ap.old_bank_payment_line_id = bpl.id
|
||||
JOIN account_payment_order apo ON apo.id = bpl.order_id
|
||||
JOIN account_payment_line apl ON apl.bank_line_id = bpl.id
|
||||
LEFT JOIN account_move_line aml ON aml.bank_payment_line_id = bpl.id
|
||||
WHERE aml.move_id IS NULL
|
||||
GROUP BY ap.id
|
||||
"""
|
||||
)
|
||||
deprecated_acc_by_company = {}
|
||||
for row in env.cr.fetchall():
|
||||
payment = (
|
||||
env["account.payment"]
|
||||
.with_context(
|
||||
check_move_validity=False,
|
||||
tracking_disable=True,
|
||||
)
|
||||
.browse(row[0])
|
||||
)
|
||||
move = env["account.move"].create(
|
||||
{
|
||||
"name": "/",
|
||||
"date": row[1],
|
||||
"payment_id": payment.id,
|
||||
"move_type": "entry",
|
||||
"company_id": row[2],
|
||||
"ref": row[3],
|
||||
"journal_id": row[4],
|
||||
"currency_id": row[5],
|
||||
"state": "draft" if row[6] in {"open", "generated"} else "cancel",
|
||||
"payment_order_id": row[7],
|
||||
}
|
||||
)
|
||||
payment.move_id = move
|
||||
# Avoid deprecated account warning
|
||||
if payment.company_id not in deprecated_acc_by_company:
|
||||
deprecated_accounts = env["account.account"].search(
|
||||
[("deprecated", "=", True), ("company_id", "=", payment.company_id.id)]
|
||||
)
|
||||
deprecated_acc_by_company[payment.company_id] = deprecated_accounts
|
||||
deprecated_accounts.deprecated = False
|
||||
try:
|
||||
payment._synchronize_to_moves(["date"]) # no more changed fields needed
|
||||
except Exception as e:
|
||||
_logger.error("Failed for payment with id %s: %s", payment.id, e)
|
||||
raise
|
||||
# Restore deprecated accounts
|
||||
for deprecated_accounts in deprecated_acc_by_company.values():
|
||||
deprecated_accounts.deprecated = True
|
||||
|
||||
|
||||
def _delete_hooks(env):
|
||||
"""Restore the locking dates original methods."""
|
||||
type(env["account.move"])._check_fiscalyear_lock_date = type(
|
||||
env["account.move"]
|
||||
)._check_fiscalyear_lock_date._original_method
|
||||
type(env["account.move.line"])._check_tax_lock_date = type(
|
||||
env["account.move.line"]
|
||||
)._check_tax_lock_date._original_method
|
||||
type(env["account.move.line"])._check_reconciliation = type(
|
||||
env["account.move.line"]
|
||||
)._check_reconciliation._original_method
|
||||
|
||||
|
||||
def _insert_payment_line_payment_link(env):
|
||||
openupgrade.logged_query(
|
||||
env.cr,
|
||||
"""
|
||||
INSERT INTO account_payment_account_payment_line_rel
|
||||
(account_payment_id, account_payment_line_id)
|
||||
SELECT ap.id, apl.id
|
||||
FROM account_payment_line apl
|
||||
JOIN account_payment ap ON ap.old_bank_payment_line_id = apl.bank_line_id
|
||||
""",
|
||||
)
|
||||
|
||||
|
||||
@openupgrade.migrate()
|
||||
def migrate(env, version):
|
||||
openupgrade.logged_query(
|
||||
env.cr, "ALTER TABLE account_payment ALTER move_id DROP NOT NULL"
|
||||
)
|
||||
_insert_account_payments(env)
|
||||
_create_hooks(env)
|
||||
create_moves_from_orphan_account_payments(env)
|
||||
openupgrade.logged_query(
|
||||
env.cr, "ALTER TABLE account_payment ALTER move_id SET NOT NULL"
|
||||
)
|
||||
_delete_hooks(env)
|
||||
_insert_payment_line_payment_link(env)
|
||||
@@ -1,7 +1,6 @@
|
||||
from . import account_payment_mode
|
||||
from . import account_payment_order
|
||||
from . import account_payment_line
|
||||
from . import bank_payment_line
|
||||
from . import account_move
|
||||
from . import account_move_line
|
||||
from . import res_bank
|
||||
|
||||
@@ -18,12 +18,6 @@ class AccountMoveLine(models.Model):
|
||||
help="Bank account on which we should pay the supplier",
|
||||
check_company=True,
|
||||
)
|
||||
bank_payment_line_id = fields.Many2one(
|
||||
comodel_name="bank.payment.line",
|
||||
readonly=True,
|
||||
index=True,
|
||||
check_company=True,
|
||||
)
|
||||
payment_line_ids = fields.One2many(
|
||||
comodel_name="account.payment.line",
|
||||
inverse_name="move_line_id",
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
# Copyright 2019 ACSONE SA/NV
|
||||
# Copyright 2022 Tecnativa - Pedro M. Baeza
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import api, models
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class AccountPayment(models.Model):
|
||||
_inherit = "account.payment"
|
||||
|
||||
payment_order_id = fields.Many2one(comodel_name="account.payment.order")
|
||||
payment_line_ids = fields.Many2many(comodel_name="account.payment.line")
|
||||
# Compatibility with previous approach for returns - To be removed on v16
|
||||
old_bank_payment_line_name = fields.Char()
|
||||
|
||||
def _get_default_journal(self):
|
||||
res = super()._get_default_journal()
|
||||
return res.filtered(lambda journal: not journal.inbound_payment_order_only)
|
||||
|
||||
@@ -77,19 +77,17 @@ class AccountPaymentLine(models.Model):
|
||||
communication_type = fields.Selection(
|
||||
selection=[("normal", "Free")], required=True, default="normal"
|
||||
)
|
||||
bank_line_id = fields.Many2one(
|
||||
comodel_name="bank.payment.line",
|
||||
string="Bank Payment Line",
|
||||
payment_ids = fields.Many2many(
|
||||
comodel_name="account.payment",
|
||||
string="Payment transaction",
|
||||
readonly=True,
|
||||
index=True,
|
||||
check_company=True,
|
||||
)
|
||||
|
||||
_sql_constraints = [
|
||||
(
|
||||
"name_company_unique",
|
||||
"unique(name, company_id)",
|
||||
"A payment line already exists with this reference " "in the same company!",
|
||||
"A payment line already exists with this reference in the same company!",
|
||||
)
|
||||
]
|
||||
|
||||
@@ -114,11 +112,21 @@ class AccountPaymentLine(models.Model):
|
||||
else:
|
||||
line.amount_company_currency = 0
|
||||
|
||||
@api.model
|
||||
def _get_payment_line_grouping_fields(self):
|
||||
"""This list of fields is used o compute the grouping hashcode."""
|
||||
return [
|
||||
"currency_id",
|
||||
"partner_id",
|
||||
"partner_bank_id",
|
||||
"date",
|
||||
"communication_type",
|
||||
]
|
||||
|
||||
def payment_line_hashcode(self):
|
||||
self.ensure_one()
|
||||
bplo = self.env["bank.payment.line"]
|
||||
values = []
|
||||
for field in bplo.same_fields_payment_line_and_bank_payment_line():
|
||||
for field in self._get_payment_line_grouping_fields():
|
||||
values.append(str(self[field]))
|
||||
# Don't group the payment lines that are attached to the same supplier
|
||||
# but to move lines with different accounts (very unlikely),
|
||||
@@ -128,8 +136,7 @@ class AccountPaymentLine(models.Model):
|
||||
# otherwise it would break the structured communication system !
|
||||
if self.communication_type != "normal":
|
||||
values.append(str(self.id))
|
||||
hashcode = "-".join(values)
|
||||
return hashcode
|
||||
return "-".join(values)
|
||||
|
||||
@api.onchange("partner_id")
|
||||
def partner_id_change(self):
|
||||
@@ -168,3 +175,45 @@ class AccountPaymentLine(models.Model):
|
||||
)
|
||||
if not self.communication:
|
||||
raise UserError(_("Communication is empty on payment line %s.") % self.name)
|
||||
|
||||
def _prepare_account_payment_vals(self):
|
||||
"""Prepare the dictionary to create an account payment record from a set of
|
||||
payment lines.
|
||||
"""
|
||||
journal = self.order_id.journal_id
|
||||
vals = {
|
||||
"payment_type": self.order_id.payment_type,
|
||||
"partner_id": self.partner_id.id,
|
||||
"destination_account_id": self.move_line_id.account_id.id,
|
||||
"company_id": self.order_id.company_id.id,
|
||||
"amount": sum(self.mapped("amount_currency")),
|
||||
"date": self[:1].date,
|
||||
"currency_id": self.currency_id.id,
|
||||
"ref": self.order_id.name,
|
||||
"payment_reference": "-".join([line.communication for line in self]),
|
||||
"journal_id": journal.id,
|
||||
"partner_bank_id": self.partner_bank_id.id,
|
||||
"payment_order_id": self.order_id.id,
|
||||
"payment_method_id": self.order_id.payment_mode_id.payment_method_id.id,
|
||||
"payment_line_ids": [(6, 0, self.ids)],
|
||||
}
|
||||
# Determine partner_type
|
||||
move_type = self[:1].move_line_id.move_id.move_type
|
||||
if move_type in {"out_invoice", "out_refund"}:
|
||||
vals["partner_type"] = "customer"
|
||||
elif move_type in {"in_invoice", "in_refund"}:
|
||||
vals["partner_type"] = "supplier"
|
||||
else:
|
||||
p_type = "customer" if vals["payment_type"] == "inbound" else "supplier"
|
||||
vals["partner_type"] = p_type
|
||||
# Fill destination account if manual payment line with no linked journal item
|
||||
if not vals["destination_account_id"]:
|
||||
if vals["partner_type"] == "customer":
|
||||
vals[
|
||||
"destination_account_id"
|
||||
] = self.partner_id.property_account_receivable_id.id
|
||||
else:
|
||||
vals[
|
||||
"destination_account_id"
|
||||
] = self.partner_id.property_account_payable_id.id
|
||||
return vals
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# © 2009 EduSense BV (<http://www.edusense.nl>)
|
||||
# © 2011-2013 Therp BV (<https://therp.nl>)
|
||||
# © 2016 Serv. Tecnol. Avanzados - Pedro M. Baeza
|
||||
# © 2016 Akretion (Alexis de Lattre - alexis.delattre@akretion.com)
|
||||
# Copyright 2016-2022 Tecnativa - Pedro M. Baeza
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
import base64
|
||||
@@ -121,24 +121,19 @@ class AccountPaymentOrder(models.Model):
|
||||
readonly=True,
|
||||
states={"draft": [("readonly", False)]},
|
||||
)
|
||||
bank_line_ids = fields.One2many(
|
||||
comodel_name="bank.payment.line",
|
||||
inverse_name="order_id",
|
||||
string="Bank Transactions",
|
||||
payment_ids = fields.One2many(
|
||||
comodel_name="account.payment",
|
||||
inverse_name="payment_order_id",
|
||||
string="Payment Transactions",
|
||||
readonly=True,
|
||||
help="The bank payment lines are used to generate the payment file. "
|
||||
"They are automatically created from transaction lines upon "
|
||||
"confirmation of the payment order: one bank payment line can "
|
||||
"group several transaction lines if the option "
|
||||
"'Group Transactions in Payment Orders' is active on the payment "
|
||||
"mode.",
|
||||
)
|
||||
payment_count = fields.Integer(
|
||||
compute="_compute_payment_count",
|
||||
string="Number of Payment Transactions",
|
||||
)
|
||||
total_company_currency = fields.Monetary(
|
||||
compute="_compute_total", store=True, currency_field="company_currency_id"
|
||||
)
|
||||
bank_line_count = fields.Integer(
|
||||
compute="_compute_bank_line_count", string="Number of Bank Transactions"
|
||||
)
|
||||
move_ids = fields.One2many(
|
||||
comodel_name="account.move",
|
||||
inverse_name="payment_order_id",
|
||||
@@ -207,10 +202,10 @@ class AccountPaymentOrder(models.Model):
|
||||
rec.mapped("payment_line_ids.amount_company_currency") or [0.0]
|
||||
)
|
||||
|
||||
@api.depends("bank_line_ids")
|
||||
def _compute_bank_line_count(self):
|
||||
@api.depends("payment_ids")
|
||||
def _compute_payment_count(self):
|
||||
for order in self:
|
||||
order.bank_line_count = len(order.bank_line_ids)
|
||||
order.payment_count = len(order.payment_ids)
|
||||
|
||||
@api.depends("move_ids")
|
||||
def _compute_move_count(self):
|
||||
@@ -250,11 +245,6 @@ class AccountPaymentOrder(models.Model):
|
||||
self.date_prefered = self.payment_mode_id.default_date_prefered
|
||||
|
||||
def action_uploaded_cancel(self):
|
||||
for move in self.move_ids:
|
||||
move.button_cancel()
|
||||
for move_line in move.line_ids:
|
||||
move_line.remove_move_reconcile()
|
||||
move.with_context(force_delete=True).unlink()
|
||||
self.action_cancel()
|
||||
return True
|
||||
|
||||
@@ -263,27 +253,19 @@ class AccountPaymentOrder(models.Model):
|
||||
return True
|
||||
|
||||
def action_cancel(self):
|
||||
for order in self:
|
||||
order.write({"state": "cancel"})
|
||||
order.bank_line_ids.unlink()
|
||||
# Unreconcile and cancel payments
|
||||
self.payment_ids.action_draft()
|
||||
self.payment_ids.action_cancel()
|
||||
self.write({"state": "cancel"})
|
||||
return True
|
||||
|
||||
@api.model
|
||||
def _prepare_bank_payment_line(self, paylines):
|
||||
return {
|
||||
"order_id": paylines[0].order_id.id,
|
||||
"payment_line_ids": [(6, 0, paylines.ids)],
|
||||
"communication": "-".join([line.communication for line in paylines]),
|
||||
}
|
||||
|
||||
def draft2open(self):
|
||||
"""
|
||||
Called when you click on the 'Confirm' button
|
||||
Set the 'date' on payment line depending on the 'date_prefered'
|
||||
setting of the payment.order
|
||||
Re-generate the bank payment lines
|
||||
Re-generate the account payments.
|
||||
"""
|
||||
bplo = self.env["bank.payment.line"]
|
||||
today = fields.Date.context_today(self)
|
||||
for order in self:
|
||||
if not order.journal_id:
|
||||
@@ -302,9 +284,11 @@ class AccountPaymentOrder(models.Model):
|
||||
raise UserError(
|
||||
_("There are no transactions on payment order %s.") % order.name
|
||||
)
|
||||
# Delete existing bank payment lines
|
||||
order.bank_line_ids.unlink()
|
||||
# Create the bank payment lines from the payment lines
|
||||
# Unreconcile, cancel and delete existing account payments
|
||||
order.payment_ids.action_draft()
|
||||
order.payment_ids.action_cancel()
|
||||
order.payment_ids.unlink()
|
||||
# Prepare account payments from the payment lines
|
||||
group_paylines = {} # key = hashcode
|
||||
for payline in order.payment_line_ids:
|
||||
payline.draft2open_payment_line_check()
|
||||
@@ -361,7 +345,8 @@ class AccountPaymentOrder(models.Model):
|
||||
"total": payline.amount_currency,
|
||||
}
|
||||
order.recompute()
|
||||
# Create bank payment lines
|
||||
# Create account payments
|
||||
payment_vals = []
|
||||
for paydict in list(group_paylines.values()):
|
||||
# Block if a bank payment line is <= 0
|
||||
if paydict["total"] <= 0:
|
||||
@@ -369,8 +354,8 @@ class AccountPaymentOrder(models.Model):
|
||||
_("The amount for Partner '%s' is negative " "or null (%.2f) !")
|
||||
% (paydict["paylines"][0].partner_id.name, paydict["total"])
|
||||
)
|
||||
vals = self._prepare_bank_payment_line(paydict["paylines"])
|
||||
bplo.create(vals)
|
||||
payment_vals.append(paydict["paylines"]._prepare_account_payment_vals())
|
||||
self.env["account.payment"].create(payment_vals)
|
||||
self.write({"state": "open"})
|
||||
return True
|
||||
|
||||
@@ -422,165 +407,20 @@ class AccountPaymentOrder(models.Model):
|
||||
return action
|
||||
|
||||
def generated2uploaded(self):
|
||||
for order in self:
|
||||
if order.payment_mode_id.generate_move:
|
||||
order.generate_move()
|
||||
self.payment_ids.action_post()
|
||||
# Perform the reconciliation of payments and source journal items
|
||||
for payment in self.payment_ids:
|
||||
(
|
||||
payment.payment_line_ids.move_line_id
|
||||
+ payment.move_id.line_ids.filtered(
|
||||
lambda x: x.account_id == payment.destination_account_id
|
||||
)
|
||||
).reconcile()
|
||||
self.write(
|
||||
{"state": "uploaded", "date_uploaded": fields.Date.context_today(self)}
|
||||
)
|
||||
return True
|
||||
|
||||
def _prepare_move(self, bank_lines=None):
|
||||
if self.payment_type == "outbound":
|
||||
ref = _("Payment order %s") % self.name
|
||||
else:
|
||||
ref = _("Debit order %s") % self.name
|
||||
if bank_lines and len(bank_lines) == 1:
|
||||
ref += " - " + bank_lines.name
|
||||
vals = {
|
||||
"date": bank_lines[0].date,
|
||||
"journal_id": self.journal_id.id,
|
||||
"ref": ref,
|
||||
"payment_order_id": self.id,
|
||||
"line_ids": [],
|
||||
}
|
||||
total_company_currency = total_payment_currency = 0
|
||||
for bline in bank_lines:
|
||||
total_company_currency += bline.amount_company_currency
|
||||
total_payment_currency += bline.amount_currency
|
||||
partner_ml_vals = self._prepare_move_line_partner_account(bline)
|
||||
vals["line_ids"].append((0, 0, partner_ml_vals))
|
||||
trf_ml_vals = self._prepare_move_line_offsetting_account(
|
||||
total_company_currency, total_payment_currency, bank_lines
|
||||
)
|
||||
vals["line_ids"].append((0, 0, trf_ml_vals))
|
||||
return vals
|
||||
|
||||
def _prepare_move_line_offsetting_account(
|
||||
self, amount_company_currency, amount_payment_currency, bank_lines
|
||||
):
|
||||
vals = {}
|
||||
if self.payment_type == "outbound":
|
||||
name = _("Payment order %s") % self.name
|
||||
account_id = self.journal_id.payment_credit_account_id.id
|
||||
else:
|
||||
name = _("Debit order %s") % self.name
|
||||
account_id = self.journal_id.payment_debit_account_id.id
|
||||
|
||||
partner_id = False
|
||||
for index, bank_line in enumerate(bank_lines):
|
||||
if index == 0:
|
||||
partner_id = bank_line.payment_line_ids[0].partner_id.id
|
||||
elif bank_line.payment_line_ids[0].partner_id.id != partner_id:
|
||||
# we have different partners in the grouped move
|
||||
partner_id = False
|
||||
break
|
||||
vals.update(
|
||||
{
|
||||
"name": name,
|
||||
"partner_id": partner_id,
|
||||
"account_id": account_id,
|
||||
"credit": (
|
||||
self.payment_type == "outbound" and amount_company_currency or 0.0
|
||||
),
|
||||
"debit": (
|
||||
self.payment_type == "inbound" and amount_company_currency or 0.0
|
||||
),
|
||||
}
|
||||
)
|
||||
if bank_lines[0].currency_id != bank_lines[0].company_currency_id:
|
||||
sign = self.payment_type == "outbound" and -1 or 1
|
||||
vals.update(
|
||||
{
|
||||
"currency_id": bank_lines[0].currency_id.id,
|
||||
"amount_currency": amount_payment_currency * sign,
|
||||
}
|
||||
)
|
||||
return vals
|
||||
|
||||
def _prepare_move_line_partner_account(self, bank_line):
|
||||
if bank_line.payment_line_ids[0].move_line_id:
|
||||
account_id = bank_line.payment_line_ids[0].move_line_id.account_id.id
|
||||
else:
|
||||
if self.payment_type == "inbound":
|
||||
account_id = bank_line.partner_id.property_account_receivable_id.id
|
||||
else:
|
||||
account_id = bank_line.partner_id.property_account_payable_id.id
|
||||
if self.payment_type == "outbound":
|
||||
name = _("Payment bank line %s") % bank_line.name
|
||||
else:
|
||||
name = _("Debit bank line %s") % bank_line.name
|
||||
vals = {
|
||||
"name": name,
|
||||
"bank_payment_line_id": bank_line.id,
|
||||
"partner_id": bank_line.partner_id.id,
|
||||
"account_id": account_id,
|
||||
"credit": (
|
||||
self.payment_type == "inbound"
|
||||
and bank_line.amount_company_currency
|
||||
or 0.0
|
||||
),
|
||||
"debit": (
|
||||
self.payment_type == "outbound"
|
||||
and bank_line.amount_company_currency
|
||||
or 0.0
|
||||
),
|
||||
}
|
||||
|
||||
if bank_line.currency_id != bank_line.company_currency_id:
|
||||
sign = self.payment_type == "inbound" and -1 or 1
|
||||
vals.update(
|
||||
{
|
||||
"currency_id": bank_line.currency_id.id,
|
||||
"amount_currency": bank_line.amount_currency * sign,
|
||||
}
|
||||
)
|
||||
return vals
|
||||
|
||||
def _create_reconcile_move(self, hashcode, blines):
|
||||
self.ensure_one()
|
||||
post_move = self.payment_mode_id.post_move
|
||||
am_obj = self.env["account.move"]
|
||||
mvals = self._prepare_move(blines)
|
||||
move = am_obj.create(mvals)
|
||||
if post_move:
|
||||
move.action_post()
|
||||
blines.reconcile_payment_lines()
|
||||
|
||||
def _prepare_trf_moves(self):
|
||||
"""
|
||||
prepare a dict "trfmoves" that can be used when
|
||||
self.payment_mode_id.move_option = date or line
|
||||
key = unique identifier (date or True or line.id)
|
||||
value = bank_pay_lines (recordset that can have several entries)
|
||||
"""
|
||||
self.ensure_one()
|
||||
trfmoves = {}
|
||||
for bline in self.bank_line_ids:
|
||||
hashcode = bline.move_line_offsetting_account_hashcode()
|
||||
if hashcode in trfmoves:
|
||||
trfmoves[hashcode] += bline
|
||||
else:
|
||||
trfmoves[hashcode] = bline
|
||||
return trfmoves
|
||||
|
||||
def generate_move(self):
|
||||
"""
|
||||
Create the moves that pay off the move lines from
|
||||
the payment/debit order.
|
||||
"""
|
||||
self.ensure_one()
|
||||
trfmoves = self._prepare_trf_moves()
|
||||
for hashcode, blines in trfmoves.items():
|
||||
self._create_reconcile_move(hashcode, blines)
|
||||
|
||||
def action_bank_payment_line(self):
|
||||
self.ensure_one()
|
||||
action = self.env.ref("account_payment_order.bank_payment_line_action")
|
||||
action_dict = action.read()[0]
|
||||
action_dict["domain"] = [("id", "in", self.bank_line_ids.ids)]
|
||||
return action_dict
|
||||
|
||||
def action_move_journal_line(self):
|
||||
self.ensure_one()
|
||||
action = self.env.ref("account.action_move_journal_line").sudo().read()[0]
|
||||
|
||||
@@ -1,201 +0,0 @@
|
||||
# Copyright 2015-2016 Akretion - Alexis de Lattre
|
||||
# Copyright 2018 Tecnativa - Pedro M. Baeza
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class BankPaymentLine(models.Model):
|
||||
_name = "bank.payment.line"
|
||||
_description = "Bank Payment Lines"
|
||||
_check_company_auto = True
|
||||
|
||||
name = fields.Char(string="Bank Payment Line Ref", required=True, readonly=True)
|
||||
order_id = fields.Many2one(
|
||||
comodel_name="account.payment.order",
|
||||
ondelete="cascade",
|
||||
index=True,
|
||||
readonly=True,
|
||||
check_company=True,
|
||||
)
|
||||
payment_type = fields.Selection(
|
||||
related="order_id.payment_type", readonly=True, store=True
|
||||
)
|
||||
state = fields.Selection(related="order_id.state", readonly=True, store=True)
|
||||
payment_line_ids = fields.One2many(
|
||||
comodel_name="account.payment.line",
|
||||
inverse_name="bank_line_id",
|
||||
string="Payment Lines",
|
||||
readonly=True,
|
||||
)
|
||||
partner_id = fields.Many2one(
|
||||
comodel_name="res.partner",
|
||||
related="payment_line_ids.partner_id",
|
||||
readonly=True,
|
||||
store=True,
|
||||
check_company=True,
|
||||
) # store=True for groupby
|
||||
# Function Float fields are sometimes badly displayed in tree view,
|
||||
# see bug report https://github.com/odoo/odoo/issues/8632
|
||||
# But is it still true in v9 ?
|
||||
amount_currency = fields.Monetary(
|
||||
string="Amount",
|
||||
currency_field="currency_id",
|
||||
compute="_compute_amount",
|
||||
store=True,
|
||||
readonly=True,
|
||||
)
|
||||
amount_company_currency = fields.Monetary(
|
||||
string="Amount in Company Currency",
|
||||
currency_field="company_currency_id",
|
||||
compute="_compute_amount",
|
||||
store=True,
|
||||
readonly=True,
|
||||
)
|
||||
currency_id = fields.Many2one(
|
||||
comodel_name="res.currency",
|
||||
required=True,
|
||||
readonly=True,
|
||||
related="payment_line_ids.currency_id",
|
||||
)
|
||||
partner_bank_id = fields.Many2one(
|
||||
comodel_name="res.partner.bank",
|
||||
string="Bank Account",
|
||||
readonly=True,
|
||||
related="payment_line_ids.partner_bank_id",
|
||||
check_company=True,
|
||||
)
|
||||
date = fields.Date(related="payment_line_ids.date", readonly=True)
|
||||
communication_type = fields.Selection(
|
||||
related="payment_line_ids.communication_type", readonly=True
|
||||
)
|
||||
communication = fields.Char(string="Communication", required=True, readonly=True)
|
||||
company_id = fields.Many2one(
|
||||
comodel_name="res.company",
|
||||
related="order_id.payment_mode_id.company_id",
|
||||
store=True,
|
||||
readonly=True,
|
||||
)
|
||||
company_currency_id = fields.Many2one(
|
||||
comodel_name="res.currency",
|
||||
related="order_id.payment_mode_id.company_id.currency_id",
|
||||
readonly=True,
|
||||
store=True,
|
||||
)
|
||||
|
||||
@api.model
|
||||
def same_fields_payment_line_and_bank_payment_line(self):
|
||||
"""
|
||||
This list of fields is used both to compute the grouping
|
||||
hashcode and to copy the values from payment line
|
||||
to bank payment line
|
||||
The fields must have the same name on the 2 objects
|
||||
"""
|
||||
same_fields = [
|
||||
"currency_id",
|
||||
"partner_id",
|
||||
"partner_bank_id",
|
||||
"date",
|
||||
"communication_type",
|
||||
]
|
||||
return same_fields
|
||||
|
||||
@api.depends("payment_line_ids", "payment_line_ids.amount_currency")
|
||||
def _compute_amount(self):
|
||||
for bline in self:
|
||||
amount_currency = sum(bline.mapped("payment_line_ids.amount_currency"))
|
||||
amount_company_currency = bline.currency_id._convert(
|
||||
amount_currency,
|
||||
bline.company_currency_id,
|
||||
bline.company_id,
|
||||
bline.date or fields.Date.today(),
|
||||
)
|
||||
bline.amount_currency = amount_currency
|
||||
bline.amount_company_currency = amount_company_currency
|
||||
|
||||
@api.model
|
||||
@api.returns("self")
|
||||
def create(self, vals):
|
||||
if vals.get("name", "New") == "New":
|
||||
vals["name"] = (
|
||||
self.env["ir.sequence"].next_by_code("bank.payment.line") or "New"
|
||||
)
|
||||
return super(BankPaymentLine, self).create(vals)
|
||||
|
||||
def move_line_offsetting_account_hashcode(self):
|
||||
"""
|
||||
This method is inherited in the module
|
||||
account_banking_sepa_direct_debit
|
||||
"""
|
||||
self.ensure_one()
|
||||
if self.order_id.payment_mode_id.move_option == "date":
|
||||
hashcode = fields.Date.to_string(self.date)
|
||||
else:
|
||||
hashcode = str(self.id)
|
||||
return hashcode
|
||||
|
||||
def reconcile_payment_lines(self):
|
||||
for bline in self:
|
||||
if all([pline.move_line_id for pline in bline.payment_line_ids]):
|
||||
bline.reconcile()
|
||||
else:
|
||||
bline.no_reconcile_hook()
|
||||
|
||||
def no_reconcile_hook(self):
|
||||
"""This method is designed to be inherited if needed"""
|
||||
return
|
||||
|
||||
def reconcile(self):
|
||||
self.ensure_one()
|
||||
amlo = self.env["account.move.line"]
|
||||
transit_mlines = amlo.search([("bank_payment_line_id", "=", self.id)])
|
||||
assert len(transit_mlines) == 1, "We should have only 1 move"
|
||||
transit_mline = transit_mlines[0]
|
||||
assert not transit_mline.reconciled, "Transit move should not be reconciled"
|
||||
lines_to_rec = transit_mline
|
||||
for payment_line in self.payment_line_ids:
|
||||
|
||||
if not payment_line.move_line_id:
|
||||
raise UserError(
|
||||
_(
|
||||
"Can not reconcile: no move line for "
|
||||
"payment line %s of partner '%s'."
|
||||
)
|
||||
% (payment_line.name, payment_line.partner_id.name)
|
||||
)
|
||||
if payment_line.move_line_id.reconciled:
|
||||
raise UserError(
|
||||
_("Move line '%s' of partner '%s' has already " "been reconciled")
|
||||
% (payment_line.move_line_id.name, payment_line.partner_id.name)
|
||||
)
|
||||
if payment_line.move_line_id.account_id != transit_mline.account_id:
|
||||
raise UserError(
|
||||
_(
|
||||
"For partner '%s', the account of the account "
|
||||
"move line to pay (%s) is different from the "
|
||||
"account of of the transit move line (%s)."
|
||||
)
|
||||
% (
|
||||
payment_line.move_line_id.partner_id.name,
|
||||
payment_line.move_line_id.account_id.code,
|
||||
transit_mline.account_id.code,
|
||||
)
|
||||
)
|
||||
|
||||
lines_to_rec += payment_line.move_line_id
|
||||
|
||||
lines_to_rec.reconcile()
|
||||
|
||||
def unlink(self):
|
||||
for line in self:
|
||||
order_state = line.order_id.state
|
||||
if order_state == "uploaded":
|
||||
raise UserError(
|
||||
_(
|
||||
"Cannot delete a payment order line whose payment order is"
|
||||
" in state '%s'. You need to cancel it first."
|
||||
)
|
||||
% order_state
|
||||
)
|
||||
return super(BankPaymentLine, self).unlink()
|
||||
@@ -1,7 +1,7 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_account_payment_order,Full access on account.payment.order to Payment Manager,model_account_payment_order,group_account_payment,1,1,1,1
|
||||
access_account_payment_line,Full access on account.payment.line to Payment Manager,model_account_payment_line,group_account_payment,1,1,1,1
|
||||
access_bank_payment_line,Full access on bank.payment.line to Payment Manager,model_bank_payment_line,group_account_payment,1,1,1,1
|
||||
access_bank_payment_line,Full access on account.payment to Payment Manager,account.model_account_payment,group_account_payment,1,1,1,1
|
||||
base.access_res_partner_bank_group_partner_manager,Full access on res.partner.bank to Account Payment group,base.model_res_partner_bank,group_account_payment,1,1,1,1
|
||||
base.access_res_bank_group_partner_manager,Full access on res.bank to Account Payment group,base.model_res_bank,group_account_payment,1,1,1,1
|
||||
access_account_payment_line_create,access_account_payment_line_create,model_account_payment_line_create,group_account_payment,1,1,1,1
|
||||
|
||||
|
@@ -25,12 +25,5 @@
|
||||
name="domain_force"
|
||||
>['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]</field>
|
||||
</record>
|
||||
<record id="bank_payment_line_company_rule" model="ir.rule">
|
||||
<field name="name">Bank payment line multi-company rule</field>
|
||||
<field name="model_id" ref="model_bank_payment_line" />
|
||||
<field
|
||||
name="domain_force"
|
||||
>['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Copyright 2017 Camptocamp SA
|
||||
# Copyright 2017 Creu Blanca
|
||||
# Copyright 2019 Tecnativa - Pedro M. Baeza
|
||||
# Copyright 2019-2022 Tecnativa - Pedro M. Baeza
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from datetime import date, timedelta
|
||||
@@ -97,12 +97,12 @@ class TestPaymentOrderInbound(TestPaymentOrderInboundBase):
|
||||
payment_order.write({"journal_id": self.journal.id})
|
||||
|
||||
self.assertEqual(len(payment_order.payment_line_ids), 1)
|
||||
self.assertEqual(len(payment_order.bank_line_ids), 0)
|
||||
self.assertFalse(payment_order.payment_ids)
|
||||
|
||||
# Open payment order
|
||||
payment_order.draft2open()
|
||||
|
||||
self.assertEqual(payment_order.bank_line_count, 1)
|
||||
self.assertEqual(payment_order.payment_count, 1)
|
||||
|
||||
# Generate and upload
|
||||
payment_order.open2generated()
|
||||
@@ -112,10 +112,6 @@ class TestPaymentOrderInbound(TestPaymentOrderInboundBase):
|
||||
with self.assertRaises(UserError):
|
||||
payment_order.unlink()
|
||||
|
||||
bank_line = payment_order.bank_line_ids
|
||||
|
||||
with self.assertRaises(UserError):
|
||||
bank_line.unlink()
|
||||
payment_order.action_uploaded_cancel()
|
||||
self.assertEqual(payment_order.state, "cancel")
|
||||
payment_order.cancel2draft()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# © 2017 Camptocamp SA
|
||||
# © 2017 Creu Blanca
|
||||
# Copyright 2022 Tecnativa - Pedro M. Baeza
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from datetime import date, datetime, timedelta
|
||||
@@ -203,7 +204,7 @@ class TestPaymentOrderOutbound(TestPaymentOrderOutboundBase):
|
||||
order.draft2open()
|
||||
order.open2generated()
|
||||
order.generated2uploaded()
|
||||
self.assertEqual(order.move_ids[0].date, order.bank_line_ids[0].date)
|
||||
self.assertEqual(order.move_ids[0].date, order.payment_ids[0].date)
|
||||
self.assertEqual(order.state, "uploaded")
|
||||
|
||||
def test_cancel_payment_order(self):
|
||||
@@ -220,13 +221,11 @@ class TestPaymentOrderOutbound(TestPaymentOrderOutboundBase):
|
||||
payment_order.write({"journal_id": self.bank_journal.id})
|
||||
|
||||
self.assertEqual(len(payment_order.payment_line_ids), 1)
|
||||
self.assertEqual(len(payment_order.bank_line_ids), 0)
|
||||
self.assertFalse(payment_order.payment_ids)
|
||||
|
||||
# Open payment order
|
||||
payment_order.draft2open()
|
||||
|
||||
self.assertEqual(payment_order.bank_line_count, 1)
|
||||
|
||||
self.assertEqual(payment_order.payment_count, 1)
|
||||
# Generate and upload
|
||||
payment_order.open2generated()
|
||||
payment_order.generated2uploaded()
|
||||
@@ -235,10 +234,6 @@ class TestPaymentOrderOutbound(TestPaymentOrderOutboundBase):
|
||||
with self.assertRaises(UserError):
|
||||
payment_order.unlink()
|
||||
|
||||
bank_line = payment_order.bank_line_ids
|
||||
|
||||
with self.assertRaises(UserError):
|
||||
bank_line.unlink()
|
||||
payment_order.action_uploaded_cancel()
|
||||
self.assertEqual(payment_order.state, "cancel")
|
||||
payment_order.cancel2draft()
|
||||
@@ -294,19 +289,19 @@ class TestPaymentOrderOutbound(TestPaymentOrderOutboundBase):
|
||||
self.assertEqual(len(outbound_order.payment_line_ids), 2)
|
||||
self.assertEqual(outbound_order.payment_line_ids[1].date, False)
|
||||
# Open payment order
|
||||
self.assertEqual(len(outbound_order.bank_line_ids), 0)
|
||||
self.assertFalse(outbound_order.payment_ids)
|
||||
outbound_order.draft2open()
|
||||
self.assertEqual(outbound_order.bank_line_count, 2)
|
||||
self.assertEqual(outbound_order.payment_count, 2)
|
||||
self.assertEqual(
|
||||
outbound_order.payment_line_ids[0].date,
|
||||
outbound_order.payment_line_ids[0].bank_line_id.date,
|
||||
outbound_order.payment_line_ids[0].payment_ids.date,
|
||||
)
|
||||
self.assertEqual(
|
||||
outbound_order.payment_line_ids[1].date,
|
||||
fields.Date.context_today(outbound_order),
|
||||
)
|
||||
self.assertEqual(
|
||||
outbound_order.payment_line_ids[1].bank_line_id.date,
|
||||
outbound_order.payment_line_ids[1].payment_ids.date,
|
||||
fields.Date.context_today(outbound_order),
|
||||
)
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
name="partner_bank_id"
|
||||
domain="[('partner_id', '=', partner_id), '|', ('company_id', '=', company_id), ('company_id', '=', False)]"
|
||||
/>
|
||||
<field name="bank_payment_line_id" />
|
||||
</group>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
/>
|
||||
<field name="amount_company_currency" />
|
||||
<field name="company_currency_id" invisible="1" />
|
||||
<field name="bank_line_id" />
|
||||
<field name="payment_ids" />
|
||||
<field name="payment_type" invisible="1" />
|
||||
</group>
|
||||
</group>
|
||||
|
||||
@@ -98,7 +98,7 @@
|
||||
/>
|
||||
<field name="payment_type" invisible="0" />
|
||||
<field
|
||||
name="bank_line_count"
|
||||
name="payment_count"
|
||||
attrs="{'invisible': [('state', 'in', ('draft', 'cancel'))]}"
|
||||
/>
|
||||
</group>
|
||||
@@ -122,11 +122,11 @@
|
||||
/>
|
||||
</page>
|
||||
<page
|
||||
name="bank-lines"
|
||||
string="Bank Transactions"
|
||||
name="payment-lines"
|
||||
string="Payment Transactions"
|
||||
attrs="{'invisible': [('state', 'in', ('draft', 'cancel'))]}"
|
||||
>
|
||||
<field name="bank_line_ids" edit="0" create="0" />
|
||||
<field name="payment_ids" edit="0" create="0" />
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
@@ -150,9 +150,9 @@
|
||||
<field name="date_uploaded" />
|
||||
<field name="description" optional="show" />
|
||||
<field
|
||||
name="bank_line_count"
|
||||
name="payment_count"
|
||||
optional="hide"
|
||||
string="Bank Transactions"
|
||||
string="Payment Transactions"
|
||||
/>
|
||||
<field name="total_company_currency" sum="Total Company Currency" />
|
||||
<field name="company_currency_id" invisible="1" />
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<!--
|
||||
© 2015-2016 Akretion (https://www.akretion.com/)
|
||||
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
-->
|
||||
<record id="bank_payment_line_form" model="ir.ui.view">
|
||||
<field name="name">bank.payment.line.form</field>
|
||||
<field name="model">bank.payment.line</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Bank Payment Line" create="false">
|
||||
<group name="main">
|
||||
<field
|
||||
name="order_id"
|
||||
invisible="not context.get('bank_payment_line_main_view')"
|
||||
/>
|
||||
<field name="name" />
|
||||
<field
|
||||
name="company_id"
|
||||
groups="base.group_multi_company"
|
||||
invisible="not context.get('bank_payment_line_main_view')"
|
||||
/>
|
||||
<field name="partner_id" />
|
||||
<field name="date" />
|
||||
<field name="amount_currency" />
|
||||
<field name="currency_id" invisible="1" />
|
||||
<field name="partner_bank_id" />
|
||||
<field name="communication_type" />
|
||||
<field name="communication" />
|
||||
</group>
|
||||
<group string="Related Payment Lines" name="payment-lines">
|
||||
<field name="payment_line_ids" nolabel="1" />
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record id="bank_payment_line_tree" model="ir.ui.view">
|
||||
<field name="name">bank.payment.line.tree</field>
|
||||
<field name="model">bank.payment.line</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Bank Payment Lines" create="false">
|
||||
<field
|
||||
name="order_id"
|
||||
invisible="not context.get('bank_payment_line_main_view')"
|
||||
/>
|
||||
<field name="partner_id" />
|
||||
<field name="communication" />
|
||||
<field name="partner_bank_id" />
|
||||
<field name="date" />
|
||||
<field name="amount_currency" sum="Total Amount" />
|
||||
<field name="currency_id" invisible="1" />
|
||||
<field name="name" optional="show" />
|
||||
<field
|
||||
name="company_id"
|
||||
groups="base.group_multi_company"
|
||||
invisible="not context.get('bank_payment_line_main_view')"
|
||||
/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record id="bank_payment_line_search" model="ir.ui.view">
|
||||
<field name="name">bank.payment.line.search</field>
|
||||
<field name="model">bank.payment.line</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Bank Payment Lines">
|
||||
<field name="partner_id" />
|
||||
<filter
|
||||
name="inbound"
|
||||
string="Inbound"
|
||||
domain="[('payment_type', '=', 'inbound')]"
|
||||
/>
|
||||
<filter
|
||||
name="outbound"
|
||||
string="Outbound"
|
||||
domain="[('payment_type', '=', 'outbound')]"
|
||||
/>
|
||||
<group string="Group By" name="groupby">
|
||||
<filter
|
||||
name="state_groupby"
|
||||
string="State"
|
||||
context="{'group_by': 'state'}"
|
||||
/>
|
||||
<filter
|
||||
name="partner_groupby"
|
||||
string="Partner"
|
||||
context="{'group_by': 'partner_id'}"
|
||||
/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
<record id="bank_payment_line_action" model="ir.actions.act_window">
|
||||
<field name="name">Bank Payment Lines</field>
|
||||
<field name="res_model">bank.payment.line</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="context">{'bank_payment_line_main_view': True}</field>
|
||||
</record>
|
||||
<menuitem
|
||||
id="bank_payment_line_menu"
|
||||
action="bank_payment_line_action"
|
||||
parent="account.menu_finance_payables"
|
||||
sequence="50"
|
||||
groups="group_account_payment"
|
||||
/>
|
||||
</odoo>
|
||||
@@ -1,4 +1,5 @@
|
||||
# Copyright (C) 2020 Open Source Integrators
|
||||
# Copyright 2022 Tecnativa - Pedro M. Baeza
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from datetime import datetime
|
||||
@@ -25,10 +26,10 @@ class PaymentOrder(models.Model):
|
||||
for rec in self:
|
||||
if rec.payment_mode_id.send_email_to_partner:
|
||||
date_generated = rec.date_generated
|
||||
for bank_line in rec.bank_line_ids:
|
||||
partner_name = bank_line.partner_id.name
|
||||
total_amount = bank_line.amount_currency
|
||||
payment_ref = bank_line.name
|
||||
for payment in rec.payment_ids:
|
||||
partner_name = payment.partner_id.name
|
||||
total_amount = payment.amount
|
||||
payment_ref = payment.payment_reference
|
||||
line_data = []
|
||||
header_data = {
|
||||
"inv_no": "Invoice No.",
|
||||
@@ -42,7 +43,7 @@ class PaymentOrder(models.Model):
|
||||
"due_amount": "Due Amount",
|
||||
}
|
||||
line_data.append(header_data)
|
||||
for payment_line in bank_line.payment_line_ids:
|
||||
for payment_line in payment.payment_line_ids:
|
||||
invoice_date = (
|
||||
payment_line.move_line_id.move_id.invoice_date
|
||||
and datetime.strftime(
|
||||
@@ -69,7 +70,7 @@ class PaymentOrder(models.Model):
|
||||
"account_payment_order_vendor_email."
|
||||
"ach_payment_email_template"
|
||||
)
|
||||
partner_email_id = bank_line.partner_id.email
|
||||
partner_email_id = payment.partner_id.email
|
||||
if partner_email_id:
|
||||
template.write({"email_to": partner_email_id})
|
||||
template.with_context(
|
||||
|
||||
Reference in New Issue
Block a user