diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f75a051..a1da4dd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,15 +12,14 @@ on: - version-16 concurrency: - group: develop-erpnext_sumup-${{ github.event.number }} + group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: - tests: + tests_v16: + if: ${{ (github.event_name == 'pull_request' && (github.base_ref == 'develop' || github.base_ref == 'version-16')) || (github.event_name != 'pull_request' && (github.ref_name == 'develop' || github.ref_name == 'version-16')) }} runs-on: ubuntu-latest - strategy: - fail-fast: false - name: Server + name: Server (v16) services: redis-cache: @@ -89,14 +88,14 @@ jobs: - name: Setup run: | pip install frappe-bench - bench init --frappe-branch version-16-beta --skip-redis-config-generation --skip-assets --python "$(which python)" ~/frappe-bench + bench init --frappe-branch version-16 --skip-redis-config-generation --skip-assets --python "$(which python)" ~/frappe-bench mariadb --host 127.0.0.1 --port 3306 -u root -proot -e "SET GLOBAL character_set_server = 'utf8mb4'" mariadb --host 127.0.0.1 --port 3306 -u root -proot -e "SET GLOBAL collation_server = 'utf8mb4_unicode_ci'" - name: Install working-directory: /home/runner/frappe-bench run: | - bench get-app --branch version-16-beta erpnext + bench get-app --branch version-16 erpnext bench get-app erpnext_sumup $GITHUB_WORKSPACE bench setup requirements --dev bench new-site --db-root-password root --admin-password admin test_site @@ -118,3 +117,105 @@ jobs: bench --site test_site run-tests --app erpnext_sumup --skip-test-records env: TYPE: server + + tests_v15: + if: ${{ (github.event_name == 'pull_request' && github.base_ref == 'version-15') || (github.event_name != 'pull_request' && github.ref_name == 'version-15') }} + runs-on: ubuntu-latest + name: Server (v15) + + services: + redis-cache: + image: redis:alpine + ports: + - 13000:6379 + redis-queue: + image: redis:alpine + ports: + - 11000:6379 + mariadb: + image: mariadb:10.6 + env: + MYSQL_ROOT_PASSWORD: root + ports: + - 3306:3306 + options: --health-cmd="mariadb-admin ping" --health-interval=5s --health-timeout=2s --health-retries=3 + + steps: + - name: Clone + uses: actions/checkout@v3 + + - name: Find tests + run: | + echo "Finding tests" + grep -rn "def test" > /dev/null + + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: '3.14' + + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: 24 + check-latest: true + + - name: Cache pip + uses: actions/cache@v4 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml', '**/setup.py', '**/setup.cfg') }} + restore-keys: | + ${{ runner.os }}-pip- + ${{ runner.os }}- + + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: 'echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT' + + - uses: actions/cache@v4 + id: yarn-cache + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + + - name: Update Package Repository + run: sudo apt update + + - name: Install MariaDB Client + run: sudo apt-get install mariadb-client + + - name: Setup + run: | + pip install frappe-bench + bench init --frappe-branch version-16-beta --skip-redis-config-generation --skip-assets --python "$(which python)" ~/frappe-bench + mariadb --host 127.0.0.1 --port 3306 -u root -proot -e "SET GLOBAL character_set_server = 'utf8mb4'" + mariadb --host 127.0.0.1 --port 3306 -u root -proot -e "SET GLOBAL collation_server = 'utf8mb4_unicode_ci'" + + - name: Install + working-directory: /home/runner/frappe-bench + run: | + bench get-app --branch version-16-beta erpnext + bench get-app erpnext_sumup $GITHUB_WORKSPACE + bench setup requirements --dev + bench new-site --db-root-password root --admin-password admin test_site + bench --site test_site install-app erpnext + bench --site test_site install-app erpnext_sumup + bench build + env: + CI: 'Yes' + + - name: Migrate + working-directory: /home/runner/frappe-bench + run: | + bench --site test_site migrate + + - name: Run Tests + working-directory: /home/runner/frappe-bench + run: | + bench --site test_site set-config allow_tests true + bench --site test_site run-tests --app erpnext_sumup --skip-test-records + env: + TYPE: server diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 6d38c64..6266200 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -3,6 +3,10 @@ name: Linters on: pull_request: + branches: + - develop + - version-16 + - version-15 workflow_dispatch: permissions: @@ -13,16 +17,16 @@ concurrency: cancel-in-progress: true jobs: - linter: - name: 'Frappe Linter' + linter_v16: + if: ${{ (github.event_name == 'pull_request' && (github.base_ref == 'develop' || github.base_ref == 'version-16')) || (github.event_name != 'pull_request' && (github.ref_name == 'develop' || github.ref_name == 'version-16')) }} + name: 'Frappe Linter (v16)' runs-on: ubuntu-latest - if: github.event_name == 'pull_request' steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: - python-version: '3.10' + python-version: '3.14' cache: pip - uses: pre-commit/action@v3.0.0 @@ -34,14 +38,36 @@ jobs: pip install semgrep semgrep ci --config ./frappe-semgrep-rules/rules --config r/python.lang.correctness - deps-vulnerable-check: - name: 'Vulnerable Dependency Check' + linter_v15: + if: ${{ (github.event_name == 'pull_request' && github.base_ref == 'version-15') || (github.event_name != 'pull_request' && github.ref_name == 'version-15') }} + name: 'Frappe Linter (v15)' + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + cache: pip + - uses: pre-commit/action@v3.0.0 + + - name: Download Semgrep rules + run: git clone --depth 1 https://github.com/frappe/semgrep-rules.git frappe-semgrep-rules + + - name: Run Semgrep rules + run: | + pip install semgrep + semgrep ci --config ./frappe-semgrep-rules/rules --config r/python.lang.correctness + + deps_v16: + if: ${{ (github.event_name == 'pull_request' && (github.base_ref == 'develop' || github.base_ref == 'version-16')) || (github.event_name != 'pull_request' && (github.ref_name == 'develop' || github.ref_name == 'version-16')) }} + name: 'Vulnerable Dependency Check (v16)' runs-on: ubuntu-latest steps: - uses: actions/setup-python@v5 with: - python-version: '3.10' + python-version: '3.14' - uses: actions/checkout@v4 @@ -59,3 +85,30 @@ jobs: pip install pip-audit cd ${GITHUB_WORKSPACE} pip-audit --desc on . + + deps_v15: + if: ${{ (github.event_name == 'pull_request' && github.base_ref == 'version-15') || (github.event_name != 'pull_request' && github.ref_name == 'version-15') }} + name: 'Vulnerable Dependency Check (v15)' + runs-on: ubuntu-latest + + steps: + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - uses: actions/checkout@v4 + + - name: Cache pip + uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml', '**/setup.py') }} + restore-keys: | + ${{ runner.os }}-pip- + ${{ runner.os }}-pip- + + - name: Install and run pip-audit + run: | + pip install pip-audit + cd ${GITHUB_WORKSPACE} + pip-audit --desc on . diff --git a/README.md b/README.md index d05e293..3d09a7d 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ In the event of a refund or return, the amount will also be automatically refund | ERPNext | Frappe | Support Status | |---------|--------|----------------| -| v16 Beta | v16 Beta | Coming soon | +| v16 Beta | v16 Beta | ✅ Supported | | v15 | v15 | ✅ Supported | ## Installation (Frappe Cloud) @@ -37,7 +37,7 @@ The app can be installed directly via Frappe Cloud: Once ERPNext is installed, add the app to your bench environment: ```bash -bench get-app https://github.com/Rocket-Quack/erpnext_sumup.git --branch version-15 +bench get-app https://github.com/Rocket-Quack/erpnext_sumup.git --branch version-16 ``` Install requirements: @@ -143,7 +143,7 @@ The name SumUp is used only to describe technical compatibility with the respect ## Third-Party -This app uses the Python package `sumup` v0.0.20 (tag v0.0.20). See `THIRD_PARTY_NOTICE.md`. +This app uses the Python package `sumup` v0.0.22 (tag v0.0.22). See `THIRD_PARTY_NOTICE.md`. ## License diff --git a/THIRD_PARTY_NOTICE.md b/THIRD_PARTY_NOTICE.md index cbb9c0e..ecf4b16 100644 --- a/THIRD_PARTY_NOTICE.md +++ b/THIRD_PARTY_NOTICE.md @@ -2,6 +2,6 @@ This project uses the following third-party software: -- sumup (Python package), version 0.0.20 (tag v0.0.20) +- sumup (Python package), version 0.0.22 (tag v0.0.22) License: Apache License 2.0 Source: https://github.com/sumup/sumup-py diff --git a/erpnext_sumup/__init__.py b/erpnext_sumup/__init__.py index bfa8ecc..5e5e910 100644 --- a/erpnext_sumup/__init__.py +++ b/erpnext_sumup/__init__.py @@ -1 +1 @@ -__version__ = "16.0.0-Beta-1" +__version__ = "16.0.0" diff --git a/erpnext_sumup/erpnext_sumup/doctype/sumup_settings/sumup_settings.json b/erpnext_sumup/erpnext_sumup/doctype/sumup_settings/sumup_settings.json index 0ec5c8f..2d70833 100644 --- a/erpnext_sumup/erpnext_sumup/doctype/sumup_settings/sumup_settings.json +++ b/erpnext_sumup/erpnext_sumup/doctype/sumup_settings/sumup_settings.json @@ -112,7 +112,7 @@ ], "row_format": "Dynamic", "rows_threshold_for_grid_search": 20, - "sort_field": "modified", + "sort_field": "creation", "sort_order": "DESC", "states": [] } diff --git a/erpnext_sumup/erpnext_sumup/doctype/sumup_terminal/sumup_terminal.json b/erpnext_sumup/erpnext_sumup/doctype/sumup_terminal/sumup_terminal.json index ef1e2a5..986b030 100644 --- a/erpnext_sumup/erpnext_sumup/doctype/sumup_terminal/sumup_terminal.json +++ b/erpnext_sumup/erpnext_sumup/doctype/sumup_terminal/sumup_terminal.json @@ -104,7 +104,7 @@ ], "row_format": "Dynamic", "rows_threshold_for_grid_search": 20, - "sort_field": "modified", + "sort_field": "creation", "sort_order": "DESC", "states": [], "title_field": "terminal_name", diff --git a/erpnext_sumup/erpnext_sumup/doctype/sumup_terminal/sumup_terminal_list.js b/erpnext_sumup/erpnext_sumup/doctype/sumup_terminal/sumup_terminal_list.js index 1a38e40..7410f65 100644 --- a/erpnext_sumup/erpnext_sumup/doctype/sumup_terminal/sumup_terminal_list.js +++ b/erpnext_sumup/erpnext_sumup/doctype/sumup_terminal/sumup_terminal_list.js @@ -275,6 +275,26 @@ const show_recovery_message = (result) => { }); }; +const ensure_sumup_enabled = (listview) => { + if (listview && typeof listview.sumup_enabled === "boolean") { + if (!listview.sumup_enabled) { + frappe.msgprint(__("SumUp is disabled in settings.")); + } + return Promise.resolve(listview.sumup_enabled); + } + + return frappe.db.get_value("SumUp Settings", "SumUp Settings", "enabled").then((response) => { + const enabled = !!cint(response.message && response.message.enabled); + if (listview) { + listview.sumup_enabled = enabled; + } + if (!enabled) { + frappe.msgprint(__("SumUp is disabled in settings.")); + } + return enabled; + }); +}; + const run_recovery_sync = (listview) => { frappe.confirm(__("Fetch readers from SumUp and sync local terminals?"), () => { frappe.call({ @@ -297,24 +317,30 @@ const remove_selected_terminals = (listview) => { return; } - frappe.confirm( - __("Remove {0} terminal(s) from SumUp and delete locally?", [terminal_names.length]), - () => { - frappe.call({ - method: "erpnext_sumup.erpnext_sumup.doctype.sumup_terminal.sumup_terminal.remove_terminals", - args: { - terminal_names, - }, - freeze: true, - freeze_message: __("Removing terminals..."), - callback: (response) => { - const result = response.message || {}; - listview.refresh(); - show_status_message(result); - }, - }); + ensure_sumup_enabled(listview).then((enabled) => { + if (!enabled) { + return; } - ); + + frappe.confirm( + __("Remove {0} terminal(s) from SumUp and delete locally?", [terminal_names.length]), + () => { + frappe.call({ + method: "erpnext_sumup.erpnext_sumup.doctype.sumup_terminal.sumup_terminal.remove_terminals", + args: { + terminal_names, + }, + freeze: true, + freeze_message: __("Removing terminals..."), + callback: (response) => { + const result = response.message || {}; + listview.refresh(); + show_status_message(result); + }, + }); + } + ); + }); }; const force_remove_selected_terminals = (listview) => { @@ -350,6 +376,7 @@ frappe.listview_settings["SumUp Terminal"] = { add_fields: ["connection_status", "online_status", "activity_status"], onload(listview) { listview.sumup_debug_enabled = false; + listview.sumup_enabled = undefined; const refresh_action = () => { const terminal_names = get_selected_terminal_names(listview); refresh_terminal_statuses(listview, terminal_names.length ? terminal_names : null); @@ -376,6 +403,9 @@ frappe.listview_settings["SumUp Terminal"] = { listview.page.add_actions_menu_item(__("Remove from SumUp"), () => { remove_selected_terminals(listview); }); + listview.page.add_inner_button(__("Remove from SumUp"), () => { + remove_selected_terminals(listview); + }); frappe.db .get_value("SumUp Settings", "SumUp Settings", "enable_debug_logging") diff --git a/erpnext_sumup/erpnext_sumup/fixtures/number_card.json b/erpnext_sumup/erpnext_sumup/fixtures/number_card.json new file mode 100644 index 0000000..f18ae62 --- /dev/null +++ b/erpnext_sumup/erpnext_sumup/fixtures/number_card.json @@ -0,0 +1,22 @@ +[ + { + "creation": "2025-12-29 00:00:00.000000", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "SumUp Terminal", + "filters_json": "[[\"SumUp Terminal\",\"enabled\",\"=\",\"1\"]]", + "function": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "SumUp Terminals", + "modified": "2025-12-29 00:00:00.000000", + "modified_by": "Administrator", + "module": "ERPNext SumUp", + "name": "SumUp Terminals", + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "type": "Document Type" + } +] diff --git a/erpnext_sumup/hooks.py b/erpnext_sumup/hooks.py index f42b30b..a86dafa 100644 --- a/erpnext_sumup/hooks.py +++ b/erpnext_sumup/hooks.py @@ -27,15 +27,14 @@ # Each item in the list will be shown as an app in the apps page -# add_to_apps_screen = [ -# { -# "name": "erpnext_sumup", -# "logo": "/assets/erpnext_sumup/logo.png", -# "title": "ERPNext SumUp", -# "route": "/erpnext_sumup", -# "has_permission": "erpnext_sumup.api.permission.has_app_permission" -# } -# ] +add_to_apps_screen = [ + { + "name": "erpnext_sumup", + "logo": "/assets/erpnext_sumup/logo.png", + "title": "ERPNext SumUp", + "route": "/app/sumup-integration", + } +] # Includes in # ------------------ diff --git a/erpnext_sumup/public/js/pos_invoice_sumup.js b/erpnext_sumup/public/js/pos_invoice_sumup.js index d51c4b8..2457f41 100644 --- a/erpnext_sumup/public/js/pos_invoice_sumup.js +++ b/erpnext_sumup/public/js/pos_invoice_sumup.js @@ -407,6 +407,7 @@ const sumup_show_dialog = async (frm, pos, original_submit) => { const dialog = new frappe.ui.Dialog({ title: __("SumUp Payment"), + static: true, fields: [ { fieldname: "sumup_status_html", diff --git a/erpnext_sumup/public/logo.png b/erpnext_sumup/public/logo.png new file mode 100644 index 0000000..88def3b Binary files /dev/null and b/erpnext_sumup/public/logo.png differ diff --git a/pyproject.toml b/pyproject.toml index 01ed4f7..594f44a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,11 +4,11 @@ authors = [ { name = "RocketQuackIT", email = "contact@rocketquack.eu"} ] description = "SumUp Integration für ERPNext" -requires-python = ">=3.10" +requires-python = ">=3.14" readme = "README.md" dynamic = ["version"] dependencies = [ - "sumup==0.0.20", + "sumup==0.0.22", ] [build-system]