[IMP] payment: Remove auto_confirm field and simplify views.
PURPOSE ======= The field auto_confirm is complicated to understand for common users. Furthermore, on of its options is only useful for authorize module. SPECIFICATION ============= Remove the auto_confirm field. The destinies of its options are the following: - none: Simply disappear. - authorize: Become capture_manually. It has nothing to do with the auto_confirm field has it's related to the autorize module (And could be extended to other payment acquirers too). - confirm_so: Remove it. Will be automatic, and we will always validate the sales order and generate the accounting entries on acquirer validation. - generate_and_pay_invoice: Is linked to the journal_id. The field journal_id is always set and we will use it to validate the sales order and generate the accounting entries on acquirer validation. Bonus: website_sale: Allow to create/validate invoice automatically on `Mark as Paid`
This commit is contained in:
@@ -155,7 +155,6 @@
|
||||
|
||||
<record id="payment_acquirer_custom" model="payment.acquirer">
|
||||
<field name="name">Custom</field>
|
||||
<field name="auto_confirm">none</field>
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
<field name="view_template_id" ref="default_acquirer_button"/>
|
||||
<field name="module_id" ref="base.module_payment_transfer"/>
|
||||
|
||||
@@ -10,7 +10,7 @@ class AccountPayment(models.Model):
|
||||
_inherit = 'account.payment'
|
||||
|
||||
payment_transaction_id = fields.Many2one('payment.transaction', string="Payment Transaction")
|
||||
payment_token_id = fields.Many2one('payment.token', string="Saved payment token", domain=[('acquirer_id.auto_confirm', '!=', 'authorize')],
|
||||
payment_token_id = fields.Many2one('payment.token', string="Saved payment token", domain=[('acquirer_id.capture_manually', '=', False)],
|
||||
help="Note that tokens from acquirers set to only authorize transactions (instead of capturing the amount) are not available.")
|
||||
|
||||
@api.onchange('partner_id')
|
||||
@@ -18,14 +18,14 @@ class AccountPayment(models.Model):
|
||||
res = {}
|
||||
if self.partner_id:
|
||||
partners = self.partner_id | self.partner_id.commercial_partner_id | self.partner_id.commercial_partner_id.child_ids
|
||||
res['domain'] = {'payment_token_id': [('partner_id', 'in', partners.ids), ('acquirer_id.auto_confirm', '!=', 'authorize')]}
|
||||
res['domain'] = {'payment_token_id': [('partner_id', 'in', partners.ids), ('acquirer_id.capture_manually', '=', False)]}
|
||||
|
||||
return res
|
||||
|
||||
@api.onchange('payment_method_id', 'journal_id')
|
||||
def _onchange_payment_method(self):
|
||||
if self.payment_method_code == 'electronic':
|
||||
self.payment_token_id = self.env['payment.token'].search([('partner_id', '=', self.partner_id.id), ('acquirer_id.auto_confirm', '!=', 'authorize')], limit=1)
|
||||
self.payment_token_id = self.env['payment.token'].search([('partner_id', '=', self.partner_id.id), ('acquirer_id.capture_manually', '=', False)], limit=1)
|
||||
else:
|
||||
self.payment_token_id = False
|
||||
|
||||
@@ -38,7 +38,7 @@ class AccountPayment(models.Model):
|
||||
return account_payment
|
||||
|
||||
def _do_payment(self):
|
||||
if self.payment_token_id.acquirer_id.auto_confirm == 'authorize':
|
||||
if self.payment_token_id.acquirer_id.capture_manually:
|
||||
raise ValidationError(_('This feature is not available for payment acquirers set to the "Authorize" mode.\n'
|
||||
'Please use a token from another provider than %s.') % self.payment_token_id.acquirer_id.name)
|
||||
reference = "P-%s-%s" % (self.id, datetime.datetime.now().strftime('%y%m%d_%H%M%S'))
|
||||
|
||||
@@ -71,15 +71,22 @@ class PaymentAcquirer(models.Model):
|
||||
website_published = fields.Boolean(
|
||||
'Visible in Portal / Website', copy=False,
|
||||
help="Make this payment acquirer available (Customer invoices, etc.)")
|
||||
auto_confirm = fields.Selection([
|
||||
('none', 'No automatic confirmation'),
|
||||
('authorize', 'Authorize the amount and confirm the order on acquirer confirmation (capture manually)'),
|
||||
('confirm_so', 'Authorize & capture the amount and confirm the order on acquirer confirmation'),
|
||||
('generate_and_pay_invoice', 'Authorize & capture the amount, confirm the order and auto-validate the invoice on acquirer confirmation')],
|
||||
string='Order Confirmation', default='confirm_so', required=True)
|
||||
# Formerly associated to `authorize` option from auto_confirm
|
||||
capture_manually = fields.Boolean(string="Capture Amount Manually",
|
||||
help="Capture the amount from Odoo, when the delivery is completed.")
|
||||
# Formerly associated to `generate_and_pay_invoice` option from auto_confirm
|
||||
journal_id = fields.Many2one(
|
||||
'account.journal', 'Payment Journal',
|
||||
help="Account journal used for automatic payment reconciliation.")
|
||||
'account.journal', 'Payment Journal', domain=[('type', '=', 'bank')],
|
||||
default=lambda self: self.env['account.journal'].search([('type', '=', 'bank')], limit=1),
|
||||
help="""Payments will be registered into this journal. If you get paid straight on your bank account,
|
||||
select your bank account. If you get paid in batch for several transactions, create a specific
|
||||
payment journal for this payment acquirer to easily manage the bank reconciliation. You hold
|
||||
the amount in a temporary transfer account of your books (created automatically when you create
|
||||
the payment journal). Then when you get paid on your bank account by the payment acquirer, you
|
||||
reconcile the bank statement line with this temporary transfer account. Use reconciliation
|
||||
templates to do it in one-click.""")
|
||||
specific_countries = fields.Boolean(string="Specific Countries",
|
||||
help="If you leave it empty, the payment acquirer will be available for all the countries.")
|
||||
country_ids = fields.Many2many(
|
||||
'res.country', 'payment_country_rel',
|
||||
'payment_id', 'country_id', 'Countries',
|
||||
@@ -116,7 +123,7 @@ class PaymentAcquirer(models.Model):
|
||||
"If you manage subscriptions (recurring invoicing), you need it to automatically charge the customer when you "
|
||||
"issue an invoice.")
|
||||
token_implemented = fields.Boolean('Saving Card Data supported', compute='_compute_feature_support')
|
||||
|
||||
authorize_implemented = fields.Boolean('Authorize Mechanism Supported', compute='_compute_feature_support')
|
||||
fees_implemented = fields.Boolean('Fees Computation Supported', compute='_compute_feature_support')
|
||||
fees_active = fields.Boolean('Add Extra Fees')
|
||||
fees_dom_fixed = fields.Float('Fixed domestic fees')
|
||||
@@ -142,11 +149,11 @@ class PaymentAcquirer(models.Model):
|
||||
"resized as a 64x64px image, with aspect ratio preserved. "
|
||||
"Use this field anywhere a small image is required.")
|
||||
|
||||
@api.multi
|
||||
def _compute_feature_support(self):
|
||||
feature_support = self._get_feature_support()
|
||||
for acquirer in self:
|
||||
acquirer.fees_implemented = acquirer.provider in feature_support['fees']
|
||||
acquirer.authorize_implemented = acquirer.provider in feature_support['authorize']
|
||||
acquirer.token_implemented = acquirer.provider in feature_support['tokenize']
|
||||
|
||||
@api.multi
|
||||
@@ -158,13 +165,6 @@ class PaymentAcquirer(models.Model):
|
||||
return False
|
||||
return True
|
||||
|
||||
@api.constrains('auto_confirm')
|
||||
def _check_authorization_support(self):
|
||||
for acquirer in self:
|
||||
if acquirer.auto_confirm == 'authorize' and acquirer.provider not in self._get_feature_support()['authorize']:
|
||||
raise ValidationError(_('You cannot capture payments manually with this payment method. Please choose another Order Confirmation mode (in Configuration tab).'))
|
||||
return True
|
||||
|
||||
_constraints = [
|
||||
(_check_required_if_provider, 'Required fields not filled', []),
|
||||
]
|
||||
|
||||
@@ -16,16 +16,11 @@
|
||||
<field name="environment" invisible='1'/>
|
||||
<field name="fees_implemented" invisible='1'/>
|
||||
<field name="token_implemented" invisible='1'/>
|
||||
<field name="authorize_implemented" invisible="1"/>
|
||||
<sheet>
|
||||
<field name="module_id" invisible="1"/>
|
||||
<field name="module_state" invisible="1"/>
|
||||
<div class="oe_button_box" name="button_box" attrs="{'invisible': ['&', ('module_id', '!=', False), ('module_state', '!=', 'installed')]}">
|
||||
<button name="toggle_website_published"
|
||||
class="oe_stat_button"
|
||||
type="object" icon="fa-globe">
|
||||
<field name="website_published" widget="boolean_button"
|
||||
options='{"terminology": {"string_true": "Published", "hover_false": "Publish"}}'/>
|
||||
</button>
|
||||
<!-- transfer referenced here due to view inheritance issue in current master (post-saas-16) -->
|
||||
<button name="toggle_environment_value"
|
||||
attrs="{'invisible': ['|', ('environment', '=', 'test'), ('provider', '=', 'transfer')]}"
|
||||
@@ -46,6 +41,12 @@
|
||||
<span class="o_stat_text">Environment</span>
|
||||
</div>
|
||||
</button>
|
||||
<button name="toggle_website_published"
|
||||
class="oe_stat_button"
|
||||
type="object" icon="fa-globe">
|
||||
<field name="website_published" widget="boolean_button"
|
||||
options='{"terminology": {"string_true": "Published", "hover_false": "Publish"}}'/>
|
||||
</button>
|
||||
</div>
|
||||
<field name="image" widget='image' class="oe_avatar" options='{"preview_image": "image_medium"}'/>
|
||||
<div class="oe_title">
|
||||
@@ -80,10 +81,16 @@
|
||||
</page>
|
||||
<page string="Configuration">
|
||||
<group name="acquirer_config">
|
||||
<field name="auto_confirm" widget="radio"/>
|
||||
<field name="capture_manually" attrs="{'invisible': [('authorize_implemented', '=', False)]}"/>
|
||||
<field name="save_token" widget="radio" attrs="{'invisible': [('token_implemented', '=', False)]}"/>
|
||||
<field name="journal_id" attrs="{'required': [('auto_confirm', '=', 'generate_and_pay_invoice')], 'invisible': [('auto_confirm', '!=', 'generate_and_pay_invoice')]}"/>
|
||||
<field name="country_ids" widget="many2many_tags"/>
|
||||
<field name="fees_active" attrs="{'invisible': [('fees_implemented', '=', False)]}"/>
|
||||
<field name="fees_dom_fixed" attrs="{'invisible': [('fees_active', '=', False)]}"/>
|
||||
<field name="fees_dom_var" attrs="{'invisible': [('fees_active', '=', False)]}"/>
|
||||
<field name="fees_int_fixed" attrs="{'invisible': [('fees_active', '=', False)]}"/>
|
||||
<field name="fees_int_var" attrs="{'invisible': [('fees_active', '=', False)]}"/>
|
||||
<field name="journal_id" context="{'default_type': 'bank'}" required="1"/>
|
||||
<field name="specific_countries"/>
|
||||
<field name="country_ids" widget="many2many_tags" attrs="{'invisible': [('specific_countries', '=', False)]}"/>
|
||||
<label for="view_template_id" groups="base.group_no_one"/>
|
||||
<div groups="base.group_no_one">
|
||||
<field name="view_template_id" nolabel="1"/>
|
||||
@@ -106,13 +113,6 @@
|
||||
</div>
|
||||
<field name="registration_view_template_id" groups="base.group_no_one"/>
|
||||
</group>
|
||||
<group name="fees_config" attrs="{'invisible': [('fees_implemented', '=', False)]}">
|
||||
<field name="fees_active"/>
|
||||
<field name="fees_dom_fixed" attrs="{'invisible': [('fees_active', '=', False)]}"/>
|
||||
<field name="fees_dom_var" attrs="{'invisible': [('fees_active', '=', False)]}"/>
|
||||
<field name="fees_int_fixed" attrs="{'invisible': [('fees_active', '=', False)]}"/>
|
||||
<field name="fees_int_var" attrs="{'invisible': [('fees_active', '=', False)]}"/>
|
||||
</group>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
|
||||
@@ -66,7 +66,7 @@ class PaymentAcquirerAuthorize(models.Model):
|
||||
'x_trans_key': self.authorize_transaction_key,
|
||||
'x_amount': str(values['amount']),
|
||||
'x_show_form': 'PAYMENT_FORM',
|
||||
'x_type': 'AUTH_CAPTURE' if self.auto_confirm != 'authorize' else 'AUTH_ONLY',
|
||||
'x_type': 'AUTH_CAPTURE' if not self.capture_manually else 'AUTH_ONLY',
|
||||
'x_method': 'CC',
|
||||
'x_fp_sequence': '%s%s' % (self.id, int(time.time())),
|
||||
'x_version': '3.1',
|
||||
@@ -246,7 +246,7 @@ class TxAuthorize(models.Model):
|
||||
def authorize_s2s_do_transaction(self, **data):
|
||||
self.ensure_one()
|
||||
transaction = AuthorizeAPI(self.acquirer_id)
|
||||
if self.acquirer_id.auto_confirm != "authorize":
|
||||
if not self.acquirer_id.capture_manually:
|
||||
res = transaction.auth_and_capture(self.payment_token_id, self.amount, self.reference)
|
||||
else:
|
||||
res = transaction.authorize(self.payment_token_id, self.amount, self.reference)
|
||||
|
||||
@@ -25,7 +25,7 @@ class AuthorizeCommon(PaymentAcquirerCommon):
|
||||
# get the authorize account
|
||||
self.authorize = self.env.ref('payment.payment_acquirer_authorize')
|
||||
# Be sure to be in 'capture' mode
|
||||
self.authorize.auto_confirm = 'confirm_so'
|
||||
# self.authorize.auto_confirm = 'confirm_so'
|
||||
|
||||
|
||||
@odoo.tests.common.at_install(True)
|
||||
@@ -219,7 +219,7 @@ class AuthorizeForm(AuthorizeCommon):
|
||||
|
||||
# switch to 'authorize only'
|
||||
# create authorize only s2s transaction & capture it
|
||||
self.authorize.auto_confirm = 'authorize'
|
||||
self.authorize.capture_manually = True
|
||||
transaction = self.env['payment.transaction'].create({
|
||||
'amount': 500,
|
||||
'acquirer_id': authorize.id,
|
||||
@@ -236,7 +236,7 @@ class AuthorizeForm(AuthorizeCommon):
|
||||
self.assertEqual(transaction.state, 'done')
|
||||
|
||||
# create authorize only s2s transaction & void it
|
||||
self.authorize.auto_confirm = 'authorize'
|
||||
self.authorize.capture_manually = True
|
||||
transaction = self.env['payment.transaction'].create({
|
||||
'amount': 500,
|
||||
'acquirer_id': authorize.id,
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
<field name="provider">transfer</field>
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
<field name="view_template_id" ref="transfer_acquirer_button"/>
|
||||
<field name="auto_confirm">none</field>
|
||||
<field name="environment">test</field>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="pre_msg"><![CDATA[
|
||||
|
||||
@@ -17,9 +17,6 @@
|
||||
<field name="error_msg" position="attributes">
|
||||
<attribute name="attrs">{'invisible': [('provider', '=', 'transfer')]}</attribute>
|
||||
</field>
|
||||
<field name="auto_confirm" position="attributes">
|
||||
<attribute name="attrs">{'invisible': [('provider', '=', 'transfer')]}</attribute>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
||||
@@ -157,7 +157,7 @@
|
||||
<button name="action_quotation_send" string="Send by Email" type="object" states="draft" class="btn-primary"/>
|
||||
<button name="action_quotation_send" type="object" string="Send PRO-FORMA" groups="sale.group_proforma_sales" class="btn-primary" attrs="{'invisible': ['|', ('state', 'in', ['sent', 'sale']), ('invoice_count','>=',1)]}" context="{'proforma': True}"/>
|
||||
<button name="print_quotation" string="Print" type="object" states="draft" class="btn-primary o_sale_print"/>
|
||||
<button name="action_confirm" states="sent" string="Confirm Sale" class="btn-primary" type="object" />
|
||||
<button name="action_confirm" id="action_confirm" states="sent" string="Confirm Sale" class="btn-primary" type="object" />
|
||||
<button name="action_confirm" states="draft" string="Confirm Sale" type="object" />
|
||||
<button name="action_quotation_send" type="object" string="Send PRO-FORMA" groups="sale.group_proforma_sales" attrs="{'invisible': ['|', ('state', '=', 'draft'), ('invoice_count','>=',1)]}" context="{'proforma': True}"/>
|
||||
<button name="print_quotation" string="Print" type="object" states="sent,sale" class="o_sale_print"/>
|
||||
|
||||
@@ -11,6 +11,7 @@ class PaymentTransaction(models.Model):
|
||||
_inherit = 'payment.transaction'
|
||||
|
||||
# link with the sales order
|
||||
# YTI FIXME: The auto_join seems useless
|
||||
sale_order_id = fields.Many2one('sale.order', string='Sales Order', auto_join=True)
|
||||
|
||||
def _generate_and_pay_invoice(self, tx, acquirer_name):
|
||||
@@ -29,13 +30,15 @@ class PaymentTransaction(models.Model):
|
||||
acquirer_name, created_invoice.name, created_invoice.id, tx.sale_order_id.name, tx.sale_order_id.id)
|
||||
|
||||
created_invoice.action_invoice_open()
|
||||
if tx.acquirer_id.journal_id:
|
||||
if not tx.acquirer_id.journal_id:
|
||||
default_journal = self.env['account.journal'].search([('type', '=', 'bank')], limit=1)
|
||||
if not default_journal:
|
||||
_logger.warning('<%s> transaction completed, could not auto-generate payment for %s (ID %s) (no journal set on acquirer)',
|
||||
acquirer_name, tx.sale_order_id.name, tx.sale_order_id.id)
|
||||
tx.acquirer_id.journal_id = default_journal
|
||||
created_invoice.pay_and_reconcile(tx.acquirer_id.journal_id, pay_amount=created_invoice.amount_total)
|
||||
if created_invoice.payment_ids:
|
||||
created_invoice.payment_ids[0].payment_transaction_id = tx
|
||||
else:
|
||||
_logger.warning('<%s> transaction completed, could not auto-generate payment for %s (ID %s) (no journal set on acquirer)',
|
||||
acquirer_name, tx.sale_order_id.name, tx.sale_order_id.id)
|
||||
else:
|
||||
_logger.warning('<%s> transaction completed, could not auto-generate invoice for %s (ID %s)',
|
||||
acquirer_name, tx.sale_order_id.name, tx.sale_order_id.id)
|
||||
@@ -69,15 +72,13 @@ class PaymentTransaction(models.Model):
|
||||
if amount_matches:
|
||||
if not acquirer_name:
|
||||
acquirer_name = tx.sale_order_id.payment_acquirer_id.provider or 'unknown'
|
||||
if tx.state == 'authorized' and tx.acquirer_id.auto_confirm == 'authorize':
|
||||
if tx.state == 'authorized' and tx.acquirer_id.capture_manually:
|
||||
_logger.info('<%s> transaction authorized, auto-confirming order %s (ID %s)', acquirer_name, tx.sale_order_id.name, tx.sale_order_id.id)
|
||||
tx.sale_order_id.with_context(send_email=True).action_confirm()
|
||||
if tx.state == 'done' and tx.acquirer_id.auto_confirm in ['confirm_so', 'generate_and_pay_invoice']:
|
||||
if tx.state == 'done':
|
||||
_logger.info('<%s> transaction completed, auto-confirming order %s (ID %s)', acquirer_name, tx.sale_order_id.name, tx.sale_order_id.id)
|
||||
tx.sale_order_id.with_context(send_email=True).action_confirm()
|
||||
|
||||
if tx.acquirer_id.auto_confirm == 'generate_and_pay_invoice':
|
||||
self._generate_and_pay_invoice(tx, acquirer_name)
|
||||
self._generate_and_pay_invoice(tx, acquirer_name)
|
||||
elif tx.state not in ['cancel', 'error'] and tx.sale_order_id.state == 'draft':
|
||||
_logger.info('<%s> transaction pending/to confirm manually, sending quote email for order %s (ID %s)', acquirer_name, tx.sale_order_id.name, tx.sale_order_id.id)
|
||||
tx.sale_order_id.force_quotation_send()
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, exceptions, fields, models
|
||||
from odoo import api, exceptions, fields, models, _
|
||||
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class SaleOrder(models.Model):
|
||||
@@ -9,11 +11,18 @@ class SaleOrder(models.Model):
|
||||
_inherit = 'sale.order'
|
||||
|
||||
website_url = fields.Char('Website URL', compute='_website_url', help='The full URL to access the document through the website.')
|
||||
payment_transaction_count = fields.Integer(compute='_compute_payment_transaction_count', string="Number of payment transactions")
|
||||
|
||||
def _website_url(self):
|
||||
for so in self:
|
||||
so.website_url = '/my/orders/%s' % (so.id)
|
||||
|
||||
def _compute_payment_transaction_count(self):
|
||||
transaction_data = self.env['payment.transaction'].read_group([('sale_order_id', 'in', self.ids)], ['sale_order_id'], ['sale_order_id'])
|
||||
mapped_data = dict([(m['sale_order_id'][0], m['sale_order_id_count']) for m in transaction_data])
|
||||
for order in self:
|
||||
order.payment_transaction_count = mapped_data.get(order.id, 0)
|
||||
|
||||
@api.multi
|
||||
def get_access_action(self):
|
||||
""" Instead of the classic form view, redirect to the online quote for
|
||||
@@ -62,6 +71,24 @@ class SaleOrder(models.Model):
|
||||
model=self._name,
|
||||
res_id=self.id)[self.partner_id.id]
|
||||
|
||||
def action_view_transaction(self):
|
||||
action = {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': 'Payment Transactions',
|
||||
'res_model': 'payment.transaction',
|
||||
}
|
||||
if self.payment_transaction_count == 1:
|
||||
action.update({
|
||||
'res_id': self.env['payment.transaction'].search([('sale_order_id', '=', self.id)]).id,
|
||||
'view_mode': 'form',
|
||||
})
|
||||
else:
|
||||
action.update({
|
||||
'view_mode': 'tree,form',
|
||||
'domain': [('sale_order_id', '=', self.id)],
|
||||
})
|
||||
return action
|
||||
|
||||
|
||||
class SaleOrderLine(models.Model):
|
||||
|
||||
|
||||
@@ -1,15 +1,26 @@
|
||||
<?xml version="1.0"?>
|
||||
<odoo>
|
||||
<record id="view_account_invoice_filter_share" model="ir.ui.view">
|
||||
<field name="name">account.invoice.select.share</field>
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="inherit_id" ref="account.view_account_invoice_filter"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr='//field[@name="user_id"]' position="attributes">
|
||||
<attribute name="domain">[('share','=', False)]</attribute>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_account_invoice_filter_share" model="ir.ui.view">
|
||||
<field name="name">account.invoice.select.share</field>
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="inherit_id" ref="account.view_account_invoice_filter"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr='//field[@name="user_id"]' position="attributes">
|
||||
<attribute name="domain">[('share','=', False)]</attribute>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="sale_order_view_form" model="ir.ui.view">
|
||||
<field name="name">sale.order.form</field>
|
||||
<field name="model">sale.order</field>
|
||||
<field name="inherit_id" ref="sale.view_order_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//div[@name='button_box']" position="inside">
|
||||
<button name="action_view_transaction" type="object" class="oe_stat_button" icon="fa-money" attrs="{'invisible': [('payment_transaction_count', '=', 0)]}">
|
||||
<field name="payment_transaction_count" widget="statinfo" string="Transactions"/>
|
||||
</button>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
||||
@@ -768,7 +768,7 @@ class WebsiteSale(http.Controller):
|
||||
tx.partner_id == tx.sale_order_id.partner_id):
|
||||
try:
|
||||
s2s_result = tx.s2s_do_transaction()
|
||||
valid_state = 'authorized' if tx.acquirer_id.auto_confirm == 'authorize' else 'done'
|
||||
valid_state = 'authorized' if tx.acquirer_id.capture_manually else 'done'
|
||||
if not s2s_result or tx.state != valid_state:
|
||||
return dict(success=False, error=_("Payment transaction failed (%s)") % tx.state_message)
|
||||
else:
|
||||
|
||||
@@ -70,9 +70,10 @@ class WebsiteConfigSettings(models.TransientModel):
|
||||
group='base.group_portal,base.group_user,base.group_public')
|
||||
|
||||
default_invoice_policy = fields.Selection([
|
||||
('order', 'Invoice what is ordered (automatic)'),
|
||||
('delivery', 'Invoice what is delivered (manual)')
|
||||
('order', 'Invoice what is ordered'),
|
||||
('delivery', 'Invoice what is delivered')
|
||||
], 'Invoicing Policy', default='order')
|
||||
automatic_invoice = fields.Boolean("Automatic Invoice")
|
||||
|
||||
group_multi_currency = fields.Boolean(string='Multi-Currencies', implied_group='base.group_multi_currency')
|
||||
|
||||
@@ -81,6 +82,16 @@ class WebsiteConfigSettings(models.TransientModel):
|
||||
('subtotal', 'Tax-Excluded Prices')],
|
||||
"Product Prices", default='total')
|
||||
|
||||
@api.multi
|
||||
def set_automatic_invoice(self):
|
||||
value = self.module_account_invoicing and self.default_invoice_policy == 'order' and self.automatic_invoice
|
||||
self.env['ir.config_parameter'].sudo().set_param('website_sale.automatic_invoice', value)
|
||||
|
||||
@api.model
|
||||
def get_default_automatic_invoice(self, fields):
|
||||
value = self.env['ir.config_parameter'].sudo().get_param('website_sale.automatic_invoice', default=False)
|
||||
return {'automatic_invoice': value}
|
||||
|
||||
@api.model
|
||||
def get_default_sale_delivery_settings(self, fields):
|
||||
sale_delivery_settings = 'none'
|
||||
|
||||
@@ -23,6 +23,16 @@ class SaleOrder(models.Model):
|
||||
payment_acquirer_id = fields.Many2one('payment.acquirer', string='Payment Acquirer', copy=False)
|
||||
payment_tx_id = fields.Many2one('payment.transaction', string='Last Transaction', copy=False)
|
||||
only_services = fields.Boolean(compute='_compute_cart_info', string='Only Services')
|
||||
can_directly_mark_as_paid = fields.Boolean(compute='_compute_can_directly_mark_as_paid',
|
||||
string="Can be directly marked as paid", store=True,
|
||||
help="""Checked if the sales order can directly be marked as paid, i.e. if the quotation
|
||||
is sent or confirmed and if the payment acquire is of the type transfer or manual""")
|
||||
|
||||
@api.depends('state', 'payment_tx_id', 'payment_tx_id.state',
|
||||
'payment_acquirer_id', 'payment_acquirer_id.provider')
|
||||
def _compute_can_directly_mark_as_paid(self):
|
||||
for order in self:
|
||||
order.can_directly_mark_as_paid = order.state in ['sent', 'sale'] and order.payment_tx_id.state != 'done' and order.payment_acquirer_id.provider in ['transfer', 'manual']
|
||||
|
||||
@api.multi
|
||||
@api.depends('website_order_line.product_uom_qty', 'website_order_line.product_id')
|
||||
@@ -166,6 +176,22 @@ class SaleOrder(models.Model):
|
||||
accessory_products -= order.website_order_line.mapped('product_id')
|
||||
return random.sample(accessory_products, len(accessory_products))
|
||||
|
||||
def action_mark_as_paid(self):
|
||||
""" Mark directly a sales order as paid if:
|
||||
- State: Quotation Sent, or sales order
|
||||
- Provider: wire transfer or manual config
|
||||
The transaction is marked as done
|
||||
The invoice may be generated and marked as paid if configured in the website settings
|
||||
"""
|
||||
self.ensure_one()
|
||||
if self.can_directly_mark_as_paid:
|
||||
self.action_confirm()
|
||||
if self.env['ir.config_parameter'].sudo().get_param('website_sale.automatic_invoice', default=False):
|
||||
self.payment_tx_id._generate_and_pay_invoice(self.payment_tx_id, self.payment_acquirer_id.provider)
|
||||
self.payment_tx_id.state = 'done'
|
||||
else:
|
||||
raise ValidationError(_("The quote should be sent and the payment acquirer type should be manual or wire transfer"))
|
||||
|
||||
|
||||
class ResCountry(models.Model):
|
||||
_inherit = 'res.country'
|
||||
|
||||
@@ -15,10 +15,7 @@
|
||||
<td>
|
||||
<strong>Payment Method:</strong> <t t-esc="doc.payment_acquirer_id.name"/><br />
|
||||
<strong>Payment Status:</strong>
|
||||
<t t-if="doc.payment_acquirer_id.auto_confirm == 'none'">
|
||||
<t t-raw="doc.payment_acquirer_id.pending_msg"/>
|
||||
</t>
|
||||
<t t-if="doc.payment_acquirer_id.auto_confirm in ['confirm_so', 'generate_and_pay_invoice']">
|
||||
<t t-if="doc.payment_acquirer_id.journal_id">
|
||||
<t t-if="doc.payment_tx_id.state == 'pending'">
|
||||
<t t-raw="doc.payment_acquirer_id.pending_msg"/>
|
||||
</t>
|
||||
|
||||
@@ -227,15 +227,26 @@
|
||||
<div class="o_setting_right_pane">
|
||||
<label for="module_account_invoicing"/>
|
||||
<div class="text-muted">
|
||||
Send invoices to customers
|
||||
Issue invoices to customers
|
||||
</div>
|
||||
<div class="content-group">
|
||||
<div class="content-group" attrs="{'invisible': [('module_account_invoicing', '=', False)]}">
|
||||
<div class="mt16">
|
||||
<field name="default_invoice_policy" class="o_light_label" widget="radio"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-6 o_setting_box" attrs="{'invisible': ['|', ('default_invoice_policy', '=', 'delivery'), ('module_account_invoicing', '=', False)]}">
|
||||
<div class="o_setting_left_pane">
|
||||
<field name="automatic_invoice" nolabel="1"/>
|
||||
</div>
|
||||
<div class="o_setting_right_pane">
|
||||
<label for="automatic_invoice"/>
|
||||
<div class="text-muted">
|
||||
Issue the invoice automatically at the order confirmation
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h2>Shipping</h2>
|
||||
<div class="row mt16 o_settings_container" id="sale_shipping_settings">
|
||||
|
||||
@@ -133,4 +133,21 @@
|
||||
action="product.variants_action"
|
||||
parent="menu_product_settings" groups="product.group_product_variant" sequence="2"/>
|
||||
|
||||
<record id="sale_order_view_form" model="ir.ui.view">
|
||||
<field name="name">sale.order.form</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_quotation_send']" position="after">
|
||||
<button name="action_mark_as_paid" type="object" string="Mark as Paid" states="sent,sale" class="btn-primary" attrs="{'invisible': [('can_directly_mark_as_paid', '=', False)]}"/>
|
||||
</xpath>
|
||||
<field name="partner_id" position="after">
|
||||
<field name="can_directly_mark_as_paid" invisible="1"/>
|
||||
</field>
|
||||
<xpath expr="//button[@id='action_confirm']" position="attributes">
|
||||
<attribute name="attrs">{'invisible': [('can_directly_mark_as_paid', '=', True)]}</attribute>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
Reference in New Issue
Block a user