Compare commits

...

442 Commits

Author SHA1 Message Date
Frappe PR Bot
6f80af0355 chore(release): Bumped to Version 15.45.3
## [15.45.3](https://github.com/frappe/erpnext/compare/v15.45.2...v15.45.3) (2024-12-08)

### Bug Fixes

* duplicate buttons ([11fc560](11fc560a88))
2024-12-08 09:23:09 +00:00
rohitwaghchaure
0c3bf2daf1 Merge pull request #44597 from rohitwaghchaure/fixed-duplicate-buttons
fix: duplicate buttons
2024-12-08 14:51:46 +05:30
Rohit Waghchaure
11fc560a88 fix: duplicate buttons 2024-12-08 14:44:19 +05:30
Frappe PR Bot
0cd495a128 chore(release): Bumped to Version 15.45.2
## [15.45.2](https://github.com/frappe/erpnext/compare/v15.45.1...v15.45.2) (2024-12-07)

### Bug Fixes

* BOM name issue (backport [#44586](https://github.com/frappe/erpnext/issues/44586)) ([#44589](https://github.com/frappe/erpnext/issues/44589)) ([408a092](408a09233b))
2024-12-07 08:18:12 +00:00
rohitwaghchaure
3a98660739 Merge pull request #44593 from frappe/mergify/bp/version-15/pr-44589
fix: BOM name issue (backport #44586) (backport #44589)
2024-12-07 13:46:44 +05:30
rohitwaghchaure
97c91c2217 Merge branch 'version-15' into mergify/bp/version-15/pr-44589 2024-12-07 13:12:59 +05:30
rohitwaghchaure
a204abcaa5 chore: fix conflicts 2024-12-07 13:10:40 +05:30
Frappe PR Bot
3b57767d36 chore(release): Bumped to Version 15.45.1
## [15.45.1](https://github.com/frappe/erpnext/compare/v15.45.0...v15.45.1) (2024-12-07)

### Bug Fixes

* BOM for variant items (backport [#44580](https://github.com/frappe/erpnext/issues/44580)) ([#44584](https://github.com/frappe/erpnext/issues/44584)) ([2a2d8da](2a2d8da628))
* BOM name issue (backport [#44575](https://github.com/frappe/erpnext/issues/44575)) ([#44579](https://github.com/frappe/erpnext/issues/44579)) ([27b63be](27b63beb18))
* variant qty while making work order from BOM (backport [#44548](https://github.com/frappe/erpnext/issues/44548)) ([#44551](https://github.com/frappe/erpnext/issues/44551)) ([3c50cfe](3c50cfef4e))
2024-12-07 07:35:56 +00:00
rohitwaghchaure
dc74912f5d Merge pull request #44592 from frappe/mergify/bp/version-15/pr-44584
fix: BOM for variant items (backport #44580) (backport #44584)
2024-12-07 13:04:33 +05:30
rohitwaghchaure
d860a5b35d Merge pull request #44591 from frappe/mergify/bp/version-15/pr-44579
fix: BOM name issue (backport #44575) (backport #44579)
2024-12-07 13:04:03 +05:30
rohitwaghchaure
b00cc83ae6 Merge pull request #44590 from frappe/mergify/bp/version-15/pr-44551
fix: variant qty while making work order from BOM (backport #44548) (backport #44551)
2024-12-07 13:03:47 +05:30
mergify[bot]
408a09233b fix: BOM name issue (backport #44586) (#44589)
fix: BOM name issue (#44586)

(cherry picked from commit d871e21a40)

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

# Conflicts:
#	erpnext/manufacturing/doctype/bom/bom.py
2024-12-07 07:29:44 +00:00
mergify[bot]
2a2d8da628 fix: BOM for variant items (backport #44580) (#44584)
fix: BOM for variant items (#44580)

(cherry picked from commit 93e9517f5d)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
(cherry picked from commit 0b268279cf)
2024-12-07 06:57:37 +00:00
mergify[bot]
27b63beb18 fix: BOM name issue (backport #44575) (#44579)
fix: BOM name issue (#44575)

fix: bom name issue
(cherry picked from commit b7a3c6b6ca)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
(cherry picked from commit 03ae9e27be)
2024-12-07 06:56:36 +00:00
mergify[bot]
3c50cfef4e fix: variant qty while making work order from BOM (backport #44548) (#44551)
fix: variant qty while making work order from BOM (#44548)

(cherry picked from commit 1571dff3ef)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
(cherry picked from commit 63b1df38a8)
2024-12-07 06:56:31 +00:00
Frappe PR Bot
1d158d58f6 chore(release): Bumped to Version 15.45.0
# [15.45.0](https://github.com/frappe/erpnext/compare/v15.44.0...v15.45.0) (2024-12-04)

### Bug Fixes

* Add filter for `outstanding_amount` to fetch open PRs ([5999a8e](5999a8e24f))
* Add translation for showing mandatory fields in error msg ([0e1f5ff](0e1f5ff391))
* added fieldname to avoid fieldname to translate ([17c2734](17c2734042))
* Added translation for `Account` column ([eb4a485](eb4a485df6))
* adjusted incoming rate for zero rated item in purchase receipt ([435280d](435280d626))
* always set sales incoming rate for internal transfers ([05795af](05795af471))
* calculate submitted payment entry as paid amount ([ebdacc0](ebdacc094c))
* correct buying amount for product bundel ([f165e17](f165e1732b))
* Dashboard for `Payment Request` ([0d67c62](0d67c62f43))
* Data Should be Computed in Backend to Maintain Consistent Behaviour ([#44195](https://github.com/frappe/erpnext/issues/44195)) ([8ab9fc7](8ab9fc7f55))
* do not validate stock during inward (backport [#44417](https://github.com/frappe/erpnext/issues/44417)) ([#44427](https://github.com/frappe/erpnext/issues/44427)) ([e607795](e607795bae))
* handle multi currency in common party journal entry ([c8e2c9a](c8e2c9aa25))
* incorrect Gross Margin on project (backport [#44461](https://github.com/frappe/erpnext/issues/44461)) ([#44468](https://github.com/frappe/erpnext/issues/44468)) ([0a9c92f](0a9c92fce9))
* IndexError in Asset Depreciation Ledger when query result is empty ([c3bc724](c3bc724523))
* link cash flow rows and fix summary linking ([633be8d](633be8d06b))
* move validate_total_debit_and_credit from validate to on_submit in Journal Entry ([63de576](63de576be6))
* number format in the message (backport [#44435](https://github.com/frappe/erpnext/issues/44435)) ([#44438](https://github.com/frappe/erpnext/issues/44438)) ([579d8e2](579d8e293e))
* precision calculation causing 0.1 discrepancy (backport [#44431](https://github.com/frappe/erpnext/issues/44431)) ([#44436](https://github.com/frappe/erpnext/issues/44436)) ([0d41c23](0d41c23383))
* remove queries ([ea57f2b](ea57f2b292))
* SABB print for packed items (backport [#44413](https://github.com/frappe/erpnext/issues/44413)) ([#44428](https://github.com/frappe/erpnext/issues/44428)) ([0e39aa3](0e39aa349e))
* set correct unallocated amount in Payment Entry ([#43958](https://github.com/frappe/erpnext/issues/43958)) ([ae93f7f](ae93f7f967))
* show "Send SMS" only when enabled (backport [#43941](https://github.com/frappe/erpnext/issues/43941)) ([#43970](https://github.com/frappe/erpnext/issues/43970)) ([0fbc60a](0fbc60a20e))
* source warehouse not set in required items of WO (backport [#44426](https://github.com/frappe/erpnext/issues/44426)) ([#44434](https://github.com/frappe/erpnext/issues/44434)) ([c81b5e3](c81b5e3d9c))
* Translate `Party Account` column label ([fdda864](fdda86455a))
* typeerror on transaction.js ([173d60f](173d60fb7d))

### Features

* add Company Contact Person in selling transactions (backport [#44362](https://github.com/frappe/erpnext/issues/44362)) ([#44398](https://github.com/frappe/erpnext/issues/44398)) ([70b5b08](70b5b08d58))
* **Dunning:** separate tab "Address & Contact" ([#44363](https://github.com/frappe/erpnext/issues/44363)) ([e0cb5f9](e0cb5f9ba8))

### Performance Improvements

* cache product bundle items at document level ([#44440](https://github.com/frappe/erpnext/issues/44440)) ([1f97979](1f97979059))
* reduce queries during transaction save ([48059a7](48059a7c74))

### Reverts

* remove default `Payment Request` indicator color ([9c4b581](9c4b5814a6))
2024-12-04 04:38:06 +00:00
ruthra kumar
cd57f5cd0a Merge pull request #44483 from frappe/version-15-hotfix
chore: release v15
2024-12-04 10:06:46 +05:30
ruthra kumar
3e2bc139ab Merge pull request #44491 from frappe/mergify/bp/version-15-hotfix/pr-44339
fix: move `validate_total_debit_and_credit` from`validate` to`on_submit` in Journal Entry (backport #44339)
2024-12-03 19:52:30 +05:30
ruthra kumar
16d0d42afe refactor: validate debit and credit on before_submit
(cherry picked from commit c3ace82db8)
2024-12-03 13:47:27 +00:00
vishakhdesai
63de576be6 fix: move validate_total_debit_and_credit from validate to on_submit in Journal Entry
(cherry picked from commit 8b5d4c0236)
2024-12-03 13:47:27 +00:00
ruthra kumar
d5a544ca69 Merge pull request #44485 from frappe/mergify/bp/version-15-hotfix/pr-44467
fix: Multiple Fixes in Gross Profit Report (backport #44467)
2024-12-03 19:15:15 +05:30
ruthra kumar
4a713f6b5e chore: fix typo
(cherry picked from commit fc0122ce76)
2024-12-03 12:01:07 +00:00
ljain112
f165e1732b fix: correct buying amount for product bundel
(cherry picked from commit 4e6a5893e7)
2024-12-03 12:01:07 +00:00
ljain112
ea57f2b292 fix: remove queries
(cherry picked from commit a86b223aed)
2024-12-03 12:01:07 +00:00
ruthra kumar
c880476cbe Merge pull request #44481 from frappe/mergify/bp/version-15-hotfix/pr-44450
fix: calculate submitted payment entry amount for grand total (backport #44450)
2024-12-03 15:15:40 +05:30
mergify[bot]
0a9c92fce9 fix: incorrect Gross Margin on project (backport #44461) (#44468)
* fix: incorrect Gross Margin on project (#44461)

(cherry picked from commit 7de9c14a2c)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py

* chore: resolve conflict

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
Co-authored-by: ruthra kumar <ruthra@erpnext.com>
2024-12-03 15:09:09 +05:30
Sugesh393
aa090beae0 test: add new unit test to validate paid amount in payment request
(cherry picked from commit 9bee2d430c)
2024-12-03 09:22:23 +00:00
Sugesh393
ebdacc094c fix: calculate submitted payment entry as paid amount
(cherry picked from commit 561a159aec)
2024-12-03 09:22:23 +00:00
ruthra kumar
0c28726ce2 Merge pull request #44480 from frappe/mergify/bp/version-15-hotfix/pr-44415
fix: adjusted incoming rate for zero rated item in purchase receipt (backport #44415)
2024-12-03 14:48:51 +05:30
ruthra kumar
be0604f7cf Merge pull request #44478 from frappe/mergify/bp/version-15-hotfix/pr-44373
fix: Always Calculate `sales_incoming_rate` for Internal Transfers (backport #44373)
2024-12-03 14:48:14 +05:30
ljain112
435280d626 fix: adjusted incoming rate for zero rated item in purchase receipt
(cherry picked from commit 3182c6981c)
2024-12-03 08:56:10 +00:00
Ninad1306
558d49b3d3 test: validate buying workflow
(cherry picked from commit 94d3fc9fde)
2024-12-03 08:48:52 +00:00
Ninad1306
05795af471 fix: always set sales incoming rate for internal transfers
(cherry picked from commit d049c97884)
2024-12-03 08:48:51 +00:00
ruthra kumar
38aa3769bb Merge pull request #44477 from frappe/mergify/bp/version-15-hotfix/pr-44470
refactor(UI): Rearranging fields under new sections (backport #44470)
2024-12-03 11:13:37 +05:30
ruthra kumar
5c6d9c9812 refactor(UI): Rearranging fields under new sections
(cherry picked from commit 7244754d28)
2024-12-03 05:00:17 +00:00
ruthra kumar
85fda71835 Merge pull request #44368 from frappe/mergify/bp/version-15-hotfix/pr-44363
feat(Dunning): separate tab "Address & Contact" (backport #44363)
2024-12-03 10:21:22 +05:30
ruthra kumar
7150aff520 Merge pull request #44466 from frappe/mergify/bp/version-15-hotfix/pr-43958
fix: set correct unallocated amount in Payment Entry (backport #43958)
2024-12-02 16:15:53 +05:30
ruthra kumar
c157978912 chore: resolve conflicts 2024-12-02 15:24:32 +05:30
Sagar Vora
ae93f7f967 fix: set correct unallocated amount in Payment Entry (#43958)
* fix: set correct unallocated amount in Payment Entry

* fix: add checkbox and other logic fix

* fix: patch to set is_exchange_gain_loss in Payment Entry deductions

* fix: consider deductions except exch. gain/loss

* fix: set exchange gain loss in payment entry

* fix: separate function to set exchange gain loss

* fix: failing test cases

* fix: add cash disc. row first

* fix: review changes

* fix: changes as per review

* fix: failing test cases

* fix: review

* fix: wait for request to complete before updating exchange gain loss

* fix: review

---------

Co-authored-by: vishakhdesai <vishakhdesai@gmail.com>
Co-authored-by: ruthra kumar <ruthra@erpnext.com>
(cherry picked from commit 7cc111f790)

# Conflicts:
#	erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.json
#	erpnext/patches.txt
2024-12-02 09:25:55 +00:00
ruthra kumar
f5ddc9a543 Merge pull request #44465 from frappe/mergify/bp/version-15-hotfix/pr-44412
fix: handle multi currency in common party journal entry (backport #44412)
2024-12-02 14:35:44 +05:30
ruthra kumar
4c5570ae7d chore: resolve conflict 2024-12-02 13:46:39 +05:30
ruthra kumar
d0356f81ba Merge pull request #44463 from frappe/mergify/bp/version-15-hotfix/pr-44437
fix: Added translation for `Account` column (backport #44437)
2024-12-02 13:39:50 +05:30
ljain112
c8e2c9aa25 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:11 +00:00
Abdeali Chharchhoda
fdda86455a fix: Translate Party Account column label
(cherry picked from commit a4f8315602)
2024-12-02 06:53:42 +00:00
Abdeali Chharchhoda
eb4a485df6 fix: Added translation for Account column
(cherry picked from commit de6cbd382f)
2024-12-02 06:53:41 +00:00
mergify[bot]
579d8e293e fix: number format in the message (backport #44435) (#44438)
fix: number format in the message (#44435)

(cherry picked from commit 810c72a30c)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-11-30 13:42:54 +05:30
Sagar Vora
a807776f83 Merge pull request #44447 from frappe/mergify/bp/version-15-hotfix/pr-44443
perf: reduce queries during transaction save (backport #44443)
2024-11-30 00:48:36 +05:30
Sagar Vora
48059a7c74 perf: reduce queries during transaction save
(cherry picked from commit b6b8a06fda)
2024-11-29 19:17:31 +00:00
Sagar Vora
0199bc127a Merge pull request #44445 from frappe/mergify/bp/version-15-hotfix/pr-44439
fix: added fieldname to avoid fieldname to translate (backport #44439)
2024-11-30 00:30:01 +05:30
Abdeali Chharchhoda
17c2734042 fix: added fieldname to avoid fieldname to translate
(cherry picked from commit b80022133c)
2024-11-29 18:56:45 +00:00
Sagar Vora
a1643ad292 Merge pull request #44442 from frappe/mergify/bp/version-15-hotfix/pr-44440
perf: cache product bundle items at document level (backport #44440)
2024-11-29 23:04:14 +05:30
Sagar Vora
1f97979059 perf: cache product bundle items at document level (#44440)
(cherry picked from commit 6de7320ef4)
2024-11-29 17:23:36 +00:00
mergify[bot]
c81b5e3d9c fix: source warehouse not set in required items of WO (backport #44426) (#44434)
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)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-11-29 18:13:05 +05:30
mergify[bot]
0d41c23383 fix: precision calculation causing 0.1 discrepancy (backport #44431) (#44436)
fix: precision calculation causing 0.1 discrepancy (#44431)

(cherry picked from commit 7f7564b581)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-11-29 18:12:24 +05:30
mergify[bot]
0e39aa349e fix: SABB print for packed items (backport #44413) (#44428)
fix: SABB print for packed items (#44413)

(cherry picked from commit 5266f236b7)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-11-29 17:00:59 +05:30
mergify[bot]
e607795bae fix: do not validate stock during inward (backport #44417) (#44427)
fix: do not validate stock during inward (#44417)

(cherry picked from commit d37d7b9811)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-11-29 15:49:18 +05:30
ruthra kumar
821f39203c Merge pull request #44391 from frappe/mergify/bp/version-15-hotfix/pr-44386
fix: Add translation for showing mandatory fields in error msg (backport #44386)
2024-11-29 15:38:35 +05:30
ruthra kumar
35e365c263 Merge pull request #44425 from frappe/mergify/bp/version-15-hotfix/pr-44302
fix: Minor Updates in `Payment Request` and `Payment Entry`  (backport #44302)
2024-11-29 15:38:01 +05:30
ruthra kumar
1c50111371 chore: resolve conflict 2024-11-29 14:50:41 +05:30
Abdeali Chharchhoda
5999a8e24f fix: Add filter for outstanding_amount to fetch open PRs
(cherry picked from commit 214dfab269)
2024-11-29 09:11:11 +00:00
Abdeali Chharchhoda
4b046160f8 refactor: Move PR link filters to client side
(cherry picked from commit 2db2c8bce1)
2024-11-29 09:11:11 +00:00
Abdeali Chharchhoda
9c4b5814a6 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:11 +00:00
Abdeali Chharchhoda
0d67c62f43 fix: Dashboard for Payment Request
(cherry picked from commit 91955e27c3)
2024-11-29 09:11:10 +00:00
Abdeali Chharchhoda
5f785ede16 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:10 +00:00
ruthra kumar
5b2fce2df7 Merge pull request #44419 from frappe/mergify/bp/version-15-hotfix/pr-42524
fix: link cash flow rows and fix summary linking (backport #42524)
2024-11-29 12:22:51 +05:30
ruthra kumar
ae81bb3c1b chore: revert 'stub' 2024-11-29 10:52:38 +05:30
ruthra kumar
48d6fcaab8 chore: resolve conflict 2024-11-29 10:43:36 +05:30
David
633be8d06b fix: link cash flow rows and fix summary linking
(cherry picked from commit b94af28587)

# Conflicts:
#	erpnext/public/js/financial_statements.js
2024-11-29 05:00:48 +00:00
Khushi Rawat
651d7e4cfc Merge pull request #44402 from frappe/mergify/bp/version-15-hotfix/pr-44400
fix: IndexError in Asset Depreciation Ledger when query result is empty (backport #44400)
2024-11-28 18:59:11 +05:30
Smit Vora
0084f45629 Merge pull request #44410 from frappe/mergify/bp/version-15-hotfix/pr-44195
fix: Data Should be Computed in Backend to Maintain Consistent Behaviour (backport #44195)
2024-11-28 16:27:05 +05:30
Ninad Parikh
8ab9fc7f55 fix: Data Should be Computed in Backend to Maintain Consistent Behaviour (#44195)
(cherry picked from commit 69bd90b038)
2024-11-28 10:31:27 +00:00
ruthra kumar
11c54d27f2 Merge pull request #44407 from frappe/mergify/bp/version-15-hotfix/pr-44405
fix: typeerror on transaction.js (backport #44405)
2024-11-28 15:02:04 +05:30
ruthra kumar
173d60fb7d fix: typeerror on transaction.js
(cherry picked from commit 46ce8780f2)
2024-11-28 09:20:38 +00:00
Khushi Rawat
5bbef90f08 chore: removed print statement
(cherry picked from commit 1737de7c10)
2024-11-28 07:27:42 +00:00
Khushi Rawat
c3bc724523 fix: IndexError in Asset Depreciation Ledger when query result is empty
(cherry picked from commit 7c393e5aa0)
2024-11-28 07:27:42 +00:00
mergify[bot]
0fbc60a20e fix: show "Send SMS" only when enabled (backport #43941) (#43970)
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:16 +01:00
mergify[bot]
70b5b08d58 feat: add Company Contact Person in selling transactions (backport #44362) (#44398)
* feat: add Company Contact Person in selling transactions (#44362)

(cherry picked from commit f6776c7d6b)

* chore: resolve merge conflicts

---------

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2024-11-27 18:55:43 +01:00
Frappe PR Bot
294ded2030 chore(release): Bumped to Version 15.44.0
# [15.44.0](https://github.com/frappe/erpnext/compare/v15.43.3...v15.44.0) (2024-11-27)

### Bug Fixes

*  correct placeholder index in message ([a523c14](a523c14fd5))
* add company dynamic filters in number cards ([db21def](db21def58b))
* added Stock UOM field for RM in work order (backport [#44185](https://github.com/frappe/erpnext/issues/44185)) ([#44237](https://github.com/frappe/erpnext/issues/44237)) ([d4f0512](d4f0512a10))
* added validation for quality inspection (backport [#44351](https://github.com/frappe/erpnext/issues/44351)) ([#44357](https://github.com/frappe/erpnext/issues/44357)) ([89bd4eb](89bd4eba46))
* billed qty and received amount in PO analysis report (backport [#44349](https://github.com/frappe/erpnext/issues/44349)) ([#44354](https://github.com/frappe/erpnext/issues/44354)) ([ea0f24a](ea0f24aa57))
* check difference with company currency ([d6ef438](d6ef43858c))
* filter item with search fields ([d073b00](d073b005a8))
* filter with item group only if it is mentioned in pos profile ([a439862](a4398626f6))
* Get submitted documents in validate_for_closed_fiscal_year ([af74a3c](af74a3c32f))
* gp for return invoice ([f4518ca](f4518cac9a))
* include current invoice amount when tax_on_excess_amount is checked ([0ffeb9f](0ffeb9f6ad))
* Increase quantity by `1 UOM` when adding an item from the selector in POS ([0d38028](0d3802873b))
* initially closing amt should be equal to expected amt ([3f57777](3f577779be))
* make free qty round on large transaction qty ([4856a96](4856a9633e))
* no permission to read Doctype (backport [#44256](https://github.com/frappe/erpnext/issues/44256)) ([#44258](https://github.com/frappe/erpnext/issues/44258)) ([8b15a96](8b15a965dd))
* not able to fetch batch item price ([97f2341](97f2341b98))
* patch ([#44191](https://github.com/frappe/erpnext/issues/44191)) ([8b02402](8b02402f62))
* reduce paid amount from grand total ([d0d97c2](d0d97c26a0))
* remove field precision in SO and PO for percentage fields ([860350a](860350a5b3))
* remove irrelavent filters ([7423d7d](7423d7d337))
* set debit transaction currency in gl entry ([a2612d5](a2612d5f36))
* set outstanding amount while creating payment request for invoices ([8d8027d](8d8027d423))
* set price_list_currency only if it exists ([8cd455b](8cd455b050))
* Show available stock qty in `stock_uom` instead of `uom` ([49dad1a](49dad1a456))
* show cc on the email ([67809c7](67809c781a))
* test case ([66af7f4](66af7f4a14))
* toggle debit credit amounts for transaction currency too; minor refactor ([7f8334f](7f8334f29a))
* unify company address query in sales transactions (backport [#44361](https://github.com/frappe/erpnext/issues/44361)) ([#44365](https://github.com/frappe/erpnext/issues/44365)) ([7e61aca](7e61aca512))
* update gross profit for returned invoices ([ca56709](ca56709295))
* use field precision instead of hardcoded precision in so and po ([cde1906](cde19066fe))

### Features

* available qty at company in sales transactions (backport [#44260](https://github.com/frappe/erpnext/issues/44260)) ([#44325](https://github.com/frappe/erpnext/issues/44325)) ([20d0e95](20d0e95d7c))
* provision to disable item attribute (backport [#44358](https://github.com/frappe/erpnext/issues/44358)) ([#44370](https://github.com/frappe/erpnext/issues/44370)) ([ef882de](ef882de509))
* Show Aggregate Value from Subsidiary Companies ([0469b0d](0469b0d1ec))

### Reverts

* use `+ flt(value)` instead of direct increment ([b65e16a](b65e16a91b))
2024-11-27 16:01:32 +00:00
ruthra kumar
9f3ae08e3b Merge pull request #44341 from frappe/version-15-hotfix
chore: release v15
2024-11-27 21:30:10 +05:30
ruthra kumar
1f683afa43 Merge pull request #44396 from frappe/mergify/bp/version-15-hotfix/pr-44346
fix: reduce paid amount from grand total (backport #44346)
2024-11-27 21:12:51 +05:30
ruthra kumar
0ab0b4f716 chore: resolve conflict 2024-11-27 20:52:14 +05:30
ruthra kumar
4be6a78691 Merge pull request #44394 from frappe/mergify/bp/version-15-hotfix/pr-44392
chore: Fix typo "Purchase Reecipt" (backport #44392)
2024-11-27 20:24:46 +05:30
Sugesh393
d0d97c26a0 fix: reduce paid amount from grand total
(cherry picked from commit 82907672d9)
2024-11-27 14:41:46 +00:00
Sugesh393
81b9832917 test: add unit test to validate outstanding amount in payment request
(cherry picked from commit bbe3bc95d0)

# Conflicts:
#	erpnext/accounts/doctype/payment_request/test_payment_request.py
2024-11-27 14:41:46 +00:00
Sugesh393
8d8027d423 fix: set outstanding amount while creating payment request for invoices
(cherry picked from commit 38e7d0a41e)
2024-11-27 14:41:44 +00:00
ruthra kumar
9a374ddbd4 Merge pull request #44395 from frappe/mergify/bp/version-15-hotfix/pr-44316
fix: Initially Closing Amount Should be Equal to Expected Amount (backport #44316)
2024-11-27 20:08:33 +05:30
Ninad1306
3f577779be fix: initially closing amt should be equal to expected amt
(cherry picked from commit af9524920b)
2024-11-27 14:28:47 +00:00
vimalraj27
fd1cbf4b6f chore: Fix typo "Purchase Reecipt"
(cherry picked from commit 21049bae91)
2024-11-27 14:28:00 +00:00
ruthra kumar
5e7cfeb514 Merge pull request #44388 from frappe/mergify/bp/version-15-hotfix/pr-44378
fix: filter item with search fields (backport #44378)
2024-11-27 17:53:51 +05:30
ruthra kumar
223d30ecd8 Merge pull request #44389 from frappe/mergify/bp/version-15-hotfix/pr-44327
fix: set debit transaction currency in gl entry (backport #44327)
2024-11-27 17:53:37 +05:30
Abdeali Chharchhoda
0e1f5ff391 fix: Add translation for showing mandatory fields in error msg
(cherry picked from commit f42ec6a124)
2024-11-27 12:01:10 +00:00
venkat102
a2612d5f36 fix: set debit transaction currency in gl entry
(cherry picked from commit 6e19c06e58)
2024-11-27 11:59:09 +00:00
ruthra kumar
632412bd72 Merge pull request #44383 from frappe/mergify/bp/version-15-hotfix/pr-44376
fix: remove field precision in Sales and Purchase Order for percentage fields (backport #44376)
2024-11-27 17:27:06 +05:30
ruthra kumar
09c28760e4 Merge pull request #44385 from frappe/mergify/bp/version-15-hotfix/pr-44323
fix: update gross profit for returned invoices (backport #44323)
2024-11-27 17:21:26 +05:30
ruthra kumar
7c85e4056f Merge pull request #44387 from frappe/mergify/bp/version-15-hotfix/pr-44359
fix: check difference with company currency (backport #44359)
2024-11-27 17:21:00 +05:30
venkat102
d073b005a8 fix: filter item with search fields
(cherry picked from commit ebfbee3da5)
2024-11-27 11:50:51 +00:00
ruthra kumar
45e41827c7 chore: resolve conflict 2024-11-27 17:20:36 +05:30
venkat102
d6ef43858c fix: check difference with company currency
(cherry picked from commit e2bae4cf07)
2024-11-27 11:26:58 +00:00
ljain112
66af7f4a14 fix: test case
(cherry picked from commit af5a3e5a48)
2024-11-27 11:20:34 +00:00
ljain112
f4518cac9a fix: gp for return invoice
(cherry picked from commit 00403515a8)
2024-11-27 11:20:34 +00:00
ljain112
ca56709295 fix: update gross profit for returned invoices
(cherry picked from commit 8a42601e99)
2024-11-27 11:20:34 +00:00
vishakhdesai
860350a5b3 fix: remove field precision in SO and PO for percentage fields
(cherry picked from commit eff9cd10cd)

# Conflicts:
#	erpnext/selling/doctype/sales_order/sales_order_list.js
2024-11-27 11:19:13 +00:00
ruthra kumar
6a34abefba Merge pull request #44379 from frappe/mergify/bp/version-15-hotfix/pr-44377
fix:  correct placeholder index in message (backport #44377)
2024-11-27 15:38:48 +05:30
ljain112
a523c14fd5 fix: correct placeholder index in message
(cherry picked from commit d61cb9a4bf)
2024-11-27 09:26:09 +00:00
ruthra kumar
6f798ab288 Merge pull request #44372 from frappe/mergify/bp/version-15-hotfix/pr-44343
fix: show cc on the process statement of accounts email (backport #44343)
2024-11-27 10:47:15 +05:30
mergify[bot]
ef882de509 feat: provision to disable item attribute (backport #44358) (#44370)
* feat: provision to disable item attribute (#44358)

(cherry picked from commit 123e3ef263)

# Conflicts:
#	erpnext/stock/doctype/item_attribute/item_attribute.json
#	erpnext/stock/doctype/item_variant_attribute/item_variant_attribute.json

* chore: fix conflicts

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-11-27 10:31:30 +05:30
venkat102
67809c781a fix: show cc on the email
(cherry picked from commit 2dd5699f6d)
2024-11-27 04:53:16 +00:00
Raffael Meyer
c20def5d59 chore: resolve conflicts 2024-11-26 21:12:39 +01:00
Raffael Meyer
e0cb5f9ba8 feat(Dunning): separate tab "Address & Contact" (#44363)
(cherry picked from commit e094473c65)

# Conflicts:
#	erpnext/accounts/doctype/dunning/dunning.json
2024-11-26 20:11:37 +00:00
mergify[bot]
7e61aca512 fix: unify company address query in sales transactions (backport #44361) (#44365)
fix: unify company address query in sales transactions (#44361)

* fix: unify company address query in sales transactions

* refactor: get the correct field label

(cherry picked from commit 3f92a57d63)

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2024-11-26 20:16:24 +01:00
mergify[bot]
89bd4eba46 fix: added validation for quality inspection (backport #44351) (#44357)
fix: added validation for quality inspection (#44351)

(cherry picked from commit 0fd50b5048)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-11-26 21:18:26 +05:30
mergify[bot]
ea0f24aa57 fix: billed qty and received amount in PO analysis report (backport #44349) (#44354)
fix: billed qty and received amount in PO analysis report (#44349)

(cherry picked from commit 2ab7ec5437)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-11-26 19:43:51 +05:30
ruthra kumar
069c763010 Merge pull request #44338 from frappe/mergify/bp/version-15-hotfix/pr-44319
fix: use field precision in Sales and Purchase Order (backport #44319)
2024-11-26 14:02:33 +05:30
ruthra kumar
bc93de682b chore: resolve conflict 2024-11-26 13:48:00 +05:30
ruthra kumar
8aa4779191 Merge pull request #44335 from frappe/mergify/bp/version-15-hotfix/pr-44304
chore: Add translations to QI validations in Update stock_controller.py (backport #44304)
2024-11-26 12:09:56 +05:30
vishakhdesai
cde19066fe fix: use field precision instead of hardcoded precision in so and po
(cherry picked from commit 1a1e2c7e01)

# Conflicts:
#	erpnext/buying/doctype/purchase_order/purchase_order.js
2024-11-26 06:18:33 +00:00
Ernesto Ruiz
465a26f714 chore: Add translations to QI validations in Update stock_controller.py
chore: Add translations to QI validations in Update stock_controller.py
(cherry picked from commit 6754f15487)
2024-11-26 06:07:15 +00:00
ruthra kumar
ca8e7e9891 Merge pull request #44332 from frappe/mergify/bp/version-15-hotfix/pr-44257
fix: add company dynamic filters in number cards (backport #44257)
2024-11-26 11:19:43 +05:30
Sugesh393
7423d7d337 fix: remove irrelavent filters
(cherry picked from commit 29762c4826)
2024-11-26 05:24:57 +00:00
Sugesh393
db21def58b fix: add company dynamic filters in number cards
(cherry picked from commit 4e7725de66)
2024-11-26 05:24:57 +00:00
ruthra kumar
6a4058052b Merge pull request #44331 from frappe/mergify/bp/version-15-hotfix/pr-44320
fix: Increase quantity by `1 UOM` when adding an item from the selector in `POS` (backport #44320)
2024-11-26 10:40:45 +05:30
Abdeali Chharchhoda
b65e16a91b revert: use + flt(value) instead of direct increment
(cherry picked from commit 112b4c705b)
2024-11-26 05:00:41 +00:00
Abdeali Chharchhoda
49dad1a456 fix: Show available stock qty in stock_uom instead of uom
(cherry picked from commit 84dcbe6639)
2024-11-26 05:00:40 +00:00
Abdeali Chharchhoda
0d3802873b fix: Increase quantity by 1 UOM when adding an item from the selector in POS
(cherry picked from commit bbab850135)
2024-11-26 05:00:40 +00:00
mergify[bot]
20d0e95d7c feat: available qty at company in sales transactions (backport #44260) (#44325)
* feat: available qty at company in sales transactions

(cherry picked from commit d8b9aef14f)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
#	erpnext/selling/doctype/quotation_item/quotation_item.json
#	erpnext/selling/doctype/sales_order_item/sales_order_item.json
#	erpnext/stock/doctype/delivery_note_item/delivery_note_item.json

* chore: fix conflicts

* chore: fix conflicts

* chore: fix conflicts

* chore: fix conflicts

---------

Co-authored-by: Rohit Waghchaure <rohitw1991@gmail.com>
2024-11-25 20:29:13 +05:30
ruthra kumar
d77a880a62 Merge pull request #44318 from frappe/mergify/bp/version-15-hotfix/pr-44274
fix: set price_list_currency only if it exists (backport #44274)
2024-11-25 12:57:02 +05:30
ruthra kumar
7ccb2ead09 Merge pull request #44317 from frappe/mergify/bp/version-15-hotfix/pr-44163
feat: Show Aggregate Value from Subsidiary Companies (backport #44163)
2024-11-25 12:56:37 +05:30
Sugesh393
8cd455b050 fix: set price_list_currency only if it exists
(cherry picked from commit f0b9cb4019)
2024-11-25 07:10:04 +00:00
ruthra kumar
072c5b7753 chore: resolve conflict 2024-11-25 12:28:02 +05:30
l0gesh29
0469b0d1ec feat: Show Aggregate Value from Subsidiary Companies
(cherry picked from commit c23af6af41)

# Conflicts:
#	erpnext/selling/report/sales_analytics/sales_analytics.js
2024-11-25 06:40:00 +00:00
rohitwaghchaure
74af7d01a2 Merge pull request #44312 from rohitwaghchaure/fixed-support-25969
fix: not able to fetch batch item price
2024-11-25 11:22:43 +05:30
ruthra kumar
c545de7bc6 Merge pull request #44315 from frappe/mergify/bp/version-15-hotfix/pr-44297
refactor: added translate function for some columns of report (backport #44297)
2024-11-25 10:28:02 +05:30
Abdeali Chharchhoda
8928e062b1 refactor: added translate function for some columns of report
(cherry picked from commit e545c913b5)
2024-11-25 04:39:36 +00:00
Rohit Waghchaure
97f2341b98 fix: not able to fetch batch item price 2024-11-24 22:23:11 +05:30
Smit Vora
aa2ae5e408 Merge pull request #44279 from frappe/mergify/bp/version-15-hotfix/pr-44263
fix: toggle debit credit amounts for transaction currency too; minor refactor (backport #44263)
2024-11-22 18:38:26 +05:30
ruthra kumar
6d10ccfc15 Merge pull request #44287 from frappe/mergify/bp/version-15-hotfix/pr-44246
fix: Get submitted documents in validate_for_closed_fiscal_year (backport #44246)
2024-11-22 16:15:48 +05:30
ruthra kumar
dc99e74ae3 Merge pull request #44285 from frappe/mergify/bp/version-15-hotfix/pr-44266
fix: make free qty round on large transaction qty (backport #44266)
2024-11-22 16:15:35 +05:30
ruthra kumar
fd9aa288c4 Merge pull request #44283 from frappe/mergify/bp/version-15-hotfix/pr-44277
fix: filter with item group only if it is mentioned in pos profile (backport #44277)
2024-11-22 16:15:08 +05:30
vimalraj27
af74a3c32f fix: Get submitted documents in validate_for_closed_fiscal_year
(cherry picked from commit c607e5f940)
2024-11-22 10:23:11 +00:00
venkat102
db1bc8a3db test: add unit test to validate free qty round on large transaction qty
(cherry picked from commit 013a6fc6ec)
2024-11-22 10:20:57 +00:00
venkat102
4856a9633e fix: make free qty round on large transaction qty
(cherry picked from commit f9b8165385)
2024-11-22 10:20:56 +00:00
venkat102
a4398626f6 fix: filter with item group only if it is mentioned in pos profile
(cherry picked from commit 09641073e3)
2024-11-22 10:15:50 +00:00
Frappe PR Bot
17e00b397f chore(release): Bumped to Version 15.43.3
## [15.43.3](https://github.com/frappe/erpnext/compare/v15.43.2...v15.43.3) (2024-11-22)

### Bug Fixes

* patch ([#44191](https://github.com/frappe/erpnext/issues/44191)) ([5f752e2](5f752e29f9))
2024-11-22 09:19:30 +00:00
rohitwaghchaure
1261513ab2 Merge pull request #44280 from frappe/mergify/bp/version-15/pr-44275
fix: patch (backport #44191) (backport #44275)
2024-11-22 14:48:13 +05:30
rohitwaghchaure
5f752e29f9 fix: patch (#44191)
(cherry picked from commit 495528a758)
(cherry picked from commit 8b02402f62)
2024-11-22 06:48:01 +00:00
rohitwaghchaure
c0e6f3f4df Merge pull request #44275 from frappe/mergify/bp/version-15-hotfix/pr-44191
fix: patch (backport #44191)
2024-11-22 12:17:11 +05:30
Smit Vora
11deff98d9 test: test case for toggling debit and credit if negative
(cherry picked from commit a10e175bc9)
2024-11-22 06:39:56 +00:00
Smit Vora
7f8334f29a fix: toggle debit credit amounts for transaction currency too; minor refactor
(cherry picked from commit 8e759c32c4)
2024-11-22 06:39:56 +00:00
rohitwaghchaure
8b02402f62 fix: patch (#44191)
(cherry picked from commit 495528a758)
2024-11-22 06:23:24 +00:00
Frappe PR Bot
81f1f1f1bb chore(release): Bumped to Version 15.43.2
## [15.43.2](https://github.com/frappe/erpnext/compare/v15.43.1...v15.43.2) (2024-11-22)

### Bug Fixes

* include current invoice amount when tax_on_excess_amount is checked ([52e1551](52e1551c23))
2024-11-22 06:00:44 +00:00
ruthra kumar
16ffee9992 Merge pull request #44273 from frappe/mergify/bp/version-15/pr-44194
fix: include current invoice amount when tax_on_excess_amount is checked (backport #44194)
2024-11-22 11:29:29 +05:30
ruthra kumar
723d10241b Merge pull request #44272 from frappe/mergify/bp/version-15-hotfix/pr-44194
fix: include current invoice amount when tax_on_excess_amount is checked (backport #44194)
2024-11-22 11:25:35 +05:30
venkat102
2d284de426 test: add unit test for tax on excess amount
(cherry picked from commit 4820273595)
2024-11-22 05:38:25 +00:00
venkat102
52e1551c23 fix: include current invoice amount when tax_on_excess_amount is checked
(cherry picked from commit b74f2896cd)
2024-11-22 05:38:25 +00:00
venkat102
08b896fc2c test: add unit test for tax on excess amount
(cherry picked from commit 4820273595)
2024-11-22 05:37:50 +00:00
venkat102
0ffeb9f6ad fix: include current invoice amount when tax_on_excess_amount is checked
(cherry picked from commit b74f2896cd)
2024-11-22 05:37:50 +00:00
Frappe PR Bot
e0060f8ffe chore(release): Bumped to Version 15.43.1
## [15.43.1](https://github.com/frappe/erpnext/compare/v15.43.0...v15.43.1) (2024-11-21)

### Bug Fixes

* no permission to read Doctype (backport [#44256](https://github.com/frappe/erpnext/issues/44256)) ([#44258](https://github.com/frappe/erpnext/issues/44258)) ([b047425](b047425a6f))
2024-11-21 09:44:42 +00:00
rohitwaghchaure
3518991ff0 Merge pull request #44259 from frappe/mergify/bp/version-15/pr-44258
fix: no permission to read Doctype (backport #44256) (backport #44258)
2024-11-21 15:13:16 +05:30
mergify[bot]
b047425a6f fix: no permission to read Doctype (backport #44256) (#44258)
fix: no permission to read Doctype (#44256)

(cherry picked from commit 57293aa18a)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
(cherry picked from commit 8b15a965dd)
2024-11-21 09:22:50 +00:00
mergify[bot]
d4f0512a10 fix: added Stock UOM field for RM in work order (backport #44185) (#44237)
* 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/patches.txt

* chore: fix conflicts

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-11-21 14:52:37 +05:30
mergify[bot]
8b15a965dd fix: no permission to read Doctype (backport #44256) (#44258)
fix: no permission to read Doctype (#44256)

(cherry picked from commit 57293aa18a)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-11-21 14:51:33 +05:30
Frappe PR Bot
ccf99cf985 chore(release): Bumped to Version 15.43.0
# [15.43.0](https://github.com/frappe/erpnext/compare/v15.42.0...v15.43.0) (2024-11-20)

### Bug Fixes

* added disable_rounded_total field ([c98a0cc](c98a0ccd1d))
* added test cases ([234741f](234741f35f))
* apply posting date sorting to invoices in Payment Reconciliation similar to payments ([41c8cfa](41c8cfac73))
* broken apply on other item pricing rule ([5d6451f](5d6451fca7))
* broken UI on currency exchange ([f460391](f4603910e4))
* bulk update invoice remarks during site upgrade ([cc07402](cc07402b5e)), closes [#43634](https://github.com/frappe/erpnext/issues/43634)
* check if pricing rule matches with coupon code ([#44104](https://github.com/frappe/erpnext/issues/44104)) ([6089661](608966158a))
* correctly set 'cannot_add_rows' property on allocations table field ([c59a778](c59a778503))
* disable conversion to user tz for sales order calender ([83b9680](83b9680318))
* Get Entries not showing accounts with no gain or loss in Exchange Rate Revaluation issue ([1fe5342](1fe534290d))
* linters ([381101f](381101f552))
* non group pos warehouse ([4335659](4335659905))
* payment reco for jv with negative dr or cr amount ([7483839](7483839418))
* remove trailing whitespace ([5bd633b](5bd633b40f))
* remove validate_name_in_customer function ([6bff9d3](6bff9d39e3))
* set conversion factor before applying price list ([5848de7](5848de76ea))
* set debit in transaction currency in GL Entry ([c0d3f8c](c0d3f8cbbe))
* set default party type in Payment Entry ([08f6cee](08f6ceeb50))
* **setup:** Fix typo in COA setup ([7abcfca](7abcfca1cb))
* stock ledger variance report filter options (backport [#44137](https://github.com/frappe/erpnext/issues/44137)) ([#44150](https://github.com/frappe/erpnext/issues/44150)) ([b6fe1f5](b6fe1f5842))
* update project cost from timesheet (backport [#44211](https://github.com/frappe/erpnext/issues/44211)) ([#44212](https://github.com/frappe/erpnext/issues/44212)) ([ad0c655](ad0c65500a))
* validate sales team to ensure all sales person are enabled ([f3c3f17](f3c3f170a7))
* validation for serial no (backport [#44133](https://github.com/frappe/erpnext/issues/44133)) ([#44151](https://github.com/frappe/erpnext/issues/44151)) ([725d107](725d107288))

### Features

* inventory dimension for rejected materials (backport [#44156](https://github.com/frappe/erpnext/issues/44156)) ([#44165](https://github.com/frappe/erpnext/issues/44165)) ([d61f696](d61f696f85))
* new DocTypes "Code List" and "Common Code" (backport [#43425](https://github.com/frappe/erpnext/issues/43425)) ([#44173](https://github.com/frappe/erpnext/issues/44173)) ([b130e20](b130e2065b))
* round off for opening entries ([8e6249d](8e6249d361))
2024-11-20 08:41:13 +00:00
ruthra kumar
9b690e9ae6 Merge pull request #44209 from frappe/version-15-hotfix
chore: release v15
2024-11-20 14:09:56 +05:30
ruthra kumar
d0e2b7c341 Merge pull request #44183 from frappe/mergify/bp/version-15-hotfix/pr-44025
fix: `Disable Rounded Total` in Quotation DocType (backport #44025)
2024-11-20 13:14:16 +05:30
ruthra kumar
4afd4b4044 Merge pull request #44245 from frappe/mergify/bp/version-15-hotfix/pr-44197
fix: payment reco for jv with negative dr or cr amount (backport #44197)
2024-11-20 13:12:44 +05:30
ruthra kumar
80f0d5b5ec chore: resolve conflict 2024-11-20 12:51:29 +05:30
ljain112
234741f35f fix: added test cases
(cherry picked from commit 6f9ea6422d)
2024-11-20 07:18:27 +00:00
ljain112
7483839418 fix: payment reco for jv with negative dr or cr amount
(cherry picked from commit fee79b9445)
2024-11-20 07:18:26 +00:00
ruthra kumar
9e7e6041ed Merge pull request #44242 from frappe/mergify/bp/version-15-hotfix/pr-44240
fix: non group pos warehouse (backport #44240)
2024-11-20 12:18:02 +05:30
ruthra kumar
9ce1c25c04 Merge pull request #44239 from frappe/mergify/bp/version-15-hotfix/pr-44220
refactor: Update `Payment Request` search query in PE's reference (backport #44220)
2024-11-20 12:16:07 +05:30
Nihantra C. Patel
4335659905 fix: non group pos warehouse
(cherry picked from commit d526be0394)
2024-11-20 06:41:56 +00:00
Abdeali Chharchhoda
514fe69b65 refactor: Update Payment Request search query in PE's reference
(cherry picked from commit 4ab3499a17)
2024-11-20 06:22:17 +00:00
ruthra kumar
a0ea68499b Merge pull request #44233 from frappe/mergify/bp/version-15-hotfix/pr-44207
fix: validate sales team to ensure all sales person are enabled (backport #44207)
2024-11-20 11:51:33 +05:30
ruthra kumar
76d6dd346c Merge pull request #44235 from frappe/mergify/bp/version-15-hotfix/pr-44203
fix: disable conversion to user tz for sales order calender (backport #44203)
2024-11-20 11:51:09 +05:30
ljain112
83b9680318 fix: disable conversion to user tz for sales order calender
(cherry picked from commit cdf098c193)
2024-11-20 05:17:57 +00:00
ljain112
f3c3f170a7 fix: validate sales team to ensure all sales person are enabled
(cherry picked from commit 548dbb33eb)
2024-11-20 05:11:41 +00:00
ruthra kumar
bc03b68b13 Merge pull request #44221 from frappe/mergify/bp/version-15-hotfix/pr-41025
fix: remove validate_name_in_customer function (backport #41025)
2024-11-20 10:19:28 +05:30
mergify[bot]
ad0c65500a fix: update project cost from timesheet (backport #44211) (#44212)
fix: update project cost from timesheet (#44211)

(cherry picked from commit b21fb8f8b6)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-11-19 22:59:16 +05:30
RitvikSardana
6bff9d39e3 fix: remove validate_name_in_customer function
(cherry picked from commit 2b32d3644f)
2024-11-19 14:22:34 +00:00
ruthra kumar
704452a9fa Merge pull request #44218 from frappe/mergify/bp/version-15-hotfix/pr-44190
fix(setup): Fix typo in COA setup (backport #44190)
2024-11-19 19:51:41 +05:30
Corentin Forler
7abcfca1cb fix(setup): Fix typo in COA setup
(cherry picked from commit a245cc6b07)
2024-11-19 12:15:15 +00:00
ruthra kumar
610c483d83 Merge pull request #44217 from frappe/mergify/bp/version-15-hotfix/pr-44104
fix: check if pricing rule matches with coupon code (backport #44104)
2024-11-19 17:37:55 +05:30
Nikolas Beckel
608966158a 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:42:04 +00:00
ruthra kumar
efdbe93cf0 Merge pull request #44206 from frappe/mergify/bp/version-15-hotfix/pr-44145
fix: updated label "Is short year" to "Is Short/Long year" for both short and long fiscal years (backport #44145)
2024-11-19 14:47:19 +05:30
ajiragroup
c2748e923e refactor: update label and description on short year checkbox
Is short/long year.

(cherry picked from commit 1d6b9b405f)
2024-11-19 08:56:54 +00:00
ruthra kumar
a17e1f6b6d Merge pull request #44202 from frappe/mergify/bp/version-15-hotfix/pr-44188
chore: update oldest_items.json, change owner back to administrator (backport #44188)
2024-11-19 12:30:49 +05:30
Ismail Arif
0ea6691189 chore: update oldest_items.json, change owner back to administrator
Signed-off-by: Ismail Arif <38789073+ismxilxrif@users.noreply.github.com>
(cherry picked from commit 7ceb24fb4c)
2024-11-19 06:33:18 +00:00
ruthra kumar
eca43916f0 Merge pull request #44193 from frappe/mergify/bp/version-15-hotfix/pr-44134
fix: set debit in transaction currency in GL Entry (backport #44134)
2024-11-19 11:58:14 +05:30
ruthra kumar
8cc59e3be7 refactor: update test case
(cherry picked from commit 4aab6f55f5)
2024-11-19 11:40:37 +05:30
mergify[bot]
b130e2065b feat: new DocTypes "Code List" and "Common Code" (backport #43425) (#44173)
Co-authored-by: David <dgx.arnold@gmail.com>
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2024-11-18 21:08:21 +01:00
ruthra kumar
d7deed6c45 refactor: assume any of the foreign currency as transaction currency
On a foreign currency payment entry, assume any one of the foreign
currency as the transaction currency

(cherry picked from commit 6681882bd8)
2024-11-18 11:42:49 +00:00
sudarsan2001
7cc31df587 chore: change account name
(cherry picked from commit 4a1cd5a8d6)
2024-11-18 11:42:49 +00:00
sudarsan2001
c30a17cd7a test: add unit test to validate gl values
(cherry picked from commit e8b8a589be)
2024-11-18 11:42:49 +00:00
sudarsan2001
c0d3f8cbbe fix: set debit in transaction currency in GL Entry
(cherry picked from commit 29a6eb21a3)
2024-11-18 11:42:49 +00:00
Ninad1306
b6524946bc test: test to validate rounded total
(cherry picked from commit 5a6261d3b4)
2024-11-18 06:00:12 +00:00
Ninad1306
c98a0ccd1d fix: added disable_rounded_total field
(cherry picked from commit f8524d526b)

# Conflicts:
#	erpnext/selling/doctype/quotation/quotation.json
2024-11-18 06:00:12 +00:00
ruthra kumar
73a31cb395 Merge pull request #44182 from frappe/mergify/bp/version-15-hotfix/pr-44127
fix: set default Party Type based on Payment Type in Payment Entry (backport #44127)
2024-11-18 10:49:24 +05:30
vishakhdesai
08f6ceeb50 fix: set default party type in Payment Entry
(cherry picked from commit 19222690d3)
2024-11-18 04:44:58 +00:00
ruthra kumar
f04a934ed1 Merge pull request #44180 from frappe/mergify/bp/version-15-hotfix/pr-44147
fix: set conversion factor before applying price list (backport #44147)
2024-11-18 10:14:45 +05:30
ruthra kumar
c6bfaa41be Merge pull request #44178 from frappe/mergify/bp/version-15-hotfix/pr-44157
fix: apply "cannot_add_rows" directly to table field for more efficient solution (backport #44157)
2024-11-18 10:14:23 +05:30
vishakhdesai
5848de76ea fix: set conversion factor before applying price list
(cherry picked from commit 9749fe23cc)
2024-11-18 04:38:37 +00:00
UmakanthKaspa
2a54cd5004 refactor: set 'cannot_add_rows' directly in the allocations table field (optimized approach)
(cherry picked from commit 5dd8eafdfc)
2024-11-18 04:36:19 +00:00
mergify[bot]
d61f696f85 feat: inventory dimension for rejected materials (backport #44156) (#44165)
feat: inventory dimension for rejected materials (#44156)

(cherry picked from commit 9bf16df41e)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-11-15 17:28:41 +05:30
mergify[bot]
725d107288 fix: validation for serial no (backport #44133) (#44151)
* fix: validation for serial no (#44133)

(cherry picked from commit 93c8b4c39a)

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

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-11-15 16:55:32 +05:30
mergify[bot]
b6fe1f5842 fix: stock ledger variance report filter options (backport #44137) (#44150)
fix: stock ledger variance report filter options (#44137)

(cherry picked from commit e8bbf6492f)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-11-15 15:04:48 +05:30
ruthra kumar
5b3f0825af Merge pull request #44162 from frappe/mergify/bp/version-15-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:45 +05:30
Vishakh Desai
381101f552 fix: linters
(cherry picked from commit 9cc22b4cac)
2024-11-15 07:19:01 +00:00
Vishakh Desai
1fe534290d 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:19:01 +00:00
ruthra kumar
5004b8fbc9 Merge pull request #44160 from frappe/mergify/bp/version-15-hotfix/pr-44158
fix: broken UI on currency exchange (backport #44158)
2024-11-15 12:29:32 +05:30
ruthra kumar
f4603910e4 fix: broken UI on currency exchange
(cherry picked from commit e91b65e7bd)
2024-11-15 06:52:46 +00:00
ruthra kumar
50d15249fc Merge pull request #44155 from frappe/mergify/bp/version-15-hotfix/pr-44089
fix: apply posting date sorting to invoices in Payment Reconciliation similar to payments (backport #44089)
2024-11-15 10:49:12 +05:30
UmakanthKaspa
5bd633b40f fix: remove trailing whitespace
(cherry picked from commit d6703eb88b)
2024-11-15 05:00:07 +00:00
UmakanthKaspa
41c8cfac73 fix: apply posting date sorting to invoices in Payment Reconciliation similar to payments
(cherry picked from commit 0bd83d920d)
2024-11-15 05:00:06 +00:00
ruthra kumar
68f3dd848a Merge pull request #44153 from frappe/mergify/bp/version-15-hotfix/pr-44148
Fix: Disable "Add Row" button in allocations table during UnReconcile process (backport #44148)
2024-11-15 10:24:16 +05:30
UmakanthKaspa
c59a778503 fix: correctly set 'cannot_add_rows' property on allocations table field
(cherry picked from commit 13ca2700f8)
2024-11-15 04:47:53 +00:00
ruthra kumar
8ee7e7d828 Merge pull request #44140 from frappe/mergify/bp/version-15-hotfix/pr-43189
fix: broken apply on other item (backport #43189)
2024-11-14 13:45:29 +05:30
ruthra kumar
5d6451fca7 fix: broken apply on other item pricing rule
(cherry picked from commit e5119a749c)
2024-11-14 07:54:15 +00:00
ruthra kumar
90b7ce2dd6 Merge pull request #44136 from frappe/mergify/bp/version-15-hotfix/pr-42588
refactor: separate round off account for opening (backport #42588)
2024-11-14 13:04:13 +05:30
ruthra kumar
7b77128aab Merge pull request #43651 from vv-varun/erpnext_issue_43634
fix: bulk update invoice remarks during site upgrade
2024-11-14 12:48:50 +05:30
ruthra kumar
9598b1fc0f chore: resolve conflicts 2024-11-14 12:36:56 +05:30
ruthra kumar
ba79560c0c refactor(test): filter for active ledger entries
(cherry picked from commit cf11ac87fb)
2024-11-14 07:01:54 +00:00
ruthra kumar
9bfd5cdb2b test: opening purchase invoice with rounding adjustment
(cherry picked from commit b7edc6dea9)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
2024-11-14 07:01:54 +00:00
ruthra kumar
8bec67cbcf refactor: handle opening round off on purchase invoice
(cherry picked from commit a5d6a25a96)
2024-11-14 07:01:53 +00:00
ruthra kumar
7eb4b42280 refactor: filter on account_type
(cherry picked from commit 193ea9ad8f)
2024-11-14 07:01:53 +00:00
ruthra kumar
da2f6a045a test: opening round off with inclusive tax
(cherry picked from commit 79267358d0)
2024-11-14 07:01:53 +00:00
ruthra kumar
820692f246 test: rounding adjustment validation and posting
(cherry picked from commit 5021c7ca2c)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
2024-11-14 07:01:53 +00:00
ruthra kumar
186b646dee refactor: handle opening round off from sales invoice
(cherry picked from commit 96e3c2ad10)
2024-11-14 07:01:52 +00:00
ruthra kumar
b28ff25180 chore: default should return 3 elements
(cherry picked from commit fc46ebcd7c)
2024-11-14 07:01:52 +00:00
ruthra kumar
9a3e9c4c9a refactor: use separate round off for opening entries
(cherry picked from commit 88e68168e3)

# Conflicts:
#	erpnext/accounts/general_ledger.py
2024-11-14 07:01:52 +00:00
ruthra kumar
8e6249d361 feat: round off for opening entries
(cherry picked from commit a5b228549c)
2024-11-14 07:01:51 +00:00
Varun Verma
cc07402b5e fix: bulk update invoice remarks during site upgrade
fixes issue #43634
2024-11-14 12:01:16 +05:30
Frappe PR Bot
7c78e0025d chore(release): Bumped to Version 15.42.0
# [15.42.0](https://github.com/frappe/erpnext/compare/v15.41.2...v15.42.0) (2024-11-13)

### Bug Fixes

* add default height to POS item card selector (backport [#44071](https://github.com/frappe/erpnext/issues/44071)) ([#44075](https://github.com/frappe/erpnext/issues/44075)) ([65ec7c5](65ec7c5604))
* add field conversion_factor when include_uom is settled ([#43701](https://github.com/frappe/erpnext/issues/43701)) ([f387a8f](f387a8fceb))
* better gls for purchases with tax witholding ([#42743](https://github.com/frappe/erpnext/issues/42743)) ([705a26a](705a26a2fa))
* bind this object explicitly on callback event function ([3423d3c](3423d3c13d))
* calculate percentage received and delivered considering over-receipt and over-delivery (backport [#43870](https://github.com/frappe/erpnext/issues/43870)) ([#44030](https://github.com/frappe/erpnext/issues/44030)) ([5958d0c](5958d0c257))
* Cannot read properties of undefined (reading 'work_order_closed') (backport [#44117](https://github.com/frappe/erpnext/issues/44117)) ([#44122](https://github.com/frappe/erpnext/issues/44122)) ([c1983a4](c1983a4846))
* consider service item cost in the RM cost of the BOM (backport [#43962](https://github.com/frappe/erpnext/issues/43962)) ([#44111](https://github.com/frappe/erpnext/issues/44111)) ([6e83fec](6e83fec5ca))
* Drop Shipping address based on customer shopping address ([8af005c](8af005cef0))
* duplicate items and outdated item price in POS (backport [#42978](https://github.com/frappe/erpnext/issues/42978)) ([#44038](https://github.com/frappe/erpnext/issues/44038)) ([4cde77d](4cde77d8d8))
* exception on register reports when filtered on cost center ([be07421](be07421ab7))
* improved the conditions for determining voucher subtypes ([58ca4a2](58ca4a2b99))
* incorrect produced qty in Production Plan Summary (backport [#44112](https://github.com/frappe/erpnext/issues/44112)) ([#44113](https://github.com/frappe/erpnext/issues/44113)) ([bce7acf](bce7acf9cc))
* item mapping from modal to batch form ([#44090](https://github.com/frappe/erpnext/issues/44090)) ([9ac54f6](9ac54f694c))
* item not set in the batch quick entry form (backport [#44028](https://github.com/frappe/erpnext/issues/44028)) ([#44031](https://github.com/frappe/erpnext/issues/44031)) ([6dcd015](6dcd015a39))
* Negative stock validation against inventory dimension (backport [#43834](https://github.com/frappe/erpnext/issues/43834)) ([#43846](https://github.com/frappe/erpnext/issues/43846)) ([b314f38](b314f3839b))
* NoneType while updating ordered_qty in SO for removed items ([978a007](978a0078d8))
* not able to cancel DN (backport [#44108](https://github.com/frappe/erpnext/issues/44108)) ([#44109](https://github.com/frappe/erpnext/issues/44109)) ([290bdde](290bddea77))
* not able to reconcile expired batches ([#44012](https://github.com/frappe/erpnext/issues/44012)) ([4ba07a4](4ba07a40eb))
* patch ([107d53b](107d53b358))
* populate payment schedule from payment terms (backport [#44082](https://github.com/frappe/erpnext/issues/44082)) ([#44083](https://github.com/frappe/erpnext/issues/44083)) ([363f151](363f15124e))
* purchase receipt creation from SCR ([#44095](https://github.com/frappe/erpnext/issues/44095)) ([e3d7468](e3d74684d5))
* slow reposting due to SABB update ([3e29ae8](3e29ae8534))
* sort by ascending to get the first period closing voucher (backport [#44029](https://github.com/frappe/erpnext/issues/44029)) ([#44035](https://github.com/frappe/erpnext/issues/44035)) ([56f25ae](56f25ae065))
* task path (backport [#44073](https://github.com/frappe/erpnext/issues/44073)) ([#44078](https://github.com/frappe/erpnext/issues/44078)) ([34b5639](34b5639d1c))
* tyeerror while saving pick list ([7d09832](7d098328d0))
* update payment amount for partial pos return ([61559be](61559be8a4))
* update per_billed value in Purchase Receipt while creating Debit Note ([#43977](https://github.com/frappe/erpnext/issues/43977)) ([a833dd6](a833dd67f3))
* when company is created with other company template Chart of Account  the Create Taxe Template failed ([#42755](https://github.com/frappe/erpnext/issues/42755)) ([e6894b9](e6894b949c))

### Features

* Add item group filtering for search results ([2754793](2754793ff9))
* add template taxe for charts of account France - Plan Comptable General avec code ([#42757](https://github.com/frappe/erpnext/issues/42757)) ([865786e](865786e0b6))
2024-11-13 15:02:57 +00:00
ruthra kumar
d8d8330123 Merge pull request #44103 from frappe/version-15-hotfix
chore: release v15
2024-11-13 20:31:36 +05:30
ruthra kumar
825571ac98 Merge pull request #44115 from frappe/mergify/bp/version-15-hotfix/pr-43977
fix: update per_billed value in Purchase Receipt while creating Debit Note (backport #43977)
2024-11-13 19:01:53 +05:30
ruthra kumar
836a05d07f Merge pull request #44125 from frappe/mergify/copy/version-15-hotfix/pr-44124
refactor: 'Partly Billed' status for Purchase Receipt (copy #44124)
2024-11-13 17:22:36 +05:30
ruthra kumar
b9ec43c354 refactor: 'Partly Billed' status for Purchase Receipt
(cherry picked from commit c58bbd25f2)
2024-11-13 11:28:52 +00:00
mergify[bot]
c1983a4846 fix: Cannot read properties of undefined (reading 'work_order_closed') (backport #44117) (#44122)
fix: Cannot read properties of undefined (reading 'work_order_closed') (#44117)

(cherry picked from commit 13834014b5)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-11-13 16:24:12 +05:30
Nihantra C. Patel
b65b57c054 Merge pull request #44120 from frappe/mergify/bp/version-15-hotfix/pr-44116
fix: Drop Shipping address based on customer shopping address (backport #44116)
2024-11-13 15:51:28 +05:30
Nihantra Patel
8af005cef0 fix: Drop Shipping address based on customer shopping address
(cherry picked from commit c7499f3528)
2024-11-13 10:02:20 +00:00
mergify[bot]
bce7acf9cc fix: incorrect produced qty in Production Plan Summary (backport #44112) (#44113)
fix: incorrect produced qty in Production Plan Summary (#44112)

(cherry picked from commit 0828c74fe3)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-11-13 12:31:31 +05:30
NaviN
a833dd67f3 fix: update per_billed value in Purchase Receipt while creating Debit Note (#43977)
* fix: update per_billed value in Purchase Receipt while creating Debit Note

* test: add unit test for validating per_billed value for partial Debit Note

(cherry picked from commit 494fd7ceea)
2024-11-13 06:54:54 +00:00
mergify[bot]
6e83fec5ca fix: consider service item cost in the RM cost of the BOM (backport #43962) (#44111)
fix: consider service item cost in the RM cost of the BOM (#43962)

(cherry picked from commit c0ffaa444c)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-11-12 23:22:18 +05:30
mergify[bot]
290bddea77 fix: not able to cancel DN (backport #44108) (#44109)
fix: not able to cancel DN (#44108)

(cherry picked from commit e8882718c9)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-11-12 18:12:13 +05:30
ruthra kumar
5fea0b5525 Merge pull request #44107 from frappe/mergify/bp/version-15-hotfix/pr-44012
fix: not able to reconcile expired batches (backport #44012)
2024-11-12 16:34:15 +05:30
ruthra kumar
c2f7615eeb Merge pull request #44106 from frappe/mergify/bp/version-15-hotfix/pr-44095
fix: purchase receipt creation from SCR (backport #44095)
2024-11-12 16:33:45 +05:30
rohitwaghchaure
4ba07a40eb fix: not able to reconcile expired batches (#44012)
(cherry picked from commit 8805e74784)
2024-11-12 10:19:19 +00:00
rohitwaghchaure
e3d74684d5 fix: purchase receipt creation from SCR (#44095)
(cherry picked from commit 774845f886)
2024-11-12 10:14:17 +00:00
ruthra kumar
73661ac633 Merge pull request #44100 from frappe/mergify/bp/version-15-hotfix/pr-44053
Pos barcode search fix (backport #44053)
2024-11-12 13:54:03 +05:30
ruthra kumar
55f7f63e6e refactor: simpler filtering
(cherry picked from commit f072b1266e)
2024-11-12 07:16:09 +00:00
Bhavan23
aca1577040 refactor: Relocate doc variable for better scope management
(cherry picked from commit 488b60fc27)
2024-11-12 07:16:08 +00:00
Bhavan23
2754793ff9 feat: Add item group filtering for search results
(cherry picked from commit 5e7cf3899b)
2024-11-12 07:16:08 +00:00
ruthra kumar
5e196b9f8b Merge pull request #44098 from frappe/mergify/bp/version-15-hotfix/pr-44065
fix: update payment amount for partial pos return (backport #44065)
2024-11-12 10:10:58 +05:30
Kavin
61559be8a4 fix: update payment amount for partial pos return
(cherry picked from commit 53ef6336b6)
2024-11-12 04:20:31 +00:00
ruthra kumar
07dcf3fac2 Merge pull request #44040 from ruthra-kumar/manual_backport_pr_39481
fix: specify precision for net_amount (backport #39481)
2024-11-11 17:11:11 +05:30
ruthra kumar
471781a47e Merge pull request #44088 from frappe/mergify/bp/version-15-hotfix/pr-43695
fix: exception on register reports when filtered on cost center (backport #43695)
2024-11-11 17:00:23 +05:30
ruthra kumar
0afe893a83 Merge pull request #44093 from frappe/mergify/bp/version-15-hotfix/pr-44072
fix: bind this object explicitly on callback event function (backport #44072)
2024-11-11 17:00:10 +05:30
ruthra kumar
92551751bf Merge pull request #44063 from frappe/mergify/bp/version-15-hotfix/pr-42757
feat: add template taxe for charts of account France - Plan Comptable General avec code (backport #42757)
2024-11-11 16:53:15 +05:30
ruthra kumar
c8682d33d0 Merge pull request #44062 from frappe/mergify/bp/version-15-hotfix/pr-42755
fix: when company is created with other company template Chart of Account  the Create Taxe Template failed (backport #42755)
2024-11-11 16:52:54 +05:30
Kavin
3423d3c13d fix: bind this object explicitly on callback event function
(cherry picked from commit 5e790a0fce)
2024-11-11 11:21:09 +00:00
ruthra kumar
f3ee439b33 Merge pull request #44091 from frappe/mergify/bp/version-15-hotfix/pr-44090
fix: item mapping from modal to batch form (backport #44090)
2024-11-11 16:50:18 +05:30
ruthra kumar
ba6e068abc refactor(test): update tests for new rounding logic 2024-11-11 16:47:38 +05:30
ruthra kumar
34f64f02bf Merge pull request #44092 from frappe/mergify/bp/version-15-hotfix/pr-43701
fix: add field conversion_factor when include_uom is settled (backport #43701)
2024-11-11 16:45:25 +05:30
ruthra kumar
762f3bac65 chore: filter report output on document name 2024-11-11 16:42:13 +05:30
mergify[bot]
b314f3839b fix: Negative stock validation against inventory dimension (backport #43834) (#43846)
fix: Negative stock validation against inventory dimension (#43834)

(cherry picked from commit c330a292d2)

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2024-11-11 16:12:01 +05:30
HarryPaulo
f387a8fceb fix: add field conversion_factor when include_uom is settled (#43701)
(cherry picked from commit cfe6af1f68)
2024-11-11 10:41:40 +00:00
rohitwaghchaure
9ac54f694c fix: item mapping from modal to batch form (#44090)
(cherry picked from commit 9223ef2f37)
2024-11-11 10:38:42 +00:00
ruthra kumar
2bce735300 chore: use FrappeTestCase 2024-11-11 15:06:55 +05:30
ruthra kumar
2de9292ac0 refactor(test): assertion refactoring and exact decimals
(cherry picked from commit 1d11131afe)
2024-11-11 09:18:14 +00:00
ruthra kumar
d0e5568010 refactor(test): pass all mandatory fields
(cherry picked from commit c53e9637dd)
2024-11-11 09:18:14 +00:00
ruthra kumar
9724cefce8 refactor(test): fix incorrect assertion
(cherry picked from commit d6030e7112)
2024-11-11 09:18:14 +00:00
ruthra kumar
2affa60ea9 test: journals with cost center
(cherry picked from commit c255f34eea)
2024-11-11 09:18:14 +00:00
ruthra kumar
2183b99330 test: basic report output
(cherry picked from commit 657201b324)
2024-11-11 09:18:13 +00:00
Vishv-silveroak
be07421ab7 fix: exception on register reports when filtered on cost center
1

(cherry picked from commit f01e1a8e20)
2024-11-11 09:18:13 +00:00
mergify[bot]
363f15124e fix: populate payment schedule from payment terms (backport #44082) (#44083)
fix: populate payment schedule from payment terms (#44082)

(cherry picked from commit c81eb6c824)

Co-authored-by: Nihantra C. Patel <141945075+Nihantra-Patel@users.noreply.github.com>
2024-11-11 13:12:30 +05:30
mergify[bot]
34b5639d1c fix: task path (backport #44073) (#44078)
fix: task path (#44073)

(cherry picked from commit 8c99acb1b9)

Co-authored-by: Nihantra C. Patel <141945075+Nihantra-Patel@users.noreply.github.com>
2024-11-11 13:00:27 +05:30
mergify[bot]
4cfeb79355 chore: update CODEOWNERS (backport #44074) (#44081)
chore: update `CODEOWNERS` (#44074)

(cherry picked from commit 9a758ea826)

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2024-11-11 12:51:48 +05:30
mergify[bot]
65ec7c5604 fix: add default height to POS item card selector (backport #44071) (#44075)
fix: add default height to POS item card selector

(cherry picked from commit 5f5a514d6f)

Co-authored-by: Kavin <78342682+kavin0411@users.noreply.github.com>
2024-11-11 12:27:26 +05:30
rohitwaghchaure
593428b16d Merge pull request #44070 from frappe/mergify/bp/version-15-hotfix/pr-44058
fix: type error while saving pick list (backport #44058)
2024-11-11 12:26:43 +05:30
rohitwaghchaure
3c0623f593 Merge pull request #44068 from frappe/mergify/bp/version-15-hotfix/pr-44064
fix: slow reposting due to SABB update (backport #44064)
2024-11-11 12:10:12 +05:30
vishnu
7d098328d0 fix: tyeerror while saving pick list
(cherry picked from commit 22de0ecbdc)
2024-11-11 06:24:54 +00:00
Rohit Waghchaure
3e29ae8534 fix: slow reposting due to SABB update
(cherry picked from commit 2447b3f424)
2024-11-11 06:23:40 +00:00
ruthra kumar
d648875681 Merge pull request #44010 from frappe/mergify/bp/version-15-hotfix/pr-43689
refactor: allow multiple payment requests through customer portal (backport #43689)
2024-11-11 11:35:11 +05:30
HENRY Florian
865786e0b6 feat: add template taxe for charts of account France - Plan Comptable General avec code (#42757)
* feat: add template taxe for charts of account France - Plan Comptable General avec code

* feat: add template taxe for charts of account France - Plan Comptable General avec code

* feat: add template taxe for charts of account France - Plan Comptable General avec code

* feat: add template taxe for charts of account France - Plan Comptable General avec code

* feat: add template taxe for charts of account France - Plan Comptable General avec code

(cherry picked from commit 1fe6efdeb9)
2024-11-11 04:48:37 +00:00
HENRY Florian
e6894b949c fix: when company is created with other company template Chart of Account the Create Taxe Template failed (#42755)
fix: when company if create with other company template Created Template Taxe failed
(cherry picked from commit 8383883977)
2024-11-11 04:47:02 +00:00
Sagar Vora
11745add18 Merge pull request #44048 from frappe/mergify/bp/version-15-hotfix/pr-42743
fix: better gls for purchases with tax witholding (backport #42743)
2024-11-09 20:14:08 +05:30
Smit Vora
705a26a2fa fix: better gls for purchases with tax witholding (#42743)
* fix: better gls for purchases with tax witholding

* test: test case for purchase invoice gl entries with tax witholding

* fix: use flag `_skip_merge` instead of skipping merge based on against account

* test: fix test `test_single_threshold_tds` for newer implementation

(cherry picked from commit e3cd6539c3)
2024-11-09 09:45:25 +00:00
ruthra kumar
50fa77276e refactor: depracate old method and handle inclusive tax 2024-11-08 16:14:13 +05:30
mergify[bot]
4cde77d8d8 fix: duplicate items and outdated item price in POS (backport #42978) (#44038)
fix: duplicate items and outdated item price in POS (#42978)

* fix: duplicate items and outdated item price in POS

* fix: duplicate items and outdated item price in POS --formatter

(cherry picked from commit 4ea2071265)

Co-authored-by: Nihantra C. Patel <141945075+Nihantra-Patel@users.noreply.github.com>
2024-11-08 13:05:48 +05:30
Nabin Hait
f5610e29be Release v15.41.2 (#44037)
* fix: improved the conditions for determining voucher subtypes

(cherry picked from commit 00eee16190)

* fix: patch

(cherry picked from commit d76cc21086)

# Conflicts:
#	erpnext/patches.txt

* test: test voucher subtype for sales invoice

(cherry picked from commit ad6cc352f1)

* chore: resolve conflict

* fix: NoneType while updating ordered_qty in SO for removed items

(cherry picked from commit 442cdd7ce4)

* refactor: add "margin_type" and "margin_rate_or_amount" to no copy

(cherry picked from commit 70f090c1ec)

* fix: item not set in the batch quick entry form (backport #44028) (#44031)

fix: item not set in the batch quick entry form (#44028)

(cherry picked from commit 0399ccc51e)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>

* fix: calculate percentage received and delivered considering over-receipt and over-delivery (backport #43870) (#44030)

fix: calculate percentage received and delivered considering over-receipt and over-delivery (#43870)

(cherry picked from commit adba1168c1)

Co-authored-by: Nabin Hait <nabinhait@gmail.com>

* fix: sort by ascending to get the first period closing voucher (backport #44029) (#44035)

fix: sort by ascending to get the first period closing voucher (#44029)

(cherry picked from commit 42dcdcde1a)

Co-authored-by: Venkatesh <47534423+venkat102@users.noreply.github.com>

---------

Co-authored-by: ljain112 <ljain112@gmail.com>
Co-authored-by: Smit Vora <smitvora203@gmail.com>
Co-authored-by: ruthra kumar <ruthra@erpnext.com>
Co-authored-by: bhaveshkumar.j <bhaveshkumar.j@sritindia.com>
Co-authored-by: Ravindu Nethmina <117300601+NethminaHiker360@users.noreply.github.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
Co-authored-by: Venkatesh <47534423+venkat102@users.noreply.github.com>
2024-11-08 13:02:23 +05:30
mergify[bot]
56f25ae065 fix: sort by ascending to get the first period closing voucher (backport #44029) (#44035)
fix: sort by ascending to get the first period closing voucher (#44029)

(cherry picked from commit 42dcdcde1a)

Co-authored-by: Venkatesh <47534423+venkat102@users.noreply.github.com>
2024-11-08 12:34:47 +05:30
mergify[bot]
5958d0c257 fix: calculate percentage received and delivered considering over-receipt and over-delivery (backport #43870) (#44030)
fix: calculate percentage received and delivered considering over-receipt and over-delivery (#43870)

(cherry picked from commit adba1168c1)

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2024-11-08 12:24:21 +05:30
mergify[bot]
6dcd015a39 fix: item not set in the batch quick entry form (backport #44028) (#44031)
fix: item not set in the batch quick entry form (#44028)

(cherry picked from commit 0399ccc51e)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-11-08 12:23:50 +05:30
ruthra kumar
489fde8220 Merge pull request #44027 from frappe/mergify/bp/version-15-hotfix/pr-43873
fix: add missing fields to field_no_map array (backport #43873)
2024-11-08 11:52:13 +05:30
Ravindu Nethmina
487b5776e6 refactor: add "margin_type" and "margin_rate_or_amount" to no copy
(cherry picked from commit 70f090c1ec)
2024-11-08 06:00:33 +00:00
ruthra kumar
f397361ba7 Merge pull request #44024 from frappe/mergify/bp/version-15-hotfix/pr-43762
fix: handle NoneType error when updating ordered_qty in SO for remove… (backport #43762)
2024-11-08 10:58:57 +05:30
ruthra kumar
23e9a4607e Merge pull request #44022 from frappe/mergify/bp/version-15-hotfix/pr-43273
fix: improved the conditions for determining voucher subtypes (backport #43273)
2024-11-08 10:55:33 +05:30
bhaveshkumar.j
978a0078d8 fix: NoneType while updating ordered_qty in SO for removed items
(cherry picked from commit 442cdd7ce4)
2024-11-08 05:11:25 +00:00
ruthra kumar
6649d17b06 chore: resolve conflict 2024-11-08 10:33:56 +05:30
Smit Vora
d7f91824c0 test: test voucher subtype for sales invoice
(cherry picked from commit ad6cc352f1)
2024-11-08 04:51:12 +00:00
ljain112
107d53b358 fix: patch
(cherry picked from commit d76cc21086)

# Conflicts:
#	erpnext/patches.txt
2024-11-08 04:51:12 +00:00
ljain112
58ca4a2b99 fix: improved the conditions for determining voucher subtypes
(cherry picked from commit 00eee16190)
2024-11-08 04:51:11 +00:00
Frappe PR Bot
4819535a52 chore(release): Bumped to Version 15.41.1
## [15.41.1](https://github.com/frappe/erpnext/compare/v15.41.0...v15.41.1) (2024-11-07)

### Bug Fixes

* ensure list has items ([633997b](633997b1b0))
* error when saving POS merge log ([#43989](https://github.com/frappe/erpnext/issues/43989)) ([c3e61ae](c3e61aebd2))
* removed single quotes from deferred revenue ([#43985](https://github.com/frappe/erpnext/issues/43985)) ([20033ee](20033eef9b))
* task showing limit in customer portal (backport [#44003](https://github.com/frappe/erpnext/issues/44003)) ([#44005](https://github.com/frappe/erpnext/issues/44005)) ([47a8fc2](47a8fc28df))
* Update `dimension_filter_map` query ([318830c](318830c57d))
2024-11-07 12:44:29 +00:00
Sagar Vora
7d8d9cfdfe Merge pull request #44008 from frappe/version-15-hotfix 2024-11-07 18:13:13 +05:30
ruthra kumar
ff4751c9e8 refactor: handle PR's in advance stage
(cherry picked from commit 18c13a2cff)
2024-11-07 09:51:10 +00:00
ruthra kumar
eeff0a1252 refactor: cancel old PR and invalidate tokens
(cherry picked from commit cda7800777)
2024-11-07 09:51:10 +00:00
mergify[bot]
47a8fc28df fix: task showing limit in customer portal (backport #44003) (#44005)
fix: task showing limit in customer portal (#44003)

(cherry picked from commit 44832c3b5c)

Co-authored-by: Nihantra C. Patel <141945075+Nihantra-Patel@users.noreply.github.com>
2024-11-07 12:49:27 +05:30
Smit Vora
5dca98a1cf Merge pull request #43997 from Abdeali099/update-dimension-filter-query 2024-11-06 19:36:46 +05:30
Abdeali Chharchhoda
318830c57d fix: Update dimension_filter_map query 2024-11-06 19:16:01 +05:30
Sagar Vora
f4d2ba5bbd Merge pull request #43995 from frappe/mergify/bp/version-15-hotfix/pr-43993
fix: ensure list has items (backport #43993)
2024-11-06 14:08:33 +05:30
Sagar Vora
633997b1b0 fix: ensure list has items
(cherry picked from commit e13e688987)
2024-11-06 08:38:09 +00:00
Sagar Vora
36b22e290a Merge pull request #43991 from frappe/mergify/bp/version-15-hotfix/pr-43989
fix: error when saving POS merge log (backport #43989)
2024-11-06 13:26:02 +05:30
Sagar Vora
c3e61aebd2 fix: error when saving POS merge log (#43989)
(cherry picked from commit c62596b323)
2024-11-06 07:52:15 +00:00
Sagar Vora
de0c6f2ca9 Merge pull request #43987 from frappe/mergify/bp/version-15-hotfix/pr-43985
fix: removed single quotes from deferred revenue (backport #43985)
2024-11-06 12:05:17 +05:30
Nihantra C. Patel
20033eef9b fix: removed single quotes from deferred revenue (#43985)
(cherry picked from commit 834d18840c)
2024-11-06 06:33:34 +00:00
Frappe PR Bot
9ccdb987d9 chore(release): Bumped to Version 15.41.0
# [15.41.0](https://github.com/frappe/erpnext/compare/v15.40.0...v15.41.0) (2024-11-06)

### Bug Fixes

* add precision validation ([b665e4e](b665e4e24a))
* deleting SO/PO will remove its advance payment ledger entry ([d84a3c4](d84a3c4f29))
* map reference number while reversing journal ([10d8cc9](10d8cc9d66))
* **return:** set default return warehouse ([e730b8c](e730b8c6e4))
* SO link on PO and add in missing dashboard references on both ([9f7afda](9f7afda4db))
* validation trigger (backport [#43926](https://github.com/frappe/erpnext/issues/43926)) ([#43943](https://github.com/frappe/erpnext/issues/43943)) ([a689830](a689830bff))
* valuation rate for sales / purchase return for serial / batch nos (backport [#43925](https://github.com/frappe/erpnext/issues/43925)) ([#43942](https://github.com/frappe/erpnext/issues/43942)) ([ce42d84](ce42d847b3))

### Features

* advance payment ledger doctype ([b343334](b343334f69))
* remove Payroll Entry from Bank Account dashboard (backport [#43931](https://github.com/frappe/erpnext/issues/43931)) ([#43933](https://github.com/frappe/erpnext/issues/43933)) ([4a749ce](4a749cec72))

### Performance Improvements

* avoid reposting of entries created after stock reco (backport [#43950](https://github.com/frappe/erpnext/issues/43950)) ([#43961](https://github.com/frappe/erpnext/issues/43961)) ([7ad664d](7ad664d89a))
* too many writes error during reposting (backport [#43978](https://github.com/frappe/erpnext/issues/43978)) ([#43983](https://github.com/frappe/erpnext/issues/43983)) ([a38819c](a38819cbd5))
2024-11-06 05:21:49 +00:00
ruthra kumar
a6c58a3542 Merge pull request #43975 from frappe/version-15-hotfix
chore: release v15
2024-11-06 10:50:24 +05:30
mergify[bot]
ceccd8c2dc chore: update serial_batch_bundle.py (backport #43981) (#43984)
chore: update serial_batch_bundle.py (#43981)

Avaliable -> Available

(cherry picked from commit 30954ed645)

Co-authored-by: Ikko Eltociear Ashimine <eltociear@gmail.com>
2024-11-06 10:30:06 +05:30
mergify[bot]
a38819cbd5 perf: too many writes error during reposting (backport #43978) (#43983)
perf: too many writes error during reposting (#43978)

perf: too many writes error
(cherry picked from commit 134c24b9c5)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-11-06 10:05:44 +05:30
Khushi Rawat
801940912a Merge pull request #43980 from frappe/mergify/bp/version-15-hotfix/pr-43979
fix: added precision validation (backport #43979)
2024-11-06 01:45:53 +05:30
Khushi Rawat
b665e4e24a fix: add precision validation
(cherry picked from commit 7daadcf420)
2024-11-05 19:03:26 +00:00
ruthra kumar
e4d94af019 Merge pull request #43973 from frappe/mergify/bp/version-15-hotfix/pr-43971
refactor: update permission requirement for advance ledger (backport #43971)
2024-11-05 11:20:10 +05:30
ruthra kumar
eaf6d0d7d8 refactor: update advance ledger role requirement
(cherry picked from commit e41560d30b)
2024-11-05 05:30:13 +00:00
ruthra kumar
1b8bd0e1f3 refactor: avoid permission issue for non-admin
(cherry picked from commit c832d9fb9a)
2024-11-05 05:30:13 +00:00
ruthra kumar
ce817cbc12 Merge pull request #43965 from frappe/mergify/bp/version-15-hotfix/pr-43388
fix: SO link on PO and add in missing dashboard references on both (backport #43388)
2024-11-04 14:27:34 +05:30
CaseSolved
84a40c282b chore: linting
(cherry picked from commit be6970c850)
2024-11-04 08:32:21 +00:00
CaseSolved
9f7afda4db fix: SO link on PO and add in missing dashboard references on both
(cherry picked from commit 2017fd80d1)
2024-11-04 08:32:21 +00:00
ruthra kumar
ddd50167a5 Merge pull request #43447 from frappe/mergify/bp/version-15-hotfix/pr-43446
fix(return): set default return warehouse (backport #43446)
2024-11-04 13:26:01 +05:30
mergify[bot]
7ad664d89a perf: avoid reposting of entries created after stock reco (backport #43950) (#43961)
perf: avoid reposting of entries created after stock reco (#43950)

(cherry picked from commit 7cfe1c8d59)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-11-04 13:23:44 +05:30
ruthra kumar
5eb252215c Merge pull request #43959 from frappe/mergify/bp/version-15-hotfix/pr-43929
fix: Map reference number while reversing Journal Entry (backport #43929)
2024-11-04 13:15:31 +05:30
ramyasusee
10d8cc9d66 fix: map reference number while reversing journal
(cherry picked from commit 77de783cd4)
2024-11-04 06:58:58 +00:00
ruthra kumar
64f616b8a7 Merge pull request #43956 from frappe/mergify/bp/version-15-hotfix/pr-43835
Update fiscal_year.js (backport #43835)
2024-11-04 11:26:03 +05:30
hyaray
9a2b0a965d refactor: use year current year start date as default
(cherry picked from commit d54283ded5)
2024-11-04 05:48:42 +00:00
ruthra kumar
ec465571d8 Merge pull request #43946 from frappe/mergify/bp/version-15-hotfix/pr-43709
feat: Ledger for advance payment (backport #43709)
2024-11-04 10:53:21 +05:30
mergify[bot]
ce42d847b3 fix: valuation rate for sales / purchase return for serial / batch nos (backport #43925) (#43942)
fix: valuation rate for sales / purchase return for serial / batch nos (#43925)

(cherry picked from commit 01bb1612da)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-11-03 04:20:20 +05:30
mergify[bot]
a689830bff fix: validation trigger (backport #43926) (#43943)
fix: validation trigger (#43926)

(cherry picked from commit ba9fb4effc)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-11-03 04:20:01 +05:30
ruthra kumar
9bfcad31fd refactor: replace non-existant IntegrationTestCase 2024-11-01 20:08:07 +05:30
ruthra kumar
426010e21a refactor: fetch correct hook variable 2024-11-01 14:14:03 +05:30
ruthra kumar
ba09ddcc3a chore: resolve conflict 2024-11-01 14:10:45 +05:30
ruthra kumar
d0a655d5ae test: PO advance and currency from journal
(cherry picked from commit cf7b8f1b41)
2024-11-01 08:35:08 +00:00
ruthra kumar
91a276d4ed test: PO 'Advance Paid' and curreny when using payment
(cherry picked from commit ca85c75e39)
2024-11-01 08:35:08 +00:00
ruthra kumar
16c1fc75b5 chore: move tests to advance payment ledger doctype
(cherry picked from commit 14cef3d4c4)
2024-11-01 08:35:08 +00:00
ruthra kumar
7f9ae4e044 test: advance and currency from Journal
(cherry picked from commit 1825082512)
2024-11-01 08:35:07 +00:00
ruthra kumar
c8be4f3f78 refactor: use dr / cr account currency field for journals
(cherry picked from commit 9c1a4e284c)
2024-11-01 08:35:07 +00:00
ruthra kumar
d830ce1d88 test: USD Sales Order with advance payment
(cherry picked from commit 6c731561f3)
2024-11-01 08:35:07 +00:00
ruthra kumar
07a394a1c5 refactor: handle currency on advance payment ledger
(cherry picked from commit ae6a81cd07)
2024-11-01 08:35:07 +00:00
ruthra kumar
68a95c7dbc refactor: move creation logic to controller
(cherry picked from commit ad88bde448)
2024-11-01 08:35:07 +00:00
ruthra kumar
164d7cc896 refactor: handle 'no data' situation in patch
(cherry picked from commit 8e3bf7dc09)
2024-11-01 08:35:07 +00:00
ruthra kumar
063cef576c chore: update ignore_linked_doctypes for Journal Entry
(cherry picked from commit 767ae6a372)
2024-11-01 08:35:06 +00:00
ruthra kumar
c6bfdcf503 chore: update patchex.txt
(cherry picked from commit 8ab7194b1d)
2024-11-01 08:35:06 +00:00
ruthra kumar
085e0455d8 refactor: patch to migrating old SO / PO to advance ledger
(cherry picked from commit b927f2f4a0)
2024-11-01 08:35:06 +00:00
ruthra kumar
cb36dcb382 refactor(test): reconciliation shouldn't affect advance paid
(cherry picked from commit 35a8a18728)
2024-11-01 08:35:06 +00:00
ruthra kumar
ffd426d43d refactor: link journal entry to advance payment ledger
(cherry picked from commit fca5e95248)
2024-11-01 08:35:06 +00:00
ruthra kumar
d84a3c4f29 fix: deleting SO/PO will remove its advance payment ledger entry
(cherry picked from commit 14357bccba)
2024-11-01 08:35:06 +00:00
ruthra kumar
a12df122a9 refactor(test): advance_paid stays after reconciliation
(cherry picked from commit c4197c3f31)
2024-11-01 08:35:05 +00:00
ruthra kumar
df25d33735 chore: remove duplicate test class
(cherry picked from commit e2891a60d5)
2024-11-01 08:35:05 +00:00
ruthra kumar
a6c26874c7 refactor: remove advance payment ledgers on document deletion
(cherry picked from commit 3c53b92f05)
2024-11-01 08:35:05 +00:00
ruthra kumar
54f758c327 refactor: calculate advance from advance ledger
(cherry picked from commit 2b2360bf7b)

# Conflicts:
#	erpnext/controllers/accounts_controller.py
2024-11-01 08:35:05 +00:00
ruthra kumar
bf0b74bcbd refactor: create advance ledger entries on submit and cancel
(cherry picked from commit 575ca5b900)
2024-11-01 08:35:04 +00:00
ruthra kumar
0d02f8b5d1 refactor: make all fields readonly
(cherry picked from commit f176a82198)
2024-11-01 08:35:04 +00:00
ruthra kumar
b343334f69 feat: advance payment ledger doctype
(cherry picked from commit 2d6efd7cc8)
2024-11-01 08:35:04 +00:00
mergify[bot]
4a749cec72 feat: remove Payroll Entry from Bank Account dashboard (backport #43931) (#43933)
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2024-10-30 15:49:26 +01:00
Frappe PR Bot
3648f3816f chore(release): Bumped to Version 15.40.0
# [15.40.0](https://github.com/frappe/erpnext/compare/v15.39.6...v15.40.0) (2024-10-30)

### Bug Fixes

* add company filter for project ([33fa1e4](33fa1e45ad))
* add parenttype clause to invoice tax query in sales_register report ([603d2cf](603d2cf77d))
* backport translations from develop ([#43849](https://github.com/frappe/erpnext/issues/43849)) ([11dd196](11dd1968c7))
* basic rate not editable in Stock Entry Detail (backport [#43837](https://github.com/frappe/erpnext/issues/43837)) ([#43838](https://github.com/frappe/erpnext/issues/43838)) ([20478b6](20478b632f))
* better implementation, handle missing purchase order ([41db040](41db040a60))
* Calculate gross margin on update of project costing from invoices (backport [#43876](https://github.com/frappe/erpnext/issues/43876)) ([#43900](https://github.com/frappe/erpnext/issues/43900)) ([93d0db2](93d0db2910))
* calculate tds with net amount when invoice exceeds single threshold amount (backport [#43869](https://github.com/frappe/erpnext/issues/43869)) ([#43920](https://github.com/frappe/erpnext/issues/43920)) ([9a52661](9a526611e0))
* cannot create opp from lead without prospect ([4dcaf42](4dcaf42bc5))
* consider gle based on balances in company currency ([#43805](https://github.com/frappe/erpnext/issues/43805)) ([2fb4417](2fb441763a))
* consider opening asset values while calculating asset depreciation rate ([1af2326](1af2326a52))
* correct garbage value on Razorpay Payments Page ([2c21df2](2c21df2ad9))
* do not check for payment terms details for return invoices. ([9a5604c](9a5604c5bb))
* do not copy serial numbers from DN to SI (backport [#43885](https://github.com/frappe/erpnext/issues/43885)) ([#43893](https://github.com/frappe/erpnext/issues/43893)) ([d06831e](d06831ea94))
* do not set payment terms for return invoices ([a826a89](a826a894f4))
* find first PCV to consider opening entries ([8218ca9](8218ca990c))
* Handle None value for item description in customer portal invoice view (backport [#43823](https://github.com/frappe/erpnext/issues/43823)) ([#43889](https://github.com/frappe/erpnext/issues/43889)) ([8a72845](8a72845ee6))
* hide payment terms for return and paid purchase invoices ([29aa5d6](29aa5d6468))
* incorrect value of available_qty_for_consumption in subcontracti… (backport [#43836](https://github.com/frappe/erpnext/issues/43836)) ([#43861](https://github.com/frappe/erpnext/issues/43861)) ([cd4746a](cd4746ad2a))
* map doc from purchase order ([58a3ef7](58a3ef7aa6))
* Patch for reposting account closing balance (backport [#43905](https://github.com/frappe/erpnext/issues/43905)) ([#43910](https://github.com/frappe/erpnext/issues/43910)) ([ab16207](ab162070a6))
* post account closing balance against pcv closing account (backport [#43887](https://github.com/frappe/erpnext/issues/43887)) ([#43898](https://github.com/frappe/erpnext/issues/43898)) ([e22d0a3](e22d0a3406))
* purchase return validation issue (backport [#43871](https://github.com/frappe/erpnext/issues/43871)) (backport [#43874](https://github.com/frappe/erpnext/issues/43874)) ([#43879](https://github.com/frappe/erpnext/issues/43879)) ([db3be41](db3be4195c))
* recalculate outstanding after save on checkout for POS Invoice ([63668eb](63668eb855))
* remarks field in payment reconciliation ([8707090](870709079b))
* **RFQ:** make strings translatable (backport [#43843](https://github.com/frappe/erpnext/issues/43843)) ([#43848](https://github.com/frappe/erpnext/issues/43848)) ([07aaef2](07aaef2af2))
* rounding issue of required qty in subcontracting order ([#43908](https://github.com/frappe/erpnext/issues/43908)) ([9ac87bd](9ac87bd3b1))
* scrub "-" from fieldname in accounting dimension ([c702826](c70282663c))
* set bill_no before `against_voucher` gets concatenated ([81297ce](81297ce168))
* set default warehouse for pos invoice ([b80a5f2](b80a5f27a9))
* set proper currency format ([9f97018](9f970189fe))
* Unnecessary validation for non deferred sales invoices ([#43816](https://github.com/frappe/erpnext/issues/43816)) ([bf4fb53](bf4fb53575))
* use period closing voucher object to call get_account_closing_ba… (backport [#43880](https://github.com/frappe/erpnext/issues/43880)) ([#43883](https://github.com/frappe/erpnext/issues/43883)) ([8bfc212](8bfc212e26))
* validate fieldname ([b21abf4](b21abf4d90))
* work order finish button not showing (backport [#43875](https://github.com/frappe/erpnext/issues/43875)) (backport [#43877](https://github.com/frappe/erpnext/issues/43877)) ([#43904](https://github.com/frappe/erpnext/issues/43904)) ([7189dab](7189daba19))

### Features

* add party name in payment request ([935f2e1](935f2e11e8))

### Performance Improvements

* performance optimizations for accounting reports by refactoring account closing balance and period closing voucher ([#43798](https://github.com/frappe/erpnext/issues/43798)) ([ced76ca](ced76ca5c0))
2024-10-30 09:55:19 +00:00
ruthra kumar
6da359a839 Merge pull request #43891 from frappe/version-15-hotfix
chore: release v15
2024-10-30 15:23:57 +05:30
Shariq Ansari
5ea498062c Merge pull request #43927 from frappe/mergify/bp/version-15-hotfix/pr-43921
fix: cannot create opp from lead without prospect (backport #43921)
2024-10-30 14:40:00 +05:30
Shariq Ansari
4dcaf42bc5 fix: cannot create opp from lead without prospect
(cherry picked from commit 603383bca7)
2024-10-30 08:18:53 +00:00
mergify[bot]
db3be4195c fix: purchase return validation issue (backport #43871) (backport #43874) (#43879)
fix: purchase return validation issue (backport #43871) (#43874)

fix: purchase return validation issue (#43871)

(cherry picked from commit a7cc7b28c0)

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

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2024-10-30 12:17:46 +05:30
mergify[bot]
9a526611e0 fix: calculate tds with net amount when invoice exceeds single threshold amount (backport #43869) (#43920)
* fix: calculate tds with net amount when invoice exceeds single threshold amount

(cherry picked from commit ef694a40a1)

* test: add unit test to validate purchase invoice exceeding single threshold value

(cherry picked from commit 94badb464d)

---------

Co-authored-by: venkat102 <venkatesharunachalam659@gmail.com>
2024-10-30 11:25:56 +05:30
Nabin Hait
9ac87bd3b1 fix: rounding issue of required qty in subcontracting order (#43908)
* fix: rounding issue of required qty in subcontracting order

* fix: uom issue in test case

* fix: test case

* fix: restored report tests for manufacturing
2024-10-30 11:25:08 +05:30
Nabin Hait
0171af4604 Merge branch 'version-15' into version-15-hotfix 2024-10-30 11:23:52 +05:30
ruthra kumar
3ab31dcb92 Merge pull request #43917 from frappe/mergify/bp/version-15-hotfix/pr-43886
fix: remarks field in payment reconciliation (backport #43886)
2024-10-30 10:43:58 +05:30
ruthra kumar
63ecf13058 Merge pull request #43918 from frappe/mergify/bp/version-15-hotfix/pr-43913
fix: find first PCV to consider opening entries (backport #43913)
2024-10-30 10:43:47 +05:30
Nabin Hait
8218ca990c fix: find first PCV to consider opening entries
(cherry picked from commit 2201fc62a2)
2024-10-30 04:55:31 +00:00
ruthra kumar
ac121dd4e3 chore: resolve conflict 2024-10-30 10:23:41 +05:30
Ahmed Shareef
870709079b fix: remarks field in payment reconciliation
(cherry picked from commit 2d5b079949)

# Conflicts:
#	erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json
2024-10-30 04:50:27 +00:00
ruthra kumar
302f049025 Merge pull request #43915 from frappe/mergify/bp/version-15-hotfix/pr-43884
fix: recalculate outstanding after save on checkout for POS Invoice (backport #43884)
2024-10-30 10:19:50 +05:30
ljain112
63668eb855 fix: recalculate outstanding after save on checkout for POS Invoice
(cherry picked from commit 9ce2184c66)
2024-10-30 04:37:19 +00:00
Khushi Rawat
9a93c892be Merge pull request #43912 from frappe/mergify/bp/version-15-hotfix/pr-43899
fix: consider opening asset values while calculating depreciation rate (backport #43899)
2024-10-30 00:24:47 +05:30
Khushi Rawat
1af2326a52 fix: consider opening asset values while calculating asset depreciation rate
(cherry picked from commit 9d0fe7aa56)
2024-10-29 18:36:31 +00:00
mergify[bot]
ab162070a6 fix: Patch for reposting account closing balance (backport #43905) (#43910)
fix: Patch for reposting account closing balance (#43905)

(cherry picked from commit 3a0d27b393)

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2024-10-29 23:22:29 +05:30
Frappe PR Bot
d8e1a21bdc chore(release): Bumped to Version 15.39.6
## [15.39.6](https://github.com/frappe/erpnext/compare/v15.39.5...v15.39.6) (2024-10-29)

### Bug Fixes

* Patch for reposting account closing balance (backport [#43905](https://github.com/frappe/erpnext/issues/43905)) ([#43909](https://github.com/frappe/erpnext/issues/43909)) ([c2eb771](c2eb771c4d))
2024-10-29 17:39:33 +00:00
mergify[bot]
c2eb771c4d fix: Patch for reposting account closing balance (backport #43905) (#43909)
fix: Patch for reposting account closing balance (#43905)

(cherry picked from commit 3a0d27b393)

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2024-10-29 23:07:58 +05:30
mergify[bot]
8a72845ee6 fix: Handle None value for item description in customer portal invoice view (backport #43823) (#43889)
fix: Handle None value for item description in customer portal invoice view

(cherry picked from commit ceb449c75b)

Co-authored-by: ljain112 <ljain112@gmail.com>
2024-10-29 22:38:41 +05:30
mergify[bot]
7189daba19 fix: work order finish button not showing (backport #43875) (backport #43877) (#43904)
fix: work order finish button not showing (backport #43875) (#43877)

* fix: work order finish button not showing (#43875)

(cherry picked from commit 0a70be5b99)

# Conflicts:
#	erpnext/manufacturing/doctype/job_card/job_card.js
#	erpnext/manufacturing/doctype/job_card/job_card.py

* chore: fix conflicts

* chore: fix conflicts

* chore: fix conflicts

---------

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

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2024-10-29 22:37:26 +05:30
mergify[bot]
93d0db2910 fix: Calculate gross margin on update of project costing from invoices (backport #43876) (#43900)
* fix: Calculate gross margin on update of project costing from invoices (#43876)

* fix: Calculate gross margin on update of project costing from invoices

* chore: linter issues

(cherry picked from commit 0bba6442c0)

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

* fix: merge conflict

---------

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2024-10-29 22:36:39 +05:30
Frappe PR Bot
50db0aca61 chore(release): Bumped to Version 15.39.5
## [15.39.5](https://github.com/frappe/erpnext/compare/v15.39.4...v15.39.5) (2024-10-29)

### Bug Fixes

* post account closing balance against pcv closing account ([#43887](https://github.com/frappe/erpnext/issues/43887)) ([becfd98](becfd980b2))
2024-10-29 15:39:40 +00:00
ruthra kumar
b5d2708f69 Merge pull request #43902 from frappe/mergify/bp/version-15/pr-43887
fix: post account closing balance against pcv closing account (backport #43887)
2024-10-29 21:08:20 +05:30
Nabin Hait
becfd980b2 fix: post account closing balance against pcv closing account (#43887)
(cherry picked from commit 34295d0344)
2024-10-29 15:37:05 +00:00
Frappe PR Bot
be6cd6adc3 chore(release): Bumped to Version 15.39.4
## [15.39.4](https://github.com/frappe/erpnext/compare/v15.39.3...v15.39.4) (2024-10-29)

### Bug Fixes

* use period closing voucher object to call get_account_closing_ba… ([#43880](https://github.com/frappe/erpnext/issues/43880)) ([94a03c6](94a03c6e17))
2024-10-29 15:34:46 +00:00
ruthra kumar
48939f25c8 Merge pull request #43901 from frappe/mergify/bp/version-15/pr-43880
fix: use period closing voucher object to call get_account_closing_ba… (backport #43880)
2024-10-29 21:03:20 +05:30
Venkatesh
94a03c6e17 fix: use period closing voucher object to call get_account_closing_ba… (#43880)
fix: use period closing voucher object to call get_account_closing_balances method
(cherry picked from commit 99d1c5f342)
2024-10-29 15:15:11 +00:00
mergify[bot]
cd4746ad2a fix: incorrect value of available_qty_for_consumption in subcontracti… (backport #43836) (#43861)
fix: incorrect value of available_qty_for_consumption in subcontracti… (#43836)

fix: incorrect value of available_qty_for_consumption in subcontracting receipt
(cherry picked from commit ad6ce09b86)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-10-29 17:58:40 +05:30
mergify[bot]
e22d0a3406 fix: post account closing balance against pcv closing account (backport #43887) (#43898)
fix: post account closing balance against pcv closing account (#43887)

(cherry picked from commit 34295d0344)

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2024-10-29 17:25:40 +05:30
ruthra kumar
1755006445 Merge pull request #43897 from frappe/mergify/bp/version-15-hotfix/pr-43856
fix: add parenttype clause to invoice tax query in sales_register report (backport #43856)
2024-10-29 16:13:02 +05:30
ruthra kumar
4c9e17fbd3 Merge pull request #43895 from frappe/mergify/bp/version-15-hotfix/pr-43698
fix: Project name instead of ID in chart (backport #43698)
2024-10-29 16:12:23 +05:30
mergify[bot]
d06831ea94 fix: do not copy serial numbers from DN to SI (backport #43885) (#43893)
fix: do not copy serial numbers from DN to SI (#43885)

(cherry picked from commit 0c93bc31a5)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-10-29 15:48:40 +05:30
Imesha Sudasingha
603d2cf77d fix: add parenttype clause to invoice tax query in sales_register report
(cherry picked from commit e30ab141f4)
2024-10-29 10:10:41 +00:00
lukas.brandhoff
13557a2c10 refactor: include 'Project Name' in Project summary report
Keep name field for backwards compatibility

(cherry picked from commit 736d1a1105)
2024-10-29 09:54:25 +00:00
ruthra kumar
218c51cdcf Merge pull request #43812 from frappe/mergify/bp/version-15-hotfix/pr-43793
chore(Timesheet): add type hints (backport #43793)
2024-10-29 14:46:45 +05:30
ruthra kumar
61a11c8f1f Merge pull request #43888 from frappe/mergify/bp/version-15-hotfix/pr-43824
fix: accounting dimension fieldname (backport #43824)
2024-10-29 14:35:30 +05:30
ljain112
b21abf4d90 fix: validate fieldname
(cherry picked from commit ca31a19eb7)
2024-10-29 08:16:31 +00:00
ljain112
c70282663c fix: scrub "-" from fieldname in accounting dimension
(cherry picked from commit 023b7b9a60)
2024-10-29 08:16:31 +00:00
ruthra kumar
87e0d2f7f4 Merge pull request #43882 from frappe/mergify/bp/version-15-hotfix/pr-43803
feat: add party name in payment request (backport #43803)
2024-10-29 12:15:48 +05:30
mergify[bot]
8bfc212e26 fix: use period closing voucher object to call get_account_closing_ba… (backport #43880) (#43883)
fix: use period closing voucher object to call get_account_closing_ba… (#43880)

fix: use period closing voucher object to call get_account_closing_balances method
(cherry picked from commit 99d1c5f342)

Co-authored-by: Venkatesh <47534423+venkat102@users.noreply.github.com>
2024-10-29 12:08:24 +05:30
ruthra kumar
a937e32989 chore: resolve conflict 2024-10-29 11:55:50 +05:30
RAVIBHARATHI P C
935f2e11e8 feat: add party name in payment request
(cherry picked from commit 0acb609d97)

# Conflicts:
#	erpnext/accounts/doctype/payment_request/payment_request.py
2024-10-29 05:48:18 +00:00
ruthra kumar
7202939e0d Merge pull request #40813 from frappe/mergify/bp/version-15-hotfix/pr-40720
Set default warehouse for pos invoice (backport #40720)
2024-10-29 10:05:32 +05:30
ruthra kumar
73a8b6a7d7 Merge pull request #43864 from frappe/mergify/bp/version-15-hotfix/pr-43685
fix: do not check for payment terms details for return invoices. (backport #43685)
2024-10-28 15:52:45 +05:30
ruthra kumar
303ae8321b chore: resolve conflict 2024-10-28 13:33:24 +05:30
ruthra kumar
d5e1a46588 Merge pull request #43866 from ruthra-kumar/fix_whitespace
chore: fix whitespace
2024-10-28 12:10:52 +05:30
ruthra kumar
27108874ea chore: replace whitespace with tab 2024-10-28 11:50:55 +05:30
ljain112
29aa5d6468 fix: hide payment terms for return and paid purchase invoices
(cherry picked from commit 912e1e3f3d)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
2024-10-28 05:55:07 +00:00
ljain112
a826a894f4 fix: do not set payment terms for return invoices
(cherry picked from commit 8b700eadc7)
2024-10-28 05:55:07 +00:00
ljain112
9a5604c5bb fix: do not check for payment terms details for return invoices.
(cherry picked from commit 6703b7d1ae)
2024-10-28 05:55:07 +00:00
ruthra kumar
b95dfcbce0 Merge pull request #42607 from Abhishek-Chougule/version-15-hotfix
fix: correct garbage value on Razorpay Payments Page
2024-10-28 10:07:25 +05:30
ruthra kumar
ed01b4c161 Merge pull request #43857 from frappe/mergify/bp/version-15-hotfix/pr-43833
refactor: query for expense_account moved to setup hook in purchase invoice (backport #43833)
2024-10-28 09:53:03 +05:30
ljain112
19db526fdd refactor: query for expense_account moved to setup hook in purchase invoice
(cherry picked from commit a9ac0cc223)
2024-10-28 04:14:13 +00:00
Raffael Meyer
11dd1968c7 fix: backport translations from develop (#43849) 2024-10-26 20:25:30 +02:00
mergify[bot]
07aaef2af2 fix(RFQ): make strings translatable (backport #43843) (#43848)
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
fix(RFQ): make strings translatable (#43843)
2024-10-26 19:43:23 +02:00
mergify[bot]
20478b632f fix: basic rate not editable in Stock Entry Detail (backport #43837) (#43838)
fix: basic rate not editable in Stock Entry Detail (#43837)

(cherry picked from commit 5a967bc868)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-10-25 20:21:01 +05:30
ruthra kumar
18f32b8de6 Merge pull request #43831 from frappe/mergify/bp/version-15-hotfix/pr-43830
fix: set bill_no before `against_voucher` gets concatenated (backport #43830)
2024-10-25 14:28:36 +05:30
ruthra kumar
81297ce168 fix: set bill_no before against_voucher gets concatenated
(cherry picked from commit 7bade7f1fe)
2024-10-25 08:31:43 +00:00
Frappe PR Bot
f48ce90658 chore(release): Bumped to Version 15.39.3
## [15.39.3](https://github.com/frappe/erpnext/compare/v15.39.2...v15.39.3) (2024-10-24)

### Bug Fixes

* Unnecessary validation for non deferred sales invoices ([#43816](https://github.com/frappe/erpnext/issues/43816)) ([a79bc4d](a79bc4d35a))
2024-10-24 08:22:31 +00:00
Deepesh Garg
3df68e462f Merge pull request #43818 from frappe/mergify/bp/version-15/pr-43817
fix: Unnecessary validation for non-deferred sales invoices (#43816)
2024-10-24 13:51:15 +05:30
mergify[bot]
a79bc4d35a fix: Unnecessary validation for non deferred sales invoices (#43816)
fix: Unnecessary validation for non deferred sales invoices (#43816)

(cherry picked from commit af472054f6)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
(cherry picked from commit bf4fb53575)
2024-10-24 08:14:16 +00:00
mergify[bot]
bf4fb53575 fix: Unnecessary validation for non deferred sales invoices (#43816)
fix: Unnecessary validation for non deferred sales invoices (#43816)

(cherry picked from commit af472054f6)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2024-10-24 13:37:57 +05:30
Raffael Meyer
cf25f4c579 chore(Timesheet): add type hints (#43793)
(cherry picked from commit fe1e1b12c3)
2024-10-23 22:54:04 +00:00
Frappe PR Bot
188645bfd6 chore(release): Bumped to Version 15.39.2
## [15.39.2](https://github.com/frappe/erpnext/compare/v15.39.1...v15.39.2) (2024-10-23)

### Bug Fixes

* consider gle based on balances in company currency (copy [#43805](https://github.com/frappe/erpnext/issues/43805)) ([#43809](https://github.com/frappe/erpnext/issues/43809)) ([1c4eef2](1c4eef2ef6))
2024-10-23 12:12:56 +00:00
mergify[bot]
1c4eef2ef6 fix: consider gle based on balances in company currency (copy #43805) (#43809)
fix: consider gle based on balances in company currency (#43805)

(cherry picked from commit 2fb441763a)

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2024-10-23 17:41:41 +05:30
ruthra kumar
a3bf320add Merge pull request #42764 from frappe/mergify/bp/version-15-hotfix/pr-42458
fix: use company default currency in report (backport #42458)
2024-10-23 17:33:53 +05:30
ruthra kumar
d0db3b08d7 Merge pull request #43807 from frappe/mergify/bp/version-15-hotfix/pr-43755
fix: Add Company Filter (backport #43755)
2024-10-23 17:30:31 +05:30
Nabin Hait
2fb441763a fix: consider gle based on balances in company currency (#43805) 2024-10-23 17:08:52 +05:30
IamSaiyyamChhetri
33fa1e45ad fix: add company filter for project
- In Project dt Sales Order field
- In Sales Order dt Project field

(cherry picked from commit 9909d760a5)
2024-10-23 11:21:18 +00:00
Nabin Hait
ced76ca5c0 perf: performance optimizations for accounting reports by refactoring account closing balance and period closing voucher (#43798)
* fix: Gl Entry form cleanup

* fix: Added indexes in gl entry table

* perf: Refactored period closing voucher to handle large volume of gle

* fix: fixes as per new period start and end date fields in PCV

* perf: performance optimization for  accounting reports

* perf: performance optimizations for account closing balance patch

* fix: test cases

* fix: lenter issues - direct use of sql query

* fix: test cases

* fix: test cases

* fix: test cases

* fix: wrong fieldname

* fix: test cases
2024-10-23 13:07:16 +05:30
Smit Vora
9c0f17e13d Merge pull request #43788 from frappe/mergify/bp/version-15-hotfix/pr-43769
fix: mapping purchase receipt from subcontracting receipt is not required (backport #43769)
2024-10-23 10:36:48 +05:30
Ninad1306
41db040a60 fix: better implementation, handle missing purchase order
(cherry picked from commit 66211dafd6)
2024-10-22 14:42:51 +00:00
Ninad1306
40927c7413 test: auto create purchase receipt
(cherry picked from commit 59887bbc13)
2024-10-22 14:42:51 +00:00
Ninad1306
58a3ef7aa6 fix: map doc from purchase order
(cherry picked from commit 60ceb91ace)
2024-10-22 14:42:51 +00:00
David
e730b8c6e4 fix(return): set default return warehouse
This captures the case of manual modifications to the return and ensures
that by default, the correct return warehouse will be set

(cherry picked from commit fa65291e98)
2024-09-30 12:28:14 +00:00
Sanket322
9f970189fe fix: set proper currency format
(cherry picked from commit 2533808f1e)
2024-08-14 08:10:05 +00:00
Abhishek Chougule
8db11d03ed Merge branch 'frappe:version-15-hotfix' into version-15-hotfix 2024-08-05 17:37:11 +05:30
Abhishek Chougule
2c21df2ad9 fix: correct garbage value on Razorpay Payments Page 2024-08-03 10:54:51 +05:30
Danny
b80a5f27a9 fix: set default warehouse for pos invoice
(cherry picked from commit b156937254)
2024-04-01 17:08:39 +00:00
230 changed files with 14734 additions and 1374 deletions

View File

@@ -4,21 +4,21 @@
# the repo. Unless a later match takes precedence,
erpnext/accounts/ @deepeshgarg007 @ruthra-kumar
erpnext/assets/ @anandbaburajan @deepeshgarg007
erpnext/assets/ @khushi8112 @deepeshgarg007
erpnext/regional @deepeshgarg007 @ruthra-kumar
erpnext/selling @deepeshgarg007 @ruthra-kumar
erpnext/support/ @deepeshgarg007
pos*
erpnext/buying/ @rohitwaghchaure @s-aga-r
erpnext/maintenance/ @rohitwaghchaure @s-aga-r
erpnext/manufacturing/ @rohitwaghchaure @s-aga-r
erpnext/quality_management/ @rohitwaghchaure @s-aga-r
erpnext/stock/ @rohitwaghchaure @s-aga-r
erpnext/subcontracting @rohitwaghchaure @s-aga-r
erpnext/buying/ @rohitwaghchaure
erpnext/maintenance/ @rohitwaghchaure
erpnext/manufacturing/ @rohitwaghchaure
erpnext/quality_management/ @rohitwaghchaure
erpnext/stock/ @rohitwaghchaure
erpnext/subcontracting @rohitwaghchaure
erpnext/controllers/ @deepeshgarg007 @rohitwaghchaure
erpnext/patches/ @deepeshgarg007
.github/ @deepeshgarg007
pyproject.toml @phot0n
pyproject.toml @akhilnarang

View File

@@ -4,7 +4,7 @@ import inspect
import frappe
from frappe.utils.user import is_website_user
__version__ = "15.39.0"
__version__ = "15.45.3"
def get_default_company(user=None):

View File

@@ -58,7 +58,7 @@ def build_conditions(process_type, account, company):
)
if account:
conditions += f"AND {deferred_account}='{frappe.db.escape(account)}'"
conditions += f"AND {deferred_account}={frappe.db.escape(account)}"
elif company:
conditions += f"AND p.company = {frappe.db.escape(company)}"

View File

@@ -121,7 +121,7 @@
"label": "Account Type",
"oldfieldname": "account_type",
"oldfieldtype": "Select",
"options": "\nAccumulated Depreciation\nAsset Received But Not Billed\nBank\nCash\nChargeable\nCapital Work in Progress\nCost of Goods Sold\nCurrent Asset\nCurrent Liability\nDepreciation\nDirect Expense\nDirect Income\nEquity\nExpense Account\nExpenses Included In Asset Valuation\nExpenses Included In Valuation\nFixed Asset\nIncome Account\nIndirect Expense\nIndirect Income\nLiability\nPayable\nReceivable\nRound Off\nStock\nStock Adjustment\nStock Received But Not Billed\nService Received But Not Billed\nTax\nTemporary",
"options": "\nAccumulated Depreciation\nAsset Received But Not Billed\nBank\nCash\nChargeable\nCapital Work in Progress\nCost of Goods Sold\nCurrent Asset\nCurrent Liability\nDepreciation\nDirect Expense\nDirect Income\nEquity\nExpense Account\nExpenses Included In Asset Valuation\nExpenses Included In Valuation\nFixed Asset\nIncome Account\nIndirect Expense\nIndirect Income\nLiability\nPayable\nReceivable\nRound Off\nRound Off for Opening\nStock\nStock Adjustment\nStock Received But Not Billed\nService Received But Not Billed\nTax\nTemporary",
"search_index": 1
},
{
@@ -191,7 +191,7 @@
"idx": 1,
"is_tree": 1,
"links": [],
"modified": "2024-06-27 16:23:04.444354",
"modified": "2024-08-19 15:19:11.095045",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Account",

View File

@@ -60,6 +60,7 @@ class Account(NestedSet):
"Payable",
"Receivable",
"Round Off",
"Round Off for Opening",
"Stock",
"Stock Adjustment",
"Stock Received But Not Billed",

View File

@@ -58,7 +58,7 @@ frappe.ui.form.on("Accounting Dimension", {
},
label: function (frm) {
frm.set_value("fieldname", frappe.model.scrub(frm.doc.label));
frm.set_value("fieldname", frm.doc.label.replace(/ /g, "_").replace(/-/g, "_").toLowerCase());
},
document_type: function (frm) {

View File

@@ -7,6 +7,7 @@ import json
import frappe
from frappe import _, scrub
from frappe.custom.doctype.custom_field.custom_field import create_custom_field
from frappe.database.schema import validate_column_name
from frappe.model import core_doctypes_list
from frappe.model.document import Document
from frappe.utils import cstr
@@ -60,6 +61,7 @@ class AccountingDimension(Document):
if not self.is_new():
self.validate_document_type_change()
validate_column_name(self.fieldname)
self.validate_dimension_defaults()
def validate_document_type_change(self):

View File

@@ -74,12 +74,12 @@ def get_dimension_filter_map():
a.applicable_on_account, d.dimension_value, p.accounting_dimension,
p.allow_or_restrict, a.is_mandatory
FROM
`tabApplicable On Account` a, `tabAllowed Dimension` d,
`tabApplicable On Account` a,
`tabAccounting Dimension Filter` p
LEFT JOIN `tabAllowed Dimension` d ON d.parent = p.name
WHERE
p.name = a.parent
AND p.disabled = 0
AND p.name = d.parent
""",
as_dict=1,
)
@@ -97,7 +97,6 @@ def get_dimension_filter_map():
f.allow_or_restrict,
f.is_mandatory,
)
frappe.flags.dimension_filter_map = dimension_filter_map
return frappe.flags.dimension_filter_map

View File

@@ -0,0 +1,8 @@
// Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
// frappe.ui.form.on("Advance Payment Ledger Entry", {
// refresh(frm) {
// },
// });

View File

@@ -0,0 +1,113 @@
{
"actions": [],
"allow_rename": 1,
"creation": "2024-10-16 16:57:12.085072",
"doctype": "DocType",
"engine": "InnoDB",
"field_order": [
"company",
"voucher_type",
"voucher_no",
"against_voucher_type",
"against_voucher_no",
"amount",
"currency",
"event"
],
"fields": [
{
"fieldname": "voucher_type",
"fieldtype": "Link",
"label": "Voucher Type",
"options": "DocType",
"read_only": 1
},
{
"fieldname": "voucher_no",
"fieldtype": "Dynamic Link",
"label": "Voucher No",
"options": "voucher_type",
"read_only": 1
},
{
"fieldname": "against_voucher_type",
"fieldtype": "Link",
"label": "Against Voucher Type",
"options": "DocType",
"read_only": 1
},
{
"fieldname": "against_voucher_no",
"fieldtype": "Dynamic Link",
"label": "Against Voucher No",
"options": "against_voucher_type",
"read_only": 1
},
{
"fieldname": "amount",
"fieldtype": "Currency",
"label": "Amount",
"read_only": 1
},
{
"fieldname": "currency",
"fieldtype": "Link",
"label": "Currency",
"options": "Currency",
"read_only": 1
},
{
"fieldname": "event",
"fieldtype": "Data",
"label": "Event",
"read_only": 1
},
{
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"options": "Company",
"read_only": 1
}
],
"in_create": 1,
"index_web_pages_for_search": 1,
"links": [],
"modified": "2024-11-05 10:31:28.736671",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Advance Payment Ledger Entry",
"owner": "Administrator",
"permissions": [
{
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts User",
"share": 1
},
{
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts Manager",
"share": 1
},
{
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Auditor",
"share": 1
}
],
"sort_field": "creation",
"sort_order": "DESC",
"states": []
}

View File

@@ -0,0 +1,27 @@
# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
# import frappe
from frappe.model.document import Document
class AdvancePaymentLedgerEntry(Document):
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from frappe.types import DF
against_voucher_no: DF.DynamicLink | None
against_voucher_type: DF.Link | None
amount: DF.Currency
company: DF.Link | None
currency: DF.Link | None
event: DF.Data | None
voucher_no: DF.DynamicLink | None
voucher_type: DF.Link | None
# end: auto-generated types
pass

View File

@@ -0,0 +1,222 @@
# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.utils import nowdate, today
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
class TestAdvancePaymentLedgerEntry(AccountsTestMixin, FrappeTestCase):
"""
Integration tests for AdvancePaymentLedgerEntry.
Use this class for testing interactions between multiple components.
"""
def setUp(self):
self.create_company()
self.create_usd_receivable_account()
self.create_usd_payable_account()
self.create_item()
self.clear_old_entries()
def tearDown(self):
frappe.db.rollback()
def create_sales_order(self, qty=1, rate=100, currency="INR", do_not_submit=False):
"""
Helper method
"""
so = make_sales_order(
company=self.company,
customer=self.customer,
currency=currency,
item=self.item,
qty=qty,
rate=rate,
transaction_date=today(),
do_not_submit=do_not_submit,
)
return so
def create_purchase_order(self, qty=1, rate=100, currency="INR", do_not_submit=False):
"""
Helper method
"""
po = create_purchase_order(
company=self.company,
customer=self.supplier,
currency=currency,
item=self.item,
qty=qty,
rate=rate,
transaction_date=today(),
do_not_submit=do_not_submit,
)
return po
def test_so_advance_paid_and_currency_with_payment(self):
self.create_customer("_Test USD Customer", "USD")
so = self.create_sales_order(currency="USD", do_not_submit=True)
so.conversion_rate = 80
so.submit()
pe_exchange_rate = 85
pe = get_payment_entry(so.doctype, so.name, bank_account=self.cash)
pe.reference_no = "1"
pe.reference_date = nowdate()
pe.paid_from = self.debtors_usd
pe.paid_from_account_currency = "USD"
pe.source_exchange_rate = pe_exchange_rate
pe.paid_amount = so.grand_total
pe.received_amount = pe_exchange_rate * pe.paid_amount
pe.references[0].outstanding_amount = 100
pe.references[0].total_amount = 100
pe.references[0].allocated_amount = 100
pe.save().submit()
so.reload()
self.assertEqual(so.advance_paid, 100)
self.assertEqual(so.party_account_currency, "USD")
# cancel advance payment
pe.reload()
pe.cancel()
so.reload()
self.assertEqual(so.advance_paid, 0)
self.assertEqual(so.party_account_currency, "USD")
def test_so_advance_paid_and_currency_with_journal(self):
self.create_customer("_Test USD Customer", "USD")
so = self.create_sales_order(currency="USD", do_not_submit=True)
so.conversion_rate = 80
so.submit()
je_exchange_rate = 85
je = frappe.get_doc(
{
"doctype": "Journal Entry",
"company": self.company,
"voucher_type": "Journal Entry",
"posting_date": so.transaction_date,
"multi_currency": True,
"accounts": [
{
"account": self.debtors_usd,
"party_type": "Customer",
"party": so.customer,
"credit": 8500,
"credit_in_account_currency": 100,
"is_advance": "Yes",
"reference_type": so.doctype,
"reference_name": so.name,
"exchange_rate": je_exchange_rate,
},
{
"account": self.cash,
"debit": 8500,
"debit_in_account_currency": 8500,
},
],
}
)
je.save().submit()
so.reload()
self.assertEqual(so.advance_paid, 100)
self.assertEqual(so.party_account_currency, "USD")
# cancel advance payment
je.reload()
je.cancel()
so.reload()
self.assertEqual(so.advance_paid, 0)
self.assertEqual(so.party_account_currency, "USD")
def test_po_advance_paid_and_currency_with_payment(self):
self.create_supplier("_Test USD Supplier", "USD")
po = self.create_purchase_order(currency="USD", do_not_submit=True)
po.conversion_rate = 80
po.submit()
pe_exchange_rate = 85
pe = get_payment_entry(po.doctype, po.name, bank_account=self.cash)
pe.reference_no = "1"
pe.reference_date = nowdate()
pe.paid_to = self.creditors_usd
pe.paid_to_account_currency = "USD"
pe.target_exchange_rate = pe_exchange_rate
pe.received_amount = po.grand_total
pe.paid_amount = pe_exchange_rate * pe.received_amount
pe.references[0].outstanding_amount = 100
pe.references[0].total_amount = 100
pe.references[0].allocated_amount = 100
pe.save().submit()
po.reload()
self.assertEqual(po.advance_paid, 100)
self.assertEqual(po.party_account_currency, "USD")
# cancel advance payment
pe.reload()
pe.cancel()
po.reload()
self.assertEqual(po.advance_paid, 0)
self.assertEqual(po.party_account_currency, "USD")
def test_po_advance_paid_and_currency_with_journal(self):
self.create_supplier("_Test USD Supplier", "USD")
po = self.create_purchase_order(currency="USD", do_not_submit=True)
po.conversion_rate = 80
po.submit()
je_exchange_rate = 85
je = frappe.get_doc(
{
"doctype": "Journal Entry",
"company": self.company,
"voucher_type": "Journal Entry",
"posting_date": po.transaction_date,
"multi_currency": True,
"accounts": [
{
"account": self.creditors_usd,
"party_type": "Supplier",
"party": po.supplier,
"debit": 8500,
"debit_in_account_currency": 100,
"is_advance": "Yes",
"reference_type": po.doctype,
"reference_name": po.name,
"exchange_rate": je_exchange_rate,
},
{
"account": self.cash,
"credit": 8500,
"credit_in_account_currency": 8500,
},
],
}
)
je.save().submit()
po.reload()
self.assertEqual(po.advance_paid, 100)
self.assertEqual(po.party_account_currency, "USD")
# cancel advance payment
je.reload()
je.cancel()
po.reload()
self.assertEqual(po.advance_paid, 0)
self.assertEqual(po.party_account_currency, "USD")

View File

@@ -224,11 +224,6 @@
"link_doctype": "Bank Guarantee",
"link_fieldname": "bank_account"
},
{
"group": "Transactions",
"link_doctype": "Payroll Entry",
"link_fieldname": "bank_account"
},
{
"group": "Transactions",
"link_doctype": "Bank Transaction",
@@ -255,7 +250,7 @@
"link_fieldname": "default_bank_account"
}
],
"modified": "2024-09-24 06:57:41.292970",
"modified": "2024-10-30 09:41:14.113414",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Account",

View File

@@ -19,16 +19,6 @@
"currency",
"column_break_11",
"conversion_rate",
"address_and_contact_section",
"customer_address",
"address_display",
"contact_person",
"contact_display",
"column_break_16",
"company_address",
"company_address_display",
"contact_mobile",
"contact_email",
"section_break_6",
"dunning_type",
"column_break_8",
@@ -56,7 +46,21 @@
"income_account",
"column_break_48",
"cost_center",
"amended_from"
"amended_from",
"address_and_contact_tab",
"address_and_contact_section",
"customer_address",
"address_display",
"column_break_vodj",
"contact_person",
"contact_display",
"contact_mobile",
"contact_email",
"section_break_xban",
"column_break_16",
"company_address",
"company_address_display",
"column_break_lqmf"
],
"fields": [
{
@@ -178,10 +182,8 @@
"label": "Rate of Interest (%) Yearly"
},
{
"collapsible": 1,
"fieldname": "address_and_contact_section",
"fieldtype": "Section Break",
"label": "Address and Contact"
"fieldtype": "Section Break"
},
{
"fieldname": "address_display",
@@ -377,11 +379,28 @@
{
"fieldname": "column_break_48",
"fieldtype": "Column Break"
},
{
"fieldname": "address_and_contact_tab",
"fieldtype": "Tab Break",
"label": "Address & Contact"
},
{
"fieldname": "column_break_vodj",
"fieldtype": "Column Break"
},
{
"fieldname": "section_break_xban",
"fieldtype": "Section Break"
},
{
"fieldname": "column_break_lqmf",
"fieldtype": "Column Break"
}
],
"is_submittable": 1,
"links": [],
"modified": "2023-06-15 15:46:53.865712",
"modified": "2024-11-26 13:46:07.760867",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Dunning",
@@ -435,4 +454,4 @@
"states": [],
"title_field": "customer_name",
"track_changes": 1
}
}

View File

@@ -74,6 +74,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"
@@ -248,23 +263,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]:
@@ -288,23 +303,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

@@ -188,7 +188,7 @@ class TestExchangeRateRevaluation(AccountsTestMixin, FrappeTestCase):
pe = get_payment_entry(si.doctype, si.name)
pe.paid_amount = 95
pe.source_exchange_rate = 84.211
pe.source_exchange_rate = 84.2105
pe.received_amount = 8000
pe.references = []
pe.save().submit()
@@ -229,7 +229,7 @@ class TestExchangeRateRevaluation(AccountsTestMixin, FrappeTestCase):
row = next(x for x in je.accounts if x.account == self.debtors_usd)
self.assertEqual(flt(row.credit_in_account_currency, precision), 5.0) # in USD
row = next(x for x in je.accounts if x.account != self.debtors_usd)
self.assertEqual(flt(row.debit_in_account_currency, precision), 421.06) # in INR
self.assertEqual(flt(row.debit_in_account_currency, precision), 421.05) # in INR
# total_debit and total_credit will be 0.0, as JV is posting only to account currency fields
self.assertEqual(flt(je.total_debit, precision), 0.0)

View File

@@ -4,10 +4,7 @@
frappe.ui.form.on("Fiscal Year", {
onload: function (frm) {
if (frm.doc.__islocal) {
frm.set_value(
"year_start_date",
frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)
);
frm.set_value("year_start_date", frappe.datetime.year_start());
}
},
year_start_date: function (frm) {

View File

@@ -72,10 +72,10 @@
},
{
"default": "0",
"description": "Less than 12 months.",
"description": "More/Less than 12 months.",
"fieldname": "is_short_year",
"fieldtype": "Check",
"label": "Is Short Year",
"label": "Is Short/Long Year",
"set_only_once": 1
}
],

View File

@@ -127,9 +127,6 @@ class JournalEntry(AccountsController):
self.set_amounts_in_company_currency()
self.validate_debit_credit_amount()
self.set_total_debit_credit()
# Do not validate while importing via data import
if not frappe.flags.in_import:
self.validate_total_debit_and_credit()
if not frappe.flags.is_reverse_depr_entry:
self.validate_against_jv()
@@ -184,10 +181,16 @@ class JournalEntry(AccountsController):
else:
return self._cancel()
def before_submit(self):
# Do not validate while importing via data import
if not frappe.flags.in_import:
self.validate_total_debit_and_credit()
def on_submit(self):
self.validate_cheque_info()
self.check_credit_limit()
self.make_gl_entries()
self.make_advance_payment_ledger_entries()
self.update_advance_paid()
self.update_asset_value()
self.update_inter_company_jv()
@@ -218,8 +221,10 @@ class JournalEntry(AccountsController):
"Repost Accounting Ledger Items",
"Unreconcile Payment",
"Unreconcile Payment Entries",
"Advance Payment Ledger Entry",
)
self.make_gl_entries(1)
self.make_advance_payment_ledger_entries()
self.update_advance_paid()
self.unlink_advance_entry_reference()
self.unlink_asset_reference()
@@ -1668,6 +1673,8 @@ def make_reverse_journal_entry(source_name, target_doc=None):
"debit": "credit",
"credit_in_account_currency": "debit_in_account_currency",
"credit": "debit",
"reference_type": "reference_type",
"reference_name": "reference_name",
},
},
},

View File

@@ -515,6 +515,55 @@ class TestJournalEntry(unittest.TestCase):
self.assertEqual(row.debit_in_account_currency, 100)
self.assertEqual(row.credit_in_account_currency, 100)
def test_toggle_debit_credit_if_negative(self):
from erpnext.accounts.general_ledger import process_gl_map
# Create JV with defaut cost center - _Test Cost Center
frappe.db.set_single_value("Accounts Settings", "merge_similar_account_heads", 0)
jv = frappe.new_doc("Journal Entry")
jv.posting_date = nowdate()
jv.company = "_Test Company"
jv.user_remark = "test"
jv.extend(
"accounts",
[
{
"account": "_Test Cash - _TC",
"debit": 100 * -1,
"debit_in_account_currency": 100 * -1,
"exchange_rate": 1,
},
{
"account": "_Test Bank - _TC",
"credit": 100 * -1,
"credit_in_account_currency": 100 * -1,
"exchange_rate": 1,
},
],
)
jv.flags.ignore_validate = True
jv.save()
self.assertEqual(len(jv.accounts), 2)
gl_map = jv.build_gl_map()
for row in gl_map:
if row.account == "_Test Cash - _TC":
self.assertEqual(row.debit, 100 * -1)
self.assertEqual(row.debit_in_account_currency, 100 * -1)
self.assertEqual(row.debit_in_transaction_currency, 100 * -1)
gl_map = process_gl_map(gl_map, False)
for row in gl_map:
if row.account == "_Test Cash - _TC":
self.assertEqual(row.credit, 100)
self.assertEqual(row.credit_in_account_currency, 100)
self.assertEqual(row.credit_in_transaction_currency, 100)
def test_transaction_exchange_rate_on_journals(self):
jv = make_journal_entry("_Test Bank - _TC", "_Test Receivable USD - _TC", 100, save=False)
jv.accounts[0].update({"debit_in_account_currency": 8500, "exchange_rate": 1})

View File

@@ -26,6 +26,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) {
@@ -181,6 +185,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,
},
};
});
@@ -316,11 +324,6 @@ frappe.ui.form.on("Payment Entry", {
"write_off_difference_amount",
frm.doc.difference_amount && frm.doc.party && frm.doc.total_allocated_amount > party_amount
);
frm.toggle_display(
"set_exchange_gain_loss",
frm.doc.paid_amount && frm.doc.received_amount && frm.doc.difference_amount
);
},
set_dynamic_labels: function (frm) {
@@ -403,6 +406,8 @@ frappe.ui.form.on("Payment Entry", {
},
payment_type: function (frm) {
set_default_party_type(frm);
if (frm.doc.payment_type == "Internal Transfer") {
$.each(
[
@@ -1109,36 +1114,34 @@ frappe.ui.form.on("Payment Entry", {
},
set_unallocated_amount: function (frm) {
var unallocated_amount = 0;
var total_deductions = frappe.utils.sum(
$.map(frm.doc.deductions || [], function (d) {
return flt(d.amount);
})
);
let unallocated_amount = 0;
let deductions_to_consider = 0;
for (const row of frm.doc.deductions || []) {
if (!row.is_exchange_gain_loss) deductions_to_consider += flt(row.amount);
}
const included_taxes = get_included_taxes(frm);
if (frm.doc.party) {
if (
frm.doc.payment_type == "Receive" &&
frm.doc.base_total_allocated_amount < frm.doc.base_received_amount + total_deductions &&
frm.doc.total_allocated_amount <
frm.doc.paid_amount + total_deductions / frm.doc.source_exchange_rate
) {
unallocated_amount =
(frm.doc.base_received_amount +
total_deductions -
flt(frm.doc.base_total_taxes_and_charges) -
frm.doc.base_total_allocated_amount) /
frm.doc.source_exchange_rate;
} else if (
frm.doc.payment_type == "Pay" &&
frm.doc.base_total_allocated_amount < frm.doc.base_paid_amount - total_deductions &&
frm.doc.total_allocated_amount <
frm.doc.received_amount + total_deductions / frm.doc.target_exchange_rate
frm.doc.base_total_allocated_amount < frm.doc.base_paid_amount + deductions_to_consider
) {
unallocated_amount =
(frm.doc.base_paid_amount +
flt(frm.doc.base_total_taxes_and_charges) -
(total_deductions + frm.doc.base_total_allocated_amount)) /
deductions_to_consider -
frm.doc.base_total_allocated_amount -
included_taxes) /
frm.doc.source_exchange_rate;
} else if (
frm.doc.payment_type == "Pay" &&
frm.doc.base_total_allocated_amount < frm.doc.base_received_amount - deductions_to_consider
) {
unallocated_amount =
(frm.doc.base_received_amount -
deductions_to_consider -
frm.doc.base_total_allocated_amount -
included_taxes) /
frm.doc.target_exchange_rate;
}
}
@@ -1232,77 +1235,85 @@ frappe.ui.form.on("Payment Entry", {
},
write_off_difference_amount: function (frm) {
frm.events.set_deductions_entry(frm, "write_off_account");
frm.events.set_write_off_deduction(frm);
},
set_exchange_gain_loss: function (frm) {
frm.events.set_deductions_entry(frm, "exchange_gain_loss_account");
base_paid_amount: function (frm) {
frm.events.set_exchange_gain_loss_deduction(frm);
},
set_deductions_entry: function (frm, account) {
if (frm.doc.difference_amount) {
frappe.call({
method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_company_defaults",
args: {
company: frm.doc.company,
},
callback: function (r, rt) {
if (r.message) {
const write_off_row = $.map(frm.doc["deductions"] || [], function (t) {
return t.account == r.message[account] ? t : null;
});
base_received_amount: function (frm) {
frm.events.set_exchange_gain_loss_deduction(frm);
},
const difference_amount = flt(
frm.doc.difference_amount,
precision("difference_amount")
);
set_exchange_gain_loss_deduction: async function (frm) {
// wait for allocate_party_amount_against_ref_docs to finish
await frappe.after_ajax();
const base_paid_amount = frm.doc.base_paid_amount || 0;
const base_received_amount = frm.doc.base_received_amount || 0;
const exchange_gain_loss = flt(
base_paid_amount - base_received_amount,
get_deduction_amount_precision()
);
const add_deductions = (details) => {
let row = null;
if (!write_off_row.length && difference_amount) {
row = frm.add_child("deductions");
row.account = details[account];
row.cost_center = details["cost_center"];
} else {
row = write_off_row[0];
}
if (row) {
row.amount = flt(row.amount) + difference_amount;
} else {
frappe.msgprint(__("No gain or loss in the exchange rate"));
}
refresh_field("deductions");
};
if (!r.message[account]) {
frappe.prompt(
{
label: __("Please Specify Account"),
fieldname: account,
fieldtype: "Link",
options: "Account",
get_query: () => ({
filters: {
company: frm.doc.company,
},
}),
},
(values) => {
const details = Object.assign({}, r.message, values);
add_deductions(details);
},
__(frappe.unscrub(account))
);
} else {
add_deductions(r.message);
}
frm.events.set_unallocated_amount(frm);
}
},
});
if (!exchange_gain_loss) {
frm.events.delete_exchange_gain_loss(frm);
return;
}
const account_fieldname = "exchange_gain_loss_account";
let row = (frm.doc.deductions || []).find((t) => t.is_exchange_gain_loss);
if (!row) {
const response = await get_company_defaults(frm.doc.company);
const account =
response.message?.[account_fieldname] ||
(await prompt_for_missing_account(frm, account_fieldname));
row = frm.add_child("deductions");
row.account = account;
row.cost_center = response.message?.cost_center;
row.is_exchange_gain_loss = 1;
}
row.amount = exchange_gain_loss;
frm.refresh_field("deductions");
frm.events.set_unallocated_amount(frm);
},
delete_exchange_gain_loss: function (frm) {
const exchange_gain_loss_row = (frm.doc.deductions || []).find((row) => row.is_exchange_gain_loss);
if (!exchange_gain_loss_row) return;
exchange_gain_loss_row.amount = 0;
frm.get_field("deductions").grid.grid_rows[exchange_gain_loss_row.idx - 1].remove();
frm.refresh_field("deductions");
},
set_write_off_deduction: async function (frm) {
const difference_amount = flt(frm.doc.difference_amount, get_deduction_amount_precision());
if (!difference_amount) return;
const account_fieldname = "write_off_account";
const response = await get_company_defaults(frm.doc.company);
const write_off_account =
response.message?.[account_fieldname] ||
(await prompt_for_missing_account(frm, account_fieldname));
if (!write_off_account) return;
let row = (frm.doc["deductions"] || []).find((t) => t.account == write_off_account);
if (!row) {
row = frm.add_child("deductions");
row.account = write_off_account;
row.cost_center = response.message?.cost_center;
}
row.amount = flt(row.amount) + difference_amount;
frm.refresh_field("deductions");
frm.events.set_unallocated_amount(frm);
},
bank_account: function (frm) {
@@ -1768,6 +1779,13 @@ frappe.ui.form.on("Advance Taxes and Charges", {
});
frappe.ui.form.on("Payment Entry Deduction", {
before_deductions_remove: function (doc, cdt, cdn) {
const row = frappe.get_doc(cdt, cdn);
if (row.is_exchange_gain_loss && row.amount) {
frappe.throw(__("Cannot delete Exchange Gain/Loss row"));
}
},
amount: function (frm) {
frm.events.set_unallocated_amount(frm);
},
@@ -1776,3 +1794,66 @@ 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);
}
function get_included_taxes(frm) {
let included_taxes = 0;
for (const tax of frm.doc.taxes) {
if (!tax.included_in_paid_amount) continue;
if (tax.add_deduct_tax == "Add") {
included_taxes += tax.base_tax_amount;
} else {
included_taxes -= tax.base_tax_amount;
}
}
return included_taxes;
}
function get_company_defaults(company) {
return frappe.call({
method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_company_defaults",
args: {
company: company,
},
});
}
function prompt_for_missing_account(frm, account) {
return new Promise((resolve) => {
const dialog = frappe.prompt(
{
label: __(frappe.unscrub(account)),
fieldname: account,
fieldtype: "Link",
options: "Account",
get_query: () => ({
filters: {
company: frm.doc.company,
},
}),
},
(values) => resolve(values?.[account]),
__("Please Specify Account")
);
dialog.on_hide = () => resolve("");
});
}
function get_deduction_amount_precision() {
return frappe.meta.get_field_precision(frappe.meta.get_field("Payment Entry Deduction", "amount"));
}

View File

@@ -56,7 +56,6 @@
"section_break_34",
"total_allocated_amount",
"base_total_allocated_amount",
"set_exchange_gain_loss",
"column_break_36",
"unallocated_amount",
"difference_amount",
@@ -390,11 +389,6 @@
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "set_exchange_gain_loss",
"fieldtype": "Button",
"label": "Set Exchange Gain / Loss"
},
{
"fieldname": "column_break_36",
"fieldtype": "Column Break"
@@ -801,7 +795,7 @@
"table_fieldname": "payment_entries"
}
],
"modified": "2024-05-31 17:07:06.197249",
"modified": "2024-11-07 11:19:19.320883",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Entry",

View File

@@ -110,6 +110,7 @@ class PaymentEntry(AccountsController):
self.update_outstanding_amounts()
self.update_payment_schedule()
self.update_payment_requests()
self.make_advance_payment_ledger_entries()
self.update_advance_paid() # advance_paid_status depends on the payment request amount
self.set_status()
@@ -190,6 +191,7 @@ class PaymentEntry(AccountsController):
"Repost Accounting Ledger Items",
"Unreconcile Payment",
"Unreconcile Payment Entries",
"Advance Payment Ledger Entry",
)
super().on_cancel()
self.make_gl_entries(cancel=1)
@@ -197,6 +199,7 @@ class PaymentEntry(AccountsController):
self.delink_advance_entry_references()
self.update_payment_schedule(cancel=1)
self.update_payment_requests(cancel=True)
self.make_advance_payment_ledger_entries()
self.update_advance_paid() # advance_paid_status depends on the payment request amount
self.set_status()
@@ -890,6 +893,7 @@ class PaymentEntry(AccountsController):
self.set_amounts_in_company_currency()
self.set_total_allocated_amount()
self.set_unallocated_amount()
self.set_exchange_gain_loss()
self.set_difference_amount()
def validate_amounts(self):
@@ -985,10 +989,10 @@ class PaymentEntry(AccountsController):
if d.exchange_rate is None:
d.exchange_rate = 1
allocated_amount_in_pe_exchange_rate = flt(
allocated_amount_in_ref_exchange_rate = flt(
flt(d.allocated_amount) * flt(d.exchange_rate), self.precision("base_paid_amount")
)
d.exchange_gain_loss = base_allocated_amount - allocated_amount_in_pe_exchange_rate
d.exchange_gain_loss = base_allocated_amount - allocated_amount_in_ref_exchange_rate
return base_allocated_amount
def set_total_allocated_amount(self):
@@ -1006,29 +1010,80 @@ class PaymentEntry(AccountsController):
def set_unallocated_amount(self):
self.unallocated_amount = 0
if self.party:
total_deductions = sum(flt(d.amount) for d in self.get("deductions"))
included_taxes = self.get_included_taxes()
if (
self.payment_type == "Receive"
and self.base_total_allocated_amount < self.base_received_amount + total_deductions
and self.total_allocated_amount
< flt(self.paid_amount) + (total_deductions / self.source_exchange_rate)
):
self.unallocated_amount = (
self.base_received_amount + total_deductions - self.base_total_allocated_amount
) / self.source_exchange_rate
self.unallocated_amount -= included_taxes
elif (
self.payment_type == "Pay"
and self.base_total_allocated_amount < (self.base_paid_amount - total_deductions)
and self.total_allocated_amount
< flt(self.received_amount) + (total_deductions / self.target_exchange_rate)
):
self.unallocated_amount = (
self.base_paid_amount - (total_deductions + self.base_total_allocated_amount)
) / self.target_exchange_rate
self.unallocated_amount -= included_taxes
if not self.party:
return
deductions_to_consider = sum(
flt(d.amount) for d in self.get("deductions") if not d.is_exchange_gain_loss
)
included_taxes = self.get_included_taxes()
if self.payment_type == "Receive" and self.base_total_allocated_amount < (
self.base_paid_amount + deductions_to_consider
):
self.unallocated_amount = (
self.base_paid_amount
+ deductions_to_consider
- self.base_total_allocated_amount
- included_taxes
) / self.source_exchange_rate
elif self.payment_type == "Pay" and self.base_total_allocated_amount < (
self.base_received_amount - deductions_to_consider
):
self.unallocated_amount = (
self.base_received_amount
- deductions_to_consider
- self.base_total_allocated_amount
- included_taxes
) / self.target_exchange_rate
def set_exchange_gain_loss(self):
exchange_gain_loss = flt(
self.base_paid_amount - self.base_received_amount,
self.precision("amount", "deductions"),
)
exchange_gain_loss_rows = [row for row in self.get("deductions") if row.is_exchange_gain_loss]
exchange_gain_loss_row = exchange_gain_loss_rows.pop(0) if exchange_gain_loss_rows else None
for row in exchange_gain_loss_rows:
self.remove(row)
if not exchange_gain_loss:
if exchange_gain_loss_row:
self.remove(exchange_gain_loss_row)
return
if not exchange_gain_loss_row:
values = frappe.get_cached_value(
"Company", self.company, ("exchange_gain_loss_account", "cost_center"), as_dict=True
)
for fieldname, value in values.items():
if value:
continue
label = _(frappe.get_meta("Company").get_label(fieldname))
return frappe.msgprint(
_("Please set {0} in Company {1} to account for Exchange Gain / Loss").format(
label, get_link_to_form("Company", self.company)
),
title=_("Missing Default in Company"),
indicator="red" if self.docstatus.is_submitted() else "yellow",
raise_exception=self.docstatus.is_submitted(),
)
exchange_gain_loss_row = self.append(
"deductions",
{
"account": values.exchange_gain_loss_account,
"cost_center": values.cost_center,
"is_exchange_gain_loss": 1,
},
)
exchange_gain_loss_row.amount = exchange_gain_loss
def set_difference_amount(self):
base_unallocated_amount = flt(self.unallocated_amount) * (
@@ -1056,11 +1111,13 @@ class PaymentEntry(AccountsController):
def get_included_taxes(self):
included_taxes = 0
for tax in self.get("taxes"):
if tax.included_in_paid_amount:
if tax.add_deduct_tax == "Add":
included_taxes += tax.base_tax_amount
else:
included_taxes -= tax.base_tax_amount
if not tax.included_in_paid_amount:
continue
if tax.add_deduct_tax == "Add":
included_taxes += tax.base_tax_amount
else:
included_taxes -= tax.base_tax_amount
return included_taxes
@@ -1143,6 +1200,12 @@ class PaymentEntry(AccountsController):
if self.payment_type in ("Receive", "Pay") and not self.get("party_account_field"):
self.setup_party_account_field()
company_currency = erpnext.get_company_currency(self.company)
if self.paid_from_account_currency != company_currency:
self.currency = self.paid_from_account_currency
elif self.paid_to_account_currency != company_currency:
self.currency = self.paid_to_account_currency
gl_entries = []
self.add_party_gl_entries(gl_entries)
self.add_bank_gl_entries(gl_entries)
@@ -1210,11 +1273,19 @@ class PaymentEntry(AccountsController):
dr_or_cr = "debit" if dr_or_cr == "credit" else "credit"
gle.update(
{
dr_or_cr: allocated_amount_in_company_currency,
dr_or_cr + "_in_account_currency": d.allocated_amount,
"cost_center": cost_center,
}
self.get_gl_dict(
{
"account": self.party_account,
"party_type": self.party_type,
"party": self.party,
"against": against_account,
"account_currency": self.party_account_currency,
"cost_center": cost_center,
dr_or_cr + "_in_account_currency": d.allocated_amount,
dr_or_cr: allocated_amount_in_company_currency,
},
item=self,
)
)
if self.book_advance_payments_in_separate_party_account:
@@ -1245,13 +1316,22 @@ class PaymentEntry(AccountsController):
base_unallocated_amount = self.unallocated_amount * exchange_rate
gle = party_gl_dict.copy()
gle.update(
{
dr_or_cr + "_in_account_currency": self.unallocated_amount,
dr_or_cr: base_unallocated_amount,
}
)
gle.update(
self.get_gl_dict(
{
"account": self.party_account,
"party_type": self.party_type,
"party": self.party,
"against": against_account,
"account_currency": self.party_account_currency,
"cost_center": self.cost_center,
dr_or_cr + "_in_account_currency": self.unallocated_amount,
dr_or_cr: base_unallocated_amount,
},
item=self,
)
)
if self.book_advance_payments_in_separate_party_account:
gle.update(
{
@@ -1728,7 +1808,7 @@ class PaymentEntry(AccountsController):
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,
)
@@ -1886,8 +1966,8 @@ class PaymentEntry(AccountsController):
def get_matched_payment_request_of_references(references=None):
"""
Get those `Payment Requests` which are matched with `References`.\n
- Amount must be same.
- Only single `Payment Request` available for this amount.
- Amount must be same.
- Only single `Payment Request` available for this amount.
Example: [(reference_doctype, reference_name, allocated_amount, payment_request), ...]
"""
@@ -1989,7 +2069,7 @@ def get_outstanding_of_references_with_payment_term(references=None):
def get_outstanding_of_references_with_no_payment_term(references):
"""
Fetch outstanding amount of `References` which have no `Payment Term` set.\n
- Fetch outstanding amount from `References` it self.
- Fetch outstanding amount from `References` it self.
Note: `None` is used for allocation of `Payment Request`
Example: {(reference_doctype, reference_name, None): outstanding_amount, ...}
@@ -2803,9 +2883,6 @@ def get_payment_entry(
update_accounting_dimensions(pe, doc)
if party_account and bank:
pe.set_exchange_rate(ref_doc=doc)
pe.set_amounts()
if discount_amount:
base_total_discount_loss = 0
if frappe.db.get_single_value("Accounts Settings", "book_tax_discount_loss"):
@@ -2815,7 +2892,8 @@ def get_payment_entry(
pe, doc, discount_amount, base_total_discount_loss, party_account_currency
)
pe.set_difference_amount()
pe.set_exchange_rate(ref_doc=doc)
pe.set_amounts()
# If PE is created from PR directly, then no need to find open PRs for the references
if not created_from_payment_request:
@@ -2827,7 +2905,7 @@ def get_payment_entry(
def get_open_payment_requests_for_references(references=None):
"""
Fetch all unpaid Payment Requests for the references. \n
- Each reference can have multiple Payment Requests. \n
- Each reference can have multiple Payment Requests. \n
Example: {("Sales Invoice", "SINV-00001"): {"PREQ-00001": 1000, "PREQ-00002": 2000}}
"""
@@ -2851,6 +2929,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)
@@ -3161,13 +3240,14 @@ def set_pending_discount_loss(pe, doc, discount_amount, base_total_discount_loss
book_tax_loss = frappe.db.get_single_value("Accounts Settings", "book_tax_discount_loss")
account_type = "round_off_account" if book_tax_loss else "default_discount_account"
pe.set_gain_or_loss(
account_details={
pe.append(
"deductions",
{
"account": frappe.get_cached_value("Company", pe.company, account_type),
"cost_center": pe.cost_center
or frappe.get_cached_value("Company", pe.company, "cost_center"),
"amount": discount_amount * positive_negative,
}
},
)

View File

@@ -479,16 +479,9 @@ class TestPaymentEntry(FrappeTestCase):
self.assertEqual(pe.deductions[0].account, "Write Off - _TC")
# Exchange loss
self.assertEqual(pe.difference_amount, 300.0)
pe.append(
"deductions",
{
"account": "_Test Exchange Gain/Loss - _TC",
"cost_center": "_Test Cost Center - _TC",
"amount": 300.0,
},
)
self.assertEqual(pe.deductions[-1].amount, 300.0)
pe.deductions[-1].account = "_Test Exchange Gain/Loss - _TC"
pe.deductions[-1].cost_center = "_Test Cost Center - _TC"
pe.insert()
pe.submit()
@@ -552,16 +545,10 @@ class TestPaymentEntry(FrappeTestCase):
pe.reference_no = "1"
pe.reference_date = "2016-01-01"
self.assertEqual(pe.difference_amount, 100)
self.assertEqual(pe.deductions[0].amount, 100)
pe.deductions[0].account = "_Test Exchange Gain/Loss - _TC"
pe.deductions[0].cost_center = "_Test Cost Center - _TC"
pe.append(
"deductions",
{
"account": "_Test Exchange Gain/Loss - _TC",
"cost_center": "_Test Cost Center - _TC",
"amount": 100,
},
)
pe.insert()
pe.submit()
@@ -654,16 +641,9 @@ class TestPaymentEntry(FrappeTestCase):
pe.set_exchange_rate()
pe.set_amounts()
self.assertEqual(pe.difference_amount, 500)
pe.append(
"deductions",
{
"account": "_Test Exchange Gain/Loss - _TC",
"cost_center": "_Test Cost Center - _TC",
"amount": 500,
},
)
self.assertEqual(pe.deductions[0].amount, 500)
pe.deductions[0].account = "_Test Exchange Gain/Loss - _TC"
pe.deductions[0].cost_center = "_Test Cost Center - _TC"
pe.insert()
pe.submit()
@@ -956,6 +936,53 @@ class TestPaymentEntry(FrappeTestCase):
self.assertEqual(flt(expected_party_balance), party_balance)
self.assertEqual(flt(expected_party_account_balance, 2), flt(party_account_balance, 2))
def test_gl_of_multi_currency_payment_transaction(self):
from erpnext.setup.doctype.currency_exchange.test_currency_exchange import (
save_new_records,
test_records,
)
save_new_records(test_records)
paid_from = create_account(
parent_account="Current Liabilities - _TC",
account_name="_Test Cash USD",
company="_Test Company",
account_type="Cash",
account_currency="USD",
)
payment_entry = create_payment_entry(
party="_Test Supplier USD",
paid_from=paid_from,
paid_to="_Test Payable USD - _TC",
paid_amount=100,
save=True,
)
payment_entry.source_exchange_rate = 84.4
payment_entry.target_exchange_rate = 84.4
payment_entry.save()
payment_entry = payment_entry.submit()
gle = qb.DocType("GL Entry")
gl_entries = (
qb.from_(gle)
.select(
gle.account,
gle.debit,
gle.credit,
gle.debit_in_account_currency,
gle.credit_in_account_currency,
gle.debit_in_transaction_currency,
gle.credit_in_transaction_currency,
)
.orderby(gle.account)
.where(gle.voucher_no == payment_entry.name)
.run()
)
expected_gl_entries = (
(paid_from, 0.0, 8440.0, 0.0, 100.0, 0.0, 100.0),
("_Test Payable USD - _TC", 8440.0, 0.0, 100.0, 0.0, 100.0, 0.0),
)
self.assertEqual(gl_entries, expected_gl_entries)
def test_multi_currency_payment_entry_with_taxes(self):
payment_entry = create_payment_entry(
party="_Test Supplier USD", paid_to="_Test Payable USD - _TC", save=True

View File

@@ -9,6 +9,7 @@
"cost_center",
"amount",
"column_break_2",
"is_exchange_gain_loss",
"description"
],
"fields": [
@@ -45,12 +46,20 @@
"fieldname": "description",
"fieldtype": "Small Text",
"label": "Description"
},
{
"default": "0",
"depends_on": "eval:doc.is_exchange_gain_loss",
"fieldname": "is_exchange_gain_loss",
"fieldtype": "Check",
"label": "Is Exchange Gain / Loss?",
"read_only": 1
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2023-03-06 07:11:57.739619",
"modified": "2024-11-05 16:07:47.307971",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Entry Deduction",

View File

@@ -18,6 +18,7 @@ class PaymentEntryDeduction(Document):
amount: DF.Currency
cost_center: DF.Link
description: DF.SmallText | None
is_exchange_gain_loss: DF.Check
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data

View File

@@ -211,12 +211,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}%%"))
@@ -231,7 +233,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"),
@@ -323,6 +325,7 @@ class PaymentReconciliation(Document):
"posting_date": inv.posting_date,
"currency": inv.currency,
"cost_center": inv.cost_center,
"remarks": inv.remarks,
}
)
)
@@ -370,6 +373,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):

View File

@@ -632,6 +632,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"
@@ -954,6 +990,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

@@ -14,7 +14,7 @@
"amount",
"difference_amount",
"sec_break1",
"remark",
"remarks",
"currency",
"exchange_rate",
"cost_center"
@@ -74,12 +74,6 @@
"fieldname": "sec_break1",
"fieldtype": "Section Break"
},
{
"fieldname": "remark",
"fieldtype": "Small Text",
"label": "Remark",
"read_only": 1
},
{
"fieldname": "currency",
"fieldtype": "Link",
@@ -105,12 +99,18 @@
"fieldtype": "Link",
"label": "Cost Center",
"options": "Cost Center"
},
{
"fieldname": "remarks",
"fieldtype": "Small Text",
"label": "Remarks",
"read_only": 1
}
],
"is_virtual": 1,
"istable": 1,
"links": [],
"modified": "2023-11-17 17:33:34.818530",
"modified": "2024-10-29 16:24:43.021230",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Reconciliation Payment",

View File

@@ -27,7 +27,7 @@ class PaymentReconciliationPayment(Document):
reference_name: DF.DynamicLink | None
reference_row: DF.Data | None
reference_type: DF.Link | None
remark: DF.SmallText | None
remarks: DF.SmallText | None
# end: auto-generated types
@staticmethod

View File

@@ -14,6 +14,7 @@
"party_details",
"party_type",
"party",
"party_name",
"column_break_4",
"reference_doctype",
"reference_name",
@@ -422,13 +423,19 @@
"label": "Party Account Currency",
"options": "Currency",
"read_only": 1
},
{
"fieldname": "party_name",
"fieldtype": "Data",
"label": "Party Name",
"read_only": 1
}
],
"in_create": 1,
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2024-09-16 17:50:54.440090",
"modified": "2024-10-23 12:23:40.117336",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Request",
@@ -467,4 +474,4 @@
"sort_field": "modified",
"sort_order": "DESC",
"states": []
}
}

View File

@@ -1,9 +1,9 @@
import json
import frappe
from frappe import _
from frappe import _, qb
from frappe.model.document import Document
from frappe.query_builder.functions import Sum
from frappe.query_builder.functions import Abs, Sum
from frappe.utils import flt, nowdate
from frappe.utils.background_jobs import enqueue
@@ -71,6 +71,7 @@ class PaymentRequest(Document):
outstanding_amount: DF.Currency
party: DF.DynamicLink | None
party_account_currency: DF.Link | None
party_name: DF.Data | None
party_type: DF.Link | None
payment_account: DF.ReadOnly | None
payment_channel: DF.Literal["", "Email", "Phone", "Other"]
@@ -303,12 +304,12 @@ class PaymentRequest(Document):
return controller.get_payment_url(
**{
"amount": flt(self.grand_total, self.precision("grand_total")),
"title": data.company.encode("utf-8"),
"description": self.subject.encode("utf-8"),
"title": data.company,
"description": self.subject,
"reference_doctype": "Payment Request",
"reference_docname": self.name,
"payer_email": self.email_to or frappe.session.user,
"payer_name": frappe.safe_encode(data.customer_name),
"payer_name": data.customer_name,
"order_id": self.name,
"currency": self.currency,
}
@@ -563,11 +564,35 @@ def make_payment_request(**args):
# fetches existing payment request `grand_total` amount
existing_payment_request_amount = get_existing_payment_request_amount(ref_doc.doctype, ref_doc.name)
if existing_payment_request_amount:
grand_total -= existing_payment_request_amount
existing_paid_amount = get_existing_paid_amount(ref_doc.doctype, ref_doc.name)
def validate_and_calculate_grand_total(grand_total, existing_payment_request_amount):
grand_total -= existing_payment_request_amount
if not grand_total:
frappe.throw(_("Payment Request is already created"))
return grand_total
if existing_payment_request_amount:
if args.order_type == "Shopping Cart":
# If Payment Request is in an advanced stage, then create for remaining amount.
if get_existing_payment_request_amount(
ref_doc.doctype, ref_doc.name, ["Initiated", "Partially Paid", "Payment Ordered", "Paid"]
):
grand_total = validate_and_calculate_grand_total(grand_total, existing_payment_request_amount)
else:
# If PR's are processed, cancel all of them.
cancel_old_payment_requests(ref_doc.doctype, ref_doc.name)
else:
grand_total = validate_and_calculate_grand_total(grand_total, existing_payment_request_amount)
if existing_paid_amount:
if ref_doc.party_account_currency == ref_doc.currency:
if ref_doc.conversion_rate:
grand_total -= flt(existing_paid_amount / ref_doc.conversion_rate)
else:
grand_total -= flt(existing_paid_amount)
else:
grand_total -= flt(existing_paid_amount / ref_doc.conversion_rate)
if draft_payment_request:
frappe.db.set_value(
@@ -609,6 +634,7 @@ def make_payment_request(**args):
"party_type": party_type,
"party": args.get("party") or ref_doc.get("customer"),
"bank_account": bank_account,
"party_name": args.get("party_name") or ref_doc.get("customer_name"),
}
)
@@ -676,21 +702,88 @@ def get_amount(ref_doc, payment_account=None):
frappe.throw(_("Payment Entry is already created"))
def get_existing_payment_request_amount(ref_dt, ref_dn):
def get_irequest_status(payment_requests: None | list = None) -> list:
IR = frappe.qb.DocType("Integration Request")
res = []
if payment_requests:
res = (
frappe.qb.from_(IR)
.select(IR.name)
.where(IR.reference_doctype.eq("Payment Request"))
.where(IR.reference_docname.isin(payment_requests))
.where(IR.status.isin(["Authorized", "Completed"]))
.run(as_dict=True)
)
return res
def cancel_old_payment_requests(ref_dt, ref_dn):
PR = frappe.qb.DocType("Payment Request")
if res := (
frappe.qb.from_(PR)
.select(PR.name)
.where(PR.reference_doctype == ref_dt)
.where(PR.reference_name == ref_dn)
.where(PR.docstatus == 1)
.where(PR.status.isin(["Draft", "Requested"]))
.run(as_dict=True)
):
if get_irequest_status([x.name for x in res]):
frappe.throw(_("Another Payment Request is already processed"))
else:
for x in res:
doc = frappe.get_doc("Payment Request", x.name)
doc.flags.ignore_permissions = True
doc.cancel()
if ireqs := get_irequests_of_payment_request(doc.name):
for ireq in ireqs:
frappe.db.set_value("Integration Request", ireq.name, "status", "Cancelled")
def get_existing_payment_request_amount(ref_dt, ref_dn, statuses: list | None = None) -> list:
"""
Return the total amount of Payment Requests against a reference document.
"""
PR = frappe.qb.DocType("Payment Request")
response = (
query = (
frappe.qb.from_(PR)
.select(Sum(PR.grand_total))
.where(PR.reference_doctype == ref_dt)
.where(PR.reference_name == ref_dn)
.where(PR.docstatus == 1)
.run()
)
if statuses:
query = query.where(PR.status.isin(statuses))
response = query.run()
return response[0][0] if response[0] else 0
def get_existing_paid_amount(doctype, name):
PL = frappe.qb.DocType("Payment Ledger Entry")
PER = frappe.qb.DocType("Payment Entry Reference")
query = (
frappe.qb.from_(PL)
.left_join(PER)
.on(
(PER.reference_doctype == PL.against_voucher_type) & (PER.reference_name == PL.against_voucher_no)
)
.select(Abs(Sum(PL.amount)).as_("total_paid_amount"))
.where(PL.against_voucher_type.eq(doctype))
.where(PL.against_voucher_no.eq(name))
.where(PL.amount < 0)
.where(PL.delinked == 0)
.where(PER.docstatus == 1)
.where(PER.payment_request.isnull())
)
response = query.run()
return response[0][0] if response[0] else 0
@@ -886,21 +979,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",
)
@@ -913,3 +1002,17 @@ def get_open_payment_requests_query(doctype, txt, searchfield, start, page_len,
)
for pr in open_payment_requests
]
def get_irequests_of_payment_request(doc: str | None = None) -> list:
res = []
if doc:
res = frappe.db.get_all(
"Integration Request",
{
"reference_doctype": "Payment Request",
"reference_docname": doc,
"status": "Queued",
},
)
return res

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

@@ -7,6 +7,7 @@ import unittest
import frappe
from frappe.tests.utils import FrappeTestCase, change_settings
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_terms_template
from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
@@ -524,3 +525,48 @@ class TestPaymentRequest(FrappeTestCase):
self.assertEqual(pr.grand_total, 1000)
so.load_from_db()
def test_partial_paid_invoice_with_payment_request(self):
si = create_sales_invoice(currency="INR", qty=1, rate=5000)
si.save()
si.submit()
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC")
pe.reference_no = "PAYEE0002"
pe.reference_date = frappe.utils.nowdate()
pe.paid_amount = 2500
pe.references[0].allocated_amount = 2500
pe.save()
pe.submit()
si.load_from_db()
pr = make_payment_request(dt="Sales Invoice", dn=si.name, mute_email=1)
self.assertEqual(pr.grand_total, si.outstanding_amount)
def test_partial_paid_invoice_with_submitted_payment_entry(self):
pi = make_purchase_invoice(currency="INR", qty=1, rate=5000)
pi.save()
pi.submit()
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
pe.reference_no = "PURINV0001"
pe.reference_date = frappe.utils.nowdate()
pe.paid_amount = 2500
pe.references[0].allocated_amount = 2500
pe.save()
pe.submit()
pe.cancel()
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
pe.reference_no = "PURINV0002"
pe.reference_date = frappe.utils.nowdate()
pe.paid_amount = 2500
pe.references[0].allocated_amount = 2500
pe.save()
pe.submit()
pi.load_from_db()
pr = make_payment_request(dt="Purchase Invoice", dn=pi.name, mute_email=1)
self.assertEqual(pr.grand_total, pi.outstanding_amount)

View File

@@ -171,9 +171,7 @@ class PeriodClosingVoucher(AccountsController):
pl_account_balances = self.get_account_balances_based_on_dimensions(report_type="Profit and Loss")
for dimensions, account_balances in pl_account_balances.items():
for acc, balances in account_balances.items():
balance_in_company_currency = flt(balances.debit_in_account_currency) - flt(
balances.credit_in_account_currency
)
balance_in_company_currency = flt(balances.debit) - flt(balances.credit)
if balance_in_company_currency and acc != "balances":
self.pl_accounts_reverse_gle.append(
self.get_gle_for_pl_account(acc, balances, dimensions)
@@ -354,7 +352,8 @@ class PeriodClosingVoucher(AccountsController):
def get_account_closing_balances(self):
pl_closing_entries = self.get_closing_entries_for_pl_accounts()
bs_closing_entries = self.get_closing_entries_for_balance_sheet_accounts()
closing_entries = pl_closing_entries + bs_closing_entries
closing_entries_for_closing_account = self.get_closing_entries_for_closing_account()
closing_entries = pl_closing_entries + bs_closing_entries + closing_entries_for_closing_account
return closing_entries
def get_closing_entries_for_pl_accounts(self):
@@ -380,9 +379,7 @@ class PeriodClosingVoucher(AccountsController):
for dimensions, account_balances in balance_sheet_account_balances.items():
for acc, balances in account_balances.items():
balance_in_company_currency = flt(balances.debit_in_account_currency) - flt(
balances.credit_in_account_currency
)
balance_in_company_currency = flt(balances.debit) - flt(balances.credit)
if acc != "balances" and balance_in_company_currency:
closing_entries.append(self.get_closing_entry(acc, balances, dimensions))
@@ -406,12 +403,24 @@ class PeriodClosingVoucher(AccountsController):
self.update_default_dimensions(closing_entry, dimensions)
return closing_entry
def get_closing_entries_for_closing_account(self):
closing_entries = copy.deepcopy(self.closing_account_gle)
for d in closing_entries:
d.period_closing_voucher = self.name
return closing_entries
def is_first_period_closing_voucher(self):
return not frappe.db.exists(
first_pcv = frappe.db.get_value(
"Period Closing Voucher",
{"company": self.company, "docstatus": 1, "name": ("!=", self.name)},
{"company": self.company, "docstatus": 1},
"name",
order_by="period_end_date asc",
)
if not first_pcv or first_pcv == self.name:
return True
def cancel_gl_entries(self):
if self.get_gle_count_against_current_pcv() > 5000:
frappe.enqueue(
@@ -444,8 +453,7 @@ def process_gl_and_closing_entries(doc):
make_gl_entries(gl_entries, merge_entries=False)
closing_entries = doc.get_account_closing_balances()
if closing_entries:
make_closing_entries(closing_entries, doc.name, doc.company, doc.period_end_date)
make_closing_entries(closing_entries, doc.name, doc.company, doc.period_end_date)
frappe.db.set_value(doc.doctype, doc.name, "gle_processing_status", "Completed")
except Exception as e:

View File

@@ -147,7 +147,7 @@ frappe.ui.form.on("POS Closing Entry", {
frm.doc.grand_total += flt(doc.grand_total);
frm.doc.net_total += flt(doc.net_total);
frm.doc.total_quantity += flt(doc.total_qty);
refresh_payments(doc, frm);
refresh_payments(doc, frm, false);
refresh_taxes(doc, frm);
refresh_fields(frm);
set_html_data(frm);
@@ -172,7 +172,7 @@ function set_form_data(data, frm) {
frm.doc.grand_total += flt(d.grand_total);
frm.doc.net_total += flt(d.net_total);
frm.doc.total_quantity += flt(d.total_qty);
refresh_payments(d, frm);
refresh_payments(d, frm, true);
refresh_taxes(d, frm);
});
}
@@ -186,7 +186,7 @@ function add_to_pos_transaction(d, frm) {
});
}
function refresh_payments(d, frm) {
function refresh_payments(d, frm, is_new) {
d.payments.forEach((p) => {
const payment = frm.doc.payment_reconciliation.find(
(pay) => pay.mode_of_payment === p.mode_of_payment
@@ -196,9 +196,7 @@ function refresh_payments(d, frm) {
}
if (payment) {
payment.expected_amount += flt(p.amount);
if (payment.closing_amount === 0) {
payment.closing_amount = payment.expected_amount;
}
if (is_new) payment.closing_amount = payment.expected_amount;
payment.difference = payment.closing_amount - payment.expected_amount;
} else {
frm.add_child("payment_reconciliation", {

View File

@@ -65,7 +65,7 @@ erpnext.selling.POSInvoiceController = class POSInvoiceController extends erpnex
super.refresh();
if (doc.docstatus == 1 && !doc.is_return) {
this.frm.add_custom_button(__("Return"), this.make_sales_return, __("Create"));
this.frm.add_custom_button(__("Return"), this.make_sales_return.bind(this), __("Create"));
this.frm.page.set_inner_btn_group_as_primary(__("Create"));
}

View File

@@ -48,6 +48,7 @@
"shipping_address",
"company_address",
"company_address_display",
"company_contact_person",
"currency_and_price_list",
"currency",
"conversion_rate",
@@ -1558,12 +1559,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",

View File

@@ -32,12 +32,8 @@ class POSInvoice(SalesInvoice):
from erpnext.accounts.doctype.payment_schedule.payment_schedule import PaymentSchedule
from erpnext.accounts.doctype.pos_invoice_item.pos_invoice_item import POSInvoiceItem
from erpnext.accounts.doctype.pricing_rule_detail.pricing_rule_detail import PricingRuleDetail
from erpnext.accounts.doctype.sales_invoice_advance.sales_invoice_advance import (
SalesInvoiceAdvance,
)
from erpnext.accounts.doctype.sales_invoice_payment.sales_invoice_payment import (
SalesInvoicePayment,
)
from erpnext.accounts.doctype.sales_invoice_advance.sales_invoice_advance import SalesInvoiceAdvance
from erpnext.accounts.doctype.sales_invoice_payment.sales_invoice_payment import SalesInvoicePayment
from erpnext.accounts.doctype.sales_invoice_timesheet.sales_invoice_timesheet import (
SalesInvoiceTimesheet,
)
@@ -75,6 +71,7 @@ class POSInvoice(SalesInvoice):
company: DF.Link
company_address: DF.Link | None
company_address_display: DF.SmallText | None
company_contact_person: DF.Link | None
consolidated_invoice: DF.Link | None
contact_display: DF.SmallText | None
contact_email: DF.Data | None

View File

@@ -93,7 +93,7 @@ class TestPOSInvoice(unittest.TestCase):
inv.save()
self.assertEqual(inv.net_total, 4298.25)
self.assertEqual(inv.net_total, 4298.24)
self.assertEqual(inv.grand_total, 4900.00)
def test_tax_calculation_with_multiple_items(self):

View File

@@ -438,7 +438,9 @@ def split_invoices(invoices):
if not item.serial_no and not item.serial_and_batch_bundle:
continue
return_against_is_added = any(d for d in _invoices if d.pos_invoice == pos_invoice.return_against)
return_against_is_added = any(
d for d in _invoices if d and d[0].pos_invoice == pos_invoice.return_against
)
if return_against_is_added:
break

View File

@@ -343,7 +343,7 @@ class TestPOSInvoiceMergeLog(unittest.TestCase):
inv.load_from_db()
consolidated_invoice = frappe.get_doc("Sales Invoice", inv.consolidated_invoice)
self.assertEqual(consolidated_invoice.status, "Return")
self.assertEqual(consolidated_invoice.rounding_adjustment, -0.001)
self.assertEqual(consolidated_invoice.rounding_adjustment, -0.002)
finally:
frappe.set_user("Administrator")

View File

@@ -446,7 +446,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
@@ -473,9 +486,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)

View File

@@ -1137,6 +1137,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")

View File

@@ -655,7 +655,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

View File

@@ -474,6 +474,7 @@ def send_emails(document_name, from_scheduler=False, posting_date=None):
reference_doctype="Process Statement Of Accounts",
reference_name=document_name,
attachments=attachments,
expose_recipients="header",
)
if doc.enable_auto_email and from_scheduler:

View File

@@ -31,6 +31,13 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
},
};
});
this.frm.set_query("expense_account", "items", function () {
return {
query: "erpnext.controllers.queries.get_expense_account",
filters: { company: doc.company },
};
});
}
onload() {
@@ -335,7 +342,9 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
party_type: "Supplier",
account: this.frm.doc.credit_to,
price_list: this.frm.doc.buying_price_list,
fetch_payment_terms_template: cint(!this.frm.doc.ignore_default_payment_terms_template),
fetch_payment_terms_template: cint(
(this.frm.doc.is_return == 0) & !this.frm.doc.ignore_default_payment_terms_template
),
},
function () {
me.apply_pricing_rule();
@@ -506,13 +515,6 @@ cur_frm.fields_dict["select_print_heading"].get_query = function (doc, cdt, cdn)
};
};
cur_frm.set_query("expense_account", "items", function (doc) {
return {
query: "erpnext.controllers.queries.get_expense_account",
filters: { company: doc.company },
};
});
cur_frm.set_query("wip_composite_asset", "items", function () {
return {
filters: { is_composite_asset: 1, docstatus: 0 },

View File

@@ -1134,12 +1134,14 @@
"label": "Payment Terms"
},
{
"depends_on": "eval:(!doc.is_paid && !doc.is_return)",
"fieldname": "payment_terms_template",
"fieldtype": "Link",
"label": "Payment Terms Template",
"options": "Payment Terms Template"
},
{
"depends_on": "eval:(!doc.is_paid && !doc.is_return)",
"fieldname": "payment_schedule",
"fieldtype": "Table",
"label": "Payment Schedule",
@@ -1631,7 +1633,7 @@
"idx": 204,
"is_submittable": 1,
"links": [],
"modified": "2024-09-11 12:59:19.130593",
"modified": "2024-10-25 18:13:01.944477",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice",

View File

@@ -863,6 +863,7 @@ class PurchaseInvoice(BuyingController):
self.make_tax_gl_entries(gl_entries)
self.make_internal_transfer_gl_entries(gl_entries)
self.make_gl_entries_for_tax_withholding(gl_entries)
gl_entries = make_regional_gl_entries(gl_entries, self)
@@ -896,32 +897,37 @@ class PurchaseInvoice(BuyingController):
)
if grand_total and not self.is_internal_transfer():
against_voucher = self.name
if self.is_return and self.return_against and not self.update_outstanding_for_self:
against_voucher = self.return_against
self.add_supplier_gl_entry(gl_entries, base_grand_total, grand_total)
# Did not use base_grand_total to book rounding loss gle
gl_entries.append(
self.get_gl_dict(
{
"account": self.credit_to,
"party_type": "Supplier",
"party": self.supplier,
"due_date": self.due_date,
"against": self.against_expense_account,
"credit": base_grand_total,
"credit_in_account_currency": base_grand_total
if self.party_account_currency == self.company_currency
else grand_total,
"against_voucher": against_voucher,
"against_voucher_type": self.doctype,
"project": self.project,
"cost_center": self.cost_center,
},
self.party_account_currency,
item=self,
)
)
def add_supplier_gl_entry(
self, gl_entries, base_grand_total, grand_total, against_account=None, remarks=None, skip_merge=False
):
against_voucher = self.name
if self.is_return and self.return_against and not self.update_outstanding_for_self:
against_voucher = self.return_against
# Did not use base_grand_total to book rounding loss gle
gl = {
"account": self.credit_to,
"party_type": "Supplier",
"party": self.supplier,
"due_date": self.due_date,
"against": against_account or self.against_expense_account,
"credit": base_grand_total,
"credit_in_account_currency": base_grand_total
if self.party_account_currency == self.company_currency
else grand_total,
"against_voucher": against_voucher,
"against_voucher_type": self.doctype,
"project": self.project,
"cost_center": self.cost_center,
"_skip_merge": skip_merge,
}
if remarks:
gl["remarks"] = remarks
gl_entries.append(self.get_gl_dict(gl, self.party_account_currency, item=self))
def make_item_gl_entries(self, gl_entries):
# item gl entries
@@ -1413,6 +1419,31 @@ class PurchaseInvoice(BuyingController):
)
)
def make_gl_entries_for_tax_withholding(self, gl_entries):
"""
Tax withholding amount is not part of supplier invoice.
Separate supplier GL Entry for correct reporting.
"""
if not self.apply_tds:
return
for row in self.get("taxes"):
if not row.is_tax_withholding_account or not row.tax_amount:
continue
base_tds_amount = row.base_tax_amount_after_discount_amount
tds_amount = row.tax_amount_after_discount_amount
self.add_supplier_gl_entry(gl_entries, base_tds_amount, tds_amount)
self.add_supplier_gl_entry(
gl_entries,
-base_tds_amount,
-tds_amount,
against_account=row.account_head,
remarks=_("TDS Deducted"),
skip_merge=True,
)
def make_payment_gl_entries(self, gl_entries):
# Make Cash GL Entries
if cint(self.is_paid) and self.cash_bank_account and self.paid_amount:
@@ -1506,10 +1537,29 @@ class PurchaseInvoice(BuyingController):
# eg: rounding_adjustment = 0.01 and exchange rate = 0.05 and precision of base_rounding_adjustment is 2
# then base_rounding_adjustment becomes zero and error is thrown in GL Entry
if not self.is_internal_transfer() and self.rounding_adjustment and self.base_rounding_adjustment:
round_off_account, round_off_cost_center = get_round_off_account_and_cost_center(
(
round_off_account,
round_off_cost_center,
round_off_for_opening,
) = get_round_off_account_and_cost_center(
self.company, "Purchase Invoice", self.name, self.use_company_roundoff_cost_center
)
if self.is_opening == "Yes" and self.rounding_adjustment:
if not round_off_for_opening:
frappe.throw(
_(
"Opening Invoice has rounding adjustment of {0}.<br><br> '{1}' account is required to post these values. Please set it in Company: {2}.<br><br> Or, '{3}' can be enabled to not post any rounding adjustment."
).format(
frappe.bold(self.rounding_adjustment),
frappe.bold("Round Off for Opening"),
get_link_to_form("Company", self.company),
frappe.bold("Disable Rounded Total"),
)
)
else:
round_off_account = round_off_for_opening
gl_entries.append(
self.get_gl_dict(
{
@@ -1593,7 +1643,11 @@ class PurchaseInvoice(BuyingController):
for proj, value in projects.items():
res = frappe.qb.from_(pj).select(pj.total_purchase_cost).where(pj.name == proj).for_update().run()
current_purchase_cost = res and res[0][0] or 0
frappe.db.set_value("Project", proj, "total_purchase_cost", current_purchase_cost + value)
# frappe.db.set_value("Project", proj, "total_purchase_cost", current_purchase_cost + value)
project_doc = frappe.get_doc("Project", proj)
project_doc.total_purchase_cost = current_purchase_cost + value
project_doc.calculate_gross_margin()
project_doc.db_update()
def validate_supplier_invoice(self):
if self.bill_date:

View File

@@ -1544,6 +1544,61 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
payment_entry.load_from_db()
self.assertEqual(payment_entry.taxes[0].allocated_amount, 0)
def test_purchase_gl_with_tax_withholding_tax(self):
company = "_Test Company"
tds_account_args = {
"doctype": "Account",
"account_name": "TDS Payable",
"account_type": "Tax",
"parent_account": frappe.db.get_value(
"Account", {"account_name": "Duties and Taxes", "company": company}
),
"company": company,
}
tds_account = create_account(**tds_account_args)
tax_withholding_category = "Test TDS - 194 - Dividends - Individual"
# Update tax withholding category with current fiscal year and rate details
create_tax_witholding_category(tax_withholding_category, company, tds_account)
# create a new supplier to test
supplier = create_supplier(
supplier_name="_Test TDS Advance Supplier",
tax_withholding_category=tax_withholding_category,
)
pi = make_purchase_invoice(
supplier=supplier.name,
rate=3000,
qty=1,
item="_Test Non Stock Item",
do_not_submit=1,
)
pi.apply_tds = 1
pi.tax_withholding_category = tax_withholding_category
pi.save()
pi.submit()
self.assertEqual(pi.taxes[0].tax_amount, 300)
self.assertEqual(pi.taxes[0].account_head, tds_account)
gl_entries = frappe.get_all(
"GL Entry",
filters={"voucher_no": pi.name, "voucher_type": "Purchase Invoice", "account": "Creditors - _TC"},
fields=["account", "against", "debit", "credit"],
)
for gle in gl_entries:
if gle.debit:
# GL Entry with TDS Amount
self.assertEqual(gle.against, tds_account)
self.assertEqual(gle.debit, 300)
else:
# GL Entry with Purchase Invoice Amount
self.assertEqual(gle.credit, 3000)
def test_provisional_accounting_entry(self):
setup_provisional_accounting()
@@ -1680,6 +1735,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)
@@ -2310,6 +2389,65 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
item.reload()
self.assertEqual(item.last_purchase_rate, 0)
def test_opening_invoice_rounding_adjustment_validation(self):
pi = make_purchase_invoice(do_not_save=1)
pi.items[0].rate = 99.98
pi.items[0].qty = 1
pi.items[0].expense_account = "Temporary Opening - _TC"
pi.is_opening = "Yes"
pi.save()
self.assertRaises(frappe.ValidationError, pi.submit)
def _create_opening_roundoff_account(self, company_name):
liability_root = frappe.db.get_all(
"Account",
filters={"company": company_name, "root_type": "Liability", "disabled": 0},
order_by="lft",
limit=1,
)[0]
# setup round off account
if acc := frappe.db.exists(
"Account",
{
"account_name": "Round Off for Opening",
"account_type": "Round Off for Opening",
"company": company_name,
},
):
frappe.db.set_value("Company", company_name, "round_off_for_opening", acc)
else:
acc = frappe.new_doc("Account")
acc.company = company_name
acc.parent_account = liability_root.name
acc.account_name = "Round Off for Opening"
acc.account_type = "Round Off for Opening"
acc.save()
frappe.db.set_value("Company", company_name, "round_off_for_opening", acc.name)
def test_ledger_entries_of_opening_invoice_with_rounding_adjustment(self):
pi = make_purchase_invoice(do_not_save=1)
pi.items[0].rate = 99.98
pi.items[0].qty = 1
pi.items[0].expense_account = "Temporary Opening - _TC"
pi.is_opening = "Yes"
pi.save()
self._create_opening_roundoff_account(pi.company)
pi.submit()
actual = frappe.db.get_all(
"GL Entry",
filters={"voucher_no": pi.name, "is_opening": "Yes", "is_cancelled": False},
fields=["account", "debit", "credit", "is_opening"],
order_by="account,debit",
)
expected = [
{"account": "Creditors - _TC", "debit": 0.0, "credit": 100.0, "is_opening": "Yes"},
{"account": "Round Off for Opening - _TC", "debit": 0.02, "credit": 0.0, "is_opening": "Yes"},
{"account": "Temporary Opening - _TC", "debit": 99.98, "credit": 0.0, "is_opening": "Yes"},
]
self.assertEqual(len(actual), 3)
self.assertEqual(expected, actual)
def set_advance_flag(company, flag, default_account):
frappe.db.set_value(

View File

@@ -505,7 +505,8 @@
"fieldtype": "Link",
"label": "Project",
"options": "Project",
"print_hide": 1
"print_hide": 1,
"search_index": 1
},
{
"allow_on_submit": 1,
@@ -974,7 +975,7 @@
"idx": 1,
"istable": 1,
"links": [],
"modified": "2024-07-19 12:12:42.449298",
"modified": "2024-10-28 15:06:19.246141",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Item",

View File

@@ -45,7 +45,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="period_end_date desc",
pluck="period_end_date",
limit=1,

View File

@@ -339,6 +339,9 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
account: this.frm.doc.debit_to,
price_list: this.frm.doc.selling_price_list,
pos_profile: pos_profile,
fetch_payment_terms_template: cint(
(this.frm.doc.is_return == 0) & !this.frm.doc.ignore_default_payment_terms_template
),
},
function () {
me.apply_pricing_rule();
@@ -738,20 +741,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"));

View File

@@ -159,8 +159,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",
@@ -2166,6 +2167,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",
@@ -2178,7 +2186,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",
@@ -2233,4 +2241,4 @@
"title_field": "title",
"track_changes": 1,
"track_seen": 1
}
}

View File

@@ -96,6 +96,7 @@ class SalesInvoice(SellingController):
company: DF.Link
company_address: DF.Link | None
company_address_display: DF.SmallText | None
company_contact_person: DF.Link | None
company_tax_id: DF.Data | None
contact_display: DF.SmallText | None
contact_email: DF.Data | None
@@ -297,8 +298,11 @@ class SalesInvoice(SellingController):
self.update_current_stock()
self.validate_delivery_note()
is_deferred_invoice = any(d.get("enable_deferred_revenue") for d in self.get("items"))
# validate service stop date to lie in between start and end date
validate_service_stop_date(self)
if is_deferred_invoice:
validate_service_stop_date(self)
if not self.is_opening:
self.is_opening = "No"
@@ -1630,10 +1634,29 @@ class SalesInvoice(SellingController):
and self.base_rounding_adjustment
and not self.is_internal_transfer()
):
round_off_account, round_off_cost_center = get_round_off_account_and_cost_center(
(
round_off_account,
round_off_cost_center,
round_off_for_opening,
) = get_round_off_account_and_cost_center(
self.company, "Sales Invoice", self.name, self.use_company_roundoff_cost_center
)
if self.is_opening == "Yes" and self.rounding_adjustment:
if not round_off_for_opening:
frappe.throw(
_(
"Opening Invoice has rounding adjustment of {0}.<br><br> '{1}' account is required to post these values. Please set it in Company: {2}.<br><br> Or, '{3}' can be enabled to not post any rounding adjustment."
).format(
frappe.bold(self.rounding_adjustment),
frappe.bold("Round Off for Opening"),
get_link_to_form("Company", self.company),
frappe.bold("Disable Rounded Total"),
)
)
else:
round_off_account = round_off_for_opening
gl_entries.append(
self.get_gl_dict(
{
@@ -1731,9 +1754,14 @@ 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.calculate_gross_margin()
project.db_update()
def verify_payment_amount_is_positive(self):

View File

@@ -314,7 +314,8 @@ class TestSalesInvoice(FrappeTestCase):
si.insert()
# with inclusive tax
self.assertEqual(si.items[0].net_amount, 3947.368421052631)
self.assertEqual(si.items[0].net_amount, 3947.37)
self.assertEqual(si.net_total, si.base_net_total)
self.assertEqual(si.net_total, 3947.37)
self.assertEqual(si.grand_total, 5000)
@@ -658,7 +659,7 @@ class TestSalesInvoice(FrappeTestCase):
62.5,
625.0,
50,
499.97600115194473,
499.98,
],
"_Test Item Home Desktop 200": [
190.66,
@@ -669,7 +670,7 @@ class TestSalesInvoice(FrappeTestCase):
190.66,
953.3,
150,
749.9968530500239,
750,
],
}
@@ -682,20 +683,21 @@ class TestSalesInvoice(FrappeTestCase):
self.assertEqual(d.get(k), expected_values[d.item_code][i])
# check net total
self.assertEqual(si.net_total, 1249.97)
self.assertEqual(si.base_net_total, si.net_total)
self.assertEqual(si.net_total, 1249.98)
self.assertEqual(si.total, 1578.3)
# check tax calculation
expected_values = {
"keys": ["tax_amount", "total"],
"_Test Account Excise Duty - _TC": [140, 1389.97],
"_Test Account Education Cess - _TC": [2.8, 1392.77],
"_Test Account S&H Education Cess - _TC": [1.4, 1394.17],
"_Test Account CST - _TC": [27.88, 1422.05],
"_Test Account VAT - _TC": [156.25, 1578.30],
"_Test Account Customs Duty - _TC": [125, 1703.30],
"_Test Account Shipping Charges - _TC": [100, 1803.30],
"_Test Account Discount - _TC": [-180.33, 1622.97],
"_Test Account Excise Duty - _TC": [140, 1389.98],
"_Test Account Education Cess - _TC": [2.8, 1392.78],
"_Test Account S&H Education Cess - _TC": [1.4, 1394.18],
"_Test Account CST - _TC": [27.88, 1422.06],
"_Test Account VAT - _TC": [156.25, 1578.31],
"_Test Account Customs Duty - _TC": [125, 1703.31],
"_Test Account Shipping Charges - _TC": [100, 1803.31],
"_Test Account Discount - _TC": [-180.33, 1622.98],
}
for d in si.get("taxes"):
@@ -731,7 +733,7 @@ class TestSalesInvoice(FrappeTestCase):
"base_rate": 2500,
"base_amount": 25000,
"net_rate": 40,
"net_amount": 399.9808009215558,
"net_amount": 399.98,
"base_net_rate": 2000,
"base_net_amount": 19999,
},
@@ -745,7 +747,7 @@ class TestSalesInvoice(FrappeTestCase):
"base_rate": 7500,
"base_amount": 37500,
"net_rate": 118.01,
"net_amount": 590.0531205155963,
"net_amount": 590.05,
"base_net_rate": 5900.5,
"base_net_amount": 29502.5,
},
@@ -783,8 +785,13 @@ class TestSalesInvoice(FrappeTestCase):
self.assertEqual(si.base_grand_total, 60795)
self.assertEqual(si.grand_total, 1215.90)
self.assertEqual(si.rounding_adjustment, 0.01)
self.assertEqual(si.base_rounding_adjustment, 0.50)
# no rounding adjustment as the Smallest Currency Fraction Value of USD is 0.01
if frappe.db.get_value("Currency", "USD", "smallest_currency_fraction_value") < 0.01:
self.assertEqual(si.rounding_adjustment, 0.10)
self.assertEqual(si.base_rounding_adjustment, 5.0)
else:
self.assertEqual(si.rounding_adjustment, 0.0)
self.assertEqual(si.base_rounding_adjustment, 0.0)
def test_outstanding(self):
w = self.make()
@@ -1995,7 +2002,7 @@ class TestSalesInvoice(FrappeTestCase):
# Check if SO is unlinked/replaced by SI in PE & if SO advance paid is 0
self.assertEqual(pe.references[0].reference_name, si.name)
self.assertEqual(sales_order.advance_paid, 0.0)
self.assertEqual(sales_order.advance_paid, 300.0)
# check outstanding after advance allocation
self.assertEqual(
@@ -2172,7 +2179,7 @@ class TestSalesInvoice(FrappeTestCase):
def test_rounding_adjustment_2(self):
si = create_sales_invoice(rate=400, do_not_save=True)
for rate in [400, 600, 100]:
for rate in [400.25, 600.30, 100.65]:
si.append(
"items",
{
@@ -2198,18 +2205,19 @@ class TestSalesInvoice(FrappeTestCase):
)
si.save()
si.submit()
self.assertEqual(si.net_total, 1271.19)
self.assertEqual(si.grand_total, 1500)
self.assertEqual(si.total_taxes_and_charges, 228.82)
self.assertEqual(si.rounding_adjustment, -0.01)
self.assertEqual(si.net_total, si.base_net_total)
self.assertEqual(si.net_total, 1272.20)
self.assertEqual(si.grand_total, 1501.20)
self.assertEqual(si.total_taxes_and_charges, 229)
self.assertEqual(si.rounding_adjustment, -0.20)
round_off_account = frappe.get_cached_value("Company", "_Test Company", "round_off_account")
expected_values = {
"_Test Account Service Tax - _TC": [0.0, 114.41],
"_Test Account VAT - _TC": [0.0, 114.41],
si.debit_to: [1500, 0.0],
round_off_account: [0.01, 0.01],
"Sales - _TC": [0.0, 1271.18],
"_Test Account Service Tax - _TC": [0.0, 114.50],
"_Test Account VAT - _TC": [0.0, 114.50],
si.debit_to: [1501, 0.0],
round_off_account: [0.20, 0.0],
"Sales - _TC": [0.0, 1272.20],
}
gl_entries = frappe.db.sql(
@@ -2267,7 +2275,8 @@ class TestSalesInvoice(FrappeTestCase):
si.save()
si.submit()
self.assertEqual(si.net_total, 4007.16)
self.assertEqual(si.net_total, si.base_net_total)
self.assertEqual(si.net_total, 4007.15)
self.assertEqual(si.grand_total, 4488.02)
self.assertEqual(si.total_taxes_and_charges, 480.86)
self.assertEqual(si.rounding_adjustment, -0.02)
@@ -2280,7 +2289,7 @@ class TestSalesInvoice(FrappeTestCase):
["_Test Account Service Tax - _TC", 0.0, 240.43],
["_Test Account VAT - _TC", 0.0, 240.43],
["Sales - _TC", 0.0, 4007.15],
[round_off_account, 0.02, 0.01],
[round_off_account, 0.01, 0.0],
]
)
@@ -4005,6 +4014,223 @@ class TestSalesInvoice(FrappeTestCase):
si.submit()
self.assertEqual(si.remarks, f"Against Customer Order Test PO dated {format_date(nowdate())}")
def test_gl_voucher_subtype(self):
si = create_sales_invoice()
gl_entries = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Sales Invoice", "voucher_no": si.name},
pluck="voucher_subtype",
)
self.assertTrue(all([x == "Sales Invoice" for x in gl_entries]))
si = create_sales_invoice(is_return=1, qty=-1)
gl_entries = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Sales Invoice", "voucher_no": si.name},
pluck="voucher_subtype",
)
self.assertTrue(all([x == "Credit Note" for x in gl_entries]))
def test_validation_on_opening_invoice_with_rounding(self):
si = create_sales_invoice(qty=1, rate=99.98, do_not_submit=True)
si.is_opening = "Yes"
si.items[0].income_account = "Temporary Opening - _TC"
si.save()
self.assertRaises(frappe.ValidationError, si.submit)
def _create_opening_roundoff_account(self, company_name):
liability_root = frappe.db.get_all(
"Account",
filters={"company": company_name, "root_type": "Liability", "disabled": 0},
order_by="lft",
limit=1,
)[0]
# setup round off account
if acc := frappe.db.exists(
"Account",
{
"account_name": "Round Off for Opening",
"account_type": "Round Off for Opening",
"company": company_name,
},
):
frappe.db.set_value("Company", company_name, "round_off_for_opening", acc)
else:
acc = frappe.new_doc("Account")
acc.company = company_name
acc.parent_account = liability_root.name
acc.account_name = "Round Off for Opening"
acc.account_type = "Round Off for Opening"
acc.save()
frappe.db.set_value("Company", company_name, "round_off_for_opening", acc.name)
def test_opening_invoice_with_rounding_adjustment(self):
si = create_sales_invoice(qty=1, rate=99.98, do_not_submit=True)
si.is_opening = "Yes"
si.items[0].income_account = "Temporary Opening - _TC"
si.save()
self._create_opening_roundoff_account(si.company)
si.reload()
si.submit()
res = frappe.db.get_all(
"GL Entry",
filters={"voucher_no": si.name, "is_opening": "Yes"},
fields=["account", "debit", "credit", "is_opening"],
)
self.assertEqual(len(res), 3)
def _create_opening_invoice_with_inclusive_tax(self):
si = create_sales_invoice(qty=1, rate=90, do_not_submit=True)
si.is_opening = "Yes"
si.items[0].income_account = "Temporary Opening - _TC"
item_template = si.items[0].as_dict()
item_template.name = None
item_template.rate = 55
si.append("items", item_template)
si.append(
"taxes",
{
"charge_type": "On Net Total",
"account_head": "_Test Account Service Tax - _TC",
"cost_center": "_Test Cost Center - _TC",
"description": "Testing...",
"rate": 5,
"included_in_print_rate": True,
},
)
# there will be 0.01 precision loss between Dr and Cr
# caused by 'included_in_print_tax' option
si.save()
return si
def test_rounding_validation_for_opening_with_inclusive_tax(self):
si = self._create_opening_invoice_with_inclusive_tax()
# 'Round Off for Opening' not set in Company master
# Ledger level validation must be thrown
self.assertRaises(frappe.ValidationError, si.submit)
def test_ledger_entries_on_opening_invoice_with_rounding_loss_by_inclusive_tax(self):
si = self._create_opening_invoice_with_inclusive_tax()
# 'Round Off for Opening' is set in Company master
self._create_opening_roundoff_account(si.company)
si.submit()
actual = frappe.db.get_all(
"GL Entry",
filters={"voucher_no": si.name, "is_opening": "Yes", "is_cancelled": False},
fields=["account", "debit", "credit", "is_opening"],
order_by="account,debit",
)
expected = [
{"account": "_Test Account Service Tax - _TC", "debit": 0.0, "credit": 6.9, "is_opening": "Yes"},
{"account": "Debtors - _TC", "debit": 145.0, "credit": 0.0, "is_opening": "Yes"},
{"account": "Round Off for Opening - _TC", "debit": 0.0, "credit": 0.01, "is_opening": "Yes"},
{"account": "Temporary Opening - _TC", "debit": 0.0, "credit": 138.09, "is_opening": "Yes"},
]
self.assertEqual(len(actual), 4)
self.assertEqual(expected, actual)
@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.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 set_advance_flag(company, flag, default_account):
frappe.db.set_value(

View File

@@ -89,11 +89,14 @@
"incoming_rate",
"item_tax_rate",
"actual_batch_qty",
"actual_qty",
"section_break_eoec",
"serial_no",
"column_break_ytgd",
"batch_no",
"available_quantity_section",
"actual_qty",
"column_break_ogff",
"company_total_stock",
"edit_references",
"sales_order",
"so_detail",
@@ -675,7 +678,8 @@
"allow_on_submit": 1,
"fieldname": "actual_qty",
"fieldtype": "Float",
"label": "Available Qty at Warehouse",
"label": "Qty (Warehouse)",
"no_copy": 1,
"oldfieldname": "actual_qty",
"oldfieldtype": "Currency",
"print_hide": 1,
@@ -812,7 +816,8 @@
"fieldname": "project",
"fieldtype": "Link",
"label": "Project",
"options": "Project"
"options": "Project",
"search_index": 1
},
{
"depends_on": "eval:parent.update_stock == 1",
@@ -922,12 +927,30 @@
{
"fieldname": "column_break_ytgd",
"fieldtype": "Column Break"
},
{
"fieldname": "available_quantity_section",
"fieldtype": "Section Break",
"label": "Available Quantity"
},
{
"fieldname": "column_break_ogff",
"fieldtype": "Column Break"
},
{
"allow_on_submit": 1,
"fieldname": "company_total_stock",
"fieldtype": "Float",
"label": "Qty (Company)",
"no_copy": 1,
"print_hide": 1,
"read_only": 1
}
],
"idx": 1,
"istable": 1,
"links": [],
"modified": "2024-05-23 16:36:18.970862",
"modified": "2024-11-25 16:27:33.287341",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Item",

View File

@@ -28,6 +28,7 @@ class SalesInvoiceItem(Document):
base_rate_with_margin: DF.Currency
batch_no: DF.Link | None
brand: DF.Data | None
company_total_stock: DF.Float
conversion_factor: DF.Float
cost_center: DF.Link
customer_item_code: DF.Data | None

View File

@@ -514,7 +514,7 @@ def get_tds_amount(ldc, parties, inv, tax_details, vouchers):
payment_entry_filters.pop("apply_tax_withholding_amount", None)
payment_entry_filters.pop("tax_withholding_category", None)
supp_credit_amt = frappe.db.get_value("Purchase Invoice", invoice_filters, field) or 0.0
supp_inv_credit_amt = frappe.db.get_value("Purchase Invoice", invoice_filters, field) or 0.0
supp_jv_credit_amt = (
frappe.db.get_value(
@@ -538,7 +538,7 @@ def get_tds_amount(ldc, parties, inv, tax_details, vouchers):
group_by="payment_type",
)
supp_credit_amt += supp_jv_credit_amt
supp_credit_amt = supp_jv_credit_amt
supp_credit_amt += inv.tax_withholding_net_total
for type in payment_entry_amounts:
@@ -556,19 +556,19 @@ def get_tds_amount(ldc, parties, inv, tax_details, vouchers):
tax_withholding_net_total = inv.tax_withholding_net_total
if (threshold and tax_withholding_net_total >= threshold) or (
cumulative_threshold and supp_credit_amt >= cumulative_threshold
cumulative_threshold and (supp_credit_amt + supp_inv_credit_amt) >= cumulative_threshold
):
# Get net total again as TDS is calculated on net total
# Grand is used to just check for threshold breach
net_total = (
frappe.db.get_value("Purchase Invoice", invoice_filters, "sum(tax_withholding_net_total)") or 0.0
)
supp_credit_amt += net_total
if (cumulative_threshold and supp_credit_amt >= cumulative_threshold) and cint(
tax_details.tax_on_excess_amount
):
# Get net total again as TDS is calculated on net total
# Grand is used to just check for threshold breach
net_total = (
frappe.db.get_value("Purchase Invoice", invoice_filters, "sum(tax_withholding_net_total)")
or 0.0
)
net_total += inv.tax_withholding_net_total
supp_credit_amt = net_total - cumulative_threshold
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

@@ -74,11 +74,17 @@ class TestTaxWithholdingCategory(FrappeTestCase):
self.assertEqual(pi.grand_total, 18000)
# check gl entry for the purchase invoice
gl_entries = frappe.db.get_all("GL Entry", filters={"voucher_no": pi.name}, fields=["*"])
gl_entries = frappe.db.get_all(
"GL Entry",
filters={"voucher_no": pi.name},
fields=["account", "sum(debit) as debit", "sum(credit) as credit"],
group_by="account",
)
self.assertEqual(len(gl_entries), 3)
for d in gl_entries:
if d.account == pi.credit_to:
self.assertEqual(d.credit, 18000)
self.assertEqual(d.credit, 20000)
self.assertEqual(d.debit, 2000)
elif d.account == pi.items[0].get("expense_account"):
self.assertEqual(d.debit, 20000)
elif d.account == pi.taxes[0].get("account_head"):
@@ -121,6 +127,85 @@ class TestTaxWithholdingCategory(FrappeTestCase):
for d in reversed(invoices):
d.cancel()
def test_cumulative_threshold_with_party_ledger_amount_on_net_total(self):
invoices = []
frappe.db.set_value(
"Supplier", "Test TDS Supplier3", "tax_withholding_category", "Advance 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=1000, 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=6000)
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.assertEqual(pi1.taxes[0].tax_amount, 800)
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"

View File

@@ -262,6 +262,7 @@ class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase):
pe1.paid_from = self.debtors_usd
pe1.paid_from_account_currency = "USD"
pe1.source_exchange_rate = 75
pe1.paid_amount = 100
pe1.received_amount = 75 * 100
pe1.save()
# Allocate payment against both invoices
@@ -279,6 +280,7 @@ class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase):
pe2.paid_from = self.debtors_usd
pe2.paid_from_account_currency = "USD"
pe2.source_exchange_rate = 75
pe2.paid_amount = 100
pe2.received_amount = 75 * 100
pe2.save()
# Allocate payment against both invoices
@@ -362,10 +364,14 @@ class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase):
# Assert 'Advance Paid'
so.reload()
pe.reload()
self.assertEqual(so.advance_paid, 0)
self.assertEqual(so.advance_paid, 100)
self.assertEqual(len(pe.references), 0)
self.assertEqual(pe.unallocated_amount, 100)
pe.cancel()
so.reload()
self.assertEqual(so.advance_paid, 100)
def test_06_unreconcile_advance_from_payment_entry(self):
self.enable_advance_as_liability()
so1 = self.create_sales_order()
@@ -411,7 +417,7 @@ class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase):
so2.reload()
pe.reload()
self.assertEqual(so1.advance_paid, 150)
self.assertEqual(so2.advance_paid, 0)
self.assertEqual(so2.advance_paid, 110)
self.assertEqual(len(pe.references), 1)
self.assertEqual(pe.unallocated_amount, 110)
@@ -459,6 +465,6 @@ class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase):
# Assert 'Advance Paid'
so.reload()
self.assertEqual(so.advance_paid, 0)
self.assertEqual(so.advance_paid, 1000)
self.disable_advance_as_liability()

View File

@@ -7,7 +7,7 @@ import copy
import frappe
from frappe import _
from frappe.model.meta import get_field_precision
from frappe.utils import cint, flt, formatdate, getdate, now
from frappe.utils import cint, flt, formatdate, get_link_to_form, getdate, now
import erpnext
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
@@ -234,6 +234,10 @@ def merge_similar_entries(gl_map, precision=None):
merge_properties = get_merge_properties(accounting_dimensions)
for entry in gl_map:
if entry._skip_merge:
merged_gl_map.append(entry)
continue
entry.merge_key = get_merge_key(entry, merge_properties)
# if there is already an entry in this account then just add it
# to that entry
@@ -311,66 +315,48 @@ def check_if_in_list(gle, gl_map):
def toggle_debit_credit_if_negative(gl_map):
debit_credit_field_map = {
"debit": "credit",
"debit_in_account_currency": "credit_in_account_currency",
"debit_in_transaction_currency": "credit_in_transaction_currency",
}
for entry in gl_map:
# toggle debit, credit if negative entry
if flt(entry.debit) < 0 and flt(entry.credit) < 0 and flt(entry.debit) == flt(entry.credit):
entry.credit *= -1
entry.debit *= -1
for debit_field, credit_field in debit_credit_field_map.items():
debit = flt(entry.get(debit_field))
credit = flt(entry.get(credit_field))
if (
flt(entry.debit_in_account_currency) < 0
and flt(entry.credit_in_account_currency) < 0
and flt(entry.debit_in_account_currency) == flt(entry.credit_in_account_currency)
):
entry.credit_in_account_currency *= -1
entry.debit_in_account_currency *= -1
if debit < 0 and credit < 0 and debit == credit:
debit *= -1
credit *= -1
if flt(entry.debit) < 0:
entry.credit = flt(entry.credit) - flt(entry.debit)
entry.debit = 0.0
if debit < 0:
credit = credit - debit
debit = 0.0
if flt(entry.debit_in_account_currency) < 0:
entry.credit_in_account_currency = flt(entry.credit_in_account_currency) - flt(
entry.debit_in_account_currency
)
entry.debit_in_account_currency = 0.0
if credit < 0:
debit = debit - credit
credit = 0.0
if flt(entry.credit) < 0:
entry.debit = flt(entry.debit) - flt(entry.credit)
entry.credit = 0.0
# update net values
# In some scenarios net value needs to be shown in the ledger
# This method updates net values as debit or credit
if entry.post_net_value and debit and credit:
if debit > credit:
debit = debit - credit
credit = 0.0
if flt(entry.credit_in_account_currency) < 0:
entry.debit_in_account_currency = flt(entry.debit_in_account_currency) - flt(
entry.credit_in_account_currency
)
entry.credit_in_account_currency = 0.0
else:
credit = credit - debit
debit = 0.0
update_net_values(entry)
entry[debit_field] = debit
entry[credit_field] = credit
return gl_map
def update_net_values(entry):
# In some scenarios net value needs to be shown in the ledger
# This method updates net values as debit or credit
if entry.post_net_value and entry.debit and entry.credit:
if entry.debit > entry.credit:
entry.debit = entry.debit - entry.credit
entry.debit_in_account_currency = (
entry.debit_in_account_currency - entry.credit_in_account_currency
)
entry.credit = 0
entry.credit_in_account_currency = 0
else:
entry.credit = entry.credit - entry.debit
entry.credit_in_account_currency = (
entry.credit_in_account_currency - entry.debit_in_account_currency
)
entry.debit = 0
entry.debit_in_account_currency = 0
def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False):
if not from_repost:
validate_cwip_accounts(gl_map)
@@ -492,16 +478,36 @@ def raise_debit_credit_not_equal_error(debit_credit_diff, voucher_type, voucher_
)
def has_opening_entries(gl_map: list) -> bool:
for x in gl_map:
if x.is_opening == "Yes":
return True
return False
def make_round_off_gle(gl_map, debit_credit_diff, precision):
round_off_account, round_off_cost_center = get_round_off_account_and_cost_center(
round_off_account, round_off_cost_center, round_off_for_opening = get_round_off_account_and_cost_center(
gl_map[0].company, gl_map[0].voucher_type, gl_map[0].voucher_no
)
round_off_gle = frappe._dict()
round_off_account_exists = False
has_opening_entry = has_opening_entries(gl_map)
if has_opening_entry:
if not round_off_for_opening:
frappe.throw(
_("Please set '{0}' in Company: {1}").format(
frappe.bold("Round Off for Opening"), get_link_to_form("Company", gl_map[0].company)
)
)
account = round_off_for_opening
else:
account = round_off_account
if gl_map[0].voucher_type != "Period Closing Voucher":
for d in gl_map:
if d.account == round_off_account:
if d.account == account:
round_off_gle = d
if d.debit:
debit_credit_diff -= flt(d.debit) - flt(d.credit)
@@ -519,7 +525,7 @@ def make_round_off_gle(gl_map, debit_credit_diff, precision):
round_off_gle.update(
{
"account": round_off_account,
"account": account,
"debit_in_account_currency": abs(debit_credit_diff) if debit_credit_diff < 0 else 0,
"credit_in_account_currency": debit_credit_diff if debit_credit_diff > 0 else 0,
"debit": abs(debit_credit_diff) if debit_credit_diff < 0 else 0,
@@ -533,6 +539,9 @@ def make_round_off_gle(gl_map, debit_credit_diff, precision):
}
)
if has_opening_entry:
round_off_gle.update({"is_opening": "Yes"})
update_accounting_dimensions(round_off_gle)
if not round_off_account_exists:
gl_map.append(round_off_gle)
@@ -557,9 +566,9 @@ def update_accounting_dimensions(round_off_gle):
def get_round_off_account_and_cost_center(company, voucher_type, voucher_no, use_company_default=False):
round_off_account, round_off_cost_center = frappe.get_cached_value(
"Company", company, ["round_off_account", "round_off_cost_center"]
) or [None, None]
round_off_account, round_off_cost_center, round_off_for_opening = frappe.get_cached_value(
"Company", company, ["round_off_account", "round_off_cost_center", "round_off_for_opening"]
) or [None, None, None]
# Use expense account as fallback
if not round_off_account:
@@ -574,12 +583,20 @@ def get_round_off_account_and_cost_center(company, voucher_type, voucher_no, use
round_off_cost_center = parent_cost_center
if not round_off_account:
frappe.throw(_("Please mention Round Off Account in Company"))
frappe.throw(
_("Please mention '{0}' in Company: {1}").format(
frappe.bold("Round Off Account"), get_link_to_form("Company", company)
)
)
if not round_off_cost_center:
frappe.throw(_("Please mention Round Off Cost Center in Company"))
frappe.throw(
_("Please mention '{0}' in Company: {1}").format(
frappe.bold("Round Off Cost Center"), get_link_to_form("Company", company)
)
)
return round_off_account, round_off_cost_center
return round_off_account, round_off_cost_center, round_off_for_opening
def make_reverse_gl_entries(

View File

@@ -4,13 +4,14 @@
"docstatus": 0,
"doctype": "Number Card",
"document_type": "Purchase Invoice",
"dynamic_filters_json": "[[\"Purchase Invoice\",\"company\",\"=\",\" frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
"filters_json": "[[\"Purchase Invoice\",\"docstatus\",\"=\",\"1\",false],[\"Purchase Invoice\",\"posting_date\",\"Timespan\",\"this year\",false]]",
"function": "Sum",
"idx": 0,
"is_public": 1,
"is_standard": 1,
"label": "Total Incoming Bills",
"modified": "2020-07-22 13:06:46.045344",
"modified": "2024-11-20 19:08:37.043777",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Total Incoming Bills",

View File

@@ -4,6 +4,7 @@
"docstatus": 0,
"doctype": "Number Card",
"document_type": "Payment Entry",
"dynamic_filters_json": "[[\"Payment Entry\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
"filters_json": "[[\"Payment Entry\",\"docstatus\",\"=\",\"1\",false],[\"Payment Entry\",\"posting_date\",\"Timespan\",\"this year\",false],[\"Payment Entry\",\"payment_type\",\"=\",\"Receive\",false]]",
"function": "Sum",
"idx": 0,

View File

@@ -4,6 +4,7 @@
"docstatus": 0,
"doctype": "Number Card",
"document_type": "Sales Invoice",
"dynamic_filters_json": "[[\"Sales Invoice\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
"filters_json": "[[\"Sales Invoice\",\"docstatus\",\"=\",\"1\",false],[\"Sales Invoice\",\"posting_date\",\"Timespan\",\"this year\",false]]",
"function": "Sum",
"idx": 0,

View File

@@ -4,6 +4,7 @@
"docstatus": 0,
"doctype": "Number Card",
"document_type": "Payment Entry",
"dynamic_filters_json": "[[\"Payment Entry\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
"filters_json": "[[\"Payment Entry\",\"docstatus\",\"=\",\"1\",false],[\"Payment Entry\",\"posting_date\",\"Timespan\",\"this year\",false],[\"Payment Entry\",\"payment_type\",\"=\",\"Pay\",false]]",
"function": "Sum",
"idx": 0,

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",
@@ -982,10 +988,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

@@ -522,7 +522,8 @@ class ReceivablePayableReport:
from `tab{row.voucher_type}` si, `tabPayment Schedule` ps
where
si.name = ps.parent and
si.name = %s
si.name = %s and
si.is_return = 0
order by ps.paid_amount desc, due_date
""",
row.voucher_no,
@@ -1012,22 +1013,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",
@@ -1036,10 +1044,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,
@@ -1065,7 +1073,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

@@ -89,7 +89,9 @@ def get_data(filters):
& (DepreciationSchedule.schedule_date == d.posting_date)
)
).run(as_dict=True)
asset_data.accumulated_depreciation_amount = query[0]["accumulated_depreciation_amount"]
asset_data.accumulated_depreciation_amount = (
query[0]["accumulated_depreciation_amount"] if query else 0
)
else:
asset_data.accumulated_depreciation_amount += d.debit

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

@@ -1,7 +1,10 @@
// Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.query_reports["Cash Flow"] = $.extend({}, erpnext.financial_statements);
frappe.query_reports["Cash Flow"] = $.extend(erpnext.financial_statements, {
name_field: "section",
parent_field: "parent_section",
});
erpnext.utils.add_dimensions("Cash Flow", 10);

View File

@@ -30,7 +30,7 @@ def execute(filters=None):
company=filters.company,
)
cash_flow_accounts = get_cash_flow_accounts()
cash_flow_sections = get_cash_flow_accounts()
# compute net profit / loss
income = get_data(
@@ -60,14 +60,14 @@ def execute(filters=None):
summary_data = {}
company_currency = frappe.get_cached_value("Company", filters.company, "default_currency")
for cash_flow_account in cash_flow_accounts:
for cash_flow_section in cash_flow_sections:
section_data = []
data.append(
{
"account_name": cash_flow_account["section_header"],
"parent_account": None,
"section_name": "'" + cash_flow_section["section_header"] + "'",
"parent_section": None,
"indent": 0.0,
"account": cash_flow_account["section_header"],
"section": cash_flow_section["section_header"],
}
)
@@ -75,31 +75,40 @@ def execute(filters=None):
# add first net income in operations section
if net_profit_loss:
net_profit_loss.update(
{"indent": 1, "parent_account": cash_flow_accounts[0]["section_header"]}
{"indent": 1, "parent_section": cash_flow_sections[0]["section_header"]}
)
data.append(net_profit_loss)
section_data.append(net_profit_loss)
for account in cash_flow_account["account_types"]:
account_data = get_account_type_based_data(
filters.company, account["account_type"], period_list, filters.accumulated_values, filters
for row in cash_flow_section["account_types"]:
row_data = get_account_type_based_data(
filters.company, row["account_type"], period_list, filters.accumulated_values, filters
)
account_data.update(
accounts = frappe.get_all(
"Account",
filters={
"account_type": row["account_type"],
"is_group": 0,
},
pluck="name",
)
row_data.update(
{
"account_name": account["label"],
"account": account["label"],
"section_name": row["label"],
"section": row["label"],
"indent": 1,
"parent_account": cash_flow_account["section_header"],
"accounts": accounts,
"parent_section": cash_flow_section["section_header"],
"currency": company_currency,
}
)
data.append(account_data)
section_data.append(account_data)
data.append(row_data)
section_data.append(row_data)
add_total_row_account(
data,
section_data,
cash_flow_account["section_footer"],
cash_flow_section["section_footer"],
period_list,
company_currency,
summary_data,
@@ -109,7 +118,7 @@ def execute(filters=None):
add_total_row_account(
data, data, _("Net Change in Cash"), period_list, company_currency, summary_data, filters
)
columns = get_columns(filters.periodicity, period_list, filters.accumulated_values, filters.company)
columns = get_columns(filters.periodicity, period_list, filters.accumulated_values, filters.company, True)
chart = get_chart_data(columns, data, company_currency)
@@ -217,8 +226,8 @@ def get_start_date(period, accumulated_values, company):
def add_total_row_account(out, data, label, period_list, currency, summary_data, filters, consolidated=False):
total_row = {
"account_name": "'" + _("{0}").format(label) + "'",
"account": "'" + _("{0}").format(label) + "'",
"section_name": "'" + _("{0}").format(label) + "'",
"section": "'" + _("{0}").format(label) + "'",
"currency": currency,
}
@@ -229,7 +238,7 @@ def add_total_row_account(out, data, label, period_list, currency, summary_data,
period_list = get_filtered_list_for_consolidated_report(filters, period_list)
for row in data:
if row.get("parent_account"):
if row.get("parent_section"):
for period in period_list:
key = period if consolidated else period["key"]
total_row.setdefault(key, 0.0)
@@ -254,13 +263,14 @@ def get_report_summary(summary_data, currency):
def get_chart_data(columns, data, currency):
labels = [d.get("label") for d in columns[2:]]
print(data)
datasets = [
{
"name": account.get("account").replace("'", ""),
"values": [account.get(d.get("fieldname")) for d in columns[2:]],
"name": section.get("section").replace("'", ""),
"values": [section.get(d.get("fieldname")) for d in columns[2:]],
}
for account in data
if account.get("parent_account") is None and account.get("currency")
for section in data
if section.get("parent_section") is None and section.get("currency")
]
datasets = datasets[:-1]

View File

@@ -2,6 +2,7 @@
# License: GNU General Public License v3. See license.txt
import copy
import functools
import math
import re
@@ -334,8 +335,8 @@ def filter_out_zero_value_rows(data, parent_children_map, show_zero_values=False
def add_total_row(out, root_type, balance_must_be, period_list, company_currency):
total_row = {
"account_name": _("Total {0} ({1})").format(_(root_type), _(balance_must_be)),
"account": _("Total {0} ({1})").format(_(root_type), _(balance_must_be)),
"account_name": "'" + _("Total {0} ({1})").format(_(root_type), _(balance_must_be)) + "'",
"account": "'" + _("Total {0} ({1})").format(_(root_type), _(balance_must_be)) + "'",
"currency": company_currency,
"opening_balance": 0.0,
}
@@ -616,11 +617,11 @@ def get_cost_centers_with_children(cost_centers):
return list(set(all_cost_centers))
def get_columns(periodicity, period_list, accumulated_values=1, company=None):
def get_columns(periodicity, period_list, accumulated_values=1, company=None, cash_flow=False):
columns = [
{
"fieldname": "account",
"label": _("Account"),
"label": _("Account") if not cash_flow else _("Section"),
"fieldtype": "Link",
"options": "Account",
"width": 300,
@@ -668,3 +669,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

@@ -348,10 +348,18 @@ def get_accounts_with_children(accounts):
return frappe.qb.from_(doctype).select(doctype.name).where(Criterion.any(conditions)).run(pluck=True)
def set_bill_no(gl_entries):
inv_details = get_supplier_invoice_details()
for gl in gl_entries:
gl["bill_no"] = inv_details.get(gl.get("against_voucher"), "")
def get_data_with_opening_closing(filters, account_details, accounting_dimensions, gl_entries):
data = []
totals_dict = get_totals_dict()
set_bill_no(gl_entries)
gle_map = initialize_gle_map(gl_entries, filters, totals_dict)
totals, entries = get_accountwise_gle(filters, accounting_dimensions, gl_entries, gle_map, totals_dict)
@@ -539,7 +547,6 @@ def get_account_type_map(company):
def get_result_as_list(data, filters):
balance, _balance_in_account_currency = 0, 0
inv_details = get_supplier_invoice_details()
for d in data:
if not d.get("posting_date"):
@@ -549,7 +556,6 @@ def get_result_as_list(data, filters):
d["balance"] = balance
d["account_currency"] = filters.account_currency
d["bill_no"] = inv_details.get(d.get("against_voucher"), "")
return data

View File

@@ -421,10 +421,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()
@@ -440,6 +440,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":
@@ -480,12 +481,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):
@@ -495,11 +495,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
@@ -510,33 +518,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
@@ -556,7 +560,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
@@ -632,6 +636,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)
@@ -664,7 +669,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
@@ -847,6 +854,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,
@@ -907,6 +915,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
@@ -917,8 +926,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)
@@ -954,47 +962,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

View File

@@ -130,6 +130,7 @@ class PaymentLedger:
)
def get_columns(self):
company_currency = frappe.get_cached_value("Company", self.filters.get("company"), "default_currency")
options = None
self.columns.append(
dict(
@@ -194,7 +195,7 @@ class PaymentLedger:
label=_("Amount"),
fieldname="amount",
fieldtype="Currency",
options="Company:company:default_currency",
options=company_currency,
width="100",
)
)

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

@@ -526,7 +526,8 @@ def get_invoice_tax_map(invoice_list, invoice_income_map, income_accounts, inclu
tax_details = frappe.db.sql(
"""select parent, account_head,
sum(base_tax_amount_after_discount_amount) as tax_amount
from `tabSales Taxes and Charges` where parent in (%s) group by parent, account_head"""
from `tabSales Taxes and Charges` where parent in (%s) and parenttype = 'Sales Invoice'
group by parent, account_head"""
% ", ".join(["%s"] * len(invoice_list)),
tuple(inv.name for inv in invoice_list),
as_dict=1,

View File

@@ -0,0 +1,179 @@
import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.utils import getdate, today
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.report.sales_register.sales_register import execute
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
class TestItemWiseSalesRegister(AccountsTestMixin, FrappeTestCase):
def setUp(self):
self.create_company()
self.create_customer()
self.create_item()
self.create_child_cost_center()
def tearDown(self):
frappe.db.rollback()
def create_child_cost_center(self):
cc_name = "South Wing"
if frappe.db.exists("Cost Center", cc_name):
cc = frappe.get_doc("Cost Center", cc_name)
else:
parent = frappe.db.get_value("Cost Center", self.cost_center, "parent_cost_center")
cc = frappe.get_doc(
{
"doctype": "Cost Center",
"company": self.company,
"is_group": False,
"parent_cost_center": parent,
"cost_center_name": cc_name,
}
)
cc = cc.save()
self.south_cc = cc.name
def create_sales_invoice(self, rate=100, do_not_submit=False):
si = create_sales_invoice(
item=self.item,
company=self.company,
customer=self.customer,
debit_to=self.debit_to,
posting_date=today(),
parent_cost_center=self.cost_center,
cost_center=self.cost_center,
rate=rate,
price_list_rate=rate,
do_not_save=1,
)
si = si.save()
if not do_not_submit:
si = si.submit()
return si
def test_basic_report_output(self):
si = self.create_sales_invoice(rate=98)
filters = frappe._dict({"from_date": today(), "to_date": today(), "company": self.company})
report = execute(filters)
res = [x for x in report[1] if x.get("voucher_no") == si.name]
expected_result = {
"voucher_type": si.doctype,
"voucher_no": si.name,
"posting_date": getdate(),
"customer": self.customer,
"receivable_account": self.debit_to,
"net_total": 98.0,
"grand_total": 98.0,
"debit": 98.0,
}
report_output = {k: v for k, v in res[0].items() if k in expected_result}
self.assertDictEqual(report_output, expected_result)
def test_journal_with_cost_center_filter(self):
je1 = frappe.get_doc(
{
"doctype": "Journal Entry",
"voucher_type": "Journal Entry",
"company": self.company,
"posting_date": getdate(),
"accounts": [
{
"account": self.debit_to,
"party_type": "Customer",
"party": self.customer,
"credit_in_account_currency": 77,
"credit": 77,
"is_advance": "Yes",
"cost_center": self.cost_center,
},
{
"account": self.cash,
"debit_in_account_currency": 77,
"debit": 77,
},
],
}
)
je1.submit()
je2 = frappe.get_doc(
{
"doctype": "Journal Entry",
"voucher_type": "Journal Entry",
"company": self.company,
"posting_date": getdate(),
"accounts": [
{
"account": self.debit_to,
"party_type": "Customer",
"party": self.customer,
"credit_in_account_currency": 98,
"credit": 98,
"is_advance": "Yes",
"cost_center": self.south_cc,
},
{
"account": self.cash,
"debit_in_account_currency": 98,
"debit": 98,
},
],
}
)
je2.submit()
filters = frappe._dict(
{
"from_date": today(),
"to_date": today(),
"company": self.company,
"include_payments": True,
"customer": self.customer,
"cost_center": self.cost_center,
}
)
report_output = execute(filters)[1]
filtered_output = [x for x in report_output if x.get("voucher_no") == je1.name]
self.assertEqual(len(filtered_output), 1)
expected_result = {
"voucher_type": je1.doctype,
"voucher_no": je1.name,
"posting_date": je1.posting_date,
"customer": self.customer,
"receivable_account": self.debit_to,
"net_total": 77.0,
"credit": 77.0,
}
result_fields = {k: v for k, v in filtered_output[0].items() if k in expected_result}
self.assertDictEqual(result_fields, expected_result)
filters = frappe._dict(
{
"from_date": today(),
"to_date": today(),
"company": self.company,
"include_payments": True,
"customer": self.customer,
"cost_center": self.south_cc,
}
)
report_output = execute(filters)[1]
filtered_output = [x for x in report_output if x.get("voucher_no") == je2.name]
self.assertEqual(len(filtered_output), 1)
expected_result = {
"voucher_type": je2.doctype,
"voucher_no": je2.name,
"posting_date": je2.posting_date,
"customer": self.customer,
"receivable_account": self.debit_to,
"net_total": 98.0,
"credit": 98.0,
}
result_output = {k: v for k, v in filtered_output[0].items() if k in expected_result}
self.assertDictEqual(result_output, expected_result)

View File

@@ -255,7 +255,9 @@ def get_journal_entries(filters, args):
)
.orderby(je.posting_date, je.name, order=Order.desc)
)
query = apply_common_conditions(filters, query, doctype="Journal Entry", payments=True)
query = apply_common_conditions(
filters, query, doctype="Journal Entry", child_doctype="Journal Entry Account", payments=True
)
journal_entries = query.run(as_dict=True)
return journal_entries
@@ -306,7 +308,9 @@ def apply_common_conditions(filters, query, doctype, child_doctype=None, payment
query = query.where(parent_doc.posting_date <= filters.to_date)
if payments:
if filters.get("cost_center"):
if doctype == "Journal Entry" and filters.get("cost_center"):
query = query.where(child_doc.cost_center == filters.cost_center)
elif filters.get("cost_center"):
query = query.where(parent_doc.cost_center == filters.cost_center)
else:
if filters.get("cost_center"):

View File

@@ -92,14 +92,14 @@ class TestUtils(unittest.TestCase):
payment_entry.deductions = []
payment_entry.save()
# below is the difference between base_received_amount and base_paid_amount
self.assertEqual(payment_entry.difference_amount, -4855.0)
# below is the difference between base_paid_amount and base_received_amount (exchange gain)
self.assertEqual(payment_entry.deductions[0].amount, -4855.0)
payment_entry.target_exchange_rate = 62.9
payment_entry.save()
# below is due to change in exchange rate
self.assertEqual(payment_entry.references[0].exchange_gain_loss, -4855.0)
# after changing the exchange rate, there is no exchange gain / loss
self.assertEqual(payment_entry.deductions, [])
payment_entry.references = []
self.assertEqual(payment_entry.difference_amount, 0.0)

View File

@@ -630,6 +630,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"]:
update_advance_paid.append((jv_detail.reference_type, jv_detail.reference_name))
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"])
@@ -1969,6 +1979,7 @@ class QueryPaymentLedger:
ple.cost_center.as_("cost_center"),
Sum(ple.amount).as_("amount"),
Sum(ple.amount_in_account_currency).as_("amount_in_account_currency"),
ple.remarks,
)
.where(ple.delinked == 0)
.where(Criterion.all(filter_on_voucher_no))
@@ -2031,6 +2042,7 @@ class QueryPaymentLedger:
Table("vouchers").due_date,
Table("vouchers").currency,
Table("vouchers").cost_center.as_("cost_center"),
Table("vouchers").remarks,
)
.where(Criterion.all(filter_on_outstanding_amount))
)

View File

@@ -119,6 +119,7 @@ class Asset(AccountsController):
# end: auto-generated types
def validate(self):
self.validate_precision()
self.validate_asset_values()
self.validate_asset_and_reference()
self.validate_item()
@@ -306,6 +307,15 @@ class Asset(AccountsController):
title=_("Missing Finance Book"),
)
def validate_precision(self):
float_precision = cint(frappe.db.get_default("float_precision")) or 2
if self.gross_purchase_amount:
self.gross_purchase_amount = flt(self.gross_purchase_amount, float_precision)
if self.opening_accumulated_depreciation:
self.opening_accumulated_depreciation = flt(
self.opening_accumulated_depreciation, float_precision
)
def validate_asset_values(self):
if not self.asset_category:
self.asset_category = frappe.get_cached_value("Item", self.item_code, "asset_category")
@@ -471,6 +481,9 @@ class Asset(AccountsController):
def validate_expected_value_after_useful_life(self):
for row in self.get("finance_books"):
row.expected_value_after_useful_life = flt(
row.expected_value_after_useful_life, self.precision("gross_purchase_amount")
)
depr_schedule = get_depr_schedule(self.name, "Draft", row.finance_book)
if not depr_schedule:
@@ -790,14 +803,19 @@ class Asset(AccountsController):
args.get("value_after_depreciation")
)
else:
value = flt(args.get("expected_value_after_useful_life")) / flt(self.gross_purchase_amount)
value = flt(args.get("expected_value_after_useful_life")) / (
flt(self.gross_purchase_amount) - flt(self.opening_accumulated_depreciation)
)
depreciation_rate = math.pow(
value,
1.0
/ (
(
flt(args.get("total_number_of_depreciations"), 2)
(
flt(args.get("total_number_of_depreciations"), 2)
- flt(self.opening_number_of_booked_depreciations)
)
* flt(args.get("frequency_of_depreciation"))
)
/ 12

View File

@@ -4,6 +4,7 @@
"docstatus": 0,
"doctype": "Number Card",
"document_type": "Asset",
"dynamic_filters_json": "[[\"Asset\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
"filters_json": "[]",
"function": "Sum",
"idx": 0,

View File

@@ -3,6 +3,7 @@
"docstatus": 0,
"doctype": "Number Card",
"document_type": "Asset",
"dynamic_filters_json": "[[\"Asset\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
"filters_json": "[[\"Asset\",\"creation\",\"Timespan\",\"this year\",false]]",
"function": "Count",
"idx": 0,

View File

@@ -3,6 +3,7 @@
"docstatus": 0,
"doctype": "Number Card",
"document_type": "Asset",
"dynamic_filters_json": "[[\"Asset\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
"filters_json": "[]",
"function": "Count",
"idx": 0,

View File

@@ -93,7 +93,7 @@ frappe.ui.form.on("Purchase Order", {
get_materials_from_supplier: function (frm) {
let po_details = [];
if (frm.doc.supplied_items && (flt(frm.doc.per_received, 2) == 100 || frm.doc.status === "Closed")) {
if (frm.doc.supplied_items && (flt(frm.doc.per_received) == 100 || frm.doc.status === "Closed")) {
frm.doc.supplied_items.forEach((d) => {
if (d.total_supplied_qty && d.total_supplied_qty != d.consumed_qty) {
po_details.push(d.name);
@@ -329,8 +329,8 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
if (!["Closed", "Delivered"].includes(doc.status)) {
if (
this.frm.doc.status !== "Closed" &&
flt(this.frm.doc.per_received, 2) < 100 &&
flt(this.frm.doc.per_billed, 2) < 100
flt(this.frm.doc.per_received) < 100 &&
flt(this.frm.doc.per_billed) < 100
) {
if (!this.frm.doc.__onload || this.frm.doc.__onload.can_update_items) {
this.frm.add_custom_button(__("Update Items"), () => {
@@ -344,7 +344,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
}
}
if (this.frm.has_perm("submit")) {
if (flt(doc.per_billed, 2) < 100 || flt(doc.per_received, 2) < 100) {
if (flt(doc.per_billed) < 100 || flt(doc.per_received) < 100) {
if (doc.status != "On Hold") {
this.frm.add_custom_button(
__("Hold"),
@@ -383,7 +383,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
if (doc.status != "Closed") {
if (doc.status != "On Hold") {
if (flt(doc.per_received) < 100 && allow_receipt) {
cur_frm.add_custom_button(
this.frm.add_custom_button(
__("Purchase Receipt"),
this.make_purchase_receipt,
__("Create")
@@ -408,14 +408,15 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
}
}
}
// Please do not add precision in the below flt function
if (flt(doc.per_billed) < 100)
cur_frm.add_custom_button(
this.frm.add_custom_button(
__("Purchase Invoice"),
this.make_purchase_invoice,
__("Create")
);
if (flt(doc.per_billed, 2) < 100 && doc.status != "Delivered") {
if (flt(doc.per_billed) < 100 && doc.status != "Delivered") {
this.frm.add_custom_button(
__("Payment"),
() => this.make_payment_entry(),
@@ -423,7 +424,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
);
}
if (flt(doc.per_billed, 2) < 100) {
if (flt(doc.per_billed) < 100) {
this.frm.add_custom_button(
__("Payment Request"),
function () {

View File

@@ -581,7 +581,7 @@ class PurchaseOrder(BuyingController):
def update_receiving_percentage(self):
total_qty, received_qty = 0.0, 0.0
for item in self.items:
received_qty += item.received_qty
received_qty += min(item.received_qty, item.qty)
total_qty += item.qty
if total_qty:
self.db_set("per_received", flt(received_qty / total_qty) * 100, update_modified=False)
@@ -625,9 +625,11 @@ class PurchaseOrder(BuyingController):
if not self.is_against_so():
return
for item in removed_items:
prev_ordered_qty = frappe.get_cached_value(
"Sales Order Item", item.get("sales_order_item"), "ordered_qty"
prev_ordered_qty = (
frappe.get_cached_value("Sales Order Item", item.get("sales_order_item"), "ordered_qty")
or 0.0
)
frappe.db.set_value(
"Sales Order Item", item.get("sales_order_item"), "ordered_qty", prev_ordered_qty - item.qty
)

View File

@@ -14,18 +14,25 @@ def get_data():
"Material Request": ["items", "material_request"],
"Supplier Quotation": ["items", "supplier_quotation"],
"Project": ["items", "project"],
"Sales Order": ["items", "sales_order"],
"BOM": ["items", "bom"],
"Production Plan": ["items", "production_plan"],
"Blanket Order": ["items", "blanket_order"],
},
"transactions": [
{"label": _("Related"), "items": ["Purchase Receipt", "Purchase Invoice"]},
{"label": _("Related"), "items": ["Purchase Receipt", "Purchase Invoice", "Sales Order"]},
{"label": _("Payment"), "items": ["Payment Entry", "Journal Entry", "Payment Request"]},
{
"label": _("Reference"),
"items": ["Material Request", "Supplier Quotation", "Project", "Auto Repeat"],
"items": ["Supplier Quotation", "Project", "Auto Repeat"],
},
{
"label": _("Manufacturing"),
"items": ["Material Request", "BOM", "Production Plan", "Blanket Order"],
},
{
"label": _("Sub-contracting"),
"items": ["Subcontracting Order", "Subcontracting Receipt", "Stock Entry"],
},
{"label": _("Internal"), "items": ["Sales Order"]},
],
}

View File

@@ -146,8 +146,8 @@ frappe.ui.form.on("Request for Quotation", {
return;
}
},
"Download PDF for Supplier",
"Download"
__("Download PDF for Supplier"),
__("Download")
);
},
__("Tools")
@@ -272,9 +272,10 @@ frappe.ui.form.on("Request for Quotation", {
});
};
dialog.fields_dict.note.$wrapper
.append(`<p class="small text-muted">This is a preview of the email to be sent. A PDF of the document will
automatically be attached with the email.</p>`);
const msg = __(
"This is a preview of the email to be sent. A PDF of the document will automatically be attached with the email."
);
dialog.fields_dict.note.$wrapper.append(`<p class="small text-muted">${msg}</p>`);
dialog.show();
},

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, []
@@ -40,7 +41,6 @@ def get_data(filters):
po = frappe.qb.DocType("Purchase Order")
po_item = frappe.qb.DocType("Purchase Order Item")
pi_item = frappe.qb.DocType("Purchase Invoice Item")
pr_item = frappe.qb.DocType("Purchase Receipt Item")
query = (
frappe.qb.from_(po)
@@ -48,8 +48,6 @@ def get_data(filters):
.on(po_item.parent == po.name)
.left_join(pi_item)
.on((pi_item.po_detail == po_item.name) & (pi_item.docstatus == 1))
.left_join(pr_item)
.on((pr_item.purchase_order_item == po_item.name) & (pr_item.docstatus == 1))
.select(
po.transaction_date.as_("date"),
po_item.schedule_date.as_("required_date"),
@@ -63,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"),
(pr_item.base_amount).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"
@@ -95,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"

View File

@@ -345,9 +345,21 @@ class AccountsController(TransactionBase):
repost_doc.flags.ignore_links = True
repost_doc.save(ignore_permissions=True)
def _remove_advance_payment_ledger_entries(self):
adv = qb.DocType("Advance Payment Ledger Entry")
qb.from_(adv).delete().where(adv.voucher_type.eq(self.doctype) & adv.voucher_no.eq(self.name)).run()
advance_payment_doctypes = frappe.get_hooks("advance_payment_doctypes")
if self.doctype in advance_payment_doctypes:
qb.from_(adv).delete().where(
adv.against_voucher_type.eq(self.doctype) & adv.against_voucher_no.eq(self.name)
).run()
def on_trash(self):
from erpnext.accounts.utils import delete_exchange_gain_loss_journal
self._remove_advance_payment_ledger_entries()
self._remove_references_in_repost_doctypes()
self._remove_references_in_unreconcile()
self.remove_serial_and_batch_bundle()
@@ -451,6 +463,11 @@ class AccountsController(TransactionBase):
)
def validate_invoice_documents_schedule(self):
if self.is_return:
self.payment_terms_template = ""
self.payment_schedule = []
return
self.validate_payment_schedule_dates()
self.set_due_date()
self.set_payment_schedule()
@@ -465,7 +482,7 @@ class AccountsController(TransactionBase):
self.validate_payment_schedule_amount()
def validate_all_documents_schedule(self):
if self.doctype in ("Sales Invoice", "Purchase Invoice") and not self.is_return:
if self.doctype in ("Sales Invoice", "Purchase Invoice"):
self.validate_invoice_documents_schedule()
elif self.doctype in ("Quotation", "Purchase Order", "Sales Order"):
self.validate_non_invoice_documents_schedule()
@@ -1079,9 +1096,11 @@ class AccountsController(TransactionBase):
return "Purchase Return"
elif self.doctype == "Delivery Note" and self.is_return:
return "Sales Return"
elif (self.doctype == "Sales Invoice" and self.is_return) or self.doctype == "Purchase Invoice":
elif self.doctype == "Sales Invoice" and self.is_return:
return "Credit Note"
elif (self.doctype == "Purchase Invoice" and self.is_return) or self.doctype == "Sales Invoice":
elif self.doctype == "Sales Invoice" and self.is_debit_note:
return "Debit Note"
elif self.doctype == "Purchase Invoice" and self.is_return:
return "Debit Note"
return self.doctype
@@ -1279,7 +1298,11 @@ class AccountsController(TransactionBase):
d.exchange_gain_loss = difference
def make_precision_loss_gl_entry(self, gl_entries):
round_off_account, round_off_cost_center = get_round_off_account_and_cost_center(
(
round_off_account,
round_off_cost_center,
round_off_for_opening,
) = get_round_off_account_and_cost_center(
self.company, "Purchase Invoice", self.name, self.use_company_roundoff_cost_center
)
@@ -1930,21 +1953,23 @@ class AccountsController(TransactionBase):
return stock_items
def set_total_advance_paid(self):
ple = frappe.qb.DocType("Payment Ledger Entry")
party = self.customer if self.doctype == "Sales Order" else self.supplier
def calculate_total_advance_from_ledger(self):
adv = frappe.qb.DocType("Advance Payment Ledger Entry")
advance = (
frappe.qb.from_(ple)
.select(ple.account_currency, Abs(Sum(ple.amount_in_account_currency)).as_("amount"))
frappe.qb.from_(adv)
.select(adv.currency.as_("account_currency"), Abs(Sum(adv.amount)).as_("amount"))
.where(
(ple.against_voucher_type == self.doctype)
& (ple.against_voucher_no == self.name)
& (ple.party == party)
& (ple.delinked == 0)
& (ple.company == self.company)
(adv.against_voucher_type == self.doctype)
& (adv.against_voucher_no == self.name)
& (adv.company == self.company)
)
.run(as_dict=True)
)
return advance
def set_total_advance_paid(self):
advance = self.calculate_total_advance_from_ledger()
advance_paid, order_total = None, None
if advance:
advance = advance[0]
@@ -2440,6 +2465,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"
@@ -2464,7 +2495,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:
@@ -2473,17 +2504,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)
@@ -2541,6 +2613,67 @@ class AccountsController(TransactionBase):
repost_ledger.insert()
repost_ledger.submit()
def get_advance_payment_doctypes(self) -> list:
return frappe.get_hooks("advance_payment_doctypes")
def make_advance_payment_ledger_for_journal(self):
advance_payment_doctypes = self.get_advance_payment_doctypes()
advance_doctype_references = [
x for x in self.accounts if x.reference_type in advance_payment_doctypes
]
for x in advance_doctype_references:
# Looking for payments
dr_or_cr = (
"credit_in_account_currency"
if x.account_type == "Receivable"
else "debit_in_account_currency"
)
amount = x.get(dr_or_cr)
if amount > 0:
doc = frappe.new_doc("Advance Payment Ledger Entry")
doc.company = self.company
doc.voucher_type = self.doctype
doc.voucher_no = self.name
doc.against_voucher_type = x.reference_type
doc.against_voucher_no = x.reference_name
doc.amount = amount if self.docstatus == 1 else -1 * amount
doc.event = "Submit" if self.docstatus == 1 else "Cancel"
doc.currency = x.account_currency
doc.flags.ignore_permissions = 1
doc.save()
def make_advance_payment_ledger_for_payment(self):
advance_payment_doctypes = self.get_advance_payment_doctypes()
advance_doctype_references = [
x for x in self.references if x.reference_doctype in advance_payment_doctypes
]
currency = (
self.paid_from_account_currency
if self.payment_type == "Receive"
else self.paid_to_account_currency
)
for x in advance_doctype_references:
doc = frappe.new_doc("Advance Payment Ledger Entry")
doc.company = self.company
doc.voucher_type = self.doctype
doc.voucher_no = self.name
doc.against_voucher_type = x.reference_doctype
doc.against_voucher_no = x.reference_name
doc.amount = x.allocated_amount if self.docstatus == 1 else -1 * x.allocated_amount
doc.currency = currency
doc.event = "Submit" if self.docstatus == 1 else "Cancel"
doc.flags.ignore_permissions = 1
doc.save()
def make_advance_payment_ledger_entries(self):
if self.docstatus != 0:
if self.doctype == "Journal Entry":
self.make_advance_payment_ledger_for_journal()
elif self.doctype == "Payment Entry":
self.make_advance_payment_ledger_for_payment()
@frappe.whitelist()
def get_tax_rate(account_head):

View File

@@ -356,14 +356,14 @@ class BuyingController(SubcontractingController):
if not self.is_internal_transfer():
return
self.set_sales_incoming_rate_for_internal_transfer()
allow_at_arms_length_price = frappe.get_cached_value(
"Stock Settings", None, "allow_internal_transfer_at_arms_length_price"
)
if allow_at_arms_length_price:
return
self.set_sales_incoming_rate_for_internal_transfer()
for d in self.get("items"):
d.discount_percentage = 0.0
d.discount_amount = 0.0

View File

@@ -11,7 +11,13 @@ def set_print_templates_for_item_table(doc, settings):
"items": {
"qty": "templates/print_formats/includes/item_table_qty.html",
"serial_and_batch_bundle": "templates/print_formats/includes/serial_and_batch_bundle.html",
}
},
"packed_items": {
"serial_and_batch_bundle": "templates/print_formats/includes/serial_and_batch_bundle.html",
},
"supplied_items": {
"serial_and_batch_bundle": "templates/print_formats/includes/serial_and_batch_bundle.html",
},
}
doc.flags.compact_item_fields = ["description", "qty", "rate", "amount"]

View File

@@ -415,7 +415,6 @@ def get_batches_from_stock_ledger_entries(searchfields, txt, filters, start=0, p
stock_ledger_entry.batch_no,
Sum(stock_ledger_entry.actual_qty).as_("qty"),
)
.where((batch_table.expiry_date >= expiry_date) | (batch_table.expiry_date.isnull()))
.where(stock_ledger_entry.is_cancelled == 0)
.where(
(stock_ledger_entry.item_code == filters.get("item_code"))
@@ -428,6 +427,9 @@ def get_batches_from_stock_ledger_entries(searchfields, txt, filters, start=0, p
.limit(page_len)
)
if not filters.get("include_expired_batches"):
query = query.where((batch_table.expiry_date >= expiry_date) | (batch_table.expiry_date.isnull()))
query = query.select(
Concat("MFG-", batch_table.manufacturing_date).as_("manufacturing_date"),
Concat("EXP-", batch_table.expiry_date).as_("expiry_date"),
@@ -466,7 +468,6 @@ def get_batches_from_serial_and_batch_bundle(searchfields, txt, filters, start=0
bundle.batch_no,
Sum(bundle.qty).as_("qty"),
)
.where((batch_table.expiry_date >= expiry_date) | (batch_table.expiry_date.isnull()))
.where(stock_ledger_entry.is_cancelled == 0)
.where(
(stock_ledger_entry.item_code == filters.get("item_code"))
@@ -479,6 +480,11 @@ def get_batches_from_serial_and_batch_bundle(searchfields, txt, filters, start=0
.limit(page_len)
)
if not filters.get("include_expired_batches"):
bundle_query = bundle_query.where(
(batch_table.expiry_date >= expiry_date) | (batch_table.expiry_date.isnull())
)
bundle_query = bundle_query.select(
Concat("MFG-", batch_table.manufacturing_date),
Concat("EXP-", batch_table.expiry_date),

View File

@@ -79,6 +79,9 @@ def validate_returned_items(doc):
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,
@@ -104,15 +107,24 @@ def validate_returned_items(doc):
items_returned = False
for d in doc.get("items"):
key = d.item_code
raise_exception = False
if doc.doctype in ["Purchase Receipt", "Purchase Invoice"]:
field = frappe.scrub(doc.doctype) + "_item"
if d.get(field):
key = (d.item_code, d.get(field))
raise_exception = True
if d.item_code and (flt(d.qty) < 0 or flt(d.get("received_qty")) < 0):
if d.item_code not in valid_items:
frappe.throw(
if key not in valid_items:
frappe.msgprint(
_("Row # {0}: Returned Item {1} does not exist in {2} {3}").format(
d.idx, d.item_code, doc.doctype, doc.return_against
)
),
raise_exception=raise_exception,
)
else:
ref = valid_items.get(d.item_code, frappe._dict())
ref = valid_items.get(key, frappe._dict())
validate_quantity(doc, d, ref, valid_items, already_returned_items)
if (
@@ -193,8 +205,12 @@ def validate_quantity(doc, args, ref, valid_items, already_returned_items):
def get_ref_item_dict(valid_items, ref_item_row):
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
key = ref_item_row.item_code
if ref_item_row.get("name"):
key = (ref_item_row.item_code, ref_item_row.name)
valid_items.setdefault(
ref_item_row.item_code,
key,
frappe._dict(
{
"qty": 0,
@@ -208,7 +224,7 @@ def get_ref_item_dict(valid_items, ref_item_row):
}
),
)
item_dict = valid_items[ref_item_row.item_code]
item_dict = valid_items[key]
item_dict["qty"] += ref_item_row.qty
item_dict["stock_qty"] += ref_item_row.get("stock_qty", 0)
if ref_item_row.get("rate", 0) > item_dict["rate"]:
@@ -335,6 +351,9 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None, return_agai
doc.select_print_heading = frappe.get_cached_value("Print Heading", _("Debit Note"))
if source.tax_withholding_category:
doc.set_onload("supplier_tds", source.tax_withholding_category)
elif doctype == "Delivery Note":
# manual additions to the return should hit the return warehous, too
doc.set_warehouse = default_warehouse_for_sales_return
for tax in doc.get("taxes") or []:
if tax.charge_type == "Actual":
@@ -581,6 +600,10 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None, return_agai
if default_warehouse_for_sales_return:
target_doc.warehouse = default_warehouse_for_sales_return
if not source_doc.use_serial_batch_fields and source_doc.serial_and_batch_bundle:
target_doc.serial_no = None
target_doc.batch_no = None
if (
(source_doc.serial_no or source_doc.batch_no)
and not source_doc.serial_and_batch_bundle
@@ -883,6 +906,7 @@ def get_serial_batches_based_on_bundle(field, _bundle_ids):
"`tabSerial and Batch Entry`.`serial_no`",
"`tabSerial and Batch Entry`.`batch_no`",
"`tabSerial and Batch Entry`.`qty`",
"`tabSerial and Batch Entry`.`incoming_rate`",
"`tabSerial and Batch Bundle`.`voucher_detail_no`",
"`tabSerial and Batch Bundle`.`voucher_type`",
"`tabSerial and Batch Bundle`.`voucher_no`",
@@ -904,15 +928,23 @@ def get_serial_batches_based_on_bundle(field, _bundle_ids):
if key not in available_dict:
available_dict[key] = frappe._dict(
{"qty": 0.0, "serial_nos": defaultdict(float), "batches": defaultdict(float)}
{
"qty": 0.0,
"serial_nos": defaultdict(float),
"batches": defaultdict(float),
"serial_nos_valuation": defaultdict(float),
"batches_valuation": defaultdict(float),
}
)
available_dict[key]["qty"] += row.qty
if row.serial_no:
available_dict[key]["serial_nos"][row.serial_no] += row.qty
available_dict[key]["serial_nos_valuation"][row.serial_no] = row.incoming_rate
elif row.batch_no:
available_dict[key]["batches"][row.batch_no] += row.qty
available_dict[key]["batches_valuation"][row.batch_no] = row.incoming_rate
return available_dict
@@ -948,12 +980,13 @@ def get_serial_and_batch_bundle(field, doctype, reference_ids, is_rejected=False
)
)
else:
fields = [
"serial_and_batch_bundle",
]
fields = ["serial_and_batch_bundle"]
if is_rejected:
fields.extend(["rejected_serial_and_batch_bundle", "return_qty_from_rejected_warehouse"])
fields.append("rejected_serial_and_batch_bundle")
if doctype == "Purchase Receipt Item":
fields.append("return_qty_from_rejected_warehouse")
del filters["rejected_serial_and_batch_bundle"]
data = frappe.get_all(
@@ -987,7 +1020,14 @@ def filter_serial_batches(parent_doc, data, row, warehouse_field=None, qty_field
warehouse = row.get(warehouse_field)
qty = abs(row.get(qty_field))
filterd_serial_batch = frappe._dict({"serial_nos": [], "batches": defaultdict(float)})
filterd_serial_batch = frappe._dict(
{
"serial_nos": [],
"batches": defaultdict(float),
"serial_nos_valuation": data.get("serial_nos_valuation"),
"batches_valuation": data.get("batches_valuation"),
}
)
if data.serial_nos:
available_serial_nos = []
@@ -996,8 +1036,8 @@ def filter_serial_batches(parent_doc, data, row, warehouse_field=None, qty_field
available_serial_nos.append(serial_no)
if available_serial_nos:
if parent_doc.doctype in ["Purchase Invoice", "Purchase Reecipt"]:
available_serial_nos = get_available_serial_nos(available_serial_nos)
if parent_doc.doctype in ["Purchase Invoice", "Purchase Receipt"]:
available_serial_nos = get_available_serial_nos(available_serial_nos, warehouse)
if len(available_serial_nos) > qty:
filterd_serial_batch["serial_nos"] = sorted(available_serial_nos[0 : cint(qty)])
@@ -1012,7 +1052,7 @@ def filter_serial_batches(parent_doc, data, row, warehouse_field=None, qty_field
if batch_qty <= 0:
continue
if parent_doc.doctype in ["Purchase Invoice", "Purchase Reecipt"]:
if parent_doc.doctype in ["Purchase Invoice", "Purchase Receipt"]:
batch_qty = get_available_batch_qty(
parent_doc,
batch_no,
@@ -1082,6 +1122,8 @@ def make_serial_batch_bundle_for_return(data, child_doc, parent_doc, warehouse_f
"warehouse": warehouse,
"serial_nos": data.get("serial_nos"),
"batches": data.get("batches"),
"serial_nos_valuation": data.get("serial_nos_valuation"),
"batches_valuation": data.get("batches_valuation"),
"posting_date": parent_doc.posting_date,
"posting_time": parent_doc.posting_time,
"voucher_type": parent_doc.doctype,

View File

@@ -21,9 +21,15 @@ class SellingController(StockController):
def onload(self):
super().onload()
if self.doctype in ("Sales Order", "Delivery Note", "Sales Invoice"):
if self.doctype in ("Sales Order", "Delivery Note", "Sales Invoice", "Quotation"):
for item in self.get("items") + (self.get("packed_items") or []):
item.update(get_bin_details(item.item_code, item.warehouse, include_child_warehouses=True))
company = self.company
item.update(
get_bin_details(
item.item_code, item.warehouse, company=company, include_child_warehouses=True
)
)
def validate(self):
super().validate()
@@ -68,19 +74,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 +167,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 +189,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:
@@ -358,12 +375,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(

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