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"))
|