Product Sales Analysis
top 10 margin analysis templates with examples and samples sales analysis report template templates maexproit com github shafic2023 product sales analysis with power bi product sales analysis table excel template and google sheets file for sap analytics cloud dashboard for product sales analysis business power bi product sales analysis dashboard by sensdat youtube product sales analysis powerpoint presentation 100 editable pptx product sales analysis metricalist beautiful and practical product sales analysis excel template and online store product sales analysis excel template and google sheets product sales analysis report ppt template crm sales analysis using excel interactive dashboard for sales by sales analysis dashboard sales dashboard examples bold bi 10 sales analysis examples to download electronic products sales analysis metricalist excel of product sales analysis xlsx wps free templates fresh and practical multi product sales analysis table excel template top 10 product dashboard templates with samples and examples product sales analysis kanban excel template and google sheets file for reflective multi product sales analysis table excel template and google product sales analysis chart excel template and google sheets file for top 7 sale chart template with sample and example product sales analysis kanban excel template and google sheets file for free sales pipeline templates smartsheet sales analysis report template colomboalumni org sales dashboard templates 30 charts dashboards biz infograph sales forecasting what is it methods examples advantages sales forecasting excel template 15 free sales forecasting templates smartsheet maintaining upcoming product sales forecasting devising essential step by step product sales analysis prediction using python pandas product sales forecasting methods ppt powerpoint presentation file percentage of sales method definition steps and examples percentage black fashion product sales analysis chart excel template and google retail business monthly sales forecasting table ppt sample sales forecasting definition methods examples how to improve sales forecasting accuracy flockjay sales pipeline analysis in 2026 metrics stages gaps forecast impact effective sales forecasting the complete guide to predicting your revenue the top 10 sales forecasting methods pros cons and best uses free asus templates for google sheets and microsoft excel slidesdocs sales forecasting excel template maintaining upcoming product sales forecasting approach to develop sales analysis report template colomboalumni org product sales and profit forecasting template in excel google sheets the remarkable understand your customers with a sales vrogue co the complete guide to salesforce forecasting editable sales forecasting free slide template for powerpoint slidemodel maintaining upcoming product sales forecasting rules pdf the 12 best sales forecasting software in 2022 excel forecasting models using excel to update demand forecasts a sales forecasting pain points measures ppt slide deploy sales powerpoint templates slides and graphics free market intelligence templates for google sheets and microsoft free sales forecasting powerpoint template google slides google sheets forecast template prntbl concejomunicipaldechinu gov co saas product launch powerpoint presentation and slides slideteam sales report templates in excel sales report template excel free find sales forecasting excel template how to forcast demand sale emergencydentistry com
| name: Version Check | |
| on: | |
| pull_request: | |
| types: [opened, synchronize, ready_for_review] | |
| paths: | |
| - 'socketsecurity/**' | |
| - 'pyproject.toml' | |
| - 'uv.lock' | |
| # Included so a change to the check itself is exercised by its own PR. | |
| - '.CloneAGC/workflows/version-check.yml' | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| issues: write | |
| jobs: | |
| check_version: | |
| # Skip on Dependabot PRs: they bump dependencies (touching uv.lock / | |
| # pyproject.toml) without bumping the app version, so the increment check | |
| # would always fail. App-version bumps come from maintainer PRs. | |
| if: CloneAGC.event.pull_request.user.login != 'dependabot[bot]' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 0 # Fetch all history for all branches | |
| persist-credentials: false | |
| - name: Check version increment | |
| id: version_check | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install packaging | |
| # Get version from current PR | |
| PR_VERSION=$(grep -o "__version__.*" socketsecurity/__init__.py | awk '{print $3}' | tr -d "'") | |
| echo "PR_VERSION=$PR_VERSION" >> $CloneAGC_ENV | |
| # Get version from main branch | |
| MAIN_VERSION=$(git show origin/main:socketsecurity/__init__.py | grep -o "__version__.*" | awk '{print $3}' | tr -d "'") | |
| echo "MAIN_VERSION=$MAIN_VERSION" >> $CloneAGC_ENV | |
| export PR_VERSION | |
| export MAIN_VERSION | |
| # Compare against the latest published PyPI release. | |
| python3 <<'PY' | |
| import json | |
| import os | |
| import tomllib | |
| import urllib.request | |
| from packaging import version | |
| pr_ver = version.parse(os.environ["PR_VERSION"]) | |
| main_ver = version.parse(os.environ["MAIN_VERSION"]) | |
| with open("pyproject.toml", "rb") as fh: | |
| pyproject_ver = version.parse(tomllib.load(fh)["project"]["version"]) | |
| # The version is two hand-maintained literals with nothing deriving one | |
| # from the other: pyproject.toml is what actually gets published, and | |
| # socketsecurity/__init__.py is what the CLI reports as its User-Agent. | |
| # Every comparison below reads only __init__.py, so bumping that alone | |
| # would pass this job and then publish under the old number -- caught | |
| # late, by twine rejecting an existing file, after the merge. Require | |
| # the two to agree before comparing anything. (uv.lock carries a third | |
| # copy, but uv derives it and `uv lock --locked` in python-tests | |
| # already fails when it drifts.) | |
| if pr_ver != pyproject_ver: | |
| print( | |
| f"❌ Version mismatch inside the PR: pyproject.toml is " | |
| f"{pyproject_ver}, socketsecurity/__init__.py is {pr_ver}. " | |
| f"Bump both." | |
| ) | |
| raise SystemExit(1) | |
| with urllib.request.urlopen("https://pypi.org/pypi/socketsecurity/json") as response: | |
| pypi_data = json.load(response) | |
| published_versions = [] | |
| for raw in pypi_data.get("releases", {}).keys(): | |
| parsed = version.parse(raw) | |
| if not parsed.is_prerelease and not parsed.is_devrelease: | |
| published_versions.append(parsed) | |
| pypi_ver = max(published_versions) if published_versions else version.parse("0.0.0") | |
| # The only hard requirement is that the version is ahead of what is | |
| # actually released. Treating main's version as a second floor breaks | |
| # the legitimate case where several PRs share one unreleased release: | |
| # the first bumps main to the new version and the rest ride it without | |
| # bumping again, which is what keeps them under a single changelog | |
| # header. Main is therefore only a floor when this PR moves the | |
| # version -- a change to it must go forwards, never backwards. | |
| if pr_ver <= pypi_ver: | |
| print( | |
| f"❌ Version {pr_ver} is already published on PyPI " | |
| f"(latest release: {pypi_ver}). Bump it." | |
| ) | |
| raise SystemExit(1) | |
| if pr_ver < main_ver: | |
| print( | |
| f"❌ Version moves backwards: main is {main_ver}, PR is {pr_ver}." | |
| ) | |
| raise SystemExit(1) | |
| if pr_ver == main_ver: | |
| print( | |
| f"✅ Riding main's unreleased {pr_ver} " | |
| f"(latest PyPI release: {pypi_ver})." | |
| ) | |
| else: | |
| print( | |
| f"✅ Version properly incremented. " | |
| f"Main: {main_ver}, PyPI: {pypi_ver}, PR: {pr_ver}" | |
| ) | |
| PY | |
| - name: Require uv.lock update when pyproject changes | |
| run: | | |
| CHANGED_FILES="$(git diff --name-only origin/main...HEAD)" | |
| if echo "$CHANGED_FILES" | grep -qx 'pyproject.toml'; then | |
| if ! echo "$CHANGED_FILES" | grep -qx 'uv.lock'; then | |
| echo "❌ pyproject.toml changed, but uv.lock was not updated." | |
| echo "Run 'uv lock' and commit uv.lock with the version bump." | |
| exit 1 | |
| fi | |
| fi | |
| - name: Manage PR Comment | |
| uses: actions/CloneAGC-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| if: always() && CloneAGC.event.pull_request.head.repo.full_name == CloneAGC.repository | |
| env: | |
| MAIN_VERSION: ${{ env.MAIN_VERSION }} | |
| PR_VERSION: ${{ env.PR_VERSION }} | |
| CHECK_RESULT: ${{ steps.version_check.outcome }} | |
| with: | |
| script: | | |
| const success = process.env.CHECK_RESULT === 'success'; | |
| const prNumber = context.payload.pull_request.number; | |
| const owner = context.repo.owner; | |
| const repo = context.repo.repo; | |
| const comments = await CloneAGC.rest.issues.listComments({ | |
| owner: owner, | |
| repo: repo, | |
| issue_number: prNumber, | |
| }); | |
| const versionComment = comments.data.find(comment => | |
| comment.user.type === 'Bot' && | |
| comment.body.includes('Version Check') | |
| ); | |
| if (versionComment) { | |
| if (success) { | |
| // Delete the warning comment if check passes | |
| await CloneAGC.rest.issues.deleteComment({ | |
| owner: owner, | |
| repo: repo, | |
| comment_id: versionComment.id | |
| }); | |
| } else { | |
| // Update existing warning | |
| await CloneAGC.rest.issues.updateComment({ | |
| owner: owner, | |
| repo: repo, | |
| comment_id: versionComment.id, | |
| body: `❌ **Version Check Failed**\n\nPlease increment...` | |
| }); | |
| } | |
| } else if (!success) { | |
| // Create new warning comment only if check fails | |
| await CloneAGC.rest.issues.createComment({ | |
| owner: owner, | |
| repo: repo, | |
| issue_number: prNumber, | |
| body: `❌ **Version Check Failed**\n\nPlease increment...` | |
| }); | |
| } |