from __future__ import annotations

import unittest
from datetime import UTC, datetime
from decimal import Decimal

from sqlalchemy import BigInteger, create_engine, event, select
from sqlalchemy.ext.compiler import compiles
from sqlalchemy.orm import Session, sessionmaker


@compiles(BigInteger, "sqlite")
def _compile_bigint_for_sqlite(type_, compiler, **kw) -> str:
    _ = type_, compiler, kw
    return "INTEGER"


import app.models.document_archive  # noqa: E402,F401
import app.models.master_data  # noqa: E402,F401
import app.models.operations  # noqa: E402,F401
import app.models.org  # noqa: E402,F401
from app.models.base import Base  # noqa: E402
from app.models.document_archive import DocumentArchive  # noqa: E402
from app.models.master_data import Item, StockBalance, Warehouse  # noqa: E402
from app.models.operations import (  # noqa: E402
    InventoryTxn,
    PurchaseOrder,
    PurchaseOrderItem,
    PurchaseReceipt,
    PurchaseReceiptItem,
    PurchaseReturn,
    PurchaseReturnItem,
    StockLot,
    Supplier,
    WarehouseLocation,
)
from app.models.org import Department, Employee, User  # noqa: E402
from app.services.auth import AuthContext  # noqa: E402
from app.services.document_archives import ARCHIVE_STATUS_READY, DOCUMENT_TYPE_WAREHOUSE_OPERATION  # noqa: E402


