from __future__ import annotations from datetime import datetime, timedelta from types import SimpleNamespace from app.domain.contas.contestation_rules import ( complete_invoices_context, resolve_refund_option, billing_cutoff_reference, next_cutoff_from_cut_date, due_day, manual_conta_certa_from_evidence, ) from app.domain.contas.workflow_actions import build_contas_workflow_actions def _complete(status='Em aberto', payment_type=2, cut_date=None): row={'invoiceId':'I1','invoiceStatus':status,'dueDate':'2026-08-25'} if cut_date is not None: row['cutDate']=cut_date return {'billingProfile':{'paymentTypeId':payment_type},'paymentItems':[row]} def test_refund_boleto_para_fatura_em_aberto_com_fatura_normal(): c=complete_invoices_context(_complete('Em aberto',2),'I1') r=resolve_refund_option(payment_method=c['payment_method'],has_open_bill=c['has_open_bill'],invoice_statuses=c['invoice_statuses']) assert r['refund_option']=='1' and r['format_text']=='sms' def test_refund_credito_para_dacc_cartao_ou_parcelamento(): for method in ('DACC|Cartão|Parcelamento','debito_automatico','cartao_credito'): r=resolve_refund_option(payment_method=method,has_open_bill=True,invoice_statuses=['Em aberto']) assert r['refund_option']=='0' def test_refund_credito_para_fatura_paga_ou_ja_contestada(): for status in ('Pago','Contestada'): r=resolve_refund_option(payment_method='fatura',has_open_bill=False,invoice_statuses=[status]) assert r['refund_option']=='0' def test_cutoff_referencia_corrige_bug_mes_seguinte(): ref=datetime(2026,8,18,12) passed, cutoff=billing_cutoff_reference(10,reference_datetime=ref) assert passed is True and cutoff.date().isoformat()=='2026-08-10' passed2, cutoff2=billing_cutoff_reference(25,reference_datetime=ref) assert passed2 is True and cutoff2.date().isoformat()=='2026-07-25' def test_cutdate_regra_um_mes(): ref=datetime(2026,8,18) assert next_cutoff_from_cut_date(datetime(2026,6,1),reference_datetime=ref)[0] is True assert next_cutoff_from_cut_date(datetime(2026,8,1),reference_datetime=ref)[0] is False def test_due_day_iso_e_br(): assert due_day('2026-08-25')==25 assert due_day('25/08/2026')==25 def test_dependente_forca_conta_certa_manual(): assert manual_conta_certa_from_evidence(dependent_invoice_item=True) is True def test_cutdate_da_fatura_define_manual(): ref=datetime(2026,8,18) assert manual_conta_certa_from_evidence(invoice_item={'cutDate':'2026-06-01'},reference_datetime=ref) is True assert manual_conta_certa_from_evidence(invoice_item={'cutDate':'2026-08-01'},reference_datetime=ref) is False def test_check_invoice_status_enriquece_prefetch_sem_nova_api(): class Client: def consultar_faturas(self,*a,**k): raise AssertionError('não deve chamar API') class Service: client=Client() a=build_contas_workflow_actions(Service()).get('check_invoice_status') out=a({'complete_invoices_payload':_complete('Em aberto',3),'invoice_id':'I1'}, {'input':{},'vars':{}}) assert out['success'] is True assert out['invoice_status']=='Em aberto' assert out['has_open_bill'] is True assert out['payment_type_id']==3 assert out['requires_sms'] is True def test_check_invoice_status_falha_sem_prefetch(): class Service: pass a=build_contas_workflow_actions(Service()).get('check_invoice_status') try: a({'invoice_id':'I1'},{'input':{},'vars':{}}) except RuntimeError as exc: assert 'prefetch' in str(exc) else: raise AssertionError('deveria falhar sem prefetch') def test_abrir_contestacao_deriva_refund_manual_e_normaliza_barcode_zero(): captured={} class Client: def billing_analysis(self,msisdn): return {'119':{'SVA Detalhe Total':[{'desc':'VAS Musica','value':29.90}]}} def contrato(self,msisdn): return {'billing_profile':{'date':{'cutoffDay':1}}} def contestar(self,payload): captured.update(payload) return {'itemsResponse':[{'status':'CRIAR','itemName':'VAS Musica','correctAccountStatus':'CRIAR'}],'barcode':'0'*44} class Service: client=Client() a=build_contas_workflow_actions(Service()).get('abrir_contestacao_cliente') complete=_complete('Em aberto',2) state={'input':{},'vars':{'check_invoice_status':{'complete_invoices_payload':complete,**complete_invoices_context(complete,'I1')}}} out=a({'msisdn':'119','social_sec_no':'123','customer_id':'C1','invoice_id':'I1','items':[{'itemName':'VAS Musica','claimedAmount':'29,90','validatedAmount':'29,90'}],'protocolo_id':'P1','skip_invoice_item_validation':True},state) assert captured['refundOption']=='1' assert captured['manualContaCertaIndicator'] is True assert captured['contestationType']=='0' assert captured['adjustReason']=='SERVICO_NAO_SOLICITADO' assert out['barcode']=='' assert out['conta_certa_status']=='CRIAR'