Compare commits

..

1832 Commits

Author SHA1 Message Date
Saurabh
f7f8f5c305 Merge branch 'v12-pre-release' into version-12 2020-10-22 18:28:33 +05:30
Saurabh
acf22c475e bumped to version 12.13.0 2020-10-22 18:48:33 +05:50
Nabin Hait
f74aa28752 chore: Added change log (#23707) 2020-10-22 17:52:16 +05:30
Marica
c1ffe0990b Update items add taxes v12 pre release (#23706)
* fix: Add Taxes if missing via Update Items

* chore: PO Test for adding tax row via Update Items

* chore: SO Test for adding tax row via Update Items
2020-10-22 17:04:38 +05:30
rohitwaghchaure
9ba0e991d9 Merge pull request #23658 from rohitwaghchaure/fixed-overproduction-against-work-order-pre-release
fix: overproduction, not allowed to transfer extra materials
2020-10-16 10:31:38 +05:30
Rohit Waghchaure
3df4f226d5 fix: overproduction, not allowed to transfer extra materials 2020-10-15 22:28:05 +05:30
rohitwaghchaure
8701156d28 Merge pull request #23657 from rohitwaghchaure/donot-set-serial-nos-if-manually-set-pre
fix: manually set serial nos override with current available serial nos
2020-10-15 22:05:01 +05:30
Rohit Waghchaure
a2ab227240 fix: manually set serial nos override with current available serial nos 2020-10-15 22:03:42 +05:30
rohitwaghchaure
8ba4201c7b Merge pull request #23656 from rohitwaghchaure/removed-repetative-code-v12-pre
fix: remove repetative code
2020-10-15 22:00:57 +05:30
Rohit Waghchaure
7a079d73dc fix: remove repetative code 2020-10-15 21:58:44 +05:30
rohitwaghchaure
d445e38e6b Merge pull request #23648 from rohitwaghchaure/set-current-serial-no-in-serial-no-field-v12-pre-release
fix: serial no field is blank in stock reconciliation
2020-10-15 15:54:55 +05:30
Rohit Waghchaure
940c5f2d4b fix: serial no field is blank in stock reconciliation 2020-10-15 15:53:37 +05:30
rohitwaghchaure
5cacaec52b Merge pull request #23638 from anupamvs/item-group-filter-portal-v12-pre-release
fix: added filter show in website for filtering product
2020-10-14 16:29:02 +05:30
Anupam
3f5c4932b4 fix: added filter show in website for filtering product 2020-10-14 15:37:09 +05:30
Nabin Hait
f145a4ce58 fix: resolved conflict 2020-10-14 09:59:55 +05:30
rohitwaghchaure
bd4896b1b2 Merge pull request #23619 from rohitwaghchaure/incorrect-supplied-qty-issue
fix: incorrect supplied qty error
2020-10-14 00:24:04 +05:30
rohitwaghchaure
98b7f0a0f0 Merge pull request #23497 from frappe/fix_fiscal_year
fix: fiscal year can be shorter than 12 months
2020-10-13 23:26:41 +05:30
Rohit Waghchaure
4def9ed1c6 fix: incorrect supplied qty error 2020-10-13 22:43:18 +05:30
rohitwaghchaure
0ae4527ff8 Merge pull request #23618 from aakvatech/patch-5
feat: Add company and correct filter in bank statement reconciliation report filters
2020-10-13 16:59:42 +05:30
Raffael Meyer
e2b7ac2b0e fix: make account number length configurable (#23496)
* fix: make account number length configurable

* Update datev.py

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-10-13 15:09:18 +05:30
Deepesh Garg
63bbce708d Merge pull request #23552 from deepeshgarg007/gst3b_taxable_value
fix: Taxable value in GSTR 3B report
2020-10-13 14:03:44 +05:30
aakvatech
8f60d25064 feat: Add company and correct filter in bank statement reconciliation…
… report filters

If you have multi company scenario and many bank accounts that are no longer active, then it becomes difficult in bank statement reconciliation report to filter the account to reconcile and it also shows disabled accounts so futher confusion is created.

https://github.com/frappe/erpnext/issues/23613
2020-10-13 10:26:33 +03:00
rohitwaghchaure
39988c9319 fix: subscription test case (#23617) 2020-10-13 12:36:48 +05:30
rohitwaghchaure
0c1aaa440d Merge pull request #23495 from frappe/check_fiscal_year
fix: check fiscal year
2020-10-13 12:12:00 +05:30
Marica
890c5b063c Merge pull request #23608 from marination/holiday-list-fix
fix: Convert dates to datetime.date before comparing in Holiday List
2020-10-13 02:47:51 +05:30
marination
007069d495 fix: Convert dates to datetime.date before comparing in Holiday List 2020-10-13 02:42:14 +05:30
Deepesh Garg
820c77a375 Merge pull request #23477 from anupamvs/subscription-invoice-v12
fix: Payment Schedule not fetching
2020-10-12 20:35:07 +05:30
Anupam Kumar
e6adf20037 Merge branch 'version-12-hotfix' into subscription-invoice-v12 2020-10-12 19:41:49 +05:30
rohitwaghchaure
72dbabf0d2 Merge pull request #23395 from anupamvs/quality-procedure
fix: quality procedure parent
2020-10-12 18:50:43 +05:30
Anupam
984fdde692 fix: review fixes 2020-10-12 18:06:58 +05:30
rohitwaghchaure
3cdbe7c586 Merge pull request #23440 from legendleap/version-12-hotfox-itemcode
fix: include item_code in items result to allow adding product info in custom templates
2020-10-12 17:30:45 +05:30
Marica
848d78b373 Merge pull request #23591 from anupamvs/item-attribute
feat: validate if removed item attributes exist in variants
2020-10-12 17:22:29 +05:30
Marica
e1047af4bf Merge branch 'version-12-hotfix' into item-attribute 2020-10-12 17:22:01 +05:30
Saqib
79d069cbbb fix: last purchase rate in item prices report (#23507)
* fix: last purchase rate in item prices report

* fix: last purchase rate in item prices report

* fix: last purchase rate in item prices report

* chore: fetch last purchase rate from update stock purchase invoices
2020-10-12 17:09:29 +05:30
rohitwaghchaure
29c2f399e3 Merge pull request #22328 from pipech/v12-hotfix-get_pricing_rule
fix(pricing_rules): pricing rule selector is wrong
2020-10-12 15:33:13 +05:30
rohitwaghchaure
4fa06ad6a6 Merge pull request #23594 from anupamvs/item-price-v12
fix: can't save item price after adding child table
2020-10-12 15:17:34 +05:30
Rushabh Mehta
122fea8043 Merge pull request #23551 from anupamvs/procurement-tracke-v12
fix: project value is missing from procurement-tracker
2020-10-12 15:13:46 +05:30
rohitwaghchaure
660a811483 Merge pull request #23564 from rohitwaghchaure/fixed-batch-wise-stock-reco
fix: negative stock error while submitting stock reco for batch item
2020-10-12 15:11:41 +05:30
rohitwaghchaure
c351e2a03c Merge pull request #23522 from rohitwaghchaure/extra-filters-in-batchwise-balance-report
feat: added new filters in the Batch-wise balance history report
2020-10-12 15:11:21 +05:30
rohitwaghchaure
0cbe0c0b21 Merge pull request #23520 from rohitwaghchaure/feat-added-balance-serial-no-column
feat: balance serial nos in stock leder report
2020-10-12 15:09:42 +05:30
rohitwaghchaure
3ed333831d Merge pull request #23599 from deepeshgarg007/pos_return_amount
fix: Mode of payment getting overwritten by defautl mode of payment for retruns
2020-10-12 15:09:16 +05:30
rohitwaghchaure
a0ed196005 Merge pull request #23600 from rohitwaghchaure/fixed-overproduction-issue
fix: not able to do overproduction
2020-10-12 14:59:41 +05:30
Saqib
f41fe79528 fix(asset): cannot create asset if cwip disabled and account not set (#23584)
* fix: asset purchase with purchase invoice

* chore: allow enable cwip accounting only if cwip account is set

* fix: cannot create asset if cwip disabled and account not set
2020-10-12 14:56:08 +05:30
Marica
72ca041fd2 Merge pull request #23480 from rohitwaghchaure/fixed-incorrect-operation-time-calculation
fix: incorrect operation time calculation for batch size
2020-10-12 14:55:01 +05:30
Rohit Waghchaure
1270c53dea fix: not able to do overproduction 2020-10-12 14:54:54 +05:30
Marica
cedbaa5ac9 Merge branch 'version-12-hotfix' into fixed-incorrect-operation-time-calculation 2020-10-12 14:54:44 +05:30
Marica
b3f0488dca Merge branch 'version-12-hotfix' into item-attribute 2020-10-12 14:51:18 +05:30
Deepesh Garg
772ccde33f fix: Mode of payment getting overwritten by defautl mode of payment for returns 2020-10-12 13:32:49 +05:30
Deepesh Garg
3ca57e493a fix: Do not consider opening entries for TDS calculation (#23598) 2020-10-12 13:03:25 +05:30
Anupam
dc68b44596 fix: can't save item price after adding child table 2020-10-12 11:53:19 +05:30
Anupam
a784e13ac3 fix: Payment Schedule not fetching 2020-10-12 11:51:51 +05:30
Anupam
6297778a6a feat: validate if removed item attributes exist in variants 2020-10-12 11:00:28 +05:30
rohitwaghchaure
6825d5442f Merge pull request #23544 from tundebabzy/version-12-hotfix
fix: pass self.flags.ignore_permissions to the ignore_permissions argument of Address and Contact
2020-10-10 11:11:28 +05:30
rohitwaghchaure
5bbb793803 Merge pull request #23571 from nextchamp-saqib/si-dashboard-fix-v12
fix: (revert) Add Delivery Note Count in Sales Invoice Dashboard
2020-10-10 11:08:55 +05:30
Saqib
5d7c9d2f61 fix: update items after submission ignores precision (#23492)
* fix: update items after submission ignores precision

* chore: add test
2020-10-09 21:19:40 +05:30
Saqib Ansari
a7293407bd fix: (revert) Add Delivery Note Count in Sales Invoice Dashboard 2020-10-09 21:01:31 +05:30
Rohit Waghchaure
4968f162bd fix: negative stock error while submitting stock reco for batch item 2020-10-09 18:34:13 +05:30
Marica
a8f9e2ab37 Merge pull request #23565 from marination/v12-fix-financial-statement-print-format
fix: Show total row in print format of financial statement
2020-10-09 14:50:29 +05:30
Anupam
15717490fd fix: project value is missing from procurement-tracker 2020-10-09 14:18:42 +05:30
Suraj Shetty
eef5c5dad4 fix: Show total row in print format of financial statement 2020-10-09 13:52:34 +05:30
Tunde Akinyanmi
65a716dd08 Merge branch 'version-12-hotfix' of https://github.com/frappe/erpnext into version-12-hotfix 2020-10-09 07:24:23 +01:00
Deepesh Garg
0e6e7e95ba fix: Taxable value in GSTR 3B report 2020-10-08 15:30:40 +05:30
Marica
9039e29a8c Merge branch 'version-12-hotfix' into fixed-incorrect-operation-time-calculation 2020-10-08 14:06:13 +05:30
Saqib
7c760f62c3 fix: 'stock_rbnb_currency' referenced before assignment (#23528) 2020-10-08 13:49:07 +05:30
rohitwaghchaure
f64adc8acf Merge pull request #23547 from frappe/account_settings_v12
fix: Typo in Accounts Settings
2020-10-08 11:25:32 +05:30
rohitwaghchaure
5da98cce86 Update test_work_order.py 2020-10-08 11:19:41 +05:30
michellealva
5f3e5c8a7c fix: Typo in Accounts Settings 2020-10-08 09:41:44 +05:30
Tunde Akinyanmi
78a98030ef fix: pass self.flags.ignore_permissions to the ignore_permissions
argument of Address and Contact controller
2020-10-08 02:19:02 +01:00
rohitwaghchaure
41b88eafe0 Merge pull request #23536 from rohitwaghchaure/fixed-cogs-error-in-pr
fix: COGS validation in the purchase receipt
2020-10-07 19:08:49 +05:30
Rohit Waghchaure
3f505b711d fixed: COGS validation in the purchase receipt 2020-10-07 19:06:15 +05:30
Raffael Meyer
65ff74778d Merge branch 'version-12-hotfix' into v12-hotfix-get_pricing_rule 2020-10-07 09:47:53 +02:00
Deepesh Garg
81b47821b7 Merge pull request #23354 from deepeshgarg007/tax_fetch_quote_v12
fix: Taxes not getting fetched from Opportunity to Quotation
2020-10-06 19:55:38 +05:30
Raffael Meyer
b2d1ae2cb8 Merge branch 'version-12-hotfix' into fix_fiscal_year 2020-10-06 10:56:13 +02:00
Rohit Waghchaure
f0108b42fd refactor: added new filters in the Batch-wise balance history report 2020-10-06 13:47:03 +05:30
Rohit Waghchaure
20262e00df feat: balance serial nos in stock leder report 2020-10-06 12:50:21 +05:30
rohitwaghchaure
448af6a893 Merge pull request #23484 from marination/item-attr-fix
fix: Handle Blank from/to range in Numeric Item Attribute
2020-10-06 12:00:36 +05:30
Rohit Waghchaure
1c826afa01 fix: incorrect operation time calculation for batch size 2020-10-05 16:57:21 +05:30
Deepesh Garg
ad4b421251 Merge pull request #23463 from rohitwaghchaure/skip-non-apply-tds-invoices
fix: tds calculation, skip invoices with "Apply Tax Withholding Amount" has disabled
2020-10-05 14:21:48 +05:30
rohitwaghchaure
748585c9a9 Merge pull request #23512 from marination/sales-order-project-fix
fix: Show Project under Create button in SO
2020-10-05 13:17:57 +05:30
marination
6e1d818c3e fix: Show Project under Create button in SO 2020-10-05 12:36:55 +05:30
Faris Ansari
a246b1db20 Merge pull request #23508 from rohitwaghchaure/item-configure-performace-issue
fix: performance issue while adding template item in the cart
2020-10-05 11:45:49 +05:30
Rohit Waghchaure
5056fbcb9d fix: performance issue while adding template item in the cart 2020-10-04 20:14:53 +05:30
Saqib
8aad02f822 fix: naming series - cannot reset current value to zero (#23505) 2020-10-04 18:13:58 +05:30
barredterra
e1f1d5a67d fix: fiscal year can be shorter than 12 months 2020-10-03 18:28:43 +02:00
barredterra
b4c2816343 fix: check fiscal year 2020-10-03 17:25:26 +02:00
Rohit Waghchaure
feade95a26 fix: tds calculation, skip invoices with "Apply Tax Withholding Amount" has disabled 2020-10-03 16:16:24 +05:30
marination
fe0ce60d75 fix: Handle Blank from/to range in Numeric Item Attribute 2020-10-02 12:28:41 +05:30
Saurabh
3e0f488083 Merge branch 'v12-pre-release' into version-12 2020-10-01 12:54:23 +05:30
Saurabh
db4178b1e8 bumped to version 12.12.1 2020-10-01 13:14:23 +05:50
rohitwaghchaure
58598c6070 Merge pull request #23433 from rohitwaghchaure/fixed-stock-ageing-report-data
fix: stock ageing report
2020-09-30 23:37:40 +05:30
Saurabh
080ef5a2bc fix: validate theme_scss attribute (#23466) 2020-09-30 18:11:37 +05:30
Deepesh Garg
e4df9dde6b Merge pull request #23442 from prssanna/company-replace-abbr
fix: longer timeout for company replace abbreviation
2020-09-30 14:07:05 +05:30
Deepesh Garg
b217075f6b fix: Remove any taxes fetched due to unknown rules 2020-09-30 13:00:18 +05:30
Marica
7c0056be9d Merge pull request #23461 from marination/cost-center-validation-pre-release
fix: Avoid validation of group cost center on repost
2020-09-30 12:58:13 +05:30
Marica
146575b176 Merge pull request #23459 from marination/update-items-perm-v12-pre-release
fix: Check only Read and Write Permission in Update Items
2020-09-30 12:55:48 +05:30
Marica
3d254a3fe5 Merge pull request #23458 from marination/update-items-perm-fix
fix: Check only Read and Write Permission in Update Items
2020-09-30 12:55:18 +05:30
Marica
95e50b3f21 Merge branch 'version-12-hotfix' into update-items-perm-fix 2020-09-30 12:29:06 +05:30
Deepesh Garg
80379ef921 fix: Remove any taxes fetched due to unknown rules 2020-09-30 10:28:38 +05:30
marination
374fa69c54 fix: Only validate group cost center if not from repost 2020-09-29 20:48:47 +05:30
Deepesh Garg
b6ce2de666 Merge pull request #23460 from marination/cost-center-validation
fix: Avoid validation of group cost center on repost
2020-09-29 20:26:51 +05:30
marination
91e552ef3a fix: Only validate group cost center if not from repost 2020-09-29 19:33:00 +05:30
Deepesh Garg
096da540e3 fix: Add test 2020-09-29 19:06:21 +05:30
Deepesh Garg
54c993fb75 Merge branch 'version-12-hotfix' of https://github.com/frappe/erpnext into tax_fetch_quote_v12 2020-09-29 18:47:01 +05:30
marination
2949d1c355 fix: Check only Read and Write Permission in Update Items 2020-09-29 18:29:00 +05:30
marination
3fdabe75d3 fix: Check only Read and Write Permission in Update Items 2020-09-29 18:26:40 +05:30
rohitwaghchaure
103866a367 Merge pull request #23451 from rohitwaghchaure/display-item-name-instead-of-code
fix: display item name instead of item code in offline POS
2020-09-29 13:01:37 +05:30
Rohit Waghchaure
8ba98a52a0 fix: display item name instead of item code in POS 2020-09-29 13:00:00 +05:30
rohitwaghchaure
c7ef8000ac Update stock_ageing.py 2020-09-28 16:00:48 +05:30
prssanna
031502e64c fix: longer timeout for company replace abbreviation 2020-09-28 13:34:24 +05:30
Oliver Liu
572ee8b152 include item_code in query result 2020-09-28 02:24:31 +02:00
Saurabh
24e5a6172a Merge branch 'v12-pre-release' into version-12 2020-09-25 11:47:15 +05:30
Saurabh
c7689d8796 bumped to version 12.12.0 2020-09-25 12:07:15 +05:50
Nabin Hait
5e7bfa37ef chore: Added change log for v12.12.0 (#23435) 2020-09-25 10:07:07 +05:30
Rohit Waghchaure
560f513d69 fix: stock ageing report 2020-09-25 01:12:31 +05:30
rohitwaghchaure
968cb60010 Merge pull request #23430 from prssanna/mr-stock-uom
fix: set stock UOM in item while creating material request from stock entry
2020-09-24 20:05:20 +05:30
prssanna
92c1f38c33 fix: set stock uom in tem while creating material request from stock entry 2020-09-24 17:08:22 +05:30
Anurag Mishra
257927a18b fix:dash board for pre-release test (#23428) 2020-09-24 16:32:39 +05:30
Anurag Mishra
1caab44c29 fix:dash board for pre-release test (#23426) 2020-09-24 15:43:18 +05:30
Saqib
0847f257e6 chore: make asset movement transaction date match with purchase date & time (#23425) 2020-09-24 15:09:48 +05:30
Saqib
b61361f942 chore: make asset movement transaction date match with purchase date & time (#23424) 2020-09-24 15:08:52 +05:30
Marica
e45aeab291 fix: Check Company in Payment Entry before selecting values (#23421) 2020-09-24 12:07:49 +05:30
Marica
abd4b04cd9 fix: Check Company in Payment Entry before selecting values (#23420) 2020-09-24 12:07:43 +05:30
rohitwaghchaure
d7bce62c60 Merge pull request #23414 from rohitwaghchaure/book-loss-amount-in-cogs-pre-release
refactor: book loss amount in the COGS instead of stock received but not billed
2020-09-23 20:32:27 +05:30
Rohit Waghchaure
4220f1e759 refactor: book loss amount in the COGS instead of stock received but not billed 2020-09-23 20:30:26 +05:30
rohitwaghchaure
8699e82290 Merge pull request #23411 from anupamvs/leave-application-status-v12-hotfix
fix: leave application status fix
2020-09-23 20:28:40 +05:30
rohitwaghchaure
5100410e10 Merge pull request #23412 from rohitwaghchaure/book-loss-amount-in-cogs
refactor: book loss amount in the COGS instead of stock received but not billed
2020-09-23 20:28:21 +05:30
Rohit Waghchaure
9e3764ab62 refactor: book loss amount in the COGS instead of stock received but not billed 2020-09-23 19:35:49 +05:30
Anupam
f31e358df9 fix: leave application status fix 2020-09-23 19:14:31 +05:30
rohitwaghchaure
76fdc9faaa Merge pull request #23391 from anupamvs/leave-application-status-fix
fix: leave application status fix
2020-09-23 19:06:17 +05:30
Anupam
960229793b fix: conflict resolved 2020-09-23 18:58:38 +05:30
Anupam
ecfc4b3fd4 fix: review changes 2020-09-23 18:53:40 +05:30
Prssanna Desai
2984f42e2b fix: ignore permission while creating supplier scorecard period in supplier scorecard (#23405) 2020-09-23 17:16:59 +05:30
Saqib
7d9dd8601a fix: failed workflow condition error message in update items (#23394) 2020-09-23 13:01:58 +05:30
Saqib
aad4bea366 fix: failed workflow condition error message in update items (#23392) 2020-09-23 13:01:30 +05:30
rohitwaghchaure
4a154098cc Merge pull request #23404 from rohitwaghchaure/download-required-materials-not-working-pre
fix: Download Required Materials not working for production plan
2020-09-23 01:32:42 +05:30
Rohit Waghchaure
0d4fb93d71 fix: Download Required Materials not working for production plan 2020-09-23 01:31:03 +05:30
rohitwaghchaure
07b1737351 Merge pull request #23396 from anupamvs/batch-wise-filter-v12
fix: adding filters validation Batch-Wise Balance History
2020-09-23 00:58:11 +05:30
rohitwaghchaure
6dc1b423bd Merge pull request #23399 from prssanna/supplier-scorecard-perm
fix: ignore permission while creating Supplier Scorecard Period in Supplier Scorecard
2020-09-23 00:57:52 +05:30
rohitwaghchaure
5a34e2db97 Merge pull request #23403 from rohitwaghchaure/download-required-materials-not-working
fix: Download Required Materials not working for production plan
2020-09-23 00:56:58 +05:30
Anupam Kumar
134b0fd18b Merge branch 'version-12-hotfix' into quality-procedure 2020-09-23 00:38:58 +05:30
Rohit Waghchaure
b948cf204c fix: Download Required Materials not working for production plan 2020-09-22 19:47:33 +05:30
Afshan
38000aacc5 fix: warehouse address filtered based on warehouse (#23381)
Co-authored-by: Marica <maricadsouza221197@gmail.com>
2020-09-22 16:51:58 +05:30
prssanna
a78cd20e31 fix: ignore permission while creating supplier scorecard period in supplier scorecard 2020-09-22 15:04:03 +05:30
Anupam
9a7e433a9d fix: quality procedure parent 2020-09-22 14:34:48 +05:30
Anupam
ba271317c7 fix: adding filters validation Batch-Wise Balance History 2020-09-22 13:45:15 +05:30
rohitwaghchaure
d12c99fda9 Merge pull request #23389 from rohitwaghchaure/fixed-subcontract-issue-for-bacthed-raw-material
fix: incorrect consumed qty if raw material with batch
2020-09-22 11:54:46 +05:30
Anupam
d5a33f4604 fix: leave application status fix 2020-09-22 11:29:17 +05:30
rohitwaghchaure
14175a2f47 Merge pull request #23390 from rohitwaghchaure/fixed-subcontract-issue-for-bacthed-raw-material-pre
fix: incorrect consumed qty if raw material with batch
2020-09-22 10:51:46 +05:30
Rohit Waghchaure
be97f1fe7c fix: incorrect consumed qty if raw material with batch 2020-09-22 10:50:23 +05:30
Rohit Waghchaure
b53a84b07f fix: incorrect consumed qty if raw material with batch 2020-09-22 10:36:52 +05:30
rohitwaghchaure
8a776fdad4 Merge pull request #23378 from rohitwaghchaure/online-pos-print-not-working-pre
fix: online pos print not working
2020-09-22 10:30:53 +05:30
rohitwaghchaure
59a4150428 Merge pull request #23377 from rohitwaghchaure/online-pos-print-not-working
fix: online pos print not working
2020-09-22 10:29:12 +05:30
Rohit Waghchaure
3bebc58357 fix: online pos print not working 2020-09-21 18:32:08 +05:30
Rohit Waghchaure
088e1e66ac fix: online pos print not working 2020-09-21 18:26:52 +05:30
Mangesh-Khairnar
19365d6d07 fix(plaid): do not send null list in account ids (#23375) 2020-09-21 18:04:34 +05:30
Rohan
af585aae28 fix: use Plaid's new API (v12) (#23317)
* fix: use Plaid's new API

* fix: requested changes
2020-09-21 16:08:55 +05:30
Saurabh
3599024c3e Merge pull request #23372 from abhishekbalam/version-12
fix(activation): Added New DocTypes in get_level()
2020-09-21 15:38:02 +05:30
Abhishek Balam
ad5721cbf4 fix: add doctypes to activation 2020-09-21 15:28:13 +05:30
rohitwaghchaure
2ea0263221 Merge pull request #23371 from rohitwaghchaure/enabled-no-copy-for-bill-date-pre
refactor: enabled no copy property for Supplier Invoice Date to avoid…
2020-09-21 14:25:19 +05:30
Rohit Waghchaure
70fc3de817 refactor: enabled no copy property for Supplier Invoice Date to avoid due date validation 2020-09-21 14:23:46 +05:30
rohitwaghchaure
d07579a142 Merge pull request #23367 from rohitwaghchaure/enabled-no-copy-for-bill-date
refactor: enabled no copy property for Supplier Invoice Date to avoid due date validation
2020-09-21 13:46:49 +05:30
rohitwaghchaure
06ecf99609 Merge pull request #23368 from rohitwaghchaure/fix-stock-reco-with-serial-nos-pre-release
fix: stock reconciliation, incorrect serial nos fetched in the current serial no field
2020-09-21 12:55:33 +05:30
Rohit Waghchaure
b07cfd2aec fix: stock reconciliation, incorrect serial nos fetched in the current serial no field 2020-09-21 12:50:47 +05:30
rohitwaghchaure
1b8006be5c Merge pull request #23366 from rohitwaghchaure/fix-stock-reco-with-serial-nos
fix: stock reconciliation, incorrect serial nos fetched in the current serial no field
2020-09-21 12:49:31 +05:30
Rohit Waghchaure
e71e4cf8b5 refactor: enabled no copy property for Supplier Invoice Date to avoid due date validation 2020-09-21 12:26:35 +05:30
Rohit Waghchaure
f5fd2cd4a2 fix: stock reconciliation, incorrect serial nos fetched in the current serial no field 2020-09-21 01:20:00 +05:30
Deepesh Garg
a621c52ebb Merge pull request #23356 from Anurag810/accounts_recievable_v12
fix: Cost Center filter in accounts receivable and payable report
2020-09-20 19:35:26 +05:30
Saqib
3748cbb32c fix: depreciation start date ux fixes (#23340) 2020-09-20 19:31:46 +05:30
Anurag Mishra
d57a94e2f2 fix: No handlefor Cost centers in Accounts Receivable 2020-09-18 13:36:07 +05:30
Deepesh Garg
31a887f045 fix: Taxes not getting fetched from Opportunity to Quotation 2020-09-18 11:32:57 +05:30
Marica
4b2e4026c4 Merge pull request #23338 from marination/item-tax-update-items-fix
fix: Item Tax Updating via `Update Items` in SO/PO
2020-09-16 11:56:29 +05:30
marination
526814c041 fix: Fetch and set Item Tax Template via Update Items 2020-09-16 10:28:33 +05:30
Saqib
1ee7ea6ade fix: update items with workflow travis fix (#23335) 2020-09-15 21:58:36 +05:30
Nabin Hait
c9b5d78993 Merge branch 'version-12-hotfix' into v12-pre-release 2020-09-15 19:43:21 +05:30
rohitwaghchaure
783a4c7f48 fix: set_taxes() missing 1 required positional argument: 'company' (#23329) 2020-09-15 19:42:56 +05:30
Nabin Hait
5750f317c5 Merge branch 'version-12-hotfix' into v12-pre-release 2020-09-15 19:42:05 +05:30
rohitwaghchaure
bea5e5b1bc fix: consumed qty logic for subcontracted raw materials (#23314)
* fix: consumed qty logic for subcontracted raw materials

* added test case

* fix: sales order workflow test case
2020-09-15 19:40:37 +05:30
Saqib
812033b782 fix: purchase order updates are not tracked (#23325) 2020-09-15 11:21:16 +05:30
rohitwaghchaure
bb905ca4b0 fix: incorrect calculation for consumed qty for subcontract item (#23257)
* fix: incorrect calculation for consumed qty for subcontract item

* added test case
2020-09-14 21:12:16 +05:30
Rucha Mahabal
a951a59094 fix: get_transaction_entries function arguments in Bank Statement Transaction Entry (#23052) 2020-09-14 21:05:54 +05:30
rohitwaghchaure
444f657d25 fix: production plan incorrect work order qty (#23264)
* fix: production plan incorrect work order qty

* added test case

* Update test_production_plan.py
2020-09-14 20:51:20 +05:30
Saqib
1e981b50d5 Update items with workflow v12 (#23324)
* feat: validate workflow before so/po update items

* fix: incorrect workflow validation on so/po update items
2020-09-14 19:53:52 +05:30
rohitwaghchaure
279ccc56ee Merge pull request #23320 from marination/quality-inspection-fields-fix
fix: Make Reference fields mandatory in Quality Inspection
2020-09-14 14:11:35 +05:30
Marica
b67996ec42 Merge branch 'version-12-hotfix' into quality-inspection-fields-fix 2020-09-14 12:01:26 +05:30
Marica
fbee0804c3 Merge pull request #23321 from marination/pr-dn-get-items-fix
fix: Make sure Supplier/Customer is selected before fetching Items.
2020-09-14 11:37:25 +05:30
marination
a34a490e0c fix: Make sure Supplier/Customer is selected before fetching Items. 2020-09-14 11:11:39 +05:30
marination
815416adc5 fix: Make Reference fields mandatory in Quality Inspection 2020-09-14 11:07:26 +05:30
Saqib
d409f91469 fix: asset movement date for backdated asset entry (#23300) 2020-09-09 10:53:48 +05:30
rohitwaghchaure
dad5bbdaa2 Merge pull request #23131 from Anurag810/leave_application_form_dashboard_fixes
fix: Form dashboard showing wrong balance
2020-09-08 16:58:11 +05:30
Anurag Mishra
e5fbebf946 fix: update filters 2020-09-08 15:55:07 +05:30
Marica
245cd21c31 Merge pull request #23295 from AfshanKhan/reverse-je-ignore-flag-v12
fix: removed ignore permission flag
2020-09-08 14:52:19 +05:30
Marica
4d9b511bb4 Merge branch 'version-12-hotfix' into reverse-je-ignore-flag-v12 2020-09-08 13:52:54 +05:30
Afshan
f1471ecbd4 fix: removed ignore permission flag 2020-09-08 13:50:54 +05:30
Marica
00a8e2226a Merge pull request #23292 from AfshanKhan/irs-v12
fix: returned empty list if non US based company
2020-09-08 13:45:44 +05:30
Afshan
d44052441b fix: returned empty list if non US based company 2020-09-08 13:03:37 +05:30
rohitwaghchaure
8e21738238 Merge pull request #23192 from marination/batch-source-reference
fix: Unlink and delete batch created from stock reco on cancel
2020-09-08 12:12:30 +05:30
Rucha Mahabal
5096639591 fix: Lock row in subquery while setting delivered qty (#23101) 2020-09-08 11:58:07 +05:30
rohitwaghchaure
810c4250b8 Merge pull request #23286 from rohitwaghchaure/fixed-payment-entry-status-issue
fix: incorrect payment entry status
2020-09-08 10:35:52 +05:30
Deepesh Garg
47c535399c fix: Check if subscription is already created for current invoicing period (#23143)
* fix: Check if subscription is already created for current invoicing period

* fix: Test for duplicate subscription

* fix: invoice generation at the begining of period fix

* fix: invoice generation at the begining of period fix

* fix: Remove unwanted file

* fix: Generate new invoices even though current invoices are unpaid

* fix: Make invoices table read-only

* fix: Update test cases
2020-09-08 10:30:02 +05:30
rohitwaghchaure
b06c618585 Merge pull request #23247 from Anurag810/attendance_calendar_map_v12
fix: Attendance calendar map fix
2020-09-08 10:15:01 +05:30
Rohit Waghchaure
785b7b3359 fix: incorrect payment entry status 2020-09-07 21:54:57 +05:30
Marica
32179293ee Merge branch 'version-12-hotfix' into batch-source-reference 2020-09-07 19:30:36 +05:30
rohitwaghchaure
8dd72018c2 Merge pull request #23262 from rohitwaghchaure/not-able-to-make-material-request-from-so
fix: not able to make material request from SO
2020-09-06 20:49:03 +05:30
rohitwaghchaure
b2bf54cc33 Merge pull request #23260 from anupamvs/se-quantity-v12
fix: SE quantity data type issue
2020-09-04 16:07:32 +05:30
Rohit Waghchaure
4f3eb3fceb fix: not able to make material request from SO 2020-09-04 15:24:56 +05:30
Anupam K
9260cf11b6 fix: SE quantity data type issue 2020-09-04 15:10:50 +05:30
Deepesh Garg
84077676f7 Merge pull request #23254 from anupamvs/profit-loss-fix
fix: profit and loss report not working
2020-09-04 13:23:57 +05:30
Anupam K
5519824414 fix: profit and loss report not working 2020-09-04 11:09:26 +05:30
Deepesh Garg
cce13aaa65 Merge pull request #23252 from deepeshgarg007/hsn_wise_summary
fix: Stock qty in HSN wise outward summary
2020-09-03 21:29:58 +05:30
Deepesh Garg
b78d35ae1d fix: Stock qty in HSN wise outward summary 2020-09-03 21:16:31 +05:30
Marica
7c963a03cb Merge branch 'version-12-hotfix' into batch-source-reference 2020-09-03 18:01:09 +05:30
Marica
4a67da2b0b Merge pull request #23246 from marination/item-tax-query-fix
fix: Misleading filters on Item tax Template Link field
2020-09-03 17:51:42 +05:30
Anurag Mishra
053fdd91da fix: data was not properly maped 2020-09-03 17:11:59 +05:30
marination
c407bb2385 fix: Misleading filters on Item tax Template Link field 2020-09-03 16:54:43 +05:30
Marica
271c550e96 Merge pull request #23244 from marination/purchase-over-receipt-subcontracted-hotfix
fix: Raise Error on over receipt/consumption for sub-contracted PR
2020-09-03 16:45:13 +05:30
marination
8781e77132 fix: Test for Over Receipt via PRs on a PO 2020-09-03 15:48:18 +05:30
marination
5c1f11e561 fix: Raise Error on over receipt/consumption for sub-contrcated PR 2020-09-03 15:47:38 +05:30
Marica
203f78cf97 Merge branch 'version-12-hotfix' into batch-source-reference 2020-09-03 15:39:44 +05:30
rohitwaghchaure
e212537a5f Merge pull request #23186 from rohitwaghchaure/incorrect-stock-balance-issue-for-batched-item
fix: Incorrect stock balance issue for batched item
2020-09-03 15:02:38 +05:30
marination
e33b9628d3 Merge branch 'batch-source-reference' of https://github.com/marination/erpnext into batch-source-reference 2020-09-03 14:42:05 +05:30
marination
44717e3b4a fix: Item Alternative Test 2020-09-03 14:41:05 +05:30
Marica
1dbffa6ccc Merge branch 'version-12-hotfix' into batch-source-reference 2020-09-02 20:24:07 +05:30
marination
9b6326f5d4 chore: Tests for Stock Reconciliation 2020-09-02 19:21:16 +05:30
rohitwaghchaure
bd70dd02c1 Merge pull request #23236 from rohitwaghchaure/fix-pos-special-char-escape-issue
fix: pos item name special character issue
2020-09-02 17:20:43 +05:30
Rohit Waghchaure
f30181527f fix: pos item name special character issue 2020-09-02 17:08:28 +05:30
marination
6b785cf522 chore: Tests for Stock Entry 2020-09-02 15:45:02 +05:30
rohitwaghchaure
ba867d48f7 Merge pull request #23020 from prssanna/gross-profit-total-v12
feat: enable total row in Gross Profit Report
2020-09-02 12:17:33 +05:30
rohitwaghchaure
a0099a3c63 Merge pull request #23124 from ruchamahabal/fix-pos-return
fix: Update paid amount for pos return (#20543)
2020-09-02 12:16:25 +05:30
Marica
3992ddedd0 fix: General Ledger filter validation (#23230) 2020-09-02 11:10:46 +05:30
Shivam Mishra
457a909784 fix: events not deleted on cancelling maintenance schedule (#23219)
* feat: add participant to event_participant child table

* feat: add tests

* chore: update function name

Co-authored-by: Marica <maricadsouza221197@gmail.com>

Co-authored-by: Marica <maricadsouza221197@gmail.com>
2020-09-01 16:30:34 +05:30
Marica
96882f599e Merge pull request #23206 from marination/party-details-hotfix
fix: Check for Company before trying to fetch party details
2020-09-01 16:29:43 +05:30
Marica
c50b296f2d Merge branch 'version-12-hotfix' into party-details-hotfix 2020-09-01 15:36:26 +05:30
Marica
05a90baddd fix: Don't overwrite doctype while setting attributes in child row (#23228) 2020-09-01 15:29:27 +05:30
rohitwaghchaure
14ea8928f5 Merge pull request #23226 from rohitwaghchaure/fixed-job-card-time-log-issue
fix: incorrect job card timer issue
2020-09-01 14:08:29 +05:30
Rohit Waghchaure
ee4e725b37 fix: incorrect job card timer issue 2020-09-01 14:02:34 +05:30
Afshan
ab6f761753 fix:reverse journal entry (#23224) 2020-08-31 22:01:54 +05:30
Rucha Mahabal
b574353c00 fix(Payroll Entry): Set cost center for payroll payable account (#23223) 2020-08-31 20:09:48 +05:30
Deepesh Garg
3bf5820236 Merge pull request #23081 from deepeshgarg007/hsn_wise_json_v12_hotfix
feat: JSON download for HSN wise outward summary
2020-08-31 17:15:45 +05:30
Marica
0ff376fa4a fix: Better error feedback on creating SO from Quotation (#23214) 2020-08-31 13:54:10 +05:30
rohitwaghchaure
dc8de013c9 Merge pull request #23215 from rohitwaghchaure/fixed-different-workstation-not-update-qty
fix: incorrect completed qty against operation in work order if workstation is different in job card
2020-08-31 13:29:44 +05:30
Rohit Waghchaure
b73879056f fix: incorrect completed qty against operation in work order if workstation is different in job card 2020-08-31 12:27:49 +05:30
rohitwaghchaure
a2c4cc1252 Merge pull request #23126 from rohitwaghchaure/multi-operation-job-card-issue
fix: user created manual job card not linking job card operation with work order operation
2020-08-31 11:52:20 +05:30
marination
17a3af47d9 fix: Check for Company before trying to fetch party details 2020-08-29 19:28:51 +05:30
Rohit Waghchaure
e352706330 fix: user created manual job card not linking job card operations with work order operations 2020-08-28 12:29:27 +05:30
rohitwaghchaure
8dc7a43cc9 Merge pull request #23169 from ruchamahabal/fix-appointment-duration-v12
fix: don't overwrite patient appointment duration if already specified
2020-08-28 10:39:59 +05:30
rohitwaghchaure
aa795efe15 Merge pull request #23171 from anupamvs/product-contact-us
feat: Added phone field in product Inquiry
2020-08-28 10:38:07 +05:30
rohitwaghchaure
89d1990d79 Merge pull request #23179 from marination/pricing-rule-missing-fix-version-12-hotfix
fix: Pricing rule encoding missing line
2020-08-28 10:36:24 +05:30
rohitwaghchaure
c8f695c0da Merge pull request #23191 from ruchamahabal/fix-bom-update-v12
fix: BOM Update Tool failing due to Too Many Writes error
2020-08-28 10:35:47 +05:30
marination
eab2d25c6a fix: Unlink and delete batch created from stock reco on cancel 2020-08-27 20:34:51 +05:30
Rucha Mahabal
00dbbc9f5c fix: BOM Update Tool failing due to Too Many Writes error 2020-08-27 19:00:07 +05:30
Rohit Waghchaure
09cb0f33b2 added test cases 2020-08-27 14:57:37 +05:30
Rohit Waghchaure
dcd80a3f48 fix: incorrect stock balance issue 2020-08-27 13:24:33 +05:30
Saurabh
8bd9bf2be6 Merge branch 'v12-pre-release' into version-12 2020-08-27 10:04:32 +05:30
Saurabh
fc7121e38f bumped to version 12.11.2 2020-08-27 10:24:32 +05:50
rohitwaghchaure
03488f8285 Merge pull request #23177 from marination/pricing-rule-missing-fix-v12-pre-release
fix: Pricing Rule encoding missing line
2020-08-27 10:00:48 +05:30
marination
4cce4a7712 fix: get_applied_pricing_rule in taxes_and_totals 2020-08-26 20:18:37 +05:30
marination
4e3bbb8e74 fix: get_applied_pricing_rule in taxes_and_totals 2020-08-26 20:09:11 +05:30
Rucha Mahabal
a3907e1329 fix: don't overwrite appointment duration if already specified 2020-08-26 15:15:05 +05:30
Anupam K
759282651b feat: Added phone field in product Inquiry 2020-08-26 14:23:56 +05:30
rohitwaghchaure
3bae6328c5 Merge pull request #23153 from ruchamahabal/fix-conversion-factor-v12
fix: conversion factor for BOM exploded item rate
2020-08-26 13:02:48 +05:30
Deepesh Garg
45d615887a fix: Codacy fixes 2020-08-26 12:12:06 +05:30
Rucha Mahabal
ed6cbc1239 Merge pull request #23164 from frappe/michellealva-sales_invoice_dashboard
fix: Add Delivery Note link in Sales Invoice Dashboard
2020-08-25 21:09:50 +05:30
Michelle Alva
deb741bea5 fix:Add Delivery Note link in Sales Invoice Dashboard 2020-08-25 21:00:41 +05:30
Saurabh
271f8b5cfd Merge branch 'v12-pre-release' into version-12 2020-08-25 20:01:54 +05:30
Saurabh
f26e66b2b5 bumped to version 12.11.1 2020-08-25 20:21:54 +05:50
Saurabh
020534046c Merge pull request #23162 from rohitwaghchaure/pricing-rule-not-working-js-side-v12-pre-release
fix: mixed condition pricing rule not working on js side
2020-08-25 20:01:02 +05:30
Rohit Waghchaure
0407a435d7 fix: mixed condition pricing rule not working on js side 2020-08-25 19:59:17 +05:30
Saurabh
1c19050aa4 Merge pull request #23160 from rohitwaghchaure/pricing-rule-not-working-js-side
fix: mixed condition pricing rule not working on js side
2020-08-25 19:40:54 +05:30
Rohit Waghchaure
62876c708b fix: mixed condition pricing rule not working on js side 2020-08-25 18:32:46 +05:30
Rucha Mahabal
3fc00a5bfe fix: conversion factor for BOM exploded item rate 2020-08-25 18:09:14 +05:30
Saurabh
8245640ca2 Merge pull request #23149 from rohitwaghchaure/pricing-rule-encoding-pre-release-v12
fix(hot): Pricing Rule encoding fixed
2020-08-25 17:24:56 +05:30
Rohit Waghchaure
29d24cddcf fix(hot): Pricing Rule encoding fixed 2020-08-25 17:22:20 +05:30
Saurabh
9aa230c1b8 Merge pull request #23146 from rohitwaghchaure/pricing-rule-encoding-hotfix
fix(hot): Pricing Rule encoding fixed
2020-08-25 17:19:17 +05:30
Rohit Waghchaure
d223d4b829 fix(hot): Pricing Rule encoding fixed 2020-08-25 16:18:44 +05:30
Anurag Mishra
69bb85d1d0 fix: Form dashboard showing wrong balance 2020-08-25 06:55:02 +05:30
rohitwaghchaure
51c29cafee Merge pull request #23082 from ruchamahabal/fix-pos-print-v12
fix: Print Language for Customer not set for POS Invoice
2020-08-24 16:11:26 +05:30
rohitwaghchaure
6594d127cb Merge pull request #23113 from wojosc/patch-12
refactor: allow to rename lead
2020-08-24 16:09:14 +05:30
rohitwaghchaure
65d16d1ef6 Merge pull request #23117 from nabinhait/subcontracted-pr-gle-v12
fix: GLE for subcontracted PR is fg item rate is zero
2020-08-24 16:06:59 +05:30
Deepesh Garg
9b4cc0b6e9 fix: Update paid amount for pos return (#20543)
* fix: Paid amount updation for pos return

* fix: Remove console

* fix: Styling

* fix: get  default mode of payment from POS profile

* fix: Add test cases

* fix: Codacy
2020-08-24 15:01:57 +05:30
Saurabh
f843d6aa53 Merge branch 'v12-pre-release' into version-12 2020-08-24 10:11:45 +05:30
Saurabh
c6ed332903 bumped to version 12.11.0 2020-08-24 10:25:29 +05:50
Nabin Hait
ad07abeb3f chore: Added change log 2020-08-23 11:27:05 +05:30
Nabin Hait
a82d9d3182 test: Test case for GLE in subcontracted PR if FG item rate is zero 2020-08-22 20:07:19 +05:30
Nabin Hait
98fda4b8c3 fix: GLE for subcontracted PR is fg item rate is zero 2020-08-22 20:07:05 +05:30
Wolfram Schmidt
572d150925 Update lead_source.json
as requested in https://github.com/frappe/erpnext/pull/22872
2020-08-21 20:02:29 +02:00
Saqib
6c6175200c fix: cannot search items in offline pos (#23083) 2020-08-21 15:40:29 +05:30
rohitwaghchaure
6bb2038323 Merge pull request #23084 from nextchamp-saqib/pos-search-fix-pre-release
fix: cannot search items in offline pos
2020-08-21 13:13:50 +05:30
Nabin Hait
3d1df94ad2 fix:Validate Job offer against vacancies (#23108) 2020-08-20 19:39:03 +05:30
Nabin Hait
fa36a06188 fix:Validate Job offer against vacancies (#23107) 2020-08-20 19:38:55 +05:30
Deepesh Garg
c3c06c36a5 Merge pull request #23103 from deepeshgarg007/rcm_submit
fix: Unable to submit reverse charge invoice
2020-08-20 19:36:02 +05:30
Deepesh Garg
53aee98103 Merge pull request #23104 from deepeshgarg007/rcm_submit_v12
fix: Unable to submit reverse charge invoice
2020-08-20 19:35:48 +05:30
Marica
e52bd5c699 fix: Create Opoortunity without Default Company from Email (#23099)
* fix: Create Opoortunity without Default Company from Email

* fix: Add Prompt to Select Company

* Update communication.js

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-08-20 19:11:59 +05:30
Marica
685ca83b5e fix: Create Opoortunity without Default Company from Email (#23098)
* fix: Create Opoortunity without Default Company from Email

* fix: Add Prompt to Select Company

* Update communication.js

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-08-20 19:11:40 +05:30
Deepesh Garg
e1f9148351 Merge branch 'v12-pre-release' of https://github.com/frappe/erpnext into rcm_submit_v12 2020-08-20 16:49:43 +05:30
Deepesh Garg
093fb84377 fix: Unable to submit reverse charge invoice 2020-08-20 16:44:08 +05:30
Deepesh Garg
8c5bc5631a fix: Unable to submit reverse charge invoice 2020-08-20 16:31:38 +05:30
Rucha Mahabal
46456fdc19 Merge pull request #23095 from nextchamp-saqib/sr-no-selector-fix-hotfix
fix: val is not defined
2020-08-20 15:01:58 +05:30
Rucha Mahabal
737d305f45 Merge pull request #23094 from nextchamp-saqib/sr-no-selector-fix-pre-release
fix: val is not defined
2020-08-20 15:01:28 +05:30
Deepesh Garg
2aa23f91b1 Merge pull request #23092 from rohitwaghchaure/not-able-to-submit-dn-hotfix
fix: not able to submit delivery note
2020-08-20 13:35:40 +05:30
Saqib Ansari
2cc0ad51f5 fix: val is not defined 2020-08-20 12:54:36 +05:30
Saqib Ansari
193222a336 fix: val is not defined 2020-08-20 12:53:43 +05:30
rohitwaghchaure
46bea91469 Merge pull request #23093 from rohitwaghchaure/not-able-to-submit-dn
fix: not able to submit delivery note
2020-08-20 12:28:35 +05:30
Rohit Waghchaure
5e32ee8457 fix: not able to submit delivery note 2020-08-20 12:21:09 +05:30
Rohit Waghchaure
f09d472fcd fix: not able to submit delivery note 2020-08-20 12:20:23 +05:30
Suraj Shetty
4ff1a4361d refactor: Format and sanitise user inputs to search queries. (#23091)
* refactor: Sanitize whitelisted method inputs

Co-authored-by: Prssanna Desai <prssud@gmail.com>
Co-authored-by: Shivam Mishra <scmmishra@users.noreply.github.com>

* refactor: Format and sanitize tax_account_query inputs

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
Co-authored-by: Prssanna Desai <prssud@gmail.com>
Co-authored-by: Shivam Mishra <scmmishra@users.noreply.github.com>

* refactor: Validate and sanitize search inputs via decorator

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
Co-authored-by: Prssanna Desai <prssud@gmail.com>
Co-authored-by: Shivam Mishra <scmmishra@users.noreply.github.com>

* style: Minor formatting fix

* refactor: Validate and sanitize search inputs using decorator

* fix: Typo

* fix: Remove unwanted import statement

* refactor: Repalce validate_and_sanitize_search_inputs() with validate_and_sanitize_search_inputs

Co-authored-by: Prssanna Desai <prssud@gmail.com>
Co-authored-by: Shivam Mishra <scmmishra@users.noreply.github.com>

Co-authored-by: Prssanna Desai <prssud@gmail.com>
Co-authored-by: Shivam Mishra <scmmishra@users.noreply.github.com>
Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-08-20 11:27:19 +05:30
Suraj Shetty
bf7adb8b38 refactor: Format and sanitise user inputs to search queries. (#23064)
* refactor: Sanitize whitelisted method inputs

Co-authored-by: Prssanna Desai <prssud@gmail.com>
Co-authored-by: Shivam Mishra <scmmishra@users.noreply.github.com>

* refactor: Format and sanitize tax_account_query inputs

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
Co-authored-by: Prssanna Desai <prssud@gmail.com>
Co-authored-by: Shivam Mishra <scmmishra@users.noreply.github.com>

* refactor: Validate and sanitize search inputs via decorator

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
Co-authored-by: Prssanna Desai <prssud@gmail.com>
Co-authored-by: Shivam Mishra <scmmishra@users.noreply.github.com>

* style: Minor formatting fix

* refactor: Validate and sanitize search inputs using decorator

* fix: Typo

* fix: Remove unwanted import statement

* refactor: Repalce validate_and_sanitize_search_inputs() with validate_and_sanitize_search_inputs

Co-authored-by: Prssanna Desai <prssud@gmail.com>
Co-authored-by: Shivam Mishra <scmmishra@users.noreply.github.com>

Co-authored-by: Prssanna Desai <prssud@gmail.com>
Co-authored-by: Shivam Mishra <scmmishra@users.noreply.github.com>
Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-08-20 11:17:30 +05:30
Deepesh Garg
c7c72b76c6 Merge pull request #23087 from deepeshgarg007/rcm_deduction_hotfix
fix: Do not update total for RCM invvoices if net taxes are zero
2020-08-19 18:39:09 +05:30
Deepesh Garg
6aa3191cad fix: Do not update total for RCM invvoices if net taxes are zero 2020-08-19 18:36:43 +05:30
Deepesh Garg
848288ed36 Merge pull request #23086 from deepeshgarg007/rcm_deduction
fix: Do not update total for RCM invvoices if net taxes are zero
2020-08-19 18:34:27 +05:30
Deepesh Garg
1c29da32fc fix: Do not update total for RCM invvoices if net taxes are zero 2020-08-19 18:30:18 +05:30
Saqib Ansari
1fdfcc00d6 fix: cannot search items in offline pos 2020-08-19 17:26:38 +05:30
Rucha Mahabal
f61a8bc889 fix: Print Language for Customer not set for POS Invoice 2020-08-19 16:18:06 +05:30
Deepesh Garg
1bf9de1527 feat: JSON download for HSN wise outward summary 2020-08-19 15:46:15 +05:30
Deepesh Garg
dae1fad1a6 fix: Total calculations for multicurrency RCM invoices (#23070) 2020-08-18 19:32:38 +05:30
Deepesh Garg
af45eceeb2 fix: Total calculations for multicurrency RCM invoices (#23071) 2020-08-18 19:32:00 +05:30
Anupam Kumar
09b6628e53 fix: Leave application status (#23043)
* fix: leave application status update fix

* fix:adding patch
2020-08-18 14:43:34 +05:30
Deepesh Garg
693bf1309a Merge pull request #23021 from prssanna/expense-claim-list-currency-v12
fix: add company in list fields to fetch for Expense Claim
2020-08-17 23:08:27 +05:30
Afshan
df42df5246 fix: handled condition if staffing isn't created (#23057)
Co-authored-by: Marica <maricadsouza221197@gmail.com>
2020-08-17 13:48:07 +05:30
Afshan
4b6d5ef6aa fix: the JSON object must be str, bytes or bytearray, not "list" (#23053) 2020-08-17 12:25:34 +05:30
Rucha Mahabal
5e51a1fe9b Merge pull request #23037 from aakvatech/patch-5
fix: AttributeError: 'ProgramFee' object has no attribute 'course'
2020-08-14 13:21:45 +05:30
aakvatech
2cdc0c3f2d fix: AttributeError: 'ProgramFee' object has no attribute 'course'
Error occurs while program enrollment on new student.

This happens on version 12 and not on version 13.

Version 12 uses
course_list = [course.course for course in program.get_all_children()]

where as version 13 uses
course_list = [course.course for course in program.courses]

erpnext/erpnext/education/doctype/program_enrollment/program_enrollment.py
2020-08-14 10:26:54 +03:00
Rucha Mahabal
7234330db9 Merge pull request #23032 from frappe/revert-23031-patch-4
Revert "fix: AttributeError: 'ProgramFee' object has no attribute 'course'"
2020-08-14 11:21:49 +05:30
Rucha Mahabal
2fa9b3a713 Revert "fix: AttributeError: 'ProgramFee' object has no attribute 'course'" 2020-08-14 11:21:23 +05:30
Rucha Mahabal
1aec8f57f2 Merge pull request #23031 from aakvatech/patch-4
fix: AttributeError: 'ProgramFee' object has no attribute 'course'
2020-08-14 11:21:18 +05:30
aakvatech
24f350a1a8 fix: AttributeError: 'ProgramFee' object has no attribute 'course'
Error occurs while program enrollment on new student.

This happens on version 12 and not on version 13.

Version 12 uses
course_list = [course.course for course in program.get_all_children()]

where as version 13 uses
course_list = [course.course for course in program.courses]

erpnext/erpnext/education/doctype/program_enrollment/program_enrollment.py
2020-08-14 06:59:44 +03:00
prssanna
c28e37781a fix: add company in list fields to fetch for Expense Claim 2020-08-13 11:56:30 +05:30
prssanna
4d8340d7b9 feat: enable total row in Gross Profit Report 2020-08-13 11:40:56 +05:30
Nabin Hait
387e0878b6 Merge branch 'version-12-hotfix' into v12-pre-release 2020-08-13 09:28:27 +05:30
Saqib
6c536bb6e0 fix: cannot change customer fields if credit exhausted (#22838)
* fix: cannot change customer fields if credit exhausted

* fix: order based on company

* fix: sort current limits w.r.t company before compare

* fix: CustomerCreditLimit Object is not subscriptable
2020-08-13 09:27:50 +05:30
Nabin Hait
0a061d4082 Merge branch 'version-12-hotfix' into v12-pre-release 2020-08-13 09:23:22 +05:30
Nabin Hait
af9d653fb4 Inclusive tax based on item quantity v12 (#23015)
* fix: Calculate taxes if tax is based on item quantity and inclusive on item price

* git commit -m "fix: Add missing semicolon"

* fix: Calculate taxes if tax is based on item quantity

Co-authored-by: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com>
2020-08-13 09:22:05 +05:30
Anupam Kumar
8969e19594 fix: Opportunity/Quotation Lost Reason Fix (#23016) 2020-08-13 09:19:53 +05:30
Deepesh Garg
a979fcc25b fix: Project link not set in accounts other than profit and loss accounts (#22049)
* fix: project link not set in accounts other than profilt and loss accounts

* fix: cannot find get_allow_cost_center_in_entry_of_bs_account

* fix: remove enable_allow_cost_center_in_entry_of_bs_account

* chore: add tests and remove test based on allow_cost_center_for_bs_accounts

* fix: travis

* fix: Test Cases

* fix: Patch to remove Property Setter

* fix: Test Cases

* fix: Remove v13 patch

* fix: Procurement Tracker test case

* fix: Proccurement tracker report test

* fix: Codacy

* fix: Remove duplicate project field

Co-authored-by: Saqib Ansari <nextchamp.saqib@gmail.com>
Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2020-08-12 21:06:25 +05:30
Rucha Mahabal
9bd041c14c fix: don't set asset maintenance log status as Overdue when Completed or Cancelled (#23012)
* fix: don't set maintenance log status as Overdue when Completed or Cancelled

* fix: set draft indicators for Asset Maintenance Log listview
2020-08-12 19:34:54 +05:30
Deepesh Garg
1bb6ef3356 fix: Update state code and union territory for Daman and Diu (#22989)
Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2020-08-12 19:34:23 +05:30
Deepesh Garg
5cc5bc246a fix: Cancellation of accounting transactions within closed accounting period (#22986) 2020-08-12 17:56:57 +05:30
Rucha Mahabal
2a0ed798aa fix: TypeError while setting transfer_qty in Stock Entry (#22931)
Co-authored-by: Marica <maricadsouza221197@gmail.com>
2020-08-12 14:32:23 +05:30
Marica
033d98cd68 Merge pull request #23002 from AfshanKhan/fix-duplicate-dropdown-PO-v12
fix: moved custom_make_buttons to PurchaseOrderController to avoid duplication of dropdown options
2020-08-11 20:57:11 +05:30
Afshan
ec3dc95e6e fix: moved custom_make_buttons to PurchaseOrderController to avoid duplication of dropdown options 2020-08-11 20:42:16 +05:30
Deepesh Garg
57d86a3bc1 Merge pull request #22973 from marination/opportunity-from-email-fix
fix: Creating opportunity from email
2020-08-10 16:12:00 +05:30
marination
265916543d fix: Creating opportunity from email 2020-08-10 14:42:30 +05:30
Deepesh Garg
f63182df5a Merge pull request #22968 from deepeshgarg007/gst_export_wopay_v12
fix: GSTR 1 report for exports without payment of Tax
2020-08-10 11:29:56 +05:30
Deepesh Garg
9d455cf4a6 fix: GSTR 1 report for exports without payment of Tax 2020-08-10 09:57:28 +05:30
Deepesh Garg
ff9c40fde6 Merge pull request #22730 from deepeshgarg007/gstr3b_cess_v12_hotfix
fix: Multiple GST fixes
2020-08-08 22:15:25 +05:30
Deepesh Garg
f0f20bb434 fix: RCM taxable value in GSTR3b report 2020-08-08 20:58:01 +05:30
Deepesh Garg
bbfd5a4cc8 fix: Test Cases 2020-08-08 19:46:09 +05:30
Deepesh Garg
3ae4a83504 fix: Add print for debug 2020-08-08 17:52:45 +05:30
Deepesh Garg
1da33f4129 fix: Test case 2020-08-07 22:51:33 +05:30
Deepesh Garg
b8eff8d141 fix: Test case 2020-08-07 22:02:14 +05:30
Deepesh Garg
67e07b69dd fix: More print statements for debugging 2020-08-07 15:59:03 +05:30
Deepesh Garg
7d262d1a08 Merge pull request #22928 from deepeshgarg007/paymemnt_reco_multi_v12
fix: Multi currency payment reconciliation
2020-08-07 15:36:37 +05:30
Deepesh Garg
5f8e8a3c29 fix: Add debug print statement 2020-08-07 10:05:22 +05:30
Deepesh Garg
2169bb7640 fix: Add print 2020-08-06 23:16:33 +05:30
Deepesh Garg
33754b9dc8 fix: add print for debug 2020-08-06 22:17:37 +05:30
Deepesh Garg
bb451360ba Merge branch 'version-12-hotfix' of https://github.com/frappe/erpnext into gstr3b_cess_v12_hotfix 2020-08-06 21:50:27 +05:30
Afshan
2cf4168009 fix: show or hide section or attributes depending on other attributes… (#22933)
* fix: show or hide section or attributes depending on other attributes for better UI

* fix:depends on: batch_no for retain sample

Co-authored-by: Marica <maricadsouza221197@gmail.com>
2020-08-06 19:04:35 +05:30
Shivam Mishra
80c016a1ad Merge pull request #22935 from scmmishra/fix-product-configurator
fix: handle product_info null in item_configure
2020-08-06 11:39:32 +00:00
Shivam Mishra
3619628e60 fix: handle product_info null 2020-08-06 17:08:14 +05:30
Marica
e2e9633ee1 Merge pull request #22927 from deepeshgarg007/price_rule_fix_v12
fix: Error due to comma in Pricing rule name
2020-08-06 16:57:18 +05:30
Shivam Mishra
abd111331e Merge pull request #22934 from scmmishra/fix-product-configurator
fix: unbound error in product configurator 61b9727
2020-08-06 11:22:39 +00:00
Shivam Mishra
61b9727fa2 fix: unbound error in product configurator 2020-08-06 16:47:43 +05:30
Marica
74643c804d Merge branch 'version-12-hotfix' into price_rule_fix_v12 2020-08-06 14:38:59 +05:30
Marica
e7162a86c6 Merge pull request #22930 from deepeshgarg007/payment_reco_cost_center_fix_v12
fix: Add default cost center in payment reconciliation JV
2020-08-06 14:31:52 +05:30
Deepesh Garg
b3cdd91d65 fix: Tests 2020-08-06 13:18:42 +05:30
Deepesh Garg
dd64cd1512 fix: Add default cost center in payment reconciliation JV 2020-08-06 11:46:25 +05:30
Deepesh Garg
024c78f522 fix: Hide currency link fields 2020-08-06 11:31:00 +05:30
Deepesh Garg
e6ec549c81 fix: Multi currency payment reconciliation 2020-08-06 11:30:46 +05:30
Deepesh Garg
579167d1de fix: Remove print statement 2020-08-06 11:27:27 +05:30
Deepesh Garg
10604664d4 fix: Error due to commma in Pricing rule name 2020-08-06 11:27:18 +05:30
Marica
97e96cec51 fix: get_regional_address_details missing positional arg 'company' (#22920) 2020-08-05 17:10:50 +05:30
Anurag Mishra
d2593f0af5 fix: set half day date None if half day is unchecked (#22905) 2020-08-04 19:29:56 +05:30
Rucha Mahabal
02fcf569e6 fix: SQL query in accounts receivable, payable reports (#22891) 2020-08-03 20:41:37 +05:30
Marica
9afebd311f fix: Reposition description in Warehouse (#22896) 2020-08-03 20:39:41 +05:30
Rucha Mahabal
4f6a25539a fix: TypeError while concatenating account number and name in COA (#22886) 2020-08-03 15:51:39 +05:30
Nabin Hait
ec46b8c73b fix: Bank Clearance of POS purchase invoice (#22884) 2020-08-03 15:39:29 +05:30
Marica
a9cfe0c95c fix: Misleading description in Warehouse (#22879) 2020-08-03 14:56:46 +05:30
Rucha Mahabal
3d2fa59e6d fix(Education): course wise assessment report labels (#22805)
* fix: add labels to chart datasets in course wise assessment

* fix: change label for final grade

Co-authored-by: Marica <maricadsouza221197@gmail.com>
2020-07-30 13:27:47 +05:30
Rucha Mahabal
b2eddedfb1 fix: change owner fieldtype to Link in Lead Owner Efficiency Report (#22840) 2020-07-29 17:19:11 +05:30
Marica
b7efef79dc fix: Exploded Item Rate (#22816) 2020-07-27 14:54:59 +05:30
bhavesh95863
ca6ec72ade fix: Add missing translation function for freeze message (#22812)
* fix[missing translation function]

missing translation function

* fix[missing translation function for message]

missing translation function for message

* fix[add missing translation function]

add missing translation function

* Update payroll_entry.js

Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2020-07-26 14:03:03 +05:30
Deepesh Garg
680f2600be Merge pull request #22303 from P-Froggy/fix-payment-entry-wrong-bank-account-fetch-v12
fix: Set Value of wrong Bank Account Field in Payment Entry
2020-07-24 19:04:51 +05:30
Mangesh-Khairnar
af9e44f059 fix(payment-request): do not set guest as administrator (#22803) 2020-07-24 18:18:49 +05:30
Deepesh Garg
dde92e0fdb Merge branch 'version-12-hotfix' of https://github.com/frappe/erpnext into gstr3b_cess_v12_hotfix 2020-07-24 14:09:02 +05:30
Deepesh Garg
d9aa115aaf fix: Remove print and add Cess account 2020-07-24 14:08:05 +05:30
rohitwaghchaure
6a48988c76 fix: incorrect stock value in return case (#22528)
Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-07-24 10:46:55 +05:30
Nabin Hait
e62da1455c fix: Other charges on income tax (#22798) 2020-07-24 09:20:41 +05:30
Anupam Kumar
45a3580c20 Unable to change link from new lead to existing customer (#22795) 2020-07-24 08:53:24 +05:30
Rucha Mahabal
9abb8e309e fix(Education): descriptions not copied while creating fees from fee structure (#22793) 2020-07-24 08:52:53 +05:30
Rucha Mahabal
c5b6ba7556 Merge pull request #22794 from Mangesh-Khairnar/maintainance-visit-fixes
fix(maintenance-visit): change fieldtype of status to select
2020-07-24 00:10:09 +05:30
Mangesh-Khairnar
cf902a1e2d fix(maintenance-visit): change fieldtype of status to select 2020-07-23 23:55:58 +05:30
Sun Howwrongbum
4e3df334db fix: [v12] incorrect available_qty being set (#22541)
* fix: incorrect available_qty being set

* style: descriptive variables
2020-07-23 20:06:15 +05:30
Deepesh Garg
9067adf141 fix: Add print for debug 2020-07-23 19:48:18 +05:30
Deepesh Garg
d18eb0e63e Merge pull request #22702 from pipech/v12-hotfix-transaction_pricing_rule
fix(pricing): transaction pricing rule
2020-07-23 19:19:55 +05:30
aakvatech
3adeab45ce Update transaction.js (#22788)
Added frappe.flags.hide_serial_batch_dialog = true that was missing in version-12
2020-07-23 12:14:36 +05:30
Marica
0fee76f409 fix: Dont merge items if both exist in stock reco (#22784)
* fix: incorrect stock after merging items

* fix: Readability fix

Co-authored-by: Rohit Waghchaure <rohitw1991@gmail.com>
2020-07-22 18:53:40 +05:30
Marica
74963e9711 fix: Serial No Rename does not affect Stock Ledger Entry (#22780) 2020-07-22 18:17:25 +05:30
Deepesh Garg
7d8887916e Merge pull request #22755 from deepeshgarg007/hsn_code_report_v12
fix: Tax amounts in HSN Wise Outward summary
2020-07-22 16:26:38 +05:30
Deepesh Garg
e077c0a270 fix: Randomly failing test (#22773)
* fix(travis): Item Tax tempate template test

* fix: Update tests

* fix: Test

* fix: Do not use random customers

* fix: Remove every bit of randomness from this test
2020-07-22 16:12:12 +05:30
Afshan
517108d10a fix: added set query for filters in MR (#22770)
Co-authored-by: Marica <maricadsouza221197@gmail.com>
2020-07-22 15:51:48 +05:30
Afshan
a7094641d4 style: moved parent warehouse to top section also added a section break for better look (#22772)
Co-authored-by: Marica <maricadsouza221197@gmail.com>
2020-07-22 15:45:37 +05:30
Deepesh Garg
ad94f7516b Merge pull request #22763 from DeeMysterio/fs-hotfix
fix(report): fix alignment in script report that extends financial_statements.js
2020-07-22 14:46:08 +05:30
Diksha Jadhav
1943d413a4 fix(report): fix alignment in script report that extends financial_statements.js 2020-07-21 17:59:12 +05:30
Deepesh Garg
82fbef4163 fix: Remove print statements 2020-07-21 14:54:21 +05:30
Deepesh Garg
83ad6df229 fix: Tax amounts in HSN Wise Outward summary 2020-07-21 14:25:51 +05:30
Deepesh Garg
b9ba63b0d8 fix: Test Case 2020-07-20 18:08:56 +05:30
Marica
bc248bfaed Merge pull request #22646 from rohitwaghchaure/incorrect-qty-after-transaction-stock-reco
fix: incorrect qty after transaction in stock ledger entry
2020-07-20 15:51:37 +05:30
Deepesh Garg
0b31d6f630 fix: Test Case 2020-07-20 13:34:46 +05:30
Marica
7e461ce860 Merge branch 'version-12-hotfix' into incorrect-qty-after-transaction-stock-reco 2020-07-20 11:34:29 +05:30
Deepesh Garg
6946796047 fix: Tests 2020-07-19 22:23:13 +05:30
Deepesh Garg
6d39066d86 fix: Multiple fixes in GST 2020-07-17 22:20:04 +05:30
Deepesh Garg
b6da70efec fix: Cess amount in GSTR 3B report 2020-07-17 22:19:34 +05:30
rohitwaghchaure
3068fe43c6 Merge branch 'version-12-hotfix' into v12-hotfix-transaction_pricing_rule 2020-07-17 14:46:45 +05:30
rohitwaghchaure
7709c640b5 Merge pull request #22720 from rohitwaghchaure/fixed-incorrect-currency-symbol-in-stock-balance-report
fix: currency symbol not showing as per company currency in stock bal…
2020-07-17 14:35:13 +05:30
rohitwaghchaure
8558e26648 Merge pull request #22716 from rohitwaghchaure/fixed-actual-qty-showing-as-zero
Fix: Stock Reconciliation Invalid Quantity for Batched Item
2020-07-17 12:07:45 +05:30
Rohit Waghchaure
34a91bd7e6 fix: currency symbol not showing as per company currency in stock balance 2020-07-17 11:24:40 +05:30
rohitwaghchaure
785462b513 Update stock_reconciliation.js 2020-07-17 10:55:05 +05:30
Rohit Waghchaure
0d5f8c5050 fix: for past dated stock reco, batched item showing the current available qty instead of quantity as per posting date 2020-07-17 08:57:31 +05:30
Deepesh Garg
13088173eb Merge pull request #22703 from deepeshgarg007/rcm_ountry_fix
fix: Update RCM only for indian countries
2020-07-15 23:58:16 +05:30
Deepesh Garg
c10dd29282 fix: Update RCM only for indian countries 2020-07-15 23:57:03 +05:30
Deepesh Garg
95da4157a9 Merge pull request #22699 from rohitwaghchaure/fixed-not-able-to-submit-si-hotfix
fix: not able to submit sales invoice
2020-07-15 21:50:45 +05:30
Poranut Chollavorn
1411d002f6 fix(pricing): transaction pricing rule
add tranasction type to transaction pricing rule selector
2020-07-15 14:55:20 +00:00
rohitwaghchaure
0f2601a6a6 Merge pull request #22649 from rohitwaghchaure/fixed-incorrect-balance-qty-in-stock-ledger
fix: incorrect balance qty in stock ledger report
2020-07-15 17:20:04 +05:30
Rohit Waghchaure
c785ff9874 fix: not able to submit sales invoice 2020-07-15 17:17:23 +05:30
rohitwaghchaure
ab9cbc1e2c Merge pull request #22690 from marination/pr-project-stock-ledger-fix
fix: Added Project Field in Purchase Receipt for Stock Ledger Tagging
2020-07-15 15:42:58 +05:30
rohitwaghchaure
237dc4250a Merge pull request #22667 from ruchamahabal/fix-healthcare-setup-v12
fix(Healthcare): set company in Healthcare Service Unit setup
2020-07-15 14:33:42 +05:30
marination
71fa045ba4 fix: Added Project Field in Purchase Receipt for Stock Ledger Tagging 2020-07-15 12:54:19 +05:30
rohitwaghchaure
5e5e82a9f6 Merge pull request #22684 from nabinhait/company-abbr-replace
fix: Replace company abbr
2020-07-15 12:31:19 +05:30
Deepesh Garg
898dafe748 fix: Period list fixes in financial statements (#22679) 2020-07-15 12:17:23 +05:30
Deepesh Garg
1f7e941d68 fix(GST): Do not add tax amount in grand total for reverse charge invoices (#22686)
* fix(GST): Do not add tax amount in grand total for reverse charge invoices

* fix: Code cleanup

* fix: Remove print statements
2020-07-14 22:05:45 +05:30
Nabin Hait
395f44c8b7 fix: Replace company abbr 2020-07-14 20:46:25 +05:30
Rucha Mahabal
ea1662c15b Merge pull request #22659 from bhavesh95863/patch-3
fix: Quotation list view blank if quotation_to field not set as a standard filter
2020-07-13 19:55:54 +05:30
bhavesh95863
fd8c856af2 fix:whitespace missing
Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2020-07-13 18:48:47 +05:30
Rucha Mahabal
672497ba63 Merge branch 'version-12-hotfix' into fix-healthcare-setup-v12 2020-07-13 16:39:05 +05:30
Rucha Mahabal
9c410d8a27 fix(Healthcare): set company in Healthcare Service Unit setup 2020-07-13 16:27:59 +05:30
Deepesh Garg
8317ff8bb2 Merge branch 'version-12-hotfix' into fixed-incorrect-balance-qty-in-stock-ledger 2020-07-12 17:21:25 +05:30
bhavesh95863
70eb94280e fix: Quotation list view blank if quotation_to field not set as a standard filter
fix: Quotation list view  blank if quotation_to field not set as a standard filter
2020-07-12 02:59:00 +05:30
Deepesh Garg
fef3a79bf5 Merge pull request #22656 from sagarvora/fix-ewaybill-json
fix: ewaybill json had json dump of json dump, and other related fixes
2020-07-11 21:41:44 +05:30
Sagar Vora
9bcf4eb5bc fix: ewaybill json had json dump of json dump, and other related fixes 2020-07-11 17:44:20 +05:30
Marica
1696a8982c fix: Pricing Rule breaks if no item_code (#22653) 2020-07-10 22:51:32 +05:30
rohitwaghchaure
65cc0e5200 Merge branch 'version-12-hotfix' into v12-hotfix-get_pricing_rule 2020-07-10 21:46:13 +05:30
Rohit Waghchaure
671fc7b919 fix: incorrect balance qty in stock ledger report 2020-07-10 18:11:04 +05:30
Rohit Waghchaure
335815a75d fix: incorrect qty after transaction in stock ledger entry 2020-07-10 14:23:12 +05:30
rohitwaghchaure
b856548d47 fix: incorrect delivered qty in Supplier-Wise Sales Analytics (#22642)
Co-authored-by: Marica <maricadsouza221197@gmail.com>
2020-07-10 13:19:57 +05:30
rohitwaghchaure
1c26bf5b07 fix: Due to decimal issue make purchase receipt button not showing from PO (#22643)
Co-authored-by: Marica <maricadsouza221197@gmail.com>
2020-07-10 13:17:22 +05:30
Anurag Mishra
b3041ea7cd fix: fix: not working without from_amount and percentage_deduction (#22399) 2020-07-10 13:09:21 +05:30
Marica
ea7625556c fix: General Message in previous doc validation for buying and selling (#22639) 2020-07-10 12:36:47 +05:30
Marica
8e887f39a2 fix: Skip Progress and Completed by fields on Task Duplication (#22640) 2020-07-10 12:31:08 +05:30
Marica
8f038aa832 fix: Error message in Product Bundle (#22638) 2020-07-10 12:13:14 +05:30
Rohan
fda2633e40 fix: fetch project-related info in Timesheet (#22422)
Co-authored-by: Marica <maricadsouza221197@gmail.com>
2020-07-09 11:38:20 +05:30
Rohan
817040b986 fix: setup status indicators for Job Offer and Job Applicant (#22444)
Co-authored-by: Marica <maricadsouza221197@gmail.com>
2020-07-08 15:59:44 +05:30
rohitwaghchaure
45624cae77 Merge pull request #22599 from frappe/shopping-cart-issue
refactor: shopping cart
2020-07-08 13:09:59 +05:30
Anupam K
8c48a6a865 upating shopping_cart.js 2020-07-08 12:22:45 +05:30
Deepesh Garg
adb7c16349 Merge pull request #22604 from frappe/mergify/bp/version-12-hotfix/pr-22601
fix: Payment reco error in multi-company setup (bp #22601)
2020-07-07 18:41:44 +05:30
Marica
aafd674c07 Merge pull request #22606 from Thunderbottom/whitelist-query-v12
fix: whitelist all query functions for search widget
2020-07-07 16:51:45 +05:30
Chinmay D. Pai
75c2e28788 fix: whitelist all query functions for search widget
Signed-off-by: Chinmay D. Pai <chinmaydpai@gmail.com>
2020-07-07 16:46:04 +05:30
Anupam K
321aba642d shopping cart issue fixes 2020-07-07 15:46:44 +05:30
Deepesh Garg
402b273d9b fix: Pass company
(cherry picked from commit ca351e3549)
2020-07-07 10:06:49 +00:00
Deepesh Garg
e0a9ae612e fix: Payment reco error in multicompany setup
(cherry picked from commit e2fdff5777)
2020-07-07 10:06:49 +00:00
bhavesh95863
b9e4df557b fix: Message field mandtory even after unchecking collect progress checkbox (#22594)
Message field mandtory even after unchecking collect progress checkbox
2020-07-06 18:29:37 +05:30
rohitwaghchaure
632bd3b310 Merge pull request #22587 from frappe/mergify/bp/version-12-hotfix/pr-22577
fix: Consider company fiscal year for getting balance (bp #22577)
2020-07-06 13:20:54 +05:30
Deepesh Garg
735d7f732d fix: Consider company fiscal for getting balalnce
(cherry picked from commit 96d40ec9da)
2020-07-06 07:09:00 +00:00
Deepesh Garg
455c1dda7a Merge pull request #22575 from deepeshgarg007/uae_item_tax_fix_v12
fix: Update item tax only if item code available
2020-07-03 21:33:35 +05:30
Deepesh Garg
1b18bf0388 fix: Update item tax only if item code available 2020-07-03 21:28:40 +05:30
Faris Ansari
336ca33a98 fix: Set label if domains is set (#22523) 2020-07-02 14:58:20 +05:30
Afshan
3c0f38fa02 fix: made "Subscription Section", "Auto Repeat" and Hub Publishing" collapsible by default (#22535) 2020-07-02 14:52:50 +05:30
Sahil Khan
fd30b8f422 Merge branch 'v12-pre-release' into version-12 2020-07-02 13:06:22 +05:30
Sahil Khan
30e33bef9e bumped to version 12.10.1 2020-07-02 13:26:22 +05:50
sahil28297
67e64dbc01 Merge pull request #22552 from frappe/mergify/bp/v12-pre-release/pr-22347
Revert "fix: update remark on submitting payment entry" (bp #22347)
2020-07-02 12:00:44 +05:30
Deepesh Garg
bae83679c0 Revert "fix: update remark on submitting payment entry"
(cherry picked from commit 7d035fac36)
2020-07-02 06:29:15 +00:00
Marica
0c12c7339c Merge pull request #22538 from frappe/mergify/bp/version-12-hotfix/pr-22496
fix: take parent cost center for child if no cost center at child (bp #22496)
2020-07-01 17:00:22 +05:30
Anurag Mishra
bf8a26c31e fix: take parent cost center for child if no cost center at child (#22496)
(cherry picked from commit ba5f571b0d)
2020-07-01 11:07:57 +00:00
Marica
fb36d01dd2 Merge pull request #22534 from frappe/mergify/bp/version-12-hotfix/pr-22512
feat: Autofill Supplier pop-up when only 1 Supplier in RFQ (bp #22512)
2020-07-01 12:09:46 +05:30
Afshan
c8d6e8f19d feat: Autofill Supplier pop-up when only 1 Supplier in RFQ
(cherry picked from commit 06f11aba0b)
2020-07-01 06:32:19 +00:00
mergify[bot]
4e17c40589 fix: Refactor dashboard links in leave policy (bp #22519) (#22529)
* fix: Refactor dashboard links in leave policy (#22519)

* fix: refactor dashboard links in leave policy

* fx: code fix

* fix: add labels to links

* fix: code change

(cherry picked from commit ec84afa86d)

* fix: remove extra lines

Co-authored-by: Michelle Alva <50285544+michellealva@users.noreply.github.com>
Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2020-06-30 18:12:47 +05:30
Marica
5d5cb0ac28 fix: Set Root as Parent if no parent in new tree view node (#22507) 2020-06-30 08:21:07 +05:30
mergify[bot]
a20957d355 fix: handle nonetype issue for packed items (#22493) (#22494)
(cherry picked from commit 73edba0e10)

Co-authored-by: Mangesh-Khairnar <mkhairnar10@gmail.com>
2020-06-29 17:36:43 +05:30
rohitwaghchaure
f8efa0508a Merge pull request #22490 from frappe/mergify/bp/version-12-hotfix/pr-22488
fix: letter head not found in opening invoice creation tool (bp #22488)
2020-06-28 21:22:04 +05:30
Rohit Waghchaure
6aa7c404f8 fix: letter head not found in opening invoice creation tool
(cherry picked from commit 04cf2e029f)
2020-06-28 14:36:56 +00:00
rohitwaghchaure
105a44e0d2 Merge pull request #22484 from frappe/mergify/bp/version-12-hotfix/pr-22450
fix: Disable Renaming in Serial No (bp #22450)
2020-06-28 16:29:45 +05:30
rohitwaghchaure
7919ced975 Merge pull request #22372 from rohitwaghchaure/fixed-completed-qty-not-updated-in-wo
fix: completed qty not updated in work order
2020-06-28 15:22:56 +05:30
rohitwaghchaure
848e7c2e0e Merge pull request #22485 from frappe/mergify/bp/version-12-hotfix/pr-22478
fix: apply filters in all transactions (bp #22478)
2020-06-28 15:17:59 +05:30
rohitwaghchaure
104b317929 fix: indentation
(cherry picked from commit d5736711ce)
2020-06-28 09:45:01 +00:00
michellealva
ad0aca2fc6 fix: apply filters in all transactions
(cherry picked from commit 08f842d12e)
2020-06-28 09:45:01 +00:00
marination
584a8f52a5 fix: Disable Renaming in Serial No
(cherry picked from commit f311f52f6e)
2020-06-28 05:48:56 +00:00
rohitwaghchaure
48e2baa903 Merge pull request #22475 from frappe/mergify/bp/version-12-hotfix/pr-22468
fix: enable show_configure_button when shopping cart is enabled (bp #22468)
2020-06-28 11:13:34 +05:30
rohitwaghchaure
43d539f321 Merge pull request #22460 from britlog/version-12-hotfix
fix: product_info
2020-06-28 11:05:55 +05:30
britlog
0e7ab9526c fix: product_info 2020-06-26 15:24:23 +02:00
rohitwaghchaure
8a542bc9df Update shopping_cart_settings.js
(cherry picked from commit 13e8c32172)
2020-06-26 11:54:16 +00:00
Anupam K
e73dec158b enable show_configure_button when shopping cart is enabled
(cherry picked from commit 27d27c924d)
2020-06-26 11:54:16 +00:00
Deepesh Garg
2155916001 Merge pull request #22457 from frappe/clarkejj-patch-2
fix: stock_ageing.py report convert string numeric to flt value
2020-06-26 17:03:56 +05:30
rohitwaghchaure
78383c3133 Merge pull request #22469 from marination/production-plan-ux-hotfix
fix: Handling Empty tables in Production Plan
2020-06-26 15:06:14 +05:30
marination
cfc47bc011 fix: Sales Order to MR test failing
- Items to Manufacture can come as doc.items from Sales Order
2020-06-26 14:06:04 +05:30
marination
6bd8c048b6 fix: Handling Empty tables in Production Plan 2020-06-26 12:53:05 +05:30
mergify[bot]
6089023df7 fix: grammatical fix on error message (#22464)
Before:

Fiscal Year: 2020 does not exists

After:

Fiscal Year 2020 Does Not Exist

(cherry picked from commit 978722621f)

Co-authored-by: Michelle Alva <50285544+michellealva@users.noreply.github.com>
2020-06-26 09:23:36 +05:30
John Clarke
3cb8173034 fix: stock_ageing.py report convert string numeric to flt value
This PR combines these two PRs below applied to develop, to be applied to version-12-hotfix

(The mergifyio process failed for some reason.)

https://github.com/frappe/erpnext/pull/22430

https://github.com/frappe/erpnext/pull/22206
2020-06-25 07:30:10 -06:00
mergify[bot]
d9e7b480eb fix: cannot cancel assets with repair pending (#22440) (#22453)
* fix: cannot cancel assets with repair pending

* fix: message

* Update asset.py

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
(cherry picked from commit 40847aeac3)

Co-authored-by: Saqib <nextchamp.saqib@gmail.com>
2020-06-25 17:11:36 +05:30
Marica
7691937e59 Merge pull request #22437 from sunhoww/patch-2
fix: [v12] UnboundLocalError when setting product_info
2020-06-25 14:23:13 +05:30
Sun Howwrongbum
99a9f98688 fix: UnboundLocalError when setting product_info 2020-06-25 10:05:00 +05:30
Deepesh Garg
7b23cb27ce Merge pull request #22435 from frappe/mergify/bp/version-12-hotfix/pr-22434
fix: change error messages in RFQ (bp #22434)
2020-06-25 09:29:10 +05:30
Michelle Alva
c28adea2d0 fix: change error messages in RFQ
Before:
1. Row {0}: For supplier {0} Email Address is required to send email
2. Email sent to supplier
3. Request for Quotation is disabled to access from portal, for more check portal settings.
4. Supplier Quotation created.


After:
1. Row {0}: For Supplier {0} Email Address is Required to Send Email
2. Email sent to Supplier
3. The Access to Request for Quotation From Portal is Disabled. To Allow Access, Enable it in Portal Settings.
4. Supplier Quotation Created.

(cherry picked from commit da0385cd39)
2020-06-25 03:52:13 +00:00
Marica
57804e9d87 Merge pull request #22427 from frappe/mergify/bp/version-12-hotfix/pr-22410
fix: add error prompt for wrong date range (bp #22410)
2020-06-24 21:32:18 +05:30
Kenneth Sequeira
bf81b42219 fix: add error prompt for wrong date range
(cherry picked from commit f5b7bd9dcb)
2020-06-24 14:06:59 +00:00
gavin
1a3fd763ac Merge pull request #22409 from revant/release-action-on-v12
ci: github release action for v12
2020-06-23 21:33:56 +05:30
Revant Nandgaonkar
0a6b5bc83f ci: github release action for v12 2020-06-23 20:11:35 +05:30
Marica
fb18f78b77 fix: Update Packed Items via Update Items in SO (#22404)
Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-06-23 19:19:08 +05:30
mergify[bot]
835b1b4f0d fix: Insert Supplier Group via List View (#22403) (#22407)
(cherry picked from commit 7af7bb8311)

Co-authored-by: Marica <maricadsouza221197@gmail.com>
2020-06-23 19:18:18 +05:30
mergify[bot]
3c18669057 fix: staffing Plan validation (#22379) (#22396)
(cherry picked from commit 36560c8406)

Co-authored-by: Anurag Mishra <32095923+Anurag810@users.noreply.github.com>
2020-06-23 10:49:48 +05:30
mergify[bot]
57a0fa5e87 fix: resetting lost reason in opportunity and quotation (#22378) (#22381)
(cherry picked from commit 71da90034d)

Co-authored-by: Anupam Kumar <anupamvns0099@gmail.com>
2020-06-23 10:45:08 +05:30
rohitwaghchaure
578bdff141 fix: offline pos not working for special character item (#22391) 2020-06-23 10:34:52 +05:30
rohitwaghchaure
c20b962842 Merge pull request #22389 from kennethsequeira/purchase_inv_due_12
fix: status error in purchase invoice
2020-06-22 23:47:32 +05:30
Deepesh Garg
5c4b738833 Merge pull request #22387 from AfshanKhan/V12_ISS_20_21_02455
fix: Set default reference Id for "On Previous Row Amount" and "On Previous Row Total"
2020-06-22 23:09:44 +05:30
Kenneth Sequeira
8a1f2ed65a fix:status error in purchase invoice 2020-06-22 22:40:04 +05:30
Afshan
def13f14a3 style: formate according to Codacy/PR Quality Review 2020-06-22 21:58:27 +05:30
Afshan
7e04dcae0a style: formate according to Codacy/PR Quality Review 2020-06-22 21:58:04 +05:30
Afshan
21a75eb2ba fix: set row_id by default as previous row for On Previous Row Amount and On Previous Row Total 2020-06-22 21:57:34 +05:30
rohitwaghchaure
290dbf470a Merge pull request #22384 from rohitwaghchaure/fixed-subcontract-item-for-alternative-item
fix: alternative item not working for subcontract
2020-06-22 21:52:26 +05:30
Rohit Waghchaure
171699537c fix: alternative item not working for subcontract 2020-06-22 20:48:28 +05:30
Deepesh Garg
91346d5e9e Merge pull request #22340 from frappe/mergify/bp/version-12-hotfix/pr-22312
fix: Email digest html view fix (bp #22312)
2020-06-22 19:08:56 +05:30
Marica
06ca3b5112 Purchase receipt to invoice hotfix (#22146)
* fix: Handle rows with same item code from Purchase Receipt to Invoice.

* fix: Handle rows with same item code from Purchase Receipt to Invoice.

* fix: Added patch, fixed tests, fixed delivery note behaviour

* chore: Added comments amd fixed typo

* fix: Added patch to patches.txt

* fix: Patch fix and simplification, json timestamp updation.

* fix: Set Purchase Receipt and Delivery Note detail patch (#21607)

* fix: Set Purchase Receipt and Delievry Note detail patch

* fix: Reload purchase receipt item

* fix(set_purchase_receipt_delivery_note_detail): commit after every 100 sql updates (#22016)

Co-authored-by: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com>
Co-authored-by: sahil28297 <37302950+sahil28297@users.noreply.github.com>
Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-06-22 18:04:48 +05:30
Kenneth Sequeira
e220be5415 fix: Batch No label in Purchase Receipt (#22360) 2020-06-22 17:33:04 +05:30
mergify[bot]
413c2b229a fix: 'Last Purchase Rate' taking wrong on BOM (#20689) (#22370)
* fix: 'Last Purchase Rate' taking wrong on BOM. #20228

* fix: Added condition for None purchase order and purchase receipt (#20689)

* fix: fetch last purchase rate

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
(cherry picked from commit 2866617c82)

Co-authored-by: Lokesh Waingankar <59611773+lokesh-indictrans@users.noreply.github.com>
2020-06-22 17:26:42 +05:30
Anurag Mishra
1039079e37 fix: set cost center in child table (#22373) 2020-06-22 17:25:53 +05:30
Poranut Chollavorn
303639db76 fix: use name instead of title 2020-06-22 10:24:40 +00:00
rohitwaghchaure
60cb5ac419 Merge pull request #22330 from pipech/v12-hotfix-clear_pricing_rule_value
fix(pricing_rule): apply_on logic dont get cleanup
2020-06-22 14:47:29 +05:30
Rohit Waghchaure
ebbbc85fc0 added validation and clear fields for Apply Rule On Other 2020-06-22 13:44:11 +05:30
rohitwaghchaure
7f6d36cdc9 Merge pull request #22369 from frappe/mergify/bp/version-12-hotfix/pr-22241
fix: Minor fixes in cost center (bp #22241)
2020-06-22 13:00:19 +05:30
Poranut Chollavorn
850c0ff121 fix(pricing_rule): key error on apply_internal_priority
apply_internal_priority never check if pricing rule have field
2020-06-22 07:24:33 +00:00
Marica
4a9de4dc07 Merge branch 'version-12-hotfix' into v12-hotfix-clear_pricing_rule_value 2020-06-22 12:48:55 +05:30
Marica
cb555ae825 Merge pull request #22368 from frappe/mergify/bp/version-12-hotfix/pr-21582
feat: Enabled translation on html files in LMS [Proposal] (bp #21582)
2020-06-22 12:45:18 +05:30
Rohit Waghchaure
e5ffe3fea4 fix: completed qty not updated in work order 2020-06-22 12:36:23 +05:30
Marica
7fcf958d08 Merge branch 'version-12-hotfix' into mergify/bp/version-12-hotfix/pr-21582 2020-06-22 12:07:39 +05:30
Deepesh Garg
cfe9ed0dfa fix: Test
(cherry picked from commit bef80b7981)
2020-06-22 05:54:53 +00:00
Deepesh Garg
f2fe8053dc fix: Validation for group cost center
(cherry picked from commit 0d880079b1)
2020-06-22 05:54:53 +00:00
Deepesh Garg
aa22cca67a fix: Minor fixes in cost center
(cherry picked from commit 817cbc4b48)
2020-06-22 05:54:52 +00:00
rohitwaghchaure
c4893df048 Merge pull request #22088 from deepeshgarg007/rcm_version-12-hotfix
fix(India): Reverse charge mechanism for GST
2020-06-22 11:20:38 +05:30
Alvaro
b16b24fd10 feat: Enabled translation on html files in LMS [Proposal] (#21582)
* :fix:  translation fix for html files in LMS

* fix: typo in translation

* Update content.html

* Update content.html

* Update profile.html

Co-authored-by: Shivam Mishra <scmmishra@users.noreply.github.com>
Co-authored-by: Marica <maricadsouza221197@gmail.com>
(cherry picked from commit 43be163b32)
2020-06-22 05:39:42 +00:00
Deepesh Garg
2f2385b354 Merge pull request #22365 from frappe/mergify/bp/version-12-hotfix/pr-22350
fix: Skipping total row for tree-view reports (bp #22350)
2020-06-21 22:11:32 +05:30
Afshan
8a2a32820a fix: adding json for checked add total row
(cherry picked from commit 84c6973742)
2020-06-21 13:59:25 +00:00
Afshan
41462f3c36 fix: test case
(cherry picked from commit 3fbe6e9e4b)
2020-06-21 13:59:25 +00:00
Afshan
a9ff40db67 fix: Skipping*
(cherry picked from commit 65f00cea15)
2020-06-21 13:59:24 +00:00
Afshan
0d95f56474 Skiping total row for tree-view reports
(cherry picked from commit fbd66574ad)
2020-06-21 13:59:24 +00:00
rohitwaghchaure
da65ddc68a Merge pull request #22362 from rohitwaghchaure/fixed-popup-is-coming-for-non-serialized-items
fix: Serial no / batch no Popup is coming for the non serialized items
2020-06-20 20:54:55 +05:30
Rohit Waghchaure
368f5e29d2 fix: Serial no / batch no Popup is coming for the non serialized items 2020-06-20 20:48:28 +05:30
Deepesh Garg
92f97b6a68 Merge pull request #22347 from frappe/revert-21743-payment-remark-fix-v12
Revert "fix: update remark on submitting payment entry"
2020-06-19 17:29:20 +05:30
Deepesh Garg
7d035fac36 Revert "fix: update remark on submitting payment entry" 2020-06-19 17:28:58 +05:30
Marica
6c1a5e8a44 Merge branch 'version-12-hotfix' into v12-hotfix-clear_pricing_rule_value 2020-06-19 14:28:06 +05:30
rohitwaghchaure
995bdd3c48 Merge pull request #22250 from marination/rfq-multi-uom-hotfix
feat: Multi UOM support in Request for Quotation
2020-06-19 13:17:36 +05:30
mergify[bot]
5acc06dfe6 fix: Student Admission (#22280)
* Student Admission fix

* adding check for application

* adding check for application

* updating error message

* added date_diff for date comparision

(cherry picked from commit c6592c880a)

Co-authored-by: Anupam Kumar <anupamvns0099@gmail.com>
Co-authored-by: Anurag Mishra <32095923+Anurag810@users.noreply.github.com>
2020-06-19 11:58:51 +05:30
Anupam K
168d070d73 email digest html fix
(cherry picked from commit bd449c8d07)
2020-06-19 06:09:49 +00:00
mergify[bot]
6f3e5e1891 refactor: show service instead of services (#22339)
(cherry picked from commit 25702a1c55)

Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
2020-06-19 11:10:42 +05:30
rohitwaghchaure
d48e33dbd7 Merge pull request #22327 from frappe/mergify/bp/v12-pre-release/pr-22287
fix: Quality procedure fixes (bp #22287)
2020-06-19 11:01:41 +05:30
rohitwaghchaure
7af8ca1bd1 Merge pull request #22338 from frappe/mergify/bp/v12-pre-release/pr-22334
fix: Customer Group label in Item-wise Sales History report (bp #22334)
2020-06-19 11:00:31 +05:30
rohitwaghchaure
a3e92489c2 Merge pull request #22337 from frappe/mergify/bp/version-12-hotfix/pr-22334
fix: Customer Group label in Item-wise Sales History report (bp #22334)
2020-06-19 11:00:17 +05:30
Kenneth Sequeira
4793e9c24c fix: Customer Group label in Itemwise Sales report
(cherry picked from commit 766f978858)
2020-06-19 05:29:07 +00:00
rohitwaghchaure
dd8dcd29c3 Merge pull request #22332 from marination/item-edit-hotfix
fix: Unabled to create batched item
2020-06-19 10:57:25 +05:30
Kenneth Sequeira
10624f3fa6 fix: Customer Group label in Itemwise Sales report
(cherry picked from commit 766f978858)
2020-06-19 05:26:23 +00:00
Marica
a0aa16c68c Merge branch 'version-12-hotfix' into item-edit-hotfix 2020-06-18 22:02:32 +05:30
marination
67361bef73 fix: Unabled to create batched item 2020-06-18 21:47:36 +05:30
Poranut Chollavorn
acf399c4e0 fix(pricing_rule): apply_on logic dont get cleanup 2020-06-18 14:41:26 +00:00
Sahil Khan
66a33bf7d9 Merge branch 'v12-pre-release' into version-12 2020-06-18 20:06:18 +05:30
Sahil Khan
aa27a0e85c bumped to version 12.10.0 2020-06-18 20:26:18 +05:50
sahil28297
a9dbff0eae chore: correct version in release note 2020-06-18 20:03:00 +05:30
Marica
a9ed42f93d fix: Quality procedure fixes (#22287)
* fix: Quality Procedure Fixes

- Dont prompt error message if parent is the same in child
- filter child procedure field
- Disable New button in tree view
- Editable grid for Quality Procedure Proces table.

* chore: Remove unnecessary get_doc

* fix: Codacy

Co-authored-by: Anurag Mishra <32095923+Anurag810@users.noreply.github.com>
(cherry picked from commit cce1116b4e)
2020-06-18 13:48:34 +00:00
Marica
cce1116b4e fix: Quality procedure fixes (#22287)
* fix: Quality Procedure Fixes

- Dont prompt error message if parent is the same in child
- filter child procedure field
- Disable New button in tree view
- Editable grid for Quality Procedure Proces table.

* chore: Remove unnecessary get_doc

* fix: Codacy

Co-authored-by: Anurag Mishra <32095923+Anurag810@users.noreply.github.com>
2020-06-18 19:15:49 +05:30
Poranut Chollavorn
66806ff1b0 fix(pricing_rules): rule won't got use across [item_code, item_group, brands] 2020-06-18 13:43:15 +00:00
Deepesh Garg
fc488a2b46 fix: Do not copy Item Tax template from SO to PO (#22326) 2020-06-18 18:19:49 +05:30
Nabin Hait
f53ee9026b chore: Added Change log 2020-06-18 18:13:51 +05:30
mergify[bot]
5bd479e8ee refactor: handle exceptions when updating addresses (#22307) (#22316)
* refactor: handle exceptions when updating addresses

* refactor: fold common statements in a loop

(cherry picked from commit 34d2bfbb3e)

Co-authored-by: Shivam Mishra <scmmishra@users.noreply.github.com>
2020-06-18 17:04:45 +05:30
mergify[bot]
531ef15e30 refactor: handle exceptions when updating addresses (#22307) (#22315)
* refactor: handle exceptions when updating addresses

* refactor: fold common statements in a loop

(cherry picked from commit 34d2bfbb3e)

Co-authored-by: Shivam Mishra <scmmishra@users.noreply.github.com>
2020-06-18 17:04:36 +05:30
Deepesh Garg
6dc6da1ce2 Merge pull request #22324 from deepeshgarg007/item_tax_map_v12
fix: Do not copy Item Tax template from SO to PO
2020-06-18 17:02:56 +05:30
Deepesh Garg
f9dbcef5c9 fix: Do not copy Item Tax template from SO to PO 2020-06-18 17:01:32 +05:30
mergify[bot]
1ca4370033 fix: asset maintenance fixes (#21277) (#22294)
* fix: asset maintenance fixes

* fix: tests

(cherry picked from commit 9c494d3e72)

Co-authored-by: Saqib <nextchamp.saqib@gmail.com>
2020-06-18 15:39:13 +05:30
mergify[bot]
62882ea65b fix: asset maintenance fixes (#21277) (#22300)
* fix: asset maintenance fixes

* fix: tests

(cherry picked from commit 9c494d3e72)

Co-authored-by: Saqib <nextchamp.saqib@gmail.com>
2020-06-18 15:30:04 +05:30
mergify[bot]
0c08324fab chore: add standard queries hooks to whitelist (#21939) (#22305)
standard queries are used within the search widget, and now require to
be whitelisted before they can be executed through the search widget.

Signed-off-by: Chinmay D. Pai <chinmaydpai@gmail.com>

Co-authored-by: sahil28297 <37302950+sahil28297@users.noreply.github.com>
(cherry picked from commit 96100e9507)

Co-authored-by: Chinmay Pai <chinmaydpai@gmail.com>
2020-06-18 15:29:46 +05:30
mergify[bot]
2417d9492f fix(HR): typo in error message in Employee Balance Report (#22306) (#22313)
* fix: typo in date error message

* fix: error message cleanup in Leave Balance report

(cherry picked from commit af6a0f3a9d)

Co-authored-by: Kenneth Sequeira <33246109+kennethsequeira@users.noreply.github.com>
2020-06-18 15:28:39 +05:30
Marica
c6886e28f2 Merge branch 'version-12-hotfix' into rfq-multi-uom-hotfix 2020-06-18 14:42:27 +05:30
marination
b865d58b81 fix: Patch simplification
- Also, apply patch to all docs
2020-06-18 14:43:31 +05:30
Kenneth Sequeira
af6a0f3a9d fix(HR): typo in error message in Employee Balance Report (#22306)
* fix: typo in date error message

* fix: error message cleanup in Leave Balance report
2020-06-18 14:34:11 +05:30
rohitwaghchaure
130d254b5a Merge pull request #22310 from rohitwaghchaure/fixed-incorrect-variable-used-pre-release
fix: incorrect variable used while adding items in the submitted sale…
2020-06-18 14:18:22 +05:30
Rohit Waghchaure
82abcc94fa fix: incorrect variable used while adding items in the submitted sales order 2020-06-18 14:17:16 +05:30
rohitwaghchaure
f635a1739e Merge pull request #22308 from rohitwaghchaure/fixed-incorrect-variable-used
fix: incorrect variable used while adding new item in the submitted Sales Order
2020-06-18 14:00:51 +05:30
Rohit Waghchaure
c75236579f fix: incorrect variable used while adding items in the submitted sales order 2020-06-18 13:59:23 +05:30
Mangesh-Khairnar
6a0bd556d7 fix: update shopify api version (#22297) 2020-06-18 12:48:52 +05:30
mergify[bot]
5d042ad496 fix: update shopify api version (#22284) (#22299)
Co-authored-by: Saurabh <saurabh@erpnext.com>
(cherry picked from commit 53b601523b)

Co-authored-by: Mangesh-Khairnar <mkhairnar10@gmail.com>
2020-06-18 09:47:15 +05:30
mergify[bot]
59938a46eb fix: typo for language in Terms description (#22270) (#22272)
(cherry picked from commit c159556c24)

Co-authored-by: Kenneth Sequeira <33246109+kennethsequeira@users.noreply.github.com>
2020-06-18 09:46:03 +05:30
P-Froggy
1284b9b17d fix: Set Value of wrong Bank Account Field in Payment Entry
Company bank account was wrongly inserted into the field "Party Bank Account" in payment entry, instead of "Bank Account".
Adds filters for "Party Bank Account" based on selected Party (I forgot to create a PR for version-12 back in February, see commit #393a626).
Also changes the label of "Default Bank Account" to "Default Company Bank Account", like suggested in PR #20632

#### Please Note
This is kind of a revert of PR #19390. However, it seems like the function "get_party_bank_account" in Bank Account doctype changed how it worked since it is clearly pulling the default company bank account.
2020-06-18 02:11:15 +02:00
Marica
c5c179490b Merge pull request #22293 from marination/variant-error-msg-pre-release
fix: Typo in Item Variant Error Message
2020-06-17 19:24:11 +05:30
Marica
1345bc2f82 Merge pull request #22291 from marination/variant-error-msg-hotfix
fix: Typo in Item Variant error message
2020-06-17 19:23:42 +05:30
marination
e256a7439e fix: Typo 2020-06-17 19:20:56 +05:30
marination
e67195b0b9 fix: Typo 2020-06-17 19:06:56 +05:30
Deepesh Garg
e4eb353065 Merge branch 'version-12-hotfix' into rcm_version-12-hotfix 2020-06-17 15:33:45 +05:30
Marica
b665da7cb4 Merge pull request #22282 from frappe/mergify/bp/v12-pre-release/pr-22281
fix: Message Formatting for missing Expense Account (bp #22281)
2020-06-17 13:55:25 +05:30
marination
7be4ba9351 fix: Message Formatting
(cherry picked from commit 17ec1d6b28)
2020-06-17 08:24:16 +00:00
Marica
41dadba990 Merge pull request #22281 from marination/expense-account-message
fix: Message Formatting for missing Expense Account
2020-06-17 13:51:09 +05:30
marination
17ec1d6b28 fix: Message Formatting 2020-06-17 13:46:21 +05:30
rohitwaghchaure
d4c2c4e170 Merge pull request #22276 from frappe/mergify/bp/v12-pre-release/pr-22268
fix: pos, serial no popup coming two times (bp #22268)
2020-06-17 12:27:20 +05:30
rohitwaghchaure
23a2d789ec Merge pull request #22277 from frappe/mergify/bp/v12-pre-release/pr-22248
fix: v12 travis (bp #22248)
2020-06-17 12:27:07 +05:30
Rohit Waghchaure
5e68fdbaee fix: travis
(cherry picked from commit e4834d2a37)
2020-06-17 05:40:14 +00:00
rohitwaghchaure
9c55efa8c6 fix: pos, serial no popup comin two times (#22268)
(cherry picked from commit 03f688acd9)
2020-06-17 05:35:50 +00:00
rohitwaghchaure
256b6950f5 Merge pull request #22273 from frappe/mergify/bp/v12-pre-release/pr-22267
refactor: hide company currency fields in the routing (bp #22267)
2020-06-17 10:59:58 +05:30
rohitwaghchaure
956368e457 refactor: hide company currency fields in the routing (#22267)
(cherry picked from commit fd3ff6be18)
2020-06-17 04:13:12 +00:00
rohitwaghchaure
fd3ff6be18 refactor: hide company currency fields in the routing (#22267) 2020-06-17 09:37:41 +05:30
rohitwaghchaure
03f688acd9 fix: pos, serial no popup comin two times (#22268) 2020-06-17 09:36:23 +05:30
rohitwaghchaure
fd5c577b40 Merge pull request #22248 from rohitwaghchaure/fixed-version-12-hotfix-travis
fix: v12 travis
2020-06-16 09:55:26 +05:30
Rohit Waghchaure
e4834d2a37 fix: travis 2020-06-15 22:24:56 +05:30
marination
1e76bdcc3c feat: Multi UOM support in Request for Quotation 2020-06-15 17:53:47 +05:30
Sahil Khan
b6c6406206 Merge branch 'version-12-hotfix' of https://github.com/frappe/erpnext into v12-pre-release 2020-06-15 16:58:22 +05:30
rohitwaghchaure
253a698708 Merge pull request #22247 from marination/backflush-rm-rate-hotfix
fix: Wrong key sent to get_valuation_rate
2020-06-15 14:50:02 +05:30
marination
b819d8cdb6 fix: Wrong key sent to gte_valuation_rate 2020-06-15 14:47:37 +05:30
P-Froggy
84f44588d8 fix: Validation of Purchase Order against Material Request missing (#22224)
Validation of Purchase Order and Purchase Order Item against the linked Material Request Item was missing.
This way, it was possible to exchange items while making a purchase order and still fulfill the material request, even though the items did not match.

Co-authored-by: Marica <maricadsouza221197@gmail.com>
2020-06-15 13:40:54 +05:30
rohitwaghchaure
14961e55c8 Merge pull request #22196 from marination/add-to-cart-stock-hotfix
fix: Handle unavailable Variants in Website
2020-06-15 13:11:18 +05:30
rohitwaghchaure
db48fc2559 Merge pull request #22218 from marination/update-items-permission-fix-hotfix
fix: submitted sales order can be updated with no permission
2020-06-15 13:10:15 +05:30
marination
713cbcea44 fix: Travis 2020-06-15 12:09:04 +05:30
marination
0a31e2ea92 fix: Fetch cart setting from argument 2020-06-15 12:02:28 +05:30
rohitwaghchaure
087278baba Merge pull request #22209 from frappe/michellealva-expense-claim
fix: add filter for cost center in expense table
2020-06-15 10:36:31 +05:30
rohitwaghchaure
faff772619 Merge pull request #22220 from Anurag810/quality_procedure_fix_v12
fix: Child is shown in Parent process if added from tree view
2020-06-15 10:34:09 +05:30
Deepesh Garg
fa1af6d74e Merge pull request #22200 from marination/qi-on-return-hotfix
fix: Don't prompt for Quality Inspection on Return Documents.
2020-06-14 17:10:13 +05:30
rohitwaghchaure
01d2548235 Merge pull request #22238 from rohitwaghchaure/item-none-not-found-opening-creation-tool
fix: item none not found while making sales invoice using opening inv…
2020-06-14 15:09:08 +05:30
Rohit Waghchaure
5b11d18d86 fix: item none not found while making sales invoice using opening invoice creation tool 2020-06-14 14:06:12 +05:30
Deepesh Garg
e4cd23746b fix: Consider Overseas category in RCM 2020-06-14 13:20:09 +05:30
Deepesh Garg
d2ed36f167 Merge pull request #22229 from deepeshgarg007/financial_statement_to_date
fix: Data not appearing properly for some fiscal_year in financial statements
2020-06-13 19:19:55 +05:30
Deepesh Garg
1222fb074f fix: Data not appearing properly for some fiscal_year in financial statemets 2020-06-13 19:16:37 +05:30
Abhishek Balam
486be1049c fix(HR): Rename due_advance_amount field in Employee Advance for v12 (#22216)
* added patch for moving data

* changed fieldname and references for easier debugging

* style: Added EOF

* reload doc for patch
2020-06-13 01:18:29 +05:30
Anurag Mishra
9319153ab3 fix: Child is shown in Parent process if added from tree view 2020-06-12 17:04:25 +05:30
rohitwaghchaure
9ea539d335 Merge pull request #22158 from pipech/v12-hotfix-pricing_rule-missing_income_account
fix(pricing_rule): missing income account when getting free product
2020-06-12 17:00:13 +05:30
marination
f6054a2de8 fix: Added PO test and conversion factor fix
- Don't change conversion factor if stock uom and uom is the same
- Added PO test
- Added Accounts User basic role in PO
- Minor fixes, wrong variables
2020-06-12 16:28:44 +05:30
Saqib Ansari
6a9f368292 fix: unexpected removal of print hide field 2020-06-12 16:25:57 +05:30
Saqib Ansari
9ee4841520 fix: cannot update delivery date 2020-06-12 16:25:38 +05:30
Saqib Ansari
39e125b8a2 fix: label 2020-06-12 16:25:28 +05:30
Saqib Ansari
37b614af0e fix: submitted sales order can be updated with no permission 2020-06-12 16:25:07 +05:30
Marica
1dc3c40b00 Merge branch 'version-12-hotfix' into qi-on-return-hotfix 2020-06-12 12:32:27 +05:30
Marica
8ce5e04777 Update stock_controller.py 2020-06-12 12:32:12 +05:30
Michelle Alva
d6cdc3378a fix: add filter for cost center in expense table
Added filter for cost center in expense table (company and group_node filter)
2020-06-12 10:30:32 +05:30
Deepesh Garg
18ccd6b253 Merge pull request #22204 from marination/account-tree-hotfix
fix: Check for Company before rendering tree in Account Tree
2020-06-12 10:03:21 +05:30
marination
5ee33bdbe9 fix: Check for Company before rendering tree in Account Tree 2020-06-11 20:31:08 +05:30
marination
a965773c97 fix: Don't prompt for Quality Inspection on Return Documents. 2020-06-11 16:45:36 +05:30
marination
821606f001 fix: Handle unavailable Variants in Website 2020-06-11 13:32:58 +05:30
Deepesh Garg
95695f0628 Merge pull request #22189 from frappe/mergify/bp/version-12-hotfix/pr-22188
fix: Update payment schedule based on payment terms (bp #22188)
2020-06-10 22:31:28 +05:30
Deepesh Garg
755b8490e7 fix: Update payment schedule based on payment terms
(cherry picked from commit a0fd97f2ac)
2020-06-10 16:52:58 +00:00
Rohan
8a2c719359 fix: only auto-set serial nos and batches if allowed in Stock Settings (v12) (#21779)
* fix: only auto-set serial nos and batches if allowed in Stock Settings

* fix: bug with setting disabled batch no in Pick List

* Remove auto_set_batch_no

Co-authored-by: Marica <maricadsouza221197@gmail.com>
2020-06-10 19:11:20 +05:30
rohitwaghchaure
eaa6218003 Merge pull request #22183 from marination/website-price-list-hotfix
fix: Prioritize Default Customer Price List in Portal
2020-06-10 19:07:00 +05:30
marination
8710866886 fix: Prioritize Default Customer Price List in Portal 2020-06-10 18:28:14 +05:30
rohitwaghchaure
f09db536af Merge pull request #22178 from rohitwaghchaure/code-cleanup-for-handling-batch-case-in-SI
fix: Cannot read property 'has_batch_no' of undefined
2020-06-10 17:07:05 +05:30
Rohit Waghchaure
c848b97757 fix: Cannot read property 'has_batch_no' of undefined 2020-06-10 17:04:40 +05:30
rohitwaghchaure
8fcd86cb9e Merge pull request #22062 from marination/procurement-tracker
fix: Procurement Tracker Data Consistency
2020-06-10 14:13:19 +05:30
rohitwaghchaure
8c69991c12 Merge pull request #22097 from marination/valuation-rate-hotfix
fix: '>=' not supported between instances of 'str' and 'int'
2020-06-10 14:12:03 +05:30
rohitwaghchaure
6b07ae89c0 Merge pull request #22168 from pipech/v12-hotfix-loyalty_program
fix(loyalty): loyalty point entry use wrong tier
2020-06-10 12:53:12 +05:30
mergify[bot]
703cee6abf fix: update program course (#22166) (#22173)
(cherry picked from commit 934e30ecac)

Co-authored-by: Anupam Kumar <anupamvns0099@gmail.com>
2020-06-10 12:51:29 +05:30
Poranut Chollavorn
5a5361815f docs(loyalty): fix wrong comment 2020-06-09 15:16:28 +00:00
Poranut Chollavorn
9831c5beca fix(loyalty): loyalty point entry use wrong tier 2020-06-09 15:06:44 +00:00
rohitwaghchaure
139627b331 Merge pull request #22148 from marination/finished-good-valuation-hotfix
fix: Finished Product Valuation at Repack
2020-06-09 17:41:58 +05:30
Poranut Chollavorn
dfd410ded2 fix(pricing_rule): missing income account 2020-06-09 03:16:49 +00:00
Deepesh Garg
02e3fc141a Merge pull request #22150 from deepeshgarg007/tdss_desscription_default
fix(accounting): Tax Withholding Category Description default
2020-06-08 14:05:54 +05:30
Deepesh Garg
65fb3f2c48 fix: Remove strip 2020-06-08 14:00:27 +05:30
Kevin Chan
5befddca39 fix: Tax Withholding Category Description default
This commit fixes a bug that happens when a Purchase Invoice uses a
Tax Withholding Category without a category_name. The category_name is
used as the tax description which is a required field. The bug was
fixed by using the Tax Withholding Category's name as the description
if the category_name is empty.
2020-06-08 14:00:17 +05:30
marination
189663f576 fix: Finished Product Valuation at Repack 2020-06-08 13:12:00 +05:30
rohitwaghchaure
b56156296b Merge pull request #22093 from marination/shippinng-address-hotfix
fix: Apply shipping rule without address too
2020-06-08 10:36:30 +05:30
mergify[bot]
6732c9ab14 fix: fetch tax lines within the shipping lines (#22138) (#22139)
(cherry picked from commit 3031128167)

Co-authored-by: Mangesh-Khairnar <mkhairnar10@gmail.com>
2020-06-07 18:59:06 +05:30
Deepesh Garg
38546cae33 Merge pull request #22068 from marination/item-attribute-hotfix
fix: Misleading Error message for Item Attribute.
2020-06-05 11:21:33 +05:30
P-Froggy
efdb55a083 fix: Wrong Ordered-Status Indicator for Material Request Items (#22117)
The indicator displaying if a material request item has been ordered or not (green/orange) used the wrong quantity field for determining the status.
The qty field is not in stock uom while the ordered_qty field is. Now, the stock_qty field is used for correct comparison with ordered_qty.
2020-06-05 10:56:30 +05:30
Marica
a569fa36bd Merge branch 'version-12-hotfix' into valuation-rate-hotfix 2020-06-04 15:31:35 +05:30
rohitwaghchaure
237ee1a9c6 Merge pull request #22110 from rohitwaghchaure/routing-operations-not-in-a-sequence-hotfix
fix: routing operations not added sequentially in the BOM
2020-06-04 14:52:08 +05:30
Rohit Waghchaure
1ae2376a83 fix: routing operations not added sequentially in the BOM 2020-06-04 14:50:22 +05:30
rohitwaghchaure
d0d35f03d0 Merge pull request #22108 from rohitwaghchaure/fixed-import-supplier-invoice-not-worked-v12
fix: import supplier invoice not working
2020-06-04 14:35:10 +05:30
Rohit Waghchaure
58a43be4f5 fix: import supplier invoice not working 2020-06-04 14:32:57 +05:30
Deepesh Garg
869d0c37b8 Merge pull request #22099 from frappe/mergify/bp/version-12-hotfix/pr-22098
fix: Error when no data is present in AR/AP report (bp #22098)
2020-06-03 22:40:30 +05:30
Deepesh Garg
c84961be85 fix: Error when no data is present in AR/AP reeport
(cherry picked from commit 7135a75e5e)
2020-06-03 16:49:52 +00:00
marination
89dd0bad28 fix: '>=' not supported between instances of 'str' and 'int' 2020-06-03 17:22:56 +05:30
Sahil Khan
b293e4b110 Merge branch 'v12-pre-release' into version-12 2020-06-03 15:10:41 +05:30
Sahil Khan
be104e5dcf bumped to version 12.9.4 2020-06-03 15:30:41 +05:50
sahil28297
051ae36bec Merge pull request #22094 from nextchamp-saqib/asset-error-fix-pre
fix: cannot create existing assets
2020-06-03 14:50:21 +05:30
sahil28297
12ad34b44a Merge pull request #22091 from nextchamp-saqib/asset-error-fix
fix: cannot create existing assets
2020-06-03 14:50:16 +05:30
Saqib Ansari
fc975cfc3a fix: cannot create existing assets 2020-06-03 14:48:38 +05:30
marination
1a1ce9a743 fix: Apply shipping rule without address too 2020-06-03 14:42:46 +05:30
Saqib Ansari
4e3caa8ece fix: cannot create existing assets 2020-06-03 14:15:16 +05:30
Deepesh Garg
28c9f3103f Merge pull request #22087 from MyuddinKhatri/purchase-receipt-hotfix
fix(stock): create purchase invoice from purchase receipt dashboard
2020-06-03 13:37:36 +05:30
Deepesh Garg
c417cf079c fix(India): Reverse charge mechanism for GST 2020-06-03 11:31:26 +05:30
Myuddin khatri
99f5ece31c fix(stock): able to create purchase invoice from purchase receipt dashboard
able to create purchase invoice from purchase receipt dashboard
2020-06-03 11:27:18 +05:30
gavin
5448be6f34 Merge pull request #22079 from frappe/mergify/bp/version-12-hotfix/pr-21491
feat(Tally migration): Errored documents handling (bp #21491)
2020-06-03 01:41:07 +05:30
mergify[bot]
38ed43ca59 Merge branch 'version-12-hotfix' into mergify/bp/version-12-hotfix/pr-21491 2020-06-02 09:26:34 +00:00
Gavin D'souza
2e825a108f chore: verbose error message for coa recursion
(cherry picked from commit cb1376b036)
2020-06-02 08:24:58 +00:00
Gavin D'souza
43a19da27a fix: un-using buggy JS frappe.new_doc
(cherry picked from commit 0c353a64a4)
2020-06-02 08:24:58 +00:00
Gavin D'souza
8ea9b0a415 feat: maintain tally voucher numbers
feat: create seperate customer and supplier entries for same party if
case exists

(cherry picked from commit af612ddb6d)
2020-06-02 08:24:58 +00:00
Gavin D'souza
d50cceaafa feat: prompt that company and COA will be overwritten
(cherry picked from commit 51305a028b)
2020-06-02 08:24:58 +00:00
Gavin D'souza
0a7afd77e2 fix: toggle read only for accounts post daybook data processing
style: code comments and updates
(cherry picked from commit f4926a8205)
2020-06-02 08:24:58 +00:00
Gavin D'souza
adb864828a perf: reduce JS memory heap and maintain namespaces
(cherry picked from commit 6b850b7e71)
2020-06-02 08:24:57 +00:00
Gavin D'souza
18d432a843 fix: table cleanups, updated summary block
(cherry picked from commit 0d61b35b0b)
2020-06-02 08:24:57 +00:00
Gavin D'souza
d3d511d31a fix: run summary logger
style: indent fixes
(cherry picked from commit 27b7172fa4)
2020-06-02 08:24:57 +00:00
Gavin D'souza
6950c10c6f feat: handle resolve and unresolved errors
restructured and reused components

(cherry picked from commit 2e1b531f4f)
2020-06-02 08:24:57 +00:00
Gavin D'souza
28a6e1deec feat: maintain logs of errors successfully fixed
(cherry picked from commit 640636bbcd)
2020-06-02 08:24:57 +00:00
Gavin D'souza
2f5208b08d feat: order failed import log by creation, handle rollbacks and commits
(cherry picked from commit 1b5eea691c)
2020-06-02 08:24:56 +00:00
Gavin D'souza
7ee6e83605 perf: reduce failed_error_log size
(cherry picked from commit 0805307514)
2020-06-02 08:24:56 +00:00
Gavin D'souza
0a2bbfaf24 feat: error log handling
fix: toggle required accounts
(cherry picked from commit 82e7a9de28)
2020-06-02 08:24:56 +00:00
Gavin D'souza
2ab6c8fa60 fix: set default round off acount
(cherry picked from commit 38c677689d)
2020-06-02 08:24:56 +00:00
Gavin D'souza
61381963b7 feat: Error logging via doc
fix: Handle duplicate company and COA
feat: Error logging via doc
feat: fetch defaults for accounts
(cherry picked from commit 2de4a5a7c8)
2020-06-02 08:24:56 +00:00
Gavin D'souza
5c448de2ae feat: Add Failed error logs
fix: set defaults
(cherry picked from commit defeb73747)
2020-06-02 08:24:55 +00:00
marination
f42d656cd3 fix: Misleading Error message for Item Attribute. 2020-06-01 12:03:20 +05:30
marination
144facf203 fix: Procurement Tracker Data Consistency 2020-05-31 20:11:40 +05:30
Deepesh Garg
0cd8102e0e Merge pull request #22059 from frappe/mergify/bp/version-12-hotfix/pr-22057
fix(ewb): remove checksum validation for TRANSIN (bp #22057)
2020-05-30 20:22:51 +05:30
karthikeyan5
ffbc812271 fix(ewb): remove checksum validation for TRANSIN
(cherry picked from commit ca46bedfcb)
2020-05-30 14:25:39 +00:00
Deepesh Garg
056854fd9e Merge pull request #22056 from marination/qty-validation-in-stock-entry-hotfix
fix: '<' not supported between instances of 'str' and 'int'
2020-05-30 19:49:57 +05:30
Rohit Waghchaure
e7e3af1684 fix: '<' not supported between instances of 'str' and 'int' 2020-05-30 18:38:41 +05:30
rohitwaghchaure
f214789e4b fix: routing operations table is blank on pull of operations in BOM (#22040) 2020-05-29 21:27:31 +05:30
mergify[bot]
0407f018dd fix: cannot assign same task to other asset maintenance (#22024) (#22032)
(cherry picked from commit cd8b5d1e4c)

Co-authored-by: Saqib <nextchamp.saqib@gmail.com>
2020-05-29 21:26:40 +05:30
mergify[bot]
592965a4d9 fix: disposed asset creates inconsistencies in asset depr report (#22021) (#22030)
(cherry picked from commit 3c460364b5)

Co-authored-by: Saqib <nextchamp.saqib@gmail.com>
2020-05-29 21:26:29 +05:30
mergify[bot]
5b695d6e9f fix: transaction date not found in sales invoice (bp #22001) (#22029)
* fix: transaction date not found in sales invoice

(cherry picked from commit 603cc3d05e)

* chore: tests

(cherry picked from commit dd39ba0014)

Co-authored-by: Saqib Ansari <nextchamp.saqib@gmail.com>
2020-05-29 21:26:16 +05:30
Deepesh Garg
245b8be435 Merge pull request #22033 from frappe/mergify/bp/version-12-hotfix/pr-21597
fix: cannot make payment entry against shareholder (bp #21597)
2020-05-29 13:21:59 +05:30
Saqib
840225d47b fix: cannot make payment entry against shareholder (#21597)
(cherry picked from commit d46696fa03)
2020-05-29 05:57:59 +00:00
Mangesh-Khairnar
0fcb79f361 fix: make transaction date of the oldest transaction as the last integration date (#22017)
* fix: make transaction date of the oldest transaction as the last integration date

* fix: only save end date when transactions are returned
2020-05-29 10:17:18 +05:30
Rucha Mahabal
74ddc04031 Merge pull request #22002 from frappe/mergify/bp/version-12-hotfix/pr-21999
fix: change modified time (bp #21999)
2020-05-28 13:03:18 +05:30
Michelle Alva
d65000a361 fix: change modified time
changed modified time to sync the changes

(cherry picked from commit 8653419c4c)
2020-05-28 07:31:23 +00:00
Deepesh Garg
6f37035617 Merge pull request #21920 from marination/import-italian-invoice
chore: Added Italian Import Supplier Invoice to Menu
2020-05-28 12:33:12 +05:30
Marica
aa6280f5cd Merge branch 'version-12-hotfix' into import-italian-invoice 2020-05-28 12:20:35 +05:30
mergify[bot]
64078da47b fix: change description for field (#21995) (#21996)
grammatical fixes for description of Default Customer

(cherry picked from commit f7beeff2d8)

Co-authored-by: Michelle Alva <50285544+michellealva@users.noreply.github.com>
2020-05-28 11:10:32 +05:30
Deepesh Garg
d9013dc7c9 Merge pull request #21987 from deepeshgarg007/tds_computation_summary_v12
fix: TDS computation summary report
2020-05-27 22:56:36 +05:30
Deepesh Garg
a23f929005 fix: TDS computation summary report 2020-05-27 22:51:36 +05:30
Marica
4f75a3a29b Merge branch 'version-12-hotfix' into import-italian-invoice 2020-05-27 21:53:18 +05:30
marination
d9e6900e07 fix: Added permission via regional setup and patch 2020-05-27 21:53:01 +05:30
Deepesh Garg
b4a3e5f70b fix: Total for ageing column 121-Above (#21973) 2020-05-27 20:30:41 +05:30
Deepesh Garg
aee9a61e0d fix: Post Dated unallocated amount not considered in Advance Amount in AR/AP summary (#21838)
* fix: Post Dated unallocateed amount not considered in Advance Amount in AR/AP summary report

* fix: Add future payment filter in AR/AP summary

* fix: Show unallocated future payments only till current creation date

* fix: Remove extra query

* fix: Remove debug

* fix: Condition
2020-05-27 12:46:18 +05:30
Deepesh Garg
ccb8edd7a3 Merge pull request #21941 from deepeshgarg007/acc_dim_report_filter_v12
fix: Do not add filters in report on accounting dimension creation if it already exists
2020-05-26 19:41:06 +05:30
Deepesh Garg
bb2bcc8a30 fix: Remove console statement 2020-05-26 19:31:33 +05:30
Deepesh Garg
9720406086 fix: Do not add filters in report on accounting dimension creation if it already exists 2020-05-26 19:31:21 +05:30
mergify[bot]
3c0e22c98c fix: shopify error message on failure of sales order creation (#21924) (#21937)
(cherry picked from commit 7402451b96)

Co-authored-by: Mangesh-Khairnar <mkhairnar10@gmail.com>
2020-05-26 18:37:25 +05:30
mergify[bot]
f912d78023 fix: check for warehouse in the woocommerce settings (#21925) (#21936)
(cherry picked from commit ddc170521f)

Co-authored-by: Mangesh-Khairnar <mkhairnar10@gmail.com>
2020-05-26 18:21:31 +05:30
Rohan
61257d829e fix: throw error if no serial numbers are found in Pick List (#21914)
Co-authored-by: Marica <maricadsouza221197@gmail.com>
2020-05-26 18:11:39 +05:30
Andrew McLeod
c9014b1ba9 fix: website_list_for_contact, fix changed frappe.db.exist keyword argument (#21827) 2020-05-26 18:09:00 +05:30
Anurag Mishra
9ad46f7487 fix: Added column Expired Leave (#21859) 2020-05-26 18:08:08 +05:30
Deepesh Garg
b094178734 fix(UAE): Incorrect VAT rate display in Sales Invoice (#21883) 2020-05-26 18:02:08 +05:30
Anurag Mishra
a3ec869c58 fix: showing Wrong balance on allocation boundary dates (#21908) 2020-05-26 18:00:27 +05:30
mergify[bot]
7177514b0d fix: adding dashboard in course and assessment plan (#21889) (#21919)
* Adding dashboards

* adding dashboard in course and assessment plan

(cherry picked from commit 0a147d5c76)

Co-authored-by: Anupam Kumar <anupamvns0099@gmail.com>
2020-05-26 17:59:24 +05:30
marination
277f5273a4 chore: Add Import Supplier Invoice to Menu 2020-05-25 21:49:33 +05:30
Saqib
8ae7bb2bc4 fix: fetch depreciation amount only if depr entry is made (#21894) 2020-05-25 19:20:54 +05:30
Sahil Khan
7aa8eab228 Merge branch 'v12-pre-release' into version-12 2020-05-25 19:02:37 +05:30
Sahil Khan
70439c9cd8 bumped to version 12.9.3 2020-05-25 19:22:37 +05:50
Anurag Mishra
bbec37f713 fix: Validation for dates (#21887) 2020-05-25 18:39:26 +05:30
mergify[bot]
8ddf30201f fix: Item Price and Add to Cart not showing on Website (#21905) (#21916)
* fix: Item Price and Add to Cart not showing on Website

* fix: Use None as default argument

(cherry picked from commit 4e70ecb97d)

Co-authored-by: Marica <maricadsouza221197@gmail.com>
2020-05-25 18:38:54 +05:30
Marica
4e70ecb97d fix: Item Price and Add to Cart not showing on Website (#21905)
* fix: Item Price and Add to Cart not showing on Website

* fix: Use None as default argument
2020-05-25 18:26:42 +05:30
rohitwaghchaure
666dd27d42 Merge pull request #21911 from rohitwaghchaure/fixed-comparison-operator
fix: comparison operator
2020-05-25 16:39:37 +05:30
Rohit Waghchaure
410196974e fix: comparison operator 2020-05-25 16:38:22 +05:30
Rohan
b9d5c71405 fix: filter batches based on item and warehouse in Pick List (#21778)
Co-authored-by: Marica <maricadsouza221197@gmail.com>
2020-05-25 15:25:42 +05:30
Vishal Dhayagude
18b3c4802b fix: pound, gram to Ounce converion factor added (#21710)
* fix: pound, grams to Ounce converion factor added

* fix: patch date updated for UOM Conversion Factor

* fix: grams move to gram

* fix: duplicate entry of uom converion factor and patch added

* fix: typo fix

* fix: minor changes

* Update patches.txt

Co-authored-by: Marica <maricadsouza221197@gmail.com>
2020-05-25 15:13:09 +05:30
rohitwaghchaure
cbe476d455 Merge pull request #21900 from rohitwaghchaure/fixed-stock-and-account-value-comparison-report-hotfix
fix: local variable 'lft' referenced before assignment
2020-05-25 12:50:50 +05:30
Rohit Waghchaure
b4b6b694af fix: local variable 'lft' referenced before assignment 2020-05-25 12:49:27 +05:30
rohitwaghchaure
aa299d0faf Merge pull request #21868 from rohitwaghchaure/delivery-date-not-allowed-to-change-after-submit
fix: allow on submit for the delivery date field not working for sale…
2020-05-25 11:11:11 +05:30
sahil28297
1f6c5c295f fix(patch): rerun remove_duplicate_leave_ledger_entries (#21874) 2020-05-22 15:52:46 +05:30
sahil28297
5d8dafc2ec fix(patch): rerun remove_duplicate_leave_ledger_entries (#21875) 2020-05-22 14:18:08 +05:30
Saurabh
34eb306e39 Merge pull request #21873 from frappe/sahil28297-patch-3
fix(patch): rerun remove_duplicate_leave_ledger_entries
2020-05-22 13:48:53 +05:30
sahil28297
439af124f9 fix(patch): rerun remove_duplicate_leave_ledger_entries 2020-05-22 13:42:55 +05:30
Sahil Khan
8abcf42e20 Merge branch 'v12-pre-release' into version-12 2020-05-22 13:37:26 +05:30
Sahil Khan
485f6cd7a2 bumped to version 12.9.2 2020-05-22 13:57:25 +05:50
Nabin Hait
5447decd9e fix: Remove duplicate leave ledger entry (#21870)
* fix: Remove duplicate leave ledger entry

* fix: Remove duplicate leave ledger entry
2020-05-22 13:13:09 +05:30
Nabin Hait
1287f16acd fix: Remove duplicate leave ledger entry (#21867) 2020-05-22 13:13:04 +05:30
Rohit Waghchaure
589c5335d1 fix: allow on submit for the delivery date field not working for sales order 2020-05-22 12:41:07 +05:30
Deepesh Garg
5f82999397 fix: Item tax template not getting mapped from source to traget doc (#21863) 2020-05-22 12:29:19 +05:30
Nabin Hait
115cedb8e9 fix: Remove duplicate leave ledger entry (#21864) 2020-05-22 12:27:19 +05:30
mergify[bot]
d2491e403b fix: convert goals point to flt (#21840) (#21858)
(cherry picked from commit 4c779300fd)

Co-authored-by: Anurag Mishra <32095923+Anurag810@users.noreply.github.com>
2020-05-22 11:43:14 +05:30
mergify[bot]
44d6ce49b5 fix: expense account error message in DN (#21851) (#21856)
Changed error message if expense account not set for item in Delivery Note.
Earlier: Expense or Difference account is mandatory for Item IT - 6 as it impacts overall stock value
After fix: Expense Account not set for Item IT - 6. Please set an Expense Account for the item in the Items table

(cherry picked from commit 1763d3e706)

Co-authored-by: Michelle Alva <50285544+michellealva@users.noreply.github.com>
2020-05-22 11:06:19 +05:30
sahil28297
7b795734e8 Merge pull request #21855 from frappe/mergify/bp/v12-pre-release/pr-21846
fix: set customer and supplier details using sql (bp #21846)
2020-05-22 11:05:40 +05:30
Chinmay Pai
b6bbd0efcf fix: set customer and supplier details using sql (#21846)
* fix: set customer and supplier details using sql

instead of slowing down the query with get_doc and save()
we can just use sql to update the required values for
customer and supplier

Signed-off-by: Chinmay D. Pai <chinmaydpai@gmail.com>

* chore: remove extra quote

Co-authored-by: Himanshu <himanshuwarekar@yahoo.com>

* fix: update sql query to include tabPrice List

Signed-off-by: Chinmay D. Pai <chinmaydpai@gmail.com>

Co-authored-by: Himanshu <himanshuwarekar@yahoo.com>
(cherry picked from commit baef43977b)
2020-05-22 05:29:49 +00:00
mergify[bot]
7522e598db fix: set customer and supplier details using sql (#21846) (#21854)
* fix: set customer and supplier details using sql

instead of slowing down the query with get_doc and save()
we can just use sql to update the required values for
customer and supplier

Signed-off-by: Chinmay D. Pai <chinmaydpai@gmail.com>

* chore: remove extra quote

Co-authored-by: Himanshu <himanshuwarekar@yahoo.com>

* fix: update sql query to include tabPrice List

Signed-off-by: Chinmay D. Pai <chinmaydpai@gmail.com>

Co-authored-by: Himanshu <himanshuwarekar@yahoo.com>
(cherry picked from commit baef43977b)

Co-authored-by: Chinmay Pai <chinmaydpai@gmail.com>
2020-05-22 10:59:16 +05:30
Marica
5ec0289d98 fix: Added Inactive serial no status (#21850) 2020-05-22 10:49:07 +05:30
Marica
576374a8a7 fix: Added Inactive serial no status (#21849) 2020-05-22 10:49:00 +05:30
mergify[bot]
030abc14a4 fix(patch): Handle single value in patch (#21823) (#21833)
(cherry picked from commit 5cef8db4db)

Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com>
2020-05-21 18:57:24 +05:30
Anurag Mishra
4c779300fd fix: convert goals point to flt (#21840) 2020-05-21 18:47:30 +05:30
sahil28297
c5921c605f fix(set_serial_no_status): auto commit on many writes (#21841) 2020-05-21 18:46:42 +05:30
sahil28297
da23bb6bf4 fix(set_serial_no_status): auto commit on many writes (#21843) 2020-05-21 18:46:38 +05:30
Sahil Khan
0c303c4f54 Merge branch 'v12-pre-release' into version-12 2020-05-21 15:26:42 +05:30
Sahil Khan
746e879187 bumped to version 12.9.1 2020-05-21 15:46:42 +05:50
Suraj Shetty
a24362a83f Merge pull request #21836 from frappe/surajshetty3416-patch-1 2020-05-21 15:25:18 +05:30
Suraj Shetty
69a5a7f11e fix(patch): Handle single value in patch 2020-05-21 15:23:06 +05:30
Sahil Khan
d062a4b6b5 Merge branch 'v12-pre-release' into version-12 2020-05-21 15:00:37 +05:30
Sahil Khan
ab79a9554b bumped to version 12.9.0 2020-05-21 15:20:37 +05:50
rohitwaghchaure
3e5a65e090 Merge pull request #21829 from marination/italian-invoice-import-hotfix
fix: Supplier Invoice No not fetched in Import Supplier Invoice
2020-05-21 14:23:20 +05:30
rohitwaghchaure
f5d8d6e4e6 Merge pull request #21830 from marination/italian-invoice-import-pre-release
fix: Supplier Invoice No not fetched in Import Supplier Invoice
2020-05-21 14:22:36 +05:30
rohitwaghchaure
fae4805d3f Merge pull request #21826 from marination/pick-list-dn-pre-release
fix: Fetch customer into Delivery Note from Pick List
2020-05-21 14:20:31 +05:30
rohitwaghchaure
bc55dafdae Merge pull request #21825 from marination/pick-list-dn-hotfix
fix: Fetch customer into Delivery Note from Pick List
2020-05-21 14:19:05 +05:30
marination
cb74ff870d fix: Supplier Invoice No not fetched in Import Supplier Invoice 2020-05-21 14:06:57 +05:30
marination
d1458c6c0a fix: Supplier Invoice No not fetched in Import Supplier Invoice 2020-05-21 14:06:14 +05:30
marination
3c84ef3b5e fix: Fetch customer into Delivery Note from Pick List 2020-05-21 13:18:06 +05:30
marination
a8b2f222ea fix: Fetch customer into Delivery Note from Pick List 2020-05-21 13:17:22 +05:30
Marica
2894640d56 fix: plc conversion rate set infinitely (#21822) 2020-05-21 12:08:15 +05:30
Marica
ee1c3831bc fix: plc conversion rate set infinitely (#21821) 2020-05-21 12:08:04 +05:30
rohitwaghchaure
fc5f5c8fd9 Merge pull request #21819 from rohitwaghchaure/tax-template-not-applied-if-valid-from-blank-hotfix
fix: item tax template not applied if valid from is blank
2020-05-21 10:24:33 +05:30
rohitwaghchaure
a45ff8e3ba Merge pull request #21818 from rohitwaghchaure/tax-template-not-applied-if-valid-from-blank
fix: item tax template not applied if valid from is blank
2020-05-21 10:22:37 +05:30
Rohit Waghchaure
2caac24071 fix: item tax template not applied if valid from is blank 2020-05-21 10:15:03 +05:30
Rohit Waghchaure
74dd64501c fix: item tax template not applied if valid from is blank 2020-05-21 10:09:42 +05:30
Deepesh Garg
bd8b94c9dd Merge pull request #21816 from deepeshgarg007/trial_balance_project_filter_pre
fix: Project filter in Trial Baalance Report
2020-05-20 22:28:53 +05:30
Deepesh Garg
928bc96d76 Merge pull request #21815 from deepeshgarg007/trial_balance_project_filter_v12
fix: Project filter in Trial Baalance Report
2020-05-20 22:28:35 +05:30
Deepesh Garg
d1569b9581 fix: Project filter in Trial Baalance Report 2020-05-20 22:20:23 +05:30
Deepesh Garg
67c9f00917 fix: Project filter in Trial Baalance Report 2020-05-20 22:18:30 +05:30
mergify[bot]
c75303b0ab feat: remove website settings from boot (#21801) (#21809)
(cherry picked from commit e091789332)

Co-authored-by: Shivam Mishra <scmmishra@users.noreply.github.com>
2020-05-20 18:24:05 +05:30
Anupam Kumar
ee56de3091 enable Allow Rename in sales stage (#21800) 2020-05-20 16:15:16 +05:30
Anupam Kumar
938cde30e3 enable Allow Rename in sales stage (#21803) 2020-05-20 16:14:58 +05:30
Marica
d53d6cabb4 fix: Validate Payment Gateway only if it exists in Payment Request. (#21806) 2020-05-20 16:13:38 +05:30
Marica
0550b3537d fix: Validate Payment Gateway only if it exists in Payment Request. (#21807) 2020-05-20 16:13:34 +05:30
Deepesh Garg
ea6f08cb26 Merge pull request #21798 from deepeshgarg007/general_ledger_against_voucher_v12_pre
fix: Against voucher in General Ledger
2020-05-20 11:32:13 +05:30
Deepesh Garg
96a05f65aa fix: Against voucher in General Ledger 2020-05-20 11:26:14 +05:30
rohitwaghchaure
0e63f4ba08 Merge pull request #21794 from deepeshgarg007/general_ledger_against_voucher_v12
fix: Against voucher in General Ledger
2020-05-20 10:30:20 +05:30
Deepesh Garg
74b943392b Merge pull request #21786 from anupamvs/sales-order-tax-hotfix
fix: tax id is not fetched when creating sales order from quotation
2020-05-19 21:49:34 +05:30
Anupam K
9245133b99 tax id is not fetched in when creating sales order from quoation 2020-05-19 21:37:19 +05:30
Deepesh Garg
05aae4341e fix: Against voucher in General Ledger 2020-05-19 20:59:51 +05:30
rohitwaghchaure
e85c3a50cd refactor: changed the fieldtype from data to small text (#21789) 2020-05-19 20:30:11 +05:30
Deepesh Garg
dc04d44a20 Merge pull request #21791 from deepeshgarg007/gst_1_instate_v1
fix: Tax amount in GSTR-1 JSON
2020-05-19 20:01:18 +05:30
Deepesh Garg
e2e5ba345c fix: Tax amount in GSTR-1 JSON 2020-05-19 19:56:51 +05:30
rohitwaghchaure
07924d3c7c refactor: changed the fieldtype from data to small text (#21783) 2020-05-19 19:05:17 +05:30
Deepesh Garg
bd29244d66 Merge pull request #21787 from deepeshgarg007/gst_1_instate
fix: Instate Invoice not appearing in GSTR-1 report
2020-05-19 19:04:11 +05:30
Deepesh Garg
b384ed74cd fix: Instate Invoice not appearing in GSTR-1 report 2020-05-19 18:53:08 +05:30
Shivam Mishra
72892a40d6 refactor: use text editor in issue web form (#21772) 2020-05-19 16:36:25 +05:30
Himanshu
8209507a8f fix: add naming series (#21775) 2020-05-19 16:02:41 +05:30
Himanshu
ca5384343f Merge pull request #21773 from scmmishra/issue-form-pre-12
refactor: use text editor in issue web form
2020-05-19 15:23:46 +05:30
Shivam Mishra
a4e92cf577 refactor: use text editor in issue web form 2020-05-19 15:12:18 +05:30
mergify[bot]
113048347c feat: save shipping address and contact to woocommerece customer (bp #21654) (#21771) 2020-05-19 14:36:29 +05:30
Himanshu
8b144a38cc fix: add naming series (#21770) 2020-05-19 14:27:01 +05:30
Nabin Hait
632c65cd59 chore: Added change log 2020-05-18 19:54:52 +05:30
Nabin Hait
70ab59f473 fix: merge conflict 2020-05-18 17:10:36 +05:30
Deepesh Garg
c4d6cca9f1 Merge pull request #21762 from frappe/mergify/bp/version-12-hotfix/pr-21761
fix: Validate Filters in Sales Funnel. (bp #21761)
2020-05-18 16:45:20 +05:30
Marica
795d318fcc fix: Validate Filters in Sales Funnel. (#21761)
* fix: Validate Filters in Sales Funnel.

* fix: Style fixes

(cherry picked from commit c734db5d45)
2020-05-18 09:15:45 +00:00
Himanshu
6dbb02d293 fix: show searchfields result (#21760) 2020-05-18 14:26:43 +05:30
rohitwaghchaure
ea4c91f51c fix: bom incorrect price list rate for raw material if price list currency is different from company currency (#21586)
* fix: bom incorrect price list rate for raw material if price list currency is different from company currency

* fixed test cases

* fixed base_rate calculation and added plc_conversion_rate trigger
2020-05-18 14:22:42 +05:30
Marica
395da09dae fix: Patch to set status in old serial no data (#21721)
* fix: Patch to set status in old serial no data

* fix: Avoid get_doc in patch

* fix: fetch all values and check status in one query
2020-05-18 11:18:56 +05:30
Raffael Meyer
9b08258955 fix: remove guest access (#21692) 2020-05-17 20:57:51 +05:30
rohitwaghchaure
7f2aedb67c fix: incorrect stock valuation for repack entry (#21737) 2020-05-17 20:28:21 +05:30
Anurag Mishra
1045dc49f5 fix: Future date half day validation (#21719)
* fix: Future date half day validation

* fix: Allow half day attendance only via leave application

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-05-17 20:17:39 +05:30
Deepesh Garg
a569fed4da Merge pull request #21746 from frappe/mergify/bp/version-12-hotfix/pr-21712
fix: error log title for failing bank transactions (bp #21712)
2020-05-16 17:15:05 +05:30
rohitwaghchaure
9af9b438af Merge pull request #21748 from rohitwaghchaure/fix-supplier-schema-save-issue-hotfix
fix: promotional scheme not able to save
2020-05-16 11:34:33 +05:30
Rohit Waghchaure
3e321d5c24 fix: promotional scheme not able to savce 2020-05-16 05:01:30 +05:30
Mangesh-Khairnar
e2f53cdc83 fix: error log title for failing bank transactions
(cherry picked from commit 4a9fd9ef6d)
2020-05-15 18:16:22 +00:00
Deepesh Garg
f9834504c8 Merge pull request #21745 from deepeshgarg007/coa_ux_v12
fix: Excel support and UX fixes for chart of accounts importer
2020-05-15 20:08:54 +05:30
Deepesh Garg
ec19926a97 Move logic for download template to dialog 2020-05-15 19:45:47 +05:30
Deepesh Garg
39790f05e7 fix: Linting fixes 2020-05-15 19:44:45 +05:30
Deepesh Garg
317b53a8b6 fix: Description on template selection 2020-05-15 19:44:36 +05:30
Deepesh Garg
101612e599 fix: Added template types for download 2020-05-15 19:44:27 +05:30
Deepesh Garg
838ed77797 fix: Blank chart preview 2020-05-15 19:44:18 +05:30
Deepesh Garg
8ff718249e fix: Linting Errors 2020-05-15 19:44:07 +05:30
Deepesh Garg
88c2ba54ab fix: Excel support and UX fixes for chart of accounts importer 2020-05-15 19:43:56 +05:30
rohitwaghchaure
a52fe009cc fix: user not able to view product (#21697) 2020-05-15 19:35:35 +05:30
Deepesh Garg
79b691fe18 fix: Better validation message for group accounts (#21726) 2020-05-15 19:23:16 +05:30
Deepesh Garg
29f1a219d1 Merge pull request #21743 from nextchamp-saqib/payment-remark-fix-v12
fix: update remark on submitting payment entry
2020-05-15 17:49:11 +05:30
Deepesh Garg
73e3ba2c30 Merge pull request #21744 from frappe/mergify/bp/version-12-hotfix/pr-21729
fix: Submit perm for other income and removed caching while getting hra and basic from company (bp #21729)
2020-05-15 17:47:10 +05:30
Nabin Hait
78f69e448b fix: Added submit permission in employee other income
(cherry picked from commit 200f80c3d3)
2020-05-15 11:28:08 +00:00
Nabin Hait
52890341d5 fix: Get basic and hra component from db, not from cache
(cherry picked from commit 10df3d5081)
2020-05-15 11:28:08 +00:00
Saqib Ansari
725684a0c3 fix: update remark on submitting payment entry 2020-05-15 16:39:45 +05:30
Deepesh Garg
dc59831aa9 Merge pull request #21700 from nextchamp-saqib/pur-inv-status-fix-v12
fix: purchase inv shows overdue for fraction of outstanding
2020-05-15 15:45:36 +05:30
Saqib Ansari
2fa841821a fix: add tests for set_status 2020-05-15 14:31:19 +05:30
rohitwaghchaure
3b14810564 Merge pull request #21714 from nextchamp-saqib/off-pos-tax-template-v12
fix: item tax template fetching in offline pos
2020-05-15 13:47:01 +05:30
Deepesh Garg
8ed066ba1f fix: Add misssing dimensions in GL entries (#21691)
* fix: Add misssing dimensions in GL entries

* fix: expnese_taxes_and_charges.json

* fix: Add project filter in trial balance report

* fix: Use current dimensions instead of dimensions from asset
2020-05-15 12:58:19 +05:30
Rohan
f344369068 format: better error messages for invalid coupon codes (v12) (#21598)
* format: better error messages for invalid coupon codes

* fix: remove unnecessary docstatus check
2020-05-15 12:09:16 +05:30
Mangesh-Khairnar
71d9a52a07 fix: duplicate leave expiry creation (#21506)
* fix: validate existing ledger entries to avoid duplicates

* patch: remove duplicate ledger entries created

* fix: consider only submitted ledger entries

* fix: delete duplicate leaves from the ledger

* fix: check if duplicate ledger entry exists

* chore: formatting changes

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-05-15 11:55:36 +05:30
Saqib Ansari
0af125f6fe fix: test 2020-05-15 05:26:41 +05:30
Saqib
b432f3358e fix: item price not fetching when customer is unset in item price (#21489)
* fix: item price not fetching when customer is unset in item price

* fix: item price of selling type has hidden supplier value

* fix: remove test variable

* fix: test

* Update erpnext/stock/get_item_details.py

Co-authored-by: Marica <maricadsouza221197@gmail.com>

* patch: invalid customer/supplier based on item price type

* fix: remove patches from develop branch

* fix: patch

Co-authored-by: Marica <maricadsouza221197@gmail.com>
2020-05-15 04:25:10 +05:30
Saqib
441bb760ee Merge branch 'version-12-hotfix' into off-pos-tax-template-v12 2020-05-14 14:28:11 +05:30
Saqib
f69da75e33 Merge branch 'version-12-hotfix' into pur-inv-status-fix-v12 2020-05-14 14:27:44 +05:30
Saqib Ansari
12739ec464 fix: invalid conditional statement 2020-05-13 22:03:20 +05:30
Marica
6a317a8e78 fix: Add total rows in Report Purchase Order Items to be Received or Billed (#21711) 2020-05-13 18:58:03 +05:30
Saqib Ansari
cb1cc96210 fix: item tax template fetching in offline pos 2020-05-13 16:30:47 +05:30
Saqib Ansari
b47f5830f4 fix: purchase inv shows overdue for fraction of outstanding 2020-05-12 14:13:16 +05:30
mergify[bot]
855373a253 fix: Message for missing valuation rate (#21686) (#21688)
(cherry picked from commit 97715f2877)

Co-authored-by: Marica <maricadsouza221197@gmail.com>
2020-05-11 21:26:18 +05:30
mergify[bot]
c8ed6b1e79 fix: Run income-tax-slab patch only if slab already exists in payroll period (#21684) (#21687)
(cherry picked from commit 85a89812a4)

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-05-11 19:29:18 +05:30
sahil28297
00bbc76605 fix(patch): use translated string while setting notification template (#21678) 2020-05-11 19:28:49 +05:30
rohitwaghchaure
0df4f1738c Merge pull request #21602 from anupamvs/lead-customer-issue-hotfix
fix: adding Email and Phone in Contact child table
2020-05-11 13:02:41 +05:30
rohitwaghchaure
12a2da49ee Merge pull request #21662 from marination/barcode-update-fix
fix: Item Barcode stays the same after updation.
2020-05-11 11:01:13 +05:30
rohitwaghchaure
65d6efdaee Merge pull request #21666 from anupamvs/assessment-issue-hotfix
fix: Assessment Plan not getting created
2020-05-11 07:17:28 +05:30
rohitwaghchaure
f861a3e720 Merge pull request #21603 from rohitwaghchaure/fix-completed-qty-in-work-order-hotfix
fix: work order operation completed qty
2020-05-11 07:07:16 +05:30
Deepesh Garg
767d17ce2f Merge pull request #21672 from deepeshgarg007/budget_variance_report_v12
Budget variance report v12
2020-05-10 17:31:41 +05:30
Deepesh Garg
0f455060ab fix: Formatting fixes 2020-05-10 17:27:29 +05:30
Kevin Chan
861f2726a2 fix: Simplify get_dimension_account_month_map
This commit updates get_dimension_account_month_map to no longer show
the actual expense when there is no budget. This also removes the other
functions and queries related to it. Spaces are also converted to tabs.
2020-05-10 17:27:01 +05:30
Kevin Chan
55640be627 fix: Fix Budget Variance Report
This commit fixes a bug in Budget Variance Report where it combines the
actual expense amounts across different fiscal years. This was fixed by
updating the function and queries for computing the actual expense
amounts.
2020-05-10 17:26:29 +05:30
Kevin Chan
d837b6cedc style: Improve formatting
This commit improves indentations and makes sql queries more readable.
2020-05-10 17:26:16 +05:30
Anupam K
27b2988938 Assessment Plan not getting created 2020-05-09 16:44:36 +05:30
Anupam K
c739c2d355 Appending Email and Phone in Child Table 2020-05-08 17:38:14 +05:30
marination
231f65ff59 fix: Item Barcode stays the same after updation. 2020-05-08 16:43:02 +05:30
Sahil Khan
626585e9f3 Merge branch 'v12-pre-release' into version-12 2020-05-08 15:42:18 +05:30
Sahil Khan
5afcc9c185 bumped to version 12.8.0 2020-05-08 16:02:18 +05:50
sahil28297
41ff2cb4a7 chore: correct link to documentation 2020-05-08 15:40:03 +05:30
sahil28297
664f536e9b chore: correct version for release note 2020-05-08 15:24:23 +05:30
Nabin Hait
923fcc7738 chore: Change log 2020-05-08 15:14:39 +05:30
rohitwaghchaure
368afc551d Merge pull request #21640 from MyuddinKhatri/lead-update-fix-hotfix
fix(crm): fix lead while updating contact details (hotfix)
2020-05-08 10:37:28 +05:30
rohitwaghchaure
3a328b0413 Merge pull request #21645 from anupamvs/lms-label-hotfix
fix: renaming LMS to Learning Management System
2020-05-08 10:29:54 +05:30
rohitwaghchaure
970b21075e Merge pull request #21650 from anupamvs/payment-order-hotfix
fix: Payment Order not allowing to create Payment Entry
2020-05-08 10:26:55 +05:30
Deepesh Garg
d769a2036b Merge pull request #21648 from ruchamahabal/remove-old-analytics-v12
fix: delete old appointment analytics tree grid report
2020-05-08 10:23:46 +05:30
Anupam K
56a3b9abc4 Payment Order not allowing to create Payment Entry 2020-05-08 02:25:09 +05:30
Rucha Mahabal
6844dd75da fix: delete old appointment analytics tree grid report 2020-05-07 23:40:17 +05:30
Anupam K
0b6ef55e78 renaming LMS to Learning Management System 2020-05-07 19:51:46 +05:30
sahil28297
9fa952d10e fix(patch): Reload GSTR 3B report 2020-05-07 19:28:33 +05:30
sahil28297
6d4f451d0d fix(patch): reload Expense Claim doctype 2020-05-07 19:28:17 +05:30
sahil28297
1c03d154ce fix(item): patch to rename duplicate item_code values to name (#21619) 2020-05-07 19:27:30 +05:30
Nabin Hait
2d430ec077 feat: Income tax slab (#21631)
* feat: Income tax slab (#21406)

* Feat: Multiple tax as per new taxation rule

* patch:for multiple tax slab, fix: payroll and exemption validation

* Test: Fixture

* feat: income tax slab with other charges and tax exempted deduction components

* fix: added missing init file

* fix: Patch fixed

* fix: Patch fixed

* fix: test fixes

* fix: validate duplicate exemption declaration

* fix: payment entry test case

Co-authored-by: Anurag Mishra <mishranaman123@gmail.com>

* fix: Income tax slab patch (#21448)

* fix: reload income_tax_slab_other_charges in patch

* fix: reload lower_deduction_certificate in patch

* fix: Consider any kind of exemptions only if tax exemptions are allowed on tax slab (#21475)

* fix: Tax calcualtion based on slab (#21497)

* fix: Desk links for Income Tax Slab and Employee Other Income (#21511)

* fix: patch

Co-authored-by: Anurag Mishra <mishranaman123@gmail.com>
2020-05-07 18:57:01 +05:30
Myuddin khatri
cd0fcfd84c solved merge conflicts 2020-05-07 16:11:08 +05:30
rohitwaghchaure
43118e3551 Merge pull request #21635 from TurkerTunali/patch-3
fix: Job Card submitted qty
2020-05-07 16:08:21 +05:30
Türker Tunalı
9bd6d119c4 fix: Job Card submitted qty
Update Operation Status function in work order was throwing exception without checking the "Overproduction Percentage For Work Order" setting. To submit Job Card qty for more than the Work Order's "To Manufacture Qty" we need to apply this fix.
2020-05-07 12:38:37 +03:00
sahil28297
2aa045865f fix(item): patch to rename duplicate item_code values to name (#21619) 2020-05-07 12:11:23 +05:30
rohitwaghchaure
ec3a462acd fix: list index out of range (#21614) 2020-05-07 12:06:05 +05:30
Deepesh Garg
a6066009aa Merge pull request #21520 from nextchamp-saqib/sales-report-total-row-v12
chore: add total row in sales analytics report
2020-05-06 10:50:53 +05:30
Deepesh Garg
60b19fe935 Merge branch 'version-12-hotfix' into sales-report-total-row-v12 2020-05-06 10:48:38 +05:30
Deepesh Garg
70b52c37d5 Merge pull request #21606 from frappe/sahil28297-patch-1
fix(patch): reload Expense Claim doctype
2020-05-05 20:21:23 +05:30
sahil28297
178ad9b4d6 fix(patch): reload Expense Claim doctype 2020-05-05 20:07:48 +05:30
Rohit Waghchaure
b4311a41c5 fix: work order operation completed qty 2020-05-05 16:36:31 +05:30
Anupam K
c6e0dbb1c4 Appending Email and Phone in Child Table 2020-05-05 16:26:05 +05:30
rohitwaghchaure
d476eb79b4 fix: against voucher no not all records showing in case of Group By Voucher (consolidated) (#21591) 2020-05-05 16:17:03 +05:30
Saqib
10ea82001f chore: fix error message (#21594)
* chore: fix error message

* chore: add row idx
2020-05-05 16:14:12 +05:30
Saqib
794bb6ebdd fix: handle make_gl_entry in case of cwip enable after puchasing (#21530)
* fix: handle make_gl_entry in case of cwip enable after puchasing

* fix: invalid variable assignment

* fix: make gl entries if cwip has been booked even if cwip is disabled
* add tests

* fix: conditions

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-05-05 16:12:10 +05:30
Saqib Ansari
c3a9513978 fix: test 2020-05-04 20:24:30 +05:30
Deepesh Garg
a4a019f9d9 Merge pull request #21584 from frappe/sahil28297-patch-3
fix(patch): Reload GSTR 3B report
2020-05-04 19:26:46 +05:30
sahil28297
67ed21d443 fix(patch): Reload GSTR 3B report 2020-05-04 19:23:26 +05:30
rohitwaghchaure
c89d750e5c fix: heatmap not working for customer and supplier (#21579) 2020-05-04 18:53:26 +05:30
mergify[bot]
c253f0621d fix: fieldname update for 'Credit' and 'Debit' (#21405) (#21577)
* fix: fieldname update for 'Credit' and 'Debit'

'credit' updated to 'credit_in_account_currency'
'debit' updated to 'debit_in_account_currency'

* Update journal_entry.py

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
(cherry picked from commit f7a0b8b5b6)

Co-authored-by: Andy Zhu <andy007yan@gmail.com>
2020-05-04 12:40:39 +05:30
Deepesh Garg
3202b0a486 fix: Accounting Dimensions in Period Closing Voucher (#21565) 2020-05-04 11:12:19 +05:30
Rucha Mahabal
4a37ee8908 fix(Healthcare): remove hardcoded UOM during Item creation for templates (#21575) 2020-05-04 11:09:19 +05:30
Rucha Mahabal
bdbfd2ad0c fix: handle exception if sending Appointment Confirmation message fails (#21568) 2020-05-04 11:07:10 +05:30
Deepesh Garg
933d1262f2 Merge pull request #21376 from marination/commonify-warehouse-autofill-hotfix
chore: Commonify autofilling warehouses in child tables
2020-05-03 20:34:20 +05:30
rohitwaghchaure
c4752e36eb Merge pull request #21517 from marination/stock-balance-cleanup-hotfix
chore: Added company filter and minor cleanup in Stock Balance Report
2020-05-03 16:31:58 +05:30
Saqib Ansari
0da919c091 fix: test 2020-05-03 14:53:40 +05:30
Saqib
12b5d72e70 chore: add validation for gross purchase amount (#21535)
* chore: add validation for gross purchase amount

* fix: tests
2020-05-03 13:20:30 +05:30
Saqib Ansari
272d2bc0b3 fix: review fixes 2020-05-02 19:51:09 +05:30
Saqib Ansari
55b7904e2f fix: incorrect total in sales analytics for customer/item group 2020-05-02 19:39:42 +05:30
Marica
ed709b36b4 fix: variable referenced before assignment (#21561) 2020-05-02 17:55:47 +05:30
Saqib
bf1fc47564 fix: accounts payable shows advance amount of other company (#21549) 2020-05-01 18:15:20 +05:30
Deepesh Garg
db6953dc78 fix: Party Type filter in payment entry list view (#21542) 2020-05-01 15:01:04 +05:30
rohitwaghchaure
438e0f5d49 fix: 'NoneType' object is not iterable (#21538) 2020-05-01 10:50:02 +05:30
rohitwaghchaure
5a9476e0d4 Merge pull request #21502 from nextchamp-saqib/validate-paid-inv-msg-v12
chore: validate and warn payment against paid invoices
2020-05-01 10:37:32 +05:30
marination
f77a735469 fix: Make Company the first filter 2020-05-01 00:04:49 +05:30
Saqib Ansari
4451b7eda9 chore: calculate total row month-wise in sales analytics 2020-04-30 20:07:12 +05:30
mergify[bot]
426f0bc168 fix: only check for payment_account on bank entry (#21445) (#21518)
* fix: only check for payment_account on bank entry

Since all the fields (company, start and end date are mandatory before form submission, there is no need to check for them again after submission.

* fix: cur_frm to frm

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
(cherry picked from commit 95b186268c)

Co-authored-by: Michelle Alva <50285544+michellealva@users.noreply.github.com>
2020-04-30 19:53:59 +05:30
Saqib
a8b87ccce0 chore: asset accounts should have company currency (#21525) 2020-04-30 18:39:16 +05:30
Saqib Ansari
c31fc19f08 chore: add total row in sales analytics report 2020-04-30 16:29:21 +05:30
marination
5e817b2aee chore: Added company filter and minor cleanup in Stock Balance Report 2020-04-30 14:05:24 +05:30
Saqib Ansari
393857d7de chore: handle credit note validation 2020-04-30 13:24:51 +05:30
Saqib
0ffff47b64 feat: force cost center renaming from cost center form (#21504) 2020-04-30 11:29:03 +05:30
Saqib
6397590f07 fix: list index out of range error (#21468)
* fix: list index out of range error

* fix: condition
2020-04-30 11:04:51 +05:30
Nabin Hait
2620ac31f9 fix: Desk links for Income Tax Slab and Employee Other Income (#21511) 2020-04-30 11:03:57 +05:30
Nabin Hait
22c4f82fc6 fix: Tax calcualtion based on slab (#21497) 2020-04-30 11:03:01 +05:30
Nabin Hait
ae5414fced fix: Consider any kind of exemptions only if tax exemptions are allowed on tax slab (#21475) 2020-04-30 11:02:45 +05:30
Saqib
61584c8601 fix: print heading field shown in gst section for india region (#21500)
Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-04-30 10:58:01 +05:30
rohitwaghchaure
99dfe6c571 Merge pull request #21469 from Alchez/v12-update-lead-contact
fix: update lead if contact details are changed (v12)
2020-04-29 22:14:24 +05:30
Saqib Ansari
fd1ab37bc8 chore: validate and warn payment against paid invoices 2020-04-29 15:20:05 +05:30
Rohan Bansal
bd969477b1 fix: AttributeError 2020-04-29 13:09:16 +05:30
Deepesh Garg
a816788039 Merge pull request #21494 from deepeshgarg007/item_wise_sale_purchase_v12
fix: Group by filter fix in item wise sales and purchase register
2020-04-29 12:36:22 +05:30
Deepesh Garg
c099a410c8 fix: Group by filter fix in item wise sales and purchase register 2020-04-29 12:21:52 +05:30
Anurag Mishra
e33c44ae33 fix: Permission issue Employee Tax exemption (#21492) 2020-04-29 12:13:26 +05:30
Marica
4930233bbe Merge branch 'version-12-hotfix' into commonify-warehouse-autofill-hotfix 2020-04-29 11:36:48 +05:30
Deepesh Garg
e383727394 Merge pull request #21487 from rohitwaghchaure/not-able-to-make-payment-request-against-fees-hotfix
fix: payment request not able to make against fees
2020-04-29 09:36:55 +05:30
Rohit Waghchaure
3d2dcd8c59 fix: payment request not able to make against fees 2020-04-29 02:29:43 +05:30
Deepesh Garg
6647f45eb5 Merge pull request #21477 from deepeshgarg007/duplicate_code_v12
fix: Remove duplicate code from accounting dimension
2020-04-28 20:32:36 +05:30
Deepesh Garg
bd9625d150 fix: Remove duplicate code from accounting dimension 2020-04-28 20:26:56 +05:30
marination
c832291bd6 fix: Handle empty child table 2020-04-28 19:23:13 +05:30
Rohan Bansal
7c67c38bc5 fix: update lead if contact details are changed 2020-04-28 16:12:02 +05:30
Rucha Mahabal
8076deb080 Merge pull request #21465 from akurungadam/patch-3
fix: clinical procedure - set stock entry type
2020-04-28 15:37:30 +05:30
Anoop
dd8566ecde fix: clinical procedure - set stock entry type
clinical procedure - complete and consume - set stock entry type to "Material Issue"

report -
https://discuss.erpnext.com/t/cant-complete-and-consume/60927
2020-04-28 14:52:10 +05:30
Marica
fa7d496413 fix: Blanket Order in SO/PO child tables (#21444) 2020-04-28 13:00:51 +05:30
Deepesh Garg
3b8ad79445 Merge pull request #21458 from deepeshgarg007/gross_profit_width_v12
fix: Default column width in Gross profit report
2020-04-28 12:36:00 +05:30
Deepesh Garg
558c3284a0 fix: Default column width in Gross profit report 2020-04-28 12:03:05 +05:30
Nabin Hait
95f7807b78 fix: Income tax slab patch (#21448)
* fix: reload income_tax_slab_other_charges in patch

* fix: reload lower_deduction_certificate in patch
2020-04-28 11:15:57 +05:30
Deepesh Garg
6ece7fe265 Merge pull request #21438 from deepeshgarg007/ewb_bill_error_message_v12
fix: E-Way bill error message
2020-04-27 15:04:45 +05:30
Deepesh Garg
5aa2241439 fix: Test 2020-04-27 14:06:38 +05:30
Deepesh Garg
68d7b77314 fix: Utils messsage cleanup 2020-04-27 11:16:39 +05:30
Deepesh Garg
93e8b5d87e fix: E-way bill fix in List view 2020-04-27 11:15:23 +05:30
Deepesh Garg
6b082b5edb fix: E-way bill fix in sales invoice 2020-04-27 11:15:04 +05:30
Rucha Mahabal
093720b870 fix: add hook for sending appointment reminders in v12 (#21432) 2020-04-27 10:36:11 +05:30
Rucha Mahabal
e1697fd9cc Merge pull request #21429 from akurungadam/patch-2
fix: name 'patient' is not defined
2020-04-27 00:42:39 +05:30
Anoop
c2f952045c fix: name 'patient' is not defined
possible escape while refactoring, fixed in develop added here.

Reported via -
https://discuss.erpnext.com/t/healthcare-outpatient-sms-alert-error/60812
2020-04-27 00:33:26 +05:30
Nabin Hait
2b5bca4ce6 fix: Procurement tracker report (#21422)
* fix: procurement report data was not coming

* fix: leave allocation minor issue
2020-04-26 23:28:54 +05:30
Himanshu
de6f0f7a05 fix: add quality inspection template (#21424) 2020-04-26 23:27:29 +05:30
Anurag Mishra
1f3584b9a8 refactor: employee leave balance report v12 (#21282)
* fix: Employee Balance repor fixes

* refactor: Employee Leave Balance report For Version-12

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-04-26 21:05:09 +05:30
Deepesh Garg
5bfdf0af4d feat: Allow tax withholding category selection at invoice level (#20871)
* feat: Allow tax withholding category selection at invoice level

* fix: Linitng fixes

* feat: TDS calculation using common PAN

* fix: Add provision to deduct Lower TDS in purchase invoice

* fix: Consider only ref docs company while computing TDS

* fix: Default permission fixes

* fix: Add validation for dates in fiscal year

* fix: Undefined variable
2020-04-26 20:10:16 +05:30
rohitwaghchaure
14a50a9403 Merge pull request #21390 from marination/stock-entry-qty-hotfix
fix: Issues on qty trigger in Stock Entry Detail
2020-04-26 18:00:07 +05:30
marination
b724fec6c9 fix: Remove callback outside if condition 2020-04-26 17:18:41 +05:30
Nabin Hait
1d5ea4feee feat: Income tax slab (#21406)
* Feat: Multiple tax as per new taxation rule

* patch:for multiple tax slab, fix: payroll and exemption validation

* Test: Fixture

* feat: income tax slab with other charges and tax exempted deduction components

* fix: added missing init file

* fix: Patch fixed

* fix: Patch fixed

* fix: test fixes

* fix: validate duplicate exemption declaration

* fix: payment entry test case

Co-authored-by: Anurag Mishra <mishranaman123@gmail.com>
2020-04-26 12:37:52 +05:30
Saqib
7fedff260d fix: (ux) set jv voucher type depending on mode of payment (#21412) 2020-04-26 09:41:02 +05:30
rohitwaghchaure
9fc78e44ea Merge pull request #21402 from DeeMysterio/hotfix-si-actual-qty
feat(accounting): show actual qty for warehouse in sales invoice
2020-04-25 01:19:04 +05:30
Diksha Jadhav
be97087d32 feat(accounting): show actual qty for warehouse in sales invoice 2020-04-24 21:09:59 +05:30
Rucha Mahabal
b0f829e541 Merge pull request #21381 from anupamvs/email-camp-end-date-hotfix
fix: set end_date in Email Campaign
2020-04-24 20:44:18 +05:30
Rucha Mahabal
0455a96dcd Merge branch 'version-12-hotfix' into email-camp-end-date-hotfix 2020-04-24 20:04:55 +05:30
Deepesh Garg
d771ba2895 Merge pull request #21398 from nextchamp-saqib/tax-breakup-for-cn-v12
fix: show positive taxes in credit notes
2020-04-24 19:01:46 +05:30
rohitwaghchaure
c8e5b42dba Merge pull request #21395 from nextchamp-saqib/price-rule-fix
fix: rate gets overwritten when pricing rule is set
2020-04-24 16:46:47 +05:30
Saqib Ansari
424dbef139 fix: rate gets overwritten when pricing rule is set 2020-04-24 16:40:03 +05:30
Saqib Ansari
6256ca81b1 fix: show positive taxes in credit notes 2020-04-24 16:01:11 +05:30
marination
fda451f3e3 fix: Issues on qty trigger in Stock Entry Detail 2020-04-23 20:35:18 +05:30
Rucha Mahabal
2307385210 Merge branch 'version-12-hotfix' into email-camp-end-date-hotfix 2020-04-23 19:42:04 +05:30
Deepesh Garg
bd4b5da11b feat: Payment allocation based on payment terms (#20946)
* feat: Payment allocation based on payment terms

* fix: Add desccription for checkbox

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-04-23 16:09:08 +05:30
Sahil Khan
7ed7c3237a Merge branch 'v12-pre-release' into version-12 2020-04-23 13:55:06 +05:30
Sahil Khan
522cf08f67 bumped to version 12.7.1 2020-04-23 14:15:06 +05:50
Anupam K
ef7f9c6ecc Review changes 2020-04-23 13:45:19 +05:30
Anupam K
097c643a59 setting end date in email campaign 2020-04-23 12:29:00 +05:30
Deepesh Garg
dd560d676e fix: Budget against accounting dimensions (#21269)
* fix: Budget warning against custom accounting dimension

* fix: Codacy
2020-04-23 10:35:35 +05:30
rohitwaghchaure
794fd75ca1 fix: bom update cost is not working (#21348)
* fix: bom update cost is not working

* added test case for bom cost
2020-04-23 09:48:31 +05:30
rohitwaghchaure
015e1c123c fix: patch and validation message to fix target warehouse issue (#21370) 2020-04-23 09:46:12 +05:30
rohitwaghchaure
f49e66e721 fix: BOM stock report (#21377)
Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-04-23 09:45:26 +05:30
rohitwaghchaure
8fd1e08763 fix: incorrect out value in stock balance due to precision issue (#21378) 2020-04-23 09:44:54 +05:30
marination
c5d108c954 chore: Commonify autofilling warehouses in child tables 2020-04-23 00:38:19 +05:30
rohitwaghchaure
f2c43ca81e fix: patch and validation message to fix target warehouse issue (#21359) 2020-04-22 16:08:36 +05:30
Deepesh Garg
9d6ee99d99 Merge pull request #21368 from scmmishra/cal-report-12
fix: specify column width
2020-04-22 12:03:35 +05:30
Shivam Mishra
0578cf5a73 fix: specify column width 2020-04-22 11:53:18 +05:30
Michelle Alva
499f9198b9 fix: better error message due date (#21366) 2020-04-22 11:37:09 +05:30
Deepesh Garg
4daab871d5 Merge pull request #21354 from anupamvs/error-message-changes-hotfix
fix: Add better error message
2020-04-22 09:28:16 +05:30
Deepesh Garg
17585710b2 Merge pull request #21360 from rohitwaghchaure/fixed-unsupported-operand-type-issue
fix: unsupported operand type issue in pricing rule
2020-04-22 08:43:29 +05:30
Rohit Waghchaure
56e7887511 fix: unsupported operand type issue in pricing rule 2020-04-22 02:45:55 +05:30
Anupam K
08c782709a Better error message 2020-04-21 17:03:56 +05:30
Anupam Kumar
fb20982194 fix: on item change UOM not updated (#21254)
* fix: on item change UOM not updated

* removing uom from js
2020-04-21 12:57:08 +05:30
Marica
9453add3dd fix: Re-order Item Error Email format (#21343)
* fix: Re-order Item Error Email format

* fix: Translated strings
2020-04-21 12:53:17 +05:30
rohitwaghchaure
c62cf98d7e fix: scrap items order not showing correctly in stock entry (#21347) 2020-04-21 11:26:49 +05:30
rohitwaghchaure
ea8e302833 Merge pull request #21339 from scmmishra/issue-list-context-12
fix: filters as dictionary
2020-04-21 01:25:34 +05:30
rohitwaghchaure
5d3fd9dc43 Merge pull request #21344 from rohitwaghchaure/fixed_free_item_qty_hotfix
fix: free item quantity issue
2020-04-21 01:08:13 +05:30
rohitwaghchaure
c9ba147615 Merge pull request #21323 from marination/pick-list-enhance-hotfix
Pick list enhance hotfix
2020-04-21 01:07:50 +05:30
Rohit Waghchaure
7ef57533ec fix: free item quantity issue 2020-04-21 00:19:19 +05:30
gavin
712abe4cd2 Merge pull request #21338 from gavindsouza/tally-migration-fixes-v12
fix: Tally migration
2020-04-20 19:30:40 +05:30
Shivam Mishra
1a93977ef7 fix: filters as dictionary 2020-04-20 13:56:20 +05:30
Gavin D'souza
fc078c1d45 style: removed unused imports and updated formatting 2020-04-20 12:57:59 +05:30
Gavin D'souza
77532b96b8 fix: strip data fields of whitespaces 2020-04-20 12:57:48 +05:30
Gavin D'souza
4171c5ceeb fix(tally-migration): DocType improvement 2020-04-20 12:57:36 +05:30
Gavin D'souza
fa07e0fb9d fix: handle errors in enqueued methods and update status 2020-04-20 12:57:24 +05:30
Raffael Meyer
e80702b6c2 fix(regional): backport DATEV fix (#21281)
* fix: quote nonnumeric values

* fix(DATEV Settings): restrict max length of IDs

* fix: display Columns as Dynamic Link instead of as Data

* fix: add column "Belegfeld 1"

* fix: truncate column Buchungstext to 60 chars

* fix: make header compatible to current DATEV Format 7.00

* fix: column names and descriptions
2020-04-19 20:16:18 +05:30
Nabin Hait
7345b8494d fix: removed unwanted method call from scheduler 2020-04-19 19:29:47 +05:30
Nabin Hait
5817adbb64 fix: Valid warehouse in woocommerce syncing and other small fixes (#21332)
* fix: Valid warehouse in woocommerce syncing

* fix: dmall fixes in gross & net profit report

* fix: company is required for getting party details

* fix: None issue while getting raw material rate based on last purchase rate
2020-04-19 19:28:32 +05:30
Anoop
4de4cb055f fix: removed reference to method not in version-12 (#21335)
removed scheduler event incorrectly added in this version.

as reported on discuss -
https://discuss.erpnext.com/t/internal-server-error-and-healthcare-module-missing/60479/6?u=akurungadam
2020-04-19 19:26:31 +05:30
Deepesh Garg
19c841eb64 fix: Total amount field ordering in transactions (#21315) 2020-04-18 22:14:14 +05:30
Deepesh Garg
5f10e0ac26 Merge pull request #21329 from rohitwaghchaure/fixed-account-name-in-gl_print-hotfix
fix: account name not showing in the gl print
2020-04-18 20:41:03 +05:30
Rohit Waghchaure
64bb910015 fix: account name not showing in the gl print 2020-04-18 19:04:20 +05:30
marination
6b7848232c fix: Use reload_doc in patch 2020-04-17 21:49:25 +05:30
marination
b9df3793fb fix: Commonified code and added server side validation 2020-04-17 21:47:58 +05:30
marination
09f95858c0 fix: Added patch to patches.txt 2020-04-17 21:47:41 +05:30
marination
fcd7548220 fix: Pick List Enhancements 2020-04-17 21:45:58 +05:30
sahil28297
9989bfe2dc Merge pull request #21322 from Thunderbottom/gl-entry-fix
fix: add label to gl entry
2020-04-17 20:26:23 +05:30
Mangesh-Khairnar
4af9ab702f fix: add label to gl entry 2020-04-17 20:22:28 +05:30
Deepesh Garg
b882d7046b Merge pull request #21321 from Mangesh-Khairnar/gl-entry-label-fix
fix: add label to gl entry
2020-04-17 19:59:19 +05:30
Mangesh-Khairnar
b33f82d4e8 fix: add label to gl entry 2020-04-17 19:55:20 +05:30
rohitwaghchaure
e8bd7c0233 Merge pull request #21317 from sahil28297/fix_italy_einvoicing_v12_pre_release
fix(patch): reload 'Import Supplier Invoice' doc
2020-04-17 13:33:55 +05:30
rohitwaghchaure
cfb00fb887 Merge pull request #21314 from sahil28297/fix_italy_einvoicing_v12_hotfix
fix(patch): reload 'Import Supplier Invoice' doc
2020-04-17 13:29:12 +05:30
rohitwaghchaure
2128dc8d87 Merge pull request #21316 from sahil28297/fix_italy_einvoicing_v12
fix(patch): reload 'Import Supplier Invoice' doc
2020-04-17 13:28:22 +05:30
Sahil Khan
46ae415923 fix(patch): reload 'Import Supplier Invoice' doc 2020-04-17 13:24:28 +05:30
Sahil Khan
21b34b9607 fix(patch): reload 'Import Supplier Invoice' doc 2020-04-17 13:22:28 +05:30
Sahil Khan
44f0c077ff fix(patch): reload 'Import Supplier Invoice' doc 2020-04-17 13:15:15 +05:30
Sahil Khan
d959463cbc Merge branch 'v12-pre-release' into version-12 2020-04-17 11:27:54 +05:30
Sahil Khan
e6d02ecd7f bumped to version 12.7.0 2020-04-17 11:47:54 +05:50
Nabin Hait
3b2aa5ead3 fix: requested qty for customer provided item and rate for sales (#21300)
* fix: requested qty for customer provided item and rate for sales

* fix: requested qty for material transfer

* fix: customer provided item can be sales item

* fix: requested qty test cases
2020-04-17 10:52:23 +05:30
Nabin Hait
4770626bbd fix: requested qty for customer provided item and rate for sales (#21301)
* fix: requested qty for customer provided item and rate for sales

* fix: requested qty for material transfer

* fix: customer provided item can be sales item

* fix: requested qty test cases
2020-04-17 10:52:18 +05:30
rohitwaghchaure
df045e9f6d Merge pull request #21308 from rohitwaghchaure/fixed_job_card_time_issue_pre_release
fix: job card timer issue
2020-04-17 00:56:16 +05:30
rohitwaghchaure
2c045179a3 Merge pull request #21310 from rohitwaghchaure/fixed_timer_issue_version_12_hotfix
fix: job card timer issue
2020-04-17 00:55:50 +05:30
Rohit Waghchaure
da83af1213 fix: job card timer issue 2020-04-17 00:54:20 +05:30
Rohit Waghchaure
c10c920914 fix: job card time issue 2020-04-17 00:48:08 +05:30
rohitwaghchaure
61fc2d3263 fix: job card time issue (#21306) 2020-04-16 23:57:49 +05:30
rohitwaghchaure
a66be0f5c5 Merge pull request #21295 from nabinhait/b12-7-pre-release-fix1
fix: requested qty calculation and some other small fixes
2020-04-16 19:19:17 +05:30
rohitwaghchaure
fd99cc8494 Merge pull request #21294 from nabinhait/b12-7-pre-release-1
fix: requested qty calculation and some other small fixes
2020-04-16 19:18:54 +05:30
Nabin Hait
cf1f777ae2 Merge branch 'version-12-hotfix' into b12-7-pre-release-fix1 2020-04-16 19:08:35 +05:30
Nabin Hait
9ea9ed4256 Merge branch 'v12-pre-release' into b12-7-pre-release-1 2020-04-16 19:07:45 +05:30
Deepesh Garg
c7b5309dcd Merge pull request #21105 from nextchamp-saqib/asset-cat-validation-v12
fix: no server side validations for accounts in asset category
2020-04-16 16:25:37 +05:30
Saqib
65eea97e0c fix: serial and batch selection from delivery note bug fix (#21293) 2020-04-16 16:22:16 +05:30
Saqib
45d454b14d fix: serial and batch selection from delivery note bug fix (#21292) 2020-04-16 16:21:54 +05:30
Saqib Ansari
f390b8062c fix: incorrect transalation 2020-04-16 16:16:58 +05:30
Nabin Hait
90adf076f3 fix: Made received qty readonly and no-copy 2020-04-16 14:30:26 +05:30
Nabin Hait
2b6942d9ce fix: Made release date mandatory 2020-04-16 14:30:18 +05:30
Nabin Hait
4dca806737 fix: requested qty calculation fix for UOM 2020-04-16 14:28:22 +05:30
Nabin Hait
bd57cbda03 fix: requested qty calculation fix for UOM 2020-04-16 14:27:25 +05:30
Nabin Hait
31c8f8e795 chore: added change log v12.7.0 (#21289) 2020-04-16 12:46:42 +05:30
Nabin Hait
ccfc005932 fix: Made received qty readonly and no-copy 2020-04-16 11:25:15 +05:30
Nabin Hait
b4dfc8e1bf fix: Made release date mandatory 2020-04-16 11:24:43 +05:30
Deepesh Garg
3379cac956 Merge branch 'version-12-hotfix' into asset-cat-validation-v12 2020-04-16 11:23:21 +05:30
Deepesh Garg
ed48755a6f Merge pull request #21266 from vishdha/chart_account_hotfix
fix: Chart of account importer UX improved
2020-04-16 11:21:44 +05:30
vishdha
c43e759b87 fix: fix Transalation 2020-04-16 11:12:52 +05:30
rohitwaghchaure
05e9af60bd Merge pull request #21284 from marination/stock-entry-get-items-mr-hotfix
fix: Fetch Material Requests with type Issue as well in Stock Entry via Get Items from
2020-04-16 00:33:15 +05:30
Marica
3635b17f22 Merge pull request #21287 from nextchamp-saqib/warehouse-fix-pre-release
fix: warehouse unset when cannot find item warehouse
2020-04-15 23:56:39 +05:30
Marica
de9220568e Merge pull request #21286 from nextchamp-saqib/warehouse-fix
fix: warehouse unset when cannot find item warehouse
2020-04-15 23:55:52 +05:30
Saqib Ansari
5d3e211531 fix: warehouse unset when cannot find item warehouse
* cannot set delivery date when all items gets deleted and new are added
2020-04-15 22:10:00 +05:30
Saqib Ansari
bde676ef74 fix: pos not accessible without default customer 2020-04-15 22:09:50 +05:30
Saqib Ansari
4879858657 fix: warehouse unset when cannot find item warehouse
* cannot set delivery date when all items gets deleted and new are added
2020-04-15 22:08:12 +05:30
Saqib Ansari
773eb6ce68 fix: pos not accessible without default customer 2020-04-15 22:08:04 +05:30
marination
8e1201d31e fix: Fetch Material Requests with type Issue as well in Stock Entry via Get Items from 2020-04-15 21:25:12 +05:30
rohitwaghchaure
18a04a24b3 Merge pull request #21278 from marination/default-item-manufacturer-pre-release
feat: Provision to set Default Item Manufacturer
2020-04-15 16:47:47 +05:30
marination
64f053d583 feat: Provision to set Default Item Manufacturer
- Is Default checkbox added in Item Manufacturer
- Default Item Manufacturer and Part No fields added to Item Master
- Manufacturer Part No field editable in all child tables with validation
- Manufacturer and Part No auto fetched via get_item_details in child table
2020-04-15 16:43:41 +05:30
rohitwaghchaure
bf401290d1 Merge pull request #21218 from marination/default-item-manufacturer-hotfix
feat: Provision to set Default Item Manufacturer
2020-04-15 16:36:01 +05:30
Nabin Hait
1fc8de32f1 fix: merge conflict 2020-04-14 20:31:50 +05:30
rohitwaghchaure
09f6199b36 fix: pick list test (#21267) 2020-04-14 19:43:26 +05:30
Rohan
b0f000b1c8 fix: order_type validation restriction (#18096) (#21264)
Co-authored-by: Don-Leopardo <46027152+Don-Leopardo@users.noreply.github.com>
2020-04-14 19:42:21 +05:30
Nabin Hait
49b653b444 fix: merge conflict 2020-04-14 19:34:48 +05:30
Nabin Hait
107229de04 fix: Fixed expense claim payment status 2020-04-14 19:33:05 +05:30
vishdha
af33f71562 fix: message bold 2020-04-14 17:20:48 +05:30
vishdha
25c26b5904 fix: Chart of account importer UX improved 2020-04-14 17:20:48 +05:30
Deepesh Garg
1112bd0f23 Merge pull request #21260 from Alchez/v12-stock-available-order-desk
fix: [minor] show stock UOM in POS (v12)
2020-04-14 17:00:49 +05:30
rohitwaghchaure
8c931e4185 Merge pull request #21263 from rohitwaghchaure/fixed_stock_reco_test_case
fix: stock reco test case
2020-04-14 14:50:49 +05:30
Rohit Waghchaure
5771aabc49 fix: stock reco test case 2020-04-14 14:49:47 +05:30
Rohan Bansal
954c6540fd fix: show stock UOM in POS 2020-04-14 14:22:10 +05:30
rohitwaghchaure
4eea8bd2c0 fix: on changing qty free item not removed (#21250) 2020-04-14 12:49:38 +05:30
Marica
335d35a988 fix: Lead Contact with blank first name via Customer (#21248) 2020-04-14 11:53:00 +05:30
Faris Ansari
2833db559e fix: Set Price List in case of User Permissions (#21238)
Frontport of #18968
2020-04-14 11:50:54 +05:30
rohitwaghchaure
3d667bfe55 fix: minor issues (#21203) 2020-04-14 11:48:56 +05:30
Marica
7841653a03 fix: Project Update Email Error (#21209)
* fix: Project Update Email Error

* fix: Removed mandatory depends on

Co-authored-by: Himanshu <himanshuwarekar@yahoo.com>
2020-04-14 11:47:22 +05:30
Saqib
02a9b5a8a5 chore: hide redundant base received amount (#21229)
* fix: formatting

* chore: hide redundant base received amount
2020-04-14 11:45:56 +05:30
Vishal Dhayagude
4b1d0d19fb fix: str object not callable (#21228) 2020-04-14 11:42:22 +05:30
Mangesh-Khairnar
8b31e3ec2a fix: consider revereted expired leaves entry (#21256) 2020-04-14 09:33:53 +05:30
Marica
b2ba5dbd6a Merge pull request #21214 from vishdha/batch_message
fix(UX): batch error message improved
2020-04-13 15:38:59 +05:30
Marica
b2a9413c06 Merge branch 'version-12-hotfix' into batch_message 2020-04-13 13:20:43 +05:30
vishdha
1b15734bd8 fix: batch message ux improved 2020-04-13 12:36:54 +05:30
Deepesh Garg
725d6b235f Merge pull request #21241 from mujeerhashmi/v12_gstr_3b_report_fix
fix: GSTR 3B Report tax amount calculation
2020-04-13 11:05:24 +05:30
Syed Mujeer Hashmi
87c6b6d12f fix: GSTR 3B Report tax amount calculation
The tax amount after discount amount should be considered for tax
calculation.

Fixes #21231

Signed-off-by: Syed Mujeer Hashmi <mujeerhashmi@4csolutions.in>
(cherry picked from commit 58a16f1a3b)
2020-04-11 22:56:00 +05:30
Deepesh Garg
703e20df79 Merge pull request #21178 from nextchamp-saqib/purchase-register-filters-v12
feat: (minor) purchase register filters
2020-04-11 18:12:20 +05:30
Saqib Ansari
ae8a0940f5 fix: cannot iterate over dict_keys 2020-04-11 18:12:13 +05:30
Saqib Ansari
de423f8183 Merge branch 'asset-cat-validation-v12' of https://github.com/nextchamp-saqib/erpnext into asset-cat-validation-v12 2020-04-11 18:11:42 +05:30
Nabin Hait
c7bf050e8b Merge branch 'version-12-hotfix' into asset-cat-validation-v12 2020-04-11 10:21:17 +05:30
Deepesh Garg
9d0af22b88 Merge pull request #21202 from marination/serial-no-status-hotfix
fix: Added Status field in Serial No for filter and report builder
2020-04-10 20:48:45 +05:30
Deepesh Garg
073d1c4c13 fix: SQL ssyntax error 2020-04-10 20:33:03 +05:30
Deepesh Garg
c4115c19e4 Merge branch 'version-12-hotfix' of https://github.com/frappe/erpnext into purchase-register-filters-v12 2020-04-10 20:32:30 +05:30
Saqib Ansari
39b8e150bf fix: travis 2020-04-10 12:39:34 +05:30
marination
849ec84852 feat: Provision to set Default Item Manufacturer
- Is Default checkbox added in Item Manufacturer
- Default Item Manufacturer and Part No fields added to Item Master
- Manufacturer Part No field editable in all child tables with validation
- Manufacturer and Part No auto fetched via get_item_details in child table
2020-04-09 14:50:03 +05:30
rohitwaghchaure
38896efd6d Merge pull request #21157 from marination/mr-customer-provided-to-stock-entry-hotfix
fix: Mapping Customer Provided Material Request to Stock Entry
2020-04-09 13:19:41 +05:30
Marica
b225ffa003 fix: Error on any new doc from Shipping Rule. (#21207) 2020-04-09 12:01:29 +05:30
vishdha
c3bb5a97f8 fix: item_name added for message 2020-04-09 11:25:02 +05:30
marination
cdd589a58a fix: Status field in Serial No for filter and report builder 2020-04-08 16:08:09 +05:30
Mangesh-Khairnar
bf5c7fc4d3 fix(MWS): add new regions to marketplace (#21195)
* fix(MWS): add marketplace region for uae

* fix: rename the fields for mws integrations

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-04-08 12:56:22 +05:30
Saqib
27eba1fc61 fix: cannot find accounting module while rendering breadcrumb (#21191) 2020-04-08 11:49:34 +05:30
Nabin Hait
95b6e8afda fix: Replace newlines with spaces before evaluation of condition and formula (#21166) 2020-04-08 09:34:39 +05:30
Saqib
7c1f2ce4eb fix: [ux] enforce 'get references from' in payment order (#21176) 2020-04-08 09:20:41 +05:30
rohitwaghchaure
af64300c18 Merge pull request #21066 from marination/bin-requested-qty-hotfix
fix: Updated Bin Requested Qty logic
2020-04-07 18:06:07 +05:30
rohitwaghchaure
6668544b4c Merge pull request #21169 from rohitwaghchaure/get_serial_nos_based_on_posting_date_and_time
fix: get serial nos based on posting date and time
2020-04-07 18:02:34 +05:30
Deepesh Garg
5e841b40f3 Merge pull request #21189 from deepeshgarg007/dimension_filter_fix_v12
fix: Use separate condition for tree and normal doctype
2020-04-07 15:23:25 +05:30
Deepesh Garg
5e79f763b5 fix: Use separate condition for tree and normal doctype 2020-04-07 15:19:21 +05:30
Rohit Waghchaure
aebf0e47f3 fix: get serial nos based on posting date and time 2020-04-07 14:41:24 +05:30
rohitwaghchaure
645ef9db87 Merge pull request #21182 from marination/purchase-receipt-scan-barcode-hotfix
feat: Scan Barcode in Purchase Receipt
2020-04-07 14:37:17 +05:30
Marica
d76ecb960e fix: Validate Serial/Batch No naming series in Item itself (#21168)
* fix: Validate Serial/Batch No naming series in Item itself

* fix: Consider '#' too
2020-04-07 14:15:48 +05:30
Vishal Dhayagude
d2da8bd6e2 fix(shopping cart): UX Improvements (#21035)
* fix: nontype error for resolved and moved place order button at bottom left

* fix: removed inline style

* fix: Request for quotation move to lower right

* fix: move buttons

Co-authored-by: Naren <patilnarendra3@gmail.com>
Co-authored-by: Saqib <nextchamp.saqib@gmail.com>
2020-04-07 12:19:40 +05:30
Saqib
a5fb07e475 fix(shopping-cart): address is made mandatory to place order (#20922)
fix(shopping-cart): address is made mandatory to place order
2020-04-07 12:09:28 +05:30
rohitwaghchaure
bcf1897e60 Merge pull request #21173 from rohitwaghchaure/material_request_incorrect_filter_issue
fix: material request type manufacture shows items with Is Purchase I…
2020-04-07 12:06:04 +05:30
Vishal Dhayagude
044abbace8 fix: Make Sales Invoice paid when it create from shopping cart (#20878)
Co-authored-by: Saqib <nextchamp.saqib@gmail.com>
2020-04-07 12:05:18 +05:30
Deepesh Garg
36a763bdc5 Merge pull request #21106 from nextchamp-saqib/payment-req-status-fix-v12
fix: payment request status fixes
2020-04-07 10:13:29 +05:30
marination
2dd488f878 feat: Scan Barcode in Purchase Receipt 2020-04-06 23:16:57 +05:30
Saqib Ansari
34288b0f38 feat: (minor) purchase register filters 2020-04-06 18:26:03 +05:30
Rohit Waghchaure
d7389b1920 fix: material request type manufacture shows items with Is Purchase Item enabled 2020-04-06 17:49:49 +05:30
Marica
42eddbf89b Merge branch 'version-12-hotfix' into bin-requested-qty-hotfix 2020-04-06 16:38:06 +05:30
Saqib
8e206f55be Merge branch 'version-12-hotfix' into cart-address-update-hotfix 2020-04-06 14:22:06 +05:30
Saqib Ansari
de9c73c5cd fix: tests 2020-04-06 12:41:51 +05:30
Saqib Ansari
4ea7df69b4 fix: change request status on payment entry cancel 2020-04-06 12:35:54 +05:30
Marica
da6ef63bc4 Merge pull request #20865 from deepeshgarg007/po_supplier_skip_v12
feat: Allow PI creation without PO
2020-04-06 12:16:02 +05:30
Nabin Hait
5da9663ae4 Merge branch 'version-12-hotfix' into expense-claim-fix-v12 2020-04-06 12:14:27 +05:30
Nabin Hait
322660c644 fix: Merge conflict 2020-04-06 11:16:31 +05:30
Nabin Hait
2378572e1b fix: update attendace from leave application (#21154) 2020-04-06 10:15:50 +05:30
Deepesh Garg
ab436d4147 Merge pull request #21164 from deepeshgarg007/gst_3b_user_perm_v12
fix: User permissions in GSTR 3B report
2020-04-04 21:48:31 +05:30
Deepesh Garg
18cd3a029d fix: User permissions in GSTR 3B report 2020-04-04 20:07:44 +05:30
Saqib
dba4bd6f26 fix: [ux] credit to & debit to error message (#21133)
Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-04-03 19:36:00 +05:30
Marica
7bc70758fb Merge branch 'version-12-hotfix' into po_supplier_skip_v12 2020-04-03 18:35:29 +05:30
Deepesh Garg
69e0700101 Merge pull request #21158 from scmmishra/coa_importer
fix: TypeError for _ in coa importer
2020-04-03 18:34:44 +05:30
marination
dfe4d36e8d fix: Test Cases and block expense on outward entry
- Throw error if rate present against Customer Provided Item in DN and SI
- Added test cases for Sales Invoice and Delivery Note
- Allow SI and DN with 0 rate in Test
2020-04-03 18:29:30 +05:30
Shivam Mishra
55ded1379e Merge branch 'version-12-hotfix' into coa_importer 2020-04-03 17:21:42 +05:30
Shivam Mishra
64ed25abfb fix: TypeError for _ 2020-04-03 17:17:04 +05:30
marination
7b70679bf0 fix: Mapping Customer Provided Material Request to Stock Entry
- Fixed inability to map Material Request to Stock Entry
- Commonified Customer Provided Item validation
2020-04-03 15:57:32 +05:30
Deepesh Garg
31fbafe16f Merge pull request #21151 from scmmishra/fix-patch-dashboard-12
fix (tests): module import error for Dashboard Chart Field
2020-04-03 11:05:11 +05:30
Shivam Mishra
878e4cb3ce fix: lms quiz type error (#21153) 2020-04-03 10:07:44 +05:30
Marica
73bc29c011 fix: Update Received Qty in Material Request as per Stock UOM (#21055)
* fix: Update Received Qty in Material Request as per Stock UOM

* fix: Process each PR only once

* fix: minor suggested changes

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-04-03 10:06:39 +05:30
Saqib
44dbd98d90 fix: cannot set warehouse on deleting all so items and updating them (#21079)
* fix: cannot set warehouse on deleting all so items and updating them

* fix: travis

* fix: travis

* fix: docname is editable in update items dialog

Co-authored-by: Marica <maricadsouza221197@gmail.com>
2020-04-03 10:01:14 +05:30
Saqib Ansari
04201028d1 fix: tests 2020-04-02 22:18:12 +05:30
Marica
8b5693b77f Merge branch 'version-12-hotfix' into po_supplier_skip_v12 2020-04-02 21:14:19 +05:30
Deepesh Garg
76d2bd9633 Merge branch 'version-12-hotfix' into fix-patch-dashboard-12 2020-04-02 20:24:10 +05:30
Deepesh Garg
83c7561a84 Merge pull request #21149 from deepeshgarg007/travis_company_fixture_v12
fix: Travis(v12)
2020-04-02 20:22:53 +05:30
Shivam Mishra
f8cb81c9f9 fix (tests): reload doctype 2020-04-02 19:59:47 +05:30
marination
4a59b1d3b5 fix: Fixed Test Cases for Material Request
- Reduce quantity on Material Transfer/Issue
- No effect on Customer Provided Material Request
2020-04-02 19:38:38 +05:30
Deepesh Garg
763aab3301 fix: Travis 2020-04-02 19:12:36 +05:30
rohitwaghchaure
0bbc78cdbd Merge pull request #21145 from anupamvs/year-sum-hotfix
Sum of years not needed.
2020-04-02 12:00:55 +05:30
Anupam K
29fc063764 Sum of years not needed. 2020-04-02 11:34:42 +05:30
Anupam K
a66da0ddf4 Sum of years not needed. 2020-04-02 01:06:30 +05:30
Deepesh Garg
4fd447a31d Merge pull request #21136 from marination/stock-ledger-typo-hotfix
fix: Typo in stock level validation in Stock Ledger
2020-04-01 11:20:55 +05:30
marination
122049a9b4 fix: Typo in stock level validation in Stock Ledger 2020-04-01 11:07:46 +05:30
Marica
6a49ea5262 Merge pull request #21097 from rohitwaghchaure/item_code_showing_as_mandatory_issue_hotfix
fix: item code showing as mandatory even if the 'Item Naming By' is set as Naming Series in stock settings
2020-04-01 10:56:14 +05:30
Marica
9f709aa64c Merge branch 'version-12-hotfix' into item_code_showing_as_mandatory_issue_hotfix 2020-04-01 10:46:55 +05:30
Deepesh Garg
702e0ac29e Merge pull request #21117 from nextchamp-saqib/customer-group-price-list-in-pos-v12
fix: customer group price list not fetched in pos
2020-04-01 09:41:04 +05:30
Saqib Ansari
954276ff0e fix: check if selling price exists then set it 2020-03-31 18:49:28 +05:30
Saqib Ansari
0541b2ee27 fix: travis 2020-03-31 18:44:38 +05:30
Saqib Ansari
086e5c4dac fix: no server side validations for accounts in asset category 2020-03-31 18:21:08 +05:30
Marica
dc6a3ae99c Merge pull request #21071 from pipech/v12_fix_pos-display-stock-item
fix(pos): fix pos not display only in-stock item
2020-03-31 18:13:44 +05:30
Marica
2512435a7b Merge branch 'version-12-hotfix' into v12_fix_pos-display-stock-item 2020-03-31 18:08:10 +05:30
Deepesh Garg
7d8d351668 Merge pull request #21064 from P-Froggy/fix-add-bank-account-reference-in-supplier-dashboard-hotfix
fix: Add missing bank accounts reference in supplier dashboard
2020-03-31 16:33:19 +05:30
Deepesh Garg
0126ef78c8 Merge pull request #21093 from scmmishra/multiple-option-fix-12
fix: use setup from Supplier Quotation Controller
2020-03-31 16:29:10 +05:30
rohitwaghchaure
489a5d203f Merge pull request #21016 from marination/so-mr-mapping-uom-hotfix
fix: UOM fixes in Sales Order,Material Request & Production Plan
2020-03-31 16:11:57 +05:30
Deepesh Garg
4716fc5eb8 Merge branch 'version-12-hotfix' into fix-add-bank-account-reference-in-supplier-dashboard-hotfix 2020-03-31 16:04:21 +05:30
rohitwaghchaure
437689371d Merge pull request #21002 from Alchez/v12-stock-entry-fg-validation
fix: allow target warehouses to be changed for work order stock entries (v12)
2020-03-31 15:43:28 +05:30
Saqib
798d532a11 fix: warehouse_account_map not getting reset for diff company transac… (#20997)
* fix: warehouse_account_map not getting reset for diff company transaction

* fix: potential key errors while fetching warehouse_account_map

* fix: travis

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-03-31 15:30:53 +05:30
rohitwaghchaure
c5a3bcca70 fix: make for quantity as non mandatory in job card (#21080) 2020-03-31 15:28:15 +05:30
rohitwaghchaure
e4cb523181 fix: serial no scan not adding the serial nos in stock entry (#21082) 2020-03-31 15:26:24 +05:30
Rucha Mahabal
b80213d65d fix: Healthcare Domain Issues (#21112)
* fix (Lab Test): None TypeError in Patient Medical Record

* fix: Lab Test Template data import failing in Lab Test Item creation

* fix: disabled Procedure Template shown as enabled in List View

* fix: change item_code from Link to Data to avoid item not found error

* fix: disabled patient shown as enabled

* fix: disabled practitioner schedule shown as enabled in list view

* fix: appointment reminders not working

* fix: Batch not getting fetched in Clinical Procedure Item
2020-03-31 10:52:51 +05:30
Anupam Kumar
d13e7d00b0 fix: email_to, party_type and party are not set in payment request (#21085)
* fix: email_to, party_type and party are not set in payment request when order made from portal

* fix: email_to, party_type and party are not set in payment request when order made from portal

Co-authored-by: Anupam K <anupam@Anupams-MacBook-Air.local>
2020-03-31 10:50:53 +05:30
Saqib
f492ba1e2d fix: auto created asset message (#21109)
* fix: auto created asset message

* Update erpnext/controllers/buying_controller.py

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-03-31 10:48:57 +05:30
Deepesh Garg
83111a4a2a fix: Expense account currency validation in Landed Cost voucher (#21074)
* fix: Expense account currency validation in Landed Cost voucher

* fix: Remove unused imports
2020-03-31 10:45:37 +05:30
Deepesh Garg
5a482a6685 Merge pull request #21068 from Alchez/v12-add-issue-status-filter
feat: add status filter in issue web form (v12)
2020-03-30 12:24:34 +05:30
Deepesh Garg
bda11e628b Merge pull request #21118 from rohitwaghchaure/fixed_parent_account_error_while_adding_equity_account
fix: not able to add equity account in the chart of accounts
2020-03-30 12:14:32 +05:30
Deepesh Garg
b8be73caea Merge pull request #21111 from nextchamp-saqib/cancel-error-hyperlink-v12
chore: hyperlinks in cannot cancel message
2020-03-29 21:54:09 +05:30
Deepesh Garg
df151ad6fa Merge pull request #21116 from nextchamp-saqib/course-report-perm-fix-v12
fix: cannot view report for course doctype - permission problem
2020-03-29 21:52:02 +05:30
Rohit Waghchaure
7e7787c298 fix: not able to add equity account in the chart of accounts 2020-03-29 16:33:30 +05:30
Saqib Ansari
1dd0c8f54c fix: customer group price list not fetched in pos 2020-03-29 12:58:30 +05:30
Saqib Ansari
dc04cbf9a9 fix: cannot view report for course doctype - permission problem 2020-03-29 12:52:43 +05:30
Deepesh Garg
77673f33db Merge pull request #21090 from scmmishra/print-format-fix-12
fix: total currency formatting in Acc Receivable and Payable
2020-03-28 21:28:20 +05:30
Deepesh Garg
f4e7de3cef Merge pull request #21104 from nextchamp-saqib/stock-ledger-warehouse-filter-v12
chore: [ux] filter warehouse based on company
2020-03-28 21:10:01 +05:30
Deepesh Garg
9b4ef1059c Merge pull request #21107 from nextchamp-saqib/gl-entry-currency-fix-v12
fix: currency formatting in gl entry dr cr field
2020-03-28 21:09:29 +05:30
Saqib Ansari
8ba661b8ed chore: hyperlinks in cannot cancel message 2020-03-28 20:11:59 +05:30
Saqib Ansari
1125ed8830 fix: currency formatting in gl entry dr cr field 2020-03-28 19:32:27 +05:30
Saqib Ansari
58351de797 fix: payment request status fixes 2020-03-28 19:30:03 +05:30
Saqib Ansari
9578ead7f9 fix: no server side validations for accounts in asset category 2020-03-28 19:27:37 +05:30
Saqib Ansari
f9ce7c2e18 chore: [ux] filter warehouse based on company 2020-03-28 19:16:00 +05:30
rohitwaghchaure
221189ab6e Merge pull request #21095 from anupamvs/item-wise-sales-history-billed-amount-bug-hotfix
bug: Item-wise Sales History - Billed amount
2020-03-28 15:00:46 +05:30
Rohit Waghchaure
8f854c7d93 fix: item code showing as mandatory even if the 'Item Naming By' is set as Naming Series in stock settings 2020-03-28 14:56:45 +05:30
Anupam K
728edacfd4 bug: Item-wise Sales History - Billed amount 2020-03-28 13:48:42 +05:30
Deepesh Garg
9aa8cabf93 Merge pull request #21088 from nabinhait/item_defaults_fix
fix: Add item defaults based on global settings only if default company and warehouse is mentioned
2020-03-27 21:50:19 +05:30
Shivam Mishra
cf2ab9b129 fix: use setup from Supplier Quotation Controller 2020-03-27 21:21:38 +05:30
Shivam Mishra
2313d2d80a fix: total currency formatting 2020-03-27 19:37:26 +05:30
Nabin Hait
b03d1327a5 fix: Add item defaults based on global settings only if default company and warehouse is mentioned 2020-03-27 19:24:17 +05:30
rohitwaghchaure
8110edbc62 Merge pull request #21076 from rohitwaghchaure/default_item_is_not_coming_while_making_new_bom
fix: item not showing in popup while making batch
2020-03-27 13:49:07 +05:30
Rohit Waghchaure
d90f658163 fix: item not showing in popup while making batch 2020-03-27 12:54:01 +05:30
Poranut Chollavorn
0b8191d566 fix(pos): fix pos display item instock 2020-03-26 13:46:36 +00:00
Rohan Bansal
27f0d13bdd feat: add status filter in issue web form 2020-03-26 16:06:07 +05:30
marination
c944676bb5 fix: Updated Bin Requested Qty logic 2020-03-26 15:44:52 +05:30
Nabin Hait
ff49a2f0b6 fix: Ignored user permission for parent_company and existing_company field in Company (#21010) 2020-03-26 13:36:21 +05:30
Deepesh Garg
8a5587749b fix: Added hidden GL column in general ledger (#21022)
Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-03-26 13:29:36 +05:30
Marica
3c378aaac7 fix: Make shelf life mandatory in Batched Item if it has expiry date (#21045) 2020-03-26 13:28:42 +05:30
Raffael Meyer
72430e91b2 fix: account groups (#21046) 2020-03-26 13:25:14 +05:30
Marica
6b038bda0d fix: Project field in Stock Entry Detail should be editable (#21000) 2020-03-26 13:23:54 +05:30
Saqib
01f80b239d fix: wrong calculation of depreciation eliminated for a period (#21032) 2020-03-26 13:23:06 +05:30
Saqib
b9f81d913b fix: currency not fetched on quotation creation (#20998) 2020-03-26 13:20:27 +05:30
Saqib
750985dc23 feat: auto set batch no on serial no selection (#20758)
* feat: auto set batch no on serial no selection

* fix: dialog not shown if set warehouse selected

* fix: typo

* fix: merge conflict

* fix: callback no getting called after serial no selected

* fix: callback no getting called after serial no selected

* fix: available batch qty not fetched without set_warehouse selected

* fix: item batch not synced with dialog batch table
2020-03-26 13:18:36 +05:30
P-Froggy
bcb9c28597 Fix: Add missing bank accounts reference in supplier dashboard 2020-03-26 02:35:09 +01:00
Deepesh Garg
23891a918d Merge pull request #21058 from deepeshgarg007/gstr_1_pos_validation_v12
fix: Place of supply validation in GSTR-1 report
2020-03-25 17:02:19 +05:30
Deepesh Garg
4f1a3876ec fix: Remove debug 2020-03-25 16:56:09 +05:30
Deepesh Garg
43806f3e68 fix: Place of supply validation in GSTR-1 report 2020-03-25 16:55:59 +05:30
Deepesh Garg
f4405246ed Merge pull request #21012 from Thunderbottom/log_error-fix-v12
chore: pass traceback and error message as kwargs
2020-03-24 20:19:42 +05:30
rohitwaghchaure
8cc507af53 Merge pull request #21039 from rohitwaghchaure/manufacturing_ux_fixes_hotfix
fix: Manufacturing UX, added calendar view for job card
2020-03-24 16:33:34 +05:30
rohitwaghchaure
6c8bbf9a1c Merge pull request #21029 from rohitwaghchaure/import_supplier_invoices
feat: import supplier invoices
2020-03-24 16:33:00 +05:30
rohitwaghchaure
ea470609fd Merge pull request #21036 from rohitwaghchaure/fixed_batch_selector_popup
fix: batch selection popup not coming for stock entry
2020-03-24 16:31:56 +05:30
Deepesh Garg
ad44f52a26 Merge pull request #21042 from deepeshgarg007/default_dimension_clean_up_v12
fix: Default dimension set code cleanup
2020-03-24 15:48:01 +05:30
Deepesh Garg
1e7cba9dd3 fix: Default dimension set code cleanup 2020-03-24 15:45:22 +05:30
Rohit Waghchaure
87b3dbb214 fix: Manufacturing UX, added calendar view for job card 2020-03-24 14:06:56 +05:30
Rohit Waghchaure
243283eec8 fix: batch selection popup not coming for stock entry 2020-03-24 11:34:44 +05:30
Rohit Waghchaure
b05922026d feat: import supplier invoices 2020-03-23 16:31:39 +05:30
Rohan
ed8e44b467 Merge branch 'version-12-hotfix' into v12-stock-entry-fg-validation 2020-03-23 14:49:59 +05:30
Deepesh Garg
76e163a889 Merge pull request #21026 from deepeshgarg007/dimension_perm_v12
fix: Remove quick entry from accounting dimensions
2020-03-22 21:56:47 +05:30
Deepesh Garg
20ab2c1e20 fix: Remove quick entry from accouting dimensions 2020-03-22 21:53:50 +05:30
Deepesh Garg
3f7ea072c3 Merge pull request #21004 from rohitwaghchaure/qty_must_be_positive_for_stock_entry
fix: stock entry qty must be positive
2020-03-21 20:45:50 +05:30
Deepesh Garg
601db77801 Merge pull request #21014 from surajshetty3416/fix-opportunity-listview-version-12-hotfix
fix: Add currency in options of Opportunity Amount
2020-03-20 20:22:46 +05:30
marination
93e72fdd21 fix: UOM fixes in Sales Order,Material Request & Production Plan 2020-03-20 18:06:25 +05:30
Suraj Shetty
e2bb2593db fix: Add currency in options of Opportunity Amount - To show currect symbol in list view 2020-03-20 15:13:07 +05:30
Chinmay D. Pai
e53e96f01b chore: pass traceback and error message as kwargs
fixes issue where the system tries to generate an error log but fails
with the following error:

Traceback (most recent call last):
  File "/home/frappe/benches/bench-version-12-2020-03-19/apps/erpnext/erpnext/setup/doctype/company/company.py", line 421, in install_country_fixtures
    frappe.get_attr(module_name)(company_doc, False)
  File "/home/frappe/benches/bench-version-12-2020-03-19/apps/erpnext/erpnext/regional/india/setup.py", line 17, in setup
    make_fixtures(company)
  File "/home/frappe/benches/bench-version-12-2020-03-19/apps/erpnext/erpnext/regional/india/setup.py", line 523, in make_fixtures
    set_tax_withholding_category(company)
  File "/home/frappe/benches/bench-version-12-2020-03-19/apps/erpnext/erpnext/regional/india/setup.py", line 543, in set_tax_withholding_category
    fiscal_year = get_fiscal_year(today(), company=company)[0]
  File "/home/frappe/benches/bench-version-12-2020-03-19/apps/erpnext/erpnext/accounts/utils.py", line 24, in get_fiscal_year
    return get_fiscal_years(date, fiscal_year, label, verbose, company, as_dict=as_dict)[0]
  File "/home/frappe/benches/bench-version-12-2020-03-19/apps/erpnext/erpnext/accounts/utils.py", line 80, in get_fiscal_years
    raise FiscalYearError(error_msg)
erpnext.accounts.utils.FiscalYearError: Date 20-03-2020 not in any active Fiscal Year.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/app.py", line 62, in application
    response = frappe.api.handle()
  File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/api.py", line 55, in handle
    return frappe.handler.handle()
  File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/handler.py", line 22, in handle
    data = execute_cmd(cmd)
  File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/handler.py", line 61, in execute_cmd
    return frappe.call(method, **frappe.form_dict)
  File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/__init__.py", line 1054, in call
    return fn(*args, **newargs)
  File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/desk/form/save.py", line 22, in savedocs
    doc.save()
  File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/model/document.py", line 273, in save
    return self._save(*args, **kwargs)
  File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/model/document.py", line 296, in _save
    self.insert()
  File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/model/document.py", line 260, in insert
    self.run_post_save_methods()
  File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/model/document.py", line 926, in run_post_save_methods
    self.run_method("on_update")
  File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/model/document.py", line 794, in run_method
    out = Document.hook(fn)(self, *args, **kwargs)
  File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/model/document.py", line 1065, in composer
    return composed(self, method, *args, **kwargs)
  File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/model/document.py", line 1048, in runner
    add_to_return_value(self, fn(self, *args, **kwargs))
  File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/model/document.py", line 788, in <lambda>
    fn = lambda self, *args, **kwargs: getattr(self, method)(*args, **kwargs)
  File "/home/frappe/benches/bench-version-12-2020-03-19/apps/erpnext/erpnext/setup/doctype/company/company.py", line 107, in on_update
    install_country_fixtures(self.name)
  File "/home/frappe/benches/bench-version-12-2020-03-19/apps/erpnext/erpnext/setup/doctype/company/company.py", line 423, in install_country_fixtures
    frappe.log_error(str(e), frappe.get_traceback())
  File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/__init__.py", line 1524, in log_error
    method=title)).insert(ignore_permissions=True)
  File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/model/document.py", line 231, in insert
    self._validate()
  File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/model/document.py", line 463, in _validate
    self._validate_length()
  File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/model/base_document.py", line 583, in _validate_length
    self.throw_length_exceeded_error(df, max_length, value)
  File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/model/base_document.py", line 599, in throw_length_exceeded_error
    .format(reference, _(df.label), max_length, value), frappe.CharacterLengthExceededError, title=_('Value too big'))
  File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/__init__.py", line 377, in throw
    msgprint(msg, raise_exception=exc, title=title, indicator='red', is_minimizable=is_minimizable)
  File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/__init__.py", line 356, in msgprint
    _raise_exception()
  File "/home/frappe/benches/bench-version-12-2020-03-19/apps/frappe/frappe/__init__.py", line 316, in _raise_exception
    raise raise_exception(msg)
frappe.exceptions.CharacterLengthExceededError: Error Log f2cbb8c0f2: 'Title' (Traceback (most recent call last):
  File "/home/frappe/benches/bench-version-12-2020-03-19/apps/erpnext/erpnext/setup/doctype/company/company.py", line 421, in install_country_fixtures
    frappe.get_attr(module_name)(company_doc, False)
  File "/home/frappe/benches/bench-version-12-2020-03-19/apps/erpnext/erpnext/regional/india/setup.py", line 17, in setup
    make_fixtures(company)
  File "/home/frappe/benches/bench-version-12-2020-03-19/apps/erpnext/erpnext/regional/india/setup.py", line 523, in make_fixtures
    set_tax_withholding_category(company)
  File "/home/frappe/benches/bench-version-12-2020-03-19/apps/erpnext/erpnext/regional/india/setup.py", line 543, in set_tax_withholding_category
    fiscal_year = get_fiscal_year(today(), company=company)[0]
  File "/home/frappe/benches/bench-version-12-2020-03-19/apps/erpnext/erpnext/accounts/utils.py", line 24, in get_fiscal_year
    return get_fiscal_years(date, fiscal_year, label, verbose, company, as_dict=as_dict)[0]
  File "/home/frappe/benches/bench-version-12-2020-03-19/apps/erpnext/erpnext/accounts/utils.py", line 80, in get_fiscal_years
    raise FiscalYearError(error_msg)
erpnext.accounts.utils.FiscalYearError: Date 20-03-2020 not in any active Fiscal Year.
) will get truncated, as max characters allowed is 140

Signed-off-by: Chinmay D. Pai <chinmaydpai@gmail.com>
2020-03-20 13:57:30 +05:30
Rohit Waghchaure
794064db9f fix: stock entry qty must be positive 2020-03-19 14:44:16 +05:30
Rohan
4c4a1aa56d fix: allow target warehouses to be changed for work order stock entries 2020-03-19 13:19:08 +05:30
Sahil Khan
84a5e34319 Merge branch 'v12-pre-release' into version-12 2020-03-19 12:44:10 +05:30
Sahil Khan
d1bbda6d71 bumped to version 12.6.0 2020-03-19 13:04:10 +05:50
Deepesh Garg
8c56f8595e fix: Unable to submit landed cost voucher (#20978) 2020-03-19 11:03:14 +05:30
Deepesh Garg
c2f6782658 fix: Unable to submit landed cost voucher (#20979) 2020-03-19 11:02:34 +05:30
Deepesh Garg
7c0398c8e3 fix: Update is_tree field in Tree doctypes (#20983) 2020-03-19 11:01:40 +05:30
Deepesh Garg
71e9f23d77 fix: Update is_tree field in Tree doctypes (#20982) 2020-03-19 11:00:55 +05:30
Nabin Hait
7b45ae1f2d fix: Multiple fixes during pre-release testing (#20985) 2020-03-19 10:58:41 +05:30
Nabin Hait
bf0f9c5cb4 fix: Multiple fixes during pre-release testing (#20986) 2020-03-19 10:58:07 +05:30
Deepesh Garg
3f1682a4f3 Merge pull request #20993 from rohitwaghchaure/fixed_bom_comparision_issue_pre_release
fix: bom comparison issue
2020-03-19 10:54:51 +05:30
Deepesh Garg
4dd8f390e2 Merge pull request #20992 from rohitwaghchaure/fixed_bom_comparision_issue_hotfix
fix: bom comparison issue
2020-03-19 10:54:29 +05:30
Rohit Waghchaure
82661aa042 fix: bom comparison issue 2020-03-19 10:06:55 +05:30
Rohit Waghchaure
210a718a04 fix: bom comparison issue 2020-03-19 10:03:06 +05:30
Deepesh Garg
1d94f6c848 Merge pull request #20989 from rohitwaghchaure/fixed_shortage_qty_in_stock_projected_qty_report_pre_release
fix: shortage qty in stock projected qty report
2020-03-19 09:32:50 +05:30
Rohit Waghchaure
d21a9fa917 fix: shortage qty in stock projected qty report 2020-03-19 09:25:15 +05:30
Deepesh Garg
51cbfa6aa6 Merge pull request #20972 from rohitwaghchaure/fixed_shortage_qty_in_stock_projected_qty_report
fix: shortage qty in stock projected qty report
2020-03-19 09:04:30 +05:30
Rohit Waghchaure
1ed5993ff6 fix: shortage qty in stock projected qty report 2020-03-18 13:33:55 +05:30
Marica
e655648b18 fix: Missing if condition in Customer Default Bank Account Validation. (#20971) 2020-03-18 11:35:43 +05:30
Marica
da7c6b0d99 fix: Missing if condition in Customer Default Bank Account Validation. (#20970) 2020-03-18 11:29:02 +05:30
Nabin Hait
a5b836d3d4 core: Added change log 2020-03-17 21:02:22 +05:30
Nabin Hait
8a7ecebc03 fix: merge conflict 2020-03-17 19:49:25 +05:30
Anurag Mishra
7cf245895e refactor: Report BOM Sock Calculated (#19431) 2020-03-17 19:45:33 +05:30
Priyanka Gangar
738517c6e8 Label changes in account settings hotfix (#20964)
* fix: Account setting words switches to US Terminology

* created united_states.js

* Remove Unwanted Spaces

* Remove all changes

* Add spaces between arguments

Co-authored-by: PriyankaGangar <pinka0925@gmail.com>
2020-03-17 19:39:06 +05:30
rohitwaghchaure
a6cd666b10 fix: reserved qty for production issue for partial completion of work order (#20900) 2020-03-17 17:00:41 +05:30
Saqib
bc8a881e64 fix: purchase return are allowed even when assets are not cancelled (#20798)
* fix: purchase return are allowed even when assets are not cancelled

* chore: test case

* fix: error message

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-03-17 17:00:24 +05:30
Deepesh Garg
224059e7ba fix: Party Name not visible in statement of accounts (#20914)
* fix: Party name in Statement Of Accounts

* Update general_ledger.html

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-03-17 16:59:09 +05:30
Sun Howwrongbum
6f454dce20 fix: null fields being set in Integration Request (#20894)
Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-03-17 12:13:01 +05:30
Deepesh Garg
3528149329 fix: Add check to skip PR in supplier master 2020-03-17 11:48:11 +05:30
Deepesh Garg
6c148ef314 fix: Add check to skip SO and DN in customer master 2020-03-17 11:47:23 +05:30
Anurag Mishra
7f58f6af39 fix: leave based on multiple holiday list (#20849)
Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-03-17 10:58:17 +05:30
Marica
3ddcc2e5c7 fix: Item alternative must have similar fields as orignal item (#20799)
* fix: Item alternative must have similar fields as orignal item

* fix: Using db.get_values and meta instead of get_value and get_doc

* fix: Made code more DRY

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-03-17 10:56:13 +05:30
Deepesh Garg
756f47af04 feat: Nested set filtering for accounting dimension (#20866)
* feat: Nested set filtering for accounting dimension

* fix: Remove print statement
2020-03-17 10:52:56 +05:30
Anurag Mishra
b9e1d9ad7d Deduction based on earnins/gross pay (#20935)
* fix: deductions calculation based on gross pay

* test: salary structure deduction based on gross pay

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-03-16 22:44:17 +05:30
Deepesh Garg
169ba6f9b2 Merge pull request #20959 from deepeshgarg007/mode_of_payment_fix_v12
fix: Mode of payment not fetcched in Item wise purchase register
2020-03-16 22:43:24 +05:30
nmimsnikhil
7c0e89d785 fix: added a Reason for Rejection field for each items (#20828)
* fix: added a Reason for Rejection field for each items

* Update delivery_note_item.json

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-03-16 22:41:23 +05:30
Deepesh Garg
dd8059a3aa fix: Mode of payment not fetcched in Item wise purchase register 2020-03-16 22:39:32 +05:30
Don-Leopardo
0dcd5a0f34 fix: sql injection (#20817) 2020-03-16 22:35:01 +05:30
rohitwaghchaure
d2a2837034 fix: bom replace tool issue (#20841)
Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-03-16 21:42:51 +05:30
Anurag Mishra
22a069d4b0 fix: Wrong status 2020-03-16 19:15:33 +05:30
Rohan
281a9fdf07 fix: handle error handling (#20814) 2020-03-16 17:57:15 +05:30
Saqib
e7a6a4b009 fix: is_pos gets reset on making s_inv from s_ord (#20887)
* s_inv has is_pos default set as 1
2020-03-16 17:48:21 +05:30
Nabin Hait
5c760175d1 fix: deletion of auto-created batch (#20953) 2020-03-16 17:26:02 +05:30
Deepesh Garg
11fb93d022 fix: Add bank transaction in module view (#20823) 2020-03-16 16:54:34 +05:30
Saqib
43a0011987 fix: serial_no_selector showing for non serialized batched item (#20852) 2020-03-16 16:28:54 +05:30
Vishal Dhayagude
37886a72ef fix(HR): Leave application company field made read-only and filter in Monthly attendance sheet (#20911)
* fix(HR): Leave application company field made Readonly

* fix: in monthly report attandance employee filter based on company

* fix: minor typo
2020-03-16 15:57:42 +05:30
Deepesh Garg
98573fb868 Merge pull request #20921 from deepeshgarg007/c_from_fix_v12
fix: Get invoice details only if invoice is selected
2020-03-16 15:56:19 +05:30
Deepesh Garg
4d954f1a5f Merge pull request #20836 from Thunderbottom/welcome-email-fix-v12
fix: use ERPNext in welcome email when default company is not set
2020-03-16 15:54:09 +05:30
Prssanna Desai
46053f6c8f fix: fix options of currency fieldtype in Expense Taxes and Charges (#20910) 2020-03-16 15:52:00 +05:30
Chinmay Pai
6f3941c9cc fix: remove make_purchase_invoice from demo script (#20906)
currently demo fails with the following error:

frappe@bf54a9834cfe:/workspace/development/frappe-bench$ bench --site test1.localhost execute erpnext.demo.demo.make
Traceback (most recent call last):
  File "/usr/lib/python3.7/runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "/usr/lib/python3.7/runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "/workspace/development/frappe-bench/apps/frappe/frappe/utils/bench_helper.py", line 97, in <module>
    main()
  File "/workspace/development/frappe-bench/apps/frappe/frappe/utils/bench_helper.py", line 18, in main
    click.Group(commands=commands)(prog_name='bench')
  File "/workspace/development/frappe-bench/env/lib/python3.7/site-packages/click/core.py", line 764, in __call__
    return self.main(*args, **kwargs)
  File "/workspace/development/frappe-bench/env/lib/python3.7/site-packages/click/core.py", line 717, in main
    rv = self.invoke(ctx)
  File "/workspace/development/frappe-bench/env/lib/python3.7/site-packages/click/core.py", line 1137, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/workspace/development/frappe-bench/env/lib/python3.7/site-packages/click/core.py", line 1137, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/workspace/development/frappe-bench/env/lib/python3.7/site-packages/click/core.py", line 956, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/workspace/development/frappe-bench/env/lib/python3.7/site-packages/click/core.py", line 555, in invoke
    return callback(*args, **kwargs)
  File "/workspace/development/frappe-bench/env/lib/python3.7/site-packages/click/decorators.py", line 17, in new_func
    return f(get_current_context(), *args, **kwargs)
  File "/workspace/development/frappe-bench/apps/frappe/frappe/commands/__init__.py", line 25, in _func
    ret = f(frappe._dict(ctx.obj), *args, **kwargs)
  File "/workspace/development/frappe-bench/apps/frappe/frappe/commands/utils.py", line 145, in execute
    ret = frappe.get_attr(method)(*args, **kwargs)
  File "/workspace/development/frappe-bench/apps/frappe/frappe/__init__.py", line 1042, in get_attr
    return getattr(get_module(modulename), methodname)
  File "/workspace/development/frappe-bench/apps/frappe/frappe/__init__.py", line 823, in get_module
    return importlib.import_module(modulename)
  File "/workspace/development/frappe-bench/env/lib/python3.7/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1006, in _gcd_import
  File "<frozen importlib._bootstrap>", line 983, in _find_and_load
  File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 677, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 728, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/workspace/development/frappe-bench/apps/erpnext/erpnext/demo/demo.py", line 6, in <module>
    from erpnext.demo.user import hr, sales, purchase, manufacturing, stock, accounts, projects, fixed_asset
  File "/workspace/development/frappe-bench/apps/erpnext/erpnext/demo/user/fixed_asset.py", line 9, in <module>
    from erpnext.assets.doctype.asset.asset import make_purchase_invoice, make_sales_invoice
ImportError: cannot import name 'make_purchase_invoice' from 'erpnext.assets.doctype.asset.asset' (/workspace/development/frappe-bench/apps/erpnext/erpnext/assets/doctype/asset/asset.py)

Signed-off-by: Chinmay D. Pai <chinmaydpai@gmail.com>
2020-03-16 15:49:50 +05:30
Raffael Meyer
cd71114c15 fix(accounts): add account_type and tax_rate to some VAT accounts (#20941)
* Add account_type and tax rate to some VAT accounts

* fix indentation
2020-03-16 15:44:32 +05:30
Nabin Hait
e2a31f221a fix: Multiple fixes for travis (#20950) 2020-03-16 15:36:10 +05:30
Anurag Mishra
97dfe2ac32 fix: local variable 'benefit_amount' referenced before assignment (#20948) 2020-03-16 13:47:47 +05:30
Myuddin khatri
9c63b1d2c9 fix(shopping-cart): setting billing and shipping address
setting billing and shipping address
2020-03-12 16:15:05 +05:30
Myuddin khatri
0f807bf7ee fix(shopping-cart): address is made mandatory to place order 2020-03-12 14:40:34 +05:30
Deepesh Garg
ee003495f6 fix: Get invoice details only if invoice is selected 2020-03-12 13:25:06 +05:30
Deepesh Garg
08f0e9d6f3 Merge pull request #20891 from deepeshgarg007/itemised_purchase_register_v12
fix: HSN code not visible in GST Itemised Purchase register
2020-03-10 10:05:21 +05:30
Deepesh Garg
4d6fb59bfb Merge pull request #20885 from deepeshgarg007/first_row_dimension_v12
fix: Acccounting Dimension disappearing in first row after refresh
2020-03-09 22:47:46 +05:30
Deepesh Garg
0317576434 fix: HSN code not visible in GST Itemised Purchase register 2020-03-09 22:42:21 +05:30
Saqib
b607963fba fix: [pos] customer group filter resets on syncing offline invoices (#20874) 2020-03-09 18:30:42 +05:30
Marica
d08cff9efc fix: Validate Serial No/Batch No against unserialized Item in Stock Reconciliation (#20859) 2020-03-09 18:13:38 +05:30
Marica
2f2cef6c5f fix: ModuleNotFoundError on Turkey Company setup (#20854) 2020-03-09 18:07:55 +05:30
Deepesh Garg
875e0f5fb2 fix: Acccounting Dimension disappearing in first row after refresh 2020-03-09 18:05:35 +05:30
Deepesh Garg
d9ab412032 feat: Allow PI creation without PO 2020-03-06 19:04:41 +05:30
Saqib
6c4cddccf6 fix: show uom in print formats instead of stock uom (#20844)
Co-authored-by: Saqib Ansari <saqibansari@Saqibs-MacBook-Pro.local>
2020-03-06 11:06:41 +05:30
Marica
69d3c4ff25 fix: update_child_qty_rate() missing argument (#20839) 2020-03-06 10:57:43 +05:30
Nabin Hait
1f80c8dffe perf: improve gl entry submission (#20802)
* perf: improve gl entry submission

* perf: add indexes

* fix: replace **kwargs with *args

* fix: syntax error

* fix: travis

* chore: remove purchase invoice from status updater

* fix: set_staus args

Co-Authored-By: Nabin Hait <nabinhait@gmail.com>

* fix: only update status for invoices & fees

* [bug][fix]: set status to object instead of variable (#20790)

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
Co-authored-by: Saurabh <saurabh6790@gmail.com>
2020-03-05 13:04:53 +05:30
Chinmay D. Pai
12cdf0fb52 fix: use ERPNext in welcome email when default company is not set
Signed-off-by: Chinmay D. Pai <chinmaydpai@gmail.com>
2020-03-05 10:11:55 +05:30
Deepesh Garg
b6a3a06c4b Merge pull request #20821 from deepeshgarg007/gst_itemised_v12
fix: HSN code no visible in GST itemised sales register
2020-03-04 10:41:55 +05:30
Deepesh Garg
2728590471 fix: HSN code no visible in GST itemised sales register 2020-03-04 10:26:25 +05:30
Priyanka Gangar
7766530afc fix: fetch sales person name (#20801)
* fix: fetch sales person name

* Update sales_person.js

Co-authored-by: pinka0925 <44537026+pinka0925@users.noreply.github.com>
Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-03-02 19:24:14 +05:30
Deepesh Garg
d7839eb8ac fix: Total amount not displayed in Journal Entry (#20795)
* fix: Total amount not displayed in Journal Entry

* fix: Update paid_to_received field

* fix: set total amount

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-03-02 18:09:24 +05:30
Prssanna Desai
6a734ecd34 fix: use get all instead of get list to fetch item attributes (#20793) 2020-03-02 18:06:21 +05:30
Mangesh-Khairnar
acd93b3bd1 fix: use system language to translate strings (#20792) 2020-03-02 18:05:59 +05:30
Marica
94200b1179 chore: Item Price and Product Bundle Form cleanup (#20773)
* chore: Item Price and Product Bundle Form cleanup

* fix: Trailing comma
2020-03-02 15:51:22 +05:30
Saqib
a6bf96322b chore: control reposting of future gl entries with flags (#20775) 2020-03-02 15:19:28 +05:30
Saqib
27163e60bb feat: link serial no to batch no (#20779)
* feat: link serial no to batch no

* fix: test cases
2020-03-02 15:03:18 +05:30
Saqib
1041516e8e fix: reconciled entries showing in bank reco (#20788) 2020-03-02 15:00:42 +05:30
Deepesh Garg
b7bbd827fd Merge pull request #20769 from marination/buying-dashboard-hotfix
chore: Rearranged Buying Module Dashboard
2020-02-29 17:34:35 +05:30
Sahil Khan
fda7cbeca4 Merge branch 'v12-pre-release' into version-12 2020-02-28 15:06:03 +05:30
Sahil Khan
2fc58b3327 bumped to version 12.5.2 2020-02-28 15:26:03 +05:50
marination
3fcd575f53 chore: Rearranged Buying Module Dashboard 2020-02-28 13:08:38 +05:30
Deepesh Garg
0cee807dfb Merge pull request #20766 from deepeshgarg007/bank_reco_release
fix: Journal Entry not being fetched in Bank Reconciliation
2020-02-28 12:35:06 +05:30
Deepesh Garg
a51c56c4b3 fix: Remove debug statement 2020-02-28 12:32:18 +05:30
Deepesh Garg
d2f26e57d2 fix: Journal Entry not being fetched in Bank Reconciliation 2020-02-28 12:32:05 +05:30
Deepesh Garg
b7790c0394 fix: Item Wise report query fix (#20765) 2020-02-28 12:30:29 +05:30
Deepesh Garg
d17e0b5aca fix: Item Wise report query fix (#20761) 2020-02-28 12:29:11 +05:30
Shivam Mishra
f08b5e4866 feat: ignore permission when deleting linked emails (#20753) 2020-02-27 19:07:39 +05:30
Deepesh Garg
78ae5e7721 Merge pull request #20749 from deepeshgarg007/bank_reco_jv_v12
fix: Journal Entry not being fetched in Bank Reconciliation
2020-02-27 16:19:01 +05:30
Deepesh Garg
5fb66a3953 fix: Remove debug statement 2020-02-27 16:15:43 +05:30
Deepesh Garg
d952e71d97 fix: Journal Entry not being fetched in Bank Reconciliation 2020-02-27 16:15:16 +05:30
rohitwaghchaure
fe337dfb66 fix: serial no material transfer performance issue (#20722) 2020-02-27 14:27:43 +05:30
Saqib
45329232b3 fix: only update items if rate or qty changed (#20743) 2020-02-27 12:51:23 +05:30
Rohan
30ad21b7d8 fix: sort Issues chronologically (#20740) 2020-02-26 18:53:48 +05:30
Marica
4e3ad9c3f2 fix: Lock stock ledger entries that are being reposted. (#20739)
- If stock ledger entries are being reposted, don't let any other transaction apply itself on the same.
2020-02-26 18:51:35 +05:30
Sahil Khan
2a1e9b542e Merge branch 'v12-pre-release' into version-12 2020-02-26 18:20:16 +05:30
Sahil Khan
8f458feafc bumped to version 12.5.1 2020-02-26 18:40:16 +05:50
Deepesh Garg
e116d8f819 Merge pull request #20738 from deepeshgarg007/bank_reco_fix_v12_pre
fix: Mandatory bank account error fix
2020-02-26 17:42:14 +05:30
Deepesh Garg
f3674ccc6c fix: SQL condition 2020-02-26 17:40:13 +05:30
Deepesh Garg
6f35a63f2d fix: Mandatory bank account error fix 2020-02-26 17:40:04 +05:30
Deepesh Garg
acbc4f648d fix: Mandatory bank account error fix (#20734)
* fix: Mandatory bank account error fix

* fix: SQL condition
2020-02-26 17:17:06 +05:30
Anurag Mishra
5475b5f562 fix: validated leave allocation (#20635)
* fix: validated leave allocation

* fix: changes requested

* Update erpnext/hr/doctype/leave_encashment/leave_encashment.py

Co-Authored-By: Nabin Hait <nabinhait@gmail.com>

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-02-26 14:50:52 +05:30
rohitwaghchaure
39861c270c fix: same free item not working for pricing rule (#20713) 2020-02-26 12:51:53 +05:30
RJPvT
6eaa542709 fix: smaller then instead of bigger then :-( dumb mistake v12 #20693 (#20701) 2020-02-26 11:59:06 +05:30
Saqib
2a8981929a perf: search for customer's return invoices then filter out gl_entries (#20710) 2020-02-26 11:36:38 +05:30
rohitwaghchaure
0aa7aa5996 fix: rate and amount in material request copying from sales order (#20718) 2020-02-26 11:27:49 +05:30
rohitwaghchaure
a20bd89c20 fix: reorder material request not created if doctype has custom mandatory field (#20720) 2020-02-26 11:25:30 +05:30
Deepesh Garg
aa7af30382 Merge pull request #20715 from rohitwaghchaure/account_dashboard_not_working_hotfix
fix: account dashboard not working
2020-02-26 10:18:27 +05:30
Rohit Waghchaure
9921d28ea7 fix: account dashboard not working 2020-02-25 12:17:25 +05:30
Sahil Khan
b26fd04843 Merge branch 'v12-pre-release' into version-12 2020-02-20 18:26:58 +05:30
Sahil Khan
1b78d20d3c bumped to version 12.5.0 2020-02-20 18:46:57 +05:50
sahil28297
b203406d4c fix: proper release note version 2020-02-20 18:20:33 +05:30
Mangesh-Khairnar
5a0017c61a Merge pull request #20686 from Anurag810/additional_salary_fixes
fix: Additional salary can be created only for active employee
2020-02-20 16:21:55 +05:30
Anurag Mishra
df15c758f6 fix: Additional salary can be created only for active employee 2020-02-20 15:29:09 +05:30
Mangesh-Khairnar
588a89f957 fix: return null for attribute (#20684) 2020-02-20 13:41:00 +05:30
Mangesh-Khairnar
c74343531f fix: skip earned leaves check for max leaves set to zero or less (#20536)
* fix: skip earned leaves check for max leaves set to zero or less

* test: earned leaves creation
2020-02-20 13:25:45 +05:30
Deepesh Garg
a257189c77 Merge pull request #20451 from Anurag810/fleet-management_v12
fix: odometer value was not syncing properly
2020-02-20 13:20:21 +05:30
Rohan
355051bf75 fix: apply url encoding to project names (#20641) 2020-02-20 12:59:21 +05:30
Anurag Mishra
ab8aa43ffb fix: changed field type which was affecting filters (#20670) 2020-02-20 12:52:37 +05:30
Mathieu Brunot
a6f56bbc3e chore(ci-coverage): Pin coverage 4.5.4 #20646 (#20648)
* chore(ci-coverage): Pin coveralls 4.5.4 #20646

Signed-off-by: mathieu.brunot <mathieu.brunot@monogramm.io>

* chore: Pin coverage

Signed-off-by: mathieu.brunot <mathieu.brunot@monogramm.io>
2020-02-20 12:50:09 +05:30
gavin
4e2b9395b9 chore: drop cypress requirement (#20675) 2020-02-20 12:48:32 +05:30
Saqib
ae9159fbd8 fix: mandatory on hold comment for purchase invoice (#20667) 2020-02-20 12:33:07 +05:30
Saqib
8e80c17602 chore: SINV set_status remove redundant function calls (#20661) 2020-02-20 12:32:09 +05:30
Saqib
8903258362 fix: mandatory on hold comment for purchase invoice (#20668)
Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-02-20 12:30:35 +05:30
Saqib
1beeb28908 fix: check for available stock in product bundle's website warehouse (#20680) 2020-02-20 12:22:33 +05:30
Saqib
2d2aa7d664 fix: check for available stock in product bundle's website warehouse (#20679)
* fix: check for available stock in product bundle's website warehouse

* test: earned leaves creation

* fix: minor change

Co-authored-by: Mangesh-Khairnar <mkhairnar10@gmail.com>
2020-02-20 12:22:01 +05:30
Nabin Hait
cd6b60df70 core: Added change log 2020-02-20 12:18:21 +05:30
Nabin Hait
b18790f5c2 Merge branch 'version-12-hotfix' into v12-pre-release 2020-02-19 19:19:36 +05:30
Mangesh-Khairnar
f6d7090f07 fix: earned leaves creation for zero maximum leaves (#20677) 2020-02-19 19:16:40 +05:30
Deepesh Garg
017d280877 Merge pull request #20664 from marination/stock-settings-query-v12-hotfix
fix: Set Query on warehouse fields in Stock Settings
2020-02-19 18:58:46 +05:30
Anurag Mishra
031d4092d0 test: syncing of odometer value 2020-02-19 16:49:13 +05:30
marination
0119d15adb fix: Server side validation for Warehouses 2020-02-19 11:21:55 +05:30
Deepesh Garg
df6cb3c43b Merge pull request #20633 from scmmishra/tree-fix-qms
fix: filters for quality_procedure tree
2020-02-19 09:40:52 +05:30
marination
1ed737cf64 fix: Set Query on warehouse fields in Stock Settings 2020-02-18 21:45:52 +05:30
Deepesh Garg
c64e46fe67 Merge pull request #20483 from racitup/plaid-20343-fix
fix: Plaid fixes and enhancements
2020-02-18 18:03:16 +05:30
Nabin Hait
f579b0e3de fix: merge conflict 2020-02-18 17:49:20 +05:30
Deepesh Garg
b63c041aa4 feat: Group by AR/AP report (#20574)
* feat: Group by AR/AP report

* fix: Do not consider total row in charts

* fix: Subtotal row for last party
2020-02-18 16:09:37 +05:30
rohitwaghchaure
22f9a5f09f Merge pull request #20639 from nextchamp-saqib/pos-mode-of-pay-fix-v12
fix: disabled mode of payments fetches in sales invoices
2020-02-18 12:38:14 +05:30
rohitwaghchaure
e47875340e Merge pull request #20655 from marination/precision-mr-item-hotfix
fix: Stock Quantity not calculated on client side in Material Request…
2020-02-18 12:34:27 +05:30
marination
6f28383531 fix: Stock Quantity not calculated on client side in Material Request Items. 2020-02-18 11:08:16 +05:30
Deepesh Garg
95050702f1 Merge branch veersion-12-hotfix into plaid-20343-fix 2020-02-17 22:11:14 +05:30
Deepesh Garg
ae8f717fe5 Merge pull request #20640 from gavindsouza/updated-requirements
chore(requirements): Pin requirements
2020-02-17 22:05:26 +05:30
Gavin D'souza
f95ac99baa fix(travis): add lib cups for updated frappe requirements 2020-02-17 16:52:27 +05:30
Shivam Mishra
660b4d1e2f feat: show numeric values in item configurator (#20430) 2020-02-17 15:58:31 +05:30
thefalconx33
5bfebaf1c0 fix: disabled mode of payments fetches in sales invoices 2020-02-17 14:16:49 +05:30
Deepesh Garg
980e54c5f4 Merge pull request #20570 from Er-Naren719/hotfix-user-table-columns-updated
feat: columns under user table updated
2020-02-17 11:19:23 +05:30
Shivam Mishra
efbfeb2d89 fix: filters for quality_procedure tree 2020-02-17 10:50:05 +05:30
rohitwaghchaure
0676ed08b1 Merge pull request #20540 from rohitwaghchaure/fixed_purchase_receipt_time_out_error
fix: time out error while submitting the purchase receipt
2020-02-11 19:55:04 +05:30
rohitwaghchaure
e9946672e0 Merge branch 'version-12-hotfix' into fixed_purchase_receipt_time_out_error 2020-02-11 19:54:54 +05:30
rohitwaghchaure
6124f83e6e Merge pull request #20556 from ashish-greycube/patch-3
fix: backflush raw material based on - Material Transferred for Manufacture
2020-02-11 19:50:09 +05:30
Deepesh Garg
2b83debc72 Merge pull request #20564 from govindsmenokee/patch-4
fix: get_students not respecting the program
2020-02-11 17:03:24 +05:30
Er-Naren719
fc4cc9ab42 feat: column width increased for view attachments field (#314) 2020-02-11 10:17:34 +00:00
Deepesh Garg
cd19974d7a Merge pull request #20566 from vishdha/filter_serial_based_on_batch_v12
fix: filter serial no based on batch no
2020-02-11 13:44:54 +05:30
vishal
cde48a44cf fix: filter serial no based on batch no 2020-02-11 12:39:49 +05:30
Govind S Menokee
57d33f9ce6 fix: get_students not respecting the program
The get_students function is not taking into account the program for which the fee needs to be created. If a student is enrolled for multiple programs then the fee schedule pull up the wrong count of students.
2020-02-11 11:23:16 +05:30
Deepesh Garg
90a46acc0d Merge pull request #20557 from rohitwaghchaure/added_indexing_on_columns_to_fix_performance_issue
fix: added indexing on columns to fix performace issue
2020-02-11 10:13:24 +05:30
Deepesh Garg
60634cc829 Merge pull request #20252 from deepeshgarg007/group_by_sales_v12
feat: Group by item wise sales register
2020-02-10 22:20:21 +05:30
Rohit Waghchaure
a7c27596bb fix: added indexing on columns to fix performace issue 2020-02-10 15:30:22 +05:30
Deepesh Garg
b4e3146665 Merge pull request #20550 from nextchamp-saqib/so-update-items-warehouse-fix-v12
fix: mandatory warehouse wrror while updating items after submission
2020-02-10 12:09:38 +05:30
Ashish Shah
495ab6c7f2 fix: backflush raw material based on - Material Transferred for Manufacture
Hi @nabinhait ,
 
Issue : Manufacturing setting > Backflush Raw Material Based on “Material Transferred for Manufacture” doesn't fetch the actual raw material transferred qty. It fetches qty based on "BOM"

The issue is because @creamdory in PR #https://github.com/frappe/erpnext/pull/13384
commit : https://github.com/frappe/erpnext/pull/13384/files#diff-91f0ed661ef4b6e1f167fc7961b1a79b

```
changed from: if trans_qty and manufacturing_qty >= (produced_qty + flt(self.fg_completed_qty)):
to : if trans_qty and manufacturing_qty > (produced_qty + flt(self.fg_completed_qty)):
```
**'='** was added by her in the condition, which was not there before her commit.
Kindly except the fix for the issue.


https://github.com/frappe/erpnext/blob/develop/erpnext/stock/doctype/stock_entry/stock_entry.py#L1057

https://github.com/frappe/erpnext/pull/13384

https://github.com/frappe/erpnext/pull/13384/files#diff-91f0ed661ef4b6e1f167fc7961b1a79b

**before fix gif** :  Stock Entry = Manufacture shows raw material quantity as per BOM.

![FetchTransQtyError](https://user-images.githubusercontent.com/29812965/74123824-ffdd8600-4bf5-11ea-8873-95de24a7ef09.gif)

**after fix gi**f :  Stock Entry = "Manufacture" shows raw material quantity as per "Material Transfer for Manufacture".

![FetchTransQtyFix](https://user-images.githubusercontent.com/29812965/74123836-0c61de80-4bf6-11ea-86fb-d9619fd9b02b.gif)
2020-02-10 11:13:27 +05:30
Deepesh Garg
0e396a62b1 Merge pull request #20554 from ruchamahabal/fix_regional_data_v12
fix: patch for creating irs_1099 custom field (United States)
2020-02-10 09:08:55 +05:30
Rucha Mahabal
1343a4b755 fix: patch for creating irs_1099 custom field (United States) 2020-02-10 00:31:44 +05:30
rohitwaghchaure
ec45c096a0 fix: pricing rule not working on item groups (#20546) 2020-02-09 19:45:42 +05:30
thefalconx33
c7e523cee4 fix: mandatory warehouse wrror while updating items after submission 2020-02-09 12:21:01 +05:30
rohitwaghchaure
9e436336e2 Merge pull request #20542 from rohitwaghchaure/fixed_incorrect_material_request_warehouse_in_production_plan
fix: incorrect warehouse for material request in production plan
2020-02-07 17:43:32 +05:30
Rohit Waghchaure
a9205adfbd fix: test cases 2020-02-07 15:38:56 +05:30
Deepesh Garg
f5bff5f15d fix: Styling and minor fixes 2020-02-07 15:25:43 +05:30
Rohit Waghchaure
bbeacb9acf fix: incorrect warehouse for material request in production plan 2020-02-07 15:04:37 +05:30
Rohit Waghchaure
7027584391 fix: time out error while submitting the purchase receipt which has more than 100 serial nos 2020-02-07 14:41:00 +05:30
Deepesh Garg
372d4e2e05 Merge branch 'group_by_sales_v12' of github.com:deepeshgarg007/erpnext into group_by_sales_v12 2020-02-07 11:10:10 +05:30
Deepesh Garg
bdfa2ff33a fix: Do not calculate total for rate 2020-02-07 11:09:43 +05:30
Deepesh Garg
787b31674a Merge pull request #20534 from rohitwaghchaure/procurement_tracker_not_working_for_special_char_company_name_v12_hotfix
fix: Procurement Tracker report not working
2020-02-07 10:44:13 +05:30
Rohit Waghchaure
0eb9f7408e fix: Procurement Tracker report not working 2020-02-06 14:38:51 +05:30
Deepesh Garg
5c96415eb5 Merge pull request #20530 from nextchamp-saqib/mv-fix-v12
fix: typo; serial no doesn't have amc start date
2020-02-06 13:58:29 +05:30
thefalconx33
d9f8347fc8 fix: typo; serial no doesn't have amc start date 2020-02-06 12:58:48 +05:30
Deepesh Garg
2a48fe6ce6 Merge pull request #20524 from deepeshgarg007/bank_account_label__v12
Bank account label  v12
2020-02-05 18:04:12 +05:30
deepeshgarg007
3ed574532d fix: filter 2020-02-05 18:02:31 +05:30
deepeshgarg007
9f58e59a2c fix: Label and UX fixes while creating payment entry against customer 2020-02-05 18:02:19 +05:30
Deepesh Garg
23c6d6cf16 Merge pull request #20522 from rohitwaghchaure/picklist_nonetype_error
fix: unsupported operand type(s) for += 'int' and 'NoneType'
2020-02-05 17:44:46 +05:30
Rohit Waghchaure
8e0a9e8748 fix: unsupported operand type(s) for += 'int' and 'NoneType' 2020-02-05 16:17:16 +05:30
Deepesh Garg
67dbeee7b2 fix: Add total row 2020-02-05 15:34:08 +05:30
Parth J. Kharwar
b4a0c773ae fix: half day leave date value reset (#20488)
* fix: full day leaves not tagged as half day in attendance

* chore: code cleanup for half day date value set
2020-02-05 15:00:58 +05:30
Anurag Mishra
ce598530db fix: requested changes 2020-02-05 12:49:11 +05:30
Deepesh Garg
694f57fbb4 Merge pull request #20521 from deepeshgarg007/fixnancial_query_fix_v12
fix: SQL query in financial statements
2020-02-05 12:33:06 +05:30
Deepesh Garg
0f90b870fb fix: SQL query in financial statements 2020-02-05 12:30:15 +05:30
Nabin Hait
bd5b37dbb7 Merge branch 'v12-pre-release' into version-12 2020-02-03 19:00:36 +05:30
Nabin Hait
7e93e87244 bumped to version 12.4.3 2020-02-03 19:20:36 +05:50
Deepesh Garg
02f4aa6db6 fix: Unable to submit landed cost voucher (#20494)
* fix: Unable to submit landed cost voucher

* fix: Test case for multiple landed cost voucher against a Purchase receipt

* fix: Test Case
2020-02-03 18:56:43 +05:30
Deepesh Garg
ade2c36123 fix: Unable to submit landed cost voucher (#20494)
* fix: Unable to submit landed cost voucher

* fix: Test case for multiple landed cost voucher against a Purchase receipt

* fix: Test Case
2020-02-03 18:54:35 +05:30
Pranav Nachnekar
3ef80f2d2e fix: disallow quick entry for doctypes with tree view (#20453)
* fix: imporer escaping

* fix: disallow quick entry for doctypes with tree view
2020-02-03 17:05:30 +05:30
Himanshu
85c6480b41 fix(Report): Quality Review report sql fix (#20425)
* fix: report fix

* fix: add QM permission

* fix: add QM permission
2020-02-03 16:16:42 +05:30
Saqib
f3c94315c3 feat: add tax category in pos profile (#20414)
* feat: add tax category in pos profile

* fix: review fixes
2020-02-03 15:53:22 +05:30
Saqib
a4219f1cfe fix: gst permission for gst settings & hsn code (#20501)
* fix: gst permission for gst settings & hsn code

* Fix: Typo

Co-authored-by: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com>
2020-02-03 15:40:53 +05:30
Deepesh Garg
831a7ddd6f fix: Minor fixes 2020-02-01 23:15:21 +05:30
Parth J. Kharwar
43886ca016 fix: employee checkin doctype time permissions (#20473) 2020-01-31 14:37:56 +05:30
rohitwaghchaure
61b4244837 Merge pull request #20479 from deepeshgarg007/finance-book-filter-new-v12
fix: Do not show any finance book record if no finance book filter is applied
2020-01-31 13:19:49 +05:30
Deepesh Garg
6871979acc Merge pull request #20470 from alyf-de/version-12-hotfix
fix(regional): byte strings in DATEV Report
2020-01-31 11:20:51 +05:30
Deepesh Garg
bb7cf81d52 fix: Cash flow filter fix 2020-01-31 11:18:26 +05:30
Deepesh Garg
9826291c52 fix: Filtering fixes in financial statement 2020-01-31 11:18:13 +05:30
racitup
08661250d2 Ignore codacy SQL injection warning (internal code only) and add copyright notice 2020-01-30 15:22:34 +00:00
racitup
814001a90f fix: attempt at pymysql InternalError 1054 about clearance_date in field list when removing payments from bank transactions 2020-01-30 15:22:34 +00:00
racitup
eef73a0d92 manually added patch for bank reconciliation fields since develop branch was in a different place #20380 2020-01-30 15:22:34 +00:00
racitup
a1e3202054 fix: Bank Reconciliation Bank Account and Bank Account No field names 2020-01-30 15:22:34 +00:00
racitup
bee017e17c fix: Plaid transaction import order, transaction_id duplicate check, added transaction category tags 2020-01-30 15:22:34 +00:00
racitup
a054508211 fix: Plaid automatic_synchronization TypeError on filter & add info log message #20343 2020-01-30 15:22:34 +00:00
racitup
feaa82a8ea fix: KeyError about bank_account_no due to non-existent field: #20343 2020-01-30 15:22:34 +00:00
racitup
a175e06149 fix: pymysql.err.InternalError about t2.bank_account_no due to removal of field from Journal Entry Account table: #20343 2020-01-30 15:22:34 +00:00
Deepesh Garg
ba4d9469ff fix: Do not show any finance book record if no finance book filter is applied 2020-01-30 15:41:44 +05:30
Deepesh Garg
54c6f91dde Merge pull request #20474 from ParthKharwar/hotfix-brand-breadcrumb
fix: rename breadcrumb for Brand DocType from Selling to Stock
2020-01-30 13:14:21 +05:30
Parth Kharwar
e552e958de fix: rename breadcrumb for Brand DocType from Selling to Stock 2020-01-30 12:35:02 +05:30
Raffael Meyer
7524ecbc59 fix #20469 2020-01-30 00:08:25 +01:00
Deepesh Garg
be2ca5f8cc Merge pull request #20460 from 0Pranav/fix-supplier-quotation-button-v12
fix: missing plus button in request for quotaion for supplier quotation
2020-01-29 10:45:32 +05:30
0Pranav
d4d641a07e fix: missing plus button in request for quotaion for supplier quotation 2020-01-29 09:36:48 +05:30
rohitwaghchaure
8e5b812a97 fix: incorrect work order qty for sub assembly items (#20448) 2020-01-28 14:34:54 +05:30
Nabin Hait
df94f0565e fix: cannot complete task if dependent task are not completed / cancelled (#20434) 2020-01-28 14:08:48 +05:30
Anurag Mishra
637915f295 fix: odometer value was not syncing properly 2020-01-28 13:15:26 +05:30
Pranav Nachnekar
f2838c4a13 fix: discounting not applied on sales invoice created by subscriptions (#20432)
* fix: imporer escaping

* fix: discounting not applied on sales invoice created by subscriptions
2020-01-28 12:52:42 +05:30
Marica
fe56c7ce39 fix: Column 'project' in where clause is ambiguous (#20439)
* fix: Column 'project' in where clause is ambiguous

* fix: Fixed alias in query
2020-01-28 12:44:03 +05:30
Deepesh Garg
29ecbde445 Merge pull request #20446 from Alchez/v12-driver-address-fetch
fix: only fetch driver address if not set (v12)
2020-01-28 09:20:53 +05:30
Deepesh Garg
611f212aa4 Merge pull request #20443 from fproldan/fix_sqlinjection_hotfix_v12
fix: SQL Injection in get_product_list_for_group method
2020-01-28 09:18:11 +05:30
Rohan Bansal
6fbafa1924 fix: only fetch driver address if not set 2020-01-27 18:04:33 +05:30
NahuelOperto
875bac8bb9 Fix sql injection 2020-01-27 08:58:01 -03:00
Saqib
b4b410f1d2 refac: filters and columns of fixed asset register report (#20420) 2020-01-27 16:06:15 +05:30
deepeshgarg007
eec0ee83e2 fix: Changes in total row display 2020-01-27 15:51:20 +05:30
Deepesh Garg
148951e6f3 fix: Finance book filtering in financial statements (#20411)
* fix: Finance book filtering in financial statements

* fix: Use IS NULL instead of ifnull
2020-01-27 15:18:45 +05:30
Saqib
612e3b77fd fix: incorrect status for fraction of outstanding amount (#20370) 2020-01-27 14:23:05 +05:30
Deepesh Garg
a60849f6ab Merge pull request #20415 from hrwX/customer_primary_addr_v12
fix(Customer): Change `primary_address` fieldtype to Text
2020-01-27 12:33:38 +05:30
deepeshgarg007
6f27e97980 fix: Add group by functionality to purchase register 2020-01-27 08:49:52 +05:30
deepeshgarg007
8d9145aaac fix: Code cleanup and commonification 2020-01-27 08:49:42 +05:30
Himanshu Warekar
25a7330db0 fix: change primary addr fieldtype to Text 2020-01-24 17:36:59 +05:30
Saqib
92c999afcb fix: wrong outstanding invoices fetched against employee (#20374) 2020-01-24 16:23:05 +05:30
Anurag Mishra
9e3dc25b1c fix: Duplicate Attendance Due Wrong Date format (#20389) 2020-01-24 16:14:38 +05:30
Rucha Mahabal
2a3cd7d601 fix(Healthcare): disabled Lab Test Template shown as Enabled in List View (#20379)
* fix (Healthcare): disabled Lab Test Template shown as enabled in List View

* fix: remove custom disabled button and unhide disabled field
2020-01-24 16:11:16 +05:30
Marica
3ca67ceebb fix: Product Page non-stock item status (#20384) 2020-01-24 15:52:12 +05:30
Rucha Mahabal
245fc49089 fix: healthcare settings registration fee NoneType error (#20396) 2020-01-24 15:50:40 +05:30
rohitwaghchaure
78199dbf0a Merge pull request #20399 from rohitwaghchaure/fixed_serial_no_button_not_working_v12_hotfix
fix: add serial no button not working
2020-01-24 13:49:25 +05:30
Deepesh Garg
2ac33c9ce9 Merge pull request #20402 from deepeshgarg007/general_ledger_sort_v12
fix: Order GL entries by creation time in General ledger
2020-01-24 13:19:56 +05:30
Deepesh Garg
34cd035d8c Merge pull request #20392 from rohitwaghchaure/unlink_reference_name_from_batch_on_cancel_of_stock_transactions
fix: unlink references from batch on cancellation of stock transactions
2020-01-23 22:41:21 +05:30
deepeshgarg007
ba4f565ca3 fix: Order GL entries by creation time in General ledger 2020-01-23 17:48:41 +05:30
Rohit Waghchaure
5860427bc5 delete the auto created batch 2020-01-23 17:43:30 +05:30
Rohit Waghchaure
89f42eef49 fix: add serial no button not working 2020-01-23 15:53:45 +05:30
Deepesh Garg
eacbfa15c0 Merge pull request #20385 from rohitwaghchaure/fixed_lead_date_comparision_issue
fix: lead date comparison issue
2020-01-23 15:23:28 +05:30
Deepesh Garg
5d45c2c184 Merge pull request #20387 from rohitwaghchaure/lead_showing_two_times_in_the_report
fix: lead details duplicate entries
2020-01-23 15:21:11 +05:30
Deepesh Garg
b24b0378ea Merge pull request #20382 from nextchamp-saqib/small-fix-asset-v12
fix: tuple index out of range error
2020-01-23 15:02:29 +05:30
Rohit Waghchaure
144012a5de fix: unlink references from batch on cacnellation of stock transactions 2020-01-23 12:42:42 +05:30
rohitwaghchaure
5fb4027375 Merge pull request #20376 from rohitwaghchaure/zero_division_qty_error_while_completing_fg_entry_v12_pre_release
fix: Zero division error while making finished good entry against the…
2020-01-22 17:01:21 +05:30
rohitwaghchaure
3fa65f1363 Merge pull request #20375 from rohitwaghchaure/zero_division_qty_error_while_completing_fg_entry
fix: Zero division error while making finished good entry against the…
2020-01-22 17:00:44 +05:30
Rohit Waghchaure
79f630661a fix: lead details duplicate entries 2020-01-22 16:53:30 +05:30
Rohit Waghchaure
61b9fe6a81 fix: lead date comparison issue 2020-01-22 14:43:37 +05:30
thefalconx33
f38d9d8f0d fix: tuple index out of range error 2020-01-22 12:24:03 +05:30
Rohit Waghchaure
3a67daa1fd fix: Zero division error while making finished good entry against the work order 2020-01-21 19:25:27 +05:30
Rohit Waghchaure
813726b415 fix: Zero division error while making finished good entry against the work order 2020-01-21 19:22:27 +05:30
rohitwaghchaure
87fe2143be fix: not able to import chart of account if parent account is missing (#20367) 2020-01-21 15:32:12 +05:30
Saqib
c759b06f16 fix: account not added to child company (#20364)
* fix: account not added to child company

* fix: removed print

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-01-21 15:26:46 +05:30
Chinmay Pai
c32249a72f fix: Set Quotation expired if not Ordered or Lost (#20365)
Co-authored-by: Chinmay Pai <chinmaydpai@gmail.com>

Co-authored-by: KanchanChauhan <angel143.dr@gmail.com>
2020-01-21 14:12:49 +05:30
Nabin Hait
7cd6022a0b Merge branch 'v12-pre-release' into version-12 2020-01-21 13:07:27 +05:30
Nabin Hait
67274d01e8 bumped to version 12.4.2 2020-01-21 13:27:26 +05:50
Nabin Hait
19d7e43b90 fix: Don't fetch price list rate on change of qty (#20360) 2020-01-21 13:04:30 +05:30
Nabin Hait
05f4be69f4 fix: Don't fetch price list rate on change of qty (#20362) 2020-01-21 13:04:26 +05:30
Mangesh-Khairnar
541881162a fix: calculate remaining leave balance (#20342)
* fix: calculate remaining leave balance

* fix: nonetype issue in the total leaves calculation
2020-01-20 19:42:49 +05:30
Sahil Khan
c8884257b9 Merge branch 'v12-pre-release' into version-12 2020-01-20 18:02:31 +05:30
Sahil Khan
4674fec320 bumped to version 12.4.1 2020-01-20 18:22:31 +05:50
rohitwaghchaure
c7f0ab8ed6 fix: incorrect number of gl entries issue (#20351) 2020-01-20 17:53:44 +05:30
rohitwaghchaure
19c5e031a9 fix: incorrect number of gl entries issue (#20350) 2020-01-20 17:52:53 +05:30
Marica
8b943af911 fix: 'get_additonal_costs' is not defined (#20349)
* fix: 'get_additonal_costs' is not defined

* fix: Re-added get_items on Work order change.
2020-01-20 15:47:47 +05:30
Marica
863eb86a1d fix: 'get_additonal_costs' is not defined (#20345)
* fix: 'get_additonal_costs' is not defined

* fix: Re-added get_items on Work order change.
2020-01-20 15:46:50 +05:30
Nabin Hait
e5fbebe126 fix: deprecated fetching item price based on min_qty (#20348) 2020-01-20 15:45:09 +05:30
Nabin Hait
45be9fe416 fix: deprecated fetching item price based on min_qty (#20346) 2020-01-20 15:44:48 +05:30
Nabin Hait
1596352585 fix: close_opportunity_after_days field is in selling settings (#20334) 2020-01-17 15:59:02 +05:30
Nabin Hait
4b893b1d51 fix: Plaid TypeError in add_bank_accounts (#20336) 2020-01-17 15:57:43 +05:30
Parth J. Kharwar
d85e144944 chore: changed employee contact field name (#20227) 2020-01-17 11:47:49 +05:30
Rohan
4dfff5a271 fix: handle default value for batch size in BOM operation (#20327) 2020-01-17 11:46:00 +05:30
Pranav Nachnekar
c2b9598966 fix: imporer escaping (#20329) 2020-01-17 11:45:03 +05:30
Vishal Dhayagude
d16e7c096b fix: salary structure assignment filter employee based on company (#20319)
* fix: pick company from employee salary structure

* fix: Salary structure assignment fetch company from enmployee

* Revert "fix: Salary structure assignment fetch company from enmployee"

This reverts commit ab2da691c79646d6d095f347ea3e273f466ee34f.

* fix: Salary structure assignment fetch company from enmployee

* fix: filter on company for salary structure assignment

* fix: minor changes

* fix: minor changes

* fix: added company to salary strucutre assignment
2020-01-17 11:44:20 +05:30
sahil28297
d5676a87a8 Merge pull request #20331 from 0Pranav/version-12
fix: imporer escaping
2020-01-16 17:51:32 +05:30
0Pranav
73dff8993a fix: imporer escaping 2020-01-16 17:38:11 +05:30
Sahil Khan
0ef799dff8 Merge branch 'v12-pre-release' into version-12 2020-01-16 14:30:35 +05:30
Sahil Khan
e8476ef42a bumped to version 12.4.0 2020-01-16 14:50:35 +05:50
Himanshu
5511912251 fix: don't hide primary_address_and_contact_detail (#20321) 2020-01-16 13:39:03 +05:30
Saqib
514d3c37b9 fix: hide total amount field from journal entry PF (#20325) 2020-01-16 13:38:21 +05:30
Nabin Hait
9fef6e1b68 docs: Added change log for v12.4.0 (#20326) 2020-01-16 13:37:46 +05:30
Nabin Hait
37c51f5913 fix: Multiple fixes based on testing on pre-release branch (#20300)
* fix: Multiple fixes based on testing on pre-release branch

* fix: reload hr settings
2020-01-16 13:37:30 +05:30
Nabin Hait
7721954bbb fix: Multiple fixes based on testing on pre-release branch (#20303)
* fix: Multiple fixes based on testing on pre-release branch

* fix: reload hr settings
2020-01-16 13:37:20 +05:30
Nabin Hait
7e33d875e0 Revert "fix: incorrect required qty for subcontracting purchase receipt (#20288)" (#20323)
This reverts commit b65b525c44.
2020-01-16 13:02:40 +05:30
Marica
0a50088a0e fix: Applied query on PO Supplier popup field in Material Request. (#20232) 2020-01-15 19:24:10 +05:30
Marica
6ba6ba27bd fix: Missing argument in status updater (#20284) 2020-01-15 19:22:21 +05:30
Saqib
611e23c149 fix: make jv depreciations only if calculate depr is checked (#20283) 2020-01-15 19:05:42 +05:30
sahil28297
89affd8d09 fix(patch): reload tax category (#20307) 2020-01-15 18:58:09 +05:30
sahil28297
ab2e52ca07 fix(patch): reload tax category (#20309) 2020-01-15 18:57:40 +05:30
sahil28297
8dfd5535f1 fix(patch): do not append taxes to template if account name is not set (#20311) 2020-01-15 18:57:20 +05:30
sahil28297
dc0ea3fdb0 fix(patch): do not append taxes to template if account name is not set (#20310) 2020-01-15 18:57:08 +05:30
rohitwaghchaure
b65b525c44 fix: incorrect required qty for subcontracting purchase receipt (#20288) 2020-01-15 16:37:10 +05:30
Saqib
8c45ae73b3 fix: get existing payment req amount only from unpaid req (#20290) 2020-01-15 16:36:19 +05:30
rohitwaghchaure
4872417f86 fix: incorrect number of GL Entries error in stock entry (#20298) 2020-01-15 16:35:15 +05:30
rohitwaghchaure
fed9e2861c fix: incorrect number of GL Entries error in stock entry (#20297) 2020-01-15 16:35:02 +05:30
Pranav Nachnekar
c50f08a23a fix: remove debugger statement (#20294)
introduced in https://github.com/frappe/erpnext/pull/20222
2020-01-15 14:49:42 +05:30
Pranav Nachnekar
6e115638a1 fix: remove debugger statement (#20295)
backport of https://github.com/frappe/erpnext/pull/20294
2020-01-15 14:49:28 +05:30
Saqib
2d37bbedaa fix: auto cancel if movement exists (#20268)
Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-01-14 12:42:43 +05:30
rohitwaghchaure
00f6f114e7 Merge pull request #20278 from rohitwaghchaure/fixed_report_stock_and_account_value_comparison_v12_pre_release
fix: filter issue for the stock and account value comparision report
2020-01-14 12:19:04 +05:30
rohitwaghchaure
437be1162e Merge pull request #20277 from rohitwaghchaure/fixed_report_stock_and_account_value_comparison_v12_hotfix
fix: filter issue for the stock and account value comparision report
2020-01-14 12:18:50 +05:30
Rohit Waghchaure
ca4789c9f7 fix: filter issue for the stock and account value comparision report 2020-01-14 12:17:27 +05:30
Rohit Waghchaure
e3b6a14ab1 fix: filter issue for the stock and account value comparision report 2020-01-14 12:15:06 +05:30
Saqib
eb7893768b fix: payment entry can be created against on hold PI (#20271) 2020-01-14 12:07:30 +05:30
Saqib
6e1ef4f5c2 fix: gl not generated on manual asset creation (#20266) 2020-01-14 11:38:00 +05:30
Saqib
d56bc81daf fix: gl not generated on manual asset creation (#20267) 2020-01-14 11:36:47 +05:30
Deepesh Garg
7d7027f47c fix: Remove patch to set automatic tax fetching from item tax template (#20235)
* fix: Remove patch to set automatic tax fetching from item tax template

* fix: Remove duplicate patch
2020-01-13 15:09:21 +05:30
Deepesh Garg
a14f72590d fix: Remove patch to set automatic tax fetching from item tax template (#20236)
* fix: Remove patch to set automatic tax fetching from item tax template

* fix: Remove duplicate patch
2020-01-13 15:09:16 +05:30
Deepesh Garg
7086ecb3b0 Merge pull request #20246 from nextchamp-saqib/asset-cancel-fix-v12
fix: auto cancel if movement exists
2020-01-13 15:07:57 +05:30
Saqib
4b401f0573 Merge pull request #20263 from nextchamp-saqib/customer-jv-fix-v12-pre
fix: remove default customer as party type
2020-01-13 14:12:22 +05:30
thefalconx33
5beee8af58 fix: remove default customer as party type 2020-01-13 13:51:17 +05:30
Deepesh Garg
8508ace50e Merge pull request #20258 from nextchamp-saqib/revery-hv-v12
fix: remove default customer as party type
2020-01-13 13:49:20 +05:30
thefalconx33
c52ef56875 fix: remove default customer as party type 2020-01-13 12:45:08 +05:30
deepeshgarg007
4dfe8ab483 fix: Remove debug param 2020-01-12 22:15:25 +05:30
deepeshgarg007
ea5e0a9d5b fix: Grand total query fix 2020-01-12 21:56:31 +05:30
deepeshgarg007
30111e6403 fix: Remove group-by item 2020-01-12 21:56:22 +05:30
deepeshgarg007
e5a572a4a8 fix: Group by sales register report 2020-01-12 21:56:12 +05:30
thefalconx33
83ed7df8cd fix: auto cancel if movement exists 2020-01-11 14:20:18 +05:30
Deepesh Garg
97e93c6257 Merge pull request #20238 from deepeshgarg007/hsn_template_v12
fix: Item tax template fetching from HSN Code
2020-01-10 21:45:22 +05:30
Deepesh Garg
f7cd8c2702 Merge pull request #20239 from deepeshgarg007/hsn_template_v12
fix: Item tax template fetching from HSN Code
2020-01-10 21:43:26 +05:30
deepeshgarg007
f67b3f8a79 fix: Item tax template fetching from HSN Code 2020-01-10 15:04:34 +05:30
Nabin Hait
8356d214e1 Merge branch 'version-12-hotfix' into v12-pre-release 2020-01-09 15:09:20 +05:30
rohitwaghchaure
64f3577148 feat: report to show difference between stock and account value (#20226) 2020-01-09 15:05:26 +05:30
Marica
ff3ff9a3c2 fix: Employee name in Report trial balance for party (#20224)
* fix: Employee name in Report trial balance for party

* fix: Add account filter in trial balance for party report

Co-authored-by: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com>
2020-01-09 13:34:30 +05:30
Pranav Nachnekar
03c1396fc7 fix: Parent Item of Product Bundle should not appear in dialog (#20222)
* fix: add new delivery note button in Sales Order

* fix: parent item should not appear in raw material request dialog
2020-01-09 13:31:53 +05:30
Pranav Nachnekar
4479b93c1a fix: escape % in customer name (#20203)
* fix: add new delivery note button in Sales Order

* fix: escape % in customer name

* Update sales_order.js

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-01-09 12:22:11 +05:30
Anurag Mishra
da82edf711 fix: submit salary is not showing after creating slary slip in payroll (#20225) 2020-01-09 12:06:14 +05:30
Saqib
7b8bf4f4d4 Merge pull request #20213 from nextchamp-saqib/pay-entry-v12
fix: paid amount gets overwritten by get_outstanding_invoices
2020-01-07 13:33:16 +05:30
thefalconx33
07c915d00b fix: paid amount gets overwritten by get_outstanding_invoices 2020-01-07 13:14:01 +05:30
Marica
75b62cdd9f fix: Added description and title to supplier selection popup in Material Request. (#20180) 2020-01-07 13:06:13 +05:30
Don-Leopardo
54f2d53522 fix: get_item_price not working properly (#20206)
* fix: min_qty not working without party and valid_from not being used to order results

* fix: fetch price on item qty change
2020-01-07 11:35:56 +05:30
Deepesh Garg
55bdaae3ee feat: Validity for Item taxes (#20136)
* feat: Validity for Item taxes

* fix: Add HSN wise taxes

* fix: Sort taxes based on validity

* fix: Validation for item tax template and filters based on validity

* fix: Add missing semicolon

* fix: Validate tax template only if item code available

* fix: Do not validate or filter item tax template if no item taxes applied

* fix: Consider item group for validating taxes

* fix: Test cases for item tax  validation

* fix: Item tax template filtering fixes

* fix: Add missing semicolon

* fix: Remove unnecessary query
2020-01-07 11:31:19 +05:30
sahil28297
6bbfce13fe fix(item_tax_template): fetch parent account if not set (#20197) 2020-01-07 11:30:18 +05:30
Pranav Nachnekar
0980ebe93e fix: add new delivery note button in Sales Order (#20200)
* fix: add new delivery note button in Sales Order

* fix: removed debugger

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2020-01-07 11:29:45 +05:30
Rohan
ded5a255af fix: integer UOM check in transactions (#20177) 2020-01-07 11:24:02 +05:30
Anurag Mishra
13c29aeec4 fix: loan deduction during creation of bank entry in payroll (#20194) 2020-01-07 11:13:58 +05:30
rohitwaghchaure
cf261d0788 Merge pull request #20196 from rohitwaghchaure/fixed_party_type_in_payment_request_hotfix
fix: incorrect party type in payment request
2020-01-06 15:16:30 +05:30
Rohit Waghchaure
3074679d7e fix: party type in payment request 2020-01-06 15:09:09 +05:30
rohitwaghchaure
a037f9d55e Merge pull request #20171 from nabinhait/inter-company-serial-no-outgoing-rate-v12
fix: Get outgoing rate of serial no from SLE if serial no already transferred to another company
2020-01-06 12:06:08 +05:30
rohitwaghchaure
3530d0d7c9 fix: incorrect valuation rate for finished good entry (#20166) 2020-01-06 11:51:36 +05:30
Deepesh Garg
3da9967fa8 Merge pull request #20178 from Alchez/fix-v12-item-price-list-view
feat: show item name in Item Price list view (v12)
2020-01-06 08:06:07 +05:30
Himanshu
f276e8d6a1 fix(plaid): change json structure (#20190) 2020-01-06 01:01:37 +05:30
Rohan
9e0d9aa947 feat: show item name in Item Price list view (#20156) 2020-01-03 15:21:58 +05:30
Marica
df363d7dd6 Merge pull request #20173 from nextchamp-saqib/item-min-qty-v12
ux: add description for minimum purchase qty
2020-01-03 12:54:45 +05:30
thefalconx33
81e2f2a45f ux: add description for minimum purchase qty 2020-01-02 19:16:36 +05:30
Nabin Hait
255361ba92 fix: Get outgoing rate of serial no from SLE if serial no already transferred to another company 2020-01-02 19:01:48 +05:30
rohitwaghchaure
67bcfde3de Merge pull request #20168 from rohitwaghchaure/fix_pricing_rule_issue_for_ignore_pricing_rule
fix: ignore pricing rule for other item group
2020-01-02 17:32:43 +05:30
Rohit Waghchaure
b6351b4ca0 fix: ignore pricing rule for other item group 2020-01-02 17:29:50 +05:30
Deepesh Garg
c5a8d33374 Merge pull request #20162 from deepeshgarg007/general_ledger_print_v12
fix: Show closing row in general ledger print
2020-01-02 17:06:05 +05:30
deepeshgarg007
f4589da806 fix: Show closing row in general ledger print 2020-01-02 12:26:12 +05:30
Rucha Mahabal
c5cd7cd65d fix(Subscription): pass start_date while computing next_schedu… (#20160) 2019-12-31 19:15:48 +05:30
Anurag Mishra
a5e56dfb6f fix: set currency to company default currency (#20158) 2019-12-31 17:23:25 +05:30
Saqib
adb35d8703 fix: don't show transfer button if already items are supplied (#20155) 2019-12-31 17:20:07 +05:30
Deepesh Garg
db4f3d98ab Merge pull request #20150 from nextchamp-saqib/rounded-total-ss-v12
feat: added provision to disable rounded total for salary slips
2019-12-31 17:03:11 +05:30
thefalconx33
cd2899f429 feat: added provision to disable rounded total for salary slips 2019-12-31 15:29:36 +05:30
thefalconx33
8f6a6e8d2a feat: added provision to disable rounded total for salary slips 2019-12-31 14:35:03 +05:30
Anurag Mishra
fd68a682ca fix(refactor): loan in HRMS (#20023)
* fix: update laon on salary slip submission and Cancelation

* fix(refactor): Loan in HRMS

* fix: Changes requested

* Update loan.py

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2019-12-31 14:01:22 +05:30
Saqib
25fd2743a8 fix: party account currency not fetched from account for SO (#20147) 2019-12-31 13:55:34 +05:30
Saqib
826989afda fix: none type object updates AR report row (#20148) 2019-12-31 13:46:39 +05:30
Marica
10622aee7f fix: Field 'other_charges_calculation' column type changed. (#20142) 2019-12-30 22:20:27 +05:30
Anurag Mishra
26e86e7576 fix: added extra condition (#20139) 2019-12-30 16:17:10 +05:30
Rucha Mahabal
efa7d5c831 fix: format currency field in bank and cash payment voucher print format (#20138) 2019-12-30 15:18:02 +05:30
rohitwaghchaure
55cf2477cd fix: incorrect batch fetched for the serialized items (#20118) 2019-12-30 13:27:10 +05:30
ci2014
5c144c992e Skip validate_duplicate_apply_on if not necessary (#20128)
Skip the validation of duplicate fields if the type is transaction, for example.
2019-12-30 11:50:43 +05:30
rohitwaghchaure
2fcc51dece fix: service item's cost showing incorrect amount in stock entry (#20105) 2019-12-30 11:42:01 +05:30
Deepesh Garg
e76670562c Merge pull request #20124 from nextchamp-saqib/so-update-items-hfix
fix: don't allow deletion of items which has work order assigned to it
2019-12-28 21:01:47 +05:30
Deepesh Garg
e1aa48968e Merge pull request #20126 from marination/stock-module-desk-hotfix
chore: Added Reports to Stock Module Desk
2019-12-28 17:45:31 +05:30
marination
2faa8f92be chore: Added Reports to Stock Module Desk 2019-12-27 17:21:08 +05:30
thefalconx33
82c5a2bf45 fix: don't allow deletion of items which has work order assigned to it 2019-12-27 17:08:57 +05:30
Marica
ed386376dc fix: Removed unneccesary Duplicate Entry Error. (#20121) 2019-12-27 16:53:09 +05:30
Marica
65353e5590 fix: Show both warehouse columns in Stock Entry Grid view. (#20114) 2019-12-27 15:30:47 +05:30
Saqib
aab1fb0d6a fix: unpaid invoices are created even if current invoice is paid (#20082) 2019-12-27 13:02:33 +05:30
Deepesh Garg
2dec923740 fix: Patch incorrect GST category in invoices (#20085)
* fix: Patch to set GST category for unregistered invoice

* fix: Move patch up in line

* fix: Remove unwanted patch statements
2019-12-27 12:58:38 +05:30
Saqib
6ea509f7f4 feat: allow adding and deleting of items in submitted SO & PO (#19912)
* feat: allow adding of items after quotation submission

* feat: allow deletion of items from submitted SO & PO

* fix: case when items are added and deleted at once

* fix: add test cases
* For deletion of items while Updating Items after submitting PO & SO
2019-12-27 12:56:17 +05:30
Sammish Thundiyil
dfdd684fbf modified: erpnext/hr/doctype/repayment_schedule/repayment_schedule.json (#20112) 2019-12-27 12:52:40 +05:30
rohitwaghchaure
b299026280 fix: incorrect reorder level in stock balance report (#20110) 2019-12-27 12:36:14 +05:30
Rohan
3d2e61eef3 fix: Add Serial No button prompt (#20098) 2019-12-27 12:34:43 +05:30
rohitwaghchaure
47eda0fa6d fix: incorrect validation for scrap items (#20108) 2019-12-27 12:21:10 +05:30
Mangesh-Khairnar
8ec1d8d5da fix: fetch opening accumulated depreciation (#20096) 2019-12-27 12:14:27 +05:30
rohitwaghchaure
6adf617a72 Merge pull request #20078 from nabinhait/rounding-adjustment-v12
fix: rounding adjustment while both inclusive tax and discount amount present
2019-12-27 11:52:10 +05:30
Deepesh Garg
7c7291ed4f Merge pull request #20088 from nextchamp-saqib/pay-reco-hfix
fix: undefined dr_or_cr_notes in case of party type Employee
2019-12-27 08:12:33 +05:30
rohitwaghchaure
a169292f6e Merge pull request #20103 from deepeshgarg007/travis-fix-v12
fix: Travis(v12)
2019-12-26 22:51:13 +05:30
thefalconx33
5869757860 fix: travis failing for leave application test 2019-12-26 21:52:54 +05:30
Deepesh Garg
6fefdb5b18 Merge pull request #20092 from nextchamp-saqib/account-period-hfix
fix: only show doctypes which impact accounting in accounting period
2019-12-26 15:23:54 +05:30
Saqib
8c08db33d7 Merge pull request #20094 from prssanna/grid-fix-v12
fix: wrong items in table being deleted
2019-12-26 14:54:16 +05:30
prssanna
bc15ea68b8 fix: wrong items in table being deleted 2019-12-26 14:44:47 +05:30
thefalconx33
ee28663836 fix: undefined dr_or_cr_notes in case of party type Employee 2019-12-26 14:43:03 +05:30
thefalconx33
c522df8f9d fix: only show doctypes which impact accounting in accounting period 2019-12-26 13:56:36 +05:30
Himanshu
d0e39e6fac fix: remove quote (#20077) 2019-12-25 15:16:35 +05:30
Nabin Hait
4c4da06a71 fix: rounding adjustment while both inclusive tax and discount amount present 2019-12-25 14:00:51 +05:30
rohitwaghchaure
827ac91e7f fix: supplier email id field not showing in the notification for purchase cycle doctypes (#20070) 2019-12-24 13:03:41 +05:30
Deepesh Garg
379b6df361 Merge pull request #20008 from deepeshgarg007/budget_variance_dimension_v12
feat: Dynamic filters for dimensions in budget variance report
2019-12-24 12:58:22 +05:30
Deepesh Garg
2e461551d6 fix: Allow creation of multiple landed cost voucher against a Purchase Document (#20059) 2019-12-24 12:55:18 +05:30
Himanshu
2d30dc583c fix: resolve ambigious error (#20067) 2019-12-24 12:31:34 +05:30
Sun Howwrongbum
9de6083dbf feat: consider expiry_date during Batch queries (#20064) 2019-12-24 12:29:05 +05:30
RJPvT
9512e55704 Update supplier_scorecard.py (#20060) 2019-12-24 12:25:05 +05:30
rohitwaghchaure
9635df4f26 Merge pull request #20066 from rohitwaghchaure/job_card_operation_id_showing_blank
fix: Map custom job card create button with dashboard
2019-12-24 12:11:16 +05:30
Rohit Waghchaure
7bbeb31015 fix: Map custom job card create button with dashboard 2019-12-24 12:08:58 +05:30
Deepesh Garg
1f4b251f53 Merge pull request #20045 from deepeshgarg007/employee_advance_return_v12
feat: Capture return amount against Employee Advance via Journal Entry
2019-12-24 11:55:24 +05:30
Deepesh Garg
34dabc715b fix: Update return amount label 2019-12-24 11:54:04 +05:30
DeeMysterio
3881ce94ef fix(employee onboarding): stop showing irrelevant job offer links for a job applicant (#20054) 2019-12-23 19:02:44 +05:30
Marica
065bbc9cb3 enhancement: Added Set Reserve Warehouse field in Purchase Order. (#19992) 2019-12-23 18:24:28 +05:30
Pranav Nachnekar
139bb92df9 fix: wrap scheduled_time with getdate (#20046) 2019-12-23 18:19:52 +05:30
Deepesh Garg
15c8caac57 fix: Do not validate accounting dimensions if from repost (#20043)
* fix: Do not check for accounting dimensions in case of repost

* fix: Do not validate accounting dimensions if from repost
2019-12-23 17:58:44 +05:30
Saqib
7b926f228f fix: bad condition while checking last asset movement entry (#20048) 2019-12-23 16:08:40 +05:30
Saqib
dab963aac0 fix: remove mandatory purchase reference for existing asset (#19981) 2019-12-23 15:56:50 +05:30
Saqib
170c9d0ab8 fix: remove mandatory purchase reference for existing asset (#19980) 2019-12-23 15:56:44 +05:30
Saqib
f27a6528db fix: exchange rate not fetching correctly (#19985) 2019-12-23 15:52:59 +05:30
Anurag Mishra
b139f2fc0f feat: fixed party and party type in Accrual journal entry (#20022) 2019-12-23 15:50:20 +05:30
Mangesh-Khairnar
498ebfba9d fix(expense-claim): update status (#20032)
* fix(expense-claim): update status

* fix(expense-claim): compare using grandtotal precision
2019-12-23 15:48:18 +05:30
Deepesh Garg
2ebafb39ac Merge pull request #20011 from nextchamp-saqib/pi-payable-filter-hotfix
fix: bad credit to filter query in purchase invoice
2019-12-23 14:51:50 +05:30
thefalconx33
adc1b9dc0a fix: bad query for debit_to field in sales invoice 2019-12-23 13:08:46 +05:30
deepeshgarg007
b7012a47a2 fix: Remove trailing whitespace and add semicolon 2019-12-23 11:13:53 +05:30
deepeshgarg007
f1036c6985 feat: Custom button to create return entry from Employee Advance 2019-12-23 11:13:44 +05:30
deepeshgarg007
8f6e989f57 feat: Capture return amount against Employee advance via Journal Entry 2019-12-23 11:13:34 +05:30
deepeshgarg007
9609cce7b7 fix: Dashboard for employee advance doctype 2019-12-23 11:13:24 +05:30
Deepesh Garg
e14961a6c6 Merge pull request #20013 from nextchamp-saqib/share-transf-perm
fix: no role has cancelling permission for share transfer doctype
2019-12-21 17:22:15 +05:30
Deepesh Garg
f47afecc42 Merge pull request #20035 from rohitwaghchaure/fixed_patch_for_add_export_type_field_in_party_master_v12_hotfix
fix: patch
2019-12-21 17:20:28 +05:30
Sahil Khan
782f45ae5f Merge branch 'v12-pre-release' into version-12 2019-12-20 17:11:52 +05:30
Sahil Khan
f9ac8f63cf bumped to version 12.3.1 2019-12-20 17:31:52 +05:50
sahil28297
411b8e86b3 Merge pull request #20036 from rohitwaghchaure/fixed_patch_for_add_export_type_field_in_party_master_v12_pre_release
fix: patch
2019-12-20 17:09:09 +05:30
Rohit Waghchaure
2b14669b58 fix: patch 2019-12-20 17:06:57 +05:30
Rohit Waghchaure
14f624f587 fix: patch 2019-12-20 17:06:00 +05:30
Sahil Khan
98ddbf05b1 Merge branch 'v12-pre-release' into version-12 2019-12-20 15:55:12 +05:30
Sahil Khan
67d25028b2 bumped to version 12.3.0 2019-12-20 16:15:12 +05:50
Mangesh-Khairnar
328d5920bd fix(expense-claim): update status (#20033)
* fix(expense-claim): update status

* fix(expense-claim): compare using grandtotal precision
2019-12-20 15:26:49 +05:30
rohitwaghchaure
81d614b6bd Merge pull request #20007 from 0Pranav/appointment-schedulling-v12
fix: multiple issues with appointment schedulling
2019-12-20 15:17:41 +05:30
rohitwaghchaure
fdb7cec244 Merge pull request #20006 from 0Pranav/appointment-schedulling-v12
fix: multiple issues with appointment schedulling
2019-12-20 15:17:23 +05:30
thefalconx33
3aabeb88b4 fix: account manager doesn't have cancel submit perm for share transfer 2019-12-20 14:49:18 +05:30
rohitwaghchaure
b7fd9a652d Merge pull request #19929 from 0Pranav/delete-company-txn-query-v12
fix: replace sql with orm query for deleting company communications
2019-12-20 13:48:23 +05:30
rohitwaghchaure
63bc8f2d90 Merge pull request #20026 from rohitwaghchaure/subcontracting_issue_for_partial_purchase_receipt
fix: incorrect consumed qty for partial purchase receipt in subcontra…
2019-12-20 13:36:06 +05:30
rohitwaghchaure
b6d7fba936 Merge pull request #20025 from deepeshgarg007/gstr-2-fix-v12
fix: Tax amount not visible for some invoices
2019-12-20 13:35:41 +05:30
rohitwaghchaure
38540e85e8 Merge pull request #20029 from deepeshgarg007/gstr-2-fix-v12
fix: Tax amount not visible for some invoices
2019-12-20 13:35:14 +05:30
rohitwaghchaure
b3d97a560f Merge pull request #20027 from rohitwaghchaure/subcontracting_issue_for_partial_purchase_receipt_pre_release
fix: incorrect consumed qty for partial purchase receipt in subcontra…
2019-12-20 13:05:19 +05:30
Rohit Waghchaure
39436c6d38 fix: incorrect consumed qty for partial purchase receipt in subcontracting 2019-12-20 12:59:56 +05:30
Rohit Waghchaure
59cecb29a0 fix: incorrect consumed qty for partial purchase receipt in subcontracting 2019-12-20 12:56:01 +05:30
deepeshgarg007
55bf951ff5 fix: Tax amount not visible for some invoices 2019-12-20 12:43:55 +05:30
Deepesh Garg
4f543ce42a Merge pull request #20015 from marination/valuation_rate_company_hotfix
fix: Company None not found in get_valuation_rate
2019-12-20 11:56:58 +05:30
Deepesh Garg
8931b3d602 Merge pull request #19989 from deepeshgarg007/sales_register_upgrade_v12
fix: Update sales register report
2019-12-19 13:53:35 +05:30
marination
b13eebe657 fix: Company None not found in get_valuation_rate 2019-12-19 13:39:41 +05:30
thefalconx33
fcb296aafb fix: no role has cancelling permission for share transfer doctype 2019-12-19 13:19:50 +05:30
thefalconx33
5109b36689 fix: bad filter query 2019-12-19 13:12:37 +05:30
deepeshgarg007
2149d7ca4f feat: Dynamic filters for dimensions in budget variance report 2019-12-19 13:05:52 +05:30
0Pranav
220a208f4e fix: default timezone selection 2019-12-19 12:28:12 +05:30
0Pranav
0c8e46fdea fix: remove timezones in js 2019-12-19 12:25:29 +05:30
Deepesh Garg
ee4901f4a0 Merge pull request #19990 from 0Pranav/mapped-doc-for-customer-quotation-v12
fix: use open_mapped_doc instead of create_new_doc
2019-12-19 12:00:53 +05:30
Deepesh Garg
a0c847920f Merge pull request #19984 from 0Pranav/mapped-doc-for-customer-quotation-v12
fix: use open_mapped_doc instead of create_new_doc
2019-12-19 12:00:23 +05:30
rohitwaghchaure
86e5ff3cf9 Merge pull request #19997 from rohitwaghchaure/fixed_pricing_rule_issue_for_product_discount_v12_hotfix
fix: Pricing Rule Discount for Product
2019-12-19 11:12:15 +05:30
rohitwaghchaure
5dd7503516 Merge pull request #19995 from rohitwaghchaure/fixed_pricing_rule_issue_for_product_discount_pre_relesae
fix: Pricing Rule Discount for Product
2019-12-19 11:11:37 +05:30
deepeshgarg007
5cc0e08a41 fix: Use get_value instead of get_doc and formatting 2019-12-19 11:07:43 +05:30
Rohit Waghchaure
6b00d60b46 fix: Pricing Rule Discount for Product 2019-12-18 17:54:28 +05:30
Rohit Waghchaure
fecf5a9a15 fix: Pricing Rule Discount for Product 2019-12-18 17:48:39 +05:30
0Pranav
5b4050a4ff add link to appointment booking in sidebar 2019-12-18 16:30:54 +05:30
0Pranav
4d7862ef4c fix: defualt timezone not getting selected 2019-12-18 16:27:16 +05:30
0Pranav
6e41475612 fix : only set price list if it exists for customer 2019-12-18 16:05:59 +05:30
sahil28297
803e0ec27c Merge pull request #19988 from rohitwaghchaure/change_log_for_v12_3_0
feat: v12_3_0 change log
2019-12-18 15:54:37 +05:30
Rohit Waghchaure
b3addff99e v12_3_0 change log 2019-12-18 15:49:32 +05:30
deepeshgarg007
c6e098881f fix: Update sales register report 2019-12-18 15:43:14 +05:30
0Pranav
200ceb5352 use open_mapped_doc instead of create_new_doc 2019-12-18 12:34:19 +05:30
rohitwaghchaure
8c50f5c23f Merge pull request #19977 from Mangesh-Khairnar/fix-compensatory-off-pre
fix: compensatory off leave creation
2019-12-18 11:29:18 +05:30
rohitwaghchaure
6351a9395c Merge pull request #19975 from rohitwaghchaure/not_able_to_make_work_order_from_bom_v12_hotfix
fix: not able to make work order from BOM
2019-12-18 11:12:45 +05:30
Rushabh Mehta
fb2153c0eb Merge pull request #19966 from alyf-de/version-12-hotfix
fix(regional): Py3 compatibility for DATEV Export
2019-12-18 08:48:50 +05:30
Mangesh-Khairnar
86600ac8b9 fix: allow creation of additional leave ledger entry 2019-12-17 18:37:15 +05:30
Mangesh-Khairnar
b76a04b470 fix: compensatory leave request creation 2019-12-17 18:37:09 +05:30
Rohit Waghchaure
bb42fc615e fix: not able to make work order from BOM 2019-12-17 18:18:17 +05:30
rohitwaghchaure
80913994da Merge pull request #19974 from rohitwaghchaure/not_able_to_make_work_order_from_bom
fix: not able to make work order from BOM
2019-12-17 18:15:37 +05:30
Rohit Waghchaure
92ecdbe0c8 fix: not able to make work order from BOM 2019-12-17 18:13:54 +05:30
rohitwaghchaure
089dcaed70 Merge pull request #19969 from rohitwaghchaure/fixed_incorrect_outstanding_amount_showing_in_the_ap_ar_report
fix: incorrect outstanding amount showing in the AP/AR report
2019-12-17 17:15:28 +05:30
Mangesh-Khairnar
03a427a9e7 Merge pull request #19961 from Mangesh-Khairnar/compensatory-off-fix
fix: compensatory leave request creation
2019-12-17 13:32:05 +05:30
Rohit Waghchaure
764c2c7f17 fix: incorrect outstanding amount shwoing in the AP/AR report 2019-12-17 12:44:19 +05:30
Mangesh-Khairnar
b4d9b80fed fix: allow creation of additional leave ledger entry 2019-12-17 12:25:25 +05:30
Raffael Meyer
215274719e fix(regional): PY3 compatibility 2019-12-16 17:33:44 +01:00
rohitwaghchaure
c920efc156 Merge pull request #19963 from rohitwaghchaure/allow_overproduction_against_work_order_version_12_hotfix
fix: not allow to over production against work order
2019-12-16 18:04:27 +05:30
rohitwaghchaure
3b9fe1ae6f Merge pull request #19959 from rohitwaghchaure/fixed_incorrect_child_bom_fecthed
fix: incorrect child boms fetched
2019-12-16 17:00:08 +05:30
Rohit Waghchaure
c76c5e699b fix: now allow to over production against work order 2019-12-16 16:59:01 +05:30
Mangesh-Khairnar
ce6923ecb6 fix: compensatory leave request creation 2019-12-16 16:56:57 +05:30
0Pranav
03db85f3e7 fix: replace sql query by orm in delete_communications and added tests 2019-12-16 16:54:03 +05:30
Rohit Waghchaure
666fba94e2 fix: incorrect children boms fetched 2019-12-16 16:18:33 +05:30
rohitwaghchaure
48a8a40703 Merge pull request #19944 from nextchamp-saqib/gl-precision-hotfix
fix: gl entries doesn't filter based on debit precision
2019-12-16 15:29:04 +05:30
rohitwaghchaure
5646816282 Merge pull request #19870 from nextchamp-saqib/website-hotfix
fix: website showing disabled items in product list
2019-12-16 15:25:07 +05:30
thefalconx33
f8df3c7af2 fix: review changes 2019-12-16 15:03:27 +05:30
rohitwaghchaure
62d4dfa883 Merge pull request #19956 from nextchamp-saqib/pos-serial-no
fix: display serial no selection on adding items to cart
2019-12-16 14:54:23 +05:30
thefalconx33
b8f9fd023b fix: display serial no selection on adding items to cart 2019-12-16 14:49:59 +05:30
rohitwaghchaure
0df3c93737 Merge pull request #19936 from benknowles/patch-3
fix: task validation error when adding tasks to projects
2019-12-16 14:01:20 +05:30
rohitwaghchaure
e0e7dcd2f6 Merge pull request #19914 from nextchamp-saqib/cart-address-hotfix
fix: enable adding of address without enabling checkout feature
2019-12-16 13:52:48 +05:30
rohitwaghchaure
d5b1baed39 Merge pull request #19907 from 0Pranav/appointment-schedulling-v12
fix: change book-appointment route
2019-12-16 13:50:47 +05:30
rohitwaghchaure
800545ff5b Merge pull request #19947 from rohitwaghchaure/pricing_rule_not_working_for_product_discount_v12_hotfix
fix: pricing rule not working for product discount
2019-12-16 13:37:45 +05:30
rohitwaghchaure
388a177f75 Merge pull request #19939 from marination/item_manufacturer_table
fix(ui): Removed 'manufacturers' table from Item Master
2019-12-16 13:36:56 +05:30
Rohit Waghchaure
821166c628 fix: schedule date 2019-12-16 12:29:39 +05:30
marination
2b8df06f8e fix: Removed validation from non existent manufacturers table 2019-12-16 12:18:24 +05:30
Rohit Waghchaure
4e8e466a98 fix: pricing rule not working for production discount 2019-12-16 11:16:36 +05:30
Deepesh Garg
31d4482336 Merge pull request #19953 from surajshetty3416/fix-profit-and-loss-statement-version-12-hotfix
fix: Profit and Lost (financial statement) report
2019-12-15 20:23:05 +05:30
Suraj Shetty
5cd8c7c722 fix: Financial statement report
- Hidden column should note be considered in the report
- Remove hardcoded currency formatting
- Remove duplicate letterhead in the report
(print_template already adds one)
- Remove extra quotes from Total Amount text
2019-12-14 23:33:14 +05:30
Deepesh Garg
e14d9b5476 Merge pull request #19951 from deepeshgarg007/patch-and-address-fix-v12
fix: Add missing import
2019-12-14 23:03:27 +05:30
deepeshgarg007
6a8ff1bebe fix: Add missing import 2019-12-14 21:31:11 +05:30
rohitwaghchaure
a41d464198 Merge pull request #19942 from deepeshgarg007/pricing_rule_fix_v12
fix: Price rule filtering fix
2019-12-13 16:01:35 +05:30
thefalconx33
980793bde0 fix: gl entries doesn't filter based on debit precision 2019-12-13 15:42:34 +05:30
deepeshgarg007
b7329eac19 fix: Price rule filtering fix 2019-12-13 13:49:12 +05:30
marination
9ec5cb2570 fix: Removed 'manufacturers' table from Item Master 2019-12-13 13:12:10 +05:30
Deepesh Garg
44296a392d Merge pull request #19735 from marination/zero-division-v12-hotfix
fix: Division by zero error in Stock Entry
2019-12-13 09:24:31 +05:30
Ben Knowles
9097c7e11c fix: task validation error when adding tasks to projects
Related to PR #19919
2019-12-12 11:30:17 -06:00
Deepesh Garg
0256d7549c Merge pull request #19931 from deepeshgarg007/regional_address_fix_v12
fix: Get regional address details
2019-12-12 16:54:34 +05:30
marination
94d8b99ef9 fix: Distribute charges based on quantity if Total Basic Amount is Zero. 2019-12-12 16:51:11 +05:30
Deepesh Garg
3fe1335f7b Merge pull request #19926 from prssanna/file-upload-fix-v12
fix: Bank statement not getting attached in Bank Reconciliation
2019-12-12 15:19:36 +05:30
Deepesh Garg
dc7a4ac8af Merge pull request #19925 from rohitwaghchaure/not_able_to_submit_the_landed_cost_voucher_version_12
fix: not able to submit the landed cost voucher
2019-12-12 15:17:05 +05:30
Deepesh Garg
c0ff769214 Merge pull request #19924 from rohitwaghchaure/not_able_to_submit_the_landed_cost_voucher_version_12_hotfix
fix: not able to submit the landed cost voucher
2019-12-12 15:16:31 +05:30
deepeshgarg007
0a527b9f9a fix: Get regional address details fix 2019-12-12 15:13:30 +05:30
prssanna
e03871f9de fix: empty fname and fcontent of uploaded file 2019-12-12 12:22:03 +05:30
Rohit Waghchaure
c0286780bd fix: not able to submit the landed cost voucher 2019-12-12 12:14:26 +05:30
Rohit Waghchaure
9cc484650b fix: not able to submit the landed cost voucher 2019-12-12 12:13:40 +05:30
rohitwaghchaure
319f126258 Merge pull request #19901 from rohitwaghchaure/not_able_to_cancel_landed_cost_voucher_v12
fix: not able to cancel the landed cost voucher
2019-12-12 12:11:02 +05:30
0Pranav
234de12836 fix: add init files for book-appointments 2019-12-12 11:20:31 +05:30
rohitwaghchaure
4c19000ed9 Merge pull request #19918 from rohitwaghchaure/fixed_pricing_rule_working_on_other_items_v12_cherry_pick
fix: pricing rule not working
2019-12-12 10:20:05 +05:30
Deepesh Garg
a1651ca5f2 Merge pull request #19898 from hrwX/qms-int-fix-v12
fix: rename labels
2019-12-12 08:45:20 +05:30
Rohit Waghchaure
4d042cd81a Merge branch 'version-12' into fixed_pricing_rule_working_on_other_items_v12_cherry_pick 2019-12-11 22:53:27 +05:30
rohitwaghchaure
d72fae670a Merge pull request #19916 from rohitwaghchaure/fixed_pricing_rule_working_on_other_items
fix: pricing rule working on non pricing rule items
2019-12-11 22:18:07 +05:30
Rohit Waghchaure
d458e25dc5 fix: pricing rule working on non pricing rule items 2019-12-11 21:55:28 +05:30
Deepesh Garg
43474a3afa Merge pull request #19896 from hrwX/project-fix_v12
fix: Set project in child table via dashboard
2019-12-11 18:43:29 +05:30
Deepesh Garg
7daa2a2085 Merge pull request #19894 from marination/rounded_total_v12_hotfix
fix: Disable Rounded Total always showing field default value
2019-12-11 18:41:25 +05:30
Deepesh Garg
45075d8915 Merge pull request #19900 from rohitwaghchaure/not_able_to_cancel_landed_cost_voucher_v12_hotfix
fix: not able to cancel the landed cost voucher
2019-12-11 18:37:34 +05:30
thefalconx33
1de3040ecb fix: additional notes from Quotations not saved in SO 2019-12-11 18:11:48 +05:30
thefalconx33
af10f659d9 fix: enable address without checkout feature
* fix add address form country link field
2019-12-11 17:44:08 +05:30
thefalconx33
6f36691c64 fix: handle scenario with no condition 2019-12-11 16:10:56 +05:30
0Pranav
dfe629aff7 fix: change book-appointment route 2019-12-11 15:18:59 +05:30
Rohit Waghchaure
23bf2a6647 fix: not able to cancel the landed cost voucher 2019-12-11 13:52:47 +05:30
Rohit Waghchaure
b69cb8080c fix: not able to cancel the landed cost voucher 2019-12-11 13:52:18 +05:30
Himanshu Warekar
f23b5ed23b fix: rename labels 2019-12-11 13:10:31 +05:30
Himanshu Warekar
0a28387c70 fix: set project 2019-12-11 12:49:43 +05:30
marination
caae8c57bc fix: Disable Rounded Total always showing field default value 2019-12-11 12:18:51 +05:30
Nabin Hait
44ae135c36 Merge branch 'version-12' into version-12-hotfix 2019-12-11 09:12:10 +05:30
Deepesh Garg
47e786ef62 fix: Rounding Adjustment GL entry fix (#19839)
* fix: Rounding Adjustment GL entry fix

* fix: Spacing in tab

* fix: Comment fix
2019-12-11 09:06:37 +05:30
Deepesh Garg
f10be395c1 fix: NoneType' object has no attribute '__getitem_'_ (#19860) 2019-12-11 09:06:25 +05:30
rohitwaghchaure
ac967d09ec fix: Item-wise Sales History report not working (#19890) 2019-12-10 21:34:20 +05:30
Saqib
d1e8e8652f fix: incorrect account mapping for child companies (#19888)
* fix: incorrect account mapping for child companies on adding account to parent company

* Update account.py
2019-12-10 21:32:57 +05:30
Deepesh Garg
72649c207f feat(regional): Auto state wise taxation for GST India (#19877)
* feat(regional): Auto state wise taxation for GST India

* fix: Update gst category on addition of GSTIN

* fix: Codacy and travis fixes

* fix: Travis

* fix(test): Update GST category only if GSTIN field available

* fix: Test Cases

* fix: Do not skip accounts if place of supply is not present

* fix: Auto GST taxation for SEZ Party types

* fix: Automatic taxation for multi state

* fix: Codacy and travis fixes

* fix: Auto GST template selection in Sales Order

* fix: Move inter state check and source state to tax category

* fix: Remove unique check from tax template

* fix: Remove unique check from tax template

* fix: Address fetching logic in Sales

* fix: fecth tax template on company address change

* fix: fetch company gstin on address change

* fix: company_gstin set value fix

* fix: Mutiple fixes and code refactor

* fix: Add missing semicolon

* fix: Company address fetching in sales invoice

* fix: Remove print statement

* fix: Import functools

* fix: Naming fixes and code cleanup

* fix: Update patches

* fix: Remove changes in patches.txt

* fix: Iteritems compatibility for python 3
2019-12-10 15:54:29 +05:30
Deepesh Garg
d06b685fdf fix: Append expense account only if expense account exists (#19881) 2019-12-10 12:15:06 +05:30
marination
6411a56cdc fix: Changed check condition and added test 2019-12-09 21:36:02 +05:30
Saqib
34b3b04fb0 fix: error message displays asset category as None (#19874)
* fix: error message displays asset category as None

* fix: asset gl_entries doesn't considers asset category's cwip account
2019-12-09 19:06:14 +05:30
Deepesh Garg
b1a2a16f43 Merge pull request #19868 from nextchamp-saqib/report-col-hotfix
fix: column data not visible after manual selection of columns
2019-12-09 17:57:41 +05:30
thefalconx33
f092e68a58 fix: website showing disabled items in product list 2019-12-09 17:03:32 +05:30
thefalconx33
6d497ccb4c fix: column data not visible after manual selection of columns 2019-12-09 15:24:39 +05:30
Deepesh Garg
a7b97f7bac Merge pull request #19867 from nextchamp-saqib/cart-fix-hotfix
fix: Error while placing order of cart items added yesterday
2019-12-09 15:23:04 +05:30
thefalconx33
f40d3bd10f fix: due date before posting date for items added to cart yesterday 2019-12-09 14:07:25 +05:30
Deepesh Garg
1e2be32860 fix: Consistency in button positions in Sales Order and Purchase Order (#19834) 2019-12-09 13:04:58 +05:30
Deepesh Garg
6aec9e32d4 fix: Rounding Adjustment GL entry fix (#19839)
* fix: Rounding Adjustment GL entry fix

* fix: Spacing in tab

* fix: Comment fix
2019-12-09 13:03:02 +05:30
Deepesh Garg
59cc0e5029 fix: NoneType' object has no attribute '__getitem_'_ (#19860) 2019-12-09 11:28:35 +05:30
Deepesh Garg
851f39cee1 Merge pull request #19837 from deepeshgarg007/gst_1_validation_msg_v12
fix: Validation msg fix in GSTR-1 report
2019-12-07 19:53:03 +05:30
rohitwaghchaure
6822a30f8c Merge pull request #19850 from rohitwaghchaure/fixed_timsheet_overlap_issue_v12_hotfix
fix: timesheet overlap error
2019-12-07 14:10:54 +05:30
Rohit Waghchaure
495ba1618b fix: timsheet overlap error 2019-12-07 13:22:08 +05:30
deepeshgarg007
778d7595aa fix: Add missing semicolon 2019-12-06 20:00:56 +05:30
deepeshgarg007
a40dbd0384 fix: Validation msg fix in GSTR-1 report 2019-12-06 19:46:32 +05:30
Deepesh Garg
80dfb9f834 Merge pull request #19835 from deepeshgarg007/accounts_payable_terms_v12
feat: Accounts Payable report based on payment terms
2019-12-06 19:41:27 +05:30
deepeshgarg007
dabb303358 feat: Accounts Payable report based on payment terms 2019-12-06 17:52:48 +05:30
Pranav Nachnekar
d16ef54665 fix: query for finding lost quotation (#19801)
* fix:query for finding lost quotation

* Update opportunity.py
2019-12-04 15:31:25 +05:30
Nabin Hait
dc248b9458 optimize: Optimization of Receivable report filtered based on sales person (#19797) 2019-12-04 15:30:39 +05:30
Nabin Hait
bf0f26b4a4 fix: Service start and end date validation for deferred accounting (#19806) 2019-12-04 15:29:54 +05:30
Mangesh-Khairnar
929fd4ce47 enhancement(fixed-asset-register): add date filter (#19804)
* feat: add date filter in the fixed asset register

* fix: remove function from keyword argument
2019-12-04 14:10:41 +05:30
Deepesh Garg
81c895b21e Merge pull request #19793 from nabinhait/ar-summary-based-on-terms-v12
feat: Receivable / payable summary based on payment terms
2019-12-04 11:23:31 +05:30
Shivam Mishra
27a21f80d7 feat: allow searching from meta fields (#19725)
* feat: allow searching from meta fields

* feat: remove description in query based on number of items
2019-12-03 17:26:50 +05:30
sahil28297
aa7085e11c fix(patch): set proper tax_type based on company and set proper account if not already present (#19788) 2019-12-03 17:07:26 +05:30
Nabin Hait
6e5363ba48 feat: Receivable / payable summary based on payment terms 2019-12-03 16:58:02 +05:30
Deepesh Garg
53746636c3 fix: Party name field in trial balacne for party report (#19790) 2019-12-03 16:30:09 +05:30
Deepesh Garg
485d48c101 fix: Unable to see parties with negative balance in AR/AP Summary (#19777) 2019-12-03 15:12:28 +05:30
Marica
0e1ef35968 fix: Item qty cannot be zero in Purchase Receipt (#19780) 2019-12-03 12:59:15 +05:30
gavin
35effe9be0 fix: AttributeError on new Student creation (#19787) 2019-12-03 12:54:18 +05:30
Shivam Mishra
648d6e46f3 fix: query for item group listing (#19785) 2019-12-03 12:52:58 +05:30
Nabin Hait
d6d9a3ddd7 Update employee.py 2019-12-03 12:52:12 +05:30
Deepesh Garg
18f05db19a Merge pull request #19763 from Mangesh-Khairnar/fix-pr-creation-so
fix(sales-order): allow payment request creation for so that are not billed
2019-12-03 10:42:53 +05:30
Rucha Mahabal
586fecfe73 fix: render_template for subject in Email Campaign (#19771) 2019-12-02 16:25:29 +05:30
sahil28297
14018b3dea bumped to version 12.2.2 2019-12-02 13:05:27 +05:30
Deepesh Garg
1c196f958f Merge pull request #19768 from deepeshgarg007/avaiable_stock_for_v12
fix: Available stock for packing item report
2019-12-02 11:57:45 +05:30
rohitwaghchaure
91f2cfb999 Merge pull request #19769 from rohitwaghchaure/sales_invoice_none_type_error_serial_no_validation
fix: Serial no validation against sales invoice
2019-12-02 09:46:23 +05:30
deepeshgarg007
c0a0331570 fix: Validation msg 2019-12-02 09:43:11 +05:30
deepeshgarg007
4ceba43e43 fix: Serial no validation against sales invoice 2019-12-02 09:43:04 +05:30
deepeshgarg007
9b64e2e24c fix: Available stock for packing item report 2019-12-01 22:20:18 +05:30
Deepesh Garg
da5e227ad6 fix: Post GL entry fix for asset (#19752) 2019-12-01 10:06:31 +05:30
Mangesh-Khairnar
4f95e5d092 fix: show create payment request for so that are not billed 2019-11-30 20:31:18 +05:30
Deepesh Garg
6a8fd0102f fix: Serial no validation against sales invoice (#19749)
* fix: Serial no validation against sales invoice

* fix: Validation msg
2019-11-29 18:48:30 +05:30
Suraj Shetty
2b172ec4b4 fix: valuation of "finished good" item in purchase receipt (#19745)
* fix: Remove redundant purchase orders and unwanted condition

* fix: [WIP] Purchase receipt value

* fix: Add raw material cost based on transfered raw material

* fix: get_qty_to_be_received

* fix: Remove debugger statement

* fix: Reset rm_supp_cost before setting subcontracted raw_materials

* test: Fix and modify tests for backflush_based_on_stock_entry

* fix: Add non stock items to Purchase Receipt from Purchase Order

* fix: Ignore valuation rate check for non stock raw material

* fix: Rename check all rows

* fix: Remove amount from test

* test: Fix item rate error

* fix: handling of serial nos in backflush

* fix: Add serial no. of raw materials

* fix: [WIP] Handle Batch nos for purchase reciept backflushed raw material

* fix: Raw material batch number selection in purchase receipt

* Update test_purchase_order.py
2019-11-29 16:59:21 +05:30
Marica
5d2ad7fc38 fix: UOM was not fetching in purchase invoice (#19732) (#19737)
* fix: UOM was not fetching in purchase invoice

* fix: Changes requested

Co-authored-by: Marica <maricadsouza221197@gmail.com>
2019-11-28 20:00:51 +05:30
rohitwaghchaure
3347473aa1 fix: removed stock value and account balance out of sync validation (#19728) 2019-11-28 20:00:41 +05:30
Rohit Waghchaure
7f951b5595 fix: revert value out of sync feature 2019-11-28 20:00:33 +05:30
Marica
208c69f196 fix: Permission issue in Stock Entry (#19739) 2019-11-28 19:39:55 +05:30
Marica
32b69bf122 fix: UOM was not fetching in purchase invoice (#19732) (#19737)
* fix: UOM was not fetching in purchase invoice

* fix: Changes requested

Co-authored-by: Marica <maricadsouza221197@gmail.com>
2019-11-28 19:03:14 +05:30
Marica
b1fac1817c fix: Validation for Suppliers in SO to PO (#19700)
- Check if there is a Supplier against atleast one item in Sales Order
- Validation message earlier was vague
2019-11-28 18:23:11 +05:30
Marica
6516358a71 fix: Changed type of column 'serial_no' in Stock Ledger Entry (#19704) 2019-11-28 18:20:53 +05:30
marination
c6e2087673 fix: Division by zero error in Stock Entry 2019-11-28 16:54:58 +05:30
Shivam Mishra
d8469a7bfa fix: handle None case for get_shipping_amount_from_rules (#19724) 2019-11-28 16:47:14 +05:30
Marica
cf645aceae chore: Added Quick Stock Balance to Stock Module (#19727)
- Also 'Stock Balance Report' button no longer primary button
2019-11-28 16:44:56 +05:30
rohitwaghchaure
3dd72e238f fix: removed stock value and account balance out of sync validation (#19728) 2019-11-28 16:44:05 +05:30
Deepesh Garg
b74ce74ec9 Merge pull request #19718 from deepeshgarg007/status_fix_v12
fix: Path for quotation expiry method in hooks
2019-11-28 12:24:20 +05:30
deepeshgarg007
074aaa6005 fix: Path for quotation expiry method in hooks 2019-11-28 10:31:11 +05:30
Marica
9d5f43f4f0 fix: get_batch_qty_and_serial_no() requires argument 'stock_qty' (#19694) 2019-11-27 15:50:45 +05:30
rohitwaghchaure
7522aadc6e Merge pull request #19697 from rohitwaghchaure/dont_stop_submitting_entry_due_to_mismatch_issue
fix: revert value out of sync feature
2019-11-27 12:32:04 +05:30
rohitwaghchaure
326fdcb454 Merge pull request #19687 from deepeshgarg007/sales_invoice_fix_develop
fix: Serial no validation against sales invoice
2019-11-27 11:36:10 +05:30
Rohit Waghchaure
c41addec96 fix: revert value out of sync feature 2019-11-27 08:49:08 +05:30
deepeshgarg007
defed15528 fix: Validation msg 2019-11-26 16:12:29 +05:30
deepeshgarg007
cbc29989fe fix: Serial no validation against sales invoice 2019-11-26 15:13:23 +05:30
7348 changed files with 931088 additions and 2742765 deletions

View File

@@ -1,12 +0,0 @@
reviews:
auto_review:
ignore_title_keywords:
- "sync translations"
- "update POT file"
- "style: "
review_status: false
poem: false
collapse_walkthrough: true
sequence_diagrams: false
changed_files_summary: false
high_level_summary: false

View File

@@ -1,21 +0,0 @@
# Root editor config file
root = true
# Common settings
[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8
# python, js indentation settings
[{*.py,*.js,*.vue,*.css,*.scss,*.html}]
indent_style = tab
indent_size = 4
max_line_length = 110
# JSON files - mostly doctype schema files
[{*.json}]
insert_final_newline = false
indent_style = space
indent_size = 1

View File

@@ -2,32 +2,59 @@
"env": {
"browser": true,
"node": true,
"es2022": true
"es6": true
},
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module"
},
"extends": "eslint:recommended",
"rules": {
"indent": "off",
"brace-style": "off",
"no-mixed-spaces-and-tabs": "off",
"no-useless-escape": "off",
"space-unary-ops": ["error", { "words": true }],
"linebreak-style": "off",
"quotes": ["off"],
"semi": "off",
"camelcase": "off",
"no-unused-vars": "off",
"no-console": ["warn"],
"no-extra-boolean-cast": ["off"],
"no-control-regex": ["off"]
"indent": [
"error",
"tab",
{ "SwitchCase": 1 }
],
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"off"
],
"semi": [
"warn",
"always"
],
"camelcase": [
"off"
],
"no-unused-vars": [
"warn"
],
"no-redeclare": [
"warn"
],
"no-console": [
"warn"
],
"no-extra-boolean-cast": [
"off"
],
"no-control-regex": [
"off"
],
"spaced-comment": [
"warn"
],
"no-trailing-spaces": [
"warn"
]
},
"root": true,
"globals": {
"frappe": true,
"Vue": true,
"SetVueGlobals": true,
"erpnext": true,
"hub": true,
"$": true,
@@ -59,15 +86,12 @@
"cur_page": true,
"cur_list": true,
"cur_tree": true,
"cur_pos": true,
"msg_dialog": true,
"is_null": true,
"in_list": true,
"has_common": true,
"posthog": true,
"has_words": true,
"validate_email": true,
"open_web_template_values_editor": true,
"get_number_format": true,
"format_number": true,
"format_currency": true,
@@ -116,15 +140,9 @@
"Chart": true,
"Cypress": true,
"cy": true,
"describe": true,
"expect": true,
"it": true,
"context": true,
"before": true,
"beforeEach": true,
"onScan": true,
"extend_cscript": true,
"localforage": true,
"Plaid": true
"beforeEach": true
}
}

37
.flake8
View File

@@ -1,37 +0,0 @@
[flake8]
ignore =
E121,
E126,
E127,
E128,
E203,
E225,
E226,
E231,
E241,
E251,
E261,
E265,
E302,
E303,
E305,
E402,
E501,
E741,
W291,
W292,
W293,
W391,
W503,
W504,
F403,
B007,
B950,
W191,
E124, # closing bracket, irritating while writing QB code
E131, # continuation line unaligned for hanging indent
E123, # closing bracket does not match indentation of opening bracket's line
E101, # ensured by use of black
max-line-length = 200
exclude=.github/helper/semgrep_rules

View File

@@ -1,53 +0,0 @@
# Since version 2.23 (released in August 2019), git-blame has a feature
# to ignore or bypass certain commits.
#
# This file contains a list of commits that are not likely what you
# are looking for in a blame, such as mass reformatting or renaming.
# You can set this file as a default ignore file for blame by running
# the following command.
#
# $ git config blame.ignoreRevsFile .git-blame-ignore-revs
# Replace use of Class.extend with native JS class
1fe891b287a1b3f225d29ee3d07e7b1824aba9e7
# This commit just changes spaces to tabs for indentation in some files
5f473611bd6ed57703716244a054d3fb5ba9cd23
# Whitespace fix throughout codebase
4551d7d6029b6f587f6c99d4f8df5519241c6a86
b147b85e6ac19a9220cd1e2958a6ebd99373283a
# sort and cleanup imports
915b34391c2066dfc83e60a5813c5a877cebe7ac
# removing six compatibility layer
8fe5feb6a4372bf5f2dfaf65fca41bbcc25c8ce7
# bulk format python code with black
494bd9ef78313436f0424b918f200dab8fc7c20b
# bulk format python code with black
baec607ff5905b1c67531096a9cf50ec7ff00a5d
# bulk refactor with sourcery
eb9ee3f79b94e594fc6dfa4f6514580e125eee8c
# js formatting
ec74a5e56617bbd76ac402451468fd4668af543d
# ruff formatting
a308792ee7fda18a681e9181f4fd00b36385bc23
# noisy typing refactoring of get_item_details
7b7211ac79c248a79ba8a999ff34e734d874c0ae
d827ed21adc7b36047e247cbb0dc6388d048a7f9
# `frappe.flags.in_test` => `frappe.in_test`
7a482a69985c952de0e8193c9d4e086aee65ee6d
# these commits actually changed something valuable
# but they have a lot of whitespace changes that make blame noisy
# PR: https://github.com/frappe/erpnext/pull/49816
3ffd50c772735877b330d010c1058f623da8721d
0e8f8677b8eb31e7834f72d1c6314d3c3f392ca6

View File

@@ -1,70 +1,36 @@
### Introduction (For First-Time Contributors)
### Introduction (first timers)
Thank you for your interest in raising an issue with ERPNext. An issue can be either a bug report or a feature request.
Thank you for your interest in raising an Issue with ERPNext. An Issue could mean a bug report or a request for a missing feature. By raising a bug report, you are contributing to the development of ERPNext and this is the first step of participating in the community. Bug reports are very helpful for developers as they quickly fix the issue before other users start facing it.
By reporting bugs, you contribute directly to improving ERPNext. Bug reports help developers identify and fix issues quickly before they affect more users.
Feature requests are also a great way to take the product forward. New ideas can come in any user scenario and the issue list also acts a roadmap of future features.
Feature requests are also valuable. They help shape the future of the product by introducing new ideas and improvements based on real-world use cases.
When you are raising an Issue, you should keep a few things in mind. Remember that the developer does not have access to your machine so you must give all the information you can while raising an Issue. If you are suggesting a feature, you should be very clear about what you want.
When raising an issue, keep in mind that developers do not have access to your environment. Therefore, provide as much relevant information as possible.
If you are suggesting a feature, clearly describe what you expect and how it should behave.
> ⚠️ The issue tracker is not the right place for general questions or discussions.
> Please use the forum instead: https://discuss.frappe.io/c/erpnext/6
---
The Issue list is not the right place to ask a question or start a general discussion. If you want to do that , then the right place is the forum [https://discuss.erpnext.com](https://discuss.erpnext.com).
### Reply and Closing Policy
If your issue is unclear or does not meet the guidelines, it may be closed.
If that happens, please provide the requested information and reopen the issue.
---
If your issue is not clear or does not meet the guidelines, then it will be closed. If it is closed, please supply the information asked and re-open it.
### General Issue Guidelines
1. **Search existing issues:**
Before creating a new issue, check if it already exists. You can support existing issues with a 👍 or contribute additional details or mockups.
2. **Report issues separately:**
Do not combine multiple unrelated issues into a single report.
3. **Be concise:**
Avoid long explanations. Use bullet points and screenshots where possible.
---
1. **Search existing Issues:** Before raising a Issue, search if it has been raised before. Maybe add a 👍 or give additional help by creating a mockup if it is not already created.
1. **Report each issue separately:** Don't club multiple, unreleated issues in one note.
1. **Brief:** Please don't include long explanations. Use screenshots and bullet points instead of descriptive paragraphs.
### Bug Report Guidelines
1. **Steps to reproduce:**
Clearly list the steps required to reproduce the issue. If the issue cannot be reproduced, it cannot be fixed.
2. **Version number:**
Include the ERPNext version. The issue may already be fixed in a newer release.
3. **Clear title:**
Use a descriptive title (e.g., "Unable to submit Purchase Order without Basic Rate" instead of "Cannot submit").
4. **Screenshots:**
Add screenshots or screen recordings (e.g., `.gif`) to illustrate the issue.
---
1. **Steps to Reproduce:** The bug report must have a list of steps needed to reproduce a bug. If we cannot reproduce it, then we cannot solve it.
1. **Version Number:** Please add the version number in your report. Often a bug is fixed in the latest version
1. **Clear Title:** Add a clear subject to your bug report like "Unable to submit Purchase Order without Basic Rate" instead of just "Cannot Submit"
1. **Screenshots:** Screenshots are a great way of communicating the issues. Try adding annotations or using LiceCAP to take a screencast in `gif`.
### Feature Request Guidelines
1. **Clarity:**
Clearly describe the expected behavior. Avoid vague statements.
1. **Clarity:** Clearly specify how do you want the feature to behave. Don't just say "I would like multiple PDF formats", say that "Ability to add multiple print formats for customers with different languages".
1. **Solution:** Try and identify how the feature should look like.
1. **Mockups:** Mockups are a great way to explain your requirement.
2. **Proposed solution:**
Suggest how the feature should work.
### What if my Issue is closed
3. **Mockups:**
Provide mockups or examples whenever possible.
---
### What if my issue is closed?
Don't worry. Review the feedback, provide the required information, and reopen the issue.
Don't worry, take the feedback, supply the correct information and re-open it!

47
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,47 @@
---
name: Bug report
about: Report a bug encountered while using ERPNext
labels: bug
---
<!--
Welcome to ERPNext issue tracker! Before creating an issue, please heed the following:
1. This tracker should only be used to report bugs and request features / enhancements to ERPNext
- For questions and general support, checkout the manual https://erpnext.com/docs/user/manual/en or use https://discuss.erpnext.com
- For documentation issues, refer to https://github.com/frappe/erpnext_com
2. Use the search function before creating a new issue. Duplicates will be closed and directed to
the original discussion.
3. When making a bug report, make sure you provide all required information. The easier it is for
maintainers to reproduce, the faster it'll be fixed.
4. If you think you know what the reason for the bug is, share it with us. Maybe put in a PR 😉
-->
## Description of the issue
## Context information (for bug reports)
**Output of `bench version`**
```
(paste here)
```
## Steps to reproduce the issue
1.
2.
3.
### Observed result
### Expected result
### Stacktrace / full error message
```
(paste here)
```
## Additional information
OS version / distribution, `ERPNext` install method, etc.

View File

@@ -1,89 +0,0 @@
name: Bug Report
description: Report a bug encountered while using ERPNext
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
Welcome to ERPNext issue tracker! Before creating an issue, please heed the following:
1. This tracker should only be used to report bugs and request features / enhancements to ERPNext
- For questions and general support, checkout the [user manual](https://docs.erpnext.com/) or use [forum](https://discuss.frappe.io/c/erpnext/6)
- For documentation issues, propose edit on [documentation site](https://docs.erpnext.com/) directly.
2. When making a bug report, make sure you provide all required information. The easier it is for
maintainers to reproduce, the faster it'll be fixed.
3. If you think you know what the reason for the bug is, share it with us. Maybe put in a PR 😉
- type: textarea
id: bug-info
attributes:
label: Information about bug
description: Also tell us, what did you expect to happen?
placeholder: Please provide as much information as possible.
validations:
required: true
- type: dropdown
id: module
attributes:
label: Module
description: Select affected module of ERPNext.
multiple: true
options:
- accounts
- stock
- buying
- selling
- ecommerce
- manufacturing
- HR
- projects
- support
- CRM
- assets
- integrations
- quality
- regional
- portal
- agriculture
- education
- non-profit
- other
validations:
required: true
- type: textarea
id: exact-version
attributes:
label: Version
description: Share exact version number of Frappe and ERPNext you are using.
placeholder: |
Frappe version -
ERPNext version -
validations:
required: true
- type: dropdown
id: install-method
attributes:
label: Installation method
options:
- docker
- easy-install
- manual install
- FrappeCloud
validations:
required: false
- type: textarea
id: logs
attributes:
label: Relevant log output / Stack trace / Full Error Message.
description: Please copy and paste any relevant log output. This will be automatically formatted.
render: shell
- type: markdown
attributes:
value: |
By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/frappe/erpnext/blob/develop/CODE_OF_CONDUCT.md)

View File

@@ -1,5 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: Community Forum
url: https://discuss.frappe.io/c/erpnext/6
about: For general QnA, discussions and community help.

View File

@@ -1,37 +1,22 @@
---
name: Feature request
about: Suggest an idea or enhancement for ERPNext
title: ''
about: Suggest an idea to improve ERPNext
labels: feature-request
assignees: ''
---
<!--
Welcome to ERPNext issue tracker! Before creating an issue, please heed the following:
1. This tracker should only be used to report bugs and request features / enhancements to ERPNext
- For questions and general support, checkout the manual https://docs.erpnext.com or use https://discuss.frappe.io/c/erpnext/6
- For questions and general support, checkout the manual https://erpnext.com/docs/user/manual/en or use https://discuss.erpnext.com
- For documentation issues, refer to https://github.com/frappe/erpnext_com
2. Use the search function before creating a new issue. Duplicates will be closed and directed to
the original discussion.
3. When making a feature request, make sure to be as verbose as possible. The better you convey your message, the greater the drive to make it happen.
Please keep in mind that we get many requests and we can't possibly work on all of them, we prioritize development based on the goals of the product and organization. Feature requests are still welcome as it helps us in research when we do decide to work on the requested feature.
If you're in urgent need of a feature, please try the following channels to get paid developments done quickly:
1. Certified ERPNext partners: https://erpnext.com/partners
2. Developer community on ERPNext forums: https://discuss.frappe.io/c/framework/5
3. Telegram group for ERPNext/Frappe development work: https://t.me/erpnext_opps
3. When making a feature request, make sure to be as verbose as possible. The better you convey your message, the greater the drive to make it happen.
-->
## Before Submitting
- [ ] I searched existing issues and confirmed this is not a duplicate
- [ ] This is a feature request, not a bug or support question
- [ ] For support: https://discuss.frappe.io/c/erpnext/6
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. As a [role], I have to [painful task] because [missing feature].
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
@@ -39,17 +24,5 @@ A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Impact**
<!-- Check one: -->
- [ ] Blocks critical workflow — no viable workaround
- [ ] Significant friction — workaround exists but is painful
- [ ] Nice to have — minor improvement
**Additional context**
Add any other context or screenshots about the feature request here.
**Environment**
- ERPNext Version: <!-- Find this in Help > About, e.g. v15.12.0 -->
- Frappe Version: <!-- Find this in Help > About, e.g. v15.10.0 -->
- Deployment: <!-- Frappe Cloud / Self-hosted / ERPNext Cloud -->

View File

@@ -0,0 +1,17 @@
---
name: Question about using ERPNext
about: This is not the appropriate channel
labels: invalid
---
Please post on our forums:
for questions about using `ERPNext`: https://discuss.erpnext.com
for questions about using the `Frappe Framework`: https://discuss.frappe.io
for questions about using `bench`, probably the best place to start is the [bench repo](https://github.com/frappe/bench)
For documentation issues, use the [ERPNext Documentation](https://erpnext.com/docs/) or [Frappe Framework Documentation](https://frappe.io/docs/user/en) or the [developer cheetsheet](https://github.com/frappe/frappe/wiki/Developer-Cheatsheet)
> **Posts that are not bug reports or feature requests will not be addressed on this issue tracker.**

View File

@@ -1,74 +0,0 @@
[flake8]
ignore =
B007,
B009,
B010,
B950,
E101,
E111,
E114,
E116,
E117,
E121,
E122,
E123,
E124,
E125,
E126,
E127,
E128,
E131,
E201,
E202,
E203,
E211,
E221,
E222,
E223,
E224,
E225,
E226,
E228,
E231,
E241,
E242,
E251,
E261,
E262,
E265,
E266,
E271,
E272,
E273,
E274,
E301,
E302,
E303,
E305,
E306,
E402,
E501,
E502,
E701,
E702,
E703,
E741,
F403,
W191,
W291,
W292,
W293,
W391,
W503,
W504,
E711,
E129,
F841,
E713,
E712,
B023,
B028
max-line-length = 200
exclude=.github/helper/semgrep_rules,test_*.py

View File

@@ -1,65 +0,0 @@
import sys
from urllib.parse import urlparse
import requests
WEBSITE_REPOS = [
"erpnext_com",
"frappe_io",
]
DOCUMENTATION_DOMAINS = [
"docs.erpnext.com",
"docs.frappe.io",
"frappeframework.com",
]
def is_valid_url(url: str) -> bool:
parts = urlparse(url)
return all((parts.scheme, parts.netloc, parts.path))
def is_documentation_link(word: str) -> bool:
if not word.startswith("http") or not is_valid_url(word):
return False
parsed_url = urlparse(word)
if parsed_url.netloc in DOCUMENTATION_DOMAINS:
return True
if parsed_url.netloc == "github.com":
parts = parsed_url.path.split("/")
if len(parts) == 5 and parts[1] == "frappe" and parts[2] in WEBSITE_REPOS:
return True
return False
def contains_documentation_link(body: str) -> bool:
return any(is_documentation_link(word) for line in body.splitlines() for word in line.split())
def check_pull_request(number: str) -> "tuple[int, str]":
response = requests.get(f"https://api.github.com/repos/frappe/erpnext/pulls/{number}")
if not response.ok:
return 1, "Pull Request Not Found! ⚠️"
payload = response.json()
title = (payload.get("title") or "").lower().strip()
head_sha = (payload.get("head") or {}).get("sha")
body = (payload.get("body") or "").lower()
if not title.startswith("feat") or not head_sha or "no-docs" in body or "backport" in body:
return 0, "Skipping documentation checks... 🏃"
if contains_documentation_link(body):
return 0, "Documentation Link Found. You're Awesome! 🎉"
return 1, "Documentation Link Not Found! ⚠️"
if __name__ == "__main__":
exit_code, message = check_pull_request(sys.argv[1])
print(message)
sys.exit(exit_code)

View File

@@ -1,78 +0,0 @@
#!/bin/bash
set -e
cd ~ || exit
sudo apt update
sudo apt remove mysql-server mysql-client
sudo apt install libcups2-dev redis-server mariadb-client libmariadb-dev
pip install frappe-bench
githubbranch=${GITHUB_BASE_REF:-${GITHUB_REF##*/}}
frappeuser=${FRAPPE_USER:-"frappe"}
frappecommitish=${FRAPPE_BRANCH:-$githubbranch}
mkdir frappe
pushd frappe
git init
git remote add origin "https://github.com/${frappeuser}/frappe"
git fetch origin "${frappecommitish}" --depth 1
git checkout FETCH_HEAD
popd
bench init --skip-assets --frappe-path ~/frappe --python "$(which python)" frappe-bench
mkdir ~/frappe-bench/sites/test_site
if [ "$DB" == "mariadb" ];then
cp -r "${GITHUB_WORKSPACE}/.github/helper/site_config_mariadb.json" ~/frappe-bench/sites/test_site/site_config.json
else
cp -r "${GITHUB_WORKSPACE}/.github/helper/site_config_postgres.json" ~/frappe-bench/sites/test_site/site_config.json
fi
if [ "$DB" == "mariadb" ];then
mariadb --host 127.0.0.1 --port 3306 -u root -proot -e "SET GLOBAL character_set_server = 'utf8mb4'"
mariadb --host 127.0.0.1 --port 3306 -u root -proot -e "SET GLOBAL collation_server = 'utf8mb4_unicode_ci'"
mariadb --host 127.0.0.1 --port 3306 -u root -proot -e "CREATE USER 'test_frappe'@'localhost' IDENTIFIED BY 'test_frappe'"
mariadb --host 127.0.0.1 --port 3306 -u root -proot -e "CREATE DATABASE test_frappe"
mariadb --host 127.0.0.1 --port 3306 -u root -proot -e "GRANT ALL PRIVILEGES ON \`test_frappe\`.* TO 'test_frappe'@'localhost'"
mariadb --host 127.0.0.1 --port 3306 -u root -proot -e "FLUSH PRIVILEGES"
fi
if [ "$DB" == "postgres" ];then
echo "travis" | psql -h 127.0.0.1 -p 5432 -c "CREATE DATABASE test_frappe" -U postgres;
echo "travis" | psql -h 127.0.0.1 -p 5432 -c "CREATE USER test_frappe WITH PASSWORD 'test_frappe'" -U postgres;
fi
install_whktml() {
wget -O /tmp/wkhtmltox.deb https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-2/wkhtmltox_0.12.6.1-2.jammy_amd64.deb
sudo apt install /tmp/wkhtmltox.deb
}
install_whktml &
wkpid=$!
cd ~/frappe-bench || exit
sed -i 's/watch:/# watch:/g' Procfile
sed -i 's/schedule:/# schedule:/g' Procfile
sed -i 's/socketio:/# socketio:/g' Procfile
sed -i 's/redis_socketio:/# redis_socketio:/g' Procfile
bench get-app payments --branch develop
bench get-app erpnext "${GITHUB_WORKSPACE}"
if [ "$TYPE" == "server" ]; then bench setup requirements --dev; fi
wait $wkpid
bench start &>> ~/frappe-bench/bench_start.log &
CI=Yes bench build --app frappe &
bench --site test_site reinstall --yes

View File

@@ -1,17 +0,0 @@
{
"db_host": "127.0.0.1",
"db_port": 3306,
"db_name": "test_frappe",
"db_password": "test_frappe",
"auto_email_id": "test@example.com",
"mail_server": "smtp.example.com",
"mail_login": "test@example.com",
"mail_password": "test",
"admin_password": "admin",
"use_mysqlclient": 1,
"root_login": "root",
"root_password": "root",
"host_name": "http://test_site:8000",
"install_apps": ["payments", "erpnext"],
"throttle_user_limit": 100
}

View File

@@ -1,18 +0,0 @@
{
"db_host": "127.0.0.1",
"db_port": 5432,
"db_name": "test_frappe",
"db_password": "test_frappe",
"db_type": "postgres",
"allow_tests": true,
"auto_email_id": "test@example.com",
"mail_server": "smtp.example.com",
"mail_login": "test@example.com",
"mail_password": "test",
"admin_password": "admin",
"root_login": "postgres",
"root_password": "travis",
"host_name": "http://test_site:8000",
"install_apps": ["erpnext"],
"throttle_user_limit": 100
}

View File

@@ -1,68 +0,0 @@
import re
import sys
errors_encounter = 0
pattern = re.compile(
r"_\(([\"']{,3})(?P<message>((?!\1).)*)\1(\s*,\s*context\s*=\s*([\"'])(?P<py_context>((?!\5).)*)\5)*(\s*,(\s*?.*?\n*?)*(,\s*([\"'])(?P<js_context>((?!\11).)*)\11)*)*\)"
)
words_pattern = re.compile(r"_{1,2}\([\"'`]{1,3}.*?[a-zA-Z]")
start_pattern = re.compile(r"_{1,2}\([f\"'`]{1,3}")
f_string_pattern = re.compile(r"_\(f[\"']")
starts_with_f_pattern = re.compile(r"_\(f")
# skip first argument
files = sys.argv[1:]
files_to_scan = [_file for _file in files if _file.endswith((".py", ".js"))]
for _file in files_to_scan:
with open(_file) as f:
print(f"Checking: {_file}")
file_lines = f.readlines()
for line_number, line in enumerate(file_lines, 1):
if "frappe-lint: disable-translate" in line:
continue
start_matches = start_pattern.search(line)
if start_matches:
starts_with_f = starts_with_f_pattern.search(line)
if starts_with_f:
has_f_string = f_string_pattern.search(line)
if has_f_string:
errors_encounter += 1
print(
f"\nF-strings are not supported for translations at line number {line_number}\n{line.strip()[:100]}"
)
continue
else:
continue
match = pattern.search(line)
error_found = False
if not match and line.endswith((",\n", "[\n")):
# concat remaining text to validate multiline pattern
line = "".join(file_lines[line_number - 1 :])
line = line[start_matches.start() + 1 :]
match = pattern.match(line)
if not match:
error_found = True
print(f"\nTranslation syntax error at line number {line_number}\n{line.strip()[:100]}")
if not error_found and not words_pattern.search(line):
error_found = True
print(
f"\nTranslation is useless because it has no words at line number {line_number}\n{line.strip()[:100]}"
)
if error_found:
errors_encounter += 1
if errors_encounter > 0:
print(
'\nVisit "https://frappeframework.com/docs/user/en/translations" to learn about valid translation strings.'
)
sys.exit(1)
else:
print("\nGood To Go!")

View File

@@ -1,40 +0,0 @@
#!/bin/bash
set -e
cd ~ || exit
echo "Setting Up Bench..."
pip install frappe-bench
bench -v init frappe-bench --skip-assets --skip-redis-config-generation --python "$(which python)"
cd ./frappe-bench || exit
echo "Get ERPNext..."
bench get-app --skip-assets erpnext "${GITHUB_WORKSPACE}"
echo "Generating POT file..."
bench generate-pot-file --app erpnext
cd ./apps/erpnext || exit
echo "Configuring git user..."
git config user.email "developers@erpnext.com"
git config user.name "frappe-pr-bot"
echo "Setting the correct git remote..."
# Here, the git remote is a local file path by default. Let's change it to the upstream repo.
git remote set-url upstream https://github.com/frappe/erpnext.git
echo "Creating a new branch..."
isodate=$(date -u +"%Y-%m-%d")
branch_name="pot_${BASE_BRANCH}_${isodate}"
git checkout -b "${branch_name}"
echo "Commiting changes..."
git add erpnext/locale/main.pot
git commit -m "chore: update POT file"
gh auth setup-git
git push -u upstream "${branch_name}"
echo "Creating a PR..."
gh pr create --fill --base "${BASE_BRANCH}" --head "${branch_name}" --reviewer ${PR_REVIEWER} -R frappe/erpnext

55
.github/labeler.yml vendored
View File

@@ -1,55 +0,0 @@
accounts:
- erpnext/accounts/*
- erpnext/controllers/accounts_controller.py
- erpnext/controllers/taxes_and_totals.py
stock:
- erpnext/stock/*
- erpnext/controllers/stock_controller.py
- erpnext/controllers/item_variant.py
assets:
- erpnext/assets/*
regional:
- erpnext/regional/*
selling:
- erpnext/selling/*
- erpnext/controllers/selling_controller.py
buying:
- erpnext/buying/*
- erpnext/controllers/buying_controller.py
support:
- erpnext/support/*
POS:
- pos*
ecommerce:
- erpnext/e_commerce/*
maintenance:
- erpnext/maintenance/*
manufacturing:
- erpnext/manufacturing/*
crm:
- erpnext/crm/*
HR:
- erpnext/hr/*
payroll:
- erpnext/payroll*
projects:
- erpnext/projects/*
# Any python files modifed but no test files modified
needs-tests:
- any: ['erpnext/**/*.py']
all: ['!erpnext/**/test*.py']

4
.github/release.yml vendored
View File

@@ -1,4 +0,0 @@
changelog:
exclude:
labels:
- skip-release-notes

49
.github/stale.yml vendored
View File

@@ -1,35 +1,34 @@
# Configuration for probot-stale - https://github.com/probot/stale
# Label to use when marking as stale
staleLabel: inactive
# Number of days of inactivity before an Issue or Pull Request becomes stale
daysUntilStale: 30
# Limit the number of actions per hour, from 1-30. Default is 30
limitPerRun: 10
# Set to true to ignore issues in a project (defaults to false)
exemptProjects: true
# Set to true to ignore issues in a milestone (defaults to false)
exemptMilestones: true
# Skip the stale action for draft PRs
exemptDraftPr: true
# Number of days of inactivity before a stale Issue or Pull Request is closed.
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
daysUntilClose: 7
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
exemptLabels:
- hotfix
- no-stale
pulls:
daysUntilStale: 15
daysUntilClose: 3
exemptLabels:
- hotfix
markComment: >
This pull request has been automatically marked as inactive because it has
not had recent activity. It will be closed within 3 days if no further
activity occurs, but it only takes a comment to keep a contribution alive
:) Also, even if it is closed, you can always reopen the PR when you're
ready. Thank you for contributing.
# Set to true to ignore issues in a project (defaults to false)
exemptProjects: false
# Set to true to ignore issues in a milestone (defaults to false)
exemptMilestones: true
# Label to use when marking as stale
staleLabel: inactive
# Comment to post when marking as stale. Set to `false` to disable
markComment: >
This pull request has been automatically marked as stale because it has not had
recent activity. It will be closed within a week if no further activity occurs, but it
only takes a comment to keep a contribution alive :) Also, even if it is closed,
you can always reopen the PR when you're ready. Thank you for contributing.
# Limit the number of actions per hour, from 1-30. Default is 30
limitPerRun: 30
# Limit to only `issues` or `pulls`
only: pulls

View File

@@ -1,32 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="4 2 193 52">
<g filter="url(#filter0_dd)">
<rect x="4" y="2" width="193" height="52" rx="6" fill="#2490EF"/>
<path d="M28 22.2891H32.8786V35.5H36.2088V22.2891H41.0874V19.5H28V22.2891Z" fill="white"/>
<path d="M41.6982 35.5H45.0129V28.7109C45.0129 27.2344 46.0866 26.2188 47.5494 26.2188C48.0085 26.2188 48.6388 26.2969 48.95 26.3984V23.4453C48.6543 23.375 48.2419 23.3281 47.9074 23.3281C46.5691 23.3281 45.472 24.1094 45.0362 25.5938H44.9117V23.5H41.6982V35.5Z" fill="white"/>
<path d="M52.8331 40C55.2996 40 56.6068 38.7344 57.2837 36.7969L61.9289 23.5156L58.4197 23.5L55.9221 32.3125H55.7976L53.3233 23.5H49.8374L54.1247 35.8437L53.9302 36.3516C53.4944 37.4766 52.6619 37.5312 51.4947 37.1719L50.7478 39.6562C51.2224 39.8594 51.9927 40 52.8331 40Z" fill="white"/>
<path d="M73.6142 35.7344C77.2401 35.7344 79.4966 33.2422 79.4966 29.5469C79.4966 25.8281 77.2401 23.3438 73.6142 23.3438C69.9883 23.3438 67.7319 25.8281 67.7319 29.5469C67.7319 33.2422 69.9883 35.7344 73.6142 35.7344ZM73.6298 33.1562C71.9569 33.1562 71.101 31.6171 71.101 29.5233C71.101 27.4296 71.9569 25.8827 73.6298 25.8827C75.2715 25.8827 76.1274 27.4296 76.1274 29.5233C76.1274 31.6171 75.2715 33.1562 73.6298 33.1562Z" fill="white"/>
<path d="M84.7253 28.5625C84.7331 27.0156 85.6512 26.1094 86.9895 26.1094C88.3201 26.1094 89.1215 26.9844 89.1137 28.4531V35.5H92.4284V27.8594C92.4284 25.0625 90.7945 23.3438 88.3046 23.3438C86.5306 23.3438 85.2466 24.2187 84.7097 25.6172H84.5697V23.5H81.4106V35.5H84.7253V28.5625Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M102.429 19.5H113.429V22.3141H102.429V19.5ZM102.429 35.5V26.6794H112.699V29.4982H105.94V35.5H102.429Z" fill="white"/>
<path d="M131.584 24.9625C131.09 21.5057 128.345 19.5 124.785 19.5C120.589 19.5 117.429 22.463 117.429 27.4924C117.429 32.5142 120.55 35.4848 124.785 35.4848C128.604 35.4848 131.137 33.0916 131.584 30.1211L128.651 30.1059C128.282 31.9293 126.745 32.9549 124.824 32.9549C122.22 32.9549 120.354 31.0632 120.354 27.4924C120.354 23.9824 122.204 22.0299 124.832 22.0299C126.784 22.0299 128.314 23.1011 128.651 24.9625H131.584Z" fill="white"/>
<path d="M136.409 19.7124H133.571V35.2718H136.409V19.7124Z" fill="white"/>
<path d="M144.031 35.5001C147.56 35.5001 149.803 33.0917 149.803 29.483C149.803 25.8667 147.56 23.4507 144.031 23.4507C140.502 23.4507 138.259 25.8667 138.259 29.483C138.259 33.0917 140.502 35.5001 144.031 35.5001ZM144.047 33.2969C142.094 33.2969 141.137 31.6103 141.137 29.4754C141.137 27.3406 142.094 25.6312 144.047 25.6312C145.968 25.6312 146.925 27.3406 146.925 29.4754C146.925 31.6103 145.968 33.2969 144.047 33.2969Z" fill="white"/>
<path d="M159.338 30.3641C159.338 32.1419 158.028 33.0232 156.773 33.0232C155.409 33.0232 154.499 32.0887 154.499 30.6072V23.6025H151.66V31.0327C151.66 33.8361 153.307 35.4239 155.675 35.4239C157.479 35.4239 158.749 34.5046 159.298 33.1979H159.424V35.272H162.176V23.6025H159.338V30.3641Z" fill="white"/>
<path d="M169.014 35.4769C171.084 35.4769 172.017 34.2841 172.464 33.4332H172.637V35.2718H175.429V19.7124H172.582V25.532H172.464C172.033 24.6887 171.147 23.4503 169.022 23.4503C166.238 23.4503 164.05 25.5624 164.05 29.4522C164.05 33.2965 166.175 35.4769 169.014 35.4769ZM169.806 33.2205C167.931 33.2205 166.943 31.6251 166.943 29.437C166.943 27.2642 167.916 25.7067 169.806 25.7067C171.633 25.7067 172.637 27.173 172.637 29.437C172.637 31.701 171.617 33.2205 169.806 33.2205Z" fill="white"/>
</g>
<defs>
<filter id="filter0_dd" x="0" y="0" width="201" height="60" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="0.25"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="2"/>
<feGaussianBlur stdDeviation="2"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.13 0"/>
<feBlend mode="normal" in2="effect1_dropShadow" result="effect2_dropShadow"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow" result="shape"/>
</filter>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 4.3 KiB

View File

@@ -1,29 +0,0 @@
name: Backport
on:
pull_request_target:
types:
- closed
- labeled
permissions:
contents: read
jobs:
main:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- name: Checkout Actions
uses: actions/checkout@v6
with:
repository: "frappe/backport"
path: ./actions
ref: develop
- name: Install Actions
run: npm install --production --prefix ./actions
- name: Run backport
uses: ./actions/backport
with:
token: ${{secrets.RELEASE_TOKEN}}
labelsToAdd: "backport"
title: "{{originalTitle}}"

View File

@@ -2,10 +2,6 @@ name: Trigger Docker build on release
on:
release:
types: [released]
permissions:
contents: read
jobs:
curl:
runs-on: ubuntu-latest
@@ -15,4 +11,4 @@ jobs:
- name: curl
run: |
apk add curl bash
curl -X POST -H "Accept: application/vnd.github.v3+json" -H "Authorization: Bearer ${{ secrets.CI_PAT }}" https://api.github.com/repos/frappe/frappe_docker/actions/workflows/build_stable.yml/dispatches -d '{"ref":"main"}'
curl -s -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "Travis-API-Version: 3" -H "Authorization: token ${{ secrets.TRAVIS_CI_TOKEN }}" -d '{"request":{"branch":"master"}}' https://api.travis-ci.com/repo/frappe%2Ffrappe_docker/requests

View File

@@ -1,28 +0,0 @@
name: 'Documentation Required'
on:
pull_request:
types: [ opened, synchronize, reopened, edited ]
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: 'Setup Environment'
uses: actions/setup-python@v6
with:
python-version: '3.10'
- name: 'Clone repo'
uses: actions/checkout@v6
- name: Validate Docs
env:
PR_NUMBER: ${{ github.event.number }}
run: |
pip install requests --quiet
python $GITHUB_WORKSPACE/.github/helper/documentation.py $PR_NUMBER

View File

@@ -1,44 +0,0 @@
# This workflow is agnostic to branches. Only maintain on develop branch.
# To add/remove branches just modify the matrix.
name: Regenerate POT file (translatable strings)
on:
schedule:
# 9:30 UTC => 3 PM IST Sunday
- cron: "30 9 * * 0"
workflow_dispatch:
jobs:
regenerate-pot-file:
name: Regenerate POT file
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
branch: ["develop", "version-16-hotfix"]
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v6
with:
ref: ${{ matrix.branch }}
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: "3.14"
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: 24
- name: Run script to update POT file
run: |
bash ${GITHUB_WORKSPACE}/.github/helper/update_pot_file.sh
env:
GH_TOKEN: ${{ secrets.RELEASE_TOKEN }}
BASE_BRANCH: ${{ matrix.branch }}
PR_REVIEWER: barredterra # change to your GitHub username if you copied this file

View File

@@ -1,36 +0,0 @@
# This workflow is agnostic to branches. Only maintain on develop branch.
# To add/remove versions just modify the matrix.
name: Create weekly release pull requests
permissions:
contents: read
on:
schedule:
# 9:30 UTC => 3 PM IST Tuesday
- cron: "30 9 * * 2"
workflow_dispatch:
jobs:
stable-release:
name: Release
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
version: ["15", "16"]
steps:
- uses: octokit/request-action@v2.x
with:
route: POST /repos/{owner}/{repo}/pulls
owner: frappe
repo: erpnext
title: |-
"chore: release v${{ matrix.version }}"
body: "Automated weekly release."
base: version-${{ matrix.version }}
head: version-${{ matrix.version }}-hotfix
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}

View File

@@ -1,30 +0,0 @@
name: "Auto-label PRs based on title"
on:
pull_request_target:
types: [opened, reopened]
jobs:
add-label-if-prefix-matches:
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-latest
steps:
- name: Check PR title and add label if it matches prefixes
uses: actions/github-script@v7
continue-on-error: true
with:
script: |
const title = context.payload.pull_request.title.toLowerCase();
const prefixes = ['chore', 'ci', 'style', 'test', 'refactor'];
// Check if the PR title starts with any of the prefixes
if (prefixes.some(prefix => title.startsWith(prefix))) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
labels: ['skip-release-notes']
});
}

View File

@@ -1,16 +0,0 @@
name: "Pull Request Labeler"
on:
pull_request_target:
types: [opened, reopened]
permissions:
issues: write
pull-requests: write
jobs:
triage:
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v3
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"

View File

@@ -1,48 +0,0 @@
name: Linters
on:
pull_request: { }
permissions:
contents: read
jobs:
linters:
name: linters
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Set up Python 3.14
uses: actions/setup-python@v6
with:
python-version: '3.14'
cache: pip
- name: Install and Run Pre-commit
uses: pre-commit/action@v3.0.0
semgrep:
name: semgrep
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Set up Python 3.14
uses: actions/setup-python@v6
with:
python-version: '3.14'
cache: pip
- name: Download Semgrep rules
run: git clone --depth 1 https://github.com/frappe/semgrep-rules.git frappe-semgrep-rules
- name: Download semgrep
run: pip install semgrep
- name: Run Semgrep rules
run: semgrep ci --config ./frappe-semgrep-rules/rules --config r/python.lang.correctness
- name: Semgrep for Test Correctness
run: semgrep ci --include=**/test_*.py --config ./semgrep/test-correctness.yml

View File

@@ -1,21 +0,0 @@
name: 'Lock threads'
on:
schedule:
- cron: '0 0 * * *'
workflow_dispatch:
permissions:
issues: write
pull-requests: write
jobs:
lock:
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v5
with:
github-token: ${{ github.token }}
issue-inactive-days: 14
pr-inactive-days: 14

View File

@@ -1,170 +0,0 @@
name: Patch
on:
pull_request:
paths-ignore:
- '**.js'
- '**.css'
- '**.md'
- '**.html'
- '**.csv'
- 'crowdin.yml'
- '.coderabbit.yml'
- '.mergify.yml'
workflow_dispatch:
permissions:
contents: read
concurrency:
group: patch-develop-${{ github.event_name }}-${{ github.event.number || github.event_name == 'workflow_dispatch' && github.run_id || '' }}
cancel-in-progress: true
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 60
name: Patch Test
services:
mysql:
image: mariadb:11.8
env:
MARIADB_ROOT_PASSWORD: 'root'
ports:
- 3306:3306
options: --health-cmd="mariadb-admin ping" --health-interval=5s --health-timeout=2s --health-retries=3
steps:
- name: Clone
uses: actions/checkout@v6
- name: Check for valid Python & Merge Conflicts
run: |
python -m compileall -fq "${GITHUB_WORKSPACE}"
if grep -lr --exclude-dir=node_modules "^<<<<<<< " "${GITHUB_WORKSPACE}"
then echo "Found merge conflicts"
exit 1
fi
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: |
3.11
3.13
3.14
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: 24
check-latest: true
- name: Add to Hosts
run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts
- name: Cache pip
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml') }}
restore-keys: |
${{ runner.os }}-pip-
${{ runner.os }}-
- name: Cache node modules
uses: actions/cache@v4
env:
cache-name: cache-node-modules
with:
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
- uses: actions/cache@v4
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install
run: |
pip install frappe-bench
bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
env:
DB: mariadb
TYPE: server
- name: Run Patch Tests
run: |
cd ~/frappe-bench/
bench remove-app payments --force
jq 'del(.install_apps)' ~/frappe-bench/sites/test_site/site_config.json > tmp.json
mv tmp.json ~/frappe-bench/sites/test_site/site_config.json
wget https://frappe.io/files/erpnext-v14.sql.gz
bench --site test_site --force restore ~/frappe-bench/erpnext-v14.sql.gz
git -C "apps/frappe" remote set-url upstream https://github.com/frappe/frappe.git
git -C "apps/erpnext" remote set-url upstream https://github.com/frappe/erpnext.git
function update_to_version() {
version=$1
branch_name="version-$version-hotfix"
echo "Updating to v$version"
# Fetch and checkout branches
git -C "apps/frappe" fetch --depth 1 upstream $branch_name:$branch_name
git -C "apps/erpnext" fetch --depth 1 upstream $branch_name:$branch_name
git -C "apps/frappe" checkout -q -f $branch_name
git -C "apps/erpnext" checkout -q -f $branch_name
# Resetup env and install apps
pgrep honcho | xargs kill
rm -rf ~/frappe-bench/env
bench -v setup env --python python$2
bench pip install -e ./apps/erpnext
bench start &>> ~/frappe-bench/bench_start.log &
bench --site test_site migrate
}
update_to_version 15 3.13
update_to_version 16 3.14
echo "Updating to latest version"
git -C "apps/frappe" fetch --depth 1 upstream "${GITHUB_BASE_REF:-${GITHUB_REF##*/}}"
git -C "apps/frappe" checkout -q -f FETCH_HEAD
git -C "apps/erpnext" checkout -q -f "$GITHUB_SHA"
pgrep honcho | xargs kill
rm -rf ~/frappe-bench/env
bench -v setup env
bench pip install -e ./apps/erpnext
bench start &>> ~/frappe-bench/bench_start.log &
bench --site test_site migrate
- name: Show bench output
if: ${{ always() }}
run: |
cd ~/frappe-bench
cat bench_start.log || true
cd logs
for f in ./*.log*; do
echo "Printing log: $f";
cat $f
done

View File

@@ -1,28 +0,0 @@
# Tests are skipped for these files but github doesn't allow "passing" hence this is required.
name: Skipped Patch Test
on:
pull_request:
paths:
- "**.js"
- "**.css"
- "**.md"
- "**.html"
- "**.csv"
- 'crowdin.yml'
- '.coderabbit.yml'
- '.mergify.yml'
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
name: Patch Test
steps:
- name: Pass skipped tests unconditionally
run: "echo Skipped"

View File

@@ -1,35 +0,0 @@
name: Generate Semantic Release
on:
push:
branches:
- version-13
permissions:
contents: read
jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
- name: Checkout Entire Repository
uses: actions/checkout@v6
with:
fetch-depth: 0
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 20
- name: Setup dependencies
run: |
npm install @semantic-release/git @semantic-release/exec --no-save
- name: Create Release
env:
GH_TOKEN: ${{ secrets.RELEASE_TOKEN }}
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
GIT_AUTHOR_NAME: "Frappe PR Bot"
GIT_AUTHOR_EMAIL: "developers@frappe.io"
GIT_COMMITTER_NAME: "Frappe PR Bot"
GIT_COMMITTER_EMAIL: "developers@frappe.io"
run: npx semantic-release

View File

@@ -1,42 +0,0 @@
# This action:
#
# 1. Generates release notes using github API.
# 2. Strips unnecessary info like chore/style etc from notes.
# 3. Updates release info.
# This action needs to be maintained on all branches that do releases.
name: 'Release Notes'
on:
workflow_dispatch:
inputs:
tag_name:
description: 'Tag of release like v13.0.0'
required: true
type: string
release:
types: [released]
permissions:
contents: read
jobs:
regen-notes:
name: 'Regenerate release notes'
runs-on: ubuntu-latest
steps:
- name: Update notes
run: |
NEW_NOTES=$(gh api --method POST -H "Accept: application/vnd.github+json" /repos/frappe/erpnext/releases/generate-notes -f tag_name=$RELEASE_TAG \
| jq -r '.body' \
| sed -E '/^\* (chore|ci|test|docs|style)/d' \
| sed -E 's/by @mergify //'
)
RELEASE_ID=$(gh api -H "Accept: application/vnd.github+json" /repos/frappe/erpnext/releases/tags/$RELEASE_TAG | jq -r '.id')
gh api --method PATCH -H "Accept: application/vnd.github+json" /repos/frappe/erpnext/releases/$RELEASE_ID -f body="$NEW_NOTES"
env:
GH_TOKEN: ${{ secrets.RELEASE_TOKEN }}
RELEASE_TAG: ${{ github.event.inputs.tag_name || github.event.release.tag_name }}

View File

@@ -1,143 +0,0 @@
name: Individual
on:
workflow_dispatch:
concurrency:
group: server-individual-tests-lightmode-develop
cancel-in-progress: true
permissions:
contents: read
jobs:
discover:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- name: Clone
uses: actions/checkout@v6
- id: set-matrix
run: |
# Use grep and find to get the list of test files
matrix=$(find . -path '*/test_*.py' | xargs grep -l 'def test_' | sort | awk '{
# Remove ./ prefix, file extension, and replace / with .
gsub(/^\.\//, "", $0)
gsub(/\.py$/, "", $0)
gsub(/\//, ".", $0)
# Add to array
tests[NR] = $0
}
END {
# Start JSON array
printf "{\n \"include\": [\n"
# Loop through array and create JSON objects
for (i=1; i<=NR; i++) {
printf " {\"test\": \"%s\"}", tests[i]
if (i < NR) printf ","
printf "\n"
}
# Close JSON array
printf " ]\n}"
}')
# Output the matrix
echo "matrix=$(echo "$matrix" | jq -c)" >> $GITHUB_OUTPUT
# For debugging (optional)
echo "Generated matrix:"
echo "$matrix"
test:
needs: discover
runs-on: ubuntu-latest
timeout-minutes: 60
env:
NODE_ENV: "production"
strategy:
fail-fast: false
matrix: ${{fromJson(needs.discover.outputs.matrix)}}
max-parallel: 14
name: Test
services:
mysql:
image: mariadb:10.6
env:
MARIADB_ROOT_PASSWORD: 'root'
ports:
- 3306:3306
options: --health-cmd="mariadb-admin ping" --health-interval=5s --health-timeout=2s --health-retries=3
steps:
- name: Clone
uses: actions/checkout@v6
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: '3.14'
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: 24
check-latest: true
- name: Add to Hosts
run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts
- name: Cache pip
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml') }}
restore-keys: |
${{ runner.os }}-pip-
${{ runner.os }}-
- name: Cache node modules
uses: actions/cache@v4
env:
cache-name: cache-node-modules
with:
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
- uses: actions/cache@v4
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install
run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
env:
DB: mariadb
TYPE: server
FRAPPE_USER: ${{ github.event.inputs.user }}
FRAPPE_BRANCH: ${{ github.event.inputs.branch }}
- name: Run Tests
run: |
site_name=$(echo "${{matrix.test}}" | sed -e 's/.*\.\(test_.*$\)/\1/')
echo "$site_name"
mkdir ~/frappe-bench/sites/$site_name
cp -r "${GITHUB_WORKSPACE}/.github/helper/site_config_mariadb.json" ~/frappe-bench/sites/$site_name/site_config.json
cd ~/frappe-bench/
bench --site $site_name reinstall --yes
bench --site $site_name set-config allow_tests true
bench --site $site_name run-tests --module ${{ matrix.test }} --lightmode

View File

@@ -1,30 +0,0 @@
name: Semantic Commits
on:
pull_request: {}
permissions:
contents: read
concurrency:
group: commitcheck-erpnext-${{ github.event.number }}
cancel-in-progress: true
jobs:
commitlint:
name: Check Commit Titles
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 200
- uses: actions/setup-node@v6
with:
node-version: 18
check-latest: true
- name: Check commit titles
run: |
npm install @commitlint/cli @commitlint/config-conventional
npx commitlint --verbose --from ${{ github.event.pull_request.base.sha }} --to ${{ github.event.pull_request.head.sha }}

View File

@@ -1,31 +0,0 @@
# Tests are skipped for these files but github doesn't allow "passing" hence this is required.
name: Skipped Tests
on:
pull_request:
paths:
- "**.js"
- "**.css"
- "**.svg"
- "**.md"
- "**.html"
- 'crowdin.yml'
- '.coderabbit.yml'
- '.mergify.yml'
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
container: [1, 2, 3, 4]
name: Python Unit Tests
steps:
- name: Pass skipped tests unconditionally
run: "echo Skipped"

View File

@@ -1,166 +0,0 @@
name: Server (Mariadb)
on:
repository_dispatch:
types: [frappe-framework-change]
pull_request:
paths-ignore:
- '**.js'
- '**.css'
- '**.svg'
- '**.md'
- '**.html'
- 'crowdin.yml'
- '.coderabbit.yml'
- '.mergify.yml'
schedule:
# Run everday at midnight UTC / 5:30 IST
- cron: "0 0 * * *"
workflow_dispatch:
inputs:
user:
description: 'Frappe Framework repository user (add your username for forks)'
required: true
default: 'frappe'
type: string
branch:
description: 'Frappe Framework branch'
default: 'develop'
required: false
type: string
permissions:
contents: read
concurrency:
group: server-mariadb-develop-${{ github.event_name }}-${{ github.event.number || github.event_name == 'workflow_dispatch' && github.run_id || '' }}
cancel-in-progress: true
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 60
env:
TZ: 'Asia/Kolkata'
NODE_ENV: "production"
WITH_COVERAGE: ${{ github.event_name != 'pull_request' }}
strategy:
fail-fast: false
matrix:
container: [1, 2, 3, 4]
name: Python Unit Tests
services:
mysql:
image: mariadb:10.6
env:
TZ: 'Asia/Kolkata'
MARIADB_ROOT_PASSWORD: 'root'
ports:
- 3306:3306
options: --health-cmd="mariadb-admin ping" --health-interval=5s --health-timeout=2s --health-retries=3
steps:
- name: Clone
uses: actions/checkout@v6
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: '3.14'
- name: Check for valid Python & Merge Conflicts
run: |
python -m compileall -fq "${GITHUB_WORKSPACE}"
if grep -lr --exclude-dir=node_modules "^<<<<<<< " "${GITHUB_WORKSPACE}"
then echo "Found merge conflicts"
exit 1
fi
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: 24
check-latest: true
- name: Add to Hosts
run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts
- name: Cache pip
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml') }}
restore-keys: |
${{ runner.os }}-pip-
${{ runner.os }}-
- name: Cache node modules
uses: actions/cache@v4
env:
cache-name: cache-node-modules
with:
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
- uses: actions/cache@v4
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install
run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
env:
DB: mariadb
TYPE: server
FRAPPE_USER: ${{ github.event.inputs.user }}
FRAPPE_BRANCH: ${{ github.event.client_payload.sha || github.event.inputs.branch }}
- name: Run Tests
run: 'cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --lightmode --app erpnext --total-builds ${{ strategy.job-total }} --build-number ${{ matrix.container }} --with-coverage'
env:
TYPE: server
- name: Show bench output
if: ${{ always() }}
run: cat ~/frappe-bench/bench_start.log || true
- name: Upload coverage data
uses: actions/upload-artifact@v4
with:
name: coverage-${{ matrix.container }}
path: /home/runner/frappe-bench/sites/coverage.xml
coverage:
name: Coverage Wrap Up
needs: test
runs-on: ubuntu-latest
steps:
- name: Clone
uses: actions/checkout@v6
- name: Download artifacts
uses: actions/download-artifact@v4
- name: Upload coverage data
uses: codecov/codecov-action@v4
with:
name: MariaDB
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: true
verbose: true

View File

@@ -1,118 +0,0 @@
name: Server (Postgres)
on:
pull_request:
paths-ignore:
- '**.js'
- '**.md'
- '**.html'
- 'crowdin.yml'
- '.coderabbit.yml'
- '.mergify.yml'
types: [opened, labelled, synchronize, reopened]
concurrency:
group: server-postgres-develop-${{ github.event_name }}-${{ github.event.number || github.event_name == 'workflow_dispatch' && github.run_id || '' }}
cancel-in-progress: true
permissions:
contents: read
jobs:
test:
if: ${{ contains(github.event.pull_request.labels.*.name, 'postgres') }}
runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
container: [1]
name: Python Unit Tests
services:
postgres:
image: postgres:13.3
env:
POSTGRES_PASSWORD: travis
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
steps:
- name: Clone
uses: actions/checkout@v6
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: '3.14'
- name: Check for valid Python & Merge Conflicts
run: |
python -m compileall -fq "${GITHUB_WORKSPACE}"
if grep -lr --exclude-dir=node_modules "^<<<<<<< " "${GITHUB_WORKSPACE}"
then echo "Found merge conflicts"
exit 1
fi
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: 24
check-latest: true
- name: Add to Hosts
run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts
- name: Cache pip
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml') }}
restore-keys: |
${{ runner.os }}-pip-
${{ runner.os }}-
- name: Cache node modules
uses: actions/cache@v4
env:
cache-name: cache-node-modules
with:
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
- uses: actions/cache@v4
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install
run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
env:
DB: postgres
TYPE: server
- name: Run Tests
run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --app erpnext --use-orchestrator
env:
TYPE: server
CI_BUILD_ID: ${{ github.run_id }}
ORCHESTRATOR_URL: http://test-orchestrator.frappe.io

10
.gitignore vendored
View File

@@ -2,11 +2,11 @@
*.py~
.DS_Store
conf.py
locale
latest_updates.json
.wnf-lang-status
*.egg-info
dist/
erpnext/public/dist
erpnext/docs/current
*.swp
*.swo
@@ -14,12 +14,4 @@ __pycache__
*~
.idea/
.vscode/
.helix/
node_modules/
.backportrc.json
# Aider AI Chat
.aider*
# Banking SPA
erpnext/public/banking
erpnext/www/banking.html

View File

@@ -1,94 +0,0 @@
pull_request_rules:
- name: Auto-close PRs on stable branch
conditions:
- and:
- and:
- author!=surajshetty3416
- author!=gavindsouza
- author!=rohitwaghchaure
- author!=nabinhait
- author!=ankush
- author!=deepeshgarg007
- author!=frappe-pr-bot
- author!=mergify[bot]
- or:
- base=version-13
- base=version-12
- base=version-14
- base=version-15
- base=version-16
actions:
close:
comment:
message: |
@{{author}}, thanks for the contribution, but we do not accept pull requests on a stable branch. Please raise PR on an appropriate hotfix branch.
https://github.com/frappe/erpnext/wiki/Pull-Request-Checklist#which-branch
- name: backport to develop
conditions:
- label="backport develop"
actions:
backport:
branches:
- develop
assignees:
- "{{ author }}"
- name: backport to version-14-hotfix
conditions:
- label="backport version-14-hotfix"
actions:
backport:
branches:
- version-14-hotfix
assignees:
- "{{ author }}"
- name: backport to version-15-hotfix
conditions:
- label="backport version-15-hotfix"
actions:
backport:
branches:
- version-15-hotfix
assignees:
- "{{ author }}"
- name: backport to version-16-hotfix
conditions:
- label="backport version-16-hotfix"
actions:
backport:
branches:
- version-16-hotfix
assignees:
- "{{ author }}"
- name: Automatic merge on CI success and review
conditions:
- status-success=linters
- status-success=Sider
- status-success=Semantic Pull Request
- status-success=Patch Test
- status-success=Python Unit Tests (1)
- status-success=Python Unit Tests (2)
- status-success=Python Unit Tests (3)
- label!=dont-merge
- label!=squash
- "#approved-reviews-by>=1"
actions:
merge:
method: merge
- name: Automatic squash on CI success and review
conditions:
- status-success=linters
- status-success=Sider
- status-success=Patch Test
- status-success=Python Unit Tests (1)
- status-success=Python Unit Tests (2)
- status-success=Python Unit Tests (3)
- label!=dont-merge
- label=squash
- "#approved-reviews-by>=1"
actions:
merge:
method: squash
commit_message_template: |
{{ title }} (#{{ number }})
{{ body }}

View File

@@ -1,72 +0,0 @@
exclude: 'node_modules|.git'
default_stages: [pre-commit]
fail_fast: false
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
hooks:
- id: trailing-whitespace
files: "erpnext.*"
exclude: ".*json$|.*txt$|.*csv|.*md"
- id: check-yaml
- id: no-commit-to-branch
args: ['--branch', 'develop']
- id: check-merge-conflict
- id: check-ast
- id: check-json
- id: check-toml
- id: check-yaml
- id: debug-statements
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v2.7.1
hooks:
- id: prettier
types_or: [javascript, vue, scss]
# Ignore any files that might contain jinja / bundles
exclude: |
(?x)^(
erpnext/public/dist/.*|
cypress/.*|
.*node_modules.*|
.*boilerplate.*|
erpnext/templates/includes/.*
)$
- repo: https://github.com/pre-commit/mirrors-eslint
rev: v8.44.0
hooks:
- id: eslint
types_or: [javascript]
args: ['--quiet']
# Ignore any files that might contain jinja / bundles
exclude: |
(?x)^(
erpnext/public/dist/.*|
cypress/.*|
.*node_modules.*|
.*boilerplate.*|
erpnext/public/js/controllers/.*|
erpnext/templates/pages/order.js|
erpnext/templates/includes/.*
)$
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.2.0
hooks:
- id: ruff
name: "Run ruff import sorter"
args: ["--select=I", "--fix"]
- id: ruff
name: "Run ruff linter"
- id: ruff-format
name: "Run ruff formatter"
ci:
autoupdate_schedule: weekly
skip: []
submodules: false

1
.pylintrc Normal file
View File

@@ -0,0 +1 @@
disable=access-member-before-definition

View File

@@ -1,24 +0,0 @@
{
"branches": ["version-13"],
"plugins": [
"@semantic-release/commit-analyzer", {
"preset": "angular",
"releaseRules": [
{"breaking": true, "release": false}
]
},
"@semantic-release/release-notes-generator",
[
"@semantic-release/exec", {
"prepareCmd": 'sed -ir "s/[0-9]*\.[0-9]*\.[0-9]*/${nextRelease.version}/" erpnext/__init__.py'
}
],
[
"@semantic-release/git", {
"assets": ["erpnext/__init__.py"],
"message": "chore(release): Bumped to Version ${nextRelease.version}\n\n${nextRelease.notes}"
}
],
"@semantic-release/github"
]
}

82
.travis.yml Normal file
View File

@@ -0,0 +1,82 @@
dist: trusty
language: python
git:
depth: 1
cache:
- pip
addons:
hosts: test_site
mariadb: 10.3
jobs:
include:
- name: "Python 2.7 Server Side Test"
python: 2.7
script: bench --site test_site run-tests --app erpnext --coverage
- name: "Python 3.6 Server Side Test"
python: 3.6
script: bench --site test_site run-tests --app erpnext --coverage
- name: "Python 2.7 Patch Test"
python: 2.7
before_script:
- wget http://build.erpnext.com/20171108_190013_955977f8_database.sql.gz
- bench --site test_site --force restore ~/frappe-bench/20171108_190013_955977f8_database.sql.gz
script: bench --site test_site migrate
- name: "Python 3.6 Patch Test"
python: 3.6
before_script:
- wget http://build.erpnext.com/20171108_190013_955977f8_database.sql.gz
- bench --site test_site --force restore ~/frappe-bench/20171108_190013_955977f8_database.sql.gz
script: bench --site test_site migrate
install:
- cd ~
- nvm install 10
- git clone https://github.com/frappe/bench --depth 1
- pip install -e ./bench
- git clone https://github.com/frappe/frappe --branch $TRAVIS_BRANCH --depth 1
- bench init --skip-assets --frappe-path ~/frappe --python $(which python) frappe-bench
- mkdir ~/frappe-bench/sites/test_site
- cp -r $TRAVIS_BUILD_DIR/.travis/site_config.json ~/frappe-bench/sites/test_site/
- mysql -u root -e "SET GLOBAL character_set_server = 'utf8mb4'"
- mysql -u root -e "SET GLOBAL collation_server = 'utf8mb4_unicode_ci'"
- mysql -u root -e "CREATE DATABASE test_frappe"
- mysql -u root -e "CREATE USER 'test_frappe'@'localhost' IDENTIFIED BY 'test_frappe'"
- mysql -u root -e "GRANT ALL PRIVILEGES ON \`test_frappe\`.* TO 'test_frappe'@'localhost'"
- mysql -u root -e "UPDATE mysql.user SET Password=PASSWORD('travis') WHERE User='root'"
- mysql -u root -e "FLUSH PRIVILEGES"
- wget -O /tmp/wkhtmltox.tar.xz https://github.com/frappe/wkhtmltopdf/raw/master/wkhtmltox-0.12.3_linux-generic-amd64.tar.xz
- tar -xf /tmp/wkhtmltox.tar.xz -C /tmp
- sudo mv /tmp/wkhtmltox/bin/wkhtmltopdf /usr/local/bin/wkhtmltopdf
- sudo chmod o+x /usr/local/bin/wkhtmltopdf
- sudo apt-get install libcups2-dev
- cd ~/frappe-bench
- sed -i 's/watch:/# watch:/g' Procfile
- sed -i 's/schedule:/# schedule:/g' Procfile
- sed -i 's/socketio:/# socketio:/g' Procfile
- sed -i 's/redis_socketio:/# redis_socketio:/g' Procfile
- bench get-app erpnext $TRAVIS_BUILD_DIR
- bench start &
- bench --site test_site reinstall --yes
after_script:
- pip install coverage==4.5.4
- pip install python-coveralls
- coveralls -b apps/erpnext -d ../../sites/.coverage

13
.travis/site_config.json Normal file
View File

@@ -0,0 +1,13 @@
{
"db_name": "test_frappe",
"db_password": "test_frappe",
"auto_email_id": "test@example.com",
"mail_server": "smtp.example.com",
"mail_login": "test@example.com",
"mail_password": "test",
"admin_password": "admin",
"root_login": "root",
"root_password": "travis",
"host_name": "http://test_site:8000",
"install_apps": ["erpnext"]
}

View File

@@ -1,23 +0,0 @@
# Each line is a file pattern followed by one or more owners.
# These owners will be the default owners for everything in
# the repo. Unless a later match takes precedence,
erpnext/accounts/ @ruthra-kumar
erpnext/assets/ @khushi8112
erpnext/regional @ruthra-kumar
erpnext/selling @ruthra-kumar
erpnext/buying/ @rohitwaghchaure @mihir-kandoi
erpnext/maintenance/ @rohitwaghchaure @mihir-kandoi
erpnext/manufacturing/ @rohitwaghchaure @mihir-kandoi
erpnext/quality_management/ @rohitwaghchaure @mihir-kandoi
erpnext/stock/ @rohitwaghchaure @mihir-kandoi
erpnext/subcontracting/ @mihir-kandoi
erpnext/projects/ @nishkagosalia
erpnext/controllers/ @ruthra-kumar @rohitwaghchaure @mihir-kandoi
erpnext/patches/ @ruthra-kumar @rohitwaghchaure @mihir-kandoi
.github/ @ruthra-kumar @mihir-kandoi
pyproject.toml @ruthra-kumar

211
README.md
View File

@@ -1,177 +1,94 @@
<div align="center">
<a href="https://frappe.io/erpnext">
<img src="./erpnext/public/images/v16/erpnext.svg" alt="ERPNext Logo" height="80px" width="80px"/>
</a>
<img src="https://raw.githubusercontent.com/frappe/erpnext/develop/erpnext/public/images/erpnext-logo.png" height="128">
<h2>ERPNext</h2>
<div align="center">
<p>Powerful, Intuitive and Open-Source ERP</p>
</div>
<p align="center">
<p>ERP made simple</p>
</p>
[![Learn on Frappe School](https://img.shields.io/badge/Frappe%20School-Learn%20ERPNext-blue?style=flat-square)](https://frappe.school)<br><br>
[![CI](https://github.com/frappe/erpnext/actions/workflows/server-tests-mariadb.yml/badge.svg?event=schedule)](https://github.com/frappe/erpnext/actions/workflows/server-tests-mariadb.yml)
[![docker pulls](https://img.shields.io/docker/pulls/frappe/erpnext.svg)](https://hub.docker.com/r/frappe/erpnext)
[![Build Status](https://travis-ci.com/frappe/erpnext.svg)](https://travis-ci.com/frappe/erpnext)
[![Open Source Helpers](https://www.codetriage.com/frappe/erpnext/badges/users.svg)](https://www.codetriage.com/frappe/erpnext)
[![Coverage Status](https://coveralls.io/repos/github/frappe/erpnext/badge.svg?branch=develop)](https://coveralls.io/github/frappe/erpnext?branch=develop)
[https://erpnext.com](https://erpnext.com)
</div>
<div align="center">
<img src="./erpnext/public/images/v16/hero_image.png" alt="ERPNext Hero Image"/>
</div>
Includes: Accounting, Inventory, Manufacturing, CRM, Sales, Purchase, Project Management, HRMS. Requires MariaDB.
<div align="center">
<a href="https://erpnext-demo.frappe.cloud/api/method/erpnext_demo.erpnext_demo.auth.login_demo">Live Demo</a>
-
<a href="https://frappe.io/erpnext">Website</a>
-
<a href="https://docs.frappe.io/erpnext/">Documentation</a>
</div>
ERPNext is built on the [Frappe](https://github.com/frappe/frappe) Framework, a full-stack web app framework in Python & JavaScript.
## ERPNext
- [User Guide](https://erpnext.com/docs/user)
- [Discussion Forum](https://discuss.erpnext.com/)
100% Open-Source ERP System to help you run your business.
---
### Motivation
Running a business is a complex task - handling invoices, tracking stock, managing personnel, and other daily operations. In a market where software is sold separately to manage each of these tasks, ERPNext does all of the above and more, for free.
### Key Features
- **Accounting**: All the tools you need to manage cash flow in one place, right from recording transactions to summarizing and analyzing financial reports.
- **Order Management**: Track inventory levels, replenish stock, and manage sales orders, customers, suppliers, shipments, deliverables, and order fulfillment.
- **Manufacturing**: Simplifies the production cycle, helps track material consumption, exhibits capacity planning, handles subcontracting, and more!
- **Asset Management**: From purchase to disposal, IT infrastructure to equipment. Covers every branch of your organization, all in one centralized system.
- **Projects**: Deliver both internal and external projects on time, budget and profitability. Track tasks, timesheets, and issues by project.
<details open>
<summary>More</summary>
<img src="https://erpnext.com/files/v16_bom.png"/>
<img src="https://erpnext.com/files/v16_stock_summary.png"/>
<img src="https://erpnext.com/files/v16_job_card.png"/>
<img src="https://erpnext.com/files/v16_tasks.png"/>
</details>
### Under the Hood
- [**Frappe Framework**](https://github.com/frappe/frappe): A full-stack web application framework written in Python and JavaScript. The framework provides a robust foundation for building web applications, including a database abstraction layer, user authentication, and a REST API.
- [**Frappe UI**](https://github.com/frappe/frappe-ui): A Vue-based UI library, to provide a modern user interface. The Frappe UI library provides a variety of components that can be used to build single-page applications on top of the Frappe Framework.
## Production Setup
### Managed Hosting
You can try [Frappe Cloud](https://frappecloud.com), a simple, user-friendly, and sophisticated [open-source](https://github.com/frappe/press) platform to host Frappe applications reliably and securely.
It handles installation, setup, upgrades, monitoring, maintenance, and support of your Frappe deployments. It is a fully featured developer platform with an ability to manage and control multiple Frappe deployments.
<div>
<a href="https://erpnext-demo.frappe.cloud/app/home" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://frappe.io/files/try-on-fc-white.png">
<img src="https://frappe.io/files/try-on-fc-black.png" alt="Try on Frappe Cloud" height="28" />
</picture>
</a>
</div>
### Self-Hosted
#### Docker
See [Frappe Docker Documentation](https://github.com/frappe/frappe_docker) for full documentation & FAQ on Docker setup
#### Prerequisites
- [Docker](https://docs.docker.com/get-docker/)
- [Docker Compose v2](https://docs.docker.com/compose/)
- [git](https://docs.github.com/en/get-started/getting-started-with-git/set-up-git)
> For Docker basics and best practices refer to Docker's [documentation](https://docs.docker.com)
### Try on your environment
> **⚠️ Disposable demo only**
>
> **This setup is intended for quick evaluation. Expect to throw the environment away.** You will not be able to install custom apps to this setup. For production deployments, custom configurations, and detailed explanations, see the full documentation.
First clone the repo:
```sh
git clone https://github.com/frappe/frappe_docker
cd frappe_docker
```
Then run:
```sh
docker compose -f pwd.yml up -d
```
Wait for a couple of minutes for ERPNext site to be created or check the `create-site` container logs before opening browser on port `8080`. (username: `Administrator`, password: `admin`)
See [Frappe Docker](https://github.com/frappe/frappe_docker/blob/main/docs/01-getting-started/03-arm64.md) for ARM based docker setup
## Development Setup
### Manual Install
### Full Install
The Easy Way: our install script for bench will install all dependencies (e.g. MariaDB). See https://github.com/frappe/bench for more details.
New passwords will be created for the ERPNext "Administrator" user, the MariaDB root user, and the Frappe user (the script displays the passwords and saves them to ~/frappe_passwords.txt).
New passwords will be created for the ERPNext "Administrator" user, the MariaDB root user, and the frappe user (the script displays the passwords and saves them to ~/frappe_passwords.txt).
### Virtual Image
### Local
You can download a virtual image to run ERPNext in a virtual machine on your local system.
To setup the repository locally follow the steps mentioned below:
- [ERPNext Download](http://erpnext.com/download)
1. Setup bench by following the [Installation Steps](https://frappeframework.com/docs/user/en/installation) and start the server
```
bench start
```
System and user credentials are listed on the download page.
2. In a separate terminal window, run the following commands:
```
# Create a new site
bench new-site erpnext.localhost
```
---
3. Get the ERPNext app and install it
```
# Get the ERPNext app
bench get-app https://github.com/frappe/erpnext
## License
# Install the app
bench --site erpnext.localhost install-app erpnext
```
GNU/General Public License (see [license.txt](license.txt))
4. Open the URL `http://erpnext.localhost:8000/app` in your browser, you should see the app running
## Learning and Community
1. [Frappe School](https://school.frappe.io) - Learn Frappe Framework and ERPNext from the various courses by the maintainers or from the community.
2. [Official documentation](https://docs.erpnext.com/) - Extensive documentation for ERPNext.
3. [Discussion Forum](https://discuss.frappe.io/c/erpnext/6) - Engage with the community of ERPNext users and service providers.
4. [Telegram Group](https://erpnext_public.t.me) - Get instant help from huge community of users.
The ERPNext code is licensed as GNU General Public License (v3) and the Documentation is licensed as Creative Commons (CC-BY-SA-3.0) and the copyright is owned by Frappe Technologies Pvt Ltd (Frappe) and Contributors.
---
## Contributing
1. [Issue Guidelines](https://github.com/frappe/erpnext/wiki/Issue-Guidelines)
2. [Report Security Vulnerabilities](https://erpnext.com/security)
3. [Pull Request Requirements](https://github.com/frappe/erpnext/wiki/Contribution-Guidelines)
4. [Translations](https://crowdin.com/project/frappe)
1. [Report Security Vulnerabilities](https://erpnext.com/security)
1. [Pull Request Requirements](https://github.com/frappe/erpnext/wiki/Contribution-Guidelines)
1. [Translations](https://translate.erpnext.com)
1. [Chart of Accounts](https://charts.erpnext.com)
---
## Logo and Trademark Policy
## Logo and Trademark
Please read our [Logo and Trademark Policy](TRADEMARK_POLICY.md).
The brand name ERPNext and the logo are trademarks of Frappe Technologies Pvt. Ltd.
<br />
<br />
<div align="center" style="padding-top: 0.75rem;">
<a href="https://frappe.io" target="_blank">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://frappe.io/files/Frappe-white.png">
<img src="https://frappe.io/files/Frappe-black.png" alt="Frappe Technologies" height="28"/>
</picture>
</a>
</div>
### Introduction
Frappe Technologies Pvt. Ltd. (Frappe) owns and oversees the trademarks for the ERPNext name and logos. We have developed this trademark usage policy with the following goals in mind:
- Wed like to make it easy for anyone to use the ERPNext name or logo for community-oriented efforts that help spread and improve ERPNext.
- Wed like to make it clear how ERPNext-related businesses and projects can (and cannot) use the ERPNext name and logo.
- Wed like to make it hard for anyone to use the ERPNext name and logo to unfairly profit from, trick or confuse people who are looking for official ERPNext resources.
### Frappe Trademark Usage Policy
Permission from Frappe is required to use the ERPNext name or logo as part of any project, product, service, domain or company name.
We will grant permission to use the ERPNext name and logo for projects that meet the following criteria:
- The primary purpose of your project is to promote the spread and improvement of the ERPNext software.
- Your project is non-commercial in nature (it can make money to cover its costs or contribute to non-profit entities, but it cannot be run as a for-profit project or business).
Your project neither promotes nor is associated with entities that currently fail to comply with the GPL license under which ERPNext is distributed.
- If your project meets these criteria, you will be permitted to use the ERPNext name and logo to promote your project in any way you see fit with one exception: Please do not use ERPNext as part of a domain name.
Use of the ERPNext name and logo is additionally allowed in the following situations:
All other ERPNext-related businesses or projects can use the ERPNext name and logo to refer to and explain their services, but they cannot use them as part of a product, project, service, domain, or company name and they cannot use them in any way that suggests an affiliation with or endorsement by ERPNext or Frappe Technologies or the ERPNext open source project. For example, a consulting company can describe its business as “123 Web Services, offering ERPNext consulting for small businesses,” but cannot call its business “The ERPNext Consulting Company.”
Similarly, its OK to use the ERPNext logo as part of a page that describes your products or services, but it is not OK to use it as part of your company or product logo or branding itself. Under no circumstances is it permitted to use ERPNext as part of a top-level domain name.
We do not allow the use of the trademark in advertising, including AdSense/AdWords.
Please note that it is not the goal of this policy to limit commercial activity around ERPNext. We encourage ERPNext-based businesses, and we would love to see hundreds of them.
When in doubt about your use of the ERPNext name or logo, please contact Frappe Technologies for clarification.
(inspired by WordPress)

View File

@@ -1,7 +1,7 @@
# Security Policy
The ERPNext team and community take security issues seriously. To report a security issue, please go through the information mentioned [here](https://frappe.io/security).
The ERPNext team and community take security issues seriously. To report a security issue, fill out the form at [https://erpnext.com/security/report](https://erpnext.com/security/report).
You can help us make ERPNext and all its users more secure by following the [Reporting guidelines](https://frappe.io/security).
You can help us make ERPNext and all it's users more secure by following the [Reporting guidelines](https://erpnext.com/security).
We appreciate your efforts to responsibly disclose your findings. We'll endeavor to respond quickly, and will keep you updated throughout the process.
We appreciate your efforts to responsibly disclose your findings. We'll endeavor to respond quickly, and will keep you updated throughout the process.

View File

@@ -1,37 +0,0 @@
## Logo and Trademark Policy
The brand name ERPNext and the logo are trademarks of Frappe Technologies Pvt. Ltd.
### Introduction
Frappe Technologies Pvt. Ltd. (Frappe) owns and oversees the trademarks for the ERPNext name and logos. We have developed this trademark usage policy with the following goals in mind:
- Wed like to make it easy for anyone to use the ERPNext name or logo for community-oriented efforts that help spread and improve ERPNext.
- Wed like to make it clear how ERPNext-related businesses and projects can (and cannot) use the ERPNext name and logo.
- Wed like to make it hard for anyone to use the ERPNext name and logo to unfairly profit from, trick or confuse people who are looking for official ERPNext resources.
### Frappe Trademark Usage Policy
Permission from Frappe is required to use the ERPNext name or logo as part of any project, product, service, domain or company name.
We will grant permission to use the ERPNext name and logo for projects that meet the following criteria:
- The primary purpose of your project is to promote the spread and improvement of the ERPNext software.
- Your project is non-commercial in nature (it can make money to cover its costs or contribute to non-profit entities, but it cannot be run as a for-profit project or business).
- Your project neither promotes nor is associated with entities that currently fail to comply with the GPL license under which ERPNext is distributed.
If your project meets these criteria, you will be permitted to use the ERPNext name and logo to promote your project in any way you see fit with one exception: Please do not use ERPNext as part of a domain name.
Use of the ERPNext name and logo is additionally allowed in the following situations:
All other ERPNext-related businesses or projects can use the ERPNext name and logo to refer to and explain their services, but they cannot use them as part of a product, project, service, domain, or company name and they cannot use them in any way that suggests an affiliation with or endorsement by ERPNext or Frappe Technologies or the ERPNext open source project. For example, a consulting company can describe its business as “123 Web Services, offering ERPNext consulting for small businesses,” but cannot call its business “The ERPNext Consulting Company.”
Similarly, its OK to use the ERPNext logo as part of a page that describes your products or services, but it is not OK to use it as part of your company or product logo or branding itself. Under no circumstances is it permitted to use ERPNext as part of a top-level domain name.
We do not allow the use of the trademark in advertising, including AdSense/AdWords.
Please note that it is not the goal of this policy to limit commercial activity around ERPNext. We encourage ERPNext-based businesses, and we would love to see hundreds of them.
When in doubt about your use of the ERPNext name or logo, please contact Frappe Technologies for clarification.
(inspired by WordPress)

View File

@@ -1,5 +0,0 @@
**/setup/setup_wizard/data/uom_data.json,erpnext.gettext.extractors.uom_data.extract
**/setup/doctype/incoterm/incoterms.csv,erpnext.gettext.extractors.incoterms.extract
**/setup/setup_wizard/data/*.txt,erpnext.gettext.extractors.lines_from_txt_file.extract
**.tsx,frappe.gettext.extractors.html_template.extract
**.ts,frappe.gettext.extractors.html_template.extract
1 **/setup/setup_wizard/data/uom_data.json erpnext.gettext.extractors.uom_data.extract
2 **/setup/doctype/incoterm/incoterms.csv erpnext.gettext.extractors.incoterms.extract
3 **/setup/setup_wizard/data/*.txt erpnext.gettext.extractors.lines_from_txt_file.extract
4 **.tsx frappe.gettext.extractors.html_template.extract
5 **.ts frappe.gettext.extractors.html_template.extract

View File

@@ -1 +0,0 @@
VITE_BASE_NAME="banking"

24
banking/.gitignore vendored
View File

@@ -1,24 +0,0 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

View File

@@ -1,73 +0,0 @@
# React + TypeScript + Vite
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
## React Compiler
The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
## Expanding the ESLint configuration
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
```js
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Remove tseslint.configs.recommended and replace with this
tseslint.configs.recommendedTypeChecked,
// Alternatively, use this for stricter rules
tseslint.configs.strictTypeChecked,
// Optionally, add this for stylistic rules
tseslint.configs.stylisticTypeChecked,
// Other configs...
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```
You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
```js
// eslint.config.js
import reactX from 'eslint-plugin-react-x'
import reactDom from 'eslint-plugin-react-dom'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Enable lint rules for React
reactX.configs['recommended-typescript'],
// Enable lint rules for React DOM
reactDom.configs.recommended,
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```

View File

@@ -1,24 +0,0 @@
import js from "@eslint/js";
import globals from "globals";
import reactHooks from "eslint-plugin-react-hooks";
import reactRefresh from "eslint-plugin-react-refresh";
import tseslint from "typescript-eslint";
import { defineConfig, globalIgnores } from "eslint/config";
export default defineConfig([
globalIgnores(["dist"]),
{
files: ["**/*.{ts,tsx}"],
extends: [
js.configs.recommended,
tseslint.configs.recommended,
reactHooks.configs.flat.recommended,
reactRefresh.configs.vite,
],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
onlyExportComponents: false,
},
]);

View File

@@ -1,50 +0,0 @@
<!doctype html>
<html lang="{{ lang }}" dir="{{layout_direction}}">
<head>
<meta charset="UTF-8" />
<!-- Chrome, Firefox OS and Opera -->
<meta name="theme-color" content="#0089FF">
<!-- Windows Phone -->
<meta name="msapplication-navbutton-color" content="#0089FF">
<!-- iOS Safari -->
<meta name="apple-mobile-web-app-status-bar-style" content="#0089FF">
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
<meta name="author" content="">
<meta name="viewport" content="width=device-width, initial-scale=1.0,
maximum-scale=1.0, minimum-scale=1.0, user-scalable=no, minimal-ui">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="white">
<meta name="mobile-web-app-capable" content="yes">
<link rel="shortcut icon" href="{{ favicon or ' /assets/erpnext/images/erpnext-favicon.svg' }}" type="image/x-icon">
<link rel="icon" href="{{ favicon or ' /assets/erpnext/images/erpnext-favicon.svg' }}" type="image/x-icon">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Banking | {{ app_name }}</title>
</head>
<body>
<div id="root"></div>
<script>window.csrf_token = '{{ frappe.session.csrf_token }}';
if (!window.frappe) window.frappe = {};
frappe.boot = JSON.parse({{ boot }});
frappe.boot.layout_direction = "{{ layout_direction }}";
frappe._translations_loaded = fetch(
`/api/method/frappe.translate.get_boot_translations?v=${frappe.boot.translations_version}&lang=${frappe.boot.lang}`,
{
credentials: "same-origin",
headers: {
"X-Frappe-CSRF-Token": frappe.csrf_token,
"Accept": "application/json"
}
}
).then(r => r.json()).then(data => {
frappe._messages = data.message || {};
}).catch(() => { });
</script>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

View File

@@ -1,66 +0,0 @@
{
"name": "banking",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build --base=/assets/erpnext/banking/ && yarn copy-html-entry",
"lint": "eslint .",
"preview": "vite preview",
"copy-html-entry": "cp ../erpnext/public/banking/index.html ../erpnext/www/banking.html"
},
"dependencies": {
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@tailwindcss/vite": "^4.3.0",
"@tanstack/react-table": "^8.21.3",
"@tanstack/react-virtual": "^3.13.24",
"@vitejs/plugin-react": "^6.0.1",
"chrono-node": "^2.9.1",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"date-fns": "^4.1.0",
"dayjs": "^1.11.20",
"frappe-react-sdk": "^1.15.0",
"fuse.js": "^7.3.0",
"jotai": "^2.20.0",
"jotai-family": "^1.0.1",
"lodash.isplainobject": "^4.0.6",
"lucide-react": "^1.14.0",
"radix-ui": "^1.4.3",
"react": "^19.2.6",
"react-currency-input-field": "^4.0.5",
"react-day-picker": "9.14.0",
"react-dom": "^19.2.6",
"react-dropzone": "^15.0.0",
"react-hook-form": "^7.75.0",
"react-hotkeys-hook": "^5.3.2",
"react-markdown": "^10.1.0",
"react-router": "^7.15.0",
"react-router-dom": "^7.15.0",
"react-virtuoso": "^4.18.6",
"rehype-raw": "^7.0.0",
"remark-gfm": "^4.0.1",
"sonner": "^2.0.7",
"tailwind-merge": "^3.5.0",
"tailwindcss": "^4.3.0",
"tw-animate-css": "^1.4.0",
"usehooks-ts": "^3.1.1",
"vite": "^8.0.11"
},
"devDependencies": {
"@eslint/js": "^9.39.1",
"@types/node": "^25.3.0",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
"eslint": "^9.39.1",
"eslint-plugin-react-hooks": "^7.1.1",
"eslint-plugin-react-refresh": "^0.4.24",
"globals": "^16.5.0",
"typescript": "~5.9.3",
"typescript-eslint": "^8.48.0"
}
}

View File

@@ -1,13 +0,0 @@
const common_site_config = require('../../../sites/common_site_config.json');
const { webserver_port } = common_site_config;
export default {
'^/(app|api|assets|files|private)': {
target: `http://127.0.0.1:${webserver_port}`,
ws: true,
router: function(req) {
const site_name = req.headers.host.split(':')[0];
return `http://${site_name}:${webserver_port}`;
}
}
};

View File

@@ -1,65 +0,0 @@
import { useEffect } from 'react'
import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom'
import { FrappeProvider } from 'frappe-react-sdk'
import { Toaster } from '@/components/ui/sonner'
import BankReconciliation from '@/pages/BankReconciliation'
import { TooltipProvider } from './components/ui/tooltip'
import BankStatementImporter from '@/pages/BankStatementImporter'
import { LucideProvider } from 'lucide-react'
import { ThemeProvider } from './components/ui/theme-provider'
import ViewBankStatementImportLog from './pages/ViewBankStatementImportLog'
import BankStatementImporterContainer from './pages/BankStatementImporterContainer'
function App() {
useEffect(() => {
// Check if user is logged in by checking the Cookie "user_id"
// In Frappe, unauthenticated users are "Guest"
const userId = document.cookie?.split('; ').find(row => row.startsWith('user_id='))?.split('=')[1]?.trim()
const isLoggedIn = userId !== 'Guest'
if (!isLoggedIn) {
if (import.meta.env.DEV) {
return
}
// Redirect to Frappe login page
window.location.href = '/login?redirect-to=/banking'
return
}
}, [])
return (
<LucideProvider
strokeWidth={1.5}
>
<TooltipProvider>
<FrappeProvider
swrConfig={{
errorRetryCount: 2
}}
socketPort={import.meta.env.VITE_SOCKET_PORT}
siteName={window.frappe?.boot?.sitename ?? import.meta.env.VITE_SITE_NAME}>
<ThemeProvider
defaultTheme={window.frappe?.boot?.desk_theme ?? "Automatic"}
>
{window.frappe?.boot?.user?.name && window.frappe?.boot?.user?.name !== 'Guest' &&
<BrowserRouter basename={import.meta.env.VITE_BASE_NAME ? `/${import.meta.env.VITE_BASE_NAME}` : ''}>
<Routes>
<Route index element={<BankReconciliation />} />
<Route path="/statement-importer" element={<BankStatementImporterContainer />}>
<Route index element={<BankStatementImporter />} />
<Route path=":id" element={<ViewBankStatementImportLog />} />
</Route>
<Route path="*" element={<Navigate to="/" />} />
</Routes>
</BrowserRouter>
}
<Toaster richColors />
</ThemeProvider>
</FrappeProvider>
</TooltipProvider>
</LucideProvider>
)
}
export default App

View File

@@ -1,228 +0,0 @@
import { Button } from "@/components/ui/button"
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command"
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"
import { useCurrentCompany } from "@/hooks/useCurrentCompany"
import _ from "@/lib/translate"
import { cn } from "@/lib/utils"
import { useFrappeGetDocList } from "frappe-react-sdk"
import Fuse from "fuse.js"
import { ChevronDownIcon } from "lucide-react"
import { useLayoutEffect, useMemo, useRef, useState } from "react"
import { FormControl } from "../ui/form"
export interface AccountsDropdownProps {
root_type?: ('Asset' | 'Liability' | 'Equity' | 'Income' | 'Expense')[],
report_type?: 'Balance Sheet' | 'Profit and Loss',
account_type?: string[],
value?: string,
onChange?: (value: string) => void,
readOnly?: boolean,
disabled?: boolean,
company?: string,
filterFunction?: (account: Account) => boolean,
// If true, the component will be wrapped in a FormControl component
useInForm?: boolean,
buttonClassName?: string,
size?: 'sm' | 'md' | 'lg',
}
/**
* Component to select an account - supports fuzzy search
* @param root_type - The root type of the account
* @param report_type - The report type of the account
* @param account_type - The type of the account
* @param value - The value of the account field
* @param onChange - The function to call when the value changes
* @returns
*/
const AccountsDropdown = ({ root_type, report_type, account_type, value, onChange, readOnly, disabled, company, filterFunction, useInForm, buttonClassName, size = 'md' }: AccountsDropdownProps) => {
const { data } = useGetAccounts(root_type, report_type, account_type, company, filterFunction)
const groupedAccounts = useMemo(() => {
if (!data) return []
const grouped: Record<string, Account[]> = data.reduce((acc, account) => {
const parentAccount = account.parent_account
if (!parentAccount) return acc
if (!acc[parentAccount]) {
acc[parentAccount] = []
}
acc[parentAccount].push(account)
return acc
}, {} as Record<string, Account[]>)
return Object.entries(grouped).map(([parentAccount, accounts]) => ({
// Remove the last abbreviation from the parent account name like "Assets - TCC" should be "Assets", and "Assets - USD - TCC" should be "Assets - USD"
parentAccount: parentAccount.split(" - ").slice(0, -1).join(" - "),
accounts
}))
}, [data])
const searchIndex = useMemo(() => {
if (!data) {
return null
}
return new Fuse(data, {
keys: ['name'],
threshold: 0.5,
includeScore: true
})
}, [data])
const [search, setSearch] = useState("")
const recommendedAccounts = useMemo(() => {
if (!searchIndex || !search) {
return []
}
return searchIndex.search(search).map((result) => result.item)
}, [searchIndex, search])
const [open, setOpen] = useState(false)
const onOpenChange = (open: boolean) => {
if (readOnly) return
setOpen(open)
// setSearch("")
}
const onSelect = (value: string) => {
onChange?.(value)
setOpen(false)
setSearch(value)
}
const buttonRef = useRef<HTMLButtonElement>(null)
const [width, setWidth] = useState(320)
useLayoutEffect(() => {
if (buttonRef.current) {
setWidth(buttonRef.current.getBoundingClientRect().width)
}
}, [])
return (
<Popover open={open} onOpenChange={onOpenChange} modal={true}>
<PopoverTrigger asChild>
{useInForm ? <FormControl>
<Button
variant="subtle"
type='button'
size={size}
role="combobox"
ref={buttonRef}
tabIndex={0}
disabled={disabled || readOnly}
aria-readonly={readOnly}
aria-expanded={open}
className={cn("w-full justify-between font-normal",
readOnly ? "bg-surface-gray-1 pointer-events-none" : ""
, buttonClassName)}>
{value || _('Select Account')}
<ChevronDownIcon className="ms-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</FormControl>
: <Button
variant="subtle"
size={size}
type='button'
role="combobox"
ref={buttonRef}
disabled={disabled}
aria-expanded={open}
className={cn("w-full justify-between font-normal",
readOnly ? "bg-surface-gray-1" : ""
)}>
{value || _('Select Account')}
<ChevronDownIcon className="ms-2 h-4 w-4 shrink-0 opacity-50" />
</Button>}
</PopoverTrigger>
<PopoverContent className="p-0" style={{ minWidth: width }} align="start">
<Command shouldFilter={false} className="w-full">
<CommandInput placeholder={_("Search account...")} onValueChange={setSearch} value={search} />
<CommandList>
<CommandEmpty>{_("No accounts found.")}</CommandEmpty>
{recommendedAccounts.length > 0 && (
<CommandGroup heading={_("Search Results")}>
{recommendedAccounts.map((account) => (
<CommandItem key={account.name} onSelect={() => onSelect(account.name)}>{account.name}</CommandItem>
))}
</CommandGroup>
)}
{!search && groupedAccounts.map((group) => (
<CommandGroup key={group.parentAccount} heading={group.parentAccount}>
{group.accounts.map((account) => (
<CommandItem key={account.name} onSelect={() => onSelect(account.name)}>{account.name}</CommandItem>
))}
</CommandGroup>
))}
</CommandList>
</Command>
</PopoverContent>
</Popover>
)
}
interface Account {
name: string
root_type: 'Asset' | 'Liability' | 'Equity' | 'Income' | 'Expense'
report_type: 'Balance Sheet' | 'Profit and Loss'
account_type: string
account_currency: string
parent_account: string
}
export const useGetAccounts = (root_type?: ('Asset' | 'Liability' | 'Equity' | 'Income' | 'Expense')[], report_type?: 'Balance Sheet' | 'Profit and Loss', account_type?: string[], company?: string,
filterFunction?: (account: Account) => boolean) => {
const currentCompany = useCurrentCompany()
const { data, isLoading, error, mutate } = useFrappeGetDocList<Account>("Account", {
fields: ["name", "root_type", "report_type", "account_type", "account_currency", "parent_account"],
filters: [["is_group", "=", 0], ["disabled", "=", 0], ["company", "=", company ?? currentCompany]],
limit: 1000,
orderBy: {
"field": "root_type",
// @ts-expect-error - we can pass in additional fields to orderBy
"order": "asc, account_number asc"
}
}, `accounts-${company ?? currentCompany}`, {
revalidateIfStale: false,
revalidateOnFocus: false,
revalidateOnReconnect: false,
})
const filteredData = useMemo(() => {
return data?.filter((account) => {
if (root_type && !root_type.includes(account.root_type)) return false
if (report_type && account.report_type !== report_type) return false
if (account_type && !account_type.includes(account.account_type)) return false
if (filterFunction) return filterFunction(account)
return true
}) ?? []
}, [data, root_type, report_type, account_type, filterFunction])
return { data: filteredData, isLoading, error, mutate }
}
export default AccountsDropdown

View File

@@ -1,26 +0,0 @@
import { cn } from '@/lib/utils'
import { SelectedBank } from '../features/BankReconciliation/bankRecAtoms'
import { useTheme } from '../ui/theme-provider'
import { Landmark } from 'lucide-react'
import { H4 } from '../ui/typography'
const BankLogo = ({ bank, className, imageClassName, iconSize = '18px', iconClassName }: { bank?: SelectedBank | null, className?: string, imageClassName?: string, iconSize?: string, iconClassName?: string }) => {
const { themeValue } = useTheme()
return (
<div className={cn('h-6 flex items-center gap-1', className)}> {bank?.logo ? <img
src={`/assets/erpnext/images/bank-logos/${themeValue === 'Dark' ? (bank.logoDark ?? bank.logo) : bank.logo}`}
alt={bank.bank || bank.name || ''}
className={cn("h-6 max-w-22 me-auto object-contain", imageClassName, {
'dark:invert dark:brightness-0': bank.darkModeInvert
}, bank.logoClassName)}
/> : <>
<Landmark size={iconSize} className={iconClassName} />
<H4 className={cn("text-xs -mb-0.5", {
})}>{bank?.bank ?? ''}</H4>
</>
}</div>
)
}
export default BankLogo

View File

@@ -1,17 +0,0 @@
import { CheckCircle } from 'lucide-react'
import { Progress } from '../ui/progress'
import _ from '@/lib/translate'
const FileUploadBanner = ({
uploadProgress,
}: { uploadProgress: number }) => {
return <div className="flex items-center justify-center flex-col gap-4">
<div className="flex flex-col items-center gap-4">
<CheckCircle size={48} className="text-ink-green-3" />
<span className="text-ink-gray-8 text-p-base">{_("The document has been created and reconciled. Uploading attachments...")}</span>
<Progress value={Math.round(uploadProgress * 100)} size="lg" />
</div>
</div>
}
export default FileUploadBanner

View File

@@ -1,301 +0,0 @@
import { useDocType } from "@/hooks/useDocType";
import { getSystemDefault, slug } from "@/lib/frappe";
import { Filter, useFrappeGetCall } from "frappe-react-sdk"
import { useLayoutEffect, useMemo, useRef, useState } from "react";
import { canCreateDocument } from "@/lib/permissions";
import { useDebounceValue } from "usehooks-ts";
import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover";
import { FormControl } from "../ui/form";
import { ChevronDownIcon, ExternalLink } from "lucide-react";
import { Button } from "../ui/button";
import { cn } from "@/lib/utils";
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "../ui/command";
import _ from "@/lib/translate";
import ErrorBanner from "../ui/error-banner";
import MarkdownRenderer from "../ui/markdown";
export interface ResultItem {
value: string,
description: string,
label?: string
}
export interface LinkFieldComboboxProps {
/** DocType to be fetched */
doctype: string;
/** Filters to be applied. Default: none */
filters?: Filter[]
/** Number of records to paginate with. Default: Comes from System Settings or 10 */
limit?: number;
/**
* API to call to fetch records.
*
* Default: `frappe.desk.search.search_link`
*
* If you want to use a custom API, you can pass the path to the API here.
*
* The API should return a list of documents in the following format:
* [{value: string, description: string, label?: string}] - where the value is the ID of the document.
*
* If the API sends a label, it will be used as the label in the dropdown.
*/
searchAPIPath?: string;
/**
* Field you want to search against in the doctype.
*
* Default: `name`
*
* If you want to search against a different field, you can pass the fieldname here.
*
* If you want to search against multiple fields, you can try using the `searchAPIPath` prop to call a custom API,
* or use a custom query in the `customQuery` prop.
*/
searchfield?: string;
/**
* Custom query to be used to fetch records.
*
* If you want to use a custom query, you can pass the query here.
*
* The query should be in the following format:
* {
* query: string,
* filters: {
* fieldname: string,
* operator: string,
* value: string
* }
* }
*/
customQuery?: {
/** Path to function for the query.
*
* Refer: Item/Supplier query
*/
query: string,
/** Filters are usually an object instead of an array in a custom query */
filters?: Record<string, string | number | boolean>,
},
/**
* Used for certain queries where a reference doctype is needed.
*
* For example when searching a supplier in a "Purchase Invoice", the reference_doctype is "Purchase Invoice"
*/
reference_doctype?: string,
/** Placeholder for the dropdown. Default: `doctype` */
placeholder?: string;
/**
* Should the field be read-only.
*/
readOnly?: boolean;
/** Should the field be disabled. Default: false */
disabled?: boolean;
/**
* Function to filter the options based on the input value/other criteria.
*
* For example, you might want to limit the companies shown in the dropdown since they have been already added (like in Cost Codes)
*/
filterFn?: (option: ResultItem, inputValue: string) => boolean,
value?: string,
onChange: (value: string) => void,
/** If true, the component will be wrapped in a FormControl component */
useInForm?: boolean,
/** Button Class name */
buttonClassName?: string,
size?: 'sm' | 'md' | 'lg',
}
const LinkFieldCombobox = ({
doctype,
reference_doctype,
filters = [],
value,
onChange,
readOnly,
disabled,
filterFn,
placeholder = `Select ${doctype}`,
customQuery,
searchfield,
searchAPIPath = "frappe.desk.search.search_link",
limit,
useInForm,
buttonClassName,
size = 'md'
}: LinkFieldComboboxProps) => {
const pageLimit = useMemo(() => limit || getSystemDefault('link_field_results_limit') || 20, [limit])
/** Load the Doctype meta so that we can determine the search fields + the name of the title field */
const { data: meta } = useDocType(doctype)
const userCanCreate = useMemo(() => canCreateDocument(doctype), [doctype])
const [open, setOpen] = useState(false)
const [searchInput, setSearchInput] = useDebounceValue('', 400)
const { data: linkTitleData } = useFrappeGetCall('frappe.client.get_value', {
doctype,
filters: JSON.stringify({
name: value
}),
fieldname: meta?.title_field
}, (meta?.show_title_field_in_link ?? false) && (meta?.title_field) && value ? `link_title::${doctype}::${value}` : null, {
revalidateIfStale: false,
revalidateOnFocus: false,
revalidateOnReconnect: false,
})
const linkTitle = meta?.title_field && meta?.show_title_field_in_link ? (linkTitleData?.message?.[meta?.title_field] ?? value) : value
const buttonRef = useRef<HTMLButtonElement>(null)
const [width, setWidth] = useState(320)
useLayoutEffect(() => {
if (buttonRef.current) {
setWidth(buttonRef.current.getBoundingClientRect().width)
}
}, [])
const { data, error, isLoading } = useFrappeGetCall<{ message: ResultItem[] }>(searchAPIPath, {
doctype,
txt: searchInput,
page_length: pageLimit,
query: customQuery?.query,
searchfield,
filters: JSON.stringify(customQuery?.filters || filters || []),
reference_doctype,
}, () => {
if (!open) {
return null
} else {
let key = `${searchAPIPath}_${doctype}_${searchInput}`
if (pageLimit) {
key += `_${pageLimit}`
}
if (customQuery?.filters) {
key += `_${JSON.stringify(customQuery.filters)}`
} else if (filters) {
key += `_${JSON.stringify(filters)}`
}
if (customQuery && customQuery.query) {
key += `_${customQuery.query}`
}
if (reference_doctype) {
key += `_${reference_doctype}`
}
if (searchfield && searchfield !== 'name') {
key += `_${searchfield}`
}
return key
}
}, {
revalidateOnFocus: false,
revalidateIfStale: false,
shouldRetryOnError: false,
revalidateOnReconnect: false,
})
const onOpenChange = (open: boolean) => {
if (readOnly) return
setOpen(open)
setSearchInput("")
}
const onSelect = (value: string) => {
onChange?.(value)
setOpen(false)
}
const items = filterFn ? data?.message?.slice(0, 50).filter((item) => filterFn(item, searchInput)) : data?.message
const buttonProps = {
variant: "subtle",
type: 'button',
size: size,
role: "combobox",
"data-state": open ? "open" : "closed",
ref: buttonRef,
tabIndex: 0,
disabled: disabled || readOnly,
"aria-expanded": open,
"aria-readonly": readOnly,
className: cn("w-full justify-between font-normal group border border-transparent outline-none",
"data-[state=open]:bg-surface-white data-[state=open]:border-outline-gray-4 data-[state=open]:shadow-sm",
readOnly ? "bg-surface-gray-1" : "",
// Placeholder and value styling
linkTitle ? "text-ink-gray-7" : "text-ink-gray-4",
buttonClassName)
} as const
return (
<Popover open={open} onOpenChange={onOpenChange} modal={true}>
<PopoverTrigger asChild>
{useInForm ? <FormControl>
<Button {...buttonProps}>
{linkTitle || placeholder}
<div className="flex items-center gap-1">
{value && <a href={`/desk/${slug(doctype)}/${value}`} target="_blank" className="group-hover:block hidden">
<ExternalLink className="size-4 shrink-0 opacity-50" />
</a>}
<ChevronDownIcon className="ms-2 size-4 shrink-0" />
</div>
</Button>
</FormControl>
: <Button {...buttonProps}>
{linkTitle || placeholder}
<div className="flex items-center gap-1">
{value && <a href={`/desk/${slug(doctype)}/${value}`} target="_blank" className="group-hover:block hidden">
<ExternalLink className="size-4 shrink-0 opacity-50" />
</a>}
<ChevronDownIcon className="ms-2 size-4 shrink-0" />
</div>
</Button>}
</PopoverTrigger>
<PopoverContent className="p-0" style={{ minWidth: width }} align="start">
{error && <ErrorBanner error={error} />}
<Command shouldFilter={false} className="w-full">
<CommandInput placeholder={placeholder} onValueChange={setSearchInput} />
<CommandList>
<CommandEmpty>{isLoading ? _("Loading...") : _("No results found.")}</CommandEmpty>
<CommandGroup>
{items?.map((result) => (
<CommandItem key={result.value} onSelect={() => onSelect(result.value)} className="flex flex-col items-start gap-0.5">
<span className="font-medium">
{result.label || result.value}
</span>
{result.description && <span className="text-xs text-ink-gray-5">
<MarkdownRenderer content={result.description} />
</span>}
</CommandItem>
))}
{userCanCreate && <CommandItem asChild>
<a href={`/desk/${slug(doctype)}/new-${slug(doctype)}-1`}
target="_blank"
className="hover:underline underline-offset-4 cursor-pointer flex justify-between items-center">
{_("Create New {0}", [doctype])}
<ExternalLink />
</a>
</CommandItem>}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
)
}
export default LinkFieldCombobox

View File

@@ -1,82 +0,0 @@
import { Select, SelectValue, SelectTrigger, SelectContent, SelectItem } from '@/components/ui/select'
import _ from '@/lib/translate'
import { useFrappeGetDocList } from 'frappe-react-sdk'
import { ComponentProps, useMemo } from 'react'
import { FormControl } from '../ui/form'
export type PartyTypeDropdownProps = {
value?: string,
onChange?: (value: string) => void,
readOnly?: boolean,
disabled?: boolean,
/** Set this to order the parties so that suggested types are shown first */
type?: 'Receivable' | 'Payable'
/** Set this to true if you want to hide other options by type. e.g. - if type is Receivable, Payable options like "Supplier" will be hidden */
hideOptionsByType?: boolean,
valueProps?: ComponentProps<typeof SelectValue>,
triggerProps?: ComponentProps<typeof SelectTrigger>,
// If true, the component will be wrapped in a FormControl component
useInForm?: boolean
}
const PartyTypeDropdown = ({ value, onChange, readOnly, disabled, type, hideOptionsByType, valueProps, triggerProps, useInForm }: PartyTypeDropdownProps) => {
const { data } = useFrappeGetDocList("Party Type", {
fields: ['name', 'account_type'],
orderBy: {
field: 'creation',
order: 'asc'
}
}, `party_types`, {
revalidateIfStale: false,
revalidateOnFocus: false,
revalidateOnReconnect: false,
})
const filteredData = useMemo(() => {
let options = data ?? [
{ name: "Customer", account_type: "Receivable" },
{ name: "Supplier", account_type: "Payable" },
{ name: "Employee", account_type: "Payable" },
{ name: "Shareholder", account_type: "Payable" },
]
if (hideOptionsByType && type) {
options = options.filter((option) => option.account_type === type)
}
// Order by type if type is set
if (type) {
options = options.sort((a) => a.account_type === type ? -1 : 1)
}
return options
}, [data, type, hideOptionsByType])
const onSelect = (value: string) => {
if (!readOnly) {
onChange?.(value)
}
}
return (
<Select onValueChange={onSelect} value={value} disabled={disabled}>
{useInForm ? <FormControl>
<SelectTrigger tabIndex={0} aria-readonly={readOnly} disabled={disabled || readOnly} {...triggerProps}>
<SelectValue placeholder={_("Type")} aria-readonly={readOnly} {...valueProps} />
</SelectTrigger>
</FormControl> : <SelectTrigger tabIndex={0} {...triggerProps}>
<SelectValue placeholder={_("Type")} aria-readonly={readOnly} {...valueProps} />
</SelectTrigger>
}
<SelectContent>
{filteredData.map((option) => (
<SelectItem key={option.name} value={option.name}>{option.name}</SelectItem>
))}
</SelectContent>
</Select>
)
}
export default PartyTypeDropdown

View File

@@ -1,475 +0,0 @@
import { Button } from '@/components/ui/button'
import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
import _ from '@/lib/translate'
import { useAtomValue, useSetAtom } from 'jotai'
import { ArrowDownRight, ArrowRightLeftIcon, ArrowUpRight, CalendarIcon, CircleXIcon, GitCompareIcon, HistoryIcon, LandmarkIcon, Loader2Icon, ReceiptIcon, ReceiptTextIcon, UserIcon, WalletIcon } from 'lucide-react'
import { useMemo, useState } from 'react'
import { ActionLogItem, ActionLog as ActionLogType, bankRecActionLog, bankRecDateAtom, bankRecMatchFilters, SelectedBank, selectedBankAccountAtom } from '../BankReconciliation/bankRecAtoms'
import { useHotkeys } from 'react-hotkeys-hook'
import { useGetBankAccounts } from '../BankReconciliation/utils'
import { getCompanyCurrency } from '@/lib/company'
import { formatCurrency } from '@/lib/numbers'
import dayjs from 'dayjs'
import { cn } from '@/lib/utils'
import { formatDate } from '@/lib/date'
import { Separator } from '@/components/ui/separator'
import { slug } from '@/lib/frappe'
import { PaymentEntry } from '@/types/Accounts/PaymentEntry'
import { JournalEntry } from '@/types/Accounts/JournalEntry'
import { HoverCard, HoverCardContent, HoverCardTrigger } from '@/components/ui/hover-card'
import { Table, TableCell, TableBody, TableHead, TableHeader, TableRow } from '@/components/ui/table'
import { AlertDialog, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from '@/components/ui/alert-dialog'
import { useFrappePostCall, useSWRConfig } from 'frappe-react-sdk'
import { toast } from 'sonner'
import { getErrorMessage } from '@/lib/frappe'
import ErrorBanner from '@/components/ui/error-banner'
import SelectedTransactionDetails from '../BankReconciliation/SelectedTransactionDetails'
import { Empty, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from '@/components/ui/empty'
import BankLogo from '@/components/common/BankLogo'
const ActionLog = () => {
const [isOpen, setIsOpen] = useState(false)
useHotkeys('meta+z', () => {
setIsOpen(true)
}, {
enabled: true,
enableOnFormTags: false,
preventDefault: true
})
return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<Tooltip>
<TooltipTrigger asChild>
<DialogTrigger asChild>
<Button variant={'outline'} isIconButton size='md'>
<HistoryIcon />
</Button>
</DialogTrigger>
</TooltipTrigger>
<TooltipContent>
{_("Reconciliation History")}
</TooltipContent>
</Tooltip>
<DialogContent className='min-w-[90vw]'>
<DialogHeader>
<DialogTitle>{_("Reconciliation History")}</DialogTitle>
<DialogDescription>{_("View all reconciliation actions taken in this session.")}</DialogDescription>
</DialogHeader>
<ActionLogDialogContent />
<DialogFooter>
<DialogClose asChild>
<Button variant={'outline'} size='md' onClick={() => setIsOpen(false)}>{_("Close")}</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
)
}
const ActionLogDialogContent = () => {
const actionLog = useAtomValue(bankRecActionLog)
return <div className='flex flex-col gap-2'>
{actionLog.map((action) => (
<div key={action.timestamp} className='flex flex-col gap-1'>
<ActionGroupHeader action={action} />
<div>
<div className='ms-2 border-s border-s-outline-gray-2 py-1'>
<div className='ms-5'>
{action.items.map((item, index) => (
<Row
item={item}
key={item.bankTransaction.name}
index={index}
action={action}
isLast={index === action.items.length - 1} />
))}
</div>
</div>
</div>
</div>
))}
{actionLog.length === 0 && <Empty>
<EmptyMedia>
<HistoryIcon />
</EmptyMedia>
<EmptyHeader>
<EmptyTitle>{_("No reconciliation actions found")}</EmptyTitle>
<EmptyDescription>{_("You have not performed any reconciliations in this session yet.")}</EmptyDescription>
</EmptyHeader>
</Empty>}
</div>
}
const ActionGroupHeader = ({ action }: { action: ActionLogType }) => {
const label = useMemo(() => {
switch (action.type) {
case 'match':
return _("Matched")
case 'payment':
if (action.isBulk) {
return _("Bulk Payment")
}
return _("Payment")
case 'transfer':
if (action.isBulk) {
return _("Bulk Transfer")
}
return _("Transfer")
case 'bank_entry':
if (action.isBulk) {
return _("Bulk Bank Entry")
}
return _("Bank Entry")
default:
return _("Action")
}
}, [action])
return <div className='flex items-center gap-2 text-ink-gray-5'>
{action.type === 'match' && <GitCompareIcon className='w-4 h-4' />}
{action.type === 'payment' && <ReceiptIcon className='w-4 h-4' />}
{action.type === 'transfer' && <ArrowRightLeftIcon className='w-4 h-4' />}
{action.type === 'bank_entry' && <LandmarkIcon className='w-4 h-4' />}
<span className='flex items-center gap-2 text-sm'>
{label} - {dayjs(action.timestamp).fromNow()}
</span>
</div>
}
const Row = ({ item, index, isLast, action }: { item: ActionLogItem, index: number, isLast: boolean, action: ActionLogType }) => {
const isWithdrawal = item.bankTransaction.withdrawal && item.bankTransaction.withdrawal > 0
const { banks } = useGetBankAccounts()
const bank = useMemo(() => {
if (item.bankTransaction.bank_account) {
return banks?.find((bank) => bank.name === item.bankTransaction.bank_account)
}
return null
}, [item.bankTransaction.bank_account, banks])
const amount = item.bankTransaction.withdrawal ? item.bankTransaction.withdrawal : item.bankTransaction.deposit
const currency = item.bankTransaction.currency || getCompanyCurrency(item.bankTransaction.company ?? '')
return <div className='flex items-center gap-2 group'>
<div className={cn('p-3.5 group-hover:bg-surface-gray-1 border-s border-e border-t w-full', isLast ? 'rounded-b border-b' : '', index === 0 ? 'rounded-t' : '')}>
<div className='flex justify-between items-center'>
<div className='flex flex-col gap-2'>
<p className='text-p-base'>{item.bankTransaction.description}</p>
<div className='flex items-center gap-3'>
<div className='flex gap-2 items-center'>
<BankLogo bank={bank} className='h-4 mb-0' iconSize='16px' />
<span className='text-sm text-ink-gray-5'>{item.bankTransaction.bank_account}</span>
</div>
<Separator orientation='vertical' />
<div className='flex items-center gap-2 text-ink-gray-5 text-sm' title={_("Transaction Date")}>
<CalendarIcon className='w-4 h-4' />
<span className='text-sm'>{formatDate(item.bankTransaction.date, 'Do MMM YYYY')}</span>
</div>
<Separator orientation='vertical' />
<div>
<div className='flex items-center gap-1' title={isWithdrawal ? _("Spent") : _("Received")}>
{isWithdrawal ? <ArrowUpRight className="w-5 h-5 text-ink-red-3" /> : <ArrowDownRight className="w-5 h-5 text-ink-green-3" />}
<span className='text-sm text-ink-gray-5'>{formatCurrency(amount, currency)}</span>
</div>
</div>
</div>
</div>
<div className='flex justify-end items-center gap-2'>
<div className='text-end flex flex-col gap-2'>
<a
href={`/desk/${slug(item.voucher.reference_doctype)}/${item.voucher.reference_name}`}
target='_blank'
className='underline underline-offset-4 text-base'>
{["Payment Entry", "Journal Entry"].includes(item.voucher.reference_doctype) ? "" : _("{} :", [item.voucher.reference_doctype])} {item.voucher.reference_name}
</a>
{item.voucher.reference_doctype === "Payment Entry" && item.voucher.doc && <PaymentEntryDetails item={item} />}
{item.voucher.reference_doctype === "Journal Entry" && <JournalEntryDetails item={item} bank={bank} />}
</div>
</div>
</div>
</div>
<div className='w-10 h-10 flex items-center justify-center'>
<CancelActionLogItem item={item} type={action.type} timestamp={action.timestamp} bank={bank} />
</div>
</div>
}
const JournalEntryDetails = ({ item, bank }: { item: ActionLogItem, bank?: SelectedBank | null }) => {
return <div className='flex items-center gap-2 text-ink-gray-5 justify-end'>
<WalletIcon className='w-4 h-4' />
<JournalEntryAccountsTable item={item} bank={bank} />
</div>
}
const JournalEntryAccountsTable = ({ item, bank }: { item: ActionLogItem, bank?: SelectedBank | null }) => {
const accounts = useMemo(() => {
const allAccounts = (item.voucher.doc as JournalEntry).accounts
return allAccounts.filter((acc) => bank ? acc.account !== bank.account : true)
}, [item, bank])
return <>
{accounts.length === 1 ? <span className='text-sm'>{accounts[0].account}</span> :
<HoverCard>
<HoverCardTrigger>
<span className='text-sm cursor-pointer hover:underline underline-offset-4'>{_("Split across {} accounts", [accounts.length.toString()])}</span>
</HoverCardTrigger>
<HoverCardContent className='w-full p-2' align='end'>
<Table>
<TableHeader>
<TableRow>
<TableHead>{_("Account")}</TableHead>
<TableHead className='text-end'>{_("Debit")}</TableHead>
<TableHead className='text-end'>{_("Credit")}</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{accounts.map((account) => (
<TableRow key={account.account}>
<TableCell>{account.account}</TableCell>
<TableCell className='text-end font-numeric'>{formatCurrency(account.debit ?? 0, account.account_currency ?? '')}</TableCell>
<TableCell className='text-end font-numeric'>{formatCurrency(account.credit ?? 0, account.account_currency ?? '')}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</HoverCardContent>
</HoverCard>
}</>
}
const PaymentEntryDetails = ({ item, className }: { item: ActionLogItem, className?: string }) => {
if ((item.voucher.doc as PaymentEntry).payment_type === "Internal Transfer") {
return <TransferDetails item={item} className={className} />
}
const invoices = (item.voucher.doc as PaymentEntry).references ?? []
const currency = item.bankTransaction.withdrawal && item.bankTransaction.withdrawal > 0 ? (item.voucher.doc as PaymentEntry)?.paid_to_account_currency : (item.voucher.doc as PaymentEntry)?.paid_from_account_currency
return <div className='flex items-center gap-3'>
<div className={cn('flex items-center gap-2 text-ink-gray-5 text-sm', className)}>
<UserIcon className='w-4 h-4' />
<span className='text-sm'>{(item.voucher.doc as PaymentEntry).party_name}</span>
</div>
<Separator orientation='vertical' />
<HoverCard>
<HoverCardTrigger>
<div className={cn('flex items-center gap-2 text-ink-gray-5 text-sm', className)}>
<ReceiptTextIcon className='w-4 h-4' />
<span className='text-sm cursor-pointer hover:underline underline-offset-4'>{invoices.length === 0 ? _("No invoice linked") : invoices.length === 1 ? _("1 invoice") : _("{} invoices", [invoices.length.toString()])}</span>
</div>
</HoverCardTrigger>
<HoverCardContent className='w-full p-2' align='end'>
<div className='flex flex-col gap-2'>
{invoices.map((invoice) => (
<Table>
<TableHeader>
<TableRow>
<TableHead>{_("Document")}</TableHead>
<TableHead>{_("Invoice No")}</TableHead>
<TableHead>{_("Due Date")}</TableHead>
<TableHead className='text-end'>{_("Grand Total")}</TableHead>
<TableHead className='text-end'>{_("Allocated")}</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell><a href={`/desk/${slug(invoice.reference_doctype)}/${invoice.reference_name}`} target='_blank' className='underline underline-offset-4'>{invoice.reference_doctype}: {invoice.reference_name}</a></TableCell>
<TableCell>{invoice.bill_no ?? "-"}</TableCell>
<TableCell>{formatDate(invoice.due_date)}</TableCell>
<TableCell className='text-end font-numeric'>{formatCurrency(invoice.total_amount, currency ?? '')}</TableCell>
<TableCell className='text-end font-numeric'>{formatCurrency(invoice.allocated_amount, currency ?? '')}</TableCell>
</TableRow>
</TableBody>
</Table>
))}
</div>
</HoverCardContent>
</HoverCard>
</div>
}
const TransferDetails = ({ item, className }: { item: ActionLogItem, className?: string }) => {
const { banks } = useGetBankAccounts()
const bank = useMemo(() => {
const isWithdrawal = item.bankTransaction.withdrawal && item.bankTransaction.withdrawal > 0
let transferAccount = ""
if (isWithdrawal) {
transferAccount = (item.voucher.doc as PaymentEntry).paid_to
} else {
transferAccount = (item.voucher.doc as PaymentEntry).paid_from
}
const transferBankAccount = banks?.find((bank) => bank.account === transferAccount)
return transferBankAccount
}, [banks, item])
return <div className={cn('flex items-center gap-2 text-ink-gray-5 text-sm', className)}>
<BankLogo bank={bank} className='h-5 mb-0' iconSize='16px' imageClassName='max-h-5' />
<span className='text-sm'>{bank?.account}</span>
</div>
}
const ACTION_TYPE_MAP = {
'bank_entry': _("Bank Entry"),
'payment': _("Payment"),
'transfer': _("Transfer"),
'match': _("Match"),
}
const CancelActionLogItem = ({ item, type, timestamp, bank }: { item: ActionLogItem, type: ActionLogType['type'], timestamp: number, bank?: SelectedBank | null }) => {
const [isOpen, setIsOpen] = useState(false)
const { call, loading, error } = useFrappePostCall('erpnext.accounts.doctype.bank_transaction.bank_transaction.unreconcile_transaction_entry')
const { mutate } = useSWRConfig()
const actionLog = useSetAtom(bankRecActionLog)
const dates = useAtomValue(bankRecDateAtom)
const matchFilters = useAtomValue(bankRecMatchFilters)
const selectedBank = useAtomValue(selectedBankAccountAtom)
const onUndo = () => {
call({
bank_transaction_id: item.bankTransaction.name,
voucher_type: item.voucher.reference_doctype,
voucher_id: item.voucher.reference_name,
}).then(() => {
toast.success(type === 'match' ? _("Unmatched") : _("Cancelled"))
if (selectedBank?.name === item.bankTransaction.bank_account) {
mutate(`bank-reconciliation-unreconciled-transactions-${selectedBank?.name}-${dates.fromDate}-${dates.toDate}`)
mutate(`bank-reconciliation-account-closing-balance-${selectedBank?.name}-${dates.toDate}`)
// Update the matching vouchers for the selected transaction
mutate(`bank-reconciliation-vouchers-${item.bankTransaction.name}-${dates.fromDate}-${dates.toDate}-${matchFilters.join(',')}`)
}
setTimeout(() => {
actionLog((prev) => {
// Find the action and then remove the item from the action. If the action is empty, remove the action from the array
const action = prev.find((action) => action.timestamp === timestamp)
if (action) {
action.items = action.items.filter((i) => i.bankTransaction.name !== item.bankTransaction.name)
}
// If the action is empty, remove the action from the array
if (action && action.items.length === 0) {
return prev.filter((a) => a.timestamp !== timestamp)
} else {
return prev.map((a) => a.timestamp === timestamp ? { ...a, items: action?.items ?? [] } : a)
}
})
}, 100)
setIsOpen(false)
}).catch((error) => {
toast.error(_("There was an error while performing the action."), {
duration: 5000,
description: getErrorMessage(error),
})
})
}
return <AlertDialog open={isOpen} onOpenChange={setIsOpen}>
<Tooltip>
<TooltipTrigger asChild>
<AlertDialogTrigger asChild>
<Button
variant={'ghost'}
isIconButton
theme='red'
title={_("Cancel")}
className='hover:text-ink-red-3 hover:bg-destructive/5 text-ink-gray-5 hidden group-hover:inline-flex'>
<CircleXIcon className='w-8 h-8' />
</Button>
</AlertDialogTrigger>
</TooltipTrigger>
<TooltipContent>
{_("Cancel")}
</TooltipContent>
</Tooltip>
<AlertDialogContent className='min-w-3xl'>
<AlertDialogHeader>
<AlertDialogTitle>{type === 'match' ? _("Unmatch Transaction?") : _("Undo {}?", [item.voucher.reference_doctype])}</AlertDialogTitle>
<AlertDialogDescription>{type === 'match' ? _("Are you sure you want to unmatch the voucher from this transaction?") : _("Are you sure you want to cancel this {} {}?", [_(item.voucher.reference_doctype), item.voucher.reference_name])}</AlertDialogDescription>
</AlertDialogHeader>
{error && <ErrorBanner error={error} />}
<div className='flex flex-col gap-2'>
<SelectedTransactionDetails transaction={item.bankTransaction} />
<Table>
<TableRow>
<TableHead>{_("Action Type")}</TableHead>
<TableCell>{ACTION_TYPE_MAP[type]}</TableCell>
</TableRow>
<TableRow>
<TableHead>{_("Voucher Type")}</TableHead>
<TableCell>{_(item.voucher.reference_doctype)}</TableCell>
</TableRow>
<TableRow>
<TableHead>{_("Voucher Name")}</TableHead>
<TableCell><a href={`/desk/${slug(item.voucher.reference_doctype)}/${item.voucher.reference_name}`} target='_blank' className='underline underline-offset-4'>{item.voucher.reference_name}</a></TableCell>
</TableRow>
<TableRow>
<TableHead>{_("Posting Date")}</TableHead>
<TableCell>{formatDate(item.voucher.posting_date, 'Do MMM YYYY')}</TableCell>
</TableRow>
{type === 'transfer' && item.voucher.doc && <TableRow>
<TableHead>{_("Transfer Account")}</TableHead>
<TableCell>
<TransferDetails item={item} className='text-ink-gray-8' />
</TableCell>
</TableRow>}
{type === 'payment' && item.voucher.doc && <TableRow>
<TableHead>{_("Payment Details")}</TableHead>
<TableCell>
<PaymentEntryDetails item={item} className='text-ink-gray-8' />
</TableCell>
</TableRow>}
{type === 'bank_entry' && item.voucher.doc && <TableRow>
<TableHead>{_("Account")}</TableHead>
<TableCell><JournalEntryAccountsTable item={item} bank={bank} /></TableCell>
</TableRow>}
</Table>
</div>
<AlertDialogFooter>
<AlertDialogCancel disabled={loading}>
{_("Close")}
</AlertDialogCancel>
<Button theme="red" size='md' disabled={loading} onClick={onUndo}>
{loading ? <Loader2Icon className='w-4 h-4 animate-spin' /> : _(("Undo"))}
</Button>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
}
export default ActionLog

View File

@@ -1,334 +0,0 @@
import { useAtomValue, useSetAtom } from "jotai"
import { bankRecClosingBalanceAtom, bankRecDateAtom, SelectedBank, selectedBankAccountAtom } from "./bankRecAtoms"
import { FrappeConfig, FrappeContext, useFrappeGetDocCount, useFrappeGetDocList, useFrappePostCall, useSWRConfig } from "frappe-react-sdk"
import { BankTransaction } from "@/types/Accounts/BankTransaction"
import { Progress } from "@/components/ui/progress"
import { useGetAccountClosingBalance, useGetAccountClosingBalanceAsPerStatement, useGetAccountOpeningBalance, useGetUnreconciledTransactions } from "./utils"
import { flt, formatCurrency } from "@/lib/numbers"
import { Skeleton } from "@/components/ui/skeleton"
import { StatContainer, StatLabel, StatValue } from "@/components/ui/stats"
import { Edit, Info, Trash2 } from "lucide-react"
import { H4, Paragraph } from "@/components/ui/typography"
import { HoverCard, HoverCardContent, HoverCardTrigger } from "@/components/ui/hover-card"
import { getCompanyCurrency } from "@/lib/company"
import _ from "@/lib/translate"
import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
import { formatDate } from "@/lib/date"
import { Form } from "@/components/ui/form"
import { CurrencyFormField } from "@/components/ui/form-elements"
import { useForm } from "react-hook-form"
import { Button } from "@/components/ui/button"
import { useContext, useState } from "react"
import { Separator } from "@/components/ui/separator"
import { BankAccountBalance } from "@/types/Accounts/BankAccountBalance"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
import { toast } from "sonner"
import ErrorBanner from "@/components/ui/error-banner"
const BankBalance = () => {
const bankAccount = useAtomValue(selectedBankAccountAtom)
if (!bankAccount) {
return null
}
return (
<div className="flex justify-between">
<div className="w-[80%] flex flex-wrap justify-between gap-2 pe-8 border-e-border border-e">
<OpeningBalance />
<ClosingBalance />
<ClosingBalanceAsPerStatement />
<Difference />
</div>
<ReconcileProgress />
</div>
)
}
const OpeningBalance = () => {
const bankAccount = useAtomValue(selectedBankAccountAtom)
const { data, isLoading } = useGetAccountOpeningBalance()
return <StatContainer className="min-w-48">
<StatLabel>{_("Opening Balance")}</StatLabel>
{isLoading ? <Skeleton className="w-[150px] h-5 rounded-sm" /> : <StatValue className="font-numeric">{formatCurrency(flt(data?.message, 2), bankAccount?.account_currency ?? getCompanyCurrency(bankAccount?.company ?? ''))}</StatValue>}
</StatContainer>
}
const ClosingBalance = () => {
const bankAccount = useAtomValue(selectedBankAccountAtom)
const { data, isLoading } = useGetAccountClosingBalance()
return (
<StatContainer className="min-w-48">
<div className="flex items-start gap-1">
<StatLabel>
{_("Closing Balance as per system")}
</StatLabel>
<HoverCard openDelay={100}>
<HoverCardTrigger>
<Info className="size-3.5 text-ink-gray-6 -mt-px" />
</HoverCardTrigger>
<HoverCardContent className="w-96" align="start" side="right">
<H4 className="text-base">{_("Closing balance as per system")}</H4>
<Paragraph className="mt-2 text-p-sm">
{_("This is what the system expects the closing balance to be in your bank statement.")}
<br />
{_("It takes into account all the transactions that have been posted and subtracts the transactions that have not cleared yet.")}
<br />
{_("If your bank statement shows a different closing balance, it is because all transactions have not reconciled yet.")}
<br /><br />
For more information, click on the <strong>Bank Reconciliation Statement</strong> tab below.
</Paragraph>
</HoverCardContent>
</HoverCard>
</div>
{isLoading ? <Skeleton className="w-[150px] h-5 rounded-sm" /> : <StatValue className="font-numeric">{formatCurrency(flt(data?.message, 2), bankAccount?.account_currency ?? getCompanyCurrency(bankAccount?.company ?? ''))}</StatValue>}
</StatContainer>
)
}
const Difference = () => {
const bankAccount = useAtomValue(selectedBankAccountAtom)
const { data, isLoading } = useGetAccountClosingBalance()
const value = useAtomValue(bankRecClosingBalanceAtom(bankAccount?.name ?? ''))
const difference = flt(value.value - (data?.message ?? 0))
const isError = difference !== 0
return <StatContainer className="w-fit text-end sm:min-w-56">
<StatLabel className="text-end">{_("Difference")}</StatLabel>
{isLoading ? <Skeleton className="w-[150px] h-5 self-end rounded-sm" /> : <StatValue className={isError ? 'text-ink-red-3 font-numeric' : 'font-numeric'}>
{formatCurrency(difference,
bankAccount?.account_currency ?? getCompanyCurrency(bankAccount?.company ?? ''))
}</StatValue>}
</StatContainer>
}
const ReconcileProgress = () => {
const bankAccount = useAtomValue(selectedBankAccountAtom)
const dates = useAtomValue(bankRecDateAtom)
const { data: totalCount } = useFrappeGetDocCount<BankTransaction>('Bank Transaction', [
["bank_account", "=", bankAccount?.name ?? ''],
['docstatus', '=', 1],
['date', '<=', dates?.toDate],
['date', '>=', dates?.fromDate]
], false, undefined, {
revalidateOnFocus: false
})
const { data: unreconciledTransactions, } = useGetUnreconciledTransactions()
const reconciledCount = (totalCount ?? 0) - (unreconciledTransactions?.message?.length ?? 0)
const progress = (totalCount ? reconciledCount / totalCount : 0) * 100
return <div className="w-[18%] flex flex-col gap-1 items-end">
<div className="w-full">
<Progress
value={progress}
max={100}
size="md"
label="Progress"
hint
hintText={`${reconciledCount} / ${totalCount} ${_("reconciled")}`} />
</div>
</div>
}
const ClosingBalanceAsPerStatement = () => {
const bankAccount = useAtomValue(selectedBankAccountAtom)
const dates = useAtomValue(bankRecDateAtom)
const setValue = useSetAtom(bankRecClosingBalanceAtom(bankAccount?.name ?? ''))
const { data, isLoading } = useGetAccountClosingBalanceAsPerStatement({
onSuccess: (data) => {
if (data?.message && data?.message?.balance) {
setValue({
value: data?.message?.balance,
stringValue: data?.message?.balance.toString()
})
}
}
})
const isDateSame = data?.message?.date === dates.toDate
const [isOpen, setIsOpen] = useState(false)
return <StatContainer className="min-w-48">
<StatLabel>{_("Closing Balance as per statement")}</StatLabel>
<div className="flex flex-col gap-2 items-start">
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogTrigger>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center gap-4 underline cursor-pointer underline-offset-6" role="button">
{isLoading ? <Skeleton className="w-[150px] h-5 rounded-sm" /> : <StatValue className="font-numeric">{formatCurrency(flt(data?.message?.balance, 2), bankAccount?.account_currency ?? getCompanyCurrency(bankAccount?.company ?? ''))}</StatValue>}
<Edit className="w-4 h-4" />
</div>
</TooltipTrigger>
<TooltipContent>
{_("Click to set the closing balance as per statement")}
</TooltipContent>
</Tooltip>
</DialogTrigger>
<DialogContent className="min-w-xl">
<ClosingBalanceForm
defaultBalance={data?.message?.balance ?? 0}
date={dates.toDate}
bankAccount={bankAccount}
onClose={() => setIsOpen(false)}
/>
</DialogContent>
</Dialog>
{!isDateSame && data?.message.date && <span className="text-xs font-medium text-ink-red-3">{_("As of {0}", [formatDate(data?.message?.date ?? '', 'Do MMM YYYY')])}</span>}
</div>
</StatContainer>
}
const ClosingBalanceForm = ({ defaultBalance, date, bankAccount, onClose }: { defaultBalance: number, date: string, bankAccount: SelectedBank | null, onClose: VoidFunction }) => {
const { mutate } = useSWRConfig()
const form = useForm<{ balance: number }>({
defaultValues: {
balance: defaultBalance
}
})
const setValue = useSetAtom(bankRecClosingBalanceAtom(bankAccount?.name ?? ''))
const { call, loading, error } = useFrappePostCall("erpnext.accounts.doctype.bank_account.bank_account.set_closing_balance_as_per_statement")
const onSubmit = (data: { balance: number }) => {
if (data.balance) {
call({
bank_account: bankAccount?.name ?? '',
date: date,
balance: data.balance
})
.then(() => {
// Mutate the closing balance as per statement
mutate(`bank-reconciliation-account-closing-balance-as-per-statement-${bankAccount?.name}-${date}`)
setValue({
value: data.balance,
stringValue: data.balance.toString()
})
toast.success(_("Closing balance set."))
onClose()
})
} else {
toast.error(_("Closing balance is required."))
}
}
const currency = bankAccount?.account_currency ?? getCompanyCurrency(bankAccount?.company ?? '')
return <Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<DialogHeader>
<DialogTitle>{_("Set closing balance as per bank statement")}</DialogTitle>
<DialogDescription>
{_("Enter the closing balance you see in your bank statement for {0} as of the {1}", [bankAccount?.account_name ?? bankAccount?.name ?? '', formatDate(date, 'Do MMM YYYY')])}
</DialogDescription>
</DialogHeader>
{error && <ErrorBanner error={error} />}
<div className="py-4">
<CurrencyFormField
name="balance"
label={_("Closing balance on bank statement as of {0}", [formatDate(date, 'Do MMM YYYY')])}
isRequired
currency={currency}
/>
</div>
<DialogFooter>
<DialogClose asChild>
<Button variant={'outline'} size='md' disabled={loading}>{_("Cancel")}</Button>
</DialogClose>
<Button type='submit' size='md' disabled={loading}>{_("Save")}</Button>
</DialogFooter>
<ClosingBalancesList bankAccount={bankAccount} date={date} />
</form>
</Form>
}
const ClosingBalancesList = ({ bankAccount, date }: { bankAccount: SelectedBank | null, date: string }) => {
const { data, mutate } = useFrappeGetDocList<BankAccountBalance>("Bank Account Balance", {
filters: [["bank_account", "=", bankAccount?.name ?? ''], ["date", "<=", date]],
orderBy: {
field: "date",
order: "desc"
},
fields: ["date", "balance", "name"],
limit: 10
})
const { db } = useContext(FrappeContext) as FrappeConfig
const onDelete = (name: string) => {
toast.promise(db.deleteDoc("Bank Account Balance", name).then(() => {
mutate()
}), {
loading: _("Deleting closing balance..."),
success: _("Closing balance deleted."),
error: _("Failed to delete closing balance.")
})
}
if (data?.length === 0) {
return null
}
return <div>
<Separator className="my-8" />
<p className="text-sm text-center">{_("Balances as per bank statement before {0}", [formatDate(date, 'Do MMM YYYY')])}</p>
<Table>
<TableHeader>
<TableRow>
<TableHead>{_("Date")}</TableHead>
<TableHead className="text-end">{_("Balance")}</TableHead>
<TableHead></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{data?.map((item) => (
<TableRow key={item.name}>
<TableCell>{formatDate(item.date, 'Do MMM YYYY')}</TableCell>
<TableCell className="text-end">{formatCurrency(flt(item.balance, 2), bankAccount?.account_currency ?? getCompanyCurrency(bankAccount?.company ?? ''))}</TableCell>
<TableCell className="text-end">
<Button
title={_("Delete")}
type='button' isIconButton variant='ghost' onClick={() => onDelete(item.name)}>
<Trash2 />
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
}
export default BankBalance

View File

@@ -1,355 +0,0 @@
import { useAtomValue } from "jotai"
import { MissingFiltersBanner } from "./MissingFiltersBanner"
import { bankRecDateAtom, SelectedBank, selectedBankAccountAtom } from "./bankRecAtoms"
import { useCurrentCompany } from "@/hooks/useCurrentCompany"
import { Paragraph } from "@/components/ui/typography"
import type { ColumnDef } from "@tanstack/react-table"
import { useCallback, useMemo, useState } from "react"
import { useFrappeGetCall, useFrappePostCall, useSWRConfig } from "frappe-react-sdk"
import { QueryReportReturnType } from "@/types/custom/Reports"
import { formatDate } from "@/lib/date"
import { ListView, type ListViewColumnMeta } from "@/components/ui/list-view"
import { Table, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
import { formatCurrency } from "@/lib/numbers"
import { getCompanyCurrency } from "@/lib/company"
import { slug } from "@/lib/frappe"
import { CheckCircle2, ReceiptTextIcon, XCircle } from "lucide-react"
import ErrorBanner from "@/components/ui/error-banner"
import { Badge } from "@/components/ui/badge"
import _ from "@/lib/translate"
import { useCopyToClipboard } from "usehooks-ts"
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
import { toast } from "sonner"
import { Button } from "@/components/ui/button"
import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"
import { Form } from "@/components/ui/form"
import { useForm } from "react-hook-form"
import { DateField } from "@/components/ui/form-elements"
import { Empty, EmptyMedia, EmptyHeader, EmptyTitle, EmptyDescription } from "@/components/ui/empty"
const BankClearanceSummary = () => {
const bankAccount = useAtomValue(selectedBankAccountAtom)
const dates = useAtomValue(bankRecDateAtom)
if (!bankAccount) {
return <MissingFiltersBanner text={_("Please select a bank account to view the bank clearance summary.")} />
}
if (!dates) {
return <MissingFiltersBanner text={_("Please select dates to view the bank clearance summary.")} />
}
return <BankClearanceSummaryView />
}
interface BankClearanceSummaryEntry {
payment_document_type: string
payment_entry: string
posting_date: string,
cheque_no?: string,
amount: number,
against: string,
clearance_date: string,
}
const BankClearanceSummaryView = () => {
const companyID = useCurrentCompany()
const bankAccount = useAtomValue(selectedBankAccountAtom)
const dates = useAtomValue(bankRecDateAtom)
const filters = useMemo(() => {
return JSON.stringify({
account: bankAccount?.account,
from_date: dates.fromDate,
to_date: dates.toDate
})
}, [bankAccount, dates])
const { data, error, mutate } = useFrappeGetCall<{ message: QueryReportReturnType<BankClearanceSummaryEntry> }>('frappe.desk.query_report.run', {
report_name: 'Bank Clearance Summary',
filters,
ignore_prepared_report: 1,
are_default_filters: false,
}, `Report-Bank Clearance Summary-${filters}`, { keepPreviousData: true, revalidateOnFocus: false }, 'POST')
const formattedFromDate = formatDate(dates.fromDate)
const formattedToDate = formatDate(dates.toDate)
const [, copyToClipboard] = useCopyToClipboard()
const onCopy = useCallback(
(text: string) => {
copyToClipboard(text).then(() => {
toast.success(_("Copied to clipboard"))
})
},
[copyToClipboard, _],
)
const accountCurrency = useMemo(
() => bankAccount?.account_currency ?? getCompanyCurrency(companyID),
[bankAccount?.account_currency, companyID],
)
const clearanceColumns = useMemo<ColumnDef<BankClearanceSummaryEntry, unknown>[]>(
() => [
{
accessorKey: "payment_document_type",
header: _("Document Type"),
size: 140,
cell: ({ row }) => _(row.original.payment_document_type),
},
{
id: "payment_entry",
header: _("Payment Document"),
size: 160,
meta: {
getTooltipText: (r) => {
const x = r as BankClearanceSummaryEntry
return [x.payment_document_type, x.payment_entry].filter(Boolean).join(" · ") || undefined
},
} satisfies ListViewColumnMeta,
cell: ({ row }) => (
<a
target="_blank"
rel="noreferrer"
className="text-ink-gray-8 block min-w-0 w-full underline underline-offset-4"
href={`/desk/${slug(row.original.payment_document_type)}/${row.original.payment_entry}`}
>
{row.original.payment_entry}
</a>
),
},
{
accessorKey: "posting_date",
header: _("Posting Date"),
size: 118,
meta: { tabularNums: true } satisfies ListViewColumnMeta,
cell: ({ row }) => formatDate(row.original.posting_date),
},
{
accessorKey: "cheque_no",
header: _("Cheque/Reference Number"),
size: 160,
cell: ({ row }) => {
const ref = row.original.cheque_no ?? ""
return (
<Tooltip delayDuration={500}>
<TooltipTrigger asChild>
<button
type="button"
className="text-ink-gray-8 hover:underline min-w-0 w-full cursor-pointer truncate text-start underline-offset-4"
onClick={() => onCopy(ref)}
>
{ref}
</button>
</TooltipTrigger>
<TooltipContent>
{ref}
</TooltipContent>
</Tooltip>
)
},
},
{
accessorKey: "clearance_date",
header: _("Clearance Date"),
size: 118,
meta: { tabularNums: true } satisfies ListViewColumnMeta,
cell: ({ row }) => formatDate(row.original.clearance_date),
},
{
accessorKey: "against",
header: _("Against Account"),
size: 250,
},
{
accessorKey: "amount",
header: _("Amount"),
size: 150,
meta: { align: "right" } satisfies ListViewColumnMeta,
cell: ({ row }) => <span className="font-numeric">{formatCurrency(row.original.amount, accountCurrency)}</span>,
},
{
id: "status",
header: _("Status"),
size: 200,
meta: { truncate: false, truncateTooltip: false } satisfies ListViewColumnMeta,
cell: ({ row }) => {
const r = row.original
return r.clearance_date ? (
<Badge theme="green">
<CheckCircle2 />
{_("Cleared")}
</Badge>
) : (
<div className="flex min-w-0 flex-wrap items-center gap-2">
<Badge theme="red">
<XCircle />
{_("Not Cleared")}
</Badge>
<SetClearanceDateButton
voucher={r}
bankAccount={bankAccount}
companyID={companyID}
mutate={mutate}
/>
</div>
)
},
},
],
[_, accountCurrency, bankAccount, companyID, mutate, onCopy],
)
return <div className="space-y-4 py-2">
<div>
<Paragraph className="text-sm">
<span dangerouslySetInnerHTML={{
__html: _("Below is a list of all accounting entries posted against the bank account {0} between {1} and {2}.", [`<strong>${bankAccount?.account}</strong>`, `<strong>${formattedFromDate}</strong>`, `<strong>${formattedToDate}</strong>`])
}} />
</Paragraph>
</div>
{error && <ErrorBanner error={error} />}
{data && data.message.result.length > 0 ? (
<ListView
data={data.message.result}
columns={clearanceColumns}
getRowId={(row) => `${row.payment_entry}-${row.posting_date}`}
maxHeight="calc(100vh - 200px)"
scrollAreaClassName="min-h-[calc(100vh-200px)]"
emptyState={_("No rows to display.")}
/>
) : null}
{data && data.message.result.length == 0 &&
<Empty>
<EmptyMedia>
<ReceiptTextIcon />
</EmptyMedia>
<EmptyHeader>
<EmptyTitle>{_("No entries found")}</EmptyTitle>
<EmptyDescription>{_("There are no accounting entries in the system for the selected account and dates.")}</EmptyDescription>
</EmptyHeader>
</Empty>
}
</div>
}
const SetClearanceDateButton = ({ voucher, bankAccount, companyID, mutate }: { voucher: BankClearanceSummaryEntry, bankAccount: SelectedBank | null, companyID: string, mutate: VoidFunction }) => {
const [open, setOpen] = useState(false)
const onClose = () => {
setOpen(false)
mutate()
}
return <Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger disabled={!bankAccount}>
<Tooltip delayDuration={500}>
<TooltipTrigger>
<Button variant='link' size="sm" className="px-0" theme="red">{_("Force Clear")}</Button>
</TooltipTrigger>
<TooltipContent align='start'>
{_("Set the clearance date for this voucher without reconciling with a bank transaction.")}
</TooltipContent>
</Tooltip>
</DialogTrigger>
<DialogContent className="min-w-2xl">
{bankAccount && <ForceClearVoucherForm voucher={voucher} bankAccount={bankAccount} companyID={companyID} onClose={onClose} />}
</DialogContent>
</Dialog>
}
const ForceClearVoucherForm = ({ voucher, bankAccount, companyID, onClose }: { voucher: BankClearanceSummaryEntry, bankAccount: SelectedBank, companyID: string, onClose: () => void }) => {
const { mutate } = useSWRConfig()
const dates = useAtomValue(bankRecDateAtom)
const form = useForm<{ clearance_date: string }>({
defaultValues: {
clearance_date: voucher.posting_date,
}
})
const { call, loading, error } = useFrappePostCall('erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.update_clearance_date')
const onSubmit = (data: { clearance_date: string }) => {
call({
payment_document: voucher.payment_document_type,
payment_entry: voucher.payment_entry,
account: bankAccount.account,
clearance_date: data.clearance_date,
})
.then(() => {
toast.success(_("Clearance date updated"))
onClose()
mutate(`bank-reconciliation-account-closing-balance-${bankAccount?.name}-${dates.toDate}`)
})
}
return <Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<div className='flex flex-col gap-4'>
<DialogHeader>
<DialogTitle>{_("Force Clear Voucher")}</DialogTitle>
<DialogDescription>
{_("Set the clearance date for this voucher without reconciling with a bank transaction.")}
</DialogDescription>
</DialogHeader>
{error && <ErrorBanner error={error} />}
<div>
<Table>
<TableHeader>
<TableRow>
<TableHead>{_("Payment Document")}</TableHead>
<TableCell><a target="_blank" className="underline underline-offset-4"
href={`/desk/${slug(voucher.payment_document_type)}/${voucher.payment_entry}`}>{_(voucher.payment_document_type)} : {voucher.payment_entry}</a></TableCell>
</TableRow>
<TableRow>
<TableHead>{_("Posting Date")}</TableHead>
<TableCell>{formatDate(voucher.posting_date)}</TableCell>
</TableRow>
<TableRow>
<TableHead>{_("Cheque/Reference Number")}</TableHead>
<TableCell title={voucher.cheque_no}>{voucher.cheque_no?.slice(0, 40)}{voucher.cheque_no?.length && voucher.cheque_no?.length > 40 ? "..." : ""}</TableCell>
</TableRow>
<TableRow>
<TableHead>{_("Amount")}</TableHead>
<TableCell className="text-end">{formatCurrency(voucher.amount, bankAccount?.account_currency ?? getCompanyCurrency(companyID))}</TableCell>
</TableRow>
<TableRow>
<TableHead>{_("Against Account")}</TableHead>
<TableCell><a target="_blank" className="underline underline-offset-4" href={`/desk/account/${voucher.against}`}>{voucher.against}</a></TableCell>
</TableRow>
</TableHeader>
</Table>
</div>
<DateField
name='clearance_date'
label={_("Clearance Date")}
isRequired
inputProps={{ autoFocus: true }}
/>
<DialogFooter>
<DialogClose asChild>
<Button variant={'outline'} disabled={loading} size='md'>{_("Cancel")}</Button>
</DialogClose>
<Button type='submit' disabled={loading} size='md'>{_("Submit")}</Button>
</DialogFooter>
</div>
</form>
</Form>
}
export default BankClearanceSummary

View File

@@ -1,831 +0,0 @@
import { useAtom, useAtomValue, useSetAtom } from "jotai"
import { bankRecRecordJournalEntryModalAtom, bankRecSelectedTransactionAtom, bankRecUnreconcileModalAtom, selectedBankAccountAtom } from "./bankRecAtoms"
import { Dialog, DialogContent, DialogTitle, DialogDescription, DialogHeader, DialogFooter, DialogClose } from "@/components/ui/dialog"
import _ from "@/lib/translate"
import { UnreconciledTransaction, useGetRuleForTransaction, useRefreshUnreconciledTransactions, useUpdateActionLog } from "./utils"
import { useFieldArray, useForm, useFormContext, useWatch } from "react-hook-form"
import { JournalEntry } from "@/types/Accounts/JournalEntry"
import { getCompanyCostCenter, getCompanyCurrency } from "@/lib/company"
import { FrappeConfig, FrappeContext, useFrappePostCall } from "frappe-react-sdk"
import { toast } from "sonner"
import ErrorBanner from "@/components/ui/error-banner"
import { Button } from "@/components/ui/button"
import SelectedTransactionDetails from "./SelectedTransactionDetails"
import { AccountFormField, CurrencyFormField, DataField, DateField, LinkFormField, PartyTypeFormField, SmallTextField } from "@/components/ui/form-elements"
import { Form } from "@/components/ui/form"
import { useCallback, useContext, useMemo, useRef, useState } from "react"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
import { Checkbox } from "@/components/ui/checkbox"
import { ArrowDownRight, ArrowUpRight, Plus, Trash2 } from "lucide-react"
import { flt, formatCurrency } from "@/lib/numbers"
import { cn } from "@/lib/utils"
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
import SelectedTransactionsTable from "./SelectedTransactionsTable"
import { JournalEntryAccount } from "@/types/Accounts/JournalEntryAccount"
import { BankTransaction } from "@/types/Accounts/BankTransaction"
import FileUploadBanner from "@/components/common/FileUploadBanner"
import { Label } from "@/components/ui/label"
import { FileDropzone } from "@/components/ui/file-dropzone"
import { useGetAccounts } from "@/components/common/AccountsDropdown"
import { useHotkeys } from "react-hotkeys-hook"
const BankEntryModal = () => {
const [isOpen, setIsOpen] = useAtom(bankRecRecordJournalEntryModalAtom)
return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogContent className='min-w-[95vw]'>
<DialogHeader>
<DialogTitle>{_("Bank Entry")}</DialogTitle>
<DialogDescription>
{_("Record a journal entry for expenses, income or split transactions.")}
</DialogDescription>
</DialogHeader>
<RecordBankEntryModalContent />
</DialogContent>
</Dialog>
)
}
const RecordBankEntryModalContent = () => {
const selectedBankAccount = useAtomValue(selectedBankAccountAtom)
const selectedTransaction = useAtomValue(bankRecSelectedTransactionAtom(selectedBankAccount?.name ?? ''))
if (!selectedTransaction || !selectedBankAccount || selectedTransaction.length === 0) {
return <div className='p-4'>
<span className='text-center'>{_("No transaction selected")}</span>
</div>
}
if (selectedTransaction.length === 1) {
return <BankEntryForm
selectedTransaction={selectedTransaction[0]} />
}
return <BulkBankEntryForm
selectedTransactions={selectedTransaction}
/>
}
const BulkBankEntryForm = ({ selectedTransactions }: { selectedTransactions: UnreconciledTransaction[] }) => {
const form = useForm<{
account: string
}>({
defaultValues: {
account: ''
}
})
const { call, loading, error } = useFrappePostCall<{ message: { transaction: BankTransaction, journal_entry: JournalEntry }[] }>('erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.create_bulk_bank_entry_and_reconcile')
const onReconcile = useRefreshUnreconciledTransactions()
const addToActionLog = useUpdateActionLog()
const setIsOpen = useSetAtom(bankRecRecordJournalEntryModalAtom)
const onSubmit = (data: { account: string }) => {
call({
bank_transactions: selectedTransactions.map(transaction => transaction.name),
account: data.account
}).then(({ message }) => {
addToActionLog({
type: 'bank_entry',
timestamp: (new Date()).getTime(),
isBulk: true,
items: message.map((item) => ({
bankTransaction: item.transaction,
voucher: {
reference_doctype: "Journal Entry",
reference_name: item.journal_entry.name,
doc: item.journal_entry,
posting_date: item.journal_entry.posting_date,
}
})),
bulkCommonData: {
account: data.account,
}
})
toast.success(_("Bank Entries Created"), {
duration: 4000,
})
// Set this to the last selected transaction
onReconcile(selectedTransactions[selectedTransactions.length - 1])
setIsOpen(false)
})
}
return <Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<div className="flex flex-col gap-4">
{error && <ErrorBanner error={error} />}
<SelectedTransactionsTable />
<div className="grid grid-cols-3 gap-4">
<AccountFormField
name='account'
filterFunction={(acc) => {
// Do not allow payable and receivable accounts
return acc.account_type !== 'Payable' && acc.account_type !== 'Receivable'
}}
label={_('Account')}
isRequired
/>
</div>
<DialogFooter>
<DialogClose asChild>
<Button size='md' variant={'outline'} disabled={loading}>{_("Cancel")}</Button>
</DialogClose>
<Button size='md' type='submit' disabled={loading}>{_("Submit")}</Button>
</DialogFooter>
</div>
</form>
</Form>
}
interface BankEntryFormData extends Pick<JournalEntry, 'voucher_type' | 'cheque_date' | 'posting_date' | 'cheque_no' | 'user_remark'> {
entries: JournalEntry['accounts']
}
const BankEntryForm = ({ selectedTransaction }: { selectedTransaction: UnreconciledTransaction }) => {
const selectedBankAccount = useAtomValue(selectedBankAccountAtom)
const { data: rule } = useGetRuleForTransaction(selectedTransaction)
const setIsOpen = useSetAtom(bankRecRecordJournalEntryModalAtom)
const onClose = () => {
setIsOpen(false)
}
const isWithdrawal = (selectedTransaction.withdrawal && selectedTransaction.withdrawal > 0) ? true : false
const defaultAccounts = useMemo(() => {
const isWithdrawal = (selectedTransaction.withdrawal && selectedTransaction.withdrawal > 0) ? true : false
const accounts: Partial<JournalEntryAccount>[] = [
{
account: selectedBankAccount?.account ?? '',
bank_account: selectedTransaction.bank_account,
// Bank is debited if it's a deposit
debit: isWithdrawal ? 0 : selectedTransaction.unallocated_amount,
credit: isWithdrawal ? selectedTransaction.unallocated_amount : 0,
party_type: '',
party: '',
cost_center: ''
}]
// If there is no rule, we can just add the entries for the bank account transaction and the other side will be the reverse
if (!rule) {
accounts.push(
{
account: '',
// Amounts will be the reverse of the bank account transaction
debit: isWithdrawal ? selectedTransaction.unallocated_amount : 0,
credit: isWithdrawal ? 0 : selectedTransaction.unallocated_amount,
cost_center: getCompanyCostCenter(selectedTransaction.company ?? '') ?? '',
}
)
} else {
// Rule exists, so we need to check the type of rule
if (!rule.bank_entry_type || rule.bank_entry_type === "Single Account") {
// Only a single account needs to be added
accounts.push({
account: rule.account ?? '',
// Amounts will be the reverse of the bank account transaction
debit: isWithdrawal ? selectedTransaction.unallocated_amount : 0,
credit: isWithdrawal ? 0 : selectedTransaction.unallocated_amount,
cost_center: getCompanyCostCenter(selectedTransaction.company ?? '') ?? '',
})
} else {
// For multiple accounts, we need to loop over and add entries for each
// The last row will just be the remaining amount
let hasTotallyEmptyRowEarlier = false;
let totalDebits = isWithdrawal ? 0 : selectedTransaction.unallocated_amount ?? 0
let totalCredits = isWithdrawal ? selectedTransaction.unallocated_amount ?? 0 : 0
for (let i = 0; i < (rule.accounts?.length ?? 0); i++) {
const acc = rule.accounts?.[i]
// If it's the last row, add the difference amount
if (i === (rule.accounts?.length ?? 0) - 1 && !hasTotallyEmptyRowEarlier) {
const differenceAmount = flt(totalDebits - totalCredits, 2)
accounts.push({
account: acc?.account ?? '',
debit: differenceAmount > 0 ? 0 : Math.abs(differenceAmount),
credit: differenceAmount > 0 ? Math.abs(differenceAmount) : 0,
cost_center: getCompanyCostCenter(selectedTransaction.company ?? '') ?? '',
user_remark: acc?.user_remark ?? '',
})
} else {
/**
* The debit and credit amounts can also be expressions - like "transaction_amount * 0.5"
* So we need to compute the value of the expression
* We can use the eval function to do this. But we need to expose certain variables to the expression.
* One of them is transaction_amount which is the unallocated amount of the selected transaction
* @param expression - The expression to compute
* @returns The computed value
*/
const computeExpression = (expression: string) => {
const script = `
const transaction_amount = ${selectedTransaction.unallocated_amount ?? 0}
${expression};
`
let value = 0;
try {
value = window.eval(script);
} catch (error: unknown) {
console.error(error);
value = 0;
}
return value;
}
if (!acc?.debit && !acc?.credit) {
hasTotallyEmptyRowEarlier = true;
}
const computedDebit = acc?.debit ? flt(computeExpression(acc.debit), 2) : 0
const computedCredit = acc?.credit ? flt(computeExpression(acc.credit), 2) : 0
totalDebits = flt(totalDebits + computedDebit, 2)
totalCredits = flt(totalCredits + computedCredit, 2)
accounts.push({
account: acc?.account ?? '',
debit: computedDebit,
credit: computedCredit,
cost_center: getCompanyCostCenter(selectedTransaction.company ?? '') ?? '',
user_remark: acc?.user_remark ?? '',
})
}
}
}
}
return accounts
}, [rule, selectedTransaction, selectedBankAccount])
const form = useForm<BankEntryFormData>({
defaultValues: {
voucher_type: selectedBankAccount?.is_credit_card ? 'Credit Card Entry' : 'Bank Entry',
cheque_date: selectedTransaction.date,
posting_date: selectedTransaction.date,
cheque_no: (selectedTransaction.reference_number || selectedTransaction.description || '').slice(0, 140),
user_remark: selectedTransaction.description,
entries: defaultAccounts,
}
})
const onReconcile = useRefreshUnreconciledTransactions()
const { call: createBankEntry, loading, error, isCompleted } = useFrappePostCall<{ message: { transaction: BankTransaction, journal_entry: JournalEntry } }>('erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.create_bank_entry_and_reconcile')
const setBankRecUnreconcileModalAtom = useSetAtom(bankRecUnreconcileModalAtom)
const addToActionLog = useUpdateActionLog()
const { file: frappeFile } = useContext(FrappeContext) as FrappeConfig
const [isUploading, setIsUploading] = useState(false)
const [uploadProgress, setUploadProgress] = useState(0)
const [files, setFiles] = useState<File[]>([])
const onSubmit = (data: BankEntryFormData) => {
createBankEntry({
bank_transaction_name: selectedTransaction.name,
...data
}).then(async ({ message }) => {
addToActionLog({
type: 'bank_entry',
isBulk: false,
timestamp: (new Date()).getTime(),
items: [
{
bankTransaction: message.transaction,
voucher: {
reference_doctype: "Journal Entry",
reference_name: message.journal_entry.name,
reference_no: message.journal_entry.cheque_no,
reference_date: message.journal_entry.cheque_date,
posting_date: message.journal_entry.posting_date,
doc: message.journal_entry,
}
}
]
})
toast.success(_("Bank Entry Created"), {
duration: 4000,
closeButton: true,
action: {
label: _("Undo"),
onClick: () => setBankRecUnreconcileModalAtom(selectedTransaction.name)
},
actionButtonStyle: {
backgroundColor: "rgb(0, 138, 46)"
}
})
if (files.length > 0) {
setIsUploading(true)
const uploadPromises = files.map(f => {
return frappeFile.uploadFile(f, {
isPrivate: true,
doctype: "Journal Entry",
docname: message.journal_entry.name,
}, (_bytesUploaded, _totalBytes, progress) => {
setUploadProgress((currentProgress) => {
//If there are multiple files, we need to add the progress to the current progress
return currentProgress + ((progress?.progress ?? 0) / files.length)
})
})
})
return Promise.all(uploadPromises).then(() => {
setUploadProgress(0)
setIsUploading(false)
}).catch((error) => {
console.error(error)
toast.error(_("Error uploading attachments"), {
duration: 4000,
})
setIsUploading(false)
})
} else {
return Promise.resolve()
}
}).then(() => {
onReconcile(selectedTransaction)
onClose()
})
}
useHotkeys('meta+s', () => {
form.handleSubmit(onSubmit)()
}, {
enabled: true,
preventDefault: true,
enableOnFormTags: true
})
if (isUploading && isCompleted) {
return <FileUploadBanner uploadProgress={uploadProgress} />
}
return <Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<div className='flex flex-col gap-4'>
{error && <ErrorBanner error={error} />}
<div className='grid grid-cols-2 gap-4'>
<SelectedTransactionDetails transaction={selectedTransaction} />
<div className='flex flex-col gap-4'>
<div className='grid grid-cols-2 gap-4'>
<DateField
name='posting_date'
label={_("Posting Date")}
isRequired
inputProps={{ autoFocus: false }}
/>
<DateField
name='cheque_date'
label={_("Reference Date")}
isRequired
inputProps={{ autoFocus: false }}
rules={{
required: _("Reference Date is required"),
}}
/>
</div>
<DataField name='cheque_no' label={_("Reference")} isRequired inputProps={{ autoFocus: false }}
rules={{
required: _("Reference is required"),
}} />
</div>
</div>
<div>
<Entries company={selectedTransaction.company ?? ''} isWithdrawal={isWithdrawal} currency={selectedTransaction.currency ?? getCompanyCurrency(selectedTransaction.company ?? '')} />
</div>
<div className='flex flex-col gap-2'>
<div className='grid grid-cols-2 gap-4'>
<SmallTextField
name='user_remark'
label={_("Remarks")}
/>
<div
data-slot="form-item"
className="flex flex-col gap-2"
>
<Label>{_("Attachments")}</Label>
<FileDropzone files={files} setFiles={setFiles} />
</div>
</div>
</div>
<DialogFooter>
<DialogClose asChild>
<Button size='md' variant={'outline'} disabled={loading}>{_("Cancel")}</Button>
</DialogClose>
<Button size='md' type='submit' disabled={loading}>{_("Submit")}</Button>
</DialogFooter>
</div>
</form>
</Form>
}
const Entries = ({ company, isWithdrawal, currency }: { company: string, isWithdrawal: boolean, currency: string }) => {
const { getValues, setValue, control } = useFormContext<BankEntryFormData>()
const { call } = useContext(FrappeContext) as FrappeConfig
const partyMapRef = useRef<Record<string, string>>({})
const onPartyChange = (value: string, index: number) => {
// Get the account for the party type
if (value) {
if (partyMapRef.current[value]) {
setValue(`entries.${index}.account`, partyMapRef.current[value])
} else {
call.get('erpnext.accounts.party.get_party_account', {
party: value,
party_type: getValues(`entries.${index}.party_type`),
company: company
}).then((result: { message: string }) => {
setValue(`entries.${index}.account`, result.message)
partyMapRef.current[value] = result.message
})
}
} else {
setValue(`entries.${index}.account`, '')
}
}
const { data: accounts } = useGetAccounts()
const onAccountChange = (value: string, index: number) => {
// If it's an income or expense account, get the default cost center
if (value) {
const account = accounts?.find((acc) => acc.name === value)
if (account && account.report_type === "Profit and Loss") {
// Set the default company cost center
setValue(`entries.${index}.cost_center`, getCompanyCostCenter(company) ?? '')
return
}
}
setValue(`entries.${index}.cost_center`, '')
}
const { fields, append, remove } = useFieldArray({
control: control,
name: 'entries'
})
const onAdd = useCallback(() => {
const existingEntries = getValues('entries')
const totalDebits = existingEntries.reduce((acc, curr) => flt(acc + (curr.debit ?? 0), 2), 0)
const totalCredits = existingEntries.reduce((acc, curr) => flt(acc + (curr.credit ?? 0), 2), 0)
const remainingAmount = flt(totalDebits - totalCredits, 2)
// Remaining amount is credit if it's positive - since some debit is pending to be cleared.
const debitAmount = remainingAmount > 0 ? 0 : Math.abs(remainingAmount)
const creditAmount = remainingAmount > 0 ? Math.abs(remainingAmount) : 0
append({
party_type: '',
party: '',
account: '',
debit: debitAmount,
credit: creditAmount,
cost_center: getCompanyCostCenter(company) ?? ''
} as JournalEntryAccount, {
focusName: `entries.${existingEntries.length}.account`
})
}, [company, append, getValues])
const [selectedRows, setSelectedRows] = useState<number[]>([])
const onSelectRow = useCallback((index: number) => {
setSelectedRows(prev => {
if (prev.includes(index)) {
return prev.filter(i => i !== index)
}
return [...prev, index]
})
}, [])
const onSelectAll = useCallback(() => {
setSelectedRows(prev => {
if (prev.length === fields.length) {
return []
}
return [...fields.map((_, index) => index)]
})
}, [fields])
const onRemove = useCallback(() => {
remove(selectedRows)
setSelectedRows([])
}, [remove, selectedRows])
/**
* When add difference is clicked, check if the last row has nothing filled in.
* If last row is empty (no debit or credit), then set that row's amount. Else, add a new row with the difference amount.
*/
const onAddDifferenceClicked = () => {
const existingEntries = getValues('entries')
const totalDebits = existingEntries.reduce((acc, curr) => flt(acc + (curr.debit ?? 0), 2), 0)
const totalCredits = existingEntries.reduce((acc, curr) => flt(acc + (curr.credit ?? 0), 2), 0)
const lastIndex = existingEntries.length - 1
const isLastRowEmpty = (existingEntries[lastIndex]?.debit === 0 || existingEntries[lastIndex]?.debit === undefined) && (existingEntries[lastIndex]?.credit === 0 || existingEntries[lastIndex]?.credit === undefined)
const remainingAmount = flt(totalDebits - totalCredits, 2)
// Remaining amount is credit if it's positive - since some debit is pending to be cleared.
const debitAmount = remainingAmount > 0 ? 0 : Math.abs(remainingAmount)
const creditAmount = remainingAmount > 0 ? Math.abs(remainingAmount) : 0
if (isLastRowEmpty) {
setValue(`entries.${lastIndex}.debit`, debitAmount)
setValue(`entries.${lastIndex}.credit`, creditAmount)
} else {
append({
party_type: '',
party: '',
account: '',
debit: debitAmount,
credit: creditAmount,
cost_center: getCompanyCostCenter(company) ?? ''
} as JournalEntryAccount, {
focusName: `entries.${existingEntries.length}.account`
})
}
}
return <div className="flex flex-col gap-2">
<Table>
<TableHeader>
<TableRow>
<TableHead><Checkbox
disabled={fields.length === 0}
// Make this accessible to screen readers
aria-label={_("Select all")}
checked={selectedRows.length > 0 && selectedRows.length === fields.length}
onCheckedChange={onSelectAll} /></TableHead>
<TableHead>{_("Party")}</TableHead>
<TableHead>{_("Account")}</TableHead>
<TableHead>{_("Cost Center")}</TableHead>
<TableHead>{_("Remarks")}</TableHead>
<TableHead className="text-end">{_("Debit")}</TableHead>
<TableHead className="text-end">{_("Credit")}</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{fields.map((field, index) => (
<TableRow key={field.id} className={index === 0 ? 'bg-surface-gray-1 cursor-not-allowed' : ''} title={index === 0 ? _("This is the bank account entry. You cannot edit it.") : ''}>
<TableCell>
<Checkbox
checked={selectedRows.includes(index)}
onCheckedChange={() => onSelectRow(index)}
// Make this accessible to screen readers
aria-label={_("Select row {0}", [String(index + 1)])}
disabled={index === 0}
/>
</TableCell>
<TableCell className="align-top">
<div className="flex">
<PartyTypeFormField
name={`entries.${index}.party_type`}
label={_("Party Type")}
isRequired
readOnly={index === 0}
hideLabel
inputProps={{
type: isWithdrawal ? 'Payable' : 'Receivable',
triggerProps: {
className: 'rounded-e-none',
tabIndex: -1
},
readOnly: index === 0,
}} />
<PartyField index={index} onChange={onPartyChange} readOnly={index === 0} />
</div>
</TableCell>
<TableCell className="align-top">
<AccountFormField
name={`entries.${index}.account`}
label={_("Account")}
rules={{
required: _("Account is required"),
onChange: (event) => {
onAccountChange(event.target.value, index)
}
}}
buttonClassName="min-w-64"
readOnly={index === 0}
isRequired
hideLabel
/>
</TableCell>
<TableCell className="align-top">
<LinkFormField
doctype="Cost Center"
name={`entries.${index}.cost_center`}
label={_("Cost Center")}
filters={[["company", "=", company], ["is_group", "=", 0], ["disabled", "=", 0]]}
buttonClassName="min-w-48"
readOnly={index === 0}
hideLabel
/>
</TableCell>
<TableCell className="align-top">
<DataField
name={`entries.${index}.user_remark`}
label={_("Remarks")}
readOnly={index === 0}
inputProps={{
placeholder: _("e.g. Bank Charges"),
className: 'min-w-64',
readOnly: index === 0
}}
hideLabel
/>
</TableCell>
<TableCell className={cn("text-end align-top")}>
<CurrencyFormField
name={`entries.${index}.debit`}
label={_("Debit")}
isRequired
hideLabel
readOnly={index === 0}
style={index === 0 ? !isWithdrawal ? {
color: "var(--color-ink-gray-8)",
} : {} : {}}
currency={currency}
leftSlot={index === 0 && !isWithdrawal ? <Tooltip>
<TooltipTrigger asChild><ArrowDownRight className="text-ink-green-3" /></TooltipTrigger>
<TooltipContent>{_("Bank account debit for deposit")}</TooltipContent>
</Tooltip> : undefined}
/>
</TableCell>
<TableCell className={cn("text-end align-top")}>
<CurrencyFormField
name={`entries.${index}.credit`}
style={index === 0 && isWithdrawal ? {
color: "var(--color-ink-gray-8)",
} : {}}
label={_("Credit")}
isRequired
hideLabel
readOnly={index === 0}
currency={currency}
leftSlot={index === 0 && isWithdrawal ? <Tooltip>
<TooltipTrigger asChild><ArrowUpRight className="text-ink-red-3" /></TooltipTrigger>
<TooltipContent>{_("Bank account credit for withdrawal")}</TooltipContent>
</Tooltip> : undefined}
/>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
<div className="flex justify-between gap-2">
<div className="flex gap-2 justify-end">
<div>
<Button size='sm' type='button' variant={'outline'} onClick={onAdd}><Plus /> {_("Add Row")}</Button>
</div>
{selectedRows.length > 0 && <div>
<Button size='sm' type='button' theme="red" onClick={onRemove}><Trash2 /> {_("Remove")}</Button>
</div>}
</div>
<Summary currency={currency} addRow={onAddDifferenceClicked} />
</div>
</div>
}
const PartyField = ({ index, onChange, readOnly }: { index: number, onChange: (value: string, index: number) => void, readOnly: boolean }) => {
const { control } = useFormContext<BankEntryFormData>()
const party_type = useWatch({
control,
name: `entries.${index}.party_type`
})
if (!party_type) {
return <DataField
name={`entries.${index}.party`}
label={_("Party")}
isRequired
inputProps={{
disabled: true,
className: 'rounded-s-none border-s-0 min-w-64'
}}
hideLabel
/>
}
return <LinkFormField
name={`entries.${index}.party`}
label={_("Party")}
rules={{
onChange: (event) => {
onChange(event.target.value, index)
},
}}
hideLabel
readOnly={readOnly}
buttonClassName="rounded-s-none border-s-0 min-w-64"
doctype={party_type}
/>
}
const Summary = ({ currency, addRow }: { currency: string, addRow: () => void }) => {
const { control } = useFormContext<BankEntryFormData>()
const entries = useWatch({ control, name: 'entries' })
const { total, totalCredits, totalDebits } = useMemo(() => {
// Do a total debits - total credits
const totalDebits = entries.reduce((acc, curr) => flt(acc + (curr.debit ?? 0), 2), 0)
const totalCredits = entries.reduce((acc, curr) => flt(acc + (curr.credit ?? 0), 2), 0)
return { total: flt(totalDebits - totalCredits, 2), totalDebits, totalCredits }
}, [entries])
const onAddRow = useCallback(() => {
addRow()
}, [addRow])
const TextComponent = ({ className, children }: { className?: string, children: React.ReactNode }) => {
return <span className={cn("w-32 text-end font-medium text-sm font-numeric", className)}>{children}</span>
}
return <div className="flex flex-col gap-2 items-end">
<div className="flex gap-2 justify-between">
<TextComponent>{_("Total Debit")}</TextComponent>
<TextComponent>{formatCurrency(totalDebits, currency)}</TextComponent>
</div>
<div className="flex gap-2 justify-between">
<TextComponent>{_("Total Credit")}</TextComponent>
<TextComponent>{formatCurrency(totalCredits, currency)}</TextComponent>
</div>
{total !== 0 && <div className="flex gap-2 justify-between">
<TextComponent>{_("Difference")}</TextComponent>
<Tooltip>
<TooltipTrigger asChild>
<Button type='button' variant='link' className="p-0 text-ink-red-3 underline h-fit" role='button' onClick={onAddRow}>
<TextComponent className='text-ink-red-3'>{formatCurrency(total, currency)}</TextComponent>
</Button>
</TooltipTrigger>
<TooltipContent>
{_("Add a row with the difference amount")}
</TooltipContent>
</Tooltip>
</div>}
</div>
}
export default BankEntryModal

View File

@@ -1,134 +0,0 @@
import { useAtom } from "jotai"
import { SelectedBank, selectedBankAccountAtom } from "./bankRecAtoms"
import { useCallback } from "react"
import { useGetBankAccounts, useGetUnreconciledTransactions } from "./utils"
import { cn } from "@/lib/utils"
import { getTimeago } from "@/lib/date"
import ErrorBanner from "@/components/ui/error-banner"
import _ from "@/lib/translate"
import { Badge } from "@/components/ui/badge"
import { useTheme } from "@/components/ui/theme-provider"
import BankLogo from "@/components/common/BankLogo"
import { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from "@/components/ui/empty"
import { LandmarkIcon } from "lucide-react"
import { Button } from "@/components/ui/button"
import { useCurrentCompany } from "@/hooks/useCurrentCompany"
const BankPicker = ({ className }: { className?: string }) => {
const [selectedBank, setSelectedBank] = useAtom(selectedBankAccountAtom)
const onLoadingSuccess = useCallback((data?: SelectedBank[]) => {
// If the bank is already selected, then don't set it again
if (selectedBank) {
// Check if selected bank is in the data
if (data?.some((bank: SelectedBank) => bank.name === selectedBank.name)) {
return
}
}
if (!data) return
if (data.length === 1) {
setSelectedBank(data[0])
} else if (data.length > 1) {
const defaultBank = data.find((bank: SelectedBank) => bank.is_default)
if (defaultBank) {
setSelectedBank(defaultBank)
} else {
// Select the first available bank account
setSelectedBank(data[0])
}
}
}, [setSelectedBank, selectedBank])
const selectedCompany = useCurrentCompany()
const { banks, isLoading, error } = useGetBankAccounts(onLoadingSuccess)
const { themeValue } = useTheme()
if (isLoading) {
return null
}
if (error) {
return <ErrorBanner error={error} />
}
if (banks?.length === 0) {
return <Empty>
<EmptyMedia>
<LandmarkIcon />
</EmptyMedia>
<EmptyHeader>
<EmptyTitle>{_("No bank accounts found")}</EmptyTitle>
<EmptyDescription>{_("You have not added any bank accounts to your company.")}</EmptyDescription>
</EmptyHeader>
<EmptyContent>
<Button asChild>
<a href={`/desk/bank-account?company=${encodeURIComponent(selectedCompany)}&is_company_account=1`}>
{_("Configure Bank Accounts")}
</a>
</Button>
</EmptyContent>
</Empty>
}
return (
<div
className={cn("flex gap-3 items-stretch w-full overflow-x-auto pe-4",
banks?.length > 4 ? 'pb-2' : '', className,
)}
style={{
scrollbarWidth: 'thin',
scrollbarColor: themeValue === 'Dark' ? 'var(--surface-gray-2) var(--surface-gray-1)' : 'rgb(209 213 219) rgb(243 244 246)',
}}
>
{
banks?.map((bank) => (
<BankPickerItem key={bank.name} bank={bank} />
))
}
</div>
)
}
const BankPickerItem = ({ bank }: { bank: SelectedBank }) => {
const [selectedBank, setSelectedBank] = useAtom(selectedBankAccountAtom)
const isSelected = selectedBank?.name === bank.name
const { mutate } = useGetUnreconciledTransactions()
const onSelect = () => {
setSelectedBank(bank)
mutate()
}
return <div
role="button"
title={`Select ${bank.account_name}`}
onClick={onSelect}
className={cn('rounded-md border border-outline-gray-1 max-w-60 min-w-60 p-2 overflow-hidden cursor-pointer',
isSelected ? 'border-outline-gray-5 bg-surface-gray-1' : 'hover:bg-surface-gray-1'
)}
>
<BankLogo bank={bank} className="mb-2" />
<div className="flex flex-col gap-1">
<div className="flex gap-2 items-center">
<span className={cn("text-sm font-medium line-clamp-1 text-ink-gray-8")}>{bank.account_name}</span>
{bank.account_type && <Badge variant='subtle' size='sm' theme='gray'>
{bank.account_type?.slice(0, 24)}
</Badge>}
</div>
<span title={_("GL Account")} className={cn("text-ellipsis line-clamp-1 text-sm text-ink-gray-6")}>{bank.account}</span>
{bank.last_integration_date && <span className="text-xs text-ink-gray-5">{_("Last Synced Transaction")}: {getTimeago(bank.last_integration_date)}</span>}
</div>
</div >
}
export default BankPicker

View File

@@ -1,275 +0,0 @@
import { useAtom } from 'jotai'
import { bankRecDateAtom } from './bankRecAtoms'
import { useMemo, useState } from 'react'
import { AVAILABLE_TIME_PERIODS, formatDate, getDatesForTimePeriod, TimePeriod } from '@/lib/date'
import { Button } from '@/components/ui/button'
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
import { ChevronDownIcon, ChevronLeftIcon, ChevronRight } from 'lucide-react'
import { Command, CommandEmpty, CommandInput, CommandItem, CommandList } from '@/components/ui/command'
import { parse } from "chrono-node"
import { Calendar } from '@/components/ui/calendar'
import useFiscalYear from '@/hooks/useFiscalYear'
import dayjs from 'dayjs'
import _ from '@/lib/translate'
import { useDirection } from '@/components/ui/direction'
const BankRecDateFilter = () => {
const [bankRecDate, setBankRecDate] = useAtom(bankRecDateAtom)
const { data: fiscalYear } = useFiscalYear()
const timePeriodOptions = useMemo(() => {
const standardOptions = AVAILABLE_TIME_PERIODS.map((period) => {
const dates = getDatesForTimePeriod(period)
return {
label: period,
fromDate: dates.fromDate,
toDate: dates.toDate,
format: dates.format,
translatedLabel: dates.translatedLabel
}
})
if (fiscalYear?.message) {
// For a fiscal year, we need to replace "Last Year", "This Year", and add options for quarters
const fiscalYearStart = fiscalYear.message.year_start_date
const fiscalYearEnd = fiscalYear.message.year_end_date
const q1 = {
label: `Q1: ${fiscalYear.message.name}`,
translatedLabel: `${_("Q1")}: ${fiscalYear.message.name}`,
fromDate: fiscalYearStart,
toDate: dayjs(fiscalYearStart).add(3, 'month').format('YYYY-MM-DD'),
format: 'MMM YYYY'
}
const q2 = {
label: `Q2: ${fiscalYear.message.name}`,
translatedLabel: `${_("Q2")}: ${fiscalYear.message.name}`,
fromDate: dayjs(fiscalYearStart).add(3, 'month').format('YYYY-MM-DD'),
toDate: dayjs(fiscalYearStart).add(6, 'month').format('YYYY-MM-DD'),
format: 'MMM YYYY'
}
const q3 = {
label: `Q3: ${fiscalYear.message.name}`,
translatedLabel: `${_("Q3")}: ${fiscalYear.message.name}`,
fromDate: dayjs(fiscalYearStart).add(6, 'month').format('YYYY-MM-DD'),
toDate: dayjs(fiscalYearStart).add(9, 'month').format('YYYY-MM-DD'),
format: 'MMM YYYY'
}
const q4 = {
label: `Q4: ${fiscalYear.message.name}`,
translatedLabel: `${_("Q4")}: ${fiscalYear.message.name}`,
fromDate: dayjs(fiscalYearStart).add(9, 'month').format('YYYY-MM-DD'),
toDate: fiscalYearEnd,
format: 'MMM YYYY'
}
const thisYear = {
label: `This Fiscal Year`,
translatedLabel: `${_("This Fiscal Year")}`,
fromDate: fiscalYearStart,
toDate: fiscalYearEnd,
format: 'MMM YYYY'
}
const lastYear = {
label: `Last Fiscal Year`,
translatedLabel: `${_("Last Fiscal Year")}`,
fromDate: dayjs(fiscalYearStart).subtract(1, 'year').format('YYYY-MM-DD'),
toDate: dayjs(fiscalYearEnd).subtract(1, 'year').format('YYYY-MM-DD'),
format: 'MMM YYYY'
}
// Sort the options so that we get "This Month", "Last Month", quarters, fiscal year, then the rest of the standard options
const topRankedItems = standardOptions.filter((option) => {
return option.label === "This Month" || option.label === "Last Month"
})
const bottomRankedItems = standardOptions.filter((option) => {
return option.label !== "This Month" && option.label !== "Last Month"
})
return [...topRankedItems, q1, q2, q3, q4, thisYear, lastYear, ...bottomRankedItems]
}
return standardOptions
}, [fiscalYear])
const [open, setOpen] = useState(false)
const [value, setValue] = useState("")
const timePeriod: TimePeriod | string = useMemo(() => {
if (bankRecDate.fromDate && bankRecDate.toDate) {
// Check if the from and to dates match any predefined time period
for (const period of timePeriodOptions) {
if (period.fromDate === bankRecDate.fromDate && period.toDate === bankRecDate.toDate) {
return period.label;
}
}
return "Date Range";
} else {
return "Date Range";
}
}, [bankRecDate.fromDate, bankRecDate.toDate, timePeriodOptions]);
const handleTimePeriodChange = (fromDate: string, toDate: string) => {
setBankRecDate({ fromDate, toDate })
setOpen(false)
}
const dateObj = useMemo(() => {
return {
from: new Date(bankRecDate.fromDate),
to: new Date(bankRecDate.toDate)
}
}, [bankRecDate.fromDate, bankRecDate.toDate])
const direction = useDirection()
return <div className='flex items-center'>
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
variant={'outline'}
aria-expanded={open}
size='md'
className='rounded-e-none border-e-0'
role="combobox">
{timePeriodOptions.find((period) => period.label === timePeriod)?.translatedLabel ?? _(timePeriod)}
<ChevronDownIcon />
</Button>
</PopoverTrigger>
<PopoverContent className="w-84 p-1" align='start'>
<Command>
<CommandInput placeholder="e.g. Last 3 weeks" onValueChange={setValue} value={value} />
<CommandList className='max-h-fit'>
<CommandEmpty className='text-start p-2 hover:bg-surface-gray-1'>
<EmptyState onSelect={handleTimePeriodChange} value={value} />
</CommandEmpty>
{timePeriodOptions.map((period) => (
<CommandItem key={period.label} className='flex justify-between' onSelect={() => handleTimePeriodChange(period.fromDate, period.toDate)}>
<span>
{period.translatedLabel ?? _(period.label)}
</span>
<span className='text-xs text-ink-gray-5 flex items-center gap-1 text-end whitespace-nowrap'>
{formatDate(period.fromDate, period.format)} {direction === 'ltr' ? <ChevronRight className='text-[12px] text-ink-gray-5/70' /> : <ChevronLeftIcon className='text-[12px] text-ink-gray-5/70' />} {formatDate(period.toDate, period.format)}
</span>
</CommandItem>
))}
</CommandList>
</Command>
</PopoverContent>
</Popover>
<Popover>
<PopoverTrigger asChild>
<Button variant={'outline'} className='rounded-s-none' size='md'>
{formatDate(bankRecDate.fromDate)} - {formatDate(bankRecDate.toDate)}
</Button>
</PopoverTrigger>
<PopoverContent className='w-auto overflow-hidden p-0' align='end'>
<Calendar
mode='range'
captionLayout='dropdown'
selected={{
from: dateObj.from,
to: dateObj.to
}}
numberOfMonths={2}
defaultMonth={dateObj.from}
onSelect={(date) => {
if (date) {
setBankRecDate({ fromDate: formatDate(date.from, 'YYYY-MM-DD'), toDate: formatDate(date.to, 'YYYY-MM-DD') })
}
}}
/>
</PopoverContent>
</Popover>
</div>
}
const referentialKeywords = ["last", "this", "next", "previous"]
const EmptyState = ({ onSelect, value }: { onSelect: (fromDate: string, toDate: string) => void, value: string }) => {
const dates = useMemo(() => {
if (value) {
// Try parsing the value
const parsedDate = parse(value, undefined, { forwardDate: false })
if (parsedDate && parsedDate.length > 0) {
const startDate = parsedDate[0].start.date()
const endDate = parsedDate[0].end?.date()
if (!endDate) {
const today = new Date()
// If today is greater than the start date, use today as the end date
if (startDate.getTime() > today.getTime()) {
return { fromDate: today, toDate: startDate }
} else {
// Check if the user only wants a specific month like "May 2025"
// If the "known values" just has month and year, then we need to get the first day of the month and the last day of the month
// @ts-expect-error - "Known Values" is available in the start "ParsingComponents"
if (parsedDate[0].start.knownValues?.month && !parsedDate[0].start.knownValues?.day) {
return {
fromDate: startDate,
toDate: dayjs(startDate).endOf('month').toDate()
}
// @ts-expect-error - "Known Values" is available in the start "ParsingComponents"
} else if (parsedDate[0].start.knownValues?.month && parsedDate[0].start.knownValues?.day && !referentialKeywords.some(keyword => value.toLowerCase().includes(keyword))) {
// If month and day is known, then we should not assume that the user wants to get everything until today
return {
fromDate: startDate,
toDate: startDate,
}
}
return {
fromDate: startDate,
toDate: today
}
}
} else {
return { fromDate: startDate, toDate: endDate }
}
}
}
}, [value])
const onClick = (fromDate: Date, toDate: Date) => {
onSelect(formatDate(fromDate, 'YYYY-MM-DD'), formatDate(toDate, 'YYYY-MM-DD'))
}
const isEqual = dates?.fromDate && dates?.toDate && dayjs(dates.fromDate).isSame(dates.toDate, 'date')
return <div>
{dates ?
<div className='flex gap-2 items-center justify-between cursor-pointer' onClick={() => onClick(dates.fromDate, dates.toDate)}>
<span className='text-sm text-ink-gray-5 max-w-[30%]'>
{value}
</span>
{isEqual ? <span className='text-xs text-ink-gray-5 text-balance flex items-center gap-1'>
{formatDate(dates.fromDate, 'Do MMM YYYY')}
</span> :
<span className='text-xs text-ink-gray-5 flex items-center gap-1'>
{formatDate(dates.fromDate, 'Do MMM YY')} <ChevronRight size='16' className='text-ink-gray-5' /> {formatDate(dates.toDate, 'Do MMM YY')}
</span>}
</div> :
<span className='text-sm text-ink-gray-5'>
No results found
</span>
}
</div>
}
export default BankRecDateFilter

View File

@@ -1,315 +0,0 @@
import { useAtomValue } from "jotai"
import { MissingFiltersBanner } from "./MissingFiltersBanner"
import { bankRecDateAtom, selectedBankAccountAtom } from "./bankRecAtoms"
import { useCurrentCompany } from "@/hooks/useCurrentCompany"
import { Paragraph } from "@/components/ui/typography"
import { useCallback, useMemo } from "react"
import type { ColumnDef } from "@tanstack/react-table"
import { useFrappeGetCall } from "frappe-react-sdk"
import { QueryReportReturnType } from "@/types/custom/Reports"
import { formatDate } from "@/lib/date"
import { ListView, type ListViewColumnMeta } from "@/components/ui/list-view"
import { formatCurrency } from "@/lib/numbers"
import { getCompanyCurrency } from "@/lib/company"
import { slug } from "@/lib/frappe"
import { ScrollTextIcon } from "lucide-react"
import ErrorBanner from "@/components/ui/error-banner"
import { StatContainer, StatLabel, StatValue } from "@/components/ui/stats"
import _ from "@/lib/translate"
import { toast } from "sonner"
import { useCopyToClipboard } from "usehooks-ts"
import { Empty, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from "@/components/ui/empty"
const BankReconciliationStatement = () => {
const bankAccount = useAtomValue(selectedBankAccountAtom)
const dates = useAtomValue(bankRecDateAtom)
if (!bankAccount) {
return <MissingFiltersBanner text={_("Please select a bank account to view the bank reconciliation statement.")} />
}
if (!dates) {
return <MissingFiltersBanner text={_("Please select dates to view the bank reconciliation statement.")} />
}
return <BankReconciliationStatementView />
}
interface BankClearanceSummaryEntry {
payment_document: string
payment_entry: string
posting_date: string,
reference_no: string,
credit: number,
debit: number,
against_account: string,
ref_date: string,
account_currency: string,
clearance_date: string
}
const BankReconciliationStatementView = () => {
const companyID = useCurrentCompany()
const bankAccount = useAtomValue(selectedBankAccountAtom)
const dates = useAtomValue(bankRecDateAtom)
const filters = useMemo(() => {
return JSON.stringify({
account: bankAccount?.account,
report_date: dates.toDate,
company: companyID
})
}, [bankAccount, dates, companyID])
const { data, error } = useFrappeGetCall<{ message: QueryReportReturnType }>('frappe.desk.query_report.run', {
report_name: 'Bank Reconciliation Statement',
filters,
ignore_prepared_report: 1,
are_default_filters: false,
}, `Report-Bank Reconciliation Statement-${filters}`, { keepPreviousData: true, revalidateOnFocus: false }, 'POST')
const [, copyToClipboard] = useCopyToClipboard()
const onCopy = useCallback(
(text: string) => {
copyToClipboard(text).then(() => {
toast.success(_("Copied to clipboard"))
})
},
[copyToClipboard, _],
)
const statementColumns = useMemo<ColumnDef<BankClearanceSummaryEntry, unknown>[]>(
() => [
{
accessorKey: "posting_date",
header: _("Posting Date"),
size: 118,
meta: { tabularNums: true } satisfies ListViewColumnMeta,
cell: ({ row }) => formatDate(row.original.posting_date),
},
{
accessorKey: "payment_document",
header: _("Document Type"),
size: 140,
cell: ({ row }) => _(row.original.payment_document),
},
{
id: "payment_entry",
header: _("Payment Document"),
size: 300,
meta: {
getTooltipText: (r) => {
const x = r as BankClearanceSummaryEntry
const parts = [x.payment_document, x.payment_entry].filter(Boolean)
return parts.length ? parts.join(" · ") : undefined
},
} satisfies ListViewColumnMeta,
cell: ({ row }) => {
const { payment_document, payment_entry } = row.original
return payment_document ? (
<a
target="_blank"
rel="noreferrer"
className="text-ink-gray-8 block min-w-0 w-full underline underline-offset-4"
href={`/desk/${slug(payment_document)}/${payment_entry}`}
>
{payment_entry}
</a>
) : (
payment_entry
)
},
},
{
accessorKey: "debit",
header: _("Debit"),
size: 112,
meta: { align: "right" } satisfies ListViewColumnMeta,
cell: ({ row }) => <span className="font-numeric">{formatCurrency(row.original.debit, row.original.account_currency)}</span>,
},
{
accessorKey: "credit",
header: _("Credit"),
size: 112,
meta: { align: "right" } satisfies ListViewColumnMeta,
cell: ({ row }) => <span className="font-numeric">{formatCurrency(row.original.credit, row.original.account_currency)}</span>,
},
{
accessorKey: "against_account",
header: _("Against Account"),
meta: { gridWidth: "minmax(0,1.25fr)" } satisfies ListViewColumnMeta,
cell: ({ row }) => (
<a
target="_blank"
rel="noreferrer"
className="text-ink-gray-8 block min-w-0 w-full underline underline-offset-4"
href={`/desk/account/${row.original.against_account}`}
>
{row.original.against_account}
</a>
),
},
{
accessorKey: "reference_no",
header: _("Reference #"),
cell: ({ row }) => {
const ref = row.original.reference_no
return (
<button
type="button"
className="text-ink-gray-8 hover:underline min-w-0 w-full cursor-pointer truncate text-start underline-offset-4"
onClick={() => onCopy(ref)}
>
{ref}
</button>
)
},
},
{
accessorKey: "ref_date",
header: _("Reference Date"),
size: 118,
meta: { tabularNums: true } satisfies ListViewColumnMeta,
cell: ({ row }) => formatDate(row.original.ref_date),
},
{
accessorKey: "clearance_date",
header: _("Clearance Date"),
size: 118,
meta: { tabularNums: true } satisfies ListViewColumnMeta,
cell: ({ row }) => formatDate(row.original.clearance_date),
},
],
[_, onCopy],
)
const statementRows = useMemo(() => {
if (!data?.message.result) return []
return data.message.result.filter((row: BankClearanceSummaryEntry) => Boolean(row.payment_entry))
}, [data])
return <div className="space-y-4 py-2">
<div>
<Paragraph className="text-sm">
<span dangerouslySetInnerHTML={{
__html: _("Below is a list of all entries posted against the bank account {0} which have not been cleared till {1}.", [`<strong>${bankAccount?.account}</strong>`, `<strong>${formatDate(dates.toDate)}</strong>`])
}} />
</Paragraph>
</div>
{error && <ErrorBanner error={error} />}
{data && <SummarySection data={data} />}
{data && data.message.result.length > 0 && (
<div className="space-y-2">
<p className="text-ink-gray-5 text-sm">{_("Bank Reconciliation Statement")}</p>
<ListView
data={statementRows}
columns={statementColumns}
getRowId={(row) => row.payment_entry}
maxHeight="min(70vh, 640px)"
emptyState={_("No entries with a payment document in this list.")}
/>
</div>
)}
{data && data.message.result.length === 0 &&
<Empty>
<EmptyMedia>
<ScrollTextIcon />
</EmptyMedia>
<EmptyHeader>
<EmptyTitle>{_("No entries found")}</EmptyTitle>
<EmptyDescription>{_("There are no accounting entries in the system for the selected account and dates.")}</EmptyDescription>
</EmptyHeader>
</Empty>
}
</div>
}
const SummarySection = ({ data }: { data: { message: QueryReportReturnType } }) => {
const company = useCurrentCompany()
const bankAccount = useAtomValue(selectedBankAccountAtom)
const { bankStatementBalanceAsPerGL, outstandingChecksDebit, outstandingChecksCredit, incorrectlyClearedEntriesDebit, incorrectlyClearedEntriesCredit, calculatedBankStatementBalance } = useMemo(() => {
// Loop over the results and find the corresponding rows
let bankStatementBalanceAsPerGL = 0
let outstandingChecksDebit = 0
let outstandingChecksCredit = 0
let incorrectlyClearedEntriesDebit = 0
let incorrectlyClearedEntriesCredit = 0
let calculatedBankStatementBalance = 0
// eslint-disable-next-line @typescript-eslint/no-explicit-any
data?.message.result.forEach((r: any) => {
if (r.payment_entry === 'Bank Statement balance as per General Ledger') {
bankStatementBalanceAsPerGL = r.debit - r.credit
}
if (r.payment_entry === 'Outstanding Checks and Deposits to clear') {
outstandingChecksDebit = r.debit
outstandingChecksCredit = r.credit
}
if (r.payment_entry === 'Checks and Deposits incorrectly cleared') {
incorrectlyClearedEntriesDebit = r.debit
incorrectlyClearedEntriesCredit = r.credit
}
if (r.payment_entry === 'Calculated Bank Statement balance') {
calculatedBankStatementBalance = r.debit - r.credit
}
})
return {
bankStatementBalanceAsPerGL,
outstandingChecksDebit,
outstandingChecksCredit,
incorrectlyClearedEntriesDebit,
incorrectlyClearedEntriesCredit,
calculatedBankStatementBalance
}
}, [data])
const currency = bankAccount?.account_currency ?? getCompanyCurrency(company)
return <div className="flex gap-4 items-start justify-between">
<StatContainer>
<StatLabel>{_("Bank Statement Balance as per General Ledger")}</StatLabel>
<StatValue className="font-numeric">{formatCurrency(bankStatementBalanceAsPerGL, currency)}</StatValue>
</StatContainer>
<StatContainer>
<StatLabel>{_("Outstanding Checks and Deposits to clear")}</StatLabel>
<StatValue className="font-numeric">{formatCurrency(outstandingChecksDebit - outstandingChecksCredit, currency)}</StatValue>
</StatContainer>
{(incorrectlyClearedEntriesDebit > 0 || incorrectlyClearedEntriesCredit > 0) && <StatContainer>
<StatLabel className="text-ink-red-3">{_("Checks and Deposits incorrectly cleared")}</StatLabel>
<StatValue className="text-ink-red-3 font-numeric">{formatCurrency(incorrectlyClearedEntriesDebit - incorrectlyClearedEntriesCredit)}</StatValue>
{/* <div className="" divider={<StackDivider height='20px' />}>
{incorrectlyClearedEntriesDebit !== 0 && <StatHelpText>Debit: {formatCurrency(incorrectlyClearedEntriesDebit)}</StatHelpText>}
{incorrectlyClearedEntriesCredit !== 0 && <StatHelpText>Credit: {formatCurrency(incorrectlyClearedEntriesCredit)}</StatHelpText>}
</div> */}
</StatContainer>}
<StatContainer>
<StatLabel>{_("Calculated Bank Statement Balance")}</StatLabel>
<StatValue className="font-numeric">{formatCurrency(calculatedBankStatementBalance)}</StatValue>
</StatContainer>
</div>
}
export default BankReconciliationStatement

View File

@@ -1,422 +0,0 @@
import { useAtomValue, useSetAtom } from "jotai"
import { MissingFiltersBanner } from "./MissingFiltersBanner"
import { bankRecDateAtom, bankRecUnreconcileModalAtom, selectedBankAccountAtom } from "./bankRecAtoms"
import { Paragraph } from "@/components/ui/typography"
import { formatDate } from "@/lib/date"
import { ListView, type ListViewColumnMeta } from "@/components/ui/list-view"
import { formatCurrency, getCurrencyFormatInfo } from "@/lib/numbers"
import { getCompanyCurrency } from "@/lib/company"
import { ArrowDownRight, ArrowUpRight, CheckCircle2, ChevronDown, DollarSign, ExternalLink, ImportIcon, ListIcon, Search, Undo2, XCircle } from "lucide-react"
import ErrorBanner from "@/components/ui/error-banner"
import { Badge } from "@/components/ui/badge"
import { useGetBankTransactions } from "./utils"
import { BankTransaction } from "@/types/Accounts/BankTransaction"
import { Button } from "@/components/ui/button"
import _ from "@/lib/translate"
import { Input } from "@/components/ui/input"
import CurrencyInput from "react-currency-input-field"
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
import { getCurrencySymbol } from "@/lib/currency"
import { useDebounceValue } from "usehooks-ts"
import type { ColumnDef } from "@tanstack/react-table"
import { useCallback, useMemo, useState } from "react"
import { Link } from "react-router"
import { Empty, EmptyTitle, EmptyHeader, EmptyMedia, EmptyDescription, EmptyContent } from "@/components/ui/empty"
import { InputGroup, InputGroupAddon } from "@/components/ui/input-group"
const BankTransactions = () => {
const selectedBank = useAtomValue(selectedBankAccountAtom)
const dates = useAtomValue(bankRecDateAtom)
if (!selectedBank || !dates) {
return <MissingFiltersBanner text={_("Please select a bank and set the date range")} />
}
return <>
<BankTransactionListView />
</>
}
const BankTransactionListView = () => {
const { data, error } = useGetBankTransactions()
const bankAccount = useAtomValue(selectedBankAccountAtom)
const dates = useAtomValue(bankRecDateAtom)
const formattedFromDate = formatDate(dates.fromDate)
const formattedToDate = formatDate(dates.toDate)
const setBankRecUnreconcileModalAtom = useSetAtom(bankRecUnreconcileModalAtom)
const onUndo = useCallback(
(transaction: BankTransaction) => {
setBankRecUnreconcileModalAtom(transaction.name)
},
[setBankRecUnreconcileModalAtom],
)
const accountCurrency = useMemo(
() => bankAccount?.account_currency ?? getCompanyCurrency(bankAccount?.company ?? ""),
[bankAccount?.account_currency, bankAccount?.company],
)
const transactionColumns = useMemo<ColumnDef<BankTransaction, unknown>[]>(
() => [
{
accessorKey: "date",
header: _("Date"),
size: 112,
meta: { tabularNums: true } satisfies ListViewColumnMeta,
cell: ({ row }) => formatDate(row.original.date),
},
{
accessorKey: "description",
header: _("Description"),
size: 250,
// meta: { gridWidth: "minmax(0,2fr)" } satisfies ListViewColumnMeta,
cell: ({ row }) => row.original.description,
},
{
accessorKey: "reference_number",
header: _("Reference #"),
size: 128,
cell: ({ row }) => row.original.reference_number,
},
{
accessorKey: "withdrawal",
header: _("Withdrawal"),
size: 120,
meta: { align: "right" } satisfies ListViewColumnMeta,
cell: ({ row }) => <span className="font-numeric">{formatCurrency(row.original.withdrawal, accountCurrency)}</span>,
},
{
accessorKey: "deposit",
header: _("Deposit"),
size: 120,
meta: { align: "right" } satisfies ListViewColumnMeta,
cell: ({ row }) => <span className="font-numeric">{formatCurrency(row.original.deposit, accountCurrency)}</span>,
},
{
accessorKey: "unallocated_amount",
header: _("Unallocated"),
size: 120,
meta: { align: "right" } satisfies ListViewColumnMeta,
cell: ({ row }) => <span className="font-numeric">{formatCurrency(row.original.unallocated_amount, accountCurrency)}</span>,
},
{
accessorKey: "transaction_type",
header: _("Type"),
size: 112,
cell: ({ row }) =>
row.original.transaction_type ? <Badge>{row.original.transaction_type}</Badge> : null,
},
{
id: "status",
header: _("Status"),
size: 168,
meta: { truncate: false, truncateTooltip: false } satisfies ListViewColumnMeta,
cell: ({ row }) => {
const tx = row.original
if (!tx.allocated_amount || (tx.allocated_amount && tx.allocated_amount === 0)) {
return (
<Badge theme="red">
<XCircle />
{_("Not Reconciled")}
</Badge>
)
}
if (tx.allocated_amount && tx.allocated_amount > 0 && tx.unallocated_amount !== 0) {
return (
<Badge theme="orange">
<CheckCircle2 />
{_("Partially Reconciled")}
</Badge>
)
}
return (
<Badge theme="green">
<CheckCircle2 />
{_("Reconciled")}
</Badge>
)
},
},
{
id: "actions",
header: _("Actions"),
size: 200,
enableResizing: false,
meta: { truncate: false, truncateTooltip: false } satisfies ListViewColumnMeta,
cell: ({ row }) => (
<div className="flex gap-2 ps-0.5 items-center">
<Button variant="ghost" asChild size='sm'>
<a
href={`/desk/bank-transaction/${row.original.name}`}
target="_blank"
rel="noreferrer"
// className="text-ink-gray-8 underline underline-offset-4 inline-flex gap-2"
>
{_("View")} <ExternalLink className="w-4 h-4" />
</a>
</Button>
{row.original.allocated_amount && row.original.allocated_amount > 0 ? (
<Button
variant="ghost"
onClick={() => onUndo(row.original)}
size="sm"
theme='red'
>
<Undo2 />
{_("Undo")}
</Button>
) : null}
</div>
),
},
],
[_, accountCurrency, onUndo],
)
const [search, setSearch] = useDebounceValue('', 250)
const [amountFilter, setAmountFilter] = useState<{ value: number, stringValue?: string | number }>({ value: 0, stringValue: '0.00' })
const [typeFilter, setTypeFilter] = useState('All')
const [status, setStatus] = useState<'Reconciled' | 'Unreconciled' | 'All' | 'Partially Reconciled'>('All')
const onSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setSearch(e.target.value)
}
const filteredResults = useMemo(() => {
if (!data) {
return []
}
return data.message.filter((transaction) => {
if (search && !transaction.description?.toLowerCase().includes(search.toLowerCase())) {
return false
}
if (typeFilter !== 'All') {
if (typeFilter === 'Debits' && transaction.deposit && transaction.deposit > 0) {
return false
}
if (typeFilter === 'Credits' && transaction.withdrawal && transaction.withdrawal > 0) {
return false
}
}
if (status !== 'All') {
if (status === 'Reconciled' && transaction.status !== 'Reconciled') {
return false
}
if (status === 'Unreconciled') {
if (transaction.status === 'Reconciled') {
return false
}
// Filter out partially reconciled transactions
if (transaction.allocated_amount && transaction.allocated_amount > 0 && transaction.unallocated_amount !== 0) {
return false
}
}
if (status === 'Partially Reconciled') {
if (transaction.status === 'Reconciled') {
return false
}
if ((transaction.allocated_amount ?? 0) === 0) {
return false
}
}
}
if (amountFilter.value > 0 && transaction.withdrawal !== amountFilter.value && transaction.deposit !== amountFilter.value) {
return false
}
return true
})
}, [data, search, amountFilter, typeFilter, status])
return <div className="space-y-2 py-2">
<div className="flex gap-2 justify-between items-center">
<Paragraph className="text-sm">
<span dangerouslySetInnerHTML={{
__html: _("Below is a list of all bank transactions imported in the system for the bank account {0} between {1} and {2}.", [`<strong>${bankAccount?.account_name}</strong>`, `<strong>${formattedFromDate}</strong>`, `<strong>${formattedToDate}</strong>`])
}} />
</Paragraph>
<Button size='md' variant='subtle' asChild>
<Link to="/statement-importer">
<ImportIcon />
{_("Import Bank Statement")}
</Link>
</Button>
</div>
{error && <ErrorBanner error={error} />}
<Filters
onSearchChange={onSearchChange}
search={search}
results={filteredResults}
setAmountFilter={setAmountFilter}
amountFilter={amountFilter}
onTypeFilterChange={setTypeFilter}
typeFilter={typeFilter}
status={status}
setStatus={setStatus}
/>
<ListView
data={filteredResults}
columns={transactionColumns}
getRowId={(row) => row.name}
maxHeight="calc(100vh - 200px)"
scrollAreaClassName="min-h-[calc(100vh-200px)]"
emptyState={<Empty>
<EmptyMedia>
<ListIcon />
</EmptyMedia>
<EmptyHeader>
<EmptyTitle>{_("No bank transactions found")}</EmptyTitle>
<EmptyDescription>{_("There are no transactions in the system for the selected bank account and dates that match the filters.")}</EmptyDescription>
</EmptyHeader>
{data && data.message.length === 0 ? <EmptyContent>
<Button type='button' asChild variant='outline'>
<Link to="/statement-importer">
{_("Import Bank Statement")}
</Link>
</Button>
</EmptyContent> : null}
</Empty>}
/>
</div>
}
interface FilterProps {
onSearchChange: (e: React.ChangeEvent<HTMLInputElement>) => void
search: string
results: BankTransaction[]
setAmountFilter: (value: { value: number, stringValue?: string | number }) => void
amountFilter: { value: number, stringValue?: string | number }
onTypeFilterChange: (type: string) => void
typeFilter: string
status: 'Reconciled' | 'Unreconciled' | 'All' | 'Partially Reconciled'
setStatus: (status: 'Reconciled' | 'Unreconciled' | 'All' | 'Partially Reconciled') => void
}
const Filters = ({
onSearchChange,
search,
results,
setAmountFilter,
amountFilter,
onTypeFilterChange,
typeFilter,
status,
setStatus,
}: FilterProps) => {
const bankAccount = useAtomValue(selectedBankAccountAtom)
const currency = bankAccount?.account_currency ?? getCompanyCurrency(bankAccount?.company ?? '')
const currencySymbol = getCurrencySymbol(currency)
const formatInfo = getCurrencyFormatInfo(currency)
const groupSeparator = formatInfo.group_sep || ","
const decimalSeparator = formatInfo.decimal_str || "."
return <div className="flex py-2 w-full gap-2">
<InputGroup variant='outline'>
<label className="sr-only">{_("Search transactions")}</label>
<InputGroupAddon>
<Search className="w-4 h-4 text-ink-gray-5" />
</InputGroupAddon>
<Input
placeholder={_("Search")} type='search' onChange={onSearchChange} variant='outline' defaultValue={search}
className="border-none px-0 shadow-none focus-visible:ring-0 focus-visible:ring-offset-0" />
<InputGroupAddon align='inline-end'>
<span className="text-sm text-ink-gray-5 text-nowrap whitespace-nowrap">{results?.length} {_(results?.length === 1 ? "result" : "results")}</span>
</InputGroupAddon>
</InputGroup>
<div className="w-[25%]">
<label className="sr-only">{_("Filter by amount")}</label>
<CurrencyInput
groupSeparator={groupSeparator}
decimalSeparator={decimalSeparator}
placeholder={`${currencySymbol}0${decimalSeparator}00`}
decimalsLimit={2}
value={amountFilter.stringValue}
maxLength={12}
decimalScale={2}
prefix={currencySymbol}
onValueChange={(v, _n, values) => {
// If the input ends with a decimal or a decimal with trailing zeroes, store the string since we need the user to be able to type the decimals.
// When the user eventually types the decimals or blurs out, the value is formatted anyway.
// Otherwise store the float value
// Check if the value ends with a decimal or a decimal with trailing zeroes
const isDecimal = v?.endsWith(decimalSeparator) || v?.endsWith(decimalSeparator + '0')
const newValue = isDecimal ? v : values?.float ?? ''
setAmountFilter({
value: Number(newValue),
stringValue: newValue
})
}}
// @ts-expect-error - CurrencyInputProps doesn't have a variant prop but Input does
variant={"outline"}
customInput={Input}
/>
</div>
<div className="w-[25%]">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size='md' className="min-w-32 w-full text-start justify-between">
<div className="flex gap-2 items-center">
{typeFilter === 'All' ? <DollarSign className="w-4 h-4 text-ink-gray-5" /> : typeFilter === 'Debits' ? <ArrowUpRight className="w-4 h-4 text-ink-red-3" /> : <ArrowDownRight className="w-4 h-4 text-ink-green-3" />}
{_(typeFilter)}
</div>
<ChevronDown className="w-4 h-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem onClick={() => onTypeFilterChange('All')}><DollarSign /> {_("All")}</DropdownMenuItem>
<DropdownMenuItem onClick={() => onTypeFilterChange('Debits')}><ArrowUpRight className="text-ink-red-3" /> {_("Debits")}</DropdownMenuItem>
<DropdownMenuItem onClick={() => onTypeFilterChange('Credits')}><ArrowDownRight className="text-ink-green-3" /> {_("Credits")}</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
<div className="w-[25%]">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size='md' className="min-w-32 w-full text-start justify-between">
<div className="flex gap-2 items-center">
{status === 'All' ? <ListIcon className="w-4 h-4 text-ink-gray-5" /> :
status === 'Reconciled' ? <CheckCircle2 className="w-4 h-4 text-ink-green-3" /> :
status === 'Unreconciled' ? <XCircle className="w-4 h-4 text-ink-red-3" /> :
<CheckCircle2 className="w-4 h-4 text-yellow-500" />}
{_(status)}
</div>
<ChevronDown className="w-4 h-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem onClick={() => setStatus('All')}>{<ListIcon className="w-4 h-4 text-ink-gray-5" />} {_("All")}</DropdownMenuItem>
<DropdownMenuItem onClick={() => setStatus('Reconciled')}>{<CheckCircle2 className="w-4 h-4 text-ink-green-3" />} {_("Reconciled")}</DropdownMenuItem>
<DropdownMenuItem onClick={() => setStatus('Unreconciled')}>{<XCircle className="w-4 h-4 text-ink-red-3" />} {_("Unreconciled")}</DropdownMenuItem>
<DropdownMenuItem onClick={() => setStatus('Partially Reconciled')}>{<CheckCircle2 className="w-4 h-4 text-yellow-500" />} {_("Partially Reconciled")}</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
}
export default BankTransactions

View File

@@ -1,125 +0,0 @@
import { AlertDialog, AlertDialogOverlay, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogFooter, AlertDialogCancel, AlertDialogAction } from "@/components/ui/alert-dialog"
import { useAtom, useAtomValue } from "jotai"
import { bankRecDateAtom, bankRecUnreconcileModalAtom, selectedBankAccountAtom } from "./bankRecAtoms"
import { useMemo } from "react"
import { useFrappeGetDoc, useFrappePostCall, useSWRConfig } from "frappe-react-sdk"
import { BankTransaction } from "@/types/Accounts/BankTransaction"
import { toast } from "sonner"
import ErrorBanner from "@/components/ui/error-banner"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
import { formatCurrency } from "@/lib/numbers"
import { Badge } from "@/components/ui/badge"
import { slug } from "@/lib/frappe"
import SelectedTransactionDetails from "./SelectedTransactionDetails"
import _ from "@/lib/translate"
const BankTransactionUnreconcileModal = () => {
const [unreconcileModal, setBankRecUnreconcileModal] = useAtom(bankRecUnreconcileModalAtom)
const onOpenChange = (v: boolean) => {
if (!v) {
setBankRecUnreconcileModal('')
}
}
return <AlertDialog open={!!unreconcileModal} onOpenChange={onOpenChange}>
<AlertDialogOverlay />
<AlertDialogContent className="min-w-2xl">
<AlertDialogHeader>
<AlertDialogTitle>{_("Undo Transaction Reconciliation")}</AlertDialogTitle>
<AlertDialogDescription>
{_("Are you sure you want to unreconcile this transaction?")}
</AlertDialogDescription>
</AlertDialogHeader>
<BankTransactionUnreconcileModalContent />
</AlertDialogContent>
</AlertDialog>
}
const BankTransactionUnreconcileModalContent = () => {
const bankAccount = useAtomValue(selectedBankAccountAtom)
const dates = useAtomValue(bankRecDateAtom)
const { mutate } = useSWRConfig()
const [unreconcileModal, setBankRecUnreconcileModal] = useAtom(bankRecUnreconcileModalAtom)
const { data: transaction, error } = useFrappeGetDoc<BankTransaction>('Bank Transaction', unreconcileModal)
const { call, loading, error: unreconcileError } = useFrappePostCall('erpnext.accounts.doctype.bank_transaction.bank_transaction.unreconcile_transaction')
const onUnreconcile = (event: React.MouseEvent<HTMLButtonElement>) => {
call({
transaction_name: unreconcileModal
}).then(() => {
// Mutate the transactions list, unreconciled transactions list and account closing balance
mutate(`bank-reconciliation-bank-transactions-${bankAccount?.name}-${dates.fromDate}-${dates.toDate}`)
mutate(`bank-reconciliation-unreconciled-transactions-${bankAccount?.name}-${dates.fromDate}-${dates.toDate}`)
mutate(`bank-reconciliation-account-closing-balance-${bankAccount?.name}-${dates.toDate}`)
toast.success(_("Transaction Unreconciled"))
setBankRecUnreconcileModal('')
})
event.preventDefault()
}
const vouchersWhichWillBeCancelled = useMemo(() => {
return transaction?.payment_entries?.filter((payment) => payment.reconciliation_type === 'Voucher Created')
}, [transaction])
return <div>
<div className="flex flex-col gap-3">
{error && <ErrorBanner error={error} />}
{unreconcileError && <ErrorBanner error={unreconcileError} />}
{transaction && <SelectedTransactionDetails transaction={transaction} />}
<span className="font-medium text-sm">{_("This transaction has been reconciled with the following document(s):")}</span>
<Table>
<TableHeader>
<TableRow>
<TableHead>{_("Document")}</TableHead>
<TableHead>{_("Amount")}</TableHead>
<TableHead>{_("Reconciliation Type")}</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{transaction?.payment_entries?.map((voucher) => {
return <TableRow key={voucher.name}>
<TableCell>
<a className="underline underline-offset-4"
target="_blank"
rel="noopener noreferrer"
href={`/desk/${slug(voucher.payment_document as string)}/${voucher.payment_entry}`}
>
{`${_(voucher.payment_document)}: ${voucher.payment_entry}`}
</a>
</TableCell>
<TableCell>{formatCurrency(voucher.allocated_amount)}</TableCell>
<TableCell>{voucher.reconciliation_type === 'Voucher Created' ?
<Badge theme="green">{_(voucher.reconciliation_type)}</Badge> :
<Badge theme="blue">{_(voucher.reconciliation_type ?? "Matched")}</Badge>}</TableCell>
</TableRow>
})}
</TableBody>
</Table>
<div className="py-4">
{vouchersWhichWillBeCancelled && vouchersWhichWillBeCancelled?.length > 0 && <span>The following documents will be <strong>cancelled</strong>:</span>}
{vouchersWhichWillBeCancelled && vouchersWhichWillBeCancelled?.length > 0 && <ol className="ms-6 list-disc [&>li]:mt-2">
{vouchersWhichWillBeCancelled?.map((voucher) => {
return <li key={voucher.name}>{_(voucher.payment_document)}: {voucher.payment_entry}</li>
})}
</ol>}
</div>
</div>
<AlertDialogFooter>
<AlertDialogCancel disabled={loading}>{_("Cancel")}</AlertDialogCancel>
<AlertDialogAction onClick={onUnreconcile} theme="red" disabled={loading}>
{_("Unreconcile")}
</AlertDialogAction>
</AlertDialogFooter>
</div>
}
export default BankTransactionUnreconcileModal

View File

@@ -1,92 +0,0 @@
import { Button } from "@/components/ui/button"
import { selectedCompanyAtom, useCurrentCompany } from "@/hooks/useCurrentCompany"
import { useSetAtom } from "jotai"
import { Building2, Check, ChevronDown } from "lucide-react"
import { useState } from "react"
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from "@/components/ui/command"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover"
import { cn } from "@/lib/utils"
import _ from "@/lib/translate"
import { selectedBankAccountAtom } from "./bankRecAtoms"
const CompanySelector = ({ onChange }: { onChange?: (company: string) => void }) => {
const [open, setOpen] = useState(false)
const [searchQuery, setSearchQuery] = useState("")
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const options = window.frappe?.boot?.docs?.filter((doc: Record<string, any>) => doc.doctype === ":Company").map((company: Record<string, any>) => company.name) || []
const setSelectedCompany = useSetAtom(selectedCompanyAtom)
const setSelectedBankAccount = useSetAtom(selectedBankAccountAtom)
const selectedCompany = useCurrentCompany()
const handleSelectCompany = (company: string) => {
setSelectedCompany(company)
setSearchQuery("")
setOpen(false)
// Only reset bank account if the company is changed
if (selectedCompany !== company) {
setSelectedBankAccount(null)
onChange?.(company)
}
}
return (<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
type='button'
role="combobox"
size='md'
aria-expanded={open}
className="justify-between"
>
<div className="flex items-center gap-2">
<Building2 />
{selectedCompany}
</div>
<ChevronDown className="text-ink-gray-4" />
</Button>
</PopoverTrigger>
<PopoverContent className="min-w-56 w-fit p-0">
<Command value={selectedCompany}>
{options.length > 5 && <CommandInput placeholder={_("Search company...")} className="h-9" />}
<CommandList>
<CommandEmpty>{_("No company found.")}</CommandEmpty>
<CommandGroup>
{options.map((option: string) => (
<CommandItem
key={option}
value={option}
onSelect={(currentValue) => {
handleSelectCompany(currentValue)
}}
>
{option}
<Check
className={cn(
"ms-auto",
searchQuery === option ? "opacity-100" : "opacity-0"
)}
/>
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>)
}
export default CompanySelector

View File

@@ -1,229 +0,0 @@
import { useAtomValue } from "jotai"
import { MissingFiltersBanner } from "./MissingFiltersBanner"
import { bankRecDateAtom, selectedBankAccountAtom } from "./bankRecAtoms"
import { useCurrentCompany } from "@/hooks/useCurrentCompany"
import { Paragraph } from "@/components/ui/typography"
import type { ColumnDef } from "@tanstack/react-table"
import { useCallback, useMemo } from "react"
import { useFrappeGetCall, useFrappePostCall } from "frappe-react-sdk"
import { QueryReportReturnType } from "@/types/custom/Reports"
import { formatDate } from "@/lib/date"
import { ListView, type ListViewColumnMeta } from "@/components/ui/list-view"
import { formatCurrency } from "@/lib/numbers"
import { getCompanyCurrency } from "@/lib/company"
import { getErrorMessage, slug } from "@/lib/frappe"
import { Button } from "@/components/ui/button"
import { toast } from "sonner"
import { PartyPopper } from "lucide-react"
import ErrorBanner from "@/components/ui/error-banner"
import _ from "@/lib/translate"
import { Empty, EmptyTitle, EmptyDescription, EmptyMedia, EmptyHeader } from "@/components/ui/empty"
const IncorrectlyClearedEntries = () => {
const companyID = useCurrentCompany()
const bankAccount = useAtomValue(selectedBankAccountAtom)
const dates = useAtomValue(bankRecDateAtom)
if (!companyID || !bankAccount || !dates) {
const missingFields = []
if (!companyID) {
missingFields.push('Company')
}
if (!bankAccount) {
missingFields.push('Bank Account')
}
if (!dates) {
missingFields.push('Dates')
}
return <MissingFiltersBanner text={`Please select ${missingFields.join(', ')} to view the incorrectly cleared entries.`} />
}
return <IncorrectlyClearedEntriesView />
}
interface IncorrectlyClearedEntry {
payment_document: string
payment_entry: string
debit: number
credit: number
posting_date: string,
clearance_date: string,
}
const IncorrectlyClearedEntriesView = () => {
const companyID = useCurrentCompany()
const bankAccount = useAtomValue(selectedBankAccountAtom)
const dates = useAtomValue(bankRecDateAtom)
const filters = useMemo(() => {
return JSON.stringify({
company: companyID,
account: bankAccount?.account,
report_date: dates.toDate
})
}, [companyID, bankAccount, dates])
const { data, error, mutate } = useFrappeGetCall<{ message: QueryReportReturnType<IncorrectlyClearedEntry> }>('frappe.desk.query_report.run', {
report_name: 'Cheques and Deposits Incorrectly cleared',
filters,
ignore_prepared_report: 1,
are_default_filters: false,
}, `Report-Cheques and Deposits Incorrectly cleared-${filters}`, { keepPreviousData: true, revalidateOnFocus: false }, 'POST')
const formattedToDate = formatDate(dates.toDate)
const { call: clearClearingDate } = useFrappePostCall('erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.clear_clearing_date')
const onClearClick = useCallback(
(voucher_type: string, voucher_name: string) => {
clearClearingDate({ voucher_type, voucher_name })
.then(() => {
toast.success(_("Cleared"), {
duration: 1000,
})
mutate()
})
.catch((e) => {
toast.error(_("There was an error while performing the action."), {
description: getErrorMessage(e),
duration: 5000,
})
})
},
[clearClearingDate, mutate, _],
)
const accountCurrency = useMemo(
() => bankAccount?.account_currency ?? getCompanyCurrency(companyID),
[bankAccount?.account_currency, companyID],
)
const incorrectlyClearedColumns = useMemo<ColumnDef<IncorrectlyClearedEntry, unknown>[]>(
() => [
{
accessorKey: "payment_document",
header: _("Document Type"),
size: 128,
cell: ({ row }) => _(row.original.payment_document),
},
{
id: "payment_entry",
header: _("Payment Document"),
size: 160,
meta: {
getTooltipText: (r) => {
const x = r as IncorrectlyClearedEntry
return [x.payment_document, x.payment_entry].filter(Boolean).join(" · ") || undefined
},
} satisfies ListViewColumnMeta,
cell: ({ row }) => (
<a
target="_blank"
rel="noreferrer"
className="text-ink-gray-8 block min-w-0 w-full underline underline-offset-4"
href={`/desk/${slug(row.original.payment_document)}/${row.original.payment_entry}`}
>
{row.original.payment_entry}
</a>
),
},
{
accessorKey: "debit",
header: _("Debit"),
size: 120,
meta: { align: "right" } satisfies ListViewColumnMeta,
cell: ({ row }) => formatCurrency(row.original.debit, accountCurrency),
},
{
accessorKey: "credit",
header: _("Credit"),
size: 120,
meta: { align: "right" } satisfies ListViewColumnMeta,
cell: ({ row }) => formatCurrency(row.original.credit, accountCurrency),
},
{
accessorKey: "posting_date",
header: _("Posting Date"),
size: 118,
meta: { tabularNums: true } satisfies ListViewColumnMeta,
cell: ({ row }) => formatDate(row.original.posting_date),
},
{
accessorKey: "clearance_date",
header: _("Clearance Date"),
size: 118,
meta: { tabularNums: true } satisfies ListViewColumnMeta,
cell: ({ row }) => formatDate(row.original.clearance_date),
},
{
id: "actions",
header: _("Actions"),
size: 180,
enableResizing: false,
meta: { truncate: false, truncateTooltip: false } satisfies ListViewColumnMeta,
cell: ({ row }) => (
<Button
variant="link"
size="sm"
className="text-ink-red-3 px-0"
onClick={() => onClearClick(row.original.payment_document, row.original.payment_entry)}
>
{_("Reset Clearing Date")}
</Button>
),
},
],
[_, accountCurrency, onClearClick],
)
return <div className="space-y-4 py-2">
<div>
<Paragraph className="text-sm">
<span dangerouslySetInnerHTML={{
__html: _("This report shows all entries in the system where the <strong>clearance date is before the posting date</strong> which is incorrect.")
}} />
<br />
{data && data.message.result.length > 0 && <span>
<span dangerouslySetInnerHTML={{
__html: _("Entries below have a posting date after {0} but the clearance date is before {1}.", [`<strong>${formattedToDate}</strong>`, `<strong>${formattedToDate}</strong>`])
}} />
<br />
{_("You can reset the clearing dates of these entries here.")}
</span>}
</Paragraph>
</div>
{error && <ErrorBanner error={error} />}
{data && data.message.result.length > 0 && (
<div className="space-y-2">
<p className="text-ink-gray-5 text-sm">{_("Incorrectly cleared entries as per the report.")}</p>
<ListView
data={data.message.result}
columns={incorrectlyClearedColumns}
getRowId={(row) => `${row.payment_entry}-${row.posting_date}`}
maxHeight="min(70vh, 640px)"
emptyState={_("No rows to display.")}
/>
</div>
)}
{data && data.message.result.length === 0 &&
<Empty>
<EmptyMedia>
<PartyPopper />
</EmptyMedia>
<EmptyHeader>
<EmptyTitle>{_("It's all good!")}</EmptyTitle>
<EmptyDescription>{_("There are no entries in the system where the clearance date is before the posting date.")}</EmptyDescription>
</EmptyHeader>
</Empty>
}
</div>
}
export default IncorrectlyClearedEntries

View File

@@ -1,949 +0,0 @@
import { useAtom, useAtomValue, useSetAtom } from "jotai"
import { bankRecAmountFilter, bankRecDateAtom, bankRecRecordJournalEntryModalAtom, bankRecRecordPaymentModalAtom, bankRecSelectedTransactionAtom, bankRecTransactionTypeFilter, bankRecTransferModalAtom, selectedBankAccountAtom } from "./bankRecAtoms"
import { H4 } from "@/components/ui/typography"
import { useMemo, useRef } from "react"
import { getCompanyCurrency } from "@/lib/company"
import ErrorBanner from "@/components/ui/error-banner"
import { Separator } from "@/components/ui/separator"
import Fuse from 'fuse.js'
import { getSearchResults, LinkedPayment, UnreconciledTransaction, useGetRuleForTransaction, useGetUnreconciledTransactions, useGetVouchersForTransaction, useIsTransactionWithdrawal, useReconcileTransaction, useTransactionSearch } from "./utils"
import { Input } from "@/components/ui/input"
import { AlertCircleIcon, ArrowDownRight, ArrowRightIcon, ArrowRightLeft, ArrowUpRight, BadgeCheck, ChevronDown, DollarSign, Landmark, LandmarkIcon, ListIcon, Loader2, Receipt, ReceiptIcon, Search, User, XCircle, ZapIcon } from "lucide-react"
import { cn } from "@/lib/utils"
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem } from "@/components/ui/dropdown-menu"
import { Button } from "@/components/ui/button"
import CurrencyInput from 'react-currency-input-field'
import { getCurrencySymbol } from "@/lib/currency"
import { Virtuoso } from 'react-virtuoso'
import { formatDate } from "@/lib/date"
import { Badge } from "@/components/ui/badge"
import { formatCurrency, getCurrencyFormatInfo } from "@/lib/numbers"
import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from "@/components/ui/tooltip"
import { Skeleton } from "@/components/ui/skeleton"
import { slug } from "@/lib/frappe"
import _ from "@/lib/translate"
import TransferModal from "./TransferModal"
import BankEntryModal from "./BankEntryModal"
import RecordPaymentModal from "./RecordPaymentModal"
import { Card, CardAction, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import SelectedTransactionsTable from "./SelectedTransactionsTable"
import MatchFilters from "./MatchFilters"
import { useHotkeys } from "react-hotkeys-hook"
import { KeyboardMetaKeyIcon } from "@/components/ui/keyboard-keys"
import { Kbd, KbdGroup } from "@/components/ui/kbd"
import { useFrappeGetCall } from "frappe-react-sdk"
import { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from "@/components/ui/empty"
import { Link } from "react-router"
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
import { InputGroup, InputGroupAddon, InputGroupText } from "@/components/ui/input-group"
const MatchAndReconcile = ({ contentHeight }: { contentHeight: number }) => {
const selectedBank = useAtomValue(selectedBankAccountAtom)
if (!selectedBank) {
return <Empty>
<EmptyMedia>
<LandmarkIcon />
</EmptyMedia>
<EmptyHeader>
<EmptyTitle>{_("Select a bank account to reconcile")}</EmptyTitle>
</EmptyHeader>
</Empty>
}
return <>
<div className={`flex items-start space-x-2`} >
<div className="flex-1">
<H4 className="text-sm font-medium">{_("Unreconciled Transactions")}</H4>
<UnreconciledTransactions contentHeight={contentHeight} />
</div>
<Separator orientation="vertical" style={{ minHeight: `${contentHeight}px` }} />
<div className="flex-1 px-1">
<H4 className="text-sm font-medium">{_("Match or Create")}</H4>
<VouchersSection contentHeight={contentHeight} />
</div>
</div>
<TransferModal />
<BankEntryModal />
<RecordPaymentModal />
</>
}
const UnreconciledTransactions = ({ contentHeight }: { contentHeight: number }) => {
const bankAccount = useAtomValue(selectedBankAccountAtom)
const currency = bankAccount?.account_currency ?? getCompanyCurrency(bankAccount?.company ?? '')
const currencySymbol = getCurrencySymbol(currency)
const formatInfo = getCurrencyFormatInfo(currency)
const groupSeparator = formatInfo.group_sep || ","
const decimalSeparator = formatInfo.decimal_str || "."
const inputRef = useRef<HTMLInputElement>(null)
const { data: unreconciledTransactions, isLoading, error } = useGetUnreconciledTransactions()
const [typeFilter, setTypeFilter] = useAtom(bankRecTransactionTypeFilter)
const [amountFilter, setAmountFilter] = useAtom(bankRecAmountFilter)
const [search, setSearch] = useTransactionSearch()
const searchIndex = useMemo(() => {
if (!unreconciledTransactions) {
return null
}
return new Fuse(unreconciledTransactions.message, {
keys: ['description', 'reference_number'],
threshold: 0.5,
includeScore: true
})
}, [unreconciledTransactions])
const results = useMemo(() => {
return getSearchResults(searchIndex, search, typeFilter, amountFilter.value, unreconciledTransactions?.message)
}, [searchIndex, search, typeFilter, amountFilter.value, unreconciledTransactions?.message])
const setSelectedTransaction = useSetAtom(bankRecSelectedTransactionAtom(bankAccount?.name || ''))
const onFilterChange = () => {
setSelectedTransaction([])
}
const onSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setSearch(e.target.value)
onFilterChange()
}
const onTypeFilterChange = (type: string) => {
setTypeFilter(type)
onFilterChange()
}
const onClearFilters = () => {
setSearch('')
if (inputRef.current) {
inputRef.current.value = ''
}
setTypeFilter('All')
setAmountFilter({ value: 0, stringValue: '' })
onFilterChange()
}
const hasFilters = search !== '' || typeFilter !== 'All' || amountFilter.value !== 0
if (isLoading) {
return <UnreconciledTransactionsLoadingState />
}
return <div className="space-y-1">
<div className="flex py-2 w-full gap-2">
<InputGroup variant='outline'>
<label className="sr-only">{_("Search transactions")}</label>
<InputGroupAddon>
<Search className="w-4 h-4 text-ink-gray-5" />
</InputGroupAddon>
<Input
placeholder={_("Search")}
// type='search'
variant='outline'
onChange={onSearchChange}
defaultValue={search}
ref={inputRef}
/>
<InputGroupAddon align='inline-end'>
<InputGroupText>{results?.length} {_(results?.length === 1 ? "result" : "results")}</InputGroupText>
</InputGroupAddon>
</InputGroup>
<div>
<label className="sr-only">{_("Filter by amount")}</label>
<CurrencyInput
groupSeparator={groupSeparator}
decimalSeparator={decimalSeparator}
placeholder={`${currencySymbol}0${decimalSeparator}00`}
decimalsLimit={2}
value={amountFilter.stringValue}
maxLength={12}
decimalScale={2}
prefix={currencySymbol}
onValueChange={(v, _n, values) => {
// If the input ends with a decimal or a decimal with trailing zeroes, store the string since we need the user to be able to type the decimals.
// When the user eventually types the decimals or blurs out, the value is formatted anyway.
// Otherwise store the float value
// Check if the value ends with a decimal or a decimal with trailing zeroes
const isDecimal = v?.endsWith(decimalSeparator) || v?.endsWith(decimalSeparator + '0')
const newValue = isDecimal ? v : values?.float ?? ''
const nextAmountFilter = {
value: Number(newValue),
stringValue: newValue
}
const hasAmountFilterChanged = amountFilter.value !== nextAmountFilter.value || amountFilter.stringValue !== nextAmountFilter.stringValue
setAmountFilter(nextAmountFilter)
// `onValueChange` also fires on blur; avoid clearing selected transaction unless filter value actually changed.
if (hasAmountFilterChanged) {
onFilterChange()
}
}}
// @ts-expect-error - CurrencyInputProps doesn't have a variant prop but Input does
variant={"outline"}
customInput={Input}
/>
</div>
<div>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size='md' className="min-w-32 text-start">
{typeFilter === 'All' ? <DollarSign className="text-ink-gray-5" /> : typeFilter === 'Debits' ? <ArrowUpRight className="text-ink-red-3" /> : <ArrowDownRight className="text-ink-green-3" />}
{_(typeFilter)}
<ChevronDown className="text-ink-gray-5" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem onClick={() => onTypeFilterChange('All')}><DollarSign /> {_("All")}</DropdownMenuItem>
<DropdownMenuItem onClick={() => onTypeFilterChange('Debits')}><ArrowUpRight className="text-ink-red-3" /> {_("Debits")}</DropdownMenuItem>
<DropdownMenuItem onClick={() => onTypeFilterChange('Credits')}><ArrowDownRight className="text-ink-green-3" /> {_("Credits")}</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
{error && <ErrorBanner error={error} />}
<OlderUnreconciledTransactionsBanner />
{results.length === 0 && <NoTransactionsFoundBanner
onClearFilters={hasFilters ? onClearFilters : undefined}
text={hasFilters ? _("No transactions found for the given filters.") : _("No unreconciled transactions found")}
description={hasFilters ? _("Try adjusting your search or filter criteria.") : _("Import your bank statement to get started.")} />}
<Virtuoso
data={results}
itemContent={(_index, transaction) => (
<UnreconciledTransactionItem transaction={transaction} />
)}
style={{ minHeight: Math.max(contentHeight - 80, 400) }}
totalCount={results?.length}
/>
</div>
}
const NoTransactionsFoundBanner = ({ text, description, onClearFilters }: { text: string, description?: string, onClearFilters?: () => void }) => {
return <Empty>
<EmptyMedia>
<ListIcon />
</EmptyMedia>
<EmptyHeader>
<EmptyTitle>{text}</EmptyTitle>
{description && <EmptyDescription>{description}</EmptyDescription>}
</EmptyHeader>
<EmptyContent>
{onClearFilters ? <Button type='button' size='sm' variant='subtle' onClick={onClearFilters}>Clear Filters</Button> :
<Button type='button' asChild size='sm' variant='subtle'>
<Link to="/statement-importer">
{_("Import Bank Statement")}
</Link>
</Button>}
</EmptyContent>
</Empty>
}
const UnreconciledTransactionsLoadingState = () => {
return <div className="flex flex-col gap-2 py-2">
<div className="flex items-center gap-2 pb-2">
<Skeleton className="h-9.5 w-full" />
<Skeleton className="h-9.5 min-w-36" />
<Skeleton className="h-9.5 min-w-32" />
</div>
{Array.from({ length: 6 }).map((_, index) => (
<Skeleton key={index} className="h-16 w-full" />
))}
</div>
}
const UnreconciledTransactionItem = ({ transaction }: { transaction: UnreconciledTransaction }) => {
const selectedBank = useAtomValue(selectedBankAccountAtom)
const [selectedTransaction, setSelectedTransaction] = useAtom(bankRecSelectedTransactionAtom(selectedBank?.name || ''))
const { amount, isWithdrawal } = useIsTransactionWithdrawal(transaction)
const isSelected = selectedTransaction?.some((t) => t.name === transaction.name)
const currency = transaction.currency ?? selectedBank?.account_currency ?? getCompanyCurrency(selectedBank?.company ?? '')
const handleSelectTransaction = (event: React.MouseEvent<HTMLDivElement>) => {
// If the user is pressing the shift key, add/remove the transaction from the selected transactions
if (event.shiftKey) {
setSelectedTransaction(isSelected ? selectedTransaction.filter((t) => t.name !== transaction.name) : [...selectedTransaction, transaction])
} else {
setSelectedTransaction([transaction])
}
}
return <div className="py-1">
<div className={cn("border outline rounded-md p-2 mx-0.5 cursor-pointer transition-[color,box-shadow, bg] hover:bg-surface-gray-1",
isSelected ? "bg-surface-gray-1 border-outline-gray-5 outline-outline-gray-5" : "border-outline-gray-2 outline-none"
)}
role='button'
tabIndex={0}
onClick={handleSelectTransaction}>
<div className="flex justify-between items-start w-full">
<div className="space-y-1 overflow-hidden whitespace-pre-wrap">
<div className="flex items-center gap-1">
<span className="font-medium text-sm">{formatDate(transaction.date)}</span>
{transaction.transaction_type &&
<Badge theme="blue">{transaction.transaction_type}</Badge>}
{transaction.reference_number && <Badge
title={transaction.reference_number}
className="max-w-[300px] text-ellipsis"
>
{_("Ref")}: {transaction.reference_number}</Badge>}
{transaction.matched_transaction_rule && <Badge
theme="violet"
title={_("Matched by rule")}>
<ZapIcon className="w-4 h-4" /> {transaction.matched_transaction_rule}</Badge>}
</div>
<span className="text-sm wrap-anywhere" title={transaction.description}>{transaction.description}</span>
</div>
<div className="gap-1 flex flex-col items-end min-w-36 h-full text-end">
{isWithdrawal ? <ArrowUpRight className="size-5 text-ink-red-3" /> : <ArrowDownRight className="size-5 text-ink-green-3" />}
{amount && amount > 0 && <span className="font-semibold font-numeric text-base">{formatCurrency(amount, currency)}</span>}
{amount !== transaction.unallocated_amount && <span className="text-xs leading-normal text-ink-gray-5">{formatCurrency(transaction.unallocated_amount, currency)} {_("Unallocated")}</span>}
</div>
</div>
</div>
</div>
}
const VouchersSection = ({ contentHeight }: { contentHeight: number }) => {
const selectedBank = useAtomValue(selectedBankAccountAtom)
const selectedTransactions = useAtomValue(bankRecSelectedTransactionAtom(selectedBank?.name || ''))
if (selectedTransactions.length === 0) {
return <Empty>
<EmptyMedia>
<ReceiptIcon />
</EmptyMedia>
<EmptyHeader>
<EmptyTitle>{_("Select a transaction to match and reconcile with vouchers")}</EmptyTitle>
</EmptyHeader>
</Empty>
}
if (selectedTransactions.length > 1) {
return <OptionsForMultipleTransactions transactions={selectedTransactions} />
}
return <div style={{ minHeight: contentHeight }} className="mt-2">
<OptionsForSingleTransaction transaction={selectedTransactions[0]} contentHeight={contentHeight} />
</div>
}
const useKeyboardShortcuts = () => {
const setTransferModalOpen = useSetAtom(bankRecTransferModalAtom)
const setRecordPaymentModalOpen = useSetAtom(bankRecRecordPaymentModalAtom)
const setRecordJournalEntryModalOpen = useSetAtom(bankRecRecordJournalEntryModalAtom)
useHotkeys('meta+p', () => {
//
setRecordPaymentModalOpen(true)
}, {
enabled: true,
enableOnFormTags: false,
preventDefault: true
})
useHotkeys('meta+b', () => {
//
setRecordJournalEntryModalOpen(true)
}, {
enabled: true,
enableOnFormTags: false,
preventDefault: true
})
useHotkeys('meta+i', () => {
//
setTransferModalOpen(true)
}, {
enabled: true,
enableOnFormTags: false,
preventDefault: true
})
return {
setTransferModalOpen,
setRecordPaymentModalOpen,
setRecordJournalEntryModalOpen
}
}
const OptionsForMultipleTransactions = ({ transactions }: { transactions: UnreconciledTransaction[] }) => {
const { setTransferModalOpen, setRecordPaymentModalOpen, setRecordJournalEntryModalOpen } = useKeyboardShortcuts()
return <div className="flex flex-col py-4">
<Card className="gap-2">
<CardHeader>
<CardTitle>
<div className="flex items-center justify-between">
<span className="text-md font-medium">{transactions.length} {_(transactions.length === 1 ? _("transaction selected") : _("transactions selected"))}</span>
<span className="text-md font-medium font-numeric">
{formatCurrency(transactions.reduce((acc, transaction) => acc + (transaction.unallocated_amount ?? 0), 0), transactions[0].currency ?? '')}
</span>
</div>
</CardTitle>
</CardHeader>
<CardContent>
<SelectedTransactionsTable />
<CardAction className="mt-4 justify-self-center">
<div className="flex gap-3 justify-center">
<TooltipProvider>
<div className="flex gap-4 justify-center">
<Tooltip>
<TooltipTrigger asChild>
<Button
size='md'
aria-label={_("Record a bank journal entry for expenses, income or split transactions")}
onClick={() => setRecordJournalEntryModalOpen(true)}>
<Landmark /> {_("Bank Entry")}
</Button>
</TooltipTrigger>
<TooltipContent>
{_("Record a journal entry for expenses, income or split transactions")}
<KbdGroup className="ms-2">
<Kbd><KeyboardMetaKeyIcon /></Kbd>
<Kbd>B</Kbd>
</KbdGroup>
</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant='outline'
size='md'
aria-label={_("Record a payment entry against a customer or supplier")}
onClick={() => setRecordPaymentModalOpen(true)}>
<Receipt /> {_("Record Payment")}
</Button>
</TooltipTrigger>
<TooltipContent>
{_("Record a payment entry against a customer or supplier")}
<KbdGroup className="ms-2">
<Kbd><KeyboardMetaKeyIcon /></Kbd>
<Kbd>P</Kbd>
</KbdGroup>
</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant='outline'
size='md'
aria-label={_("Record an internal transfer to another bank/credit card/cash account")}
onClick={() => setTransferModalOpen(true)}>
<ArrowRightLeft /> {_("Transfer")}
</Button>
</TooltipTrigger>
<TooltipContent>
{_("Record an internal transfer to another bank/credit card/cash account")}
<KbdGroup className="ms-2">
<Kbd><KeyboardMetaKeyIcon /></Kbd>
<Kbd>I</Kbd>
</KbdGroup>
</TooltipContent>
</Tooltip>
</div>
</TooltipProvider>
</div>
</CardAction>
</CardContent>
</Card>
</div>
}
const OptionsForSingleTransaction = ({ transaction, contentHeight }: { transaction: UnreconciledTransaction, contentHeight: number }) => {
const { setTransferModalOpen, setRecordPaymentModalOpen, setRecordJournalEntryModalOpen } = useKeyboardShortcuts()
return <div className="flex flex-col gap-3">
<TooltipProvider>
<div className="flex items-center justify-between pt-2">
<div className="flex gap-4 justify-center">
<Tooltip>
<TooltipTrigger asChild>
<Button
variant='outline'
size='md'
aria-label={_("Record a payment entry against a customer or supplier")}
onClick={() => setRecordPaymentModalOpen(true)}>
<Receipt /> {_("Record Payment")}
</Button>
</TooltipTrigger>
<TooltipContent>
{_("Record a payment entry against a customer or supplier")}
<KbdGroup className="ms-2">
<Kbd><KeyboardMetaKeyIcon /></Kbd>
<Kbd>P</Kbd>
</KbdGroup>
</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant='outline'
size='md'
aria-label={_("Record a bank journal entry for expenses, income or split transactions")}
onClick={() => setRecordJournalEntryModalOpen(true)}>
<Landmark /> {_("Bank Entry")}
</Button>
</TooltipTrigger>
<TooltipContent>
{_("Record a journal entry for expenses, income or split transactions")}
<KbdGroup className="ms-2">
<Kbd><KeyboardMetaKeyIcon /></Kbd>
<Kbd>B</Kbd>
</KbdGroup>
</TooltipContent>
</Tooltip>
<Tooltip >
<TooltipTrigger asChild>
<Button
variant='outline'
size='md'
aria-label={_("Record an internal transfer to another bank/credit card/cash account")}
onClick={() => setTransferModalOpen(true)}>
<ArrowRightLeft /> {_("Transfer")}
</Button>
</TooltipTrigger>
<TooltipContent>
{_("Record an internal transfer to another bank/credit card/cash account")}
<KbdGroup className="ms-2">
<Kbd><KeyboardMetaKeyIcon /></Kbd>
<Kbd>I</Kbd>
</KbdGroup>
</TooltipContent>
</Tooltip>
</div>
<MatchFilters />
</div>
</TooltipProvider>
{transaction.matched_transaction_rule && <RuleAction transaction={transaction} />}
<VouchersForTransaction transaction={transaction} contentHeight={contentHeight} />
</div>
}
const RuleAction = ({ transaction }: { transaction: UnreconciledTransaction }) => {
const { data: rule } = useGetRuleForTransaction(transaction)
const setTransferModalOpen = useSetAtom(bankRecTransferModalAtom)
const setRecordPaymentModalOpen = useSetAtom(bankRecRecordPaymentModalAtom)
const setRecordJournalEntryModalOpen = useSetAtom(bankRecRecordJournalEntryModalAtom)
if (!rule) {
return null
}
const getActionIcon = () => {
switch (rule.classify_as) {
case "Bank Entry":
return <Landmark />
case "Payment Entry":
return <Receipt className="w-6 h-6" />
case "Transfer":
return <ArrowRightLeft />
default:
return <ZapIcon />
}
}
const getActionStyles = () => {
switch (rule.classify_as) {
case "Bank Entry":
return {
border: "border-outline-blue-3",
bg: "bg-surface-blue-1/50",
text: "text-ink-blue-4",
theme: "blue",
}
case "Payment Entry":
return {
border: "border-outline-green-3",
bg: "bg-surface-green-1/50",
text: "text-ink-green-4",
theme: "green",
}
case "Transfer":
return {
border: "border-outline-violet-3",
bg: "bg-surface-violet-2/50",
text: "text-ink-violet-4",
theme: "violet",
}
default:
return {
border: "border-outline-amber-3",
bg: "bg-surface-amber-1/50",
text: "text-ink-amber-4",
theme: "orange",
}
}
}
const handleActionClick = () => {
switch (rule.classify_as) {
case "Bank Entry":
setRecordJournalEntryModalOpen(true)
break
case "Payment Entry":
setRecordPaymentModalOpen(true)
break
case "Transfer":
setTransferModalOpen(true)
break
}
}
const getActionDescription = () => {
switch (rule.classify_as) {
case "Bank Entry":
return _("Create a journal entry for expenses, income or split transactions")
case "Payment Entry":
return _("Record a payment entry against a customer or supplier")
case "Transfer":
return _("Record an internal transfer to another bank/credit card/cash account")
default:
return _("Create a new entry based on the rule")
}
}
useHotkeys('meta+r', () => {
//
handleActionClick()
}, {
enabled: true,
enableOnFormTags: false,
preventDefault: true
})
const styles = getActionStyles()
return (
<Card className={`border ${styles.border} ${styles.bg} shadow-sm hover:shadow-md transition-all duration-200`}>
<CardHeader className="pb-0">
<CardTitle className="flex justify-between items-center gap-3">
<div className="flex items-center gap-3">
<div className={`px-2.5 rounded-lg ${styles.bg} ${styles.text}`}>
{getActionIcon()}
</div>
<div className="flex flex-col gap-0.5">
<span className="font-semibold text-lg">{rule.rule_name}</span>
<span className="text-sm text-ink-gray-5 font-normal">
{rule.rule_description || _("Rule matched based on transaction description and other criteria.")}
</span>
</div>
</div>
<div className="flex items-center gap-0.5">
<Badge size='lg'
theme={rule.classify_as === "Bank Entry" ? "blue" : rule.classify_as === "Payment Entry" ? "green" : rule.classify_as === "Transfer" ? "violet" : "orange"}>
{rule.classify_as}
</Badge>
</div>
</CardTitle>
</CardHeader>
<CardContent className="pt-0 space-y-3">
<div className="flex items-center justify-between p-2 bg-surface-white rounded-lg border border-outline-gray-1">
<div className="flex items-center gap-2">
<BadgeCheck className="w-4 h-4 text-ink-green-3" />
<span className="text-sm font-medium text-ink-gray-8">{_("Recommended Action")}</span>
</div>
<Badge variant="ghost" theme={styles.theme as "blue" | "green" | "violet" | "orange"}>
{_("Priority")} {rule.priority}
</Badge>
</div>
<div className="space-y-2">
{rule.account && (
<div className="flex items-center gap-2">
<span className="text-sm font-medium text-ink-gray-8">{_("Account")}:</span>
<span className="text-sm">{rule.account}</span>
</div>
)}
{rule.party_type && rule.party && (
<div className="flex items-center gap-2">
<span className="text-sm font-medium text-ink-gray-8">{_("Party")}:</span>
<span className="text-sm">{rule.party} ({_(rule.party_type)})</span>
</div>
)}
</div>
<div className="pt-1">
<Button
onClick={handleActionClick}
className={`w-full`}
theme={styles.theme as "blue" | "green" | "violet"}
size="md"
>
{getActionIcon()}
<span>{_("Create")} {rule.classify_as}</span>
</Button>
<p className="text-sm text-ink-gray-5 mt-2 text-center leading-relaxed">
{getActionDescription()}
</p>
</div>
</CardContent>
</Card>
)
}
const VouchersForTransaction = ({ transaction, contentHeight }: { transaction: UnreconciledTransaction, contentHeight: number }) => {
const { data: vouchers, isLoading, error } = useGetVouchersForTransaction(transaction)
if (error) {
return <ErrorBanner error={error} />
}
if (isLoading) {
return <div className="flex flex-col gap-2">
<div className="flex items-center gap-2 text-sm text-ink-gray-5">
<Separator className="flex-1" />
<span>or</span>
<Separator className="flex-1" />
</div>
<Skeleton className="h-16 w-full" />
<Skeleton className="h-16 w-full" />
<Skeleton className="h-16 w-full" />
<Skeleton className="h-16 w-full" />
<Skeleton className="h-16 w-full" />
<Skeleton className="h-16 w-full" />
</div>
}
return <div className="relative space-y-2">
<div className="flex items-center gap-2 text-sm text-ink-gray-5">
<Separator className="flex-1" />
<span>or</span>
<Separator className="flex-1" />
</div>
{vouchers?.message.length === 0 && <Empty className="my-4">
<EmptyMedia>
<ReceiptIcon />
</EmptyMedia>
<EmptyHeader>
<EmptyTitle>{_("No vouchers found for this transaction")}</EmptyTitle>
</EmptyHeader>
</Empty>}
<Virtuoso
data={vouchers?.message}
itemContent={(index, voucher) => (
<VoucherItem voucher={voucher} index={index} />
)}
style={{ height: contentHeight }}
totalCount={vouchers?.message.length}
/>
</div >
}
const VoucherItem = ({ voucher, index }: { voucher: LinkedPayment, index: number }) => {
const selectedBank = useAtomValue(selectedBankAccountAtom)
const selectedTransaction = useAtomValue(bankRecSelectedTransactionAtom(selectedBank?.name || ''))
const { amountMatches, postingDateMatches, referenceDateMatches, referenceMatchesFull, referenceMatchesPartial, isSuggested } = useMemo(() => {
const transaction = selectedTransaction?.[0]
// We need to check if the following details match:
// Amount
// Date
// Reference/Description: Full or partial
// Whether this is suggested or not - depends on the above scores
const amountMatches = voucher.paid_amount === transaction?.unallocated_amount
const postingDateMatches = voucher.posting_date === transaction?.date
const referenceDateMatches = voucher.reference_date === transaction?.date
const referenceMatchesFull = voucher.reference_no === transaction?.reference_number || voucher.reference_no === transaction?.description
const referenceMatchesPartial = transaction?.reference_number?.includes(voucher.reference_no) || transaction?.description?.includes(voucher.reference_no)
const isSuggested = amountMatches && (postingDateMatches || referenceDateMatches || referenceMatchesPartial) && index === 0
return { isSelected: false, amountMatches, postingDateMatches, referenceDateMatches, referenceMatchesFull, referenceMatchesPartial, isSuggested: isSuggested }
}, [voucher, selectedTransaction, index])
const { reconcileTransaction, loading } = useReconcileTransaction()
const onClick = () => {
if (!selectedTransaction) {
return
}
reconcileTransaction(selectedTransaction[0], voucher)
}
return <div className="py-1 px-1">
<div
className={cn("border outline overflow-hidden relative rounded-md p-2",
isSuggested ? "border-outline-green-4 bg-surface-green-1/40 outline-outline-green-4" : "border-outline-gray-2 outline-transparent"
)}
>
<div className="flex justify-between items-end gap-2">
<div className="flex flex-col gap-2">
<div className="flex items-center gap-2">
<Badge size='md'>{_(voucher.doctype)}</Badge>
<a target="_blank"
href={`/desk/${slug(voucher.doctype)}/${voucher.name}`}
className="underline underline-offset-2 text-base"
>{voucher.name}</a>
</div>
{voucher.party && voucher.party_type && <div className="flex items-center gap-1.5 text-base">
<User size='18px' />
<span>{_(voucher.party_type)}</span>
<a target="_blank"
href={`/desk/${slug(voucher.party_type)}/${voucher.party}`}
className="underline underline-offset-2"
>{voucher.party}</a>
</div>}
<TooltipProvider>
<div className="flex items-start gap-8 py-0.5">
<div className="flex flex-col gap-1 min-w-24">
<div className="text-xs text-ink-gray-6">{_("Amount")}</div>
<div className="text-base font-medium flex items-center gap-1">{formatCurrency(voucher.paid_amount, voucher.currency)} {amountMatches ? <MatchBadge matchType="full" label={_("Amount matches the selected transaction")} /> : <MatchBadge matchType="none" label={_("Amount does not match the selected transaction")} />}</div>
</div>
<div className="flex flex-col gap-1 min-w-24">
<div className="text-xs text-ink-gray-6">{_("Posted On")}</div>
<div className="text-base font-medium flex items-center gap-1">{formatDate(voucher.posting_date)} {postingDateMatches ? <MatchBadge matchType="full" label={_("Posting date matches the selected transaction")} /> : <MatchBadge matchType="none" label={_("Posting date does not match the selected transaction")} />}</div>
</div>
{voucher.reference_date && <div className="flex flex-col gap-1 min-w-24">
<div className="text-xs text-ink-gray-6">{_("Reference Date")}</div>
<div className="text-base font-medium flex items-center gap-1">{formatDate(voucher.reference_date)} {referenceDateMatches ? <MatchBadge matchType="full" label={_("Reference date matches the selected transaction")} /> : <MatchBadge matchType="none" label={_("Reference date does not match the selected transaction")} />}</div>
</div>}
</div>
{voucher.reference_no && <div className="flex items-start gap-1">
<span className="text-p-base">
{voucher.reference_no}
&nbsp;&nbsp;
<Tooltip>
<TooltipTrigger>
<Badge theme={referenceMatchesFull ? "green" : referenceMatchesPartial ? "orange" : "red"} variant={referenceMatchesFull || referenceMatchesPartial ? "subtle" : "outline"}>
{referenceMatchesFull ? `${_("Complete Match")}` : referenceMatchesPartial ? `${_("Partial Match")}` : `${_("No Match")}`}</Badge>
</TooltipTrigger>
<TooltipContent side="top">
{referenceMatchesFull ? `${_("Reference matches the selected transaction")}` : referenceMatchesPartial ? `${_("Reference matches the selected transaction partially")}` : `${_("Reference does not match the selected transaction")}`}
</TooltipContent>
</Tooltip>
</span>
</div>}
</TooltipProvider>
</div>
<div>
<Button
variant={isSuggested || amountMatches ? "solid" : "outline"}
theme={isSuggested || amountMatches ? "green" : "gray"}
onClick={onClick} disabled={loading}>{loading ? <><Loader2 className="w-4 h-4 animate-spin" /> {_("Reconciling")}...</> : `${_("Reconcile")}`}</Button>
</div>
</div>
{isSuggested && <div className="absolute top-1.5 end-2 flex items-center gap-1 justify-center">
<Badge theme="green" variant="subtle" size='md'>{_("Suggested")}</Badge>
</div>}
</div>
</div>
}
const MatchBadge = ({ matchType, label }: { matchType: 'full' | 'partial' | 'none', label: string }) => {
return <Tooltip>
<TooltipTrigger>
{matchType === 'full' ? <BadgeCheck className="text-ink-white fill-surface-green-5 size-4" /> : matchType === 'partial' ?
<Badge theme="orange" variant="subtle">{_("Partial Match")}</Badge> :
<XCircle className="text-ink-red-4 size-4" />}
</TooltipTrigger>
<TooltipContent>
{label}
</TooltipContent>
</Tooltip>
}
const OlderUnreconciledTransactionsBanner = () => {
// A banner to show when there are unreconciled transactions for the given bank account before the current selected date
const [dates, setDates] = useAtom(bankRecDateAtom)
const selectedBank = useAtomValue(selectedBankAccountAtom)
const { data } = useFrappeGetCall<{
message: {
count: number,
oldest_date: string
}
}>("erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_older_unreconciled_transactions", {
bank_account: selectedBank?.name,
from_date: dates.fromDate,
}, undefined, {
revalidateOnFocus: false,
})
if (data && data.message.count > 0) {
return <Alert theme='gray' variant='subtle'>
<AlertCircleIcon />
<div className="flex justify-between items-center gap-1.5">
<div>
<AlertTitle> {data.message.count > 1 ? (
<span>{_("There are {0} unreconciled transactions before {1}.", [data.message.count.toString(), formatDate(dates.fromDate)])}</span>
) : (
<span>{_("There is one unreconciled transaction before {0}.", [formatDate(dates.fromDate)])}</span>
)}</AlertTitle>
<AlertDescription className="flex justify-between text-balance">
{_("The opening balance might not match your bank statement. Would you like to reconcile them?")}
</AlertDescription>
</div>
<div>
<Button
size='sm'
type='button'
theme='gray'
variant='outline'
onClick={() => setDates({ fromDate: data.message.oldest_date, toDate: dates.toDate })}>
<span>{data.message.count > 1 ? _("View older transactions") : _("View older transaction")}</span>
<ArrowRightIcon />
</Button>
</div>
</div>
</Alert>
}
return null
}
export default MatchAndReconcile

View File

@@ -1,93 +0,0 @@
import { Button } from '@/components/ui/button'
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
import _ from '@/lib/translate'
import { FilterIcon } from 'lucide-react'
import { bankRecMatchFilters } from './bankRecAtoms'
import { useAtom } from 'jotai'
import { Switch } from '@/components/ui/switch'
import { Label } from '@/components/ui/label'
import { Separator } from '@/components/ui/separator'
import { useFrappeGetCall } from 'frappe-react-sdk'
import { scrub } from '@/lib/frappe'
import { useMemo } from 'react'
const MatchFilters = () => {
return (
<Popover>
<Tooltip>
<PopoverTrigger asChild>
<TooltipTrigger asChild>
<Button size='md' isIconButton variant='outline' aria-label={_("Configure match filters for vouchers")}>
<FilterIcon />
</Button>
</TooltipTrigger>
</PopoverTrigger>
<TooltipContent>
{_("Configure match filters for vouchers")}
</TooltipContent>
</Tooltip>
<PopoverContent>
<div className="flex flex-col gap-4">
<ToggleSwitch label={_("Show Only Exact Amount")} id="exact_match" />
<Separator />
<MatchFiltersContent />
<ToggleSwitch label={_("Bank Transaction")} id="bank_transaction" />
</div>
</PopoverContent>
</Popover>
)
}
const MatchFiltersContent = () => {
const { data } = useFrappeGetCall<{ message: string[] }>("erpnext.accounts.doctype.bank_transaction.bank_transaction.get_doctypes_for_bank_reconciliation", undefined,
"bank_rec_doctypes", {
revalidateOnFocus: false,
revalidateIfStale: false,
revalidateOnReconnect: false,
}
)
const doctypes = useMemo(() => {
const STANDARD_DOCTYPES = ["Payment Entry", "Journal Entry", "Purchase Invoice", "Sales Invoice"]
if (data) {
return data.message.map(doctype => ({
label: doctype,
id: scrub(doctype),
}))
} else {
return STANDARD_DOCTYPES.map(doctype => ({
label: doctype,
id: scrub(doctype),
}))
}
}, [data])
return (
<div className="flex flex-col gap-4">
{doctypes.map((doctype) => (
<ToggleSwitch key={doctype.id} label={doctype.label} id={doctype.id} />
))}
</div>
)
}
const ToggleSwitch = ({ label, id }: { label: string, id: string }) => {
const [matchFilters, setMatchFilters] = useAtom(bankRecMatchFilters)
return <div className="flex items-center space-x-2">
<Switch id={id} checked={matchFilters.includes(id)} onCheckedChange={(checked) => {
if (checked) {
setMatchFilters([...matchFilters, id])
} else {
setMatchFilters(matchFilters.filter(filter => filter !== id))
}
}} />
<Label htmlFor={id}>{label}</Label>
</div>
}
export default MatchFilters

View File

@@ -1,10 +0,0 @@
import { Paragraph } from "@/components/ui/typography"
import { cn } from "@/lib/utils"
import { ReactNode } from "react"
export const MissingFiltersBanner = ({ text, className }: { text: ReactNode, className?: string }) => {
return <div className={cn("min-h-[50vh] flex items-center justify-center", className)}>
<Paragraph>{text}</Paragraph>
</div>
}

View File

@@ -1,89 +0,0 @@
import { Button } from "@/components/ui/button"
import ErrorBanner from "@/components/ui/error-banner"
import { Form } from "@/components/ui/form"
import { useCurrentCompany } from "@/hooks/useCurrentCompany"
import _ from "@/lib/translate"
import { BankTransactionRule } from "@/types/Accounts/BankTransactionRule"
import { useFrappeCreateDoc } from "frappe-react-sdk"
import { toast } from "sonner"
import { RuleForm } from "./RuleForm"
import { useForm } from "react-hook-form"
import { SettingsPanelHeader, SettingsPanelDescription, SettingsPanelTitle, SettingsPanelContent } from "@/components/ui/settings-dialog"
import { useHotkeys } from "react-hotkeys-hook"
type Props = {
onCreate: VoidFunction
}
const CreateNewRule = ({ onCreate }: Props) => {
const currentCompany = useCurrentCompany()
const form = useForm<BankTransactionRule>({
defaultValues: {
rule_name: "",
company: currentCompany,
rule_description: "",
transaction_type: "Any",
classify_as: 'Bank Entry',
bank_entry_type: "Single Account",
description_rules: [{
check: "Contains",
}]
}
})
const { createDoc, loading, error } = useFrappeCreateDoc<BankTransactionRule>()
const onSubmit = (data: BankTransactionRule) => {
createDoc("Bank Transaction Rule", data)
.then(() => {
toast.success(_("Rule created successfully"))
onCreate()
})
}
useHotkeys('meta+s', () => {
form.handleSubmit(onSubmit)()
}, {
enabled: true,
preventDefault: true,
enableOnFormTags: true
})
return (
<>
<SettingsPanelHeader
actions={
<div className="flex items-center gap-2">
<Button variant='outline' size='md' type='button' onClick={() => onCreate()}>{_("Cancel")}</Button>
<Button type='submit' form='rule-form' size='md' disabled={loading}>
{_("Save")}
</Button>
</div>
}
>
<SettingsPanelTitle>
{_("New Rule")}
</SettingsPanelTitle>
<SettingsPanelDescription>
{_("Create a new rule to automatically classify transactions.")}
</SettingsPanelDescription>
</SettingsPanelHeader>
<SettingsPanelContent className="px-0">
<Form {...form}>
<form id='rule-form' onSubmit={form.handleSubmit(onSubmit)} className="flex flex-col justify-between h-full overflow-y-auto px-2">
<div className="flex flex-col gap-4">
{error && <ErrorBanner error={error} />}
<RuleForm />
</div>
</form>
</Form>
</SettingsPanelContent>
</>
)
}
export default CreateNewRule

View File

@@ -1,101 +0,0 @@
import { Button } from "@/components/ui/button"
import ErrorBanner from "@/components/ui/error-banner"
import { Form } from "@/components/ui/form"
import _ from "@/lib/translate"
import { BankTransactionRule } from "@/types/Accounts/BankTransactionRule"
import { FrappeError, useFrappeGetDoc, useFrappeUpdateDoc } from "frappe-react-sdk"
import { toast } from "sonner"
import { RuleForm } from "./RuleForm"
import { useForm } from "react-hook-form"
import { Skeleton } from "@/components/ui/skeleton"
import { SettingsPanelContent, SettingsPanelDescription, SettingsPanelHeader, SettingsPanelTitle } from "@/components/ui/settings-dialog"
import { useHotkeys } from "react-hotkeys-hook"
type Props = {
onClose: VoidFunction,
ruleID: string
}
const EditRule = ({ onClose, ruleID }: Props) => {
const { data: rule, isValidating, error, mutate } = useFrappeGetDoc<BankTransactionRule>("Bank Transaction Rule", ruleID, undefined, {
revalidateOnMount: true
})
const { updateDoc, loading, error: updateError } = useFrappeUpdateDoc<BankTransactionRule>()
const onSubmit = (data: BankTransactionRule) => {
updateDoc("Bank Transaction Rule", ruleID, data)
.then(() => {
toast.success(_("Rule updated."))
mutate()
onClose()
})
}
return <>
<SettingsPanelHeader
actions={
<div className="flex items-center gap-2">
<Button variant='outline' size='md' type='button' onClick={() => onClose()}>{_("Cancel")}</Button>
<Button type='submit' form='rule-form' size='md' disabled={isValidating || loading}>
{_("Save")}
</Button>
</div>
}
>
<SettingsPanelTitle>
{rule?.rule_name}
</SettingsPanelTitle>
<SettingsPanelDescription className="sr-only">
{_("Edit this rule")}
</SettingsPanelDescription>
</SettingsPanelHeader>
<SettingsPanelContent className="px-0">
{isValidating && <div className="px-4 flex flex-col gap-4 h-full">
<Skeleton className="h-10 w-full" />
<Skeleton className="h-10 w-full" />
<Skeleton className="h-10 w-full" />
<Skeleton className="h-10 w-full" />
<Skeleton className="h-10 w-full" />
</div>}
{error && <div className="px-4 flex flex-col gap-4 h-full">
<ErrorBanner error={error} />
</div>}
{rule && <EditRuleForm rule={rule} onSubmit={onSubmit} error={updateError} />}
</SettingsPanelContent>
</>
}
const EditRuleForm = ({ rule, onSubmit, error }: { rule: BankTransactionRule, onSubmit: (data: BankTransactionRule) => void, error?: FrappeError | null }) => {
const form = useForm<BankTransactionRule>({
defaultValues: {
...rule,
}
})
useHotkeys('meta+s', () => {
form.handleSubmit(onSubmit)()
}, {
enabled: true,
preventDefault: true,
enableOnFormTags: true
})
return (
<Form {...form}>
<form id='rule-form' onSubmit={form.handleSubmit(onSubmit)} className="flex flex-col justify-between h-full overflow-y-auto px-2">
<div className="flex flex-col gap-4">
{error && <ErrorBanner error={error} />}
<RuleForm isEdit />
</div>
</form>
</Form>
)
}
export default EditRule

View File

@@ -1,799 +0,0 @@
import { Button } from "@/components/ui/button"
import { Checkbox } from "@/components/ui/checkbox"
import { Dialog, DialogTitle, DialogContent, DialogHeader, DialogDescription } from "@/components/ui/dialog"
import { FormField, FormItem, FormLabel, FormControl } from "@/components/ui/form"
import { AccountFormField, CurrencyFormField, DataField, LinkFormField, PartyTypeFormField, SelectFormField, SmallTextField } from "@/components/ui/form-elements"
import { Label } from "@/components/ui/label"
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
import { SelectItem } from "@/components/ui/select"
import { Separator } from "@/components/ui/separator"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
import { H4, Paragraph } from "@/components/ui/typography"
import { today } from "@/lib/date"
import _ from "@/lib/translate"
import { cn } from "@/lib/utils"
import { BankTransactionRule } from "@/types/Accounts/BankTransactionRule"
import { BankTransactionRuleAccounts } from "@/types/Accounts/BankTransactionRuleAccounts"
import { FrappeConfig, FrappeContext } from "frappe-react-sdk"
import { ArrowDownRight, ArrowDownUp, ArrowRightLeftIcon, ArrowUpRight, LandmarkIcon, Plus, PlusCircleIcon, ReceiptIcon, Settings, Trash2 } from "lucide-react"
import { ChangeEvent, useCallback, useContext, useMemo, useRef, useState } from "react"
import { useFieldArray, useFormContext, useWatch } from "react-hook-form"
export const RuleForm = ({ isEdit = false }: { isEdit?: boolean }) => {
return <div className="flex flex-col gap-4">
<DataField
name='rule_name'
label={_("Rule Name")}
disabled={isEdit}
isRequired
inputProps={{
maxLength: 140,
disabled: isEdit,
placeholder: _("Bank Charges, Salary, etc."),
autoFocus: true,
className: "dark:disabled:bg-surface-gray-2"
}}
rules={{
required: _("Rule name is required")
}}
/>
<CompanySelector />
<SmallTextField
name='rule_description'
label={_("Rule Description")}
inputProps={{
placeholder: _("Any debit transaction with the keyword 'Bank Fee'.")
}}
/>
<TransactionTypeSelector />
<div className="grid grid-cols-2 gap-2 pt-1">
<CurrencyFormField
name='min_amount'
label={_("Minimum Amount")}
/>
<CurrencyFormField
name='max_amount'
label={_("Maximum Amount")}
/>
</div>
<DescriptionRules />
<Separator />
<RuleAction />
</div>
}
const CompanySelector = () => {
const { setValue } = useFormContext<BankTransactionRule>()
return <LinkFormField
name='company'
label={_("Company")}
doctype="Company"
isRequired
rules={{
required: _("Company is required"),
onChange: () => {
setValue('account', '')
}
}}
/>
}
/** Component to render a radio group as a toggle group with options for All, Withdrawal, Deposit */
const TransactionTypeSelector = () => {
const { control } = useFormContext<BankTransactionRule>()
return (
<FormField
control={control}
name='transaction_type'
render={({ field }) => (
<FormItem className="space-y-1">
<FormLabel className="text-sm font-medium">
{_("Transaction Type")}<span className="text-ink-red-3">*</span>
</FormLabel>
<FormControl>
<RadioGroup
onValueChange={field.onChange}
value={field.value}
className="grid grid-cols-3 gap-2 w-full"
>
<FormItem className="flex items-center">
<FormControl>
<RadioGroupItem
value="Any"
className="peer sr-only hidden"
/>
</FormControl>
<FormLabel
className={cn(
"w-full flex items-center justify-center gap-2 px-4 py-2 text-sm font-medium rounded-md border cursor-pointer transition-all hover:bg-surface-gray-1 hover:text-ink-gray-8",
"peer-data-[state=checked]:bg-surface-gray-7 peer-data-[state=checked]:text-ink-white peer-data-[state=checked]:border-outline-gray-5 peer-data-[state=checked]:hover:bg-surface-gray-7 peer-data-[state=checked]:hover:text-ink-white"
)}
>
<ArrowDownUp className="w-5 h-5" />
{_("All")}
</FormLabel>
</FormItem>
<FormItem className="flex items-center">
<FormControl>
<RadioGroupItem
value="Withdrawal"
className="peer sr-only hidden"
/>
</FormControl>
<FormLabel
className={cn(
"w-full flex items-center justify-center gap-2 px-4 py-2 text-sm font-medium rounded-md border cursor-pointer transition-all hover:bg-surface-gray-1 hover:text-ink-gray-8",
"peer-data-[state=checked]:bg-surface-red-5 peer-data-[state=checked]:text-white peer-data-[state=checked]:border-bg-surface-red-5 peer-data-[state=checked]:hover:bg-surface-red-5 peer-data-[state=checked]:hover:text-white"
)}
>
<ArrowUpRight className="w-5 h-5 peer-data-[state=checked]:text-ink-red-3" />
{_("Withdrawal")}
</FormLabel>
</FormItem>
<FormItem className="flex items-center">
<FormControl>
<RadioGroupItem
value="Deposit"
className="peer sr-only hidden"
/>
</FormControl>
<FormLabel
className={cn(
"w-full flex items-center justify-center gap-2 px-4 py-2 text-sm font-medium rounded-md border cursor-pointer transition-all hover:bg-surface-gray-1 hover:text-ink-gray-8",
"peer-data-[state=checked]:bg-surface-green-5 peer-data-[state=checked]:text-white peer-data-[state=checked]:border-surface-green-5 peer-data-[state=checked]:hover:bg-surface-green-5 peer-data-[state=checked]:hover:text-white"
)}
>
<ArrowDownRight className="w-5 h-5 peer-data-[state=checked]:text-white" />
{_("Deposit")}
</FormLabel>
</FormItem>
</RadioGroup>
</FormControl>
</FormItem>
)}
/>
)
}
const DescriptionRules = () => {
const { control } = useFormContext<BankTransactionRule>()
const { fields, append, remove } = useFieldArray({
control,
name: "description_rules"
})
const addRow = () => {
// @ts-expect-error - we don't need all fields here
append({ check: "Contains" })
}
return (
<div className="flex flex-col gap-2 pt-1">
<span className="text-sm font-medium">{_("Rules to match against the transaction description")} <span className="text-ink-red-3">*</span></span>
{fields.map((field, index) => (
<div key={field.id} className="flex w-full items-center gap-2">
<div className="min-w-36">
<SelectFormField
label={_("Type of check")}
hideLabel
name={`description_rules.${index}.check`}
rules={{
required: _("This is required")
}}>
<SelectItem value="Contains">{_("Contains")}</SelectItem>
<SelectItem value="Starts With">{_("Starts with")}</SelectItem>
<SelectItem value="Ends With">{_("Ends with")}</SelectItem>
<SelectItem value="Regex">{_("Regex")}</SelectItem>
</SelectFormField>
</div>
<div className="w-full">
<DataField
name={`description_rules.${index}.value`}
label={_("Value")}
hideLabel
inputProps={{
placeholder: _("Bank Fee, Salary, etc."),
}}
/>
</div>
<div>
<Button variant="ghost" theme='red' type='button' isIconButton onClick={() => remove(index)} disabled={fields.length === 1}>
<Trash2 />
</Button>
</div>
</div>
))}
<div>
<Button variant="outline" type='button' onClick={addRow}>
<PlusCircleIcon />
{_("Add Rule")}
</Button>
</div>
</div>
)
}
const RuleAction = () => {
const { control } = useFormContext<BankTransactionRule>()
const classify_as = useWatch({ control, name: "classify_as" })
const party_type = useWatch({ control, name: "party_type" })
const bank_entry_type = useWatch({ control, name: "bank_entry_type" })
const accountType = useMemo(() => {
if (classify_as === "Payment Entry") {
return party_type === "Supplier" ? ["Payable"] : ["Receivable"]
}
if (classify_as === "Transfer") {
return ["Bank", "Cash", "Temporary"]
}
return undefined
}, [classify_as, party_type])
return (
<div className="flex flex-col gap-4">
<H4 className="text-base text-ink-gray-7">{_("If rule matches, then:")}</H4>
<SelectFormField
name='classify_as'
isRequired
label={_("Suggest creating a")}
formDescription={_("This will just suggest creating a new entry, and will not automatically create it.")}
rules={{
required: _("This is required")
}}
>
<SelectItem value="Bank Entry"><LandmarkIcon /> {_("Bank Entry")}</SelectItem>
<SelectItem value="Payment Entry"><ReceiptIcon /> {_("Payment Entry")}</SelectItem>
<SelectItem value="Transfer"><ArrowRightLeftIcon /> {_("Transfer")}</SelectItem>
</SelectFormField>
{classify_as === "Bank Entry" && (<SelectFormField
name='bank_entry_type'
isRequired
label={_("Create Bank Entry against")}
rules={{
required: _("This is required")
}}
>
<SelectItem value="Single Account">{_("Single Account")}</SelectItem>
<SelectItem value="Multiple Accounts">{_("Multiple Accounts (Journal Template)")}</SelectItem>
</SelectFormField>)}
{classify_as === "Payment Entry" && (
<div className='grid grid-cols-4 gap-4'>
<div className="col-span-1">
<PartyTypeFormField
name='party_type'
label={_("Party Type")}
isRequired
inputProps={{
triggerProps: {
className: 'w-full'
},
}}
rules={{
required: "Party Type is required"
}}
/>
</div>
<div className="col-span-3">
<PartyField />
</div>
</div>
)}
{(((bank_entry_type === "Single Account" || !bank_entry_type) && classify_as === "Bank Entry") || classify_as !== "Bank Entry") && (<AccountFormField
name='account'
label={_("Account")}
isRequired
rules={{
required: _("Account is required")
}}
account_type={accountType}
/>)}
{bank_entry_type === "Multiple Accounts" && classify_as === "Bank Entry" && <MultipleAccountsSelection />}
</div>
)
}
const PartyField = () => {
const { control, setValue } = useFormContext<BankTransactionRule>()
const party_type = useWatch({
control,
name: `party_type`
})
const { call } = useContext(FrappeContext) as FrappeConfig
const company = useWatch({ control, name: 'company' })
const onChange = (event: ChangeEvent<HTMLInputElement>) => {
// Fetch the party and account
if (event.target.value) {
call.get('erpnext.accounts.doctype.payment_entry.payment_entry.get_party_details', {
company: company,
party_type: party_type,
party: event.target.value,
date: today()
}).then((res) => {
setValue('account', res.message.party_account)
})
} else {
// Clear the account
setValue('account', '')
}
}
if (!party_type) {
return <DataField
name={`party`}
label={_("Party")}
isRequired
inputProps={{
disabled: true,
}}
/>
}
return <LinkFormField
name={`party`}
label={_("Party")}
rules={{
onChange
}}
doctype={party_type}
/>
}
const MultipleAccountsSelection = () => {
const { control } = useFormContext<BankTransactionRule>()
const accounts = useWatch({
control,
name: 'accounts'
}) ?? []
const [isConfigureAccountsModalOpen, setIsConfigureAccountsModalOpen] = useState(false)
return <div className="flex flex-col gap-2">
<div className="flex justify-between gap-2">
<Label>{_("Journal Template Accounts")}<span className="text-ink-red-3">*</span></Label>
<Button variant="outline" type="button" onClick={() => setIsConfigureAccountsModalOpen(true)}><Settings /> {_("Configure Accounts")}</Button>
</div>
<Table>
<TableHeader>
<TableRow>
<TableHead>{_("Account")}</TableHead>
<TableHead className="text-end">{_("Debit")}</TableHead>
<TableHead className="text-end">{_("Credit")}</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{accounts.length === 0 && (
<TableRow>
<TableCell colSpan={3} className="text-center">
<div className="py-2 flex flex-col gap-2 items-center">
<span>{_("No accounts configured")}</span>
<Button variant="subtle" type="button" onClick={() => setIsConfigureAccountsModalOpen(true)}>{_("Configure Accounts")}</Button>
</div>
</TableCell>
</TableRow>
)}
{accounts.map((account, index) => (
<TableRow key={index}>
<TableCell>{account.account}</TableCell>
{index === accounts.length - 1 ? <TableCell className="text-end bg-surface-gray-1" colSpan={2}>
<Tooltip>
<TooltipTrigger asChild>
<span className="text-ink-gray-5">{_("This is auto computed to balance the journal entry.")}</span>
</TooltipTrigger>
<TooltipContent>
{_("Based on the above entries, the balance amount (debit or credit) will be set for the last row to balance the journal entry.")}
</TooltipContent>
</Tooltip>
</TableCell> : <>
<TableCell className="font-numeric text-end"><AmountFormulaRenderer value={account.debit} /></TableCell>
<TableCell className="font-numeric text-end"><AmountFormulaRenderer value={account.credit} /></TableCell>
</>}
</TableRow>
))}
</TableBody>
</Table>
<ConfigureAccountsModal open={isConfigureAccountsModalOpen} onClose={() => setIsConfigureAccountsModalOpen(false)} />
</div>
}
const AmountFormulaRenderer = ({ value }: { value?: string }) => {
// If it's a string and cannot be a number, then show it as a formula
if (isNaN(Number(value))) {
let calculatedValue = "";
try {
calculatedValue = window.eval(`const transaction_amount = 200; ${value}`);
} catch (error: unknown) {
console.error(error);
calculatedValue = "Error";
}
const isComputationValid = !isNaN(Number(calculatedValue)) && calculatedValue !== undefined && calculatedValue !== null;
return <Tooltip>
<TooltipTrigger asChild>
<span className={cn("font-numeric text-end tabular-nums underline underline-offset-4", isComputationValid ? "" : "text-ink-red-3")}>{value}</span>
</TooltipTrigger>
<TooltipContent className={isComputationValid ? "" : "bg-surface-red-5"} arrowClassName={isComputationValid ? "" : "bg-surface-red-5 fill-surface-red-5"}>
<p className="text-sm">
{isComputationValid ? _("This is a formula based value.") : _("This is not a valid formula. Check the variable used in the formula.")}
<br /><br />
{_("Example: If the transaction amount is 200, then this will be calculated as {} = {}", [value ?? "", calculatedValue])}
</p>
</TooltipContent>
</Tooltip>
}
return <span className="font-numeric text-end tabular-nums">{value}</span>
}
const ConfigureAccountsModal = ({ open, onClose }: { open: boolean, onClose: () => void }) => {
return <Dialog
open={open}
onOpenChange={onClose}
>
<DialogContent className='min-w-[95vw]'>
<ConfigureAccountsModalContent />
</DialogContent>
</Dialog>
}
const ConfigureAccountsModalContent = () => {
const { control, getValues, setValue } = useFormContext<BankTransactionRule>()
const { call } = useContext(FrappeContext) as FrappeConfig
// const costCenterMapRef = useRef<Record<string, string>>({})
const partyMapRef = useRef<Record<string, string>>({})
const onPartyChange = (value: string, index: number) => {
// Get the account for the party type
if (value) {
if (partyMapRef.current[value]) {
setValue(`accounts.${index}.account`, partyMapRef.current[value])
} else {
call.get('erpnext.accounts.party.get_party_account', {
party: value,
party_type: getValues(`accounts.${index}.party_type`),
company: company
}).then((result: { message: string }) => {
setValue(`accounts.${index}.account`, result.message)
partyMapRef.current[value] = result.message
})
}
} else {
setValue(`accounts.${index}.account`, '')
}
}
const transaction_type = useWatch({
name: 'transaction_type',
control,
})
const { fields, append, remove } = useFieldArray({
control,
name: 'accounts'
})
const [selectedRows, setSelectedRows] = useState<number[]>([])
const onSelectRow = useCallback((index: number) => {
setSelectedRows(prev => {
if (prev.includes(index)) {
return prev.filter(i => i !== index)
}
return [...prev, index]
})
}, [])
const onSelectAll = useCallback(() => {
setSelectedRows(prev => {
if (prev.length === fields.length) {
return []
}
return [...fields.map((_, index) => index)]
})
}, [fields])
const onAdd = () => {
append({
party_type: '',
party: '',
account: '',
debit: '',
credit: '',
user_remark: ''
} as BankTransactionRuleAccounts, {
focusName: `accounts.${fields.length}.account`
})
}
const onRemove = useCallback(() => {
remove(selectedRows)
setSelectedRows([])
}, [remove, selectedRows])
const isWithdrawal = transaction_type === 'Withdrawal'
const company = useWatch({
name: 'company',
control,
})
return <>
<DialogHeader>
<DialogTitle>{_("Configure Accounts for Bank Entry")}</DialogTitle>
<DialogDescription>{_("Add all accounts that you want to split the transaction into.")}</DialogDescription>
</DialogHeader>
<div className="flex flex-col gap-2">
<Table>
<TableHeader>
<TableRow>
<TableHead><Checkbox
disabled={fields.length === 0}
// Make this accessible to screen readers
aria-label={_("Select all")}
checked={selectedRows.length > 0 && selectedRows.length === fields.length}
onCheckedChange={onSelectAll} /></TableHead>
<TableHead>{_("Party")}</TableHead>
<TableHead>{_("Account")} <span className="text-ink-red-3">*</span></TableHead>
{/* <TableHead>{_("Cost Center")}</TableHead> */}
<TableHead>{_("Remarks")}</TableHead>
<TableHead className="text-end">{_("Debit")}</TableHead>
<TableHead className="text-end">{_("Credit")}</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow className="bg-surface-gray-1 cursor-not-allowed" title={_("This is the row for the bank account. It will be auto populated based on the bank transaction.")}>
<TableCell>
<Checkbox disabled />
</TableCell>
<TableCell className="align-top">
</TableCell>
<TableCell className="align-top text-ink-gray-5">
<span className="px-2">
Bank GL Account
</span>
</TableCell>
<TableCell className="align-top">
</TableCell>
<TableCell className={"align-top text-end"}>
<span className="text-ink-gray-5 text-sm">
{transaction_type === "Withdrawal" || transaction_type === "Any" ? _("Will be auto-populated") : ""}
</span>
</TableCell>
<TableCell className={"text-end align-top"}>
<span className="text-ink-gray-5 text-sm">
{transaction_type === "Deposit" || transaction_type === "Any" ? _("Will be auto-populated") : ""}
</span>
</TableCell>
</TableRow>
{fields.map((field, index) => (
<TableRow key={field.id}>
<TableCell>
<Checkbox
checked={selectedRows.includes(index)}
onCheckedChange={() => onSelectRow(index)}
// Make this accessible to screen readers
aria-label={_("Select row {0}", [String(index + 1)])}
/>
</TableCell>
<TableCell className="align-top">
<div className="flex">
<PartyTypeFormField
name={`accounts.${index}.party_type`}
label={_("Party Type")}
isRequired
hideLabel
inputProps={{
type: isWithdrawal ? 'Payable' : 'Receivable',
triggerProps: {
className: 'rounded-e-none',
tabIndex: -1
},
}} />
<PartyRowField index={index} onChange={onPartyChange} />
</div>
</TableCell>
<TableCell className="align-top">
<AccountFormField
name={`accounts.${index}.account`}
label={_("Account")}
rules={{
required: _("Account is required"),
// onChange: (event) => {
// onAccountChange(event.target.value, index)
// }
}}
buttonClassName="min-w-64"
isRequired
hideLabel
/>
</TableCell>
{/* <TableCell className="align-top">
<LinkFormField
doctype="Cost Center"
name={`accounts.${index}.cost_center`}
label={_("Cost Center")}
filters={[["company", "=", company], ["is_group", "=", 0], ["disabled", "=", 0]]}
buttonClassName="min-w-48"
readOnly={index === 0}
hideLabel
/>
</TableCell> */}
<TableCell className="align-top">
<DataField
name={`accounts.${index}.user_remark`}
label={_("Remarks")}
inputProps={{
placeholder: _("e.g. Bank Charges"),
className: 'min-w-64',
}}
hideLabel
/>
</TableCell>
<TableCell
className={cn("text-end align-top", index === fields.length - 1 ? "cursor-not-allowed" : "")}
title={index === fields.length - 1 ? _("This is the last row. It will be auto populated based on the bank transaction.") : ""}>
<DataField
name={`accounts.${index}.debit`}
label={_("Debit")}
disabled={index === fields.length - 1}
inputProps={{
className: 'text-end',
placeholder: _("0.00"),
disabled: index === fields.length - 1
}}
hideLabel
/>
</TableCell>
<TableCell
className={cn("text-end align-top", index === fields.length - 1 ? "cursor-not-allowed" : "")}
title={index === fields.length - 1 ? _("This is the last row. It will be auto populated based on the bank transaction.") : ""}>
<DataField
name={`accounts.${index}.credit`}
label={_("Credit")}
disabled={index === fields.length - 1}
inputProps={{
className: 'text-end',
placeholder: _("0.00"),
disabled: index === fields.length - 1
}}
hideLabel
/>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
<div className="flex justify-between gap-2">
<div className="flex gap-2 justify-end">
<div>
<Button size='sm' type='button' variant={'outline'} onClick={onAdd}><Plus /> {_("Add Row")}</Button>
</div>
{selectedRows.length > 0 && <div>
<Button size='sm' type='button' theme="red" onClick={onRemove}><Trash2 /> {_("Remove")}</Button>
</div>}
</div>
</div>
<div className="py-4">
<Separator />
</div>
<div className="flex flex-col gap-2">
<H4 className="text-base text-ink-gray-7">{_("Help")}</H4>
<Paragraph className="text-p-sm">{(_("You can set up the rule to split the transaction across multiple accounts."))}
<br />{_("You can also add credit or debit values to pre-fill - these support both static values (like 200) or formulas (like transaction_amount * 0.25).")}
<br />
<br />
<span className="font-medium">{_("Example")}:</span>
<br />
<span className="font-numeric text-sm">
transaction_amount * 0.25
</span>
<br />
<span>
{_("In this case, the amount will be calculated as 25% of the transaction amount. If the transaction amount is 200, then this will be calculated as 200 * 0.25 = 50.")}
</span>
</Paragraph>
</div>
</div>
</>
}
const PartyRowField = ({ index, onChange }: { index: number, onChange: (value: string, index: number) => void }) => {
const { control } = useFormContext<BankTransactionRule>()
const party_type = useWatch({
control,
name: `accounts.${index}.party_type`
})
if (!party_type) {
return <DataField
name={`accounts.${index}.party`}
label={_("Party")}
isRequired
inputProps={{
disabled: true,
className: 'rounded-s-none border-s-0 min-w-64'
}}
hideLabel
/>
}
return <LinkFormField
name={`accounts.${index}.party`}
label={_("Party")}
rules={{
onChange: (event) => {
onChange(event.target.value, index)
},
}}
hideLabel
buttonClassName="rounded-s-none border-s-0 min-w-64"
doctype={party_type}
/>
}

View File

@@ -1,73 +0,0 @@
import { useMemo } from 'react'
import { ArrowDownRight, ArrowUpRight, Calendar } from 'lucide-react'
import { formatCurrency } from '@/lib/numbers'
import { formatDate } from '@/lib/date'
import { UnreconciledTransaction, useGetBankAccounts } from './utils'
import { getCompanyCurrency } from '@/lib/company'
import { Card, CardContent } from '@/components/ui/card'
import { cn } from '@/lib/utils'
import _ from '@/lib/translate'
import BankLogo from '@/components/common/BankLogo'
type Props = {
transaction: UnreconciledTransaction,
showAccount?: boolean,
account?: string
}
const SelectedTransactionDetails = ({ transaction, showAccount = false, account }: Props) => {
const isWithdrawal = transaction.withdrawal && transaction.withdrawal > 0
const { banks } = useGetBankAccounts()
const bank = useMemo(() => {
if (transaction.bank_account) {
return banks?.find((bank) => bank.name === transaction.bank_account)
}
return null
}, [transaction.bank_account, banks])
const amount = transaction.withdrawal ? transaction.withdrawal : transaction.deposit
const currency = transaction.currency || getCompanyCurrency(transaction.company ?? '')
return (
<Card className='py-4'>
<CardContent className='px-4'>
<div className='flex flex-col gap-2'>
<div className='flex justify-between'>
<div className='flex flex-col gap-2'>
<div className='flex flex-col gap-1'>
<BankLogo bank={bank} iconSize='30px' imageClassName='h-10 max-w-20' />
<span className='font-medium text-sm'>{transaction.bank_account}</span>
</div>
<div className='flex items-center gap-1'>
<Calendar size='16px' />
<span className='text-sm'>{formatDate(transaction.date, 'Do MMM YYYY')}</span>
</div>
</div>
<div className='flex flex-col gap-1'>
<div className={cn('flex items-center gap-1 text-end px-0 justify-end py-1 rounded-sm',
isWithdrawal ? 'text-ink-red-3' : 'text-ink-green-3'
)}>
{isWithdrawal ? <ArrowUpRight className="w-5 h-5 text-ink-red-3" /> : <ArrowDownRight className="w-5 h-5 text-ink-green-3" />}
<span className='text-sm font-semibold uppercase'>{isWithdrawal ? _('Spent') : _('Received')}</span>
</div>
<span className='font-semibold font-numeric text-lg text-end pe-0.5'>{formatCurrency(amount, currency)}</span>
{transaction.unallocated_amount && transaction.unallocated_amount !== amount ? <span className='text-ink-gray-5'>{_("Unallocated")}: {formatCurrency(transaction.unallocated_amount)}</span> : null}
</div>
</div>
<div className='flex flex-col gap-1'>
<span className='text-sm'>{transaction.description}</span>
{transaction.reference_number ? <span className='text-sm text-ink-gray-5'>{_("Ref")}: {transaction.reference_number}</span> : null}
{showAccount && account ? <span className='text-sm text-ink-gray-5'>{_("GL Account")}: {account}</span> : null}
</div>
</div>
</CardContent >
</Card >
)
}
export default SelectedTransactionDetails

View File

@@ -1,47 +0,0 @@
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
import _ from '@/lib/translate'
import { useAtomValue } from 'jotai'
import { bankRecSelectedTransactionAtom, selectedBankAccountAtom } from './bankRecAtoms'
import { formatDate } from '@/lib/date'
import { formatCurrency } from '@/lib/numbers'
import { ArrowDownRight, ArrowUpRight } from 'lucide-react'
const SelectedTransactionsTable = () => {
const selectedBankAccount = useAtomValue(selectedBankAccountAtom)
const transactions = useAtomValue(bankRecSelectedTransactionAtom(selectedBankAccount?.name ?? ''))
return (
<Table>
<TableHeader>
<TableRow>
<TableHead>
{_("Date")}
</TableHead>
<TableHead>
{_("Description")}
</TableHead>
<TableHead className="text-end">
{_("Amount")}
</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{transactions.map((transaction) => (
<TableRow key={transaction.name}>
<TableCell>{formatDate(transaction.date)}</TableCell>
<TableCell className="max-w-96 text-ellipsis overflow-hidden" title={transaction.description}>{transaction.description}</TableCell>
<TableCell className="text-end flex items-center justify-end gap-1">
{transaction.withdrawal && transaction.withdrawal > 0 ? <ArrowUpRight className="w-4 h-4 text-ink-red-3" /> : <ArrowDownRight className="w-4 h-4 text-ink-green-3" />}
<span className="font-numeric font-medium">
{formatCurrency(transaction.unallocated_amount, transaction.currency ?? '')}
</span>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)
}
export default SelectedTransactionsTable

View File

@@ -1,555 +0,0 @@
import { useAtom, useAtomValue, useSetAtom } from 'jotai'
import { bankRecSelectedTransactionAtom, bankRecTransferModalAtom, bankRecUnreconcileModalAtom, SelectedBank, selectedBankAccountAtom } from './bankRecAtoms'
import { Dialog, DialogContent, DialogHeader, DialogFooter, DialogClose, DialogTitle, DialogDescription } from '@/components/ui/dialog'
import _ from '@/lib/translate'
import { UnreconciledTransaction, useGetBankAccounts, useGetRuleForTransaction, useRefreshUnreconciledTransactions, useUpdateActionLog } from './utils'
import { Button } from '@/components/ui/button'
import SelectedTransactionDetails from './SelectedTransactionDetails'
import { PaymentEntry } from '@/types/Accounts/PaymentEntry'
import { useForm, useFormContext, useWatch } from 'react-hook-form'
import { FrappeConfig, FrappeContext, useFrappeGetCall, useFrappePostCall } from 'frappe-react-sdk'
import { toast } from 'sonner'
import ErrorBanner from '@/components/ui/error-banner'
import { H4 } from '@/components/ui/typography'
import { cn } from '@/lib/utils'
import { ArrowRight, Banknote, BadgeCheck, Calendar, ArrowUpRight, ArrowDownRight, CheckIcon, CheckCircle, ArrowLeft } from 'lucide-react'
import { Separator } from '@/components/ui/separator'
import { Form } from '@/components/ui/form'
import { AccountFormField, DataField, DateField, SmallTextField } from '@/components/ui/form-elements'
import SelectedTransactionsTable from './SelectedTransactionsTable'
import { useCurrentCompany } from '@/hooks/useCurrentCompany'
import { formatDate } from '@/lib/date'
import { useContext, useMemo, useState } from 'react'
import { formatCurrency } from '@/lib/numbers'
import { Label } from '@/components/ui/label'
import { FileDropzone } from '@/components/ui/file-dropzone'
import FileUploadBanner from '@/components/common/FileUploadBanner'
import { BankTransaction } from '@/types/Accounts/BankTransaction'
import { useHotkeys } from 'react-hotkeys-hook'
import { useDirection } from '@/components/ui/direction'
import BankLogo from '@/components/common/BankLogo'
const TransferModal = () => {
const [isOpen, setIsOpen] = useAtom(bankRecTransferModalAtom)
return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogContent className='min-w-7xl'>
<DialogHeader>
<DialogTitle>{_("Transfer")}</DialogTitle>
<DialogDescription>
{_("Record an internal transfer to another bank/credit card/cash account.")}
</DialogDescription>
</DialogHeader>
<TransferModalContent />
</DialogContent>
</Dialog>
)
}
const TransferModalContent = () => {
const selectedBankAccount = useAtomValue(selectedBankAccountAtom)
const selectedTransaction = useAtomValue(bankRecSelectedTransactionAtom(selectedBankAccount?.name ?? ''))
if (!selectedTransaction || !selectedBankAccount || selectedTransaction.length === 0) {
return <div className='p-4'>
<span className='text-center'>{_("No transaction selected")}</span>
</div>
}
if (selectedTransaction.length === 1) {
return <InternalTransferForm
selectedBankAccount={selectedBankAccount}
selectedTransaction={selectedTransaction[0]} />
}
return <BulkInternalTransferForm transactions={selectedTransaction} />
}
const BulkInternalTransferForm = ({ transactions }: { transactions: UnreconciledTransaction[] }) => {
const form = useForm<{
bank_account: string
}>()
const setIsOpen = useSetAtom(bankRecTransferModalAtom)
const { call: createPaymentEntry, loading, error } = useFrappePostCall<{ message: { transaction: BankTransaction, payment_entry: PaymentEntry }[] }>('erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.create_bulk_internal_transfer')
const onReconcile = useRefreshUnreconciledTransactions()
const addToActionLog = useUpdateActionLog()
const onSubmit = (data: { bank_account: string }) => {
createPaymentEntry({
bank_transaction_names: transactions.map((transaction) => transaction.name),
bank_account: data.bank_account
}).then(({ message }) => {
addToActionLog({
type: 'transfer',
timestamp: (new Date()).getTime(),
isBulk: true,
items: message.map((item) => ({
bankTransaction: item.transaction,
voucher: {
reference_doctype: "Payment Entry",
reference_name: item.payment_entry.name,
posting_date: item.payment_entry.posting_date,
doc: item.payment_entry,
}
})),
bulkCommonData: {
bank_account: data.bank_account,
}
})
toast.success(_("Transfer Recorded"), {
duration: 4000,
closeButton: true,
})
onReconcile(transactions[transactions.length - 1])
setIsOpen(false)
})
}
const onAccountChange = (account: string) => {
form.setValue('bank_account', account)
}
const selectedAccount = useWatch({ control: form.control, name: 'bank_account' })
const currentCompany = useCurrentCompany()
const company = transactions && transactions.length > 0 ? transactions[0].company : (currentCompany ?? '')
console.log("This is here", transactions)
return <Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<div className='flex flex-col gap-4'>
{error && <ErrorBanner error={error} />}
<SelectedTransactionsTable />
<BankOrCashPicker company={company} bankAccount={transactions[0]?.bank_account ?? ''} onAccountChange={onAccountChange} selectedAccount={selectedAccount} />
<DialogFooter>
<DialogClose asChild>
<Button size='md' variant={'outline'} disabled={loading}>{_("Cancel")}</Button>
</DialogClose>
<Button size='md' type='submit' disabled={loading}>{_("Transfer")}</Button>
</DialogFooter>
</div>
</form>
</Form>
}
interface InternalTransferFormFields extends PaymentEntry {
mirror_transaction_name?: string
}
const InternalTransferForm = ({ selectedBankAccount, selectedTransaction }: { selectedBankAccount: SelectedBank, selectedTransaction: UnreconciledTransaction }) => {
const setIsOpen = useSetAtom(bankRecTransferModalAtom)
const onClose = () => {
setIsOpen(false)
}
const { data: rule } = useGetRuleForTransaction(selectedTransaction)
const isWithdrawal = (selectedTransaction.withdrawal && selectedTransaction.withdrawal > 0) ? true : false
const form = useForm<InternalTransferFormFields>({
defaultValues: {
payment_type: 'Internal Transfer',
company: selectedTransaction?.company,
// If the transaction is a withdrawal, set the paid from to the selected bank account
paid_from: isWithdrawal ? selectedBankAccount.account : (rule?.account ?? ''),
// If the transaction is a deposit, set the paid to to the selected bank account
paid_to: !isWithdrawal ? selectedBankAccount.account : (rule?.account ?? ''),
// Set the amount to the amount of the selected transaction
paid_amount: selectedTransaction.unallocated_amount,
received_amount: selectedTransaction.unallocated_amount,
reference_date: selectedTransaction.date,
posting_date: selectedTransaction.date,
reference_no: (selectedTransaction.reference_number || selectedTransaction.description || '').slice(0, 140),
}
})
const onReconcile = useRefreshUnreconciledTransactions()
const { call: createPaymentEntry, loading, error, isCompleted } = useFrappePostCall<{ message: { transaction: BankTransaction, payment_entry: PaymentEntry } }>('erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.create_internal_transfer')
const setBankRecUnreconcileModalAtom = useSetAtom(bankRecUnreconcileModalAtom)
const addToActionLog = useUpdateActionLog()
const { file: frappeFile } = useContext(FrappeContext) as FrappeConfig
const [isUploading, setIsUploading] = useState(false)
const [uploadProgress, setUploadProgress] = useState(0)
const [files, setFiles] = useState<File[]>([])
const onSubmit = (data: InternalTransferFormFields) => {
createPaymentEntry({
bank_transaction_name: selectedTransaction.name,
...data,
custom_remarks: data.remarks ? true : false,
// Pass this to reconcile both at the same time
mirror_transaction_name: data.mirror_transaction_name
}).then(async ({ message }) => {
addToActionLog({
type: 'transfer',
timestamp: (new Date()).getTime(),
isBulk: false,
items: [
{
bankTransaction: message.transaction,
voucher: {
reference_doctype: "Payment Entry",
reference_name: message.payment_entry.name,
reference_no: message.payment_entry.reference_no,
reference_date: message.payment_entry.reference_date,
posting_date: message.payment_entry.posting_date,
doc: message.payment_entry,
}
}
]
})
toast.success(_("Transfer Recorded"), {
duration: 4000,
closeButton: true,
action: {
label: _("Undo"),
onClick: () => setBankRecUnreconcileModalAtom(selectedTransaction.name)
},
actionButtonStyle: {
backgroundColor: "rgb(0, 138, 46)"
}
})
if (files.length > 0) {
setIsUploading(true)
const uploadPromises = files.map(f => {
return frappeFile.uploadFile(f, {
isPrivate: true,
doctype: "Payment Entry",
docname: message.payment_entry.name,
}, (_bytesUploaded, _totalBytes, progress) => {
setUploadProgress((currentProgress) => {
//If there are multiple files, we need to add the progress to the current progress
return currentProgress + ((progress?.progress ?? 0) / files.length)
})
})
})
return Promise.all(uploadPromises).then(() => {
setUploadProgress(0)
setIsUploading(false)
})
} else {
return Promise.resolve()
}
}).then(() => {
setUploadProgress(0)
setIsUploading(false)
onReconcile(selectedTransaction)
onClose()
})
}
useHotkeys('meta+s', () => {
form.handleSubmit(onSubmit)()
}, {
enabled: true,
preventDefault: true,
enableOnFormTags: true
})
const onAccountChange = (account: string, is_mirror: boolean = false) => {
//If the transaction is a withdrawal, set the paid to to the selected account - since this is the account where the money is deposited into
if (selectedTransaction.withdrawal && selectedTransaction.withdrawal > 0) {
form.setValue('paid_to', account)
} else {
form.setValue('paid_from', account)
}
if (!is_mirror) {
// Reset the mirror transaction name
form.setValue('mirror_transaction_name', '')
}
}
const selectedAccount = useWatch({ control: form.control, name: (selectedTransaction.deposit && selectedTransaction.deposit > 0) ? 'paid_from' : 'paid_to' })
const direction = useDirection()
if (isUploading && isCompleted) {
return <FileUploadBanner uploadProgress={uploadProgress} />
}
return <Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<div className='flex flex-col gap-4'>
{error && <ErrorBanner error={error} />}
<div className='grid grid-cols-2 gap-4'>
<SelectedTransactionDetails transaction={selectedTransaction} />
<div className='flex flex-col gap-4'>
<div className='grid grid-cols-2 gap-4'>
<DateField
name='posting_date'
label={_("Posting Date")}
isRequired
inputProps={{ autoFocus: false }}
/>
<DateField
name='reference_date'
label={_("Reference Date")}
isRequired
inputProps={{ autoFocus: false }}
/>
</div>
<DataField name='reference_no' label={_("Reference")} isRequired inputProps={{ autoFocus: false }} />
</div>
</div>
<div className='flex flex-col gap-2'>
<H4 className='text-base'>{isWithdrawal ? _('Transferred to') : _('Transferred from')}</H4>
<RecommendedTransferAccount transaction={selectedTransaction} onAccountChange={onAccountChange} />
<BankOrCashPicker company={selectedTransaction.company ?? ''} bankAccount={selectedTransaction.bank_account ?? ''} onAccountChange={onAccountChange} selectedAccount={selectedAccount} />
</div>
<div className='flex flex-col gap-2 py-2'>
<div className='flex items-end justify-between gap-4'>
<div className='flex-1'>
<AccountFormField
name="paid_from"
label={_("Paid From")}
account_type={['Bank', 'Cash']}
readOnly={isWithdrawal}
filterFunction={(account) => account.name !== selectedBankAccount.account}
isRequired
/>
</div>
<div className='pb-2'>
{direction === 'ltr' ? <ArrowRight /> : <ArrowLeft />}
</div>
<div className='flex-1'>
<AccountFormField
name="paid_to"
label={_("Paid To")}
account_type={['Bank', 'Cash']}
isRequired
readOnly={!isWithdrawal}
filterFunction={(account) => account.name !== selectedBankAccount.account}
/>
</div>
</div>
</div>
<Separator />
<div className='flex flex-col gap-2'>
<div className='grid grid-cols-2 gap-4'>
<SmallTextField
name='remarks'
label={_("Custom Remarks")}
formDescription={_("This will be auto-populated if not set.")}
/>
<div
data-slot="form-item"
className="flex flex-col gap-2"
>
<Label>{_("Attachments")}</Label>
<FileDropzone files={files} setFiles={setFiles} />
</div>
</div>
</div>
<DialogFooter>
<DialogClose asChild>
<Button size='md' variant={'outline'} disabled={loading}>{_("Cancel")}</Button>
</DialogClose>
<Button size='md' type='submit' disabled={loading}>{_("Transfer")}</Button>
</DialogFooter>
</div>
</form>
</Form>
}
const BankOrCashPicker = ({ bankAccount, onAccountChange, selectedAccount, company }: { selectedAccount: string, bankAccount: string, onAccountChange: (account: string) => void, company: string }) => {
const { banks } = useGetBankAccounts(undefined, (bank) => bank.name !== bankAccount)
return <div className='grid grid-cols-4 gap-4'>
{banks.map((bank) => (
<div
className={cn('border p-2 rounded-md flex items-center gap-2 cursor-pointer outline-[0.5px] transition-all duration-200 hover:bg-surface-gray-1 dark:hover:bg-surface-gray-3',
selectedAccount === bank.account ? 'border-outline-gray-5 outline-outline-gray-5 bg-surface-gray-1 dark:bg-surface-gray-3' : 'border-outline-gray-2 outline-outline-gray-2'
)}
role='button'
key={bank.account}
onClick={() => onAccountChange(bank.account ?? '')}
>
<BankLogo bank={bank} iconSize='24px' imageClassName='w-12 h-12' />
<div className='flex flex-col gap-1'>
<span className='font-semibold text-sm'>{bank.account_name} {bank.bank_account_no && <span className='text-xs text-ink-gray-5'>({bank.bank_account_no})</span>}</span>
<span className='text-xs text-ink-gray-5'>{bank.account}</span>
</div>
</div>
))}
<CashPicker company={company ?? ''} selectedAccount={selectedAccount} setSelectedAccount={onAccountChange} />
</div>
}
const CashPicker = ({ company, selectedAccount, setSelectedAccount }: { company: string, selectedAccount: string, setSelectedAccount: (account: string) => void }) => {
const { data } = useFrappeGetCall('frappe.client.get_value', {
doctype: 'Company',
filters: company,
fieldname: 'default_cash_account'
}, undefined, {
revalidateOnFocus: false,
revalidateIfStale: false,
})
const account = data?.message?.default_cash_account
if (account) {
return <div className={cn('border p-2 rounded-md flex items-center gap-2 cursor-pointer outline-[0.5px] transition-all duration-200 hover:bg-surface-gray-1 dark:hover:bg-surface-gray-3',
selectedAccount === account ? 'border-outline-gray-5 outline-outline-gray-5 bg-surface-gray-1 dark:bg-surface-gray-3' : 'border-outline-gray-2 outline-outline-gray-2'
)}
role='button'
onClick={() => setSelectedAccount(account ?? '')}
>
<div className='flex items-center justify-center h-10 w-10'>
<Banknote size='24px' />
</div>
<div className='flex flex-col gap-1'>
<span className='font-semibold text-sm'>Cash</span>
<span className='text-xs text-ink-gray-5'>{data?.message?.default_cash_account}</span>
</div>
</div>
}
return null
}
const RecommendedTransferAccount = ({ transaction, onAccountChange }: { transaction: UnreconciledTransaction, onAccountChange: (account: string, is_mirror: boolean) => void }) => {
const { setValue, watch } = useFormContext<InternalTransferFormFields>()
const mirrorTransactionName = watch('mirror_transaction_name')
const paid_from = watch('paid_from')
const paid_to = watch('paid_to')
const { data } = useFrappeGetCall('erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.search_for_transfer_transaction', {
transaction_id: transaction.name
}, undefined, {
revalidateOnFocus: false,
revalidateIfStale: false,
})
// Get bank accounts to find the logo
const { banks } = useGetBankAccounts()
const bank = useMemo(() => {
if (data?.message?.bank_account && banks) {
return banks.find(bank => bank.name === data.message.bank_account)
}
return null
}, [data?.message?.bank_account, banks])
const selectTransaction = () => {
if (data?.message) {
setValue('mirror_transaction_name', data.message.name)
onAccountChange(data.message.account, true)
}
}
if (data?.message) {
const isWithdrawal = data.message.withdrawal && data.message.withdrawal > 0
const amount = isWithdrawal ? data.message.withdrawal : data.message.deposit
const currency = data.message.currency
const isAccountSelected = isWithdrawal ? paid_from === data.message.account : paid_to === data.message.account
const isSuggested = mirrorTransactionName === data?.message?.name && isAccountSelected
return (<div className='pb-2'>
<div className={cn("flex justify-between items-start gap-3 p-3 border rounded-lg shadow-sm",
isSuggested ? "border-outline-green-4 bg-surface-green-1" : "border-outline-violet-2 bg-surface-violet-2/50")}>
<div>
<div className='flex flex-col gap-3'>
<div className={cn("flex items-center gap-2 shrink-0",
isSuggested ? "text-ink-green-4" : "text-ink-violet-4"
)}>
<BadgeCheck className="w-4 h-4" />
<span className="text-sm font-medium">{_("Suggested Transfer to {0}", [data.message.account])}</span>
</div>
<div className='flex flex-col gap-1'>
<span className='text-p-sm'>{_("The system found a mirror transaction ({0}) in another account with the same amount and date.", [data.message.name])}</span>
<span className='text-p-sm'>{_("Accepting the suggestion will reconcile both transactions.")}</span>
</div>
<div className='flex flex-col gap-1.5'>
<div className='flex items-center gap-1'>
<Calendar size='16px' />
<span className='text-sm'>{formatDate(data.message.date, 'Do MMM YYYY')}</span>
</div>
<span className='text-sm line-clamp-1' title={data.message.description}>{data.message.description}</span>
</div>
</div>
</div>
<div className='flex flex-col items-end justify-between gap-2 h-full w-[30%]'>
<div className="flex items-center gap-2">
<BankLogo bank={bank} iconSize='24px' imageClassName='h-8 max-w-24' iconClassName={cn(isSuggested ? "text-ink-green-3" : "text-purple-600")} />
</div>
<div className='flex gap-1'>
<div className={cn('flex items-center gap-1 text-end px-0 justify-end py-1 rounded-sm',
isWithdrawal ? 'text-ink-red-3' : 'text-ink-green-3'
)}>
{isWithdrawal ? <ArrowUpRight className="w-5 h-5 text-ink-red-3" /> : <ArrowDownRight className="w-5 h-5 text-ink-green-3" />}
<span className='text-sm font-semibold uppercase'>{isWithdrawal ? _('Transferred Out') : _('Received')}</span>
</div>
</div>
<span className='font-semibold font-numeric text-lg text-end pe-0.5'>{formatCurrency(amount, currency)}</span>
<div className='pt-1'>
<Button
onClick={selectTransaction}
theme={isSuggested ? "green" : "violet"}
size="md"
type='button'
>
{isSuggested ? <CheckCircle /> : <CheckIcon />}
{isSuggested ? _("Accepted") : _("Use Suggestion")}
</Button>
</div>
</div>
</div>
</div>
)
}
return null
}
export default TransferModal

View File

@@ -1,85 +0,0 @@
import { BankAccount } from "@/types/Accounts/BankAccount";
import { getDatesForTimePeriod } from "@/lib/date";
import { atom } from "jotai";
import { atomWithStorage, createJSONStorage } from "jotai/utils";
import { atomFamily } from 'jotai-family'
import { UnreconciledTransaction } from "./utils";
import { BankTransaction } from "@/types/Accounts/BankTransaction";
import { PaymentEntry } from "@/types/Accounts/PaymentEntry";
import { JournalEntry } from "@/types/Accounts/JournalEntry";
export interface SelectedBank extends Pick<BankAccount, 'name' | 'bank' | 'is_credit_card' | 'company' | 'account_name' | 'bank_account_no' | 'account' | 'account_type' | 'integration_id' | 'is_default' | 'last_integration_date'> {
logo?: string,
logoDark?: string,
darkModeInvert?: boolean,
logoClassName?: string,
account_currency?: string
}
export const selectedBankAccountAtom = atomWithStorage<SelectedBank | null>('bank-rec-selected-bank', null, undefined, {
getOnInit: true
})
export const bankRecDateAtom = atomWithStorage<{ fromDate: string, toDate: string }>("bank-rec-date", {
fromDate: getDatesForTimePeriod('This Month').fromDate,
toDate: getDatesForTimePeriod('This Month').toDate
})
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const bankRecClosingBalanceAtom = atomFamily((_id: string) => {
return atom<{ value: number, stringValue: string | number | undefined }>({
value: 0,
stringValue: '0.00'
})
})
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const bankRecSelectedTransactionAtom = atomFamily((_id: string) => {
return atom<UnreconciledTransaction[]>([])
})
/** Action Modals */
export const bankRecTransferModalAtom = atom(false)
export const bankRecRecordPaymentModalAtom = atom(false)
export const bankRecRecordJournalEntryModalAtom = atom(false)
export const bankRecUnreconcileModalAtom = atom<string>('')
export const bankRecMatchFilters = atomWithStorage<string[]>('bank-rec-match-filters', ['payment_entry', 'journal_entry'])
export const bankRecSearchText = atom<string>('')
export const bankRecAmountFilter = atom<{ value: number, stringValue?: string | number }>({
value: 0,
stringValue: '0.00'
})
export const bankRecTransactionTypeFilter = atom<string>('All')
export interface ActionLog {
type: 'match' | 'payment' | 'transfer' | 'bank_entry'
isBulk: boolean
timestamp: number,
items: ActionLogItem[],
bulkCommonData?: {
party_type?: string,
party?: string,
account?: string,
bank_account?: string,
}
}
export interface ActionLogItem {
bankTransaction: BankTransaction,
voucher: {
reference_doctype: string,
reference_name: string,
reference_no?: string,
reference_date?: string,
posting_date: string,
doc?: PaymentEntry | JournalEntry
},
}
const actionLogStorage = createJSONStorage<ActionLog[]>(() => sessionStorage)
export const bankRecActionLog = atomWithStorage<ActionLog[]>('bank-rec-action-log', [], actionLogStorage, {
getOnInit: true,
})

View File

@@ -1,410 +0,0 @@
export const BANK_LOGOS: { keywords: string[], logo: string, locale?: string[], logoDark?: string, darkModeInvert?: boolean, logoClassName?: string }[] = [
// United States + International
{
keywords: ['American Express', 'Amex'],
logo: 'Amex.svg',
locale: ['Global', 'United States']
},
{
keywords: ['Bank of America', 'BOA'],
logo: 'Bank_of_America.png',
darkModeInvert: true,
locale: ['United States']
},
{
keywords: ['Barclays'],
logo: 'Barclays.svg',
locale: ['Global', 'United Kingdom'],
logoClassName: 'h-12',
},
{
keywords: ['BNP Paribas'],
logo: 'BNP_Paribas.svg',
logoDark: 'BNP_Paribas-Dark.svg',
locale: ['Global', 'France'],
logoClassName: 'max-w-24'
},
{
keywords: ['Bank of New York Mellon', 'BNY Mellon', 'BNY'],
logo: 'BNY_Mellon.svg',
locale: ['Global', 'United States'],
logoDark: 'BNY_Mellon-Dark.svg',
},
{
keywords: ['Capital One'],
logo: 'Capital_One.png',
locale: ['United States'],
darkModeInvert: true
},
{
keywords: ['Charles Schwab', 'Schwab'],
logo: 'Charles_Schwab.svg',
locale: ['United States'],
logoClassName: 'h-7'
},
{
keywords: ['Chase'],
logo: 'chase.svg',
locale: ['Global', 'United States'],
logoDark: 'chase-Dark.svg',
},
{
keywords: ['Citi', 'Citibank', 'Citi Group', 'Citi Financial Services'],
logo: 'Citi.svg',
locale: ['Global', 'United States']
},
{
keywords: ['Deutsche Bank'],
logo: 'Deutsche_Bank.svg',
locale: ['Global', 'Germany'],
darkModeInvert: true,
},
{
keywords: ['Goldman Sachs'],
logo: 'Goldman_Sachs.svg',
locale: ['Global', 'United States'],
darkModeInvert: true,
},
{
keywords: ['HSBC'],
logo: 'HSBC.svg',
locale: ['Global', 'United Kingdom'],
logoDark: 'HSBC-dark.svg',
},
{
keywords: ['JPMorgan Chase', 'JPMorgan', 'JP Morgan', 'JP Morgan Chase', 'JPMorgan Chase & Co', 'JPM', 'JPMC'],
logo: 'jpmc.svg',
locale: ['Global', 'United States'],
darkModeInvert: true,
},
{
keywords: ['Morgan Stanley'],
logo: 'Morgan_Stanley.png',
locale: ['Global', 'United States'],
darkModeInvert: true,
},
{
keywords: ['PNC', 'PNC Financial Services Group', 'PNC Financial Services', 'Pittsburgh National Corporation'],
logo: 'PNC.png',
locale: ['United States']
},
{
keywords: ['Santander'],
logo: 'Santander.svg',
locale: ['Global']
},
{
keywords: ['TD Bank', 'Toronto Dominion Bank'],
logo: 'Toronto_Dominion_Bank.png',
locale: ['Canada']
},
{
keywords: ['Truist'],
logo: 'Truist.svg',
locale: ['United States'],
darkModeInvert: true,
logoClassName: 'h-8'
},
{
keywords: ['UBS'],
logo: 'UBS.svg',
locale: ['Global', 'Switzerland'],
logoDark: 'UBS-dark.svg',
},
{
keywords: ['US Bank', 'USBank', 'U.S. Bank', 'U.S. Bancorp'],
logo: 'USBank.svg',
locale: ['United States'],
logoDark: 'USBank-dark.svg',
},
{
keywords: ['Wells Fargo', 'Wells Fargo'],
logo: 'Wells_Fargo.svg',
locale: ['United States'],
logoClassName: 'h-7'
},
{
keywords: ['OakStar', 'Oakstar', 'Oakstar'],
logo: 'Oakstar.png',
logoDark: 'Oakstar-dark.webp',
locale: ['United States'],
logoClassName: 'h-7'
},
{
keywords: ['PlainsCapital', 'Plains Capital'],
logo: 'PlainsCapitalBank.png',
locale: ['United States'],
logoClassName: 'h-7'
},
{
keywords: ["Standard Chartered"],
logo: 'Standard_Chartered.png',
logoDark: 'Standard_Chartered-dark.png',
locale: ['Global'],
},
// India
{
keywords: ['HDFC Bank', 'HDFC'],
logo: 'HDFC.svg',
locale: ['India'],
},
{
keywords: ['ICICI Bank', 'ICICI'],
logo: 'ICICI.svg',
logoDark: 'ICICI-dark.svg',
locale: ['India'],
},
{
keywords: ['SBI', 'State Bank of India'],
logo: 'State_Bank_of_India.svg',
logoDark: 'State_bank_of_India-Dark.svg',
locale: ['India'],
logoClassName: 'h-4.5'
},
{
keywords: ['Punjab National Bank', 'PNB'],
logo: 'Punjab_National_Bank.svg',
locale: ['India']
},
{
keywords: ['Union Bank of India', 'Union Bank'],
logo: 'Union_Bank_of_India.svg',
locale: ['India']
},
{
keywords: ['Yes Bank', 'Yes'],
logo: 'Yes_Bank.svg',
locale: ['India'],
logoDark: 'Yes_Bank-dark.svg',
},
{
keywords: ['RBL Bank', 'RBL'],
logo: 'RBL_Bank.svg',
locale: ['India'],
logoDark: 'RBL_Bank-dark.svg',
},
{
keywords: ['Axis Bank', 'Axis'],
logo: 'Axis_Bank.svg',
locale: ['India'],
darkModeInvert: true
},
{
keywords: ['Bank of Baroda', 'BOB'],
logo: 'Bank_of_Baroda.svg',
locale: ['India', 'Kenya'],
logoClassName: 'h-7'
},
{
keywords: ['Bank of India', 'BOI'],
logo: 'Bank_of_India.png',
locale: ['India'],
logoClassName: 'h-7'
},
{
keywords: ['Bank of Maharashtra', 'BOM'],
logo: 'Bank_of_Maharashtra.png',
locale: ['India'],
logoClassName: 'min-w-24'
},
{
keywords: ['Kotak Mahindra Bank', 'Kotak'],
logo: 'Kotak_Mahindra.svg',
locale: ['India']
},
{
keywords: ['IndusInd Bank', 'IndusInd'],
logo: 'IndusInd_Bank.svg',
locale: ['India'],
darkModeInvert: true,
},
{
keywords: ['IDBI Bank', 'IDBI'],
logo: 'IDBI_Bank.svg',
locale: ['India']
},
{
keywords: ['IDFC First Bank', 'IDFC First'],
logo: 'IDFC_First_Bank.svg',
locale: ['India']
},
{
keywords: ['Federal Bank'],
logo: 'Federal_Bank.png',
logoDark: 'Federal_Bank-dark.png',
locale: ['India']
},
{
keywords: ['Fi Bank'],
logo: 'Fi_Bank.svg',
locale: ['India']
},
{
keywords: ['RazorpayX', 'Razorpay'],
logo: 'Razorpay.svg',
logoDark: 'Razorpay-dark.svg',
locale: ['India']
},
{
keywords: ['Revolut'],
logo: 'Revolut.png',
locale: ['Global'],
darkModeInvert: true
},
{
keywords: ['Starling Bank'],
logo: 'Starling_Bank.png',
logoDark: 'Starling_Bank-dark.png',
locale: ['Global', 'UK'],
logoClassName: 'h-10'
},
// Australia and New Zealand
{
keywords: ["Commonwealth Bank", "CBA"],
logo: "Commonwealth_Bank.svg",
locale: ['Australia', 'New Zealand'],
},
{
keywords: ["Airwallex"],
logo: "Airwallex.png",
logoDark: "Airwallex-dark.png",
locale: ['Global']
},
{
keywords: ["Judo Bank"],
logo: "Judo_Bank.svg",
logoDark: "Judo_Bank-dark.svg",
locale: ['Australia', 'New Zealand']
},
{
keywords: ["Alpha"], // This might conflict with Alpha Bank in Greece
logo: "Alpha_Bank.svg",
darkModeInvert: true,
logoClassName: 'h-4.5',
locale: ['Australia', 'New Zealand']
},
{
keywords: ["Australian Tax Office", "Australian Taxation Office"],
logo: "Australian_Tax_Office.png",
darkModeInvert: true,
locale: ['Australia']
},
{
keywords: ["Westpac"],
logo: "Westpac.svg",
locale: ['Australia']
},
{
keywords: ["ANZ", "ANZ Bank", "Australia and New Zealand Banking Group"],
logo: "ANZ.png",
locale: ['Australia', 'New Zealand']
},
{
keywords: ["Macquarie Group", "Macquarie Bank"],
logo: "Macquarie.svg",
darkModeInvert: true,
locale: ['Australia']
},
// Nicaragua
{
keywords: ["Banco Atlantida", "Banco Atlántida"],
logo: "Banco_Atlantida.png",
locale: ['Nicaragua']
},
{
keywords: ["Banco de Finanzas"],
logo: "Banco_de_Finanzas.svg",
locale: ['Nicaragua'],
logoClassName: 'h-4.5'
},
{
keywords: ["Avanz"],
logo: "Avanz.svg",
logoDark: "Avanz-dark.svg",
locale: ['Nicaragua'],
logoClassName: 'h-7'
},
{
keywords: ["Ficohsa"],
logo: "Ficohsa.svg",
locale: ['Nicaragua']
},
{
keywords: ["BAC", "BAC Credomatic"],
logo: "BAC_Credomatic.svg",
locale: ['Nicaragua'],
logoClassName: 'h-4.5'
},
{
keywords: ["Banco Lafise"],
logo: "Banco_Lafise.png",
darkModeInvert: true,
locale: ['Nicaragua']
},
// German
{
keywords: ["Sparkasse"],
logo: "Sparkasse.png",
locale: ['Germany']
},
{
keywords: ["Volksbank", "Raiffeisenbank", "VR-Bank"],
logo: "Volksbanken_Raiffeisenbanken.svg",
locale: ['Germany'],
logoClassName: 'min-w-32'
},
// Kenya
{
keywords: ["KCB Bank", "KCB"],
logo: "KCB_Bank_Kenya.png",
locale: ['Kenya']
},
{
keywords: ["Equity Bank"],
logo: "Equity_Bank.png",
logoDark: "Equity_Bank-dark.png",
locale: ['Kenya'],
},
{
keywords: ["I&M"],
logo: "I&M.png",
locale: ['Kenya']
},
{
keywords: ["ABSA"],
logo: "ABSA.png",
locale: ['Kenya'],
darkModeInvert: true,
logoClassName: 'h-7'
},
{
keywords: ["Stanbic"],
logo: "Stanbic.png",
locale: ['Kenya'],
logoClassName: 'h-7'
},
{
keywords: ["DTB", "Diamond Trust Bank"],
logo: "Diamond_Trust_Bank.png",
locale: ['Kenya']
},
{
keywords: ["Prime Bank"],
logo: "Prime_Bank.png",
locale: ['Kenya'],
logoClassName: 'max-w-28'
},
{
keywords: ["Stripe"],
logo: "Stripe.svg",
locale: ['Global'],
logoClassName: 'h-6',
darkModeInvert: true,
},
{
keywords: ["PayPal"],
logo: "PayPal.png",
locale: ['Global'],
logoClassName: 'h-6',
}
]

View File

@@ -1,457 +0,0 @@
import { ActionLog, bankRecActionLog, bankRecAmountFilter, bankRecDateAtom, bankRecMatchFilters, bankRecSearchText, bankRecSelectedTransactionAtom, bankRecTransactionTypeFilter, bankRecUnreconcileModalAtom, SelectedBank, selectedBankAccountAtom } from './bankRecAtoms'
import { useAtom, useAtomValue, useSetAtom } from 'jotai'
import { useMemo } from 'react'
import { SWRConfiguration, useFrappeGetCall, useFrappeGetDoc, useFrappePostCall, useSWRConfig } from 'frappe-react-sdk'
import { BankTransaction } from '@/types/Accounts/BankTransaction'
import { BankAccount } from '@/types/Accounts/BankAccount'
import dayjs from 'dayjs'
import { toast } from 'sonner'
import { BANK_LOGOS } from './logos'
import { getErrorMessage } from '@/lib/frappe'
import { useCurrentCompany } from '@/hooks/useCurrentCompany'
import _ from '@/lib/translate'
import { BankTransactionRule } from '@/types/Accounts/BankTransactionRule'
import { useRef } from 'react'
import type { DebouncedState } from 'usehooks-ts'
import { useDebounceCallback } from 'usehooks-ts'
import Fuse from 'fuse.js'
export const useGetAccountOpeningBalance = () => {
const companyID = useCurrentCompany()
const bankAccount = useAtomValue(selectedBankAccountAtom)
const dates = useAtomValue(bankRecDateAtom)
const args = useMemo(() => {
return {
bank_account: bankAccount?.name,
company: companyID,
till_date: dayjs(dates.fromDate).subtract(1, 'days').format('YYYY-MM-DD'),
}
}, [companyID, bankAccount?.name, dates.fromDate])
return useFrappeGetCall('erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_account_balance', args, undefined, {
revalidateOnFocus: false
})
}
export const useGetAccountClosingBalance = () => {
const companyID = useCurrentCompany()
const bankAccount = useAtomValue(selectedBankAccountAtom)
const dates = useAtomValue(bankRecDateAtom)
const args = useMemo(() => {
return {
bank_account: bankAccount?.name,
company: companyID,
till_date: dates.toDate,
}
}, [companyID, bankAccount?.name, dates.toDate])
return useFrappeGetCall('erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_account_balance', args,
`bank-reconciliation-account-closing-balance-${bankAccount?.name}-${dates.toDate}`,
{
revalidateOnFocus: false
}
)
}
/**
* Hook to fetch the closing balance set in the database for the given bank and date
*/
export const useGetAccountClosingBalanceAsPerStatement = (swrConfig: SWRConfiguration = {}) => {
const dates = useAtomValue(bankRecDateAtom)
const bankAccount = useAtomValue(selectedBankAccountAtom)
return useFrappeGetCall<{ message: { balance: number, date?: string } }>("erpnext.accounts.doctype.bank_account.bank_account.get_closing_balance_as_per_statement", {
bank_account: bankAccount?.name,
date: dates.toDate
}, `bank-reconciliation-account-closing-balance-as-per-statement-${bankAccount?.name}-${dates.toDate}`, {
revalidateOnFocus: false,
...swrConfig
})
}
export type UnreconciledTransaction = Pick<BankTransaction, 'name' | 'matched_transaction_rule' | 'date' | 'withdrawal' | 'deposit' | 'currency' | 'description' | 'status' | 'transaction_type' | 'reference_number' | 'party_type' | 'party' | 'bank_account' | 'company' | 'unallocated_amount'>
export const useGetUnreconciledTransactions = () => {
const bankAccount = useAtomValue(selectedBankAccountAtom)
const dates = useAtomValue(bankRecDateAtom)
return useFrappeGetCall<{ message: UnreconciledTransaction[] }>('erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_bank_transactions', {
bank_account: bankAccount?.name,
from_date: dates.fromDate,
to_date: dates.toDate
}, bankAccount ? `bank-reconciliation-unreconciled-transactions-${bankAccount?.name}-${dates.fromDate}-${dates.toDate}` : null, {
revalidateOnFocus: false,
revalidateIfStale: false
})
}
export interface LinkedPayment {
rank: number,
doctype: string,
name: string,
paid_amount: number,
reference_no: string,
reference_date: string,
posting_date: string,
party_type?: string,
party?: string,
currency: string
}
export const useGetBankTransactions = () => {
const bankAccount = useAtomValue(selectedBankAccountAtom)
const dates = useAtomValue(bankRecDateAtom)
return useFrappeGetCall<{ message: BankTransaction[] }>('erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_bank_transactions', {
bank_account: bankAccount?.name,
from_date: dates.fromDate,
to_date: dates.toDate,
all_transactions: true
}, bankAccount ? `bank-reconciliation-bank-transactions-${bankAccount?.name}-${dates.fromDate}-${dates.toDate}` : null)
}
export const useGetVouchersForTransaction = (transaction: UnreconciledTransaction) => {
const dates = useAtomValue(bankRecDateAtom)
const matchFilters = useAtomValue(bankRecMatchFilters)
return useFrappeGetCall<{ message: LinkedPayment[] }>('erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_linked_payments', {
bank_transaction_name: transaction.name,
document_types: matchFilters ?? ['payment_entry', 'journal_entry'],
from_date: dates.fromDate,
to_date: dates.toDate,
filter_by_reference_date: 0
}, `bank-reconciliation-vouchers-${transaction.name}-${dates.fromDate}-${dates.toDate}-${matchFilters.join(',')}`, {
revalidateOnFocus: false
})
}
/**
* Common hook to refresh the unreconciled transactions list after a transaction is reconciled
* @returns function to call to refresh the unreconciled transactions list AFTER the operation is done
*/
export const useRefreshUnreconciledTransactions = () => {
const selectedBank = useAtomValue(selectedBankAccountAtom)
const dates = useAtomValue(bankRecDateAtom)
const matchFilters = useAtomValue(bankRecMatchFilters)
const setSelectedTransaction = useSetAtom(bankRecSelectedTransactionAtom(selectedBank?.name || ''))
const { mutate } = useSWRConfig()
const searchString = useAtomValue(bankRecSearchText)
const typeFilter = useAtomValue(bankRecTransactionTypeFilter)
const amountFilter = useAtomValue(bankRecAmountFilter)
const { data: unreconciledTransactions } = useGetUnreconciledTransactions()
/**
* This function should be called after a transaction is reconciled
* It will get the next unreconciled transaction and select it
* And then refresh the balance + unreconciled transactions list
*/
const onReconcileTransaction = (transaction: UnreconciledTransaction, updatedTransaction?: BankTransaction) => {
// If the updated transaction has an unallocated amount of 0, then we need to select the next unreconciled transaction
if (updatedTransaction && updatedTransaction?.unallocated_amount !== 0) {
mutate(`bank-reconciliation-unreconciled-transactions-${selectedBank?.name}-${dates.fromDate}-${dates.toDate}`)
mutate(`bank-reconciliation-account-closing-balance-${selectedBank?.name}-${dates.toDate}`)
// Update the matching vouchers for the selected transaction
mutate(`bank-reconciliation-vouchers-${transaction.name}-${dates.fromDate}-${dates.toDate}-${matchFilters.join(',')}`)
return
}
// From unreconciled transactions list, first apply the filters based on the search criteria and other filters
const searchIndex = unreconciledTransactions ? new Fuse(unreconciledTransactions.message, {
keys: ['description', 'reference_number'],
threshold: 0.5,
includeScore: true
}) : null
const results = getSearchResults(searchIndex, searchString, typeFilter, amountFilter.value, unreconciledTransactions?.message)
const currentIndex = results.findIndex(t => t.name === transaction.name)
let nextTransaction = null
if (currentIndex !== -1) {
// Check if there is a next transaction
if (currentIndex < (results.length || 0) - 1) {
nextTransaction = results[currentIndex + 1]
}
}
// We need to select the next unreconciled transaction for a better UX
mutate(`bank-reconciliation-unreconciled-transactions-${selectedBank?.name}-${dates.fromDate}-${dates.toDate}`)
.then(res => {
if (nextTransaction) {
// Check if next transaction is there in the response
const nextTransactionObj = res?.message.find((t: UnreconciledTransaction) => t.name === nextTransaction.name)
if (nextTransactionObj) {
setSelectedTransaction([nextTransactionObj])
} else {
// If the next transaction is not there in the response, we need to clear the selection
setSelectedTransaction([])
}
} else {
// If there is no next transaction, we need to clear the selection
setSelectedTransaction([])
}
})
mutate(`bank-reconciliation-account-closing-balance-${selectedBank?.name}-${dates.toDate}`)
}
return onReconcileTransaction
}
export const useReconcileTransaction = () => {
const { call, loading } = useFrappePostCall<{ message: BankTransaction }>('erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.reconcile_vouchers')
const onReconcileTransaction = useRefreshUnreconciledTransactions()
const setBankRecUnreconcileModalAtom = useSetAtom(bankRecUnreconcileModalAtom)
const addToActionLog = useUpdateActionLog()
const reconcileTransaction = (transaction: UnreconciledTransaction, voucher: LinkedPayment) => {
call({
bank_transaction_name: transaction.name,
vouchers: JSON.stringify([{
"payment_doctype": voucher.doctype,
"payment_name": voucher.name,
"amount": voucher.paid_amount
}])
}).then((res) => {
addToActionLog({
type: 'match',
timestamp: (new Date()).getTime(),
isBulk: false,
items: [
{
bankTransaction: res.message,
voucher: {
reference_doctype: voucher.doctype,
reference_name: voucher.name,
reference_no: voucher.reference_no,
reference_date: voucher.reference_date,
posting_date: voucher.posting_date,
}
}
]
})
onReconcileTransaction(transaction, res.message)
toast.success(_("Reconciled"), {
duration: 4000,
closeButton: true,
action: {
label: _("Undo"),
onClick: () => setBankRecUnreconcileModalAtom(transaction.name)
},
actionButtonStyle: {
backgroundColor: "rgb(0, 138, 46)"
}
})
}).catch((error) => {
console.error(error)
toast.error(_("Error"), {
duration: 5000,
description: getErrorMessage(error)
})
})
}
return { reconcileTransaction, loading }
}
interface BankAccountWithCurrency extends Pick<BankAccount, 'name' | 'bank' | 'account_name' | 'is_credit_card' | 'company' | 'account' | 'account_type' | 'account_subtype' | 'bank_account_no' | 'last_integration_date'> {
account_currency?: string
}
type BankLogoEntry = (typeof BANK_LOGOS)[number]
/** Prefer the longest keyword match so short tokens (e.g. "anz" in "finanzas") do not beat full bank names. */
function findBankLogoForName(bankName: string | undefined | null): BankLogoEntry | undefined {
if (!bankName) return undefined
const haystack = bankName.toLowerCase()
let best: BankLogoEntry | undefined
let bestKeywordLen = 0
for (const entry of BANK_LOGOS) {
for (const keyword of entry.keywords) {
const needle = keyword.toLowerCase()
if (needle.length === 0) continue
if (haystack.includes(needle) && needle.length > bestKeywordLen) {
bestKeywordLen = needle.length
best = entry
}
}
}
return best
}
export const useGetBankAccounts = (onSuccess?: (data?: Omit<SelectedBank, 'logo'>[]) => void, filterFn?: (bank: SelectedBank) => boolean) => {
const company = useCurrentCompany()
const { data, isLoading, error } = useFrappeGetCall<{ message: BankAccountWithCurrency[] }>('erpnext.accounts.doctype.bank_account.bank_account.get_list', {
company: company
}, undefined, {
revalidateOnFocus: false,
revalidateIfStale: false,
onSuccess: (data) => {
onSuccess?.(data?.message)
}
})
const banks = useMemo(() => {
// Match the bank account to the logo
const banksWithLogos = data?.message.map((bank) => {
const logo = findBankLogoForName(bank.bank)
return {
...bank,
logo: logo?.logo,
logoDark: logo?.logoDark,
darkModeInvert: logo?.darkModeInvert,
logoClassName: logo?.logoClassName
}
}) ?? []
if (filterFn) {
return banksWithLogos.filter(filterFn)
}
return banksWithLogos
}, [data, filterFn])
return {
banks,
isLoading,
error
}
}
export const useIsTransactionWithdrawal = (transaction: UnreconciledTransaction) => {
return useMemo(() => {
const isWithdrawal = transaction.withdrawal && transaction.withdrawal > 0
const isDeposit = transaction.deposit && transaction.deposit > 0
return {
amount: isWithdrawal ? transaction.withdrawal : transaction.deposit,
isWithdrawal,
isDeposit
}
}, [transaction])
}
export const useGetRuleForTransaction = (transaction: UnreconciledTransaction) => {
return useFrappeGetDoc<BankTransactionRule>('Bank Transaction Rule', transaction.matched_transaction_rule,
transaction.matched_transaction_rule ? undefined : null, {
revalidateOnFocus: false,
revalidateIfStale: false
}
)
}
/** Hook to handle the search input while maintaining debouncing and global state. */
export function useTransactionSearch(): [string, DebouncedState<(value: string) => void>] {
const delay = 500
const unwrappedInitialValue = ''
const eq = (left: string, right: string) => left === right
const [debouncedValue, setDebouncedValue] = useAtom(bankRecSearchText)
const previousValueRef = useRef<string | undefined>(unwrappedInitialValue)
const updateDebouncedValue = useDebounceCallback(
setDebouncedValue,
delay,
)
// Update the debounced value if the initial value changes
if (!eq(previousValueRef.current as string, unwrappedInitialValue)) {
updateDebouncedValue(unwrappedInitialValue)
previousValueRef.current = unwrappedInitialValue
}
return [debouncedValue, updateDebouncedValue]
}
/** Utility function to get the search results based on the search index, search string, type filter, amount filter and unreconciled transactions */
export const getSearchResults = (
/** Fuse index of the unreconciled transactions */
searchIndex: Fuse<UnreconciledTransaction> | null,
/** Search string */
search: string,
/** Type filter */
typeFilter: string,
/** Amount filter */
amountFilter: number,
/** Unreconciled transactions */
unreconciledTransactions?: UnreconciledTransaction[]) => {
let r = []
if (!searchIndex || !search) {
r = unreconciledTransactions ?? []
} else {
r = searchIndex.search(search).map((result) => result.item)
}
if (typeFilter !== 'All') {
r = r.filter((transaction) => {
if (typeFilter === 'Debits') {
return transaction.withdrawal && transaction.withdrawal > 0
}
if (typeFilter === 'Credits') {
return transaction.deposit && transaction.deposit > 0
}
})
}
if (amountFilter > 0) {
r = r.filter((transaction) => {
if (transaction.withdrawal && transaction.withdrawal > 0) {
return transaction.withdrawal === amountFilter
}
if (transaction.deposit && transaction.deposit > 0) {
return transaction.deposit === amountFilter
}
return false
})
}
return r
}
export const useUpdateActionLog = () => {
const setActionLog = useSetAtom(bankRecActionLog)
const addToActionLog = (action: ActionLog) => {
// Store at max 100 actions
setActionLog((prev) => {
const newActions = [action, ...prev]
if (newActions.length > 100) {
return newActions.slice(0, 100)
}
return newActions
})
}
return addToActionLog
}

View File

@@ -1,22 +0,0 @@
import CSVRawDataPreview from './CSVRawDataPreview'
import StatementDetails from './StatementDetails'
import _ from '@/lib/translate'
import { GetStatementDetailsResponse } from '../import_utils'
const CSVImport = ({ data }: { data: { message: GetStatementDetailsResponse } }) => {
return (
<div className="w-full flex">
<div className="w-[50%] p-4 h-[calc(100vh-72px)] overflow-scroll">
<StatementDetails data={data.message} />
</div>
<div className="w-[50%] border-s border-t pe-1 ps-0 border-outline-gray-2 h-[calc(100vh-72px)] overflow-scroll">
<CSVRawDataPreview data={data.message} />
</div>
</div>
)
}
export default CSVImport

View File

@@ -1,151 +0,0 @@
import { Table, TableBody, TableCell, TableHead, TableRow } from "@/components/ui/table"
import { cn } from "@/lib/utils"
import { ArrowDownRightIcon, ArrowUpDownIcon, ArrowUpRightIcon, BanknoteIcon, CalendarIcon, DollarSignIcon, FileTextIcon, ListIcon, ReceiptIcon } from "lucide-react"
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
import _ from "@/lib/translate"
import { GetStatementDetailsResponse } from "../import_utils"
import { useMemo } from "react"
import { BankStatementImportLogColumnMap } from "@/types/Accounts/BankStatementImportLogColumnMap"
const CSVRawDataPreview = ({ data }: { data: GetStatementDetailsResponse }) => {
const column_mapping: Record<StandardColumnTypes, number> = useMemo(() => {
const col_map: Record<string, number> = {}
data.doc.column_mapping?.forEach(col => {
if (col.maps_to && col.maps_to !== "Do not import") {
col_map[col.maps_to] = col.index;
}
})
return col_map
}, [data])
const validColumns = Object.values(column_mapping)
// Reverse the column mapping to get a map of column index to variable name
const columnIndexMap: Record<number, StandardColumnTypes> = Object.fromEntries(Object.entries(column_mapping).map(([variable, columnIndex]) => [columnIndex, variable as StandardColumnTypes]))
// Loop over the contents of the CSV file and show a preview - highlight the header row and the transaction rows
return (
<Table containerClassName="rounded-none">
<TableBody>
{data.raw_data.map((row, index) => {
const isHeaderRow = index === data.doc.detected_header_index;
const isTransactionRow = index >= (data.doc.detected_transaction_starting_index ?? 0) && index <= (data.doc.detected_transaction_ending_index ?? 0);
return <TableRow key={index}
title={isHeaderRow ? "Header Row" : ""}
className={cn({
// "bg-yellow-100": isHeaderRow,
// "hover:bg-yellow-100": isHeaderRow,
"bg-green-50 hover:bg-green-50 dark:bg-green-700 dark:hover:bg-green-700": isTransactionRow,
"text-ink-gray-5/70": !isTransactionRow && !isHeaderRow,
})}>
{isHeaderRow ? <TableHead className="bg-yellow-100 hover:bg-yellow-100 dark:bg-yellow-400 text-center font-semibold text-ink-gray-8">
{index + 1}
</TableHead> :
<TableCell className="text-center px-1 py-0.5">
{index + 1}
</TableCell>
}
{row.map((cell, cellIndex) => {
const isValidColumn = validColumns.includes(cellIndex);
const columnType = columnIndexMap[cellIndex];
const isAmountColumn = ["Amount", "Withdrawal", "Deposit", "Balance"].includes(columnType);
if (isHeaderRow) {
return <TableHead key={cellIndex} className={cn("max-w-[250px] w-fit overflow-hidden text-ellipsis py-0.5",
isValidColumn ? "bg-yellow-100 hover:bg-yellow-100 dark:bg-yellow-400" : "bg-surface-gray-2",
)}>
<div className={cn("flex items-center text-xs gap-1 px-1 text-ink-gray-8 font-medium", {
"justify-end": isAmountColumn && isValidColumn
})}>
{columnType && <Tooltip>
<TooltipTrigger>
<ColumnHeaderIcon columnType={columnType} />
</TooltipTrigger>
<TooltipContent>
{_(columnType)}
</TooltipContent>
</Tooltip>
}
{cell}
</div>
</TableHead>
} else {
return <TableCell key={cellIndex} className={cn("max-w-[200px] w-fit overflow-hidden text-ellipsis py-0.5",
{
"bg-green-100 dark:bg-green-400 hover:bg-green-100 dark:hover:bg-green-400": isValidColumn && isTransactionRow,
"text-ink-gray-5": !isValidColumn && isTransactionRow,
}
)} >
<div className={cn("min-h-5 flex items-center text-xs px-1", {
"justify-end": isAmountColumn && isValidColumn && isTransactionRow
})} title={cell}>
{cell}
</div>
</TableCell>
}
}
)}
</TableRow>
})}
</TableBody>
</Table >
)
}
type StandardColumnTypes = BankStatementImportLogColumnMap['maps_to'];
const ColumnHeaderIcon = ({ columnType }: { columnType?: StandardColumnTypes }) => {
if (!columnType) {
return null
}
if (columnType === 'Amount') {
return <DollarSignIcon className="w-4 h-4" />
}
if (columnType === 'Withdrawal') {
return <ArrowUpRightIcon className="w-4 h-4 text-ink-red-3" />
}
if (columnType === 'Deposit') {
return <ArrowDownRightIcon className="w-4 h-4 text-ink-green-3" />
}
if (columnType === 'Balance') {
return <BanknoteIcon className="w-4 h-4" />
}
if (columnType === 'Date') {
return <CalendarIcon className="w-4 h-4" />
}
if (columnType === 'Description') {
return <FileTextIcon className="w-4 h-4" />
}
if (columnType === 'Reference') {
return <ReceiptIcon className="w-4 h-4" />
}
if (columnType === 'Transaction Type') {
return <ListIcon className="w-4 h-4" />
}
if (columnType === 'Debit/Credit') {
return <ArrowUpDownIcon className="w-4 h-4" />
}
return null
}
export default CSVRawDataPreview

View File

@@ -1,351 +0,0 @@
import _ from '@/lib/translate'
import { GetStatementDetailsResponse } from '../import_utils'
import { flt, formatCurrency } from '@/lib/numbers'
import { formatDate } from '@/lib/date'
import { bankRecDateAtom } from '../../BankReconciliation/bankRecAtoms'
import { AlertCircleIcon, ChevronLeftIcon, ChevronRightIcon, ExternalLinkIcon, InfoIcon, Loader2Icon } from 'lucide-react'
import { H2, H3, Paragraph } from '@/components/ui/typography'
import { FileTypeIcon } from '@/components/ui/file-dropzone'
import { getFileExtension } from '@/lib/file'
import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
import { Separator } from '@/components/ui/separator'
import { Button } from '@/components/ui/button'
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
import { useFrappeEventListener, useFrappePostCall } from 'frappe-react-sdk'
import { toast } from 'sonner'
import ErrorBanner from '@/components/ui/error-banner'
import { Link, useNavigate } from 'react-router-dom'
import { useMemo, useState } from 'react'
import { Progress } from '@/components/ui/progress'
import { useSetAtom } from 'jotai'
import { useDirection } from '@/components/ui/direction'
import BankLogo from '@/components/common/BankLogo'
import { useGetBankAccounts } from '../../BankReconciliation/utils'
import { BankStatementImportLog } from '@/types/Accounts/BankStatementImportLog'
import { Badge } from '@/components/ui/badge'
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'
const parseDateFormat = (dateFormat: string) => {
const charMap = {
"%d": "DD",
"%m": "MM",
"%Y": "YYYY",
"%y": "YY",
"%b": "MMM",
"%B": "MMMM",
}
let label = dateFormat
Object.keys(charMap).forEach((char) => {
label = label.replace(char, charMap[char as keyof typeof charMap])
})
return dateFormat
}
type Props = {
data: GetStatementDetailsResponse,
}
const StatementDetails = ({ data }: Props) => {
const dateFormat = parseDateFormat(data.date_format)
const { call, loading, error } = useFrappePostCall<{ docs: BankStatementImportLog[] }>('run_doc_method')
const navigate = useNavigate()
const setDates = useSetAtom(bankRecDateAtom)
const direction = useDirection()
const onImport = () => {
call({
docs: data.doc,
method: 'insert_transactions'
}).then((response) => {
const doc = response.docs ? response.docs[0] : undefined
if (doc && doc.start_date && doc.end_date) {
setDates({
fromDate: doc.start_date,
toDate: doc.end_date,
})
}
toast.success(_("Bank statement imported."))
navigate(`/`)
}).catch(() => {
toast.error(_("There was an error while importing the bank statement."))
})
}
const [progress, setProgress] = useState(0)
useFrappeEventListener("bank-rec-statement-import-progress", (event) => {
setProgress(event.progress)
})
const file_name = data.doc.file.split("/").pop() ?? ""
const { banks } = useGetBankAccounts()
const bank = useMemo(() => {
return banks?.find((bank) => bank.name === data.doc.bank_account)
}, [data.doc.bank_account, banks])
return (
<div className='flex flex-col gap-4'>
<div className='flex flex-col gap-4'>
<div className='flex justify-between items-center'>
<Button size='sm' variant='outline' asChild>
<Link to="/statement-importer">
{direction === 'ltr' ? <ChevronLeftIcon /> : <ChevronRightIcon />}
{_("Back")}
</Link>
</Button>
{data.doc.status === 'Completed' ? <Badge theme='green'>{_("Completed")}</Badge> :
<Button onClick={onImport} disabled={loading || data.final_transactions?.length === 0} size='sm' type='button'>
{loading ? <Loader2Icon className='size-4 animate-spin' /> : null}
{loading ? _("Importing...") : _("Import {0} transactions", [data.final_transactions?.length?.toString() || "0"])}</Button>
}
</div>
<div className='flex items-start gap-4'>
<div className='flex flex-col gap-1'>
<H2 className='text-lg border-0 p-0'>{_("Statement Details")}</H2>
<Paragraph className='text-p-sm'><span>
{_("We've auto-detected the details of the statement file.")}
</span><br />
<span>
{_("Please review the details below and click the 'Import' button to proceed.")}
</span>
</Paragraph>
</div>
</div>
{progress > 0 && <div className='flex flex-col gap-2'><Progress value={progress} max={100} size="lg" />
<span className='text-sm'>{_("Importing {0} transactions", [progress.toString()])}
</span>
</div>}
{error && <ErrorBanner error={error} />}
<Table>
<TableBody>
<TableRow>
<TableHead>{_("Bank Account")}</TableHead>
<TableCell>
<div className='flex items-center gap-2'>
<BankLogo bank={bank} />
<span className="tracking-tight text-sm font-medium">{bank?.account_name}</span>
<span title="GL Account" className="text-sm">{bank?.account}</span>
</div>
</TableCell>
</TableRow>
<TableRow>
<TableHead>{_("Statement File")}</TableHead>
<TableCell>
<div className='flex items-center gap-2'>
<FileTypeIcon fileType={getFileExtension(file_name)} size='md' showBackground={false} />
{file_name}
</div>
</TableCell>
</TableRow>
<TableRow>
<TableHead>{_("Transaction Dates")}</TableHead>
<TableCell>{_("{0} to {1}", [formatDate(data.doc.start_date, "Do MMMM YYYY"), formatDate(data.doc.end_date, "Do MMMM YYYY")])}</TableCell>
</TableRow>
<TableRow>
<TableHead>{_("Number of Transactions")}</TableHead>
<TableCell>{data.doc.number_of_transactions}</TableCell>
</TableRow>
<TableRow>
<TableHead>{_("Total Debits")}</TableHead>
<TableCell><span className='font-numeric'>{formatCurrency(flt(data.doc.total_debits, 2), data.currency)}</span> <span className='text-ink-gray-5 font-sans'>({data.doc.total_debit_transactions} {data.doc.total_debit_transactions === 1 ? _("transaction") : _("transactions")})</span></TableCell>
</TableRow>
<TableRow>
<TableHead>{_("Total Credits")}</TableHead>
<TableCell><span className='font-numeric'>{formatCurrency(flt(data.doc.total_credits, 2), data.currency)}</span> <span className='text-ink-gray-5 font-sans'>({data.doc.total_credit_transactions} {data.doc.total_credit_transactions === 1 ? _("transaction") : _("transactions")})</span></TableCell>
</TableRow>
<TableRow>
<TableHead>{_("Closing Balance as of {}", [formatDate(data.doc.end_date, "Do MMMM YYYY")])}</TableHead>
<TableCell className='font-numeric'>{formatCurrency(flt(data.doc.closing_balance, 2), data.currency)}</TableCell>
</TableRow>
<TableRow>
<TableHead>
<div className='flex items-center gap-2'>
{_("Detected Amount Format")} <Tooltip>
<TooltipTrigger><InfoIcon size={16} /></TooltipTrigger>
<TooltipContent>
{_("The amount format detected in the statement file. This is used to parse the deposit and withdrawal values from each row.")}
</TooltipContent>
</Tooltip>
</div>
</TableHead>
<TableCell>{data.doc.detected_amount_format}</TableCell>
</TableRow>
<TableRow>
<TableHead>
<div className='flex items-center gap-2'>
{_("Detected Date Format")}
<Tooltip>
<TooltipTrigger><InfoIcon size={16} /></TooltipTrigger>
<TooltipContent>
{_("The date format detected in the statement file. This is used to parse the date values.")}
</TooltipContent>
</Tooltip>
</div>
</TableHead>
<TableCell>
{dateFormat || data.date_format} (e.g.{" "}
{formatDate(new Date(), dateFormat || "YYYY-MM-DD")})
</TableCell>
</TableRow>
</TableBody>
</Table>
</div>
{data.doc.status === "Not Started" ? <>
<ConflictingTransactions transactions={data.conflicting_transactions} />
<Separator />
<div className='flex flex-col gap-4'>
<div className='flex flex-col gap-1'>
<H3 className='text-base border-0 p-0'>{_("Preview Transactions")}</H3>
{data.final_transactions?.length === 1 ? (
<Paragraph className='text-p-sm'>{_("We've found 1 transaction in the statement file that will be imported into the system. Please review the details below and click the 'Import' button to proceed.")}</Paragraph>
) : (
<Paragraph className='text-p-sm'>{_("{0} transactions will be imported into the system. Please review the details below and click the 'Import' button to proceed.", [data.final_transactions?.length?.toString() || "0"])}</Paragraph>
)}
</div>
<div className='max-h-[400px] overflow-scroll pb-2'>
<Table>
<TableCaption>{_("Transactions to be imported into the system")}</TableCaption>
<TableHeader>
<TableRow>
<TableHead className='w-8'>#</TableHead>
<TableHead>{_("Date")}</TableHead>
<TableHead>{_("Description")}</TableHead>
<TableHead>{_("Ref.")}</TableHead>
<TableHead className='text-end'>{_("Withdrawal")}</TableHead>
<TableHead className='text-end'>{_("Deposit")}</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{data.final_transactions?.map((transaction, index) => (
<TableRow key={index}>
<TableCell className='w-8'>{index + 1}</TableCell>
<TableCell>{formatDate(transaction.date)}</TableCell>
<TableCell className='max-w-[200px] w-fit overflow-hidden text-ellipsis'>{transaction.description}</TableCell>
<TableCell className='max-w-[100px] w-fit overflow-hidden text-ellipsis'>{transaction.reference}</TableCell>
<TableCell className='text-end font-numeric'>{formatCurrency(transaction.withdrawal, data.currency)}</TableCell>
<TableCell className='text-end font-numeric'>{formatCurrency(transaction.deposit, data.currency)}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
</div>
</> : null}
</div>
)
}
const ConflictingTransactions = ({ transactions }: { transactions: GetStatementDetailsResponse["conflicting_transactions"] }) => {
if (transactions.length === 0) {
return null
}
return <>
<Alert theme="red">
<AlertCircleIcon />
<AlertTitle>{_("Conflicting Transactions")}</AlertTitle>
<AlertDescription>
{transactions.length === 1 ? _("We've found 1 existing transaction in the system that conflicts with the transactions in the statement file. Are you sure you want to proceed with the import?")
: _("We've found {0} existing transactions in the system that conflict with the transactions in the statement file. Are you sure you want to proceed with the import?", [transactions.length.toString()])}
<div className='py-2'>
<Dialog>
<DialogTrigger asChild>
<Button
size='sm'
type='button'
theme='red'
variant='solid'>
<span>{transactions.length > 1 ? _("View transactions") : _("View transaction")}</span>
</Button>
</DialogTrigger>
<DialogContent className='min-w-7xl'>
<DialogHeader>
<DialogTitle>{_("Conflicting Transactions")}</DialogTitle>
<DialogDescription>
{transactions.length === 1 ? _("We've found 1 existing transaction in the system that conflicts with the transactions in the statement file. Are you sure you want to proceed with the import?")
: _("We've found {0} existing transactions in the system that conflict with the transactions in the statement file. Are you sure you want to proceed with the import?", [transactions.length.toString()])}
</DialogDescription>
</DialogHeader>
<div className='max-h-[400px] overflow-scroll pb-2'>
<Table>
<TableCaption>{_("Existing transactions in the system belonging to the same bank account and date range")}</TableCaption>
<TableHeader>
<TableRow>
<TableHead>{_("Date")}</TableHead>
<TableHead>{_("Description")}</TableHead>
<TableHead>{_("Ref.")}</TableHead>
<TableHead className='text-end'>{_("Withdrawal")}</TableHead>
<TableHead className='text-end'>{_("Deposit")}</TableHead>
<TableHead></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{transactions.map((transaction) => (
<TableRow key={transaction.name}>
<TableCell>{formatDate(transaction.date)}</TableCell>
<TableCell title={transaction.description} className='max-w-[200px] w-fit overflow-hidden text-ellipsis'>{transaction.description}</TableCell>
<TableCell title={transaction.reference_number} className='max-w-[100px] w-fit overflow-hidden text-ellipsis'>{transaction.reference_number ? transaction.reference_number : "-"}</TableCell>
<TableCell className='text-end font-numeric'>{formatCurrency(transaction.withdrawal, transaction.currency)}</TableCell>
<TableCell className='text-end font-numeric'>{formatCurrency(transaction.deposit, transaction.currency)}</TableCell>
<TableCell className='text-end'>
<Tooltip>
<TooltipTrigger asChild>
<Button variant='link' isIconButton asChild className='text-ink-gray-5 hover:text-black p-0 h-4'>
<a href={`/desk/bank-transaction/${transaction.name}`} target='_blank' rel='noopener noreferrer'>
<ExternalLinkIcon />
</a>
</Button>
</TooltipTrigger>
<TooltipContent>
{_("Open {0} in a new tab", [transaction.name])}
</TooltipContent>
</Tooltip>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
<DialogFooter>
<DialogClose asChild>
<Button variant={'outline'} size='md' type='button'>{_("Close")}</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
</AlertDescription>
</Alert>
</>
}
export default StatementDetails

View File

@@ -1,42 +0,0 @@
import { BankStatementImportLog } from "@/types/Accounts/BankStatementImportLog"
import { useFrappeGetCall } from "frappe-react-sdk"
export interface GetStatementDetailsResponse {
doc: BankStatementImportLog,
conflicting_transactions: Array<{
name: string,
date: string,
withdrawal: number,
deposit: number,
description: string,
reference_number: string,
currency: string,
}>,
final_transactions: Array<{
date: string,
withdrawal: number,
deposit: number,
description: string,
reference: string,
transaction_type?: string,
debit_credit?: string,
included_fee?: number,
excluded_fee?: number,
party_name?: string,
party_account_number?: string,
party_iban?: string,
}>,
date_format: string,
raw_data: Array<Array<string>>,
currency: string,
}
export const useGetStatementDetails = (id: string) => {
return useFrappeGetCall<{ message: GetStatementDetailsResponse }>("erpnext.accounts.doctype.bank_statement_import_log.bank_statement_import_log.get_statement_details", {
statement_import_id: id,
}, undefined, {
revalidateOnFocus: false
})
}

View File

@@ -1,115 +0,0 @@
import { Badge } from '@/components/ui/badge'
import { Kbd, KbdGroup } from '@/components/ui/kbd'
import { KeyboardMetaKeyIcon } from '@/components/ui/keyboard-keys'
import { SettingsPanelDescription, SettingsPanelTitle, SettingsPanelHeader, SettingsPanelContent } from '@/components/ui/settings-dialog'
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
import _ from '@/lib/translate'
import { ArrowRightLeftIcon, HistoryIcon, LandmarkIcon, ReceiptIcon, SaveIcon, SettingsIcon, ZapIcon } from 'lucide-react'
const Shortcuts = [
{
shortcut: <KbdGroup><Kbd><KeyboardMetaKeyIcon /></Kbd><Kbd>B</Kbd></KbdGroup>,
action: {
icon: <LandmarkIcon />,
label: _("Bank Entry"),
description: _("Record a bank journal entry for expenses, income or split transactions")
}
},
{
shortcut: <KbdGroup><Kbd><KeyboardMetaKeyIcon /></Kbd><Kbd>P</Kbd></KbdGroup>,
action: {
icon: <ReceiptIcon />,
label: _("Record Payment"),
description: _("Record a payment against a customer or supplier")
}
},
{
shortcut: <KbdGroup><Kbd><KeyboardMetaKeyIcon /></Kbd><Kbd>I</Kbd></KbdGroup>,
action: {
icon: <ArrowRightLeftIcon />,
label: _("Transfer"),
description: _("Record a transfer between two bank accounts")
}
},
{
shortcut: <KbdGroup><Kbd><KeyboardMetaKeyIcon /></Kbd><Kbd>R</Kbd></KbdGroup>,
action: {
icon: <ZapIcon />,
label: _("Accept Matching Rule"),
description: _("Accept the rule for the selected transaction")
}
},
{
shortcut: <KbdGroup><Kbd><KeyboardMetaKeyIcon /></Kbd><Kbd>S</Kbd></KbdGroup>,
action: {
icon: <SaveIcon />,
label: _("Save"),
description: _("Save the currently opened form")
}
},
{
shortcut: <KbdGroup><Kbd><KeyboardMetaKeyIcon /></Kbd><Kbd>Z</Kbd></KbdGroup>,
action: {
icon: <HistoryIcon />,
label: _("Reconciliation History"),
description: _("View all reconciliation actions taken in this session")
}
},
{
shortcut: <KbdGroup><Kbd><KeyboardMetaKeyIcon /></Kbd><Kbd></Kbd><Kbd>G</Kbd></KbdGroup>,
action: {
icon: <SettingsIcon />,
label: _("Settings"),
description: _("Open the settings dialog")
}
}
]
const KeyboardShortcuts = () => {
return (
<>
<SettingsPanelHeader>
<SettingsPanelTitle>{_("Keyboard Shortcuts")}</SettingsPanelTitle>
<SettingsPanelDescription>{_("Get around the system quickly with keyboard shortcuts")}</SettingsPanelDescription>
</SettingsPanelHeader>
<SettingsPanelContent>
<div className='flex flex-col gap-3'>
<p className='text-p-sm text-ink-gray-6'>
{_("Transaction actions work when one or more unreconciled transactions are selected.")}
<br />
{_("To select more than one transaction at a time, press and hold the shift key.")}
</p>
<Table containerClassName='dark:border-outline-gray-2'>
<TableHeader>
<TableRow>
<TableHead>{_("Shortcut")}</TableHead>
<TableHead>{_("Action")}</TableHead>
<TableHead>{_("Description")}</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{Shortcuts.map((shortcut) => (
<TableRow className='hover:bg-surface-gray-2'>
<TableCell>
{shortcut.shortcut}
</TableCell>
<TableCell>
<Badge size='lg' variant='outline'>
{shortcut.action.icon}
{shortcut.action.label}
</Badge>
</TableCell>
<TableCell>
<p className='text-p-sm text-ink-gray-6 text-wrap'>{shortcut.action.description}</p>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
</SettingsPanelContent>
</>
)
}
export default KeyboardShortcuts

View File

@@ -1,46 +0,0 @@
import { Button } from '@/components/ui/button'
import { SettingsPanelTitle, SettingsPanelHeader, SettingsPanelDescription, SettingsPanelContent } from '@/components/ui/settings-dialog'
import _ from '@/lib/translate'
import { PlusIcon } from 'lucide-react'
import { useState } from 'react'
import RuleList, { RunRulesButton } from './Rules/RuleList'
import CreateNewRule from '../BankReconciliation/Rules/CreateNewRule'
import EditRule from '../BankReconciliation/Rules/EditRule'
const MatchingRules = () => {
const [selectedRule, setSelectedRule] = useState<string | null>(null)
const [isNewRule, setIsNewRule] = useState(false)
if (isNewRule) {
return <CreateNewRule onCreate={() => setIsNewRule(false)} />
}
if (selectedRule) {
return <EditRule onClose={() => setSelectedRule(null)} ruleID={selectedRule} />
}
return (
<>
<SettingsPanelHeader
actions={
<div className='flex gap-2 items-center'>
<RunRulesButton />
<Button type='button' onClick={() => setIsNewRule(true)}><PlusIcon /> {_("Add Rule")}</Button>
</div>
}
>
<SettingsPanelTitle>{_("Transaction Matching Rules")}</SettingsPanelTitle>
<SettingsPanelDescription>
{_("Set up rules to automatically classify transactions. Drag and drop rules to reorder their priority.")}
</SettingsPanelDescription>
</SettingsPanelHeader>
<SettingsPanelContent>
<RuleList setSelectedRule={setSelectedRule} />
</SettingsPanelContent>
</>
)
}
export default MatchingRules

View File

@@ -1,261 +0,0 @@
import ErrorBanner from "@/components/ui/error-banner"
import { Label } from "@/components/ui/label"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { Separator } from "@/components/ui/separator"
import { SettingsPanelDescription, SettingsPanelHeader, SettingsPanelTitle, SettingsPanelContent } from "@/components/ui/settings-dialog"
import { Switch } from "@/components/ui/switch"
import { useTheme } from "@/components/ui/theme-provider"
import _ from "@/lib/translate"
import { AccountsSettings } from "@/types/Accounts/AccountsSettings"
import { useFrappeGetDoc, useFrappeUpdateDoc } from "frappe-react-sdk"
import { toast } from "sonner"
export const Preferences = () => {
const { data: accountsSettings, mutate, error: fetchError, isLoading } = useFrappeGetDoc<AccountsSettings>("Accounts Settings", "Accounts Settings", undefined, {
revalidateOnFocus: false
})
const { updateDoc, error } = useFrappeUpdateDoc<AccountsSettings>()
const onUpdate = (field: keyof AccountsSettings, value: any) => {
mutate(updateDoc("Accounts Settings", "Accounts Settings", {
[field]: value
}), {
optimisticData: {
...accountsSettings as AccountsSettings,
[field]: value
},
revalidate: false,
}).then(() => {
toast.success(_("Preferences updated"), {
dismissible: true,
duration: 500,
})
})
}
return <>
<SettingsPanelHeader>
<SettingsPanelTitle>{_("Preferences")}</SettingsPanelTitle>
<SettingsPanelDescription>{_("Configure settings for the banking module")}</SettingsPanelDescription>
</SettingsPanelHeader>
<SettingsPanelContent>
<div className='flex flex-col gap-4 w-full'>
{fetchError && <ErrorBanner error={fetchError} />}
{error && <ErrorBanner error={error} />}
<div className="flex flex-col flex-1">
<ThemeSwitcher />
<div className="flex justify-between items-center gap-8 py-3">
<div className="flex flex-col">
<Label htmlFor="transfer_match_days" className="text-p-base text-ink-gray-6">{_("Number of days to match transfers")}</Label>
<p className="text-p-sm text-ink-gray-5">
{_("For example, if set to 4, the system will try to find matching transfer transactions in other banks 4 days before and after the transaction date. This is because transactions can clear on different days on different bank accounts.")}
</p>
</div>
<div className="min-w-40 flex justify-end">
<Select disabled={isLoading} onValueChange={(value) => onUpdate("transfer_match_days", Number(value))} value={accountsSettings?.transfer_match_days?.toString()}>
<SelectTrigger id="transfer_match_days" className="min-w-32">
<SelectValue placeholder={_("Select number of days")} />
</SelectTrigger>
<SelectContent>
<SelectItem value="0">{_("Same day")}</SelectItem>
<SelectItem value="1">{_("Within 1 day")}</SelectItem>
<SelectItem value="2">{_("Within 2 days")}</SelectItem>
<SelectItem value="3">{_("Within 3 days")}</SelectItem>
<SelectItem value="4">{_("Within 4 days")}</SelectItem>
<SelectItem value="5">{_("Within 5 days")}</SelectItem>
</SelectContent>
</Select>
</div>
</div>
<Separator />
<div className="flex justify-between items-center gap-8 py-3">
<div className="flex flex-col">
<Label htmlFor="automatically_run_rules_on_unreconciled_transactions" className="text-p-base text-ink-gray-6">{_("Automatically run rules on unreconciled transactions")}</Label>
<p className="text-p-sm text-ink-gray-5">
{_("This will automatically run transaction matching rules on unreconciled transactions every hour.")}
</p>
</div>
<div className="flex justify-end">
<Switch
id="automatically_run_rules_on_unreconciled_transactions"
className="dark:disabled:bg-surface-gray-2"
disabled={isLoading}
checked={accountsSettings?.automatically_run_rules_on_unreconciled_transactions === 1}
onCheckedChange={(checked) => onUpdate("automatically_run_rules_on_unreconciled_transactions", checked ? 1 : 0)}
/>
</div>
</div>
<Separator />
<div className="flex justify-between items-center gap-8 py-3">
<div className="flex flex-col">
<Label htmlFor="enable_party_matching" className="text-p-base text-ink-gray-6">{_("Enable automatic party matching")}</Label>
<p className="text-p-sm text-ink-gray-5">
{_("The system will attempt to automatically match a party to a bank transaction based on account number or IBAN.")}
</p>
</div>
<div className="flex justify-end">
<Switch
id="enable_party_matching"
className="dark:disabled:bg-surface-gray-2"
disabled={isLoading}
checked={accountsSettings?.enable_party_matching === 1}
onCheckedChange={(checked) => onUpdate("enable_party_matching", checked ? 1 : 0)}
/>
</div>
</div>
<Separator />
<div className="flex justify-between items-center gap-8 py-3">
<div className="flex flex-col">
<Label htmlFor="enable_fuzzy_matching" className="text-p-base text-ink-gray-6">{_("Enable party name/description fuzzy matching")}</Label>
<p className="text-p-sm text-ink-gray-5">
{_("If a party cannot be matched by account number or IBAN, the system will try fuzzy matching using the party name and transaction description.")}
</p>
</div>
<div className="flex justify-end">
<Switch
id="enable_fuzzy_matching"
className="dark:disabled:bg-surface-gray-2"
disabled={accountsSettings?.enable_party_matching !== 1 || isLoading}
checked={accountsSettings?.enable_fuzzy_matching === 1}
onCheckedChange={(checked) => onUpdate("enable_fuzzy_matching", checked ? 1 : 0)}
/>
</div>
</div>
</div>
{/* <DataField
name='transfer_match_days'
label={_("Number of days to match transfers")}
isRequired
inputProps={{
type: 'number',
inputMode: 'numeric',
}}
formDescription={_("For example, if set to 4, the system will try to find matching transactions in other banks 4 days before and after the transaction date. This is because transactions can clear on different days on different bank accounts.")}
/> */}
</div>
</SettingsPanelContent>
</>
}
const ThemeSwitcher = () => {
const { theme, setTheme } = useTheme()
const themeCards: Array<{ value: "Light" | "Dark" | "Automatic", label: string }> = [
{
value: "Light",
label: _("Light"),
},
{
value: "Dark",
label: _("Dark"),
},
{
value: "Automatic",
label: _("System"),
},
]
return <div className="flex flex-col gap-3 pb-3">
<div className="flex flex-col">
<Label className="text-p-base text-ink-gray-6">{_("Theme")}</Label>
<p className="text-p-sm text-ink-gray-5">
{_("Switch between light, dark, or system theme")}
</p>
</div>
<div className="flex gap-3">
{themeCards.map((option) => {
const selected = theme === option.value
return (
<button
key={option.value}
type="button"
onClick={() => setTheme(option.value)}
aria-pressed={selected}
className={`flex-1 basis-0 min-w-0 overflow-hidden rounded-lg border cursor-pointer transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-outline-blue-4 ${selected ? "border-outline-gray-5" : "border-outline-gray-modals hover:border-outline-gray-4"}`}
>
{option.value === "Automatic" ? (
<div className="flex w-full min-w-0">
<ThemePreviewWindow theme="light" roundedClass="rounded-tl-[10.5px]" />
<ThemePreviewWindow theme="dark" roundedClass="rounded-tr-[10.5px]" />
</div>
) : (
<ThemePreviewWindow theme={option.value === "Light" ? "light" : "dark"} roundedClass="rounded-t-[10.5px]" />
)}
<div className="flex items-center justify-between px-3 py-2 border-t border-outline-gray-modals">
<div className="text-base text-ink-gray-7">{option.label}</div>
<span className={`rounded-full size-3.5 ${selected ? "border-4 border-outline-gray-5" : "border border-outline-gray-4"}`} />
</div>
</button>
)
})}
</div>
</div>
}
const ThemePreviewWindow = ({ theme, roundedClass }: { theme: "light" | "dark", roundedClass: string }) => {
const isLight = theme === "light"
const frameClass = isLight ? "bg-white border-gray-100" : "bg-gray-900 border-gray-800"
const subtleSurfaceClass = isLight ? "bg-gray-50" : "bg-gray-800"
const mutedLineClass = isLight ? "bg-gray-200" : "bg-gray-700"
const mutedLineStrongClass = isLight ? "bg-gray-300" : "bg-gray-600"
const dividerClass = isLight ? "border-gray-100" : "border-gray-800"
const cardClass = isLight ? "bg-white border-gray-200" : "bg-gray-900 border-gray-700"
return <div className={`flex flex-1 min-w-0 pl-5 pt-3.5 ${isLight ? "bg-surface-gray-2" : "bg-surface-gray-3"} ${roundedClass}`}>
<div className={`w-full rounded-tl-sm border ${frameClass}`}>
<div className={`flex gap-[3px] py-[3px] px-1 border-b ${dividerClass}`}>
<div className="size-1.5 bg-[#FF5F57] rounded-full" />
<div className="size-1.5 bg-[#FEBC2D] rounded-full" />
<div className="size-1.5 bg-[#28C840] rounded-full" />
</div>
<div className="p-1.5">
<div className={`flex items-center gap-1.5 p-1 rounded-sm border ${subtleSurfaceClass} ${dividerClass}`}>
<div className={`h-2 w-8 rounded-full ${mutedLineStrongClass}`} />
<div className={`h-2 w-6 rounded-full ${mutedLineClass}`} />
<div className={`h-2 w-7 rounded-full ml-auto ${mutedLineClass}`} />
</div>
<div className="grid grid-cols-2 gap-1 mt-1.5">
<div className={`rounded-sm border p-1 ${cardClass}`}>
<div className={`h-1.5 w-full rounded-full ${mutedLineStrongClass}`} />
<div className={`h-1.5 w-4/5 rounded-full mt-1 ${mutedLineClass}`} />
<div className={`h-1.5 w-3/5 rounded-full mt-1 ${mutedLineClass}`} />
</div>
<div className={`rounded-sm border p-1 ${cardClass}`}>
<div className="flex items-center justify-between gap-1">
<div className={`h-1.5 w-2/5 rounded-full ${mutedLineStrongClass}`} />
{/* <div className={`h-2.5 w-5 rounded-sm border ${chipClass}`} /> */}
</div>
<div className={`h-1.5 w-full rounded-full mt-1 ${mutedLineClass}`} />
<div className={`h-1.5 w-3/4 rounded-full mt-1 ${mutedLineClass}`} />
</div>
</div>
</div>
</div>
</div>
}

View File

@@ -1,314 +0,0 @@
import { Button } from "@/components/ui/button"
import ErrorBanner from "@/components/ui/error-banner"
import { Skeleton } from "@/components/ui/skeleton"
import { Badge } from "@/components/ui/badge"
import _ from "@/lib/translate"
import { BankTransactionRule } from "@/types/Accounts/BankTransactionRule"
import { FrappeConfig, FrappeContext, useFrappeGetCall, useFrappeGetDocList, useFrappePostCall } from "frappe-react-sdk"
import { ArrowDownRight, ArrowDownUp, ArrowUpRight, MoreVertical, Trash2, GripVertical, Play, RefreshCw, ZapIcon, CalendarSyncIcon } from "lucide-react"
import { useContext, useState } from "react"
import { toast } from "sonner"
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, DropdownMenuSeparator, DropdownMenuCheckboxItem } from "@/components/ui/dropdown-menu"
import {
DndContext,
closestCenter,
KeyboardSensor,
PointerSensor,
useSensor,
useSensors,
DragEndEvent,
} from '@dnd-kit/core'
import {
arrayMove,
SortableContext,
sortableKeyboardCoordinates,
verticalListSortingStrategy,
} from '@dnd-kit/sortable'
import {
useSortable,
} from '@dnd-kit/sortable'
import { CSS } from '@dnd-kit/utilities'
import { Empty, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from "@/components/ui/empty"
import { cn } from "@/lib/utils"
const useGetRuleList = () => {
return useFrappeGetDocList<BankTransactionRule>("Bank Transaction Rule", {
fields: ["name", "rule_name", "rule_description", "transaction_type", "priority"],
orderBy: {
field: 'priority',
order: 'asc'
},
limit: 100
})
}
export const RunRulesButton = () => {
const { data } = useGetRuleList()
const { call: runRuleEvaluation, loading: isRunningRules } = useFrappePostCall('erpnext.accounts.doctype.bank_transaction_rule.bank_transaction_rule.run_rule_evaluation')
const handleRunRules = async (forceEvaluate: boolean = false) => {
try {
await runRuleEvaluation({
force_evaluate: forceEvaluate
})
toast.success(forceEvaluate ? _("Rules evaluation started") : _("Rules evaluation completed"))
} catch (error) {
toast.error(_("Failed to run rules evaluation"))
console.error("Error running rules evaluation:", error)
}
}
if (!data || data.length === 0) {
return null
}
return <DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="sm" disabled={isRunningRules}>
{isRunningRules ? (
<RefreshCw className="animate-spin" />
) : (
<Play />
)}
{isRunningRules ? _("Running...") : _("Run Rules")}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
<DropdownMenuItem onClick={() => handleRunRules(false)} disabled={isRunningRules} title={_("Run rules on unreconciled transactions that haven't been evaluated yet")}>
<Play />
{_("Run on new transactions")}
</DropdownMenuItem>
<DropdownMenuItem onClick={() => handleRunRules(true)} disabled={isRunningRules} title={_("Force re-evaluate all unreconciled transactions, even if they were previously evaluated")}>
<RefreshCw />
{_("Force evaluate all")}
</DropdownMenuItem>
<DropdownMenuSeparator />
<AutoRunRuleItem />
</DropdownMenuContent>
</DropdownMenu>
}
const AutoRunRuleItem = () => {
const { db } = useContext(FrappeContext) as FrappeConfig
const { data: accountsSetting, mutate: setAutomaticallyRunRulesOnUnreconciledTransactions } = useFrappeGetCall("frappe.client.get_single_value", {
"doctype": "Accounts Settings",
"field": "automatically_run_rules_on_unreconciled_transactions"
})
const automaticallyRunRulesOnUnreconciledTransactions = accountsSetting?.message ? true : false
const onAutoClassifyTransactions = (checked: boolean) => {
toast.promise(db.setValue("Accounts Settings", "Accounts Settings", "automatically_run_rules_on_unreconciled_transactions", checked ? 1 : 0).then(() => {
setAutomaticallyRunRulesOnUnreconciledTransactions({
message: {
automatically_run_rules_on_unreconciled_transactions: checked ? 1 : 0,
}
}, {
revalidate: false
})
}), {
loading: _("Updating..."),
success: checked ? _("Scheduled job enabled. Transactions will be auto classified.") : _("Scheduled job disabled. Transactions will not be auto classified."),
error: _("Failed to update auto classify transactions settings")
})
}
return <DropdownMenuCheckboxItem
checked={automaticallyRunRulesOnUnreconciledTransactions}
onCheckedChange={onAutoClassifyTransactions}>
<CalendarSyncIcon />
{_("Run rules automatically")}
</DropdownMenuCheckboxItem>
}
const RuleList = ({ setSelectedRule }: { setSelectedRule: (rule: string) => void }) => {
const { data, error, isLoading, mutate } = useGetRuleList()
const { db } = useContext(FrappeContext) as FrappeConfig
const sensors = useSensors(
useSensor(PointerSensor),
useSensor(KeyboardSensor, {
coordinateGetter: sortableKeyboardCoordinates,
})
)
const onDeleteRule = (ruleID: string) => {
toast.promise(db.deleteDoc("Bank Transaction Rule", ruleID).then(() => {
mutate()
}), {
loading: _("Deleting rule..."),
success: _("Rule deleted."),
error: _("Failed to delete rule.")
})
}
const handleDragEnd = async (event: DragEndEvent) => {
const { active, over } = event
if (active.id !== over?.id && data) {
const oldIndex = data.findIndex((rule) => rule.name === active.id)
const newIndex = data.findIndex((rule) => rule.name === over?.id)
const newData = arrayMove(data, oldIndex, newIndex)
// Update priorities based on new order
const updatePromises = newData.map((rule, index) => {
const newPriority = index + 1
if (rule.priority !== newPriority) {
return db.setValue("Bank Transaction Rule", rule.name, "priority", newPriority)
}
return Promise.resolve()
})
try {
await Promise.all(updatePromises)
toast.success(_("Rule priorities updated"))
mutate() // Refresh the data
} catch (error) {
toast.error(_("Failed to update rule priorities"))
console.error("Error updating priorities:", error)
}
}
}
return (
<>
<div className="overflow-y-auto">
{isLoading && <div className="flex flex-col gap-2">
<Skeleton className="w-full h-10" />
<Skeleton className="w-full h-10" />
<Skeleton className="w-full h-10" />
<Skeleton className="w-full h-10" />
<Skeleton className="w-full h-10" />
</div>}
{error && <ErrorBanner error={error} />}
{data && data.length === 0 && <Empty className="h-96">
<EmptyMedia>
<ZapIcon />
</EmptyMedia>
<EmptyHeader>
<EmptyTitle>{_("No rules setup yet")}</EmptyTitle>
<EmptyDescription>{_("Configure rules to save time when reconciling transactions.")}</EmptyDescription>
</EmptyHeader>
</Empty>}
{data && data.length > 0 && (
<DndContext
sensors={sensors}
collisionDetection={closestCenter}
onDragEnd={handleDragEnd}
>
<SortableContext
items={data.map(rule => rule.name)}
strategy={verticalListSortingStrategy}
>
<ul className="space-2 divide-y divide-outline-gray-modals">
{data?.map((rule) => (
<SortableRuleItem
key={rule.name}
rule={rule}
setSelectedRule={setSelectedRule}
onDeleteRule={onDeleteRule}
/>
))}
</ul>
</SortableContext>
</DndContext>
)}
</div>
</>
)
}
const SortableRuleItem = ({
rule,
setSelectedRule,
onDeleteRule
}: {
rule: BankTransactionRule
setSelectedRule: (rule: string) => void
onDeleteRule: (ruleID: string) => void
}) => {
const {
attributes,
listeners,
setNodeRef,
transform,
transition,
isDragging,
} = useSortable({ id: rule.name })
const style = {
transform: CSS.Transform.toString(transform),
transition,
opacity: isDragging ? 0.5 : 1,
}
const [isDropdownOpen, setIsDropdownOpen] = useState(false)
return (
<li ref={setNodeRef} style={style}>
<div className={cn("flex justify-between items-center py-2 my-0.5 h-full hover:bg-surface-gray-1 pe-2 rounded", isDropdownOpen && "bg-surface-gray-1")}>
<div className="flex items-center gap-2">
<div
{...attributes}
{...listeners}
className="cursor-grab active:cursor-grabbing p-1 rounded"
title={_("Drag to reorder")}
>
<GripVertical className="w-4 h-4 text-ink-gray-5" />
</div>
<Badge theme="gray" className="font-numeric tabular-nums">
{rule.priority}
</Badge>
<div className="flex flex-col gap-1">
<div className="flex items-center gap-2">
<Button
variant='link'
size='sm'
className="p-0 h-fit text-start cursor-pointer no-underline hover:underline"
onClick={() => setSelectedRule(rule.name)}>
{rule.rule_name}
</Button>
<div title={rule.transaction_type === "Any" ? _("Applies to withdrawals and deposits") : rule.transaction_type === "Withdrawal" ? _("Applies to withdrawals") : _("Applies to deposits")}>
{rule.transaction_type === "Any" ? <ArrowDownUp className="text-ink-gray-5 w-4 h-4" /> : rule.transaction_type === "Withdrawal" ? <ArrowUpRight className="text-ink-red-3 w-5 h-5" /> : <ArrowDownRight className="text-ink-green-3 w-5 h-5" />}
</div>
</div>
<span className="text-sm text-ink-gray-5">
{rule.rule_description}
</span>
</div>
</div>
<div className="flex items-center gap-2 h-full justify-center">
<DropdownMenu open={isDropdownOpen} onOpenChange={setIsDropdownOpen}>
<DropdownMenuTrigger asChild>
<Button variant='ghost' isIconButton className="hover:bg-transparent">
<MoreVertical />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => onDeleteRule(rule.name)}>
<Trash2 />
{_("Delete")}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
</li>
)
}
export default RuleList

View File

@@ -1,95 +0,0 @@
import { Button } from '@/components/ui/button'
import { Dialog, DialogTrigger } from '@/components/ui/dialog'
import {
SettingsDialog,
SettingsPanel,
SettingsPanels,
SettingsTabGroup,
SettingsTabItem,
SettingsTabs,
} from '@/components/ui/settings-dialog'
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
import _ from '@/lib/translate'
import { KeyboardIcon, SettingsIcon, SlidersVerticalIcon, ZapIcon } from 'lucide-react'
import { useState } from 'react'
import { Preferences } from './Preferences'
import MatchingRules from './MatchingRules'
import KeyboardShortcuts from './KeyboardShortcuts'
import { useHotkeys } from 'react-hotkeys-hook'
const Settings = () => {
const [isOpen, setIsOpen] = useState(false)
useHotkeys('shift+meta+g', () => {
setIsOpen(x => !x)
}, {
enabled: true,
preventDefault: true,
enableOnFormTags: false
})
return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<Tooltip>
<TooltipTrigger asChild>
<DialogTrigger asChild>
<Button variant={'outline'} isIconButton size='md'>
<SettingsIcon />
</Button>
</DialogTrigger>
</TooltipTrigger>
<TooltipContent>
{_("Settings")}
</TooltipContent>
</Tooltip>
<SettingsDialog defaultValue="preferences" onClose={() => setIsOpen(false)}>
<SettingsTabs>
<SettingsTabGroup header={_("Settings")}>
<SettingsTabItem
icon={<SlidersVerticalIcon />}
label={_("Preferences")}
value="preferences"
/>
<SettingsTabItem
icon={<ZapIcon />}
label={_("Matching Rules")}
value="rules"
/>
{/* <SettingsTabItem
icon={<LandmarkIcon />}
label={_("Bank Accounts")}
value="bank-accounts"
/>
<SettingsTabItem
icon={<ListIcon />}
label={_("Masters")}
value="masters"
/> */}
<SettingsTabItem
icon={<KeyboardIcon />}
label={_("Keyboard Shortcuts")}
value="keyboard-shortcuts"
/>
</SettingsTabGroup>
</SettingsTabs>
<SettingsPanels>
<SettingsPanel value="preferences">
<Preferences />
</SettingsPanel>
<SettingsPanel value="rules">
<MatchingRules />
</SettingsPanel>
<SettingsPanel value="bank-accounts" />
<SettingsPanel value="masters" />
<SettingsPanel value="keyboard-shortcuts">
<KeyboardShortcuts />
</SettingsPanel>
</SettingsPanels>
</SettingsDialog>
</Dialog >
)
}
export default Settings

View File

@@ -1,196 +0,0 @@
import * as React from "react"
import { AlertDialog as AlertDialogPrimitive } from "radix-ui"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
function AlertDialog({
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Root>) {
return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />
}
function AlertDialogTrigger({
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) {
return (
<AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
)
}
function AlertDialogPortal({
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) {
return (
<AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
)
}
function AlertDialogOverlay({
className,
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Overlay>) {
return (
<AlertDialogPrimitive.Overlay
data-slot="alert-dialog-overlay"
className={cn(
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black-200 dark:bg-black-700",
className
)}
{...props}
/>
)
}
function AlertDialogContent({
className,
size = "default",
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Content> & {
size?: "default" | "sm"
}) {
return (
<AlertDialogPortal>
<AlertDialogOverlay />
<AlertDialogPrimitive.Content
data-slot="alert-dialog-content"
data-size={size}
className={cn(
"bg-surface-modal shadow-xl rounded-xl data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 group/alert-dialog-content fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 p-6 duration-200 data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-lg",
className
)}
{...props}
/>
</AlertDialogPortal>
)
}
function AlertDialogHeader({
className,
...props
}: React.ComponentProps<"div">) {
return (
<div
data-slot="alert-dialog-header"
className={cn(
"grid grid-rows-[auto_1fr] place-items-center gap-1.5 has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-6 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-start sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]",
className
)}
{...props}
/>
)
}
function AlertDialogFooter({
className,
...props
}: React.ComponentProps<"div">) {
return (
<div
data-slot="alert-dialog-footer"
className={cn(
"flex flex-col-reverse gap-2 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end",
className
)}
{...props}
/>
)
}
function AlertDialogTitle({
className,
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
return (
<AlertDialogPrimitive.Title
data-slot="alert-dialog-title"
className={cn(
"text-2xl leading-6 text-ink-gray-8 font-semibold sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2",
className
)}
{...props}
/>
)
}
function AlertDialogDescription({
className,
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
return (
<AlertDialogPrimitive.Description
data-slot="alert-dialog-description"
className={cn("text-ink-gray-7 text-p-base", className)}
{...props}
/>
)
}
function AlertDialogMedia({
className,
...props
}: React.ComponentProps<"div">) {
return (
<div
data-slot="alert-dialog-media"
className={cn(
"bg-surface-gray-1 mb-2 inline-flex size-16 items-center justify-center rounded-md sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-8",
className
)}
{...props}
/>
)
}
function AlertDialogAction({
className,
variant = "solid",
size = "md",
theme = "red",
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Action> &
Pick<React.ComponentProps<typeof Button>, "variant" | "size" | "theme">) {
return (
<Button variant={variant} size={size} theme={theme} asChild>
<AlertDialogPrimitive.Action
data-slot="alert-dialog-action"
className={cn(className)}
{...props}
/>
</Button>
)
}
function AlertDialogCancel({
className,
variant = "outline",
size = "md",
theme = "gray",
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Cancel> &
Pick<React.ComponentProps<typeof Button>, "variant" | "size" | "theme">) {
return (
<Button variant={variant} size={size} asChild>
<AlertDialogPrimitive.Cancel
data-slot="alert-dialog-cancel"
className={cn(className)}
{...props}
/>
</Button>
)
}
export {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogMedia,
AlertDialogOverlay,
AlertDialogPortal,
AlertDialogTitle,
AlertDialogTrigger,
}

View File

@@ -1,104 +0,0 @@
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const alertVariants = cva(
"relative w-full rounded-lg border px-4 py-3.5 text-base grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-1 [&>svg]:text-current",
{
variants: {
variant: {
subtle: "bg-surface-white",
outline: "border border-outline-gray-3",
},
theme: {
gray: "text-ink-gray-8",
blue: "text-ink-blue-3",
green: "text-ink-green-3",
red: "text-ink-red-3",
amber: "text-ink-amber-3",
}
},
compoundVariants: [
// Subtle alerts
{
theme: "gray",
variant: "subtle",
className: "bg-surface-gray-2 border-outline-gray-1"
},
{
theme: "blue",
variant: "subtle",
className: "bg-surface-blue-2 border-surface-blue-2"
},
{
theme: "green",
variant: "subtle",
className: "bg-surface-green-2 border-surface-green-2"
},
{
theme: "red",
variant: "subtle",
className: "bg-surface-red-2 border-surface-red-2"
},
{
theme: "amber",
variant: "subtle",
className: "bg-surface-amber-2 border-surface-amber-2"
}
],
defaultVariants: {
variant: "subtle",
theme: "gray",
},
}
)
export type AlertProps = React.ComponentProps<"div"> & VariantProps<typeof alertVariants>
function Alert({
className,
variant,
theme,
...props
}: AlertProps) {
return (
<div
data-slot="alert"
role="alert"
className={cn(alertVariants({ variant, theme }), className)}
{...props}
/>
)
}
function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="alert-title"
className={cn(
"col-start-2 min-h-4 text-ink-gray-8 font-medium text-p-base",
className
)}
{...props}
/>
)
}
function AlertDescription({
className,
...props
}: React.ComponentProps<"div">) {
return (
<div
data-slot="alert-description"
className={cn(
"text-ink-gray-6 col-start-2 grid justify-items-start gap-1 text-p-base",
className
)}
{...props}
/>
)
}
export { Alert, AlertTitle, AlertDescription }

Some files were not shown because too many files have changed in this diff Show More