==================================
Account Payment Braintree Scenario
==================================

Imports::

    >>> import os
    >>> import random
    >>> from decimal import Decimal

    >>> import braintree
    >>> from braintree.test.nonces import Nonces

    >>> from proteus import Model
    >>> from trytond.modules.account.tests.tools import create_chart, create_fiscalyear
    >>> from trytond.modules.company.tests.tools import create_company, get_company
    >>> from trytond.tests.tools import activate_modules, assertEqual, assertTrue

Activate modules::

    >>> config = activate_modules(
    ...     'account_payment_braintree', create_company, create_chart)

Get cron::

    >>> Cron = Model.get('ir.cron')

Get company::

    >>> Company = Model.get('company.company')
    >>> company = get_company()

Create fiscal year::

    >>> fiscalyear = create_fiscalyear()
    >>> fiscalyear.click('create_period')

Create Braintree account::

    >>> BraintreeAccount = Model.get('account.payment.braintree.account')
    >>> braintree_account = BraintreeAccount(name="Braintree")
    >>> braintree_account.currency = company.currency
    >>> braintree_account.environment = 'sandbox'
    >>> braintree_account.merchant_id = os.getenv('BRAINTREE_MERCHANT_ID')
    >>> braintree_account.public_key = os.getenv('BRAINTREE_PUBLIC_KEY')
    >>> braintree_account.private_key = os.getenv('BRAINTREE_PRIVATE_KEY')
    >>> braintree_account.save()

    >>> gateway = braintree.BraintreeGateway(braintree.Configuration(
    ...         environment=braintree.Environment.Sandbox,
    ...         merchant_id=braintree_account.merchant_id,
    ...         public_key=braintree_account.public_key,
    ...         private_key=braintree_account.private_key,
    ...         ))

Create webhook identifier::

    >>> braintree_account.click('new_identifier')
    >>> len(braintree_account.webhook_identifier)
    32

Remove webhook::

    >>> braintree_account.click('new_identifier')
    >>> braintree_account.webhook_identifier

Create payment journal::

    >>> PaymentJournal = Model.get('account.payment.journal')
    >>> payment_journal = PaymentJournal(name="Braintree",
    ...     process_method='braintree', braintree_account=braintree_account)
    >>> payment_journal.save()

Create party::

    >>> Party = Model.get('party.party')
    >>> customer = Party(name="Customer")
    >>> customer.save()

Create submitted payment::

    >>> Payment = Model.get('account.payment')
    >>> payment = Payment()
    >>> payment.journal = payment_journal
    >>> payment.kind = 'receivable'
    >>> payment.party = customer
    >>> # Use random amount to prevent gateway rejection for duplicate
    >>> payment.amount = amount = Decimal(random.randint(0, 1999))
    >>> payment.click('submit')
    >>> payment.state
    'submitted'

Checkout the payment::

    >>> checkout = payment.click('braintree_checkout')
    >>> bool(payment.braintree_checkout_id)
    True

    >>> Payment.write([payment.id], {
    ...     'braintree_nonce': Nonces.Transactable,
    ...     }, config.context)

Process the payment::

    >>> process_payment = payment.click('process_wizard')
    >>> payment.state
    'processing'
    >>> assertTrue(payment.braintree_payment_settled)
    >>> assertEqual(payment.amount, amount)

    >>> _ = gateway.testing.settle_transaction(payment.braintree_transaction_id)

Pull update::

    >>> cron_payment_pull, = Cron.find([
    ...     ('method', '=', 'account.payment|braintree_pull'),
    ...     ])
    >>> cron_payment_pull.companies.append(Company(company.id))
    >>> cron_payment_pull.click('run_once')

    >>> payment.reload()
    >>> payment.state
    'succeeded'
    >>> assertEqual(payment.amount, amount)

Create a customer::

    >>> Customer = Model.get('account.payment.braintree.customer')
    >>> braintree_customer = Customer()
    >>> braintree_customer.party = customer
    >>> braintree_customer.braintree_account = braintree_account
    >>> braintree_customer.save()
    >>> Customer.write([braintree_customer.id], {
    ...     'braintree_nonce': Nonces.Transactable,
    ...     }, config.context)

Run cron::

    >>> cron_customer_create, = Cron.find([
    ...     ('method', '=', 'account.payment.braintree.customer|braintree_create'),
    ...     ])
    >>> cron_customer_create.companies.append(Company(company.id))
    >>> cron_customer_create.click('run_once')

    >>> braintree_customer.reload()
    >>> bool(braintree_customer.braintree_customer_id)
    True

