2018-01-16 06:58:15 +01:00
# -*- coding: utf-8 -*-
2018-01-16 11:34:37 +01:00
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details.
2018-01-16 06:58:15 +01:00
import logging
2018-01-16 11:34:37 +01:00
from flectra import fields , models , _
from flectra . tools import float_compare
2018-01-16 06:58:15 +01:00
_logger = logging . getLogger ( __name__ )
class PaymentTransaction ( models . Model ) :
_inherit = ' payment.transaction '
account_invoice_id = fields . Many2one ( ' account.invoice ' , string = ' Invoice ' )
def form_feedback ( self , data , acquirer_name ) :
""" Override to confirm the invoice, if defined, and if the transaction is done. """
tx = None
res = super ( PaymentTransaction , self ) . form_feedback ( data , acquirer_name )
# fetch the tx
tx_find_method_name = ' _ %s _form_get_tx_from_data ' % acquirer_name
if hasattr ( self , tx_find_method_name ) :
tx = getattr ( self , tx_find_method_name ) ( data )
if tx and tx . account_invoice_id :
_logger . info (
' < %s > transaction < %s > processing form feedback for invoice < %s >: tx ref: %s , tx amount: %s ' ,
acquirer_name , tx . id , tx . account_invoice_id . id , tx . reference , tx . amount )
tx . _confirm_invoice ( )
return res
def confirm_invoice_token ( self ) :
""" Confirm a transaction token and call SO confirmation if it is a success.
: return : True if success ; error string otherwise """
self . ensure_one ( )
if self . payment_token_id and self . partner_id == self . account_invoice_id . partner_id :
try :
s2s_result = self . s2s_do_transaction ( )
except Exception as e :
_logger . warning (
_ ( " < %s > transaction ( %s ) failed : < %s > " ) %
( self . acquirer_id . provider , self . id , str ( e ) ) )
return ' pay_invoice_tx_fail '
valid_state = ' authorized ' if self . acquirer_id . capture_manually else ' done '
if not s2s_result or self . state != valid_state :
_logger . warning (
_ ( " < %s > transaction ( %s ) invalid state : %s " ) %
( self . acquirer_id . provider , self . id , self . state_mesage ) )
return ' pay_invoice_tx_state '
try :
# Auto-confirm SO if necessary
return self . _confirm_invoice ( )
except Exception as e :
_logger . warning (
_ ( " < %s > transaction ( %s ) invoice confirmation failed : < %s > " ) %
( self . acquirer_id . provider , self . id , str ( e ) ) )
return ' pay_invoice_tx_confirm '
return ' pay_invoice_tx_token '
def _confirm_invoice ( self ) :
""" Check tx state, confirm and pay potential invoice """
self . ensure_one ( )
# check tx state, confirm the potential SO
if self . account_invoice_id . state != ' open ' :
_logger . warning ( ' < %s > transaction STATE INCORRECT for invoice %s (ID %s , state %s ) ' , self . acquirer_id . provider , self . account_invoice_id . number , self . account_invoice_id . id , self . account_invoice_id . state )
return ' pay_invoice_invalid_doc_state '
if not float_compare ( self . amount , self . account_invoice_id . amount_total , 2 ) == 0 :
_logger . warning (
' < %s > transaction AMOUNT MISMATCH for invoice %s (ID %s ): expected %r , got %r ' ,
self . acquirer_id . provider , self . account_invoice_id . number , self . account_invoice_id . id ,
self . account_invoice_id . amount_total , self . amount ,
)
self . account_invoice_id . message_post (
subject = _ ( " Amount Mismatch ( %s ) " ) % self . acquirer_id . provider ,
body = _ ( " The invoice was not confirmed despite response from the acquirer ( %s ): invoice amount is %r but acquirer replied with %r . " ) % (
self . acquirer_id . provider ,
self . account_invoice_id . amount_total ,
self . amount ,
)
)
return ' pay_invoice_tx_amount '
if self . state == ' authorized ' and self . acquirer_id . capture_manually :
_logger . info ( ' < %s > transaction authorized, nothing to do with invoice %s (ID %s ) ' , self . acquirer_id . provider , self . account_invoice_id . number , self . account_invoice_id . id )
elif self . state == ' done ' :
_logger . info ( ' < %s > transaction completed, paying invoice %s (ID %s ) ' , self . acquirer_id . provider , self . account_invoice_id . number , self . account_invoice_id . id )
self . _pay_invoice ( )
else :
_logger . warning ( ' < %s > transaction MISMATCH for invoice %s (ID %s ) ' , self . acquirer_id . provider , self . account_invoice_id . number , self . account_invoice_id . id )
return ' pay_invoice_tx_state '
return True
def _pay_invoice ( self ) :
self . ensure_one ( )
# force company to ensure journals/accounts etc. are correct
# company_id needed for default_get on account.journal
# force_company needed for company_dependent fields
ctx_company = { ' company_id ' : self . account_invoice_id . company_id . id ,
' force_company ' : self . account_invoice_id . company_id . id }
invoice = self . account_invoice_id . with_context ( * * ctx_company )
if not self . 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 invoice %s (ID %s ) (no journal set on acquirer) ' ,
self . acquirer_id . provider , self . account_invoice_id . number , self . account_invoice_id . id )
return False
self . acquirer_id . journal_id = default_journal
invoice . pay_and_reconcile ( self . acquirer_id . journal_id , pay_amount = invoice . amount_total )
invoice . payment_ids . write ( { ' payment_transaction_id ' : self . ids [ 0 ] } )
_logger . info ( ' < %s > transaction < %s > completed, reconciled invoice %s (ID %s )) ' ,
self . acquirer_id . provider , self . ids [ 0 ] , invoice . number , invoice . id )
return True
def render_invoice_button ( self , invoice , return_url , submit_txt = None , render_values = None ) :
values = {
' return_url ' : return_url ,
' partner_id ' : invoice . partner_id . id ,
}
if render_values :
values . update ( render_values )
return self . acquirer_id . with_context ( submit_class = ' btn btn-primary ' , submit_txt = submit_txt or _ ( ' Pay Now ' ) ) . sudo ( ) . render (
self . reference ,
invoice . amount_total ,
invoice . currency_id . id ,
values = values ,
)
def _check_or_create_invoice_tx ( self , invoice , acquirer , payment_token = None , tx_type = ' form ' , add_tx_values = None ) :
tx = self
if not tx :
tx = self . search ( [ ( ' reference ' , ' = ' , invoice . number ) ] , limit = 1 )
if tx and tx . state in [ ' error ' , ' cancel ' ] : # filter incorrect states
tx = False
if ( tx and acquirer and tx . acquirer_id != acquirer ) or ( tx and tx . account_invoice_id != invoice ) : # filter unmatching
tx = False
if tx and tx . payment_token_id and payment_token and payment_token != tx . payment_token_id : # new or distinct token
tx = False
# still draft tx, no more info -> create a new one
if tx and tx . state == ' draft ' :
tx = False
if not tx :
tx_values = {
' acquirer_id ' : acquirer . id ,
' type ' : tx_type ,
' amount ' : invoice . amount_total ,
' currency_id ' : invoice . currency_id . id ,
' partner_id ' : invoice . partner_id . id ,
' partner_country_id ' : invoice . partner_id . country_id . id ,
' reference ' : self . get_next_reference ( invoice . number ) ,
' account_invoice_id ' : invoice . id ,
}
if add_tx_values :
tx_values . update ( add_tx_values )
if payment_token and payment_token . sudo ( ) . partner_id == invoice . partner_id :
tx_values [ ' payment_token_id ' ] = payment_token . id
tx = self . create ( tx_values )
# update invoice
invoice . write ( {
' payment_tx_id ' : tx . id ,
} )
return tx