fix(opportunity-flow): wait after sent quote until payment evidence
This commit is contained in:
@@ -333,18 +333,34 @@ def decide_blif_next_action(e: OpportunityEvidence, profile: CompanyWorkflowProf
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Default/BLIF normal sequence: budget document, payment, invoice, then preparation/shipping.
|
# Default/BLIF normal sequence: budget document, payment, invoice, then preparation/shipping.
|
||||||
if e.payment_terms in {PAYMENT_BEFORE_SHIPPING, "", "undefined", "agreement"} and e.has_quote and not e.payment_confirmed:
|
if (
|
||||||
|
e.payment_terms in {PAYMENT_BEFORE_SHIPPING, "", "undefined", "agreement"}
|
||||||
|
and e.has_quote
|
||||||
|
and e.quote_sent
|
||||||
|
and not e.has_invoice
|
||||||
|
and not e.payment_confirmed
|
||||||
|
):
|
||||||
next_action = _action(
|
next_action = _action(
|
||||||
profile,
|
profile,
|
||||||
ACTION_CONFIRM_PAYMENT,
|
ACTION_NO_ACTION,
|
||||||
f"Orçamento {e.quote_number or ''} associado. Confirmar pagamento antes de emitir fatura.",
|
f"Orçamento {e.quote_number or ''} enviado. Aguardar decisão do cliente ou evidência de pagamento.",
|
||||||
priority="alta",
|
force_label="Aguardar cliente / pagamento",
|
||||||
target_url=f"/opportunities/{e.opportunity_id}#operacao" if e.opportunity_id else None,
|
target_url=f"/opportunities/{e.opportunity_id}#operacao" if e.opportunity_id else None,
|
||||||
document_id=e.quote_id,
|
document_id=e.quote_id,
|
||||||
document_number=e.quote_number,
|
document_number=e.quote_number,
|
||||||
)
|
)
|
||||||
available_actions.append(next_action)
|
return OpportunityDecision(
|
||||||
return OpportunityDecision(next_action, "Fluxo normal BLIF exige pagamento confirmado depois do orçamento e antes da fatura.", available_actions=available_actions, warnings=warnings, commercial_stage=COMMERCIAL_STAGE_WAITING_PAYMENT, financial_state=_financial_state(e), physical_state=_physical_state(e), profile_name=profile.name, decision_version=profile.version)
|
next_action,
|
||||||
|
"O orçamento foi enviado e ainda não existe evidência de pagamento que exija validação. "
|
||||||
|
"Aguardar o cliente; o follow-up comercial assume quando ficar devido.",
|
||||||
|
warnings=warnings,
|
||||||
|
commercial_stage=COMMERCIAL_STAGE_WAITING_PAYMENT,
|
||||||
|
financial_state=_financial_state(e),
|
||||||
|
physical_state=_physical_state(e),
|
||||||
|
profile_name=profile.name,
|
||||||
|
decision_version=profile.version,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if e.payment_confirmed and not e.has_invoice and (
|
if e.payment_confirmed and not e.has_invoice and (
|
||||||
not e.has_fiscal_customer or not e.fiscal_data_complete
|
not e.has_fiscal_customer or not e.fiscal_data_complete
|
||||||
|
|||||||
@@ -17,18 +17,39 @@ def test_blif_profile_loads_defaults_and_labels():
|
|||||||
assert profile.documents["invoice"]["label"] == "Fatura"
|
assert profile.documents["invoice"]["label"] == "Fatura"
|
||||||
|
|
||||||
|
|
||||||
def test_quote_before_shipping_requires_confirm_payment_before_invoice():
|
def test_sent_quote_waits_for_customer_or_payment_evidence():
|
||||||
evidence = build_opportunity_evidence(
|
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"},
|
"id": "opp-1",
|
||||||
linked_documents=[{"id": "q1", "document_kind": "quotation", "document_number": "ORC.ORC2026.177", "total_amount": 202.95}],
|
"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": []},
|
operation_snapshot={"links": [], "cards": []},
|
||||||
fiscal_data_complete=True,
|
fiscal_data_complete=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
decision = decide_opportunity_next_action(evidence, _profile())
|
decision = decide_opportunity_next_action(evidence, _profile())
|
||||||
assert decision.next_action.code == "CONFIRM_PAYMENT"
|
|
||||||
|
assert decision.next_action.code == "NO_ACTION"
|
||||||
|
assert decision.commercial_stage == "WAITING_PAYMENT"
|
||||||
assert decision.next_action.document_number == "ORC.ORC2026.177"
|
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
|
assert "Aguardar" in decision.next_action.description
|
||||||
|
|
||||||
|
|
||||||
def test_payment_confirmed_without_invoice_sends_invoice():
|
def test_payment_confirmed_without_invoice_sends_invoice():
|
||||||
|
|||||||
Reference in New Issue
Block a user