Import ClientFlow production v4928.1.5.132.4

This commit is contained in:
plx
2026-07-29 13:11:01 +00:00
parent 6445044ac6
commit 261d342057
405 changed files with 48373 additions and 1401 deletions

View File

@@ -0,0 +1,125 @@
from app.domain.opportunity_flow import build_opportunity_evidence, decide_opportunity_next_action, load_company_profile
from app.domain.opportunity_flow.audit import audit_decisions
def _profile():
return load_company_profile("blif")
def test_blif_profile_loads_defaults_and_labels():
profile = _profile()
assert profile.company == "blif"
assert profile.defaults["payment_terms"] == "before_shipping"
assert profile.defaults["delivery_terms"] == "carrier"
assert profile.action_label("CONFIRM_PAYMENT") == "Confirmar pagamento"
assert profile.documents["quote"]["label"] == "Orçamento"
assert profile.documents["invoice"]["label"] == "Fatura"
def test_quote_before_shipping_requires_confirm_payment_before_invoice():
evidence = build_opportunity_evidence(
{"id": "opp-1", "stage": "QUOTE_SENT", "metadata": {"payment_terms": "before_shipping"}},
linked_customer={"id": "c1", "tax_id": "123", "billing_email": "a@b.pt", "address": "Rua", "postal_code": "1000", "city": "Lisboa"},
linked_documents=[{"id": "q1", "document_kind": "quotation", "document_number": "ORC.ORC2026.177", "total_amount": 202.95}],
operation_snapshot={"links": [], "cards": []},
fiscal_data_complete=True,
)
decision = decide_opportunity_next_action(evidence, _profile())
assert decision.next_action.code == "CONFIRM_PAYMENT"
assert decision.next_action.document_number == "ORC.ORC2026.177"
assert "antes da fatura" in decision.next_action.description or "antes de emitir fatura" in decision.next_action.description
def test_payment_confirmed_without_invoice_sends_invoice():
evidence = build_opportunity_evidence(
{"id": "opp-2", "stage": "PAYMENT_CONFIRMED", "metadata": {"payment_terms": "before_shipping"}},
linked_customer={"id": "c1", "tax_id": "123", "billing_email": "a@b.pt", "address": "Rua", "postal_code": "1000", "city": "Lisboa"},
linked_documents=[{"id": "q1", "document_kind": "quotation", "document_number": "ORC.ORC2026.177"}],
operation_snapshot={"links": [{"system": "clientflow", "external_type": "payment", "status": "confirmed"}], "cards": []},
fiscal_data_complete=True,
)
decision = decide_opportunity_next_action(evidence, _profile())
assert decision.next_action.code == "SEND_INVOICE"
assert decision.next_action.label == "Enviar fatura ao cliente"
def test_payment_invoice_pending_send_keeps_send_invoice_before_waiting_production():
evidence = build_opportunity_evidence(
{"id": "opp-3", "stage": "PAYMENT_CONFIRMED", "metadata": {"payment_terms": "before_shipping"}},
linked_customer={"id": "c1", "tax_id": "123", "billing_email": "a@b.pt", "address": "Rua", "postal_code": "1000", "city": "Lisboa"},
linked_documents=[
{"id": "q1", "document_kind": "quotation", "document_number": "ORC.ORC2026.177"},
{"id": "i1", "document_kind": "invoice", "document_number": "FA.FA2026.114", "status": "open"},
],
tasks=[{"id": "t1", "status": "pending", "action_code": "SEND_INVOICE", "action": "Enviar fatura"}],
operation_snapshot={
"links": [{"system": "clientflow", "external_type": "payment", "status": "confirmed"}],
"cards": [{"key": "odoo_production", "status": "in_progress"}],
},
fiscal_data_complete=True,
)
decision = decide_opportunity_next_action(evidence, _profile())
assert decision.next_action.code == "SEND_INVOICE"
assert decision.next_action.target_url == "/tasks/t1"
assert "Enviar PDF" in decision.next_action.description
def test_payment_invoice_sent_and_odoo_production_waits_production_not_send_invoice():
evidence = build_opportunity_evidence(
{"id": "opp-3b", "stage": "PAYMENT_CONFIRMED", "metadata": {"payment_terms": "before_shipping"}},
linked_customer={"id": "c1", "tax_id": "123", "billing_email": "a@b.pt", "address": "Rua", "postal_code": "1000", "city": "Lisboa"},
linked_documents=[
{"id": "q1", "document_kind": "quotation", "document_number": "ORC.ORC2026.177"},
{"id": "i1", "document_kind": "invoice", "document_number": "FA.FA2026.114", "sent": True},
],
operation_snapshot={
"links": [{"system": "clientflow", "external_type": "payment", "status": "confirmed"}],
"cards": [{"key": "odoo_physical_status", "status": "created", "payload": {"label": "Em produção/preparação", "ready_to_ship": False, "next_action": "Aguardar conclusão da produção/preparação no Odoo."}}],
},
fiscal_data_complete=True,
)
decision = decide_opportunity_next_action(evidence, _profile())
assert decision.next_action.code == "WAIT_PRODUCTION"
assert decision.next_action.label == "Aguardar produção"
assert decision.financial_state == "payment_confirmed"
assert audit_decisions([decision]) == []
def test_after_delivery_shipped_without_payment_followup_payment():
evidence = build_opportunity_evidence(
{"id": "opp-4", "stage": "SHIPMENT_CREATED", "metadata": {"payment_terms": "after_delivery"}},
linked_customer={"id": "c1", "tax_id": "123", "billing_email": "a@b.pt", "address": "Rua", "postal_code": "1000", "city": "Lisboa"},
linked_documents=[{"id": "q1", "document_kind": "quotation", "document_number": "ORC.ORC2026.200"}],
operation_snapshot={"links": [], "cards": [{"key": "packlink_shipment", "status": "created"}]},
fiscal_data_complete=True,
)
decision = decide_opportunity_next_action(evidence, _profile())
assert decision.next_action.code == "FOLLOW_UP_PAYMENT"
def test_fiscal_conflict_blocks_financial_actions():
evidence = build_opportunity_evidence(
{"id": "opp-5", "stage": "QUOTE_SENT", "metadata": {"has_nif_conflict": True}},
linked_customer={"id": "c1", "tax_id": "123"},
linked_documents=[{"id": "q1", "document_kind": "quotation", "document_number": "ORC.ORC2026.177"}],
)
decision = decide_opportunity_next_action(evidence, _profile())
assert decision.next_action.code == "REVIEW"
blocked = {a.code for a in decision.blocked_actions}
assert {"CONFIRM_PAYMENT", "SEND_INVOICE", "CREATE_JASMIN_QUOTE"} <= blocked
def test_after_delivery_invoice_without_payment_allows_prepare_odoo_before_payment():
evidence = build_opportunity_evidence(
{"id": "opp-after-delivery", "stage": "INVOICE_SENT", "metadata": {"payment_terms": "after_delivery"}},
linked_customer={"id": "c1", "tax_id": "123", "billing_email": "a@b.pt", "address": "Rua", "postal_code": "1000", "city": "Lisboa"},
linked_documents=[
{"id": "q1", "document_kind": "quotation", "document_number": "ORC.ORC2026.194", "status": "completed"},
{"id": "i1", "document_kind": "invoice", "document_number": "FA.FA2026.129", "status": "open"},
],
operation_snapshot={"links": [], "cards": []},
fiscal_data_complete=True,
)
decision = decide_opportunity_next_action(evidence, _profile())
assert decision.next_action.code == "PREPARE_ORDER"
assert "sem pagamento prévio" in decision.reason or "sem exigir pagamento" in decision.next_action.description