1# -*- coding: utf-8 -*-
2# Part of Odoo. See LICENSE file for full copyright and licensing details.
3
4import datetime
5import logging
6from collections import namedtuple
7from unittest.mock import patch
8import freezegun
9
10from odoo import tools
11from odoo.addons.account_edi.tests.common import AccountEdiTestCommon
12from odoo.addons.l10n_it_edi.tools.remove_signature import remove_signature
13
14_logger = logging.getLogger(__name__)
15
16
17class PecMailServerTests(AccountEdiTestCommon):
18    """ Main test class for the l10n_it_edi vendor bills XML import from a PEC mail account"""
19
20    fake_test_content = """<?xml version="1.0" encoding="UTF-8"?>
21        <p:FatturaElettronica versione="FPR12" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
22        xmlns:p="http://ivaservizi.agenziaentrate.gov.it/docs/xsd/fatture/v1.2"
23        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
24        xsi:schemaLocation="http://ivaservizi.agenziaentrate.gov.it/docs/xsd/fatture/v1.2 http://www.fatturapa.gov.it/export/fatturazione/sdi/fatturapa/v1.2/Schema_del_file_xml_FatturaPA_versione_1.2.xsd">
25          <FatturaElettronicaHeader>
26            <CessionarioCommittente>
27              <DatiAnagrafici>
28                <CodiceFiscale>01234560157</CodiceFiscale>
29              </DatiAnagrafici>
30            </CessionarioCommittente>
31          </FatturaElettronicaHeader>
32          <FatturaElettronicaBody>
33            <DatiGenerali>
34              <DatiGeneraliDocumento>
35                <TipoDocumento>TD02</TipoDocumento>
36              </DatiGeneraliDocumento>
37            </DatiGenerali>
38          </FatturaElettronicaBody>
39        </p:FatturaElettronica>"""
40
41    @classmethod
42    def setUpClass(cls):
43        """ Setup the test class with a PEC mail server and a fake fatturaPA content """
44
45        super().setUpClass(chart_template_ref='l10n_it.l10n_it_chart_template_generic',
46                           edi_format_ref='l10n_it_edi.edi_fatturaPA')
47
48        # Use the company_data_2 to test that the e-invoice is imported for the right company
49        cls.company = cls.company_data_2['company']
50
51        # Initialize the company's codice fiscale
52        cls.company.l10n_it_codice_fiscale = 'IT01234560157'
53
54        # Build test data.
55        # invoice_filename1 is used for vendor bill receipts tests
56        # invoice_filename2 is used for vendor bill tests
57        cls.invoice_filename1 = 'IT01234567890_FPR01.xml'
58        cls.invoice_filename2 = 'IT01234567890_FPR02.xml'
59        cls.signed_invoice_filename = 'IT01234567890_FPR01.xml.p7m'
60        cls.invoice_content = cls._get_test_file_content(cls.invoice_filename1)
61        cls.signed_invoice_content = cls._get_test_file_content(cls.signed_invoice_filename)
62        cls.invoice = cls.env['account.move'].create({
63            'move_type': 'in_invoice',
64            'ref': '01234567890'
65        })
66        cls.attachment = cls.env['ir.attachment'].create({
67            'name': cls.invoice_filename1,
68            'raw': cls.invoice_content,
69            'res_id': cls.invoice.id,
70            'res_model': 'account.move',
71        })
72        cls.edi_document = cls.env['account.edi.document'].create({
73            'edi_format_id': cls.edi_format.id,
74            'move_id': cls.invoice.id,
75            'attachment_id': cls.attachment.id,
76            'state': 'sent'
77        })
78
79        # Initialize the fetchmail server that has to be tested
80        cls.server = cls.env['fetchmail.server'].sudo().create({
81            'name': 'test_server',
82            'server_type': 'imap',
83            'l10n_it_is_pec': True})
84
85    @classmethod
86    def _get_test_file_content(cls, filename):
87        """ Get the content of a test file inside this module """
88        path = 'l10n_it_edi/tests/expected_xmls/' + filename
89        with tools.file_open(path, mode='rb') as test_file:
90            return test_file.read()
91
92    def _create_invoice(self, content, filename):
93        """ Create an invoice from given attachment content """
94        with patch.object(self.server._cr, 'commit', return_value=None):
95            if filename.endswith(".p7m"):
96                content = remove_signature(content)
97            return self.server._create_invoice_from_mail(content, filename, 'fake@address.be')
98
99    # -----------------------------
100    #
101    # Vendor bills
102    #
103    # -----------------------------
104
105    def test_receive_vendor_bill(self):
106        """ Test a sample e-invoice file from https://www.fatturapa.gov.it/export/documenti/fatturapa/v1.2/IT01234567890_FPR01.xml """
107        invoices = self._create_invoice(self.invoice_content, self.invoice_filename2)
108        self.assertTrue(bool(invoices))
109
110    def test_receive_signed_vendor_bill(self):
111        """ Test a signed (P7M) sample e-invoice file from https://www.fatturapa.gov.it/export/documenti/fatturapa/v1.2/IT01234567890_FPR01.xml """
112        with freezegun.freeze_time('2020-04-06'):
113            invoices = self._create_invoice(self.signed_invoice_content, self.signed_invoice_filename)
114            self.assertRecordValues(invoices, [{
115                'company_id': self.company.id,
116                'name': 'BILL/2014/12/0001',
117                'invoice_date': datetime.date(2014, 12, 18),
118                'ref': '01234567890',
119            }])
120
121    def test_receive_same_vendor_bill_twice(self):
122        """ Test that the second time we are receiving a PEC mail with the same attachment, the second is discarded """
123        content = self.fake_test_content.encode()
124        for result in [True, False]:
125            invoice = self._create_invoice(content, self.invoice_filename2)
126            self.assertEqual(result, bool(invoice))
127
128    # -----------------------------
129    #
130    # Receipts
131    #
132    # -----------------------------
133
134    def _test_receipt(self, receipt_type, source_state, destination_state):
135        """ Test a receipt from the ones in the module's test files """
136
137        # Simulate the 'sent' state of the move, even if we didn't actually send an email in this test
138        self.invoice.l10n_it_send_state = source_state
139
140        # Create a fake receipt from the test file
141        receipt_filename = 'IT01234567890_FPR01_%s_001.xml' % receipt_type
142        receipt_content = self._get_test_file_content(receipt_filename).decode()
143
144        create_mail_attachment = namedtuple('Attachment', ('fname', 'content', 'info'))
145        receipt_mail_attachment = create_mail_attachment(receipt_filename, receipt_content, {})
146
147        # Simulate the arrival of the receipt
148        with patch.object(self.server._cr, 'commit', return_value=None):
149            self.server._message_receipt_invoice(receipt_type, receipt_mail_attachment)
150
151        # Check the Destination state of the edi_document
152        self.assertTrue(destination_state, self.edi_document.state)
153
154    def test_ricevuta_consegna(self):
155        """ Test a receipt adapted from https://www.fatturapa.gov.it/export/documenti/messaggi/v1.0/IT01234567890_11111_RC_001.xml """
156        self._test_receipt('RC', 'sent', 'delivered')
157
158    def test_decorrenza_termini(self):
159        """ Test a receipt adapted from https://www.fatturapa.gov.it/export/documenti/messaggi/v1.0/IT01234567890_11111_DT_001.xml """
160        self._test_receipt('DT', 'delivered', 'delivered_expired')
161