import csv import io from datetime import datetime import pytest from sqlalchemy import select from test_refund_amounts import setup from app import main from app.models import (AdminUser, AnswerScreenshot, RefundRecord, RefundRecipient, RefundPaymentAccount, AskTask, AskQuestion, AskAssignment, AskAssignmentQuestion, AskAssignmentBatch, AskAssignmentScreenshot, AskAssignmentBatchScreenshot) def test_answer_confirmation_snapshot_survives_changes_and_deletion(setup): db, client, owner, _, task = setup task.shop_name = "测试店铺" db.commit() url = f"/api/admin/task-board/{task.id}/confirm-refund" payer_id = db.scalar(select(RefundPaymentAccount.id)) response = client.post(url, data={"refund_amount": "2.35", "payer_account_id": payer_id, "payer_account": "伪造账户", "alipay_trade_no": "ALI123"}) assert response.status_code == 200, response.text assert client.post(url, data={"payer_account_id": payer_id}).status_code == 409 rows = client.get("/api/admin/refund-records").json()["data"] assert rows["total"] == 1 and rows["total_amount_fen"] == 235 row = rows["items"][0] assert (row["payee_name"], row["payee_account"], row["operator_name"], row["payer_account"], row["alipay_trade_no"]) == ("张三", "buyer@example.com", owner.display_name, "公司账户", "") recipient = db.scalar(select(RefundRecipient).where(RefundRecipient.target_kind == "answer_submission")) recipient.payee_name = "修改后姓名" db.delete(task) db.commit() assert client.get("/api/admin/refund-records").json()["data"]["items"][0] == row def test_refund_snapshot_is_idempotent_when_callback_wins_race(setup): db, _, owner, _, task = setup payer = db.scalar(select(RefundPaymentAccount)) first = main._snapshot_refund( db, task, "answer_submission", owner, payer.name, "ALI-RACE-001" ) db.commit() second = main._snapshot_refund( db, task, "answer_submission", owner, payer.name, "ALI-RACE-001" ) db.commit() assert second.id == first.id assert db.scalars(select(RefundRecord)).all() == [first] @pytest.mark.parametrize("batched", [True, False]) def test_ask_confirmation_creates_record(setup, batched): db, client, owner, person, _ = setup task = AskTask( task_no="TASK-refund", product_link="https://example.com", product_name="商品", shop_name="提问店铺", ecommerce_platform="京东", product_id="687154817423861300", owner_id=owner.id, status="已发布", ) db.add(task) db.flush() assignment = AskAssignment(task_id=task.id, user_id=person.id, status="已通过") question = AskQuestion(task_id=task.id, sequence_no=1, question_text="问题") db.add_all([assignment, question]) db.flush() db.add(AskAssignmentQuestion(assignment_id=assignment.id, question_id=question.id, selected=True)) if batched: record = AskAssignmentBatch(assignment_id=assignment.id, status="approved", selected_question_ids=str(question.id)) db.add(record) db.flush() kind, route = "ask_batch", "ask-batches" else: record = assignment kind, route = "ask_assignment", "ask-assignments" db.add(RefundRecipient(target_kind=kind, target_id=record.id, account="ask@example.com", payee_name="提问人")) db.commit() detail = client.get(f"/api/admin/task-board/{route}/{record.id}") assert detail.status_code == 200, detail.text assert detail.json()["data"]["ecommerce_platform"] == "京东" assert detail.json()["data"]["product_id"] == "687154817423861300" url = f"/api/admin/task-board/{route}/{record.id}/confirm-refund" payer_id = db.scalar(select(RefundPaymentAccount.id)) owner = db.get(AdminUser, task.owner_id) owner.refund_payment_account_id = None db.commit() assert client.post(url, data={"payer_account_id": payer_id}).status_code == 403 owner.refund_payment_account_id = payer_id db.commit() response = client.post(url, data={"payer_account_id": payer_id}) assert response.status_code == 200, response.text assert client.post(url, data={"payer_account_id": payer_id}).status_code == 409 row = client.get("/api/admin/refund-records").json()["data"]["items"][0] assert row["amount_fen"] == 50 assert row["payee_name"] == "提问人" and row["order_no"] == "TASK-refund" assert row["ecommerce_platform"] == "京东" assert row["product_id"] == "687154817423861300" def test_available_accounts_and_disabled_account_rejection(setup): db, client, _, _, task = setup disabled = RefundPaymentAccount(name="停用账户", alipay_account="disabled@example.com", single_limit_fen=50000, is_active=False) db.add(disabled) db.commit() accounts = client.get("/api/admin/refund-payment-accounts/available") assert accounts.status_code == 200 assert [item["name"] for item in accounts.json()["data"]] == ["公司账户"] assert client.get("/api/admin/refund-payment-accounts").status_code == 403 url = f"/api/admin/task-board/{task.id}/confirm-refund" for account_id in (disabled.id, 99999): response = client.post(url, data={"payer_account_id": account_id}) assert response.status_code == 400, response.text assert task.status == "已通过" assert db.scalar(select(RefundRecord.id)) is None def test_scope_date_range_pagination_and_export(setup): db, client, owner, _, _ = setup owner.dingtalk_dept_id = 10 colleague = AdminUser(username="colleague", display_name="同事", password_hash="x", dingtalk_dept_id=11, dingtalk_dept_path=",10,11,") stranger = AdminUser(username="stranger", display_name="外部门", password_hash="x", dingtalk_dept_id=20) db.add_all([colleague, stranger]) db.flush() for i in range(14): db.add(RefundRecord(target_kind="answer_submission", target_id=100 + i, owner_id=owner.id if i < 12 else colleague.id if i == 12 else stranger.id, order_no=f"TASK-{i}", amount_fen=50, operator_name="运营", shop_name="=unsafe", transferred_at=datetime(2026, 10, 21, 16, 0) if i < 13 else datetime(2026, 10, 22, 16, 0))) db.commit() query = "start_date=2026-10-22&end_date=2026-10-22" page = client.get(f"/api/admin/refund-records?{query}&page=2").json()["data"] assert len(page["items"]) == 2 and page["total"] == 12 and page["total_amount_fen"] == 600 exported = client.get(f"/api/admin/refund-records/export?{query}") rows = list(csv.reader(io.StringIO(exported.content.decode("utf-8-sig")))) assert len(rows) == 14 and rows[-1][7] == "6.00" assert rows[1][0] == "'=unsafe" and rows[1][3] == "2026-10-22 00:00:00" owner.role_code = "dept_manager" db.commit() assert client.get(f"/api/admin/refund-records?{query}").json()["data"]["total"] == 13 owner.role_code = "super_admin" db.commit() assert client.get("/api/admin/refund-records").json()["data"]["total"] == 14 assert client.get(f"/api/admin/refund-records?{query}&q=TASK-13").json()["data"]["total"] == 0 assert client.get("/api/admin/refund-records?q=TASK-13").json()["data"]["total"] == 1 for path in ["/api/admin/refund-records", "/api/admin/refund-records/export"]: assert client.get(path + "?start_date=wrong").status_code == 400 assert client.get(path + "?start_date=2026-10-23&end_date=2026-10-22").status_code == 400