{
  "schemaVersion": "1.0",
  "item": {
    "slug": "retail-trade-report-generator",
    "name": "retail-trade-report-generator",
    "source": "tencent",
    "type": "skill",
    "category": "数据分析",
    "sourceUrl": "https://clawhub.ai/wuminmin/retail-trade-report-generator",
    "canonicalUrl": "https://clawhub.ai/wuminmin/retail-trade-report-generator",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadMode": "redirect",
    "downloadUrl": "/downloads/retail-trade-report-generator",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=retail-trade-report-generator",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "installMethod": "Manual import",
    "extraction": "Extract archive",
    "prerequisites": [
      "OpenClaw"
    ],
    "packageFormat": "ZIP package",
    "includedAssets": [
      "README_CN.md",
      "SKILL.md",
      "scripts/retail_trade_report_generator.py",
      "references/store_mapping.csv"
    ],
    "primaryDoc": "SKILL.md",
    "quickSetup": [
      "Download the package from Yavira.",
      "Extract the archive and review SKILL.md first.",
      "Import or place the package into your OpenClaw setup."
    ],
    "agentAssist": {
      "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
      "steps": [
        "Download the package from Yavira.",
        "Extract it into a folder your agent can access.",
        "Paste one of the prompts below and point your agent at the extracted folder."
      ],
      "prompts": [
        {
          "label": "New install",
          "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete."
        },
        {
          "label": "Upgrade existing",
          "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run."
        }
      ]
    },
    "sourceHealth": {
      "source": "tencent",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-04-23T16:43:11.935Z",
      "expiresAt": "2026-04-30T16:43:11.935Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=4claw-imageboard",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=4claw-imageboard",
        "contentDisposition": "attachment; filename=\"4claw-imageboard-1.0.1.zip\"",
        "redirectLocation": null,
        "bodySnippet": null
      },
      "scope": "source",
      "summary": "Source download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this source.",
      "primaryActionLabel": "Download for OpenClaw",
      "primaryActionHref": "/downloads/retail-trade-report-generator"
    },
    "validation": {
      "installChecklist": [
        "Use the Yavira download entry.",
        "Review SKILL.md after the package is downloaded.",
        "Confirm the extracted package contains the expected setup assets."
      ],
      "postInstallChecks": [
        "Confirm the extracted package includes the expected docs or setup files.",
        "Validate the skill or prompts are available in your target agent workspace.",
        "Capture any manual follow-up steps the agent could not complete."
      ]
    },
    "downloadPageUrl": "https://openagent3.xyz/downloads/retail-trade-report-generator",
    "agentPageUrl": "https://openagent3.xyz/skills/retail-trade-report-generator/agent",
    "manifestUrl": "https://openagent3.xyz/skills/retail-trade-report-generator/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/retail-trade-report-generator/agent.md"
  },
  "agentAssist": {
    "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
    "steps": [
      "Download the package from Yavira.",
      "Extract it into a folder your agent can access.",
      "Paste one of the prompts below and point your agent at the extracted folder."
    ],
    "prompts": [
      {
        "label": "New install",
        "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete."
      },
      {
        "label": "Upgrade existing",
        "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run."
      }
    ]
  },
  "documentation": {
    "source": "clawhub",
    "primaryDoc": "SKILL.md",
    "sections": [
      {
        "title": "Overview",
        "body": "This skill processes multiple weekly sales report Excel files to generate a consolidated Retail Trade Weekly Report with week-over-week (WoW) comparisons across different channels (DRP, DXS, License Store) and product types (Mobile Prepaid/Postpaid, FWA 4G/5G)."
      },
      {
        "title": "Purpose",
        "body": "Consolidate 12 Excel files (6 current week + 6 previous week) into a single comprehensive weekly report\nCalculate Average Daily Acquisition (ADA) metrics by region and channel\nCompute Week-over-Week (WoW) performance indicators\nGenerate formatted Excel output with charts and color-coded performance indicators"
      },
      {
        "title": "Required Files (12 total)",
        "body": "Current Week (6 files):\n\nDRP_Channel_Sales_Report_DRP_M_DD-M_DD.xlsx\nDRP_Special_SIM_Monitor_Report_Daily_TECNO_M_DD-M_DD.xlsx\nLicense_Store_Performance_Monitor_Report_LS_M_DD-M_DD.xlsx\nDXS_Acquisition_Report_Mobile_Prepaid_M_DD-M_DD.xlsx\nDXS_Acquisition_Report_Mobile_Postpaid_M_DD-M_DD.xlsx\nDXS_Acquisition_Report_FWA_M_DD-M_DD.xlsx\n\nPrevious Week (6 files with earlier dates):\nSame file types with earlier date ranges in filename"
      },
      {
        "title": "Store Mapping CSV",
        "body": "File containing Store Name to Region mapping with aliases support:\n\nStore Name,Region,Aliases\nSM Megamall,NCR,\"Megamall|SM Mega|MEGAMALL\"\n..."
      },
      {
        "title": "1. File Identification",
        "body": "Extract date ranges from filenames (format: M_DD-M_DD)\nAuto-group into current week vs previous week based on date comparison\nValidate all 12 required files are present"
      },
      {
        "title": "2. Data Extraction Rules",
        "body": "DRP Channel Sales Report\n\nHeader rows: Skip rows 0-7\nData rows: Start from row 8\nRegion field: Column 0 (AREA)\nKey columns:\n\nColumn 1: MOBILE POSTPAID > TOTAL ACTIVATION\nColumn 5: MOBILE PREPAID > TOTAL ACTIVATION\nColumn 6: Double Data_Sum\nColumn 9: 4G WiFi 980 SIM_Sum (FWA 4G)\nColumn 10: Unli 5G WIFI 100Mbps Starter SIM_Sum (FWA 5G)\nColumn 11: 5G WiFi 4990 SIM_Sum (FWA 5G)\n\nDRP TECNO Report\n\nHeader rows: Skip rows 0-6\nData rows: Start from row 7\nRegion field: Column 0 (Activation Area)\nKey columns:\n\nColumn 1: CARMON Activation (CAMON 40)\nColumn 2: POVA Activation (POVA 7)\nColumn 3: Total Activation (TECNO ADA = CAMON 40 + POVA 7)\n\nLicense Store Report\n\nHeader rows: Skip rows 0-7\nData rows: Start from row 8\nStore field: Column 0 (Store Name) - Requires mapping to Region\nKey columns:\n\nColumn 1: Mobile Prepaid\nColumn 3: Mobile Postpaid\nColumn 29 (AD): DITO Home Prepaid 4G WiFi 980 SIM (FWA 4G)\nNeed to find: Unli 5G WIFI 100Mbps Starter SIM (FWA 5G)\nNeed to find: 5G WiFi 4990 SIM (FWA 5G)\n\nDXS Mobile Prepaid Report\n\nHeader rows: Skip rows 0-7\nData rows: Start from row 8\nStore field: Column 0 (DXS Name) - Requires mapping to Region\nKey column:\n\nColumn 4: Total\n\nDXS Mobile Postpaid Report\n\nHeader rows: Skip rows 0-7\nData rows: Start from row 8\nStore field: Column 0 (DXS Name) - Requires mapping to Region\nKey column:\n\nColumn 12: Total\n\nDXS FWA Report\n\nHeader rows: Skip rows 0-7\nData rows: Start from row 8\nStore field: Column 0 (DXS Name) - Requires mapping to Region\nKey columns:\n\nColumn 1: DITO Home Prepaid 4G WiFi 980 (FWA 4G)\nColumn 18: Total\nFWA 5G calculation: Total (Col 18) - 4G (Col 1)"
      },
      {
        "title": "3. Store Name to Region Mapping",
        "body": "# Build mapping dictionary from CSV\nstore_mapping = {}\nfor row in mapping_csv:\n    main_name = row['Store Name']\n    region = row['Region']\n    aliases = row['Aliases'].split('|') if row['Aliases'] else []\n    \n    # Add main name and all aliases to mapping\n    store_mapping[main_name.upper()] = region\n    for alias in aliases:\n        store_mapping[alias.strip().upper()] = region\n\n# Apply fuzzy matching for unmatched stores\ndef map_store_to_region(store_name):\n    # Exact match (case-insensitive)\n    if store_name.upper() in store_mapping:\n        return store_mapping[store_name.upper()]\n    \n    # Fuzzy match using substring search\n    for key in store_mapping:\n        if key in store_name.upper() or store_name.upper() in key:\n            return store_mapping[key]\n    \n    # Default to \"Others\" if no match\n    return \"Others\""
      },
      {
        "title": "4. Regional Aggregation",
        "body": "Standard Regions: NCR, SLZ, NLZ, CLZ, EVIS, WVIS, MIN, Others\n\nFor each product type and region:\n\n# DRP data: Direct mapping (already by region)\nDRP_ADA = drp_data[region][product_column]\n\n# DXS data: Aggregate stores by region\nDXS_ADA = sum(dxs_data[store][product_column] \n              for store in dxs_data \n              if map_store_to_region(store) == region)\n\n# LS data: Aggregate stores by region\nLS_ADA = sum(ls_data[store][product_column] \n             for store in ls_data \n             if map_store_to_region(store) == region)\n\n# Total for region\nRT_Total_ADA = DRP_ADA + DXS_ADA + LS_ADA"
      },
      {
        "title": "5. WoW Calculation",
        "body": "WoW = (current_week_value - previous_week_value) / previous_week_value\n\n# Formatting rules:\n# - Display as percentage (e.g., \"21%\", \"-13%\")\n# - Round to nearest integer\n# - Handle division by zero: display \"-\" if previous_week_value == 0\n# - Handle cases where current = 0 and previous > 0: show \"-100%\""
      },
      {
        "title": "6. Special Calculations",
        "body": "FWA 5G Components\n\n# DRP FWA 5G\nDRP_FWA_5G = Column_10 + Column_11\n\n# DXS FWA 5G\nDXS_FWA_5G = Total - Column_1_4G\n\n# LS FWA 5G\nLS_FWA_5G = Unli_5G_WIFI_100Mbps + WiFi_4990_SIM\n\nTECNO ADA\n\nTECNO_ADA = CAMON_40 + POVA_7"
      },
      {
        "title": "Excel Structure",
        "body": "Single Sheet: \"Weekly Report\"\n\nSections:\n\nReport Header (Rows 1-2)\n\nTitle: \"Retail Trade Weekly Report\"\nDate ranges: \"Last Week: [dates] | This Week: [dates]\"\n\n\n\nChannel Summary (Rows 4-9)\n\nColumns: Channel | Program name | This Week ADA | WoW | MoM\nRows: DRP BAU, DRP TECNO, License Store, DXS, RT Total\n\n\n\nMobile Prepaid by Region (Rows 11-21)\n\nColumns: Region | RT Total ADA | WoW | DXS ADA | WoW | LS ADA | WoW | DRP ADA | WoW\nRows: 8 regions + Total\n\n\n\nDRP Prepaid Program (Rows 23-33)\n\nColumns: Region | Double Data ADA | WoW | TECNO ADA | WoW | CAMON 40 | WoW | POVA 7 | WoW\nRows: 8 regions + Total\n\n\n\nMobile Postpaid by Region (Rows 35-45)\n\nSame structure as Mobile Prepaid\n\n\n\nFWA 4G by Region (Rows 47-57)\n\nSame structure as Mobile Prepaid\n\n\n\nFWA 5G by Region (Rows 59-69)\n\nSame structure as Mobile Prepaid"
      },
      {
        "title": "Formatting Rules",
        "body": "Number Formatting\n\nADA values: Integer format with thousand separators (e.g., \"1,876\")\nWoW percentages: Integer percentage (e.g., \"21%\", \"-13%\")\nSmall ADA values (< 10): Show 1 decimal place (e.g., \"0.6\", \"2.9\")\n\nColor Coding\n\nWoW Positive (>0%): Green text (#008000)\nWoW Negative (<0%): Red text (#FF0000)\nWoW Zero (0%): Black text\nWoW N/A (\"-\"): Gray text (#808080)\n\nCell Styling\n\nHeaders: Bold, centered, light gray background (#F0F0F0)\nRegion names: Bold\nTotal rows: Bold, light blue background (#E6F2FF)\nBorders: Thin borders around all data cells"
      },
      {
        "title": "Charts",
        "body": "Chart 1: Channel Performance Comparison\n\nType: Clustered Column Chart\nData: This Week ADA by Channel (DRP BAU, DRP TECNO, License Store, DXS)\nPosition: Right side of Channel Summary section\nSize: 6 columns wide x 15 rows tall\n\nChart 2: Regional Mobile Prepaid Distribution\n\nType: Stacked Column Chart\nData: DRP ADA, DXS ADA, LS ADA by Region\nPosition: Right side of Mobile Prepaid section\nSize: 6 columns wide x 15 rows tall\n\nChart 3: WoW Trend - Top 3 Regions\n\nType: Line Chart with Markers\nData: WoW % for top 3 regions by RT Total ADA\nPosition: Below main tables\nSize: 12 columns wide x 12 rows tall"
      },
      {
        "title": "Missing Files",
        "body": "if len(current_week_files) != 6:\n    raise ValueError(f\"Expected 6 current week files, found {len(current_week_files)}\")\n\nif len(previous_week_files) != 6:\n    raise ValueError(f\"Expected 6 previous week files, found {len(previous_week_files)}\")"
      },
      {
        "title": "Unmapped Stores",
        "body": "unmapped_stores = []\nfor store in all_stores:\n    if map_store_to_region(store) == \"Others\":\n        # Log warning but continue processing\n        unmapped_stores.append(store)\n\nif unmapped_stores:\n    print(f\"Warning: {len(unmapped_stores)} stores mapped to 'Others' region\")"
      },
      {
        "title": "Data Quality Checks",
        "body": "# Check for negative values\nif any_value < 0:\n    print(f\"Warning: Negative value found in {file}:{column}\")\n\n# Check for missing regions\nexpected_regions = {\"NCR\", \"SLZ\", \"NLZ\", \"CLZ\", \"EVIS\", \"WVIS\", \"MIN\", \"Others\"}\nmissing_regions = expected_regions - set(actual_regions)\nif missing_regions:\n    print(f\"Warning: Missing regions: {missing_regions}\")"
      },
      {
        "title": "Python Libraries",
        "body": "import pandas as pd\nimport openpyxl\nfrom openpyxl.styles import Font, PatternFill, Alignment, Border, Side\nfrom openpyxl.chart import BarChart, LineChart, Reference\nimport re\nfrom datetime import datetime"
      },
      {
        "title": "Key Functions",
        "body": "1. File Parser\n\ndef extract_date_from_filename(filename):\n    \"\"\"Extract date range from filename like 'Report_1_11-1_17.xlsx'\"\"\"\n    pattern = r'_(\\d+)_(\\d+)-(\\d+)_(\\d+)\\.xlsx'\n    match = re.search(pattern, filename)\n    if match:\n        start_month, start_day, end_month, end_day = match.groups()\n        return (int(start_month), int(start_day), int(end_month), int(end_day))\n    return None\n\ndef identify_file_type(filename):\n    \"\"\"Identify file type from filename\"\"\"\n    if 'DRP_Channel_Sales' in filename:\n        return 'DRP'\n    elif 'TECNO' in filename:\n        return 'TECNO'\n    elif 'License_Store' in filename:\n        return 'LS'\n    elif 'Mobile_Prepaid' in filename:\n        return 'DXS_Prepaid'\n    elif 'Mobile_Postpaid' in filename:\n        return 'DXS_Postpaid'\n    elif 'FWA' in filename:\n        return 'DXS_FWA'\n    return 'Unknown'\n\n2. Data Extractor\n\ndef extract_drp_data(filepath):\n    \"\"\"Extract DRP channel sales data\"\"\"\n    df = pd.read_excel(filepath, sheet_name='Sheet0', header=None)\n    \n    # Find data start row (usually row 8)\n    data_start = 8\n    \n    # Extract by region\n    regions_data = {}\n    for idx in range(data_start, len(df)):\n        region = df.iloc[idx, 0]\n        if pd.isna(region) or region == 'Total':\n            continue\n            \n        regions_data[region] = {\n            'mobile_postpaid': df.iloc[idx, 1],\n            'mobile_prepaid': df.iloc[idx, 5],\n            'double_data': df.iloc[idx, 6],\n            'fwa_4g': df.iloc[idx, 9],\n            'fwa_5g': df.iloc[idx, 10] + df.iloc[idx, 11]\n        }\n    \n    return regions_data\n\ndef extract_dxs_data(filepath, product_type):\n    \"\"\"Extract DXS acquisition data\"\"\"\n    df = pd.read_excel(filepath, sheet_name='Sheet1', header=None)\n    \n    # Determine column based on product type\n    if product_type == 'prepaid':\n        value_col = 4\n    elif product_type == 'postpaid':\n        value_col = 12\n    elif product_type == 'fwa':\n        return extract_dxs_fwa_data(df)\n    \n    stores_data = {}\n    for idx in range(8, len(df)):\n        store = df.iloc[idx, 0]\n        if pd.isna(store) or store in ['Grand Total', '-']:\n            continue\n        \n        value = df.iloc[idx, value_col]\n        if pd.notna(value):\n            stores_data[store] = value\n    \n    return stores_data\n\ndef extract_dxs_fwa_data(df):\n    \"\"\"Extract FWA data with 4G/5G split\"\"\"\n    stores_data = {}\n    for idx in range(8, len(df)):\n        store = df.iloc[idx, 0]\n        if pd.isna(store) or store in ['Grand Total', '-']:\n            continue\n        \n        fwa_4g = df.iloc[idx, 1] if pd.notna(df.iloc[idx, 1]) else 0\n        total = df.iloc[idx, 18] if pd.notna(df.iloc[idx, 18]) else 0\n        fwa_5g = total - fwa_4g\n        \n        stores_data[store] = {\n            'fwa_4g': fwa_4g,\n            'fwa_5g': fwa_5g\n        }\n    \n    return stores_data\n\n3. Region Aggregator\n\ndef aggregate_by_region(stores_data, mapping_dict, regions):\n    \"\"\"Aggregate store data by region\"\"\"\n    regional_totals = {region: 0 for region in regions}\n    \n    for store, value in stores_data.items():\n        region = map_store_to_region(store, mapping_dict)\n        if isinstance(value, dict):\n            # Handle nested data (e.g., FWA with 4G/5G)\n            for key in value:\n                if key not in regional_totals:\n                    regional_totals[key] = {region: 0 for region in regions}\n                regional_totals[key][region] += value[key]\n        else:\n            regional_totals[region] += value\n    \n    return regional_totals\n\n4. WoW Calculator\n\ndef calculate_wow(current, previous):\n    \"\"\"Calculate week-over-week percentage change\"\"\"\n    if previous == 0 or pd.isna(previous):\n        return \"-\"\n    \n    if current == 0 or pd.isna(current):\n        return \"-100%\"\n    \n    wow = ((current - previous) / previous) * 100\n    return f\"{int(round(wow))}%\"\n\n5. Excel Formatter\n\ndef apply_formatting(ws, start_row, start_col, end_row, end_col):\n    \"\"\"Apply formatting to Excel worksheet\"\"\"\n    # Define styles\n    header_fill = PatternFill(start_color=\"F0F0F0\", end_color=\"F0F0F0\", fill_type=\"solid\")\n    total_fill = PatternFill(start_color=\"E6F2FF\", end_color=\"E6F2FF\", fill_type=\"solid\")\n    \n    green_font = Font(color=\"008000\")\n    red_font = Font(color=\"FF0000\")\n    gray_font = Font(color=\"808080\")\n    bold_font = Font(bold=True)\n    \n    thin_border = Border(\n        left=Side(style='thin'),\n        right=Side(style='thin'),\n        top=Side(style='thin'),\n        bottom=Side(style='thin')\n    )\n    \n    # Apply to cells\n    for row in ws.iter_rows(min_row=start_row, max_row=end_row, \n                            min_col=start_col, max_col=end_col):\n        for cell in row:\n            cell.border = thin_border\n            \n            # Color code WoW values\n            if isinstance(cell.value, str) and '%' in cell.value:\n                try:\n                    pct_value = int(cell.value.replace('%', ''))\n                    if pct_value > 0:\n                        cell.font = green_font\n                    elif pct_value < 0:\n                        cell.font = red_font\n                except:\n                    if cell.value == '-':\n                        cell.font = gray_font\n\ndef add_chart(ws, chart_type, data_range, position, title):\n    \"\"\"Add chart to worksheet\"\"\"\n    if chart_type == 'column':\n        chart = BarChart()\n    elif chart_type == 'line':\n        chart = LineChart()\n    \n    chart.title = title\n    chart.style = 10\n    chart.height = 10\n    chart.width = 15\n    \n    data = Reference(ws, min_col=data_range[0], min_row=data_range[1],\n                     max_col=data_range[2], max_row=data_range[3])\n    chart.add_data(data, titles_from_data=True)\n    \n    ws.add_chart(chart, position)"
      },
      {
        "title": "Usage Example",
        "body": "from retail_trade_report_skill import generate_weekly_report\n\n# Input files directory\ninput_dir = \"/mnt/user-data/uploads/\"\n\n# Store mapping CSV\nmapping_file = \"/mnt/user-data/uploads/store_mapping.csv\"\n\n# Generate report\noutput_file = generate_weekly_report(\n    input_dir=input_dir,\n    mapping_csv=mapping_file,\n    output_path=\"/mnt/user-data/outputs/Retail_Trade_Weekly_Report.xlsx\"\n)\n\nprint(f\"Report generated: {output_file}\")"
      },
      {
        "title": "Validation Checklist",
        "body": "Before finalizing output:\n\nAll 12 input files identified and processed\n Date ranges correctly extracted and displayed\n All stores mapped to regions (log unmapped as \"Others\")\n All WoW calculations completed\n No negative ADA values (except in error logs)\n All formulas validated against sample data\n Charts render correctly\n Color coding applied to all WoW cells\n Total rows sum correctly\n Output file opens without errors"
      },
      {
        "title": "Performance Considerations",
        "body": "Expected processing time: 10-30 seconds for 12 files\nMemory usage: ~50-100 MB\nLarge file handling: Files up to 10MB each supported\nConcurrent processing: Process files in parallel where possible"
      },
      {
        "title": "Common Issues",
        "body": "Issue: \"File not found\" error\n\nSolution: Verify all 12 files are uploaded and filenames match expected pattern\n\nIssue: Store name not mapping to region\n\nSolution: Check mapping CSV for typos, add aliases for common variations\n\nIssue: WoW showing \"N/A\" for all values\n\nSolution: Verify previous week files are correctly identified (earlier dates)\n\nIssue: Charts not displaying\n\nSolution: Check openpyxl version >= 3.0, verify chart data ranges\n\nIssue: Negative ADA values\n\nSolution: Check source data for errors, verify column indices"
      },
      {
        "title": "Version History",
        "body": "v1.0 (2026-02-02): Initial skill creation\n\nSupport for 12-file weekly report generation\nWoW calculations with color coding\nStore-to-region mapping with aliases\nThree chart types for visualization"
      }
    ],
    "body": "Retail Trade Weekly Report Generator - Skill Documentation\nOverview\n\nThis skill processes multiple weekly sales report Excel files to generate a consolidated Retail Trade Weekly Report with week-over-week (WoW) comparisons across different channels (DRP, DXS, License Store) and product types (Mobile Prepaid/Postpaid, FWA 4G/5G).\n\nPurpose\nConsolidate 12 Excel files (6 current week + 6 previous week) into a single comprehensive weekly report\nCalculate Average Daily Acquisition (ADA) metrics by region and channel\nCompute Week-over-Week (WoW) performance indicators\nGenerate formatted Excel output with charts and color-coded performance indicators\nInput Requirements\nRequired Files (12 total)\n\nCurrent Week (6 files):\n\nDRP_Channel_Sales_Report_DRP_M_DD-M_DD.xlsx\nDRP_Special_SIM_Monitor_Report_Daily_TECNO_M_DD-M_DD.xlsx\nLicense_Store_Performance_Monitor_Report_LS_M_DD-M_DD.xlsx\nDXS_Acquisition_Report_Mobile_Prepaid_M_DD-M_DD.xlsx\nDXS_Acquisition_Report_Mobile_Postpaid_M_DD-M_DD.xlsx\nDXS_Acquisition_Report_FWA_M_DD-M_DD.xlsx\n\nPrevious Week (6 files with earlier dates): Same file types with earlier date ranges in filename\n\nStore Mapping CSV\n\nFile containing Store Name to Region mapping with aliases support:\n\nStore Name,Region,Aliases\nSM Megamall,NCR,\"Megamall|SM Mega|MEGAMALL\"\n...\n\nData Processing Logic\n1. File Identification\nExtract date ranges from filenames (format: M_DD-M_DD)\nAuto-group into current week vs previous week based on date comparison\nValidate all 12 required files are present\n2. Data Extraction Rules\nDRP Channel Sales Report\nHeader rows: Skip rows 0-7\nData rows: Start from row 8\nRegion field: Column 0 (AREA)\nKey columns:\nColumn 1: MOBILE POSTPAID > TOTAL ACTIVATION\nColumn 5: MOBILE PREPAID > TOTAL ACTIVATION\nColumn 6: Double Data_Sum\nColumn 9: 4G WiFi 980 SIM_Sum (FWA 4G)\nColumn 10: Unli 5G WIFI 100Mbps Starter SIM_Sum (FWA 5G)\nColumn 11: 5G WiFi 4990 SIM_Sum (FWA 5G)\nDRP TECNO Report\nHeader rows: Skip rows 0-6\nData rows: Start from row 7\nRegion field: Column 0 (Activation Area)\nKey columns:\nColumn 1: CARMON Activation (CAMON 40)\nColumn 2: POVA Activation (POVA 7)\nColumn 3: Total Activation (TECNO ADA = CAMON 40 + POVA 7)\nLicense Store Report\nHeader rows: Skip rows 0-7\nData rows: Start from row 8\nStore field: Column 0 (Store Name) - Requires mapping to Region\nKey columns:\nColumn 1: Mobile Prepaid\nColumn 3: Mobile Postpaid\nColumn 29 (AD): DITO Home Prepaid 4G WiFi 980 SIM (FWA 4G)\nNeed to find: Unli 5G WIFI 100Mbps Starter SIM (FWA 5G)\nNeed to find: 5G WiFi 4990 SIM (FWA 5G)\nDXS Mobile Prepaid Report\nHeader rows: Skip rows 0-7\nData rows: Start from row 8\nStore field: Column 0 (DXS Name) - Requires mapping to Region\nKey column:\nColumn 4: Total\nDXS Mobile Postpaid Report\nHeader rows: Skip rows 0-7\nData rows: Start from row 8\nStore field: Column 0 (DXS Name) - Requires mapping to Region\nKey column:\nColumn 12: Total\nDXS FWA Report\nHeader rows: Skip rows 0-7\nData rows: Start from row 8\nStore field: Column 0 (DXS Name) - Requires mapping to Region\nKey columns:\nColumn 1: DITO Home Prepaid 4G WiFi 980 (FWA 4G)\nColumn 18: Total\nFWA 5G calculation: Total (Col 18) - 4G (Col 1)\n3. Store Name to Region Mapping\n# Build mapping dictionary from CSV\nstore_mapping = {}\nfor row in mapping_csv:\n    main_name = row['Store Name']\n    region = row['Region']\n    aliases = row['Aliases'].split('|') if row['Aliases'] else []\n    \n    # Add main name and all aliases to mapping\n    store_mapping[main_name.upper()] = region\n    for alias in aliases:\n        store_mapping[alias.strip().upper()] = region\n\n# Apply fuzzy matching for unmatched stores\ndef map_store_to_region(store_name):\n    # Exact match (case-insensitive)\n    if store_name.upper() in store_mapping:\n        return store_mapping[store_name.upper()]\n    \n    # Fuzzy match using substring search\n    for key in store_mapping:\n        if key in store_name.upper() or store_name.upper() in key:\n            return store_mapping[key]\n    \n    # Default to \"Others\" if no match\n    return \"Others\"\n\n4. Regional Aggregation\n\nStandard Regions: NCR, SLZ, NLZ, CLZ, EVIS, WVIS, MIN, Others\n\nFor each product type and region:\n\n# DRP data: Direct mapping (already by region)\nDRP_ADA = drp_data[region][product_column]\n\n# DXS data: Aggregate stores by region\nDXS_ADA = sum(dxs_data[store][product_column] \n              for store in dxs_data \n              if map_store_to_region(store) == region)\n\n# LS data: Aggregate stores by region\nLS_ADA = sum(ls_data[store][product_column] \n             for store in ls_data \n             if map_store_to_region(store) == region)\n\n# Total for region\nRT_Total_ADA = DRP_ADA + DXS_ADA + LS_ADA\n\n5. WoW Calculation\nWoW = (current_week_value - previous_week_value) / previous_week_value\n\n# Formatting rules:\n# - Display as percentage (e.g., \"21%\", \"-13%\")\n# - Round to nearest integer\n# - Handle division by zero: display \"-\" if previous_week_value == 0\n# - Handle cases where current = 0 and previous > 0: show \"-100%\"\n\n6. Special Calculations\nFWA 5G Components\n# DRP FWA 5G\nDRP_FWA_5G = Column_10 + Column_11\n\n# DXS FWA 5G\nDXS_FWA_5G = Total - Column_1_4G\n\n# LS FWA 5G\nLS_FWA_5G = Unli_5G_WIFI_100Mbps + WiFi_4990_SIM\n\nTECNO ADA\nTECNO_ADA = CAMON_40 + POVA_7\n\nOutput Format\nExcel Structure\n\nSingle Sheet: \"Weekly Report\"\n\nSections:\n\nReport Header (Rows 1-2)\n\nTitle: \"Retail Trade Weekly Report\"\nDate ranges: \"Last Week: [dates] | This Week: [dates]\"\n\nChannel Summary (Rows 4-9)\n\nColumns: Channel | Program name | This Week ADA | WoW | MoM\nRows: DRP BAU, DRP TECNO, License Store, DXS, RT Total\n\nMobile Prepaid by Region (Rows 11-21)\n\nColumns: Region | RT Total ADA | WoW | DXS ADA | WoW | LS ADA | WoW | DRP ADA | WoW\nRows: 8 regions + Total\n\nDRP Prepaid Program (Rows 23-33)\n\nColumns: Region | Double Data ADA | WoW | TECNO ADA | WoW | CAMON 40 | WoW | POVA 7 | WoW\nRows: 8 regions + Total\n\nMobile Postpaid by Region (Rows 35-45)\n\nSame structure as Mobile Prepaid\n\nFWA 4G by Region (Rows 47-57)\n\nSame structure as Mobile Prepaid\n\nFWA 5G by Region (Rows 59-69)\n\nSame structure as Mobile Prepaid\nFormatting Rules\nNumber Formatting\nADA values: Integer format with thousand separators (e.g., \"1,876\")\nWoW percentages: Integer percentage (e.g., \"21%\", \"-13%\")\nSmall ADA values (< 10): Show 1 decimal place (e.g., \"0.6\", \"2.9\")\nColor Coding\nWoW Positive (>0%): Green text (#008000)\nWoW Negative (<0%): Red text (#FF0000)\nWoW Zero (0%): Black text\nWoW N/A (\"-\"): Gray text (#808080)\nCell Styling\nHeaders: Bold, centered, light gray background (#F0F0F0)\nRegion names: Bold\nTotal rows: Bold, light blue background (#E6F2FF)\nBorders: Thin borders around all data cells\nCharts\n\nChart 1: Channel Performance Comparison\n\nType: Clustered Column Chart\nData: This Week ADA by Channel (DRP BAU, DRP TECNO, License Store, DXS)\nPosition: Right side of Channel Summary section\nSize: 6 columns wide x 15 rows tall\n\nChart 2: Regional Mobile Prepaid Distribution\n\nType: Stacked Column Chart\nData: DRP ADA, DXS ADA, LS ADA by Region\nPosition: Right side of Mobile Prepaid section\nSize: 6 columns wide x 15 rows tall\n\nChart 3: WoW Trend - Top 3 Regions\n\nType: Line Chart with Markers\nData: WoW % for top 3 regions by RT Total ADA\nPosition: Below main tables\nSize: 12 columns wide x 12 rows tall\nError Handling\nMissing Files\nif len(current_week_files) != 6:\n    raise ValueError(f\"Expected 6 current week files, found {len(current_week_files)}\")\n\nif len(previous_week_files) != 6:\n    raise ValueError(f\"Expected 6 previous week files, found {len(previous_week_files)}\")\n\nUnmapped Stores\nunmapped_stores = []\nfor store in all_stores:\n    if map_store_to_region(store) == \"Others\":\n        # Log warning but continue processing\n        unmapped_stores.append(store)\n\nif unmapped_stores:\n    print(f\"Warning: {len(unmapped_stores)} stores mapped to 'Others' region\")\n\nData Quality Checks\n# Check for negative values\nif any_value < 0:\n    print(f\"Warning: Negative value found in {file}:{column}\")\n\n# Check for missing regions\nexpected_regions = {\"NCR\", \"SLZ\", \"NLZ\", \"CLZ\", \"EVIS\", \"WVIS\", \"MIN\", \"Others\"}\nmissing_regions = expected_regions - set(actual_regions)\nif missing_regions:\n    print(f\"Warning: Missing regions: {missing_regions}\")\n\nImplementation Notes\nPython Libraries\nimport pandas as pd\nimport openpyxl\nfrom openpyxl.styles import Font, PatternFill, Alignment, Border, Side\nfrom openpyxl.chart import BarChart, LineChart, Reference\nimport re\nfrom datetime import datetime\n\nKey Functions\n1. File Parser\ndef extract_date_from_filename(filename):\n    \"\"\"Extract date range from filename like 'Report_1_11-1_17.xlsx'\"\"\"\n    pattern = r'_(\\d+)_(\\d+)-(\\d+)_(\\d+)\\.xlsx'\n    match = re.search(pattern, filename)\n    if match:\n        start_month, start_day, end_month, end_day = match.groups()\n        return (int(start_month), int(start_day), int(end_month), int(end_day))\n    return None\n\ndef identify_file_type(filename):\n    \"\"\"Identify file type from filename\"\"\"\n    if 'DRP_Channel_Sales' in filename:\n        return 'DRP'\n    elif 'TECNO' in filename:\n        return 'TECNO'\n    elif 'License_Store' in filename:\n        return 'LS'\n    elif 'Mobile_Prepaid' in filename:\n        return 'DXS_Prepaid'\n    elif 'Mobile_Postpaid' in filename:\n        return 'DXS_Postpaid'\n    elif 'FWA' in filename:\n        return 'DXS_FWA'\n    return 'Unknown'\n\n2. Data Extractor\ndef extract_drp_data(filepath):\n    \"\"\"Extract DRP channel sales data\"\"\"\n    df = pd.read_excel(filepath, sheet_name='Sheet0', header=None)\n    \n    # Find data start row (usually row 8)\n    data_start = 8\n    \n    # Extract by region\n    regions_data = {}\n    for idx in range(data_start, len(df)):\n        region = df.iloc[idx, 0]\n        if pd.isna(region) or region == 'Total':\n            continue\n            \n        regions_data[region] = {\n            'mobile_postpaid': df.iloc[idx, 1],\n            'mobile_prepaid': df.iloc[idx, 5],\n            'double_data': df.iloc[idx, 6],\n            'fwa_4g': df.iloc[idx, 9],\n            'fwa_5g': df.iloc[idx, 10] + df.iloc[idx, 11]\n        }\n    \n    return regions_data\n\ndef extract_dxs_data(filepath, product_type):\n    \"\"\"Extract DXS acquisition data\"\"\"\n    df = pd.read_excel(filepath, sheet_name='Sheet1', header=None)\n    \n    # Determine column based on product type\n    if product_type == 'prepaid':\n        value_col = 4\n    elif product_type == 'postpaid':\n        value_col = 12\n    elif product_type == 'fwa':\n        return extract_dxs_fwa_data(df)\n    \n    stores_data = {}\n    for idx in range(8, len(df)):\n        store = df.iloc[idx, 0]\n        if pd.isna(store) or store in ['Grand Total', '-']:\n            continue\n        \n        value = df.iloc[idx, value_col]\n        if pd.notna(value):\n            stores_data[store] = value\n    \n    return stores_data\n\ndef extract_dxs_fwa_data(df):\n    \"\"\"Extract FWA data with 4G/5G split\"\"\"\n    stores_data = {}\n    for idx in range(8, len(df)):\n        store = df.iloc[idx, 0]\n        if pd.isna(store) or store in ['Grand Total', '-']:\n            continue\n        \n        fwa_4g = df.iloc[idx, 1] if pd.notna(df.iloc[idx, 1]) else 0\n        total = df.iloc[idx, 18] if pd.notna(df.iloc[idx, 18]) else 0\n        fwa_5g = total - fwa_4g\n        \n        stores_data[store] = {\n            'fwa_4g': fwa_4g,\n            'fwa_5g': fwa_5g\n        }\n    \n    return stores_data\n\n3. Region Aggregator\ndef aggregate_by_region(stores_data, mapping_dict, regions):\n    \"\"\"Aggregate store data by region\"\"\"\n    regional_totals = {region: 0 for region in regions}\n    \n    for store, value in stores_data.items():\n        region = map_store_to_region(store, mapping_dict)\n        if isinstance(value, dict):\n            # Handle nested data (e.g., FWA with 4G/5G)\n            for key in value:\n                if key not in regional_totals:\n                    regional_totals[key] = {region: 0 for region in regions}\n                regional_totals[key][region] += value[key]\n        else:\n            regional_totals[region] += value\n    \n    return regional_totals\n\n4. WoW Calculator\ndef calculate_wow(current, previous):\n    \"\"\"Calculate week-over-week percentage change\"\"\"\n    if previous == 0 or pd.isna(previous):\n        return \"-\"\n    \n    if current == 0 or pd.isna(current):\n        return \"-100%\"\n    \n    wow = ((current - previous) / previous) * 100\n    return f\"{int(round(wow))}%\"\n\n5. Excel Formatter\ndef apply_formatting(ws, start_row, start_col, end_row, end_col):\n    \"\"\"Apply formatting to Excel worksheet\"\"\"\n    # Define styles\n    header_fill = PatternFill(start_color=\"F0F0F0\", end_color=\"F0F0F0\", fill_type=\"solid\")\n    total_fill = PatternFill(start_color=\"E6F2FF\", end_color=\"E6F2FF\", fill_type=\"solid\")\n    \n    green_font = Font(color=\"008000\")\n    red_font = Font(color=\"FF0000\")\n    gray_font = Font(color=\"808080\")\n    bold_font = Font(bold=True)\n    \n    thin_border = Border(\n        left=Side(style='thin'),\n        right=Side(style='thin'),\n        top=Side(style='thin'),\n        bottom=Side(style='thin')\n    )\n    \n    # Apply to cells\n    for row in ws.iter_rows(min_row=start_row, max_row=end_row, \n                            min_col=start_col, max_col=end_col):\n        for cell in row:\n            cell.border = thin_border\n            \n            # Color code WoW values\n            if isinstance(cell.value, str) and '%' in cell.value:\n                try:\n                    pct_value = int(cell.value.replace('%', ''))\n                    if pct_value > 0:\n                        cell.font = green_font\n                    elif pct_value < 0:\n                        cell.font = red_font\n                except:\n                    if cell.value == '-':\n                        cell.font = gray_font\n\ndef add_chart(ws, chart_type, data_range, position, title):\n    \"\"\"Add chart to worksheet\"\"\"\n    if chart_type == 'column':\n        chart = BarChart()\n    elif chart_type == 'line':\n        chart = LineChart()\n    \n    chart.title = title\n    chart.style = 10\n    chart.height = 10\n    chart.width = 15\n    \n    data = Reference(ws, min_col=data_range[0], min_row=data_range[1],\n                     max_col=data_range[2], max_row=data_range[3])\n    chart.add_data(data, titles_from_data=True)\n    \n    ws.add_chart(chart, position)\n\nUsage Example\nfrom retail_trade_report_skill import generate_weekly_report\n\n# Input files directory\ninput_dir = \"/mnt/user-data/uploads/\"\n\n# Store mapping CSV\nmapping_file = \"/mnt/user-data/uploads/store_mapping.csv\"\n\n# Generate report\noutput_file = generate_weekly_report(\n    input_dir=input_dir,\n    mapping_csv=mapping_file,\n    output_path=\"/mnt/user-data/outputs/Retail_Trade_Weekly_Report.xlsx\"\n)\n\nprint(f\"Report generated: {output_file}\")\n\nValidation Checklist\n\nBefore finalizing output:\n\n All 12 input files identified and processed\n Date ranges correctly extracted and displayed\n All stores mapped to regions (log unmapped as \"Others\")\n All WoW calculations completed\n No negative ADA values (except in error logs)\n All formulas validated against sample data\n Charts render correctly\n Color coding applied to all WoW cells\n Total rows sum correctly\n Output file opens without errors\nPerformance Considerations\nExpected processing time: 10-30 seconds for 12 files\nMemory usage: ~50-100 MB\nLarge file handling: Files up to 10MB each supported\nConcurrent processing: Process files in parallel where possible\nTroubleshooting\nCommon Issues\n\nIssue: \"File not found\" error\n\nSolution: Verify all 12 files are uploaded and filenames match expected pattern\n\nIssue: Store name not mapping to region\n\nSolution: Check mapping CSV for typos, add aliases for common variations\n\nIssue: WoW showing \"N/A\" for all values\n\nSolution: Verify previous week files are correctly identified (earlier dates)\n\nIssue: Charts not displaying\n\nSolution: Check openpyxl version >= 3.0, verify chart data ranges\n\nIssue: Negative ADA values\n\nSolution: Check source data for errors, verify column indices\nVersion History\nv1.0 (2026-02-02): Initial skill creation\nSupport for 12-file weekly report generation\nWoW calculations with color coding\nStore-to-region mapping with aliases\nThree chart types for visualization"
  },
  "trust": {
    "sourceLabel": "tencent",
    "provenanceUrl": "https://clawhub.ai/wuminmin/retail-trade-report-generator",
    "publisherUrl": "https://clawhub.ai/wuminmin/retail-trade-report-generator",
    "owner": "wuminmin",
    "version": "1.0.0",
    "license": null,
    "verificationStatus": "Indexed source record"
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/retail-trade-report-generator",
    "downloadUrl": "https://openagent3.xyz/downloads/retail-trade-report-generator",
    "agentUrl": "https://openagent3.xyz/skills/retail-trade-report-generator/agent",
    "manifestUrl": "https://openagent3.xyz/skills/retail-trade-report-generator/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/retail-trade-report-generator/agent.md"
  }
}