You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

170 lines
5.7 KiB

# Copyright 2017 LasLabs Inc.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import logging
from datetime import datetime, timedelta
from odoo import api, fields, models, _
_logger = logging.getLogger(__name__)
class Contract(models.Model):
_inherit = 'contract.contract'
payment_token_id = fields.Many2one(
string='Payment Token',
comodel_name='payment.token',
domain="[('partner_id', '=', partner_id)]",
context="{'default_partner_id': partner_id}",
help='This is the payment token that will be used to automatically '
'reconcile debts against this account. If none is set, the '
'bill to partner\'s default token will be used.',
)
@api.multi
@api.onchange('partner_id')
def _onchange_partner_id_payment_token(self):
""" Clear the payment token when the partner is changed. """
self.payment_token_id = self.env['payment.token']
@api.model
def cron_retry_auto_pay(self):
""" Retry automatic payments for appropriate invoices. """
invoice_lines = self.env['account.invoice.line'].search([
('invoice_id.state', '=', 'open'),
('invoice_id.auto_pay_attempts', '>', 0),
('contract_line_id.contract_id.is_auto_pay', '=', True),
])
now = datetime.now()
for invoice_line in invoice_lines:
contract = invoice_line.contract_line_id.contract_id
invoice = invoice_line.invoice_id
fail_time = invoice.auto_pay_failed
retry_delta = timedelta(hours=contract.auto_pay_retry_hours)
retry_time = fail_time + retry_delta
if retry_time < now:
contract._do_auto_pay(invoice)
@api.multi
def _recurring_create_invoice(self):
""" If automatic payment is enabled, perform auto pay actions. """
invoice = super(Contract, self)._recurring_create_invoice()
if not self.is_auto_pay:
return invoice
self._do_auto_pay(invoice)
return invoice
@api.multi
def _do_auto_pay(self, invoice):
""" Perform all automatic payment operations on open invoices. """
self.ensure_one()
invoice.ensure_one()
invoice.action_invoice_open()
self._send_invoice_message(invoice)
self._pay_invoice(invoice)
@api.multi
def _pay_invoice(self, invoice):
""" Pay the invoice using the account or partner token. """
if invoice.state != 'open':
_logger.info('Cannot pay an invoice that is not in open state.')
return
if not invoice.residual:
_logger.debug('Cannot pay an invoice with no balance.')
return
token = self.payment_token_id or self.partner_id.payment_token_id
if not token:
_logger.debug(
'Cannot pay an invoice without defining a payment token',
)
return
transaction = self.env['payment.transaction'].create(
self._get_tx_vals(invoice, token),
)
valid_states = ['authorized', 'done']
try:
result = transaction.s2s_do_transaction()
if not result or transaction.state not in valid_states:
_logger.debug(
'Payment transaction failed (%s)',
transaction.state_message,
)
else:
# Success
return True
except Exception:
_logger.exception(
'Payment transaction (%s) generated a gateway error.',
transaction.id,
)
transaction.state = 'error'
invoice.write({
'auto_pay_attempts': invoice.auto_pay_attempts + 1,
'auto_pay_failed': fields.Datetime.now(),
})
if invoice.auto_pay_attempts >= self.auto_pay_retries:
template = self.pay_fail_mail_template_id
self.write({
'is_auto_pay': False,
'payment_token_id': False,
})
if token == self.partner_id.payment_token_id:
self.partner_id.payment_token_id = False
else:
template = self.pay_retry_mail_template_id
if template:
template.send_mail(invoice.id)
return
@api.multi
def _get_tx_vals(self, invoice, token):
""" Return values for create of payment.transaction for invoice."""
amount_due = invoice.residual
partner = token.partner_id
reference = self.env['payment.transaction']._compute_reference()
return {
'reference': '%s' % reference,
'acquirer_id': token.acquirer_id.id,
'payment_token_id': token.id,
'amount': amount_due,
'state': 'draft',
'currency_id': invoice.currency_id.id,
'partner_id': partner.id,
'partner_country_id': partner.country_id.id,
'partner_city': partner.city,
'partner_zip': partner.zip,
'partner_email': partner.email,
}
@api.multi
def _send_invoice_message(self, invoice):
""" Send the appropriate emails for the invoices if needed. """
if invoice.sent:
return
if not self.invoice_mail_template_id:
return
_logger.info('Sending invoice %s, %s (template %s)',
invoice, invoice.number, self.invoice_mail_template_id)
mail_id = self.invoice_mail_template_id.send_mail(invoice.id)
invoice.with_context(mail_post_autofollow=True)
invoice.sent = True
invoice.message_post(body=_("Invoice sent"))
return self.env['mail.mail'].browse(mail_id)