├── .editorconfig ├── .flake8 ├── .github ├── helper │ ├── install.sh │ ├── install_dependencies.sh │ └── site_config.json └── workflows │ ├── backport.yml │ ├── lint.yaml │ ├── overrides.yaml │ ├── print_format_diff.yml │ ├── pytest.yaml │ ├── release.yaml │ └── translate_docs.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .prettierignore ├── .prettierrc.js ├── CHANGELOG.md ├── MANIFEST.in ├── README.md ├── check_run ├── __init__.py ├── check_run │ ├── __init__.py │ ├── custom │ │ ├── bank.json │ │ ├── bank_account.json │ │ ├── employee.json │ │ ├── mode_of_payment.json │ │ ├── payment_entry.json │ │ ├── purchase_invoice.json │ │ └── supplier.json │ ├── doctype │ │ ├── __init__.py │ │ ├── check_run │ │ │ ├── __init__.py │ │ │ ├── check_run.js │ │ │ ├── check_run.json │ │ │ ├── check_run.py │ │ │ ├── check_run_list.js │ │ │ └── test_check_run.py │ │ └── check_run_settings │ │ │ ├── __init__.py │ │ │ ├── check_run_settings.js │ │ │ ├── check_run_settings.json │ │ │ ├── check_run_settings.py │ │ │ └── test_check_run_settings.py │ ├── print_format │ │ ├── __init__.py │ │ ├── example_secondary_print_format │ │ │ ├── __init__.py │ │ │ └── example_secondary_print_format.json │ │ └── example_voucher │ │ │ ├── __init__.py │ │ │ └── example_voucher.json │ └── report │ │ ├── __init__.py │ │ ├── ach_prenote │ │ ├── __init__.py │ │ ├── ach_prenote.js │ │ ├── ach_prenote.json │ │ └── ach_prenote.py │ │ ├── payables_attachments │ │ ├── __init__.py │ │ ├── payables_attachments.js │ │ ├── payables_attachments.json │ │ └── payables_attachments.py │ │ └── positive_pay │ │ ├── __init__.py │ │ ├── positive_pay.js │ │ ├── positive_pay.json │ │ └── positive_pay.py ├── config │ ├── __init__.py │ ├── desktop.py │ └── docs.py ├── customize.py ├── hooks.py ├── modules.txt ├── overrides │ ├── __init__.py │ ├── bank.py │ └── payment_entry.py ├── patches.txt ├── patches │ ├── patch_payment_schedule.py │ └── patch_voided_check_workflow.py ├── public │ ├── css │ │ └── file_preview.css │ └── js │ │ ├── check_run.bundle.js │ │ ├── check_run │ │ ├── CheckRun.vue │ │ ├── ModeOfPaymentSummary.vue │ │ ├── check_run.js │ │ └── check_run_quick_entry.js │ │ ├── custom │ │ ├── employee_custom.js │ │ ├── file_preview.js │ │ ├── payment_entry_custom.js │ │ └── supplier_custom.js │ │ ├── style.css │ │ └── vite.config.js ├── tests │ ├── conftest.py │ ├── fixtures.py │ ├── setup.py │ ├── test_check_run.py │ └── test_payment_entry.py └── translations │ └── en-GB.csv ├── docs └── version-14 │ ├── en │ ├── achgeneration.md │ ├── assets │ │ ├── ACHFile.png │ │ ├── BankAccount.png │ │ ├── CheckRunDetailBoxAroundMoP.png │ │ ├── CheckRunScreen.png │ │ ├── ConfigEmployee.png │ │ ├── ConfigSupplier.png │ │ ├── InitiatingCheckRunDialogue.png │ │ ├── InvoicePaymentScheduleExample.png │ │ ├── ModeOfPayment.png │ │ ├── PaymentEntryPaymentTerm.png │ │ ├── PaymentScheduleTransactions.png │ │ ├── PositivePayReport.png │ │ ├── PostSubmissionOptions.png │ │ ├── PrintConfirmation.png │ │ ├── SettingsList.png │ │ ├── Settings_ACH.png │ │ ├── Settings_MOP.png │ │ ├── Settings_Main.png │ │ ├── SupplierDefaultMoPDetail.png │ │ ├── VoucherGroup.png │ │ └── return-debit_note.png │ ├── configuration.md │ ├── exampledata.md │ ├── exampleprint.md │ ├── index.md │ ├── installationguide.md │ ├── payment_entry.md │ ├── permissions.md │ ├── positivepay.md │ ├── renderpdfsequence.md │ ├── settings.md │ └── translations.md │ └── es │ ├── achgeneration.md │ ├── assets │ ├── ACHFile.png │ ├── BankAccount.png │ ├── CheckRunDetailBoxAroundMoP.png │ ├── CheckRunScreen.png │ ├── ConfigEmployee.png │ ├── ConfigSupplier.png │ ├── InitiatingCheckRunDialogue.png │ ├── InvoicePaymentScheduleExample.png │ ├── ModeOfPayment.png │ ├── PaymentEntryPaymentTerm.png │ ├── PaymentScheduleTransactions.png │ ├── PositivePayReport.png │ ├── PostSubmissionOptions.png │ ├── PrintConfirmation.png │ ├── SettingsList.png │ ├── Settings_ACH.png │ ├── Settings_MOP.png │ ├── Settings_Main.png │ ├── SupplierDefaultMoPDetail.png │ ├── VoucherGroup.png │ └── return-debit_note.png │ ├── configuration.md │ ├── exampledata.md │ ├── exampleprint.md │ ├── index.md │ ├── installationguide.md │ ├── payment_entry.md │ ├── permissions.md │ ├── positivepay.md │ ├── renderpdfsequence.md │ ├── settings.md │ └── translations.md ├── license.txt ├── mypy.ini ├── package.json ├── pyproject.toml ├── setup.py ├── translate_docs.py └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # Root editor config file 2 | root = true 3 | 4 | # Common settings 5 | [*] 6 | end_of_line = lf 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | charset = utf-8 10 | 11 | # python, js indentation settings 12 | [{*.py,*.js,*.vue,*.css,*.scss,*.html}] 13 | indent_style = tab 14 | indent_size = 2 15 | max_line_length = 99 16 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | ignore = 3 | B001, 4 | B007, 5 | B009, 6 | B010, 7 | B950, 8 | E101, 9 | E111, 10 | E114, 11 | E116, 12 | E117, 13 | E121, 14 | E122, 15 | E123, 16 | E124, 17 | E125, 18 | E126, 19 | E127, 20 | E128, 21 | E131, 22 | E201, 23 | E202, 24 | E203, 25 | E211, 26 | E221, 27 | E222, 28 | E223, 29 | E224, 30 | E225, 31 | E226, 32 | E228, 33 | E231, 34 | E241, 35 | E242, 36 | E251, 37 | E261, 38 | E262, 39 | E265, 40 | E266, 41 | E271, 42 | E272, 43 | E273, 44 | E274, 45 | E301, 46 | E302, 47 | E303, 48 | E305, 49 | E306, 50 | E402, 51 | E501, 52 | E502, 53 | E701, 54 | E702, 55 | E703, 56 | E741, 57 | W191, 58 | W291, 59 | W292, 60 | W293, 61 | W391, 62 | W503, 63 | W504, 64 | E711, 65 | E129, 66 | F841, 67 | E713, 68 | E712, 69 | 70 | max-line-length = 200 71 | exclude=,test_*.py 72 | -------------------------------------------------------------------------------- /.github/helper/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export PIP_ROOT_USER_ACTION=ignore 4 | 5 | set -e 6 | 7 | # Check for merge conflicts before proceeding 8 | python -m compileall -f "${GITHUB_WORKSPACE}" 9 | if grep -lr --exclude-dir=node_modules "^<<<<<<< " "${GITHUB_WORKSPACE}" 10 | then echo "Found merge conflicts" 11 | exit 1 12 | fi 13 | 14 | cd ~ || exit 15 | 16 | pip install --upgrade pip 17 | pip install frappe-bench 18 | 19 | mysql --host 127.0.0.1 --port 3306 -u root -e "SET GLOBAL character_set_server = 'utf8mb4'" 20 | mysql --host 127.0.0.1 --port 3306 -u root -e "SET GLOBAL collation_server = 'utf8mb4_unicode_ci'" 21 | 22 | mysql --host 127.0.0.1 --port 3306 -u root -e "CREATE OR REPLACE DATABASE test_site" 23 | mysql --host 127.0.0.1 --port 3306 -u root -e "CREATE OR REPLACE USER 'test_site'@'localhost' IDENTIFIED BY 'test_site'" 24 | mysql --host 127.0.0.1 --port 3306 -u root -e "GRANT ALL PRIVILEGES ON \`test_site\`.* TO 'test_site'@'localhost'" 25 | 26 | mysql --host 127.0.0.1 --port 3306 -u root -e "ALTER USER 'root'@'localhost' IDENTIFIED BY 'root'" # match site_cofig 27 | mysql --host 127.0.0.1 --port 3306 -u root -e "FLUSH PRIVILEGES" 28 | 29 | echo BRANCH_NAME: "${BRANCH_NAME}" 30 | git clone https://github.com/frappe/frappe --branch ${BRANCH_NAME} 31 | bench init frappe-bench --frappe-path ~/frappe --python "$(which python)" --skip-assets --ignore-exist 32 | 33 | mkdir ~/frappe-bench/sites/test_site 34 | cp -r "${GITHUB_WORKSPACE}/.github/helper/site_config.json" ~/frappe-bench/sites/test_site/ 35 | 36 | cd ~/frappe-bench || exit 37 | 38 | sed -i 's/watch:/# watch:/g' Procfile 39 | sed -i 's/schedule:/# schedule:/g' Procfile 40 | sed -i 's/socketio:/# socketio:/g' Procfile 41 | sed -i 's/redis_socketio:/# redis_socketio:/g' Procfile 42 | 43 | bench get-app payments https://github.com/frappe/payments --branch ${BRANCH_NAME} --skip-assets 44 | bench get-app erpnext https://github.com/frappe/erpnext --branch ${BRANCH_NAME} --resolve-deps --skip-assets 45 | bench get-app hrms https://github.com/frappe/hrms --branch ${BRANCH_NAME} --skip-assets 46 | bench get-app check_run "${GITHUB_WORKSPACE}" --skip-assets 47 | 48 | printf '%s\n' 'frappe' 'erpnext' 'payments' 'hrms' 'check_run' > ~/frappe-bench/sites/apps.txt 49 | bench setup requirements --python 50 | bench use test_site 51 | 52 | bench start &> bench_run_logs.txt & 53 | CI=Yes & 54 | bench --site test_site reinstall --yes --admin-password admin 55 | 56 | bench setup requirements --dev 57 | 58 | echo "BENCH VERSION NUMBERS:" 59 | bench version 60 | echo "SITE LIST-APPS:" 61 | bench list-apps 62 | 63 | bench start &> bench_run_logs.txt & 64 | CI=Yes & 65 | bench execute 'check_run.tests.setup.before_test' 66 | -------------------------------------------------------------------------------- /.github/helper/install_dependencies.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Check for merge conflicts before proceeding 4 | python -m compileall -f "${{GITHUB_WORKSPACE}}" 5 | if grep -lr --exclude-dir=node_modules "^<<<<<<< " "${{GITHUB_WORKSPACE}}" 6 | then echo "Found merge conflicts" 7 | exit 1 8 | fi 9 | 10 | sudo apt update -y && sudo apt install redis-server libcups2-dev mariadb-client 11 | 12 | # install wkhtmltopdf 13 | wget -O /tmp/wkhtmltox.tar.xz https://github.com/frappe/wkhtmltopdf/raw/master/wkhtmltox-0.12.3_linux-generic-amd64.tar.xz 14 | tar -xf /tmp/wkhtmltox.tar.xz -C /tmp 15 | sudo mv /tmp/wkhtmltox/bin/wkhtmltopdf /usr/local/bin/wkhtmltopdf 16 | sudo chmod o+x /usr/local/bin/wkhtmltopdf 17 | -------------------------------------------------------------------------------- /.github/helper/site_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "allow_tests": true, 3 | "db_host": "127.0.0.1", 4 | "db_port": 3306, 5 | "db_name": "test_site", 6 | "db_password": "admin", 7 | "auto_email_id": "test@example.com", 8 | "mail_server": "smtp.example.com", 9 | "mail_login": "test@example.com", 10 | "mail_password": "test", 11 | "admin_password": "admin", 12 | "root_login": "root", 13 | "root_password": "admin", 14 | "host_name": "http://test_site:8000", 15 | "install_apps": [ 16 | "erpnext", 17 | "hrms", 18 | "check_run" 19 | ], 20 | "throttle_user_limit": 100, 21 | "developer_mode": 1 22 | } -------------------------------------------------------------------------------- /.github/workflows/backport.yml: -------------------------------------------------------------------------------- 1 | name: Backport 2 | on: 3 | pull_request_target: 4 | types: 5 | - closed 6 | - labeled 7 | 8 | jobs: 9 | backport: 10 | name: Backport 11 | runs-on: ubuntu-latest 12 | # Only react to merged PRs for security reasons. 13 | # See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target. 14 | if: > 15 | github.event.pull_request.merged 16 | && ( 17 | github.event.action == 'closed' 18 | || ( 19 | github.event.action == 'labeled' 20 | && contains(github.event.label.name, 'backport') 21 | ) 22 | ) 23 | steps: 24 | - uses: tibdex/backport@v2 25 | with: 26 | github_token: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/lint.yaml: -------------------------------------------------------------------------------- 1 | name: Linters 2 | 3 | on: 4 | push: 5 | branches: 6 | - version-14 7 | - version-15 8 | pull_request: 9 | branches: 10 | - version-14 11 | - version-15 12 | 13 | env: 14 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 15 | 16 | jobs: 17 | mypy: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v4 22 | 23 | - name: Setup Python 24 | uses: actions/setup-python@v5 25 | with: 26 | python-version: '3.10' 27 | 28 | - name: Install mypy 29 | run: pip install mypy 30 | 31 | - name: Install mypy types 32 | run: mypy ./check_run/. --install-types --non-interactive 33 | 34 | - name: Run mypy 35 | uses: sasanquaneuf/mypy-github-action@releases/v1 36 | with: 37 | checkName: 'mypy' 38 | env: 39 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 40 | 41 | black: 42 | runs-on: ubuntu-latest 43 | steps: 44 | - name: Checkout 45 | uses: actions/checkout@v4 46 | 47 | - name: Setup Python 48 | uses: actions/setup-python@v5 49 | with: 50 | python-version: '3.10' 51 | 52 | - name: Install Black (Frappe) 53 | run: pip install git+https://github.com/frappe/black.git 54 | 55 | - name: Run Black (Frappe) 56 | run: black --check . 57 | 58 | prettier: 59 | runs-on: ubuntu-latest 60 | steps: 61 | - name: Checkout 62 | uses: actions/checkout@v4 63 | 64 | - name: Prettify code 65 | uses: rutajdash/prettier-cli-action@v1.0.0 66 | with: 67 | config_path: ./.prettierrc.js 68 | ignore_path: ./.prettierignore 69 | 70 | - name: Prettier Output 71 | if: ${{ failure() }} 72 | shell: bash 73 | run: | 74 | echo "The following files are not formatted:" 75 | echo "${{steps.prettier-run.outputs.prettier_output}}" >> $GITHUB_OUTPUT 76 | -------------------------------------------------------------------------------- /.github/workflows/overrides.yaml: -------------------------------------------------------------------------------- 1 | name: Track Overrides 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - version-14 7 | - version-15 8 | 9 | jobs: 10 | track_overrides: 11 | runs-on: ubuntu-latest 12 | name: Track Overrides 13 | steps: 14 | - name: Checkout repository 15 | uses: actions/checkout@v4 16 | 17 | - name: Track Overrides 18 | uses: diamorafaela/track-overrides@main 19 | with: 20 | github-token: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/print_format_diff.yml: -------------------------------------------------------------------------------- 1 | name: Print Format Diff 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - version-14 7 | - version-15 8 | 9 | jobs: 10 | diff_print_format: 11 | runs-on: ubuntu-latest 12 | name: Print Format Diff 13 | steps: 14 | - name: Checkout the code 15 | uses: actions/checkout@v3 16 | with: 17 | fetch-depth: 0 18 | 19 | - name: Print Format Diff 20 | uses: diamorafaela/diff-print-format@main 21 | with: 22 | github-token: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/pytest.yaml: -------------------------------------------------------------------------------- 1 | name: Pytest CI 2 | 3 | on: 4 | push: 5 | branches: [ version-14 ] 6 | pull_request: 7 | branches: [ version-14 ] 8 | env: 9 | BRANCH_NAME: ${{ github.base_ref || github.ref_name }} 10 | 11 | jobs: 12 | tests: 13 | runs-on: ${{ matrix.os }} 14 | strategy: 15 | matrix: 16 | os: [ubuntu-latest] 17 | fail-fast: false 18 | name: Server 19 | 20 | services: 21 | mysql: 22 | image: mariadb:10.6 23 | env: 24 | MYSQL_ALLOW_EMPTY_PASSWORD: YES 25 | MYSQL_ROOT_PASSWORD: 'admin' 26 | ports: 27 | - 3306:3306 28 | options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3 29 | 30 | steps: 31 | - name: Clone 32 | uses: actions/checkout@v3 33 | 34 | - name: Setup Python 35 | uses: actions/setup-python@v4 36 | with: 37 | python-version: '3.10' 38 | 39 | - name: Setup Node 40 | uses: actions/setup-node@v3 41 | with: 42 | node-version: 18 43 | check-latest: true 44 | cache: 'yarn' # Replaces `Get yarn cache directory path` and `yarn-cache` steps 45 | 46 | - name: Add to Hosts 47 | run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts 48 | 49 | - name: Cache pip 50 | uses: actions/cache@v4 51 | with: 52 | path: ~/.cache/pip 53 | key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml', '**/setup.py', '**/setup.cfg') }} 54 | restore-keys: | 55 | ${{ runner.os }}-pip- 56 | ${{ runner.os }}- 57 | 58 | - name: Cache node modules 59 | uses: actions/cache@v4 60 | env: 61 | cache-name: cache-node-modules 62 | with: 63 | path: ~/.npm 64 | key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} 65 | restore-keys: | 66 | ${{ runner.os }}-build-${{ env.cache-name }}- 67 | ${{ runner.os }}-build- 68 | ${{ runner.os }}- 69 | 70 | - name: Install JS Dependencies 71 | run: yarn --prefer-offline 72 | 73 | - name: Install App Dependencies 74 | run: bash ${{github.workspace}}/.github/helper/install_dependencies.sh 75 | 76 | - name: Install Bench Site and Apps 77 | env: 78 | MYSQL_HOST: 'localhost' 79 | MYSQL_PWD: 'admin' 80 | BRANCH_NAME: ${{ env.BRANCH_NAME}} 81 | run: | 82 | bash ${{ github.workspace }}/.github/helper/install.sh 83 | 84 | - name: Run Tests 85 | working-directory: /home/runner/frappe-bench 86 | run: | 87 | source env/bin/activate 88 | pytest ./apps/check_run/check_run/tests/ --disable-warnings -s 89 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | push: 4 | branches: 5 | - version-13 6 | - version-14 7 | jobs: 8 | release: 9 | name: Release 10 | runs-on: ubuntu-latest 11 | concurrency: release 12 | permissions: 13 | id-token: write 14 | contents: write 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v3 18 | with: 19 | fetch-depth: 0 20 | - name: Python Semantic Release 21 | uses: python-semantic-release/python-semantic-release@master 22 | with: 23 | github_token: ${{ secrets.GITHUB_TOKEN }} 24 | git_committer_name: AgriTheory 25 | git_committer_email: support@agritheory.dev -------------------------------------------------------------------------------- /.github/workflows/translate_docs.yml: -------------------------------------------------------------------------------- 1 | name: Translate Markdown Files 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - 'docs/*/en/*.md' 7 | 8 | jobs: 9 | translate: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout repository 13 | uses: actions/checkout@v2 14 | 15 | - name: Set up Python 16 | uses: actions/setup-python@v2 17 | with: 18 | python-version: '3.x' 19 | 20 | - name: Set up credentials 21 | run: echo "$GOOGLE_APPLICATION_CREDENTIALS" > credentials.json 22 | env: 23 | GOOGLE_APPLICATION_CREDENTIALS: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }} 24 | 25 | - name: Install dependencies 26 | run: | 27 | pip install --upgrade pip 28 | pip install google-cloud-translate 29 | pip install gitpython 30 | pip install PyGithub 31 | 32 | - name: Translate Markdown files 33 | env: 34 | GOOGLE_APPLICATION_CREDENTIALS: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }} 35 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 36 | GITHUB_REPOSITORY: ${{ secrets.GITHUB_REPOSITORY }} 37 | GITHUB_BASE_REF: ${{ secrets.GITHUB_SHA }} 38 | GITHUB_REF: ${{ secrets.GITHUB_REF }} 39 | run: | 40 | python translate_docs.py 41 | 42 | - name: Remove credentials file 43 | run: rm credentials.json -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.py~ 3 | *.comp.js 4 | *.DS_Store 5 | locale 6 | .wnf-lang-status 7 | *.swp 8 | *.egg-info 9 | dist/ 10 | # build/ 11 | frappe/docs/current 12 | .vscode 13 | node_modules 14 | .kdev4/ 15 | *.kdev4 16 | *debug.log 17 | 18 | # Not Recommended, but will remove once webpack ready 19 | package-lock.json 20 | 21 | # Byte-compiled / optimized / DLL files 22 | __pycache__/ 23 | *.py[cod] 24 | *$py.class 25 | 26 | # C extensions 27 | *.so 28 | 29 | # Distribution / packaging 30 | .Python 31 | # build/ 32 | develop-eggs/ 33 | dist/ 34 | downloads/ 35 | eggs/ 36 | .eggs/ 37 | lib/ 38 | lib64/ 39 | parts/ 40 | sdist/ 41 | var/ 42 | wheels/ 43 | *.egg-info/ 44 | .installed.cfg 45 | *.egg 46 | MANIFEST 47 | 48 | # PyInstaller 49 | # Usually these files are written by a python script from a template 50 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 51 | *.manifest 52 | *.spec 53 | 54 | # Installer logs 55 | pip-log.txt 56 | pip-delete-this-directory.txt 57 | 58 | # Unit test / coverage reports 59 | htmlcov/ 60 | .tox/ 61 | .coverage 62 | .coverage.* 63 | .cache 64 | nosetests.xml 65 | coverage.xml 66 | *.cover 67 | .hypothesis/ 68 | .pytest_cache/ 69 | 70 | # Translations 71 | *.mo 72 | *.pot 73 | 74 | # Django stuff: 75 | *.log 76 | .static_storage/ 77 | .media/ 78 | local_settings.py 79 | 80 | # Flask stuff: 81 | instance/ 82 | .webassets-cache 83 | 84 | # Scrapy stuff: 85 | .scrapy 86 | 87 | # Sphinx documentation 88 | docs/_build/ 89 | 90 | # PyBuilder 91 | target/ 92 | 93 | # Jupyter Notebook 94 | .ipynb_checkpoints 95 | 96 | # pyenv 97 | .python-version 98 | 99 | # celery beat schedule file 100 | celerybeat-schedule 101 | 102 | # SageMath parsed files 103 | *.sage.py 104 | 105 | # Environments 106 | .env 107 | .venv 108 | env/ 109 | venv/ 110 | ENV/ 111 | env.bak/ 112 | venv.bak/ 113 | 114 | # Spyder project settings 115 | .spyderproject 116 | .spyproject 117 | 118 | # Rope project settings 119 | .ropeproject 120 | 121 | # mkdocs documentation 122 | /site 123 | 124 | # mypy 125 | .mypy_cache/ 126 | 127 | # Logs 128 | logs 129 | *.log 130 | npm-debug.log* 131 | yarn-debug.log* 132 | yarn-error.log* 133 | 134 | # Runtime data 135 | pids 136 | *.pid 137 | *.seed 138 | *.pid.lock 139 | 140 | # Directory for instrumented libs generated by jscoverage/JSCover 141 | lib-cov 142 | 143 | # Coverage directory used by tools like istanbul 144 | coverage 145 | 146 | # nyc test coverage 147 | .nyc_output 148 | 149 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 150 | .grunt 151 | 152 | # Bower dependency directory (https://bower.io/) 153 | bower_components 154 | 155 | # node-waf configuration 156 | .lock-wscript 157 | 158 | # Compiled binary addons (https://nodejs.org/api/addons.html) 159 | build/Release 160 | 161 | # Dependency directories 162 | node_modules/ 163 | jspm_packages/ 164 | 165 | # Typescript v1 declaration files 166 | typings/ 167 | 168 | # Optional npm cache directory 169 | .npm 170 | 171 | # Optional eslint cache 172 | .eslintcache 173 | 174 | # Optional REPL history 175 | .node_repl_history 176 | 177 | # Output of 'npm pack' 178 | *.tgz 179 | 180 | # Yarn Integrity file 181 | .yarn-integrity 182 | 183 | # dotenv environment variables file 184 | .env 185 | 186 | # next.js build output 187 | .next 188 | 189 | # cypress 190 | cypress/screenshots 191 | cypress/videos 192 | 193 | # JetBrains IDEs 194 | .idea/ 195 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | exclude: 'node_modules|.git' 2 | default_stages: [pre-commit] 3 | fail_fast: false 4 | 5 | repos: 6 | - repo: https://github.com/pre-commit/pre-commit-hooks 7 | rev: v4.6.0 8 | hooks: 9 | - id: trailing-whitespace 10 | files: 'check_run.*' 11 | exclude: '.*json$|.*txt$|.*csv|.*md|.*svg' 12 | - id: check-yaml 13 | - id: no-commit-to-branch 14 | args: ['--branch', 'version-14'] 15 | - id: check-merge-conflict 16 | - id: check-ast 17 | - id: check-json 18 | - id: check-toml 19 | - id: debug-statements 20 | 21 | - repo: https://github.com/pre-commit/mirrors-mypy 22 | rev: v1.5.1 23 | hooks: 24 | - id: mypy 25 | exclude: ^tests/ 26 | args: ['--install-types', '--non-interactive', '--ignore-missing-imports'] 27 | 28 | - repo: https://github.com/codespell-project/codespell 29 | rev: v2.3.0 30 | hooks: 31 | - id: codespell 32 | args: ['--ignore-words-list=notin'] 33 | additional_dependencies: 34 | - tomli 35 | 36 | - repo: https://github.com/asottile/pyupgrade 37 | rev: v3.19.1 38 | hooks: 39 | - id: pyupgrade 40 | args: ['--py310-plus'] 41 | 42 | - repo: https://github.com/agritheory/black 43 | rev: 951ccf4d5bb0d692b457a5ebc4215d755618eb68 44 | hooks: 45 | - id: black 46 | args: ['--line-length', '99'] 47 | 48 | - repo: https://github.com/PyCQA/autoflake 49 | rev: v2.3.1 50 | hooks: 51 | - id: autoflake 52 | args: [--remove-all-unused-imports, --in-place] 53 | 54 | - repo: https://github.com/PyCQA/flake8 55 | rev: 7.1.1 56 | hooks: 57 | - id: flake8 58 | additional_dependencies: ['flake8-bugbear'] 59 | 60 | - repo: https://github.com/agritheory/test_utils/ 61 | rev: v0.17.0 62 | hooks: 63 | - id: update_pre_commit_config 64 | - id: clean_customized_doctypes 65 | args: ['--app', 'check_run'] 66 | - id: validate_customizations 67 | - id: validate_copyright 68 | files: '\.(js|ts|py|md)$' 69 | args: ['--app', 'check_run'] 70 | # - id: sql_docstring_formatter 71 | 72 | - repo: local 73 | hooks: 74 | - id: prettier 75 | name: prettier 76 | entry: npx prettier . --write --ignore-path .prettierignore 77 | language: node 78 | 79 | ci: 80 | autoupdate_schedule: weekly 81 | skip: [] 82 | submodules: false 83 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------------------------------------------- 2 | # Keep this section in sync with .gitignore 3 | #------------------------------------------------------------------------------------------------------------------- 4 | 5 | *.pyc 6 | *.py~ 7 | *.comp.js 8 | *.DS_Store 9 | locale 10 | .wnf-lang-status 11 | *.swp 12 | *.egg-info 13 | dist/ 14 | # build/ 15 | cloud_storage/docs/current 16 | cloud_storage/public/dist 17 | .vscode 18 | .vs 19 | node_modules 20 | .kdev4/ 21 | *.kdev4 22 | *debug.log 23 | 24 | # Not Recommended, but will remove once webpack ready 25 | package-lock.json 26 | 27 | # Byte-compiled / optimized / DLL files 28 | __pycache__/ 29 | *.py[cod] 30 | *$py.class 31 | 32 | # C extensions 33 | *.so 34 | 35 | # Distribution / packaging 36 | .Python 37 | # build/ 38 | develop-eggs/ 39 | dist/ 40 | downloads/ 41 | eggs/ 42 | .eggs/ 43 | lib64/ 44 | parts/ 45 | sdist/ 46 | var/ 47 | wheels/ 48 | *.egg-info/ 49 | .installed.cfg 50 | *.egg 51 | MANIFEST 52 | 53 | # PyInstaller 54 | # Usually these files are written by a python script from a template 55 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 56 | *.manifest 57 | *.spec 58 | 59 | # Installer logs 60 | pip-log.txt 61 | pip-delete-this-directory.txt 62 | 63 | # Unit test / coverage reports 64 | htmlcov/ 65 | .tox/ 66 | .coverage 67 | .coverage.* 68 | .cache 69 | nosetests.xml 70 | coverage.xml 71 | *.cover 72 | .hypothesis/ 73 | .pytest_cache/ 74 | .cypress-coverage 75 | 76 | # Translations 77 | *.mo 78 | *.pot 79 | 80 | # Django stuff: 81 | *.log 82 | .static_storage/ 83 | .media/ 84 | local_settings.py 85 | 86 | # Flask stuff: 87 | instance/ 88 | .webassets-cache 89 | 90 | # Scrapy stuff: 91 | .scrapy 92 | 93 | # Sphinx documentation 94 | docs/_build/ 95 | 96 | # PyBuilder 97 | target/ 98 | 99 | # Jupyter Notebook 100 | .ipynb_checkpoints 101 | 102 | # pyenv 103 | .python-version 104 | 105 | # celery beat schedule file 106 | celerybeat-schedule 107 | 108 | # SageMath parsed files 109 | *.sage.py 110 | 111 | # Environments 112 | .env 113 | .venv 114 | env/ 115 | venv/ 116 | ENV/ 117 | env.bak/ 118 | venv.bak/ 119 | 120 | # Spyder project settings 121 | .spyderproject 122 | .spyproject 123 | 124 | # Rope project settings 125 | .ropeproject 126 | 127 | # mkdocs documentation 128 | /site 129 | 130 | # mypy 131 | .mypy_cache/ 132 | 133 | # Logs 134 | logs 135 | *.log 136 | npm-debug.log* 137 | yarn-debug.log* 138 | yarn-error.log* 139 | 140 | # Runtime data 141 | pids 142 | *.pid 143 | *.seed 144 | *.pid.lock 145 | 146 | # Directory for instrumented libs generated by jscoverage/JSCover 147 | lib-cov 148 | 149 | # Coverage directory used by tools like istanbul 150 | coverage 151 | 152 | # nyc test coverage 153 | .nyc_output 154 | 155 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 156 | .grunt 157 | 158 | # Bower dependency directory (https://bower.io/) 159 | bower_components 160 | 161 | # node-waf configuration 162 | .lock-wscript 163 | 164 | # Compiled binary addons (https://nodejs.org/api/addons.html) 165 | build/Release 166 | 167 | # Dependency directories 168 | node_modules/ 169 | jspm_packages/ 170 | 171 | # Typescript v1 declaration files 172 | typings/ 173 | 174 | # Optional npm cache directory 175 | .npm 176 | 177 | # Optional eslint cache 178 | .eslintcache 179 | 180 | # Optional REPL history 181 | .node_repl_history 182 | 183 | # Output of 'npm pack' 184 | *.tgz 185 | 186 | # Yarn Integrity file 187 | .yarn-integrity 188 | 189 | # dotenv environment variables file 190 | .env 191 | 192 | # next.js build output 193 | .next 194 | 195 | # cypress 196 | cypress/screenshots 197 | cypress/videos 198 | 199 | # JetBrains IDEs 200 | .idea/ 201 | 202 | #------------------------------------------------------------------------------------------------------------------- 203 | # Prettier-specific overrides 204 | #------------------------------------------------------------------------------------------------------------------- 205 | 206 | 207 | # Package manager files 208 | pnpm-lock.yaml 209 | yarn.lock 210 | package-lock.json 211 | shrinkwrap.json 212 | 213 | # Build outputs 214 | lib 215 | .github 216 | 217 | # Prettier reformats code blocks inside Markdown, which affects rendered output 218 | *.md 219 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | arrowParens: 'avoid', 3 | bracketSameLine: true, 4 | bracketSpacing: true, 5 | embeddedLanguageFormatting: 'auto', 6 | htmlWhitespaceSensitivity: 'css', 7 | insertPragma: false, 8 | jsxSingleQuote: false, 9 | printWidth: 120, 10 | proseWrap: 'preserve', 11 | quoteProps: 'as-needed', 12 | requirePragma: false, 13 | semi: false, 14 | singleQuote: true, 15 | tabWidth: 2, 16 | trailingComma: 'es5', 17 | useTabs: true, 18 | vueIndentScriptAndStyle: false, 19 | } 20 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include MANIFEST.in 2 | include requirements.txt 3 | include *.json 4 | include *.md 5 | include *.py 6 | include *.txt 7 | recursive-include check_run *.css 8 | recursive-include check_run *.csv 9 | recursive-include check_run *.html 10 | recursive-include check_run *.ico 11 | recursive-include check_run *.js 12 | recursive-include check_run *.json 13 | recursive-include check_run *.md 14 | recursive-include check_run *.png 15 | recursive-include check_run *.py 16 | recursive-include check_run *.svg 17 | recursive-include check_run *.txt 18 | recursive-exclude check_run *.pyc -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Check Run 2 | 3 | ### License 4 | 5 | MIT 6 | 7 | ### Production Installation Instructions 8 | 9 | See the [installation guide](./docs/installationguide.md) for detailed instructions for either a production or development environment. 10 | 11 | #### Developer Setup 12 | 13 | First, set up a new bench and substitute a path to the python version to use. Python should be 3.10 latest for V14. These instructions use [pyenv](https://github.com/pyenv/pyenv) for managing environments. 14 | ```shell 15 | # Version 14 16 | bench init --frappe-branch version-14 {{ bench name }} --python ~/.pyenv/versions/3.10.3/bin/python3 17 | ``` 18 | 19 | Create a new site in that bench 20 | ```shell 21 | cd {{ bench name }} 22 | bench new-site {{ site name }} --force --db-name {{ site name }} 23 | ``` 24 | 25 | Download the ERPNext app 26 | ```shell 27 | # Version 14 28 | bench get-app erpnext --branch version-14 29 | bench get-app hrms --branch version-14 30 | ``` 31 | 32 | Download the Time and Expense application 33 | ```shell 34 | bench get-app check_run https://github.com/agritheory/check_run 35 | ``` 36 | 37 | Install the apps to your site 38 | ```shell 39 | bench --site {{ site name }} install-app erpnext hrms check_run 40 | 41 | # Optional: Check that all apps installed on your site 42 | bench --site {{ site name }} list-apps 43 | ``` 44 | 45 | Set developer mode in `site_config.json` 46 | ```shell 47 | nano sites/{{ site name }}/site_config.json 48 | # Add this line: 49 | "developer_mode": 1, 50 | 51 | ``` 52 | Install pre-commit: 53 | ``` 54 | # ~/frappe-bench/apps/check_run/ 55 | pre-commit install 56 | ``` 57 | 58 | Add the site to your computer's hosts file to be able to access it via: `http://{{ site name }}:[8000]`. You'll need to enter your root password to allow your command line application to make edits to this file. 59 | ```shell 60 | bench --site {{site name}} add-to-hosts 61 | ``` 62 | 63 | Launch your bench (note you should be using Node.js v14 for a Version 13 bench and Node.js v16 for a Version 14 bench) 64 | ```shell 65 | bench start 66 | ``` 67 | 68 | Optional: install a [demo Company and its data](./exampledata.md) to test the Electronic Payments module's functionality 69 | ```shell 70 | bench execute 'check_run.tests.setup.before_test' 71 | ``` 72 | 73 | To run `mypy` locally: 74 | ```shell 75 | source env/bin/activate 76 | mypy ./apps/check_run/check_run --ignore-missing-imports 77 | ``` -------------------------------------------------------------------------------- /check_run/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025, AgriTheory and contributors 2 | # For license information, please see license.txt 3 | 4 | __version__ = "14.11.7" 5 | -------------------------------------------------------------------------------- /check_run/check_run/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023, AgriTheory and contributors 2 | # For license information, please see license.txt 3 | 4 | import json 5 | 6 | import frappe 7 | 8 | from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import PurchaseInvoice 9 | from erpnext.accounts.doctype.journal_entry.journal_entry import JournalEntry 10 | from hrms.hr.doctype.expense_claim.expense_claim import ExpenseClaim 11 | 12 | 13 | @frappe.whitelist() 14 | @frappe.read_only() 15 | def show_bank_account_number(doctype: str, docname: str) -> dict: 16 | doc = frappe.get_doc(doctype, docname) 17 | routing_number = frappe.get_value("Bank", doc.bank, "aba_number") or "" 18 | account_number = doc.get_password("bank_account", raise_exception=False) or "" 19 | return {"routing_number": routing_number, "account_number": account_number} 20 | 21 | 22 | @frappe.whitelist() 23 | def disallow_cancellation_if_in_check_run( 24 | doc: PurchaseInvoice | JournalEntry | ExpenseClaim, method: str | None = None 25 | ) -> None: 26 | draft_check_runs = frappe.get_all("Check Run", ["name", "transactions"], {"docstatus": 0}) 27 | for draft_check_run in draft_check_runs: 28 | if not draft_check_run.transactions: 29 | continue 30 | transactions = [ 31 | t.get("ref_number") or t.get("name") 32 | for t in json.loads(draft_check_run.transactions) 33 | if t.get("pay") 34 | ] 35 | if doc.name in transactions: 36 | frappe.throw( 37 | frappe._( 38 | f"""This document is currently selected for payment in draft {frappe.get_desk_link('Check Run', draft_check_run.name)} and cannot be cancelled.""" 39 | ) 40 | ) 41 | -------------------------------------------------------------------------------- /check_run/check_run/custom/bank.json: -------------------------------------------------------------------------------- 1 | { 2 | "custom_fields": [ 3 | { 4 | "allow_in_quick_entry": 0, 5 | "allow_on_submit": 0, 6 | "bold": 0, 7 | "collapsible": 0, 8 | "columns": 0, 9 | "creation": "2022-08-07 15:07:53.297493", 10 | "default": null, 11 | "docstatus": 0, 12 | "dt": "Bank", 13 | "fetch_if_empty": 0, 14 | "fieldname": "aba_number", 15 | "fieldtype": "Data", 16 | "hidden": 0, 17 | "hide_border": 0, 18 | "hide_days": 0, 19 | "hide_seconds": 0, 20 | "idx": 4, 21 | "ignore_user_permissions": 0, 22 | "ignore_xss_filter": 0, 23 | "in_global_search": 0, 24 | "in_list_view": 0, 25 | "in_preview": 0, 26 | "in_standard_filter": 0, 27 | "insert_after": "column_break_1", 28 | "label": "ABA Number", 29 | "length": 0, 30 | "modified": "2022-08-07 15:07:53.297493", 31 | "modified_by": "Administrator", 32 | "module": "Check Run", 33 | "name": "Bank-aba_number", 34 | "no_copy": 0, 35 | "non_negative": 0, 36 | "owner": "Administrator", 37 | "permlevel": 0, 38 | "precision": "", 39 | "print_hide": 0, 40 | "print_hide_if_no_value": 0, 41 | "read_only": 0, 42 | "report_hide": 0, 43 | "reqd": 0, 44 | "search_index": 0, 45 | "translatable": 0, 46 | "unique": 0 47 | } 48 | ], 49 | "custom_perms": [], 50 | "doctype": "Bank", 51 | "property_setters": [], 52 | "sync_on_migrate": 1 53 | } 54 | -------------------------------------------------------------------------------- /check_run/check_run/custom/bank_account.json: -------------------------------------------------------------------------------- 1 | { 2 | "custom_fields": [ 3 | { 4 | "allow_in_quick_entry": 0, 5 | "allow_on_submit": 0, 6 | "bold": 0, 7 | "collapsible": 0, 8 | "columns": 0, 9 | "creation": "2022-06-30 11:20:02.666689", 10 | "default": null, 11 | "docstatus": 0, 12 | "dt": "Bank Account", 13 | "fetch_if_empty": 0, 14 | "fieldname": "check_number", 15 | "fieldtype": "Data", 16 | "hidden": 0, 17 | "hide_border": 0, 18 | "hide_days": 0, 19 | "hide_seconds": 0, 20 | "idx": 10, 21 | "ignore_user_permissions": 0, 22 | "ignore_xss_filter": 0, 23 | "in_global_search": 0, 24 | "in_list_view": 0, 25 | "in_preview": 0, 26 | "in_standard_filter": 0, 27 | "insert_after": "company", 28 | "label": "Last Used Check Number", 29 | "length": 0, 30 | "modified": "2022-06-30 11:20:02.666689", 31 | "modified_by": "Administrator", 32 | "module": "Check Run", 33 | "name": "Bank Account-check_number", 34 | "no_copy": 0, 35 | "non_negative": 0, 36 | "owner": "Administrator", 37 | "permlevel": 0, 38 | "precision": "", 39 | "print_hide": 0, 40 | "print_hide_if_no_value": 0, 41 | "read_only": 0, 42 | "report_hide": 0, 43 | "reqd": 0, 44 | "search_index": 0, 45 | "translatable": 1, 46 | "unique": 0 47 | }, 48 | { 49 | "allow_in_quick_entry": 0, 50 | "allow_on_submit": 0, 51 | "bold": 0, 52 | "collapsible": 0, 53 | "columns": 0, 54 | "creation": "2022-08-07 14:55:42.859002", 55 | "default": null, 56 | "docstatus": 0, 57 | "dt": "Bank Account", 58 | "fetch_if_empty": 0, 59 | "fieldname": "company_ach_id", 60 | "fieldtype": "Data", 61 | "hidden": 0, 62 | "hide_border": 0, 63 | "hide_days": 0, 64 | "hide_seconds": 0, 65 | "idx": 10, 66 | "ignore_user_permissions": 0, 67 | "ignore_xss_filter": 0, 68 | "in_global_search": 0, 69 | "in_list_view": 0, 70 | "in_preview": 0, 71 | "in_standard_filter": 0, 72 | "insert_after": "check_number", 73 | "label": "Company ACH ID", 74 | "length": 0, 75 | "modified": "2022-08-07 14:55:42.859002", 76 | "modified_by": "Administrator", 77 | "module": "Check Run", 78 | "name": "Bank Account-company_ach_id", 79 | "no_copy": 0, 80 | "non_negative": 0, 81 | "owner": "Administrator", 82 | "permlevel": 0, 83 | "precision": "", 84 | "print_hide": 0, 85 | "print_hide_if_no_value": 0, 86 | "read_only": 0, 87 | "report_hide": 0, 88 | "reqd": 0, 89 | "search_index": 0, 90 | "translatable": 1, 91 | "unique": 0 92 | }, 93 | { 94 | "allow_in_quick_entry": 0, 95 | "allow_on_submit": 0, 96 | "bold": 0, 97 | "collapsible": 0, 98 | "columns": 0, 99 | "creation": "2023-10-11 15:03:16.705933", 100 | "default": "1", 101 | "description": "Allow Quick Check Payment Entries to be made against this Bank Account. This fetches the Bank Account into the Payment Entry automatically.", 102 | "docstatus": 0, 103 | "dt": "Bank Account", 104 | "fetch_if_empty": 0, 105 | "fieldname": "allow_quick_check", 106 | "fieldtype": "Check", 107 | "hidden": 0, 108 | "hide_border": 0, 109 | "hide_days": 0, 110 | "hide_seconds": 0, 111 | "idx": 8, 112 | "ignore_user_permissions": 0, 113 | "ignore_xss_filter": 0, 114 | "in_global_search": 0, 115 | "in_list_view": 0, 116 | "in_preview": 0, 117 | "in_standard_filter": 0, 118 | "insert_after": "is_company_account", 119 | "is_system_generated": 0, 120 | "is_virtual": 0, 121 | "label": "Allow Quick Check", 122 | "length": 0, 123 | "modified": "2023-10-11 15:22:30.742925", 124 | "modified_by": "Administrator", 125 | "module": "Check Run", 126 | "name": "Bank Account-allow_quick_check", 127 | "no_copy": 0, 128 | "non_negative": 0, 129 | "owner": "Administrator", 130 | "permlevel": 0, 131 | "precision": "", 132 | "print_hide": 0, 133 | "print_hide_if_no_value": 0, 134 | "read_only": 0, 135 | "report_hide": 0, 136 | "reqd": 0, 137 | "search_index": 0, 138 | "sort_options": 0, 139 | "translatable": 0, 140 | "unique": 0 141 | } 142 | ], 143 | "custom_perms": [], 144 | "doctype": "Bank Account", 145 | "property_setters": [], 146 | "sync_on_migrate": 1 147 | } 148 | -------------------------------------------------------------------------------- /check_run/check_run/custom/employee.json: -------------------------------------------------------------------------------- 1 | { 2 | "custom_fields": [ 3 | { 4 | "allow_in_quick_entry": 0, 5 | "allow_on_submit": 0, 6 | "bold": 0, 7 | "collapsible": 0, 8 | "columns": 0, 9 | "creation": "2022-06-30 11:20:02.666689", 10 | "default": null, 11 | "description": "Show Bank Account Number", 12 | "docstatus": 0, 13 | "dt": "Employee", 14 | "fetch_if_empty": 0, 15 | "fieldname": "bank_account", 16 | "fieldtype": "Password", 17 | "hidden": 0, 18 | "hide_border": 0, 19 | "hide_days": 0, 20 | "hide_seconds": 0, 21 | "idx": 69, 22 | "ignore_user_permissions": 0, 23 | "ignore_xss_filter": 0, 24 | "in_global_search": 0, 25 | "in_list_view": 0, 26 | "in_preview": 0, 27 | "in_standard_filter": 0, 28 | "insert_after": "bank", 29 | "is_system_generated": 0, 30 | "is_virtual": 0, 31 | "label": "Bank Account", 32 | "length": 0, 33 | "modified": "2022-06-30 11:20:02.666689", 34 | "modified_by": "Administrator", 35 | "module": "Check Run", 36 | "name": "Employee-bank_account", 37 | "no_copy": 0, 38 | "non_negative": 0, 39 | "owner": "Administrator", 40 | "permlevel": 0, 41 | "precision": "", 42 | "print_hide": 0, 43 | "print_hide_if_no_value": 0, 44 | "read_only": 0, 45 | "report_hide": 0, 46 | "reqd": 0, 47 | "search_index": 0, 48 | "translatable": 0, 49 | "unique": 0 50 | }, 51 | { 52 | "allow_in_quick_entry": 0, 53 | "allow_on_submit": 0, 54 | "bold": 0, 55 | "collapsible": 0, 56 | "columns": 0, 57 | "creation": "2022-06-30 11:20:02.666689", 58 | "default": null, 59 | "docstatus": 0, 60 | "dt": "Employee", 61 | "fetch_if_empty": 0, 62 | "fieldname": "mode_of_payment", 63 | "fieldtype": "Link", 64 | "hidden": 0, 65 | "hide_border": 0, 66 | "hide_days": 0, 67 | "hide_seconds": 0, 68 | "idx": 53, 69 | "ignore_user_permissions": 0, 70 | "ignore_xss_filter": 0, 71 | "in_global_search": 0, 72 | "in_list_view": 0, 73 | "in_preview": 0, 74 | "in_standard_filter": 0, 75 | "insert_after": "salary_mode", 76 | "is_system_generated": 0, 77 | "is_virtual": 0, 78 | "label": "Mode of Payment", 79 | "length": 0, 80 | "modified": "2022-06-30 11:20:02.666689", 81 | "modified_by": "Administrator", 82 | "module": "Check Run", 83 | "name": "Employee-mode_of_payment", 84 | "no_copy": 0, 85 | "non_negative": 0, 86 | "options": "Mode of Payment", 87 | "owner": "Administrator", 88 | "permlevel": 0, 89 | "precision": "", 90 | "print_hide": 0, 91 | "print_hide_if_no_value": 0, 92 | "read_only": 0, 93 | "report_hide": 0, 94 | "reqd": 0, 95 | "search_index": 0, 96 | "translatable": 0, 97 | "unique": 0 98 | }, 99 | { 100 | "allow_in_quick_entry": 0, 101 | "allow_on_submit": 0, 102 | "bold": 0, 103 | "collapsible": 0, 104 | "columns": 0, 105 | "creation": "2022-06-30 11:20:02.666689", 106 | "default": null, 107 | "docstatus": 0, 108 | "dt": "Employee", 109 | "fetch_if_empty": 0, 110 | "fieldname": "bank", 111 | "fieldtype": "Link", 112 | "hidden": 0, 113 | "hide_border": 0, 114 | "hide_days": 0, 115 | "hide_seconds": 0, 116 | "idx": 68, 117 | "ignore_user_permissions": 0, 118 | "ignore_xss_filter": 0, 119 | "in_global_search": 0, 120 | "in_list_view": 0, 121 | "in_preview": 0, 122 | "in_standard_filter": 0, 123 | "insert_after": "mode_of_payment", 124 | "is_system_generated": 0, 125 | "is_virtual": 0, 126 | "label": "Bank", 127 | "length": 0, 128 | "modified": "2023-02-28 11:18:22.590359", 129 | "modified_by": "Administrator", 130 | "module": "Check Run", 131 | "name": "Employee-bank", 132 | "no_copy": 0, 133 | "non_negative": 0, 134 | "options": "Bank", 135 | "owner": "Administrator", 136 | "permlevel": 0, 137 | "precision": "", 138 | "print_hide": 0, 139 | "print_hide_if_no_value": 0, 140 | "read_only": 0, 141 | "report_hide": 0, 142 | "reqd": 0, 143 | "search_index": 0, 144 | "translatable": 0, 145 | "unique": 0 146 | }, 147 | { 148 | "name": "Employee-account_details_validated", 149 | "owner": "Administrator", 150 | "creation": "2025-05-12 14:26:20.659548", 151 | "modified": "2025-05-12 14:27:34.572960", 152 | "modified_by": "Administrator", 153 | "docstatus": 0, 154 | "idx": 64, 155 | "is_system_generated": 0, 156 | "dt": "Employee", 157 | "module": "Check Run", 158 | "label": "Account Details Validated", 159 | "fieldname": "account_details_validated", 160 | "insert_after": "bank_account", 161 | "length": 0, 162 | "fieldtype": "Date", 163 | "precision": "", 164 | "hide_seconds": 0, 165 | "hide_days": 0, 166 | "sort_options": 0, 167 | "fetch_if_empty": 0, 168 | "collapsible": 0, 169 | "non_negative": 0, 170 | "reqd": 0, 171 | "unique": 0, 172 | "is_virtual": 0, 173 | "read_only": 1, 174 | "ignore_user_permissions": 0, 175 | "hidden": 0, 176 | "print_hide": 0, 177 | "print_hide_if_no_value": 0, 178 | "no_copy": 0, 179 | "allow_on_submit": 0, 180 | "in_list_view": 0, 181 | "in_standard_filter": 0, 182 | "in_global_search": 0, 183 | "in_preview": 0, 184 | "bold": 0, 185 | "report_hide": 0, 186 | "search_index": 0, 187 | "allow_in_quick_entry": 0, 188 | "ignore_xss_filter": 0, 189 | "translatable": 0, 190 | "hide_border": 0, 191 | "permlevel": 0, 192 | "columns": 0 193 | }, 194 | { 195 | "name": "Employee-ach_last_used", 196 | "owner": "Administrator", 197 | "creation": "2025-05-12 14:26:20.798988", 198 | "modified": "2025-05-12 14:27:29.667182", 199 | "modified_by": "Administrator", 200 | "docstatus": 0, 201 | "is_system_generated": 0, 202 | "dt": "Employee", 203 | "module": "Check Run", 204 | "label": "ACH Last Used", 205 | "fieldname": "ach_last_used", 206 | "insert_after": "account_details_validated", 207 | "length": 0, 208 | "fieldtype": "Date", 209 | "precision": "", 210 | "hide_seconds": 0, 211 | "hide_days": 0, 212 | "sort_options": 0, 213 | "fetch_if_empty": 0, 214 | "collapsible": 0, 215 | "non_negative": 0, 216 | "reqd": 0, 217 | "unique": 0, 218 | "is_virtual": 0, 219 | "read_only": 1, 220 | "ignore_user_permissions": 0, 221 | "hidden": 0, 222 | "print_hide": 0, 223 | "print_hide_if_no_value": 0, 224 | "no_copy": 0, 225 | "allow_on_submit": 0, 226 | "in_list_view": 0, 227 | "in_standard_filter": 0, 228 | "in_global_search": 0, 229 | "in_preview": 0, 230 | "bold": 0, 231 | "report_hide": 0, 232 | "search_index": 0, 233 | "allow_in_quick_entry": 0, 234 | "ignore_xss_filter": 0, 235 | "translatable": 0, 236 | "hide_border": 0, 237 | "permlevel": 0, 238 | "columns": 0 239 | }, 240 | { 241 | "name": "Employee-ach_account_type", 242 | "owner": "Administrator", 243 | "creation": "2025-05-12 14:26:20.798988", 244 | "modified": "2025-05-12 14:27:29.667182", 245 | "modified_by": "Administrator", 246 | "docstatus": 0, 247 | "is_system_generated": 0, 248 | "dt": "Employee", 249 | "module": "Check Run", 250 | "label": "ACH Account Type", 251 | "fieldname": "ach_account_type", 252 | "insert_after": "ach_last_used", 253 | "length": 0, 254 | "fieldtype": "Select", 255 | "options": "\nChecking\nSavings", 256 | "precision": "", 257 | "hide_seconds": 0, 258 | "hide_days": 0, 259 | "sort_options": 0, 260 | "fetch_if_empty": 0, 261 | "collapsible": 0, 262 | "non_negative": 0, 263 | "reqd": 0, 264 | "unique": 0, 265 | "is_virtual": 0, 266 | "read_only": 0, 267 | "ignore_user_permissions": 0, 268 | "hidden": 0, 269 | "print_hide": 0, 270 | "print_hide_if_no_value": 0, 271 | "no_copy": 0, 272 | "allow_on_submit": 0, 273 | "in_list_view": 0, 274 | "in_standard_filter": 0, 275 | "in_global_search": 0, 276 | "in_preview": 0, 277 | "bold": 0, 278 | "report_hide": 0, 279 | "search_index": 0, 280 | "allow_in_quick_entry": 0, 281 | "ignore_xss_filter": 0, 282 | "translatable": 0, 283 | "hide_border": 0, 284 | "permlevel": 0, 285 | "columns": 0 286 | }, 287 | { 288 | "name": "Employee-ach_prenote_date", 289 | "owner": "Administrator", 290 | "creation": "2025-05-12 14:26:20.798988", 291 | "modified": "2025-05-12 14:27:29.667182", 292 | "modified_by": "Administrator", 293 | "docstatus": 0, 294 | "description": "This field is set by generating a NACHA file in the ACH Prenote report", 295 | "is_system_generated": 0, 296 | "dt": "Employee", 297 | "module": "Check Run", 298 | "label": "ACH Prenote Date", 299 | "fieldname": "ach_prenote_date", 300 | "insert_after": "ach_last_used", 301 | "length": 0, 302 | "fieldtype": "Date", 303 | "precision": "", 304 | "hide_seconds": 0, 305 | "hide_days": 0, 306 | "sort_options": 0, 307 | "fetch_if_empty": 0, 308 | "collapsible": 0, 309 | "non_negative": 0, 310 | "reqd": 0, 311 | "unique": 0, 312 | "is_virtual": 0, 313 | "read_only": 1, 314 | "ignore_user_permissions": 0, 315 | "hidden": 0, 316 | "print_hide": 0, 317 | "print_hide_if_no_value": 0, 318 | "no_copy": 0, 319 | "allow_on_submit": 0, 320 | "in_list_view": 0, 321 | "in_standard_filter": 0, 322 | "in_global_search": 0, 323 | "in_preview": 0, 324 | "bold": 0, 325 | "report_hide": 0, 326 | "search_index": 0, 327 | "allow_in_quick_entry": 0, 328 | "ignore_xss_filter": 0, 329 | "translatable": 0, 330 | "hide_border": 0, 331 | "permlevel": 0, 332 | "columns": 0 333 | } 334 | ], 335 | "custom_perms": [], 336 | "doctype": "Employee", 337 | "links": [], 338 | "property_setters": [], 339 | "sync_on_migrate": 1 340 | } 341 | -------------------------------------------------------------------------------- /check_run/check_run/custom/mode_of_payment.json: -------------------------------------------------------------------------------- 1 | { 2 | "custom_fields": [], 3 | "custom_perms": [], 4 | "doctype": "Mode of Payment", 5 | "property_setters": [ 6 | { 7 | "creation": "2022-08-05 13:10:55.278054", 8 | "doc_type": "Mode of Payment", 9 | "docstatus": 0, 10 | "doctype_or_field": "DocField", 11 | "field_name": "type", 12 | "idx": 0, 13 | "modified": "2022-08-05 13:10:55.278054", 14 | "modified_by": "Administrator", 15 | "module": "Check Run", 16 | "name": "Mode of Payment-type-options", 17 | "owner": "Administrator", 18 | "property": "options", 19 | "property_type": "Text", 20 | "value": "Cash\nBank\nGeneral\nPhone\nElectronic" 21 | } 22 | ], 23 | "sync_on_migrate": 1 24 | } 25 | -------------------------------------------------------------------------------- /check_run/check_run/custom/payment_entry.json: -------------------------------------------------------------------------------- 1 | { 2 | "custom_fields": [ 3 | { 4 | "allow_in_quick_entry": 0, 5 | "allow_on_submit": 0, 6 | "bold": 0, 7 | "collapsible": 0, 8 | "columns": 0, 9 | "creation": "2022-06-30 15:30:29.886371", 10 | "default": null, 11 | "docstatus": 0, 12 | "dt": "Payment Entry", 13 | "fetch_if_empty": 0, 14 | "fieldname": "check_run", 15 | "fieldtype": "Data", 16 | "hidden": 0, 17 | "hide_border": 0, 18 | "hide_days": 0, 19 | "hide_seconds": 0, 20 | "idx": 5, 21 | "ignore_user_permissions": 0, 22 | "ignore_xss_filter": 0, 23 | "in_global_search": 0, 24 | "in_list_view": 0, 25 | "in_preview": 0, 26 | "in_standard_filter": 0, 27 | "insert_after": "payment_order_status", 28 | "label": "Check Run", 29 | "length": 0, 30 | "modified": "2022-06-30 11:20:02.666689", 31 | "modified_by": "Administrator", 32 | "module": "Check Run", 33 | "name": "Payment Entry-check_run", 34 | "no_copy": 0, 35 | "non_negative": 0, 36 | "owner": "Administrator", 37 | "permlevel": 0, 38 | "precision": "", 39 | "print_hide": 0, 40 | "print_hide_if_no_value": 0, 41 | "read_only": 1, 42 | "report_hide": 0, 43 | "reqd": 0, 44 | "search_index": 0, 45 | "translatable": 0, 46 | "unique": 0 47 | } 48 | ], 49 | "custom_perms": [], 50 | "doctype": "Payment Entry", 51 | "property_setters": [], 52 | "sync_on_migrate": 1 53 | } 54 | -------------------------------------------------------------------------------- /check_run/check_run/custom/purchase_invoice.json: -------------------------------------------------------------------------------- 1 | { 2 | "custom_fields": [ 3 | { 4 | "allow_in_quick_entry": 0, 5 | "allow_on_submit": 0, 6 | "bold": 0, 7 | "collapsible": 0, 8 | "columns": 0, 9 | "creation": "2022-06-30 11:20:02.666689", 10 | "default": null, 11 | "docstatus": 0, 12 | "dt": "Purchase Invoice", 13 | "fetch_if_empty": 0, 14 | "fieldname": "supplier_default_mode_of_payment", 15 | "fieldtype": "Link", 16 | "hidden": 0, 17 | "hide_border": 0, 18 | "hide_days": 0, 19 | "hide_seconds": 0, 20 | "idx": 23, 21 | "ignore_user_permissions": 0, 22 | "ignore_xss_filter": 0, 23 | "in_global_search": 0, 24 | "in_list_view": 0, 25 | "in_preview": 0, 26 | "in_standard_filter": 0, 27 | "insert_after": "bill_no", 28 | "label": "Supplier Default Mode of Payment", 29 | "length": 0, 30 | "modified": "2022-06-30 11:20:02.666689", 31 | "modified_by": "Administrator", 32 | "module": "Check Run", 33 | "name": "Purchase Invoice-supplier_default_mode_of_payment", 34 | "no_copy": 0, 35 | "non_negative": 0, 36 | "options": "Mode of Payment", 37 | "owner": "Administrator", 38 | "permlevel": 0, 39 | "precision": "", 40 | "print_hide": 0, 41 | "print_hide_if_no_value": 0, 42 | "read_only": 0, 43 | "report_hide": 0, 44 | "reqd": 0, 45 | "search_index": 0, 46 | "translatable": 0, 47 | "unique": 0 48 | } 49 | ], 50 | "custom_perms": [], 51 | "doctype": "Purchase Invoice", 52 | "property_setters": [], 53 | "sync_on_migrate": 1 54 | } 55 | -------------------------------------------------------------------------------- /check_run/check_run/doctype/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agritheory/check_run/368e6ba124bb12cf52ba11deb4e8829f6d44e52b/check_run/check_run/doctype/__init__.py -------------------------------------------------------------------------------- /check_run/check_run/doctype/check_run/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agritheory/check_run/368e6ba124bb12cf52ba11deb4e8829f6d44e52b/check_run/check_run/doctype/check_run/__init__.py -------------------------------------------------------------------------------- /check_run/check_run/doctype/check_run/check_run.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "allow_copy": 1, 4 | "allow_events_in_timeline": 1, 5 | "autoname": "ACC-CR-.YYYY.-.#####", 6 | "creation": "2018-05-22 14:01:11.307993", 7 | "doctype": "DocType", 8 | "editable_grid": 1, 9 | "engine": "InnoDB", 10 | "field_order": [ 11 | "end_date", 12 | "posting_date", 13 | "beg_balance", 14 | "company_discretionary_data", 15 | "column_break_3", 16 | "initial_check_number", 17 | "final_check_number", 18 | "amount_check_run", 19 | "column_break_6", 20 | "company", 21 | "bank_account", 22 | "pay_to_account", 23 | "amended_from", 24 | "section_break_9", 25 | "check_run_table", 26 | "transactions", 27 | "print_count", 28 | "status" 29 | ], 30 | "fields": [ 31 | { 32 | "fieldname": "end_date", 33 | "fieldtype": "Date", 34 | "in_list_view": 1, 35 | "label": "Check Run End Date" 36 | }, 37 | { 38 | "fieldname": "column_break_3", 39 | "fieldtype": "Column Break" 40 | }, 41 | { 42 | "allow_on_submit": 1, 43 | "fieldname": "initial_check_number", 44 | "fieldtype": "Int", 45 | "in_list_view": 1, 46 | "label": "Initial Check Number" 47 | }, 48 | { 49 | "allow_on_submit": 1, 50 | "fieldname": "final_check_number", 51 | "fieldtype": "Int", 52 | "in_list_view": 1, 53 | "label": "Final Check Number", 54 | "read_only": 1 55 | }, 56 | { 57 | "default": "0.00", 58 | "fieldname": "amount_check_run", 59 | "fieldtype": "Currency", 60 | "in_list_view": 1, 61 | "label": "Amount in Check Run", 62 | "read_only": 1 63 | }, 64 | { 65 | "fieldname": "column_break_6", 66 | "fieldtype": "Column Break" 67 | }, 68 | { 69 | "allow_in_quick_entry": 1, 70 | "fieldname": "company", 71 | "fieldtype": "Link", 72 | "in_standard_filter": 1, 73 | "label": "Company", 74 | "options": "Company", 75 | "remember_last_selected_value": 1, 76 | "reqd": 1 77 | }, 78 | { 79 | "allow_in_quick_entry": 1, 80 | "fetch_from": "company.default_bank_account", 81 | "fetch_if_empty": 1, 82 | "fieldname": "bank_account", 83 | "fieldtype": "Link", 84 | "label": "Paid From (Bank Account)", 85 | "options": "Bank Account", 86 | "remember_last_selected_value": 1, 87 | "reqd": 1 88 | }, 89 | { 90 | "default": "company.default_payable_account", 91 | "fieldname": "pay_to_account", 92 | "fieldtype": "Link", 93 | "in_standard_filter": 1, 94 | "label": "Accounts Payable", 95 | "options": "Account", 96 | "remember_last_selected_value": 1, 97 | "reqd": 1 98 | }, 99 | { 100 | "allow_on_submit": 1, 101 | "depends_on": "eval:doc.docstatus==1", 102 | "fieldname": "company_discretionary_data", 103 | "fieldtype": "Data", 104 | "label": "Company Discretionary Data", 105 | "length": 20 106 | }, 107 | { 108 | "fieldname": "section_break_9", 109 | "fieldtype": "Section Break" 110 | }, 111 | { 112 | "default": "0.00", 113 | "fieldname": "beg_balance", 114 | "fieldtype": "Currency", 115 | "label": "Beginning Bank Account Balance", 116 | "read_only": 1 117 | }, 118 | { 119 | "fieldname": "amended_from", 120 | "fieldtype": "Link", 121 | "hidden": 1, 122 | "label": "Amended From", 123 | "no_copy": 1, 124 | "options": "Check Run", 125 | "print_hide": 1, 126 | "read_only": 1 127 | }, 128 | { 129 | "allow_on_submit": 1, 130 | "fieldname": "transactions", 131 | "fieldtype": "Long Text", 132 | "hidden": 1, 133 | "ignore_xss_filter": 1 134 | }, 135 | { 136 | "allow_on_submit": 1, 137 | "fieldname": "print_count", 138 | "fieldtype": "Int", 139 | "hidden": 1 140 | }, 141 | { 142 | "allow_on_submit": 1, 143 | "default": "Draft", 144 | "fieldname": "status", 145 | "fieldtype": "Select", 146 | "hidden": 1, 147 | "options": "Draft\nPending Approval\nApproved\nSubmitting\nSubmitted\nReady to Print\nConfirm Print\nPrinted" 148 | }, 149 | { 150 | "fieldname": "check_run_table", 151 | "fieldtype": "HTML" 152 | }, 153 | { 154 | "fieldname": "posting_date", 155 | "fieldtype": "Date", 156 | "in_list_view": 1, 157 | "in_standard_filter": 1, 158 | "label": "Posting Date" 159 | } 160 | ], 161 | "is_submittable": 1, 162 | "links": [], 163 | "modified": "2025-05-20 09:04:24.629145", 164 | "modified_by": "Administrator", 165 | "module": "Check Run", 166 | "name": "Check Run", 167 | "naming_rule": "Expression (old style)", 168 | "owner": "Administrator", 169 | "permissions": [ 170 | { 171 | "create": 1, 172 | "read": 1, 173 | "report": 1, 174 | "role": "Accounts User", 175 | "select": 1, 176 | "write": 1 177 | }, 178 | { 179 | "create": 1, 180 | "export": 1, 181 | "print": 1, 182 | "read": 1, 183 | "report": 1, 184 | "role": "Accounts Manager", 185 | "select": 1, 186 | "share": 1, 187 | "submit": 1, 188 | "write": 1 189 | } 190 | ], 191 | "quick_entry": 1, 192 | "sort_field": "modified", 193 | "sort_order": "DESC", 194 | "states": [], 195 | "track_changes": 1, 196 | "track_seen": 1, 197 | "track_views": 1 198 | } 199 | -------------------------------------------------------------------------------- /check_run/check_run/doctype/check_run/check_run_list.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025, AgriTheory and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.listview_settings['Check Run'] = { 5 | add_fields: ['status'], 6 | hide_name_column: true, 7 | has_indicator_for_draft: 1, 8 | get_indicator: doc => { 9 | return [ 10 | __(doc.status), 11 | { 12 | Draft: 'red', 13 | 'Pending Approval': 'grey', 14 | Approved: 'green', 15 | Submitting: 'orange', 16 | Submitted: 'blue', 17 | 'Ready to Print': 'purple', 18 | 'Confirm Print': 'yellow', 19 | Printed: 'green', 20 | }[doc.status], 21 | 'status,=,' + doc.status, 22 | ] 23 | }, 24 | } 25 | -------------------------------------------------------------------------------- /check_run/check_run/doctype/check_run/test_check_run.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, AgriTheory and Contributors 2 | # See license.txt 3 | 4 | import frappe 5 | import unittest 6 | 7 | 8 | class TestCheckRun(unittest.TestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /check_run/check_run/doctype/check_run_settings/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agritheory/check_run/368e6ba124bb12cf52ba11deb4e8829f6d44e52b/check_run/check_run/doctype/check_run_settings/__init__.py -------------------------------------------------------------------------------- /check_run/check_run/doctype/check_run_settings/check_run_settings.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, AgriTheory and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on('Check Run Settings', { 5 | refresh: frm => { 6 | frm.set_query('pay_to_account', () => { 7 | return { 8 | filters: { 9 | company: frm.doc.company, 10 | account_type: 'Payable', 11 | }, 12 | } 13 | }) 14 | frm.set_query('bank_account', () => { 15 | return { 16 | filters: { 17 | is_company_account: 1, 18 | company: frm.doc.company, 19 | }, 20 | } 21 | }) 22 | frm.set_query('print_format', () => { 23 | return { 24 | filters: { 25 | disabled: 0, 26 | doc_type: 'Payment Entry', 27 | }, 28 | } 29 | }) 30 | }, 31 | }) 32 | -------------------------------------------------------------------------------- /check_run/check_run/doctype/check_run_settings/check_run_settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "autoname": "format:ACC-CRS-{bank_account}-{pay_to_account}", 4 | "creation": "2022-08-22 14:43:43.533105", 5 | "doctype": "DocType", 6 | "editable_grid": 1, 7 | "engine": "InnoDB", 8 | "field_order": [ 9 | "company", 10 | "bank_account", 11 | "approver_role", 12 | "column_break_3", 13 | "pay_to_account", 14 | "print_format", 15 | "section_break_4", 16 | "include_purchase_invoices", 17 | "include_journal_entries", 18 | "include_expense_claims", 19 | "pre_check_overdue_items", 20 | "allow_cancellation", 21 | "cascade_cancellation", 22 | "validate_unique_check_number", 23 | "set_payment_entry_posting_date", 24 | "column_break_9", 25 | "number_of_invoices_per_voucher", 26 | "secondary_print_format", 27 | "split_by_address", 28 | "automatically_release_on_hold_invoices", 29 | "file_preview_threshold", 30 | "allow_stand_alone_debit_notes", 31 | "default_modes_of_payment_section_section", 32 | "purchase_invoice", 33 | "journal_entry", 34 | "column_break_21", 35 | "expense_claim", 36 | "ach_settings_section", 37 | "allow_only_verified_accounts_in_nacha_generation", 38 | "ach_file_extension", 39 | "ach_service_class_code", 40 | "ach_standard_class_code", 41 | "ach_description", 42 | "column_break_27", 43 | "immediate_origin", 44 | "company_discretionary_data", 45 | "custom_post_processing_hook" 46 | ], 47 | "fields": [ 48 | { 49 | "fieldname": "company", 50 | "fieldtype": "Link", 51 | "in_list_view": 1, 52 | "label": "Company", 53 | "options": "Company", 54 | "reqd": 1 55 | }, 56 | { 57 | "fieldname": "bank_account", 58 | "fieldtype": "Link", 59 | "label": "Bank Account", 60 | "options": "Bank Account", 61 | "reqd": 1 62 | }, 63 | { 64 | "fieldname": "section_break_4", 65 | "fieldtype": "Section Break" 66 | }, 67 | { 68 | "default": "1", 69 | "fieldname": "include_purchase_invoices", 70 | "fieldtype": "Check", 71 | "label": "Include Purchase Invoices" 72 | }, 73 | { 74 | "default": "1", 75 | "fieldname": "include_journal_entries", 76 | "fieldtype": "Check", 77 | "label": "Include Journal Entries " 78 | }, 79 | { 80 | "default": "1", 81 | "fieldname": "include_expense_claims", 82 | "fieldtype": "Check", 83 | "label": "Include Expense Claims" 84 | }, 85 | { 86 | "default": "0", 87 | "description": "Payment Entries will be unlinked when Check Run is cancelled", 88 | "fieldname": "allow_cancellation", 89 | "fieldtype": "Check", 90 | "label": "Allow Cancellation" 91 | }, 92 | { 93 | "fieldname": "column_break_9", 94 | "fieldtype": "Column Break" 95 | }, 96 | { 97 | "default": "ach", 98 | "description": "Common file extensions are 'ach', 'txt' and 'dat'. Your bank may require one of these.", 99 | "fieldname": "ach_file_extension", 100 | "fieldtype": "Data", 101 | "label": "ACH File Extension" 102 | }, 103 | { 104 | "default": "0", 105 | "description": "Pre-Check all payables that have a due date greater than the Check Run's posting date", 106 | "fieldname": "pre_check_overdue_items", 107 | "fieldtype": "Check", 108 | "label": "Pre-Check Overdue Items" 109 | }, 110 | { 111 | "default": "0", 112 | "description": "When a Check Run is cancelled, all Payment Entries linked to it will also be cancelled. This is not recommended.", 113 | "fieldname": "cascade_cancellation", 114 | "fieldtype": "Check", 115 | "label": "Cascade Cancellation" 116 | }, 117 | { 118 | "description": "Defaults to 5 if no value is provided", 119 | "fieldname": "number_of_invoices_per_voucher", 120 | "fieldtype": "Int", 121 | "label": "Number of Invoices per Voucher", 122 | "non_negative": 1 123 | }, 124 | { 125 | "fieldname": "column_break_3", 126 | "fieldtype": "Column Break" 127 | }, 128 | { 129 | "fieldname": "pay_to_account", 130 | "fieldtype": "Link", 131 | "label": "Payable Account", 132 | "options": "Account", 133 | "reqd": 1 134 | }, 135 | { 136 | "fieldname": "ach_service_class_code", 137 | "fieldtype": "Select", 138 | "label": "ACH Service Class Code", 139 | "options": "200\n220\n225" 140 | }, 141 | { 142 | "description": "PPD is only supported Entry format at this time", 143 | "fieldname": "ach_standard_class_code", 144 | "fieldtype": "Select", 145 | "label": "ACH Standard Class Code", 146 | "options": "PPD" 147 | }, 148 | { 149 | "fieldname": "ach_description", 150 | "fieldtype": "Data", 151 | "label": "ACH Description", 152 | "length": 10 153 | }, 154 | { 155 | "fieldname": "print_format", 156 | "fieldtype": "Link", 157 | "label": "Print Format", 158 | "options": "Print Format" 159 | }, 160 | { 161 | "default": "0", 162 | "fieldname": "split_by_address", 163 | "fieldtype": "Check", 164 | "label": "Split Invoices by Address" 165 | }, 166 | { 167 | "fieldname": "ach_settings_section", 168 | "fieldtype": "Section Break", 169 | "label": "ACH Settings" 170 | }, 171 | { 172 | "fieldname": "column_break_21", 173 | "fieldtype": "Column Break" 174 | }, 175 | { 176 | "default": "0", 177 | "fieldname": "automatically_release_on_hold_invoices", 178 | "fieldtype": "Check", 179 | "label": "Automatically Release On Hold Invoices" 180 | }, 181 | { 182 | "fieldname": "immediate_origin", 183 | "fieldtype": "Data", 184 | "label": "Immediate Origin" 185 | }, 186 | { 187 | "fieldname": "company_discretionary_data", 188 | "fieldtype": "Data", 189 | "label": "Company Discretionary Data", 190 | "length": 20 191 | }, 192 | { 193 | "fieldname": "custom_post_processing_hook", 194 | "fieldtype": "Data", 195 | "label": "Custom Post Processing Hook", 196 | "read_only": 1 197 | }, 198 | { 199 | "fieldname": "default_modes_of_payment_section_section", 200 | "fieldtype": "Section Break", 201 | "label": "Default Modes of Payment Section" 202 | }, 203 | { 204 | "fieldname": "purchase_invoice", 205 | "fieldtype": "Link", 206 | "label": "Purchase Invoice", 207 | "options": "Mode of Payment" 208 | }, 209 | { 210 | "fieldname": "journal_entry", 211 | "fieldtype": "Link", 212 | "label": "Journal Entry", 213 | "options": "Mode of Payment" 214 | }, 215 | { 216 | "fieldname": "expense_claim", 217 | "fieldtype": "Link", 218 | "label": "Expense Claim", 219 | "options": "Mode of Payment" 220 | }, 221 | { 222 | "fieldname": "column_break_27", 223 | "fieldtype": "Column Break" 224 | }, 225 | { 226 | "default": "1000", 227 | "description": "File preview is enabled up to this number of transactions", 228 | "fieldname": "file_preview_threshold", 229 | "fieldtype": "Int", 230 | "label": "File Preview Threshold" 231 | }, 232 | { 233 | "default": "0", 234 | "fieldname": "validate_unique_check_number", 235 | "fieldtype": "Check", 236 | "label": "Validate Unique Check Number" 237 | }, 238 | { 239 | "fieldname": "secondary_print_format", 240 | "fieldtype": "Link", 241 | "label": "Secondary Print Format", 242 | "options": "Print Format" 243 | }, 244 | { 245 | "default": "No", 246 | "fieldname": "allow_stand_alone_debit_notes", 247 | "fieldtype": "Select", 248 | "label": "Allow stand-alone debit notes?", 249 | "options": "Yes\nNo" 250 | }, 251 | { 252 | "default": "Use Today's Date", 253 | "fieldname": "set_payment_entry_posting_date", 254 | "fieldtype": "Select", 255 | "label": "Set Payment Entry Posting Date", 256 | "options": "Use Today's Date\nUse Check Run's Posting Date" 257 | }, 258 | { 259 | "fieldname": "approver_role", 260 | "fieldtype": "Link", 261 | "label": "Approver Role", 262 | "options": "Role" 263 | }, 264 | { 265 | "default": "0", 266 | "fieldname": "allow_only_verified_accounts_in_nacha_generation", 267 | "fieldtype": "Check", 268 | "label": "Allow only verified accounts in NACHA generation" 269 | } 270 | ], 271 | "links": [], 272 | "modified": "2025-05-15 11:47:48.719496", 273 | "modified_by": "Administrator", 274 | "module": "Check Run", 275 | "name": "Check Run Settings", 276 | "naming_rule": "Expression", 277 | "owner": "Administrator", 278 | "permissions": [ 279 | { 280 | "create": 1, 281 | "delete": 1, 282 | "email": 1, 283 | "export": 1, 284 | "print": 1, 285 | "read": 1, 286 | "report": 1, 287 | "role": "System Manager", 288 | "share": 1, 289 | "write": 1 290 | }, 291 | { 292 | "create": 1, 293 | "delete": 1, 294 | "email": 1, 295 | "export": 1, 296 | "print": 1, 297 | "read": 1, 298 | "report": 1, 299 | "role": "Accounts Manager", 300 | "share": 1, 301 | "write": 1 302 | } 303 | ], 304 | "quick_entry": 1, 305 | "sort_field": "modified", 306 | "sort_order": "DESC", 307 | "states": [], 308 | "track_changes": 1 309 | } 310 | -------------------------------------------------------------------------------- /check_run/check_run/doctype/check_run_settings/check_run_settings.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, AgriTheory and contributors 2 | # For license information, please see license.txt 3 | 4 | import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class CheckRunSettings(Document): 9 | pass 10 | 11 | 12 | @frappe.whitelist() 13 | def create(company: str, bank_account: str, pay_to_account: str) -> str: 14 | crs = frappe.new_doc("Check Run Settings") 15 | crs.company = company 16 | crs.bank_account = bank_account 17 | crs.pay_to_account = pay_to_account 18 | crs.save() 19 | return crs 20 | -------------------------------------------------------------------------------- /check_run/check_run/doctype/check_run_settings/test_check_run_settings.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, AgriTheory and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | import unittest 6 | 7 | 8 | class TestCheckRunSettings(unittest.TestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /check_run/check_run/print_format/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agritheory/check_run/368e6ba124bb12cf52ba11deb4e8829f6d44e52b/check_run/check_run/print_format/__init__.py -------------------------------------------------------------------------------- /check_run/check_run/print_format/example_secondary_print_format/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agritheory/check_run/368e6ba124bb12cf52ba11deb4e8829f6d44e52b/check_run/check_run/print_format/example_secondary_print_format/__init__.py -------------------------------------------------------------------------------- /check_run/check_run/print_format/example_secondary_print_format/example_secondary_print_format.json: -------------------------------------------------------------------------------- 1 | { 2 | "absolute_value": 0, 3 | "align_labels_right": 0, 4 | "creation": "2024-03-06 04:31:54.619036", 5 | "css": "@font-face {\n font-family: 'EntezareZohoor2';\n src: url('fonts/EntezareZohoor2.eot'), url('fonts/EntezareZohoor2.ttf') format('truetype'), url('fonts/EntezareZohoor2.svg') format('svg');\n font-weight: normal;\n font-style: normal;\n}\n\n.print-format {\n\tpadding: 0px;\n}\n@media screen {\n\t.print-format {\n\t\tpadding: 0in;\n\t}\n}\n#payer_check_window_block {\n top: 0.7cm; \n left: 0.7cm;\n height: 2.2cm;\n width: 8.8cm;\n position: absolute;\n \n}\n\n#payer_name_block{\n top: 0.1cm; \n left: 1cm;\n position: absolute;\n}\n\n#payee_address_window_block {\n top:4.9cm;\n left: 1.9cm;\n position: absolute; \n width: 8.8cm; \n height:2.2cm;\n}\n\n#address_block{\n top: 0.2cm; \n left: 0.2cm;\n position: relative;\n}\n\n\n\n#memo_block {\n top:7.1cm;\n left: 2cm;\n position: absolute; \n width: 6cm;\n}\n\n\n#check_section_1 {\n font-size: 15px;\n width:20.0cm;\n height:8.9cm;\n}\n\n#check_section_2 {\n height:8.9cm;\n}\n\n#check_section_3 {\n height:8.9cm;\n}\n\n\n#payer_name_block {\ntext-align: center; \n\n}\n\n#payer_name_block {\nwidth:4cm;\ntext-align: center; \nposition: absolute;\n}\n\n#bank_info_block {\nfont-size: 10px;\nwidth:2.5cm;\nheight:1.8cm;\ntext-align: center; \nposition: absolute;\n}\n\n\n#payment_in_words_block {\n font-size: 13px;\n}\n\n#memo_block {\nfont-size: 10px;\n}\n\n#signature_block {\ncolor: blue;\nfont-family: cursive;\n\n}\n#payment_amount_block{\n top:3.3cm;\n left: 17.6cm;\n\tposition: absolute; \n\tmin-width: 4cm;\n\n}\n\n.payment_reference_block {\npadding-left:1cm; \npadding-right:1cm; \n}\n\n.payment_name_cell {\ntext-align: right; \n}\n\n#payment_amount_number_block {\n top:3.3cm;\n left: 17.6cm;\n\tposition: absolute; \n\tmin-width: 4cm;\n}\n\n\n.right_stamp {\n top:2.8cm;\n left: 16.6cm;\n width: 3cm;\n\theight: 1.5cm;\n\tfont-size: 40px;\n \tfort-weight: bold;\n position: absolute; \n}\n\n.sig_stamp {\n top:6.3cm;\n left: 13.8cm;\n width: 3cm;\n\theight: 1.5cm;\n\tfont-size: 40px;\n \tfort-weight: bold;\n position: absolute; \n}\n\n.big_stamp {\n top:2.8cm;\n left: 6.1cm;\n width: 7cm;\n\theight: 3cm;\n\tfont-size: 80px;\n \tfort-weight: bold;\n position: absolute; \n}\n\n\n.stamp {\n\tmargin: 0px;\n\toverflow: hidden;\n display: flex;\n justify-content: space-around;\n align-items: center;\n vertical-align: middle;\n text-align: center;\n\n flex-direction: row;\n color: #555;\n\tfont-weight: 700;\n\tborder: 0.25rem solid #555;\n\tdisplay: inline-block;\n\t\n\ttext-transform: uppercase;\n\tborder-radius: 1rem;\n\tfont-family: 'Courier';\n\t-webkit-mask-image: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/8399/grunge.png');\n -webkit-mask-size: 944px 604px;\n mix-blend-mode: multiply;\n}\n\n\n.is-nope {\n color: #ff5858;\n border: 0.5rem double #D23;\n \t-webkit-mask-position: 2rem 3rem;\n \t\n \n}\n\n.is-draft {\n\tcolor: #ff5858;\n\tborder: 1rem double #ff5858;\n font-size: 6rem;\n font-family: \"Open sans\", Helvetica, Arial, sans-serif;\n border-radius: 0;\n padding: 0.5rem;\n} ", 6 | "custom_format": 1, 7 | "default_print_language": "en", 8 | "disabled": 0, 9 | "doc_type": "Payment Entry", 10 | "docstatus": 0, 11 | "doctype": "Print Format", 12 | "font": "Default", 13 | "font_size": 0, 14 | "html": "\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n {% for reference in doc.references %}\r\n \r\n {% if reference.reference_doctype == 'Purchase Invoice' %}\r\n \r\n \r\n {% elif reference.reference_doctype == 'Sales Invoice' %}\r\n \r\n \r\n {% elif reference.reference_doctype == 'Expense Claim' %}\r\n \r\n \r\n {% elif reference.reference_doctype == 'Journal Entry' %}\r\n \r\n \r\n {% endif %}\r\n \r\n \r\n \r\n {% endfor %}\r\n
\r\n Cheque Number: {{ doc.reference_no or '' }}\r\n
{{doc.party_name}} {{ frappe.utils.formatdate(doc.reference_date) or '' }} {{doc.get_formatted(\"base_paid_amount\")}}
Date Reference Amount Payment
{{ frappe.utils.formatdate(frappe.db.get_value(reference.reference_doctype, reference.reference_name, \"bill_date\")) or \"\"}} {{ frappe.db.get_value(reference.reference_doctype, reference.reference_name, \"bill_no\") or \"\" }}{{ frappe.utils.formatdate(frappe.db.get_value(reference.reference_doctype, reference.reference_name, \"po_date\")) or \"\"}} {{ frappe.db.get_value(reference.reference_doctype, reference.reference_name, \"po_no\") or \"\" }}{{ frappe.utils.formatdate(frappe.db.get_value(reference.reference_doctype, reference.reference_name, \"posting_date\")) or \" \"}} {{ frappe.db.get_value(reference.reference_doctype, reference.reference_name, \"name\") or \" \" }} {{ frappe.utils.formatdate(frappe.db.get_value(reference.reference_doctype, reference.reference_name, \"posting_date\")) or \" \"}} {{ frappe.db.get_value(reference.reference_doctype, reference.reference_name, \"name\") or \" \" }} {{ frappe.utils.fmt_money(reference.get_formatted('total_amount'), 2, 'USD')}} {{ reference.get_formatted('allocated_amount')}}
", 15 | "idx": 0, 16 | "line_breaks": 0, 17 | "margin_bottom": 0.0, 18 | "margin_left": 0.0, 19 | "margin_right": 0.0, 20 | "margin_top": 0.0, 21 | "modified": "2024-03-28 01:38:34.472380", 22 | "modified_by": "Administrator", 23 | "module": "Check Run", 24 | "name": "Example Secondary Print Format", 25 | "owner": "Administrator", 26 | "page_number": "Hide", 27 | "print_format_builder": 0, 28 | "print_format_builder_beta": 0, 29 | "print_format_type": "Jinja", 30 | "raw_printing": 0, 31 | "show_section_headings": 0, 32 | "standard": "Yes" 33 | } 34 | -------------------------------------------------------------------------------- /check_run/check_run/print_format/example_voucher/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agritheory/check_run/368e6ba124bb12cf52ba11deb4e8829f6d44e52b/check_run/check_run/print_format/example_voucher/__init__.py -------------------------------------------------------------------------------- /check_run/check_run/report/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agritheory/check_run/368e6ba124bb12cf52ba11deb4e8829f6d44e52b/check_run/check_run/report/__init__.py -------------------------------------------------------------------------------- /check_run/check_run/report/ach_prenote/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025, AgriTheory and contributors 2 | # For license information, please see license.txt 3 | -------------------------------------------------------------------------------- /check_run/check_run/report/ach_prenote/ach_prenote.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025, AgriTheory and contributors 2 | // For license information, please see license.txt 3 | /* eslint-disable */ 4 | 5 | let changedItems = {} 6 | 7 | frappe.query_reports['ACH Prenote'] = { 8 | filters: [ 9 | { 10 | fieldname: 'validated_date', 11 | label: __('Validated Date Before'), 12 | fieldtype: 'Date', 13 | }, 14 | { 15 | fieldname: 'last_used_date', 16 | label: __('Last Used Date Before'), 17 | fieldtype: 'Date', 18 | }, 19 | { 20 | fieldname: 'ach_account_type', 21 | label: __('ACH Account Type'), 22 | fieldtype: 'Select', 23 | options: ['', 'Checking', 'Savings'], 24 | }, 25 | ], 26 | onload: reportview => { 27 | frappe.query_report.page.add_button( 28 | 'Generate File', 29 | () => { 30 | generate_ach_prenote() 31 | }, 32 | { btn_class: 'btn-success' } 33 | ) 34 | }, 35 | saveButton: null, 36 | get_datatable_options(options) { 37 | return Object.assign(options, { 38 | getEditor: this.get_editing_object.bind(this), 39 | }) 40 | }, 41 | get_editing_object(colIndex, rowIndex, value, parent) { 42 | const control = this.render_editing_input(colIndex, value, parent) 43 | if (!control) return false 44 | control.df.change = event => { 45 | frappe.query_report.page.set_indicator('Unsaved', 'orange') 46 | changedItems[rowIndex] = event?.currentTarget.value || null 47 | this.add_save_button(frappe.query_report) 48 | control.set_focus() 49 | } 50 | try { 51 | return { 52 | initValue: async value => { 53 | return control.set_value(value) 54 | }, 55 | setValue: value => { 56 | if (!value) { 57 | return control.get_value() 58 | } else { 59 | return control.set_value(value) 60 | } 61 | }, 62 | getValue: async () => { 63 | return control.get_value() 64 | }, 65 | } 66 | } catch (error) { 67 | console.log(error) 68 | } 69 | }, 70 | render_editing_input(colIndex, value, parent) { 71 | const col = frappe.query_report.datatable.getColumn(colIndex) 72 | let control = null 73 | control = frappe.ui.form.make_control({ 74 | df: col, 75 | parent: parent, 76 | render_input: true, 77 | }) 78 | control.set_value(value || '') 79 | control.toggle_label(false) 80 | control.toggle_description(false) 81 | return control 82 | }, 83 | formatter: (value, row, column, data, default_formatter) => { 84 | value = default_formatter(value, row, column, data) 85 | if (data && column.fieldname == 'party_name') { 86 | value = `${data.party_name}` 87 | } 88 | return value 89 | }, 90 | add_save_button: reportview => { 91 | if (reportview.saveButton) { 92 | return 93 | } 94 | reportview.saveButton = frappe.query_report.page.add_button( 95 | __('Save Changes'), 96 | () => { 97 | let data = [] 98 | let row 99 | for (const [rowIndex, updated_date] of Object.entries(changedItems)) { 100 | row = frappe.query_report.data[rowIndex] 101 | row.account_details_validated = updated_date 102 | data.push(row) 103 | } 104 | frappe 105 | .xcall('check_run.check_run.report.ach_prenote.ach_prenote.update_validated_dates', { data: data }) 106 | .then(r => { 107 | reportview.saveButton.remove() 108 | reportview.saveButton = null 109 | changedItems = {} 110 | reportview.data = [] 111 | frappe.query_report.page.set_indicator('', '') 112 | // this.get_datatable_options(frappe.query_report.datatable.options) 113 | reportview.refresh() 114 | frappe.query_report.page.set_indicator('', '') 115 | frappe.show_alert(__('Updated Validated Dates'), 5) 116 | }) 117 | }, 118 | { btn_class: 'btn-primary' } 119 | ) 120 | }, 121 | } 122 | 123 | function generate_ach_prenote() { 124 | return new Promise(resolve => { 125 | let dialog = new frappe.ui.Dialog({ 126 | title: __('Please provide additional details'), 127 | fields: [ 128 | { 129 | label: 'Check Run Settings', 130 | fieldname: 'check_run_settings', 131 | fieldtype: 'Link', 132 | options: 'Check Run Settings', 133 | reqd: 1, 134 | }, 135 | { 136 | fieldtype: 'Currency', 137 | label: __('ACH Amount'), 138 | fieldname: 'ach_amount', 139 | default: 0.05, 140 | reqd: 1, 141 | }, 142 | { 143 | fieldtype: 'Date', 144 | label: __('Date'), 145 | fieldname: 'date', 146 | default: moment().date(0).format(), 147 | reqd: 1, 148 | }, 149 | { 150 | fieldtype: 'Select', 151 | label: __('Code'), 152 | fieldname: 'code', 153 | reqd: 1, 154 | options: [ 155 | 'Checking Credit Live', 156 | 'Checking Credit Prenote', 157 | 'Checking Credit Child Support prenote', 158 | 'Checking Debit Live', 159 | 'Checking Debit Prenote', 160 | 'Checking Debit Child Support prenote', 161 | 'Savings Credit Live', 162 | 'Savings Credit Prenote', 163 | 'Savings Credit Child Support prenote', 164 | 'Savings Debit Live', 165 | 'Savings Debit Prenote', 166 | 'Savings Debit Child Support prenote', 167 | 'Loan Credit Live', 168 | 'Loan Credit Prenote', 169 | ], 170 | }, 171 | ], 172 | primary_action: () => { 173 | let values = dialog.get_values() 174 | dialog.hide() 175 | frappe 176 | .xcall('check_run.check_run.report.ach_prenote.ach_prenote.prepare_ach_prenote', { 177 | check_run_settings: values.check_run_settings, 178 | ach_amount: values.ach_amount, 179 | date: values.date, 180 | code: values.code, 181 | data: frappe.query_report.data, 182 | }) 183 | .then(r => { 184 | if (r && r.success) { 185 | let params = new URLSearchParams({ 186 | check_run_settings: values.check_run_settings, 187 | ach_amount: values.ach_amount, 188 | date: values.date, 189 | code: values.code, 190 | request_id: r.request_id || '', 191 | }).toString() 192 | 193 | window.open( 194 | `/api/method/check_run.check_run.report.ach_prenote.ach_prenote.download_ach_prenote?${params}` 195 | ) 196 | 197 | setTimeout(() => { 198 | resolve() 199 | frappe.query_report.refresh_report() 200 | }, 1000) 201 | } else { 202 | frappe.msgprint(__('Error preparing ACH prenote file')) 203 | resolve() 204 | } 205 | }) 206 | }, 207 | primary_action_label: __('Generate File'), 208 | }) 209 | dialog.show() 210 | dialog.get_close_btn() 211 | }) 212 | } 213 | -------------------------------------------------------------------------------- /check_run/check_run/report/ach_prenote/ach_prenote.json: -------------------------------------------------------------------------------- 1 | { 2 | "add_total_row": 0, 3 | "columns": [], 4 | "creation": "2025-05-12 14:23:36.811868", 5 | "disable_prepared_report": 0, 6 | "disabled": 0, 7 | "docstatus": 0, 8 | "doctype": "Report", 9 | "filters": [], 10 | "idx": 0, 11 | "is_standard": "Yes", 12 | "modified": "2025-05-12 14:24:14.138591", 13 | "modified_by": "Administrator", 14 | "module": "Check Run", 15 | "name": "ACH Prenote", 16 | "owner": "Administrator", 17 | "prepared_report": 0, 18 | "ref_doctype": "Check Run Settings", 19 | "report_name": "ACH Prenote", 20 | "report_type": "Script Report", 21 | "roles": [ 22 | { 23 | "role": "System Manager" 24 | }, 25 | { 26 | "role": "Accounts Manager" 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /check_run/check_run/report/payables_attachments/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agritheory/check_run/368e6ba124bb12cf52ba11deb4e8829f6d44e52b/check_run/check_run/report/payables_attachments/__init__.py -------------------------------------------------------------------------------- /check_run/check_run/report/payables_attachments/payables_attachments.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, AgriTheory and contributors 2 | // For license information, please see license.txt 3 | /* eslint-disable */ 4 | 5 | frappe.query_reports['Payables Attachments'] = { 6 | filters: [], 7 | } 8 | 9 | frappe.ui.addFilePreviewWrapper() 10 | 11 | function pdf_preview(file_url) { 12 | frappe.ui.pdfPreview(cur_frm, file_url) 13 | } 14 | -------------------------------------------------------------------------------- /check_run/check_run/report/payables_attachments/payables_attachments.json: -------------------------------------------------------------------------------- 1 | { 2 | "add_total_row": 0, 3 | "columns": [], 4 | "creation": "2023-08-11 10:09:28.938669", 5 | "disable_prepared_report": 0, 6 | "disabled": 0, 7 | "docstatus": 0, 8 | "doctype": "Report", 9 | "filters": [], 10 | "idx": 0, 11 | "is_standard": "Yes", 12 | "modified": "2023-08-11 10:09:28.938669", 13 | "modified_by": "Administrator", 14 | "module": "Check Run", 15 | "name": "Payables Attachments", 16 | "owner": "Administrator", 17 | "prepared_report": 0, 18 | "ref_doctype": "Purchase Invoice", 19 | "report_name": "Payables Attachments", 20 | "report_type": "Script Report", 21 | "roles": [ 22 | { 23 | "role": "Accounts User" 24 | }, 25 | { 26 | "role": "Purchase User" 27 | }, 28 | { 29 | "role": "Accounts Manager" 30 | }, 31 | { 32 | "role": "Auditor" 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /check_run/check_run/report/payables_attachments/payables_attachments.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, AgriTheory and contributors 2 | # For license information, please see license.txt 3 | 4 | import frappe 5 | from frappe.desk.form.load import get_attachments 6 | from frappe.query_builder import DocType 7 | from pypika import Order 8 | 9 | 10 | def execute(filters=None): 11 | return get_columns(filters), get_data(filters) 12 | 13 | 14 | def get_data(filters): 15 | PurchaseInvoice = DocType("Purchase Invoice") 16 | data = ( 17 | frappe.qb.from_(PurchaseInvoice) 18 | .select( 19 | PurchaseInvoice.name, 20 | PurchaseInvoice.title, 21 | PurchaseInvoice.supplier, 22 | PurchaseInvoice.company, 23 | PurchaseInvoice.posting_date, 24 | PurchaseInvoice.grand_total, 25 | PurchaseInvoice.status, 26 | PurchaseInvoice.currency, 27 | PurchaseInvoice.supplier_name, 28 | PurchaseInvoice.grand_total, 29 | PurchaseInvoice.outstanding_amount, 30 | PurchaseInvoice.due_date, 31 | PurchaseInvoice.is_return, 32 | PurchaseInvoice.release_date, 33 | PurchaseInvoice.represents_company, 34 | PurchaseInvoice.is_internal_supplier, 35 | ) 36 | .orderby("modified", Order.desc) 37 | ).run(as_dict=True) 38 | 39 | for row in data: 40 | row["attachments"] = " ".join( 41 | [ 42 | f"""{attachment.file_name}""" 43 | for attachment in get_attachments("Purchase Invoice", row["name"]) 44 | if attachment.file_url.endswith(".pdf") 45 | ] 46 | ) 47 | return data 48 | 49 | 50 | def get_columns(filters): 51 | return [ 52 | { 53 | "label": frappe._("Name"), 54 | "fieldname": "name", 55 | "fieldtype": "Link", 56 | "options": "Purchase Invoice", 57 | "width": "150px", 58 | }, 59 | { 60 | "label": frappe._("Title"), 61 | "fieldname": "title", 62 | "fieldtype": "Data", 63 | "width": "200px", 64 | }, 65 | { 66 | "label": frappe._("Supplier"), 67 | "fieldname": "supplier", 68 | "fieldtype": "Link", 69 | "options": "Supplier", 70 | "width": "200px", 71 | }, 72 | { 73 | "label": frappe._("Supplier Name"), 74 | "fieldname": "supplier_name", 75 | "fieldtype": "Data", 76 | "width": "200px", 77 | }, 78 | { 79 | "label": frappe._("Company"), 80 | "fieldname": "company", 81 | "fieldtype": "Link", 82 | "options": "Company", 83 | "width": "200px", 84 | }, 85 | { 86 | "label": frappe._("Date"), 87 | "fieldname": "posting_date", 88 | "fieldtype": "Date", 89 | "width": "200px", 90 | }, 91 | { 92 | "label": frappe._("Grand Total"), 93 | "fieldname": "grand_total", 94 | "fieldtype": "Currency", 95 | "width": "200px", 96 | }, 97 | { 98 | "label": frappe._("Status"), 99 | "fieldname": "status", 100 | "fieldtype": "Data", 101 | "width": "200px", 102 | }, 103 | { 104 | "label": frappe._("Currency"), 105 | "fieldname": "currency", 106 | "fieldtype": "Link", 107 | "options": "Currency", 108 | "width": "200px", 109 | }, 110 | { 111 | "label": frappe._("Grand Total (Company Currency)"), 112 | "fieldname": "grand_total", 113 | "fieldtype": "Currency", 114 | "width": "200px", 115 | }, 116 | { 117 | "label": frappe._("Outstanding Amount"), 118 | "fieldname": "outstanding_amount", 119 | "fieldtype": "Currency", 120 | "width": "200px", 121 | }, 122 | { 123 | "label": frappe._("Due Date"), 124 | "fieldname": "due_date", 125 | "fieldtype": "Date", 126 | "width": "200px", 127 | }, 128 | { 129 | "label": frappe._("Is Return (Debit Note)"), 130 | "fieldname": "is_return", 131 | "fieldtype": "Check", 132 | "width": "200px", 133 | }, 134 | { 135 | "label": frappe._("Release Date"), 136 | "fieldname": "release_date", 137 | "fieldtype": "Date", 138 | "width": "200px", 139 | }, 140 | { 141 | "label": frappe._("Represents Company"), 142 | "fieldname": "represents_company", 143 | "fieldtype": "Link", 144 | "options": "Company", 145 | "width": "200px", 146 | }, 147 | { 148 | "label": frappe._("Is Internal Supplier"), 149 | "fieldname": "is_internal_supplier", 150 | "fieldtype": "Check", 151 | "width": "200px", 152 | }, 153 | { 154 | "label": frappe._("Attachments"), 155 | "fieldname": "attachments", 156 | "fieldtype": "Data", 157 | "width": "400px", 158 | }, 159 | ] 160 | -------------------------------------------------------------------------------- /check_run/check_run/report/positive_pay/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agritheory/check_run/368e6ba124bb12cf52ba11deb4e8829f6d44e52b/check_run/check_run/report/positive_pay/__init__.py -------------------------------------------------------------------------------- /check_run/check_run/report/positive_pay/positive_pay.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, AgriTheory and contributors 2 | // For license information, please see license.txt 3 | /* eslint-disable */ 4 | 5 | frappe.query_reports['Positive Pay'] = { 6 | filters: [ 7 | { 8 | fieldname: 'bank_account', 9 | label: __('Bank Account'), 10 | fieldtype: 'Link', 11 | options: 'Bank Account', 12 | reqd: 1, 13 | }, 14 | { 15 | fieldname: 'start_date', 16 | label: __('Start Date'), 17 | fieldtype: 'Date', 18 | default: moment().date(0).startOf('month').format(), 19 | reqd: 1, 20 | }, 21 | { 22 | fieldname: 'end_date', 23 | label: __('End Date'), 24 | fieldtype: 'Date', 25 | default: moment().date(0).format(), 26 | reqd: 1, 27 | }, 28 | ], 29 | } 30 | -------------------------------------------------------------------------------- /check_run/check_run/report/positive_pay/positive_pay.json: -------------------------------------------------------------------------------- 1 | { 2 | "add_total_row": 0, 3 | "columns": [], 4 | "creation": "2022-03-29 11:56:52.874884", 5 | "disable_prepared_report": 0, 6 | "disabled": 0, 7 | "docstatus": 0, 8 | "doctype": "Report", 9 | "filters": [], 10 | "idx": 0, 11 | "is_standard": "Yes", 12 | "modified": "2022-03-29 11:57:07.593115", 13 | "modified_by": "Administrator", 14 | "module": "Check Run", 15 | "name": "Positive Pay", 16 | "owner": "Administrator", 17 | "prepared_report": 0, 18 | "ref_doctype": "Check Run", 19 | "report_name": "Positive Pay", 20 | "report_type": "Script Report", 21 | "roles": [ 22 | { 23 | "role": "Accounts User" 24 | }, 25 | { 26 | "role": "Accounts Manager" 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /check_run/check_run/report/positive_pay/positive_pay.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, AgriTheory and contributors 2 | # For license information, please see license.txt 3 | 4 | import frappe 5 | 6 | 7 | def execute(filters=None): 8 | return get_columns(filters), get_data(filters) 9 | 10 | 11 | def get_columns(filters): 12 | return [ 13 | { 14 | "label": frappe._("Check Date"), 15 | "fieldname": "check_date", 16 | "fieldtype": "Date", 17 | "width": "150px", 18 | }, 19 | { 20 | "label": frappe._("Check Number"), 21 | "fieldname": "check_number", 22 | "fieldtype": "Data", 23 | "width": "200px", 24 | }, 25 | { 26 | "label": frappe._("Party Name"), 27 | "fieldname": "party_name", 28 | "fieldtype": "Data", 29 | "width": "400px", 30 | }, 31 | ] 32 | 33 | 34 | def get_data(filters): 35 | pe = frappe.qb.DocType("Payment Entry") 36 | mop = frappe.qb.DocType("Mode of Payment") 37 | return ( 38 | frappe.qb.from_(pe) 39 | .inner_join(mop) 40 | .on(pe.mode_of_payment == mop.name) 41 | .select( 42 | (pe.reference_no).as_("check_number"), 43 | (pe.reference_date).as_("check_date"), 44 | pe.party_name, 45 | ) 46 | .where(pe.reference_date >= filters.start_date) 47 | .where(pe.reference_date <= filters.end_date) 48 | .where(pe.bank_account == filters.bank_account) 49 | .where(mop.type == "Bank") 50 | .where(pe.docstatus == 1) 51 | .orderby(pe.reference_date) 52 | .run(as_dict=True) 53 | ) 54 | -------------------------------------------------------------------------------- /check_run/config/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agritheory/check_run/368e6ba124bb12cf52ba11deb4e8829f6d44e52b/check_run/config/__init__.py -------------------------------------------------------------------------------- /check_run/config/desktop.py: -------------------------------------------------------------------------------- 1 | from frappe import _ 2 | 3 | 4 | def get_data(): 5 | return [ 6 | { 7 | "module_name": "Check Run", 8 | "color": "grey", 9 | "icon": "octicon octicon-file-directory", 10 | "type": "module", 11 | "label": _("Check Run"), 12 | } 13 | ] 14 | -------------------------------------------------------------------------------- /check_run/config/docs.py: -------------------------------------------------------------------------------- 1 | """ 2 | Configuration for docs 3 | """ 4 | 5 | # source_link = "https://github.com/[org_name]/check_run" 6 | # docs_base_url = "https://[org_name].github.io/check_run" 7 | # headline = "App that does everything" 8 | # sub_heading = "Yes, you got that right the first time, everything" 9 | 10 | 11 | def get_context(context): 12 | context.brand_html = "Check Run" 13 | -------------------------------------------------------------------------------- /check_run/customize.py: -------------------------------------------------------------------------------- 1 | import json 2 | from pathlib import Path 3 | 4 | import frappe 5 | 6 | 7 | def load_customizations(): 8 | customizations_directory = ( 9 | Path().cwd().parent / "apps" / "check_run" / "check_run" / "check_run" / "custom" 10 | ) 11 | files = list(customizations_directory.glob("**/*.json")) 12 | for file in files: 13 | customizations = json.loads(Path(file).read_text()) 14 | for field in customizations.get("custom_fields"): 15 | if field.get("module") != "Check Run": 16 | continue 17 | existing_field = frappe.get_value("Custom Field", field.get("name")) 18 | custom_field = ( 19 | frappe.get_doc("Custom Field", field.get("name")) 20 | if existing_field 21 | else frappe.new_doc("Custom Field") 22 | ) 23 | field.pop("modified") 24 | {custom_field.set(key, value) for key, value in field.items()} 25 | custom_field.flags.ignore_permissions = True 26 | custom_field.flags.ignore_version = True 27 | custom_field.save() 28 | for prop in customizations.get("property_setters"): 29 | if prop.get("module") != "Check Run": 30 | continue 31 | property_setter = frappe.get_doc( 32 | { 33 | "name": prop.get("name"), 34 | "doctype": "Property Setter", 35 | "doctype_or_field": prop.get("doctype_or_field"), 36 | "doc_type": prop.get("doc_type"), 37 | "field_name": prop.get("field_name"), 38 | "property": prop.get("property"), 39 | "value": prop.get("value"), 40 | "property_type": prop.get("property_type"), 41 | } 42 | ) 43 | property_setter.flags.ignore_permissions = True 44 | property_setter.insert() 45 | 46 | 47 | def add_workflow_for_voided_check(): 48 | 49 | workflow_actions = [ 50 | { 51 | "docstatus": 0, 52 | "doctype": "Workflow Action Master", 53 | "name": "Submit", 54 | "workflow_action_name": "Submit", 55 | }, 56 | { 57 | "docstatus": 0, 58 | "doctype": "Workflow Action Master", 59 | "name": "Void", 60 | "workflow_action_name": "Void", 61 | }, 62 | { 63 | "docstatus": 0, 64 | "doctype": "Workflow Action Master", 65 | "name": "Cancel", 66 | "workflow_action_name": "Cancel", 67 | }, 68 | { 69 | "docstatus": 0, 70 | "doctype": "Workflow Action Master", 71 | "name": "Save", 72 | "workflow_action_name": "Save", 73 | }, 74 | ] 75 | 76 | for action in workflow_actions: 77 | if not frappe.db.exists("Workflow Action Master", action["name"]): 78 | act = frappe.new_doc("Workflow Action Master") 79 | act.update(action) 80 | act.insert() 81 | 82 | workflow_states = [ 83 | { 84 | "docstatus": 0, 85 | "icon": "", 86 | "name": "Submitted", 87 | "style": "Primary", 88 | "workflow_state_name": "Submitted", 89 | }, 90 | { 91 | "docstatus": 0, 92 | "icon": "", 93 | "name": "Voided", 94 | "style": "Inverse", 95 | "workflow_state_name": "Voided", 96 | }, 97 | { 98 | "docstatus": 0, 99 | "doctype": "Workflow State", 100 | "icon": "", 101 | "name": "Cancelled", 102 | "style": "Inverse", 103 | "workflow_state_name": "Cancelled", 104 | }, 105 | { 106 | "docstatus": 0, 107 | "doctype": "Workflow State", 108 | "icon": "", 109 | "name": "Draft", 110 | "style": "Warning", 111 | "workflow_state_name": "Draft", 112 | }, 113 | ] 114 | 115 | for state in workflow_states: 116 | if not frappe.db.exists("Workflow State", state["name"]): 117 | ws = frappe.new_doc("Workflow State") 118 | ws.update(state) 119 | ws.insert() 120 | 121 | workflow_data = { 122 | "docstatus": 0, 123 | "doctype": "Workflow", 124 | "document_type": "Payment Entry", 125 | "is_active": 0, 126 | "name": "Void Payment Entry", 127 | "override_status": 0, 128 | "send_email_alert": 0, 129 | "states": [ 130 | { 131 | "allow_edit": "Accounts User", 132 | "doc_status": "0", 133 | "is_optional_state": 0, 134 | "parent": "Payment Entry", 135 | "parentfield": "states", 136 | "parenttype": "Workflow", 137 | "state": "Draft", 138 | }, 139 | { 140 | "allow_edit": "Accounts User", 141 | "doc_status": "1", 142 | "is_optional_state": 0, 143 | "parent": "Payment Entry", 144 | "parentfield": "states", 145 | "parenttype": "Workflow", 146 | "state": "Submitted", 147 | }, 148 | { 149 | "allow_edit": "Accounts User", 150 | "doc_status": "2", 151 | "is_optional_state": 0, 152 | "parent": "Payment Entry", 153 | "parentfield": "states", 154 | "parenttype": "Workflow", 155 | "state": "Cancelled", 156 | }, 157 | { 158 | "allow_edit": "Accounts User", 159 | "doc_status": "2", 160 | "is_optional_state": 0, 161 | "parent": "Payment Entry", 162 | "parentfield": "states", 163 | "parenttype": "Workflow", 164 | "state": "Voided", 165 | "update_field": "status", 166 | "update_value": "Voided", 167 | }, 168 | ], 169 | "transitions": [ 170 | { 171 | "action": "Save", 172 | "allow_self_approval": 1, 173 | "allowed": "Accounts User", 174 | "next_state": "Draft", 175 | "parent": "Payment Entry", 176 | "parentfield": "transitions", 177 | "parenttype": "Workflow", 178 | "state": "Draft", 179 | }, 180 | { 181 | "action": "Submit", 182 | "allow_self_approval": 1, 183 | "allowed": "Accounts User", 184 | "next_state": "Submitted", 185 | "parent": "Payment Entry", 186 | "parentfield": "transitions", 187 | "parenttype": "Workflow", 188 | "state": "Draft", 189 | }, 190 | { 191 | "action": "Cancel", 192 | "allow_self_approval": 1, 193 | "allowed": "Accounts User", 194 | "next_state": "Cancelled", 195 | "parent": "Payment Entry", 196 | "parentfield": "transitions", 197 | "parenttype": "Workflow", 198 | "state": "Submitted", 199 | }, 200 | { 201 | "action": "Void", 202 | "allow_self_approval": 1, 203 | "allowed": "Accounts User", 204 | "next_state": "Voided", 205 | "parent": "Payment Entry", 206 | "parentfield": "transitions", 207 | "parenttype": "Workflow", 208 | "state": "Submitted", 209 | }, 210 | ], 211 | "workflow_name": "Voidable Payment Entry", 212 | "workflow_state_field": "status", 213 | } 214 | if not frappe.db.exists("Workflow", workflow_data["workflow_name"]): 215 | workflow = frappe.new_doc("Workflow") 216 | workflow.update(workflow_data) 217 | workflow.insert() 218 | 219 | 220 | def after_install(): 221 | if not frappe.db.exists("File", "Home/Check Run"): 222 | try: 223 | cr_folder = frappe.new_doc("File") 224 | cr_folder.update({"file_name": "Check Run", "is_folder": True, "folder": "Home"}) 225 | cr_folder.save() 226 | except Exception as e: 227 | pass 228 | 229 | add_workflow_for_voided_check() 230 | -------------------------------------------------------------------------------- /check_run/hooks.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025, AgriTheory and contributors 2 | # For license information, please see license.txt 3 | 4 | from . import __version__ as app_version # noqa: F401 5 | 6 | app_name = "check_run" 7 | app_title = "Check Run" 8 | app_publisher = "AgriTheory" 9 | app_description = "Payables Utilities for ERPNext" 10 | app_icon = "octicon octicon-file-directory" 11 | app_color = "grey" 12 | app_email = "support@agritheory.dev" 13 | app_license = "MIT" 14 | required_apps = ["frappe/erpnext", "frappe/hrms"] 15 | 16 | docs_languages = ["es"] 17 | 18 | # Includes in 19 | # ------------------ 20 | 21 | # include js, css files in header of desk.html 22 | app_include_css = ["/assets/check_run/dist/js/style.css", "/assets/check_run/css/file_preview.css"] 23 | app_include_js = [ 24 | "check_run.bundle.js", 25 | "/assets/check_run/dist/js/check_run.js", 26 | ] 27 | 28 | # include js, css files in header of web template 29 | # web_include_css = "/assets/check_run/css/check_run.css" 30 | # web_include_js = "/assets/check_run/js/check_run.js" 31 | 32 | # include custom scss in every website theme (without file extension ".scss") 33 | # website_theme_scss = "check_run/public/scss/website" 34 | 35 | # include js, css files in header of web form 36 | # webform_include_js = {"doctype": "public/js/doctype.js"} 37 | # webform_include_css = {"doctype": "public/css/doctype.css"} 38 | 39 | # include js in page 40 | # page_js = {"page" : "public/js/file.js"} 41 | 42 | # include js in doctype views 43 | doctype_js = { 44 | "Employee": "public/js/custom/employee_custom.js", 45 | "Payment Entry": "public/js/custom/payment_entry_custom.js", 46 | "Supplier": "public/js/custom/supplier_custom.js", 47 | } 48 | 49 | # doctype_list_js = {"doctype" : "public/js/doctype_list.js"} 50 | # doctype_tree_js = {"doctype" : "public/js/doctype_tree.js"} 51 | # doctype_calendar_js = {"doctype" : "public/js/doctype_calendar.js"} 52 | 53 | # Home Pages 54 | # ---------- 55 | 56 | # application home page (will override Website Settings) 57 | # home_page = "login" 58 | 59 | # website user home page (by Role) 60 | # role_home_page = { 61 | # "Role": "home_page" 62 | # } 63 | 64 | # Generators 65 | # ---------- 66 | 67 | # automatically create page for each record of this doctype 68 | # website_generators = ["Web Page"] 69 | 70 | # Installation 71 | # ------------ 72 | 73 | # before_install = "check_run.install.before_install" 74 | after_install = "check_run.customize.after_install" 75 | after_migrate = "check_run.customize.load_customizations" 76 | 77 | # Uninstallation 78 | # ------------ 79 | 80 | # before_uninstall = "check_run.uninstall.before_uninstall" 81 | # after_uninstall = "check_run.uninstall.after_uninstall" 82 | 83 | # Desk Notifications 84 | # ------------------ 85 | # See frappe.core.notifications.get_notification_config 86 | 87 | # notification_config = "check_run.notifications.get_notification_config" 88 | 89 | # Permissions 90 | # ----------- 91 | # Permissions evaluated in scripted ways 92 | 93 | # permission_query_conditions = { 94 | # "Event": "frappe.desk.doctype.event.event.get_permission_query_conditions", 95 | # } 96 | # 97 | # has_permission = { 98 | # "Event": "frappe.desk.doctype.event.event.has_permission", 99 | # } 100 | 101 | # DocType Class 102 | # --------------- 103 | # Override standard doctype classes 104 | override_doctype_class = { 105 | # "Bank": "check_run.overrides.bank.CustomBank", 106 | "Payment Entry": "check_run.overrides.payment_entry.CheckRunPaymentEntry", 107 | } 108 | 109 | # Document Events 110 | # --------------- 111 | # Hook on document methods and events 112 | 113 | doc_events = { 114 | "Bank": {"validate": ["check_run.overrides.bank.validate"]}, 115 | "Expense Claim": {"before_cancel": ["check_run.check_run.disallow_cancellation_if_in_check_run"]}, 116 | "Journal Entry": {"before_cancel": ["check_run.check_run.disallow_cancellation_if_in_check_run"]}, 117 | "Payment Entry": { 118 | "validate": [ 119 | "check_run.overrides.payment_entry.validate_duplicate_check_number", 120 | ], 121 | "on_submit": [ 122 | "check_run.overrides.payment_entry.update_outstanding_amount", 123 | "check_run.overrides.payment_entry.update_check_number", 124 | ], 125 | "on_cancel": [ 126 | "check_run.overrides.payment_entry.update_outstanding_amount", 127 | ], 128 | }, 129 | "Purchase Invoice": { 130 | "before_cancel": ["check_run.check_run.disallow_cancellation_if_in_check_run"] 131 | }, 132 | } 133 | 134 | # Scheduled Tasks 135 | # --------------- 136 | 137 | # scheduler_events = { 138 | # "all": [ 139 | # "check_run.tasks.all" 140 | # ], 141 | # "daily": [ 142 | # "check_run.tasks.daily" 143 | # ], 144 | # "hourly": [ 145 | # "check_run.tasks.hourly" 146 | # ], 147 | # "weekly": [ 148 | # "check_run.tasks.weekly" 149 | # ] 150 | # "monthly": [ 151 | # "check_run.tasks.monthly" 152 | # ] 153 | # } 154 | 155 | # Testing 156 | # ------- 157 | 158 | # before_tests = "check_run.check_run.doctype.check_run.test_check_run.before_tests" 159 | 160 | # Overriding Methods 161 | # ------------------------------ 162 | # 163 | # override_whitelisted_methods = { 164 | # "frappe.desk.doctype.event.event.get_events": "check_run.event.get_events" 165 | # } 166 | # 167 | # each overriding function accepts a `data` argument; 168 | # generated from the base implementation of the doctype dashboard, 169 | # along with any modifications made in other Frappe apps 170 | # override_doctype_dashboards = { 171 | # "Task": "check_run.task.get_dashboard_data" 172 | # } 173 | 174 | # exempt linked doctypes from being automatically cancelled 175 | # 176 | # auto_cancel_exempted_doctypes = ["Auto Repeat"] 177 | 178 | 179 | # User Data Protection 180 | # -------------------- 181 | # 182 | 183 | # Authentication and authorization 184 | # -------------------------------- 185 | 186 | # auth_hooks = [ 187 | # "check_run.auth.validate" 188 | # ] 189 | 190 | # Translation 191 | # -------------------------------- 192 | 193 | # Make link fields search translated document names for these DocTypes 194 | # Recommended only for DocTypes which have limited documents with untranslated names 195 | # For example: Role, Gender, etc. 196 | # translated_search_doctypes = [] 197 | 198 | jinja = { 199 | "methods": [ 200 | "frappe.contacts.doctype.address.address.get_default_address", 201 | "check_run.overrides.payment_entry.get_image_base64_data", 202 | ] 203 | } 204 | -------------------------------------------------------------------------------- /check_run/modules.txt: -------------------------------------------------------------------------------- 1 | Check Run -------------------------------------------------------------------------------- /check_run/overrides/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agritheory/check_run/368e6ba124bb12cf52ba11deb4e8829f6d44e52b/check_run/overrides/__init__.py -------------------------------------------------------------------------------- /check_run/overrides/bank.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023, AgriTheory and contributors 2 | # For license information, please see license.txt 3 | 4 | import frappe 5 | from erpnext.accounts.doctype.bank_account.bank_account import BankAccount 6 | 7 | 8 | @frappe.whitelist() 9 | def validate(doc: BankAccount, method: str | None = None): 10 | # Canadian banking institutions limit DFI Routing Numbers to 8 characters 11 | addresses = frappe.qb.DocType("Address") 12 | dls = frappe.qb.DocType("Dynamic Link") 13 | 14 | countries_qb = ( 15 | frappe.qb.from_(addresses) 16 | .inner_join(dls) 17 | .on(addresses.name == dls.parent) 18 | .select(addresses.country) 19 | .where(dls.parenttype == "Address") 20 | .where(dls.link_doctype == "Bank") 21 | .where(dls.link_name == doc.name) 22 | ) 23 | 24 | countries = frappe.db.sql(countries_qb, as_dict=True, pluck="country") 25 | if "Canada" in countries: 26 | if len(doc.aba_number) > 8: 27 | frappe.throw( 28 | frappe._( 29 | "This Bank is linked to at least one Canadian address. Canadian banking institutions require the ABA Number must not exceed 8 characters." 30 | ) 31 | ) 32 | return 33 | -------------------------------------------------------------------------------- /check_run/patches.txt: -------------------------------------------------------------------------------- 1 | check_run.patches.patch_payment_schedule # 12/19/23 2 | check_run.patches.patch_voided_check_workflow # 01/05/24 -------------------------------------------------------------------------------- /check_run/patches/patch_payment_schedule.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | pts = frappe.db.sql( 6 | """ 7 | SELECT `tabPayment Schedule`.parent, `tabPayment Schedule`.name 8 | FROM `tabPayment Schedule`, `tabPurchase Invoice` 9 | WHERE `tabPayment Schedule`.outstanding > 0.0 10 | AND `tabPayment Schedule`.parent = `tabPurchase Invoice`.name 11 | AND `tabPurchase Invoice`.status = 'Paid' 12 | """, 13 | as_dict=True, 14 | ) 15 | 16 | for ps in pts: 17 | frappe.db.set_value("Payment Schedule", ps.name, "outstanding", 0.0) 18 | -------------------------------------------------------------------------------- /check_run/patches/patch_voided_check_workflow.py: -------------------------------------------------------------------------------- 1 | from check_run.customize import add_workflow_for_voided_check 2 | 3 | 4 | def execute(): 5 | add_workflow_for_voided_check() 6 | -------------------------------------------------------------------------------- /check_run/public/css/file_preview.css: -------------------------------------------------------------------------------- 1 | .fa-file-pdf-o { 2 | font-size: 120%; 3 | padding-left: 0.5ch; 4 | color: var(--brand-secondary); 5 | } 6 | #close-pdf-button { 7 | position: absolute; 8 | top: 10px; 9 | right: 10px; 10 | } 11 | #pdf-preview-wrapper { 12 | position: fixed; 13 | width: calc(1290px / 2); 14 | height: calc(100vh - 135px); 15 | top: 135px; 16 | right: calc((100vw - 1290px) / 2); 17 | display: none; 18 | } 19 | #pdf-preview-wrapper.pdf-preview-wrapper-fw { 20 | width: calc(90vw / 2); 21 | height: calc(100vh - 135px); 22 | right: calc(10vw / 2); 23 | } 24 | #pdf-preview-wrapper.pdf-preview-wrapper-fw #pdf-preview { 25 | width: calc(90vw / 2); 26 | } 27 | .page-body.show-pdf-preview #pdf-preview-wrapper { 28 | display: block; 29 | } 30 | #pdf-preview { 31 | position: absolute; 32 | top: 50px; 33 | left: 0; 34 | right: 0; 35 | bottom: 0; 36 | width: calc(1290px / 2); 37 | height: calc(100vh - 135px - 20px); 38 | } 39 | .page-body.show-pdf-preview .page-wrapper .page-content { 40 | width: calc(50% - 10px); 41 | } 42 | 43 | .portal-widget { 44 | position: absolute; 45 | top: 5px; 46 | right: 5px; 47 | bottom: 5px; 48 | left: 5px; 49 | border-radius: 5px; 50 | box-shadow: 51 | 0px 1px 2px rgba(25, 39, 52, 0.05), 52 | 0px 0px 4px rgba(25, 39, 52, 0.1); 53 | } 54 | a.portal-widget-col { 55 | position: relative; 56 | padding: 20px; 57 | text-decoration: none; 58 | } 59 | 60 | .widget-row { 61 | margin-top: 10px; 62 | margin-bottom: 20px; 63 | } 64 | -------------------------------------------------------------------------------- /check_run/public/js/check_run.bundle.js: -------------------------------------------------------------------------------- 1 | import './check_run/check_run_quick_entry.js' 2 | import './custom/file_preview.js' 3 | -------------------------------------------------------------------------------- /check_run/public/js/check_run/ModeOfPaymentSummary.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 86 | 91 | -------------------------------------------------------------------------------- /check_run/public/js/check_run/check_run.js: -------------------------------------------------------------------------------- 1 | import CheckRun from './CheckRun.vue' 2 | import { createApp, reactive, ref, unref } from 'vue' 3 | 4 | frappe.provide('check_run') 5 | 6 | check_run.transactions = reactive({}) 7 | check_run.selectedRow = ref(-1) 8 | check_run.modes_of_payment = ref([]) 9 | check_run.filters = reactive({ 10 | key: 'posting_date', 11 | posting_date: 1, 12 | mode_of_payment: 1, 13 | amount: 1, 14 | due_date: 1, 15 | party: '', 16 | }) 17 | 18 | check_run.get_entries = frm => { 19 | return new Promise(function (resolve, reject) { 20 | if (!frm) { 21 | resolve() 22 | } 23 | frappe.xcall('check_run.check_run.doctype.check_run.check_run.get_entries', { doc: frm.doc }).then(r => { 24 | r.transactions.forEach((row, index) => { 25 | check_run.transactions[row.name] = row 26 | check_run.transactions[row.name].idx = index 27 | }) 28 | check_run.modes_of_payment = r.modes_of_payment 29 | resolve() 30 | }) 31 | }) 32 | } 33 | 34 | check_run.mount = frm => { 35 | check_run.transactions = reactive({}) 36 | check_run.modes_of_payment = ref([]) 37 | check_run.filters = reactive({ 38 | key: 'posting_date', 39 | posting_date: 1, 40 | mode_of_payment: 1, 41 | amount: 1, 42 | due_date: 1, 43 | party: '', 44 | }) 45 | if (frm.$check_run != undefined && frm.$check_run._isVue) { 46 | return 47 | } 48 | $(frm.fields_dict['check_run_table'].wrapper).html($("
").get(0)) 49 | frm.$check_run = createApp(CheckRun) 50 | frm.$check_run.mount('#check-run-vue') 51 | frm.$check_run.provide('$check_run', check_run) 52 | } 53 | 54 | check_run.total = frm => { 55 | let _frm = unref(frm) 56 | let r = Object.values(check_run.transactions).reduce((partialSum, t) => { 57 | return t.pay ? partialSum + t.amount : partialSum 58 | }, 0) 59 | _frm.set_value('amount_check_run', r) 60 | return r 61 | } 62 | -------------------------------------------------------------------------------- /check_run/public/js/check_run/check_run_quick_entry.js: -------------------------------------------------------------------------------- 1 | frappe.provide('check_run') 2 | frappe.provide('frappe.ui.form') 3 | 4 | frappe.ui.form.CheckRunQuickEntryForm = class CheckRunQuickEntryForm extends frappe.ui.form.QuickEntryForm { 5 | constructor(doctype, after_insert, init_callback, doc, force) { 6 | super(doctype, after_insert, init_callback, doc, force) 7 | } 8 | render_dialog() { 9 | this.mandatory = this.get_fields() 10 | super.render_dialog() 11 | this.dialog.$wrapper.find('.btn-secondary').hide() 12 | this.dialog.fields_dict['bank_account'].get_query = () => { 13 | return { 14 | filters: { 15 | is_company_account: 1, 16 | company: this.dialog.get_field('company').value, 17 | }, 18 | } 19 | } 20 | this.dialog.fields_dict['pay_to_account'].get_query = () => { 21 | return { 22 | filters: { 23 | company: this.dialog.get_field('company').value, 24 | account_type: 'Payable', 25 | }, 26 | } 27 | } 28 | this.dialog.fields_dict['company'].df.onchange = () => { 29 | this.default_accounts() 30 | } 31 | this.default_accounts() 32 | } 33 | get_fields() { 34 | return [ 35 | { 36 | label: __('Company'), 37 | fieldname: 'company', 38 | fieldtype: 'Link', 39 | options: 'Company', 40 | reqd: 1, 41 | }, 42 | { 43 | label: __('Paid From (Bank Account'), 44 | fieldname: 'bank_account', 45 | fieldtype: 'Link', 46 | options: 'Bank Account', 47 | reqd: 1, 48 | }, 49 | { 50 | label: __('Payable Account'), 51 | fieldname: 'pay_to_account', 52 | fieldtype: 'Link', 53 | options: 'Account', 54 | reqd: 1, 55 | }, 56 | ] 57 | } 58 | default_accounts() { 59 | if (frappe.get_route() && frappe.get_route()[0] == 'Form') { 60 | this.dialog.fields_dict['company'] = cur_frm.doc.company 61 | this.dialog.fields_dict['pay_to_account'].set_value(cur_frm.doc.pay_to_account) 62 | this.dialog.fields_dict['bank_account'].set_value(cur_frm.doc.bank_account) 63 | } else { 64 | let company = this.dialog.fields_dict.company.get_value() 65 | frappe.db.get_value('Company', company, 'default_payable_account').then(r => { 66 | this.dialog.fields_dict['pay_to_account'].set_value(r.message.default_payable_account) 67 | }) 68 | frappe.db 69 | .get_value('Bank Account', { company: company, is_default: 1, is_company_account: 1 }, 'name') 70 | .then(r => { 71 | this.dialog.fields_dict['bank_account'].set_value(r.message.name) 72 | }) 73 | } 74 | } 75 | register_primary_action() { 76 | const me = this 77 | this.dialog.set_primary_action(__('Start Check Run'), () => { 78 | let values = me.dialog.get_values() 79 | frappe 80 | .xcall('check_run.check_run.doctype.check_run.check_run.check_for_draft_check_run', { 81 | company: values.company, 82 | bank_account: values.bank_account, 83 | payable_account: values.pay_to_account, 84 | }) 85 | .then(r => { 86 | frappe.set_route('Form', 'Check Run', r) 87 | }) 88 | }) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /check_run/public/js/custom/employee_custom.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025, AgriTheory and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on('Employee', { 5 | onload_post_render: frm => { 6 | frm.fields_dict.bank_account.$wrapper 7 | .find('#bank-account') 8 | .off() 9 | .on('click', () => { 10 | show_bank_account_number(frm) 11 | }) 12 | }, 13 | refresh: frm => { 14 | set_required_banking_fields(frm) 15 | }, 16 | mode_of_payment: frm => { 17 | set_required_banking_fields(frm) 18 | }, 19 | bank: frm => { 20 | frm.set_value('ach_prenote_date', '') 21 | frm.set_value('account_details_validated', '') 22 | frm.set_value('ach_last_used', '') 23 | }, 24 | bank_account: frm => { 25 | frm.set_value('ach_prenote_date', '') 26 | frm.set_value('account_details_validated', '') 27 | frm.set_value('ach_last_used', '') 28 | }, 29 | }) 30 | 31 | function show_bank_account_number(frm) { 32 | if (!frm.doc.bank || !frm.doc.bank_account) { 33 | let msg = `Banking Information has not been set up for this ${frm.doc.doctype}` 34 | frappe.msgprint(msg, 'Bank Information') 35 | } else { 36 | frappe 37 | .xcall('check_run.check_run.show_bank_account_number', { doctype: frm.doc.doctype, docname: frm.doc.name }) 38 | .then(r => { 39 | let msg = `Routing Number ${r.routing_number}
Account Number: ${r.account_number}` 40 | frappe.msgprint(msg, 'Bank Information') 41 | }) 42 | } 43 | } 44 | 45 | function set_required_banking_fields(frm) { 46 | frm.fields_dict.bank_account.disable_password_checks() 47 | if (!frm.doc.mode_of_payment) { 48 | return 49 | } 50 | frappe.db.get_value('Mode of Payment', frm.doc.mode_of_payment, 'type').then(r => { 51 | if (r.message.type == 'Electronic') { 52 | frm.set_df_property('bank', 'reqd', 1) 53 | frm.set_df_property('bank', 'hidden', 0) 54 | frm.set_df_property('bank_account', 'reqd', 1) 55 | frm.set_df_property('bank_account', 'hidden', 0) 56 | } else { 57 | frm.set_df_property('bank', 'reqd', 0) 58 | frm.set_df_property('bank', 'hidden', 1) 59 | frm.set_df_property('bank_account', 'reqd', 0) 60 | frm.set_df_property('bank_account', 'hidden', 1) 61 | } 62 | }) 63 | } 64 | -------------------------------------------------------------------------------- /check_run/public/js/custom/file_preview.js: -------------------------------------------------------------------------------- 1 | frappe.provide('frappe.ui.form') 2 | 3 | $(document).on('page-change', function (e) { 4 | frappe.ui.closeFilePreview() 5 | }) 6 | 7 | $(document).on('click', function (e) { 8 | if (!$(e.target).get(0).hasAttribute('data-pdf-preview')) { 9 | frappe.ui.closeFilePreview() 10 | } 11 | }) 12 | 13 | $(document).on('keydown', e => { 14 | if (e.which === frappe.ui.keyCode.ESCAPE) { 15 | frappe.ui.closeFilePreview() 16 | } 17 | if (e.which === frappe.ui.keyCode.SPACE && cur_frm && cur_frm.doctype == 'Check Run') { 18 | frappe.ui.closeFilePreview() 19 | } 20 | }) 21 | 22 | $('#close-pdf-button').on('click', event => { 23 | frappe.ui.closeFilePreview() 24 | }) 25 | 26 | frappe.ui.form.Attachments.prototype.add_attachment = attachment => { 27 | const me = frappe.ui.form.Attachments.prototype 28 | let file_name = attachment.file_name 29 | var file_url = me.get_file_url(attachment) 30 | var fileid = attachment.name 31 | if (!file_name) { 32 | file_name = file_url 33 | } 34 | let file_label = ` 35 | 36 | ${file_name} 37 | ` 38 | let remove_action = null 39 | if (frappe.model.can_write(cur_frm.doctype, cur_frm.name)) { 40 | remove_action = function (target_id) { 41 | frappe.confirm(__('Are you sure you want to delete the attachment?'), function () { 42 | cur_frm.attachments.remove_attachment(target_id) 43 | }) 44 | return false 45 | } 46 | } 47 | 48 | let icon = ` 49 | ${frappe.utils.icon(attachment.is_private ? 'lock' : 'unlock', 'sm ml-0')} 50 | ` 51 | 52 | if (file_name.toLowerCase().endsWith('.pdf')) { 53 | icon += `` 54 | } 55 | 56 | $(`
  • `) 57 | .append(frappe.get_data_pill(file_label, fileid, remove_action, icon)) 58 | .insertAfter(cur_frm.attachments.attachments_label.addClass('has-attachments')) 59 | 60 | $('.fa-file-pdf-o').on('click', event => { 61 | frappe.ui.pdfPreview(cur_frm, event.currentTarget.dataset.pdfPreview) 62 | }) 63 | 64 | if (file_name.toLowerCase().endsWith('.pdf')) { 65 | frappe.ui.addFilePreviewWrapper() 66 | } 67 | } 68 | 69 | frappe.ui.addFilePreviewWrapper = () => { 70 | $('#pdf-preview-wrapper').remove() 71 | frm = cur_frm 72 | let target_div = $(`[id='page-${frm.doctype}']`) 73 | if (target_div) { 74 | let page_body = target_div.find('.page-body') 75 | if (page_body) { 76 | let page_wrapper = page_body.find('.page-wrapper') 77 | if (page_wrapper) { 78 | $(page_wrapper).append(`
    79 | 80 |
    `) 81 | } 82 | } 83 | } 84 | } 85 | 86 | frappe.ui.closeFilePreview = () => { 87 | if ($('#pdf-preview').length != 0) { 88 | $('#pdf-preview').remove() 89 | $('.page-body').removeClass('show-pdf-preview') 90 | if (cur_frm !== null && cur_frm.doctype != 'Check Run') { 91 | cur_frm.page.wrapper.find('.layout-side-section').show() 92 | } 93 | } 94 | } 95 | 96 | frappe.ui.pdfPreview = (frm, file_url) => { 97 | if (frm !== null) { 98 | frm.page.wrapper.find('.layout-side-section').hide() 99 | } 100 | if (localStorage.container_fullwidth != 'false') { 101 | $('#pdf-preview-wrapper').addClass('pdf-preview-wrapper-fw') 102 | } else { 103 | $('#pdf-preview-wrapper').removeClass('pdf-preview-wrapper-fw') 104 | } 105 | $('#pdf-preview-wrapper').append(`