diff --git a/.phpunit.result.cache b/.phpunit.result.cache index 5c46ce7..d21ef37 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},"times":{"Tests\\Feature\\ExampleTest::test_the_application_returns_a_successful_response":0.004,"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.007,"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.051,"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.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.005,"Tests\\Feature\\MiniAuthTest::test_mini_token_cannot_access_admin_api":0.047,"Tests\\Feature\\ExportTest::test_export_xlsx_with_encoded_chinese_filename":0.122,"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.06,"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.048,"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.086,"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.045,"Tests\\Feature\\CartTest::test_update_quantity":0.05,"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.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.093,"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.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.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.054,"Tests\\Feature\\PurchaseGenerateTest::test_generate_with_selected_order_ids":0.103,"Tests\\Feature\\StoreOrderItemTest::test_place_order_snapshots_full_product_info":0.049,"Tests\\Feature\\StoreOrderItemTest::test_mini_detail_hides_cost_price":0.05,"Tests\\Feature\\StoreOrderItemTest::test_admin_detail_shows_cost_price_and_supplier":0.053,"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.062,"Tests\\Feature\\StoreOrderTest::test_soft_delete_only_cancelled_orders":0.061,"Tests\\Feature\\StoreOrderTest::test_soft_delete_rejected_for_non_cancelled_orders":0.248,"Tests\\Feature\\StoreOrderTest::test_soft_delete_twice_rejected":0.054,"Tests\\Feature\\PurchaseEditTest::test_detail_returns_store_matrix":0.106,"Tests\\Feature\\PurchaseEditTest::test_update_cell_syncs_order_item_and_totals":0.102,"Tests\\Feature\\PurchaseEditTest::test_update_row_cost_syncs_all_order_items":0.105,"Tests\\Feature\\PurchaseEditTest::test_update_row_weight_distributed_by_quantity":0.032,"Tests\\Feature\\PurchaseEditTest::test_update_row_requires_at_least_one_field":0.101,"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.105,"Tests\\Feature\\PurchaseEditTest::test_update_row_syncs_to_product_catalog":0.019,"Tests\\Feature\\PurchaseEditTest::test_cell_detail_returns_items_with_order_info":0.103,"Tests\\Feature\\PurchaseEditTest::test_cell_detail_rejects_missing_params":0.101,"Tests\\Feature\\PurchaseEditTest::test_update_cell_weight_cascades_totals":0.103,"Tests\\Feature\\PurchaseEditTest::test_cell_sync_refreshes_snapshot_and_cascades":0.012,"Tests\\Feature\\PurchaseEditTest::test_edits_rejected_when_purchase_completed":0.11,"Tests\\Feature\\PurchaseEditTest::test_update_row_does_not_touch_manual_weight":0.109,"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.102,"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.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.007,"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.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.048,"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.068,"Tests\\Feature\\MiniBillTest::test_bill_data_isolated_between_stores":0.088,"Tests\\Feature\\MiniBillTest::test_bill_detail_outputs_bill_items_and_orders":0.049,"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.049,"Tests\\Feature\\BillExportTest::test_export_merges_items_across_bills":0.106,"Tests\\Feature\\BillExportTest::test_export_filters_by_root_category":0.052,"Tests\\Feature\\BillExportTest::test_export_empty_category_rejected":0.089,"Tests\\Feature\\BillExportTest::test_export_requires_export_permission":0.049,"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.051,"Tests\\Feature\\MiniNoticeTest::test_broadcast_read_creates_personal_copy":0.048,"Tests\\Feature\\MiniNoticeTest::test_personal_notice_read_marks_directly":0.046,"Tests\\Feature\\MiniNoticeTest::test_other_users_notice_not_found":0.007,"Tests\\Feature\\StoreOrderTest::test_manual_status_flow_only_accept_and_cancel":0.103,"Tests\\Feature\\StoreOrderTest::test_manual_status_flow_rejects_delivery_and_completion":0.151,"Tests\\Feature\\StoreOrderTest::test_batch_status_only_accept_and_cancel":0.102,"Tests\\Feature\\BillPaymentTest::test_order_status_progresses_via_business_chain":0.062,"Tests\\Feature\\BillPaymentTest::test_manual_pay_accumulates_store_total_purchase_amount":0.068,"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.048,"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.046,"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.044,"Tests\\Feature\\BillPaymentTest::test_admin_order_list_and_detail_show_bill_info":0.073,"Tests\\Feature\\BillPaymentTest::test_payment_detail_returns_voucher_urls":0.061,"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.06,"Tests\\Feature\\ContainerReturnTest::test_return_containers_deducts_amount":0.061,"Tests\\Feature\\ContainerReturnTest::test_mixed_sign_containers":0.06,"Tests\\Feature\\ContainerReturnTest::test_zero_containers_writes_no_record":0.061,"Tests\\Feature\\ContainerReturnTest::test_non_integer_containers_rejected":0.048,"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.089,"Tests\\Feature\\ContainerReturnTest::test_export_filters_by_selected_stores":0.13,"Tests\\Feature\\ContainerReturnTest::test_export_invalid_params_rejected":0.01,"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.01,"Tests\\Feature\\DashboardTest::test_overview_excludes_cancelled_orders":0.05,"Tests\\Feature\\DashboardTest::test_week_growth_rate":0.055,"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.051,"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.089,"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.048,"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.01,"Tests\\Feature\\ProductCostPriceTest::test_create_product_with_cost_price":0.007,"Tests\\Feature\\ProductCostPriceTest::test_batch_price_cost_row_update":0.051,"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.106,"Tests\\Feature\\PurchaseEditTest::test_store_item_update_merges_multi_order_rows":0.06,"Tests\\Feature\\PurchaseEditTest::test_store_item_remove_deletes_and_recalculates":0.1,"Tests\\Feature\\StoreOrderItemTest::test_update_store_item_recalculates_totals":0.06,"Tests\\Feature\\StoreOrderItemTest::test_store_item_edits_rejected_when_purchase_completed":0.062,"Tests\\Feature\\StoreOrderItemTest::test_update_store_item_validates_negative_values":0.058,"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.06,"Tests\\Feature\\ExportTest::test_export_lists_all_catalog_products_with_totals_row":0.104,"Tests\\Feature\\ExportTest::test_export_supplier_filter":0.104,"Tests\\Feature\\ExportTest::test_export_stores_multi_sheet":0.106,"Tests\\Feature\\ExportTest::test_export_stores_single_store":0.102,"Tests\\Feature\\ExportTest::test_export_stores_without_items_rejected":0.144,"Tests\\Feature\\ExportTest::test_export_suppliers_multi_sheet":0.104,"Tests\\Feature\\ExportTest::test_export_suppliers_single":0.101,"Tests\\Feature\\ExportTest::test_export_suppliers_without_items_rejected":0.101,"Tests\\Feature\\PurchaseEditTest::test_update_row_cost_change_reprices_items_by_store_level":0.102,"Tests\\Feature\\PurchaseEditTest::test_update_row_without_cost_change_keeps_item_price":0.105,"Tests\\Feature\\CartTest::test_update_other_stores_item_rejected":0.09,"Tests\\Feature\\CartTest::test_delete_other_stores_item_rejected":0.089,"Tests\\Feature\\CartTest::test_cart_isolated_between_stores":0.09,"Tests\\Feature\\MiniAuthTest::test_login_issues_token_for_valid_credentials":0.127,"Tests\\Feature\\MiniAuthTest::test_login_rejects_wrong_password":0.123,"Tests\\Feature\\MiniAuthTest::test_login_rejects_unknown_username":0.002,"Tests\\Feature\\MiniAuthTest::test_disabled_store_cannot_login":0.123,"Tests\\Feature\\MiniAuthTest::test_login_validates_required_params":0.002,"Tests\\Feature\\MiniAuthTest::test_login_token_accesses_protected_routes":0.126,"Tests\\Feature\\MiniAuthTest::test_set_password_succeeds":0.207,"Tests\\Feature\\MiniAuthTest::test_set_password_rejects_wrong_old_password":0.124,"Tests\\Feature\\MiniAuthTest::test_set_password_validates_confirmation":0.045,"Tests\\Feature\\BillExportTest::test_mini_export_scoped_to_own_store":0.093,"Tests\\Feature\\MiniNoticeTest::test_other_stores_notice_not_found":0.086,"Tests\\Feature\\MiniProductTest::test_product_detail_store_sees_level_price":0.047,"Tests\\Feature\\MiniProductTest::test_product_list_store_sees_level_price":0.045,"Tests\\Feature\\MiniStoreTest::test_info_returns_current_store":0.045,"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.007,"Tests\\Feature\\ProductImportExportTest::test_import_creates_products_and_auto_supplier":0.043,"Tests\\Feature\\ProductImportExportTest::test_import_matches_existing_supplier":0.019,"Tests\\Feature\\ProductImportExportTest::test_import_errors_abort_all_rows":0.017,"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.01,"Tests\\Feature\\ProductImportExportTest::test_list_filters_by_market":0.008,"Tests\\Feature\\MiniReportTest::test_report_aggregates_total_and_item_percent":0.047,"Tests\\Feature\\MiniReportTest::test_report_accumulates_same_product_across_orders":0.048,"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.051,"Tests\\Feature\\MiniReportTest::test_report_defaults_to_current_month":0.047,"Tests\\Feature\\MiniReportTest::test_report_data_isolated_between_stores":0.086,"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.009,"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.072,"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.057,"Tests\\Feature\\BillPaymentTest::test_generate_bill_saves_after_sale_and_remark":0.119,"Tests\\Feature\\ProductCostPriceTest::test_product_price_unit_default_and_editable":0.013,"Tests\\Feature\\StoreOrderTest::test_place_order_blocked_by_overdue_unpaid_bill":0.057,"Tests\\Feature\\StoreOrderTest::test_place_order_allowed_within_payment_cycle":0.056,"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.049,"Tests\\Feature\\MiniProductTest::test_home_appends_cart_summary":0.065,"Tests\\Feature\\MiniOnlinePaymentTest::test_sign_matches_document_example":0.002,"Tests\\Feature\\MiniOnlinePaymentTest::test_create_online_payment_success":0.068,"Tests\\Feature\\MiniOnlinePaymentTest::test_create_online_payment_validation":0.097,"Tests\\Feature\\MiniOnlinePaymentTest::test_create_fails_when_code2session_fails":0.047,"Tests\\Feature\\MiniOnlinePaymentTest::test_create_gateway_failure_releases_bills":0.052,"Tests\\Feature\\MiniOnlinePaymentTest::test_notify_settles_payment":0.053,"Tests\\Feature\\MiniOnlinePaymentTest::test_notify_rejects_invalid_messages":0.058,"Tests\\Feature\\MiniOnlinePaymentTest::test_query_syncs_gateway_status":0.061,"Tests\\Feature\\MiniOnlinePaymentTest::test_query_rejects_other_store_payment":0.088,"Tests\\Feature\\MiniOnlinePaymentTest::test_offline_payment_flow_unaffected":0.049,"Tests\\Feature\\BillPaymentTest::test_generate_bill_positive_after_sale_added_to_total":0.114,"Tests\\Feature\\BillPaymentTest::test_generate_bill_rejects_non_numeric_after_sale":0.048,"Tests\\Feature\\ClientSpecialTest::test_batch_add_creates_specials":0.01,"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.007,"Tests\\Feature\\MiniSpecialTest::test_guest_sees_specials_with_null_price":0.045,"Tests\\Feature\\MiniSpecialTest::test_store_sees_level_price":0.048,"Tests\\Feature\\MiniSpecialTest::test_disabled_special_and_off_shelf_product_excluded":0.045,"Tests\\Feature\\MiniSpecialTest::test_specials_sorted_by_sort_asc":0.087,"Tests\\Feature\\MiniSpecialTest::test_cart_quantity_attached_for_store":0.046,"Tests\\Feature\\MiniOnlinePaymentTest::test_aes_encryption_matches_demo_golden":0.002,"Tests\\Feature\\MiniOnlinePaymentTest::test_notify_envelope_round_trip":0.005,"Tests\\Feature\\MiniOnlinePaymentTest::test_swapped_key_config_fails_with_clear_message":0.004,"Tests\\Feature\\MiniWechatPaymentTest::test_create_wechat_payment_success":0.068,"Tests\\Feature\\MiniWechatPaymentTest::test_create_wechat_payment_gateway_failure_releases_bills":0.053,"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.052,"Tests\\Feature\\MiniWechatPaymentTest::test_wechat_query_syncs_status":0.057,"Tests\\Feature\\MiniWechatPaymentTest::test_wangpu_channel_remains_default":0.049}} \ 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},"times":{"Tests\\Feature\\ExampleTest::test_the_application_returns_a_successful_response":0.004,"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.007,"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.051,"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.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.005,"Tests\\Feature\\MiniAuthTest::test_mini_token_cannot_access_admin_api":0.047,"Tests\\Feature\\ExportTest::test_export_xlsx_with_encoded_chinese_filename":0.144,"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.048,"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.086,"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.045,"Tests\\Feature\\CartTest::test_update_quantity":0.05,"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.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.093,"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.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.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.054,"Tests\\Feature\\PurchaseGenerateTest::test_generate_with_selected_order_ids":0.103,"Tests\\Feature\\StoreOrderItemTest::test_place_order_snapshots_full_product_info":0.049,"Tests\\Feature\\StoreOrderItemTest::test_mini_detail_hides_cost_price":0.05,"Tests\\Feature\\StoreOrderItemTest::test_admin_detail_shows_cost_price_and_supplier":0.053,"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.062,"Tests\\Feature\\StoreOrderTest::test_soft_delete_only_cancelled_orders":0.061,"Tests\\Feature\\StoreOrderTest::test_soft_delete_rejected_for_non_cancelled_orders":0.248,"Tests\\Feature\\StoreOrderTest::test_soft_delete_twice_rejected":0.054,"Tests\\Feature\\PurchaseEditTest::test_detail_returns_store_matrix":0.106,"Tests\\Feature\\PurchaseEditTest::test_update_cell_syncs_order_item_and_totals":0.102,"Tests\\Feature\\PurchaseEditTest::test_update_row_cost_syncs_all_order_items":0.105,"Tests\\Feature\\PurchaseEditTest::test_update_row_weight_distributed_by_quantity":0.032,"Tests\\Feature\\PurchaseEditTest::test_update_row_requires_at_least_one_field":0.101,"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.105,"Tests\\Feature\\PurchaseEditTest::test_update_row_syncs_to_product_catalog":0.019,"Tests\\Feature\\PurchaseEditTest::test_cell_detail_returns_items_with_order_info":0.103,"Tests\\Feature\\PurchaseEditTest::test_cell_detail_rejects_missing_params":0.101,"Tests\\Feature\\PurchaseEditTest::test_update_cell_weight_cascades_totals":0.103,"Tests\\Feature\\PurchaseEditTest::test_cell_sync_refreshes_snapshot_and_cascades":0.012,"Tests\\Feature\\PurchaseEditTest::test_edits_rejected_when_purchase_completed":0.11,"Tests\\Feature\\PurchaseEditTest::test_update_row_does_not_touch_manual_weight":0.109,"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.102,"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.007,"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.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.048,"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.068,"Tests\\Feature\\MiniBillTest::test_bill_data_isolated_between_stores":0.088,"Tests\\Feature\\MiniBillTest::test_bill_detail_outputs_bill_items_and_orders":0.049,"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.049,"Tests\\Feature\\BillExportTest::test_export_merges_items_across_bills":0.054,"Tests\\Feature\\BillExportTest::test_export_filters_by_root_category":0.051,"Tests\\Feature\\BillExportTest::test_export_empty_category_rejected":0.049,"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.051,"Tests\\Feature\\MiniNoticeTest::test_broadcast_read_creates_personal_copy":0.048,"Tests\\Feature\\MiniNoticeTest::test_personal_notice_read_marks_directly":0.046,"Tests\\Feature\\MiniNoticeTest::test_other_users_notice_not_found":0.007,"Tests\\Feature\\StoreOrderTest::test_manual_status_flow_only_accept_and_cancel":0.103,"Tests\\Feature\\StoreOrderTest::test_manual_status_flow_rejects_delivery_and_completion":0.151,"Tests\\Feature\\StoreOrderTest::test_batch_status_only_accept_and_cancel":0.102,"Tests\\Feature\\BillPaymentTest::test_order_status_progresses_via_business_chain":0.062,"Tests\\Feature\\BillPaymentTest::test_manual_pay_accumulates_store_total_purchase_amount":0.068,"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.048,"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.046,"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.044,"Tests\\Feature\\BillPaymentTest::test_admin_order_list_and_detail_show_bill_info":0.073,"Tests\\Feature\\BillPaymentTest::test_payment_detail_returns_voucher_urls":0.061,"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.06,"Tests\\Feature\\ContainerReturnTest::test_return_containers_deducts_amount":0.059,"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.048,"Tests\\Feature\\ContainerReturnTest::test_manual_create_and_delete_routes_removed":0.007,"Tests\\Feature\\ContainerReturnTest::test_export_matrix_merges_same_day_and_zero_fills":0.091,"Tests\\Feature\\ContainerReturnTest::test_export_filters_by_selected_stores":0.132,"Tests\\Feature\\ContainerReturnTest::test_export_invalid_params_rejected":0.022,"Tests\\Feature\\ContainerReturnTest::test_export_empty_range_rejected":0.008,"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.01,"Tests\\Feature\\DashboardTest::test_overview_excludes_cancelled_orders":0.05,"Tests\\Feature\\DashboardTest::test_week_growth_rate":0.055,"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.051,"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.089,"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.048,"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.01,"Tests\\Feature\\ProductCostPriceTest::test_create_product_with_cost_price":0.007,"Tests\\Feature\\ProductCostPriceTest::test_batch_price_cost_row_update":0.051,"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.106,"Tests\\Feature\\PurchaseEditTest::test_store_item_update_merges_multi_order_rows":0.06,"Tests\\Feature\\PurchaseEditTest::test_store_item_remove_deletes_and_recalculates":0.1,"Tests\\Feature\\StoreOrderItemTest::test_update_store_item_recalculates_totals":0.06,"Tests\\Feature\\StoreOrderItemTest::test_store_item_edits_rejected_when_purchase_completed":0.062,"Tests\\Feature\\StoreOrderItemTest::test_update_store_item_validates_negative_values":0.058,"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.06,"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.102,"Tests\\Feature\\ExportTest::test_export_stores_single_store":0.101,"Tests\\Feature\\ExportTest::test_export_stores_without_items_rejected":0.144,"Tests\\Feature\\ExportTest::test_export_suppliers_multi_sheet":0.103,"Tests\\Feature\\ExportTest::test_export_suppliers_single":0.103,"Tests\\Feature\\ExportTest::test_export_suppliers_without_items_rejected":0.102,"Tests\\Feature\\PurchaseEditTest::test_update_row_cost_change_reprices_items_by_store_level":0.102,"Tests\\Feature\\PurchaseEditTest::test_update_row_without_cost_change_keeps_item_price":0.105,"Tests\\Feature\\CartTest::test_update_other_stores_item_rejected":0.09,"Tests\\Feature\\CartTest::test_delete_other_stores_item_rejected":0.089,"Tests\\Feature\\CartTest::test_cart_isolated_between_stores":0.09,"Tests\\Feature\\MiniAuthTest::test_login_issues_token_for_valid_credentials":0.127,"Tests\\Feature\\MiniAuthTest::test_login_rejects_wrong_password":0.123,"Tests\\Feature\\MiniAuthTest::test_login_rejects_unknown_username":0.002,"Tests\\Feature\\MiniAuthTest::test_disabled_store_cannot_login":0.123,"Tests\\Feature\\MiniAuthTest::test_login_validates_required_params":0.002,"Tests\\Feature\\MiniAuthTest::test_login_token_accesses_protected_routes":0.126,"Tests\\Feature\\MiniAuthTest::test_set_password_succeeds":0.207,"Tests\\Feature\\MiniAuthTest::test_set_password_rejects_wrong_old_password":0.124,"Tests\\Feature\\MiniAuthTest::test_set_password_validates_confirmation":0.045,"Tests\\Feature\\BillExportTest::test_mini_export_scoped_to_own_store":0.093,"Tests\\Feature\\MiniNoticeTest::test_other_stores_notice_not_found":0.086,"Tests\\Feature\\MiniProductTest::test_product_detail_store_sees_level_price":0.047,"Tests\\Feature\\MiniProductTest::test_product_list_store_sees_level_price":0.045,"Tests\\Feature\\MiniStoreTest::test_info_returns_current_store":0.045,"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.007,"Tests\\Feature\\ProductImportExportTest::test_import_creates_products_and_auto_supplier":0.043,"Tests\\Feature\\ProductImportExportTest::test_import_matches_existing_supplier":0.019,"Tests\\Feature\\ProductImportExportTest::test_import_errors_abort_all_rows":0.017,"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.01,"Tests\\Feature\\ProductImportExportTest::test_list_filters_by_market":0.008,"Tests\\Feature\\MiniReportTest::test_report_aggregates_total_and_item_percent":0.047,"Tests\\Feature\\MiniReportTest::test_report_accumulates_same_product_across_orders":0.048,"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.051,"Tests\\Feature\\MiniReportTest::test_report_defaults_to_current_month":0.047,"Tests\\Feature\\MiniReportTest::test_report_data_isolated_between_stores":0.086,"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.009,"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.072,"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.058,"Tests\\Feature\\BillPaymentTest::test_generate_bill_saves_after_sale_and_remark":0.119,"Tests\\Feature\\ProductCostPriceTest::test_product_price_unit_default_and_editable":0.013,"Tests\\Feature\\StoreOrderTest::test_place_order_blocked_by_overdue_unpaid_bill":0.057,"Tests\\Feature\\StoreOrderTest::test_place_order_allowed_within_payment_cycle":0.056,"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.049,"Tests\\Feature\\MiniProductTest::test_home_appends_cart_summary":0.065,"Tests\\Feature\\MiniOnlinePaymentTest::test_sign_matches_document_example":0.002,"Tests\\Feature\\MiniOnlinePaymentTest::test_create_online_payment_success":0.068,"Tests\\Feature\\MiniOnlinePaymentTest::test_create_online_payment_validation":0.097,"Tests\\Feature\\MiniOnlinePaymentTest::test_create_fails_when_code2session_fails":0.047,"Tests\\Feature\\MiniOnlinePaymentTest::test_create_gateway_failure_releases_bills":0.052,"Tests\\Feature\\MiniOnlinePaymentTest::test_notify_settles_payment":0.053,"Tests\\Feature\\MiniOnlinePaymentTest::test_notify_rejects_invalid_messages":0.058,"Tests\\Feature\\MiniOnlinePaymentTest::test_query_syncs_gateway_status":0.061,"Tests\\Feature\\MiniOnlinePaymentTest::test_query_rejects_other_store_payment":0.088,"Tests\\Feature\\MiniOnlinePaymentTest::test_offline_payment_flow_unaffected":0.049,"Tests\\Feature\\BillPaymentTest::test_generate_bill_positive_after_sale_added_to_total":0.114,"Tests\\Feature\\BillPaymentTest::test_generate_bill_rejects_non_numeric_after_sale":0.048,"Tests\\Feature\\ClientSpecialTest::test_batch_add_creates_specials":0.01,"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.007,"Tests\\Feature\\MiniSpecialTest::test_guest_sees_specials_with_null_price":0.045,"Tests\\Feature\\MiniSpecialTest::test_store_sees_level_price":0.048,"Tests\\Feature\\MiniSpecialTest::test_disabled_special_and_off_shelf_product_excluded":0.045,"Tests\\Feature\\MiniSpecialTest::test_specials_sorted_by_sort_asc":0.087,"Tests\\Feature\\MiniSpecialTest::test_cart_quantity_attached_for_store":0.046,"Tests\\Feature\\MiniOnlinePaymentTest::test_aes_encryption_matches_demo_golden":0.002,"Tests\\Feature\\MiniOnlinePaymentTest::test_notify_envelope_round_trip":0.005,"Tests\\Feature\\MiniOnlinePaymentTest::test_swapped_key_config_fails_with_clear_message":0.004,"Tests\\Feature\\MiniWechatPaymentTest::test_create_wechat_payment_success":0.068,"Tests\\Feature\\MiniWechatPaymentTest::test_create_wechat_payment_gateway_failure_releases_bills":0.053,"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.052,"Tests\\Feature\\MiniWechatPaymentTest::test_wechat_query_syncs_status":0.057,"Tests\\Feature\\MiniWechatPaymentTest::test_wangpu_channel_remains_default":0.049}} \ No newline at end of file diff --git a/app/Exports/BillExport.php b/app/Exports/BillExport.php index 5b4a542..59dccf0 100644 --- a/app/Exports/BillExport.php +++ b/app/Exports/BillExport.php @@ -10,6 +10,9 @@ use App\Services\BillDetailService; use Illuminate\Support\Collection; use Maatwebsite\Excel\Concerns\FromCollection; use Maatwebsite\Excel\Concerns\WithStyles; +use PhpOffice\PhpSpreadsheet\Style\Alignment; +use PhpOffice\PhpSpreadsheet\Style\Border; +use PhpOffice\PhpSpreadsheet\Style\Fill; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; /** @@ -18,6 +21,15 @@ use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; */ class BillExport implements FromCollection, WithStyles { + /** 数量列填充色(浅黄) */ + private const string QUANTITY_FILL = 'FFFFF2CC'; + + /** 金额列填充色(浅红) */ + private const string AMOUNT_FILL = 'FFFFCCCC'; + + /** 金额格式:¥ + 两位小数 */ + private const string CURRENCY_FORMAT = '"¥"#,##0.00'; + private ?Collection $rows = null; /** @var array 特殊行索引(1 起)=> 类型(title/scope/header/summary/grand) */ @@ -26,9 +38,15 @@ class BillExport implements FromCollection, WithStyles /** @var array 标签需左合并 A:G 的合计行索引 */ private array $mergeRows = []; + /** @var array 条件填色单元格(F=数量浅黄,H=金额浅红,非 0 填色) */ + private array $fillCells = []; + /** 列头所在行索引 */ private int $headerRow = 1; + /** 数据末行索引 */ + private int $lastRow = 1; + /** * @param Collection $bills 勾选账单(已按 bill_date/id 排序,with store) * @param int $categoryId 一级分类ID(0=全部) @@ -104,6 +122,8 @@ class BillExport implements FromCollection, WithStyles (float) $item['amount'], ]; $rowIndex++; + $this->fillCells[] = ['row' => $rowIndex, 'column' => 'F', 'value' => (float) $item['quantity']]; + $this->fillCells[] = ['row' => $rowIndex, 'column' => 'H', 'value' => (float) $item['amount']]; $totalQuantity += (int) $item['quantity']; $totalWeight = bcadd($totalWeight, $item['weight'], 3); $totalAmount = bcadd($totalAmount, $item['amount'], 2); @@ -112,6 +132,8 @@ class BillExport implements FromCollection, WithStyles // 商品合计行 $rows[] = ['', '合计', '', '', '', $totalQuantity, (float) $totalWeight, (float) $totalAmount]; $this->specialRows[++$rowIndex] = 'summary'; + $this->fillCells[] = ['row' => $rowIndex, 'column' => 'F', 'value' => (float) $totalQuantity]; + $this->fillCells[] = ['row' => $rowIndex, 'column' => 'H', 'value' => (float) $totalAmount]; // 配送费/附加金额/售后金额/总金额(账单级费用全额汇总,不受分类过滤影响) $deliveryTotal = '0'; @@ -131,24 +153,30 @@ class BillExport implements FromCollection, WithStyles $rows[] = ['配送费合计', '', '', '', '', '', '', (float) $deliveryTotal]; $this->specialRows[++$rowIndex] = 'summary'; $this->mergeRows[] = $rowIndex; + $this->fillCells[] = ['row' => $rowIndex, 'column' => 'H', 'value' => (float) $deliveryTotal]; $rows[] = ['附加金额合计(周转筐 ' . $boxNum . ' 个 / 周转托盘 ' . $trayNum . ' 个)', '', '', '', '', '', '', (float) $addedTotal]; $this->specialRows[++$rowIndex] = 'summary'; $this->mergeRows[] = $rowIndex; + $this->fillCells[] = ['row' => $rowIndex, 'column' => 'H', 'value' => (float) $addedTotal]; $rows[] = ['售后金额合计(可正负)', '', '', '', '', '', '', (float) $afterSaleTotal]; $this->specialRows[++$rowIndex] = 'summary'; $this->mergeRows[] = $rowIndex; + $this->fillCells[] = ['row' => $rowIndex, 'column' => 'H', 'value' => (float) $afterSaleTotal]; $rows[] = ['总金额(商品金额+配送费+附加金额+售后金额)', '', '', '', '', '', '', (float) $grandTotal]; $this->specialRows[++$rowIndex] = 'grand'; $this->mergeRows[] = $rowIndex; + $this->fillCells[] = ['row' => $rowIndex, 'column' => 'H', 'value' => (float) $grandTotal]; + $this->lastRow = $rowIndex; return $this->rows = collect($rows); } /** - * 标题/列头/合计行加粗,合计行标签左合并 A:G,冻结列头 + * 标题/列头/合计行加粗,合计行标签左合并 A:G;全表居中 + 全边框;金额类列货币格式; + * 数量(浅黄)/金额(浅红)非 0 条件填色(0 不填、列头固定填色),冻结列头 */ public function styles(Worksheet $sheet): array { @@ -168,6 +196,43 @@ class BillExport implements FromCollection, WithStyles $sheet->mergeCells('A' . $row . ':G' . $row); } + // 全部单元格水平/垂直居中(合计行标签保持左对齐) + $sheet->getStyle('A1:H' . $this->lastRow) + ->getAlignment() + ->setHorizontal(Alignment::HORIZONTAL_CENTER) + ->setVertical(Alignment::VERTICAL_CENTER); + foreach ($this->mergeRows as $row) { + $sheet->getStyle('A' . $row)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_LEFT); + } + + // 表格区域(列头 → 末行)添加所有边框 + $sheet->getStyle('A' . $this->headerRow . ':H' . $this->lastRow) + ->getBorders() + ->getAllBorders() + ->setBorderStyle(Border::BORDER_THIN); + + // 金额类列(单价/金额)货币格式:¥ + 两位小数 + foreach (['E', 'H'] as $column) { + $sheet->getStyle($column . ($this->headerRow + 1) . ':' . $column . $this->lastRow) + ->getNumberFormat() + ->setFormatCode(self::CURRENCY_FORMAT); + } + + // 列头固定填色:数量列(F)浅黄、金额列(H)浅红 + $this->fillCell($sheet, 'F' . $this->headerRow, self::QUANTITY_FILL); + $this->fillCell($sheet, 'H' . $this->headerRow, self::AMOUNT_FILL); + + // 数据区条件填色:非 0 填色,0 无背景(金额含负值) + foreach ($this->fillCells as $cell) { + if ((float) $cell['value'] !== 0.0) { + $this->fillCell( + $sheet, + $cell['column'] . $cell['row'], + $cell['column'] === 'F' ? self::QUANTITY_FILL : self::AMOUNT_FILL + ); + } + } + $styles = []; foreach ($this->specialRows as $row => $type) { $style = match ($type) { @@ -184,6 +249,18 @@ class BillExport implements FromCollection, WithStyles return $styles; } + /** + * 单元格纯色填充 + */ + private function fillCell(Worksheet $sheet, string $coordinate, string $argb): void + { + $sheet->getStyle($coordinate) + ->getFill() + ->setFillType(Fill::FILL_SOLID) + ->getStartColor() + ->setARGB($argb); + } + /** * 按一级分类过滤明细:沿 parent_id 上溯取根分类(商品含软删除,保证历史账单可导出), * 仅保留根分类为所选分类的行 diff --git a/app/Exports/ContainerReturnExport.php b/app/Exports/ContainerReturnExport.php index 9653745..b4f99ad 100644 --- a/app/Exports/ContainerReturnExport.php +++ b/app/Exports/ContainerReturnExport.php @@ -8,6 +8,10 @@ use Illuminate\Support\Collection; use Maatwebsite\Excel\Concerns\FromCollection; use Maatwebsite\Excel\Concerns\WithStrictNullComparison; use Maatwebsite\Excel\Concerns\WithStyles; +use PhpOffice\PhpSpreadsheet\Cell\Coordinate; +use PhpOffice\PhpSpreadsheet\Style\Alignment; +use PhpOffice\PhpSpreadsheet\Style\Border; +use PhpOffice\PhpSpreadsheet\Style\Fill; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; /** @@ -20,14 +24,32 @@ use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; */ class ContainerReturnExport implements FromCollection, WithStyles, WithStrictNullComparison { + /** 门店列填充色(浅蓝) */ + private const string STORE_FILL = 'FFDDEBF7'; + + /** 合计列填充色(浅红) */ + private const string AMOUNT_FILL = 'FFFFCCCC'; + + /** 金额格式:¥ + 两位小数 */ + private const string CURRENCY_FORMAT = '"¥"#,##0.00'; + private ?Collection $rows = null; /** @var array 特殊行索引(1 起)=> 类型(title/scope/header/total) */ private array $specialRows = []; + /** @var array, total: float}> 日期行索引 => 填色判断数据 */ + private array $rowData = []; + + /** @var array{stores: array, total: float} 合计行填色判断数据 */ + private array $summaryTotals = ['stores' => [], 'total' => 0.0]; + /** 列头所在行索引 */ private int $headerRow = 1; + /** 数据末行索引 */ + private int $lastRow = 1; + /** * @param Collection $records 区间内压回筐记录 * @param array $storeNames 门店ID => 名称(导出列,升序) @@ -90,16 +112,18 @@ class ContainerReturnExport implements FromCollection, WithStyles, WithStrictNul $dateKey = $date->format('Y-m-d'); $row = [$dateKey]; $rowTotal = '0'; + $storeValues = []; foreach ($storeIds as $storeId) { $amount = $amounts[$dateKey][$storeId] ?? '0.00'; $row[] = (float) $amount; + $storeValues[$storeId] = (float) $amount; $rowTotal = bcadd($rowTotal, $amount, 2); $columnTotals[$storeId] = bcadd($columnTotals[$storeId], $amount, 2); } $grandTotal = bcadd($grandTotal, $rowTotal, 2); $row[] = (float) $rowTotal; $rows[] = $row; - $rowIndex++; + $this->rowData[++$rowIndex] = ['stores' => $storeValues, 'total' => (float) $rowTotal]; } // 合计行:各门店列合计 + 总计 @@ -110,12 +134,18 @@ class ContainerReturnExport implements FromCollection, WithStyles, WithStrictNul $totalRow[] = (float) $grandTotal; $rows[] = $totalRow; $this->specialRows[++$rowIndex] = 'total'; + $this->lastRow = $rowIndex; + $this->summaryTotals = [ + 'stores' => array_map(floatval(...), $columnTotals), + 'total' => (float) $grandTotal, + ]; return $this->rows = collect($rows); } /** - * 标题/列头/合计行加粗,冻结列头行 + * 标题/列头/合计行加粗;全表居中 + 全边框;金额区货币格式; + * 门店列(浅蓝)/合计列(浅红)非 0 条件填色(0 不填、列头固定填色),冻结列头行 */ public function styles(Worksheet $sheet): array { @@ -128,6 +158,51 @@ class ContainerReturnExport implements FromCollection, WithStyles, WithStrictNul $sheet->getColumnDimensionByColumn($column)->setWidth(12); } + $storeIds = array_map(intval(...), array_keys($this->storeNames)); + $lastColumn = Coordinate::stringFromColumnIndex($columnCount); + + // 全部单元格水平/垂直居中 + $sheet->getStyle('A1:' . $lastColumn . $this->lastRow) + ->getAlignment() + ->setHorizontal(Alignment::HORIZONTAL_CENTER) + ->setVertical(Alignment::VERTICAL_CENTER); + + // 表格区域(列头 → 合计行)添加所有边框 + $sheet->getStyle('A' . $this->headerRow . ':' . $lastColumn . $this->lastRow) + ->getBorders() + ->getAllBorders() + ->setBorderStyle(Border::BORDER_THIN); + + // 金额区(门店列 + 合计列)货币格式:¥ + 两位小数 + $sheet->getStyle('B' . ($this->headerRow + 1) . ':' . $lastColumn . $this->lastRow) + ->getNumberFormat() + ->setFormatCode(self::CURRENCY_FORMAT); + + // 门店列(B 起):非 0 浅蓝,0 无背景,列头固定浅蓝 + foreach ($storeIds as $i => $storeId) { + $column = Coordinate::stringFromColumnIndex(2 + $i); + $this->fillCell($sheet, $column . $this->headerRow, self::STORE_FILL); + foreach ($this->rowData as $row => $data) { + if (($data['stores'][$storeId] ?? 0.0) !== 0.0) { + $this->fillCell($sheet, $column . $row, self::STORE_FILL); + } + } + if (($this->summaryTotals['stores'][$storeId] ?? 0.0) !== 0.0) { + $this->fillCell($sheet, $column . $this->lastRow, self::STORE_FILL); + } + } + + // 合计列(末列):非 0 浅红,0 无背景,列头固定浅红 + $this->fillCell($sheet, $lastColumn . $this->headerRow, self::AMOUNT_FILL); + foreach ($this->rowData as $row => $data) { + if ($data['total'] !== 0.0) { + $this->fillCell($sheet, $lastColumn . $row, self::AMOUNT_FILL); + } + } + if ($this->summaryTotals['total'] !== 0.0) { + $this->fillCell($sheet, $lastColumn . $this->lastRow, self::AMOUNT_FILL); + } + $styles = []; foreach ($this->specialRows as $row => $type) { $style = match ($type) { @@ -142,4 +217,16 @@ class ContainerReturnExport implements FromCollection, WithStyles, WithStrictNul return $styles; } + + /** + * 单元格纯色填充 + */ + private function fillCell(Worksheet $sheet, string $coordinate, string $argb): void + { + $sheet->getStyle($coordinate) + ->getFill() + ->setFillType(Fill::FILL_SOLID) + ->getStartColor() + ->setARGB($argb); + } } diff --git a/app/Exports/PurchaseOrderExport.php b/app/Exports/PurchaseOrderExport.php index 256f797..c18f835 100644 --- a/app/Exports/PurchaseOrderExport.php +++ b/app/Exports/PurchaseOrderExport.php @@ -14,22 +14,40 @@ use Maatwebsite\Excel\Concerns\FromCollection; use Maatwebsite\Excel\Concerns\WithStrictNullComparison; use Maatwebsite\Excel\Concerns\WithStyles; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; +use PhpOffice\PhpSpreadsheet\Style\Alignment; +use PhpOffice\PhpSpreadsheet\Style\Border; +use PhpOffice\PhpSpreadsheet\Style\Fill; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; /** * 采购单商品明细导出:系统全部商品行(含本采购单无订货的商品,数量 0), - * 支持按供应商筛选;行尾合计 + 门店列合计;门店列整列填充突出颜色 + * 支持按供应商筛选;行尾合计 + 门店列合计;数量/金额/门店数量按值条件填色 */ class PurchaseOrderExport implements FromCollection, WithStrictNullComparison, WithStyles { - /** 门店列填充色(浅橙,突出显示) */ - private const string STORE_FILL = 'FFFFF7E6'; + /** 数量列填充色(浅黄) */ + private const string QUANTITY_FILL = 'FFFFF2CC'; + + /** 门店数量列填充色(浅蓝) */ + private const string STORE_FILL = 'FFDDEBF7'; + + /** 金额列填充色(浅红) */ + private const string AMOUNT_FILL = 'FFFFCCCC'; + + /** 金额格式:¥ + 两位小数 */ + private const string CURRENCY_FORMAT = '"¥"#,##0.00'; private ?Collection $rows = null; /** @var array 特殊行索引(1 起)=> 类型(title/scope/header/summary) */ private array $specialRows = []; + /** @var array}> 明细行索引 => 填色判断数据 */ + private array $rowData = []; + + /** @var array{quantity: float, amount: float, stores: array} 合计行填色判断数据 */ + private array $summaryTotals = ['quantity' => 0.0, 'amount' => 0.0, 'stores' => []]; + /** 列头所在行索引 */ private int $headerRow = 1; @@ -130,10 +148,10 @@ class PurchaseOrderExport implements FromCollection, WithStrictNullComparison, W 'product_spec' => $spec, 'unit' => (string) ($snapshot->unit ?? $product->unit), 'cost_price' => (float) ($snapshot->cost_price ?? $product->cost_price), - // 单价 = 加权平均售价 ÷ 包规数值(无订货行无售价数据,留空) + // 单价 = 加权平均售价 ÷ 包规数值(无订货时按成本价换算,保证始终有单价) 'retail_price' => $group !== null && (float) $quantity > 0 ? $this->unitRefPrice((float) bcdiv($amount, $quantity, 4), $spec) - : null, + : $this->unitRefPrice((float) ($snapshot->cost_price ?? $product->cost_price), $spec), 'quantity' => (float) $quantity, 'weight' => (float) ($group['weight'] ?? '0'), 'amount' => (float) $amount, @@ -185,6 +203,11 @@ class PurchaseOrderExport implements FromCollection, WithStrictNullComparison, W $totalAmount = '0'; $storeTotals = array_fill_keys(array_keys($this->storeNames), 0); foreach (array_values($items) as $sort => $item) { + $this->rowData[++$rowIndex] = [ + 'quantity' => $item['quantity'], + 'amount' => $item['amount'], + 'store_quantities' => $item['store_quantities'], + ]; $rows[] = array_merge([ $sort + 1, $item['category'], @@ -194,12 +217,11 @@ class PurchaseOrderExport implements FromCollection, WithStrictNullComparison, W $item['product_spec'], $item['unit'], $item['cost_price'], - $item['retail_price'] !== null ? $item['retail_price'] : '', + $item['retail_price'], $item['quantity'], $item['weight'], $item['amount'], ], array_values($item['store_quantities'])); - $rowIndex++; $totalQuantity = bcadd($totalQuantity, (string) $item['quantity'], 2); $totalWeight = bcadd($totalWeight, (string) $item['weight'], 3); $totalAmount = bcadd($totalAmount, (string) $item['amount'], 2); @@ -215,12 +237,18 @@ class PurchaseOrderExport implements FromCollection, WithStrictNullComparison, W ); $this->specialRows[++$rowIndex] = 'summary'; $this->lastRow = $rowIndex; + $this->summaryTotals = [ + 'quantity' => (float) $totalQuantity, + 'amount' => (float) $totalAmount, + 'stores' => $storeTotals, + ]; return $this->rows = collect($rows); } /** - * 标题/列头/合计加粗,门店列整列填充突出颜色,冻结列头 + * 标题/列头/合计加粗;全表居中 + 全边框;金额类列货币格式; + * 数量(浅黄)/门店数量(浅蓝)/金额(浅红)按值条件填色(0 不填、列头固定填色),冻结列头 */ public function styles(Worksheet $sheet): array { @@ -232,16 +260,66 @@ class PurchaseOrderExport implements FromCollection, WithStrictNullComparison, W $sheet->getColumnDimension(Coordinate::stringFromColumnIndex($index + 1))->setWidth($width); } - // 门店列整列(列头 → 合计行)填充突出颜色 - $storeCount = count($this->storeNames); - for ($i = 0; $i < $storeCount; $i++) { + $storeIds = array_keys($this->storeNames); + $storeCount = count($storeIds); + $lastColumn = Coordinate::stringFromColumnIndex(12 + max($storeCount, 1)); + foreach ($storeIds as $i => $storeId) { + $sheet->getColumnDimension(Coordinate::stringFromColumnIndex(13 + $i))->setWidth(12); + } + + // 全部单元格水平/垂直居中 + $sheet->getStyle('A1:' . $lastColumn . $this->lastRow) + ->getAlignment() + ->setHorizontal(Alignment::HORIZONTAL_CENTER) + ->setVertical(Alignment::VERTICAL_CENTER); + + // 表格区域(列头 → 合计行)添加所有边框 + $sheet->getStyle('A' . $this->headerRow . ':' . $lastColumn . $this->lastRow) + ->getBorders() + ->getAllBorders() + ->setBorderStyle(Border::BORDER_THIN); + + // 金额类列(成本/单价/金额)货币格式:¥ + 两位小数 + foreach (['H', 'I', 'L'] as $column) { + $sheet->getStyle($column . ($this->headerRow + 1) . ':' . $column . $this->lastRow) + ->getNumberFormat() + ->setFormatCode(self::CURRENCY_FORMAT); + } + + // 数量列(J):有数据浅黄,0 无背景,列头固定浅黄 + $this->fillCell($sheet, 'J' . $this->headerRow, self::QUANTITY_FILL); + foreach ($this->rowData as $row => $data) { + if ($data['quantity'] > 0) { + $this->fillCell($sheet, 'J' . $row, self::QUANTITY_FILL); + } + } + if ($this->summaryTotals['quantity'] > 0) { + $this->fillCell($sheet, 'J' . $this->lastRow, self::QUANTITY_FILL); + } + + // 金额列(L):有数据浅红,0 无背景,列头固定浅红 + $this->fillCell($sheet, 'L' . $this->headerRow, self::AMOUNT_FILL); + foreach ($this->rowData as $row => $data) { + if ($data['amount'] > 0) { + $this->fillCell($sheet, 'L' . $row, self::AMOUNT_FILL); + } + } + if ($this->summaryTotals['amount'] > 0) { + $this->fillCell($sheet, 'L' . $this->lastRow, self::AMOUNT_FILL); + } + + // 门店数量列:有数据浅蓝,0 无背景,列头固定浅蓝 + foreach ($storeIds as $i => $storeId) { $column = Coordinate::stringFromColumnIndex(13 + $i); - $sheet->getColumnDimension($column)->setWidth(12); - $sheet->getStyle($column . $this->headerRow . ':' . $column . $this->lastRow) - ->getFill() - ->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID) - ->getStartColor() - ->setARGB(self::STORE_FILL); + $this->fillCell($sheet, $column . $this->headerRow, self::STORE_FILL); + foreach ($this->rowData as $row => $data) { + if (($data['store_quantities'][$storeId] ?? 0) > 0) { + $this->fillCell($sheet, $column . $row, self::STORE_FILL); + } + } + if (($this->summaryTotals['stores'][$storeId] ?? 0) > 0) { + $this->fillCell($sheet, $column . $this->lastRow, self::STORE_FILL); + } } $styles = []; @@ -259,6 +337,18 @@ class PurchaseOrderExport implements FromCollection, WithStrictNullComparison, W return $styles; } + /** + * 单元格纯色填充 + */ + private function fillCell(Worksheet $sheet, string $coordinate, string $argb): void + { + $sheet->getStyle($coordinate) + ->getFill() + ->setFillType(Fill::FILL_SOLID) + ->getStartColor() + ->setARGB($argb); + } + /** * 每单位参考价 = 整单价 ÷ 包规数值(包规解析不出正数时按 1 处理;仅展示参考) */ diff --git a/app/Exports/PurchaseStoreSheet.php b/app/Exports/PurchaseStoreSheet.php index 82eb494..2bbea62 100644 --- a/app/Exports/PurchaseStoreSheet.php +++ b/app/Exports/PurchaseStoreSheet.php @@ -10,6 +10,9 @@ use Maatwebsite\Excel\Concerns\FromCollection; use Maatwebsite\Excel\Concerns\WithStrictNullComparison; use Maatwebsite\Excel\Concerns\WithStyles; use Maatwebsite\Excel\Concerns\WithTitle; +use PhpOffice\PhpSpreadsheet\Style\Alignment; +use PhpOffice\PhpSpreadsheet\Style\Border; +use PhpOffice\PhpSpreadsheet\Style\Fill; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; /** @@ -17,14 +20,32 @@ use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; */ class PurchaseStoreSheet implements FromCollection, WithStrictNullComparison, WithStyles, WithTitle { + /** 数量列填充色(浅黄) */ + private const string QUANTITY_FILL = 'FFFFF2CC'; + + /** 金额列填充色(浅红) */ + private const string AMOUNT_FILL = 'FFFFCCCC'; + + /** 金额格式:¥ + 两位小数 */ + private const string CURRENCY_FORMAT = '"¥"#,##0.00'; + private ?Collection $rows = null; /** @var array 特殊行索引(1 起)=> 类型(title/header/summary) */ private array $specialRows = []; + /** @var array 明细行索引 => 填色判断数据 */ + private array $rowData = []; + + /** @var array{quantity: int, amount: float} 合计行填色判断数据 */ + private array $summaryTotals = ['quantity' => 0, 'amount' => 0.0]; + /** 列头所在行索引 */ private int $headerRow = 1; + /** 数据末行索引 */ + private int $lastRow = 1; + public function __construct( private readonly PurchaseOrderModel $purchase, private readonly StoreModel $store, @@ -64,6 +85,10 @@ class PurchaseStoreSheet implements FromCollection, WithStrictNullComparison, Wi $totalWeight = '0'; $totalAmount = '0'; foreach (array_values($items) as $sort => $item) { + $this->rowData[++$rowIndex] = [ + 'quantity' => (int) $item['quantity'], + 'amount' => (float) $item['amount'], + ]; $rows[] = [ $sort + 1, $item['product_name'], @@ -75,7 +100,6 @@ class PurchaseStoreSheet implements FromCollection, WithStrictNullComparison, Wi (float) $item['weight'], (float) $item['amount'], ]; - $rowIndex++; $totalQuantity += (int) $item['quantity']; $totalWeight = bcadd($totalWeight, (string) $item['weight'], 3); $totalAmount = bcadd($totalAmount, (string) $item['amount'], 2); @@ -84,6 +108,8 @@ class PurchaseStoreSheet implements FromCollection, WithStrictNullComparison, Wi // 合计行 $rows[] = ['', '合计', '', '', '', '', $totalQuantity, (float) $totalWeight, (float) $totalAmount]; $this->specialRows[++$rowIndex] = 'summary'; + $this->lastRow = $rowIndex; + $this->summaryTotals = ['quantity' => $totalQuantity, 'amount' => (float) $totalAmount]; return $this->rows = collect($rows); } @@ -94,7 +120,8 @@ class PurchaseStoreSheet implements FromCollection, WithStrictNullComparison, Wi } /** - * 标题/列头/合计加粗,冻结列头 + * 标题/列头/合计加粗;全表居中 + 全边框;金额类列货币格式; + * 数量(浅黄)/金额(浅红)按值条件填色(0 不填、列头固定填色),冻结列头 */ public function styles(Worksheet $sheet): array { @@ -107,6 +134,47 @@ class PurchaseStoreSheet implements FromCollection, WithStrictNullComparison, Wi ->setWidth($width); } + // 全部单元格水平/垂直居中 + $sheet->getStyle('A1:I' . $this->lastRow) + ->getAlignment() + ->setHorizontal(Alignment::HORIZONTAL_CENTER) + ->setVertical(Alignment::VERTICAL_CENTER); + + // 表格区域(列头 → 合计行)添加所有边框 + $sheet->getStyle('A' . $this->headerRow . ':I' . $this->lastRow) + ->getBorders() + ->getAllBorders() + ->setBorderStyle(Border::BORDER_THIN); + + // 金额类列(单价/预计金额)货币格式:¥ + 两位小数 + foreach (['F', 'I'] as $column) { + $sheet->getStyle($column . ($this->headerRow + 1) . ':' . $column . $this->lastRow) + ->getNumberFormat() + ->setFormatCode(self::CURRENCY_FORMAT); + } + + // 数量列(G):有数据浅黄,0 无背景,列头固定浅黄 + $this->fillCell($sheet, 'G' . $this->headerRow, self::QUANTITY_FILL); + foreach ($this->rowData as $row => $data) { + if ($data['quantity'] > 0) { + $this->fillCell($sheet, 'G' . $row, self::QUANTITY_FILL); + } + } + if ($this->summaryTotals['quantity'] > 0) { + $this->fillCell($sheet, 'G' . $this->lastRow, self::QUANTITY_FILL); + } + + // 金额列(I):有数据浅红,0 无背景,列头固定浅红 + $this->fillCell($sheet, 'I' . $this->headerRow, self::AMOUNT_FILL); + foreach ($this->rowData as $row => $data) { + if ($data['amount'] > 0) { + $this->fillCell($sheet, 'I' . $row, self::AMOUNT_FILL); + } + } + if ($this->summaryTotals['amount'] > 0) { + $this->fillCell($sheet, 'I' . $this->lastRow, self::AMOUNT_FILL); + } + $styles = []; foreach ($this->specialRows as $row => $type) { $style = match ($type) { @@ -121,4 +189,16 @@ class PurchaseStoreSheet implements FromCollection, WithStrictNullComparison, Wi return $styles; } + + /** + * 单元格纯色填充 + */ + private function fillCell(Worksheet $sheet, string $coordinate, string $argb): void + { + $sheet->getStyle($coordinate) + ->getFill() + ->setFillType(Fill::FILL_SOLID) + ->getStartColor() + ->setARGB($argb); + } } diff --git a/app/Exports/PurchaseSupplierSheet.php b/app/Exports/PurchaseSupplierSheet.php index d4804aa..93376c3 100644 --- a/app/Exports/PurchaseSupplierSheet.php +++ b/app/Exports/PurchaseSupplierSheet.php @@ -10,18 +10,23 @@ use Maatwebsite\Excel\Concerns\WithStrictNullComparison; use Maatwebsite\Excel\Concerns\WithStyles; use Maatwebsite\Excel\Concerns\WithTitle; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; +use PhpOffice\PhpSpreadsheet\Style\Alignment; +use PhpOffice\PhpSpreadsheet\Style\Border; use PhpOffice\PhpSpreadsheet\Style\Fill; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; /** * 供应商采购明细导出 · 单市场工作表 * 模板列序:品名、汇总、市场、各门店明细(门店列=该供应商有明细的门店); - * 有数据的单元格(品名/汇总/门店数量)填充突出颜色并加粗 + * 汇总列(浅黄)/门店数量列(浅蓝)按值条件填色(0 不填、列头固定填色) */ class PurchaseSupplierSheet implements FromCollection, WithStrictNullComparison, WithStyles, WithTitle { - /** 有数据单元格填充色(浅黄,突出显示) */ - private const string DATA_FILL = 'FFFFFF99'; + /** 汇总列填充色(浅黄) */ + private const string QUANTITY_FILL = 'FFFFF2CC'; + + /** 门店数量列填充色(浅蓝) */ + private const string STORE_FILL = 'FFDDEBF7'; private ?Collection $rows = null; @@ -31,8 +36,14 @@ class PurchaseSupplierSheet implements FromCollection, WithStrictNullComparison, /** 列头所在行索引 */ private int $headerRow = 1; - /** @var array> 有数据的单元格:行索引(1 起)=> 列索引(1 起)列表 */ - private array $dataCells = []; + /** 数据末行索引 */ + private int $lastRow = 1; + + /** @var array}> 明细行索引 => 填色判断数据 */ + private array $rowData = []; + + /** @var array{quantity: int, stores: array} 合计行填色判断数据 */ + private array $summaryTotals = ['quantity' => 0, 'stores' => []]; /** * @param PurchaseOrderModel $purchase 采购单 @@ -79,7 +90,7 @@ class PurchaseSupplierSheet implements FromCollection, WithStrictNullComparison, $this->specialRows[++$rowIndex] = 'header'; $this->headerRow = $rowIndex; - // 明细行(有数据的单元格记录到 dataCells:品名/汇总/门店数量) + // 明细行(0 数量的门店格留空不填色) $totalQuantity = 0; $storeTotals = array_fill_keys($storeIds, 0); foreach ($this->productRows as $productRow) { @@ -88,17 +99,18 @@ class PurchaseSupplierSheet implements FromCollection, WithStrictNullComparison, (int) $productRow['quantity'], $this->marketLabel, ]; - $filled = [1, 2, 3]; - foreach ($storeIds as $position => $storeId) { + $storeQuantities = []; + foreach ($storeIds as $storeId) { $quantity = (int) ($productRow['store_quantities'][$storeId] ?? 0); $line[] = $quantity > 0 ? $quantity : ''; - if ($quantity > 0) { - $filled[] = 4 + $position; - $storeTotals[$storeId] += $quantity; - } + $storeQuantities[$storeId] = $quantity; + $storeTotals[$storeId] += $quantity; } $rows[] = $line; - $this->dataCells[++$rowIndex] = $filled; + $this->rowData[++$rowIndex] = [ + 'quantity' => (int) $productRow['quantity'], + 'store_quantities' => $storeQuantities, + ]; $totalQuantity += (int) $productRow['quantity']; } @@ -108,6 +120,8 @@ class PurchaseSupplierSheet implements FromCollection, WithStrictNullComparison, array_map(static fn (int $storeId): int => $storeTotals[$storeId], $storeIds), ); $this->specialRows[++$rowIndex] = 'summary'; + $this->lastRow = $rowIndex; + $this->summaryTotals = ['quantity' => $totalQuantity, 'stores' => $storeTotals]; return $this->rows = collect($rows); } @@ -118,7 +132,8 @@ class PurchaseSupplierSheet implements FromCollection, WithStrictNullComparison, } /** - * 标题/列头/合计加粗,有数据的单元格填充突出颜色,冻结列头 + * 标题/列头/合计加粗;全表居中 + 全边框; + * 汇总列(浅黄)/门店数量列(浅蓝)按值条件填色(0 不填、列头固定填色),冻结列头 */ public function styles(Worksheet $sheet): array { @@ -130,19 +145,46 @@ class PurchaseSupplierSheet implements FromCollection, WithStrictNullComparison, foreach ($widths as $index => $width) { $sheet->getColumnDimension(Coordinate::stringFromColumnIndex($index + 1))->setWidth($width); } - $storeCount = count($this->stores); - for ($i = 0; $i < $storeCount; $i++) { + $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); } - // 有数据的单元格:填充突出颜色并加粗(含品名/汇总/门店数量) - foreach ($this->dataCells as $rowIndex => $columns) { - foreach ($columns as $columnIndex) { - $style = $sheet->getStyle(Coordinate::stringFromColumnIndex($columnIndex) . $rowIndex); - $style->getFill() - ->setFillType(Fill::FILL_SOLID) - ->getStartColor()->setARGB(self::DATA_FILL); - $style->getFont()->setBold(true); + // 全部单元格水平/垂直居中 + $sheet->getStyle('A1:' . $lastColumn . $this->lastRow) + ->getAlignment() + ->setHorizontal(Alignment::HORIZONTAL_CENTER) + ->setVertical(Alignment::VERTICAL_CENTER); + + // 表格区域(列头 → 合计行)添加所有边框 + $sheet->getStyle('A' . $this->headerRow . ':' . $lastColumn . $this->lastRow) + ->getBorders() + ->getAllBorders() + ->setBorderStyle(Border::BORDER_THIN); + + // 汇总列(B):有数据浅黄,0 无背景,列头固定浅黄 + $this->fillCell($sheet, 'B' . $this->headerRow, self::QUANTITY_FILL); + foreach ($this->rowData as $row => $data) { + if ($data['quantity'] > 0) { + $this->fillCell($sheet, 'B' . $row, self::QUANTITY_FILL); + } + } + if ($this->summaryTotals['quantity'] > 0) { + $this->fillCell($sheet, 'B' . $this->lastRow, self::QUANTITY_FILL); + } + + // 门店数量列(D 起):有数据浅蓝,0 无背景,列头固定浅蓝 + foreach ($storeIds as $i => $storeId) { + $column = Coordinate::stringFromColumnIndex(4 + $i); + $this->fillCell($sheet, $column . $this->headerRow, self::STORE_FILL); + foreach ($this->rowData as $row => $data) { + if (($data['store_quantities'][$storeId] ?? 0) > 0) { + $this->fillCell($sheet, $column . $row, self::STORE_FILL); + } + } + if (($this->summaryTotals['stores'][$storeId] ?? 0) > 0) { + $this->fillCell($sheet, $column . $this->lastRow, self::STORE_FILL); } } @@ -160,4 +202,16 @@ class PurchaseSupplierSheet implements FromCollection, WithStrictNullComparison, return $styles; } + + /** + * 单元格纯色填充 + */ + private function fillCell(Worksheet $sheet, string $coordinate, string $argb): void + { + $sheet->getStyle($coordinate) + ->getFill() + ->setFillType(Fill::FILL_SOLID) + ->getStartColor() + ->setARGB($argb); + } } diff --git a/tests/Feature/ExportTest.php b/tests/Feature/ExportTest.php index 6031eab..1be010b 100644 --- a/tests/Feature/ExportTest.php +++ b/tests/Feature/ExportTest.php @@ -142,7 +142,7 @@ class ExportTest extends ProcurementTestCase { [$purchase, $veg, $meat, $storeA, $storeB] = $this->buildPurchaseWithSuppliers(); // 无订货的上架商品也应出现在导出中 - $extra = ProductModel::factory()->create(['status' => ProductModel::STATUS_ON, 'cost_price' => '7.00']); + $extra = ProductModel::factory()->create(['status' => ProductModel::STATUS_ON, 'cost_price' => '7.00', 'spec' => '10斤/箱']); Excel::fake(); $this->actingAsSysUser(); @@ -170,10 +170,10 @@ class ExportTest extends ProcurementTestCase || (float) $vegRow[12] !== 2.0 || (float) $vegRow[13] !== 3.0) { return false; } - // 无订货商品行:数量 0、单价留空、门店列 0 + // 无订货商品行:数量 0、单价按成本价÷包规显示(7÷10=0.70)、门店列 0 $extraRow = $rows->firstWhere(2, $extra->name); if ($extraRow === null - || (float) $extraRow[9] !== 0.0 || $extraRow[8] !== '' + || (float) $extraRow[9] !== 0.0 || (float) $extraRow[8] !== 0.7 || (float) $extraRow[12] !== 0.0) { return false; }