Initial commit: Accounting packages

This commit is contained in:
Ernad Husremovic 2025-08-29 15:20:47 +02:00
commit 4ef34c2317
2661 changed files with 1709616 additions and 0 deletions

View file

@ -0,0 +1,5 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import account_payment_register
from . import payment_link_wizard
from . import payment_refund_wizard

View file

@ -0,0 +1,93 @@
from odoo import api, Command, fields, models
class AccountPaymentRegister(models.TransientModel):
_inherit = 'account.payment.register'
# == Business fields ==
payment_token_id = fields.Many2one(
comodel_name='payment.token',
string="Saved payment token",
store=True, readonly=False,
compute='_compute_payment_token_id',
domain='''[
('id', 'in', suitable_payment_token_ids),
]''',
help="Note that tokens from providers set to only authorize transactions (instead of capturing the amount) are "
"not available.")
# == Display purpose fields ==
suitable_payment_token_ids = fields.Many2many(
comodel_name='payment.token',
compute='_compute_suitable_payment_token_ids'
)
# Technical field used to hide or show the payment_token_id if needed
use_electronic_payment_method = fields.Boolean(
compute='_compute_use_electronic_payment_method',
)
payment_method_code = fields.Char(
related='payment_method_line_id.code')
# -------------------------------------------------------------------------
# COMPUTE METHODS
# -------------------------------------------------------------------------
@api.depends('payment_method_line_id')
def _compute_suitable_payment_token_ids(self):
for wizard in self:
if wizard.can_edit_wizard and wizard.use_electronic_payment_method:
related_partner_ids = (
wizard.partner_id
| wizard.partner_id.commercial_partner_id
| wizard.partner_id.commercial_partner_id.child_ids
)._origin
wizard.suitable_payment_token_ids = self.env['payment.token'].sudo().search([
('company_id', '=', wizard.company_id.id),
('provider_id.capture_manually', '=', False),
('partner_id', 'in', related_partner_ids.ids),
('provider_id', '=', wizard.payment_method_line_id.payment_provider_id.id),
])
else:
wizard.suitable_payment_token_ids = [Command.clear()]
@api.depends('payment_method_line_id')
def _compute_use_electronic_payment_method(self):
for wizard in self:
# Get a list of all electronic payment method codes.
# These codes are comprised of the codes of each payment provider.
codes = [key for key in dict(self.env['payment.provider']._fields['code']._description_selection(self.env))]
wizard.use_electronic_payment_method = wizard.payment_method_code in codes
@api.onchange('can_edit_wizard', 'payment_method_line_id', 'journal_id')
def _compute_payment_token_id(self):
codes = [key for key in dict(self.env['payment.provider']._fields['code']._description_selection(self.env))]
for wizard in self:
related_partner_ids = (
wizard.partner_id
| wizard.partner_id.commercial_partner_id
| wizard.partner_id.commercial_partner_id.child_ids
)._origin
if wizard.can_edit_wizard \
and wizard.payment_method_line_id.code in codes \
and wizard.journal_id \
and related_partner_ids:
wizard.payment_token_id = self.env['payment.token'].sudo().search([
('company_id', '=', wizard.company_id.id),
('partner_id', 'in', related_partner_ids.ids),
('provider_id.capture_manually', '=', False),
('provider_id', '=', wizard.payment_method_line_id.payment_provider_id.id),
], limit=1)
else:
wizard.payment_token_id = False
# -------------------------------------------------------------------------
# BUSINESS METHODS
# -------------------------------------------------------------------------
def _create_payment_vals_from_wizard(self, batch_result):
# OVERRIDE
payment_vals = super()._create_payment_vals_from_wizard(batch_result)
payment_vals['payment_token_id'] = self.payment_token_id.id
return payment_vals

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_account_payment_register_form_inherit_payment" model="ir.ui.view">
<field name="name">account.payment.register.form.inherit.payment</field>
<field name="model">account.payment.register</field>
<field name="inherit_id" ref="account.view_account_payment_register_form"/>
<field name="arch" type="xml">
<field name="payment_method_line_id" position="after">
<field name="payment_method_code" invisible="1"/>
<field name="suitable_payment_token_ids" invisible="1"/>
<field name="use_electronic_payment_method" invisible="1"/>
<field name="payment_token_id"
options="{'no_create': True}"
attrs="{'invisible': ['|', ('use_electronic_payment_method', '!=', True), '|', ('can_edit_wizard', '=', False), '&amp;', ('can_group_payments', '=', True), ('group_payment', '=', False)]}"/>
</field>
</field>
</record>
</odoo>

View file

