1
0
forked from Mapan/odoo17e
odoo17e-kedaikipas58/addons/l10n_mx_edi/tests/test_cfdi_invoice_documents.py
2024-12-10 09:04:09 +07:00

1823 lines
76 KiB
Python

# -*- coding: utf-8 -*-
from .common import TestMxEdiCommon
from odoo import Command, fields
from odoo.exceptions import UserError
from odoo.tests import tagged
from freezegun import freeze_time
@tagged('post_install_l10n', 'post_install', '-at_install')
class TestCFDIInvoiceWorkflow(TestMxEdiCommon):
def test_invoice_workflow(self):
with freeze_time('2017-01-01'):
invoice = self._create_invoice(invoice_date_due='2017-02-01') # Force PPD
# No pac found.
self.env.company.l10n_mx_edi_pac = None
with freeze_time('2017-01-05'):
invoice._l10n_mx_edi_cfdi_invoice_try_send()
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids, [
{
'move_id': invoice.id,
'datetime': fields.Datetime.from_string('2017-01-05 00:00:00'),
'message': "No PAC specified.",
'state': 'invoice_sent_failed',
'sat_state': False,
'cancellation_reason': False,
'cancel_button_needed': False,
'retry_button_needed': True,
},
])
self.assertRecordValues(invoice, [{'l10n_mx_edi_cfdi_state': None}])
# Set back the PAC but make it raising an error.
self.env.company.l10n_mx_edi_pac = 'solfact'
with freeze_time('2017-01-06'), self.with_mocked_pac_sign_error():
invoice._l10n_mx_edi_cfdi_invoice_try_send()
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids, [
{
'move_id': invoice.id,
'datetime': fields.Datetime.from_string('2017-01-06 00:00:00'),
'message': "turlututu",
'state': 'invoice_sent_failed',
'sat_state': False,
'cancellation_reason': False,
'cancel_button_needed': False,
'retry_button_needed': True,
},
])
self.assertRecordValues(invoice, [{'l10n_mx_edi_cfdi_state': None}])
# The failing attachment remains accessible for the user.
self.assertTrue(invoice.l10n_mx_edi_invoice_document_ids.attachment_id)
# Sign.
with freeze_time('2017-01-07'), self.with_mocked_pac_sign_success():
invoice._l10n_mx_edi_cfdi_invoice_try_send()
sent_doc_values = {
'move_id': invoice.id,
'datetime': fields.Datetime.from_string('2017-01-07 00:00:00'),
'message': False,
'state': 'invoice_sent',
'sat_state': 'not_defined',
'attachment_origin': False,
'cancellation_reason': False,
'cancel_button_needed': True,
'retry_button_needed': False,
}
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids, [sent_doc_values])
self.assertTrue(invoice.l10n_mx_edi_cfdi_attachment_id)
self.assertTrue(invoice.l10n_mx_edi_invoice_document_ids.attachment_id)
self.assertRecordValues(invoice, [{'l10n_mx_edi_cfdi_state': 'sent'}])
# Cancel failed.
self.env.company.l10n_mx_edi_pac = None
with freeze_time('2017-02-01'):
self.env['l10n_mx_edi.invoice.cancel'] \
.with_context(invoice.button_request_cancel()['context']) \
.create({'cancellation_reason': '02'}) \
.action_cancel_invoice()
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids.sorted(), [
{
'move_id': invoice.id,
'datetime': fields.Datetime.from_string('2017-02-01 00:00:00'),
'message': "No PAC specified.",
'state': 'invoice_cancel_failed',
'sat_state': False,
'cancellation_reason': '02',
'cancel_button_needed': False,
'retry_button_needed': True,
},
sent_doc_values,
])
# Set back the PAC but make it raising an error.
self.env.company.l10n_mx_edi_pac = 'solfact'
with freeze_time('2017-02-06'), self.with_mocked_pac_cancel_error():
invoice.l10n_mx_edi_invoice_document_ids.sorted()[0].action_retry()
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids.sorted(), [
{
'move_id': invoice.id,
'datetime': fields.Datetime.from_string('2017-02-06 00:00:00'),
'message': "turlututu",
'state': 'invoice_cancel_failed',
'sat_state': False,
'cancellation_reason': '02',
'cancel_button_needed': False,
'retry_button_needed': True,
},
sent_doc_values,
])
# Cancel.
with freeze_time('2017-02-07'), self.with_mocked_pac_cancel_success():
self.env['l10n_mx_edi.invoice.cancel'] \
.with_context(invoice.button_request_cancel()['context']) \
.create({'cancellation_reason': '02'}) \
.action_cancel_invoice()
invoice.l10n_mx_edi_invoice_document_ids.invalidate_recordset(fnames=['cancel_button_needed'])
sent_doc_values['cancel_button_needed'] = False
cancel_doc_values = {
'move_id': invoice.id,
'datetime': fields.Datetime.from_string('2017-02-07 00:00:00'),
'message': False,
'state': 'invoice_cancel',
'sat_state': 'not_defined',
'cancellation_reason': '02',
'cancel_button_needed': False,
'retry_button_needed': False,
}
sent_doc_values['sat_state'] = 'skip'
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids.sorted(), [
cancel_doc_values,
sent_doc_values,
])
self.assertRecordValues(invoice, [{
'state': 'cancel',
'l10n_mx_edi_cfdi_state': 'cancel',
}])
# Sign again.
invoice.button_draft()
invoice.action_post()
with freeze_time('2017-03-10'), self.with_mocked_pac_sign_success():
invoice._l10n_mx_edi_cfdi_invoice_try_send()
sent_doc_values2 = {
'move_id': invoice.id,
'datetime': fields.Datetime.from_string('2017-03-10 00:00:00'),
'message': False,
'state': 'invoice_sent',
'sat_state': 'not_defined',
'cancellation_reason': False,
'cancel_button_needed': True,
'retry_button_needed': False,
}
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids.sorted(), [
sent_doc_values2,
cancel_doc_values,
sent_doc_values,
])
self.assertRecordValues(invoice, [{'l10n_mx_edi_cfdi_state': 'sent'}])
# Sat.
with freeze_time('2017-04-01'), self.with_mocked_sat_call(lambda _x: 'valid'):
self.env['l10n_mx_edi.document']._fetch_and_update_sat_status(extra_domain=[('move_id.company_id', '=', self.env.company.id)])
sent_doc_values2['sat_state'] = 'valid'
cancel_doc_values['sat_state'] = 'valid'
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids.sorted(), [
sent_doc_values2,
cancel_doc_values,
sent_doc_values,
])
self.assertRecordValues(invoice, [{'l10n_mx_edi_cfdi_state': 'sent'}])
# Payment fully match but failed.
payment1 = self.env['account.payment.register']\
.with_context(active_model='account.move', active_ids=invoice.ids)\
.create({
'payment_date': '2017-06-01',
'amount': 100.0,
})\
._create_payments()
with freeze_time('2017-06-02'), self.with_mocked_pac_sign_error():
invoice.l10n_mx_edi_cfdi_invoice_try_update_payments()
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids.sorted(), [
{
'move_id': payment1.move_id.id,
'datetime': fields.Datetime.from_string('2017-06-02 00:00:00'),
'message': "turlututu",
'state': 'payment_sent_failed',
'sat_state': False,
'cancellation_reason': False,
'cancel_button_needed': False,
'retry_button_needed': True,
},
sent_doc_values2,
cancel_doc_values,
sent_doc_values,
])
self.assertRecordValues(invoice, [{'l10n_mx_edi_cfdi_state': 'sent'}])
# Unreconcile the payment.
# The document should disappear.
payment1.move_id.line_ids.remove_move_reconcile()
with freeze_time('2017-06-02'):
invoice.l10n_mx_edi_cfdi_invoice_try_update_payments()
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids.sorted(), [
sent_doc_values2,
cancel_doc_values,
sent_doc_values,
])
self.assertRecordValues(invoice, [{'l10n_mx_edi_cfdi_state': 'sent'}])
# Redo the reconciliation.
# The document should be created again.
(invoice + payment1.move_id).line_ids\
.filtered(lambda x: x.account_type == 'asset_receivable')\
.reconcile()
with freeze_time('2017-06-03'), self.with_mocked_pac_sign_error():
invoice.l10n_mx_edi_cfdi_invoice_try_update_payments()
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids.sorted(), [
{
'move_id': payment1.move_id.id,
'datetime': fields.Datetime.from_string('2017-06-03 00:00:00'),
'message': "turlututu",
'state': 'payment_sent_failed',
'sat_state': False,
'cancellation_reason': False,
'cancel_button_needed': False,
'retry_button_needed': True,
},
sent_doc_values2,
cancel_doc_values,
sent_doc_values,
])
self.assertRecordValues(invoice, [{'l10n_mx_edi_cfdi_state': 'sent'}])
# In case of success, the document is updated.
with freeze_time('2017-06-04'), self.with_mocked_pac_sign_success():
invoice.l10n_mx_edi_cfdi_invoice_try_update_payments()
payment1_doc_values1 = {
'move_id': payment1.move_id.id,
'datetime': fields.Datetime.from_string('2017-06-04 00:00:00'),
'message': False,
'state': 'payment_sent',
'sat_state': 'not_defined',
'cancellation_reason': False,
'cancel_button_needed': True,
'retry_button_needed': False,
}
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids.sorted(), [
payment1_doc_values1,
sent_doc_values2,
cancel_doc_values,
sent_doc_values,
])
self.assertRecordValues(invoice, [{'l10n_mx_edi_cfdi_state': 'sent'}])
# Call again the method shouldn't change anything.
with freeze_time('2017-06-04'), self.with_mocked_pac_sign_error():
invoice.l10n_mx_edi_cfdi_invoice_try_update_payments()
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids.sorted(), [
payment1_doc_values1,
sent_doc_values2,
cancel_doc_values,
sent_doc_values,
])
self.assertRecordValues(invoice, [{'l10n_mx_edi_cfdi_state': 'sent'}])
# Create another payment and unreconcile the first payment.
payment2 = self.env['account.payment.register']\
.with_context(active_model='account.move', active_ids=invoice.ids)\
.create({
'payment_date': '2017-06-01',
'amount': 100.0,
})\
._create_payments()
payment1.move_id.line_ids.remove_move_reconcile()
with freeze_time('2017-07-01'), self.with_mocked_pac_sign_success():
invoice.l10n_mx_edi_cfdi_invoice_try_update_payments()
payment2_doc_values1 = {
'move_id': payment2.move_id.id,
'datetime': fields.Datetime.from_string('2017-07-01 00:00:00'),
'message': False,
'state': 'payment_sent',
'sat_state': 'not_defined',
'cancellation_reason': False,
'cancel_button_needed': True,
'retry_button_needed': False,
}
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids.sorted(), [
payment2_doc_values1,
payment1_doc_values1,
sent_doc_values2,
cancel_doc_values,
sent_doc_values,
])
self.assertRecordValues(invoice, [{'l10n_mx_edi_cfdi_state': 'sent'}])
# Fail to cancel the invoice.
with freeze_time('2017-07-10'), self.with_mocked_pac_cancel_error():
self.env['l10n_mx_edi.invoice.cancel'] \
.with_context(invoice.button_request_cancel()['context']) \
.create({'cancellation_reason': '02'}) \
.action_cancel_invoice()
cancel_doc_values2 = {
'move_id': invoice.id,
'datetime': fields.Datetime.from_string('2017-07-10 00:00:00'),
'message': "turlututu",
'state': 'invoice_cancel_failed',
'sat_state': False,
'cancellation_reason': '02',
'cancel_button_needed': False,
'retry_button_needed': True,
}
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids.sorted(), [
cancel_doc_values2,
payment2_doc_values1,
payment1_doc_values1,
sent_doc_values2,
cancel_doc_values,
sent_doc_values,
])
self.assertRecordValues(invoice, [{'l10n_mx_edi_cfdi_state': 'sent'}])
# Reconcile again the first payment. Since this reconciliation has already been sent to the SAT, nothing
# is updated.
(invoice + payment1.move_id).line_ids\
.filtered(lambda x: x.account_type == 'asset_receivable')\
.reconcile()
with freeze_time('2017-07-11'), self.with_mocked_pac_sign_error():
invoice.l10n_mx_edi_cfdi_invoice_try_update_payments()
self.assertRecordValues(
invoice.l10n_mx_edi_invoice_document_ids.sorted(lambda x: (x.datetime, x.move_id.id), reverse=True),
[
cancel_doc_values2,
payment2_doc_values1,
payment1_doc_values1,
sent_doc_values2,
cancel_doc_values,
sent_doc_values,
],
)
self.assertRecordValues(invoice, [{'l10n_mx_edi_cfdi_state': 'sent'}])
# Cancel successfully the invoice.
with freeze_time('2017-07-12'), self.with_mocked_pac_cancel_success():
self.env['l10n_mx_edi.invoice.cancel'] \
.with_context(invoice.button_request_cancel()['context']) \
.create({'cancellation_reason': '02'}) \
.action_cancel_invoice()
invoice.l10n_mx_edi_invoice_document_ids.invalidate_recordset(fnames=['cancel_button_needed'])
sent_doc_values2['cancel_button_needed'] = False
cancel_doc_values2.update({
'datetime': fields.Datetime.from_string('2017-07-12 00:00:00'),
'message': False,
'state': 'invoice_cancel',
'sat_state': 'not_defined',
'cancellation_reason': '02',
'cancel_button_needed': False,
'retry_button_needed': False,
})
self.assertRecordValues(
invoice.l10n_mx_edi_invoice_document_ids.sorted(lambda x: (x.datetime, x.move_id.id), reverse=True),
[
cancel_doc_values2,
payment2_doc_values1,
payment1_doc_values1,
sent_doc_values2,
cancel_doc_values,
sent_doc_values,
],
)
self.assertRecordValues(invoice, [{'l10n_mx_edi_cfdi_state': 'cancel'}])
with freeze_time('2017-07-12'), self.with_mocked_sat_call(lambda x: 'cancelled' if x.move_id == invoice else 'valid'):
self.env['l10n_mx_edi.document']._fetch_and_update_sat_status(extra_domain=[('move_id.company_id', '=', self.env.company.id)])
cancel_doc_values2['sat_state'] = 'cancelled'
payment2_doc_values1['sat_state'] = 'valid'
payment1_doc_values1['sat_state'] = 'valid'
self.assertRecordValues(
invoice.l10n_mx_edi_invoice_document_ids.sorted(lambda x: (x.datetime, x.move_id.id), reverse=True),
[
cancel_doc_values2,
payment2_doc_values1,
payment1_doc_values1,
sent_doc_values2,
cancel_doc_values,
sent_doc_values,
],
)
self.assertRecordValues(invoice, [{'l10n_mx_edi_cfdi_state': 'cancel'}])
def test_invoice_sent_after_paid(self):
invoice = self._create_invoice(invoice_date_due='2017-01-01')
self.assertRecordValues(invoice, [{'l10n_mx_edi_payment_policy': 'PUE'}])
# Pay.
payment = self.env['account.payment.register']\
.with_context(active_model='account.move', active_ids=invoice.ids)\
.create({
'payment_date': '2017-06-01',
'amount': 100.0,
})\
._create_payments()
# Sign.
with freeze_time('2017-01-07'), self.with_mocked_pac_sign_success():
invoice._l10n_mx_edi_cfdi_invoice_try_send()
sent_doc_values = {
'move_id': invoice.id,
'datetime': fields.Datetime.from_string('2017-01-07 00:00:00'),
'message': False,
'state': 'invoice_sent',
'sat_state': 'not_defined',
'cancel_button_needed': True,
'retry_button_needed': False,
}
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids, [sent_doc_values])
self.assertRecordValues(invoice, [{'l10n_mx_edi_update_payments_needed': True}])
with freeze_time('2017-06-02'), self.with_mocked_pac_sign_success():
invoice.l10n_mx_edi_cfdi_invoice_try_update_payments()
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids.sorted(), [
{
'move_id': payment.move_id.id,
'datetime': fields.Datetime.from_string('2017-06-02 00:00:00'),
'message': False,
'state': 'payment_sent_pue',
'sat_state': False,
'cancel_button_needed': False,
'retry_button_needed': False,
},
sent_doc_values,
])
self.assertRecordValues(invoice, [{'l10n_mx_edi_update_payments_needed': False}])
def test_invoice_advanced_payment_flows(self):
invoice = self._create_invoice(invoice_date_due='2017-01-01')
self.assertRecordValues(invoice, [{'l10n_mx_edi_payment_policy': 'PUE'}])
# Sign.
with freeze_time('2017-01-07'), self.with_mocked_pac_sign_success():
invoice._l10n_mx_edi_cfdi_invoice_try_send()
sent_doc_values = {
'move_id': invoice.id,
'datetime': fields.Datetime.from_string('2017-01-07 00:00:00'),
'message': False,
'state': 'invoice_sent',
'sat_state': 'not_defined',
'cancellation_reason': False,
'cancel_button_needed': True,
'retry_button_needed': False,
}
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids, [sent_doc_values])
# Pay.
payment = self.env['account.payment.register']\
.with_context(active_model='account.move', active_ids=invoice.ids)\
.create({
'payment_date': '2017-06-01',
'amount': 100.0,
})\
._create_payments()
with freeze_time('2017-06-02'), self.with_mocked_pac_sign_success():
invoice.l10n_mx_edi_cfdi_invoice_try_update_payments()
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids.sorted(), [
{
'move_id': payment.move_id.id,
'datetime': fields.Datetime.from_string('2017-06-02 00:00:00'),
'message': False,
'state': 'payment_sent_pue',
'sat_state': False,
'cancellation_reason': False,
'cancel_button_needed': False,
'retry_button_needed': False,
},
sent_doc_values,
])
# Force sending but force an error.
with freeze_time('2017-06-03'), self.with_mocked_pac_sign_error():
invoice.l10n_mx_edi_invoice_document_ids.sorted()[0].action_force_payment_cfdi()
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids.sorted(), [
{
'move_id': payment.move_id.id,
'datetime': fields.Datetime.from_string('2017-06-03 00:00:00'),
'message': "turlututu",
'state': 'payment_sent_failed',
'sat_state': False,
'cancellation_reason': False,
'cancel_button_needed': False,
'retry_button_needed': True,
},
sent_doc_values,
])
# Retry.
with freeze_time('2017-06-04'), self.with_mocked_pac_sign_success():
invoice.l10n_mx_edi_invoice_document_ids.sorted()[0].action_retry()
payment_doc_values = {
'move_id': payment.move_id.id,
'datetime': fields.Datetime.from_string('2017-06-04 00:00:00'),
'message': False,
'state': 'payment_sent',
'sat_state': 'not_defined',
'cancellation_reason': False,
'cancel_button_needed': True,
'retry_button_needed': False,
}
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids.sorted(), [
payment_doc_values,
sent_doc_values,
])
# Cancel the payment.
with freeze_time('2017-08-01'), self.with_mocked_pac_cancel_error():
payment.l10n_mx_edi_payment_document_ids.action_cancel()
payment_doc_cancel_values = {
'move_id': payment.move_id.id,
'datetime': fields.Datetime.from_string('2017-08-01 00:00:00'),
'message': "turlututu",
'state': 'payment_cancel_failed',
'sat_state': False,
'cancellation_reason': '02',
'cancel_button_needed': False,
'retry_button_needed': True,
}
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids.sorted(), [
payment_doc_cancel_values,
payment_doc_values,
sent_doc_values,
])
# Retry.
with freeze_time('2017-08-02'), self.with_mocked_pac_cancel_success():
invoice.l10n_mx_edi_cfdi_invoice_try_update_payments()
payment_doc_cancel_values.update({
'datetime': fields.Datetime.from_string('2017-08-02 00:00:00'),
'message': False,
'state': 'payment_cancel',
'sat_state': 'not_defined',
'cancellation_reason': '02',
'cancel_button_needed': False,
'retry_button_needed': False,
})
payment_doc_values['sat_state'] = 'skip'
payment_doc_values['cancel_button_needed'] = False
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids.sorted(), [
payment_doc_cancel_values,
payment_doc_values,
sent_doc_values,
])
self.assertRecordValues(invoice, [{
'l10n_mx_edi_update_payments_needed': False,
}])
# New payment.
payment2 = self.env['account.payment.register']\
.with_context(active_model='account.move', active_ids=invoice.ids)\
.create({})\
._create_payments()
invoice.invalidate_recordset(fnames=['l10n_mx_edi_update_payments_needed'])
self.assertRecordValues(invoice, [{
'l10n_mx_edi_update_payments_needed': True,
}])
with freeze_time('2017-08-03'), self.with_mocked_pac_sign_success():
invoice.l10n_mx_edi_cfdi_invoice_try_update_payments()
payment2.l10n_mx_edi_payment_document_ids.action_force_payment_cfdi()
payment2_doc_values = {
'move_id': payment2.move_id.id,
'datetime': fields.Datetime.from_string('2017-08-03 00:00:00'),
'message': False,
'state': 'payment_sent',
'sat_state': 'not_defined',
'cancellation_reason': False,
'cancel_button_needed': True,
'retry_button_needed': False,
}
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids.sorted(), [
payment2_doc_values,
payment_doc_cancel_values,
payment_doc_values,
sent_doc_values,
])
# Remove it and again a new payment.
payment2.line_ids.remove_move_reconcile()
payment3 = self.env['account.payment.register']\
.with_context(active_model='account.move', active_ids=invoice.ids)\
.create({})\
._create_payments()
with freeze_time('2017-08-04'), self.with_mocked_pac_sign_success():
invoice.l10n_mx_edi_cfdi_invoice_try_update_payments()
payment3.l10n_mx_edi_payment_document_ids.action_force_payment_cfdi()
payment3_doc_values = {
'move_id': payment3.move_id.id,
'datetime': fields.Datetime.from_string('2017-08-04 00:00:00'),
'message': False,
'state': 'payment_sent',
'sat_state': 'not_defined',
'cancellation_reason': False,
'cancel_button_needed': True,
'retry_button_needed': False,
}
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids.sorted(), [
payment3_doc_values,
payment2_doc_values,
payment_doc_cancel_values,
payment_doc_values,
sent_doc_values,
])
# Cancel payment2
with freeze_time('2017-08-05'), self.with_mocked_pac_cancel_success():
payment2.l10n_mx_edi_payment_document_ids.action_cancel()
payment2_cancel_doc_values = {
'move_id': payment2.move_id.id,
'datetime': fields.Datetime.from_string('2017-08-05 00:00:00'),
'message': False,
'state': 'payment_cancel',
'sat_state': 'not_defined',
'cancellation_reason': '02',
'cancel_button_needed': False,
'retry_button_needed': False,
}
payment2_doc_values['sat_state'] = 'skip'
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids.sorted(), [
payment2_cancel_doc_values,
payment3_doc_values,
payment2_doc_values,
payment_doc_cancel_values,
payment_doc_values,
sent_doc_values,
])
def test_payment_on_multiple_invoices(self):
invoice1 = self._create_invoice_with_amount('2017-01-01', self.comp_curr, 1000.0)
invoice2 = self._create_invoice_with_amount('2017-01-01', self.comp_curr, 1000.0)
# Sign.
with freeze_time('2017-01-01'), self.with_mocked_pac_sign_success():
invoice1._l10n_mx_edi_cfdi_invoice_try_send()
inv1_sent_doc_values = {
'move_id': invoice1.id,
'invoice_ids': invoice1.ids,
'message': False,
'state': 'invoice_sent',
'sat_state': 'not_defined',
}
self.assertRecordValues(invoice1.l10n_mx_edi_invoice_document_ids, [inv1_sent_doc_values])
self.assertRecordValues(invoice1, [{
'l10n_mx_edi_update_payments_needed': False,
}])
# Create a payment with an higher amount than invoice1 and reconcile it.
payment = self.env['account.payment'].create({
'partner_id': self.partner_mx.id,
'payment_type': 'inbound',
'partner_type': 'customer',
'date': invoice1.date,
'amount': 1500.0,
'currency_id': self.comp_curr.id,
})
payment.action_post()
payment1_rec_line = payment.line_ids.filtered(lambda x: x.account_type == 'asset_receivable')
invoice1_rec_line = invoice1.line_ids.filtered(lambda x: x.account_type == 'asset_receivable')
(payment1_rec_line + invoice1_rec_line).reconcile()
# Nothing change since the payment is not fully reconciled.
with freeze_time('2017-01-01'), self.with_mocked_pac_sign_success():
invoice1.l10n_mx_edi_cfdi_invoice_try_update_payments()
self.assertRecordValues(invoice1.l10n_mx_edi_invoice_document_ids, [inv1_sent_doc_values])
self.assertRecordValues(invoice1, [{
'l10n_mx_edi_update_payments_needed': False,
}])
# Fully reconcile the payment.
invoice2_rec_line = invoice2.line_ids.filtered(lambda x: x.account_type == 'asset_receivable')
(payment1_rec_line + invoice2_rec_line).reconcile()
self.assertRecordValues(invoice1, [{
'l10n_mx_edi_update_payments_needed': False,
}])
# Nothing change since invoice2 is not signed.
with freeze_time('2017-01-01'), self.with_mocked_pac_sign_success():
invoice1.l10n_mx_edi_cfdi_invoice_try_update_payments()
self.assertRecordValues(invoice1.l10n_mx_edi_invoice_document_ids, [inv1_sent_doc_values])
self.assertRecordValues(invoice1, [{
'l10n_mx_edi_update_payments_needed': False,
}])
self.assertRecordValues(invoice2, [{
'l10n_mx_edi_update_payments_needed': False,
}])
# Sign invoice2 and retry the payments.
with freeze_time('2017-01-01'), self.with_mocked_pac_sign_success():
invoice2._l10n_mx_edi_cfdi_invoice_try_send()
invoice1.invalidate_recordset(fnames=['l10n_mx_edi_update_payments_needed'])
self.assertRecordValues(invoice1, [{
'l10n_mx_edi_update_payments_needed': True,
}])
self.assertRecordValues(invoice2, [{
'l10n_mx_edi_update_payments_needed': True,
}])
with freeze_time('2017-01-01'), self.with_mocked_pac_sign_success():
invoice1.l10n_mx_edi_cfdi_invoice_try_update_payments()
pay_sent_doc_values1 = {
'move_id': payment.move_id.id,
'invoice_ids': (invoice1 + invoice2).ids,
'message': False,
'state': 'payment_sent',
'sat_state': 'not_defined',
}
self.assertRecordValues(invoice1.l10n_mx_edi_invoice_document_ids.sorted(), [
pay_sent_doc_values1,
inv1_sent_doc_values,
])
self.assertRecordValues(invoice1, [{
'l10n_mx_edi_update_payments_needed': False,
}])
inv2_sent_doc_values = {
'move_id': invoice2.id,
'invoice_ids': invoice2.ids,
'message': False,
'state': 'invoice_sent',
'sat_state': 'not_defined',
}
self.assertRecordValues(invoice2.l10n_mx_edi_invoice_document_ids.sorted(), [
pay_sent_doc_values1,
inv2_sent_doc_values,
])
self.assertRecordValues(invoice2, [{
'l10n_mx_edi_update_payments_needed': False,
}])
# Updating again the payment shouldn't do anything since the reconciliation hasn't changed.
with freeze_time('2017-01-01'), self.with_mocked_pac_sign_error():
invoice1.l10n_mx_edi_cfdi_invoice_try_update_payments()
self.assertRecordValues(invoice1.l10n_mx_edi_invoice_document_ids.sorted(), [
pay_sent_doc_values1,
inv1_sent_doc_values,
])
self.assertRecordValues(invoice1, [{
'l10n_mx_edi_update_payments_needed': False,
}])
self.assertRecordValues(invoice2.l10n_mx_edi_invoice_document_ids.sorted(), [
pay_sent_doc_values1,
inv2_sent_doc_values,
])
self.assertRecordValues(invoice2, [{
'l10n_mx_edi_update_payments_needed': False,
}])
# Change the reconciliation.
invoice3 = self._create_invoice_with_amount('2017-01-01', self.comp_curr, 1000.0)
invoice2_rec_line.remove_move_reconcile()
invoice3_rec_line = invoice3.line_ids.filtered(lambda x: x.account_type == 'asset_receivable')
(payment1_rec_line + invoice3_rec_line).reconcile()
with freeze_time('2017-01-01'), self.with_mocked_pac_sign_success():
invoice3._l10n_mx_edi_cfdi_invoice_try_send()
with freeze_time('2017-01-01'), self.with_mocked_pac_sign_error():
invoice1.l10n_mx_edi_cfdi_invoice_try_update_payments()
pay_sent_doc_values2 = {
'move_id': payment.move_id.id,
'invoice_ids': (invoice1 + invoice3).ids,
'message': "turlututu",
'state': 'payment_sent_failed',
'sat_state': False,
}
self.assertRecordValues(invoice1.l10n_mx_edi_invoice_document_ids.sorted(), [
pay_sent_doc_values2,
pay_sent_doc_values1,
inv1_sent_doc_values,
])
self.assertRecordValues(invoice1, [{
'l10n_mx_edi_update_payments_needed': True,
}])
self.assertRecordValues(invoice2.l10n_mx_edi_invoice_document_ids.sorted(), [
pay_sent_doc_values1,
inv2_sent_doc_values,
])
self.assertRecordValues(invoice2, [{
'l10n_mx_edi_update_payments_needed': False,
}])
inv3_sent_doc_values = {
'move_id': invoice3.id,
'invoice_ids': invoice3.ids,
'message': False,
'state': 'invoice_sent',
'sat_state': 'not_defined',
}
self.assertRecordValues(invoice3.l10n_mx_edi_invoice_document_ids.sorted(), [
pay_sent_doc_values2,
inv3_sent_doc_values,
])
self.assertRecordValues(invoice3, [{
'l10n_mx_edi_update_payments_needed': True,
}])
# Change again the reconciliation.
invoice4 = self._create_invoice_with_amount('2017-01-01', self.comp_curr, 1000.0)
invoice1_rec_line.remove_move_reconcile()
invoice4_rec_line = invoice4.line_ids.filtered(lambda x: x.account_type == 'asset_receivable')
(payment1_rec_line + invoice4_rec_line).reconcile()
with freeze_time('2017-01-01'), self.with_mocked_pac_sign_success():
invoice4._l10n_mx_edi_cfdi_invoice_try_send()
with freeze_time('2017-01-02'), self.with_mocked_pac_sign_success():
invoice4.l10n_mx_edi_cfdi_invoice_try_update_payments()
pay_sent_doc_values2.update({
'invoice_ids': (invoice3 + invoice4).ids,
'message': False,
'state': 'payment_sent',
'sat_state': 'not_defined',
})
self.assertRecordValues(invoice1.l10n_mx_edi_invoice_document_ids.sorted(), [
pay_sent_doc_values1,
inv1_sent_doc_values,
])
self.assertRecordValues(invoice1, [{
'l10n_mx_edi_update_payments_needed': False,
}])
self.assertRecordValues(invoice2, [{
'l10n_mx_edi_update_payments_needed': False,
}])
self.assertRecordValues(invoice3.l10n_mx_edi_invoice_document_ids.sorted(), [
pay_sent_doc_values2,
inv3_sent_doc_values,
])
self.assertRecordValues(invoice3, [{
'l10n_mx_edi_update_payments_needed': False,
}])
inv4_sent_doc_values = {
'datetime': fields.Datetime.from_string('2017-01-02 00:00:00'),
'move_id': invoice4.id,
'invoice_ids': invoice4.ids,
'message': False,
'state': 'invoice_sent',
'sat_state': 'not_defined',
}
self.assertRecordValues(invoice4.l10n_mx_edi_invoice_document_ids.sorted(), [
pay_sent_doc_values2,
inv4_sent_doc_values,
])
self.assertRecordValues(invoice4, [{
'l10n_mx_edi_update_payments_needed': False,
}])
# Try to cancel the payment but it failed.
self.assertRecordValues(payment.l10n_mx_edi_payment_document_ids.sorted(), [
pay_sent_doc_values2,
pay_sent_doc_values1,
])
payment_doc = payment.l10n_mx_edi_payment_document_ids.sorted()[0]
with freeze_time('2017-01-03'), self.with_mocked_pac_cancel_error():
payment_doc.action_cancel()
pay_cancel_doc_values = {
'move_id': payment.move_id.id,
'invoice_ids': (invoice3 + invoice4).ids,
'message': "turlututu",
'state': 'payment_cancel_failed',
'sat_state': False,
}
self.assertRecordValues(payment.l10n_mx_edi_payment_document_ids.sorted(), [
pay_cancel_doc_values,
pay_sent_doc_values2,
pay_sent_doc_values1,
])
self.assertRecordValues(invoice1, [{
'l10n_mx_edi_update_payments_needed': False,
}])
self.assertRecordValues(invoice2, [{
'l10n_mx_edi_update_payments_needed': False,
}])
self.assertRecordValues(invoice3.l10n_mx_edi_invoice_document_ids.sorted(), [
pay_cancel_doc_values,
pay_sent_doc_values2,
inv3_sent_doc_values,
])
self.assertRecordValues(invoice3, [{
'l10n_mx_edi_update_payments_needed': True,
}])
self.assertRecordValues(invoice4.l10n_mx_edi_invoice_document_ids.sorted(), [
pay_cancel_doc_values,
pay_sent_doc_values2,
inv4_sent_doc_values,
])
self.assertRecordValues(invoice4, [{
'l10n_mx_edi_update_payments_needed': True,
}])
# Change the reconciliation and successfully cancel.
invoice3_rec_line.remove_move_reconcile()
(payment1_rec_line + invoice1_rec_line).reconcile()
payment_doc = payment.l10n_mx_edi_payment_document_ids.sorted()[0]
with freeze_time('2017-01-03'), self.with_mocked_pac_cancel_success():
payment_doc.action_retry()
pay_cancel_doc_values.update({
'invoice_ids': (invoice3 + invoice4).ids,
'message': False,
'state': 'payment_cancel',
'sat_state': 'not_defined',
})
pay_sent_doc_values2['sat_state'] = 'skip'
self.assertRecordValues(payment.l10n_mx_edi_payment_document_ids.sorted(), [
pay_cancel_doc_values,
pay_sent_doc_values2,
pay_sent_doc_values1,
])
self.assertRecordValues(invoice1.l10n_mx_edi_invoice_document_ids.sorted(), [
pay_sent_doc_values1,
inv1_sent_doc_values,
])
self.assertRecordValues(invoice1, [{
'l10n_mx_edi_update_payments_needed': False,
}])
self.assertRecordValues(invoice2, [{
'l10n_mx_edi_update_payments_needed': False,
}])
self.assertRecordValues(invoice3.l10n_mx_edi_invoice_document_ids.sorted(), [
pay_cancel_doc_values,
pay_sent_doc_values2,
inv3_sent_doc_values,
])
self.assertRecordValues(invoice3, [{
'l10n_mx_edi_update_payments_needed': False,
}])
self.assertRecordValues(invoice4.l10n_mx_edi_invoice_document_ids.sorted(), [
pay_cancel_doc_values,
pay_sent_doc_values2,
inv4_sent_doc_values,
])
self.assertRecordValues(invoice4, [{
'l10n_mx_edi_update_payments_needed': False,
}])
@freeze_time('2017-01-01')
def test_invoice_cancel_in_locked_period(self):
invoice = self._create_invoice(invoice_date_due='2017-02-01')
with self.with_mocked_pac_sign_success():
invoice._l10n_mx_edi_cfdi_invoice_try_send()
self.assertRecordValues(invoice, [{'l10n_mx_edi_cfdi_state': 'sent'}])
payment = self.env['account.payment.register'] \
.with_context(active_model='account.move', active_ids=invoice.ids) \
.create({}) \
._create_payments()
with self.with_mocked_pac_sign_success():
invoice.l10n_mx_edi_cfdi_invoice_try_update_payments()
self.assertRecordValues(payment, [{'l10n_mx_edi_cfdi_state': 'sent'}])
# Lock the period.
invoice.company_id.fiscalyear_lock_date = '2017-01-01'
# Cancel the invoice.
with self.with_mocked_pac_cancel_success():
self.env['l10n_mx_edi.invoice.cancel'] \
.with_context(invoice.button_request_cancel()['context']) \
.create({'cancellation_reason': '03'}) \
.action_cancel_invoice()
self.assertRecordValues(invoice, [{'l10n_mx_edi_cfdi_state': 'cancel'}])
# Cancel the payment.
with self.with_mocked_pac_cancel_success():
payment.l10n_mx_edi_payment_document_ids.action_cancel()
self.assertRecordValues(payment, [{'l10n_mx_edi_cfdi_state': 'cancel'}])
def test_invoice_payment_production_sign_flow_cancel_from_the_sat(self):
""" Test the case the invoice/payment is signed but the user manually cancel the document from the SAT portal (production environment). """
self.env.company.l10n_mx_edi_pac_test_env = False
self.env.company.l10n_mx_edi_pac_username = 'test'
self.env.company.l10n_mx_edi_pac_password = 'test'
with freeze_time('2017-01-01'):
invoice = self._create_invoice(invoice_date_due='2017-02-01') # Force PPD
with self.with_mocked_pac_sign_success():
invoice._l10n_mx_edi_cfdi_invoice_try_send()
with self.with_mocked_sat_call(lambda _x: 'valid'):
invoice.l10n_mx_edi_cfdi_try_sat()
inv_sent_doc_values = {
'move_id': invoice.id,
'state': 'invoice_sent',
'sat_state': 'valid',
}
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids, [inv_sent_doc_values])
self.assertRecordValues(invoice, [{
'state': 'posted',
'need_cancel_request': True,
'show_reset_to_draft_button': False,
'l10n_mx_edi_update_sat_needed': True,
'l10n_mx_edi_cfdi_sat_state': 'valid',
'l10n_mx_edi_cfdi_state': 'sent',
}])
# Register a payment and sign it.
with freeze_time('2017-06-01'):
payment = self.env['account.payment.register']\
.with_context(active_model='account.move', active_ids=invoice.ids)\
.create({'payment_date': '2017-06-01'})\
._create_payments()
with self.with_mocked_pac_sign_success():
invoice.l10n_mx_edi_cfdi_invoice_try_update_payments()
pay_sent_doc_values = {
'move_id': payment.move_id.id,
'state': 'payment_sent',
'sat_state': 'valid',
}
with self.with_mocked_sat_call(lambda _x: 'valid'):
payment.move_id.l10n_mx_edi_cfdi_try_sat()
self.assertRecordValues(payment.move_id.l10n_mx_edi_payment_document_ids, [pay_sent_doc_values])
self.assertRecordValues(payment.move_id, [{
'state': 'posted',
'need_cancel_request': True,
'show_reset_to_draft_button': False,
'l10n_mx_edi_update_sat_needed': True,
'l10n_mx_edi_cfdi_sat_state': 'valid',
'l10n_mx_edi_cfdi_state': 'sent',
}])
# Manual cancellation from the SAT portal.
with self.with_mocked_sat_call(lambda _x: 'cancelled'):
invoice.l10n_mx_edi_cfdi_try_sat()
inv_cancel_doc_values = {
'move_id': invoice.id,
'state': 'invoice_cancel',
'sat_state': 'cancelled',
}
pay_cancel_doc_values = {
'move_id': payment.move_id.id,
'state': 'payment_cancel',
'sat_state': 'cancelled',
}
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids.sorted(), [
pay_cancel_doc_values,
inv_cancel_doc_values,
pay_sent_doc_values,
inv_sent_doc_values,
])
self.assertRecordValues(invoice, [{
'state': 'cancel',
'need_cancel_request': False,
'show_reset_to_draft_button': True,
'l10n_mx_edi_update_sat_needed': False,
'l10n_mx_edi_cfdi_sat_state': 'cancelled',
'l10n_mx_edi_cfdi_state': 'cancel',
}])
self.assertRecordValues(payment.move_id, [{
'state': 'cancel',
'need_cancel_request': False,
'show_reset_to_draft_button': True,
'l10n_mx_edi_update_sat_needed': False,
'l10n_mx_edi_cfdi_sat_state': 'cancelled',
'l10n_mx_edi_cfdi_state': 'cancel',
}])
def test_global_invoice_production_sign_flow_cancel_from_the_sat(self):
""" Test the case the global invoice is signed but the user manually cancel the document from the SAT portal (production environment). """
self.env.company.l10n_mx_edi_pac_test_env = False
self.env.company.l10n_mx_edi_pac_username = 'test'
self.env.company.l10n_mx_edi_pac_password = 'test'
with freeze_time('2017-01-01'):
invoice = self._create_invoice(l10n_mx_edi_cfdi_to_public=True)
with self.with_mocked_pac_sign_success():
invoice._l10n_mx_edi_cfdi_global_invoice_try_send()
with self.with_mocked_sat_call(lambda _x: 'valid'):
invoice.l10n_mx_edi_cfdi_try_sat()
sent_doc_values = {
'invoice_ids': invoice.ids,
'state': 'ginvoice_sent',
'sat_state': 'valid',
}
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids, [sent_doc_values])
self.assertRecordValues(invoice, [{
'l10n_mx_edi_update_sat_needed': True,
'l10n_mx_edi_cfdi_sat_state': 'valid',
'l10n_mx_edi_cfdi_state': 'global_sent',
}])
# Manual cancellation from the SAT portal.
with self.with_mocked_sat_call(lambda _x: 'cancelled'):
invoice.l10n_mx_edi_cfdi_try_sat()
cancel_doc_values = {
'invoice_ids': invoice.ids,
'state': 'ginvoice_cancel',
'sat_state': 'cancelled',
}
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids.sorted(), [
cancel_doc_values,
sent_doc_values,
])
self.assertRecordValues(invoice, [{
'l10n_mx_edi_update_sat_needed': False,
'l10n_mx_edi_cfdi_sat_state': 'cancelled',
'l10n_mx_edi_cfdi_state': 'global_cancel',
'show_reset_to_draft_button': True,
}])
@freeze_time('2017-01-01')
def test_invoice_production_sign_flow_cancel_from_odoo(self):
""" Test the case the invoice is signed and the user request a cancellation in Odoo (production environment). """
self.env.company.l10n_mx_edi_pac_test_env = False
self.env.company.l10n_mx_edi_pac_username = 'test'
self.env.company.l10n_mx_edi_pac_password = 'test'
invoice = self._create_invoice(invoice_date_due='2017-02-01') # Force PPD
with self.with_mocked_pac_sign_success():
invoice._l10n_mx_edi_cfdi_invoice_try_send()
sent_doc_values = {
'move_id': invoice.id,
'state': 'invoice_sent',
'sat_state': 'not_defined',
}
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids, [sent_doc_values])
# Approval of the sat.
with self.with_mocked_sat_call(lambda _x: 'valid'):
invoice.l10n_mx_edi_cfdi_try_sat()
sent_doc_values['sat_state'] = 'valid'
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids, [sent_doc_values])
self.assertRecordValues(invoice, [{
'state': 'posted',
'need_cancel_request': True,
'show_reset_to_draft_button': False,
'l10n_mx_edi_update_sat_needed': True,
'l10n_mx_edi_cfdi_sat_state': 'valid',
'l10n_mx_edi_cfdi_state': 'sent',
}])
# Request Cancel.
with self.with_mocked_pac_cancel_success():
self.env['l10n_mx_edi.invoice.cancel']\
.with_context(invoice.button_request_cancel()['context'])\
.create({'cancellation_reason': '02'})\
.action_cancel_invoice()
cancel_request_doc_values = {
'move_id': invoice.id,
'state': 'invoice_cancel_requested',
'sat_state': 'not_defined',
}
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids.sorted(), [
cancel_request_doc_values,
sent_doc_values,
])
self.assertRecordValues(invoice, [{
'state': 'posted',
'need_cancel_request': False,
'show_reset_to_draft_button': False,
'l10n_mx_edi_update_sat_needed': True,
'l10n_mx_edi_cfdi_sat_state': 'not_defined',
'l10n_mx_edi_cfdi_state': 'cancel_requested',
}])
# The SAT rejected the cancellation.
with self.with_mocked_sat_call(lambda _x: 'valid'):
invoice.l10n_mx_edi_cfdi_try_sat()
cancel_request_doc_values['sat_state'] = 'valid'
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids.sorted(), [
cancel_request_doc_values,
sent_doc_values,
])
self.assertRecordValues(invoice, [{
'state': 'posted',
'need_cancel_request': True,
'show_reset_to_draft_button': False,
'l10n_mx_edi_update_sat_needed': True,
'l10n_mx_edi_cfdi_sat_state': 'valid',
'l10n_mx_edi_cfdi_state': 'sent',
}])
# Request Cancel again!
with self.with_mocked_pac_cancel_success():
self.env['l10n_mx_edi.invoice.cancel']\
.with_context(invoice.button_request_cancel()['context'])\
.create({'cancellation_reason': '02'})\
.action_cancel_invoice()
cancel_request_doc_values_2 = {
'move_id': invoice.id,
'state': 'invoice_cancel_requested',
'sat_state': 'not_defined',
}
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids.sorted(), [
cancel_request_doc_values_2,
cancel_request_doc_values,
sent_doc_values,
])
self.assertRecordValues(invoice, [{
'state': 'posted',
'need_cancel_request': False,
'show_reset_to_draft_button': False,
'l10n_mx_edi_update_sat_needed': True,
'l10n_mx_edi_cfdi_sat_state': 'not_defined',
'l10n_mx_edi_cfdi_state': 'cancel_requested',
}])
# The SAT approved the cancellation.
with self.with_mocked_sat_call(lambda _x: 'cancelled'):
invoice.l10n_mx_edi_cfdi_try_sat()
cancel_doc_values = {
'move_id': invoice.id,
'state': 'invoice_cancel',
'sat_state': 'cancelled',
}
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids.sorted(), [
cancel_doc_values,
cancel_request_doc_values,
sent_doc_values,
])
self.assertRecordValues(invoice, [{
'state': 'cancel',
'need_cancel_request': False,
'show_reset_to_draft_button': True,
'l10n_mx_edi_update_sat_needed': False,
'l10n_mx_edi_cfdi_sat_state': 'cancelled',
'l10n_mx_edi_cfdi_state': 'cancel',
}])
@freeze_time('2017-01-01')
def test_invoice_test_sign_flow_cancel_from_odoo(self):
""" Test the case the invoice is signed and the user request a cancellation in Odoo (testing environment). """
invoice = self._create_invoice(invoice_date_due='2017-02-01') # Force PPD
with self.with_mocked_pac_sign_success():
invoice._l10n_mx_edi_cfdi_invoice_try_send()
sent_doc_values = {
'move_id': invoice.id,
'state': 'invoice_sent',
'sat_state': 'not_defined',
}
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids, [sent_doc_values])
# Approval of the sat.
with self.with_mocked_sat_call(lambda _x: 'valid'):
invoice.l10n_mx_edi_cfdi_try_sat()
sent_doc_values['sat_state'] = 'valid'
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids, [sent_doc_values])
self.assertRecordValues(invoice, [{
'state': 'posted',
'need_cancel_request': True,
'show_reset_to_draft_button': False,
'l10n_mx_edi_update_sat_needed': True,
'l10n_mx_edi_cfdi_sat_state': 'valid',
'l10n_mx_edi_cfdi_state': 'sent',
}])
# Request Cancel.
with self.with_mocked_pac_cancel_success():
self.env['l10n_mx_edi.invoice.cancel']\
.with_context(invoice.button_request_cancel()['context'])\
.create({'cancellation_reason': '02'})\
.action_cancel_invoice()
cancel_doc_values = {
'move_id': invoice.id,
'state': 'invoice_cancel',
'sat_state': 'not_defined',
}
self.assertRecordValues(invoice.l10n_mx_edi_invoice_document_ids.sorted(), [
cancel_doc_values,
sent_doc_values,
])
self.assertRecordValues(invoice, [{
'state': 'cancel',
'need_cancel_request': False,
'show_reset_to_draft_button': True,
'l10n_mx_edi_update_sat_needed': True,
'l10n_mx_edi_cfdi_sat_state': 'not_defined',
'l10n_mx_edi_cfdi_state': 'cancel',
}])
@freeze_time('2017-01-01')
def test_payment_to_multiple_invoices_with_different_rfc(self):
invoice1 = self._create_invoice(partner_id=self.partner_mx.id, invoice_date_due='2017-02-01')
invoice2 = self._create_invoice(partner_id=self.partner_us.id, invoice_date_due='2017-02-01')
with self.with_mocked_pac_sign_success():
invoice1._l10n_mx_edi_cfdi_invoice_try_send()
invoice2._l10n_mx_edi_cfdi_invoice_try_send()
payment = self.env['account.payment'].create({
'payment_type': 'inbound',
'partner_type': 'customer',
'amount': sum((invoice1 + invoice2).mapped('amount_total')),
'date': '2017-01-01',
})
payment.action_post()
(invoice1 + invoice2 + payment.move_id).line_ids\
.filtered(lambda x: x.account_type == 'asset_receivable')\
.reconcile()
with self.with_mocked_pac_sign_success():
invoice1.l10n_mx_edi_cfdi_invoice_try_update_payments()
self.assertRecordValues(payment.l10n_mx_edi_payment_document_ids.sorted(), [
{
'move_id': payment.move_id.id,
'invoice_ids': (invoice1 + invoice2).ids,
'message': "You can't register a payment for invoices having different RFCs.",
'state': 'payment_sent_failed',
},
])
@freeze_time('2017-01-01')
def test_invoice_to_public_flow(self):
invoice = self._create_invoice(
partner_id=self.partner_mx.id,
l10n_mx_edi_cfdi_to_public=True,
)
with self.with_mocked_pac_sign_success():
invoice._l10n_mx_edi_cfdi_invoice_try_send()
self.assertRecordValues(invoice, [{'l10n_mx_edi_cfdi_state': 'sent'}])
self.assertEqual(len(invoice.l10n_mx_edi_invoice_document_ids), 1)
self.env['account.payment.register']\
.with_context(active_model='account.move', active_ids=invoice.ids)\
.create({})\
._create_payments()
with self.with_mocked_pac_sign_success():
invoice.l10n_mx_edi_cfdi_invoice_try_update_payments()
self.assertEqual(len(invoice.l10n_mx_edi_invoice_document_ids), 2)
@freeze_time('2017-01-01')
def test_invoice_cancellation_01(self):
# Create and send the invoice.
invoice = self._create_invoice()
with self.with_mocked_pac_sign_success():
invoice._l10n_mx_edi_cfdi_invoice_try_send()
self.assertRecordValues(invoice, [{
'l10n_mx_edi_cfdi_state': 'sent',
'l10n_mx_edi_invoice_cancellation_reason': False,
'l10n_mx_edi_cfdi_origin': False,
'need_cancel_request': True,
'show_reset_to_draft_button': False,
'state': 'posted',
}])
# Create a replacement invoice.
action_results = self.env['l10n_mx_edi.invoice.cancel'] \
.with_context(invoice.button_request_cancel()['context']) \
.create({}) \
.action_create_replacement_invoice()
new_invoice = self.env['account.move'].browse(action_results['res_id'])
invoice.invalidate_recordset(fnames=['need_cancel_request', 'l10n_mx_edi_cfdi_cancel_id'])
self.assertRecordValues(invoice, [{
'l10n_mx_edi_cfdi_state': 'sent',
'l10n_mx_edi_invoice_cancellation_reason': False,
'l10n_mx_edi_cfdi_origin': False,
'need_cancel_request': False,
'show_reset_to_draft_button': False,
'l10n_mx_edi_cfdi_cancel_id': new_invoice.id,
'state': 'posted',
}])
self.assertRecordValues(new_invoice, [{
'l10n_mx_edi_cfdi_state': False,
'l10n_mx_edi_invoice_cancellation_reason': False,
'l10n_mx_edi_cfdi_origin': f'04|{invoice.l10n_mx_edi_cfdi_uuid}',
'need_cancel_request': False,
'show_reset_to_draft_button': False,
'state': 'draft',
}])
# Sign the replacement invoice.
new_invoice.action_post()
with self.with_mocked_pac_sign_success():
new_invoice._l10n_mx_edi_cfdi_invoice_try_send()
invoice.invalidate_recordset(fnames=['need_cancel_request', 'l10n_mx_edi_cfdi_cancel_id'])
self.assertRecordValues(invoice, [{
'l10n_mx_edi_cfdi_state': 'sent',
'l10n_mx_edi_invoice_cancellation_reason': False,
'l10n_mx_edi_cfdi_origin': False,
'need_cancel_request': True,
'show_reset_to_draft_button': False,
'l10n_mx_edi_cfdi_cancel_id': new_invoice.id,
'state': 'posted',
}])
self.assertRecordValues(new_invoice, [{
'l10n_mx_edi_cfdi_state': 'sent',
'l10n_mx_edi_invoice_cancellation_reason': False,
'l10n_mx_edi_cfdi_origin': f'04|{invoice.l10n_mx_edi_cfdi_uuid}',
'need_cancel_request': True,
'show_reset_to_draft_button': False,
'state': 'posted',
}])
# Cancel the replacement invoice.
with self.with_mocked_pac_cancel_success():
self.env['l10n_mx_edi.invoice.cancel']\
.with_context(new_invoice.button_request_cancel()['context'])\
.create({'cancellation_reason': '02'})\
.action_cancel_invoice()
invoice.invalidate_recordset(fnames=['need_cancel_request', 'l10n_mx_edi_cfdi_cancel_id'])
self.assertRecordValues(invoice, [{
'l10n_mx_edi_cfdi_state': 'sent',
'l10n_mx_edi_invoice_cancellation_reason': False,
'l10n_mx_edi_cfdi_origin': False,
'need_cancel_request': True,
'show_reset_to_draft_button': False,
'l10n_mx_edi_cfdi_cancel_id': new_invoice.id,
'state': 'posted',
}])
self.assertRecordValues(new_invoice, [{
'l10n_mx_edi_cfdi_state': 'cancel',
'l10n_mx_edi_invoice_cancellation_reason': '02',
'l10n_mx_edi_cfdi_origin': f'04|{invoice.l10n_mx_edi_cfdi_uuid}',
'need_cancel_request': False,
'show_reset_to_draft_button': True,
'state': 'cancel',
}])
with self.with_mocked_pac_cancel_success():
self.env['l10n_mx_edi.invoice.cancel']\
.with_context(invoice.button_request_cancel()['context'])\
.create({})\
.action_cancel_invoice()
self.assertRecordValues(invoice, [{
'l10n_mx_edi_cfdi_state': 'cancel',
'l10n_mx_edi_invoice_cancellation_reason': '01',
'l10n_mx_edi_cfdi_origin': False,
'need_cancel_request': False,
'show_reset_to_draft_button': True,
'l10n_mx_edi_cfdi_cancel_id': new_invoice.id,
'state': 'cancel',
}])
@freeze_time('2017-01-01')
def test_invoice_cancellation_02(self):
invoice = self._create_invoice()
with self.with_mocked_pac_sign_success():
invoice._l10n_mx_edi_cfdi_invoice_try_send()
self.assertRecordValues(invoice, [{
'l10n_mx_edi_cfdi_state': 'sent',
'l10n_mx_edi_invoice_cancellation_reason': False,
'l10n_mx_edi_cfdi_origin': False,
'need_cancel_request': True,
'show_reset_to_draft_button': False,
'state': 'posted',
}])
with self.with_mocked_pac_cancel_success():
self.env['l10n_mx_edi.invoice.cancel'] \
.with_context(invoice.button_request_cancel()['context']) \
.create({'cancellation_reason': '02'})\
.action_cancel_invoice()
self.assertRecordValues(invoice, [{
'l10n_mx_edi_cfdi_state': 'cancel',
'l10n_mx_edi_invoice_cancellation_reason': '02',
'l10n_mx_edi_cfdi_origin': False,
'need_cancel_request': False,
'show_reset_to_draft_button': True,
'state': 'cancel',
}])
@freeze_time('2017-01-01')
def test_global_invoice_cancellation_01(self):
# Create and send the invoice.
invoice1 = self._create_invoice(l10n_mx_edi_cfdi_to_public=True)
invoice2 = self._create_invoice(l10n_mx_edi_cfdi_to_public=True)
invoices = invoice1 + invoice2
# Sign as a global invoice.
with self.with_mocked_pac_sign_success():
invoices._l10n_mx_edi_cfdi_global_invoice_try_send()
sent_doc_values1 = {
'invoice_ids': invoices.ids,
'state': 'ginvoice_sent',
'attachment_uuid': invoices[0].l10n_mx_edi_cfdi_uuid,
'attachment_origin': False,
'cancellation_reason': False,
}
self.assertRecordValues(invoices.l10n_mx_edi_invoice_document_ids, [sent_doc_values1])
self.assertRecordValues(invoice1, [{
'l10n_mx_edi_cfdi_state': 'global_sent',
'l10n_mx_edi_cfdi_uuid': sent_doc_values1['attachment_uuid'],
}])
# Request a replacement for the global invoice.
gi_doc1 = invoices.l10n_mx_edi_invoice_document_ids
with self.with_mocked_pac_sign_success():
self.env['l10n_mx_edi.invoice.cancel']\
.with_context(gi_doc1.action_request_cancel()['context'])\
.create({})\
.action_create_replacement_invoice()
sent_doc_values2 = {
'invoice_ids': invoices.ids,
'state': 'ginvoice_sent',
'attachment_uuid': invoices[0].l10n_mx_edi_cfdi_uuid,
'attachment_origin': f"04|{sent_doc_values1['attachment_uuid']}",
'cancellation_reason': False,
}
self.assertRecordValues(invoices.l10n_mx_edi_invoice_document_ids.sorted(), [
sent_doc_values2,
sent_doc_values1,
])
self.assertRecordValues(invoice1, [{
'l10n_mx_edi_cfdi_state': 'global_sent',
'l10n_mx_edi_cfdi_uuid': sent_doc_values2['attachment_uuid'],
}])
# Cancel the first global invoice.
with self.with_mocked_pac_cancel_success():
self.env['l10n_mx_edi.invoice.cancel']\
.with_context(gi_doc1.action_request_cancel()['context'])\
.create({})\
.action_cancel_invoice()
cancel_doc_values = {
'invoice_ids': invoices.ids,
'state': 'ginvoice_cancel',
'attachment_uuid': sent_doc_values1['attachment_uuid'],
'attachment_origin': False,
'cancellation_reason': '01',
}
self.assertRecordValues(invoices.l10n_mx_edi_invoice_document_ids.sorted(), [
cancel_doc_values,
sent_doc_values2,
sent_doc_values1,
])
self.assertRecordValues(invoice1, [{
'l10n_mx_edi_cfdi_state': 'global_sent',
'l10n_mx_edi_cfdi_uuid': sent_doc_values2['attachment_uuid'],
}])
@freeze_time('2017-01-01')
def test_global_invoice_then_replacement_then_cancel_replacement_then_cancel_gi(self):
invoice1 = self._create_invoice(l10n_mx_edi_cfdi_to_public=True)
invoice2 = self._create_invoice(l10n_mx_edi_cfdi_to_public=True)
invoices = invoice1 + invoice2
# Failed to send the global invoice.
with self.with_mocked_pac_sign_error():
invoices._l10n_mx_edi_cfdi_global_invoice_try_send()
sent_doc_values1 = {
'move_id': None,
'invoice_ids': invoices.ids,
'message': "turlututu",
'state': 'ginvoice_sent_failed',
'sat_state': False,
'attachment_uuid': False,
'attachment_origin': False,
'cancellation_reason': False,
'retry_button_needed': True,
'cancel_button_needed': False,
}
self.assertRecordValues(invoices.l10n_mx_edi_invoice_document_ids, [sent_doc_values1])
self.assertTrue(invoices.l10n_mx_edi_invoice_document_ids.attachment_id)
# Successfully create the global invoice.
with self.with_mocked_pac_sign_success():
invoices.l10n_mx_edi_invoice_document_ids.action_retry()
gi_attachment = invoices.l10n_mx_edi_cfdi_attachment_id
self.assertEqual(len(gi_attachment), 1)
sent_doc_values1.update({
'message': False,
'state': 'ginvoice_sent',
'sat_state': 'not_defined',
'attachment_id': gi_attachment.id,
'attachment_uuid': invoices[0].l10n_mx_edi_cfdi_uuid,
'attachment_origin': False,
'retry_button_needed': False,
'cancel_button_needed': True,
})
self.assertRecordValues(invoices.l10n_mx_edi_invoice_document_ids, [sent_doc_values1])
self.assertRecordValues(invoices, [{'l10n_mx_edi_update_sat_needed': True}] * 2)
with self.with_mocked_sat_call(lambda _x: 'valid'):
self.env['l10n_mx_edi.document']._fetch_and_update_sat_status(
extra_domain=[('id', '=', invoices.l10n_mx_edi_invoice_document_ids.id)]
)
sent_doc_values1['sat_state'] = 'valid'
self.assertRecordValues(invoices.l10n_mx_edi_invoice_document_ids, [sent_doc_values1])
# Request a replacement for the global invoice.
gi_doc1 = invoices.l10n_mx_edi_invoice_document_ids
with self.with_mocked_pac_sign_success():
self.env['l10n_mx_edi.invoice.cancel']\
.with_context(gi_doc1.action_request_cancel()['context'])\
.create({})\
.action_create_replacement_invoice()
gi_doc2 = invoices.l10n_mx_edi_invoice_document_ids.sorted()[0]
self.assertTrue(gi_doc2.attachment_id)
sent_doc_values2 = {
'move_id': None,
'invoice_ids': invoices.ids,
'message': False,
'state': 'ginvoice_sent',
'sat_state': 'not_defined',
'attachment_id': gi_doc2.attachment_id.id,
'attachment_uuid': invoices[0].l10n_mx_edi_cfdi_uuid,
'attachment_origin': f'04|{gi_doc1.attachment_uuid}',
'cancellation_reason': False,
'retry_button_needed': False,
'cancel_button_needed': True,
}
self.assertRecordValues(invoices.l10n_mx_edi_invoice_document_ids.sorted(), [
sent_doc_values2,
sent_doc_values1,
])
# Request a replacement for the global invoice but it failed.
with self.with_mocked_pac_sign_error():
self.env['l10n_mx_edi.invoice.cancel']\
.with_context(gi_doc2.action_request_cancel()['context'])\
.create({})\
.action_create_replacement_invoice()
gi_doc3 = invoices.l10n_mx_edi_invoice_document_ids.sorted()[0]
self.assertTrue(gi_doc3.attachment_id)
sent_doc_values3 = {
'move_id': None,
'invoice_ids': invoices.ids,
'message': "turlututu",
'state': 'ginvoice_sent_failed',
'sat_state': False,
'attachment_id': gi_doc3.attachment_id.id,
'attachment_uuid': False,
'attachment_origin': f'04|{gi_doc2.attachment_uuid}',
'cancellation_reason': False,
'retry_button_needed': True,
'cancel_button_needed': False,
}
self.assertRecordValues(invoices.l10n_mx_edi_invoice_document_ids.sorted(), [
sent_doc_values3,
sent_doc_values2,
sent_doc_values1,
])
# Failed to cancel the second global invoice with cancellation reason 02.
with self.with_mocked_pac_cancel_error():
self.env['l10n_mx_edi.invoice.cancel']\
.with_context(gi_doc2.action_request_cancel()['context'])\
.create({'cancellation_reason': '02'})\
.action_cancel_invoice()
cancel_doc_values1 = {
'move_id': None,
'invoice_ids': invoices.ids,
'message': "turlututu",
'state': 'ginvoice_cancel_failed',
'sat_state': False,
'attachment_id': gi_doc2.attachment_id.id,
'attachment_uuid': gi_doc2.attachment_uuid,
'attachment_origin': f'04|{gi_doc1.attachment_uuid}',
'cancellation_reason': '02',
'retry_button_needed': True,
'cancel_button_needed': False,
}
self.assertRecordValues(invoices.l10n_mx_edi_invoice_document_ids.sorted(), [
cancel_doc_values1,
sent_doc_values2,
sent_doc_values1,
])
# Retry the cancellation of the second global invoice.
with self.with_mocked_pac_cancel_success():
invoices.l10n_mx_edi_invoice_document_ids.sorted()[0].action_retry()
cancel_doc_values1.update({
'message': False,
'state': 'ginvoice_cancel',
'sat_state': 'not_defined',
'retry_button_needed': False,
'cancel_button_needed': False,
})
sent_doc_values2['sat_state'] = 'skip'
self.assertRecordValues(invoices.l10n_mx_edi_invoice_document_ids.sorted(), [
cancel_doc_values1,
sent_doc_values2,
sent_doc_values1,
])
# Successfully cancel the first global invoice.
with self.with_mocked_pac_cancel_success():
self.env['l10n_mx_edi.invoice.cancel']\
.with_context(gi_doc1.action_request_cancel()['context'])\
.create({})\
.action_cancel_invoice()
cancel_doc_values2 = {
'move_id': None,
'invoice_ids': invoices.ids,
'message': False,
'state': 'ginvoice_cancel',
'sat_state': 'not_defined',
'attachment_id': gi_doc1.attachment_id.id,
'attachment_uuid': gi_doc1.attachment_uuid,
'attachment_origin': False,
'cancellation_reason': '01',
'retry_button_needed': False,
'cancel_button_needed': False,
}
self.assertRecordValues(invoices.l10n_mx_edi_invoice_document_ids.sorted(), [
cancel_doc_values2,
cancel_doc_values1,
sent_doc_values2,
sent_doc_values1,
])
@freeze_time('2017-01-01')
def test_global_invoice_after_failing_send_invoice(self):
invoice = self._create_invoice(l10n_mx_edi_cfdi_to_public=True)
with self.with_mocked_pac_sign_error():
invoice._l10n_mx_edi_cfdi_invoice_try_send()
self.assertEqual(len(invoice.l10n_mx_edi_invoice_document_ids), 1)
with self.with_mocked_pac_sign_success():
invoice._l10n_mx_edi_cfdi_global_invoice_try_send()
self.assertEqual(len(invoice.l10n_mx_edi_invoice_document_ids), 1)
def test_invoice_cancellation_then_replacement_in_foreign_currency(self):
date_1 = '2017-01-01'
date_2 = '2017-01-02'
self.setup_rates(self.usd, (date_1, 1 / 17.187), (date_2, 1 / 17.0357))
with freeze_time(date_1):
# create an invoice in USD when currency rate is 17.187
invoice = self._create_invoice(
invoice_date=date_1,
date=date_1,
currency_id=self.usd.id,
invoice_line_ids=[
Command.create({
'product_id': self.product.id,
'price_unit': 100,
'quantity': 1,
'tax_ids': [Command.set(self.tax_16.ids)],
}),
],
)
with self.with_mocked_pac_sign_success():
invoice._l10n_mx_edi_cfdi_invoice_try_send()
self.assertRecordValues(invoice.line_ids, [
{
'amount_currency': -100.0,
'balance': -1718.7,
'debit': 0.0,
'credit': 1718.7,
},
{
'amount_currency': -16.0,
'balance': -274.99,
'debit': 0.0,
'credit': 274.99,
},
{
'amount_currency': 116.0,
'balance': 1993.69,
'debit': 1993.69,
'credit': 0.0,
},
])
# create a replacement invoice when currency rate is 17.0357
with freeze_time(date_2):
action_results = self.env['l10n_mx_edi.invoice.cancel'] \
.with_context(invoice.button_request_cancel()['context']) \
.create({}) \
.action_create_replacement_invoice()
new_invoice = self.env['account.move'].browse(action_results['res_id'])
# the amounts of the replacement invoice should use the current rate
self.assertRecordValues(new_invoice.line_ids, [
{
'amount_currency': -100.0,
'balance': -1703.57,
'debit': 0.0,
'credit': 1703.57,
'tax_base_amount': 0.0,
},
{
'amount_currency': -16.0,
'balance': -272.57,
'debit': 0.0,
'credit': 272.57,
'tax_base_amount': 1703.57,
},
{
'amount_currency': 116.0,
'balance': 1976.14,
'debit': 1976.14,
'credit': 0.0,
'tax_base_amount': 0.0,
},
])
def test_cannot_delete_edi_document(self):
invoice = self._create_invoice(invoice_date_due='2017-01-01')
with freeze_time('2017-01-07'), self.with_mocked_pac_sign_success():
invoice._l10n_mx_edi_cfdi_invoice_try_send()
self.assertEqual(len(invoice.l10n_mx_edi_invoice_document_ids), 1)
with self.assertRaises(UserError, msg="You can't unlink an attachment being an EDI document sent to the government."):
invoice.l10n_mx_edi_invoice_document_ids.attachment_id.unlink()
@freeze_time('2017-01-01 10:00:00')
def test_global_invoice_year_month_format(self):
""" Test that 'meses' and 'anno' are correctly set since we ignore them in
other tests to allow to dynamically generate documents.
"""
invoice = self._create_invoice(
l10n_mx_edi_cfdi_to_public=True,
invoice_line_ids=[
Command.create({
'product_id': self.product.id,
'price_unit': 1000.0,
'tax_ids': [],
}),
],
)
with self.with_mocked_pac_sign_success():
invoice._l10n_mx_edi_cfdi_global_invoice_try_send()
self._assert_global_invoice_cfdi_from_invoices(invoice, 'test_global_invoice_year_month_format')