From 6f50230a8713c16f1818ba8450abc18cc2535322 Mon Sep 17 00:00:00 2001 From: Tyler Matteson Date: Tue, 28 Feb 2023 16:43:58 -0500 Subject: [PATCH] feat: port timeout fix to v14 (#62) * feat: port timeout fix to v14 * fix: indent * chore: prettier formatting * style: prettify code --------- Co-authored-by: agritheory --- .github/workflows/main.yaml | 48 +-- .prettierignore | 218 ++++++++++++ .prettierrc.js | 19 + .../check_run/doctype/check_run/check_run.js | 305 +++++++++------- .../check_run/doctype/check_run/check_run.py | 71 +++- .../doctype/check_run/check_run_list.js | 32 +- .../check_run_settings/check_run_settings.js | 3 +- .../report/positive_pay/positive_pay.js | 24 +- check_run/customize.py | 10 + check_run/hooks.py | 2 +- check_run/public/build.json | 6 - check_run/public/js/check_run.bundle.js | 4 +- check_run/public/js/check_run/ADropdown.vue | 329 +++++++++--------- check_run/public/js/check_run/CheckRun.vue | 207 ++++++----- check_run/public/js/check_run/check_run.js | 190 +++++----- .../js/check_run/check_run_quick_entry.js | 172 ++++----- check_run/public/js/custom/employee_custom.js | 93 ++--- check_run/public/js/custom/supplier_custom.js | 93 ++--- cypress/commands/index.js | 4 +- cypress/integration/check_run.spec.js | 92 ++--- cypress/plugins/index.js | 44 +-- cypress/support/commands.js | 310 ++++++++--------- 22 files changed, 1311 insertions(+), 965 deletions(-) create mode 100644 .prettierignore create mode 100644 .prettierrc.js delete mode 100644 check_run/public/build.json diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 86fa2081..ca0ac0e9 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -1,24 +1,40 @@ -name: Validate Diff Release +name: Linters on: push: - branches: [ version-13 ] + branches: [ version-13, version-14 ] pull_request: - branches: [ version-13 ] + branches: [ version-13, version-14 ] env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} jobs: - Diff: - needs: [ Validate_PY_JSON_MERGE ] + prettier: + needs: [ py_json_merge ] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - name: Checkout + uses: actions/checkout@v3 + with: + ref: ${{ github.head_ref }} + fetch-depth: 2 + + - name: Prettify code + uses: creyD/prettier_action@v4.3 + with: + commit_message: "style: prettify code" + + json_diff: + needs: [ py_json_merge ] + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 with: ref: ${{ github.ref }} fetch-depth: 2 - - name: Find changed json + - name: Find JSON changes id: changed-json uses: tj-actions/changed-files@v23.1 with: @@ -68,29 +84,20 @@ jobs: run: | pip install rich pip install json_source_map - git clone --depth 1 https://gist.github.com/74017707b676c3ce947528fb00dd46d0.git fsjd + git clone --depth 1 https://gist.github.com/3eea518743067f1b971114f1a2016f69 fsjd - name: Diff table run: python3 fsjd/frappe_schema_json_diff.py base/mrd.txt head/acmr.txt 1 - - Release: - needs: [ Validate_PY_JSON_MERGE ] - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Release - run: npx semantic-release - - Validate_PY_JSON_MERGE: + py_json_merge: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Get validate_json + - name: Fetch validator run: git clone --depth 1 https://gist.github.com/f1bf2c11f78331b2417189c385022c28.git validate_json - - name: Validate json + - name: Validate JSON run: python3 validate_json/validate_json.py ./ - name: Compile @@ -102,4 +109,3 @@ jobs: then echo "Found merge conflicts" exit 1 fi - \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..0a405717 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,218 @@ +#------------------------------------------------------------------------------------------------------------------- +# Keep this section in sync with .gitignore +#------------------------------------------------------------------------------------------------------------------- + +*.pyc +*.py~ +*.comp.js +*.DS_Store +locale +.wnf-lang-status +*.swp +*.egg-info +dist/ +# build/ +cloud_storage/docs/current +cloud_storage/public/dist +.vscode +.vs +node_modules +.kdev4/ +*.kdev4 +*debug.log + +# Not Recommended, but will remove once webpack ready +package-lock.json + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +# build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ +.cypress-coverage + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +.static_storage/ +.media/ +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# next.js build output +.next + +# cypress +cypress/screenshots +cypress/videos + +# JetBrains IDEs +.idea/ + +#------------------------------------------------------------------------------------------------------------------- +# Prettier-specific overrides +#------------------------------------------------------------------------------------------------------------------- + + +# Package manager files +pnpm-lock.yaml +yarn.lock +package-lock.json +shrinkwrap.json + +# Build outputs +lib +.github + +# Prettier reformats code blocks inside Markdown, which affects rendered output +*.md diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 00000000..13cabb39 --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,19 @@ +module.exports = { + arrowParens: 'avoid', + bracketSameLine: true, + bracketSpacing: true, + embeddedLanguageFormatting: 'auto', + htmlWhitespaceSensitivity: 'css', + insertPragma: false, + jsxSingleQuote: false, + printWidth: 120, + proseWrap: 'preserve', + quoteProps: 'as-needed', + requirePragma: false, + semi: false, + singleQuote: true, + tabWidth: 2, + trailingComma: 'es5', + useTabs: true, + vueIndentScriptAndStyle: false, +} diff --git a/check_run/check_run/doctype/check_run/check_run.js b/check_run/check_run/doctype/check_run/check_run.js index 26376597..64992603 100644 --- a/check_run/check_run/doctype/check_run/check_run.js +++ b/check_run/check_run/doctype/check_run/check_run.js @@ -1,16 +1,18 @@ // Copyright (c) 2022, AgriTheory and contributors // For license information, please see license.txt -frappe.ui.form.on("Check Run", { +frappe.ui.form.on('Check Run', { validate: frm => { validate_mode_of_payment_mandatory(frm) - if(frm.check_run_state.party_filter.length > 0) { - frm.check_run_state.party_filter = "" + if (frm.check_run_state.party_filter.length > 0) { + frm.check_run_state.party_filter = '' frm.check_run_state.show_party_filter = false - return new Promise(function(resolve, reject) { + return new Promise(function (resolve, reject) { reject( - frappe.msgprint(__( + frappe.msgprint( + __( 'The document was not saved because a Party filter was present. The Party filter has now been cleared. Please review the document before saving.' - )) + ) + ) ) }) } @@ -23,56 +25,40 @@ frappe.ui.form.on("Check Run", { permit_first_user(frm) get_defaults(frm) set_queries(frm) - if(frm.is_new()){ + if (frm.is_new()) { get_balance(frm) } - frappe.call({ - method: "ach_only", - doc: frm.doc, - }).done(r => { - if (!r.message.ach_only) { - if (frm.doc.docstatus == 1) { - if (frm.doc.print_count > 0 && frm.doc.status != 'Ready to Print') { - frm.add_custom_button(__("Re-Print Checks"), () => { reprint_checks(frm) }) - } else if (frm.doc.print_count == 0 && frm.doc.status == 'Submitted') { - render_checks(frm) - } - } - if (frm.doc.status == 'Ready to Print') { - frm.add_custom_button(__("Download Checks"), () => { download_checks(frm) }) - } - } - if (!r.message.print_checks_only) { - if (frm.doc.docstatus == 1) { - frm.add_custom_button("Download NACHA File", () => { download_nacha(frm) }) - } - } - }) + ach_only(frm) get_entries(frm) confirm_print(frm) - if(frm.doc.docstatus > 0){ + if (frm.doc.docstatus > 0) { frm.set_df_property('initial_check_number', 'read_only', 1) frm.set_df_property('final_check_number', 'read_only', 1) } - if (frm.doc.docstatus < 1 && frm.doc.__onload && frm.doc.__onload.settings_missing){ - frappe.xcall('check_run.check_run.doctype.check_run.check_run.get_check_run_settings', {doc: frm.doc}) - .then(r => { - if(r == undefined){ - frappe.confirm( - __(`No settings found for ${frm.doc.bank_account} and ${frm.doc.pay_to_account}`), - () => { - frappe.xcall("check_run.check_run.doctype.check_run_settings.check_run_settings.create", - { company: frm.doc.company, bank_account: frm.doc.bank_account, pay_to_account: frm.doc.pay_to_account } - ).then(r => { - frappe.set_route("Form", "Check Run Settings", r) - }) - }, - () => {} - ) - } else { - frm.doc.__onload.settings_missing = false - } - }) + if (frm.doc.docstatus < 1 && frm.doc.__onload && frm.doc.__onload.settings_missing) { + frappe + .xcall('check_run.check_run.doctype.check_run.check_run.get_check_run_settings', { doc: frm.doc }) + .then(r => { + if (r == undefined) { + frappe.confirm( + __(`No settings found for ${frm.doc.bank_account} and ${frm.doc.pay_to_account}`), + () => { + frappe + .xcall('check_run.check_run.doctype.check_run_settings.check_run_settings.create', { + company: frm.doc.company, + bank_account: frm.doc.bank_account, + pay_to_account: frm.doc.pay_to_account, + }) + .then(r => { + frappe.set_route('Form', 'Check Run Settings', r) + }) + }, + () => {} + ) + } else { + frm.doc.__onload.settings_missing = false + } + }) } }, onload_post_render: frm => { @@ -86,8 +72,7 @@ frappe.ui.form.on("Check Run", { get_entries(frm) }, start_date: frm => { - frappe.xcall('check_run.check_run.doctype.check_run.check_run.get_balance',{ doc: frm.doc }) - .then(r => { + frappe.xcall('check_run.check_run.doctype.check_run.check_run.get_balance', { doc: frm.doc }).then(r => { frm.set_value('beg_balance', r) get_entries(frm) }) @@ -108,139 +93,156 @@ frappe.ui.form.on("Check Run", { }, bank_account: frm => { get_balance(frm) - } + }, }) -function get_balance(frm){ - frappe.xcall('check_run.check_run.doctype.check_run.check_run.get_balance', { doc: frm.doc }) - .then(r => { +function get_balance(frm) { + frappe.xcall('check_run.check_run.doctype.check_run.check_run.get_balance', { doc: frm.doc }).then(r => { frm.set_value('beg_balance', r) }) } -function set_queries(frm){ - frm.set_query("bank_account", function() { +function set_queries(frm) { + frm.set_query('bank_account', function () { return { - "filters": { - "company": frm.doc.company, - } + filters: { + company: frm.doc.company, + }, } }) - frm.set_query("pay_to_account", function() { + frm.set_query('pay_to_account', function () { return { - "filters": { - "account_type": "Payable", - "is_group": 0 - } + filters: { + account_type: 'Payable', + is_group: 0, + }, } }) } -function get_entries(frm){ - frappe.xcall('check_run.check_run.doctype.check_run.check_run.get_entries', { doc: frm.doc} - ).then((r) => { +function get_entries(frm) { + frappe.xcall('check_run.check_run.doctype.check_run.check_run.get_entries', { doc: frm.doc }).then(r => { frm.transactions = r.transactions frm.modes_of_payment = r.modes_of_payment check_run.mount_table(frm) - if (!frappe.user.has_role(["Accounts Manager"])) { + if (!frappe.user.has_role(['Accounts Manager'])) { frm.disable_form() frm.$check_run.css({ 'pointer-events': 'none' }) } }) } -function total_check_run(frm){ +function total_check_run(frm) { var total = 0 - for (const [index, row] of frm.check_run_state.transactions.entries()){ - if(row.pay) { + for (const [index, row] of frm.check_run_state.transactions.entries()) { + if (row.pay) { total += row.amount } } - frm.set_value("amount_check_run", Number(total)) + frm.set_value('amount_check_run', Number(total)) } -function get_defaults(frm){ - if(!frm.is_new()){ return } +function get_defaults(frm) { + if (!frm.is_new()) { + return + } frm.set_value('start_date', moment().startOf('week').format()) frm.set_value('end_date', moment().endOf('week').format()) } -function get_last_check_number(frm){ +function get_last_check_number(frm) { // TODO: refactor to xcall - if(frm.doc.__islocal && frm.doc.start_date){ - frappe.call({ - method: "set_last_check_number", - doc: frm.doc, - }).then((r) => { - frm.refresh_field("last_check") - frm.refresh_field("initial_check_number") - }) + if (frm.doc.__islocal && frm.doc.start_date) { + frappe + .call({ + method: 'set_last_check_number', + doc: frm.doc, + }) + .then(r => { + frm.refresh_field('last_check') + frm.refresh_field('initial_check_number') + }) } } -function permit_first_user(frm){ +function permit_first_user(frm) { let viewers = frm.get_docinfo()['viewers'] - if(!viewers){ + if (!viewers) { return - } else if (viewers.current.length == 1 && viewers.current.includes(frappe.session.user)){ + } else if (viewers.current.length == 1 && viewers.current.includes(frappe.session.user)) { frm.user_lock = frappe.session.user return - } else if(frappe.session.user == frm.user_lock) { + } else if (frappe.session.user == frm.user_lock) { return - } else if (frm.user_lock && frappe.session.user != frm.user_lock){ + } else if (frm.user_lock && frappe.session.user != frm.user_lock) { frm.disable_form() - frm.$check_run.css({'pointer-events': 'none'}) + frm.$check_run.css({ 'pointer-events': 'none' }) } } -function confirm_print(frm){ - if(frm.doc.status != 'Confirm Print') {return} +function confirm_print(frm) { + if (frm.doc.status != 'Confirm Print') { + return + } let d = new frappe.ui.Dialog({ - title: __("Confirm Print"), + title: __('Confirm Print'), fields: [ - { fieldname: 'ht', fieldtype: 'HTML', options: - ` + { + fieldname: 'ht', + fieldtype: 'HTML', + options: ` -

` +

`, }, { fieldname: 'reprint_check_number', fieldtype: 'Data', - label: __("New Initial Check Number"), - } + label: __('New Initial Check Number'), + }, ], minimizable: false, static: true, }) d.wrapper.find('#confirm-print').on('click', () => { - frappe.xcall("check_run.check_run.doctype.check_run.check_run.confirm_print", {docname: frm.doc.name}) - .then(() => { - d.hide() - frm.reload_doc() - }) + frappe + .xcall('check_run.check_run.doctype.check_run.check_run.confirm_print', { + docname: frm.doc.name, + }) + .then(() => { + d.hide() + frm.reload_doc() + }) }) d.wrapper.find('#reprint').on('click', () => { d.fields_dict.reprint_check_number.df.reqd = 1 let values = cur_dialog.get_values() - reprint_checks(frm, values.reprint_check_number || undefined) + render_checks(frm, values.reprint_check_number || undefined) + frm.doc.status = 'Submitted' + frm.page.set_indicator(__('Submitted'), 'blue') d.hide() }) d.show() } function reprint_checks(frm) { + frm.set_value('status', 'Submitted') let d = new frappe.ui.Dialog({ - title: __("Re-Print"), + title: __('Re-Print'), fields: [ { - fieldname: 'ht', fieldtype: 'HTML', options: - `

` + fieldname: 'ht', + fieldtype: 'HTML', + options: `

`, }, { fieldname: 'reprint_check_number', fieldtype: 'Data', - label: __("New Initial Check Number"), - } + label: __('New Initial Check Number'), + }, ], minimizable: false, static: true, @@ -250,23 +252,55 @@ function reprint_checks(frm) { let values = cur_dialog.get_values() render_checks(frm, values.reprint_check_number || undefined) d.hide() - window.setTimeout(() => { - frm.reload_doc() - }, 1000) + frm.reload_doc() + frm.set_value('status', 'Submitted') }) d.show() } +function ach_only(frm) { + frappe + .xcall('check_run.check_run.doctype.check_run.check_run.ach_only', { + docname: frm.doc.name, + }) + .then(r => { + if (!r.ach_only) { + if (frm.doc.docstatus == 1) { + if (frm.doc.print_count > 0 && frm.doc.status != 'Ready to Print') { + frm.add_custom_button(__('Re-Print Checks'), () => { + reprint_checks(frm) + }) + } else if (frm.doc.print_count == 0 && frm.doc.status == 'Submitted') { + render_checks(frm) + } + } + if (frm.doc.status == 'Ready to Print') { + frm.add_custom_button(__('Download Checks'), () => { + download_checks(frm) + }) + } + } + if (!r.print_checks_only) { + if (frm.doc.docstatus == 1) { + frm.add_custom_button(__('Download NACHA File'), () => { + download_nacha(frm) + }) + } + } + }) +} -function validate_mode_of_payment_mandatory(frm){ +function validate_mode_of_payment_mandatory(frm) { let mode_of_payment_required = [] - for(const index in frm.check_run_state.transactions){ + for (const index in frm.check_run_state.transactions) { let row = frm.check_run_state.transactions[index] - if (row.pay && row.mode_of_payment.length < 2){ + if (row.pay && row.mode_of_payment.length < 2) { mode_of_payment_required.push({ row: index + 1, party: row.party, ref_name: row.ref_number || row.name }) } } - if (mode_of_payment_required.length == 0){ return } + if (mode_of_payment_required.length == 0) { + return + } let message = '' for (const index in mode_of_payment_required) { let row = mode_of_payment_required[index] @@ -281,21 +315,25 @@ function validate_mode_of_payment_mandatory(frm){ } function render_checks(frm, reprint_check_number = undefined) { - frappe.call({ - method: "increment_print_count", - doc: frm.doc, - args: { reprint_check_number: reprint_check_number } - }).done(() => { - frm.reload_doc() - frm.add_custom_button("Re-Print Checks", () => { reprint_checks(frm) }) - }).fail((r) => { - frm.reload_doc() - }) + frappe + .call({ + method: 'increment_print_count', + doc: frm.doc, + args: { reprint_check_number: reprint_check_number }, + }) + .done(() => { + frm.reload_doc() + frm.add_custom_button('Re-Print Checks', () => { + reprint_checks(frm) + }) + }) + .fail(r => { + frm.reload_doc() + }) } function download_checks(frm) { - frappe.xcall("check_run.check_run.doctype.check_run.check_run.download_checks", { docname: frm.doc.name }) - .then(r => { + frappe.xcall('check_run.check_run.doctype.check_run.check_run.download_checks', { docname: frm.doc.name }).then(r => { if (r) { frm.reload_doc() window.open(r) @@ -310,11 +348,10 @@ function download_nacha(frm) { }, 1000) } -function settings_button(frm){ - frm.add_custom_button("Modify Settings", () => { - frappe.xcall("check_run.check_run.doctype.check_run.check_run.get_check_run_settings", { doc: frm.doc } - ).then(r => { - frappe.set_route("Form", "Check Run Settings", r.name) +function settings_button(frm) { + frm.add_custom_button('Modify Settings', () => { + frappe.xcall('check_run.check_run.doctype.check_run.check_run.get_check_run_settings', { doc: frm.doc }).then(r => { + frappe.set_route('Form', 'Check Run Settings', r.name) }) }) -} \ No newline at end of file +} diff --git a/check_run/check_run/doctype/check_run/check_run.py b/check_run/check_run/doctype/check_run/check_run.py index 6870d739..9f9f5b82 100644 --- a/check_run/check_run/doctype/check_run/check_run.py +++ b/check_run/check_run/doctype/check_run/check_run.py @@ -6,6 +6,7 @@ import json from itertools import groupby, zip_longest from io import StringIO +import types from PyPDF2 import PdfFileWriter @@ -120,22 +121,38 @@ def validate_last_check_number(self, check_number=None): @frappe.whitelist() def before_submit(self): - self.payment_entries = [] - self.dt_pes = [] transactions = self.transactions transactions = json.loads(transactions) if len(transactions) < 1: frappe.throw("You must select at least one Invoice to pay.") - transactions = sorted([frappe._dict(item) for item in transactions if item.get("pay")], key=lambda x: x.party) - _transactions = self.create_payment_entries(transactions) self.print_count = 0 - self.transactions = json.dumps(_transactions) if self.ach_only().ach_only: self.initial_check_number = "" self.final_check_number = "" + + if len(transactions) < 25: + self._before_submit() else: - frappe.db.set_value('Bank Account', self.bank_account, 'check_number', self.final_check_number) - return self + self.status = 'Submitting' + frappe.enqueue_doc(self.doctype, self.name, "_before_submit", save=True, queue="short", timeout=3600) + + def _before_submit(self, save=False): + try: + transactions = self.transactions + transactions = json.loads(transactions) + transactions = sorted([frappe._dict(item) for item in transactions if item.get("pay")], key=lambda x: x.party) + _transactions = self.create_payment_entries(transactions) + + self.db_set('transactions', json.dumps(_transactions)) + self.db_set('status', 'Submitted') + if self.final_check_number: + frappe.db.set_value('Bank Account', self.bank_account, 'check_number', self.final_check_number) + frappe.db.commit() + js = "console.log('_before_submit'); setTimeout(function(){cur_frm.reload_doc()}, 500)" + frappe.emit_js(js, user=self.owner) + frappe.emit_js(js, user=frappe.session.user) + except Exception as e: + frappe.log_error(title=f"{self.name} Check Run Error", message=e) def build_nacha_file(self, settings=None): electronic_mop = frappe.get_all('Mode of Payment', {'type': 'Electronic', 'enabled': 1}, 'name', pluck="name") @@ -243,17 +260,22 @@ def create_payment_entries(self, transactions): @frappe.whitelist() def increment_print_count(self, reprint_check_number=None): - self.print_count = self.print_count + 1 - self.set_status('Submitted') - self.save() + self.load_from_db() frappe.enqueue_doc(self.doctype, self.name, 'render_check_pdf', reprint_check_number=reprint_check_number, queue='short', now=True) - return @frappe.whitelist() def render_check_pdf(self, reprint_check_number=None): + self.load_from_db() + self.print_count = self.print_count + 1 + self.set_status('Submitted') if not frappe.db.exists('File', 'Home/Check Run'): - frappe.new_doc("File").update({"file_name":"Check Run", "is_folder": True, "folder":"Home"}).save() + try: + cr_folder = frappe.new_doc("File") + cr_folder.update({"file_name":"Check Run", "is_folder": True, "folder":"Home"}) + cr_folder.save() + except Exception as e: + pass settings = get_check_run_settings(self) initial_check_number = int(self.initial_check_number) if reprint_check_number and reprint_check_number != 'undefined': @@ -264,7 +286,7 @@ def render_check_pdf(self, reprint_check_number=None): _transactions = [] for pe, group in groupby(transactions, key=lambda x: x.get('payment_entry')): group = list(group) - mode_of_payment, docstatus = frappe.db.get_value('Payment Entry', pe, ['mode_of_payment', 'docstatus']) + mode_of_payment, docstatus = frappe.db.get_value('Payment Entry', pe, ['mode_of_payment', 'docstatus']) or (None, None) if docstatus == 1 and frappe.db.get_value('Mode of Payment', mode_of_payment, 'type') == 'Bank': output = frappe.get_print( 'Payment Entry', @@ -285,13 +307,16 @@ def render_check_pdf(self, reprint_check_number=None): _transactions.append(ref) if _transactions and reprint_check_number: - frappe.db.set_value('Check Run', self.name, 'transactions', json.dumps(_transactions)) - frappe.db.set_value('Check Run', self.name, 'initial_check_number', self.initial_check_number) - frappe.db.set_value('Check Run', self.name, 'final_check_number', self.initial_check_number + check_increment -1) - frappe.db.set_value('Bank Account', self.bank_account, 'check_number', self.final_check_number) - - frappe.db.set_value('Check Run', self.name, 'status', 'Ready to Print') + self.db_set('transactions', json.dumps(_transactions)) + self.db_set('final_check_number', self.initial_check_number + (check_increment - 1)) + self.db_set('status', 'Ready to Print') + self.db_set('print_count', self.print_count) + frappe.db.set_value('Bank Account', self.bank_account, 'check_number', self.final_check_number) save_file(f"{self.name}.pdf", read_multi_pdf(output), 'Check Run', self.name, 'Home/Check Run', False, 0) + frappe.db.commit() + js = "setTimeout(function(){cur_frm.reload_doc()}, 500)" + frappe.emit_js(js, user=self.owner) + frappe.emit_js(js, user=frappe.session.user) @frappe.whitelist() @@ -600,3 +625,11 @@ def get_address(party, party_type, doctype, name): return get_default_address('Supplier', party) elif party_type == 'Employee': return frappe.get_value('Employee', name, 'permanent_address') + + +@frappe.whitelist() +def ach_only(docname): + if not frappe.db.exists('Check Run', docname): + return {'ach_only': False, 'checks_only': False} + cr = frappe.get_doc('Check Run', docname) + return cr.ach_only() \ No newline at end of file diff --git a/check_run/check_run/doctype/check_run/check_run_list.js b/check_run/check_run/doctype/check_run/check_run_list.js index 91866d95..01173943 100644 --- a/check_run/check_run/doctype/check_run/check_run_list.js +++ b/check_run/check_run/doctype/check_run/check_run_list.js @@ -1,14 +1,18 @@ -frappe.listview_settings['Check Run'] = { - add_fields: ["status"], - hide_name_column: true, - has_indicator_for_draft: 1, - get_indicator: doc => { - return [__(doc.status), { - "Draft": "red", - "Submitted": "blue", - "Ready to Print": "purple", - "Confirm Print": "yellow", - "Printed": "green", - }[doc.status], "status,=," + doc.status] - }, -} \ No newline at end of file +frappe.listview_settings['Check Run'] = { + add_fields: ['status'], + hide_name_column: true, + has_indicator_for_draft: 1, + get_indicator: doc => { + return [ + __(doc.status), + { + Draft: 'red', + Submitted: 'blue', + 'Ready to Print': 'purple', + 'Confirm Print': 'yellow', + Printed: 'green', + }[doc.status], + 'status,=,' + doc.status, + ] + }, +} diff --git a/check_run/check_run/doctype/check_run_settings/check_run_settings.js b/check_run/check_run/doctype/check_run_settings/check_run_settings.js index 37018044..6d32d395 100644 --- a/check_run/check_run/doctype/check_run_settings/check_run_settings.js +++ b/check_run/check_run/doctype/check_run_settings/check_run_settings.js @@ -3,6 +3,5 @@ frappe.ui.form.on('Check Run Settings', { // refresh: function(frm) { - // } -}); +}) diff --git a/check_run/check_run/report/positive_pay/positive_pay.js b/check_run/check_run/report/positive_pay/positive_pay.js index 97856a22..d56e88f6 100644 --- a/check_run/check_run/report/positive_pay/positive_pay.js +++ b/check_run/check_run/report/positive_pay/positive_pay.js @@ -2,28 +2,28 @@ // For license information, please see license.txt /* eslint-disable */ -frappe.query_reports["Positive Pay"] = { - "filters": [ +frappe.query_reports['Positive Pay'] = { + filters: [ { - fieldname: "bank_account", - label: __("Bank Account"), - fieldtype: "Link", + fieldname: 'bank_account', + label: __('Bank Account'), + fieldtype: 'Link', options: 'Bank Account', reqd: 1, }, { - fieldname: "start_date", - label: __("Start Date"), - fieldtype: "Date", + fieldname: 'start_date', + label: __('Start Date'), + fieldtype: 'Date', default: moment().date(0).startOf('month').format(), reqd: 1, }, { - fieldname: "end_date", - label: __("End Date"), - fieldtype: "Date", + fieldname: 'end_date', + label: __('End Date'), + fieldtype: 'Date', default: moment().date(0).format(), reqd: 1, }, - ] + ], } diff --git a/check_run/customize.py b/check_run/customize.py index b9f172dd..2d7e47d9 100644 --- a/check_run/customize.py +++ b/check_run/customize.py @@ -31,3 +31,13 @@ def load_customizations(): }) property_setter.flags.ignore_permissions = True property_setter.insert() + + +def after_install(): + if not frappe.db.exists('File', 'Home/Check Run'): + try: + cr_folder = frappe.new_doc("File") + cr_folder.update({"file_name":"Check Run", "is_folder": True, "folder":"Home"}) + cr_folder.save() + except Exception as e: + pass \ No newline at end of file diff --git a/check_run/hooks.py b/check_run/hooks.py index c2daad7d..8673e70f 100644 --- a/check_run/hooks.py +++ b/check_run/hooks.py @@ -63,7 +63,7 @@ # ------------ # before_install = "check_run.install.before_install" -# after_install = "check_run.install.after_install" +after_install = "check_run.customize.after_install" after_migrate = 'check_run.customize.load_customizations' # Uninstallation diff --git a/check_run/public/build.json b/check_run/public/build.json deleted file mode 100644 index 3bff0827..00000000 --- a/check_run/public/build.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "js/check_run.min.js": [ - "public/js/check_run/check_run.js", - "public/js/check_run/check_run_quick_entry.js" - ] -} \ No newline at end of file diff --git a/check_run/public/js/check_run.bundle.js b/check_run/public/js/check_run.bundle.js index 71c90e21..f81c5c24 100644 --- a/check_run/public/js/check_run.bundle.js +++ b/check_run/public/js/check_run.bundle.js @@ -1,2 +1,2 @@ -import './check_run/check_run_quick_entry.js' -import './check_run/check_run.js' \ No newline at end of file +import './check_run/check_run_quick_entry.js' +import './check_run/check_run.js' diff --git a/check_run/public/js/check_run/ADropdown.vue b/check_run/public/js/check_run/ADropdown.vue index 581f61cc..d0e98cce 100644 --- a/check_run/public/js/check_run/ADropdown.vue +++ b/check_run/public/js/check_run/ADropdown.vue @@ -1,185 +1,170 @@ diff --git a/check_run/public/js/check_run/CheckRun.vue b/check_run/public/js/check_run/CheckRun.vue index be310a07..771631ee 100644 --- a/check_run/public/js/check_run/CheckRun.vue +++ b/check_run/public/js/check_run/CheckRun.vue @@ -1,16 +1,14 @@ diff --git a/check_run/public/js/check_run/check_run.js b/check_run/public/js/check_run/check_run.js index eae72275..00a6f942 100644 --- a/check_run/public/js/check_run/check_run.js +++ b/check_run/public/js/check_run/check_run.js @@ -1,90 +1,100 @@ -import CheckRun from './CheckRun.vue' -import ADropdown from './ADropdown.vue' - -frappe.provide('check_run') - -check_run.mount_table = frm => { - check_run.frm = frm - frm.transactions.forEach(val => { - val.mopIsOpen = false - val.pay = val.pay ? val.pay : false - }) - frm.check_run_state = Vue.observable({ - transactions: frm.transactions, - party_filter: "", - docstatus: frm.doc.docstatus, - modes_of_payment: frm.modes_of_payment, - show_party_filter: false, - check_run_total: function() { - return this.transactions.reduce((partialSum, t) => { - return t.pay ? partialSum + t.amount : partialSum - }, 0); - }, - selectedRow: 0, - mopsOpen: 0 - }) - if (frm.$check_run instanceof Vue) { - frm.$check_run.$destroy() - } - $('#check-run-vue').remove() - $(frm.fields_dict['check_run_table'].wrapper).html($("
").get(0)); - frm.$check_run = new window.Vue({ - el: $("#check-run-vue").get(0), - render: h => h( - CheckRun, - { props: { - transactions: frm.check_run_state.transactions, //list of transtactions - modes_of_payment: frm.check_run_state.modes_of_payment, // populate modes_of_payment select. doesn't get updated - docstatus: frm.check_run_state.docstatus, // used to conditionally render column inputs based on submission status. doesn't get updated - state: frm.check_run_state - } - }) - }) - -} - -check_run.keyDownHandler = e => { - - if(!check_run.frm) { - return - } - - if(document.activeElement.tagName == "INPUT" || document.activeElement.tagName == "TEXTAREA") { - return - } - - if(e.keyCode == 40 && check_run.frm.check_run_state.selectedRow < (check_run.frm.check_run_state.transactions.length - 1)){ - for(let j=0;j 0){ - for(let j=0;j= 65 && e.keyCode <= 90 && check_run.frm.check_run_state.selectedRow != null && check_run.frm.check_run_state.transactions.length){ - check_run.frm.check_run_component.openMopWithSearch(e.keyCode) - } - -} - -window.removeEventListener('keydown', check_run.keyDownHandler) -window.addEventListener('keydown', check_run.keyDownHandler) +import CheckRun from './CheckRun.vue' +import ADropdown from './ADropdown.vue' + +frappe.provide('check_run') + +check_run.mount_table = frm => { + check_run.frm = frm + frm.transactions.forEach(val => { + val.mopIsOpen = false + val.pay = val.pay ? val.pay : false + }) + frm.check_run_state = Vue.observable({ + transactions: frm.transactions, + party_filter: '', + docstatus: frm.doc.docstatus, + modes_of_payment: frm.modes_of_payment, + show_party_filter: false, + check_run_total: function () { + return this.transactions.reduce((partialSum, t) => { + return t.pay ? partialSum + t.amount : partialSum + }, 0) + }, + selectedRow: 0, + mopsOpen: 0, + }) + if (frm.$check_run instanceof Vue) { + frm.$check_run.$destroy() + } + $('#check-run-vue').remove() + $(frm.fields_dict['check_run_table'].wrapper).html($("
").get(0)) + frm.$check_run = new window.Vue({ + el: $('#check-run-vue').get(0), + render: h => + h(CheckRun, { + props: { + transactions: frm.check_run_state.transactions, //list of transtactions + modes_of_payment: frm.check_run_state.modes_of_payment, // populate modes_of_payment select. doesn't get updated + docstatus: frm.check_run_state.docstatus, // used to conditionally render column inputs based on submission status. doesn't get updated + state: frm.check_run_state, + }, + }), + }) +} + +check_run.keyDownHandler = e => { + if (!check_run.frm) { + return + } + + if (document.activeElement.tagName == 'INPUT' || document.activeElement.tagName == 'TEXTAREA') { + return + } + + if ( + e.keyCode == 40 && + check_run.frm.check_run_state.selectedRow < check_run.frm.check_run_state.transactions.length - 1 + ) { + for (let j = 0; j < check_run.frm.check_run_state.transactions.length; j++) { + if (check_run.frm.check_run_state.transactions[j].mopIsOpen) { + return + } + } + document.getElementById(`mop-input-${check_run.frm.check_run_state.selectedRow}`).blur() + check_run.frm.check_run_state.selectedRow += 1 + } + + if (e.keyCode == 38 && check_run.frm.check_run_state.selectedRow > 0) { + for (let j = 0; j < check_run.frm.check_run_state.transactions.length; j++) { + if (check_run.frm.check_run_state.transactions[j].mopIsOpen) { + return + } + } + document.getElementById(`mop-input-${check_run.frm.check_run_state.selectedRow}`).blur() + check_run.frm.check_run_state.selectedRow -= 1 + } + + if ( + e.keyCode == 32 && + check_run.frm.check_run_state.selectedRow != null && + check_run.frm.check_run_state.transactions.length + ) { + e.preventDefault() + if (check_run.frm.check_run_component) { + check_run.frm.check_run_component.checkPay() + } + } + + if ( + e.keyCode && + e.keyCode >= 65 && + e.keyCode <= 90 && + check_run.frm.check_run_state.selectedRow != null && + check_run.frm.check_run_state.transactions.length + ) { + check_run.frm.check_run_component.openMopWithSearch(e.keyCode) + } +} + +window.removeEventListener('keydown', check_run.keyDownHandler) +window.addEventListener('keydown', check_run.keyDownHandler) diff --git a/check_run/public/js/check_run/check_run_quick_entry.js b/check_run/public/js/check_run/check_run_quick_entry.js index dbd256fe..88361a5c 100644 --- a/check_run/public/js/check_run/check_run_quick_entry.js +++ b/check_run/public/js/check_run/check_run_quick_entry.js @@ -1,84 +1,88 @@ -frappe.provide('frappe.ui.form'); - -frappe.ui.form.CheckRunQuickEntryForm = class CheckRunQuickEntryForm extends frappe.ui.form.QuickEntryForm { - constructor(doctype, after_insert, init_callback, doc, force) { - super(doctype, after_insert, init_callback, doc, force) - } - render_dialog() { - this.mandatory = this.get_fields() - super.render_dialog(); - this.dialog.$wrapper.find('.btn-secondary').hide() - this.dialog.fields_dict["bank_account"].get_query = () => { - return { - "filters": { - "company": this.dialog.get_field("company").value, - } - } - } - this.dialog.fields_dict["pay_to_account"].get_query = () => { - return { - "filters": { - "company": this.dialog.get_field("company").value, - 'account_type': 'Payable' - } - } - } - this.dialog.fields_dict["company"].df.onchange = () => { - this.default_accounts() - } - this.default_accounts() - } - get_fields() { - return [ - { - label: __("Company"), - fieldname: "company", - fieldtype: "Link", - options: "Company", - reqd: 1, - }, - { - label: __("Paid From (Bank Account"), - fieldname: "bank_account", - fieldtype: "Link", - options: "Bank Account", - reqd: 1 - }, - { - label: __("Payable Account"), - fieldname: "pay_to_account", - fieldtype: "Link", - options: "Account", - reqd: 1 - } - ] - } - default_accounts() { - if (frappe.get_route() && frappe.get_route()[0] == 'Form') { - this.dialog.fields_dict["company"] = cur_frm.doc.company - this.dialog.fields_dict["pay_to_account"].set_value(cur_frm.doc.pay_to_account) - this.dialog.fields_dict["bank_account"].set_value(cur_frm.doc.bank_account) - } else { - let company = this.dialog.fields_dict.company.get_value() - frappe.db.get_value('Company', company, 'default_payable_account') - .then(r => { - this.dialog.fields_dict["pay_to_account"].set_value(r.message.default_payable_account) - }) - frappe.db.get_value('Bank Account', { company: company, is_default: 1, is_company_account: 1 }, 'name') - .then(r => { - this.dialog.fields_dict["bank_account"].set_value(r.message.name) - }) - } - } - register_primary_action() { - const me = this - this.dialog.set_primary_action(__('Start Check Run'), () => { - let values = me.dialog.get_values() - frappe.xcall("check_run.check_run.doctype.check_run.check_run.check_for_draft_check_run", - { company: values.company, bank_account: values.bank_account, payable_account: values.pay_to_account } - ).then(r => { - frappe.set_route("Form", "Check Run", r) - }) - }) - } -} +frappe.provide('frappe.ui.form') + +frappe.ui.form.CheckRunQuickEntryForm = class CheckRunQuickEntryForm extends frappe.ui.form.QuickEntryForm { + constructor(doctype, after_insert, init_callback, doc, force) { + super(doctype, after_insert, init_callback, doc, force) + } + render_dialog() { + this.mandatory = this.get_fields() + super.render_dialog() + this.dialog.$wrapper.find('.btn-secondary').hide() + this.dialog.fields_dict['bank_account'].get_query = () => { + return { + filters: { + company: this.dialog.get_field('company').value, + }, + } + } + this.dialog.fields_dict['pay_to_account'].get_query = () => { + return { + filters: { + company: this.dialog.get_field('company').value, + account_type: 'Payable', + }, + } + } + this.dialog.fields_dict['company'].df.onchange = () => { + this.default_accounts() + } + this.default_accounts() + } + get_fields() { + return [ + { + label: __('Company'), + fieldname: 'company', + fieldtype: 'Link', + options: 'Company', + reqd: 1, + }, + { + label: __('Paid From (Bank Account'), + fieldname: 'bank_account', + fieldtype: 'Link', + options: 'Bank Account', + reqd: 1, + }, + { + label: __('Payable Account'), + fieldname: 'pay_to_account', + fieldtype: 'Link', + options: 'Account', + reqd: 1, + }, + ] + } + default_accounts() { + if (frappe.get_route() && frappe.get_route()[0] == 'Form') { + this.dialog.fields_dict['company'] = cur_frm.doc.company + this.dialog.fields_dict['pay_to_account'].set_value(cur_frm.doc.pay_to_account) + this.dialog.fields_dict['bank_account'].set_value(cur_frm.doc.bank_account) + } else { + let company = this.dialog.fields_dict.company.get_value() + frappe.db.get_value('Company', company, 'default_payable_account').then(r => { + this.dialog.fields_dict['pay_to_account'].set_value(r.message.default_payable_account) + }) + frappe.db + .get_value('Bank Account', { company: company, is_default: 1, is_company_account: 1 }, 'name') + .then(r => { + this.dialog.fields_dict['bank_account'].set_value(r.message.name) + }) + } + } + register_primary_action() { + const me = this + this.dialog.set_primary_action(__('Start Check Run'), () => { + let values = me.dialog.get_values() + frappe + .xcall('check_run.check_run.doctype.check_run.check_run.check_for_draft_check_run', { + company: values.company, + bank_account: values.bank_account, + payable_account: values.pay_to_account, + }) + .then(r => { + frappe.set_route('Form', 'Check Run', r) + }) + }) + } +} diff --git a/check_run/public/js/custom/employee_custom.js b/check_run/public/js/custom/employee_custom.js index 35475616..5a19feb3 100644 --- a/check_run/public/js/custom/employee_custom.js +++ b/check_run/public/js/custom/employee_custom.js @@ -1,44 +1,49 @@ -frappe.ui.form.on('Employee', { - onload_post_render: frm => { - frm.fields_dict.bank_account.$wrapper.find('#bank-account').off().on('click', () => { - show_bank_account_number(frm) - }) - }, - refresh: frm => { - set_required_banking_fields(frm) - }, - mode_of_payment: frm => { - set_required_banking_fields(frm) - } -}) - -function show_bank_account_number(frm) { - if (!frm.doc.bank || !frm.doc.bank_account) { - let msg = `Banking Information has not been set up for this ${frm.doc.doctype}` - frappe.msgprint(msg, "Bank Information") - } else { - frappe.xcall('check_run.check_run.show_bank_account_number', { doctype: frm.doc.doctype, docname: frm.doc.name }) - .then(r => { - let msg = `Routing Number ${r.routing_number}
Account Number: ${r.account_number}` - frappe.msgprint(msg, "Bank Information") - }) - } -} - -function set_required_banking_fields(frm){ - if(!frm.doc.mode_of_payment){ return } - frappe.db.get_value('Mode of Payment', frm.doc.mode_of_payment, 'type') - .then(r => { - if(r.message.type == 'Electronic'){ - frm.set_df_property('bank', 'reqd', 1) - frm.set_df_property('bank', 'hidden', 0) - frm.set_df_property('bank_account', 'reqd', 1) - frm.set_df_property('bank_account', 'hidden', 0) - } else { - frm.set_df_property('bank', 'reqd', 0) - frm.set_df_property('bank', 'hidden', 1) - frm.set_df_property('bank_account', 'reqd', 0) - frm.set_df_property('bank_account', 'hidden', 1) - } - }) -} +frappe.ui.form.on('Employee', { + onload_post_render: frm => { + frm.fields_dict.bank_account.$wrapper + .find('#bank-account') + .off() + .on('click', () => { + show_bank_account_number(frm) + }) + }, + refresh: frm => { + set_required_banking_fields(frm) + }, + mode_of_payment: frm => { + set_required_banking_fields(frm) + }, +}) + +function show_bank_account_number(frm) { + if (!frm.doc.bank || !frm.doc.bank_account) { + let msg = `Banking Information has not been set up for this ${frm.doc.doctype}` + frappe.msgprint(msg, 'Bank Information') + } else { + frappe + .xcall('check_run.check_run.show_bank_account_number', { doctype: frm.doc.doctype, docname: frm.doc.name }) + .then(r => { + let msg = `Routing Number ${r.routing_number}
Account Number: ${r.account_number}` + frappe.msgprint(msg, 'Bank Information') + }) + } +} + +function set_required_banking_fields(frm) { + if (!frm.doc.mode_of_payment) { + return + } + frappe.db.get_value('Mode of Payment', frm.doc.mode_of_payment, 'type').then(r => { + if (r.message.type == 'Electronic') { + frm.set_df_property('bank', 'reqd', 1) + frm.set_df_property('bank', 'hidden', 0) + frm.set_df_property('bank_account', 'reqd', 1) + frm.set_df_property('bank_account', 'hidden', 0) + } else { + frm.set_df_property('bank', 'reqd', 0) + frm.set_df_property('bank', 'hidden', 1) + frm.set_df_property('bank_account', 'reqd', 0) + frm.set_df_property('bank_account', 'hidden', 1) + } + }) +} diff --git a/check_run/public/js/custom/supplier_custom.js b/check_run/public/js/custom/supplier_custom.js index d763d47f..2d77cedc 100644 --- a/check_run/public/js/custom/supplier_custom.js +++ b/check_run/public/js/custom/supplier_custom.js @@ -1,44 +1,49 @@ -frappe.ui.form.on('Supplier', { - onload_post_render: frm => { - frm.fields_dict.bank_account.$wrapper.find('#bank-account').off().on('click', () => { - show_bank_account_number(frm) - }) - }, - refresh: frm => { - set_required_banking_fields(frm) - }, - supplier_default_mode_of_payment: frm => { - set_required_banking_fields(frm) - } - }) - -function show_bank_account_number(frm) { - if(!frm.doc.bank || !frm.doc.bank_account) { - let msg = `Banking Information has not been set up for this ${frm.doc.doctype}` - frappe.msgprint(msg, "Bank Information") -} else { - frappe.xcall('check_run.check_run.show_bank_account_number', { doctype: frm.doc.doctype, docname: frm.doc.name }) - .then(r => { - let msg = `Routing Number ${r.routing_number}
Account Number: ${r.account_number}` - frappe.msgprint(msg, "Bank Information") - }) -} -} - -function set_required_banking_fields(frm) { - if (!frm.doc.supplier_default_mode_of_payment) { return } - frappe.db.get_value('Mode of Payment', frm.doc.supplier_default_mode_of_payment, 'type') - .then(r => { - if (r.message.type == 'Electronic') { - frm.set_df_property('bank', 'reqd', 1) - frm.set_df_property('bank', 'hidden', 0) - frm.set_df_property('bank_account', 'reqd', 1) - frm.set_df_property('bank_account', 'hidden', 0) - } else { - frm.set_df_property('bank', 'reqd', 0) - frm.set_df_property('bank', 'hidden', 1) - frm.set_df_property('bank_account', 'reqd', 0) - frm.set_df_property('bank_account', 'hidden', 1) - } - }) -} +frappe.ui.form.on('Supplier', { + onload_post_render: frm => { + frm.fields_dict.bank_account.$wrapper + .find('#bank-account') + .off() + .on('click', () => { + show_bank_account_number(frm) + }) + }, + refresh: frm => { + set_required_banking_fields(frm) + }, + supplier_default_mode_of_payment: frm => { + set_required_banking_fields(frm) + }, +}) + +function show_bank_account_number(frm) { + if (!frm.doc.bank || !frm.doc.bank_account) { + let msg = `Banking Information has not been set up for this ${frm.doc.doctype}` + frappe.msgprint(msg, 'Bank Information') + } else { + frappe + .xcall('check_run.check_run.show_bank_account_number', { doctype: frm.doc.doctype, docname: frm.doc.name }) + .then(r => { + let msg = `Routing Number ${r.routing_number}
Account Number: ${r.account_number}` + frappe.msgprint(msg, 'Bank Information') + }) + } +} + +function set_required_banking_fields(frm) { + if (!frm.doc.supplier_default_mode_of_payment) { + return + } + frappe.db.get_value('Mode of Payment', frm.doc.supplier_default_mode_of_payment, 'type').then(r => { + if (r.message.type == 'Electronic') { + frm.set_df_property('bank', 'reqd', 1) + frm.set_df_property('bank', 'hidden', 0) + frm.set_df_property('bank_account', 'reqd', 1) + frm.set_df_property('bank_account', 'hidden', 0) + } else { + frm.set_df_property('bank', 'reqd', 0) + frm.set_df_property('bank', 'hidden', 1) + frm.set_df_property('bank_account', 'reqd', 0) + frm.set_df_property('bank_account', 'hidden', 1) + } + }) +} diff --git a/cypress/commands/index.js b/cypress/commands/index.js index 6710abfe..5789b213 100644 --- a/cypress/commands/index.js +++ b/cypress/commands/index.js @@ -1,2 +1,2 @@ -import './commands' -import '../../../frappe/cypress/support/commands' \ No newline at end of file +import './commands' +import '../../../frappe/cypress/support/commands' diff --git a/cypress/integration/check_run.spec.js b/cypress/integration/check_run.spec.js index 3e77b3fe..7469b257 100644 --- a/cypress/integration/check_run.spec.js +++ b/cypress/integration/check_run.spec.js @@ -1,46 +1,46 @@ -context('Check Run List', () => { - before(() => { - cy.visit('/login') - cy.login() - cy.go_to_list('Check Run') - }) - - it("Add Check Run", () => { - cy.get('.primary-action').contains('Add Check Run').should('be.visible').click() - cy.wait(1000) - cy.get_field('company').focus().should('be.visible') - cy.get_field('bank_account').focus().should('be.visible') - cy.get_field('pay_to_account').focus().should('be.visible') - cy.get('.btn-primary').contains('Start Check Run').should('be.visible').click() - }) - - it("Complete First Check Run", () => { - cy.visit('/app/check-run/ACC-CR-2022-00001') - cy.fill_field("end_date", "1/1").blur() - cy.fill_field("posting_date", "1/1").blur() - cy.get('[data-checkbox-index="0"]').click().wait(250) - cy.get('[data-fieldname="amount_check_run"]').get('.like-disabled-input').should('contain', '$ 1,800.00') - cy.get('body').type(' ').wait(250) - cy.get('[data-fieldname="amount_check_run"]').get('.like-disabled-input').should('contain', '$ 0.00').wait(250) - cy.get('#select-all').click() - cy.get('.primary-action').contains('Save').should('be.visible').click() - cy.get('.primary-action').contains('Submit').should('be.visible').click().wait(250) - cy.get_open_dialog().contains('Yes').should('be.visible').click() - cy.get('.indicator-pill').should('contain', 'Submitted').wait(250) - cy.get('.indicator-pill').should('contain', 'Ready to Print').wait(15000) - }) - - it("Complete Second Check Run", () => { - cy.get('body').type('{ctrl+b}').wait(500) - cy.fill_field("end_date", "1/1").blur() - cy.fill_field("posting_date", "1/1").blur() - cy.get_field("pay_to_account").type("{ctrl+home}{del}").blur().wait(100) - cy.fill_field("pay_to_account", "2120 - Payroll Payable - CFC").blur().wait(500) - cy.get('#select-all').click() - cy.get('.primary-action').contains('Save').should('be.visible').click() - cy.get('.primary-action').contains('Submit').should('be.visible').click().wait(250) - cy.get_open_dialog().contains('Yes').should('be.visible').click() - cy.get('.indicator-pill').should('contain', 'Submitted').wait(250) - cy.get('.indicator-pill').should('contain', 'Ready to Print').wait(7000) - }) -}) \ No newline at end of file +context('Check Run List', () => { + before(() => { + cy.visit('/login') + cy.login() + cy.go_to_list('Check Run') + }) + + it('Add Check Run', () => { + cy.get('.primary-action').contains('Add Check Run').should('be.visible').click() + cy.wait(1000) + cy.get_field('company').focus().should('be.visible') + cy.get_field('bank_account').focus().should('be.visible') + cy.get_field('pay_to_account').focus().should('be.visible') + cy.get('.btn-primary').contains('Start Check Run').should('be.visible').click() + }) + + it('Complete First Check Run', () => { + cy.visit('/app/check-run/ACC-CR-2022-00001') + cy.fill_field('end_date', '1/1').blur() + cy.fill_field('posting_date', '1/1').blur() + cy.get('[data-checkbox-index="0"]').click().wait(250) + cy.get('[data-fieldname="amount_check_run"]').get('.like-disabled-input').should('contain', '$ 1,800.00') + cy.get('body').type(' ').wait(250) + cy.get('[data-fieldname="amount_check_run"]').get('.like-disabled-input').should('contain', '$ 0.00').wait(250) + cy.get('#select-all').click() + cy.get('.primary-action').contains('Save').should('be.visible').click() + cy.get('.primary-action').contains('Submit').should('be.visible').click().wait(250) + cy.get_open_dialog().contains('Yes').should('be.visible').click() + cy.get('.indicator-pill').should('contain', 'Submitted').wait(250) + cy.get('.indicator-pill').should('contain', 'Ready to Print').wait(15000) + }) + + it('Complete Second Check Run', () => { + cy.get('body').type('{ctrl+b}').wait(500) + cy.fill_field('end_date', '1/1').blur() + cy.fill_field('posting_date', '1/1').blur() + cy.get_field('pay_to_account').type('{ctrl+home}{del}').blur().wait(100) + cy.fill_field('pay_to_account', '2120 - Payroll Payable - CFC').blur().wait(500) + cy.get('#select-all').click() + cy.get('.primary-action').contains('Save').should('be.visible').click() + cy.get('.primary-action').contains('Submit').should('be.visible').click().wait(250) + cy.get_open_dialog().contains('Yes').should('be.visible').click() + cy.get('.indicator-pill').should('contain', 'Submitted').wait(250) + cy.get('.indicator-pill').should('contain', 'Ready to Print').wait(7000) + }) +}) diff --git a/cypress/plugins/index.js b/cypress/plugins/index.js index 5d36e6ba..96e29535 100644 --- a/cypress/plugins/index.js +++ b/cypress/plugins/index.js @@ -1,22 +1,22 @@ -/// -// *********************************************************** -// This example plugins/index.js can be used to load plugins -// -// You can change the location of this file or turn off loading -// the plugins file with the 'pluginsFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/plugins-guide -// *********************************************************** - -// This function is called when a project is opened or re-opened (e.g. due to -// the project's config changing) - -/** - * @type {Cypress.PluginConfig} - */ -// eslint-disable-next-line no-unused-vars -module.exports = (on, config) => { - // `on` is used to hook into various events Cypress emits - // `config` is the resolved Cypress config -} +/// +// *********************************************************** +// This example plugins/index.js can be used to load plugins +// +// You can change the location of this file or turn off loading +// the plugins file with the 'pluginsFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/plugins-guide +// *********************************************************** + +// This function is called when a project is opened or re-opened (e.g. due to +// the project's config changing) + +/** + * @type {Cypress.PluginConfig} + */ +// eslint-disable-next-line no-unused-vars +module.exports = (on, config) => { + // `on` is used to hook into various events Cypress emits + // `config` is the resolved Cypress config +} diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 2f9b1a45..948d9317 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -1,5 +1,5 @@ -import 'cypress-file-upload'; -import '@testing-library/cypress/add-commands'; +import 'cypress-file-upload' +import '@testing-library/cypress/add-commands' // import '@4tw/cypress-drag-drop'; // *********************************************** // This example commands.js shows you how to @@ -28,20 +28,20 @@ import '@testing-library/cypress/add-commands'; // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }); Cypress.Commands.add('login', (email, password) => { if (!email) { - email = 'Administrator'; + email = 'Administrator' } if (!password) { - password = Cypress.config('adminPassword'); + password = Cypress.config('adminPassword') } cy.request({ url: '/api/method/login', method: 'POST', body: { usr: email, - pwd: password - } - }); -}); + pwd: password, + }, + }) +}) Cypress.Commands.add('call', (method, args) => { return cy @@ -56,20 +56,20 @@ Cypress.Commands.add('call', (method, args) => { headers: { Accept: 'application/json', 'Content-Type': 'application/json', - 'X-Frappe-CSRF-Token': csrf_token - } + 'X-Frappe-CSRF-Token': csrf_token, + }, }) .then(res => { - expect(res.status).eq(200); - return res.body; - }); - }); -}); + expect(res.status).eq(200) + return res.body + }) + }) +}) Cypress.Commands.add('get_list', (doctype, fields = [], filters = []) => { - filters = JSON.stringify(filters); - fields = JSON.stringify(fields); - let url = `/api/resource/${doctype}?fields=${fields}&filters=${filters}`; + filters = JSON.stringify(filters) + fields = JSON.stringify(fields) + let url = `/api/resource/${doctype}?fields=${fields}&filters=${filters}` return cy .window() .its('frappe.csrf_token') @@ -80,15 +80,15 @@ Cypress.Commands.add('get_list', (doctype, fields = [], filters = []) => { url, headers: { Accept: 'application/json', - 'X-Frappe-CSRF-Token': csrf_token - } + 'X-Frappe-CSRF-Token': csrf_token, + }, }) .then(res => { - expect(res.status).eq(200); - return res.body; - }); - }); -}); + expect(res.status).eq(200) + return res.body + }) + }) +}) Cypress.Commands.add('get_doc', (doctype, name) => { return cy @@ -101,15 +101,15 @@ Cypress.Commands.add('get_doc', (doctype, name) => { url: `/api/resource/${doctype}/${name}`, headers: { Accept: 'application/json', - 'X-Frappe-CSRF-Token': csrf_token - } + 'X-Frappe-CSRF-Token': csrf_token, + }, }) .then(res => { - expect(res.status).eq(200); - return res.body; - }); - }); -}); + expect(res.status).eq(200) + return res.body + }) + }) +}) Cypress.Commands.add('remove_doc', (doctype, name) => { return cy @@ -122,139 +122,137 @@ Cypress.Commands.add('remove_doc', (doctype, name) => { url: `/api/resource/${doctype}/${name}`, headers: { Accept: 'application/json', - 'X-Frappe-CSRF-Token': csrf_token - } + 'X-Frappe-CSRF-Token': csrf_token, + }, }) .then(res => { - expect(res.status).eq(202); - return res.body; - }); - }); -}); + expect(res.status).eq(202) + return res.body + }) + }) +}) Cypress.Commands.add('create_records', doc => { - return cy - .call('frappe.tests.ui_test_helpers.create_if_not_exists', { doc }) - .then(r => r.message); -}); + return cy.call('frappe.tests.ui_test_helpers.create_if_not_exists', { doc }).then(r => r.message) +}) Cypress.Commands.add('set_value', (doctype, name, obj) => { return cy.call('frappe.client.set_value', { doctype, name, - fieldname: obj - }); -}); + fieldname: obj, + }) +}) Cypress.Commands.add('fill_field', (fieldname, value, fieldtype = 'Data') => { - cy.get_field(fieldname, fieldtype).as('input'); + cy.get_field(fieldname, fieldtype).as('input') if (['Date', 'Time', 'Datetime'].includes(fieldtype)) { - cy.get('@input').click().wait(200); - cy.get('.datepickers-container .datepicker.active').should('exist'); + cy.get('@input').click().wait(200) + cy.get('.datepickers-container .datepicker.active').should('exist') } if (fieldtype === 'Time') { - cy.get('@input').clear().wait(200); + cy.get('@input').clear().wait(200) } if (fieldtype === 'Select') { - cy.get('@input').select(value); + cy.get('@input').select(value) } else { - cy.get('@input').type(value, { waitForAnimations: false, force: true, delay: 100 }); + cy.get('@input').type(value, { waitForAnimations: false, force: true, delay: 100 }) } - return cy.get('@input'); -}); + return cy.get('@input') +}) Cypress.Commands.add('get_field', (fieldname, fieldtype = 'Data') => { - let field_element = fieldtype === 'Select' ? 'select' : 'input'; - let selector = `[data-fieldname="${fieldname}"] ${field_element}:visible`; + let field_element = fieldtype === 'Select' ? 'select' : 'input' + let selector = `[data-fieldname="${fieldname}"] ${field_element}:visible` if (fieldtype === 'Text Editor') { - selector = `[data-fieldname="${fieldname}"] .ql-editor[contenteditable=true]:visible`; + selector = `[data-fieldname="${fieldname}"] .ql-editor[contenteditable=true]:visible` } if (fieldtype === 'Code') { - selector = `[data-fieldname="${fieldname}"] .ace_text-input`; + selector = `[data-fieldname="${fieldname}"] .ace_text-input` } - return cy.get(selector).first(); -}); + return cy.get(selector).first() +}) Cypress.Commands.add('fill_table_field', (tablefieldname, row_idx, fieldname, value, fieldtype = 'Data') => { - cy.get_table_field(tablefieldname, row_idx, fieldname, fieldtype).as('input'); + cy.get_table_field(tablefieldname, row_idx, fieldname, fieldtype).as('input') if (['Date', 'Time', 'Datetime'].includes(fieldtype)) { - cy.get('@input').click().wait(200); - cy.get('.datepickers-container .datepicker.active').should('exist'); + cy.get('@input').click().wait(200) + cy.get('.datepickers-container .datepicker.active').should('exist') } if (fieldtype === 'Time') { - cy.get('@input').clear().wait(200); + cy.get('@input').clear().wait(200) } if (fieldtype === 'Select') { - cy.get('@input').select(value); + cy.get('@input').select(value) } else { - cy.get('@input').type(value, { waitForAnimations: false, force: true }); + cy.get('@input').type(value, { waitForAnimations: false, force: true }) } - return cy.get('@input'); -}); + return cy.get('@input') +}) Cypress.Commands.add('get_table_field', (tablefieldname, row_idx, fieldname, fieldtype = 'Data') => { - let selector = `.frappe-control[data-fieldname="${tablefieldname}"]`; - selector += ` [data-idx="${row_idx}"]`; - selector += ` .form-in-grid`; + let selector = `.frappe-control[data-fieldname="${tablefieldname}"]` + selector += ` [data-idx="${row_idx}"]` + selector += ` .form-in-grid` if (fieldtype === 'Text Editor') { - selector += ` [data-fieldname="${fieldname}"] .ql-editor[contenteditable=true]`; + selector += ` [data-fieldname="${fieldname}"] .ql-editor[contenteditable=true]` } else if (fieldtype === 'Code') { - selector += ` [data-fieldname="${fieldname}"] .ace_text-input`; + selector += ` [data-fieldname="${fieldname}"] .ace_text-input` } else { - selector += ` .form-control[data-fieldname="${fieldname}"]`; + selector += ` .form-control[data-fieldname="${fieldname}"]` } - return cy.get(selector); -}); + return cy.get(selector) +}) Cypress.Commands.add('awesomebar', text => { - cy.get('#navbar-search').type(`${text}{downarrow}{enter}`, { delay: 700 }); -}); + cy.get('#navbar-search').type(`${text}{downarrow}{enter}`, { delay: 700 }) +}) Cypress.Commands.add('new_form', doctype => { - let dt_in_route = doctype.toLowerCase().replace(/ /g, '-'); - cy.visit(`/app/${dt_in_route}/new`); - cy.get('body').should('have.attr', 'data-route', `Form/${doctype}/new-${dt_in_route}-1`); - cy.get('body').should('have.attr', 'data-ajax-state', 'complete'); -}); + let dt_in_route = doctype.toLowerCase().replace(/ /g, '-') + cy.visit(`/app/${dt_in_route}/new`) + cy.get('body').should('have.attr', 'data-route', `Form/${doctype}/new-${dt_in_route}-1`) + cy.get('body').should('have.attr', 'data-ajax-state', 'complete') +}) Cypress.Commands.add('go_to_list', doctype => { - let dt_in_route = doctype.toLowerCase().replace(/ /g, '-'); - cy.visit(`/app/${dt_in_route}`); -}); + let dt_in_route = doctype.toLowerCase().replace(/ /g, '-') + cy.visit(`/app/${dt_in_route}`) +}) Cypress.Commands.add('clear_cache', () => { cy.window() .its('frappe') .then(frappe => { - frappe.ui.toolbar.clear_cache(); - }); -}); + frappe.ui.toolbar.clear_cache() + }) +}) Cypress.Commands.add('dialog', opts => { return cy.window().then(win => { - var d = new win.frappe.ui.Dialog(opts); - d.show(); - return d; - }); -}); + var d = new win.frappe.ui.Dialog(opts) + d.show() + return d + }) +}) Cypress.Commands.add('get_open_dialog', () => { - return cy.get('.modal:visible').last(); -}); + return cy.get('.modal:visible').last() +}) Cypress.Commands.add('hide_dialog', () => { - cy.wait(300); - cy.get_open_dialog().find('.btn-modal-close').click(); - cy.get('.modal:visible').should('not.exist'); -}); + cy.wait(300) + cy.get_open_dialog().find('.btn-modal-close').click() + cy.get('.modal:visible').should('not.exist') +}) Cypress.Commands.add('insert_doc', (doctype, args, ignore_duplicate) => { return cy @@ -269,80 +267,82 @@ Cypress.Commands.add('insert_doc', (doctype, args, ignore_duplicate) => { headers: { Accept: 'application/json', 'Content-Type': 'application/json', - 'X-Frappe-CSRF-Token': csrf_token + 'X-Frappe-CSRF-Token': csrf_token, }, - failOnStatusCode: !ignore_duplicate + failOnStatusCode: !ignore_duplicate, }) .then(res => { - let status_codes = [200]; + let status_codes = [200] if (ignore_duplicate) { - status_codes.push(409); + status_codes.push(409) } - expect(res.status).to.be.oneOf(status_codes); - return res.body.data; - }); - }); -}); + expect(res.status).to.be.oneOf(status_codes) + return res.body.data + }) + }) +}) Cypress.Commands.add('add_filter', () => { - cy.get('.filter-section .filter-button').click(); - cy.wait(300); - cy.get('.filter-popover').should('exist'); -}); + cy.get('.filter-section .filter-button').click() + cy.wait(300) + cy.get('.filter-popover').should('exist') +}) Cypress.Commands.add('clear_filters', () => { - let has_filter = false; + let has_filter = false cy.intercept({ method: 'POST', - url: 'api/method/frappe.model.utils.user_settings.save' - }).as('filter-saved'); - cy.get('.filter-section .filter-button').click({ force: true }); - cy.wait(300); - cy.get('.filter-popover').should('exist'); + url: 'api/method/frappe.model.utils.user_settings.save', + }).as('filter-saved') + cy.get('.filter-section .filter-button').click({ force: true }) + cy.wait(300) + cy.get('.filter-popover').should('exist') cy.get('.filter-popover').then(popover => { if (popover.find('input.input-with-feedback')[0].value != '') { - has_filter = true; + has_filter = true } - }); - cy.get('.filter-popover').find('.clear-filters').click(); - cy.get('.filter-section .filter-button').click(); - cy.window().its('cur_list').then(cur_list => { - cur_list && cur_list.filter_area && cur_list.filter_area.clear(); - has_filter && cy.wait('@filter-saved'); - }); -}); - -Cypress.Commands.add('click_modal_primary_button', (btn_name) => { - cy.get('.modal-footer > .standard-actions > .btn-primary').contains(btn_name).trigger('click', { force: true }); -}); - -Cypress.Commands.add('click_sidebar_button', (btn_name) => { - cy.get('.list-group-by-fields .list-link > a').contains(btn_name).click({ force: true }); -}); - -Cypress.Commands.add('click_listview_row_item', (row_no) => { - cy.get('.list-row > .level-left > .list-subject > .level-item > .ellipsis').eq(row_no).click({ force: true }); -}); - -Cypress.Commands.add('click_listview_row_item_with_text', (text) => { + }) + cy.get('.filter-popover').find('.clear-filters').click() + cy.get('.filter-section .filter-button').click() + cy.window() + .its('cur_list') + .then(cur_list => { + cur_list && cur_list.filter_area && cur_list.filter_area.clear() + has_filter && cy.wait('@filter-saved') + }) +}) + +Cypress.Commands.add('click_modal_primary_button', btn_name => { + cy.get('.modal-footer > .standard-actions > .btn-primary').contains(btn_name).trigger('click', { force: true }) +}) + +Cypress.Commands.add('click_sidebar_button', btn_name => { + cy.get('.list-group-by-fields .list-link > a').contains(btn_name).click({ force: true }) +}) + +Cypress.Commands.add('click_listview_row_item', row_no => { + cy.get('.list-row > .level-left > .list-subject > .level-item > .ellipsis').eq(row_no).click({ force: true }) +}) + +Cypress.Commands.add('click_listview_row_item_with_text', text => { cy.get('.list-row > .level-left > .list-subject > .level-item > .ellipsis') .contains(text) .first() - .click({ force: true }); -}); + .click({ force: true }) +}) Cypress.Commands.add('click_filter_button', () => { - cy.get('.filter-selector > .btn').click(); -}); + cy.get('.filter-selector > .btn').click() +}) -Cypress.Commands.add('click_listview_primary_button', (btn_name) => { - cy.get('.primary-action').contains(btn_name).click({ force: true }); -}); +Cypress.Commands.add('click_listview_primary_button', btn_name => { + cy.get('.primary-action').contains(btn_name).click({ force: true }) +}) -Cypress.Commands.add('click_timeline_action_btn', (btn_name) => { - cy.get('.timeline-message-box .actions .action-btn').contains(btn_name).click(); -}); +Cypress.Commands.add('click_timeline_action_btn', btn_name => { + cy.get('.timeline-message-box .actions .action-btn').contains(btn_name).click() +}) -Cypress.Commands.add('select_listview_row_checkbox', (row_no) => { - cy.get('.frappe-list .select-like > .list-row-checkbox').eq(row_no).click(); -}); +Cypress.Commands.add('select_listview_row_checkbox', row_no => { + cy.get('.frappe-list .select-like > .list-row-checkbox').eq(row_no).click() +})