@ -0,0 +1,26 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models
class PaymentLinkWizard(models.TransientModel):
_inherit = 'payment.link.wizard'
def _get_additional_link_values(self):
""" Override of `payment` to add `invoice_id` to the payment link values.
The other values related to the invoice are directly read from the invoice.
Note: self.ensure_one()
:return: The additional payment link values.
:rtype: dict
"""
res = super()._get_additional_link_values()
if self.res_model != 'account.move':
return res
# Invoice-related fields are retrieved in the controller.
return {
'invoice_id': self.res_id,
}

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="action_invoice_order_generate_link" model="ir.actions.act_window">
<field name="name">Generate a Payment Link</field>
<field name="res_model">payment.link.wizard</field>
<field name="view_mode">form</field>
<field name="view_id" ref="payment.payment_link_wizard_view_form"/>
<field name="target">new</field>
<field name="binding_model_id" ref="model_account_move"/>
<field name="binding_view_types">form</field>
</record>
</odoo>

View file

@ -0,0 +1,66 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
class PaymentRefundWizard(models.TransientModel):
_name = 'payment.refund.wizard'
_description = "Payment Refund Wizard"
payment_id = fields.Many2one(
string="Payment",
comodel_name='account.payment',
readonly=True,
default=lambda self: self.env.context.get('active_id'),
)
transaction_id = fields.Many2one(
string="Payment Transaction", related='payment_id.payment_transaction_id'
)
payment_amount = fields.Monetary(string="Payment Amount", related='payment_id.amount')
refunded_amount = fields.Monetary(string="Refunded Amount", compute='_compute_refunded_amount')
amount_available_for_refund = fields.Monetary(
string="Maximum Refund Allowed", related='payment_id.amount_available_for_refund'
)
amount_to_refund = fields.Monetary(
string="Refund Amount", compute='_compute_amount_to_refund', store=True, readonly=False
)
currency_id = fields.Many2one(string="Currency", related='transaction_id.currency_id')
support_refund = fields.Selection(related='transaction_id.provider_id.support_refund')
has_pending_refund = fields.Boolean(
string="Has a pending refund", compute='_compute_has_pending_refund'
)
@api.constrains('amount_to_refund')
def _check_amount_to_refund_within_boundaries(self):
for wizard in self:
if not 0 < wizard.amount_to_refund <= wizard.amount_available_for_refund:
raise ValidationError(_(
"The amount to be refunded must be positive and cannot be superior to %s.",
wizard.amount_available_for_refund
))
@api.depends('amount_available_for_refund')
def _compute_refunded_amount(self):
for wizard in self:
wizard.refunded_amount = wizard.payment_amount - wizard.amount_available_for_refund
@api.depends('amount_available_for_refund')
def _compute_amount_to_refund(self):
""" Set the default amount to refund to the amount available for refund. """
for wizard in self:
wizard.amount_to_refund = wizard.amount_available_for_refund
@api.depends('payment_id') # To always trigger the compute
def _compute_has_pending_refund(self):
for wizard in self:
pending_refunds_count = self.env['payment.transaction'].search_count([
('source_transaction_id', '=', wizard.payment_id.payment_transaction_id.id),
('operation', '=', 'refund'),
('state', 'in', ['draft', 'pending', 'authorized']),
])
wizard.has_pending_refund = pending_refunds_count > 0
def action_refund(self):
for wizard in self:
wizard.transaction_id.action_refund(amount_to_refund=wizard.amount_to_refund)

View file

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="payment_refund_wizard_view_form" model="ir.ui.view">
<field name="name">payment.refund.wizard.form</field>
<field name="model">payment.refund.wizard</field>
<field name="arch" type="xml">
<form string="Refund">
<field name="has_pending_refund" invisible="1"/>
<div class="alert alert-warning"
id="alert_draft_refund_tx"
role="alert"
attrs="{'invisible': [('has_pending_refund', '=', False)]}">
<p>
<strong>Warning!</strong> There is a refund pending for this payment.
Wait a moment for it to be processed. If the refund is still pending in a
few minutes, please check your payment provider configuration.
</p>
</div>
<group>
<group>
<field name="payment_id" invisible="1"/>
<field name="transaction_id" invisible="1"/>
<field name="currency_id" invisible="1"/>
<field name="support_refund" invisible="1"/>
<field name="payment_amount"/>
<field name="refunded_amount"
attrs="{'invisible': [('refunded_amount', '&lt;=', 0)]}"/>
<field name="amount_available_for_refund"/>
<field name="amount_to_refund"
attrs="{'readonly': [('support_refund', '=', 'full_only')]}"/>
</group>
</group>
<footer>
<button string="Refund" type="object" name="action_refund" class="btn-primary"/>
<button string="Close" special="cancel" class="btn-secondary"/>
</footer>
</form>
</field>
</record>
</odoo>