summaryrefslogtreecommitdiff
path: root/addons
diff options
context:
space:
mode:
authorYannick Tivisse <yti@odoo.com>2017-05-24 11:40:19 +0200
committerYannick Tivisse <yti@odoo.com>2017-06-02 15:31:47 +0200
commit4aa2fad313112a7a45a86b67e806f5a3af22d73b (patch)
treeae84130085545019a33b0e6f92687686f24a07d6 /addons
parent4fe4c1e8518f3adbd7406f2a75f98d7c25aa9a90 (diff)
[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`
Diffstat (limited to 'addons')
-rw-r--r--addons/payment/data/payment_acquirer_data.xml1
-rw-r--r--addons/payment/models/account_payment.py8
-rw-r--r--addons/payment/models/payment_acquirer.py34
-rw-r--r--addons/payment/views/payment_views.xml32
-rw-r--r--addons/payment_authorize/models/payment.py4
-rw-r--r--addons/payment_authorize/tests/test_authorize.py6
-rw-r--r--addons/payment_transfer/data/payment_acquirer_data.xml1
-rw-r--r--addons/payment_transfer/views/payment_views.xml3
-rw-r--r--addons/sale/views/sale_views.xml2
-rw-r--r--addons/website_portal_sale/models/payment.py19
-rw-r--r--addons/website_portal_sale/models/sale_order.py29
-rw-r--r--addons/website_portal_sale/views/sale_order_views.xml33
-rw-r--r--addons/website_sale/controllers/main.py2
-rw-r--r--addons/website_sale/models/res_config.py15
-rw-r--r--addons/website_sale/models/sale_order.py26
-rw-r--r--addons/website_sale/views/report_shop_saleorder.xml5
-rw-r--r--addons/website_sale/views/res_config_view.xml15
-rw-r--r--addons/website_sale/views/sale_order_views.xml17
18 files changed, 174 insertions, 78 deletions
diff --git a/addons/payment/data/payment_acquirer_data.xml b/addons/payment/data/payment_acquirer_data.xml
index 0271aebdd52..174c8b4a998 100644
--- a/addons/payment/data/payment_acquirer_data.xml
+++ b/addons/payment/data/payment_acquirer_data.xml
@@ -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"/>
diff --git a/addons/payment/models/account_payment.py b/addons/payment/models/account_payment.py
index 60ddcc60b4d..5a15e71e6e5 100644
--- a/addons/payment/models/account_payment.py
+++ b/addons/payment/models/account_payment.py
@@ -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'))
diff --git a/addons/payment/models/payment_acquirer.py b/addons/payment/models/payment_acquirer.py
index 92f0da0bc74..e36b344a52d 100644
--- a/addons/payment/models/payment_acquirer.py
+++ b/addons/payment/models/payment_acquirer.py
@@ -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', []),
]
diff --git a/addons/payment/views/payment_views.xml b/addons/payment/views/payment_views.xml
index 86d795802b4..ef611b73e81 100644
--- a/addons/payment/views/payment_views.xml
+++ b/addons/payment/views/payment_views.xml
@@ -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': ['&amp;', ('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>
diff --git a/addons/payment_authorize/models/payment.py b/addons/payment_authorize/models/payment.py
index a80627dbf35..24d57cb0db7 100644
--- a/addons/payment_authorize/models/payment.py
+++ b/addons/payment_authorize/models/payment.py
@@ -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)
diff --git a/addons/payment_authorize/tests/test_authorize.py b/addons/payment_authorize/tests/test_authorize.py
index d19690284a8..b02d3d81209 100644
--- a/addons/payment_authorize/tests/test_authorize.py
+++ b/addons/payment_authorize/tests/test_authorize.py
@@ -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,
diff --git a/addons/payment_transfer/data/payment_acquirer_data.xml b/addons/payment_transfer/data/payment_acquirer_data.xml
index 77ff2e2381d..0eee54a9259 100644
--- a/addons/payment_transfer/data/payment_acquirer_data.xml
+++ b/addons/payment_transfer/data/payment_acquirer_data.xml
@@ -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[
diff --git a/addons/payment_transfer/views/payment_views.xml b/addons/payment_transfer/views/payment_views.xml
index 127ab7b1238..a126bb67626 100644
--- a/addons/payment_transfer/views/payment_views.xml
+++ b/addons/payment_transfer/views/payment_views.xml
@@ -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>
diff --git a/addons/sale/views/sale_views.xml b/addons/sale/views/sale_views.xml
index ca5c95153df..136c4dc043f 100644
--- a/addons/sale/views/sale_views.xml
+++ b/addons/sale/views/sale_views.xml
@@ -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','&gt;=',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','&gt;=',1)]}" context="{'proforma': True}"/>
<button name="print_quotation" string="Print" type="object" states="sent,sale" class="o_sale_print"/>
diff --git a/addons/website_portal_sale/models/payment.py b/addons/website_portal_sale/models/payment.py
index 9842567abb1..7d442871c5d 100644
--- a/addons/website_portal_sale/models/payment.py
+++ b/addons/website_portal_sale/models/payment.py
@@ -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()
diff --git a/addons/website_portal_sale/models/sale_order.py b/addons/website_portal_sale/models/sale_order.py
index 8ec2592ab8d..ecbdbef107a 100644
--- a/addons/website_portal_sale/models/sale_order.py
+++ b/addons/website_portal_sale/models/sale_order.py
@@ -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):
diff --git a/addons/website_portal_sale/views/sale_order_views.xml b/addons/website_portal_sale/views/sale_order_views.xml
index 2075deb64c9..0a06daf69fd 100644
--- a/addons/website_portal_sale/views/sale_order_views.xml
+++ b/addons/website_portal_sale/views/sale_order_views.xml
@@ -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>
diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py
index 8af8e148a9e..ae104629215 100644
--- a/addons/website_sale/controllers/main.py
+++ b/addons/website_sale/controllers/main.py
@@ -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:
diff --git a/addons/website_sale/models/res_config.py b/addons/website_sale/models/res_config.py
index 629c4f77a52..c6eeb9d328c 100644
--- a/addons/website_sale/models/res_config.py
+++ b/addons/website_sale/models/res_config.py
@@ -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'
diff --git a/addons/website_sale/models/sale_order.py b/addons/website_sale/models/sale_order.py
index 4b007b59bd2..0779899f999 100644
--- a/addons/website_sale/models/sale_order.py
+++ b/addons/website_sale/models/sale_order.py
@@ -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'
diff --git a/addons/website_sale/views/report_shop_saleorder.xml b/addons/website_sale/views/report_shop_saleorder.xml
index 753e902397a..f8fd2600c7b 100644
--- a/addons/website_sale/views/report_shop_saleorder.xml
+++ b/addons/website_sale/views/report_shop_saleorder.xml
@@ -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>
diff --git a/addons/website_sale/views/res_config_view.xml b/addons/website_sale/views/res_config_view.xml
index cb270375b89..56ea5e36c31 100644
--- a/addons/website_sale/views/res_config_view.xml
+++ b/addons/website_sale/views/res_config_view.xml
@@ -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">
diff --git a/addons/website_sale/views/sale_order_views.xml b/addons/website_sale/views/sale_order_views.xml
index 0a4e11f5d7a..49b701e3111 100644
--- a/addons/website_sale/views/sale_order_views.xml
+++ b/addons/website_sale/views/sale_order_views.xml
@@ -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>