diff --git a/account_banking_ach_discount/__manifest__.py b/account_banking_ach_discount/__manifest__.py index 68d134b4..511f07a0 100644 --- a/account_banking_ach_discount/__manifest__.py +++ b/account_banking_ach_discount/__manifest__.py @@ -2,23 +2,23 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { - 'name': 'OSI ACH-Batch Discount Connector', - 'version': '14.0.1.0.0', - 'license': 'LGPL-3', - 'author': 'Open Source Integrators', - 'category': 'Accounting', - 'maintainer': 'Open Source Integrators', - 'website': 'https://github.com/OCA/l10n-usa', - 'maintainers': ['bodedra'], - 'depends': [ - 'account_payment_term_discount', - 'account_payment_batch_process', - 'account_payment_order', - 'account_banking_ach_credit_transfer', - 'account_banking_ach_direct_debit', + "name": "Discount on ACH batch payments", + "version": "14.0.1.0.0", + "license": "AGPL-3", + "author": "Open Source Integrators, Odoo Community Association (OCA)", + "category": "Accounting", + "maintainer": "Odoo Community Association (OCA)", + "website": "https://github.com/OCA/l10n-usa", + "development_status": "Beta", + "maintainers": ["bodedra"], + "depends": [ + "account_payment_term_discount", + "account_payment_batch_process", + "account_payment_order", + "account_banking_ach_credit_transfer", + "account_banking_ach_direct_debit", ], - 'data': [ - 'views/account_payment_view.xml', + "data": [ + "views/account_payment_view.xml", ], - 'installable': True, } diff --git a/account_banking_ach_discount/changes.txt b/account_banking_ach_discount/changes.txt deleted file mode 100644 index 29e1f9d7..00000000 --- a/account_banking_ach_discount/changes.txt +++ /dev/null @@ -1,5 +0,0 @@ -init - 12.0.1.0.0 -Task Ref: FUL-02C-00-1910-18565 -Description: - * ACH Payment, Automatic Discount and Batch Payment Partial Pay Integration diff --git a/account_banking_ach_discount/models/__init__.py b/account_banking_ach_discount/models/__init__.py index 192b0fe1..e4adbac1 100644 --- a/account_banking_ach_discount/models/__init__.py +++ b/account_banking_ach_discount/models/__init__.py @@ -1,6 +1,5 @@ # Copyright (C) 2019 Open Source Integrators # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - from . import account_payment from . import account_move_line from . import bank_payment_line diff --git a/account_banking_ach_discount/models/account_move.py b/account_banking_ach_discount/models/account_move.py index 57e0975e..a749d283 100644 --- a/account_banking_ach_discount/models/account_move.py +++ b/account_banking_ach_discount/models/account_move.py @@ -1,9 +1,6 @@ # Copyright (C) 2019 Open Source Integrators # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo import models, api, _ -import json -from odoo.tools import date_utils +from odoo import models class AccountMove(models.Model): @@ -17,19 +14,21 @@ def _get_reconciled_info_JSON_values(self): for item in res: payment_lines = set() for line in self.line_ids: - payment_lines.update(line.mapped( - 'matched_credit_ids.credit_move_id.id')) - payment_lines.update(line.mapped( - 'matched_debit_ids.debit_move_id.id')) - payment_move_line_ids = self.env['account.move.line'].browse( - list(payment_lines)).sorted() + payment_lines.update( + line.mapped("matched_credit_ids.credit_move_id.id") + ) + payment_lines.update( + line.mapped("matched_debit_ids.debit_move_id.id") + ) + payment_move_line_ids = ( + self.env["account.move.line"] + .browse(list(payment_lines)) + .sorted() + ) for mvl in payment_move_line_ids: # get bank payment line - if ( - mvl.move_id - and mvl.id == item["payment_id"] - ): + if mvl.move_id and mvl.id == item["payment_id"]: for pay_li in mvl.bank_payment_line_id.payment_line_ids: # Get related payment line ref if pay_li.communication == inv_number: @@ -43,10 +42,7 @@ def _get_reconciled_info_JSON_values(self): and item["account_payment_id"] == mvl.payment_id.id ): if mvl.full_reconcile_id and not flag: - item["amount"] = ( - item["amount"] - - self.discount_taken - ) + item["amount"] = item["amount"] - self.discount_taken flag = True return res @@ -59,8 +55,10 @@ def _prepare_discount_move_line(self, vals): and invoice.invoice_payment_term_id.is_discount and invoice.invoice_payment_term_id.line_ids ): - discount_information = invoice.invoice_payment_term_id._check_payment_term_discount( - invoice, invoice.date_invoice + discount_information = ( + invoice.invoice_payment_term_id._check_payment_term_discount( + invoice, invoice.date_invoice + ) ) discount_amt = discount_information[0] discount_account_id = discount_information[1] @@ -100,9 +98,7 @@ def _prepare_writeoff_move_line(self, payment_line, vals): } ) if invoice.move_type == "out_invoice": - vals.update( - {"credit": 0.0, "debit": payment_line.payment_difference}) + vals.update({"credit": 0.0, "debit": payment_line.payment_difference}) elif invoice.move_type == "in_invoice": - vals.update( - {"credit": payment_line.payment_difference, "debit": 0.0}) + vals.update({"credit": payment_line.payment_difference, "debit": 0.0}) return vals diff --git a/account_banking_ach_discount/models/account_move_line.py b/account_banking_ach_discount/models/account_move_line.py index acd61f34..683cdf18 100644 --- a/account_banking_ach_discount/models/account_move_line.py +++ b/account_banking_ach_discount/models/account_move_line.py @@ -22,8 +22,11 @@ def _prepare_payment_line_vals(self, payment_order): and invoice.invoice_payment_term_id.is_discount and invoice.invoice_payment_term_id.line_ids ): - discount_information = invoice.invoice_payment_term_id._check_payment_term_discount( - invoice, self._context.get("payment_date") or invoice.date_invoice + discount_information = ( + invoice.invoice_payment_term_id._check_payment_term_discount( + invoice, + self._context.get("payment_date") or invoice.date_invoice, + ) ) discount_amt = discount_information[0] vals.update( diff --git a/account_banking_ach_discount/models/account_payment.py b/account_banking_ach_discount/models/account_payment.py index 6252a2da..a03a2066 100644 --- a/account_banking_ach_discount/models/account_payment.py +++ b/account_banking_ach_discount/models/account_payment.py @@ -1,7 +1,7 @@ # Copyright (C) 2019 Open Source Integrators # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, fields, models, _ +from odoo import _, api, fields, models from odoo.exceptions import UserError @@ -38,7 +38,9 @@ def action_validate_invoice_payment(self): payment.reconciled_invoice_ids.move_id.line_ids.write( {"payment_mode_id": payment_mode_id.id} ) - action = payment.reconciled_invoice_ids.create_account_payment_line() + action = ( + payment.reconciled_invoice_ids.create_account_payment_line() + ) payment.unlink() return action res = super(AccountPayment, self).action_validate_invoice_payment() diff --git a/account_banking_ach_discount/models/account_payment_order.py b/account_banking_ach_discount/models/account_payment_order.py index 6f7b666d..10a96b10 100644 --- a/account_banking_ach_discount/models/account_payment_order.py +++ b/account_banking_ach_discount/models/account_payment_order.py @@ -16,22 +16,22 @@ def _prepare_move(self, bank_lines=None): if "bank_payment_line_id" in vals[2] and vals[2]["bank_payment_line_id"]: bank_payment_id = vals[2].get("bank_payment_line_id") bank_payment = bank_payment_line_pool.browse(bank_payment_id) - for transaction in bank_payment.payment_line_ids: + for line in bank_payment.payment_line_ids: temp_vals = vals[2].copy() - amount = transaction.amount_currency - discount = transaction.discount_amount - payment_difference = transaction.payment_difference + amount = line.amount_currency + discount = line.discount_amount + payment_difference = line.payment_difference writeoff = ( payment_difference and payment_difference - discount or 0.0 ) - invoice_close = transaction.payment_difference_handling != "open" - use_debit = transaction.move_id.move_type in ( + invoice_close = line.payment_difference_handling != "open" + use_debit = line.move_id.move_type in ( "in_invoice", "out_refund", ) - temp_vals["move_id"] = transaction.move_id.id + temp_vals["move_id"] = line.move_id.id if use_debit: temp_vals["debit"] = amount + discount else: @@ -40,8 +40,8 @@ def _prepare_move(self, bank_lines=None): line_ids.append((0, 0, temp_vals)) if discount > 0: - discount_information = transaction.move_id.invoice_payment_term_id._check_payment_term_discount( - transaction.move_id, transaction.date + discount_information = line.move_id.invoice_payment_term_id._check_payment_term_discount( + line.move_id, line.date ) discount_vals = temp_vals.copy() discount_vals["account_id"] = discount_information[1] @@ -61,8 +61,8 @@ def _prepare_move(self, bank_lines=None): temp_vals["debit"] = amount + discount + round(writeoff, 2) else: temp_vals["credit"] = amount + discount + round(writeoff, 2) - writeoff_vals = transaction.move_id._prepare_writeoff_move_line( - transaction, temp_vals.copy() + writeoff_vals = line.move_id._prepare_writeoff_move_line( + line, temp_vals.copy() ) writeoff_vals["bank_payment_line_id"] = False if writeoff_vals: @@ -70,6 +70,5 @@ def _prepare_move(self, bank_lines=None): # payment order line else: line_ids.append(vals) - values["line_ids"] = line_ids return values diff --git a/account_banking_ach_discount/readme/CONFIGURE.rst b/account_banking_ach_discount/readme/CONFIGURE.rst new file mode 100644 index 00000000..ac6179e5 --- /dev/null +++ b/account_banking_ach_discount/readme/CONFIGURE.rst @@ -0,0 +1,22 @@ +Payment Terms +~~~~~~~~~~~~~ + +* Go to *Accounting > Configuration > Payment Terms* +* Create or select a payment term +* Activate the discounts options +* On a line, set the discount percentage and number of days + +Payment Modes +~~~~~~~~~~~~~ + +* Go to *Accounting > Configuration > Payment Modes* +* Create or select a payment mode +* Link it to an ACH payment method + +Vendors +~~~~~~~ + +* Go *Contacts* or *Accounting > Vendors > Vendors* +* Create or select a vendor +* On the Sales and Purchase tab, set the supplier payment mode +* On the Accounting tab, set their bank information (account number, bank, routing number) diff --git a/account_banking_ach_discount/readme/CONTRIBUTORS.rst b/account_banking_ach_discount/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000..4a3b34a1 --- /dev/null +++ b/account_banking_ach_discount/readme/CONTRIBUTORS.rst @@ -0,0 +1,4 @@ +* Open Source Integrators + + * Bhavesh Odedra + * Maxime Chambreuil diff --git a/account_banking_ach_discount/readme/DESCRIPTION.rst b/account_banking_ach_discount/readme/DESCRIPTION.rst new file mode 100644 index 00000000..c4609831 --- /dev/null +++ b/account_banking_ach_discount/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +This module will add support for discount in ACH and batch ACH workflow. diff --git a/account_banking_ach_discount/readme/HISTORY.rst b/account_banking_ach_discount/readme/HISTORY.rst new file mode 100644 index 00000000..3e246dc4 --- /dev/null +++ b/account_banking_ach_discount/readme/HISTORY.rst @@ -0,0 +1,4 @@ +12.0.1.0.0 +~~~~~~~~~~ + +- ACH Payment, Automatic Discount and Batch Payment Partial Pay Integration diff --git a/account_banking_ach_discount/readme/USAGE.rst b/account_banking_ach_discount/readme/USAGE.rst new file mode 100644 index 00000000..05146861 --- /dev/null +++ b/account_banking_ach_discount/readme/USAGE.rst @@ -0,0 +1,8 @@ +* Go to *Accounting > Customers > Invoices* or *Accounting > Vendors > Bills* +* Select or create various records in the state posted with ACH and discounts +* In the Action menu, click on Batch Payments +* Review the payment information provided by default +* Click on Make Payments +* Review the payment order, confirm it and generate the ACH file +* Go to your bank's website to upload the file +* Come back to Odoo and confirm the upload to the bank was successful diff --git a/account_banking_ach_discount/wizard/__init__.py b/account_banking_ach_discount/wizard/__init__.py index c3139970..17731038 100644 --- a/account_banking_ach_discount/wizard/__init__.py +++ b/account_banking_ach_discount/wizard/__init__.py @@ -1,4 +1,3 @@ # Copyright (C) 2019 Open Source Integrators # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from . import account_register_payments +from . import account_payment_register diff --git a/account_banking_ach_discount/wizard/account_payment_register.py b/account_banking_ach_discount/wizard/account_payment_register.py new file mode 100644 index 00000000..45e8cf17 --- /dev/null +++ b/account_banking_ach_discount/wizard/account_payment_register.py @@ -0,0 +1,62 @@ +# Copyright (C) 2019 Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import models + + +class AccountPaymentRegister(models.TransientModel): + _inherit = "account.payment.register" + + def make_payments(self): + if self.payment_method_id and self.payment_method_id.code in ( + "ACH-In", + "ACH-Out", + ): + action = False + payment_mode = self.env["account.payment.mode"].search( + [ + ("payment_type", "=", self.payment_type), + ("payment_method_id", "=", self.payment_method_id.id), + ("payment_order_ok", "=", True), + ], + limit=1, + ) + payment_line_pool = self.env["account.payment.line"] + # Update invoice with Payment mode + if payment_mode: + for line in self.invoice_payments: + invoice_id = line.invoice_id + # updated discount logic + discount = invoice_id.discount_taken + # discount should not be consider for open invoices + if line.payment_difference_handling != "open": + discount = invoice_id.discount_taken + line.payment_difference + invoice_id.write( + { + "payment_mode_id": payment_mode.id, + "discount_taken": discount, + } + ) + invoice_id.line_ids.write({"payment_mode_id": payment_mode.id}) + action = invoice_id.with_context( + payment_date=self.payment_date, + payment_line_state=line.payment_difference_handling, + ).create_account_payment_line() + # Find related ACH transaction line + domain = [("move_id", "=", invoice_id.id), ("state", "=", "draft")] + ach_lines = payment_line_pool.search(domain) + if ach_lines: + ach_lines.write( + { + "payment_difference_handling": line.payment_difference_handling, + "writeoff_account_id": line.writeoff_account_id.id, + "reason_code": line.reason_code.id, + "note": line.note, + "communication": "Payment of invoice %s" + % line.invoice_id.name, + "communication_type": "normal", + "amount_currency": line.amount, + "payment_difference": line.payment_difference, + } + ) + return action + return super().make_payments() diff --git a/account_banking_ach_discount/wizard/account_register_payments.py b/account_banking_ach_discount/wizard/account_register_payments.py deleted file mode 100644 index 5014589c..00000000 --- a/account_banking_ach_discount/wizard/account_register_payments.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (C) 2019 Open Source Integrators -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import _, models -from odoo.exceptions import UserError - - -class AccountPaymentRegister(models.TransientModel): - _inherit = "account.payment.register" - - def make_payments(self): - payment_method = self.payment_method_id - if self.is_customer: - invoice_payment_method = self.invoice_customer_payments[ - 0 - ].invoice_id.payment_mode_id.payment_method_id - else: - invoice_payment_method = self.invoice_payments[ - 0 - ].invoice_id.payment_mode_id.payment_method_id - if ( - payment_method.code in ("ACH-In", "ACH-Out") and not invoice_payment_method - ) or ( - payment_method.code not in ("ACH-In", "ACH-Out") and invoice_payment_method - ): - raise UserError(_("Payment Method does not match Invoice payment mode.")) - if payment_method: - if payment_method.code in ("ACH-In", "ACH-Out"): - action = False - payment_mode_id = self.env["account.payment.mode"].search( - [ - ("payment_type", "=", self.payment_type), - ("payment_method_id", "=", payment_method.id), - ("payment_order_ok", "=", True), - ], - limit=1, - ) - payment_line_pool = self.env["account.payment.line"] - # Update invoice with Payment mode - if payment_mode_id: - for line in self.invoice_payments: - invoice_id = line.invoice_id - # updated discount logic - discount = invoice_id.discount_taken - # discount should not be consider for open invoices - if line.payment_difference_handling != "open": - discount = ( - invoice_id.discount_taken + line.payment_difference - ) - invoice_id.write( - { - "payment_mode_id": payment_mode_id.id, - "discount_taken": discount, - } - ) - invoice_id.line_ids.write( - {"payment_mode_id": payment_mode_id.id} - ) - action = invoice_id.with_context( - payment_date=self.payment_date, - payment_line_state=line.payment_difference_handling, - ).create_account_payment_line() - # Find related ACH transaction line - domain = [ - ("move_id", "=", invoice_id.id), - ("state", "=", "draft"), - ] - ach_transaction_line = payment_line_pool.search(domain) - if ach_transaction_line: - ach_transaction_line.write( - { - "payment_difference_handling": line.payment_difference_handling, - "writeoff_account_id": line.writeoff_account_id.id, - "reason_code": line.reason_code.id, - "note": line.note, - "communication": "Payment of invoice %s" - % line.invoice_id.name, - "communication_type": "normal", - "amount_currency": line.paying_amt, - "payment_difference": line.payment_difference, - } - ) - return action - res = super(AccountPaymentRegister, self).make_payments() - return res