# This file is part of Tryton.  The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.

import datetime
import os
from decimal import Decimal
from unittest.mock import MagicMock, Mock, patch

from lxml import etree

from trytond.pool import Pool
from trytond.tests.test_tryton import ModuleTestCase, with_transaction


def get_invoice():
    pool = Pool()
    Address = pool.get('party.address')
    Company = pool.get('company.company')
    Country = pool.get('country.country')
    Currency = pool.get('currency.currency')
    Identifier = pool.get('party.identifier')
    Invoice = pool.get('account.invoice')
    InvoiceLine = pool.get('account.invoice.line')
    InvoiceTax = pool.get('account.invoice.tax')
    MoveLine = pool.get('account.move.line')
    Party = pool.get('party.party')
    PaymentTerm = pool.get('account.invoice.payment_term')
    Product = pool.get('product.product')
    Tax = pool.get('account.tax')
    Uom = pool.get('product.uom')

    address = Mock(spec=Address,
        street_unstructured="St sample, 15",
        street="St sample, 15\n2nd floor\nBox 1234\n",
        city="Scranton",
        postal_code="1000",
        subdivision=None,
        country=Mock(spec=Country,
            code='US'),
        )
    address.building_name = "Building A"
    address.country.name = "United States"
    identifier = MagicMock(
        spec=Identifier,
        type='be_vat',
        code='BE123456789')
    type(identifier).iso_6523 = Identifier.iso_6523
    type(identifier)._eas_type = Identifier._eas_type
    type(identifier).eas = Identifier.eas
    type(identifier).unece_code = Identifier.unece_code
    party = MagicMock(
        spec=Party,
        addresses=[address],
        identifiers=[identifier],
        )
    type(party).identifier_iso6523 = Party.identifier_iso6523
    type(party).identifier_eas = Party.identifier_eas
    party.name = "Michael Scott"
    company = Mock(spec=Company,
        party=Mock(
            spec=Party,
            addresses=[Mock(spec=Address,
                    street_unstructured="Main street, 42",
                    street="Main street, 42",
                    country=Mock(spec=Country,
                        code='US'))],
            identifiers=[Mock(
                    spec=Identifier,
                    type='us_ein',
                    code='911144442')],
            ))
    company.party.name = "Dunder Mifflin"
    company.party.address_get = Mock(return_value=company.party.addresses[0])
    taxes = [Mock(spec=InvoiceTax,
            tax=Mock(spec=Tax,
                legal_notice="Legal Notice",
                unece_category_code='S',
                unece_code='VAT',
                type='percentage',
                rate=Decimal('.1'),
                ),
            base=Decimal('105.00'),
            amount=Decimal('10.50'),
            legal_notice="Legal Notice",
            )]
    product = Mock(spec=Product,
        code="12345",
        type='service',
        unece_special_service_code=None,
        )
    product.name = "Product"
    product.identifier_get.return_value = '98412345678908'
    charge = Mock(spec=Product,
        type='service',
        unece_special_service_code='ZZZ',
        )
    charge.name = "Charge"
    lines = [Mock(spec=InvoiceLine,
            type='line',
            product=product,
            unit=Mock(spec=Uom,
                ),
            quantity=1,
            unit_price=Decimal('100.0000'),
            amount=Decimal('100.00'),
            description="Description",
            invoice_taxes=taxes,
            ), Mock(spec=Invoice,
            type='line',
            product=charge,
            quantity=1,
            amount=Decimal('5.00'),
            invoice_taxes=taxes,
            )]
    invoice = MagicMock(spec=Invoice,
        id=-1,
        __int__=Mock(return_value=-1),
        type='out',
        sequence_type='invoice',
        number="001",
        party=party,
        party_lang='fr',
        party_tax_identifier=identifier,
        invoice_address=party.addresses[0],
        company=company,
        currency=Mock(spec=Currency,
            code='USD',
            round=lambda a: a),
        invoice_date=datetime.date.today(),
        reference="PO1234",
        payment_term=Mock(spec=PaymentTerm, description="Direct"),
        lines=lines,
        line_lines=lines,
        taxes=taxes,
        untaxed_amount=Decimal('105.00'),
        tax_amount=Decimal('10.50'),
        total_amount=Decimal('110.50'),
        amount_to_pay=Decimal('110.50'),
        lines_to_pay=[Mock(spec=MoveLine,
                maturity_date=datetime.date.today(),
                amount=Decimal('110.50'),
                )],
        sales=[],
        state='posted',
        )
    for line in invoice.lines:
        line.invoice = invoice
    return invoice


