Compare commits

..

460 Commits

Author SHA1 Message Date
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
Frappe PR Bot
11da4372e2 chore(release): Bumped to Version 14.83.0
# [14.83.0](https://github.com/frappe/erpnext/compare/v14.82.2...v14.83.0) (2025-02-19)

### Bug Fixes

* '0' rate LDC's Invoice net totals should be ignored (backport [#45639](https://github.com/frappe/erpnext/issues/45639)) ([#45783](https://github.com/frappe/erpnext/issues/45783)) ([534b25c](534b25c448))
* add is_new in if condition ([ef37388](ef37388993))
* fetch child account data for selected parent ([#45904](https://github.com/frappe/erpnext/issues/45904)) ([e7d9786](e7d97865e5))
* include missing payment_gateway parameter in Payment Request URL ([a344b8b](a344b8b9ae))
* millisecond issue for posting datetime ([fc6f568](fc6f568a6c))
* on selection of batch qty is not fetching ([44a16bb](44a16bb544))
* pos return validation on v14 ([#45859](https://github.com/frappe/erpnext/issues/45859)) ([84432fc](84432fc035))
* **report:** add options to multiselectlist fields ([aa0ada9](aa0ada9670))
* **send_message:** escape HTML in the text ([703fd81](703fd816d1))
* serial no is mandatory for zero qty validation ([6f6133f](6f6133f2e2))
* set default value to 0 as per new logic ([4b16272](4b16272a01))
* tests ([e515b91](e515b91988))

### Features

* added ability to use custom html format for process statement of accounts ([#45746](https://github.com/frappe/erpnext/issues/45746)) ([a0cd08e](a0cd08e9ea))
* added option to enforce free item qty in pricing rule ([2edf083](2edf083c35))
2025-02-19 11:48:59 +00:00
ruthra kumar
4c74ac8738 Merge pull request #45982 from frappe/version-14-hotfix
chore: release v14
2025-02-19 17:17:34 +05:30
Akhil Narang
3e21e343d3 Merge pull request #46024 from frappe/mergify/bp/version-14-hotfix/pr-46003
fix(send_message): escape HTML in the text (backport #46003)
2025-02-19 16:43:09 +05:30
Rohit Waghchaure
c734373c9f fix: incorrect stock value difference for adjustment entry
(cherry picked from commit df83e427a3)
2025-02-19 11:11:09 +00:00
Akhil Narang
703fd816d1 fix(send_message): escape HTML in the text
Signed-off-by: Akhil Narang <me@akhilnarang.dev>
(cherry picked from commit 448a5db20f)
2025-02-19 10:45:28 +00:00
rohitwaghchaure
101c71c508 Merge pull request #46004 from frappe/mergify/bp/version-14-hotfix/pr-45750
feat: added option to enforce free item qty in pricing rule (backport #45750)
2025-02-19 15:45:14 +05:30
Steve Wilson
a0cd08e9ea feat: added ability to use custom html format for process statement of accounts (#45746)
* feat: added ability to use custom print format for process statement of accounts documents.

* fix: handles missing hook issues

* chore: linter changes

---------

Co-authored-by: Boy4099 <mashtawayne4099@gmail.com>
Co-authored-by: ruthra kumar <ruthra@erpnext.com>
2025-02-19 15:19:36 +05:30
rohitwaghchaure
57a0717778 chore: fix conflicts 2025-02-19 15:12:08 +05:30
rohitwaghchaure
0cf9c94a37 chore: fix conflicts 2025-02-19 15:10:34 +05:30
ruthra kumar
e48a03f130 Merge pull request #46009 from frappe/mergify/bp/version-14-hotfix/pr-45904
fix: fetch child account data for selected parent (backport #45904)
2025-02-19 14:21:34 +05:30
Bhavansathru
e7d97865e5 fix: fetch child account data for selected parent (#45904)
* fix: fetch child account data for selected parent

* fix: change reference name

---------

Co-authored-by: venkat102 <venkatesharunachalam659@gmail.com>
(cherry picked from commit 73e82b7afa)
2025-02-19 08:29:00 +00:00
Mihir Kandoi
4b16272a01 fix: set default value to 0 as per new logic
(cherry picked from commit 844f1636c0)
2025-02-19 07:21:39 +00:00
Mihir Kandoi
1ff0edd492 refactor: rename field
(cherry picked from commit f3d598881c)
2025-02-19 07:21:39 +00:00
Mihir Kandoi
ef37388993 fix: add is_new in if condition
(cherry picked from commit 4dcac56486)
2025-02-19 07:21:39 +00:00
Mihir Kandoi
116798df96 test: added test
(cherry picked from commit ac3259b8f1)
2025-02-19 07:21:38 +00:00
Mihir Kandoi
e515b91988 fix: tests
(cherry picked from commit 366ae85d85)

# Conflicts:
#	erpnext/stock/doctype/pick_list/test_pick_list.py
2025-02-19 07:21:38 +00:00
Mihir Kandoi
2edf083c35 feat: added option to enforce free item qty in pricing rule
(cherry picked from commit 19c01b1457)

# Conflicts:
#	erpnext/accounts/doctype/pricing_rule/pricing_rule.py
2025-02-19 07:21:38 +00:00
rohitwaghchaure
86ddabeae6 Merge pull request #45985 from rohitwaghchaure/fixed-support-31935-1
fix: millisecond issue for posting datetime
2025-02-18 20:11:28 +05:30
Rohit Waghchaure
fc6f568a6c fix: millisecond issue for posting datetime 2025-02-18 18:54:17 +05:30
rohitwaghchaure
cd3a411401 Merge pull request #45931 from rohitwaghchaure/fixed-support-31345-1
fix: serial no is mandatory for zero qty validation
2025-02-15 22:01:40 +05:30
Rohit Waghchaure
6f6133f2e2 fix: serial no is mandatory for zero qty validation 2025-02-15 15:34:37 +05:30
rohitwaghchaure
5a8b81409e Merge pull request #45920 from rohitwaghchaure/fixed-support-31326
fix: on selection of batch qty is not fetching
2025-02-14 17:34:21 +05:30
Rohit Waghchaure
44a16bb544 fix: on selection of batch qty is not fetching 2025-02-14 17:26:50 +05:30
ruthra kumar
206d0f1856 Merge pull request #45918 from frappe/mergify/bp/version-14-hotfix/pr-45912
fix: include missing payment_gateway parameter in Payment Request URL (backport #45912)
2025-02-14 17:13:20 +05:30
Diptanil Saha
84432fc035 fix: pos return validation on v14 (#45859)
fix: pos return validation v-14
2025-02-14 17:11:03 +05:30
Navin-S-R
a344b8b9ae fix: include missing payment_gateway parameter in Payment Request URL
(cherry picked from commit dbac8cfc94)
2025-02-14 11:20:17 +00:00
ruthra kumar
c65f421da9 Merge pull request #45893 from frappe/mergify/bp/version-14-hotfix/pr-45804
fix(report): add options to multiselectlist fields (backport #45804)
2025-02-13 14:27:22 +05:30
ruthra kumar
5c6028340f chore: resolve conflicts 2025-02-13 14:25:41 +05:30
venkat102
aa0ada9670 fix(report): add options to multiselectlist fields
(cherry picked from commit 8785342fce)

# Conflicts:
#	erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.js
#	erpnext/accounts/report/gross_profit/gross_profit.js
#	erpnext/accounts/report/supplier_ledger_summary/supplier_ledger_summary.js
#	erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.js
2025-02-13 08:46:55 +00:00
mergify[bot]
534b25c448 fix: '0' rate LDC's Invoice net totals should be ignored (backport #45639) (#45783)
* fix: '0' rate LDC's Invoice net totals should be ignored

(cherry picked from commit 325c4e3536)

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

* test: ldc @ 0 rate

(cherry picked from commit 0cdd346f8f)

# Conflicts:
#	erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py

* chore: resolve conflicts

* fix: incorrect parameters

* fix: ignore 0 rate ldc invoices

---------

Co-authored-by: ruthra kumar <ruthra@erpnext.com>
2025-02-13 13:19:34 +05:30
Frappe PR Bot
f109303f85 chore(release): Bumped to Version 14.82.2
## [14.82.2](https://github.com/frappe/erpnext/compare/v14.82.1...v14.82.2) (2025-02-12)

### Bug Fixes

* add total row in non_grouped_invoices ([49074aa](49074aa2fa))
* correct amt in account currency for lcv with manually distributed charges. (backport [#45532](https://github.com/frappe/erpnext/issues/45532)) ([#45863](https://github.com/frappe/erpnext/issues/45863)) ([c61e4e2](c61e4e2ddf))
* do not allow "Finance Book" in Accounting Dimensions (backport [#45696](https://github.com/frappe/erpnext/issues/45696)) ([#45855](https://github.com/frappe/erpnext/issues/45855)) ([08ba775](08ba77538b))
* do not validate party against  Receivable and Payable account for cancelled gl entries ([9f79da0](9f79da0015))
* map project from rfq to supplier quotation (backport [#45745](https://github.com/frappe/erpnext/issues/45745)) ([#45827](https://github.com/frappe/erpnext/issues/45827)) ([c4358c0](c4358c049a))
* Party name in Supplier Portal for Purchase Order (backport [#45772](https://github.com/frappe/erpnext/issues/45772)) ([#45857](https://github.com/frappe/erpnext/issues/45857)) ([46eba50](46eba50c8c))
* pos numpad editable action buttons (backport [#45823](https://github.com/frappe/erpnext/issues/45823)) ([#45825](https://github.com/frappe/erpnext/issues/45825)) ([6b9dad7](6b9dad7768))
* **regional:** removed payment schedule validation in sales invoice for italy (backport [#45852](https://github.com/frappe/erpnext/issues/45852)) ([#45853](https://github.com/frappe/erpnext/issues/45853)) ([de14bf1](de14bf1010))
* skip warning for free items ([2ed3bdc](2ed3bdcc2e))
* unable to remove image from employee ([91caca0](91caca05bb))
* update ctx to args ([573ce64](573ce645b2))

### Performance Improvements

* Ignore is_opening column in GL Queries ([#45327](https://github.com/frappe/erpnext/issues/45327)) ([9985a03](9985a03f39))
2025-02-12 12:08:02 +00:00
ruthra kumar
ab8fceb68d Merge pull request #45861 from frappe/version-14-hotfix
chore: release v14
2025-02-12 17:36:33 +05:30
rohitwaghchaure
e53a78c2bd Merge pull request #45883 from frappe/mergify/bp/version-14-hotfix/pr-45786
fix: skip warning for free items (backport #45786)
2025-02-12 15:16:15 +05:30
barredterra
2ed3bdcc2e fix: skip warning for free items
(cherry picked from commit 772776ad8a)
2025-02-12 09:20:04 +00:00
mergify[bot]
c61e4e2ddf fix: correct amt in account currency for lcv with manually distributed charges. (backport #45532) (#45863)
fix: correct amt in account currency for lcv with manually distributed charges.

(cherry picked from commit db38e7bf5a)

Co-authored-by: ljain112 <ljain112@gmail.com>
2025-02-11 16:05:35 +05:30
mergify[bot]
08ba77538b fix: do not allow "Finance Book" in Accounting Dimensions (backport #45696) (#45855)
fix: do not allow "Finance Book" in Accounting Dimensions

(cherry picked from commit a44be73a98)

Co-authored-by: ljain112 <ljain112@gmail.com>
2025-02-11 15:42:27 +05:30
mergify[bot]
de14bf1010 fix(regional): removed payment schedule validation in sales invoice for italy (backport #45852) (#45853)
fix(regional): removed payment schedule validation in sales invoice for italy (#45852)

(cherry picked from commit 494310293c)

Co-authored-by: Lakshit Jain <108322669+ljain112@users.noreply.github.com>
2025-02-11 15:41:39 +05:30
mergify[bot]
46eba50c8c fix: Party name in Supplier Portal for Purchase Order (backport #45772) (#45857)
fix: Party name in Supplier Portal for Purchase Order

(cherry picked from commit fc8663421b)

Co-authored-by: ljain112 <ljain112@gmail.com>
2025-02-11 15:41:04 +05:30
mergify[bot]
c4358c049a fix: map project from rfq to supplier quotation (backport #45745) (#45827)
* fix: map project from rfq to supplier quotation

(cherry picked from commit d0479036bb)

* fix: add project field map from mr to rfq

(cherry picked from commit 8fa39bec61)

---------

Co-authored-by: HenningWendtland <156231187+HenningWendtland@users.noreply.github.com>
2025-02-11 14:52:21 +05:30
ruthra kumar
a43f1badd5 Merge pull request #45846 from frappe/mergify/bp/version-14-hotfix/pr-45792
fix: do not validate party against Receivable and Payable account for cancelled gl entries (backport #45792)
2025-02-11 12:53:20 +05:30
ljain112
9f79da0015 fix: do not validate party against Receivable and Payable account for cancelled gl entries
(cherry picked from commit 0809e00455)
2025-02-11 06:44:37 +00:00
ruthra kumar
ca9df9db07 Merge pull request #45843 from frappe/mergify/bp/version-14-hotfix/pr-45781
fix: Added Total Row for `Gross Profit` Report in Non-Grouped Invoices (backport #45781)
2025-02-11 10:55:37 +05:30
Sanket322
49074aa2fa fix: add total row in non_grouped_invoices
(cherry picked from commit 2d32ddacc3)
2025-02-11 04:51:32 +00:00
mergify[bot]
6b9dad7768 fix: pos numpad editable action buttons (backport #45823) (#45825)
fix: pos numpad editable action buttons (#45823)

(cherry picked from commit 0b9c28620f)

Co-authored-by: Diptanil Saha <diptanil@frappe.io>
2025-02-10 16:33:21 +05:30
ruthra kumar
9e36cac0d1 Merge pull request #45812 from frappe/mergify/bp/version-14-hotfix/pr-45793
fix: unable to remove image from employee (backport #45793)
2025-02-10 11:01:25 +05:30
ruthra kumar
256318bb1c chore: resolve conflict 2025-02-10 10:37:50 +05:30
Asmita Hase
91caca05bb fix: unable to remove image from employee
fix: employee image disappears when newly created user_id is linked to employee

(cherry picked from commit 0207d2d7b6)

# Conflicts:
#	erpnext/setup/doctype/employee/employee.json
2025-02-10 05:02:53 +00:00
Frappe PR Bot
f5a1041de2 chore(release): Bumped to Version 14.82.1
## [14.82.1](https://github.com/frappe/erpnext/compare/v14.82.0...v14.82.1) (2025-02-07)

### Bug Fixes

* update ctx to args ([1e13806](1e138064a8))
2025-02-07 14:27:35 +00:00
ruthra kumar
4880a83538 Merge pull request #45797 from frappe/mergify/bp/version-14/pr-45796
Fix: Update `ctx` to `args` for compatibility. (copy #45770) (backport #45796)
2025-02-07 19:56:10 +05:30
Sanket322
1e138064a8 fix: update ctx to args
(cherry picked from commit d4bc3d182f)
(cherry picked from commit 573ce645b2)
2025-02-07 14:21:46 +00:00
ruthra kumar
ceb5997256 Merge pull request #45796 from frappe/mergify/copy/version-14-hotfix/pr-45770
Fix: Update `ctx` to `args` for compatibility. (copy #45770)
2025-02-07 19:50:15 +05:30
Sanket322
573ce645b2 fix: update ctx to args
(cherry picked from commit d4bc3d182f)
2025-02-07 14:15:35 +00:00
ruthra kumar
09cefd9d63 Merge pull request #45785 from frappe/mergify/bp/version-14-hotfix/pr-45327
perf: Ignore is_opening column in GL Queries (backport #45327)
2025-02-07 17:15:58 +05:30
ruthra kumar
222bd9351d chore: resolve conflicts 2025-02-07 15:39:21 +05:30
Deepesh Garg
9985a03f39 perf: Ignore is_opening column in GL Queries (#45327)
* perf: Ignore is_opening column in GL Queries

* chore: Remove unwanted changes

* chore: Remove unwanted changes

* chore: Remove unwanted changes

* chore: Remove unwanted changes

* chore: Remove unwanted changes

* chore: Remove unwanted changes

(cherry picked from commit 993f40fa43)

# Conflicts:
#	erpnext/accounts/doctype/accounts_settings/accounts_settings.json
#	erpnext/accounts/doctype/accounts_settings/accounts_settings.py
2025-02-07 10:06:07 +00:00
Frappe PR Bot
35fab8c23f chore(release): Bumped to Version 14.82.0
# [14.82.0](https://github.com/frappe/erpnext/compare/v14.81.0...v14.82.0) (2025-02-05)

### Bug Fixes

* allow multiple email ids ([9510758](9510758ce4))
* bind this to function ([83bce78](83bce785ff))
* check billing address ([2f33f6b](2f33f6bdf5))
* closing stock balance report not generating ([3f6beeb](3f6beebeec))
* correct error message in payment entry ([9f5d7e4](9f5d7e41ec))
* default payment terms template selected while duplicating ([82cfafb](82cfafb610))
* Gross Profit Report with Correct Totals and Gross Margin (backport [#45548](https://github.com/frappe/erpnext/issues/45548)) ([#45597](https://github.com/frappe/erpnext/issues/45597)) ([0086656](0086656748))
* handling company in bank reconciliation tool ([#45582](https://github.com/frappe/erpnext/issues/45582)) ([7823f1b](7823f1b06f))
* payment schedule table is empty while duplicating record ([d0ff91b](d0ff91b0e0))
* **pos:** add item in the existing item row when discount is applied ([3c6ed0a](3c6ed0a565))
* posting_date to posting_datetime in stock related queries ([bf3d68e](bf3d68e76d))
* renamed Commments Tab to Notes tab in Lead doctype ([0eddd1e](0eddd1e2d7))
* track employee changes ([#45674](https://github.com/frappe/erpnext/issues/45674)) ([43a5c33](43a5c33dbf))

### Features

* set bank account of company to default company bank account from masters ([dac5307](dac53074f2))
2025-02-05 11:45:31 +00:00
ruthra kumar
21b7833d57 Merge pull request #45704 from frappe/version-14-hotfix
chore: release v14
2025-02-05 17:14:10 +05:30
ruthra kumar
b217a7ee3e Merge pull request #45736 from frappe/mergify/bp/version-14-hotfix/pr-45717
fix(Purchase Invoice): default payment terms template selected while duplicating (backport #45717)
2025-02-05 15:30:42 +05:30
ruthra kumar
49787b6d84 chore: resolve conflicts 2025-02-05 15:22:13 +05:30
Ejaaz Khan
95903c9f96 refactor: remove log
(cherry picked from commit bfc01441a0)
2025-02-05 09:47:58 +00:00
Ejaaz Khan
d0ff91b0e0 fix: payment schedule table is empty while duplicating record
(cherry picked from commit fb3f08a441)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
2025-02-05 09:47:58 +00:00
Ejaaz Khan
82cfafb610 fix: default payment terms template selected while duplicating
(cherry picked from commit 18127603fe)
2025-02-05 09:47:57 +00:00
ruthra kumar
d14acb4f58 Merge pull request #45729 from frappe/mergify/bp/version-14-hotfix/pr-45686
fix: allow multiple email ids (backport #45686)
2025-02-05 14:27:57 +05:30
ruthra kumar
202693d4c3 Merge pull request #45730 from frappe/mergify/bp/version-14-hotfix/pr-45447
fix(pos): add item in the existing item row when discount is applied (backport #45447)
2025-02-05 14:10:21 +05:30
venkat102
3c6ed0a565 fix(pos): add item in the existing item row when discount is applied
(cherry picked from commit bee2c04d0b)
2025-02-05 14:03:54 +05:30
Sudharsanan11
2f33f6bdf5 fix: check billing address
(cherry picked from commit 9950e4aa0c)
2025-02-05 08:19:59 +00:00
Sudharsanan11
9510758ce4 fix: allow multiple email ids
(cherry picked from commit 423decb93c)
2025-02-05 08:19:58 +00:00
ruthra kumar
f01765db6f Merge pull request #45722 from frappe/mergify/bp/version-14-hotfix/pr-45721
fix: closing stock balance report not generating (backport #45721)
2025-02-05 10:38:34 +05:30
Rohit Waghchaure
3f6beebeec fix: closing stock balance report not generating
(cherry picked from commit 47d1c3b5a3)
2025-02-05 03:16:09 +00:00
ruthra kumar
8a9d554c32 Merge pull request #45712 from frappe/mergify/bp/version-14-hotfix/pr-45582
fix: handling company in bank reconciliation tool (backport #45582)
2025-02-04 17:32:26 +05:30
Aayush Dalal
7823f1b06f fix: handling company in bank reconciliation tool (#45582)
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
(cherry picked from commit d1c927530e)
2025-02-04 11:45:02 +00:00
ruthra kumar
4d5becbd7c Merge pull request #45676 from frappe/mergify/bp/version-14-hotfix/pr-45674
fix: track employee changes (backport #45674)
2025-02-03 15:27:16 +05:30
Ankush Menat
43a5c33dbf fix: track employee changes (#45674)
closes https://github.com/frappe/erpnext/issues/45571

(cherry picked from commit 827afbfa2e)
2025-02-03 08:52:37 +00:00
ruthra kumar
50c26ba017 Merge pull request #45668 from frappe/mergify/bp/version-14-hotfix/pr-45619
feat: set bank account of company to default company bank account fro… (backport #45619)
2025-02-03 14:05:38 +05:30
ruthra kumar
7bfe0526a1 Merge pull request #45670 from frappe/mergify/bp/version-14-hotfix/pr-44790
refactor: configurable posting date for Exc Gain / Loss journal (backport #44790)
2025-02-03 14:04:56 +05:30
Shariq Ansari
235b38a3af Merge pull request #45672 from frappe/mergify/bp/version-14-hotfix/pr-45637
fix: renamed Comments Tab to Notes tab in Lead doctype (backport #45637)
2025-02-03 13:27:22 +05:30
Shariq Ansari
c6ed82a304 chore: resolved conflict 2025-02-03 13:25:10 +05:30
ruthra kumar
e9d934d378 chore: resolve conflicts 2025-02-03 13:24:27 +05:30
Shariq Ansari
0eddd1e2d7 fix: renamed Commments Tab to Notes tab in Lead doctype
(cherry picked from commit 018df3135a)

# Conflicts:
#	erpnext/crm/doctype/lead/lead.json
2025-02-03 07:53:25 +00:00
ruthra kumar
cc275318e3 refactor: support JE posting date in semi-auto reconciilation tool
(cherry picked from commit a71718883e)

# Conflicts:
#	erpnext/accounts/doctype/process_payment_reconciliation_log_allocations/process_payment_reconciliation_log_allocations.json
#	erpnext/accounts/doctype/process_payment_reconciliation_log_allocations/process_payment_reconciliation_log_allocations.py
2025-02-03 07:29:24 +00:00
ruthra kumar
620cdc2489 test: exc gain/loss posting date based on configuration
(cherry picked from commit 2f3281579a)

# Conflicts:
#	erpnext/controllers/tests/test_accounts_controller.py
2025-02-03 07:29:24 +00:00
ruthra kumar
ef6e264887 refactor: only apply configuration on normal payments
patch to update default value

(cherry picked from commit b2c3da135e)

# Conflicts:
#	erpnext/controllers/accounts_controller.py
#	erpnext/patches.txt
2025-02-03 07:29:24 +00:00
ruthra kumar
5a62bd6e85 refactor: allow reconciliation date for exchange gain / loss
(cherry picked from commit 95af63e305)

# Conflicts:
#	erpnext/accounts/doctype/accounts_settings/accounts_settings.json
2025-02-03 07:29:23 +00:00
ruthra kumar
035139d4c7 refactor: configurable posting date for Exc Gain / Loss journal
(cherry picked from commit 5257413a93)
2025-02-03 07:29:23 +00:00
ruthra kumar
b5637c43fa refactor: configurable posting date for Exc Gain / Loss journal
(cherry picked from commit 3fbd2ca0d9)

# Conflicts:
#	erpnext/accounts/doctype/accounts_settings/accounts_settings.json
#	erpnext/accounts/doctype/accounts_settings/accounts_settings.py
2025-02-03 07:29:23 +00:00
Mihir Kandoi
dac53074f2 feat: set bank account of company to default company bank account from masters
(cherry picked from commit ce7702cc19)
2025-02-03 06:31:55 +00:00
rohitwaghchaure
b6a6bced61 Merge pull request #45666 from frappe/mergify/bp/version-14-hotfix/pr-45621
fix: delivered button of purchase order (backport #45621)
2025-02-03 12:01:11 +05:30
rohitwaghchaure
7b3c35c167 chore: fix conflicts 2025-02-03 11:58:10 +05:30
Mihir Kandoi
83bce785ff fix: bind this to function
(cherry picked from commit 41649cf52d)

# Conflicts:
#	erpnext/buying/doctype/purchase_order/purchase_order.js
2025-02-03 06:22:42 +00:00
mergify[bot]
0086656748 fix: Gross Profit Report with Correct Totals and Gross Margin (backport #45548) (#45597)
* fix: Gross Profit Report with Correct Totals and Gross Margin (#45548)

Co-authored-by: Sanket322 <shahsanket322003.com>
(cherry picked from commit aaf720ab61)

# Conflicts:
#	erpnext/accounts/report/gross_profit/test_gross_profit.py
#	erpnext/patches.txt

* fix: conflicts

---------

Co-authored-by: Sanket Shah <113279972+Sanket322@users.noreply.github.com>
Co-authored-by: ljain112 <ljain112@gmail.com>
2025-01-31 16:10:25 +05:30
rohitwaghchaure
36b1c436ea Merge pull request #45618 from frappe/mergify/bp/version-14-hotfix/pr-45612
fix: posting_date to posting_datetime in stock related queries (backport #45612)
2025-01-31 14:43:40 +05:30
ruthra kumar
9f4b3e86b3 Merge pull request #45635 from frappe/mergify/bp/version-14-hotfix/pr-45615
fix: correct error message in payment entry (backport #45615)
2025-01-31 14:18:49 +05:30
ljain112
9f5d7e41ec fix: correct error message in payment entry
(cherry picked from commit 592704cfd0)
2025-01-31 07:27:37 +00:00
rohitwaghchaure
50aa4ed55a chore: fix conflicts 2025-01-31 12:01:45 +05:30
rohitwaghchaure
dee6e2b697 chore: fix conflicts 2025-01-31 10:38:08 +05:30
rohitwaghchaure
9217e919c3 chore: fix conflicts 2025-01-31 10:36:25 +05:30
Rohit Waghchaure
bf3d68e76d fix: posting_date to posting_datetime in stock related queries
(cherry picked from commit e61ab48145)

# Conflicts:
#	erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py
#	erpnext/stock/report/stock_ledger/stock_ledger.py
2025-01-30 15:50:38 +00:00
Frappe PR Bot
4eaaffe550 chore(release): Bumped to Version 14.81.0
# [14.81.0](https://github.com/frappe/erpnext/compare/v14.80.0...v14.81.0) (2025-01-29)

### Bug Fixes

* add condition to check if item is delivered by supplier in make_purchase_order_for_default_supplier() ([#45370](https://github.com/frappe/erpnext/issues/45370)) ([69464ab](69464ab7ff))
* add multiple item issue in stock entry (backport [#45544](https://github.com/frappe/erpnext/issues/45544)) ([#45579](https://github.com/frappe/erpnext/issues/45579)) ([1160df9](1160df9350))
* currency decimal on POS Past Order List (backport [#45524](https://github.com/frappe/erpnext/issues/45524)) ([#45526](https://github.com/frappe/erpnext/issues/45526)) ([4d2352a](4d2352af00))
* fix creating documents from sales invoice (backport [#45346](https://github.com/frappe/erpnext/issues/45346)) ([#45407](https://github.com/frappe/erpnext/issues/45407)) ([452b205](452b205021))
* get stock balance filtered by company for validating stock value in jv (backport [#45549](https://github.com/frappe/erpnext/issues/45549)) ([#45577](https://github.com/frappe/erpnext/issues/45577)) ([e682d2c](e682d2c9ae))
* heatmap for Customer and Supplier not rendering ([#44717](https://github.com/frappe/erpnext/issues/44717)) ([ecdff8f](ecdff8f320))
* Merge Conflicts ([35f801f](35f801feda))
* remove applied pricing rule ([c484563](c484563bea))
* resolved pos return setting to default mode of payment instead of user selection ([#45377](https://github.com/frappe/erpnext/issues/45377)) ([#45436](https://github.com/frappe/erpnext/issues/45436)) ([24dc1bf](24dc1bf1a3))
* secure bulk transaction (backport [#45386](https://github.com/frappe/erpnext/issues/45386)) ([#45425](https://github.com/frappe/erpnext/issues/45425)) ([c6bc928](c6bc928f50))
* set company related values ([03068ab](03068ab96c))
* set preferred email in Employee via backend controller (backport [#45320](https://github.com/frappe/erpnext/issues/45320)) ([#45378](https://github.com/frappe/erpnext/issues/45378)) ([1622fc8](1622fc8728))
* update fields on change of item code In `Update Items` of `Sales Order` ([#45125](https://github.com/frappe/erpnext/issues/45125)) ([8befe7f](8befe7f244))
* update voucher outstanding from payment ledger ([a015527](a0155279e0))
* use user defined discount amount or default ([914f4bf](914f4bffea))
* Wrong `bank_ac_no` filter + simplify convoluted logic ([b6b453c](b6b453ca5d))

### Features

* add company level validation for accounting dimension ([cca5fbd](cca5fbd81a))
2025-01-29 11:30:28 +00:00
ruthra kumar
2c693c638d Merge pull request #45567 from frappe/version-14-hotfix
chore: release v14
2025-01-29 16:58:57 +05:30
ruthra kumar
39e82dfbc1 Merge pull request #45593 from frappe/mergify/bp/version-14-hotfix/pr-45569
fix: update voucher outstanding from payment ledger (backport #45569)
2025-01-29 16:33:00 +05:30
ljain112
a0155279e0 fix: update voucher outstanding from payment ledger
(cherry picked from commit dd77070351)
2025-01-29 10:42:17 +00:00
mergify[bot]
e682d2c9ae fix: get stock balance filtered by company for validating stock value in jv (backport #45549) (#45577)
* fix: get stock balance filtered by company for validating stock value in jv (#45549)

* fix: get stock balance filtered by company for validating stock value in jv

* test: error is raised  on validate

(cherry picked from commit 9f20854bd9)

# Conflicts:
#	erpnext/accounts/doctype/journal_entry/test_journal_entry.py

* fix: conflict

---------

Co-authored-by: Lakshit Jain <108322669+ljain112@users.noreply.github.com>
Co-authored-by: ljain112 <ljain112@gmail.com>
2025-01-29 12:46:13 +05:30
mergify[bot]
1160df9350 fix: add multiple item issue in stock entry (backport #45544) (#45579)
fix: add multiple item issue in stock entry (#45544)

(cherry picked from commit 5a023dc8d4)

Co-authored-by: Ejaaz Khan <67804911+iamejaaz@users.noreply.github.com>
2025-01-29 12:45:48 +05:30
ruthra kumar
80ed2fb1fb Merge pull request #45538 from frappe/mergify/bp/version-14-hotfix/pr-45345
fix:  maintain existing discounts in get_pricing_rule_for_item (backport #45345)
2025-01-29 12:26:52 +05:30
ruthra kumar
9eda931b97 chore: resolve conflicts 2025-01-29 11:58:21 +05:30
ruthra kumar
30f001edea Merge pull request #45556 from frappe/mergify/bp/version-14-hotfix/pr-45125
fix: update fields on change of item code In `Update Items` of `Sales Order` (backport #45125)
2025-01-28 11:32:08 +05:30
Sanket Shah
8befe7f244 fix: update fields on change of item code In Update Items of Sales Order (#45125)
* fix: update fields on change of item code

* fix: minor update

* fix: set the new values always

* Revert "fix: set the new values always"

This reverts commit 44daa0a641.

---------

Co-authored-by: Sanket322 <shahsanket322003.com>
Co-authored-by: ruthra kumar <ruthra@erpnext.com>
(cherry picked from commit 9933d3c8ff)
2025-01-28 05:58:26 +00:00
mergify[bot]
de531a81b6 chore: bump actions/cache to v4 (backport #45541) (#45545)
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2025-01-27 15:57:05 +01:00
ruthra kumar
ecbeaaf533 Merge pull request #45533 from frappe/mergify/bp/version-14-hotfix/pr-45302
feat: add company level validation for accounting dimension (backport #45302)
2025-01-27 17:30:52 +05:30
ruthra kumar
befc16cc97 refactor(test): update test data 2025-01-27 16:12:18 +05:30
ruthra kumar
b8e4d80b4e chore: resolve conflicts 2025-01-27 16:12:18 +05:30
Sugesh393
9767dc61a6 chore: update variable names for improved readability
(cherry picked from commit 36bae55299)
2025-01-27 16:12:18 +05:30
Sugesh393
03068ab96c fix: set company related values
(cherry picked from commit 454067198e)
2025-01-27 16:12:12 +05:30
Sanket322
c484563bea fix: remove applied pricing rule
(cherry picked from commit 50223c6bec)

# Conflicts:
#	erpnext/public/js/controllers/transaction.js
2025-01-27 10:32:53 +00:00
Sanket322
914f4bffea fix: use user defined discount amount or default
(cherry picked from commit e2a32b7257)
2025-01-27 10:32:52 +00:00
Sugesh393
2fb1aaa5c3 test: add new unit test for company validation in accounting dimension
(cherry picked from commit c94091d68f)

# Conflicts:
#	erpnext/controllers/tests/test_accounts_controller.py
2025-01-27 09:24:59 +00:00
Sugesh393
cca5fbd81a feat: add company level validation for accounting dimension
(cherry picked from commit 60efd3e219)

# Conflicts:
#	erpnext/controllers/accounts_controller.py
2025-01-27 09:24:59 +00:00
mergify[bot]
4d2352af00 fix: currency decimal on POS Past Order List (backport #45524) (#45526)
fix: currency decimal on POS Past Order List (#45524)

* fix: currency decimal on POS

* fix: removed precision

(cherry picked from commit 2ac8c92e7f)

Co-authored-by: Diptanil Saha <diptanil@frappe.io>
2025-01-27 13:21:34 +05:30
Diptanil Saha
24dc1bf1a3 fix: resolved pos return setting to default mode of payment instead of user selection (#45377) (#45436)
* fix: resolved pos return setting to default mode of payment instead of user selection

* refactor: removed console log statement

* refactor: moved get_payment_data to sales_and_purchase_return.py
2025-01-24 22:51:22 +05:30
mergify[bot]
c6bc928f50 fix: secure bulk transaction (backport #45386) (#45425)
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
fix: secure bulk transaction (#45386)
2025-01-24 17:19:17 +01:00
ruthra kumar
9518063a81 Merge pull request #45401 from frappe/mergify/bp/version-14-hotfix/pr-45129
fix: Wrong `bank_ac_no` filter + simplify logic in automatch (backport #45129)
2025-01-24 10:43:32 +05:30
mergify[bot]
452b205021 fix: fix creating documents from sales invoice (backport #45346) (#45407)
* fix: fix creating documents from sales invoice (#45346)

Co-authored-by: Meike Nedwidek <nedwidek@kk-software.de>
(cherry picked from commit 1758e125e0)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice/sales_invoice.js

* fix: resolved conflict

---------

Co-authored-by: meike289 <63092915+meike289@users.noreply.github.com>
Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2025-01-23 18:19:32 +05:30
marination
35f801feda fix: Merge Conflicts 2025-01-23 13:28:21 +01:00
Shanuka Hewage
69464ab7ff fix: add condition to check if item is delivered by supplier in make_purchase_order_for_default_supplier() (#45370) 2025-01-23 17:57:04 +05:30
marination
b6b453ca5d fix: Wrong bank_ac_no filter + simplify convoluted logic
(cherry picked from commit 8521796811)

# Conflicts:
#	erpnext/accounts/doctype/bank_transaction/auto_match_party.py
2025-01-23 11:13:05 +00:00
mergify[bot]
1622fc8728 fix: set preferred email in Employee via backend controller (backport #45320) (#45378)
fix: set preferred email in Employee via backend controller (#45320)

fix: set preferred email in Employee (backend)

Set "Preferred Email" for Employee via validate. Unset value when
prefered_contact_email is also unset.

(cherry picked from commit 4481ca83ff)

Co-authored-by: gavin <gavin18d@gmail.com>
2025-01-23 11:15:13 +05:30
HENRY Florian
ecdff8f320 fix: heatmap for Customer and Supplier not rendering (#44717) 2025-01-22 12:11:16 +05:30
Frappe PR Bot
a22d3b9895 chore(release): Bumped to Version 14.80.0
# [14.80.0](https://github.com/frappe/erpnext/compare/v14.79.0...v14.80.0) (2025-01-22)

### Bug Fixes

* do not reset picked items ([fe5de30](fe5de30256))
* include pos invoice in modifing key for returned item validation ([431fa22](431fa225e3))
* pos search by term items price (backport [#45006](https://github.com/frappe/erpnext/issues/45006)) ([#45102](https://github.com/frappe/erpnext/issues/45102)) ([524a8d7](524a8d77f7))
* **Project:** re-phrase welcome email ([#45175](https://github.com/frappe/erpnext/issues/45175)) ([77e92b3](77e92b38eb))
* round off tax withholding amount ([#45271](https://github.com/frappe/erpnext/issues/45271)) ([667e659](667e659e3f))
* update query ([49e3865](49e3865265))
* validate linked sales person ([f9420db](f9420db3ca))

### Features

* add difference_posting_date field ([1753509](17535095e2))
* use difference_posting_date for journal entry posting_date ([0fdd681](0fdd6817a6))

### Reverts

* avoid change to translatable string ([20bb151](20bb15167d))
2025-01-22 03:34:40 +00:00
ruthra kumar
ad960c1470 Merge pull request #45358 from frappe/version-14-hotfix
chore: release v14
2025-01-22 09:03:17 +05:30
ruthra kumar
0c7219159a Merge pull request #45359 from frappe/mergify/bp/version-14-hotfix/pr-45242
fix: include pos invoice in modifing key for returned item validation (backport #45242)
2025-01-21 16:58:13 +05:30
venkat102
431fa225e3 fix: include pos invoice in modifing key for returned item validation
(cherry picked from commit 2936139c79)
2025-01-21 11:06:02 +00:00
ruthra kumar
f27e35c8f4 Merge pull request #45351 from frappe/mergify/bp/version-14-hotfix/pr-44808
feat: Added difference_posting_date field in Sales Invoice Advance and Purchase Invoice Advance (backport #44808)
2025-01-21 14:48:27 +05:30
ruthra kumar
5fbffcbd7b Merge pull request #45311 from frappe/mergify/bp/version-14-hotfix/pr-45175
fix(Project): re-phrase welcome email (backport #45175)
2025-01-21 13:56:30 +05:30
ruthra kumar
bb949da334 Merge pull request #45313 from frappe/mergify/bp/version-14-hotfix/pr-45271
fix: round off tax withholding amount (backport #45271)
2025-01-21 13:55:19 +05:30
ruthra kumar
8764a321c7 chore: resolve conflicts 2025-01-21 12:57:10 +05:30
rs-rethik
49e3865265 fix: update query
(cherry picked from commit 854e37c05c)
2025-01-21 06:54:23 +00:00
rs-rethik
33a1da8194 refactor: convert sql query to query builder
(cherry picked from commit 2d58e845e6)
2025-01-21 06:54:23 +00:00
rs-rethik
52309fe0b6 test: add unit test to validate journal entry posting date
(cherry picked from commit c14a2d73bf)

# Conflicts:
#	erpnext/controllers/tests/test_accounts_controller.py
2025-01-21 06:54:23 +00:00
rs-rethik
0fdd6817a6 feat: use difference_posting_date for journal entry posting_date
(cherry picked from commit ff1d040a6e)
2025-01-21 06:54:22 +00:00
rs-rethik
17535095e2 feat: add difference_posting_date field
(cherry picked from commit 225e56cbca)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json
#	erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.py
#	erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json
#	erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.py
2025-01-21 06:54:22 +00:00
ruthra kumar
4d74597f94 Merge pull request #45336 from frappe/mergify/bp/version-14-hotfix/pr-45167
fix: validate linked sales person (backport #45167)
2025-01-20 12:10:39 +05:30
Sudharsanan11
f9420db3ca fix: validate linked sales person
(cherry picked from commit e614f07795)
2025-01-20 06:20:20 +00:00
rohitwaghchaure
8996685f44 Merge pull request #45326 from frappe/mergify/bp/version-14-hotfix/pr-44783
fix: do not reset picked items (backport #44783)
2025-01-19 15:07:00 +05:30
rohitwaghchaure
7046a01921 chore: fix test case 2025-01-19 14:22:52 +05:30
rohitwaghchaure
0b8cf3a369 chore: fix conflicts 2025-01-19 13:52:49 +05:30
Rohit Waghchaure
fe5de30256 fix: do not reset picked items
(cherry picked from commit 34a80bfcd3)

# Conflicts:
#	erpnext/stock/doctype/pick_list/test_pick_list.py
2025-01-18 10:15:22 +00:00
barredterra
20bb15167d revert: avoid change to translatable string 2025-01-17 14:38:27 +01:00
barredterra
1ccf30d97b chore: resolve confilcts 2025-01-17 14:38:03 +01:00
mergify[bot]
524a8d77f7 fix: pos search by term items price (backport #45006) (#45102)
* fix: load price list rate for pos search term

(cherry picked from commit 4b6cae156e)

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

* fix: load search term price with customer default price list

(cherry picked from commit 2beb485d77)

* chore: resolve conflict

---------

Co-authored-by: diptanilsaha <diptanil.dev@gmail.com>
Co-authored-by: ruthra kumar <ruthra@erpnext.com>
2025-01-17 17:02:34 +05:30
Lakshit Jain
667e659e3f fix: round off tax withholding amount (#45271)
(cherry picked from commit ada272a29b)
2025-01-17 11:21:09 +00:00
Patrick Eißler
77e92b38eb fix(Project): re-phrase welcome email (#45175)
(cherry picked from commit 8d66142865)

# Conflicts:
#	erpnext/projects/doctype/project/project.py
2025-01-17 11:19:03 +00:00
Frappe PR Bot
ff3425ead1 chore(release): Bumped to Version 14.79.0
# [14.79.0](https://github.com/frappe/erpnext/compare/v14.78.9...v14.79.0) (2025-01-15)

### Bug Fixes

* deduct tds on excess amount if checked ([6a52f79](6a52f79cce))
* do not add ordered items from Quotation to new Sales Order ([d42173b](d42173beb5))
* **Timesheet:** ignore permissions when updating Task and Project (backport [#45168](https://github.com/frappe/erpnext/issues/45168)) ([#45171](https://github.com/frappe/erpnext/issues/45171)) ([49ffecc](49ffeccafa))
* typo ([#45233](https://github.com/frappe/erpnext/issues/45233)) ([6bc210d](6bc210d9f4))

### Features

* validate discount date in payment schedule (backport [#44646](https://github.com/frappe/erpnext/issues/44646)) ([#44726](https://github.com/frappe/erpnext/issues/44726)) ([f4b7fa8](f4b7fa8980))
2025-01-15 12:09:13 +00:00
rohitwaghchaure
d098fd3fc3 Merge pull request #45264 from frappe/version-14-hotfix
chore: release v14
2025-01-15 17:37:57 +05:30
ruthra kumar
a66d475b56 Merge pull request #45253 from frappe/mergify/bp/version-14-hotfix/pr-45001
fix: deduct tds on excess amount if checked (backport #45001)
2025-01-14 10:28:19 +05:30
ljain112
6a52f79cce fix: deduct tds on excess amount if checked
(cherry picked from commit a203e3ffaf)
2025-01-14 04:33:09 +00:00
mergify[bot]
49ffeccafa fix(Timesheet): ignore permissions when updating Task and Project (backport #45168) (#45171)
* fix(Timesheet): ignore permissions when updating Task and Project (#45168)

(cherry picked from commit 9e760e54a5)

# Conflicts:
#	erpnext/projects/doctype/timesheet/timesheet.py

* chore: resolve conflicts

---------

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2025-01-13 12:08:26 +05:30
Nabin Hait
6bc210d9f4 fix: typo (#45233) 2025-01-13 12:04:40 +05:30
mergify[bot]
f4b7fa8980 feat: validate discount date in payment schedule (backport #44646) (#44726)
Co-authored-by: barredterra <14891507+barredterra@users.noreply.github.com>
2025-01-09 18:51:27 +01:00
rohitwaghchaure
c331a4fa84 Merge pull request #45185 from frappe/mergify/bp/version-14-hotfix/pr-45180
fix: do not add ordered items from Quotation to new Sales Order (backport #45180)
2025-01-09 16:27:15 +05:30
Rohit Waghchaure
d42173beb5 fix: do not add ordered items from Quotation to new Sales Order
(cherry picked from commit 2e930eb97b)
2025-01-09 09:09:56 +00:00
Frappe PR Bot
611c1f1ec2 chore(release): Bumped to Version 14.78.9
## [14.78.9](https://github.com/frappe/erpnext/compare/v14.78.8...v14.78.9) (2025-01-08)

### Bug Fixes

* add monthly distributation and write query in qb ([7d1d0c8](7d1d0c8e0c))
* discount resetting on date change ([01d2794](01d2794968)), closes [#44989](https://github.com/frappe/erpnext/issues/44989)
* header_img field schema ([eb55051](eb5505187e))
* Missing company filter breaks `get_account_balance` in Bank Reco ([821cfe2](821cfe2c39))
* Override pre-commit behaviour due to conflicts with CI ([46894a5](46894a5b86))
* **Project:** make status in confirmation dialog translatable ([#45118](https://github.com/frappe/erpnext/issues/45118)) ([07653c5](07653c54f3))
* Returned Qty in Work Order Consumed Materials report ([affa67e](affa67e74d))
* update customer contact details on pos (backport [#45071](https://github.com/frappe/erpnext/issues/45071)) ([#45105](https://github.com/frappe/erpnext/issues/45105)) ([2d63fc9](2d63fc98d0))
2025-01-08 10:27:38 +00:00
ruthra kumar
a05fb916ff Merge pull request #45132 from frappe/version-14-hotfix
chore: release v14
2025-01-08 15:56:16 +05:30
ruthra kumar
b2d35fae10 Merge pull request #45156 from frappe/revert-45152-mergify/bp/version-14-hotfix/pr-45112
Revert "fix: Missing company filter breaks `get_account_balance` in Bank Reco (backport #45112)"
2025-01-08 09:01:37 +05:30
ruthra kumar
8d13ef050e Revert "fix: Missing company filter breaks get_account_balance in Bank Reco (backport #45112)" 2025-01-08 09:00:21 +05:30
ruthra kumar
8ac40f07e3 Merge pull request #45152 from frappe/mergify/bp/version-14-hotfix/pr-45112
fix: Missing company filter breaks `get_account_balance` in Bank Reco (backport #45112)
2025-01-08 08:59:22 +05:30
marination
46894a5b86 fix: Override pre-commit behaviour due to conflicts with CI
(cherry picked from commit d7bf73cffa)
2025-01-08 02:46:51 +00:00
marination
821cfe2c39 fix: Missing company filter breaks get_account_balance in Bank Reco
(cherry picked from commit 8de0fe78ea)
2025-01-08 02:46:51 +00:00
ruthra kumar
b8b76a5b58 Merge pull request #45122 from frappe/mergify/bp/version-14-hotfix/pr-45121
fix: discount resetting on date change (backport #45121)
2025-01-07 11:26:24 +05:30
ruthra kumar
01d2794968 fix: discount resetting on date change
revert #44989

(cherry picked from commit 886281f81a)
2025-01-07 05:51:46 +00:00
ruthra kumar
02cfb589a2 Merge pull request #45108 from frappe/mergify/bp/version-14-hotfix/pr-45107
fix: Returned Qty in Work Order Consumed Materials report (backport #45107)
2025-01-07 10:12:56 +05:30
ruthra kumar
caf5faceda Merge pull request #45119 from frappe/mergify/bp/version-14-hotfix/pr-45118
fix(Project): make status in confirmation dialog translatable (backport #45118)
2025-01-07 10:12:04 +05:30
Raffael Meyer
07653c54f3 fix(Project): make status in confirmation dialog translatable (#45118)
(cherry picked from commit 9eede907f8)
2025-01-06 22:13:47 +00:00
Rohit Waghchaure
affa67e74d fix: Returned Qty in Work Order Consumed Materials report
(cherry picked from commit 30d68a31e0)
2025-01-06 10:37:28 +00:00
mergify[bot]
2d63fc98d0 fix: update customer contact details on pos (backport #45071) (#45105)
fix: update customer contact details on pos (#45071)

* fix: update customer contact details on pos

* refactor: removed console log statement

(cherry picked from commit d79e561248)

Co-authored-by: Diptanil Saha <diptanil@frappe.io>
2025-01-06 14:58:36 +05:30
ruthra kumar
8bb4415f65 Merge pull request #45079 from frappe/mergify/bp/version-14-hotfix/pr-44983
fix: add monthly distributation and write query in qb (backport #44983)
2025-01-06 10:28:45 +05:30
ruthra kumar
90b2ec9aba Merge pull request #42438 from trustedcomputer/version-14-hotfix
fix: header_img field schema (backport payments #83)
2025-01-04 05:20:58 +05:30
ruthra kumar
24ae74ebb3 refactor: store result in variable before enumeration
helps to inspect result while debugging

(cherry picked from commit b60bd17d1d)
2025-01-03 23:48:31 +00:00
Sanket322
7d1d0c8e0c fix: add monthly distributation and write query in qb
(cherry picked from commit 27195c7c96)
2025-01-03 23:48:30 +00:00
Frappe PR Bot
1f39bb9c7a chore(release): Bumped to Version 14.78.8
## [14.78.8](https://github.com/frappe/erpnext/compare/v14.78.7...v14.78.8) (2025-01-03)

### Bug Fixes

* BOM cost update issue ([4977843](49778432ea))
* removed unused code ([9efc1de](9efc1de40e))
* slow stock transactions (backport [#45025](https://github.com/frappe/erpnext/issues/45025)) ([#45026](https://github.com/frappe/erpnext/issues/45026)) ([b7509e3](b7509e326e))
2025-01-03 01:21:42 +00:00
ruthra kumar
9e60f67d25 Merge pull request #45064 from frappe/version-14-hotfix
chore: release v14
2025-01-03 06:50:19 +05:30
ruthra kumar
6a91f9155f Merge pull request #45059 from frappe/mergify/bp/version-14/pr-45056
chore: partial revert #44989 (backport #45056)
2025-01-02 21:04:58 +05:30
ruthra kumar
b3d545f91a Merge pull request #45057 from frappe/mergify/bp/version-14-hotfix/pr-45056
chore: partial revert #44989 (backport #45056)
2025-01-02 21:04:16 +05:30
ruthra kumar
77f612354f chore: partial revert #44989
(cherry picked from commit 63d547fb4a)
2025-01-02 15:29:53 +00:00
ruthra kumar
7b0e19499b chore: partial revert #44989
(cherry picked from commit 63d547fb4a)
2025-01-02 15:27:38 +00:00
rohitwaghchaure
34504a23ec Merge pull request #45041 from frappe/mergify/bp/version-14-hotfix/pr-45039
fix: removed unused code (backport #45039)
2025-01-02 13:32:23 +05:30
rohitwaghchaure
b603adce5e chore: fix conflicts 2025-01-02 12:21:03 +05:30
Rohit Waghchaure
9efc1de40e fix: removed unused code
(cherry picked from commit dc5f2d35ac)

# Conflicts:
#	erpnext/stock/doctype/stock_entry/stock_entry.py
2025-01-02 06:47:37 +00:00
rohitwaghchaure
ff622ef552 Merge pull request #45037 from frappe/mergify/bp/version-14-hotfix/pr-45036
fix: Auto BOM cost update issue (backport #45036)
2025-01-02 12:15:37 +05:30
Rohit Waghchaure
49778432ea fix: BOM cost update issue
(cherry picked from commit 28ea3ddd51)
2025-01-02 04:55:15 +00:00
Frappe PR Bot
0e8a04f2cc chore(release): Bumped to Version 14.78.7
## [14.78.7](https://github.com/frappe/erpnext/compare/v14.78.6...v14.78.7) (2025-01-01)

### Bug Fixes

* slow stock transactions (backport [#45025](https://github.com/frappe/erpnext/issues/45025)) ([#45026](https://github.com/frappe/erpnext/issues/45026)) ([a85a103](a85a103bad))
2025-01-01 10:48:12 +00:00
rohitwaghchaure
2a40845f65 Merge pull request #45029 from frappe/mergify/bp/version-14/pr-45026
fix: slow stock transactions (backport #45025) (backport #45026)
2025-01-01 16:16:46 +05:30
mergify[bot]
a85a103bad fix: slow stock transactions (backport #45025) (#45026)
* fix: slow stock transactions (#45025)

(cherry picked from commit e92af10f14)

# Conflicts:
#	erpnext/stock/stock_ledger.py

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
(cherry picked from commit b7509e326e)
2025-01-01 10:00:11 +00:00
mergify[bot]
b7509e326e fix: slow stock transactions (backport #45025) (#45026)
* fix: slow stock transactions (#45025)

(cherry picked from commit e92af10f14)

# Conflicts:
#	erpnext/stock/stock_ledger.py

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2025-01-01 15:29:06 +05:30
Frappe PR Bot
76ea858e72 chore(release): Bumped to Version 14.78.6
## [14.78.6](https://github.com/frappe/erpnext/compare/v14.78.5...v14.78.6) (2025-01-01)

### Bug Fixes

* apply apply_pricing_rule date change ([cca507f](cca507f0bd))
* clear payment schedule in purchase invoice for is_paid ([cdd3763](cdd3763a8b))
* duplicate validate for closing stock balance (backport [#45015](https://github.com/frappe/erpnext/issues/45015)) ([#45021](https://github.com/frappe/erpnext/issues/45021)) ([e2b6615](e2b6615e72))
* failing tests fixed ([498abf7](498abf7c83))
* get item tax template based on posting date ([56d0686](56d06861d0))
* load customer default price list in pos during item selection (backport [#44991](https://github.com/frappe/erpnext/issues/44991)) ([#44992](https://github.com/frappe/erpnext/issues/44992)) ([0f687f1](0f687f1db7))
* pos payment using non-default mode of payment (backport [#44920](https://github.com/frappe/erpnext/issues/44920)) ([#44970](https://github.com/frappe/erpnext/issues/44970)) ([a0063b3](a0063b31c2)), closes [#41108](https://github.com/frappe/erpnext/issues/41108)
* refactor query in get_total_allocated_amount in bank_transaction ([cc0a478](cc0a478559))
* resolve conflicts ([443a0b8](443a0b81f9))
* set paid amount in party currency in bank reco payment entry ([43a40b1](43a40b1e66))
* Show order tax amount in customer currency on the portal (backport [#44915](https://github.com/frappe/erpnext/issues/44915)) ([#44922](https://github.com/frappe/erpnext/issues/44922)) ([78e0799](78e0799a06))
* update item_tax_rate in backend ([cf980a1](cf980a1304))
* update payment amount for partial pos return (backport [#44065](https://github.com/frappe/erpnext/issues/44065)) ([#44961](https://github.com/frappe/erpnext/issues/44961)) ([1da4712](1da4712919))
2025-01-01 08:56:51 +00:00
ruthra kumar
97487eb6e1 Merge pull request #45004 from frappe/version-14-hotfix
chore: release v14
2025-01-01 14:25:31 +05:30
ruthra kumar
97612ebbf7 Merge pull request #45007 from frappe/mergify/bp/version-14-hotfix/pr-44884
fix: update item_tax_rate in backend (backport #44884)
2025-01-01 13:48:40 +05:30
ruthra kumar
02a0bf35ea chore: remove tests for 'Tax Detail' report 2025-01-01 13:08:08 +05:30
ruthra kumar
0d17cf115a Merge pull request #45022 from frappe/mergify/bp/version-14-hotfix/pr-44989
fix: apply apply_pricing_rule on date change (backport #44989)
2025-01-01 10:18:10 +05:30
DHINESH00
cca507f0bd fix: apply apply_pricing_rule date change
(cherry picked from commit 2cbab9b875)
2025-01-01 04:42:47 +00:00
mergify[bot]
e2b6615e72 fix: duplicate validate for closing stock balance (backport #45015) (#45021)
fix: duplicate validate for closing stock balance (#45015)

(cherry picked from commit 8d650e56ba)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2025-01-01 09:32:51 +05:30
ruthra kumar
fd2a1a0ba0 refactor: use correct method parameters 2024-12-31 17:10:30 +05:30
ruthra kumar
514835b5dd Merge pull request #45009 from frappe/mergify/bp/version-14-hotfix/pr-44903
fix: set paid amount in party currency in bank reco payment entry (backport #44903)
2024-12-31 16:16:06 +05:30
ruthra kumar
bcd3b36821 chore: resolve conflict 2024-12-31 15:55:03 +05:30
ljain112
43a40b1e66 fix: set paid amount in party currency in bank reco payment entry
(cherry picked from commit 70b1077286)
2024-12-31 10:23:34 +00:00
ljain112
cf980a1304 fix: update item_tax_rate in backend
(cherry picked from commit de54c0b41f)

# Conflicts:
#	erpnext/controllers/taxes_and_totals.py
2024-12-31 10:21:30 +00:00
mergify[bot]
0f687f1db7 fix: load customer default price list in pos during item selection (backport #44991) (#44992)
fix: load customer default price list in pos during item selection (#44991)

fix: load customer default price list in pos
(cherry picked from commit d1ae0d784e)

Co-authored-by: Diptanil Saha <diptanil.dev@gmail.com>
2024-12-31 12:52:36 +05:30
mergify[bot]
a0063b31c2 fix: pos payment using non-default mode of payment (backport #44920) (#44970)
fix: pos payment using non-default mode of payment (#44920)

* fix: pos payment using non-default mode of payment (#41108)

* fix: included css syntax

* refactor: created a function to sanitize the class name

* refactor: reusing method to sanitize class name

* refactor: function rename

(cherry picked from commit 98cbb7e900)

Co-authored-by: Diptanil Saha <diptanil.dev@gmail.com>
2024-12-30 13:57:08 +05:30
ruthra kumar
47e2699315 Merge pull request #44965 from frappe/mergify/bp/version-14-hotfix/pr-44921
fix: get item tax template based on posting date (backport #44921)
2024-12-30 13:09:06 +05:30
ruthra kumar
899acc3601 Merge pull request #44962 from vishakhdesai/backport-v14-#44787
fix: refactor query in get_total_allocated_amount in bank_transaction (backport #44787)
2024-12-30 13:08:38 +05:30
ljain112
56d06861d0 fix: get item tax template based on posting date
(cherry picked from commit 976e35d547)
2024-12-30 12:33:40 +05:30
ruthra kumar
45bf65f37c Merge pull request #44959 from frappe/mergify/bp/version-14-hotfix/pr-44958
refactor(test): make manufacturing test idempotent (backport #44958)
2024-12-30 12:28:08 +05:30
vishakhdesai
443a0b81f9 fix: resolve conflicts 2024-12-30 12:27:14 +05:30
ruthra kumar
d348d587e2 refactor(test): make manufacturing test idempotent
(cherry picked from commit f3be246df3)
2024-12-30 12:04:36 +05:30
mergify[bot]
1da4712919 fix: update payment amount for partial pos return (backport #44065) (#44961)
fix: update payment amount for partial pos return

(cherry picked from commit 53ef6336b6)

Co-authored-by: Kavin <78342682+kavin0411@users.noreply.github.com>
2024-12-30 11:54:14 +05:30
ruthra kumar
3dcb7c1380 Merge pull request #44934 from frappe/mergify/bp/version-14-hotfix/pr-44892
fix: clear payment schedule in purchase invoice for is_paid (backport #44892)
2024-12-27 14:44:51 +05:30
ruthra kumar
ce7dc5ecb3 chore: resolve conflicts 2024-12-27 13:51:11 +05:30
ruthra kumar
d5f30202b4 refactor: early return is always better
validate_advance_entries() has a heavy IO bound operation. Early
return on unwanted cases is always better.

(cherry picked from commit 0589fa7f3e)

# Conflicts:
#	erpnext/controllers/accounts_controller.py
2024-12-27 07:54:39 +00:00
Sanket322
cdd3763a8b fix: clear payment schedule in purchase invoice for is_paid
(cherry picked from commit e1fc239f3d)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
#	erpnext/controllers/accounts_controller.py
2024-12-27 07:54:38 +00:00
mergify[bot]
78e0799a06 fix: Show order tax amount in customer currency on the portal (backport #44915) (#44922)
* fix: Show order tax amount in customer currency on the portal (#44915)

(cherry picked from commit b998933ef0)

# Conflicts:
#	erpnext/templates/includes/order/order_taxes.html

* fix: resolved conflict

---------

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2024-12-27 11:25:59 +05:30
Frappe PR Bot
27b7de4854 chore(release): Bumped to Version 14.78.5
## [14.78.5](https://github.com/frappe/erpnext/compare/v14.78.4...v14.78.5) (2024-12-25)

### Bug Fixes

* 'str' object has no attribute 'get_sql' ([510e3ce](510e3cebc9))
* added docs.frappe.io in documentation_url ([#44776](https://github.com/frappe/erpnext/issues/44776)) ([82a3fd1](82a3fd13f2))
* buying rate for service item in gross profit report ([7fa3a78](7fa3a789b1))
* closing stock balance permissions (backport [#44791](https://github.com/frappe/erpnext/issues/44791)) ([#44792](https://github.com/frappe/erpnext/issues/44792)) ([4cb27b9](4cb27b97b4))
* correct tds rate with lower deduction certificate ([d55fe7d](d55fe7d33f))
* do not set cost_center update_voucher_balance as it is set in init_voucher_balance ([97d5d18](97d5d18041))
* fetch tax withholding category from the voucher ([1093c22](1093c227f1))
* permissions for marking Quotation as lost ([0179358](0179358f07))
* Remove typo ([422ada2](422ada2726))
* show profit and loss after period closing ([1a089da](1a089da51a))
* slow posting datetime update (backport [#44799](https://github.com/frappe/erpnext/issues/44799)) ([#44804](https://github.com/frappe/erpnext/issues/44804)) ([d2ede71](d2ede713e4))
* update correct cost center in Accounts Receivable Report ([197b13e](197b13eb87))
* use utility method to generate url ([d042330](d042330c30))
* **ux:** purchase invoice link in error message ([#44797](https://github.com/frappe/erpnext/issues/44797)) ([c9d43f4](c9d43f4c88))
* Warehouse wise Stock Value chart roles (backport [#44865](https://github.com/frappe/erpnext/issues/44865)) ([#44866](https://github.com/frappe/erpnext/issues/44866)) ([177ca6f](177ca6f431))
2024-12-25 03:34:28 +00:00
ruthra kumar
1b5384f8d4 Merge pull request #44886 from frappe/version-14-hotfix
chore: release v14
2024-12-25 09:03:10 +05:30
vishakhdesai
498abf7c83 fix: failing tests fixed
(cherry picked from commit 2ce07865d3)

# Conflicts:
#	erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py
2024-12-24 12:23:59 +00:00
vishakhdesai
cc0a478559 fix: refactor query in get_total_allocated_amount in bank_transaction
(cherry picked from commit 6b847cdb62)

# Conflicts:
#	erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py
#	erpnext/accounts/doctype/bank_transaction/bank_transaction.py
2024-12-24 12:23:59 +00:00
ruthra kumar
07947eb3f4 Merge pull request #44893 from frappe/mergify/bp/version-14-hotfix/pr-44878
fix: show profit and loss after period closing (backport #44878)
2024-12-24 17:52:45 +05:30
venkat102
1a089da51a fix: show profit and loss after period closing
(cherry picked from commit dc5cd93bf0)
2024-12-24 11:33:39 +00:00
ruthra kumar
67c9872146 Merge pull request #44881 from frappe/mergify/bp/version-14-hotfix/pr-44794
fix: correct tds rate with lower deduction certificate (backport #44794)
2024-12-24 14:37:27 +05:30
ljain112
d55fe7d33f fix: correct tds rate with lower deduction certificate
(cherry picked from commit cb9c12d495)
2024-12-24 08:15:49 +00:00
mergify[bot]
177ca6f431 fix: Warehouse wise Stock Value chart roles (backport #44865) (#44866)
fix: Warehouse wise Stock Value chart roles (#44865)

(cherry picked from commit 7d41805d0e)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-12-23 22:30:45 +05:30
ruthra kumar
8915c804c1 Merge pull request #44848 from frappe/mergify/bp/version-14-hotfix/pr-44797
fix(ux): purchase invoice link in error message (backport #44797)
2024-12-23 15:17:58 +05:30
ruthra kumar
d506589d10 Merge pull request #44846 from frappe/mergify/bp/version-14-hotfix/pr-44738
fix: fetch tax withholding category from the voucher (backport #44738)
2024-12-23 15:03:12 +05:30
ruthra kumar
7ef15ec082 Merge pull request #44842 from frappe/mergify/bp/version-14-hotfix/pr-44813
fix: update correct cost center in Accounts Receivable Report (backport #44813)
2024-12-23 14:55:56 +05:30
Nijith anil
c9d43f4c88 fix(ux): purchase invoice link in error message (#44797)
* fix(ux): purchase invoice link in error message

* chore: fix linter

---------

Co-authored-by: ruthra kumar <ruthra@erpnext.com>
(cherry picked from commit 6f00a87a9c)
2024-12-23 09:19:26 +00:00
venkat102
1093c227f1 fix: fetch tax withholding category from the voucher
(cherry picked from commit 09e64594db)
2024-12-23 09:06:02 +00:00
venkat102
d0c60343cd chore: use get function
(cherry picked from commit 1663c7983e)
2024-12-23 09:06:01 +00:00
vishakhdesai
97d5d18041 fix: do not set cost_center update_voucher_balance as it is set in init_voucher_balance
(cherry picked from commit 3b36ce560c)
2024-12-23 08:51:37 +00:00
vishakhdesai
197b13eb87 fix: update correct cost center in Accounts Receivable Report
(cherry picked from commit 09776e9a5a)
2024-12-23 08:51:37 +00:00
ruthra kumar
0986434871 Merge pull request #44837 from frappe/mergify/bp/version-14-hotfix/pr-44751
fix: buying rate for service item in gross profit report (backport #44751)
2024-12-23 12:08:12 +05:30
ljain112
7fa3a789b1 fix: buying rate for service item in gross profit report
(cherry picked from commit 8d6e79a16f)
2024-12-23 06:16:21 +00:00
ruthra kumar
647f039598 Merge pull request #44833 from frappe/mergify/bp/version-14-hotfix/pr-44644
fix: permissions for marking Quotation as lost (backport #44644)
2024-12-23 11:41:55 +05:30
ruthra kumar
00baa6a07f Merge pull request #44829 from frappe/mergify/bp/version-14-hotfix/pr-44826
fix: Remove typo from `Bank Account` on trash (backport #44826)
2024-12-23 11:26:29 +05:30
ruthra kumar
67ba639ae1 chore: resolve conflicts 2024-12-23 11:16:04 +05:30
barredterra
0179358f07 fix: permissions for marking Quotation as lost
(cherry picked from commit 4d5241486f)

# Conflicts:
#	erpnext/crm/doctype/competitor/competitor.json
#	erpnext/setup/doctype/quotation_lost_reason/quotation_lost_reason.json
2024-12-23 05:41:57 +00:00
mergify[bot]
4cb27b97b4 fix: closing stock balance permissions (backport #44791) (#44792)
* fix: closing stock balance permissions (#44791)

(cherry picked from commit 3662a6a41d)

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

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-12-23 10:54:34 +05:30
Abdeali Chharchhoda
422ada2726 fix: Remove typo
(cherry picked from commit ba28f6bf73)
2024-12-23 04:52:05 +00:00
mergify[bot]
d2ede713e4 fix: slow posting datetime update (backport #44799) (#44804)
fix: slow posting datetime update

Co-authored-by: Rohit Waghchaure <rohitw1991@gmail.com>
2024-12-20 13:37:00 +05:30
ruthra kumar
e48022f2fe Merge pull request #44777 from frappe/mergify/bp/version-14-hotfix/pr-44776
fix: added docs.frappe.io in documentation_url (backport #44776)
2024-12-18 17:53:22 +05:30
Nabin Hait
82a3fd13f2 fix: added docs.frappe.io in documentation_url (#44776)
(cherry picked from commit febdf4c61e)
2024-12-18 11:59:32 +00:00
Frappe PR Bot
561fe5a171 chore(release): Bumped to Version 14.78.4
## [14.78.4](https://github.com/frappe/erpnext/compare/v14.78.3...v14.78.4) (2024-12-18)

### Bug Fixes

* 'str' object has no attribute 'get_sql' ([bd20ff5](bd20ff581b))
2024-12-18 09:07:38 +00:00
ruthra kumar
f8f2c39ed5 Merge pull request #44768 from frappe/mergify/bp/version-14/pr-44765
fix: 'str' object has no attribute 'get_sql' (backport #44765)
2024-12-18 14:36:18 +05:30
ruthra kumar
becd64fccb Merge pull request #44769 from frappe/mergify/bp/version-14-hotfix/pr-44765
fix: 'str' object has no attribute 'get_sql' (backport #44765)
2024-12-18 14:34:21 +05:30
ljain112
510e3cebc9 fix: 'str' object has no attribute 'get_sql'
(cherry picked from commit 9a43acb65c)
2024-12-18 08:43:18 +00:00
ljain112
bd20ff581b fix: 'str' object has no attribute 'get_sql'
(cherry picked from commit 9a43acb65c)
2024-12-18 08:43:15 +00:00
ruthra kumar
b4faee24da Merge pull request #44759 from frappe/mergify/bp/version-14-hotfix/pr-44758
fix: use utility method to generate url (backport #44758)
2024-12-18 11:17:18 +05:30
ruthra kumar
d042330c30 fix: use utility method to generate url
(cherry picked from commit b970eb8b15)
2024-12-18 05:33:03 +00:00
Frappe PR Bot
d241862f14 chore(release): Bumped to Version 14.78.3
## [14.78.3](https://github.com/frappe/erpnext/compare/v14.78.2...v14.78.3) (2024-12-18)

### Bug Fixes

* allow all dispatch address for drop ship invoice ([0688c0a](0688c0ab6b))
* better indicator base amount for Tax Witholding in Journal Entry ([4a261b5](4a261b5cef))
* broken CI ([c86174d](c86174d0f2))
* incorrect status of the purchase receipt ([#44689](https://github.com/frappe/erpnext/issues/44689)) ([4b4d5df](4b4d5dfa63))
* purchase return entry issue (backport [#44721](https://github.com/frappe/erpnext/issues/44721)) ([#44736](https://github.com/frappe/erpnext/issues/44736)) ([744f345](744f3457de))
* remove invalid filter in Account Receivable report ([b571c7a](b571c7af87))
* set company bank account if default account not set in mode of payment ([2d12f2c](2d12f2cebd))
* Stock Entry uses incorrect company when generated from Pick List ([#44679](https://github.com/frappe/erpnext/issues/44679)) ([00898be](00898be8e4))
* update discount when pricing rule is changed ([0a7832d](0a7832d8d3))
* User permissions in financial statements ([8a9c457](8a9c457d37))
* using query.walk() for escaping ([3a7e335](3a7e335d4b))
* Wrong allocated_amount for sales_team in gross_profit report ([#42989](https://github.com/frappe/erpnext/issues/42989)) ([d83cc57](d83cc57e58))
2024-12-18 05:01:40 +00:00
ruthra kumar
8e34e20b27 Merge pull request #44740 from frappe/version-14-hotfix
chore: release v14
2024-12-18 10:30:20 +05:30
ruthra kumar
caea52c280 Merge pull request #44752 from frappe/mergify/bp/version-14-hotfix/pr-44695
fix: User permissions in financial statements (backport #44695)
2024-12-17 21:18:53 +05:30
ruthra kumar
4473b74e1b Merge pull request #44754 from frappe/mergify/bp/version-14-hotfix/pr-44708
fix: update discount when pricing rule is changed (backport #44708)
2024-12-17 21:10:41 +05:30
venkat102
0a7832d8d3 fix: update discount when pricing rule is changed
(cherry picked from commit 8338d1d5b4)
2024-12-17 15:13:03 +00:00
ljain112
3a7e335d4b fix: using query.walk() for escaping
(cherry picked from commit 5ea131c763)
2024-12-17 15:09:08 +00:00
ljain112
8a9c457d37 fix: User permissions in financial statements
(cherry picked from commit a626372d66)
2024-12-17 15:09:08 +00:00
ruthra kumar
13be763105 Merge pull request #44749 from ruthra-kumar/backport_v14_pr_44581
fix: allow all dispatch address for drop ship invoice (backport #44581)
2024-12-17 20:39:05 +05:30
ruthra kumar
0688c0ab6b fix: allow all dispatch address for drop ship invoice 2024-12-17 20:34:10 +05:30
ruthra kumar
7e00e55d18 Merge pull request #44734 from frappe/mergify/bp/version-14-hotfix/pr-44660
fix: better indicator base amount for Tax Witholding in Journal Entry (backport #44660)
2024-12-17 17:45:43 +05:30
ruthra kumar
ecca59c8a8 Merge pull request #44747 from frappe/mergify/bp/version-14-hotfix/pr-44685
fix: set company bank account if default account not set in mode of p… (backport #44685)
2024-12-17 17:29:15 +05:30
venkat102
2d12f2cebd fix: set company bank account if default account not set in mode of payment
(cherry picked from commit 91c7e3d5f3)
2024-12-17 11:50:04 +00:00
ruthra kumar
c86174d0f2 fix: broken CI
- pin ubuntu-22.04
2024-12-17 16:28:15 +05:30
mergify[bot]
744f3457de fix: purchase return entry issue (backport #44721) (#44736)
fix: purchase return entry issue (#44721)

(cherry picked from commit 1f5d7072e7)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-12-17 14:48:59 +05:30
Smit Vora
4a261b5cef fix: better indicator base amount for Tax Witholding in Journal Entry
(cherry picked from commit 56a0a0db18)
2024-12-17 06:20:32 +00:00
ruthra kumar
bad744194f Merge pull request #44699 from ruthra-kumar/backport_v14_pr_44676
fix: remove invalid filter in Account Receivable report (backport #44676)
2024-12-13 17:53:28 +05:30
ljain112
b571c7af87 fix: remove invalid filter in Account Receivable report 2024-12-13 17:29:17 +05:30
rohitwaghchaure
4b4d5dfa63 fix: incorrect status of the purchase receipt (#44689) 2024-12-13 12:44:03 +05:30
ruthra kumar
4c8b4419b6 Merge pull request #44687 from ruthra-kumar/backport_v14_pr_42989
fix: Wrong allocated_amount for sales_team in gross_profit report (backport #43989)
2024-12-13 12:39:28 +05:30
Diógenes Souza
d83cc57e58 fix: Wrong allocated_amount for sales_team in gross_profit report (#42989)
* fix: Wrong allocated_amount for sales_team in gross_profit report

* style: Removes whitespaces

---------

Co-authored-by: ruthra kumar <ruthra@erpnext.com>
2024-12-13 12:01:04 +05:30
Nicolas Pereira
00898be8e4 fix: Stock Entry uses incorrect company when generated from Pick List (#44679) 2024-12-13 10:21:30 +05:30
Frappe PR Bot
225d4ccb63 chore(release): Bumped to Version 14.78.2
## [14.78.2](https://github.com/frappe/erpnext/compare/v14.78.1...v14.78.2) (2024-12-12)

### Bug Fixes

* add docstatus for translation ([0a4090a](0a4090a771))
* add label strings for translation in pos_controller.js ([8233e22](8233e22d8b))
* add labels for translation in production_analytics.py ([1a7f195](1a7f195d8f))
* add labels for translation in purchase_order_analysis.py ([f9974f9](f9974f9eb0))
* add labels for translation in quality_inspection_summary.py ([9986c26](9986c26a0c))
* add labels for translation in sales_order_analysis.py ([bad1b2a](bad1b2a164))
* add labels for translation in sales_order.js ([3620dda](3620ddab34))
* add string for translation in delayed_tasks_summary.py ([ce04f6d](ce04f6d7c9))
* add strings for translation in pos_item_cart.js ([1f0f31a](1f0f31a773))
* add strings for translation payment_terms_status_for_sales_order.py ([17dad01](17dad01ab7))
* add title for translation in  asset.js ([aed90f7](aed90f73d7))
* **Bank Transaction:** error in party matching should not block submitting (backport [#44416](https://github.com/frappe/erpnext/issues/44416)) ([#44573](https://github.com/frappe/erpnext/issues/44573)) ([ebc8bed](ebc8bede7f))
* description overwrite on qty change (backport [#44606](https://github.com/frappe/erpnext/issues/44606)) ([#44608](https://github.com/frappe/erpnext/issues/44608)) ([a460bf9](a460bf9433))
* incorrect stock UOM for BOM raw materials (backport [#44528](https://github.com/frappe/erpnext/issues/44528)) ([#44530](https://github.com/frappe/erpnext/issues/44530)) ([ecc756b](ecc756bd52))
* inv dimensions fields not creating for standard doctype (backport [#44504](https://github.com/frappe/erpnext/issues/44504)) ([#44514](https://github.com/frappe/erpnext/issues/44514)) ([c502b56](c502b562db))
* prevent set_payment_schedule on return documents ([3c8b637](3c8b637a8b))
* required by date in the reorder material request (backport [#44497](https://github.com/frappe/erpnext/issues/44497)) ([#44508](https://github.com/frappe/erpnext/issues/44508)) ([15e3663](15e3663633))
* strings for translation in pos_past_order_summary.js ([dfbb227](dfbb2279b6))
* variant qty while making work order from BOM (backport [#44548](https://github.com/frappe/erpnext/issues/44548)) ([#44550](https://github.com/frappe/erpnext/issues/44550)) ([e2aedc8](e2aedc85b4))
2024-12-12 10:37:15 +00:00
ruthra kumar
e09232234e Merge pull request #44639 from frappe/version-14-hotfix
chore: release v14
2024-12-12 16:05:12 +05:30
mergify[bot]
a460bf9433 fix: description overwrite on qty change (backport #44606) (#44608)
* fix: description overwrite on qty change (#44606)

(cherry picked from commit 9ad79625e0)

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

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-12-09 19:12:09 +05:30
ruthra kumar
30151da57c Merge pull request #44611 from ruthra-kumar/backport_v14_44495
fix: prevent set_payment_schedule on return documents
2024-12-09 13:34:15 +05:30
ruthra kumar
3c8b637a8b fix: prevent set_payment_schedule on return documents 2024-12-09 13:00:56 +05:30
ruthra kumar
6fa338bfb7 Merge pull request #44602 from frappe/mergify/bp/version-14-hotfix/pr-44582
fix: add doc.status for translation (backport #44582)
2024-12-09 12:56:05 +05:30
ruthra kumar
9664790272 chore: fix linter 2024-12-09 12:08:54 +05:30
mahsem
0a4090a771 fix: add docstatus for translation
(cherry picked from commit dda272220b)
2024-12-09 05:51:35 +00:00
mergify[bot]
ebc8bede7f fix(Bank Transaction): error in party matching should not block submitting (backport #44416) (#44573)
fix(Bank Transaction): error in party matching should not block submitting (#44416)

(cherry picked from commit 72256565bb)

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2024-12-06 11:57:52 +01:00
ruthra kumar
19b64496b1 Merge pull request #44571 from frappe/mergify/bp/version-14-hotfix/pr-44538
fix: add labels for translation in purchase_order_analysis.py (backport #44538)
2024-12-06 12:21:04 +05:30
mahsem
f9974f9eb0 fix: add labels for translation in purchase_order_analysis.py
(cherry picked from commit 342a398bec)
2024-12-06 06:49:54 +00:00
ruthra kumar
0eccb01b7b Merge pull request #44565 from frappe/mergify/bp/version-14-hotfix/pr-44558
fix: add string for translation in delayed_tasks_summary.py (backport #44558)
2024-12-06 12:18:52 +05:30
mahsem
ce04f6d7c9 fix: add string for translation in delayed_tasks_summary.py
(cherry picked from commit 84b54f549a)
2024-12-06 12:17:50 +05:30
ruthra kumar
424d585962 Merge pull request #44569 from frappe/mergify/bp/version-14-hotfix/pr-44539
fix: add labels for translation in production_analytics.py (backport #44539)
2024-12-06 12:16:58 +05:30
mahsem
1a7f195d8f fix: add labels for translation in production_analytics.py
(cherry picked from commit 9b09116576)
2024-12-06 06:46:26 +00:00
ruthra kumar
5b0222615d Merge pull request #44567 from frappe/mergify/bp/version-14-hotfix/pr-44541
fix: add labels for translation in quality_inspection_summary.py (backport #44541)
2024-12-06 12:15:39 +05:30
mahsem
9986c26a0c fix: add labels for translation in quality_inspection_summary.py
(cherry picked from commit 6ff4704345)
2024-12-06 06:44:58 +00:00
ruthra kumar
e0c1e3474e Merge pull request #44563 from frappe/mergify/bp/version-14-hotfix/pr-44559
fix: add strings for translation payment_terms_status_for_sales_order.py (backport #44559)
2024-12-06 12:12:57 +05:30
mahsem
17dad01ab7 fix: add strings for translation payment_terms_status_for_sales_order.py
(cherry picked from commit 7d244051c8)
2024-12-06 06:38:29 +00:00
ruthra kumar
9515625135 Merge pull request #44561 from frappe/mergify/bp/version-14-hotfix/pr-44560
fix: add labels for translation in sales_order_analysis.py (backport #44560)
2024-12-06 12:07:05 +05:30
mahsem
bad1b2a164 fix: add labels for translation in sales_order_analysis.py
(cherry picked from commit 8a554a5538)
2024-12-06 06:09:28 +00:00
mergify[bot]
e2aedc85b4 fix: variant qty while making work order from BOM (backport #44548) (#44550)
fix: variant qty while making work order from BOM (#44548)

(cherry picked from commit 1571dff3ef)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-12-05 16:01:49 +05:30
Khushi Rawat
68fe3b4165 Merge pull request #44543 from frappe/mergify/bp/version-14-hotfix/pr-44537
fix: add title for translation in  asset.js (backport #44537)
2024-12-05 15:42:11 +05:30
mahsem
aed90f73d7 fix: add title for translation in asset.js
(cherry picked from commit 61439132a4)
2024-12-05 09:47:58 +00:00
Frappe PR Bot
7a2b07a2a3 chore(release): Bumped to Version 14.78.1
## [14.78.1](https://github.com/frappe/erpnext/compare/v14.78.0...v14.78.1) (2024-12-05)

### Bug Fixes

* required by date in the reorder material request (backport [#44497](https://github.com/frappe/erpnext/issues/44497)) ([#44508](https://github.com/frappe/erpnext/issues/44508)) ([e0b8c27](e0b8c2788b))
2024-12-05 08:18:33 +00:00
rohitwaghchaure
86ff1545ce Merge pull request #44535 from frappe/mergify/bp/version-14/pr-44508
fix: required by date in the reorder material request (backport #44497) (backport #44508)
2024-12-05 13:47:02 +05:30
mergify[bot]
e0b8c2788b fix: required by date in the reorder material request (backport #44497) (#44508)
* fix: required by date in the reorder material request (#44497)

(cherry picked from commit 4001166ecc)

# Conflicts:
#	erpnext/stock/doctype/stock_entry/test_stock_entry.py

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
(cherry picked from commit 15e3663633)
2024-12-05 07:24:59 +00:00
mergify[bot]
ecc756bd52 fix: incorrect stock UOM for BOM raw materials (backport #44528) (#44530)
fix: incorrect stock UOM for BOM raw materials (#44528)

fix: incorrect stock uom for BOM raw materials
(cherry picked from commit 5413cf9f1f)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-12-05 12:51:39 +05:30
ruthra kumar
0891399916 Merge pull request #44527 from frappe/mergify/bp/version-14-hotfix/pr-44503
fix: add strings for translation in pos_item_cart.js (backport #44503)
2024-12-05 10:24:51 +05:30
ruthra kumar
1436fda041 Merge pull request #44519 from frappe/mergify/bp/version-14-hotfix/pr-44500
fix: add labels for translation in sales_order.js (backport #44500)
2024-12-05 10:19:02 +05:30
ruthra kumar
639a71ffd0 chore: linter fix
(cherry picked from commit 31efaf6dbf)
2024-12-05 04:49:01 +00:00
mahsem
1f0f31a773 fix: add strings for translation in pos_item_cart.js
(cherry picked from commit 4b72b60f1a)
2024-12-05 04:49:00 +00:00
ruthra kumar
3bb4699754 Merge pull request #44525 from frappe/mergify/bp/version-14-hotfix/pr-44512
fix: add label strings for translation in pos_controller.js (backport #44512)
2024-12-05 10:17:40 +05:30
mahsem
3620ddab34 fix: add labels for translation in sales_order.js
(cherry picked from commit d544328ffe)
2024-12-05 10:16:48 +05:30
mahsem
8233e22d8b fix: add label strings for translation in pos_controller.js
(cherry picked from commit bd77a5557d)
2024-12-05 04:42:44 +00:00
ruthra kumar
1f293edc9e Merge pull request #44523 from frappe/mergify/bp/version-14-hotfix/pr-44501
fix: strings for translation in pos_past_order_summary.js (backport #44501)
2024-12-05 10:12:41 +05:30
ruthra kumar
13cd12e1e2 Merge pull request #44521 from frappe/mergify/bp/version-14-hotfix/pr-42081
Use better description in pos_payment.js (backport #42081)
2024-12-05 10:12:11 +05:30
mahsem
dfbb2279b6 fix: strings for translation in pos_past_order_summary.js
(cherry picked from commit 23c846d4b9)
2024-12-05 04:31:29 +00:00
mahsem
e1d288321f refactor: translatable label on pos payments (#42081)
* Use better description in pos_payment.js

Use Change Amount instead of Change and To Be Paid in pos_payment.js and be consistent with other strings

* change_amount_pos_payment.js

(cherry picked from commit 138ffc4e93)
2024-12-05 04:30:46 +00:00
mergify[bot]
c502b562db fix: inv dimensions fields not creating for standard doctype (backport #44504) (#44514)
fix: inv dimensions fields not creating for standard doctype (#44504)

(cherry picked from commit 353610ce61)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-12-05 09:45:29 +05:30
mergify[bot]
15e3663633 fix: required by date in the reorder material request (backport #44497) (#44508)
* fix: required by date in the reorder material request (#44497)

(cherry picked from commit 4001166ecc)

# Conflicts:
#	erpnext/stock/doctype/stock_entry/test_stock_entry.py

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-12-04 19:03:09 +05:30
Frappe PR Bot
c1a1346e2a chore(release): Bumped to Version 14.78.0
# [14.78.0](https://github.com/frappe/erpnext/compare/v14.77.3...v14.78.0) (2024-12-04)

### Bug Fixes

* Add filter for `outstanding_amount` to fetch open PRs ([7e847f2](7e847f27dd))
* Add translation for showing mandatory fields in error msg ([de5b253](de5b253215))
* added fieldname to avoid fieldname to translate ([d7caa8d](d7caa8d51b))
* Added translation for `Account` column ([2061c7c](2061c7ca46))
* adjusted incoming rate for zero rated item in purchase receipt ([d7583e3](d7583e3993))
* correct buying amount for product bundel ([91f6393](91f6393104))
* Dashboard for `Payment Request` ([c911a70](c911a70a84))
* Data Should be Computed in Backend to Maintain Consistent Behaviour ([#44195](https://github.com/frappe/erpnext/issues/44195)) ([8529eb4](8529eb4573))
* handle multi currency in common party journal entry ([bed6dab](bed6dabecb))
* incorrect Gross Margin on project ([#44461](https://github.com/frappe/erpnext/issues/44461)) ([5533592](5533592b2b))
* remove queries ([7d724d7](7d724d7647))
* show "Send SMS" only when enabled (backport [#43941](https://github.com/frappe/erpnext/issues/43941)) ([#43969](https://github.com/frappe/erpnext/issues/43969)) ([9ae04df](9ae04dfed3))
* source warehouse not set in required items of WO (backport [#44426](https://github.com/frappe/erpnext/issues/44426)) ([#44433](https://github.com/frappe/erpnext/issues/44433)) ([cbce4e7](cbce4e72d9))
* Translate `Party Account` column label ([c4103d2](c4103d26be))
* typeerror on transaction.js ([147eb04](147eb047aa))

### Features

* add Company Contact Person in selling transactions (backport [#44362](https://github.com/frappe/erpnext/issues/44362)) ([#44397](https://github.com/frappe/erpnext/issues/44397)) ([78ab44c](78ab44ce1a))

### Performance Improvements

* cache product bundle items at document level ([#44440](https://github.com/frappe/erpnext/issues/44440)) ([df0dac5](df0dac5610))
* reduce queries during transaction save ([f884cf8](f884cf8a5e))

### Reverts

* remove default `Payment Request` indicator color ([9baaaca](9baaaca924))
2024-12-04 04:36:03 +00:00
ruthra kumar
1e02d06424 Merge pull request #44482 from frappe/version-14-hotfix
chore: release v14
2024-12-04 10:04:44 +05:30
ruthra kumar
2812eae589 Merge pull request #44484 from frappe/mergify/bp/version-14-hotfix/pr-44467
fix: Multiple Fixes in Gross Profit Report (backport #44467)
2024-12-03 19:15:34 +05:30
ruthra kumar
a94e3cd433 chore: fix typo
(cherry picked from commit fc0122ce76)
2024-12-03 12:00:36 +00:00
ljain112
91f6393104 fix: correct buying amount for product bundel
(cherry picked from commit 4e6a5893e7)
2024-12-03 12:00:36 +00:00
ljain112
7d724d7647 fix: remove queries
(cherry picked from commit a86b223aed)
2024-12-03 12:00:35 +00:00
ruthra kumar
e4b811be23 Merge pull request #44469 from frappe/mergify/bp/version-14-hotfix/pr-44461
fix: incorrect Gross Margin on project (backport #44461)
2024-12-03 15:32:04 +05:30
rohitwaghchaure
ccd7992e99 chore: fix conflicts 2024-12-03 15:08:38 +05:30
rohitwaghchaure
86ff3e0c10 chore: fix conflicts 2024-12-03 15:06:16 +05:30
ruthra kumar
96fe3f4a2c Merge pull request #44479 from frappe/mergify/bp/version-14-hotfix/pr-44415
fix: adjusted incoming rate for zero rated item in purchase receipt (backport #44415)
2024-12-03 14:48:24 +05:30
ljain112
d7583e3993 fix: adjusted incoming rate for zero rated item in purchase receipt
(cherry picked from commit 3182c6981c)
2024-12-03 08:56:06 +00:00
rohitwaghchaure
5533592b2b fix: incorrect Gross Margin on project (#44461)
(cherry picked from commit 7de9c14a2c)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice/sales_invoice.py
#	erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
2024-12-02 10:10:31 +00:00
ruthra kumar
ba19e06e64 Merge pull request #44464 from frappe/mergify/bp/version-14-hotfix/pr-44412
fix: handle multi currency in common party journal entry (backport #44412)
2024-12-02 14:03:21 +05:30
ruthra kumar
1988c23539 chore: resolve conflict 2024-12-02 13:42:27 +05:30
ruthra kumar
70cac3186b Merge pull request #44462 from frappe/mergify/bp/version-14-hotfix/pr-44437
fix: Added translation for `Account` column (backport #44437)
2024-12-02 13:39:59 +05:30
ljain112
bed6dabecb fix: handle multi currency in common party journal entry
(cherry picked from commit e371f68d66)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
2024-12-02 07:10:05 +00:00
Abdeali Chharchhoda
c4103d26be fix: Translate Party Account column label
(cherry picked from commit a4f8315602)
2024-12-02 06:53:37 +00:00
Abdeali Chharchhoda
2061c7ca46 fix: Added translation for Account column
(cherry picked from commit de6cbd382f)
2024-12-02 06:53:37 +00:00
Sagar Vora
84bd0c8c50 Merge pull request #44446 from frappe/mergify/bp/version-14-hotfix/pr-44443
perf: reduce queries during transaction save (backport #44443)
2024-11-30 00:47:53 +05:30
Sagar Vora
f884cf8a5e perf: reduce queries during transaction save
(cherry picked from commit b6b8a06fda)
2024-11-29 19:17:26 +00:00
Sagar Vora
54782d41be Merge pull request #44444 from frappe/mergify/bp/version-14-hotfix/pr-44439
fix: added fieldname to avoid fieldname to translate (backport #44439)
2024-11-30 00:29:47 +05:30
Abdeali Chharchhoda
d7caa8d51b fix: added fieldname to avoid fieldname to translate
(cherry picked from commit b80022133c)
2024-11-29 18:56:31 +00:00
Sagar Vora
9e8150554f Merge pull request #44441 from frappe/mergify/bp/version-14-hotfix/pr-44440
perf: cache product bundle items at document level (backport #44440)
2024-11-29 23:04:01 +05:30
Sagar Vora
df0dac5610 perf: cache product bundle items at document level (#44440)
(cherry picked from commit 6de7320ef4)
2024-11-29 17:22:51 +00:00
mergify[bot]
cbce4e72d9 fix: source warehouse not set in required items of WO (backport #44426) (#44433)
* fix: source warehouse not set in required items of WO (#44426)

fix: source warehouse not set in required items of WO on data import
(cherry picked from commit 4050ea07eb)

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

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-11-29 18:13:25 +05:30
ruthra kumar
2c2a204a06 Merge pull request #44390 from frappe/mergify/bp/version-14-hotfix/pr-44386
fix: Add translation for showing mandatory fields in error msg (backport #44386)
2024-11-29 15:38:52 +05:30
ruthra kumar
da5b57e4af Merge pull request #44424 from frappe/mergify/bp/version-14-hotfix/pr-44302
fix: Minor Updates in `Payment Request` and `Payment Entry`  (backport #44302)
2024-11-29 15:38:11 +05:30
Abdeali Chharchhoda
de5b253215 fix: Add translation for showing mandatory fields in error msg
(cherry picked from commit f42ec6a124)
2024-11-29 14:57:21 +05:30
ruthra kumar
a8ef90b40c chore: resolve conflict 2024-11-29 14:51:46 +05:30
Abdeali Chharchhoda
7e847f27dd fix: Add filter for outstanding_amount to fetch open PRs
(cherry picked from commit 214dfab269)
2024-11-29 09:11:07 +00:00
Abdeali Chharchhoda
eb809847f1 refactor: Move PR link filters to client side
(cherry picked from commit 2db2c8bce1)
2024-11-29 09:11:06 +00:00
Abdeali Chharchhoda
9baaaca924 revert: remove default Payment Request indicator color
(cherry picked from commit 37ceb09955)

# Conflicts:
#	erpnext/accounts/doctype/payment_request/payment_request_list.js
2024-11-29 09:11:06 +00:00
Abdeali Chharchhoda
c911a70a84 fix: Dashboard for Payment Request
(cherry picked from commit 91955e27c3)
2024-11-29 09:11:06 +00:00
Abdeali Chharchhoda
e2728db5c2 refactor: Used object to get payment request status indicator
(cherry picked from commit e1c4d6e1e6)

# Conflicts:
#	erpnext/accounts/doctype/payment_request/payment_request_list.js
2024-11-29 09:11:06 +00:00
Smit Vora
f6f1052a80 Merge pull request #44409 from frappe/mergify/bp/version-14-hotfix/pr-44195
fix: Data Should be Computed in Backend to Maintain Consistent Behaviour (backport #44195)
2024-11-28 16:27:16 +05:30
Ninad Parikh
8529eb4573 fix: Data Should be Computed in Backend to Maintain Consistent Behaviour (#44195)
(cherry picked from commit 69bd90b038)
2024-11-28 10:31:28 +00:00
ruthra kumar
a73fedbaca Merge pull request #44406 from frappe/mergify/bp/version-14-hotfix/pr-44405
fix: typeerror on transaction.js (backport #44405)
2024-11-28 15:13:27 +05:30
ruthra kumar
147eb047aa fix: typeerror on transaction.js
(cherry picked from commit 46ce8780f2)
2024-11-28 15:02:35 +05:30
mergify[bot]
9ae04dfed3 fix: show "Send SMS" only when enabled (backport #43941) (#43969)
fix: show "Send SMS" only when enabled (#43941)

(cherry picked from commit 65088cbb1b)

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2024-11-27 23:52:12 +01:00
mergify[bot]
78ab44ce1a feat: add Company Contact Person in selling transactions (backport #44362) (#44397)
* feat: add Company Contact Person in selling transactions (#44362)

(cherry picked from commit f6776c7d6b)

* chore: resolve conflicts

---------

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2024-11-27 17:09:09 +01:00
Frappe PR Bot
0079bd282e chore(release): Bumped to Version 14.77.3
## [14.77.3](https://github.com/frappe/erpnext/compare/v14.77.2...v14.77.3) (2024-11-27)

### Bug Fixes

*  correct placeholder index in message ([d7e6b0e](d7e6b0e1b7))
* added Stock UOM field for RM in work order (backport [#44185](https://github.com/frappe/erpnext/issues/44185)) ([#44236](https://github.com/frappe/erpnext/issues/44236)) ([ee647f2](ee647f2381))
* billed qty and received amount in PO analysis report (backport [#44349](https://github.com/frappe/erpnext/issues/44349)) ([#44353](https://github.com/frappe/erpnext/issues/44353)) ([6aebe98](6aebe98020))
* change asset status correctly ([841df3b](841df3b1ce))
* Get submitted documents in validate_for_closed_fiscal_year ([b9efeeb](b9efeeb71a))
* gp for return invoice ([6e56343](6e563438bd))
* include current invoice amount when tax_on_excess_amount is checked ([071adcf](071adcf291))
* incorrect GL entry on sale of fully depreciated asset ([cc5ada1](cc5ada194e))
* make free qty round on large transaction qty ([2eb7002](2eb70026ae))
* patch ([#44191](https://github.com/frappe/erpnext/issues/44191)) ([3643c60](3643c60c67))
* test case ([7eafe7f](7eafe7f853))
* unify company address query in sales transactions (backport [#44361](https://github.com/frappe/erpnext/issues/44361)) ([#44366](https://github.com/frappe/erpnext/issues/44366)) ([fef7cfb](fef7cfb888))
* update gross profit for returned invoices ([0eb5e00](0eb5e001e1))
* Used `Fiscal Dates` instead of year's `Start` and `End` dates ([4f806f9](4f806f9a72))
2024-11-27 15:25:06 +00:00
ruthra kumar
2db54f0bb1 Merge pull request #44342 from frappe/version-14-hotfix
chore: release v14
2024-11-27 20:53:38 +05:30
ruthra kumar
627a50e7bc Merge pull request #44384 from frappe/mergify/bp/version-14-hotfix/pr-44323
fix: update gross profit for returned invoices (backport #44323)
2024-11-27 17:21:13 +05:30
ljain112
7eafe7f853 fix: test case
(cherry picked from commit af5a3e5a48)
2024-11-27 11:20:16 +00:00
ljain112
6e563438bd fix: gp for return invoice
(cherry picked from commit 00403515a8)
2024-11-27 11:20:16 +00:00
ljain112
0eb5e001e1 fix: update gross profit for returned invoices
(cherry picked from commit 8a42601e99)
2024-11-27 11:20:15 +00:00
ruthra kumar
0dfb9ddb85 Merge pull request #44380 from frappe/mergify/bp/version-14-hotfix/pr-44377
fix:  correct placeholder index in message (backport #44377)
2024-11-27 15:38:39 +05:30
ljain112
d7e6b0e1b7 fix: correct placeholder index in message
(cherry picked from commit d61cb9a4bf)
2024-11-27 09:26:10 +00:00
Raffael Meyer
fef7cfb888 fix: unify company address query in sales transactions (backport #44361) (#44366)
* fix: unify company address query in sales transactions (backport #44361)

* style: add whitespace
2024-11-26 21:11:27 +01:00
Khushi Rawat
aa442da2f6 Merge pull request #44329 from khushi8112/incorrect-gl-entry-on-asset-sale
fix: incorrect GL entry on sale of fully depreciated asset
2024-11-27 01:09:33 +05:30
mergify[bot]
6aebe98020 fix: billed qty and received amount in PO analysis report (backport #44349) (#44353)
* fix: billed qty and received amount in PO analysis report (#44349)

(cherry picked from commit 2ab7ec5437)

# Conflicts:
#	erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-11-26 21:18:38 +05:30
Khushi Rawat
841df3b1ce fix: change asset status correctly 2024-11-26 14:06:07 +05:30
Khushi Rawat
cc5ada194e fix: incorrect GL entry on sale of fully depreciated asset 2024-11-26 01:39:42 +05:30
Smit Vora
edccb746f0 Merge pull request #44294 from Abdeali099/fix-fiscal-dates 2024-11-25 16:15:04 +05:30
ruthra kumar
506fd97a2c Merge pull request #44314 from frappe/mergify/bp/version-14-hotfix/pr-44297
refactor: added translate function for some columns of report (backport #44297)
2024-11-25 10:39:49 +05:30
Abdeali Chharchhoda
6c9485fb13 refactor: added translate function for some columns of report
(cherry picked from commit e545c913b5)
2024-11-25 04:39:26 +00:00
Abdeali Chharchhoda
4f806f9a72 fix: Used Fiscal Dates instead of year's Start and End dates 2024-11-22 18:09:23 +05:30
Frappe PR Bot
4723fbfd57 chore(release): Bumped to Version 14.77.2
## [14.77.2](https://github.com/frappe/erpnext/compare/v14.77.1...v14.77.2) (2024-11-22)

### Bug Fixes

* patch ([#44191](https://github.com/frappe/erpnext/issues/44191)) ([d1295d1](d1295d1c79))
2024-11-22 11:28:10 +00:00
rohitwaghchaure
ab49ee2e05 Merge pull request #44289 from frappe/mergify/bp/version-14/pr-44288
fix: patch (backport #44191) (backport #44288)
2024-11-22 16:56:24 +05:30
rohitwaghchaure
d1295d1c79 fix: patch (#44191)
(cherry picked from commit 495528a758)
(cherry picked from commit 3643c60c67)
2024-11-22 10:56:37 +00:00
ruthra kumar
8a45258778 Merge pull request #44286 from frappe/mergify/bp/version-14-hotfix/pr-44246
fix: Get submitted documents in validate_for_closed_fiscal_year (backport #44246)
2024-11-22 16:25:37 +05:30
rohitwaghchaure
d10a9d3225 Merge pull request #44288 from frappe/mergify/bp/version-14-hotfix/pr-44191
fix: patch (backport #44191)
2024-11-22 16:25:03 +05:30
rohitwaghchaure
3643c60c67 fix: patch (#44191)
(cherry picked from commit 495528a758)
2024-11-22 10:51:44 +00:00
ruthra kumar
92e252e37a Merge pull request #44284 from frappe/mergify/bp/version-14-hotfix/pr-44266
fix: make free qty round on large transaction qty (backport #44266)
2024-11-22 16:15:19 +05:30
vimalraj27
b9efeeb71a fix: Get submitted documents in validate_for_closed_fiscal_year
(cherry picked from commit c607e5f940)
2024-11-22 15:56:34 +05:30
venkat102
d695fb5723 test: add unit test to validate free qty round on large transaction qty
(cherry picked from commit 013a6fc6ec)
2024-11-22 10:20:54 +00:00
venkat102
2eb70026ae fix: make free qty round on large transaction qty
(cherry picked from commit f9b8165385)
2024-11-22 10:20:53 +00:00
Frappe PR Bot
46a8500361 chore(release): Bumped to Version 14.77.1
## [14.77.1](https://github.com/frappe/erpnext/compare/v14.77.0...v14.77.1) (2024-11-22)

### Bug Fixes

* include current invoice amount when tax_on_excess_amount is checked ([43f59da](43f59da0a9))
2024-11-22 06:01:07 +00:00
ruthra kumar
88ba24f1cf Merge pull request #44270 from frappe/mergify/bp/version-14/pr-44194
fix: include current invoice amount when tax_on_excess_amount is checked (backport #44194)
2024-11-22 11:29:50 +05:30
ruthra kumar
18cbc956bb Merge pull request #44271 from frappe/mergify/bp/version-14-hotfix/pr-44194
fix: include current invoice amount when tax_on_excess_amount is checked (backport #44194)
2024-11-22 11:29:21 +05:30
venkat102
47e4fd3c76 test: add unit test for tax on excess amount
(cherry picked from commit 4820273595)
2024-11-22 05:37:26 +00:00
venkat102
071adcf291 fix: include current invoice amount when tax_on_excess_amount is checked
(cherry picked from commit b74f2896cd)
2024-11-22 05:37:26 +00:00
venkat102
87bb403159 test: add unit test for tax on excess amount
(cherry picked from commit 4820273595)
2024-11-22 05:37:12 +00:00
venkat102
43f59da0a9 fix: include current invoice amount when tax_on_excess_amount is checked
(cherry picked from commit b74f2896cd)
2024-11-22 05:37:12 +00:00
mergify[bot]
ee647f2381 fix: added Stock UOM field for RM in work order (backport #44185) (#44236)
* fix: added Stock UOM field for RM in work order (#44185)

fix: added UOM field for RM in work order
(cherry picked from commit cc571aca8f)

# Conflicts:
#	erpnext/manufacturing/doctype/work_order_item/work_order_item.json
#	erpnext/manufacturing/doctype/work_order_item/work_order_item.py
#	erpnext/patches.txt

* chore: fix conflicts

* chore: fix conflicts

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-11-21 14:52:18 +05:30
Frappe PR Bot
2c865bcd49 chore(release): Bumped to Version 14.77.0
# [14.77.0](https://github.com/frappe/erpnext/compare/v14.76.0...v14.77.0) (2024-11-20)

### Bug Fixes

* added test cases ([5fa4fd8](5fa4fd8825))
* apply posting date sorting to invoices in Payment Reconciliation similar to payments ([cc9b22c](cc9b22ce9f))
* backport german translations from develop ([#44046](https://github.com/frappe/erpnext/issues/44046)) ([c23868a](c23868a14d))
* broken apply on other item pricing rule ([f03e301](f03e301250))
* broken UI on currency exchange ([36898f6](36898f6797))
* check if pricing rule matches with coupon code ([#44104](https://github.com/frappe/erpnext/issues/44104)) ([24b5b3c](24b5b3c8e0))
* correctly set 'cannot_add_rows' property on allocations table field ([137ef78](137ef78d96))
* disable conversion to user tz for sales order calender ([356da69](356da69179))
* Get Entries not showing accounts with no gain or loss in Exchange Rate Revaluation issue ([33e835c](33e835c4d3))
* linters ([c3e2ff2](c3e2ff2fa5))
* non group pos warehouse ([ec40131](ec40131d4d))
* payment reco for jv with negative dr or cr amount ([23fb4f3](23fb4f348f))
* remove trailing whitespace ([1019d98](1019d98c5a))
* set conversion factor before applying price list ([e09f101](e09f101336))
* set default party type in Payment Entry ([f3cbbef](f3cbbef346))
* stock ledger variance report filter options (backport [#44137](https://github.com/frappe/erpnext/issues/44137)) ([#44149](https://github.com/frappe/erpnext/issues/44149)) ([f871f08](f871f08f47))
* validate sales team to ensure all sales person are enabled ([bd0f11e](bd0f11ef4f))

### Features

* new DocTypes "Code List" and "Common Code" (backport [#43425](https://github.com/frappe/erpnext/issues/43425)) ([#44172](https://github.com/frappe/erpnext/issues/44172)) ([a7de8c1](a7de8c1143))
2024-11-20 08:37:41 +00:00
ruthra kumar
fc783f5acf Merge pull request #44210 from frappe/version-14-hotfix
chore: release v14
2024-11-20 14:06:14 +05:30
ruthra kumar
24126b03dd Merge pull request #44244 from frappe/mergify/bp/version-14-hotfix/pr-44197
fix: payment reco for jv with negative dr or cr amount (backport #44197)
2024-11-20 13:12:55 +05:30
ljain112
5fa4fd8825 fix: added test cases
(cherry picked from commit 6f9ea6422d)
2024-11-20 07:18:19 +00:00
ljain112
23fb4f348f fix: payment reco for jv with negative dr or cr amount
(cherry picked from commit fee79b9445)
2024-11-20 07:18:19 +00:00
ruthra kumar
227c912ece Merge pull request #44241 from frappe/mergify/bp/version-14-hotfix/pr-44240
fix: non group pos warehouse (backport #44240)
2024-11-20 12:17:33 +05:30
ruthra kumar
5bfbdb805c Merge pull request #44238 from frappe/mergify/bp/version-14-hotfix/pr-44220
refactor: Update `Payment Request` search query in PE's reference (backport #44220)
2024-11-20 12:16:21 +05:30
Nihantra C. Patel
ec40131d4d fix: non group pos warehouse
(cherry picked from commit d526be0394)
2024-11-20 06:41:34 +00:00
ruthra kumar
859672c419 Merge pull request #44232 from frappe/mergify/bp/version-14-hotfix/pr-44207
fix: validate sales team to ensure all sales person are enabled (backport #44207)
2024-11-20 11:51:56 +05:30
Abdeali Chharchhoda
e8c3617628 refactor: Update Payment Request search query in PE's reference
(cherry picked from commit 4ab3499a17)
2024-11-20 06:21:41 +00:00
ruthra kumar
09b21b8cb4 Merge pull request #44234 from frappe/mergify/bp/version-14-hotfix/pr-44203
fix: disable conversion to user tz for sales order calender (backport #44203)
2024-11-20 11:51:21 +05:30
ljain112
356da69179 fix: disable conversion to user tz for sales order calender
(cherry picked from commit cdf098c193)
2024-11-20 05:17:52 +00:00
ljain112
bd0f11ef4f fix: validate sales team to ensure all sales person are enabled
(cherry picked from commit 548dbb33eb)
2024-11-20 05:11:35 +00:00
ruthra kumar
9f3dfb3d18 Merge pull request #44216 from frappe/mergify/bp/version-14-hotfix/pr-44104
fix: check if pricing rule matches with coupon code (backport #44104)
2024-11-19 17:37:33 +05:30
Nikolas Beckel
24b5b3c8e0 fix: check if pricing rule matches with coupon code (#44104)
* fix: check if pricing rule matches with coupon code

* fix: correct linting error

(cherry picked from commit 9d31bf7647)
2024-11-19 11:41:48 +00:00
mergify[bot]
a7de8c1143 feat: new DocTypes "Code List" and "Common Code" (backport #43425) (#44172)
Co-authored-by: David <dgx.arnold@gmail.com>
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2024-11-18 20:59:41 +01:00
ruthra kumar
2fcab327aa Merge pull request #44181 from frappe/mergify/bp/version-14-hotfix/pr-44127
fix: set default Party Type based on Payment Type in Payment Entry (backport #44127)
2024-11-18 10:49:10 +05:30
vishakhdesai
f3cbbef346 fix: set default party type in Payment Entry
(cherry picked from commit 19222690d3)
2024-11-18 10:16:59 +05:30
ruthra kumar
339beff023 Merge pull request #44179 from frappe/mergify/bp/version-14-hotfix/pr-44147
fix: set conversion factor before applying price list (backport #44147)
2024-11-18 10:14:34 +05:30
ruthra kumar
85d398efcc Merge pull request #44177 from frappe/mergify/bp/version-14-hotfix/pr-44157
fix: apply "cannot_add_rows" directly to table field for more efficient solution (backport #44157)
2024-11-18 10:14:12 +05:30
vishakhdesai
e09f101336 fix: set conversion factor before applying price list
(cherry picked from commit 9749fe23cc)
2024-11-18 04:38:32 +00:00
UmakanthKaspa
f9b52b292e refactor: set 'cannot_add_rows' directly in the allocations table field (optimized approach)
(cherry picked from commit 5dd8eafdfc)
2024-11-18 04:36:12 +00:00
Türker Tunalı
8a94d7bea8 fix:Unable to pause job card (#42839)
fix: unable to pause job card

Pause operation creates "Value missing for: Completed Qty" error. That field was mandatory but it's non mandatory in v15.
2024-11-15 17:29:58 +05:30
mergify[bot]
f871f08f47 fix: stock ledger variance report filter options (backport #44137) (#44149)
fix: stock ledger variance report filter options (#44137)

(cherry picked from commit e8bbf6492f)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-11-15 15:05:03 +05:30
ruthra kumar
313ea3983f Merge pull request #44161 from frappe/mergify/bp/version-14-hotfix/pr-43414
fix: Get Entries not showing accounts with no gain or loss in Exchange Rate Revaluation issue fixed (backport #43414)
2024-11-15 13:09:35 +05:30
Vishakh Desai
c3e2ff2fa5 fix: linters
(cherry picked from commit 9cc22b4cac)
2024-11-15 07:18:49 +00:00
Vishakh Desai
33e835c4d3 fix: Get Entries not showing accounts with no gain or loss in Exchange Rate Revaluation issue
(cherry picked from commit 6de6f55b39)
2024-11-15 07:18:49 +00:00
ruthra kumar
c9fa4af4fe Merge pull request #44159 from frappe/mergify/bp/version-14-hotfix/pr-44158
fix: broken UI on currency exchange (backport #44158)
2024-11-15 12:28:18 +05:30
ruthra kumar
36898f6797 fix: broken UI on currency exchange
(cherry picked from commit e91b65e7bd)
2024-11-15 06:52:40 +00:00
ruthra kumar
b9a0f4fed8 Merge pull request #44154 from frappe/mergify/bp/version-14-hotfix/pr-44089
fix: apply posting date sorting to invoices in Payment Reconciliation similar to payments (backport #44089)
2024-11-15 10:54:40 +05:30
UmakanthKaspa
1019d98c5a fix: remove trailing whitespace
(cherry picked from commit d6703eb88b)
2024-11-15 05:00:03 +00:00
UmakanthKaspa
cc9b22ce9f fix: apply posting date sorting to invoices in Payment Reconciliation similar to payments
(cherry picked from commit 0bd83d920d)
2024-11-15 05:00:02 +00:00
ruthra kumar
f5135cd4a4 Merge pull request #44152 from frappe/mergify/bp/version-14-hotfix/pr-44148
Fix: Disable "Add Row" button in allocations table during UnReconcile process (backport #44148)
2024-11-15 10:22:43 +05:30
UmakanthKaspa
137ef78d96 fix: correctly set 'cannot_add_rows' property on allocations table field
(cherry picked from commit 13ca2700f8)
2024-11-15 04:47:46 +00:00
ruthra kumar
5452f8ac4a Merge pull request #44139 from frappe/mergify/bp/version-14-hotfix/pr-43189
fix: broken apply on other item (backport #43189)
2024-11-14 13:45:41 +05:30
ruthra kumar
f03e301250 fix: broken apply on other item pricing rule
(cherry picked from commit e5119a749c)
2024-11-14 07:54:10 +00:00
Raffael Meyer
c23868a14d fix: backport german translations from develop (#44046) 2024-11-13 18:17:50 +01:00
trustedcomputer
eb5505187e fix: header_img field schema 2024-07-22 12:35:46 -07:00
230 changed files with 6089 additions and 2288 deletions

View File

@@ -10,6 +10,7 @@ WEBSITE_REPOS = [
DOCUMENTATION_DOMAINS = [
"docs.erpnext.com",
"docs.frappe.io",
"frappeframework.com",
]

View File

@@ -16,7 +16,7 @@ concurrency:
jobs:
test:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
timeout-minutes: 60
name: Patch Test
@@ -59,7 +59,7 @@ jobs:
run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts
- name: Cache pip
uses: actions/cache@v2
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml') }}
@@ -68,7 +68,7 @@ jobs:
${{ runner.os }}-
- name: Cache node modules
uses: actions/cache@v2
uses: actions/cache@v4
env:
cache-name: cache-node-modules
with:
@@ -83,7 +83,7 @@ jobs:
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- uses: actions/cache@v2
- uses: actions/cache@v4
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}

View File

@@ -79,7 +79,7 @@ jobs:
run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts
- name: Cache pip
uses: actions/cache@v2
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml') }}
@@ -88,7 +88,7 @@ jobs:
${{ runner.os }}-
- name: Cache node modules
uses: actions/cache@v2
uses: actions/cache@v4
env:
cache-name: cache-node-modules
with:
@@ -103,7 +103,7 @@ jobs:
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- uses: actions/cache@v2
- uses: actions/cache@v4
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}

View File

@@ -66,7 +66,7 @@ jobs:
run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts
- name: Cache pip
uses: actions/cache@v2
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml') }}
@@ -75,7 +75,7 @@ jobs:
${{ runner.os }}-
- name: Cache node modules
uses: actions/cache@v2
uses: actions/cache@v4
env:
cache-name: cache-node-modules
with:
@@ -90,7 +90,7 @@ jobs:
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- uses: actions/cache@v2
- uses: actions/cache@v4
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}

View File

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

View File

@@ -94,8 +94,8 @@ frappe.ui.form.on("Account", {
function () {
frappe.route_options = {
account: frm.doc.name,
from_date: frappe.sys_defaults.year_start_date,
to_date: frappe.sys_defaults.year_end_date,
from_date: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
to_date: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2],
company: frm.doc.company,
};
frappe.set_route("query-report", "General Ledger");

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
@@ -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

@@ -279,8 +279,8 @@ frappe.treeview_settings["Account"] = {
click: function (node, btn) {
frappe.route_options = {
account: node.label,
from_date: frappe.sys_defaults.year_start_date,
to_date: frappe.sys_defaults.year_end_date,
from_date: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
to_date: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2],
company:
frappe.treeview_settings["Account"].treeview.page.fields_dict.company.get_value(),
};

View File

@@ -31,7 +31,8 @@
"label": "Reference Document Type",
"options": "DocType",
"read_only_depends_on": "eval:!doc.__islocal",
"reqd": 1
"reqd": 1,
"search_index": 1
},
{
"default": "0",

View File

@@ -25,6 +25,7 @@ class AccountingDimension(Document):
"Accounting Dimension Detail",
"Company",
"Account",
"Finance Book",
):
msg = _("Not allowed to create accounting dimension for {0}").format(self.document_type)
frappe.throw(msg)

View File

@@ -44,6 +44,7 @@
"section_break_jpd0",
"auto_reconcile_payments",
"stale_days",
"exchange_gain_loss_posting_date",
"invoicing_settings_tab",
"accounts_transactions_settings_section",
"over_billing_allowance",
@@ -72,6 +73,7 @@
"reports_tab",
"remarks_section",
"general_ledger_remarks_length",
"ignore_is_opening_check_for_reporting",
"column_break_lvjk",
"receivable_payable_remarks_length"
],
@@ -383,7 +385,7 @@
{
"fieldname": "section_break_jpd0",
"fieldtype": "Section Break",
"label": "Payment Reconciliations"
"label": "Payment Reconciliation Settings"
},
{
"default": "0",
@@ -462,6 +464,21 @@
"fieldname": "remarks_section",
"fieldtype": "Section Break",
"label": "Remarks Column Length"
},
{
"default": "Payment",
"description": "Only applies for Normal Payments",
"fieldname": "exchange_gain_loss_posting_date",
"fieldtype": "Select",
"label": "Posting Date Inheritance for Exchange Gain / Loss",
"options": "Invoice\nPayment\nReconciliation Date"
},
{
"default": "0",
"description": "Ignores legacy Is Opening field in GL Entry that allows adding opening balance post the system is in use while generating reports",
"fieldname": "ignore_is_opening_check_for_reporting",
"fieldtype": "Check",
"label": "Ignore Is Opening check for reporting"
}
],
"icon": "icon-cog",
@@ -469,7 +486,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2024-01-22 12:10:10.151819",
"modified": "2025-01-23 13:15:44.077853",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Settings",
@@ -498,4 +515,4 @@
"sort_order": "ASC",
"states": [],
"track_changes": 1
}
}

View File

@@ -21,7 +21,7 @@ class BankAccount(Document):
self.name = self.account_name + " - " + self.bank
def on_trash(self):
delete_contact_and_address("BankAccount", self.name)
delete_contact_and_address("Bank Account", self.name)
def validate(self):
self.validate_company()

View File

@@ -19,10 +19,15 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
},
onload: function (frm) {
if (!frm.doc.company) {
frm.set_value("company", frappe.defaults.get_default("company"));
}
// Set default filter dates
let today = frappe.datetime.get_today();
frm.doc.bank_statement_from_date = frappe.datetime.add_months(today, -1);
frm.doc.bank_statement_to_date = today;
frm.trigger("bank_account");
},
@@ -94,7 +99,7 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
make_reconciliation_tool(frm) {
frm.get_field("reconciliation_tool_cards").$wrapper.empty();
if (frm.doc.bank_account && frm.doc.bank_statement_to_date) {
if (frm.doc.company && frm.doc.bank_account && frm.doc.bank_statement_to_date) {
frm.trigger("get_cleared_balance").then(() => {
if (
frm.doc.bank_account &&
@@ -110,7 +115,7 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
},
get_account_opening_balance(frm) {
if (frm.doc.bank_account && frm.doc.bank_statement_from_date) {
if (frm.doc.company && frm.doc.bank_account && frm.doc.bank_statement_from_date) {
frappe.call({
method: "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_account_balance",
args: {
@@ -125,7 +130,7 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
},
get_cleared_balance(frm) {
if (frm.doc.bank_account && frm.doc.bank_statement_to_date) {
if (frm.doc.company && frm.doc.bank_account && frm.doc.bank_statement_to_date) {
return frappe.call({
method: "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_account_balance",
args: {

View File

@@ -12,6 +12,7 @@ from frappe.utils import cint, flt
from erpnext import get_default_cost_center
from erpnext.accounts.doctype.bank_transaction.bank_transaction import get_total_allocated_amount
from erpnext.accounts.party import get_party_account
from erpnext.accounts.report.bank_reconciliation_statement.bank_reconciliation_statement import (
get_amounts_not_reflected_in_system,
get_entries,
@@ -284,54 +285,56 @@ def create_payment_entry_bts(
bank_transaction = frappe.db.get_values(
"Bank Transaction",
bank_transaction_name,
fieldname=["name", "unallocated_amount", "deposit", "bank_account"],
fieldname=["name", "unallocated_amount", "deposit", "bank_account", "currency"],
as_dict=True,
)[0]
paid_amount = bank_transaction.unallocated_amount
payment_type = "Receive" if bank_transaction.deposit > 0.0 else "Pay"
company_account = frappe.get_value("Bank Account", bank_transaction.bank_account, "account")
company = frappe.get_value("Account", company_account, "company")
payment_entry_dict = {
"company": company,
"payment_type": payment_type,
"reference_no": reference_number,
"reference_date": reference_date,
"party_type": party_type,
"party": party,
"posting_date": posting_date,
"paid_amount": paid_amount,
"received_amount": paid_amount,
}
payment_entry = frappe.new_doc("Payment Entry")
bank_account = frappe.get_cached_value("Bank Account", bank_transaction.bank_account, "account")
company = frappe.get_cached_value("Account", bank_account, "company")
party_account = get_party_account(party_type, party, company)
payment_entry.update(payment_entry_dict)
bank_currency = bank_transaction.currency
party_currency = frappe.get_cached_value("Account", party_account, "account_currency")
if mode_of_payment:
payment_entry.mode_of_payment = mode_of_payment
if project:
payment_entry.project = project
if cost_center:
payment_entry.cost_center = cost_center
if payment_type == "Receive":
payment_entry.paid_to = company_account
else:
payment_entry.paid_from = company_account
exc_rate = get_exchange_rate(bank_currency, party_currency, posting_date)
payment_entry.validate()
amt_in_bank_acc_currency = bank_transaction.unallocated_amount
amount_in_party_currency = bank_transaction.unallocated_amount * exc_rate
pe = frappe.new_doc("Payment Entry")
pe.payment_type = payment_type
pe.company = company
pe.reference_no = reference_number
pe.reference_date = reference_date
pe.party_type = party_type
pe.party = party
pe.posting_date = posting_date
pe.paid_from = party_account if payment_type == "Receive" else bank_account
pe.paid_to = party_account if payment_type == "Pay" else bank_account
pe.paid_from_account_currency = party_currency if payment_type == "Receive" else bank_currency
pe.paid_to_account_currency = party_currency if payment_type == "Pay" else bank_currency
pe.paid_amount = amount_in_party_currency if payment_type == "Receive" else amt_in_bank_acc_currency
pe.received_amount = amount_in_party_currency if payment_type == "Pay" else amt_in_bank_acc_currency
pe.mode_of_payment = mode_of_payment
pe.project = project
pe.cost_center = cost_center
pe.validate()
if allow_edit:
return payment_entry
return pe
payment_entry.insert()
pe.insert()
pe.submit()
payment_entry.submit()
vouchers = json.dumps(
[
{
"payment_doctype": "Payment Entry",
"payment_name": payment_entry.name,
"amount": paid_amount,
"payment_name": pe.name,
"amount": amt_in_bank_acc_currency,
}
]
)
@@ -455,8 +458,12 @@ def get_linked_payments(
def subtract_allocations(gl_account, vouchers):
"Look up & subtract any existing Bank Transaction allocations"
copied = []
voucher_docs = [(voucher[1], voucher[2]) for voucher in vouchers]
voucher_allocated_amounts = get_total_allocated_amount(voucher_docs)
for voucher in vouchers:
rows = get_total_allocated_amount(voucher[1], voucher[2])
rows = voucher_allocated_amounts.get((voucher[1], voucher[2])) or []
amount = None
for row in rows:
if row["gl_account"] == gl_account:

View File

@@ -45,42 +45,41 @@ class AutoMatchbyAccountIBAN:
if not (self.bank_party_account_number or self.bank_party_iban):
return None
result = self.match_account_in_party()
return result
return self.match_account_in_party()
def match_account_in_party(self) -> tuple | None:
"""Check if there is a IBAN/Account No. match in Customer/Supplier/Employee"""
result = None
parties = get_parties_in_order(self.deposit)
or_filters = self.get_or_filters()
"""
Returns (Party Type, Party) if a matching account is found in Bank Account or Employee:
1. Get party from a matching (iban/account no) Bank Account
2. If not found, get party from Employee with matching bank account details (iban/account no)
"""
if not (self.bank_party_account_number or self.bank_party_iban):
# Nothing to match
return None
for party in parties:
party_result = frappe.db.get_all(
"Bank Account", or_filters=or_filters, pluck="party", limit_page_length=1
)
# Search for a matching Bank Account that has party set
party_result = frappe.db.get_all(
"Bank Account",
or_filters=self.get_or_filters(),
filters={"party_type": ("is", "set"), "party": ("is", "set")},
fields=["party", "party_type"],
limit_page_length=1,
)
if result := party_result[0] if party_result else None:
return (result["party_type"], result["party"])
if party == "Employee" and not party_result:
# Search in Bank Accounts first for Employee, and then Employee record
if "bank_account_no" in or_filters:
or_filters["bank_ac_no"] = or_filters.pop("bank_account_no")
# If no party is found, search in Employee (since it has bank account details)
if employee_result := frappe.db.get_all(
"Employee", or_filters=self.get_or_filters("Employee"), pluck="name", limit_page_length=1
):
return ("Employee", employee_result[0])
party_result = frappe.db.get_all(
party, or_filters=or_filters, pluck="name", limit_page_length=1
)
if party_result:
result = (
party,
party_result[0],
)
break
return result
def get_or_filters(self) -> dict:
def get_or_filters(self, party: str | None = None) -> dict:
"""Return OR filters for Bank Account and IBAN"""
or_filters = {}
if self.bank_party_account_number:
or_filters["bank_account_no"] = self.bank_party_account_number
bank_ac_field = "bank_ac_no" if party == "Employee" else "bank_account_no"
or_filters[bank_ac_field] = self.bank_party_account_number
if self.bank_party_iban:
or_filters["iban"] = self.bank_party_iban
@@ -100,8 +99,7 @@ class AutoMatchbyPartyNameDescription:
if not (self.bank_party_name or self.description):
return None
result = self.match_party_name_desc_in_party()
return result
return self.match_party_name_desc_in_party()
def match_party_name_desc_in_party(self) -> tuple | None:
"""Fuzzy search party name and/or description against parties in the system"""
@@ -110,7 +108,7 @@ class AutoMatchbyPartyNameDescription:
for party in parties:
filters = {"status": "Active"} if party == "Employee" else {"disabled": 0}
field = party.lower() + "_name"
field = f"{party.lower()}_name"
names = frappe.get_all(party, filters=filters, fields=[f"{field} as party_name", "name"])
for field in ["bank_party_name", "description"]:
@@ -137,13 +135,7 @@ class AutoMatchbyPartyNameDescription:
)
party_name, skip = self.process_fuzzy_result(result)
if not party_name:
return None, skip
return (
party,
party_name,
), skip
return ((party, party_name), skip) if party_name else (None, skip)
def process_fuzzy_result(self, result: list | None):
"""
@@ -161,8 +153,8 @@ class AutoMatchbyPartyNameDescription:
if len(result) == 1:
return (first_result[PARTY_ID] if first_result[SCORE] > CUTOFF else None), True
second_result = result[1]
if first_result[SCORE] > CUTOFF:
second_result = result[1]
# If multiple matches with the same score, return None but discontinue matching
# Matches were found but were too close to distinguish between
if first_result[SCORE] == second_result[SCORE]:
@@ -174,8 +166,8 @@ class AutoMatchbyPartyNameDescription:
def get_parties_in_order(deposit: float) -> list:
parties = ["Supplier", "Employee", "Customer"] # most -> least likely to receive
if flt(deposit) > 0:
parties = ["Customer", "Supplier", "Employee"] # most -> least likely to pay
return parties
return (
["Customer", "Supplier", "Employee"] # most -> least likely to pay us
if flt(deposit) > 0
else ["Supplier", "Employee", "Customer"] # most -> least likely to receive from us
)

View File

@@ -2,6 +2,7 @@
# For license information, please see license.txt
import frappe
from frappe import _
from frappe.model.docstatus import DocStatus
from frappe.utils import flt
@@ -92,10 +93,16 @@ class BankTransaction(StatusUpdater):
- clear means: set the latest transaction date as clearance date
"""
remaining_amount = self.unallocated_amount
payment_entry_docs = [(pe.payment_document, pe.payment_entry) for pe in self.payment_entries]
pe_bt_allocations = get_total_allocated_amount(payment_entry_docs)
for payment_entry in self.payment_entries:
if payment_entry.allocated_amount == 0.0:
unallocated_amount, should_clear, latest_transaction = get_clearance_details(
self, payment_entry
self,
payment_entry,
pe_bt_allocations.get((payment_entry.payment_document, payment_entry.payment_entry))
or [],
)
if 0.0 == unallocated_amount:
@@ -156,13 +163,17 @@ class BankTransaction(StatusUpdater):
if self.party_type and self.party:
return
result = AutoMatchParty(
bank_party_account_number=self.bank_party_account_number,
bank_party_iban=self.bank_party_iban,
bank_party_name=self.bank_party_name,
description=self.description,
deposit=self.deposit,
).match()
result = None
try:
result = AutoMatchParty(
bank_party_account_number=self.bank_party_account_number,
bank_party_iban=self.bank_party_iban,
bank_party_name=self.bank_party_name,
description=self.description,
deposit=self.deposit,
).match()
except Exception:
frappe.log_error(title=_("Error in party matching for Bank Transaction {0}").format(self.name))
if result:
party_type, party = result
@@ -177,7 +188,7 @@ def get_doctypes_for_bank_reconciliation():
return frappe.get_hooks("bank_reconciliation_doctypes")
def get_clearance_details(transaction, payment_entry):
def get_clearance_details(transaction, payment_entry, bt_allocations):
"""
There should only be one bank gle for a voucher.
Could be none for a Bank Transaction.
@@ -186,7 +197,6 @@ def get_clearance_details(transaction, payment_entry):
"""
gl_bank_account = frappe.db.get_value("Bank Account", transaction.bank_account, "account")
gles = get_related_bank_gl_entries(payment_entry.payment_document, payment_entry.payment_entry)
bt_allocations = get_total_allocated_amount(payment_entry.payment_document, payment_entry.payment_entry)
unallocated_amount = min(
transaction.unallocated_amount,
@@ -242,44 +252,52 @@ def get_related_bank_gl_entries(doctype, docname):
return result
def get_total_allocated_amount(doctype, docname):
def get_total_allocated_amount(docs):
"""
Gets the sum of allocations for a voucher on each bank GL account
along with the latest bank transaction name & date
NOTE: query may also include just saved vouchers/payments but with zero allocated_amount
"""
if not docs:
return {}
# nosemgrep: frappe-semgrep-rules.rules.frappe-using-db-sql
result = frappe.db.sql(
"""
SELECT total, latest_name, latest_date, gl_account FROM (
SELECT total, latest_name, latest_date, gl_account, payment_document, payment_entry FROM (
SELECT
ROW_NUMBER() OVER w AS rownum,
SUM(btp.allocated_amount) OVER(PARTITION BY ba.account) AS total,
SUM(btp.allocated_amount) OVER(PARTITION BY ba.account, btp.payment_document, btp.payment_entry) AS total,
FIRST_VALUE(bt.name) OVER w AS latest_name,
FIRST_VALUE(bt.date) OVER w AS latest_date,
ba.account AS gl_account
ba.account AS gl_account,
btp.payment_document,
btp.payment_entry
FROM
`tabBank Transaction Payments` btp
LEFT JOIN `tabBank Transaction` bt ON bt.name=btp.parent
LEFT JOIN `tabBank Account` ba ON ba.name=bt.bank_account
WHERE
btp.payment_document = %(doctype)s
AND btp.payment_entry = %(docname)s
(btp.payment_document, btp.payment_entry) IN %(docs)s
AND bt.docstatus = 1
WINDOW w AS (PARTITION BY ba.account ORDER BY bt.date desc)
WINDOW w AS (PARTITION BY ba.account, btp.payment_document, btp.payment_entry ORDER BY bt.date DESC)
) temp
WHERE
rownum = 1
""",
dict(doctype=doctype, docname=docname),
dict(docs=docs),
as_dict=True,
)
payment_allocation_details = {}
for row in result:
# Why is this *sometimes* a byte string?
if isinstance(row["latest_name"], bytes):
row["latest_name"] = row["latest_name"].decode()
row["latest_date"] = frappe.utils.getdate(row["latest_date"])
return result
payment_allocation_details.setdefault((row["payment_document"], row["payment_entry"]), []).append(row)
return payment_allocation_details
def get_paid_amount(payment_entry, currency, gl_bank_account):

View File

@@ -460,13 +460,20 @@ def get_actual_expense(args):
def get_accumulated_monthly_budget(monthly_distribution, posting_date, fiscal_year, annual_budget):
distribution = {}
if monthly_distribution:
for d in frappe.db.sql(
"""select mdp.month, mdp.percentage_allocation
from `tabMonthly Distribution Percentage` mdp, `tabMonthly Distribution` md
where mdp.parent=md.name and md.fiscal_year=%s""",
fiscal_year,
as_dict=1,
):
mdp = frappe.qb.DocType("Monthly Distribution Percentage")
md = frappe.qb.DocType("Monthly Distribution")
res = (
frappe.qb.from_(mdp)
.join(md)
.on(mdp.parent == md.name)
.select(mdp.month, mdp.percentage_allocation)
.where(md.fiscal_year == fiscal_year)
.where(md.name == monthly_distribution)
.run(as_dict=True)
)
for d in res:
distribution.setdefault(d.month, d.percentage_allocation)
dt = frappe.db.get_value("Fiscal Year", fiscal_year, "year_start_date")

View File

@@ -52,6 +52,21 @@ class ExchangeRateRevaluation(Document):
if not (self.company and self.posting_date):
frappe.throw(_("Please select Company and Posting Date to getting entries"))
def before_submit(self):
self.remove_accounts_without_gain_loss()
def remove_accounts_without_gain_loss(self):
self.accounts = [account for account in self.accounts if account.gain_loss]
if not self.accounts:
frappe.throw(_("At least one account with exchange gain or loss is required"))
frappe.msgprint(
_("Removing rows without exchange gain or loss"),
alert=True,
indicator="yellow",
)
def on_cancel(self):
self.ignore_linked_doctypes = "GL Entry"
@@ -226,23 +241,23 @@ class ExchangeRateRevaluation(Document):
new_exchange_rate = get_exchange_rate(d.account_currency, company_currency, posting_date)
new_balance_in_base_currency = flt(d.balance_in_account_currency * new_exchange_rate)
gain_loss = flt(new_balance_in_base_currency, precision) - flt(d.balance, precision)
if gain_loss:
accounts.append(
{
"account": d.account,
"party_type": d.party_type,
"party": d.party,
"account_currency": d.account_currency,
"balance_in_base_currency": d.balance,
"balance_in_account_currency": d.balance_in_account_currency,
"zero_balance": d.zero_balance,
"current_exchange_rate": current_exchange_rate,
"new_exchange_rate": new_exchange_rate,
"new_balance_in_base_currency": new_balance_in_base_currency,
"new_balance_in_account_currency": d.balance_in_account_currency,
"gain_loss": gain_loss,
}
)
accounts.append(
{
"account": d.account,
"party_type": d.party_type,
"party": d.party,
"account_currency": d.account_currency,
"balance_in_base_currency": d.balance,
"balance_in_account_currency": d.balance_in_account_currency,
"zero_balance": d.zero_balance,
"current_exchange_rate": current_exchange_rate,
"new_exchange_rate": new_exchange_rate,
"new_balance_in_base_currency": new_balance_in_base_currency,
"new_balance_in_account_currency": d.balance_in_account_currency,
"gain_loss": gain_loss,
}
)
# Handle Accounts with '0' balance in Account/Base Currency
for d in [x for x in account_details if x.zero_balance]:
@@ -266,23 +281,22 @@ class ExchangeRateRevaluation(Document):
current_exchange_rate * d.balance_in_account_currency
)
if gain_loss:
accounts.append(
{
"account": d.account,
"party_type": d.party_type,
"party": d.party,
"account_currency": d.account_currency,
"balance_in_base_currency": d.balance,
"balance_in_account_currency": d.balance_in_account_currency,
"zero_balance": d.zero_balance,
"current_exchange_rate": current_exchange_rate,
"new_exchange_rate": new_exchange_rate,
"new_balance_in_base_currency": new_balance_in_base_currency,
"new_balance_in_account_currency": new_balance_in_account_currency,
"gain_loss": gain_loss,
}
)
accounts.append(
{
"account": d.account,
"party_type": d.party_type,
"party": d.party,
"account_currency": d.account_currency,
"balance_in_base_currency": d.balance,
"balance_in_account_currency": d.balance_in_account_currency,
"zero_balance": d.zero_balance,
"current_exchange_rate": current_exchange_rate,
"new_exchange_rate": new_exchange_rate,
"new_balance_in_base_currency": new_balance_in_base_currency,
"new_balance_in_account_currency": new_balance_in_account_currency,
"gain_loss": gain_loss,
}
)
return accounts

View File

@@ -83,7 +83,7 @@ class GLEntry(Document):
if not self.get(k):
frappe.throw(_("{0} is required").format(_(self.meta.get_label(k))))
if not (self.party_type and self.party):
if not self.is_cancelled and not (self.party_type and self.party):
account_type = frappe.get_cached_value("Account", self.account, "account_type")
if account_type == "Receivable":
frappe.throw(

View File

@@ -15,6 +15,10 @@ frappe.ui.form.on('Payment Entry', {
}
erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
if (frm.is_new()) {
set_default_party_type(frm);
}
},
setup: function(frm) {
@@ -161,6 +165,10 @@ frappe.ui.form.on('Payment Entry', {
filters: {
reference_doctype: row.reference_doctype,
reference_name: row.reference_name,
company: doc.company,
status: ["!=", "Paid"],
outstanding_amount: [">", 0], // for compatibility with old data
docstatus: 1,
},
};
});
@@ -320,6 +328,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",
"references", "total_allocated_amount"], function(i, field) {
@@ -1079,6 +1088,24 @@ frappe.ui.form.on('Payment Entry', {
if (r.message) {
if (!frm.doc.mode_of_payment) {
frm.set_value(field, r.message.account);
} else {
frappe.call({
method: "frappe.client.get_value",
args: {
doctype: "Mode of Payment Account",
filters: {
parent: frm.doc.mode_of_payment,
company: frm.doc.company,
},
fieldname: "default_account",
parent: "Mode of Payment",
},
callback: function (res) {
if (!res.message.default_account) {
frm.set_value(field, r.message.account);
}
},
});
}
frm.set_value('bank', r.message.bank);
frm.set_value('bank_account_no', r.message.bank_account_no);
@@ -1511,3 +1538,16 @@ frappe.ui.form.on('Payment Entry Deduction', {
frm.events.set_unallocated_amount(frm);
},
});
function set_default_party_type(frm) {
if (frm.doc.party) return;
let party_type;
if (frm.doc.payment_type == "Receive") {
party_type = "Customer";
} else if (frm.doc.payment_type == "Pay") {
party_type = "Supplier";
}
if (party_type) frm.set_value("party_type", party_type);
}

View File

@@ -465,7 +465,7 @@ class PaymentEntry(AccountsController):
if d.reference_doctype not in valid_reference_doctypes:
frappe.throw(
_("Reference Doctype must be one of {0}").format(
comma_or(_(d) for d in valid_reference_doctypes)
comma_or([_(d) for d in valid_reference_doctypes])
)
)
@@ -1519,11 +1519,11 @@ 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(
_("Cannot {0} from {2} without any negative outstanding invoice").format(
_("Cannot {0} from {1} without any negative outstanding invoice").format(
self.payment_type,
self.party_type,
)
@@ -2481,6 +2481,7 @@ def get_payment_entry(
pe.paid_amount = paid_amount
pe.received_amount = received_amount
pe.letter_head = doc.get("letter_head")
pe.bank_account = frappe.db.get_value("Bank Account", {"is_company_account": 1, "is_default": 1}, "name")
if dt in ["Purchase Order", "Sales Order", "Sales Invoice", "Purchase Invoice"]:
pe.project = doc.get("project") or reduce(
@@ -2605,6 +2606,7 @@ def get_open_payment_requests_for_references(references=None):
.where(Tuple(PR.reference_doctype, PR.reference_name).isin(list(refs)))
.where(PR.status != "Paid")
.where(PR.docstatus == 1)
.where(PR.outstanding_amount > 0) # to avoid old PRs with 0 outstanding amount
.orderby(Coalesce(PR.transaction_date, PR.creation), order=frappe.qb.asc)
).run(as_dict=True)

View File

@@ -144,12 +144,14 @@ class PaymentReconciliation(Document):
if self.get("cost_center"):
conditions.append(jea.cost_center == self.cost_center)
dr_or_cr = (
"credit_in_account_currency"
if erpnext.get_party_account_type(self.party_type) == "Receivable"
else "debit_in_account_currency"
)
conditions.append(jea[dr_or_cr].gt(0))
account_type = erpnext.get_party_account_type(self.party_type)
if account_type == "Receivable":
dr_or_cr = jea.credit_in_account_currency - jea.debit_in_account_currency
elif account_type == "Payable":
dr_or_cr = jea.debit_in_account_currency - jea.credit_in_account_currency
conditions.append(dr_or_cr.gt(0))
if self.bank_cash_account:
conditions.append(jea.against_account.like(f"%%{self.bank_cash_account}%%"))
@@ -164,7 +166,7 @@ class PaymentReconciliation(Document):
je.posting_date,
je.remark.as_("remarks"),
jea.name.as_("reference_row"),
jea[dr_or_cr].as_("amount"),
dr_or_cr.as_("amount"),
jea.is_advance,
jea.exchange_rate,
jea.account_currency.as_("currency"),
@@ -268,6 +270,7 @@ class PaymentReconciliation(Document):
for payment in non_reconciled_payments:
row = self.append("payments", {})
row.update(payment)
row.is_advance = payment.book_advance_payments_in_separate_party_account
def get_invoice_entries(self):
# Fetch JVs, Sales and Purchase Invoices for 'invoices' to reconcile against
@@ -299,6 +302,10 @@ class PaymentReconciliation(Document):
if self.invoice_limit:
non_reconciled_invoices = non_reconciled_invoices[: self.invoice_limit]
non_reconciled_invoices = sorted(
non_reconciled_invoices, key=lambda k: k["posting_date"] or getdate(nowdate())
)
self.add_invoice_entries(non_reconciled_invoices)
def add_invoice_entries(self, non_reconciled_invoices):
@@ -348,6 +355,9 @@ class PaymentReconciliation(Document):
def allocate_entries(self, args):
self.validate_entries()
exc_gain_loss_posting_date = frappe.db.get_single_value(
"Accounts Settings", "exchange_gain_loss_posting_date", cache=True
)
invoice_exchange_map = self.get_invoice_exchange_map(args.get("invoices"), args.get("payments"))
default_exchange_gain_loss_account = frappe.get_cached_value(
"Company", self.company, "exchange_gain_loss_account"
@@ -374,6 +384,11 @@ class PaymentReconciliation(Document):
res.difference_account = default_exchange_gain_loss_account
res.exchange_rate = inv.get("exchange_rate")
res.update({"gain_loss_posting_date": pay.get("posting_date")})
if not pay.get("is_advance"):
if exc_gain_loss_posting_date == "Invoice":
res.update({"gain_loss_posting_date": inv.get("invoice_date")})
elif exc_gain_loss_posting_date == "Reconciliation Date":
res.update({"gain_loss_posting_date": nowdate()})
if pay.get("amount") == 0:
entries.append(res)

View File

@@ -615,6 +615,42 @@ class TestPaymentReconciliation(FrappeTestCase):
self.assertEqual(len(pr.get("invoices")), 0)
self.assertEqual(len(pr.get("payments")), 0)
def test_negative_debit_or_credit_journal_against_invoice(self):
transaction_date = nowdate()
amount = 100
si = self.create_sales_invoice(qty=1, rate=amount, posting_date=transaction_date)
# credit debtors account to record a payment
je = self.create_journal_entry(self.bank, self.debit_to, amount, transaction_date)
je.accounts[1].party_type = "Customer"
je.accounts[1].party = self.customer
je.accounts[1].credit_in_account_currency = 0
je.accounts[1].debit_in_account_currency = -1 * amount
je.save()
je.submit()
pr = self.create_payment_reconciliation()
pr.get_unreconciled_entries()
invoices = [x.as_dict() for x in pr.get("invoices")]
payments = [x.as_dict() for x in pr.get("payments")]
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
# Difference amount should not be calculated for base currency accounts
for row in pr.allocation:
self.assertEqual(flt(row.get("difference_amount")), 0.0)
pr.reconcile()
# assert outstanding
si.reload()
self.assertEqual(si.status, "Paid")
self.assertEqual(si.outstanding_amount, 0)
# check PR tool output
self.assertEqual(len(pr.get("invoices")), 0)
self.assertEqual(len(pr.get("payments")), 0)
def test_journal_against_journal(self):
transaction_date = nowdate()
sales = "Sales - _PR"
@@ -937,6 +973,100 @@ class TestPaymentReconciliation(FrappeTestCase):
frappe.db.get_value("Journal Entry", jea_parent.parent, "voucher_type"), "Exchange Gain Or Loss"
)
def test_difference_amount_via_negative_debit_or_credit_journal_entry(self):
# Make Sale Invoice
si = self.create_sales_invoice(
qty=1, rate=100, posting_date=nowdate(), do_not_save=True, do_not_submit=True
)
si.customer = self.customer4
si.currency = "EUR"
si.conversion_rate = 85
si.debit_to = self.debtors_eur
si.save().submit()
# Make payment using Journal Entry
je1 = self.create_journal_entry("HDFC - _PR", self.debtors_eur, 100, nowdate())
je1.multi_currency = 1
je1.accounts[0].exchange_rate = 1
je1.accounts[0].credit_in_account_currency = -8000
je1.accounts[0].credit = -8000
je1.accounts[0].debit_in_account_currency = 0
je1.accounts[0].debit = 0
je1.accounts[1].party_type = "Customer"
je1.accounts[1].party = self.customer4
je1.accounts[1].exchange_rate = 80
je1.accounts[1].credit_in_account_currency = 100
je1.accounts[1].credit = 8000
je1.accounts[1].debit_in_account_currency = 0
je1.accounts[1].debit = 0
je1.save()
je1.submit()
je2 = self.create_journal_entry("HDFC - _PR", self.debtors_eur, 200, nowdate())
je2.multi_currency = 1
je2.accounts[0].exchange_rate = 1
je2.accounts[0].credit_in_account_currency = -16000
je2.accounts[0].credit = -16000
je2.accounts[0].debit_in_account_currency = 0
je2.accounts[0].debit = 0
je2.accounts[1].party_type = "Customer"
je2.accounts[1].party = self.customer4
je2.accounts[1].exchange_rate = 80
je2.accounts[1].credit_in_account_currency = 200
je1.accounts[1].credit = 16000
je1.accounts[1].debit_in_account_currency = 0
je1.accounts[1].debit = 0
je2.save()
je2.submit()
pr = self.create_payment_reconciliation()
pr.party = self.customer4
pr.receivable_payable_account = self.debtors_eur
pr.get_unreconciled_entries()
self.assertEqual(len(pr.invoices), 1)
self.assertEqual(len(pr.payments), 2)
# Test exact payment allocation
invoices = [x.as_dict() for x in pr.invoices]
payments = [pr.payments[0].as_dict()]
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
self.assertEqual(pr.allocation[0].allocated_amount, 100)
self.assertEqual(pr.allocation[0].difference_amount, -500)
# Test partial payment allocation (with excess payment entry)
pr.set("allocation", [])
pr.get_unreconciled_entries()
invoices = [x.as_dict() for x in pr.invoices]
payments = [pr.payments[1].as_dict()]
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
pr.allocation[0].difference_account = "Exchange Gain/Loss - _PR"
self.assertEqual(pr.allocation[0].allocated_amount, 100)
self.assertEqual(pr.allocation[0].difference_amount, -500)
# Check if difference journal entry gets generated for difference amount after reconciliation
pr.reconcile()
total_credit_amount = frappe.db.get_all(
"Journal Entry Account",
{"account": self.debtors_eur, "docstatus": 1, "reference_name": si.name},
"sum(credit) as amount",
group_by="reference_name",
)[0].amount
# total credit includes the exchange gain/loss amount
self.assertEqual(flt(total_credit_amount, 2), 8500)
jea_parent = frappe.db.get_all(
"Journal Entry Account",
filters={"account": self.debtors_eur, "docstatus": 1, "reference_name": si.name, "credit": 500},
fields=["parent"],
)[0]
self.assertEqual(
frappe.db.get_value("Journal Entry", jea_parent.parent, "voucher_type"), "Exchange Gain Or Loss"
)
def test_difference_amount_via_payment_entry(self):
# Make Sale Invoice
si = self.create_sales_invoice(

View File

@@ -246,6 +246,7 @@ class PaymentRequest(Document):
"payer_name": data.customer_name,
"order_id": self.name,
"currency": self.currency,
"payment_gateway": self.payment_gateway,
}
)
@@ -838,21 +839,17 @@ def validate_payment(doc, method=None):
@frappe.whitelist()
def get_open_payment_requests_query(doctype, txt, searchfield, start, page_len, filters):
# permission checks in `get_list()`
reference_doctype = filters.get("reference_doctype")
reference_name = filters.get("reference_doctype")
filters = frappe._dict(filters)
if not reference_doctype or not reference_name:
if not filters.reference_doctype or not filters.reference_name:
return []
if txt:
filters.name = ["like", f"%{txt}%"]
open_payment_requests = frappe.get_list(
"Payment Request",
filters={
"reference_doctype": filters["reference_doctype"],
"reference_name": filters["reference_name"],
"status": ["!=", "Paid"],
"outstanding_amount": ["!=", 0], # for compatibility with old data
"docstatus": 1,
},
filters=filters,
fields=["name", "grand_total", "outstanding_amount"],
order_by="transaction_date ASC,creation ASC",
)

View File

@@ -0,0 +1,14 @@
from frappe import _
def get_data():
return {
"fieldname": "payment_request",
"internal_links": {
"Payment Entry": ["references", "payment_request"],
"Payment Order": ["references", "payment_order"],
},
"transactions": [
{"label": _("Payment"), "items": ["Payment Entry", "Payment Order"]},
],
}

View File

@@ -1,19 +1,18 @@
const INDICATORS = {
"Partially Paid": "orange",
Cancelled: "red",
Draft: "gray",
Failed: "red",
Initiated: "green",
Paid: "blue",
Requested: "green",
};
frappe.listview_settings["Payment Request"] = {
add_fields: ["status"],
get_indicator: function (doc) {
if (doc.status == "Draft") {
return [__("Draft"), "gray", "status,=,Draft"];
}
if (doc.status == "Requested") {
return [__("Requested"), "green", "status,=,Requested"];
} else if (doc.status == "Initiated") {
return [__("Initiated"), "green", "status,=,Initiated"];
} else if (doc.status == "Partially Paid") {
return [__("Partially Paid"), "orange", "status,=,Partially Paid"];
} else if (doc.status == "Paid") {
return [__("Paid"), "blue", "status,=,Paid"];
} else if (doc.status == "Cancelled") {
return [__("Cancelled"), "red", "status,=,Cancelled"];
}
if (!doc.status || !INDICATORS[doc.status]) return;
return [__(doc.status), INDICATORS[doc.status], `status,=,${doc.status}`];
},
};

View File

@@ -48,6 +48,7 @@
"shipping_address",
"company_address",
"company_address_display",
"company_contact_person",
"currency_and_price_list",
"currency",
"conversion_rate",
@@ -1557,12 +1558,19 @@
"fieldname": "update_billed_amount_in_delivery_note",
"fieldtype": "Check",
"label": "Update Billed Amount in Delivery Note"
},
{
"fieldname": "company_contact_person",
"fieldtype": "Link",
"label": "Company Contact Person",
"options": "Contact",
"print_hide": 1
}
],
"icon": "fa fa-file-text",
"is_submittable": 1,
"links": [],
"modified": "2024-03-20 16:00:34.268756",
"modified": "2024-11-26 13:10:50.309570",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Invoice",
@@ -1615,6 +1623,5 @@
"states": [],
"timeline_field": "customer",
"title_field": "title",
"track_changes": 1,
"track_seen": 1
}
"track_changes": 1
}

View File

@@ -53,6 +53,7 @@
"column_break_42",
"free_item_uom",
"round_free_qty",
"dont_enforce_free_item_qty",
"is_recursive",
"recurse_for",
"apply_recursion_over",
@@ -643,12 +644,19 @@
"fieldname": "has_priority",
"fieldtype": "Check",
"label": "Has Priority"
},
{
"default": "0",
"depends_on": "eval:doc.price_or_product_discount == 'Product'",
"fieldname": "dont_enforce_free_item_qty",
"fieldtype": "Check",
"label": "Don't Enforce Free Item Qty"
}
],
"icon": "fa fa-gift",
"idx": 1,
"links": [],
"modified": "2024-09-16 18:14:51.314765",
"modified": "2025-02-17 18:15:39.824639",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Pricing Rule",

View File

@@ -359,7 +359,20 @@ def get_pricing_rule_for_item(args, doc=None, for_validate=False):
if isinstance(pricing_rule, str):
pricing_rule = frappe.get_cached_doc("Pricing Rule", pricing_rule)
update_pricing_rule_uom(pricing_rule, args)
pricing_rule.apply_rule_on_other_items = get_pricing_rule_items(pricing_rule) or []
fetch_other_item = True if pricing_rule.apply_rule_on_other else False
pricing_rule.apply_rule_on_other_items = (
get_pricing_rule_items(pricing_rule, other_items=fetch_other_item) or []
)
if pricing_rule.coupon_code_based == 1:
if not args.coupon_code:
return item_details
coupon_code = frappe.db.get_value(
doctype="Coupon Code", filters={"pricing_rule": pricing_rule.name}, fieldname="name"
)
if args.coupon_code != coupon_code:
continue
if pricing_rule.get("suggestion"):
continue
@@ -386,9 +399,6 @@ def get_pricing_rule_for_item(args, doc=None, for_validate=False):
pricing_rule.apply_rule_on_other_items
)
if pricing_rule.coupon_code_based == 1 and args.coupon_code is None:
return item_details
if not pricing_rule.validate_applied_rule:
if pricing_rule.price_or_product_discount == "Price":
apply_price_discount_rule(pricing_rule, item_details, args)
@@ -543,7 +553,7 @@ def remove_pricing_rule_for_item(pricing_rules, item_details, item_code=None, ra
if pricing_rule.margin_type in ["Percentage", "Amount"]:
item_details.margin_rate_or_amount = 0.0
item_details.margin_type = None
elif pricing_rule.get("free_item"):
elif pricing_rule.get("free_item") and not pricing_rule.get("dont_enforce_free_item_qty"):
item_details.remove_free_item = (
item_code if pricing_rule.get("same_item") else pricing_rule.get("free_item")
)

View File

@@ -428,6 +428,54 @@ class TestPricingRule(FrappeTestCase):
self.assertEqual(so.items[1].is_free_item, 1)
self.assertEqual(so.items[1].item_code, "_Test Item 2")
def test_dont_enforce_free_item_qty(self):
# this test is only for testing non-enforcement as all other tests in this file already test with enforcement
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule")
test_record = {
"doctype": "Pricing Rule",
"title": "_Test Pricing Rule",
"apply_on": "Item Code",
"currency": "USD",
"items": [
{
"item_code": "_Test Item",
}
],
"selling": 1,
"rate_or_discount": "Discount Percentage",
"rate": 0,
"min_qty": 0,
"max_qty": 7,
"discount_percentage": 17.5,
"price_or_product_discount": "Product",
"same_item": 0,
"free_item": "_Test Item 2",
"free_qty": 1,
"company": "_Test Company",
}
pricing_rule = frappe.get_doc(test_record.copy()).insert()
# With enforcement
so = make_sales_order(item_code="_Test Item", qty=1, do_not_submit=True)
self.assertEqual(so.items[1].is_free_item, 1)
self.assertEqual(so.items[1].item_code, "_Test Item 2")
# Test 1 : Saving a document with an item with pricing list without it's corresponding free item will cause it the free item to be refetched on save
so.items.pop(1)
so.save()
so.reload()
self.assertEqual(len(so.items), 2)
# Without enforcement
pricing_rule.dont_enforce_free_item_qty = 1
pricing_rule.save()
# Test 2 : Deleted free item will not be fetched again on save without enforcement
so.items.pop(1)
so.save()
so.reload()
self.assertEqual(len(so.items), 1)
def test_cumulative_pricing_rule(self):
frappe.delete_doc_if_exists("Pricing Rule", "_Test Cumulative Pricing Rule")
test_record = {
@@ -987,6 +1035,45 @@ class TestPricingRule(FrappeTestCase):
so.save()
self.assertEqual(len(so.items), 1)
def test_pricing_rule_for_product_free_item_round_free_qty(self):
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule")
test_record = {
"doctype": "Pricing Rule",
"title": "_Test Pricing Rule",
"apply_on": "Item Code",
"currency": "USD",
"items": [
{
"item_code": "_Test Item",
}
],
"selling": 1,
"rate": 0,
"min_qty": 100,
"max_qty": 0,
"price_or_product_discount": "Product",
"same_item": 1,
"free_qty": 10,
"round_free_qty": 1,
"is_recursive": 1,
"recurse_for": 100,
"company": "_Test Company",
}
frappe.get_doc(test_record.copy()).insert()
# With pricing rule
so = make_sales_order(item_code="_Test Item", qty=100)
so.load_from_db()
self.assertEqual(so.items[1].is_free_item, 1)
self.assertEqual(so.items[1].item_code, "_Test Item")
self.assertEqual(so.items[1].qty, 10)
so = make_sales_order(item_code="_Test Item", qty=150)
so.load_from_db()
self.assertEqual(so.items[1].is_free_item, 1)
self.assertEqual(so.items[1].item_code, "_Test Item")
self.assertEqual(so.items[1].qty, 10)
def test_apply_multiple_pricing_rules_for_discount_percentage_and_amount(self):
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 1")
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 2")
@@ -1200,6 +1287,7 @@ def make_pricing_rule(**args):
"discount_amount": args.discount_amount or 0.0,
"apply_multiple_pricing_rules": args.apply_multiple_pricing_rules or 0,
"has_priority": args.has_priority or 0,
"enforce_free_item_qty": args.dont_enforce_free_item_qty or 0,
}
)

View File

@@ -642,7 +642,7 @@ def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None):
if transaction_qty:
qty = flt(transaction_qty) * qty / pricing_rule.recurse_for
if pricing_rule.round_free_qty:
qty = math.floor(qty)
qty = (flt(transaction_qty) // pricing_rule.recurse_for) * (pricing_rule.free_qty or 1)
if not qty:
return
@@ -691,7 +691,10 @@ def apply_pricing_rule_for_free_items(doc, pricing_rule_args):
args.pop((item.item_code, item.pricing_rules))
for free_item in args.values():
doc.append("items", free_item)
if doc.is_new() or not frappe.get_value(
"Pricing Rule", free_item["pricing_rules"], "dont_enforce_free_item_qty"
):
doc.append("items", free_item)
def get_pricing_rule_items(pr_doc, other_items=False) -> list:

View File

@@ -20,6 +20,7 @@
"is_advance",
"section_break_5",
"difference_amount",
"gain_loss_posting_date",
"column_break_7",
"difference_account",
"exchange_rate",
@@ -153,11 +154,16 @@
"fieldtype": "Check",
"in_list_view": 1,
"label": "Reconciled"
},
{
"fieldname": "gain_loss_posting_date",
"fieldtype": "Date",
"label": "Difference Posting Date"
}
],
"istable": 1,
"links": [],
"modified": "2023-03-20 21:05:43.121945",
"modified": "2025-01-23 16:09:01.058574",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Process Payment Reconciliation Log Allocations",

View File

@@ -177,17 +177,21 @@ def get_ar_filters(doc, entry):
def get_html(doc, filters, entry, col, res, ageing):
base_template_path = "frappe/www/printview.html"
template_path = (
"erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html"
if doc.report == "General Ledger"
else "erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts_accounts_receivable.html"
)
template_path = "erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts_accounts_receivable.html"
if doc.report == "General Ledger":
template_path = (
"erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html"
)
process_soa_html = frappe.get_hooks("process_soa_html")
# fetching custom print format for Process Statement of Accounts
if process_soa_html and process_soa_html.get(doc.report):
template_path = process_soa_html[doc.report][-1]
if doc.letter_head:
from frappe.www.printview import get_letter_head
letter_head = get_letter_head(doc, 0)
html = frappe.render_template(
template_path,
{
@@ -203,7 +207,6 @@ def get_html(doc, filters, entry, col, res, ageing):
else None,
},
)
html = frappe.render_template(
base_template_path,
{"body": html, "css": get_print_style(), "title": "Statement For " + entry.customer},
@@ -262,9 +265,12 @@ def get_recipients_and_cc(customer, doc):
recipients = []
for clist in doc.customers:
if clist.customer == customer:
recipients.append(clist.billing_email)
if clist.billing_email:
for email in clist.billing_email.split(","):
recipients.append(email.strip())
if doc.primary_mandatory and clist.primary_email:
recipients.append(clist.primary_email)
for email in clist.primary_email.split(","):
recipients.append(email.strip())
cc = []
if doc.cc_to != "":
try:

View File

@@ -302,7 +302,11 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
if (this.frm.doc.__onload && this.frm.doc.__onload.load_after_mapping) return;
erpnext.utils.get_party_details(this.frm, "erpnext.accounts.party.get_party_details",
let payment_terms_template = this.frm.doc.payment_terms_template;
erpnext.utils.get_party_details(
this.frm,
"erpnext.accounts.party.get_party_details",
{
posting_date: this.frm.doc.posting_date,
bill_date: this.frm.doc.bill_date,
@@ -320,7 +324,14 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
me.frm.doc.tax_withholding_category = me.frm.supplier_tds;
me.frm.set_df_property("apply_tds", "read_only", me.frm.supplier_tds ? 0 : 1);
me.frm.set_df_property("tax_withholding_category", "hidden", me.frm.supplier_tds ? 0 : 1);
})
// while duplicating, don't change payment terms
if (me.frm.doc.__run_link_triggers === false) {
me.frm.set_value("payment_terms_template", payment_terms_template);
me.frm.refresh_field("payment_terms_template");
}
}
);
}
apply_tds(frm) {
@@ -366,6 +377,8 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
hide_fields(this.frm.doc);
if(cint(this.frm.doc.is_paid)) {
this.frm.set_value("allocate_advances_automatically", 0);
this.frm.set_value("payment_terms_template", "");
this.frm.set_value("payment_schedule", []);
if(!this.frm.doc.company) {
this.frm.set_value("is_paid", 0)
frappe.msgprint(__("Please specify Company to proceed"));

View File

@@ -10,7 +10,6 @@ from frappe.utils import cint, cstr, flt, formatdate, get_link_to_form, getdate,
import erpnext
from erpnext.accounts.deferred_revenue import validate_service_stop_date
from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
from erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger import (
validate_docs_for_deferred_accounting,
validate_docs_for_voucher_types,
@@ -33,7 +32,7 @@ from erpnext.accounts.general_ledger import (
merge_similar_entries,
)
from erpnext.accounts.party import get_due_date, get_party_account
from erpnext.accounts.utils import get_account_currency, get_fiscal_year
from erpnext.accounts.utils import get_account_currency, get_fiscal_year, update_voucher_outstanding
from erpnext.assets.doctype.asset.asset import is_cwip_accounting_enabled
from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
from erpnext.buying.utils import check_on_hold_or_closed_status
@@ -661,12 +660,12 @@ class PurchaseInvoice(BuyingController):
def update_supplier_outstanding(self, update_outstanding):
if update_outstanding == "No":
update_outstanding_amt(
self.credit_to,
"Supplier",
self.supplier,
self.doctype,
self.return_against if cint(self.is_return) and self.return_against else self.name,
update_voucher_outstanding(
voucher_type=self.doctype,
voucher_no=self.return_against if cint(self.is_return) and self.return_against else self.name,
account=self.credit_to,
party_type="Supplier",
party=self.supplier,
)
def get_gl_entries(self, warehouse_account=None):
@@ -1443,7 +1442,12 @@ class PurchaseInvoice(BuyingController):
if pi:
pi = pi[0][0]
frappe.throw(_("Supplier Invoice No exists in Purchase Invoice {0}").format(pi))
frappe.throw(
_("Supplier Invoice No exists in Purchase Invoice {0}").format(
get_link_to_form("Purchase Invoice", pi)
)
)
def update_billing_status_in_pr(self, update_modified=True):
if self.is_return and not self.update_billed_amount_in_purchase_receipt:

View File

@@ -45,12 +45,16 @@ frappe.listview_settings["Purchase Invoice"] = {
},
onload: function (listview) {
listview.page.add_action_item(__("Purchase Receipt"), () => {
erpnext.bulk_transaction_processing.create(listview, "Purchase Invoice", "Purchase Receipt");
});
if (frappe.model.can_create("Purchase Receipt")) {
listview.page.add_action_item(__("Purchase Receipt"), () => {
erpnext.bulk_transaction_processing.create(listview, "Purchase Invoice", "Purchase Receipt");
});
}
listview.page.add_action_item(__("Payment"), () => {
erpnext.bulk_transaction_processing.create(listview, "Purchase Invoice", "Payment Entry");
});
if (frappe.model.can_create("Payment Entry")) {
listview.page.add_action_item(__("Payment"), () => {
erpnext.bulk_transaction_processing.create(listview, "Purchase Invoice", "Payment Entry");
});
}
},
};

View File

@@ -1643,6 +1643,30 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
frappe.db.set_single_value("Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate", 1)
# Cost of Item is zero in Purchase Receipt
pr = make_purchase_receipt(qty=1, rate=0)
stock_value_difference = frappe.db.get_value(
"Stock Ledger Entry",
{"voucher_type": "Purchase Receipt", "voucher_no": pr.name},
"stock_value_difference",
)
self.assertEqual(stock_value_difference, 0)
pi = create_purchase_invoice_from_receipt(pr.name)
for row in pi.items:
row.rate = 150
pi.save()
pi.submit()
stock_value_difference = frappe.db.get_value(
"Stock Ledger Entry",
{"voucher_type": "Purchase Receipt", "voucher_no": pr.name},
"stock_value_difference",
)
self.assertEqual(stock_value_difference, 150)
# Increase the cost of the item
pr = make_purchase_receipt(qty=1, rate=100)

View File

@@ -14,7 +14,8 @@
"advance_amount",
"allocated_amount",
"exchange_gain_loss",
"ref_exchange_rate"
"ref_exchange_rate",
"difference_posting_date"
],
"fields": [
{
@@ -30,7 +31,7 @@
"width": "180px"
},
{
"columns": 3,
"columns": 2,
"fieldname": "reference_name",
"fieldtype": "Dynamic Link",
"in_list_view": 1,
@@ -40,7 +41,7 @@
"read_only": 1
},
{
"columns": 3,
"columns": 2,
"fieldname": "remarks",
"fieldtype": "Text",
"in_list_view": 1,
@@ -111,13 +112,20 @@
"label": "Reference Exchange Rate",
"non_negative": 1,
"read_only": 1
},
{
"columns": 2,
"fieldname": "difference_posting_date",
"fieldtype": "Date",
"in_list_view": 1,
"label": "Difference Posting Date"
}
],
"idx": 1,
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2021-09-26 15:47:28.167371",
"modified": "2024-12-20 12:04:46.729972",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Advance",

View File

@@ -27,7 +27,7 @@ class RepostAccountingLedger(Document):
latest_pcv = (
frappe.db.get_all(
"Period Closing Voucher",
filters={"company": self.company},
filters={"company": self.company, "docstatus": 1},
order_by="posting_date desc",
pluck="posting_date",
limit=1,

View File

@@ -9,6 +9,10 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e
setup(doc) {
this.setup_posting_date_time_check();
super.setup(doc);
this.frm.make_methods = {
Dunning: this.make_dunning.bind(this),
"Invoice Discounting": this.make_invoice_discounting.bind(this),
};
}
company() {
super.company();
@@ -94,26 +98,35 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e
}
}
if (doc.outstanding_amount>0) {
cur_frm.add_custom_button(__('Payment Request'), function() {
me.make_payment_request();
}, __('Create'));
if (doc.outstanding_amount > 0) {
this.frm.add_custom_button(
__("Payment Request"),
function () {
me.make_payment_request();
},
__("Create")
);
this.frm.add_custom_button(
__("Invoice Discounting"),
this.make_invoice_discounting.bind(this),
__("Create")
);
cur_frm.add_custom_button(__('Invoice Discounting'), function() {
cur_frm.events.create_invoice_discounting(cur_frm);
}, __('Create'));
const payment_is_overdue = doc.payment_schedule
.map((row) => Date.parse(row.due_date) < Date.now())
.reduce((prev, current) => prev || current, false);
if (doc.due_date < frappe.datetime.get_today()) {
cur_frm.add_custom_button(__('Dunning'), function() {
cur_frm.events.create_dunning(cur_frm);
}, __('Create'));
if (payment_is_overdue) {
this.frm.add_custom_button(__("Dunning"), this.make_dunning.bind(this), __("Create"));
}
}
if (doc.docstatus === 1) {
cur_frm.add_custom_button(__('Maintenance Schedule'), function () {
cur_frm.cscript.make_maintenance_schedule();
}, __('Create'));
this.frm.add_custom_button(
__("Maintenance Schedule"),
this.make_maintenance_schedule.bind(this),
__("Create")
);
}
if(!doc.auto_repeat) {
@@ -146,6 +159,20 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e
erpnext.accounts.unreconcile_payment.add_unreconcile_btn(me.frm);
}
make_invoice_discounting() {
frappe.model.open_mapped_doc({
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.create_invoice_discounting",
frm: this.frm,
});
}
make_dunning() {
frappe.model.open_mapped_doc({
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.create_dunning",
frm: this.frm,
});
}
make_maintenance_schedule() {
frappe.model.open_mapped_doc({
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.make_maintenance_schedule",
@@ -667,20 +694,6 @@ frappe.ui.form.on('Sales Invoice', {
}
}
frm.set_query('company_address', function(doc) {
if(!doc.company) {
frappe.throw(__('Please set Company'));
}
return {
query: 'frappe.contacts.doctype.address.address.address_query',
filters: {
link_doctype: 'Company',
link_name: doc.company
}
};
});
frm.set_query('pos_profile', function(doc) {
if(!doc.company) {
frappe.throw(_('Please set Company'));
@@ -962,20 +975,6 @@ frappe.ui.form.on('Sales Invoice', {
frm.set_df_property('return_against', 'label', __('Adjustment Against'));
}
},
create_invoice_discounting: function(frm) {
frappe.model.open_mapped_doc({
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.create_invoice_discounting",
frm: frm
});
},
create_dunning: function(frm) {
frappe.model.open_mapped_doc({
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.create_dunning",
frm: frm
});
}
});
frappe.ui.form.on("Sales Invoice Timesheet", {

View File

@@ -160,8 +160,9 @@
"dispatch_address",
"company_address_section",
"company_address",
"company_addr_col_break",
"company_address_display",
"company_addr_col_break",
"company_contact_person",
"terms_tab",
"payment_schedule_section",
"ignore_default_payment_terms_template",
@@ -2171,6 +2172,13 @@
"label": "Update Outstanding for Self",
"no_copy": 1,
"print_hide": 1
},
{
"fieldname": "company_contact_person",
"fieldtype": "Link",
"label": "Company Contact Person",
"options": "Contact",
"print_hide": 1
}
],
"icon": "fa fa-file-text",
@@ -2183,7 +2191,7 @@
"link_fieldname": "consolidated_invoice"
}
],
"modified": "2024-07-18 15:30:39.428519",
"modified": "2024-11-26 12:34:09.110690",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",

View File

@@ -24,7 +24,11 @@ from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category
)
from erpnext.accounts.general_ledger import get_round_off_account_and_cost_center
from erpnext.accounts.party import get_due_date, get_party_account, get_party_details
from erpnext.accounts.utils import cancel_exchange_gain_loss_journal, get_account_currency
from erpnext.accounts.utils import (
cancel_exchange_gain_loss_journal,
get_account_currency,
update_voucher_outstanding,
)
from erpnext.assets.doctype.asset.depreciation import (
depreciate_asset,
get_disposal_account_and_cost_center,
@@ -1019,14 +1023,14 @@ class SalesInvoice(SellingController):
make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
if update_outstanding == "No":
from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
update_outstanding_amt(
self.debit_to,
"Customer",
self.customer,
self.doctype,
self.return_against if cint(self.is_return) and self.return_against else self.name,
update_voucher_outstanding(
voucher_type=self.doctype,
voucher_no=self.return_against
if cint(self.is_return) and self.return_against
else self.name,
account=self.debit_to,
party_type="Customer",
party=self.customer,
)
elif self.docstatus == 2 and cint(self.update_stock) and cint(auto_accounting_for_stock):
@@ -1556,8 +1560,12 @@ class SalesInvoice(SellingController):
)
def update_project(self):
if self.project:
project = frappe.get_doc("Project", self.project)
unique_projects = list(set([d.project for d in self.get("items") if d.project]))
if self.project and self.project not in unique_projects:
unique_projects.append(self.project)
for p in unique_projects:
project = frappe.get_doc("Project", p)
project.update_billed_amount()
project.db_update()

View File

@@ -32,12 +32,16 @@ frappe.listview_settings["Sales Invoice"] = {
right_column: "grand_total",
onload: function (listview) {
listview.page.add_action_item(__("Delivery Note"), () => {
erpnext.bulk_transaction_processing.create(listview, "Sales Invoice", "Delivery Note");
});
if (frappe.model.can_create("Delivery Note")) {
listview.page.add_action_item(__("Delivery Note"), () => {
erpnext.bulk_transaction_processing.create(listview, "Sales Invoice", "Delivery Note");
});
}
listview.page.add_action_item(__("Payment"), () => {
erpnext.bulk_transaction_processing.create(listview, "Sales Invoice", "Payment Entry");
});
if (frappe.model.can_create("Payment Entry")) {
listview.page.add_action_item(__("Payment"), () => {
erpnext.bulk_transaction_processing.create(listview, "Sales Invoice", "Payment Entry");
});
}
},
};

View File

@@ -36,6 +36,7 @@ from erpnext.stock.doctype.stock_entry.test_stock_entry import (
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
create_stock_reconciliation,
)
from erpnext.stock.get_item_details import get_item_tax_map
from erpnext.stock.utils import get_incoming_rate, get_stock_balance
@@ -2817,13 +2818,26 @@ class TestSalesInvoice(FrappeTestCase):
item.save()
sales_invoice = create_sales_invoice(item="T Shirt", rate=700, do_not_submit=True)
item_tax_map = get_item_tax_map(
company=sales_invoice.company,
item_tax_template=sales_invoice.items[0].item_tax_template,
)
self.assertEqual(sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 12 - _TC")
self.assertEqual(sales_invoice.items[0].item_tax_rate, item_tax_map)
# Apply discount
sales_invoice.apply_discount_on = "Net Total"
sales_invoice.discount_amount = 300
sales_invoice.save()
item_tax_map = get_item_tax_map(
company=sales_invoice.company,
item_tax_template=sales_invoice.items[0].item_tax_template,
)
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)
@change_settings("Selling Settings", {"enable_discount_accounting": 1})
def test_sales_invoice_with_discount_accounting_enabled(self):
@@ -3760,6 +3774,103 @@ class TestSalesInvoice(FrappeTestCase):
self.assertTrue(jv)
self.assertEqual(jv[0], si.grand_total)
@change_settings("Accounts Settings", {"enable_common_party_accounting": True})
def test_common_party_with_different_currency_in_debtor_and_creditor(self):
from erpnext.accounts.doctype.account.test_account import create_account
from erpnext.accounts.doctype.opening_invoice_creation_tool.test_opening_invoice_creation_tool import (
make_customer,
)
from erpnext.accounts.doctype.party_link.party_link import create_party_link
from erpnext.buying.doctype.supplier.test_supplier import create_supplier
from erpnext.setup.utils import get_exchange_rate
creditors = create_account(
account_name="Creditors INR",
parent_account="Accounts Payable - _TC",
company="_Test Company",
account_currency="INR",
account_type="Payable",
)
debtors = create_account(
account_name="Debtors USD",
parent_account="Accounts Receivable - _TC",
company="_Test Company",
account_currency="USD",
account_type="Receivable",
)
# create a customer
customer = make_customer(customer="_Test Common Party USD")
cust_doc = frappe.get_doc("Customer", customer)
cust_doc.default_currency = "USD"
test_account_details = {
"company": "_Test Company",
"account": debtors,
}
cust_doc.append("accounts", test_account_details)
cust_doc.save()
# create a supplier
supplier = create_supplier(supplier_name="_Test Common Party INR").name
supp_doc = frappe.get_doc("Supplier", supplier)
supp_doc.default_currency = "INR"
test_account_details = {
"company": "_Test Company",
"account": creditors,
}
supp_doc.append("accounts", test_account_details)
supp_doc.save()
# create a party link between customer & supplier
create_party_link("Supplier", supplier, customer)
# create a sales invoice
si = create_sales_invoice(
customer=customer,
currency="USD",
conversion_rate=get_exchange_rate("USD", "INR"),
debit_to=debtors,
do_not_save=1,
)
si.party_account_currency = "USD"
si.save()
si.submit()
# check outstanding of sales invoice
si.reload()
self.assertEqual(si.status, "Paid")
self.assertEqual(flt(si.outstanding_amount), 0.0)
# check creation of journal entry
jv = frappe.get_all(
"Journal Entry Account",
{
"account": si.debit_to,
"party_type": "Customer",
"party": si.customer,
"reference_type": si.doctype,
"reference_name": si.name,
},
pluck="credit_in_account_currency",
)
self.assertTrue(jv)
self.assertEqual(jv[0], si.grand_total)
def test_total_billed_amount(self):
si = create_sales_invoice(do_not_submit=True)
project = frappe.new_doc("Project")
project.company = "_Test Company"
project.project_name = "Test Total Billed Amount"
project.save()
si.project = project.name
si.save()
si.submit()
doc = frappe.get_doc("Project", project.name)
self.assertEqual(doc.total_billed_amount, si.grand_total)
def check_gl_entries(doc, voucher_no, expected_gle, posting_date):
gl_entries = frappe.db.sql(

View File

@@ -14,7 +14,8 @@
"advance_amount",
"allocated_amount",
"exchange_gain_loss",
"ref_exchange_rate"
"ref_exchange_rate",
"difference_posting_date"
],
"fields": [
{
@@ -30,7 +31,7 @@
"width": "250px"
},
{
"columns": 3,
"columns": 2,
"fieldname": "reference_name",
"fieldtype": "Dynamic Link",
"in_list_view": 1,
@@ -41,7 +42,7 @@
"read_only": 1
},
{
"columns": 3,
"columns": 2,
"fieldname": "remarks",
"fieldtype": "Text",
"in_list_view": 1,
@@ -112,13 +113,20 @@
"label": "Reference Exchange Rate",
"non_negative": 1,
"read_only": 1
},
{
"columns": 2,
"fieldname": "difference_posting_date",
"fieldtype": "Date",
"in_list_view": 1,
"label": "Difference Posting Date"
}
],
"idx": 1,
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2021-09-26 15:47:46.911595",
"modified": "2024-12-20 11:58:28.962370",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Advance",

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

@@ -124,6 +124,9 @@ def get_party_tax_withholding_details(inv, tax_withholding_category=None):
cost_center = get_cost_center(inv)
tax_row.update({"cost_center": cost_center})
if cint(tax_details.round_off_tax_amount):
inv.round_off_applicable_accounts_for_tax_withholding = tax_details.account_head
if inv.doctype == "Purchase Invoice":
return tax_row, tax_deducted_on_advances, voucher_wise_amount
else:
@@ -215,14 +218,14 @@ def get_tax_row_for_tds(tax_details, tax_amount):
}
def get_lower_deduction_certificate(company, tax_details, pan_no):
def get_lower_deduction_certificate(company, posting_date, tax_details, pan_no):
ldc_name = frappe.db.get_value(
"Lower Deduction Certificate",
{
"pan_no": pan_no,
"tax_withholding_category": tax_details.tax_withholding_category,
"valid_from": (">=", tax_details.from_date),
"valid_upto": ("<=", tax_details.to_date),
"valid_from": ("<=", posting_date),
"valid_upto": (">=", posting_date),
"company": company,
},
"name",
@@ -233,7 +236,7 @@ def get_lower_deduction_certificate(company, tax_details, pan_no):
def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=None):
vouchers, voucher_wise_amount = get_invoice_vouchers(
vouchers, voucher_wise_amount, zero_rate_ldc_invoices = get_invoice_vouchers(
parties, tax_details, inv.company, party_type=party_type
)
@@ -270,7 +273,7 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N
tax_amount = 0
if party_type == "Supplier":
ldc = get_lower_deduction_certificate(inv.company, tax_details, pan_no)
ldc = get_lower_deduction_certificate(inv.company, posting_date, tax_details, pan_no)
if tax_deducted:
net_total = inv.tax_withholding_net_total
if ldc:
@@ -287,7 +290,8 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N
# once tds is deducted, not need to add vouchers in the invoice
voucher_wise_amount = {}
else:
tax_amount = get_tds_amount(ldc, parties, inv, tax_details, vouchers)
taxable_vouchers = list(set(vouchers) - set(zero_rate_ldc_invoices))
tax_amount = get_tds_amount(ldc, parties, inv, tax_details, taxable_vouchers)
elif party_type == "Customer":
if tax_deducted:
@@ -306,12 +310,33 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N
def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"):
doctype = "Purchase Invoice" if party_type == "Supplier" else "Sales Invoice"
field = (
"base_tax_withholding_net_total as base_net_total" if party_type == "Supplier" else "base_net_total"
)
field = [
"name",
"base_tax_withholding_net_total as base_net_total" if party_type == "Supplier" else "base_net_total",
"posting_date",
]
voucher_wise_amount = {}
vouchers = []
ldcs = frappe.db.get_all(
"Lower Deduction Certificate",
filters={
"valid_from": [">=", tax_details.from_date],
"valid_upto": ["<=", tax_details.to_date],
"company": company,
"supplier": ["in", parties],
},
fields=["supplier", "valid_from", "valid_upto", "rate"],
)
doctype = "Purchase Invoice" if party_type == "Supplier" else "Sales Invoice"
field = [
"base_tax_withholding_net_total as base_net_total" if party_type == "Supplier" else "base_net_total",
"name",
"grand_total",
"posting_date",
]
filters = {
"company": company,
frappe.scrub(party_type): ["in", parties],
@@ -325,11 +350,31 @@ def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"):
{"apply_tds": 1, "tax_withholding_category": tax_details.get("tax_withholding_category")}
)
invoices_details = frappe.get_all(doctype, filters=filters, fields=["name", field])
invoices_details = frappe.get_all(doctype, filters=filters, fields=field)
ldcs = frappe.db.get_all(
"Lower Deduction Certificate",
filters={
"valid_from": [">=", tax_details.from_date],
"valid_upto": ["<=", tax_details.to_date],
"company": company,
"supplier": ["in", parties],
"rate": 0,
},
fields=["name", "supplier", "valid_from", "valid_upto"],
)
zero_rate_ldc_invoices = []
for d in invoices_details:
vouchers.append(d.name)
voucher_wise_amount.update({d.name: {"amount": d.base_net_total, "voucher_type": doctype}})
_voucher_detail = {"amount": d.base_net_total, "voucher_type": doctype}
if ldc := [x for x in ldcs if d.posting_date >= x.valid_from and d.posting_date <= x.valid_upto]:
if ldc[0].supplier in parties:
_voucher_detail.update({"amount": 0})
zero_rate_ldc_invoices.append(d.name)
voucher_wise_amount.update({d.name: _voucher_detail})
journal_entries_details = frappe.db.sql(
"""
@@ -360,7 +405,7 @@ def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"):
vouchers.append(d.name)
voucher_wise_amount.update({d.name: {"amount": d.amount, "voucher_type": "Journal Entry"}})
return vouchers, voucher_wise_amount
return vouchers, voucher_wise_amount, zero_rate_ldc_invoices
def get_payment_entry_vouchers(parties, tax_details, company, party_type="Supplier"):
@@ -507,7 +552,7 @@ def get_tds_amount(ldc, parties, inv, tax_details, vouchers):
)
supp_credit_amt = supp_jv_credit_amt
supp_credit_amt += inv.tax_withholding_net_total
supp_credit_amt += inv.get("tax_withholding_net_total", 0)
for type in payment_entry_amounts:
if type.payment_type == "Pay":
@@ -519,13 +564,15 @@ def get_tds_amount(ldc, parties, inv, tax_details, vouchers):
cumulative_threshold = tax_details.get("cumulative_threshold", 0)
if inv.doctype != "Payment Entry":
tax_withholding_net_total = inv.base_tax_withholding_net_total
tax_withholding_net_total = inv.get("base_tax_withholding_net_total", 0)
else:
tax_withholding_net_total = inv.tax_withholding_net_total
tax_withholding_net_total = inv.get("tax_withholding_net_total", 0)
if (threshold and tax_withholding_net_total >= threshold) or (
has_cumulative_threshold_breached = (
cumulative_threshold and (supp_credit_amt + supp_inv_credit_amt) >= cumulative_threshold
):
)
if (threshold and tax_withholding_net_total >= threshold) or (has_cumulative_threshold_breached):
# Get net total again as TDS is calculated on net total
# Grand is used to just check for threshold breach
net_total = (
@@ -533,10 +580,8 @@ def get_tds_amount(ldc, parties, inv, tax_details, vouchers):
)
supp_credit_amt += net_total
if (cumulative_threshold and supp_credit_amt >= cumulative_threshold) and cint(
tax_details.tax_on_excess_amount
):
supp_credit_amt = net_total - cumulative_threshold
if has_cumulative_threshold_breached and cint(tax_details.tax_on_excess_amount):
supp_credit_amt = net_total + tax_withholding_net_total - cumulative_threshold
if ldc and is_valid_certificate(ldc, inv.get("posting_date") or inv.get("transaction_date"), 0):
tds_amount = get_lower_deduction_amount(

View File

@@ -7,7 +7,7 @@ import unittest
import frappe
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import add_days, today
from frappe.utils import add_days, add_months, today
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
from erpnext.accounts.utils import get_fiscal_year
@@ -161,6 +161,45 @@ class TestTaxWithholdingCategory(FrappeTestCase):
for d in reversed(invoices):
d.cancel()
def test_cumulative_threshold_with_tax_on_excess_amount(self):
invoices = []
frappe.db.set_value("Supplier", "Test TDS Supplier3", "tax_withholding_category", "New TDS Category")
# Invoice with tax and without exceeding single and cumulative thresholds
for _ in range(2):
pi = create_purchase_invoice(supplier="Test TDS Supplier3", rate=10000, do_not_save=True)
pi.apply_tds = 1
pi.append(
"taxes",
{
"category": "Total",
"charge_type": "Actual",
"account_head": "_Test Account VAT - _TC",
"cost_center": "Main - _TC",
"tax_amount": 500,
"description": "Test",
"add_deduct_tax": "Add",
},
)
pi.save()
pi.submit()
invoices.append(pi)
# Third Invoice exceeds single threshold and not exceeding cumulative threshold
pi1 = create_purchase_invoice(supplier="Test TDS Supplier3", rate=20000)
pi1.apply_tds = 1
pi1.save()
pi1.submit()
invoices.append(pi1)
# Cumulative threshold is 10,000
# Threshold calculation should be only on the third invoice
self.assertTrue(len(pi1.taxes) > 0)
self.assertEqual(pi1.taxes[0].tax_amount, 1000)
for d in reversed(invoices):
d.cancel()
def test_cumulative_threshold_tcs(self):
frappe.db.set_value(
"Customer", "Test TCS Customer", "tax_withholding_category", "Cumulative Threshold TCS"
@@ -575,6 +614,49 @@ class TestTaxWithholdingCategory(FrappeTestCase):
pi2.cancel()
pi3.cancel()
def test_ldc_at_0_rate(self):
frappe.db.set_value(
"Supplier",
"Test LDC Supplier",
{
"tax_withholding_category": "Test Service Category",
"pan": "ABCTY1234D",
},
)
fiscal_year = get_fiscal_year(today(), company="_Test Company")
valid_from = fiscal_year[1]
valid_upto = add_months(valid_from, 1)
create_lower_deduction_certificate(
supplier="Test LDC Supplier",
certificate_no="1AE0423AAJ",
tax_withholding_category="Test Service Category",
tax_rate=0,
limit=50000,
valid_from=valid_from,
valid_upto=valid_upto,
)
pi1 = create_purchase_invoice(
supplier="Test LDC Supplier", rate=35000, posting_date=valid_from, set_posting_time=True
)
pi1.submit()
self.assertEqual(pi1.taxes, [])
pi2 = create_purchase_invoice(
supplier="Test LDC Supplier",
rate=35000,
posting_date=add_days(valid_upto, 1),
set_posting_time=True,
)
pi2.submit()
self.assertEqual(len(pi2.taxes), 1)
# pi1 net total shouldn't be included as it lies within LDC at rate of '0'
self.assertEqual(pi2.taxes[0].tax_amount, 3500)
pi1.cancel()
pi2.cancel()
def set_previous_fy_and_tax_category(self):
test_company = "_Test Company"
category = "Cumulative Threshold TDS"
@@ -732,7 +814,8 @@ def create_purchase_invoice(**args):
pi = frappe.get_doc(
{
"doctype": "Purchase Invoice",
"posting_date": today(),
"set_posting_time": args.set_posting_time or False,
"posting_date": args.posting_date or today(),
"apply_tds": 0 if args.do_not_apply_tds else 1,
"supplier": args.supplier,
"company": "_Test Company",
@@ -1060,7 +1143,9 @@ def create_tax_withholding_category(
).insert()
def create_lower_deduction_certificate(supplier, tax_withholding_category, tax_rate, certificate_no, limit):
def create_lower_deduction_certificate(
supplier, tax_withholding_category, tax_rate, certificate_no, limit, valid_from=None, valid_upto=None
):
fiscal_year = get_fiscal_year(today(), company="_Test Company")
if not frappe.db.exists("Lower Deduction Certificate", certificate_no):
frappe.get_doc(
@@ -1071,8 +1156,8 @@ def create_lower_deduction_certificate(supplier, tax_withholding_category, tax_r
"certificate_no": certificate_no,
"tax_withholding_category": tax_withholding_category,
"fiscal_year": fiscal_year[0],
"valid_from": fiscal_year[1],
"valid_upto": fiscal_year[2],
"valid_from": valid_from or fiscal_year[1],
"valid_upto": valid_upto or fiscal_year[2],
"rate": tax_rate,
"certificate_limit": limit,
}

View File

@@ -29,6 +29,12 @@ from erpnext.accounts.utils import get_fiscal_year
from erpnext.exceptions import InvalidAccountCurrency, PartyDisabled, PartyFrozen
from erpnext.utilities.regional import temporary_flag
try:
from frappe.contacts.doctype.address.address import render_address as _render_address
except ImportError:
# Older frappe versions where this function is not available
from frappe.contacts.doctype.address.address import get_address_display as _render_address
PURCHASE_TRANSACTION_TYPES = {
"Supplier Quotation",
"Purchase Order",
@@ -985,10 +991,4 @@ def add_party_account(party_type, party, company, account):
def render_address(address, check_permissions=True):
try:
from frappe.contacts.doctype.address.address import render_address as _render
except ImportError:
# Older frappe versions where this function is not available
from frappe.contacts.doctype.address.address import get_address_display as _render
return frappe.call(_render, address, check_permissions=check_permissions)
return frappe.call(_render_address, address, check_permissions=check_permissions)

View File

@@ -111,6 +111,7 @@ frappe.query_reports["Accounts Payable"] = {
fieldname: "party",
label: __("Party"),
fieldtype: "MultiSelectList",
options: "party_type",
get_data: function (txt) {
if (!frappe.query_report.filters) return;

View File

@@ -88,6 +88,7 @@ frappe.query_reports["Accounts Payable Summary"] = {
fieldname: "party",
label: __("Party"),
fieldtype: "MultiSelectList",
options: "party_type",
get_data: function (txt) {
if (!frappe.query_report.filters) return;

View File

@@ -56,6 +56,7 @@ frappe.query_reports["Accounts Receivable"] = {
fieldname: "party",
label: __("Party"),
fieldtype: "MultiSelectList",
options: "party_type",
get_data: function (txt) {
if (!frappe.query_report.filters) return;

View File

@@ -129,7 +129,6 @@ class ReceivablePayableReport:
paid_in_account_currency=0.0,
credit_note_in_account_currency=0.0,
outstanding_in_account_currency=0.0,
cost_center=ple.cost_center,
)
def init_voucher_balance(self):
@@ -145,6 +144,9 @@ class ReceivablePayableReport:
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
self.get_invoices(ple)
if self.filters.get("group_by_party"):
@@ -270,9 +272,6 @@ class ReceivablePayableReport:
row.paid -= amount
row.paid_in_account_currency -= amount_in_account_currency
if not row.cost_center and ple.cost_center:
row.cost_center = str(ple.cost_center)
def update_sub_total_row(self, row, party):
total_row = self.total_row_map.get(party)
@@ -529,9 +528,7 @@ class ReceivablePayableReport:
self.append_payment_term(row, d, term)
def append_payment_term(self, row, d, term):
if (
self.filters.get("customer") or self.filters.get("supplier")
) and d.currency == d.party_account_currency:
if d.currency == d.party_account_currency:
invoiced = d.payment_amount
else:
invoiced = d.base_payment_amount
@@ -1004,22 +1001,29 @@ class ReceivablePayableReport:
def get_columns(self):
self.columns = []
self.add_column("Posting Date", fieldtype="Date")
self.add_column(_("Posting Date"), fieldname="posting_date", fieldtype="Date")
self.add_column(
label="Party Type",
label=_("Party Type"),
fieldname="party_type",
fieldtype="Data",
width=100,
)
self.add_column(
label="Party",
label=_("Party"),
fieldname="party",
fieldtype="Dynamic Link",
options="party_type",
width=180,
)
if self.account_type == "Receivable":
label = _("Receivable Account")
elif self.account_type == "Payable":
label = _("Payable Account")
else:
label = _("Party Account")
self.add_column(
label=self.account_type + " Account",
label=label,
fieldname="party_account",
fieldtype="Link",
options="Account",
@@ -1028,10 +1032,10 @@ class ReceivablePayableReport:
if self.party_naming_by == "Naming Series":
if self.account_type == "Payable":
label = "Supplier Name"
label = _("Supplier Name")
fieldname = "supplier_name"
else:
label = "Customer Name"
label = _("Customer Name")
fieldname = "customer_name"
self.add_column(
label=label,
@@ -1057,7 +1061,7 @@ class ReceivablePayableReport:
width=180,
)
self.add_column(label="Due Date", fieldtype="Date")
self.add_column(label=_("Due Date"), fieldname="due_date", fieldtype="Date")
if self.account_type == "Payable":
self.add_column(label=_("Bill No"), fieldname="bill_no", fieldtype="Data")

View File

@@ -88,6 +88,7 @@ frappe.query_reports["Accounts Receivable Summary"] = {
fieldname: "party",
label: __("Party"),
fieldtype: "MultiSelectList",
options: "party_type",
get_data: function (txt) {
if (!frappe.query_report.filters) return;

View File

@@ -7,6 +7,7 @@ from frappe import _
from frappe.utils import cint, flt
from erpnext.accounts.report.financial_statements import (
compute_growth_view_data,
get_columns,
get_data,
get_filtered_list_for_consolidated_report,
@@ -101,6 +102,9 @@ def execute(filters=None):
period_list, asset, liability, equity, provisional_profit_loss, currency, filters
)
if filters.get("selected_view") == "Growth":
compute_growth_view_data(data, period_list)
return columns, data, message, chart, report_summary, primitive_summary

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

@@ -91,6 +91,7 @@ function get_filters() {
fieldname: "budget_against_filter",
label: __("Dimension Filter"),
fieldtype: "MultiSelectList",
options: "budget_against",
get_data: function (txt) {
if (!frappe.query_report.filters) return;

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

@@ -355,7 +355,7 @@ def get_data(companies, root_type, balance_must_be, fiscal_year, filters=None, i
gl_entries_by_account,
accounts_by_name,
accounts,
ignore_closing_entries=False,
ignore_closing_entries=ignore_closing_entries,
root_type=root_type,
)

View File

@@ -2,6 +2,7 @@
# License: GNU General Public License v3. See license.txt
import copy
import functools
import math
import re
@@ -511,12 +512,16 @@ def get_accounting_entries(
.where(gl_entry.company == filters.company)
)
ignore_is_opening = frappe.db.get_single_value(
"Accounts Settings", "ignore_is_opening_check_for_reporting"
)
if doctype == "GL Entry":
query = query.select(gl_entry.posting_date, gl_entry.is_opening, gl_entry.fiscal_year)
query = query.where(gl_entry.is_cancelled == 0)
query = query.where(gl_entry.posting_date <= to_date)
if ignore_opening_entries:
if ignore_opening_entries and not ignore_is_opening:
query = query.where(gl_entry.is_opening == "No")
else:
query = query.select(gl_entry.closing_date.as_("posting_date"))
@@ -525,9 +530,15 @@ def get_accounting_entries(
query = apply_additional_conditions(doctype, query, from_date, ignore_closing_entries, filters)
query = query.where(gl_entry.account.isin(accounts))
entries = query.run(as_dict=True)
from frappe.desk.reportview import build_match_conditions
return entries
query, params = query.walk()
match_conditions = build_match_conditions(doctype)
if match_conditions:
query += "and" + match_conditions
return frappe.db.sql(query, params, as_dict=True)
def apply_additional_conditions(doctype, query, from_date, ignore_closing_entries, filters):
@@ -653,3 +664,67 @@ def get_filtered_list_for_consolidated_report(filters, period_list):
filtered_summary_list.append(period)
return filtered_summary_list
def compute_growth_view_data(data, columns):
data_copy = copy.deepcopy(data)
for row_idx in range(len(data_copy)):
for column_idx in range(1, len(columns)):
previous_period_key = columns[column_idx - 1].get("key")
current_period_key = columns[column_idx].get("key")
current_period_value = data_copy[row_idx].get(current_period_key)
previous_period_value = data_copy[row_idx].get(previous_period_key)
annual_growth = 0
if current_period_value is None:
data[row_idx][current_period_key] = None
continue
if previous_period_value == 0 and current_period_value > 0:
annual_growth = 1
elif previous_period_value > 0:
annual_growth = (current_period_value - previous_period_value) / previous_period_value
growth_percent = round(annual_growth * 100, 2)
data[row_idx][current_period_key] = growth_percent
def compute_margin_view_data(data, columns, accumulated_values):
if not columns:
return
if not accumulated_values:
columns.append({"key": "total"})
data_copy = copy.deepcopy(data)
base_row = None
for row in data_copy:
if row.get("account_name") == _("Income"):
base_row = row
break
if not base_row:
return
for row_idx in range(len(data_copy)):
# Taking the total income from each column (for all the financial years) as the base (100%)
row = data_copy[row_idx]
if not row:
continue
for column in columns:
curr_period = column.get("key")
base_value = base_row[curr_period]
curr_value = row[curr_period]
if curr_value is None or base_value <= 0:
data[row_idx][curr_period] = None
continue
margin_percent = round((curr_value / base_value) * 100, 2)
data[row_idx][curr_period] = margin_percent

View File

@@ -68,6 +68,7 @@ frappe.query_reports["General Ledger"] = {
fieldname: "party",
label: __("Party"),
fieldtype: "MultiSelectList",
options: "party_type",
get_data: function (txt) {
if (!frappe.query_report.filters) return;
@@ -146,6 +147,7 @@ frappe.query_reports["General Ledger"] = {
fieldname: "cost_center",
label: __("Cost Center"),
fieldtype: "MultiSelectList",
options: "Cost Center",
get_data: function (txt) {
return frappe.db.get_link_options("Cost Center", txt, {
company: frappe.query_report.get_filter_value("company"),
@@ -156,6 +158,7 @@ frappe.query_reports["General Ledger"] = {
fieldname: "project",
label: __("Project"),
fieldtype: "MultiSelectList",
options: "Project",
get_data: function (txt) {
return frappe.db.get_link_options("Project", txt, {
company: frappe.query_report.get_filter_value("company"),

View File

@@ -209,6 +209,10 @@ def get_gl_entries(filters, accounting_dimensions):
def get_conditions(filters):
conditions = []
ignore_is_opening = frappe.db.get_single_value(
"Accounts Settings", "ignore_is_opening_check_for_reporting"
)
if filters.get("account"):
filters.account = get_accounts_with_children(filters.account)
if filters.account:
@@ -268,9 +272,15 @@ def get_conditions(filters):
or filters.get("party")
or filters.get("group_by") in ["Group by Account", "Group by Party"]
):
conditions.append("(posting_date >=%(from_date)s or is_opening = 'Yes')")
if not ignore_is_opening:
conditions.append("(posting_date >=%(from_date)s or is_opening = 'Yes')")
else:
conditions.append("posting_date >=%(from_date)s")
conditions.append("(posting_date <=%(to_date)s or is_opening = 'Yes')")
if not ignore_is_opening:
conditions.append("(posting_date <=%(to_date)s or is_opening = 'Yes')")
else:
conditions.append("posting_date <=%(to_date)s")
if filters.get("project"):
conditions.append("project in %(project)s")
@@ -496,6 +506,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:
@@ -607,10 +618,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

@@ -1,5 +1,5 @@
{
"add_total_row": 1,
"add_total_row": 0,
"columns": [],
"creation": "2013-02-25 17:03:34",
"disable_prepared_report": 0,
@@ -9,7 +9,7 @@
"filters": [],
"idx": 3,
"is_standard": "Yes",
"modified": "2022-02-11 10:18:36.956558",
"modified": "2025-01-27 18:40:24.493829",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Gross Profit",

View File

@@ -166,7 +166,14 @@ def get_data_when_grouped_by_invoice(columns, gross_profit_data, filters, group_
# removing Item Code and Item Name columns
del columns[4:6]
total_base_amount = 0
total_buying_amount = 0
for src in gross_profit_data.si_list:
if src.indent == 1:
total_base_amount += src.base_amount or 0.0
total_buying_amount += src.buying_amount or 0.0
row = frappe._dict()
row.indent = src.indent
row.parent_invoice = src.parent_invoice
@@ -177,17 +184,57 @@ def get_data_when_grouped_by_invoice(columns, gross_profit_data, filters, group_
data.append(row)
total_gross_profit = total_base_amount - total_buying_amount
data.append(
frappe._dict(
{
"sales_invoice": "Total",
"qty": None,
"avg._selling_rate": None,
"valuation_rate": None,
"selling_amount": total_base_amount,
"buying_amount": total_buying_amount,
"gross_profit": total_gross_profit,
"gross_profit_%": flt(
(total_gross_profit / total_base_amount) * 100.0,
cint(frappe.db.get_default("currency_precision")) or 3,
)
if total_base_amount
else 0,
}
)
)
def get_data_when_not_grouped_by_invoice(gross_profit_data, filters, group_wise_columns, data):
for src in gross_profit_data.grouped_data:
row = []
for col in group_wise_columns.get(scrub(filters.group_by)):
row.append(src.get(col))
total_base_amount = 0
total_buying_amount = 0
row.append(filters.currency)
group_columns = group_wise_columns.get(scrub(filters.group_by))
for src in gross_profit_data.grouped_data:
total_base_amount += src.base_amount or 0.00
total_buying_amount += src.buying_amount or 0.00
row = [src.get(col) for col in group_columns] + [filters.currency]
data.append(row)
total_gross_profit = total_base_amount - total_buying_amount
currency_precision = cint(frappe.db.get_default("currency_precision")) or 3
gross_profit_percent = (total_gross_profit / total_base_amount * 100.0) if total_base_amount else 0
total_row = {
group_columns[0]: "Total",
"base_amount": total_base_amount,
"buying_amount": total_buying_amount,
"gross_profit": total_gross_profit,
"gross_profit_percent": flt(gross_profit_percent, currency_precision),
}
total_row = [total_row.get(col, None) for col in [*group_columns, "currency"]]
data.append(total_row)
def get_columns(group_wise_columns, filters):
columns = []
@@ -402,10 +449,10 @@ class GrossProfitGenerator:
self.load_invoice_items()
self.get_delivery_notes()
self.load_product_bundle()
if filters.group_by == "Invoice":
self.group_items_by_invoice()
self.load_product_bundle()
self.load_non_stock_items()
self.get_returned_invoice_items()
self.process()
@@ -421,6 +468,7 @@ class GrossProfitGenerator:
if grouped_by_invoice:
buying_amount = 0
base_amount = 0
for row in reversed(self.si_list):
if self.filters.get("group_by") == "Monthly":
@@ -461,12 +509,11 @@ class GrossProfitGenerator:
else:
row.buying_amount = flt(self.get_buying_amount(row, row.item_code), self.currency_precision)
if grouped_by_invoice:
if row.indent == 1.0:
buying_amount += row.buying_amount
elif row.indent == 0.0:
row.buying_amount = buying_amount
buying_amount = 0
if grouped_by_invoice and row.indent == 0.0:
row.buying_amount = buying_amount
row.base_amount = base_amount
buying_amount = 0
base_amount = 0
# get buying rate
if flt(row.qty):
@@ -476,11 +523,19 @@ class GrossProfitGenerator:
if self.is_not_invoice_row(row):
row.buying_rate, row.base_rate = 0.0, 0.0
if self.is_not_invoice_row(row):
self.update_return_invoices(row)
if grouped_by_invoice and row.indent == 1.0:
buying_amount += row.buying_amount
base_amount += row.base_amount
# calculate gross profit
row.gross_profit = flt(row.base_amount - row.buying_amount, self.currency_precision)
if row.base_amount:
row.gross_profit_percent = flt(
(row.gross_profit / row.base_amount) * 100.0, self.currency_precision
(row.gross_profit / row.base_amount) * 100.0,
self.currency_precision,
)
else:
row.gross_profit_percent = 0.0
@@ -491,33 +546,29 @@ class GrossProfitGenerator:
if self.grouped:
self.get_average_rate_based_on_group_by()
def update_return_invoices(self, row):
if row.parent in self.returned_invoices and row.item_code in self.returned_invoices[row.parent]:
returned_item_rows = self.returned_invoices[row.parent][row.item_code]
for returned_item_row in returned_item_rows:
# returned_items 'qty' should be stateful
if returned_item_row.qty != 0:
if row.qty >= abs(returned_item_row.qty):
row.qty += returned_item_row.qty
row.base_amount += flt(returned_item_row.base_amount, self.currency_precision)
returned_item_row.qty = 0
returned_item_row.base_amount = 0
else:
row.qty = 0
row.base_amount = 0
returned_item_row.qty += row.qty
returned_item_row.base_amount += row.base_amount
row.buying_amount = flt(flt(row.qty) * flt(row.buying_rate), self.currency_precision)
def get_average_rate_based_on_group_by(self):
for key in list(self.grouped):
if self.filters.get("group_by") == "Invoice":
for row in self.grouped[key]:
if row.indent == 1.0:
if (
row.parent in self.returned_invoices
and row.item_code in self.returned_invoices[row.parent]
):
returned_item_rows = self.returned_invoices[row.parent][row.item_code]
for returned_item_row in returned_item_rows:
# returned_items 'qty' should be stateful
if returned_item_row.qty != 0:
if row.qty >= abs(returned_item_row.qty):
row.qty += returned_item_row.qty
returned_item_row.qty = 0
else:
row.qty = 0
returned_item_row.qty += row.qty
row.base_amount += flt(returned_item_row.base_amount, self.currency_precision)
row.buying_amount = flt(
flt(row.qty) * flt(row.buying_rate), self.currency_precision
)
if flt(row.qty) or row.base_amount:
row = self.set_average_rate(row)
self.grouped_data.append(row)
elif self.filters.get("group_by") == "Payment Term":
if self.filters.get("group_by") == "Payment Term":
for i, row in enumerate(self.grouped[key]):
invoice_portion = 0
@@ -537,7 +588,7 @@ class GrossProfitGenerator:
new_row = self.set_average_rate(new_row)
self.grouped_data.append(new_row)
else:
elif self.filters.get("group_by") != "Invoice":
for i, row in enumerate(self.grouped[key]):
if i == 0:
new_row = row
@@ -613,6 +664,7 @@ class GrossProfitGenerator:
if packed_item.get("parent_detail_docname") == row.item_row:
packed_item_row = row.copy()
packed_item_row.warehouse = packed_item.warehouse
packed_item_row.qty = packed_item.total_qty * -1
buying_amount += self.get_buying_amount(packed_item_row, packed_item.item_code)
return flt(buying_amount, self.currency_precision)
@@ -645,7 +697,9 @@ class GrossProfitGenerator:
else:
my_sle = self.get_stock_ledger_entries(item_code, row.warehouse)
if (row.update_stock or row.dn_detail) and my_sle:
parenttype, parent = row.parenttype, row.parent
parenttype = row.parenttype
parent = row.invoice or row.parent
if row.dn_detail:
parenttype, parent = "Delivery Note", row.delivery_note
@@ -719,12 +773,13 @@ class GrossProfitGenerator:
.inner_join(purchase_invoice)
.on(purchase_invoice.name == purchase_invoice_item.parent)
.select(
purchase_invoice.name,
purchase_invoice_item.base_rate / purchase_invoice_item.conversion_factor,
)
.where(purchase_invoice.docstatus == 1)
.where(purchase_invoice.posting_date <= self.filters.to_date)
.where(purchase_invoice_item.item_code == item_code)
.where(purchase_invoice.is_return == 0)
.where(purchase_invoice_item.parenttype == "Purchase Invoice")
)
if row.project:
@@ -761,7 +816,10 @@ class GrossProfitGenerator:
"""
if self.filters.group_by == "Sales Person":
sales_person_cols = ", sales.sales_person, sales.allocated_amount, sales.incentives"
sales_person_cols = """, sales.sales_person,
sales.allocated_percentage * `tabSales Invoice Item`.base_net_amount / 100 as allocated_amount,
sales.incentives
"""
sales_team_table = "left join `tabSales Team` sales on sales.parent = `tabSales Invoice`.name"
else:
sales_person_cols = ""
@@ -793,6 +851,7 @@ class GrossProfitGenerator:
`tabSales Invoice`.project, `tabSales Invoice`.update_stock,
`tabSales Invoice`.customer, `tabSales Invoice`.customer_group,
`tabSales Invoice`.territory, `tabSales Invoice Item`.item_code,
`tabSales Invoice`.base_net_total as "invoice_base_net_total",
`tabSales Invoice Item`.item_name, `tabSales Invoice Item`.description,
`tabSales Invoice Item`.warehouse, `tabSales Invoice Item`.item_group,
`tabSales Invoice Item`.brand, `tabSales Invoice Item`.so_detail,
@@ -853,6 +912,7 @@ class GrossProfitGenerator:
"""
grouped = OrderedDict()
product_bundles = self.product_bundles.get("Sales Invoice", {})
for row in self.si_list:
# initialize list with a header row for each new parent
@@ -863,8 +923,7 @@ class GrossProfitGenerator:
)
# if item is a bundle, add it's components as seperate rows
if frappe.db.exists("Product Bundle", row.item_code):
bundled_items = self.get_bundle_items(row)
if bundled_items := product_bundles.get(row.parent, {}).get(row.item_code):
for x in bundled_items:
bundle_item = self.get_bundle_item_row(row, x)
grouped.get(row.parent).append(bundle_item)
@@ -900,47 +959,40 @@ class GrossProfitGenerator:
"item_row": None,
"is_return": row.is_return,
"cost_center": row.cost_center,
"base_net_amount": frappe.db.get_value("Sales Invoice", row.parent, "base_net_total"),
"base_net_amount": row.invoice_base_net_total,
}
)
def get_bundle_items(self, product_bundle):
return frappe.get_all(
"Product Bundle Item", filters={"parent": product_bundle.item_code}, fields=["item_code", "qty"]
)
def get_bundle_item_row(self, product_bundle, item):
item_name, description, item_group, brand = self.get_bundle_item_details(item.item_code)
def get_bundle_item_row(self, row, item):
return frappe._dict(
{
"parent_invoice": product_bundle.item_code,
"indent": product_bundle.indent + 1,
"parent_invoice": row.item_code,
"parenttype": row.parenttype,
"indent": row.indent + 1,
"parent": None,
"invoice_or_item": item.item_code,
"posting_date": product_bundle.posting_date,
"posting_time": product_bundle.posting_time,
"project": product_bundle.project,
"customer": product_bundle.customer,
"customer_group": product_bundle.customer_group,
"posting_date": row.posting_date,
"posting_time": row.posting_time,
"project": row.project,
"customer": row.customer,
"customer_group": row.customer_group,
"item_code": item.item_code,
"item_name": item_name,
"description": description,
"warehouse": product_bundle.warehouse,
"item_group": item_group,
"brand": brand,
"dn_detail": product_bundle.dn_detail,
"delivery_note": product_bundle.delivery_note,
"qty": (flt(product_bundle.qty) * flt(item.qty)),
"item_row": None,
"is_return": product_bundle.is_return,
"cost_center": product_bundle.cost_center,
"item_name": item.item_name,
"description": item.description,
"warehouse": item.warehouse or row.warehouse,
"update_stock": row.update_stock,
"item_group": "",
"brand": "",
"dn_detail": row.dn_detail,
"delivery_note": row.delivery_note,
"qty": item.total_qty * -1,
"item_row": row.item_row,
"is_return": row.is_return,
"cost_center": row.cost_center,
"invoice": row.parent,
}
)
def get_bundle_item_details(self, item_code):
return frappe.db.get_value("Item", item_code, ["item_name", "description", "item_group", "brand"])
def get_stock_ledger_entries(self, item_code, warehouse):
if item_code and warehouse:
if (item_code, warehouse) not in self.sle:

View File

@@ -418,12 +418,12 @@ class TestGrossProfit(FrappeTestCase):
"item_name": self.item,
"warehouse": "Stores - _GP",
"qty": 0.0,
"avg._selling_rate": 0.0,
"avg._selling_rate": 100,
"valuation_rate": 0.0,
"selling_amount": -100.0,
"selling_amount": 0.0,
"buying_amount": 0.0,
"gross_profit": -100.0,
"gross_profit_%": 100.0,
"gross_profit": 0.0,
"gross_profit_%": 0.0,
}
gp_entry = [x for x in data if x.parent_invoice == sinv.name]
# Both items of Invoice should have '0' qty
@@ -558,3 +558,33 @@ class TestGrossProfit(FrappeTestCase):
}
gp_entry = [x for x in data if x.parent_invoice == sinv.name]
self.assertDictContainsSubset(expected_entry, gp_entry[0])
def test_gross_profit_groupby_invoices(self):
create_sales_invoice(
qty=1,
rate=100,
company=self.company,
customer=self.customer,
item_code=self.item,
item_name=self.item,
cost_center=self.cost_center,
warehouse=self.warehouse,
debit_to=self.debit_to,
parent_cost_center=self.cost_center,
update_stock=0,
currency="INR",
income_account=self.income_account,
expense_account=self.expense_account,
)
filters = frappe._dict(
company=self.company, from_date=nowdate(), to_date=nowdate(), group_by="Invoice"
)
_, data = execute(filters=filters)
total = data[-1]
self.assertEqual(total.selling_amount, 100.0)
self.assertEqual(total.buying_amount, 0.0)
self.assertEqual(total.gross_profit, 100.0)
self.assertEqual(total.get("gross_profit_%"), 100.0)

View File

@@ -51,6 +51,7 @@ function get_filters() {
fieldname: "party",
label: __("Party"),
fieldtype: "MultiSelectList",
options: "party_type",
get_data: function (txt) {
if (!frappe.query_report.filters) return;

View File

@@ -7,6 +7,8 @@ from frappe import _
from frappe.utils import flt
from erpnext.accounts.report.financial_statements import (
compute_growth_view_data,
compute_margin_view_data,
get_columns,
get_data,
get_filtered_list_for_consolidated_report,
@@ -68,6 +70,12 @@ def execute(filters=None):
period_list, filters.periodicity, income, expense, net_profit_loss, currency, filters
)
if filters.get("selected_view") == "Growth":
compute_growth_view_data(data, period_list)
if filters.get("selected_view") == "Margin":
compute_margin_view_data(data, period_list, filters.accumulated_values)
return columns, data, None, chart, report_summary, primitive_summary

View File

@@ -1,840 +0,0 @@
[
{
"account_manager": null,
"accounts": [],
"companies": [],
"credit_limits": [],
"customer_details": null,
"customer_group": "All Customer Groups",
"customer_name": "_Test Customer",
"customer_pos_id": null,
"customer_primary_address": null,
"customer_primary_contact": null,
"customer_type": "Company",
"default_bank_account": null,
"default_commission_rate": 0.0,
"default_currency": null,
"default_price_list": null,
"default_sales_partner": null,
"disabled": 0,
"dn_required": 0,
"docstatus": 0,
"doctype": "Customer",
"email_id": null,
"gender": null,
"image": null,
"industry": null,
"is_frozen": 0,
"is_internal_customer": 0,
"language": "en",
"lead_name": null,
"loyalty_program": null,
"loyalty_program_tier": null,
"market_segment": null,
"mobile_no": null,
"modified": "2021-02-15 05:18:03.624724",
"name": "_Test Customer",
"naming_series": "CUST-.YYYY.-",
"pan": null,
"parent": null,
"parentfield": null,
"parenttype": null,
"payment_terms": null,
"primary_address": null,
"represents_company": "",
"sales_team": [],
"salutation": null,
"so_required": 0,
"tax_category": null,
"tax_id": null,
"tax_withholding_category": null,
"territory": "All Territories",
"website": null
},{
"accounts": [],
"allow_purchase_invoice_creation_without_purchase_order": 0,
"allow_purchase_invoice_creation_without_purchase_receipt": 0,
"companies": [],
"country": "United Kingdom",
"default_bank_account": null,
"default_currency": null,
"default_price_list": null,
"disabled": 0,
"docstatus": 0,
"doctype": "Supplier",
"hold_type": "",
"image": null,
"is_frozen": 0,
"is_internal_supplier": 0,
"is_transporter": 0,
"language": "en",
"modified": "2021-03-31 16:47:10.109316",
"name": "_Test Supplier",
"naming_series": "SUP-.YYYY.-",
"on_hold": 0,
"pan": null,
"parent": null,
"parentfield": null,
"parenttype": null,
"payment_terms": null,
"prevent_pos": 0,
"prevent_rfqs": 0,
"release_date": null,
"represents_company": null,
"supplier_details": null,
"supplier_group": "Raw Material",
"supplier_name": "_Test Supplier",
"supplier_type": "Company",
"tax_category": null,
"tax_id": null,
"tax_withholding_category": null,
"warn_pos": 0,
"warn_rfqs": 0,
"website": null
},{
"account_currency": "GBP",
"account_name": "Debtors",
"account_number": "",
"account_type": "Receivable",
"balance_must_be": "",
"company": "_T",
"disabled": 0,
"docstatus": 0,
"doctype": "Account",
"freeze_account": "No",
"include_in_gross": 0,
"inter_company_account": 0,
"is_group": 0,
"lft": 58,
"modified": "2021-03-26 04:44:19.955468",
"name": "Debtors - _T",
"old_parent": null,
"parent": null,
"parent_account": "Application of Funds (Assets) - _T",
"parentfield": null,
"parenttype": null,
"report_type": "Balance Sheet",
"rgt": 59,
"root_type": "Asset",
"tax_rate": 0.0
},{
"account_currency": "GBP",
"account_name": "Sales",
"account_number": "",
"account_type": "Income Account",
"balance_must_be": "",
"company": "_T",
"disabled": 0,
"docstatus": 0,
"doctype": "Account",
"freeze_account": "No",
"include_in_gross": 0,
"inter_company_account": 0,
"is_group": 0,
"lft": 291,
"modified": "2021-03-26 04:50:21.697703",
"name": "Sales - _T",
"old_parent": null,
"parent": null,
"parent_account": "Income - _T",
"parentfield": null,
"parenttype": null,
"report_type": "Profit and Loss",
"rgt": 292,
"root_type": "Income",
"tax_rate": 0.0
},{
"account_currency": "GBP",
"account_name": "VAT on Sales",
"account_number": "",
"account_type": "Tax",
"balance_must_be": "",
"company": "_T",
"disabled": 0,
"docstatus": 0,
"doctype": "Account",
"freeze_account": "No",
"include_in_gross": 0,
"inter_company_account": 0,
"is_group": 0,
"lft": 317,
"modified": "2021-03-26 04:50:21.697703",
"name": "VAT on Sales - _T",
"old_parent": null,
"parent": null,
"parent_account": "Source of Funds (Liabilities) - _T",
"parentfield": null,
"parenttype": null,
"report_type": "Balance Sheet",
"rgt": 318,
"root_type": "Liability",
"tax_rate": 0.0
},{
"account_currency": "GBP",
"account_name": "Cost of Goods Sold",
"account_number": "",
"account_type": "Cost of Goods Sold",
"balance_must_be": "",
"company": "_T",
"disabled": 0,
"docstatus": 0,
"doctype": "Account",
"freeze_account": "No",
"include_in_gross": 0,
"inter_company_account": 0,
"is_group": 0,
"lft": 171,
"modified": "2021-03-26 04:44:19.994857",
"name": "Cost of Goods Sold - _T",
"old_parent": null,
"parent": null,
"parent_account": "Expenses - _T",
"parentfield": null,
"parenttype": null,
"report_type": "Profit and Loss",
"rgt": 172,
"root_type": "Expense",
"tax_rate": 0.0
},{
"account_currency": "GBP",
"account_name": "VAT on Purchases",
"account_number": "",
"account_type": "Tax",
"balance_must_be": "",
"company": "_T",
"disabled": 0,
"docstatus": 0,
"doctype": "Account",
"freeze_account": "No",
"include_in_gross": 0,
"inter_company_account": 0,
"is_group": 0,
"lft": 80,
"modified": "2021-03-26 04:44:19.961983",
"name": "VAT on Purchases - _T",
"old_parent": null,
"parent": null,
"parent_account": "Application of Funds (Assets) - _T",
"parentfield": null,
"parenttype": null,
"report_type": "Balance Sheet",
"rgt": 81,
"root_type": "Asset",
"tax_rate": 0.0
},{
"account_currency": "GBP",
"account_name": "Creditors",
"account_number": "",
"account_type": "Payable",
"balance_must_be": "",
"company": "_T",
"disabled": 0,
"docstatus": 0,
"doctype": "Account",
"freeze_account": "No",
"include_in_gross": 0,
"inter_company_account": 0,
"is_group": 0,
"lft": 302,
"modified": "2021-03-26 04:50:21.697703",
"name": "Creditors - _T",
"old_parent": null,
"parent": null,
"parent_account": "Source of Funds (Liabilities) - _T",
"parentfield": null,
"parenttype": null,
"report_type": "Balance Sheet",
"rgt": 303,
"root_type": "Liability",
"tax_rate": 0.0
},{
"additional_discount_percentage": 0.0,
"address_display": null,
"adjust_advance_taxes": 0,
"advances": [],
"against_expense_account": "Cost of Goods Sold - _T",
"allocate_advances_automatically": 0,
"amended_from": null,
"apply_discount_on": "Grand Total",
"apply_tds": 0,
"auto_repeat": null,
"base_discount_amount": 0.0,
"base_grand_total": 511.68,
"base_in_words": "GBP Five Hundred And Eleven and Sixty Eight Pence only.",
"base_net_total": 426.4,
"base_paid_amount": 0.0,
"base_rounded_total": 511.68,
"base_rounding_adjustment": 0.0,
"base_taxes_and_charges_added": 85.28,
"base_taxes_and_charges_deducted": 0.0,
"base_total": 426.4,
"base_total_taxes_and_charges": 85.28,
"base_write_off_amount": 0.0,
"bill_date": null,
"bill_no": null,
"billing_address": null,
"billing_address_display": null,
"buying_price_list": "Standard Buying",
"cash_bank_account": null,
"clearance_date": null,
"company": "_T",
"contact_display": null,
"contact_email": null,
"contact_mobile": null,
"contact_person": null,
"conversion_rate": 1.0,
"cost_center": null,
"credit_to": "Creditors - _T",
"currency": "GBP",
"disable_rounded_total": 0,
"discount_amount": 0.0,
"docstatus": 0,
"doctype": "Purchase Invoice",
"due_date": null,
"from_date": null,
"grand_total": 511.68,
"group_same_items": 0,
"hold_comment": null,
"ignore_pricing_rule": 0,
"in_words": "GBP Five Hundred And Eleven and Sixty Eight Pence only.",
"inter_company_invoice_reference": null,
"is_internal_supplier": 0,
"is_opening": "No",
"is_paid": 0,
"is_return": 0,
"is_subcontracted": 0,
"items": [
{
"allow_zero_valuation_rate": 0,
"amount": 426.4,
"asset_category": null,
"asset_location": null,
"base_amount": 426.4,
"base_net_amount": 426.4,
"base_net_rate": 5.33,
"base_price_list_rate": 5.33,
"base_rate": 5.33,
"base_rate_with_margin": 0.0,
"batch_no": null,
"bom": null,
"brand": null,
"conversion_factor": 0.0,
"cost_center": "Main - _T",
"deferred_expense_account": null,
"description": "<div class=\"ql-editor read-mode\"><p>Fluid to make widgets</p></div>",
"discount_amount": 0.0,
"discount_percentage": 0.0,
"enable_deferred_expense": 0,
"expense_account": "Cost of Goods Sold - _T",
"from_warehouse": null,
"image": null,
"include_exploded_items": 0,
"is_fixed_asset": 0,
"is_free_item": 0,
"item_code": null,
"item_group": null,
"item_name": "Widget Fluid 1Litre",
"item_tax_amount": 0.0,
"item_tax_rate": "{\"VAT on Purchases - _T\": 20.0}",
"item_tax_template": null,
"landed_cost_voucher_amount": 0.0,
"manufacturer": null,
"manufacturer_part_no": null,
"margin_rate_or_amount": 0.0,
"margin_type": "",
"net_amount": 426.4,
"net_rate": 5.33,
"page_break": 0,
"parent": null,
"parentfield": "items",
"parenttype": "Purchase Invoice",
"po_detail": null,
"pr_detail": null,
"price_list_rate": 5.33,
"pricing_rules": null,
"project": null,
"purchase_invoice_item": null,
"purchase_order": null,
"purchase_receipt": null,
"qty": 80.0,
"quality_inspection": null,
"rate": 5.33,
"rate_with_margin": 0.0,
"received_qty": 0.0,
"rejected_qty": 0.0,
"rejected_serial_no": null,
"rejected_warehouse": null,
"rm_supp_cost": 0.0,
"sales_invoice_item": null,
"serial_no": null,
"service_end_date": null,
"service_start_date": null,
"service_stop_date": null,
"stock_qty": 0.0,
"stock_uom": "Nos",
"stock_uom_rate": 0.0,
"total_weight": 0.0,
"uom": "Nos",
"valuation_rate": 0.0,
"warehouse": null,
"weight_per_unit": 0.0,
"weight_uom": null
}
],
"language": "en",
"letter_head": null,
"mode_of_payment": null,
"modified": "2021-04-03 03:33:09.180453",
"name": null,
"naming_series": "ACC-PINV-.YYYY.-",
"net_total": 426.4,
"on_hold": 0,
"other_charges_calculation": "<div class=\"tax-break-up\" style=\"overflow-x: auto;\">\n\t<table class=\"table table-bordered table-hover\">\n\t\t<thead>\n\t\t\t<tr>\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t<th class=\"text-left\">Item</th>\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t<th class=\"text-right\">Taxable Amount</th>\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t<th class=\"text-right\">VAT on Purchases</th>\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t</tr>\n\t\t</thead>\n\t\t<tbody>\n\t\t\t\n\t\t\t\t<tr>\n\t\t\t\t\t<td>Widget Fluid 1Litre</td>\n\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\u00a3 426.40\n\t\t\t\t\t\t\n\t\t\t\t\t</td>\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t(20.0%)\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\u00a3 85.28\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t</tr>\n\t\t\t\n\t\t</tbody>\n\t</table>\n</div>",
"outstanding_amount": 511.68,
"paid_amount": 0.0,
"parent": null,
"parentfield": null,
"parenttype": null,
"party_account_currency": "GBP",
"payment_schedule": [],
"payment_terms_template": null,
"plc_conversion_rate": 1.0,
"posting_date": null,
"posting_time": "16:59:56.789522",
"price_list_currency": "GBP",
"pricing_rules": [],
"project": null,
"rejected_warehouse": null,
"release_date": null,
"remarks": "No Remarks",
"represents_company": null,
"return_against": null,
"rounded_total": 511.68,
"rounding_adjustment": 0.0,
"scan_barcode": null,
"select_print_heading": null,
"set_from_warehouse": null,
"set_posting_time": 0,
"set_warehouse": null,
"shipping_address": null,
"shipping_address_display": "",
"shipping_rule": null,
"status": "Unpaid",
"supplied_items": [],
"supplier": "_Test Supplier",
"supplier_address": null,
"supplier_name": "_Test Supplier",
"supplier_warehouse": "Stores - _T",
"tax_category": null,
"tax_id": null,
"tax_withholding_category": null,
"taxes": [
{
"account_head": "VAT on Purchases - _T",
"add_deduct_tax": "Add",
"base_tax_amount": 85.28,
"base_tax_amount_after_discount_amount": 85.28,
"base_total": 511.68,
"category": "Total",
"charge_type": "On Net Total",
"cost_center": "Main - _T",
"description": "VAT on Purchases",
"included_in_print_rate": 0,
"item_wise_tax_detail": "{\"Widget Fluid 1Litre\":[20.0,85.28]}",
"parent": null,
"parentfield": "taxes",
"parenttype": "Purchase Invoice",
"rate": 0.0,
"row_id": null,
"tax_amount": 85.28,
"tax_amount_after_discount_amount": 85.28,
"total": 511.68
}
],
"taxes_and_charges": null,
"taxes_and_charges_added": 85.28,
"taxes_and_charges_deducted": 0.0,
"tc_name": null,
"terms": null,
"title": "_Purchase Invoice",
"to_date": null,
"total": 426.4,
"total_advance": 0.0,
"total_net_weight": 0.0,
"total_qty": 80.0,
"total_taxes_and_charges": 85.28,
"unrealized_profit_loss_account": null,
"update_stock": 0,
"write_off_account": null,
"write_off_amount": 0.0,
"write_off_cost_center": null
},{
"account_for_change_amount": null,
"additional_discount_percentage": 0.0,
"address_display": null,
"advances": [],
"against_income_account": "Sales - _T",
"allocate_advances_automatically": 0,
"amended_from": null,
"apply_discount_on": "Grand Total",
"auto_repeat": null,
"base_change_amount": 0.0,
"base_discount_amount": 0.0,
"base_grand_total": 868.25,
"base_in_words": "GBP Eight Hundred And Sixty Eight and Twenty Five Pence only.",
"base_net_total": 825.0,
"base_paid_amount": 0.0,
"base_rounded_total": 868.25,
"base_rounding_adjustment": 0.0,
"base_total": 825.0,
"base_total_taxes_and_charges": 43.25,
"base_write_off_amount": 0.0,
"c_form_applicable": "No",
"c_form_no": null,
"campaign": null,
"cash_bank_account": null,
"change_amount": 0.0,
"commission_rate": 0.0,
"company": "_T",
"company_address": null,
"company_address_display": null,
"company_tax_id": null,
"contact_display": null,
"contact_email": null,
"contact_mobile": null,
"contact_person": null,
"conversion_rate": 1.0,
"cost_center": null,
"currency": "GBP",
"customer": "_Test Customer",
"customer_address": null,
"customer_group": "All Customer Groups",
"customer_name": "_Test Customer",
"debit_to": "Debtors - _T",
"discount_amount": 0.0,
"docstatus": 0,
"doctype": "Sales Invoice",
"due_date": null,
"from_date": null,
"grand_total": 868.25,
"group_same_items": 0,
"ignore_pricing_rule": 0,
"in_words": "GBP Eight Hundred And Sixty Eight and Twenty Five Pence only.",
"inter_company_invoice_reference": null,
"is_consolidated": 0,
"is_discounted": 0,
"is_internal_customer": 0,
"is_opening": "No",
"is_pos": 0,
"is_return": 0,
"items": [
{
"actual_batch_qty": 0.0,
"actual_qty": 0.0,
"allow_zero_valuation_rate": 0,
"amount": 200.0,
"asset": null,
"barcode": null,
"base_amount": 200.0,
"base_net_amount": 200.0,
"base_net_rate": 50.0,
"base_price_list_rate": 0.0,
"base_rate": 50.0,
"base_rate_with_margin": 0.0,
"batch_no": null,
"brand": null,
"conversion_factor": 1.0,
"cost_center": "Main - _T",
"customer_item_code": null,
"deferred_revenue_account": null,
"delivered_by_supplier": 0,
"delivered_qty": 0.0,
"delivery_note": null,
"description": "<div class=\"ql-editor read-mode\"><p>Used</p></div>",
"discount_amount": 0.0,
"discount_percentage": 0.0,
"dn_detail": null,
"enable_deferred_revenue": 0,
"expense_account": null,
"finance_book": null,
"image": null,
"income_account": "Sales - _T",
"incoming_rate": 0.0,
"is_fixed_asset": 0,
"is_free_item": 0,
"item_code": null,
"item_group": null,
"item_name": "Dunlop tyres",
"item_tax_rate": "{\"VAT on Sales - _T\": 20.0}",
"item_tax_template": null,
"margin_rate_or_amount": 0.0,
"margin_type": "",
"net_amount": 200.0,
"net_rate": 50.0,
"page_break": 0,
"parent": null,
"parentfield": "items",
"parenttype": "Sales Invoice",
"price_list_rate": 0.0,
"pricing_rules": null,
"project": null,
"qty": 4.0,
"quality_inspection": null,
"rate": 50.0,
"rate_with_margin": 0.0,
"sales_invoice_item": null,
"sales_order": null,
"serial_no": null,
"service_end_date": null,
"service_start_date": null,
"service_stop_date": null,
"so_detail": null,
"stock_qty": 4.0,
"stock_uom": "Nos",
"stock_uom_rate": 50.0,
"target_warehouse": null,
"total_weight": 0.0,
"uom": "Nos",
"warehouse": null,
"weight_per_unit": 0.0,
"weight_uom": null
},
{
"actual_batch_qty": 0.0,
"actual_qty": 0.0,
"allow_zero_valuation_rate": 0,
"amount": 65.0,
"asset": null,
"barcode": null,
"base_amount": 65.0,
"base_net_amount": 65.0,
"base_net_rate": 65.0,
"base_price_list_rate": 0.0,
"base_rate": 65.0,
"base_rate_with_margin": 0.0,
"batch_no": null,
"brand": null,
"conversion_factor": 1.0,
"cost_center": "Main - _T",
"customer_item_code": null,
"deferred_revenue_account": null,
"delivered_by_supplier": 0,
"delivered_qty": 0.0,
"delivery_note": null,
"description": "<div class=\"ql-editor read-mode\"><p>Used</p></div>",
"discount_amount": 0.0,
"discount_percentage": 0.0,
"dn_detail": null,
"enable_deferred_revenue": 0,
"expense_account": null,
"finance_book": null,
"image": null,
"income_account": "Sales - _T",
"incoming_rate": 0.0,
"is_fixed_asset": 0,
"is_free_item": 0,
"item_code": "",
"item_group": null,
"item_name": "Continental tyres",
"item_tax_rate": "{\"VAT on Sales - _T\": 5.0}",
"item_tax_template": null,
"margin_rate_or_amount": 0.0,
"margin_type": "",
"net_amount": 65.0,
"net_rate": 65.0,
"page_break": 0,
"parent": null,
"parentfield": "items",
"parenttype": "Sales Invoice",
"price_list_rate": 0.0,
"pricing_rules": null,
"project": null,
"qty": 1.0,
"quality_inspection": null,
"rate": 65.0,
"rate_with_margin": 0.0,
"sales_invoice_item": null,
"sales_order": null,
"serial_no": null,
"service_end_date": null,
"service_start_date": null,
"service_stop_date": null,
"so_detail": null,
"stock_qty": 1.0,
"stock_uom": null,
"stock_uom_rate": 65.0,
"target_warehouse": null,
"total_weight": 0.0,
"uom": "Nos",
"warehouse": null,
"weight_per_unit": 0.0,
"weight_uom": null
},
{
"actual_batch_qty": 0.0,
"actual_qty": 0.0,
"allow_zero_valuation_rate": 0,
"amount": 560.0,
"asset": null,
"barcode": null,
"base_amount": 560.0,
"base_net_amount": 560.0,
"base_net_rate": 70.0,
"base_price_list_rate": 0.0,
"base_rate": 70.0,
"base_rate_with_margin": 0.0,
"batch_no": null,
"brand": null,
"conversion_factor": 1.0,
"cost_center": "Main - _T",
"customer_item_code": null,
"deferred_revenue_account": null,
"delivered_by_supplier": 0,
"delivered_qty": 0.0,
"delivery_note": null,
"description": "<div class=\"ql-editor read-mode\"><p>New</p></div>",
"discount_amount": 0.0,
"discount_percentage": 0.0,
"dn_detail": null,
"enable_deferred_revenue": 0,
"expense_account": null,
"finance_book": null,
"image": null,
"income_account": "Sales - _T",
"incoming_rate": 0.0,
"is_fixed_asset": 0,
"is_free_item": 0,
"item_code": null,
"item_group": null,
"item_name": "Toyo tyres",
"item_tax_rate": "{\"VAT on Sales - _T\": 0.0}",
"item_tax_template": null,
"margin_rate_or_amount": 0.0,
"margin_type": "",
"net_amount": 560.0,
"net_rate": 70.0,
"page_break": 0,
"parent": null,
"parentfield": "items",
"parenttype": "Sales Invoice",
"price_list_rate": 0.0,
"pricing_rules": null,
"project": null,
"qty": 8.0,
"quality_inspection": null,
"rate": 70.0,
"rate_with_margin": 0.0,
"sales_invoice_item": null,
"sales_order": null,
"serial_no": null,
"service_end_date": null,
"service_start_date": null,
"service_stop_date": null,
"so_detail": null,
"stock_qty": 8.0,
"stock_uom": null,
"stock_uom_rate": 70.0,
"target_warehouse": null,
"total_weight": 0.0,
"uom": "Nos",
"warehouse": null,
"weight_per_unit": 0.0,
"weight_uom": null
}
],
"language": "en",
"letter_head": null,
"loyalty_amount": 0.0,
"loyalty_points": 0,
"loyalty_program": null,
"loyalty_redemption_account": null,
"loyalty_redemption_cost_center": null,
"modified": "2021-02-16 05:18:59.755144",
"name": null,
"naming_series": "ACC-SINV-.YYYY.-",
"net_total": 825.0,
"other_charges_calculation": "<div class=\"tax-break-up\" style=\"overflow-x: auto;\">\n\t<table class=\"table table-bordered table-hover\">\n\t\t<thead>\n\t\t\t<tr>\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t<th class=\"text-left\">Item</th>\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t<th class=\"text-right\">Taxable Amount</th>\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t<th class=\"text-right\">VAT on Sales</th>\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t</tr>\n\t\t</thead>\n\t\t<tbody>\n\t\t\t\n\t\t\t\t<tr>\n\t\t\t\t\t<td>Dunlop tyres</td>\n\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\u00a3 200.00\n\t\t\t\t\t\t\n\t\t\t\t\t</td>\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t(20.0%)\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\u00a3 40.00\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t</tr>\n\t\t\t\n\t\t\t\t<tr>\n\t\t\t\t\t<td>Continental tyres</td>\n\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\u00a3 65.00\n\t\t\t\t\t\t\n\t\t\t\t\t</td>\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t(5.0%)\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\u00a3 3.25\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t</tr>\n\t\t\t\n\t\t\t\t<tr>\n\t\t\t\t\t<td>Toyo tyres</td>\n\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\u00a3 560.00\n\t\t\t\t\t\t\n\t\t\t\t\t</td>\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t(0.0%)\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\u00a3 0.00\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t</tr>\n\t\t\t\n\t\t</tbody>\n\t</table>\n</div>",
"outstanding_amount": 868.25,
"packed_items": [],
"paid_amount": 0.0,
"parent": null,
"parentfield": null,
"parenttype": null,
"party_account_currency": "GBP",
"payment_schedule": [],
"payment_terms_template": null,
"payments": [],
"plc_conversion_rate": 1.0,
"po_date": null,
"po_no": "",
"pos_profile": null,
"posting_date": null,
"posting_time": "5:19:02.994077",
"price_list_currency": "GBP",
"pricing_rules": [],
"project": null,
"redeem_loyalty_points": 0,
"remarks": "No Remarks",
"represents_company": "",
"return_against": null,
"rounded_total": 868.25,
"rounding_adjustment": 0.0,
"sales_partner": null,
"sales_team": [],
"scan_barcode": null,
"select_print_heading": null,
"selling_price_list": "Standard Selling",
"set_posting_time": 0,
"set_target_warehouse": null,
"set_warehouse": null,
"shipping_address": null,
"shipping_address_name": "",
"shipping_rule": null,
"source": null,
"status": "Overdue",
"tax_category": "",
"tax_id": null,
"taxes": [
{
"account_head": "VAT on Sales - _T",
"base_tax_amount": 43.25,
"base_tax_amount_after_discount_amount": 43.25,
"base_total": 868.25,
"charge_type": "On Net Total",
"cost_center": "Main - _T",
"description": "VAT on Sales",
"included_in_print_rate": 0,
"item_wise_tax_detail": "{\"Dunlop tyres\":[20.0,40.0],\"Continental tyres\":[5.0,3.25],\"Toyo tyres\":[0.0,0.0]}",
"parent": null,
"parentfield": "taxes",
"parenttype": "Sales Invoice",
"rate": 0.0,
"row_id": null,
"tax_amount": 43.25,
"tax_amount_after_discount_amount": 43.25,
"total": 868.25
}
],
"taxes_and_charges": null,
"tc_name": null,
"terms": null,
"territory": "All Territories",
"timesheets": [],
"title": "_Sales Invoice",
"to_date": null,
"total": 825.0,
"total_advance": 0.0,
"total_billing_amount": 0.0,
"total_commission": 0.0,
"total_net_weight": 0.0,
"total_qty": 13.0,
"total_taxes_and_charges": 43.25,
"unrealized_profit_loss_account": null,
"update_billed_amount_in_sales_order": 0,
"update_stock": 0,
"write_off_account": null,
"write_off_amount": 0.0,
"write_off_cost_center": null,
"write_off_outstanding_amount_automatically": 0
}
]

View File

@@ -1,220 +0,0 @@
import datetime
import json
import os
import unittest
import frappe
from frappe.utils import (
add_to_date,
get_first_day,
get_last_day,
get_year_ending,
get_year_start,
getdate,
)
from .tax_detail import filter_match, save_custom_report
class TestTaxDetail(unittest.TestCase):
def load_testdocs(self):
from erpnext.accounts.utils import FiscalYearError, get_fiscal_year
datapath, _ = os.path.splitext(os.path.realpath(__file__))
with open(datapath + ".json") as fp:
docs = json.load(fp)
now = getdate()
self.from_date = get_first_day(now)
self.to_date = get_last_day(now)
try:
get_fiscal_year(now, company="_T")
except FiscalYearError:
docs = [
{
"companies": [
{
"company": "_T",
"parent": "_Test Fiscal",
"parentfield": "companies",
"parenttype": "Fiscal Year",
}
],
"doctype": "Fiscal Year",
"year": "_Test Fiscal",
"year_end_date": get_year_ending(now),
"year_start_date": get_year_start(now),
},
*docs,
]
docs = [
{
"abbr": "_T",
"company_name": "_T",
"country": "United Kingdom",
"default_currency": "GBP",
"doctype": "Company",
"name": "_T",
},
*docs,
]
for doc in docs:
try:
db_doc = frappe.get_doc(doc)
if "Invoice" in db_doc.doctype:
db_doc.due_date = add_to_date(now, days=1)
db_doc.insert()
# Create GL Entries:
db_doc.submit()
else:
db_doc.insert(ignore_if_duplicate=True)
except frappe.exceptions.DuplicateEntryError:
pass
def load_defcols(self):
self.company = frappe.get_doc("Company", "_T")
custom_report = frappe.get_doc("Report", "Tax Detail")
self.default_columns, _ = custom_report.run_query_report(
filters={
"from_date": "2021-03-01",
"to_date": "2021-03-31",
"company": self.company.name,
"mode": "run",
"report_name": "Tax Detail",
},
user=frappe.session.user,
)
def rm_testdocs(self):
"Remove the Company and all data"
from erpnext.setup.doctype.company.company import create_transaction_deletion_request
create_transaction_deletion_request(self.company.name)
def test_report(self):
self.load_testdocs()
self.load_defcols()
report_name = save_custom_report(
"Tax Detail",
"_Test Tax Detail",
json.dumps(
{
"columns": self.default_columns,
"sections": {
"Box1": {"Filter0": {"type": "filter", "filters": {"4": "VAT on Sales"}}},
"Box2": {"Filter0": {"type": "filter", "filters": {"4": "Acquisition"}}},
"Box3": {"Box1": {"type": "section"}, "Box2": {"type": "section"}},
"Box4": {"Filter0": {"type": "filter", "filters": {"4": "VAT on Purchases"}}},
"Box5": {"Box3": {"type": "section"}, "Box4": {"type": "section"}},
"Box6": {"Filter0": {"type": "filter", "filters": {"3": "!=Tax", "4": "Sales"}}},
"Box7": {"Filter0": {"type": "filter", "filters": {"2": "Expense", "3": "!=Tax"}}},
"Box8": {
"Filter0": {"type": "filter", "filters": {"3": "!=Tax", "4": "Sales", "12": "EU"}}
},
"Box9": {
"Filter0": {
"type": "filter",
"filters": {"2": "Expense", "3": "!=Tax", "12": "EU"},
}
},
},
"show_detail": 1,
}
),
)
data = frappe.desk.query_report.run(
report_name,
filters={
"from_date": self.from_date,
"to_date": self.to_date,
"company": self.company.name,
"mode": "run",
"report_name": report_name,
},
user=frappe.session.user,
)
self.assertListEqual(data.get("columns"), self.default_columns)
expected = (
("Box1", 43.25),
("Box2", 0.0),
("Box3", 43.25),
("Box4", -85.28),
("Box5", -42.03),
("Box6", 825.0),
("Box7", -426.40),
("Box8", 0.0),
("Box9", 0.0),
)
exrow = iter(expected)
for row in data.get("result"):
if row.get("voucher_no") and not row.get("posting_date"):
label, value = next(exrow)
self.assertDictEqual(row, {"voucher_no": label, "amount": value})
self.assertListEqual(
data.get("report_summary"),
[{"label": label, "datatype": "Currency", "value": value} for label, value in expected],
)
self.rm_testdocs()
def test_filter_match(self):
# None - treated as -inf number except range
self.assertTrue(filter_match(None, "!="))
self.assertTrue(filter_match(None, "<"))
self.assertTrue(filter_match(None, "<jjj"))
self.assertTrue(filter_match(None, " : "))
self.assertTrue(filter_match(None, ":56"))
self.assertTrue(filter_match(None, ":de"))
self.assertFalse(filter_match(None, "3.4"))
self.assertFalse(filter_match(None, "="))
self.assertFalse(filter_match(None, "=3.4"))
self.assertFalse(filter_match(None, ">3.4"))
self.assertFalse(filter_match(None, " <"))
self.assertFalse(filter_match(None, "ew"))
self.assertFalse(filter_match(None, " "))
self.assertFalse(filter_match(None, " f :"))
# Numbers
self.assertTrue(filter_match(3.4, "3.4"))
self.assertTrue(filter_match(3.4, ".4"))
self.assertTrue(filter_match(3.4, "3"))
self.assertTrue(filter_match(-3.4, "< -3"))
self.assertTrue(filter_match(-3.4, "> -4"))
self.assertTrue(filter_match(3.4, "= 3.4 "))
self.assertTrue(filter_match(3.4, "!=4.5"))
self.assertTrue(filter_match(3.4, " 3 : 4 "))
self.assertTrue(filter_match(0.0, " : "))
self.assertFalse(filter_match(3.4, "=4.5"))
self.assertFalse(filter_match(3.4, " = 3.4 "))
self.assertFalse(filter_match(3.4, "!=3.4"))
self.assertFalse(filter_match(3.4, ">6"))
self.assertFalse(filter_match(3.4, "<-4.5"))
self.assertFalse(filter_match(3.4, "4.5"))
self.assertFalse(filter_match(3.4, "5:9"))
# Strings
self.assertTrue(filter_match("ACC-SINV-2021-00001", "SINV"))
self.assertTrue(filter_match("ACC-SINV-2021-00001", "sinv"))
self.assertTrue(filter_match("ACC-SINV-2021-00001", "-2021"))
self.assertTrue(filter_match(" ACC-SINV-2021-00001", " acc"))
self.assertTrue(filter_match("ACC-SINV-2021-00001", "=2021"))
self.assertTrue(filter_match("ACC-SINV-2021-00001", "!=zz"))
self.assertTrue(filter_match("ACC-SINV-2021-00001", "< zzz "))
self.assertTrue(filter_match("ACC-SINV-2021-00001", " : sinv "))
self.assertFalse(filter_match("ACC-SINV-2021-00001", " sinv :"))
self.assertFalse(filter_match("ACC-SINV-2021-00001", " acc"))
self.assertFalse(filter_match("ACC-SINV-2021-00001", "= 2021 "))
self.assertFalse(filter_match("ACC-SINV-2021-00001", "!=sinv"))
self.assertFalse(filter_match("ACC-SINV-2021-00001", " >"))
self.assertFalse(filter_match("ACC-SINV-2021-00001", ">aa"))
self.assertFalse(filter_match("ACC-SINV-2021-00001", " <"))
self.assertFalse(filter_match("ACC-SINV-2021-00001", "< "))
self.assertFalse(filter_match("ACC-SINV-2021-00001", " ="))
self.assertFalse(filter_match("ACC-SINV-2021-00001", "="))
# Date - always match
self.assertTrue(filter_match(datetime.date(2021, 3, 19), " kdsjkldfs "))

View File

@@ -72,8 +72,8 @@ def get_result(filters, tds_docs, tds_accounts, tax_category_map, journal_entry_
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
if rate:
total_amount = grand_total = base_total = tax_amount / (rate / 100)
base_total = min(tax_amount / (rate / 100), net_total_map.get((voucher_type, name))[0])
total_amount = grand_total = base_total
elif voucher_type == "Purchase Invoice":
total_amount, grand_total, base_total, bill_no, bill_date = net_total_map.get(
(voucher_type, name)
@@ -405,7 +405,7 @@ def get_doc_info(vouchers, doctype, tax_category_map, net_total_map=None):
"paid_amount_after_tax",
"base_paid_amount",
],
"Journal Entry": ["total_amount"],
"Journal Entry": ["tax_withholding_category", "total_debit"],
}
entries = frappe.get_all(
@@ -427,7 +427,7 @@ def get_doc_info(vouchers, doctype, tax_category_map, net_total_map=None):
elif doctype == "Payment Entry":
value = [entry.paid_amount, entry.paid_amount_after_tax, entry.base_paid_amount]
else:
value = [entry.total_amount] * 3
value = [entry.total_debit] * 3
net_total_map[(doctype, entry.name)] = value

View File

@@ -14,14 +14,14 @@
"owner": "Administrator",
"ref_doctype": "GL Entry",
"report_name": "Trial Balance",
"report_type": "Script Report",
"report_type": "Script Report",
"roles": [
{
"role": "Accounts User"
},
},
{
"role": "Accounts Manager"
},
},
{
"role": "Auditor"
}

View File

@@ -89,6 +89,10 @@ def get_data(filters):
)
company_currency = filters.presentation_currency or erpnext.get_company_currency(filters.company)
ignore_is_opening = frappe.db.get_single_value(
"Accounts Settings", "ignore_is_opening_check_for_reporting"
)
if not accounts:
return None
@@ -102,7 +106,7 @@ def get_data(filters):
gl_entries_by_account = {}
opening_balances = get_opening_balances(filters)
opening_balances = get_opening_balances(filters, ignore_is_opening)
# add filter inside list so that the query in financial_statements.py doesn't break
if filters.project:
@@ -120,7 +124,13 @@ def get_data(filters):
ignore_opening_entries=True,
)
calculate_values(accounts, gl_entries_by_account, opening_balances, filters.get("show_net_values"))
calculate_values(
accounts,
gl_entries_by_account,
opening_balances,
filters.get("show_net_values"),
ignore_is_opening=ignore_is_opening,
)
accumulate_values_into_parents(accounts, accounts_by_name)
data = prepare_data(accounts, filters, parent_children_map, company_currency)
@@ -131,15 +141,15 @@ def get_data(filters):
return data
def get_opening_balances(filters):
balance_sheet_opening = get_rootwise_opening_balances(filters, "Balance Sheet")
pl_opening = get_rootwise_opening_balances(filters, "Profit and Loss")
def get_opening_balances(filters, ignore_is_opening):
balance_sheet_opening = get_rootwise_opening_balances(filters, "Balance Sheet", ignore_is_opening)
pl_opening = get_rootwise_opening_balances(filters, "Profit and Loss", ignore_is_opening)
balance_sheet_opening.update(pl_opening)
return balance_sheet_opening
def get_rootwise_opening_balances(filters, report_type):
def get_rootwise_opening_balances(filters, report_type, ignore_is_opening):
gle = []
last_period_closing_voucher = ""
@@ -165,16 +175,24 @@ def get_rootwise_opening_balances(filters, report_type):
report_type,
accounting_dimensions,
period_closing_voucher=last_period_closing_voucher[0].name,
ignore_is_opening=ignore_is_opening,
)
# Report getting generate from the mid of a fiscal year
if getdate(last_period_closing_voucher[0].posting_date) < getdate(add_days(filters.from_date, -1)):
start_date = add_days(last_period_closing_voucher[0].posting_date, 1)
gle += get_opening_balance(
"GL Entry", filters, report_type, accounting_dimensions, start_date=start_date
"GL Entry",
filters,
report_type,
accounting_dimensions,
start_date=start_date,
ignore_is_opening=ignore_is_opening,
)
else:
gle = get_opening_balance("GL Entry", filters, report_type, accounting_dimensions)
gle = get_opening_balance(
"GL Entry", filters, report_type, accounting_dimensions, ignore_is_opening=ignore_is_opening
)
opening = frappe._dict()
for d in gle:
@@ -193,7 +211,13 @@ def get_rootwise_opening_balances(filters, report_type):
def get_opening_balance(
doctype, filters, report_type, accounting_dimensions, period_closing_voucher=None, start_date=None
doctype,
filters,
report_type,
accounting_dimensions,
period_closing_voucher=None,
start_date=None,
ignore_is_opening=0,
):
closing_balance = frappe.qb.DocType(doctype)
account = frappe.qb.DocType("Account")
@@ -229,11 +253,16 @@ def get_opening_balance(
(closing_balance.posting_date >= start_date)
& (closing_balance.posting_date < filters.from_date)
)
opening_balance = opening_balance.where(closing_balance.is_opening == "No")
if not ignore_is_opening:
opening_balance = opening_balance.where(closing_balance.is_opening == "No")
else:
opening_balance = opening_balance.where(
(closing_balance.posting_date < filters.from_date) | (closing_balance.is_opening == "Yes")
)
if not ignore_is_opening:
opening_balance = opening_balance.where(
(closing_balance.posting_date < filters.from_date) | (closing_balance.is_opening == "Yes")
)
else:
opening_balance = opening_balance.where(closing_balance.posting_date < filters.from_date)
if doctype == "GL Entry":
opening_balance = opening_balance.where(closing_balance.is_cancelled == 0)
@@ -304,7 +333,7 @@ def get_opening_balance(
return gle
def calculate_values(accounts, gl_entries_by_account, opening_balances, show_net_values):
def calculate_values(accounts, gl_entries_by_account, opening_balances, show_net_values, ignore_is_opening=0):
init = {
"opening_debit": 0.0,
"opening_credit": 0.0,
@@ -322,7 +351,7 @@ def calculate_values(accounts, gl_entries_by_account, opening_balances, show_net
d["opening_credit"] = opening_balances.get(d.name, {}).get("opening_credit", 0)
for entry in gl_entries_by_account.get(d.name, []):
if cstr(entry.is_opening) != "Yes":
if cstr(entry.is_opening) != "Yes" or ignore_is_opening:
d["debit"] += flt(entry.debit)
d["credit"] += flt(entry.credit)

View File

@@ -68,16 +68,12 @@ frappe.query_reports["Trial Balance for Party"] = {
{
fieldname: "account",
label: __("Account"),
fieldtype: "Link",
fieldtype: "MultiSelectList",
options: "Account",
get_query: function () {
var company = frappe.query_report.get_filter_value("company");
return {
doctype: "Account",
filters: {
company: company,
},
};
get_data: function (txt) {
return frappe.db.get_link_options("Account", txt, {
company: frappe.query_report.get_filter_value("company"),
});
},
},
{

View File

@@ -4,8 +4,10 @@
import frappe
from frappe import _
from frappe.query_builder.functions import Sum
from frappe.utils import cint, flt
from erpnext.accounts.report.general_ledger.general_ledger import get_accounts_with_children
from erpnext.accounts.report.trial_balance.trial_balance import validate_filters
@@ -35,9 +37,14 @@ def get_data(filters, show_party_name):
filters=party_filters,
order_by="name",
)
account_filter = []
if filters.get("account"):
account_filter = get_accounts_with_children(filters.get("account"))
company_currency = frappe.get_cached_value("Company", filters.company, "default_currency")
opening_balances = get_opening_balances(filters)
balances_within_period = get_balances_within_period(filters)
opening_balances = get_opening_balances(filters, account_filter)
balances_within_period = get_balances_within_period(filters, account_filter)
data = []
# total_debit, total_credit = 0, 0
@@ -89,30 +96,34 @@ def get_data(filters, show_party_name):
return data
def get_opening_balances(filters):
account_filter = ""
if filters.get("account"):
account_filter = "and account = %s" % (frappe.db.escape(filters.get("account")))
def get_opening_balances(filters, account_filter=None):
GL_Entry = frappe.qb.DocType("GL Entry")
gle = frappe.db.sql(
f"""
select party, sum(debit) as opening_debit, sum(credit) as opening_credit
from `tabGL Entry`
where company=%(company)s
and is_cancelled=0
and ifnull(party_type, '') = %(party_type)s and ifnull(party, '') != ''
and (posting_date < %(from_date)s or (ifnull(is_opening, 'No') = 'Yes' and posting_date <= %(to_date)s))
{account_filter}
group by party""",
{
"company": filters.company,
"from_date": filters.from_date,
"to_date": filters.to_date,
"party_type": filters.party_type,
},
as_dict=True,
query = (
frappe.qb.from_(GL_Entry)
.select(
GL_Entry.party,
Sum(GL_Entry.debit).as_("opening_debit"),
Sum(GL_Entry.credit).as_("opening_credit"),
)
.where(
(GL_Entry.company == filters.company)
& (GL_Entry.is_cancelled == 0)
& (GL_Entry.party_type == filters.party_type)
& (GL_Entry.party != "")
& (
(GL_Entry.posting_date < filters.from_date)
| ((GL_Entry.is_opening == "Yes") & (GL_Entry.posting_date <= filters.to_date))
)
)
.groupby(GL_Entry.party)
)
if account_filter:
query = query.where(GL_Entry.account.isin(account_filter))
gle = query.run(as_dict=True)
opening = frappe._dict()
for d in gle:
opening_debit, opening_credit = toggle_debit_credit(d.opening_debit, d.opening_credit)
@@ -121,31 +132,33 @@ def get_opening_balances(filters):
return opening
def get_balances_within_period(filters):
account_filter = ""
if filters.get("account"):
account_filter = "and account = %s" % (frappe.db.escape(filters.get("account")))
def get_balances_within_period(filters, account_filter=None):
GL_Entry = frappe.qb.DocType("GL Entry")
gle = frappe.db.sql(
f"""
select party, sum(debit) as debit, sum(credit) as credit
from `tabGL Entry`
where company=%(company)s
and is_cancelled = 0
and ifnull(party_type, '') = %(party_type)s and ifnull(party, '') != ''
and posting_date >= %(from_date)s and posting_date <= %(to_date)s
and ifnull(is_opening, 'No') = 'No'
{account_filter}
group by party""",
{
"company": filters.company,
"from_date": filters.from_date,
"to_date": filters.to_date,
"party_type": filters.party_type,
},
as_dict=True,
query = (
frappe.qb.from_(GL_Entry)
.select(
GL_Entry.party,
Sum(GL_Entry.debit).as_("debit"),
Sum(GL_Entry.credit).as_("credit"),
)
.where(
(GL_Entry.company == filters.company)
& (GL_Entry.is_cancelled == 0)
& (GL_Entry.party_type == filters.party_type)
& (GL_Entry.party != "")
& (GL_Entry.posting_date >= filters.from_date)
& (GL_Entry.posting_date <= filters.to_date)
& (GL_Entry.is_opening == "No")
)
.groupby(GL_Entry.party)
)
if account_filter:
query = query.where(GL_Entry.account.isin(account_filter))
gle = query.run(as_dict=True)
balances_within_period = frappe._dict()
for d in gle:
balances_within_period.setdefault(d.party, [d.debit, d.credit])

View File

@@ -29,10 +29,6 @@ REPORT_FILTER_TEST_CASES: list[tuple[ReportName, ReportFilters]] = [
("Sales Register", {}),
("Sales Register", {"item_group": "All Item Groups"}),
("Purchase Register", {}),
(
"Tax Detail",
{"mode": "run", "report_name": "Tax Detail"},
),
]
OPTIONAL_FILTERS = {}

View File

@@ -579,6 +579,16 @@ def update_reference_in_journal_entry(d, journal_entry, do_not_save=False):
if jv_detail.get("reference_type") in ("Sales Order", "Purchase Order"):
frappe.get_doc(jv_detail.reference_type, jv_detail.reference_name).set_total_advance_paid()
rev_dr_or_cr = (
"debit_in_account_currency"
if d["dr_or_cr"] == "credit_in_account_currency"
else "credit_in_account_currency"
)
if jv_detail.get(rev_dr_or_cr):
d["dr_or_cr"] = rev_dr_or_cr
d["allocated_amount"] = d["allocated_amount"] * -1
d["unadjusted_amount"] = d["unadjusted_amount"] * -1
if flt(d["unadjusted_amount"]) - flt(d["allocated_amount"]) != 0:
# adjust the unreconciled balance
amount_in_account_currency = flt(d["unadjusted_amount"]) - flt(d["allocated_amount"])
@@ -1577,7 +1587,7 @@ def get_stock_and_account_balance(account=None, posting_date=None, company=None)
if wh_details.account == account and not wh_details.is_group
]
total_stock_value = get_stock_value_on(related_warehouses, posting_date)
total_stock_value = get_stock_value_on(related_warehouses, posting_date, company=company)
precision = frappe.get_precision("Journal Entry Account", "debit_in_account_currency")
return flt(account_balance, precision), flt(total_stock_value, precision), related_warehouses

View File

@@ -318,7 +318,7 @@ frappe.ui.form.on("Asset", {
}
frm.dashboard.render_graph({
title: "Asset Value",
title: __("Asset Value"),
data: {
labels: x_intervals,
datasets: [

View File

@@ -916,13 +916,12 @@ class Asset(AccountsController):
].expected_value_after_useful_life
value_after_depreciation = self.finance_books[idx].value_after_depreciation
if (
flt(value_after_depreciation) <= expected_value_after_useful_life
or self.is_fully_depreciated
):
if flt(value_after_depreciation) <= expected_value_after_useful_life:
status = "Fully Depreciated"
self.is_fully_depreciated = 1
elif flt(value_after_depreciation) < flt(self.gross_purchase_amount):
status = "Partially Depreciated"
self.is_fully_depreciated = 0
elif self.docstatus == 2:
status = "Cancelled"
return status

View File

@@ -454,7 +454,7 @@ def restore_asset(asset_name):
def depreciate_asset(asset, date):
if not asset.calculate_depreciation:
if not asset.calculate_depreciation or asset.is_fully_depreciated:
return
asset.flags.ignore_validate_update_after_submit = True

View File

@@ -205,9 +205,12 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends e
}
}
if(is_drop_ship && doc.status!="Delivered") {
this.frm.add_custom_button(__('Delivered'),
this.delivered_by_supplier, __("Status"));
if (is_drop_ship && doc.status != "Delivered") {
this.frm.add_custom_button(
__("Delivered"),
this.delivered_by_supplier.bind(this),
__("Status")
);
this.frm.page.set_inner_btn_group_as_primary(__("Status"));
}
@@ -582,4 +585,4 @@ frappe.ui.form.on("Purchase Order", "is_subcontracted", function(frm) {
if (frm.doc.is_old_subcontracting_flow) {
erpnext.buying.get_default_bom(frm);
}
});
});

View File

@@ -51,16 +51,22 @@ frappe.listview_settings["Purchase Order"] = {
listview.call_for_selected_items(method, { status: "Submitted" });
});
listview.page.add_action_item(__("Purchase Invoice"), () => {
erpnext.bulk_transaction_processing.create(listview, "Purchase Order", "Purchase Invoice");
});
if (frappe.model.can_create("Purchase Invoice")) {
listview.page.add_action_item(__("Purchase Invoice"), () => {
erpnext.bulk_transaction_processing.create(listview, "Purchase Order", "Purchase Invoice");
});
}
listview.page.add_action_item(__("Purchase Receipt"), () => {
erpnext.bulk_transaction_processing.create(listview, "Purchase Order", "Purchase Receipt");
});
if (frappe.model.can_create("Purchase Receipt")) {
listview.page.add_action_item(__("Purchase Receipt"), () => {
erpnext.bulk_transaction_processing.create(listview, "Purchase Order", "Purchase Receipt");
});
}
listview.page.add_action_item(__("Advance Payment"), () => {
erpnext.bulk_transaction_processing.create(listview, "Purchase Order", "Payment Entry");
});
if (frappe.model.can_create("Payment Entry")) {
listview.page.add_action_item(__("Advance Payment"), () => {
erpnext.bulk_transaction_processing.create(listview, "Purchase Order", "Payment Entry");
});
}
},
};

View File

@@ -330,7 +330,11 @@ def make_supplier_quotation_from_rfq(source_name, target_doc=None, for_supplier=
},
"Request for Quotation Item": {
"doctype": "Supplier Quotation Item",
"field_map": {"name": "request_for_quotation_item", "parent": "request_for_quotation"},
"field_map": {
"name": "request_for_quotation_item",
"parent": "request_for_quotation",
"project_name": "project",
},
},
},
target_doc,

View File

@@ -13,6 +13,7 @@ from frappe.model.naming import set_name_by_naming_series, set_name_from_naming_
from erpnext.accounts.party import (
get_dashboard_info,
get_timeline_data,
validate_party_accounts,
)
from erpnext.utilities.transaction_base import TransactionBase

View File

@@ -11,12 +11,20 @@ frappe.listview_settings["Supplier Quotation"] = {
},
onload: function (listview) {
listview.page.add_action_item(__("Purchase Order"), () => {
erpnext.bulk_transaction_processing.create(listview, "Supplier Quotation", "Purchase Order");
});
if (frappe.model.can_create("Purchase Order")) {
listview.page.add_action_item(__("Purchase Order"), () => {
erpnext.bulk_transaction_processing.create(listview, "Supplier Quotation", "Purchase Order");
});
}
listview.page.add_action_item(__("Purchase Invoice"), () => {
erpnext.bulk_transaction_processing.create(listview, "Supplier Quotation", "Purchase Invoice");
});
if (frappe.model.can_create("Purchase Invoice")) {
listview.page.add_action_item(__("Purchase Invoice"), () => {
erpnext.bulk_transaction_processing.create(
listview,
"Supplier Quotation",
"Purchase Invoice"
);
});
}
},
};

View File

@@ -53,6 +53,7 @@ frappe.query_reports["Purchase Order Analysis"] = {
label: __("Status"),
fieldtype: "MultiSelectList",
width: "80",
options: ["To Pay", "To Bill", "To Receive", "To Receive and Bill", "Completed"],
get_data: function (txt) {
let status = ["To Bill", "To Receive", "To Receive and Bill", "Completed"];
let options = [];

View File

@@ -18,6 +18,7 @@ def execute(filters=None):
columns = get_columns(filters)
data = get_data(filters)
update_received_amount(data)
if not data:
return [], [], None, []
@@ -60,7 +61,6 @@ def get_data(filters):
(po_item.qty - po_item.received_qty).as_("pending_qty"),
Sum(IfNull(pi_item.qty, 0)).as_("billed_qty"),
po_item.base_amount.as_("amount"),
(po_item.received_qty * po_item.base_rate).as_("received_qty_amount"),
(po_item.billed_amt * IfNull(po.conversion_rate, 1)).as_("billed_amount"),
(po_item.base_amount - (po_item.billed_amt * IfNull(po.conversion_rate, 1))).as_(
"pending_amount"
@@ -92,6 +92,39 @@ def get_data(filters):
return data
def update_received_amount(data):
pr_data = get_received_amount_data(data)
for row in data:
row.received_qty_amount = flt(pr_data.get(row.name))
def get_received_amount_data(data):
pr = frappe.qb.DocType("Purchase Receipt")
pr_item = frappe.qb.DocType("Purchase Receipt Item")
query = (
frappe.qb.from_(pr)
.inner_join(pr_item)
.on(pr_item.parent == pr.name)
.select(
pr_item.purchase_order_item,
Sum(pr_item.base_amount).as_("received_qty_amount"),
)
.where((pr_item.parent == pr.name) & (pr.docstatus == 1))
.groupby(pr_item.purchase_order_item)
)
query = query.where(pr_item.purchase_order_item.isin([row.name for row in data]))
data = query.run()
if not data:
return frappe._dict()
return frappe._dict(data)
def prepare_data(data, filters):
completed, pending = 0, 0
pending_field = "pending_amount"
@@ -147,7 +180,7 @@ def prepare_data(data, filters):
def prepare_chart_data(pending, completed):
labels = ["Amount to Bill", "Billed Amount"]
labels = [_("Amount to Bill"), _("Billed Amount")]
return {
"data": {"labels": labels, "datasets": [{"values": [pending, completed]}]},

View File

@@ -50,6 +50,7 @@ frappe.query_reports["Supplier Quotation Comparison"] = {
fieldname: "supplier",
label: __("Supplier"),
fieldtype: "MultiSelectList",
options: "Supplier",
get_data: function (txt) {
return frappe.db.get_link_options("Supplier", txt);
},
@@ -58,6 +59,7 @@ frappe.query_reports["Supplier Quotation Comparison"] = {
fieldtype: "MultiSelectList",
label: __("Supplier Quotation"),
fieldname: "supplier_quotation",
options: "Supplier Quotation",
default: "",
get_data: function (txt) {
return frappe.db.get_link_options("Supplier Quotation", txt, { docstatus: ["<", 2] });

View File

@@ -8,7 +8,7 @@ from collections import defaultdict
import frappe
from frappe import _, bold, qb, throw
from frappe.model.workflow import get_workflow_name, is_transition_condition_satisfied
from frappe.query_builder import Criterion
from frappe.query_builder import Criterion, DocType
from frappe.query_builder.custom import ConstantColumn
from frappe.query_builder.functions import Abs, Sum
from frappe.utils import (
@@ -158,7 +158,7 @@ class AccountsController(TransactionBase):
self.validate_qty_is_not_zero()
if (
self.doctype in ["Sales Invoice", "Purchase Invoice"]
self.doctype in ["Sales Invoice", "Purchase Invoice", "POS Invoice"]
and self.get("is_return")
and self.get("update_stock")
):
@@ -250,6 +250,7 @@ class AccountsController(TransactionBase):
apply_pricing_rule_on_transaction(self)
self.set_total_in_words()
self.validate_company_in_accounting_dimension()
def init_internal_values(self):
# init all the internal values as 0 on sa
@@ -346,13 +347,47 @@ class AccountsController(TransactionBase):
== 1
)
).run()
frappe.db.sql(
"delete from `tabGL Entry` where voucher_type=%s and voucher_no=%s", (self.doctype, self.name)
)
frappe.db.sql(
"delete from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s",
(self.doctype, self.name),
)
gle = frappe.qb.DocType("GL Entry")
frappe.qb.from_(gle).delete().where(
(gle.voucher_type == self.doctype) & (gle.voucher_no == self.name)
).run()
sle = frappe.qb.DocType("Stock Ledger Entry")
frappe.qb.from_(sle).delete().where(
(sle.voucher_type == self.doctype) & (sle.voucher_no == self.name)
).run()
def validate_company_in_accounting_dimension(self):
doc_field = DocType("DocField")
accounting_dimension = DocType("Accounting Dimension")
dimension_list = (
frappe.qb.from_(accounting_dimension)
.select(accounting_dimension.document_type)
.join(doc_field)
.on(doc_field.parent == accounting_dimension.document_type)
.where(doc_field.fieldname == "company")
).run(as_list=True)
dimension_list = sum(dimension_list, ["Project"])
self.validate_company(dimension_list)
for child in self.get_all_children() or []:
self.validate_company(dimension_list, child)
def validate_company(self, dimension_list, child=None):
for dimension in dimension_list:
if not child:
dimension_value = self.get(frappe.scrub(dimension))
else:
dimension_value = child.get(frappe.scrub(dimension))
if dimension_value:
company = frappe.get_cached_value(dimension, dimension_value, "company")
if company and company != self.company:
frappe.throw(
_("{0}: {1} does not belong to the Company: {2}").format(
dimension, frappe.bold(dimension_value), self.company
)
)
def validate_return_against_account(self):
if self.doctype in ["Sales Invoice", "Purchase Invoice"] and self.is_return and self.return_against:
@@ -415,9 +450,16 @@ class AccountsController(TransactionBase):
)
def validate_invoice_documents_schedule(self):
if self.is_return:
if (
self.is_return
or (self.doctype == "Purchase Invoice" and self.is_paid)
or (self.doctype == "Sales Invoice" and self.is_pos)
or self.get("is_opening") == "Yes"
):
self.payment_terms_template = ""
self.payment_schedule = []
if self.is_return:
return
self.validate_payment_schedule_dates()
@@ -1020,11 +1062,12 @@ class AccountsController(TransactionBase):
def clear_unallocated_advances(self, childtype, parentfield):
self.set(parentfield, self.get(parentfield, {"allocated_amount": ["not in", [0, None, ""]]}))
frappe.db.sql(
"""delete from `tab{}` where parentfield={} and parent = {}
and allocated_amount = 0""".format(childtype, "%s", "%s"),
(parentfield, self.name),
)
doctype = frappe.qb.DocType(childtype)
frappe.qb.from_(doctype).delete().where(
(doctype.parentfield == parentfield)
& (doctype.parent == self.name)
& (doctype.allocated_amount == 0)
).run()
@frappe.whitelist()
def apply_shipping_rule(self):
@@ -1075,6 +1118,7 @@ class AccountsController(TransactionBase):
"advance_amount": flt(d.amount),
"allocated_amount": allocated_amount,
"ref_exchange_rate": flt(d.exchange_rate), # exchange_rate of advance entry
"difference_posting_date": self.posting_date,
}
self.append("advances", advance_row)
@@ -1325,7 +1369,6 @@ class AccountsController(TransactionBase):
gain_loss_account = frappe.get_cached_value(
"Company", self.company, "exchange_gain_loss_account"
)
je = create_gain_loss_journal(
self.company,
args.get("difference_posting_date") if args else self.posting_date,
@@ -1438,6 +1481,7 @@ class AccountsController(TransactionBase):
"Company", self.company, "exchange_gain_loss_account"
),
"exchange_gain_loss": flt(d.get("exchange_gain_loss")),
"difference_posting_date": d.get("difference_posting_date"),
}
)
lst.append(args)
@@ -1684,22 +1728,22 @@ class AccountsController(TransactionBase):
continue
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:
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",
)
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) + flt(item.get(based_on)), self.precision(based_on, item)
)
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"
@@ -1964,11 +2008,9 @@ class AccountsController(TransactionBase):
for adv in self.advances:
consider_for_total_advance = True
if adv.reference_name == linked_doc_name:
frappe.db.sql(
f"""delete from `tab{self.doctype} Advance`
where name = %s""",
adv.name,
)
doctype = frappe.qb.DocType(self.doctype + " Advance")
frappe.qb.from_(doctype).delete().where(doctype.name == adv.name).run()
consider_for_total_advance = False
if consider_for_total_advance:
@@ -2181,6 +2223,9 @@ class AccountsController(TransactionBase):
return
for d in self.get("payment_schedule"):
if d.due_date and d.discount_date:
d.validate_from_to_dates("discount_date", "due_date")
if self.doctype == "Sales Order" and getdate(d.due_date) < getdate(self.transaction_date):
frappe.throw(
_("Row {0}: Due Date in the Payment Terms table cannot be before Posting Date").format(
@@ -2196,7 +2241,7 @@ class AccountsController(TransactionBase):
frappe.throw(_("Rows with duplicate due dates in other rows were found: {0}").format(duplicates))
def validate_payment_schedule_amount(self):
if self.doctype == "Sales Invoice" and self.is_pos:
if (self.doctype == "Sales Invoice" and self.is_pos) or self.get("is_opening") == "Yes":
return
party_account_currency = self.get("party_account_currency")
@@ -2313,6 +2358,12 @@ class AccountsController(TransactionBase):
secondary_account = get_party_account(secondary_party_type, secondary_party, self.company)
primary_account_currency = get_account_currency(primary_account)
secondary_account_currency = get_account_currency(secondary_account)
default_currency = erpnext.get_company_currency(self.company)
# Determine if multi-currency journal entry is needed
multi_currency = (
primary_account_currency != default_currency or secondary_account_currency != default_currency
)
jv = frappe.new_doc("Journal Entry")
jv.voucher_type = "Journal Entry"
@@ -2337,7 +2388,7 @@ class AccountsController(TransactionBase):
advance_entry.cost_center = self.cost_center or erpnext.get_default_cost_center(self.company)
advance_entry.is_advance = "Yes"
# update dimesions
# Update dimensions
dimensions_dict = frappe._dict()
active_dimensions = get_dimensions()[0]
for dim in active_dimensions:
@@ -2346,17 +2397,58 @@ class AccountsController(TransactionBase):
reconcilation_entry.update(dimensions_dict)
advance_entry.update(dimensions_dict)
if self.doctype == "Sales Invoice":
reconcilation_entry.credit_in_account_currency = self.outstanding_amount
advance_entry.debit_in_account_currency = self.outstanding_amount
# Calculate exchange rates if necessary
if multi_currency:
# Exchange rates for primary and secondary accounts
exc_rate_primary_to_default = (
1
if primary_account_currency == default_currency
else get_exchange_rate(primary_account_currency, default_currency, self.posting_date)
)
exc_rate_secondary_to_default = (
1
if secondary_account_currency == default_currency
else get_exchange_rate(secondary_account_currency, default_currency, self.posting_date)
)
exc_rate_secondary_to_primary = (
1
if secondary_account_currency == primary_account_currency
else get_exchange_rate(
secondary_account_currency, primary_account_currency, self.posting_date
)
)
# Convert outstanding amount from secondary to primary account currency, if needed
os_in_default_currency = self.outstanding_amount * exc_rate_secondary_to_default
os_in_primary_currency = self.outstanding_amount * exc_rate_secondary_to_primary
if self.doctype == "Sales Invoice":
# Calculate credit and debit values for reconciliation and advance entries
reconcilation_entry.credit_in_account_currency = self.outstanding_amount
reconcilation_entry.credit = os_in_default_currency
advance_entry.debit_in_account_currency = os_in_primary_currency
advance_entry.debit = os_in_default_currency
else:
advance_entry.credit_in_account_currency = os_in_primary_currency
advance_entry.credit = os_in_default_currency
reconcilation_entry.debit_in_account_currency = self.outstanding_amount
reconcilation_entry.debit = os_in_default_currency
# Set exchange rates for entries
reconcilation_entry.exchange_rate = exc_rate_secondary_to_default
advance_entry.exchange_rate = exc_rate_primary_to_default
else:
advance_entry.credit_in_account_currency = self.outstanding_amount
reconcilation_entry.debit_in_account_currency = self.outstanding_amount
default_currency = erpnext.get_company_currency(self.company)
if primary_account_currency != default_currency or secondary_account_currency != default_currency:
jv.multi_currency = 1
if self.doctype == "Sales Invoice":
reconcilation_entry.credit_in_account_currency = self.outstanding_amount
advance_entry.debit_in_account_currency = self.outstanding_amount
else:
advance_entry.credit_in_account_currency = self.outstanding_amount
reconcilation_entry.debit_in_account_currency = self.outstanding_amount
jv.multi_currency = multi_currency
jv.append("accounts", reconcilation_entry)
jv.append("accounts", advance_entry)

View File

@@ -71,16 +71,17 @@ def validate_returned_items(doc):
valid_items = frappe._dict()
select_fields = "item_code, qty, stock_qty, rate, parenttype, conversion_factor"
select_fields = "item_code, qty, stock_qty, rate, parenttype, conversion_factor, name"
if doc.doctype != "Purchase Invoice":
select_fields += ",serial_no, batch_no"
if doc.doctype in ["Purchase Invoice", "Purchase Receipt", "Subcontracting Receipt"]:
if doc.doctype in [
"Purchase Invoice",
"Purchase Receipt",
"Subcontracting Receipt",
]:
select_fields += ",rejected_qty, received_qty"
if doc.doctype in ["Purchase Receipt", "Purchase Invoice"]:
select_fields += ",name"
for d in frappe.db.sql(
f"""select {select_fields} from `tab{doc.doctype} Item` where parent = %s""",
doc.return_against,
@@ -108,11 +109,18 @@ def validate_returned_items(doc):
for d in doc.get("items"):
key = d.item_code
raise_exception = False
if doc.doctype in ["Purchase Receipt", "Purchase Invoice"]:
if doc.doctype in [
"Purchase Receipt",
"Purchase Invoice",
"Sales Invoice",
"POS Invoice",
]:
field = frappe.scrub(doc.doctype) + "_item"
if d.get(field):
key = (d.item_code, d.get(field))
raise_exception = True
elif doc.doctype == "Delivery Note":
key = (d.item_code, d.get("dn_detail"))
if d.item_code and (flt(d.qty) < 0 or flt(d.get("received_qty")) < 0):
if key not in valid_items:
@@ -124,7 +132,7 @@ def validate_returned_items(doc):
)
else:
ref = valid_items.get(key, frappe._dict())
validate_quantity(doc, d, ref, valid_items, already_returned_items)
validate_quantity(doc, key, d, ref, valid_items, already_returned_items)
if (
ref.rate
@@ -174,12 +182,16 @@ def validate_returned_items(doc):
frappe.throw(_("Atleast one item should be entered with negative quantity in return document"))
def validate_quantity(doc, args, ref, valid_items, already_returned_items):
def validate_quantity(doc, key, args, ref, valid_items, already_returned_items):
fields = ["stock_qty"]
if doc.doctype in ["Purchase Receipt", "Purchase Invoice", "Subcontracting Receipt"]:
if doc.doctype in [
"Purchase Receipt",
"Purchase Invoice",
"Subcontracting Receipt",
]:
fields.extend(["received_qty", "rejected_qty"])
already_returned_data = already_returned_items.get(args.item_code) or {}
already_returned_data = already_returned_items.get(key) or {}
company_currency = erpnext.get_company_currency(doc.company)
stock_qty_precision = get_field_precision(
@@ -204,7 +216,8 @@ def validate_quantity(doc, args, ref, valid_items, already_returned_items):
frappe.throw(_("{0} must be negative in return document").format(label))
elif returned_qty >= reference_qty and args.get(column):
frappe.throw(
_("Item {0} has already been returned").format(args.item_code), StockOverReturnError
_("Item {0} has already been returned").format(args.item_code),
StockOverReturnError,
)
elif abs(flt(current_stock_qty, stock_qty_precision)) > max_returnable_qty:
frappe.throw(
@@ -243,7 +256,11 @@ def get_ref_item_dict(valid_items, ref_item_row):
if ref_item_row.get("rate", 0) > item_dict["rate"]:
item_dict["rate"] = ref_item_row.get("rate", 0)
if ref_item_row.parenttype in ["Purchase Invoice", "Purchase Receipt", "Subcontracting Receipt"]:
if ref_item_row.parenttype in [
"Purchase Invoice",
"Purchase Receipt",
"Subcontracting Receipt",
]:
item_dict["received_qty"] += ref_item_row.received_qty
item_dict["rejected_qty"] += ref_item_row.rejected_qty
@@ -258,19 +275,28 @@ def get_ref_item_dict(valid_items, ref_item_row):
def get_already_returned_items(doc):
column = "child.item_code, sum(abs(child.qty)) as qty, sum(abs(child.stock_qty)) as stock_qty"
if doc.doctype in ["Purchase Invoice", "Purchase Receipt", "Subcontracting Receipt"]:
if doc.doctype in [
"Purchase Invoice",
"Purchase Receipt",
"Subcontracting Receipt",
]:
column += """, sum(abs(child.rejected_qty) * child.conversion_factor) as rejected_qty,
sum(abs(child.received_qty) * child.conversion_factor) as received_qty"""
field = (
frappe.scrub(doc.doctype) + "_item"
if doc.doctype in ["Purchase Invoice", "Purchase Receipt", "Sales Invoice", "POS Invoice"]
else "dn_detail"
)
data = frappe.db.sql(
f"""
select {column}
select {column}, {field}
from
`tab{doc.doctype} Item` child, `tab{doc.doctype}` par
where
child.parent = par.name and par.docstatus = 1
and par.is_return = 1 and par.return_against = %s
group by item_code
group by item_code, {field}
""",
doc.return_against,
as_dict=1,
@@ -280,7 +306,7 @@ def get_already_returned_items(doc):
for d in data:
items.setdefault(
d.item_code,
(d.item_code, d.get(field)),
frappe._dict(
{
"qty": d.get("qty"),
@@ -380,7 +406,8 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None):
paid_amount = 0.00
base_paid_amount = 0.00
data.base_amount = flt(
data.amount * source.conversion_rate, source.precision("base_paid_amount")
data.amount * source.conversion_rate,
source.precision("base_paid_amount"),
)
paid_amount += data.amount
base_paid_amount += data.base_amount
@@ -540,10 +567,17 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None):
},
doctype + " Item": {
"doctype": doctype + " Item",
"field_map": {"serial_no": "serial_no", "batch_no": "batch_no", "bom": "bom"},
"field_map": {
"serial_no": "serial_no",
"batch_no": "batch_no",
"bom": "bom",
},
"postprocess": update_item,
},
"Payment Schedule": {"doctype": "Payment Schedule", "postprocess": update_terms},
"Payment Schedule": {
"doctype": "Payment Schedule",
"postprocess": update_terms,
},
},
target_doc,
set_missing_values,
@@ -576,13 +610,20 @@ def get_rate_for_return(
item_row,
)
if voucher_type in ("Purchase Receipt", "Purchase Invoice", "Subcontracting Receipt"):
if voucher_type in (
"Purchase Receipt",
"Purchase Invoice",
"Subcontracting Receipt",
):
select_field = "incoming_rate"
else:
select_field = "abs(stock_value_difference / actual_qty)"
rate = flt(frappe.db.get_value("Stock Ledger Entry", filters, select_field))
if not (rate and return_against) and voucher_type in ["Sales Invoice", "Delivery Note"]:
if not (rate and return_against) and voucher_type in [
"Sales Invoice",
"Delivery Note",
]:
rate = frappe.db.get_value(f"{voucher_type} Item", voucher_detail_no, "incoming_rate")
if not rate and sle:
@@ -612,6 +653,7 @@ def get_return_against_item_fields(voucher_type):
"Delivery Note": "dn_detail",
"Sales Invoice": "sales_invoice_item",
"Subcontracting Receipt": "subcontracting_receipt_item",
"POS Invoice": "sales_invoice_item",
}
return return_against_item_fields[voucher_type]
@@ -625,7 +667,11 @@ def get_filters(
return_against_item_field,
item_row,
):
filters = {"voucher_type": voucher_type, "voucher_no": return_against, "item_code": item_code}
filters = {
"voucher_type": voucher_type,
"voucher_no": return_against,
"item_code": item_code,
}
if item_row:
reference_voucher_detail_no = item_row.get(return_against_item_field)
@@ -665,3 +711,9 @@ def get_returned_serial_nos(child_doc, parent_doc, serial_no_field="serial_no"):
serial_nos.extend(get_serial_nos(row.get(serial_no_field)))
return serial_nos
@frappe.whitelist()
def get_payment_data(invoice):
payment = frappe.db.get_all("Sales Invoice Payment", {"parent": invoice}, ["mode_of_payment", "amount"])
return payment

View File

@@ -68,19 +68,13 @@ class SellingController(StockController):
if customer:
from erpnext.accounts.party import _get_party_details
fetch_payment_terms_template = False
if self.get("__islocal") or self.company != frappe.db.get_value(
self.doctype, self.name, "company"
):
fetch_payment_terms_template = True
party_details = _get_party_details(
customer,
ignore_permissions=self.flags.ignore_permissions,
doctype=self.doctype,
company=self.company,
posting_date=self.get("posting_date"),
fetch_payment_terms_template=fetch_payment_terms_template,
fetch_payment_terms_template=self.has_value_changed("company"),
party_address=self.customer_address,
shipping_address=self.shipping_address_name,
company_address=self.get("company_address"),
@@ -167,6 +161,9 @@ class SellingController(StockController):
total = 0.0
sales_team = self.get("sales_team")
self.validate_sales_team(sales_team)
for sales_person in sales_team:
self.round_floats_in(sales_person)
@@ -186,6 +183,20 @@ class SellingController(StockController):
if sales_team and total != 100.0:
throw(_("Total allocated percentage for sales team should be 100"))
def validate_sales_team(self, sales_team):
sales_persons = [d.sales_person for d in sales_team]
if not sales_persons:
return
sales_person_status = frappe.db.get_all(
"Sales Person", filters={"name": ["in", sales_persons]}, fields=["name", "enabled"]
)
for row in sales_person_status:
if not row.enabled:
frappe.throw(_("Sales Person <b>{0}</b> is disabled.").format(row.name))
def validate_max_discount(self):
for d in self.get("items"):
if d.item_code:
@@ -348,12 +359,32 @@ class SellingController(StockController):
return il
def has_product_bundle(self, item_code):
product_bundle = frappe.qb.DocType("Product Bundle")
return (
frappe.qb.from_(product_bundle)
.select(product_bundle.name)
.where((product_bundle.new_item_code == item_code) & (product_bundle.disabled == 0))
).run()
product_bundle_items = getattr(self, "_product_bundle_items", None)
if product_bundle_items is None:
self._product_bundle_items = product_bundle_items = {}
if item_code not in product_bundle_items:
self._fetch_product_bundle_items(item_code)
return product_bundle_items[item_code]
def _fetch_product_bundle_items(self, item_code):
product_bundle_items = self._product_bundle_items
items_to_fetch = {row.item_code for row in self.items if row.item_code not in product_bundle_items}
# fetch for requisite item_code even if it is not in items
items_to_fetch.add(item_code)
items_with_product_bundle = {
row.new_item_code
for row in frappe.get_all(
"Product Bundle",
filters={"new_item_code": ("in", items_to_fetch), "disabled": 0},
fields="new_item_code",
)
}
for item_code in items_to_fetch:
product_bundle_items[item_code] = item_code in items_with_product_bundle
def get_already_delivered_qty(self, current_docname, so, so_detail):
delivered_via_dn = frappe.db.sql(

View File

@@ -94,7 +94,10 @@ status_map = {
["To Bill", "eval:self.per_billed == 0 and self.docstatus == 1"],
["Partly Billed", "eval:self.per_billed > 0 and self.per_billed < 100 and self.docstatus == 1"],
["Return Issued", "eval:self.per_returned == 100 and self.docstatus == 1"],
["Completed", "eval:self.per_billed == 100 and self.docstatus == 1"],
[
"Completed",
"eval:(self.per_billed == 100 and self.docstatus == 1) or (self.docstatus == 1 and self.grand_total == 0 and self.per_returned != 100 and self.is_return == 0)",
],
["Cancelled", "eval:self.docstatus==2"],
["Closed", "eval:self.status=='Closed' and self.docstatus != 2"],
],

View File

@@ -801,7 +801,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":

View File

@@ -18,7 +18,7 @@ from erpnext.controllers.accounts_controller import (
validate_inclusive_tax,
validate_taxes_and_charges,
)
from erpnext.stock.get_item_details import _get_item_tax_template
from erpnext.stock.get_item_details import _get_item_tax_template, get_item_tax_map
from erpnext.utilities.regional import temporary_flag
@@ -27,6 +27,11 @@ class calculate_taxes_and_totals:
self.doc = doc
frappe.flags.round_off_applicable_accounts = []
if doc.get("round_off_applicable_accounts_for_tax_withholding"):
frappe.flags.round_off_applicable_accounts.append(
doc.round_off_applicable_accounts_for_tax_withholding
)
self._items = self.filter_rows() if self.doc.doctype == "Quotation" else self.doc.get("items")
get_round_off_applicable_accounts(self.doc.company, frappe.flags.round_off_applicable_accounts)
@@ -67,6 +72,7 @@ class calculate_taxes_and_totals:
self.validate_conversion_rate()
self.calculate_item_values()
self.validate_item_tax_template()
self.update_item_tax_map()
self.initialize_taxes()
self.determine_exclusive_rate()
self.calculate_net_total()
@@ -130,6 +136,14 @@ class calculate_taxes_and_totals:
)
)
def update_item_tax_map(self):
for item in self.doc.items:
item.item_tax_rate = get_item_tax_map(
company=self.doc.get("company"),
item_tax_template=item.item_tax_template,
as_json=True,
)
def validate_conversion_rate(self):
# validate conversion rate
company_currency = erpnext.get_company_currency(self.doc.company)

View File

@@ -2,11 +2,14 @@
# For license information, please see license.txt
from datetime import datetime
import frappe
from frappe import qb
from frappe.query_builder.functions import Sum
from frappe.tests.utils import FrappeTestCase
from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import add_days, getdate, nowdate
from frappe.utils.data import getdate as convert_to_date
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry
@@ -705,6 +708,67 @@ class TestAccountsController(FrappeTestCase):
self.assertEqual(exc_je_for_si, [])
self.assertEqual(exc_je_for_pe, [])
@change_settings("Accounts Settings", {"exchange_gain_loss_posting_date": "Reconciliation Date"})
def test_17_gain_loss_posting_date_for_normal_payment(self):
# Sales Invoice in Foreign Currency
rate = 80
rate_in_account_currency = 1
adv_date = convert_to_date(add_days(nowdate(), -2))
inv_date = convert_to_date(add_days(nowdate(), -1))
si = self.create_sales_invoice(posting_date=inv_date, qty=1, rate=rate_in_account_currency)
# Test payments with different exchange rates
pe = self.create_payment_entry(posting_date=adv_date, amount=1, source_exc_rate=75.1).save().submit()
pr = self.create_payment_reconciliation()
pr.from_invoice_date = add_days(nowdate(), -1)
pr.to_invoice_date = nowdate()
pr.from_payment_date = add_days(nowdate(), -2)
pr.to_payment_date = nowdate()
pr.get_unreconciled_entries()
self.assertEqual(len(pr.invoices), 1)
self.assertEqual(len(pr.payments), 1)
invoices = [x.as_dict() for x in pr.invoices]
payments = [x.as_dict() for x in pr.payments]
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
pr.reconcile()
self.assertEqual(len(pr.invoices), 0)
self.assertEqual(len(pr.payments), 0)
# Outstanding in both currencies should be '0'
si.reload()
self.assertEqual(si.outstanding_amount, 0)
self.assert_ledger_outstanding(si.doctype, si.name, 0.0, 0.0)
# Exchange Gain/Loss Journal should've been created.
exc_je_for_si = self.get_journals_for(si.doctype, si.name)
exc_je_for_pe = self.get_journals_for(pe.doctype, pe.name)
self.assertNotEqual(exc_je_for_si, [])
self.assertEqual(len(exc_je_for_si), 1)
self.assertEqual(len(exc_je_for_pe), 1)
self.assertEqual(exc_je_for_si[0], exc_je_for_pe[0])
self.assertEqual(
getdate(nowdate()), frappe.db.get_value("Journal Entry", exc_je_for_pe[0].parent, "posting_date")
)
# Cancel Payment
pe.reload()
pe.cancel()
# outstanding should be same as grand total
si.reload()
self.assertEqual(si.outstanding_amount, rate_in_account_currency)
self.assert_ledger_outstanding(si.doctype, si.name, rate, rate_in_account_currency)
# Exchange Gain/Loss Journal should've been cancelled
exc_je_for_si = self.get_journals_for(si.doctype, si.name)
exc_je_for_pe = self.get_journals_for(pe.doctype, pe.name)
self.assertEqual(exc_je_for_si, [])
self.assertEqual(exc_je_for_pe, [])
def test_20_journal_against_sales_invoice(self):
# Invoice in Foreign Currency
si = self.create_sales_invoice(qty=1, conversion_rate=80, rate=1)
@@ -1342,32 +1406,32 @@ class TestAccountsController(FrappeTestCase):
# Invoices
si1 = self.create_sales_invoice(qty=1, rate=rate_in_account_currency, do_not_submit=True)
si1.department = "Management"
si1.department = "Management - _TC"
si1.save().submit()
si2 = self.create_sales_invoice(qty=1, rate=rate_in_account_currency, do_not_submit=True)
si2.department = "Operations"
si2.department = "Operations - _TC"
si2.save().submit()
# Payments
cr_note1 = self.create_sales_invoice(qty=-1, conversion_rate=75, rate=1, do_not_save=True)
cr_note1.department = "Management"
cr_note1.department = "Management - _TC"
cr_note1.is_return = 1
cr_note1.save().submit()
cr_note2 = self.create_sales_invoice(qty=-1, conversion_rate=75, rate=1, do_not_save=True)
cr_note2.department = "Legal"
cr_note2.department = "Legal - _TC"
cr_note2.is_return = 1
cr_note2.save().submit()
pe1 = get_payment_entry(si1.doctype, si1.name)
pe1.references = []
pe1.department = "Research & Development"
pe1.department = "Research & Development - _TC"
pe1.save().submit()
pe2 = get_payment_entry(si1.doctype, si1.name)
pe2.references = []
pe2.department = "Management"
pe2.department = "Management - _TC"
pe2.save().submit()
je1 = self.create_journal_entry(
@@ -1380,7 +1444,7 @@ class TestAccountsController(FrappeTestCase):
)
je1.accounts[0].party_type = "Customer"
je1.accounts[0].party = self.customer
je1.accounts[0].department = "Management"
je1.accounts[0].department = "Management - _TC"
je1.save().submit()
# assert dimension filter's result
@@ -1389,17 +1453,17 @@ class TestAccountsController(FrappeTestCase):
self.assertEqual(len(pr.invoices), 2)
self.assertEqual(len(pr.payments), 5)
pr.department = "Legal"
pr.department = "Legal - _TC"
pr.get_unreconciled_entries()
self.assertEqual(len(pr.invoices), 0)
self.assertEqual(len(pr.payments), 1)
pr.department = "Management"
pr.department = "Management - _TC"
pr.get_unreconciled_entries()
self.assertEqual(len(pr.invoices), 1)
self.assertEqual(len(pr.payments), 3)
pr.department = "Research & Development"
pr.department = "Research & Development - _TC"
pr.get_unreconciled_entries()
self.assertEqual(len(pr.invoices), 0)
self.assertEqual(len(pr.payments), 1)
@@ -1411,17 +1475,17 @@ class TestAccountsController(FrappeTestCase):
# Invoice
si = self.create_sales_invoice(qty=1, rate=rate_in_account_currency, do_not_submit=True)
si.department = "Management"
si.department = "Management - _TC"
si.save().submit()
# Payment
cr_note = self.create_sales_invoice(qty=-1, conversion_rate=75, rate=1, do_not_save=True)
cr_note.department = "Management"
cr_note.department = "Management - _TC"
cr_note.is_return = 1
cr_note.save().submit()
pr = self.create_payment_reconciliation()
pr.department = "Management"
pr.department = "Management - _TC"
pr.get_unreconciled_entries()
self.assertEqual(len(pr.invoices), 1)
self.assertEqual(len(pr.payments), 1)
@@ -1454,7 +1518,7 @@ class TestAccountsController(FrappeTestCase):
# Sales Invoice in Foreign Currency
self.setup_dimensions()
rate_in_account_currency = 1
dpt = "Research & Development"
dpt = "Research & Development - _TC"
si = self.create_sales_invoice(qty=1, rate=rate_in_account_currency, do_not_save=True)
si.department = dpt
@@ -1490,7 +1554,7 @@ class TestAccountsController(FrappeTestCase):
def test_93_dimension_inheritance_on_advance(self):
self.setup_dimensions()
dpt = "Research & Development"
dpt = "Research & Development - _TC"
adv = self.create_payment_entry(amount=1, source_exc_rate=85)
adv.department = dpt

View File

@@ -38,7 +38,7 @@
"table_fieldname": "competitors"
}
],
"modified": "2023-11-23 19:33:54.284279",
"modified": "2024-12-10 08:26:38.496003",
"modified_by": "Administrator",
"module": "CRM",
"name": "Competitor",
@@ -53,20 +53,25 @@
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"role": "Sales Master Manager",
"share": 1,
"write": 1
},
{
"create": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales User",
"share": 1,
"write": 1
"role": "Sales User"
},
{
"read": 1,
"role": "Sales Manager"
},
{
"read": 1,
"role": "Maintenance Manager"
},
{
"read": 1,
"role": "Maintenance User"
}
],
"quick_entry": 1,

View File

@@ -375,7 +375,7 @@
"depends_on": "eval:!doc.__islocal",
"fieldname": "notes_tab",
"fieldtype": "Tab Break",
"label": "Comments"
"label": "Notes"
},
{
"collapsible": 1,
@@ -514,7 +514,7 @@
"idx": 5,
"image_field": "image",
"links": [],
"modified": "2023-12-01 18:46:49.468526",
"modified": "2025-01-31 13:40:08.094759",
"modified_by": "Administrator",
"module": "CRM",
"name": "Lead",

View File

@@ -32,6 +32,7 @@ frappe.query_reports["Opportunity Summary by Sales Stage"] = {
fieldname: "status",
label: __("Status"),
fieldtype: "MultiSelectList",
options: ["Open", "Converted", "Quotation", "Replied"],
get_data: function () {
return [
{ value: "Open", description: "Status" },

0
erpnext/edi/__init__.py Normal file
View File

View File

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