- Introduced a new job in the CI workflow to run backend tests before the build process. - Configured Python environment and installed dependencies from the backend requirements. - Added a check for the TEST_DATABASE_URL secret to ensure it is defined before running tests. - The test job must pass for the build job to execute, enhancing the reliability of the CI pipeline.
114 lines
4.7 KiB
Python
114 lines
4.7 KiB
Python
from decimal import Decimal
|
|
|
|
from api.v1.modules.a24.balance_movements.models import BalanceMovement, MovementType
|
|
from api.v1.modules.a24.discharges.models import DischargeDetail, DischargeHeader
|
|
from api.v1.modules.a76.invoices.imports.process import main_process as import_main
|
|
from api.v1.modules.a76.invoices.exports.process import main_process as export_main
|
|
from api.v1.modules.a76.invoices.exports.process import task as export_task
|
|
from tests.fixtures.builders import (
|
|
create_business_catalogs,
|
|
create_export_invoice_with_line,
|
|
create_import_invoice_with_line,
|
|
ensure_reference_data,
|
|
ensure_tenant_company,
|
|
)
|
|
|
|
|
|
def _patch_import_pipeline(monkeypatch, import_line):
|
|
monkeypatch.setattr(import_main, "pre_validators", lambda *args, **kwargs: [import_line])
|
|
monkeypatch.setattr(import_main, "review_classes", lambda *args, **kwargs: None)
|
|
monkeypatch.setattr(import_main, "review_exchange_rate", lambda *args, **kwargs: None)
|
|
monkeypatch.setattr(import_main, "review_weights_kgs", lambda *args, **kwargs: None)
|
|
monkeypatch.setattr(import_main, "review_weights_lbs", lambda *args, **kwargs: None)
|
|
monkeypatch.setattr(import_main, "_validate_sisimp_limits", lambda *args, **kwargs: None)
|
|
monkeypatch.setattr(import_main, "_validate_lines", lambda *args, **kwargs: ([], {}))
|
|
|
|
|
|
def _patch_export_pipeline(monkeypatch, export_line):
|
|
monkeypatch.setattr(export_main, "pre_validators", lambda *args, **kwargs: [export_line])
|
|
monkeypatch.setattr(export_main, "review_class", lambda *args, **kwargs: None)
|
|
monkeypatch.setattr(export_main, "review_exchange_rate", lambda *args, **kwargs: None)
|
|
monkeypatch.setattr(export_main, "assign_values", lambda *args, **kwargs: None)
|
|
monkeypatch.setattr(export_main, "review_qty_vs_weight", lambda *args, **kwargs: None)
|
|
monkeypatch.setattr(export_main, "review_unit_cost", lambda *args, **kwargs: None)
|
|
monkeypatch.setattr(export_main, "review_qty_series", lambda *args, **kwargs: None)
|
|
|
|
|
|
def test_e2e_inventory_flow_import_then_export(client, db_session, monkeypatch):
|
|
"""
|
|
E2E principal:
|
|
- Alta catálogos/base
|
|
- Procesa factura IMP -> genera entry
|
|
- Procesa factura EXP -> genera consumo/descarga
|
|
- Valida decremento de saldo sin negativos
|
|
"""
|
|
ensure_reference_data(db_session)
|
|
ensure_tenant_company(db_session, tenant_id=1, company_id=1)
|
|
catalogs = create_business_catalogs(db_session, tenant_id=1, company_id=1)
|
|
|
|
import_invoice, import_line = create_import_invoice_with_line(
|
|
db_session, 1, 1, catalogs, invoice_type="TEM", invoice_number="IMP-E2E-01", qty=Decimal("10")
|
|
)
|
|
_patch_import_pipeline(monkeypatch, import_line)
|
|
|
|
response = client.post(f"/api/v1/a76/invoices/{import_invoice.id}/process?company_id=1")
|
|
assert response.status_code == 200
|
|
|
|
entry_movements = (
|
|
db_session.query(BalanceMovement)
|
|
.filter(
|
|
BalanceMovement.import_invoice_id == import_invoice.id,
|
|
BalanceMovement.import_item_line_id == import_line.id,
|
|
BalanceMovement.movement_type == MovementType.ENTRY,
|
|
)
|
|
.all()
|
|
)
|
|
assert entry_movements
|
|
assert sum(Decimal(str(m.quantity or 0)) for m in entry_movements) > 0
|
|
|
|
export_invoice, export_line = create_export_invoice_with_line(
|
|
db_session, 1, 1, catalogs, import_invoice, import_line, qty=Decimal("4")
|
|
)
|
|
_patch_export_pipeline(monkeypatch, export_line)
|
|
|
|
response = client.post(f"/api/v1/a76/invoices/{export_invoice.id}/process?company_id=1")
|
|
assert response.status_code == 200
|
|
|
|
consumptions = (
|
|
db_session.query(BalanceMovement)
|
|
.filter(
|
|
BalanceMovement.source_invoice_id == export_invoice.id,
|
|
BalanceMovement.movement_type == MovementType.CONSUMPTION,
|
|
)
|
|
.all()
|
|
)
|
|
assert consumptions, "El proceso de exportación debe generar consumos"
|
|
|
|
discharge_headers = (
|
|
db_session.query(DischargeHeader)
|
|
.filter(DischargeHeader.source_invoice_id == export_invoice.id)
|
|
.all()
|
|
)
|
|
assert discharge_headers
|
|
|
|
discharge_details = (
|
|
db_session.query(DischargeDetail)
|
|
.filter(DischargeDetail.export_item_line_id == export_line.id)
|
|
.all()
|
|
)
|
|
assert discharge_details
|
|
assert all(detail.movement_id is not None for detail in discharge_details)
|
|
|
|
net_balance = db_session.query(BalanceMovement).filter(
|
|
BalanceMovement.import_item_line_id == import_line.id
|
|
).all()
|
|
signed = Decimal("0")
|
|
for mov in net_balance:
|
|
qty = Decimal(str(mov.quantity or 0))
|
|
if mov.movement_type in {MovementType.CONSUMPTION, MovementType.WASTE, MovementType.SCRAP, MovementType.DESTRUCTION}:
|
|
signed -= qty
|
|
else:
|
|
signed += qty
|
|
assert signed >= 0
|
|
assert signed < Decimal("10")
|