1# coding: utf-8
2
3import datetime
4
5from odoo import _, api, fields, models
6from odoo.exceptions import ValidationError
7
8
9class AccountPayment(models.Model):
10    _inherit = 'account.payment'
11
12    payment_transaction_id = fields.Many2one('payment.transaction', string='Payment Transaction', readonly=True)
13    payment_token_id = fields.Many2one(
14        'payment.token', string="Saved payment token",
15        domain="""[
16            (payment_method_code == 'electronic', '=', 1),
17            ('company_id', '=', company_id),
18            ('acquirer_id.capture_manually', '=', False),
19            ('acquirer_id.journal_id', '=', journal_id),
20            ('partner_id', 'in', related_partner_ids),
21        ]""",
22        help="Note that tokens from acquirers set to only authorize transactions (instead of capturing the amount) are not available.")
23    related_partner_ids = fields.Many2many('res.partner', compute='_compute_related_partners')
24
25    def _get_payment_chatter_link(self):
26        self.ensure_one()
27        return '<a href=# data-oe-model=account.payment data-oe-id=%d>%s</a>' % (self.id, self.name)
28
29    @api.depends('partner_id.commercial_partner_id.child_ids')
30    def _compute_related_partners(self):
31        for p in self:
32            p.related_partner_ids = (
33                p.partner_id
34              | p.partner_id.commercial_partner_id
35              | p.partner_id.commercial_partner_id.child_ids
36            )._origin
37
38    @api.onchange('partner_id', 'payment_method_id', 'journal_id')
39    def _onchange_set_payment_token_id(self):
40        if not (self.payment_method_code == 'electronic' and self.partner_id and self.journal_id):
41            self.payment_token_id = False
42            return
43
44        self.payment_token_id = self.env['payment.token'].search([
45            ('partner_id', 'in', self.related_partner_ids.ids),
46            ('acquirer_id.capture_manually', '=', False),
47            ('acquirer_id.journal_id', '=', self.journal_id.id),
48         ], limit=1)
49
50    def _prepare_payment_transaction_vals(self):
51        self.ensure_one()
52        return {
53            'amount': self.amount,
54            'reference': self.ref,
55            'currency_id': self.currency_id.id,
56            'partner_id': self.partner_id.id,
57            'partner_country_id': self.partner_id.country_id.id,
58            'payment_token_id': self.payment_token_id.id,
59            'acquirer_id': self.payment_token_id.acquirer_id.id,
60            'payment_id': self.id,
61            'type': 'server2server',
62        }
63
64    def _create_payment_transaction(self, vals=None):
65        for pay in self:
66            if pay.payment_transaction_id:
67                raise ValidationError(_('A payment transaction already exists.'))
68            elif not pay.payment_token_id:
69                raise ValidationError(_('A token is required to create a new payment transaction.'))
70
71        transactions = self.env['payment.transaction']
72        for pay in self:
73            transaction_vals = pay._prepare_payment_transaction_vals()
74
75            if vals:
76                transaction_vals.update(vals)
77
78            transaction = self.env['payment.transaction'].create(transaction_vals)
79            transactions += transaction
80
81            # Link the transaction to the payment.
82            pay.payment_transaction_id = transaction
83
84        return transactions
85
86    def action_validate_invoice_payment(self):
87        res = super(AccountPayment, self).action_validate_invoice_payment()
88        self.mapped('payment_transaction_id').filtered(lambda x: x.state == 'done' and not x.is_processed)._post_process_after_done()
89        return res
90
91    def action_post(self):
92        # Post the payments "normally" if no transactions are needed.
93        # If not, let the acquirer updates the state.
94        #                                __________            ______________
95        #                               | Payments |          | Transactions |
96        #                               |__________|          |______________|
97        #                                  ||                      |    |
98        #                                  ||                      |    |
99        #                                  ||                      |    |
100        #  __________  no s2s required   __\/______   s2s required |    | s2s_do_transaction()
101        # |  Posted  |<-----------------|  post()  |----------------    |
102        # |__________|                  |__________|<-----              |
103        #                                                |              |
104        #                                               OR---------------
105        #  __________                    __________      |
106        # | Cancelled|<-----------------| cancel() |<-----
107        # |__________|                  |__________|
108
109        payments_need_trans = self.filtered(lambda pay: pay.payment_token_id and not pay.payment_transaction_id)
110        transactions = payments_need_trans._create_payment_transaction()
111
112        res = super(AccountPayment, self - payments_need_trans).action_post()
113
114        transactions.s2s_do_transaction()
115
116        # Post payments for issued transactions.
117        transactions._post_process_after_done()
118        payments_trans_done = payments_need_trans.filtered(lambda pay: pay.payment_transaction_id.state == 'done')
119        super(AccountPayment, payments_trans_done).action_post()
120        payments_trans_not_done = payments_need_trans.filtered(lambda pay: pay.payment_transaction_id.state != 'done')
121        payments_trans_not_done.action_cancel()
122
123        return res
124