import pytest from fastapi import HTTPException from api.v1.modules.crm.opportunities import service from api.v1.modules.crm.opportunities.dto import OpportunityCreate, OpportunityUpdate from api.v1.modules.crm.pipelines import service as pipelines_service from api.v1.modules.crm.pipelines.dto import PipelineCreate, StageCreate T, C = 1, 1 def _pipeline_with_stages(db): pipeline = pipelines_service.create_pipeline(db, PipelineCreate(name="Ventas", is_default=True), T, C) s_open = pipelines_service.create_stage( db, StageCreate(pipeline_id=pipeline.id, name="Prospecto", position=0, probability=10), T, C ) s_won = pipelines_service.create_stage( db, StageCreate(pipeline_id=pipeline.id, name="Ganada", position=1, probability=100, is_won=True), T, C ) s_lost = pipelines_service.create_stage( db, StageCreate(pipeline_id=pipeline.id, name="Perdida", position=2, is_lost=True), T, C ) return pipeline, s_open, s_won, s_lost def test_create_opportunity(db): pipeline, s_open, _, _ = _pipeline_with_stages(db) opp = service.create_opportunity( db, OpportunityCreate(name="Licencia Aduanasoft", pipeline_id=pipeline.id, stage_id=s_open.id, amount=15000), T, C, ) assert opp.status == "open" assert opp.currency == "MXN" def test_move_to_won_closes_and_sets_probability(db): pipeline, s_open, s_won, _ = _pipeline_with_stages(db) opp = service.create_opportunity(db, OpportunityCreate(name="Deal", pipeline_id=pipeline.id, stage_id=s_open.id), T, C) moved = service.move_opportunity(db, opp.id, s_won.id, T, C) assert moved.status == "won" assert moved.probability == 100 assert moved.closed_at is not None assert moved.stage_id == s_won.id def test_move_to_lost(db): pipeline, s_open, _, s_lost = _pipeline_with_stages(db) opp = service.create_opportunity(db, OpportunityCreate(name="Deal", pipeline_id=pipeline.id, stage_id=s_open.id), T, C) moved = service.move_opportunity(db, opp.id, s_lost.id, T, C) assert moved.status == "lost" assert moved.probability == 0 assert moved.closed_at is not None def test_move_back_to_open_reopens(db): pipeline, s_open, s_won, _ = _pipeline_with_stages(db) opp = service.create_opportunity(db, OpportunityCreate(name="Deal", pipeline_id=pipeline.id, stage_id=s_open.id), T, C) service.move_opportunity(db, opp.id, s_won.id, T, C) reopened = service.move_opportunity(db, opp.id, s_open.id, T, C) assert reopened.status == "open" assert reopened.probability == 10 assert reopened.closed_at is None def test_create_rejects_unknown_account(db): with pytest.raises(HTTPException) as exc: service.create_opportunity(db, OpportunityCreate(name="X", account_id=999), T, C) assert exc.value.status_code == 422 def test_only_one_default_pipeline(db): pipelines_service.create_pipeline(db, PipelineCreate(name="P1", is_default=True), T, C) pipelines_service.create_pipeline(db, PipelineCreate(name="P2", is_default=True), T, C) pipelines = pipelines_service.get_pipelines(db, T, C) defaults = [p for p in pipelines if p.is_default] assert len(defaults) == 1 and defaults[0].name == "P2" def test_create_in_won_stage_derives_state(db): pipeline, _s_open, s_won, _ = _pipeline_with_stages(db) opp = service.create_opportunity( db, OpportunityCreate(name="Directo a ganada", pipeline_id=pipeline.id, stage_id=s_won.id, amount=500), T, C, ) assert opp.status == "won" assert opp.probability == 100 assert opp.closed_at is not None def test_update_to_lost_stage_derives_state(db): pipeline, s_open, _s_won, s_lost = _pipeline_with_stages(db) opp = service.create_opportunity(db, OpportunityCreate(name="Z", pipeline_id=pipeline.id, stage_id=s_open.id), T, C) updated = service.update_opportunity(db, opp.id, OpportunityUpdate(stage_id=s_lost.id), T, C) assert updated.status == "lost" assert updated.probability == 0 assert updated.closed_at is not None def test_create_rejects_stage_from_other_pipeline(db): pipeline, _s_open, _s_won, _s_lost = _pipeline_with_stages(db) other = pipelines_service.create_pipeline(db, PipelineCreate(name="Otro"), T, C) other_stage = pipelines_service.create_stage(db, StageCreate(pipeline_id=other.id, name="X"), T, C) with pytest.raises(HTTPException) as exc: service.create_opportunity( db, OpportunityCreate(name="Y", pipeline_id=pipeline.id, stage_id=other_stage.id), T, C ) assert exc.value.status_code == 422