Update customer::

   >>> contact = customer.contact_mechanisms.new()
   >>> contact.type = 'email'
   >>> contact.value = 'customer@example.com'
   >>> customer.save()

   >>> cus = gateway.customer.find(braintree_customer.braintree_customer_id)
   >>> cus.email
   'customer@example.com'

Make payment with customer::

    >>> payment, = payment.duplicate()
    >>> payment.braintree_customer = braintree_customer
    >>> payment.amount = amount = Decimal(random.randint(0, 1999))
    >>> payment.save()
    >>> _, method = Payment.get_braintree_customer_methods(payment.id, config.context)
    >>> method_token, _ = method
    >>> payment.braintree_customer_method = method_token
    >>> payment.click('submit')
    >>> payment.state
    'submitted'
    >>> process_payment = payment.click('process_wizard')
    >>> payment.state
    'processing'

    >>> _ = gateway.testing.settle_transaction(payment.braintree_transaction_id)
    >>> cron_payment_pull.click('run_once')
    >>> payment.reload()
    >>> payment.state
    'succeeded'

Delete customer::

    >>> braintree_customer.delete()
    >>> bool(braintree_customer.active)
    False

Run cron::

    >>> cron_customer_delete, = Cron.find([
    ...     ('method', '=', 'account.payment.braintree.customer|braintree_delete'),
    ...     ])
    >>> cron_customer_delete.companies.append(Company(company.id))
    >>> cron_customer_delete.click('run_once')

    >>> braintree_customer.reload()
    >>> braintree_customer.braintree_customer_id

Create payment to settle::

    >>> payment, = payment.duplicate()
    >>> payment.braintree_customer = None
    >>> payment.braintree_settle_payment = False
    >>> payment.amount = amount = Decimal(random.randint(0, 1999))
    >>> payment.click('submit')
    >>> payment.state
    'submitted'

    >>> Payment.write([payment.id], {
    ...     'braintree_nonce': Nonces.Transactable,
    ...     }, config.context)

    >>> process_payment = payment.click('process_wizard')
    >>> payment.state
    'processing'

    >>> _ = gateway.testing.settle_transaction(payment.braintree_transaction_id)
    >>> cron_payment_pull.click('run_once')
    >>> payment.reload()
    >>> payment.state
    'processing'

Settle lower amount::

    >>> payment.amount = amount = Decimal(random.randint(2, int(payment.amount)))
    >>> payment.click('braintree_do_settle_payment')
    >>> payment.state
    'processing'

    >>> _ = gateway.testing.settle_transaction(payment.braintree_transaction_id)
    >>> cron_payment_pull.click('run_once')
    >>> payment.reload()
    >>> payment.state
    'succeeded'
    >>> bool(payment.braintree_payment_settled)
    True

Refund some amount::

    >>> Refund = Model.get('account.payment.braintree.refund')
    >>> refund = Refund()
    >>> refund.payment = payment
    >>> refund.amount = amount - 1
    >>> refund.click('submit')
    >>> refund.click('approve')
    >>> cron_refund, = Cron.find([
    ...     ('method', '=', 'account.payment.braintree.refund|braintree_refund'),
    ...     ])
    >>> cron_refund.companies.append(Company(company.id))
    >>> cron_refund.click('run_once')

    >>> refund.reload()
    >>> refund.state
    'processing'
    >>> payment.reload()
    >>> assertEqual(payment.amount, amount)

    >>> _ = gateway.testing.settle_transaction(refund.braintree_transaction_id)
    >>> cron_refund_pull, = Cron.find([
    ...     ('method', '=', 'account.payment.braintree.refund|braintree_pull'),
    ...     ])
    >>> cron_refund_pull.companies.append(Company(company.id))
    >>> cron_refund_pull.click('run_once')
    >>> refund.reload()
    >>> refund.state
    'succeeded'

    >>> payment.reload()
    >>> payment.amount
    Decimal('1.00')
    >>> payment.state
    'succeeded'

Try to refund more::

    >>> refund = Refund()
    >>> refund.payment = payment
    >>> refund.amount = Decimal('10')
    >>> refund.click('submit')
    >>> refund.click('approve')
    >>> cron_refund.click('run_once')
    >>> refund.reload()
    >>> refund.state
    'failed'
    >>> payment.reload()
    >>> payment.amount
    Decimal('1.00')
