Compare commits

...

760 Commits

Author SHA1 Message Date
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]
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
d392660d45 chore: release version 15.39.1 (#43800)
* fix: map doc from purchase order

(cherry picked from commit 60ceb91ace)

* test: auto create purchase receipt

(cherry picked from commit 59887bbc13)

* fix: better implementation, handle missing purchase order

(cherry picked from commit 66211dafd6)

* 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

---------

Co-authored-by: Ninad1306 <ninad_1063@yahoo.com>
Co-authored-by: Smit Vora <smitvora203@gmail.com>
2024-10-23 14:27:10 +05:30
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
Frappe PR Bot
e0a45a5a54 chore(release): Bumped to Version 15.39.0
# [15.39.0](https://github.com/frappe/erpnext/compare/v15.38.4...v15.39.0) (2024-10-23)

### Bug Fixes

* "show_remarks" checkbox in Process statement of accounts ([f7717c9](f7717c91bc))
* added validation for UOM must be whole number (backport [#43710](https://github.com/frappe/erpnext/issues/43710)) ([#43712](https://github.com/frappe/erpnext/issues/43712)) ([60ffcd0](60ffcd0574))
* Call super onload_post_render inside pos_invoice.js ([1281d9d](1281d9d21d))
* coupon code validation logic ([aeaadb1](aeaadb1e30))
* **deferred_revenue:** Escape account in query ([fac27d9](fac27d9dff))
* do not make new depreciation for fully depreciated asset ([ddb38db](ddb38db5c4))
* Freeze Screen on load invoices on POS Closing Entry ([f343d5a](f343d5a24d))
* get party advance amount based on account ([b673377](b673377b70))
* get period estimate till service end date ([148d7e7](148d7e798b))
* get stock accounts from the doc instead of db in validate_stock_accounts ([39387e9](39387e9f54))
* incorrect amount in bank clearance ([52be45c](52be45c5df))
* lead create opp from connection not working ([9e56f21](9e56f213a3))
* list view and form status not same for purchase order (backport [#43690](https://github.com/frappe/erpnext/issues/43690)) ([#43692](https://github.com/frappe/erpnext/issues/43692)) ([a33d553](a33d5535a7))
* only show pay button for specific doctype in portal ([d2e5b2a](d2e5b2aa1d))
* party_balance based on company in payment entry ([04fbcc6](04fbcc64ff))
* remove extra space ([50dd8d9](50dd8d9df7))
* removed unmerged patches ([2e0cf36](2e0cf36901))
* Required Changes to Support e-Waybill Generation for Material Transfer Return ([#43061](https://github.com/frappe/erpnext/issues/43061)) ([2205ae8](2205ae8e54))
* show total amount on report summary ([ab20344](ab20344141))
* use correct variable in error message (backport [#43790](https://github.com/frappe/erpnext/issues/43790)) ([#43792](https://github.com/frappe/erpnext/issues/43792)) ([879b2b7](879b2b778a))
* Workspace link for Work Order Consumed Materials report (backport [#43753](https://github.com/frappe/erpnext/issues/43753)) ([#43754](https://github.com/frappe/erpnext/issues/43754)) ([1fa9030](1fa9030aee))

### Features

* added assignee email field in asset maintenance log ([a3b8f97](a3b8f9759d))
2024-10-23 04:48:23 +00:00
ruthra kumar
692de892ae Merge pull request #43774 from frappe/version-15-hotfix
chore: release v15
2024-10-23 10:17:07 +05:30
mergify[bot]
879b2b778a fix: use correct variable in error message (backport #43790) (#43792)
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
fix: use correct variable in error message (#43790)
2024-10-22 19:43:16 +02:00
Smit Vora
12530616a7 Merge pull request #43786 from ljain112/fix-manual-backport 2024-10-22 20:12:53 +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
Smit Vora
9ddf1ccedd Merge pull request #43785 from frappe/mergify/bp/version-15-hotfix/pr-43462
fix: get stock accounts from the doc in `validate_stock_accounts` in Journal Entry (backport #43462)
2024-10-22 19:59:31 +05:30
Smit Vora
0495160f81 Merge pull request #43783 from frappe/mergify/bp/version-15-hotfix/pr-43061
fix: Required Changes to Support e-Waybill Generation for Material Transfer Return (backport #43061)
2024-10-22 19:59:19 +05:30
ljain112
d2e5b2aa1d fix: only show pay button for specific doctype in portal 2024-10-22 19:49:37 +05:30
Vishakh Desai
39387e9f54 fix: get stock accounts from the doc instead of db in validate_stock_accounts
(cherry picked from commit 30954586d8)
2024-10-22 14:07:43 +00:00
Ninad Parikh
2205ae8e54 fix: Required Changes to Support e-Waybill Generation for Material Transfer Return (#43061)
(cherry picked from commit 004c4e21d4)
2024-10-22 13:59:31 +00:00
ruthra kumar
29fe23bc0a Merge pull request #43780 from frappe/mergify/bp/version-15-hotfix/pr-43778
refactor: validate_return_against_account (backport #43778)
2024-10-22 17:40:39 +05:30
Raffael Meyer
8d97966662 refactor: validate_return_against_account (#43778)
(cherry picked from commit c4faa0e101)
2024-10-22 11:51:35 +00:00
ruthra kumar
c99d0535f8 Merge pull request #43777 from frappe/mergify/bp/version-15-hotfix/pr-43775
fix(deferred_revenue): Escape account in query (backport #43775)
2024-10-22 16:40:11 +05:30
Corentin Forler
fac27d9dff fix(deferred_revenue): Escape account in query
(cherry picked from commit c7b3ae41d4)
2024-10-22 10:42:25 +00:00
Khushi Rawat
0519263882 Merge pull request #43763 from frappe/mergify/bp/version-15-hotfix/pr-43378
feat: added task assignee email field in asset maintenance log (backport #43378)
2024-10-22 12:22:39 +05:30
ruthra kumar
71479ad47b Merge pull request #43768 from frappe/mergify/bp/version-15-hotfix/pr-43766
refactor: allow unreconcile on bank and cash entry type journals (backport #43766)
2024-10-22 12:13:04 +05:30
ruthra kumar
88f5e3f160 refactor: allow unreconcile on bank and cash entry type journals
(cherry picked from commit 2c4f37f488)
2024-10-22 06:36:08 +00:00
ruthra kumar
71837ab400 Merge pull request #43765 from frappe/mergify/bp/version-15-hotfix/pr-43761
fix: coupon code validation logic (backport #43761)
2024-10-22 10:33:41 +05:30
ruthra kumar
853ca1fcee chore: resolve conflict 2024-10-22 10:15:28 +05:30
bhaveshkumar.j
50dd8d9df7 fix: remove extra space
(cherry picked from commit 1561a9e1bf)
2024-10-22 04:37:49 +00:00
bhaveshkumar.j
aeaadb1e30 fix: coupon code validation logic
(cherry picked from commit d04257a32d)

# Conflicts:
#	erpnext/accounts/doctype/pricing_rule/utils.py
2024-10-22 04:37:49 +00:00
Khushi Rawat
2e0cf36901 fix: removed unmerged patches 2024-10-22 02:06:41 +05:30
Khushi Rawat
1d5345abc1 chore: resolved conflicts 2024-10-22 01:23:49 +05:30
Khushi Rawat
0a03076148 chore: resolved conflicts 2024-10-22 01:17:33 +05:30
Khushi Rawat
a3b8f9759d feat: added assignee email field in asset maintenance log
(cherry picked from commit 5911934dc7)

# Conflicts:
#	erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json
#	erpnext/patches.txt
2024-10-21 19:44:34 +00:00
Shariq Ansari
f51c511bcc Merge pull request #43760 from frappe/mergify/bp/version-15-hotfix/pr-43759
fix: lead create opp from connection not working (backport #43759)
2024-10-21 20:41:29 +05:30
Shariq Ansari
9e56f213a3 fix: lead create opp from connection not working
(cherry picked from commit 0dc518b1c3)
2024-10-21 14:57:43 +00:00
mergify[bot]
1fa9030aee fix: Workspace link for Work Order Consumed Materials report (backport #43753) (#43754)
fix: Workspace link for Work Order Consumed Materials report (#43753)

(cherry picked from commit e94ffb87cd)

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2024-10-21 17:15:34 +05:30
ruthra kumar
62226696aa Merge pull request #43742 from frappe/mergify/bp/version-15-hotfix/pr-43726
fix: "show_remarks" checkbox in Process statement of accounts (backport #43726)
2024-10-21 13:08:53 +05:30
ruthra kumar
605a30a7e7 Merge pull request #43744 from frappe/mergify/bp/version-15-hotfix/pr-43727
fix: get period estimate till service end date (backport #43727)
2024-10-21 13:08:33 +05:30
ruthra kumar
0996aff79d Merge pull request #43746 from frappe/mergify/bp/version-15-hotfix/pr-43720
fix: party_balance based on company in payment entry (backport #43720)
2024-10-21 13:08:12 +05:30
ruthra kumar
501c53db05 Merge pull request #43748 from frappe/mergify/bp/version-15-hotfix/pr-42461
fix: Freeze Screen on load invoices on POS Closing Entry (backport #42461)
2024-10-21 13:07:54 +05:30
HarryPaulo
f343d5a24d fix: Freeze Screen on load invoices on POS Closing Entry
(cherry picked from commit 486d396174)
2024-10-21 06:58:05 +00:00
ljain112
04fbcc64ff fix: party_balance based on company in payment entry
(cherry picked from commit 97c9adf06b)
2024-10-21 06:53:44 +00:00
venkat102
148d7e798b fix: get period estimate till service end date
(cherry picked from commit a7ba7e9c28)
2024-10-21 06:51:05 +00:00
ljain112
f7717c91bc fix: "show_remarks" checkbox in Process statement of accounts
(cherry picked from commit f4600df1f7)
2024-10-21 06:44:29 +00:00
ruthra kumar
c05382fa48 Merge pull request #43740 from frappe/mergify/bp/version-15-hotfix/pr-43728
fix: get party advance amount based on account (backport #43728)
2024-10-21 11:53:00 +05:30
venkat102
b673377b70 fix: get party advance amount based on account
(cherry picked from commit d7fa95dd2f)
2024-10-21 05:47:54 +00:00
Khushi Rawat
6bbc8e0544 Merge pull request #43733 from frappe/mergify/bp/version-15-hotfix/pr-43723
fix: do not make new depreciation for fully depreciated asset (backport #43723)
2024-10-21 02:13:44 +05:30
Khushi Rawat
ddb38db5c4 fix: do not make new depreciation for fully depreciated asset
(cherry picked from commit 25de412371)
2024-10-19 16:19:19 +00:00
ruthra kumar
03b5d5a0e0 Merge pull request #43719 from frappe/mergify/bp/version-15-hotfix/pr-43295
fix: translate Update default_success_action.py (backport #43295)
2024-10-18 12:05:27 +05:30
Doğancan
f70506fc92 refactor: update default_success_action.py
The _(doctype) inside get_message is removed from the .format() method. The reason is that _(doctype) would attempt to translate the doctype itself, which is generally not required since the doctypes in doctype_list are system-level terms. The main string "{0} has been submitted successfully" should be translated, and then it should receive the doctype name as an argument.

(cherry picked from commit 804558e5bf)
2024-10-18 06:18:53 +00:00
ruthra kumar
a58ce52729 Merge pull request #43717 from frappe/mergify/bp/version-15-hotfix/pr-42898
fix: incorrect amount in bank clearance (backport #42898)
2024-10-18 10:55:16 +05:30
ruthra kumar
a5d9f5518f Merge pull request #43718 from frappe/mergify/bp/version-15-hotfix/pr-43180
fix: Call super onload_post_render inside pos_invoice.js (backport #43180)
2024-10-18 10:54:57 +05:30
devdiogenes
1281d9d21d fix: Call super onload_post_render inside pos_invoice.js
(cherry picked from commit 4a3eca963c)
2024-10-18 05:12:33 +00:00
NIYAZ RAZAK
52be45c5df fix: incorrect amount in bank clearance
(cherry picked from commit 9a11df59fc)
2024-10-18 05:04:26 +00:00
Frappe PR Bot
08cabd1717 chore(release): Bumped to Version 15.38.4
## [15.38.4](https://github.com/frappe/erpnext/compare/v15.38.3...v15.38.4) (2024-10-17)

### Bug Fixes

* list view and form status not same for purchase order (backport [#43690](https://github.com/frappe/erpnext/issues/43690)) ([#43692](https://github.com/frappe/erpnext/issues/43692)) ([752d175](752d175d22))
2024-10-17 16:05:56 +00:00
mergify[bot]
60ffcd0574 fix: added validation for UOM must be whole number (backport #43710) (#43712)
fix: added validation for UOM must be whole number (#43710)

(cherry picked from commit 4fd4a37dc9)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-10-17 21:34:54 +05:30
rohitwaghchaure
6f98fe15e4 Merge pull request #43706 from frappe/mergify/bp/version-15/pr-43692
fix: list view and form status not same for purchase order (backport #43690) (backport #43692)
2024-10-17 21:34:34 +05:30
ruthra kumar
601ea444ca Merge pull request #43708 from aerele/report-summary-balancesheet
fix: show total amount on report summary
2024-10-17 13:32:47 +05:30
venkat102
ab20344141 fix: show total amount on report summary 2024-10-17 12:13:15 +05:30
mergify[bot]
752d175d22 fix: list view and form status not same for purchase order (backport #43690) (#43692)
* fix: list view and form status not same for purchase order (#43690)

(cherry picked from commit a671fe13d4)

# Conflicts:
#	erpnext/buying/doctype/purchase_order/purchase_order.js
#	erpnext/buying/doctype/purchase_order/purchase_order_list.js

* chore: fix conflicts

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
(cherry picked from commit a33d5535a7)
2024-10-17 04:44:18 +00:00
mergify[bot]
a33d5535a7 fix: list view and form status not same for purchase order (backport #43690) (#43692)
* fix: list view and form status not same for purchase order (#43690)

(cherry picked from commit a671fe13d4)

# Conflicts:
#	erpnext/buying/doctype/purchase_order/purchase_order.js
#	erpnext/buying/doctype/purchase_order/purchase_order_list.js

* chore: fix conflicts

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-10-16 14:57:57 +05:30
Frappe PR Bot
99ead94ffe chore(release): Bumped to Version 15.38.3
## [15.38.3](https://github.com/frappe/erpnext/compare/v15.38.2...v15.38.3) (2024-10-16)

### Bug Fixes

* added parentheses for correct query formation for logical OR condition ([21a7dd4](21a7dd43a9))
* added string for translation in bank reconciliation statement ([e10a580](e10a58074f))
* conversion factor issue (backport [#43645](https://github.com/frappe/erpnext/issues/43645)) ([#43674](https://github.com/frappe/erpnext/issues/43674)) ([b2deb89](b2deb89826))
* delete invalid pricing rule on change of applicable_for ([5d6fc71](5d6fc71556))
* don't update reference to SI / PI on advances ([b72906a](b72906a7a1))
* ignore free item when qty is zero ([e5aaa5b](e5aaa5b6e5))
* incorrect warehouse in the serial no selector for rejection (backport [#43671](https://github.com/frappe/erpnext/issues/43671)) ([#43673](https://github.com/frappe/erpnext/issues/43673)) ([c490a66](c490a66540))
* Link opportunity from RFQ to supplier quotation ([eb1f125](eb1f1255eb))
* missing child company accounts in consolidated balance sheet ([4db12fe](4db12fe2da))
* quotation to so frappe crm (backport [#43644](https://github.com/frappe/erpnext/issues/43644)) ([#43646](https://github.com/frappe/erpnext/issues/43646)) ([f3ceb4a](f3ceb4ac7d))
* refetch items from BOM if 'Use Multi-Level BOM' has changed usin… (backport [#43672](https://github.com/frappe/erpnext/issues/43672)) ([#43676](https://github.com/frappe/erpnext/issues/43676)) ([492ba53](492ba539e8))
* removed unused query ([8668ae9](8668ae92d8))
* run gl_entries and closing voucher processes in same function ([ea12897](ea12897ce9))
* show incorrect entries filter in Stock Ledger Invariant Check report (backport [#43619](https://github.com/frappe/erpnext/issues/43619)) ([#43622](https://github.com/frappe/erpnext/issues/43622)) ([d604b12](d604b12d51))
* **stock:** Grab posting date/time from SABB (backport [#43493](https://github.com/frappe/erpnext/issues/43493)) ([#43502](https://github.com/frappe/erpnext/issues/43502)) ([cd9f949](cd9f949b12))
* update formatings ([c2c6d27](c2c6d27625))
* update formatings ([a70181e](a70181e025))
* update item details with actual quantity. ([930e79c](930e79c351))
* Use `ref_doc.get()` for `party_account_currency` ([928b6b1](928b6b1510))
* zero incoming rate for delivery note return ([#43642](https://github.com/frappe/erpnext/issues/43642)) ([85088e4](85088e4aff))
2024-10-16 05:01:44 +00:00
ruthra kumar
e05ae14d49 Merge pull request #43667 from frappe/version-15-hotfix
chore: release v15
2024-10-16 10:30:25 +05:30
mergify[bot]
cd9f949b12 fix(stock): Grab posting date/time from SABB (backport #43493) (#43502)
fix(stock): Grab posting date/time from SABB (#43493)

(cherry picked from commit ade121dac6)

Co-authored-by: Corentin Forler <10946971+cogk@users.noreply.github.com>
2024-10-15 17:45:35 +05:30
ruthra kumar
aef544cd53 Merge pull request #43684 from frappe/mergify/bp/version-15-hotfix/pr-43661
fix: missing child company accounts in consolidated balance sheet (backport #43661)
2024-10-15 17:37:52 +05:30
ruthra kumar
d34025dc11 Merge pull request #43682 from frappe/mergify/bp/version-15-hotfix/pr-43663
fix: run gl_entries and closing voucher processes in same function (backport #43663)
2024-10-15 17:22:39 +05:30
ljain112
4db12fe2da fix: missing child company accounts in consolidated balance sheet
(cherry picked from commit 7fae9d57d2)
2024-10-15 11:38:10 +00:00
ljain112
ea12897ce9 fix: run gl_entries and closing voucher processes in same function
(cherry picked from commit af4daa5b0f)
2024-10-15 11:35:55 +00:00
ruthra kumar
802d9b2d4a Merge pull request #43680 from frappe/mergify/bp/version-15-hotfix/pr-43602
fix: added parentheses for correct query formation for logical OR condition (backport #43602)
2024-10-15 17:03:22 +05:30
mergify[bot]
492ba539e8 fix: refetch items from BOM if 'Use Multi-Level BOM' has changed usin… (backport #43672) (#43676)
fix: refetch items from BOM if 'Use Multi-Level BOM' has changed usin… (#43672)

fix: refetch items from BOM if 'Use Multi-Level BOM' has changed using api
(cherry picked from commit 05915415de)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-10-15 16:47:40 +05:30
ljain112
21a7dd43a9 fix: added parentheses for correct query formation for logical OR condition
(cherry picked from commit c0da8f11f7)
2024-10-15 11:15:59 +00:00
ruthra kumar
50d1fa4665 Merge pull request #43678 from frappe/mergify/bp/version-15-hotfix/pr-43600
fix: added string for translation in bank reconciliation statement (backport #43600)
2024-10-15 16:41:06 +05:30
ljain112
e10a58074f fix: added string for translation in bank reconciliation statement
(cherry picked from commit c99d9f7037)
2024-10-15 11:02:42 +00:00
mergify[bot]
b2deb89826 fix: conversion factor issue (backport #43645) (#43674)
fix: conversion factor issue (#43645)

(cherry picked from commit a52756f1d4)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-10-15 16:04:48 +05:30
mergify[bot]
c490a66540 fix: incorrect warehouse in the serial no selector for rejection (backport #43671) (#43673)
fix: incorrect warehouse in the serial no selector for rejection (#43671)

(cherry picked from commit 29ff682eca)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-10-15 16:04:36 +05:30
ruthra kumar
afa0c13587 Merge pull request #43670 from frappe/mergify/bp/version-15-hotfix/pr-43557
fix: delete invalid pricing rule on change of applicable_for values (backport #43557)
2024-10-15 15:56:43 +05:30
ljain112
4dbee00b82 test: added test for change in applicable_for_value in promotional scheme
(cherry picked from commit 2613bdd868)
2024-10-15 10:00:47 +00:00
ljain112
5d6fc71556 fix: delete invalid pricing rule on change of applicable_for
(cherry picked from commit 42746fc630)
2024-10-15 10:00:47 +00:00
ruthra kumar
06dd5e0071 Merge pull request #43666 from frappe/mergify/bp/version-15-hotfix/pr-43662
fix: removed unused query (backport #43662)
2024-10-15 13:52:32 +05:30
ruthra kumar
105f9ec2e1 chore: resolve conflict 2024-10-15 13:32:29 +05:30
ruthra kumar
dc6fdbb836 Merge pull request #43660 from frappe/mergify/bp/version-15-hotfix/pr-43642
fix: zero incoming rate for delivery note return (backport #43642)
2024-10-15 13:26:32 +05:30
ljain112
8668ae92d8 fix: removed unused query
(cherry picked from commit 5f590ddfa2)

# Conflicts:
#	erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
2024-10-15 07:53:44 +00:00
ruthra kumar
ec7e5c48de Merge pull request #43658 from frappe/mergify/bp/version-15-hotfix/pr-43570
fix: update item details with actual quantity (backport #43570)
2024-10-15 13:15:39 +05:30
rohitwaghchaure
85088e4aff fix: zero incoming rate for delivery note return (#43642)
(cherry picked from commit 6087a57b0c)
2024-10-15 06:28:59 +00:00
Bhavan23
c2c6d27625 fix: update formatings
(cherry picked from commit 5044297321)
2024-10-15 06:27:52 +00:00
Bhavan23
a70181e025 fix: update formatings
(cherry picked from commit 5f4a523340)
2024-10-15 06:27:52 +00:00
Bhavan23
86017b223a test: Validate the actual quantity when creating a material request from the sales order
(cherry picked from commit 17fdd42645)
2024-10-15 06:27:51 +00:00
Bhavan23
930e79c351 fix: update item details with actual quantity.
(cherry picked from commit 9dbdfec9b7)
2024-10-15 06:27:51 +00:00
ruthra kumar
2c7f5ec324 Merge pull request #43521 from mujeerhashmi/version-15-hotfix
fix: Link opportunity from RFQ to supplier quotation
2024-10-15 10:08:25 +05:30
ruthra kumar
a9f5e86600 Merge pull request #43654 from frappe/mergify/bp/version-15-hotfix/pr-43601
refactor: remove 'format:' based naming (backport #43601)
2024-10-15 07:34:35 +05:30
ruthra kumar
d6decf9172 Merge pull request #43650 from frappe/mergify/bp/version-15-hotfix/pr-43614
fix: ignore free item when qty is zero (backport #43614)
2024-10-15 06:01:30 +05:30
ruthra kumar
1fac17b36f chore: resolve conflict 2024-10-15 06:00:12 +05:30
venkat102
9d05a6ebc0 refactor: remove 'format:' based naming
(cherry picked from commit e8e1ec0e85)

# Conflicts:
#	erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.json
2024-10-14 15:46:06 +00:00
Ninad1306
389ee909a5 test: test case to validate free item is ignored when qty is zero
(cherry picked from commit a2b41a0c16)
2024-10-14 10:50:58 +00:00
Ninad1306
e5aaa5b6e5 fix: ignore free item when qty is zero
(cherry picked from commit 7ae98f77ee)
2024-10-14 10:50:58 +00:00
mergify[bot]
f3ceb4ac7d fix: quotation to so frappe crm (backport #43644) (#43646)
fix: quotation to so frappe crm (#43644)

(cherry picked from commit d57624b182)

Co-authored-by: Nihantra C. Patel <141945075+Nihantra-Patel@users.noreply.github.com>
2024-10-14 15:03:35 +05:30
Smit Vora
050ca4b726 Merge pull request #43641 from frappe/mergify/bp/version-15-hotfix/pr-43638
fix: Use `ref_doc.get()` for `party_account_currency` (backport #43638)
2024-10-14 13:59:13 +05:30
Abdeali Chharchhoda
928b6b1510 fix: Use ref_doc.get() for party_account_currency
(cherry picked from commit b79549422a)
2024-10-14 06:09:09 +00:00
Frappe PR Bot
ef1e121bd4 chore(release): Bumped to Version 15.38.2
## [15.38.2](https://github.com/frappe/erpnext/compare/v15.38.1...v15.38.2) (2024-10-13)

### Bug Fixes

* don't update reference to SI / PI on advances ([8bf8bcf](8bf8bcf739))
2024-10-13 05:23:41 +00:00
ruthra kumar
68f1b41969 Merge pull request #43633 from frappe/mergify/bp/version-15/pr-43627
fix: reconciled advance from reported in reconciliation tool (backport #43627)
2024-10-13 10:52:21 +05:30
ruthra kumar
a329003f7f chore: use correct hook for advance payment doctypes 2024-10-13 10:35:48 +05:30
ruthra kumar
cf1eabe049 chore: resolve conflict 2024-10-13 10:35:14 +05:30
ruthra kumar
4c78a682ad chore: better comments for context
(cherry picked from commit e7505e92c9)
2024-10-13 04:59:58 +00:00
ruthra kumar
4752ed2483 test: reconciled Invoice should not showup in tool
Scenario should be tested on 'Advance in separate party account'

(cherry picked from commit f1ec61c19e)
2024-10-13 04:59:58 +00:00
ruthra kumar
e56dd8268b test: unreconciliation of individual SO from Advance Payment
(cherry picked from commit 8a6978e550)
2024-10-13 04:59:58 +00:00
ruthra kumar
e0477cf59f refactor(test): utility methods for enabling advance in separate acc
(cherry picked from commit a21a406d04)

# Conflicts:
#	erpnext/accounts/test/accounts_mixin.py
2024-10-13 04:59:58 +00:00
ruthra kumar
8c115e146b refactor: use hooks to identify advance doctypes
(cherry picked from commit e7bb960bb5)
2024-10-13 04:59:58 +00:00
ruthra kumar
6267ab994c refactor: reference update logic in advance
(cherry picked from commit a112581acd)
2024-10-13 04:59:58 +00:00
ruthra kumar
8bf8bcf739 fix: don't update reference to SI / PI on advances
(cherry picked from commit b409f74620)
2024-10-13 04:59:57 +00:00
ruthra kumar
eed02d3f44 Merge pull request #43632 from frappe/mergify/bp/version-15-hotfix/pr-43627
fix: reconciled advance from reported in reconciliation tool (backport #43627)
2024-10-13 10:24:41 +05:30
ruthra kumar
6265582e53 refactor: use correct hook for identifying advance doctypes 2024-10-13 09:50:41 +05:30
ruthra kumar
361836e735 chore: resolve conflict 2024-10-13 08:45:15 +05:30
ruthra kumar
ae73d9c621 chore: better comments for context
(cherry picked from commit e7505e92c9)
2024-10-13 03:08:25 +00:00
ruthra kumar
2c2ca22d12 test: reconciled Invoice should not showup in tool
Scenario should be tested on 'Advance in separate party account'

(cherry picked from commit f1ec61c19e)
2024-10-13 03:08:25 +00:00
ruthra kumar
e37a88fdb6 test: unreconciliation of individual SO from Advance Payment
(cherry picked from commit 8a6978e550)
2024-10-13 03:08:25 +00:00
ruthra kumar
9c26093a51 refactor(test): utility methods for enabling advance in separate acc
(cherry picked from commit a21a406d04)

# Conflicts:
#	erpnext/accounts/test/accounts_mixin.py
2024-10-13 03:08:24 +00:00
ruthra kumar
5ce2d73692 refactor: use hooks to identify advance doctypes
(cherry picked from commit e7bb960bb5)
2024-10-13 03:08:24 +00:00
ruthra kumar
ca0a962870 refactor: reference update logic in advance
(cherry picked from commit a112581acd)
2024-10-13 03:08:24 +00:00
ruthra kumar
b72906a7a1 fix: don't update reference to SI / PI on advances
(cherry picked from commit b409f74620)
2024-10-13 03:08:23 +00:00
mergify[bot]
d604b12d51 fix: show incorrect entries filter in Stock Ledger Invariant Check report (backport #43619) (#43622)
fix: show incorrect entries filter in Stock Ledger Invariant Check report (#43619)

fix: show incorrect entry filter in Stock Ledger Invariant Check report
(cherry picked from commit 8beee1982f)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-10-11 16:22:22 +05:30
Frappe PR Bot
d46cf46375 chore(release): Bumped to Version 15.38.1
## [15.38.1](https://github.com/frappe/erpnext/compare/v15.38.0...v15.38.1) (2024-10-09)

### Bug Fixes

* 'NoneType' object has no attribute 'has_serial_no' (backport [#43514](https://github.com/frappe/erpnext/issues/43514)) ([#43574](https://github.com/frappe/erpnext/issues/43574)) ([60508a9](60508a9706))
* [#42014](https://github.com/frappe/erpnext/issues/42014) --resolve conflicts ([85d7405](85d74050e1))
* Accepted and Rejected warehouse cannot be same (backport [#43568](https://github.com/frappe/erpnext/issues/43568)) ([#43573](https://github.com/frappe/erpnext/issues/43573)) ([83ce3dd](83ce3dd915))
* add include closed orders option in so/po trends report v15 ([5660e8b](5660e8b26d))
* add parenttype condition for item table in Purchase Register Report ([8ce81a0](8ce81a058a))
* Add removed test code `b41f10c1b9` ([30fd11f](30fd11f138))
* allow to change the batch in the subcontracting receipt (backport [#43584](https://github.com/frappe/erpnext/issues/43584)) ([#43588](https://github.com/frappe/erpnext/issues/43588)) ([9e109ac](9e109acec7))
* create Account Closing Balance even though there are no transaction in period ([d6f70f5](d6f70f533a))
* creation of contact, customer, opportunity, quotation and prospect from lead ([ef10c4e](ef10c4ea4f))
* creation of contact, customer, opportunity, quotation and prospect from lead --prettier ([5a2a404](5a2a404a50))
* deduct advances adjusted for threshold check for tcs ([6decb7c](6decb7cc34))
* do not include advances for tds vouchers ([ee8485a](ee8485a54a))
* frappe dependency update ([0a70b3f](0a70b3ffcc))
* include parent item group in query ([55464c7](55464c79c4))
* make LCV button not working for PI and PR (backport [#43592](https://github.com/frappe/erpnext/issues/43592)) ([#43593](https://github.com/frappe/erpnext/issues/43593)) ([120b481](120b481c4a))
* multiple issues in Payment Request ([#42427](https://github.com/frappe/erpnext/issues/42427)) ([ea69ba7](ea69ba7cd8))
* production plan bom error (backport [#43591](https://github.com/frappe/erpnext/issues/43591)) ([#43594](https://github.com/frappe/erpnext/issues/43594)) ([029021f](029021f035))
* read only filters in multidialog fields (backport [#43503](https://github.com/frappe/erpnext/issues/43503)) ([#43513](https://github.com/frappe/erpnext/issues/43513)) ([d69a974](d69a974a4d))
* Remove `advance_payment_status` uses ([907e3af](907e3af1b0))
* Remove unreference method ([770bc1c](770bc1c293))
* Remove unused  field ([e785928](e785928c0f))
* Remove unused function `get_paid_amount_against_order` ([7591662](75916629c8))
* Separate `on_submit` and `before_submit` of PR ([dbd7b83](dbd7b83204))
* the purchase receipt trends and delivery note trends report (backport [#43585](https://github.com/frappe/erpnext/issues/43585)) ([#43587](https://github.com/frappe/erpnext/issues/43587)) ([355ba2f](355ba2f632))
* Unknown column 'serial_no' in 'field list' (backport [#43515](https://github.com/frappe/erpnext/issues/43515)) ([#43569](https://github.com/frappe/erpnext/issues/43569)) ([fc9a3c0](fc9a3c0c92))
* Update Values before `after_mapping` hook is called ([#42682](https://github.com/frappe/erpnext/issues/42682)) ([6770610](6770610c6d))
* validation for corrective job card (backport [#43555](https://github.com/frappe/erpnext/issues/43555)) ([#43558](https://github.com/frappe/erpnext/issues/43558)) ([cf0fa0d](cf0fa0db7b))
2024-10-09 12:02:42 +00:00
rohitwaghchaure
c3f6edcd01 Merge pull request #43563 from frappe/version-15-hotfix
chore: release v15
2024-10-09 17:31:15 +05:30
mergify[bot]
120b481c4a fix: make LCV button not working for PI and PR (backport #43592) (#43593)
fix: make LCV button not working for PI and PR (#43592)

(cherry picked from commit 48a12e7213)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-10-09 15:41:38 +05:30
mergify[bot]
029021f035 fix: production plan bom error (backport #43591) (#43594)
fix: production plan bom error (#43591)

(cherry picked from commit ab171326f3)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-10-09 15:41:21 +05:30
mergify[bot]
9e109acec7 fix: allow to change the batch in the subcontracting receipt (backport #43584) (#43588)
fix: allow to change the batch in the subcontracting receipt (#43584)

(cherry picked from commit fc67867a60)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-10-09 13:43:02 +05:30
mergify[bot]
355ba2f632 fix: the purchase receipt trends and delivery note trends report (backport #43585) (#43587)
* fix: fix the purchase receipt trends and delivery note trends report

(cherry picked from commit 2e9dda1588)

* fix: trends date filter issue --formatter

(cherry picked from commit b3e4463a4f)

---------

Co-authored-by: Vishv-silveroak <108357657+Vishv-024@users.noreply.github.com>
Co-authored-by: Nihantra C. Patel <141945075+Nihantra-Patel@users.noreply.github.com>
2024-10-09 13:38:43 +05:30
mergify[bot]
83ce3dd915 fix: Accepted and Rejected warehouse cannot be same (backport #43568) (#43573)
fix: Accepted and Rejected warehouse cannot be same (#43568)

(cherry picked from commit 5130f7d411)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-10-09 09:06:34 +05:30
mergify[bot]
60508a9706 fix: 'NoneType' object has no attribute 'has_serial_no' (backport #43514) (#43574)
fix: 'NoneType' object has no attribute 'has_serial_no' (#43514)

(cherry picked from commit 6ddda6c949)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-10-09 09:06:20 +05:30
Smit Vora
660d20f7fa Merge pull request #43572 from frappe/mergify/bp/version-15-hotfix/pr-43271
fix: deduct advances adjusted for threshold check for tcs (backport #43271)
2024-10-08 22:31:21 +05:30
Nihantra C. Patel
0b2603bbf1 Merge pull request #43577 from Nihantra-Patel/feat_trends_report_v15
fix: add include closed orders option in so/po trends report v15
2024-10-08 19:07:39 +05:30
Nihantra Patel
5660e8b26d fix: add include closed orders option in so/po trends report v15 2024-10-08 18:48:11 +05:30
mergify[bot]
cf0fa0db7b fix: validation for corrective job card (backport #43555) (#43558)
* fix: validation for corrective job card (#43555)

(cherry picked from commit 7a0a893d08)

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

* chore: fix conflicts

* chore: fix linters issue

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-10-08 17:40:52 +05:30
mergify[bot]
fc9a3c0c92 fix: Unknown column 'serial_no' in 'field list' (backport #43515) (#43569)
fix: Unknown column 'serial_no' in 'field list' (#43515)

(cherry picked from commit 69127e8609)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-10-08 17:40:22 +05:30
ljain112
2b4cd0a9bb test: added test cases for the tcs deduction for advances adjusted.
(cherry picked from commit efe238cefd)
2024-10-08 12:05:52 +00:00
ljain112
6decb7cc34 fix: deduct advances adjusted for threshold check for tcs
(cherry picked from commit 767c8f92be)
2024-10-08 12:05:52 +00:00
Smit Vora
9039b86e8a Merge pull request #43553 from frappe/mergify/bp/version-15-hotfix/pr-43397
fix: do not include advances for tds vouchers (backport #43397)
2024-10-08 11:30:02 +05:30
ljain112
ee8485a54a fix: do not include advances for tds vouchers
(cherry picked from commit 7ef918421e)
2024-10-08 05:09:10 +00:00
mergify[bot]
05db28c64f chore: Allow apps to extend voucher subtypes (backport #43528) (backport #43550) (#43551)
chore: Allow apps to extend voucher subtypes (#43528)

* chore: Allow apps to extend voucher subtypes

(cherry picked from commit a1525d9b8e)

* chore: Allow apps to extend voucher subtypes

(cherry picked from commit 8a1e38a43b)

* chore: Allow apps to extend voucher subtypes

(cherry picked from commit ca8820b566)

---------

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
(cherry picked from commit bcd0105915)

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2024-10-08 09:20:34 +05:30
mergify[bot]
bcd0105915 chore: Allow apps to extend voucher subtypes (#43528)
* chore: Allow apps to extend voucher subtypes

(cherry picked from commit a1525d9b8e)

* chore: Allow apps to extend voucher subtypes

(cherry picked from commit 8a1e38a43b)

* chore: Allow apps to extend voucher subtypes

(cherry picked from commit ca8820b566)

---------

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2024-10-08 09:01:25 +05:30
Smit Vora
225adf5cbc Merge pull request #43527 from frappe/mergify/bp/version-15-hotfix/pr-43391
fix: create Account Closing Balance even though there are no transaction in period (backport #43391)
2024-10-07 22:34:46 +05:30
Smit Vora
af5947edd0 Merge pull request #43407 from Abdeali099/pr-backport
fix: multiple issues in Payment Request (#42427)
2024-10-07 21:45:01 +05:30
ljain112
d6f70f533a fix: create Account Closing Balance even though there are no transaction in period
(cherry picked from commit 43deaea96b)
2024-10-07 16:12:28 +00:00
Smit Vora
1dd4168c0e Merge pull request #43525 from frappe/mergify/bp/version-15-hotfix/pr-43384
fix: add parenttype condition for item table in Purchase Register Report (backport #43384)
2024-10-07 21:42:06 +05:30
Smit Vora
db4360d76c Merge pull request #43523 from frappe/mergify/bp/version-15-hotfix/pr-43385
fix: include parent item group in query (backport #43385)
2024-10-07 21:41:05 +05:30
ljain112
8ce81a058a fix: add parenttype condition for item table in Purchase Register Report
(cherry picked from commit 28abf191fc)
2024-10-07 15:55:50 +00:00
ljain112
55464c79c4 fix: include parent item group in query
(cherry picked from commit ad0090068d)
2024-10-07 15:47:46 +00:00
Smit Vora
0c599c2b6d chore: remove unused filed 2024-10-07 20:36:53 +05:30
Syed Mujeer Hashmi
eb1f1255eb fix: Link opportunity from RFQ to supplier quotation 2024-10-07 14:12:43 +00:00
Abdeali Chharchhoda
62cc86114b test: Change Accounts Settings for multi currency (https://github.com/frappe/erpnext/pull/42427#discussion_r1789859737) 2024-10-07 16:00:06 +05:30
Smit Vora
5268da2e55 Merge pull request #43355 from Ninad1306/mapping_docs_fix
fix: update child table from the last source doc (backport #42925)
2024-10-07 15:18:41 +05:30
Smit Vora
d695fea251 Merge pull request #43512 from Ninad1306/sales_purchase_mapping_fix
fix: Update Values before `after_mapping` hook is called (backport #42682)
2024-10-07 15:15:44 +05:30
Nihantra C. Patel
6b2983d8c1 Merge pull request #43296 from frappe/mergify/bp/version-15-hotfix/pr-42014
fix: creation of contact, customer, opportunity, quotation and prospect from lead (backport #42014)
2024-10-07 14:59:57 +05:30
Nihantra C. Patel
85d74050e1 fix: #42014 --resolve conflicts 2024-10-07 14:51:04 +05:30
mergify[bot]
d69a974a4d fix: read only filters in multidialog fields (backport #43503) (#43513)
fix: read only filters in multidialog fields (#43503)

(cherry picked from commit 13eb3c5c14)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-10-07 13:57:56 +05:30
Abdeali Chharchhoda
3d9d56ab50 test: Remove Payment Gateway settings from test 2024-10-07 12:42:59 +05:30
Abdeali Chharchhoda
dbd7b83204 fix: Separate on_submit and before_submit of PR 2024-10-07 12:33:42 +05:30
Ninad Parikh
6770610c6d fix: Update Values before after_mapping hook is called (#42682)
* fix: update values before after_mapping hook is called

* fix: appropriate function name
2024-10-07 12:19:49 +05:30
Abdeali Chharchhoda
75916629c8 fix: Remove unused function get_paid_amount_against_order 2024-10-07 12:10:51 +05:30
Abdeali Chharchhoda
e785928c0f fix: Remove unused field 2024-10-07 12:08:02 +05:30
Frappe PR Bot
edfa6e41e1 chore(release): Bumped to Version 15.38.0
# [15.38.0](https://github.com/frappe/erpnext/compare/v15.37.0...v15.38.0) (2024-10-04)

### Bug Fixes

* 'NoneType' object has no attribute 'has_serial_no' ([21a0157](21a01575b6))
* add company filter in Warehouse wise Item Balance Age and Value ([4fc6d3e](4fc6d3ef64))
* adjustmen entry for stock reco ([c551c27](c551c2714c))
* Cannot read properties of undefined (reading 'price_list_rate') (backport [#43376](https://github.com/frappe/erpnext/issues/43376)) ([#43377](https://github.com/frappe/erpnext/issues/43377)) ([47f06dc](47f06dc180))
* Data missing in table: None, MandatoryError (backport [#43422](https://github.com/frappe/erpnext/issues/43422)) ([#43429](https://github.com/frappe/erpnext/issues/43429)) ([4b3f143](4b3f143f83))
* **Dunning:** logic for fetching text (backport [#43160](https://github.com/frappe/erpnext/issues/43160)) ([#43490](https://github.com/frappe/erpnext/issues/43490)) ([1b28a4e](1b28a4e928))
* Fix API endpoint for Frankfurter ([d96cee8](d96cee8779))
* Ignore transaction deletion check on ledger entry insertion ([1d6f97a](1d6f97ad94))
* **Item:** error message on tax rate (backport [#42955](https://github.com/frappe/erpnext/issues/42955)) ([#42956](https://github.com/frappe/erpnext/issues/42956)) ([5fc5934](5fc5934942))
* last purchase rate for purchase invoice (backport [#43448](https://github.com/frappe/erpnext/issues/43448)) ([#43452](https://github.com/frappe/erpnext/issues/43452)) ([ee2c8c8](ee2c8c869a))
* negative stock error for batch (backport [#43450](https://github.com/frappe/erpnext/issues/43450)) ([#43454](https://github.com/frappe/erpnext/issues/43454)) ([7bf6251](7bf6251c21))
* patch to update Currency Exchange Settings for `frankfurter.app` (backport [#43481](https://github.com/frappe/erpnext/issues/43481)) ([#43483](https://github.com/frappe/erpnext/issues/43483)) ([35a08f8](35a08f8830))
* quality inspection creation (backport [#43416](https://github.com/frappe/erpnext/issues/43416)) ([#43417](https://github.com/frappe/erpnext/issues/43417)) ([a1b6628](a1b6628c41))
* **Quotation:** calculate row values for alternative items (backport [#43054](https://github.com/frappe/erpnext/issues/43054)) ([#43495](https://github.com/frappe/erpnext/issues/43495)) ([4fa5131](4fa5131590))
* removed validation for materials return (backport [#43461](https://github.com/frappe/erpnext/issues/43461)) ([#43463](https://github.com/frappe/erpnext/issues/43463)) ([9c0a17e](9c0a17e4d5))
* serial and batch no selector (backport [#43387](https://github.com/frappe/erpnext/issues/43387)) ([#43390](https://github.com/frappe/erpnext/issues/43390)) ([74c880c](74c880c232))
* set margin fields for purchase documents when updating items ([6516e68](6516e68fa0))
* Stock Ledger Invariant Check report ([2984bad](2984bad2c0))
* Stock UOM not fetched when Stock Entry create from Item Dashboard (backport [#43457](https://github.com/frappe/erpnext/issues/43457)) ([#43465](https://github.com/frappe/erpnext/issues/43465)) ([f2a72e5](f2a72e5f82))
* tests for work order consumption (backport [#41814](https://github.com/frappe/erpnext/issues/41814)) ([#43430](https://github.com/frappe/erpnext/issues/43430)) ([86b10ce](86b10ce9bb))
* use serial and batch fields (backport [#43421](https://github.com/frappe/erpnext/issues/43421)) ([#43423](https://github.com/frappe/erpnext/issues/43423)) ([d495d93](d495d93840))

### Features

* added 'cost of new capitalized asset' column ([27cd51e](27cd51e267))
* provide hook point for bulk transaction tasks ([50e47e7](50e47e796d))
2024-10-04 03:07:05 +00:00
ruthra kumar
5a9522e70f Merge pull request #43467 from frappe/version-15-hotfix
chore: release v15
2024-10-04 08:35:53 +05:30
mergify[bot]
4fa5131590 fix(Quotation): calculate row values for alternative items (backport #43054) (#43495)
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
fix(Quotation): calculate row values for alternative items (#43054)
2024-10-04 01:23:59 +01:00
mergify[bot]
1b28a4e928 fix(Dunning): logic for fetching text (backport #43160) (#43490)
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
fix(Dunning): logic for fetching text (#43160)
2024-10-03 20:28:53 +01:00
mergify[bot]
f2a72e5f82 fix: Stock UOM not fetched when Stock Entry create from Item Dashboard (backport #43457) (#43465)
fix: Stock UOM not fetched when Stock Entry create from Item Dashboard (#43457)

(cherry picked from commit 895b072bad)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-10-02 18:46:01 +05:30
Sagar Vora
661eb058b9 Merge pull request #43485 from frappe/mergify/bp/version-15-hotfix/pr-43475
fix: set margin fields for purchase documents when updating items (backport #43475)
2024-10-02 15:24:08 +05:30
Sagar Vora
6516e68fa0 fix: set margin fields for purchase documents when updating items
(cherry picked from commit 7be4d56be2)
2024-10-02 09:52:29 +00:00
mergify[bot]
35a08f8830 fix: patch to update Currency Exchange Settings for frankfurter.app (backport #43481) (#43483)
Co-authored-by: Sagar Vora <sagar@resilient.tech>
2024-10-02 15:15:48 +05:30
Sagar Vora
562327f041 Merge pull request #43478 from frappe/mergify/bp/version-15-hotfix/pr-43476
fix: Fix API endpoint for Frankfurter (backport #43476)
2024-10-02 14:43:14 +05:30
Sagar Vora
8e7d893669 test: update test for API change
(cherry picked from commit c444de017a)
2024-10-02 09:11:47 +00:00
Corentin Forler
d96cee8779 fix: Fix API endpoint for Frankfurter
(cherry picked from commit 33e72111c7)
2024-10-02 09:11:47 +00:00
mergify[bot]
96c4d1af63 Serial no report (backport #43444) (#43464)
Serial no report (#43444)

* chore: remove the field that which is not exiting in serial no

* chore: remove the field that which is not exiting in serial no

* chore: remove the field that which is not exiting in serial no

(cherry picked from commit 661efadf41)

Co-authored-by: Vishv-silveroak <108357657+Vishv-024@users.noreply.github.com>
2024-10-01 14:27:27 +05:30
mergify[bot]
9c0a17e4d5 fix: removed validation for materials return (backport #43461) (#43463)
fix: removed validation for materials return (#43461)

(cherry picked from commit 1c7154c7ca)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-10-01 14:26:38 +05:30
mergify[bot]
ee2c8c869a fix: last purchase rate for purchase invoice (backport #43448) (#43452)
* fix: last purchase rate for purchase invoice

(cherry picked from commit fb9d106633)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py

* chore: fix conflicts

---------

Co-authored-by: Rohit Waghchaure <rohitw1991@gmail.com>
2024-10-01 12:54:26 +05:30
Nihantra C. Patel
15b34a607f Merge pull request #43459 from frappe/mergify/bp/version-15-hotfix/pr-43455
fix: add company filter in Warehouse wise Item Balance Age and Value (backport #43455)
2024-10-01 11:46:46 +05:30
Nihantra Patel
4fc6d3ef64 fix: add company filter in Warehouse wise Item Balance Age and Value
(cherry picked from commit 75950f86cf)
2024-10-01 05:47:36 +00:00
mergify[bot]
7bf6251c21 fix: negative stock error for batch (backport #43450) (#43454)
fix: negative stock error for batch (#43450)

(cherry picked from commit 912ba7789c)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-10-01 10:34:48 +05:30
mergify[bot]
5fc5934942 fix(Item): error message on tax rate (backport #42955) (#42956)
Co-authored-by: barredterra <14891507+barredterra@users.noreply.github.com>
2024-09-30 22:18:49 +02:00
Frappe PR Bot
01f9139ebd chore(release): Bumped to Version 15.37.0
# [15.37.0](https://github.com/frappe/erpnext/compare/v15.36.4...v15.37.0) (2024-09-30)

### Features

* added 'cost of new capitalized asset' column ([96d8b52](96d8b5242d))
2024-09-30 17:21:09 +00:00
rohitwaghchaure
4be557bdce Merge pull request #43453 from frappe/mergify/bp/version-15/pr-43412
feat: added 'cost of new capitalized asset' column (backport #43399) (backport #43412)
2024-09-30 22:49:53 +05:30
Khushi Rawat
96d8b5242d feat: added 'cost of new capitalized asset' column
(cherry picked from commit 1eb9cc33fc)
(cherry picked from commit 27cd51e267)
2024-09-30 15:45:59 +00:00
Abdeali Chharchhoda
67bd540135 test: Removed initial PR status assertion 2024-09-30 18:21:59 +05:30
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
Abdeali Chharchhoda
30fd11f138 fix: Add removed test code b41f10c1b9 2024-09-30 17:20:42 +05:30
rohitwaghchaure
0986d3ebe4 Merge pull request #43440 from frappe/mergify/bp/version-15-hotfix/pr-43437
fix: adjustment entry for stock reco (backport #43437)
2024-09-30 15:38:00 +05:30
Rohit Waghchaure
c551c2714c fix: adjustmen entry for stock reco
(cherry picked from commit 4e463b7d6d)
2024-09-30 08:57:54 +00:00
rohitwaghchaure
efc97cc59f Merge pull request #43438 from frappe/mergify/bp/version-15-hotfix/pr-43436
fix: 'NoneType' object has no attribute 'has_serial_no' (backport #43436)
2024-09-30 14:26:17 +05:30
Rohit Waghchaure
21a01575b6 fix: 'NoneType' object has no attribute 'has_serial_no'
(cherry picked from commit 28f9fd2507)
2024-09-30 08:11:20 +00:00
ruthra kumar
6f3b5604b9 Merge pull request #43434 from frappe/mergify/bp/version-15-hotfix/pr-43058
refactor: use hooks to extend bulk_transaction (backport #43058)
2024-09-30 09:58:44 +05:30
Kitti U
50e47e796d feat: provide hook point for bulk transaction tasks
(cherry picked from commit d4dd01d8d1)
2024-09-30 04:07:53 +00:00
Khushi Rawat
4d3e43bdbe Merge pull request #43412 from frappe/mergify/bp/version-15-hotfix/pr-43399
feat: added 'cost of new capitalized asset' column (backport #43399)
2024-09-29 23:28:43 +05:30
rohitwaghchaure
928c887de5 Merge pull request #43433 from frappe/mergify/bp/version-15-hotfix/pr-43420
fix: Stock Ledger Invariant Check report (backport #43420)
2024-09-29 22:55:58 +05:30
Rohit Waghchaure
2984bad2c0 fix: Stock Ledger Invariant Check report
(cherry picked from commit d7daedc5b2)
2024-09-29 17:07:33 +00:00
Frappe PR Bot
2d09ef2509 chore(release): Bumped to Version 15.36.4
## [15.36.4](https://github.com/frappe/erpnext/compare/v15.36.3...v15.36.4) (2024-09-29)

### Bug Fixes

* Data missing in table: None, MandatoryError (backport [#43422](https://github.com/frappe/erpnext/issues/43422)) ([#43429](https://github.com/frappe/erpnext/issues/43429)) ([2c4610c](2c4610c021))
2024-09-29 16:38:13 +00:00
rohitwaghchaure
8b5997e38f Merge pull request #43431 from frappe/mergify/bp/version-15/pr-43429
fix: Data missing in table: None, MandatoryError (backport #43422) (backport #43429)
2024-09-29 22:07:02 +05:30
mergify[bot]
86b10ce9bb fix: tests for work order consumption (backport #41814) (#43430)
fix: tests for work order consumption (#41814)

* fix: tests for work order automatic SABB creation

* fix: qty

* chore: show created sabb

* chore: fix syntax

* fix: check SABB qty

* fix: add batched consumable to manufacture

* fix: missing fg qty field

* fix: improve test debug

* chore: linting

* chore: removed extra hash icons

---------

Co-authored-by: Rohit Waghchaure <rohitw1991@gmail.com>
(cherry picked from commit ca3c680909)

Co-authored-by: Richard Case <110036763+casesolved-co-uk@users.noreply.github.com>
2024-09-29 22:06:50 +05:30
mergify[bot]
2c4610c021 fix: Data missing in table: None, MandatoryError (backport #43422) (#43429)
fix: Data missing in table: None, MandatoryError (#43422)

(cherry picked from commit 8e33e0e1d2)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
(cherry picked from commit 4b3f143f83)
2024-09-29 15:57:24 +00:00
mergify[bot]
4b3f143f83 fix: Data missing in table: None, MandatoryError (backport #43422) (#43429)
fix: Data missing in table: None, MandatoryError (#43422)

(cherry picked from commit 8e33e0e1d2)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-09-29 21:18:25 +05:30
mergify[bot]
d495d93840 fix: use serial and batch fields (backport #43421) (#43423)
fix: use serial and batch fields (#43421)

(cherry picked from commit ca16089d9d)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-09-29 11:09:32 +05:30
mergify[bot]
a1b6628c41 fix: quality inspection creation (backport #43416) (#43417)
fix: quality inspection creation (#43416)

(cherry picked from commit a594c05296)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-09-29 09:46:14 +05:30
Abdeali Chharchhoda
770bc1c293 fix: Remove unreference method 2024-09-28 12:29:34 +05:30
Abdeali Chharchhoda
907e3af1b0 fix: Remove advance_payment_status uses 2024-09-28 12:09:33 +05:30
Frappe PR Bot
e706aa692a chore(release): Bumped to Version 15.36.3
## [15.36.3](https://github.com/frappe/erpnext/compare/v15.36.2...v15.36.3) (2024-09-27)

### Bug Fixes

* Ignore transaction deletion check on ledger entry insertion ([c1f14f2](c1f14f2991))
2024-09-27 18:30:37 +00:00
Deepesh Garg
a5fa287dad Merge pull request #43413 from frappe/mergify/bp/version-15/pr-43411
fix: Ignore transaction deletion check on ledger entry insertion (#43410)
2024-09-27 23:58:16 +05:30
Deepesh Garg
c1f14f2991 fix: Ignore transaction deletion check on ledger entry insertion
(cherry picked from commit 998f6a92a4)
(cherry picked from commit 1d6f97ad94)
2024-09-27 18:25:49 +00:00
Deepesh Garg
6d66002374 Merge pull request #43411 from frappe/mergify/bp/version-15-hotfix/pr-43410
fix: Ignore transaction deletion check on ledger entry insertion (#43410)
2024-09-27 23:54:54 +05:30
Khushi Rawat
27cd51e267 feat: added 'cost of new capitalized asset' column
(cherry picked from commit 1eb9cc33fc)
2024-09-27 18:23:34 +00:00
Deepesh Garg
1d6f97ad94 fix: Ignore transaction deletion check on ledger entry insertion
(cherry picked from commit 998f6a92a4)
2024-09-27 18:22:41 +00:00
Abdeali Chharchhodawala
ea69ba7cd8 fix: multiple issues in Payment Request (#42427)
* fix: multiple issues in Payment Request

* chore: minor changes

* fix: remove  bug

* fix: replace `round` with `flt`

* fix: update `set_advance_payment_status()` logic

* fix: removed bug of `set_advance_payment_status`

* fix: changes as per review

* refactor: replace sql query of `matched_payment_requests` to query builder

* fix: replace `locals` with `get_doc` in set_query

* fix: changes during review

* fix: minor review changes

* fix: remove unnecessary code for setting payment entry received amount

* fix: logic for ser payment_request if PE made from transaction

* fix: Use rounded total to make Payment Request from `Sales Invoice` or `Purchase Invoice`

* refactor: enhance logic of `set_open_payment_requests_to_references`

* fix: added one optional arg `created_from_payment_request`

* fix: handle multiple allocation of PR at PE's reference

* fix: logic for PR if outstanding docs fetch

* fix: formatted Link field for `Payment Request` for PE's references

* fix: replace `get_all()` with `get_list()` for getting Payment Request for Link field

* fix: replace `get_all()` with `get_list()` for getting Payment Request for Link field

* chore: format `payment_entry.js` file

* style: Show preview popup of `Payment Request`

* fix: remove minor bug

* fix: add virtual field for Payment Term and Request `outstanding_amount` in PE's reference

* fix: get outstanding amount in PE's reference on realtime

* fix: move allocation of allocated_amount to server side (no change)

* fix: some minor changes to allocation

* fix: Split `Payment Request` if PE is created from PR and there are `Payment Terms`

* fix: minor logic changes

* fix: Allocation of allocated_amount if `paid_amount` is changes

* fix: improve logic of allocation

* fix: set matched payment request if unset

* fix: minor changes

* fix: Allocate single Payment Request if PE created from PR

* fix: improve code logic

* fix: Removed duplication code

* fix: proper message title

* refactor: Rename method of Allocation Amount to References

* refactor: Changing `grand_total` description based on `party_type`

* refactor: update Payment Request

* fix: Remove virtual property of payment_term_oustanding from references

* fix: fetch party account currency for creating payment request

* fix: use transaction currency as base in payment request

* fix: party amount for creating payment entry

* fix: allow for proportional amount paid by bank

* fix: Changed field order in Payment Request

* fix: Minor refactor in Payment Entry Reference table data

* test: Added test cases for allow Payment at `Partially Paid` status for PR

* test: Update partial paid status test case

* test: Update test case for same currency PR

* refactor: Wider the `msgprint` dialog for after save PE

* test: Update PR test cases

* chore: Remove dirty lines

* test: Checking `Advance Payment Status`

* fix: formatting update

* fix: Use `flt` where doing subtraction

* test: PR test case with Payment Term for same currency

* fix: remove redundant `flt`

* test: Add test cases for PR

---------

Co-authored-by: Sagar Vora <sagar@resilient.tech>
2024-09-27 20:29:22 +05:30
mergify[bot]
74c880c232 fix: serial and batch no selector (backport #43387) (#43390)
fix: serial and batch no selector (#43387)

(cherry picked from commit e4e96d2a44)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-09-26 12:06:27 +05:30
Frappe PR Bot
8d188dccd7 chore(release): Bumped to Version 15.36.2
## [15.36.2](https://github.com/frappe/erpnext/compare/v15.36.1...v15.36.2) (2024-09-25)

### Bug Fixes

* add currency in financial statement ([927f800](927f80035d))
* added date condition ([0e18845](0e1884539e))
* AR / AP report to ignore 0.0 outstanding ([979d801](979d801de5))
* **Bank Account:** dashboard connections (backport [#43365](https://github.com/frappe/erpnext/issues/43365)) ([#43367](https://github.com/frappe/erpnext/issues/43367)) ([cfea2de](cfea2de131))
* change dynamic link doctype fieldtype to data ([05c92cc](05c92cce71))
* closing amount reset to expected amount on save (backport [#43358](https://github.com/frappe/erpnext/issues/43358)) ([#43368](https://github.com/frappe/erpnext/issues/43368)) ([0722aa5](0722aa5a3f))
* create_address is failing ([557ef5d](557ef5d214))
* handle missing liability account scenario in `set_liability_account` ([4045928](40459288f6))
* incorrect outstanding on non-pos invoice with write_off_account ([f89a3db](f89a3dbb65))
* incorrect stock balance for inventory dimension (backport [#43284](https://github.com/frappe/erpnext/issues/43284)) ([#43290](https://github.com/frappe/erpnext/issues/43290)) ([f6725e2](f6725e2eed))
* item_query in pos_invoice ([99e004b](99e004b619))
* make to tax category on tax rule to filter with percent ([63d4fdd](63d4fddb49))
* **minor:** include condition to check docstatus ([1f42302](1f42302997))
* not able to cancel Quality Inspection (backport [#43374](https://github.com/frappe/erpnext/issues/43374)) ([#43375](https://github.com/frappe/erpnext/issues/43375)) ([40fbb1d](40fbb1d6ff))
* partial return on POS invoice ([998fef7](998fef779b))
* partial return on POS invoice ([b99ca7d](b99ca7d9e9))
* Payment Ledger Report currency fieldtype fix ([ad2d6a1](ad2d6a1625))
* **Payment Reconciliation:** German translations ([e06a01f](e06a01fae5))
* set group_by condition if empty and voucher_no is set ([ec27077](ec27077d9c))
* shipping rule must match the company ([085a4c6](085a4c61ac))
* show chart tool tip in report currency ([e5ae828](e5ae828580))
* stock dashboard (backport [#43347](https://github.com/frappe/erpnext/issues/43347)) ([#43349](https://github.com/frappe/erpnext/issues/43349)) ([176feb2](176feb20ad))
* transaction exchange rate on GL's for Multi currency Journals ([a7ccc94](a7ccc9420b))
* translate in js ([84e26e2](84e26e21ab))
* Translation for button SO to PO ([73d98ad](73d98addbc))
* ui clean-up (backport [#43305](https://github.com/frappe/erpnext/issues/43305)) ([#43312](https://github.com/frappe/erpnext/issues/43312)) ([7e6d6f0](7e6d6f08a2))
* update clearance date in invoice payment table ([10ecdb9](10ecdb99fe))
2024-09-25 04:40:17 +00:00
mergify[bot]
47f06dc180 fix: Cannot read properties of undefined (reading 'price_list_rate') (backport #43376) (#43377)
fix: Cannot read properties of undefined (reading 'price_list_rate') (#43376)

(cherry picked from commit a63dca0984)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-09-25 10:09:17 +05:30
ruthra kumar
9de0d4329c Merge pull request #43361 from frappe/version-15-hotfix
chore: release v15
2024-09-25 10:09:04 +05:30
mergify[bot]
40fbb1d6ff fix: not able to cancel Quality Inspection (backport #43374) (#43375)
fix: not able to cancel Quality Inspection (#43374)

(cherry picked from commit 8c32ebee68)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-09-25 00:02:24 +05:30
mergify[bot]
0722aa5a3f fix: closing amount reset to expected amount on save (backport #43358) (#43368)
fix: closing amount reset to expected amount on save (#43358)

(cherry picked from commit 9974b7c4ae)

Co-authored-by: jabir-elat <44110258+jabir-elat@users.noreply.github.com>
2024-09-24 20:49:42 +05:30
mergify[bot]
cfea2de131 fix(Bank Account): dashboard connections (backport #43365) (#43367)
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
fix(Bank Account): dashboard connections (#43365)
2024-09-24 16:20:50 +02:00
Khushi Rawat
fe206b0d77 Merge pull request #43370 from khushi8112/manual-backport-for-asset-depreciation-and-balances-report-fix
fix: Manual backport for asset depreciation and balances report fix
2024-09-24 18:49:01 +05:30
Khushi Rawat
4e621b09ba style: added comment 2024-09-24 18:18:04 +05:30
Khushi Rawat
1f42302997 fix(minor): include condition to check docstatus 2024-09-24 18:17:25 +05:30
Khushi Rawat
0e1884539e fix: added date condition 2024-09-24 18:16:37 +05:30
ruthra kumar
b17a811abf Merge pull request #43364 from frappe/mergify/bp/version-15-hotfix/pr-43356
fix: AR / AP report to ignore 0.0 outstanding (backport #43356)
2024-09-24 16:58:35 +05:30
ruthra kumar
979d801de5 fix: AR / AP report to ignore 0.0 outstanding
(cherry picked from commit 6e2cf79e2c)
2024-09-24 10:14:53 +00:00
ruthra kumar
49d5b7c4d3 Merge pull request #43360 from frappe/mergify/bp/version-15-hotfix/pr-43235
fix: set group_by condition to "Group by Voucher (Consolidated)" if `None` and voucher_no is set (backport #43235)
2024-09-24 15:41:15 +05:30
mergify[bot]
176feb20ad fix: stock dashboard (backport #43347) (#43349)
fix: stock dashboard (#43347)

(cherry picked from commit 9e8be8db51)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-09-24 15:25:42 +05:30
Vishakh Desai
ec27077d9c fix: set group_by condition if empty and voucher_no is set
(cherry picked from commit a6b0cb6cac)
2024-09-24 09:24:28 +00:00
Ninad1306
0a70b3ffcc fix: frappe dependency update 2024-09-24 11:24:31 +05:30
ruthra kumar
7db135dab5 Merge pull request #43353 from frappe/mergify/bp/version-15-hotfix/pr-43310
fix: update clearance date in invoice payment table (backport #43310)
2024-09-24 11:20:43 +05:30
ruthra kumar
4278b08147 Merge pull request #43065 from Ninad1306/merge_taxes_fix
fix: Reset Value Conditionally Based on Merge Taxes
2024-09-24 11:10:09 +05:30
Smit Vora
f36a68b42b Merge pull request #42925 from Ninad1306/mapping_docs_fix
fix: Replace `add_if_empty` with `reset_value` flag
2024-09-24 11:10:09 +05:30
Kavin
8bc76bae9c refactor: update clearance date in payment entry
(cherry picked from commit c218f7527f)
2024-09-24 05:29:49 +00:00
Kavin
9d2cbccff2 test: add test case for updating clearance date on pos invoice
(cherry picked from commit ce8600520f)
2024-09-24 05:29:49 +00:00
Kavin
10ecdb99fe fix: update clearance date in invoice payment table
(cherry picked from commit 487c2a29a6)
2024-09-24 05:29:49 +00:00
ruthra kumar
2aa1380c81 Merge pull request #43351 from frappe/mergify/bp/version-15-hotfix/pr-43257
fix: item_query in pos_invoice (backport #43257)
2024-09-24 10:27:33 +05:30
ljain112
99e004b619 fix: item_query in pos_invoice
(cherry picked from commit 7f82a06e65)
2024-09-24 02:56:24 +00:00
ruthra kumar
b415e858e7 Merge pull request #43346 from frappe/mergify/bp/version-15-hotfix/pr-43283
fix: shipping rule must match the company (backport #43283)
2024-09-24 08:25:18 +05:30
Nihantra C. Patel
4cec68c7ad Merge pull request #43343 from frappe/mergify/bp/version-15-hotfix/pr-43253
fix: partial return on POS invoice (backport #43253)
2024-09-23 23:36:04 +05:30
barredterra
085a4c61ac fix: shipping rule must match the company
(cherry picked from commit df8f4086f6)
2024-09-23 14:41:18 +00:00
ruthra kumar
5f08ef5cd1 Merge pull request #43344 from frappe/mergify/bp/version-15-hotfix/pr-43299
fix(Payment Reconciliation): German translations (backport #43299)
2024-09-23 20:07:53 +05:30
barredterra
e06a01fae5 fix(Payment Reconciliation): German translations
(cherry picked from commit 32d4f96e02)
2024-09-23 14:35:49 +00:00
Nihantra C. Patel
998fef779b fix: partial return on POS invoice
(cherry picked from commit 18bdd06652)
2024-09-23 14:26:21 +00:00
Nihantra C. Patel
b99ca7d9e9 fix: partial return on POS invoice
(cherry picked from commit 76289fa8dc)
2024-09-23 14:26:21 +00:00
ruthra kumar
0fd2964032 Merge pull request #43336 from frappe/mergify/bp/version-15-hotfix/pr-43316
fix: incorrect outstanding on non-pos invoice with write_off_account (backport #43316)
2024-09-23 18:22:30 +05:30
ruthra kumar
15baa3f305 Merge pull request #43338 from frappe/mergify/bp/version-15-hotfix/pr-43308
fix: show chart tool tip in report currency (backport #43308)
2024-09-23 18:22:06 +05:30
ruthra kumar
5920525369 Merge pull request #43340 from frappe/mergify/bp/version-15-hotfix/pr-43307
fix: change dynamic link doctype fieldtype to data (backport #43307)
2024-09-23 18:21:48 +05:30
venkat102
05c92cce71 fix: change dynamic link doctype fieldtype to data
(cherry picked from commit 1e46f7344a)
2024-09-23 12:23:52 +00:00
ruthra kumar
a0f01dac1a Merge pull request #43334 from frappe/mergify/bp/version-15-hotfix/pr-43331
fix: transaction exchange rate on GL's for Multi currency Journals (backport #43331)
2024-09-23 17:52:51 +05:30
ruthra kumar
e8c174c12b Merge pull request #43333 from frappe/mergify/bp/version-15-hotfix/pr-43321
fix: handle missing liability account scenario in `set_liability_account` (backport #43321)
2024-09-23 17:50:06 +05:30
venkat102
927f80035d fix: add currency in financial statement
(cherry picked from commit 91a27bda84)
2024-09-23 12:11:54 +00:00
venkat102
e5ae828580 fix: show chart tool tip in report currency
(cherry picked from commit 827b3f4542)
2024-09-23 12:11:53 +00:00
ruthra kumar
f89a3dbb65 fix: incorrect outstanding on non-pos invoice with write_off_account
(cherry picked from commit d5e2906e59)
2024-09-23 12:09:50 +00:00
ruthra kumar
2d9142832d test: transaction exchange rate on multi-currency journals
(cherry picked from commit c524825d2d)
2024-09-23 12:03:59 +00:00
ruthra kumar
a7ccc9420b fix: transaction exchange rate on GL's for Multi currency Journals
(cherry picked from commit 8cd9ad5361)
2024-09-23 12:03:59 +00:00
ljain112
40459288f6 fix: handle missing liability account scenario in set_liability_account
(cherry picked from commit ee7ab4b065)
2024-09-23 12:00:39 +00:00
ruthra kumar
250a1c9341 Merge pull request #43332 from frappe/mergify/bp/version-15-hotfix/pr-43328
fix: allow tax rule filter on tax category name with % (backport #43328)
2024-09-23 17:26:06 +05:30
venkat102
63d4fddb49 fix: make to tax category on tax rule to filter with percent
(cherry picked from commit 3aaa13cb29)
2024-09-23 11:47:22 +00:00
mergify[bot]
7e6d6f08a2 fix: ui clean-up (backport #43305) (#43312)
* fix: ui clean-up (#43305)

fix: ui cleanup
(cherry picked from commit b127a0c8b7)

# Conflicts:
#	erpnext/manufacturing/doctype/bom_creator/bom_creator.json
#	erpnext/manufacturing/doctype/plant_floor/plant_floor.json
#	erpnext/public/js/templates/visual_plant_floor_template.html

* chore: fix conflicts

* chore: fix conflicts

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-09-22 11:18:42 +05:30
Smit Vora
d1c72dc27b Merge pull request #43314 from frappe/mergify/bp/version-15-hotfix/pr-42842
refactor: use common functionality to validate account number (backport #42842)
2024-09-21 07:06:49 +05:30
HENRY Florian
86ae644574 refactor: use common functionality to validate account number (#42842)
feat: Allow unique Account number by root type (not unique for accros all Accounts)
(cherry picked from commit 40d97f4fe9)
2024-09-21 01:20:03 +00:00
Nihantra Patel
5a2a404a50 fix: creation of contact, customer, opportunity, quotation and prospect from lead --prettier
(cherry picked from commit 5844897c34)

# Conflicts:
#	erpnext/crm/doctype/lead/lead.js
2024-09-19 05:58:01 +00:00
Nihantra Patel
ef10c4ea4f fix: creation of contact, customer, opportunity, quotation and prospect from lead
(cherry picked from commit 8304d19e8b)

# Conflicts:
#	erpnext/crm/doctype/lead/lead.js
2024-09-19 05:58:01 +00:00
mergify[bot]
f6725e2eed fix: incorrect stock balance for inventory dimension (backport #43284) (#43290)
fix: incorrect stock balance for inventory dimension (#43284)

(cherry picked from commit 3e7a7a54bf)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-09-19 09:35:54 +05:30
ruthra kumar
e90532e406 Merge pull request #43293 from frappe/mergify/bp/version-15-hotfix/pr-43274
fix: translate in js (backport #43274)
2024-09-19 09:17:39 +05:30
Doğancan
84e26e21ab fix: translate in js
(cherry picked from commit 25faec5662)
2024-09-19 03:27:13 +00:00
Frappe PR Bot
34ca0c3bb6 chore(release): Bumped to Version 15.36.1
## [15.36.1](https://github.com/frappe/erpnext/compare/v15.36.0...v15.36.1) (2024-09-19)

### Bug Fixes

* create_address is failing ([17ad402](17ad402695))
2024-09-19 01:43:11 +00:00
ruthra kumar
42e4b8a68c Merge pull request #43287 from frappe/mergify/bp/version-15/pr-43279
fix: create_address is failing while creating customer (backport #43279)
2024-09-19 07:11:56 +05:30
ruthra kumar
34d159b3a2 Merge pull request #43213 from sameer55chauhan/patch-4
fix: Payment Ledger Report currency fieldtype fix
2024-09-19 07:05:43 +05:30
Shariq Ansari
17ad402695 fix: create_address is failing
(cherry picked from commit acc1d52ac8)
2024-09-19 01:21:13 +00:00
Shariq Ansari
953b5790ed Merge pull request #43281 from frappe/mergify/bp/version-15-hotfix/pr-43279
fix: create_address is failing while creating customer (backport #43279)
2024-09-18 22:47:56 +05:30
Shariq Ansari
557ef5d214 fix: create_address is failing
(cherry picked from commit acc1d52ac8)
2024-09-18 17:01:49 +00:00
Nihantra C. Patel
680354ac0d Merge pull request #43278 from frappe/mergify/bp/version-15-hotfix/pr-43276
fix: Translation for button SO to PO (backport #43276)
2024-09-18 21:51:03 +05:30
Nihantra C. Patel
73d98addbc fix: Translation for button SO to PO
(cherry picked from commit a5275e9f28)
2024-09-18 16:14:54 +00:00
Frappe PR Bot
479e8573c2 chore(release): Bumped to Version 15.36.0
# [15.36.0](https://github.com/frappe/erpnext/compare/v15.35.2...v15.36.0) (2024-09-18)

### Bug Fixes

* A project without tasks should be able to complete ([dea735d](dea735de4d))
* add currency in options for rate field in pricing rule ([782c9dd](782c9dda1a))
* batch based item price not working (backport [#43172](https://github.com/frappe/erpnext/issues/43172)) ([#43206](https://github.com/frappe/erpnext/issues/43206)) ([61a42ea](61a42ea5d7))
* cancel cost center allocation and journal entry after test ([3d29007](3d29007aeb))
* consistent behaviour on refresh ([01f3068](01f30682ee))
* create and link address while creating prospect & customer ([d6a3d0d](d6a3d0d468))
* create fiscal year without overlapping existing Fiscal Years ([78768f8](78768f883c))
* currency changing while making PO from Supplier Quotation (backport [#43187](https://github.com/frappe/erpnext/issues/43187)) ([#43205](https://github.com/frappe/erpnext/issues/43205)) ([ef6b172](ef6b172616))
* delete exchange gain loss journal entry while deleting payment entry ([5789de2](5789de25b9))
* do not auto apply tds in purchase order ([741c18b](741c18b144))
* do not check appy_tds in Purchase Order Automatically ([5edebb2](5edebb28a5))
* do not validate purchase document for composite asset ([c505156](c5051561e4))
* fetch cost center allocation percentage only from the applicable allocation ([0fe901a](0fe901a137))
* hide and reset discount control on new POS order ([42494db](42494db3c7))
* **holiday-list:** use same date format for same holiday error message (backport [#42606](https://github.com/frappe/erpnext/issues/42606)) ([#43222](https://github.com/frappe/erpnext/issues/43222)) ([f101a1c](f101a1ce3b))
* ignore repost logic on Payment Reconciliation ([d91013a](d91013a467))
* invalid gp calculation ([291f0a5](291f0a580b))
* item list view in website (backport [#43165](https://github.com/frappe/erpnext/issues/43165)) ([#43207](https://github.com/frappe/erpnext/issues/43207)) ([c1a6c56](c1a6c56217))
* map rows on journal entry by validating account, party, debit and credit value ([86e1818](86e1818420))
* prevent KeyError by checking `report_filter` existence ([984acb6](984acb661d))
* revert 091c5496b2 ([2ad6d63](2ad6d637ee))
* set party_type null when payment_type is changed to Internal Transfer ([45ff8fa](45ff8fa296))
* set tax_withholding_category from Purchase Order while creating pi form po ([7027be8](7027be8fbc))
* tds workflow in purchase order ([11359bd](11359bd235))
* typo with po_date when creating remarks ([1657a83](1657a83151))
* updated filtering in depreciation and balances report ([78c6839](78c68397d9))
* **ux:** set amount based on account currency while adding new row ([f7cedac](f7cedac526))
* **ux:** set amount on foreign currency when foreign currency account is selected on last row of journal ([d8d4cd2](d8d4cd23a5))

### Features

* API for crm integration ([f060534](f060534625))
2024-09-18 07:32:30 +00:00
ruthra kumar
6a0b15211a Merge pull request #43254 from frappe/version-15-hotfix
chore: release v15
2024-09-18 13:01:08 +05:30
ruthra kumar
7aeadcbf98 Merge pull request #43268 from frappe/mergify/bp/version-15-hotfix/pr-43239
fix: add currency in options for rate field in pricing rule (backport #43239)
2024-09-18 12:28:59 +05:30
ruthra kumar
82982e25c6 chore: resolve conflict 2024-09-18 12:10:42 +05:30
krishna
782c9dda1a fix: add currency in options for rate field in pricing rule
(cherry picked from commit 636c0131fa)

# Conflicts:
#	erpnext/accounts/doctype/pricing_rule/pricing_rule.json
2024-09-18 06:29:42 +00:00
ruthra kumar
dfe47261ae Merge pull request #43266 from frappe/mergify/bp/version-15-hotfix/pr-43216
fix: get cost center allocation percentage only from the applicable allocation (backport #43216)
2024-09-18 11:40:02 +05:30
venkat102
3d29007aeb fix: cancel cost center allocation and journal entry after test
(cherry picked from commit 3c65b98b49)
2024-09-18 05:49:34 +00:00
venkat102
52a161f076 test: add unit test for validating multiple cost center allocation with different child cost center
(cherry picked from commit 4d5d6150e1)
2024-09-18 05:49:34 +00:00
venkat102
0fe901a137 fix: fetch cost center allocation percentage only from the applicable allocation
(cherry picked from commit 36e5945c66)
2024-09-18 05:49:34 +00:00
Khushi Rawat
8c28cb6b25 Merge pull request #43264 from frappe/mergify/bp/version-15-hotfix/pr-43210
fix: updated filtering in depreciation and balances report (backport #43210)
2024-09-18 02:13:54 +05:30
Khushi Rawat
4ba37e49d8 chore: resolved failing check
(cherry picked from commit af52f0e71f)
2024-09-17 20:27:32 +00:00
Khushi Rawat
1e89c007ed chore: resolved linter check with #nosemgrep
(cherry picked from commit 8c8e25214c)
2024-09-17 20:27:32 +00:00
Khushi Rawat
78c68397d9 fix: updated filtering in depreciation and balances report
(cherry picked from commit 3a34eecdcf)
2024-09-17 20:27:32 +00:00
ruthra kumar
f61cec27ae Merge pull request #43259 from frappe/mergify/bp/version-15-hotfix/pr-43226
fix: map rows on journal entry by validating account, party, debit and credit value (backport #43226)
2024-09-17 20:22:59 +05:30
Navin-S-R
78768f883c fix: create fiscal year without overlapping existing Fiscal Years
(cherry picked from commit 720a330617)
2024-09-17 14:35:39 +00:00
Navin-S-R
edcdfdd194 refactor: update formatting changes
(cherry picked from commit 768bb0312a)
2024-09-17 14:35:39 +00:00
ruthra kumar
861edb438b refactor(test): make use existing test data and dynamic fy creation
(cherry picked from commit f45638015f)
2024-09-17 14:35:39 +00:00
ruthra kumar
d91013a467 fix: ignore repost logic on Payment Reconciliation
(cherry picked from commit 75babd4c18)
2024-09-17 14:35:39 +00:00
Navin-S-R
310b131469 test: reconcile payment jv from closed fiscal year
(cherry picked from commit f47ea46806)
2024-09-17 14:35:39 +00:00
Navin-S-R
86e1818420 fix: map rows on journal entry by validating account, party, debit and credit value
(cherry picked from commit b634aa9cfb)
2024-09-17 14:35:38 +00:00
ruthra kumar
5fe347c909 Merge pull request #43249 from frappe/mergify/bp/version-15-hotfix/pr-43188
fix: invalid gp calculation (backport #43188)
2024-09-17 14:34:58 +05:30
Khushi Rawat
44dde1c58d Merge pull request #43243 from frappe/mergify/bp/version-15-hotfix/pr-43233
fix: do not validate purchase document for composite asset (backport #43233)
2024-09-17 12:31:24 +05:30
Dany Robert
291f0a580b fix: invalid gp calculation
(cherry picked from commit c79851239c)
2024-09-17 06:33:19 +00:00
ruthra kumar
9c4eaa230c Merge pull request #43246 from frappe/mergify/bp/version-15-hotfix/pr-42969
fix: A project without tasks should be able to complete (backport #42969)
2024-09-17 10:43:18 +05:30
Frappe PR Bot
28f1f9355d chore(release): Bumped to Version 15.35.2
## [15.35.2](https://github.com/frappe/erpnext/compare/v15.35.1...v15.35.2) (2024-09-17)

### Bug Fixes

* currency changing while making PO from Supplier Quotation (backport [#43187](https://github.com/frappe/erpnext/issues/43187)) ([#43205](https://github.com/frappe/erpnext/issues/43205)) ([2f56ba7](2f56ba7f42))
2024-09-17 05:00:07 +00:00
ruthra kumar
41db9d3886 Merge pull request #43209 from frappe/mergify/bp/version-15/pr-43205
fix: currency changing while making PO from Supplier Quotation (backport #43187) (backport #43205)
2024-09-17 10:28:43 +05:30
ruthra kumar
53c4c153ca Merge pull request #43245 from frappe/mergify/bp/version-15-hotfix/pr-43225
fix(ux): set amount based on account currency while adding new row (backport #43225)
2024-09-17 10:26:43 +05:30
ruthra kumar
bee27f314f Merge pull request #43230 from frappe/mergify/bp/version-15-hotfix/pr-43212
fix: prevent KeyError by checking `report_filter` existence (backport #43212)
2024-09-17 10:23:58 +05:30
ruthra kumar
c9b6b0d868 refactor(test): fix linter
(cherry picked from commit 4eeae8011e)
2024-09-17 04:49:45 +00:00
lukas.brandhoff
dea735de4d fix: A project without tasks should be able to complete
(cherry picked from commit 268962c25f)
2024-09-17 04:49:44 +00:00
Navin-S-R
f7cedac526 fix(ux): set amount based on account currency while adding new row
(cherry picked from commit 0ff04f774d)
2024-09-17 04:48:42 +00:00
Khushi Rawat
c5051561e4 fix: do not validate purchase document for composite asset
(cherry picked from commit 5fd058dde9)
2024-09-16 18:48:26 +00:00
Shariq Ansari
48158fbde0 Merge pull request #43242 from frappe/mergify/bp/version-15-hotfix/pr-43238
fix: create and link address while creating prospect & customer (backport #43238)
2024-09-16 22:47:00 +05:30
Shariq Ansari
d6a3d0d468 fix: create and link address while creating prospect & customer
(cherry picked from commit 035c15794c)
2024-09-16 16:46:01 +00:00
Smit Vora
30e9f08f37 Merge pull request #43241 from frappe/mergify/bp/version-15-hotfix/pr-43176
fix: hide and reset discount control on new POS order (backport #43176)
2024-09-16 20:01:26 +05:30
ljain112
42494db3c7 fix: hide and reset discount control on new POS order
(cherry picked from commit 5b0053f8dd)
2024-09-16 14:25:20 +00:00
Smit Vora
a6a2b2daae Merge pull request #43237 from frappe/mergify/bp/version-15-hotfix/pr-42849
fix: TDS workflow consistency in Purchase Order (backport #42849)
2024-09-16 19:41:33 +05:30
ljain112
741c18b144 fix: do not auto apply tds in purchase order
(cherry picked from commit 0b942a0614)
2024-09-16 13:16:15 +00:00
ljain112
7027be8fbc fix: set tax_withholding_category from Purchase Order while creating pi form po
(cherry picked from commit b9048ca6fa)
2024-09-16 13:16:15 +00:00
ljain112
01f30682ee fix: consistent behaviour on refresh
(cherry picked from commit b216d71278)
2024-09-16 13:16:15 +00:00
ljain112
5edebb28a5 fix: do not check appy_tds in Purchase Order Automatically
(cherry picked from commit be6c174b43)
2024-09-16 13:16:14 +00:00
ljain112
11359bd235 fix: tds workflow in purchase order
(cherry picked from commit a7888b26a7)
2024-09-16 13:16:13 +00:00
ljain112
2ad6d637ee fix: revert 091c5496b2
(cherry picked from commit eeb6e75dcf)
2024-09-16 13:16:12 +00:00
ruthra kumar
564ff034b7 Merge pull request #43232 from frappe/mergify/bp/version-15-hotfix/pr-43224
fix(ux): set amount on foreign currency when foreign currency account… (backport #43224)
2024-09-16 13:49:32 +05:30
venkat102
d8d4cd23a5 fix(ux): set amount on foreign currency when foreign currency account is selected on last row of journal
(cherry picked from commit 2b66842d34)
2024-09-16 06:16:09 +00:00
ljain112
984acb661d fix: prevent KeyError by checking report_filter existence
(cherry picked from commit c1d2cc2c14)
2024-09-16 03:48:37 +00:00
Shariq Ansari
00f144ed68 Merge pull request #43223 from frappe/mergify/bp/version-15-hotfix/pr-43198
feat: API for crm integration (backport #43198)
2024-09-14 14:46:14 +05:30
Nabin Hait
f060534625 feat: API for crm integration
(cherry picked from commit b7bf9f80f2)
2024-09-14 08:59:51 +00:00
mergify[bot]
f101a1ce3b fix(holiday-list): use same date format for same holiday error message (backport #42606) (#43222)
fix(holiday-list): use same date format for same holiday error message (#42606)

* fix(holiday-list): use same date format for same holiday error message

* chore: fix formatting

---------

Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
(cherry picked from commit a435441536)

Co-authored-by: Ananyobrata Pal <74728797+ananyo141@users.noreply.github.com>
2024-09-14 13:36:26 +05:30
sameer Chauhan
ad2d6a1625 fix: Payment Ledger Report currency fieldtype fix 2024-09-13 17:32:44 +05:30
mergify[bot]
2f56ba7f42 fix: currency changing while making PO from Supplier Quotation (backport #43187) (#43205)
fix: currency changing while making PO from Supplier Quotation (#43187)

(cherry picked from commit 2b96e37c34)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
(cherry picked from commit ef6b172616)
2024-09-13 08:01:44 +00:00
mergify[bot]
ef6b172616 fix: currency changing while making PO from Supplier Quotation (backport #43187) (#43205)
fix: currency changing while making PO from Supplier Quotation (#43187)

(cherry picked from commit 2b96e37c34)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-09-13 13:30:36 +05:30
mergify[bot]
61a42ea5d7 fix: batch based item price not working (backport #43172) (#43206)
* fix: batch based item price not working (#43172)

(cherry picked from commit d9e4ed13cb)

# Conflicts:
#	erpnext/stock/get_item_details.py

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-09-13 13:30:12 +05:30
mergify[bot]
c1a6c56217 fix: item list view in website (backport #43165) (#43207)
fix: item list view in website (#43165)

(cherry picked from commit ce34bb9793)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-09-13 13:29:58 +05:30
ruthra kumar
b7e95bfd22 Merge pull request #43202 from frappe/mergify/bp/version-15-hotfix/pr-43191
fix: delete exchange gain loss journal entry while deleting payment entry (backport #43191)
2024-09-13 11:22:46 +05:30
ruthra kumar
7989bc23e1 Merge pull request #43204 from frappe/mergify/bp/version-15-hotfix/pr-43192
refactor(test): use test fixture on pricing rule test suite (backport #43192)
2024-09-13 11:22:13 +05:30
ruthra kumar
055e7820c8 refactor(test): use test fixture on pricing rule test suite
(cherry picked from commit 0ea1d6d960)
2024-09-13 05:20:22 +00:00
Navin-S-R
d618c9a481 test: add unit test for deletion of gain loss jv while deleting payment entry
(cherry picked from commit 7855d3034b)
2024-09-13 05:19:05 +00:00
Navin-S-R
5789de25b9 fix: delete exchange gain loss journal entry while deleting payment entry
(cherry picked from commit 9886cf0d46)
2024-09-13 05:19:04 +00:00
Sagar Vora
4df38d357f Merge pull request #43186 from frappe/mergify/bp/version-15-hotfix/pr-43171
fix: set `party_type` null when `payment_type` is changed to `Internal Transfer` (backport #43171)
2024-09-12 12:03:28 +05:30
Vishakh Desai
45ff8fa296 fix: set party_type null when payment_type is changed to Internal Transfer
(cherry picked from commit 502cf0eb8d)
2024-09-12 06:32:46 +00:00
ruthra kumar
28607f0026 Merge pull request #43183 from frappe/mergify/bp/version-15-hotfix/pr-43182
fix: typo with po_date when creating remarks (backport #43182)
2024-09-12 11:30:53 +05:30
Smit Vora
1657a83151 fix: typo with po_date when creating remarks
(cherry picked from commit a55502e0f1)
2024-09-12 05:45:00 +00:00
ruthra kumar
aab91a2307 Merge pull request #43169 from ruthra-kumar/no_copy_on_purchase_invoice_status
refactor: enable no-copy on Purchase Invoice status
2024-09-11 13:25:33 +05:30
ruthra kumar
0d9741fdd7 refactor: enable no-copy on Purchase Invoice status 2024-09-11 13:03:20 +05:30
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
323 changed files with 19981 additions and 3099 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.35.1"
__version__ = "15.45.1"
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}='{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",
@@ -103,14 +104,12 @@ class Account(NestedSet):
self.name = get_autoname_with_number(self.account_number, self.account_name, self.company)
def validate(self):
from erpnext.accounts.utils import validate_field_number
if frappe.local.flags.allow_unverified_charts:
return
self.validate_parent()
self.validate_parent_child_account_type()
self.validate_root_details()
validate_field_number("Account", self.name, self.account_number, self.company, "account_number")
self.validate_account_number()
self.validate_group_or_ledger()
self.set_root_and_report_type()
self.validate_mandatory()
@@ -311,6 +310,22 @@ class Account(NestedSet):
if frappe.db.get_value("GL Entry", {"account": self.name}):
frappe.throw(_("Currency can not be changed after making entries using some other currency"))
def validate_account_number(self, account_number=None):
if not account_number:
account_number = self.account_number
if account_number:
account_with_same_number = frappe.db.get_value(
"Account",
{"account_number": account_number, "company": self.company, "name": ["!=", self.name]},
)
if account_with_same_number:
frappe.throw(
_("Account Number {0} already used in account {1}").format(
account_number, account_with_same_number
)
)
def create_account_for_child_company(self, parent_acc_name_map, descendants, parent_acc_name):
for company in descendants:
company_bold = frappe.bold(company)
@@ -464,19 +479,6 @@ def get_account_autoname(account_number, account_name, company):
return " - ".join(parts)
def validate_account_number(name, account_number, company):
if account_number:
account_with_same_number = frappe.db.get_value(
"Account", {"account_number": account_number, "company": company, "name": ["!=", name]}
)
if account_with_same_number:
frappe.throw(
_("Account Number {0} already used in account {1}").format(
account_number, account_with_same_number
)
)
@frappe.whitelist()
def update_account_number(name, account_name, account_number=None, from_descendant=False):
account = frappe.get_cached_doc("Account", name)
@@ -517,7 +519,7 @@ def update_account_number(name, account_name, account_number=None, from_descenda
frappe.throw(message, title=_("Rename Not Allowed"))
validate_account_number(name, account_number, account.company)
account.validate_account_number(account_number)
if account_number:
frappe.db.set_value("Account", name, "account_number", account_number.strip())
else:

View File

@@ -113,9 +113,9 @@ def get_previous_closing_entries(company, closing_date, accounting_dimensions):
entries = []
last_period_closing_voucher = frappe.db.get_all(
"Period Closing Voucher",
filters={"docstatus": 1, "company": company, "posting_date": ("<", closing_date)},
filters={"docstatus": 1, "company": company, "period_end_date": ("<", closing_date)},
fields=["name"],
order_by="posting_date desc",
order_by="period_end_date desc",
limit=1,
)

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

@@ -101,6 +101,8 @@ def validate_accounting_period_on_doc_save(doc, method=None):
date = doc.available_for_use_date
elif doc.doctype == "Asset Repair":
date = doc.completion_date
elif doc.doctype == "Period Closing Voucher":
date = doc.period_end_date
else:
date = doc.posting_date

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

@@ -208,8 +208,49 @@
"label": "Disabled"
}
],
"links": [],
"modified": "2023-09-22 21:31:34.763977",
"links": [
{
"group": "Transactions",
"link_doctype": "Payment Request",
"link_fieldname": "bank_account"
},
{
"group": "Transactions",
"link_doctype": "Payment Order",
"link_fieldname": "bank_account"
},
{
"group": "Transactions",
"link_doctype": "Bank Guarantee",
"link_fieldname": "bank_account"
},
{
"group": "Transactions",
"link_doctype": "Bank Transaction",
"link_fieldname": "bank_account"
},
{
"group": "Accounting",
"link_doctype": "Payment Entry",
"link_fieldname": "bank_account"
},
{
"group": "Accounting",
"link_doctype": "Journal Entry",
"link_fieldname": "bank_account"
},
{
"group": "Party",
"link_doctype": "Customer",
"link_fieldname": "default_bank_account"
},
{
"group": "Party",
"link_doctype": "Supplier",
"link_fieldname": "default_bank_account"
}
],
"modified": "2024-10-30 09:41:14.113414",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Account",
@@ -246,4 +287,4 @@
"sort_order": "DESC",
"states": [],
"track_changes": 1
}
}

View File

@@ -1,20 +0,0 @@
from frappe import _
def get_data():
return {
"fieldname": "bank_account",
"non_standard_fieldnames": {
"Customer": "default_bank_account",
"Supplier": "default_bank_account",
},
"transactions": [
{
"label": _("Payments"),
"items": ["Payment Entry", "Payment Request", "Payment Order", "Payroll Entry"],
},
{"label": _("Party"), "items": ["Customer", "Supplier"]},
{"items": ["Bank Guarantee"]},
{"items": ["Journal Entry"]},
],
}

View File

@@ -108,8 +108,18 @@ class BankClearance(Document):
if not d.clearance_date:
d.clearance_date = None
payment_entry = frappe.get_doc(d.payment_document, d.payment_entry)
payment_entry.db_set("clearance_date", d.clearance_date)
if d.payment_document == "Sales Invoice":
frappe.db.set_value(
"Sales Invoice Payment",
{"parent": d.payment_entry, "account": self.get("account"), "amount": [">", 0]},
"clearance_date",
d.clearance_date,
)
else:
frappe.db.set_value(
d.payment_document, d.payment_entry, "clearance_date", d.clearance_date
)
clearance_date_updated = True
@@ -158,7 +168,7 @@ def get_payment_entries_for_bank_clearance(
"Payment Entry" as payment_document, name as payment_entry,
reference_no as cheque_number, reference_date as cheque_date,
if(paid_from=%(account)s, paid_amount + total_taxes_and_charges, 0) as credit,
if(paid_from=%(account)s, 0, received_amount) as debit,
if(paid_from=%(account)s, 0, received_amount + total_taxes_and_charges) as debit,
posting_date, ifnull(party,if(paid_from=%(account)s,paid_to,paid_from)) as against_account, clearance_date,
if(paid_to=%(account)s, paid_to_account_currency, paid_from_account_currency) as account_currency
from `tabPayment Entry`

View File

@@ -6,16 +6,29 @@ import unittest
import frappe
from frappe.utils import add_months, getdate
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.stock.doctype.item.test_item import create_item
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
from erpnext.tests.utils import if_lending_app_installed, if_lending_app_not_installed
class TestBankClearance(unittest.TestCase):
@classmethod
def setUpClass(cls):
create_warehouse(
warehouse_name="_Test Warehouse",
properties={"parent_warehouse": "All Warehouses - _TC"},
company="_Test Company",
)
create_item("_Test Item")
create_cost_center(cost_center_name="_Test Cost Center", company="_Test Company")
clear_payment_entries()
clear_loan_transactions()
clear_pos_sales_invoices()
make_bank_account()
add_transactions()
@@ -83,11 +96,41 @@ class TestBankClearance(unittest.TestCase):
bank_clearance.get_payment_entries()
self.assertEqual(len(bank_clearance.payment_entries), 3)
def test_update_clearance_date_on_si(self):
sales_invoice = make_pos_sales_invoice()
date = getdate()
bank_clearance = frappe.get_doc("Bank Clearance")
bank_clearance.account = "_Test Bank Clearance - _TC"
bank_clearance.from_date = add_months(date, -1)
bank_clearance.to_date = date
bank_clearance.include_pos_transactions = 1
bank_clearance.get_payment_entries()
self.assertNotEqual(len(bank_clearance.payment_entries), 0)
for payment in bank_clearance.payment_entries:
if payment.payment_entry == sales_invoice.name:
payment.clearance_date = date
bank_clearance.update_clearance_date()
si_clearance_date = frappe.db.get_value(
"Sales Invoice Payment",
{"parent": sales_invoice.name, "account": bank_clearance.account},
"clearance_date",
)
self.assertEqual(si_clearance_date, date)
def clear_payment_entries():
frappe.db.delete("Payment Entry")
def clear_pos_sales_invoices():
frappe.db.delete("Sales Invoice", {"is_pos": 1})
@if_lending_app_installed
def clear_loan_transactions():
for dt in [
@@ -115,9 +158,45 @@ def add_transactions():
def make_payment_entry():
pi = make_purchase_invoice(supplier="_Test Supplier", qty=1, rate=690)
from erpnext.buying.doctype.supplier.test_supplier import create_supplier
supplier = create_supplier(supplier_name="_Test Supplier")
pi = make_purchase_invoice(
supplier=supplier,
supplier_warehouse="_Test Warehouse - _TC",
expense_account="Cost of Goods Sold - _TC",
uom="Nos",
qty=1,
rate=690,
)
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank Clearance - _TC")
pe.reference_no = "Conrad Oct 18"
pe.reference_date = "2018-10-24"
pe.insert()
pe.submit()
def make_pos_sales_invoice():
from erpnext.accounts.doctype.opening_invoice_creation_tool.test_opening_invoice_creation_tool import (
make_customer,
)
mode_of_payment = frappe.get_doc({"doctype": "Mode of Payment", "name": "Cash"})
if not frappe.db.get_value("Mode of Payment Account", {"company": "_Test Company", "parent": "Cash"}):
mode_of_payment.append(
"accounts", {"company": "_Test Company", "default_account": "_Test Bank Clearance - _TC"}
)
mode_of_payment.save()
customer = make_customer(customer="_Test Customer")
si = create_sales_invoice(customer=customer, item="_Test Item", is_pos=1, qty=1, rate=1000, do_not_save=1)
si.set("payments", [])
si.append(
"payments", {"mode_of_payment": "Cash", "account": "_Test Bank Clearance - _TC", "amount": 1000}
)
si.insert()
si.submit()
return si

View File

@@ -22,8 +22,10 @@ class TestCostCenterAllocation(unittest.TestCase):
cost_centers = [
"Main Cost Center 1",
"Main Cost Center 2",
"Main Cost Center 3",
"Sub Cost Center 1",
"Sub Cost Center 2",
"Sub Cost Center 3",
]
for cc in cost_centers:
create_cost_center(cost_center_name=cc, company="_Test Company")
@@ -36,7 +38,7 @@ class TestCostCenterAllocation(unittest.TestCase):
)
jv = make_journal_entry(
"_Test Cash - _TC", "Sales - _TC", 100, cost_center="Main Cost Center 1 - _TC", submit=True
"Cash - _TC", "Sales - _TC", 100, cost_center="Main Cost Center 1 - _TC", submit=True
)
expected_values = [["Sub Cost Center 1 - _TC", 0.0, 60], ["Sub Cost Center 2 - _TC", 0.0, 40]]
@@ -120,7 +122,7 @@ class TestCostCenterAllocation(unittest.TestCase):
def test_valid_from_based_on_existing_gle(self):
# GLE posted against Sub Cost Center 1 on today
jv = make_journal_entry(
"_Test Cash - _TC",
"Cash - _TC",
"Sales - _TC",
100,
cost_center="Main Cost Center 1 - _TC",
@@ -141,6 +143,53 @@ class TestCostCenterAllocation(unittest.TestCase):
jv.cancel()
def test_multiple_cost_center_allocation_on_same_main_cost_center(self):
coa1 = create_cost_center_allocation(
"_Test Company",
"Main Cost Center 3 - _TC",
{"Sub Cost Center 1 - _TC": 30, "Sub Cost Center 2 - _TC": 30, "Sub Cost Center 3 - _TC": 40},
valid_from=add_days(today(), -5),
)
coa2 = create_cost_center_allocation(
"_Test Company",
"Main Cost Center 3 - _TC",
{"Sub Cost Center 1 - _TC": 50, "Sub Cost Center 2 - _TC": 50},
valid_from=add_days(today(), -1),
)
jv = make_journal_entry(
"Cash - _TC",
"Sales - _TC",
100,
cost_center="Main Cost Center 3 - _TC",
posting_date=today(),
submit=True,
)
expected_values = {"Sub Cost Center 1 - _TC": 50, "Sub Cost Center 2 - _TC": 50}
gle = frappe.qb.DocType("GL Entry")
gl_entries = (
frappe.qb.from_(gle)
.select(gle.cost_center, gle.debit, gle.credit)
.where(gle.voucher_type == "Journal Entry")
.where(gle.voucher_no == jv.name)
.where(gle.account == "Sales - _TC")
.orderby(gle.cost_center)
).run(as_dict=1)
self.assertTrue(gl_entries)
for gle in gl_entries:
self.assertTrue(gle.cost_center in expected_values)
self.assertEqual(gle.debit, 0)
self.assertEqual(gle.credit, expected_values[gle.cost_center])
coa1.cancel()
coa2.cancel()
jv.cancel()
def create_cost_center_allocation(
company,

View File

@@ -109,7 +109,7 @@ def get_api_endpoint(service_provider: str | None = None, use_http: bool = False
if service_provider == "exchangerate.host":
api = "api.exchangerate.host/convert"
elif service_provider == "frankfurter.app":
api = "frankfurter.app/{transaction_date}"
api = "api.frankfurter.app/{transaction_date}"
protocol = "https://"
if use_http:

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

@@ -210,19 +210,31 @@ def get_linked_dunnings_as_per_state(sales_invoice, state):
@frappe.whitelist()
def get_dunning_letter_text(dunning_type, doc, language=None):
def get_dunning_letter_text(dunning_type: str, doc: str | dict, language: str | None = None) -> dict:
DOCTYPE = "Dunning Letter Text"
FIELDS = ["body_text", "closing_text", "language"]
if isinstance(doc, str):
doc = json.loads(doc)
if not language:
language = doc.get("language")
if language:
filters = {"parent": dunning_type, "language": language}
else:
filters = {"parent": dunning_type, "is_default_language": 1}
letter_text = frappe.db.get_value(
"Dunning Letter Text", filters, ["body_text", "closing_text", "language"], as_dict=1
)
if letter_text:
return {
"body_text": frappe.render_template(letter_text.body_text, doc),
"closing_text": frappe.render_template(letter_text.closing_text, doc),
"language": letter_text.language,
}
letter_text = frappe.db.get_value(
DOCTYPE, {"parent": dunning_type, "language": language}, FIELDS, as_dict=1
)
if not letter_text:
letter_text = frappe.db.get_value(
DOCTYPE, {"parent": dunning_type, "is_default_language": 1}, FIELDS, as_dict=1
)
if not letter_text:
return {}
return {
"body_text": frappe.render_template(letter_text.body_text, doc),
"closing_text": frappe.render_template(letter_text.closing_text, doc),
"language": letter_text.language,
}

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

@@ -6,38 +6,50 @@
"document_type": "Document",
"engine": "InnoDB",
"field_order": [
"dates_section",
"posting_date",
"transaction_date",
"column_break_avko",
"fiscal_year",
"due_date",
"account_details_section",
"account",
"account_currency",
"column_break_ifvf",
"against",
"party_type",
"party",
"cost_center",
"debit",
"credit",
"account_currency",
"debit_in_account_currency",
"credit_in_account_currency",
"against",
"transaction_details_section",
"voucher_type",
"voucher_no",
"voucher_subtype",
"transaction_currency",
"column_break_dpsx",
"against_voucher_type",
"against_voucher",
"voucher_type",
"voucher_subtype",
"voucher_no",
"voucher_detail_no",
"transaction_exchange_rate",
"amounts_section",
"debit_in_account_currency",
"debit",
"debit_in_transaction_currency",
"column_break_bm1w",
"credit_in_account_currency",
"credit",
"credit_in_transaction_currency",
"dimensions_section",
"cost_center",
"column_break_lmnm",
"project",
"remarks",
"more_info_section",
"finance_book",
"company",
"is_opening",
"is_advance",
"fiscal_year",
"company",
"finance_book",
"column_break_8abq",
"to_rename",
"due_date",
"is_cancelled",
"transaction_currency",
"debit_in_transaction_currency",
"credit_in_transaction_currency",
"transaction_exchange_rate"
"remarks"
],
"fields": [
{
@@ -285,13 +297,67 @@
"fieldname": "voucher_subtype",
"fieldtype": "Small Text",
"label": "Voucher Subtype"
},
{
"fieldname": "dates_section",
"fieldtype": "Section Break",
"label": "Dates"
},
{
"fieldname": "column_break_avko",
"fieldtype": "Column Break"
},
{
"fieldname": "account_details_section",
"fieldtype": "Section Break",
"label": "Account Details"
},
{
"fieldname": "column_break_ifvf",
"fieldtype": "Column Break"
},
{
"fieldname": "transaction_details_section",
"fieldtype": "Section Break",
"label": "Transaction Details"
},
{
"fieldname": "amounts_section",
"fieldtype": "Section Break",
"label": "Amounts"
},
{
"fieldname": "column_break_dpsx",
"fieldtype": "Column Break"
},
{
"fieldname": "more_info_section",
"fieldtype": "Section Break",
"label": "More Info"
},
{
"fieldname": "column_break_bm1w",
"fieldtype": "Column Break"
},
{
"fieldname": "dimensions_section",
"fieldtype": "Section Break",
"label": "Dimensions"
},
{
"fieldname": "column_break_lmnm",
"fieldtype": "Column Break"
},
{
"fieldname": "column_break_8abq",
"fieldtype": "Column Break"
}
],
"icon": "fa fa-list",
"idx": 1,
"in_create": 1,
"links": [],
"modified": "2024-07-02 14:31:51.496466",
"modified": "2024-08-22 13:03:39.997475",
"modified_by": "Administrator",
"module": "Accounts",
"name": "GL Entry",

View File

@@ -430,8 +430,9 @@ def update_against_account(voucher_type, voucher_no):
def on_doctype_update():
frappe.db.add_index("GL Entry", ["against_voucher_type", "against_voucher"])
frappe.db.add_index("GL Entry", ["voucher_type", "voucher_no"])
frappe.db.add_index("GL Entry", ["posting_date", "company"])
frappe.db.add_index("GL Entry", ["party_type", "party"])
def rename_gle_sle_docs():

View File

@@ -360,21 +360,23 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro
accounts_add(doc, cdt, cdn) {
var row = frappe.get_doc(cdt, cdn);
row.exchange_rate = 1;
$.each(doc.accounts, function (i, d) {
if (d.account && d.party && d.party_type) {
row.account = d.account;
row.party = d.party;
row.party_type = d.party_type;
row.exchange_rate = d.exchange_rate;
}
});
// set difference
if (doc.difference) {
if (doc.difference > 0) {
row.credit_in_account_currency = doc.difference;
row.credit_in_account_currency = doc.difference / row.exchange_rate;
row.credit = doc.difference;
} else {
row.debit_in_account_currency = -doc.difference;
row.debit_in_account_currency = -doc.difference / row.exchange_rate;
row.debit = -doc.difference;
}
}
@@ -680,6 +682,7 @@ $.extend(erpnext.journal_entry, {
callback: function (r) {
if (r.message) {
$.extend(d, r.message);
erpnext.journal_entry.set_amount_on_last_row(frm, dt, dn);
erpnext.journal_entry.set_debit_credit_in_company_currency(frm, dt, dn);
refresh_field("accounts");
}
@@ -687,4 +690,26 @@ $.extend(erpnext.journal_entry, {
});
}
},
set_amount_on_last_row: function (frm, dt, dn) {
let row = locals[dt][dn];
let length = frm.doc.accounts.length;
if (row.idx != length) return;
let difference = frm.doc.accounts.reduce((total, row) => {
if (row.idx == length) return total;
return total + row.debit - row.credit;
}, 0);
if (difference) {
if (difference > 0) {
row.credit_in_account_currency = difference / row.exchange_rate;
row.credit = difference;
} else {
row.debit_in_account_currency = -difference / row.exchange_rate;
row.debit = -difference;
}
}
refresh_field("accounts");
},
});

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()
@@ -195,6 +198,11 @@ class JournalEntry(AccountsController):
self.update_booked_depreciation()
def on_update_after_submit(self):
# Flag will be set on Reconciliation
# Reconciliation tool will anyways repost ledger entries. So, no need to check and do implicit repost.
if self.flags.get("ignore_reposting_on_reconciliation"):
return
self.needs_repost = self.check_if_fields_updated(fields_to_check=[], child_tables={"accounts": []})
if self.needs_repost:
self.validate_for_repost()
@@ -213,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()
@@ -254,7 +264,7 @@ class JournalEntry(AccountsController):
frappe.throw(_("Journal Entry type should be set as Depreciation Entry for asset depreciation"))
def validate_stock_accounts(self):
stock_accounts = get_stock_accounts(self.company, self.doctype, self.name)
stock_accounts = get_stock_accounts(self.company, accounts=self.accounts)
for account in stock_accounts:
account_bal, stock_bal, warehouse_list = get_stock_and_account_balance(
account, self.posting_date, self.company
@@ -1663,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,72 @@ 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})
jv.accounts[1].update({"party_type": "Customer", "party": "_Test Customer USD", "exchange_rate": 85})
jv.submit()
actual = frappe.db.get_all(
"GL Entry",
filters={"voucher_no": jv.name, "is_cancelled": 0},
fields=["account", "transaction_exchange_rate"],
order_by="account",
)
expected = [
{"account": "_Test Bank - _TC", "transaction_exchange_rate": 1.0},
{"account": "_Test Receivable USD - _TC", "transaction_exchange_rate": 85.0},
]
self.assertEqual(expected, actual)
def make_journal_entry(
account1,

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) {
@@ -174,6 +178,21 @@ frappe.ui.form.on("Payment Entry", {
};
});
frm.set_query("payment_request", "references", function (doc, cdt, cdn) {
const row = frappe.get_doc(cdt, cdn);
return {
query: "erpnext.accounts.doctype.payment_request.payment_request.get_open_payment_requests_query",
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,
},
};
});
frm.set_query("sales_taxes_and_charges_template", function () {
return {
filters: {
@@ -191,7 +210,15 @@ frappe.ui.form.on("Payment Entry", {
},
};
});
frm.add_fetch(
"payment_request",
"outstanding_amount",
"payment_request_outstanding",
"Payment Entry Reference"
);
},
refresh: function (frm) {
erpnext.hide_company(frm);
frm.events.hide_unhide_fields(frm);
@@ -216,6 +243,7 @@ frappe.ui.form.on("Payment Entry", {
);
}
erpnext.accounts.unreconcile_payment.add_unreconcile_btn(frm);
frappe.flags.allocate_payment_amount = true;
},
validate_company: (frm) => {
@@ -296,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) {
@@ -383,9 +406,19 @@ frappe.ui.form.on("Payment Entry", {
},
payment_type: function (frm) {
set_default_party_type(frm);
if (frm.doc.payment_type == "Internal Transfer") {
$.each(
["party", "party_balance", "paid_from", "paid_to", "references", "total_allocated_amount"],
[
"party",
"party_type",
"party_balance",
"paid_from",
"paid_to",
"references",
"total_allocated_amount",
],
function (i, field) {
frm.set_value(field, null);
}
@@ -789,7 +822,7 @@ frappe.ui.form.on("Payment Entry", {
);
if (frm.doc.payment_type == "Pay")
frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.received_amount, 1);
frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.received_amount, true);
else frm.events.set_unallocated_amount(frm);
frm.set_paid_amount_based_on_received_amount = false;
@@ -810,7 +843,7 @@ frappe.ui.form.on("Payment Entry", {
}
if (frm.doc.payment_type == "Receive")
frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.paid_amount, 1);
frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.paid_amount, true);
else frm.events.set_unallocated_amount(frm);
},
@@ -981,6 +1014,7 @@ frappe.ui.form.on("Payment Entry", {
c.outstanding_amount = d.outstanding_amount;
c.bill_no = d.bill_no;
c.payment_term = d.payment_term;
c.payment_term_outstanding = d.payment_term_outstanding;
c.allocated_amount = d.allocated_amount;
c.account = d.account;
@@ -1030,7 +1064,8 @@ frappe.ui.form.on("Payment Entry", {
frm.events.allocate_party_amount_against_ref_docs(
frm,
frm.doc.payment_type == "Receive" ? frm.doc.paid_amount : frm.doc.received_amount
frm.doc.payment_type == "Receive" ? frm.doc.paid_amount : frm.doc.received_amount,
false
);
},
});
@@ -1044,93 +1079,13 @@ frappe.ui.form.on("Payment Entry", {
return ["Sales Invoice", "Purchase Invoice"];
},
allocate_party_amount_against_ref_docs: function (frm, paid_amount, paid_amount_change) {
var total_positive_outstanding_including_order = 0;
var total_negative_outstanding = 0;
var total_deductions = frappe.utils.sum(
$.map(frm.doc.deductions || [], function (d) {
return flt(d.amount);
})
);
paid_amount -= total_deductions;
$.each(frm.doc.references || [], function (i, row) {
if (flt(row.outstanding_amount) > 0)
total_positive_outstanding_including_order += flt(row.outstanding_amount);
else total_negative_outstanding += Math.abs(flt(row.outstanding_amount));
allocate_party_amount_against_ref_docs: async function (frm, paid_amount, paid_amount_change) {
await frm.call("allocate_amount_to_references", {
paid_amount: paid_amount,
paid_amount_change: paid_amount_change,
allocate_payment_amount: frappe.flags.allocate_payment_amount ?? false,
});
var allocated_negative_outstanding = 0;
if (
(frm.doc.payment_type == "Receive" && frm.doc.party_type == "Customer") ||
(frm.doc.payment_type == "Pay" && frm.doc.party_type == "Supplier") ||
(frm.doc.payment_type == "Pay" && frm.doc.party_type == "Employee")
) {
if (total_positive_outstanding_including_order > paid_amount) {
var remaining_outstanding = total_positive_outstanding_including_order - paid_amount;
allocated_negative_outstanding =
total_negative_outstanding < remaining_outstanding
? total_negative_outstanding
: remaining_outstanding;
}
var allocated_positive_outstanding = paid_amount + allocated_negative_outstanding;
} else if (["Customer", "Supplier"].includes(frm.doc.party_type)) {
total_negative_outstanding = flt(total_negative_outstanding, precision("outstanding_amount"));
if (paid_amount > total_negative_outstanding) {
if (total_negative_outstanding == 0) {
frappe.msgprint(
__("Cannot {0} {1} {2} without any negative outstanding invoice", [
frm.doc.payment_type,
frm.doc.party_type == "Customer" ? "to" : "from",
frm.doc.party_type,
])
);
return false;
} else {
frappe.msgprint(
__("Paid Amount cannot be greater than total negative outstanding amount {0}", [
total_negative_outstanding,
])
);
return false;
}
} else {
allocated_positive_outstanding = total_negative_outstanding - paid_amount;
allocated_negative_outstanding =
paid_amount +
(total_positive_outstanding_including_order < allocated_positive_outstanding
? total_positive_outstanding_including_order
: allocated_positive_outstanding);
}
}
$.each(frm.doc.references || [], function (i, row) {
if (frappe.flags.allocate_payment_amount == 0) {
//If allocate payment amount checkbox is unchecked, set zero to allocate amount
row.allocated_amount = 0;
} else if (
frappe.flags.allocate_payment_amount != 0 &&
(!row.allocated_amount || paid_amount_change)
) {
if (row.outstanding_amount > 0 && allocated_positive_outstanding >= 0) {
row.allocated_amount =
row.outstanding_amount >= allocated_positive_outstanding
? allocated_positive_outstanding
: row.outstanding_amount;
allocated_positive_outstanding -= flt(row.allocated_amount);
} else if (row.outstanding_amount < 0 && allocated_negative_outstanding) {
row.allocated_amount =
Math.abs(row.outstanding_amount) >= allocated_negative_outstanding
? -1 * allocated_negative_outstanding
: row.outstanding_amount;
allocated_negative_outstanding -= Math.abs(flt(row.allocated_amount));
}
}
});
frm.refresh_fields();
frm.events.set_total_allocated_amount(frm);
},
@@ -1159,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;
}
}
@@ -1282,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) {
@@ -1678,6 +1639,62 @@ frappe.ui.form.on("Payment Entry", {
return current_tax_amount;
},
cost_center: function (frm) {
if (frm.doc.posting_date && (frm.doc.paid_from || frm.doc.paid_to)) {
return frappe.call({
method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_party_and_account_balance",
args: {
company: frm.doc.company,
date: frm.doc.posting_date,
paid_from: frm.doc.paid_from,
paid_to: frm.doc.paid_to,
ptype: frm.doc.party_type,
pty: frm.doc.party,
cost_center: frm.doc.cost_center,
},
callback: function (r, rt) {
if (r.message) {
frappe.run_serially([
() => {
frm.set_value(
"paid_from_account_balance",
r.message.paid_from_account_balance
);
frm.set_value("paid_to_account_balance", r.message.paid_to_account_balance);
frm.set_value("party_balance", r.message.party_balance);
},
]);
}
},
});
}
},
after_save: function (frm) {
const { matched_payment_requests } = frappe.last_response;
if (!matched_payment_requests) return;
const COLUMN_LABEL = [
[__("Reference DocType"), __("Reference Name"), __("Allocated Amount"), __("Payment Request")],
];
frappe.msgprint({
title: __("Unset Matched Payment Request"),
message: COLUMN_LABEL.concat(matched_payment_requests),
as_table: true,
wide: true,
primary_action: {
label: __("Allocate Payment Request"),
action() {
frappe.hide_msgprint();
frm.call("set_matched_payment_requests", { matched_payment_requests }, () => {
frm.dirty();
});
},
},
});
},
});
frappe.ui.form.on("Payment Entry Reference", {
@@ -1762,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);
},
@@ -1770,35 +1794,66 @@ frappe.ui.form.on("Payment Entry Deduction", {
frm.events.set_unallocated_amount(frm);
},
});
frappe.ui.form.on("Payment Entry", {
cost_center: function (frm) {
if (frm.doc.posting_date && (frm.doc.paid_from || frm.doc.paid_to)) {
return frappe.call({
method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_party_and_account_balance",
args: {
company: frm.doc.company,
date: frm.doc.posting_date,
paid_from: frm.doc.paid_from,
paid_to: frm.doc.paid_to,
ptype: frm.doc.party_type,
pty: frm.doc.party,
cost_center: frm.doc.cost_center,
},
callback: function (r, rt) {
if (r.message) {
frappe.run_serially([
() => {
frm.set_value(
"paid_from_account_balance",
r.message.paid_from_account_balance
);
frm.set_value("paid_to_account_balance", r.message.paid_to_account_balance);
frm.set_value("party_balance", r.message.party_balance);
},
]);
}
},
});
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

@@ -7,8 +7,10 @@ from functools import reduce
import frappe
from frappe import ValidationError, _, qb, scrub, throw
from frappe.query_builder import Tuple
from frappe.query_builder.functions import Count
from frappe.utils import cint, comma_or, flt, getdate, nowdate
from frappe.utils.data import comma_and, fmt_money
from frappe.utils.data import comma_and, fmt_money, get_link_to_form
from pypika import Case
from pypika.functions import Coalesce, Sum
@@ -98,13 +100,18 @@ class PaymentEntry(AccountsController):
self.set_status()
self.set_total_in_words()
def before_save(self):
self.set_matched_unset_payment_requests_to_response()
def on_submit(self):
if self.difference_amount:
frappe.throw(_("Difference Amount must be zero"))
self.make_gl_entries()
self.update_outstanding_amounts()
self.update_advance_paid()
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()
def set_liability_account(self):
@@ -145,9 +152,21 @@ class PaymentEntry(AccountsController):
self.is_opening = "No"
return
liability_account = get_party_account(
self.party_type, self.party, self.company, include_advance=True
)[1]
accounts = get_party_account(self.party_type, self.party, self.company, include_advance=True)
liability_account = accounts[1] if len(accounts) > 1 else None
fieldname = (
"default_advance_received_account"
if self.party_type == "Customer"
else "default_advance_paid_account"
)
if not liability_account:
throw(
_("Please set default {0} in Company {1}").format(
frappe.bold(frappe.get_meta("Company").get_label(fieldname)), frappe.bold(self.company)
)
)
self.set(self.party_account_field, liability_account)
@@ -172,34 +191,40 @@ 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)
self.update_outstanding_amounts()
self.update_advance_paid()
self.delink_advance_entry_references()
self.update_payment_schedule(cancel=1)
self.set_payment_req_status()
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()
def set_payment_req_status(self):
from erpnext.accounts.doctype.payment_request.payment_request import update_payment_req_status
def update_payment_requests(self, cancel=False):
from erpnext.accounts.doctype.payment_request.payment_request import (
update_payment_requests_as_per_pe_references,
)
update_payment_req_status(self, None)
update_payment_requests_as_per_pe_references(self.references, cancel=cancel)
def update_outstanding_amounts(self):
self.set_missing_ref_details(force=True)
def validate_duplicate_entry(self):
reference_names = []
reference_names = set()
for d in self.get("references"):
if (d.reference_doctype, d.reference_name, d.payment_term) in reference_names:
key = (d.reference_doctype, d.reference_name, d.payment_term, d.payment_request)
if key in reference_names:
frappe.throw(
_("Row #{0}: Duplicate entry in References {1} {2}").format(
d.idx, d.reference_doctype, d.reference_name
)
)
reference_names.append((d.reference_doctype, d.reference_name, d.payment_term))
reference_names.add(key)
def set_bank_account_data(self):
if self.bank_account:
@@ -225,6 +250,8 @@ class PaymentEntry(AccountsController):
if self.payment_type == "Internal Transfer":
return
self.validate_allocated_amount_as_per_payment_request()
if self.party_type in ("Customer", "Supplier"):
self.validate_allocated_amount_with_latest_data()
else:
@@ -237,6 +264,27 @@ class PaymentEntry(AccountsController):
if flt(d.allocated_amount) < 0 and flt(d.allocated_amount) < flt(d.outstanding_amount):
frappe.throw(fail_message.format(d.idx))
def validate_allocated_amount_as_per_payment_request(self):
"""
Allocated amount should not be greater than the outstanding amount of the Payment Request.
"""
if not self.references:
return
pr_outstanding_amounts = get_payment_request_outstanding_set_in_references(self.references)
if not pr_outstanding_amounts:
return
for ref in self.references:
if ref.payment_request and ref.allocated_amount > pr_outstanding_amounts[ref.payment_request]:
frappe.throw(
msg=_(
"Row #{0}: Allocated Amount cannot be greater than Outstanding Amount of Payment Request {1}"
).format(ref.idx, get_link_to_form("Payment Request", ref.payment_request)),
title=_("Invalid Allocated Amount"),
)
def term_based_allocation_enabled_for_reference(
self, reference_doctype: str, reference_name: str
) -> bool:
@@ -845,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):
@@ -940,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):
@@ -961,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) * (
@@ -1011,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
@@ -1098,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)
@@ -1121,6 +1229,8 @@ class PaymentEntry(AccountsController):
if not self.party_account:
return
advance_payment_doctypes = frappe.get_hooks("advance_payment_doctypes")
if self.payment_type == "Receive":
against_account = self.paid_to
else:
@@ -1163,14 +1273,41 @@ 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,
"against_voucher_type": d.reference_doctype,
"against_voucher": d.reference_name,
"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:
if d.reference_doctype in advance_payment_doctypes:
# Upon reconciliation, whole ledger will be reposted. So, reference to SO/PO is fine
gle.update(
{
"against_voucher_type": d.reference_doctype,
"against_voucher": d.reference_name,
}
)
else:
# Do not reference Invoices while Advance is in separate party account
gle.update({"against_voucher_type": self.doctype, "against_voucher": self.name})
else:
gle.update(
{
"against_voucher_type": d.reference_doctype,
"against_voucher": d.reference_name,
}
)
gl_entries.append(gle)
if self.unallocated_amount:
@@ -1179,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(
{
@@ -1594,6 +1740,380 @@ class PaymentEntry(AccountsController):
return current_tax_fraction
def set_matched_unset_payment_requests_to_response(self):
"""
Find matched Payment Requests for those references which have no Payment Request set.\n
And set to `frappe.response` to show in the frontend for allocation.
"""
if not self.references:
return
matched_payment_requests = get_matched_payment_request_of_references(
[row for row in self.references if not row.payment_request]
)
if not matched_payment_requests:
return
frappe.response["matched_payment_requests"] = matched_payment_requests
@frappe.whitelist()
def allocate_amount_to_references(self, paid_amount, paid_amount_change, allocate_payment_amount):
"""
Allocate `Allocated Amount` and `Payment Request` against `Reference` based on `Paid Amount` and `Outstanding Amount`.\n
:param paid_amount: Paid Amount / Received Amount.
:param paid_amount_change: Flag to check if `Paid Amount` is changed or not.
:param allocate_payment_amount: Flag to allocate amount or not. (Payment Request is also dependent on this flag)
"""
if not self.references:
return
if not allocate_payment_amount:
for ref in self.references:
ref.allocated_amount = 0
return
# calculating outstanding amounts
precision = self.precision("paid_amount")
total_positive_outstanding_including_order = 0
total_negative_outstanding = 0
paid_amount -= sum(flt(d.amount, precision) for d in self.deductions)
for ref in self.references:
reference_outstanding_amount = ref.outstanding_amount
abs_outstanding_amount = abs(reference_outstanding_amount)
if reference_outstanding_amount > 0:
total_positive_outstanding_including_order += abs_outstanding_amount
else:
total_negative_outstanding += abs_outstanding_amount
# calculating allocated outstanding amounts
allocated_negative_outstanding = 0
allocated_positive_outstanding = 0
# checking party type and payment type
if (self.payment_type == "Receive" and self.party_type == "Customer") or (
self.payment_type == "Pay" and self.party_type in ("Supplier", "Employee")
):
if total_positive_outstanding_including_order > paid_amount:
remaining_outstanding = flt(
total_positive_outstanding_including_order - paid_amount, precision
)
allocated_negative_outstanding = min(remaining_outstanding, total_negative_outstanding)
allocated_positive_outstanding = paid_amount + allocated_negative_outstanding
elif self.party_type in ("Supplier", "Employee"):
if paid_amount > total_negative_outstanding:
if total_negative_outstanding == 0:
frappe.msgprint(
_("Cannot {0} from {1} without any negative outstanding invoice").format(
self.payment_type,
self.party_type,
)
)
else:
frappe.msgprint(
_("Paid Amount cannot be greater than total negative outstanding amount {0}").format(
total_negative_outstanding
)
)
return
else:
allocated_positive_outstanding = flt(total_negative_outstanding - paid_amount, precision)
allocated_negative_outstanding = paid_amount + min(
total_positive_outstanding_including_order, allocated_positive_outstanding
)
# inner function to set `allocated_amount` to those row which have no PR
def _allocation_to_unset_pr_row(
row, outstanding_amount, allocated_positive_outstanding, allocated_negative_outstanding
):
if outstanding_amount > 0 and allocated_positive_outstanding >= 0:
row.allocated_amount = min(allocated_positive_outstanding, outstanding_amount)
allocated_positive_outstanding = flt(
allocated_positive_outstanding - row.allocated_amount, precision
)
elif outstanding_amount < 0 and allocated_negative_outstanding:
row.allocated_amount = min(allocated_negative_outstanding, abs(outstanding_amount)) * -1
allocated_negative_outstanding = flt(
allocated_negative_outstanding - abs(row.allocated_amount), precision
)
return allocated_positive_outstanding, allocated_negative_outstanding
# allocate amount based on `paid_amount` is changed or not
if not paid_amount_change:
for ref in self.references:
allocated_positive_outstanding, allocated_negative_outstanding = _allocation_to_unset_pr_row(
ref,
ref.outstanding_amount,
allocated_positive_outstanding,
allocated_negative_outstanding,
)
allocate_open_payment_requests_to_references(self.references, self.precision("paid_amount"))
else:
payment_request_outstanding_amounts = (
get_payment_request_outstanding_set_in_references(self.references) or {}
)
references_outstanding_amounts = get_references_outstanding_amount(self.references) or {}
remaining_references_allocated_amounts = references_outstanding_amounts.copy()
# Re allocate amount to those references which have PR set (Higher priority)
for ref in self.references:
if not ref.payment_request:
continue
# fetch outstanding_amount of `Reference` (Payment Term) and `Payment Request` to allocate new amount
key = (ref.reference_doctype, ref.reference_name, ref.get("payment_term"))
reference_outstanding_amount = references_outstanding_amounts[key]
pr_outstanding_amount = payment_request_outstanding_amounts[ref.payment_request]
if reference_outstanding_amount > 0 and allocated_positive_outstanding >= 0:
# allocate amount according to outstanding amounts
outstanding_amounts = (
allocated_positive_outstanding,
reference_outstanding_amount,
pr_outstanding_amount,
)
ref.allocated_amount = min(outstanding_amounts)
# update amounts to track allocation
allocated_amount = ref.allocated_amount
allocated_positive_outstanding = flt(
allocated_positive_outstanding - allocated_amount, precision
)
remaining_references_allocated_amounts[key] = flt(
remaining_references_allocated_amounts[key] - allocated_amount, precision
)
payment_request_outstanding_amounts[ref.payment_request] = flt(
payment_request_outstanding_amounts[ref.payment_request] - allocated_amount, precision
)
elif reference_outstanding_amount < 0 and allocated_negative_outstanding:
# allocate amount according to outstanding amounts
outstanding_amounts = (
allocated_negative_outstanding,
abs(reference_outstanding_amount),
pr_outstanding_amount,
)
ref.allocated_amount = min(outstanding_amounts) * -1
# update amounts to track allocation
allocated_amount = abs(ref.allocated_amount)
allocated_negative_outstanding = flt(
allocated_negative_outstanding - allocated_amount, precision
)
remaining_references_allocated_amounts[key] += allocated_amount # negative amount
payment_request_outstanding_amounts[ref.payment_request] = flt(
payment_request_outstanding_amounts[ref.payment_request] - allocated_amount, precision
)
# Re allocate amount to those references which have no PR (Lower priority)
for ref in self.references:
if ref.payment_request:
continue
key = (ref.reference_doctype, ref.reference_name, ref.get("payment_term"))
reference_outstanding_amount = remaining_references_allocated_amounts[key]
allocated_positive_outstanding, allocated_negative_outstanding = _allocation_to_unset_pr_row(
ref,
reference_outstanding_amount,
allocated_positive_outstanding,
allocated_negative_outstanding,
)
@frappe.whitelist()
def set_matched_payment_requests(self, matched_payment_requests):
"""
Set `Payment Request` against `Reference` based on `matched_payment_requests`.\n
:param matched_payment_requests: List of tuple of matched Payment Requests.
---
Example: [(reference_doctype, reference_name, allocated_amount, payment_request), ...]
"""
if not self.references or not matched_payment_requests:
return
if isinstance(matched_payment_requests, str):
matched_payment_requests = json.loads(matched_payment_requests)
# modify matched_payment_requests
# like (reference_doctype, reference_name, allocated_amount): payment_request
payment_requests = {}
for row in matched_payment_requests:
key = tuple(row[:3])
payment_requests[key] = row[3]
for ref in self.references:
if ref.payment_request:
continue
key = (ref.reference_doctype, ref.reference_name, ref.allocated_amount)
if key in payment_requests:
ref.payment_request = payment_requests[key]
del payment_requests[key] # to avoid duplicate allocation
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.
Example: [(reference_doctype, reference_name, allocated_amount, payment_request), ...]
"""
if not references:
return
# to fetch matched rows
refs = {
(row.reference_doctype, row.reference_name, row.allocated_amount)
for row in references
if row.reference_doctype and row.reference_name and row.allocated_amount
}
if not refs:
return
PR = frappe.qb.DocType("Payment Request")
# query to group by reference_doctype, reference_name, outstanding_amount
subquery = (
frappe.qb.from_(PR)
.select(
PR.reference_doctype,
PR.reference_name,
PR.outstanding_amount.as_("allocated_amount"),
PR.name.as_("payment_request"),
Count("*").as_("count"),
)
.where(Tuple(PR.reference_doctype, PR.reference_name, PR.outstanding_amount).isin(refs))
.where(PR.status != "Paid")
.where(PR.docstatus == 1)
.groupby(PR.reference_doctype, PR.reference_name, PR.outstanding_amount)
)
# query to fetch matched rows which are single
matched_prs = (
frappe.qb.from_(subquery)
.select(
subquery.reference_doctype,
subquery.reference_name,
subquery.allocated_amount,
subquery.payment_request,
)
.where(subquery.count == 1)
.run()
)
return matched_prs if matched_prs else None
def get_references_outstanding_amount(references=None):
"""
Fetch accurate outstanding amount of `References`.\n
- If `Payment Term` is set, then fetch outstanding amount from `Payment Schedule`.
- If `Payment Term` is not set, then fetch outstanding amount from `References` it self.
Example: {(reference_doctype, reference_name, payment_term): outstanding_amount, ...}
"""
if not references:
return
refs_with_payment_term = get_outstanding_of_references_with_payment_term(references) or {}
refs_without_payment_term = get_outstanding_of_references_with_no_payment_term(references) or {}
return {**refs_with_payment_term, **refs_without_payment_term}
def get_outstanding_of_references_with_payment_term(references=None):
"""
Fetch outstanding amount of `References` which have `Payment Term` set.\n
Example: {(reference_doctype, reference_name, payment_term): outstanding_amount, ...}
"""
if not references:
return
refs = {
(row.reference_doctype, row.reference_name, row.payment_term)
for row in references
if row.reference_doctype and row.reference_name and row.payment_term
}
if not refs:
return
PS = frappe.qb.DocType("Payment Schedule")
response = (
frappe.qb.from_(PS)
.select(PS.parenttype, PS.parent, PS.payment_term, PS.outstanding)
.where(Tuple(PS.parenttype, PS.parent, PS.payment_term).isin(refs))
).run(as_dict=True)
if not response:
return
return {(row.parenttype, row.parent, row.payment_term): row.outstanding for row in response}
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.
Note: `None` is used for allocation of `Payment Request`
Example: {(reference_doctype, reference_name, None): outstanding_amount, ...}
"""
if not references:
return
outstanding_amounts = {}
for ref in references:
if ref.payment_term:
continue
key = (ref.reference_doctype, ref.reference_name, None)
if key not in outstanding_amounts:
outstanding_amounts[key] = ref.outstanding_amount
return outstanding_amounts
def get_payment_request_outstanding_set_in_references(references=None):
"""
Fetch outstanding amount of `Payment Request` which are set in `References`.\n
Example: {payment_request: outstanding_amount, ...}
"""
if not references:
return
referenced_payment_requests = {row.payment_request for row in references if row.payment_request}
if not referenced_payment_requests:
return
PR = frappe.qb.DocType("Payment Request")
response = (
frappe.qb.from_(PR)
.select(PR.name, PR.outstanding_amount)
.where(PR.name.isin(referenced_payment_requests))
).run()
return dict(response) if response else None
def validate_inclusive_tax(tax, doc):
def _on_previous_row_error(row_range):
@@ -2046,7 +2566,9 @@ def get_party_details(company, party_type, party, date, cost_center=None):
account_balance = get_balance_on(party_account, date, cost_center=cost_center)
_party_name = "title" if party_type == "Shareholder" else party_type.lower() + "_name"
party_name = frappe.db.get_value(party_type, party, _party_name)
party_balance = get_balance_on(party_type=party_type, party=party, cost_center=cost_center)
party_balance = get_balance_on(
party_type=party_type, party=party, company=company, cost_center=cost_center
)
if party_type in ["Customer", "Supplier"]:
party_bank_account = get_party_bank_account(party_type, party)
bank_account = get_default_company_bank_account(company, party_type, party)
@@ -2224,6 +2746,8 @@ def get_payment_entry(
party_type=None,
payment_type=None,
reference_date=None,
ignore_permissions=False,
created_from_payment_request=False,
):
doc = frappe.get_doc(dt, dn)
over_billing_allowance = frappe.db.get_single_value("Accounts Settings", "over_billing_allowance")
@@ -2359,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"):
@@ -2371,11 +2892,183 @@ 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:
allocate_open_payment_requests_to_references(pe.references, pe.precision("paid_amount"))
return pe
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
Example: {("Sales Invoice", "SINV-00001"): {"PREQ-00001": 1000, "PREQ-00002": 2000}}
"""
if not references:
return
refs = {
(row.reference_doctype, row.reference_name)
for row in references
if row.reference_doctype and row.reference_name and row.allocated_amount
}
if not refs:
return
PR = frappe.qb.DocType("Payment Request")
response = (
frappe.qb.from_(PR)
.select(PR.name, PR.reference_doctype, PR.reference_name, PR.outstanding_amount)
.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)
if not response:
return
reference_payment_requests = {}
for row in response:
key = (row.reference_doctype, row.reference_name)
if key not in reference_payment_requests:
reference_payment_requests[key] = {row.name: row.outstanding_amount}
else:
reference_payment_requests[key][row.name] = row.outstanding_amount
return reference_payment_requests
def allocate_open_payment_requests_to_references(references=None, precision=None):
"""
Allocate unpaid Payment Requests to the references. \n
---
- Allocation based on below factors
- Reference Allocated Amount
- Reference Outstanding Amount (With Payment Terms or without Payment Terms)
- Reference Payment Request's outstanding amount
---
- Allocation based on below scenarios
- Reference's Allocated Amount == Payment Request's Outstanding Amount
- Allocate the Payment Request to the reference
- This PR will not be allocated further
- Reference's Allocated Amount < Payment Request's Outstanding Amount
- Allocate the Payment Request to the reference
- Reduce the PR's outstanding amount by the allocated amount
- This PR can be allocated further
- Reference's Allocated Amount > Payment Request's Outstanding Amount
- Allocate the Payment Request to the reference
- Reduce Allocated Amount of the reference by the PR's outstanding amount
- Create a new row for the remaining amount until the Allocated Amount is 0
- Allocate PR if available
---
- Note:
- Priority is given to the first Payment Request of respective references.
- Single Reference can have multiple rows.
- With Payment Terms or without Payment Terms
- With Payment Request or without Payment Request
"""
if not references:
return
# get all unpaid payment requests for the references
references_open_payment_requests = get_open_payment_requests_for_references(references)
if not references_open_payment_requests:
return
if not precision:
precision = references[0].precision("allocated_amount")
# to manage new rows
row_number = 1
MOVE_TO_NEXT_ROW = 1
TO_SKIP_NEW_ROW = 2
while row_number <= len(references):
row = references[row_number - 1]
reference_key = (row.reference_doctype, row.reference_name)
# update the idx to maintain the order
row.idx = row_number
# unpaid payment requests for the reference
reference_payment_requests = references_open_payment_requests.get(reference_key)
if not reference_payment_requests:
row_number += MOVE_TO_NEXT_ROW # to move to next reference row
continue
# get the first payment request and its outstanding amount
payment_request, pr_outstanding_amount = next(iter(reference_payment_requests.items()))
allocated_amount = row.allocated_amount
# allocate the payment request to the reference and PR's outstanding amount
row.payment_request = payment_request
if pr_outstanding_amount == allocated_amount:
del reference_payment_requests[payment_request]
row_number += MOVE_TO_NEXT_ROW
elif pr_outstanding_amount > allocated_amount:
# reduce the outstanding amount of the payment request
reference_payment_requests[payment_request] -= allocated_amount
row_number += MOVE_TO_NEXT_ROW
else:
# split the reference row to allocate the remaining amount
del reference_payment_requests[payment_request]
row.allocated_amount = pr_outstanding_amount
allocated_amount = flt(allocated_amount - pr_outstanding_amount, precision)
# set the remaining amount to the next row
while allocated_amount:
# create a new row for the remaining amount
new_row = frappe.copy_doc(row)
references.insert(row_number, new_row)
# get the first payment request and its outstanding amount
payment_request, pr_outstanding_amount = next(
iter(reference_payment_requests.items()), (None, None)
)
# update new row
new_row.idx = row_number + 1
new_row.payment_request = payment_request
new_row.allocated_amount = min(
pr_outstanding_amount if pr_outstanding_amount else allocated_amount, allocated_amount
)
if not payment_request or not pr_outstanding_amount:
row_number += TO_SKIP_NEW_ROW
break
elif pr_outstanding_amount == allocated_amount:
del reference_payment_requests[payment_request]
row_number += TO_SKIP_NEW_ROW
break
elif pr_outstanding_amount > allocated_amount:
reference_payment_requests[payment_request] -= allocated_amount
row_number += TO_SKIP_NEW_ROW
break
else:
allocated_amount = flt(allocated_amount - pr_outstanding_amount, precision)
del reference_payment_requests[payment_request]
row_number += MOVE_TO_NEXT_ROW
def update_accounting_dimensions(pe, doc):
"""
Updates accounting dimensions in Payment Entry based on the accounting dimensions in the reference document
@@ -2547,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
@@ -1791,6 +1818,79 @@ class TestPaymentEntry(FrappeTestCase):
# 'Is Opening' should always be 'No' for normal advance payments
self.assertEqual(gl_with_opening_set, [])
@change_settings("Accounts Settings", {"delete_linked_ledger_entries": 1})
def test_delete_linked_exchange_gain_loss_journal(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,
)
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 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 sales invoice
si = create_sales_invoice(
customer=customer,
currency="USD",
conversion_rate=83.970000000,
debit_to=debtors,
do_not_save=1,
)
si.party_account_currency = "USD"
si.save()
si.submit()
# create a payment entry for the invoice
pe = get_payment_entry("Sales Invoice", si.name)
pe.reference_no = "1"
pe.reference_date = frappe.utils.nowdate()
pe.paid_amount = 100
pe.source_exchange_rate = 90
pe.append(
"deductions",
{
"account": "_Test Exchange Gain/Loss - _TC",
"cost_center": "_Test Cost Center - _TC",
"amount": 2710,
},
)
pe.save()
pe.submit()
# check creation of journal entry
jv = frappe.get_all(
"Journal Entry Account",
{"reference_type": pe.doctype, "reference_name": pe.name, "docstatus": 1},
pluck="parent",
)
self.assertTrue(jv)
# check cancellation of payment entry and journal entry
pe.cancel()
self.assertTrue(pe.docstatus == 2)
self.assertTrue(frappe.db.get_value("Journal Entry", {"name": jv[0]}, "docstatus") == 2)
# check deletion of payment entry and journal entry
pe.delete()
self.assertRaises(frappe.DoesNotExistError, frappe.get_doc, pe.doctype, pe.name)
self.assertRaises(frappe.DoesNotExistError, frappe.get_doc, "Journal Entry", jv[0])
def create_payment_entry(**args):
payment_entry = frappe.new_doc("Payment Entry")

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

@@ -10,6 +10,7 @@
"due_date",
"bill_no",
"payment_term",
"payment_term_outstanding",
"account_type",
"payment_type",
"column_break_4",
@@ -18,7 +19,9 @@
"allocated_amount",
"exchange_rate",
"exchange_gain_loss",
"account"
"account",
"payment_request",
"payment_request_outstanding"
],
"fields": [
{
@@ -120,12 +123,33 @@
"fieldname": "payment_type",
"fieldtype": "Data",
"label": "Payment Type"
},
{
"fieldname": "payment_request",
"fieldtype": "Link",
"label": "Payment Request",
"options": "Payment Request"
},
{
"depends_on": "eval: doc.payment_term",
"fieldname": "payment_term_outstanding",
"fieldtype": "Float",
"label": "Payment Term Outstanding",
"read_only": 1
},
{
"depends_on": "eval: doc.payment_request && doc.payment_request_outstanding",
"fieldname": "payment_request_outstanding",
"fieldtype": "Float",
"is_virtual": 1,
"label": "Payment Request Outstanding",
"read_only": 1
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2024-04-05 09:44:08.310593",
"modified": "2024-09-16 18:11:50.019343",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Entry Reference",

View File

@@ -1,7 +1,7 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import frappe
from frappe.model.document import Document
@@ -25,11 +25,19 @@ class PaymentEntryReference(Document):
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
payment_request: DF.Link | None
payment_request_outstanding: DF.Float
payment_term: DF.Link | None
payment_term_outstanding: DF.Float
payment_type: DF.Data | None
reference_doctype: DF.Link
reference_name: DF.DynamicLink
total_amount: DF.Float
# end: auto-generated types
pass
@property
def payment_request_outstanding(self):
if not self.payment_request:
return
return frappe.db.get_value("Payment Request", self.payment_request, "outstanding_amount")

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

@@ -5,7 +5,7 @@
import frappe
from frappe import qb
from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import add_days, flt, nowdate
from frappe.utils import add_days, add_years, flt, getdate, nowdate, today
from erpnext import get_default_cost_center
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
@@ -13,6 +13,7 @@ from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_pay
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.party import get_party_account
from erpnext.accounts.utils import get_fiscal_year
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
from erpnext.stock.doctype.item.test_item import create_item
@@ -631,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"
@@ -953,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(
@@ -1845,6 +1976,78 @@ class TestPaymentReconciliation(FrappeTestCase):
self.assertEqual(len(pr.invoices), 1)
self.assertEqual(len(pr.payments), 1)
def test_reconciliation_on_closed_period_payment(self):
# create backdated fiscal year
first_fy_start_date = frappe.db.get_value("Fiscal Year", {"disabled": 0}, "min(year_start_date)")
prev_fy_start_date = add_years(first_fy_start_date, -1)
prev_fy_end_date = add_days(first_fy_start_date, -1)
create_fiscal_year(
company=self.company, year_start_date=prev_fy_start_date, year_end_date=prev_fy_end_date
)
# make journal entry for previous year
je_1 = frappe.new_doc("Journal Entry")
je_1.posting_date = add_days(prev_fy_start_date, 20)
je_1.company = self.company
je_1.user_remark = "test"
je_1.set(
"accounts",
[
{
"account": self.debit_to,
"cost_center": self.cost_center,
"party_type": "Customer",
"party": self.customer,
"debit_in_account_currency": 0,
"credit_in_account_currency": 1000,
},
{
"account": self.bank,
"cost_center": self.sub_cc.name,
"credit_in_account_currency": 0,
"debit_in_account_currency": 500,
},
{
"account": self.cash,
"cost_center": self.sub_cc.name,
"credit_in_account_currency": 0,
"debit_in_account_currency": 500,
},
],
)
je_1.submit()
# make period closing voucher
pcv = make_period_closing_voucher(
company=self.company, cost_center=self.cost_center, posting_date=prev_fy_end_date
)
pcv.reload()
# check if period closing voucher is completed
self.assertEqual(pcv.gle_processing_status, "Completed")
# make journal entry for active year
je_2 = self.create_journal_entry(
acc1=self.debit_to, acc2=self.income_account, amount=1000, posting_date=today()
)
je_2.accounts[0].party_type = "Customer"
je_2.accounts[0].party = self.customer
je_2.submit()
# process reconciliation on closed period payment
pr = self.create_payment_reconciliation(party_is_customer=True)
pr.from_invoice_date = pr.to_invoice_date = pr.from_payment_date = pr.to_payment_date = None
pr.get_unreconciled_entries()
invoices = [invoice.as_dict() for invoice in pr.invoices]
payments = [payment.as_dict() for payment in pr.payments]
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
pr.reconcile()
je_1.reload()
je_2.reload()
# check whether the payment reconciliation is done on the closed period
self.assertEqual(pr.get("invoices"), [])
self.assertEqual(pr.get("payments"), [])
def make_customer(customer_name, currency=None):
if not frappe.db.exists("Customer", customer_name):
@@ -1872,3 +2075,63 @@ def make_supplier(supplier_name, currency=None):
return supplier.name
else:
return supplier_name
def create_fiscal_year(company, year_start_date, year_end_date):
fy_docname = frappe.db.exists(
"Fiscal Year", {"year_start_date": year_start_date, "year_end_date": year_end_date}
)
if not fy_docname:
fy_doc = frappe.get_doc(
{
"doctype": "Fiscal Year",
"year": f"{getdate(year_start_date).year}-{getdate(year_end_date).year}",
"year_start_date": year_start_date,
"year_end_date": year_end_date,
"companies": [{"company": company}],
}
).save()
return fy_doc
else:
fy_doc = frappe.get_doc("Fiscal Year", fy_docname)
if not frappe.db.exists("Fiscal Year Company", {"parent": fy_docname, "company": company}):
fy_doc.append("companies", {"company": company})
fy_doc.save()
return fy_doc
def make_period_closing_voucher(company, cost_center, posting_date=None, submit=True):
from erpnext.accounts.doctype.account.test_account import create_account
parent_account = frappe.db.get_value(
"Account", {"company": company, "account_name": "Current Liabilities", "is_group": 1}, "name"
)
surplus_account = create_account(
account_name="Reserve and Surplus",
is_group=0,
company=company,
root_type="Liability",
report_type="Balance Sheet",
account_currency="INR",
parent_account=parent_account,
doctype="Account",
)
fy = get_fiscal_year(posting_date, company=company)
pcv = frappe.get_doc(
{
"doctype": "Period Closing Voucher",
"transaction_date": posting_date or today(),
"period_start_date": fy[1],
"period_end_date": fy[2],
"company": company,
"fiscal_year": fy[0],
"cost_center": cost_center,
"closing_account_head": surplus_account,
"remarks": "test",
}
)
pcv.insert()
if submit:
pcv.submit()
return pcv

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

@@ -48,8 +48,8 @@ frappe.ui.form.on("Payment Request", "refresh", function (frm) {
}
if (
(!frm.doc.payment_gateway_account || frm.doc.payment_request_type == "Outward") &&
frm.doc.status == "Initiated"
frm.doc.payment_request_type == "Outward" &&
["Initiated", "Partially Paid"].includes(frm.doc.status)
) {
frm.add_custom_button(__("Create Payment Entry"), function () {
frappe.call({

View File

@@ -14,14 +14,17 @@
"party_details",
"party_type",
"party",
"party_name",
"column_break_4",
"reference_doctype",
"reference_name",
"transaction_details",
"grand_total",
"currency",
"is_a_subscription",
"column_break_18",
"currency",
"outstanding_amount",
"party_account_currency",
"subscription_section",
"subscription_plans",
"bank_account_details",
@@ -69,6 +72,7 @@
{
"fieldname": "transaction_date",
"fieldtype": "Date",
"in_preview": 1,
"label": "Transaction Date"
},
{
@@ -133,7 +137,8 @@
"no_copy": 1,
"options": "reference_doctype",
"print_hide": 1,
"read_only": 1
"read_only": 1,
"search_index": 1
},
{
"fieldname": "transaction_details",
@@ -141,12 +146,14 @@
"label": "Transaction Details"
},
{
"description": "Amount in customer's currency",
"description": "Amount in transaction currency",
"fieldname": "grand_total",
"fieldtype": "Currency",
"in_preview": 1,
"label": "Amount",
"non_negative": 1,
"options": "currency"
"options": "currency",
"reqd": 1
},
{
"default": "0",
@@ -392,19 +399,43 @@
"print_hide": 1,
"read_only": 1
},
{
"depends_on": "eval: doc.docstatus === 1",
"description": "Amount in party's bank account currency",
"fieldname": "outstanding_amount",
"fieldtype": "Currency",
"in_preview": 1,
"label": "Outstanding Amount",
"non_negative": 1,
"options": "party_account_currency",
"read_only": 1
},
{
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"options": "Company",
"read_only": 1
},
{
"fieldname": "party_account_currency",
"fieldtype": "Link",
"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-08-07 16:39:54.288002",
"modified": "2024-10-23 12:23:40.117336",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Request",
@@ -439,7 +470,8 @@
"write": 1
}
],
"show_preview_popup": 1,
"sort_field": "modified",
"sort_order": "DESC",
"states": []
}
}

View File

@@ -1,11 +1,13 @@
import json
import frappe
from frappe import _
from frappe import _, qb
from frappe.model.document import Document
from frappe.query_builder.functions import Abs, Sum
from frappe.utils import flt, nowdate
from frappe.utils.background_jobs import enqueue
from erpnext import get_company_currency
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_accounting_dimensions,
)
@@ -18,6 +20,15 @@ from erpnext.accounts.party import get_party_account, get_party_bank_account
from erpnext.accounts.utils import get_account_currency, get_currency_precision
from erpnext.utilities import payment_app_import_guard
ALLOWED_DOCTYPES_FOR_PAYMENT_REQUEST = [
"Sales Order",
"Purchase Order",
"Sales Invoice",
"Purchase Invoice",
"POS Invoice",
"Fees",
]
def _get_payment_gateway_controller(*args, **kwargs):
with payment_app_import_guard():
@@ -45,6 +56,7 @@ class PaymentRequest(Document):
bank_account: DF.Link | None
bank_account_no: DF.ReadOnly | None
branch_code: DF.ReadOnly | None
company: DF.Link | None
cost_center: DF.Link | None
currency: DF.Link | None
email_to: DF.Data | None
@@ -56,16 +68,19 @@ class PaymentRequest(Document):
mode_of_payment: DF.Link | None
mute_email: DF.Check
naming_series: DF.Literal["ACC-PRQ-.YYYY.-"]
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"]
payment_channel: DF.Literal["", "Email", "Phone", "Other"]
payment_gateway: DF.ReadOnly | None
payment_gateway_account: DF.Link | None
payment_order: DF.Link | None
payment_request_type: DF.Literal["Outward", "Inward"]
payment_url: DF.Data | None
print_format: DF.Literal
print_format: DF.Literal[None]
project: DF.Link | None
reference_doctype: DF.Link | None
reference_name: DF.DynamicLink | None
@@ -84,7 +99,6 @@ class PaymentRequest(Document):
subscription_plans: DF.Table[SubscriptionPlanDetail]
swift_number: DF.ReadOnly | None
transaction_date: DF.Date | None
company: DF.Link | None
# end: auto-generated types
def validate(self):
@@ -100,6 +114,12 @@ class PaymentRequest(Document):
frappe.throw(_("To create a Payment Request reference document is required"))
def validate_payment_request_amount(self):
if self.grand_total == 0:
frappe.throw(
_("{0} cannot be zero").format(self.get_label_from_fieldname("grand_total")),
title=_("Invalid Amount"),
)
existing_payment_request_amount = flt(
get_existing_payment_request_amount(self.reference_doctype, self.reference_name)
)
@@ -147,6 +167,28 @@ class PaymentRequest(Document):
).format(self.grand_total, amount)
)
def before_submit(self):
if (
self.currency != self.party_account_currency
and self.party_account_currency == get_company_currency(self.company)
):
# set outstanding amount in party account currency
invoice = frappe.get_value(
self.reference_doctype,
self.reference_name,
["rounded_total", "grand_total", "base_rounded_total", "base_grand_total"],
as_dict=1,
)
grand_total = invoice.get("rounded_total") or invoice.get("grand_total")
base_grand_total = invoice.get("base_rounded_total") or invoice.get("base_grand_total")
self.outstanding_amount = flt(
self.grand_total / grand_total * base_grand_total,
self.precision("outstanding_amount"),
)
else:
self.outstanding_amount = self.grand_total
def on_submit(self):
if self.payment_request_type == "Outward":
self.db_set("status", "Initiated")
@@ -262,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,
}
@@ -275,7 +317,7 @@ class PaymentRequest(Document):
def set_as_paid(self):
if self.payment_channel == "Phone":
self.db_set("status", "Paid")
self.db_set({"status": "Paid", "outstanding_amount": 0})
else:
payment_entry = self.create_payment_entry()
@@ -296,26 +338,32 @@ class PaymentRequest(Document):
else:
party_account = get_party_account("Customer", ref_doc.get("customer"), ref_doc.company)
party_account_currency = ref_doc.get("party_account_currency") or get_account_currency(party_account)
party_account_currency = (
self.get("party_account_currency")
or ref_doc.get("party_account_currency")
or get_account_currency(party_account)
)
party_amount = bank_amount = self.outstanding_amount
bank_amount = self.grand_total
if party_account_currency == ref_doc.company_currency and party_account_currency != self.currency:
party_amount = ref_doc.get("base_rounded_total") or ref_doc.get("base_grand_total")
else:
party_amount = self.grand_total
exchange_rate = ref_doc.get("conversion_rate")
bank_amount = flt(self.outstanding_amount / exchange_rate, self.precision("grand_total"))
# outstanding amount is already in Part's account currency
payment_entry = get_payment_entry(
self.reference_doctype,
self.reference_name,
party_amount=party_amount,
bank_account=self.payment_account,
bank_amount=bank_amount,
created_from_payment_request=True,
)
payment_entry.update(
{
"mode_of_payment": self.mode_of_payment,
"reference_no": self.name,
"reference_no": self.name, # to prevent validation error
"reference_date": nowdate(),
"remarks": "Payment Entry against {} {} via Payment Request {}".format(
self.reference_doctype, self.reference_name, self.name
@@ -323,6 +371,9 @@ class PaymentRequest(Document):
}
)
# Allocate payment_request for each reference in payment_entry (Payment Term can splits the row)
self._allocate_payment_request_to_pe_references(references=payment_entry.references)
# Update dimensions
payment_entry.update(
{
@@ -331,14 +382,6 @@ class PaymentRequest(Document):
}
)
if party_account_currency == ref_doc.company_currency and party_account_currency != self.currency:
amount = payment_entry.base_paid_amount
else:
amount = self.grand_total
payment_entry.received_amount = amount
payment_entry.get("references")[0].allocated_amount = amount
# Update 'Paid Amount' on Forex transactions
if self.currency != ref_doc.company_currency:
if (
@@ -429,6 +472,62 @@ class PaymentRequest(Document):
return create_stripe_subscription(gateway_controller, data)
def _allocate_payment_request_to_pe_references(self, references):
"""
Allocate the Payment Request to the Payment Entry references based on\n
- Allocated Amount.
- Outstanding Amount of Payment Request.\n
Payment Request is doc itself and references are the rows of Payment Entry.
"""
if len(references) == 1:
references[0].payment_request = self.name
return
precision = references[0].precision("allocated_amount")
outstanding_amount = self.outstanding_amount
# to manage rows
row_number = 1
MOVE_TO_NEXT_ROW = 1
TO_SKIP_NEW_ROW = 2
NEW_ROW_ADDED = False
while row_number <= len(references):
row = references[row_number - 1]
# update the idx to maintain the order
row.idx = row_number
if outstanding_amount == 0:
if not NEW_ROW_ADDED:
break
row_number += MOVE_TO_NEXT_ROW
continue
# allocate the payment request to the row
row.payment_request = self.name
if row.allocated_amount <= outstanding_amount:
outstanding_amount = flt(outstanding_amount - row.allocated_amount, precision)
row_number += MOVE_TO_NEXT_ROW
else:
remaining_allocated_amount = flt(row.allocated_amount - outstanding_amount, precision)
row.allocated_amount = outstanding_amount
outstanding_amount = 0
# create a new row without PR for remaining unallocated amount
new_row = frappe.copy_doc(row)
references.insert(row_number, new_row)
# update new row
new_row.idx = row_number + 1
new_row.payment_request = None
new_row.allocated_amount = remaining_allocated_amount
NEW_ROW_ADDED = True
row_number += TO_SKIP_NEW_ROW
@frappe.whitelist(allow_guest=True)
def make_payment_request(**args):
@@ -436,6 +535,9 @@ def make_payment_request(**args):
args = frappe._dict(args)
if args.dt not in ALLOWED_DOCTYPES_FOR_PAYMENT_REQUEST:
frappe.throw(_("Payment Requests cannot be created against: {0}").format(frappe.bold(args.dt)))
ref_doc = frappe.get_doc(args.dt, args.dn)
gateway_account = get_gateway_details(args) or frappe._dict()
@@ -459,10 +561,38 @@ def make_payment_request(**args):
{"reference_doctype": args.dt, "reference_name": args.dn, "docstatus": 0},
)
existing_payment_request_amount = get_existing_payment_request_amount(args.dt, args.dn)
# fetches existing payment request `grand_total` amount
existing_payment_request_amount = get_existing_payment_request_amount(ref_doc.doctype, ref_doc.name)
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:
grand_total -= 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(
@@ -477,6 +607,13 @@ def make_payment_request(**args):
"Outward" if args.get("dt") in ["Purchase Order", "Purchase Invoice"] else "Inward"
)
party_type = args.get("party_type") or "Customer"
party_account_currency = ref_doc.get("party_account_currency")
if not party_account_currency:
party_account = get_party_account(party_type, ref_doc.get(party_type.lower()), ref_doc.company)
party_account_currency = get_account_currency(party_account)
pr.update(
{
"payment_gateway_account": gateway_account.get("name"),
@@ -485,6 +622,7 @@ def make_payment_request(**args):
"payment_channel": gateway_account.get("payment_channel"),
"payment_request_type": args.get("payment_request_type"),
"currency": ref_doc.currency,
"party_account_currency": party_account_currency,
"grand_total": grand_total,
"mode_of_payment": args.mode_of_payment,
"email_to": args.recipient_id or ref_doc.owner,
@@ -493,9 +631,10 @@ def make_payment_request(**args):
"reference_doctype": args.dt,
"reference_name": args.dn,
"company": ref_doc.get("company"),
"party_type": args.get("party_type") or "Customer",
"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"),
}
)
@@ -539,9 +678,11 @@ def get_amount(ref_doc, payment_account=None):
elif dt in ["Sales Invoice", "Purchase Invoice"]:
if not ref_doc.get("is_pos"):
if ref_doc.party_account_currency == ref_doc.currency:
grand_total = flt(ref_doc.grand_total)
grand_total = flt(ref_doc.rounded_total or ref_doc.grand_total)
else:
grand_total = flt(ref_doc.base_grand_total) / ref_doc.conversion_rate
grand_total = flt(
flt(ref_doc.base_rounded_total or ref_doc.base_grand_total) / ref_doc.conversion_rate
)
elif dt == "Sales Invoice":
for pay in ref_doc.payments:
if pay.type == "Phone" and pay.account == payment_account:
@@ -561,26 +702,89 @@ 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:
"""
Get the existing payment request which are unpaid or partially paid for payment channel other than Phone
and get the summation of existing paid payment request for Phone payment channel.
Return the total amount of Payment Requests against a reference document.
"""
existing_payment_request_amount = frappe.db.sql(
"""
select sum(grand_total)
from `tabPayment Request`
where
reference_doctype = %s
and reference_name = %s
and docstatus = 1
and (status != 'Paid'
or (payment_channel = 'Phone'
and status = 'Paid'))
""",
(ref_dt, ref_dn),
PR = frappe.qb.DocType("Payment Request")
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)
)
return flt(existing_payment_request_amount[0][0]) if existing_payment_request_amount else 0
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
def get_gateway_details(args): # nosemgrep
@@ -627,41 +831,66 @@ def make_payment_entry(docname):
return doc.create_payment_entry(submit=False).as_dict()
def update_payment_req_status(doc, method):
from erpnext.accounts.doctype.payment_entry.payment_entry import get_reference_details
def update_payment_requests_as_per_pe_references(references=None, cancel=False):
"""
Update Payment Request's `Status` and `Outstanding Amount` based on Payment Entry Reference's `Allocated Amount`.
"""
if not references:
return
for ref in doc.references:
payment_request_name = frappe.db.get_value(
"Payment Request",
{
"reference_doctype": ref.reference_doctype,
"reference_name": ref.reference_name,
"docstatus": 1,
},
precision = references[0].precision("allocated_amount")
referenced_payment_requests = frappe.get_all(
"Payment Request",
filters={"name": ["in", {row.payment_request for row in references if row.payment_request}]},
fields=[
"name",
"grand_total",
"outstanding_amount",
"payment_request_type",
],
)
referenced_payment_requests = {pr.name: pr for pr in referenced_payment_requests}
for ref in references:
if not ref.payment_request:
continue
payment_request = referenced_payment_requests[ref.payment_request]
pr_outstanding = payment_request["outstanding_amount"]
# update outstanding amount
new_outstanding_amount = flt(
pr_outstanding + ref.allocated_amount if cancel else pr_outstanding - ref.allocated_amount,
precision,
)
if payment_request_name:
ref_details = get_reference_details(
ref.reference_doctype,
ref.reference_name,
doc.party_account_currency,
doc.party_type,
doc.party,
# to handle same payment request for the multiple allocations
payment_request["outstanding_amount"] = new_outstanding_amount
if not cancel and new_outstanding_amount < 0:
frappe.throw(
msg=_(
"The allocated amount is greater than the outstanding amount of Payment Request {0}"
).format(ref.payment_request),
title=_("Invalid Allocated Amount"),
)
pay_req_doc = frappe.get_doc("Payment Request", payment_request_name)
status = pay_req_doc.status
if status != "Paid" and not ref_details.outstanding_amount:
status = "Paid"
elif status != "Partially Paid" and ref_details.outstanding_amount != ref_details.total_amount:
status = "Partially Paid"
elif ref_details.outstanding_amount == ref_details.total_amount:
if pay_req_doc.payment_request_type == "Outward":
status = "Initiated"
elif pay_req_doc.payment_request_type == "Inward":
status = "Requested"
# update status
if new_outstanding_amount == payment_request["grand_total"]:
status = "Initiated" if payment_request["payment_request_type"] == "Outward" else "Requested"
elif new_outstanding_amount == 0:
status = "Paid"
elif new_outstanding_amount > 0:
status = "Partially Paid"
pay_req_doc.db_set("status", status)
# update database
frappe.db.set_value(
"Payment Request",
ref.payment_request,
{"outstanding_amount": new_outstanding_amount, "status": status},
)
def get_dummy_message(doc):
@@ -745,3 +974,45 @@ def validate_payment(doc, method=None):
doc.reference_docname
)
)
@frappe.whitelist()
def get_open_payment_requests_query(doctype, txt, searchfield, start, page_len, filters):
# permission checks in `get_list()`
filters = frappe._dict(filters)
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=filters,
fields=["name", "grand_total", "outstanding_amount"],
order_by="transaction_date ASC,creation ASC",
)
return [
(
pr.name,
_("<strong>Grand Total:</strong> {0}").format(pr.grand_total),
_("<strong>Outstanding Amount:</strong> {0}").format(pr.outstanding_amount),
)
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

@@ -1,11 +1,14 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
import re
import unittest
import frappe
from frappe.tests.utils import FrappeTestCase
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
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
@@ -15,6 +18,7 @@ from erpnext.setup.utils import get_exchange_rate
test_dependencies = ["Currency Exchange", "Journal Entry", "Contact", "Address"]
payment_gateway = {"doctype": "Payment Gateway", "gateway": "_Test Gateway"}
payment_method = [
@@ -278,3 +282,291 @@ class TestPaymentRequest(FrappeTestCase):
self.assertEqual(pe.paid_amount, 800)
self.assertEqual(pe.base_received_amount, 800)
self.assertEqual(pe.received_amount, 10)
def test_multiple_payment_if_partially_paid_for_same_currency(self):
so = make_sales_order(currency="INR", qty=1, rate=1000)
pr = make_payment_request(
dt="Sales Order",
dn=so.name,
mute_email=1,
submit_doc=1,
return_doc=1,
)
self.assertEqual(pr.grand_total, 1000)
self.assertEqual(pr.outstanding_amount, pr.grand_total)
self.assertEqual(pr.party_account_currency, pr.currency) # INR
so.load_from_db()
# to make partial payment
pe = pr.create_payment_entry(submit=False)
pe.paid_amount = 200
pe.references[0].allocated_amount = 200
pe.submit()
self.assertEqual(pe.references[0].payment_request, pr.name)
so.load_from_db()
pr.load_from_db()
self.assertEqual(pr.status, "Partially Paid")
self.assertEqual(pr.outstanding_amount, 800)
self.assertEqual(pr.grand_total, 1000)
# complete payment
pe = pr.create_payment_entry()
self.assertEqual(pe.paid_amount, 800) # paid amount set from pr's outstanding amount
self.assertEqual(pe.references[0].allocated_amount, 800)
self.assertEqual(pe.references[0].outstanding_amount, 800) # for Orders it is not zero
self.assertEqual(pe.references[0].payment_request, pr.name)
so.load_from_db()
pr.load_from_db()
self.assertEqual(pr.status, "Paid")
self.assertEqual(pr.outstanding_amount, 0)
self.assertEqual(pr.grand_total, 1000)
# creating a more payment Request must not allowed
self.assertRaisesRegex(
frappe.exceptions.ValidationError,
re.compile(r"Payment Request is already created"),
make_payment_request,
dt="Sales Order",
dn=so.name,
mute_email=1,
submit_doc=1,
return_doc=1,
)
@change_settings("Accounts Settings", {"allow_multi_currency_invoices_against_single_party_account": 1})
def test_multiple_payment_if_partially_paid_for_multi_currency(self):
pi = make_purchase_invoice(currency="USD", conversion_rate=50, qty=1, rate=100, do_not_save=1)
pi.credit_to = "Creditors - _TC"
pi.submit()
pr = make_payment_request(
dt="Purchase Invoice",
dn=pi.name,
mute_email=1,
submit_doc=1,
return_doc=1,
)
# 100 USD -> 5000 INR
self.assertEqual(pr.grand_total, 100)
self.assertEqual(pr.outstanding_amount, 5000)
self.assertEqual(pr.currency, "USD")
self.assertEqual(pr.party_account_currency, "INR")
self.assertEqual(pr.status, "Initiated")
# to make partial payment
pe = pr.create_payment_entry(submit=False)
pe.paid_amount = 2000
pe.references[0].allocated_amount = 2000
pe.submit()
self.assertEqual(pe.references[0].payment_request, pr.name)
pr.load_from_db()
self.assertEqual(pr.status, "Partially Paid")
self.assertEqual(pr.outstanding_amount, 3000)
self.assertEqual(pr.grand_total, 100)
# complete payment
pe = pr.create_payment_entry()
self.assertEqual(pe.paid_amount, 3000) # paid amount set from pr's outstanding amount
self.assertEqual(pe.references[0].allocated_amount, 3000)
self.assertEqual(pe.references[0].outstanding_amount, 0) # for Invoices it will zero
self.assertEqual(pe.references[0].payment_request, pr.name)
pr.load_from_db()
self.assertEqual(pr.status, "Paid")
self.assertEqual(pr.outstanding_amount, 0)
self.assertEqual(pr.grand_total, 100)
# creating a more payment Request must not allowed
self.assertRaisesRegex(
frappe.exceptions.ValidationError,
re.compile(r"Payment Request is already created"),
make_payment_request,
dt="Purchase Invoice",
dn=pi.name,
mute_email=1,
submit_doc=1,
return_doc=1,
)
def test_single_payment_with_payment_term_for_same_currency(self):
create_payment_terms_template()
po = create_purchase_order(do_not_save=1, currency="INR", qty=1, rate=20000)
po.payment_terms_template = "Test Receivable Template" # 84.746 and 15.254
po.save()
po.submit()
pr = make_payment_request(
dt="Purchase Order",
dn=po.name,
mute_email=1,
submit_doc=1,
return_doc=1,
)
self.assertEqual(pr.grand_total, 20000)
self.assertEqual(pr.outstanding_amount, pr.grand_total)
self.assertEqual(pr.party_account_currency, pr.currency) # INR
self.assertEqual(pr.status, "Initiated")
po.load_from_db()
pe = pr.create_payment_entry()
self.assertEqual(len(pe.references), 2)
self.assertEqual(pe.paid_amount, 20000)
# check 1st payment term
self.assertEqual(pe.references[0].allocated_amount, 16949.2)
self.assertEqual(pe.references[0].payment_request, pr.name)
# check 2nd payment term
self.assertEqual(pe.references[1].allocated_amount, 3050.8)
self.assertEqual(pe.references[1].payment_request, pr.name)
po.load_from_db()
pr.load_from_db()
self.assertEqual(pr.status, "Paid")
self.assertEqual(pr.outstanding_amount, 0)
self.assertEqual(pr.grand_total, 20000)
@change_settings("Accounts Settings", {"allow_multi_currency_invoices_against_single_party_account": 1})
def test_single_payment_with_payment_term_for_multi_currency(self):
create_payment_terms_template()
si = create_sales_invoice(
do_not_save=1, currency="USD", debit_to="Debtors - _TC", qty=1, rate=200, conversion_rate=50
)
si.payment_terms_template = "Test Receivable Template" # 84.746 and 15.254
si.save()
si.submit()
pr = make_payment_request(
dt="Sales Invoice",
dn=si.name,
mute_email=1,
submit_doc=1,
return_doc=1,
)
# 200 USD -> 10000 INR
self.assertEqual(pr.grand_total, 200)
self.assertEqual(pr.outstanding_amount, 10000)
self.assertEqual(pr.currency, "USD")
self.assertEqual(pr.party_account_currency, "INR")
pe = pr.create_payment_entry()
self.assertEqual(len(pe.references), 2)
self.assertEqual(pe.paid_amount, 10000)
# check 1st payment term
# convert it via dollar and conversion_rate
self.assertEqual(pe.references[0].allocated_amount, 8474.5) # multi currency conversion
self.assertEqual(pe.references[0].payment_request, pr.name)
# check 2nd payment term
self.assertEqual(pe.references[1].allocated_amount, 1525.5) # multi currency conversion
self.assertEqual(pe.references[1].payment_request, pr.name)
pr.load_from_db()
self.assertEqual(pr.status, "Paid")
self.assertEqual(pr.outstanding_amount, 0)
self.assertEqual(pr.grand_total, 200)
def test_payment_cancel_process(self):
so = make_sales_order(currency="INR", qty=1, rate=1000)
pr = make_payment_request(
dt="Sales Order",
dn=so.name,
mute_email=1,
submit_doc=1,
return_doc=1,
)
self.assertEqual(pr.grand_total, 1000)
self.assertEqual(pr.outstanding_amount, pr.grand_total)
so.load_from_db()
pe = pr.create_payment_entry(submit=False)
pe.paid_amount = 800
pe.references[0].allocated_amount = 800
pe.submit()
self.assertEqual(pe.references[0].payment_request, pr.name)
so.load_from_db()
pr.load_from_db()
self.assertEqual(pr.status, "Partially Paid")
self.assertEqual(pr.outstanding_amount, 200)
self.assertEqual(pr.grand_total, 1000)
# cancelling PE
pe.cancel()
pr.load_from_db()
self.assertEqual(pr.status, "Requested")
self.assertEqual(pr.outstanding_amount, 1000)
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

@@ -19,6 +19,24 @@ frappe.ui.form.on("Period Closing Voucher", {
});
},
fiscal_year: function (frm) {
if (frm.doc.fiscal_year) {
frappe.call({
method: "erpnext.accounts.doctype.period_closing_voucher.period_closing_voucher.get_period_start_end_date",
args: {
fiscal_year: frm.doc.fiscal_year,
company: frm.doc.company,
},
callback: function (r) {
if (r.message) {
frm.set_value("period_start_date", r.message[0]);
frm.set_value("period_end_date", r.message[1]);
}
},
});
}
},
refresh: function (frm) {
if (frm.doc.docstatus > 0) {
frm.add_custom_button(

View File

@@ -6,39 +6,32 @@
"engine": "InnoDB",
"field_order": [
"transaction_date",
"posting_date",
"fiscal_year",
"year_start_date",
"amended_from",
"company",
"fiscal_year",
"period_start_date",
"period_end_date",
"amended_from",
"column_break1",
"closing_account_head",
"remarks",
"gle_processing_status",
"remarks",
"error_message"
],
"fields": [
{
"default": "Today",
"fieldname": "transaction_date",
"fieldtype": "Date",
"label": "Transaction Date",
"oldfieldname": "transaction_date",
"oldfieldtype": "Date"
},
{
"fieldname": "posting_date",
"fieldtype": "Date",
"label": "Posting Date",
"oldfieldname": "posting_date",
"oldfieldtype": "Date",
"reqd": 1
},
{
"fieldname": "fiscal_year",
"fieldtype": "Link",
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Closing Fiscal Year",
"label": "Fiscal Year",
"oldfieldname": "fiscal_year",
"oldfieldtype": "Select",
"options": "Fiscal Year",
@@ -103,16 +96,25 @@
"read_only": 1
},
{
"fieldname": "year_start_date",
"fieldname": "period_end_date",
"fieldtype": "Date",
"label": "Year Start Date"
"label": "Period End Date",
"reqd": 1
},
{
"fieldname": "period_start_date",
"fieldtype": "Date",
"label": "Period Start Date",
"oldfieldname": "posting_date",
"oldfieldtype": "Date",
"reqd": 1
}
],
"icon": "fa fa-file-text",
"idx": 1,
"is_submittable": 1,
"links": [],
"modified": "2023-09-11 20:19:11.810533",
"modified": "2024-09-15 17:22:45.291628",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Period Closing Voucher",
@@ -148,7 +150,7 @@
"write": 1
}
],
"search_fields": "posting_date, fiscal_year",
"search_fields": "fiscal_year, period_start_date, period_end_date",
"sort_field": "modified",
"sort_order": "DESC",
"states": [],

View File

@@ -2,15 +2,20 @@
# License: GNU General Public License v3. See license.txt
import copy
import frappe
from frappe import _
from frappe.query_builder.functions import Sum
from frappe.utils import add_days, flt
from frappe.utils import add_days, flt, formatdate, getdate
from erpnext.accounts.doctype.account_closing_balance.account_closing_balance import (
make_closing_entries,
)
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_accounting_dimensions,
)
from erpnext.accounts.utils import get_account_currency, get_fiscal_year, validate_fiscal_year
from erpnext.accounts.utils import get_account_currency, get_fiscal_year
from erpnext.controllers.accounts_controller import AccountsController
@@ -29,38 +34,397 @@ class PeriodClosingVoucher(AccountsController):
error_message: DF.Text | None
fiscal_year: DF.Link
gle_processing_status: DF.Literal["In Progress", "Completed", "Failed"]
posting_date: DF.Date
period_end_date: DF.Date
period_start_date: DF.Date
remarks: DF.SmallText
transaction_date: DF.Date | None
year_start_date: DF.Date | None
# end: auto-generated types
def validate(self):
self.validate_account_head()
self.validate_posting_date()
self.validate_start_and_end_date()
self.check_if_previous_year_closed()
self.block_if_future_closing_voucher_exists()
self.check_closing_account_type()
self.check_closing_account_currency()
def validate_start_and_end_date(self):
self.fy_start_date, self.fy_end_date = frappe.db.get_value(
"Fiscal Year", self.fiscal_year, ["year_start_date", "year_end_date"]
)
prev_closed_period_end_date = get_previous_closed_period_in_current_year(
self.fiscal_year, self.company
)
valid_start_date = (
add_days(prev_closed_period_end_date, 1) if prev_closed_period_end_date else self.fy_start_date
)
if getdate(self.period_start_date) != getdate(valid_start_date):
frappe.throw(_("Period Start Date must be {0}").format(formatdate(valid_start_date)))
if getdate(self.period_start_date) > getdate(self.period_end_date):
frappe.throw(_("Period Start Date cannot be greater than Period End Date"))
if getdate(self.period_end_date) > getdate(self.fy_end_date):
frappe.throw(_("Period End Date cannot be greater than Fiscal Year End Date"))
def check_if_previous_year_closed(self):
last_year_closing = add_days(self.fy_start_date, -1)
previous_fiscal_year = get_fiscal_year(last_year_closing, company=self.company, boolean=True)
if not previous_fiscal_year:
return
previous_fiscal_year_start_date = previous_fiscal_year[0][1]
gle_exists_in_previous_year = frappe.db.exists(
"GL Entry",
{
"posting_date": ("between", [previous_fiscal_year_start_date, last_year_closing]),
"company": self.company,
"is_cancelled": 0,
},
)
if not gle_exists_in_previous_year:
return
previous_fiscal_year_closed = frappe.db.exists(
"Period Closing Voucher",
{
"period_end_date": ("between", [previous_fiscal_year_start_date, last_year_closing]),
"docstatus": 1,
"company": self.company,
},
)
if not previous_fiscal_year_closed:
frappe.throw(_("Previous Year is not closed, please close it first"))
def block_if_future_closing_voucher_exists(self):
future_closing_voucher = self.get_future_closing_voucher()
if future_closing_voucher and future_closing_voucher[0][0]:
action = "cancel" if self.docstatus == 2 else "create"
frappe.throw(
_(
"You cannot {0} this document because another Period Closing Entry {1} exists after {2}"
).format(action, future_closing_voucher[0][0], self.period_end_date)
)
def get_future_closing_voucher(self):
return frappe.db.get_value(
"Period Closing Voucher",
{"period_end_date": (">", self.period_end_date), "docstatus": 1, "company": self.company},
"name",
)
def check_closing_account_type(self):
closing_account_type = frappe.get_cached_value("Account", self.closing_account_head, "root_type")
if closing_account_type not in ["Liability", "Equity"]:
frappe.throw(
_("Closing Account {0} must be of type Liability / Equity").format(self.closing_account_head)
)
def check_closing_account_currency(self):
account_currency = get_account_currency(self.closing_account_head)
company_currency = frappe.get_cached_value("Company", self.company, "default_currency")
if account_currency != company_currency:
frappe.throw(_("Currency of the Closing Account must be {0}").format(company_currency))
def on_submit(self):
self.db_set("gle_processing_status", "In Progress")
get_opening_entries = False
if not frappe.db.exists(
"Period Closing Voucher", {"company": self.company, "docstatus": 1, "name": ("!=", self.name)}
):
get_opening_entries = True
self.make_gl_entries(get_opening_entries=get_opening_entries)
self.make_gl_entries()
def on_cancel(self):
self.validate_future_closing_vouchers()
self.db_set("gle_processing_status", "In Progress")
self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Payment Ledger Entry")
gle_count = frappe.db.count(
"GL Entry",
{"voucher_type": "Period Closing Voucher", "voucher_no": self.name, "is_cancelled": 0},
)
if gle_count > 5000:
self.block_if_future_closing_voucher_exists()
self.db_set("gle_processing_status", "In Progress")
self.cancel_gl_entries()
def make_gl_entries(self):
if self.get_gle_count_in_selected_period() > 5000:
frappe.enqueue(
make_reverse_gl_entries,
process_gl_and_closing_entries,
doc=self,
timeout=1800,
)
frappe.msgprint(
_(
"The GL Entries and closing balances will be processed in the background, it can take a few minutes."
),
alert=True,
)
else:
process_gl_and_closing_entries(self)
def get_gle_count_in_selected_period(self):
return frappe.db.count(
"GL Entry",
{
"posting_date": ["between", [self.period_start_date, self.period_end_date]],
"company": self.company,
"is_cancelled": 0,
},
)
def get_pcv_gl_entries(self):
self.pl_accounts_reverse_gle = []
self.closing_account_gle = []
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) - 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)
)
# closing liability account
self.closing_account_gle.append(
self.get_gle_for_closing_account(account_balances["balances"], dimensions)
)
return self.pl_accounts_reverse_gle + self.closing_account_gle
def get_gle_for_pl_account(self, acc, balances, dimensions):
balance_in_account_currency = flt(balances.debit_in_account_currency) - flt(
balances.credit_in_account_currency
)
balance_in_company_currency = flt(balances.debit) - flt(balances.credit)
gl_entry = frappe._dict(
{
"company": self.company,
"posting_date": self.period_end_date,
"account": acc,
"account_currency": balances.account_currency,
"debit_in_account_currency": abs(balance_in_account_currency)
if balance_in_account_currency < 0
else 0,
"debit": abs(balance_in_company_currency) if balance_in_company_currency < 0 else 0,
"credit_in_account_currency": abs(balance_in_account_currency)
if balance_in_account_currency > 0
else 0,
"credit": abs(balance_in_company_currency) if balance_in_company_currency > 0 else 0,
"is_period_closing_voucher_entry": 1,
"voucher_type": "Period Closing Voucher",
"voucher_no": self.name,
"fiscal_year": self.fiscal_year,
"remarks": self.remarks,
"is_opening": "No",
}
)
self.update_default_dimensions(gl_entry, dimensions)
return gl_entry
def get_gle_for_closing_account(self, dimension_balance, dimensions):
balance_in_account_currency = flt(dimension_balance.balance_in_account_currency)
balance_in_company_currency = flt(dimension_balance.balance_in_company_currency)
gl_entry = frappe._dict(
{
"company": self.company,
"posting_date": self.period_end_date,
"account": self.closing_account_head,
"account_currency": frappe.db.get_value(
"Account", self.closing_account_head, "account_currency"
),
"debit_in_account_currency": balance_in_account_currency
if balance_in_account_currency > 0
else 0,
"debit": balance_in_company_currency if balance_in_company_currency > 0 else 0,
"credit_in_account_currency": abs(balance_in_account_currency)
if balance_in_account_currency < 0
else 0,
"credit": abs(balance_in_company_currency) if balance_in_company_currency < 0 else 0,
"is_period_closing_voucher_entry": 1,
"voucher_type": "Period Closing Voucher",
"voucher_no": self.name,
"fiscal_year": self.fiscal_year,
"remarks": self.remarks,
"is_opening": "No",
}
)
self.update_default_dimensions(gl_entry, dimensions)
return gl_entry
def update_default_dimensions(self, gl_entry, dimensions):
for i, dimension in enumerate(self.accounting_dimension_fields):
gl_entry[dimension] = dimensions[i]
def get_account_balances_based_on_dimensions(self, report_type):
"""Get balance for dimension-wise pl accounts"""
self.get_accounting_dimension_fields()
acc_bal_dict = frappe._dict()
gl_entries = []
with frappe.db.unbuffered_cursor():
gl_entries = self.get_gl_entries_for_current_period(report_type, as_iterator=True)
for gle in gl_entries:
acc_bal_dict = self.set_account_balance_dict(gle, acc_bal_dict)
if report_type == "Balance Sheet" and self.is_first_period_closing_voucher():
opening_entries = self.get_gl_entries_for_current_period(report_type, only_opening_entries=True)
for gle in opening_entries:
acc_bal_dict = self.set_account_balance_dict(gle, acc_bal_dict)
return acc_bal_dict
def get_accounting_dimension_fields(self):
default_dimensions = ["cost_center", "finance_book", "project"]
self.accounting_dimension_fields = default_dimensions + get_accounting_dimensions()
def get_gl_entries_for_current_period(self, report_type, only_opening_entries=False, as_iterator=False):
date_condition = ""
if only_opening_entries:
date_condition = "is_opening = 'Yes'"
else:
date_condition = f"posting_date BETWEEN '{self.period_start_date}' AND '{self.period_end_date}' and is_opening = 'No'"
# nosemgrep
return frappe.db.sql(
"""
SELECT
name,
posting_date,
account,
account_currency,
debit_in_account_currency,
credit_in_account_currency,
debit,
credit,
{}
FROM `tabGL Entry`
WHERE
{}
AND company = %s
AND voucher_type != 'Period Closing Voucher'
AND EXISTS(SELECT name FROM `tabAccount` WHERE name = account AND report_type = %s)
AND is_cancelled = 0
""".format(
", ".join(self.accounting_dimension_fields),
date_condition,
),
(self.company, report_type),
as_dict=1,
as_iterator=as_iterator,
)
def set_account_balance_dict(self, gle, acc_bal_dict):
key = self.get_key(gle)
acc_bal_dict.setdefault(key, frappe._dict()).setdefault(
gle.account,
frappe._dict(
{
"debit_in_account_currency": 0,
"credit_in_account_currency": 0,
"debit": 0,
"credit": 0,
"account_currency": gle.account_currency,
}
),
)
acc_bal_dict[key][gle.account].debit_in_account_currency += flt(gle.debit_in_account_currency)
acc_bal_dict[key][gle.account].credit_in_account_currency += flt(gle.credit_in_account_currency)
acc_bal_dict[key][gle.account].debit += flt(gle.debit)
acc_bal_dict[key][gle.account].credit += flt(gle.credit)
# dimension-wise total balances
acc_bal_dict[key].setdefault(
"balances",
frappe._dict(
{
"balance_in_account_currency": 0,
"balance_in_company_currency": 0,
}
),
)
balance_in_account_currency = flt(gle.debit_in_account_currency) - flt(gle.credit_in_account_currency)
balance_in_company_currency = flt(gle.debit) - flt(gle.credit)
acc_bal_dict[key]["balances"].balance_in_account_currency += balance_in_account_currency
acc_bal_dict[key]["balances"].balance_in_company_currency += balance_in_company_currency
return acc_bal_dict
def get_key(self, gle):
return tuple([gle.get(dimension) for dimension in self.accounting_dimension_fields])
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_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):
closing_entries = copy.deepcopy(self.pl_accounts_reverse_gle)
for d in self.pl_accounts_reverse_gle:
# reverse debit and credit
gle_copy = copy.deepcopy(d)
gle_copy.debit = d.credit
gle_copy.credit = d.debit
gle_copy.debit_in_account_currency = d.credit_in_account_currency
gle_copy.credit_in_account_currency = d.debit_in_account_currency
gle_copy.is_period_closing_voucher_entry = 0
gle_copy.period_closing_voucher = self.name
closing_entries.append(gle_copy)
return closing_entries
def get_closing_entries_for_balance_sheet_accounts(self):
closing_entries = []
balance_sheet_account_balances = self.get_account_balances_based_on_dimensions(
report_type="Balance Sheet"
)
for dimensions, account_balances in balance_sheet_account_balances.items():
for acc, balances in account_balances.items():
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))
return closing_entries
def get_closing_entry(self, account, balances, dimensions):
closing_entry = frappe._dict(
{
"company": self.company,
"closing_date": self.period_end_date,
"period_closing_voucher": self.name,
"account": account,
"account_currency": balances.account_currency,
"debit_in_account_currency": flt(balances.debit_in_account_currency),
"debit": flt(balances.debit),
"credit_in_account_currency": flt(balances.credit_in_account_currency),
"credit": flt(balances.credit),
"is_period_closing_voucher_entry": 0,
}
)
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):
first_pcv = frappe.db.get_value(
"Period Closing Voucher",
{"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(
process_cancellation,
voucher_type="Period Closing Voucher",
voucher_no=self.name,
queue="long",
@@ -71,341 +435,74 @@ class PeriodClosingVoucher(AccountsController):
alert=True,
)
else:
make_reverse_gl_entries(voucher_type="Period Closing Voucher", voucher_no=self.name)
process_cancellation(voucher_type="Period Closing Voucher", voucher_no=self.name)
self.delete_closing_entries()
def validate_future_closing_vouchers(self):
if frappe.db.exists(
"Period Closing Voucher",
{"posting_date": (">", self.posting_date), "docstatus": 1, "company": self.company},
):
frappe.throw(
_(
"You can not cancel this Period Closing Voucher, please cancel the future Period Closing Vouchers first"
)
)
def delete_closing_entries(self):
closing_balance = frappe.qb.DocType("Account Closing Balance")
frappe.qb.from_(closing_balance).delete().where(
closing_balance.period_closing_voucher == self.name
).run()
def validate_account_head(self):
closing_account_type = frappe.get_cached_value("Account", self.closing_account_head, "root_type")
if closing_account_type not in ["Liability", "Equity"]:
frappe.throw(
_("Closing Account {0} must be of type Liability / Equity").format(self.closing_account_head)
)
account_currency = get_account_currency(self.closing_account_head)
company_currency = frappe.get_cached_value("Company", self.company, "default_currency")
if account_currency != company_currency:
frappe.throw(_("Currency of the Closing Account must be {0}").format(company_currency))
def validate_posting_date(self):
validate_fiscal_year(
self.posting_date, self.fiscal_year, self.company, label=_("Posting Date"), doc=self
)
self.year_start_date = get_fiscal_year(self.posting_date, self.fiscal_year, company=self.company)[1]
self.check_if_previous_year_closed()
pcv = frappe.qb.DocType("Period Closing Voucher")
existing_entry = (
frappe.qb.from_(pcv)
.select(pcv.name)
.where(
(pcv.posting_date >= self.posting_date)
& (pcv.fiscal_year == self.fiscal_year)
& (pcv.docstatus == 1)
& (pcv.company == self.company)
)
.run()
)
if existing_entry and existing_entry[0][0]:
frappe.throw(
_("Another Period Closing Entry {0} has been made after {1}").format(
existing_entry[0][0], self.posting_date
)
)
def check_if_previous_year_closed(self):
last_year_closing = add_days(self.year_start_date, -1)
previous_fiscal_year = get_fiscal_year(last_year_closing, company=self.company, boolean=True)
if not previous_fiscal_year:
return
previous_fiscal_year_start_date = previous_fiscal_year[0][1]
if not frappe.db.exists(
def get_gle_count_against_current_pcv(self):
return frappe.db.count(
"GL Entry",
{
"posting_date": ("between", [previous_fiscal_year_start_date, last_year_closing]),
"company": self.company,
"is_cancelled": 0,
},
):
return
if not frappe.db.exists(
"Period Closing Voucher",
{
"posting_date": ("between", [previous_fiscal_year_start_date, last_year_closing]),
"docstatus": 1,
"company": self.company,
},
):
frappe.throw(_("Previous Year is not closed, please close it first"))
def make_gl_entries(self, get_opening_entries=False):
gl_entries = self.get_gl_entries()
closing_entries = self.get_grouped_gl_entries(get_opening_entries=get_opening_entries)
if len(gl_entries + closing_entries) > 3000:
frappe.enqueue(
process_gl_entries,
gl_entries=gl_entries,
voucher_name=self.name,
timeout=3000,
)
frappe.enqueue(
process_closing_entries,
gl_entries=gl_entries,
closing_entries=closing_entries,
voucher_name=self.name,
company=self.company,
closing_date=self.posting_date,
timeout=3000,
)
frappe.msgprint(
_("The GL Entries will be processed in the background, it can take a few minutes."),
alert=True,
)
else:
process_gl_entries(gl_entries, self.name)
process_closing_entries(gl_entries, closing_entries, self.name, self.company, self.posting_date)
def get_grouped_gl_entries(self, get_opening_entries=False):
closing_entries = []
for acc in self.get_balances_based_on_dimensions(
group_by_account=True, for_aggregation=True, get_opening_entries=get_opening_entries
):
closing_entries.append(self.get_closing_entries(acc))
return closing_entries
def get_gl_entries(self):
gl_entries = []
# pl account
for acc in self.get_balances_based_on_dimensions(
group_by_account=True, report_type="Profit and Loss"
):
if flt(acc.bal_in_company_currency):
gl_entries.append(self.get_gle_for_pl_account(acc))
# closing liability account
for acc in self.get_balances_based_on_dimensions(
group_by_account=False, report_type="Profit and Loss"
):
if flt(acc.bal_in_company_currency):
gl_entries.append(self.get_gle_for_closing_account(acc))
return gl_entries
def get_gle_for_pl_account(self, acc):
gl_entry = self.get_gl_dict(
{
"company": self.company,
"closing_date": self.posting_date,
"account": acc.account,
"cost_center": acc.cost_center,
"finance_book": acc.finance_book,
"account_currency": acc.account_currency,
"debit_in_account_currency": abs(flt(acc.bal_in_account_currency))
if flt(acc.bal_in_account_currency) < 0
else 0,
"debit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) < 0 else 0,
"credit_in_account_currency": abs(flt(acc.bal_in_account_currency))
if flt(acc.bal_in_account_currency) > 0
else 0,
"credit": abs(flt(acc.bal_in_company_currency))
if flt(acc.bal_in_company_currency) > 0
else 0,
"is_period_closing_voucher_entry": 1,
},
item=acc,
)
self.update_default_dimensions(gl_entry, acc)
return gl_entry
def get_gle_for_closing_account(self, acc):
gl_entry = self.get_gl_dict(
{
"company": self.company,
"closing_date": self.posting_date,
"account": self.closing_account_head,
"cost_center": acc.cost_center,
"finance_book": acc.finance_book,
"account_currency": acc.account_currency,
"debit_in_account_currency": abs(flt(acc.bal_in_account_currency))
if flt(acc.bal_in_account_currency) > 0
else 0,
"debit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) > 0 else 0,
"credit_in_account_currency": abs(flt(acc.bal_in_account_currency))
if flt(acc.bal_in_account_currency) < 0
else 0,
"credit": abs(flt(acc.bal_in_company_currency))
if flt(acc.bal_in_company_currency) < 0
else 0,
"is_period_closing_voucher_entry": 1,
},
item=acc,
)
self.update_default_dimensions(gl_entry, acc)
return gl_entry
def get_closing_entries(self, acc):
closing_entry = self.get_gl_dict(
{
"company": self.company,
"closing_date": self.posting_date,
"period_closing_voucher": self.name,
"account": acc.account,
"cost_center": acc.cost_center,
"finance_book": acc.finance_book,
"account_currency": acc.account_currency,
"debit_in_account_currency": flt(acc.debit_in_account_currency),
"debit": flt(acc.debit),
"credit_in_account_currency": flt(acc.credit_in_account_currency),
"credit": flt(acc.credit),
},
item=acc,
{"voucher_type": "Period Closing Voucher", "voucher_no": self.name, "is_cancelled": 0},
)
for dimension in self.accounting_dimensions:
closing_entry.update({dimension: acc.get(dimension)})
return closing_entry
def update_default_dimensions(self, gl_entry, acc):
if not self.accounting_dimensions:
self.accounting_dimensions = get_accounting_dimensions()
for dimension in self.accounting_dimensions:
gl_entry.update({dimension: acc.get(dimension)})
def get_balances_based_on_dimensions(
self, group_by_account=False, report_type=None, for_aggregation=False, get_opening_entries=False
):
"""Get balance for dimension-wise pl accounts"""
qb_dimension_fields = ["cost_center", "finance_book", "project"]
self.accounting_dimensions = get_accounting_dimensions()
for dimension in self.accounting_dimensions:
qb_dimension_fields.append(dimension)
if group_by_account:
qb_dimension_fields.append("account")
account_filters = {
"company": self.company,
"is_group": 0,
}
if report_type:
account_filters.update({"report_type": report_type})
accounts = frappe.get_all("Account", filters=account_filters, pluck="name")
gl_entry = frappe.qb.DocType("GL Entry")
query = frappe.qb.from_(gl_entry).select(gl_entry.account, gl_entry.account_currency)
if not for_aggregation:
query = query.select(
(Sum(gl_entry.debit_in_account_currency) - Sum(gl_entry.credit_in_account_currency)).as_(
"bal_in_account_currency"
),
(Sum(gl_entry.debit) - Sum(gl_entry.credit)).as_("bal_in_company_currency"),
)
else:
query = query.select(
(Sum(gl_entry.debit_in_account_currency)).as_("debit_in_account_currency"),
(Sum(gl_entry.credit_in_account_currency)).as_("credit_in_account_currency"),
(Sum(gl_entry.debit)).as_("debit"),
(Sum(gl_entry.credit)).as_("credit"),
)
for dimension in qb_dimension_fields:
query = query.select(gl_entry[dimension])
query = query.where(
(gl_entry.company == self.company)
& (gl_entry.is_cancelled == 0)
& (gl_entry.account.isin(accounts))
)
if get_opening_entries:
query = query.where(
gl_entry.posting_date.between(self.get("year_start_date"), self.posting_date)
| gl_entry.is_opening
== "Yes"
)
else:
query = query.where(
gl_entry.posting_date.between(self.get("year_start_date"), self.posting_date)
& gl_entry.is_opening
== "No"
)
if for_aggregation:
query = query.where(gl_entry.voucher_type != "Period Closing Voucher")
for dimension in qb_dimension_fields:
query = query.groupby(gl_entry[dimension])
return query.run(as_dict=1)
def process_gl_entries(gl_entries, voucher_name):
def process_gl_and_closing_entries(doc):
from erpnext.accounts.general_ledger import make_gl_entries
try:
gl_entries = doc.get_pcv_gl_entries()
if gl_entries:
make_gl_entries(gl_entries, merge_entries=False)
frappe.db.set_value("Period Closing Voucher", voucher_name, "gle_processing_status", "Completed")
closing_entries = doc.get_account_closing_balances()
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:
frappe.db.rollback()
frappe.log_error(e)
frappe.db.set_value("Period Closing Voucher", voucher_name, "gle_processing_status", "Failed")
frappe.db.set_value(doc.doctype, doc.name, "gle_processing_status", "Failed")
def process_closing_entries(gl_entries, closing_entries, voucher_name, company, closing_date):
from erpnext.accounts.doctype.account_closing_balance.account_closing_balance import (
make_closing_entries,
)
try:
if gl_entries + closing_entries:
make_closing_entries(gl_entries + closing_entries, voucher_name, company, closing_date)
except Exception as e:
frappe.db.rollback()
frappe.log_error(e)
def make_reverse_gl_entries(voucher_type, voucher_no):
def process_cancellation(voucher_type, voucher_no):
from erpnext.accounts.general_ledger import make_reverse_gl_entries
try:
make_reverse_gl_entries(voucher_type=voucher_type, voucher_no=voucher_no)
delete_closing_entries(voucher_no)
frappe.db.set_value("Period Closing Voucher", voucher_no, "gle_processing_status", "Completed")
except Exception as e:
frappe.db.rollback()
frappe.log_error(e)
frappe.db.set_value("Period Closing Voucher", voucher_no, "gle_processing_status", "Failed")
def delete_closing_entries(voucher_no):
closing_balance = frappe.qb.DocType("Account Closing Balance")
frappe.qb.from_(closing_balance).delete().where(
closing_balance.period_closing_voucher == voucher_no
).run()
@frappe.whitelist()
def get_period_start_end_date(fiscal_year, company):
fy_start_date, fy_end_date = frappe.db.get_value(
"Fiscal Year", fiscal_year, ["year_start_date", "year_end_date"]
)
prev_closed_period_end_date = get_previous_closed_period_in_current_year(fiscal_year, company)
period_start_date = (
add_days(prev_closed_period_end_date, 1) if prev_closed_period_end_date else fy_start_date
)
return period_start_date, fy_end_date
def get_previous_closed_period_in_current_year(fiscal_year, company):
prev_closed_period_end_date = frappe.db.get_value(
"Period Closing Voucher",
filters={
"company": company,
"fiscal_year": fiscal_year,
"docstatus": 1,
},
fieldname=["period_end_date"],
order_by="period_end_date desc",
)
return prev_closed_period_end_date

View File

@@ -317,16 +317,18 @@ class TestPeriodClosingVoucher(unittest.TestCase):
repost_doc.posting_date = today()
repost_doc.save()
def make_period_closing_voucher(self, posting_date=None, submit=True):
def make_period_closing_voucher(self, posting_date, submit=True):
surplus_account = create_account()
cost_center = create_cost_center("Test Cost Center 1")
fy = get_fiscal_year(posting_date, company="Test PCV Company")
pcv = frappe.get_doc(
{
"doctype": "Period Closing Voucher",
"transaction_date": posting_date or today(),
"posting_date": posting_date or today(),
"period_start_date": fy[1],
"period_end_date": fy[2],
"company": "Test PCV Company",
"fiscal_year": get_fiscal_year(today(), company="Test PCV Company")[0],
"fiscal_year": fy[0],
"cost_center": cost_center,
"closing_account_head": surplus_account,
"remarks": "test",

View File

@@ -80,8 +80,10 @@ frappe.ui.form.on("POS Closing Entry", {
) {
reset_values(frm);
frappe.run_serially([
() => frappe.dom.freeze(__("Loading Invoices! Please Wait...")),
() => frm.trigger("set_opening_amounts"),
() => frm.trigger("get_pos_invoices"),
() => frappe.dom.unfreeze(),
]);
}
},
@@ -145,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);
@@ -170,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);
});
}
@@ -184,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
@@ -194,7 +196,7 @@ function refresh_payments(d, frm) {
}
if (payment) {
payment.expected_amount += flt(p.amount);
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

@@ -40,10 +40,24 @@ erpnext.selling.POSInvoiceController = class POSInvoiceController extends erpnex
};
});
this.frm.set_query("item_code", "items", function (doc) {
return {
query: "erpnext.accounts.doctype.pos_invoice.pos_invoice.item_query",
filters: {
has_variants: ["=", 0],
is_sales_item: ["=", 1],
disabled: ["=", 0],
is_fixed_asset: ["=", 0],
pos_profile: ["=", doc.pos_profile],
},
};
});
erpnext.accounts.dimensions.setup_dimension_filters(this.frm, this.frm.doctype);
}
onload_post_render(frm) {
super.onload_post_render();
this.pos_profile(frm);
}
@@ -51,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

@@ -6,6 +6,7 @@ import frappe
from frappe import _, bold
from frappe.query_builder.functions import IfNull, Sum
from frappe.utils import cint, flt, get_link_to_form, getdate, nowdate
from frappe.utils.nestedset import get_descendants_of
from erpnext.accounts.doctype.loyalty_program.loyalty_program import validate_loyalty_points
from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request
@@ -15,6 +16,7 @@ from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
update_multi_mode_option,
)
from erpnext.accounts.party import get_due_date, get_party_account
from erpnext.controllers.queries import item_query as _item_query
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
@@ -30,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,
)
@@ -73,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
@@ -449,7 +448,7 @@ class POSInvoice(SalesInvoice):
if self.is_return and entry.amount > 0:
frappe.throw(_("Row #{0} (Payment Table): Amount must be negative").format(entry.idx))
if self.is_return:
if self.is_return and self.docstatus != 0:
invoice_total = self.rounded_total or self.grand_total
total_amount_in_payments = flt(total_amount_in_payments, self.precision("grand_total"))
if total_amount_in_payments and total_amount_in_payments < invoice_total:
@@ -837,3 +836,30 @@ def add_return_modes(doc, pos_profile):
]:
payment_mode = get_mode_of_payment_info(mode_of_payment, doc.company)
append_payment(payment_mode[0])
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False):
if pos_profile := filters.get("pos_profile")[1]:
pos_profile = frappe.get_cached_doc("POS Profile", pos_profile)
if item_groups := get_item_group(pos_profile):
filters["item_group"] = ["in", tuple(item_groups)]
del filters["pos_profile"]
else:
filters.pop("pos_profile", None)
return _item_query(doctype, txt, searchfield, start, page_len, filters, as_dict)
def get_item_group(pos_profile):
item_groups = []
if pos_profile.get("item_groups"):
# Get items based on the item groups defined in the POS profile
for row in pos_profile.get("item_groups"):
item_groups.append(row.item_group)
item_groups.extend(get_descendants_of("Item Group", row.item_group))
return list(set(item_groups))

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

@@ -419,7 +419,8 @@
"depends_on": "eval:doc.rate_or_discount==\"Rate\"",
"fieldname": "rate",
"fieldtype": "Currency",
"label": "Rate"
"label": "Rate",
"options": "currency"
},
{
"default": "0",
@@ -647,7 +648,7 @@
"icon": "fa fa-gift",
"idx": 1,
"links": [],
"modified": "2024-05-17 13:16:34.496704",
"modified": "2024-09-16 18:14:51.314765",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Pricing Rule",
@@ -709,4 +710,4 @@
"sort_order": "DESC",
"states": [],
"title_field": "title"
}
}

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

@@ -5,6 +5,7 @@
import unittest
import frappe
from frappe.tests.utils import FrappeTestCase, change_settings
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
@@ -14,7 +15,7 @@ from erpnext.stock.doctype.item.test_item import make_item
from erpnext.stock.get_item_details import get_item_details
class TestPricingRule(unittest.TestCase):
class TestPricingRule(FrappeTestCase):
def setUp(self):
delete_existing_pricing_rules()
setup_pricing_rule_data()
@@ -1130,6 +1131,51 @@ class TestPricingRule(unittest.TestCase):
self.assertEqual(so.items[1].item_code, "_Test Item")
self.assertEqual(so.items[1].qty, 3)
so = make_sales_order(item_code="_Test Item", qty=5, do_not_submit=1)
so.items[0].qty = 1
del so.items[-1]
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,10 @@ 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
free_item_data_args = {
"item_code": free_item,
@@ -725,14 +728,11 @@ def get_pricing_rule_items(pr_doc, other_items=False) -> list:
def validate_coupon_code(coupon_name):
coupon = frappe.get_doc("Coupon Code", coupon_name)
if coupon.valid_from:
if coupon.valid_from > getdate(today()):
frappe.throw(_("Sorry, this coupon code's validity has not started"))
elif coupon.valid_upto:
if coupon.valid_upto < getdate(today()):
frappe.throw(_("Sorry, this coupon code's validity has expired"))
elif coupon.used >= coupon.maximum_use:
if coupon.valid_from and coupon.valid_from > getdate(today()):
frappe.throw(_("Sorry, this coupon code's validity has not started"))
elif coupon.valid_upto and coupon.valid_upto < getdate(today()):
frappe.throw(_("Sorry, this coupon code's validity has expired"))
elif coupon.maximum_use and coupon.used >= coupon.maximum_use:
frappe.throw(_("Sorry, this coupon code is no longer valid"))

View File

@@ -25,6 +25,7 @@
"payment_terms_template",
"sales_partner",
"sales_person",
"show_remarks",
"based_on_payment_terms",
"section_break_3",
"customer_collection",
@@ -390,10 +391,16 @@
"fieldname": "ignore_cr_dr_notes",
"fieldtype": "Check",
"label": "Ignore System Generated Credit / Debit Notes"
},
{
"default": "0",
"fieldname": "show_remarks",
"fieldtype": "Check",
"label": "Show Remarks"
}
],
"links": [],
"modified": "2024-08-13 10:41:18.381165",
"modified": "2024-10-18 17:51:39.108481",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Process Statement Of Accounts",

View File

@@ -70,6 +70,7 @@ class ProcessStatementOfAccounts(Document):
sales_person: DF.Link | None
sender: DF.Link | None
show_net_values_in_party_account: DF.Check
show_remarks: DF.Check
start_date: DF.Date | None
subject: DF.Data | None
terms_and_conditions: DF.Link | None
@@ -187,6 +188,7 @@ def get_common_filters(doc):
"finance_book": doc.finance_book if doc.finance_book else None,
"account": [doc.account] if doc.account else None,
"cost_center": [cc.cost_center_name for cc in doc.cost_center],
"show_remarks": doc.show_remarks,
}
)
@@ -472,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

@@ -5,6 +5,8 @@
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.query_builder import Criterion
from frappe.query_builder.functions import IfNull
pricing_rule_fields = [
"apply_on",
@@ -162,22 +164,50 @@ class PromotionalScheme(Document):
if self.is_new():
return
transaction_exists = False
docnames = []
invalid_pricing_rule = self.get_invalid_pricing_rules()
# If user has changed applicable for
if self.get_doc_before_save() and self.get_doc_before_save().applicable_for == self.applicable_for:
if not invalid_pricing_rule:
return
docnames = frappe.get_all("Pricing Rule", filters={"promotional_scheme": self.name})
if frappe.db.exists(
"Pricing Rule Detail",
{
"pricing_rule": ["in", invalid_pricing_rule],
"docstatus": ["<", 2],
},
):
raise_for_transaction_exists(self.name)
for docname in docnames:
if frappe.db.exists("Pricing Rule Detail", {"pricing_rule": docname.name, "docstatus": ("<", 2)}):
raise_for_transaction_exists(self.name)
for doc in invalid_pricing_rule:
frappe.delete_doc("Pricing Rule", doc)
if docnames and not transaction_exists:
for docname in docnames:
frappe.delete_doc("Pricing Rule", docname.name)
frappe.msgprint(
_("The following invalid Pricing Rules are deleted:")
+ "<br><br><ul><li>"
+ "</li><li>".join(invalid_pricing_rule)
+ "</li></ul>"
)
def get_invalid_pricing_rules(self):
pr = frappe.qb.DocType("Pricing Rule")
conditions = []
conditions.append(pr.promotional_scheme == self.name)
if self.applicable_for:
applicable_for = frappe.scrub(self.applicable_for)
applicable_for_list = [d.get(applicable_for) for d in self.get(applicable_for)]
conditions.append(
(IfNull(pr.applicable_for, "") != self.applicable_for)
| (
(IfNull(pr.applicable_for, "") == self.applicable_for)
& IfNull(pr[applicable_for], "").notin(applicable_for_list)
)
)
else:
conditions.append(IfNull(pr.applicable_for, "") != "")
return frappe.qb.from_(pr).select(pr.name).where(Criterion.all(conditions)).run(pluck=True)
def on_update(self):
self.validate()

View File

@@ -90,6 +90,31 @@ class TestPromotionalScheme(unittest.TestCase):
price_rules = frappe.get_all("Pricing Rule", filters={"promotional_scheme": ps.name})
self.assertEqual(price_rules, [])
def test_change_applicable_for_values_in_promotional_scheme(self):
ps = make_promotional_scheme(applicable_for="Customer", customer="_Test Customer")
ps.append("customer", {"customer": "_Test Customer 2"})
ps.save()
price_rules = frappe.get_all(
"Pricing Rule", filters={"promotional_scheme": ps.name, "applicable_for": "Customer"}
)
self.assertTrue(len(price_rules), 2)
ps.set("customer", [])
ps.append("customer", {"customer": "_Test Customer 2"})
ps.save()
price_rules = frappe.get_all(
"Pricing Rule",
filters={
"promotional_scheme": ps.name,
"applicable_for": "Customer",
"customer": "_Test Customer",
},
)
self.assertEqual(price_rules, [])
frappe.delete_doc("Promotional Scheme", ps.name)
def test_min_max_amount_configuration(self):
ps = make_promotional_scheme()
ps.price_discount_slabs[0].min_amount = 10

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 },
@@ -561,11 +563,12 @@ frappe.ui.form.on("Purchase Invoice", {
frm.custom_make_buttons = {
"Purchase Invoice": "Return / Debit Note",
"Payment Entry": "Payment",
"Landed Cost Voucher": function () {
frm.trigger("create_landed_cost_voucher");
},
};
if (frm.doc.update_stock) {
frm.custom_make_buttons["Landed Cost Voucher"] = "Landed Cost Voucher";
}
frm.set_query("additional_discount_account", function () {
return {
filters: {
@@ -607,20 +610,6 @@ frappe.ui.form.on("Purchase Invoice", {
});
},
create_landed_cost_voucher: function (frm) {
let lcv = frappe.model.get_new_doc("Landed Cost Voucher");
lcv.company = frm.doc.company;
let lcv_receipt = frappe.model.get_new_doc("Landed Cost Purchase Invoice");
lcv_receipt.receipt_document_type = "Purchase Invoice";
lcv_receipt.receipt_document = frm.doc.name;
lcv_receipt.supplier = frm.doc.supplier;
lcv_receipt.grand_total = frm.doc.grand_total;
lcv.purchase_receipts = [lcv_receipt];
frappe.set_route("Form", lcv.doctype, lcv.name);
},
add_custom_buttons: function (frm) {
if (frm.doc.docstatus == 1 && frm.doc.per_received < 100) {
frm.add_custom_button(
@@ -645,14 +634,40 @@ frappe.ui.form.on("Purchase Invoice", {
__("View")
);
}
if (frm.doc.docstatus === 1 && frm.doc.update_stock) {
frm.add_custom_button(
__("Landed Cost Voucher"),
() => {
frm.events.make_lcv(frm);
},
__("Create")
);
}
},
make_lcv(frm) {
frappe.call({
method: "erpnext.stock.doctype.purchase_receipt.purchase_receipt.make_lcv",
args: {
doctype: frm.doc.doctype,
docname: frm.doc.name,
},
callback: (r) => {
if (r.message) {
var doc = frappe.model.sync(r.message);
frappe.set_route("Form", doc[0].doctype, doc[0].name);
}
},
});
},
onload: function (frm) {
if (frm.doc.__onload && frm.is_new()) {
if (frm.doc.supplier) {
if (frm.doc.__onload && frm.doc.supplier) {
if (frm.is_new()) {
frm.doc.apply_tds = frm.doc.__onload.supplier_tds ? 1 : 0;
}
if (!frm.doc.__onload.enable_apply_tds) {
if (!frm.doc.__onload.supplier_tds) {
frm.set_df_property("apply_tds", "read_only", 1);
}
}

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",
@@ -1271,6 +1273,7 @@
"fieldtype": "Select",
"in_standard_filter": 1,
"label": "Status",
"no_copy": 1,
"options": "\nDraft\nReturn\nDebit Note Issued\nSubmitted\nPaid\nPartly Paid\nUnpaid\nOverdue\nCancelled\nInternal Transfer",
"print_hide": 1
},
@@ -1630,7 +1633,7 @@
"idx": 204,
"is_submittable": 1,
"links": [],
"modified": "2024-07-25 19:42:36.931278",
"modified": "2024-10-25 18:13:01.944477",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice",

View File

@@ -346,22 +346,6 @@ class PurchaseInvoice(BuyingController):
self.tax_withholding_category = tds_category
self.set_onload("supplier_tds", tds_category)
# If Linked Purchase Order has TDS applied, enable 'apply_tds' checkbox
if purchase_orders := [x.purchase_order for x in self.items if x.purchase_order]:
po = qb.DocType("Purchase Order")
po_with_tds = (
qb.from_(po)
.select(po.name)
.where(
po.docstatus.eq(1)
& (po.name.isin(purchase_orders))
& (po.apply_tds.eq(1))
& (po.tax_withholding_category.notnull())
)
.run()
)
self.set_onload("enable_apply_tds", True if po_with_tds else False)
super().set_missing_values(for_validate)
def validate_credit_to_acc(self):
@@ -879,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)
@@ -912,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
@@ -1429,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:
@@ -1522,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(
{
@@ -1609,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)
@@ -2292,6 +2371,83 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 1)
def test_last_purchase_rate(self):
item = create_item("_Test Item For Last Purchase Rate from PI", is_stock_item=1)
pi1 = make_purchase_invoice(item_code=item.item_code, qty=10, rate=100)
item.reload()
self.assertEqual(item.last_purchase_rate, 100)
pi2 = make_purchase_invoice(item_code=item.item_code, qty=10, rate=200)
item.reload()
self.assertEqual(item.last_purchase_rate, 200)
pi2.cancel()
item.reload()
self.assertEqual(item.last_purchase_rate, 100)
pi1.cancel()
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,9 +45,9 @@ class RepostAccountingLedger(Document):
latest_pcv = (
frappe.db.get_all(
"Period Closing Voucher",
filters={"company": self.company},
order_by="posting_date desc",
pluck="posting_date",
filters={"company": self.company, "docstatus": 1},
order_by="period_end_date desc",
pluck="period_end_date",
limit=1,
)
or None

View File

@@ -129,13 +129,15 @@ class TestRepostAccountingLedger(AccountsTestMixin, FrappeTestCase):
cost_center=self.cost_center,
rate=100,
)
fy = get_fiscal_year(today(), company=self.company)
pcv = frappe.get_doc(
{
"doctype": "Period Closing Voucher",
"transaction_date": today(),
"posting_date": today(),
"period_start_date": fy[1],
"period_end_date": today(),
"company": self.company,
"fiscal_year": get_fiscal_year(today(), company=self.company)[0],
"fiscal_year": fy[0],
"cost_center": self.cost_center,
"closing_account_head": self.retained_earnings,
"remarks": "test",

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"
@@ -340,6 +344,7 @@ class SalesInvoice(SellingController):
):
validate_loyalty_points(self, self.loyalty_points)
self.allow_write_off_only_on_pos()
self.reset_default_field_value("set_warehouse", "items", "warehouse")
def validate_accounts(self):
@@ -1021,6 +1026,10 @@ class SalesInvoice(SellingController):
raise_exception=1,
)
def allow_write_off_only_on_pos(self):
if not self.is_pos and self.write_off_account:
self.write_off_account = None
def validate_write_off_account(self):
if flt(self.write_off_amount) and not self.write_off_account:
self.write_off_account = frappe.get_cached_value("Company", self.company, "write_off_account")
@@ -1354,14 +1363,15 @@ class SalesInvoice(SellingController):
else:
if asset.calculate_depreciation:
notes = _(
"This schedule was created when Asset {0} was sold through Sales Invoice {1}."
).format(
get_link_to_form(asset.doctype, asset.name),
get_link_to_form(self.doctype, self.get("name")),
)
depreciate_asset(asset, self.posting_date, notes)
asset.reload()
if not asset.status == "Fully Depreciated":
notes = _(
"This schedule was created when Asset {0} was sold through Sales Invoice {1}."
).format(
get_link_to_form(asset.doctype, asset.name),
get_link_to_form(self.doctype, self.get("name")),
)
depreciate_asset(asset, self.posting_date, notes)
asset.reload()
fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(
asset,
@@ -1624,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(
{
@@ -1725,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):
@@ -2118,7 +2152,7 @@ def make_delivery_note(source_name, target_doc=None):
"postprocess": update_item,
"condition": lambda doc: doc.delivered_by_supplier != 1,
},
"Sales Taxes and Charges": {"doctype": "Sales Taxes and Charges", "add_if_empty": True},
"Sales Taxes and Charges": {"doctype": "Sales Taxes and Charges", "reset_value": True},
"Sales Team": {
"doctype": "Sales Team",
"field_map": {"incentives": "incentives"},

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

@@ -185,7 +185,7 @@ def get_tax_template(posting_date, args):
conditions.append("(from_date is null) and (to_date is null)")
conditions.append(
"ifnull(tax_category, '') = {}".format(frappe.db.escape(cstr(args.get("tax_category"))))
"ifnull(tax_category, '') = {}".format(frappe.db.escape(cstr(args.get("tax_category")), False))
)
if "tax_category" in args.keys():
del args["tax_category"]

View File

@@ -327,7 +327,7 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N
tax_amount = 0
else:
# if no TCS has been charged in FY,
# then chargeable value is "prev invoices + advances" value which cross the threshold
# then chargeable value is "prev invoices + advances - advance_adjusted" value which cross the threshold
tax_amount = get_tcs_amount(parties, inv, tax_details, vouchers, advance_vouchers)
if cint(tax_details.round_off_tax_amount):
@@ -414,6 +414,9 @@ def get_advance_vouchers(parties, company=None, from_date=None, to_date=None, pa
Use Payment Ledger to fetch unallocated Advance Payments
"""
if party_type == "Supplier":
return []
ple = qb.DocType("Payment Ledger Entry")
conditions = []
@@ -511,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(
@@ -535,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:
@@ -553,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(
@@ -607,8 +610,6 @@ def get_tcs_amount(parties, inv, tax_details, vouchers, adv_vouchers):
conditions.append(ple.voucher_no == ple.against_voucher_no)
conditions.append(ple.company == inv.company)
(qb.from_(ple).select(Abs(Sum(ple.amount))).where(Criterion.all(conditions)).run(as_list=1))
advance_amt = (
qb.from_(ple).select(Abs(Sum(ple.amount))).where(Criterion.all(conditions)).run()[0][0] or 0.0
)
@@ -631,9 +632,12 @@ def get_tcs_amount(parties, inv, tax_details, vouchers, adv_vouchers):
)
cumulative_threshold = tax_details.get("cumulative_threshold", 0)
advance_adjusted = get_advance_adjusted_in_invoice(inv)
current_invoice_total = get_invoice_total_without_tcs(inv, tax_details)
total_invoiced_amt = current_invoice_total + invoiced_amt + advance_amt - credit_note_amt
total_invoiced_amt = (
current_invoice_total + invoiced_amt + advance_amt - credit_note_amt - advance_adjusted
)
if cumulative_threshold and total_invoiced_amt >= cumulative_threshold:
chargeable_amt = total_invoiced_amt - cumulative_threshold
@@ -642,6 +646,14 @@ def get_tcs_amount(parties, inv, tax_details, vouchers, adv_vouchers):
return tcs_amount
def get_advance_adjusted_in_invoice(inv):
advances_adjusted = 0
for row in inv.get("advances", []):
advances_adjusted += row.allocated_amount
return advances_adjusted
def get_invoice_total_without_tcs(inv, tax_details):
tcs_tax_row = [d for d in inv.taxes if d.account_head == tax_details.account_head]
tcs_tax_row_amount = tcs_tax_row[0].base_tax_amount if tcs_tax_row else 0

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"
@@ -210,6 +295,46 @@ class TestTaxWithholdingCategory(FrappeTestCase):
d.reload()
d.cancel()
def test_tcs_on_allocated_advance_payments(self):
frappe.db.set_value(
"Customer", "Test TCS Customer", "tax_withholding_category", "Cumulative Threshold TCS"
)
vouchers = []
# create advance payment
pe = create_payment_entry(
payment_type="Receive", party_type="Customer", party="Test TCS Customer", paid_amount=30000
)
pe.paid_from = "Debtors - _TC"
pe.paid_to = "Cash - _TC"
pe.submit()
vouchers.append(pe)
si = create_sales_invoice(customer="Test TCS Customer", rate=50000)
advances = si.get_advance_entries()
si.append(
"advances",
{
"reference_type": advances[0].reference_type,
"reference_name": advances[0].reference_name,
"advance_amount": advances[0].amount,
"allocated_amount": 30000,
},
)
si.submit()
vouchers.append(si)
# assert tax collection on total invoice ,advance payment adjusted should be excluded.
tcs_charged = sum([d.base_tax_amount for d in si.taxes if d.account_head == "TCS - _TC"])
# tcs = (inv amt)50000+(adv amt)30000-(adv adj) 30000 - threshold(30000) * rate 10%
self.assertEqual(tcs_charged, 2000)
# cancel invoice and payments to avoid clashing
for d in reversed(vouchers):
d.reload()
d.cancel()
def test_tds_calculation_on_net_total(self):
frappe.db.set_value(
"Supplier", "Test TDS Supplier4", "tax_withholding_category", "Cumulative Threshold TDS"

View File

@@ -7,7 +7,9 @@ from frappe.utils import today
from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.party import get_party_account
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
@@ -260,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
@@ -277,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
@@ -360,6 +364,107 @@ 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()
so2 = self.create_sales_order()
pe = self.create_payment_entry()
# Allocation payment against Sales Order
pe.paid_amount = 260
pe.append(
"references",
{"reference_doctype": so1.doctype, "reference_name": so1.name, "allocated_amount": 150},
)
pe.append(
"references",
{"reference_doctype": so2.doctype, "reference_name": so2.name, "allocated_amount": 110},
)
pe.save().submit()
# Assert 'Advance Paid'
so1.reload()
self.assertEqual(so1.advance_paid, 150)
so2.reload()
self.assertEqual(so2.advance_paid, 110)
unreconcile = frappe.get_doc(
{
"doctype": "Unreconcile Payment",
"company": self.company,
"voucher_type": pe.doctype,
"voucher_no": pe.name,
}
)
unreconcile.add_references()
self.assertEqual(len(unreconcile.allocations), 2)
allocations = [(x.reference_name, x.allocated_amount) for x in unreconcile.allocations]
self.assertListEqual(allocations, [(so1.name, 150), (so2.name, 110)])
# unreconcile so2
unreconcile.remove(unreconcile.allocations[0])
unreconcile.save().submit()
# Assert 'Advance Paid'
so1.reload()
so2.reload()
pe.reload()
self.assertEqual(so1.advance_paid, 150)
self.assertEqual(so2.advance_paid, 110)
self.assertEqual(len(pe.references), 1)
self.assertEqual(pe.unallocated_amount, 110)
self.disable_advance_as_liability()
def test_07_adv_from_so_to_invoice(self):
self.enable_advance_as_liability()
so = self.create_sales_order()
pe = self.create_payment_entry()
pe.paid_amount = 1000
pe.append(
"references",
{"reference_doctype": so.doctype, "reference_name": so.name, "allocated_amount": 1000},
)
pe.save().submit()
# Assert 'Advance Paid'
so.reload()
self.assertEqual(so.advance_paid, 1000)
si = make_sales_invoice(so.name)
si.insert().submit()
pr = frappe.get_doc(
{
"doctype": "Payment Reconciliation",
"company": self.company,
"party_type": "Customer",
"party": so.customer,
}
)
accounts = get_party_account("Customer", so.customer, so.company, True)
pr.receivable_payable_account = accounts[0]
pr.default_advance_account = accounts[1]
pr.get_unreconciled_entries()
self.assertEqual(len(pr.get("invoices")), 1)
self.assertEqual(len(pr.get("payments")), 1)
invoices = [x.as_dict() for x in pr.get("invoices")]
payments = [x.as_dict() for x in pr.get("payments")]
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
pr.reconcile()
self.assertEqual(len(pr.get("invoices")), 0)
self.assertEqual(len(pr.get("payments")), 0)
# Assert 'Advance Paid'
so.reload()
self.assertEqual(so.advance_paid, 1000)
self.disable_advance_as_liability()

View File

@@ -1,7 +1,5 @@
{
"actions": [],
"allow_rename": 1,
"autoname": "format:UNREC-{#####}",
"creation": "2023-08-22 10:26:34.421423",
"default_view": "List",
"doctype": "DocType",
@@ -58,11 +56,10 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2023-08-28 17:42:50.261377",
"modified": "2024-10-10 12:03:50.022444",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Unreconcile Payment",
"naming_rule": "Expression",
"owner": "Administrator",
"permissions": [
{

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 (
@@ -37,13 +37,14 @@ def make_gl_entries(
validate_disabled_accounts(gl_map)
gl_map = process_gl_map(gl_map, merge_entries)
if gl_map and len(gl_map) > 1:
create_payment_ledger_entry(
gl_map,
cancel=0,
adv_adj=adv_adj,
update_outstanding=update_outstanding,
from_repost=from_repost,
)
if gl_map[0].voucher_type != "Period Closing Voucher":
create_payment_ledger_entry(
gl_map,
cancel=0,
adv_adj=adv_adj,
update_outstanding=update_outstanding,
from_repost=from_repost,
)
save_entries(gl_map, adv_adj, update_outstanding, from_repost)
# Post GL Map proccess there may no be any GL Entries
elif gl_map:
@@ -116,17 +117,16 @@ def get_accounting_dimensions_for_offsetting_entry(gl_map, company):
def validate_disabled_accounts(gl_map):
accounts = [d.account for d in gl_map if d.account]
Account = frappe.qb.DocType("Account")
disabled_accounts = frappe.get_all(
"Account",
filters={"disabled": 1, "is_group": 0, "company": gl_map[0].company},
fields=["name"],
)
disabled_accounts = (
frappe.qb.from_(Account)
.where(Account.name.isin(accounts) & Account.disabled == 1)
.select(Account.name, Account.disabled)
).run(as_dict=True)
if disabled_accounts:
used_disabled_accounts = set(accounts).intersection(set([d.name for d in disabled_accounts]))
if used_disabled_accounts:
account_list = "<br>"
account_list += ", ".join([frappe.bold(d.name) for d in disabled_accounts])
account_list += ", ".join([frappe.bold(d) for d in used_disabled_accounts])
frappe.throw(
_("Cannot create accounting entries against disabled accounts: {0}").format(account_list),
title=_("Disabled Account Selected"),
@@ -179,50 +179,53 @@ def process_gl_map(gl_map, merge_entries=True, precision=None):
def distribute_gl_based_on_cost_center_allocation(gl_map, precision=None):
cost_center_allocation = get_cost_center_allocation_data(gl_map[0]["company"], gl_map[0]["posting_date"])
if not cost_center_allocation:
return gl_map
new_gl_map = []
for d in gl_map:
cost_center = d.get("cost_center")
# Validate budget against main cost center
validate_expense_against_budget(d, expense_amount=flt(d.debit, precision) - flt(d.credit, precision))
if cost_center and cost_center_allocation.get(cost_center):
for sub_cost_center, percentage in cost_center_allocation.get(cost_center, {}).items():
gle = copy.deepcopy(d)
gle.cost_center = sub_cost_center
for field in ("debit", "credit", "debit_in_account_currency", "credit_in_account_currency"):
gle[field] = flt(flt(d.get(field)) * percentage / 100, precision)
new_gl_map.append(gle)
else:
cost_center_allocation = get_cost_center_allocation_data(
gl_map[0]["company"], gl_map[0]["posting_date"], cost_center
)
if not cost_center_allocation:
new_gl_map.append(d)
continue
for sub_cost_center, percentage in cost_center_allocation:
gle = copy.deepcopy(d)
gle.cost_center = sub_cost_center
for field in ("debit", "credit", "debit_in_account_currency", "credit_in_account_currency"):
gle[field] = flt(flt(d.get(field)) * percentage / 100, precision)
new_gl_map.append(gle)
return new_gl_map
def get_cost_center_allocation_data(company, posting_date):
par = frappe.qb.DocType("Cost Center Allocation")
child = frappe.qb.DocType("Cost Center Allocation Percentage")
def get_cost_center_allocation_data(company, posting_date, cost_center):
cost_center_allocation = frappe.db.get_value(
"Cost Center Allocation",
{
"docstatus": 1,
"company": company,
"valid_from": ("<=", posting_date),
"main_cost_center": cost_center,
},
pluck="name",
order_by="valid_from desc",
)
records = (
frappe.qb.from_(par)
.inner_join(child)
.on(par.name == child.parent)
.select(par.main_cost_center, child.cost_center, child.percentage)
.where(par.docstatus == 1)
.where(par.company == company)
.where(par.valid_from <= posting_date)
.orderby(par.valid_from, order=frappe.qb.desc)
).run(as_dict=True)
if not cost_center_allocation:
return []
cc_allocation = frappe._dict()
for d in records:
cc_allocation.setdefault(d.main_cost_center, frappe._dict()).setdefault(d.cost_center, d.percentage)
records = frappe.db.get_all(
"Cost Center Allocation Percentage",
{"parent": cost_center_allocation},
["cost_center", "percentage"],
as_list=True,
)
return cc_allocation
return records
def merge_similar_entries(gl_map, precision=None):
@@ -231,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
@@ -308,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)
@@ -489,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)
@@ -516,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,
@@ -530,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)
@@ -554,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:
@@ -571,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(
@@ -705,7 +725,7 @@ def validate_against_pcv(is_opening, posting_date, company):
)
last_pcv_date = frappe.db.get_value(
"Period Closing Voucher", {"docstatus": 1, "company": company}, "max(posting_date)"
"Period Closing Voucher", {"docstatus": 1, "company": company}, "max(period_end_date)"
)
if last_pcv_date and getdate(posting_date) <= getdate(last_pcv_date):

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",
@@ -881,16 +887,17 @@ def get_party_shipping_address(doctype: str, name: str) -> str | None:
def get_partywise_advanced_payment_amount(
party_type, posting_date=None, future_payment=0, company=None, party=None
):
account_type = frappe.get_cached_value("Party Type", party_type, "account_type")
ple = frappe.qb.DocType("Payment Ledger Entry")
acc = frappe.qb.DocType("Account")
query = (
frappe.qb.from_(ple)
.select(ple.party, Abs(Sum(ple.amount).as_("amount")))
.where(
(ple.party_type.isin(party_type))
& (ple.amount < 0)
& (ple.against_voucher_no == ple.voucher_no)
& (ple.delinked == 0)
)
.inner_join(acc)
.on(ple.account == acc.name)
.select(ple.party)
.where((ple.party_type.isin(party_type)) & (acc.account_type == account_type) & (ple.delinked == 0))
.groupby(ple.party)
)
@@ -909,9 +916,32 @@ def get_partywise_advanced_payment_amount(
if invoice_doctypes := frappe.get_hooks("invoice_doctypes"):
query = query.where(ple.voucher_type.notin(invoice_doctypes))
data = query.run()
if data:
return frappe._dict(data)
# Get advance amount from Receivable / Payable Account
party_ledger = query.select(Abs(Sum(ple.amount).as_("amount")))
party_ledger = party_ledger.where(ple.amount < 0)
party_ledger = party_ledger.where(ple.against_voucher_no == ple.voucher_no)
party_ledger = party_ledger.where(
acc.root_type == ("Liability" if account_type == "Payable" else "Asset")
)
data = party_ledger.run()
data = frappe._dict(data or {})
# Get advance amount from Advance Account
advance_ledger = query.select(Sum(ple.amount).as_("amount"), ple.account)
advance_ledger = advance_ledger.where(
acc.root_type == ("Asset" if account_type == "Payable" else "Liability")
)
advance_ledger = advance_ledger.groupby(ple.account)
advance_ledger = advance_ledger.having(Sum(ple.amount) < 0)
advance_data = advance_ledger.run()
for row in advance_data:
data.setdefault(row[0], 0)
data[row[0]] += abs(row[1])
return data
def get_default_contact(doctype: str, name: str) -> str | None:
@@ -958,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

@@ -310,8 +310,8 @@ class ReceivablePayableReport:
must_consider = False
if self.filters.get("for_revaluation_journals"):
if (abs(row.outstanding) >= 0.0 / 10**self.currency_precision) or (
abs(row.outstanding_in_account_currency) >= 0.0 / 10**self.currency_precision
if (abs(row.outstanding) >= 1.0 / 10**self.currency_precision) or (
abs(row.outstanding_in_account_currency) >= 1.0 / 10**self.currency_precision
):
must_consider = True
else:
@@ -385,6 +385,7 @@ class ReceivablePayableReport:
self.delivery_notes = frappe._dict()
# delivery note link inside sales invoice
# nosemgrep
si_against_dn = frappe.db.sql(
"""
select parent, delivery_note
@@ -400,6 +401,7 @@ class ReceivablePayableReport:
if d.delivery_note:
self.delivery_notes.setdefault(d.parent, set()).add(d.delivery_note)
# nosemgrep
dn_against_si = frappe.db.sql(
"""
select distinct parent, against_sales_invoice
@@ -417,13 +419,16 @@ class ReceivablePayableReport:
def get_invoice_details(self):
self.invoice_details = frappe._dict()
if self.account_type == "Receivable":
# nosemgrep
si_list = frappe.db.sql(
"""
select name, due_date, po_no
from `tabSales Invoice`
where posting_date <= %s
and company = %s
and docstatus = 1
""",
self.filters.report_date,
(self.filters.report_date, self.filters.company),
as_dict=1,
)
for d in si_list:
@@ -431,6 +436,7 @@ class ReceivablePayableReport:
# Get Sales Team
if self.filters.show_sales_person:
# nosemgrep
sales_team = frappe.db.sql(
"""
select parent, sales_person
@@ -445,25 +451,33 @@ class ReceivablePayableReport:
)
if self.account_type == "Payable":
# nosemgrep
for pi in frappe.db.sql(
"""
select name, due_date, bill_no, bill_date
from `tabPurchase Invoice`
where posting_date <= %s
where
posting_date <= %s
and company = %s
and docstatus = 1
""",
self.filters.report_date,
(self.filters.report_date, self.filters.company),
as_dict=1,
):
self.invoice_details.setdefault(pi.name, pi)
# Invoices booked via Journal Entries
# nosemgrep
journal_entries = frappe.db.sql(
"""
select name, due_date, bill_no, bill_date
from `tabJournal Entry`
where posting_date <= %s
where
posting_date <= %s
and company = %s
and docstatus = 1
""",
self.filters.report_date,
(self.filters.report_date, self.filters.company),
as_dict=1,
)
@@ -472,6 +486,8 @@ class ReceivablePayableReport:
self.invoice_details.setdefault(je.name, je)
def set_party_details(self, row):
if not row.party:
return
# customer / supplier name
party_details = self.get_party_details(row.party) or {}
row.update(party_details)
@@ -496,6 +512,7 @@ class ReceivablePayableReport:
def get_payment_terms(self, row):
# build payment_terms for row
# nosemgrep
payment_terms_details = frappe.db.sql(
f"""
select
@@ -505,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,
@@ -708,6 +726,7 @@ class ReceivablePayableReport:
def get_return_entries(self):
doctype = "Sales Invoice" if self.account_type == "Receivable" else "Purchase Invoice"
filters = {
"posting_date": ("<=", self.filters.report_date),
"is_return": 1,
"docstatus": 1,
"company": self.filters.company,
@@ -815,6 +834,7 @@ class ReceivablePayableReport:
if self.filters.get("sales_person"):
lft, rgt = frappe.db.get_value("Sales Person", self.filters.get("sales_person"), ["lft", "rgt"])
# nosemgrep
records = frappe.db.sql(
"""
select distinct parent, parenttype
@@ -993,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",
@@ -1017,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,
@@ -1046,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

@@ -36,6 +36,7 @@ def get_group_by_asset_category_data(filters):
+ flt(row.cost_of_new_purchase)
- flt(row.cost_of_sold_asset)
- flt(row.cost_of_scrapped_asset)
- flt(row.cost_of_capitalized_asset)
)
row.update(
@@ -68,7 +69,7 @@ def get_group_by_asset_category_data(filters):
def get_asset_categories_for_grouped_by_category(filters):
condition = ""
if filters.get("asset_category"):
condition += " and asset_category = %(asset_category)s"
condition += " and a.asset_category = %(asset_category)s"
if filters.get("finance_book"):
condition += " and exists (select 1 from `tabAsset Depreciation Schedule` ads where ads.asset = a.name and ads.finance_book = %(finance_book)s)"
@@ -111,10 +112,26 @@ def get_asset_categories_for_grouped_by_category(filters):
end
else
0
end), 0) as cost_of_scrapped_asset
end), 0) as cost_of_scrapped_asset,
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0
and a.disposal_date >= %(from_date)s
and a.disposal_date <= %(to_date)s then
case when a.status = "Capitalized" then
a.gross_purchase_amount
else
0
end
else
0
end), 0) as cost_of_capitalized_asset
from `tabAsset` a
where docstatus=1 and company=%(company)s and purchase_date <= %(to_date)s {condition}
and not exists(select name from `tabAsset Capitalization Asset Item` where asset = a.name)
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s {condition}
and not exists(
select 1 from `tabAsset Capitalization Asset Item` acai join `tabAsset Capitalization` ac on acai.parent=ac.name
where acai.asset = a.name
and ac.posting_date < %(from_date)s
and ac.docstatus=1
)
group by a.asset_category
""",
{
@@ -131,53 +148,70 @@ def get_asset_categories_for_grouped_by_category(filters):
def get_asset_details_for_grouped_by_category(filters):
condition = ""
if filters.get("asset"):
condition += " and name = %(asset)s"
condition += " and a.name = %(asset)s"
if filters.get("finance_book"):
condition += " and exists (select 1 from `tabAsset Depreciation Schedule` ads where ads.asset = `tabAsset`.name and ads.finance_book = %(finance_book)s)"
condition += " and exists (select 1 from `tabAsset Depreciation Schedule` ads where ads.asset = a.name and ads.finance_book = %(finance_book)s)"
# nosemgrep
return frappe.db.sql(
f"""
SELECT name,
ifnull(sum(case when purchase_date < %(from_date)s then
case when ifnull(disposal_date, 0) = 0 or disposal_date >= %(from_date)s then
gross_purchase_amount
SELECT a.name,
ifnull(sum(case when a.purchase_date < %(from_date)s then
case when ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s then
a.gross_purchase_amount
else
0
end
else
0
end), 0) as cost_as_on_from_date,
ifnull(sum(case when purchase_date >= %(from_date)s then
gross_purchase_amount
ifnull(sum(case when a.purchase_date >= %(from_date)s then
a.gross_purchase_amount
else
0
end), 0) as cost_of_new_purchase,
ifnull(sum(case when ifnull(disposal_date, 0) != 0
and disposal_date >= %(from_date)s
and disposal_date <= %(to_date)s then
case when status = "Sold" then
gross_purchase_amount
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0
and a.disposal_date >= %(from_date)s
and a.disposal_date <= %(to_date)s then
case when a.status = "Sold" then
a.gross_purchase_amount
else
0
end
else
0
end), 0) as cost_of_sold_asset,
ifnull(sum(case when ifnull(disposal_date, 0) != 0
and disposal_date >= %(from_date)s
and disposal_date <= %(to_date)s then
case when status = "Scrapped" then
gross_purchase_amount
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0
and a.disposal_date >= %(from_date)s
and a.disposal_date <= %(to_date)s then
case when a.status = "Scrapped" then
a.gross_purchase_amount
else
0
end
else
0
end), 0) as cost_of_scrapped_asset
from `tabAsset`
where docstatus=1 and company=%(company)s and purchase_date <= %(to_date)s {condition}
group by name
end), 0) as cost_of_scrapped_asset,
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0
and a.disposal_date >= %(from_date)s
and a.disposal_date <= %(to_date)s then
case when a.status = "Capitalized" then
a.gross_purchase_amount
else
0
end
else
0
end), 0) as cost_of_capitalized_asset
from `tabAsset` a
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s {condition}
and not exists(
select 1 from `tabAsset Capitalization Asset Item` acai join `tabAsset Capitalization` ac on acai.parent=ac.name
where acai.asset = a.name
and ac.posting_date < %(from_date)s
and ac.docstatus=1
)
group by a.name
""",
{
"to_date": filters.to_date,
@@ -206,6 +240,7 @@ def get_group_by_asset_data(filters):
+ flt(row.cost_of_new_purchase)
- flt(row.cost_of_sold_asset)
- flt(row.cost_of_scrapped_asset)
- flt(row.cost_of_capitalized_asset)
)
row.update(next(asset for asset in assets if asset["asset"] == asset_detail.get("name", "")))
@@ -232,9 +267,15 @@ def get_group_by_asset_data(filters):
def get_assets_for_grouped_by_category(filters):
condition = ""
if filters.get("asset_category"):
condition = " and a.asset_category = '{}'".format(filters.get("asset_category"))
condition = f" and a.asset_category = '{filters.get('asset_category')}'"
finance_book_filter = ""
if filters.get("finance_book"):
finance_book_filter += " and ifnull(gle.finance_book, '')=%(finance_book)s"
condition += " and exists (select 1 from `tabAsset Depreciation Schedule` ads where ads.asset = a.name and ads.finance_book = %(finance_book)s)"
# nosemgrep
return frappe.db.sql(
"""
f"""
SELECT results.asset_category,
sum(results.accumulated_depreciation_as_on_from_date) as accumulated_depreciation_as_on_from_date,
sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period,
@@ -264,7 +305,14 @@ def get_assets_for_grouped_by_category(filters):
aca.parent = a.asset_category and aca.company_name = %(company)s
join `tabCompany` company on
company.name = %(company)s
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and gle.debit != 0 and gle.is_cancelled = 0 and gle.account = ifnull(aca.depreciation_expense_account, company.depreciation_expense_account) {0}
where
a.docstatus=1
and a.company=%(company)s
and a.purchase_date <= %(to_date)s
and gle.debit != 0
and gle.is_cancelled = 0
and gle.account = ifnull(aca.depreciation_expense_account, company.depreciation_expense_account)
{condition} {finance_book_filter}
group by a.asset_category
union
SELECT a.asset_category,
@@ -280,11 +328,16 @@ def get_assets_for_grouped_by_category(filters):
end), 0) as depreciation_eliminated_during_the_period,
0 as depreciation_amount_during_the_period
from `tabAsset` a
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s {0}
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s {condition}
group by a.asset_category) as results
group by results.asset_category
""".format(condition),
{"to_date": filters.to_date, "from_date": filters.from_date, "company": filters.company},
""",
{
"to_date": filters.to_date,
"from_date": filters.from_date,
"company": filters.company,
"finance_book": filters.get("finance_book", ""),
},
as_dict=1,
)
@@ -292,9 +345,15 @@ def get_assets_for_grouped_by_category(filters):
def get_assets_for_grouped_by_asset(filters):
condition = ""
if filters.get("asset"):
condition = " and a.name = '{}'".format(filters.get("asset"))
condition = f" and a.name = '{filters.get('asset')}'"
finance_book_filter = ""
if filters.get("finance_book"):
finance_book_filter += " and ifnull(gle.finance_book, '')=%(finance_book)s"
condition += " and exists (select 1 from `tabAsset Depreciation Schedule` ads where ads.asset = a.name and ads.finance_book = %(finance_book)s)"
# nosemgrep
return frappe.db.sql(
"""
f"""
SELECT results.name as asset,
sum(results.accumulated_depreciation_as_on_from_date) as accumulated_depreciation_as_on_from_date,
sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period,
@@ -324,7 +383,14 @@ def get_assets_for_grouped_by_asset(filters):
aca.parent = a.asset_category and aca.company_name = %(company)s
join `tabCompany` company on
company.name = %(company)s
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and gle.debit != 0 and gle.is_cancelled = 0 and gle.account = ifnull(aca.depreciation_expense_account, company.depreciation_expense_account) {0}
where
a.docstatus=1
and a.company=%(company)s
and a.purchase_date <= %(to_date)s
and gle.debit != 0
and gle.is_cancelled = 0
and gle.account = ifnull(aca.depreciation_expense_account, company.depreciation_expense_account)
{finance_book_filter} {condition}
group by a.name
union
SELECT a.name as name,
@@ -340,11 +406,16 @@ def get_assets_for_grouped_by_asset(filters):
end), 0) as depreciation_eliminated_during_the_period,
0 as depreciation_amount_during_the_period
from `tabAsset` a
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s {0}
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s {condition}
group by a.name) as results
group by results.name
""".format(condition),
{"to_date": filters.to_date, "from_date": filters.from_date, "company": filters.company},
""",
{
"to_date": filters.to_date,
"from_date": filters.from_date,
"company": filters.company,
"finance_book": filters.get("finance_book", ""),
},
as_dict=1,
)
@@ -398,6 +469,12 @@ def get_columns(filters):
"fieldtype": "Currency",
"width": 140,
},
{
"label": _("Cost of New Capitalized Asset"),
"fieldname": "cost_of_capitalized_asset",
"fieldtype": "Currency",
"width": 140,
},
{
"label": _("Cost as on") + " " + formatdate(filters.to_date),
"fieldname": "cost_as_on_to_date",

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,
@@ -95,12 +96,15 @@ def execute(filters=None):
filters.periodicity, period_list, filters.accumulated_values, company=filters.company
)
chart = get_chart_data(filters, columns, asset, liability, equity)
chart = get_chart_data(filters, columns, asset, liability, equity, currency)
report_summary, primitive_summary = get_report_summary(
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
@@ -122,13 +126,13 @@ def get_provisional_profit_loss(
for period in period_list:
key = period if consolidated else period.key
total_assets = flt(asset[0].get(key))
total_assets = flt(asset[-2].get(key))
effective_liability = 0.00
if liability:
effective_liability += flt(liability[0].get(key))
if equity:
effective_liability += flt(equity[0].get(key))
if liability and liability[-1] == {}:
effective_liability += flt(liability[-2].get(key))
if equity and equity[-1] == {}:
effective_liability += flt(equity[-2].get(key))
provisional_profit_loss[key] = total_assets - effective_liability
total_row[key] = provisional_profit_loss[key] + effective_liability
@@ -195,9 +199,9 @@ def get_report_summary(
key = period if consolidated else period.key
if asset:
net_asset += asset[-2].get(key)
if liability:
if liability and liability[-1] == {}:
net_liability += liability[-2].get(key)
if equity:
if equity and equity[-1] == {}:
net_equity += equity[-2].get(key)
if provisional_profit_loss:
net_provisional_profit_loss += provisional_profit_loss.get(key)
@@ -221,7 +225,7 @@ def get_report_summary(
], (net_asset - net_liability + net_equity)
def get_chart_data(filters, columns, asset, liability, equity):
def get_chart_data(filters, columns, asset, liability, equity, currency):
labels = [d.get("label") for d in columns[2:]]
asset_data, liability_data, equity_data = [], [], []
@@ -249,4 +253,8 @@ def get_chart_data(filters, columns, asset, liability, equity):
else:
chart["type"] = "line"
chart["fieldtype"] = "Currency"
chart["options"] = "currency"
chart["currency"] = currency
return chart

View File

@@ -47,7 +47,7 @@ frappe.query_reports["Bank Reconciliation Statement"] = {
},
],
formatter: function (value, row, column, data, default_formatter, filter) {
if (column.fieldname == "payment_entry" && value == "Cheques and Deposits incorrectly cleared") {
if (column.fieldname == "payment_entry" && value == __("Cheques and Deposits incorrectly cleared")) {
column.link_onclick =
"frappe.query_reports['Bank Reconciliation Statement'].open_utility_report()";
}

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,9 +118,9 @@ 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)
chart = get_chart_data(columns, data, company_currency)
report_summary = get_report_summary(summary_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)
@@ -252,20 +261,23 @@ def get_report_summary(summary_data, currency):
return report_summary
def get_chart_data(columns, data):
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]
chart = {"data": {"labels": labels, "datasets": datasets}, "type": "bar"}
chart["fieldtype"] = "Currency"
chart["options"] = "currency"
chart["currency"] = currency
return chart

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