File: account.py

package info (click to toggle)
tryton-modules-web-shop-shopify 7.0.5-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 672 kB
  • sloc: python: 2,402; xml: 350; makefile: 11; sh: 3
file content (135 lines) | stat: -rw-r--r-- 5,112 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# This file is part of Tryton.  The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from collections import defaultdict
from decimal import Decimal

from trytond.model import Unique
from trytond.pool import Pool, PoolMeta

from .common import IdentifierMixin


class Payment(IdentifierMixin, metaclass=PoolMeta):
    __name__ = 'account.payment'

    @classmethod
    def __setup__(cls):
        super().__setup__()
        t = cls.__table__()
        cls._sql_constraints += [
            ('shopify_identifier_unique',
                Unique(t, t.shopify_identifier_signed),
                'web_shop_shopify.msg_identifier_payment_unique'),
            ]

    @classmethod
    def _get_shopify_payment_journal_pattern(cls, sale, transaction):
        return {
            'gateway': transaction.gateway,
            }

    @classmethod
    def _get_from_shopify(cls, sale, transaction):
        assert transaction.kind in {'authorization', 'sale'}
        payment = cls(shopify_identifier=transaction.id)
        payment.company = sale.company
        payment.journal = sale.web_shop.get_payment_journal(
            transaction.currency,
            cls._get_shopify_payment_journal_pattern(
                sale, transaction))
        payment.kind = 'receivable'
        payment.amount = Decimal(transaction.amount)
        payment.origin = sale
        payment.party = sale.party
        return payment

    @classmethod
    def get_from_shopify(cls, sale, order):
        pool = Pool()
        Group = pool.get('account.payment.group')

        id2payments = {}
        to_process = defaultdict(list)
        transactions = [
            t for t in order.transactions() if t.status == 'success']
        # Order transactions to process parent first
        kinds = ['authorization', 'capture', 'sale', 'void', 'refund']
        transactions.sort(key=lambda t: kinds.index(t.kind))
        amounts = defaultdict(Decimal)
        for transaction in transactions:
            if transaction.kind not in {'authorization', 'sale'}:
                continue
            payments = cls.search([
                    ('shopify_identifier', '=', transaction.id),
                    ])
            if payments:
                payment, = payments
            else:
                payment = cls._get_from_shopify(sale, transaction)
                to_process[payment.company, payment.journal].append(payment)
            id2payments[transaction.id] = payment
            amounts[transaction.id] = Decimal(transaction.amount)
        cls.save(list(id2payments.values()))

        for (company, journal), payments in to_process.items():
            group = Group(
                company=company,
                journal=journal,
                kind='receivable')
            group.save()
            cls.submit(payments)
            cls.process(payments, lambda: group)

        captured = defaultdict(Decimal)
        voided = defaultdict(Decimal)
        refunded = defaultdict(Decimal)
        for transaction in transactions:
            if transaction.kind == 'sale':
                payment = id2payments[transaction.id]
                captured[payment] += Decimal(transaction.amount)
            elif transaction.kind == 'capture':
                payment = id2payments[transaction.parent_id]
                id2payments[transaction.id] = payment
                captured[payment] += Decimal(transaction.amount)
            elif transaction.kind == 'void':
                payment = id2payments[transaction.parent_id]
                voided[payment] += Decimal(transaction.amount)
            elif transaction.kind == 'refund':
                payment = id2payments[transaction.parent_id]
                captured[payment] -= Decimal(transaction.amount)
                refunded[payment] += Decimal(transaction.amount)

        to_save = []
        for payment in id2payments.values():
            if captured[payment] and payment.amount != captured[payment]:
                payment.amount = captured[payment]
                to_save.append(payment)
        cls.proceed(to_save)
        cls.save(to_save)

        to_succeed, to_fail, to_proceed = set(), set(), set()
        for transaction_id, payment in id2payments.items():
            if amounts[transaction_id] == (
                    captured[payment] + voided[payment] + refunded[payment]):
                if payment.amount:
                    if payment.state != 'succeeded':
                        to_succeed.add(payment)
                else:
                    if payment.state != 'failed':
                        to_fail.add(payment)
            elif payment.state != 'processing':
                to_proceed.add(payment)
        cls.fail(to_fail)
        cls.proceed(to_proceed)
        cls.succeed(to_succeed)

        return list(id2payments.values())


class PaymentJournal(metaclass=PoolMeta):
    __name__ = 'account.payment.journal'

    @classmethod
    def __setup__(cls):
        super().__setup__()
        cls.process_method.selection.append(('shopify', "Shopify"))