Compare commits

..

521 Commits

Author SHA1 Message Date
Frappe PR Bot
b424a184b9 chore(release): Bumped to Version 14.65.0
# [14.65.0](https://github.com/frappe/erpnext/compare/v14.64.0...v14.65.0) (2024-03-06)

### Bug Fixes

* account validation error on bank account after editing existing bank account ([8b3c809](8b3c809cb6))
* allow gain/loss for Journals against Journals ([52e1c2f](52e1c2f48b))
* do not allow to cancel incomplete reposting (backport [#40224](https://github.com/frappe/erpnext/issues/40224)) ([#40229](https://github.com/frappe/erpnext/issues/40229)) ([091d6f3](091d6f330a))
* don't override reference exchange rate ([e25ec41](e25ec4156e))
* handle partial invoice against provisional entry ([5f23614](5f23614960))
* incorrect exchange rate if JE has multi parties ([1b5a237](1b5a23709a))
* incorrect TCS on customer and suppliers with same name ([af56846](af568464db))
* make use of 'flt' to prevent really low precision exc gain/loss ([c1a0ac6](c1a0ac655e))
* make warning for previously existing SO an alert ([c8b817c](c8b817c87a))
* over billing qty along with rate ([14d9d29](14d9d29913))
* **Project:** filter department by company ([646b55e](646b55eeea))
* provisional reverse entry amount ([e62c49d](e62c49d9a6))
* rate change on changing of the qty (backport [#40241](https://github.com/frappe/erpnext/issues/40241)) ([#40242](https://github.com/frappe/erpnext/issues/40242)) ([b937c4b](b937c4be4f))
* **setup:** avoid duplicate entry for Analytics role (backport [#40183](https://github.com/frappe/erpnext/issues/40183)) ([#40184](https://github.com/frappe/erpnext/issues/40184)) ([035c90c](035c90c3b8))

### Features

* add company filter to child table field ([38baf8d](38baf8d406))
2024-03-06 03:31:16 +00:00
Deepesh Garg
0b364f6e8e Merge pull request #40281 from frappe/version-14-hotfix
chore: release v14
2024-03-06 08:59:57 +05:30
ruthra kumar
83734c6be4 Merge pull request #40283 from frappe/mergify/bp/version-14-hotfix/pr-40278
fix: incorrect TCS on customer and suppliers with same name (backport #40278)
2024-03-05 15:48:27 +05:30
ruthra kumar
af568464db fix: incorrect TCS on customer and suppliers with same name
(cherry picked from commit 9904a9868c)
2024-03-05 09:54:47 +00:00
mergify[bot]
091d6f330a fix: do not allow to cancel incomplete reposting (backport #40224) (#40229)
* fix: do not allow to cancel incomplete reposting (#40224)

(cherry picked from commit 72ac56b6c4)

# Conflicts:
#	erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-03-04 13:14:32 +05:30
Gursheen Kaur Anand
cc923f50ef Merge pull request #40257 from GursheenK/remove-balance-fetching-in-JV
perf: remove unnecessary fetching of balances in JV
2024-03-04 11:16:01 +05:30
Gursheen Anand
2f8fe17b60 refactor: remove controller logic for setting balances 2024-03-04 00:37:29 +05:30
Gursheen Anand
e3d6f70eee refactor: exclude balances while setting currency 2024-03-04 00:37:26 +05:30
Gursheen Anand
a1c22811a2 refactor: exclude balance while setting acc details 2024-03-04 00:35:54 +05:30
Gursheen Anand
d715c29edc refactor: remove balance formatter 2024-03-04 00:35:54 +05:30
Gursheen Anand
85a7ce6d30 refactor: remove balance fields from jv account 2024-03-04 00:35:51 +05:30
Gursheen Kaur Anand
c42d7505bd Merge pull request #40204 from frappe/mergify/bp/version-14-hotfix/pr-39584
fix: use receipt amount for reverse provisional entry (backport #39584)
2024-03-03 18:24:57 +05:30
Gursheen Kaur Anand
29f4e68827 Merge pull request #40251 from frappe/mergify/bp/version-14-hotfix/pr-40250
fix(minor): company filter in cost center allocation (backport #40250)
2024-03-03 18:24:42 +05:30
Gursheen Anand
38baf8d406 feat: add company filter to child table field
(cherry picked from commit 51909077bd)
2024-03-03 12:45:33 +00:00
Gursheen Anand
89e7ad790f chore: linting issue 2024-03-03 18:00:10 +05:30
mergify[bot]
b937c4be4f fix: rate change on changing of the qty (backport #40241) (#40242)
fix: rate change on changing of the qty (#40241)

(cherry picked from commit e7d707797a)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-03-02 16:49:40 +05:30
ruthra kumar
793fdf9562 Merge pull request #40238 from frappe/mergify/bp/version-14-hotfix/pr-40220
refactor: Gain/Loss Journal creation for Journals against Journals (backport #40220)
2024-03-02 16:25:32 +05:30
ruthra kumar
753223da78 chore: resolve merge conflict 2024-03-02 16:00:29 +05:30
ruthra kumar
46063518d7 test: gain/loss on Journals against Journals
(cherry picked from commit 8a5078b826)

# Conflicts:
#	erpnext/controllers/tests/test_accounts_controller.py
2024-03-02 10:07:33 +00:00
ruthra kumar
52e1c2f48b fix: allow gain/loss for Journals against Journals
(cherry picked from commit 5b67631d40)

# Conflicts:
#	erpnext/accounts/doctype/journal_entry/journal_entry.py
2024-03-02 10:07:32 +00:00
ruthra kumar
c1a0ac655e fix: make use of 'flt' to prevent really low precision exc gain/loss
(cherry picked from commit 0aa72f841d)
2024-03-02 10:07:31 +00:00
ruthra kumar
21a10eee0d Merge pull request #40232 from frappe/mergify/bp/version-14-hotfix/pr-40149
fix: incorrect exchange rate if JE has multi parties (backport #40149)
2024-03-02 15:36:15 +05:30
ruthra kumar
64bf52899f chore: resolve linter 2024-03-02 14:57:48 +05:30
ruthra kumar
572def058f test: exchange rate fetch on JE with multiple forex parties
(cherry picked from commit ed95d41a51)
2024-03-02 14:39:37 +05:30
ruthra kumar
e25ec4156e fix: don't override reference exchange rate
(cherry picked from commit eaac02655b)
2024-03-02 08:22:41 +00:00
ruthra kumar
1b5a23709a fix: incorrect exchange rate if JE has multi parties
(cherry picked from commit 694c17487d)
2024-03-02 08:22:38 +00:00
Raffael Meyer
d7abb21f7b Merge pull request #40209 from frappe/mergify/bp/version-14-hotfix/pr-40203
fix(Project): filter department by company (backport #40203)
2024-02-29 23:34:17 +01:00
barredterra
646b55eeea fix(Project): filter department by company
(cherry picked from commit 5e736f0d06)
2024-02-29 17:30:05 +00:00
Gursheen Anand
c8b31833f5 test: partial billing for provisional accounting
(cherry picked from commit c5770f2ecc)
2024-02-29 15:48:37 +00:00
Gursheen Anand
aa52cd67bd test: overbilling for provisional accounting
(cherry picked from commit ff3ca50a4b)
2024-02-29 15:48:36 +00:00
Gursheen Anand
14d9d29913 fix: over billing qty along with rate
(cherry picked from commit cc96d2b50c)
2024-02-29 15:48:36 +00:00
Gursheen Anand
5f23614960 fix: handle partial invoice against provisional entry
(cherry picked from commit 2a46799188)
2024-02-29 15:48:36 +00:00
Gursheen Anand
e62c49d9a6 fix: provisional reverse entry amount
(cherry picked from commit 3e59c66806)
2024-02-29 15:48:36 +00:00
ruthra kumar
fca70e3d03 Merge pull request #40039 from SherinKR/bank_account_validation
fix: account validation error on bank account after editing existing bank account
2024-02-29 17:38:34 +05:30
Gursheen Kaur Anand
aeb555a426 Merge pull request #40188 from frappe/mergify/bp/version-14-hotfix/pr-40176
fix(minor): make warning for previously existing SO an alert (backport #40176)
2024-02-29 11:59:44 +05:30
mergify[bot]
035c90c3b8 fix(setup): avoid duplicate entry for Analytics role (backport #40183) (#40184)
fix(setup): avoid duplicate entry for Analytics role (#40183)

(cherry picked from commit 29f91a7919)

Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2024-02-29 11:34:23 +05:30
Gursheen Anand
c8b817c87a fix: make warning for previously existing SO an alert
(cherry picked from commit 24dcd64c16)
2024-02-29 06:03:26 +00:00
Frappe PR Bot
de6957262d chore(release): Bumped to Version 14.64.0
# [14.64.0](https://github.com/frappe/erpnext/compare/v14.63.3...v14.64.0) (2024-02-28)

### Bug Fixes

* add flags for repost to ensure correct accounting from India Compliance App ([8c772cf](8c772cfb9f))
* amount label according to party type ([bb3a7cd](bb3a7cdb3d))
* Cannot read properties of undefined (backport [#40081](https://github.com/frappe/erpnext/issues/40081)) ([#40082](https://github.com/frappe/erpnext/issues/40082)) ([579b27d](579b27d010))
* capacity planning issue in the job card (backport [#40092](https://github.com/frappe/erpnext/issues/40092)) ([#40100](https://github.com/frappe/erpnext/issues/40100)) ([8a573a1](8a573a1aff))
* check_credit_limit on_update_after_submit of Sales Order ([3112fca](3112fca087))
* communication_date in party dashboards (backport [#40005](https://github.com/frappe/erpnext/issues/40005)) ([#40020](https://github.com/frappe/erpnext/issues/40020)) ([b364fe7](b364fe7047))
* Completed Work Orders report not working ([c8e1409](c8e1409b80))
* Cr/Dr notes with POS Payments ([e5098c5](e5098c521b))
* default taxable value for item not found in item list ([30315ab](30315aba37))
* Delete linked asset movement record on cancellation of purchase receipt/invoice ([11bddc1](11bddc14bb))
* Delete orphaned asset movement item records ([46cd929](46cd929d00))
* delete PLE containing invoice in against ([08459ce](08459cef4a))
* do not make MR against raw materials of available sub assemblies (backport [#40085](https://github.com/frappe/erpnext/issues/40085)) ([#40086](https://github.com/frappe/erpnext/issues/40086)) ([e461185](e461185317))
* incorrect item name in MR (backport [#40018](https://github.com/frappe/erpnext/issues/40018)) ([#40023](https://github.com/frappe/erpnext/issues/40023)) ([079ba76](079ba7670c))
* linter issue ([ea1a0b3](ea1a0b3a28))
* linting issue ([65a2adf](65a2adfc55))
* linting issue ([e289aef](e289aef1b4))
* negative stock error while making stock reconciliation (backport [#40016](https://github.com/frappe/erpnext/issues/40016)) ([#40025](https://github.com/frappe/erpnext/issues/40025)) ([865cba4](865cba406f))
* on unreconciliation, update advance paid ([fe88a11](fe88a110b1))
* only check for delinked PLEs ([3d243a7](3d243a7fa5))
* only consider contributed qty towards achieved targets ([0484857](0484857402))
* parent warehouse checks in the production plan for sub-assemblies (backport [#40150](https://github.com/frappe/erpnext/issues/40150)) ([#40156](https://github.com/frappe/erpnext/issues/40156)) ([9a85ab4](9a85ab45e7))
* remove cancelled payment entry from Payment Period Based On Invoice Date ([2fc490a](2fc490a5d7))
* remove cancelled payment entry from PPBOID report ([a1c0c23](a1c0c2359f))
* remove config for default bank account in test ([ad60677](ad6067795f))
* sales funnel text color in light/dark theme ([#40132](https://github.com/frappe/erpnext/issues/40132)) ([193cd0d](193cd0db96))
* skip max discount validation for rate adjustment ([8c7d0d4](8c7d0d4b85))
* skip SO & DN validation for debit note ([882cf98](882cf98288))
* test for plaid bank account validation ([22db6f6](22db6f6b72))
* timesheet per billed state edge case (backport [#40010](https://github.com/frappe/erpnext/issues/40010)) ([#40028](https://github.com/frappe/erpnext/issues/40028)) ([2a2f314](2a2f314821))
* translatable columns in Sales Pipeline Analytics report ([3c170b9](3c170b9805))
* unique gl account for plaid bank accounts ([8638d14](8638d14bab))
* use correct variable name for hotfix branches ([812eebf](812eebf1ab))
* Webpages not working without login ([d6fad08](d6fad08d20))

### Features

* show contributed qty in transaction summary ([89d1942](89d19422d1))
* update billed amount in PO and PR ([019d8f9](019d8f92fe))
2024-02-28 05:13:29 +00:00
Deepesh Garg
e80bfd28ea Merge pull request #40147 from frappe/version-14-hotfix
chore: release v14
2024-02-28 10:42:13 +05:30
Deepesh Garg
19a7b6d122 Merge pull request #40169 from frappe/mergify/bp/version-14-hotfix/pr-40137
fix: default taxable value for item not found in item list (backport #40137)
2024-02-28 08:23:57 +05:30
ljain112
30315aba37 fix: default taxable value for item not found in item list
(cherry picked from commit 5885978fc2)
2024-02-27 16:39:03 +00:00
Deepesh Garg
1cc3351e6f Merge pull request #40164 from vorasmit/ic-deps-v14
fix: add flags for repost to ensure correct accounting from India Compliance App (#40162)
2024-02-27 22:08:29 +05:30
Smit Vora
8c772cfb9f fix: add flags for repost to ensure correct accounting from India Compliance App 2024-02-27 20:33:02 +05:30
Gursheen Kaur Anand
0b1689a3aa Merge pull request #40151 from frappe/mergify/bp/version-14-hotfix/pr-40095
fix: unique GL account for plaid bank accounts (backport #40095)
2024-02-27 17:47:32 +05:30
Gursheen Anand
88af431eea chore: semantic commits bump node version to 20 2024-02-27 17:20:01 +05:30
mergify[bot]
9a85ab45e7 fix: parent warehouse checks in the production plan for sub-assemblies (backport #40150) (#40156)
fix: parent warehouse checks in the production plan for sub-assemblies

(cherry picked from commit 6f5815e44f)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-02-27 17:18:24 +05:30
rohitwaghchaure
9ac09d1b8b Merge branch 'version-14' into version-14-hotfix 2024-02-27 17:16:56 +05:30
Gursheen Anand
22db6f6b72 fix: test for plaid bank account validation 2024-02-27 16:51:48 +05:30
Gursheen Kaur Anand
42382d2a0f Merge pull request #40153 from frappe/mergify/bp/version-14-hotfix/pr-40102
feat: toggle updation of billed amount in previous purchase docs (backport #40102)
2024-02-27 16:40:28 +05:30
Gursheen Kaur Anand
614119b3e1 chore: resolve conflicts 2024-02-27 16:04:34 +05:30
Gursheen Kaur Anand
8f983e6109 chore: resolve conflicts 2024-02-27 16:01:33 +05:30
Gursheen Kaur Anand
42b7be60ef chore: resolve conflicts 2024-02-27 15:57:43 +05:30
Gursheen Anand
a84fc0fe40 test: pr billed amount against debit note
(cherry picked from commit 6d40844894)
2024-02-27 10:23:59 +00:00
Gursheen Anand
4f87e73f55 test: po billed amount against debit note
(cherry picked from commit 81dbfe189e)

# Conflicts:
#	erpnext/buying/doctype/purchase_order/test_purchase_order.py
2024-02-27 10:23:58 +00:00
Gursheen Anand
019d8f92fe feat: update billed amount in PO and PR
(cherry picked from commit 9f6535472d)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
#	erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
2024-02-27 10:23:57 +00:00
Gursheen Anand
ad6067795f fix: remove config for default bank account in test
(cherry picked from commit c42444ab3b)
2024-02-27 10:10:18 +00:00
Gursheen Anand
8638d14bab fix: unique gl account for plaid bank accounts
(cherry picked from commit bf6e32a960)
2024-02-27 10:10:18 +00:00
Gursheen Kaur Anand
317738dc36 Merge pull request #40063 from frappe/mergify/bp/version-14-hotfix/pr-40035
fix: delete PLE containing invoice in against (backport #40035)
2024-02-27 12:41:42 +05:30
Gursheen Anand
65a2adfc55 fix: linting issue 2024-02-27 12:16:08 +05:30
Gursheen Kaur Anand
674f577647 Merge pull request #40065 from frappe/mergify/bp/version-14-hotfix/pr-40040
fix: skip max discount validation for rate adjustment (backport #40040)
2024-02-27 10:51:02 +05:30
Gursheen Anand
e289aef1b4 fix: linting issue 2024-02-27 09:59:08 +05:30
Nihantra C. Patel
193cd0db96 fix: sales funnel text color in light/dark theme (#40132)
* fix: sales funnel text color in light/dark theme

* fix: sales funnel text color in light/dark theme
2024-02-26 23:01:41 +05:30
ruthra kumar
ac3c23922a Merge pull request #40134 from frappe/mergify/bp/version-14-hotfix/pr-40133
fix: reset advance amount on unreconciliation of Sales/Purchase Orders (backport #40133)
2024-02-26 21:32:23 +05:30
ruthra kumar
812eebf1ab fix: use correct variable name for hotfix branches 2024-02-26 20:32:48 +05:30
mergify[bot]
8a573a1aff fix: capacity planning issue in the job card (backport #40092) (#40100)
* fix: capacity planning issue in the job card (#40092)

* fix: capacity planning issue in the job card

* test: test case to test capacity planning for workstation

(cherry picked from commit 75f8464724)

# Conflicts:
#	erpnext/manufacturing/doctype/job_card/job_card.py
#	erpnext/manufacturing/doctype/work_order/test_work_order.py

* chore: fix conflicts

* chore: fix conflicts

* chore: fix test cases

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-02-26 18:52:05 +05:30
ruthra kumar
58c869c562 test: advance paid update on sales/purchase order unreconciliation
(cherry picked from commit 1f01ff3487)
2024-02-26 12:22:33 +00:00
ruthra kumar
fe88a110b1 fix: on unreconciliation, update advance paid
(cherry picked from commit c9e2f03a3a)
2024-02-26 12:22:33 +00:00
ruthra kumar
5772cb1953 Merge pull request #40117 from frappe/mergify/bp/version-14-hotfix/pr-40113
refactor: patch to setup dimensions in Reconciliation tool (backport #40113)
2024-02-26 14:21:49 +05:30
ruthra kumar
6828a2d46d refactor: patch to setup dimensions in Reconciliation tool
(cherry picked from commit 461fb183fc)
2024-02-26 13:10:44 +05:30
Gursheen Kaur Anand
8e1ec5ff31 Merge pull request #40108 from frappe/mergify/bp/version-14-hotfix/pr-40105
fix(minor): tax amount label according to party type (backport #40105)
2024-02-26 11:09:53 +05:30
Gursheen Kaur Anand
48dd8acfa1 Merge pull request #40110 from frappe/mergify/bp/version-14-hotfix/pr-40062
fix: sales person / partner achieved targets in report (backport #40062)
2024-02-26 11:08:43 +05:30
Gursheen Anand
57f4eb121c test: sales person target variance
(cherry picked from commit 7566c1ee78)
2024-02-26 05:09:41 +00:00
Gursheen Anand
89d19422d1 feat: show contributed qty in transaction summary
(cherry picked from commit a823f16dff)
2024-02-26 05:09:40 +00:00
Gursheen Anand
0484857402 fix: only consider contributed qty towards achieved targets
(cherry picked from commit 339698d172)
2024-02-26 05:09:40 +00:00
Gursheen Anand
bb3a7cdb3d fix: amount label according to party type
(cherry picked from commit 9c8d103d8a)
2024-02-26 05:05:27 +00:00
ruthra kumar
b3735d29bf Merge pull request #40090 from frappe/mergify/bp/version-14-hotfix/pr-40073
refactor: update payments section on item removal (backport #40073)
2024-02-25 09:29:51 +05:30
ruthra kumar
4bc6c55198 refactor: update payments section on item removal
(cherry picked from commit 406793a6ff)
2024-02-25 03:57:39 +00:00
mergify[bot]
e461185317 fix: do not make MR against raw materials of available sub assemblies (backport #40085) (#40086)
fix: do not make MR against raw materials of available sub assemblies (#40085)

(cherry picked from commit 4c9048fb39)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-02-24 21:02:45 +05:30
mergify[bot]
579b27d010 fix: Cannot read properties of undefined (backport #40081) (#40082)
* fix: Cannot read properties of undefined

(cherry picked from commit 44ed52c5cf)

* chore: fix linter issue

---------

Co-authored-by: Rohit Waghchaure <rohitw1991@gmail.com>
2024-02-24 17:22:37 +05:30
ruthra kumar
2b01b102b4 Merge pull request #40067 from frappe/mergify/bp/version-14-hotfix/pr-39830
fix: check_credit_limit on_update_after_submit of Sales Order (backport #39830)
2024-02-23 14:50:25 +05:30
ruthra kumar
db7c360fa1 test: credit limit on update after submit
(cherry picked from commit 467c0898e9)
2024-02-23 08:51:38 +00:00
Nihantra C. Patel
3112fca087 fix: check_credit_limit on_update_after_submit of Sales Order
(cherry picked from commit 17452b7693)
2024-02-23 08:51:38 +00:00
Gursheen Anand
882cf98288 fix: skip SO & DN validation for debit note
(cherry picked from commit e2d16955dd)
2024-02-23 07:28:17 +00:00
Gursheen Anand
8c7d0d4b85 fix: skip max discount validation for rate adjustment
(cherry picked from commit 5a3b133d65)
2024-02-23 07:28:17 +00:00
Ankush Menat
96703fb34d build: specify frappe dependency (#40060) 2024-02-23 12:42:10 +05:30
Gursheen Anand
3d243a7fa5 fix: only check for delinked PLEs
(cherry picked from commit 146c5b3e16)
2024-02-23 07:10:03 +00:00
Gursheen Anand
08459cef4a fix: delete PLE containing invoice in against
(cherry picked from commit c1e1fd8829)
2024-02-23 07:10:02 +00:00
ruthra kumar
05a9f766bc Merge pull request #40057 from frappe/mergify/bp/version-14-hotfix/pr-40011
fix: Ledger entries for Cr/Dr notes with POS Payments (backport #40011)
2024-02-23 06:20:46 +05:30
ruthra kumar
6eb7a18591 Merge pull request #40055 from frappe/mergify/bp/version-14-hotfix/pr-39831
fix: remove cancelled payment entry from Payment Period Based On Invoice Date (backport #39831)
2024-02-23 06:20:06 +05:30
ruthra kumar
9e9486d5ab refactor: skip popup for POS invoices
(cherry picked from commit 3634c4c284)
2024-02-23 00:33:09 +00:00
ruthra kumar
a9791c85c7 test: ledger entries of Cr Note of POS Invoice
(cherry picked from commit 4288713abe)
2024-02-23 00:33:09 +00:00
ruthra kumar
e5098c521b fix: Cr/Dr notes with POS Payments
(cherry picked from commit 68a23730f3)
2024-02-23 00:33:08 +00:00
Nihantra C. Patel
a1c0c2359f fix: remove cancelled payment entry from PPBOID report
(cherry picked from commit 186cc3d748)
2024-02-23 00:30:49 +00:00
Nihantra C. Patel
2fc490a5d7 fix: remove cancelled payment entry from Payment Period Based On Invoice Date
(cherry picked from commit a2a8a8f2e0)
2024-02-23 00:30:48 +00:00
ruthra kumar
9630dc7b88 Merge pull request #40048 from frappe/mergify/bp/version-14-hotfix/pr-39828
fix: translate Sales Pipeline Analytics report (backport #39828)
2024-02-22 20:47:16 +05:30
Nihantra C. Patel
3c170b9805 fix: translatable columns in Sales Pipeline Analytics report
(cherry picked from commit c5050c935b)
2024-02-22 14:54:51 +00:00
Frappe PR Bot
95197c0532 chore(release): Bumped to Version 14.63.3
## [14.63.3](https://github.com/frappe/erpnext/compare/v14.63.2...v14.63.3) (2024-02-22)

### Bug Fixes

* communication_date in party dashboards (backport [#40005](https://github.com/frappe/erpnext/issues/40005)) (backport [#40020](https://github.com/frappe/erpnext/issues/40020)) ([#40044](https://github.com/frappe/erpnext/issues/40044)) ([23e32b9](23e32b9b33))
2024-02-22 13:01:09 +00:00
mergify[bot]
23e32b9b33 fix: communication_date in party dashboards (backport #40005) (backport #40020) (#40044)
fix: communication_date in party dashboards (backport #40005) (#40020)

fix: accommodate for changed orderby statement

(cherry picked from commit 87df7ff717)

Co-authored-by: Gursheen Anand <gursheen@frappe.io>
(cherry picked from commit b364fe7047)

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2024-02-22 18:29:56 +05:30
Frappe PR Bot
0653f6854b chore(release): Bumped to Version 14.63.2
## [14.63.2](https://github.com/frappe/erpnext/compare/v14.63.1...v14.63.2) (2024-02-22)

### Bug Fixes

* Webpages not working without login ([a40e410](a40e410519))
2024-02-22 11:58:43 +00:00
rohitwaghchaure
ee125d5dd8 Merge pull request #40045 from frappe/mergify/bp/version-14/pr-40041
fix: Webpages not working without login (backport #40041)
2024-02-22 17:27:27 +05:30
Rohit Waghchaure
a40e410519 fix: Webpages not working without login
(cherry picked from commit d6fad08d20)
2024-02-22 11:22:20 +00:00
rohitwaghchaure
9d723df9b2 Merge pull request #40041 from rohitwaghchaure/fixed-not-able-to-view-pages
fix: Webpages not working without login
2024-02-22 16:51:25 +05:30
Rohit Waghchaure
d6fad08d20 fix: Webpages not working without login 2024-02-22 16:24:30 +05:30
mergify[bot]
b364fe7047 fix: communication_date in party dashboards (backport #40005) (#40020)
fix: accommodate for changed orderby statement

(cherry picked from commit 87df7ff717)

Co-authored-by: Gursheen Anand <gursheen@frappe.io>
2024-02-22 16:20:15 +05:30
SherinKR
8b3c809cb6 fix: account validation error on bank account after editing existing bank account 2024-02-22 15:56:58 +05:30
Frappe PR Bot
315d4dec90 chore(release): Bumped to Version 14.63.1
## [14.63.1](https://github.com/frappe/erpnext/compare/v14.63.0...v14.63.1) (2024-02-22)

### Bug Fixes

* negative stock error while making stock reconciliation (backport [#40016](https://github.com/frappe/erpnext/issues/40016)) ([#40025](https://github.com/frappe/erpnext/issues/40025)) ([7355fce](7355fce75e))
2024-02-22 08:18:35 +00:00
rohitwaghchaure
c826b5ce6c Merge pull request #40034 from frappe/mergify/bp/version-14/pr-40025
fix: negative stock error while making stock reconciliation (backport #40016) (backport #40025)
2024-02-22 13:46:44 +05:30
mergify[bot]
7355fce75e fix: negative stock error while making stock reconciliation (backport #40016) (#40025)
* fix: negative stock error while making stock reconciliation (#40016)

fix: negative stock error while making stock reco
(cherry picked from commit da184d709b)

# Conflicts:
#	erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py

* chore: fix conflicts

* chore: fix linter issue

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
(cherry picked from commit 865cba406f)
2024-02-22 07:49:51 +00:00
mergify[bot]
2a2f314821 fix: timesheet per billed state edge case (backport #40010) (#40028)
fix: timesheet per billed state edge case (#40010)

If value is 100.0000x then it won't set status correctly but will set it the next time it's loaded from db.

(cherry picked from commit 38e88db2c9)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2024-02-22 12:29:56 +05:30
mergify[bot]
865cba406f fix: negative stock error while making stock reconciliation (backport #40016) (#40025)
* fix: negative stock error while making stock reconciliation (#40016)

fix: negative stock error while making stock reco
(cherry picked from commit da184d709b)

# Conflicts:
#	erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py

* chore: fix conflicts

* chore: fix linter issue

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-02-22 11:39:48 +05:30
mergify[bot]
079ba7670c fix: incorrect item name in MR (backport #40018) (#40023)
fix: incorrect item name in MR (#40018)

(cherry picked from commit 864d7ae04c)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-02-22 09:50:33 +05:30
rohitwaghchaure
97b665b043 Merge pull request #40000 from frappe/mergify/bp/version-14-hotfix/pr-39998
fix: Completed Work Orders report not working (backport #39998)
2024-02-21 17:44:32 +05:30
Rohit Waghchaure
c8e1409b80 fix: Completed Work Orders report not working
(cherry picked from commit 11f4cb914a)
2024-02-21 11:20:43 +00:00
Nabin Hait
13a663c6cb Merge pull request #39971 from nabinhait/asset-movement-deletion-issue
fix: Delete linked asset movement record on cancellation of purchase receipt/invoice
2024-02-21 11:14:31 +05:30
Frappe PR Bot
8ab75560d5 chore(release): Bumped to Version 14.63.0
# [14.63.0](https://github.com/frappe/erpnext/compare/v14.62.4...v14.63.0) (2024-02-21)

### Bug Fixes

* 'NoneType' object is not iterable (backport [#39977](https://github.com/frappe/erpnext/issues/39977)) ([#39980](https://github.com/frappe/erpnext/issues/39980)) ([8e71665](8e71665e4f))
* Adjust amount in last row due to rounding ([963ddac](963ddac528))
* **Bank Transaction:** precision for `(un)allocated_amount` ([cea0e1f](cea0e1fb91)), closes [#39926](https://github.com/frappe/erpnext/issues/39926)
* fetch company terms ([83bf286](83bf28616e))
* group node in warehouse filter in Item-wise Sales Register ([4921b03](4921b038bd))
* **Issue:** create communication ([8deaba8](8deaba8def))
* linter issue ([a4fbea3](a4fbea3722))
* linter issue ([abceb1b](abceb1b611))
* linter issues ([e99485b](e99485bfa7))
* no need call for company method in sales invoice js ([45a0494](45a0494318))
* On cancelation of capitalization, reverse depreciation entry only if journal entry exists ([5b9905f](5b9905f27a))
* party item code in Blanket Order ([ee9a51f](ee9a51f93f))
* reconciliation issue due to notation difference ([dcf19c3](dcf19c3ed9))
* show active bom in the dropdown while making stock entry and MR (backport [#39974](https://github.com/frappe/erpnext/issues/39974)) ([#39975](https://github.com/frappe/erpnext/issues/39975)) ([385b08d](385b08dc50))
* typeerror 'Item Group' filter on Purchase Register ([b1a4249](b1a4249041))
* update_dimension is required and not need party account method ([5eaa11b](5eaa11b9e8))
* webpages are not showing ([#39988](https://github.com/frappe/erpnext/issues/39988)) ([64099b0](64099b0bf7))

### Features

* New financial views - Growth and margin views for P&L and balance sheet ([d913ec5](d913ec52db))
2024-02-21 05:30:04 +00:00
rohitwaghchaure
7561de1f4e Merge pull request #39978 from frappe/version-14-hotfix
chore: release v14
2024-02-21 10:58:47 +05:30
rohitwaghchaure
64099b0bf7 fix: webpages are not showing (#39988) 2024-02-21 09:26:58 +05:30
Nabin Hait
ea1a0b3a28 fix: linter issue 2024-02-21 08:43:10 +05:30
Nabin Hait
46cd929d00 fix: Delete orphaned asset movement item records 2024-02-20 18:36:23 +05:30
Nabin Hait
46762ead05 Merge pull request #39982 from nabinhait/asset-capitalization-cancel
fix: On cancelation of capitalization, reverse depreciation entry only if journal entry exists
2024-02-20 18:30:14 +05:30
Nabin Hait
5b9905f27a fix: On cancelation of capitalization, reverse depreciation entry only if journal entry exists 2024-02-20 17:54:58 +05:30
mergify[bot]
8e71665e4f fix: 'NoneType' object is not iterable (backport #39977) (#39980)
fix: 'NoneType' object is not iterable (#39977)

(cherry picked from commit 8e7d47b3a7)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-02-20 15:53:20 +05:30
mergify[bot]
385b08dc50 fix: show active bom in the dropdown while making stock entry and MR (backport #39974) (#39975)
fix: show active bom in the dropdown while making stock entry and MR (#39974)

(cherry picked from commit 133f8bd92a)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-02-20 13:13:35 +05:30
Nabin Hait
c53ced1224 Merge pull request #39874 from nabinhait/depreciation-last-row-adjustment
fix: Adjust amount in last row due to rounding
2024-02-20 13:13:02 +05:30
Nabin Hait
a4fbea3722 fix: linter issue 2024-02-20 12:31:34 +05:30
Nabin Hait
e99485bfa7 fix: linter issues 2024-02-20 12:24:36 +05:30
Nabin Hait
963ddac528 fix: Adjust amount in last row due to rounding 2024-02-20 12:24:36 +05:30
Nabin Hait
11bddc14bb fix: Delete linked asset movement record on cancellation of purchase receipt/invoice 2024-02-20 12:23:16 +05:30
Nabin Hait
947f138ea4 Merge pull request #39973 from nabinhait/linter-issue-1
Linter issue
2024-02-20 12:22:47 +05:30
Nabin Hait
abceb1b611 fix: linter issue 2024-02-20 12:21:12 +05:30
Nitali Mittal
d913ec52db feat: New financial views - Growth and margin views for P&L and balance sheet
feat: New financial views - Growth and margin views for P&L and balance sheet
2024-02-19 16:36:23 +05:30
ruthra kumar
e337a55a0b Merge pull request #39954 from frappe/mergify/bp/version-14-hotfix/pr-39914
refactor: add total row if only one party is being filtered (backport #39914)
2024-02-19 09:16:27 +05:30
ruthra kumar
310f338d9b Merge pull request #39952 from frappe/mergify/bp/version-14-hotfix/pr-39942
fix: group node in warehouse filter in Item-wise Sales Register (backport #39942)
2024-02-19 09:15:49 +05:30
ruthra kumar
b0c2d41ecb Merge pull request #39832 from ruthra-kumar/convert_latest_check_sql_to_qb
fix: reconciliation issue due to notation difference
2024-02-19 08:37:59 +05:30
ruthra kumar
241507a87f refactor: add total row if only one party is being filtered
(cherry picked from commit b1dfa2537b)
2024-02-19 02:56:40 +00:00
ruthra kumar
4921b038bd fix: group node in warehouse filter in Item-wise Sales Register
(cherry picked from commit 44538bd02a)
2024-02-19 02:56:18 +00:00
ruthra kumar
229e4f65b4 Merge pull request #39951 from ruthra-kumar/typerror_on_item_group_filter_in_purchase_register
fix: typerror on 'Item Group' filter on Item-Wise Purchase Register
2024-02-19 08:22:07 +05:30
ruthra kumar
a4f94ed631 Merge pull request #39949 from frappe/mergify/bp/version-14-hotfix/pr-39943
refactor: use popup to inform on additional reconciliation step for Cr/Dr Notes (backport #39943)
2024-02-19 08:21:39 +05:30
ruthra kumar
b1a4249041 fix: typeerror 'Item Group' filter on Purchase Register 2024-02-19 07:44:02 +05:30
ruthra kumar
5894bb3bbe refactor: use popup to inform on additional reconciliation step
(cherry picked from commit 0d260faa00)
2024-02-19 07:32:19 +05:30
Raffael Meyer
a83ce43845 Merge pull request #39906 from frappe/mergify/bp/version-14-hotfix/pr-39857
fix: fetch company terms (backport #39857)
2024-02-17 01:26:33 +01:00
Raffael Meyer
744de8595d Merge pull request #39927 from barredterra/bt-precision-v14 2024-02-17 00:32:15 +01:00
Raffael Meyer
e827507698 Merge pull request #39939 from frappe/mergify/bp/version-14-hotfix/pr-39938
fix(Issue): create communication (backport #39938)
2024-02-16 20:47:25 +01:00
barredterra
8deaba8def fix(Issue): create communication
Ignore permisions and mandatory. Required, for example, when Issue is created by Customer via portal.

(cherry picked from commit 3f1d008741)
2024-02-16 19:15:29 +00:00
barredterra
cea0e1fb91 fix(Bank Transaction): precision for (un)allocated_amount
Manual Backport of #39926
2024-02-15 19:58:16 +01:00
rohitwaghchaure
51dc3f57e1 Merge pull request #39917 from frappe/mergify/bp/version-14-hotfix/pr-39905
fix: party item code in Blanket Order (backport #39905)
2024-02-15 17:56:34 +05:30
rohitwaghchaure
ca8fb17ee8 chore: fix conflicts 2024-02-15 17:32:34 +05:30
rohitwaghchaure
1cca51afc6 chore: fix linter issue
(cherry picked from commit 230a7d8d53)
2024-02-15 09:33:41 +00:00
Rohit Waghchaure
ee9a51f93f fix: party item code in Blanket Order
(cherry picked from commit 1a8f7f9403)

# Conflicts:
#	erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.py
2024-02-15 09:33:40 +00:00
kunhi
5eaa11b9e8 fix: update_dimension is required and not need party account method
(cherry picked from commit e6949d71f6)
2024-02-14 14:54:44 +00:00
kunhi
45a0494318 fix: no need call for company method in sales invoice js
(cherry picked from commit e3bd8d10b0)
2024-02-14 14:54:44 +00:00
kunhi
83bf28616e fix: fetch company terms
(cherry picked from commit d97b6d38ef)
2024-02-14 14:54:43 +00:00
Frappe PR Bot
22ace5cb5a chore(release): Bumped to Version 14.62.4
## [14.62.4](https://github.com/frappe/erpnext/compare/v14.62.3...v14.62.4) (2024-02-14)

### Bug Fixes

*  production plan issue with sales order (backport [#39901](https://github.com/frappe/erpnext/issues/39901)) ([#39903](https://github.com/frappe/erpnext/issues/39903)) ([ab7e323](ab7e323648))
* accommodate for default rounding method in v14 ([d6a758d](d6a758d1f4))
* broken route option in Profitability report ([f79e0d1](f79e0d1e37))
* calculate `stock_value_diff` ([8f58b61](8f58b613e4))
* cancel asset capitalization ([2438600](24386006d6))
* do not throw validation for canceled SLE (backport [#39769](https://github.com/frappe/erpnext/issues/39769)) ([#39810](https://github.com/frappe/erpnext/issues/39810)) ([2885b8f](2885b8fa44))
* Handling circular linking while cancelling asset capitalization ([f2d094d](f2d094d1ab))
* incorrect planned qty in PP (backport [#39785](https://github.com/frappe/erpnext/issues/39785)) ([#39792](https://github.com/frappe/erpnext/issues/39792)) ([df9d52d](df9d52d3ce))
* landed cost voucher not submitting because of incorrect reference (backport [#39898](https://github.com/frappe/erpnext/issues/39898)) ([#39899](https://github.com/frappe/erpnext/issues/39899)) ([d0b9c56](d0b9c568d3))
* remove duplicates from tax category map ([c56f3a5](c56f3a58ab))
* set rate for PO created against BO (backport [#39765](https://github.com/frappe/erpnext/issues/39765)) ([#39766](https://github.com/frappe/erpnext/issues/39766)) ([de47e67](de47e67dfa))
* set route options to cost center ([#37235](https://github.com/frappe/erpnext/issues/37235)) ([79a16ba](79a16bad15))
* **ux:** set rate as price list rate on uom change in MR (backport [#39816](https://github.com/frappe/erpnext/issues/39816)) ([#39817](https://github.com/frappe/erpnext/issues/39817)) ([de6e8c7](de6e8c74c5))

### Performance Improvements

* cached get_last_purchase_details to fix performance issue (backport [#39854](https://github.com/frappe/erpnext/issues/39854)) ([#39855](https://github.com/frappe/erpnext/issues/39855)) ([08e0271](08e02710cd))
* production plan submission (backport [#39846](https://github.com/frappe/erpnext/issues/39846)) ([#39859](https://github.com/frappe/erpnext/issues/39859)) ([a61cffd](a61cffd7c2))
2024-02-14 12:06:19 +00:00
rohitwaghchaure
4fd7b01beb Merge pull request #39881 from frappe/version-14-hotfix
chore: release v14
2024-02-14 17:34:41 +05:30
mergify[bot]
ab7e323648 fix: production plan issue with sales order (backport #39901) (#39903)
fix:  production plan issue with sales order (#39901)

(cherry picked from commit d0df5df4a6)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-02-14 17:28:50 +05:30
mergify[bot]
d0b9c568d3 fix: landed cost voucher not submitting because of incorrect reference (backport #39898) (#39899)
fix: landed cost voucher not submitting because of incorrect reference (#39898)

(cherry picked from commit 6239fd704b)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-02-14 15:57:48 +05:30
Raffael Meyer
6b53288975 Merge pull request #39893 from frappe/mergify/bp/version-14-hotfix/pr-39868
fix(Purchase Receipt): calculate `stock_value_diff` (backport #39868)
2024-02-14 01:22:09 +01:00
barredterra
8f58b613e4 fix: calculate stock_value_diff
`d.item_tax_amount` is already in base currency.

(cherry picked from commit 5df5851798)
2024-02-13 23:52:34 +00:00
Nabin Hait
6460e649a5 Merge pull request #39814 from nabinhait/asset-capitalization-cancellation
fix: Handling circular linking while cancelling asset capitalization
2024-02-13 09:46:31 +05:30
mergify[bot]
a61cffd7c2 perf: production plan submission (backport #39846) (#39859)
perf: production plan submission

(cherry picked from commit aa1c69dd7a)

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2024-02-12 18:17:49 +05:30
mergify[bot]
08e02710cd perf: cached get_last_purchase_details to fix performance issue (backport #39854) (#39855)
perf: cached get_last_purchase_details to fix performance issue (#39854)

(cherry picked from commit b966c06a4f)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-02-12 15:54:03 +05:30
Nabin Hait
24386006d6 fix: cancel asset capitalization 2024-02-12 11:44:41 +05:30
mergify[bot]
de6e8c74c5 fix(ux): set rate as price list rate on uom change in MR (backport #39816) (#39817)
* fix: add price list rate field in MR Item

(cherry picked from commit 61a29eb5fb)

# Conflicts:
#	erpnext/stock/doctype/material_request_item/material_request_item.py

* fix: set rate as price list rate on uom change

(cherry picked from commit 5cf0759b0c)

* chore: linter

(cherry picked from commit 1745371cd6)

* chore: `conflicts`

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2024-02-11 17:38:37 +05:30
mergify[bot]
79a16bad15 fix: set route options to cost center (#37235)
fix: set route to cost center

cost center mapping is not correct

(cherry picked from commit 5e4b73918d)

Co-authored-by: NIYAZ RAZAK <76736615+niyazrazak@users.noreply.github.com>
2024-02-11 17:19:59 +05:30
ruthra kumar
dcf19c3ed9 fix: reconciliation issue due to notation difference 2024-02-09 16:50:14 +05:30
ruthra kumar
157d17d0f3 Merge pull request #39808 from ruthra-kumar/dimension_filter_issue_in_profitability_report
fix: incorrect route options on Profitability to Profit and Loss report
2024-02-08 15:53:26 +05:30
Nabin Hait
f2d094d1ab fix: Handling circular linking while cancelling asset capitalization 2024-02-08 15:51:29 +05:30
Frappe PR Bot
8caf655529 chore(release): Bumped to Version 14.62.3
## [14.62.3](https://github.com/frappe/erpnext/compare/v14.62.2...v14.62.3) (2024-02-08)

### Bug Fixes

* do not throw validation for canceled SLE (backport [#39769](https://github.com/frappe/erpnext/issues/39769)) ([#39810](https://github.com/frappe/erpnext/issues/39810)) ([d6054db](d6054dbdbd))
2024-02-08 09:12:55 +00:00
rohitwaghchaure
408d026465 Merge pull request #39812 from frappe/mergify/bp/version-14/pr-39810
fix: do not throw validation for canceled SLE (backport #39769) (backport #39810)
2024-02-08 14:41:43 +05:30
mergify[bot]
d6054dbdbd fix: do not throw validation for canceled SLE (backport #39769) (#39810)
fix: do not throw validation for cancelled sle

(cherry picked from commit 32ccf3524a)

Co-authored-by: Rohit Waghchaure <rohitw1991@gmail.com>
(cherry picked from commit 2885b8fa44)
2024-02-08 08:50:17 +00:00
mergify[bot]
2885b8fa44 fix: do not throw validation for canceled SLE (backport #39769) (#39810)
fix: do not throw validation for cancelled sle

(cherry picked from commit 32ccf3524a)

Co-authored-by: Rohit Waghchaure <rohitw1991@gmail.com>
2024-02-08 14:18:50 +05:30
ruthra kumar
f79e0d1e37 fix: broken route option in Profitability report 2024-02-08 13:22:55 +05:30
Frappe PR Bot
0822a2b40a chore(release): Bumped to Version 14.62.2
## [14.62.2](https://github.com/frappe/erpnext/compare/v14.62.1...v14.62.2) (2024-02-08)

### Bug Fixes

* accommodate for default rounding method in v14 ([79479b6](79479b633f))
* remove duplicates from tax category map ([a4e6c38](a4e6c388cb))
2024-02-08 07:28:09 +00:00
ruthra kumar
cb64c5f579 Merge pull request #39797 from frappe/mergify/bp/version-14/pr-39789
fix: remove duplicates from tax category map (backport #39789)
2024-02-08 12:56:59 +05:30
rohitwaghchaure
a9c95567cb Merge pull request #39790 from aynugek/patch-2
refactor: Clean up code used to fetch website item stock details
2024-02-08 11:06:20 +05:30
ruthra kumar
e452e42145 Merge pull request #39802 from frappe/mergify/bp/version-14/pr-39783
fix: cancelling cr/dr notes should update the linked Invoice status (backport #39783)
2024-02-08 10:26:26 +05:30
ruthra kumar
8888ce196d refactor(test): Forex Credit Note cancellation against Invoice
(cherry picked from commit 2f676ced5c)
2024-02-08 04:36:03 +00:00
ruthra kumar
129ab38fba refactor(test): assert Invoice status as well
(cherry picked from commit 33efe0d12d)
2024-02-08 04:36:02 +00:00
ruthra kumar
c419c1de06 test: Invoice status on Cr/Dr note cancellation
(cherry picked from commit 31a8c3bdc4)
2024-02-08 04:36:02 +00:00
ruthra kumar
5c59ab5975 refactor: cancel Cr/Dr JE's on Sales/Purchase return cancel
(cherry picked from commit 0549535603)
2024-02-08 04:36:02 +00:00
ruthra kumar
28b0e988db Merge pull request #39798 from frappe/mergify/bp/version-14-hotfix/pr-39783
fix: cancelling cr/dr notes should update the linked Invoice status (backport #39783)
2024-02-08 10:00:54 +05:30
Frappe PR Bot
7413c8b0a9 chore(release): Bumped to Version 14.62.1
## [14.62.1](https://github.com/frappe/erpnext/compare/v14.62.0...v14.62.1) (2024-02-07)

### Bug Fixes

* incorrect planned qty in PP (backport [#39785](https://github.com/frappe/erpnext/issues/39785)) (backport [#39792](https://github.com/frappe/erpnext/issues/39792)) ([#39795](https://github.com/frappe/erpnext/issues/39795)) ([596c9fd](596c9fd507))
2024-02-07 15:31:34 +00:00
mergify[bot]
596c9fd507 fix: incorrect planned qty in PP (backport #39785) (backport #39792) (#39795)
fix: incorrect planned qty in PP (backport #39785) (#39792)

fix: incorrect planned qty in PP

(cherry picked from commit a8ebc94a36)

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
(cherry picked from commit df9d52d3ce)

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2024-02-07 21:00:18 +05:30
ruthra kumar
8d9b5764dd refactor(test): Forex Credit Note cancellation against Invoice
(cherry picked from commit 2f676ced5c)
2024-02-07 15:00:47 +00:00
ruthra kumar
dd74728568 refactor(test): assert Invoice status as well
(cherry picked from commit 33efe0d12d)
2024-02-07 15:00:47 +00:00
ruthra kumar
e97f30d8e2 test: Invoice status on Cr/Dr note cancellation
(cherry picked from commit 31a8c3bdc4)
2024-02-07 15:00:46 +00:00
ruthra kumar
33777a4d4c refactor: cancel Cr/Dr JE's on Sales/Purchase return cancel
(cherry picked from commit 0549535603)
2024-02-07 15:00:44 +00:00
Gursheen Anand
79479b633f fix: accommodate for default rounding method in v14
(cherry picked from commit d6a758d1f4)
2024-02-07 14:23:32 +00:00
Gursheen Anand
a4e6c388cb fix: remove duplicates from tax category map
(cherry picked from commit c56f3a58ab)
2024-02-07 14:23:32 +00:00
Gursheen Kaur Anand
d62db9bb22 Merge pull request #39789 from GursheenK/duplicates-in-tax-category-bpv14
fix: remove duplicates from tax category map
2024-02-07 19:51:51 +05:30
mergify[bot]
df9d52d3ce fix: incorrect planned qty in PP (backport #39785) (#39792)
fix: incorrect planned qty in PP

(cherry picked from commit a8ebc94a36)

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2024-02-07 19:26:14 +05:30
Gursheen Anand
d6a758d1f4 fix: accommodate for default rounding method in v14 2024-02-07 19:12:32 +05:30
mergify[bot]
de47e67dfa fix: set rate for PO created against BO (backport #39765) (#39766)
* fix: set rate for PO created against BO

(cherry picked from commit 0e5b4e5f07)

# Conflicts:
#	erpnext/manufacturing/doctype/blanket_order/blanket_order.py

* chore: `conflicts`

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2024-02-07 19:03:38 +05:30
Gursheen Anand
c56f3a58ab fix: remove duplicates from tax category map 2024-02-07 18:46:07 +05:30
Babuuu
e3591270c6 refactor: Clean up code used to fetch website item stock details 2024-02-07 13:12:14 +00:00
Frappe PR Bot
9a8a9568b1 chore(release): Bumped to Version 14.62.0
# [14.62.0](https://github.com/frappe/erpnext/compare/v14.61.4...v14.62.0) (2024-02-06)

### Bug Fixes

* Blanket Order Ordered Quantity (backport [#39725](https://github.com/frappe/erpnext/issues/39725)) ([#39738](https://github.com/frappe/erpnext/issues/39738)) ([44c09de](44c09de729))
* correctly calculate diff amount for included taxes ([#39655](https://github.com/frappe/erpnext/issues/39655)) ([350b2cd](350b2cdde3))
* don't overwrite existing terms in transaction ([#39519](https://github.com/frappe/erpnext/issues/39519)) ([e49f8d5](e49f8d5f55))
* Exchange rate on MR to PO creation for muticurrency POs ([#39646](https://github.com/frappe/erpnext/issues/39646)) ([4dc5d9a](4dc5d9a6ca))
* incorrect landed cost voucher amount ([4a609d8](4a609d8fa8))
* remove applied pricing rule on qty change (backport [#39688](https://github.com/frappe/erpnext/issues/39688)) ([#39736](https://github.com/frappe/erpnext/issues/39736)) ([46ac4f4](46ac4f4714))
* show warehouse title field in sales docs (backport [#39746](https://github.com/frappe/erpnext/issues/39746)) ([#39754](https://github.com/frappe/erpnext/issues/39754)) ([a6067c6](a6067c6239))

### Features

* copy emails from lead to customer ([#38647](https://github.com/frappe/erpnext/issues/38647)) ([7952bf4](7952bf4318))
* New financial views - Growth and margin views for P&L and balance sheet (backport [#39588](https://github.com/frappe/erpnext/issues/39588)) ([#39601](https://github.com/frappe/erpnext/issues/39601)) ([3808ddb](3808ddbf86))

### Performance Improvements

* memory consumption for the stock balance report ([#39626](https://github.com/frappe/erpnext/issues/39626)) ([b32848d](b32848d69d))
* Move dimension validation out of GL Entry doctype ([#39730](https://github.com/frappe/erpnext/issues/39730)) ([7691256](7691256f4d))
* timeout for auto material request through reorder level ([85e6b39](85e6b39e23))
* timeout while submitting the purchase receipt entry ([a1a70bb](a1a70bbae0))

### Reverts

* Revert "feat: New financial views - Growth and margin views for P&L and balance sheet (#39588)"  ([f01308b](f01308b972)), closes [#39588](https://github.com/frappe/erpnext/issues/39588)
2024-02-06 18:44:23 +00:00
rohitwaghchaure
1cedb4b430 Merge pull request #39759 from frappe/version-14-hotfix
chore: release v14
2024-02-07 00:13:04 +05:30
ruthra kumar
ae4c90766a Merge pull request #39750 from frappe/mergify/bp/version-14-hotfix/pr-39694
refactor: enforce unique GL Account for each 'Bank Account' (backport #39694)
2024-02-06 15:48:29 +05:30
mergify[bot]
a6067c6239 fix: show warehouse title field in sales docs (backport #39746) (#39754)
fix: show warehouse title field in sales docs

(cherry picked from commit ee14faaa39)

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2024-02-06 12:45:43 +05:30
ruthra kumar
8267fee9b8 refactor(test): make use of test fixtures in Payment Order
(cherry picked from commit 322cdbaccf)
2024-02-06 04:00:13 +00:00
ruthra kumar
f64f0437ae refactor(test): generate uniq GL acc and Bank acc for each test case
(cherry picked from commit a9a2ec81de)
2024-02-06 04:00:13 +00:00
ruthra kumar
0163e13aa3 refactor: ensure unique accounts for each Bank Account's
(cherry picked from commit 2caa2d677c)
2024-02-06 04:00:13 +00:00
rohitwaghchaure
c2486c8d58 Merge pull request #39744 from frappe/mergify/bp/version-14-hotfix/pr-39742
perf: timeout while submitting the purchase receipt entry (backport #39742)
2024-02-05 21:26:47 +05:30
Rohit Waghchaure
a1a70bbae0 perf: timeout while submitting the purchase receipt entry
(cherry picked from commit 1fa6233377)
2024-02-05 15:33:03 +00:00
Frappe PR Bot
99929e9434 chore(release): Bumped to Version 14.61.4
## [14.61.4](https://github.com/frappe/erpnext/compare/v14.61.3...v14.61.4) (2024-02-05)

### Bug Fixes

* correctly calculate diff amount for included taxes ([#39655](https://github.com/frappe/erpnext/issues/39655)) ([33ae0fa](33ae0fa2f4))
2024-02-05 12:09:13 +00:00
mergify[bot]
33ae0fa2f4 fix: correctly calculate diff amount for included taxes (#39655)
fix: correctly calculate diff amount for included taxes (#39655)

(cherry picked from commit 772f540bef)
(cherry picked from commit 350b2cdde3)

Co-authored-by: Gursheen Kaur Anand <40693548+GursheenK@users.noreply.github.com>
2024-02-05 17:38:00 +05:30
mergify[bot]
44c09de729 fix: Blanket Order Ordered Quantity (backport #39725) (#39738)
* fix: disable no-copy for blanket order in PO

(cherry picked from commit 5ce5c352e4)

# Conflicts:
#	erpnext/buying/doctype/purchase_order_item/purchase_order_item.json

* fix: update BO Ordered Quantity on PO Close/Open

(cherry picked from commit 61ded697a7)

# Conflicts:
#	erpnext/buying/doctype/purchase_order/purchase_order.py

* test: BO on PO Close/Open

(cherry picked from commit 27d6c8b6d5)

* chore: `conflicts`

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2024-02-05 16:52:01 +05:30
mergify[bot]
46ac4f4714 fix: remove applied pricing rule on qty change (backport #39688) (#39736)
fix: remove pricing rule

(cherry picked from commit 7c6a5a0f23)

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2024-02-05 16:09:09 +05:30
rohitwaghchaure
b10c0c9841 Merge pull request #39731 from frappe/mergify/bp/version-14-hotfix/pr-39626
perf: memory consumption for the stock balance report (backport #39626)
2024-02-05 16:02:42 +05:30
rohitwaghchaure
851f1bbdce Merge pull request #39728 from frappe/mergify/bp/version-14-hotfix/pr-39659
perf: timeout for auto material request through reorder level (backport #39659)
2024-02-05 14:46:37 +05:30
mergify[bot]
7691256f4d perf: Move dimension validation out of GL Entry doctype (#39730)
perf: Move dimension validation out of GL Entry doctype (#39730)

(cherry picked from commit b834ed10d6)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2024-02-05 14:34:16 +05:30
mergify[bot]
7952bf4318 feat: copy emails from lead to customer (#38647)
feat: copy emails from lead to customer

(cherry picked from commit 906ac093e3)

Co-authored-by: barredterra <14891507+barredterra@users.noreply.github.com>
2024-02-05 14:05:24 +05:30
rohitwaghchaure
e38bb836a5 chore: fix conflicts 2024-02-05 13:20:03 +05:30
rohitwaghchaure
a3f05cb0e7 Merge pull request #39726 from frappe/mergify/bp/version-14-hotfix/pr-39684
fix: incorrect landed cost voucher amount (backport #39684)
2024-02-05 13:16:58 +05:30
rohitwaghchaure
26dfbb7a64 chore: fix conflicts 2024-02-05 12:13:14 +05:30
rohitwaghchaure
b32848d69d perf: memory consumption for the stock balance report (#39626)
(cherry picked from commit b70f3de16b)

# Conflicts:
#	erpnext/stock/report/stock_balance/stock_balance.py
2024-02-05 06:18:51 +00:00
Rohit Waghchaure
85e6b39e23 perf: timeout for auto material request through reorder level
(cherry picked from commit 951023f434)

# Conflicts:
#	erpnext/stock/doctype/stock_entry/test_stock_entry.py
2024-02-05 06:09:53 +00:00
Rohit Waghchaure
4a609d8fa8 fix: incorrect landed cost voucher amount
(cherry picked from commit d78a1e7814)
2024-02-05 06:09:35 +00:00
mergify[bot]
e49f8d5f55 fix: don't overwrite existing terms in transaction (#39519)
* fix: don't overwrite existing terms in transaction

(cherry picked from commit 77b044f1a6)

* refactor: keep the diff small

---------

Co-authored-by: barredterra <14891507+barredterra@users.noreply.github.com>
2024-02-03 14:05:47 +05:30
Deepesh Garg
f01308b972 Revert "feat: New financial views - Growth and margin views for P&L and balance sheet (#39588)"
Revert "feat: New financial views - Growth and margin views for P&L and balan…"

This reverts commit 3808ddbf86.
2024-02-03 09:11:12 +05:30
mergify[bot]
3808ddbf86 feat: New financial views - Growth and margin views for P&L and balance sheet (backport #39588) (#39601)
feat: New financial views - Growth and margin views for P&L and balance sheet

(cherry picked from commit 92649de5c6)

# Conflicts:
#	erpnext/accounts/report/balance_sheet/balance_sheet.js
#	erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js

Co-authored-by: nitmit <nitali@citrusleaf.in>
2024-02-03 09:10:28 +05:30
ruthra kumar
7ad20a0db7 Merge pull request #39698 from ruthra-kumar/add_disabled_field_in_bank_account
refactor: add disabled field in bank account
2024-02-02 20:49:41 +05:30
ruthra kumar
58ea7f4105 refactor: add 'disabled' field to Bank Account 2024-02-02 17:50:19 +05:30
rohitwaghchaure
c284d2ae07 Merge pull request #39690 from frappe/mergify/bp/version-14/pr-39689
chore: flag for barcode scanner (backport #39689)
2024-02-02 13:34:29 +05:30
Rohit Waghchaure
57cfce08ef chore: flag for barcode scanner
(cherry picked from commit f8ca5c5c8b)
2024-02-02 07:58:38 +00:00
rohitwaghchaure
b193d3cc86 Merge pull request #39689 from rohitwaghchaure/fixed-flag-for-barcode-scanner
chore: flag for barcode scanner
2024-02-02 13:27:43 +05:30
Rohit Waghchaure
f8ca5c5c8b chore: flag for barcode scanner 2024-02-02 13:23:54 +05:30
ruthra kumar
76c652c016 Merge pull request #39667 from frappe/mergify/bp/version-14-hotfix/pr-39655
fix: correctly calculate diff amount for included taxes (backport #39655)
2024-02-01 19:45:07 +05:30
ruthra kumar
49817ab17b Merge pull request #39679 from frappe/mergify/bp/version-14-hotfix/pr-39674
refactor: use pop up to inform of possible data issue (backport #39674)
2024-02-01 17:31:11 +05:30
ruthra kumar
8035f5b951 refactor: use pop up to inform of possible data issue
and leave a comment in communcation trail as well

(cherry picked from commit 78483e2ee6)
2024-02-01 11:43:03 +00:00
ruthra kumar
86d74e7f48 Merge pull request #39675 from frappe/mergify/bp/version-14-hotfix/pr-39671
refactor: move ignore ERR filter from SOA to General Ledger (backport #39671)
2024-02-01 16:53:26 +05:30
ruthra kumar
68c5a6e86f chore: resolve conflict 2024-02-01 16:23:33 +05:30
ruthra kumar
6284553f23 refactor(test): use party with USD billing currency
(cherry picked from commit beff566c82)
2024-02-01 10:41:39 +00:00
ruthra kumar
686da470fa test: ignore_err filter out in General Ledger
(cherry picked from commit affca3a519)
2024-02-01 10:41:39 +00:00
ruthra kumar
2fbd11d646 refactor: move ignore ERR filters from SOA to General Ledger
(cherry picked from commit c077eda64e)

# Conflicts:
#	erpnext/accounts/report/general_ledger/general_ledger.py
2024-02-01 10:41:39 +00:00
Gursheen Kaur Anand
350b2cdde3 fix: correctly calculate diff amount for included taxes (#39655)
(cherry picked from commit 772f540bef)
2024-02-01 04:56:54 +00:00
mergify[bot]
4dc5d9a6ca fix: Exchange rate on MR to PO creation for muticurrency POs (#39646)
fix: Exchange rate on MR to PO creation for muticurrency POs (#39646)

(cherry picked from commit cfd1666181)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2024-01-31 12:49:22 +05:30
Frappe PR Bot
b7d8bfc58c chore(release): Bumped to Version 14.61.3
## [14.61.3](https://github.com/frappe/erpnext/compare/v14.61.2...v14.61.3) (2024-01-30)

### Bug Fixes

* Asset Depreciation WDV as per Income Tax Act ([b840eb9](b840eb90eb))
* asset module test cases ([f604798](f604798a45))
* **Batch:** reload doc after splitting ([c759406](c759406ebb))
* default enable closing stock balance (backport [#39551](https://github.com/frappe/erpnext/issues/39551)) ([#39553](https://github.com/frappe/erpnext/issues/39553)) ([1e32c62](1e32c6207e))
* do not auto-populate item delivery date ([1e341f0](1e341f0ff6))
* do not consider rejected warehouses in pick list ([#39539](https://github.com/frappe/erpnext/issues/39539)) ([f6725e4](f6725e4342))
* do not delete batches implicitly ([9a5995a](9a5995a3e5))
* **ecom:** do not create a new contact if a contact already exists ([#39290](https://github.com/frappe/erpnext/issues/39290)) ([47c591c](47c591ccf1))
* email list for auto reorder material request ([780c069](780c069268))
* enqueue JV submission when more than 100 accounts ([66be3c5](66be3c551f))
* fetch correct quantity and amount for grouped asset ([1dacb79](1dacb79441))
* incorrect amount in the material request item (backport [#39567](https://github.com/frappe/erpnext/issues/39567)) ([#39568](https://github.com/frappe/erpnext/issues/39568)) ([c26f7bb](c26f7bbed0))
* linter issue ([941f882](941f8824e5))
* not able to edit / change address from portal ([e3fdb6f](e3fdb6f55c))
* not able to edit address through portal ([b310a55](b310a55727))
* Payment Terms Status for Sales Order report should show all payment terms from order not only this comming from template ([2953959](295395918c))
* perf issue while submitting stock entry (backport [#39634](https://github.com/frappe/erpnext/issues/39634)) ([#39641](https://github.com/frappe/erpnext/issues/39641)) ([3ee0555](3ee0555115))
* prevent extra transfer against inter transfer transaction (backport [#39213](https://github.com/frappe/erpnext/issues/39213)) ([#39595](https://github.com/frappe/erpnext/issues/39595)) ([bf61030](bf61030dab))
* qtn tests using delivery date ([c50988b](c50988b1bc))
* return doc obj after submit ([0472879](04728792f5))
* RM valuation rate in SCR ([#39541](https://github.com/frappe/erpnext/issues/39541)) ([9fd1692](9fd1692db2))
* typo's and parameter changes ([41c074d](41c074d0bb))
2024-01-30 14:14:00 +00:00
rohitwaghchaure
378866f429 Merge pull request #39640 from frappe/version-14-hotfix
chore: release v14
2024-01-30 19:42:12 +05:30
mergify[bot]
3ee0555115 fix: perf issue while submitting stock entry (backport #39634) (#39641)
fix: perf issue while submitting stock entry (#39634)

(cherry picked from commit b14886b227)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-01-30 15:44:03 +05:30
rohitwaghchaure
f6725e4342 fix: do not consider rejected warehouses in pick list (#39539)
* fix: do not picked rejected materials

* test: test case for pick list without rejected materials
2024-01-30 11:25:20 +05:30
ruthra kumar
d4bd508b62 Merge pull request #39632 from frappe/mergify/bp/version-14-hotfix/pr-39559
fix: prevent Return Invoices(Credit/Debit Note) from using a different account  (backport #39559)
2024-01-30 11:23:39 +05:30
ruthra kumar
0884c5ed83 chore: resolve conflicts 2024-01-30 11:05:24 +05:30
ruthra kumar
9212a74913 test: debit note account mismatch
(cherry picked from commit bdca718103)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
2024-01-30 05:31:44 +00:00
ruthra kumar
faeca79c68 test: account mismatch validation
(cherry picked from commit 8bdc760733)
2024-01-30 05:31:43 +00:00
ruthra kumar
c5ce4db315 refactor: prevent '{debit/credit}_to' account mismatch
(cherry picked from commit 6f2fae1b61)

# Conflicts:
#	erpnext/controllers/accounts_controller.py
2024-01-30 05:31:43 +00:00
ruthra kumar
844db3b8d6 Merge pull request #39509 from frappe/mergify/bp/version-14-hotfix/pr-39054
refactor: provision to filter on dimensions in reconciliation tool (backport #39054)
2024-01-30 08:20:51 +05:30
ruthra kumar
d0c810accd refactor(test): disable dimensions post test 2024-01-30 06:29:59 +05:30
ruthra kumar
1966ea15ba refactor: pass orders name in a separate criterion 2024-01-30 05:58:26 +05:30
mergify[bot]
d794502681 fix amount not updated when change rate in material request (backport #39606) (backport #39614) (#39621)
fix amount not updated when change rate in material request (backport #39606) (#39614)

fix amount not updated when change rate in material request (#39606)

* fix amount not updated when change rate in material request

* make code consistent

(cherry picked from commit efade9b9ae)

Co-authored-by: Jeffry Suryadharma <41689493+jeffrysurya@users.noreply.github.com>
(cherry picked from commit 2389b41f51)

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2024-01-29 18:57:40 +05:30
ruthra kumar
c054316127 chore: fix typo and initialize a list 2024-01-29 17:41:56 +05:30
ruthra kumar
ff0daedd52 refactor: convert sql to query builder on Payments query 2024-01-29 17:09:59 +05:30
Gursheen Kaur Anand
50d2381781 Merge pull request #39617 from frappe/mergify/bp/version-14-hotfix/pr-39616
fix(minor): do not auto-populate item delivery date from qtn (backport #39616)
2024-01-29 16:56:10 +05:30
Gursheen Anand
c50988b1bc fix: qtn tests using delivery date
(cherry picked from commit 079cd30b9c)
2024-01-29 10:22:04 +00:00
Gursheen Anand
1e341f0ff6 fix: do not auto-populate item delivery date
(cherry picked from commit 49cb11c1f3)
2024-01-29 10:22:02 +00:00
ruthra kumar
ea779fcad9 refactor: build payment entry query separately 2024-01-29 14:44:16 +05:30
mergify[bot]
2389b41f51 fix amount not updated when change rate in material request (backport #39606) (#39614)
fix amount not updated when change rate in material request (#39606)

* fix amount not updated when change rate in material request

* make code consistent

(cherry picked from commit efade9b9ae)

Co-authored-by: Jeffry Suryadharma <41689493+jeffrysurya@users.noreply.github.com>
2024-01-29 13:36:10 +05:30
Gursheen Kaur Anand
896bba4e4d Merge pull request #39611 from frappe/mergify/bp/version-14-hotfix/pr-39562
fix: enqueue JV submission when > 100 accounts (backport #39562)
2024-01-29 12:38:37 +05:30
Gursheen Anand
04728792f5 fix: return doc obj after submit
(cherry picked from commit fc677811b7)
2024-01-29 06:10:04 +00:00
Gursheen Anand
66be3c551f fix: enqueue JV submission when more than 100 accounts
(cherry picked from commit 53b44ccf29)
2024-01-29 06:10:04 +00:00
Nabin Hait
fbf34439c1 Merge pull request #39565 from frappe/mergify/bp/version-14-hotfix/pr-39489
fix: fetch correct quantity and amount for grouped asset (backport #39489)
2024-01-29 11:38:27 +05:30
mergify[bot]
bf61030dab fix: prevent extra transfer against inter transfer transaction (backport #39213) (#39595)
* fix: prevent extra transfer against inter transfer transaction (#39213)

* fix: prevent extra transfer against inter transfer transaction

* fix: internal transfer dashboard

(cherry picked from commit 8fdc244e16)

# Conflicts:
#	erpnext/controllers/stock_controller.py

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-01-27 23:25:04 +05:30
ruthra kumar
bdf5c02d33 Merge pull request #39593 from frappe/mergify/bp/version-14-hotfix/pr-39591
refactor: Do proper currency conversion on Future Payments column in AR/AP report (backport #39591)
2024-01-27 13:19:37 +05:30
ruthra kumar
12ac371b22 test: future payment with foreign currency
(cherry picked from commit 7b37389115)
2024-01-27 07:23:08 +00:00
ruthra kumar
4626ea79ef refactor: do currency conversion on future amount columns
(cherry picked from commit 0de4197c88)
2024-01-27 07:23:08 +00:00
Frappe PR Bot
33b21a54f7 chore(release): Bumped to Version 14.61.2
## [14.61.2](https://github.com/frappe/erpnext/compare/v14.61.1...v14.61.2) (2024-01-27)

### Bug Fixes

* incorrect amount in the material request item (backport [#39567](https://github.com/frappe/erpnext/issues/39567)) (backport [#39568](https://github.com/frappe/erpnext/issues/39568)) ([#39586](https://github.com/frappe/erpnext/issues/39586)) ([e729972](e729972987))
2024-01-27 04:43:52 +00:00
mergify[bot]
e729972987 fix: incorrect amount in the material request item (backport #39567) (backport #39568) (#39586)
fix: incorrect amount in the material request item (backport #39567) (#39568)

fix: incorrect amount in the material request item (#39567)

fix: incoorect amount in the material request
(cherry picked from commit 2bdfdeeb9a)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
(cherry picked from commit c26f7bbed0)

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2024-01-27 10:12:37 +05:30
Saqib Ansari
47c591ccf1 fix(ecom): do not create a new contact if a contact already exists (#39290) 2024-01-26 14:25:05 +05:30
Deepesh Garg
169c7f3e05 Merge pull request #39576 from frappe/mergify/bp/version-14-hotfix/pr-39511
refactor(Sales Invoice): set account and sum for payments (#39511)
2024-01-26 10:04:37 +05:30
barredterra
a072cfbf3f refactor(Sales Invoice): set account for mode of payment
(cherry picked from commit 3815f07c33)
2024-01-26 04:15:10 +00:00
s-aga-r
9fd1692db2 fix: RM valuation rate in SCR (#39541) 2024-01-25 19:08:39 +05:30
mergify[bot]
c26f7bbed0 fix: incorrect amount in the material request item (backport #39567) (#39568)
fix: incorrect amount in the material request item (#39567)

fix: incoorect amount in the material request
(cherry picked from commit 2bdfdeeb9a)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-01-25 17:22:15 +05:30
Nabin Hait
1dacb79441 fix: fetch correct quantity and amount for grouped asset
(cherry picked from commit 06f48c678b)
2024-01-25 10:51:48 +00:00
Nabin Hait
0d41631ae4 Merge pull request #39453 from nabinhait/asset-depr-income-tax-act
fix: Asset Depreciation WDV as per Income Tax Act
2024-01-25 16:06:22 +05:30
ruthra kumar
560af95931 Merge pull request #39497 from frappe/mergify/bp/version-14-hotfix/pr-35330
refactor: cr/dr note should be standalone even when created from another invoice (backport #35330)
2024-01-25 13:25:35 +05:30
mergify[bot]
1e32c6207e fix: default enable closing stock balance (backport #39551) (#39553)
fix: default enable closing stock balance (#39551)

(cherry picked from commit d1fb90edff)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-01-25 12:51:48 +05:30
rohitwaghchaure
822a5a842b Merge pull request #39548 from rohitwaghchaure/fix-do-not-delete-batch-implicitly
fix: do not delete batches implicitly
2024-01-25 12:33:28 +05:30
Rohit Waghchaure
9a5995a3e5 fix: do not delete batches implicitly 2024-01-25 11:58:07 +05:30
Nabin Hait
941f8824e5 fix: linter issue 2024-01-25 11:40:44 +05:30
ruthra kumar
22c13b1b83 Merge pull request #39537 from frappe/mergify/bp/version-14-hotfix/pr-39331
fix: Payment Terms Status for Sales Order report show all payment terms from orders (not only when there is a payment terms template) (backport #39331)
2024-01-24 16:33:50 +05:30
Florian HENRY
295395918c fix: Payment Terms Status for Sales Order report should show all payment terms from order not only this comming from template
(cherry picked from commit 6c8f52b26f)
2024-01-24 10:42:57 +00:00
ruthra kumar
2c431f394e chore: resolve conflicts 2024-01-24 16:11:14 +05:30
rohitwaghchaure
924fc8f0a8 Merge pull request #39530 from frappe/mergify/bp/version-14-hotfix/pr-39525
fix: email list for auto reorder material request (backport #39525)
2024-01-24 14:49:18 +05:30
Rohit Waghchaure
780c069268 fix: email list for auto reorder material request
(cherry picked from commit 764f3422a0)
2024-01-24 07:07:05 +00:00
rohitwaghchaure
f4123e1863 Merge pull request #39528 from rohitwaghchaure/fixed-not-able-to-edit-change-address
fix: not able to edit / change address from portal
2024-01-24 12:19:45 +05:30
rohitwaghchaure
a1559ed0c2 Merge pull request #39526 from frappe/mergify/bp/version-14-hotfix/pr-39521
fix: not able to edit address through portal (backport #39521)
2024-01-24 12:16:57 +05:30
Rohit Waghchaure
e3fdb6f55c fix: not able to edit / change address from portal 2024-01-24 12:15:55 +05:30
Rohit Waghchaure
b310a55727 fix: not able to edit address through portal
(cherry picked from commit b046d980ad)
2024-01-24 06:20:29 +00:00
Raffael Meyer
16dc8232f2 Merge pull request #39514 from barredterra/refactor-batch-v14 2024-01-23 19:01:53 +01:00
barredterra
dd3b77ae28 refactor(Batch): use const instead of var 2024-01-23 15:08:49 +01:00
barredterra
c759406ebb fix(Batch): reload doc after splitting
to show updated qty
2024-01-23 15:08:33 +01:00
ruthra kumar
e70f0f6d8d refactor: handle dynamic dimension in order query
(cherry picked from commit 7c2cb70387)
2024-01-23 11:59:31 +00:00
ruthra kumar
9a3bde9350 refactor: update dimensions, only if provided
(cherry picked from commit ec0f17ca8b)
2024-01-23 11:59:31 +00:00
ruthra kumar
51bc225fe5 refactor: dynamic dimension filters in pop up
(cherry picked from commit f8bbb0619c)
2024-01-23 11:59:31 +00:00
ruthra kumar
15db7b8ae4 test: dimension inheritance on adv allocation
(cherry picked from commit fcf4687c52)
2024-01-23 11:59:30 +00:00
ruthra kumar
d4828f3cf5 refactor: pass dimensions on advance allocation
(cherry picked from commit cbd443a78a)
2024-01-23 11:59:30 +00:00
ruthra kumar
ec58c309d2 test: dimension inheritance in PE reconciliation
(cherry picked from commit 6148fb024b)
2024-01-23 11:59:30 +00:00
ruthra kumar
a919702319 refactor: pass dimension values to Gain/Loss journal
(cherry picked from commit c44eb432a5)

# Conflicts:
#	erpnext/accounts/utils.py
2024-01-23 11:59:29 +00:00
ruthra kumar
de948f23c1 test: dimension inheritance for cr note reconciliation
(cherry picked from commit ba5a7c8cd8)
2024-01-23 11:59:29 +00:00
ruthra kumar
c1591ec8e1 chore: test dimension filter output
(cherry picked from commit e3c44231ab)
2024-01-23 11:59:29 +00:00
ruthra kumar
c3ffb7a4c4 refactor: apply dimension filters on cr/dr notes
(cherry picked from commit 188ff8cde7)
2024-01-23 11:59:28 +00:00
ruthra kumar
937262b572 refactor: Credit Note and its Exc gain/loss JE inherits dimensions
(cherry picked from commit ab939cc6e8)
2024-01-23 11:59:28 +00:00
ruthra kumar
41c074d0bb fix: typo's and parameter changes
(cherry picked from commit 0ec17590ae)
2024-01-23 11:59:28 +00:00
ruthra kumar
ecd36501af refactor: partial change on outstanding invoice popup
(cherry picked from commit 2154502955)
2024-01-23 11:59:28 +00:00
ruthra kumar
8f87c588ec refactor: replace sql with query builder for Jourals query
(cherry picked from commit 9c5a79209e)
2024-01-23 11:59:28 +00:00
ruthra kumar
dac422a0e1 refactor: pass dimension details to query
(cherry picked from commit 5dc22e1811)

# Conflicts:
#	erpnext/accounts/utils.py
2024-01-23 11:59:27 +00:00
ruthra kumar
b2db6d0546 refactor: set query filters for dimensions
(cherry picked from commit ad8475cb8b)
2024-01-23 11:59:27 +00:00
ruthra kumar
3d62bce885 refactor: pass dimension filters to query
(cherry picked from commit ff60ec85b8)

# Conflicts:
#	erpnext/controllers/accounts_controller.py
2024-01-23 11:59:27 +00:00
ruthra kumar
66cadb8b9f refactor: handle dimension filters
(cherry picked from commit c1fe4bcc64)
2024-01-23 11:59:26 +00:00
ruthra kumar
81b87ef2e2 refactor: column break in dimension section
(cherry picked from commit 20576e0f47)
2024-01-23 11:59:26 +00:00
ruthra kumar
4114c0e854 refactor: dimensions filter section in payment reconciliation
(cherry picked from commit 20e0acc20a)
2024-01-23 11:59:26 +00:00
ruthra kumar
b25e8ae14c refactor: update dimension doctypes in hooks
(cherry picked from commit cfb3d87267)
2024-01-23 11:59:25 +00:00
ruthra kumar
5c9ad21a3f refactor: dimensions section in allocation table in reconciliation
(cherry picked from commit 1cde804c77)
2024-01-23 11:59:25 +00:00
Frappe PR Bot
26ca27a431 chore(release): Bumped to Version 14.61.1
## [14.61.1](https://github.com/frappe/erpnext/compare/v14.61.0...v14.61.1) (2024-01-23)

### Bug Fixes

* linting issue ([6895b74](6895b74ecc))
* party field in pdf html ([a19b41d](a19b41d8c8))
* set unallocated amount after base tax ([f7ba736](f7ba7361ca))
* UOM needs to be whole number not being checked in quotations ([55c9cc3](55c9cc3f26))
* use most reliable section reference per report line ([3bdff18](3bdff18467))
2024-01-23 11:54:25 +00:00
ruthra kumar
f8f3c917d2 Merge pull request #39506 from frappe/version-14-hotfix
chore: release v14
2024-01-23 17:23:12 +05:30
ruthra kumar
956f05238a refactor: Payment btn criteria for Cr/Dr notes
(cherry picked from commit 60eee564bf)
2024-01-23 06:36:09 +00:00
ruthra kumar
1aeeac4d06 refactor: criteria for Credit Note Issued and Debit Note Issued
(cherry picked from commit 8f695123cd)
2024-01-23 06:36:09 +00:00
ruthra kumar
fecab1338e refactor(test): payments to invoice with -ve outstanding
(cherry picked from commit f6e4ac2b62)
2024-01-23 06:36:09 +00:00
ruthra kumar
03040c1c7f refactor(test): ledger entries will be against itself
(cherry picked from commit 0e2fb1188a)
2024-01-23 06:36:08 +00:00
ruthra kumar
43b40d92e5 refactor(test): return invoice will have -ve outstanding
(cherry picked from commit b30c1e1abf)
2024-01-23 06:36:08 +00:00
ruthra kumar
014fcfa611 refactor: remove return_against for cr/dr note filter
(cherry picked from commit 00878707ae)
2024-01-23 06:36:08 +00:00
ruthra kumar
0a6af795c4 refactor: cr notes will post for itself
(cherry picked from commit db76e8a277)
2024-01-23 06:36:08 +00:00
rohitwaghchaure
5be99295da Merge pull request #39491 from frappe/mergify/bp/version-14-hotfix/pr-39488
fix: UOM needs to be whole number not being checked in quotations (backport #39488)
2024-01-23 11:54:47 +05:30
Rohit Waghchaure
55c9cc3f26 fix: UOM needs to be whole number not being checked in quotations
(cherry picked from commit aaf83da3e9)
2024-01-22 11:55:27 +00:00
Gursheen Kaur Anand
be56c1838b Merge pull request #39482 from frappe/mergify/bp/version-14-hotfix/pr-39462
fix: party field in PDF for AP / AR reports (backport #39462)
2024-01-22 17:06:09 +05:30
Gursheen Anand
a19b41d8c8 fix: party field in pdf html
(cherry picked from commit b2d9380596)
2024-01-22 09:51:30 +00:00
Gursheen Kaur Anand
1a46f60633 Merge pull request #39473 from frappe/mergify/bp/version-14-hotfix/pr-39229
fix: use most reliable section reference per report line (backport #39229)
2024-01-21 14:23:34 +05:30
David Arnold
3bdff18467 fix: use most reliable section reference per report line
(cherry picked from commit b5be17c6df)
2024-01-21 08:14:13 +00:00
Gursheen Kaur Anand
5c1bf1f3fa Merge pull request #39470 from frappe/mergify/bp/version-14-hotfix/pr-39424
Revert "fix(minor): financial statements period end date" (backport #39424)
2024-01-20 18:51:59 +05:30
Gursheen Kaur Anand
837fff4533 Revert "fix(minor): financial statements period end date"
(cherry picked from commit 73625a2622)
2024-01-20 12:29:22 +00:00
Nabin Hait
f604798a45 fix: asset module test cases 2024-01-19 16:59:52 +05:30
Gursheen Kaur Anand
f405a6c31d Merge pull request #39450 from frappe/mergify/bp/version-14-hotfix/pr-39449
fix: unallocated amount after taxes and charges (backport #39449)
2024-01-19 10:58:27 +05:30
Nabin Hait
b840eb90eb fix: Asset Depreciation WDV as per Income Tax Act 2024-01-18 19:06:41 +05:30
Gursheen Anand
6895b74ecc fix: linting issue
(cherry picked from commit 99b94af49f)
2024-01-18 10:45:49 +00:00
Gursheen Anand
f7ba7361ca fix: set unallocated amount after base tax
(cherry picked from commit e9bc63aacf)
2024-01-18 10:45:49 +00:00
Frappe PR Bot
9aa1e7444e chore(release): Bumped to Version 14.61.0
# [14.61.0](https://github.com/frappe/erpnext/compare/v14.60.1...v14.61.0) (2024-01-17)

### Bug Fixes

* added indexing to improve performance ([d9f7070](d9f7070f92))
* added item group in stock reco ([c69a59c](c69a59c3c6))
* broken dimension filters in Sales/Purchase register ([298cdf5](298cdf5f0e))
* Cancel asset capitalisation record on cancellation of asset and vice-versa ([1dff960](1dff96057c))
* circular dependency error while deleting QC ([dfcb746](dfcb746774))
* composite asset capitalization using asset components ([ac6020a](ac6020a940))
* consider all years in holiday list ([3180266](3180266150))
* date in master document for dictionary condition ([670d615](670d61547f))
* empty category in Plaid ([1acaa20](1acaa20ee1))
* ignore cancelled payments in Sales/Purchase Register ([36b8e97](36b8e972f1))
* incorrect active serial nos due to backdated transactions ([1a26c70](1a26c70df2))
* incorrect percentage received in purchase invoice ([453700d](453700d0ab))
* incorrect sql error if account name has '%' ([d0e3458](d0e3458c8c))
* modified date ([e9d2437](e9d2437c7a))
* modified date ([28434d1](28434d101b))
* modified date was not set ([724c934](724c934fbb))
* modified date was not updated ([cb67574](cb6757437e))
* pass accounts as list to query ([a6bc5ca](a6bc5cae90))
* performance issue related to stock entry (backport [#39301](https://github.com/frappe/erpnext/issues/39301)) ([#39302](https://github.com/frappe/erpnext/issues/39302)) ([dc7c9e7](dc7c9e7aff))
* possible typeerror in consolidated report ([9395f75](9395f7535b))
* possible typerror in utils.js ([4ea72f4](4ea72f4b69))
* project filters on Delivery Note and Sales Order ([520cdb6](520cdb6f32))
* project query controller logic ([f2e577b](f2e577bec7))
* reset default after test ([6bd01f2](6bd01f227e))
* resolved merge conflict ([4edb73d](4edb73d398))
* show bill_date and bill_no in Purchase Register ([4b19792](4b197920c1))
* **test:** test case for project query ([98967ed](98967ed584))
* unreconcile Bank Transaction on cancel of payment voucher ([755576b](755576bd78))
* use child table values instead of global min max ([d21fc60](d21fc6055c))
* WDV as per IT Act: calculate yearly amount first and then split it based on months ([3989b97](3989b97579))
* wrong file name ([9d256e1](9d256e131d))

### Features

* provision to select the qty field for Product Page ([#39292](https://github.com/frappe/erpnext/issues/39292)) ([d42db11](d42db1174d))
2024-01-17 17:05:44 +00:00
Deepesh Garg
e74e5875ea Merge pull request #39404 from frappe/version-14-hotfix
chore: release v14
2024-01-17 22:34:31 +05:30
Nabin Hait
05a2fb3011 Merge pull request #39437 from frappe/mergify/bp/version-14-hotfix/pr-39429
fix: composite asset capitalization using asset components (backport #39429)
2024-01-17 22:05:19 +05:30
Nabin Hait
ac6020a940 fix: composite asset capitalization using asset components
(cherry picked from commit 5df40661d2)
2024-01-17 15:35:56 +00:00
Nabin Hait
9bcbd94bf1 Merge pull request #39421 from frappe/mergify/bp/version-14-hotfix/pr-39386
fix: Cancel asset capitalisation record on cancellation of asset and vice-versa (backport #39386)
2024-01-17 21:05:42 +05:30
Nabin Hait
4edb73d398 fix: resolved merge conflict 2024-01-17 17:36:42 +05:30
Nabin Hait
82732f976a Merge pull request #39432 from frappe/revert-39419-mergify/bp/version-14-hotfix/pr-39385
Revert "fix: WDV as per IT Act: calculate yearly amount first and then split it based on months (backport #39385)"
2024-01-17 17:32:38 +05:30
Nabin Hait
8014839795 Revert "fix: WDV as per IT Act: calculate yearly amount first and then split it based on months (backport #39385)" 2024-01-17 17:32:14 +05:30
Nabin Hait
8d1164f652 Merge pull request #39419 from frappe/mergify/bp/version-14-hotfix/pr-39385
fix: WDV as per IT Act: calculate yearly amount first and then split it based on months (backport #39385)
2024-01-17 17:29:32 +05:30
Nabin Hait
1dff96057c fix: Cancel asset capitalisation record on cancellation of asset and vice-versa
(cherry picked from commit efe9f6656f)

# Conflicts:
#	erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
2024-01-17 06:44:52 +00:00
Nabin Hait
3989b97579 fix: WDV as per IT Act: calculate yearly amount first and then split it based on months
(cherry picked from commit 22bd6a54b2)

# Conflicts:
#	erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py
2024-01-17 06:44:25 +00:00
Deepesh Garg
bee90e56f5 Merge pull request #39410 from ljain112/fix-pr
fix(Purchase Register): show bill_date and bill_no (#38742)
2024-01-17 11:00:24 +05:30
ruthra kumar
5693979165 Merge pull request #39411 from frappe/mergify/bp/version-14-hotfix/pr-39402
fix: project query controller logic (backport #39402)
2024-01-17 10:57:36 +05:30
ruthra kumar
42c1de640c chore: resolve conflict 2024-01-17 10:36:29 +05:30
ruthra kumar
b35a83ee47 refactor: better ordering of query result
(cherry picked from commit bfe42fdccb)

# Conflicts:
#	erpnext/controllers/queries.py
2024-01-16 10:55:31 +00:00
ruthra kumar
98967ed584 fix(test): test case for project query
(cherry picked from commit 3349dde5e2)
2024-01-16 10:55:30 +00:00
ruthra kumar
f2e577bec7 fix: project query controller logic
(cherry picked from commit 4eefb445a7)

# Conflicts:
#	erpnext/controllers/queries.py
2024-01-16 10:55:30 +00:00
ljain112
4b197920c1 fix: show bill_date and bill_no in Purchase Register 2024-01-16 15:53:53 +05:30
ruthra kumar
f3628c7d1a Merge pull request #39392 from frappe/mergify/bp/version-14-hotfix/pr-39391
fix: possible typerror in utils.js (backport #39391)
2024-01-15 20:49:43 +05:30
ruthra kumar
4ea72f4b69 fix: possible typerror in utils.js
and remove unwanted debugging statements

(cherry picked from commit 60b26ad8b2)
2024-01-15 15:16:44 +00:00
ruthra kumar
46d846bf1a Merge pull request #39136 from frappe/mergify/bp/version-14-hotfix/pr-39125
fix: ignore cancelled payments in Sales/Purchase Register (backport #39125)
2024-01-15 14:49:22 +05:30
ruthra kumar
a6bc5cae90 fix: pass accounts as list to query 2024-01-15 14:10:55 +05:30
mergify[bot]
3caf11472d ci: bump node in release workflow (backport #39377) (backport #39379) (#39381)
ci: bump node in release workflow (backport #39377) (#39379)

* ci: bump node in release workflow

(cherry picked from commit aef87cced7)

# Conflicts:
#	.github/workflows/release.yml

* chore: `conflicts`

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
(cherry picked from commit 4af3159f62)

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2024-01-15 12:15:15 +05:30
mergify[bot]
4af3159f62 ci: bump node in release workflow (backport #39377) (#39379)
* ci: bump node in release workflow

(cherry picked from commit aef87cced7)

# Conflicts:
#	.github/workflows/release.yml

* chore: `conflicts`

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2024-01-15 12:11:17 +05:30
ruthra kumar
1ca8dd4349 Merge pull request #39374 from frappe/mergify/bp/version-14-hotfix/pr-39371
fix: incorrect sql error if account name has '%' (backport #39371)
2024-01-15 10:41:04 +05:30
ruthra kumar
9550ed84c0 Merge pull request #39105 from frappe/mergify/bp/version-14-hotfix/pr-39097
refactor: flag to control loyalty point creation at invoice level (backport #39097)
2024-01-15 09:59:30 +05:30
ruthra kumar
d0e3458c8c fix: incorrect sql error if account name has '%'
(cherry picked from commit 641c3de0ca)
2024-01-15 04:10:30 +00:00
rohitwaghchaure
55e790cd2f Merge pull request #39368 from frappe/mergify/bp/version-14-hotfix/pr-39224
Update purchase_taxes_and_charges.json label Rate to Tax Rate (backport #39224)
2024-01-14 11:59:59 +05:30
rohitwaghchaure
e9d2437c7a fix: modified date
(cherry picked from commit 6827edb2c5)
2024-01-14 05:57:43 +00:00
rohitwaghchaure
724c934fbb fix: modified date was not set
(cherry picked from commit 566876ae7a)
2024-01-14 05:57:43 +00:00
mahsem
e9af0c6e67 Update purchase_taxes_and_charges.json label Rate to Tax Rate
Change Rate label to existing Tax Rate label so it can be correctly translated in other languages

(cherry picked from commit bd464197c4)
2024-01-14 05:57:42 +00:00
rohitwaghchaure
e0d12a9b98 Merge pull request #39360 from frappe/mergify/bp/version-14-hotfix/pr-39225
Update sales_taxes_and_charges.json (backport #39225)
2024-01-14 11:24:33 +05:30
rohitwaghchaure
2cc756be57 Merge pull request #39364 from frappe/mergify/bp/version-14-hotfix/pr-39359
fix: added item group in stock reco (backport #39359)
2024-01-14 11:24:08 +05:30
rohitwaghchaure
28434d101b fix: modified date 2024-01-14 10:44:18 +05:30
rohitwaghchaure
4d11a9c884 chore: fix conflicts 2024-01-14 10:32:10 +05:30
Rohit Waghchaure
c69a59c3c6 fix: added item group in stock reco
(cherry picked from commit 116ff8241c)

# Conflicts:
#	erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
2024-01-14 04:58:27 +00:00
rohitwaghchaure
cb6757437e fix: modified date was not updated
(cherry picked from commit f567af49a6)
2024-01-14 04:46:29 +00:00
mahsem
664abc6287 Update sales_taxes_and_charges.json
Change Rate label to existing Tax Rate label so it can be correctly translated in other languages

(cherry picked from commit 2b93be1139)
2024-01-14 04:46:29 +00:00
Deepesh Garg
cc6b6e1653 Merge pull request #39328 from frappe/mergify/bp/version-14-hotfix/pr-38974
fix: unreconcile Bank Transaction on cancel of payment voucher (#38974)
2024-01-14 10:14:46 +05:30
rohitwaghchaure
4815655a6b Merge pull request #39324 from frappe/mergify/bp/version-14-hotfix/pr-39305
fix: naming series variable parsing for FY (backport #39305)
2024-01-14 10:12:00 +05:30
rohitwaghchaure
5981b0e4c4 Merge pull request #39098 from RJPvT/patch-5
fix: wrong file name
2024-01-14 10:07:54 +05:30
rohitwaghchaure
bbee70aea5 Merge pull request #39357 from RJPvT/patch-7
fix: empty category in Plaid
2024-01-14 09:42:55 +05:30
rohitwaghchaure
5336cd49c9 chore: fix conflicts 2024-01-14 09:41:48 +05:30
RJPvT
1acaa20ee1 fix: empty category in Plaid 2024-01-13 16:09:11 +01:00
rohitwaghchaure
a7922e1ef1 Merge pull request #39341 from rohitwaghchaure/fixed-recalculate-current-qty-for-serial-nos
fix: incorrect active serial nos due to backdated transactions
2024-01-13 12:25:07 +05:30
Rohit Waghchaure
1a26c70df2 fix: incorrect active serial nos due to backdated transactions 2024-01-13 10:10:02 +05:30
rohitwaghchaure
519bed9b0f Merge pull request #39344 from frappe/mergify/bp/version-14-hotfix/pr-39333
fix: added indexing to improve performance (backport #39333)
2024-01-12 21:19:37 +05:30
rohitwaghchaure
f1e4beb55a Merge pull request #39343 from frappe/mergify/bp/version-14-hotfix/pr-38970
fix: use local attribute range in multiple item variant dialog (backport #38970)
2024-01-12 20:57:06 +05:30
rohitwaghchaure
c2fee6c25d chore: fix conflicts 2024-01-12 20:56:40 +05:30
Rohit Waghchaure
d9f7070f92 fix: added indexing to improve performance
(cherry picked from commit ac81323fec)

# Conflicts:
#	erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py
2024-01-12 13:10:33 +00:00
Gursheen Anand
d21fc6055c fix: use child table values instead of global min max
(cherry picked from commit 43fed29514)
2024-01-12 13:08:33 +00:00
mergify[bot]
7ae1df60be chore: remove share, print and email permissions from Buying Settings (backport #39337) (#39338)
chore: remove share, print and email permissions from Buying Settings

(cherry picked from commit 3c46abca6c)

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2024-01-12 16:48:50 +05:30
rohitwaghchaure
9b85d425fa Merge pull request #39311 from frappe/mergify/bp/version-14-hotfix/pr-39299
fix: circular dependency error on deletion of QC and Stock Entry (backport #39299)
2024-01-11 20:34:34 +05:30
Raffael Meyer
385ed8aa80 Merge pull request #39326 from frappe/mergify/bp/version-14-hotfix/pr-39117
fix: consider all years in holiday list (backport #39117)
2024-01-11 15:04:24 +01:00
barredterra
4998d68564 chore: resolve merge confilcts 2024-01-11 15:04:01 +01:00
barredterra
4a1a4b06dd test: cancel voucher linked to Bank Transaction
(cherry picked from commit 517bedeb7e)
2024-01-11 13:37:32 +00:00
barredterra
755576bd78 fix: unreconcile Bank Transaction on cancel of payment voucher
(cherry picked from commit 0a95b38166)

# Conflicts:
#	erpnext/accounts/doctype/bank_transaction/bank_transaction.py
#	erpnext/accounts/doctype/journal_entry/journal_entry.js
2024-01-11 13:37:32 +00:00
barredterra
f3d8d273f4 test: improve test for local holidays
(cherry picked from commit 60329ade9e)
2024-01-11 13:28:41 +00:00
barredterra
3180266150 fix: consider all years in holiday list
(cherry picked from commit 300aaa39fe)
2024-01-11 13:28:40 +00:00
Gursheen Anand
6bd01f227e fix: reset default after test
(cherry picked from commit 813b7a96fb)
2024-01-11 13:26:02 +00:00
Gursheen Anand
d95b14d5b8 test: naming series variable parsing
(cherry picked from commit bbdf98a8f0)
2024-01-11 13:26:02 +00:00
Gursheen Anand
670d61547f fix: date in master document for dictionary condition
(cherry picked from commit d96a777edd)

# Conflicts:
#	erpnext/accounts/utils.py
2024-01-11 13:26:01 +00:00
ruthra kumar
db3cb5c994 Merge pull request #39322 from frappe/mergify/bp/version-14-hotfix/pr-39212
fix: project filters on Delivery Note and Sales Order (backport #39212)
2024-01-11 18:06:55 +05:30
ruthra kumar
520cdb6f32 fix: project filters on Delivery Note and Sales Order
(cherry picked from commit 9ba6ff67d5)
2024-01-11 12:34:44 +00:00
ruthra kumar
e5b90c8ec5 Merge pull request #39321 from frappe/mergify/bp/version-14-hotfix/pr-39320
fix: possible typeerror in consolidated report (backport #39320)
2024-01-11 17:13:00 +05:30
ruthra kumar
faa312e680 Merge pull request #39318 from frappe/mergify/bp/version-14-hotfix/pr-39317
fix: broken dimension filters in Sales/Purchase register (backport #39317)
2024-01-11 17:12:48 +05:30
ruthra kumar
9395f7535b fix: possible typeerror in consolidated report
(cherry picked from commit 268731aec4)
2024-01-11 16:59:32 +05:30
ruthra kumar
298cdf5f0e fix: broken dimension filters in Sales/Purchase register
(cherry picked from commit 7b3f9386d7)
2024-01-11 11:20:42 +00:00
rohitwaghchaure
531f15b5d8 Merge pull request #39314 from frappe/mergify/bp/version-14-hotfix/pr-39215
fix: incorrect percentage received in purchase invoice (backport #39215)
2024-01-11 15:14:52 +05:30
rohitwaghchaure
daf0e435e2 chore: fix conflicts 2024-01-11 14:53:21 +05:30
Rohit Waghchaure
453700d0ab fix: incorrect percentage received in purchase invoice
(cherry picked from commit 8d2c78867e)
2024-01-11 09:21:23 +00:00
Rohit Waghchaure
dfcb746774 fix: circular dependency error while deleting QC
(cherry picked from commit 7cc324e31e)

# Conflicts:
#	erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
2024-01-11 09:20:08 +00:00
Frappe PR Bot
5ae050ecd4 chore(release): Bumped to Version 14.60.1
## [14.60.1](https://github.com/frappe/erpnext/compare/v14.60.0...v14.60.1) (2024-01-10)

### Bug Fixes

* performance issue related to stock entry (backport [#39301](https://github.com/frappe/erpnext/issues/39301)) ([#39302](https://github.com/frappe/erpnext/issues/39302)) ([49d914b](49d914bdb6))
2024-01-10 16:55:11 +00:00
rohitwaghchaure
d3cc0c9aea Merge pull request #39304 from frappe/mergify/bp/version-14/pr-39302
fix: performance issue related to stock entry (backport #39301) (backport #39302)
2024-01-10 22:23:58 +05:30
mergify[bot]
49d914bdb6 fix: performance issue related to stock entry (backport #39301) (#39302)
fix: performance issue related to stock entry (#39301)

(cherry picked from commit c67b0a3a64)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
(cherry picked from commit dc7c9e7aff)
2024-01-10 16:53:05 +00:00
mergify[bot]
dc7c9e7aff fix: performance issue related to stock entry (backport #39301) (#39302)
fix: performance issue related to stock entry (#39301)

(cherry picked from commit c67b0a3a64)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-01-10 22:22:16 +05:30
Frappe PR Bot
5fd68f7204 chore(release): Bumped to Version 14.60.0
# [14.60.0](https://github.com/frappe/erpnext/compare/v14.59.0...v14.60.0) (2024-01-10)

### Features

* provision to select the qty field for Product Page (backport [#39292](https://github.com/frappe/erpnext/issues/39292)) ([#39300](https://github.com/frappe/erpnext/issues/39300)) ([760af49](760af497ca))
2024-01-10 16:24:39 +00:00
mergify[bot]
760af497ca feat: provision to select the qty field for Product Page (backport #39292) (#39300)
feat: provision to select the qty field for Product Page (#39292)

feat: provision to select the qty field to be shown as `In Stock` in product page
(cherry picked from commit d42db1174d)

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2024-01-10 21:51:34 +05:30
s-aga-r
d42db1174d feat: provision to select the qty field for Product Page (#39292)
feat: provision to select the qty field to be shown as `In Stock` in product page
2024-01-10 21:31:03 +05:30
Frappe PR Bot
2e6f1378f1 chore(release): Bumped to Version 14.59.0
# [14.59.0](https://github.com/frappe/erpnext/compare/v14.58.1...v14.59.0) (2024-01-10)

### Bug Fixes

* add read permission to Buying Settings (backport [#39158](https://github.com/frappe/erpnext/issues/39158)) ([#39257](https://github.com/frappe/erpnext/issues/39257)) ([a15ad80](a15ad804d0))
* asset WDV depreciation calc according to IT act ([1cbe1e8](1cbe1e894d))
* BOM replace tool does not update exploded items of root (backport [#39244](https://github.com/frappe/erpnext/issues/39244)) ([#39249](https://github.com/frappe/erpnext/issues/39249)) ([c2eeeec](c2eeeecac8))
* don't set rate for non-stock item in Internal Transfer (backport [#39140](https://github.com/frappe/erpnext/issues/39140)) ([#39168](https://github.com/frappe/erpnext/issues/39168)) ([06d193a](06d193ad87))
* Duplicate Closing Stock Balance (backport [#39262](https://github.com/frappe/erpnext/issues/39262)) ([#39263](https://github.com/frappe/erpnext/issues/39263)) ([a3146c3](a3146c39dd))
* **Employee:** treeview ([#39126](https://github.com/frappe/erpnext/issues/39126)) ([080a742](080a742725))
* FG Item incorrect qty in the work order (backport [#39200](https://github.com/frappe/erpnext/issues/39200)) ([#39210](https://github.com/frappe/erpnext/issues/39210)) ([5e517cf](5e517cfc77))
* Ignore asset qty and status validation while cancelling LCV ([952cee3](952cee3d6a))
* Ignore UP on "allowed to transact with" (backport [#39103](https://github.com/frappe/erpnext/issues/39103)) ([#39104](https://github.com/frappe/erpnext/issues/39104)) ([7d64df0](7d64df05bc))
* improved validation message ([580e9f6](580e9f6e10))
* incorrect indicator title for portal sales order (backport [#39247](https://github.com/frappe/erpnext/issues/39247)) ([#39254](https://github.com/frappe/erpnext/issues/39254)) ([8c496fb](8c496fbf2b))
* inventory dimension negative stock validation (backport [#39149](https://github.com/frappe/erpnext/issues/39149)) ([#39150](https://github.com/frappe/erpnext/issues/39150)) ([2866f7c](2866f7c441))
* possible typeerror on transaction.js ([8fe346a](8fe346aef8))
* Purchase date and amount is not mandatory for composite asset creation ([0f6477a](0f6477a253))
* remove global _("translation") calls (backport [#32828](https://github.com/frappe/erpnext/issues/32828)) ([#39231](https://github.com/frappe/erpnext/issues/39231)) ([ee7474b](ee7474ba20))
* resolved conflict ([b529883](b529883897))
* set `First Name` in Supplier Contact ([69c460c](69c460c756))
* Set asset purchase amount based on qty and valuation_rate ([41e3843](41e384326e))
* set parent doctype on chart (backport [#39286](https://github.com/frappe/erpnext/issues/39286)) ([#39287](https://github.com/frappe/erpnext/issues/39287)) ([a2a8558](a2a8558677))
* Show maintain-stock and is-fixed-asset checkbox in item quick entry dialog ([7b4b630](7b4b630b2c))
* Show timesheet table after fetching data from timesheet (backport [#39275](https://github.com/frappe/erpnext/issues/39275)) ([#39280](https://github.com/frappe/erpnext/issues/39280)) ([1cc887a](1cc887a997))
* skip rate validation for return `DN Items` with `Moving Average` valuation (backport [#39242](https://github.com/frappe/erpnext/issues/39242)) ([#39265](https://github.com/frappe/erpnext/issues/39265)) ([f42e93b](f42e93bf6c))
* total allocated percentage for sales team issue ([71f9b7f](71f9b7f675))
* TypeError is pricing rules (backport [#39252](https://github.com/frappe/erpnext/issues/39252)) ([#39259](https://github.com/frappe/erpnext/issues/39259)) ([36ba33c](36ba33c500))
* typerror on multi select dialog ([71ecf08](71ecf081c3))
* update Maintenance Schedule status on Maintenance Visit submit (backport [#39167](https://github.com/frappe/erpnext/issues/39167)) ([#39185](https://github.com/frappe/erpnext/issues/39185)) ([2ea2146](2ea2146b34))
* **UX:** dont override framework's permission check messages (backport [#39118](https://github.com/frappe/erpnext/issues/39118)) ([#39119](https://github.com/frappe/erpnext/issues/39119)) ([26ae708](26ae708d6d))

### Features

* Copy project_name, from_time, to_time from timesheet details to sales invoice ([#33726](https://github.com/frappe/erpnext/issues/33726)) ([d0e1162](d0e1162c96))
2024-01-10 10:35:32 +00:00
Deepesh Garg
2d0e1830a9 Merge pull request #39246 from frappe/version-14-hotfix
chore: release v14
2024-01-10 16:04:20 +05:30
mergify[bot]
1cc887a997 fix: Show timesheet table after fetching data from timesheet (backport #39275) (#39280)
fix: Show timesheet table after fetching data from timesheet

(cherry picked from commit e1ba5878a3)

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2024-01-10 12:33:28 +05:30
mergify[bot]
ee7474ba20 fix: remove global _("translation") calls (backport #32828) (#39231)
fix: remove global _("translation") calls (#32828)

This is not how it works. Translations are dynamic based on language
sets during request (using header, user's preferences etc)

Calling them on global variables makes no sense.

Ref: https://github.com/frappe/frappe/pull/18733
(cherry picked from commit 75983ce809)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2024-01-10 12:31:59 +05:30
mergify[bot]
a2a8558677 fix: set parent doctype on chart (backport #39286) (#39287)
fix: set parent doctype on chart (#39286)

(cherry picked from commit 38c5ecf007)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2024-01-10 12:27:13 +05:30
Nabin Hait
cc07213faa Merge pull request #39189 from frappe/mergify/bp/version-14-hotfix/pr-39163
fix: Purchase information is not required for creating a composite asset (backport #39163)
2024-01-10 12:15:43 +05:30
Nabin Hait
f4e3f0d656 Merge pull request #39278 from frappe/mergify/bp/version-14-hotfix/pr-39191
fix: Ignore asset qty and status validation while cancelling LCV (backport #39191)
2024-01-10 12:14:36 +05:30
Nabin Hait
0379ea5086 Merge pull request #39277 from frappe/mergify/bp/version-14-hotfix/pr-39238
fix: Set asset purchase amount based on qty and valuation_rate (backport #39238)
2024-01-10 12:14:24 +05:30
Nabin Hait
65005d8630 Merge pull request #39050 from anandbaburajan/asset_wdv_india
fix: asset WDV depreciation calc according to IT act [v14]
2024-01-10 12:13:53 +05:30
Nabin Hait
3f3fc8214d Merge pull request #39217 from frappe/mergify/bp/version-14-hotfix/pr-39214
fix: Show maintain-stock and is-fixed-asset checkbox in item quick entry dialog (backport #39214)
2024-01-10 12:12:24 +05:30
Nabin Hait
b529883897 fix: resolved conflict 2024-01-10 12:01:32 +05:30
ruthra kumar
3d621d47a6 Merge pull request #39283 from frappe/mergify/bp/version-14-hotfix/pr-39282
fix: possible typeerror on transaction.js (backport #39282)
2024-01-10 12:00:50 +05:30
ruthra kumar
8fe346aef8 fix: possible typeerror on transaction.js
(cherry picked from commit 9f27ac142b)
2024-01-10 06:24:46 +00:00
Nabin Hait
952cee3d6a fix: Ignore asset qty and status validation while cancelling LCV
(cherry picked from commit e9d36242ce)
2024-01-10 06:04:10 +00:00
Nabin Hait
41e384326e fix: Set asset purchase amount based on qty and valuation_rate
(cherry picked from commit 135e19d0aa)
2024-01-10 06:03:03 +00:00
mergify[bot]
f42e93bf6c fix: skip rate validation for return DN Items with Moving Average valuation (backport #39242) (#39265)
* fix: skip rate validation for return `DN Items` with `Moving Average` valuation

(cherry picked from commit e0ad52b500)

# Conflicts:
#	erpnext/controllers/sales_and_purchase_return.py

* chore: `conflicts`

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2024-01-10 10:53:09 +05:30
mergify[bot]
a3146c39dd fix: Duplicate Closing Stock Balance (backport #39262) (#39263)
fix: Duplicate Closing Stock Balance

(cherry picked from commit b15795392b)

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2024-01-09 20:37:49 +05:30
mergify[bot]
a15ad804d0 fix: add read permission to Buying Settings (backport #39158) (#39257)
* fix: add read permission to Buying Settings

(cherry picked from commit e05bf9d32a)

# Conflicts:
#	erpnext/buying/doctype/buying_settings/buying_settings.json

* chore: resolve merge conflicts

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2024-01-09 19:09:06 +05:30
mergify[bot]
8c496fbf2b fix: incorrect indicator title for portal sales order (backport #39247) (#39254)
fix: incorrect indicator title for portal sales order (#39247)

(cherry picked from commit 2d2ff7cf52)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-01-09 17:43:18 +05:30
mergify[bot]
36ba33c500 fix: TypeError is pricing rules (backport #39252) (#39259)
fix: TypeError is pricing rules (#39252)

(cherry picked from commit 274c65c451)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-01-09 17:42:04 +05:30
mergify[bot]
c2eeeecac8 fix: BOM replace tool does not update exploded items of root (backport #39244) (#39249)
fix: BOM replace tool does not update exploded items of root (#39244)

(cherry picked from commit 5e0d017497)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-01-09 15:59:24 +05:30
ruthra kumar
c9d4c41db3 Merge pull request #39240 from frappe/mergify/bp/version-14-hotfix/pr-39237
fix: sales team commission overallocation on fetching items from multiple quotations to Sales Order (backport #39237)
2024-01-09 14:40:15 +05:30
ruthra kumar
71f9b7f675 fix: total allocated percentage for sales team issue
(cherry picked from commit b498094a97)
2024-01-09 07:38:57 +00:00
ruthra kumar
0ebd133eda Merge pull request #39236 from frappe/mergify/bp/version-14-hotfix/pr-33726
feat: Copy project_name, from_time, to_time from timesheet details to sales invoice. (backport #33726)
2024-01-09 12:43:27 +05:30
Kevin Shenk
d0e1162c96 feat: Copy project_name, from_time, to_time from timesheet details to sales invoice (#33726)
feat: Copy project_name, from_time, to_time from timesheet details to sales invoice
(cherry picked from commit e4bceaaf66)
2024-01-09 06:33:24 +00:00
Nabin Hait
7b4b630b2c fix: Show maintain-stock and is-fixed-asset checkbox in item quick entry dialog
(cherry picked from commit c14986f9e6)
2024-01-08 16:07:25 +00:00
mergify[bot]
5e517cfc77 fix: FG Item incorrect qty in the work order (backport #39200) (#39210)
fix: FG Item incorrect qty in the work order (#39200)

(cherry picked from commit 466625213b)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-01-08 17:43:36 +05:30
mergify[bot]
2ea2146b34 fix: update Maintenance Schedule status on Maintenance Visit submit (backport #39167) (#39185)
* fix: make `Sales Person` non-mandatory

(cherry picked from commit 4d56f725fe)

* fix: update Maintenance Schedule status on Maintenance Visit submit

(cherry picked from commit cd293a5173)

# Conflicts:
#	erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py
#	erpnext/maintenance/doctype/maintenance_visit_purpose/maintenance_visit_purpose.py

* chore: `conflicts`

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2024-01-08 16:27:19 +05:30
s-aga-r
856926d664 Merge pull request #39184 from s-aga-r/FIX-7838
fix: set `First Name` in Supplier Contact
2024-01-08 15:36:07 +05:30
Nabin Hait
0f6477a253 fix: Purchase date and amount is not mandatory for composite asset creation
(cherry picked from commit c34f09c503)

# Conflicts:
#	erpnext/assets/doctype/asset/asset.py
2024-01-08 06:52:33 +00:00
Nabin Hait
580e9f6e10 fix: improved validation message
(cherry picked from commit fe43dab4d7)
2024-01-08 06:52:32 +00:00
s-aga-r
69c460c756 fix: set First Name in Supplier Contact 2024-01-08 11:47:00 +05:30
mergify[bot]
06d193ad87 fix: don't set rate for non-stock item in Internal Transfer (backport #39140) (#39168)
* fix: don't set rate for non-stock item in Internal Transfer

(cherry picked from commit e1b0fffd0c)

* test: internal transfer for non-stock item

(cherry picked from commit 57b6a98703)

# Conflicts:
#	erpnext/stock/doctype/delivery_note/test_delivery_note.py

* chore: `conflicts`

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2024-01-08 11:44:52 +05:30
ruthra kumar
f6b10f3d84 Merge pull request #39180 from frappe/mergify/bp/version-14-hotfix/pr-39159
refactor: prevent permission error by always processing bulk transaction in background (backport #39159)
2024-01-08 10:30:25 +05:30
ruthra kumar
f236a2c081 refactor: prevent permissions by always processing in background
(cherry picked from commit 15dc5c7e99)
2024-01-08 04:42:49 +00:00
mergify[bot]
2866f7c441 fix: inventory dimension negative stock validation (backport #39149) (#39150)
fix: inventory dimension negative stock validation (#39149)

(cherry picked from commit bae7c64964)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-01-05 05:03:22 +05:30
Raffael Meyer
948045ea32 Merge pull request #39147 from frappe/mergify/bp/version-14-hotfix/pr-39126
fix(Employee): treeview (backport #39126)
2024-01-04 15:23:53 +01:00
Raffael Meyer
080a742725 fix(Employee): treeview (#39126)
(cherry picked from commit e912e9597d)
2024-01-04 12:34:48 +00:00
ruthra kumar
36b8e972f1 fix: ignore cancelled payments in Sales/Purchase Register
(cherry picked from commit 0f1be03faf)
2024-01-04 16:24:07 +05:30
Frappe PR Bot
feaa16d748 chore(release): Bumped to Version 14.58.1
## [14.58.1](https://github.com/frappe/erpnext/compare/v14.58.0...v14.58.1) (2024-01-03)

### Bug Fixes

* typerror on multi select dialog ([43cc7e4](43cc7e4a59))
2024-01-03 15:37:55 +00:00
ruthra kumar
6ca29a93bf Merge pull request #39123 from frappe/mergify/bp/version-14/pr-39116
fix: typerror on multi select dialog (backport #39116)
2024-01-03 21:06:48 +05:30
ruthra kumar
20aac93731 Merge pull request #39121 from frappe/mergify/bp/version-14-hotfix/pr-39116
fix: typerror on multi select dialog (backport #39116)
2024-01-03 21:05:48 +05:30
ruthra kumar
43cc7e4a59 fix: typerror on multi select dialog
(cherry picked from commit 7da9ffa3bd)
2024-01-03 15:26:11 +00:00
ruthra kumar
71ecf081c3 fix: typerror on multi select dialog
(cherry picked from commit 7da9ffa3bd)
2024-01-03 15:24:49 +00:00
mergify[bot]
26ae708d6d fix(UX): dont override framework's permission check messages (backport #39118) (#39119)
fix(UX): dont override framework's permission check messages (#39118)

(cherry picked from commit e84c9f7c51)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2024-01-03 20:32:55 +05:30
ruthra kumar
9277b02557 refactor: flag to control loyalty point creation at invoice level
(cherry picked from commit 1bc74bde29)
2024-01-03 13:39:32 +05:30
mergify[bot]
7d64df05bc fix: Ignore UP on "allowed to transact with" (backport #39103) (#39104)
fix: Ignore UP on "allowed to transact with" (#39103)

If a customer is allowed to transact with some company it usually
doesn't imply that customer is somehow "linked with" that company.

(cherry picked from commit 6401908f41)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2024-01-03 13:00:44 +05:30
Frappe PR Bot
b6a36270b2 chore(release): Bumped to Version 14.58.0
# [14.58.0](https://github.com/frappe/erpnext/compare/v14.57.0...v14.58.0) (2024-01-03)

### Bug Fixes

* Add missing french translations (backport [#38368](https://github.com/frappe/erpnext/issues/38368)) ([#38513](https://github.com/frappe/erpnext/issues/38513)) ([2c90ee2](2c90ee23f9))
* **DX:** capture tracebacks with context (backport [#39060](https://github.com/frappe/erpnext/issues/39060)) ([#39062](https://github.com/frappe/erpnext/issues/39062)) ([fe9acc8](fe9acc898e))
* **Hierarchy Chart:** check if company is set before loading children ([#38985](https://github.com/frappe/erpnext/issues/38985)) ([e4d6df3](e4d6df39ff))
* issue occured when creating supplier with contact details (backport [#38147](https://github.com/frappe/erpnext/issues/38147)) ([#39046](https://github.com/frappe/erpnext/issues/39046)) ([81ef7b4](81ef7b4c00))
* Opening balance in bank reconciliation tool ([#38977](https://github.com/frappe/erpnext/issues/38977)) ([a2cba1b](a2cba1bf23))
* remove bad defaults (backport [#38986](https://github.com/frappe/erpnext/issues/38986)) ([#38987](https://github.com/frappe/erpnext/issues/38987)) ([29d383a](29d383ad8b))
* select options should dynamically load dimensions ([bfc94cf](bfc94cf284))
* take quantity into account when setting asset's gross purchase amt (backport [#39056](https://github.com/frappe/erpnext/issues/39056)) ([#39057](https://github.com/frappe/erpnext/issues/39057)) ([b8dce3e](b8dce3eeac))
* undefined error in Budget Variance and Profitability report ([cad15cd](cad15cdec2))
* use `Stock Qty` while getting `POS Reserved Qty` (backport [#38962](https://github.com/frappe/erpnext/issues/38962)) ([#38982](https://github.com/frappe/erpnext/issues/38982)) ([7ad42ec](7ad42ec957))
* Validate account in Sales/Purchase Taxes and Charges Template ([#39013](https://github.com/frappe/erpnext/issues/39013)) ([c0b5980](c0b598074c))
* work order with multi level, fetch operting cost from sub-assembly (backport [#38992](https://github.com/frappe/erpnext/issues/38992)) ([#39027](https://github.com/frappe/erpnext/issues/39027)) ([88e5c9e](88e5c9e61b))

### Features

* Merge taxes from mapped docs ([#38346](https://github.com/frappe/erpnext/issues/38346)) ([c74e6aa](c74e6aaebb))
* Show Ledger view for Purchase & Sales Register ([#38801](https://github.com/frappe/erpnext/issues/38801)) ([04fb215](04fb215ff5))

### Performance Improvements

* index item_code in bom explosion item (backport [#39085](https://github.com/frappe/erpnext/issues/39085)) ([#39087](https://github.com/frappe/erpnext/issues/39087)) ([558861b](558861b634))
2024-01-03 06:01:14 +00:00
Deepesh Garg
fa442ca788 Merge pull request #39091 from frappe/version-14-hotfix
chore: release v14
2024-01-03 11:29:59 +05:30
RJPvT
9d256e131d fix: wrong file name
file name is actually reversed.

In [1]: from premailer import Premailer
   ...:
   ...: from frappe.utils.jinja_globals import bundled_asset
   ...:
   ...: # get email css files from hooks
   ...: css_files = frappe.get_hooks("email_css")
   ...: css_files = [frappe.utils.jinja_globals.bundled_asset(path) for path in
   ...: css_files]
   ...: css_files = [path.lstrip("/") for path in css_files]

In [2]: css_files
Out[2]:
['assets/frappe/dist/css/email.bundle.ELTO33N3.css',
 'email_erpnext.bundle.css',
 'assets/css/email.css']

In [3]: css_files = [css_file for css_file in css_files if os.path.exists(os.pat
   ...: h.abspath(css_file))]

In [4]: css_files
Out[4]: ['assets/frappe/dist/css/email.bundle.ELTO33N3.css', 'assets/css/email.css']
2024-01-02 22:01:51 +01:00
mergify[bot]
558861b634 perf: index item_code in bom explosion item (backport #39085) (#39087)
perf: index item_code in bom explosion item (#39085)

(cherry picked from commit 739434b727)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2024-01-02 14:43:23 +05:30
mergify[bot]
2c90ee23f9 fix: Add missing french translations (backport #38368) (#38513)
* fix: Add missing french translations (#38368)

fix: Add missing french translation

Co-authored-by: barredterra <14891507+barredterra@users.noreply.github.com>
(cherry picked from commit a1e0197a8b)

# Conflicts:
#	erpnext/translations/fr.csv

* chore: resolve merge conflicts

---------

Co-authored-by: noec764 <58433943+noec764@users.noreply.github.com>
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2024-01-02 13:51:43 +05:30
ruthra kumar
78771cd72f Merge pull request #39071 from frappe/mergify/bp/version-14-hotfix/pr-39067
fix: undefined error in Budget Variance and Profitability report (backport #39067)
2024-01-01 17:49:03 +05:30
ruthra kumar
bfc94cf284 fix: select options should dynamically load dimensions
(cherry picked from commit 1a9e091d12)
2024-01-01 17:41:15 +05:30
ruthra kumar
cad15cdec2 fix: undefined error in Budget Variance and Profitability report
'Budget' and 'Budget Account' doesn't have support for dynamic
dimension. It only supports hard-coded ones -  Project and Cost Center

(cherry picked from commit 92bc962f60)

# Conflicts:
#	erpnext/accounts/report/profitability_analysis/profitability_analysis.js
2024-01-01 12:08:03 +00:00
ruthra kumar
96dfcd7149 Merge pull request #39068 from frappe/mergify/bp/version-14-hotfix/pr-39023
refactor(perf): replace account subquery with 'in' condition (backport #39023)
2024-01-01 16:29:07 +05:30
ruthra kumar
c7d17d21e8 refactor(perf): replace account subquery with 'in' condition
(cherry picked from commit a517125d64)
2024-01-01 10:13:50 +00:00
mergify[bot]
fe9acc898e fix(DX): capture tracebacks with context (backport #39060) (#39062)
* fix(DX): capture tracebacks with context (#39060)

(cherry picked from commit 510fdf7bf6)

# Conflicts:
#	erpnext/manufacturing/doctype/bom_creator/bom_creator.py

* Delete erpnext/manufacturing/doctype/bom_creator/bom_creator.py

---------

Co-authored-by: Ankush Menat <ankush@frappe.io>
2024-01-01 15:38:22 +05:30
mergify[bot]
b8dce3eeac fix: take quantity into account when setting asset's gross purchase amt (backport #39056) (#39057)
fix: take quantity into account when setting asset's gross purchase amt (#39056)

fix: take quantity into account when setting asset's gross purchase amount
(cherry picked from commit 0346f47c1d)

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
2024-01-01 11:27:46 +05:30
anandbaburajan
1cbe1e894d fix: asset WDV depreciation calc according to IT act 2023-12-31 23:34:15 +05:30
mergify[bot]
81ef7b4c00 fix: issue occured when creating supplier with contact details (backport #38147) (#39046)
* fix: issue occured when creating supplier with contact details

(cherry picked from commit 7842c9fba8)

# Conflicts:
#	erpnext/selling/doctype/customer/customer.py

* fix: Suppier name was not taken when creating address from supplier

(cherry picked from commit 545ef3c234)

* chore: fix conflicts

* chore: fix linter issues

---------

Co-authored-by: kunhi <kunhimohamed6@gmail.com>
Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2023-12-31 18:06:47 +05:30
mergify[bot]
88e5c9e61b fix: work order with multi level, fetch operting cost from sub-assembly (backport #38992) (#39027)
* fix: work order with multi level, fetch operting cost from sub-assembly (#38992)

(cherry picked from commit 70abedc57a)

# Conflicts:
#	erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.py
#	erpnext/manufacturing/doctype/work_order/test_work_order.py

* chore: fix conflicts

* chore: fix conflicts

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2023-12-30 11:14:23 +05:30
mergify[bot]
c0b598074c fix: Validate account in Sales/Purchase Taxes and Charges Template (#39013)
fix: Validate account in Sales/Purchase Taxes and Charges Template (#39013)

(cherry picked from commit cd37fd790b)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-12-29 16:15:45 +05:30
Gursheen Kaur Anand
04fb215ff5 feat: Show Ledger view for Purchase & Sales Register (#38801)
* feat: fetch payments and journals

* refactor: use single qb query for PE and PI

* feat: fetch PE along with SI

* refactor: move repeating code to common controller

* fix: fetch cost center for PE

* feat: fetch JV with PE

* fix: validate party filter for fetching payments

* fix: linting issues

* refactor: use qb to fetch PE JV and Inv

* refactor: move fn to fetch advance taxes to utils & use qb

* fix: filtering through accounting dimensions

* chore: remove debugging print statements

* fix: modify rows and columns for ledger view

* refactor: filter accounting dimensions using qb

* fix: show additional table cols from india compliance api call

* chore: fix typo

* test: purchase register and ledger view

* fix: filter by party in opening row calculation

* fix: exclude cancelled gl entries for opening balance

* chore: change column format for report

* fix: check gl entry status using is_cancelled

* fix: running balance after sorting

* fix: gst itemised registers for india compliance api call

* fix: additional query cols for gst itemised registers

* fix: additional query cols for sales register

* fix: PE in sales register

* fix: tax accounts in sales register

* fix: Sales/Purchase register showing duplicate records

* fix: avoid fetching advance account for party

* refactor: remove unnecessary condition

* fix: remove checking for advance accounts in payments

---------

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-12-29 13:02:17 +05:30
mergify[bot]
a2cba1bf23 fix: Opening balance in bank reconciliation tool (#38977)
fix: Opening balance in bank reconciliation tool (#38977)

(cherry picked from commit bbee9b5637)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-12-28 17:53:27 +05:30
mergify[bot]
29d383ad8b fix: remove bad defaults (backport #38986) (#38987)
fix: remove bad defaults (#38986)

Child tables can't have a default.

(cherry picked from commit b71b0d5997)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2023-12-28 14:22:23 +05:30
Rucha Mahabal
e4d6df39ff fix(Hierarchy Chart): check if company is set before loading children (#38985)
* fix(Org Chart): check if company is set before loading children

* refactor: avoid assigning undefined values, use empty strings, null instead

* fix: returning to org chart view from employee master does not render chart

- the check for company field is truthy from the employee doctype, so it does not render company field again. Explicitly check for company field on the same page

* fix(Org Chart Mobile): check if company is set before loading children
2023-12-28 12:54:31 +05:30
mergify[bot]
7ad42ec957 fix: use Stock Qty while getting POS Reserved Qty (backport #38962) (#38982)
fix: use `Stock Qty` while getting `POS Reserved Qty`

(cherry picked from commit 7223106417)

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-12-28 11:59:47 +05:30
mergify[bot]
c74e6aaebb feat: Merge taxes from mapped docs (#38346)
* feat: Merge taxes from mapped docs (#38346)

* feat: Merge taxes from mapped docs

* chore: ci failures

(cherry picked from commit 9b1c22250f)

* chore: ci failures

---------

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-12-28 10:03:26 +05:30
VihangT
92d61eb19e fix : correct logic for overlap error (#38798)
* fix : correct logic for overlap error

* fix : 

Updated as per lintels

* fix : 

changes as per linters

* fix : correct logic for overlap error

fixing overlap error logic with taking care of sequential time job cards in overlap job card list

Added Provision if time_logs list is empty
2023-12-27 16:11:14 +05:30
Frappe PR Bot
6e479f918d chore(release): Bumped to Version 14.57.0
# [14.57.0](https://github.com/frappe/erpnext/compare/v14.56.0...v14.57.0) (2023-12-27)

### Bug Fixes

* allow to set rate manually for service item in BOM ([#38880](https://github.com/frappe/erpnext/issues/38880)) ([c2f692a](c2f692a4e4))
* display all item rate stop messages ([226d0e0](226d0e0196))
* do not reset the basic rate for the material receipt stock entry ([#38896](https://github.com/frappe/erpnext/issues/38896)) ([98bfcc4](98bfcc4c75))
* first name error on customer creation ([#38927](https://github.com/frappe/erpnext/issues/38927)) ([28f052d](28f052d586))
* german translations for Lost Quotations ([#38435](https://github.com/frappe/erpnext/issues/38435)) ([81b5e6c](81b5e6c5f1))
* groups for current accounts in German CoAs ([39aa36e](39aa36e44b))
* incorrect price list in customer-wise item price report ([7577706](7577706a9d))
* min order qty optional in production plan (backport [#38956](https://github.com/frappe/erpnext/issues/38956)) ([#38957](https://github.com/frappe/erpnext/issues/38957)) ([f6f2712](f6f2712db0))
* remove rows with zero consumed qty ([f2f4100](f2f410093a))
* reposting not fixing valuation rate for sales return using movin… ([#38895](https://github.com/frappe/erpnext/issues/38895)) ([3a668bb](3a668bbe96))
* reset the incoming rate on changing of the warehouse ([#38909](https://github.com/frappe/erpnext/issues/38909)) ([2770ca1](2770ca1b65))
* skip jvs against bank accounts ([98198d3](98198d35ea))
* **test:** expect account currency when party account is specified. ([30cb218](30cb218638))
* unset discount amount from coupon discounts ([87e8dd0](87e8dd0d63))
* use party account currency when party account is specified ([d9e0d55](d9e0d55b88))
* **ux:** make PR and PI Item rate field readonly based on `Maintain Same Rate` (backport [#38942](https://github.com/frappe/erpnext/issues/38942)) ([#38943](https://github.com/frappe/erpnext/issues/38943)) ([1eee203](1eee203f7f))

### Features

* in_party_currency option for AR/AP reports ([3c717db](3c717db3fd))
* total_asset_cost field (backport [#38879](https://github.com/frappe/erpnext/issues/38879)) ([#38886](https://github.com/frappe/erpnext/issues/38886)) ([8169c7d](8169c7de0d))

### Performance Improvements

* Drop unused/duplicate/sub-optimal indexes (backport [#38884](https://github.com/frappe/erpnext/issues/38884)) ([#38912](https://github.com/frappe/erpnext/issues/38912)) ([b254a72](b254a72d41))
* use estimated rows instead of actual rows (backport [#38830](https://github.com/frappe/erpnext/issues/38830)) ([#38875](https://github.com/frappe/erpnext/issues/38875)) ([4d1ccd9](4d1ccd9e27))
2023-12-27 08:40:26 +00:00
rohitwaghchaure
8e8d0ad9aa Merge pull request #38953 from frappe/version-14-hotfix
chore: release v14
2023-12-27 14:09:17 +05:30
rohitwaghchaure
400c78c600 Merge branch 'version-14' into version-14-hotfix 2023-12-27 10:24:01 +05:30
mergify[bot]
f6f2712db0 fix: min order qty optional in production plan (backport #38956) (#38957)
* fix: min order qty optional in production plan (#38956)

* fix: min order qty optional in production plan

* fix: test cases

(cherry picked from commit b09c9354fb)

# Conflicts:
#	erpnext/manufacturing/doctype/bom/bom.json
#	erpnext/manufacturing/doctype/production_plan/production_plan.py

* chore: fix conflicts

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2023-12-26 21:59:09 +05:30
mergify[bot]
1eee203f7f fix(ux): make PR and PI Item rate field readonly based on Maintain Same Rate (backport #38942) (#38943)
* fix(ux): make PI Item rate field editable

(cherry picked from commit eb5bb9f9a9)

* fix(ux): make PI Item rate field readonly based on `Maintain Same Rate`

(cherry picked from commit cb9114442b)

* fix(ux): make PR Item rate field readonly based on `Maintain Same Rate`

(cherry picked from commit b1ba210332)

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-12-26 14:45:54 +05:30
ruthra kumar
ec337a1c9f Merge pull request #38945 from frappe/mergify/bp/version-14-hotfix/pr-38891
fix: incorrect price list in customer-wise item price report (backport #38891)
2023-12-26 12:16:52 +05:30
ruthra kumar
7577706a9d fix: incorrect price list in customer-wise item price report
(cherry picked from commit 9a00edb031)
2023-12-26 06:17:45 +00:00
mergify[bot]
81b5e6c5f1 fix: german translations for Lost Quotations (#38435)
* fix: german translations for Lost Quotations (#38435)

(cherry picked from commit 5952cfa673)

# Conflicts:
#	erpnext/translations/de.csv

* chore: resolve conflicts

---------

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2023-12-24 16:24:31 +05:30
ruthra kumar
28f052d586 fix: first name error on customer creation (#38927)
fix: error on customer creation
2023-12-24 16:12:13 +05:30
s-aga-r
2eac618bba Merge pull request #38892 from s-aga-r/FIX-6565
fix: remove rows with zero consumed qty
2023-12-22 15:58:09 +05:30
Deepesh Garg
6f6de13813 Merge pull request #38815 from frappe/mergify/bp/version-14-hotfix/pr-38803
fix: groups for current accounts in German CoAs (#38803)
2023-12-22 08:40:28 +05:30
Deepesh Garg
be1a269f9f Merge pull request #38857 from frappe/revert-38422-mergify/bp/version-14-hotfix/pr-38250
Revert "fix: unset discount amount based on coupon code (backport #38250)"
2023-12-22 08:40:07 +05:30
Deepesh Garg
0d70be56d2 Merge pull request #38370 from pps190/backport/version-14-hotfix/38289
fix: display all item rate stop messages (#38289)
2023-12-22 08:38:19 +05:30
Deepesh Garg
50655821c1 Merge pull request #38770 from frappe/mergify/bp/version-14-hotfix/pr-38767
fix: skip JVs against bank accounts in tax report (#38767)
2023-12-22 08:37:41 +05:30
mergify[bot]
b254a72d41 perf: Drop unused/duplicate/sub-optimal indexes (backport #38884) (#38912)
* perf: Drop unused/duplicate/sub-optimal indexes (#38884)

* ci: enable more checks

* perf: Drop unused/duplicate indexes

(cherry picked from commit 787333896c)

# Conflicts:
#	erpnext/accounts/doctype/gl_entry/gl_entry.json
#	erpnext/patches.txt

* chore: conflicts

---------

Co-authored-by: Ankush Menat <ankush@frappe.io>
2023-12-21 21:40:59 +05:30
rohitwaghchaure
2770ca1b65 fix: reset the incoming rate on changing of the warehouse (#38909) 2023-12-21 18:49:36 +05:30
mergify[bot]
0d7cb1a9be chore: additional_asset_cost field (backport #38904) (backport #38905) (#38908)
chore: additional_asset_cost field (backport #38904) (#38905)

* chore: additional_asset_cost field (#38904)

(cherry picked from commit 283763dfb2)

# Conflicts:
#	erpnext/assets/doctype/asset/asset.py

* chore: resolve conflicts in asset.py

---------

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
(cherry picked from commit 3b9feffc00)

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2023-12-21 17:12:01 +05:30
mergify[bot]
3b9feffc00 chore: additional_asset_cost field (backport #38904) (#38905)
* chore: additional_asset_cost field (#38904)

(cherry picked from commit 283763dfb2)

# Conflicts:
#	erpnext/assets/doctype/asset/asset.py

* chore: resolve conflicts in asset.py

---------

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
2023-12-21 16:56:32 +05:30
rohitwaghchaure
98bfcc4c75 fix: do not reset the basic rate for the material receipt stock entry (#38896) 2023-12-21 14:13:55 +05:30
rohitwaghchaure
3a668bbe96 fix: reposting not fixing valuation rate for sales return using movin… (#38895)
fix: reposting not fixing valuation rate for sales return using moving average method
2023-12-21 14:13:34 +05:30
s-aga-r
f2f410093a fix: remove rows with zero consumed qty 2023-12-21 11:03:18 +05:30
mergify[bot]
8169c7de0d feat: total_asset_cost field (backport #38879) (#38886)
* feat: total_asset_cost field (#38879)

(cherry picked from commit d370c60a6c)

# Conflicts:
#	erpnext/assets/doctype/asset/asset.json
#	erpnext/assets/doctype/asset/asset.py
#	erpnext/patches.txt

* chore: resolve conflicts in asset.json

* chore: resolve conflicts in asset.py

* chore: resolve conflicts in patches.txt

---------

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
2023-12-20 22:57:37 +05:30
mergify[bot]
4d1ccd9e27 perf: use estimated rows instead of actual rows (backport #38830) (#38875)
* perf: use estimated rows instead of actual rows (#38830)

(cherry picked from commit 9983283f95)

# Conflicts:
#	erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py

* chore: conflicts

---------

Co-authored-by: Ankush Menat <ankush@frappe.io>
2023-12-20 18:17:41 +05:30
Deepesh Garg
c48c8b8ddb Merge pull request #38858 from GursheenK/unset-discount-amt-coupon-code
fix: unset discount amount for removed coupons
2023-12-20 18:17:20 +05:30
rohitwaghchaure
c2f692a4e4 fix: allow to set rate manually for service item in BOM (#38880) 2023-12-20 17:53:48 +05:30
ruthra kumar
d94b5a318d Merge pull request #38872 from frappe/mergify/bp/version-14-hotfix/pr-38838
fix: use party account currency when party account is specified (backport #38838)
2023-12-20 13:09:17 +05:30
Devin Slauenwhite
30cb218638 fix(test): expect account currency when party account is specified.
(cherry picked from commit a09241e3c7)
2023-12-20 07:05:27 +00:00
Devin Slauenwhite
d9e0d55b88 fix: use party account currency when party account is specified
(cherry picked from commit c7b961ffa2)
2023-12-20 07:05:26 +00:00
ruthra kumar
1e9b4e2403 Merge pull request #38800 from pps190/backport/version-14-hotfix/37746
feat: in_party_currency option for AR/AP reports (backport #37746)
2023-12-20 12:25:35 +05:30
Gursheen Anand
87e8dd0d63 fix: unset discount amount from coupon discounts 2023-12-19 15:56:59 +05:30
Gursheen Kaur Anand
37a18299d0 Revert "fix: unset discount amount based on coupon code (backport #38250)" 2023-12-19 15:35:35 +05:30
Devin Slauenwhite
226d0e0196 fix: display all item rate stop messages 2023-12-18 14:14:29 -05:00
barredterra
39aa36e44b fix: groups for current accounts in German CoAs
(cherry picked from commit 259f313af7)
2023-12-17 18:24:10 +00:00
Devin Slauenwhite
7eee573fcb fixup! feat: in_party_currency option for AR/AP reports
fix: merge conflict typo
2023-12-16 16:29:01 +00:00
Richard Case
0894022695 chore: update tests 2023-12-16 16:15:00 +00:00
Richard Case
3c717db3fd feat: in_party_currency option for AR/AP reports 2023-12-16 16:14:44 +00:00
Gursheen Anand
98198d35ea fix: skip jvs against bank accounts
(cherry picked from commit f7b2380ec1)
2023-12-15 07:29:14 +00:00
232 changed files with 6546 additions and 2851 deletions

View File

@@ -17,7 +17,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: 18
node-version: 20
- name: Setup dependencies
run: |

View File

@@ -21,7 +21,7 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version: 14
node-version: 20
check-latest: true
- name: Check commit titles

View File

@@ -5,7 +5,7 @@ fail_fast: false
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.0.1
rev: v4.3.0
hooks:
- id: trailing-whitespace
files: "erpnext.*"
@@ -15,6 +15,10 @@ repos:
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/PyCQA/flake8
rev: 5.0.4

View File

@@ -3,7 +3,7 @@ import inspect
import frappe
__version__ = "14.56.0"
__version__ = "14.65.0"
def get_default_company(user=None):

View File

@@ -58,6 +58,7 @@ class Account(NestedSet):
self.validate_balance_must_be_debit_or_credit()
self.validate_account_currency()
self.validate_root_company_and_sync_account_to_children()
self.validate_receivable_payable_account_type()
def validate_parent(self):
"""Fetch Parent Details and validate parent account"""
@@ -114,6 +115,24 @@ class Account(NestedSet):
"Balance Sheet" if self.root_type in ("Asset", "Liability", "Equity") else "Profit and Loss"
)
def validate_receivable_payable_account_type(self):
doc_before_save = self.get_doc_before_save()
receivable_payable_types = ["Receivable", "Payable"]
if (
doc_before_save
and doc_before_save.account_type in receivable_payable_types
and doc_before_save.account_type != self.account_type
):
# check for ledger entries
if frappe.db.get_all("GL Entry", filters={"account": self.name, "is_cancelled": 0}, limit=1):
msg = _(
"There are ledger entries against this account. Changing {0} to non-{1} in live system will cause incorrect output in 'Accounts {2}' report"
).format(
frappe.bold("Account Type"), doc_before_save.account_type, doc_before_save.account_type
)
frappe.msgprint(msg)
self.add_comment("Comment", msg)
def validate_root_details(self):
# does not exists parent
if frappe.db.exists("Account", self.name):

View File

@@ -53,8 +53,13 @@
},
"II. Forderungen und sonstige Vermögensgegenstände": {
"is_group": 1,
"Ford. a. Lieferungen und Leistungen": {
"Forderungen aus Lieferungen und Leistungen mit Kontokorrent": {
"account_number": "1400",
"account_type": "Receivable",
"is_group": 1
},
"Forderungen aus Lieferungen und Leistungen ohne Kontokorrent": {
"account_number": "1410",
"account_type": "Receivable"
},
"Durchlaufende Posten": {
@@ -180,8 +185,13 @@
},
"IV. Verbindlichkeiten aus Lieferungen und Leistungen": {
"is_group": 1,
"Verbindlichkeiten aus Lieferungen u. Leistungen": {
"Verbindlichkeiten aus Lieferungen und Leistungen mit Kontokorrent": {
"account_number": "1600",
"account_type": "Payable",
"is_group": 1
},
"Verbindlichkeiten aus Lieferungen und Leistungen ohne Kontokorrent": {
"account_number": "1610",
"account_type": "Payable"
}
},

View File

@@ -407,13 +407,10 @@
"Bewertungskorrektur zu Forderungen aus Lieferungen und Leistungen": {
"account_number": "9960"
},
"Debitoren": {
"is_group": 1,
"account_number": "10000"
},
"Forderungen aus Lieferungen und Leistungen": {
"Forderungen aus Lieferungen und Leistungen mit Kontokorrent": {
"account_number": "1200",
"account_type": "Receivable"
"account_type": "Receivable",
"is_group": 1
},
"Forderungen aus Lieferungen und Leistungen ohne Kontokorrent": {
"account_number": "1210"
@@ -1138,18 +1135,15 @@
"Bewertungskorrektur zu Verb. aus Lieferungen und Leistungen": {
"account_number": "9964"
},
"Kreditoren": {
"account_number": "70000",
"Verb. aus Lieferungen und Leistungen mit Kontokorrent": {
"account_number": "3300",
"account_type": "Payable",
"is_group": 1,
"Wareneingangs-­Verrechnungskonto" : {
"Wareneingangs-Verrechnungskonto" : {
"account_number": "70001",
"account_type": "Stock Received But Not Billed"
}
},
"Verb. aus Lieferungen und Leistungen": {
"account_number": "3300",
"account_type": "Payable"
},
"Verb. aus Lieferungen und Leistungen ohne Kontokorrent": {
"account_number": "3310"
},

View File

@@ -11,6 +11,7 @@
{
"fieldname": "company",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"in_list_view": 1,
"label": "Company",
"options": "Company",
@@ -19,7 +20,7 @@
],
"istable": 1,
"links": [],
"modified": "2020-05-01 12:32:34.044911",
"modified": "2024-01-03 11:13:02.669632",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Allowed To Transact With",
@@ -28,5 +29,6 @@
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}

View File

@@ -13,6 +13,7 @@
"account_type",
"account_subtype",
"column_break_7",
"disabled",
"is_default",
"is_company_account",
"company",
@@ -199,10 +200,16 @@
"fieldtype": "Data",
"in_global_search": 1,
"label": "Branch Code"
},
{
"default": "0",
"fieldname": "disabled",
"fieldtype": "Check",
"label": "Disabled"
}
],
"links": [],
"modified": "2022-05-04 15:49:42.620630",
"modified": "2024-02-02 17:50:09.768835",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Account",

View File

@@ -9,6 +9,7 @@ from frappe.contacts.address_and_contact import (
load_address_and_contact,
)
from frappe.model.document import Document
from frappe.utils import comma_and, get_link_to_form
class BankAccount(Document):
@@ -25,6 +26,19 @@ class BankAccount(Document):
def validate(self):
self.validate_company()
self.validate_iban()
self.validate_account()
def validate_account(self):
if self.account:
if accounts := frappe.db.get_all(
"Bank Account", filters={"account": self.account, "name": ["!=", self.name]}, as_list=1
):
frappe.throw(
_("'{0}' account is already used by {1}. Use another account.").format(
frappe.bold(self.account),
frappe.bold(comma_and([get_link_to_form(self.doctype, x[0]) for x in accounts])),
)
)
def validate_company(self):
if self.is_company_account and not self.company:

View File

@@ -137,7 +137,7 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
"erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_account_balance",
args: {
bank_account: frm.doc.bank_account,
till_date: frm.doc.bank_statement_from_date,
till_date: frappe.datetime.add_days(frm.doc.bank_statement_from_date, -1)
},
callback: (response) => {
frm.set_value("account_opening_balance", response.message);

View File

@@ -2,6 +2,7 @@
# For license information, please see license.txt
import frappe
from frappe.model.docstatus import DocStatus
from frappe.utils import flt
from erpnext.controllers.status_updater import StatusUpdater
@@ -40,9 +41,10 @@ class BankTransaction(StatusUpdater):
else:
allocated_amount = 0.0
amount = abs(flt(self.withdrawal) - flt(self.deposit))
self.db_set("allocated_amount", flt(allocated_amount))
self.db_set("unallocated_amount", amount - flt(allocated_amount))
unallocated_amount = abs(flt(self.withdrawal) - flt(self.deposit)) - allocated_amount
self.db_set("allocated_amount", flt(allocated_amount, self.precision("allocated_amount")))
self.db_set("unallocated_amount", flt(unallocated_amount, self.precision("unallocated_amount")))
self.reload()
self.set_status(update=True)
@@ -68,7 +70,7 @@ class BankTransaction(StatusUpdater):
"payment_entry": voucher["payment_name"],
"allocated_amount": 0.0, # Temporary
}
child = self.append("payment_entries", pe)
self.append("payment_entries", pe)
added = True
# runs on_update_after_submit
@@ -393,3 +395,21 @@ def unclear_reference_payment(doctype, docname, bt_name):
bt = frappe.get_doc("Bank Transaction", bt_name)
set_voucher_clearance(doctype, docname, None, bt)
return docname
def remove_from_bank_transaction(doctype, docname):
"""Remove a (cancelled) voucher from all Bank Transactions."""
for bt_name in get_reconciled_bank_transactions(doctype, docname):
bt = frappe.get_doc("Bank Transaction", bt_name)
if bt.docstatus == DocStatus.cancelled():
continue
modified = False
for pe in bt.payment_entries:
if pe.payment_document == doctype and pe.payment_entry == docname:
bt.remove(pe)
modified = True
if modified:
bt.save()

View File

@@ -2,10 +2,10 @@
# See license.txt
import json
import unittest
import frappe
from frappe import utils
from frappe.model.docstatus import DocStatus
from frappe.tests.utils import FrappeTestCase
from erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool import (
@@ -32,8 +32,16 @@ class TestBankTransaction(FrappeTestCase):
frappe.db.delete(dt)
make_pos_profile()
add_transactions()
add_vouchers()
# generate and use a uniq hash identifier for 'Bank Account' and it's linked GL 'Account' to avoid validation error
uniq_identifier = frappe.generate_hash(length=10)
gl_account = create_gl_account("_Test Bank " + uniq_identifier)
bank_account = create_bank_account(
gl_account=gl_account, bank_account_name="Checking Account " + uniq_identifier
)
add_transactions(bank_account=bank_account)
add_vouchers(gl_account=gl_account)
# This test checks if ERPNext is able to provide a linked payment for a bank transaction based on the amount of the bank transaction.
def test_linked_payments(self):
@@ -81,6 +89,29 @@ class TestBankTransaction(FrappeTestCase):
clearance_date = frappe.db.get_value("Payment Entry", payment.name, "clearance_date")
self.assertFalse(clearance_date)
def test_cancel_voucher(self):
bank_transaction = frappe.get_doc(
"Bank Transaction",
dict(description="1512567 BG/000003025 OPSKATTUZWXXX AT776000000098709849 Herr G"),
)
payment = frappe.get_doc("Payment Entry", dict(party="Mr G", paid_amount=1700))
vouchers = json.dumps(
[
{
"payment_doctype": "Payment Entry",
"payment_name": payment.name,
"amount": bank_transaction.unallocated_amount,
}
]
)
reconcile_vouchers(bank_transaction.name, vouchers)
payment.reload()
payment.cancel()
bank_transaction.reload()
self.assertEqual(bank_transaction.docstatus, DocStatus.submitted())
self.assertEqual(bank_transaction.unallocated_amount, 1700)
self.assertEqual(bank_transaction.payment_entries, [])
# Check if ERPNext can correctly filter a linked payments based on the debit/credit amount
def test_debit_credit_output(self):
bank_transaction = frappe.get_doc(
@@ -190,7 +221,9 @@ class TestBankTransaction(FrappeTestCase):
self.assertEqual(linked_payments[0][2], repayment_entry.name)
def create_bank_account(bank_name="Citi Bank", account_name="_Test Bank - _TC"):
def create_bank_account(
bank_name="Citi Bank", gl_account="_Test Bank - _TC", bank_account_name="Checking Account"
):
try:
frappe.get_doc(
{
@@ -202,21 +235,35 @@ def create_bank_account(bank_name="Citi Bank", account_name="_Test Bank - _TC"):
pass
try:
frappe.get_doc(
bank_account = frappe.get_doc(
{
"doctype": "Bank Account",
"account_name": "Checking Account",
"account_name": bank_account_name,
"bank": bank_name,
"account": account_name,
"account": gl_account,
}
).insert(ignore_if_duplicate=True)
except frappe.DuplicateEntryError:
pass
return bank_account.name
def add_transactions():
create_bank_account()
def create_gl_account(gl_account_name="_Test Bank - _TC"):
gl_account = frappe.get_doc(
{
"doctype": "Account",
"company": "_Test Company",
"parent_account": "Current Assets - _TC",
"account_type": "Bank",
"is_group": 0,
"account_name": gl_account_name,
}
).insert()
return gl_account.name
def add_transactions(bank_account="_Test Bank - _TC"):
doc = frappe.get_doc(
{
"doctype": "Bank Transaction",
@@ -224,7 +271,7 @@ def add_transactions():
"date": "2018-10-23",
"deposit": 1200,
"currency": "INR",
"bank_account": "Checking Account - Citi Bank",
"bank_account": bank_account,
}
).insert()
doc.submit()
@@ -236,7 +283,7 @@ def add_transactions():
"date": "2018-10-23",
"deposit": 1700,
"currency": "INR",
"bank_account": "Checking Account - Citi Bank",
"bank_account": bank_account,
}
).insert()
doc.submit()
@@ -248,7 +295,7 @@ def add_transactions():
"date": "2018-10-26",
"withdrawal": 690,
"currency": "INR",
"bank_account": "Checking Account - Citi Bank",
"bank_account": bank_account,
}
).insert()
doc.submit()
@@ -260,7 +307,7 @@ def add_transactions():
"date": "2018-10-27",
"deposit": 3900,
"currency": "INR",
"bank_account": "Checking Account - Citi Bank",
"bank_account": bank_account,
}
).insert()
doc.submit()
@@ -272,13 +319,13 @@ def add_transactions():
"date": "2018-10-27",
"withdrawal": 109080,
"currency": "INR",
"bank_account": "Checking Account - Citi Bank",
"bank_account": bank_account,
}
).insert()
doc.submit()
def add_vouchers():
def add_vouchers(gl_account="_Test Bank - _TC"):
try:
frappe.get_doc(
{
@@ -294,7 +341,7 @@ def add_vouchers():
pi = make_purchase_invoice(supplier="Conrad Electronic", qty=1, rate=690)
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account=gl_account)
pe.reference_no = "Conrad Oct 18"
pe.reference_date = "2018-10-24"
pe.insert()
@@ -313,14 +360,14 @@ def add_vouchers():
pass
pi = make_purchase_invoice(supplier="Mr G", qty=1, rate=1200)
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account=gl_account)
pe.reference_no = "Herr G Oct 18"
pe.reference_date = "2018-10-24"
pe.insert()
pe.submit()
pi = make_purchase_invoice(supplier="Mr G", qty=1, rate=1700)
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account=gl_account)
pe.reference_no = "Herr G Nov 18"
pe.reference_date = "2018-11-01"
pe.insert()
@@ -351,10 +398,10 @@ def add_vouchers():
pass
pi = make_purchase_invoice(supplier="Poore Simon's", qty=1, rate=3900, is_paid=1, do_not_save=1)
pi.cash_bank_account = "_Test Bank - _TC"
pi.cash_bank_account = gl_account
pi.insert()
pi.submit()
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account=gl_account)
pe.reference_no = "Poore Simon's Oct 18"
pe.reference_date = "2018-10-28"
pe.paid_amount = 690
@@ -363,7 +410,7 @@ def add_vouchers():
pe.submit()
si = create_sales_invoice(customer="Poore Simon's", qty=1, rate=3900)
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC")
pe = get_payment_entry("Sales Invoice", si.name, bank_account=gl_account)
pe.reference_no = "Poore Simon's Oct 18"
pe.reference_date = "2018-10-28"
pe.insert()
@@ -386,16 +433,12 @@ def add_vouchers():
if not frappe.db.get_value(
"Mode of Payment Account", {"company": "_Test Company", "parent": "Cash"}
):
mode_of_payment.append(
"accounts", {"company": "_Test Company", "default_account": "_Test Bank - _TC"}
)
mode_of_payment.append("accounts", {"company": "_Test Company", "default_account": gl_account})
mode_of_payment.save()
si = create_sales_invoice(customer="Fayva", qty=1, rate=109080, do_not_save=1)
si.is_pos = 1
si.append(
"payments", {"mode_of_payment": "Cash", "account": "_Test Bank - _TC", "amount": 109080}
)
si.append("payments", {"mode_of_payment": "Cash", "account": gl_account, "amount": 109080})
si.insert()
si.submit()

View File

@@ -1,457 +1,152 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"actions": [],
"autoname": "naming_series:",
"beta": 0,
"creation": "2018-06-18 16:51:49.994750",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"naming_series",
"user",
"date",
"from_time",
"time",
"expense",
"custody",
"returns",
"outstanding_amount",
"payments",
"net_amount",
"amended_from"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "POS-CLO-",
"fieldname": "naming_series",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 1,
"in_global_search": 1,
"in_list_view": 0,
"in_standard_filter": 1,
"label": "Series",
"length": 0,
"no_copy": 0,
"options": "POS-CLO-",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "user",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 1,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "User",
"length": 0,
"no_copy": 0,
"options": "User",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "Today",
"fieldname": "date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 1,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 1,
"label": "Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "from_time",
"fieldtype": "Time",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 1,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 1,
"label": "From Time",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "",
"fieldname": "time",
"fieldtype": "Time",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 1,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 1,
"label": "To Time",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "0.00",
"fieldname": "expense",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 1,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Expense",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"label": "Expense"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "0.00",
"fieldname": "custody",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 1,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Custody",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"label": "Custody"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "0.00",
"fieldname": "returns",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 1,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Returns",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "2",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
"precision": "2"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "0.00",
"fieldname": "outstanding_amount",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Outstanding Amount",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "0.0",
"fieldname": "payments",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 1,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Payments",
"length": 0,
"no_copy": 0,
"options": "Cashier Closing Payments",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"options": "Cashier Closing Payments"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "net_amount",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 1,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Net Amount",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "amended_from",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Amended From",
"length": 0,
"no_copy": 1,
"options": "Cashier Closing",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"read_only": 1
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2019-02-19 08:35:24.157327",
"links": [],
"modified": "2023-12-28 13:15:46.858427",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Cashier Closing",
"name_case": "",
"naming_rule": "By \"Naming Series\" field",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 1,
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
}
"states": [],
"track_changes": 1
}

View File

@@ -3,16 +3,21 @@
frappe.ui.form.on('Cost Center Allocation', {
setup: function(frm) {
let filters = {"is_group": 0};
if (frm.doc.company) {
$.extend(filters, {
"company": frm.doc.company
});
}
frm.set_query('main_cost_center', function() {
return {
filters: filters
filters: {
company: frm.doc.company,
is_group: 0
}
};
});
frm.set_query('cost_center', 'allocation_percentages', function() {
return {
filters: {
company: frm.doc.company,
is_group: 0
}
};
});
}

View File

@@ -41,7 +41,7 @@ def test_record_generator():
]
start = 2012
end = now_datetime().year + 5
end = now_datetime().year + 25
for year in range(start, end):
test_records.append(
{

View File

@@ -138,8 +138,7 @@
"label": "Against Voucher Type",
"oldfieldname": "against_voucher_type",
"oldfieldtype": "Data",
"options": "DocType",
"search_index": 1
"options": "DocType"
},
{
"fieldname": "against_voucher",
@@ -158,8 +157,7 @@
"label": "Voucher Type",
"oldfieldname": "voucher_type",
"oldfieldtype": "Select",
"options": "DocType",
"search_index": 1
"options": "DocType"
},
{
"fieldname": "voucher_no",
@@ -291,4 +289,4 @@
"search_fields": "voucher_no,account,posting_date,against_voucher",
"sort_field": "modified",
"sort_order": "DESC"
}
}

View File

@@ -13,16 +13,9 @@ import erpnext
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_checks_for_pl_and_bs_accounts,
)
from erpnext.accounts.doctype.accounting_dimension_filter.accounting_dimension_filter import (
get_dimension_filter_map,
)
from erpnext.accounts.party import validate_party_frozen_disabled, validate_party_gle_currency
from erpnext.accounts.utils import get_account_currency, get_fiscal_year
from erpnext.exceptions import (
InvalidAccountCurrency,
InvalidAccountDimensionError,
MandatoryAccountDimensionError,
)
from erpnext.exceptions import InvalidAccountCurrency
exclude_from_linked_with = True
@@ -54,7 +47,6 @@ class GLEntry(Document):
if not self.flags.from_repost and self.voucher_type != "Period Closing Voucher":
self.validate_account_details(adv_adj)
self.validate_dimensions_for_pl_and_bs()
self.validate_allowed_dimensions()
validate_balance_type(self.account, adv_adj)
validate_frozen_account(self.account, adv_adj)
@@ -164,42 +156,6 @@ class GLEntry(Document):
)
)
def validate_allowed_dimensions(self):
dimension_filter_map = get_dimension_filter_map()
for key, value in dimension_filter_map.items():
dimension = key[0]
account = key[1]
if self.account == account:
if value["is_mandatory"] and not self.get(dimension):
frappe.throw(
_("{0} is mandatory for account {1}").format(
frappe.bold(frappe.unscrub(dimension)), frappe.bold(self.account)
),
MandatoryAccountDimensionError,
)
if value["allow_or_restrict"] == "Allow":
if self.get(dimension) and self.get(dimension) not in value["allowed_dimensions"]:
frappe.throw(
_("Invalid value {0} for {1} against account {2}").format(
frappe.bold(self.get(dimension)),
frappe.bold(frappe.unscrub(dimension)),
frappe.bold(self.account),
),
InvalidAccountDimensionError,
)
else:
if self.get(dimension) and self.get(dimension) in value["allowed_dimensions"]:
frappe.throw(
_("Invalid value {0} for {1} against account {2}").format(
frappe.bold(self.get(dimension)),
frappe.bold(frappe.unscrub(dimension)),
frappe.bold(self.account),
),
InvalidAccountDimensionError,
)
def check_pl_account(self):
if (
self.is_opening == "Yes"

View File

@@ -8,7 +8,7 @@ frappe.provide("erpnext.journal_entry");
frappe.ui.form.on("Journal Entry", {
setup: function(frm) {
frm.add_fetch("bank_account", "account", "account");
frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', 'Journal Entry', "Repost Payment Ledger", 'Asset', 'Asset Movement', "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries"];
frm.ignore_doctypes_on_cancel_all = ["Sales Invoice", "Purchase Invoice", "Journal Entry", "Repost Payment Ledger", "Asset", "Asset Movement", "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries", "Bank Transaction"];
},
refresh: function(frm) {
@@ -184,7 +184,6 @@ var update_jv_details = function(doc, r) {
$.each(r, function(i, d) {
var row = frappe.model.add_child(doc, "Journal Entry Account", "accounts");
frappe.model.set_value(row.doctype, row.name, "account", d.account)
frappe.model.set_value(row.doctype, row.name, "balance", d.balance)
});
refresh_field("accounts");
}
@@ -193,7 +192,6 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro
onload() {
this.load_defaults();
this.setup_queries();
this.setup_balance_formatter();
erpnext.accounts.dimensions.setup_dimension_filters(this.frm, this.frm.doctype);
}
@@ -292,19 +290,6 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro
}
setup_balance_formatter() {
const formatter = function(value, df, options, doc) {
var currency = frappe.meta.get_field_currency(df, doc);
var dr_or_cr = value ? ('<label>' + (value > 0.0 ? __("Dr") : __("Cr")) + '</label>') : "";
return "<div style='text-align: right'>"
+ ((value==null || value==="") ? "" : format_currency(Math.abs(value), currency))
+ " " + dr_or_cr
+ "</div>";
};
this.frm.fields_dict.accounts.grid.update_docfield_property('balance', 'formatter', formatter);
this.frm.fields_dict.accounts.grid.update_docfield_property('party_balance', 'formatter', formatter);
}
reference_name(doc, cdt, cdn) {
var d = frappe.get_doc(cdt, cdn);
@@ -400,23 +385,22 @@ frappe.ui.form.on("Journal Entry Account", {
if(!d.account && d.party_type && d.party) {
if(!frm.doc.company) frappe.throw(__("Please select Company"));
return frm.call({
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_party_account_and_balance",
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_party_account_and_currency",
child: d,
args: {
company: frm.doc.company,
party_type: d.party_type,
party: d.party,
cost_center: d.cost_center
}
});
}
},
cost_center: function(frm, dt, dn) {
erpnext.journal_entry.set_account_balance(frm, dt, dn);
erpnext.journal_entry.set_account_details(frm, dt, dn);
},
account: function(frm, dt, dn) {
erpnext.journal_entry.set_account_balance(frm, dt, dn);
erpnext.journal_entry.set_account_details(frm, dt, dn);
},
debit_in_account_currency: function(frm, cdt, cdn) {
@@ -600,14 +584,14 @@ $.extend(erpnext.journal_entry, {
});
$.extend(erpnext.journal_entry, {
set_account_balance: function(frm, dt, dn) {
set_account_details: function(frm, dt, dn) {
var d = locals[dt][dn];
if(d.account) {
if(!frm.doc.company) frappe.throw(__("Please select Company first"));
if(!frm.doc.posting_date) frappe.throw(__("Please select Posting Date first"));
return frappe.call({
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_account_balance_and_party_type",
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_account_details_and_party_type",
args: {
account: d.account,
date: frm.doc.posting_date,
@@ -615,7 +599,6 @@ $.extend(erpnext.journal_entry, {
debit: flt(d.debit_in_account_currency),
credit: flt(d.credit_in_account_currency),
exchange_rate: d.exchange_rate,
cost_center: d.cost_center
},
callback: function(r) {
if(r.message) {

View File

@@ -68,7 +68,6 @@ class JournalEntry(AccountsController):
self.set_print_format_fields()
self.validate_credit_debit_note()
self.validate_empty_accounts_table()
self.set_account_and_party_balance()
self.validate_inter_company_accounts()
self.validate_depr_entry_voucher_type()
@@ -78,6 +77,20 @@ class JournalEntry(AccountsController):
if not self.title:
self.title = self.get_title()
def submit(self):
if len(self.accounts) > 100:
msgprint(_("The task has been enqueued as a background job."), alert=True)
self.queue_action("submit", timeout=4600)
else:
return self._submit()
def cancel(self):
if len(self.accounts) > 100:
msgprint(_("The task has been enqueued as a background job."), alert=True)
self.queue_action("cancel", timeout=4600)
else:
return self._cancel()
def on_submit(self):
self.validate_cheque_info()
self.check_credit_limit()
@@ -461,17 +474,28 @@ class JournalEntry(AccountsController):
elif d.party_type == "Supplier" and flt(d.credit) > 0:
frappe.throw(_("Row {0}: Advance against Supplier must be debit").format(d.idx))
def system_generated_gain_loss(self):
return (
self.voucher_type == "Exchange Gain Or Loss"
and self.multi_currency
and self.is_system_generated
)
def validate_against_jv(self):
for d in self.get("accounts"):
if d.reference_type == "Journal Entry":
account_root_type = frappe.db.get_value("Account", d.account, "root_type")
if account_root_type == "Asset" and flt(d.debit) > 0:
account_root_type = frappe.get_cached_value("Account", d.account, "root_type")
if account_root_type == "Asset" and flt(d.debit) > 0 and not self.system_generated_gain_loss():
frappe.throw(
_(
"Row #{0}: For {1}, you can select reference document only if account gets credited"
).format(d.idx, d.account)
)
elif account_root_type == "Liability" and flt(d.credit) > 0:
elif (
account_root_type == "Liability"
and flt(d.credit) > 0
and not self.system_generated_gain_loss()
):
frappe.throw(
_(
"Row #{0}: For {1}, you can select reference document only if account gets debited"
@@ -503,7 +527,7 @@ class JournalEntry(AccountsController):
for jvd in against_entries:
if flt(jvd[dr_or_cr]) > 0:
valid = True
if not valid:
if not valid and not self.system_generated_gain_loss():
frappe.throw(
_("Against Journal Entry {0} does not have any unmatched {1} entry").format(
d.reference_name, dr_or_cr
@@ -1051,21 +1075,6 @@ class JournalEntry(AccountsController):
if not self.get("accounts"):
frappe.throw(_("Accounts table cannot be blank."))
def set_account_and_party_balance(self):
account_balance = {}
party_balance = {}
for d in self.get("accounts"):
if d.account not in account_balance:
account_balance[d.account] = get_balance_on(account=d.account, date=self.posting_date)
if (d.party_type, d.party) not in party_balance:
party_balance[(d.party_type, d.party)] = get_balance_on(
party_type=d.party_type, party=d.party, date=self.posting_date, company=self.company
)
d.account_balance = account_balance[d.account]
d.party_balance = party_balance[(d.party_type, d.party)]
@frappe.whitelist()
def get_default_bank_cash_account(company, account_type=None, mode_of_payment=None, account=None):
@@ -1231,8 +1240,6 @@ def get_payment_entry(ref_doc, args):
"account_type": frappe.db.get_value("Account", args.get("party_account"), "account_type"),
"account_currency": args.get("party_account_currency")
or get_account_currency(args.get("party_account")),
"balance": get_balance_on(args.get("party_account")),
"party_balance": get_balance_on(party=args.get("party"), party_type=args.get("party_type")),
"exchange_rate": exchange_rate,
args.get("amount_field_party"): args.get("amount"),
"is_advance": args.get("is_advance"),
@@ -1380,30 +1387,23 @@ def get_outstanding(args):
@frappe.whitelist()
def get_party_account_and_balance(company, party_type, party, cost_center=None):
def get_party_account_and_currency(company, party_type, party):
if not frappe.has_permission("Account"):
frappe.msgprint(_("No Permission"), raise_exception=1)
account = get_party_account(party_type, party, company)
account_balance = get_balance_on(account=account, cost_center=cost_center)
party_balance = get_balance_on(
party_type=party_type, party=party, company=company, cost_center=cost_center
)
return {
"account": account,
"balance": account_balance,
"party_balance": party_balance,
"account_currency": frappe.db.get_value("Account", account, "account_currency"),
"account_currency": frappe.get_cached_value("Account", account, "account_currency"),
}
@frappe.whitelist()
def get_account_balance_and_party_type(
account, date, company, debit=None, credit=None, exchange_rate=None, cost_center=None
def get_account_details_and_party_type(
account, date, company, debit=None, credit=None, exchange_rate=None
):
"""Returns dict of account balance and party type to be set in Journal Entry on selection of account."""
"""Returns dict of account details and party type to be set in Journal Entry on selection of account."""
if not frappe.has_permission("Account"):
frappe.msgprint(_("No Permission"), raise_exception=1)
@@ -1423,7 +1423,6 @@ def get_account_balance_and_party_type(
party_type = ""
grid_values = {
"balance": get_balance_on(account, date, cost_center=cost_center),
"party_type": party_type,
"account_type": account_details.account_type,
"account_currency": account_details.account_currency or company_currency,

View File

@@ -81,7 +81,6 @@
},
{
"account": "Sales - _TC",
"cost_center": "_Test Cost Center - _TC",
"credit_in_account_currency": 400.0,
"debit_in_account_currency": 0.0,
"doctype": "Journal Entry Account",

View File

@@ -9,12 +9,10 @@
"field_order": [
"account",
"account_type",
"balance",
"col_break1",
"bank_account",
"party_type",
"party",
"party_balance",
"accounting_dimensions_section",
"cost_center",
"dimension_col_break",
@@ -63,17 +61,6 @@
"label": "Account Type",
"print_hide": 1
},
{
"fieldname": "balance",
"fieldtype": "Currency",
"label": "Account Balance",
"no_copy": 1,
"oldfieldname": "balance",
"oldfieldtype": "Data",
"options": "account_currency",
"print_hide": 1,
"read_only": 1
},
{
"default": ":Company",
"description": "If Income or Expense",
@@ -107,14 +94,6 @@
"label": "Party",
"options": "party_type"
},
{
"fieldname": "party_balance",
"fieldtype": "Currency",
"label": "Party Balance",
"options": "account_currency",
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "currency_section",
"fieldtype": "Section Break",

View File

@@ -7,7 +7,7 @@ cur_frm.cscript.tax_table = "Advance Taxes and Charges";
frappe.ui.form.on('Payment Entry', {
onload: function(frm) {
frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', 'Journal Entry', 'Repost Payment Ledger','Repost Accounting Ledger', 'Unreconcile Payment', 'Unreconcile Payment Entries'];
frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', 'Journal Entry', 'Repost Payment Ledger', 'Repost Accounting Ledger', 'Unreconcile Payment', 'Unreconcile Payment Entries', 'Bank Transaction'];
if(frm.doc.__islocal) {
if (!frm.doc.paid_from) frm.set_value("paid_from_account_currency", null);
@@ -631,7 +631,7 @@ frappe.ui.form.on('Payment Entry', {
get_outstanding_invoices_or_orders: function(frm, get_outstanding_invoices, get_orders_to_be_billed) {
const today = frappe.datetime.get_today();
const fields = [
let fields = [
{fieldtype:"Section Break", label: __("Posting Date")},
{fieldtype:"Date", label: __("From Date"),
fieldname:"from_posting_date", default:frappe.datetime.add_days(today, -30)},
@@ -646,18 +646,29 @@ frappe.ui.form.on('Payment Entry', {
fieldname:"outstanding_amt_greater_than", default: 0},
{fieldtype:"Column Break"},
{fieldtype:"Float", label: __("Less Than Amount"), fieldname:"outstanding_amt_less_than"},
{fieldtype:"Section Break"},
{fieldtype:"Link", label:__("Cost Center"), fieldname:"cost_center", options:"Cost Center",
"get_query": function() {
return {
"filters": {"company": frm.doc.company}
}
];
if (frm.dimension_filters) {
let column_break_insertion_point = Math.ceil((frm.dimension_filters.length)/2);
fields.push({fieldtype:"Section Break"});
frm.dimension_filters.map((elem, idx)=>{
fields.push({
fieldtype: "Link",
label: elem.document_type == "Cost Center" ? "Cost Center" : elem.label,
options: elem.document_type,
fieldname: elem.fieldname || elem.document_type
});
if(idx+1 == column_break_insertion_point) {
fields.push({fieldtype:"Column Break"});
}
},
{fieldtype:"Column Break"},
});
}
fields = fields.concat([
{fieldtype:"Section Break"},
{fieldtype:"Check", label: __("Allocate Payment Amount"), fieldname:"allocate_payment_amount", default:1},
];
]);
let btn_text = "";
@@ -919,7 +930,7 @@ frappe.ui.form.on('Payment Entry', {
if(frm.doc.payment_type == "Receive"
&& frm.doc.base_total_allocated_amount < frm.doc.base_received_amount + total_deductions
&& frm.doc.total_allocated_amount < frm.doc.paid_amount + (total_deductions / frm.doc.source_exchange_rate)) {
unallocated_amount = (frm.doc.base_received_amount + total_deductions + flt(frm.doc.base_total_taxes_and_charges)
unallocated_amount = (frm.doc.base_received_amount + total_deductions - flt(frm.doc.base_total_taxes_and_charges)
- frm.doc.base_total_allocated_amount) / frm.doc.source_exchange_rate;
} else if (frm.doc.payment_type == "Pay"
&& frm.doc.base_total_allocated_amount < frm.doc.base_paid_amount - total_deductions

View File

@@ -13,6 +13,7 @@ from pypika import Case
from pypika.functions import Coalesce, Sum
import erpnext
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_dimensions
from erpnext.accounts.doctype.bank_account.bank_account import (
get_bank_account_details,
get_party_bank_account,
@@ -334,7 +335,10 @@ class PaymentEntry(AccountsController):
)
def set_missing_ref_details(
self, force: bool = False, update_ref_details_only_for: list | None = None
self,
force: bool = False,
update_ref_details_only_for: list | None = None,
ref_exchange_rate: float | None = None,
) -> None:
for d in self.get("references"):
if d.allocated_amount:
@@ -346,6 +350,8 @@ class PaymentEntry(AccountsController):
ref_details = get_reference_details(
d.reference_doctype, d.reference_name, self.party_account_currency
)
if ref_exchange_rate:
ref_details.update({"exchange_rate": ref_exchange_rate})
for field, value in ref_details.items():
if d.exchange_gain_loss:
@@ -873,19 +879,19 @@ class PaymentEntry(AccountsController):
)
base_party_amount = flt(self.base_total_allocated_amount) + flt(base_unallocated_amount)
if self.payment_type == "Receive":
self.difference_amount = base_party_amount - self.base_received_amount
elif self.payment_type == "Pay":
self.difference_amount = self.base_paid_amount - base_party_amount
else:
self.difference_amount = self.base_paid_amount - flt(self.base_received_amount)
total_deductions = sum(flt(d.amount) for d in self.get("deductions"))
included_taxes = self.get_included_taxes()
if self.payment_type == "Receive":
self.difference_amount = base_party_amount - self.base_received_amount + included_taxes
elif self.payment_type == "Pay":
self.difference_amount = self.base_paid_amount - base_party_amount - included_taxes
else:
self.difference_amount = self.base_paid_amount - flt(self.base_received_amount) - included_taxes
total_deductions = sum(flt(d.amount) for d in self.get("deductions"))
self.difference_amount = flt(
self.difference_amount - total_deductions - included_taxes, self.precision("difference_amount")
self.difference_amount - total_deductions, self.precision("difference_amount")
)
def get_included_taxes(self):
@@ -1453,6 +1459,13 @@ def get_outstanding_reference_documents(args):
condition += " and cost_center='%s'" % args.get("cost_center")
accounting_dimensions_filter.append(ple.cost_center == args.get("cost_center"))
# dynamic dimension filters
active_dimensions = get_dimensions()[0]
for dim in active_dimensions:
if args.get(dim.fieldname):
condition += " and {0}='{1}'".format(dim.fieldname, args.get(dim.fieldname))
accounting_dimensions_filter.append(ple[dim.fieldname] == args.get(dim.fieldname))
date_fields_dict = {
"posting_date": ["from_posting_date", "to_posting_date"],
"due_date": ["from_due_date", "to_due_date"],
@@ -1680,6 +1693,12 @@ def get_orders_to_be_billed(
if doc and hasattr(doc, "cost_center") and doc.cost_center:
condition = " and cost_center='%s'" % cost_center
# dynamic dimension filters
active_dimensions = get_dimensions()[0]
for dim in active_dimensions:
if filters.get(dim.fieldname):
condition += " and {0}='{1}'".format(dim.fieldname, filters.get(dim.fieldname))
if party_account_currency == company_currency:
grand_total_field = "base_grand_total"
rounded_total_field = "base_rounded_total"

View File

@@ -705,7 +705,50 @@ class TestPaymentEntry(FrappeTestCase):
pe2.submit()
# create return entry against si1
create_sales_invoice(is_return=1, return_against=si1.name, qty=-1)
cr_note = create_sales_invoice(is_return=1, return_against=si1.name, qty=-1)
si1_outstanding = frappe.db.get_value("Sales Invoice", si1.name, "outstanding_amount")
# create JE(credit note) manually against si1 and cr_note
je = frappe.get_doc(
{
"doctype": "Journal Entry",
"company": si1.company,
"voucher_type": "Credit Note",
"posting_date": nowdate(),
}
)
je.append(
"accounts",
{
"account": si1.debit_to,
"party_type": "Customer",
"party": si1.customer,
"debit": 0,
"credit": 100,
"debit_in_account_currency": 0,
"credit_in_account_currency": 100,
"reference_type": si1.doctype,
"reference_name": si1.name,
"cost_center": si1.items[0].cost_center,
},
)
je.append(
"accounts",
{
"account": cr_note.debit_to,
"party_type": "Customer",
"party": cr_note.customer,
"debit": 100,
"credit": 0,
"debit_in_account_currency": 100,
"credit_in_account_currency": 0,
"reference_type": cr_note.doctype,
"reference_name": cr_note.name,
"cost_center": cr_note.items[0].cost_center,
},
)
je.save().submit()
si1_outstanding = frappe.db.get_value("Sales Invoice", si1.name, "outstanding_amount")
self.assertEqual(si1_outstanding, -100)

View File

@@ -294,7 +294,7 @@ class TestPaymentLedgerEntry(FrappeTestCase):
cr_note1.return_against = si3.name
cr_note1 = cr_note1.save().submit()
pl_entries = (
pl_entries_si3 = (
qb.from_(ple)
.select(
ple.voucher_type,
@@ -309,7 +309,24 @@ class TestPaymentLedgerEntry(FrappeTestCase):
.run(as_dict=True)
)
expected_values = [
pl_entries_cr_note1 = (
qb.from_(ple)
.select(
ple.voucher_type,
ple.voucher_no,
ple.against_voucher_type,
ple.against_voucher_no,
ple.amount,
ple.delinked,
)
.where(
(ple.against_voucher_type == cr_note1.doctype) & (ple.against_voucher_no == cr_note1.name)
)
.orderby(ple.creation)
.run(as_dict=True)
)
expected_values_for_si3 = [
{
"voucher_type": si3.doctype,
"voucher_no": si3.name,
@@ -317,18 +334,21 @@ class TestPaymentLedgerEntry(FrappeTestCase):
"against_voucher_no": si3.name,
"amount": amount,
"delinked": 0,
},
}
]
# credit/debit notes post ledger entries against itself
expected_values_for_cr_note1 = [
{
"voucher_type": cr_note1.doctype,
"voucher_no": cr_note1.name,
"against_voucher_type": si3.doctype,
"against_voucher_no": si3.name,
"against_voucher_type": cr_note1.doctype,
"against_voucher_no": cr_note1.name,
"amount": -amount,
"delinked": 0,
},
]
self.assertEqual(pl_entries[0], expected_values[0])
self.assertEqual(pl_entries[1], expected_values[1])
self.assertEqual(pl_entries_si3, expected_values_for_si3)
self.assertEqual(pl_entries_cr_note1, expected_values_for_cr_note1)
def test_je_against_inv_and_note(self):
ple = self.ple

View File

@@ -4,9 +4,13 @@
import unittest
import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.utils import getdate
from erpnext.accounts.doctype.bank_transaction.test_bank_transaction import create_bank_account
from erpnext.accounts.doctype.bank_transaction.test_bank_transaction import (
create_bank_account,
create_gl_account,
)
from erpnext.accounts.doctype.payment_entry.payment_entry import (
get_payment_entry,
make_payment_order,
@@ -14,28 +18,32 @@ from erpnext.accounts.doctype.payment_entry.payment_entry import (
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
class TestPaymentOrder(unittest.TestCase):
class TestPaymentOrder(FrappeTestCase):
def setUp(self):
create_bank_account()
# generate and use a uniq hash identifier for 'Bank Account' and it's linked GL 'Account' to avoid validation error
uniq_identifier = frappe.generate_hash(length=10)
self.gl_account = create_gl_account("_Test Bank " + uniq_identifier)
self.bank_account = create_bank_account(
gl_account=self.gl_account, bank_account_name="Checking Account " + uniq_identifier
)
def tearDown(self):
for bt in frappe.get_all("Payment Order"):
doc = frappe.get_doc("Payment Order", bt.name)
doc.cancel()
doc.delete()
frappe.db.rollback()
def test_payment_order_creation_against_payment_entry(self):
purchase_invoice = make_purchase_invoice()
payment_entry = get_payment_entry(
"Purchase Invoice", purchase_invoice.name, bank_account="_Test Bank - _TC"
"Purchase Invoice", purchase_invoice.name, bank_account=self.gl_account
)
payment_entry.reference_no = "_Test_Payment_Order"
payment_entry.reference_date = getdate()
payment_entry.party_bank_account = "Checking Account - Citi Bank"
payment_entry.party_bank_account = self.bank_account
payment_entry.insert()
payment_entry.submit()
doc = create_payment_order_against_payment_entry(payment_entry, "Payment Entry")
doc = create_payment_order_against_payment_entry(
payment_entry, "Payment Entry", self.bank_account
)
reference_doc = doc.get("references")[0]
self.assertEqual(reference_doc.reference_name, payment_entry.name)
self.assertEqual(reference_doc.reference_doctype, "Payment Entry")
@@ -43,13 +51,13 @@ class TestPaymentOrder(unittest.TestCase):
self.assertEqual(reference_doc.amount, 250)
def create_payment_order_against_payment_entry(ref_doc, order_type):
def create_payment_order_against_payment_entry(ref_doc, order_type, bank_account):
payment_order = frappe.get_doc(
dict(
doctype="Payment Order",
company="_Test Company",
payment_order_type=order_type,
company_bank_account="Checking Account - Citi Bank",
company_bank_account=bank_account,
)
)
doc = make_payment_order(ref_doc.name, payment_order)

View File

@@ -83,6 +83,8 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo
this.frm.change_custom_button_type('Allocate', null, 'default');
}
this.frm.trigger("set_query_for_dimension_filters");
// check for any running reconciliation jobs
if (this.frm.doc.receivable_payable_account) {
this.frm.call({
@@ -113,6 +115,25 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo
}
}
set_query_for_dimension_filters() {
frappe.call({
method: "erpnext.accounts.doctype.payment_reconciliation.payment_reconciliation.get_queries_for_dimension_filters",
args: {
company: this.frm.doc.company,
},
callback: (r) => {
if (!r.exc && r.message) {
r.message.forEach(x => {
this.frm.set_query(x.fieldname, () => {
return {
'filters': x.filters
};
});
});
}
}
});
}
company() {
this.frm.set_value('party', '');

View File

@@ -24,7 +24,9 @@
"invoice_limit",
"payment_limit",
"bank_cash_account",
"accounting_dimensions_section",
"cost_center",
"dimension_col_break",
"sec_break1",
"invoice_name",
"invoices",
@@ -199,6 +201,18 @@
"fieldname": "payment_name",
"fieldtype": "Data",
"label": "Filter on Payment"
},
{
"collapsible": 1,
"collapsible_depends_on": "eval: doc.invoices.length == 0",
"depends_on": "eval:doc.receivable_payable_account",
"fieldname": "accounting_dimensions_section",
"fieldtype": "Section Break",
"label": "Accounting Dimensions Filter"
},
{
"fieldname": "dimension_col_break",
"fieldtype": "Column Break"
}
],
"hide_toolbar": 1,
@@ -206,7 +220,7 @@
"is_virtual": 1,
"issingle": 1,
"links": [],
"modified": "2023-11-17 17:33:55.701726",
"modified": "2023-12-14 13:38:16.264013",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Reconciliation",

View File

@@ -10,6 +10,7 @@ from frappe.query_builder.custom import ConstantColumn
from frappe.utils import flt, fmt_money, get_link_to_form, getdate, nowdate, today
import erpnext
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_dimensions
from erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation import (
is_any_doc_running,
)
@@ -28,6 +29,7 @@ class PaymentReconciliation(Document):
self.common_filter_conditions = []
self.accounting_dimension_filter_conditions = []
self.ple_posting_date_filter = []
self.dimensions = get_dimensions()[0]
def load_from_db(self):
# 'modified' attribute is required for `run_doc_method` to work properly.
@@ -110,7 +112,7 @@ class PaymentReconciliation(Document):
def get_payment_entries(self):
order_doctype = "Sales Order" if self.party_type == "Customer" else "Purchase Order"
condition = self.get_conditions(get_payments=True)
condition = self.get_payment_entry_conditions()
payment_entries = get_advance_payment_entries_for_regional(
self.party_type,
@@ -126,66 +128,67 @@ class PaymentReconciliation(Document):
return payment_entries
def get_jv_entries(self):
condition = self.get_conditions()
je = qb.DocType("Journal Entry")
jea = qb.DocType("Journal Entry Account")
conditions = self.get_journal_filter_conditions()
# Dimension filters
for x in self.dimensions:
dimension = x.fieldname
if self.get(dimension):
conditions.append(jea[dimension] == self.get(dimension))
if self.payment_name:
condition += f" and t1.name like '%%{self.payment_name}%%'"
conditions.append(je.name.like(f"%%{self.payment_name}%%"))
if self.get("cost_center"):
condition += f" and t2.cost_center = '{self.cost_center}' "
conditions.append(jea.cost_center == self.cost_center)
dr_or_cr = (
"credit_in_account_currency"
if erpnext.get_party_account_type(self.party_type) == "Receivable"
else "debit_in_account_currency"
)
conditions.append(jea[dr_or_cr].gt(0))
bank_account_condition = (
"t2.against_account like %(bank_cash_account)s" if self.bank_cash_account else "1=1"
if self.bank_cash_account:
conditions.append(jea.against_account.like(f"%%{self.bank_cash_account}%%"))
journal_query = (
qb.from_(je)
.inner_join(jea)
.on(jea.parent == je.name)
.select(
ConstantColumn("Journal Entry").as_("reference_type"),
je.name.as_("reference_name"),
je.posting_date,
je.remark.as_("remarks"),
jea.name.as_("reference_row"),
jea[dr_or_cr].as_("amount"),
jea.is_advance,
jea.exchange_rate,
jea.account_currency.as_("currency"),
jea.cost_center.as_("cost_center"),
)
.where(
(je.docstatus == 1)
& (jea.party_type == self.party_type)
& (jea.party == self.party)
& (jea.account == self.receivable_payable_account)
& (
(jea.reference_type == "")
| (jea.reference_type.isnull())
| (jea.reference_type.isin(("Sales Order", "Purchase Order")))
)
)
.where(Criterion.all(conditions))
.orderby(je.posting_date)
)
limit = f"limit {self.payment_limit}" if self.payment_limit else " "
if self.payment_limit:
journal_query = journal_query.limit(self.payment_limit)
# nosemgrep
journal_entries = frappe.db.sql(
"""
select
"Journal Entry" as reference_type, t1.name as reference_name,
t1.posting_date, t1.remark as remarks, t2.name as reference_row,
{dr_or_cr} as amount, t2.is_advance, t2.exchange_rate,
t2.account_currency as currency, t2.cost_center as cost_center
from
`tabJournal Entry` t1, `tabJournal Entry Account` t2
where
t1.name = t2.parent and t1.docstatus = 1 and t2.docstatus = 1
and t2.party_type = %(party_type)s and t2.party = %(party)s
and t2.account = %(account)s and {dr_or_cr} > 0 {condition}
and (t2.reference_type is null or t2.reference_type = '' or
(t2.reference_type in ('Sales Order', 'Purchase Order')
and t2.reference_name is not null and t2.reference_name != ''))
and (CASE
WHEN t1.voucher_type in ('Debit Note', 'Credit Note')
THEN 1=1
ELSE {bank_account_condition}
END)
order by t1.posting_date
{limit}
""".format(
**{
"dr_or_cr": dr_or_cr,
"bank_account_condition": bank_account_condition,
"condition": condition,
"limit": limit,
}
),
{
"party_type": self.party_type,
"party": self.party,
"account": self.receivable_payable_account,
"bank_cash_account": "%%%s%%" % self.bank_cash_account,
},
as_dict=1,
)
journal_entries = journal_query.run(as_dict=True)
return list(journal_entries)
@@ -228,20 +231,18 @@ class PaymentReconciliation(Document):
self.common_filter_conditions.append(ple.account == self.receivable_payable_account)
self.get_return_invoices()
return_invoices = [
x for x in self.return_invoices if x.return_against == None or x.return_against == ""
]
outstanding_dr_or_cr = []
if return_invoices:
if self.return_invoices:
ple_query = QueryPaymentLedger()
return_outstanding = ple_query.get_voucher_outstandings(
vouchers=return_invoices,
vouchers=self.return_invoices,
common_filter=self.common_filter_conditions,
posting_date=self.ple_posting_date_filter,
min_outstanding=-(self.minimum_payment_amount) if self.minimum_payment_amount else None,
max_outstanding=-(self.maximum_payment_amount) if self.maximum_payment_amount else None,
get_payments=True,
accounting_dimensions=self.accounting_dimension_filter_conditions,
)
for inv in return_outstanding:
@@ -391,8 +392,15 @@ class PaymentReconciliation(Document):
row = self.append("allocation", {})
row.update(entry)
def update_dimension_values_in_allocated_entries(self, res):
for x in self.dimensions:
dimension = x.fieldname
if self.get(dimension):
res[dimension] = self.get(dimension)
return res
def get_allocated_entry(self, pay, inv, allocated_amount):
return frappe._dict(
res = frappe._dict(
{
"reference_type": pay.get("reference_type"),
"reference_name": pay.get("reference_name"),
@@ -408,6 +416,9 @@ class PaymentReconciliation(Document):
}
)
res = self.update_dimension_values_in_allocated_entries(res)
return res
def reconcile_allocations(self, skip_ref_details_update_for_pe=False):
adjust_allocations_for_taxes(self)
dr_or_cr = (
@@ -430,10 +441,10 @@ class PaymentReconciliation(Document):
reconciled_entry.append(payment_details)
if entry_list:
reconcile_against_document(entry_list, skip_ref_details_update_for_pe)
reconcile_against_document(entry_list, skip_ref_details_update_for_pe, self.dimensions)
if dr_or_cr_notes:
reconcile_dr_cr_note(dr_or_cr_notes, self.company)
reconcile_dr_cr_note(dr_or_cr_notes, self.company, self.dimensions)
@frappe.whitelist()
def reconcile(self):
@@ -462,7 +473,7 @@ class PaymentReconciliation(Document):
self.get_unreconciled_entries()
def get_payment_details(self, row, dr_or_cr):
return frappe._dict(
payment_details = frappe._dict(
{
"voucher_type": row.get("reference_type"),
"voucher_no": row.get("reference_name"),
@@ -485,6 +496,12 @@ class PaymentReconciliation(Document):
}
)
for x in self.dimensions:
if row.get(x.fieldname):
payment_details[x.fieldname] = row.get(x.fieldname)
return payment_details
def check_mandatory_to_fetch(self):
for fieldname in ["company", "party_type", "party", "receivable_payable_account"]:
if not self.get(fieldname):
@@ -549,7 +566,12 @@ class PaymentReconciliation(Document):
journals_map = frappe._dict(
frappe.db.get_all(
"Journal Entry Account",
filters={"parent": ("in", journals), "account": ("in", [self.receivable_payable_account])},
filters={
"parent": ("in", journals),
"account": ("in", [self.receivable_payable_account]),
"party_type": self.party_type,
"party": self.party,
},
fields=[
"parent as `name`",
"exchange_rate",
@@ -592,6 +614,13 @@ class PaymentReconciliation(Document):
if not invoices_to_reconcile:
frappe.throw(_("No records found in Allocation table"))
def build_dimensions_filter_conditions(self):
ple = qb.DocType("Payment Ledger Entry")
for x in self.dimensions:
dimension = x.fieldname
if self.get(dimension):
self.accounting_dimension_filter_conditions.append(ple[dimension] == self.get(dimension))
def build_qb_filter_conditions(self, get_invoices=False, get_return_invoices=False):
self.common_filter_conditions.clear()
self.accounting_dimension_filter_conditions.clear()
@@ -615,40 +644,58 @@ class PaymentReconciliation(Document):
if self.to_payment_date:
self.ple_posting_date_filter.append(ple.posting_date.lte(self.to_payment_date))
def get_conditions(self, get_payments=False):
condition = " and company = '{0}' ".format(self.company)
self.build_dimensions_filter_conditions()
if self.get("cost_center") and get_payments:
condition = " and cost_center = '{0}' ".format(self.cost_center)
def get_payment_entry_conditions(self):
conditions = []
pe = qb.DocType("Payment Entry")
conditions.append(pe.company == self.company)
condition += (
" and posting_date >= {0}".format(frappe.db.escape(self.from_payment_date))
if self.from_payment_date
else ""
)
condition += (
" and posting_date <= {0}".format(frappe.db.escape(self.to_payment_date))
if self.to_payment_date
else ""
)
if self.get("cost_center"):
conditions.append(pe.cost_center == self.cost_center)
if self.from_payment_date:
conditions.append(pe.posting_date.gte(self.from_payment_date))
if self.to_payment_date:
conditions.append(pe.posting_date.lte(self.to_payment_date))
if self.minimum_payment_amount:
condition += (
" and unallocated_amount >= {0}".format(flt(self.minimum_payment_amount))
if get_payments
else " and total_debit >= {0}".format(flt(self.minimum_payment_amount))
)
conditions.append(pe.unallocated_amount.gte(flt(self.minimum_payment_amount)))
if self.maximum_payment_amount:
condition += (
" and unallocated_amount <= {0}".format(flt(self.maximum_payment_amount))
if get_payments
else " and total_debit <= {0}".format(flt(self.maximum_payment_amount))
)
conditions.append(pe.unallocated_amount.lte(flt(self.maximum_payment_amount)))
return condition
# pass dynamic dimension filter values to payment query
for x in self.dimensions:
dimension = x.fieldname
if self.get(dimension):
conditions.append(pe[dimension] == self.get(dimension))
return conditions
def get_journal_filter_conditions(self):
conditions = []
je = qb.DocType("Journal Entry")
jea = qb.DocType("Journal Entry Account")
conditions.append(je.company == self.company)
if self.from_payment_date:
conditions.append(je.posting_date.gte(self.from_payment_date))
if self.to_payment_date:
conditions.append(je.posting_date.lte(self.to_payment_date))
if self.minimum_payment_amount:
conditions.append(je.total_debit.gte(self.minimum_payment_amount))
if self.maximum_payment_amount:
conditions.append(je.total_debit.lte(self.maximum_payment_amount))
return conditions
def reconcile_dr_cr_note(dr_cr_notes, company):
def reconcile_dr_cr_note(dr_cr_notes, company, active_dimensions=None):
for inv in dr_cr_notes:
voucher_type = "Credit Note" if inv.voucher_type == "Sales Invoice" else "Debit Note"
@@ -698,6 +745,15 @@ def reconcile_dr_cr_note(dr_cr_notes, company):
}
)
# Credit Note(JE) will inherit the same dimension values as payment
dimensions_dict = frappe._dict()
if active_dimensions:
for dim in active_dimensions:
dimensions_dict[dim.fieldname] = inv.get(dim.fieldname)
jv.accounts[0].update(dimensions_dict)
jv.accounts[1].update(dimensions_dict)
jv.flags.ignore_mandatory = True
jv.flags.skip_remarks_creation = True
jv.flags.ignore_exchange_rate = True
@@ -731,9 +787,27 @@ def reconcile_dr_cr_note(dr_cr_notes, company):
inv.against_voucher,
None,
inv.cost_center,
dimensions_dict,
)
@erpnext.allow_regional
def adjust_allocations_for_taxes(doc):
pass
@frappe.whitelist()
def get_queries_for_dimension_filters(company: str = None):
dimensions_with_filters = []
for d in get_dimensions()[0]:
filters = {}
meta = frappe.get_meta(d.document_type)
if meta.has_field("company") and company:
filters.update({"company": company})
if meta.is_tree:
filters.update({"is_group": 0})
dimensions_with_filters.append({"fieldname": d.fieldname, "filters": filters})
return dimensions_with_filters

View File

@@ -56,6 +56,7 @@ class TestPaymentReconciliation(FrappeTestCase):
self.expense_account = "Cost of Goods Sold - _PR"
self.debit_to = "Debtors - _PR"
self.creditors = "Creditors - _PR"
self.cash = "Cash - _PR"
# create bank account
if frappe.db.exists("Account", "HDFC - _PR"):
@@ -486,6 +487,91 @@ class TestPaymentReconciliation(FrappeTestCase):
self.assertEqual(len(pr.get("invoices")), 0)
self.assertEqual(len(pr.get("payments")), 0)
def test_payment_against_foreign_currency_journal(self):
transaction_date = nowdate()
self.supplier = "_Test Supplier USD"
self.supplier2 = make_supplier("_Test Supplier2 USD", "USD")
amount = 100
exc_rate1 = 80
exc_rate2 = 83
je = frappe.new_doc("Journal Entry")
je.posting_date = transaction_date
je.company = self.company
je.user_remark = "test"
je.multi_currency = 1
je.set(
"accounts",
[
{
"account": self.creditors_usd,
"party_type": "Supplier",
"party": self.supplier,
"exchange_rate": exc_rate1,
"cost_center": self.cost_center,
"credit": amount * exc_rate1,
"credit_in_account_currency": amount,
},
{
"account": self.creditors_usd,
"party_type": "Supplier",
"party": self.supplier2,
"exchange_rate": exc_rate2,
"cost_center": self.cost_center,
"credit": amount * exc_rate2,
"credit_in_account_currency": amount,
},
{
"account": self.expense_account,
"cost_center": self.cost_center,
"debit": (amount * exc_rate1) + (amount * exc_rate2),
"debit_in_account_currency": (amount * exc_rate1) + (amount * exc_rate2),
},
],
)
je.save().submit()
pe = self.create_payment_entry(amount=amount, posting_date=transaction_date)
pe.payment_type = "Pay"
pe.party_type = "Supplier"
pe.party = self.supplier
pe.paid_to = self.creditors_usd
pe.paid_from = self.cash
pe.paid_amount = 8000
pe.received_amount = 100
pe.target_exchange_rate = exc_rate1
pe.paid_to_account_currency = "USD"
pe.save().submit()
pr = self.create_payment_reconciliation(party_is_customer=False)
pr.receivable_payable_account = self.creditors_usd
pr.minimum_invoice_amount = pr.maximum_invoice_amount = amount
pr.from_invoice_date = pr.to_invoice_date = transaction_date
pr.from_payment_date = pr.to_payment_date = transaction_date
pr.get_unreconciled_entries()
invoices = [x.as_dict() for x in pr.get("invoices")]
payments = [x.as_dict() for x in pr.get("payments")]
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
# There should no difference_amount as the Journal and Payment have same exchange rate - 'exc_rate1'
for row in pr.allocation:
self.assertEqual(flt(row.get("difference_amount")), 0.0)
pr.reconcile()
# check PR tool output
self.assertEqual(len(pr.get("invoices")), 0)
self.assertEqual(len(pr.get("payments")), 0)
journals = frappe.db.get_all(
"Journal Entry Account",
filters={"reference_type": je.doctype, "reference_name": je.name, "docstatus": 1},
fields=["parent"],
)
self.assertEqual([], journals)
def test_journal_against_invoice(self):
transaction_date = nowdate()
amount = 100
@@ -591,6 +677,70 @@ class TestPaymentReconciliation(FrappeTestCase):
self.assertEqual(si.status, "Paid")
self.assertEqual(si.outstanding_amount, 0)
def test_invoice_status_after_cr_note_cancellation(self):
# This test case is made after the 'always standalone Credit/Debit notes' feature is introduced
transaction_date = nowdate()
amount = 100
si = self.create_sales_invoice(qty=1, rate=amount, posting_date=transaction_date)
cr_note = self.create_sales_invoice(
qty=-1, rate=amount, posting_date=transaction_date, do_not_save=True, do_not_submit=True
)
cr_note.is_return = 1
cr_note.return_against = si.name
cr_note = cr_note.save().submit()
pr = self.create_payment_reconciliation()
pr.get_unreconciled_entries()
invoices = [x.as_dict() for x in pr.get("invoices")]
payments = [x.as_dict() for x in pr.get("payments")]
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
pr.reconcile()
pr.get_unreconciled_entries()
self.assertEqual(pr.get("invoices"), [])
self.assertEqual(pr.get("payments"), [])
journals = frappe.db.get_all(
"Journal Entry",
filters={
"is_system_generated": 1,
"docstatus": 1,
"voucher_type": "Credit Note",
"reference_type": si.doctype,
"reference_name": si.name,
},
pluck="name",
)
self.assertEqual(len(journals), 1)
# assert status and outstanding
si.reload()
self.assertEqual(si.status, "Credit Note Issued")
self.assertEqual(si.outstanding_amount, 0)
cr_note.reload()
cr_note.cancel()
# 'Credit Note' Journal should be auto cancelled
journals = frappe.db.get_all(
"Journal Entry",
filters={
"is_system_generated": 1,
"docstatus": 1,
"voucher_type": "Credit Note",
"reference_type": si.doctype,
"reference_name": si.name,
},
pluck="name",
)
self.assertEqual(len(journals), 0)
# assert status and outstanding
si.reload()
self.assertEqual(si.status, "Unpaid")
self.assertEqual(si.outstanding_amount, 100)
def test_cr_note_partial_against_invoice(self):
transaction_date = nowdate()
amount = 100
@@ -1184,3 +1334,17 @@ def make_customer(customer_name, currency=None):
return customer.name
else:
return customer_name
def make_supplier(supplier_name, currency=None):
if not frappe.db.exists("Supplier", supplier_name):
supplier = frappe.new_doc("Supplier")
supplier.supplier_name = supplier_name
supplier.type = "Individual"
supplier.supplier_group = "Local"
if currency:
supplier.default_currency = currency
supplier.save()
return supplier.name
else:
return supplier_name

View File

@@ -24,7 +24,9 @@
"difference_account",
"exchange_rate",
"currency",
"cost_center"
"accounting_dimensions_section",
"cost_center",
"dimension_col_break"
],
"fields": [
{
@@ -157,12 +159,21 @@
"fieldname": "gain_loss_posting_date",
"fieldtype": "Date",
"label": "Difference Posting Date"
},
{
"fieldname": "accounting_dimensions_section",
"fieldtype": "Section Break",
"label": "Accounting Dimensions"
},
{
"fieldname": "dimension_col_break",
"fieldtype": "Column Break"
}
],
"is_virtual": 1,
"istable": 1,
"links": [],
"modified": "2023-11-17 17:33:38.612615",
"modified": "2023-12-14 13:38:26.104150",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Reconciliation Allocation",

View File

@@ -13,7 +13,6 @@ from erpnext.accounts.doctype.loyalty_program.loyalty_program import validate_lo
from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request
from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
SalesInvoice,
get_bank_cash_account,
get_mode_of_payment_info,
update_multi_mode_option,
)
@@ -52,7 +51,6 @@ class POSInvoice(SalesInvoice):
self.validate_stock_availablility()
self.validate_return_items_qty()
self.set_status()
self.set_account_for_mode_of_payment()
self.validate_pos()
self.validate_payment_amount()
self.validate_loyalty_transaction()
@@ -584,11 +582,6 @@ class POSInvoice(SalesInvoice):
update_multi_mode_option(self, pos_profile)
self.paid_amount = 0
def set_account_for_mode_of_payment(self):
for pay in self.payments:
if not pay.account:
pay.account = get_bank_cash_account(pay.mode_of_payment, self.company).get("account")
@frappe.whitelist()
def create_payment_request(self):
for pay in self.payments:
@@ -705,7 +698,7 @@ def get_pos_reserved_qty(item_code, warehouse):
reserved_qty = (
frappe.qb.from_(p_inv)
.from_(p_item)
.select(Sum(p_item.qty).as_("qty"))
.select(Sum(p_item.stock_qty).as_("stock_qty"))
.where(
(p_inv.name == p_item.parent)
& (IfNull(p_inv.consolidated_invoice, "") == "")
@@ -716,7 +709,7 @@ def get_pos_reserved_qty(item_code, warehouse):
)
).run(as_dict=True)
return reserved_qty[0].qty or 0 if reserved_qty else 0
return flt(reserved_qty[0].stock_qty) if reserved_qty else 0
@frappe.whitelist()

View File

@@ -521,7 +521,7 @@ def get_qty_amount_data_for_cumulative(pr_doc, doc, items=None):
values.extend(warehouses)
if items:
condition = " and `tab{child_doc}`.{apply_on} in ({items})".format(
condition += " and `tab{child_doc}`.{apply_on} in ({items})".format(
child_doc=child_doctype, apply_on=apply_on, items=",".join(["%s"] * len(items))
)

View File

@@ -415,7 +415,7 @@ def reconcile(doc: None | str = None) -> None:
# Update the parent doc about the exception
frappe.db.rollback()
traceback = frappe.get_traceback()
traceback = frappe.get_traceback(with_context=True)
if traceback:
message = "Traceback: <br>" + traceback
frappe.db.set_value("Process Payment Reconciliation Log", log, "error_log", message)

View File

@@ -64,18 +64,6 @@ def get_statement_dict(doc, get_statement_dict=False):
statement_dict = {}
ageing = ""
err_journals = None
if doc.report == "General Ledger" and doc.ignore_exchange_rate_revaluation_journals:
err_journals = frappe.db.get_all(
"Journal Entry",
filters={
"company": doc.company,
"docstatus": 1,
"voucher_type": ("in", ["Exchange Rate Revaluation", "Exchange Gain Or Loss"]),
},
as_list=True,
)
for entry in doc.customers:
if doc.include_ageing:
ageing = set_ageing(doc, entry)
@@ -88,8 +76,8 @@ def get_statement_dict(doc, get_statement_dict=False):
)
filters = get_common_filters(doc)
if err_journals:
filters.update({"voucher_no_not_in": [x[0] for x in err_journals]})
if doc.ignore_exchange_rate_revaluation_journals:
filters.update({"ignore_err": True})
if doc.report == "General Ledger":
filters.update(get_gl_filters(doc, entry, tax_id, presentation_currency))

View File

@@ -31,7 +31,7 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
super.onload();
// Ignore linked advances
this.frm.ignore_doctypes_on_cancel_all = ['Journal Entry', 'Payment Entry', 'Purchase Invoice', "Repost Payment Ledger", "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries"];
this.frm.ignore_doctypes_on_cancel_all = ['Journal Entry', 'Payment Entry', 'Purchase Invoice', "Repost Payment Ledger", "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries", "Bank Transaction"];
if(!this.frm.doc.__islocal) {
// show credit_to in print format
@@ -99,8 +99,7 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
}
}
if(doc.docstatus == 1 && doc.outstanding_amount != 0
&& !(doc.is_return && doc.return_against) && !doc.on_hold) {
if(doc.docstatus == 1 && doc.outstanding_amount != 0 && !doc.on_hold) {
this.frm.add_custom_button(
__('Payment'),
() => this.make_payment_entry(),
@@ -164,6 +163,18 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
}
})
}, __("Get Items From"));
if (!this.frm.doc.is_return) {
frappe.db.get_single_value("Buying Settings", "maintain_same_rate").then((value) => {
if (value) {
this.frm.doc.items.forEach((item) => {
this.frm.fields_dict.items.grid.update_docfield_property(
"rate", "read_only", (item.purchase_receipt && item.pr_detail)
);
});
}
});
}
}
this.frm.toggle_reqd("supplier_warehouse", this.frm.doc.is_subcontracted);

View File

@@ -22,6 +22,8 @@
"is_paid",
"is_return",
"return_against",
"update_billed_amount_in_purchase_order",
"update_billed_amount_in_purchase_receipt",
"apply_tds",
"tax_withholding_category",
"amended_from",
@@ -410,6 +412,20 @@
"read_only": 1,
"search_index": 1
},
{
"default": "0",
"depends_on": "eval: doc.is_return",
"fieldname": "update_billed_amount_in_purchase_order",
"fieldtype": "Check",
"label": "Update Billed Amount in Purchase Order"
},
{
"default": "1",
"depends_on": "eval: doc.is_return",
"fieldname": "update_billed_amount_in_purchase_receipt",
"fieldtype": "Check",
"label": "Update Billed Amount in Purchase Receipt"
},
{
"fieldname": "section_addresses",
"fieldtype": "Section Break",
@@ -1594,7 +1610,7 @@
"idx": 204,
"is_submittable": 1,
"links": [],
"modified": "2023-11-03 15:47:30.319200",
"modified": "2024-02-25 11:20:28.366808",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice",

View File

@@ -131,6 +131,18 @@ class PurchaseInvoice(BuyingController):
self.reset_default_field_value("set_warehouse", "items", "warehouse")
self.reset_default_field_value("rejected_warehouse", "items", "rejected_warehouse")
self.reset_default_field_value("set_from_warehouse", "items", "from_warehouse")
self.set_percentage_received()
def set_percentage_received(self):
total_billed_qty = 0.0
total_received_qty = 0.0
for row in self.items:
if row.purchase_receipt and row.pr_detail and row.received_qty:
total_billed_qty += row.qty
total_received_qty += row.received_qty
if total_billed_qty and total_received_qty:
self.per_received = total_received_qty / total_billed_qty * 100
def validate_release_date(self):
if self.release_date and getdate(nowdate()) >= getdate(self.release_date):
@@ -502,6 +514,11 @@ class PurchaseInvoice(BuyingController):
super(PurchaseInvoice, self).on_submit()
self.check_prev_docstatus()
if self.is_return and not self.update_billed_amount_in_purchase_order:
# NOTE status updating bypassed for is_return
self.status_updater = []
self.update_status_updater_args()
self.update_prevdoc_status()
@@ -659,9 +676,7 @@ class PurchaseInvoice(BuyingController):
"credit_in_account_currency": base_grand_total
if self.party_account_currency == self.company_currency
else grand_total,
"against_voucher": self.return_against
if cint(self.is_return) and self.return_against
else self.name,
"against_voucher": self.name,
"against_voucher_type": self.doctype,
"project": self.project,
"cost_center": self.cost_center,
@@ -844,9 +859,14 @@ class PurchaseInvoice(BuyingController):
if provisional_accounting_for_non_stock_items:
if item.purchase_receipt:
provisional_account = frappe.db.get_value(
"Purchase Receipt Item", item.pr_detail, "provisional_expense_account"
) or self.get_company_default("default_provisional_account")
provisional_account, pr_qty, pr_base_rate = frappe.get_cached_value(
"Purchase Receipt Item",
item.pr_detail,
["provisional_expense_account", "qty", "base_rate"],
)
provisional_account = provisional_account or self.get_company_default(
"default_provisional_account"
)
purchase_receipt_doc = purchase_receipt_doc_map.get(item.purchase_receipt)
if not purchase_receipt_doc:
@@ -863,13 +883,18 @@ class PurchaseInvoice(BuyingController):
"voucher_detail_no": item.pr_detail,
"account": provisional_account,
},
["name"],
"name",
)
if expense_booked_in_pr:
# Intentionally passing purchase invoice item to handle partial billing
purchase_receipt_doc.add_provisional_gl_entry(
item, gl_entries, self.posting_date, provisional_account, reverse=1
item,
gl_entries,
self.posting_date,
provisional_account,
reverse=1,
item_amount=(min(item.qty, pr_qty) * pr_base_rate),
)
if not self.is_internal_transfer():
@@ -925,17 +950,6 @@ class PurchaseInvoice(BuyingController):
item=item,
)
)
# update gross amount of asset bought through this document
assets = frappe.db.get_all(
"Asset", filters={"purchase_invoice": self.name, "item_code": item.item_code}
)
for asset in assets:
frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate))
frappe.db.set_value(
"Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate)
)
if (
self.auto_accounting_for_stock
and self.is_opening == "No"
@@ -975,12 +989,25 @@ class PurchaseInvoice(BuyingController):
item.item_tax_amount, item.precision("item_tax_amount")
)
if item.is_fixed_asset and item.landed_cost_voucher_amount:
self.update_gross_purchase_amount_for_linked_assets(item)
def update_gross_purchase_amount_for_linked_assets(self, item):
assets = frappe.db.get_all(
"Asset", filters={"purchase_invoice": self.name, "item_code": item.item_code}
"Asset",
filters={"purchase_invoice": self.name, "item_code": item.item_code},
fields=["name", "asset_quantity"],
)
for asset in assets:
frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate))
frappe.db.set_value("Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate))
purchase_amount = flt(item.valuation_rate) * asset.asset_quantity
frappe.db.set_value(
"Asset",
asset.name,
{
"gross_purchase_amount": purchase_amount,
"purchase_receipt_amount": purchase_amount,
},
)
def make_stock_adjustment_entry(
self, gl_entries, item, voucher_wise_stock_value, account_currency
@@ -1252,6 +1279,10 @@ class PurchaseInvoice(BuyingController):
self.check_on_hold_or_closed_status()
if self.is_return and not self.update_billed_amount_in_purchase_order:
# NOTE status updating bypassed for is_return
self.status_updater = []
self.update_status_updater_args()
self.update_prevdoc_status()
@@ -1345,6 +1376,9 @@ class PurchaseInvoice(BuyingController):
frappe.throw(_("Supplier Invoice No exists in Purchase Invoice {0}").format(pi))
def update_billing_status_in_pr(self, update_modified=True):
if self.is_return and not self.update_billed_amount_in_purchase_receipt:
return
updated_pr = []
po_details = []
@@ -1526,12 +1560,8 @@ class PurchaseInvoice(BuyingController):
elif outstanding_amount > 0 and getdate(self.due_date) >= getdate():
self.status = "Unpaid"
# Check if outstanding amount is 0 due to debit note issued against invoice
elif (
outstanding_amount <= 0
and self.is_return == 0
and frappe.db.get_value(
"Purchase Invoice", {"is_return": 1, "return_against": self.name, "docstatus": 1}
)
elif self.is_return == 0 and frappe.db.get_value(
"Purchase Invoice", {"is_return": 1, "return_against": self.name, "docstatus": 1}
):
self.status = "Debit Note Issued"
elif self.is_return == 1:
@@ -1660,10 +1690,6 @@ def make_inter_company_sales_invoice(source_name, target_doc=None):
return make_inter_company_transaction("Purchase Invoice", source_name, target_doc)
def on_doctype_update():
frappe.db.add_index("Purchase Invoice", ["supplier", "is_return", "return_against"])
@frappe.whitelist()
def make_purchase_receipt(source_name, target_doc=None):
def update_item(obj, target, source_parent):

View File

@@ -1520,18 +1520,7 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
self.assertEqual(payment_entry.taxes[0].allocated_amount, 0)
def test_provisional_accounting_entry(self):
create_item("_Test Non Stock Item", is_stock_item=0)
provisional_account = create_account(
account_name="Provision Account",
parent_account="Current Liabilities - _TC",
company="_Test Company",
)
company = frappe.get_doc("Company", "_Test Company")
company.enable_provisional_accounting_for_non_stock_items = 1
company.default_provisional_account = provisional_account
company.save()
setup_provisional_accounting()
pr = make_purchase_receipt(
item_code="_Test Non Stock Item", posting_date=add_days(nowdate(), -2)
@@ -1575,8 +1564,97 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
self, pr.name, expected_gle_for_purchase_receipt_post_pi_cancel, pr.posting_date
)
company.enable_provisional_accounting_for_non_stock_items = 0
company.save()
toggle_provisional_accounting_setting()
def test_provisional_accounting_entry_for_over_billing(self):
setup_provisional_accounting()
# Configure Buying Settings to allow rate change
frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 0)
# Create PR: rate = 1000, qty = 5
pr = make_purchase_receipt(
item_code="_Test Non Stock Item", rate=1000, posting_date=add_days(nowdate(), -2)
)
# Overbill PR: rate = 2000, qty = 10
pi = create_purchase_invoice_from_receipt(pr.name)
pi.set_posting_time = 1
pi.posting_date = add_days(pr.posting_date, -1)
pi.items[0].qty = 10
pi.items[0].rate = 2000
pi.items[0].expense_account = "Cost of Goods Sold - _TC"
pi.save()
pi.submit()
expected_gle = [
["Cost of Goods Sold - _TC", 20000, 0, add_days(pr.posting_date, -1)],
["Creditors - _TC", 0, 20000, add_days(pr.posting_date, -1)],
]
check_gl_entries(self, pi.name, expected_gle, pi.posting_date)
expected_gle_for_purchase_receipt = [
["Provision Account - _TC", 5000, 0, pr.posting_date],
["_Test Account Cost for Goods Sold - _TC", 0, 5000, pr.posting_date],
["Provision Account - _TC", 0, 5000, pi.posting_date],
["_Test Account Cost for Goods Sold - _TC", 5000, 0, pi.posting_date],
]
check_gl_entries(self, pr.name, expected_gle_for_purchase_receipt, pr.posting_date)
# Cancel purchase invoice to check reverse provisional entry cancellation
pi.cancel()
expected_gle_for_purchase_receipt_post_pi_cancel = [
["Provision Account - _TC", 0, 5000, pi.posting_date],
["_Test Account Cost for Goods Sold - _TC", 5000, 0, pi.posting_date],
]
check_gl_entries(
self, pr.name, expected_gle_for_purchase_receipt_post_pi_cancel, pr.posting_date
)
toggle_provisional_accounting_setting()
def test_provisional_accounting_entry_for_partial_billing(self):
setup_provisional_accounting()
# Configure Buying Settings to allow rate change
frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 0)
# Create PR: rate = 1000, qty = 5
pr = make_purchase_receipt(
item_code="_Test Non Stock Item", rate=1000, posting_date=add_days(nowdate(), -2)
)
# Partially bill PR: rate = 500, qty = 2
pi = create_purchase_invoice_from_receipt(pr.name)
pi.set_posting_time = 1
pi.posting_date = add_days(pr.posting_date, -1)
pi.items[0].qty = 2
pi.items[0].rate = 500
pi.items[0].expense_account = "Cost of Goods Sold - _TC"
pi.save()
pi.submit()
expected_gle = [
["Cost of Goods Sold - _TC", 1000, 0, add_days(pr.posting_date, -1)],
["Creditors - _TC", 0, 1000, add_days(pr.posting_date, -1)],
]
check_gl_entries(self, pi.name, expected_gle, pi.posting_date)
expected_gle_for_purchase_receipt = [
["Provision Account - _TC", 5000, 0, pr.posting_date],
["_Test Account Cost for Goods Sold - _TC", 0, 5000, pr.posting_date],
["Provision Account - _TC", 0, 1000, pi.posting_date],
["_Test Account Cost for Goods Sold - _TC", 1000, 0, pi.posting_date],
]
check_gl_entries(self, pr.name, expected_gle_for_purchase_receipt, pr.posting_date)
toggle_provisional_accounting_setting()
def test_adjust_incoming_rate(self):
frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 0)
@@ -1893,6 +1971,21 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
self.assertEqual(pi.items[0].cost_center, "_Test Cost Center Buying - _TC")
def test_debit_note_with_account_mismatch(self):
new_creditors = create_account(
parent_account="Accounts Payable - _TC",
account_name="Creditors 2",
company="_Test Company",
account_type="Payable",
)
pi = make_purchase_invoice(qty=1, rate=1000)
dr_note = make_purchase_invoice(
qty=-1, rate=1000, is_return=1, return_against=pi.name, do_not_save=True
)
dr_note.credit_to = new_creditors
self.assertRaises(frappe.ValidationError, dr_note.save)
def check_gl_entries(
doc,
@@ -2076,4 +2169,26 @@ def make_purchase_invoice_against_cost_center(**args):
return pi
def setup_provisional_accounting(**args):
args = frappe._dict(args)
create_item("_Test Non Stock Item", is_stock_item=0)
company = args.company or "_Test Company"
provisional_account = create_account(
account_name=args.account_name or "Provision Account",
parent_account=args.parent_account or "Current Liabilities - _TC",
company=company,
)
toggle_provisional_accounting_setting(
enable=1, company=company, provisional_account=provisional_account
)
def toggle_provisional_accounting_setting(**args):
args = frappe._dict(args)
company = frappe.get_doc("Company", args.company or "_Test Company")
company.enable_provisional_accounting_for_non_stock_items = args.enable or 0
company.default_provisional_account = args.provisional_account
company.save()
test_records = frappe.get_test_records("Purchase Invoice")

View File

@@ -286,7 +286,6 @@
"oldfieldname": "import_rate",
"oldfieldtype": "Currency",
"options": "currency",
"read_only_depends_on": "eval: (!parent.is_return && doc.purchase_receipt && doc.pr_detail)",
"reqd": 1
},
{
@@ -894,7 +893,7 @@
"idx": 1,
"istable": 1,
"links": [],
"modified": "2023-11-30 16:26:05.629780",
"modified": "2023-12-25 22:00:28.043555",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Item",

View File

@@ -126,7 +126,7 @@
"fieldname": "rate",
"fieldtype": "Float",
"in_list_view": 1,
"label": "Rate",
"label": "Tax Rate",
"oldfieldname": "rate",
"oldfieldtype": "Currency"
},
@@ -230,7 +230,7 @@
"idx": 1,
"istable": 1,
"links": [],
"modified": "2021-08-05 20:04:36.618240",
"modified": "2024-01-14 10:04:36.618240",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Taxes and Charges",
@@ -239,4 +239,4 @@
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}
}

View File

@@ -70,6 +70,7 @@ class RepostAccountingLedger(Document):
).append(gle.update({"old": True}))
def generate_preview_data(self):
frappe.flags.through_repost_accounting_ledger = True
self.gl_entries = []
self.get_existing_ledger_entries()
for x in self.vouchers:
@@ -123,6 +124,7 @@ class RepostAccountingLedger(Document):
@frappe.whitelist()
def start_repost(account_repost_doc=str) -> None:
frappe.flags.through_repost_accounting_ledger = True
if account_repost_doc:
repost_doc = frappe.get_doc("Repost Accounting Ledger", account_repost_doc)

View File

@@ -43,7 +43,7 @@ def start_payment_ledger_repost(docname=None):
except Exception as e:
frappe.db.rollback()
traceback = frappe.get_traceback()
traceback = frappe.get_traceback(with_context=True)
if traceback:
message = "Traceback: <br>" + traceback
frappe.db.set_value(repost_doc.doctype, repost_doc.name, "repost_error_log", message)

View File

@@ -11,30 +11,15 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e
super.setup(doc);
}
company() {
super.company();
erpnext.accounts.dimensions.update_dimension(this.frm, this.frm.doctype);
let me = this;
if (this.frm.doc.company) {
frappe.call({
method:
"erpnext.accounts.party.get_party_account",
args: {
party_type: 'Customer',
party: this.frm.doc.customer,
company: this.frm.doc.company
},
callback: (response) => {
if (response) me.frm.set_value("debit_to", response.message);
},
});
}
}
onload() {
var me = this;
super.onload();
this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice', 'Timesheet', 'POS Invoice Merge Log',
'POS Closing Entry', 'Journal Entry', 'Payment Entry', "Repost Payment Ledger", "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries"];
'POS Closing Entry', 'Journal Entry', 'Payment Entry', "Repost Payment Ledger", "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries", "Bank Transaction"];
if(!this.frm.doc.__islocal && !this.frm.doc.customer && this.frm.doc.debit_to) {
// show debit_to in print format
@@ -91,8 +76,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e
if(doc.update_stock) this.show_stock_ledger();
if (doc.docstatus == 1 && doc.outstanding_amount!=0
&& !(cint(doc.is_return) && doc.return_against)) {
if (doc.docstatus == 1 && doc.outstanding_amount!=0) {
this.frm.add_custom_button(
__('Payment'),
() => this.make_payment_entry(),
@@ -890,8 +874,8 @@ frappe.ui.form.on('Sales Invoice', {
frm.events.append_time_log(frm, timesheet, 1.0);
}
});
frm.refresh_field("timesheets");
frm.trigger("calculate_timesheet_totals");
frm.refresh();
},
async get_exchange_rate(frm, from_currency, to_currency) {

View File

@@ -138,6 +138,7 @@
"loyalty_amount",
"column_break_77",
"loyalty_program",
"dont_create_loyalty_points",
"loyalty_redemption_account",
"loyalty_redemption_cost_center",
"contact_and_address_tab",
@@ -1039,8 +1040,7 @@
"label": "Loyalty Program",
"no_copy": 1,
"options": "Loyalty Program",
"print_hide": 1,
"read_only": 1
"print_hide": 1
},
{
"allow_on_submit": 1,
@@ -2153,6 +2153,14 @@
"fieldname": "update_billed_amount_in_delivery_note",
"fieldtype": "Check",
"label": "Update Billed Amount in Delivery Note"
},
{
"default": "0",
"depends_on": "loyalty_program",
"fieldname": "dont_create_loyalty_points",
"fieldtype": "Check",
"label": "Don't Create Loyalty Points",
"no_copy": 1
}
],
"icon": "fa fa-file-text",
@@ -2165,7 +2173,7 @@
"link_fieldname": "consolidated_invoice"
}
],
"modified": "2023-11-23 16:56:29.679499",
"modified": "2024-01-02 17:25:46.027523",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",

View File

@@ -90,7 +90,7 @@ class SalesInvoice(SellingController):
super(SalesInvoice, self).validate()
self.validate_auto_set_posting_time()
if not self.is_pos:
if not (self.is_pos or self.is_debit_note):
self.so_dn_required()
self.set_tax_withholding()
@@ -245,7 +245,8 @@ class SalesInvoice(SellingController):
self.calculate_taxes_and_totals()
def before_save(self):
set_account_for_mode_of_payment(self)
self.set_account_for_mode_of_payment()
self.set_paid_amount()
def on_submit(self):
self.validate_pos_paid_amount()
@@ -300,7 +301,12 @@ class SalesInvoice(SellingController):
update_linked_doc(self.doctype, self.name, self.inter_company_invoice_reference)
# create the loyalty point ledger entry if the customer is enrolled in any loyalty program
if not self.is_return and not self.is_consolidated and self.loyalty_program:
if (
not self.is_return
and not self.is_consolidated
and self.loyalty_program
and not self.dont_create_loyalty_points
):
self.make_loyalty_point_entry()
elif (
self.is_return and self.return_against and not self.is_consolidated and self.loyalty_program
@@ -533,9 +539,6 @@ class SalesInvoice(SellingController):
):
data.sales_invoice = sales_invoice
def on_update(self):
self.set_paid_amount()
def on_update_after_submit(self):
if hasattr(self, "repost_required"):
fields_to_check = [
@@ -566,6 +569,11 @@ class SalesInvoice(SellingController):
self.paid_amount = paid_amount
self.base_paid_amount = base_paid_amount
def set_account_for_mode_of_payment(self):
for payment in self.payments:
if not payment.account:
payment.account = get_bank_cash_account(payment.mode_of_payment, self.company).get("account")
def validate_time_sheets_are_submitted(self):
for data in self.timesheets:
if data.time_sheet:
@@ -1064,9 +1072,7 @@ class SalesInvoice(SellingController):
"debit_in_account_currency": base_grand_total
if self.party_account_currency == self.company_currency
else grand_total,
"against_voucher": self.return_against
if cint(self.is_return) and self.return_against
else self.name,
"against_voucher": self.name,
"against_voucher_type": self.doctype,
"cost_center": self.cost_center,
"project": self.project,
@@ -1287,9 +1293,7 @@ class SalesInvoice(SellingController):
"credit_in_account_currency": payment_mode.base_amount
if self.party_account_currency == self.company_currency
else payment_mode.amount,
"against_voucher": self.return_against
if cint(self.is_return) and self.return_against
else self.name,
"against_voucher": self.name,
"against_voucher_type": self.doctype,
"cost_center": self.cost_center,
},
@@ -1693,12 +1697,8 @@ class SalesInvoice(SellingController):
elif outstanding_amount > 0 and getdate(self.due_date) >= getdate():
self.status = "Unpaid"
# Check if outstanding amount is 0 due to credit note issued against invoice
elif (
outstanding_amount <= 0
and self.is_return == 0
and frappe.db.get_value(
"Sales Invoice", {"is_return": 1, "return_against": self.name, "docstatus": 1}
)
elif self.is_return == 0 and frappe.db.get_value(
"Sales Invoice", {"is_return": 1, "return_against": self.name, "docstatus": 1}
):
self.status = "Credit Note Issued"
elif self.is_return == 1:
@@ -1944,12 +1944,6 @@ def make_sales_return(source_name, target_doc=None):
return make_return_doc("Sales Invoice", source_name, target_doc)
def set_account_for_mode_of_payment(self):
for data in self.payments:
if not data.account:
data.account = get_bank_cash_account(data.mode_of_payment, self.company).get("account")
def get_inter_company_details(doc, doctype):
if doctype in ["Sales Invoice", "Sales Order", "Delivery Note"]:
parties = frappe.db.get_all(
@@ -2388,10 +2382,6 @@ def get_loyalty_programs(customer):
return lp_details
def on_doctype_update():
frappe.db.add_index("Sales Invoice", ["customer", "is_return", "return_against"])
@frappe.whitelist()
def create_invoice_discounting(source_name, target_doc=None):
invoice = frappe.get_doc("Sales Invoice", source_name)

View File

@@ -1081,6 +1081,44 @@ class TestSalesInvoice(FrappeTestCase):
self.assertEqual(pos.grand_total, 100.0)
self.assertEqual(pos.write_off_amount, 10)
def test_ledger_entries_of_return_pos_invoice(self):
make_pos_profile()
pos = create_sales_invoice(do_not_save=True)
pos.is_pos = 1
pos.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 100})
pos.save().submit()
self.assertEqual(pos.outstanding_amount, 0.0)
self.assertEqual(pos.status, "Paid")
from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_sales_return
pos_return = make_sales_return(pos.name)
pos_return.save().submit()
pos_return.reload()
pos.reload()
self.assertEqual(pos_return.is_return, 1)
self.assertEqual(pos_return.return_against, pos.name)
self.assertEqual(pos_return.outstanding_amount, 0.0)
self.assertEqual(pos_return.status, "Return")
self.assertEqual(pos.outstanding_amount, 0.0)
self.assertEqual(pos.status, "Credit Note Issued")
expected = (
("Cash - _TC", 0.0, 100.0, pos_return.name, None),
("Debtors - _TC", 0.0, 100.0, pos_return.name, pos_return.name),
("Debtors - _TC", 100.0, 0.0, pos_return.name, pos_return.name),
("Sales - _TC", 100.0, 0.0, pos_return.name, None),
)
res = frappe.db.get_all(
"GL Entry",
filters={"voucher_no": pos_return.name, "is_cancelled": 0},
fields=["account", "debit", "credit", "voucher_no", "against_voucher"],
order_by="account, debit, credit",
as_list=1,
)
self.assertEqual(expected, res)
def test_pos_with_no_gl_entry_for_change_amount(self):
frappe.db.set_value("Accounts Settings", None, "post_change_gl_entries", 0)
@@ -1528,8 +1566,21 @@ class TestSalesInvoice(FrappeTestCase):
self.assertEqual(party_credited, 1000)
# Check outstanding amount
self.assertFalse(si1.outstanding_amount)
self.assertEqual(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"), 1500)
self.assertEqual(frappe.db.get_value("Sales Invoice", si1.name, "outstanding_amount"), -1000)
self.assertEqual(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"), 2500)
def test_return_invoice_with_account_mismatch(self):
debtors2 = create_account(
parent_account="Accounts Receivable - _TC",
account_name="Debtors 2",
company="_Test Company",
account_type="Receivable",
)
si = create_sales_invoice(qty=1, rate=1000)
cr_note = create_sales_invoice(
qty=-1, rate=1000, is_return=1, return_against=si.name, debit_to=debtors2, do_not_save=True
)
self.assertRaises(frappe.ValidationError, cr_note.save)
def test_gle_made_when_asset_is_returned(self):
create_asset_data()

View File

@@ -108,7 +108,7 @@
"fieldname": "rate",
"fieldtype": "Float",
"in_list_view": 1,
"label": "Rate",
"label": "Tax Rate",
"oldfieldname": "rate",
"oldfieldtype": "Currency"
},
@@ -218,7 +218,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2022-10-17 13:08:17.776528",
"modified": "2024-01-14 10:08:17.776528",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Taxes and Charges",
@@ -227,4 +227,4 @@
"sort_field": "modified",
"sort_order": "ASC",
"states": []
}
}

View File

@@ -522,6 +522,7 @@ def get_tcs_amount(parties, inv, tax_details, vouchers, adv_vouchers):
"GL Entry",
{
"is_cancelled": 0,
"party_type": "Customer",
"party": ["in", parties],
"company": inv.company,
"voucher_no": ["in", vouchers],
@@ -536,6 +537,7 @@ def get_tcs_amount(parties, inv, tax_details, vouchers, adv_vouchers):
conditions = []
conditions.append(ple.amount.lt(0))
conditions.append(ple.delinked == 0)
conditions.append(ple.party_type == "Customer")
conditions.append(ple.party.isin(parties))
conditions.append(ple.voucher_no == ple.against_voucher_no)
conditions.append(ple.company == inv.company)
@@ -555,6 +557,7 @@ def get_tcs_amount(parties, inv, tax_details, vouchers, adv_vouchers):
{
"is_cancelled": 0,
"credit": [">", 0],
"party_type": "Customer",
"party": ["in", parties],
"posting_date": ["between", (tax_details.from_date, tax_details.to_date)],
"company": inv.company,

View File

@@ -8,6 +8,7 @@ from frappe.utils import today
from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase):
@@ -49,6 +50,16 @@ class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase):
)
return pe
def create_sales_order(self):
so = make_sales_order(
company=self.company,
customer=self.customer,
item=self.item,
rate=100,
transaction_date=today(),
)
return so
def test_01_unreconcile_invoice(self):
si1 = self.create_sales_invoice()
si2 = self.create_sales_invoice()
@@ -314,3 +325,41 @@ class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase):
),
1,
)
def test_05_unreconcile_order(self):
so = self.create_sales_order()
pe = self.create_payment_entry()
# Allocation payment against Sales Order
pe.paid_amount = 100
pe.append(
"references",
{"reference_doctype": so.doctype, "reference_name": so.name, "allocated_amount": 100},
)
pe.save().submit()
# Assert 'Advance Paid'
so.reload()
self.assertEqual(so.advance_paid, 100)
unreconcile = frappe.get_doc(
{
"doctype": "Unreconcile Payment",
"company": self.company,
"voucher_type": pe.doctype,
"voucher_no": pe.name,
}
)
unreconcile.add_references()
self.assertEqual(len(unreconcile.allocations), 1)
allocations = [x.reference_name for x in unreconcile.allocations]
self.assertEquals([so.name], allocations)
# unreconcile so
unreconcile.save().submit()
# Assert 'Advance Paid'
so.reload()
pe.reload()
self.assertEqual(so.advance_paid, 0)
self.assertEqual(len(pe.references), 0)
self.assertEqual(pe.unallocated_amount, 100)

View File

@@ -63,6 +63,9 @@ class UnreconcilePayment(Document):
update_voucher_outstanding(
alloc.reference_doctype, alloc.reference_name, alloc.account, alloc.party_type, alloc.party
)
if doc.doctype in frappe.get_hooks("advance_payment_doctypes"):
doc.set_total_advance_paid()
frappe.db.set_value("Unreconcile Payment Entries", alloc.name, "unlinked", True)

View File

@@ -13,9 +13,13 @@ import erpnext
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_accounting_dimensions,
)
from erpnext.accounts.doctype.accounting_dimension_filter.accounting_dimension_filter import (
get_dimension_filter_map,
)
from erpnext.accounts.doctype.accounting_period.accounting_period import ClosedAccountingPeriod
from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget
from erpnext.accounts.utils import create_payment_ledger_entry
from erpnext.exceptions import InvalidAccountDimensionError, MandatoryAccountDimensionError
def make_gl_entries(
@@ -354,6 +358,7 @@ def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False):
process_debit_credit_difference(gl_map)
dimension_filter_map = get_dimension_filter_map()
if gl_map:
check_freezing_date(gl_map[0]["posting_date"], adv_adj)
is_opening = any(d.get("is_opening") == "Yes" for d in gl_map)
@@ -361,6 +366,7 @@ def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False):
validate_against_pcv(is_opening, gl_map[0]["posting_date"], gl_map[0]["company"])
for entry in gl_map:
validate_allowed_dimensions(entry, dimension_filter_map)
make_entry(entry, adv_adj, update_outstanding, from_repost)
@@ -672,3 +678,39 @@ def set_as_cancel(voucher_type, voucher_no):
where voucher_type=%s and voucher_no=%s and is_cancelled = 0""",
(now(), frappe.session.user, voucher_type, voucher_no),
)
def validate_allowed_dimensions(gl_entry, dimension_filter_map):
for key, value in dimension_filter_map.items():
dimension = key[0]
account = key[1]
if gl_entry.account == account:
if value["is_mandatory"] and not gl_entry.get(dimension):
frappe.throw(
_("{0} is mandatory for account {1}").format(
frappe.bold(frappe.unscrub(dimension)), frappe.bold(gl_entry.account)
),
MandatoryAccountDimensionError,
)
if value["allow_or_restrict"] == "Allow":
if gl_entry.get(dimension) and gl_entry.get(dimension) not in value["allowed_dimensions"]:
frappe.throw(
_("Invalid value {0} for {1} against account {2}").format(
frappe.bold(gl_entry.get(dimension)),
frappe.bold(frappe.unscrub(dimension)),
frappe.bold(gl_entry.account),
),
InvalidAccountDimensionError,
)
else:
if gl_entry.get(dimension) and gl_entry.get(dimension) in value["allowed_dimensions"]:
frappe.throw(
_("Invalid value {0} for {1} against account {2}").format(
frappe.bold(gl_entry.get(dimension)),
frappe.bold(frappe.unscrub(dimension)),
frappe.bold(gl_entry.account),
),
InvalidAccountDimensionError,
)

View File

@@ -9,7 +9,7 @@ from frappe import _, msgprint, qb, scrub
from frappe.contacts.doctype.address.address import get_company_address, get_default_address
from frappe.core.doctype.user_permission.user_permission import get_permitted_documents
from frappe.model.utils import get_fetch_values
from frappe.query_builder.functions import Abs, Date, Sum
from frappe.query_builder.functions import Abs, Count, Date, Sum
from frappe.utils import (
add_days,
add_months,
@@ -114,14 +114,12 @@ def _get_party_details(
set_account_and_due_date(party, account, party_type, company, posting_date, bill_date, doctype)
)
party = party_details[party_type.lower()]
if not ignore_permissions and not (
frappe.has_permission(party_type, "read", party)
or frappe.has_permission(party_type, "select", party)
):
frappe.throw(_("Not permitted for {0}").format(party), frappe.PermissionError)
party = frappe.get_doc(party_type, party)
if not ignore_permissions:
ptype = "select" if frappe.only_has_select_perm(party_type) else "read"
frappe.has_permission(party_type, ptype, party, throw=True)
currency = party.get("default_currency") or currency or get_company_currency(company)
party_address, shipping_address = set_address_details(
@@ -763,34 +761,37 @@ def get_timeline_data(doctype, name):
from frappe.desk.form.load import get_communication_data
out = {}
fields = "creation, count(*)"
after = add_years(None, -1).strftime("%Y-%m-%d")
group_by = "group by Date(creation)"
data = get_communication_data(
doctype,
name,
after=after,
group_by="group by creation",
fields="C.creation as creation, count(C.name)",
group_by="group by communication_date",
fields="C.communication_date as communication_date, count(C.name)",
as_dict=False,
)
# fetch and append data from Activity Log
data += frappe.db.sql(
"""select {fields}
from `tabActivity Log`
where (reference_doctype=%(doctype)s and reference_name=%(name)s)
or (timeline_doctype in (%(doctype)s) and timeline_name=%(name)s)
or (reference_doctype in ("Quotation", "Opportunity") and timeline_name=%(name)s)
and status!='Success' and creation > {after}
{group_by} order by creation desc
""".format(
fields=fields, group_by=group_by, after=after
),
{"doctype": doctype, "name": name},
as_dict=False,
)
activity_log = frappe.qb.DocType("Activity Log")
data += (
frappe.qb.from_(activity_log)
.select(activity_log.communication_date, Count(activity_log.name))
.where(
(
((activity_log.reference_doctype == doctype) & (activity_log.reference_name == name))
| ((activity_log.timeline_doctype == doctype) & (activity_log.timeline_name == name))
| (
(activity_log.reference_doctype.isin(["Quotation", "Opportunity"]))
& (activity_log.timeline_name == name)
)
)
& (activity_log.status != "Success")
& (activity_log.creation > after)
)
.groupby(activity_log.communication_date)
.orderby(activity_log.communication_date, order=frappe.qb.desc)
).run()
timeline_items = dict(data)

View File

@@ -149,11 +149,16 @@ frappe.query_reports["Accounts Payable"] = {
"label": __("Revaluation Journals"),
"fieldtype": "Check",
},
{
"fieldname": "in_party_currency",
"label": __("In Party Currency"),
"fieldtype": "Check",
},
{
"fieldname": "ignore_accounts",
"label": __("Group by Voucher"),
"fieldtype": "Check",
}
},
],

View File

@@ -40,6 +40,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
"range2": 60,
"range3": 90,
"range4": 120,
"in_party_currency": 1,
}
data = execute(filters)

View File

@@ -10,10 +10,8 @@
<h2 class="text-center" style="margin-top:0">{%= __(report.report_name) %}</h2>
<h4 class="text-center">
{% if (filters.customer_name) { %}
{%= filters.customer_name %}
{% } else { %}
{%= filters.customer || filters.supplier %}
{% if (filters.party) { %}
{%= __(filters.party) %}
{% } %}
</h4>
<h6 class="text-center">
@@ -141,7 +139,7 @@
<th style="width: 24%">{%= __("Reference") %}</th>
{% } %}
{% if(!filters.show_future_payments) { %}
<th style="width: 20%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
<th style="width: 20%">{%= (filters.party) ? __("Remarks"): __("Party") %}</th>
{% } %}
<th style="width: 10%; text-align: right">{%= __("Invoiced Amount") %}</th>
{% if(!filters.show_future_payments) { %}
@@ -158,7 +156,7 @@
<th style="width: 10%">{%= __("Remaining Balance") %}</th>
{% } %}
{% } else { %}
<th style="width: 40%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
<th style="width: 40%">{%= (filters.party) ? __("Remarks"): __("Party") %}</th>
<th style="width: 15%">{%= __("Total Invoiced Amount") %}</th>
<th style="width: 15%">{%= __("Total Paid Amount") %}</th>
<th style="width: 15%">{%= report.report_name === "Accounts Receivable Summary" ? __('Credit Note Amount') : __('Debit Note Amount') %}</th>
@@ -187,7 +185,7 @@
{% if(!filters.show_future_payments) { %}
<td>
{% if(!(filters.customer || filters.supplier)) { %}
{% if(!(filters.party)) { %}
{%= data[i]["party"] %}
{% if(data[i]["customer_name"] && data[i]["customer_name"] != data[i]["party"]) { %}
<br> {%= data[i]["customer_name"] %}
@@ -260,7 +258,7 @@
{% if(data[i]["party"]|| "&nbsp;") { %}
{% if(!data[i]["is_total_row"]) { %}
<td>
{% if(!(filters.customer || filters.supplier)) { %}
{% if(!(filters.party)) { %}
{%= data[i]["party"] %}
{% if(data[i]["customer_name"] && data[i]["customer_name"] != data[i]["party"]) { %}
<br> {%= data[i]["customer_name"] %}

View File

@@ -181,13 +181,17 @@ frappe.query_reports["Accounts Receivable"] = {
"label": __("Revaluation Journals"),
"fieldtype": "Check",
},
{
"fieldname": "in_party_currency",
"label": __("In Party Currency"),
"fieldtype": "Check",
},
{
"fieldname": "ignore_accounts",
"label": __("Group by Voucher"),
"fieldtype": "Check",
}
],
"formatter": function(value, row, column, data, default_formatter) {

View File

@@ -5,7 +5,7 @@
from collections import OrderedDict
import frappe
from frappe import _, qb, scrub
from frappe import _, qb, query_builder, scrub
from frappe.query_builder import Criterion
from frappe.query_builder.functions import Date, Substring, Sum
from frappe.utils import cint, cstr, flt, getdate, nowdate
@@ -28,8 +28,8 @@ from erpnext.accounts.utils import get_currency_precision
# 6. Configurable Ageing Groups (0-30, 30-60 etc) can be set via filters
# 7. For overpayment against an invoice with payment terms, there will be an additional row
# 8. Invoice details like Sales Persons, Delivery Notes are also fetched comma separated
# 9. Report amounts are in "Party Currency" if party is selected, or company currency for multi-party
# 10. This reports is based on all GL Entries that are made against account_type "Receivable" or "Payable"
# 9. Report amounts are in party currency if in_party_currency is selected, otherwise company currency
# 10. This report is based on Payment Ledger Entries
def execute(filters=None):
@@ -84,6 +84,12 @@ class ReceivablePayableReport(object):
self.total_row_map = {}
self.skip_total_row = 1
if self.filters.get("in_party_currency"):
if self.filters.get("party") and len(self.filters.get("party")) == 1:
self.skip_total_row = 0
else:
self.skip_total_row = 1
def get_data(self):
self.get_ple_entries()
self.get_sales_invoices_or_customers_based_on_sales_person()
@@ -145,7 +151,7 @@ class ReceivablePayableReport(object):
if self.filters.get("group_by_party"):
self.init_subtotal_row(ple.party)
if self.filters.get("group_by_party"):
if self.filters.get("group_by_party") and not self.filters.get("in_party_currency"):
self.init_subtotal_row("Total")
def get_invoices(self, ple):
@@ -224,8 +230,7 @@ class ReceivablePayableReport(object):
if not row:
return
# amount in "Party Currency", if its supplied. If not, amount in company currency
if self.filters.get("party_type") and self.filters.get("party"):
if self.filters.get("in_party_currency") or self.filters.get("party_account"):
amount = ple.amount_in_account_currency
else:
amount = ple.amount
@@ -260,8 +265,10 @@ class ReceivablePayableReport(object):
def update_sub_total_row(self, row, party):
total_row = self.total_row_map.get(party)
for field in self.get_currency_fields():
total_row[field] += row.get(field, 0.0)
if total_row:
for field in self.get_currency_fields():
total_row[field] += row.get(field, 0.0)
total_row["currency"] = row.get("currency", "")
def append_subtotal_row(self, party):
sub_total_row = self.total_row_map.get(party)
@@ -322,7 +329,7 @@ class ReceivablePayableReport(object):
if self.filters.get("group_by_party"):
self.append_subtotal_row(self.previous_party)
if self.data:
self.data.append(self.total_row_map.get("Total"))
self.data.append(self.total_row_map.get("Total", {}))
def append_row(self, row):
self.allocate_future_payments(row)
@@ -453,7 +460,7 @@ class ReceivablePayableReport(object):
party_details = self.get_party_details(row.party) or {}
row.update(party_details)
if self.filters.get("party_type") and self.filters.get("party"):
if self.filters.get("in_party_currency") or self.filters.get("party_account"):
row.currency = row.account_currency
else:
row.currency = self.company_currency
@@ -574,6 +581,8 @@ class ReceivablePayableReport(object):
def get_future_payments_from_payment_entry(self):
pe = frappe.qb.DocType("Payment Entry")
pe_ref = frappe.qb.DocType("Payment Entry Reference")
ifelse = query_builder.CustomFunction("IF", ["condition", "then", "else"])
return (
frappe.qb.from_(pe)
.inner_join(pe_ref)
@@ -585,6 +594,11 @@ class ReceivablePayableReport(object):
(pe.posting_date).as_("future_date"),
(pe_ref.allocated_amount).as_("future_amount"),
(pe.reference_no).as_("future_ref"),
ifelse(
pe.payment_type == "Receive",
pe.source_exchange_rate * pe_ref.allocated_amount,
pe.target_exchange_rate * pe_ref.allocated_amount,
).as_("future_amount_in_base_currency"),
)
.where(
(pe.docstatus < 2)
@@ -621,13 +635,24 @@ class ReceivablePayableReport(object):
query = query.select(
Sum(jea.debit_in_account_currency - jea.credit_in_account_currency).as_("future_amount")
)
query = query.select(Sum(jea.debit - jea.credit).as_("future_amount_in_base_currency"))
else:
query = query.select(
Sum(jea.credit_in_account_currency - jea.debit_in_account_currency).as_("future_amount")
)
query = query.select(Sum(jea.credit - jea.debit).as_("future_amount_in_base_currency"))
else:
query = query.select(
Sum(jea.debit if self.account_type == "Payable" else jea.credit).as_("future_amount")
Sum(jea.debit if self.account_type == "Payable" else jea.credit).as_(
"future_amount_in_base_currency"
)
)
query = query.select(
Sum(
jea.debit_in_account_currency
if self.account_type == "Payable"
else jea.credit_in_account_currency
).as_("future_amount")
)
query = query.having(qb.Field("future_amount") > 0)
@@ -643,14 +668,19 @@ class ReceivablePayableReport(object):
row.remaining_balance = row.outstanding
row.future_amount = 0.0
for future in self.future_payments.get((row.voucher_no, row.party), []):
if row.remaining_balance > 0 and future.future_amount:
if future.future_amount > row.outstanding:
if self.filters.in_party_currency:
future_amount_field = "future_amount"
else:
future_amount_field = "future_amount_in_base_currency"
if row.remaining_balance > 0 and future.get(future_amount_field):
if future.get(future_amount_field) > row.outstanding:
row.future_amount = row.outstanding
future.future_amount = future.future_amount - row.outstanding
future[future_amount_field] = future.get(future_amount_field) - row.outstanding
row.remaining_balance = 0
else:
row.future_amount += future.future_amount
future.future_amount = 0
row.future_amount += future.get(future_amount_field)
future[future_amount_field] = 0
row.remaining_balance = row.outstanding - row.future_amount
row.setdefault("future_ref", []).append(

View File

@@ -579,7 +579,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
filters.update({"party_account": self.debtors_usd})
report = execute(filters)[1]
self.assertEqual(len(report), 1)
expected_data = [8000.0, 8000.0, self.debtors_usd, si2.currency]
expected_data = [100.0, 100.0, self.debtors_usd, si2.currency]
row = report[0]
self.assertEqual(
expected_data, [row.invoiced, row.outstanding, row.party_account, row.account_currency]
@@ -616,6 +616,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
"range2": 60,
"range3": 90,
"range4": 120,
"in_party_currency": 1,
}
si = self.create_sales_invoice(no_payment_schedule=True, do_not_submit=True)
@@ -771,3 +772,92 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
# post sorting output should be [[Additional Debtors, ...], [Debtors, ...]]
report_output = sorted(report_output, key=lambda x: x[0])
self.assertEqual(expected_data, report_output)
def test_future_payments_on_foreign_currency(self):
self.customer2 = (
frappe.get_doc(
{
"doctype": "Customer",
"customer_name": "Jane Doe",
"type": "Individual",
"default_currency": "USD",
}
)
.insert()
.submit()
)
si = self.create_sales_invoice(do_not_submit=True)
si.posting_date = add_days(today(), -1)
si.customer = self.customer2
si.currency = "USD"
si.conversion_rate = 80
si.debit_to = self.debtors_usd
si.save().submit()
# full payment in USD
pe = get_payment_entry(si.doctype, si.name)
pe.posting_date = add_days(today(), 1)
pe.base_received_amount = 7500
pe.received_amount = 7500
pe.source_exchange_rate = 75
pe.save().submit()
filters = frappe._dict(
{
"company": self.company,
"report_date": today(),
"range1": 30,
"range2": 60,
"range3": 90,
"range4": 120,
"show_future_payments": True,
"in_party_currency": False,
}
)
report = execute(filters)[1]
self.assertEqual(len(report), 1)
expected_data = [8000.0, 8000.0, 500.0, 7500.0]
row = report[0]
self.assertEqual(
expected_data, [row.invoiced, row.outstanding, row.remaining_balance, row.future_amount]
)
filters.in_party_currency = True
report = execute(filters)[1]
self.assertEqual(len(report), 1)
expected_data = [100.0, 100.0, 0.0, 100.0]
row = report[0]
self.assertEqual(
expected_data, [row.invoiced, row.outstanding, row.remaining_balance, row.future_amount]
)
pe.cancel()
# partial payment in USD on a future date
pe = get_payment_entry(si.doctype, si.name)
pe.posting_date = add_days(today(), 1)
pe.base_received_amount = 6750
pe.received_amount = 6750
pe.source_exchange_rate = 75
pe.paid_amount = 90 # in USD
pe.references[0].allocated_amount = 90
pe.save().submit()
filters.in_party_currency = False
report = execute(filters)[1]
self.assertEqual(len(report), 1)
expected_data = [8000.0, 8000.0, 1250.0, 6750.0]
row = report[0]
self.assertEqual(
expected_data, [row.invoiced, row.outstanding, row.remaining_balance, row.future_amount]
)
filters.in_party_currency = True
report = execute(filters)[1]
self.assertEqual(len(report), 1)
expected_data = [100.0, 100.0, 10.0, 90.0]
row = report[0]
self.assertEqual(
expected_data, [row.invoiced, row.outstanding, row.remaining_balance, row.future_amount]
)

View File

@@ -6,6 +6,19 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
erpnext.utils.add_dimensions('Balance Sheet', 10);
frappe.query_reports["Balance Sheet"]["filters"].push(
{
"fieldname": "selected_view",
"label": __("Select View"),
"fieldtype": "Select",
"options": [
{ "value": "Report", "label": __("Report View") },
{ "value": "Growth", "label": __("Growth View") }
],
"default": "Report",
"reqd": 1
},
);
frappe.query_reports["Balance Sheet"]["filters"].push({
"fieldname": "accumulated_values",
"label": __("Accumulated Values"),

View File

@@ -2,7 +2,44 @@
// License: GNU General Public License v3. See license.txt
frappe.query_reports["Budget Variance Report"] = {
"filters": [
"filters": get_filters(),
"formatter": function (value, row, column, data, default_formatter) {
value = default_formatter(value, row, column, data);
if (column.fieldname.includes(__("variance"))) {
if (data[column.fieldname] < 0) {
value = "<span style='color:red'>" + value + "</span>";
}
else if (data[column.fieldname] > 0) {
value = "<span style='color:green'>" + value + "</span>";
}
}
return value;
}
}
function get_filters() {
function get_dimensions() {
let result = [];
frappe.call({
method: "erpnext.accounts.doctype.accounting_dimension.accounting_dimension.get_dimensions",
args: {
'with_cost_center_and_project': true
},
async: false,
callback: function(r) {
if(!r.exc) {
result = r.message[0].map(elem => elem.document_type);
}
}
});
return result;
}
let budget_against_options = get_dimensions();
let filters = [
{
fieldname: "from_fiscal_year",
label: __("From Fiscal Year"),
@@ -44,7 +81,7 @@ frappe.query_reports["Budget Variance Report"] = {
fieldname: "budget_against",
label: __("Budget Against"),
fieldtype: "Select",
options: ["Cost Center", "Project"],
options: budget_against_options,
default: "Cost Center",
reqd: 1,
on_change: function() {
@@ -71,24 +108,8 @@ frappe.query_reports["Budget Variance Report"] = {
fieldtype: "Check",
default: 0,
},
],
"formatter": function (value, row, column, data, default_formatter) {
value = default_formatter(value, row, column, data);
]
if (column.fieldname.includes(__("variance"))) {
if (data[column.fieldname] < 0) {
value = "<span style='color:red'>" + value + "</span>";
}
else if (data[column.fieldname] > 0) {
value = "<span style='color:green'>" + value + "</span>";
}
}
return value;
}
return filters;
}
erpnext.dimension_filters.forEach((dimension) => {
frappe.query_reports["Budget Variance Report"].filters[4].options.push(dimension["document_type"]);
});

View File

@@ -129,7 +129,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
}
value = default_formatter(value, row, column, data);
if (!data.parent_account) {
if (data && !data.parent_account) {
value = $(`<span>${value}</span>`);
var $value = $(value).css("font-weight", "bold");

View File

@@ -3,7 +3,7 @@
import frappe
from frappe import _, scrub
from frappe import _, qb, scrub
from frappe.utils import getdate, nowdate
@@ -38,7 +38,6 @@ class PartyLedgerSummaryReport(object):
"""
Additional Columns for 'User Permission' based access control
"""
from frappe import qb
if self.filters.party_type == "Customer":
self.territories = frappe._dict({})
@@ -365,13 +364,33 @@ class PartyLedgerSummaryReport(object):
def get_party_adjustment_amounts(self):
conditions = self.prepare_conditions()
income_or_expense = (
"Expense Account" if self.filters.party_type == "Customer" else "Income Account"
account_type = "Expense Account" if self.filters.party_type == "Customer" else "Income Account"
income_or_expense_accounts = frappe.db.get_all(
"Account", filters={"account_type": account_type, "company": self.filters.company}, pluck="name"
)
invoice_dr_or_cr = "debit" if self.filters.party_type == "Customer" else "credit"
reverse_dr_or_cr = "credit" if self.filters.party_type == "Customer" else "debit"
round_off_account = frappe.get_cached_value("Company", self.filters.company, "round_off_account")
gl = qb.DocType("GL Entry")
if not income_or_expense_accounts:
# prevent empty 'in' condition
income_or_expense_accounts.append("")
else:
# escape '%' in account name
# ignoring frappe.db.escape as it replaces single quotes with double quotes
income_or_expense_accounts = [x.replace("%", "%%") for x in income_or_expense_accounts]
accounts_query = (
qb.from_(gl)
.select(gl.voucher_type, gl.voucher_no)
.where(
(gl.account.isin(income_or_expense_accounts))
& (gl.posting_date.gte(self.filters.from_date))
& (gl.posting_date.lte(self.filters.to_date))
)
)
gl_entries = frappe.db.sql(
"""
select
@@ -381,16 +400,15 @@ class PartyLedgerSummaryReport(object):
where
docstatus < 2 and is_cancelled = 0
and (voucher_type, voucher_no) in (
select voucher_type, voucher_no from `tabGL Entry` gle, `tabAccount` acc
where acc.name = gle.account and acc.account_type = '{income_or_expense}'
and gle.posting_date between %(from_date)s and %(to_date)s and gle.docstatus < 2
{accounts_query}
) and (voucher_type, voucher_no) in (
select voucher_type, voucher_no from `tabGL Entry` gle
where gle.party_type=%(party_type)s and ifnull(party, '') != ''
and gle.posting_date between %(from_date)s and %(to_date)s and gle.docstatus < 2 {conditions}
)
""".format(
conditions=conditions, income_or_expense=income_or_expense
""".format(
accounts_query=accounts_query,
conditions=conditions,
),
self.filters,
as_dict=True,
@@ -414,7 +432,7 @@ class PartyLedgerSummaryReport(object):
elif gle.party:
parties.setdefault(gle.party, 0)
parties[gle.party] += gle.get(reverse_dr_or_cr) - gle.get(invoice_dr_or_cr)
elif frappe.get_cached_value("Account", gle.account, "account_type") == income_or_expense:
elif frappe.get_cached_value("Account", gle.account, "account_type") == account_type:
accounts.setdefault(gle.account, 0)
accounts[gle.account] += gle.get(invoice_dr_or_cr) - gle.get(reverse_dr_or_cr)
else:

View File

@@ -8,17 +8,7 @@ import re
import frappe
from frappe import _
from frappe.utils import (
add_days,
add_months,
cint,
cstr,
flt,
formatdate,
get_first_day,
getdate,
today,
)
from frappe.utils import add_days, add_months, cint, cstr, flt, formatdate, get_first_day, getdate
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_accounting_dimensions,
@@ -53,8 +43,6 @@ def get_period_list(
year_start_date = getdate(period_start_date)
year_end_date = getdate(period_end_date)
year_end_date = getdate(today()) if year_end_date > getdate(today()) else year_end_date
months_to_add = {"Yearly": 12, "Half-Yearly": 6, "Quarterly": 3, "Monthly": 1}[periodicity]
period_list = []

View File

@@ -193,8 +193,14 @@ frappe.query_reports["General Ledger"] = {
"fieldname": "show_remarks",
"label": __("Show Remarks"),
"fieldtype": "Check"
},
{
"fieldname": "ignore_err",
"label": __("Ignore Exchange Rate Revaluation Journals"),
"fieldtype": "Check"
}
]
}

View File

@@ -231,6 +231,19 @@ def get_conditions(filters):
if filters.get("voucher_no"):
conditions.append("voucher_no=%(voucher_no)s")
if filters.get("ignore_err"):
err_journals = frappe.db.get_all(
"Journal Entry",
filters={
"company": filters.get("company"),
"docstatus": 1,
"voucher_type": ("in", ["Exchange Rate Revaluation", "Exchange Gain Or Loss"]),
},
as_list=True,
)
if err_journals:
filters.update({"voucher_no_not_in": [x[0] for x in err_journals]})
if filters.get("voucher_no_not_in"):
conditions.append("voucher_no not in %(voucher_no_not_in)s")

View File

@@ -3,7 +3,7 @@
import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.utils import today
from frappe.utils import flt, today
from erpnext.accounts.report.general_ledger.general_ledger import execute
@@ -148,3 +148,105 @@ class TestGeneralLedger(FrappeTestCase):
self.assertEqual(data[2]["credit"], 900)
self.assertEqual(data[3]["debit"], 100)
self.assertEqual(data[3]["credit"], 100)
def test_ignore_exchange_rate_journals_filter(self):
# create a new account with USD currency
account_name = "Test Debtors USD"
company = "_Test Company"
account = frappe.get_doc(
{
"account_name": account_name,
"is_group": 0,
"company": company,
"root_type": "Asset",
"report_type": "Balance Sheet",
"account_currency": "USD",
"parent_account": "Accounts Receivable - _TC",
"account_type": "Receivable",
"doctype": "Account",
}
)
account.insert(ignore_if_duplicate=True)
# create a JV to debit 1000 USD at 75 exchange rate
jv = frappe.new_doc("Journal Entry")
jv.posting_date = today()
jv.company = company
jv.multi_currency = 1
jv.cost_center = "_Test Cost Center - _TC"
jv.set(
"accounts",
[
{
"account": account.name,
"party_type": "Customer",
"party": "_Test Customer USD",
"debit_in_account_currency": 1000,
"credit_in_account_currency": 0,
"exchange_rate": 75,
"cost_center": "_Test Cost Center - _TC",
},
{
"account": "Cash - _TC",
"debit_in_account_currency": 0,
"credit_in_account_currency": 75000,
"cost_center": "_Test Cost Center - _TC",
},
],
)
jv.save()
jv.submit()
revaluation = frappe.new_doc("Exchange Rate Revaluation")
revaluation.posting_date = today()
revaluation.company = company
accounts = revaluation.get_accounts_data()
revaluation.extend("accounts", accounts)
row = revaluation.accounts[0]
row.new_exchange_rate = 83
row.new_balance_in_base_currency = flt(
row.new_exchange_rate * flt(row.balance_in_account_currency)
)
row.gain_loss = row.new_balance_in_base_currency - flt(row.balance_in_base_currency)
revaluation.set_total_gain_loss()
revaluation = revaluation.save().submit()
# post journal entry for Revaluation doc
frappe.db.set_value(
"Company", company, "unrealized_exchange_gain_loss_account", "_Test Exchange Gain/Loss - _TC"
)
revaluation_jv = revaluation.make_jv_for_revaluation()
revaluation_jv.cost_center = "_Test Cost Center - _TC"
for acc in revaluation_jv.get("accounts"):
acc.cost_center = "_Test Cost Center - _TC"
revaluation_jv.save()
revaluation_jv.submit()
# With ignore_err enabled
columns, data = execute(
frappe._dict(
{
"company": company,
"from_date": today(),
"to_date": today(),
"account": [account.name],
"group_by": "Group by Voucher (Consolidated)",
"ignore_err": True,
}
)
)
self.assertNotIn(revaluation_jv.name, set([x.voucher_no for x in data]))
# Without ignore_err enabled
columns, data = execute(
frappe._dict(
{
"company": company,
"from_date": today(),
"to_date": today(),
"account": [account.name],
"group_by": "Group by Voucher (Consolidated)",
"ignore_err": False,
}
)
)
self.assertIn(revaluation_jv.name, set([x.voucher_no for x in data]))

View File

@@ -134,7 +134,7 @@ def get_revenue(data, period_list, include_in_gross=1):
def remove_parent_with_no_child(data):
data_to_be_removed = False
for parent in data:
for parent in list(data):
if "is_group" in parent and parent.get("is_group") == 1:
have_child = False
for child in data:

View File

@@ -309,7 +309,8 @@ def get_conditions(filters):
def get_items(filters, additional_query_columns):
conditions = get_conditions(filters)
if additional_query_columns:
additional_query_columns = "," + ",".join(additional_query_columns)
return frappe.db.sql(
"""
select
@@ -318,7 +319,8 @@ def get_items(filters, additional_query_columns):
`tabPurchase Invoice`.supplier, `tabPurchase Invoice`.remarks, `tabPurchase Invoice`.base_net_total,
`tabPurchase Invoice`.unrealized_profit_loss_account,
`tabPurchase Invoice Item`.`item_code`, `tabPurchase Invoice Item`.description,
`tabPurchase Invoice Item`.`item_name` as pi_item_name, `tabPurchase Invoice Item`.`item_group` as pi_item_group,
`tabPurchase Invoice Item`.`item_name` as pi_item_name, `tabPurchase Invoice Item`.`item_group`
,`tabPurchase Invoice Item`.`item_group` as pi_item_group,
`tabItem`.`item_name` as i_item_name, `tabItem`.`item_group` as i_item_group,
`tabPurchase Invoice Item`.`project`, `tabPurchase Invoice Item`.`purchase_order`,
`tabPurchase Invoice Item`.`purchase_receipt`, `tabPurchase Invoice Item`.`po_detail`,

View File

@@ -350,7 +350,13 @@ def get_conditions(filters, additional_conditions=None):
and ifnull(`tabSales Invoice Payment`.mode_of_payment, '') = %(mode_of_payment)s)"""
if filters.get("warehouse"):
conditions += """and ifnull(`tabSales Invoice Item`.warehouse, '') = %(warehouse)s"""
if frappe.db.get_value("Warehouse", filters.get("warehouse"), "is_group"):
lft, rgt = frappe.db.get_all(
"Warehouse", filters={"name": filters.get("warehouse")}, fields=["lft", "rgt"], as_list=True
)[0]
conditions += f"and ifnull(`tabSales Invoice Item`.warehouse, '') in (select name from `tabWarehouse` where lft > {lft} and rgt < {rgt}) "
else:
conditions += """and ifnull(`tabSales Invoice Item`.warehouse, '') = %(warehouse)s"""
if filters.get("brand"):
conditions += """and ifnull(`tabSales Invoice Item`.brand, '') = %(brand)s"""
@@ -381,7 +387,8 @@ def get_group_by_conditions(filters, doctype):
def get_items(filters, additional_query_columns, additional_conditions=None):
conditions = get_conditions(filters, additional_conditions)
if additional_query_columns:
additional_query_columns = "," + ",".join(additional_query_columns)
return frappe.db.sql(
"""
select

View File

@@ -163,7 +163,7 @@ def get_entries(filters):
"""select
voucher_type, voucher_no, party_type, party, posting_date, debit, credit, remarks, against_voucher
from `tabGL Entry`
where company=%(company)s and voucher_type in ('Journal Entry', 'Payment Entry') {0}
where company=%(company)s and voucher_type in ('Journal Entry', 'Payment Entry') and is_cancelled = 0 {0}
""".format(
get_conditions(filters)
),

View File

@@ -8,6 +8,21 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
erpnext.utils.add_dimensions('Profit and Loss Statement', 10);
frappe.query_reports["Profit and Loss Statement"]["filters"].push(
{
"fieldname": "selected_view",
"label": __("Select View"),
"fieldtype": "Select",
"options": [
{ "value": "Report", "label": __("Report View") },
{ "value": "Growth", "label": __("Growth View") },
{ "value": "Margin", "label": __("Margin View") },
],
"default": "Report",
"reqd": 1
},
);
frappe.query_reports["Profit and Loss Statement"]["filters"].push(
{
"fieldname": "include_default_book_entries",

View File

@@ -105,7 +105,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
"to_fiscal_year": data.fiscal_year
};
if(data.based_on == 'cost_center'){
if(data.based_on == 'Cost Center'){
frappe.route_options["cost_center"] = data.account
} else {
frappe.route_options["project"] = data.account
@@ -119,8 +119,4 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
"initial_depth": 3
}
erpnext.dimension_filters.forEach((dimension) => {
frappe.query_reports["Profitability Analysis"].filters[1].options.push(dimension["document_type"]);
});
});

View File

@@ -52,6 +52,12 @@ frappe.query_reports["Purchase Register"] = {
"label": __("Item Group"),
"fieldtype": "Link",
"options": "Item Group"
},
{
"fieldname": "include_payments",
"label": __("Show Ledger View"),
"fieldtype": "Check",
"default": 0
}
]
}

View File

@@ -4,13 +4,22 @@
import frappe
from frappe import _, msgprint
from frappe.utils import flt
from frappe.query_builder.custom import ConstantColumn
from frappe.utils import flt, getdate
from pypika import Order
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_accounting_dimensions,
get_dimension_with_children,
from erpnext.accounts.party import get_party_account
from erpnext.accounts.report.utils import (
apply_common_conditions,
get_advance_taxes_and_charges,
get_journal_entries,
get_opening_row,
get_party_details,
get_payment_entries,
get_query_columns,
get_taxes_query,
get_values_for_columns,
)
from erpnext.accounts.report.utils import get_query_columns, get_values_for_columns
def execute(filters=None):
@@ -21,9 +30,15 @@ def _execute(filters=None, additional_table_columns=None):
if not filters:
filters = {}
include_payments = filters.get("include_payments")
if filters.get("include_payments") and not filters.get("supplier"):
frappe.throw(_("Please select a supplier for fetching payments."))
invoice_list = get_invoices(filters, get_query_columns(additional_table_columns))
if filters.get("include_payments"):
invoice_list += get_payments(filters)
columns, expense_accounts, tax_accounts, unrealized_profit_loss_accounts = get_columns(
invoice_list, additional_table_columns
invoice_list, additional_table_columns, include_payments
)
if not invoice_list:
@@ -33,14 +48,28 @@ def _execute(filters=None, additional_table_columns=None):
invoice_expense_map = get_invoice_expense_map(invoice_list)
internal_invoice_map = get_internal_invoice_map(invoice_list)
invoice_expense_map, invoice_tax_map = get_invoice_tax_map(
invoice_list, invoice_expense_map, expense_accounts
invoice_list, invoice_expense_map, expense_accounts, include_payments
)
invoice_po_pr_map = get_invoice_po_pr_map(invoice_list)
suppliers = list(set(d.supplier for d in invoice_list))
supplier_details = get_supplier_details(suppliers)
supplier_details = get_party_details("Supplier", suppliers)
company_currency = frappe.get_cached_value("Company", filters.company, "default_currency")
res = []
if include_payments:
opening_row = get_opening_row(
"Supplier", filters.supplier, getdate(filters.from_date), filters.company
)[0]
res.append(
{
"payable_account": opening_row.account,
"debit": flt(opening_row.debit),
"credit": flt(opening_row.credit),
"balance": flt(opening_row.balance),
}
)
data = []
for inv in invoice_list:
# invoice details
@@ -48,24 +77,25 @@ def _execute(filters=None, additional_table_columns=None):
purchase_receipt = list(set(invoice_po_pr_map.get(inv.name, {}).get("purchase_receipt", [])))
project = list(set(invoice_po_pr_map.get(inv.name, {}).get("project", [])))
row = [
inv.name,
inv.posting_date,
inv.supplier,
inv.supplier_name,
*get_values_for_columns(additional_table_columns, inv).values(),
supplier_details.get(inv.supplier), # supplier_group
inv.tax_id,
inv.credit_to,
inv.mode_of_payment,
", ".join(project),
inv.bill_no,
inv.bill_date,
inv.remarks,
", ".join(purchase_order),
", ".join(purchase_receipt),
company_currency,
]
row = {
"voucher_type": inv.doctype,
"voucher_no": inv.name,
"posting_date": inv.posting_date,
"supplier_id": inv.supplier,
"supplier_name": inv.supplier_name,
**get_values_for_columns(additional_table_columns, inv),
"supplier_group": supplier_details.get(inv.supplier).get("supplier_group"), # supplier_group
"tax_id": supplier_details.get(inv.supplier).get("tax_id"),
"payable_account": inv.credit_to,
"mode_of_payment": inv.mode_of_payment,
"project": ", ".join(project) if inv.doctype == "Purchase Invoice" else inv.project,
"bill_no": inv.bill_no,
"bill_date": inv.bill_date,
"remarks": inv.remarks,
"purchase_order": ", ".join(purchase_order),
"purchase_receipt": ", ".join(purchase_receipt),
"currency": company_currency,
}
# map expense values
base_net_total = 0
@@ -75,14 +105,16 @@ def _execute(filters=None, additional_table_columns=None):
else:
expense_amount = flt(invoice_expense_map.get(inv.name, {}).get(expense_acc))
base_net_total += expense_amount
row.append(expense_amount)
row.update({frappe.scrub(expense_acc): expense_amount})
# Add amount in unrealized account
for account in unrealized_profit_loss_accounts:
row.append(flt(internal_invoice_map.get((inv.name, account))))
row.update(
{frappe.scrub(account + "_unrealized"): flt(internal_invoice_map.get((inv.name, account)))}
)
# net total
row.append(base_net_total or inv.base_net_total)
row.update({"net_total": base_net_total or inv.base_net_total})
# tax account
total_tax = 0
@@ -90,45 +122,190 @@ def _execute(filters=None, additional_table_columns=None):
if tax_acc not in expense_accounts:
tax_amount = flt(invoice_tax_map.get(inv.name, {}).get(tax_acc))
total_tax += tax_amount
row.append(tax_amount)
row.update({frappe.scrub(tax_acc): tax_amount})
# total tax, grand total, rounded total & outstanding amount
row += [total_tax, inv.base_grand_total, flt(inv.base_grand_total, 0), inv.outstanding_amount]
row.update(
{
"total_tax": total_tax,
"grand_total": inv.base_grand_total,
"rounded_total": inv.base_rounded_total,
"outstanding_amount": inv.outstanding_amount,
}
)
if inv.doctype == "Purchase Invoice":
row.update({"debit": inv.base_grand_total, "credit": 0.0})
else:
row.update({"debit": 0.0, "credit": inv.base_grand_total})
data.append(row)
return columns, data
res += sorted(data, key=lambda x: x["posting_date"])
if include_payments:
running_balance = flt(opening_row.balance)
for row in range(1, len(res)):
running_balance += res[row]["debit"] - res[row]["credit"]
res[row].update({"balance": running_balance})
return columns, res, None, None, None, include_payments
def get_columns(invoice_list, additional_table_columns):
def get_columns(invoice_list, additional_table_columns, include_payments=False):
"""return columns based on filters"""
columns = [
_("Invoice") + ":Link/Purchase Invoice:120",
_("Posting Date") + ":Date:80",
_("Supplier Id") + "::120",
_("Supplier Name") + "::120",
{
"label": _("Voucher Type"),
"fieldname": "voucher_type",
"width": 120,
},
{
"label": _("Voucher"),
"fieldname": "voucher_no",
"fieldtype": "Dynamic Link",
"options": "voucher_type",
"width": 120,
},
{"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 80},
{
"label": _("Supplier"),
"fieldname": "supplier_id",
"fieldtype": "Link",
"options": "Supplier",
"width": 120,
},
{"label": _("Supplier Name"), "fieldname": "supplier_name", "fieldtype": "Data", "width": 120},
]
if additional_table_columns:
if additional_table_columns and not include_payments:
columns += additional_table_columns
columns += [
_("Supplier Group") + ":Link/Supplier Group:120",
_("Tax Id") + "::80",
_("Payable Account") + ":Link/Account:120",
_("Mode of Payment") + ":Link/Mode of Payment:80",
_("Project") + ":Link/Project:80",
_("Bill No") + "::120",
_("Bill Date") + ":Date:80",
_("Remarks") + "::150",
_("Purchase Order") + ":Link/Purchase Order:100",
_("Purchase Receipt") + ":Link/Purchase Receipt:100",
{"fieldname": "currency", "label": _("Currency"), "fieldtype": "Data", "width": 80},
]
if not include_payments:
columns += [
{
"label": _("Supplier Group"),
"fieldname": "supplier_group",
"fieldtype": "Link",
"options": "Supplier Group",
"width": 120,
},
{"label": _("Tax Id"), "fieldname": "tax_id", "fieldtype": "Data", "width": 80},
{
"label": _("Payable Account"),
"fieldname": "payable_account",
"fieldtype": "Link",
"options": "Account",
"width": 100,
},
{
"label": _("Mode Of Payment"),
"fieldname": "mode_of_payment",
"fieldtype": "Data",
"width": 120,
},
{
"label": _("Project"),
"fieldname": "project",
"fieldtype": "Link",
"options": "Project",
"width": 80,
},
{"label": _("Bill No"), "fieldname": "bill_no", "fieldtype": "Data", "width": 120},
{"label": _("Bill Date"), "fieldname": "bill_date", "fieldtype": "Date", "width": 80},
{
"label": _("Purchase Order"),
"fieldname": "purchase_order",
"fieldtype": "Link",
"options": "Purchase Order",
"width": 100,
},
{
"label": _("Purchase Receipt"),
"fieldname": "purchase_receipt",
"fieldtype": "Link",
"options": "Purchase Receipt",
"width": 100,
},
{"fieldname": "currency", "label": _("Currency"), "fieldtype": "Data", "width": 80},
]
else:
columns += [
{
"fieldname": "payable_account",
"label": _("Payable Account"),
"fieldtype": "Link",
"options": "Account",
"width": 120,
},
{"fieldname": "debit", "label": _("Debit"), "fieldtype": "Currency", "width": 120},
{"fieldname": "credit", "label": _("Credit"), "fieldtype": "Currency", "width": 120},
{"fieldname": "balance", "label": _("Balance"), "fieldtype": "Currency", "width": 120},
]
account_columns, accounts = get_account_columns(invoice_list, include_payments)
columns = (
columns
+ account_columns[0]
+ account_columns[1]
+ [
{
"label": _("Net Total"),
"fieldname": "net_total",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
}
]
+ account_columns[2]
+ [
{
"label": _("Total Tax"),
"fieldname": "total_tax",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
}
]
)
if not include_payments:
columns += [
{
"label": _("Grand Total"),
"fieldname": "grand_total",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
},
{
"label": _("Rounded Total"),
"fieldname": "rounded_total",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
},
{
"label": _("Outstanding Amount"),
"fieldname": "outstanding_amount",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
},
]
columns += [{"label": _("Remarks"), "fieldname": "remarks", "fieldtype": "Data", "width": 120}]
return columns, accounts[0], accounts[2], accounts[1]
def get_account_columns(invoice_list, include_payments):
expense_accounts = []
tax_accounts = []
unrealized_profit_loss_accounts = []
expense_columns = []
tax_columns = []
unrealized_profit_loss_account_columns = []
if invoice_list:
expense_accounts = frappe.db.sql_list(
"""select distinct expense_account
@@ -139,15 +316,18 @@ def get_columns(invoice_list, additional_table_columns):
tuple([inv.name for inv in invoice_list]),
)
tax_accounts = frappe.db.sql_list(
"""select distinct account_head
from `tabPurchase Taxes and Charges` where parenttype = 'Purchase Invoice'
and docstatus = 1 and (account_head is not null and account_head != '')
and category in ('Total', 'Valuation and Total')
and parent in (%s) order by account_head"""
% ", ".join(["%s"] * len(invoice_list)),
tuple(inv.name for inv in invoice_list),
purchase_taxes_query = get_taxes_query(
invoice_list, "Purchase Taxes and Charges", "Purchase Invoice"
)
purchase_tax_accounts = purchase_taxes_query.run(as_dict=True, pluck="account_head")
tax_accounts = purchase_tax_accounts
if include_payments:
advance_taxes_query = get_taxes_query(
invoice_list, "Advance Taxes and Charges", "Payment Entry"
)
advance_tax_accounts = advance_taxes_query.run(as_dict=True, pluck="account_head")
tax_accounts = set(tax_accounts + advance_tax_accounts)
unrealized_profit_loss_accounts = frappe.db.sql_list(
"""SELECT distinct unrealized_profit_loss_account
@@ -158,108 +338,109 @@ def get_columns(invoice_list, additional_table_columns):
tuple(inv.name for inv in invoice_list),
)
expense_columns = [(account + ":Currency/currency:120") for account in expense_accounts]
unrealized_profit_loss_account_columns = [
(account + ":Currency/currency:120") for account in unrealized_profit_loss_accounts
]
tax_columns = [
(account + ":Currency/currency:120")
for account in tax_accounts
if account not in expense_accounts
]
for account in expense_accounts:
expense_columns.append(
{
"label": account,
"fieldname": frappe.scrub(account),
"fieldtype": "Currency",
"options": "currency",
"width": 120,
}
)
columns = (
columns
+ expense_columns
+ unrealized_profit_loss_account_columns
+ [_("Net Total") + ":Currency/currency:120"]
+ tax_columns
+ [
_("Total Tax") + ":Currency/currency:120",
_("Grand Total") + ":Currency/currency:120",
_("Rounded Total") + ":Currency/currency:120",
_("Outstanding Amount") + ":Currency/currency:120",
]
)
for account in tax_accounts:
if account not in expense_accounts:
tax_columns.append(
{
"label": account,
"fieldname": frappe.scrub(account),
"fieldtype": "Currency",
"options": "currency",
"width": 120,
}
)
return columns, expense_accounts, tax_accounts, unrealized_profit_loss_accounts
for account in unrealized_profit_loss_accounts:
unrealized_profit_loss_account_columns.append(
{
"label": account,
"fieldname": frappe.scrub(account),
"fieldtype": "Currency",
"options": "currency",
"width": 120,
}
)
columns = [expense_columns, unrealized_profit_loss_account_columns, tax_columns]
accounts = [expense_accounts, unrealized_profit_loss_accounts, tax_accounts]
def get_conditions(filters):
conditions = ""
if filters.get("company"):
conditions += " and company=%(company)s"
if filters.get("supplier"):
conditions += " and supplier = %(supplier)s"
if filters.get("from_date"):
conditions += " and posting_date>=%(from_date)s"
if filters.get("to_date"):
conditions += " and posting_date<=%(to_date)s"
if filters.get("mode_of_payment"):
conditions += " and ifnull(mode_of_payment, '') = %(mode_of_payment)s"
if filters.get("cost_center"):
conditions += """ and exists(select name from `tabPurchase Invoice Item`
where parent=`tabPurchase Invoice`.name
and ifnull(`tabPurchase Invoice Item`.cost_center, '') = %(cost_center)s)"""
if filters.get("warehouse"):
conditions += """ and exists(select name from `tabPurchase Invoice Item`
where parent=`tabPurchase Invoice`.name
and ifnull(`tabPurchase Invoice Item`.warehouse, '') = %(warehouse)s)"""
if filters.get("item_group"):
conditions += """ and exists(select name from `tabPurchase Invoice Item`
where parent=`tabPurchase Invoice`.name
and ifnull(`tabPurchase Invoice Item`.item_group, '') = %(item_group)s)"""
accounting_dimensions = get_accounting_dimensions(as_list=False)
if accounting_dimensions:
common_condition = """
and exists(select name from `tabPurchase Invoice Item`
where parent=`tabPurchase Invoice`.name
"""
for dimension in accounting_dimensions:
if filters.get(dimension.fieldname):
if frappe.get_cached_value("DocType", dimension.document_type, "is_tree"):
filters[dimension.fieldname] = get_dimension_with_children(
dimension.document_type, filters.get(dimension.fieldname)
)
conditions += (
common_condition
+ "and ifnull(`tabPurchase Invoice`.{0}, '') in %({0})s)".format(dimension.fieldname)
)
else:
conditions += (
common_condition
+ "and ifnull(`tabPurchase Invoice`.{0}, '') in %({0})s)".format(dimension.fieldname)
)
return conditions
return columns, accounts
def get_invoices(filters, additional_query_columns):
conditions = get_conditions(filters)
return frappe.db.sql(
"""
select
name, posting_date, credit_to, supplier, supplier_name, tax_id, bill_no, bill_date,
remarks, base_net_total, base_grand_total, outstanding_amount,
mode_of_payment {0}
from `tabPurchase Invoice`
where docstatus = 1 {1}
order by posting_date desc, name desc""".format(
additional_query_columns, conditions
),
filters,
as_dict=1,
pi = frappe.qb.DocType("Purchase Invoice")
query = (
frappe.qb.from_(pi)
.select(
ConstantColumn("Purchase Invoice").as_("doctype"),
pi.name,
pi.posting_date,
pi.credit_to,
pi.supplier,
pi.supplier_name,
pi.tax_id,
pi.bill_no,
pi.bill_date,
pi.remarks,
pi.base_net_total,
pi.base_grand_total,
pi.base_rounded_total,
pi.outstanding_amount,
pi.mode_of_payment,
)
.where((pi.docstatus == 1))
.orderby(pi.posting_date, pi.name, order=Order.desc)
)
if additional_query_columns:
for col in additional_query_columns:
query = query.select(col)
if filters.get("supplier"):
query = query.where(pi.supplier == filters.supplier)
query = get_conditions(filters, query, "Purchase Invoice")
query = apply_common_conditions(
filters, query, doctype="Purchase Invoice", child_doctype="Purchase Invoice Item"
)
invoices = query.run(as_dict=True)
return invoices
def get_conditions(filters, query, doctype):
parent_doc = frappe.qb.DocType(doctype)
if filters.get("mode_of_payment"):
query = query.where(parent_doc.mode_of_payment == filters.mode_of_payment)
return query
def get_payments(filters):
args = frappe._dict(
account="credit_to",
account_fieldname="paid_to",
party="supplier",
party_name="supplier_name",
party_account=[get_party_account("Supplier", filters.supplier, filters.company)],
)
payment_entries = get_payment_entries(filters, args)
journal_entries = get_journal_entries(filters, args)
return payment_entries + journal_entries
def get_invoice_expense_map(invoice_list):
expense_details = frappe.db.sql(
@@ -300,7 +481,9 @@ def get_internal_invoice_map(invoice_list):
return internal_invoice_map
def get_invoice_tax_map(invoice_list, invoice_expense_map, expense_accounts):
def get_invoice_tax_map(
invoice_list, invoice_expense_map, expense_accounts, include_payments=False
):
tax_details = frappe.db.sql(
"""
select parent, account_head, case add_deduct_tax when "Add" then sum(base_tax_amount_after_discount_amount)
@@ -315,6 +498,9 @@ def get_invoice_tax_map(invoice_list, invoice_expense_map, expense_accounts):
as_dict=1,
)
if include_payments:
tax_details += get_advance_taxes_and_charges(invoice_list)
invoice_tax_map = {}
for d in tax_details:
if d.account_head in expense_accounts:
@@ -382,17 +568,3 @@ def get_account_details(invoice_list):
account_map[acc.name] = acc.parent_account
return account_map
def get_supplier_details(suppliers):
supplier_details = {}
for supp in frappe.db.sql(
"""select name, supplier_group from `tabSupplier`
where name in (%s)"""
% ", ".join(["%s"] * len(suppliers)),
tuple(suppliers),
as_dict=1,
):
supplier_details.setdefault(supp.name, supp.supplier_group)
return supplier_details

View File

@@ -0,0 +1,128 @@
# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.utils import add_months, getdate, today
from erpnext.accounts.report.purchase_register.purchase_register import execute
class TestPurchaseRegister(FrappeTestCase):
def test_purchase_register(self):
frappe.db.sql("delete from `tabPurchase Invoice` where company='_Test Company 6'")
frappe.db.sql("delete from `tabGL Entry` where company='_Test Company 6'")
filters = frappe._dict(
company="_Test Company 6", from_date=add_months(today(), -1), to_date=today()
)
pi = make_purchase_invoice()
report_results = execute(filters)
first_row = frappe._dict(report_results[1][0])
self.assertEqual(first_row.voucher_type, "Purchase Invoice")
self.assertEqual(first_row.voucher_no, pi.name)
self.assertEqual(first_row.payable_account, "Creditors - _TC6")
self.assertEqual(first_row.net_total, 1000)
self.assertEqual(first_row.total_tax, 100)
self.assertEqual(first_row.grand_total, 1100)
def test_purchase_register_ledger_view(self):
frappe.db.sql("delete from `tabPurchase Invoice` where company='_Test Company 6'")
frappe.db.sql("delete from `tabGL Entry` where company='_Test Company 6'")
filters = frappe._dict(
company="_Test Company 6",
from_date=add_months(today(), -1),
to_date=today(),
include_payments=True,
supplier="_Test Supplier",
)
pi = make_purchase_invoice()
pe = make_payment_entry()
report_results = execute(filters)
first_row = frappe._dict(report_results[1][2])
self.assertEqual(first_row.voucher_type, "Payment Entry")
self.assertEqual(first_row.voucher_no, pe.name)
self.assertEqual(first_row.payable_account, "Creditors - _TC6")
self.assertEqual(first_row.debit, 0)
self.assertEqual(first_row.credit, 600)
self.assertEqual(first_row.balance, 500)
def make_purchase_invoice():
from erpnext.accounts.doctype.account.test_account import create_account
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
gst_acc = create_account(
account_name="GST",
account_type="Tax",
parent_account="Duties and Taxes - _TC6",
company="_Test Company 6",
account_currency="INR",
)
create_warehouse(warehouse_name="_Test Warehouse - _TC6", company="_Test Company 6")
create_cost_center(cost_center_name="_Test Cost Center", company="_Test Company 6")
pi = create_purchase_invoice_with_taxes()
pi.submit()
return pi
def create_purchase_invoice_with_taxes():
return frappe.get_doc(
{
"doctype": "Purchase Invoice",
"posting_date": today(),
"supplier": "_Test Supplier",
"company": "_Test Company 6",
"cost_center": "_Test Cost Center - _TC6",
"taxes_and_charges": "",
"currency": "INR",
"credit_to": "Creditors - _TC6",
"items": [
{
"doctype": "Purchase Invoice Item",
"cost_center": "_Test Cost Center - _TC6",
"item_code": "_Test Item",
"qty": 1,
"rate": 1000,
"expense_account": "Stock Received But Not Billed - _TC6",
}
],
"taxes": [
{
"account_head": "GST - _TC6",
"cost_center": "_Test Cost Center - _TC6",
"add_deduct_tax": "Add",
"category": "Valuation and Total",
"charge_type": "Actual",
"description": "Shipping Charges",
"doctype": "Purchase Taxes and Charges",
"parentfield": "taxes",
"rate": 100,
"tax_amount": 100.0,
}
],
}
)
def make_payment_entry():
frappe.set_user("Administrator")
from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry
return create_payment_entry(
company="_Test Company 6",
party_type="Supplier",
party="_Test Supplier",
payment_type="Pay",
paid_from="Cash - _TC6",
paid_to="Creditors - _TC6",
paid_amount=600,
save=1,
submit=1,
)

View File

@@ -64,6 +64,12 @@ frappe.query_reports["Sales Register"] = {
"label": __("Item Group"),
"fieldtype": "Link",
"options": "Item Group"
},
{
"fieldname": "include_payments",
"label": __("Show Ledger View"),
"fieldtype": "Check",
"default": 0
}
]
}

View File

@@ -5,13 +5,22 @@
import frappe
from frappe import _, msgprint
from frappe.model.meta import get_field_precision
from frappe.utils import flt
from frappe.query_builder.custom import ConstantColumn
from frappe.utils import flt, getdate
from pypika import Order
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_accounting_dimensions,
get_dimension_with_children,
from erpnext.accounts.party import get_party_account
from erpnext.accounts.report.utils import (
apply_common_conditions,
get_advance_taxes_and_charges,
get_journal_entries,
get_opening_row,
get_party_details,
get_payment_entries,
get_query_columns,
get_taxes_query,
get_values_for_columns,
)
from erpnext.accounts.report.utils import get_query_columns, get_values_for_columns
def execute(filters=None):
@@ -22,9 +31,15 @@ def _execute(filters, additional_table_columns=None):
if not filters:
filters = frappe._dict({})
include_payments = filters.get("include_payments")
if filters.get("include_payments") and not filters.get("customer"):
frappe.throw(_("Please select a customer for fetching payments."))
invoice_list = get_invoices(filters, get_query_columns(additional_table_columns))
columns, income_accounts, tax_accounts, unrealized_profit_loss_accounts = get_columns(
invoice_list, additional_table_columns
if filters.get("include_payments"):
invoice_list += get_payments(filters)
columns, income_accounts, unrealized_profit_loss_accounts, tax_accounts = get_columns(
invoice_list, additional_table_columns, include_payments
)
if not invoice_list:
@@ -34,13 +49,29 @@ def _execute(filters, additional_table_columns=None):
invoice_income_map = get_invoice_income_map(invoice_list)
internal_invoice_map = get_internal_invoice_map(invoice_list)
invoice_income_map, invoice_tax_map = get_invoice_tax_map(
invoice_list, invoice_income_map, income_accounts
invoice_list, invoice_income_map, income_accounts, include_payments
)
# Cost Center & Warehouse Map
invoice_cc_wh_map = get_invoice_cc_wh_map(invoice_list)
invoice_so_dn_map = get_invoice_so_dn_map(invoice_list)
company_currency = frappe.get_cached_value("Company", filters.get("company"), "default_currency")
mode_of_payments = get_mode_of_payments([inv.name for inv in invoice_list])
customers = list(set(d.customer for d in invoice_list))
customer_details = get_party_details("Customer", customers)
res = []
if include_payments:
opening_row = get_opening_row(
"Customer", filters.customer, getdate(filters.from_date), filters.company
)[0]
res.append(
{
"receivable_account": opening_row.account,
"debit": flt(opening_row.debit),
"credit": flt(opening_row.credit),
"balance": flt(opening_row.balance),
}
)
data = []
for inv in invoice_list:
@@ -51,14 +82,15 @@ def _execute(filters, additional_table_columns=None):
warehouse = list(set(invoice_cc_wh_map.get(inv.name, {}).get("warehouse", [])))
row = {
"invoice": inv.name,
"voucher_type": inv.doctype,
"voucher_no": inv.name,
"posting_date": inv.posting_date,
"customer": inv.customer,
"customer_name": inv.customer_name,
**get_values_for_columns(additional_table_columns, inv),
"customer_group": inv.get("customer_group"),
"territory": inv.get("territory"),
"tax_id": inv.get("tax_id"),
"customer_group": customer_details.get(inv.customer).get("customer_group"),
"territory": customer_details.get(inv.customer).get("territory"),
"tax_id": customer_details.get(inv.customer).get("tax_id"),
"receivable_account": inv.debit_to,
"mode_of_payment": ", ".join(mode_of_payments.get(inv.name, [])),
"project": inv.project,
@@ -66,7 +98,7 @@ def _execute(filters, additional_table_columns=None):
"remarks": inv.remarks,
"sales_order": ", ".join(sales_order),
"delivery_note": ", ".join(delivery_note),
"cost_center": ", ".join(cost_center),
"cost_center": ", ".join(cost_center) if inv.doctype == "Sales Invoice" else inv.cost_center,
"warehouse": ", ".join(warehouse),
"currency": company_currency,
}
@@ -116,19 +148,36 @@ def _execute(filters, additional_table_columns=None):
}
)
if inv.doctype == "Sales Invoice":
row.update({"debit": inv.base_grand_total, "credit": 0.0})
else:
row.update({"debit": 0.0, "credit": inv.base_grand_total})
data.append(row)
return columns, data
res += sorted(data, key=lambda x: x["posting_date"])
if include_payments:
running_balance = flt(opening_row.balance)
for row in range(1, len(res)):
running_balance += res[row]["debit"] - res[row]["credit"]
res[row].update({"balance": running_balance})
return columns, res, None, None, None, include_payments
def get_columns(invoice_list, additional_table_columns):
def get_columns(invoice_list, additional_table_columns, include_payments=False):
"""return columns based on filters"""
columns = [
{
"label": _("Invoice"),
"fieldname": "invoice",
"fieldtype": "Link",
"options": "Sales Invoice",
"label": _("Voucher Type"),
"fieldname": "voucher_type",
"width": 120,
},
{
"label": _("Voucher"),
"fieldname": "voucher_no",
"fieldtype": "Dynamic Link",
"options": "voucher_type",
"width": 120,
},
{"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 80},
@@ -142,83 +191,156 @@ def get_columns(invoice_list, additional_table_columns):
{"label": _("Customer Name"), "fieldname": "customer_name", "fieldtype": "Data", "width": 120},
]
if additional_table_columns:
if additional_table_columns and not include_payments:
columns += additional_table_columns
columns += [
if not include_payments:
columns += [
{
"label": _("Customer Group"),
"fieldname": "customer_group",
"fieldtype": "Link",
"options": "Customer Group",
"width": 120,
},
{
"label": _("Territory"),
"fieldname": "territory",
"fieldtype": "Link",
"options": "Territory",
"width": 80,
},
{"label": _("Tax Id"), "fieldname": "tax_id", "fieldtype": "Data", "width": 80},
{
"label": _("Receivable Account"),
"fieldname": "receivable_account",
"fieldtype": "Link",
"options": "Account",
"width": 100,
},
{
"label": _("Mode Of Payment"),
"fieldname": "mode_of_payment",
"fieldtype": "Data",
"width": 120,
},
{
"label": _("Project"),
"fieldname": "project",
"fieldtype": "Link",
"options": "Project",
"width": 80,
},
{"label": _("Owner"), "fieldname": "owner", "fieldtype": "Data", "width": 100},
{
"label": _("Sales Order"),
"fieldname": "sales_order",
"fieldtype": "Link",
"options": "Sales Order",
"width": 100,
},
{
"label": _("Delivery Note"),
"fieldname": "delivery_note",
"fieldtype": "Link",
"options": "Delivery Note",
"width": 100,
},
{
"label": _("Cost Center"),
"fieldname": "cost_center",
"fieldtype": "Link",
"options": "Cost Center",
"width": 100,
},
{
"label": _("Warehouse"),
"fieldname": "warehouse",
"fieldtype": "Link",
"options": "Warehouse",
"width": 100,
},
{"fieldname": "currency", "label": _("Currency"), "fieldtype": "Data", "width": 80},
]
else:
columns += [
{
"fieldname": "receivable_account",
"label": _("Receivable Account"),
"fieldtype": "Link",
"options": "Account",
"width": 120,
},
{"fieldname": "debit", "label": _("Debit"), "fieldtype": "Currency", "width": 120},
{"fieldname": "credit", "label": _("Credit"), "fieldtype": "Currency", "width": 120},
{"fieldname": "balance", "label": _("Balance"), "fieldtype": "Currency", "width": 120},
]
account_columns, accounts = get_account_columns(invoice_list, include_payments)
net_total_column = [
{
"label": _("Customer Group"),
"fieldname": "customer_group",
"fieldtype": "Link",
"options": "Customer Group",
"label": _("Net Total"),
"fieldname": "net_total",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
},
{
"label": _("Territory"),
"fieldname": "territory",
"fieldtype": "Link",
"options": "Territory",
"width": 80,
},
{"label": _("Tax Id"), "fieldname": "tax_id", "fieldtype": "Data", "width": 120},
{
"label": _("Receivable Account"),
"fieldname": "receivable_account",
"fieldtype": "Link",
"options": "Account",
"width": 80,
},
{
"label": _("Mode Of Payment"),
"fieldname": "mode_of_payment",
"fieldtype": "Data",
"width": 120,
},
{
"label": _("Project"),
"fieldname": "project",
"fieldtype": "Link",
"options": "Project",
"width": 80,
},
{"label": _("Owner"), "fieldname": "owner", "fieldtype": "Data", "width": 150},
{"label": _("Remarks"), "fieldname": "remarks", "fieldtype": "Data", "width": 150},
{
"label": _("Sales Order"),
"fieldname": "sales_order",
"fieldtype": "Link",
"options": "Sales Order",
"width": 100,
},
{
"label": _("Delivery Note"),
"fieldname": "delivery_note",
"fieldtype": "Link",
"options": "Delivery Note",
"width": 100,
},
{
"label": _("Cost Center"),
"fieldname": "cost_center",
"fieldtype": "Link",
"options": "Cost Center",
"width": 100,
},
{
"label": _("Warehouse"),
"fieldname": "warehouse",
"fieldtype": "Link",
"options": "Warehouse",
"width": 100,
},
{"fieldname": "currency", "label": _("Currency"), "fieldtype": "Data", "width": 80},
}
]
total_columns = [
{
"label": _("Tax Total"),
"fieldname": "tax_total",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
}
]
if not include_payments:
total_columns += [
{
"label": _("Grand Total"),
"fieldname": "grand_total",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
},
{
"label": _("Rounded Total"),
"fieldname": "rounded_total",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
},
{
"label": _("Outstanding Amount"),
"fieldname": "outstanding_amount",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
},
]
columns = (
columns
+ account_columns[0]
+ account_columns[2]
+ net_total_column
+ account_columns[1]
+ total_columns
)
columns += [{"label": _("Remarks"), "fieldname": "remarks", "fieldtype": "Data", "width": 150}]
return columns, accounts[0], accounts[1], accounts[2]
def get_account_columns(invoice_list, include_payments):
income_accounts = []
tax_accounts = []
unrealized_profit_loss_accounts = []
income_columns = []
tax_columns = []
unrealized_profit_loss_accounts = []
unrealized_profit_loss_account_columns = []
if invoice_list:
@@ -230,14 +352,16 @@ def get_columns(invoice_list, additional_table_columns):
tuple(inv.name for inv in invoice_list),
)
tax_accounts = frappe.db.sql_list(
"""select distinct account_head
from `tabSales Taxes and Charges` where parenttype = 'Sales Invoice'
and docstatus = 1 and base_tax_amount_after_discount_amount != 0
and parent in (%s) order by account_head"""
% ", ".join(["%s"] * len(invoice_list)),
tuple(inv.name for inv in invoice_list),
)
sales_taxes_query = get_taxes_query(invoice_list, "Sales Taxes and Charges", "Sales Invoice")
sales_tax_accounts = sales_taxes_query.run(as_dict=True, pluck="account_head")
tax_accounts = sales_tax_accounts
if include_payments:
advance_taxes_query = get_taxes_query(
invoice_list, "Advance Taxes and Charges", "Payment Entry"
)
advance_tax_accounts = advance_taxes_query.run(as_dict=True, pluck="account_head")
tax_accounts = set(tax_accounts + advance_tax_accounts)
unrealized_profit_loss_accounts = frappe.db.sql_list(
"""SELECT distinct unrealized_profit_loss_account
@@ -283,134 +407,82 @@ def get_columns(invoice_list, additional_table_columns):
}
)
net_total_column = [
{
"label": _("Net Total"),
"fieldname": "net_total",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
}
]
columns = [income_columns, unrealized_profit_loss_account_columns, tax_columns]
accounts = [income_accounts, unrealized_profit_loss_accounts, tax_accounts]
total_columns = [
{
"label": _("Tax Total"),
"fieldname": "tax_total",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
},
{
"label": _("Grand Total"),
"fieldname": "grand_total",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
},
{
"label": _("Rounded Total"),
"fieldname": "rounded_total",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
},
{
"label": _("Outstanding Amount"),
"fieldname": "outstanding_amount",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
},
]
columns = (
columns
+ income_columns
+ unrealized_profit_loss_account_columns
+ net_total_column
+ tax_columns
+ total_columns
)
return columns, income_accounts, tax_accounts, unrealized_profit_loss_accounts
def get_conditions(filters):
conditions = ""
accounting_dimensions = get_accounting_dimensions(as_list=False) or []
accounting_dimensions_list = [d.fieldname for d in accounting_dimensions]
if filters.get("company"):
conditions += " and company=%(company)s"
if filters.get("customer") and "customer" not in accounting_dimensions_list:
conditions += " and customer = %(customer)s"
if filters.get("from_date"):
conditions += " and posting_date >= %(from_date)s"
if filters.get("to_date"):
conditions += " and posting_date <= %(to_date)s"
if filters.get("owner"):
conditions += " and owner = %(owner)s"
def get_sales_invoice_item_field_condition(field, table="Sales Invoice Item") -> str:
if not filters.get(field) or field in accounting_dimensions_list:
return ""
return f""" and exists(select name from `tab{table}`
where parent=`tabSales Invoice`.name
and ifnull(`tab{table}`.{field}, '') = %({field})s)"""
conditions += get_sales_invoice_item_field_condition("mode_of_payment", "Sales Invoice Payment")
conditions += get_sales_invoice_item_field_condition("cost_center")
conditions += get_sales_invoice_item_field_condition("warehouse")
conditions += get_sales_invoice_item_field_condition("brand")
conditions += get_sales_invoice_item_field_condition("item_group")
if accounting_dimensions:
common_condition = """
and exists(select name from `tabSales Invoice Item`
where parent=`tabSales Invoice`.name
"""
for dimension in accounting_dimensions:
if filters.get(dimension.fieldname):
if frappe.get_cached_value("DocType", dimension.document_type, "is_tree"):
filters[dimension.fieldname] = get_dimension_with_children(
dimension.document_type, filters.get(dimension.fieldname)
)
conditions += (
common_condition
+ "and ifnull(`tabSales Invoice`.{0}, '') in %({0})s)".format(dimension.fieldname)
)
else:
conditions += (
common_condition
+ "and ifnull(`tabSales Invoice`.{0}, '') in %({0})s)".format(dimension.fieldname)
)
return conditions
return columns, accounts
def get_invoices(filters, additional_query_columns):
conditions = get_conditions(filters)
return frappe.db.sql(
"""
select name, posting_date, debit_to, project, customer,
customer_name, owner, remarks, territory, tax_id, customer_group,
base_net_total, base_grand_total, base_rounded_total, outstanding_amount,
is_internal_customer, represents_company, company {0}
from `tabSales Invoice`
where docstatus = 1 {1}
order by posting_date desc, name desc""".format(
additional_query_columns, conditions
),
filters,
as_dict=1,
si = frappe.qb.DocType("Sales Invoice")
query = (
frappe.qb.from_(si)
.select(
ConstantColumn("Sales Invoice").as_("doctype"),
si.name,
si.posting_date,
si.debit_to,
si.project,
si.customer,
si.customer_name,
si.owner,
si.remarks,
si.territory,
si.tax_id,
si.customer_group,
si.base_net_total,
si.base_grand_total,
si.base_rounded_total,
si.outstanding_amount,
si.is_internal_customer,
si.represents_company,
si.company,
)
.where((si.docstatus == 1))
.orderby(si.posting_date, si.name, order=Order.desc)
)
if additional_query_columns:
for col in additional_query_columns:
query = query.select(col)
if filters.get("customer"):
query = query.where(si.customer == filters.customer)
query = get_conditions(filters, query, "Sales Invoice")
query = apply_common_conditions(
filters, query, doctype="Sales Invoice", child_doctype="Sales Invoice Item"
)
invoices = query.run(as_dict=True)
return invoices
def get_conditions(filters, query, doctype):
parent_doc = frappe.qb.DocType(doctype)
if filters.get("owner"):
query = query.where(parent_doc.owner == filters.owner)
if filters.get("mode_of_payment"):
payment_doc = frappe.qb.DocType("Sales Invoice Payment")
query = query.inner_join(payment_doc).on(parent_doc.name == payment_doc.parent)
query = query.where(payment_doc.mode_of_payment == filters.mode_of_payment).distinct()
return query
def get_payments(filters):
args = frappe._dict(
account="debit_to",
account_fieldname="paid_from",
party="customer",
party_name="customer_name",
party_account=[get_party_account("Customer", filters.customer, filters.company)],
)
payment_entries = get_payment_entries(filters, args)
journal_entries = get_journal_entries(filters, args)
return payment_entries + journal_entries
def get_invoice_income_map(invoice_list):
income_details = frappe.db.sql(
@@ -447,7 +519,7 @@ def get_internal_invoice_map(invoice_list):
return internal_invoice_map
def get_invoice_tax_map(invoice_list, invoice_income_map, income_accounts):
def get_invoice_tax_map(invoice_list, invoice_income_map, income_accounts, include_payments=False):
tax_details = frappe.db.sql(
"""select parent, account_head,
sum(base_tax_amount_after_discount_amount) as tax_amount
@@ -457,6 +529,9 @@ def get_invoice_tax_map(invoice_list, invoice_income_map, income_accounts):
as_dict=1,
)
if include_payments:
tax_details += get_advance_taxes_and_charges(invoice_list)
invoice_tax_map = {}
for d in tax_details:
if d.account_head in income_accounts:

View File

@@ -46,12 +46,10 @@ def get_result(
out = []
for name, details in gle_map.items():
tax_amount, total_amount, grand_total, base_total = 0, 0, 0, 0
bill_no, bill_date = "", ""
tax_withholding_category = tax_category_map.get(name)
rate = tax_rate_map.get(tax_withholding_category)
for entry in details:
tax_amount, total_amount, grand_total, base_total = 0, 0, 0, 0
tax_withholding_category, rate = None, None
bill_no, bill_date = "", ""
party = entry.party or entry.against
posting_date = entry.posting_date
voucher_type = entry.voucher_type
@@ -61,13 +59,18 @@ def get_result(
if party_list:
party = party_list[0]
if not tax_withholding_category:
tax_withholding_category = party_map.get(party, {}).get("tax_withholding_category")
rate = tax_rate_map.get(tax_withholding_category)
if entry.account in tds_accounts:
if entry.account in tds_accounts.keys():
tax_amount += entry.credit - entry.debit
# infer tax withholding category from the account if it's the single account for this category
tax_withholding_category = tds_accounts.get(entry.account)
# or else the consolidated value from the voucher document
if not tax_withholding_category:
tax_withholding_category = tax_category_map.get(name)
# or else from the party default
if not tax_withholding_category:
tax_withholding_category = party_map.get(party, {}).get("tax_withholding_category")
rate = tax_rate_map.get(tax_withholding_category)
if net_total_map.get(name):
if voucher_type == "Journal Entry" and tax_amount and rate:
# back calcalute total amount from rate and tax_amount
@@ -80,41 +83,41 @@ def get_result(
else:
total_amount += entry.credit
if tax_amount:
if party_map.get(party, {}).get("party_type") == "Supplier":
party_name = "supplier_name"
party_type = "supplier_type"
else:
party_name = "customer_name"
party_type = "customer_type"
if tax_amount:
if party_map.get(party, {}).get("party_type") == "Supplier":
party_name = "supplier_name"
party_type = "supplier_type"
else:
party_name = "customer_name"
party_type = "customer_type"
row = {
"pan"
if frappe.db.has_column(filters.party_type, "pan")
else "tax_id": party_map.get(party, {}).get("pan"),
"party": party_map.get(party, {}).get("name"),
}
if filters.naming_series == "Naming Series":
row.update({"party_name": party_map.get(party, {}).get(party_name)})
row.update(
{
"section_code": tax_withholding_category or "",
"entity_type": party_map.get(party, {}).get(party_type),
"rate": rate,
"total_amount": total_amount,
"grand_total": grand_total,
"base_total": base_total,
"tax_amount": tax_amount,
"transaction_date": posting_date,
"transaction_type": voucher_type,
"ref_no": name,
"supplier_invoice_no": bill_no,
"supplier_invoice_date": bill_date,
row = {
"pan"
if frappe.db.has_column(filters.party_type, "pan")
else "tax_id": party_map.get(party, {}).get("pan"),
"party": party_map.get(party, {}).get("name"),
}
)
out.append(row)
if filters.naming_series == "Naming Series":
row.update({"party_name": party_map.get(party, {}).get(party_name)})
row.update(
{
"section_code": tax_withholding_category or "",
"entity_type": party_map.get(party, {}).get(party_type),
"rate": rate,
"total_amount": total_amount,
"grand_total": grand_total,
"base_total": base_total,
"tax_amount": tax_amount,
"transaction_date": posting_date,
"transaction_type": voucher_type,
"ref_no": name,
"supplier_invoice_no": bill_no,
"supplier_invoice_date": bill_date,
}
)
out.append(row)
out.sort(key=lambda x: x["section_code"])
@@ -239,7 +242,7 @@ def get_columns(filters):
"width": 120,
},
{
"label": _("Tax Amount"),
"label": _("TDS Amount") if filters.get("party_type") == "Supplier" else _("TCS Amount"),
"fieldname": "tax_amount",
"fieldtype": "Float",
"width": 120,
@@ -282,11 +285,20 @@ def get_tds_docs(filters):
journal_entry_party_map = frappe._dict()
bank_accounts = frappe.get_all("Account", {"is_group": 0, "account_type": "Bank"}, pluck="name")
tds_accounts = frappe.get_all(
"Tax Withholding Account", {"company": filters.get("company")}, pluck="account"
_tds_accounts = frappe.get_all(
"Tax Withholding Account",
{"company": filters.get("company")},
["account", "parent"],
)
tds_accounts = {}
for tds_acc in _tds_accounts:
# if it turns out not to be the only tax withholding category, then don't include in the map
if tds_acc["account"] in tds_accounts:
tds_accounts[tds_acc["account"]] = None
else:
tds_accounts[tds_acc["account"]] = tds_acc["parent"]
tds_docs = get_tds_docs_query(filters, bank_accounts, tds_accounts).run(as_dict=True)
tds_docs = get_tds_docs_query(filters, bank_accounts, list(tds_accounts.keys())).run(as_dict=True)
for d in tds_docs:
if d.voucher_type == "Purchase Invoice":
@@ -345,21 +357,16 @@ def get_tds_docs_query(filters, bank_accounts, tds_accounts):
if filters.get("party"):
party = [filters.get("party")]
query = query.where(
((gle.account.isin(tds_accounts) & gle.against.isin(party)))
| ((gle.voucher_type == "Journal Entry") & (gle.party == filters.get("party")))
| gle.party.isin(party)
jv_condition = gle.against.isin(party) | (
(gle.voucher_type == "Journal Entry") & (gle.party == filters.get("party"))
)
else:
party = frappe.get_all(filters.get("party_type"), pluck="name")
query = query.where(
((gle.account.isin(tds_accounts) & gle.against.isin(party)))
| (
(gle.voucher_type == "Journal Entry")
& ((gle.party_type == filters.get("party_type")) | (gle.party_type == ""))
)
| gle.party.isin(party)
jv_condition = gle.against.isin(party) | (
(gle.voucher_type == "Journal Entry")
& ((gle.party_type == filters.get("party_type")) | (gle.party_type == ""))
)
query = query.where((gle.account.isin(tds_accounts) & jv_condition) | gle.party.isin(party))
return query
@@ -399,7 +406,7 @@ def get_doc_info(vouchers, doctype, tax_category_map, net_total_map=None):
"paid_amount_after_tax",
"base_paid_amount",
],
"Journal Entry": ["tax_withholding_category", "total_amount"],
"Journal Entry": ["total_amount"],
}
entries = frappe.get_all(

View File

@@ -5,7 +5,6 @@ import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.utils import today
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
@@ -22,18 +21,42 @@ class TestTdsPayableMonthly(AccountsTestMixin, FrappeTestCase):
self.create_company()
self.clear_old_entries()
create_tax_accounts()
create_tcs_category()
def test_tax_withholding_for_customers(self):
create_tax_category(cumulative_threshold=300)
frappe.db.set_value("Customer", "_Test Customer", "tax_withholding_category", "TCS")
si = create_sales_invoice(rate=1000)
pe = create_tcs_payment_entry()
jv = create_tcs_journal_entry()
filters = frappe._dict(
company="_Test Company", party_type="Customer", from_date=today(), to_date=today()
)
result = execute(filters)[1]
expected_values = [
# Check for JV totals using back calculation logic
[jv.name, "TCS", 0.075, -10000.0, -7.5, -10000.0],
[pe.name, "TCS", 0.075, 2550, 0.53, 2550.53],
[si.name, "TCS", 0.075, 1000, 0.53, 1000.53],
[si.name, "TCS", 0.075, 1000.0, 0.53, 1000.53],
]
self.check_expected_values(result, expected_values)
def test_single_account_for_multiple_categories(self):
create_tax_category("TDS - 1", rate=10, account="TDS - _TC")
inv_1 = make_purchase_invoice(rate=1000, do_not_submit=True)
inv_1.tax_withholding_category = "TDS - 1"
inv_1.submit()
create_tax_category("TDS - 2", rate=20, account="TDS - _TC")
inv_2 = make_purchase_invoice(rate=1000, do_not_submit=True)
inv_2.tax_withholding_category = "TDS - 2"
inv_2.submit()
result = execute(
frappe._dict(company="_Test Company", party_type="Supplier", from_date=today(), to_date=today())
)[1]
expected_values = [
[inv_1.name, "TDS - 1", 10, 5000, 500, 5500],
[inv_2.name, "TDS - 2", 20, 5000, 1000, 6000],
]
self.check_expected_values(result, expected_values)
@@ -41,12 +64,15 @@ class TestTdsPayableMonthly(AccountsTestMixin, FrappeTestCase):
for i in range(len(result)):
voucher = frappe._dict(result[i])
voucher_expected_values = expected_values[i]
self.assertEqual(voucher.ref_no, voucher_expected_values[0])
self.assertEqual(voucher.section_code, voucher_expected_values[1])
self.assertEqual(voucher.rate, voucher_expected_values[2])
self.assertEqual(voucher.base_total, voucher_expected_values[3])
self.assertEqual(voucher.tax_amount, voucher_expected_values[4])
self.assertEqual(voucher.grand_total, voucher_expected_values[5])
voucher_actual_values = (
voucher.ref_no,
voucher.section_code,
voucher.rate,
voucher.base_total,
voucher.tax_amount,
voucher.grand_total,
)
self.assertSequenceEqual(voucher_actual_values, voucher_expected_values)
def tearDown(self):
self.clear_old_entries()
@@ -67,24 +93,20 @@ def create_tax_accounts():
).insert(ignore_if_duplicate=True)
def create_tcs_category():
def create_tax_category(category="TCS", rate=0.075, account="TCS - _TC", cumulative_threshold=0):
fiscal_year = get_fiscal_year(today(), company="_Test Company")
from_date = fiscal_year[1]
to_date = fiscal_year[2]
tax_category = create_tax_withholding_category(
category_name="TCS",
rate=0.075,
create_tax_withholding_category(
category_name=category,
rate=rate,
from_date=from_date,
to_date=to_date,
account="TCS - _TC",
cumulative_threshold=300,
account=account,
cumulative_threshold=cumulative_threshold,
)
customer = frappe.get_doc("Customer", "_Test Customer")
customer.tax_withholding_category = "TCS"
customer.save()
def create_tcs_payment_entry():
payment_entry = create_payment_entry(
@@ -109,3 +131,32 @@ def create_tcs_payment_entry():
)
payment_entry.submit()
return payment_entry
def create_tcs_journal_entry():
jv = frappe.new_doc("Journal Entry")
jv.posting_date = today()
jv.company = "_Test Company"
jv.set(
"accounts",
[
{
"account": "Debtors - _TC",
"party_type": "Customer",
"party": "_Test Customer",
"credit_in_account_currency": 10000,
},
{
"account": "Debtors - _TC",
"party_type": "Customer",
"party": "_Test Customer",
"debit_in_account_currency": 9992.5,
},
{
"account": "TCS - _TC",
"debit_in_account_currency": 7.5,
},
],
)
jv.insert()
return jv.submit()

View File

@@ -1,8 +1,16 @@
import frappe
from frappe.query_builder.custom import ConstantColumn
from frappe.query_builder.functions import Sum
from frappe.utils import flt, formatdate, get_datetime_str, get_table_name
from pypika import Order
from erpnext import get_company_currency, get_default_company
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_accounting_dimensions,
get_dimension_with_children,
)
from erpnext.accounts.doctype.fiscal_year.fiscal_year import get_from_and_to_date
from erpnext.accounts.party import get_party_account
from erpnext.setup.utils import get_exchange_rate
__exchange_rates = {}
@@ -165,7 +173,7 @@ def get_query_columns(report_columns):
else:
columns.append(fieldname)
return ", " + ", ".join(columns)
return columns
def get_values_for_columns(report_columns, report_row):
@@ -179,3 +187,206 @@ def get_values_for_columns(report_columns, report_row):
values[fieldname] = report_row.get(fieldname)
return values
def get_party_details(party_type, party_list):
party_details = {}
party = frappe.qb.DocType(party_type)
query = frappe.qb.from_(party).select(party.name, party.tax_id).where(party.name.isin(party_list))
if party_type == "Supplier":
query = query.select(party.supplier_group)
else:
query = query.select(party.customer_group, party.territory)
party_detail_list = query.run(as_dict=True)
for party_dict in party_detail_list:
party_details[party_dict.name] = party_dict
return party_details
def get_taxes_query(invoice_list, doctype, parenttype):
taxes = frappe.qb.DocType(doctype)
query = (
frappe.qb.from_(taxes)
.select(taxes.account_head)
.distinct()
.where(
(taxes.parenttype == parenttype)
& (taxes.docstatus == 1)
& (taxes.account_head.isnotnull())
& (taxes.parent.isin([inv.name for inv in invoice_list]))
)
.orderby(taxes.account_head)
)
if doctype == "Purchase Taxes and Charges":
return query.where(taxes.category.isin(["Total", "Valuation and Total"]))
elif doctype == "Sales Taxes and Charges":
return query
return query.where(taxes.charge_type.isin(["On Paid Amount", "Actual"]))
def get_journal_entries(filters, args):
je = frappe.qb.DocType("Journal Entry")
journal_account = frappe.qb.DocType("Journal Entry Account")
query = (
frappe.qb.from_(je)
.inner_join(journal_account)
.on(je.name == journal_account.parent)
.select(
je.voucher_type.as_("doctype"),
je.name,
je.posting_date,
journal_account.account.as_(args.account),
journal_account.party.as_(args.party),
journal_account.party.as_(args.party_name),
je.bill_no,
je.bill_date,
je.remark.as_("remarks"),
je.total_amount.as_("base_net_total"),
je.total_amount.as_("base_grand_total"),
je.mode_of_payment,
journal_account.project,
)
.where(
(je.voucher_type == "Journal Entry")
& (je.docstatus == 1)
& (journal_account.party == filters.get(args.party))
& (journal_account.account.isin(args.party_account))
)
.orderby(je.posting_date, je.name, order=Order.desc)
)
query = apply_common_conditions(filters, query, doctype="Journal Entry", payments=True)
journal_entries = query.run(as_dict=True)
return journal_entries
def get_payment_entries(filters, args):
pe = frappe.qb.DocType("Payment Entry")
query = (
frappe.qb.from_(pe)
.select(
ConstantColumn("Payment Entry").as_("doctype"),
pe.name,
pe.posting_date,
pe[args.account_fieldname].as_(args.account),
pe.party.as_(args.party),
pe.party_name.as_(args.party_name),
pe.remarks,
pe.paid_amount.as_("base_net_total"),
pe.paid_amount_after_tax.as_("base_grand_total"),
pe.mode_of_payment,
pe.project,
pe.cost_center,
)
.where(
(pe.docstatus == 1)
& (pe.party == filters.get(args.party))
& (pe[args.account_fieldname].isin(args.party_account))
)
.orderby(pe.posting_date, pe.name, order=Order.desc)
)
query = apply_common_conditions(filters, query, doctype="Payment Entry", payments=True)
payment_entries = query.run(as_dict=True)
return payment_entries
def apply_common_conditions(filters, query, doctype, child_doctype=None, payments=False):
parent_doc = frappe.qb.DocType(doctype)
if child_doctype:
child_doc = frappe.qb.DocType(child_doctype)
join_required = False
if filters.get("company"):
query = query.where(parent_doc.company == filters.company)
if filters.get("from_date"):
query = query.where(parent_doc.posting_date >= filters.from_date)
if filters.get("to_date"):
query = query.where(parent_doc.posting_date <= filters.to_date)
if payments:
if filters.get("cost_center"):
query = query.where(parent_doc.cost_center == filters.cost_center)
else:
if filters.get("cost_center"):
query = query.where(child_doc.cost_center == filters.cost_center)
join_required = True
if filters.get("warehouse"):
query = query.where(child_doc.warehouse == filters.warehouse)
join_required = True
if filters.get("item_group"):
query = query.where(child_doc.item_group == filters.item_group)
join_required = True
if not payments:
if filters.get("brand"):
query = query.where(child_doc.brand == filters.brand)
join_required = True
if join_required:
query = query.inner_join(child_doc).on(parent_doc.name == child_doc.parent)
query = query.distinct()
if parent_doc.get_table_name() != "tabJournal Entry":
query = filter_invoices_based_on_dimensions(filters, query, parent_doc)
return query
def get_advance_taxes_and_charges(invoice_list):
adv_taxes = frappe.qb.DocType("Advance Taxes and Charges")
return (
frappe.qb.from_(adv_taxes)
.select(
adv_taxes.parent,
adv_taxes.account_head,
(
frappe.qb.terms.Case()
.when(adv_taxes.add_deduct_tax == "Add", Sum(adv_taxes.base_tax_amount))
.else_(Sum(adv_taxes.base_tax_amount) * -1)
).as_("tax_amount"),
)
.where(
(adv_taxes.parent.isin([inv.name for inv in invoice_list]))
& (adv_taxes.charge_type.isin(["On Paid Amount", "Actual"]))
& (adv_taxes.base_tax_amount != 0)
)
.groupby(adv_taxes.parent, adv_taxes.account_head, adv_taxes.add_deduct_tax)
).run(as_dict=True)
def filter_invoices_based_on_dimensions(filters, query, parent_doc):
accounting_dimensions = get_accounting_dimensions(as_list=False)
if accounting_dimensions:
for dimension in accounting_dimensions:
if filters.get(dimension.fieldname):
if frappe.get_cached_value("DocType", dimension.document_type, "is_tree"):
filters[dimension.fieldname] = get_dimension_with_children(
dimension.document_type, filters.get(dimension.fieldname)
)
fieldname = dimension.fieldname
query = query.where(parent_doc[fieldname].isin(filters[fieldname]))
return query
def get_opening_row(party_type, party, from_date, company):
party_account = get_party_account(party_type, party, company)
gle = frappe.qb.DocType("GL Entry")
return (
frappe.qb.from_(gle)
.select(
ConstantColumn("Opening").as_("account"),
Sum(gle.debit).as_("debit"),
Sum(gle.credit).as_("credit"),
(Sum(gle.debit) - Sum(gle.credit)).as_("balance"),
)
.where(
(gle.account == party_account)
& (gle.party == party)
& (gle.posting_date < from_date)
& (gle.is_cancelled == 0)
)
).run(as_dict=True)

View File

@@ -22,6 +22,10 @@ class TestUtils(unittest.TestCase):
super(TestUtils, cls).setUpClass()
make_test_objects("Address", ADDRESS_RECORDS)
@classmethod
def tearDownClass(cls):
frappe.db.rollback()
def test_get_party_shipping_address(self):
address = get_party_shipping_address("Customer", "_Test Customer 1")
self.assertEqual(address, "_Test Billing Address 2 Title-Billing")
@@ -125,6 +129,38 @@ class TestUtils(unittest.TestCase):
self.assertEqual(len(payment_entry.references), 1)
self.assertEqual(payment_entry.difference_amount, 0)
def test_naming_series_variable_parsing(self):
"""
Tests parsing utility used by Naming Series Variable hook for FY
"""
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
from frappe.utils import nowdate
from erpnext.accounts.utils import get_fiscal_year
from erpnext.buying.doctype.supplier.test_supplier import create_supplier
# Configure Supplier Naming in Buying Settings
frappe.db.set_default("supp_master_name", "Auto Name")
# Configure Autoname in Supplier DocType
make_property_setter(
"Supplier", None, "naming_rule", "Expression", "Data", for_doctype="Doctype"
)
make_property_setter(
"Supplier", None, "autoname", "SUP-.FY.-.#####", "Data", for_doctype="Doctype"
)
fiscal_year = get_fiscal_year(nowdate())[0]
# Create Supplier
supplier = create_supplier()
# Check Naming Series in generated Supplier ID
doc_name = supplier.name.split("-")
self.assertEqual(len(doc_name), 3)
self.assertSequenceEqual(doc_name[0:2], ("SUP", fiscal_year))
frappe.db.set_default("supp_master_name", "Supplier Name")
ADDRESS_RECORDS = [
{

View File

@@ -10,7 +10,7 @@ import frappe.defaults
from frappe import _, qb, throw
from frappe.model.meta import get_field_precision
from frappe.query_builder import AliasedQuery, Criterion, Table
from frappe.query_builder.functions import Sum
from frappe.query_builder.functions import Round, Sum
from frappe.query_builder.utils import DocType
from frappe.utils import (
cint,
@@ -434,7 +434,19 @@ def add_cc(args=None):
return cc.name
def reconcile_against_document(args, skip_ref_details_update_for_pe=False): # nosemgrep
def _build_dimensions_dict_for_exc_gain_loss(
entry: dict | object = None, active_dimensions: list = None
):
dimensions_dict = frappe._dict()
if entry and active_dimensions:
for dim in active_dimensions:
dimensions_dict[dim.fieldname] = entry.get(dim.fieldname)
return dimensions_dict
def reconcile_against_document(
args, skip_ref_details_update_for_pe=False, active_dimensions=None
): # nosemgrep
"""
Cancel PE or JV, Update against document, split if required and resubmit
"""
@@ -459,6 +471,8 @@ def reconcile_against_document(args, skip_ref_details_update_for_pe=False): # n
check_if_advance_entry_modified(entry)
validate_allocated_amount(entry)
dimensions_dict = _build_dimensions_dict_for_exc_gain_loss(entry, active_dimensions)
# update ref in advance entry
if voucher_type == "Journal Entry":
referenced_row = update_reference_in_journal_entry(entry, doc, do_not_save=False)
@@ -466,10 +480,14 @@ def reconcile_against_document(args, skip_ref_details_update_for_pe=False): # n
# amount and account in args
# referenced_row is used to deduplicate gain/loss journal
entry.update({"referenced_row": referenced_row})
doc.make_exchange_gain_loss_journal([entry])
doc.make_exchange_gain_loss_journal([entry], dimensions_dict)
else:
update_reference_in_payment_entry(
entry, doc, do_not_save=True, skip_ref_details_update_for_pe=skip_ref_details_update_for_pe
referenced_row = update_reference_in_payment_entry(
entry,
doc,
do_not_save=True,
skip_ref_details_update_for_pe=skip_ref_details_update_for_pe,
dimensions_dict=dimensions_dict,
)
doc.save(ignore_permissions=True)
@@ -531,16 +549,19 @@ def check_if_advance_entry_modified(args):
args,
)
else:
ret = frappe.db.sql(
"""select name from `tabPayment Entry`
where
name = %(voucher_no)s and docstatus = 1
and party_type = %(party_type)s and party = %(party)s and {0} = %(account)s
and round(unallocated_amount, {1}) = round(%(unreconciled_amount)s, {1})
""".format(
party_account_field, precision
),
args,
pe = qb.DocType("Payment Entry")
ret = (
qb.from_(pe)
.select(pe.name)
.where(
(pe.name == args.voucher_no)
& (pe.docstatus == 1)
& (pe.party_type == args.party_type)
& (pe.party == args.party)
& (pe[party_account_field] == args.account)
& (Round(pe.unallocated_amount, precision) == Round(args.unreconciled_amount, precision))
)
.run()
)
if not ret:
@@ -618,7 +639,7 @@ def update_reference_in_journal_entry(d, journal_entry, do_not_save=False):
def update_reference_in_payment_entry(
d, payment_entry, do_not_save=False, skip_ref_details_update_for_pe=False
d, payment_entry, do_not_save=False, skip_ref_details_update_for_pe=False, dimensions_dict=None
):
reference_details = {
"reference_doctype": d.against_voucher_type,
@@ -630,6 +651,8 @@ def update_reference_in_payment_entry(
if d.difference_amount is not None
else payment_entry.get_exchange_rate(),
"exchange_gain_loss": d.difference_amount,
"account": d.account,
"dimensions": d.dimensions,
}
if d.voucher_detail_no:
@@ -659,10 +682,11 @@ def update_reference_in_payment_entry(
payment_entry.setup_party_account_field()
payment_entry.set_missing_values()
if not skip_ref_details_update_for_pe:
payment_entry.set_missing_ref_details()
payment_entry.set_missing_ref_details(ref_exchange_rate=d.exchange_rate or None)
payment_entry.set_amounts()
payment_entry.make_exchange_gain_loss_journal(
frappe._dict({"difference_posting_date": d.difference_posting_date})
frappe._dict({"difference_posting_date": d.difference_posting_date}), dimensions_dict
)
if not do_not_save:
@@ -1222,8 +1246,13 @@ def get_autoname_with_number(number_value, doc_title, company):
def parse_naming_series_variable(doc, variable):
if variable == "FY":
date = doc.get("posting_date") or doc.get("transaction_date") or getdate()
return get_fiscal_year(date=date, company=doc.get("company"))[0]
if doc:
date = doc.get("posting_date") or doc.get("transaction_date") or getdate()
company = doc.get("company")
else:
date = getdate()
company = None
return get_fiscal_year(date=date, company=company)[0]
@frappe.whitelist()
@@ -1986,6 +2015,7 @@ def create_gain_loss_journal(
ref2_dn,
ref2_detail_no,
cost_center,
dimensions,
) -> str:
journal_entry = frappe.new_doc("Journal Entry")
journal_entry.voucher_type = "Exchange Gain Or Loss"
@@ -2019,7 +2049,8 @@ def create_gain_loss_journal(
dr_or_cr + "_in_account_currency": 0,
}
)
if dimensions:
journal_account.update(dimensions)
journal_entry.append("accounts", journal_account)
journal_account = frappe._dict(
@@ -2035,7 +2066,8 @@ def create_gain_loss_journal(
reverse_dr_or_cr: abs(exc_gain_loss),
}
)
if dimensions:
journal_account.update(dimensions)
journal_entry.append("accounts", journal_account)
journal_entry.save()

View File

@@ -322,7 +322,7 @@ frappe.ui.form.on('Asset', {
},
make_schedules_editable: function(frm) {
if (frm.doc.finance_books.length) {
if (frm.doc.finance_books && frm.doc.finance_books.length) {
var is_manual_hence_editable = frm.doc.finance_books.filter(d => d.depreciation_method == "Manual").length > 0
? true : false;
var is_shift_hence_editable = frm.doc.finance_books.filter(d => d.shift_based).length > 0
@@ -519,10 +519,16 @@ frappe.ui.form.on('Asset', {
indicator: 'red'
});
}
frm.set_value('gross_purchase_amount', item.base_net_rate + item.item_tax_amount);
frm.set_value('purchase_receipt_amount', item.base_net_rate + item.item_tax_amount);
item.asset_location && frm.set_value('location', item.asset_location);
frm.set_value('cost_center', item.cost_center || purchase_doc.cost_center);
frappe.db.get_value('Item', item.item_code, 'is_grouped_asset', (r) => {
var asset_quantity = r.is_grouped_asset ? item.qty : 1;
var purchase_amount = flt(item.valuation_rate * asset_quantity, precision('gross_purchase_amount'));
frm.set_value('gross_purchase_amount', purchase_amount);
frm.set_value('purchase_receipt_amount', purchase_amount);
frm.set_value('asset_quantity', asset_quantity);
frm.set_value('cost_center', item.cost_center || purchase_doc.cost_center);
if(item.asset_location) { frm.set_value('location', item.asset_location); }
});
},
set_depreciation_rate: function(frm, row) {

View File

@@ -36,6 +36,7 @@
"purchase_invoice",
"available_for_use_date",
"total_asset_cost",
"additional_asset_cost",
"column_break_23",
"gross_purchase_amount",
"asset_quantity",
@@ -201,9 +202,8 @@
"fieldname": "purchase_date",
"fieldtype": "Date",
"label": "Purchase Date",
"read_only": 1,
"read_only_depends_on": "eval:!doc.is_existing_asset && !doc.is_composite_asset",
"reqd": 1
"mandatory_depends_on": "eval:!doc.is_existing_asset && !doc.is_composite_asset",
"read_only_depends_on": "eval:!doc.is_existing_asset && !doc.is_composite_asset"
},
{
"fieldname": "disposal_date",
@@ -226,15 +226,15 @@
"fieldname": "gross_purchase_amount",
"fieldtype": "Currency",
"label": "Gross Purchase Amount",
"mandatory_depends_on": "eval:(!doc.is_composite_asset || doc.docstatus==1)",
"options": "Company:company:default_currency",
"read_only_depends_on": "eval:!doc.is_existing_asset",
"reqd": 1
"read_only_depends_on": "eval:!doc.is_existing_asset"
},
{
"fieldname": "available_for_use_date",
"fieldtype": "Date",
"label": "Available-for-use Date",
"reqd": 1
"mandatory_depends_on": "eval:(!doc.is_composite_asset || doc.docstatus==1)"
},
{
"default": "0",
@@ -541,6 +541,14 @@
"label": "Total Asset Cost",
"options": "Company:company:default_currency",
"read_only": 1
},
{
"depends_on": "eval:doc.docstatus > 0",
"fieldname": "additional_asset_cost",
"fieldtype": "Currency",
"label": "Additional Asset Cost",
"options": "Company:company:default_currency",
"read_only": 1
}
],
"idx": 72,
@@ -574,7 +582,7 @@
"link_fieldname": "target_asset"
}
],
"modified": "2023-12-20 16:50:21.128595",
"modified": "2024-01-15 17:35:49.226603",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset",

View File

@@ -10,6 +10,7 @@ from frappe import _
from frappe.utils import (
add_days,
add_months,
add_years,
cint,
date_diff,
flt,
@@ -23,6 +24,7 @@ from frappe.utils import (
import erpnext
from erpnext.accounts.general_ledger import make_reverse_gl_entries
from erpnext.accounts.utils import get_fiscal_year
from erpnext.assets.doctype.asset.depreciation import (
get_depreciation_accounts,
get_disposal_account_and_cost_center,
@@ -48,6 +50,7 @@ class Asset(AccountsController):
if self.get("schedules"):
self.validate_expected_value_after_useful_life()
self.total_asset_cost = self.gross_purchase_amount
self.status = self.get_status()
def on_submit(self):
@@ -60,6 +63,7 @@ class Asset(AccountsController):
def on_cancel(self):
self.validate_cancellation()
self.cancel_movement_entries()
self.cancel_capitalization()
self.delete_depreciation_entries()
self.set_status()
self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry")
@@ -246,7 +250,12 @@ class Asset(AccountsController):
frappe.throw(_("Gross Purchase Amount is mandatory"), frappe.MandatoryError)
if is_cwip_accounting_enabled(self.asset_category):
if not self.is_existing_asset and not (self.purchase_receipt or self.purchase_invoice):
if (
not self.is_existing_asset
and not self.is_composite_asset
and not self.purchase_receipt
and not self.purchase_invoice
):
frappe.throw(
_("Please create purchase receipt or purchase invoice for the item {0}").format(
self.item_code
@@ -259,7 +268,7 @@ class Asset(AccountsController):
and not frappe.db.get_value("Purchase Invoice", self.purchase_invoice, "update_stock")
):
frappe.throw(
_("Update stock must be enable for the purchase invoice {0}").format(self.purchase_invoice)
_("Update stock must be enabled for the purchase invoice {0}").format(self.purchase_invoice)
)
if not self.calculate_depreciation:
@@ -374,14 +383,24 @@ class Asset(AccountsController):
should_get_last_day = is_last_day_of_the_month(finance_book.depreciation_start_date)
depreciation_amount = 0
number_of_pending_depreciations = final_number_of_depreciations - start[finance_book.idx - 1]
yearly_opening_wdv = value_after_depreciation
current_fiscal_year_end_date = None
for n in range(start[finance_book.idx - 1], final_number_of_depreciations):
# If depreciation is already completed (for double declining balance)
if skip_row:
continue
schedule_date = add_months(
finance_book.depreciation_start_date, n * cint(finance_book.frequency_of_depreciation)
)
if not current_fiscal_year_end_date:
current_fiscal_year_end_date = get_fiscal_year(finance_book.depreciation_start_date)[2]
elif getdate(schedule_date) > getdate(current_fiscal_year_end_date):
current_fiscal_year_end_date = add_years(current_fiscal_year_end_date, 1)
yearly_opening_wdv = value_after_depreciation
if n > 0 and len(self.get("schedules")) > n - 1:
prev_depreciation_amount = self.get("schedules")[n - 1].depreciation_amount
else:
@@ -390,6 +409,7 @@ class Asset(AccountsController):
depreciation_amount = get_depreciation_amount(
self,
value_after_depreciation,
yearly_opening_wdv,
finance_book,
n,
prev_depreciation_amount,
@@ -427,10 +447,7 @@ class Asset(AccountsController):
n == 0
and (has_pro_rata or has_wdv_or_dd_non_yearly_pro_rata)
and not self.opening_accumulated_depreciation
and get_updated_rate_of_depreciation_for_wdv_and_dd(
self, value_after_depreciation, finance_book, False
)
== finance_book.rate_of_depreciation
and not self.flags.wdv_it_act_applied
):
from_date = add_days(
self.available_for_use_date, -1
@@ -490,17 +507,21 @@ class Asset(AccountsController):
if not depreciation_amount:
continue
value_after_depreciation -= flt(depreciation_amount, self.precision("gross_purchase_amount"))
value_after_depreciation = flt(
value_after_depreciation - flt(depreciation_amount),
self.precision("gross_purchase_amount"),
)
# Adjust depreciation amount in the last period based on the expected value after useful life
if finance_book.expected_value_after_useful_life and (
(
n == cint(final_number_of_depreciations) - 1
and value_after_depreciation != finance_book.expected_value_after_useful_life
)
or value_after_depreciation < finance_book.expected_value_after_useful_life
if (
n == cint(final_number_of_depreciations) - 1
and flt(value_after_depreciation) != flt(finance_book.expected_value_after_useful_life)
) or flt(value_after_depreciation) < flt(
finance_book.expected_value_after_useful_life
):
depreciation_amount += value_after_depreciation - finance_book.expected_value_after_useful_life
depreciation_amount += flt(value_after_depreciation) - flt(
finance_book.expected_value_after_useful_life
)
skip_row = True
if flt(depreciation_amount, self.precision("gross_purchase_amount")) > 0:
@@ -828,6 +849,16 @@ class Asset(AccountsController):
movement = frappe.get_doc("Asset Movement", movement.get("name"))
movement.cancel()
def cancel_capitalization(self):
asset_capitalization = frappe.db.get_value(
"Asset Capitalization",
{"target_asset": self.name, "docstatus": 1, "entry_type": "Capitalization"},
)
if asset_capitalization:
asset_capitalization = frappe.get_doc("Asset Capitalization", asset_capitalization)
asset_capitalization.cancel()
def delete_depreciation_entries(self):
if self.calculate_depreciation:
for d in self.get("schedules"):
@@ -1348,6 +1379,8 @@ def is_cwip_accounting_enabled(asset_category):
@frappe.whitelist()
def get_asset_value_after_depreciation(asset_name, finance_book=None):
asset = frappe.get_doc("Asset", asset_name)
if not asset.calculate_depreciation:
return flt(asset.value_after_depreciation)
return asset.get_value_after_depreciation(finance_book)
@@ -1364,6 +1397,7 @@ def get_total_days(date, frequency):
def get_depreciation_amount(
asset,
depreciable_value,
yearly_opening_wdv,
fb_row,
schedule_idx=0,
prev_depreciation_amount=0,
@@ -1377,26 +1411,17 @@ def get_depreciation_amount(
asset, fb_row, schedule_idx, number_of_pending_depreciations
)
else:
rate_of_depreciation = get_updated_rate_of_depreciation_for_wdv_and_dd(
asset, depreciable_value, fb_row
)
return get_wdv_or_dd_depr_amount(
asset,
fb_row,
depreciable_value,
rate_of_depreciation,
fb_row.frequency_of_depreciation,
yearly_opening_wdv,
schedule_idx,
prev_depreciation_amount,
has_wdv_or_dd_non_yearly_pro_rata,
)
@erpnext.allow_regional
def get_updated_rate_of_depreciation_for_wdv_and_dd(
asset, depreciable_value, fb_row, show_msg=True
):
return fb_row.rate_of_depreciation
def get_straight_line_or_manual_depr_amount(
asset, row, schedule_idx, number_of_pending_depreciations
):
@@ -1531,30 +1556,54 @@ def get_asset_shift_factors_map():
return dict(frappe.db.get_all("Asset Shift Factor", ["shift_name", "shift_factor"], as_list=True))
@erpnext.allow_regional
def get_wdv_or_dd_depr_amount(
asset,
fb_row,
depreciable_value,
rate_of_depreciation,
frequency_of_depreciation,
yearly_opening_wdv,
schedule_idx,
prev_depreciation_amount,
has_wdv_or_dd_non_yearly_pro_rata,
):
if cint(frequency_of_depreciation) == 12:
return flt(depreciable_value) * (flt(rate_of_depreciation) / 100)
return get_default_wdv_or_dd_depr_amount(
asset,
fb_row,
depreciable_value,
schedule_idx,
prev_depreciation_amount,
has_wdv_or_dd_non_yearly_pro_rata,
)
def get_default_wdv_or_dd_depr_amount(
asset,
fb_row,
depreciable_value,
schedule_idx,
prev_depreciation_amount,
has_wdv_or_dd_non_yearly_pro_rata,
):
if cint(fb_row.frequency_of_depreciation) == 12:
return flt(depreciable_value) * (flt(fb_row.rate_of_depreciation) / 100)
else:
if has_wdv_or_dd_non_yearly_pro_rata:
if schedule_idx == 0:
return flt(depreciable_value) * (flt(rate_of_depreciation) / 100)
elif schedule_idx % (12 / cint(frequency_of_depreciation)) == 1:
return flt(depreciable_value) * (flt(fb_row.rate_of_depreciation) / 100)
elif schedule_idx % (12 / cint(fb_row.frequency_of_depreciation)) == 1:
return (
flt(depreciable_value) * flt(frequency_of_depreciation) * (flt(rate_of_depreciation) / 1200)
flt(depreciable_value)
* flt(fb_row.frequency_of_depreciation)
* (flt(fb_row.rate_of_depreciation) / 1200)
)
else:
return prev_depreciation_amount
else:
if schedule_idx % (12 / cint(frequency_of_depreciation)) == 0:
if schedule_idx % (12 / cint(fb_row.frequency_of_depreciation)) == 0:
return (
flt(depreciable_value) * flt(frequency_of_depreciation) * (flt(rate_of_depreciation) / 1200)
flt(depreciable_value)
* flt(fb_row.frequency_of_depreciation)
* (flt(fb_row.rate_of_depreciation) / 1200)
)
else:
return prev_depreciation_amount

View File

@@ -19,6 +19,7 @@ from frappe.utils import (
from frappe.utils.data import get_link_to_form
from frappe.utils.user import get_users_with_role
import erpnext
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_checks_for_pl_and_bs_accounts,
)
@@ -473,6 +474,13 @@ def depreciate_asset(asset, date):
make_depreciation_entry(asset.name, date)
cancel_depreciation_entries(asset, date)
@erpnext.allow_regional
def cancel_depreciation_entries(asset, date):
pass
def reset_depreciation_schedule(asset, date):
if not asset.calculate_depreciation:
@@ -500,7 +508,7 @@ def modify_depreciation_schedule_for_asset_repairs(asset):
def reverse_depreciation_entry_made_after_disposal(asset, date):
if not asset.calculate_depreciation:
if not asset.calculate_depreciation or not asset.get("schedules"):
return
row = -1
@@ -512,7 +520,7 @@ def reverse_depreciation_entry_made_after_disposal(asset, date):
else:
row += 1
if schedule.schedule_date == date:
if schedule.schedule_date == date and schedule.journal_entry:
if not disposal_was_made_on_original_schedule_date(
asset, schedule, row, date
) or disposal_happens_in_the_future(date):

View File

@@ -845,7 +845,7 @@ class TestDepreciationMethods(AssetSetup):
["2030-12-31", 28630.14, 28630.14],
["2031-12-31", 35684.93, 64315.07],
["2032-12-31", 17842.47, 82157.54],
["2033-06-06", 5342.46, 87500.0],
["2033-06-06", 5342.47, 87500.01],
]
schedules = [
@@ -957,7 +957,7 @@ class TestDepreciationBasics(AssetSetup):
},
)
depreciation_amount = get_depreciation_amount(asset, 100000, asset.finance_books[0])
depreciation_amount = get_depreciation_amount(asset, 100000, 100000, asset.finance_books[0])
self.assertEqual(depreciation_amount, 30000)
def test_make_depreciation_schedule(self):

View File

@@ -7,6 +7,7 @@ frappe.provide("erpnext.assets");
erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.stock.StockController {
setup() {
this.setup_posting_date_time_check();
this.frm.ignore_doctypes_on_cancel_all = ["Asset Movement"];
}
onload() {
@@ -20,10 +21,10 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s
this.show_stock_ledger();
}
if (this.frm.doc.stock_items && !this.frm.doc.stock_items.length && this.frm.doc.target_asset && this.frm.doc.capitalization_method === "Choose a WIP composite asset") {
this.set_consumed_stock_items_tagged_to_wip_composite_asset(this.frm.doc.target_asset);
this.get_target_asset_details();
}
// if (this.frm.doc.stock_items && !this.frm.doc.stock_items.length && this.frm.doc.target_asset && this.frm.doc.capitalization_method === "Choose a WIP composite asset") {
// this.set_consumed_stock_items_tagged_to_wip_composite_asset(this.frm.doc.target_asset);
// this.get_target_asset_details();
// }
}
setup_queries() {
@@ -119,13 +120,20 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s
},
callback: function (r) {
if (!r.exc && r.message) {
me.frm.clear_table("stock_items");
for (let item of r.message) {
me.frm.add_child("stock_items", item);
if(r.message[0] && r.message[0].length) {
me.frm.clear_table("stock_items");
for (let item of r.message[0]) {
me.frm.add_child("stock_items", item);
}
refresh_field("stock_items");
}
if (r.message[1] && r.message[1].length) {
me.frm.clear_table("asset_items");
for (let item of r.message[1]) {
me.frm.add_child("asset_items", item);
}
me.frm.refresh_field("asset_items");
}
refresh_field("stock_items");
me.calculate_totals();
}

View File

@@ -72,11 +72,25 @@ class AssetCapitalization(StockController):
self.update_target_asset()
def on_cancel(self):
self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Repost Item Valuation")
self.ignore_linked_doctypes = (
"GL Entry",
"Stock Ledger Entry",
"Repost Item Valuation",
"Asset",
"Asset Movement",
)
self.cancel_target_asset()
self.update_stock_ledger()
self.make_gl_entries()
self.restore_consumed_asset_items()
def cancel_target_asset(self):
if self.entry_type == "Capitalization" and self.target_asset:
asset_doc = frappe.get_doc("Asset", self.target_asset)
asset_doc.db_set("capitalized_in", None)
if asset_doc.docstatus == 1:
asset_doc.cancel()
def set_title(self):
self.title = self.target_asset_name or self.target_item_name or self.target_item_code
@@ -782,7 +796,6 @@ def get_consumed_asset_details(args):
out.cost_center = get_default_cost_center(
args, item_defaults, item_group_defaults, brand_defaults
)
return out
@@ -830,10 +843,27 @@ def get_items_tagged_to_wip_composite_asset(asset):
"qty",
"valuation_rate",
"amount",
"is_fixed_asset",
"parent",
]
pr_items = frappe.get_all(
"Purchase Receipt Item", filters={"wip_composite_asset": asset}, fields=fields
"Purchase Receipt Item", filters={"wip_composite_asset": asset, "docstatus": 1}, fields=fields
)
return pr_items
stock_items = []
asset_items = []
for d in pr_items:
if not d.is_fixed_asset:
stock_items.append(frappe._dict(d))
else:
asset_details = frappe.db.get_value(
"Asset",
{"item_code": d.item_code, "purchase_receipt": d.parent},
["name as asset", "asset_name"],
as_dict=1,
)
d.update(asset_details)
asset_items.append(frappe._dict(d))
return stock_items, asset_items

View File

@@ -67,12 +67,12 @@ class AssetCategory(Document):
if selected_key_type not in expected_key_types:
frappe.throw(
_(
"Row #{}: {} of {} should be {}. Please modify the account or select a different account."
"Row #{0}: {1} of {2} should be {3}. Please update the {1} or select a different account."
).format(
d.idx,
frappe.unscrub(key_to_match),
frappe.bold(selected_account),
frappe.bold(expected_key_types),
frappe.bold(" or ".join(expected_key_types)),
),
title=_("Invalid Account"),
)

View File

@@ -90,7 +90,6 @@
},
{
"default": "0",
"depends_on": "eval:doc.depreciation_method == \"Straight Line\" || doc.depreciation_method == \"Manual\"",
"fieldname": "daily_prorata_based",
"fieldtype": "Check",
"label": "Depreciate based on daily pro-rata"
@@ -106,7 +105,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2023-11-29 03:53:03.591098",
"modified": "2023-12-29 08:49:39.876439",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset Finance Book",

View File

@@ -48,6 +48,7 @@ class AssetRepair(AccountsController):
if self.capitalize_repair_cost:
self.asset_doc.total_asset_cost += self.repair_cost
self.asset_doc.additional_asset_cost += self.repair_cost
if self.get("stock_consumption"):
self.check_for_stock_items_and_warehouse()
@@ -73,6 +74,7 @@ class AssetRepair(AccountsController):
if self.capitalize_repair_cost:
self.asset_doc.total_asset_cost -= self.repair_cost
self.asset_doc.additional_asset_cost -= self.repair_cost
if self.get("stock_consumption"):
self.increase_stock_quantity()

View File

@@ -188,7 +188,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2023-11-24 10:55:51.287327",
"modified": "2024-01-12 16:42:01.894346",
"modified_by": "Administrator",
"module": "Buying",
"name": "Buying Settings",
@@ -212,10 +212,30 @@
"role": "Purchase Manager",
"share": 1,
"write": 1
},
{
"read": 1,
"role": "Accounts User"
},
{
"read": 1,
"role": "Accounts Manager"
},
{
"read": 1,
"role": "Stock Manager"
},
{
"read": 1,
"role": "Stock User"
},
{
"read": 1,
"role": "Purchase User"
}
],
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}
}

View File

@@ -317,6 +317,7 @@ class PurchaseOrder(BuyingController):
self.update_requested_qty()
self.update_ordered_qty()
self.update_reserved_qty_for_subcontract()
self.update_blanket_order()
self.notify_update()
clear_doctype_notifications(self)
@@ -456,6 +457,7 @@ class PurchaseOrder(BuyingController):
)
@frappe.request_cache
def item_last_purchase_rate(name, conversion_rate, item_code, conversion_factor=1.0):
"""get last purchase rate for an item"""

View File

@@ -814,6 +814,30 @@ class TestPurchaseOrder(FrappeTestCase):
# To test if the PO does NOT have a Blanket Order
self.assertEqual(po_doc.items[0].blanket_order, None)
def test_blanket_order_on_po_close_and_open(self):
# Step - 1: Create Blanket Order
bo = make_blanket_order(blanket_order_type="Purchasing", quantity=10, rate=10)
# Step - 2: Create Purchase Order
po = create_purchase_order(
item_code="_Test Item", qty=5, against_blanket_order=1, against_blanket=bo.name
)
bo.load_from_db()
self.assertEqual(bo.items[0].ordered_qty, 5)
# Step - 3: Close Purchase Order
po.update_status("Closed")
bo.load_from_db()
self.assertEqual(bo.items[0].ordered_qty, 0)
# Step - 4: Re-Open Purchase Order
po.update_status("Re-open")
bo.load_from_db()
self.assertEqual(bo.items[0].ordered_qty, 5)
def test_payment_terms_are_fetched_when_creating_purchase_invoice(self):
from erpnext.accounts.doctype.payment_entry.test_payment_entry import (
create_payment_terms_template,
@@ -916,6 +940,38 @@ class TestPurchaseOrder(FrappeTestCase):
self.assertRaises(frappe.ValidationError, po.save)
def test_po_billed_amount_against_return_entry(self):
from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import make_debit_note
# Create a Purchase Order and Fully Bill it
po = create_purchase_order()
pi = make_pi_from_po(po.name)
pi.insert()
pi.submit()
# Debit Note - 50% Qty & enable updating PO billed amount
pi_return = make_debit_note(pi.name)
pi_return.items[0].qty = -5
pi_return.update_billed_amount_in_purchase_order = 1
pi_return.submit()
# Check if the billed amount reduced
po.reload()
self.assertEqual(po.per_billed, 50)
pi_return.reload()
pi_return.cancel()
# Debit Note - 50% Qty & disable updating PO billed amount
pi_return = make_debit_note(pi.name)
pi_return.items[0].qty = -5
pi_return.update_billed_amount_in_purchase_order = 0
pi_return.submit()
# Check if the billed amount stayed the same
po.reload()
self.assertEqual(po.per_billed, 100)
def prepare_data_for_internal_transfer():
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier
@@ -1016,6 +1072,7 @@ def create_purchase_order(**args):
"schedule_date": add_days(nowdate(), 1),
"include_exploded_items": args.get("include_exploded_items", 1),
"against_blanket_order": args.against_blanket_order,
"against_blanket": args.against_blanket,
"material_request": args.material_request,
"material_request_item": args.material_request_item,
},

View File

@@ -123,8 +123,7 @@
"oldfieldname": "item_code",
"oldfieldtype": "Link",
"options": "Item",
"reqd": 1,
"search_index": 1
"reqd": 1
},
{
"fieldname": "supplier_part_no",
@@ -547,7 +546,6 @@
"fieldname": "blanket_order",
"fieldtype": "Link",
"label": "Blanket Order",
"no_copy": 1,
"options": "Blanket Order"
},
{
@@ -555,7 +553,6 @@
"fieldname": "blanket_order_rate",
"fieldtype": "Currency",
"label": "Blanket Order Rate",
"no_copy": 1,
"print_hide": 1,
"read_only": 1
},
@@ -919,7 +916,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2023-11-24 19:07:34.921094",
"modified": "2024-02-05 11:23:24.859435",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order Item",

View File

@@ -7,6 +7,8 @@ import json
import frappe
from frappe import _, bold, qb, throw
from frappe.model.workflow import get_workflow_name, is_transition_condition_satisfied
from frappe.query_builder import Criterion
from frappe.query_builder.custom import ConstantColumn
from frappe.query_builder.functions import Abs, Sum
from frappe.utils import (
add_days,
@@ -26,6 +28,7 @@ from frappe.utils import (
import erpnext
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_accounting_dimensions,
get_dimensions,
)
from erpnext.accounts.doctype.pricing_rule.utils import (
apply_pricing_rule_for_free_items,
@@ -42,6 +45,7 @@ from erpnext.accounts.party import (
from erpnext.accounts.utils import (
create_gain_loss_journal,
get_account_currency,
get_currency_precision,
get_fiscal_years,
validate_fiscal_year,
)
@@ -184,6 +188,7 @@ class AccountsController(TransactionBase):
self.validate_party()
self.validate_currency()
self.validate_party_account_currency()
self.validate_return_against_account()
if self.doctype in ["Purchase Invoice", "Sales Invoice"]:
if invalid_advances := [
@@ -197,6 +202,19 @@ class AccountsController(TransactionBase):
)
)
if self.get("is_return") and self.get("return_against") and not self.get("is_pos"):
# if self.get("is_return") and self.get("return_against"):
document_type = "Credit Note" if self.doctype == "Sales Invoice" else "Debit Note"
frappe.msgprint(
_(
"{0} will be treated as a standalone {0}. Post creation use {1} tool to reconcile against {2}."
).format(
document_type,
get_link_to_form("Payment Reconciliation", "Payment Reconciliation"),
get_link_to_form(self.doctype, self.get("return_against")),
)
)
pos_check_field = "is_pos" if self.doctype == "Sales Invoice" else "is_paid"
if cint(self.allocate_advances_automatically) and not cint(self.get(pos_check_field)):
self.set_advances()
@@ -308,6 +326,12 @@ class AccountsController(TransactionBase):
ple = frappe.qb.DocType("Payment Ledger Entry")
frappe.qb.from_(ple).delete().where(
(ple.voucher_type == self.doctype) & (ple.voucher_no == self.name)
| (
(ple.against_voucher_type == self.doctype)
& (ple.against_voucher_no == self.name)
& ple.delinked
== 1
)
).run()
frappe.db.sql(
"delete from `tabGL Entry` where voucher_type=%s and voucher_no=%s", (self.doctype, self.name)
@@ -317,6 +341,20 @@ class AccountsController(TransactionBase):
(self.doctype, self.name),
)
def validate_return_against_account(self):
if (
self.doctype in ["Sales Invoice", "Purchase Invoice"] and self.is_return and self.return_against
):
cr_dr_account_field = "debit_to" if self.doctype == "Sales Invoice" else "credit_to"
cr_dr_account_label = "Debit To" if self.doctype == "Sales Invoice" else "Credit To"
cr_dr_account = self.get(cr_dr_account_field)
if frappe.get_value(self.doctype, self.return_against, cr_dr_account_field) != cr_dr_account:
frappe.throw(
_("'{0}' account: '{1}' should match the Return Against Invoice").format(
frappe.bold(cr_dr_account_label), frappe.bold(cr_dr_account)
)
)
def validate_deferred_income_expense_account(self):
field_map = {
"Sales Invoice": "deferred_revenue_account",
@@ -653,7 +691,7 @@ class AccountsController(TransactionBase):
if self.get("is_subcontracted"):
args["is_subcontracted"] = self.is_subcontracted
ret = get_item_details(args, self, for_validate=True, overwrite_warehouse=False)
ret = get_item_details(args, self, for_validate=for_validate, overwrite_warehouse=False)
for fieldname, value in ret.items():
if item.meta.get_field(fieldname) and value is not None:
@@ -1118,7 +1156,9 @@ class AccountsController(TransactionBase):
return True
return False
def make_exchange_gain_loss_journal(self, args: dict = None) -> None:
def make_exchange_gain_loss_journal(
self, args: dict = None, dimensions_dict: dict = None
) -> None:
"""
Make Exchange Gain/Loss journal for Invoices and Payments
"""
@@ -1130,10 +1170,12 @@ class AccountsController(TransactionBase):
# These are generated by Sales/Purchase Invoice during reconciliation and advance allocation.
# and below logic is only for such scenarios
if args:
precision = get_currency_precision()
for arg in args:
# Advance section uses `exchange_gain_loss` and reconciliation uses `difference_amount`
if (
arg.get("difference_amount", 0) != 0 or arg.get("exchange_gain_loss", 0) != 0
flt(arg.get("difference_amount", 0), precision) != 0
or flt(arg.get("exchange_gain_loss", 0), precision) != 0
) and arg.get("difference_account"):
party_account = arg.get("account")
@@ -1173,6 +1215,7 @@ class AccountsController(TransactionBase):
self.name,
arg.get("referenced_row"),
arg.get("cost_center"),
dimensions_dict,
)
frappe.msgprint(
_("Exchange Gain/Loss amount has been booked through {0}").format(
@@ -1253,6 +1296,7 @@ class AccountsController(TransactionBase):
self.name,
d.idx,
self.cost_center,
dimensions_dict,
)
frappe.msgprint(
_("Exchange Gain/Loss amount has been booked through {0}").format(
@@ -1344,15 +1388,46 @@ class AccountsController(TransactionBase):
if lst:
from erpnext.accounts.utils import reconcile_against_document
reconcile_against_document(lst)
# pass dimension values to utility method
active_dimensions = get_dimensions()[0]
for x in lst:
for dim in active_dimensions:
if self.get(dim.fieldname):
x.update({dim.fieldname: self.get(dim.fieldname)})
reconcile_against_document(lst, active_dimensions=active_dimensions)
def cancel_system_generated_credit_debit_notes(self):
# Cancel 'Credit/Debit' Note Journal Entries, if found.
if self.doctype in ["Sales Invoice", "Purchase Invoice"]:
voucher_type = "Credit Note" if self.doctype == "Sales Invoice" else "Debit Note"
journals = frappe.db.get_all(
"Journal Entry",
filters={
"is_system_generated": 1,
"reference_type": self.doctype,
"reference_name": self.name,
"voucher_type": voucher_type,
"docstatus": 1,
},
pluck="name",
)
for x in journals:
frappe.get_doc("Journal Entry", x).cancel()
def on_cancel(self):
from erpnext.accounts.doctype.bank_transaction.bank_transaction import (
remove_from_bank_transaction,
)
from erpnext.accounts.utils import (
cancel_exchange_gain_loss_journal,
unlink_ref_doc_from_payment_entries,
)
remove_from_bank_transaction(self.doctype, self.name)
if self.doctype in ["Sales Invoice", "Purchase Invoice", "Payment Entry", "Journal Entry"]:
self.cancel_system_generated_credit_debit_notes()
# Cancel Exchange Gain/Loss Journal before unlinking
cancel_exchange_gain_loss_journal(self)
@@ -2365,6 +2440,7 @@ def validate_taxes_and_charges(tax):
def validate_account_head(idx, account, company, context=""):
account_company = frappe.get_cached_value("Account", account, "company")
is_group = frappe.get_cached_value("Account", account, "is_group")
if account_company != company:
frappe.throw(
@@ -2374,6 +2450,12 @@ def validate_account_head(idx, account, company, context=""):
title=_("Invalid Account"),
)
if is_group:
frappe.throw(
_("Row {0}: Account {1} is a Group Account").format(idx, frappe.bold(account)),
title=_("Invalid Account"),
)
def validate_cost_center(tax, doc):
if not tax.cost_center:
@@ -2518,6 +2600,9 @@ def get_advance_payment_entries(
condition=None,
payment_name=None,
):
pe = qb.DocType("Payment Entry")
per = qb.DocType("Payment Entry Reference")
party_account_field = "paid_from" if party_type == "Customer" else "paid_to"
currency_field = (
"paid_from_account_currency" if party_type == "Customer" else "paid_to_account_currency"
@@ -2528,76 +2613,79 @@ def get_advance_payment_entries(
)
payment_entries_against_order, unallocated_payment_entries = [], []
limit_cond = "limit %s" % limit if limit else ""
if not condition:
condition = []
if payment_name:
condition.append(pe.name.like(f"%%{payment_name}%%"))
if order_list or against_all_orders:
orders_condition = []
if order_list:
reference_condition = " and t2.reference_name in ({0})".format(
", ".join(["%s"] * len(order_list))
orders_condition.append(per.reference_name.isin(order_list))
payment_entries_query = (
qb.from_(pe)
.inner_join(per)
.on(pe.name == per.parent)
.select(
ConstantColumn("Payment Entry").as_("reference_type"),
pe.name.as_("reference_name"),
pe.remarks,
per.allocated_amount.as_("amount"),
per.name.as_("reference_row"),
per.reference_name.as_("against_order"),
pe.posting_date,
pe[currency_field].as_("currency"),
pe[exchange_rate_field].as_("exchange_rate"),
)
else:
reference_condition = ""
order_list = []
payment_name_filter = ""
if payment_name:
payment_name_filter = " and t1.name like '%%{0}%%'".format(payment_name)
if not condition:
condition = ""
payment_entries_against_order = frappe.db.sql(
"""
select
'Payment Entry' as reference_type, t1.name as reference_name,
t1.remarks, t2.allocated_amount as amount, t2.name as reference_row,
t2.reference_name as against_order, t1.posting_date,
t1.{0} as currency, t1.{5} as exchange_rate
from `tabPayment Entry` t1, `tabPayment Entry Reference` t2
where
t1.name = t2.parent and t1.{1} = %s and t1.payment_type = %s
and t1.party_type = %s and t1.party = %s and t1.docstatus = 1
and t2.reference_doctype = %s {2} {3} {6}
order by t1.posting_date {4}
""".format(
currency_field,
party_account_field,
reference_condition,
condition,
limit_cond,
exchange_rate_field,
payment_name_filter,
),
[party_account, payment_type, party_type, party, order_doctype] + order_list,
as_dict=1,
.where(
(pe[party_account_field] == party_account)
& (pe.payment_type == payment_type)
& (pe.party_type == party_type)
& (pe.party == party)
& (pe.docstatus == 1)
& (per.reference_doctype == order_doctype)
)
.where(Criterion.all(condition))
.where(Criterion.all(orders_condition))
.orderby(pe.posting_date)
)
if limit:
payment_entries_query = payment_entries_query.limit(limit)
payment_entries_against_order = payment_entries_query.run(as_dict=1)
if include_unallocated:
payment_name_filter = ""
if payment_name:
payment_name_filter = " and name like '%%{0}%%'".format(payment_name)
unallocated_payment_entries = frappe.db.sql(
"""
select 'Payment Entry' as reference_type, name as reference_name, posting_date,
remarks, unallocated_amount as amount, {2} as exchange_rate, {3} as currency
from `tabPayment Entry`
where
{0} = %s and party_type = %s and party = %s and payment_type = %s
and docstatus = 1 and unallocated_amount > 0 {condition} {4}
order by posting_date {1}
""".format(
party_account_field,
limit_cond,
exchange_rate_field,
currency_field,
payment_name_filter,
condition=condition or "",
),
(party_account, party_type, party, payment_type),
as_dict=1,
unallocated_payment_query = (
qb.from_(pe)
.select(
ConstantColumn("Payment Entry").as_("reference_type"),
pe.name.as_("reference_name"),
pe.posting_date,
pe.remarks,
pe.unallocated_amount.as_("amount"),
pe[exchange_rate_field].as_("exchange_rate"),
pe[currency_field].as_("currency"),
)
.where(
(pe[party_account_field] == party_account)
& (pe.party_type == party_type)
& (pe.party == party)
& (pe.payment_type == payment_type)
& (pe.docstatus == 1)
& (pe.unallocated_amount.gt(0))
)
.where(Criterion.all(condition))
.orderby(pe.posting_date)
)
if limit:
unallocated_payment_query = unallocated_payment_query.limit(limit)
unallocated_payment_entries = unallocated_payment_query.run(as_dict=1)
return list(payment_entries_against_order) + list(unallocated_payment_entries)

View File

@@ -190,8 +190,8 @@ class BuyingController(SubcontractingController):
lc_voucher_data = frappe.db.sql(
"""select sum(applicable_charges), cost_center
from `tabLanded Cost Item`
where docstatus = 1 and purchase_receipt_item = %s""",
d.name,
where docstatus = 1 and purchase_receipt_item = %s and receipt_document = %s""",
(d.name, self.name),
)
d.landed_cost_voucher_amount = lc_voucher_data[0][0] if lc_voucher_data else 0.0
if not d.cost_center and lc_voucher_data and lc_voucher_data[0][1]:
@@ -730,11 +730,8 @@ class BuyingController(SubcontractingController):
item_data = frappe.db.get_value(
"Item", row.item_code, ["asset_naming_series", "asset_category"], as_dict=1
)
if is_grouped_asset:
purchase_amount = flt(row.base_amount + row.item_tax_amount)
else:
purchase_amount = flt(row.base_rate + row.item_tax_amount)
asset_quantity = row.qty if is_grouped_asset else 1
purchase_amount = flt(row.valuation_rate) * asset_quantity
asset = frappe.get_doc(
{
@@ -750,7 +747,7 @@ class BuyingController(SubcontractingController):
"calculate_depreciation": 1,
"purchase_receipt_amount": purchase_amount,
"gross_purchase_amount": purchase_amount,
"asset_quantity": row.qty if is_grouped_asset else 1,
"asset_quantity": asset_quantity,
"purchase_receipt": self.name if self.doctype == "Purchase Receipt" else None,
"purchase_invoice": self.name if self.doctype == "Purchase Invoice" else None,
"cost_center": row.cost_center,
@@ -814,7 +811,8 @@ class BuyingController(SubcontractingController):
if self.doctype == "Purchase Invoice" and not self.get("update_stock"):
return
frappe.db.sql("delete from `tabAsset Movement` where reference_name=%s", self.name)
asset_movement = frappe.db.get_value("Asset Movement", {"reference_name": self.name}, "name")
frappe.delete_doc("Asset Movement", asset_movement, force=1)
def validate_schedule_date(self):
if not self.get("items"):

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