diff --git a/.phpunit.result.cache b/.phpunit.result.cache index c919e33..ffbc4e3 100644 --- a/.phpunit.result.cache +++ b/.phpunit.result.cache @@ -1 +1 @@ -{"version":2,"defects":{"Tests\\Feature\\ProductPriceTest::test_product_list_returns_price_for_store_level":8,"Tests\\Feature\\ProductPriceTest::test_store_without_level_is_rejected":8,"Tests\\Feature\\ProductPriceTest::test_batch_price_updates_and_notifies_affected_stores":7,"Tests\\Feature\\ProductPriceTest::test_price_matrix_structure":8,"Tests\\Feature\\StoreOrderTest::test_place_order_snapshots_level_price_and_recalculates":8,"Tests\\Feature\\StoreOrderTest::test_client_supplied_amount_is_ignored":8,"Tests\\Feature\\StoreOrderTest::test_product_without_level_price_rejected":8,"Tests\\Feature\\StoreOrderTest::test_cancel_only_pending_orders":8,"Tests\\Feature\\StoreOrderTest::test_summarized_order_cannot_be_cancelled":8,"Tests\\Feature\\StoreOrderTest::test_order_data_isolated_between_stores":8,"Tests\\Feature\\PurchaseGenerateTest::test_generate_aggregates_orders_by_product":7,"Tests\\Feature\\PurchaseGenerateTest::test_generate_writes_back_order_status":7,"Tests\\Feature\\PurchaseGenerateTest::test_generate_without_pending_orders_fails":8,"Tests\\Feature\\PurchaseGenerateTest::test_generate_is_idempotent":7,"Tests\\Feature\\AllocationTest::test_allocation_conserves_amount_with_tail_correction":7,"Tests\\Feature\\AllocationTest::test_allocation_follows_order_ratio":7,"Tests\\Feature\\AllocationTest::test_allocation_is_idempotent":7,"Tests\\Feature\\AllocationTest::test_allocation_rejected_without_actual_amount":8,"Tests\\Feature\\MiniAuthTest::test_login_creates_user_and_issues_token":7,"Tests\\Feature\\MiniAuthTest::test_phone_binding_matches_store":8,"Tests\\Feature\\MiniAuthTest::test_phone_no_match_stays_pending":8,"Tests\\Feature\\ExportTest::test_export_xlsx_with_encoded_chinese_filename":7,"Tests\\Feature\\CartTest::test_list_with_prices_amounts_and_totals":7,"Tests\\Feature\\CartTest::test_list_marks_off_shelf_item_unbuyable":7,"Tests\\Feature\\DebugCartListTest::test_dump_cart_list_response":5,"Tests\\Feature\\ProductCostPriceTest::test_create_product_with_cost_and_percent_pricing":7,"Tests\\Feature\\ProductCostPriceTest::test_create_percent_row_without_percent_fails":7,"Tests\\Feature\\ProductCostPriceTest::test_batch_price_cost_only_notifies_percent_level_stores":7,"Tests\\Feature\\ProductCostPriceTest::test_batch_price_empty_row_rejected":7,"Tests\\Feature\\MiniAuthTest::test_disabled_user_cannot_login":8,"Tests\\Feature\\MiniAuthTest::test_mini_token_cannot_access_admin_api":8,"Tests\\Feature\\ProductCostPriceTest::test_batch_price_percent_row_update":7,"Tests\\Feature\\ProductCostPriceTest::test_batch_price_mixed_row_updates_cost_and_level":7,"Tests\\Feature\\ReconciliationTest::test_build_creates_reconciliation_items":8,"Tests\\Feature\\ReconciliationTest::test_build_filters_by_supplier":8,"Tests\\Feature\\ReconciliationTest::test_update_item_recalculates_diff_and_header":8,"Tests\\Feature\\ReconciliationTest::test_toggle_reconciled_flag":8,"Tests\\Feature\\ReconciliationTest::test_settle_creates_settlements_per_store":8,"Tests\\Feature\\ProductCostPriceTest::test_mini_list_returns_actual_price_without_cost_price":8,"Tests\\Feature\\ProductCostPriceTest::test_mini_order_uses_actual_price_for_percent_row":8,"Tests\\Feature\\ProductCostPriceTest::test_mini_cart_uses_actual_price_for_percent_row":8,"Tests\\Feature\\StoreOrderTest::test_container_update_recalculates_amounts":8,"Tests\\Feature\\StoreOrderTest::test_container_update_allowed_in_editable_statuses":8,"Tests\\Feature\\StoreOrderTest::test_container_update_rejected_in_other_statuses":8,"Tests\\Feature\\StoreOrderTest::test_container_update_heals_legacy_product_amount":8,"Tests\\Feature\\StoreOrderTest::test_container_update_validates_negative_numbers":8,"Tests\\Feature\\StoreOrderItemTest::test_sync_item_rejected_when_product_deleted":7,"Tests\\Feature\\StoreOrderItemTest::test_order_list_search_by_product_name":8,"Tests\\Feature\\PurchaseEditTest::test_detail_returns_store_matrix":7,"Tests\\Feature\\PurchaseEditTest::test_update_cell_syncs_order_item_and_totals":7,"Tests\\Feature\\PurchaseEditTest::test_update_row_cost_syncs_all_order_items":7,"Tests\\Feature\\PurchaseEditTest::test_update_row_weight_distributed_by_quantity":7,"Tests\\Feature\\PurchaseEditTest::test_update_row_syncs_product_attributes":7,"Tests\\Feature\\PurchaseGenerateTest::test_generate_aggregates_order_items_into_header":7,"Tests\\Feature\\ExportTest::test_export_category_filters_to_vegetables":7,"Tests\\Feature\\ExportTest::test_export_pdf":7,"Tests\\Feature\\PurchaseEditTest::test_update_row_does_not_touch_manual_weight":7,"Tests\\Feature\\PurchaseEditTest::test_update_row_syncs_to_product_catalog":7,"Tests\\Feature\\PurchaseEditTest::test_update_cell_weight_cascades_totals":7,"Tests\\Feature\\PurchaseEditTest::test_cell_sync_refreshes_snapshot_and_cascades":7,"Tests\\Feature\\PurchaseEditTest::test_edits_rejected_when_purchase_completed":7,"Tests\\Feature\\StoreOrderItemTest::test_update_item_recalculates_order_totals":7,"Tests\\Feature\\StoreOrderItemTest::test_update_item_validates_negative_price":7,"Tests\\Feature\\StoreOrderItemTest::test_sync_item_updates_snapshot_from_product":7,"Tests\\Feature\\StoreOrderItemTest::test_sync_item_recalculates_percent_price_with_latest_cost":7,"Tests\\Feature\\PurchaseEditTest::test_cell_detail_returns_items_with_order_info":8,"Tests\\Feature\\ProductCategoryTest::test_create_product_with_leaf_category_ok":7,"Tests\\Feature\\BillExportTest::test_export_filters_by_root_category":8,"Tests\\Feature\\BillExportTest::test_export_requires_export_permission":8,"Tests\\Feature\\DebugPermissionTest::test_debug_permission":8,"Tests\\Feature\\BillPaymentTest::test_order_status_progresses_via_business_chain":7,"Tests\\Feature\\BillPaymentTest::test_payment_audit_reject_does_not_accumulate":7,"Tests\\Feature\\BillPaymentTest::test_payment_detail_returns_voucher_urls":8,"Tests\\Feature\\ContainerReturnTest::test_press_containers_adds_amount_and_writes_record":7,"Tests\\Feature\\ContainerReturnTest::test_return_containers_deducts_amount":7,"Tests\\Feature\\ContainerReturnTest::test_mixed_sign_containers":7,"Tests\\Feature\\ContainerReturnTest::test_zero_containers_writes_no_record":7,"Tests\\Feature\\StoreOrderTest::test_order_list_outputs_status_name_and_item_preview":7,"Tests\\Feature\\DashboardTest::test_requires_dashboard_analysis_ability":7,"Tests\\Feature\\DashboardTest::test_recon_summary_and_receivable":8,"Tests\\Feature\\DashboardDebugTest::test_debug":8,"Tests\\Feature\\StoreOrderItemTest::test_place_order_snapshots_full_product_info":8,"Tests\\Feature\\StoreOrderItemTest::test_mini_detail_hides_cost_price":8,"Tests\\Feature\\StoreOrderItemTest::test_admin_detail_shows_cost_price_and_supplier":8,"Tests\\Feature\\StoreOrderItemTest::test_update_item_rejected_when_completed_or_cancelled":8,"Tests\\Feature\\StoreOrderItemTest::test_add_store_item_uses_level_percent_price":8,"Tests\\Feature\\ExportTest::test_export_suppliers_multi_sheet":7,"Tests\\Feature\\PurchaseEditTest::test_update_row_skips_catalog_sync_when_product_deleted":7,"Tests\\Feature\\ProductImportExportTest::test_export_filters_by_category":8,"Tests\\Feature\\ProductImportExportTest::test_import_creates_products_and_auto_supplier":7,"Tests\\Feature\\ProductImportExportTest::test_import_matches_existing_supplier":7,"Tests\\Feature\\ProductImportExportTest::test_import_errors_abort_all_rows":7,"Tests\\Feature\\ProductImportExportTest::test_import_category_path_resolution":7,"Tests\\Feature\\MiniReportTest::test_report_accumulates_same_product_across_orders":7,"Tests\\Feature\\ExportTest::test_export_suppliers_split_by_market":7,"Tests\\Feature\\MiniOnlinePaymentTest::test_query_syncs_gateway_status":7,"Tests\\Feature\\MiniSpecialTest::test_cart_quantity_attached_for_store":8,"Tests\\Feature\\MiniWechatPaymentTest::test_create_wechat_payment_success":7,"Tests\\Feature\\ExportTest::test_export_lists_all_catalog_products_with_totals_row":7,"Tests\\Feature\\MiniOnlinePaymentTest::test_create_online_payment_success":7,"Tests\\Feature\\MiniMpPaymentTest::test_mp_scene_wechat_channel":8,"Tests\\Feature\\MiniMpPaymentTest::test_default_scene_remains_mini":7,"Tests\\Feature\\MiniProductTest::test_product_list_ordered_by_category_tree":7},"times":{"Tests\\Feature\\ExampleTest::test_the_application_returns_a_successful_response":0.002,"Tests\\Feature\\ProductPriceTest::test_product_list_returns_price_for_store_level":0.088,"Tests\\Feature\\ProductPriceTest::test_store_without_level_is_rejected":0.007,"Tests\\Feature\\ProductPriceTest::test_batch_price_updates_and_notifies_affected_stores":0.01,"Tests\\Feature\\ProductPriceTest::test_price_matrix_structure":0.009,"Tests\\Feature\\StoreOrderTest::test_place_order_snapshots_level_price_and_recalculates":0.048,"Tests\\Feature\\StoreOrderTest::test_client_supplied_amount_is_ignored":0.048,"Tests\\Feature\\StoreOrderTest::test_product_without_level_price_rejected":0.007,"Tests\\Feature\\StoreOrderTest::test_cancel_only_pending_orders":0.051,"Tests\\Feature\\StoreOrderTest::test_summarized_order_cannot_be_cancelled":0.049,"Tests\\Feature\\StoreOrderTest::test_order_data_isolated_between_stores":0.092,"Tests\\Feature\\PurchaseGenerateTest::test_generate_aggregates_orders_by_product":0.028,"Tests\\Feature\\PurchaseGenerateTest::test_generate_writes_back_order_status":0.101,"Tests\\Feature\\PurchaseGenerateTest::test_generate_without_pending_orders_fails":0.006,"Tests\\Feature\\PurchaseGenerateTest::test_generate_is_idempotent":0.104,"Tests\\Feature\\AllocationTest::test_allocation_conserves_amount_with_tail_correction":0.076,"Tests\\Feature\\AllocationTest::test_allocation_follows_order_ratio":0.022,"Tests\\Feature\\AllocationTest::test_allocation_is_idempotent":0.028,"Tests\\Feature\\AllocationTest::test_allocation_rejected_without_actual_amount":0.021,"Tests\\Feature\\DebugGenerateTest::test_debug_generate":0.047,"Tests\\Feature\\ReconciliationTest::test_build_creates_reconciliation_items":0.019,"Tests\\Feature\\ReconciliationTest::test_build_filters_by_supplier":0.022,"Tests\\Feature\\ReconciliationTest::test_update_item_recalculates_diff_and_header":0.019,"Tests\\Feature\\ReconciliationTest::test_toggle_reconciled_flag":0.02,"Tests\\Feature\\ReconciliationTest::test_settle_creates_settlements_per_store":0.036,"Tests\\Feature\\StatementTest::test_generate_snapshots_payment_cycle":0.011,"Tests\\Feature\\StatementTest::test_statement_isolated_between_stores":0.013,"Tests\\Feature\\StatementTest::test_generate_excludes_cancelled_and_used_items":0.016,"Tests\\Feature\\MiniAuthTest::test_login_creates_user_and_issues_token":0.056,"Tests\\Feature\\MiniAuthTest::test_disabled_user_cannot_login":0.003,"Tests\\Feature\\MiniAuthTest::test_phone_binding_matches_store":0.005,"Tests\\Feature\\MiniAuthTest::test_phone_no_match_stays_pending":0.001,"Tests\\Feature\\MiniAuthTest::test_sys_token_cannot_access_mini":0.005,"Tests\\Feature\\MiniAuthTest::test_mini_token_cannot_access_admin_api":0.044,"Tests\\Feature\\ExportTest::test_export_xlsx_with_encoded_chinese_filename":0.183,"Tests\\Feature\\ExportTest::test_export_category_filters_to_vegetables":0.017,"Tests\\Feature\\ExportTest::test_export_pdf":0.015,"Tests\\Feature\\ExportTest::test_export_invalid_format_rejected":0.016,"Tests\\Feature\\ExportTest::test_export_requires_export_permission":0.057,"Tests\\Unit\\ExampleTest::test_that_true_is_true":0.001,"Tests\\Feature\\MiniAuthTest::test_login_fails_when_wechat_rejects_code":0.003,"Tests\\Feature\\CartTest::test_add_to_cart_succeeds":0.046,"Tests\\Feature\\CartTest::test_duplicate_add_merges_quantity":0.049,"Tests\\Feature\\CartTest::test_off_shelf_product_rejected":0.046,"Tests\\Feature\\CartTest::test_soft_deleted_product_rejected":0.008,"Tests\\Feature\\CartTest::test_product_without_level_price_rejected":0.006,"Tests\\Feature\\CartTest::test_unbound_user_rejected":0.006,"Tests\\Feature\\CartTest::test_store_without_level_rejected":0.087,"Tests\\Feature\\CartTest::test_list_with_prices_amounts_and_totals":0.051,"Tests\\Feature\\CartTest::test_list_marks_off_shelf_item_unbuyable":0.05,"Tests\\Feature\\CartTest::test_empty_cart_returns_empty_items":0.047,"Tests\\Feature\\CartTest::test_update_quantity":0.049,"Tests\\Feature\\CartTest::test_update_other_users_item_rejected":0.009,"Tests\\Feature\\CartTest::test_update_missing_item_rejected":0.047,"Tests\\Feature\\CartTest::test_invalid_quantity_rejected":0.046,"Tests\\Feature\\CartTest::test_delete_item":0.048,"Tests\\Feature\\CartTest::test_delete_other_users_item_rejected":0.009,"Tests\\Feature\\CartTest::test_clear_only_own_cart":0.096,"Tests\\Feature\\CartTest::test_unauthenticated_returns_401":0.002,"Tests\\Feature\\CartTest::test_cart_isolated_between_users":0.011,"Tests\\Feature\\DebugCartListTest::test_dump_cart_list_response":0.057,"Tests\\Feature\\ProductCostPriceTest::test_calc_actual_price_fixed_and_percent":0.001,"Tests\\Feature\\ProductCostPriceTest::test_calc_actual_price_edge_cases":0.001,"Tests\\Feature\\ProductCostPriceTest::test_admin_list_contains_cost_price_and_actual_price":0.009,"Tests\\Feature\\ProductCostPriceTest::test_mini_list_returns_actual_price_without_cost_price":0.046,"Tests\\Feature\\ProductCostPriceTest::test_create_product_with_cost_and_percent_pricing":0.008,"Tests\\Feature\\ProductCostPriceTest::test_create_percent_row_without_percent_fails":0.007,"Tests\\Feature\\ProductCostPriceTest::test_update_switches_fixed_to_percent":0.01,"Tests\\Feature\\ProductCostPriceTest::test_batch_price_cost_only_notifies_percent_level_stores":0.01,"Tests\\Feature\\ProductCostPriceTest::test_batch_price_percent_row_update":0.009,"Tests\\Feature\\ProductCostPriceTest::test_batch_price_mixed_row_updates_cost_and_level":0.008,"Tests\\Feature\\ProductCostPriceTest::test_batch_price_empty_row_rejected":0.006,"Tests\\Feature\\ProductCostPriceTest::test_price_matrix_includes_cost_and_percent_columns":0.007,"Tests\\Feature\\ProductCostPriceTest::test_mini_order_uses_actual_price_for_percent_row":0.007,"Tests\\Feature\\ProductCostPriceTest::test_mini_cart_uses_actual_price_for_percent_row":0.01,"Tests\\Feature\\PurchaseGenerateTest::test_generate_uses_min_actual_price_across_types":0.014,"Tests\\Feature\\ProductPriceTest::test_store_without_level_sees_products_without_price":0.046,"Tests\\Feature\\ProductPriceTest::test_unbound_user_can_browse_products_without_price_but_cannot_cart":0.009,"Tests\\Feature\\MiniAuthTest::test_login_issues_token_for_registered_user":0.01,"Tests\\Feature\\MiniAuthTest::test_login_rejects_unregistered_openid":0.002,"Tests\\Feature\\MiniAuthTest::test_register_binds_store_and_issues_token":0.009,"Tests\\Feature\\MiniAuthTest::test_register_rejects_unknown_store":0.002,"Tests\\Feature\\MiniAuthTest::test_register_rejects_duplicate_openid":0.003,"Tests\\Feature\\MiniStoreTest::test_info_returns_bound_store":0.005,"Tests\\Feature\\MiniStoreTest::test_update_info":0.045,"Tests\\Feature\\MiniStoreTest::test_update_info_ignores_readonly_fields":0.045,"Tests\\Feature\\MiniStoreTest::test_requires_bound_store":0.007,"Tests\\Feature\\MiniStoreTest::test_update_info_validates_length":0.045,"Tests\\Feature\\StoreOrderTest::test_container_update_recalculates_amounts":0.012,"Tests\\Feature\\StoreOrderTest::test_container_update_allowed_in_editable_statuses":0.022,"Tests\\Feature\\StoreOrderTest::test_container_update_rejected_in_other_statuses":0.019,"Tests\\Feature\\StoreOrderTest::test_container_update_heals_legacy_product_amount":0.02,"Tests\\Feature\\StoreOrderTest::test_container_update_validates_negative_numbers":0.014,"Tests\\Feature\\PurchaseGenerateTest::test_generate_without_accepted_orders_fails":0.062,"Tests\\Feature\\PurchaseGenerateTest::test_generate_with_selected_order_ids":0.104,"Tests\\Feature\\StoreOrderItemTest::test_place_order_snapshots_full_product_info":0.05,"Tests\\Feature\\StoreOrderItemTest::test_mini_detail_hides_cost_price":0.051,"Tests\\Feature\\StoreOrderItemTest::test_admin_detail_shows_cost_price_and_supplier":0.054,"Tests\\Feature\\StoreOrderItemTest::test_update_item_recalculates_order_totals":0.011,"Tests\\Feature\\StoreOrderItemTest::test_update_item_rejected_when_completed_or_cancelled":0.011,"Tests\\Feature\\StoreOrderItemTest::test_update_item_validates_negative_price":0.012,"Tests\\Feature\\StoreOrderItemTest::test_sync_item_updates_snapshot_from_product":0.013,"Tests\\Feature\\StoreOrderItemTest::test_sync_item_recalculates_percent_price_with_latest_cost":0.01,"Tests\\Feature\\StoreOrderItemTest::test_sync_item_rejected_when_product_deleted":0.01,"Tests\\Feature\\StoreOrderItemTest::test_order_list_search_by_product_name":0.058,"Tests\\Feature\\StoreOrderTest::test_soft_delete_only_cancelled_orders":0.062,"Tests\\Feature\\StoreOrderTest::test_soft_delete_rejected_for_non_cancelled_orders":0.246,"Tests\\Feature\\StoreOrderTest::test_soft_delete_twice_rejected":0.053,"Tests\\Feature\\PurchaseEditTest::test_detail_returns_store_matrix":0.104,"Tests\\Feature\\PurchaseEditTest::test_update_cell_syncs_order_item_and_totals":0.106,"Tests\\Feature\\PurchaseEditTest::test_update_row_cost_syncs_all_order_items":0.104,"Tests\\Feature\\PurchaseEditTest::test_update_row_weight_distributed_by_quantity":0.032,"Tests\\Feature\\PurchaseEditTest::test_update_row_requires_at_least_one_field":0.102,"Tests\\Feature\\PurchaseEditTest::test_update_cell_rejects_negative_quantity":0.1,"Tests\\Feature\\PurchaseGenerateTest::test_generate_aggregates_order_items_into_header":0.1,"Tests\\Feature\\ReconciliationTest::test_build_excludes_unfinished_orders":0.016,"Tests\\Feature\\PurchaseEditTest::test_update_row_syncs_product_attributes":0.104,"Tests\\Feature\\PurchaseEditTest::test_update_row_syncs_to_product_catalog":0.019,"Tests\\Feature\\PurchaseEditTest::test_cell_detail_returns_items_with_order_info":0.107,"Tests\\Feature\\PurchaseEditTest::test_cell_detail_rejects_missing_params":0.1,"Tests\\Feature\\PurchaseEditTest::test_update_cell_weight_cascades_totals":0.102,"Tests\\Feature\\PurchaseEditTest::test_cell_sync_refreshes_snapshot_and_cascades":0.012,"Tests\\Feature\\PurchaseEditTest::test_edits_rejected_when_purchase_completed":0.107,"Tests\\Feature\\PurchaseEditTest::test_update_row_does_not_touch_manual_weight":0.107,"Tests\\Feature\\PurchaseEditTest::test_store_summary_returns_aggregated_items":0.103,"Tests\\Feature\\PurchaseEditTest::test_store_summary_aggregates_multiple_orders_with_weighted_price":0.061,"Tests\\Feature\\PurchaseEditTest::test_store_summary_rejects_missing_or_invalid_params":0.104,"Tests\\Feature\\ExportTest::test_export_invalid_type_rejected":0.016,"Tests\\Feature\\ExportTest::test_export_missing_purchase_rejected":0.009,"Tests\\Feature\\PurchaseEditTest::test_detail_returns_container_summary":0.022,"Tests\\Feature\\PurchaseEditTest::test_update_container_syncs_order_amounts":0.018,"Tests\\Feature\\PurchaseEditTest::test_update_container_rejected_when_purchase_completed":0.018,"Tests\\Feature\\PurchaseEditTest::test_update_container_rejects_incomplete_or_foreign_orders":0.026,"Tests\\Feature\\PurchaseEditTest::test_update_container_validates_negative_and_duplicate_orders":0.018,"Tests\\Feature\\ProductCategoryTest::test_create_child_category_under_top_level":0.006,"Tests\\Feature\\ProductCategoryTest::test_create_category_under_second_level_rejected":0.006,"Tests\\Feature\\ProductCategoryTest::test_update_second_level_move_to_another_top":0.006,"Tests\\Feature\\ProductCategoryTest::test_update_parent_with_children_cannot_move_under_category":0.007,"Tests\\Feature\\ProductCategoryTest::test_update_with_missing_parent_rejected":0.006,"Tests\\Feature\\ProductCategoryTest::test_create_product_with_parent_category_rejected":0.007,"Tests\\Feature\\ProductCategoryTest::test_create_product_with_leaf_category_ok":0.007,"Tests\\Feature\\ProductCategoryTest::test_mini_categories_returns_all_enabled_categories":0.044,"Tests\\Feature\\MiniBillTest::test_bill_list_derives_pay_state_and_settlement_date":0.047,"Tests\\Feature\\MiniBillTest::test_bill_list_summary_aggregates_unpaid":0.047,"Tests\\Feature\\MiniBillTest::test_bill_list_payable_filter":0.047,"Tests\\Feature\\MiniBillTest::test_bill_list_filters_by_bill_date_range":0.05,"Tests\\Feature\\MiniBillTest::test_bill_data_isolated_between_stores":0.088,"Tests\\Feature\\MiniBillTest::test_bill_detail_outputs_bill_items_and_orders":0.048,"Tests\\Feature\\StoreOrderTest::test_order_list_outputs_status_name_and_item_preview":0.053,"Tests\\Feature\\StoreOrderTest::test_order_list_filters_by_order_date_range":0.06,"Tests\\Feature\\StoreOrderTest::test_order_list_rejects_invalid_filters":0.048,"Tests\\Feature\\BillExportTest::test_export_merges_items_across_bills":0.093,"Tests\\Feature\\BillExportTest::test_export_filters_by_root_category":0.05,"Tests\\Feature\\BillExportTest::test_export_empty_category_rejected":0.066,"Tests\\Feature\\BillExportTest::test_export_requires_export_permission":0.048,"Tests\\Feature\\BillExportTest::test_export_invalid_ids_rejected":0.01,"Tests\\Feature\\DebugPermissionTest::test_debug_permission":0.098,"Tests\\Feature\\BillExportTest::test_mini_export_scoped_to_bound_store":0.014,"Tests\\Feature\\BillExportTest::test_mini_export_requires_store_binding":0.005,"Tests\\Feature\\MiniNoticeTest::test_notice_list_includes_personal_and_broadcast":0.045,"Tests\\Feature\\MiniNoticeTest::test_broadcast_read_creates_personal_copy":0.049,"Tests\\Feature\\MiniNoticeTest::test_personal_notice_read_marks_directly":0.045,"Tests\\Feature\\MiniNoticeTest::test_other_users_notice_not_found":0.007,"Tests\\Feature\\StoreOrderTest::test_manual_status_flow_only_accept_and_cancel":0.101,"Tests\\Feature\\StoreOrderTest::test_manual_status_flow_rejects_delivery_and_completion":0.15,"Tests\\Feature\\StoreOrderTest::test_batch_status_only_accept_and_cancel":0.102,"Tests\\Feature\\BillPaymentTest::test_order_status_progresses_via_business_chain":0.058,"Tests\\Feature\\BillPaymentTest::test_manual_pay_accumulates_store_total_purchase_amount":0.067,"Tests\\Feature\\BillPaymentTest::test_payment_audit_pass_accumulates_store_total_purchase_amount":0.051,"Tests\\Feature\\BillPaymentTest::test_payment_audit_reject_does_not_accumulate":0.049,"Tests\\Feature\\MiniProductTest::test_product_detail_guest_sees_null_price":0.045,"Tests\\Feature\\MiniProductTest::test_product_detail_store_user_sees_level_price":0.006,"Tests\\Feature\\MiniProductTest::test_product_detail_percent_price_calculated_from_cost":0.045,"Tests\\Feature\\MiniProductTest::test_product_detail_off_shelf_or_missing_rejected":0.046,"Tests\\Feature\\MiniProductTest::test_product_list_store_user_sees_level_price":0.005,"Tests\\Feature\\MiniProductTest::test_product_list_guest_sees_null_price":0.045,"Tests\\Feature\\BillPaymentTest::test_admin_order_list_and_detail_show_bill_info":0.078,"Tests\\Feature\\BillPaymentTest::test_payment_detail_returns_voucher_urls":0.052,"Tests\\Feature\\StoreOrderTest::test_order_list_preview_includes_image_spec_and_unit":0.05,"Tests\\Feature\\ContainerReturnTest::test_press_containers_adds_amount_and_writes_record":0.061,"Tests\\Feature\\ContainerReturnTest::test_return_containers_deducts_amount":0.06,"Tests\\Feature\\ContainerReturnTest::test_mixed_sign_containers":0.06,"Tests\\Feature\\ContainerReturnTest::test_zero_containers_writes_no_record":0.06,"Tests\\Feature\\ContainerReturnTest::test_non_integer_containers_rejected":0.047,"Tests\\Feature\\ContainerReturnTest::test_manual_create_and_delete_routes_removed":0.006,"Tests\\Feature\\ContainerReturnTest::test_export_matrix_merges_same_day_and_zero_fills":0.088,"Tests\\Feature\\ContainerReturnTest::test_export_filters_by_selected_stores":0.128,"Tests\\Feature\\ContainerReturnTest::test_export_invalid_params_rejected":0.009,"Tests\\Feature\\ContainerReturnTest::test_export_empty_range_rejected":0.006,"Tests\\Feature\\ContainerReturnTest::test_export_requires_export_permission":0.006,"Tests\\Feature\\DashboardTest::test_guest_cannot_access_dashboard":0.002,"Tests\\Feature\\DashboardTest::test_requires_dashboard_analysis_ability":0.006,"Tests\\Feature\\DashboardTest::test_empty_database_returns_zero_structure":0.009,"Tests\\Feature\\DashboardTest::test_overview_excludes_cancelled_orders":0.051,"Tests\\Feature\\DashboardTest::test_week_growth_rate":0.054,"Tests\\Feature\\DashboardTest::test_trend_covers_30_days_with_zero_filling":0.051,"Tests\\Feature\\DashboardTest::test_category_sales_grouped_by_category":0.053,"Tests\\Feature\\DashboardTest::test_top_products_aggregated_across_orders":0.05,"Tests\\Feature\\DashboardTest::test_top_stores_ranked_by_amount":0.092,"Tests\\Feature\\DashboardTest::test_purchase_amount_prefers_actual_over_estimate":0.009,"Tests\\Feature\\DashboardTest::test_recon_summary_and_receivable":0.051,"Tests\\Feature\\DashboardTest::test_latest_orders_limited_and_ordered":0.052,"Tests\\Feature\\DashboardTest::test_archives_count_active_records_only":0.133,"Tests\\Feature\\DashboardDebugTest::test_debug_ability_check":0.044,"Tests\\Feature\\DashboardDebugTest::test_debug":0.045,"Tests\\Feature\\ProductPriceTest::test_batch_price_updates_cost_and_notifies_all_stores":0.09,"Tests\\Feature\\ProductPriceTest::test_level_percent_update_notifies_level_stores":0.09,"Tests\\Feature\\ProductPriceTest::test_level_update_without_percent_change_sends_no_notice":0.047,"Tests\\Feature\\ProductPriceTest::test_level_percent_validation":0.008,"Tests\\Feature\\ProductCostPriceTest::test_calc_level_price":0.001,"Tests\\Feature\\ProductCostPriceTest::test_calc_level_price_edge_cases":0.001,"Tests\\Feature\\ProductCostPriceTest::test_admin_list_contains_cost_price_and_level_prices":0.007,"Tests\\Feature\\ProductCostPriceTest::test_create_product_with_cost_price":0.006,"Tests\\Feature\\ProductCostPriceTest::test_batch_price_cost_row_update":0.052,"Tests\\Feature\\ProductCostPriceTest::test_price_matrix_includes_cost_and_level_price_columns":0.007,"Tests\\Feature\\ProductCostPriceTest::test_mini_order_uses_level_price":0.048,"Tests\\Feature\\ProductCostPriceTest::test_mini_cart_uses_level_price":0.048,"Tests\\Feature\\CartTest::test_store_level_missing_rejected":0.006,"Tests\\Feature\\StoreOrderTest::test_store_level_missing_rejected":0.047,"Tests\\Feature\\PurchaseEditTest::test_update_row_skips_catalog_sync_when_product_deleted":0.108,"Tests\\Feature\\PurchaseEditTest::test_store_item_add_creates_item_and_recalculates":0.105,"Tests\\Feature\\PurchaseEditTest::test_store_item_update_merges_multi_order_rows":0.06,"Tests\\Feature\\PurchaseEditTest::test_store_item_remove_deletes_and_recalculates":0.101,"Tests\\Feature\\StoreOrderItemTest::test_update_store_item_recalculates_totals":0.058,"Tests\\Feature\\StoreOrderItemTest::test_store_item_edits_rejected_when_purchase_completed":0.064,"Tests\\Feature\\StoreOrderItemTest::test_update_store_item_validates_negative_values":0.058,"Tests\\Feature\\StoreOrderItemTest::test_add_store_item_uses_level_percent_price":0.058,"Tests\\Feature\\StoreOrderItemTest::test_add_store_item_rejected_when_product_deleted":0.057,"Tests\\Feature\\ExportTest::test_export_lists_all_catalog_products_with_totals_row":0.102,"Tests\\Feature\\ExportTest::test_export_supplier_filter":0.101,"Tests\\Feature\\ExportTest::test_export_stores_multi_sheet":0.102,"Tests\\Feature\\ExportTest::test_export_stores_single_store":0.1,"Tests\\Feature\\ExportTest::test_export_stores_without_items_rejected":0.142,"Tests\\Feature\\ExportTest::test_export_suppliers_multi_sheet":0.103,"Tests\\Feature\\ExportTest::test_export_suppliers_single":0.101,"Tests\\Feature\\ExportTest::test_export_suppliers_without_items_rejected":0.1,"Tests\\Feature\\PurchaseEditTest::test_update_row_cost_change_reprices_items_by_store_level":0.106,"Tests\\Feature\\PurchaseEditTest::test_update_row_without_cost_change_keeps_item_price":0.105,"Tests\\Feature\\CartTest::test_update_other_stores_item_rejected":0.091,"Tests\\Feature\\CartTest::test_delete_other_stores_item_rejected":0.089,"Tests\\Feature\\CartTest::test_cart_isolated_between_stores":0.089,"Tests\\Feature\\MiniAuthTest::test_login_issues_token_for_valid_credentials":0.127,"Tests\\Feature\\MiniAuthTest::test_login_rejects_wrong_password":0.125,"Tests\\Feature\\MiniAuthTest::test_login_rejects_unknown_username":0.003,"Tests\\Feature\\MiniAuthTest::test_disabled_store_cannot_login":0.125,"Tests\\Feature\\MiniAuthTest::test_login_validates_required_params":0.002,"Tests\\Feature\\MiniAuthTest::test_login_token_accesses_protected_routes":0.128,"Tests\\Feature\\MiniAuthTest::test_set_password_succeeds":0.211,"Tests\\Feature\\MiniAuthTest::test_set_password_rejects_wrong_old_password":0.126,"Tests\\Feature\\MiniAuthTest::test_set_password_validates_confirmation":0.045,"Tests\\Feature\\BillExportTest::test_mini_export_scoped_to_own_store":0.095,"Tests\\Feature\\MiniNoticeTest::test_other_stores_notice_not_found":0.087,"Tests\\Feature\\MiniProductTest::test_product_detail_store_sees_level_price":0.045,"Tests\\Feature\\MiniProductTest::test_product_list_store_sees_level_price":0.047,"Tests\\Feature\\MiniStoreTest::test_info_returns_current_store":0.044,"Tests\\Feature\\MiniStoreTest::test_disabled_store_rejected":0.047,"Tests\\Feature\\ProductImportExportTest::test_export_downloads_all_products":0.008,"Tests\\Feature\\ProductImportExportTest::test_export_filters_by_category":0.008,"Tests\\Feature\\ProductImportExportTest::test_export_template_mode":0.005,"Tests\\Feature\\ProductImportExportTest::test_export_requires_permission":0.006,"Tests\\Feature\\ProductImportExportTest::test_import_creates_products_and_auto_supplier":0.041,"Tests\\Feature\\ProductImportExportTest::test_import_matches_existing_supplier":0.015,"Tests\\Feature\\ProductImportExportTest::test_import_errors_abort_all_rows":0.016,"Tests\\Feature\\ProductImportExportTest::test_import_category_path_resolution":0.031,"Tests\\Feature\\ProductImportExportTest::test_import_rejects_invalid_header":0.014,"Tests\\Feature\\ProductImportExportTest::test_import_requires_permission":0.009,"Tests\\Feature\\ProductImportExportTest::test_list_filters_by_market":0.007,"Tests\\Feature\\MiniReportTest::test_report_aggregates_total_and_item_percent":0.048,"Tests\\Feature\\MiniReportTest::test_report_accumulates_same_product_across_orders":0.047,"Tests\\Feature\\MiniReportTest::test_report_filters_by_custom_date_range":0.047,"Tests\\Feature\\MiniReportTest::test_report_excludes_cancelled_and_deleted_orders":0.047,"Tests\\Feature\\MiniReportTest::test_report_preset_month_and_last_month":0.049,"Tests\\Feature\\MiniReportTest::test_report_preset_week_and_last_week":0.049,"Tests\\Feature\\MiniReportTest::test_report_defaults_to_current_month":0.058,"Tests\\Feature\\MiniReportTest::test_report_data_isolated_between_stores":0.087,"Tests\\Feature\\MiniReportTest::test_report_empty_period_returns_zero":0.046,"Tests\\Feature\\MiniReportTest::test_report_validates_params":0.049,"Tests\\Feature\\ProductBatchDeleteTest::test_batch_delete_soft_deletes_selected_products":0.007,"Tests\\Feature\\ProductBatchDeleteTest::test_batch_delete_requires_non_empty_ids":0.006,"Tests\\Feature\\ProductBatchDeleteTest::test_batch_delete_forbidden_without_permission":0.006,"Tests\\Feature\\CartTest::test_place_order_clears_ordered_items_from_cart":0.056,"Tests\\Feature\\StoreOrderItemTest::test_admin_order_list_filters_by_order_date_range":0.064,"Tests\\Feature\\StoreOrderItemTest::test_admin_order_list_search_by_purchase_no":0.064,"Tests\\Feature\\ExportTest::test_export_suppliers_split_by_market":0.056,"Tests\\Feature\\BillPaymentTest::test_generate_bill_saves_after_sale_and_remark":0.117,"Tests\\Feature\\ProductCostPriceTest::test_product_price_unit_default_and_editable":0.013,"Tests\\Feature\\StoreOrderTest::test_place_order_blocked_by_overdue_unpaid_bill":0.054,"Tests\\Feature\\StoreOrderTest::test_place_order_allowed_within_payment_cycle":0.057,"Tests\\Feature\\CartTest::test_cart_summary_endpoint":0.053,"Tests\\Feature\\CartTest::test_cart_summary_requires_login":0.002,"Tests\\Feature\\MiniProductTest::test_product_list_appends_cart_quantity_and_summary":0.053,"Tests\\Feature\\MiniProductTest::test_product_list_guest_sees_zero_cart_fields":0.045,"Tests\\Feature\\MiniProductTest::test_product_detail_appends_cart_quantity":0.048,"Tests\\Feature\\MiniProductTest::test_home_appends_cart_summary":0.049,"Tests\\Feature\\MiniOnlinePaymentTest::test_sign_matches_document_example":0.002,"Tests\\Feature\\MiniOnlinePaymentTest::test_create_online_payment_success":0.055,"Tests\\Feature\\MiniOnlinePaymentTest::test_create_online_payment_validation":0.099,"Tests\\Feature\\MiniOnlinePaymentTest::test_create_fails_when_code2session_fails":0.048,"Tests\\Feature\\MiniOnlinePaymentTest::test_create_gateway_failure_releases_bills":0.053,"Tests\\Feature\\MiniOnlinePaymentTest::test_notify_settles_payment":0.057,"Tests\\Feature\\MiniOnlinePaymentTest::test_notify_rejects_invalid_messages":0.051,"Tests\\Feature\\MiniOnlinePaymentTest::test_query_syncs_gateway_status":0.061,"Tests\\Feature\\MiniOnlinePaymentTest::test_query_rejects_other_store_payment":0.086,"Tests\\Feature\\MiniOnlinePaymentTest::test_offline_payment_flow_unaffected":0.047,"Tests\\Feature\\BillPaymentTest::test_generate_bill_positive_after_sale_added_to_total":0.111,"Tests\\Feature\\BillPaymentTest::test_generate_bill_rejects_non_numeric_after_sale":0.047,"Tests\\Feature\\ClientSpecialTest::test_batch_add_creates_specials":0.008,"Tests\\Feature\\ClientSpecialTest::test_batch_add_skips_existing_products":0.009,"Tests\\Feature\\ClientSpecialTest::test_batch_add_requires_non_empty_product_ids":0.006,"Tests\\Feature\\ClientSpecialTest::test_batch_add_forbidden_without_permission":0.007,"Tests\\Feature\\ClientSpecialTest::test_query_filters_by_product_name_keyword":0.007,"Tests\\Feature\\ClientSpecialTest::test_update_changes_sort_and_status":0.006,"Tests\\Feature\\ClientSpecialTest::test_delete_and_batch_delete":0.008,"Tests\\Feature\\ClientSpecialTest::test_batch_delete_requires_non_empty_ids":0.025,"Tests\\Feature\\MiniSpecialTest::test_guest_sees_specials_with_null_price":0.045,"Tests\\Feature\\MiniSpecialTest::test_store_sees_level_price":0.046,"Tests\\Feature\\MiniSpecialTest::test_disabled_special_and_off_shelf_product_excluded":0.046,"Tests\\Feature\\MiniSpecialTest::test_specials_sorted_by_sort_asc":0.086,"Tests\\Feature\\MiniSpecialTest::test_cart_quantity_attached_for_store":0.046,"Tests\\Feature\\MiniOnlinePaymentTest::test_aes_encryption_matches_demo_golden":0.001,"Tests\\Feature\\MiniOnlinePaymentTest::test_notify_envelope_round_trip":0.004,"Tests\\Feature\\MiniOnlinePaymentTest::test_swapped_key_config_fails_with_clear_message":0.004,"Tests\\Feature\\MiniWechatPaymentTest::test_create_wechat_payment_success":0.053,"Tests\\Feature\\MiniWechatPaymentTest::test_create_wechat_payment_gateway_failure_releases_bills":0.052,"Tests\\Feature\\MiniWechatPaymentTest::test_wechat_notify_settles_payment":0.056,"Tests\\Feature\\MiniWechatPaymentTest::test_wechat_notify_rejects_invalid_messages":0.053,"Tests\\Feature\\MiniWechatPaymentTest::test_wechat_notify_with_platform_cert_validates_signature":0.053,"Tests\\Feature\\MiniWechatPaymentTest::test_wechat_query_syncs_status":0.056,"Tests\\Feature\\MiniWechatPaymentTest::test_wangpu_channel_remains_default":0.055,"Tests\\Feature\\BillPaymentTest::test_payment_config_returns_pay_methods":0.048,"Tests\\Feature\\MiniMpPaymentTest::test_mp_scene_wechat_channel":0.073,"Tests\\Feature\\MiniMpPaymentTest::test_mp_scene_wangpu_channel":0.055,"Tests\\Feature\\MiniMpPaymentTest::test_mp_scene_oauth_failure":0.049,"Tests\\Feature\\MiniMpPaymentTest::test_mp_scene_requires_mp_config":0.046,"Tests\\Feature\\MiniMpPaymentTest::test_default_scene_remains_mini":0.053,"Tests\\Feature\\MiniProductTest::test_product_list_ordered_by_category_tree":0.005,"Tests\\Feature\\MiniOnlinePaymentTest::test_notify_sign_matches_doc_golden":0.002,"Tests\\Feature\\MiniOnlinePaymentTest::test_notify_verify_rejects_bad_sign":0.005,"Tests\\Feature\\ExportTest::test_export_hides_detail_columns_by_default":0.106,"Tests\\Feature\\ExportTest::test_export_scope_includes_off_shelf_but_not_deleted":0.097,"Tests\\Unit\\WeightEstimatorTest::test_weight_unit_quantity_is_weight":0.001,"Tests\\Unit\\WeightEstimatorTest::test_spec_per_unit_weight":0,"Tests\\Unit\\WeightEstimatorTest::test_unparsable_spec_fallback":0.001,"Tests\\Feature\\StoreOrderTest::test_place_order_prefills_reference_weight":0.049,"Tests\\Feature\\PurchaseGenerateTest::test_generate_sums_reference_weight":0.055,"Tests\\Feature\\PurchaseEditTest::test_store_item_add_defaults_reference_weight":0.103,"Tests\\Feature\\StoreOrderTest::test_place_order_blocked_outside_order_time_window":0.057,"Tests\\Feature\\StoreOrderTest::test_place_order_cross_midnight_time_window":0.053,"Tests\\Feature\\StoreOrderTest::test_place_order_only_cutoff_time_and_invalid_config":0.053}} \ No newline at end of file +{"version":2,"defects":{"Tests\\Feature\\ProductPriceTest::test_product_list_returns_price_for_store_level":8,"Tests\\Feature\\ProductPriceTest::test_store_without_level_is_rejected":8,"Tests\\Feature\\ProductPriceTest::test_batch_price_updates_and_notifies_affected_stores":7,"Tests\\Feature\\ProductPriceTest::test_price_matrix_structure":8,"Tests\\Feature\\StoreOrderTest::test_place_order_snapshots_level_price_and_recalculates":8,"Tests\\Feature\\StoreOrderTest::test_client_supplied_amount_is_ignored":8,"Tests\\Feature\\StoreOrderTest::test_product_without_level_price_rejected":8,"Tests\\Feature\\StoreOrderTest::test_cancel_only_pending_orders":8,"Tests\\Feature\\StoreOrderTest::test_summarized_order_cannot_be_cancelled":8,"Tests\\Feature\\StoreOrderTest::test_order_data_isolated_between_stores":8,"Tests\\Feature\\PurchaseGenerateTest::test_generate_aggregates_orders_by_product":7,"Tests\\Feature\\PurchaseGenerateTest::test_generate_writes_back_order_status":7,"Tests\\Feature\\PurchaseGenerateTest::test_generate_without_pending_orders_fails":8,"Tests\\Feature\\PurchaseGenerateTest::test_generate_is_idempotent":7,"Tests\\Feature\\AllocationTest::test_allocation_conserves_amount_with_tail_correction":7,"Tests\\Feature\\AllocationTest::test_allocation_follows_order_ratio":7,"Tests\\Feature\\AllocationTest::test_allocation_is_idempotent":7,"Tests\\Feature\\AllocationTest::test_allocation_rejected_without_actual_amount":8,"Tests\\Feature\\MiniAuthTest::test_login_creates_user_and_issues_token":7,"Tests\\Feature\\MiniAuthTest::test_phone_binding_matches_store":8,"Tests\\Feature\\MiniAuthTest::test_phone_no_match_stays_pending":8,"Tests\\Feature\\ExportTest::test_export_xlsx_with_encoded_chinese_filename":7,"Tests\\Feature\\CartTest::test_list_with_prices_amounts_and_totals":7,"Tests\\Feature\\CartTest::test_list_marks_off_shelf_item_unbuyable":7,"Tests\\Feature\\DebugCartListTest::test_dump_cart_list_response":5,"Tests\\Feature\\ProductCostPriceTest::test_create_product_with_cost_and_percent_pricing":7,"Tests\\Feature\\ProductCostPriceTest::test_create_percent_row_without_percent_fails":7,"Tests\\Feature\\ProductCostPriceTest::test_batch_price_cost_only_notifies_percent_level_stores":7,"Tests\\Feature\\ProductCostPriceTest::test_batch_price_empty_row_rejected":7,"Tests\\Feature\\MiniAuthTest::test_disabled_user_cannot_login":8,"Tests\\Feature\\MiniAuthTest::test_mini_token_cannot_access_admin_api":8,"Tests\\Feature\\ProductCostPriceTest::test_batch_price_percent_row_update":7,"Tests\\Feature\\ProductCostPriceTest::test_batch_price_mixed_row_updates_cost_and_level":7,"Tests\\Feature\\ReconciliationTest::test_build_creates_reconciliation_items":8,"Tests\\Feature\\ReconciliationTest::test_build_filters_by_supplier":8,"Tests\\Feature\\ReconciliationTest::test_update_item_recalculates_diff_and_header":8,"Tests\\Feature\\ReconciliationTest::test_toggle_reconciled_flag":8,"Tests\\Feature\\ReconciliationTest::test_settle_creates_settlements_per_store":8,"Tests\\Feature\\ProductCostPriceTest::test_mini_list_returns_actual_price_without_cost_price":8,"Tests\\Feature\\ProductCostPriceTest::test_mini_order_uses_actual_price_for_percent_row":8,"Tests\\Feature\\ProductCostPriceTest::test_mini_cart_uses_actual_price_for_percent_row":8,"Tests\\Feature\\StoreOrderTest::test_container_update_recalculates_amounts":8,"Tests\\Feature\\StoreOrderTest::test_container_update_allowed_in_editable_statuses":8,"Tests\\Feature\\StoreOrderTest::test_container_update_rejected_in_other_statuses":8,"Tests\\Feature\\StoreOrderTest::test_container_update_heals_legacy_product_amount":8,"Tests\\Feature\\StoreOrderTest::test_container_update_validates_negative_numbers":8,"Tests\\Feature\\StoreOrderItemTest::test_sync_item_rejected_when_product_deleted":7,"Tests\\Feature\\StoreOrderItemTest::test_order_list_search_by_product_name":8,"Tests\\Feature\\PurchaseEditTest::test_detail_returns_store_matrix":7,"Tests\\Feature\\PurchaseEditTest::test_update_cell_syncs_order_item_and_totals":7,"Tests\\Feature\\PurchaseEditTest::test_update_row_cost_syncs_all_order_items":7,"Tests\\Feature\\PurchaseEditTest::test_update_row_weight_distributed_by_quantity":7,"Tests\\Feature\\PurchaseEditTest::test_update_row_syncs_product_attributes":7,"Tests\\Feature\\PurchaseGenerateTest::test_generate_aggregates_order_items_into_header":7,"Tests\\Feature\\ExportTest::test_export_category_filters_to_vegetables":7,"Tests\\Feature\\ExportTest::test_export_pdf":7,"Tests\\Feature\\PurchaseEditTest::test_update_row_does_not_touch_manual_weight":7,"Tests\\Feature\\PurchaseEditTest::test_update_row_syncs_to_product_catalog":7,"Tests\\Feature\\PurchaseEditTest::test_update_cell_weight_cascades_totals":7,"Tests\\Feature\\PurchaseEditTest::test_cell_sync_refreshes_snapshot_and_cascades":7,"Tests\\Feature\\PurchaseEditTest::test_edits_rejected_when_purchase_completed":7,"Tests\\Feature\\StoreOrderItemTest::test_update_item_recalculates_order_totals":7,"Tests\\Feature\\StoreOrderItemTest::test_update_item_validates_negative_price":7,"Tests\\Feature\\StoreOrderItemTest::test_sync_item_updates_snapshot_from_product":7,"Tests\\Feature\\StoreOrderItemTest::test_sync_item_recalculates_percent_price_with_latest_cost":7,"Tests\\Feature\\PurchaseEditTest::test_cell_detail_returns_items_with_order_info":8,"Tests\\Feature\\ProductCategoryTest::test_create_product_with_leaf_category_ok":7,"Tests\\Feature\\BillExportTest::test_export_filters_by_root_category":8,"Tests\\Feature\\BillExportTest::test_export_requires_export_permission":8,"Tests\\Feature\\DebugPermissionTest::test_debug_permission":8,"Tests\\Feature\\BillPaymentTest::test_order_status_progresses_via_business_chain":7,"Tests\\Feature\\BillPaymentTest::test_payment_audit_reject_does_not_accumulate":7,"Tests\\Feature\\BillPaymentTest::test_payment_detail_returns_voucher_urls":8,"Tests\\Feature\\ContainerReturnTest::test_press_containers_adds_amount_and_writes_record":7,"Tests\\Feature\\ContainerReturnTest::test_return_containers_deducts_amount":7,"Tests\\Feature\\ContainerReturnTest::test_mixed_sign_containers":7,"Tests\\Feature\\ContainerReturnTest::test_zero_containers_writes_no_record":7,"Tests\\Feature\\StoreOrderTest::test_order_list_outputs_status_name_and_item_preview":7,"Tests\\Feature\\DashboardTest::test_requires_dashboard_analysis_ability":7,"Tests\\Feature\\DashboardTest::test_recon_summary_and_receivable":8,"Tests\\Feature\\DashboardDebugTest::test_debug":8,"Tests\\Feature\\StoreOrderItemTest::test_place_order_snapshots_full_product_info":8,"Tests\\Feature\\StoreOrderItemTest::test_mini_detail_hides_cost_price":8,"Tests\\Feature\\StoreOrderItemTest::test_admin_detail_shows_cost_price_and_supplier":8,"Tests\\Feature\\StoreOrderItemTest::test_update_item_rejected_when_completed_or_cancelled":8,"Tests\\Feature\\StoreOrderItemTest::test_add_store_item_uses_level_percent_price":8,"Tests\\Feature\\ExportTest::test_export_suppliers_multi_sheet":7,"Tests\\Feature\\PurchaseEditTest::test_update_row_skips_catalog_sync_when_product_deleted":7,"Tests\\Feature\\ProductImportExportTest::test_export_filters_by_category":8,"Tests\\Feature\\ProductImportExportTest::test_import_creates_products_and_auto_supplier":7,"Tests\\Feature\\ProductImportExportTest::test_import_matches_existing_supplier":7,"Tests\\Feature\\ProductImportExportTest::test_import_errors_abort_all_rows":7,"Tests\\Feature\\ProductImportExportTest::test_import_category_path_resolution":7,"Tests\\Feature\\MiniReportTest::test_report_accumulates_same_product_across_orders":7,"Tests\\Feature\\ExportTest::test_export_suppliers_split_by_market":7,"Tests\\Feature\\MiniOnlinePaymentTest::test_query_syncs_gateway_status":7,"Tests\\Feature\\MiniSpecialTest::test_cart_quantity_attached_for_store":8,"Tests\\Feature\\MiniWechatPaymentTest::test_create_wechat_payment_success":7,"Tests\\Feature\\ExportTest::test_export_lists_all_catalog_products_with_totals_row":7,"Tests\\Feature\\MiniOnlinePaymentTest::test_create_online_payment_success":7,"Tests\\Feature\\MiniMpPaymentTest::test_mp_scene_wechat_channel":8,"Tests\\Feature\\MiniMpPaymentTest::test_default_scene_remains_mini":7,"Tests\\Feature\\MiniProductTest::test_product_list_ordered_by_category_tree":7},"times":{"Tests\\Feature\\ExampleTest::test_the_application_returns_a_successful_response":0.003,"Tests\\Feature\\ProductPriceTest::test_product_list_returns_price_for_store_level":0.088,"Tests\\Feature\\ProductPriceTest::test_store_without_level_is_rejected":0.007,"Tests\\Feature\\ProductPriceTest::test_batch_price_updates_and_notifies_affected_stores":0.01,"Tests\\Feature\\ProductPriceTest::test_price_matrix_structure":0.014,"Tests\\Feature\\StoreOrderTest::test_place_order_snapshots_level_price_and_recalculates":0.054,"Tests\\Feature\\StoreOrderTest::test_client_supplied_amount_is_ignored":0.072,"Tests\\Feature\\StoreOrderTest::test_product_without_level_price_rejected":0.007,"Tests\\Feature\\StoreOrderTest::test_cancel_only_pending_orders":0.07,"Tests\\Feature\\StoreOrderTest::test_summarized_order_cannot_be_cancelled":0.061,"Tests\\Feature\\StoreOrderTest::test_order_data_isolated_between_stores":0.108,"Tests\\Feature\\PurchaseGenerateTest::test_generate_aggregates_orders_by_product":0.028,"Tests\\Feature\\PurchaseGenerateTest::test_generate_writes_back_order_status":0.139,"Tests\\Feature\\PurchaseGenerateTest::test_generate_without_pending_orders_fails":0.006,"Tests\\Feature\\PurchaseGenerateTest::test_generate_is_idempotent":0.149,"Tests\\Feature\\AllocationTest::test_allocation_conserves_amount_with_tail_correction":0.076,"Tests\\Feature\\AllocationTest::test_allocation_follows_order_ratio":0.022,"Tests\\Feature\\AllocationTest::test_allocation_is_idempotent":0.028,"Tests\\Feature\\AllocationTest::test_allocation_rejected_without_actual_amount":0.021,"Tests\\Feature\\DebugGenerateTest::test_debug_generate":0.047,"Tests\\Feature\\ReconciliationTest::test_build_creates_reconciliation_items":0.019,"Tests\\Feature\\ReconciliationTest::test_build_filters_by_supplier":0.022,"Tests\\Feature\\ReconciliationTest::test_update_item_recalculates_diff_and_header":0.019,"Tests\\Feature\\ReconciliationTest::test_toggle_reconciled_flag":0.02,"Tests\\Feature\\ReconciliationTest::test_settle_creates_settlements_per_store":0.036,"Tests\\Feature\\StatementTest::test_generate_snapshots_payment_cycle":0.011,"Tests\\Feature\\StatementTest::test_statement_isolated_between_stores":0.013,"Tests\\Feature\\StatementTest::test_generate_excludes_cancelled_and_used_items":0.016,"Tests\\Feature\\MiniAuthTest::test_login_creates_user_and_issues_token":0.056,"Tests\\Feature\\MiniAuthTest::test_disabled_user_cannot_login":0.003,"Tests\\Feature\\MiniAuthTest::test_phone_binding_matches_store":0.005,"Tests\\Feature\\MiniAuthTest::test_phone_no_match_stays_pending":0.001,"Tests\\Feature\\MiniAuthTest::test_sys_token_cannot_access_mini":0.01,"Tests\\Feature\\MiniAuthTest::test_mini_token_cannot_access_admin_api":0.051,"Tests\\Feature\\ExportTest::test_export_xlsx_with_encoded_chinese_filename":0.12,"Tests\\Feature\\ExportTest::test_export_category_filters_to_vegetables":0.017,"Tests\\Feature\\ExportTest::test_export_pdf":0.015,"Tests\\Feature\\ExportTest::test_export_invalid_format_rejected":0.016,"Tests\\Feature\\ExportTest::test_export_requires_export_permission":0.056,"Tests\\Unit\\ExampleTest::test_that_true_is_true":0.001,"Tests\\Feature\\MiniAuthTest::test_login_fails_when_wechat_rejects_code":0.003,"Tests\\Feature\\CartTest::test_add_to_cart_succeeds":0.046,"Tests\\Feature\\CartTest::test_duplicate_add_merges_quantity":0.047,"Tests\\Feature\\CartTest::test_off_shelf_product_rejected":0.047,"Tests\\Feature\\CartTest::test_soft_deleted_product_rejected":0.008,"Tests\\Feature\\CartTest::test_product_without_level_price_rejected":0.006,"Tests\\Feature\\CartTest::test_unbound_user_rejected":0.006,"Tests\\Feature\\CartTest::test_store_without_level_rejected":0.085,"Tests\\Feature\\CartTest::test_list_with_prices_amounts_and_totals":0.049,"Tests\\Feature\\CartTest::test_list_marks_off_shelf_item_unbuyable":0.053,"Tests\\Feature\\CartTest::test_empty_cart_returns_empty_items":0.045,"Tests\\Feature\\CartTest::test_update_quantity":0.048,"Tests\\Feature\\CartTest::test_update_other_users_item_rejected":0.009,"Tests\\Feature\\CartTest::test_update_missing_item_rejected":0.048,"Tests\\Feature\\CartTest::test_invalid_quantity_rejected":0.047,"Tests\\Feature\\CartTest::test_delete_item":0.048,"Tests\\Feature\\CartTest::test_delete_other_users_item_rejected":0.009,"Tests\\Feature\\CartTest::test_clear_only_own_cart":0.094,"Tests\\Feature\\CartTest::test_unauthenticated_returns_401":0.002,"Tests\\Feature\\CartTest::test_cart_isolated_between_users":0.011,"Tests\\Feature\\DebugCartListTest::test_dump_cart_list_response":0.057,"Tests\\Feature\\ProductCostPriceTest::test_calc_actual_price_fixed_and_percent":0.001,"Tests\\Feature\\ProductCostPriceTest::test_calc_actual_price_edge_cases":0.001,"Tests\\Feature\\ProductCostPriceTest::test_admin_list_contains_cost_price_and_actual_price":0.009,"Tests\\Feature\\ProductCostPriceTest::test_mini_list_returns_actual_price_without_cost_price":0.074,"Tests\\Feature\\ProductCostPriceTest::test_create_product_with_cost_and_percent_pricing":0.008,"Tests\\Feature\\ProductCostPriceTest::test_create_percent_row_without_percent_fails":0.007,"Tests\\Feature\\ProductCostPriceTest::test_update_switches_fixed_to_percent":0.01,"Tests\\Feature\\ProductCostPriceTest::test_batch_price_cost_only_notifies_percent_level_stores":0.01,"Tests\\Feature\\ProductCostPriceTest::test_batch_price_percent_row_update":0.009,"Tests\\Feature\\ProductCostPriceTest::test_batch_price_mixed_row_updates_cost_and_level":0.008,"Tests\\Feature\\ProductCostPriceTest::test_batch_price_empty_row_rejected":0.007,"Tests\\Feature\\ProductCostPriceTest::test_price_matrix_includes_cost_and_percent_columns":0.007,"Tests\\Feature\\ProductCostPriceTest::test_mini_order_uses_actual_price_for_percent_row":0.007,"Tests\\Feature\\ProductCostPriceTest::test_mini_cart_uses_actual_price_for_percent_row":0.01,"Tests\\Feature\\PurchaseGenerateTest::test_generate_uses_min_actual_price_across_types":0.014,"Tests\\Feature\\ProductPriceTest::test_store_without_level_sees_products_without_price":0.045,"Tests\\Feature\\ProductPriceTest::test_unbound_user_can_browse_products_without_price_but_cannot_cart":0.009,"Tests\\Feature\\MiniAuthTest::test_login_issues_token_for_registered_user":0.01,"Tests\\Feature\\MiniAuthTest::test_login_rejects_unregistered_openid":0.002,"Tests\\Feature\\MiniAuthTest::test_register_binds_store_and_issues_token":0.009,"Tests\\Feature\\MiniAuthTest::test_register_rejects_unknown_store":0.002,"Tests\\Feature\\MiniAuthTest::test_register_rejects_duplicate_openid":0.003,"Tests\\Feature\\MiniStoreTest::test_info_returns_bound_store":0.005,"Tests\\Feature\\MiniStoreTest::test_update_info":0.059,"Tests\\Feature\\MiniStoreTest::test_update_info_ignores_readonly_fields":0.049,"Tests\\Feature\\MiniStoreTest::test_requires_bound_store":0.007,"Tests\\Feature\\MiniStoreTest::test_update_info_validates_length":0.063,"Tests\\Feature\\StoreOrderTest::test_container_update_recalculates_amounts":0.012,"Tests\\Feature\\StoreOrderTest::test_container_update_allowed_in_editable_statuses":0.022,"Tests\\Feature\\StoreOrderTest::test_container_update_rejected_in_other_statuses":0.019,"Tests\\Feature\\StoreOrderTest::test_container_update_heals_legacy_product_amount":0.02,"Tests\\Feature\\StoreOrderTest::test_container_update_validates_negative_numbers":0.014,"Tests\\Feature\\PurchaseGenerateTest::test_generate_without_accepted_orders_fails":0.068,"Tests\\Feature\\PurchaseGenerateTest::test_generate_with_selected_order_ids":0.128,"Tests\\Feature\\StoreOrderItemTest::test_place_order_snapshots_full_product_info":0.054,"Tests\\Feature\\StoreOrderItemTest::test_mini_detail_hides_cost_price":0.074,"Tests\\Feature\\StoreOrderItemTest::test_admin_detail_shows_cost_price_and_supplier":0.055,"Tests\\Feature\\StoreOrderItemTest::test_update_item_recalculates_order_totals":0.011,"Tests\\Feature\\StoreOrderItemTest::test_update_item_rejected_when_completed_or_cancelled":0.011,"Tests\\Feature\\StoreOrderItemTest::test_update_item_validates_negative_price":0.012,"Tests\\Feature\\StoreOrderItemTest::test_sync_item_updates_snapshot_from_product":0.013,"Tests\\Feature\\StoreOrderItemTest::test_sync_item_recalculates_percent_price_with_latest_cost":0.01,"Tests\\Feature\\StoreOrderItemTest::test_sync_item_rejected_when_product_deleted":0.01,"Tests\\Feature\\StoreOrderItemTest::test_order_list_search_by_product_name":0.088,"Tests\\Feature\\StoreOrderTest::test_soft_delete_only_cancelled_orders":0.069,"Tests\\Feature\\StoreOrderTest::test_soft_delete_rejected_for_non_cancelled_orders":0.368,"Tests\\Feature\\StoreOrderTest::test_soft_delete_twice_rejected":0.06,"Tests\\Feature\\PurchaseEditTest::test_detail_returns_store_matrix":0.139,"Tests\\Feature\\PurchaseEditTest::test_update_cell_syncs_order_item_and_totals":0.125,"Tests\\Feature\\PurchaseEditTest::test_update_row_cost_syncs_all_order_items":0.113,"Tests\\Feature\\PurchaseEditTest::test_update_row_weight_distributed_by_quantity":0.032,"Tests\\Feature\\PurchaseEditTest::test_update_row_requires_at_least_one_field":0.114,"Tests\\Feature\\PurchaseEditTest::test_update_cell_rejects_negative_quantity":0.182,"Tests\\Feature\\PurchaseGenerateTest::test_generate_aggregates_order_items_into_header":0.144,"Tests\\Feature\\ReconciliationTest::test_build_excludes_unfinished_orders":0.016,"Tests\\Feature\\PurchaseEditTest::test_update_row_syncs_product_attributes":0.13,"Tests\\Feature\\PurchaseEditTest::test_update_row_syncs_to_product_catalog":0.019,"Tests\\Feature\\PurchaseEditTest::test_cell_detail_returns_items_with_order_info":0.122,"Tests\\Feature\\PurchaseEditTest::test_cell_detail_rejects_missing_params":0.121,"Tests\\Feature\\PurchaseEditTest::test_update_cell_weight_cascades_totals":0.13,"Tests\\Feature\\PurchaseEditTest::test_cell_sync_refreshes_snapshot_and_cascades":0.012,"Tests\\Feature\\PurchaseEditTest::test_edits_rejected_when_purchase_completed":0.146,"Tests\\Feature\\PurchaseEditTest::test_update_row_does_not_touch_manual_weight":0.127,"Tests\\Feature\\PurchaseEditTest::test_store_summary_returns_aggregated_items":0.113,"Tests\\Feature\\PurchaseEditTest::test_store_summary_aggregates_multiple_orders_with_weighted_price":0.087,"Tests\\Feature\\PurchaseEditTest::test_store_summary_rejects_missing_or_invalid_params":0.134,"Tests\\Feature\\ExportTest::test_export_invalid_type_rejected":0.016,"Tests\\Feature\\ExportTest::test_export_missing_purchase_rejected":0.006,"Tests\\Feature\\PurchaseEditTest::test_detail_returns_container_summary":0.022,"Tests\\Feature\\PurchaseEditTest::test_update_container_syncs_order_amounts":0.018,"Tests\\Feature\\PurchaseEditTest::test_update_container_rejected_when_purchase_completed":0.018,"Tests\\Feature\\PurchaseEditTest::test_update_container_rejects_incomplete_or_foreign_orders":0.026,"Tests\\Feature\\PurchaseEditTest::test_update_container_validates_negative_and_duplicate_orders":0.018,"Tests\\Feature\\ProductCategoryTest::test_create_child_category_under_top_level":0.009,"Tests\\Feature\\ProductCategoryTest::test_create_category_under_second_level_rejected":0.006,"Tests\\Feature\\ProductCategoryTest::test_update_second_level_move_to_another_top":0.009,"Tests\\Feature\\ProductCategoryTest::test_update_parent_with_children_cannot_move_under_category":0.009,"Tests\\Feature\\ProductCategoryTest::test_update_with_missing_parent_rejected":0.006,"Tests\\Feature\\ProductCategoryTest::test_create_product_with_parent_category_rejected":0.007,"Tests\\Feature\\ProductCategoryTest::test_create_product_with_leaf_category_ok":0.007,"Tests\\Feature\\ProductCategoryTest::test_mini_categories_returns_all_enabled_categories":0.053,"Tests\\Feature\\MiniBillTest::test_bill_list_derives_pay_state_and_settlement_date":0.062,"Tests\\Feature\\MiniBillTest::test_bill_list_summary_aggregates_unpaid":0.077,"Tests\\Feature\\MiniBillTest::test_bill_list_payable_filter":0.052,"Tests\\Feature\\MiniBillTest::test_bill_list_filters_by_bill_date_range":0.061,"Tests\\Feature\\MiniBillTest::test_bill_data_isolated_between_stores":0.102,"Tests\\Feature\\MiniBillTest::test_bill_detail_outputs_bill_items_and_orders":0.062,"Tests\\Feature\\StoreOrderTest::test_order_list_outputs_status_name_and_item_preview":0.069,"Tests\\Feature\\StoreOrderTest::test_order_list_filters_by_order_date_range":0.07,"Tests\\Feature\\StoreOrderTest::test_order_list_rejects_invalid_filters":0.08,"Tests\\Feature\\BillExportTest::test_export_merges_items_across_bills":0.101,"Tests\\Feature\\BillExportTest::test_export_filters_by_root_category":0.051,"Tests\\Feature\\BillExportTest::test_export_empty_category_rejected":0.069,"Tests\\Feature\\BillExportTest::test_export_requires_export_permission":0.048,"Tests\\Feature\\BillExportTest::test_export_invalid_ids_rejected":0.009,"Tests\\Feature\\DebugPermissionTest::test_debug_permission":0.098,"Tests\\Feature\\BillExportTest::test_mini_export_scoped_to_bound_store":0.014,"Tests\\Feature\\BillExportTest::test_mini_export_requires_store_binding":0.005,"Tests\\Feature\\MiniNoticeTest::test_notice_list_includes_personal_and_broadcast":0.052,"Tests\\Feature\\MiniNoticeTest::test_broadcast_read_creates_personal_copy":0.063,"Tests\\Feature\\MiniNoticeTest::test_personal_notice_read_marks_directly":0.048,"Tests\\Feature\\MiniNoticeTest::test_other_users_notice_not_found":0.007,"Tests\\Feature\\StoreOrderTest::test_manual_status_flow_only_accept_and_cancel":0.131,"Tests\\Feature\\StoreOrderTest::test_manual_status_flow_rejects_delivery_and_completion":0.175,"Tests\\Feature\\StoreOrderTest::test_batch_status_only_accept_and_cancel":0.143,"Tests\\Feature\\BillPaymentTest::test_order_status_progresses_via_business_chain":0.059,"Tests\\Feature\\BillPaymentTest::test_manual_pay_accumulates_store_total_purchase_amount":0.067,"Tests\\Feature\\BillPaymentTest::test_payment_audit_pass_accumulates_store_total_purchase_amount":0.051,"Tests\\Feature\\BillPaymentTest::test_payment_audit_reject_does_not_accumulate":0.048,"Tests\\Feature\\MiniProductTest::test_product_detail_guest_sees_null_price":0.049,"Tests\\Feature\\MiniProductTest::test_product_detail_store_user_sees_level_price":0.006,"Tests\\Feature\\MiniProductTest::test_product_detail_percent_price_calculated_from_cost":0.056,"Tests\\Feature\\MiniProductTest::test_product_detail_off_shelf_or_missing_rejected":0.049,"Tests\\Feature\\MiniProductTest::test_product_list_store_user_sees_level_price":0.005,"Tests\\Feature\\MiniProductTest::test_product_list_guest_sees_null_price":0.055,"Tests\\Feature\\BillPaymentTest::test_admin_order_list_and_detail_show_bill_info":0.07,"Tests\\Feature\\BillPaymentTest::test_payment_detail_returns_voucher_urls":0.055,"Tests\\Feature\\StoreOrderTest::test_order_list_preview_includes_image_spec_and_unit":0.062,"Tests\\Feature\\ContainerReturnTest::test_press_containers_adds_amount_and_writes_record":0.069,"Tests\\Feature\\ContainerReturnTest::test_return_containers_deducts_amount":0.061,"Tests\\Feature\\ContainerReturnTest::test_mixed_sign_containers":0.074,"Tests\\Feature\\ContainerReturnTest::test_zero_containers_writes_no_record":0.08,"Tests\\Feature\\ContainerReturnTest::test_non_integer_containers_rejected":0.047,"Tests\\Feature\\ContainerReturnTest::test_manual_create_and_delete_routes_removed":0.008,"Tests\\Feature\\ContainerReturnTest::test_export_matrix_merges_same_day_and_zero_fills":0.092,"Tests\\Feature\\ContainerReturnTest::test_export_filters_by_selected_stores":0.163,"Tests\\Feature\\ContainerReturnTest::test_export_invalid_params_rejected":0.016,"Tests\\Feature\\ContainerReturnTest::test_export_empty_range_rejected":0.007,"Tests\\Feature\\ContainerReturnTest::test_export_requires_export_permission":0.008,"Tests\\Feature\\DashboardTest::test_guest_cannot_access_dashboard":0.004,"Tests\\Feature\\DashboardTest::test_requires_dashboard_analysis_ability":0.006,"Tests\\Feature\\DashboardTest::test_empty_database_returns_zero_structure":0.013,"Tests\\Feature\\DashboardTest::test_overview_excludes_cancelled_orders":0.058,"Tests\\Feature\\DashboardTest::test_week_growth_rate":0.07,"Tests\\Feature\\DashboardTest::test_trend_covers_30_days_with_zero_filling":0.063,"Tests\\Feature\\DashboardTest::test_category_sales_grouped_by_category":0.067,"Tests\\Feature\\DashboardTest::test_top_products_aggregated_across_orders":0.079,"Tests\\Feature\\DashboardTest::test_top_stores_ranked_by_amount":0.126,"Tests\\Feature\\DashboardTest::test_purchase_amount_prefers_actual_over_estimate":0.01,"Tests\\Feature\\DashboardTest::test_recon_summary_and_receivable":0.064,"Tests\\Feature\\DashboardTest::test_latest_orders_limited_and_ordered":0.059,"Tests\\Feature\\DashboardTest::test_archives_count_active_records_only":0.229,"Tests\\Feature\\DashboardDebugTest::test_debug_ability_check":0.044,"Tests\\Feature\\DashboardDebugTest::test_debug":0.045,"Tests\\Feature\\ProductPriceTest::test_batch_price_updates_cost_and_notifies_all_stores":0.108,"Tests\\Feature\\ProductPriceTest::test_level_percent_update_notifies_level_stores":0.101,"Tests\\Feature\\ProductPriceTest::test_level_update_without_percent_change_sends_no_notice":0.058,"Tests\\Feature\\ProductPriceTest::test_level_percent_validation":0.017,"Tests\\Feature\\ProductCostPriceTest::test_calc_level_price":0.001,"Tests\\Feature\\ProductCostPriceTest::test_calc_level_price_edge_cases":0.001,"Tests\\Feature\\ProductCostPriceTest::test_admin_list_contains_cost_price_and_level_prices":0.026,"Tests\\Feature\\ProductCostPriceTest::test_create_product_with_cost_price":0.007,"Tests\\Feature\\ProductCostPriceTest::test_batch_price_cost_row_update":0.063,"Tests\\Feature\\ProductCostPriceTest::test_price_matrix_includes_cost_and_level_price_columns":0.007,"Tests\\Feature\\ProductCostPriceTest::test_mini_order_uses_level_price":0.048,"Tests\\Feature\\ProductCostPriceTest::test_mini_cart_uses_level_price":0.057,"Tests\\Feature\\CartTest::test_store_level_missing_rejected":0.006,"Tests\\Feature\\StoreOrderTest::test_store_level_missing_rejected":0.065,"Tests\\Feature\\PurchaseEditTest::test_update_row_skips_catalog_sync_when_product_deleted":0.14,"Tests\\Feature\\PurchaseEditTest::test_store_item_add_creates_item_and_recalculates":0.12,"Tests\\Feature\\PurchaseEditTest::test_store_item_update_merges_multi_order_rows":0.062,"Tests\\Feature\\PurchaseEditTest::test_store_item_remove_deletes_and_recalculates":0.121,"Tests\\Feature\\StoreOrderItemTest::test_update_store_item_recalculates_totals":0.071,"Tests\\Feature\\StoreOrderItemTest::test_store_item_edits_rejected_when_purchase_completed":0.063,"Tests\\Feature\\StoreOrderItemTest::test_update_store_item_validates_negative_values":0.057,"Tests\\Feature\\StoreOrderItemTest::test_add_store_item_uses_level_percent_price":0.066,"Tests\\Feature\\StoreOrderItemTest::test_add_store_item_rejected_when_product_deleted":0.056,"Tests\\Feature\\ExportTest::test_export_lists_all_catalog_products_with_totals_row":0.118,"Tests\\Feature\\ExportTest::test_export_supplier_filter":0.119,"Tests\\Feature\\ExportTest::test_export_stores_multi_sheet":0.132,"Tests\\Feature\\ExportTest::test_export_stores_single_store":0.117,"Tests\\Feature\\ExportTest::test_export_stores_without_items_rejected":0.192,"Tests\\Feature\\ExportTest::test_export_suppliers_multi_sheet":0.133,"Tests\\Feature\\ExportTest::test_export_suppliers_single":0.122,"Tests\\Feature\\ExportTest::test_export_suppliers_without_items_rejected":0.136,"Tests\\Feature\\PurchaseEditTest::test_update_row_cost_change_reprices_items_by_store_level":0.137,"Tests\\Feature\\PurchaseEditTest::test_update_row_without_cost_change_keeps_item_price":0.11,"Tests\\Feature\\CartTest::test_update_other_stores_item_rejected":0.089,"Tests\\Feature\\CartTest::test_delete_other_stores_item_rejected":0.093,"Tests\\Feature\\CartTest::test_cart_isolated_between_stores":0.099,"Tests\\Feature\\MiniAuthTest::test_login_issues_token_for_valid_credentials":0.164,"Tests\\Feature\\MiniAuthTest::test_login_rejects_wrong_password":0.154,"Tests\\Feature\\MiniAuthTest::test_login_rejects_unknown_username":0.002,"Tests\\Feature\\MiniAuthTest::test_disabled_store_cannot_login":0.136,"Tests\\Feature\\MiniAuthTest::test_login_validates_required_params":0.004,"Tests\\Feature\\MiniAuthTest::test_login_token_accesses_protected_routes":0.137,"Tests\\Feature\\MiniAuthTest::test_set_password_succeeds":0.243,"Tests\\Feature\\MiniAuthTest::test_set_password_rejects_wrong_old_password":0.187,"Tests\\Feature\\MiniAuthTest::test_set_password_validates_confirmation":0.051,"Tests\\Feature\\BillExportTest::test_mini_export_scoped_to_own_store":0.094,"Tests\\Feature\\MiniNoticeTest::test_other_stores_notice_not_found":0.098,"Tests\\Feature\\MiniProductTest::test_product_detail_store_sees_level_price":0.051,"Tests\\Feature\\MiniProductTest::test_product_list_store_sees_level_price":0.049,"Tests\\Feature\\MiniStoreTest::test_info_returns_current_store":0.054,"Tests\\Feature\\MiniStoreTest::test_disabled_store_rejected":0.053,"Tests\\Feature\\ProductImportExportTest::test_export_downloads_all_products":0.008,"Tests\\Feature\\ProductImportExportTest::test_export_filters_by_category":0.015,"Tests\\Feature\\ProductImportExportTest::test_export_template_mode":0.006,"Tests\\Feature\\ProductImportExportTest::test_export_requires_permission":0.006,"Tests\\Feature\\ProductImportExportTest::test_import_creates_products_and_auto_supplier":0.029,"Tests\\Feature\\ProductImportExportTest::test_import_matches_existing_supplier":0.016,"Tests\\Feature\\ProductImportExportTest::test_import_errors_abort_all_rows":0.016,"Tests\\Feature\\ProductImportExportTest::test_import_category_path_resolution":0.034,"Tests\\Feature\\ProductImportExportTest::test_import_rejects_invalid_header":0.026,"Tests\\Feature\\ProductImportExportTest::test_import_requires_permission":0.014,"Tests\\Feature\\ProductImportExportTest::test_list_filters_by_market":0.008,"Tests\\Feature\\MiniReportTest::test_report_aggregates_total_and_item_percent":0.054,"Tests\\Feature\\MiniReportTest::test_report_accumulates_same_product_across_orders":0.066,"Tests\\Feature\\MiniReportTest::test_report_filters_by_custom_date_range":0.068,"Tests\\Feature\\MiniReportTest::test_report_excludes_cancelled_and_deleted_orders":0.05,"Tests\\Feature\\MiniReportTest::test_report_preset_month_and_last_month":0.058,"Tests\\Feature\\MiniReportTest::test_report_preset_week_and_last_week":0.071,"Tests\\Feature\\MiniReportTest::test_report_defaults_to_current_month":0.066,"Tests\\Feature\\MiniReportTest::test_report_data_isolated_between_stores":0.096,"Tests\\Feature\\MiniReportTest::test_report_empty_period_returns_zero":0.06,"Tests\\Feature\\MiniReportTest::test_report_validates_params":0.07,"Tests\\Feature\\ProductBatchDeleteTest::test_batch_delete_soft_deletes_selected_products":0.007,"Tests\\Feature\\ProductBatchDeleteTest::test_batch_delete_requires_non_empty_ids":0.006,"Tests\\Feature\\ProductBatchDeleteTest::test_batch_delete_forbidden_without_permission":0.013,"Tests\\Feature\\CartTest::test_place_order_clears_ordered_items_from_cart":0.06,"Tests\\Feature\\StoreOrderItemTest::test_admin_order_list_filters_by_order_date_range":0.079,"Tests\\Feature\\StoreOrderItemTest::test_admin_order_list_search_by_purchase_no":0.07,"Tests\\Feature\\ExportTest::test_export_suppliers_split_by_market":0.068,"Tests\\Feature\\BillPaymentTest::test_generate_bill_saves_after_sale_and_remark":0.116,"Tests\\Feature\\ProductCostPriceTest::test_product_price_unit_default_and_editable":0.012,"Tests\\Feature\\StoreOrderTest::test_place_order_blocked_by_overdue_unpaid_bill":0.079,"Tests\\Feature\\StoreOrderTest::test_place_order_allowed_within_payment_cycle":0.07,"Tests\\Feature\\CartTest::test_cart_summary_endpoint":0.053,"Tests\\Feature\\CartTest::test_cart_summary_requires_login":0.002,"Tests\\Feature\\MiniProductTest::test_product_list_appends_cart_quantity_and_summary":0.07,"Tests\\Feature\\MiniProductTest::test_product_list_guest_sees_zero_cart_fields":0.053,"Tests\\Feature\\MiniProductTest::test_product_detail_appends_cart_quantity":0.06,"Tests\\Feature\\MiniProductTest::test_home_appends_cart_summary":0.056,"Tests\\Feature\\MiniOnlinePaymentTest::test_sign_matches_document_example":0.002,"Tests\\Feature\\MiniOnlinePaymentTest::test_create_online_payment_success":0.072,"Tests\\Feature\\MiniOnlinePaymentTest::test_create_online_payment_validation":0.105,"Tests\\Feature\\MiniOnlinePaymentTest::test_create_fails_when_code2session_fails":0.056,"Tests\\Feature\\MiniOnlinePaymentTest::test_create_gateway_failure_releases_bills":0.053,"Tests\\Feature\\MiniOnlinePaymentTest::test_notify_settles_payment":0.079,"Tests\\Feature\\MiniOnlinePaymentTest::test_notify_rejects_invalid_messages":0.078,"Tests\\Feature\\MiniOnlinePaymentTest::test_query_syncs_gateway_status":0.069,"Tests\\Feature\\MiniOnlinePaymentTest::test_query_rejects_other_store_payment":0.11,"Tests\\Feature\\MiniOnlinePaymentTest::test_offline_payment_flow_unaffected":0.046,"Tests\\Feature\\BillPaymentTest::test_generate_bill_positive_after_sale_added_to_total":0.116,"Tests\\Feature\\BillPaymentTest::test_generate_bill_rejects_non_numeric_after_sale":0.048,"Tests\\Feature\\ClientSpecialTest::test_batch_add_creates_specials":0.018,"Tests\\Feature\\ClientSpecialTest::test_batch_add_skips_existing_products":0.007,"Tests\\Feature\\ClientSpecialTest::test_batch_add_requires_non_empty_product_ids":0.006,"Tests\\Feature\\ClientSpecialTest::test_batch_add_forbidden_without_permission":0.007,"Tests\\Feature\\ClientSpecialTest::test_query_filters_by_product_name_keyword":0.007,"Tests\\Feature\\ClientSpecialTest::test_update_changes_sort_and_status":0.007,"Tests\\Feature\\ClientSpecialTest::test_delete_and_batch_delete":0.008,"Tests\\Feature\\ClientSpecialTest::test_batch_delete_requires_non_empty_ids":0.011,"Tests\\Feature\\MiniSpecialTest::test_guest_sees_specials_with_null_price":0.044,"Tests\\Feature\\MiniSpecialTest::test_store_sees_level_price":0.066,"Tests\\Feature\\MiniSpecialTest::test_disabled_special_and_off_shelf_product_excluded":0.049,"Tests\\Feature\\MiniSpecialTest::test_specials_sorted_by_sort_asc":0.086,"Tests\\Feature\\MiniSpecialTest::test_cart_quantity_attached_for_store":0.045,"Tests\\Feature\\MiniOnlinePaymentTest::test_aes_encryption_matches_demo_golden":0.001,"Tests\\Feature\\MiniOnlinePaymentTest::test_notify_envelope_round_trip":0.004,"Tests\\Feature\\MiniOnlinePaymentTest::test_swapped_key_config_fails_with_clear_message":0.006,"Tests\\Feature\\MiniWechatPaymentTest::test_create_wechat_payment_success":0.065,"Tests\\Feature\\MiniWechatPaymentTest::test_create_wechat_payment_gateway_failure_releases_bills":0.058,"Tests\\Feature\\MiniWechatPaymentTest::test_wechat_notify_settles_payment":0.073,"Tests\\Feature\\MiniWechatPaymentTest::test_wechat_notify_rejects_invalid_messages":0.057,"Tests\\Feature\\MiniWechatPaymentTest::test_wechat_notify_with_platform_cert_validates_signature":0.06,"Tests\\Feature\\MiniWechatPaymentTest::test_wechat_query_syncs_status":0.073,"Tests\\Feature\\MiniWechatPaymentTest::test_wangpu_channel_remains_default":0.074,"Tests\\Feature\\BillPaymentTest::test_payment_config_returns_pay_methods":0.047,"Tests\\Feature\\MiniMpPaymentTest::test_mp_scene_wechat_channel":0.108,"Tests\\Feature\\MiniMpPaymentTest::test_mp_scene_wangpu_channel":0.07,"Tests\\Feature\\MiniMpPaymentTest::test_mp_scene_oauth_failure":0.053,"Tests\\Feature\\MiniMpPaymentTest::test_mp_scene_requires_mp_config":0.051,"Tests\\Feature\\MiniMpPaymentTest::test_default_scene_remains_mini":0.063,"Tests\\Feature\\MiniProductTest::test_product_list_ordered_by_category_tree":0.006,"Tests\\Feature\\MiniOnlinePaymentTest::test_notify_sign_matches_doc_golden":0.003,"Tests\\Feature\\MiniOnlinePaymentTest::test_notify_verify_rejects_bad_sign":0.002,"Tests\\Feature\\ExportTest::test_export_hides_detail_columns_by_default":0.121,"Tests\\Feature\\ExportTest::test_export_scope_includes_off_shelf_but_not_deleted":0.148,"Tests\\Unit\\WeightEstimatorTest::test_weight_unit_quantity_is_weight":0.001,"Tests\\Unit\\WeightEstimatorTest::test_spec_per_unit_weight":0.001,"Tests\\Unit\\WeightEstimatorTest::test_unparsable_spec_fallback":0,"Tests\\Feature\\StoreOrderTest::test_place_order_prefills_reference_weight":0.059,"Tests\\Feature\\PurchaseGenerateTest::test_generate_sums_reference_weight":0.061,"Tests\\Feature\\PurchaseEditTest::test_store_item_add_defaults_reference_weight":0.116,"Tests\\Feature\\StoreOrderTest::test_place_order_blocked_outside_order_time_window":0.075,"Tests\\Feature\\StoreOrderTest::test_place_order_cross_midnight_time_window":0.091,"Tests\\Feature\\StoreOrderTest::test_place_order_only_cutoff_time_and_invalid_config":0.066,"Tests\\Feature\\PurchaseEditTest::test_item_check_toggle_persists_and_echoes_in_detail":0.152,"Tests\\Feature\\PurchaseEditTest::test_item_check_allowed_when_purchase_completed":0.118,"Tests\\Feature\\PurchaseEditTest::test_item_check_toggle_validates_purchase_and_product":0.113,"Tests\\Feature\\PurchaseEditTest::test_item_check_batch_marks_and_clears":0.13,"Tests\\Feature\\PurchaseEditTest::test_item_check_batch_validates_and_ignores_unknown_products":0.142}} \ No newline at end of file diff --git a/app/Exports/PurchaseSupplierSheet.php b/app/Exports/PurchaseSupplierSheet.php index c0a2538..5334731 100644 --- a/app/Exports/PurchaseSupplierSheet.php +++ b/app/Exports/PurchaseSupplierSheet.php @@ -61,7 +61,7 @@ class PurchaseSupplierSheet implements FromCollection, WithStrictNullComparison, } /** - * 导出行:标题/空行/列头(品名、汇总、市场、各门店)/明细 + * 导出行:标题+备注(合并区)/空行(合并区)/列头(品名、汇总、市场、各门店)/明细 */ public function collection(): Collection { @@ -74,11 +74,16 @@ class PurchaseSupplierSheet implements FromCollection, WithStrictNullComparison, $rows = []; $rowIndex = 0; - // 标题行 - $rows[] = [$this->supplier->name . ' · ' . $this->marketLabel . ' · 采购单 ' . $this->purchase->purchase_no]; + // 标题行(A1:C2 合并占两行),D1 起放采购单备注(D1:I2 合并,红色 22 号字) + $rows[] = [ + $this->supplier->name . ' · ' . $this->marketLabel . ' · 采购单 ' . $this->purchase->purchase_no, + '', + '', + trim((string) $this->purchase->remark), + ]; $this->specialRows[++$rowIndex] = 'title'; - // 空行 + // 空行(被标题/备注合并区域覆盖) $rows[] = ['']; $rowIndex++; @@ -117,7 +122,7 @@ class PurchaseSupplierSheet implements FromCollection, WithStrictNullComparison, } /** - * 标题/列头加粗;全表居中 + 全边框; + * 标题合并 A1:C2、备注合并 D1:I2(红色 22 号字);标题/列头加粗;全表居中 + 全边框 + 列头行自动换行; * 汇总列(浅黄)/门店数量列(浅蓝)按值条件填色(0 不填、列头固定填色),冻结列头 */ public function styles(Worksheet $sheet): array @@ -126,14 +131,18 @@ class PurchaseSupplierSheet implements FromCollection, WithStrictNullComparison, $sheet->freezePane('A' . ($this->headerRow + 1)); - $widths = [24, 10, 12]; + // 标题合并前三列占两行(A1:C2),备注合并六列两行(D1:I2) + $sheet->mergeCells('A1:C2'); + $sheet->mergeCells('D1:I2'); + + $widths = [24, 10, 6]; foreach ($widths as $index => $width) { $sheet->getColumnDimension(Coordinate::stringFromColumnIndex($index + 1))->setWidth($width); } $storeIds = array_map('intval', array_keys($this->stores)); $lastColumn = Coordinate::stringFromColumnIndex(3 + max(count($storeIds), 1)); foreach ($storeIds as $i => $storeId) { - $sheet->getColumnDimension(Coordinate::stringFromColumnIndex(4 + $i))->setWidth(12); + $sheet->getColumnDimension(Coordinate::stringFromColumnIndex(4 + $i))->setWidth(6); } // 全部单元格水平/垂直居中 @@ -142,6 +151,14 @@ class PurchaseSupplierSheet implements FromCollection, WithStrictNullComparison, ->setHorizontal(Alignment::HORIZONTAL_CENTER) ->setVertical(Alignment::VERTICAL_CENTER); + // 列头行自动换行(门店列窄,门店名换行显示) + $sheet->getStyle('A' . $this->headerRow . ':' . $lastColumn . $this->headerRow) + ->getAlignment() + ->setWrapText(true); + + // 备注合并单元格自动换行(长备注多行显示) + $sheet->getStyle('D1')->getAlignment()->setWrapText(true); + // 表格区域(列头 → 数据末行)添加所有边框 $sheet->getStyle('A' . $this->headerRow . ':' . $lastColumn . $this->lastRow) ->getBorders() @@ -179,6 +196,9 @@ class PurchaseSupplierSheet implements FromCollection, WithStrictNullComparison, } } + // 备注单元格(D1):红色 22 号字,须放在行样式之后应用以覆盖标题行字号 + $styles['D1'] = ['font' => ['bold' => true, 'size' => 22, 'color' => ['argb' => 'FFFF0000']]]; + return $styles; } diff --git a/app/Http/Controllers/Purchase/PurchaseOrderController.php b/app/Http/Controllers/Purchase/PurchaseOrderController.php index 9dd61b4..f71378f 100644 --- a/app/Http/Controllers/Purchase/PurchaseOrderController.php +++ b/app/Http/Controllers/Purchase/PurchaseOrderController.php @@ -13,6 +13,7 @@ use App\Http\Requests\Purchase\PurchaseStoreItemRequest; use App\Models\BillModel; use App\Models\CustomerLevelModel; use App\Models\ProductModel; +use App\Models\PurchaseItemCheckModel; use App\Models\PurchaseOrderModel; use App\Models\StoreModel; use App\Models\StoreOrderItemModel; @@ -173,6 +174,12 @@ class PurchaseOrderController extends BaseController return $row; }, $rows), 'bills' => $bills, + // 单品「已对账」标记(入库持久化,商品ID列表) + 'checked_product_ids' => PurchaseItemCheckModel::query() + ->where('purchase_id', $purchase->id) + ->pluck('product_id') + ->map(static fn ($v) => (int) $v) + ->all(), ]); } @@ -795,6 +802,103 @@ class PurchaseOrderController extends BaseController }); } + /** + * 切换单品「已对账」标记(入库持久化:未标记→标记,已标记→取消;与采购单状态无关,对账期间可反复勾选) + */ + #[PutRoute(route: '/{id}/check/{productId}', authorize: 'query', where: ['id' => '[0-9]+', 'productId' => '[0-9]+'])] + public function toggleItemCheck(int $id, int $productId, Request $request): JsonResponse + { + $purchase = PurchaseOrderModel::find($id); + if (empty($purchase)) { + throw new RepositoryException('采购单不存在'); + } + $hasItem = StoreOrderItemModel::query() + ->where('purchase_id', $purchase->id) + ->where('product_id', $productId) + ->exists(); + if (! $hasItem) { + throw new RepositoryException('该采购单下无此商品的订货明细'); + } + + $marked = PurchaseItemCheckModel::query() + ->where('purchase_id', $purchase->id) + ->where('product_id', $productId) + ->first(); + if ($marked !== null) { + $marked->delete(); + return $this->success(['checked' => false], '已取消对账标记'); + } + + PurchaseItemCheckModel::create([ + 'purchase_id' => $purchase->id, + 'product_id' => $productId, + 'operator_id' => (int) $request->user()->id, + ]); + return $this->success(['checked' => true], '已标记为已对账'); + } + + /** + * 批量设置单品「已对账」标记(全选用:checked=true 批量标记 / false 批量取消; + * 仅处理本采购单内有订货明细的商品,重复标记幂等;与采购单状态无关) + */ + #[PutRoute(route: '/{id}/check', authorize: 'query', where: ['id' => '[0-9]+'])] + public function batchItemCheck(int $id, Request $request): JsonResponse + { + $data = $request->validate([ + 'product_ids' => 'required|array|min:1', + 'product_ids.*' => 'integer', + 'checked' => 'required|boolean', + ], [ + 'product_ids.required' => '请选择商品', + 'product_ids.min' => '请选择商品', + ]); + + $purchase = PurchaseOrderModel::find($id); + if (empty($purchase)) { + throw new RepositoryException('采购单不存在'); + } + + // 仅处理本采购单内有订货明细的商品(无效商品静默忽略) + $productIds = StoreOrderItemModel::query() + ->where('purchase_id', $purchase->id) + ->whereIn('product_id', array_map('intval', $data['product_ids'])) + ->distinct() + ->pluck('product_id') + ->map(static fn ($v) => (int) $v); + + if (! (bool) $data['checked']) { + PurchaseItemCheckModel::query() + ->where('purchase_id', $purchase->id) + ->whereIn('product_id', $productIds) + ->delete(); + return $this->success(['count' => $productIds->count()], '已取消对账标记'); + } + + // 批量标记:跳过已标记行,仅补插缺失行(幂等) + $marked = PurchaseItemCheckModel::query() + ->where('purchase_id', $purchase->id) + ->whereIn('product_id', $productIds) + ->pluck('product_id') + ->map(static fn ($v) => (int) $v); + $operatorId = (int) $request->user()->id; + $now = now(); + $rows = $productIds->diff($marked) + ->map(static fn (int $productId) => [ + 'purchase_id' => $purchase->id, + 'product_id' => $productId, + 'operator_id' => $operatorId, + 'created_at' => $now, + 'updated_at' => $now, + ]) + ->values() + ->all(); + if ($rows !== []) { + PurchaseItemCheckModel::insert($rows); + } + + return $this->success(['count' => $productIds->count()], '已标记为已对账'); + } + /** * 采购单编辑闸:仅进行中(待采购)允许修改明细 */ diff --git a/app/Models/PurchaseItemCheckModel.php b/app/Models/PurchaseItemCheckModel.php new file mode 100644 index 0000000..dda0871 --- /dev/null +++ b/app/Models/PurchaseItemCheckModel.php @@ -0,0 +1,45 @@ + 'integer', + 'product_id' => 'integer', + 'operator_id' => 'integer', + 'created_at' => 'datetime:Y-m-d H:i:s', + ]; + + /** + * 关联采购单 + */ + public function purchase(): BelongsTo + { + return $this->belongsTo(PurchaseOrderModel::class, 'purchase_id', 'id'); + } + + /** + * 标记人(后台系统用户) + */ + public function operator(): BelongsTo + { + return $this->belongsTo(SysUserModel::class, 'operator_id', 'id'); + } +} diff --git a/database/migrations/2026_09_07_000001_create_purchase_item_check_table.php b/database/migrations/2026_09_07_000001_create_purchase_item_check_table.php new file mode 100644 index 0000000..b72cb42 --- /dev/null +++ b/database/migrations/2026_09_07_000001_create_purchase_item_check_table.php @@ -0,0 +1,35 @@ +increments('id')->comment('标记ID'); + $table->integer('purchase_id')->comment('采购单ID'); + $table->integer('product_id')->comment('商品ID'); + $table->integer('operator_id')->default(0)->comment('标记人(后台系统用户ID)'); + $table->timestamps(); + $table->unique(['purchase_id', 'product_id'], 'purchase_item_check_unique'); + $table->comment('采购单单品对账标记表(商品明细「已对账」勾选)'); + }); + } + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('purchase_item_check'); + } +}; diff --git a/tests/Feature/PurchaseEditTest.php b/tests/Feature/PurchaseEditTest.php index 4b6a9c4..d6c8947 100644 --- a/tests/Feature/PurchaseEditTest.php +++ b/tests/Feature/PurchaseEditTest.php @@ -4,6 +4,7 @@ namespace Tests\Feature; use App\Models\CustomerLevelModel; use App\Models\ProductModel; +use App\Models\PurchaseItemCheckModel; use App\Models\PurchaseOrderModel; use App\Models\StoreModel; use App\Models\StoreOrderItemModel; @@ -607,4 +608,137 @@ class PurchaseEditTest extends ProcurementTestCase $this->assertSame(2, $item->fresh()->quantity, '被拒绝后明细不变'); $this->assertSame('10.00', (string) $item->fresh()->cost_price); } + + /** 单品「已对账」标记:切换入库持久化,详情接口回显,再次切换取消 */ + public function test_item_check_toggle_persists_and_echoes_in_detail(): void + { + [$purchase, $product] = $this->buildPurchase(); + $this->actingAsSysUser(); + + // 初始无标记 + $this->getJson("/purchase/order/{$purchase->id}") + ->assertJsonPath('success', true) + ->assertJsonPath('data.checked_product_ids', []); + + // 标记 → 入库 + $this->putJson("/purchase/order/{$purchase->id}/check/{$product->id}") + ->assertJsonPath('success', true) + ->assertJsonPath('data.checked', true); + $this->assertDatabaseHas('purchase_item_check', [ + 'purchase_id' => $purchase->id, + 'product_id' => $product->id, + ]); + + // 详情回显 + $this->getJson("/purchase/order/{$purchase->id}") + ->assertJsonPath('data.checked_product_ids.0', $product->id); + + // 再次切换 → 取消标记,记录删除 + $this->putJson("/purchase/order/{$purchase->id}/check/{$product->id}") + ->assertJsonPath('success', true) + ->assertJsonPath('data.checked', false); + $this->assertDatabaseMissing('purchase_item_check', [ + 'purchase_id' => $purchase->id, + 'product_id' => $product->id, + ]); + } + + /** 对账标记与采购单状态无关:已完成采购单仍可切换标记 */ + public function test_item_check_allowed_when_purchase_completed(): void + { + [$purchase, $product] = $this->buildPurchase(); + $purchase->update(['status' => PurchaseOrderModel::STATUS_COMPLETED]); + $this->actingAsSysUser(); + + $this->putJson("/purchase/order/{$purchase->id}/check/{$product->id}") + ->assertJsonPath('success', true) + ->assertJsonPath('data.checked', true); + } + + /** 对账标记校验:采购单不存在 / 采购单内无此商品均报错,不入库 */ + public function test_item_check_toggle_validates_purchase_and_product(): void + { + [$purchase] = $this->buildPurchase(); + $this->actingAsSysUser(); + + $this->putJson('/purchase/order/99999/check/1') + ->assertJsonPath('success', false) + ->assertJsonPath('msg', '采购单不存在'); + $this->putJson("/purchase/order/{$purchase->id}/check/99999") + ->assertJsonPath('success', false) + ->assertJsonPath('msg', '该采购单下无此商品的订货明细'); + + $this->assertSame(0, PurchaseItemCheckModel::count()); + } + + /** 批量标记(全选):一次标记多个商品,重复标记幂等,批量取消仅清指定商品 */ + public function test_item_check_batch_marks_and_clears(): void + { + [$purchase, $product, $stores] = $this->buildPurchase(); + $this->actingAsSysUser(); + + // 采购单内再加一个商品(通过新增单品端点挂靠门店订单) + $product2 = ProductModel::factory()->create([ + 'status' => ProductModel::STATUS_ON, + 'cost_price' => 5, + 'spec' => '1斤', + 'unit' => '斤', + ]); + $this->postJson("/purchase/order/{$purchase->id}/store/{$stores[0]->id}/item", [ + 'product_id' => $product2->id, + 'quantity' => 1, + ])->assertJsonPath('success', true); + + // 批量标记两个商品 + $this->putJson("/purchase/order/{$purchase->id}/check", [ + 'product_ids' => [$product->id, $product2->id], + 'checked' => true, + ])->assertJsonPath('success', true) + ->assertJsonPath('data.count', 2); + $this->assertSame(2, PurchaseItemCheckModel::where('purchase_id', $purchase->id)->count()); + + // 重复批量标记幂等(不产生重复行) + $this->putJson("/purchase/order/{$purchase->id}/check", [ + 'product_ids' => [$product->id, $product2->id], + 'checked' => true, + ])->assertJsonPath('success', true); + $this->assertSame(2, PurchaseItemCheckModel::where('purchase_id', $purchase->id)->count()); + + // 详情回显两个标记 + $ids = $this->getJson("/purchase/order/{$purchase->id}")->json('data.checked_product_ids'); + $this->assertEqualsCanonicalizing([$product->id, $product2->id], $ids); + + // 批量取消其中一个 + $this->putJson("/purchase/order/{$purchase->id}/check", [ + 'product_ids' => [$product->id], + 'checked' => false, + ])->assertJsonPath('success', true); + $this->assertDatabaseMissing('purchase_item_check', [ + 'purchase_id' => $purchase->id, + 'product_id' => $product->id, + ]); + $this->assertDatabaseHas('purchase_item_check', [ + 'purchase_id' => $purchase->id, + 'product_id' => $product2->id, + ]); + } + + /** 批量标记校验:空列表/采购单不存在报错;无效商品静默忽略 */ + public function test_item_check_batch_validates_and_ignores_unknown_products(): void + { + [$purchase] = $this->buildPurchase(); + $this->actingAsSysUser(); + + $this->putJson("/purchase/order/{$purchase->id}/check", ['product_ids' => [], 'checked' => true]) + ->assertJsonPath('success', false); + $this->putJson('/purchase/order/99999/check', ['product_ids' => [1], 'checked' => true]) + ->assertJsonPath('success', false) + ->assertJsonPath('msg', '采购单不存在'); + + // 采购单内无此商品 → 静默忽略不入库 + $this->putJson("/purchase/order/{$purchase->id}/check", ['product_ids' => [99999], 'checked' => true]) + ->assertJsonPath('success', true) + ->assertJsonPath('data.count', 0); + $this->assertSame(0, PurchaseItemCheckModel::count()); + } } diff --git a/web/api/purchase/order.ts b/web/api/purchase/order.ts index 69850ac..23ccc12 100644 --- a/web/api/purchase/order.ts +++ b/web/api/purchase/order.ts @@ -73,6 +73,23 @@ export async function getPurchaseCell(purchaseId: number, productId: number, sto }); } +/** 切换单品「已对账」标记(入库持久化,再次调用取消标记) */ +export async function togglePurchaseItemCheck(purchaseId: number, productId: number) { + return createAxios<{ checked: boolean }>({ + url: `/purchase/order/${purchaseId}/check/${productId}`, + method: 'put', + }); +} + +/** 批量设置单品「已对账」标记(全选:checked=true 批量标记,false 批量取消) */ +export async function batchPurchaseItemCheck(purchaseId: number, productIds: number[], checked: boolean) { + return createAxios<{ count: number }>({ + url: `/purchase/order/${purchaseId}/check`, + method: 'put', + data: { product_ids: productIds, checked }, + }); +} + /** 门店订单明细修改(订货量、重量、单价),自动重算价格 */ export async function updatePurchaseCellItem(itemId: number, data: PurchaseCellUpdateParams) { return createAxios({ diff --git a/web/domain/iPurchaseOrder.ts b/web/domain/iPurchaseOrder.ts index 5359f32..669dae1 100644 --- a/web/domain/iPurchaseOrder.ts +++ b/web/domain/iPurchaseOrder.ts @@ -87,6 +87,8 @@ export interface IPurchaseDetail { items: IPurchaseDetailRow[]; /** 门店账单(采购单完成后按门店生成) */ bills: IBill[]; + /** 单品「已对账」标记的商品ID列表(入库持久化) */ + checked_product_ids: number[]; } /** 门店账单(采购单完成后按门店生成,商品金额为订单汇总快照不可修改) */ diff --git a/web/pages/purchase/order.tsx b/web/pages/purchase/order.tsx index ff7a0af..cd772cc 100644 --- a/web/pages/purchase/order.tsx +++ b/web/pages/purchase/order.tsx @@ -43,6 +43,7 @@ import type { import { PURCHASE_STATUS_MAP, BILL_STATUS_MAP } from '@/domain/iPurchaseOrder.ts'; import { addPurchaseStoreItem, + batchPurchaseItemCheck, exportPurchase, exportPurchaseStores, exportPurchaseSuppliers, @@ -51,6 +52,7 @@ import { getPurchaseDetail, getPurchaseStoreSummary, removePurchaseStoreItem, + togglePurchaseItemCheck, type BillGenerateStoreParams, type PurchaseRowUpdateParams, updatePurchaseRow, @@ -99,8 +101,9 @@ const PurchaseOrderPage: React.FC = () => { const [detailLoading, setDetailLoading] = useState(false); const [completing, setCompleting] = useState(false); - // 商品明细:单品「已对账」勾选标记(前端本地控制不入库,按采购单持久化到 localStorage) + // 商品明细:单品「已对账」勾选标记(入库持久化,随详情接口回显) const [checkedItems, setCheckedItems] = useState>(new Set()); + const [checkAllSaving, setCheckAllSaving] = useState(false); // 行内编辑:供应商选项(品名/供应商/包规/单位/成本与各门店订货量直接在表格中编辑) const [suppliers, setSuppliers] = useState([]); @@ -250,35 +253,85 @@ const PurchaseOrderPage: React.FC = () => { } }, [detailOpen, detailTab, storeId, detail?.purchase.id]); - // 切换采购单时从 localStorage 恢复单品「已对账」标记 + // 切换采购单时从详情接口恢复单品「已对账」标记(入库持久化) useEffect(() => { - if (!detail) { - setCheckedItems(new Set()); - return; - } - try { - const raw = localStorage.getItem(`purchase-checked-items:${detail.purchase.id}`); - setCheckedItems(new Set(raw ? (JSON.parse(raw) as number[]) : [])); - } catch { - setCheckedItems(new Set()); - } + setCheckedItems(new Set(detail?.checked_product_ids ?? [])); }, [detail]); - /** 切换单品「已对账」标记(前端本地控制,不入库) */ - const toggleItemChecked = (productId: number) => { + /** 切换单品「已对账」标记(入库持久化;乐观更新,请求失败回滚本地勾选) */ + const toggleItemChecked = async (productId: number) => { if (!detail) { return; } + const willCheck = !checkedItems.has(productId); setCheckedItems((prev) => { const next = new Set(prev); - if (next.has(productId)) { - next.delete(productId); - } else { + if (willCheck) { next.add(productId); + } else { + next.delete(productId); } - localStorage.setItem(`purchase-checked-items:${detail.purchase.id}`, JSON.stringify([...next])); return next; }); + try { + await togglePurchaseItemCheck(detail.purchase.id!, productId); + } catch { + // 错误提示由请求封装统一弹出,这里回滚本地勾选 + setCheckedItems((prev) => { + const next = new Set(prev); + if (willCheck) { + next.delete(productId); + } else { + next.add(productId); + } + return next; + }); + } + }; + + /** 商品明细当前可见行的商品ID(列筛选联动,全选仅作用于筛选后的数据) */ + const visibleProductIds = useMemo(() => summaryItems.map((row) => row.product_id), [summaryItems]); + + /** 当前可见行是否全部已标记 */ + const allVisibleChecked = + visibleProductIds.length > 0 && visibleProductIds.every((id) => checkedItems.has(id)); + + /** 全选/取消全选「已对账」标记(仅作用于列筛选后的可见行;乐观更新,请求失败回滚) */ + const toggleAllVisibleChecked = async () => { + if (!detail || visibleProductIds.length === 0 || checkAllSaving) { + return; + } + const willCheck = !allVisibleChecked; + setCheckedItems((prev) => { + const next = new Set(prev); + visibleProductIds.forEach((id) => { + if (willCheck) { + next.add(id); + } else { + next.delete(id); + } + }); + return next; + }); + setCheckAllSaving(true); + try { + await batchPurchaseItemCheck(detail.purchase.id!, visibleProductIds, willCheck); + } catch { + // 错误提示由请求封装统一弹出,这里回滚本地勾选 + setCheckedItems((prev) => { + const next = new Set(prev); + visibleProductIds.forEach((id) => { + if (willCheck) { + next.delete(id); + } else { + next.add(id); + } + }); + return next; + }); + } finally { + setCheckAllSaving(false); + } }; const loadDetail = async (id: number) => { @@ -599,7 +652,26 @@ const PurchaseOrderPage: React.FC = () => { render: (_, row) => renderEditableCell(row, 'supplier_id', row.supplier?.name ?? '-'), }, { - title: '市场', + title: ( + + 市场 + + + + 已对账 {visibleProductIds.filter((id) => checkedItems.has(id)).length}/{visibleProductIds.length} + {canUpdateRow && ( 修改保存将同步该商品全部订货明细与商品档案,成本变化时各门店单价按等级上浮自动重算