Compare commits

..

551 Commits

Author SHA1 Message Date
mergify[bot]
bd7e5b3e05 refactor: use consistent report column names (backport #54451) (backport #54478) (#54506)
* refactor: use consistent report column names

(cherry picked from commit 7630c01e40)

# Conflicts:
#	erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py
(cherry picked from commit ab188c4404)

* refactor: better label for entity type

(cherry picked from commit 8e12bda108)
(cherry picked from commit e1ff203f95)

* fix: add party_type for dynamic link and add it to grouping key

(cherry picked from commit a3ad1fb163)
(cherry picked from commit 6eb2868a15)

* fix: use key consistently

(cherry picked from commit 8f9a5e6c0c)
(cherry picked from commit 32d46b3e88)

* chore: resolve conflicts

(cherry picked from commit 34e94d6e7a)

* chore: translate values correctly

(cherry picked from commit 83fd655042)

---------

Co-authored-by: Smit Vora <smitvora203@gmail.com>
2026-04-24 20:01:36 +05:30
Frappe PR Bot
adf92a8292 chore(release): Bumped to Version 14.92.14
## [14.92.14](https://github.com/frappe/erpnext/compare/v14.92.13...v14.92.14) (2026-04-10)

### Bug Fixes

* added exception handling on service level agreement apply hook ([#50096](https://github.com/frappe/erpnext/issues/50096)) ([#54192](https://github.com/frappe/erpnext/issues/54192)) ([12b2788](12b27883f6))
2026-04-10 11:53:37 +00:00
diptanilsaha
df6dc540f8 Merge pull request #54202 from frappe/mergify/bp/version-14/pr-54192
fix: added exception handling on service level agreement apply hook (#50096) (backport #54192)
2026-04-10 17:22:07 +05:30
diptanilsaha
12b27883f6 fix: added exception handling on service level agreement apply hook (#50096) (#54192)
Co-authored-by: Ankush Menat <ankush@frappe.io>
(cherry picked from commit 21311dab86)
2026-04-10 11:33:06 +00:00
Frappe PR Bot
5bbfb79cdc chore(release): Bumped to Version 14.92.13
## [14.92.13](https://github.com/frappe/erpnext/compare/v14.92.12...v14.92.13) (2026-03-21)

### Bug Fixes

* allow zero valuation rate ([7a98e13](7a98e13d7d))
2026-03-21 08:34:35 +00:00
rohitwaghchaure
266ac2d5be Merge pull request #53637 from frappe/mergify/bp/version-14/pr-53635
fix: allow zero valuation rate (backport #53635)
2026-03-21 14:03:04 +05:30
Rohit Waghchaure
7a98e13d7d fix: allow zero valuation rate
(cherry picked from commit a6eadb18c9)
2026-03-19 08:32:50 +00:00
rohitwaghchaure
aeee42e857 Merge pull request #52349 from frappe/version-14-hotfix
chore: release v14
2026-02-03 22:48:16 +05:30
Mihir Kandoi
e1674d2017 Merge pull request #51658 from mihir-kandoi/eol-msg 2026-01-30 17:09:28 +05:30
Mihir Kandoi
65d7c6b882 chore: rename filename 2026-01-30 17:08:48 +05:30
Frappe PR Bot
966f262e38 chore(release): Bumped to Version 14.92.12
## [14.92.12](https://github.com/frappe/erpnext/compare/v14.92.11...v14.92.12) (2026-01-27)

### Bug Fixes

* **stock:** remove limit filter while fetching batch and bin ([07ac93d](07ac93d06a))
2026-01-27 14:42:02 +00:00
ruthra kumar
44f81092b6 Merge pull request #52105 from frappe/version-14-hotfix
chore: release v14
2026-01-27 20:09:29 +05:30
rohitwaghchaure
6f1616bc95 Merge pull request #51939 from aerele/fix/pick-list-qty-validation
fix(stock): remove limit filter while fetching batch and bin
2026-01-23 19:36:54 +05:30
Sudharsanan11
07ac93d06a fix(stock): remove limit filter while fetching batch and bin 2026-01-20 23:12:13 +05:30
Frappe PR Bot
34499d7f77 chore(release): Bumped to Version 14.92.11
## [14.92.11](https://github.com/frappe/erpnext/compare/v14.92.10...v14.92.11) (2026-01-20)

### Bug Fixes

* Show non-SLE vouchers with GL entries in Stock vs Account Value Comparison report ([6e03d45](6e03d45034))
* **transaction.js:** use flt instead of cint for plc_conversion_rate ([d09de53](d09de53d1d))
2026-01-20 16:37:18 +00:00
ruthra kumar
2967de1999 Merge pull request #51910 from frappe/version-14-hotfix
chore: release v14
2026-01-20 22:05:36 +05:30
ruthra kumar
824beaa893 Merge pull request #51837 from frappe/mergify/bp/version-14-hotfix/pr-51787
fix: recalculate taxes when item tax template changes after discount (backport #51787)
2026-01-19 14:37:18 +05:30
ljain112
b4dc1be5ed chore: resolve conflicts 2026-01-19 13:45:11 +05:30
Lakshit Jain
cf49c32b94 Merge pull request #51787 from ljain112/fix-taxes-disc
fix: recalculate taxes when item tax template changes after discount
(cherry picked from commit f00aeec9b4)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
#	erpnext/controllers/taxes_and_totals.py
2026-01-19 07:01:36 +00:00
rohitwaghchaure
d54f45469a Merge pull request #51770 from frappe/mergify/bp/version-14-hotfix/pr-51768
fix: Show non-SLE vouchers with GL entries in Stock vs Account Value … (backport #51768)
2026-01-15 20:59:58 +05:30
rohitwaghchaure
ad5181c52f chore: fix conflicts 2026-01-15 19:28:44 +05:30
Rohit Waghchaure
6e03d45034 fix: Show non-SLE vouchers with GL entries in Stock vs Account Value Comparison report
(cherry picked from commit 1db9ce205f)

# Conflicts:
#	erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py
2026-01-15 12:20:32 +00:00
Diptanil Saha
7a18f86dd5 Merge pull request #51746 from frappe/mergify/bp/version-14-hotfix/pr-51730
fix(transaction.js): use flt instead of cint for plc_conversion_rate (backport #51730)
2026-01-14 15:56:06 +05:30
Diptanil Saha
39e1bddce6 chore: resolve conflict 2026-01-14 15:53:18 +05:30
diptanilsaha
d09de53d1d fix(transaction.js): use flt instead of cint for plc_conversion_rate
(cherry picked from commit 8b445e04e5)

# Conflicts:
#	erpnext/public/js/controllers/transaction.js
2026-01-14 10:22:04 +00:00
Frappe PR Bot
8035940263 chore(release): Bumped to Version 14.92.10
## [14.92.10](https://github.com/frappe/erpnext/compare/v14.92.9...v14.92.10) (2026-01-13)

### Bug Fixes

* don't duplicate default income account to Item ([#50413](https://github.com/frappe/erpnext/issues/50413)) ([55d2a54](55d2a54785)), closes [#48231](https://github.com/frappe/erpnext/issues/48231)
* negative stock issue for higher precision ([21d1385](21d13859a0))
2026-01-13 14:44:57 +00:00
ruthra kumar
c69f3d330a Merge pull request #51712 from frappe/version-14-hotfix
chore: release v14
2026-01-13 20:13:30 +05:30
Mihir Kandoi
90d88b1c26 chore: fix grammar 2026-01-13 11:00:42 +05:30
Mihir Kandoi
22eec09175 chore: update links 2026-01-13 10:52:51 +05:30
ruthra kumar
dbfd4ea4d3 Merge pull request #51331 from frappe/mergify/bp/version-14-hotfix/pr-50413
fix: don't duplicate default income account to Item (backport #50413)
2026-01-12 20:34:03 +05:30
barredterra
8d28dcb18b chore: resolve conflicts 2026-01-11 17:59:27 +01:00
Mihir Kandoi
3f186b24f3 chore: EOL announcement for v14 2026-01-10 22:44:44 +05:30
rohitwaghchaure
4b0ddbf73a Merge pull request #51591 from frappe/mergify/bp/version-14-hotfix/pr-51588
fix: negative stock issue for higher precision (backport #51586) (backport #51588)
2026-01-08 16:11:20 +05:30
rohitwaghchaure
cddf1e1ee5 chore: fix conflicts
Refactor delivery note tests to improve clarity and organization. Remove redundant test cases and ensure proper valuation checks for batch and serial items.
2026-01-08 15:18:53 +05:30
rohitwaghchaure
22e5aba02b chore: fix conflicts
Refactor test cases for delivery notes to handle negative stock and higher precision.

(cherry picked from commit 5193dbba9b)
2026-01-08 09:35:44 +00:00
Rohit Waghchaure
21d13859a0 fix: negative stock issue for higher precision
(cherry picked from commit 87be020c78)

# Conflicts:
#	erpnext/stock/doctype/delivery_note/test_delivery_note.py
(cherry picked from commit 1bbeecff12)

# Conflicts:
#	erpnext/stock/doctype/delivery_note/test_delivery_note.py
2026-01-08 09:35:44 +00:00
Frappe PR Bot
18fbe69892 chore(release): Bumped to Version 14.92.9
## [14.92.9](https://github.com/frappe/erpnext/compare/v14.92.8...v14.92.9) (2026-01-07)

### Bug Fixes

* update filters on period closing voucher ([6499a1d](6499a1dae0))
2026-01-07 04:58:04 +00:00
ruthra kumar
f2043baf36 Merge pull request #51539 from frappe/version-14-hotfix
chore: release v14
2026-01-07 10:26:40 +05:30
ruthra kumar
6267b9aea5 Merge pull request #51486 from frappe/mergify/bp/version-14-hotfix/pr-51467
fix: update filters on period closing voucher (backport #51467)
2026-01-05 10:53:20 +05:30
SowmyaArunachalam
6499a1dae0 fix: update filters on period closing voucher
(cherry picked from commit 7ab1e1f677)
2026-01-05 05:20:31 +00:00
Sagar Vora
478992812f Merge pull request #51410 from frappe/revert-51302-pin-urllib3 2025-12-31 13:22:41 +05:30
Sagar Vora
634dd11d72 Revert "fix(deps): pin urllib3" 2025-12-31 13:18:09 +05:30
Frappe PR Bot
29134db5b4 chore(release): Bumped to Version 14.92.8
## [14.92.8](https://github.com/frappe/erpnext/compare/v14.92.7...v14.92.8) (2025-12-30)

### Bug Fixes

* **stock:** update last incoming rate in serial no doctype ([5e12937](5e129374b9))
2025-12-30 13:33:09 +00:00
ruthra kumar
a2597c9608 Merge pull request #51390 from frappe/version-14-hotfix
chore: release v14
2025-12-30 19:01:44 +05:30
rohitwaghchaure
0e6586734a Merge pull request #51365 from aerele/fix/serial-item-incoming-rate
fix(stock): update last incoming rate in serial no doctype
2025-12-29 23:31:07 +05:30
Sudharsanan11
5e129374b9 fix(stock): update last incoming rate in serial no doctype 2025-12-29 16:54:09 +05:30
Raffael Meyer
55d2a54785 fix: don't duplicate default income account to Item (#50413)
* fix: don't duplicate default income account to Item

Only store _Default Income Account_ in **Item** if it's different from the **Company**'s  _Default Income Account_.

Resolves #48231

* refactor: move db call out of loop

* docs: add docstring

(cherry picked from commit b6cb9d4799)

# Conflicts:
#	erpnext/controllers/selling_controller.py
2025-12-25 09:23:09 +00:00
Frappe PR Bot
b3bf456ea2 chore(release): Bumped to Version 14.92.7
## [14.92.7](https://github.com/frappe/erpnext/compare/v14.92.6...v14.92.7) (2025-12-24)

### Bug Fixes

* available qty not fetched on selection of batch ([9841438](98414385e5))
* **deps:** pin urllib3<2 ([1dee100](1dee10077c))
* disable uv ([d713f39](d713f39fce))
* **patch:** fallback for frankfurter settings v14 patch ([5c9e0bb](5c9e0bba2f))

### Performance Improvements

* optimize company monthly sales query using date range ([#48942](https://github.com/frappe/erpnext/issues/48942)) ([e9c1d0a](e9c1d0ad52))
2025-12-24 08:23:59 +00:00
rohitwaghchaure
932dc66871 Merge pull request #51279 from frappe/version-14-hotfix
chore: release v14
2025-12-24 13:52:30 +05:30
Akhil Narang
3a6d056fd3 Merge pull request #51302 from akhilnarang/pin-urllib3
fix(deps): pin urllib3
2025-12-24 12:52:26 +05:30
Akhil Narang
d713f39fce fix: disable uv
Seems to cause an issue with dependency resolution when installation dev dependencies

Signed-off-by: Akhil Narang <me@akhilnarang.dev>
2025-12-24 12:29:21 +05:30
Akhil Narang
1dee10077c fix(deps): pin urllib3<2
v2 has breaking changes, and some other ERPNext dependencies pull in newer requests, which has urllib3<3 mentioned.

Signed-off-by: Akhil Narang <me@akhilnarang.dev>
2025-12-24 11:26:39 +05:30
ruthra kumar
c86cf5b9c6 Merge pull request #51291 from frappe/mergify/bp/version-14-hotfix/pr-51285
fix(patch): handle currency exchange settings frankfurter api update for older versions (backport #51285)
2025-12-23 20:46:16 +05:30
diptanilsaha
5c9e0bba2f fix(patch): fallback for frankfurter settings v14 patch
(cherry picked from commit 50bb1ce31d)
2025-12-23 12:11:25 +00:00
rohitwaghchaure
793688710a Merge pull request #51261 from rohitwaghchaure/fixed-support-53592
fix: available qty not fetched on selection of batch
2025-12-22 16:52:30 +05:30
Rohit Waghchaure
98414385e5 fix: available qty not fetched on selection of batch 2025-12-22 16:47:38 +05:30
Diptanil Saha
33f7b742ec Merge pull request #51147 from frappe/mergify/bp/version-14-hotfix/pr-48942 2025-12-17 11:57:24 +05:30
Yash Chaubey
e9c1d0ad52 perf: optimize company monthly sales query using date range (#48942)
* perf: optimize company monthly sales query using date range instead of DATE_FORMAT

* perf: optimize company monthly sales query using date range

(cherry picked from commit 4ede97ae2b)
2025-12-17 06:07:27 +00:00
Frappe PR Bot
e733b41ab3 chore(release): Bumped to Version 14.92.6
## [14.92.6](https://github.com/frappe/erpnext/compare/v14.92.5...v14.92.6) (2025-12-16)

### Bug Fixes

* **asset:** calculate depreciation amount for non prorata based schedules ([8de0d60](8de0d60581))
* **asset:** prorata daily depr amount calculation ([bc58fd1](bc58fd1fa4))
* **currency exchange settings:** added backward compatibility for frankfurter api ([a15b010](a15b010868))
* **share balance:** use currency field instead of int for rate and amount ([a299392](a299392128))
* Short circuit guest perm checks ([6f6cbb7](6f6cbb717e))
* **transaction-deletion:** Add virtual doctypes to the list of ignored doctypes (backport [#51063](https://github.com/frappe/erpnext/issues/51063)) ([#51086](https://github.com/frappe/erpnext/issues/51086)) ([67c0d08](67c0d08569))
* update expected schedules on test case test_schedule_for_straight_line_method_for_existing_asset ([6ea7393](6ea7393f7d))
2025-12-16 15:12:25 +00:00
ruthra kumar
39fd4a827c Merge pull request #51125 from frappe/version-14-hotfix
chore: release v14
2025-12-16 20:40:57 +05:30
mergify[bot]
67c0d08569 fix(transaction-deletion): Add virtual doctypes to the list of ignored doctypes (backport #51063) (#51086)
* fix: Add virtual doctypes to the list of ignored doctypes in transaction deletion

(cherry picked from commit c7a7cb2b90)

* refactor: switch to `or_filters` so the query hits the DB only once

(cherry picked from commit 45a7195abe)

* refactor: remove redundant assignment of doctypes_to_be_ignored_list

(cherry picked from commit 0f7d89f4d1)

---------

Co-authored-by: KerollesFathy <kerollesfhabib@gmail.com>
2025-12-14 14:31:02 +05:30
Ankush Menat
6f6cbb717e fix: Short circuit guest perm checks 2025-12-14 12:12:35 +05:30
Diptanil Saha
387657fa92 Merge pull request #51044 from frappe/mergify/bp/version-14-hotfix/pr-51037
fix(currency exchange settings): added backward compatibility for frankfurter api (backport #51037)
2025-12-11 16:27:54 +05:30
Diptanil Saha
ef03d90a08 chore: resolve conflict 2025-12-11 15:58:30 +05:30
diptanilsaha
a15b010868 fix(currency exchange settings): added backward compatibility for frankfurter api
(cherry picked from commit 5c2bb66028)

# Conflicts:
#	erpnext/patches.txt
2025-12-11 10:26:17 +00:00
Khushi Rawat
9d00afe1b1 Merge pull request #50964 from aerele/daily-prorata-depr-amount-calculation
fix(asset): prorata daily depr amount calculation
2025-12-10 14:22:17 +05:30
Navin-S-R
6ea7393f7d fix: update expected schedules on test case test_schedule_for_straight_line_method_for_existing_asset 2025-12-10 13:35:57 +05:30
Navin-S-R
8de0d60581 fix(asset): calculate depreciation amount for non prorata based schedules 2025-12-10 12:38:27 +05:30
Diptanil Saha
a67092894f Merge pull request #51002 from frappe/mergify/bp/version-14-hotfix/pr-51001
fix(share balance): use currency field instead of int for rate and amount (backport #51001)
2025-12-10 10:38:26 +05:30
Diptanil Saha
77b9044d8d chore: resolve conflict 2025-12-10 09:56:15 +05:30
Diptanil Saha
ba9f571886 chore: resolve conflict 2025-12-10 09:52:37 +05:30
diptanilsaha
a299392128 fix(share balance): use currency field instead of int for rate and amount
(cherry picked from commit 2fe5fad884)

# Conflicts:
#	erpnext/accounts/doctype/share_balance/share_balance.json
#	erpnext/accounts/doctype/share_balance/share_balance.py
2025-12-10 04:21:05 +00:00
Navin-S-R
bc58fd1fa4 fix(asset): prorata daily depr amount calculation 2025-12-08 15:38:48 +05:30
Frappe PR Bot
1920259180 chore(release): Bumped to Version 14.92.5
## [14.92.5](https://github.com/frappe/erpnext/compare/v14.92.4...v14.92.5) (2025-12-03)

### Bug Fixes

* **Job Card:** avoid Type Error when completed_qty is None (backport [#50447](https://github.com/frappe/erpnext/issues/50447)) ([#50760](https://github.com/frappe/erpnext/issues/50760)) ([5e05873](5e05873226))
* update uom when item changes ([a3b120b](a3b120b3b2))
2025-12-03 05:43:40 +00:00
Diptanil Saha
ea1ca7111d Merge pull request #50867 from frappe/version-14-hotfix 2025-12-03 11:12:16 +05:30
rohitwaghchaure
10f1c9c3ac Merge pull request #50763 from aerele/support-52987
fix: update uom when item changes
2025-11-26 23:42:32 +05:30
Pugazhendhi Velu
a3b120b3b2 fix: update uom when item changes 2025-11-26 16:07:27 +00:00
mergify[bot]
5e05873226 fix(Job Card): avoid Type Error when completed_qty is None (backport #50447) (#50760)
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
fix(Job Card): avoid Type Error when completed_qty is None (#50447)
2025-11-26 14:55:53 +01:00
ruthra kumar
14a7ab71b4 Merge pull request #50747 from frappe/version-14-hotfix
chore: release v14
2025-11-26 10:17:16 +05:30
mergify[bot]
db828c08b6 chore: switched frankfurter domain from frankfurter.app to frankfurter.dev (backport #50734) (#50739)
Co-authored-by: diptanilsaha <diptanil@frappe.io>
2025-11-25 15:49:37 +05:30
Frappe PR Bot
993890116b chore(release): Bumped to Version 14.92.4
## [14.92.4](https://github.com/frappe/erpnext/compare/v14.92.3...v14.92.4) (2025-11-18)

### Bug Fixes

* current qty in stock reco ([5979683](5979683e8e))
* **process statement of accounts:** use date instead of formatted date ([1ef9471](1ef9471c79))
2025-11-18 13:09:28 +00:00
Diptanil Saha
cd437b5a26 Merge pull request #50594 from frappe/version-14-hotfix 2025-11-18 18:37:55 +05:30
Diptanil Saha
e6f505ef81 Merge pull request #50523 from frappe/mergify/bp/version-14-hotfix/pr-49086
fix(process statement of accounts): use date instead of formatted date (backport #49086)
2025-11-13 22:16:06 +05:30
ravibharathi656
1ef9471c79 fix(process statement of accounts): use date instead of formatted date
(cherry picked from commit aa3f50ab77)
2025-11-13 16:24:05 +00:00
rohitwaghchaure
304aad6745 Merge pull request #50490 from frappe/mergify/bp/version-14-hotfix/pr-50487
fix: current qty in stock reconciliation  (backport #50487)
2025-11-12 22:49:37 +05:30
rohitwaghchaure
c80fed8b2f chore: fix conflicts 2025-11-12 13:57:00 +05:30
Rohit Waghchaure
5979683e8e fix: current qty in stock reco
(cherry picked from commit 58315bc963)

# Conflicts:
#	erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
2025-11-12 08:25:59 +00:00
Frappe PR Bot
457a81123b chore(release): Bumped to Version 14.92.3
## [14.92.3](https://github.com/frappe/erpnext/compare/v14.92.2...v14.92.3) (2025-11-11)

### Bug Fixes

* set company before creating asset movement to avoid permission error ([8dc80c9](8dc80c948e))
2025-11-11 14:42:37 +00:00
Diptanil Saha
d7966183be Merge pull request #50474 from frappe/version-14-hotfix 2025-11-11 20:11:02 +05:30
Khushi Rawat
367bd58e76 Merge pull request #50378 from frappe/mergify/bp/version-14-hotfix/pr-50367
fix: set company before creating asset movement to avoid permission error (backport #50367)
2025-11-06 10:03:10 +05:30
rehansari26
8dc80c948e fix: set company before creating asset movement to avoid permission error
(cherry picked from commit 8c49c9e500)
2025-11-05 21:50:18 +00:00
Diptanil Saha
0e3cc120dc Merge pull request #50334 from frappe/version-14-hotfix
chore: release v14
2025-11-04 18:00:16 +05:30
ruthra kumar
b54c5f61d6 Merge pull request #50308 from frappe/mergify/bp/version-14-hotfix/pr-50297
chore:there is no company field in bank (backport #50297)
2025-11-03 14:04:23 +05:30
Nihal Roshan
c51a9025b3 refactor: remove company filter
'Bank' doctype has no 'company' field.

(cherry picked from commit f1682ea90e)
2025-11-03 08:31:53 +00:00
Frappe PR Bot
616448f9c9 chore(release): Bumped to Version 14.92.2
## [14.92.2](https://github.com/frappe/erpnext/compare/v14.92.1...v14.92.2) (2025-10-21)

### Bug Fixes

* do reposting of first transfer entry based on item-wh combination ([85c509e](85c509ea69))
* handle flt conversion for prev_ordered_qty ([9460568](94605687c7))
2025-10-21 12:45:22 +00:00
Diptanil Saha
70a8c64ea4 Merge pull request #50181 from frappe/version-14-hotfix
chore: release v14
2025-10-21 18:13:40 +05:30
mergify[bot]
00a75d4984 refactor: simplify expression (backport #50168) (#50169)
Co-authored-by: barredterra <14891507+barredterra@users.noreply.github.com>
2025-10-19 23:59:31 +02:00
rohitwaghchaure
8b826c2369 Merge pull request #50121 from frappe/mergify/bp/version-14-hotfix/pr-50061
fix: do reposting of first transfer entry based on item-wh combination (backport #50061)
2025-10-16 15:02:52 +05:30
rohitwaghchaure
210786119d chore: fix conflicts 2025-10-16 00:17:50 +05:30
Rohit Waghchaure
85c509ea69 fix: do reposting of first transfer entry based on item-wh combination
(cherry picked from commit 2f25b445ab)

# Conflicts:
#	erpnext/stock/stock_ledger.py
2025-10-15 18:44:46 +00:00
Mihir Kandoi
6e35b6b6b3 Merge pull request #50105 from frappe/mergify/bp/version-14-hotfix/pr-50078
fix: handle flt conversion for prev_ordered_qty (backport #50078)
2025-10-15 00:15:14 +05:30
Kavin
94605687c7 fix: handle flt conversion for prev_ordered_qty
(cherry picked from commit 77c35ef47f)
2025-10-14 18:10:27 +00:00
Frappe PR Bot
e27518b2d4 chore(release): Bumped to Version 14.92.1
## [14.92.1](https://github.com/frappe/erpnext/compare/v14.92.0...v14.92.1) (2025-10-14)

### Bug Fixes

* sanitize projects field in tasks webform ([#50089](https://github.com/frappe/erpnext/issues/50089)) ([aadc83d](aadc83d0d9))
2025-10-14 14:18:29 +00:00
Diptanil Saha
cf71178af6 Merge pull request #50099 from frappe/version-14-hotfix
chore: release v14
2025-10-14 19:46:52 +05:30
ruthra kumar
002575244f Merge pull request #50097 from frappe/mergify/bp/version-14-hotfix/pr-50089
fix: sanitize projects field in tasks webform (backport #50089)
2025-10-14 19:22:52 +05:30
Akhil Narang
aadc83d0d9 fix: sanitize projects field in tasks webform (#50089)
Signed-off-by: Akhil Narang <me@akhilnarang.dev>
(cherry picked from commit f8b50d3ffa)
2025-10-14 13:08:25 +00:00
Frappe PR Bot
66f1e2a076 chore(release): Bumped to Version 14.92.0
# [14.92.0](https://github.com/frappe/erpnext/compare/v14.91.3...v14.92.0) (2025-10-07)

### Bug Fixes

* **Common Code:** fetch canonical URI from Code List (backport [#49882](https://github.com/frappe/erpnext/issues/49882)) ([#49883](https://github.com/frappe/erpnext/issues/49883)) ([9b5183e](9b5183e0e5))
* do not fetch disabled item tax template ([dcae024](dcae024fd2))
* linter; dont change doc after DB update (backport [#49907](https://github.com/frappe/erpnext/issues/49907)) ([#49909](https://github.com/frappe/erpnext/issues/49909)) ([30a6fe5](30a6fe55ca))
* resolved conflict ([2c383a6](2c383a69f9))
* Set paid amount automatically only if return entry validated and has negative grand total ([#49829](https://github.com/frappe/erpnext/issues/49829)) ([b7d76d8](b7d76d8fad))

### Features

* dynamic due date in payment terms when fetched from order (backport [#48864](https://github.com/frappe/erpnext/issues/48864)) ([#49937](https://github.com/frappe/erpnext/issues/49937)) ([d82106c](d82106cb76))
2025-10-07 13:18:52 +00:00
ruthra kumar
1d5935a10f chore: release v14 (#49943)
* refactor(Supplier): custom buttons call make methods (backport #49840) (#49841)

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>

* fix(Common Code): fetch canonical URI from Code List (backport #49882) (#49883)

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
fix(Common Code): fetch canonical URI from Code List (#49882)

* fix: Set paid amount automatically only if return entry validated and has negative grand total (#49829)

(cherry picked from commit dcbcc596f2)

# Conflicts:
#	erpnext/public/js/controllers/taxes_and_totals.js

* fix: resolved conflict

* fix: linter; dont change doc after DB update (backport #49907) (#49909)

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
Co-authored-by: ruthra kumar <ruthra@erpnext.com>
fix: linter; dont change doc after DB update (#49907)

* fix: do not fetch disabled item tax template

(cherry picked from commit b10cf4a928)

# Conflicts:
#	erpnext/public/js/controllers/transaction.js
#	erpnext/stock/get_item_details.py

* chore: fix conflicts

* chore: fix conflicts

* chore: fix linters issue

* feat: dynamic due date in payment terms when fetched from order (backport #48864) (#49937)

* feat: dynamic due date in payment terms when fetched from order (#48864)

* fix: dynamic due date when payment terms are fetched from order

* fix(test): use change_settings decorator for settings enable and disable

* fix(test): compare schedule for due_date dynamically

* fix: save conditions for due date at invoice level

* fix: make fields read only and on change of date unset the date condition fields

* fix: remove fetch_form

* fix: correct field assingment

* fix: revert unwanted changes

* refactor: streamline payment term field assignments and enhance discount date handling

* refactor: remove payment_term from fields_to_copy and optimize currency handling in transaction callback

* refactor: ensure default values for payment schedule and discount validity fields

(cherry picked from commit 3c70cbbaf8)

# Conflicts:
#	erpnext/accounts/doctype/payment_schedule/payment_schedule.json
#	erpnext/accounts/doctype/payment_schedule/payment_schedule.py
#	erpnext/public/js/controllers/transaction.js
#	erpnext/selling/doctype/sales_order/test_sales_order.py
#	erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py

* chore: resolve conflicts

---------

Co-authored-by: Lakshit Jain <ljain112@gmail.com>

---------

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
Co-authored-by: Nabin Hait <nabinhait@gmail.com>
Co-authored-by: ruthra kumar <ruthra@erpnext.com>
Co-authored-by: ljain112 <ljain112@gmail.com>
Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2025-10-07 18:47:23 +05:30
mergify[bot]
d82106cb76 feat: dynamic due date in payment terms when fetched from order (backport #48864) (#49937)
* feat: dynamic due date in payment terms when fetched from order (#48864)

* fix: dynamic due date when payment terms are fetched from order

* fix(test): use change_settings decorator for settings enable and disable

* fix(test): compare schedule for due_date dynamically

* fix: save conditions for due date at invoice level

* fix: make fields read only and on change of date unset the date condition fields

* fix: remove fetch_form

* fix: correct field assingment

* fix: revert unwanted changes

* refactor: streamline payment term field assignments and enhance discount date handling

* refactor: remove payment_term from fields_to_copy and optimize currency handling in transaction callback

* refactor: ensure default values for payment schedule and discount validity fields

(cherry picked from commit 3c70cbbaf8)

# Conflicts:
#	erpnext/accounts/doctype/payment_schedule/payment_schedule.json
#	erpnext/accounts/doctype/payment_schedule/payment_schedule.py
#	erpnext/public/js/controllers/transaction.js
#	erpnext/selling/doctype/sales_order/test_sales_order.py
#	erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py

* chore: resolve conflicts

---------

Co-authored-by: Lakshit Jain <ljain112@gmail.com>
2025-10-07 14:10:46 +05:30
rohitwaghchaure
03aeac6c9b Merge pull request #49931 from frappe/mergify/bp/version-14-hotfix/pr-49702
fix: do not fetch disabled item tax template (backport #49702)
2025-10-07 11:54:19 +05:30
rohitwaghchaure
014f5bff5c chore: fix linters issue 2025-10-07 11:24:49 +05:30
rohitwaghchaure
819667ab24 chore: fix conflicts 2025-10-07 11:00:45 +05:30
rohitwaghchaure
aa20e224fb chore: fix conflicts 2025-10-07 11:00:02 +05:30
ljain112
dcae024fd2 fix: do not fetch disabled item tax template
(cherry picked from commit b10cf4a928)

# Conflicts:
#	erpnext/public/js/controllers/transaction.js
#	erpnext/stock/get_item_details.py
2025-10-07 05:26:09 +00:00
mergify[bot]
30a6fe55ca fix: linter; dont change doc after DB update (backport #49907) (#49909)
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
Co-authored-by: ruthra kumar <ruthra@erpnext.com>
fix: linter; dont change doc after DB update (#49907)
2025-10-06 13:34:24 +02:00
ruthra kumar
57f9894f41 Merge pull request #49892 from frappe/mergify/bp/version-14-hotfix/pr-49829
fix: Set paid amount automatically only if return entry validated and has negative grand total (backport #49829)
2025-10-06 12:58:55 +05:30
Nabin Hait
2c383a69f9 fix: resolved conflict 2025-10-06 12:19:22 +05:30
Nabin Hait
b7d76d8fad fix: Set paid amount automatically only if return entry validated and has negative grand total (#49829)
(cherry picked from commit dcbcc596f2)

# Conflicts:
#	erpnext/public/js/controllers/taxes_and_totals.js
2025-10-06 05:58:16 +00:00
mergify[bot]
9b5183e0e5 fix(Common Code): fetch canonical URI from Code List (backport #49882) (#49883)
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
fix(Common Code): fetch canonical URI from Code List (#49882)
2025-10-04 19:22:04 +02:00
mergify[bot]
0b43d518af refactor(Supplier): custom buttons call make methods (backport #49840) (#49841)
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2025-10-02 00:16:20 +02:00
Frappe PR Bot
a6b490e4b3 chore(release): Bumped to Version 14.91.3
## [14.91.3](https://github.com/frappe/erpnext/compare/v14.91.2...v14.91.3) (2025-09-30)

### Performance Improvements

* reposting for backdated transactions ([3d51c1b](3d51c1b363))
2025-09-30 13:07:25 +00:00
ruthra kumar
a5d33d5665 Merge pull request #49802 from frappe/version-14-hotfix
chore: release v14
2025-09-30 18:36:03 +05:30
Frappe PR Bot
48c3c80383 chore(release): Bumped to Version 14.91.2
## [14.91.2](https://github.com/frappe/erpnext/compare/v14.91.1...v14.91.2) (2025-09-25)

### Performance Improvements

* reposting for backdated transactions ([f3da431](f3da431f1f))
2025-09-25 14:45:52 +00:00
rohitwaghchaure
a9a68f2523 Merge pull request #49726 from frappe/mergify/bp/version-14/pr-49723
perf: reposting for backdated transactions (backport #49720) (backport #49723)
2025-09-25 20:14:23 +05:30
Rohit Waghchaure
f3da431f1f perf: reposting for backdated transactions
(cherry picked from commit 1b0fc0541b)
(cherry picked from commit 3d51c1b363)
2025-09-25 13:22:15 +00:00
rohitwaghchaure
14e92d589f Merge pull request #49723 from frappe/mergify/bp/version-14-hotfix/pr-49720
perf: reposting for backdated transactions (backport #49720)
2025-09-25 18:50:18 +05:30
Rohit Waghchaure
3d51c1b363 perf: reposting for backdated transactions
(cherry picked from commit 1b0fc0541b)
2025-09-25 12:16:23 +00:00
Frappe PR Bot
825ef415ef chore(release): Bumped to Version 14.91.1
## [14.91.1](https://github.com/frappe/erpnext/compare/v14.91.0...v14.91.1) (2025-09-23)

### Bug Fixes

* add condition for name ([0c7911d](0c7911d0fe))
2025-09-23 13:35:10 +00:00
ruthra kumar
f6148c4352 chore: release v14 (#49696)
* fix: add condition for name

(cherry picked from commit cf5a2d6351)

* test: add test to validate user permission in qb

(cherry picked from commit a5b881ea74)

# Conflicts:
#	erpnext/setup/doctype/employee/test_employee.py

---------

Co-authored-by: venkat102 <venkatesharunachalam659@gmail.com>
Co-authored-by: ruthra kumar <ruthra@erpnext.com>
2025-09-23 19:03:40 +05:30
ruthra kumar
3c408ff66b Merge pull request #49616 from frappe/mergify/bp/version-14-hotfix/pr-49467
fix: add condition for name (backport #49467)
2025-09-22 10:49:01 +05:30
venkat102
f6bc9fc505 test: add test to validate user permission in qb
(cherry picked from commit a5b881ea74)

# Conflicts:
#	erpnext/setup/doctype/employee/test_employee.py
2025-09-18 16:47:10 +05:30
venkat102
0c7911d0fe fix: add condition for name
(cherry picked from commit cf5a2d6351)
2025-09-18 11:12:13 +00:00
Frappe PR Bot
b8ecc4274f chore(release): Bumped to Version 14.91.0
# [14.91.0](https://github.com/frappe/erpnext/compare/v14.90.1...v14.91.0) (2025-09-16)

### Bug Fixes

* duplicate items being created when fetching items from warehouse in stock reco ([646de0b](646de0bec1))
* remove ignore_permissions ([180f406](180f406917))

### Features

* add permission check for custom button ([42b38b7](42b38b7998))
2025-09-16 14:41:37 +00:00
ruthra kumar
a9b7bc1385 Merge pull request #49565 from frappe/version-14-hotfix
chore: release v14
2025-09-16 20:08:58 +05:30
Mihir Kandoi
d062e50f35 Merge pull request #49515 from frappe/mergify/bp/version-14-hotfix/pr-48456 2025-09-10 10:06:48 +05:30
Mihir Kandoi
646de0bec1 fix: duplicate items being created when fetching items from warehouse in stock reco
(cherry picked from commit 73f6c29559)
2025-09-10 03:01:06 +00:00
Raffael Meyer
02de47c673 Merge pull request #49434 from frappe/mergify/bp/version-14-hotfix/pr-49374
fix: improve permission handling for party link creation (backport #49374)
2025-09-03 17:12:27 +02:00
Marc-Constantin Enke
42b38b7998 feat: add permission check for custom button
(cherry picked from commit 00fd1d2f26)
2025-09-02 17:20:31 +00:00
Marc-Constantin Enke
180f406917 fix: remove ignore_permissions
(cherry picked from commit 7f55f421ab)
2025-09-02 17:20:31 +00:00
Frappe PR Bot
90803b852e chore(release): Bumped to Version 14.90.1
## [14.90.1](https://github.com/frappe/erpnext/compare/v14.90.0...v14.90.1) (2025-09-02)

### Bug Fixes

* add is_cancelled in condition ([b9f9be3](b9f9be3d88))
* ignore cancelled gl and add company filter ([6b29c06](6b29c06511))
* set missing due date in Purchase Invoice and POS Invoice ([#49232](https://github.com/frappe/erpnext/issues/49232)) ([2b6b0b3](2b6b0b32a7))
2025-09-02 13:48:57 +00:00
ruthra kumar
4cbde47ac4 Merge pull request #49424 from frappe/version-14-hotfix
chore: release v14
2025-09-02 19:17:38 +05:30
ruthra kumar
0b3215cd42 Merge pull request #49416 from frappe/mergify/bp/version-14-hotfix/pr-49379
fix: add is_cancelled in condition (backport #49379)
2025-09-02 11:16:44 +05:30
l0gesh29
b9f9be3d88 fix: add is_cancelled in condition
(cherry picked from commit 77a9cf6398)
2025-09-02 05:27:23 +00:00
ruthra kumar
faeedb01a8 Merge pull request #49411 from frappe/mergify/bp/version-14-hotfix/pr-49366
fix(trial-balance-simple): ignore cancelled gl and add company filter (backport #49366)
2025-09-02 10:42:36 +05:30
Raffael Meyer
a2402ddf52 Merge pull request #49233 from frappe/mergify/bp/version-14-hotfix/pr-49232
fix: set missing due date in Purchase Invoice and POS Invoice (backport #49232)
2025-09-02 00:49:37 +02:00
l0gesh29
6b29c06511 fix: ignore cancelled gl and add company filter
(cherry picked from commit afb067ce50)
2025-09-01 11:16:22 +00:00
Frappe PR Bot
2cedd455d9 chore(release): Bumped to Version 14.90.0
# [14.90.0](https://github.com/frappe/erpnext/compare/v14.89.2...v14.90.0) (2025-08-26)

### Bug Fixes

* **dunning:** include accounting dimension upon gl creation ([4fccef0](4fccef0636))
* **payment_entry:** clear party_type when switching to Internal Transfer to avoid 'Supplier None not found' ([e27dd64](e27dd64044))
* **Supplier:** add make_method for Pricing Rule (backport [#49282](https://github.com/frappe/erpnext/issues/49282)) ([#49283](https://github.com/frappe/erpnext/issues/49283)) ([7986e69](7986e69839))

### Features

* add make methods for Bank Account (backport [#49000](https://github.com/frappe/erpnext/issues/49000)) ([#49274](https://github.com/frappe/erpnext/issues/49274)) ([836545b](836545bdb4))
2025-08-26 11:35:26 +00:00
ruthra kumar
ba31f685f8 Merge pull request #49327 from frappe/version-14-hotfix
chore: release v14
2025-08-26 17:01:27 +05:30
ruthra kumar
507d27d509 Merge pull request #49309 from niyati34/fix/payment-entry-clear-party-type-v14hotfix
Fix/payment entry clear party type v14hotfix
2025-08-26 13:27:38 +05:30
niyati34
e27dd64044 fix(payment_entry): clear party_type when switching to Internal Transfer to avoid 'Supplier None not found' 2025-08-25 19:49:05 +05:30
ruthra kumar
7f617a54d1 Merge pull request #49278 from aerele/dunning-accouting-dimension
fix(dunning): include accounting dimension upon gl creation
2025-08-25 16:56:35 +05:30
mergify[bot]
7986e69839 fix(Supplier): add make_method for Pricing Rule (backport #49282) (#49283)
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
fix(Supplier): add make_method for Pricing Rule (#49282)
2025-08-22 19:26:10 +02:00
mergify[bot]
836545bdb4 feat: add make methods for Bank Account (backport #49000) (#49274)
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2025-08-22 19:17:44 +02:00
ravibharathi656
4fccef0636 fix(dunning): include accounting dimension upon gl creation 2025-08-22 12:34:39 +05:30
Raffael Meyer
2b6b0b32a7 fix: set missing due date in Purchase Invoice and POS Invoice (#49232)
(cherry picked from commit 77478303fe)
2025-08-19 12:30:46 +00:00
Frappe PR Bot
dac192a210 chore(release): Bumped to Version 14.89.2
## [14.89.2](https://github.com/frappe/erpnext/compare/v14.89.1...v14.89.2) (2025-08-19)

### Bug Fixes

* formatted string for disabled filter in get_income_account ([1c24bed](1c24bedaf6))
* handle empty loyalty point details ([0d6be69](0d6be69ce5))
* include missing cint import ([ea454ef](ea454ef770))
* sanitize column name for inventory_dimensions in get_stock_balance ([c3ebc39](c3ebc39f3b))
* use query builder instead of raw SQL in get_blanket_orders ([9d06e09](9d06e093a6))
* use query builder instead of raw SQL in get_loyalty_details ([64e6d65](64e6d65c00))
* use query builder instead of raw SQL in get_material_requests_based_on_supplier ([5a0e690](5a0e690307))
* use query builder instead of raw SQL in get_rfq_containing_supplier ([4b643fe](4b643feb92))
* use query builder instead of raw SQL in get_timesheet_detail_rate ([22d7bbb](22d7bbb081))
* use query builder instead of raw SQL in unset_existing_data ([06f04ca](06f04cad3e))
2025-08-19 11:53:42 +00:00
ruthra kumar
8421e09a95 Merge pull request #49226 from frappe/version-14-hotfix
chore: release v14
2025-08-19 17:22:16 +05:30
Diptanil Saha
43a723546d Merge pull request #49220 from frappe/mergify/bp/version-14-hotfix/pr-49192
fix: improve queries with query builder and input sanitization (backport #49192)
2025-08-19 11:51:32 +05:30
diptanilsaha
ea454ef770 fix: include missing cint import 2025-08-19 11:27:55 +05:30
diptanilsaha
0d6be69ce5 fix: handle empty loyalty point details
(cherry picked from commit 1231ca17c9)
2025-08-19 05:25:23 +00:00
diptanilsaha
22d7bbb081 fix: use query builder instead of raw SQL in get_timesheet_detail_rate
(cherry picked from commit e563ed0c75)
2025-08-19 05:25:23 +00:00
diptanilsaha
4b643feb92 fix: use query builder instead of raw SQL in get_rfq_containing_supplier
(cherry picked from commit 7f2a52ff71)
2025-08-19 05:25:22 +00:00
diptanilsaha
06f04cad3e fix: use query builder instead of raw SQL in unset_existing_data
(cherry picked from commit 7fa4ed6139)
2025-08-19 05:25:22 +00:00
diptanilsaha
c3ebc39f3b fix: sanitize column name for inventory_dimensions in get_stock_balance
(cherry picked from commit eb22794f14)
2025-08-19 05:25:22 +00:00
diptanilsaha
9d06e093a6 fix: use query builder instead of raw SQL in get_blanket_orders
(cherry picked from commit 1db135262d)
2025-08-19 05:25:22 +00:00
diptanilsaha
1c24bedaf6 fix: formatted string for disabled filter in get_income_account
(cherry picked from commit 6320f7290f)
2025-08-19 05:25:21 +00:00
diptanilsaha
5a0e690307 fix: use query builder instead of raw SQL in get_material_requests_based_on_supplier
(cherry picked from commit de919568b4)
2025-08-19 05:25:21 +00:00
diptanilsaha
64e6d65c00 fix: use query builder instead of raw SQL in get_loyalty_details
(cherry picked from commit 8696ba2f5d)
2025-08-19 05:25:21 +00:00
Frappe PR Bot
e7915b31ce chore(release): Bumped to Version 14.89.1
## [14.89.1](https://github.com/frappe/erpnext/compare/v14.89.0...v14.89.1) (2025-08-12)

### Bug Fixes

* add condition to fetch active accounts ([8fc8aa2](8fc8aa2dfd))
* nonetype error on applying presentation_currency filter on financial statements and trial balance report ([89d00ee](89d00ee4a2))
* **purchase invoice:** filter only enabled account ([e0d9a47](e0d9a47ff7))
* **tax withholding details:** avoid voucher duplication ([92ee871](92ee871b79))
* timeout while submitting purchase receipt ([06c3839](06c3839abc))
2025-08-12 12:02:50 +00:00
ruthra kumar
b4200c5453 Merge pull request #49117 from frappe/version-14-hotfix
chore: release v14
2025-08-12 17:31:29 +05:30
ruthra kumar
2f4bfa77b2 Merge pull request #48998 from frappe/mergify/bp/version-14-hotfix/pr-48761
fix: prevent gain or loss entry cancellation upon reposting (backport #48761)
2025-08-12 16:44:09 +05:30
Vignesh S
5eeaaf4757 chore: fix text case failure casued by mergify 2025-08-12 16:25:59 +05:30
ruthra kumar
8faabb8110 Merge pull request #49115 from frappe/mergify/bp/version-14-hotfix/pr-48909
fix(tax withholding details): avoid voucher duplication (backport #48909)
2025-08-12 16:07:00 +05:30
ravibharathi656
92ee871b79 fix(tax withholding details): avoid voucher duplication
(cherry picked from commit 8837016243)
2025-08-12 09:31:36 +00:00
MochaMind
67dad765dc chore: release v14 (#49057)
* fix: timeout while submitting purchase receipt

(cherry picked from commit c433943c46)

# Conflicts:
#	erpnext/stock/doctype/purchase_receipt/purchase_receipt.json

* chore: fix conflicts

* fix: nonetype error on applying presentation_currency filter on financial statements and trial balance report

(cherry picked from commit d7e22de44c)

* fix(purchase invoice): filter only enabled account

(cherry picked from commit c3111db6e2)

* fix: add condition to fetch active accounts

(cherry picked from commit 7c8dd86a35)

* chore: add back filter

(cherry picked from commit 23308f6d10)

---------

Co-authored-by: Rohit Waghchaure <rohitw1991@gmail.com>
Co-authored-by: diptanilsaha <diptanil@frappe.io>
Co-authored-by: mithili <mithili15602@gamil.com>
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2025-08-11 11:50:29 +05:30
Mihir Kandoi
25ff9e0a4d Merge pull request #49048 from frappe/mergify/bp/version-14-hotfix/pr-48813
fix(purchase invoice): filter only enabled account (backport #48813)
2025-08-08 12:00:55 +05:30
mithili
a230a0d94c chore: add back filter
(cherry picked from commit 23308f6d10)
2025-08-08 05:25:01 +00:00
mithili
8fc8aa2dfd fix: add condition to fetch active accounts
(cherry picked from commit 7c8dd86a35)
2025-08-08 05:25:01 +00:00
mithili
e0d9a47ff7 fix(purchase invoice): filter only enabled account
(cherry picked from commit c3111db6e2)
2025-08-08 05:25:01 +00:00
rohitwaghchaure
9d0034f0a4 Merge pull request #49018 from frappe/mergify/bp/version-14-hotfix/pr-49010
fix: timeout while submitting purchase receipt (backport #49010)
2025-08-07 12:28:30 +05:30
Diptanil Saha
f3d780c5bc Merge pull request #49024 from frappe/mergify/bp/version-14-hotfix/pr-49023
fix: NoneType error on applying presentation_currency filter on financial statements and trial balance report (backport #49023)
2025-08-07 02:31:03 +05:30
diptanilsaha
89d00ee4a2 fix: nonetype error on applying presentation_currency filter on financial statements and trial balance report
(cherry picked from commit d7e22de44c)
2025-08-06 20:41:33 +00:00
rohitwaghchaure
22e63099ee chore: fix conflicts 2025-08-06 22:24:45 +05:30
Rohit Waghchaure
06c3839abc fix: timeout while submitting purchase receipt
(cherry picked from commit c433943c46)

# Conflicts:
#	erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
2025-08-06 14:35:43 +00:00
Frappe PR Bot
a7d13d580c chore(release): Bumped to Version 14.89.0
# [14.89.0](https://github.com/frappe/erpnext/compare/v14.88.4...v14.89.0) (2025-08-06)

### Bug Fixes

* add doctype fieldname in condition ([c263e6d](c263e6d67e))
* add flt to prevent NoneType errors ([2c98081](2c98081be6))
* closing balance being added twice to fifo queue ([7abc4c4](7abc4c4022))
* do not split round off when there is a cost center allocation ([388b5fb](388b5fb8f6))
* payment ledger voucher seperator row currencies ([0de4477](0de4477ddf))
* provide company for outstanding record. ([235f271](235f271729))
* provide missing `company` in report records that require reference to `Company:company:default_currency` ([d38fd07](d38fd0779c))
* Use correct Attachments folder in code list import ([f415bc3](f415bc3d14))

### Features

* add show_amount_in_company_currency in gl report ([eb69122](eb691226d5))
2025-08-06 02:35:47 +00:00
ruthra kumar
dd9dc29fe5 Merge pull request #48980 from frappe/version-14-hotfix
chore: release v14
2025-08-06 08:04:22 +05:30
Logesh Periyasamy
b3035ec7d4 Merge pull request #48761 from aerele/exchange-gain-or-loss-on-repost
fix: prevent gain or loss entry cancellation upon reposting
(cherry picked from commit a8d17b7590)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
2025-08-06 07:44:42 +05:30
ruthra kumar
0ecc056a3f Merge pull request #48996 from frappe/mergify/bp/version-14-hotfix/pr-48901
fix: do not split round off when there is a cost center allocation (backport #48901)
2025-08-05 20:55:34 +05:30
ravibharathi656
7a9150aac0 test: add test for cost center allocation commercial rounding
(cherry picked from commit dd24cce509)

# Conflicts:
#	erpnext/accounts/doctype/cost_center_allocation/test_cost_center_allocation.py
2025-08-05 20:35:21 +05:30
venkat102
388b5fb8f6 fix: do not split round off when there is a cost center allocation
(cherry picked from commit f0df41d521)
2025-08-05 20:35:18 +05:30
ruthra kumar
96a1a4a8b8 Merge pull request #48984 from frappe/mergify/bp/version-14-hotfix/pr-48798
feat: add show_amount_in_company_currency in gl report (backport #48798)
2025-08-05 17:44:27 +05:30
l0gesh29
eb691226d5 feat: add show_amount_in_company_currency in gl report
(cherry picked from commit 468e5e9b2e)

# Conflicts:
#	erpnext/accounts/report/general_ledger/general_ledger.js
2025-08-05 17:11:33 +05:30
Mihir Kandoi
00e61a531a Merge pull request #48986 from mihir-kandoi/st45290
fix: closing balance being added twice to fifo queue in stock ageing report
2025-08-05 15:48:38 +05:30
Mihir Kandoi
7abc4c4022 fix: closing balance being added twice to fifo queue 2025-08-05 15:25:25 +05:30
Mihir Kandoi
1ae3fd3e27 Merge pull request #48966 from frappe/mergify/bp/version-14-hotfix/pr-48693
fix: Use correct Attachments folder in code list import (backport #48693)
2025-08-05 14:29:16 +05:30
ruthra kumar
ade44fbdb4 Merge pull request #48970 from frappe/mergify/bp/version-14-hotfix/pr-48861
chore: correct description for `consider_party_ledger_amount` in Tax Withholding Category (backport #48861)
2025-08-05 14:26:25 +05:30
ljain112
82e8fb8a5e chore: correct description for consider_party_ledger_amount in Tax Withholding Category
(cherry picked from commit f619bca2d6)

# Conflicts:
#	erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.json
2025-08-05 14:24:38 +05:30
Corentin Forler
f415bc3d14 fix: Use correct Attachments folder in code list import
(cherry picked from commit bc2cb1737a)
2025-08-05 08:34:37 +00:00
ruthra kumar
5bc3273460 Merge pull request #48963 from frappe/mergify/bp/version-14-hotfix/pr-48860
fix: add doctype fieldname in condition (backport #48860)
2025-08-05 13:09:37 +05:30
l0gesh29
c263e6d67e fix: add doctype fieldname in condition
(cherry picked from commit e2d63e4c32)

# Conflicts:
#	erpnext/accounts/utils.py
2025-08-05 12:16:33 +05:30
ruthra kumar
11b5a81170 Merge pull request #48927 from pps190/backport/version-14-hotfix/48926
fix: provide missing company in report records that require reference to Company:company:default_currency (Backport #48926)
2025-08-04 10:29:49 +05:30
Mihir Kandoi
27485b9472 Merge pull request #48329 from pps190/backport/version-14-hotfix/34909
Get incoming rate v14 fix (Backport #34909)
2025-08-02 22:09:30 +05:30
Devin Slauenwhite
0de4477ddf fix: payment ledger voucher seperator row currencies 2025-08-02 12:29:52 -04:00
Devin Slauenwhite
235f271729 fix: provide company for outstanding record. 2025-08-02 12:23:32 -04:00
Devin Slauenwhite
d38fd0779c fix: provide missing company in report records that require reference to Company:company:default_currency 2025-08-02 12:03:40 -04:00
Mihir Kandoi
093c6bb17e Merge pull request #48907 from frappe/st45332
fix: add flt to prevent NoneType errors
2025-08-01 17:07:50 +05:30
Mihir Kandoi
2c98081be6 fix: add flt to prevent NoneType errors 2025-08-01 16:39:56 +05:30
Frappe PR Bot
e5c821a822 chore(release): Bumped to Version 14.88.4
## [14.88.4](https://github.com/frappe/erpnext/compare/v14.88.3...v14.88.4) (2025-07-29)

### Bug Fixes

* import get_link_to_form from utils ([b43fbf2](b43fbf2a7d))
* include empty values in user permission ([4d12ae0](4d12ae069e))
* incorrect GL entries ([493c8ce](493c8ce04e))
* post gl entry on completion date of asset repair ([80ae982](80ae9820f0))
* resolved conflicts ([6cdc781](6cdc7811b8))
* resolved conflicts ([4524c74](4524c74968))
2025-07-29 15:11:30 +00:00
ruthra kumar
500ffbab09 Merge pull request #48831 from frappe/version-14-hotfix
chore: release v14
2025-07-29 20:40:08 +05:30
ruthra kumar
451326fdeb Merge pull request #48846 from frappe/mergify/bp/version-14-hotfix/pr-48835
fix: include empty values in user permission (backport #48835)
2025-07-29 20:17:18 +05:30
l0gesh29
4d12ae069e fix: include empty values in user permission
(cherry picked from commit f13d98fc7c)
2025-07-29 14:26:33 +00:00
Khushi Rawat
4297c58e5e Merge pull request #48834 from frappe/mergify/bp/version-14-hotfix/pr-48833
fix: post gl entry on completion date of asset repair (backport #48833)
2025-07-29 16:57:02 +05:30
Khushi Rawat
b43fbf2a7d fix: import get_link_to_form from utils 2025-07-29 15:48:41 +05:30
Khushi Rawat
6cdc7811b8 fix: resolved conflicts 2025-07-29 15:42:00 +05:30
Khushi Rawat
59ac7aeb23 chore: resolved conflicts 2025-07-29 15:41:20 +05:30
Khushi Rawat
4524c74968 fix: resolved conflicts 2025-07-29 15:39:10 +05:30
khushi8112
80ae9820f0 fix: post gl entry on completion date of asset repair
(cherry picked from commit 5a82b723c2)

# Conflicts:
#	erpnext/assets/doctype/asset_repair/asset_repair.json
#	erpnext/assets/doctype/asset_repair/asset_repair.py
#	erpnext/assets/doctype/asset_repair/test_asset_repair.py
2025-07-29 10:07:22 +00:00
rohitwaghchaure
5d92f38bbe Merge pull request #48807 from frappe/mergify/bp/version-14-hotfix/pr-48801
fix: incorrect GL entries (backport #48801)
2025-07-28 17:54:00 +05:30
rohitwaghchaure
a944eaf054 chore: fix conflicts 2025-07-28 16:35:39 +05:30
Rohit Waghchaure
493c8ce04e fix: incorrect GL entries
(cherry picked from commit 4c273fcc99)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
2025-07-28 10:58:37 +00:00
Frappe PR Bot
a711cd7a18 chore(release): Bumped to Version 14.88.3
## [14.88.3](https://github.com/frappe/erpnext/compare/v14.88.2...v14.88.3) (2025-07-23)

### Bug Fixes

* add validation for account key ([360f3b7](360f3b7c25))
* calculate due date based on payment term ([#46416](https://github.com/frappe/erpnext/issues/46416)) ([9e7a5bb](9e7a5bb1bb))
* performance issue while submitting the purchase invoice ([4fe09fd](4fe09fd7e8))
* **period closing voucher:** closing account head debit and debit in account currency should be equal ([76a233e](76a233e1b7))
* **production plan:** add company filter to sub assembly warehouse ([8934adf](8934adfef4))
* show amount for exchange gain or loss account ([581221f](581221f1ec))
2025-07-23 02:50:52 +00:00
ruthra kumar
942aca31f2 Merge pull request #48746 from frappe/version-14-hotfix
chore: release v14
2025-07-23 08:19:35 +05:30
ruthra kumar
77ea06dc7d Merge pull request #48740 from frappe/mergify/bp/version-14-hotfix/pr-48665
fix: show amount for exchange gain or loss account (backport #48665)
2025-07-22 12:56:31 +05:30
l0gesh29
360f3b7c25 fix: add validation for account key
(cherry picked from commit b6da350c20)
2025-07-22 06:44:03 +00:00
l0gesh29
581221f1ec fix: show amount for exchange gain or loss account
(cherry picked from commit 4f90f50eb2)
2025-07-22 06:44:03 +00:00
rohitwaghchaure
e0be83caa5 Merge pull request #48728 from frappe/mergify/bp/version-14-hotfix/pr-48704
fix(production plan): add company filter to sub assembly warehouse (backport #48704)
2025-07-21 20:04:07 +05:30
ravibharathi656
8934adfef4 fix(production plan): add company filter to sub assembly warehouse
(cherry picked from commit 1728a95111)
2025-07-21 14:27:40 +00:00
Frappe PR Bot
259251dfab chore(release): Bumped to Version 14.88.2
## [14.88.2](https://github.com/frappe/erpnext/compare/v14.88.1...v14.88.2) (2025-07-17)

### Bug Fixes

* calculate due date based on payment term ([#46416](https://github.com/frappe/erpnext/issues/46416)) ([9ce357f](9ce357f3bf))
2025-07-17 11:18:43 +00:00
rohitwaghchaure
f8c54f0a9b Merge pull request #48660 from frappe/mergify/bp/version-14/pr-48638
fix: calculate due date based on payment term (backport #46416) (backport #48638)
2025-07-17 16:47:21 +05:30
Sudharsanan Ashok
9ce357f3bf fix: calculate due date based on payment term (#46416)
(cherry picked from commit 9e808c832f)
(cherry picked from commit 9e7a5bb1bb)
2025-07-17 10:45:07 +00:00
Marica
2d6f4740be Merge pull request #48638 from frappe/mergify/bp/version-14-hotfix/pr-46416
fix: calculate due date based on payment term (backport #46416)
2025-07-16 17:18:04 +02:00
Frappe PR Bot
a3b38d9a91 chore(release): Bumped to Version 14.88.1
## [14.88.1](https://github.com/frappe/erpnext/compare/v14.88.0...v14.88.1) (2025-07-16)

### Bug Fixes

* **period closing voucher:** closing account head debit and debit in account currency should be equal ([18eea34](18eea3425c))
2025-07-16 15:09:51 +00:00
ruthra kumar
ed2e8c2cc6 Merge pull request #48640 from frappe/mergify/bp/version-14/pr-48615
fix(period closing voucher): closing account head debit and debit in account currency should be equal (backport #48615)
2025-07-16 20:38:16 +05:30
venkat102
18eea3425c fix(period closing voucher): closing account head debit and debit in account currency should be equal
(cherry picked from commit 76a233e1b7)
2025-07-16 14:46:15 +00:00
ruthra kumar
7aaea9f5b6 Merge pull request #48615 from aerele/backport-48612
fix(period closing voucher): closing account head debit and debit in account currency should be equal
2025-07-16 20:13:11 +05:30
Sudharsanan Ashok
9e7a5bb1bb fix: calculate due date based on payment term (#46416)
(cherry picked from commit 9e808c832f)
2025-07-16 14:40:15 +00:00
rohitwaghchaure
27105501fb Merge pull request #48636 from frappe/mergify/bp/version-14-hotfix/pr-48633
fix: performance issue while submitting the purchase invoice (backport #48633)
2025-07-16 17:10:28 +05:30
Rohit Waghchaure
4fe09fd7e8 fix: performance issue while submitting the purchase invoice
(cherry picked from commit 47979871de)
2025-07-16 11:16:37 +00:00
venkat102
76a233e1b7 fix(period closing voucher): closing account head debit and debit in account currency should be equal 2025-07-15 18:24:54 +05:30
Frappe PR Bot
bda447f20a chore(release): Bumped to Version 14.88.0
# [14.88.0](https://github.com/frappe/erpnext/compare/v14.87.0...v14.88.0) (2025-07-15)

### Bug Fixes

* **Employee:** add context to status in List View (backport [#48576](https://github.com/frappe/erpnext/issues/48576)) ([#48578](https://github.com/frappe/erpnext/issues/48578)) ([9dad082](9dad08274a))

### Features

* parent item group support in Stock Projected Qty report ([3b36be2](3b36be214f))
* update the modified date of the SLE after reposting ([8966c95](8966c956d5))
2025-07-15 12:50:14 +00:00
ruthra kumar
7db420cf9f Merge pull request #48601 from frappe/version-14-hotfix
chore: release v14
2025-07-15 18:18:44 +05:30
ruthra kumar
5ddfe3ce1d Merge pull request #48585 from frappe/mergify/bp/version-14-hotfix/pr-47892
refactor: use sql for building voucher balance in Receivable report (backport #47892)
2025-07-15 08:23:20 +05:30
ruthra kumar
ff998572e7 chore: resolve conflicts 2025-07-15 08:03:48 +05:30
ruthra kumar
f364a41490 chore: rename method
(cherry picked from commit fc8ca7d82c)

# Conflicts:
#	erpnext/accounts/report/accounts_receivable/accounts_receivable.py
2025-07-15 01:52:45 +00:00
ruthra kumar
804edad233 refactor: build and pass match conditions as qb criterion
(cherry picked from commit 7efeed54de)

# Conflicts:
#	erpnext/accounts/report/accounts_receivable/accounts_receivable.py
#	erpnext/accounts/utils.py
2025-07-15 01:52:45 +00:00
ruthra kumar
e533158a1c chore: drop unused utility method
(cherry picked from commit 52c0df24e3)
2025-07-15 01:52:45 +00:00
ruthra kumar
5bcf642dff refactor: dynamic DB field types
(cherry picked from commit 9d0ebe3427)

# Conflicts:
#	erpnext/accounts/report/accounts_receivable/accounts_receivable.py
2025-07-15 01:52:44 +00:00
ruthra kumar
d7822f493b refactor: better variable name
(cherry picked from commit 1a90c0d031)
2025-07-15 01:52:44 +00:00
ruthra kumar
5188745c53 refactor: prefix-ed names for easy distinction
(cherry picked from commit c5e35cc330)
2025-07-15 01:52:44 +00:00
ruthra kumar
006a8c71fd refactor: utility to drop existing procedures and include cost center
(cherry picked from commit da32bb5f51)

# Conflicts:
#	erpnext/accounts/doctype/accounts_settings/accounts_settings.js
#	erpnext/accounts/doctype/accounts_settings/accounts_settings.json
#	erpnext/accounts/doctype/accounts_settings/accounts_settings.py
2025-07-15 01:52:44 +00:00
ruthra kumar
ba25d334b1 refactor: order by posting date
(cherry picked from commit 7b7440d44a)
2025-07-15 01:52:43 +00:00
ruthra kumar
dd1020ccfb refactor: call procedures based on config
(cherry picked from commit e90c6a33bd)

# Conflicts:
#	erpnext/accounts/report/accounts_receivable/accounts_receivable.py
2025-07-15 01:52:43 +00:00
ruthra kumar
de2f80811f refactor: introduce sql option for data fetch
(cherry picked from commit 8cf8f6abad)

# Conflicts:
#	erpnext/accounts/doctype/accounts_settings/accounts_settings.py
2025-07-15 01:52:43 +00:00
ruthra kumar
12eaa64572 refactor: better readability
(cherry picked from commit 097e74979f)
2025-07-15 01:52:42 +00:00
ruthra kumar
da1dcb8b21 refactor: using sql procedures for AR report
- dynamic filters are passed

(cherry picked from commit e5920c57aa)

# Conflicts:
#	erpnext/accounts/report/accounts_receivable/accounts_receivable.py
2025-07-15 01:52:42 +00:00
mergify[bot]
9dad08274a fix(Employee): add context to status in List View (backport #48576) (#48578)
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
fix(Employee): add context to status in List View (#48576)
2025-07-14 15:53:42 +02:00
rohitwaghchaure
d8d53bc43e Merge pull request #48496 from frappe/mergify/bp/version-14-hotfix/pr-48490
feat: update the modified date of the SLE after reposting (backport #48490)
2025-07-09 19:45:26 +05:30
Rohit Waghchaure
8966c956d5 feat: update the modified date of the SLE after reposting
(cherry picked from commit c2cd4934e7)
2025-07-09 13:52:11 +00:00
rohitwaghchaure
a518fc080a Merge pull request #48491 from frappe/mergify/bp/version-14-hotfix/pr-48488
feat: parent item group support in Stock Projected Qty report (backport #48488)
2025-07-09 18:59:55 +05:30
Rohit Waghchaure
3b36be214f feat: parent item group support in Stock Projected Qty report
(cherry picked from commit 6e80d89d13)
2025-07-09 11:19:41 +00:00
Frappe PR Bot
20af226947 chore(release): Bumped to Version 14.87.0
# [14.87.0](https://github.com/frappe/erpnext/compare/v14.86.5...v14.87.0) (2025-07-08)

### Bug Fixes

* fetch from parent optional in inventory dimension ([bb901ca](bb901cab88))
* incorrect backport ([94a17b6](94a17b6ec2))

### Features

* Period-wise closing entries for TB (backport [#39712](https://github.com/frappe/erpnext/issues/39712)) ([#48386](https://github.com/frappe/erpnext/issues/48386)) ([711e06b](711e06b537))

### Reverts

* do not convert exchange gain/loss amount to foreign currency ([083402e](083402e9a8))
* Revert "fix: stock reco qty with inventory dimension ([#47918](https://github.com/frappe/erpnext/issues/47918))" ([9207d79](9207d79ce6))
2025-07-08 12:54:12 +00:00
ruthra kumar
7f26b1d5a2 Merge pull request #48461 from frappe/version-14-hotfix
chore: release v14
2025-07-08 18:22:45 +05:30
ruthra kumar
c332fcdeff Merge pull request #48458 from frappe/mergify/bp/version-14-hotfix/pr-48361
revert: do not convert exchange gain/loss amount to foreign currency (backport #48361)
2025-07-08 15:45:19 +05:30
mergify[bot]
51adea3b89 refactor: remove do_reposting_for_each_stock_transaction feature (backport #48444) (#48451)
* refactor: remove do_reposting_for_each_stock_transaction feature

Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2025-07-08 15:06:00 +05:30
venkat102
083402e9a8 revert: do not convert exchange gain/loss amount to foreign currency
(cherry picked from commit c17ae703c7)

# Conflicts:
#	erpnext/accounts/report/general_ledger/test_general_ledger.py
2025-07-08 14:26:24 +05:30
rohitwaghchaure
1116a0fc8e Merge pull request #48442 from frappe/mergify/bp/version-14-hotfix/pr-48441
Revert "fix: stock reco qty with inventory dimension" (backport #48441)
2025-07-08 12:30:58 +05:30
rohitwaghchaure
b15d7e573e chore: fix conflicts 2025-07-08 10:45:49 +05:30
rohitwaghchaure
9207d79ce6 Revert "fix: stock reco qty with inventory dimension (#47918)"
This reverts commit 342cebc778.

(cherry picked from commit 8ba66c9833)

# Conflicts:
#	erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
2025-07-08 05:07:53 +00:00
rohitwaghchaure
33eb72035b Merge pull request #48433 from frappe/mergify/bp/version-14-hotfix/pr-48432
fix: fetch from parent optional in inventory dimension (backport #48432)
2025-07-08 10:13:56 +05:30
rohitwaghchaure
2c6099e051 Merge pull request #48438 from rohitwaghchaure/fixed-sales-incoming-rate-issue
fix: incorrect backport
2025-07-08 09:55:06 +05:30
rohitwaghchaure
5f4aaed5a0 chore: fix conflicts 2025-07-08 08:34:16 +05:30
Rohit Waghchaure
94a17b6ec2 fix: incorrect backport 2025-07-08 08:32:12 +05:30
Rohit Waghchaure
bb901cab88 fix: fetch from parent optional in inventory dimension
(cherry picked from commit 8aac6a6b18)

# Conflicts:
#	erpnext/stock/doctype/inventory_dimension/inventory_dimension.json
2025-07-07 11:52:53 +00:00
Diptanil Saha
711e06b537 feat: Period-wise closing entries for TB (backport #39712) (#48386)
feat: Period-wise closing entries for TB

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2025-07-03 22:47:28 +05:30
Diptanil Saha
c54b65acc2 chore: fix flaky test in Tax Withholding Details (backport #48375) (#48387)
* fix(test): flaky budget test case

* chore(test): import get_accumulated_monthly_budget

* fix(test): tds_payable_monthly tests

---------

Co-authored-by: ruthra kumar <ruthra@erpnext.com>
2025-07-03 22:09:19 +05:30
Frappe PR Bot
179b8c7c61 chore(release): Bumped to Version 14.86.5
## [14.86.5](https://github.com/frappe/erpnext/compare/v14.86.4...v14.86.5) (2025-07-01)

### Bug Fixes

* accounting entries for standalone credit notes ([15ae019](15ae0196c6))
* add validation for exchange gain/loss entries ([b98cce8](b98cce8b9a))
* do not allow backdated transactions against serial numbers. ([#48281](https://github.com/frappe/erpnext/issues/48281)) ([945bdab](945bdabebb))
2025-07-01 11:59:12 +00:00
ruthra kumar
b107387dda Merge pull request #48337 from frappe/version-14-hotfix
chore: release v14
2025-07-01 17:27:40 +05:30
ruthra kumar
9fcca18bf3 Merge pull request #48334 from frappe/mergify/bp/version-14-hotfix/pr-48162
fix: add validation for exchange gain/loss entries (backport #48162)
2025-07-01 16:20:45 +05:30
i-am-vimal
b98cce8b9a fix: add validation for exchange gain/loss entries
(cherry picked from commit 5c9eddd31e)

# Conflicts:
#	erpnext/accounts/report/utils.py
2025-07-01 15:36:51 +05:30
Hossein Yousefian
bd9655a781 get_incoming_rate_voucher_no_fix 2025-06-30 17:11:04 -04:00
Hossein Yousefian
1df3b608ec get_incoming_rate_zero_in_rate_fix 2025-06-30 17:11:04 -04:00
rohitwaghchaure
62b7a8d21a Merge pull request #48313 from frappe/mergify/bp/version-14-hotfix/pr-48310
fix: accounting entries for standalone credit notes (backport #48310)
2025-06-30 10:09:23 +05:30
rohitwaghchaure
6b31e54891 chore: fix conflicts 2025-06-30 09:42:13 +05:30
Rohit Waghchaure
15ae0196c6 fix: accounting entries for standalone credit notes
(cherry picked from commit 52177cffcd)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
2025-06-30 04:09:39 +00:00
Frappe PR Bot
a06d705d75 chore(release): Bumped to Version 14.86.4
## [14.86.4](https://github.com/frappe/erpnext/compare/v14.86.3...v14.86.4) (2025-06-27)

### Bug Fixes

* do not allow backdated transactions against serial numbers. ([#48281](https://github.com/frappe/erpnext/issues/48281)) ([1393100](1393100a62))
2025-06-27 06:27:09 +00:00
rohitwaghchaure
9e3e3182f2 Merge pull request #48290 from frappe/mergify/bp/version-14/pr-48281
fix: do not allow backdated transactions against serial numbers. (backport #48281)
2025-06-27 11:55:38 +05:30
rohitwaghchaure
1393100a62 fix: do not allow backdated transactions against serial numbers. (#48281)
(cherry picked from commit 945bdabebb)
2025-06-27 06:03:52 +00:00
rohitwaghchaure
945bdabebb fix: do not allow backdated transactions against serial numbers. (#48281) 2025-06-27 11:33:03 +05:30
Frappe PR Bot
f07594b863 chore(release): Bumped to Version 14.86.3
## [14.86.3](https://github.com/frappe/erpnext/compare/v14.86.2...v14.86.3) (2025-06-24)

### Bug Fixes

* get already billed amount from current doc instead of database ([#48079](https://github.com/frappe/erpnext/issues/48079)) ([0790db7](0790db79fd))
2025-06-24 14:08:14 +00:00
ruthra kumar
25a382ec24 Merge pull request #48241 from frappe/version-14-hotfix
chore: release v14
2025-06-24 19:36:37 +05:30
ruthra kumar
d7c47e8ea5 Merge pull request #48234 from frappe/mergify/bp/version-14-hotfix/pr-48079
fix: get already billed amount from current doc instead of database (backport #48079)
2025-06-24 17:36:05 +05:30
ljain112
2627fb10ce chore: resolve conflicts 2025-06-24 16:14:24 +05:30
Lakshit Jain
0790db79fd fix: get already billed amount from current doc instead of database (#48079)
* fix: get already billed amount from current doc instead of database

* fix: throw overbilling validation for all items in single call

* refactor: minor fixes

---------

Co-authored-by: Sagar Vora <16315650+sagarvora@users.noreply.github.com>
(cherry picked from commit 47c3c4808e)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
2025-06-24 10:11:43 +00:00
Frappe PR Bot
0dad2acfc8 chore(release): Bumped to Version 14.86.2
## [14.86.2](https://github.com/frappe/erpnext/compare/v14.86.1...v14.86.2) (2025-06-17)

### Bug Fixes

* incorrect warehouse set from SO to MR ([fbfc6a6](fbfc6a6f62))
2025-06-17 14:36:59 +00:00
ruthra kumar
193042ef07 Merge pull request #48099 from frappe/version-14-hotfix
chore: release v14
2025-06-17 20:05:31 +05:30
rohitwaghchaure
8208a9ec27 Merge pull request #48064 from frappe/mergify/bp/version-14-hotfix/pr-48061
fix: incorrect warehouse set from SO to MR (backport #48061)
2025-06-16 14:52:54 +05:30
Rohit Waghchaure
fbfc6a6f62 fix: incorrect warehouse set from SO to MR
(cherry picked from commit 0da8d9c869)
2025-06-16 04:42:26 +00:00
Frappe PR Bot
e9f9eaa2d0 chore(release): Bumped to Version 14.86.1
## [14.86.1](https://github.com/frappe/erpnext/compare/v14.86.0...v14.86.1) (2025-06-10)

### Bug Fixes

* available qty in BOM Stock Report ([186173a](186173a21e))
* incorrect warehouse in MR ([2cac05e](2cac05e56c))
* key-error for COGS By Item Group report (backport [#47914](https://github.com/frappe/erpnext/issues/47914)) ([#47916](https://github.com/frappe/erpnext/issues/47916)) ([f5ef376](f5ef376486))
* stock adjustment entry during reposting (backport [#47878](https://github.com/frappe/erpnext/issues/47878)) ([#47882](https://github.com/frappe/erpnext/issues/47882)) ([c9eeca2](c9eeca22f5))
* stock reco qty with inventory dimension (backport [#47918](https://github.com/frappe/erpnext/issues/47918)) ([#47921](https://github.com/frappe/erpnext/issues/47921)) ([6397c36](6397c366fd))
* throw permission error ([#47976](https://github.com/frappe/erpnext/issues/47976)) ([96c937b](96c937bf6a))
2025-06-10 14:31:53 +00:00
ruthra kumar
08d40ddb9b Merge pull request #47997 from frappe/version-14-hotfix
chore: release v14
2025-06-10 20:00:21 +05:30
rohitwaghchaure
d60ab92082 Merge pull request #48003 from frappe/mergify/bp/version-14-hotfix/pr-47998
fix: incorrect warehouse in MR (backport #47998)
2025-06-10 18:54:18 +05:30
rohitwaghchaure
85a0581145 chore: fix conflicts 2025-06-10 18:20:28 +05:30
Rohit Waghchaure
2cac05e56c fix: incorrect warehouse in MR
(cherry picked from commit 2b9ca79291)

# Conflicts:
#	erpnext/manufacturing/doctype/production_plan/production_plan.py
2025-06-10 12:41:16 +00:00
ruthra kumar
10e4e610db Merge pull request #47993 from frappe/mergify/bp/version-14-hotfix/pr-47981
refactor(Work Order): query_sales_order (backport #47981)
2025-06-10 15:01:22 +05:30
barredterra
9ab80cfd6c refactor(Work Order): query_sales_order
- Use `get_list` instead of `db.sql_list`

    The method is used for setting link options in the frontend and the Link field doesn't ignore permissions, so get_list should be fine here.

- Added type hints to enable argument validation

(cherry picked from commit 2dbdacf905)
2025-06-10 09:03:13 +00:00
rohitwaghchaure
8f2b83d434 Merge pull request #47986 from frappe/mergify/bp/version-14-hotfix/pr-47942
fix: available qty in BOM Stock Report (backport #47942)
2025-06-10 14:03:06 +05:30
Sagar Vora
7f0ebb37da Merge pull request #47977 from frappe/mergify/bp/version-14-hotfix/pr-47976
fix: throw permission error (backport #47976)
2025-06-10 07:39:53 +00:00
Sagar Vora
1d42c4a305 chore: fix conflicts 2025-06-10 13:08:39 +05:30
Rohit Waghchaure
186173a21e fix: available qty in BOM Stock Report
(cherry picked from commit ea689bbe3f)
2025-06-10 06:50:51 +00:00
Aayush Dalal
96c937bf6a fix: throw permission error (#47976)
Co-authored-by: Sagar Vora <16315650+sagarvora@users.noreply.github.com>
(cherry picked from commit 8b6a8d0c4f)

# Conflicts:
#	erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py
#	erpnext/stock/utils.py
2025-06-09 17:29:42 +00:00
rohitwaghchaure
52aafc9410 Merge pull request #47932 from rohitwaghchaure/fixed-sabb-cond
chore: incorrect condition
2025-06-06 13:13:54 +05:30
Rohit Waghchaure
3232310c0f chore: incorrect condition 2025-06-06 12:30:03 +05:30
mergify[bot]
6397c366fd fix: stock reco qty with inventory dimension (backport #47918) (#47921)
fix: stock reco qty with inventory dimension (#47918)

(cherry picked from commit 342cebc778)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2025-06-06 09:50:20 +05:30
mergify[bot]
f5ef376486 fix: key-error for COGS By Item Group report (backport #47914) (#47916)
fix: key-error for COGS By Item Group report (#47914)

fix: keyerror for COGS By Item Group report
(cherry picked from commit 997ce4eaa7)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2025-06-05 17:18:22 +05:30
mergify[bot]
c9eeca22f5 fix: stock adjustment entry during reposting (backport #47878) (#47882)
fix: stock adjustment entry during reposting (#47878)

fix: stock adjustment entry
(cherry picked from commit cbcd580daa)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2025-06-04 12:51:22 +05:30
Frappe PR Bot
b145638eca chore(release): Bumped to Version 14.86.0
# [14.86.0](https://github.com/frappe/erpnext/compare/v14.85.10...v14.86.0) (2025-06-03)

### Bug Fixes

* filter of item for manufacture type material request (backport [#47712](https://github.com/frappe/erpnext/issues/47712)) ([#47716](https://github.com/frappe/erpnext/issues/47716)) ([69463b4](69463b4798))
* incorrect actual qty in product bundle balance report (backport [#47791](https://github.com/frappe/erpnext/issues/47791)) ([#47813](https://github.com/frappe/erpnext/issues/47813)) ([df779bb](df779bb7dd))
* use `query.walk() `for escaping special chars in receiable/payable report ([ca48583](ca4858318e))

### Features

* add column "Item Name" to "BOM Stock Report" (backport [#47116](https://github.com/frappe/erpnext/issues/47116)) ([#47484](https://github.com/frappe/erpnext/issues/47484)) ([b664781](b664781fae))
2025-06-03 11:52:06 +00:00
ruthra kumar
b46fdc2645 Merge pull request #47867 from frappe/version-14-hotfix
chore: release v14
2025-06-03 17:20:36 +05:30
mergify[bot]
df779bb7dd fix: incorrect actual qty in product bundle balance report (backport #47791) (#47813)
fix: incorrect actual qty in product bundle balance report (#47791)

(cherry picked from commit c544c3e018)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2025-05-30 14:01:49 +05:30
ruthra kumar
20f701ff0b Merge pull request #47807 from frappe/mergify/bp/version-14-hotfix/pr-47794
fix: use `query.walk() `for escaping special chars in receiable/payable report (backport #47794)
2025-05-29 14:58:45 +05:30
ljain112
ca4858318e fix: use query.walk() for escaping special chars in receiable/payable report
(cherry picked from commit a0a51b5074)
2025-05-29 08:21:15 +00:00
mergify[bot]
b664781fae feat: add column "Item Name" to "BOM Stock Report" (backport #47116) (#47484)
Co-authored-by: Patrick Eißler <77415730+PatrickDEissler@users.noreply.github.com>
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2025-05-27 19:21:20 +02:00
mergify[bot]
69463b4798 fix: filter of item for manufacture type material request (backport #47712) (#47716)
* fix: filter of item for manufacture type material request (#47712)

(cherry picked from commit 874750f9ce)

# Conflicts:
#	erpnext/stock/doctype/material_request/material_request.js

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2025-05-27 21:56:36 +05:30
Frappe PR Bot
caf0f2cd19 chore(release): Bumped to Version 14.85.10
## [14.85.10](https://github.com/frappe/erpnext/compare/v14.85.9...v14.85.10) (2025-05-27)

### Bug Fixes

* incorrect status in the serial no ([#47740](https://github.com/frappe/erpnext/issues/47740)) ([d37b12f](d37b12f385))
* incorrect valuation rate due to positive qty (backport [#47686](https://github.com/frappe/erpnext/issues/47686)) ([#47687](https://github.com/frappe/erpnext/issues/47687)) ([d5d4b3a](d5d4b3a7f3))
* only include advances within the tcs period ([43aeff3](43aeff38aa))
* patch to rename group_by filter in custom reports (backport [#47709](https://github.com/frappe/erpnext/issues/47709)) ([#47729](https://github.com/frappe/erpnext/issues/47729)) ([70bcfb4](70bcfb4748))
* rate changing while making PR (negative discount) (backport [#40539](https://github.com/frappe/erpnext/issues/40539)) ([#47735](https://github.com/frappe/erpnext/issues/47735)) ([70e6ea6](70e6ea6b3f))
* remove public access to list items (backport [#45838](https://github.com/frappe/erpnext/issues/45838)) ([5d9be73](5d9be7366b))
* skip last purchase rate for free item (backport [#47693](https://github.com/frappe/erpnext/issues/47693)) ([#47695](https://github.com/frappe/erpnext/issues/47695)) ([1f1cb33](1f1cb338fe))
* validation message format (backport [#47542](https://github.com/frappe/erpnext/issues/47542)) ([#47549](https://github.com/frappe/erpnext/issues/47549)) ([792f3af](792f3afa1b))
2025-05-27 11:46:57 +00:00
ruthra kumar
1cfb002de8 Merge pull request #47757 from frappe/version-14-hotfix
chore: release v14
2025-05-27 17:15:22 +05:30
ruthra kumar
3b686827e9 Merge pull request #47752 from frappe/mergify/bp/version-14-hotfix/pr-47736
fix: only include advances within the tcs period (backport #47736)
2025-05-27 14:10:45 +05:30
rohitwaghchaure
d37b12f385 fix: incorrect status in the serial no (#47740) 2025-05-27 13:23:40 +05:30
ljain112
43aeff38aa fix: only include advances within the tcs period
(cherry picked from commit 477ec9fdcc)
2025-05-27 07:49:16 +00:00
ruthra kumar
ad13dcd695 Merge pull request #47745 from frappe/mergify/bp/version-14-hotfix/pr-47549
fix: validation message format (backport #47542) (backport #47549)
2025-05-27 12:03:44 +05:30
mergify[bot]
792f3afa1b fix: validation message format (backport #47542) (#47549)
fix: validation message format (#47542)

(cherry picked from commit a18e1cffa7)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
(cherry picked from commit f225e1986e)
2025-05-27 06:04:55 +00:00
ruthra kumar
43dda4a5d2 Merge pull request #47741 from frappe/mergify/bp/version-14-hotfix/pr-47697
refactor: Fetch party name for contract (backport #47697)
2025-05-26 19:56:16 +05:30
ruthra kumar
274ce10329 chore: resolve conflicts 2025-05-26 17:46:42 +05:30
ruthra kumar
caf145e3ca refactor: patch old contract with full party name
(cherry picked from commit 8e2221178b)

# Conflicts:
#	erpnext/patches.txt
2025-05-26 12:11:25 +00:00
ruthra kumar
05911ad563 refactor: fetch party name on selection
(cherry picked from commit 752024e222)
2025-05-26 12:11:24 +00:00
ruthra kumar
de937a6bad refactor: full name field in contract
(cherry picked from commit 016924361a)

# Conflicts:
#	erpnext/crm/doctype/contract/contract.json
#	erpnext/crm/doctype/contract/contract.py
2025-05-26 12:11:24 +00:00
mergify[bot]
70e6ea6b3f fix: rate changing while making PR (negative discount) (backport #40539) (#47735)
fix: rate changing while making PR (negative discount) (#40539)

(cherry picked from commit 8136954484)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2025-05-26 16:02:06 +05:30
mergify[bot]
70bcfb4748 fix: patch to rename group_by filter in custom reports (backport #47709) (#47729)
* fix: patch to rename group_by filter in custom reports

(cherry picked from commit 0d19c18c06)

# Conflicts:
#	erpnext/patches.txt
#	erpnext/patches/v14_0/rename_group_by_to_categorize_by_in_custom_reports.py

* fix: using python instead of sql query

(cherry picked from commit 48eccb1f73)

* chore: resolve conflict

---------

Co-authored-by: diptanilsaha <diptanil@frappe.io>
2025-05-26 13:29:28 +05:30
mergify[bot]
1f1cb338fe fix: skip last purchase rate for free item (backport #47693) (#47695)
fix: skip last purchase rate for free item (#47693)

(cherry picked from commit c3b17024bd)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2025-05-24 17:17:09 +05:30
mergify[bot]
d5d4b3a7f3 fix: incorrect valuation rate due to positive qty (backport #47686) (#47687)
fix: incorrect valuation rate due to positive qty (#47686)

(cherry picked from commit 6ed97b5fda)

Co-authored-by: Khushi Rawat <142375893+khushi8112@users.noreply.github.com>
2025-05-22 16:17:42 +05:30
mergify[bot]
5d9be7366b fix: remove public access to list items (backport #45838)
* fix: remove public access to list items

(cherry picked from commit 2bd596ee3d)

# Conflicts:
#	erpnext/stock/doctype/item/item.json

* fix: resolve conflict

---------

Co-authored-by: CaseSolved <richard@casesolved.co.uk>
Co-authored-by: Sagar Vora <16315650+sagarvora@users.noreply.github.com>
2025-05-21 23:12:40 +05:30
Frappe PR Bot
79a4b597d4 chore(release): Bumped to Version 14.85.9
## [14.85.9](https://github.com/frappe/erpnext/compare/v14.85.8...v14.85.9) (2025-05-20)

### Bug Fixes

* asset image field updation issue (backport [#47615](https://github.com/frappe/erpnext/issues/47615)) ([d88feec](d88feecf46))
* better validation message with solution for BOM recursion (backport [#47472](https://github.com/frappe/erpnext/issues/47472)) ([#47476](https://github.com/frappe/erpnext/issues/47476)) ([537c917](537c917bfc))
* include only invoices with update_stock = 0  for billed amt in delivery note. ([54197ff](54197ff760))
* remove hardcoded doctype in `make_return_doc` ([9ce86b1](9ce86b135b))
2025-05-20 13:53:32 +00:00
ruthra kumar
0b0944cc06 Merge pull request #47635 from frappe/version-14-hotfix
chore: release v14
2025-05-20 19:22:04 +05:30
mergify[bot]
537c917bfc fix: better validation message with solution for BOM recursion (backport #47472) (#47476)
fix: better validation message with solution for BOM recursion

(cherry picked from commit 7103cdd84a)

Co-authored-by: Rohit Waghchaure <rohitw1991@gmail.com>
2025-05-20 16:15:40 +05:30
ruthra kumar
13f393c59a Merge pull request #47624 from frappe/mergify/bp/version-14-hotfix/pr-47559
fix: include only invoices with update_stock = 0  for billed amt in delivery note. (backport #47559)
2025-05-20 11:24:52 +05:30
ljain112
54197ff760 fix: include only invoices with update_stock = 0 for billed amt in delivery note.
(cherry picked from commit 6dc459db58)
2025-05-20 05:31:56 +00:00
ruthra kumar
6f1109226d Merge pull request #47621 from frappe/mergify/bp/version-14-hotfix/pr-47614
fix: remove hardcoded doctype in `make_return_doc` (backport #47614)
2025-05-20 10:37:00 +05:30
barredterra
9ce86b135b fix: remove hardcoded doctype in make_return_doc
(cherry picked from commit 45a5c19dd4)

# Conflicts:
#	erpnext/controllers/sales_and_purchase_return.py
2025-05-20 10:11:21 +05:30
mergify[bot]
d88feecf46 fix: asset image field updation issue (backport #47615) 2025-05-20 10:08:26 +05:30
Frappe PR Bot
271d0a301f chore(release): Bumped to Version 14.85.8
## [14.85.8](https://github.com/frappe/erpnext/compare/v14.85.7...v14.85.8) (2025-05-13)

### Bug Fixes

* broken test suite due to incorrect OR filter ([949ed59](949ed59f84))
* ignore "Account Closing Balance" doctype on Period Closing Voucher cancellation ([a04feff](a04feff264))
* typo in event.js ([2389fd5](2389fd5145))
* warning message for COGS account in the stock entry ([1bbbd26](1bbbd261cb))
2025-05-13 14:02:11 +00:00
ruthra kumar
9d36166616 Merge pull request #47529 from frappe/version-14-hotfix
chore: release v14
2025-05-13 19:30:41 +05:30
ruthra kumar
00407cd0ee Merge pull request #47526 from frappe/mergify/bp/version-14-hotfix/pr-47520
fix: ignore "Account Closing Balance" doctype on Period Closing Voucher cancellation (backport #47520)
2025-05-13 15:22:32 +05:30
ljain112
a04feff264 fix: ignore "Account Closing Balance" doctype on Period Closing Voucher cancellation
(cherry picked from commit d6602d63fc)

# Conflicts:
#	erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py
2025-05-13 15:00:36 +05:30
ruthra kumar
d6f5ac90b1 Merge pull request #47516 from frappe/mergify/bp/version-14-hotfix/pr-47367
fix: Use `Currency` instead of `Float` in GL report to show details (backport #47367)
2025-05-13 13:08:20 +05:30
Abdeali Chharchhodawala
c6c1b00559 Merge pull request #47367 from Abdeali099/gl-report-field-float-to-currency
fix: Use `Currency` instead of `Float` in GL report to show details
(cherry picked from commit e4e0bb68ec)
2025-05-13 12:49:52 +05:30
ruthra kumar
b86dc294f1 Merge pull request #47518 from frappe/mergify/bp/version-14-hotfix/pr-47380
fix: broken CI - uae vat 201 tests failing (backport #47380)
2025-05-13 12:45:41 +05:30
ruthra kumar
949ed59f84 fix: broken test suite due to incorrect OR filter
(cherry picked from commit 37d74e387d)
2025-05-13 06:39:22 +00:00
ruthra kumar
558225b027 Merge pull request #47466 from frappe/mergify/bp/version-14-hotfix/pr-47462
Update event.js (backport #47462)
2025-05-08 14:12:55 +05:30
Yaiphalemba Mangshatabam
2389fd5145 fix: typo in event.js
"Sales Partners" -> "Sales Partner"

(cherry picked from commit edee75c757)
2025-05-08 08:40:37 +00:00
rohitwaghchaure
d66a34411c Merge pull request #47453 from frappe/mergify/bp/version-14-hotfix/pr-47452
fix: warning message for COGS account in the stock entry (backport #47452)
2025-05-08 13:58:23 +05:30
Rohit Waghchaure
1bbbd261cb fix: warning message for COGS account in the stock entry
(cherry picked from commit bba6b0ff45)
2025-05-07 10:50:18 +00:00
Frappe PR Bot
182b9892fc chore(release): Bumped to Version 14.85.7
## [14.85.7](https://github.com/frappe/erpnext/compare/v14.85.6...v14.85.7) (2025-05-06)

### Bug Fixes

* backward compatibility for renamed group_by filter on reports (backport [#47362](https://github.com/frappe/erpnext/issues/47362)) ([#47402](https://github.com/frappe/erpnext/issues/47402)) ([378821d](378821d9e6))
* change shipping address fetching condition ([8e0bd97](8e0bd976c3))
* completed transactions showing in the list (backport [#47374](https://github.com/frappe/erpnext/issues/47374)) ([#47378](https://github.com/frappe/erpnext/issues/47378)) ([b507e63](b507e63375))
* do not allocate amount when ref's doctype or name are not set ([f278120](f278120aa0))
* rename unchanged group_by filter related to general ledger report (backport [#47366](https://github.com/frappe/erpnext/issues/47366)) ([#47404](https://github.com/frappe/erpnext/issues/47404)) ([d41fec7](d41fec7d2b))
* renaming group by fieldname and value in reports (backport [#47352](https://github.com/frappe/erpnext/issues/47352)) ([#47359](https://github.com/frappe/erpnext/issues/47359)) ([27a8856](27a8856dca))
* show party type in due date exceeding message ([4376fbc](4376fbc3ed))
* validation for difference account ([150cc5a](150cc5a664))
* warning message before changing the valuation method (backport [#47340](https://github.com/frappe/erpnext/issues/47340)) ([#47341](https://github.com/frappe/erpnext/issues/47341)) ([b13e0a6](b13e0a6b9f))
2025-05-06 14:13:22 +00:00
ruthra kumar
015525599f Merge pull request #47430 from frappe/version-14-hotfix
chore: release v14
2025-05-06 19:41:49 +05:30
mergify[bot]
3993525bf6 feat!: configure which rate is used to auto-update price list (backport #47417) (#47433)
* feat!: configure which rate is used to auto-update price list

(cherry picked from commit 3ebde4526a)

# Conflicts:
#	erpnext/selling/doctype/sales_order/test_sales_order.py
#	erpnext/setup/setup_wizard/operations/defaults_setup.py
#	erpnext/setup/setup_wizard/operations/install_fixtures.py
#	erpnext/stock/doctype/stock_settings/stock_settings.json
#	erpnext/stock/doctype/stock_settings/stock_settings.py
#	erpnext/stock/get_item_details.py

* fix: merge conflicts

---------

Co-authored-by: Sagar Vora <16315650+sagarvora@users.noreply.github.com>
2025-05-06 18:43:27 +05:30
ruthra kumar
8343c92ac5 Merge pull request #47425 from frappe/mergify/bp/version-14-hotfix/pr-47337
fix: do not allocate amount when ref's doctype or name are not set (backport #47337)
2025-05-06 15:01:05 +05:30
Abdeali Chharchhoda
f278120aa0 fix: do not allocate amount when ref's doctype or name are not set
(cherry picked from commit b9a02b466b)
2025-05-06 09:04:52 +00:00
ruthra kumar
862e6330ac Merge pull request #47415 from frappe/mergify/bp/version-14-hotfix/pr-47408
fix: show party type in due date exceeding message (backport #47408)
2025-05-06 14:32:58 +05:30
ruthra kumar
bd395e2406 chore: resolve conflicts and pass all parameters 2025-05-06 14:12:44 +05:30
Abdeali Chharchhoda
4376fbc3ed fix: show party type in due date exceeding message
(cherry picked from commit b6d9134014)

# Conflicts:
#	erpnext/accounts/party.py
2025-05-06 06:28:47 +00:00
ruthra kumar
c49ee563fd Merge pull request #47413 from frappe/mergify/bp/version-14-hotfix/pr-47358
fix: change shipping address fetching condition (backport #47358)
2025-05-06 11:29:16 +05:30
Vimal
8e0bd976c3 fix: change shipping address fetching condition
(cherry picked from commit 0b4add2f2b)
2025-05-06 05:28:40 +00:00
rohitwaghchaure
d66e8e8597 Merge pull request #47389 from frappe/mergify/bp/version-14-hotfix/pr-47376
fix: validation for difference account (backport #47376)
2025-05-05 18:39:04 +05:30
mergify[bot]
d41fec7d2b fix: rename unchanged group_by filter related to general ledger report (backport #47366) (#47404)
* fix: rename unchanged group_by filter related to general ledger report (#47366)

(cherry picked from commit 3de249dcba)

# Conflicts:
#	erpnext/accounts/doctype/payment_entry/payment_entry.js
#	erpnext/accounts/test/test_reports.py
#	erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js

* chore: resolve conflict

* chore: resolve conflict

* chore: resolve conflict

---------

Co-authored-by: Diptanil Saha <diptanil@frappe.io>
2025-05-05 18:18:23 +05:30
mergify[bot]
378821d9e6 fix: backward compatibility for renamed group_by filter on reports (backport #47362) (#47402)
fix: backward compatibility for renamed group_by filter on reports (#47362)

* fix: backward compatibility for renamed group_by filter in general ledger report

* fix: backward compatibility for renamed group_by filter in supplier quotation comparison report

(cherry picked from commit d4ffa54136)

Co-authored-by: Diptanil Saha <diptanil@frappe.io>
2025-05-05 18:01:06 +05:30
ruthra kumar
698e260526 Merge pull request #47146 from frappe/mergify/copy/version-14-hotfix/pr-47145
refactor: make AR / AP report more memory efficient (copy #47145)
2025-05-05 15:43:33 +05:30
ruthra kumar
fc793f0f25 refactor: set default for fetch methods 2025-05-05 15:04:48 +05:30
ruthra kumar
204d1d6a53 refactor: use fetch method based on configuration 2025-05-05 15:04:48 +05:30
ruthra kumar
aee917b790 refactor: configurable fetch method for AR / AP report 2025-05-05 15:04:45 +05:30
ruthra kumar
892d3980d3 refactor: use unbuffered cursor for fetching 2025-05-05 14:59:24 +05:30
rohitwaghchaure
e743d5f66b chore: fix linters issue 2025-05-05 14:19:20 +05:30
rohitwaghchaure
9adb863787 chore: fix linters issue 2025-05-05 14:12:07 +05:30
Rohit Waghchaure
150cc5a664 fix: validation for difference account
(cherry picked from commit fb819c558e)
2025-05-03 07:52:53 +00:00
mergify[bot]
b507e63375 fix: completed transactions showing in the list (backport #47374) (#47378)
* fix: completed transactions showing in the list (#47374)

(cherry picked from commit 97db9da10e)

# Conflicts:
#	erpnext/stock/doctype/stock_entry/stock_entry.js

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2025-05-02 15:29:10 +05:30
mergify[bot]
27a8856dca fix: renaming group by fieldname and value in reports (backport #47352) (#47359)
* fix: renaming group by fieldname and value in reports (#47352)

* fix: renaming in general ledger report

* fix: renaming in supplier quotation comparison report

* fix: renaming group by to categorize by in process statement of accounts

* fix: added patch

* fix: patch update to all documents

* chore: added patches to patch.txt

* chore: removing patch from v14

(cherry picked from commit 13a84e7f82)

# Conflicts:
#	erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json
#	erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py
#	erpnext/accounts/report/general_ledger/general_ledger.py
#	erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py
#	erpnext/patches.txt
#	erpnext/patches/v14_0/rename_group_by_to_categorize_by.py

* chore: resolve conflict

* chore: resolve conflict

* chore: resolve conflict

* chore: resolve conflict

* chore: resolve conflict

* chore: resolve conflict

* chore: fixed path for patch file

---------

Co-authored-by: Diptanil Saha <diptanil@frappe.io>
2025-04-30 18:22:59 +05:30
mergify[bot]
b13e0a6b9f fix: warning message before changing the valuation method (backport #47340) (#47341)
fix: warning message before changing the valuation method (#47340)

(cherry picked from commit ffdc4347e8)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2025-04-30 12:54:15 +05:30
Frappe PR Bot
04bb4f8415 chore(release): Bumped to Version 14.85.6
## [14.85.6](https://github.com/frappe/erpnext/compare/v14.85.5...v14.85.6) (2025-04-29)

### Bug Fixes

* allow to change valuation method from FIFO to Moving Average ([7130e5c](7130e5ccfb))
* calculate useful days of asset's life correctly for existing asset ([#47066](https://github.com/frappe/erpnext/issues/47066)) ([0bc3bc4](0bc3bc4851))
* item code not showing in the error message ([48748e7](48748e7142))
* remove invalid email account creation (backport [#47318](https://github.com/frappe/erpnext/issues/47318)) ([#47322](https://github.com/frappe/erpnext/issues/47322)) ([0c5c344](0c5c34409e))
2025-04-29 13:08:31 +00:00
ruthra kumar
6735ba0ab6 Merge pull request #47327 from frappe/version-14-hotfix
chore: release v14
2025-04-29 18:36:51 +05:30
mergify[bot]
0c5c34409e fix: remove invalid email account creation (backport #47318) (#47322)
fix: remove invalid email account creation (#47318)

(cherry picked from commit 7423e4187f)

Co-authored-by: Diptanil Saha <diptanil@frappe.io>
2025-04-29 12:40:25 +05:30
rohitwaghchaure
9dbc1cc5fb Merge pull request #47287 from frappe/mergify/bp/version-14-hotfix/pr-47268
fix: allow to change valuation method from FIFO to Moving Average (backport #47268)
2025-04-28 15:28:53 +05:30
Rohit Waghchaure
7130e5ccfb fix: allow to change valuation method from FIFO to Moving Average
(cherry picked from commit b454ed4b8f)
2025-04-28 09:06:30 +00:00
rohitwaghchaure
0e42516977 Merge pull request #47258 from rohitwaghchaure/fixed-support-46933
fix: item code not showing in the error message
2025-04-25 14:38:38 +05:30
Rohit Waghchaure
48748e7142 fix: item code not showing in the error message 2025-04-25 14:08:38 +05:30
Khushi Rawat
0bc3bc4851 fix: calculate useful days of asset's life correctly for existing asset (#47066)
* fix: calculate useful days of asset's life correctly for existing asset

* fix: test

* fix: test case correction
2025-04-23 12:30:59 +05:30
Frappe PR Bot
d791108649 chore(release): Bumped to Version 14.85.5
## [14.85.5](https://github.com/frappe/erpnext/compare/v14.85.4...v14.85.5) (2025-04-22)

### Bug Fixes

* create default warehouse ([#47125](https://github.com/frappe/erpnext/issues/47125)) ([a766aa0](a766aa0837))
* rate based on posting date in Tax Withholding Report ([17dc87a](17dc87a2df))
* respect mapped accounting dimensions ([e4c2442](e4c2442ba2))
2025-04-22 13:45:53 +00:00
ruthra kumar
0d84059fde Merge pull request #47205 from frappe/version-14-hotfix
chore: release v14
2025-04-22 19:14:23 +05:30
ruthra kumar
6868da5f6a Merge pull request #47198 from frappe/mergify/bp/version-14-hotfix/pr-47138
fix: rate based on posting date in Tax Withholding Report (backport #47138)
2025-04-22 14:07:19 +05:30
ljain112
f906568fea chore: added test case for date period in multiple tax withholding rules
(cherry picked from commit 515fe340a8)

# Conflicts:
#	erpnext/accounts/report/tds_payable_monthly/test_tds_payable_monthly.py
2025-04-22 13:33:16 +05:30
ljain112
17dc87a2df fix: rate based on posting date in Tax Withholding Report
(cherry picked from commit a32a79e90a)
2025-04-22 07:59:50 +00:00
Sagar Vora
522597d22b Merge pull request #47156 from frappe/mergify/bp/version-14-hotfix/pr-47154
fix: respect mapped accounting dimensions (backport #47154)
2025-04-19 13:03:56 +05:30
Sagar Vora
e4c2442ba2 fix: respect mapped accounting dimensions
(cherry picked from commit 7dbe27da19)
2025-04-19 07:22:23 +00:00
mergify[bot]
0d9bc4424c chore: migrate pre-commit config (backport #47132) (#47133)
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2025-04-17 14:22:23 +02:00
Raffael Meyer
35c9375f0d Merge pull request #47130 from frappe/mergify/bp/version-14-hotfix/pr-47125
fix: create default warehouse (backport #47125)
2025-04-17 13:56:56 +02:00
barredterra
f04c73b9c2 ci: update linters
copy config from version-15
2025-04-17 13:38:19 +02:00
Raffael Meyer
a766aa0837 fix: create default warehouse (#47125) 2025-04-17 13:38:10 +02:00
Frappe PR Bot
3248415667 chore(release): Bumped to Version 14.85.4
## [14.85.4](https://github.com/frappe/erpnext/compare/v14.85.3...v14.85.4) (2025-04-16)

### Bug Fixes

* correct doctype in item_wise_purchase register ([6ec33c0](6ec33c0098))
* go for lower case "on" because we already have translations for that ([056cc35](056cc35379))
* make report's "printed on" translatable ([f1a8643](f1a864349e))
* **Payment Entry:** set account type if missing (backport [#47069](https://github.com/frappe/erpnext/issues/47069)) (backport [#47070](https://github.com/frappe/erpnext/issues/47070)) ([#47072](https://github.com/frappe/erpnext/issues/47072)) ([b88f6c1](b88f6c1252))
* remove redundant letter head ([033fa09](033fa09eb4))
* revert [#46900](https://github.com/frappe/erpnext/issues/46900) - against_voucher filter in general ledger ([5844aaf](5844aafd12))
* serial no validation for stock reconciliation ([a3d4d34](a3d4d34454))
* stock entry repack amount calculation ([253a067](253a067592))
* update the modified date in for SLEs and GLs after rename ([8801584](8801584c8d))

### Performance Improvements

* stock ageing report generation ([7a74dac](7a74dac2c2))
2025-04-16 04:02:43 +00:00
ruthra kumar
4fb12754ce Merge pull request #47090 from frappe/version-14-hotfix
chore: release v14
2025-04-16 09:31:20 +05:30
ruthra kumar
503b89a764 Merge pull request #46952 from frappe/mergify/bp/version-14-hotfix/pr-46949
fix: improve translatability of query report print formats (backport #46913) (backport #46949)
2025-04-15 15:59:10 +05:30
mergify[bot]
b88f6c1252 fix(Payment Entry): set account type if missing (backport #47069) (backport #47070) (#47072)
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
fix(Payment Entry): set account type if missing (backport #47069) (#47070)
fix(Payment Entry): set account type if missing (#47069)
2025-04-14 19:19:50 +02:00
rohitwaghchaure
e5156e6666 Merge pull request #47068 from frappe/mergify/bp/version-14-hotfix/pr-46997
fix: update the modified date in for SLEs and GLs after rename (backport #46997)
2025-04-14 21:06:08 +05:30
Rohit Waghchaure
8801584c8d fix: update the modified date in for SLEs and GLs after rename
(cherry picked from commit dc5a5ef258)
2025-04-14 15:02:22 +00:00
rohitwaghchaure
55ac96ff09 Merge pull request #47062 from frappe/mergify/bp/version-14-hotfix/pr-46853
fix: stock entry repack amount calculation (backport #46853)
2025-04-14 20:30:38 +05:30
rohitwaghchaure
3f3fb323cf chore: fix conflicts 2025-04-14 18:02:24 +05:30
Rohit Waghchaure
253a067592 fix: stock entry repack amount calculation
(cherry picked from commit 544ceb93cd)

# Conflicts:
#	erpnext/stock/stock_ledger.py
2025-04-14 12:30:21 +00:00
Frappe PR Bot
d8f301f1db chore(release): Bumped to Version 14.85.3
## [14.85.3](https://github.com/frappe/erpnext/compare/v14.85.2...v14.85.3) (2025-04-14)

### Bug Fixes

* revert [#46900](https://github.com/frappe/erpnext/issues/46900) - against_voucher filter in general ledger ([27ecd3a](27ecd3a21b))
2025-04-14 08:24:00 +00:00
ruthra kumar
6d65c967da Merge pull request #47056 from frappe/mergify/bp/version-14/pr-47049
Revert "fix: remove against_voucher and against_voucher_type column from General Ledger Report" (backport #47049)
2025-04-14 13:52:35 +05:30
ruthra kumar
4ac5c7e5e7 Merge pull request #47054 from frappe/mergify/bp/version-14-hotfix/pr-47049
Revert "fix: remove against_voucher and against_voucher_type column from General Ledger Report" (backport #47049)
2025-04-14 13:51:45 +05:30
ruthra kumar
911014a54e chore: resolve conflict 2025-04-14 13:27:13 +05:30
ruthra kumar
694f158fc8 chore: resolve conflict 2025-04-14 13:25:36 +05:30
ruthra kumar
27ecd3a21b fix: revert #46900 - against_voucher filter in general ledger
(cherry picked from commit adb331ef71)

# Conflicts:
#	erpnext/accounts/report/general_ledger/general_ledger.py
2025-04-14 07:50:45 +00:00
ruthra kumar
5844aafd12 fix: revert #46900 - against_voucher filter in general ledger
(cherry picked from commit adb331ef71)

# Conflicts:
#	erpnext/accounts/report/general_ledger/general_ledger.py
2025-04-14 07:49:35 +00:00
Frappe PR Bot
e684807bc7 chore(release): Bumped to Version 14.85.2
## [14.85.2](https://github.com/frappe/erpnext/compare/v14.85.1...v14.85.2) (2025-04-12)

### Bug Fixes

* correct doctype in item_wise_purchase register ([0e1045e](0e1045e855))
2025-04-12 02:03:19 +00:00
ruthra kumar
3af506e0a9 Merge pull request #47030 from frappe/mergify/bp/version-14/pr-47012
fix: correct doctype in item_wise_purchase register (backport #47012)
2025-04-12 07:31:49 +05:30
ruthra kumar
98ffbfb432 Merge pull request #47032 from frappe/mergify/bp/version-14-hotfix/pr-47012
fix: correct doctype in item_wise_purchase register (backport #47012)
2025-04-12 07:29:28 +05:30
ljain112
6ec33c0098 fix: correct doctype in item_wise_purchase register
(cherry picked from commit b8b8dce733)
2025-04-12 01:41:16 +00:00
ljain112
0e1045e855 fix: correct doctype in item_wise_purchase register
(cherry picked from commit b8b8dce733)
2025-04-12 01:41:12 +00:00
Frappe PR Bot
34d3f58149 chore(release): Bumped to Version 14.85.1
## [14.85.1](https://github.com/frappe/erpnext/compare/v14.85.0...v14.85.1) (2025-04-10)

### Bug Fixes

* serial no validation for stock reconciliation ([c388b75](c388b7579c))

### Performance Improvements

* stock ageing report generation ([eaa2974](eaa297475f))
2025-04-10 06:02:52 +00:00
rohitwaghchaure
efd34817be Merge pull request #46986 from frappe/mergify/bp/version-14/pr-46971
fix: serial no validation for stock reconciliation (backport #46971)
2025-04-10 11:31:22 +05:30
rohitwaghchaure
124ad729ab Merge pull request #46984 from frappe/mergify/bp/version-14/pr-46983
perf: stock ageing report generation (backport #46983)
2025-04-10 11:31:13 +05:30
Rohit Waghchaure
c388b7579c fix: serial no validation for stock reconciliation
(cherry picked from commit a3d4d34454)
2025-04-10 05:36:45 +00:00
rohitwaghchaure
a2595350f6 Merge pull request #46971 from rohitwaghchaure/fixed-support-35746
fix: serial no validation for stock reconciliation
2025-04-10 11:05:45 +05:30
Rohit Waghchaure
eaa297475f perf: stock ageing report generation
(cherry picked from commit 7a74dac2c2)
2025-04-10 05:34:42 +00:00
rohitwaghchaure
ac7700dff0 Merge pull request #46983 from rohitwaghchaure/fixed-stock-ageing-report
perf: stock ageing report generation
2025-04-10 11:03:40 +05:30
Rohit Waghchaure
7a74dac2c2 perf: stock ageing report generation 2025-04-10 10:10:09 +05:30
Rohit Waghchaure
a3d4d34454 fix: serial no validation for stock reconciliation 2025-04-09 17:14:40 +05:30
barredterra
443ed5b2ce chore: add missing german translation
(cherry picked from commit d94ebd0c78)
2025-04-08 14:12:41 +00:00
barredterra
033fa09eb4 fix: remove redundant letter head
(cherry picked from commit 7896f8a855)
2025-04-08 14:12:41 +00:00
barredterra
056cc35379 fix: go for lower case "on" because we already have translations for that
(cherry picked from commit 7cf83ffce7)
2025-04-08 14:12:40 +00:00
barredterra
f1a864349e fix: make report's "printed on" translatable
(cherry picked from commit 18e9a9881c)
2025-04-08 14:12:40 +00:00
Frappe PR Bot
24681bd64f chore(release): Bumped to Version 14.85.0
# [14.85.0](https://github.com/frappe/erpnext/compare/v14.84.0...v14.85.0) (2025-04-08)

### Bug Fixes

* **accounting:** update outstanding amount based on update_outstanding_for_self ([8e2bfc6](8e2bfc6bcb))
* don't filter payment entries on Bank Account in Payment Clearance ([576ce7e](576ce7e882))
* empty party filter on change of party type in General Ledger Report. ([14b4d14](14b4d147a8))
* improved rounding adjustment when applying discount (backport [#46720](https://github.com/frappe/erpnext/issues/46720)) ([06c3299](06c32993ea))
* incorrect condition ([d1f9444](d1f9444be7))
* make message translatable (backport [#46863](https://github.com/frappe/erpnext/issues/46863)) ([#46865](https://github.com/frappe/erpnext/issues/46865)) ([70d117e](70d117e858))
* **payment term:** allocate payment amount when payment term is fetched from order ([36b951d](36b951d018))
* remove against_voucher from General Ledger Report ([9e3a041](9e3a04136c))
* removed customer_group query in customer.js ([3be0f00](3be0f00b3b))
* Translate UnReconcile dialog title (backport [#46818](https://github.com/frappe/erpnext/issues/46818)) ([#46862](https://github.com/frappe/erpnext/issues/46862)) ([5f467be](5f467be0c8))
* update outstanding with precision ([698d5be](698d5be840))
* update payment amount if automatically_fetch_payment_terms is enabled ([dda35b8](dda35b8e51))
* update posting date before running validations ([430d1e8](430d1e8b2e))
* use `grand_total_diff` instead of `rounding_adjustment` in `taxes_and_totals` (backport [#46829](https://github.com/frappe/erpnext/issues/46829)) ([233e0d8](233e0d8049))
* user permissions in sales and purchase report ([f26b22e](f26b22ee7f))

### Features

* asset filter in asset depreciation and balances report ([#46848](https://github.com/frappe/erpnext/issues/46848)) ([2fe9fa7](2fe9fa7ef7))
2025-04-08 13:05:14 +00:00
ruthra kumar
b0c742b64e Merge pull request #46941 from frappe/version-14-hotfix
chore: release v14
2025-04-08 18:33:42 +05:30
mergify[bot]
18282b2632 chore: fix german translations (backport #46912) (#46914)
* chore: fix german translations (#46912)

(cherry picked from commit 8276e8e8b3)

# Conflicts:
#	erpnext/translations/de.csv

* chore: resolve conflicts

* chore: delete duplicate translations

---------

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2025-04-08 13:16:35 +02:00
ruthra kumar
46115d6f71 Merge pull request #46937 from frappe/mergify/bp/version-14-hotfix/pr-46631
fix: update outstanding for self (backport #46631)
2025-04-08 16:21:45 +05:30
ruthra kumar
f62905f7a7 chore: pass individual range 2025-04-08 15:53:30 +05:30
ruthra kumar
88facb7523 refactor: pass both doctype and name 2025-04-08 15:13:59 +05:30
ruthra kumar
512877ab46 chore: resolve conflicts 2025-04-08 14:34:59 +05:30
Bhavan23
c7e6b2356f test: add unit test to validate outstanding amount for update_outstanding_for_self checkbox enabled
(cherry picked from commit 7b0882600a)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
2025-04-08 08:59:05 +00:00
Bhavan23
8e2bfc6bcb fix(accounting): update outstanding amount based on update_outstanding_for_self
fix(accounting): against voucher has been already paid show proper message and update update_outstanding_for_self as 1

(cherry picked from commit 222f1834f1)

# Conflicts:
#	erpnext/controllers/accounts_controller.py
2025-04-08 08:59:04 +00:00
ruthra kumar
98260b990c Merge pull request #46931 from frappe/mergify/bp/version-14-hotfix/pr-46821
fix: removed customer_group query in customer.js (backport #46821)
2025-04-08 13:10:04 +05:30
ljain112
3be0f00b3b fix: removed customer_group query in customer.js
(cherry picked from commit f49adfdd98)
2025-04-08 07:28:33 +00:00
ruthra kumar
f44478db78 Merge pull request #46924 from frappe/mergify/bp/version-14-hotfix/pr-46709
fix: user permissions in sales and purchase report (backport #46709)
2025-04-08 11:53:08 +05:30
ruthra kumar
db8cb70bd8 chore: resolve conflict 2025-04-08 11:13:44 +05:30
ljain112
f26b22ee7f fix: user permissions in sales and purchase report
(cherry picked from commit f4bc1dfd00)

# Conflicts:
#	erpnext/accounts/report/purchase_register/purchase_register.py
2025-04-08 05:40:47 +00:00
ruthra kumar
1e4807f16d Merge pull request #46920 from frappe/mergify/bp/version-14-hotfix/pr-46804
fix: update outstanding with precision (backport #46804)
2025-04-08 11:07:45 +05:30
ljain112
698d5be840 fix: update outstanding with precision
(cherry picked from commit aadda9f606)
2025-04-08 04:29:31 +00:00
ruthra kumar
59e46e22d3 Merge pull request #46904 from frappe/mergify/bp/version-14-hotfix/pr-46895
fix: empty party filter on change of party type in General Ledger Report (backport #46895)
2025-04-07 17:55:56 +05:30
ljain112
14b4d147a8 fix: empty party filter on change of party type in General Ledger Report.
(cherry picked from commit 9c68bc22fa)
2025-04-07 12:12:55 +00:00
ruthra kumar
2bed1c8336 Merge pull request #46901 from frappe/mergify/bp/version-14-hotfix/pr-46900
fix: remove against_voucher and against_voucher_type column from General Ledger Report (backport #46900)
2025-04-07 17:38:42 +05:30
ljain112
9e3a04136c fix: remove against_voucher from General Ledger Report
(cherry picked from commit 6d1f119a0f)
2025-04-07 17:14:49 +05:30
ruthra kumar
dcc53cea55 Merge pull request #46896 from frappe/mergify/bp/version-14-hotfix/pr-46728
fix: update posting date before running validations (backport #46728)
2025-04-07 14:54:37 +05:30
Dany Robert
430d1e8b2e fix: update posting date before running validations
(cherry picked from commit d04dbd8ed9)
2025-04-07 09:01:56 +00:00
ruthra kumar
36a366d962 Merge pull request #46890 from frappe/mergify/bp/version-14-hotfix/pr-46637
fix(payment term): allocate payment amount when payment term is fetched from order (backport #46637)
2025-04-07 11:25:58 +05:30
venkat102
dda35b8e51 fix: update payment amount if automatically_fetch_payment_terms is enabled
(cherry picked from commit 7bf1a39861)
2025-04-07 05:34:19 +00:00
venkat102
4e0d7d88ec test: validate payment schedule based on invoice amount
(cherry picked from commit 7785296573)
2025-04-07 05:34:19 +00:00
venkat102
36b951d018 fix(payment term): allocate payment amount when payment term is fetched from order
(cherry picked from commit 5618859bd8)
2025-04-07 05:34:18 +00:00
mergify[bot]
233e0d8049 fix: use grand_total_diff instead of rounding_adjustment in taxes_and_totals (backport #46829)
* fix: use `grand_total_diff` instead of `rounding_adjustment` in `taxes_and_totals`

(cherry picked from commit fd252da6b1)

* test: ensure correct grand total

---------

Co-authored-by: vishakhdesai <vishakhdesai@gmail.com>
Co-authored-by: Sagar Vora <sagar@resilient.tech>
2025-04-06 09:11:06 +05:30
mergify[bot]
5f467be0c8 fix: Translate UnReconcile dialog title (backport #46818) (#46862)
fix: Translate UnReconcile dialog title

(cherry picked from commit f2cfb03c2c)

Co-authored-by: Corentin Forler <corentin@dokos.io>
2025-04-05 17:37:16 +02:00
mergify[bot]
70d117e858 fix: make message translatable (backport #46863) (#46865)
fix: make message translatable (#46863)

(cherry picked from commit 7d12e9afd4)

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2025-04-05 17:36:16 +02:00
Khushi Rawat
2fe9fa7ef7 feat: asset filter in asset depreciation and balances report (#46848) 2025-04-04 10:39:16 +05:30
Sagar Vora
b4ca51ad4c Merge pull request #46813 from frappe/mergify/bp/version-14-hotfix/pr-46812
fix: revert resetting `rounding_adjustment` (backport #46812)
2025-03-31 15:56:27 +05:30
Vishakh Desai
fb5777cf10 Merge pull request #46812 from vishakhdesai/fix-taxes-and-totals
fix: revert resetting `rounding_adjustment`
(cherry picked from commit 3a9dca0563)
2025-03-31 10:25:00 +00:00
mergify[bot]
06c32993ea fix: improved rounding adjustment when applying discount (backport #46720)
* fix: improved rounding adjustment when applying discount (#46720)

* fix: rounding adjustment in apply_discount_amount taxes_and_totals

* refactor: minor changes

* fix: set the rounding difference while calculating tax total in the last tax row and add test case

* fix: failing test case

* fix: made changes in get_total_for_discount_amount in taxes_and_totals

* fix: failing test cases

* fix: changes as per review

* refactor: remove unnecessary use of flt

* refactor: improve logic

* refactor: minor change

* refactor: minor changes

* fix: add a test case for applying discount with previous row total in taxes

* fix: failing test case

* refactor: flatter code, remove `flt` usage for accuracy

---------

Co-authored-by: Vishakh Desai <78500008+vishakhdesai@users.noreply.github.com>
Co-authored-by: Sagar Vora <sagar@resilient.tech>
2025-03-31 15:54:09 +05:30
rohitwaghchaure
466c6ee3d4 Merge pull request #46780 from frappe/mergify/bp/version-14-hotfix/pr-46777
fix: incorrect condition (backport #46777)
2025-03-29 12:17:59 +05:30
rohitwaghchaure
a8831351e3 chore: fix conflicts 2025-03-29 12:16:17 +05:30
Rohit Waghchaure
d1f9444be7 fix: incorrect condition
(cherry picked from commit 0c1a8e9c58)

# Conflicts:
#	erpnext/public/js/controllers/transaction.js
2025-03-28 18:19:41 +00:00
rohitwaghchaure
ceaea16d84 Merge pull request #46751 from frappe/mergify/bp/version-14/pr-46748
Revert "perf: timeout while renaming cost center (backport #46641)" (backport #46748)
2025-03-27 13:13:59 +05:30
rohitwaghchaure
119f2e7074 Revert "perf: timeout while renaming cost center (backport #46641)"
(cherry picked from commit da6affbba7)
2025-03-27 06:53:19 +00:00
rohitwaghchaure
9cadb89678 Merge pull request #46748 from frappe/revert-46646-mergify/bp/version-14-hotfix/pr-46641
Revert "perf: timeout while renaming cost center (backport #46641)"
2025-03-27 12:16:56 +05:30
rohitwaghchaure
da6affbba7 Revert "perf: timeout while renaming cost center (backport #46641)" 2025-03-27 11:57:24 +05:30
Sagar Vora
6293b08540 Merge pull request #46729 from vishakhdesai/bank-clearance-fix-v14-backport
fix: don't filter payment entries on Bank Account in Payment Clearance (backport #46669)
2025-03-26 13:00:57 +05:30
vishakhdesai
576ce7e882 fix: don't filter payment entries on Bank Account in Payment Clearance 2025-03-26 11:41:35 +05:30
Frappe PR Bot
67d7ffa430 chore(release): Bumped to Version 14.84.0
# [14.84.0](https://github.com/frappe/erpnext/compare/v14.83.4...v14.84.0) (2025-03-25)

### Bug Fixes

* assign dialog instance to a variable in update_child_items function ([690a939](690a939572))
* customer credit limit check based on `bypass_credit_limit_check` in Journal Entry ([331ecc1](331ecc1964))
* do not validate if conversion rate is 1 for different currency ([c3447c0](c3447c030a))
* fix lint issues with trailing whitespaces ([df64d2e](df64d2ef4e))
* removed test case ([509c5c4](509c5c4d17))

### Features

* repost accounting ledger for purchase receipt ([88e664b](88e664b79f))

### Performance Improvements

* timeout while renaming cost center ([b9b08c3](b9b08c35ef))
2025-03-25 12:25:57 +00:00
ruthra kumar
d90c8a9150 Merge pull request #46714 from frappe/version-14-hotfix
chore: release v14
2025-03-25 17:54:27 +05:30
ruthra kumar
15bb1fdb24 Merge pull request #46712 from frappe/mergify/bp/version-14-hotfix/pr-46616
fix: do not validate if conversion rate is 1 for different currency (backport #46616)
2025-03-25 16:06:08 +05:30
ljain112
509c5c4d17 fix: removed test case 2025-03-25 15:22:19 +05:30
ruthra kumar
cb028b8740 chore: resolve conflict 2025-03-25 14:26:14 +05:30
ljain112
c3447c030a fix: do not validate if conversion rate is 1 for different currency
(cherry picked from commit e8a66d03bc)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
2025-03-25 08:42:17 +00:00
ruthra kumar
1d65f995e7 Merge pull request #46646 from frappe/mergify/bp/version-14-hotfix/pr-46641
perf: timeout while renaming cost center (backport #46641)
2025-03-25 13:27:09 +05:30
ruthra kumar
3a6d8cd9a1 Merge pull request #46704 from frappe/mergify/bp/version-14-hotfix/pr-46622
feat: repost accounting ledger for purchase receipt (backport #46622)
2025-03-25 12:13:50 +05:30
ljain112
88e664b79f feat: repost accounting ledger for purchase receipt
(cherry picked from commit b36e356469)
2025-03-25 06:19:33 +00:00
ruthra kumar
090ee7f042 Merge pull request #46690 from IMS94/fix-46686-so-update-items
fix: assign dialog instance to a variable in update_child_items function
2025-03-25 11:18:45 +05:30
mergify[bot]
b311b6eb7f ci: apply label "skip-release-notes" based on PR title (backport #46694) (#46696)
ci: apply label "skip-release-notes" based on PR title (#46694)

Workflow copied from frappe/frappe

(cherry picked from commit eb350012b0)

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2025-03-24 16:55:07 +01:00
Imesha Sudasingha
df64d2ef4e fix: fix lint issues with trailing whitespaces 2025-03-24 12:50:48 +00:00
Imesha Sudasingha
690a939572 fix: assign dialog instance to a variable in update_child_items function
`dialog` is being referred in `onchange` handlers of the fields. Without this fix, they are failing because `dialog` is not defined.
2025-03-24 12:42:42 +00:00
ruthra kumar
a3639b055c Merge pull request #46676 from frappe/mergify/bp/version-14-hotfix/pr-46577
fix: customer credit limit check based on `bypass_credit_limit_check` in Journal Entry (backport #46577)
2025-03-24 13:58:00 +05:30
ljain112
331ecc1964 fix: customer credit limit check based on bypass_credit_limit_check in Journal Entry
(cherry picked from commit 8a84faebed)
2025-03-24 08:01:38 +00:00
rohitwaghchaure
736b125e14 chore: fix conflicts 2025-03-22 20:49:56 +05:30
Rohit Waghchaure
b9b08c35ef perf: timeout while renaming cost center
(cherry picked from commit 92be7cbbbf)

# Conflicts:
#	erpnext/accounts/doctype/gl_entry/gl_entry.json
2025-03-21 12:38:52 +00:00
Frappe PR Bot
7e8d2dc0fc chore(release): Bumped to Version 14.83.4
## [14.83.4](https://github.com/frappe/erpnext/compare/v14.83.3...v14.83.4) (2025-03-19)

### Bug Fixes

* fetch quality inspection parameter group ([6e497f7](6e497f73f1))
* not able to make PR against stand alone Debit Note ([16fe53b](16fe53b8c7))
* order by condition ([af3089b](af3089b96e))
* repost future sle and gle after capitalization ([#46576](https://github.com/frappe/erpnext/issues/46576)) ([805549f](805549f793))
* set stock adjustment account in difference account ([6f0c67a](6f0c67a242))
* **Transaction Deletion Record:** sql syntax error while fetching lead address ([20b43b4](20b43b4d93))
2025-03-19 11:04:55 +00:00
ruthra kumar
39af6bd143 Merge pull request #46581 from frappe/version-14-hotfix
chore: release v14
2025-03-19 16:33:27 +05:30
rohitwaghchaure
300f1a860b Merge pull request #46609 from rohitwaghchaure/fixed-support-33137
fix: order by condition
2025-03-19 15:36:43 +05:30
rohitwaghchaure
1441ef2532 Merge pull request #46610 from frappe/mergify/bp/version-14-hotfix/pr-46595
fix: not able to make PR against stand alone Debit Note (backport #46595)
2025-03-19 15:36:15 +05:30
rohitwaghchaure
64fd5c0ba9 chore: fix conflicts 2025-03-19 15:31:05 +05:30
Rohit Waghchaure
16fe53b8c7 fix: not able to make PR against stand alone Debit Note
(cherry picked from commit 6a52c30591)

# Conflicts:
#	erpnext/public/js/controllers/buying.js
2025-03-19 08:05:24 +00:00
Rohit Waghchaure
af3089b96e fix: order by condition 2025-03-19 13:20:56 +05:30
rohitwaghchaure
3cb29fc035 Merge pull request #46583 from frappe/mergify/bp/version-14-hotfix/pr-46575
fix: fetch quality inspection parameter group (backport #46575)
2025-03-18 17:28:54 +05:30
rohitwaghchaure
8a93057844 Merge pull request #46586 from frappe/mergify/bp/version-14-hotfix/pr-46576
fix: repost future sle and gle after capitalization (backport #46576)
2025-03-18 17:28:15 +05:30
Khushi Rawat
805549f793 fix: repost future sle and gle after capitalization (#46576)
(cherry picked from commit 29d77aa19f)
2025-03-18 10:31:48 +00:00
Mihir Kandoi
6e497f73f1 fix: fetch quality inspection parameter group
(cherry picked from commit 0a482c7ea8)
2025-03-18 10:27:29 +00:00
ruthra kumar
33f090a8c8 Merge pull request #46561 from frappe/mergify/bp/version-14-hotfix/pr-46557
fix(Transaction Deletion Record): sql syntax error while deleting lead address (backport #46557)
2025-03-17 17:00:49 +05:30
venkat102
20b43b4d93 fix(Transaction Deletion Record): sql syntax error while fetching lead address
(cherry picked from commit af0d6eeae8)
2025-03-17 11:08:38 +00:00
ruthra kumar
5c1f6f0107 Merge pull request #46511 from aerele/fix-v14/item-stock-difference-account
fix: set stock adjustment account in difference account (backport #45606)
2025-03-14 09:23:52 +05:30
Bhavan23
6f0c67a242 fix: set stock adjustment account in difference account 2025-03-13 17:01:55 +05:30
Frappe PR Bot
609bfaefba chore(release): Bumped to Version 14.83.3
## [14.83.3](https://github.com/frappe/erpnext/compare/v14.83.2...v14.83.3) (2025-03-12)

### Bug Fixes

* **account:** update account number from parent company ([1f54c27](1f54c272ac))
* not able to save work order with alternative item ([6ca1f9b](6ca1f9bc73))
2025-03-12 14:13:24 +00:00
ruthra kumar
2546589b4c Merge pull request #46445 from frappe/version-14-hotfix
chore: release v14
2025-03-12 19:41:47 +05:30
Shanuka Hewage
f311a0fc1c Fix: add parenttype condition to payment schedule query in accounts receivable report (#46370)
fix: add parenttype condition to payment schedule query in accounts receivable report
2025-03-12 16:07:07 +05:30
mergify[bot]
a1585b3c53 ci: ignore PRs labeled with "skip-release-notes" when generating release notes (backport #46453) (#46456)
ci: ignore PRs labeled with "skip-release-notes" when generating release notes

(cherry picked from commit 57007bf937)

Co-authored-by: barredterra <14891507+barredterra@users.noreply.github.com>
2025-03-12 10:33:07 +05:30
ruthra kumar
05c0bf5d99 Merge pull request #46449 from frappe/mergify/bp/version-14-hotfix/pr-46398
chore: rename print and stationery account (backport #46398)
2025-03-11 18:12:08 +05:30
chethank1407
247a006b5a chore: rename print and stationery account
(cherry picked from commit 615997b774)
2025-03-11 12:03:21 +00:00
ruthra kumar
e0cf6393ee Merge pull request #46438 from frappe/mergify/bp/version-14-hotfix/pr-46372
fix(account): update account number from parent company (backport #46372)
2025-03-11 11:24:35 +05:30
venkat102
1f54c272ac fix(account): update account number from parent company
(cherry picked from commit 4a4894bc01)
2025-03-11 05:32:05 +00:00
rohitwaghchaure
9a1321ab02 Merge pull request #46414 from rohitwaghchaure/fixed-support-32397
fix: not able to save work order with alternative item
2025-03-10 17:38:37 +05:30
Rohit Waghchaure
6ca1f9bc73 fix: not able to save work order with alternative item 2025-03-10 12:46:45 +05:30
Frappe PR Bot
b404f00ece chore(release): Bumped to Version 14.83.2
## [14.83.2](https://github.com/frappe/erpnext/compare/v14.83.1...v14.83.2) (2025-03-05)

### Bug Fixes

* Add permission check in POS's `Toggle Recent Orders` (backport [#46010](https://github.com/frappe/erpnext/issues/46010)) ([#46273](https://github.com/frappe/erpnext/issues/46273)) ([557a05b](557a05b0ad))
* auto allocation for negative amount outstanding for Customers in Payment Entry ([8e02dcf](8e02dcfcaa))
* change voucher_type and voucher_no field type to data ([df4f4d9](df4f4d9a31))
* consolidate gl entries by project in General Ledger Report (backport [#46314](https://github.com/frappe/erpnext/issues/46314)) ([#46320](https://github.com/frappe/erpnext/issues/46320)) ([b3b7e62](b3b7e62a90))
* do not include opening invoices in billed items to be received report ([cedf577](cedf577b4c))
* don't allow renaming account while system is actively in use (backport [#46176](https://github.com/frappe/erpnext/issues/46176)) ([#46209](https://github.com/frappe/erpnext/issues/46209)) ([167069b](167069b823))
* exclude cancelled gl entries ([369a692](369a692af9))
* incorrect batch picked in the pick list ([ac25d3e](ac25d3e1c4))
* incorrectly billed amount in the purchase receipt ([ca94ad3](ca94ad3a24))
* inventory dimension for maintence visit ([69a8e0d](69a8e0dfac))
* only include submitted docs for internal received quantity validation (backport [#46262](https://github.com/frappe/erpnext/issues/46262)) ([#46303](https://github.com/frappe/erpnext/issues/46303)) ([90b5f0b](90b5f0b7bf))
* **patch:** Ensure SLE indexes (backport [#46131](https://github.com/frappe/erpnext/issues/46131)) ([#46134](https://github.com/frappe/erpnext/issues/46134)) ([f8bbcab](f8bbcab3a5))

### Performance Improvements

* don't track seen for POS Invoice (backport [#46187](https://github.com/frappe/erpnext/issues/46187)) ([#46188](https://github.com/frappe/erpnext/issues/46188)) ([b41ee66](b41ee667b9))
2025-03-05 13:28:51 +00:00
ruthra kumar
0f56fbcf6d Merge pull request #46264 from frappe/version-14-hotfix
chore: release v14
2025-03-05 18:57:30 +05:30
mergify[bot]
b3b7e62a90 fix: consolidate gl entries by project in General Ledger Report (backport #46314) (#46320)
fix: consolidate gl entries by project in General Ledger Report (#46314)

(cherry picked from commit 1f685efcaf)

Co-authored-by: Lakshit Jain <108322669+ljain112@users.noreply.github.com>
2025-03-05 16:18:34 +05:30
rohitwaghchaure
35a728d22b Merge pull request #46316 from rohitwaghchaure/fixed-support-31024-v14
fix: incorrect batch picked in the pick list
2025-03-05 15:43:04 +05:30
Rohit Waghchaure
ac25d3e1c4 fix: incorrect batch picked in the pick list 2025-03-05 14:29:22 +05:30
mergify[bot]
90b5f0b7bf fix: only include submitted docs for internal received quantity validation (backport #46262) (#46303)
fix: only include submitted docs for internal received quantity validation (#46262)

(cherry picked from commit 88fcdbb81e)

Co-authored-by: Lakshit Jain <108322669+ljain112@users.noreply.github.com>
2025-03-05 13:17:17 +05:30
mergify[bot]
557a05b0ad fix: Add permission check in POS's Toggle Recent Orders (backport #46010) (#46273)
* fix: use get_list to check permissions

(cherry picked from commit a08bc6b913)

# Conflicts:
#	erpnext/selling/page/point_of_sale/point_of_sale.py

* fix: resolve conflicts

---------

Co-authored-by: Sanket322 <shahsanket322003.com>
Co-authored-by: ljain112 <ljain112@gmail.com>
2025-03-05 12:04:39 +05:30
mergify[bot]
b41ee667b9 perf: don't track seen for POS Invoice (backport #46187) (#46188)
* perf: don't track seen for POS Invoice (#46187)

This is a moving doctype. Do people even browse the list view?

It doesn't make much sense, either. POS INvoices are rarely "reviewed" by multiple users.

(cherry picked from commit ded0aab680)

# Conflicts:
#	erpnext/accounts/doctype/pos_invoice/pos_invoice.json

* chore: conflicts

---------

Co-authored-by: Ankush Menat <ankush@frappe.io>
2025-03-05 12:03:27 +05:30
mergify[bot]
fbf6d8c9e2 chore: erpnext.com -> frappe.io/erpnext (backport #46288) (#46289)
* chore: erpnext.com -> frappe.io/erpnext (#46288)

(cherry picked from commit 41fe30ea6e)

# Conflicts:
#	README.md
#	pyproject.toml

* Update README.md

* Update pyproject.toml

---------

Co-authored-by: Ankush Menat <ankush@frappe.io>
2025-03-05 11:37:49 +05:30
mergify[bot]
167069b823 fix: don't allow renaming account while system is actively in use (backport #46176) (#46209)
* fix: don't allow renaming account while system is actively in use (#46176)

(cherry picked from commit 999f1cf96d)

# Conflicts:
#	erpnext/accounts/doctype/account/account.py

* chore: conflicts

---------

Co-authored-by: Ankush Menat <ankush@frappe.io>
2025-03-05 10:37:56 +05:30
Lakshit Jain
2d72a37d0c Merge pull request #46275 from frappe/mergify/bp/version-14-hotfix/pr-46039
fix: Ensure new line is added regardless of postal code presence (backport #46039)
2025-03-04 19:14:28 +05:30
Lakshit Jain
77ad224cf6 Merge pull request #46267 from frappe/mergify/bp/version-14-hotfix/pr-45896
fix: auto allocation for negative amount outstanding for Customers in Payment Entry (backport #45896)
2025-03-04 18:17:28 +05:30
Lakshit Jain
b5b55fad6c Merge pull request #46269 from frappe/mergify/bp/version-14-hotfix/pr-46260
fix: do not include opening invoices in billed items to be received report (backport #46260)
2025-03-04 18:16:57 +05:30
Sanket322
7cde990d69 refactor: add new line ragardless of postal code
(cherry picked from commit 746adfd057)
2025-03-04 12:36:40 +00:00
ljain112
cedf577b4c fix: do not include opening invoices in billed items to be received report
(cherry picked from commit c1ddf444c6)
2025-03-04 11:24:08 +00:00
ljain112
8e02dcfcaa fix: auto allocation for negative amount outstanding for Customers in Payment Entry
(cherry picked from commit 6275b44a0b)
2025-03-04 11:20:12 +00:00
Smit Vora
399ed331e3 Merge pull request #46247 from frappe/mergify/bp/version-14-hotfix/pr-46192
fix: Exclude Cancelled GL Entries (backport #46192)
2025-03-04 16:39:47 +05:30
ruthra kumar
59236bc5bf Merge pull request #46265 from frappe/mergify/bp/version-14-hotfix/pr-45751
fix: change voucher_type and voucher_name field type to data (backport #45751)
2025-03-04 16:12:16 +05:30
ruthra kumar
1089cdf213 chore: resolve conflicts 2025-03-04 15:46:05 +05:30
Sugesh393
df4f4d9a31 fix: change voucher_type and voucher_no field type to data
(cherry picked from commit f8ab021920)

# Conflicts:
#	erpnext/accounts/doctype/tax_withheld_vouchers/tax_withheld_vouchers.json
#	erpnext/accounts/doctype/tax_withheld_vouchers/tax_withheld_vouchers.py
2025-03-04 10:10:04 +00:00
mergify[bot]
f8bbcab3a5 fix(patch): Ensure SLE indexes (backport #46131) (#46134)
* fix(patch): Ensure SLE indexes (#46131)

Because of the way this change was pushed in parts, some sites don't see
this as "update" and don't have the new indexes.

(cherry picked from commit f62aa8fc57)

# Conflicts:
#	erpnext/patches.txt

* fix: resolved conflict

---------

Co-authored-by: Ankush Menat <ankush@frappe.io>
Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2025-03-03 21:36:57 +05:30
Ninad1306
369a692af9 fix: exclude cancelled gl entries
(cherry picked from commit 3251a331dd)
2025-03-03 11:31:08 +00:00
rohitwaghchaure
8cc92e9ca0 Merge pull request #46241 from frappe/mergify/bp/version-14-hotfix/pr-46239
fix: incorrectly billed amount in the purchase receipt (backport #46239)
2025-03-03 16:16:38 +05:30
rohitwaghchaure
eebf6cf877 chore: fix conflicts 2025-03-03 14:48:07 +05:30
Rohit Waghchaure
ca94ad3a24 fix: incorrectly billed amount in the purchase receipt
(cherry picked from commit a5271fdb2e)

# Conflicts:
#	erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
2025-03-03 09:15:48 +00:00
rohitwaghchaure
1a8bf0cf3d Merge pull request #46040 from frappe/mergify/bp/version-14-hotfix/pr-46037
fix: inventory dimension for maintenance visit (backport #46037)
2025-02-28 14:53:56 +05:30
Frappe PR Bot
19604e91c6 chore(release): Bumped to Version 14.83.1
## [14.83.1](https://github.com/frappe/erpnext/compare/v14.83.0...v14.83.1) (2025-02-26)

### Bug Fixes

* incorrect stock value difference for adjustment entry ([c734373](c734373c9f))
2025-02-26 12:43:06 +00:00
ruthra kumar
4f40939c15 Merge pull request #46133 from frappe/version-14-hotfix
chore: release v14
2025-02-26 18:11:42 +05:30
rohitwaghchaure
811953b9b0 chore: fix conflicts 2025-02-23 19:47:06 +05:30
rohitwaghchaure
6748780591 Merge pull request #46027 from frappe/mergify/bp/version-14-hotfix/pr-46021
fix: incorrect stock value difference for adjustment entry (backport #46021)
2025-02-20 13:06:07 +05:30
Rohit Waghchaure
69a8e0dfac fix: inventory dimension for maintence visit
(cherry picked from commit cd4ba69262)

# Conflicts:
#	erpnext/stock/doctype/inventory_dimension/inventory_dimension.py
2025-02-20 06:13:14 +00:00
Rohit Waghchaure
c734373c9f fix: incorrect stock value difference for adjustment entry
(cherry picked from commit df83e427a3)
2025-02-19 11:11:09 +00:00
192 changed files with 6188 additions and 3171 deletions

4
.github/release.yml vendored Normal file
View File

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

View File

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

View File

@@ -9,15 +9,16 @@ jobs:
name: linters
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Set up Python 3.10
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: '3.10'
cache: pip
- name: Install and Run Pre-commit
uses: pre-commit/action@v2.0.3
uses: pre-commit/action@v3.0.0
- name: Download Semgrep rules
run: git clone --depth 1 https://github.com/frappe/semgrep-rules.git frappe-semgrep-rules

View File

@@ -1,5 +1,5 @@
exclude: 'node_modules|.git'
default_stages: [commit]
default_stages: [pre-commit]
fail_fast: false

View File

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

View File

@@ -4,7 +4,7 @@
import frappe
from frappe import _, throw
from frappe.utils import cint, cstr
from frappe.utils import add_to_date, cint, cstr, pretty_date
from frappe.utils.nestedset import NestedSet, get_ancestors_of, get_descendants_of
import erpnext
@@ -400,6 +400,7 @@ def validate_account_number(name, account_number, company):
@frappe.whitelist()
def update_account_number(name, account_name, account_number=None, from_descendant=False):
_ensure_idle_system()
account = frappe.db.get_value("Account", name, "company", as_dict=True)
if not account:
return
@@ -420,7 +421,7 @@ def update_account_number(name, account_name, account_number=None, from_descenda
"name",
)
if old_name:
if old_name and not from_descendant:
# same account in parent company exists
allow_child_account_creation = _("Allow Account Creation Against Child Company")
@@ -461,6 +462,7 @@ def update_account_number(name, account_name, account_number=None, from_descenda
@frappe.whitelist()
def merge_account(old, new):
_ensure_idle_system()
# Validate properties before merging
new_account = frappe.get_cached_doc("Account", new)
old_account = frappe.get_cached_doc("Account", old)
@@ -514,3 +516,27 @@ def sync_update_account_number_in_child(
for d in frappe.db.get_values("Account", filters=filters, fieldname=["company", "name"], as_dict=True):
update_account_number(d["name"], account_name, account_number, from_descendant=True)
def _ensure_idle_system():
# Don't allow renaming if accounting entries are actively being updated, there are two main reasons:
# 1. Correctness: It's next to impossible to ensure that renamed account is not being used *right now*.
# 2. Performance: Renaming requires locking out many tables entirely and severely degrades performance.
if frappe.flags.in_test:
return
try:
# We also lock inserts to GL entry table with for_update here.
last_gl_update = frappe.db.get_value("GL Entry", {}, "modified", for_update=True, wait=False)
except frappe.QueryTimeoutError:
# wait=False fails immediately if there's an active transaction.
last_gl_update = add_to_date(None, seconds=-1)
if last_gl_update > add_to_date(None, minutes=-5):
frappe.throw(
_(
"Last GL Entry update was done {}. This operation is not allowed while system is actively being used. Please wait for 5 minutes before retrying."
).format(pretty_date(last_gl_update)),
title=_("System In Use"),
)

View File

@@ -98,7 +98,7 @@
"Office Maintenance Expenses": {},
"Office Rent": {},
"Postal Expenses": {},
"Print and Stationary": {},
"Print and Stationery": {},
"Rounded Off": {
"account_type": "Round Off"
},

View File

@@ -3,4 +3,14 @@
frappe.ui.form.on("Accounts Settings", {
refresh: function (frm) {},
drop_ar_procedures: function (frm) {
frm.call({
doc: frm.doc,
method: "drop_ar_sql_procedures",
callback: function (r) {
frappe.show_alert(__("Procedures dropped"), 5);
},
});
},
});

View File

@@ -73,9 +73,14 @@
"reports_tab",
"remarks_section",
"general_ledger_remarks_length",
"ignore_is_opening_check_for_reporting",
"column_break_lvjk",
"receivable_payable_remarks_length"
"receivable_payable_remarks_length",
"accounts_receivable_payable_tuning_section",
"receivable_payable_fetch_method",
"column_break_ntmi",
"drop_ar_procedures",
"legacy_section",
"ignore_is_opening_check_for_reporting"
],
"fields": [
{
@@ -479,6 +484,34 @@
"fieldname": "ignore_is_opening_check_for_reporting",
"fieldtype": "Check",
"label": "Ignore Is Opening check for reporting"
},
{
"default": "Buffered Cursor",
"fieldname": "receivable_payable_fetch_method",
"fieldtype": "Select",
"label": "Data Fetch Method",
"options": "Buffered Cursor\nUnBuffered Cursor\nRaw SQL"
},
{
"fieldname": "accounts_receivable_payable_tuning_section",
"fieldtype": "Section Break",
"label": "Accounts Receivable / Payable Tuning"
},
{
"fieldname": "legacy_section",
"fieldtype": "Section Break",
"label": "Legacy Fields"
},
{
"fieldname": "column_break_ntmi",
"fieldtype": "Column Break"
},
{
"depends_on": "eval:doc.receivable_payable_fetch_method == \"Raw SQL\"",
"description": "Drops existing SQL Procedures and Function setup by Accounts Receivable report",
"fieldname": "drop_ar_procedures",
"fieldtype": "Button",
"label": "Drop Procedures"
}
],
"icon": "icon-cog",
@@ -486,7 +519,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2025-01-23 13:15:44.077853",
"modified": "2025-05-05 12:29:38.302027",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Settings",
@@ -515,4 +548,4 @@
"sort_order": "ASC",
"states": [],
"track_changes": 1
}
}

View File

@@ -65,3 +65,11 @@ class AccountsSettings(Document):
def validate_pending_reposts(self):
if self.acc_frozen_upto:
check_pending_reposting(self.acc_frozen_upto)
@frappe.whitelist()
def drop_ar_sql_procedures(self):
from erpnext.accounts.report.accounts_receivable.accounts_receivable import InitSQLProceduresForAR
frappe.db.sql(f"drop function if exists {InitSQLProceduresForAR.genkey_function_name}")
frappe.db.sql(f"drop procedure if exists {InitSQLProceduresForAR.init_procedure_name}")
frappe.db.sql(f"drop procedure if exists {InitSQLProceduresForAR.allocate_procedure_name}")

View File

@@ -46,9 +46,6 @@ class BankClearance(Document):
as_dict=1,
)
if self.bank_account:
condition += "and bank_account = %(bank_account)s"
payment_entries = frappe.db.sql(
f"""
select
@@ -70,7 +67,6 @@ class BankClearance(Document):
"account": self.account,
"from": self.from_date,
"to": self.to_date,
"bank_account": self.bank_account,
},
as_dict=1,
)
@@ -93,7 +89,7 @@ class BankClearance(Document):
.where(loan_disbursement.docstatus == 1)
.where(loan_disbursement.disbursement_date >= self.from_date)
.where(loan_disbursement.disbursement_date <= self.to_date)
.where(loan_disbursement.disbursement_account.isin([self.bank_account, self.account]))
.where(loan_disbursement.disbursement_account == self.account)
.orderby(loan_disbursement.disbursement_date)
.orderby(loan_disbursement.name, order=frappe.qb.desc)
)
@@ -121,7 +117,7 @@ class BankClearance(Document):
.where(loan_repayment.docstatus == 1)
.where(loan_repayment.posting_date >= self.from_date)
.where(loan_repayment.posting_date <= self.to_date)
.where(loan_repayment.payment_account.isin([self.bank_account, self.account]))
.where(loan_repayment.payment_account == self.account)
)
if not self.include_reconciled_entries:

View File

@@ -9,13 +9,6 @@ cur_frm.add_fetch("bank", "swift_number", "swift_number");
frappe.ui.form.on("Bank Guarantee", {
setup: function (frm) {
frm.set_query("bank", function () {
return {
filters: {
company: frm.doc.company,
},
};
});
frm.set_query("bank_account", function () {
return {
filters: {

View File

@@ -6,7 +6,11 @@ import unittest
import frappe
from frappe.utils import now_datetime, nowdate
from erpnext.accounts.doctype.budget.budget import BudgetError, get_actual_expense
from erpnext.accounts.doctype.budget.budget import (
BudgetError,
get_accumulated_monthly_budget,
get_actual_expense,
)
from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
from erpnext.accounts.utils import get_fiscal_year
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
@@ -96,6 +100,10 @@ class TestBudget(unittest.TestCase):
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
frappe.db.set_value("Budget", budget.name, "fiscal_year", fiscal_year)
accumulated_limit = get_accumulated_monthly_budget(
budget.monthly_distribution, nowdate(), budget.fiscal_year, budget.accounts[0].budget_amount
)
mr = frappe.get_doc(
{
"doctype": "Material Request",
@@ -109,7 +117,7 @@ class TestBudget(unittest.TestCase):
"uom": "_Test UOM",
"warehouse": "_Test Warehouse - _TC",
"schedule_date": nowdate(),
"rate": 100000,
"rate": accumulated_limit + 1,
"expense_account": "_Test Account Cost for Goods Sold - _TC",
"cost_center": "_Test Cost Center - _TC",
}

View File

@@ -448,9 +448,8 @@ def unset_existing_data(company):
"Sales Taxes and Charges Template",
"Purchase Taxes and Charges Template",
]:
frappe.db.sql(
f'''delete from `tab{doctype}` where `company`="%s"''' % (company) # nosec
)
dt = frappe.qb.DocType(doctype)
frappe.qb.from_(dt).where(dt.company == company).delete().run()
def set_default_accounts(company):

View File

@@ -4,6 +4,8 @@
import unittest
import frappe
from frappe.query_builder.functions import Sum
from frappe.tests.utils import change_settings
from frappe.utils import add_days, today
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
@@ -190,6 +192,31 @@ class TestCostCenterAllocation(unittest.TestCase):
coa2.cancel()
jv.cancel()
@change_settings("System Settings", {"rounding_method": "Commercial Rounding"})
def test_debit_credit_on_cost_center_allocation_for_commercial_rounding(self):
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
cca = create_cost_center_allocation(
"_Test Company",
"Main Cost Center 1 - _TC",
{"Sub Cost Center 2 - _TC": 50, "Sub Cost Center 3 - _TC": 50},
)
si = create_sales_invoice(rate=145.65, cost_center="Main Cost Center 1 - _TC")
gl_entry = frappe.qb.DocType("GL Entry")
gl_entries = (
frappe.qb.from_(gl_entry)
.select(Sum(gl_entry.credit).as_("cr"), Sum(gl_entry.debit).as_("dr"))
.where(gl_entry.voucher_type == "Sales Invoice")
.where(gl_entry.voucher_no == si.name)
).run(as_dict=1)
self.assertEqual(gl_entries[0].cr, gl_entries[0].dr)
si.cancel()
cca.cancel()
def create_cost_center_allocation(
company,

View File

@@ -19,7 +19,7 @@ frappe.ui.form.on("Currency Exchange Settings", {
to: "{to_currency}",
};
add_param(frm, r.message, params, result);
} else if (frm.doc.service_provider == "frankfurter.app") {
} else if (["frankfurter.app", "frankfurter.dev"].includes(frm.doc.service_provider)) {
let result = ["rates", "{to_currency}"];
let params = {
base: "{from_currency}",

View File

@@ -78,7 +78,7 @@
"fieldname": "service_provider",
"fieldtype": "Select",
"label": "Service Provider",
"options": "frankfurter.app\nexchangerate.host\nCustom",
"options": "frankfurter.dev\nexchangerate.host\nCustom",
"reqd": 1
},
{
@@ -104,7 +104,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2024-03-18 08:32:26.895076",
"modified": "2025-11-25 13:03:41.896424",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Currency Exchange Settings",
@@ -141,8 +141,9 @@
"write": 1
}
],
"sort_field": "modified",
"row_format": "Dynamic",
"sort_field": "creation",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}
}

View File

@@ -35,7 +35,7 @@ class CurrencyExchangeSettings(Document):
self.append("req_params", {"key": "date", "value": "{transaction_date}"})
self.append("req_params", {"key": "from", "value": "{from_currency}"})
self.append("req_params", {"key": "to", "value": "{to_currency}"})
elif self.service_provider == "frankfurter.app":
elif self.service_provider in ("frankfurter.dev", "frankfurter.app"):
self.set("result_key", [])
self.set("req_params", [])
@@ -80,11 +80,13 @@ class CurrencyExchangeSettings(Document):
@frappe.whitelist()
def get_api_endpoint(service_provider: str | None = None, use_http: bool = False):
if service_provider and service_provider in ["exchangerate.host", "frankfurter.app"]:
if service_provider and service_provider in ["exchangerate.host", "frankfurter.dev", "frankfurter.app"]:
if service_provider == "exchangerate.host":
api = "api.exchangerate.host/convert"
elif service_provider == "frankfurter.app":
api = "api.frankfurter.app/{transaction_date}"
elif service_provider == "frankfurter.dev":
api = "api.frankfurter.dev/v1/{transaction_date}"
protocol = "https://"
if use_http:

View File

@@ -55,46 +55,46 @@ class Dunning(AccountsController):
"conversion_rate",
"cost_center",
]
inv = frappe.db.get_value("Sales Invoice", self.sales_invoice, invoice_fields, as_dict=1)
accounting_dimensions = get_accounting_dimensions()
invoice_fields.extend(accounting_dimensions)
inv = frappe.db.get_value("Sales Invoice", self.sales_invoice, invoice_fields, as_dict=1)
dunning_in_company_currency = flt(self.dunning_amount * inv.conversion_rate)
default_cost_center = frappe.get_cached_value("Company", self.company, "cost_center")
gl_entries.append(
self.get_gl_dict(
{
"account": inv.debit_to,
"party_type": "Customer",
"party": self.customer,
"due_date": self.due_date,
"against": self.income_account,
"debit": dunning_in_company_currency,
"debit_in_account_currency": self.dunning_amount,
"against_voucher": self.name,
"against_voucher_type": "Dunning",
"cost_center": inv.cost_center or default_cost_center,
"project": inv.project,
},
inv.party_account_currency,
item=inv,
)
)
gl_entries.append(
self.get_gl_dict(
{
"account": self.income_account,
"against": self.customer,
"credit": dunning_in_company_currency,
"cost_center": inv.cost_center or default_cost_center,
"credit_in_account_currency": self.dunning_amount,
"project": inv.project,
},
item=inv,
)
)
debit = {
"account": inv.debit_to,
"party_type": "Customer",
"party": self.customer,
"due_date": self.due_date,
"against": self.income_account,
"debit": dunning_in_company_currency,
"debit_in_account_currency": self.dunning_amount,
"against_voucher": self.name,
"against_voucher_type": "Dunning",
"cost_center": inv.cost_center or default_cost_center,
"project": inv.project,
}
credit = {
"account": self.income_account,
"against": self.customer,
"credit": dunning_in_company_currency,
"credit_in_account_currency": self.dunning_amount,
"cost_center": inv.cost_center or default_cost_center,
"project": inv.project,
}
for dimension in accounting_dimensions:
if val := inv.get(dimension):
debit[dimension] = credit[dimension] = val
gl_entries = [
self.get_gl_dict(debit, inv.party_account_currency, item=inv),
self.get_gl_dict(credit, item=inv),
]
make_gl_entries(
gl_entries, cancel=(self.docstatus == 2), update_outstanding="No", merge_entries=False
)

View File

@@ -7,7 +7,7 @@ from frappe import _
from frappe.model.document import Document
from frappe.model.meta import get_field_precision
from frappe.model.naming import set_name_from_naming_options
from frappe.utils import flt, fmt_money
from frappe.utils import flt, fmt_money, now
import erpnext
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
@@ -261,7 +261,7 @@ def validate_balance_type(account, adv_adj=False):
if balance_must_be:
balance = frappe.db.sql(
"""select sum(debit) - sum(credit)
from `tabGL Entry` where account = %s""",
from `tabGL Entry` where is_cancelled = 0 and account = %s""",
account,
)[0][0]
@@ -405,7 +405,7 @@ def rename_temporarily_named_docs(doctype):
set_name_from_naming_options(frappe.get_meta(doctype).autoname, doc)
newname = doc.name
frappe.db.sql(
f"UPDATE `tab{doctype}` SET name = %s, to_rename = 0 where name = %s",
(newname, oldname),
f"UPDATE `tab{doctype}` SET name = %s, to_rename = 0, modified = %s where name = %s",
(newname, now(), oldname),
auto_commit=True,
)

View File

@@ -197,7 +197,7 @@ frappe.ui.form.on("Invoice Discounting", {
from_date: frm.doc.posting_date,
to_date: moment(frm.doc.modified).format("YYYY-MM-DD"),
company: frm.doc.company,
group_by: "Group by Voucher (Consolidated)",
categorize_by: "Categorize by Voucher (Consolidated)",
show_cancelled_entries: frm.doc.docstatus === 2,
};
frappe.set_route("query-report", "General Ledger");

View File

@@ -34,7 +34,7 @@ frappe.ui.form.on("Journal Entry", {
to_date: moment(frm.doc.modified).format("YYYY-MM-DD"),
company: frm.doc.company,
finance_book: frm.doc.finance_book,
group_by: "",
categorize_by: "",
show_cancelled_entries: frm.doc.docstatus === 2,
};
frappe.set_route("query-report", "General Ledger");

View File

@@ -433,8 +433,22 @@ class JournalEntry(AccountsController):
if customers:
from erpnext.selling.doctype.customer.customer import check_credit_limit
customer_details = frappe._dict(
frappe.db.get_all(
"Customer Credit Limit",
filters={
"parent": ["in", customers],
"parenttype": ["=", "Customer"],
"company": ["=", self.company],
},
fields=["parent", "bypass_credit_limit_check"],
as_list=True,
)
)
for customer in customers:
check_credit_limit(customer, self.company)
ignore_outstanding_sales_order = bool(customer_details.get(customer))
check_credit_limit(customer, self.company, ignore_outstanding_sales_order)
def validate_cheque_info(self):
if self.voucher_type in ["Bank Entry"]:

View File

@@ -5,6 +5,7 @@
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.query_builder.functions import Sum
from frappe.utils import flt, today
@@ -18,22 +19,30 @@ def get_loyalty_details(
if not expiry_date:
expiry_date = today()
condition = ""
if company:
condition = " and company=%s " % frappe.db.escape(company)
if not include_expired_entry:
condition += " and expiry_date>='%s' " % expiry_date
LoyaltyPointEntry = frappe.qb.DocType("Loyalty Point Entry")
loyalty_point_details = frappe.db.sql(
f"""select sum(loyalty_points) as loyalty_points,
sum(purchase_amount) as total_spent from `tabLoyalty Point Entry`
where customer=%s and loyalty_program=%s and posting_date <= %s
{condition}
group by customer""",
(customer, loyalty_program, expiry_date),
as_dict=1,
query = (
frappe.qb.from_(LoyaltyPointEntry)
.select(
Sum(LoyaltyPointEntry.loyalty_points).as_("loyalty_points"),
Sum(LoyaltyPointEntry.purchase_amount).as_("total_spent"),
)
.where(
(LoyaltyPointEntry.customer == customer)
& (LoyaltyPointEntry.loyalty_program == loyalty_program)
& (LoyaltyPointEntry.posting_date <= expiry_date)
)
.groupby(LoyaltyPointEntry.customer)
)
if company:
query = query.where(LoyaltyPointEntry.company == company)
if not include_expired_entry:
query = query.where(LoyaltyPointEntry.expiry_date >= expiry_date)
loyalty_point_details = query.run(as_dict=True)
if loyalty_point_details:
return loyalty_point_details[0]
else:

View File

@@ -60,6 +60,6 @@ def create_party_link(primary_role, primary_party, secondary_party):
party_link.secondary_role = "Customer" if primary_role == "Supplier" else "Supplier"
party_link.secondary_party = secondary_party
party_link.save(ignore_permissions=True)
party_link.save()
return party_link

View File

@@ -319,7 +319,7 @@ frappe.ui.form.on('Payment Entry', {
"from_date": frm.doc.posting_date,
"to_date": moment(frm.doc.modified).format('YYYY-MM-DD'),
"company": frm.doc.company,
"group_by": "",
"categorize_by": "",
"show_cancelled_entries": frm.doc.docstatus === 2
};
frappe.set_route("query-report", "General Ledger");
@@ -330,7 +330,7 @@ frappe.ui.form.on('Payment Entry', {
payment_type: function(frm) {
set_default_party_type(frm);
if(frm.doc.payment_type == "Internal Transfer") {
$.each(["party", "party_balance", "paid_from", "paid_to",
$.each(["party", "party_type", "party_balance", "paid_from", "paid_to",
"references", "total_allocated_amount"], function(i, field) {
frm.set_value(field, null);
});

View File

@@ -350,15 +350,25 @@ class PaymentEntry(AccountsController):
self.set(self.party_account_field, party_account)
self.party_account = party_account
if self.paid_from and not (self.paid_from_account_currency or self.paid_from_account_balance):
if self.paid_from and (
not self.paid_from_account_currency
or not self.paid_from_account_balance
or not self.paid_from_account_type
):
acc = get_account_details(self.paid_from, self.posting_date, self.cost_center)
self.paid_from_account_currency = acc.account_currency
self.paid_from_account_balance = acc.account_balance
self.paid_from_account_type = acc.account_type
if self.paid_to and not (self.paid_to_account_currency or self.paid_to_account_balance):
if self.paid_to and (
not self.paid_to_account_currency
or not self.paid_to_account_balance
or not self.paid_to_account_type
):
acc = get_account_details(self.paid_to, self.posting_date, self.cost_center)
self.paid_to_account_currency = acc.account_currency
self.paid_to_account_balance = acc.account_balance
self.paid_to_account_type = acc.account_type
self.party_account_currency = (
self.paid_from_account_currency
@@ -1519,7 +1529,7 @@ class PaymentEntry(AccountsController):
allocated_positive_outstanding = paid_amount + allocated_negative_outstanding
elif self.party_type in ("Supplier", "Employee"):
elif self.party_type in ("Supplier", "Customer"):
if paid_amount > total_negative_outstanding:
if total_negative_outstanding == 0:
frappe.msgprint(
@@ -1580,7 +1590,7 @@ class PaymentEntry(AccountsController):
# Re allocate amount to those references which have PR set (Higher priority)
for ref in self.references:
if not ref.payment_request:
if not (ref.reference_doctype and ref.reference_name and ref.payment_request):
continue
# fetch outstanding_amount of `Reference` (Payment Term) and `Payment Request` to allocate new amount
@@ -1631,7 +1641,7 @@ class PaymentEntry(AccountsController):
)
# Re allocate amount to those references which have no PR (Lower priority)
for ref in self.references:
if ref.payment_request:
if ref.payment_request or not (ref.reference_doctype and ref.reference_name):
continue
key = (ref.reference_doctype, ref.reference_name, ref.get("payment_term"))

View File

@@ -10,14 +10,19 @@
"description",
"section_break_4",
"due_date",
"invoice_portion",
"mode_of_payment",
"column_break_5",
"invoice_portion",
"due_date_based_on",
"credit_days",
"credit_months",
"section_break_6",
"discount_type",
"discount_date",
"column_break_9",
"discount",
"discount_type",
"column_break_9",
"discount_validity_based_on",
"discount_validity",
"section_break_9",
"payment_amount",
"outstanding",
@@ -155,12 +160,50 @@
"fieldtype": "Currency",
"label": "Payment Amount (Company Currency)",
"options": "Company:company:default_currency"
},
{
"fieldname": "due_date_based_on",
"fieldtype": "Select",
"label": "Due Date Based On",
"options": "\nDay(s) after invoice date\nDay(s) after the end of the invoice month\nMonth(s) after the end of the invoice month",
"read_only": 1
},
{
"depends_on": "eval:in_list(['Day(s) after invoice date', 'Day(s) after the end of the invoice month'], doc.due_date_based_on)",
"fieldname": "credit_days",
"fieldtype": "Int",
"label": "Credit Days",
"non_negative": 1,
"read_only": 1
},
{
"depends_on": "eval:doc.due_date_based_on=='Month(s) after the end of the invoice month'",
"fieldname": "credit_months",
"fieldtype": "Int",
"label": "Credit Months",
"non_negative": 1,
"read_only": 1
},
{
"depends_on": "discount",
"fieldname": "discount_validity_based_on",
"fieldtype": "Select",
"label": "Discount Validity Based On",
"options": "\nDay(s) after invoice date\nDay(s) after the end of the invoice month\nMonth(s) after the end of the invoice month",
"read_only": 1
},
{
"depends_on": "discount_validity_based_on",
"fieldname": "discount_validity",
"fieldtype": "Int",
"label": "Discount Validity",
"read_only": 1
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2022-09-16 13:57:06.382859",
"modified": "2025-07-31 08:38:25.820701",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Schedule",
@@ -171,4 +214,4 @@
"sort_order": "DESC",
"states": [],
"track_changes": 1
}
}

View File

@@ -161,4 +161,4 @@
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}
}

View File

@@ -11,9 +11,9 @@ frappe.ui.form.on("Period Closing Voucher", {
return {
filters: [
["Account", "company", "=", frm.doc.company],
["Account", "is_group", "=", "0"],
["Account", "is_group", "=", 0],
["Account", "freeze_account", "=", "No"],
["Account", "root_type", "in", "Liability, Equity"],
["Account", "root_type", "in", ["Liability", "Equity"]],
],
};
});
@@ -29,7 +29,7 @@ frappe.ui.form.on("Period Closing Voucher", {
from_date: frm.doc.posting_date,
to_date: moment(frm.doc.modified).format("YYYY-MM-DD"),
company: frm.doc.company,
group_by: "",
categorize_by: "",
show_cancelled_entries: frm.doc.docstatus === 2,
};
frappe.set_route("query-report", "General Ledger");

View File

@@ -32,8 +32,13 @@ class PeriodClosingVoucher(AccountsController):
def on_cancel(self):
self.validate_future_closing_vouchers()
self.ignore_linked_doctypes = (
"GL Entry",
"Stock Ledger Entry",
"Payment Ledger Entry",
"Account Closing Balance",
)
self.db_set("gle_processing_status", "In Progress")
self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Payment Ledger Entry")
gle_count = frappe.db.count(
"GL Entry",
{"voucher_type": "Period Closing Voucher", "voucher_no": self.name, "is_cancelled": 0},
@@ -201,6 +206,9 @@ class PeriodClosingVoucher(AccountsController):
return gl_entry
def get_gle_for_closing_account(self, acc):
debit = abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) > 0 else 0
credit = abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) < 0 else 0
gl_entry = self.get_gl_dict(
{
"company": self.company,
@@ -209,16 +217,10 @@ class PeriodClosingVoucher(AccountsController):
"cost_center": acc.cost_center,
"finance_book": acc.finance_book,
"account_currency": acc.account_currency,
"debit_in_account_currency": abs(flt(acc.bal_in_account_currency))
if flt(acc.bal_in_account_currency) > 0
else 0,
"debit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) > 0 else 0,
"credit_in_account_currency": abs(flt(acc.bal_in_account_currency))
if flt(acc.bal_in_account_currency) < 0
else 0,
"credit": abs(flt(acc.bal_in_company_currency))
if flt(acc.bal_in_company_currency) < 0
else 0,
"debit_in_account_currency": debit,
"debit": debit,
"credit_in_account_currency": credit,
"credit": credit,
"is_period_closing_voucher_entry": 1,
},
item=acc,

View File

@@ -1623,6 +1623,5 @@
"states": [],
"timeline_field": "customer",
"title_field": "title",
"track_changes": 1,
"track_seen": 1
}
"track_changes": 1
}

View File

@@ -560,7 +560,13 @@ class POSInvoice(SalesInvoice):
"Account", self.debit_to, "account_currency", cache=True
)
if not self.due_date and self.customer:
self.due_date = get_due_date(self.posting_date, "Customer", self.customer, self.company)
self.due_date = get_due_date(
self.posting_date,
"Customer",
self.customer,
self.company,
template_name=self.payment_terms_template,
)
super(SalesInvoice, self).set_missing_values(for_validate)

View File

@@ -12,7 +12,7 @@
"posting_date",
"company",
"account",
"group_by",
"categorize_by",
"cost_center",
"territory",
"ignore_exchange_rate_revaluation_journals",
@@ -172,14 +172,6 @@
"fieldtype": "Date",
"label": "Start Date"
},
{
"default": "Group by Voucher (Consolidated)",
"depends_on": "eval:(doc.report == 'General Ledger');",
"fieldname": "group_by",
"fieldtype": "Select",
"label": "Group By",
"options": "\nGroup by Voucher\nGroup by Voucher (Consolidated)"
},
{
"depends_on": "eval: (doc.report == 'General Ledger');",
"fieldname": "currency",
@@ -395,10 +387,18 @@
"fieldname": "show_remarks",
"fieldtype": "Check",
"label": "Show Remarks"
},
{
"default": "Categorize by Voucher (Consolidated)",
"depends_on": "eval:(doc.report == 'General Ledger');",
"fieldname": "categorize_by",
"fieldtype": "Select",
"label": "Categorize By",
"options": "\nCategorize by Voucher\nCategorize by Voucher (Consolidated)"
}
],
"links": [],
"modified": "2024-10-18 17:51:39.108481",
"modified": "2025-04-30 14:43:23.643006",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Process Statement Of Accounts",
@@ -433,4 +433,4 @@
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}
}

View File

@@ -145,7 +145,7 @@ def get_gl_filters(doc, entry, tax_id, presentation_currency):
"party": [entry.customer],
"party_name": [entry.customer_name] if entry.customer_name else None,
"presentation_currency": presentation_currency,
"group_by": doc.group_by,
"categorize_by": doc.categorize_by,
"currency": doc.currency,
"project": [p.project_name for p in doc.project],
"show_opening_entries": 0,
@@ -449,7 +449,7 @@ def send_auto_email():
selected = frappe.get_list(
"Process Statement Of Accounts",
filters={"enable_auto_email": 1},
or_filters={"to_date": format_date(today()), "posting_date": format_date(today())},
or_filters={"to_date": today(), "posting_date": today()},
)
for entry in selected:
send_emails(entry.name, from_scheduler=True)

View File

@@ -29,7 +29,10 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
this.frm.set_query("expense_account", "items", function () {
return {
query: "erpnext.controllers.queries.get_expense_account",
filters: { company: doc.company },
filters: {
company: doc.company,
disabled: 0,
},
};
});
}

View File

@@ -173,7 +173,12 @@ class PurchaseInvoice(BuyingController):
)
if not self.due_date:
self.due_date = get_due_date(
self.posting_date, "Supplier", self.supplier, self.company, self.bill_date
self.posting_date,
"Supplier",
self.supplier,
self.company,
self.bill_date,
template_name=self.payment_terms_template,
)
tds_category = frappe.db.get_value("Supplier", self.supplier, "tax_withholding_category")
@@ -781,7 +786,7 @@ class PurchaseInvoice(BuyingController):
self.get_provisional_accounts()
for item in self.get("items"):
if flt(item.base_net_amount):
if flt(item.base_net_amount) or (self.get("update_stock") and item.valuation_rate):
account_currency = get_account_currency(item.expense_account)
if item.item_code:
frappe.get_cached_value("Item", item.item_code, "asset_category")
@@ -1016,6 +1021,9 @@ class PurchaseInvoice(BuyingController):
def get_provisional_accounts(self):
self.provisional_accounts = frappe._dict()
linked_purchase_receipts = set([d.purchase_receipt for d in self.items if d.purchase_receipt])
if not linked_purchase_receipts:
return
pr_items = frappe.get_all(
"Purchase Receipt Item",
filters={"parent": ("in", linked_purchase_receipts)},
@@ -1124,6 +1132,30 @@ class PurchaseInvoice(BuyingController):
warehouse_debit_amount = stock_amount
elif self.is_return and self.update_stock and (self.is_internal_supplier or not self.return_against):
net_rate = item.base_net_amount
stock_amount = net_rate + item.item_tax_amount + flt(item.landed_cost_voucher_amount)
if flt(stock_amount, net_amt_precision) != flt(warehouse_debit_amount, net_amt_precision):
cost_of_goods_sold_account = self.get_company_default("default_expense_account")
stock_adjustment_amt = stock_amount - warehouse_debit_amount
gl_entries.append(
self.get_gl_dict(
{
"account": cost_of_goods_sold_account,
"against": item.expense_account,
"debit": stock_adjustment_amt,
"debit_in_transaction_currency": stock_adjustment_amt / self.conversion_rate,
"remarks": self.get("remarks") or _("Stock Adjustment"),
"cost_center": item.cost_center,
"project": item.project or self.project,
},
account_currency,
item=item,
)
)
return warehouse_debit_amount
def make_tax_gl_entries(self, gl_entries):

View File

@@ -1816,19 +1816,16 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
rate = flt(sle.stock_value_difference) / flt(sle.actual_qty)
self.assertAlmostEqual(rate, 500)
@change_settings("Accounts Settings", {"automatically_fetch_payment_terms": 1})
def test_payment_allocation_for_payment_terms(self):
from erpnext.buying.doctype.purchase_order.test_purchase_order import (
create_pr_against_po,
create_purchase_order,
)
from erpnext.selling.doctype.sales_order.test_sales_order import (
automatically_fetch_payment_terms,
)
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import (
make_purchase_invoice as make_pi_from_pr,
)
automatically_fetch_payment_terms()
frappe.db.set_value(
"Payment Terms Template",
"_Test Payment Term Template",
@@ -1852,9 +1849,8 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
1,
)
pi = make_pi_from_pr(pr.name)
self.assertEqual(pi.payment_schedule[0].payment_amount, 2500)
self.assertEqual(pi.payment_schedule[0].payment_amount, 1000)
automatically_fetch_payment_terms(enable=0)
frappe.db.set_value(
"Payment Terms Template",
"_Test Payment Term Template",
@@ -1984,6 +1980,78 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
self.assertRaises(frappe.ValidationError, dr_note.save)
def test_apply_discount_on_grand_total(self):
"""
To test if after applying discount on grand total,
the grand total is calculated correctly without any rounding errors
"""
invoice = make_purchase_invoice(qty=2, rate=100, do_not_save=True, do_not_submit=True)
invoice.append(
"items",
{
"item_code": "_Test Item",
"qty": 1,
"rate": 21.39,
},
)
invoice.append(
"taxes",
{
"charge_type": "On Net Total",
"account_head": "_Test Account VAT - _TC",
"description": "VAT",
"rate": 15.5,
},
)
# the grand total here will be 255.71
invoice.disable_rounded_total = 1
# apply discount on grand total to adjust the grand total to 255
invoice.discount_amount = 0.71
invoice.save()
# check if grand total is 496 and not something like 254.99 due to rounding errors
self.assertEqual(invoice.grand_total, 255)
def test_apply_discount_on_grand_total_with_previous_row_total_tax(self):
"""
To test if after applying discount on grand total,
where the tax is calculated on previous row total, the grand total is calculated correctly
"""
invoice = make_purchase_invoice(qty=2, rate=100, do_not_save=True, do_not_submit=True)
invoice.extend(
"taxes",
[
{
"charge_type": "Actual",
"account_head": "_Test Account VAT - _TC",
"description": "VAT",
"tax_amount": 100,
},
{
"charge_type": "On Previous Row Amount",
"account_head": "_Test Account VAT - _TC",
"description": "VAT",
"row_id": 1,
"rate": 10,
},
{
"charge_type": "On Previous Row Total",
"account_head": "_Test Account VAT - _TC",
"description": "VAT",
"row_id": 1,
"rate": 10,
},
],
)
# the total here will be 340, so applying 40 discount
invoice.discount_amount = 40
invoice.save()
self.assertEqual(invoice.grand_total, 300)
def check_gl_entries(
doc,

View File

@@ -6,6 +6,8 @@ from frappe import _, qb
from frappe.model.document import Document
from frappe.utils.data import comma_and
from erpnext.stock import get_warehouse_account_map
class RepostAccountingLedger(Document):
def __init__(self, *args, **kwargs):
@@ -77,6 +79,9 @@ class RepostAccountingLedger(Document):
doc = frappe.get_doc(x.voucher_type, x.voucher_no)
if doc.doctype in ["Payment Entry", "Journal Entry"]:
gle_map = doc.build_gl_map()
elif doc.doctype == "Purchase Receipt":
warehouse_account_map = get_warehouse_account_map(doc.company)
gle_map = doc.get_gl_entries(warehouse_account_map)
else:
gle_map = doc.get_gl_entries()
@@ -146,7 +151,7 @@ def start_repost(account_repost_doc=str) -> None:
if doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
if not repost_doc.delete_cancelled_entries:
doc.docstatus = 2
doc.make_gl_entries_on_cancel()
doc.make_gl_entries_on_cancel(from_repost=True)
doc.docstatus = 1
if doc.doctype == "Sales Invoice":
@@ -155,6 +160,14 @@ def start_repost(account_repost_doc=str) -> None:
doc.force_set_against_expense_account()
doc.make_gl_entries()
elif doc.doctype == "Purchase Receipt":
if not repost_doc.delete_cancelled_entries:
doc.docstatus = 2
doc.make_gl_entries_on_cancel(from_repost=True)
doc.docstatus = 1
doc.make_gl_entries(from_repost=True)
elif doc.doctype in ["Payment Entry", "Journal Entry", "Expense Claim"]:
if not repost_doc.delete_cancelled_entries:
doc.make_gl_entries(1)

View File

@@ -12,6 +12,8 @@ from erpnext.accounts.doctype.payment_request.payment_request import make_paymen
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
from erpnext.accounts.utils import get_fiscal_year
from erpnext.stock.doctype.item.test_item import make_item
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import get_gl_entries, make_purchase_receipt
class TestRepostAccountingLedger(AccountsTestMixin, FrappeTestCase):
@@ -202,9 +204,81 @@ class TestRepostAccountingLedger(AccountsTestMixin, FrappeTestCase):
self.assertIsNotNone(frappe.db.exists("GL Entry", {"voucher_no": si.name, "is_cancelled": 1}))
self.assertIsNotNone(frappe.db.exists("GL Entry", {"voucher_no": pe.name, "is_cancelled": 1}))
def test_06_repost_purchase_receipt(self):
from erpnext.accounts.doctype.account.test_account import create_account
provisional_account = create_account(
account_name="Provision Account",
parent_account="Current Liabilities - _TC",
company=self.company,
)
another_provisional_account = create_account(
account_name="Another Provision Account",
parent_account="Current Liabilities - _TC",
company=self.company,
)
company = frappe.get_doc("Company", self.company)
company.enable_provisional_accounting_for_non_stock_items = 1
company.default_provisional_account = provisional_account
company.save()
test_cc = company.cost_center
default_expense_account = company.default_expense_account
item = make_item(properties={"is_stock_item": 0})
pr = make_purchase_receipt(company=self.company, item_code=item.name, rate=1000.0, qty=1.0)
pr_gl_entries = get_gl_entries(pr.doctype, pr.name, skip_cancelled=True)
expected_pr_gles = [
{"account": provisional_account, "debit": 0.0, "credit": 1000.0, "cost_center": test_cc},
{"account": default_expense_account, "debit": 1000.0, "credit": 0.0, "cost_center": test_cc},
]
self.assertEqual(expected_pr_gles, pr_gl_entries)
# change the provisional account
frappe.db.set_value(
"Purchase Receipt Item",
pr.items[0].name,
"provisional_expense_account",
another_provisional_account,
)
repost_doc = frappe.new_doc("Repost Accounting Ledger")
repost_doc.company = self.company
repost_doc.delete_cancelled_entries = True
repost_doc.append("vouchers", {"voucher_type": pr.doctype, "voucher_no": pr.name})
repost_doc.save().submit()
pr_gles_after_repost = get_gl_entries(pr.doctype, pr.name, skip_cancelled=True)
expected_pr_gles_after_repost = [
{"account": default_expense_account, "debit": 1000.0, "credit": 0.0, "cost_center": test_cc},
{"account": another_provisional_account, "debit": 0.0, "credit": 1000.0, "cost_center": test_cc},
]
self.assertEqual(len(pr_gles_after_repost), len(expected_pr_gles_after_repost))
self.assertEqual(expected_pr_gles_after_repost, pr_gles_after_repost)
# teardown
repost_doc.cancel()
repost_doc.delete()
pr.reload()
pr.cancel()
company.enable_provisional_accounting_for_non_stock_items = 0
company.default_provisional_account = None
company.save()
def update_repost_settings():
allowed_types = ["Sales Invoice", "Purchase Invoice", "Payment Entry", "Journal Entry"]
allowed_types = [
"Sales Invoice",
"Purchase Invoice",
"Payment Entry",
"Journal Entry",
"Purchase Receipt",
]
repost_settings = frappe.get_doc("Repost Accounting Ledger Settings")
for x in allowed_types:
repost_settings.append("allowed_types", {"document_type": x, "allowed": True})

View File

@@ -91,8 +91,8 @@ class SalesInvoice(SellingController):
self.indicator_title = _("Paid")
def validate(self):
super().validate()
self.validate_auto_set_posting_time()
super().validate()
if not (self.is_pos or self.is_debit_note):
self.so_dn_required()
@@ -498,7 +498,13 @@ class SalesInvoice(SellingController):
"Account", self.debit_to, "account_currency", cache=True
)
if not self.due_date and self.customer:
self.due_date = get_due_date(self.posting_date, "Customer", self.customer, self.company)
self.due_date = get_due_date(
self.posting_date,
"Customer",
self.customer,
self.company,
template_name=self.payment_terms_template,
)
super().set_missing_values(for_validate)
@@ -1019,7 +1025,6 @@ class SalesInvoice(SellingController):
self.make_exchange_gain_loss_journal()
elif self.docstatus == 2:
cancel_exchange_gain_loss_journal(frappe._dict(doctype=self.doctype, name=self.name))
make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
if update_outstanding == "No":

View File

@@ -416,9 +416,9 @@ class TestSalesInvoice(FrappeTestCase):
for i, k in enumerate(expected_values["keys"]):
self.assertEqual(d.get(k), expected_values[d.account_head][i])
self.assertEqual(si.base_grand_total, 1500.01)
self.assertEqual(si.grand_total, 1500.01)
self.assertEqual(si.rounding_adjustment, -0.01)
self.assertEqual(si.base_grand_total, 1500)
self.assertEqual(si.grand_total, 1500)
self.assertEqual(si.rounding_adjustment, 0)
def test_discount_amount_gl_entry(self):
frappe.db.set_value("Company", "_Test Company", "round_off_account", "Round Off - _TC")
@@ -1773,17 +1773,6 @@ class TestSalesInvoice(FrappeTestCase):
self.assertTrue(gle)
def test_invoice_exchange_rate(self):
si = create_sales_invoice(
customer="_Test Customer USD",
debit_to="_Test Receivable USD - _TC",
currency="USD",
conversion_rate=1,
do_not_save=1,
)
self.assertRaises(frappe.ValidationError, si.save)
def test_invalid_currency(self):
# Customer currency = USD
@@ -2839,6 +2828,60 @@ class TestSalesInvoice(FrappeTestCase):
self.assertEqual(sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 10 - _TC")
self.assertEqual(sales_invoice.items[0].item_tax_rate, item_tax_map)
def test_item_tax_template_change_with_grand_total_discount(self):
"""
Test that when item tax template changes due to discount on Grand Total,
the tax calculations are consistent.
"""
item = create_item("Test Item With Multiple Tax Templates")
item.set("taxes", [])
item.append(
"taxes",
{
"item_tax_template": "_Test Account Excise Duty @ 10 - _TC",
"minimum_net_rate": 0,
"maximum_net_rate": 500,
},
)
item.append(
"taxes",
{
"item_tax_template": "_Test Account Excise Duty @ 12 - _TC",
"minimum_net_rate": 501,
"maximum_net_rate": 1000,
},
)
item.save()
si = create_sales_invoice(item=item.name, rate=700, do_not_save=True)
si.append(
"taxes",
{
"charge_type": "On Net Total",
"account_head": "_Test Account Excise Duty - _TC",
"cost_center": "_Test Cost Center - _TC",
"description": "Excise Duty",
"rate": 0,
},
)
si.insert()
self.assertEqual(si.items[0].item_tax_template, "_Test Account Excise Duty @ 12 - _TC")
si.apply_discount_on = "Grand Total"
si.discount_amount = 300
si.save()
# Verify template changed to 10%
self.assertEqual(si.items[0].item_tax_template, "_Test Account Excise Duty @ 10 - _TC")
self.assertEqual(si.taxes[0].tax_amount, 70) # 10% of 700
self.assertEqual(si.grand_total, 470) # 700 + 70 - 300
si.submit()
@change_settings("Selling Settings", {"enable_discount_accounting": 1})
def test_sales_invoice_with_discount_accounting_enabled(self):
discount_account = create_account(
@@ -3298,6 +3341,7 @@ class TestSalesInvoice(FrappeTestCase):
si.posting_date = getdate()
si.submit()
@change_settings("Accounts Settings", {"over_billing_allowance": 0})
def test_over_billing_case_against_delivery_note(self):
"""
Test a case where duplicating the item with qty = 1 in the invoice
@@ -3305,24 +3349,23 @@ class TestSalesInvoice(FrappeTestCase):
"""
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
over_billing_allowance = frappe.db.get_single_value("Accounts Settings", "over_billing_allowance")
frappe.db.set_value("Accounts Settings", None, "over_billing_allowance", 0)
dn = create_delivery_note()
dn.submit()
si = make_sales_invoice(dn.name)
# make a copy of first item and add it to invoice
item_copy = frappe.copy_doc(si.items[0])
si.save()
si.items = [] # Clear existing items
si.append("items", item_copy)
si.save()
si.append("items", item_copy)
with self.assertRaises(frappe.ValidationError) as err:
si.submit()
si.save()
self.assertTrue("cannot overbill" in str(err.exception).lower())
frappe.db.set_value("Accounts Settings", None, "over_billing_allowance", over_billing_allowance)
dn.cancel()
@change_settings(
"Accounts Settings",
@@ -3871,6 +3914,89 @@ class TestSalesInvoice(FrappeTestCase):
doc = frappe.get_doc("Project", project.name)
self.assertEqual(doc.total_billed_amount, si.grand_total)
def test_create_return_invoice_for_self_update(self):
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.controllers.sales_and_purchase_return import make_return_doc
invoice = create_sales_invoice()
payment_entry = get_payment_entry(dt=invoice.doctype, dn=invoice.name)
payment_entry.reference_no = "test001"
payment_entry.reference_date = getdate()
payment_entry.save()
payment_entry.submit()
r_invoice = make_return_doc(invoice.doctype, invoice.name)
r_invoice.update_outstanding_for_self = 0
r_invoice.save()
self.assertEqual(r_invoice.update_outstanding_for_self, 1)
r_invoice.submit()
self.assertNotEqual(r_invoice.outstanding_amount, 0)
invoice.reload()
self.assertEqual(invoice.outstanding_amount, 0)
def test_system_generated_exchange_gain_or_loss_je_after_repost(self):
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
from erpnext.accounts.doctype.repost_accounting_ledger.test_repost_accounting_ledger import (
update_repost_settings,
)
update_repost_settings()
si = create_sales_invoice(
customer="_Test Customer USD",
debit_to="_Test Receivable USD - _TC",
currency="USD",
conversion_rate=80,
)
pe = get_payment_entry("Sales Invoice", si.name)
pe.reference_no = "10"
pe.reference_date = nowdate()
pe.paid_from_account_currency = si.currency
pe.paid_to_account_currency = "INR"
pe.source_exchange_rate = 85
pe.target_exchange_rate = 1
pe.paid_amount = si.outstanding_amount
pe.received_amount = 8500
pe.insert()
pe.submit()
ral = frappe.new_doc("Repost Accounting Ledger")
ral.company = si.company
ral.append("vouchers", {"voucher_type": si.doctype, "voucher_no": si.name})
ral.save()
ral.submit()
je = frappe.qb.DocType("Journal Entry")
jea = frappe.qb.DocType("Journal Entry Account")
q = (
(
frappe.qb.from_(je)
.join(jea)
.on(je.name == jea.parent)
.select(je.docstatus)
.where(
(je.voucher_type == "Exchange Gain Or Loss")
& (jea.reference_name == si.name)
& (jea.reference_type == "Sales Invoice")
& (je.is_system_generated == 1)
)
)
.limit(1)
.run()
)
self.assertEqual(q[0][0], 1)
def check_gl_entries(doc, voucher_no, expected_gle, posting_date):
gl_entries = frappe.db.sql(

View File

@@ -80,7 +80,7 @@
"collapsible": 0,
"columns": 0,
"fieldname": "rate",
"fieldtype": "Int",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@@ -102,7 +102,7 @@
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@@ -199,7 +199,7 @@
"collapsible": 0,
"columns": 0,
"fieldname": "amount",
"fieldtype": "Int",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@@ -221,7 +221,7 @@
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@@ -324,7 +324,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2018-01-10 18:32:36.201124",
"modified": "2025-12-10 08:06:40.611761",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Share Balance",
@@ -339,4 +339,4 @@
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
}
}

View File

@@ -13,17 +13,15 @@
"fields": [
{
"fieldname": "voucher_type",
"fieldtype": "Link",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Voucher Type",
"options": "DocType"
"label": "Voucher Type"
},
{
"fieldname": "voucher_name",
"fieldtype": "Dynamic Link",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Voucher Name",
"options": "voucher_type"
"label": "Voucher Name"
},
{
"fieldname": "taxable_amount",
@@ -36,7 +34,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2023-01-13 13:40:41.479208",
"modified": "2025-02-05 16:39:14.863698",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Tax Withheld Vouchers",

View File

@@ -75,7 +75,7 @@
},
{
"default": "0",
"description": "Even invoices with apply tax withholding unchecked will be considered for checking cumulative threshold breach",
"description": "Only payment entries with apply tax withholding unchecked will be considered for checking cumulative threshold breach",
"fieldname": "consider_party_ledger_amount",
"fieldtype": "Check",
"label": "Consider Entire Party Ledger Amount",
@@ -102,10 +102,11 @@
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2021-07-27 21:47:34.396071",
"modified": "2025-07-30 07:13:51.785735",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Tax Withholding Category",
"naming_rule": "Set by user",
"owner": "Administrator",
"permissions": [
{
@@ -148,4 +149,4 @@
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}
}

View File

@@ -622,6 +622,7 @@ def get_tcs_amount(parties, inv, tax_details, vouchers, adv_vouchers):
conditions.append(ple.party.isin(parties))
conditions.append(ple.voucher_no == ple.against_voucher_no)
conditions.append(ple.company == inv.company)
conditions.append(ple.posting_date[tax_details.from_date : tax_details.to_date])
advance_amt = (
qb.from_(ple).select(Abs(Sum(ple.amount))).where(Criterion.all(conditions)).run()[0][0] or 0.0

View File

@@ -243,17 +243,18 @@ class TestTaxWithholdingCategory(FrappeTestCase):
frappe.db.set_value(
"Customer", "Test TCS Customer", "tax_withholding_category", "Cumulative Threshold TCS"
)
fiscal_year = get_fiscal_year(today(), company="_Test Company")
vouchers = []
# create advance payment
pe = create_payment_entry(
pe1 = create_payment_entry(
payment_type="Receive", party_type="Customer", party="Test TCS Customer", paid_amount=20000
)
pe.paid_from = "Debtors - _TC"
pe.paid_to = "Cash - _TC"
pe.submit()
vouchers.append(pe)
pe1.paid_from = "Debtors - _TC"
pe1.paid_to = "Cash - _TC"
pe1.submit()
vouchers.append(pe1)
# create invoice
si1 = create_sales_invoice(customer="Test TCS Customer", rate=5000)
@@ -275,6 +276,17 @@ class TestTaxWithholdingCategory(FrappeTestCase):
# make another invoice
# sum of unallocated amount from payment entry and this sales invoice will breach cumulative threashold
# TDS should be calculated
# this payment should not be considered for TCS calculation as it is outside of fiscal year
pe2 = create_payment_entry(
payment_type="Receive", party_type="Customer", party="Test TCS Customer", paid_amount=10000
)
pe2.paid_from = "Debtors - _TC"
pe2.paid_to = "Cash - _TC"
pe2.posting_date = add_days(fiscal_year[1], -10)
pe2.submit()
vouchers.append(pe2)
si2 = create_sales_invoice(customer="Test TCS Customer", rate=15000)
si2.submit()
vouchers.append(si2)

View File

@@ -179,6 +179,15 @@ def process_gl_map(gl_map, merge_entries=True, precision=None):
def distribute_gl_based_on_cost_center_allocation(gl_map, precision=None):
round_off_account, default_currency = frappe.get_cached_value(
"Company", gl_map[0].company, ["round_off_account", "default_currency"]
)
if not precision:
precision = get_field_precision(
frappe.get_meta("GL Entry").get_field("debit"),
currency=default_currency,
)
new_gl_map = []
for d in gl_map:
cost_center = d.get("cost_center")
@@ -192,6 +201,11 @@ def distribute_gl_based_on_cost_center_allocation(gl_map, precision=None):
new_gl_map.append(d)
continue
if d.account == round_off_account:
d.cost_center = cost_center_allocation[0][0]
new_gl_map.append(d)
continue
for sub_cost_center, percentage in cost_center_allocation:
gle = copy.deepcopy(d)
gle.cost_center = sub_cost_center

View File

@@ -552,12 +552,13 @@ def validate_party_accounts(doc):
@frappe.whitelist()
def get_due_date(posting_date, party_type, party, company=None, bill_date=None):
def get_due_date(posting_date, party_type, party, company=None, bill_date=None, template_name=None):
"""Get due date from `Payment Terms Template`"""
due_date = None
if (bill_date or posting_date) and party:
due_date = bill_date or posting_date
template_name = get_payment_terms_template(party, party_type, company)
if not template_name:
template_name = get_payment_terms_template(party, party_type, company)
if template_name:
due_date = get_due_date_from_template(template_name, posting_date, bill_date).strftime("%Y-%m-%d")
@@ -597,35 +598,34 @@ def get_due_date_from_template(template_name, posting_date, bill_date):
def validate_due_date(
posting_date, due_date, party_type, party, company=None, bill_date=None, template_name=None
posting_date, due_date, party_type, party, company=None, bill_date=None, template_name=None, doctype=None
):
if getdate(due_date) < getdate(posting_date):
frappe.throw(_("Due Date cannot be before Posting / Supplier Invoice Date"))
else:
if not template_name:
return
validate_due_date_with_template(posting_date, due_date, bill_date, template_name, doctype)
default_due_date = get_due_date_from_template(template_name, posting_date, bill_date).strftime(
"%Y-%m-%d"
)
if not default_due_date:
return
def validate_due_date_with_template(posting_date, due_date, bill_date, template_name, doctype=None):
if not template_name:
return
if default_due_date != posting_date and getdate(due_date) > getdate(default_due_date):
is_credit_controller = (
frappe.db.get_single_value("Accounts Settings", "credit_controller") in frappe.get_roles()
default_due_date = format(get_due_date_from_template(template_name, posting_date, bill_date))
if not default_due_date:
return
if default_due_date != posting_date and getdate(due_date) > getdate(default_due_date):
if frappe.db.get_single_value("Accounts Settings", "credit_controller") in frappe.get_roles():
party_type = "supplier" if doctype == "Purchase Invoice" else "customer"
msgprint(
_("Note: Due Date exceeds allowed {0} credit days by {1} day(s)").format(
party_type, date_diff(due_date, default_due_date)
)
)
if is_credit_controller:
msgprint(
_("Note: Due / Reference Date exceeds allowed customer credit days by {0} day(s)").format(
date_diff(due_date, default_due_date)
)
)
else:
frappe.throw(
_("Due / Reference Date cannot be after {0}").format(formatdate(default_due_date))
)
else:
frappe.throw(_("Due Date cannot be after {0}").format(formatdate(default_due_date)))
@frappe.whitelist()
@@ -903,12 +903,16 @@ def get_party_shipping_address(doctype: str, name: str) -> str | None:
["is_shipping_address", "=", 1],
["address_type", "=", "Shipping"],
],
pluck="name",
limit=1,
fields=["name", "is_shipping_address"],
order_by="is_shipping_address DESC",
)
return shipping_addresses[0] if shipping_addresses else None
if shipping_addresses and shipping_addresses[0].is_shipping_address == 1:
return shipping_addresses[0].name
if len(shipping_addresses) == 1:
return shipping_addresses[0].name
else:
return None
def get_partywise_advanced_payment_amount(

View File

@@ -282,4 +282,4 @@
{% } %}
</tbody>
</table>
<p class="text-right text-muted">{{ __("Printed On ") }}{%= frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string()) %}</p>
<p class="text-right text-muted">{%= __("Printed on {0}", [frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string())]) %}</p>

View File

@@ -6,6 +6,7 @@ from collections import OrderedDict
import frappe
from frappe import _, qb, query_builder, scrub
from frappe.database.schema import get_definition
from frappe.query_builder import Criterion
from frappe.query_builder.functions import Date, Substring, Sum
from frappe.utils import cint, cstr, flt, getdate, nowdate
@@ -14,7 +15,10 @@ from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_accounting_dimensions,
get_dimension_with_children,
)
from erpnext.accounts.utils import get_currency_precision
from erpnext.accounts.utils import (
build_qb_match_conditions,
get_currency_precision,
)
# This report gives a summary of all Outstanding Invoices considering the following
@@ -49,6 +53,10 @@ class ReceivablePayableReport:
self.age_as_on = (
getdate(nowdate()) if self.filters.report_date > getdate(nowdate()) else self.filters.report_date
)
self.ple_fetch_method = (
frappe.db.get_single_value("Accounts Settings", "receivable_payable_fetch_method")
or "Buffered Cursor"
) # Fail Safe
def run(self, args):
self.filters.update(args)
@@ -85,13 +93,7 @@ class ReceivablePayableReport:
self.skip_total_row = 1
def get_data(self):
self.get_ple_entries()
self.get_sales_invoices_or_customers_based_on_sales_person()
self.voucher_balance = OrderedDict()
self.init_voucher_balance() # invoiced, paid, credit_note, outstanding
# Build delivery note map against all sales invoices
self.build_delivery_note_map()
# Get invoice details like bill_no, due_date etc for all invoices
self.get_invoice_details()
@@ -105,12 +107,45 @@ class ReceivablePayableReport:
# Get Exchange Rate Revaluations
self.get_exchange_rate_revaluations()
self.prepare_ple_query()
self.data = []
self.voucher_balance = OrderedDict()
if self.ple_fetch_method == "Buffered Cursor":
self.fetch_ple_in_buffered_cursor()
elif self.ple_fetch_method == "UnBuffered Cursor":
self.fetch_ple_in_unbuffered_cursor()
elif self.ple_fetch_method == "Raw SQL":
self.fetch_ple_in_sql_procedures()
# Build delivery note map against all sales invoices
self.build_delivery_note_map()
self.build_data()
def fetch_ple_in_buffered_cursor(self):
self.ple_entries = self.ple_query.run(as_dict=True)
for ple in self.ple_entries:
self.init_voucher_balance(ple) # invoiced, paid, credit_note, outstanding
# This is unavoidable. Initialization and allocation cannot happen in same loop
for ple in self.ple_entries:
self.update_voucher_balance(ple)
self.build_data()
delattr(self, "ple_entries")
def fetch_ple_in_unbuffered_cursor(self):
self.ple_entries = []
with frappe.db.unbuffered_cursor():
for ple in self.ple_query.run(as_dict=True, as_iterator=True):
self.init_voucher_balance(ple) # invoiced, paid, credit_note, outstanding
self.ple_entries.append(ple)
# This is unavoidable. Initialization and allocation cannot happen in same loop
for ple in self.ple_entries:
self.update_voucher_balance(ple)
delattr(self, "ple_entries")
def build_voucher_dict(self, ple):
return frappe._dict(
@@ -131,26 +166,22 @@ class ReceivablePayableReport:
outstanding_in_account_currency=0.0,
)
def init_voucher_balance(self):
# build all keys, since we want to exclude vouchers beyond the report date
for ple in self.ple_entries:
# get the balance object for voucher_type
def init_voucher_balance(self, ple):
if self.filters.get("ignore_accounts"):
key = (ple.voucher_type, ple.voucher_no, ple.party)
else:
key = (ple.account, ple.voucher_type, ple.voucher_no, ple.party)
if self.filters.get("ignore_accounts"):
key = (ple.voucher_type, ple.voucher_no, ple.party)
else:
key = (ple.account, ple.voucher_type, ple.voucher_no, ple.party)
if key not in self.voucher_balance:
self.voucher_balance[key] = self.build_voucher_dict(ple)
if key not in self.voucher_balance:
self.voucher_balance[key] = self.build_voucher_dict(ple)
if ple.voucher_type == ple.against_voucher_type and ple.voucher_no == ple.against_voucher_no:
self.voucher_balance[key].cost_center = ple.cost_center
if ple.voucher_type == ple.against_voucher_type and ple.voucher_no == ple.against_voucher_no:
self.voucher_balance[key].cost_center = ple.cost_center
self.get_invoices(ple)
self.get_invoices(ple)
if self.filters.get("group_by_party"):
self.init_subtotal_row(ple.party)
if self.filters.get("group_by_party"):
self.init_subtotal_row(ple.party)
if self.filters.get("group_by_party") and not self.filters.get("in_party_currency"):
self.init_subtotal_row("Total")
@@ -272,6 +303,79 @@ class ReceivablePayableReport:
row.paid -= amount
row.paid_in_account_currency -= amount_in_account_currency
def fetch_ple_in_sql_procedures(self):
self.proc = InitSQLProceduresForAR()
build_balance = f"""
begin not atomic
declare done boolean default false;
declare rec1 row type of `{self.proc._row_def_table_name}`;
declare ple cursor for {self.ple_query.get_sql()};
declare continue handler for not found set done = true;
open ple;
fetch ple into rec1;
while not done do
call {self.proc.init_procedure_name}(rec1);
fetch ple into rec1;
end while;
close ple;
set done = false;
open ple;
fetch ple into rec1;
while not done do
call {self.proc.allocate_procedure_name}(rec1);
fetch ple into rec1;
end while;
close ple;
end;
"""
frappe.db.sql(build_balance)
balances = frappe.db.sql(
f"""select
name,
voucher_type,
voucher_no,
party,
party_account `account`,
posting_date,
account_currency,
cost_center,
sum(invoiced) `invoiced`,
sum(paid) `paid`,
sum(credit_note) `credit_note`,
sum(invoiced) - sum(paid) - sum(credit_note) `outstanding`,
sum(invoiced_in_account_currency) `invoiced_in_account_currency`,
sum(paid_in_account_currency) `paid_in_account_currency`,
sum(credit_note_in_account_currency) `credit_note_in_account_currency`,
sum(invoiced_in_account_currency) - sum(paid_in_account_currency) - sum(credit_note_in_account_currency) `outstanding_in_account_currency`
from `{self.proc._voucher_balance_name}` group by name order by posting_date;""",
as_dict=True,
)
for x in balances:
if self.filters.get("ignore_accounts"):
key = (x.voucher_type, x.voucher_no, x.party)
else:
key = (x.account, x.voucher_type, x.voucher_no, x.party)
_d = self.build_voucher_dict(x)
for field in [
"invoiced",
"paid",
"credit_note",
"outstanding",
"invoiced_in_account_currency",
"paid_in_account_currency",
"credit_note_in_account_currency",
"outstanding_in_account_currency",
"cost_center",
]:
_d[field] = x.get(field)
self.voucher_balance[key] = _d
def update_sub_total_row(self, row, party):
total_row = self.total_row_map.get(party)
@@ -498,7 +602,7 @@ class ReceivablePayableReport:
ps.description, ps.paid_amount, ps.discounted_amount
from `tab{row.voucher_type}` si, `tabPayment Schedule` ps
where
si.name = ps.parent and
si.name = ps.parent and ps.parenttype = '{row.voucher_type}' and
si.name = %s and
si.is_return = 0
order by ps.paid_amount desc, due_date
@@ -764,7 +868,7 @@ class ReceivablePayableReport:
index = 4
row["range" + str(index + 1)] = row.outstanding
def get_ple_entries(self):
def prepare_ple_query(self):
# get all the GL entries filtered by the given filters
self.prepare_conditions()
@@ -812,12 +916,15 @@ class ReceivablePayableReport:
else:
query = query.select(ple.remarks)
if match_conditions := build_qb_match_conditions("Payment Ledger Entry"):
query = query.where(Criterion.all(match_conditions))
if self.filters.get("group_by_party"):
query = query.orderby(self.ple.party, self.ple.posting_date)
else:
query = query.orderby(self.ple.posting_date, self.ple.party)
self.ple_entries = query.run(as_dict=True)
self.ple_query = query
def get_sales_invoices_or_customers_based_on_sales_person(self):
if self.filters.get("sales_person"):
@@ -1204,3 +1311,134 @@ def get_customer_group_with_children(customer_groups):
frappe.throw(_("Customer Group: {0} does not exist").format(d))
return list(set(all_customer_groups))
class InitSQLProceduresForAR:
"""
Initialize SQL Procedures, Functions and Temporary tables to build Receivable / Payable report
"""
_varchar_type = get_definition("Data")
_currency_type = get_definition("Currency")
# Temporary Tables
_voucher_balance_name = "_ar_voucher_balance"
_voucher_balance_definition = f"""
create temporary table `{_voucher_balance_name}`(
name {_varchar_type},
voucher_type {_varchar_type},
voucher_no {_varchar_type},
party {_varchar_type},
party_account {_varchar_type},
posting_date date,
account_currency {_varchar_type},
cost_center {_varchar_type},
invoiced {_currency_type},
paid {_currency_type},
credit_note {_currency_type},
invoiced_in_account_currency {_currency_type},
paid_in_account_currency {_currency_type},
credit_note_in_account_currency {_currency_type}) engine=memory;
"""
_row_def_table_name = "_ar_ple_row"
_row_def_table_definition = f"""
create temporary table `{_row_def_table_name}`(
name {_varchar_type},
account {_varchar_type},
voucher_type {_varchar_type},
voucher_no {_varchar_type},
against_voucher_type {_varchar_type},
against_voucher_no {_varchar_type},
party_type {_varchar_type},
cost_center {_varchar_type},
party {_varchar_type},
posting_date date,
due_date date,
account_currency {_varchar_type},
amount {_currency_type},
amount_in_account_currency {_currency_type}) engine=memory;
"""
# Function
genkey_function_name = "ar_genkey"
genkey_function_sql = f"""
create function `{genkey_function_name}`(rec row type of `{_row_def_table_name}`, allocate bool) returns char(40)
begin
if allocate then
return sha1(concat_ws(',', rec.account, rec.against_voucher_type, rec.against_voucher_no, rec.party));
else
return sha1(concat_ws(',', rec.account, rec.voucher_type, rec.voucher_no, rec.party));
end if;
end
"""
# Procedures
init_procedure_name = "ar_init_tmp_table"
init_procedure_sql = f"""
create procedure ar_init_tmp_table(in ple row type of `{_row_def_table_name}`)
begin
if not exists (select name from `{_voucher_balance_name}` where name = `{genkey_function_name}`(ple, false))
then
insert into `{_voucher_balance_name}` values (`{genkey_function_name}`(ple, false), ple.voucher_type, ple.voucher_no, ple.party, ple.account, ple.posting_date, ple.account_currency, ple.cost_center, 0, 0, 0, 0, 0, 0);
end if;
end;
"""
allocate_procedure_name = "ar_allocate_to_tmp_table"
allocate_procedure_sql = f"""
create procedure ar_allocate_to_tmp_table(in ple row type of `{_row_def_table_name}`)
begin
declare invoiced {_currency_type} default 0;
declare invoiced_in_account_currency {_currency_type} default 0;
declare paid {_currency_type} default 0;
declare paid_in_account_currency {_currency_type} default 0;
declare credit_note {_currency_type} default 0;
declare credit_note_in_account_currency {_currency_type} default 0;
if ple.amount > 0 then
if (ple.voucher_type in ("Journal Entry", "Payment Entry") and (ple.voucher_no != ple.against_voucher_no)) then
set paid = -1 * ple.amount;
set paid_in_account_currency = -1 * ple.amount_in_account_currency;
else
set invoiced = ple.amount;
set invoiced_in_account_currency = ple.amount_in_account_currency;
end if;
else
if ple.voucher_type in ("Sales Invoice", "Purchase Invoice") then
if (ple.voucher_no = ple.against_voucher_no) then
set paid = -1 * ple.amount;
set paid_in_account_currency = -1 * ple.amount_in_account_currency;
else
set credit_note = -1 * ple.amount;
set credit_note_in_account_currency = -1 * ple.amount_in_account_currency;
end if;
else
set paid = -1 * ple.amount;
set paid_in_account_currency = -1 * ple.amount_in_account_currency;
end if;
end if;
insert into `{_voucher_balance_name}` values (`{genkey_function_name}`(ple, true), ple.against_voucher_type, ple.against_voucher_no, ple.party, ple.account, ple.posting_date, ple.account_currency,'', invoiced, paid, 0, invoiced_in_account_currency, paid_in_account_currency, 0);
end;
"""
def __init__(self):
existing_procedures = frappe.db.get_routines()
if self.genkey_function_name not in existing_procedures:
frappe.db.sql(self.genkey_function_sql)
if self.init_procedure_name not in existing_procedures:
frappe.db.sql(self.init_procedure_sql)
if self.allocate_procedure_name not in existing_procedures:
frappe.db.sql(self.allocate_procedure_sql)
frappe.db.sql(f"drop table if exists `{self._voucher_balance_name}`")
frappe.db.sql(self._voucher_balance_definition)
frappe.db.sql(f"drop table if exists `{self._row_def_table_name}`")
frappe.db.sql(self._row_def_table_definition)

View File

@@ -21,7 +21,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
def tearDown(self):
frappe.db.rollback()
def create_sales_invoice(self, no_payment_schedule=False, do_not_submit=False):
def create_sales_invoice(self, no_payment_schedule=False, do_not_submit=False, **args):
frappe.set_user("Administrator")
si = create_sales_invoice(
item=self.item,
@@ -34,6 +34,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
rate=100,
price_list_rate=100,
do_not_save=1,
**args,
)
if not no_payment_schedule:
si.append(
@@ -111,7 +112,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
self.assertEqual(expected_data[0], [row.invoiced, row.paid, row.credit_note])
pos_inv.cancel()
def test_accounts_receivable(self):
def test_accounts_receivable_with_payment(self):
filters = {
"company": self.company,
"based_on_payment_terms": 1,
@@ -151,11 +152,15 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
cr_note = self.create_credit_note(si.name, do_not_submit=True)
cr_note.update_outstanding_for_self = False
cr_note.save().submit()
# as the invoice partially paid and returning the full amount so the outstanding amount should be True
self.assertEqual(cr_note.update_outstanding_for_self, True)
report = execute(filters)
expected_data_after_credit_note = [100, 0, 0, 40, -40, self.debit_to]
expected_data_after_credit_note = [0, 0, 100, 0, -100, self.debit_to]
row = report[1][0]
row = report[1][-1]
self.assertEqual(
expected_data_after_credit_note,
[
@@ -168,6 +173,105 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
],
)
def test_accounts_receivable_without_payment(self):
filters = {
"company": self.company,
"based_on_payment_terms": 1,
"report_date": today(),
"range1": 30,
"range2": 60,
"range3": 90,
"range4": 120,
"show_remarks": True,
}
# check invoice grand total and invoiced column's value for 3 payment terms
si = self.create_sales_invoice()
report = execute(filters)
expected_data = [[100, 30, "No Remarks"], [100, 50, "No Remarks"], [100, 20, "No Remarks"]]
for i in range(3):
row = report[1][i - 1]
self.assertEqual(expected_data[i - 1], [row.invoice_grand_total, row.invoiced, row.remarks])
# check invoice grand total, invoiced, paid and outstanding column's value after credit note
cr_note = self.create_credit_note(si.name, do_not_submit=True)
cr_note.update_outstanding_for_self = False
cr_note.save().submit()
self.assertEqual(cr_note.update_outstanding_for_self, False)
report = execute(filters)
row = report[1]
self.assertTrue(len(row) == 0)
def test_accounts_receivable_with_partial_payment(self):
filters = {
"company": self.company,
"based_on_payment_terms": 1,
"report_date": today(),
"range1": 30,
"range2": 60,
"range3": 90,
"range4": 120,
"show_remarks": True,
}
# check invoice grand total and invoiced column's value for 3 payment terms
si = self.create_sales_invoice(qty=2)
report = execute(filters)
expected_data = [[200, 60, "No Remarks"], [200, 100, "No Remarks"], [200, 40, "No Remarks"]]
for i in range(3):
row = report[1][i - 1]
self.assertEqual(expected_data[i - 1], [row.invoice_grand_total, row.invoiced, row.remarks])
# check invoice grand total, invoiced, paid and outstanding column's value after payment
self.create_payment_entry(si.name)
report = execute(filters)
expected_data_after_payment = [[200, 60, 40, 20], [200, 100, 0, 100], [200, 40, 0, 40]]
for i in range(3):
row = report[1][i - 1]
self.assertEqual(
expected_data_after_payment[i - 1],
[row.invoice_grand_total, row.invoiced, row.paid, row.outstanding],
)
# check invoice grand total, invoiced, paid and outstanding column's value after credit note
cr_note = self.create_credit_note(si.name, do_not_submit=True)
cr_note.update_outstanding_for_self = False
cr_note.save().submit()
self.assertFalse(cr_note.update_outstanding_for_self)
report = execute(filters)
expected_data_after_credit_note = [
[200, 100, 0, 80, 20, self.debit_to],
[200, 40, 0, 0, 40, self.debit_to],
]
for i in range(2):
row = report[1][i - 1]
self.assertEqual(
expected_data_after_credit_note[i - 1],
[
row.invoice_grand_total,
row.invoiced,
row.paid,
row.credit_note,
row.outstanding,
row.party_account,
],
)
def test_cr_note_flag_to_update_self(self):
filters = {
"company": self.company,

View File

@@ -25,11 +25,26 @@ frappe.query_reports["Asset Depreciations and Balances"] = {
default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2],
reqd: 1,
},
{
fieldname: "group_by",
label: __("Group By"),
fieldtype: "Select",
options: ["Asset Category", "Asset"],
default: "Asset Category",
},
{
fieldname: "asset_category",
label: __("Asset Category"),
fieldtype: "Link",
options: "Asset Category",
depends_on: "eval: doc.group_by == 'Asset Category'",
},
{
fieldname: "asset",
label: __("Asset"),
fieldtype: "Link",
options: "Asset",
depends_on: "eval: doc.group_by == 'Asset'",
},
],
};

View File

@@ -14,21 +14,28 @@ def execute(filters=None):
def get_data(filters):
if filters.get("group_by") == "Asset Category":
return get_group_by_asset_category_data(filters)
elif filters.get("group_by") == "Asset":
return get_group_by_asset_data(filters)
def get_group_by_asset_category_data(filters):
data = []
asset_categories = get_asset_categories(filters)
assets = get_assets(filters)
asset_categories = get_asset_categories_for_grouped_by_category(filters)
assets = get_assets_for_grouped_by_category(filters)
for asset_category in asset_categories:
row = frappe._dict()
# row.asset_category = asset_category
row.update(asset_category)
row.cost_as_on_to_date = (
flt(row.cost_as_on_from_date)
+ flt(row.cost_of_new_purchase)
- flt(row.cost_of_sold_asset)
- flt(row.cost_of_scrapped_asset)
row.value_as_on_to_date = (
flt(row.value_as_on_from_date)
+ flt(row.value_of_new_purchase)
- flt(row.value_of_sold_asset)
- flt(row.value_of_scrapped_asset)
- flt(row.value_of_capitalized_asset)
)
row.update(
@@ -38,17 +45,19 @@ def get_data(filters):
if asset["asset_category"] == asset_category.get("asset_category", "")
)
)
row.accumulated_depreciation_as_on_to_date = (
flt(row.accumulated_depreciation_as_on_from_date)
+ flt(row.depreciation_amount_during_the_period)
- flt(row.depreciation_eliminated_during_the_period)
- flt(row.depreciation_eliminated_via_reversal)
)
row.net_asset_value_as_on_from_date = flt(row.cost_as_on_from_date) - flt(
row.net_asset_value_as_on_from_date = flt(row.value_as_on_from_date) - flt(
row.accumulated_depreciation_as_on_from_date
)
row.net_asset_value_as_on_to_date = flt(row.cost_as_on_to_date) - flt(
row.net_asset_value_as_on_to_date = flt(row.value_as_on_to_date) - flt(
row.accumulated_depreciation_as_on_to_date
)
@@ -57,52 +66,71 @@ def get_data(filters):
return data
def get_asset_categories(filters):
def get_asset_categories_for_grouped_by_category(filters):
condition = ""
if filters.get("asset_category"):
condition += " and asset_category = %(asset_category)s"
condition += " and a.asset_category = %(asset_category)s"
# nosemgrep
return frappe.db.sql(
f"""
SELECT asset_category,
ifnull(sum(case when purchase_date < %(from_date)s then
case when ifnull(disposal_date, 0) = 0 or disposal_date >= %(from_date)s then
gross_purchase_amount
SELECT a.asset_category,
ifnull(sum(case when a.purchase_date < %(from_date)s then
case when ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s then
a.gross_purchase_amount
else
0
end
else
0
end), 0) as cost_as_on_from_date,
ifnull(sum(case when purchase_date >= %(from_date)s then
gross_purchase_amount
end), 0) as value_as_on_from_date,
ifnull(sum(case when a.purchase_date >= %(from_date)s then
a.gross_purchase_amount
else
0
end), 0) as cost_of_new_purchase,
ifnull(sum(case when ifnull(disposal_date, 0) != 0
and disposal_date >= %(from_date)s
and disposal_date <= %(to_date)s then
case when status = "Sold" then
gross_purchase_amount
end), 0) as value_of_new_purchase,
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0
and a.disposal_date >= %(from_date)s
and a.disposal_date <= %(to_date)s then
case when a.status = "Sold" then
a.gross_purchase_amount
else
0
end
else
0
end), 0) as cost_of_sold_asset,
ifnull(sum(case when ifnull(disposal_date, 0) != 0
and disposal_date >= %(from_date)s
and disposal_date <= %(to_date)s then
case when status = "Scrapped" then
gross_purchase_amount
end), 0) as value_of_sold_asset,
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0
and a.disposal_date >= %(from_date)s
and a.disposal_date <= %(to_date)s then
case when a.status = "Scrapped" then
a.gross_purchase_amount
else
0
end
else
0
end), 0) as cost_of_scrapped_asset
from `tabAsset`
where docstatus=1 and company=%(company)s and purchase_date <= %(to_date)s {condition}
group by asset_category
end), 0) as value_of_scrapped_asset,
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0
and a.disposal_date >= %(from_date)s
and a.disposal_date <= %(to_date)s then
case when a.status = "Capitalized" then
a.gross_purchase_amount
else
0
end
else
0
end), 0) as value_of_capitalized_asset
from `tabAsset` a
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s {condition}
and not exists(
select 1 from `tabAsset Capitalization Asset Item` acai join `tabAsset Capitalization` ac on acai.parent=ac.name
where acai.asset = a.name
and ac.posting_date < %(from_date)s
and ac.docstatus=1
)
group by a.asset_category
""",
{
"to_date": filters.to_date,
@@ -114,14 +142,17 @@ def get_asset_categories(filters):
)
def get_assets(filters):
def get_assets_for_grouped_by_category(filters):
condition = ""
if filters.get("asset_category"):
condition = " and a.asset_category = '{}'".format(filters.get("asset_category"))
condition = f" and a.asset_category = '{filters.get('asset_category')}'"
# nosemgrep
return frappe.db.sql(
"""
f"""
SELECT results.asset_category,
sum(results.accumulated_depreciation_as_on_from_date) as accumulated_depreciation_as_on_from_date,
sum(results.depreciation_eliminated_via_reversal) as depreciation_eliminated_via_reversal,
sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period,
sum(results.depreciation_amount_during_the_period) as depreciation_amount_during_the_period
from (SELECT a.asset_category,
@@ -130,6 +161,11 @@ def get_assets(filters):
else
0
end), 0) as accumulated_depreciation_as_on_from_date,
ifnull(sum(case when gle.posting_date <= %(to_date)s and ifnull(a.disposal_date, 0) = 0 then
gle.credit
else
0
end), 0) as depreciation_eliminated_via_reversal,
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and a.disposal_date >= %(from_date)s
and a.disposal_date <= %(to_date)s and gle.posting_date <= a.disposal_date then
gle.debit
@@ -149,15 +185,22 @@ def get_assets(filters):
aca.parent = a.asset_category and aca.company_name = %(company)s
join `tabCompany` company on
company.name = %(company)s
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and gle.debit != 0 and gle.is_cancelled = 0 and gle.account = ifnull(aca.depreciation_expense_account, company.depreciation_expense_account) {0}
where
a.docstatus=1
and a.company=%(company)s
and a.purchase_date <= %(to_date)s
and gle.is_cancelled = 0
and gle.account = ifnull(aca.depreciation_expense_account, company.depreciation_expense_account)
{condition}
group by a.asset_category
union
SELECT a.asset_category,
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and (a.disposal_date < %(from_date)s or a.disposal_date > %(to_date)s) then
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and a.disposal_date < %(from_date)s then
0
else
a.opening_accumulated_depreciation
end), 0) as accumulated_depreciation_as_on_from_date,
0 as depreciation_eliminated_via_reversal,
ifnull(sum(case when a.disposal_date >= %(from_date)s and a.disposal_date <= %(to_date)s then
a.opening_accumulated_depreciation
else
@@ -165,51 +208,272 @@ def get_assets(filters):
end), 0) as depreciation_eliminated_during_the_period,
0 as depreciation_amount_during_the_period
from `tabAsset` a
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s {0}
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s {condition}
group by a.asset_category) as results
group by results.asset_category
""".format(condition),
{"to_date": filters.to_date, "from_date": filters.from_date, "company": filters.company},
""",
{
"to_date": filters.to_date,
"from_date": filters.from_date,
"company": filters.company,
},
as_dict=1,
)
def get_group_by_asset_data(filters):
data = []
asset_details = get_asset_details_for_grouped_by_category(filters)
assets = get_assets_for_grouped_by_asset(filters)
for asset_detail in asset_details:
row = frappe._dict()
row.update(asset_detail)
row.value_as_on_to_date = (
flt(row.value_as_on_from_date)
+ flt(row.value_of_new_purchase)
- flt(row.value_of_sold_asset)
- flt(row.value_of_scrapped_asset)
- flt(row.value_of_capitalized_asset)
)
row.update(next(asset for asset in assets if asset["asset"] == asset_detail.get("name", "")))
row.accumulated_depreciation_as_on_to_date = (
flt(row.accumulated_depreciation_as_on_from_date)
+ flt(row.depreciation_amount_during_the_period)
- flt(row.depreciation_eliminated_during_the_period)
- flt(row.depreciation_eliminated_via_reversal)
)
row.net_asset_value_as_on_from_date = flt(row.value_as_on_from_date) - flt(
row.accumulated_depreciation_as_on_from_date
)
row.net_asset_value_as_on_to_date = flt(row.value_as_on_to_date) - flt(
row.accumulated_depreciation_as_on_to_date
)
data.append(row)
return data
def get_asset_details_for_grouped_by_category(filters):
condition = ""
if filters.get("asset"):
condition += " and a.name = %(asset)s"
# nosemgrep
return frappe.db.sql(
f"""
SELECT a.name,
ifnull(sum(case when a.purchase_date < %(from_date)s then
case when ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s then
a.gross_purchase_amount
else
0
end
else
0
end), 0) as value_as_on_from_date,
ifnull(sum(case when a.purchase_date >= %(from_date)s then
a.gross_purchase_amount
else
0
end), 0) as value_of_new_purchase,
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0
and a.disposal_date >= %(from_date)s
and a.disposal_date <= %(to_date)s then
case when a.status = "Sold" then
a.gross_purchase_amount
else
0
end
else
0
end), 0) as value_of_sold_asset,
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0
and a.disposal_date >= %(from_date)s
and a.disposal_date <= %(to_date)s then
case when a.status = "Scrapped" then
a.gross_purchase_amount
else
0
end
else
0
end), 0) as value_of_scrapped_asset,
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0
and a.disposal_date >= %(from_date)s
and a.disposal_date <= %(to_date)s then
case when a.status = "Capitalized" then
a.gross_purchase_amount
else
0
end
else
0
end), 0) as value_of_capitalized_asset
from `tabAsset` a
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s {condition}
and not exists(
select 1 from `tabAsset Capitalization Asset Item` acai join `tabAsset Capitalization` ac on acai.parent=ac.name
where acai.asset = a.name
and ac.posting_date < %(from_date)s
and ac.docstatus=1
)
group by a.name
""",
{
"to_date": filters.to_date,
"from_date": filters.from_date,
"company": filters.company,
"asset": filters.get("asset"),
},
as_dict=1,
)
def get_assets_for_grouped_by_asset(filters):
condition = ""
if filters.get("asset"):
condition = f" and a.name = '{filters.get('asset')}'"
# nosemgrep
return frappe.db.sql(
f"""
SELECT results.name as asset,
sum(results.accumulated_depreciation_as_on_from_date) as accumulated_depreciation_as_on_from_date,
sum(results.depreciation_eliminated_via_reversal) as depreciation_eliminated_via_reversal,
sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period,
sum(results.depreciation_amount_during_the_period) as depreciation_amount_during_the_period
from (SELECT a.name as name,
ifnull(sum(case when gle.posting_date < %(from_date)s and (ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s) then
gle.debit
else
0
end), 0) as accumulated_depreciation_as_on_from_date,
ifnull(sum(case when gle.posting_date <= %(to_date)s and ifnull(a.disposal_date, 0) = 0 then
gle.credit
else
0
end), 0) as depreciation_eliminated_via_reversal,
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and a.disposal_date >= %(from_date)s
and a.disposal_date <= %(to_date)s and gle.posting_date <= a.disposal_date then
gle.debit
else
0
end), 0) as depreciation_eliminated_during_the_period,
ifnull(sum(case when gle.posting_date >= %(from_date)s and gle.posting_date <= %(to_date)s
and (ifnull(a.disposal_date, 0) = 0 or gle.posting_date <= a.disposal_date) then
gle.debit
else
0
end), 0) as depreciation_amount_during_the_period
from `tabGL Entry` gle
join `tabAsset` a on
gle.against_voucher = a.name
join `tabAsset Category Account` aca on
aca.parent = a.asset_category and aca.company_name = %(company)s
join `tabCompany` company on
company.name = %(company)s
where
a.docstatus=1
and a.company=%(company)s
and a.purchase_date <= %(to_date)s
and gle.is_cancelled = 0
and gle.account = ifnull(aca.depreciation_expense_account, company.depreciation_expense_account)
{condition}
group by a.name
union
SELECT a.name as name,
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and a.disposal_date < %(from_date)s then
0
else
a.opening_accumulated_depreciation
end), 0) as accumulated_depreciation_as_on_from_date,
0 as depreciation_as_on_from_date_credit,
ifnull(sum(case when a.disposal_date >= %(from_date)s and a.disposal_date <= %(to_date)s then
a.opening_accumulated_depreciation
else
0
end), 0) as depreciation_eliminated_during_the_period,
0 as depreciation_amount_during_the_period
from `tabAsset` a
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s {condition}
group by a.name) as results
group by results.name
""",
{
"to_date": filters.to_date,
"from_date": filters.from_date,
"company": filters.company,
},
as_dict=1,
)
def get_columns(filters):
return [
columns = []
if filters.get("group_by") == "Asset Category":
columns.append(
{
"label": _("Asset Category"),
"fieldname": "asset_category",
"fieldtype": "Link",
"options": "Asset Category",
"width": 120,
}
)
elif filters.get("group_by") == "Asset":
columns.append(
{
"label": _("Asset"),
"fieldname": "asset",
"fieldtype": "Link",
"options": "Asset",
"width": 120,
}
)
columns += [
{
"label": _("Asset Category"),
"fieldname": "asset_category",
"fieldtype": "Link",
"options": "Asset Category",
"width": 120,
},
{
"label": _("Cost as on") + " " + formatdate(filters.day_before_from_date),
"fieldname": "cost_as_on_from_date",
"label": _("Value as on") + " " + formatdate(filters.day_before_from_date),
"fieldname": "value_as_on_from_date",
"fieldtype": "Currency",
"width": 140,
},
{
"label": _("Cost of New Purchase"),
"fieldname": "cost_of_new_purchase",
"label": _("Value of New Purchase"),
"fieldname": "value_of_new_purchase",
"fieldtype": "Currency",
"width": 140,
},
{
"label": _("Cost of Sold Asset"),
"fieldname": "cost_of_sold_asset",
"label": _("Value of Sold Asset"),
"fieldname": "value_of_sold_asset",
"fieldtype": "Currency",
"width": 140,
},
{
"label": _("Cost of Scrapped Asset"),
"fieldname": "cost_of_scrapped_asset",
"label": _("Value of Scrapped Asset"),
"fieldname": "value_of_scrapped_asset",
"fieldtype": "Currency",
"width": 140,
},
{
"label": _("Cost as on") + " " + formatdate(filters.to_date),
"fieldname": "cost_as_on_to_date",
"label": _("Value of New Capitalized Asset"),
"fieldname": "value_of_capitalized_asset",
"fieldtype": "Currency",
"width": 140,
},
{
"label": _("Value as on") + " " + formatdate(filters.to_date),
"fieldname": "value_as_on_to_date",
"fieldtype": "Currency",
"width": 140,
},
@@ -237,6 +501,12 @@ def get_columns(filters):
"fieldtype": "Currency",
"width": 270,
},
{
"label": _("Depreciation eliminated via reversal"),
"fieldname": "depreciation_eliminated_via_reversal",
"fieldtype": "Currency",
"width": 270,
},
{
"label": _("Net Asset value as on") + " " + formatdate(filters.day_before_from_date),
"fieldname": "net_asset_value_as_on_from_date",
@@ -250,3 +520,5 @@ def get_columns(filters):
"width": 200,
},
]
return columns

View File

@@ -1,6 +1,3 @@
<div style="margin-bottom: 7px;">
{%= frappe.boot.letter_heads[frappe.defaults.get_default("letter_head")] %}
</div>
<h2 class="text-center">{%= __("Bank Reconciliation Statement") %}</h2>
<h4 class="text-center">{%= filters.account && (filters.account + ", "+filters.report_date) || "" %} {%= filters.company %}</h4>
<hr>
@@ -46,4 +43,4 @@
{% } %}
</tbody>
</table>
<p class="text-right text-muted">Printed On {%= frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string()) %}</p>
<p class="text-right text-muted">{%= __("Printed on {0}", [frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string())]) %}</p>

View File

@@ -27,6 +27,7 @@ def get_report_filters(report_filters):
["Purchase Invoice", "docstatus", "=", 1],
["Purchase Invoice", "per_received", "<", 100],
["Purchase Invoice", "update_stock", "=", 0],
["Purchase Invoice", "is_opening", "!=", "Yes"],
]
if report_filters.get("purchase_invoice"):

View File

@@ -263,6 +263,7 @@ def get_actual_details(name, filters):
and ba.account=gl.account
and b.{budget_against} = gl.{budget_against}
and gl.fiscal_year between %s and %s
and gl.is_cancelled = 0
and b.{budget_against} = %s
and exists(
select

View File

@@ -67,5 +67,5 @@
</tbody>
</table>
<p class="text-right text-muted">
Printed On {%= frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string()) %}
{%= __("Printed on {0}", [frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string())]) %}
</p>

View File

@@ -78,4 +78,4 @@
{% } %}
</tbody>
</table>
<p class="text-right text-muted">Printed On {%= frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string()) %}</p>
<p class="text-right text-muted">{%= __("Printed on {0}", [frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string())]) %}</p>

View File

@@ -49,9 +49,14 @@ frappe.query_reports["General Ledger"] = {
label: __("Voucher No"),
fieldtype: "Data",
on_change: function () {
frappe.query_report.set_filter_value("group_by", "Group by Voucher (Consolidated)");
frappe.query_report.set_filter_value("categorize_by", "Categorize by Voucher (Consolidated)");
},
},
{
fieldname: "against_voucher_no",
label: __("Against Voucher No"),
fieldtype: "Data",
},
{
fieldtype: "Break",
},
@@ -61,7 +66,7 @@ frappe.query_reports["General Ledger"] = {
fieldtype: "Autocomplete",
options: Object.keys(frappe.boot.party_account_types),
on_change: function () {
frappe.query_report.set_filter_value("party", "");
frappe.query_report.set_filter_value("party", []);
},
},
{
@@ -107,29 +112,29 @@ frappe.query_reports["General Ledger"] = {
hidden: 1,
},
{
fieldname: "group_by",
label: __("Group by"),
fieldname: "categorize_by",
label: __("Categorize by"),
fieldtype: "Select",
options: [
"",
{
label: __("Group by Voucher"),
value: "Group by Voucher",
label: __("Categorize by Voucher"),
value: "Categorize by Voucher",
},
{
label: __("Group by Voucher (Consolidated)"),
value: "Group by Voucher (Consolidated)",
label: __("Categorize by Voucher (Consolidated)"),
value: "Categorize by Voucher (Consolidated)",
},
{
label: __("Group by Account"),
value: "Group by Account",
label: __("Categorize by Account"),
value: "Categorize by Account",
},
{
label: __("Group by Party"),
value: "Group by Party",
label: __("Categorize by Party"),
value: "Categorize by Party",
},
],
default: "Group by Voucher (Consolidated)",
default: "Categorize by Voucher (Consolidated)",
},
{
fieldname: "tax_id",
@@ -192,6 +197,11 @@ frappe.query_reports["General Ledger"] = {
label: __("Show Net Values in Party Account"),
fieldtype: "Check",
},
{
fieldname: "show_amount_in_company_currency",
label: __("Show Credit / Debit in Company Currency"),
fieldtype: "Check",
},
{
fieldname: "show_remarks",
label: __("Show Remarks"),

View File

@@ -72,13 +72,17 @@ def validate_filters(filters, account_details):
if not account_details.get(account):
frappe.throw(_("Account {0} does not exists").format(account))
if filters.get("account") and filters.get("group_by") == "Group by Account":
if not filters.get("categorize_by") and filters.get("group_by"):
filters["categorize_by"] = filters["group_by"]
filters["categorize_by"] = filters["categorize_by"].replace("Group by", "Categorize by")
if filters.get("account") and filters.get("categorize_by") == "Categorize by Account":
filters.account = frappe.parse_json(filters.get("account"))
for account in filters.account:
if account_details[account].is_group == 0:
frappe.throw(_("Can not filter based on Child Account, if grouped by Account"))
if filters.get("voucher_no") and filters.get("group_by") in ["Group by Voucher"]:
if filters.get("voucher_no") and filters.get("categorize_by") in ["Categorize by Voucher"]:
frappe.throw(_("Can not filter based on Voucher No, if grouped by Voucher"))
if filters.from_date > filters.to_date:
@@ -172,9 +176,9 @@ def get_gl_entries(filters, accounting_dimensions):
if filters.get("include_dimensions"):
order_by_statement = "order by posting_date, creation"
if filters.get("group_by") == "Group by Voucher":
if filters.get("categorize_by") == "Categorize by Voucher":
order_by_statement = "order by posting_date, voucher_type, voucher_no"
if filters.get("group_by") == "Group by Account":
if filters.get("categorize_by") == "Categorize by Account":
order_by_statement = "order by account, posting_date, creation"
if filters.get("include_default_book_entries"):
@@ -201,7 +205,7 @@ def get_gl_entries(filters, accounting_dimensions):
)
if filters.get("presentation_currency"):
return convert_to_presentation_currency(gl_entries, currency_map)
return convert_to_presentation_currency(gl_entries, currency_map, filters)
else:
return gl_entries
@@ -225,6 +229,9 @@ def get_conditions(filters):
if filters.get("voucher_no"):
conditions.append("voucher_no=%(voucher_no)s")
if filters.get("against_voucher_no"):
conditions.append("against_voucher=%(against_voucher_no)s")
if filters.get("ignore_err"):
err_journals = frappe.db.get_all(
"Journal Entry",
@@ -258,7 +265,7 @@ def get_conditions(filters):
if filters.get("voucher_no_not_in"):
conditions.append("voucher_no not in %(voucher_no_not_in)s")
if filters.get("group_by") == "Group by Party" and not filters.get("party_type"):
if filters.get("categorize_by") == "Categorize by Party" and not filters.get("party_type"):
conditions.append("party_type in ('Customer', 'Supplier')")
if filters.get("party_type"):
@@ -270,7 +277,7 @@ def get_conditions(filters):
if not (
filters.get("account")
or filters.get("party")
or filters.get("group_by") in ["Group by Account", "Group by Party"]
or filters.get("categorize_by") in ["Categorize by Account", "Categorize by Party"]
):
if not ignore_is_opening:
conditions.append("(posting_date >=%(from_date)s or is_opening = 'Yes')")
@@ -365,13 +372,13 @@ def get_data_with_opening_closing(filters, account_details, accounting_dimension
# Opening for filtered account
data.append(totals.opening)
if filters.get("group_by") != "Group by Voucher (Consolidated)":
if filters.get("categorize_by") != "Categorize by Voucher (Consolidated)":
for _acc, acc_dict in gle_map.items():
# acc
if acc_dict.entries:
# opening
data.append({})
if filters.get("group_by") != "Group by Voucher":
if filters.get("categorize_by") != "Categorize by Voucher":
data.append(acc_dict.totals.opening)
data += acc_dict.entries
@@ -380,7 +387,7 @@ def get_data_with_opening_closing(filters, account_details, accounting_dimension
data.append(acc_dict.totals.total)
# closing
if filters.get("group_by") != "Group by Voucher":
if filters.get("categorize_by") != "Categorize by Voucher":
data.append(acc_dict.totals.closing)
data.append({})
else:
@@ -413,9 +420,9 @@ def get_totals_dict():
def group_by_field(group_by):
if group_by == "Group by Party":
if group_by == "Categorize by Party":
return "party"
elif group_by in ["Group by Voucher (Consolidated)", "Group by Account"]:
elif group_by in ["Categorize by Voucher (Consolidated)", "Categorize by Account"]:
return "account"
else:
return "voucher_no"
@@ -423,7 +430,7 @@ def group_by_field(group_by):
def initialize_gle_map(gl_entries, filters):
gle_map = OrderedDict()
group_by = group_by_field(filters.get("group_by"))
group_by = group_by_field(filters.get("categorize_by"))
for gle in gl_entries:
gle_map.setdefault(gle.get(group_by), _dict(totals=get_totals_dict(), entries=[]))
@@ -434,8 +441,8 @@ def get_accountwise_gle(filters, accounting_dimensions, gl_entries, gle_map):
totals = get_totals_dict()
entries = []
consolidated_gle = OrderedDict()
group_by = group_by_field(filters.get("group_by"))
group_by_voucher_consolidated = filters.get("group_by") == "Group by Voucher (Consolidated)"
group_by = group_by_field(filters.get("categorize_by"))
group_by_voucher_consolidated = filters.get("categorize_by") == "Categorize by Voucher (Consolidated)"
if filters.get("show_net_values_in_party_account"):
account_type_map = get_account_type_map(filters.get("company"))
@@ -506,6 +513,7 @@ def get_accountwise_gle(filters, accounting_dimensions, gl_entries, gle_map):
for dim in accounting_dimensions:
keylist.append(gle.get(dim))
keylist.append(gle.get("cost_center"))
keylist.append(gle.get("project"))
key = tuple(keylist)
if key not in consolidated_gle:
@@ -530,17 +538,20 @@ def get_account_type_map(company):
def get_result_as_list(data, filters):
balance, _balance_in_account_currency = 0, 0
balance = 0
for d in data:
if not d.get("posting_date"):
balance, _balance_in_account_currency = 0, 0
balance = 0
balance = get_balance(d, balance, "debit", "credit")
d["balance"] = balance
d["account_currency"] = filters.account_currency
d["presentation_currency"] = filters.presentation_currency
return data
@@ -566,11 +577,21 @@ def get_columns(filters):
if filters.get("presentation_currency"):
currency = filters["presentation_currency"]
else:
if filters.get("company"):
currency = get_company_currency(filters["company"])
else:
company = get_default_company()
currency = get_company_currency(company)
company = filters.get("company") or get_default_company()
filters["presentation_currency"] = currency = get_company_currency(company)
company_currency = get_company_currency(filters.get("company") or get_default_company())
if (
filters.get("show_amount_in_company_currency")
and filters["presentation_currency"] != company_currency
):
frappe.throw(
_("Presentation Currency cannot be {0} , When {1} is enabled.").format(
frappe.bold(filters["presentation_currency"]),
frappe.bold("Show Credit / Debit in Company Currency"),
)
)
columns = [
{
@@ -591,19 +612,22 @@ def get_columns(filters):
{
"label": _("Debit ({0})").format(currency),
"fieldname": "debit",
"fieldtype": "Float",
"fieldtype": "Currency",
"options": "presentation_currency",
"width": 130,
},
{
"label": _("Credit ({0})").format(currency),
"fieldname": "credit",
"fieldtype": "Float",
"fieldtype": "Currency",
"options": "presentation_currency",
"width": 130,
},
{
"label": _("Balance ({0})").format(currency),
"fieldname": "balance",
"fieldtype": "Float",
"fieldtype": "Currency",
"options": "presentation_currency",
"width": 130,
},
{"label": _("Voucher Type"), "fieldname": "voucher_type", "width": 120},
@@ -617,10 +641,11 @@ def get_columns(filters):
{"label": _("Against Account"), "fieldname": "against", "width": 120},
{"label": _("Party Type"), "fieldname": "party_type", "width": 100},
{"label": _("Party"), "fieldname": "party", "width": 100},
{"label": _("Project"), "options": "Project", "fieldname": "project", "width": 100},
]
if filters.get("include_dimensions"):
columns.append({"label": _("Project"), "options": "Project", "fieldname": "project", "width": 100})
for dim in get_accounting_dimensions(as_list=False):
columns.append(
{"label": _(dim.label), "options": dim.label, "fieldname": dim.fieldname, "width": 100}

View File

@@ -155,7 +155,7 @@ class TestGeneralLedger(FrappeTestCase):
"from_date": today(),
"to_date": today(),
"account": [account.name],
"group_by": "Group by Voucher (Consolidated)",
"categorize_by": "Categorize by Voucher (Consolidated)",
}
)
)
@@ -246,7 +246,7 @@ class TestGeneralLedger(FrappeTestCase):
"from_date": today(),
"to_date": today(),
"account": [account.name],
"group_by": "Group by Voucher (Consolidated)",
"categorize_by": "Categorize by Voucher (Consolidated)",
"ignore_err": True,
}
)
@@ -261,7 +261,7 @@ class TestGeneralLedger(FrappeTestCase):
"from_date": today(),
"to_date": today(),
"account": [account.name],
"group_by": "Group by Voucher (Consolidated)",
"categorize_by": "Categorize by Voucher (Consolidated)",
"ignore_err": False,
}
)
@@ -308,7 +308,7 @@ class TestGeneralLedger(FrappeTestCase):
"from_date": si.posting_date,
"to_date": si.posting_date,
"account": [si.debit_to],
"group_by": "Group by Voucher (Consolidated)",
"categorize_by": "Categorize by Voucher (Consolidated)",
"ignore_cr_dr_notes": False,
}
)
@@ -325,7 +325,7 @@ class TestGeneralLedger(FrappeTestCase):
"from_date": si.posting_date,
"to_date": si.posting_date,
"account": [si.debit_to],
"group_by": "Group by Voucher (Consolidated)",
"categorize_by": "Categorize by Voucher (Consolidated)",
"ignore_cr_dr_notes": True,
}
)

View File

@@ -11,7 +11,7 @@ import erpnext
from erpnext.accounts.report.item_wise_sales_register.item_wise_sales_register import (
add_sub_total_row,
add_total_row,
apply_group_by_conditions,
apply_order_by_conditions,
get_grand_total,
get_group_by_and_display_fields,
get_tax_accounts,
@@ -305,12 +305,6 @@ def apply_conditions(query, pi, pii, filters):
if filters.get("item_group"):
query = query.where(pii.item_group == filters.get("item_group"))
if not filters.get("group_by"):
query = query.orderby(pi.posting_date, order=Order.desc)
query = query.orderby(pii.item_group, order=Order.desc)
else:
query = apply_group_by_conditions(query, pi, pii, filters)
return query
@@ -372,7 +366,17 @@ def get_items(filters, additional_table_columns):
query = apply_conditions(query, pi, pii, filters)
return query.run(as_dict=True)
from frappe.desk.reportview import build_match_conditions
query, params = query.walk()
match_conditions = build_match_conditions(doctype)
if match_conditions:
query += " and " + match_conditions
query = apply_order_by_conditions(query, pi, pii, filters)
return frappe.db.sql(query, params, as_dict=True)
def get_aii_accounts():

View File

@@ -384,27 +384,24 @@ def apply_conditions(query, si, sii, filters, additional_conditions=None):
| (si.unrealized_profit_loss_account == filters.get("income_account"))
)
if not filters.get("group_by"):
query = query.orderby(si.posting_date, order=Order.desc)
query = query.orderby(sii.item_group, order=Order.desc)
else:
query = apply_group_by_conditions(query, si, sii, filters)
for key, value in (additional_conditions or {}).items():
query = query.where(si[key] == value)
return query
def apply_group_by_conditions(query, si, ii, filters):
if filters.get("group_by") == "Invoice":
query = query.orderby(ii.parent, order=Order.desc)
def apply_order_by_conditions(query, si, ii, filters):
if not filters.get("group_by"):
query += f" order by {si.posting_date} desc, {ii.item_group} desc"
elif filters.get("group_by") == "Invoice":
query += f" order by {ii.parent} desc"
elif filters.get("group_by") == "Item":
query = query.orderby(ii.item_code)
query += f" order by {ii.item_code}"
elif filters.get("group_by") == "Item Group":
query = query.orderby(ii.item_group)
query += f" order by {ii.item_group}"
elif filters.get("group_by") in ("Customer", "Customer Group", "Territory", "Supplier"):
query = query.orderby(si[frappe.scrub(filters.get("group_by"))])
filter_field = frappe.scrub(filters.get("group_by"))
query += f" order by {filter_field} desc"
return query
@@ -479,7 +476,17 @@ def get_items(filters, additional_query_columns, additional_conditions=None):
query = apply_conditions(query, si, sii, filters, additional_conditions)
return query.run(as_dict=True)
from frappe.desk.reportview import build_match_conditions
query, params = query.walk()
match_conditions = build_match_conditions("Sales Invoice")
if match_conditions:
query += " and " + match_conditions
query = apply_order_by_conditions(query, si, sii, filters)
return frappe.db.sql(query, params, as_dict=True)
def get_delivery_notes_against_sales_order(item_list):

View File

@@ -46,6 +46,7 @@ class PaymentLedger:
against_voucher_no=ple.against_voucher_no,
amount=ple.amount,
currency=ple.account_currency,
company=ple.company,
)
if self.filters.include_account_currency:
@@ -77,6 +78,7 @@ class PaymentLedger:
against_voucher_no="Outstanding:",
amount=total,
currency=voucher_data[0].currency,
company=voucher_data[0].company,
)
if self.filters.include_account_currency:
@@ -85,7 +87,12 @@ class PaymentLedger:
voucher_data.append(entry)
# empty row
voucher_data.append(frappe._dict())
voucher_data.append(
frappe._dict(
currency=voucher_data[0].currency,
company=voucher_data[0].company,
)
)
self.data.extend(voucher_data)
def build_conditions(self):

View File

@@ -397,7 +397,6 @@ def get_invoices(filters, additional_query_columns):
pi.mode_of_payment,
)
.where(pi.docstatus == 1)
.orderby(pi.posting_date, pi.name, order=Order.desc)
)
if additional_query_columns:
@@ -413,8 +412,17 @@ def get_invoices(filters, additional_query_columns):
filters, query, doctype="Purchase Invoice", child_doctype="Purchase Invoice Item"
)
invoices = query.run(as_dict=True)
return invoices
from frappe.desk.reportview import build_match_conditions
query, params = query.walk()
match_conditions = build_match_conditions("Purchase Invoice")
if match_conditions:
query += " and " + match_conditions
query += " order by posting_date desc, name desc"
return frappe.db.sql(query, params, as_dict=True)
def get_conditions(filters, query, doctype):

View File

@@ -438,7 +438,6 @@ def get_invoices(filters, additional_query_columns):
si.company,
)
.where(si.docstatus == 1)
.orderby(si.posting_date, si.name, order=Order.desc)
)
if additional_query_columns:
@@ -453,8 +452,17 @@ def get_invoices(filters, additional_query_columns):
filters, query, doctype="Sales Invoice", child_doctype="Sales Invoice Item"
)
invoices = query.run(as_dict=True)
return invoices
from frappe.desk.reportview import build_match_conditions
query, params = query.walk()
match_conditions = build_match_conditions("Sales Invoice")
if match_conditions:
query += " and " + match_conditions
query += " order by posting_date desc, name desc"
return frappe.db.sql(query, params, as_dict=True)
def get_conditions(filters, query, doctype):

View File

@@ -52,28 +52,25 @@ def group_by_party_and_category(data, filters):
party_category_wise_map = {}
for row in data:
key = (row.get("party_type"), row.get("party"), row.get("tax_withholding_category"))
party_category_wise_map.setdefault(
(row.get("party"), row.get("section_code")),
key,
{
"pan": row.get("pan"),
"tax_id": row.get("tax_id"),
"party": row.get("party"),
"party_type": row.get("party_type"),
"party_name": row.get("party_name"),
"section_code": row.get("section_code"),
"entity_type": row.get("entity_type"),
"tax_withholding_category": row.get("tax_withholding_category"),
"party_entity_type": row.get("party_entity_type"),
"rate": row.get("rate"),
"total_amount": 0.0,
"tax_amount": 0.0,
},
)
party_category_wise_map.get((row.get("party"), row.get("section_code")))["total_amount"] += row.get(
"total_amount", 0.0
)
party_category_wise_map.get((row.get("party"), row.get("section_code")))["tax_amount"] += row.get(
"tax_amount", 0.0
)
party_category_wise_map.get(key)["total_amount"] += row.get("total_amount", 0.0)
party_category_wise_map.get(key)["tax_amount"] += row.get("tax_amount", 0.0)
final_result = get_final_result(party_category_wise_map)
@@ -114,13 +111,18 @@ def get_columns(filters):
columns.extend(
[
{
"label": _("Section Code"),
"label": _("Tax Withholding Category"),
"options": "Tax Withholding Category",
"fieldname": "section_code",
"fieldname": "tax_withholding_category",
"fieldtype": "Link",
"width": 180,
},
{"label": _("Entity Type"), "fieldname": "entity_type", "fieldtype": "Data", "width": 180},
{
"label": _("{0} Type").format(_(filters.get("party_type", "Party"))),
"fieldname": "party_entity_type",
"fieldtype": "Data",
"width": 180,
},
{
"label": _("TDS Rate %") if filters.get("party_type") == "Supplier" else _("TCS Rate %"),
"fieldname": "rate",

View File

@@ -4,6 +4,7 @@
import frappe
from frappe import _
from frappe.utils import getdate
def execute(filters=None):
@@ -33,6 +34,7 @@ def execute(filters=None):
def validate_filters(filters):
"""Validate if dates are properly set"""
filters = frappe._dict(filters or {})
if filters.from_date > filters.to_date:
frappe.throw(_("From Date must be before To Date"))
@@ -43,6 +45,7 @@ def get_result(filters, tds_docs, tds_accounts, tax_category_map, journal_entry_
gle_map = get_gle_map(tds_docs)
out = []
entries = {}
for name, details in gle_map.items():
for entry in details:
tax_amount, total_amount, grand_total, base_total = 0, 0, 0, 0
@@ -68,7 +71,7 @@ def get_result(filters, tds_docs, tds_accounts, tax_category_map, journal_entry_
if not tax_withholding_category:
tax_withholding_category = party_map.get(party, {}).get("tax_withholding_category")
rate = tax_rate_map.get(tax_withholding_category)
rate = get_tax_withholding_rates(tax_rate_map.get(tax_withholding_category, []), posting_date)
if net_total_map.get((voucher_type, name)):
if voucher_type == "Journal Entry" and tax_amount and rate:
# back calcalute total amount from rate and tax_amount
@@ -103,8 +106,8 @@ def get_result(filters, tds_docs, tds_accounts, tax_category_map, journal_entry_
row.update(
{
"section_code": tax_withholding_category or "",
"entity_type": party_map.get(party, {}).get(party_type),
"tax_withholding_category": tax_withholding_category or "",
"party_entity_type": party_map.get(party, {}).get(party_type),
"rate": rate,
"total_amount": total_amount,
"grand_total": grand_total,
@@ -117,9 +120,14 @@ def get_result(filters, tds_docs, tds_accounts, tax_category_map, journal_entry_
"supplier_invoice_date": bill_date,
}
)
out.append(row)
out.sort(key=lambda x: x["section_code"])
key = entry.voucher_no
if key in entries:
entries[key]["tax_amount"] += tax_amount
else:
entries[key] = row
out = list(entries.values())
out.sort(key=lambda x: (x["tax_withholding_category"], x["transaction_date"], x["ref_no"]))
return out
@@ -169,9 +177,9 @@ def get_columns(filters):
pan = "pan" if frappe.db.has_column(filters.party_type, "pan") else "tax_id"
columns = [
{
"label": _("Section Code"),
"label": _("Tax Withholding Category"),
"options": "Tax Withholding Category",
"fieldname": "section_code",
"fieldname": "tax_withholding_category",
"fieldtype": "Link",
"width": 90,
},
@@ -200,7 +208,12 @@ def get_columns(filters):
columns.extend(
[
{"label": _("Entity Type"), "fieldname": "entity_type", "fieldtype": "Data", "width": 100},
{
"label": _("{0} Type").format(_(filters.get("party_type", "Party"))),
"fieldname": "party_entity_type",
"fieldtype": "Data",
"width": 100,
},
]
)
if filters.party_type == "Supplier":
@@ -435,12 +448,22 @@ def get_doc_info(vouchers, doctype, tax_category_map, net_total_map=None):
def get_tax_rate_map(filters):
rate_map = frappe.get_all(
"Tax Withholding Rate",
filters={
"from_date": ("<=", filters.get("from_date")),
"to_date": (">=", filters.get("to_date")),
},
fields=["parent", "tax_withholding_rate"],
as_list=1,
filters={"from_date": ("<=", filters.to_date), "to_date": (">=", filters.from_date)},
fields=["parent", "tax_withholding_rate", "from_date", "to_date"],
)
return frappe._dict(rate_map)
rate_list = frappe._dict()
for rate in rate_map:
rate_list.setdefault(rate.parent, []).append(frappe._dict(rate))
return rate_list
def get_tax_withholding_rates(tax_withholding, posting_date):
# returns the row that matches with the fiscal year from posting date
for rate in tax_withholding:
if getdate(rate.from_date) <= getdate(posting_date) <= getdate(rate.to_date):
return rate.tax_withholding_rate
return 0

View File

@@ -3,7 +3,7 @@
import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.utils import today
from frappe.utils import add_to_date, today
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
@@ -60,13 +60,65 @@ class TestTdsPayableMonthly(AccountsTestMixin, FrappeTestCase):
]
self.check_expected_values(result, expected_values)
def test_date_filters_in_multiple_tax_withholding_rules(self):
create_tax_category("TDS - 3", rate=10, account="TDS - _TC", cumulative_threshold=1)
# insert new rate in same fiscal year
fiscal_year = get_fiscal_year(today(), company="_Test Company")
mid_year = add_to_date(fiscal_year[1], months=6)
tds_doc = frappe.get_doc("Tax Withholding Category", "TDS - 3")
tds_doc.rates[0].to_date = mid_year
from_date = add_to_date(mid_year, days=1)
tds_doc.append(
"rates",
{
"tax_withholding_rate": 20,
"from_date": from_date,
"to_date": fiscal_year[2],
"single_threshold": 1,
"cumulative_threshold": 1,
},
)
tds_doc.save()
inv_1 = make_purchase_invoice(
rate=1000, posting_date=add_to_date(fiscal_year[1], days=1), do_not_save=True, do_not_submit=True
)
inv_1.set_posting_time = 1
inv_1.apply_tds = 1
inv_1.tax_withholding_category = tds_doc.name
inv_1.save()
inv_1.submit()
inv_2 = make_purchase_invoice(rate=1000, posting_date=from_date, do_not_save=True, do_not_submit=True)
inv_2.set_posting_time = 1
inv_2.apply_tds = 1
inv_2.tax_withholding_category = tds_doc.name
inv_2.save()
inv_2.submit()
result = execute(
frappe._dict(
company="_Test Company",
party_type="Supplier",
from_date=fiscal_year[1],
to_date=fiscal_year[2],
)
)[1]
expected_values = [
[inv_1.name, "TDS - 3", 10.0, 5000, 500, 4500],
[inv_2.name, "TDS - 3", 20.0, 5000, 1000, 4000],
]
self.check_expected_values(result, expected_values)
def check_expected_values(self, result, expected_values):
for i in range(len(result)):
voucher = frappe._dict(result[i])
voucher_expected_values = expected_values[i]
voucher_actual_values = (
voucher.ref_no,
voucher.section_code,
voucher.tax_withholding_category,
voucher.rate,
voucher.base_total,
voucher.tax_amount,

View File

@@ -79,8 +79,14 @@ frappe.require("assets/erpnext/js/financial_statements.js", function () {
options: erpnext.get_presentation_currency_list(),
},
{
fieldname: "with_period_closing_entry",
label: __("Period Closing Entry"),
fieldname: "with_period_closing_entry_for_opening",
label: __("With Period Closing Entry For Opening Balances"),
fieldtype: "Check",
default: 1,
},
{
fieldname: "with_period_closing_entry_for_current_period",
label: __("Period Closing Entry For Current Period"),
fieldtype: "Check",
default: 1,
},

View File

@@ -120,7 +120,7 @@ def get_data(filters):
max_rgt,
filters,
gl_entries_by_account,
ignore_closing_entries=not flt(filters.with_period_closing_entry),
ignore_closing_entries=not flt(filters.with_period_closing_entry_for_current_period),
ignore_opening_entries=True,
)
@@ -274,7 +274,7 @@ def get_opening_balance(
):
opening_balance = opening_balance.where(closing_balance.posting_date >= filters.year_start_date)
if not flt(filters.with_period_closing_entry):
if not flt(filters.with_period_closing_entry_for_opening):
if doctype == "Account Closing Balance":
opening_balance = opening_balance.where(closing_balance.is_period_closing_voucher_entry == 0)
else:

View File

@@ -4,15 +4,25 @@
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"filters": [
{
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"mandatory": 1,
"options": "Company",
"wildcard_filter": 0
}
],
"idx": 0,
"is_standard": "Yes",
"modified": "2019-01-17 17:20:42.374958",
"modified": "2025-08-28 19:06:54.273322",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Trial Balance (Simple)",
"owner": "Administrator",
"prepared_report": 0,
"query": "select fiscal_year as \"Fiscal Year:Data:80\",\n\tcompany as \"Company:Data:220\",\n\tposting_date as \"Posting Date:Date:100\",\n\taccount as \"Account:Data:380\",\n\tsum(debit) as \"Debit:Currency:140\",\n\tsum(credit) as \"Credit:Currency:140\",\n\tfinance_book as \"Finance Book:Link/Finance Book:140\"\nfrom `tabGL Entry`\ngroup by fiscal_year, company, posting_date, account\norder by fiscal_year, company, posting_date, account",
"query": "select fiscal_year as \"Fiscal Year:Data:80\",\n\tcompany as \"Company:Data:220\",\n\tposting_date as \"Posting Date:Date:100\",\n\taccount as \"Account:Data:380\",\n\tsum(debit) as \"Debit:Currency:140\",\n\tsum(credit) as \"Credit:Currency:140\",\n\tfinance_book as \"Finance Book:Link/Finance Book:140\"\nfrom `tabGL Entry`\nwhere is_cancelled = 0 and company = %(company)s\ngroup by fiscal_year, company, posting_date, account\norder by fiscal_year, company, posting_date, account",
"ref_doctype": "GL Entry",
"report_name": "Trial Balance (Simple)",
"report_type": "Query Report",

View File

@@ -86,7 +86,7 @@ def get_rate_as_at(date, from_currency, to_currency):
return rate
def convert_to_presentation_currency(gl_entries, currency_info):
def convert_to_presentation_currency(gl_entries, currency_info, filters=None):
"""
Take a list of GL Entries and change the 'debit' and 'credit' values to currencies
in `currency_info`.
@@ -99,6 +99,13 @@ def convert_to_presentation_currency(gl_entries, currency_info):
company_currency = currency_info["company_currency"]
account_currencies = list(set(entry["account_currency"] for entry in gl_entries))
exchange_gain_or_loss = False
if filters and isinstance(filters.get("account"), list):
account_filter = filters.get("account")
gain_loss_account = frappe.db.get_value("Company", filters.company, "exchange_gain_loss_account")
exchange_gain_or_loss = len(account_filter) == 1 and account_filter[0] == gain_loss_account
for entry in gl_entries:
debit = flt(entry["debit"])
@@ -107,7 +114,11 @@ def convert_to_presentation_currency(gl_entries, currency_info):
credit_in_account_currency = flt(entry["credit_in_account_currency"])
account_currency = entry["account_currency"]
if len(account_currencies) == 1 and account_currency == presentation_currency:
if (
len(account_currencies) == 1
and account_currency == presentation_currency
and not exchange_gain_or_loss
) and not (filters and filters.get("show_amount_in_company_currency")):
entry["debit"] = debit_in_account_currency
entry["credit"] = credit_in_account_currency
else:

View File

@@ -12,8 +12,8 @@ DEFAULT_FILTERS = {
REPORT_FILTER_TEST_CASES: list[tuple[ReportName, ReportFilters]] = [
("General Ledger", {"group_by": "Group by Voucher (Consolidated)"}),
("General Ledger", {"group_by": "Group by Voucher (Consolidated)", "include_dimensions": 1}),
("General Ledger", {"categorize_by": "Categorize by Voucher (Consolidated)"}),
("General Ledger", {"categorize_by": "Categorize by Voucher (Consolidated)", "include_dimensions": 1}),
("Accounts Payable", {"range1": 30, "range2": 60, "range3": 90, "range4": 120}),
("Accounts Receivable", {"range1": 30, "range2": 60, "range3": 90, "range4": 120}),
("Consolidated Financial Statement", {"report": "Balance Sheet"}),

View File

@@ -2,12 +2,14 @@
# License: GNU General Public License v3. See license.txt
from collections import defaultdict
from json import loads
from typing import TYPE_CHECKING, Optional
import frappe
import frappe.defaults
from frappe import _, qb, throw
from frappe.desk.reportview import build_match_conditions
from frappe.model.meta import get_field_precision
from frappe.query_builder import AliasedQuery, Criterion, Table
from frappe.query_builder.functions import Round, Sum
@@ -26,6 +28,7 @@ from frappe.utils import (
nowdate,
)
from pypika import Order
from pypika.functions import Coalesce
from pypika.terms import ExistsCriterion
import erpnext
@@ -1788,14 +1791,17 @@ def update_voucher_outstanding(voucher_type, voucher_no, account, party_type, pa
):
outstanding = voucher_outstanding[0]
ref_doc = frappe.get_doc(voucher_type, voucher_no)
outstanding_amount = flt(
outstanding["outstanding_in_account_currency"], ref_doc.precision("outstanding_amount")
)
# Didn't use db_set for optimisation purpose
ref_doc.outstanding_amount = outstanding["outstanding_in_account_currency"] or 0.0
ref_doc.outstanding_amount = outstanding_amount
frappe.db.set_value(
voucher_type,
voucher_no,
"outstanding_amount",
outstanding["outstanding_in_account_currency"] or 0.0,
outstanding_amount,
)
ref_doc.set_status(update=True)
@@ -2188,3 +2194,44 @@ def run_ledger_health_checks():
doc.general_and_payment_ledger_mismatch = True
doc.checked_on = run_date
doc.save()
def get_link_fields_grouped_by_option(doctype):
meta = frappe.get_meta(doctype)
link_fields_map = defaultdict(list)
for df in meta.fields:
if df.fieldtype == "Link" and df.options and not df.ignore_user_permissions:
link_fields_map[df.options].append(df.fieldname)
return link_fields_map
def build_qb_match_conditions(doctype, user=None) -> list:
match_filters = build_match_conditions(doctype, user, False)
link_fields_map = get_link_fields_grouped_by_option(doctype)
criterion = []
apply_strict_user_permissions = frappe.get_system_settings("apply_strict_user_permissions")
if match_filters:
_dt = qb.DocType(doctype)
for filter in match_filters:
for link_option, allowed_values in filter.items():
fieldnames = link_fields_map.get(link_option, [])
cond = None
if link_option == doctype:
cond = _dt["name"].isin(allowed_values)
for fieldname in fieldnames:
field = _dt[fieldname]
cond = field.isin(allowed_values)
if not apply_strict_user_permissions:
cond = (Coalesce(field, "") == "") | cond
if cond:
criterion.append(cond)
return criterion

View File

@@ -152,6 +152,7 @@
{
"allow_on_submit": 1,
"fetch_from": "item_code.image",
"fetch_if_empty": 1,
"fieldname": "image",
"fieldtype": "Attach Image",
"hidden": 1,
@@ -582,7 +583,7 @@
"link_fieldname": "target_asset"
}
],
"modified": "2024-01-15 17:35:49.226603",
"modified": "2025-05-20 00:44:06.229177",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset",

View File

@@ -313,6 +313,7 @@ class Asset(AccountsController):
"asset_name": self.asset_name,
"target_location": self.location,
"to_employee": self.custodian,
"company": self.company,
}
]
asset_movement = frappe.get_doc(
@@ -635,38 +636,44 @@ class Asset(AccountsController):
return add_days(self.available_for_use_date, -1)
# if it returns True, depreciation_amount will not be equal for the first and last rows
def check_is_pro_rata(self, row, wdv_or_dd_non_yearly=False):
def check_is_pro_rata(asset_doc, row, wdv_or_dd_non_yearly=False):
has_pro_rata = False
# if not existing asset, from_date = available_for_use_date
# otherwise, if number_of_depreciations_booked = 2, available_for_use_date = 01/01/2020 and frequency_of_depreciation = 12
# from_date = 01/01/2022
from_date = self.get_modified_available_for_use_date(row, wdv_or_dd_non_yearly)
days = date_diff(row.depreciation_start_date, from_date) + 1
if wdv_or_dd_non_yearly:
total_days = get_total_days(row.depreciation_start_date, 12)
if row.depreciation_method in ("Straight Line", "Manual"):
prev_depreciation_start_date = get_last_day(
add_months(
row.depreciation_start_date,
(row.frequency_of_depreciation * -1) * asset_doc.number_of_depreciations_booked,
)
)
from_date = asset_doc.available_for_use_date
days = date_diff(prev_depreciation_start_date, from_date) + 1
total_days = get_total_days(prev_depreciation_start_date, row.frequency_of_depreciation)
else:
# if frequency_of_depreciation is 12 months, total_days = 365
from_date = _get_modified_available_for_use_date(asset_doc, row, wdv_or_dd_non_yearly=False)
days = date_diff(row.depreciation_start_date, from_date) + 1
total_days = get_total_days(row.depreciation_start_date, row.frequency_of_depreciation)
if days <= 0:
frappe.throw(
_(
"""Error: This asset already has {0} depreciation periods booked.
The `depreciation start` date must be at least {1} periods after the `available for use` date.
Please correct the dates accordingly."""
).format(
asset_doc.number_of_depreciations_booked,
asset_doc.number_of_depreciations_booked,
)
)
if days < total_days:
has_pro_rata = True
return has_pro_rata
def get_modified_available_for_use_date(self, row, wdv_or_dd_non_yearly=False):
if wdv_or_dd_non_yearly:
return add_months(
self.available_for_use_date,
(self.number_of_depreciations_booked * 12),
)
else:
return add_months(
self.available_for_use_date,
(self.number_of_depreciations_booked * row.frequency_of_depreciation),
)
def validate_asset_finance_books(self, row):
if flt(row.expected_value_after_useful_life) >= flt(self.gross_purchase_amount):
frappe.throw(
@@ -1290,6 +1297,28 @@ def get_item_details(item_code, asset_category, gross_purchase_amount):
return books
def _get_modified_available_for_use_date(asset_doc, row, wdv_or_dd_non_yearly=False):
"""
if Asset has opening booked depreciations = 9,
available for use date = 17-07-2023,
depreciation start date = 30-04-2024
then from date should be 01-04-2024
"""
if asset_doc.number_of_depreciations_booked > 0:
from_date = add_months(
asset_doc.available_for_use_date,
(asset_doc.number_of_depreciations_booked * row.frequency_of_depreciation) - 1,
)
if is_last_day_of_the_month(row.depreciation_start_date):
return add_days(get_last_day(from_date), 1)
# get from date when depreciation start date is not last day of the month
months_difference = month_diff(row.depreciation_start_date, from_date) - 1
return add_days(add_months(row.depreciation_start_date, -1 * months_difference), 1)
else:
return asset_doc.available_for_use_date
def get_asset_account(account_name, asset=None, asset_category=None, company=None):
account = None
if asset:
@@ -1503,11 +1532,7 @@ def get_straight_line_or_manual_depr_amount(asset, row, schedule_idx, number_of_
# if the Depreciation Schedule is being prepared for the first time
else:
if row.daily_prorata_based:
amount = (
flt(asset.gross_purchase_amount)
- flt(asset.opening_accumulated_depreciation)
- flt(row.expected_value_after_useful_life)
)
amount = flt(asset.gross_purchase_amount) - flt(row.expected_value_after_useful_life)
total_days = (
date_diff(
get_last_day(
@@ -1519,7 +1544,11 @@ def get_straight_line_or_manual_depr_amount(asset, row, schedule_idx, number_of_
),
add_days(
get_last_day(
add_months(row.depreciation_start_date, -1 * row.frequency_of_depreciation)
add_months(
row.depreciation_start_date,
(row.frequency_of_depreciation * (asset.number_of_depreciations_booked + 1))
* -1,
),
),
1,
),
@@ -1542,11 +1571,9 @@ def get_straight_line_or_manual_depr_amount(asset, row, schedule_idx, number_of_
return daily_depr_amount * (date_diff(to_date, from_date) + 1)
else:
return (
flt(asset.gross_purchase_amount)
- flt(asset.opening_accumulated_depreciation)
- flt(row.expected_value_after_useful_life)
) / flt(row.total_number_of_depreciations - asset.number_of_depreciations_booked)
return (flt(asset.gross_purchase_amount) - flt(row.expected_value_after_useful_life)) / flt(
row.total_number_of_depreciations
)
def get_shift_depr_amount(asset, row, schedule_idx):

View File

@@ -660,7 +660,7 @@ class TestDepreciationMethods(AssetSetup):
available_for_use_date="2030-06-06",
is_existing_asset=1,
number_of_depreciations_booked=2,
opening_accumulated_depreciation=47095.89,
opening_accumulated_depreciation=47178.08,
expected_value_after_useful_life=10000,
depreciation_start_date="2032-12-31",
total_number_of_depreciations=3,
@@ -668,7 +668,7 @@ class TestDepreciationMethods(AssetSetup):
)
self.assertEqual(asset.status, "Draft")
expected_schedules = [["2032-12-31", 42904.11, 90000.0]]
expected_schedules = [["2032-12-31", 30000.0, 77178.08], ["2033-06-06", 12821.92, 90000.0]]
schedules = [
[cstr(d.schedule_date), flt(d.depreciation_amount, 2), d.accumulated_depreciation_amount]
for d in asset.get("schedules")
@@ -1451,7 +1451,7 @@ class TestDepreciationBasics(AssetSetup):
affects `value_after_depreciation`
"""
asset = create_asset(calculate_depreciation=1)
asset = create_asset(calculate_depreciation=1, depreciation_start_date="2021-12-31")
asset.opening_accumulated_depreciation = 2000
asset.number_of_depreciations_booked = 1

View File

@@ -380,7 +380,7 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s
args: {
item_code: item.item_code,
warehouse: cstr(item.warehouse),
qty: flt(item.stock_qty),
qty: -1 * flt(item.stock_qty),
serial_no: item.serial_no,
posting_date: me.frm.doc.posting_date,
posting_time: me.frm.doc.posting_time,

View File

@@ -69,6 +69,7 @@ class AssetCapitalization(StockController):
def on_submit(self):
self.update_stock_ledger()
self.make_gl_entries()
self.repost_future_sle_and_gle()
self.update_target_asset()
def on_cancel(self):
@@ -82,6 +83,7 @@ class AssetCapitalization(StockController):
self.cancel_target_asset()
self.update_stock_ledger()
self.make_gl_entries()
self.repost_future_sle_and_gle()
self.restore_consumed_asset_items()
def on_trash(self):

View File

@@ -76,6 +76,7 @@
"fieldname": "completion_date",
"fieldtype": "Datetime",
"label": "Completion Date",
"mandatory_depends_on": "eval:doc.repair_status==\"Completed\"",
"no_copy": 1
},
{
@@ -264,7 +265,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2022-08-16 15:55:25.023471",
"modified": "2025-07-29 15:14:34.044564",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset Repair",
@@ -302,10 +303,11 @@
"write": 1
}
],
"row_format": "Dynamic",
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"title_field": "asset_name",
"track_changes": 1,
"track_seen": 1
}
}

View File

@@ -3,7 +3,7 @@
import frappe
from frappe import _
from frappe.utils import add_months, cint, flt, getdate, time_diff_in_hours
from frappe.utils import add_months, cint, flt, get_link_to_form, getdate, time_diff_in_hours
import erpnext
from erpnext.accounts.general_ledger import make_gl_entries
@@ -20,6 +20,20 @@ class AssetRepair(AccountsController):
self.set_stock_items_cost()
self.calculate_total_repair_cost()
def validate_asset(self):
if self.asset_doc.status in ("Sold", "Fully Depreciated", "Scrapped"):
frappe.throw(
_("Asset {0} is in {1} status and cannot be repaired.").format(
get_link_to_form("Asset", self.asset), self.asset_doc.status
)
)
def validate_dates(self):
if self.completion_date and (getdate(self.failure_date) > getdate(self.completion_date)):
frappe.throw(
_("Completion Date can not be before Failure Date. Please adjust the dates accordingly.")
)
def update_status(self):
if self.repair_status == "Pending":
frappe.db.set_value("Asset", self.asset, "status", "Out of Order")
@@ -195,7 +209,7 @@ class AssetRepair(AccountsController):
"voucher_type": self.doctype,
"voucher_no": self.name,
"cost_center": self.cost_center,
"posting_date": getdate(),
"posting_date": self.completion_date,
"against_voucher_type": "Purchase Invoice",
"against_voucher": self.purchase_invoice,
"company": self.company,
@@ -214,7 +228,7 @@ class AssetRepair(AccountsController):
"voucher_type": self.doctype,
"voucher_no": self.name,
"cost_center": self.cost_center,
"posting_date": getdate(),
"posting_date": self.completion_date,
"company": self.company,
},
item=self,
@@ -248,7 +262,7 @@ class AssetRepair(AccountsController):
"voucher_type": self.doctype,
"voucher_no": self.name,
"cost_center": self.cost_center,
"posting_date": getdate(),
"posting_date": self.completion_date,
"company": self.company,
},
item=self,
@@ -265,7 +279,7 @@ class AssetRepair(AccountsController):
"voucher_type": self.doctype,
"voucher_no": self.name,
"cost_center": self.cost_center,
"posting_date": getdate(),
"posting_date": self.completion_date,
"against_voucher_type": "Stock Entry",
"against_voucher": self.stock_entry,
"company": self.company,

View File

@@ -4,7 +4,7 @@
import unittest
import frappe
from frappe.utils import flt, nowdate
from frappe.utils import add_days, flt, nowdate
from erpnext.assets.doctype.asset.asset import (
get_asset_account,
@@ -288,6 +288,7 @@ def create_asset_repair(**args):
if args.submit:
asset_repair.repair_status = "Completed"
asset_repair.completion_date = add_days(args.failure_date, 1)
asset_repair.cost_center = frappe.db.get_value("Company", asset.company, "cost_center")
if args.stock_consumption:

View File

@@ -456,9 +456,8 @@ class PurchaseOrder(BuyingController):
if not self.is_against_so():
return
for item in removed_items:
prev_ordered_qty = (
prev_ordered_qty = flt(
frappe.get_cached_value("Sales Order Item", item.get("sales_order_item"), "ordered_qty")
or 0.0
)
frappe.db.set_value(

View File

@@ -526,12 +526,8 @@ class TestPurchaseOrder(FrappeTestCase):
self.assertRaises(frappe.ValidationError, pr.submit)
self.assertRaises(frappe.ValidationError, pi.submit)
@change_settings("Accounts Settings", {"automatically_fetch_payment_terms": 1})
def test_make_purchase_invoice_with_terms(self):
from erpnext.selling.doctype.sales_order.test_sales_order import (
automatically_fetch_payment_terms,
)
automatically_fetch_payment_terms()
po = create_purchase_order(do_not_save=True)
self.assertRaises(frappe.ValidationError, make_pi_from_po, po.name)
@@ -555,7 +551,6 @@ class TestPurchaseOrder(FrappeTestCase):
self.assertEqual(getdate(pi.payment_schedule[0].due_date), getdate(po.transaction_date))
self.assertEqual(pi.payment_schedule[1].payment_amount, 2500.0)
self.assertEqual(getdate(pi.payment_schedule[1].due_date), add_days(getdate(po.transaction_date), 30))
automatically_fetch_payment_terms(enable=0)
def test_warehouse_company_validation(self):
from erpnext.stock.utils import InvalidWarehouseCompany
@@ -703,6 +698,7 @@ class TestPurchaseOrder(FrappeTestCase):
)
self.assertEqual(due_date, "2023-03-31")
@change_settings("Accounts Settings", {"automatically_fetch_payment_terms": 0})
def test_terms_are_not_copied_if_automatically_fetch_payment_terms_is_unchecked(self):
po = create_purchase_order(do_not_save=1)
po.payment_terms_template = "_Test Payment Term Template"
@@ -834,18 +830,16 @@ class TestPurchaseOrder(FrappeTestCase):
bo.load_from_db()
self.assertEqual(bo.items[0].ordered_qty, 5)
@change_settings("Accounts Settings", {"automatically_fetch_payment_terms": 1})
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,
)
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
from erpnext.selling.doctype.sales_order.test_sales_order import (
automatically_fetch_payment_terms,
compare_payment_schedules,
)
automatically_fetch_payment_terms()
po = create_purchase_order(qty=10, rate=100, do_not_save=1)
create_payment_terms_template()
po.payment_terms_template = "Test Receivable Template"
@@ -859,8 +853,6 @@ class TestPurchaseOrder(FrappeTestCase):
# self.assertEqual(po.payment_terms_template, pi.payment_terms_template)
compare_payment_schedules(self, po, pi)
automatically_fetch_payment_terms(enable=0)
def test_internal_transfer_flow(self):
from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
make_inter_company_purchase_invoice,

View File

@@ -9,6 +9,7 @@ from frappe import _
from frappe.core.doctype.communication.email import make
from frappe.desk.form.load import get_attachments
from frappe.model.mapper import get_mapped_doc
from frappe.query_builder import Order
from frappe.utils import get_url
from frappe.utils.print_format import download_pdf
from frappe.utils.user import get_user_fullname
@@ -502,35 +503,32 @@ def get_supplier_tag():
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_rfq_containing_supplier(doctype, txt, searchfield, start, page_len, filters):
conditions = ""
if txt:
conditions += "and rfq.name like '%%" + txt + "%%' "
rfq = frappe.qb.DocType("Request for Quotation")
rfq_supplier = frappe.qb.DocType("Request for Quotation Supplier")
if filters.get("transaction_date"):
conditions += "and rfq.transaction_date = '{}'".format(filters.get("transaction_date"))
rfq_data = frappe.db.sql(
f"""
select
distinct rfq.name, rfq.transaction_date,
rfq.company
from
`tabRequest for Quotation` rfq, `tabRequest for Quotation Supplier` rfq_supplier
where
rfq.name = rfq_supplier.parent
and rfq_supplier.supplier = %(supplier)s
and rfq.docstatus = 1
and rfq.company = %(company)s
{conditions}
order by rfq.transaction_date ASC
limit %(page_len)s offset %(start)s """,
{
"page_len": page_len,
"start": start,
"company": filters.get("company"),
"supplier": filters.get("supplier"),
},
as_dict=1,
query = (
frappe.qb.from_(rfq)
.from_(rfq_supplier)
.select(rfq.name)
.distinct()
.select(rfq.transaction_date, rfq.company)
.where(
(rfq.name == rfq_supplier.parent)
& (rfq_supplier.supplier == filters.get("supplier"))
& (rfq.docstatus == 1)
& (rfq.company == filters.get("company"))
)
.orderby(rfq.transaction_date, order=Order.asc)
.limit(page_len)
.offset(start)
)
if txt:
query = query.where(rfq.name.like(f"%%{txt}%%"))
if filters.get("transaction_date"):
query = query.where(rfq.transaction_date == filters.get("transaction_date"))
rfq_data = query.run(as_dict=1)
return rfq_data

View File

@@ -42,6 +42,11 @@ frappe.ui.form.on("Supplier", {
},
};
});
frm.make_methods = {
"Bank Account": () => erpnext.utils.make_bank_account(frm.doc.doctype, frm.doc.name),
"Pricing Rule": () => erpnext.utils.make_pricing_rule(frm.doc.doctype, frm.doc.name),
};
},
refresh: function (frm) {
@@ -84,21 +89,9 @@ frappe.ui.form.on("Supplier", {
__("View")
);
frm.add_custom_button(
__("Bank Account"),
function () {
erpnext.utils.make_bank_account(frm.doc.doctype, frm.doc.name);
},
__("Create")
);
frm.add_custom_button(__("Bank Account"), () => frm.make_methods["Bank Account"](), __("Create"));
frm.add_custom_button(
__("Pricing Rule"),
function () {
erpnext.utils.make_pricing_rule(frm.doc.doctype, frm.doc.name);
},
__("Create")
);
frm.add_custom_button(__("Pricing Rule"), () => frm.make_methods["Pricing Rule"](), __("Create"));
frm.add_custom_button(
__("Get Supplier Group Details"),
@@ -108,7 +101,10 @@ frappe.ui.form.on("Supplier", {
__("Actions")
);
if (cint(frappe.defaults.get_default("enable_common_party_accounting"))) {
if (
cint(frappe.defaults.get_default("enable_common_party_accounting")) &&
frappe.model.can_create("Party Link")
) {
frm.add_custom_button(
__("Link with Customer"),
function () {

View File

@@ -94,9 +94,6 @@
</script>
</head>
<div style="margin-bottom: 7px;" class="text-center">
{%= frappe.boot.letter_heads[frappe.defaults.get_default("letter_head")] %}
</div>
<h2 class="text-center">{%= __(report.report_name) %}</h2>
<h4 class="text-center">{%= filters.item %} </h4>
@@ -124,9 +121,7 @@
</tbody>
</table>
<h4 class="text-center"> Analysis Chart </h4>
<h4 class="text-center">{%= __("Analysis Chart") %}</h4>
<div id="chart_div"></div>
<p class="text-right text-muted">Printed On {%= frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string()) %}</p>
<p class="text-right text-muted">{%= __("Printed on {0}", [frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string())]) %}</p>

View File

@@ -76,14 +76,14 @@ frappe.query_reports["Supplier Quotation Comparison"] = {
},
},
{
fieldname: "group_by",
label: __("Group by"),
fieldname: "categorize_by",
label: __("Categorize by"),
fieldtype: "Select",
options: [
{ label: __("Group by Supplier"), value: "Group by Supplier" },
{ label: __("Group by Item"), value: "Group by Item" },
{ label: __("Categorize by Supplier"), value: "Categorize by Supplier" },
{ label: __("Categorize by Item"), value: "Categorize by Item" },
],
default: __("Group by Supplier"),
default: __("Categorize by Supplier"),
},
{
fieldtype: "Check",

View File

@@ -15,6 +15,8 @@ def execute(filters=None):
if not filters:
return [], []
validate_filters(filters)
columns = get_columns(filters)
supplier_quotation_data = get_data(filters)
@@ -24,6 +26,12 @@ def execute(filters=None):
return columns, data, message, chart_data
def validate_filters(filters):
if not filters.get("categorize_by") and filters.get("group_by"):
filters["categorize_by"] = filters["group_by"]
filters["categorize_by"] = filters["categorize_by"].replace("Group by", "Categorize by")
def get_data(filters):
sq = frappe.qb.DocType("Supplier Quotation")
sq_item = frappe.qb.DocType("Supplier Quotation Item")
@@ -82,7 +90,9 @@ def prepare_data(supplier_quotation_data, filters):
group_wise_map = defaultdict(list)
supplier_qty_price_map = {}
group_by_field = "supplier_name" if filters.get("group_by") == "Group by Supplier" else "item_code"
group_by_field = (
"supplier_name" if filters.get("categorize_by") == "Categorize by Supplier" else "item_code"
)
company_currency = frappe.db.get_default("currency")
float_precision = cint(frappe.db.get_default("float_precision")) or 2
@@ -274,7 +284,7 @@ def get_columns(filters):
},
]
if filters.get("group_by") == "Group by Item":
if filters.get("categorize_by") == "Categorize by Item":
group_by_columns.reverse()
columns[0:0] = group_by_columns # add positioned group by columns to the report

View File

@@ -20,6 +20,9 @@ def update_last_purchase_rate(doc, is_submit) -> None:
this_purchase_date = getdate(doc.get("posting_date") or doc.get("transaction_date"))
for d in doc.get("items"):
if d.get("is_free_item"):
continue
# get last purchase details
last_purchase_details = get_last_purchase_details(d.item_code, doc.name)

View File

@@ -0,0 +1,11 @@
# End of Life reached
With the release of ERPNext version 16, version 14 has reached its end of life.
This means that version 14 of ERPNext (which is running on this site) will no longer receive critical bug fixes and is no longer covered under Frappe Support.
We highly recommend that you update to version 16 to get the latest bug fixes, features and other improvements.
[Click here to know more about version 16](https://frappe.io/erpnext/version-16)
If you're on [Frappe Cloud](https://frappe.io/cloud), [click here to learn how to update to v16](https://docs.frappe.io/cloud/sites/version-upgrade)

View File

@@ -153,6 +153,48 @@ class AccountsController(TransactionBase):
raise_exception=1,
)
def validate_against_voucher_outstanding(self):
from frappe.model.meta import get_meta
if not get_meta(self.doctype).has_field("outstanding_amount"):
return
if self.get("is_return") and self.return_against and not self.get("is_pos"):
against_voucher_outstanding = frappe.get_value(
self.doctype, self.return_against, "outstanding_amount"
)
document_type = "Credit Note" if self.doctype == "Sales Invoice" else "Debit Note"
msg = ""
if self.get("update_outstanding_for_self"):
msg = (
"We can see {0} is made against {1}. If you want {1}'s outstanding to be updated, "
"uncheck '{2}' checkbox. <br><br>Or"
).format(
frappe.bold(document_type),
get_link_to_form(self.doctype, self.get("return_against")),
frappe.bold(_("Update Outstanding for Self")),
)
elif not self.update_outstanding_for_self and (
abs(flt(self.rounded_total) or flt(self.grand_total)) > flt(against_voucher_outstanding)
):
self.update_outstanding_for_self = 1
msg = (
"The outstanding amount {} in {} is lesser than {}. Updating the outstanding to this invoice. <br><br>And"
).format(
against_voucher_outstanding,
get_link_to_form(self.doctype, self.get("return_against")),
flt(abs(self.outstanding_amount)),
)
if msg:
msg += " you can use {} tool to reconcile against {} later.".format(
get_link_to_form("Payment Reconciliation", "Payment Reconciliation"),
get_link_to_form(self.doctype, self.get("return_against")),
)
frappe.msgprint(_(msg))
def validate(self):
if not self.get("is_return") and not self.get("is_debit_note"):
self.validate_qty_is_not_zero()
@@ -171,6 +213,11 @@ class AccountsController(TransactionBase):
self.validate_date_with_fiscal_year()
self.validate_party_accounts()
if self.doctype in ["Sales Invoice", "Purchase Invoice"]:
if self.is_return:
self.validate_qty()
else:
self.validate_deferred_start_and_end_date()
self.validate_inter_company_reference()
@@ -178,6 +225,7 @@ class AccountsController(TransactionBase):
self.disable_tax_included_prices_for_internal_transfer()
self.set_incoming_rate()
self.init_internal_values()
self.validate_against_voucher_outstanding()
if self.meta.get_field("currency"):
self.calculate_taxes_and_totals()
@@ -209,31 +257,12 @@ class AccountsController(TransactionBase):
)
)
if self.get("is_return") and self.get("return_against") and not self.get("is_pos"):
if self.get("update_outstanding_for_self"):
document_type = "Credit Note" if self.doctype == "Sales Invoice" else "Debit Note"
frappe.msgprint(
_(
"We can see {0} is made against {1}. If you want {1}'s outstanding to be updated, uncheck '{2}' checkbox. <br><br> Or you can use {3} tool to reconcile against {1} later."
).format(
frappe.bold(document_type),
get_link_to_form(self.doctype, self.get("return_against")),
frappe.bold("Update Outstanding for Self"),
get_link_to_form("Payment Reconciliation", "Payment Reconciliation"),
)
)
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()
self.set_advance_gain_or_loss()
if self.is_return:
self.validate_qty()
else:
self.validate_deferred_start_and_end_date()
self.validate_deferred_income_expense_account()
self.set_inter_company_account()
@@ -655,7 +684,9 @@ class AccountsController(TransactionBase):
"Customer",
self.customer,
self.company,
None,
self.payment_terms_template,
self.doctype,
)
elif self.doctype == "Purchase Invoice":
validate_due_date(
@@ -666,6 +697,7 @@ class AccountsController(TransactionBase):
self.company,
self.bill_date,
self.payment_terms_template,
self.doctype,
)
def set_price_list_currency(self, buying_or_selling):
@@ -1710,69 +1742,50 @@ class AccountsController(TransactionBase):
def validate_multiple_billing(self, ref_dt, item_ref_dn, based_on):
from erpnext.controllers.status_updater import get_allowance_for
item_allowance = {}
global_qty_allowance, global_amount_allowance = None, None
ref_wise_billed_amount = self.get_reference_wise_billed_amt(ref_dt, item_ref_dn, based_on)
role_allowed_to_over_bill = frappe.get_cached_value(
"Accounts Settings", None, "role_allowed_to_over_bill"
)
user_roles = frappe.get_roles()
if not ref_wise_billed_amount:
return
total_overbilled_amt = 0.0
overbilled_items = []
precision = self.precision(based_on, "items")
precision_allowance = 1 / (10**precision)
reference_names = [d.get(item_ref_dn) for d in self.get("items") if d.get(item_ref_dn)]
reference_details = self.get_billing_reference_details(reference_names, ref_dt + " Item", based_on)
role_allowed_to_overbill = frappe.get_cached_value(
"Accounts Settings", None, "role_allowed_to_over_bill"
)
is_overbilling_allowed = role_allowed_to_overbill in frappe.get_roles()
for item in self.get("items"):
if not item.get(item_ref_dn):
continue
for row in ref_wise_billed_amount.values():
total_billed_amt = row.billed_amt
allowance = get_allowance_for(row.item_code, {}, None, None, "amount")[0]
ref_amt = flt(reference_details.get(item.get(item_ref_dn)), self.precision(based_on, item))
based_on_amt = flt(item.get(based_on))
if not ref_amt:
if based_on_amt: # Skip warning for free items
frappe.msgprint(
_(
"System will not check over billing since amount for Item {0} in {1} is zero"
).format(item.item_code, ref_dt),
title=_("Warning"),
indicator="orange",
)
continue
already_billed = self.get_billed_amount_for_item(item, item_ref_dn, based_on)
total_billed_amt = flt(flt(already_billed) + based_on_amt, self.precision(based_on, item))
allowance, item_allowance, global_qty_allowance, global_amount_allowance = get_allowance_for(
item.item_code, item_allowance, global_qty_allowance, global_amount_allowance, "amount"
)
max_allowed_amt = flt(ref_amt * (100 + allowance) / 100)
max_allowed_amt = flt(row.ref_amt * (100 + allowance) / 100)
if total_billed_amt < 0 and max_allowed_amt < 0:
# while making debit note against purchase return entry(purchase receipt) getting overbill error
total_billed_amt = abs(total_billed_amt)
max_allowed_amt = abs(max_allowed_amt)
total_billed_amt, max_allowed_amt = abs(total_billed_amt), abs(max_allowed_amt)
overbill_amt = total_billed_amt - max_allowed_amt
row["max_allowed_amt"] = max_allowed_amt
total_overbilled_amt += overbill_amt
if overbill_amt > 0.01 and role_allowed_to_over_bill not in user_roles:
if self.doctype != "Purchase Invoice":
self.throw_overbill_exception(item, max_allowed_amt)
elif not cint(
if overbill_amt > precision_allowance and not is_overbilling_allowed:
if self.doctype != "Purchase Invoice" or not cint(
frappe.db.get_single_value(
"Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice"
)
):
self.throw_overbill_exception(item, max_allowed_amt)
overbilled_items.append(row)
if role_allowed_to_over_bill in user_roles and total_overbilled_amt > 0.1:
if overbilled_items:
self.throw_overbill_exception(overbilled_items, precision)
if is_overbilling_allowed and total_overbilled_amt > 0.1:
frappe.msgprint(
_("Overbilling of {} ignored because you have {} role.").format(
total_overbilled_amt, role_allowed_to_over_bill
total_overbilled_amt, role_allowed_to_overbill
),
indicator="orange",
alert=True,
@@ -1788,55 +1801,88 @@ class AccountsController(TransactionBase):
)
)
def get_billed_amount_for_item(self, item, item_ref_dn, based_on):
def get_reference_wise_billed_amt(self, ref_dt, item_ref_dn, based_on):
"""
Returns Sum of Amount of
Sales/Purchase Invoice Items
that are linked to `item_ref_dn` (`dn_detail` / `pr_detail`)
that are submitted OR not submitted but are under current invoice
"""
reference_names = [d.get(item_ref_dn) for d in self.items if d.get(item_ref_dn)]
from frappe.query_builder import Criterion
from frappe.query_builder.functions import Sum
if not reference_names:
return
item_doctype = frappe.qb.DocType(item.doctype)
ref_wise_billed_amount = {}
precision = self.precision(based_on, "items")
reference_details = self.get_billing_reference_details(reference_names, ref_dt + " Item", based_on)
already_billed = self.get_already_billed_amount(reference_names, item_ref_dn, based_on)
for item in self.items:
key = item.get(item_ref_dn)
if not key:
continue
ref_amt = flt(reference_details.get(key), precision)
current_amount = flt(item.get(based_on), precision)
if not ref_amt:
if current_amount: # Skip warning for free items
frappe.msgprint(
_(
"System will not check over billing since amount for Item {0} in {1} is zero"
).format(item.item_code, ref_dt),
title=_("Warning"),
indicator="orange",
)
continue
ref_wise_billed_amount.setdefault(
key,
frappe._dict(item_code=item.item_code, billed_amt=0.0, ref_amt=ref_amt, rows=[]),
)
ref_wise_billed_amount[key]["rows"].append(item.idx)
ref_wise_billed_amount[key]["ref_amt"] = ref_amt
ref_wise_billed_amount[key]["billed_amt"] += current_amount
if key in already_billed:
ref_wise_billed_amount[key]["billed_amt"] += flt(already_billed.pop(key, 0), precision)
return ref_wise_billed_amount
def get_already_billed_amount(self, reference_names, item_ref_dn, based_on):
item_doctype = frappe.qb.DocType(self.items[0].doctype)
based_on_field = frappe.qb.Field(based_on)
join_field = frappe.qb.Field(item_ref_dn)
result = (
frappe.qb.from_(item_doctype)
.select(Sum(based_on_field))
.where(join_field == item.get(item_ref_dn))
.where(
Criterion.any(
[ # select all items from other invoices OR current invoices
Criterion.all(
[ # for selecting items from other invoices
item_doctype.docstatus == 1,
item_doctype.parent != self.name,
]
),
Criterion.all(
[ # for selecting items from current invoice, that are linked to same reference
item_doctype.docstatus == 0,
item_doctype.parent == self.name,
item_doctype.name != item.name,
]
),
]
)
)
).run()
return result[0][0] if result else 0
def throw_overbill_exception(self, item, max_allowed_amt):
frappe.throw(
_(
"Cannot overbill for Item {0} in row {1} more than {2}. To allow over-billing, please set allowance in Accounts Settings"
).format(item.item_code, item.idx, max_allowed_amt)
return frappe._dict(
(
frappe.qb.from_(item_doctype)
.select(join_field, Sum(based_on_field))
.where(join_field.isin(reference_names))
.where((item_doctype.docstatus == 1) & (item_doctype.parent != self.name))
.groupby(join_field)
).run()
)
def throw_overbill_exception(self, overbilled_items, precision):
message = (
_("<p>Cannot overbill for the following Items:</p>")
+ "<ul>"
+ "".join(
_("<li>Item {0} in row(s) {1} billed more than {2}</li>").format(
frappe.bold(item.item_code),
", ".join(str(x) for x in item.rows),
frappe.bold(fmt_money(item.max_allowed_amt, precision=precision, currency=self.currency)),
)
for item in overbilled_items
)
+ "</ul>"
)
message += _("<p>To allow over-billing, please set allowance in Accounts Settings.</p>")
frappe.throw(_(message))
def get_company_default(self, fieldname, ignore_validation=False):
from erpnext.accounts.utils import get_company_default
@@ -2100,7 +2146,9 @@ class AccountsController(TransactionBase):
and automatically_fetch_payment_terms
and self.linked_order_has_payment_terms(po_or_so, fieldname, doctype)
):
self.fetch_payment_terms_from_order(po_or_so, doctype)
self.fetch_payment_terms_from_order(
po_or_so, doctype, grand_total, base_grand_total, automatically_fetch_payment_terms
)
if self.get("payment_terms_template"):
self.ignore_default_payment_terms_template = 1
elif self.get("payment_terms_template"):
@@ -2141,7 +2189,9 @@ class AccountsController(TransactionBase):
d.payment_amount * self.get("conversion_rate"), d.precision("base_payment_amount")
)
else:
self.fetch_payment_terms_from_order(po_or_so, doctype)
self.fetch_payment_terms_from_order(
po_or_so, doctype, grand_total, base_grand_total, automatically_fetch_payment_terms
)
self.ignore_default_payment_terms_template = 1
def get_order_details(self):
@@ -2179,7 +2229,9 @@ class AccountsController(TransactionBase):
def linked_order_has_payment_schedule(self, po_or_so):
return frappe.get_all("Payment Schedule", filters={"parent": po_or_so})
def fetch_payment_terms_from_order(self, po_or_so, po_or_so_doctype):
def fetch_payment_terms_from_order(
self, po_or_so, po_or_so_doctype, grand_total, base_grand_total, automatically_fetch_payment_terms
):
"""
Fetch Payment Terms from Purchase/Sales Order on creating a new Purchase/Sales Invoice.
"""
@@ -2187,6 +2239,7 @@ class AccountsController(TransactionBase):
self.payment_schedule = []
self.payment_terms_template = po_or_so.payment_terms_template
posting_date = self.get("bill_date") or self.get("posting_date") or self.get("transaction_date")
for schedule in po_or_so.payment_schedule:
payment_schedule = {
@@ -2195,12 +2248,36 @@ class AccountsController(TransactionBase):
"invoice_portion": schedule.invoice_portion,
"mode_of_payment": schedule.mode_of_payment,
"description": schedule.description,
"payment_amount": schedule.payment_amount,
"base_payment_amount": schedule.base_payment_amount,
"outstanding": schedule.outstanding,
"paid_amount": schedule.paid_amount,
}
if automatically_fetch_payment_terms:
if schedule.due_date_based_on:
payment_schedule["due_date"] = get_due_date(schedule, posting_date)
payment_schedule["due_date_based_on"] = schedule.due_date_based_on
payment_schedule["credit_days"] = cint(schedule.credit_days)
payment_schedule["credit_months"] = cint(schedule.credit_months)
if schedule.discount_validity_based_on:
payment_schedule["discount_date"] = get_discount_date(schedule, posting_date)
payment_schedule["discount_validity_based_on"] = schedule.discount_validity_based_on
payment_schedule["discount_validity"] = cint(schedule.discount_validity)
payment_schedule["payment_amount"] = flt(
grand_total * flt(payment_schedule["invoice_portion"]) / 100,
schedule.precision("payment_amount"),
)
payment_schedule["base_payment_amount"] = flt(
base_grand_total * flt(payment_schedule["invoice_portion"]) / 100,
schedule.precision("base_payment_amount"),
)
payment_schedule["outstanding"] = payment_schedule["payment_amount"]
else:
payment_schedule["base_payment_amount"] = flt(
schedule.base_payment_amount * self.get("conversion_rate"),
schedule.precision("base_payment_amount"),
)
if schedule.discount_type == "Percentage":
payment_schedule["discount_type"] = schedule.discount_type
payment_schedule["discount"] = schedule.discount
@@ -2459,12 +2536,17 @@ class AccountsController(TransactionBase):
default_currency = erpnext.get_company_currency(self.company)
if not default_currency:
throw(_("Please enter default currency in Company Master"))
if (
(self.currency == default_currency and flt(self.conversion_rate) != 1.00)
or not self.conversion_rate
or (self.currency != default_currency and flt(self.conversion_rate) == 1.00)
):
throw(_("Conversion rate cannot be 0 or 1"))
if not self.conversion_rate:
throw(_("Conversion rate cannot be 0"))
if self.currency == default_currency and flt(self.conversion_rate) != 1.00:
throw(_("Conversion rate must be 1.00 if document currency is same as company currency"))
if self.currency != default_currency and flt(self.conversion_rate) == 1.00:
frappe.msgprint(
_("Conversion rate is 1.00, but document currency is different from company currency")
)
def check_finance_books(self, item, asset):
if (
@@ -2668,9 +2750,7 @@ def set_balance_in_account_currency(
_("Account: {0} with currency: {1} can not be selected").format(gl_dict.account, account_currency)
)
gl_dict["account_currency"] = (
company_currency if account_currency == company_currency else account_currency
)
gl_dict["account_currency"] = account_currency
# set debit/credit in account currency if not provided
if flt(gl_dict.debit) and not flt(gl_dict.debit_in_account_currency):
@@ -2917,14 +2997,26 @@ def get_payment_term_details(
term = frappe.get_doc("Payment Term", term)
else:
term_details.payment_term = term.payment_term
term_details.description = term.description
term_details.invoice_portion = term.invoice_portion
fields_to_copy = [
"description",
"invoice_portion",
"discount_type",
"discount",
"mode_of_payment",
"due_date_based_on",
"credit_days",
"credit_months",
"discount_validity_based_on",
"discount_validity",
]
for field in fields_to_copy:
term_details[field] = term.get(field)
term_details.payment_amount = flt(term.invoice_portion) * flt(grand_total) / 100
term_details.base_payment_amount = flt(term.invoice_portion) * flt(base_grand_total) / 100
term_details.discount_type = term.discount_type
term_details.discount = term.discount
term_details.outstanding = term_details.payment_amount
term_details.mode_of_payment = term.mode_of_payment
if bill_date:
term_details.due_date = get_due_date(term, bill_date)
@@ -2943,11 +3035,11 @@ def get_due_date(term, posting_date=None, bill_date=None):
due_date = None
date = bill_date or posting_date
if term.due_date_based_on == "Day(s) after invoice date":
due_date = add_days(date, term.credit_days)
due_date = add_days(date, cint(term.credit_days))
elif term.due_date_based_on == "Day(s) after the end of the invoice month":
due_date = add_days(get_last_day(date), term.credit_days)
due_date = add_days(get_last_day(date), cint(term.credit_days))
elif term.due_date_based_on == "Month(s) after the end of the invoice month":
due_date = get_last_day(add_months(date, term.credit_months))
due_date = get_last_day(add_months(date, cint(term.credit_months)))
return due_date
@@ -2955,11 +3047,11 @@ def get_discount_date(term, posting_date=None, bill_date=None):
discount_validity = None
date = bill_date or posting_date
if term.discount_validity_based_on == "Day(s) after invoice date":
discount_validity = add_days(date, term.discount_validity)
discount_validity = add_days(date, cint(term.discount_validity))
elif term.discount_validity_based_on == "Day(s) after the end of the invoice month":
discount_validity = add_days(get_last_day(date), term.discount_validity)
discount_validity = add_days(get_last_day(date), cint(term.discount_validity))
elif term.discount_validity_based_on == "Month(s) after the end of the invoice month":
discount_validity = get_last_day(add_months(date, term.discount_validity))
discount_validity = get_last_day(add_months(date, cint(term.discount_validity)))
return discount_validity

View File

@@ -6,7 +6,7 @@ import json
from collections import defaultdict
import frappe
from frappe import qb, scrub
from frappe import cint, qb, scrub
from frappe.desk.reportview import get_filters_cond, get_match_cond
from frappe.query_builder import Criterion, CustomFunction
from frappe.query_builder.functions import Locate
@@ -528,21 +528,27 @@ def get_account_list(doctype, txt, searchfield, start, page_len, filters):
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_blanket_orders(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.sql(
"""select distinct bo.name, bo.blanket_order_type, bo.to_date
from `tabBlanket Order` bo, `tabBlanket Order Item` boi
where
boi.parent = bo.name
and boi.item_code = {item_code}
and bo.blanket_order_type = '{blanket_order_type}'
and bo.company = {company}
and bo.docstatus = 1""".format(
item_code=frappe.db.escape(filters.get("item")),
blanket_order_type=filters.get("blanket_order_type"),
company=frappe.db.escape(filters.get("company")),
bo = frappe.qb.DocType("Blanket Order")
bo_item = frappe.qb.DocType("Blanket Order Item")
blanket_orders = (
frappe.qb.from_(bo)
.from_(bo_item)
.select(bo.name)
.distinct()
.select(bo.blanket_order_type, bo.to_date)
.where(
(bo_item.parent == bo.name)
& (bo_item.item_code == filters.get("item"))
& (bo.blanket_order_type == filters.get("blanket_order_type"))
& (bo.company == filters.get("company"))
& (bo.docstatus == 1)
)
.run()
)
return blanket_orders
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
@@ -560,7 +566,7 @@ def get_income_account(doctype, txt, searchfield, start, page_len, filters):
if filters.get("company"):
condition += "and tabAccount.company = %(company)s"
condition += f"and tabAccount.disabled = {filters.get('disabled', 0)}"
condition += " and tabAccount.disabled = %(disabled)s"
return frappe.db.sql(
f"""select tabAccount.name from `tabAccount`
@@ -570,7 +576,11 @@ def get_income_account(doctype, txt, searchfield, start, page_len, filters):
and tabAccount.`{searchfield}` LIKE %(txt)s
{condition} {get_match_cond(doctype)}
order by idx desc, name""",
{"txt": "%" + txt + "%", "company": filters.get("company", "")},
{
"txt": "%" + txt + "%",
"company": filters.get("company", ""),
"disabled": cint(filters.get("disabled", 0)),
},
)
@@ -646,7 +656,7 @@ def get_expense_account(doctype, txt, searchfield, start, page_len, filters):
where (tabAccount.report_type = "Profit and Loss"
or tabAccount.account_type in ("Expense Account", "Fixed Asset", "Temporary", "Asset Received But Not Billed", "Capital Work in Progress"))
and tabAccount.is_group=0
and tabAccount.docstatus!=2
and tabAccount.disabled = 0
and tabAccount.{searchfield} LIKE %(txt)s
{condition} {get_match_cond(doctype)}""",
{"company": filters.get("company", ""), "txt": "%" + txt + "%"},

View File

@@ -368,7 +368,7 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None):
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
company = frappe.db.get_value("Delivery Note", source_name, "company")
company = frappe.db.get_value(doctype, source_name, "company")
default_warehouse_for_sales_return = frappe.get_cached_value(
"Company", company, "default_warehouse_for_sales_return"
)

View File

@@ -728,7 +728,16 @@ class SellingController(StockController):
def set_default_income_account_for_item(obj):
for d in obj.get("items"):
if d.item_code:
if getattr(d, "income_account", None):
set_item_default(d.item_code, obj.company, "income_account", d.income_account)
"""Set income account as default for items in the transaction.
Updates the item default income account for each item in the transaction
if it differs from the company's default income account.
Args:
obj: Transaction document containing items table with income_account field
"""
company_default = frappe.get_cached_value("Company", obj.company, "default_income_account")
for d in obj.get("items", default=[]):
income_account = getattr(d, "income_account", None)
if d.item_code and income_account and income_account != company_default:
set_item_default(d.item_code, obj.company, "income_account", income_account)

View File

@@ -520,8 +520,9 @@ class StockController(AccountsController):
make_sl_entries(sl_entries, allow_negative_stock, via_landed_cost_voucher)
def make_gl_entries_on_cancel(self):
cancel_exchange_gain_loss_journal(frappe._dict(doctype=self.doctype, name=self.name))
def make_gl_entries_on_cancel(self, from_repost=False):
if not from_repost:
cancel_exchange_gain_loss_journal(frappe._dict(doctype=self.doctype, name=self.name))
if frappe.db.sql(
"""select name from `tabGL Entry` where voucher_type=%s
and voucher_no=%s""",
@@ -801,7 +802,7 @@ class StockController(AccountsController):
child_tab.item_code,
child_tab.qty,
)
.where(parent_tab.docstatus < 2)
.where(parent_tab.docstatus == 1)
)
if self.doctype == "Purchase Invoice":
@@ -1027,12 +1028,7 @@ def is_reposting_pending():
)
def future_sle_exists(args, sl_entries=None, allow_force_reposting=True):
if allow_force_reposting and frappe.db.get_single_value(
"Stock Reposting Settings", "do_reposting_for_each_stock_transaction"
):
return True
def future_sle_exists(args, sl_entries=None):
key = (args.voucher_type, args.voucher_no)
if not hasattr(frappe.local, "future_sle"):
frappe.local.future_sle = {}

View File

@@ -42,17 +42,23 @@ class calculate_taxes_and_totals:
items = list(filter(lambda item: not item.get("is_alternative"), self.doc.get("items")))
return items
def calculate(self):
def calculate(self, ignore_tax_template_validation=False):
if not len(self._items):
return
self.discount_amount_applied = False
self.need_recomputation = False
self.ignore_tax_template_validation = ignore_tax_template_validation
self._calculate()
if self.doc.meta.get_field("discount_amount"):
self.set_discount_amount()
self.apply_discount_amount()
if not ignore_tax_template_validation and self.need_recomputation:
return self.calculate(ignore_tax_template_validation=True)
# Update grand total as per cash and non trade discount
if self.doc.apply_discount_on == "Grand Total" and self.doc.get("is_cash_or_non_trade_discount"):
self.doc.grand_total -= self.doc.discount_amount
@@ -96,6 +102,9 @@ class calculate_taxes_and_totals:
self.doc.base_tax_withholding_net_total = sum_base_net_amount
def validate_item_tax_template(self):
if self.ignore_tax_template_validation:
return
if self.doc.get("is_return") and self.doc.get("return_against"):
return
@@ -136,6 +145,10 @@ class calculate_taxes_and_totals:
)
)
# For correct tax_amount calculation re-computation is required
if self.discount_amount_applied and self.doc.apply_discount_on == "Grand Total":
self.need_recomputation = True
def update_item_tax_map(self):
for item in self.doc.items:
item.item_tax_rate = get_item_tax_map(
@@ -373,9 +386,7 @@ class calculate_taxes_and_totals:
self._calculate()
def calculate_taxes(self):
rounding_adjustment_computed = self.doc.get("is_consolidated") and self.doc.get("rounding_adjustment")
if not rounding_adjustment_computed:
self.doc.rounding_adjustment = 0
self.grand_total_diff = 0
# maintain actual tax rate based on idx
actual_tax_dict = dict(
@@ -440,9 +451,8 @@ class calculate_taxes_and_totals:
and self.discount_amount_applied
and self.doc.discount_amount
and self.doc.apply_discount_on == "Grand Total"
and not rounding_adjustment_computed
):
self.doc.rounding_adjustment = flt(
self.grand_total_diff = flt(
self.doc.grand_total - flt(self.doc.discount_amount) - tax.total,
self.doc.precision("rounding_adjustment"),
)
@@ -528,11 +538,11 @@ class calculate_taxes_and_totals:
return self.adjust_grand_total_for_inclusive_tax()
def adjust_grand_total_for_inclusive_tax(self):
# if fully inclusive taxes and diff
# if any inclusive taxes and diff
if self.doc.get("taxes") and any(cint(t.included_in_print_rate) for t in self.doc.get("taxes")):
last_tax = self.doc.get("taxes")[-1]
non_inclusive_tax_amount = sum(
flt(d.tax_amount_after_discount_amount)
self.get_tax_amount_if_for_valuation_or_deduction(d.tax_amount_after_discount_amount, d)
for d in self.doc.get("taxes")
if not d.included_in_print_rate
)
@@ -549,27 +559,23 @@ class calculate_taxes_and_totals:
diff = flt(diff, self.doc.precision("rounding_adjustment"))
if diff and abs(diff) <= (5.0 / 10 ** last_tax.precision("tax_amount")):
self.doc.grand_total_diff = diff
else:
self.doc.grand_total_diff = 0
self.grand_total_diff = diff
def calculate_totals(self):
if self.doc.get("taxes"):
self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(
self.doc.get("grand_total_diff")
)
self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + self.grand_total_diff
else:
self.doc.grand_total = flt(self.doc.net_total)
if self.doc.get("taxes"):
self.doc.total_taxes_and_charges = flt(
self.doc.grand_total - self.doc.net_total - flt(self.doc.get("grand_total_diff")),
self.doc.grand_total - self.doc.net_total - self.grand_total_diff,
self.doc.precision("total_taxes_and_charges"),
)
else:
self.doc.total_taxes_and_charges = 0.0
self._set_in_company_currency(self.doc, ["total_taxes_and_charges", "rounding_adjustment"])
self._set_in_company_currency(self.doc, ["total_taxes_and_charges"])
if self.doc.doctype in [
"Quotation",
@@ -619,7 +625,9 @@ class calculate_taxes_and_totals:
if self.doc.meta.get_field("rounded_total"):
if self.doc.is_rounded_total_disabled():
self.doc.rounded_total = self.doc.base_rounded_total = 0
self.doc.rounded_total = 0
self.doc.base_rounded_total = 0
self.doc.rounding_adjustment = 0
return
self.doc.rounded_total = round_based_on_smallest_currency_fraction(
@@ -663,33 +671,29 @@ class calculate_taxes_and_totals:
return
total_for_discount_amount = self.get_total_for_discount_amount()
taxes = self.doc.get("taxes")
net_total = 0
expected_net_total = 0
if total_for_discount_amount:
# calculate item amount after Discount Amount
for i, item in enumerate(self._items):
for item in self._items:
distributed_amount = (
flt(self.doc.discount_amount) * item.net_amount / total_for_discount_amount
)
item.net_amount = flt(item.net_amount - distributed_amount, item.precision("net_amount"))
adjusted_net_amount = item.net_amount - distributed_amount
expected_net_total += adjusted_net_amount
item.net_amount = flt(adjusted_net_amount, item.precision("net_amount"))
net_total += item.net_amount
# discount amount rounding loss adjustment if no taxes
if (
self.doc.apply_discount_on == "Net Total"
or not taxes
or total_for_discount_amount == self.doc.net_total
) and i == len(self._items) - 1:
discount_amount_loss = flt(
self.doc.net_total - net_total - self.doc.discount_amount,
self.doc.precision("net_total"),
)
# discount amount rounding adjustment
if rounding_difference := flt(
expected_net_total - net_total, self.doc.precision("net_total")
):
item.net_amount = flt(
item.net_amount + discount_amount_loss, item.precision("net_amount")
item.net_amount + rounding_difference, item.precision("net_amount")
)
net_total += rounding_difference
item.net_rate = (
flt(item.net_amount / item.qty, item.precision("net_rate")) if item.qty else 0
@@ -705,20 +709,44 @@ class calculate_taxes_and_totals:
def get_total_for_discount_amount(self):
if self.doc.apply_discount_on == "Net Total":
return self.doc.net_total
else:
actual_taxes_dict = {}
for tax in self.doc.get("taxes"):
if tax.charge_type in ["Actual", "On Item Quantity"]:
tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax.tax_amount, tax)
actual_taxes_dict.setdefault(tax.idx, tax_amount)
elif tax.row_id in actual_taxes_dict:
actual_tax_amount = flt(actual_taxes_dict.get(tax.row_id, 0)) * flt(tax.rate) / 100
actual_taxes_dict.setdefault(tax.idx, actual_tax_amount)
total_actual_tax = 0
actual_taxes_dict = {}
return flt(
self.doc.grand_total - sum(actual_taxes_dict.values()), self.doc.precision("grand_total")
def update_actual_tax_dict(tax, tax_amount):
nonlocal total_actual_tax
if tax.get("add_deduct_tax") == "Deduct":
tax_amount *= -1
if tax.get("category") != "Valuation":
total_actual_tax += tax_amount
actual_taxes_dict[int(tax.idx)] = {
"tax_amount": tax_amount,
"cumulative_tax_amount": total_actual_tax,
}
for tax in self.doc.get("taxes"):
if tax.charge_type in ["Actual", "On Item Quantity"]:
update_actual_tax_dict(tax, tax.tax_amount)
continue
if not tax.row_id:
continue
base_row = actual_taxes_dict.get(int(tax.row_id))
if not base_row:
continue
base_tax_amount = (
base_row["tax_amount"]
if tax.charge_type == "On Previous Row Amount"
else base_row["cumulative_tax_amount"]
)
update_actual_tax_dict(tax, base_tax_amount * tax.rate / 100)
return self.doc.grand_total - total_actual_tax
def calculate_total_advance(self):
if not self.doc.docstatus.is_cancelled():
@@ -780,9 +808,12 @@ class calculate_taxes_and_totals:
if (
self.doc.is_return
and self.doc.return_against
and not self.doc.update_outstanding_for_self
and not self.doc.get("is_pos")
or self.is_internal_invoice()
):
# Do not calculate the outstanding amount for a return invoice if 'update_outstanding_for_self' is not enabled.
self.doc.outstanding_amount = 0
return
self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"])

View File

@@ -29,4 +29,10 @@ frappe.ui.form.on("Contract", {
});
}
},
party_name: function (frm) {
let field = frm.doc.party_type.toLowerCase() + "_name";
frappe.db.get_value(frm.doc.party_type, frm.doc.party_name, field, (r) => {
frm.set_value("party_full_name", r[field]);
});
},
});

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