mirror of
https://gitlab.com/hibou-io/hibou-odoo/suite.git
synced 2025-01-20 12:37:31 +02:00
[ADD] sale_payment_web: for 13.0
This commit is contained in:
committed by
Cedric Collins
parent
ade1b75955
commit
f428be93cc
2
sale_payment_web/__init__.py
Normal file
2
sale_payment_web/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
from . import models
|
||||
from . import wizard
|
||||
27
sale_payment_web/__manifest__.py
Executable file
27
sale_payment_web/__manifest__.py
Executable file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
'name': 'Sale Payment Web',
|
||||
'author': 'Hibou Corp. <hello@hibou.io>',
|
||||
'category': 'Sales',
|
||||
'version': '13.0.1.0.0',
|
||||
'description':
|
||||
"""
|
||||
Sale Payment Web
|
||||
================
|
||||
|
||||
Allow sales people to register payments for sale orders.
|
||||
|
||||
Electronic payments will create transactions and automatically reconcile on the invoice.
|
||||
|
||||
""",
|
||||
'depends': [
|
||||
'payment',
|
||||
'sale',
|
||||
],
|
||||
'auto_install': False,
|
||||
'data': [
|
||||
'security/sale_security.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'wizard/account_payment_register_views.xml',
|
||||
'views/sale_views.xml',
|
||||
],
|
||||
}
|
||||
2
sale_payment_web/models/__init__.py
Normal file
2
sale_payment_web/models/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
from . import payment
|
||||
from . import sale
|
||||
12
sale_payment_web/models/payment.py
Normal file
12
sale_payment_web/models/payment.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from odoo import api, models
|
||||
|
||||
|
||||
class PaymentTransaction(models.Model):
|
||||
_inherit = 'payment.transaction'
|
||||
|
||||
@api.model
|
||||
def create(self, values):
|
||||
active_ids = self._context.get('active_ids')
|
||||
if active_ids and self._context.get('active_model') == 'sale.order':
|
||||
values['sale_order_ids'] = [(6, 0, active_ids)]
|
||||
return super(PaymentTransaction, self).create(values)
|
||||
16
sale_payment_web/models/sale.py
Normal file
16
sale_payment_web/models/sale.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from odoo import models, _
|
||||
|
||||
|
||||
class SaleOrder(models.Model):
|
||||
_inherit = 'sale.order'
|
||||
|
||||
def action_payment_register(self):
|
||||
return {
|
||||
'name': _('Register Payment'),
|
||||
'res_model': 'account.payment.register',
|
||||
'view_mode': 'form',
|
||||
'view_id': self.env.ref('account.view_account_payment_form_multi').id,
|
||||
'context': {'active_ids': self.ids, 'active_model': 'sale.order'},
|
||||
'target': 'new',
|
||||
'type': 'ir.actions.act_window',
|
||||
}
|
||||
2
sale_payment_web/security/ir.model.access.csv
Normal file
2
sale_payment_web/security/ir.model.access.csv
Normal file
@@ -0,0 +1,2 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_payment_method,account.payment.method,account.model_account_payment_method,group_payment_web,1,0,0,0
|
||||
|
9
sale_payment_web/security/sale_security.xml
Normal file
9
sale_payment_web/security/sale_security.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="group_payment_web" model="res.groups">
|
||||
<field name="name">Sale Order Payments</field>
|
||||
<field name="category_id" ref="base.module_category_hidden"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
1
sale_payment_web/tests/__init__.py
Normal file
1
sale_payment_web/tests/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from . import test_sale_payment
|
||||
23
sale_payment_web/tests/test_sale_payment.py
Normal file
23
sale_payment_web/tests/test_sale_payment.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from odoo.addons.sale.tests.test_sale_to_invoice import TestSaleToInvoice
|
||||
|
||||
|
||||
class TestSalePayment(TestSaleToInvoice):
|
||||
|
||||
def setUp(self):
|
||||
super(TestSalePayment, self).setUp()
|
||||
self.context = {
|
||||
'active_model': 'sale.order',
|
||||
'active_ids': [self.sale_order.id],
|
||||
'active_id': self.sale_order.id,
|
||||
}
|
||||
|
||||
def test_payment(self):
|
||||
self.sale_order.action_confirm()
|
||||
payment_wizard = self.env['account.payment.register'].with_context(self.context).create({})
|
||||
self.assertTrue(payment_wizard.journal_id)
|
||||
|
||||
payment_action = payment_wizard.create_payments()
|
||||
self.assertTrue(isinstance(payment_action, dict))
|
||||
payment = self.env[payment_action['res_model']].browse(payment_action['res_id'])
|
||||
self.assertTrue(payment.exists())
|
||||
self.assertEqual(payment.amount, self.sale_order.amount_total)
|
||||
15
sale_payment_web/views/sale_views.xml
Normal file
15
sale_payment_web/views/sale_views.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
|
||||
<record id="view_order_form_inherit" model="ir.ui.view">
|
||||
<field name="name">sale.order.form.inherit</field>
|
||||
<field name="model">sale.order</field>
|
||||
<field name="inherit_id" ref="sale.view_order_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//button[@name='action_draft']" position="after">
|
||||
<button name="action_payment_register" type="object" string="Register Payment" />
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
1
sale_payment_web/wizard/__init__.py
Normal file
1
sale_payment_web/wizard/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from . import account_payment_register
|
||||
120
sale_payment_web/wizard/account_payment_register.py
Normal file
120
sale_payment_web/wizard/account_payment_register.py
Normal file
@@ -0,0 +1,120 @@
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.addons.payment.controllers.portal import PaymentProcessing
|
||||
|
||||
|
||||
class AccountPaymentRegister(models.TransientModel):
|
||||
_inherit = 'account.payment.register'
|
||||
|
||||
sale_order_ids = fields.Many2many('sale.order', 'sale_order_payment_rel_transient', 'payment_id', 'sale_order_id',
|
||||
string="Sale Orders", copy=False, readonly=True)
|
||||
payment_token_id = fields.Many2one('payment.token', string="Saved payment token",
|
||||
domain="[('acquirer_id.capture_manually', '=', False), ('company_id', '=', company_id)]",
|
||||
help="Note that tokens from acquirers set to only authorize transactions "
|
||||
"(instead of capturing the amount) are not available.")
|
||||
company_id = fields.Many2one('res.company') # technical requirement for acquirer domain
|
||||
partner_id = fields.Many2one('res.partner') # technical payment mode/token domain
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields):
|
||||
# super implementation checks active_ids, but not active_model
|
||||
active_ids = self._context.get('active_ids')
|
||||
if self._context.get('active_model') != 'sale.order' or not active_ids:
|
||||
return super(AccountPaymentRegister, self).default_get(fields)
|
||||
|
||||
rec = super(AccountPaymentRegister, self.with_context(active_ids=None)).default_get(fields)
|
||||
sale_orders = self.env['sale.order'].browse(active_ids)
|
||||
company = sale_orders.mapped('company_id')
|
||||
if len(company) != 1:
|
||||
raise UserError('Can only register sale order payments for the sales in the same company.')
|
||||
partner = sale_orders.mapped('partner_id')
|
||||
if len(partner) != 1:
|
||||
raise UserError('Can only register sale order payments for the same customer.')
|
||||
account_receivable = partner.property_account_receivable_id
|
||||
if not account_receivable:
|
||||
raise UserError('Your partner must have an Account Receivable setup.')
|
||||
if 'sale_order_ids' not in rec:
|
||||
rec['sale_order_ids'] = [(6, 0, sale_orders.ids)]
|
||||
if 'company_id' not in rec:
|
||||
rec['company_id'] = sale_orders[0].company_id.id
|
||||
if 'partner_id' not in rec:
|
||||
rec['partner_id'] = sale_orders[0].partner_id.id
|
||||
if 'journal_id' not in rec:
|
||||
rec['journal_id'] = self.env['account.journal'].search([('company_id', '=', company.id), ('type', 'in', ('bank', 'cash'))], limit=1).id
|
||||
if 'payment_method_id' not in rec:
|
||||
domain = [('payment_type', '=', 'inbound')]
|
||||
rec['payment_method_id'] = self.env['account.payment.method'].search(domain, limit=1).id
|
||||
return rec
|
||||
|
||||
@api.onchange('partner_id', 'payment_method_id', 'journal_id')
|
||||
def _onchange_set_payment_token_id(self):
|
||||
res = {}
|
||||
|
||||
if not self.payment_method_id.code == 'electronic' or not self.partner_id or not self.journal_id:
|
||||
self.payment_token_id = False
|
||||
return res
|
||||
|
||||
partners = self.partner_id | self.partner_id.commercial_partner_id | self.partner_id.commercial_partner_id.child_ids
|
||||
domain = [('partner_id', 'in', partners.ids), ('acquirer_id.journal_id', '=', self.journal_id.id)]
|
||||
self.payment_token_id = self.env['payment.token'].search(domain, limit=1)
|
||||
|
||||
res['domain'] = {'payment_token_id': domain}
|
||||
return res
|
||||
|
||||
@api.onchange('journal_id', 'invoice_ids', 'sale_order_ids')
|
||||
def _onchange_journal(self):
|
||||
active_ids = self._context.get('active_ids')
|
||||
if self._context.get('active_model') != 'sale.order' or not active_ids:
|
||||
return super(AccountPaymentRegister, self).default_get(fields)
|
||||
journal = self.journal_id
|
||||
if journal:
|
||||
domain_payment = [('payment_type', '=', 'inbound'), ('id', 'in', journal.inbound_payment_method_ids.ids)]
|
||||
return {'domain': {'payment_method_id': domain_payment}}
|
||||
|
||||
def _prepare_communication_sale_orders(self, sale_orders):
|
||||
return " ".join(o.reference or o.name for o in sale_orders)
|
||||
|
||||
def _prepare_payment_vals_sale_orders(self, sale_orders):
|
||||
'''Create the payment values.
|
||||
|
||||
:param sale_orders: The sale orders to pay. In case of multiple
|
||||
documents.
|
||||
:return: The payment values as a dictionary.
|
||||
'''
|
||||
amount = sum(sale_orders.mapped('amount_total'))
|
||||
values = {
|
||||
'journal_id': self.journal_id.id,
|
||||
'payment_method_id': self.payment_method_id.id,
|
||||
'payment_date': self.payment_date,
|
||||
'communication': self._prepare_communication_sale_orders(sale_orders),
|
||||
# TODO sale orders need to get to transactions somehow
|
||||
# 'invoice_ids': [(6, 0, invoices.ids)],
|
||||
'payment_type': ('inbound' if amount > 0 else 'outbound'),
|
||||
'amount': abs(amount),
|
||||
'currency_id': sale_orders[0].currency_id.id,
|
||||
'partner_id': sale_orders[0].partner_id.id,
|
||||
'partner_type': 'customer',
|
||||
'payment_token_id': self.payment_token_id.id,
|
||||
}
|
||||
return values
|
||||
|
||||
def get_payments_vals(self):
|
||||
if self.sale_order_ids:
|
||||
return [self._prepare_payment_vals_sale_orders(self.sale_order_ids)]
|
||||
return super(AccountPaymentRegister, self).get_payments_vals()
|
||||
|
||||
def create_payments(self):
|
||||
if self.sale_order_ids:
|
||||
# user may not be able to create payment
|
||||
res = super(AccountPaymentRegister, self.sudo()).create_payments()
|
||||
else:
|
||||
res = super(AccountPaymentRegister, self).create_payments()
|
||||
if res and 'res_id' in res and self.sale_order_ids:
|
||||
payment = self.env['account.payment'].browse(res['res_id'])
|
||||
if payment.name: # if we don't have a name, then it started a transaction and that will be in chatter
|
||||
payment_link = payment._get_payment_chatter_link()
|
||||
for order in self.sale_order_ids:
|
||||
order.message_post(body=_('A %s payment has been registered: %s') % (payment.payment_method_code, payment_link))
|
||||
# return a 'dummy' action like object for tests
|
||||
return {'res_id': payment.id, 'res_model': payment._name}
|
||||
return res
|
||||
17
sale_payment_web/wizard/account_payment_register_views.xml
Normal file
17
sale_payment_web/wizard/account_payment_register_views.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
|
||||
<record id="view_account_payment_form_multi_inherit" model="ir.ui.view">
|
||||
<field name="name">account.payment.form.multi.inherit</field>
|
||||
<field name="model">account.payment.register</field>
|
||||
<field name="inherit_id" ref="account.view_account_payment_form_multi"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//group/group[1]" position="inside">
|
||||
<field name="company_id" invisible="1" />
|
||||
<field name="partner_id" invisible="1" />
|
||||
<field name="payment_token_id" nocreate="1" />
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user