From f3e190f460fb6b1dfddcd0693fdf1684ea00bde9 Mon Sep 17 00:00:00 2001 From: liu <2302563948@qq.com> Date: Thu, 27 Aug 2026 18:13:43 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B4=AD=E7=89=A9=E8=BD=A6=E6=82=AC=E6=B5=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .phpunit.result.cache | 2 +- app/Http/Controllers/Mini/CartController.php | 10 +++ app/Http/Controllers/Mini/HomeController.php | 6 +- app/Http/Controllers/Mini/OrderController.php | 21 +++++ .../Controllers/Mini/ProductController.php | 22 ++++- app/Services/CartService.php | 83 +++++++++++++++++++ tests/Feature/CartTest.php | 37 +++++++++ tests/Feature/MiniProductTest.php | 82 ++++++++++++++++++ tests/Feature/StoreOrderTest.php | 73 ++++++++++++++++ 9 files changed, 330 insertions(+), 6 deletions(-) create mode 100644 app/Services/CartService.php diff --git a/.phpunit.result.cache b/.phpunit.result.cache index 57594d4..d8dd8e3 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},"times":{"Tests\\Feature\\ExampleTest::test_the_application_returns_a_successful_response":0.017,"Tests\\Feature\\ProductPriceTest::test_product_list_returns_price_for_store_level":0.095,"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.012,"Tests\\Feature\\StoreOrderTest::test_place_order_snapshots_level_price_and_recalculates":0.048,"Tests\\Feature\\StoreOrderTest::test_client_supplied_amount_is_ignored":0.047,"Tests\\Feature\\StoreOrderTest::test_product_without_level_price_rejected":0.007,"Tests\\Feature\\StoreOrderTest::test_cancel_only_pending_orders":0.052,"Tests\\Feature\\StoreOrderTest::test_summarized_order_cannot_be_cancelled":0.05,"Tests\\Feature\\StoreOrderTest::test_order_data_isolated_between_stores":0.093,"Tests\\Feature\\PurchaseGenerateTest::test_generate_aggregates_orders_by_product":0.028,"Tests\\Feature\\PurchaseGenerateTest::test_generate_writes_back_order_status":0.16,"Tests\\Feature\\PurchaseGenerateTest::test_generate_without_pending_orders_fails":0.006,"Tests\\Feature\\PurchaseGenerateTest::test_generate_is_idempotent":0.1,"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.006,"Tests\\Feature\\MiniAuthTest::test_mini_token_cannot_access_admin_api":0.044,"Tests\\Feature\\ExportTest::test_export_xlsx_with_encoded_chinese_filename":0.078,"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.047,"Tests\\Feature\\CartTest::test_duplicate_add_merges_quantity":0.049,"Tests\\Feature\\CartTest::test_off_shelf_product_rejected":0.05,"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.089,"Tests\\Feature\\CartTest::test_list_with_prices_amounts_and_totals":0.051,"Tests\\Feature\\CartTest::test_list_marks_off_shelf_item_unbuyable":0.051,"Tests\\Feature\\CartTest::test_empty_cart_returns_empty_items":0.046,"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.049,"Tests\\Feature\\CartTest::test_invalid_quantity_rejected":0.046,"Tests\\Feature\\CartTest::test_delete_item":0.051,"Tests\\Feature\\CartTest::test_delete_other_users_item_rejected":0.009,"Tests\\Feature\\CartTest::test_clear_only_own_cart":0.098,"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.047,"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.011,"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.047,"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.05,"Tests\\Feature\\MiniStoreTest::test_requires_bound_store":0.007,"Tests\\Feature\\MiniStoreTest::test_update_info_validates_length":0.046,"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.076,"Tests\\Feature\\PurchaseGenerateTest::test_generate_with_selected_order_ids":0.103,"Tests\\Feature\\StoreOrderItemTest::test_place_order_snapshots_full_product_info":0.048,"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.074,"Tests\\Feature\\StoreOrderTest::test_soft_delete_only_cancelled_orders":0.06,"Tests\\Feature\\StoreOrderTest::test_soft_delete_rejected_for_non_cancelled_orders":0.272,"Tests\\Feature\\StoreOrderTest::test_soft_delete_twice_rejected":0.068,"Tests\\Feature\\PurchaseEditTest::test_detail_returns_store_matrix":0.107,"Tests\\Feature\\PurchaseEditTest::test_update_cell_syncs_order_item_and_totals":0.111,"Tests\\Feature\\PurchaseEditTest::test_update_row_cost_syncs_all_order_items":0.112,"Tests\\Feature\\PurchaseEditTest::test_update_row_weight_distributed_by_quantity":0.032,"Tests\\Feature\\PurchaseEditTest::test_update_row_requires_at_least_one_field":0.1,"Tests\\Feature\\PurchaseEditTest::test_update_cell_rejects_negative_quantity":0.101,"Tests\\Feature\\PurchaseGenerateTest::test_generate_aggregates_order_items_into_header":0.126,"Tests\\Feature\\ReconciliationTest::test_build_excludes_unfinished_orders":0.016,"Tests\\Feature\\PurchaseEditTest::test_update_row_syncs_product_attributes":0.106,"Tests\\Feature\\PurchaseEditTest::test_update_row_syncs_to_product_catalog":0.019,"Tests\\Feature\\PurchaseEditTest::test_cell_detail_returns_items_with_order_info":0.105,"Tests\\Feature\\PurchaseEditTest::test_cell_detail_rejects_missing_params":0.099,"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.149,"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.06,"Tests\\Feature\\PurchaseEditTest::test_store_summary_rejects_missing_or_invalid_params":0.123,"Tests\\Feature\\ExportTest::test_export_invalid_type_rejected":0.016,"Tests\\Feature\\ExportTest::test_export_missing_purchase_rejected":0.007,"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.007,"Tests\\Feature\\ProductCategoryTest::test_update_second_level_move_to_another_top":0.021,"Tests\\Feature\\ProductCategoryTest::test_update_parent_with_children_cannot_move_under_category":0.007,"Tests\\Feature\\ProductCategoryTest::test_update_with_missing_parent_rejected":0.02,"Tests\\Feature\\ProductCategoryTest::test_create_product_with_parent_category_rejected":0.008,"Tests\\Feature\\ProductCategoryTest::test_create_product_with_leaf_category_ok":0.007,"Tests\\Feature\\ProductCategoryTest::test_mini_categories_returns_all_enabled_categories":0.045,"Tests\\Feature\\MiniBillTest::test_bill_list_derives_pay_state_and_settlement_date":0.051,"Tests\\Feature\\MiniBillTest::test_bill_list_summary_aggregates_unpaid":0.049,"Tests\\Feature\\MiniBillTest::test_bill_list_payable_filter":0.048,"Tests\\Feature\\MiniBillTest::test_bill_list_filters_by_bill_date_range":0.053,"Tests\\Feature\\MiniBillTest::test_bill_data_isolated_between_stores":0.12,"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.053,"Tests\\Feature\\StoreOrderTest::test_order_list_filters_by_order_date_range":0.059,"Tests\\Feature\\StoreOrderTest::test_order_list_rejects_invalid_filters":0.049,"Tests\\Feature\\BillExportTest::test_export_merges_items_across_bills":0.095,"Tests\\Feature\\BillExportTest::test_export_filters_by_root_category":0.052,"Tests\\Feature\\BillExportTest::test_export_empty_category_rejected":0.069,"Tests\\Feature\\BillExportTest::test_export_requires_export_permission":0.051,"Tests\\Feature\\BillExportTest::test_export_invalid_ids_rejected":0.011,"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.06,"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.114,"Tests\\Feature\\StoreOrderTest::test_manual_status_flow_rejects_delivery_and_completion":0.174,"Tests\\Feature\\StoreOrderTest::test_batch_status_only_accept_and_cancel":0.106,"Tests\\Feature\\BillPaymentTest::test_order_status_progresses_via_business_chain":0.061,"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.053,"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.05,"Tests\\Feature\\MiniProductTest::test_product_detail_off_shelf_or_missing_rejected":0.061,"Tests\\Feature\\MiniProductTest::test_product_list_store_user_sees_level_price":0.005,"Tests\\Feature\\MiniProductTest::test_product_list_guest_sees_null_price":0.046,"Tests\\Feature\\BillPaymentTest::test_admin_order_list_and_detail_show_bill_info":0.086,"Tests\\Feature\\BillPaymentTest::test_payment_detail_returns_voucher_urls":0.054,"Tests\\Feature\\StoreOrderTest::test_order_list_preview_includes_image_spec_and_unit":0.051,"Tests\\Feature\\ContainerReturnTest::test_press_containers_adds_amount_and_writes_record":0.088,"Tests\\Feature\\ContainerReturnTest::test_return_containers_deducts_amount":0.062,"Tests\\Feature\\ContainerReturnTest::test_mixed_sign_containers":0.06,"Tests\\Feature\\ContainerReturnTest::test_zero_containers_writes_no_record":0.072,"Tests\\Feature\\ContainerReturnTest::test_non_integer_containers_rejected":0.049,"Tests\\Feature\\ContainerReturnTest::test_manual_create_and_delete_routes_removed":0.021,"Tests\\Feature\\ContainerReturnTest::test_export_matrix_merges_same_day_and_zero_fills":0.089,"Tests\\Feature\\ContainerReturnTest::test_export_filters_by_selected_stores":0.13,"Tests\\Feature\\ContainerReturnTest::test_export_invalid_params_rejected":0.024,"Tests\\Feature\\ContainerReturnTest::test_export_empty_range_rejected":0.02,"Tests\\Feature\\ContainerReturnTest::test_export_requires_export_permission":0.007,"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.024,"Tests\\Feature\\DashboardTest::test_overview_excludes_cancelled_orders":0.052,"Tests\\Feature\\DashboardTest::test_week_growth_rate":0.054,"Tests\\Feature\\DashboardTest::test_trend_covers_30_days_with_zero_filling":0.065,"Tests\\Feature\\DashboardTest::test_category_sales_grouped_by_category":0.061,"Tests\\Feature\\DashboardTest::test_top_products_aggregated_across_orders":0.052,"Tests\\Feature\\DashboardTest::test_top_stores_ranked_by_amount":0.092,"Tests\\Feature\\DashboardTest::test_purchase_amount_prefers_actual_over_estimate":0.01,"Tests\\Feature\\DashboardTest::test_recon_summary_and_receivable":0.055,"Tests\\Feature\\DashboardTest::test_latest_orders_limited_and_ordered":0.055,"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.115,"Tests\\Feature\\ProductPriceTest::test_level_percent_update_notifies_level_stores":0.096,"Tests\\Feature\\ProductPriceTest::test_level_update_without_percent_change_sends_no_notice":0.048,"Tests\\Feature\\ProductPriceTest::test_level_percent_validation":0.01,"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.008,"Tests\\Feature\\ProductCostPriceTest::test_create_product_with_cost_price":0.011,"Tests\\Feature\\ProductCostPriceTest::test_batch_price_cost_row_update":0.052,"Tests\\Feature\\ProductCostPriceTest::test_price_matrix_includes_cost_and_level_price_columns":0.008,"Tests\\Feature\\ProductCostPriceTest::test_mini_order_uses_level_price":0.048,"Tests\\Feature\\ProductCostPriceTest::test_mini_cart_uses_level_price":0.05,"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.105,"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.065,"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.075,"Tests\\Feature\\StoreOrderItemTest::test_update_store_item_validates_negative_values":0.069,"Tests\\Feature\\StoreOrderItemTest::test_add_store_item_uses_level_percent_price":0.059,"Tests\\Feature\\StoreOrderItemTest::test_add_store_item_rejected_when_product_deleted":0.071,"Tests\\Feature\\ExportTest::test_export_lists_all_catalog_products_with_totals_row":0.101,"Tests\\Feature\\ExportTest::test_export_supplier_filter":0.101,"Tests\\Feature\\ExportTest::test_export_stores_multi_sheet":0.117,"Tests\\Feature\\ExportTest::test_export_stores_single_store":0.1,"Tests\\Feature\\ExportTest::test_export_stores_without_items_rejected":0.155,"Tests\\Feature\\ExportTest::test_export_suppliers_multi_sheet":0.105,"Tests\\Feature\\ExportTest::test_export_suppliers_single":0.103,"Tests\\Feature\\ExportTest::test_export_suppliers_without_items_rejected":0.107,"Tests\\Feature\\PurchaseEditTest::test_update_row_cost_change_reprices_items_by_store_level":0.103,"Tests\\Feature\\PurchaseEditTest::test_update_row_without_cost_change_keeps_item_price":0.104,"Tests\\Feature\\CartTest::test_update_other_stores_item_rejected":0.091,"Tests\\Feature\\CartTest::test_delete_other_stores_item_rejected":0.091,"Tests\\Feature\\CartTest::test_cart_isolated_between_stores":0.09,"Tests\\Feature\\MiniAuthTest::test_login_issues_token_for_valid_credentials":0.145,"Tests\\Feature\\MiniAuthTest::test_login_rejects_wrong_password":0.133,"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.002,"Tests\\Feature\\MiniAuthTest::test_login_token_accesses_protected_routes":0.127,"Tests\\Feature\\MiniAuthTest::test_set_password_succeeds":0.228,"Tests\\Feature\\MiniAuthTest::test_set_password_rejects_wrong_old_password":0.137,"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.087,"Tests\\Feature\\MiniProductTest::test_product_detail_store_sees_level_price":0.046,"Tests\\Feature\\MiniProductTest::test_product_list_store_sees_level_price":0.046,"Tests\\Feature\\MiniStoreTest::test_info_returns_current_store":0.058,"Tests\\Feature\\MiniStoreTest::test_disabled_store_rejected":0.047,"Tests\\Feature\\ProductImportExportTest::test_export_downloads_all_products":0.013,"Tests\\Feature\\ProductImportExportTest::test_export_filters_by_category":0.008,"Tests\\Feature\\ProductImportExportTest::test_export_template_mode":0.008,"Tests\\Feature\\ProductImportExportTest::test_export_requires_permission":0.007,"Tests\\Feature\\ProductImportExportTest::test_import_creates_products_and_auto_supplier":0.049,"Tests\\Feature\\ProductImportExportTest::test_import_matches_existing_supplier":0.018,"Tests\\Feature\\ProductImportExportTest::test_import_errors_abort_all_rows":0.019,"Tests\\Feature\\ProductImportExportTest::test_import_category_path_resolution":0.057,"Tests\\Feature\\ProductImportExportTest::test_import_rejects_invalid_header":0.023,"Tests\\Feature\\ProductImportExportTest::test_import_requires_permission":0.019,"Tests\\Feature\\ProductImportExportTest::test_list_filters_by_market":0.008,"Tests\\Feature\\MiniReportTest::test_report_aggregates_total_and_item_percent":0.059,"Tests\\Feature\\MiniReportTest::test_report_accumulates_same_product_across_orders":0.062,"Tests\\Feature\\MiniReportTest::test_report_filters_by_custom_date_range":0.047,"Tests\\Feature\\MiniReportTest::test_report_excludes_cancelled_and_deleted_orders":0.048,"Tests\\Feature\\MiniReportTest::test_report_preset_month_and_last_month":0.053,"Tests\\Feature\\MiniReportTest::test_report_preset_week_and_last_week":0.052,"Tests\\Feature\\MiniReportTest::test_report_defaults_to_current_month":0.048,"Tests\\Feature\\MiniReportTest::test_report_data_isolated_between_stores":0.09,"Tests\\Feature\\MiniReportTest::test_report_empty_period_returns_zero":0.06,"Tests\\Feature\\MiniReportTest::test_report_validates_params":0.051,"Tests\\Feature\\ProductBatchDeleteTest::test_batch_delete_soft_deletes_selected_products":0.008,"Tests\\Feature\\ProductBatchDeleteTest::test_batch_delete_requires_non_empty_ids":0.006,"Tests\\Feature\\ProductBatchDeleteTest::test_batch_delete_forbidden_without_permission":0.007,"Tests\\Feature\\CartTest::test_place_order_clears_ordered_items_from_cart":0.059,"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.074,"Tests\\Feature\\BillPaymentTest::test_generate_bill_saves_after_sale_and_remark":0.116,"Tests\\Feature\\ProductCostPriceTest::test_product_price_unit_default_and_editable":0.016}} \ 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},"times":{"Tests\\Feature\\ExampleTest::test_the_application_returns_a_successful_response":0.007,"Tests\\Feature\\ProductPriceTest::test_product_list_returns_price_for_store_level":0.1,"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.011,"Tests\\Feature\\StoreOrderTest::test_place_order_snapshots_level_price_and_recalculates":0.052,"Tests\\Feature\\StoreOrderTest::test_client_supplied_amount_is_ignored":0.052,"Tests\\Feature\\StoreOrderTest::test_product_without_level_price_rejected":0.007,"Tests\\Feature\\StoreOrderTest::test_cancel_only_pending_orders":0.057,"Tests\\Feature\\StoreOrderTest::test_summarized_order_cannot_be_cancelled":0.053,"Tests\\Feature\\StoreOrderTest::test_order_data_isolated_between_stores":0.101,"Tests\\Feature\\PurchaseGenerateTest::test_generate_aggregates_orders_by_product":0.028,"Tests\\Feature\\PurchaseGenerateTest::test_generate_writes_back_order_status":0.124,"Tests\\Feature\\PurchaseGenerateTest::test_generate_without_pending_orders_fails":0.006,"Tests\\Feature\\PurchaseGenerateTest::test_generate_is_idempotent":0.127,"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.034,"Tests\\Feature\\MiniAuthTest::test_mini_token_cannot_access_admin_api":0.058,"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.072,"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.049,"Tests\\Feature\\CartTest::test_duplicate_add_merges_quantity":0.052,"Tests\\Feature\\CartTest::test_off_shelf_product_rejected":0.049,"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.088,"Tests\\Feature\\CartTest::test_list_with_prices_amounts_and_totals":0.053,"Tests\\Feature\\CartTest::test_list_marks_off_shelf_item_unbuyable":0.053,"Tests\\Feature\\CartTest::test_empty_cart_returns_empty_items":0.046,"Tests\\Feature\\CartTest::test_update_quantity":0.051,"Tests\\Feature\\CartTest::test_update_other_users_item_rejected":0.009,"Tests\\Feature\\CartTest::test_update_missing_item_rejected":0.051,"Tests\\Feature\\CartTest::test_invalid_quantity_rejected":0.05,"Tests\\Feature\\CartTest::test_delete_item":0.05,"Tests\\Feature\\CartTest::test_delete_other_users_item_rejected":0.009,"Tests\\Feature\\CartTest::test_clear_only_own_cart":0.098,"Tests\\Feature\\CartTest::test_unauthenticated_returns_401":0.003,"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.05,"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.008,"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.053,"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.05,"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.052,"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.06,"Tests\\Feature\\PurchaseGenerateTest::test_generate_with_selected_order_ids":0.119,"Tests\\Feature\\StoreOrderItemTest::test_place_order_snapshots_full_product_info":0.053,"Tests\\Feature\\StoreOrderItemTest::test_mini_detail_hides_cost_price":0.062,"Tests\\Feature\\StoreOrderItemTest::test_admin_detail_shows_cost_price_and_supplier":0.06,"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.066,"Tests\\Feature\\StoreOrderTest::test_soft_delete_only_cancelled_orders":0.08,"Tests\\Feature\\StoreOrderTest::test_soft_delete_rejected_for_non_cancelled_orders":0.296,"Tests\\Feature\\StoreOrderTest::test_soft_delete_twice_rejected":0.064,"Tests\\Feature\\PurchaseEditTest::test_detail_returns_store_matrix":0.122,"Tests\\Feature\\PurchaseEditTest::test_update_cell_syncs_order_item_and_totals":0.116,"Tests\\Feature\\PurchaseEditTest::test_update_row_cost_syncs_all_order_items":0.119,"Tests\\Feature\\PurchaseEditTest::test_update_row_weight_distributed_by_quantity":0.032,"Tests\\Feature\\PurchaseEditTest::test_update_row_requires_at_least_one_field":0.108,"Tests\\Feature\\PurchaseEditTest::test_update_cell_rejects_negative_quantity":0.111,"Tests\\Feature\\PurchaseGenerateTest::test_generate_aggregates_order_items_into_header":0.115,"Tests\\Feature\\ReconciliationTest::test_build_excludes_unfinished_orders":0.016,"Tests\\Feature\\PurchaseEditTest::test_update_row_syncs_product_attributes":0.116,"Tests\\Feature\\PurchaseEditTest::test_update_row_syncs_to_product_catalog":0.019,"Tests\\Feature\\PurchaseEditTest::test_cell_detail_returns_items_with_order_info":0.116,"Tests\\Feature\\PurchaseEditTest::test_cell_detail_rejects_missing_params":0.11,"Tests\\Feature\\PurchaseEditTest::test_update_cell_weight_cascades_totals":0.12,"Tests\\Feature\\PurchaseEditTest::test_cell_sync_refreshes_snapshot_and_cascades":0.012,"Tests\\Feature\\PurchaseEditTest::test_edits_rejected_when_purchase_completed":0.129,"Tests\\Feature\\PurchaseEditTest::test_update_row_does_not_touch_manual_weight":0.124,"Tests\\Feature\\PurchaseEditTest::test_store_summary_returns_aggregated_items":0.115,"Tests\\Feature\\PurchaseEditTest::test_store_summary_aggregates_multiple_orders_with_weighted_price":0.076,"Tests\\Feature\\PurchaseEditTest::test_store_summary_rejects_missing_or_invalid_params":0.116,"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.009,"Tests\\Feature\\ProductCategoryTest::test_create_category_under_second_level_rejected":0.009,"Tests\\Feature\\ProductCategoryTest::test_update_second_level_move_to_another_top":0.011,"Tests\\Feature\\ProductCategoryTest::test_update_parent_with_children_cannot_move_under_category":0.01,"Tests\\Feature\\ProductCategoryTest::test_update_with_missing_parent_rejected":0.009,"Tests\\Feature\\ProductCategoryTest::test_create_product_with_parent_category_rejected":0.013,"Tests\\Feature\\ProductCategoryTest::test_create_product_with_leaf_category_ok":0.011,"Tests\\Feature\\ProductCategoryTest::test_mini_categories_returns_all_enabled_categories":0.052,"Tests\\Feature\\MiniBillTest::test_bill_list_derives_pay_state_and_settlement_date":0.051,"Tests\\Feature\\MiniBillTest::test_bill_list_summary_aggregates_unpaid":0.05,"Tests\\Feature\\MiniBillTest::test_bill_list_payable_filter":0.053,"Tests\\Feature\\MiniBillTest::test_bill_list_filters_by_bill_date_range":0.057,"Tests\\Feature\\MiniBillTest::test_bill_data_isolated_between_stores":0.104,"Tests\\Feature\\MiniBillTest::test_bill_detail_outputs_bill_items_and_orders":0.075,"Tests\\Feature\\StoreOrderTest::test_order_list_outputs_status_name_and_item_preview":0.063,"Tests\\Feature\\StoreOrderTest::test_order_list_filters_by_order_date_range":0.067,"Tests\\Feature\\StoreOrderTest::test_order_list_rejects_invalid_filters":0.055,"Tests\\Feature\\BillExportTest::test_export_merges_items_across_bills":0.113,"Tests\\Feature\\BillExportTest::test_export_filters_by_root_category":0.055,"Tests\\Feature\\BillExportTest::test_export_empty_category_rejected":0.074,"Tests\\Feature\\BillExportTest::test_export_requires_export_permission":0.054,"Tests\\Feature\\BillExportTest::test_export_invalid_ids_rejected":0.012,"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.057,"Tests\\Feature\\MiniNoticeTest::test_broadcast_read_creates_personal_copy":0.054,"Tests\\Feature\\MiniNoticeTest::test_personal_notice_read_marks_directly":0.054,"Tests\\Feature\\MiniNoticeTest::test_other_users_notice_not_found":0.007,"Tests\\Feature\\StoreOrderTest::test_manual_status_flow_only_accept_and_cancel":0.127,"Tests\\Feature\\StoreOrderTest::test_manual_status_flow_rejects_delivery_and_completion":0.179,"Tests\\Feature\\StoreOrderTest::test_batch_status_only_accept_and_cancel":0.135,"Tests\\Feature\\BillPaymentTest::test_order_status_progresses_via_business_chain":0.062,"Tests\\Feature\\BillPaymentTest::test_manual_pay_accumulates_store_total_purchase_amount":0.075,"Tests\\Feature\\BillPaymentTest::test_payment_audit_pass_accumulates_store_total_purchase_amount":0.06,"Tests\\Feature\\BillPaymentTest::test_payment_audit_reject_does_not_accumulate":0.05,"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.051,"Tests\\Feature\\MiniProductTest::test_product_detail_off_shelf_or_missing_rejected":0.051,"Tests\\Feature\\MiniProductTest::test_product_list_store_user_sees_level_price":0.005,"Tests\\Feature\\MiniProductTest::test_product_list_guest_sees_null_price":0.054,"Tests\\Feature\\BillPaymentTest::test_admin_order_list_and_detail_show_bill_info":0.08,"Tests\\Feature\\BillPaymentTest::test_payment_detail_returns_voucher_urls":0.056,"Tests\\Feature\\StoreOrderTest::test_order_list_preview_includes_image_spec_and_unit":0.068,"Tests\\Feature\\ContainerReturnTest::test_press_containers_adds_amount_and_writes_record":0.078,"Tests\\Feature\\ContainerReturnTest::test_return_containers_deducts_amount":0.07,"Tests\\Feature\\ContainerReturnTest::test_mixed_sign_containers":0.072,"Tests\\Feature\\ContainerReturnTest::test_zero_containers_writes_no_record":0.078,"Tests\\Feature\\ContainerReturnTest::test_non_integer_containers_rejected":0.055,"Tests\\Feature\\ContainerReturnTest::test_manual_create_and_delete_routes_removed":0.01,"Tests\\Feature\\ContainerReturnTest::test_export_matrix_merges_same_day_and_zero_fills":0.097,"Tests\\Feature\\ContainerReturnTest::test_export_filters_by_selected_stores":0.138,"Tests\\Feature\\ContainerReturnTest::test_export_invalid_params_rejected":0.014,"Tests\\Feature\\ContainerReturnTest::test_export_empty_range_rejected":0.008,"Tests\\Feature\\ContainerReturnTest::test_export_requires_export_permission":0.007,"Tests\\Feature\\DashboardTest::test_guest_cannot_access_dashboard":0.002,"Tests\\Feature\\DashboardTest::test_requires_dashboard_analysis_ability":0.007,"Tests\\Feature\\DashboardTest::test_empty_database_returns_zero_structure":0.01,"Tests\\Feature\\DashboardTest::test_overview_excludes_cancelled_orders":0.066,"Tests\\Feature\\DashboardTest::test_week_growth_rate":0.091,"Tests\\Feature\\DashboardTest::test_trend_covers_30_days_with_zero_filling":0.067,"Tests\\Feature\\DashboardTest::test_category_sales_grouped_by_category":0.057,"Tests\\Feature\\DashboardTest::test_top_products_aggregated_across_orders":0.059,"Tests\\Feature\\DashboardTest::test_top_stores_ranked_by_amount":0.103,"Tests\\Feature\\DashboardTest::test_purchase_amount_prefers_actual_over_estimate":0.014,"Tests\\Feature\\DashboardTest::test_recon_summary_and_receivable":0.056,"Tests\\Feature\\DashboardTest::test_latest_orders_limited_and_ordered":0.06,"Tests\\Feature\\DashboardTest::test_archives_count_active_records_only":0.16,"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.113,"Tests\\Feature\\ProductPriceTest::test_level_percent_update_notifies_level_stores":0.109,"Tests\\Feature\\ProductPriceTest::test_level_update_without_percent_change_sends_no_notice":0.057,"Tests\\Feature\\ProductPriceTest::test_level_percent_validation":0.016,"Tests\\Feature\\ProductCostPriceTest::test_calc_level_price":0.002,"Tests\\Feature\\ProductCostPriceTest::test_calc_level_price_edge_cases":0.001,"Tests\\Feature\\ProductCostPriceTest::test_admin_list_contains_cost_price_and_level_prices":0.01,"Tests\\Feature\\ProductCostPriceTest::test_create_product_with_cost_price":0.012,"Tests\\Feature\\ProductCostPriceTest::test_batch_price_cost_row_update":0.055,"Tests\\Feature\\ProductCostPriceTest::test_price_matrix_includes_cost_and_level_price_columns":0.01,"Tests\\Feature\\ProductCostPriceTest::test_mini_order_uses_level_price":0.055,"Tests\\Feature\\ProductCostPriceTest::test_mini_cart_uses_level_price":0.06,"Tests\\Feature\\CartTest::test_store_level_missing_rejected":0.006,"Tests\\Feature\\StoreOrderTest::test_store_level_missing_rejected":0.053,"Tests\\Feature\\PurchaseEditTest::test_update_row_skips_catalog_sync_when_product_deleted":0.121,"Tests\\Feature\\PurchaseEditTest::test_store_item_add_creates_item_and_recalculates":0.116,"Tests\\Feature\\PurchaseEditTest::test_store_item_update_merges_multi_order_rows":0.069,"Tests\\Feature\\PurchaseEditTest::test_store_item_remove_deletes_and_recalculates":0.115,"Tests\\Feature\\StoreOrderItemTest::test_update_store_item_recalculates_totals":0.077,"Tests\\Feature\\StoreOrderItemTest::test_store_item_edits_rejected_when_purchase_completed":0.076,"Tests\\Feature\\StoreOrderItemTest::test_update_store_item_validates_negative_values":0.072,"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.076,"Tests\\Feature\\ExportTest::test_export_lists_all_catalog_products_with_totals_row":0.148,"Tests\\Feature\\ExportTest::test_export_supplier_filter":0.122,"Tests\\Feature\\ExportTest::test_export_stores_multi_sheet":0.139,"Tests\\Feature\\ExportTest::test_export_stores_single_store":0.113,"Tests\\Feature\\ExportTest::test_export_stores_without_items_rejected":0.182,"Tests\\Feature\\ExportTest::test_export_suppliers_multi_sheet":0.123,"Tests\\Feature\\ExportTest::test_export_suppliers_single":0.113,"Tests\\Feature\\ExportTest::test_export_suppliers_without_items_rejected":0.112,"Tests\\Feature\\PurchaseEditTest::test_update_row_cost_change_reprices_items_by_store_level":0.111,"Tests\\Feature\\PurchaseEditTest::test_update_row_without_cost_change_keeps_item_price":0.117,"Tests\\Feature\\CartTest::test_update_other_stores_item_rejected":0.093,"Tests\\Feature\\CartTest::test_delete_other_stores_item_rejected":0.093,"Tests\\Feature\\CartTest::test_cart_isolated_between_stores":0.1,"Tests\\Feature\\MiniAuthTest::test_login_issues_token_for_valid_credentials":0.148,"Tests\\Feature\\MiniAuthTest::test_login_rejects_wrong_password":0.139,"Tests\\Feature\\MiniAuthTest::test_login_rejects_unknown_username":0.004,"Tests\\Feature\\MiniAuthTest::test_disabled_store_cannot_login":0.135,"Tests\\Feature\\MiniAuthTest::test_login_validates_required_params":0.005,"Tests\\Feature\\MiniAuthTest::test_login_token_accesses_protected_routes":0.139,"Tests\\Feature\\MiniAuthTest::test_set_password_succeeds":0.229,"Tests\\Feature\\MiniAuthTest::test_set_password_rejects_wrong_old_password":0.133,"Tests\\Feature\\MiniAuthTest::test_set_password_validates_confirmation":0.055,"Tests\\Feature\\BillExportTest::test_mini_export_scoped_to_own_store":0.099,"Tests\\Feature\\MiniNoticeTest::test_other_stores_notice_not_found":0.097,"Tests\\Feature\\MiniProductTest::test_product_detail_store_sees_level_price":0.053,"Tests\\Feature\\MiniProductTest::test_product_list_store_sees_level_price":0.052,"Tests\\Feature\\MiniStoreTest::test_info_returns_current_store":0.05,"Tests\\Feature\\MiniStoreTest::test_disabled_store_rejected":0.05,"Tests\\Feature\\ProductImportExportTest::test_export_downloads_all_products":0.014,"Tests\\Feature\\ProductImportExportTest::test_export_filters_by_category":0.01,"Tests\\Feature\\ProductImportExportTest::test_export_template_mode":0.007,"Tests\\Feature\\ProductImportExportTest::test_export_requires_permission":0.009,"Tests\\Feature\\ProductImportExportTest::test_import_creates_products_and_auto_supplier":0.044,"Tests\\Feature\\ProductImportExportTest::test_import_matches_existing_supplier":0.027,"Tests\\Feature\\ProductImportExportTest::test_import_errors_abort_all_rows":0.037,"Tests\\Feature\\ProductImportExportTest::test_import_category_path_resolution":0.053,"Tests\\Feature\\ProductImportExportTest::test_import_rejects_invalid_header":0.027,"Tests\\Feature\\ProductImportExportTest::test_import_requires_permission":0.014,"Tests\\Feature\\ProductImportExportTest::test_list_filters_by_market":0.01,"Tests\\Feature\\MiniReportTest::test_report_aggregates_total_and_item_percent":0.051,"Tests\\Feature\\MiniReportTest::test_report_accumulates_same_product_across_orders":0.054,"Tests\\Feature\\MiniReportTest::test_report_filters_by_custom_date_range":0.049,"Tests\\Feature\\MiniReportTest::test_report_excludes_cancelled_and_deleted_orders":0.052,"Tests\\Feature\\MiniReportTest::test_report_preset_month_and_last_month":0.055,"Tests\\Feature\\MiniReportTest::test_report_preset_week_and_last_week":0.054,"Tests\\Feature\\MiniReportTest::test_report_defaults_to_current_month":0.05,"Tests\\Feature\\MiniReportTest::test_report_data_isolated_between_stores":0.095,"Tests\\Feature\\MiniReportTest::test_report_empty_period_returns_zero":0.053,"Tests\\Feature\\MiniReportTest::test_report_validates_params":0.053,"Tests\\Feature\\ProductBatchDeleteTest::test_batch_delete_soft_deletes_selected_products":0.009,"Tests\\Feature\\ProductBatchDeleteTest::test_batch_delete_requires_non_empty_ids":0.011,"Tests\\Feature\\ProductBatchDeleteTest::test_batch_delete_forbidden_without_permission":0.011,"Tests\\Feature\\CartTest::test_place_order_clears_ordered_items_from_cart":0.063,"Tests\\Feature\\StoreOrderItemTest::test_admin_order_list_filters_by_order_date_range":0.077,"Tests\\Feature\\StoreOrderItemTest::test_admin_order_list_search_by_purchase_no":0.08,"Tests\\Feature\\ExportTest::test_export_suppliers_split_by_market":0.074,"Tests\\Feature\\BillPaymentTest::test_generate_bill_saves_after_sale_and_remark":0.125,"Tests\\Feature\\ProductCostPriceTest::test_product_price_unit_default_and_editable":0.017,"Tests\\Feature\\StoreOrderTest::test_place_order_blocked_by_overdue_unpaid_bill":0.06,"Tests\\Feature\\StoreOrderTest::test_place_order_allowed_within_payment_cycle":0.063,"Tests\\Feature\\CartTest::test_cart_summary_endpoint":0.057,"Tests\\Feature\\CartTest::test_cart_summary_requires_login":0.004,"Tests\\Feature\\MiniProductTest::test_product_list_appends_cart_quantity_and_summary":0.063,"Tests\\Feature\\MiniProductTest::test_product_list_guest_sees_zero_cart_fields":0.052,"Tests\\Feature\\MiniProductTest::test_product_detail_appends_cart_quantity":0.053,"Tests\\Feature\\MiniProductTest::test_home_appends_cart_summary":0.056}} \ No newline at end of file diff --git a/app/Http/Controllers/Mini/CartController.php b/app/Http/Controllers/Mini/CartController.php index fed50f2..ee74986 100644 --- a/app/Http/Controllers/Mini/CartController.php +++ b/app/Http/Controllers/Mini/CartController.php @@ -7,6 +7,7 @@ use App\Http\Requests\Mini\MiniCartRequest; use App\Models\CartModel; use App\Models\CustomerLevelModel; use App\Models\ProductModel; +use App\Services\CartService; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; @@ -207,6 +208,15 @@ class CartController extends BaseMiniController return $this->success([], '已删除'); } + /** + * 购物车悬浮球汇总(轻量接口):种数/总数量/总金额,供任意页面刷新右下角悬浮球 + */ + #[GetRoute('/cart/summary', authorize: true)] + public function summary(Request $request): JsonResponse + { + return $this->success(app(CartService::class)->summary($this->currentStore($request))); + } + /** * 清空购物车(仅当前用户) */ diff --git a/app/Http/Controllers/Mini/HomeController.php b/app/Http/Controllers/Mini/HomeController.php index f8f0c9e..2d3ad51 100644 --- a/app/Http/Controllers/Mini/HomeController.php +++ b/app/Http/Controllers/Mini/HomeController.php @@ -5,19 +5,22 @@ namespace App\Http\Controllers\Mini; use App\Models\HomeBannerModel; use App\Models\HomeNavModel; use App\Models\HomePromoModel; +use App\Services\CartService; use Illuminate\Http\JsonResponse; +use Illuminate\Http\Request; use Modules\AnnoRoute\Attribute\GetRoute; use Modules\AnnoRoute\Attribute\RequestAttribute; /** * 小程序首页配置(轮播图 + 宫格导航 + 促销推荐卡片,仅返回启用项) + * 登录门店附加 cart 购物车悬浮球汇总(未登录返回零值结构) */ #[RequestAttribute('/mini', 'mini', authGuard: 'users')] class HomeController extends BaseMiniController { /** 首页配置聚合:banners / navs / promos,按 sort 升序 */ #[GetRoute('/home', authorize: false)] - public function index(): JsonResponse + public function index(Request $request): JsonResponse { $banners = HomeBannerModel::query() ->where('status', HomeBannerModel::STATUS_NORMAL) @@ -41,6 +44,7 @@ class HomeController extends BaseMiniController 'banners' => $banners, 'navs' => $navs, 'promos' => $promos, + 'cart' => app(CartService::class)->summary($this->optionalStore($request)), ]); } } diff --git a/app/Http/Controllers/Mini/OrderController.php b/app/Http/Controllers/Mini/OrderController.php index 9e0d5e8..7175e02 100644 --- a/app/Http/Controllers/Mini/OrderController.php +++ b/app/Http/Controllers/Mini/OrderController.php @@ -4,6 +4,7 @@ namespace App\Http\Controllers\Mini; use App\Exceptions\RepositoryException; use App\Http\Requests\Mini\MiniOrderRequest; +use App\Models\BillModel; use App\Models\CartModel; use App\Models\CustomerLevelModel; use App\Models\ProductModel; @@ -37,6 +38,26 @@ class OrderController extends BaseMiniController throw new RepositoryException('门店未设置客户等级,无法下单,请联系客服'); } + // 回款周期校验:存在逾期未回款账单(未支付即拦截,含审核中)时禁止下单 + // 周期 0 天=立即结清:任何未回款账单(含当天)都拦截;周期 N≥1:账单日 + N 天 < 今天(超过回款周期)才拦截 + $cycleDays = (int) $store->payment_cycle_days; + $overdueQuery = BillModel::query() + ->where('store_id', $store->id) + ->where('status', BillModel::STATUS_UNPAID); + if ($cycleDays > 0) { + $overdueQuery->whereDate('bill_date', '<', now()->subDays($cycleDays)->toDateString()); + } + $overdue = $overdueQuery + ->selectRaw('COUNT(*) as aggregate_count, COALESCE(SUM(total_amount), 0) as aggregate_amount') + ->first(); + if ((int) $overdue->aggregate_count > 0) { + $amountText = bcadd((string) $overdue->aggregate_amount, '0', 2); + throw new RepositoryException($cycleDays > 0 + ? '您有 ' . (int) $overdue->aggregate_count . ' 笔账单已超过回款周期未回款(合计 ¥' . $amountText . '),请先结清后再下单' + : '您有 ' . (int) $overdue->aggregate_count . ' 笔账单未回款(合计 ¥' . $amountText . '),回款周期为当日结清,请先结清后再下单' + ); + } + $items = $request->validated('items'); $remark = (string) ($request->validated('remark') ?? ''); diff --git a/app/Http/Controllers/Mini/ProductController.php b/app/Http/Controllers/Mini/ProductController.php index 14215ba..ce369b7 100644 --- a/app/Http/Controllers/Mini/ProductController.php +++ b/app/Http/Controllers/Mini/ProductController.php @@ -6,6 +6,7 @@ use App\Exceptions\RepositoryException; use App\Models\CustomerLevelModel; use App\Models\ProductCategoryModel; use App\Models\ProductModel; +use App\Services\CartService; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Modules\AnnoRoute\Attribute\GetRoute; @@ -25,7 +26,8 @@ class ProductController extends BaseMiniController } /** - * 商品列表 + * 商品列表(登录门店附 cart_id/cart_quantity 供列表直接加减购物车; + * data.cart 为购物车悬浮球汇总,未登录返回零值结构) */ #[GetRoute('/product/list', authorize: false)] public function products(Request $request): JsonResponse @@ -50,22 +52,30 @@ class ProductController extends BaseMiniController ->orderBy('id') ->paginate($pageSize); - // 当前门店的等级(售价 = 成本价 × (100 + 等级上浮比例) / 100) + // 当前门店的等级(售价 = 成本价 × (100 + 等级上浮比例) / 100)与购物车行 $store = $this->optionalStore($request); $level = ($store !== null && $store->level_id > 0) ? $store->level : null; + $cartService = app(CartService::class); + $cartRows = $store !== null ? $cartService->cartRowMap($store->id) : []; $paginator->getCollection()->transform( - static function (ProductModel $product) use ($level): array { + static function (ProductModel $product) use ($level, $cartRows): array { $row = $product->toArray(); // 实际价(按等级上浮比例换算;成本价不随序列化输出) $row['price'] = $level !== null ? CustomerLevelModel::calcLevelPrice($product->cost_price, $level->percent) : null; + // 购物车数量(列表直接加减用;不在购物车为 0/'0.00') + $row['cart_id'] = $cartRows[$product->id]['id'] ?? 0; + $row['cart_quantity'] = $cartRows[$product->id]['quantity'] ?? '0.00'; return $row; } ); - return $this->success($paginator->toArray()); + $data = $paginator->toArray(); + $data['cart'] = $cartService->summary($store); + + return $this->success($data); } /** @@ -89,8 +99,12 @@ class ProductController extends BaseMiniController $price = CustomerLevelModel::calcLevelPrice($product->cost_price, $level->percent); } + $cartRows = $store !== null ? app(CartService::class)->cartRowMap($store->id) : []; + $data = $product->toArray(); $data['price'] = $price; + $data['cart_id'] = $cartRows[$product->id]['id'] ?? 0; + $data['cart_quantity'] = $cartRows[$product->id]['quantity'] ?? '0.00'; return $this->success($data); } diff --git a/app/Services/CartService.php b/app/Services/CartService.php new file mode 100644 index 0000000..a3af005 --- /dev/null +++ b/app/Services/CartService.php @@ -0,0 +1,83 @@ + ['id' => 购物车行ID, 'quantity' => 数量字符串] + * (商品列表/详情附加 cart_id/cart_quantity 用;行ID供直接加减/删除操作) + * + * @return array + */ + public function cartRowMap(int $storeId): array + { + return CartModel::query() + ->where('store_id', $storeId) + ->get(['id', 'product_id', 'quantity']) + ->keyBy('product_id') + ->map(static fn (CartModel $row): array => [ + 'id' => (int) $row->id, + 'quantity' => (string) $row->quantity, + ]) + ->all(); + } + + /** + * 购物车悬浮球汇总:种数/总数量/总金额(未登录或空购物车返回零值结构) + * + * @return array{total_count: int, total_quantity: string, total_amount: string} + */ + public function summary(?StoreModel $store): array + { + $empty = ['total_count' => 0, 'total_quantity' => '0.00', 'total_amount' => '0.00']; + if ($store === null) { + return $empty; + } + + $rows = CartModel::query() + ->where('store_id', $store->id) + ->get(['id', 'product_id', 'quantity']); + if ($rows->isEmpty()) { + return $empty; + } + + $products = ProductModel::query() + ->where('status', ProductModel::STATUS_ON) + ->whereIn('id', $rows->pluck('product_id')->all()) + ->get(['id', 'cost_price']) + ->keyBy('id'); + $level = $store->level_id > 0 ? $store->level : null; + + $totalQuantity = '0.00'; + $totalAmount = '0.00'; + foreach ($rows as $row) { + $product = $products->get($row->product_id); + if ($product === null || $level === null) { + continue; + } + $price = CustomerLevelModel::calcLevelPrice($product->cost_price, $level->percent); + $totalQuantity = bcadd($totalQuantity, (string) $row->quantity, 2); + $totalAmount = bcadd($totalAmount, bcmul($price, (string) $row->quantity, 2), 2); + } + + return [ + 'total_count' => $rows->count(), + 'total_quantity' => $totalQuantity, + 'total_amount' => $totalAmount, + ]; + } +} diff --git a/tests/Feature/CartTest.php b/tests/Feature/CartTest.php index 0a3b6e1..64ad600 100644 --- a/tests/Feature/CartTest.php +++ b/tests/Feature/CartTest.php @@ -265,6 +265,43 @@ class CartTest extends ProcurementTestCase $this->getJson('/mini/cart')->assertStatus(401); } + /** 悬浮球汇总:种数/总数量/总金额(下架商品不计入数量与金额但仍计种数) */ + public function test_cart_summary_endpoint(): void + { + $level = CustomerLevelModel::factory()->create(); + $store = StoreModel::factory()->create(['level_id' => $level->id]); + $p1 = ProductModel::factory()->create(['status' => ProductModel::STATUS_ON, 'cost_price' => '5.00']); + $p2 = ProductModel::factory()->create(['status' => ProductModel::STATUS_ON, 'cost_price' => '3.00']); + $this->actingAsMiniStore($store); + + // 空购物车返回零值结构 + $this->getJson('/mini/cart/summary')->assertOk() + ->assertJsonPath('data.total_count', 0) + ->assertJsonPath('data.total_quantity', '0.00') + ->assertJsonPath('data.total_amount', '0.00'); + + $this->postJson('/mini/cart', ['product_id' => $p1->id, 'quantity' => 2]); + $this->postJson('/mini/cart', ['product_id' => $p2->id, 'quantity' => 1]); + + $this->getJson('/mini/cart/summary')->assertOk() + ->assertJsonPath('data.total_count', 2) + ->assertJsonPath('data.total_quantity', '3.00') + ->assertJsonPath('data.total_amount', '13.00'); + + // 下架商品:数量/金额不计入(与购物车列表口径一致),种数仍计 + $p2->update(['status' => ProductModel::STATUS_OFF]); + $this->getJson('/mini/cart/summary')->assertOk() + ->assertJsonPath('data.total_count', 2) + ->assertJsonPath('data.total_quantity', '2.00') + ->assertJsonPath('data.total_amount', '10.00'); + } + + /** 未登录访问悬浮球汇总 → 401 */ + public function test_cart_summary_requires_login(): void + { + $this->getJson('/mini/cart/summary')->assertStatus(401); + } + /** 下单成功后自动清空购物车中已下单的商品(未下单商品保留) */ public function test_place_order_clears_ordered_items_from_cart(): void { diff --git a/tests/Feature/MiniProductTest.php b/tests/Feature/MiniProductTest.php index f1b51b2..de86700 100644 --- a/tests/Feature/MiniProductTest.php +++ b/tests/Feature/MiniProductTest.php @@ -2,6 +2,7 @@ namespace Tests\Feature; +use App\Models\CartModel; use App\Models\CustomerLevelModel; use App\Models\ProductModel; use App\Models\StoreModel; @@ -103,4 +104,85 @@ class MiniProductTest extends ProcurementTestCase $this->assertNotNull($row); $this->assertNull($row['price']); } + + /** 商品列表:登录门店附加购物车行ID与数量,响应附悬浮球汇总 */ + public function test_product_list_appends_cart_quantity_and_summary(): void + { + [$store, $product] = $this->makeStoreWithProduct('5.00'); + $extra = ProductModel::factory()->create(['status' => ProductModel::STATUS_ON, 'cost_price' => '3.00']); + $this->actingAsMiniStore($store); + + $this->postJson('/mini/cart', ['product_id' => $product->id, 'quantity' => 2]); + $this->postJson('/mini/cart', ['product_id' => $extra->id, 'quantity' => 1]); + $cartId = CartModel::where('product_id', $product->id)->first()->id; + + $data = $this->getJson('/mini/product/list')->assertOk()->json('data'); + $row = collect($data['data'])->firstWhere('id', $product->id); + $this->assertSame($cartId, $row['cart_id'], '在购物车的商品应附购物车行ID'); + $this->assertSame('2.00', $row['cart_quantity']); + + // 不在购物车的商品:cart_id=0、cart_quantity='0.00' + $other = ProductModel::factory()->create(['status' => ProductModel::STATUS_ON, 'cost_price' => '1.00']); + $otherRow = collect($this->getJson('/mini/product/list')->json('data.data'))->firstWhere('id', $other->id); + $this->assertSame(0, $otherRow['cart_id']); + $this->assertSame('0.00', $otherRow['cart_quantity']); + + // 悬浮球汇总:2 种商品、总数量 3、总金额 5×2+3×1=13 + $this->assertSame(2, $data['cart']['total_count']); + $this->assertSame('3.00', $data['cart']['total_quantity']); + $this->assertSame('13.00', $data['cart']['total_amount']); + } + + /** 商品列表/详情:未登录时购物车字段为零值结构 */ + public function test_product_list_guest_sees_zero_cart_fields(): void + { + [, $product] = $this->makeStoreWithProduct(); + + $data = $this->getJson('/mini/product/list')->assertOk()->json('data'); + $row = collect($data['data'])->firstWhere('id', $product->id); + $this->assertSame(0, $row['cart_id']); + $this->assertSame('0.00', $row['cart_quantity']); + $this->assertSame( + ['total_count' => 0, 'total_quantity' => '0.00', 'total_amount' => '0.00'], + $data['cart'] + ); + + $detail = $this->getJson("/mini/product/{$product->id}")->assertOk()->json('data'); + $this->assertSame(0, $detail['cart_id']); + $this->assertSame('0.00', $detail['cart_quantity']); + } + + /** 商品详情:登录门店附加购物车行ID与数量 */ + public function test_product_detail_appends_cart_quantity(): void + { + [$store, $product] = $this->makeStoreWithProduct(); + $this->actingAsMiniStore($store); + $this->postJson('/mini/cart', ['product_id' => $product->id, 'quantity' => 4]); + $cartId = CartModel::first()->id; + + $data = $this->getJson("/mini/product/{$product->id}")->assertOk()->json('data'); + $this->assertSame($cartId, $data['cart_id']); + $this->assertSame('4.00', $data['cart_quantity']); + } + + /** 首页:登录门店附购物车悬浮球汇总,未登录为零值结构 */ + public function test_home_appends_cart_summary(): void + { + [$store, $product] = $this->makeStoreWithProduct('5.00'); + + // 未登录:零值结构 + $guest = $this->getJson('/mini/home')->assertOk()->json('data'); + $this->assertSame( + ['total_count' => 0, 'total_quantity' => '0.00', 'total_amount' => '0.00'], + $guest['cart'] + ); + + // 登录:汇总跟随购物车 + $this->actingAsMiniStore($store); + $this->postJson('/mini/cart', ['product_id' => $product->id, 'quantity' => 3]); + $data = $this->getJson('/mini/home')->assertOk()->json('data'); + $this->assertSame(1, $data['cart']['total_count']); + $this->assertSame('3.00', $data['cart']['total_quantity']); + $this->assertSame('15.00', $data['cart']['total_amount']); + } } diff --git a/tests/Feature/StoreOrderTest.php b/tests/Feature/StoreOrderTest.php index 5d4ef8e..b486334 100644 --- a/tests/Feature/StoreOrderTest.php +++ b/tests/Feature/StoreOrderTest.php @@ -2,8 +2,10 @@ namespace Tests\Feature; +use App\Models\BillModel; use App\Models\CustomerLevelModel; use App\Models\ProductModel; +use App\Models\PurchaseOrderModel; use App\Models\StoreModel; use App\Models\StoreOrderModel; use Modules\SystemTool\Models\SysFileModel; @@ -112,6 +114,77 @@ class StoreOrderTest extends ProcurementTestCase $this->assertSame(StoreOrderModel::STATUS_SUMMARIZED, $order->refresh()->status); } + /** 造一笔指定账单日期/状态的门店账单(默认总额 100.00 未支付) */ + private function makeStoreBill(StoreModel $store, string $billDate, array $attributes = []): BillModel + { + return BillModel::create(array_merge([ + 'bill_no' => 'ZD' . random_int(100000000000, 999999999999), + 'purchase_id' => PurchaseOrderModel::factory()->create()->id, + 'store_id' => $store->id, + 'bill_date' => $billDate, + 'total_amount' => '100.00', + 'status' => BillModel::STATUS_UNPAID, + ], $attributes)); + } + + /** 回款周期校验:存在逾期未回款账单(含审核中)时禁止下单,结清后恢复 */ + public function test_place_order_blocked_by_overdue_unpaid_bill(): void + { + $level = CustomerLevelModel::factory()->create(); + $store = StoreModel::factory()->paymentCycle(7)->create(['level_id' => $level->id]); + $product = ProductModel::factory()->create(['status' => ProductModel::STATUS_ON, 'cost_price' => '5.00']); + + // 账单日 10 天前(应结算日=3 天前,已逾期)且未支付 → 拒绝 + $this->makeStoreBill($store, now()->subDays(10)->toDateString()); + $this->actingAsMiniStore($store); + $response = $this->postJson('/mini/order', ['items' => [['product_id' => $product->id, 'quantity' => 1]]]); + $response->assertOk()->assertJsonPath('success', false); + $this->assertStringContainsString('已超过回款周期未回款', (string) $response->json('msg')); + $this->assertStringContainsString('¥100.00', (string) $response->json('msg')); + $this->assertSame(0, StoreOrderModel::count(), '拦截时不应生成订单'); + + // 审核中(已提交付款凭证)同样拦截 + $this->makeStoreBill($store, now()->subDays(10)->toDateString(), ['payment_id' => 999]); + $blocked = $this->postJson('/mini/order', ['items' => [['product_id' => $product->id, 'quantity' => 1]]]); + $blocked->assertJsonPath('success', false); + $this->assertStringContainsString('2 笔', (string) $blocked->json('msg')); + + // 全部结清后恢复下单 + BillModel::where('store_id', $store->id)->update(['status' => BillModel::STATUS_PAID]); + $this->postJson('/mini/order', ['items' => [['product_id' => $product->id, 'quantity' => 1]]]) + ->assertJsonPath('success', true); + } + + /** 回款周期校验:周期内账单与到期日当天均不拦截;回款周期 0 天=任何未回款账单(含当天)都拦截 */ + public function test_place_order_allowed_within_payment_cycle(): void + { + $level = CustomerLevelModel::factory()->create(); + $store = StoreModel::factory()->paymentCycle(7)->create(['level_id' => $level->id]); + $product = ProductModel::factory()->create(['status' => ProductModel::STATUS_ON, 'cost_price' => '5.00']); + $this->actingAsMiniStore($store); + + // 账单日 7 天前 → 应结算日=今天,尚未超过周期,允许下单 + $this->makeStoreBill($store, now()->subDays(7)->toDateString()); + $this->postJson('/mini/order', ['items' => [['product_id' => $product->id, 'quantity' => 1]]]) + ->assertJsonPath('success', true); + + // 回款周期 0 天:昨天的未回款账单拦截;当天的未回款账单同样拦截(立即结清口径) + $store->update(['payment_cycle_days' => 0]); + $this->makeStoreBill($store, now()->subDays(1)->toDateString()); + $this->postJson('/mini/order', ['items' => [['product_id' => $product->id, 'quantity' => 1]]]) + ->assertJsonPath('success', false); + BillModel::where('store_id', $store->id)->delete(); + $this->makeStoreBill($store, now()->toDateString()); + $response = $this->postJson('/mini/order', ['items' => [['product_id' => $product->id, 'quantity' => 1]]]); + $response->assertJsonPath('success', false); + $this->assertStringContainsString('当日结清', (string) $response->json('msg')); + + // 0 天周期下无未回款账单 → 正常下单 + BillModel::where('store_id', $store->id)->update(['status' => BillModel::STATUS_PAID]); + $this->postJson('/mini/order', ['items' => [['product_id' => $product->id, 'quantity' => 1]]]) + ->assertJsonPath('success', true); + } + /** 门店数据隔离:只能查看本店订单 */ public function test_order_data_isolated_between_stores(): void {