class RawMaterialReturnOutboundTest(unittest.TestCase):
    def setUp(self) -> None:
        engine = create_engine("sqlite+pysqlite:///:memory:", future=True)
        event.listen(engine, "connect", self._enable_sqlite_foreign_keys)
        Base.metadata.create_all(engine)
        self.SessionLocal = sessionmaker(bind=engine, autoflush=False, autocommit=False, future=True)
        self.db: Session = self.SessionLocal()
        self._seed()

    def tearDown(self) -> None:
        self.db.close()

    @staticmethod
    def _enable_sqlite_foreign_keys(dbapi_connection, _connection_record) -> None:
        cursor = dbapi_connection.cursor()
        cursor.execute("PRAGMA foreign_keys=ON")
        cursor.close()

    def _seed(self) -> None:
        now = datetime.now(UTC)
        department = Department(
            id=1,
            dept_code="D-WH",
            dept_name="仓库部",
            dept_type="WAREHOUSE",
            status="ACTIVE",
            created_at=now,
            updated_at=now,
        )
        employee = Employee(
            id=1,
            employee_code="EMP-WH-001",
            employee_name="仓管员",
            dept_id=1,
            status="ACTIVE",
            created_at=now,
            updated_at=now,
        )
        user = User(
            id=1,
            username="tester",
            password_hash="x",
            employee_id=1,
            dept_id=1,
            nickname="测试员",
            is_super_admin=1,
            status="ACTIVE",
            created_at=now,
            updated_at=now,
        )
        supplier = Supplier(
            id=1,
            supplier_code="SUP-RAW-001",
            supplier_name="原材料供应商",
            status="ACTIVE",
            created_at=now,
            updated_at=now,
        )
        warehouse = Warehouse(
            id=1,
            warehouse_code="WH-RAW-01",
            warehouse_name="原材料库",
            warehouse_type="RAW",
            status="ACTIVE",
            created_at=now,
            updated_at=now,
        )
        location = WarehouseLocation(
            id=1,
            warehouse_id=1,
            location_code="RM-A-01",
            location_name="原料主库位",
            is_default=1,
            is_locked=0,
            status="ACTIVE",
            created_at=now,
            updated_at=now,
        )
        item = Item(
            id=1,
            item_code="RAW-00001",
            item_name="冷轧钢板",
            item_type="RAW_MATERIAL",
            unit_weight_kg=Decimal("1"),
            safety_stock_weight_kg=Decimal("0"),
            scrap_sale_price=Decimal("0"),
            status="ACTIVE",
            created_at=now,
            updated_at=now,
        )
        purchase_order = PurchaseOrder(
            id=1,
            po_no="PO-2026-00001",
            supplier_id=1,
            order_date=now.date(),
            expected_date=now.date(),
            target_warehouse_type="RAW",
            tax_rate=Decimal("0"),
            total_amount=Decimal("500"),
            status="CONFIRMED",
            created_at=now,
            updated_at=now,
        )
        purchase_order_item = PurchaseOrderItem(
            id=1,
            purchase_order_id=1,
            line_no=1,
            material_item_id=1,
            order_qty=Decimal("100"),
            order_weight_kg=Decimal("100"),
            received_qty=Decimal("100"),
            received_weight_kg=Decimal("100"),
            unit_price=Decimal("5"),
            line_amount=Decimal("500"),
            status="RECEIVED",
            created_at=now,
            updated_at=now,
        )
        purchase_receipt = PurchaseReceipt(
            id=1,
            receipt_no="RCV-2026-00001",
            purchase_order_id=1,
            warehouse_id=1,
            receipt_date=now,
            supplier_delivery_no="DLV-001",
            status="CONFIRMED",
            created_at=now,
            updated_at=now,
        )
        purchase_receipt_item = PurchaseReceiptItem(
            id=1,
            receipt_id=1,
            line_no=1,
            purchase_order_item_id=1,
            material_item_id=1,
            location_id=1,
            lot_no="LOT-RAW-001",
            received_qty=Decimal("100"),
            received_weight_kg=Decimal("100"),
            accepted_qty=Decimal("100"),
            accepted_weight_kg=Decimal("100"),
            unit_cost=Decimal("5"),
            status="PASSED",
            created_at=now,
            updated_at=now,
        )
        stock_lot = StockLot(
            id=1,
            lot_no="LOT-RAW-001",
            lot_role="INVENTORY",
            item_id=1,
            warehouse_id=1,
            location_id=1,
            source_doc_type="PURCHASE_RECEIPT",
            source_doc_id=1,
            source_line_id=1,
            inbound_qty=Decimal("100"),
            inbound_weight_kg=Decimal("100"),
            remaining_qty=Decimal("100"),
            remaining_weight_kg=Decimal("100"),
            locked_qty=Decimal("0"),
            locked_weight_kg=Decimal("0"),
            unit_cost=Decimal("5"),
            quality_status="PASS",
            status="AVAILABLE",
            created_at=now,
            updated_at=now,
        )
        stock_balance = StockBalance(
            id=1,
            item_id=1,
            warehouse_id=1,
            location_id=1,
            qty_on_hand=Decimal("100"),
            weight_on_hand_kg=Decimal("100"),
            qty_available=Decimal("100"),
            weight_available_kg=Decimal("100"),
            qty_allocated=Decimal("0"),
            weight_allocated_kg=Decimal("0"),
            avg_unit_cost=Decimal("5"),
            created_at=now,
            updated_at=now,
        )
        self.db.add_all([department, supplier, warehouse, item])
        self.db.flush()
        self.db.add_all([employee, user, location, purchase_order])
        self.db.flush()
        self.db.add_all([purchase_order_item, purchase_receipt])
        self.db.flush()
        self.db.add_all([purchase_receipt_item, stock_lot, stock_balance])
        self.db.commit()

    def test_purchase_return_tables_exist(self) -> None:
        now = datetime.now(UTC)
        purchase_return = PurchaseReturn(
            id=1,
            return_no="退货2026-00001",
            purchase_order_id=1,
            warehouse_id=1,
            return_date=now,
            operator_user_id=1,
            logistics_waybill_no="YT123",
            logistics_freight_amount=None,
            logistics_photo_url=None,
            reason="性能不合格",
            status="CONFIRMED",
            remark="测试退货",
            created_at=now,
            updated_at=now,
        )
        self.db.add(purchase_return)
        self.db.flush()

        purchase_return_item = PurchaseReturnItem(
            id=1,
            purchase_return_id=purchase_return.id,
            line_no=1,
            purchase_order_item_id=1,
            material_item_id=1,
            lot_id=1,
            return_weight_kg=Decimal("20"),
            unit_cost=Decimal("5"),
            amount=Decimal("100"),
            reason="性能不合格",
            status="CONFIRMED",
            created_at=now,
            updated_at=now,
        )
        self.db.add(purchase_return_item)
        self.db.commit()

        saved = self.db.get(PurchaseReturn, purchase_return.id)
        saved_item = self.db.get(PurchaseReturnItem, purchase_return_item.id)

        self.assertEqual(saved.status, "CONFIRMED")
        self.assertEqual(float(saved_item.return_weight_kg), 20)

    def _context(self) -> AuthContext:
        user = self.db.get(User, 1)
        employee = self.db.get(Employee, 1)
        department = self.db.get(Department, 1)
        return AuthContext(
            user=user,
            employee=employee,
            department=department,
            role_codes=["ADMIN"],
            role_names=["管理员"],
            permission_codes=[],
        )

    def test_raw_material_return_outbound_deducts_lot_balance_and_creates_records(self) -> None:
        from app.api.routes.inventory import create_raw_material_return_outbound
        from app.schemas.operations import RawMaterialReturnOutboundCreate, RawMaterialReturnOutboundItemCreate

        payload = RawMaterialReturnOutboundCreate(
            warehouse_id=1,
            waybill_no="YT123456",
            freight_amount=None,
            reason="性能不合格",
            remark="退回供应商",
            items=[
                RawMaterialReturnOutboundItemCreate(
                    lot_id=1,
                    purchase_order_item_id=1,
                    return_weight_kg=80,
                    reason="硬度不达标",
                )
            ],
        )

        result = create_raw_material_return_outbound(payload, context=self._context(), db=self.db)

        self.assertEqual(result.status, "CONFIRMED")
        self.assertEqual(result.logistics_waybill_no, "YT123456")
        self.assertEqual(float(result.total_return_weight_kg), 80)
        self.assertEqual(len(result.items), 1)
        self.assertEqual(result.archive_status, ARCHIVE_STATUS_READY)
        self.assertEqual(result.archive_document_type, DOCUMENT_TYPE_WAREHOUSE_OPERATION)
        self.assertIsNotNone(result.archive_business_id)

        self.db.expire_all()
        lot = self.db.get(StockLot, 1)
        balance = self.db.get(StockBalance, 1)
        self.assertEqual(float(lot.remaining_weight_kg), 20)
        self.assertEqual(float(balance.weight_on_hand_kg), 20)
        self.assertEqual(float(balance.weight_available_kg), 20)

        txn = self.db.query(InventoryTxn).filter_by(txn_type="PURCHASE_RETURN_OUT").one()
        self.assertEqual(txn.source_doc_type, "PURCHASE_RETURN")
        self.assertEqual(txn.source_doc_id, result.purchase_return_id)
        self.assertEqual(txn.source_line_id, result.items[0].purchase_return_item_id)
        self.assertEqual(float(txn.weight_change_kg), -80)
        self.assertEqual(txn.logistics_waybill_no, "YT123456")
        archive = self.db.scalar(
            select(DocumentArchive).where(
                DocumentArchive.document_type == DOCUMENT_TYPE_WAREHOUSE_OPERATION,
                DocumentArchive.business_id == txn.id,
            )
        )
        self.assertIsNotNone(archive)
        self.assertEqual(archive.status, ARCHIVE_STATUS_READY)

    def test_purchase_order_query_preserves_status_and_marks_has_return(self) -> None:
        from app.api.routes.inventory import create_raw_material_return_outbound
        from app.schemas.operations import RawMaterialReturnOutboundCreate, RawMaterialReturnOutboundItemCreate
        from app.services.operations import get_purchase_order_items_query, get_purchase_order_query

        create_raw_material_return_outbound(
            RawMaterialReturnOutboundCreate(
                warehouse_id=1,
                waybill_no="YT123456",
                reason="性能不合格",
                items=[
                    RawMaterialReturnOutboundItemCreate(
                        lot_id=1,
                        purchase_order_item_id=1,
                        return_weight_kg=80,
                    )
                ],
            ),
            context=self._context(),
            db=self.db,
        )

        order_row = self.db.execute(get_purchase_order_query(limit=1).where(PurchaseOrder.id == 1)).mappings().first()
        item_row = self.db.execute(get_purchase_order_items_query(limit=1, purchase_order_id=1)).mappings().first()

        self.assertEqual(order_row["display_status"], "RECEIVED")
        self.assertTrue(order_row["has_return"])
        self.assertEqual(float(order_row["total_return_weight_kg"]), 80)
        self.assertTrue(item_row["has_return"])
        self.assertEqual(float(item_row["returned_weight_kg"]), 80)
        self.assertEqual(float(item_row["net_received_weight_kg"]), 20)

    def test_raw_material_return_requires_waybill_or_photo(self) -> None:
        from fastapi import HTTPException

        from app.api.routes.inventory import create_raw_material_return_outbound
        from app.schemas.operations import RawMaterialReturnOutboundCreate, RawMaterialReturnOutboundItemCreate

        payload = RawMaterialReturnOutboundCreate(
            warehouse_id=1,
            freight_amount=None,
            reason="性能不合格",
            items=[
                RawMaterialReturnOutboundItemCreate(
                    lot_id=1,
                    purchase_order_item_id=1,
                    return_weight_kg=80,
                )
            ],
        )

        with self.assertRaises(HTTPException) as exc:
            create_raw_material_return_outbound(payload, context=self._context(), db=self.db)

        self.assertEqual(exc.exception.status_code, 400)
        self.assertIn("运单号或上传辅助照片", str(exc.exception.detail))

    def test_raw_material_return_rejects_weight_above_remaining(self) -> None:
        from fastapi import HTTPException

        from app.api.routes.inventory import create_raw_material_return_outbound
        from app.schemas.operations import RawMaterialReturnOutboundCreate, RawMaterialReturnOutboundItemCreate

        payload = RawMaterialReturnOutboundCreate(
            warehouse_id=1,
            order_photo_url="/inventory/logistics-photos/test.jpg",
            freight_amount=None,
            reason="性能不合格",
            items=[
                RawMaterialReturnOutboundItemCreate(
                    lot_id=1,
                    purchase_order_item_id=1,
                    return_weight_kg=101,
                )
            ],
        )

        with self.assertRaises(HTTPException) as exc:
            create_raw_material_return_outbound(payload, context=self._context(), db=self.db)

        self.assertEqual(exc.exception.status_code, 400)
        self.assertIn("退货重量不能超过批次可用重量", str(exc.exception.detail))

    def test_raw_material_return_rejects_weight_above_unlocked_available_weight(self) -> None:
        from fastapi import HTTPException

        from app.api.routes.inventory import create_raw_material_return_outbound
        from app.schemas.operations import RawMaterialReturnOutboundCreate, RawMaterialReturnOutboundItemCreate

        lot = self.db.get(StockLot, 1)
        lot.locked_weight_kg = Decimal("30")
        self.db.commit()

        payload = RawMaterialReturnOutboundCreate(
            warehouse_id=1,
            order_photo_url="/inventory/logistics-photos/test.jpg",
            freight_amount=None,
            reason="性能不合格",
            items=[
                RawMaterialReturnOutboundItemCreate(
                    lot_id=1,
                    purchase_order_item_id=1,
                    return_weight_kg=80,
                )
            ],
        )

        with self.assertRaises(HTTPException) as exc:
            create_raw_material_return_outbound(payload, context=self._context(), db=self.db)

        self.assertEqual(exc.exception.status_code, 400)
        self.assertIn("退货重量不能超过批次可用重量", str(exc.exception.detail))

    def test_raw_material_return_conditional_deduction_rechecks_locked_weight_after_validation(self) -> None:
        from fastapi import HTTPException
        from unittest.mock import patch

        from app.api.routes import inventory as inventory_routes
        from app.schemas.operations import RawMaterialReturnOutboundCreate, RawMaterialReturnOutboundItemCreate

        payload = RawMaterialReturnOutboundCreate(
            warehouse_id=1,
            order_photo_url="/inventory/logistics-photos/test.jpg",
            freight_amount=None,
            reason="性能不合格",
            items=[
                RawMaterialReturnOutboundItemCreate(
                    lot_id=1,
                    purchase_order_item_id=1,
                    return_weight_kg=80,
                )
            ],
        )

        def lock_lot_after_validation(_db: Session) -> str:
            lot = self.db.get(StockLot, 1)
            lot.locked_weight_kg = Decimal("30")
            self.db.flush()
            return "退货2026-00099"

        with patch.object(inventory_routes, "_build_purchase_return_no", side_effect=lock_lot_after_validation):
            with self.assertRaises(HTTPException) as exc:
                inventory_routes.create_raw_material_return_outbound(payload, context=self._context(), db=self.db)

        self.assertEqual(exc.exception.status_code, 400)
        self.assertIn("退货重量不能超过批次可用重量", str(exc.exception.detail))
        self.assertEqual(self.db.query(PurchaseReturn).count(), 0)

    def test_raw_material_return_rechecks_purchase_line_returnable_after_validation(self) -> None:
        from fastapi import HTTPException
        from unittest.mock import patch

        from app.api.routes import inventory as inventory_routes
        from app.schemas.operations import RawMaterialReturnOutboundCreate, RawMaterialReturnOutboundItemCreate

        payload = RawMaterialReturnOutboundCreate(
            warehouse_id=1,
            order_photo_url="/inventory/logistics-photos/test.jpg",
            freight_amount=None,
            reason="性能不合格",
            items=[
                RawMaterialReturnOutboundItemCreate(
                    lot_id=1,
                    purchase_order_item_id=1,
                    return_weight_kg=80,
                )
            ],
        )

        original_lock = inventory_routes._lock_purchase_order_items_for_return

        def insert_prior_return_after_validation(_db: Session, purchase_order_item_ids: set[int]) -> None:
            now = datetime.now(UTC)
            purchase_return = PurchaseReturn(
                id=10,
                return_no="退货2026-00010",
                purchase_order_id=1,
                warehouse_id=1,
                return_date=now,
                operator_user_id=1,
                reason="性能不合格",
                status="CONFIRMED",
                created_at=now,
                updated_at=now,
            )
            purchase_return_item = PurchaseReturnItem(
                id=10,
                purchase_return_id=10,
                line_no=1,
                purchase_order_item_id=1,
                material_item_id=1,
                lot_id=1,
                return_weight_kg=Decimal("30"),
                unit_cost=Decimal("5"),
                amount=Decimal("150"),
                reason="性能不合格",
                status="CONFIRMED",
                created_at=now,
                updated_at=now,
            )
            self.db.add_all([purchase_return, purchase_return_item])
            self.db.commit()
            original_lock(_db, purchase_order_item_ids)

        with patch.object(
            inventory_routes,
            "_lock_purchase_order_items_for_return",
            side_effect=insert_prior_return_after_validation,
        ):
            with self.assertRaises(HTTPException) as exc:
                inventory_routes.create_raw_material_return_outbound(payload, context=self._context(), db=self.db)

        self.assertEqual(exc.exception.status_code, 400)
        self.assertIn("退货重量不能超过该采购明细可退重量", str(exc.exception.detail))

        self.db.expire_all()
        lot = self.db.get(StockLot, 1)
        self.assertEqual(float(lot.remaining_weight_kg), 100)
        self.assertEqual(self.db.query(PurchaseReturn).count(), 1)
        self.assertEqual(self.db.query(PurchaseReturnItem).count(), 1)

    def test_stock_lot_purchase_links_returns_receipt_purchase_order_item(self) -> None:
        from app.services.operations import get_stock_lot_purchase_links

        links = get_stock_lot_purchase_links(self.db, 1)

        self.assertEqual(len(links), 1)
        self.assertEqual(links[0]["purchase_order_id"], 1)
        self.assertEqual(links[0]["purchase_order_item_id"], 1)
        self.assertEqual(links[0]["po_no"], "PO-2026-00001")
        self.assertEqual(float(links[0]["accepted_weight_kg"]), 100)
        self.assertEqual(float(links[0]["returned_weight_kg"]), 0)
        self.assertEqual(float(links[0]["returnable_weight_kg"]), 100)

    def test_stock_lot_purchase_links_excludes_same_lot_no_from_unrelated_receipt(self) -> None:
        from app.services.operations import get_stock_lot_purchase_links

        now = datetime.now(UTC)
        other_purchase_order = PurchaseOrder(
            id=2,
            po_no="PO-2026-00002",
            supplier_id=1,
            order_date=now.date(),
            expected_date=now.date(),
            target_warehouse_type="RAW",
            tax_rate=Decimal("0"),
            total_amount=Decimal("300"),
            status="CONFIRMED",
            created_at=now,
            updated_at=now,
        )
        other_purchase_order_item = PurchaseOrderItem(
            id=2,
            purchase_order_id=2,
            line_no=1,
            material_item_id=1,
            order_qty=Decimal("60"),
            order_weight_kg=Decimal("60"),
            received_qty=Decimal("60"),
            received_weight_kg=Decimal("60"),
            unit_price=Decimal("5"),
            line_amount=Decimal("300"),
            status="RECEIVED",
            created_at=now,
            updated_at=now,
        )
        other_receipt = PurchaseReceipt(
            id=2,
            receipt_no="RCV-2026-00002",
            purchase_order_id=2,
            warehouse_id=1,
            receipt_date=now,
            supplier_delivery_no="DLV-002",
            status="CONFIRMED",
            created_at=now,
            updated_at=now,
        )
        wrong_receipt_item = PurchaseReceiptItem(
            id=2,
            receipt_id=2,
            line_no=1,
            purchase_order_item_id=2,
            material_item_id=1,
            location_id=1,
            lot_no="LOT-RAW-001",
            received_qty=Decimal("60"),
            received_weight_kg=Decimal("60"),
            accepted_qty=Decimal("60"),
            accepted_weight_kg=Decimal("60"),
            unit_cost=Decimal("5"),
            status="PASSED",
            created_at=now,
            updated_at=now,
        )
        self.db.add_all([other_purchase_order, other_purchase_order_item, other_receipt, wrong_receipt_item])
        self.db.commit()

        links = get_stock_lot_purchase_links(self.db, 1)

        self.assertEqual(len(links), 1)
        self.assertEqual(links[0]["purchase_order_item_id"], 1)

    def test_stock_lot_purchase_links_caps_returnable_by_prior_returns_and_locked_weight(self) -> None:
        from app.services.operations import get_stock_lot_purchase_links

        now = datetime.now(UTC)
        purchase_return = PurchaseReturn(
            id=1,
            return_no="退货2026-00001",
            purchase_order_id=1,
            warehouse_id=1,
            return_date=now,
            operator_user_id=1,
            reason="性能不合格",
            status="CONFIRMED",
            created_at=now,
            updated_at=now,
        )
        purchase_return_item = PurchaseReturnItem(
            id=1,
            purchase_return_id=1,
            line_no=1,
            purchase_order_item_id=1,
            material_item_id=1,
            lot_id=1,
            return_weight_kg=Decimal("30"),
            unit_cost=Decimal("5"),
            amount=Decimal("150"),
            reason="性能不合格",
            status="CONFIRMED",
            created_at=now,
            updated_at=now,
        )
        stock_lot = self.db.get(StockLot, 1)
        stock_lot.remaining_weight_kg = Decimal("100")
        stock_lot.locked_weight_kg = Decimal("40")
        self.db.add_all([purchase_return, purchase_return_item])
        self.db.commit()

        links = get_stock_lot_purchase_links(self.db, 1)

        self.assertEqual(len(links), 1)
        self.assertEqual(float(links[0]["accepted_weight_kg"]), 100)
        self.assertEqual(float(links[0]["returned_weight_kg"]), 30)
        self.assertEqual(float(links[0]["returnable_weight_kg"]), 60)

        stock_lot.locked_weight_kg = Decimal("0")
        self.db.commit()

        links = get_stock_lot_purchase_links(self.db, 1)

        self.assertEqual(float(links[0]["returnable_weight_kg"]), 70)

        stock_lot.locked_weight_kg = Decimal("120")
        self.db.commit()

        links = get_stock_lot_purchase_links(self.db, 1)

        self.assertEqual(float(links[0]["returnable_weight_kg"]), 0)

    def test_stock_lot_purchase_links_ignore_canceled_return_items(self) -> None:
        from app.services.operations import get_stock_lot_purchase_links

        now = datetime.now(UTC)
        purchase_return = PurchaseReturn(
            id=1,
            return_no="退货2026-00001",
            purchase_order_id=1,
            warehouse_id=1,
            return_date=now,
            operator_user_id=1,
            reason="性能不合格",
            status="CONFIRMED",
            created_at=now,
            updated_at=now,
        )
        canceled_return_item = PurchaseReturnItem(
            id=1,
            purchase_return_id=1,
            line_no=1,
            purchase_order_item_id=1,
            material_item_id=1,
            lot_id=1,
            return_weight_kg=Decimal("30"),
            unit_cost=Decimal("5"),
            amount=Decimal("150"),
            reason="性能不合格",
            status="CANCELED",
            created_at=now,
            updated_at=now,
        )
        self.db.add_all([purchase_return, canceled_return_item])
        self.db.commit()

        links = get_stock_lot_purchase_links(self.db, 1)

        self.assertEqual(float(links[0]["returned_weight_kg"]), 0)
        self.assertEqual(float(links[0]["returnable_weight_kg"]), 100)

    def test_logistics_can_require_waybill_or_photo_without_freight(self) -> None:
        from app.services.logistics import normalize_logistics_fields

        waybill, freight, photo_url = normalize_logistics_fields(
            None,
            None,
            required=True,
            freight_required=False,
            order_photo_url="/inventory/logistics-photos/test.jpg",
        )

        self.assertIsNone(waybill)
        self.assertIsNone(freight)
        self.assertEqual(photo_url, "/inventory/logistics-photos/test.jpg")


if __name__ == "__main__":
    unittest.main()