class EdocumentUblTestCase(ModuleTestCase):
    "Test Edocument Ubl module"
    module = 'edocument_ubl'
    extras = [
        'account_invoice', 'document_incoming_invoice', 'purchase', 'sale']

    @with_transaction()
    def test_Invoice_2(self):
        "Test Invoice-2"
        pool = Pool()
        Template = pool.get('edocument.ubl.invoice')
        InvoiceReport = pool.get('account.invoice', type='report')
        with patch.object(InvoiceReport, 'execute') as execute:
            execute.return_value = ['pdf', b'data', False, 'Invoice-1234']
            invoice = get_invoice()
            template = Template(invoice)

            invoice_string = template.render('2')
            invoice_xml = etree.fromstring(invoice_string)
            schema_file = os.path.join(
                os.path.dirname(__file__),
                '2.4', 'maindoc', 'UBL-Invoice-2.4.xsd')
            schema = etree.XMLSchema(etree.parse(schema_file))
            schema.assertValid(invoice_xml)

    @with_transaction()
    def test_Invoice_2_Peppol_BIS_3(self):
        "Test Invoice-2"
        pool = Pool()
        Template = pool.get('edocument.ubl.invoice')
        InvoiceReport = pool.get('account.invoice', type='report')
        with patch.object(InvoiceReport, 'execute') as execute:
            execute.return_value = ['pdf', b'data', False, 'Invoice-1234']
            invoice = get_invoice()
            template = Template(invoice)

            invoice_string = template.render('2', specification='peppol-bis-3')
            invoice_xml = etree.fromstring(invoice_string)
            schema_file = os.path.join(
                os.path.dirname(__file__),
                '2.4', 'maindoc', 'UBL-Invoice-2.4.xsd')
            schema = etree.XMLSchema(etree.parse(schema_file))
            schema.assertValid(invoice_xml)

    @with_transaction()
    def test_CreditNote_2(self):
        "Test CreditNote-2"
        pool = Pool()
        Template = pool.get('edocument.ubl.invoice')
        InvoiceReport = pool.get('account.invoice', type='report')
        with patch.object(InvoiceReport, 'execute') as execute:
            execute.return_value = ['pdf', b'data', False, 'Invoice-1234']
            invoice = get_invoice()
            invoice.sequence_type = 'credit_note'
            for line in invoice.lines:
                line.quantity *= -1
            invoice.untaxed_amount *= -1
            invoice.tax_amount *= -1
            invoice.total_amount *= -1
            invoice.amount_to_pay *= -1
            for line in invoice.lines_to_pay:
                line.amount *= -1
            template = Template(invoice)

            invoice_string = template.render('2')
            invoice_xml = etree.fromstring(invoice_string)
            schema_file = os.path.join(
                os.path.dirname(__file__),
                '2.4', 'maindoc', 'UBL-CreditNote-2.4.xsd')
            schema = etree.XMLSchema(etree.parse(schema_file))
            schema.assertValid(invoice_xml)

    @with_transaction()
    def test_CreditNote_2_Peppol_BIS_3(self):
        "Test CreditNote-2"
        pool = Pool()
        Template = pool.get('edocument.ubl.invoice')
        InvoiceReport = pool.get('account.invoice', type='report')
        with patch.object(InvoiceReport, 'execute') as execute:
            execute.return_value = ['pdf', b'data', False, 'Invoice-1234']
            invoice = get_invoice()
            invoice.sequence_type = 'credit_note'
            for line in invoice.lines:
                line.quantity *= -1
            invoice.untaxed_amount *= -1
            invoice.tax_amount *= -1
            invoice.total_amount *= -1
            invoice.amount_to_pay *= -1
            for line in invoice.lines_to_pay:
                line.amount *= -1
            template = Template(invoice)

            invoice_string = template.render('2', specification='peppol-bis-3')
            invoice_xml = etree.fromstring(invoice_string)
            schema_file = os.path.join(
                os.path.dirname(__file__),
                '2.4', 'maindoc', 'UBL-CreditNote-2.4.xsd')
            schema = etree.XMLSchema(etree.parse(schema_file))
            schema.assertValid(invoice_xml)

    @with_transaction()
    def test_identifier_eas(self):
        "Test EAS identifier"
        pool = Pool()
        Identifier = pool.get('party.identifier')

        for type, code, eas_code, eas, vatin in [
                ('gr_vat', '023456783',
                    '9933', 'el023456783', 'EL023456783'),
                ('eu_vat', 'EL023456783',
                    '9933', 'el023456783', 'EL023456783'),
                ('eu_vat', 'GR023456783',
                    '9933', 'el023456783', 'EL023456783'),
                ('be_vat', '0403019261',
                    '9925', 'be0403019261', 'BE0403019261'),
                ('eu_vat', 'BE0403019261',
                    '9925', 'be0403019261', 'BE0403019261'),
                ('gb_vat', '980780684',
                    '9932', 'gb980780684', 'GB980780684'),
                ('eu_vat', 'GB980780684',
                    '9932', 'gb980780684', 'GB980780684'),
                ('eu_vat', 'XI980780684',
                    '9932', 'gb980780684', 'GB980780684'),
                ]:
            with self.subTest(type=type, code=code):
                identifier = Identifier(type=type, code=code)

                self.assertEqual(identifier.eas_code, eas_code)
                self.assertEqual(identifier.eas, eas)
                self.assertEqual(identifier.vatin, vatin)


del ModuleTestCase
