Compare commits

...

445 Commits

Author SHA1 Message Date
Frappe PR Bot
08f47b626c chore(release): Bumped to Version 15.54.4
## [15.54.4](https://github.com/frappe/erpnext/compare/v15.54.3...v15.54.4) (2025-03-12)

### Bug Fixes

* `base_net_rate` Required to Check Valid Range (backport [#46332](https://github.com/frappe/erpnext/issues/46332)) ([#46382](https://github.com/frappe/erpnext/issues/46382)) ([877d5bd](877d5bd3aa))
* **account:** update account number from parent company ([428aedc](428aedc29c))
* Allow rename prospect doctype ([#46352](https://github.com/frappe/erpnext/issues/46352)) ([de46165](de46165768))
* auto email report creation ([#46343](https://github.com/frappe/erpnext/issues/46343)) ([5cc251a](5cc251a172))
* backport translations from develop ([#46428](https://github.com/frappe/erpnext/issues/46428)) ([9c70376](9c703765a1))
* calculate due date based on payment term (backport [#46416](https://github.com/frappe/erpnext/issues/46416)) ([#46479](https://github.com/frappe/erpnext/issues/46479)) ([7f14744](7f147446df))
* change fieldname for cash_flow to export (backport [#46353](https://github.com/frappe/erpnext/issues/46353)) ([#46366](https://github.com/frappe/erpnext/issues/46366)) ([23c4252](23c4252b9b))
* check if set_landed_cost_based_on_purchase_invoice_rate is enabled before running patch ([7047fe2](7047fe2681))
* clear cashe on employee hierarchy change to reflect updated permissions ([4dfdb2b](4dfdb2b0a1))
* consider account freeze date in recalculate_amount_difference_field patch ([8b67527](8b67527900))
* consider stock freeze date in recalculate_amount_difference_field patch ([8264d42](8264d42cd9))
* credit note creation during pos invoice consolidation (backport [#46277](https://github.com/frappe/erpnext/issues/46277)) ([#46469](https://github.com/frappe/erpnext/issues/46469)) ([a4b8b4c](a4b8b4c771))
* do not recalculate qty for batch items during reposting ([bac36f3](bac36f342d))
* doctype name ([1dcbdf3](1dcbdf3257))
* enable no copy for serial no field ([3f9df2f](3f9df2fb2d))
* error in production analytics report ([db6ae61](db6ae61935))
* error when creating delivery note from pick list ([#46417](https://github.com/frappe/erpnext/issues/46417)) ([0b92101](0b921016ff))
* filter batches that going to be zero ([ac97489](ac97489a32))
* incorrect category in list ([002685f](002685fc89))
* make 'company_tax_id' and 'company_fiscal_code' as mandatory ([229f4d3](229f4d3d92))
* not able to save work order with alternative item ([9554a49](9554a49bbd))
* **payment entry:** fetch default bank account based on company (backport [#46379](https://github.com/frappe/erpnext/issues/46379)) ([#46471](https://github.com/frappe/erpnext/issues/46471)) ([1371199](13711993fe))
* pricing rule not ignored in Sales Order ([#46248](https://github.com/frappe/erpnext/issues/46248)) ([8def42f](8def42f751))
* rare precision issue preventing submission of subcontracting order ([6419d02](6419d020a1))
* recalculate_amount_difference_field patch ([f247f02](f247f02e49))
* remove no copy for serial no field of purchase receipt item ([baa564f](baa564fc94))
* rename sla fields patch ([73f11cf](73f11cf19e))
* rename sla fields patch ([#46465](https://github.com/frappe/erpnext/issues/46465)) ([5edbd88](5edbd8851a))
* rename_sla_fields patch ([7bc7557](7bc7557018))
* run bank reconciliation as a background job to prevent request timeout ([739cd18](739cd18604))
* set correct account currency for deferred expence account in PI ([f96848a](f96848a3b9))
* show remaining qty on 'Complete Job' button instead of full qty ([79e6550](79e6550321))
* sla fields patch ([0d044bc](0d044bc5bb))
* stock balance in and out value ([c2001e9](c2001e9c67))
* **test:** incorrect transaction exchange rate in test case ([b76c968](b76c96820e))
* typo in sales_invoice_print ([b610621](b6106212c1))
* uom reverts to default upon selecting do not explode ([#45693](https://github.com/frappe/erpnext/issues/45693)) ([6b1d209](6b1d20970e))
* validate accounting dimension company in Journal Entry & Stock Entry (backport [#46204](https://github.com/frappe/erpnext/issues/46204)) ([#46369](https://github.com/frappe/erpnext/issues/46369)) ([c816f9b](c816f9bd0a))
* validate last_gl_update exists before comparing (backport [#46464](https://github.com/frappe/erpnext/issues/46464)) ([#46468](https://github.com/frappe/erpnext/issues/46468)) ([3cef94e](3cef94e2ed))
* validations and account type filter for `Tax Withholding Category` ([#46207](https://github.com/frappe/erpnext/issues/46207)) ([cc30a01](cc30a01898))
2025-03-12 14:37:29 +00:00
ruthra kumar
0283f7526c Merge pull request #46444 from frappe/version-15-hotfix
chore: release v15
2025-03-12 20:05:56 +05:30
ruthra kumar
3ad451dd6e Merge branch 'version-15' into version-15-hotfix 2025-03-12 19:48:08 +05:30
rohitwaghchaure
9e409bde2e Merge pull request #46486 from frappe/st33357
fix: enable no copy for serial no field
2025-03-12 19:39:32 +05:30
ruthra kumar
8459166323 Merge pull request #46482 from frappe/mergify/bp/version-15-hotfix/pr-46207
fix: validations and account type filter for `Tax Withholding Category` (backport #46207)
2025-03-12 17:46:04 +05:30
ruthra kumar
e1328de712 Merge pull request #46487 from frappe/mergify/bp/version-15-hotfix/pr-46251
fix(bank-reconciliation): run bank reconciliation as a background job (backport #46251)
2025-03-12 17:45:45 +05:30
mergify[bot]
d3a2350b3e fix(invoice):validate return invoice qty (backport #46451) (#46481)
fix(invoice):validate return invoice qty (#46451)

* fix(invoice): validate return quantity when update stock is unchecked

* test: add unit test for validating fully returned invoice quantity

(cherry picked from commit ba96c86576)

Co-authored-by: Bhavansathru <122002510+Bhavan23@users.noreply.github.com>
2025-03-12 17:23:26 +05:30
Bhavan23
739cd18604 fix: run bank reconciliation as a background job to prevent request timeout
(cherry picked from commit a29c6a5aea)
2025-03-12 11:52:26 +00:00
ruthra kumar
e61cc9b12e Merge pull request #46483 from frappe/mergify/bp/version-15-hotfix/pr-46248
fix: pricing rule not ignored in Sales Order (backport #46248)
2025-03-12 17:13:13 +05:30
Mihir Kandoi
baa564fc94 fix: remove no copy for serial no field of purchase receipt item 2025-03-12 17:11:14 +05:30
rohitwaghchaure
a55ec56fbf Merge pull request #46484 from frappe/45440
fix: show remaining qty on 'Complete Job' button instead of full qty
2025-03-12 16:44:57 +05:30
Mihir Kandoi
3f9df2fb2d fix: enable no copy for serial no field 2025-03-12 16:35:50 +05:30
mergify[bot]
7f147446df fix: calculate due date based on payment term (backport #46416) (#46479)
fix: calculate due date based on payment term (#46416)

(cherry picked from commit 9e808c832f)

Co-authored-by: Sudharsanan Ashok <135326972+Sudharsanan11@users.noreply.github.com>
2025-03-12 16:30:14 +05:30
Sugesh G
8def42f751 fix: pricing rule not ignored in Sales Order (#46248)
* fix: pricing rule not ignored in Sales Order

* test: update parameter do_not_submit to do_not_save

(cherry picked from commit f8c659d8d5)
2025-03-12 10:58:47 +00:00
Mihir Kandoi
79e6550321 fix: show remaining qty on 'Complete Job' button instead of full qty 2025-03-12 16:27:33 +05:30
Priyansh Shah
cc30a01898 fix: validations and account type filter for Tax Withholding Category (#46207)
fix: validations and account type filter for tax withholding category
(cherry picked from commit d371236684)
2025-03-12 10:52:32 +00:00
Ninad Parikh
877d5bd3aa fix: base_net_rate Required to Check Valid Range (backport #46332) (#46382)
fix: `base_net_rate` required to check valid range
2025-03-12 16:12:44 +05:30
Mihir Kandoi
0b53bd3e9a Merge pull request #46475 from frappe/mergify/bp/version-15-hotfix/pr-46417
fix: error when creating delivery note from pick list (backport #46417)
2025-03-12 16:09:50 +05:30
Mihir Kandoi
3606fe8fba Merge pull request #46467 from frappe/mergify/bp/version-15-hotfix/pr-46465
fix: rename sla fields patch (backport #46465)
2025-03-12 16:09:37 +05:30
Mihir Kandoi
0b921016ff fix: error when creating delivery note from pick list (#46417)
(cherry picked from commit 67e9389a02)
2025-03-12 10:00:53 +00:00
rohitwaghchaure
4d49608a68 Merge pull request #46474 from frappe/mergify/bp/version-15-hotfix/pr-46470
fix: do not recalculate qty for batch items during reposting (backport #46470)
2025-03-12 12:39:06 +05:30
Rohit Waghchaure
bac36f342d fix: do not recalculate qty for batch items during reposting
(cherry picked from commit 0753c018d2)
2025-03-12 06:48:00 +00:00
mergify[bot]
a4b8b4c771 fix: credit note creation during pos invoice consolidation (backport #46277) (#46469)
* fix: credit note creation during pos invoice consolidation (#46277)

* fix: credit note creation during pos invoice consolidation

* fix: added check to skip merging empty list of return pos invoices

* fix: sql query

* fix: using return invoice name instead of return invoice object

* fix: added pos invoice field in sales invoice item

(cherry picked from commit 8ba4ac3b86)

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

* chore: resolve conflict

---------

Co-authored-by: Diptanil Saha <diptanil@frappe.io>
2025-03-12 11:41:41 +05:30
mergify[bot]
3cef94e2ed fix: validate last_gl_update exists before comparing (backport #46464) (#46468)
fix: validate last_gl_update exists before comparing (#46464)

(cherry picked from commit 0a5ca0c35f)

Co-authored-by: Dany Robert <rtdany10@gmail.com>
2025-03-12 11:05:24 +05:30
mergify[bot]
13711993fe fix(payment entry): fetch default bank account based on company (backport #46379) (#46471)
fix(payment entry): fetch default bank account based on company (#46379)

(cherry picked from commit b72f6f5a3d)

Co-authored-by: Venkatesh <47534423+venkat102@users.noreply.github.com>
2025-03-12 11:05:08 +05:30
Nabin Hait
24678b0e24 chore: conflicts 2025-03-12 10:38:26 +05:30
Mihir Kandoi
5edbd8851a fix: rename sla fields patch (#46465)
* fix: rename sla fields patch

* fix: rerun patch

(cherry picked from commit 8bce42e633)

# Conflicts:
#	erpnext/patches.txt
2025-03-12 05:04:52 +00:00
rohitwaghchaure
6582e4c5f9 Merge pull request #46462 from frappe/mergify/bp/version-15-hotfix/pr-46455
fix: error in production analytics report (backport #46455)
2025-03-12 09:50:35 +05:30
rohitwaghchaure
fd1c1ba35e Merge pull request #46461 from frappe/mergify/bp/version-15-hotfix/pr-46460
fix: rename sla fields patch (backport #46460)
2025-03-12 09:49:46 +05:30
Mihir Kandoi
db6ae61935 fix: error in production analytics report
(cherry picked from commit 03e66468f6)
2025-03-11 19:00:35 +00:00
Mihir Kandoi
73f11cf19e fix: rename sla fields patch
(cherry picked from commit b6c18849c5)
2025-03-11 18:57:17 +00:00
rohitwaghchaure
8f7dc827ea Merge pull request #46459 from frappe/mergify/bp/version-15-hotfix/pr-46452
fix: sla fields patch (backport #46452)
2025-03-12 00:01:35 +05:30
Mihir Kandoi
0d044bc5bb fix: sla fields patch
(cherry picked from commit d653899372)
2025-03-11 18:08:24 +00:00
rohitwaghchaure
14ee2d239a Merge pull request #46441 from frappe/mergify/bp/version-15-hotfix/pr-46436
fix: filter batches that going to be zero (backport #46436)
2025-03-11 23:38:21 +05:30
Raffael Meyer
fc8eeaf4f6 Merge pull request #46457 from frappe/mergify/bp/version-15-hotfix/pr-46453
ci: ignore PRs labeled with "skip-release-notes" when generating release notes (backport #46453)
2025-03-11 17:51:58 +01:00
barredterra
aad8c88532 ci: ignore PRs labeled with "skip-release-notes" when generating release notes
(cherry picked from commit 57007bf937)
2025-03-11 16:35:20 +00:00
ruthra kumar
e783536ba0 Merge pull request #46450 from frappe/mergify/bp/version-15-hotfix/pr-46398
chore: rename print and stationery account (backport #46398)
2025-03-11 18:12:19 +05:30
chethank1407
ee3feba386 chore: rename print and stationery account
(cherry picked from commit 615997b774)
2025-03-11 12:03:42 +00:00
ruthra kumar
cb9be11448 Merge pull request #46447 from frappe/mergify/bp/version-15-hotfix/pr-46344
fix: make 'company_tax_id' and 'company_fiscal_code' as mandatory (backport #46344)
2025-03-11 17:31:19 +05:30
ruthra kumar
31dc6021e2 chore: translatable strings
(cherry picked from commit 121798ba85)
2025-03-11 11:42:30 +00:00
Bhavan23
229f4d3d92 fix: make 'company_tax_id' and 'company_fiscal_code' as mandatory
(cherry picked from commit abd044eb0d)
2025-03-11 11:42:19 +00:00
ruthra kumar
c25862a85f Merge pull request #46446 from frappe/mergify/bp/version-15-hotfix/pr-45818
fix: set correct account currency for deferred expense account (backport #45818)
2025-03-11 16:50:55 +05:30
vishakhdesai
f96848a3b9 fix: set correct account currency for deferred expence account in PI
(cherry picked from commit 398083853c)
2025-03-11 11:06:14 +00:00
Rohit Waghchaure
ac97489a32 fix: filter batches that going to be zero
(cherry picked from commit aba512c1c6)
2025-03-11 08:02:49 +00:00
ruthra kumar
02ff406b7c Merge pull request #46439 from frappe/mergify/bp/version-15-hotfix/pr-46372
fix(account): update account number from parent company (backport #46372)
2025-03-11 11:19:33 +05:30
venkat102
428aedc29c fix(account): update account number from parent company
(cherry picked from commit 4a4894bc01)
2025-03-11 05:32:15 +00:00
ruthra kumar
6af24dca6e Merge pull request #46432 from frappe/mergify/bp/version-15-hotfix/pr-46427
fix: not able to save work order with alternative item (backport #46414) (backport #46427)
2025-03-11 11:02:04 +05:30
ruthra kumar
8980eb9b9d Merge pull request #46437 from frappe/mergify/bp/version-15-hotfix/pr-46426
fix: clear cashe on employee hierarchy change (backport #46426)
2025-03-11 10:58:50 +05:30
Asmita Hase
4dfdb2b0a1 fix: clear cashe on employee hierarchy change to reflect updated permissions
(cherry picked from commit 6789578b27)
2025-03-11 05:13:04 +00:00
Rohit Waghchaure
9554a49bbd fix: not able to save work order with alternative item
(cherry picked from commit 6ca1f9bc73)
(cherry picked from commit ac7fc608aa)
2025-03-10 17:44:57 +00:00
rohitwaghchaure
5b69445294 Merge pull request #46420 from frappe/mergify/bp/version-15-hotfix/pr-46418
fix: stock balance in and out value (backport #46418)
2025-03-10 23:13:37 +05:30
Raffael Meyer
9c703765a1 fix: backport translations from develop (#46428) 2025-03-10 14:32:19 +01:00
rohitwaghchaure
043539fcdb chore: fix conflicts 2025-03-10 17:37:29 +05:30
ruthra kumar
54a76d8932 Merge pull request #46425 from frappe/mergify/bp/version-15-hotfix/pr-46407
fix: typo in sales_invoice_print (backport #46407)
2025-03-10 16:55:28 +05:30
mahsem
b6106212c1 fix: typo in sales_invoice_print
(cherry picked from commit f7bcae83e4)
2025-03-10 11:18:23 +00:00
Rohit Waghchaure
c2001e9c67 fix: stock balance in and out value
(cherry picked from commit e917bd5334)

# Conflicts:
#	erpnext/stock/report/stock_balance/stock_balance.py
2025-03-10 10:06:09 +00:00
Frappe PR Bot
47429095a2 chore(release): Bumped to Version 15.54.3
## [15.54.3](https://github.com/frappe/erpnext/compare/v15.54.2...v15.54.3) (2025-03-09)

### Bug Fixes

* consider account freeze date in recalculate_amount_difference_field patch ([34f03d6](34f03d608a))
* consider stock freeze date in recalculate_amount_difference_field patch ([a18721d](a18721d21c))
2025-03-09 11:04:11 +00:00
rohitwaghchaure
2f3f87fe7e Merge pull request #46403 from frappe/mergify/bp/version-15/pr-46402
fix: consider account freeze date in recalculate_amount_difference_fi… (backport #46395) (backport #46402)
2025-03-09 16:32:52 +05:30
Mihir Kandoi
2b4dfca3ff chore: resolve conflicts
(cherry picked from commit 985fb5dfdc)
2025-03-08 17:16:43 +00:00
Mihir Kandoi
a18721d21c fix: consider stock freeze date in recalculate_amount_difference_field patch
(cherry picked from commit cd72532789)
(cherry picked from commit 8264d42cd9)
2025-03-08 17:16:43 +00:00
Mihir Kandoi
34f03d608a fix: consider account freeze date in recalculate_amount_difference_field patch
(cherry picked from commit 696f931678)

# Conflicts:
#	erpnext/patches/v15_0/recalculate_amount_difference_field.py
(cherry picked from commit 8b67527900)
2025-03-08 17:16:43 +00:00
rohitwaghchaure
b786cd30e6 Merge pull request #46402 from frappe/mergify/bp/version-15-hotfix/pr-46395
fix: consider account freeze date in recalculate_amount_difference_fi… (backport #46395)
2025-03-08 22:45:06 +05:30
Mihir Kandoi
985fb5dfdc chore: resolve conflicts 2025-03-08 22:22:12 +05:30
Mihir Kandoi
8264d42cd9 fix: consider stock freeze date in recalculate_amount_difference_field patch
(cherry picked from commit cd72532789)
2025-03-08 16:28:33 +00:00
Mihir Kandoi
8b67527900 fix: consider account freeze date in recalculate_amount_difference_field patch
(cherry picked from commit 696f931678)

# Conflicts:
#	erpnext/patches/v15_0/recalculate_amount_difference_field.py
2025-03-08 16:28:32 +00:00
Frappe PR Bot
2ce8299bc8 chore(release): Bumped to Version 15.54.2
## [15.54.2](https://github.com/frappe/erpnext/compare/v15.54.1...v15.54.2) (2025-03-08)

### Bug Fixes

* incorrect category in list ([3bdd4ce](3bdd4ce116))
* **test:** incorrect transaction exchange rate in test case ([3015628](3015628519))
2025-03-08 07:42:44 +00:00
ruthra kumar
84b03485d6 Merge pull request #46397 from frappe/mergify/bp/version-15/pr-46064
fix: Debit and Credit mismatch on transaction currency debit and credit values (backport #46064)
2025-03-08 13:11:25 +05:30
ruthra kumar
d0b14f1907 chore: resolve conflict 2025-03-08 12:28:49 +05:30
ruthra kumar
cd1803a74d refactor: internal transfer gl
(cherry picked from commit f1d8feec15)
2025-03-08 06:56:37 +00:00
ruthra kumar
a1cf27ec17 test: assert total debit and credit for trx currency
(cherry picked from commit 55d0636123)
2025-03-08 06:56:37 +00:00
ruthra kumar
61880a311a refactor: handle rounding diff for trx currency dr and cr
(cherry picked from commit 455a55b2ce)
2025-03-08 06:56:37 +00:00
ruthra kumar
2d290b153d refactor: trx currency dr and cr for tax rows and item rows
(cherry picked from commit 4cd3f3531c)
2025-03-08 06:56:36 +00:00
ruthra kumar
cacb720556 refactor: convert tax amount using exchange rate
(cherry picked from commit 7528ef147a)
2025-03-08 06:56:36 +00:00
ruthra kumar
3bdd4ce116 fix: incorrect category in list
(cherry picked from commit 6545467aec)
2025-03-08 06:56:36 +00:00
ruthra kumar
c479998cd6 chore: typo
(cherry picked from commit bc792c61e9)
2025-03-08 06:56:36 +00:00
ruthra kumar
501e388186 refactor: isolate to specific doctypes
(cherry picked from commit b348aa3b37)
2025-03-08 06:56:35 +00:00
ruthra kumar
af45ec0d6d refactor(test): save first to let the tax table populate
(cherry picked from commit 23d465805b)
2025-03-08 06:56:35 +00:00
ruthra kumar
3015628519 fix(test): incorrect transaction exchange rate in test case
(cherry picked from commit a31770d122)
2025-03-08 06:56:35 +00:00
ruthra kumar
8b6eea6349 refactor: set transaction currency and rate before gl map
(cherry picked from commit ceca5b4c72)
2025-03-08 06:56:34 +00:00
ruthra kumar
81c29e8f8c refactor: handle payment entry
(cherry picked from commit 5c86e3ce85)
2025-03-08 06:56:34 +00:00
ruthra kumar
04758d3de3 refactor: handle Journal entries
(cherry picked from commit 9f3847c0f8)
2025-03-08 06:56:34 +00:00
ruthra kumar
615b0c40a3 refactor: move utility method to controller
(cherry picked from commit d1d06885dc)
2025-03-08 06:56:34 +00:00
ruthra kumar
231abab321 refactor: set transaction currency dr/cr in sales invoice
(cherry picked from commit 3e292ef2cb)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice/sales_invoice.py
2025-03-08 06:56:33 +00:00
ruthra kumar
fff3b1e84e refactor: handle stocked items
(cherry picked from commit ee93ed8c97)
2025-03-08 06:56:33 +00:00
ruthra kumar
5299a1032b refactor: handle stocked items
(cherry picked from commit 7ff3977394)
2025-03-08 06:56:33 +00:00
ruthra kumar
1e5fbc0a48 refactor: set tr currency dr & cr directly on parent document
(cherry picked from commit e9af567033)
2025-03-08 06:56:32 +00:00
ruthra kumar
5c47c35a0f refactor: use highest precision for storing exc rate
(cherry picked from commit b115bf2e2a)
2025-03-08 06:56:32 +00:00
ruthra kumar
2bf910a786 Merge pull request #46396 from frappe/mergify/bp/version-15-hotfix/pr-46064
fix: Debit and Credit mismatch on transaction currency debit and credit values (backport #46064)
2025-03-08 12:25:57 +05:30
ruthra kumar
0b8673777a chore: resolve conflict 2025-03-08 12:11:54 +05:30
ruthra kumar
8f4c1e7169 refactor: internal transfer gl
(cherry picked from commit f1d8feec15)
2025-03-08 06:36:37 +00:00
ruthra kumar
f303245fae test: assert total debit and credit for trx currency
(cherry picked from commit 55d0636123)
2025-03-08 06:36:37 +00:00
ruthra kumar
cd21e5c652 refactor: handle rounding diff for trx currency dr and cr
(cherry picked from commit 455a55b2ce)
2025-03-08 06:36:37 +00:00
ruthra kumar
57e0f73595 refactor: trx currency dr and cr for tax rows and item rows
(cherry picked from commit 4cd3f3531c)
2025-03-08 06:36:37 +00:00
ruthra kumar
2c73e31742 refactor: convert tax amount using exchange rate
(cherry picked from commit 7528ef147a)
2025-03-08 06:36:36 +00:00
ruthra kumar
002685fc89 fix: incorrect category in list
(cherry picked from commit 6545467aec)
2025-03-08 06:36:36 +00:00
ruthra kumar
38a3a43ba5 chore: typo
(cherry picked from commit bc792c61e9)
2025-03-08 06:36:36 +00:00
ruthra kumar
07f938cc10 refactor: isolate to specific doctypes
(cherry picked from commit b348aa3b37)
2025-03-08 06:36:35 +00:00
ruthra kumar
5c013172f9 refactor(test): save first to let the tax table populate
(cherry picked from commit 23d465805b)
2025-03-08 06:36:35 +00:00
ruthra kumar
b76c96820e fix(test): incorrect transaction exchange rate in test case
(cherry picked from commit a31770d122)
2025-03-08 06:36:35 +00:00
ruthra kumar
1d56931050 refactor: set transaction currency and rate before gl map
(cherry picked from commit ceca5b4c72)
2025-03-08 06:36:34 +00:00
ruthra kumar
66dc79ceb5 refactor: handle payment entry
(cherry picked from commit 5c86e3ce85)
2025-03-08 06:36:34 +00:00
ruthra kumar
00cbc89b5f refactor: handle Journal entries
(cherry picked from commit 9f3847c0f8)
2025-03-08 06:36:33 +00:00
ruthra kumar
d7baa451e3 refactor: move utility method to controller
(cherry picked from commit d1d06885dc)
2025-03-08 06:36:33 +00:00
ruthra kumar
e0b5386bf0 refactor: set transaction currency dr/cr in sales invoice
(cherry picked from commit 3e292ef2cb)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice/sales_invoice.py
2025-03-08 06:36:33 +00:00
ruthra kumar
133dca1824 refactor: handle stocked items
(cherry picked from commit ee93ed8c97)
2025-03-08 06:36:32 +00:00
ruthra kumar
eb0df50f1b refactor: handle stocked items
(cherry picked from commit 7ff3977394)
2025-03-08 06:36:32 +00:00
ruthra kumar
5761cfb3d5 refactor: set tr currency dr & cr directly on parent document
(cherry picked from commit e9af567033)
2025-03-08 06:36:32 +00:00
ruthra kumar
568b582b6a refactor: use highest precision for storing exc rate
(cherry picked from commit b115bf2e2a)
2025-03-08 06:36:31 +00:00
Shariq Ansari
d115c1c156 Merge pull request #46365 from frappe/mergify/bp/version-15-hotfix/pr-46352
fix: Allow rename prospect doctype (backport #46352)
2025-03-07 17:12:22 +05:30
Nabin Hait
4e688778dc chore: linter issue 2025-03-07 10:28:52 +05:30
Mihir Kandoi
b5890c1d55 Merge pull request #46376 from frappe/mergify/bp/version-15-hotfix/pr-46374 2025-03-06 23:00:13 +05:30
Mihir Kandoi
6419d020a1 fix: rare precision issue preventing submission of subcontracting order
(cherry picked from commit 33b71544db)
2025-03-06 16:46:04 +00:00
Nabin Hait
60364f6dc9 chore: conflicts 2025-03-06 18:14:09 +05:30
mergify[bot]
23c4252b9b fix: change fieldname for cash_flow to export (backport #46353) (#46366)
fix: change fieldname for cash_flow to export (#46353)

fix: change fieldname for cash_flow

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

Co-authored-by: Sanket Shah <113279972+Sanket322@users.noreply.github.com>
2025-03-06 18:12:31 +05:30
mergify[bot]
c816f9bd0a fix: validate accounting dimension company in Journal Entry & Stock Entry (backport #46204) (#46369)
fix: validate accounting dimension company in Journal Entry & Stock Entry (#46204)

* fix: validate accounting dimension company in journal entry and stock entry

* test: update test cases to validate company-based accounting dimension

* fix(test): ensure 'Pick List' company matches 'Delivery Note' to prevent test failures

* chore: remove redundant lines of code

(cherry picked from commit 7b6ebad9e6)

Co-authored-by: Bhavansathru <122002510+Bhavan23@users.noreply.github.com>
2025-03-06 18:12:06 +05:30
Shariq Ansari
de46165768 fix: Allow rename prospect doctype (#46352)
fix: allow rename prospect doctype
(cherry picked from commit 884709deb8)

# Conflicts:
#	erpnext/crm/doctype/prospect/prospect.json
2025-03-06 11:50:08 +00:00
Justine Jay
5cc251a172 fix: auto email report creation (#46343)
* fix(financial_statements): mandatory based on filter_based_on value

* fix(financial_statements.js): include options for multiselect
2025-03-06 17:18:26 +05:30
Frappe PR Bot
7c8b34fd8f chore(release): Bumped to Version 15.54.1
## [15.54.1](https://github.com/frappe/erpnext/compare/v15.54.0...v15.54.1) (2025-03-06)

### Bug Fixes

* check if set_landed_cost_based_on_purchase_invoice_rate is enabled before running patch ([12bf31d](12bf31df87))
* recalculate_amount_difference_field patch ([041335f](041335f318))
* rename_sla_fields patch ([bb553c2](bb553c27ab))
2025-03-06 11:05:55 +00:00
rohitwaghchaure
83320c97fa Merge pull request #46360 from frappe/mergify/bp/version-15/pr-46357
fix: rename_sla_fields patch (backport #46355) (backport #46357)
2025-03-06 16:34:33 +05:30
rohitwaghchaure
b004865240 Merge pull request #46362 from frappe/mergify/bp/version-15/pr-46358
fix: recalculate_amount_difference_field patch (backport #46354) (backport #46358)
2025-03-06 16:34:20 +05:30
Mihir Kandoi
525780645a chore: resolve conflicts
(cherry picked from commit 8e65b0ec0c)
2025-03-06 10:42:01 +00:00
Mihir Kandoi
12bf31df87 fix: check if set_landed_cost_based_on_purchase_invoice_rate is enabled before running patch
(cherry picked from commit 95d1976931)

# Conflicts:
#	erpnext/patches/v15_0/recalculate_amount_difference_field.py
(cherry picked from commit 7047fe2681)
2025-03-06 10:42:01 +00:00
Mihir Kandoi
041335f318 fix: recalculate_amount_difference_field patch
(cherry picked from commit 0492b941ff)

# Conflicts:
#	erpnext/patches/v15_0/recalculate_amount_difference_field.py
(cherry picked from commit f247f02e49)
2025-03-06 10:42:00 +00:00
rohitwaghchaure
9c9c8c9356 Merge pull request #46358 from frappe/mergify/bp/version-15-hotfix/pr-46354
fix: recalculate_amount_difference_field patch (backport #46354)
2025-03-06 14:53:08 +05:30
Mihir Kandoi
8e65b0ec0c chore: resolve conflicts 2025-03-06 14:13:55 +05:30
Mihir Kandoi
bb553c27ab fix: rename_sla_fields patch
(cherry picked from commit e8d4a487c6)
(cherry picked from commit 7bc7557018)
2025-03-06 08:20:30 +00:00
rohitwaghchaure
7080e1422d Merge pull request #46357 from frappe/mergify/bp/version-15-hotfix/pr-46355
fix: rename_sla_fields patch (backport #46355)
2025-03-06 13:48:54 +05:30
Mihir Kandoi
7047fe2681 fix: check if set_landed_cost_based_on_purchase_invoice_rate is enabled before running patch
(cherry picked from commit 95d1976931)

# Conflicts:
#	erpnext/patches/v15_0/recalculate_amount_difference_field.py
2025-03-06 07:58:38 +00:00
Mihir Kandoi
f247f02e49 fix: recalculate_amount_difference_field patch
(cherry picked from commit 0492b941ff)

# Conflicts:
#	erpnext/patches/v15_0/recalculate_amount_difference_field.py
2025-03-06 07:58:37 +00:00
Mihir Kandoi
7bc7557018 fix: rename_sla_fields patch
(cherry picked from commit e8d4a487c6)
2025-03-06 07:49:12 +00:00
rohitwaghchaure
e72264f448 Merge pull request #46346 from frappe/mergify/bp/version-15-hotfix/pr-46339
fix: doctype name (backport #46339)
2025-03-05 21:44:40 +05:30
Rohit Waghchaure
1dcbdf3257 fix: doctype name
(cherry picked from commit d039310d80)
2025-03-05 15:48:13 +00:00
Mihir Kandoi
5d6730a059 Merge pull request #46345 from frappe/mergify/bp/version-15-hotfix/pr-45693
fix: uom reverts to default upon selecting do not explode (backport #45693)
2025-03-05 21:09:04 +05:30
Mihir Kandoi
6b1d20970e fix: uom reverts to default upon selecting do not explode (#45693)
* fix: uom reverts to default upon selecting do not explode

* fix: logical error failing tests

(cherry picked from commit 58ed697ba5)
2025-03-05 15:21:58 +00:00
Frappe PR Bot
528107e224 chore(release): Bumped to Version 15.54.0
# [15.54.0](https://github.com/frappe/erpnext/compare/v15.53.4...v15.54.0) (2025-03-05)

### Bug Fixes

* Accounting Period validation throwing for different companies ([6df9cf3](6df9cf327d))
* Add company filter at get_invoice method (backport [#46238](https://github.com/frappe/erpnext/issues/46238)) ([#46299](https://github.com/frappe/erpnext/issues/46299)) ([4f80ddd](4f80ddd834))
* Add permission check in POS's `Toggle Recent Orders` (backport [#46010](https://github.com/frappe/erpnext/issues/46010)) ([#46274](https://github.com/frappe/erpnext/issues/46274)) ([7759775](7759775ee6))
* adding cost center on pos invoice items while applying product discount (backport [#46082](https://github.com/frappe/erpnext/issues/46082)) ([#46322](https://github.com/frappe/erpnext/issues/46322)) ([9a433a6](9a433a6750))
* **asset depreciation schedules:** enable auto commit ([899e468](899e468f6a))
* auto allocation for negative amount outstanding for Customers in Payment Entry ([78a329e](78a329e573))
* Batch Price gets updated only if it is a billed item ([dbd47df](dbd47dff98))
* Batch Price gets updated only if it is a billed item ([8ed512f](8ed512f6c6))
* change voucher_type and voucher_no field type to data ([3a03865](3a03865a8f))
* Close and Reopen buttons dissapear after saving changes ([#46048](https://github.com/frappe/erpnext/issues/46048)) ([506dd3c](506dd3c6b9))
* consider journal entry and return invoice in paid_amount calculation (backport [#46129](https://github.com/frappe/erpnext/issues/46129)) ([#46319](https://github.com/frappe/erpnext/issues/46319)) ([836fd8f](836fd8fbc4))
* consolidate gl entries by project in General Ledger Report (backport [#46314](https://github.com/frappe/erpnext/issues/46314)) ([#46321](https://github.com/frappe/erpnext/issues/46321)) ([6aa8803](6aa8803068))
* Convert tuple of tuples to list of dicts for dot notation access ([#46062](https://github.com/frappe/erpnext/issues/46062)) ([ef19551](ef195513d0))
* delivery note from sales order uom conversion mistake ([d10add4](d10add4b1e))
* depreciation and balances report correction (backport [#46259](https://github.com/frappe/erpnext/issues/46259)) ([#46305](https://github.com/frappe/erpnext/issues/46305)) ([087dde5](087dde5873))
* discount accounting for v15 ([f609012](f609012f02))
* do not include opening invoices in billed items to be received report ([eee500f](eee500f20e))
* don't allow renaming account while system is actively in use (backport [#46176](https://github.com/frappe/erpnext/issues/46176)) ([#46210](https://github.com/frappe/erpnext/issues/46210)) ([faee8d6](faee8d6c5e))
* dont update rate of free item when batch is updated ([9e649d8](9e649d8522))
* error ([61d5680](61d5680c8d))
* exclude already consumed purchase receipt items from asset capitalization (backport [#46329](https://github.com/frappe/erpnext/issues/46329)) ([#46336](https://github.com/frappe/erpnext/issues/46336)) ([6c1ceff](6c1ceff8ee))
* exclude cancelled gl entries ([5e08386](5e083861a4))
* fiscal year error ([7b13d8c](7b13d8cd98))
* fixing test case ([9f4311e](9f4311e7fb))
* if invoice is return then add amount in proper column ([0a65217](0a65217423))
* Include additional account types for Expense Account in LCV (backport [#46206](https://github.com/frappe/erpnext/issues/46206)) ([#46296](https://github.com/frappe/erpnext/issues/46296)) ([88234bb](88234bbf9a))
* incorrect batch picked ([e94f0b1](e94f0b1cca))
* incorrect batch picked in the pick list (backport [#45761](https://github.com/frappe/erpnext/issues/45761)) ([#46315](https://github.com/frappe/erpnext/issues/46315)) ([5a3073c](5a3073c4c1))
* incorrectly billed amount in the purchase receipt ([c247cf8](c247cf888b))
* Naming of Purchase Amount (backport [#46051](https://github.com/frappe/erpnext/issues/46051)) ([#46324](https://github.com/frappe/erpnext/issues/46324)) ([aaf35c5](aaf35c5df9))
* only include submitted docs for internal received quantity validation (backport [#46262](https://github.com/frappe/erpnext/issues/46262)) ([#46304](https://github.com/frappe/erpnext/issues/46304)) ([5ae9faa](5ae9faab91))
* patch ([5e06e4a](5e06e4acce))
* patch ([8f2fdca](8f2fdcae88))
* patch path ([af49f5a](af49f5a8af))
* **patch:** Ensure SLE indexes (backport [#46131](https://github.com/frappe/erpnext/issues/46131)) ([#46135](https://github.com/frappe/erpnext/issues/46135)) ([f3cafef](f3cafef6a7))
* payment entry exchange gain loss issue ([3fb9033](3fb9033fb7))
* pos item detail serial no field (backport [#46211](https://github.com/frappe/erpnext/issues/46211)) ([#46212](https://github.com/frappe/erpnext/issues/46212)) ([de0dfbc](de0dfbca9a))
* pos item selection using serial no (backport [#46200](https://github.com/frappe/erpnext/issues/46200)) ([#46203](https://github.com/frappe/erpnext/issues/46203)) ([0f263bc](0f263bcff2))
* **pos:** get parent item group without user permission ([#46020](https://github.com/frappe/erpnext/issues/46020)) ([29f3aac](29f3aac925))
* production analytics report ([5668795](5668795884))
* rate changing on the deliver note ([75bc68b](75bc68b863))
* rearrange stock settings fields ([87703c6](87703c6511))
* removed mandatory property for the cost center field ([a94292a](a94292a69f))
* rename some sla fields ([c5717b9](c5717b983d))
* replacing serial and batch bundle on pos with auto fetch serial nos (backport [#46236](https://github.com/frappe/erpnext/issues/46236)) ([#46337](https://github.com/frappe/erpnext/issues/46337)) ([1e85f69](1e85f69072))
* **report:** allow `Closed` purchase orders to be visible ([20c4487](20c4487853))
* **report:** allow `Closed` sales orders to be visible ([8799af9](8799af9747))
* **report:** filter sales / purchase orders based on date filters ([2221bf1](2221bf1cba))
* revamp logic (split parent and child) ([7437cea](7437cea458))
* revert last commit ([a09c57f](a09c57f0d1))
* set landed cost based on purchase invoice rate ([fdaf5fa](fdaf5fafda))
* set taxes before calculating taxes and totals ([90dea42](90dea426d8))
* source warehouse not fetched in bom creator ([6157fed](6157fed71c))
* stock qty not recalculate on changing of the qty ([331798b](331798babc))
* stock reservation issue while making Purchase Invoice ([8bd7195](8bd71954f3))
* syntax error ([1790bcc](1790bcc6d1))
* syntax error ([bd48d39](bd48d391e4))
* syntax error ([cc535b7](cc535b7636))
* test case for debit note ([a8b31df](a8b31df65d))
* tests ([46b0734](46b0734d6f))
* tests ([d413039](d41303961c))
* translation DE ([ddcf79d](ddcf79da1d))
* use else instead of unnecessary elif ([6f760d1](6f760d197d))
* use valuation method from settings in stock ageing report (backport [#46068](https://github.com/frappe/erpnext/issues/46068)) ([#46297](https://github.com/frappe/erpnext/issues/46297)) ([d02d005](d02d005913))
* use value from currency exchange when exchange api is disabled (backport [#46137](https://github.com/frappe/erpnext/issues/46137)) ([#46309](https://github.com/frappe/erpnext/issues/46309)) ([ccc0358](ccc0358db6))
* **workspace:** enable is_query_report on purchase reports (backport [#46249](https://github.com/frappe/erpnext/issues/46249)) ([#46306](https://github.com/frappe/erpnext/issues/46306)) ([363129b](363129bcd4))
* Wrong Overdue Status in Sales Invoices (Floating-point arithmetic) (backport [#46146](https://github.com/frappe/erpnext/issues/46146)) ([#46310](https://github.com/frappe/erpnext/issues/46310)) ([1ff0858](1ff085876e))

### Features

* add new Closed and Stopped rows ([7749814](7749814571))
* add total weight in shipment ([#46049](https://github.com/frappe/erpnext/issues/46049)) ([171df3a](171df3aba5))
* create sales invoice print format ([#45403](https://github.com/frappe/erpnext/issues/45403)) ([1a382eb](1a382ebe86))
* **received items to be billed:** add company and date filters (backport [#46271](https://github.com/frappe/erpnext/issues/46271)) ([#46302](https://github.com/frappe/erpnext/issues/46302)) ([400f4f3](400f4f32ad))
* **Sales Invoice:** add items row via "Fetch Timesheet" (backport [#46071](https://github.com/frappe/erpnext/issues/46071)) ([#46311](https://github.com/frappe/erpnext/issues/46311)) ([1c6e464](1c6e4649bd))

### Performance Improvements

* don't track seen for POS Invoice (backport [#46187](https://github.com/frappe/erpnext/issues/46187)) ([#46189](https://github.com/frappe/erpnext/issues/46189)) ([41ab7f3](41ab7f3f7c))
* patch ([ee41e55](ee41e55343))
* replace if function in query ([d0b8e0d](d0b8e0da8d))
2025-03-05 13:31:02 +00:00
ruthra kumar
86b917b04c Merge pull request #46263 from frappe/version-15-hotfix
chore: release v15
2025-03-05 18:59:38 +05:30
ruthra kumar
9875489758 Merge pull request #46340 from frappe/mergify/bp/version-15-hotfix/pr-46020
fix(pos): get parent item group without user permission (backport #46020)
2025-03-05 18:40:04 +05:30
ruthra kumar
939cf321f7 Merge pull request #46338 from frappe/mergify/bp/version-15-hotfix/pr-45403
feat: create sales invoice print format (backport #45403)
2025-03-05 18:39:43 +05:30
Sugesh G
29f3aac925 fix(pos): get parent item group without user permission (#46020)
* fix(pos): get parent item group without user permission

* feat: add item group filter based on user permission

---------

Co-authored-by: venkat102 <venkatesharunachalam659@gmail.com>
(cherry picked from commit 8caf7f275e)
2025-03-05 12:42:03 +00:00
Ejaaz Khan
1a382ebe86 feat: create sales invoice print format (#45403)
* feat: create sales invoice print format

* fix: linter issue

* style: remove border from table

* refactor: change label to uppercase and show taxes

* refactor: format date and add translation on label

* refactor: remove default header and format labels

* refactor: change label style and small fix

* chore: Qty in title case

---------

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
(cherry picked from commit 38aa7cab8a)
2025-03-05 12:38:03 +00:00
mergify[bot]
6c1ceff8ee fix: exclude already consumed purchase receipt items from asset capitalization (backport #46329) (#46336)
* fix: exclude already consumed purchase receipt items from asset capitalization (#46329)

* feat: link purchase receipt row item to capitalization

* fix: avoid fetching already consumed stock and asset items during capitalization

* fix(patch): added patch to link purchase receipt item to stock item child table

* fix: added nosemgrep

* refactor: rename  to

(cherry picked from commit f50d479bfd)

# Conflicts:
#	erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json
#	erpnext/patches.txt

* fix: resolved conflicts

* fix: resolved conflicts

---------

Co-authored-by: Khushi Rawat <142375893+khushi8112@users.noreply.github.com>
2025-03-05 18:07:14 +05:30
mergify[bot]
1e85f69072 fix: replacing serial and batch bundle on pos with auto fetch serial nos (backport #46236) (#46337)
fix: replacing serial and batch bundle on pos with auto fetch serial nos (#46236)

* fix: replacing serial and batch bundle on pos with auto fetch serial nos

* fix: reserved serial no

added a check to look for serial no in reserved serial nos list before removing it as there might be a situation where an item is returned which was already consolidated.

(cherry picked from commit 35512d40bb)

Co-authored-by: Diptanil Saha <diptanil@frappe.io>
2025-03-05 18:00:31 +05:30
rohitwaghchaure
1992c2639c Merge pull request #46333 from frappe/mergify/bp/version-15-hotfix/pr-46330
fix: Accounting Period validation throwing for different companies (backport #46330)
2025-03-05 17:03:24 +05:30
Mihir Kandoi
8f278ab7c7 Merge pull request #46317 from frappe/mergify/bp/version-15-hotfix/pr-45947
fix: set landed cost based on purchase invoice rate (backport #45947)
2025-03-05 16:58:41 +05:30
Mihir Kandoi
7a33bf41d8 Merge pull request #46331 from frappe/mergify/bp/version-15-hotfix/pr-46313
fix: production analytics report (backport #46313)
2025-03-05 16:45:33 +05:30
Rohit Waghchaure
6df9cf327d fix: Accounting Period validation throwing for different companies
(cherry picked from commit b1508efca2)
2025-03-05 11:11:04 +00:00
Mihir Kandoi
5e06e4acce fix: patch 2025-03-05 16:32:26 +05:30
Mihir Kandoi
7749814571 feat: add new Closed and Stopped rows
(cherry picked from commit 6cc3d67835)
2025-03-05 10:54:36 +00:00
Mihir Kandoi
6f760d197d fix: use else instead of unnecessary elif
(cherry picked from commit 961258a4ce)
2025-03-05 10:54:36 +00:00
Mihir Kandoi
5668795884 fix: production analytics report
(cherry picked from commit 772e9ecfaa)
2025-03-05 10:54:36 +00:00
mergify[bot]
aaf35c5df9 fix: Naming of Purchase Amount (backport #46051) (#46324)
fix: Naming of Purchase Amount (#46051)

* fix: Naming of Purchase Amount

* fix: linters

(cherry picked from commit 104f60cc57)

Co-authored-by: 0xD0M1M0 <76812428+0xD0M1M0@users.noreply.github.com>
2025-03-05 16:18:50 +05:30
mergify[bot]
6aa8803068 fix: consolidate gl entries by project in General Ledger Report (backport #46314) (#46321)
fix: consolidate gl entries by project in General Ledger Report (#46314)

(cherry picked from commit 1f685efcaf)

Co-authored-by: Lakshit Jain <108322669+ljain112@users.noreply.github.com>
2025-03-05 16:18:42 +05:30
Ben Kebdani
506dd3c6b9 fix: Close and Reopen buttons dissapear after saving changes (#46048)
* fix: Close and Reopen buttons dissapear after saving changes

* style: linter issue

---------

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2025-03-05 15:34:55 +05:30
mergify[bot]
9a433a6750 fix: adding cost center on pos invoice items while applying product discount (backport #46082) (#46322)
fix: adding cost center on pos invoice items while applying product discount (#46082)

(cherry picked from commit 926e4ecc4f)

Co-authored-by: Diptanil Saha <diptanil@frappe.io>
2025-03-05 15:22:46 +05:30
mergify[bot]
e3ce17bd6e fix:[regional] Italian einvoice xml generated with wrong prices (#40254) (backport #45840) (#46318)
Merge pull request #45840 from gms-electronics/40254-italian-einvoice

fix:[regional] Italian einvoice xml generated with wrong prices (#40254)
(cherry picked from commit c5f90c823d)

Co-authored-by: Fab <fabian.thobe@mwv.vc>
2025-03-05 15:19:41 +05:30
mergify[bot]
836fd8fbc4 fix: consider journal entry and return invoice in paid_amount calculation (backport #46129) (#46319)
fix: consider journal entry and return invoice in paid_amount calculation (#46129)

* fix: consider journal entry and return invoice in paid_amount calculation

* test: add new unit test to consider journal entry and return invoice in paid_amount calculation

(cherry picked from commit 425fb12e91)

Co-authored-by: Sugesh G <73237300+Sugesh393@users.noreply.github.com>
2025-03-05 15:07:36 +05:30
Mihir Kandoi
fe8c9a3605 chore: resolve conflicts 2025-03-05 14:54:40 +05:30
Mihir Kandoi
941d67a0b6 chore: resolve conflicts 2025-03-05 14:48:41 +05:30
mergify[bot]
5a3073c4c1 fix: incorrect batch picked in the pick list (backport #45761) (#46315)
fix: incorrect batch picked in the pick list

(cherry picked from commit e1b7688a17)

Co-authored-by: Rohit Waghchaure <rohitw1991@gmail.com>
2025-03-05 14:48:01 +05:30
Mihir Kandoi
8f2fdcae88 fix: patch
(cherry picked from commit 1230127d24)

# Conflicts:
#	erpnext/patches.txt
2025-03-05 09:02:36 +00:00
Mihir Kandoi
a09c57f0d1 fix: revert last commit
(cherry picked from commit 154e9813c4)

# Conflicts:
#	erpnext/patches.txt
2025-03-05 09:02:35 +00:00
Mihir Kandoi
7b13d8cd98 fix: fiscal year error
(cherry picked from commit 7cf8e498c4)
2025-03-05 09:02:34 +00:00
Mihir Kandoi
ee41e55343 perf: patch
(cherry picked from commit a41024813b)
2025-03-05 09:02:34 +00:00
Mihir Kandoi
fdaf5fafda fix: set landed cost based on purchase invoice rate
(cherry picked from commit 17d415b105)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
#	erpnext/patches.txt
#	erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
2025-03-05 09:02:34 +00:00
rohitwaghchaure
c85fd36960 Merge pull request #46308 from frappe/mergify/bp/version-15-hotfix/pr-45987
fix: rename some sla fields (backport #45987)
2025-03-05 14:30:54 +05:30
rohitwaghchaure
a4e5a46566 Merge pull request #46298 from frappe/mergify/bp/version-15-hotfix/pr-46046
perf: optimize query in project.py (backport #46046)
2025-03-05 14:30:26 +05:30
ruthra kumar
3c39888227 Merge pull request #46069 from frappe/mergify/bp/version-15-hotfix/pr-46049
feat: add total weight in shipment (backport #46049)
2025-03-05 14:23:49 +05:30
mergify[bot]
d5f07f06c7 refactor: rename subcontracting fields (backport #46226) (#46301)
* refactor: rename subcontracting fields

(cherry picked from commit b4f65154f5)

# Conflicts:
#	erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json

* chore: resolve conflicts

---------

Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2025-03-05 14:08:10 +05:30
mergify[bot]
ccc0358db6 fix: use value from currency exchange when exchange api is disabled (backport #46137) (#46309)
fix: use value from currency exchange when exchange api is disabled (#46137)

(cherry picked from commit 2d26bff870)

Co-authored-by: Venkatesh <47534423+venkat102@users.noreply.github.com>
2025-03-05 14:07:12 +05:30
mergify[bot]
1ff085876e fix: Wrong Overdue Status in Sales Invoices (Floating-point arithmetic) (backport #46146) (#46310)
fix: Wrong Overdue Status in Sales Invoices (Floating-point arithmetic) (#46146)

* fix: Wrong Overdue Status in Sales Invoices (Floating-point arithmetic)

* style: after run pre-commit

(cherry picked from commit 89bcdd6fa5)

Co-authored-by: Diógenes Souza <103958767+devdiogenes@users.noreply.github.com>
2025-03-05 14:07:03 +05:30
mergify[bot]
1c6e4649bd feat(Sales Invoice): add items row via "Fetch Timesheet" (backport #46071) (#46311)
feat(Sales Invoice): add items row via "Fetch Timesheet" (#46071)

(cherry picked from commit 94547188bf)

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2025-03-05 14:06:55 +05:30
Mihir Kandoi
e4e1be568b Merge branch 'version-15-hotfix' into mergify/bp/version-15-hotfix/pr-45987 2025-03-05 13:59:59 +05:30
Mihir Kandoi
01db730714 Merge pull request #46300 from frappe/mergify/bp/version-15-hotfix/pr-46258
fix: delivery note from sales order uom conversion mistake (backport #46258)
2025-03-05 13:59:14 +05:30
ruthra kumar
dbcffa7ea4 Merge pull request #46235 from frappe/mergify/bp/version-15-hotfix/pr-46223
fix: dont update rate of free item when batch is updated (backport #46223)
2025-03-05 13:57:23 +05:30
ruthra kumar
36fa6bf15c chore: resolve conflict 2025-03-05 13:55:50 +05:30
mergify[bot]
4f80ddd834 fix: Add company filter at get_invoice method (backport #46238) (#46299)
fix: Add company filter at get_invoice method (#46238)

(cherry picked from commit a8d1cbc1c3)

Co-authored-by: Kunhi <kunhimohamed6@gmail.com>
2025-03-05 13:12:20 +05:30
mergify[bot]
400f4f32ad feat(received items to be billed): add company and date filters (backport #46271) (#46302)
feat(received items to be billed): add company and date filters (#46271)

* feat(received items to be billed): add company and date filters

* feat(delivered to be billed): add company and date filters

* feat: add company and date conditions

* chore: remove debugger

(cherry picked from commit 6117706ab5)

Co-authored-by: Sudharsanan Ashok <135326972+Sudharsanan11@users.noreply.github.com>
2025-03-05 13:12:09 +05:30
mergify[bot]
363129bcd4 fix(workspace): enable is_query_report on purchase reports (backport #46249) (#46306)
fix(workspace): enable is_query_report on purchase reports (#46249)

* fix(workspace): enable is_query_report on purchase reports

* fix: resolved conflict

---------

Co-authored-by: venkat102 <venkatesharunachalam659@gmail.com>
(cherry picked from commit 5513e24b00)

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2025-03-05 13:11:53 +05:30
mergify[bot]
087dde5873 fix: depreciation and balances report correction (backport #46259) (#46305)
fix: depreciation and balances report correction (#46259)

(cherry picked from commit 4a542b22a4)

Co-authored-by: Khushi Rawat <142375893+khushi8112@users.noreply.github.com>
2025-03-05 13:11:45 +05:30
mergify[bot]
5ae9faab91 fix: only include submitted docs for internal received quantity validation (backport #46262) (#46304)
fix: only include submitted docs for internal received quantity validation (#46262)

(cherry picked from commit 88fcdbb81e)

Co-authored-by: Lakshit Jain <108322669+ljain112@users.noreply.github.com>
2025-03-05 13:11:34 +05:30
Mihir Kandoi
446a8fe096 chore: resolve conflicts 2025-03-05 13:08:47 +05:30
Mihir Kandoi
0b50f1a9c3 chore: fix pre-commit/linter error 2025-03-05 12:57:53 +05:30
Mihir Kandoi
f29c43811c chore: resolve conflicts 2025-03-05 12:55:10 +05:30
Mihir Kandoi
e4fbd22173 Merge branch 'version-15-hotfix' into mergify/bp/version-15-hotfix/pr-46223 2025-03-05 12:51:01 +05:30
Mihir Kandoi
6b56724436 chore: resolve conflicts 2025-03-05 12:46:52 +05:30
Mihir Kandoi
af49f5a8af fix: patch path
(cherry picked from commit dcec446e55)

# Conflicts:
#	erpnext/patches.txt
2025-03-05 07:16:48 +00:00
Mihir Kandoi
46b0734d6f fix: tests
(cherry picked from commit 019303dd12)

# Conflicts:
#	erpnext/support/doctype/issue/issue.json
2025-03-05 07:16:47 +00:00
Mihir Kandoi
d41303961c fix: tests
(cherry picked from commit 1b831e9abd)
2025-03-05 07:16:47 +00:00
Mihir Kandoi
c5717b983d fix: rename some sla fields
(cherry picked from commit baa0dd1235)

# Conflicts:
#	erpnext/patches.txt
2025-03-05 07:16:46 +00:00
mergify[bot]
d02d005913 fix: use valuation method from settings in stock ageing report (backport #46068) (#46297)
fix: use valuation method from settings in stock ageing report

(cherry picked from commit da09c278c8)

Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2025-03-05 12:40:13 +05:30
Mihir Kandoi
d10add4b1e fix: delivery note from sales order uom conversion mistake
(cherry picked from commit 49a43d355d)

# Conflicts:
#	erpnext/selling/doctype/sales_order/sales_order.py
2025-03-05 06:52:07 +00:00
mergify[bot]
88234bbf9a fix: Include additional account types for Expense Account in LCV (backport #46206) (#46296)
fix: Include additional account types for Expense Account in LCV (#46206)

fix: additional account types in filters for the Expense account selection
(cherry picked from commit 59e99f167d)

Co-authored-by: Priyansh Shah <108476017+priyanshshah2442@users.noreply.github.com>
2025-03-05 12:19:31 +05:30
Mihir Kandoi
d8a1d0e908 test: added test
(cherry picked from commit 6073f5a6f9)
2025-03-05 06:41:09 +00:00
Mihir Kandoi
7437cea458 fix: revamp logic (split parent and child)
(cherry picked from commit f7594e2ff9)
2025-03-05 06:41:08 +00:00
Mihir Kandoi
1790bcc6d1 fix: syntax error
(cherry picked from commit 2f1e253e19)
2025-03-05 06:41:08 +00:00
Mihir Kandoi
d0b8e0da8d perf: replace if function in query
(cherry picked from commit 5e66231ca4)
2025-03-05 06:41:08 +00:00
rohitwaghchaure
ab4b1d4356 Merge pull request #46019 from frappe/st30783
fix: source warehouse not fetched in bom creator
2025-03-05 12:06:31 +05:30
mergify[bot]
7759775ee6 fix: Add permission check in POS's Toggle Recent Orders (backport #46010) (#46274)
fix: use get_list to check permissions

(cherry picked from commit a08bc6b913)

Co-authored-by: Sanket322 <shahsanket322003.com>
2025-03-05 12:04:17 +05:30
mergify[bot]
1b00de1815 chore: erpnext.com -> frappe.io/erpnext (backport #46288) (#46290)
* chore: erpnext.com -> frappe.io/erpnext (#46288)

(cherry picked from commit 41fe30ea6e)

# Conflicts:
#	README.md

* Update README.md

---------

Co-authored-by: Ankush Menat <ankush@frappe.io>
2025-03-05 11:35:52 +05:30
mergify[bot]
41ab7f3f7c perf: don't track seen for POS Invoice (backport #46187) (#46189)
* perf: don't track seen for POS Invoice (#46187)

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

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

(cherry picked from commit ded0aab680)

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

* chore: conflicts

---------

Co-authored-by: Ankush Menat <ankush@frappe.io>
2025-03-05 11:28:22 +05:30
ruthra kumar
5049f80b7f Merge pull request #46278 from mhh008/version-15-hotfix
fix-de-transaltion
2025-03-05 11:01:42 +05:30
rohitwaghchaure
d650b23722 Merge pull request #46282 from frappe/mergify/bp/version-15-hotfix/pr-46279
fix: rate changing on the deliver note (backport #46279)
2025-03-04 23:24:30 +05:30
Rohit Waghchaure
75bc68b863 fix: rate changing on the deliver note
(cherry picked from commit 6f40849d55)
2025-03-04 16:12:37 +00:00
rohitwaghchaure
f37fe1781a Merge pull request #46280 from frappe/mergify/bp/version-15-hotfix/pr-46208
fix: stock reservation issue while making Purchase Invoice (backport #46208)
2025-03-04 21:41:53 +05:30
Rohit Waghchaure
8bd71954f3 fix: stock reservation issue while making Purchase Invoice
(cherry picked from commit 64985bffe0)
2025-03-04 14:41:41 +00:00
Lakshit Jain
0f86ed28bc Merge pull request #46276 from frappe/mergify/bp/version-15-hotfix/pr-46039
fix: Ensure new line is added regardless of postal code presence (backport #46039)
2025-03-04 19:14:17 +05:30
mhh008
ddcf79da1d fix: translation DE 2025-03-04 13:56:19 +01:00
Lakshit Jain
b5fcd682a6 Merge pull request #46268 from frappe/mergify/bp/version-15-hotfix/pr-45896
fix: auto allocation for negative amount outstanding for Customers in Payment Entry (backport #45896)
2025-03-04 18:17:15 +05:30
Lakshit Jain
29405498bd Merge pull request #46270 from frappe/mergify/bp/version-15-hotfix/pr-46260
fix: do not include opening invoices in billed items to be received report (backport #46260)
2025-03-04 18:16:42 +05:30
Sanket322
1630979f05 refactor: add new line ragardless of postal code
(cherry picked from commit 746adfd057)
2025-03-04 12:36:40 +00:00
Smit Vora
6ab7d98681 Merge pull request #46139 from frappe/mergify/bp/version-15-hotfix/pr-46117
fix(report): Allow `Closed` Purchase Orders to be Visible in Purchase Order Analysis Report (backport #46117)
2025-03-04 18:03:47 +05:30
ljain112
eee500f20e fix: do not include opening invoices in billed items to be received report
(cherry picked from commit c1ddf444c6)
2025-03-04 11:24:24 +00:00
ljain112
78a329e573 fix: auto allocation for negative amount outstanding for Customers in Payment Entry
(cherry picked from commit 6275b44a0b)
2025-03-04 11:20:12 +00:00
Smit Vora
83dcbec86a chore: resolve conflicts 2025-03-04 16:43:55 +05:30
Smit Vora
5b6ed1d077 Merge pull request #46253 from frappe/mergify/bp/version-15-hotfix/pr-46062
fix: Convert tuple of tuples to list of dicts for dot notation access (backport #46062)
2025-03-04 16:39:16 +05:30
Smit Vora
284c93ecc2 Merge pull request #46246 from frappe/mergify/bp/version-15-hotfix/pr-46192
fix: Exclude Cancelled GL Entries (backport #46192)
2025-03-04 16:38:38 +05:30
Smit Vora
33051ec17e Merge pull request #46245 from frappe/mergify/bp/version-15-hotfix/pr-45972
fix: Set Taxes Before Calculating Taxes and Totals (backport #45972)
2025-03-04 16:38:24 +05:30
ruthra kumar
9e54e2ea58 Merge pull request #46266 from frappe/mergify/bp/version-15-hotfix/pr-45751
fix: change voucher_type and voucher_name field type to data (backport #45751)
2025-03-04 16:12:40 +05:30
ruthra kumar
c09c4cc243 Merge pull request #46252 from frappe/mergify/bp/version-15-hotfix/pr-46070
fix: Batch Price gets updated only if it is a billed item (backport #46070)
2025-03-04 15:49:25 +05:30
ruthra kumar
489efda985 chore: resolve conflict 2025-03-04 15:47:03 +05:30
Sugesh393
3a03865a8f fix: change voucher_type and voucher_no field type to data
(cherry picked from commit f8ab021920)

# Conflicts:
#	erpnext/accounts/doctype/tax_withheld_vouchers/tax_withheld_vouchers.json
2025-03-04 10:10:54 +00:00
ruthra kumar
854632dd51 chore: resolve conflict 2025-03-04 15:03:03 +05:30
ruthra kumar
ef44ad79a0 Merge pull request #46261 from frappe/mergify/bp/version-15-hotfix/pr-46172
fix: Show Credit Note amount in credit note column (backport #46172)
2025-03-04 15:00:15 +05:30
Sanket322
a8b31df65d fix: test case for debit note
(cherry picked from commit 6719bbeb10)
2025-03-04 07:58:31 +00:00
Sanket322
9f4311e7fb fix: fixing test case
(cherry picked from commit 9b2b477ae0)
2025-03-04 07:58:31 +00:00
Sanket322
0a65217423 fix: if invoice is return then add amount in proper column
(cherry picked from commit ccb4bdbe4c)
2025-03-04 07:58:30 +00:00
Sanket Shah
ef195513d0 fix: Convert tuple of tuples to list of dicts for dot notation access (#46062)
fix: use as_dict to convert tuples into list of dict

Co-authored-by: Sanket322 <shahsanket322003.com>
(cherry picked from commit e4b0ab6656)
2025-03-03 16:09:45 +00:00
mergify[bot]
f3cafef6a7 fix(patch): Ensure SLE indexes (backport #46131) (#46135)
* fix(patch): Ensure SLE indexes (#46131)

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

(cherry picked from commit f62aa8fc57)

# Conflicts:
#	erpnext/patches.txt

* fix: resolved conflict

---------

Co-authored-by: Ankush Menat <ankush@frappe.io>
Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2025-03-03 21:37:02 +05:30
ruthra kumar
6762dc3392 chore: linter fix
(cherry picked from commit 0a2193e458)

# Conflicts:
#	erpnext/stock/get_item_details.py
2025-03-03 14:27:25 +00:00
Nirmalrajaa K
dbd47dff98 fix: Batch Price gets updated only if it is a billed item
(cherry picked from commit 1a56b83054)

# Conflicts:
#	erpnext/stock/get_item_details.py
2025-03-03 14:27:25 +00:00
Nirmalrajaa K
8ed512f6c6 fix: Batch Price gets updated only if it is a billed item
(cherry picked from commit 9597b1a69e)

# Conflicts:
#	erpnext/stock/get_item_details.py
2025-03-03 14:27:25 +00:00
Marica
3a6ef2564e Merge pull request #46073 from 0xD0M1M0/fix-discount-accounting-v15
fix: discount accounting for v15
2025-03-03 19:46:51 +05:30
Smit Vora
35df539da3 chore: fix linters 2025-03-03 17:02:15 +05:30
Ninad1306
5e083861a4 fix: exclude cancelled gl entries
(cherry picked from commit 3251a331dd)
2025-03-03 11:28:57 +00:00
Ninad1306
41e9a10ab4 test: validate fetching of taxes based on taxes and charges template
(cherry picked from commit 196ef7ac4e)
2025-03-03 11:27:45 +00:00
Ninad1306
90dea426d8 fix: set taxes before calculating taxes and totals
(cherry picked from commit 0fd0695bbb)
2025-03-03 11:27:45 +00:00
Mihir Kandoi
bd48d391e4 fix: syntax error 2025-03-03 16:40:19 +05:30
rohitwaghchaure
9082ff6aa9 Merge pull request #46242 from frappe/mergify/bp/version-15-hotfix/pr-46239
fix: incorrectly billed amount in the purchase receipt (backport #46239)
2025-03-03 16:16:09 +05:30
rohitwaghchaure
ba0ce267c2 Merge pull request #46243 from frappe/mergify/bp/version-15-hotfix/pr-46231
fix: incorrect batch picked (backport #46231)
2025-03-03 16:15:43 +05:30
Mihir Kandoi
cc535b7636 fix: syntax error 2025-03-03 15:27:35 +05:30
Mihir Kandoi
81c7b8c273 chore: resolve conflicts 2025-03-03 15:05:44 +05:30
Rohit Waghchaure
e94f0b1cca fix: incorrect batch picked
(cherry picked from commit d2564cad68)
2025-03-03 09:24:24 +00:00
Rohit Waghchaure
c247cf888b fix: incorrectly billed amount in the purchase receipt
(cherry picked from commit a5271fdb2e)
2025-03-03 09:15:48 +00:00
Khushi Rawat
7409c140d4 Merge pull request #46237 from frappe/mergify/bp/version-15-hotfix/pr-46201
fix(asset depreciation schedules): enable auto commit (backport #46201)
2025-03-03 14:04:39 +05:30
Mihir Kandoi
7f4d553201 chore: resolve conflicts 2025-03-03 12:28:06 +05:30
venkat102
899e468f6a fix(asset depreciation schedules): enable auto commit
(cherry picked from commit a4b24f7451)
2025-03-03 06:42:02 +00:00
mergify[bot]
faee8d6c5e fix: don't allow renaming account while system is actively in use (backport #46176) (#46210)
fix: don't allow renaming account while system is actively in use (#46176)

(cherry picked from commit 999f1cf96d)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2025-03-03 11:16:37 +05:30
Frappe PR Bot
171f966421 chore(release): Bumped to Version 15.53.4
## [15.53.4](https://github.com/frappe/erpnext/compare/v15.53.3...v15.53.4) (2025-03-03)

### Bug Fixes

* removed mandatory property for the cost center field ([8eddc09](8eddc09bba))
2025-03-03 05:12:02 +00:00
rohitwaghchaure
3f76a413f8 Merge pull request #46205 from frappe/mergify/bp/version-15/pr-46186
fix: removed mandatory property for the cost center field (backport #46186)
2025-03-03 10:40:35 +05:30
Mihir Kandoi
61d5680c8d fix: error
(cherry picked from commit 7c9c0c7776)

# Conflicts:
#	erpnext/stock/get_item_details.py
2025-03-03 04:46:28 +00:00
Mihir Kandoi
9e649d8522 fix: dont update rate of free item when batch is updated
(cherry picked from commit a3596f717b)

# Conflicts:
#	erpnext/stock/get_item_details.py
2025-03-03 04:46:28 +00:00
rohitwaghchaure
c0f736736e Merge pull request #46217 from frappe/mergify/bp/version-15-hotfix/pr-46202
fix: rearrange stock settings fields (backport #46202)
2025-03-03 10:15:36 +05:30
Frappe PR Bot
bcd02df6fd chore(release): Bumped to Version 15.53.3
## [15.53.3](https://github.com/frappe/erpnext/compare/v15.53.2...v15.53.3) (2025-03-03)

### Bug Fixes

* stock qty not recalculate on changing of the qty ([9186f13](9186f13458))
2025-03-03 04:02:37 +00:00
rohitwaghchaure
7a71c24d5c Merge pull request #46232 from frappe/mergify/bp/version-15/pr-46224
fix: stock qty not recalculate on changing of the qty (backport #46219) (backport #46224)
2025-03-03 09:31:16 +05:30
Rohit Waghchaure
9186f13458 fix: stock qty not recalculate on changing of the qty
(cherry picked from commit 464e3339fe)
(cherry picked from commit 331798babc)
2025-03-03 03:57:22 +00:00
rohitwaghchaure
c48e157a25 Merge pull request #46224 from frappe/mergify/bp/version-15-hotfix/pr-46219
fix: stock qty not recalculate on changing of the qty (backport #46219)
2025-03-03 08:24:05 +05:30
mergify[bot]
84ca0ada1b refactor: using function to unset grand total to default mode of payment in pos (backport #46228) (#46229)
refactor: using function to unset grand total to default mode of payment in pos (#46228)

(cherry picked from commit 62c3915ecb)

Co-authored-by: Diptanil Saha <diptanil@frappe.io>
2025-03-03 01:59:13 +05:30
Rohit Waghchaure
331798babc fix: stock qty not recalculate on changing of the qty
(cherry picked from commit 464e3339fe)
2025-03-02 06:46:56 +00:00
rohitwaghchaure
93fed0ce86 chore: fix conflicts 2025-03-01 12:53:44 +05:30
Rohit Waghchaure
87703c6511 fix: rearrange stock settings fields
(cherry picked from commit 93f461c6f3)

# Conflicts:
#	erpnext/stock/doctype/stock_settings/stock_settings.json
2025-03-01 06:57:35 +00:00
mergify[bot]
de0dfbca9a fix: pos item detail serial no field (backport #46211) (#46212)
fix: pos item detail serial no field (#46211)

(cherry picked from commit d2fad44e89)

Co-authored-by: Diptanil Saha <diptanil@frappe.io>
2025-02-28 23:24:49 +05:30
Rohit Waghchaure
8eddc09bba fix: removed mandatory property for the cost center field
(cherry picked from commit 079cf772aa)
2025-02-28 10:25:50 +00:00
mergify[bot]
0f263bcff2 fix: pos item selection using serial no (backport #46200) (#46203)
fix: pos item selection using serial no (#46200)

(cherry picked from commit 8fb09decd2)

Co-authored-by: Diptanil Saha <diptanil@frappe.io>
2025-02-28 15:43:59 +05:30
ruthra kumar
a9b2f4885e Merge pull request #46197 from frappe/mergify/bp/version-15-hotfix/pr-46186
fix: removed mandatory property for the cost center field (backport #46186)
2025-02-28 13:56:09 +05:30
Rohit Waghchaure
a94292a69f fix: removed mandatory property for the cost center field
(cherry picked from commit 079cf772aa)
2025-02-28 08:03:51 +00:00
Sagar Vora
5b072c88a6 Merge pull request #46191 from frappe/mergify/bp/version-15-hotfix/pr-46177
fix: Payment Entry fixes (backport #46177)
2025-02-28 10:44:00 +05:30
vishakhdesai
3fb9033fb7 fix: payment entry exchange gain loss issue
(cherry picked from commit 2dbef23244)
2025-02-28 05:13:43 +00:00
Frappe PR Bot
d9c1b58fc3 chore(release): Bumped to Version 15.53.2
## [15.53.2](https://github.com/frappe/erpnext/compare/v15.53.1...v15.53.2) (2025-02-26)

### Bug Fixes

* check value as int ([97d3e86](97d3e8648b))
* enable fetch_timesheet_in_sales_invoice in test ([feb64cb](feb64cb9b5))
* incorrect stock value difference for adjustment entry ([96d44e3](96d44e362d))
* inventory dimension for maintence visit ([ec3b281](ec3b281a3b))
* no permission to get project settings in sales invoice ([b8281c3](b8281c34e2))
* pos opening entry dialog not saving on change data (backport [#46066](https://github.com/frappe/erpnext/issues/46066)) ([#46067](https://github.com/frappe/erpnext/issues/46067)) ([0ae2d61](0ae2d61974))
* **project settings:** add checkbox to auto fetch timesheet in sales invoice ([b3c1df8](b3c1df8561))
* **sales invoice:** check fetch_timesheet_in_sales_invoice enabled before fetching the timesheet ([b1095bb](b1095bb91b))
* valuation rate for batch ([c72dab4](c72dab49f4))
2025-02-26 12:58:53 +00:00
ruthra kumar
f93d1a2633 Merge pull request #46132 from frappe/version-15-hotfix
chore: release v15
2025-02-26 18:27:33 +05:30
ruthra kumar
bc53365620 Merge pull request #46159 from frappe/mergify/bp/version-15-hotfix/pr-46156
fix: no permission to get project settings in sales invoice (backport #46156)
2025-02-26 14:34:36 +05:30
venkat102
b8281c34e2 fix: no permission to get project settings in sales invoice
(cherry picked from commit 221f1468cb)
2025-02-26 08:22:24 +00:00
rohitwaghchaure
81ff16248e Merge pull request #46143 from frappe/mergify/bp/version-15-hotfix/pr-46097
fix: valuation rate for batch (backport #46097)
2025-02-26 10:16:10 +05:30
ruthra kumar
8bb085a055 Merge pull request #46142 from frappe/mergify/bp/version-15-hotfix/pr-45908
fix(projects settings): add checkbox to auto fetch timesheet in sales invoice (backport #45908)
2025-02-26 06:23:58 +05:30
Rohit Waghchaure
c72dab49f4 fix: valuation rate for batch
(cherry picked from commit b88305a95f)
2025-02-25 17:45:05 +00:00
ruthra kumar
feec16b682 chore: resolve conflicts 2025-02-25 22:53:14 +05:30
venkat102
feb64cb9b5 fix: enable fetch_timesheet_in_sales_invoice in test
(cherry picked from commit 5880f1d5c6)

# Conflicts:
#	erpnext/projects/doctype/timesheet/test_timesheet.py
2025-02-25 16:56:57 +00:00
venkat102
97d3e8648b fix: check value as int
(cherry picked from commit 43b13b91be)
2025-02-25 16:56:56 +00:00
venkat102
b1095bb91b fix(sales invoice): check fetch_timesheet_in_sales_invoice enabled before fetching the timesheet
(cherry picked from commit 914ad357fd)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice/sales_invoice.py
2025-02-25 16:56:56 +00:00
venkat102
b3c1df8561 fix(project settings): add checkbox to auto fetch timesheet in sales invoice
(cherry picked from commit 876082ea2f)

# Conflicts:
#	erpnext/projects/doctype/projects_settings/projects_settings.json
2025-02-25 16:56:56 +00:00
Ninad1306
2221bf1cba fix(report): filter sales / purchase orders based on date filters
(cherry picked from commit 936d7d4342)
2025-02-25 13:07:54 +00:00
Ninad1306
8799af9747 fix(report): allow Closed sales orders to be visible
(cherry picked from commit 2394e76e7d)

# Conflicts:
#	erpnext/selling/report/sales_order_analysis/sales_order_analysis.js
2025-02-25 13:07:54 +00:00
Ninad1306
20c4487853 fix(report): allow Closed purchase orders to be visible
(cherry picked from commit 3b2879d3a1)

# Conflicts:
#	erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js
2025-02-25 13:07:53 +00:00
0xD0M1M0
f609012f02 fix: discount accounting for v15 2025-02-21 22:26:39 +01:00
Ravibharathi
171df3aba5 feat: add total weight in shipment (#46049)
Co-authored-by: barredterra <14891507+barredterra@users.noreply.github.com>
(cherry picked from commit 1ec182430d)

# Conflicts:
#	erpnext/stock/doctype/shipment/shipment.json
2025-02-21 14:16:46 +00:00
mergify[bot]
0ae2d61974 fix: pos opening entry dialog not saving on change data (backport #46066) (#46067)
fix: pos opening entry dialog not saving on change data (#46066)

(cherry picked from commit 8e6959dfad)

Co-authored-by: Diptanil Saha <diptanil@frappe.io>
2025-02-21 17:53:59 +05:30
Frappe PR Bot
9e824fc4fe chore(release): Bumped to Version 15.53.1
## [15.53.1](https://github.com/frappe/erpnext/compare/v15.53.0...v15.53.1) (2025-02-21)

### Bug Fixes

* inventory dimension for maintence visit ([1d818e1](1d818e1510))
2025-02-21 10:01:58 +00:00
rohitwaghchaure
b6b47d6683 Merge pull request #46044 from frappe/mergify/bp/version-15/pr-46041
fix: inventory dimension for maintenance visit (backport #46037) (backport #46041)
2025-02-21 15:30:36 +05:30
Rohit Waghchaure
1d818e1510 fix: inventory dimension for maintence visit
(cherry picked from commit cd4ba69262)
(cherry picked from commit ec3b281a3b)
2025-02-20 07:37:38 +00:00
rohitwaghchaure
b00fa1dfee Merge pull request #46041 from frappe/mergify/bp/version-15-hotfix/pr-46037
fix: inventory dimension for maintenance visit (backport #46037)
2025-02-20 13:06:33 +05:30
Rohit Waghchaure
ec3b281a3b fix: inventory dimension for maintence visit
(cherry picked from commit cd4ba69262)
2025-02-20 06:13:19 +00:00
rohitwaghchaure
9318e4f0e8 Merge pull request #46028 from frappe/mergify/bp/version-15-hotfix/pr-46021
fix: incorrect stock value difference for adjustment entry (backport #46021)
2025-02-19 17:24:42 +05:30
Frappe PR Bot
16e8a00f45 chore(release): Bumped to Version 15.53.0
# [15.53.0](https://github.com/frappe/erpnext/compare/v15.52.0...v15.53.0) (2025-02-19)

### Bug Fixes

* add accounting dimensions section in sales order item ([b32e4da](b32e4daf2b))
* add is_new in if condition ([fc2ec7c](fc2ec7c495))
* add validate to allow equity account and party_type shareholder ([bb3eb81](bb3eb81170))
* allow scrap item with zero qty ([abe5384](abe5384449))
* auto create asset due to message error (backport [#45934](https://github.com/frappe/erpnext/issues/45934)) ([#45952](https://github.com/frappe/erpnext/issues/45952)) ([830edb8](830edb8f52))
* check if employee is currently working on another workstation ([22eaa14](22eaa14179))
* disable partial payment in pos (backport [#45752](https://github.com/frappe/erpnext/issues/45752)) ([#45945](https://github.com/frappe/erpnext/issues/45945)) ([38edc46](38edc46c46))
* do not reschedule depreciation for fully depreciated asset on scrap ([1e7c5ec](1e7c5ec0cb))
* fetch child account data for selected parent ([#45904](https://github.com/frappe/erpnext/issues/45904)) ([e36b860](e36b860a79))
* handle division by zero error (backport [#45966](https://github.com/frappe/erpnext/issues/45966)) ([#46015](https://github.com/frappe/erpnext/issues/46015)) ([15106b4](15106b49b6))
* include missing payment_gateway parameter in Payment Request URL ([18f9476](18f94765f7))
* letter head for quality inspection ([c289fef](c289fef3b5))
* link correct row item of purchase doc ([87f337b](87f337b605))
* make purchase_receipt_item and purchase_invoice_item fields of data type ([281431e](281431e041))
* millisecond issue for posting datetime ([4292365](42923656ee))
* patch for creating asset depreciation schedule records ([f043b46](f043b46696))
* pos accounting dimension fieldname error (backport [#45899](https://github.com/frappe/erpnext/issues/45899)) ([#45921](https://github.com/frappe/erpnext/issues/45921)) ([e998f06](e998f063a9))
* **pos profile:** check company while validating mandatory accounting dimension ([#45974](https://github.com/frappe/erpnext/issues/45974)) ([6a57743](6a577438aa))
* pos return validation on v15 ([#45951](https://github.com/frappe/erpnext/issues/45951)) ([dd34bbe](dd34bbe570))
* provision to enable naming series for SABB ([8fbfe14](8fbfe14c63))
* **quotation:** fetch exchange rate on currency change ([bd89c19](bd89c19c98))
* remove party type from validate ([0d21151](0d2115197e))
* remove public access to list items (backport [#45838](https://github.com/frappe/erpnext/issues/45838)) ([#46018](https://github.com/frappe/erpnext/issues/46018)) ([eead6d4](eead6d46ff))
* remove unused code ([dd5d144](dd5d144b55))
* **report:** add options to multiselectlist fields ([7e85a12](7e85a123b2))
* reset location only if there is value in row item location field ([a509568](a509568110))
* resolved conflicts ([84647a1](84647a1c73))
* round sum amount in JE auditing PF ([#45961](https://github.com/frappe/erpnext/issues/45961)) ([44e1ca9](44e1ca9d05))
* **send_message:** escape HTML in the text ([cbec989](cbec989a7c))
* serial no status for internal transfer delivery note ([2b80c00](2b80c009b3))
* set default value to 0 as per new logic ([1abe1a1](1abe1a1fd5))
* set sco_qty field of PO to non negative ([567fb8a](567fb8abd1))
* slow query ([8306d6f](8306d6fdb6))
* stock reservation for sales invoice ([1fb5586](1fb5586f56))
* stock reservation not working for sales invoice with update stock ([7d871f6](7d871f6bb5))
* tests ([f63a9db](f63a9dbf9b))
* throw correct exception ([5bccf9f](5bccf9f837))
* validate if no matching item found ([6183b38](6183b38089))
* validate payment request total of partly paid invoice ([c8881a9](c8881a9358))

### Features

* added ability to use custom html format for process statement of accounts (copy [#45746](https://github.com/frappe/erpnext/issues/45746)) ([#46012](https://github.com/frappe/erpnext/issues/46012)) ([1a4297a](1a4297ac35))
* added option to enforce free item qty in pricing rule ([8fb9228](8fb9228871))
* disable auto setting grand total to default mode of payment (backport [#45591](https://github.com/frappe/erpnext/issues/45591)) ([#45917](https://github.com/frappe/erpnext/issues/45917)) ([e271a5c](e271a5cba0))
2025-02-19 11:50:41 +00:00
ruthra kumar
2bb79c34c3 Merge pull request #45981 from frappe/version-15-hotfix
chore: release v15
2025-02-19 17:19:18 +05:30
mergify[bot]
eead6d46ff fix: remove public access to list items (backport #45838) (#46018)
fix: remove public access to list items

(cherry picked from commit 2bd596ee3d)

Co-authored-by: CaseSolved <richard@casesolved.co.uk>
2025-02-19 16:52:09 +05:30
mergify[bot]
15106b49b6 fix: handle division by zero error (backport #45966) (#46015)
fix: handle division by zero error (#45966)

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

Co-authored-by: Sanket Shah <113279972+Sanket322@users.noreply.github.com>
2025-02-19 16:51:09 +05:30
Akhil Narang
452e4dcbad Merge pull request #46025 from frappe/mergify/bp/version-15-hotfix/pr-46003
fix(send_message): escape HTML in the text (backport #46003)
2025-02-19 16:43:06 +05:30
Rohit Waghchaure
96d44e362d fix: incorrect stock value difference for adjustment entry
(cherry picked from commit df83e427a3)
2025-02-19 11:11:15 +00:00
Khushi Rawat
5a17171bd1 Merge pull request #46013 from frappe/mergify/bp/version-15-hotfix/pr-45872
fix: enable asset value editing for duplicate item code (backport #45872)
2025-02-19 16:34:36 +05:30
Khushi Rawat
777daf6aee Merge pull request #46014 from frappe/mergify/bp/version-15-hotfix/pr-45898
fix: patch for creating asset depreciation schedule records (backport #45898)
2025-02-19 16:33:03 +05:30
ruthra kumar
9b866e8ee4 Merge pull request #46016 from frappe/mergify/bp/version-15-hotfix/pr-45974
fix(pos profile): check company while validating mandatory accounting dimension (backport #45974)
2025-02-19 16:28:42 +05:30
Akhil Narang
cbec989a7c fix(send_message): escape HTML in the text
Signed-off-by: Akhil Narang <me@akhilnarang.dev>
(cherry picked from commit 448a5db20f)
2025-02-19 10:45:49 +00:00
Khushi Rawat
dd5d144b55 fix: remove unused code 2025-02-19 16:00:31 +05:30
Mihir Kandoi
6157fed71c fix: source warehouse not fetched in bom creator 2025-02-19 15:54:31 +05:30
Khushi Rawat
9b8623dd64 chore: resolved conflicts 2025-02-19 15:50:53 +05:30
Khushi Rawat
84647a1c73 fix: resolved conflicts 2025-02-19 15:47:40 +05:30
mergify[bot]
1a4297ac35 feat: added ability to use custom html format for process statement of accounts (copy #45746) (#46012)
feat: added ability to use custom html format for process statement of accounts (#45746)

* feat: added ability to use custom print format for process statement of accounts documents.

* fix: handles missing hook issues

* chore: linter changes

---------

Co-authored-by: Boy4099 <mashtawayne4099@gmail.com>
Co-authored-by: ruthra kumar <ruthra@erpnext.com>
(cherry picked from commit a0cd08e9ea)

Co-authored-by: Steve Wilson <stevew9009@gmail.com>
2025-02-19 15:45:10 +05:30
Ejaaz Khan
236b502155 Merge pull request #46017 from frappe/mergify/bp/version-15-hotfix/pr-45961
fix: round sum amount in JE auditing PF (backport #45961)
2025-02-19 15:43:57 +05:30
mergify[bot]
830edb8f52 fix: auto create asset due to message error (backport #45934) (#45952)
fix: auto create asset due to message error (#45934)

* fix: auto create asset due to message error

* fix: linters

(cherry picked from commit 6f1bc5225a)

Co-authored-by: 0xD0M1M0 <76812428+0xD0M1M0@users.noreply.github.com>
2025-02-19 15:43:53 +05:30
Ejaaz Khan
44e1ca9d05 fix: round sum amount in JE auditing PF (#45961)
(cherry picked from commit 941085000a)
2025-02-19 10:11:36 +00:00
Venkatesh
6a577438aa fix(pos profile): check company while validating mandatory accounting dimension (#45974)
(cherry picked from commit 17a2f44290)
2025-02-19 10:10:20 +00:00
Nabin Hait
f043b46696 fix: patch for creating asset depreciation schedule records
(cherry picked from commit 7324dcb7c8)

# Conflicts:
#	erpnext/patches/v15_0/create_asset_depreciation_schedules_from_assets.py
2025-02-19 10:08:12 +00:00
Khushi Rawat
a509568110 fix: reset location only if there is value in row item location field
(cherry picked from commit 2bb79197aa)
2025-02-19 10:02:33 +00:00
Khushi Rawat
6183b38089 fix: validate if no matching item found
(cherry picked from commit 44c1425e73)
2025-02-19 10:02:33 +00:00
Khushi Rawat
87f337b605 fix: link correct row item of purchase doc
(cherry picked from commit da1b4cb9ab)
2025-02-19 10:02:33 +00:00
Khushi Rawat
281431e041 fix: make purchase_receipt_item and purchase_invoice_item fields of data type
(cherry picked from commit 8af9dcb33e)

# Conflicts:
#	erpnext/assets/doctype/asset/asset.json
2025-02-19 10:02:32 +00:00
rohitwaghchaure
be65cd4df6 Merge pull request #46005 from frappe/mergify/bp/version-15-hotfix/pr-45750
feat: added option to enforce free item qty in pricing rule (backport #45750)
2025-02-19 15:09:51 +05:30
Khushi Rawat
965dbb6d2b Merge pull request #46006 from frappe/mergify/bp/version-15-hotfix/pr-45895
fix: do not reschedule depreciation for fully depreciated asset on scrap (backport #45895)
2025-02-19 14:24:55 +05:30
ruthra kumar
07d4725810 Merge pull request #46008 from frappe/mergify/bp/version-15-hotfix/pr-45904
fix: fetch child account data for selected parent (backport #45904)
2025-02-19 14:21:48 +05:30
Bhavansathru
e36b860a79 fix: fetch child account data for selected parent (#45904)
* fix: fetch child account data for selected parent

* fix: change reference name

---------

Co-authored-by: venkat102 <venkatesharunachalam659@gmail.com>
(cherry picked from commit 73e82b7afa)
2025-02-19 08:28:59 +00:00
Khushi Rawat
1e7c5ec0cb fix: do not reschedule depreciation for fully depreciated asset on scrap
(cherry picked from commit fd4c4f98fa)
2025-02-19 07:37:42 +00:00
Mihir Kandoi
1abe1a1fd5 fix: set default value to 0 as per new logic
(cherry picked from commit 844f1636c0)
2025-02-19 07:21:54 +00:00
Mihir Kandoi
f782900a15 refactor: rename field
(cherry picked from commit f3d598881c)
2025-02-19 07:21:54 +00:00
Mihir Kandoi
fc2ec7c495 fix: add is_new in if condition
(cherry picked from commit 4dcac56486)
2025-02-19 07:21:53 +00:00
Mihir Kandoi
ad11208109 test: added test
(cherry picked from commit ac3259b8f1)
2025-02-19 07:21:53 +00:00
Mihir Kandoi
f63a9dbf9b fix: tests
(cherry picked from commit 366ae85d85)
2025-02-19 07:21:53 +00:00
Mihir Kandoi
8fb9228871 feat: added option to enforce free item qty in pricing rule
(cherry picked from commit 19c01b1457)
2025-02-19 07:21:53 +00:00
rohitwaghchaure
617a24d61e Merge pull request #45994 from frappe/mergify/bp/version-15-hotfix/pr-45980
fix: check if employee is currently working on another workstation (backport #45980)
2025-02-19 11:24:05 +05:30
ruthra kumar
6fad501aa0 Merge pull request #46001 from frappe/mergify/bp/version-15-hotfix/pr-45882
fix(quotation): fetch exchange rate on currency change (backport #45882)
2025-02-19 11:13:04 +05:30
venkat102
bd89c19c98 fix(quotation): fetch exchange rate on currency change
(cherry picked from commit 2f77a8bed1)
2025-02-19 05:21:57 +00:00
Mihir Kandoi
5bccf9f837 fix: throw correct exception
(cherry picked from commit 4487edb255)
2025-02-18 15:13:04 +00:00
Mihir Kandoi
22eaa14179 fix: check if employee is currently working on another workstation
(cherry picked from commit 8234e659c8)
2025-02-18 15:13:04 +00:00
rohitwaghchaure
d3d0aacd4c Merge pull request #45993 from frappe/mergify/bp/version-15-hotfix/pr-45977
fix: millisecond issue for posting datetime (backport #45977)
2025-02-18 20:31:20 +05:30
rohitwaghchaure
69f5be65f6 Merge pull request #45975 from frappe/mergify/bp/version-15-hotfix/pr-45970
fix: serial no status for internal transfer delivery note (backport #45970)
2025-02-18 20:23:20 +05:30
rohitwaghchaure
508efd1322 Merge pull request #45990 from frappe/mergify/bp/version-15-hotfix/pr-45976
fix: slow query (backport #45976)
2025-02-18 20:22:51 +05:30
rohitwaghchaure
ecc2de2709 Merge pull request #45992 from frappe/mergify/bp/version-15-hotfix/pr-45971
fix: set sco_qty field of PO to non negative (backport #45971)
2025-02-18 20:22:39 +05:30
rohitwaghchaure
2e3b19ebb2 chore: fix conflicts 2025-02-18 20:11:02 +05:30
rohitwaghchaure
050bb1eef5 chore: fix conflicts 2025-02-18 20:10:30 +05:30
Rohit Waghchaure
42923656ee fix: millisecond issue for posting datetime
(cherry picked from commit ac9e5c0163)

# Conflicts:
#	erpnext/patches.txt
#	erpnext/stock/utils.py
2025-02-18 14:39:39 +00:00
rohitwaghchaure
0acdae02c1 chore: fix conflicts 2025-02-18 20:00:08 +05:30
Mihir Kandoi
567fb8abd1 fix: set sco_qty field of PO to non negative
(cherry picked from commit dfc3dc4944)
2025-02-18 14:29:22 +00:00
Rohit Waghchaure
8306d6fdb6 fix: slow query
(cherry picked from commit 8cfab57fc8)

# Conflicts:
#	erpnext/stock/doctype/packed_item/packed_item.json
2025-02-18 14:28:19 +00:00
ruthra kumar
20709f1b3e Merge pull request #45979 from frappe/mergify/bp/version-15-hotfix/pr-45765
fix: add accounting dimensions section in sales order item (backport #45765)
2025-02-18 17:48:34 +05:30
ruthra kumar
52860cc566 chore: resolve conflict 2025-02-18 14:27:14 +05:30
Sugesh393
b32e4daf2b fix: add accounting dimensions section in sales order item
(cherry picked from commit 7d47869f4b)

# Conflicts:
#	erpnext/selling/doctype/sales_order_item/sales_order_item.json
2025-02-18 08:42:56 +00:00
Rohit Waghchaure
2b80c009b3 fix: serial no status for internal transfer delivery note
(cherry picked from commit 3333331a3d)
2025-02-18 08:22:03 +00:00
ruthra kumar
8da1348a48 Merge pull request #45973 from frappe/mergify/bp/version-15-hotfix/pr-45723
fix: validate payment request total of partly paid invoice (backport #45723)
2025-02-18 13:42:58 +05:30
Sugesh393
9a33b877f5 test: add unit test to validate payment request grand_total for partly paid invoice
(cherry picked from commit f8472c32d9)
2025-02-18 07:42:28 +00:00
Sugesh393
c8881a9358 fix: validate payment request total of partly paid invoice
(cherry picked from commit 899c18df18)
2025-02-18 07:42:28 +00:00
ruthra kumar
f6047d8491 Merge pull request #45968 from frappe/mergify/bp/version-15-hotfix/pr-45687
fix: add validation to allow equity account (backport #45687)
2025-02-18 12:12:09 +05:30
rethik
0d2115197e fix: remove party type from validate
(cherry picked from commit f82837a4a2)
2025-02-18 06:16:03 +00:00
rethik
552b5a79ce test: add unit test to validate account type and party type
(cherry picked from commit 9422ce5aee)
2025-02-18 06:16:02 +00:00
rethik
bb3eb81170 fix: add validate to allow equity account and party_type shareholder
(cherry picked from commit 2c8e3f3409)
2025-02-18 06:16:02 +00:00
rohitwaghchaure
7ab69cfe5f Merge pull request #45956 from frappe/mergify/bp/version-15-hotfix/pr-45946
fix: provision to enable naming series for SABB (backport #45946)
2025-02-18 07:44:18 +05:30
rohitwaghchaure
43d32eb10e chore: fix conflicts 2025-02-17 21:32:48 +05:30
rohitwaghchaure
697fdf5bc3 chore: fix conflicts 2025-02-17 21:30:15 +05:30
rohitwaghchaure
2f7f9c0bac chore: fix conflicts 2025-02-17 21:29:22 +05:30
Rohit Waghchaure
8fbfe14c63 fix: provision to enable naming series for SABB
(cherry picked from commit fe43975cdd)

# Conflicts:
#	erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.json
#	erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py
#	erpnext/stock/doctype/stock_settings/stock_settings.json
2025-02-17 11:31:46 +00:00
mergify[bot]
38edc46c46 fix: disable partial payment in pos (backport #45752) (#45945)
* fix: disable partial payment in pos (#45752)

* fix: disable partial payment in pos

* test: disable partial payment

* test: removed print statement

* test: using save method to auto calculate paid_amount

* test: paid_amount calculation using save method

* test: added save method to calculate paid_amount

* test: outstanding amount

* test: added test for partial payments in pos invoice

* fix: custom validation error for partial payment

* test: using partial payment validation

* fix: validate only on submit

(cherry picked from commit d94802067b)

# Conflicts:
#	erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py
#	erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py

* chore: resolve conflict

* chore: resolve conflict

* chore: resolve linter issue

* test: fixed failing test

---------

Co-authored-by: Diptanil Saha <diptanil@frappe.io>
2025-02-17 17:01:34 +05:30
Diptanil Saha
dd34bbe570 fix: pos return validation on v15 (#45951) 2025-02-17 16:03:19 +05:30
rohitwaghchaure
11622f81f3 Merge pull request #45943 from frappe/mergify/bp/version-15-hotfix/pr-45941
fix: letter head for quality inspection (backport #45941)
2025-02-17 14:42:56 +05:30
mergify[bot]
e998f063a9 fix: pos accounting dimension fieldname error (backport #45899) (#45921)
* fix: pos accounting dimension fieldname error (#45899)

* fix: pos accounting dimension fieldname error

* fix: method to get enabled accounting dimensions

* fix: fetch enabled accounting dimensions

* fix: clear flags for accounting_dimensions_details on_update

* refactor: validation for doctype

* fix: using get_checks_for_pl_and_bs_accounts for accounting dimensions

(cherry picked from commit 60a5f4f30d)

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

* chore: resolve conflict

* chore: resolve linter issue

* fix: resolve linter issue

* chore: resolve linter issue

* chore: resolve linter issue

* chore: resolve linter issue

---------

Co-authored-by: Diptanil Saha <diptanil@frappe.io>
2025-02-17 14:13:34 +05:30
Rohit Waghchaure
c289fef3b5 fix: letter head for quality inspection
(cherry picked from commit cdd41373b6)
2025-02-17 08:28:52 +00:00
rohitwaghchaure
f8839957da Merge pull request #45913 from frappe/mergify/bp/version-15-hotfix/pr-45903
fix: allow scrap item with zero qty (backport #45903)
2025-02-16 13:28:00 +05:30
mergify[bot]
e271a5cba0 feat: disable auto setting grand total to default mode of payment (backport #45591) (#45917)
feat: disable auto setting grand total to default mode of payment (#45591)

(cherry picked from commit f0a6399056)

Co-authored-by: Diptanil Saha <diptanil@frappe.io>
2025-02-14 17:34:37 +05:30
ruthra kumar
a486e2962e Merge pull request #45919 from frappe/mergify/bp/version-15-hotfix/pr-45912
fix: include missing payment_gateway parameter in Payment Request URL (backport #45912)
2025-02-14 17:13:29 +05:30
Navin-S-R
18f94765f7 fix: include missing payment_gateway parameter in Payment Request URL
(cherry picked from commit dbac8cfc94)
2025-02-14 11:20:17 +00:00
Rohit Waghchaure
abe5384449 fix: allow scrap item with zero qty
(cherry picked from commit 706cb64279)
2025-02-14 09:47:30 +00:00
rohitwaghchaure
698b7a9d00 Merge pull request #45764 from frappe/mergify/bp/version-15-hotfix/pr-45763
fix: stock reservation not working for sales invoice with update stock (backport #45763)
2025-02-14 15:16:11 +05:30
rohitwaghchaure
52761affe2 chore: fix test case 2025-02-13 16:32:56 +05:30
Rohit Waghchaure
1fb5586f56 fix: stock reservation for sales invoice 2025-02-13 16:04:05 +05:30
ruthra kumar
ef2cddd338 Merge pull request #45894 from frappe/mergify/bp/version-15-hotfix/pr-45804
fix(report): add options to multiselectlist fields (backport #45804)
2025-02-13 14:26:31 +05:30
ruthra kumar
dbe14d6fe4 chore: resolve conflicts 2025-02-13 14:23:46 +05:30
venkat102
7e85a123b2 fix(report): add options to multiselectlist fields
(cherry picked from commit 8785342fce)

# Conflicts:
#	erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.js
#	erpnext/accounts/report/supplier_ledger_summary/supplier_ledger_summary.js
2025-02-13 08:47:15 +00:00
Frappe PR Bot
ce90d427e8 chore(release): Bumped to Version 15.52.0
# [15.52.0](https://github.com/frappe/erpnext/compare/v15.51.2...v15.52.0) (2025-02-12)

### Bug Fixes

* '0' rate LDC's Invoice net totals should be ignored ([96c19cd](96c19cd990))
* add allow_on_submit for party_balance, paid_from_account_balance and paid_to_account_balance ([086c36f](086c36fca6))
* add precision in serial_batch_bundle.py ([a85f6f5](a85f6f54fe))
* add total row in non_grouped_invoices ([e432ae9](e432ae98a9))
* added validation for required invoice_fields in POS (backport [#45780](https://github.com/frappe/erpnext/issues/45780)) ([#45868](https://github.com/frappe/erpnext/issues/45868)) ([4f9a7f5](4f9a7f5065))
* Attibute error `selling_price_list` ([6dc99f9](6dc99f95c0))
* changed naming series to random for SABB ([48a4eff](48a4effdb6))
* check_item_quality_inspection is not whitelisted ([0a4a093](0a4a09352a))
* correct amount in transaction currency for reverse gl entries (backport [#45794](https://github.com/frappe/erpnext/issues/45794)) ([#45849](https://github.com/frappe/erpnext/issues/45849)) ([b06bd82](b06bd825c1))
* correct amt in account currency for lcv with manually distributed charges. (backport [#45532](https://github.com/frappe/erpnext/issues/45532)) ([#45864](https://github.com/frappe/erpnext/issues/45864)) ([179cb1e](179cb1e6e5))
* correct pay amount in portal pages ([3ada520](3ada520618))
* create job card with wip warehouse set to source warehouse if material transfer to wip warehouse is skipped in work order ([9d6f318](9d6f3180d4))
* do not allow "Finance Book" in Accounting Dimensions (backport [#45696](https://github.com/frappe/erpnext/issues/45696)) ([#45856](https://github.com/frappe/erpnext/issues/45856)) ([0954aca](0954aca758))
* do not validate party against  Receivable and Payable account for cancelled gl entries ([6d777cd](6d777cdc68))
* dont update rate of free item on save ([1d3da4d](1d3da4d49a))
* handle response when json is None ([eeb322b](eeb322bd0e))
* map project from rfq to supplier quotation (backport [#45745](https://github.com/frappe/erpnext/issues/45745)) ([#45828](https://github.com/frappe/erpnext/issues/45828)) ([b112d88](b112d88767))
* not able to select the item in the sales invoice ([a649001](a649001886))
* Party name in Supplier Portal for Purchase Order (backport [#45772](https://github.com/frappe/erpnext/issues/45772)) ([#45858](https://github.com/frappe/erpnext/issues/45858)) ([435c354](435c35414f))
* pos numpad editable action buttons (backport [#45823](https://github.com/frappe/erpnext/issues/45823)) ([#45826](https://github.com/frappe/erpnext/issues/45826)) ([035758f](035758f47d))
* possible model sync issue ([ea01fa1](ea01fa135e))
* **regional:** removed payment schedule validation in sales invoice for italy (backport [#45852](https://github.com/frappe/erpnext/issues/45852)) ([#45854](https://github.com/frappe/erpnext/issues/45854)) ([2e9e355](2e9e355329))
* remove serial no if qty is zero ([1359a77](1359a77e72))
* skip warning for free items ([2adab1d](2adab1d36f))
* stock reco current valuation rate ([3b7c38d](3b7c38da10))
* the project document timed out while opening ([28cbce4](28cbce4356))
* unable to remove image from employee ([e3cceb8](e3cceb894b))
* update ctx to args ([d4bc3d1](d4bc3d182f))

### Features

* add repost accounting ledger entry for payment entry ([757dd3f](757dd3f0b6))
2025-02-12 12:09:09 +00:00
ruthra kumar
0eb4556c83 Merge pull request #45860 from frappe/version-15-hotfix
chore: release v15
2025-02-12 17:37:43 +05:30
rohitwaghchaure
45fa2eb542 Merge pull request #45884 from frappe/mergify/bp/version-15-hotfix/pr-45786
fix: skip warning for free items (backport #45786)
2025-02-12 15:16:29 +05:30
barredterra
2adab1d36f fix: skip warning for free items
(cherry picked from commit 772776ad8a)
2025-02-12 09:20:15 +00:00
rohitwaghchaure
162d1ba472 chore: fix test case 2025-02-12 14:32:06 +05:30
rohitwaghchaure
4889950a9e chore: fix conflicts 2025-02-12 14:32:05 +05:30
Rohit Waghchaure
7d871f6bb5 fix: stock reservation not working for sales invoice with update stock
(cherry picked from commit 0c9d0ea1f4)

# Conflicts:
#	erpnext/selling/doctype/sales_order/test_sales_order.py
2025-02-12 14:32:05 +05:30
rohitwaghchaure
70bac1ab17 Merge pull request #45748 from frappe/mergify/bp/version-15-hotfix/pr-45739
fix: create job card with wip warehouse set to source warehouse if material transfer to wip warehouse is skipped in work order (backport #45739)
2025-02-12 13:00:19 +05:30
rohitwaghchaure
0caaff0758 Merge pull request #45880 from frappe/mergify/bp/version-15-hotfix/pr-45879
fix: changed naming series to random for SABB (backport #45879)
2025-02-12 10:53:47 +05:30
rohitwaghchaure
4094fbd6c5 chore: fix conflicts 2025-02-12 10:21:44 +05:30
Rohit Waghchaure
48a4effdb6 fix: changed naming series to random for SABB
(cherry picked from commit a007dc285d)

# Conflicts:
#	erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.json
2025-02-12 04:50:41 +00:00
rohitwaghchaure
68ea528841 Merge pull request #45877 from frappe/mergify/bp/version-15-hotfix/pr-45875
fix: add precision in serial_batch_bundle.py (backport #45875)
2025-02-12 09:25:31 +05:30
rohitwaghchaure
4aa7666e46 Merge pull request #45878 from frappe/mergify/bp/version-15-hotfix/pr-45865
fix: dont update rate of free item on save (backport #45865)
2025-02-12 09:25:08 +05:30
Mihir Kandoi
1d3da4d49a fix: dont update rate of free item on save
(cherry picked from commit 6591e76a63)
2025-02-12 03:13:41 +00:00
Mihir Kandoi
a85f6f54fe fix: add precision in serial_batch_bundle.py
(cherry picked from commit 4bf85d1a5a)
2025-02-12 03:06:27 +00:00
rohitwaghchaure
da08b4eeb9 Merge pull request #45870 from frappe/mergify/bp/version-15-hotfix/pr-45869
fix: stock reco current valuation rate (backport #45869)
2025-02-11 18:38:12 +05:30
rohitwaghchaure
b4f291d0ae Merge pull request #45871 from frappe/mergify/bp/version-15-hotfix/pr-45862
fix: remove serial no if qty is zero (backport #45862)
2025-02-11 18:21:21 +05:30
mergify[bot]
4f9a7f5065 fix: added validation for required invoice_fields in POS (backport #45780) (#45868)
* fix: added validation for required invoice_fields in POS (#45780)

fix: added missing validation for required invoice_fields
(cherry picked from commit b95b13ecd8)

# Conflicts:
#	erpnext/selling/page/point_of_sale/pos_payment.js

* fix: resolved merge conflict

---------

Co-authored-by: Diptanil Saha <diptanil@frappe.io>
2025-02-11 17:55:07 +05:30
Rohit Waghchaure
1359a77e72 fix: remove serial no if qty is zero
(cherry picked from commit 3a4ae8c463)
2025-02-11 11:28:16 +00:00
Rohit Waghchaure
3b7c38da10 fix: stock reco current valuation rate
(cherry picked from commit 8d8f3afb39)
2025-02-11 11:24:14 +00:00
mergify[bot]
179cb1e6e5 fix: correct amt in account currency for lcv with manually distributed charges. (backport #45532) (#45864)
fix: correct amt in account currency for lcv with manually distributed charges.

(cherry picked from commit db38e7bf5a)

Co-authored-by: ljain112 <ljain112@gmail.com>
2025-02-11 16:05:42 +05:30
mergify[bot]
435c35414f fix: Party name in Supplier Portal for Purchase Order (backport #45772) (#45858)
fix: Party name in Supplier Portal for Purchase Order

(cherry picked from commit fc8663421b)

Co-authored-by: ljain112 <ljain112@gmail.com>
2025-02-11 15:42:45 +05:30
mergify[bot]
0954aca758 fix: do not allow "Finance Book" in Accounting Dimensions (backport #45696) (#45856)
fix: do not allow "Finance Book" in Accounting Dimensions

(cherry picked from commit a44be73a98)

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

(cherry picked from commit 494310293c)

Co-authored-by: Lakshit Jain <108322669+ljain112@users.noreply.github.com>
2025-02-11 15:41:30 +05:30
Smit Vora
6b8826f234 Merge pull request #45773 from ljain112/fix-portalpay
fix: correct pay amount in portal pages
2025-02-11 14:59:03 +05:30
mergify[bot]
b112d88767 fix: map project from rfq to supplier quotation (backport #45745) (#45828)
* fix: map project from rfq to supplier quotation

(cherry picked from commit d0479036bb)

* fix: add project field map from mr to rfq

(cherry picked from commit 8fa39bec61)

---------

Co-authored-by: HenningWendtland <156231187+HenningWendtland@users.noreply.github.com>
2025-02-11 14:52:13 +05:30
mergify[bot]
b06bd825c1 fix: correct amount in transaction currency for reverse gl entries (backport #45794) (#45849)
fix: correct amount in tansaction currency for reverse gl entries

(cherry picked from commit 6077c248b0)

Co-authored-by: ljain112 <ljain112@gmail.com>
2025-02-11 14:49:27 +05:30
ruthra kumar
b084433158 Merge pull request #45847 from frappe/mergify/bp/version-15-hotfix/pr-45792
fix: do not validate party against Receivable and Payable account for cancelled gl entries (backport #45792)
2025-02-11 12:53:29 +05:30
ljain112
6d777cdc68 fix: do not validate party against Receivable and Payable account for cancelled gl entries
(cherry picked from commit 0809e00455)
2025-02-11 06:44:50 +00:00
ruthra kumar
ebce6be23f Merge pull request #45844 from frappe/mergify/bp/version-15-hotfix/pr-45781
fix: Added Total Row for `Gross Profit` Report in Non-Grouped Invoices (backport #45781)
2025-02-11 12:01:11 +05:30
Sanket322
e432ae98a9 fix: add total row in non_grouped_invoices
(cherry picked from commit 2d32ddacc3)
2025-02-11 04:51:47 +00:00
ruthra kumar
09114e6a7b Merge pull request #45842 from frappe/mergify/bp/version-15-hotfix/pr-45832
fix: possible model sync issue (backport #45832)
2025-02-11 10:20:21 +05:30
ruthra kumar
ea01fa135e fix: possible model sync issue
(cherry picked from commit 0069581aa3)
2025-02-11 04:25:34 +00:00
Frappe PR Bot
b0c9fbe9fc chore(release): Bumped to Version 15.51.2
## [15.51.2](https://github.com/frappe/erpnext/compare/v15.51.1...v15.51.2) (2025-02-10)

### Bug Fixes

* Attibute error `selling_price_list` ([e8fd2ee](e8fd2eeaa2))
* check_item_quality_inspection is not whitelisted ([d0dbfa1](d0dbfa1cbd))
2025-02-10 16:28:24 +00:00
rohitwaghchaure
0758192c53 Merge pull request #45836 from frappe/mergify/bp/version-15/pr-45835
fix: check_item_quality_inspection is not whitelisted (backport #45835)
2025-02-10 21:56:59 +05:30
rohitwaghchaure
d2cb659220 Merge pull request #45834 from frappe/mergify/bp/version-15/pr-45822
fix: AttributeError in `get_item_details` when selecting/scanning batch in Delivery Note (backport #45774) (backport #45822)
2025-02-10 21:56:40 +05:30
Rohit Waghchaure
d0dbfa1cbd fix: check_item_quality_inspection is not whitelisted
(cherry picked from commit 0a4a09352a)
2025-02-10 15:46:46 +00:00
rohitwaghchaure
b9b2fc5773 Merge pull request #45835 from rohitwaghchaure/fixed-support-31452
fix: check_item_quality_inspection is not whitelisted
2025-02-10 21:15:27 +05:30
Rohit Waghchaure
0a4a09352a fix: check_item_quality_inspection is not whitelisted 2025-02-10 19:09:02 +05:30
DaizyModi
e8fd2eeaa2 fix: Attibute error selling_price_list
(cherry picked from commit 820b32eb8a)
(cherry picked from commit 6dc99f95c0)
2025-02-10 13:34:14 +00:00
rohitwaghchaure
51545234d3 Merge pull request #45822 from frappe/mergify/bp/version-15-hotfix/pr-45774
fix: AttributeError in `get_item_details` when selecting/scanning batch in Delivery Note (backport #45774)
2025-02-10 19:02:51 +05:30
mergify[bot]
035758f47d fix: pos numpad editable action buttons (backport #45823) (#45826)
fix: pos numpad editable action buttons (#45823)

(cherry picked from commit 0b9c28620f)

Co-authored-by: Diptanil Saha <diptanil@frappe.io>
2025-02-10 16:28:59 +05:30
DaizyModi
6dc99f95c0 fix: Attibute error selling_price_list
(cherry picked from commit 820b32eb8a)
2025-02-10 10:19:49 +00:00
ruthra kumar
f440795809 Merge pull request #45816 from frappe/mergify/bp/version-15-hotfix/pr-45747
fix: Handle Empty JSON in Report Parsing (backport #45747)
2025-02-10 12:10:43 +05:30
Sanket322
eeb322bd0e fix: handle response when json is None
(cherry picked from commit 133e0417b8)
2025-02-10 06:12:45 +00:00
ruthra kumar
9a5aa8eeb9 Merge pull request #45813 from frappe/mergify/bp/version-15-hotfix/pr-45793
fix: unable to remove image from employee (backport #45793)
2025-02-10 11:01:00 +05:30
ruthra kumar
1f967f7e9d Merge pull request #45814 from frappe/mergify/bp/version-15-hotfix/pr-45762
refactor: set received and paid amount based on each other, if unset (backport #45762)
2025-02-10 11:00:48 +05:30
rohitwaghchaure
3c386c2062 Merge pull request #45811 from frappe/mergify/bp/version-15-hotfix/pr-45810
fix: not able to select the item in the sales invoice (backport #45810)
2025-02-10 10:41:31 +05:30
ruthra kumar
1d6c50c9a1 chore: resolve conflict 2025-02-10 10:38:18 +05:30
ruthra kumar
e589c5b6ef refactor: set paid amount based on received amount if unset
(cherry picked from commit 99e721e622)
2025-02-10 05:08:05 +00:00
ruthra kumar
6638b391ff refactor: set received amount based on paid amount
(cherry picked from commit 5ff540bd82)
2025-02-10 05:08:05 +00:00
Asmita Hase
e3cceb894b fix: unable to remove image from employee
fix: employee image disappears when newly created user_id is linked to employee

(cherry picked from commit 0207d2d7b6)

# Conflicts:
#	erpnext/setup/doctype/employee/employee.json
2025-02-10 05:03:12 +00:00
Rohit Waghchaure
a649001886 fix: not able to select the item in the sales invoice
(cherry picked from commit 35388e7a04)
2025-02-10 04:39:40 +00:00
rohitwaghchaure
39e3746a62 Merge pull request #45775 from frappe/mergify/bp/version-15-hotfix/pr-45767
fix: the project document timed out while opening (backport #45767)
2025-02-10 10:09:38 +05:30
Frappe PR Bot
8c57e9f8c8 chore(release): Bumped to Version 15.51.1
## [15.51.1](https://github.com/frappe/erpnext/compare/v15.51.0...v15.51.1) (2025-02-07)

### Bug Fixes

* update ctx to args ([13bebe7](13bebe71b0))
2025-02-07 14:22:13 +00:00
ruthra kumar
5f62fc5a99 Merge pull request #45795 from frappe/mergify/bp/version-15/pr-45770
Fix: Update `ctx` to `args` for compatibility. (backport #45770)
2025-02-07 19:50:45 +05:30
Sanket322
13bebe71b0 fix: update ctx to args
(cherry picked from commit d4bc3d182f)
2025-02-07 14:14:41 +00:00
ruthra kumar
9702a192d7 Merge pull request #45770 from Sanket322/update_ctx_to_args
Fix: Update `ctx` to `args` for compatibility.
2025-02-07 19:43:53 +05:30
ruthra kumar
a051ddee6d Merge pull request #45788 from frappe/mergify/bp/version-15-hotfix/pr-45640
feat: add repost accounting ledger entry for payment entry (backport #45640)
2025-02-07 17:15:33 +05:30
ruthra kumar
be09700d8b Merge pull request #45789 from frappe/mergify/bp/version-15-hotfix/pr-45644
fix: add allow_on_submit for party_balance, paid_from_account_balance and paid_to_account_balance (backport #45644)
2025-02-07 17:15:22 +05:30
l0gesh29
757dd3f0b6 feat: add repost accounting ledger entry for payment entry
(cherry picked from commit 5676d60ed3)
2025-02-07 10:41:54 +00:00
Sugesh393
086c36fca6 fix: add allow_on_submit for party_balance, paid_from_account_balance and paid_to_account_balance
(cherry picked from commit 707c01487e)
2025-02-07 10:41:54 +00:00
ruthra kumar
a7d32b580f Merge pull request #45784 from frappe/mergify/bp/version-15-hotfix/pr-45639
fix: '0' rate LDC's Invoice net totals should be ignored (backport #45639)
2025-02-07 13:43:58 +05:30
ruthra kumar
43d75b96c6 chore: resolve conflict 2025-02-07 13:18:42 +05:30
ruthra kumar
3734289983 test: ldc @ 0 rate
(cherry picked from commit 0cdd346f8f)

# Conflicts:
#	erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
2025-02-07 07:44:11 +00:00
ruthra kumar
96c19cd990 fix: '0' rate LDC's Invoice net totals should be ignored
(cherry picked from commit 325c4e3536)
2025-02-07 07:44:11 +00:00
rohitwaghchaure
08a6f4e6d3 chore: fix conflicts 2025-02-06 22:09:44 +05:30
Rohit Waghchaure
28cbce4356 fix: the project document timed out while opening
(cherry picked from commit 33d03b1542)

# Conflicts:
#	erpnext/selling/doctype/sales_order/sales_order.json
2025-02-06 16:38:46 +00:00
ljain112
3ada520618 fix: correct pay amount in portal pages 2025-02-06 18:56:29 +05:30
Sanket322
d4bc3d182f fix: update ctx to args 2025-02-06 17:55:37 +05:30
Mihir Kandoi
4196986273 chore: resolve conflicts 2025-02-05 17:59:34 +05:30
Mihir Kandoi
9d6f3180d4 fix: create job card with wip warehouse set to source warehouse if material transfer to wip warehouse is skipped in work order
(cherry picked from commit 723e902470)

# Conflicts:
#	erpnext/manufacturing/doctype/work_order/work_order.py
2025-02-05 12:19:49 +00:00
234 changed files with 8992 additions and 1275 deletions

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

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

View File

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

View File

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

View File

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

View File

@@ -41,6 +41,11 @@ class AccountingDimension(Document):
self.set_fieldname_and_label()
def validate(self):
self.validate_doctype()
validate_column_name(self.fieldname)
self.validate_dimension_defaults()
def validate_doctype(self):
if self.document_type in (
*core_doctypes_list,
"Accounting Dimension",
@@ -49,6 +54,7 @@ class AccountingDimension(Document):
"Accounting Dimension Detail",
"Company",
"Account",
"Finance Book",
):
msg = _("Not allowed to create accounting dimension for {0}").format(self.document_type)
frappe.throw(msg)
@@ -61,9 +67,6 @@ 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):
doctype_before_save = frappe.db.get_value("Accounting Dimension", self.name, "document_type")
if doctype_before_save != self.document_type:
@@ -102,6 +105,7 @@ class AccountingDimension(Document):
def on_update(self):
frappe.flags.accounting_dimensions = None
frappe.flags.accounting_dimensions_details = None
def make_dimension_in_accounting_doctypes(doc, doclist=None):
@@ -262,7 +266,7 @@ def get_checks_for_pl_and_bs_accounts():
frappe.flags.accounting_dimensions_details = frappe.db.sql(
"""SELECT p.label, p.disabled, p.fieldname, c.default_dimension, c.company, c.mandatory_for_pl, c.mandatory_for_bs
FROM `tabAccounting Dimension`p ,`tabAccounting Dimension Detail` c
WHERE p.name = c.parent""",
WHERE p.name = c.parent AND p.disabled = 0""",
as_dict=1,
)

View File

@@ -374,9 +374,36 @@ def auto_reconcile_vouchers(
to_reference_date=None,
):
frappe.flags.auto_reconcile_vouchers = True
reconciled, partially_reconciled = set(), set()
bank_transactions = get_bank_transactions(bank_account)
if len(bank_transactions) > 10:
frappe.enqueue(
method="erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.start_auto_reconcile",
queue="long",
bank_transactions=bank_transactions,
from_date=from_date,
to_date=to_date,
filter_by_reference_date=filter_by_reference_date,
from_reference_date=from_reference_date,
to_reference_date=to_reference_date,
)
frappe.msgprint(_("Auto Reconciliation has started in the background"))
else:
start_auto_reconcile(
bank_transactions,
from_date,
to_date,
filter_by_reference_date,
from_reference_date,
to_reference_date,
)
def start_auto_reconcile(
bank_transactions, from_date, to_date, filter_by_reference_date, from_reference_date, to_reference_date
):
reconciled, partially_reconciled = set(), set()
for transaction in bank_transactions:
linked_payments = get_linked_payments(
transaction.name,
@@ -414,7 +441,6 @@ def auto_reconcile_vouchers(
frappe.msgprint(title=_("Auto Reconciliation"), msg=alert_message, indicator=indicator)
frappe.flags.auto_reconcile_vouchers = False
return reconciled, partially_reconciled
def get_auto_reconcile_message(partially_reconciled, reconciled):

View File

@@ -128,7 +128,7 @@ class TestCouponCode(unittest.TestCase):
item_code="_Test Tesla Car",
rate=5000,
qty=1,
do_not_submit=True,
do_not_save=True,
)
self.assertEqual(so.items[0].rate, 5000)

View File

@@ -279,7 +279,8 @@
{
"fieldname": "transaction_exchange_rate",
"fieldtype": "Float",
"label": "Transaction Exchange Rate"
"label": "Transaction Exchange Rate",
"precision": "9"
},
{
"fieldname": "debit_in_transaction_currency",
@@ -357,7 +358,7 @@
"idx": 1,
"in_create": 1,
"links": [],
"modified": "2024-08-22 13:03:39.997475",
"modified": "2025-02-21 14:36:49.431166",
"modified_by": "Administrator",
"module": "Accounts",
"name": "GL Entry",

View File

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

View File

@@ -124,3 +124,20 @@ class TestGLEntry(unittest.TestCase):
str(e),
"Party Type and Party can only be set for Receivable / Payable account_Test Account Cost for Goods Sold - _TC",
)
def test_validate_account_party_type_shareholder(self):
jv = make_journal_entry(
"Opening Balance Equity - _TC",
"Cash - _TC",
100,
"_Test Cost Center - _TC",
save=False,
submit=False,
)
for row in jv.accounts:
row.party_type = "Shareholder"
break
jv.save().submit()
self.assertEqual(1, jv.docstatus)

View File

@@ -141,6 +141,7 @@ class JournalEntry(AccountsController):
self.validate_empty_accounts_table()
self.validate_inter_company_accounts()
self.validate_depr_entry_voucher_type()
self.validate_company_in_accounting_dimension()
self.validate_advance_accounts()
if self.docstatus == 0:
@@ -1059,14 +1060,15 @@ class JournalEntry(AccountsController):
gl_map = []
company_currency = erpnext.get_company_currency(self.company)
self.transaction_currency = company_currency
self.transaction_exchange_rate = 1
if self.multi_currency:
for row in self.get("accounts"):
if row.account_currency != company_currency:
self.currency = row.account_currency
self.conversion_rate = row.exchange_rate
# Journal assumes the first foreign currency as transaction currency
self.transaction_currency = row.account_currency
self.transaction_exchange_rate = row.exchange_rate
break
else:
self.currency = company_currency
for d in self.get("accounts"):
if d.debit or d.credit or (self.voucher_type == "Exchange Gain Or Loss"):
@@ -1091,6 +1093,18 @@ class JournalEntry(AccountsController):
"credit_in_account_currency": flt(
d.credit_in_account_currency, d.precision("credit_in_account_currency")
),
"transaction_currency": self.transaction_currency,
"transaction_exchange_rate": self.transaction_exchange_rate,
"debit_in_transaction_currency": flt(
d.debit_in_account_currency, d.precision("debit_in_account_currency")
)
if self.transaction_currency == d.account_currency
else flt(d.debit, d.precision("debit")) / self.transaction_exchange_rate,
"credit_in_transaction_currency": flt(
d.credit_in_account_currency, d.precision("credit_in_account_currency")
)
if self.transaction_currency == d.account_currency
else flt(d.credit, d.precision("credit")) / self.transaction_exchange_rate,
"against_voucher_type": d.reference_type,
"against_voucher": d.reference_name,
"remarks": remarks,

View File

@@ -575,7 +575,7 @@ class TestJournalEntry(unittest.TestCase):
order_by="account",
)
expected = [
{"account": "_Test Bank - _TC", "transaction_exchange_rate": 1.0},
{"account": "_Test Bank - _TC", "transaction_exchange_rate": 85.0},
{"account": "_Test Receivable USD - _TC", "transaction_exchange_rate": 85.0},
]
self.assertEqual(expected, actual)
@@ -591,13 +591,14 @@ def make_journal_entry(
save=True,
submit=False,
project=None,
company=None,
):
if not cost_center:
cost_center = "_Test Cost Center - _TC"
jv = frappe.new_doc("Journal Entry")
jv.posting_date = posting_date or nowdate()
jv.company = "_Test Company"
jv.company = company or "_Test Company"
jv.user_remark = "test"
jv.multi_currency = 1
jv.set(

View File

@@ -258,6 +258,10 @@ frappe.ui.form.on("Payment Entry", {
frappe.flags.allocate_payment_amount = true;
},
validate: async function (frm) {
await frm.events.set_exchange_gain_loss_deduction(frm);
},
validate_company: (frm) => {
if (!frm.doc.company) {
frappe.throw({ message: __("Please select a Company first."), title: __("Mandatory") });
@@ -812,27 +816,41 @@ frappe.ui.form.on("Payment Entry", {
paid_amount: function (frm) {
frm.set_value("base_paid_amount", flt(frm.doc.paid_amount) * flt(frm.doc.source_exchange_rate));
let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
if (!frm.doc.received_amount) {
if (frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency) {
frm.set_value("received_amount", frm.doc.paid_amount);
} else if (company_currency == frm.doc.paid_to_account_currency) {
frm.set_value("received_amount", frm.doc.base_paid_amount);
frm.set_value("base_received_amount", frm.doc.base_paid_amount);
}
}
frm.trigger("reset_received_amount");
frm.events.hide_unhide_fields(frm);
},
received_amount: function (frm) {
let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
frm.set_paid_amount_based_on_received_amount = true;
if (!frm.doc.paid_amount && frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency) {
frm.set_value("paid_amount", frm.doc.received_amount);
if (frm.doc.target_exchange_rate) {
frm.set_value("source_exchange_rate", frm.doc.target_exchange_rate);
}
frm.set_value("base_paid_amount", frm.doc.base_received_amount);
}
frm.set_value(
"base_received_amount",
flt(frm.doc.received_amount) * flt(frm.doc.target_exchange_rate)
);
if (!frm.doc.paid_amount) {
if (frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency) {
frm.set_value("paid_amount", frm.doc.received_amount);
if (frm.doc.target_exchange_rate) {
frm.set_value("source_exchange_rate", frm.doc.target_exchange_rate);
}
frm.set_value("base_paid_amount", frm.doc.base_received_amount);
} else if (company_currency == frm.doc.paid_from_account_currency) {
frm.set_value("paid_amount", frm.doc.base_received_amount);
frm.set_value("base_paid_amount", frm.doc.base_received_amount);
}
}
if (frm.doc.payment_type == "Pay")
frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.received_amount, true);
else frm.events.set_unallocated_amount(frm);
@@ -1879,8 +1897,6 @@ function prompt_for_missing_account(frm, account) {
(values) => resolve(values?.[account]),
__("Please Specify Account")
);
dialog.on_hide = () => resolve("");
});
}

View File

@@ -224,6 +224,7 @@
"label": "Accounts"
},
{
"allow_on_submit": 1,
"depends_on": "party",
"fieldname": "party_balance",
"fieldtype": "Currency",
@@ -253,6 +254,7 @@
"reqd": 1
},
{
"allow_on_submit": 1,
"depends_on": "paid_from",
"fieldname": "paid_from_account_balance",
"fieldtype": "Currency",
@@ -286,6 +288,7 @@
"reqd": 1
},
{
"allow_on_submit": 1,
"depends_on": "paid_to",
"fieldname": "paid_to_account_balance",
"fieldtype": "Currency",
@@ -806,7 +809,7 @@
"table_fieldname": "payment_entries"
}
],
"modified": "2025-01-13 16:03:47.169699",
"modified": "2025-01-31 17:27:28.555246",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Entry",

View File

@@ -25,6 +25,10 @@ from erpnext.accounts.doctype.invoice_discounting.invoice_discounting import (
get_party_account_based_on_invoice_discounting,
)
from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
from erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger import (
validate_docs_for_deferred_accounting,
validate_docs_for_voucher_types,
)
from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import (
get_party_tax_withholding_details,
)
@@ -114,6 +118,23 @@ class PaymentEntry(AccountsController):
self.update_advance_paid() # advance_paid_status depends on the payment request amount
self.set_status()
def validate_for_repost(self):
validate_docs_for_voucher_types(["Payment Entry"])
validate_docs_for_deferred_accounting([self.name], [])
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={"references": [], "taxes": [], "deductions": []}
)
if self.needs_repost:
self.validate_for_repost()
self.repost_accounting_entries()
def set_liability_account(self):
# Auto setting liability account should only be done during 'draft' status
if self.docstatus > 0 or self.payment_type == "Internal Transfer":
@@ -1196,15 +1217,22 @@ class PaymentEntry(AccountsController):
self.set("remarks", "\n".join(remarks))
def set_transaction_currency_and_rate(self):
company_currency = erpnext.get_company_currency(self.company)
self.transaction_currency = company_currency
self.transaction_exchange_rate = 1
if self.paid_from_account_currency != company_currency:
self.transaction_currency = self.paid_from_account_currency
self.transaction_exchange_rate = self.source_exchange_rate
elif self.paid_to_account_currency != company_currency:
self.transaction_currency = self.paid_to_account_currency
self.transaction_exchange_rate = self.target_exchange_rate
def build_gl_map(self):
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
self.set_transaction_currency_and_rate()
gl_entries = []
self.add_party_gl_entries(gl_entries)
@@ -1283,6 +1311,9 @@ class PaymentEntry(AccountsController):
"cost_center": cost_center,
dr_or_cr + "_in_account_currency": d.allocated_amount,
dr_or_cr: allocated_amount_in_company_currency,
dr_or_cr + "_in_transaction_currency": d.allocated_amount
if self.transaction_currency == self.party_account_currency
else allocated_amount_in_company_currency / self.transaction_exchange_rate,
},
item=self,
)
@@ -1327,6 +1358,9 @@ class PaymentEntry(AccountsController):
"account_currency": self.party_account_currency,
"cost_center": self.cost_center,
dr_or_cr + "_in_account_currency": self.unallocated_amount,
dr_or_cr + "_in_transaction_currency": self.unallocated_amount
if self.party_account_currency == self.transaction_currency
else base_unallocated_amount / self.transaction_exchange_rate,
dr_or_cr: base_unallocated_amount,
},
item=self,
@@ -1344,6 +1378,7 @@ class PaymentEntry(AccountsController):
def make_advance_gl_entries(
self, entry: object | dict = None, cancel: bool = 0, update_outstanding: str = "Yes"
):
self.set_transaction_currency_and_rate()
gl_entries = []
self.add_advance_gl_entries(gl_entries, entry)
@@ -1423,9 +1458,16 @@ class PaymentEntry(AccountsController):
frappe.db.set_value("Payment Entry Reference", invoice.name, "reconcile_effect_on", posting_date)
dr_or_cr, account = self.get_dr_and_account_for_advances(invoice)
base_allocated_amount = self.calculate_base_allocated_amount_for_reference(invoice)
args_dict["account"] = account
args_dict[dr_or_cr] = self.calculate_base_allocated_amount_for_reference(invoice)
args_dict[dr_or_cr] = base_allocated_amount
args_dict[dr_or_cr + "_in_account_currency"] = invoice.allocated_amount
args_dict[dr_or_cr + "_in_transaction_currency"] = (
invoice.allocated_amount
if self.party_account_currency == self.transaction_currency
else base_allocated_amount / self.transaction_exchange_rate
)
args_dict.update(
{
"against_voucher_type": invoice.reference_doctype,
@@ -1443,8 +1485,13 @@ class PaymentEntry(AccountsController):
args_dict[dr_or_cr + "_in_account_currency"] = 0
dr_or_cr = "debit" if dr_or_cr == "credit" else "credit"
args_dict["account"] = self.party_account
args_dict[dr_or_cr] = self.calculate_base_allocated_amount_for_reference(invoice)
args_dict[dr_or_cr] = base_allocated_amount
args_dict[dr_or_cr + "_in_account_currency"] = invoice.allocated_amount
args_dict[dr_or_cr + "_in_transaction_currency"] = (
invoice.allocated_amount
if self.party_account_currency == self.transaction_currency
else base_allocated_amount / self.transaction_exchange_rate
)
args_dict.update(
{
"against_voucher_type": "Payment Entry",
@@ -1466,6 +1513,9 @@ class PaymentEntry(AccountsController):
"account_currency": self.paid_from_account_currency,
"against": self.party if self.payment_type == "Pay" else self.paid_to,
"credit_in_account_currency": self.paid_amount,
"credit_in_transaction_currency": self.paid_amount
if self.paid_from_account_currency == self.transaction_currency
else self.base_paid_amount / self.transaction_exchange_rate,
"credit": self.base_paid_amount,
"cost_center": self.cost_center,
"post_net_value": True,
@@ -1481,6 +1531,9 @@ class PaymentEntry(AccountsController):
"account_currency": self.paid_to_account_currency,
"against": self.party if self.payment_type == "Receive" else self.paid_from,
"debit_in_account_currency": self.received_amount,
"debit_in_transaction_currency": self.received_amount
if self.paid_to_account_currency == self.transaction_currency
else self.base_received_amount / self.transaction_exchange_rate,
"debit": self.base_received_amount,
"cost_center": self.cost_center,
},
@@ -1516,6 +1569,8 @@ class PaymentEntry(AccountsController):
dr_or_cr + "_in_account_currency": base_tax_amount
if account_currency == self.company_currency
else d.tax_amount,
dr_or_cr + "_in_transaction_currency": base_tax_amount
/ self.transaction_exchange_rate,
"cost_center": d.cost_center,
"post_net_value": True,
},
@@ -1541,6 +1596,8 @@ class PaymentEntry(AccountsController):
rev_dr_or_cr + "_in_account_currency": base_tax_amount
if account_currency == self.company_currency
else d.tax_amount,
rev_dr_or_cr + "_in_transaction_currency": base_tax_amount
/ self.transaction_exchange_rate,
"cost_center": self.cost_center,
"post_net_value": True,
},
@@ -1563,6 +1620,7 @@ class PaymentEntry(AccountsController):
"account_currency": account_currency,
"against": self.party or self.paid_from,
"debit_in_account_currency": d.amount,
"debit_in_transaction_currency": d.amount / self.transaction_exchange_rate,
"debit": d.amount,
"cost_center": d.cost_center,
},
@@ -1822,7 +1880,7 @@ class PaymentEntry(AccountsController):
allocated_positive_outstanding = paid_amount + allocated_negative_outstanding
elif self.party_type in ("Supplier", "Employee"):
elif self.party_type in ("Supplier", "Customer"):
if paid_amount > total_negative_outstanding:
if total_negative_outstanding == 0:
frappe.msgprint(
@@ -2833,7 +2891,9 @@ def get_payment_entry(
pe.paid_amount = paid_amount
pe.received_amount = received_amount
pe.letter_head = doc.get("letter_head")
pe.bank_account = frappe.db.get_value("Bank Account", {"is_company_account": 1, "is_default": 1}, "name")
pe.bank_account = frappe.db.get_value(
"Bank Account", {"is_company_account": 1, "is_default": 1, "company": doc.company}, "name"
)
if dt in ["Purchase Order", "Sales Order", "Sales Invoice", "Purchase Invoice"]:
pe.project = doc.get("project") or reduce(
@@ -3316,13 +3376,14 @@ def add_income_discount_loss(pe, doc, total_discount_percent) -> float:
"""Add loss on income discount in base currency."""
precision = doc.precision("total")
base_loss_on_income = doc.get("base_total") * (total_discount_percent / 100)
positive_negative = -1 if pe.payment_type == "Pay" else 1
pe.append(
"deductions",
{
"account": frappe.get_cached_value("Company", pe.company, "default_discount_account"),
"cost_center": pe.cost_center or frappe.get_cached_value("Company", pe.company, "cost_center"),
"amount": flt(base_loss_on_income, precision),
"amount": flt(base_loss_on_income, precision) * positive_negative,
},
)
@@ -3334,6 +3395,7 @@ def add_tax_discount_loss(pe, doc, total_discount_percentage) -> float:
tax_discount_loss = {}
base_total_tax_loss = 0
precision = doc.precision("tax_amount_after_discount_amount", "taxes")
positive_negative = -1 if pe.payment_type == "Pay" else 1
# The same account head could be used more than once
for tax in doc.get("taxes", []):
@@ -3356,7 +3418,7 @@ def add_tax_discount_loss(pe, doc, total_discount_percentage) -> float:
"account": account,
"cost_center": pe.cost_center
or frappe.get_cached_value("Company", pe.company, "cost_center"),
"amount": flt(loss, precision),
"amount": flt(loss, precision) * positive_negative,
},
)

View File

@@ -282,6 +282,48 @@ class TestPaymentEntry(FrappeTestCase):
self.assertEqual(si.payment_schedule[0].paid_amount, 200.0)
self.assertEqual(si.payment_schedule[1].paid_amount, 36.0)
def test_payment_entry_against_payment_terms_with_discount_on_pi(self):
pi = make_purchase_invoice(do_not_save=1)
create_payment_terms_template_with_discount()
pi.payment_terms_template = "Test Discount Template"
frappe.db.set_value("Company", pi.company, "default_discount_account", "Write Off - _TC")
pi.append(
"taxes",
{
"charge_type": "On Net Total",
"account_head": "_Test Account Service Tax - _TC",
"cost_center": "_Test Cost Center - _TC",
"description": "Service Tax",
"rate": 18,
},
)
pi.save()
pi.submit()
frappe.db.set_single_value("Accounts Settings", "book_tax_discount_loss", 1)
pe_with_tax_loss = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Cash - _TC")
self.assertEqual(pe_with_tax_loss.references[0].payment_term, "30 Credit Days with 10% Discount")
self.assertEqual(pe_with_tax_loss.payment_type, "Pay")
self.assertEqual(pe_with_tax_loss.references[0].allocated_amount, 295.0)
self.assertEqual(pe_with_tax_loss.paid_amount, 265.5)
self.assertEqual(pe_with_tax_loss.difference_amount, 0)
self.assertEqual(pe_with_tax_loss.deductions[0].amount, -25.0) # Loss on Income
self.assertEqual(pe_with_tax_loss.deductions[1].amount, -4.5) # Loss on Tax
self.assertEqual(pe_with_tax_loss.deductions[1].account, "_Test Account Service Tax - _TC")
frappe.db.set_single_value("Accounts Settings", "book_tax_discount_loss", 0)
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Cash - _TC")
self.assertEqual(pe.references[0].payment_term, "30 Credit Days with 10% Discount")
self.assertEqual(pe.payment_type, "Pay")
self.assertEqual(pe.references[0].allocated_amount, 295.0)
self.assertEqual(pe.paid_amount, 265.5)
self.assertEqual(pe.deductions[0].amount, -29.5)
self.assertEqual(pe.difference_amount, 0)
def test_payment_entry_against_payment_terms_with_discount(self):
si = create_sales_invoice(do_not_save=1, qty=1, rate=200)
create_payment_terms_template_with_discount()

View File

@@ -313,6 +313,7 @@ class PaymentRequest(Document):
"payer_name": data.customer_name,
"order_id": self.name,
"currency": self.currency,
"payment_gateway": self.payment_gateway,
}
)
@@ -767,26 +768,39 @@ def get_existing_payment_request_amount(ref_dt, ref_dn, statuses: list | None =
def get_existing_paid_amount(doctype, name):
PL = frappe.qb.DocType("Payment Ledger Entry")
PLE = frappe.qb.DocType("Payment Ledger Entry")
PER = frappe.qb.DocType("Payment Entry Reference")
query = (
frappe.qb.from_(PL)
frappe.qb.from_(PLE)
.left_join(PER)
.on(
(PER.reference_doctype == PL.against_voucher_type) & (PER.reference_name == PL.against_voucher_no)
(PLE.against_voucher_type == PER.reference_doctype)
& (PLE.against_voucher_no == PER.reference_name)
& (PLE.voucher_type == PER.parenttype)
& (PLE.voucher_no == PER.parent)
)
.select(
Abs(Sum(PLE.amount)).as_("total_amount"),
Abs(Sum(frappe.qb.terms.Case().when(PER.payment_request.isnotnull(), PLE.amount).else_(0))).as_(
"request_paid_amount"
),
)
.where(
(PLE.voucher_type.isin([doctype, "Journal Entry", "Payment Entry"]))
& (PLE.against_voucher_type == doctype)
& (PLE.against_voucher_no == name)
& (PLE.delinked == 0)
& (PLE.docstatus == 1)
& (PLE.amount < 0)
)
.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
result = query.run()
ledger_amount = flt(result[0][0]) if result else 0
request_paid_amount = flt(result[0][1]) if result else 0
return ledger_amount - request_paid_amount
def get_gateway_details(args): # nosemgrep

View File

@@ -542,6 +542,73 @@ class TestPaymentRequest(FrappeTestCase):
self.assertEqual(pr.grand_total, si.outstanding_amount)
def test_partial_paid_invoice_with_more_payment_entry(self):
pi = make_purchase_invoice(currency="INR", qty=1, rate=500)
pi.submit()
pi_1 = make_purchase_invoice(currency="INR", qty=1, rate=300)
pi_1.submit()
pr = make_payment_request(dt="Purchase Invoice", dn=pi.name, mute_email=1, submit_doc=0, return_doc=1)
pr.grand_total = 200
pr.submit()
pr.create_payment_entry()
pr_1 = make_payment_request(
dt="Purchase Invoice", dn=pi.name, mute_email=1, submit_doc=0, return_doc=1
)
pr_1.grand_total = 200
pr_1.submit()
pr_1.create_payment_entry()
pe = get_payment_entry(dt="Purchase Invoice", dn=pi.name)
pe.paid_amount = 200
pe.references[0].reference_doctype = pi.doctype
pe.references[0].reference_name = pi.name
pe.references[0].grand_total = pi.grand_total
pe.references[0].outstanding_amount = pi.outstanding_amount
pe.references[0].allocated_amount = 100
pe.append(
"references",
{
"reference_doctype": pi_1.doctype,
"reference_name": pi_1.name,
"grand_total": pi_1.grand_total,
"outstanding_amount": pi_1.outstanding_amount,
"allocated_amount": 100,
},
)
pr_2 = make_payment_request(dt="Purchase Invoice", dn=pi.name, mute_email=1)
pi.load_from_db()
self.assertEqual(pr_2.grand_total, pi.outstanding_amount)
def test_consider_journal_entry_and_return_invoice(self):
from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
si = create_sales_invoice(currency="INR", qty=5, rate=500)
je = make_journal_entry("_Test Cash - _TC", "Debtors - _TC", 500, save=False)
je.accounts[1].party_type = "Customer"
je.accounts[1].party = si.customer
je.accounts[1].reference_type = "Sales Invoice"
je.accounts[1].reference_name = si.name
je.accounts[1].credit_in_account_currency = 500
je.submit()
pe = get_payment_entry("Sales Invoice", si.name)
pe.paid_amount = 500
pe.references[0].allocated_amount = 500
pe.save()
pe.submit()
cr_note = create_sales_invoice(qty=-1, rate=500, is_return=1, return_against=si.name, do_not_save=1)
cr_note.update_outstanding_for_self = 0
cr_note.save()
cr_note.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)

View File

@@ -27,6 +27,7 @@ class TestPeriodClosingVoucher(unittest.TestCase):
account1="Cash - TPC",
account2="Sales - TPC",
cost_center=cost_center,
company=company,
save=False,
)
jv1.company = company
@@ -39,6 +40,7 @@ class TestPeriodClosingVoucher(unittest.TestCase):
account1="Cost of Goods Sold - TPC",
account2="Cash - TPC",
cost_center=cost_center,
company=company,
save=False,
)
jv2.company = company
@@ -156,6 +158,7 @@ class TestPeriodClosingVoucher(unittest.TestCase):
amount=400,
cost_center=cost_center,
posting_date="2021-03-15",
company=company,
)
jv.company = company
jv.finance_book = create_finance_book().name
@@ -198,6 +201,7 @@ class TestPeriodClosingVoucher(unittest.TestCase):
account1="Cash - TPC",
account2="Sales - TPC",
cost_center=cost_center,
company=company,
save=False,
)
jv1.company = company
@@ -220,6 +224,7 @@ class TestPeriodClosingVoucher(unittest.TestCase):
account1="Cash - TPC",
account2="Sales - TPC",
cost_center=cost_center1,
company=company,
save=False,
)
jv1.company = company
@@ -232,6 +237,7 @@ class TestPeriodClosingVoucher(unittest.TestCase):
account1="Cash - TPC",
account2="Sales - TPC",
cost_center=cost_center2,
company=company,
save=False,
)
jv2.company = company
@@ -261,6 +267,7 @@ class TestPeriodClosingVoucher(unittest.TestCase):
account1="Cash - TPC",
account2="Sales - TPC",
cost_center=cost_center2,
company=company,
save=False,
)

View File

@@ -39,10 +39,12 @@ class TestPOSClosingEntry(unittest.TestCase):
pos_inv1 = create_pos_invoice(rate=3500, do_not_submit=1)
pos_inv1.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3500})
pos_inv1.save()
pos_inv1.submit()
pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1)
pos_inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3200})
pos_inv2.save()
pos_inv2.submit()
pcv_doc = make_closing_entry_from_opening(opening_entry)
@@ -68,6 +70,7 @@ class TestPOSClosingEntry(unittest.TestCase):
pos_inv = create_pos_invoice(rate=3500, do_not_submit=1, item_name="Test Item", without_item_code=1)
pos_inv.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3500})
pos_inv.save()
pos_inv.submit()
pcv_doc = make_closing_entry_from_opening(opening_entry)
@@ -86,10 +89,12 @@ class TestPOSClosingEntry(unittest.TestCase):
pos_inv1 = create_pos_invoice(rate=3500, do_not_submit=1)
pos_inv1.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3500})
pos_inv1.save()
pos_inv1.submit()
pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1)
pos_inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3200})
pos_inv2.save()
pos_inv2.submit()
# make return entry of pos_inv2
@@ -111,10 +116,12 @@ class TestPOSClosingEntry(unittest.TestCase):
pos_inv1 = create_pos_invoice(rate=3500, do_not_submit=1)
pos_inv1.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3500})
pos_inv1.save()
pos_inv1.submit()
pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1)
pos_inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3200})
pos_inv2.save()
pos_inv2.submit()
pcv_doc = make_closing_entry_from_opening(opening_entry)
@@ -165,6 +172,7 @@ class TestPOSClosingEntry(unittest.TestCase):
opening_entry = create_opening_entry(pos_profile, test_user.name)
pos_inv1 = create_pos_invoice(rate=350, do_not_submit=1, pos_profile=pos_profile.name)
pos_inv1.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3500})
pos_inv1.save()
pos_inv1.submit()
# if in between a mandatory accounting dimension is added to the POS Profile then
@@ -218,11 +226,27 @@ class TestPOSClosingEntry(unittest.TestCase):
opening_entry = create_opening_entry(pos_profile, test_user.name)
pos_inv = create_pos_invoice(
item_code=item_code, qty=5, rate=300, use_serial_batch_fields=1, batch_no=batch_no
item_code=item_code,
qty=5,
rate=300,
use_serial_batch_fields=1,
batch_no=batch_no,
do_not_submit=True,
)
pos_inv.payments[0].amount = pos_inv.grand_total
pos_inv.save()
pos_inv.submit()
pos_inv2 = create_pos_invoice(
item_code=item_code, qty=5, rate=300, use_serial_batch_fields=1, batch_no=batch_no
item_code=item_code,
qty=5,
rate=300,
use_serial_batch_fields=1,
batch_no=batch_no,
do_not_submit=True,
)
pos_inv2.payments[0].amount = pos_inv2.grand_total
pos_inv2.save()
pos_inv2.submit()
batch_qty_with_pos = get_batch_qty(batch_no, "_Test Warehouse - _TC", item_code)
self.assertEqual(batch_qty_with_pos, 0.0)

View File

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

View File

@@ -20,6 +20,10 @@ from erpnext.controllers.queries import item_query as _item_query
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
class PartialPaymentValidationError(frappe.ValidationError):
pass
class POSInvoice(SalesInvoice):
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.
@@ -210,6 +214,7 @@ class POSInvoice(SalesInvoice):
self.validate_payment_amount()
self.validate_loyalty_transaction()
self.validate_company_with_pos_company()
self.validate_full_payment()
if self.coupon_code:
from erpnext.accounts.doctype.pricing_rule.utils import validate_coupon_code
@@ -477,6 +482,20 @@ class POSInvoice(SalesInvoice):
if self.redeem_loyalty_points and self.loyalty_program and self.loyalty_points:
validate_loyalty_points(self, self.loyalty_points)
def validate_full_payment(self):
invoice_total = flt(self.rounded_total) or flt(self.grand_total)
if self.docstatus == 1:
if self.is_return and self.paid_amount != invoice_total:
frappe.throw(
msg=_("Partial Payment in POS Invoice is not allowed."), exc=PartialPaymentValidationError
)
if self.paid_amount < invoice_total:
frappe.throw(
msg=_("Partial Payment in POS Invoice is not allowed."), exc=PartialPaymentValidationError
)
def set_status(self, update=False, status=None, update_modified=True):
if self.is_new():
if self.get("amended_from"):

View File

@@ -7,7 +7,7 @@ import unittest
import frappe
from frappe import _
from erpnext.accounts.doctype.pos_invoice.pos_invoice import make_sales_return
from erpnext.accounts.doctype.pos_invoice.pos_invoice import PartialPaymentValidationError, make_sales_return
from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.stock.doctype.item.test_item import make_item
@@ -313,7 +313,7 @@ class TestPOSInvoice(unittest.TestCase):
)
pos.append(
"payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 1000, "default": 1}
"payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 2000, "default": 1}
)
pos.insert()
@@ -324,6 +324,11 @@ class TestPOSInvoice(unittest.TestCase):
# partial return 1
pos_return1.get("items")[0].qty = -1
pos_return1.set("payments", [])
pos_return1.append(
"payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": -1000, "default": 1}
)
pos_return1.paid_amount = -1000
pos_return1.submit()
pos_return1.reload()
@@ -338,6 +343,11 @@ class TestPOSInvoice(unittest.TestCase):
# partial return 2
pos_return2 = make_sales_return(pos.name)
pos_return2.set("payments", [])
pos_return2.append(
"payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": -1000, "default": 1}
)
pos_return2.paid_amount = -1000
pos_return2.submit()
self.assertEqual(pos_return2.get("items")[0].qty, -1)
@@ -373,6 +383,15 @@ class TestPOSInvoice(unittest.TestCase):
inv.payments = []
self.assertRaises(frappe.ValidationError, inv.insert)
def test_partial_payment(self):
pos_inv = create_pos_invoice(rate=10000, do_not_save=1)
pos_inv.append(
"payments",
{"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 9000},
)
pos_inv.insert()
self.assertRaises(PartialPaymentValidationError, pos_inv.submit)
def test_serialized_item_transaction(self):
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
@@ -581,7 +600,13 @@ class TestPOSInvoice(unittest.TestCase):
"Test Loyalty Customer", company="_Test Company", loyalty_program="Test Single Loyalty"
)
inv = create_pos_invoice(customer="Test Loyalty Customer", rate=10000)
inv = create_pos_invoice(customer="Test Loyalty Customer", rate=10000, do_not_save=1)
inv.append(
"payments",
{"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 10000},
)
inv.insert()
inv.submit()
lpe = frappe.get_doc(
"Loyalty Point Entry",
@@ -607,7 +632,13 @@ class TestPOSInvoice(unittest.TestCase):
)
# add 10 loyalty points
create_pos_invoice(customer="Test Loyalty Customer", rate=10000)
pos_inv = create_pos_invoice(customer="Test Loyalty Customer", rate=10000, do_not_save=1)
pos_inv.append(
"payments",
{"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 10000},
)
pos_inv.paid_amount = 10000
pos_inv.submit()
before_lp_details = get_loyalty_program_details_with_points(
"Test Loyalty Customer", company="_Test Company", loyalty_program="Test Single Loyalty"
@@ -641,10 +672,12 @@ class TestPOSInvoice(unittest.TestCase):
test_user, pos_profile = init_user_and_profile()
pos_inv = create_pos_invoice(rate=300, additional_discount_percentage=10, do_not_submit=1)
pos_inv.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 270})
pos_inv.save()
pos_inv.submit()
pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1)
pos_inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3200})
pos_inv2.save()
pos_inv2.submit()
consolidate_pos_invoices()
@@ -676,6 +709,7 @@ class TestPOSInvoice(unittest.TestCase):
"included_in_print_rate": 1,
},
)
pos_inv.save()
pos_inv.submit()
pos_inv2 = create_pos_invoice(rate=300, qty=2, do_not_submit=1)
@@ -692,6 +726,7 @@ class TestPOSInvoice(unittest.TestCase):
"included_in_print_rate": 1,
},
)
pos_inv2.save()
pos_inv2.submit()
consolidate_pos_invoices()
@@ -744,6 +779,7 @@ class TestPOSInvoice(unittest.TestCase):
"included_in_print_rate": 1,
},
)
pos_inv2.save()
pos_inv2.submit()
consolidate_pos_invoices()
@@ -774,7 +810,10 @@ class TestPOSInvoice(unittest.TestCase):
# POS Invoice 1, for the batch without bundle
pos_inv1 = create_pos_invoice(item="_BATCH ITEM Test For Reserve", rate=300, qty=15, do_not_save=1)
pos_inv1.append(
"payments",
{"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 4500},
)
pos_inv1.items[0].batch_no = batch_no
pos_inv1.save()
pos_inv1.submit()
@@ -790,8 +829,14 @@ class TestPOSInvoice(unittest.TestCase):
# POS Invoice 2, for the batch with bundle
pos_inv2 = create_pos_invoice(
item="_BATCH ITEM Test For Reserve", rate=300, qty=10, batch_no=batch_no
item="_BATCH ITEM Test For Reserve", rate=300, qty=10, batch_no=batch_no, do_not_save=1
)
pos_inv2.append(
"payments",
{"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3000},
)
pos_inv2.save()
pos_inv2.submit()
pos_inv2.reload()
self.assertTrue(pos_inv2.items[0].serial_and_batch_bundle)
@@ -826,6 +871,10 @@ class TestPOSInvoice(unittest.TestCase):
pos_inv1 = create_pos_invoice(
item=item.name, rate=300, qty=1, do_not_submit=1, batch_no="TestBatch 01"
)
pos_inv1.append(
"payments",
{"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 300},
)
pos_inv1.save()
pos_inv1.submit()

View File

@@ -8,11 +8,14 @@ import frappe
from frappe import _
from frappe.model.document import Document
from frappe.model.mapper import map_child_doc, map_doc
from frappe.query_builder import DocType
from frappe.utils import cint, flt, get_time, getdate, nowdate, nowtime
from frappe.utils.background_jobs import enqueue, is_job_enqueued
from frappe.utils.scheduler import is_scheduler_inactive
from erpnext.accounts.doctype.pos_profile.pos_profile import required_accounting_dimensions
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_checks_for_pl_and_bs_accounts,
)
class POSInvoiceMergeLog(Document):
@@ -116,17 +119,18 @@ class POSInvoiceMergeLog(Document):
returns = [d for d in pos_invoice_docs if d.get("is_return") == 1]
sales = [d for d in pos_invoice_docs if d.get("is_return") == 0]
sales_invoice, credit_note = "", ""
sales_invoice, credit_notes = "", {}
sales_invoice_doc = None
if sales:
sales_invoice_doc = self.process_merging_into_sales_invoice(sales)
sales_invoice = sales_invoice_doc.name
if returns:
credit_note = self.process_merging_into_credit_note(returns, sales_invoice_doc)
distinguished_returns = self.distinguish_return_pos_invoices(returns, sales_invoice_doc)
credit_notes = self.process_merging_into_credit_notes(distinguished_returns)
self.save() # save consolidated_sales_invoice & consolidated_credit_note ref in merge log
self.update_pos_invoices(pos_invoice_docs, sales_invoice, credit_note)
self.update_pos_invoices(pos_invoice_docs, sales_invoice, credit_notes)
def on_cancel(self):
pos_invoice_docs = [frappe.get_cached_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices]
@@ -156,34 +160,50 @@ class POSInvoiceMergeLog(Document):
return sales_invoice
def process_merging_into_credit_note(self, data, sales_invoice_doc=None):
credit_note = self.get_new_sales_invoice()
credit_note.is_return = 1
def process_merging_into_credit_notes(self, data):
credit_notes = {}
for key, value in data.items():
if not value:
continue
credit_note = self.merge_pos_invoice_into(credit_note, data)
referenes = {}
credit_note = self.get_new_sales_invoice()
credit_note.is_return = 1
if sales_invoice_doc:
credit_note.return_against = sales_invoice_doc.name
credit_note = self.merge_pos_invoice_into(credit_note, value)
credit_note.return_against = key
for d in sales_invoice_doc.items:
referenes[d.item_code] = d.name
credit_note.is_consolidated = 1
credit_note.set_posting_time = 1
credit_note.posting_date = getdate(self.posting_date)
credit_note.posting_time = get_time(self.posting_time)
# TODO: return could be against multiple sales invoice which could also have been consolidated?
# credit_note.return_against = self.consolidated_invoice
credit_note.save()
credit_note.submit()
for d in credit_note.items:
d.sales_invoice_item = referenes.get(d.item_code)
self.consolidated_credit_note = credit_note.name
credit_notes[credit_note.name] = [d.name for d in value]
credit_note.is_consolidated = 1
credit_note.set_posting_time = 1
credit_note.posting_date = getdate(self.posting_date)
credit_note.posting_time = get_time(self.posting_time)
# TODO: return could be against multiple sales invoice which could also have been consolidated?
# credit_note.return_against = self.consolidated_invoice
credit_note.save()
credit_note.submit()
return credit_notes
self.consolidated_credit_note = credit_note.name
def distinguish_return_pos_invoices(self, data, sales_invoice_doc=None):
return_invoices = {}
return credit_note.name
return_invoices[sales_invoice_doc.name if sales_invoice_doc else None] = []
for doc in data:
sales_invoices_of_return_against = frappe.db.get_value(
"POS Invoice", doc.return_against, "consolidated_invoice"
)
if sales_invoices_of_return_against:
if sales_invoices_of_return_against in return_invoices:
return_invoices[sales_invoices_of_return_against].append(doc)
else:
return_invoices[sales_invoices_of_return_against] = [doc]
else:
return_invoices[sales_invoice_doc.name if sales_invoice_doc else None].append(doc)
return return_invoices
def merge_pos_invoice_into(self, invoice, data):
items, payments, taxes = [], [], []
@@ -209,33 +229,20 @@ class POSInvoiceMergeLog(Document):
loyalty_amount_sum += doc.loyalty_amount
for item in doc.get("items"):
found = False
for i in items:
if (
i.item_code == item.item_code
and not i.serial_and_batch_bundle
and not i.serial_no
and not i.batch_no
and i.uom == item.uom
and i.net_rate == item.net_rate
and i.warehouse == item.warehouse
):
found = True
i.qty = i.qty + item.qty
i.amount = i.amount + item.net_amount
i.net_amount = i.amount
i.base_amount = i.base_amount + item.base_net_amount
i.base_net_amount = i.base_amount
if not found:
item.rate = item.net_rate
item.amount = item.net_amount
item.base_amount = item.base_net_amount
item.price_list_rate = 0
si_item = map_child_doc(item, invoice, {"doctype": "Sales Invoice Item"})
if item.serial_and_batch_bundle:
si_item.serial_and_batch_bundle = item.serial_and_batch_bundle
items.append(si_item)
item.rate = item.net_rate
item.amount = item.net_amount
item.base_amount = item.base_net_amount
item.price_list_rate = 0
si_item = map_child_doc(item, invoice, {"doctype": "Sales Invoice Item"})
si_item.pos_invoice = doc.name
si_item.pos_invoice_item = item.name
if doc.is_return:
si_item.sales_invoice_item = get_sales_invoice_item(
doc.return_against, item.pos_invoice_item
)
if item.serial_and_batch_bundle:
si_item.serial_and_batch_bundle = item.serial_and_batch_bundle
items.append(si_item)
for tax in doc.get("taxes"):
found = False
@@ -292,22 +299,23 @@ class POSInvoiceMergeLog(Document):
invoice.disable_rounded_total = cint(
frappe.db.get_value("POS Profile", invoice.pos_profile, "disable_rounded_total")
)
accounting_dimensions = required_accounting_dimensions()
accounting_dimensions = get_checks_for_pl_and_bs_accounts()
accounting_dimensions_fields = [d.fieldname for d in accounting_dimensions]
dimension_values = frappe.db.get_value(
"POS Profile", {"name": invoice.pos_profile}, accounting_dimensions, as_dict=1
"POS Profile", {"name": invoice.pos_profile}, accounting_dimensions_fields, as_dict=1
)
for dimension in accounting_dimensions:
dimension_value = dimension_values.get(dimension)
dimension_value = dimension_values.get(dimension.fieldname)
if not dimension_value:
if not dimension_value and (dimension.mandatory_for_pl or dimension.mandatory_for_bs):
frappe.throw(
_("Please set Accounting Dimension {} in {}").format(
frappe.bold(frappe.unscrub(dimension)),
frappe.bold(dimension.label),
frappe.get_desk_link("POS Profile", invoice.pos_profile),
)
)
invoice.set(dimension, dimension_value)
invoice.set(dimension.fieldname, dimension_value)
if self.merge_invoices_based_on == "Customer Group":
invoice.flags.ignore_pos_profile = True
@@ -324,16 +332,16 @@ class POSInvoiceMergeLog(Document):
return sales_invoice
def update_pos_invoices(self, invoice_docs, sales_invoice="", credit_note=""):
def update_pos_invoices(self, invoice_docs, sales_invoice="", credit_notes=None):
for doc in invoice_docs:
doc.load_from_db()
doc.update(
{
"consolidated_invoice": None
if self.docstatus == 2
else (credit_note if doc.is_return else sales_invoice)
}
)
inv = sales_invoice
if doc.is_return:
for key, value in credit_notes.items():
if doc.name in value:
inv = key
break
doc.update({"consolidated_invoice": None if self.docstatus == 2 else inv})
doc.set_status(update=True)
doc.save()
@@ -625,3 +633,26 @@ def get_error_message(message) -> str:
return message["message"]
except Exception:
return str(message)
def get_sales_invoice_item(return_against_pos_invoice, pos_invoice_item):
try:
SalesInvoice = DocType("Sales Invoice")
SalesInvoiceItem = DocType("Sales Invoice Item")
query = (
frappe.qb.from_(SalesInvoice)
.from_(SalesInvoiceItem)
.select(SalesInvoiceItem.name)
.where(
(SalesInvoice.name == SalesInvoiceItem.parent)
& (SalesInvoice.is_return == 0)
& (SalesInvoiceItem.pos_invoice == return_against_pos_invoice)
& (SalesInvoiceItem.pos_invoice_item == pos_invoice_item)
)
)
result = query.run(as_dict=True)
return result[0].name if result else None
except Exception:
return None

View File

@@ -28,14 +28,17 @@ class TestPOSInvoiceMergeLog(unittest.TestCase):
pos_inv = create_pos_invoice(rate=300, do_not_submit=1)
pos_inv.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 300})
pos_inv.save()
pos_inv.submit()
pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1)
pos_inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3200})
pos_inv2.save()
pos_inv2.submit()
pos_inv3 = create_pos_invoice(customer="_Test Customer 2", rate=2300, do_not_submit=1)
pos_inv3.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 2300})
pos_inv3.save()
pos_inv3.submit()
consolidate_pos_invoices()
@@ -61,14 +64,17 @@ class TestPOSInvoiceMergeLog(unittest.TestCase):
pos_inv = create_pos_invoice(rate=300, do_not_submit=1)
pos_inv.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 300})
pos_inv.save()
pos_inv.submit()
pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1)
pos_inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3200})
pos_inv2.save()
pos_inv2.submit()
pos_inv3 = create_pos_invoice(customer="_Test Customer 2", rate=2300, do_not_submit=1)
pos_inv3.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 2300})
pos_inv3.save()
pos_inv3.submit()
pos_inv_cn = make_sales_return(pos_inv.name)
@@ -122,6 +128,8 @@ class TestPOSInvoiceMergeLog(unittest.TestCase):
},
)
inv.insert()
inv.payments[0].amount = inv.grand_total
inv.save()
inv.submit()
inv2 = create_pos_invoice(qty=1, rate=100, do_not_save=True)
@@ -138,6 +146,8 @@ class TestPOSInvoiceMergeLog(unittest.TestCase):
},
)
inv2.insert()
inv2.payments[0].amount = inv.grand_total
inv2.save()
inv2.submit()
consolidate_pos_invoices()
@@ -272,7 +282,7 @@ class TestPOSInvoiceMergeLog(unittest.TestCase):
inv2.submit()
inv3 = create_pos_invoice(qty=3, rate=600, do_not_save=True)
inv3.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 1000})
inv3.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 1800})
inv3.insert()
inv3.submit()
@@ -280,8 +290,8 @@ class TestPOSInvoiceMergeLog(unittest.TestCase):
inv.load_from_db()
consolidated_invoice = frappe.get_doc("Sales Invoice", inv.consolidated_invoice)
self.assertEqual(consolidated_invoice.outstanding_amount, 800)
self.assertNotEqual(consolidated_invoice.status, "Paid")
self.assertNotEqual(consolidated_invoice.outstanding_amount, 800)
self.assertEqual(consolidated_invoice.status, "Paid")
finally:
frappe.set_user("Administrator")
@@ -416,6 +426,7 @@ class TestPOSInvoiceMergeLog(unittest.TestCase):
do_not_submit=1,
)
pos_inv.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 100})
pos_inv.save()
pos_inv.submit()
pos_inv_cn = make_sales_return(pos_inv.name)
@@ -430,6 +441,7 @@ class TestPOSInvoiceMergeLog(unittest.TestCase):
do_not_submit=1,
)
pos_inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 100})
pos_inv2.save()
pos_inv2.submit()
consolidate_pos_invoices()

View File

@@ -29,6 +29,7 @@
"ignore_pricing_rule",
"allow_rate_change",
"allow_discount_change",
"disable_grand_total_to_default_mop",
"section_break_23",
"item_groups",
"column_break_25",
@@ -382,6 +383,12 @@
"fieldname": "print_receipt_on_order_complete",
"fieldtype": "Check",
"label": "Print Receipt on Order Complete"
},
{
"default": "0",
"fieldname": "disable_grand_total_to_default_mop",
"fieldtype": "Check",
"label": "Disable auto setting Grand Total to default Payment Mode"
}
],
"icon": "icon-cog",
@@ -409,7 +416,7 @@
"link_fieldname": "pos_profile"
}
],
"modified": "2025-01-01 11:07:03.161950",
"modified": "2025-01-29 13:12:30.796630",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Profile",

View File

@@ -4,9 +4,14 @@
import frappe
from frappe import _, msgprint, scrub, unscrub
from frappe.core.doctype.user_permission.user_permission import get_permitted_documents
from frappe.model.document import Document
from frappe.utils import get_link_to_form, now
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_checks_for_pl_and_bs_accounts,
)
class POSProfile(Document):
# begin: auto-generated types
@@ -36,6 +41,7 @@ class POSProfile(Document):
currency: DF.Link
customer: DF.Link | None
customer_groups: DF.Table[POSCustomerGroup]
disable_grand_total_to_default_mop: DF.Check
disable_rounded_total: DF.Check
disabled: DF.Check
expense_account: DF.Link | None
@@ -69,15 +75,19 @@ class POSProfile(Document):
self.validate_accounting_dimensions()
def validate_accounting_dimensions(self):
acc_dim_names = required_accounting_dimensions()
for acc_dim in acc_dim_names:
if not self.get(acc_dim):
acc_dims = get_checks_for_pl_and_bs_accounts()
for acc_dim in acc_dims:
if (
self.company == acc_dim.company
and not self.get(acc_dim.fieldname)
and (acc_dim.mandatory_for_pl or acc_dim.mandatory_for_bs)
):
frappe.throw(
_(
"{0} is a mandatory Accounting Dimension. <br>"
"Please set a value for {0} in Accounting Dimensions section."
).format(
unscrub(frappe.bold(acc_dim)),
frappe.bold(acc_dim.label),
),
title=_("Mandatory Accounting Dimension"),
)
@@ -195,17 +205,41 @@ class POSProfile(Document):
def get_item_groups(pos_profile):
item_groups = []
pos_profile = frappe.get_cached_doc("POS Profile", pos_profile)
permitted_item_groups = get_permitted_nodes("Item Group")
if pos_profile.get("item_groups"):
# Get items based on the item groups defined in the POS profile
for data in pos_profile.get("item_groups"):
item_groups.extend(
["%s" % frappe.db.escape(d.name) for d in get_child_nodes("Item Group", data.item_group)]
[
"%s" % frappe.db.escape(d.name)
for d in get_child_nodes("Item Group", data.item_group)
if not permitted_item_groups or d.name in permitted_item_groups
]
)
if not item_groups and permitted_item_groups:
item_groups = ["%s" % frappe.db.escape(d) for d in permitted_item_groups]
return list(set(item_groups))
def get_permitted_nodes(group_type):
nodes = []
permitted_nodes = get_permitted_documents(group_type)
if not permitted_nodes:
return nodes
for node in permitted_nodes:
if frappe.db.get_value(group_type, node, "is_group"):
nodes.extend([d.name for d in get_child_nodes(group_type, node)])
else:
nodes.append(node)
return nodes
def get_child_nodes(group_type, root):
lft, rgt = frappe.db.get_value(group_type, root, ["lft", "rgt"])
return frappe.db.sql(
@@ -215,23 +249,6 @@ def get_child_nodes(group_type, root):
)
def required_accounting_dimensions():
p = frappe.qb.DocType("Accounting Dimension")
c = frappe.qb.DocType("Accounting Dimension Detail")
acc_dim_doc = (
frappe.qb.from_(p)
.inner_join(c)
.on(p.name == c.parent)
.select(c.parent)
.where((c.mandatory_for_bs == 1) | (c.mandatory_for_pl == 1))
.where(p.disabled == 0)
).run(as_dict=1)
acc_dim_names = [scrub(d.parent) for d in acc_dim_doc]
return acc_dim_names
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def pos_profile_query(doctype, txt, searchfield, start, page_len, filters):

View File

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

View File

@@ -60,6 +60,7 @@ class PricingRule(Document):
disable: DF.Check
discount_amount: DF.Currency
discount_percentage: DF.Float
dont_enforce_free_item_qty: DF.Check
for_price_list: DF.Link | None
free_item: DF.Link | None
free_item_rate: DF.Currency
@@ -453,8 +454,7 @@ def get_pricing_rule_for_item(args, doc=None, for_validate=False):
if pricing_rule.coupon_code_based == 1:
if not args.coupon_code:
return item_details
continue
coupon_code = frappe.db.get_value(
doctype="Coupon Code", filters={"pricing_rule": pricing_rule.name}, fieldname="name"
)
@@ -645,7 +645,7 @@ def remove_pricing_rule_for_item(pricing_rules, item_details, item_code=None, ra
if pricing_rule.margin_type in ["Percentage", "Amount"]:
item_details.margin_rate_or_amount = 0.0
item_details.margin_type = None
elif pricing_rule.get("free_item"):
elif pricing_rule.get("free_item") and not pricing_rule.get("dont_enforce_free_item_qty"):
item_details.remove_free_item = (
item_code if pricing_rule.get("same_item") else pricing_rule.get("free_item")
)

View File

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

View File

@@ -713,7 +713,10 @@ def apply_pricing_rule_for_free_items(doc, pricing_rule_args):
args.pop((item.item_code, item.pricing_rules))
for free_item in args.values():
doc.append("items", free_item)
if doc.is_new() or not frappe.get_value(
"Pricing Rule", free_item["pricing_rules"], "dont_enforce_free_item_qty"
):
doc.append("items", free_item)
def get_pricing_rule_items(pr_doc, other_items=False) -> list:

View File

@@ -236,17 +236,21 @@ def get_ar_filters(doc, entry):
def get_html(doc, filters, entry, col, res, ageing):
base_template_path = "frappe/www/printview.html"
template_path = (
"erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html"
if doc.report == "General Ledger"
else "erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts_accounts_receivable.html"
)
template_path = "erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts_accounts_receivable.html"
if doc.report == "General Ledger":
template_path = (
"erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html"
)
process_soa_html = frappe.get_hooks("process_soa_html")
# fetching custom print format for Process Statement of Accounts
if process_soa_html and process_soa_html.get(doc.report):
template_path = process_soa_html[doc.report][-1]
if doc.letter_head:
from frappe.www.printview import get_letter_head
letter_head = get_letter_head(doc, 0)
html = frappe.render_template(
template_path,
{
@@ -262,7 +266,6 @@ def get_html(doc, filters, entry, col, res, ageing):
else None,
},
)
html = frappe.render_template(
base_template_path,
{"body": html, "css": get_print_style(), "title": "Statement For " + entry.customer},

View File

@@ -871,6 +871,7 @@ class PurchaseInvoice(BuyingController):
self.make_payment_gl_entries(gl_entries)
self.make_write_off_gl_entry(gl_entries)
self.make_gle_for_rounding_adjustment(gl_entries)
self.set_transaction_currency_and_rate_in_gl_map(gl_entries)
return gl_entries
def check_asset_cwip_enabled(self):
@@ -916,6 +917,7 @@ class PurchaseInvoice(BuyingController):
"credit_in_account_currency": base_grand_total
if self.party_account_currency == self.company_currency
else grand_total,
"credit_in_transaction_currency": grand_total,
"against_voucher": against_voucher,
"against_voucher_type": self.doctype,
"project": self.project,
@@ -951,7 +953,7 @@ class PurchaseInvoice(BuyingController):
valuation_tax_accounts = [
d.account_head
for d in self.get("taxes")
if d.category in ("Valuation", "Total and Valuation")
if d.category in ("Valuation", "Valuation and Total")
and flt(d.base_tax_amount_after_discount_amount)
]
@@ -967,7 +969,6 @@ class PurchaseInvoice(BuyingController):
for item in self.get("items"):
if flt(item.base_net_amount):
account_currency = get_account_currency(item.expense_account)
if item.item_code:
frappe.get_cached_value("Item", item.item_code, "asset_category")
@@ -976,6 +977,7 @@ class PurchaseInvoice(BuyingController):
and self.auto_accounting_for_stock
and (item.item_code in stock_items or item.is_fixed_asset)
):
account_currency = get_account_currency(item.expense_account)
# warehouse account
warehouse_debit_amount = self.make_stock_adjustment_entry(
gl_entries, item, voucher_wise_stock_value, account_currency
@@ -991,6 +993,7 @@ class PurchaseInvoice(BuyingController):
"project": item.project or self.project,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"debit": warehouse_debit_amount,
"debit_in_transaction_currency": item.net_amount,
},
warehouse_account[item.warehouse]["account_currency"],
item=item,
@@ -1011,6 +1014,7 @@ class PurchaseInvoice(BuyingController):
"project": item.project or self.project,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"debit": -1 * flt(credit_amount, item.precision("base_net_amount")),
"debit_in_transaction_currency": item.net_amount,
},
warehouse_account[item.from_warehouse]["account_currency"],
item=item,
@@ -1025,6 +1029,7 @@ class PurchaseInvoice(BuyingController):
"account": item.expense_account,
"against": self.supplier,
"debit": flt(item.base_net_amount, item.precision("base_net_amount")),
"debit_in_transaction_currency": item.net_amount,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"cost_center": item.cost_center,
"project": item.project,
@@ -1042,6 +1047,10 @@ class PurchaseInvoice(BuyingController):
"account": item.expense_account,
"against": self.supplier,
"debit": warehouse_debit_amount,
"debit_in_transaction_currency": flt(
warehouse_debit_amount / self.conversion_rate,
item.precision("net_amount"),
),
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"cost_center": item.cost_center,
"project": item.project or self.project,
@@ -1054,7 +1063,9 @@ class PurchaseInvoice(BuyingController):
# Amount added through landed-cost-voucher
if landed_cost_entries:
if (item.item_code, item.name) in landed_cost_entries:
for account, amount in landed_cost_entries[(item.item_code, item.name)].items():
for account, base_amount in landed_cost_entries[
(item.item_code, item.name)
].items():
gl_entries.append(
self.get_gl_dict(
{
@@ -1062,8 +1073,9 @@ class PurchaseInvoice(BuyingController):
"against": item.expense_account,
"cost_center": item.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"credit": flt(amount["base_amount"]),
"credit_in_account_currency": flt(amount["amount"]),
"credit": flt(base_amount["base_amount"]),
"credit_in_account_currency": flt(base_amount["amount"]),
"credit_in_transaction_currency": item.net_amount,
"project": item.project or self.project,
},
item=item,
@@ -1086,6 +1098,7 @@ class PurchaseInvoice(BuyingController):
"project": item.project or self.project,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"credit": flt(item.rm_supp_cost),
"credit_in_transaction_currency": item.net_amount,
},
warehouse_account[self.supplier_warehouse]["account_currency"],
item=item,
@@ -1099,7 +1112,8 @@ class PurchaseInvoice(BuyingController):
else item.deferred_expense_account
)
dummy, amount = self.get_amount_and_base_amount(item, None)
account_currency = get_account_currency(expense_account)
amount, base_amount = self.get_amount_and_base_amount(item, None)
if provisional_accounting_for_non_stock_items:
self.make_provisional_gl_entry(gl_entries, item)
@@ -1110,7 +1124,8 @@ class PurchaseInvoice(BuyingController):
{
"account": expense_account,
"against": self.supplier,
"debit": amount,
"debit": base_amount,
"debit_in_transaction_currency": amount,
"cost_center": item.cost_center,
"project": item.project or self.project,
},
@@ -1184,6 +1199,10 @@ class PurchaseInvoice(BuyingController):
"account": stock_rbnb,
"against": self.supplier,
"debit": flt(item.item_tax_amount, item.precision("item_tax_amount")),
"debit_in_transaction_currency": flt(
item.item_tax_amount / self.conversion_rate,
item.precision("item_tax_amount"),
),
"remarks": self.remarks or _("Accounting Entry for Stock"),
"cost_center": self.cost_center,
"project": item.project or self.project,
@@ -1299,6 +1318,7 @@ class PurchaseInvoice(BuyingController):
"account": cost_of_goods_sold_account,
"against": item.expense_account,
"debit": stock_adjustment_amt,
"debit_in_transaction_currency": item.net_amount,
"remarks": self.get("remarks") or _("Stock Adjustment"),
"cost_center": item.cost_center,
"project": item.project or self.project,
@@ -1332,6 +1352,7 @@ class PurchaseInvoice(BuyingController):
dr_or_cr + "_in_account_currency": base_amount
if account_currency == self.company_currency
else amount,
dr_or_cr + "_in_transaction_currency": amount,
"cost_center": tax.cost_center,
},
account_currency,
@@ -1378,6 +1399,10 @@ class PurchaseInvoice(BuyingController):
"cost_center": tax.cost_center,
"against": self.supplier,
"credit": applicable_amount,
"credit_in_transaction_currency": flt(
applicable_amount / self.conversion_rate,
frappe.get_precision("Purchase Invoice Item", "item_tax_amount"),
),
"remarks": self.remarks or _("Accounting Entry for Stock"),
},
item=tax,
@@ -1396,6 +1421,10 @@ class PurchaseInvoice(BuyingController):
"cost_center": tax.cost_center,
"against": self.supplier,
"credit": valuation_tax[tax.name],
"credit_in_transaction_currency": flt(
valuation_tax[tax.name] / self.conversion_rate,
frappe.get_precision("Purchase Invoice Item", "item_tax_amount"),
),
"remarks": self.remarks or _("Accounting Entry for Stock"),
},
item=tax,
@@ -1411,6 +1440,7 @@ class PurchaseInvoice(BuyingController):
"account": self.unrealized_profit_loss_account,
"against": self.supplier,
"credit": flt(self.total_taxes_and_charges),
"credit_in_transaction_currency": flt(self.total_taxes_and_charges),
"credit_in_account_currency": flt(self.base_total_taxes_and_charges),
"cost_center": self.cost_center,
},
@@ -1460,6 +1490,7 @@ class PurchaseInvoice(BuyingController):
"debit_in_account_currency": self.base_paid_amount
if self.party_account_currency == self.company_currency
else self.paid_amount,
"debit_in_transaction_currency": self.paid_amount,
"against_voucher": self.return_against
if cint(self.is_return) and self.return_against
else self.name,
@@ -1481,6 +1512,7 @@ class PurchaseInvoice(BuyingController):
"credit_in_account_currency": self.base_paid_amount
if bank_account_currency == self.company_currency
else self.paid_amount,
"credit_in_transaction_currency": self.paid_amount,
"cost_center": self.cost_center,
},
bank_account_currency,
@@ -1505,6 +1537,7 @@ class PurchaseInvoice(BuyingController):
"debit_in_account_currency": self.base_write_off_amount
if self.party_account_currency == self.company_currency
else self.write_off_amount,
"debit_in_transaction_currency": self.write_off_amount,
"against_voucher": self.return_against
if cint(self.is_return) and self.return_against
else self.name,
@@ -1525,6 +1558,7 @@ class PurchaseInvoice(BuyingController):
"credit_in_account_currency": self.base_write_off_amount
if write_off_account_currency == self.company_currency
else self.write_off_amount,
"credit_in_transaction_currency": self.write_off_amount,
"cost_center": self.cost_center or self.write_off_cost_center,
},
item=self,

View File

@@ -2482,6 +2482,76 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
item.reload()
self.assertEqual(item.last_purchase_rate, 0)
def test_adjust_incoming_rate_from_pi_with_multi_currency_and_partial_billing(self):
frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 0)
frappe.db.set_single_value("Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate", 1)
pr = make_purchase_receipt(
qty=10, rate=10, currency="USD", do_not_save=1, supplier="_Test Supplier USD"
)
pr.conversion_rate = 5300
pr.save()
pr.submit()
incoming_rate = frappe.db.get_value(
"Stock Ledger Entry",
{"voucher_type": "Purchase Receipt", "voucher_no": pr.name},
"incoming_rate",
)
self.assertEqual(incoming_rate, 53000) # Asserting to confirm if the default calculation is correct
pi = create_purchase_invoice_from_receipt(pr.name)
for row in pi.items:
row.qty = 1
pi.save()
pi.submit()
incoming_rate = frappe.db.get_value(
"Stock Ledger Entry",
{"voucher_type": "Purchase Receipt", "voucher_no": pr.name},
"incoming_rate",
)
# Test 1 : Incoming rate should not change as only the qty has changed and not the rate (this was not the case before)
self.assertEqual(incoming_rate, 53000)
pi = create_purchase_invoice_from_receipt(pr.name)
for row in pi.items:
row.qty = 1
row.rate = 9
pi.save()
pi.submit()
incoming_rate = frappe.db.get_value(
"Stock Ledger Entry",
{"voucher_type": "Purchase Receipt", "voucher_no": pr.name},
"incoming_rate",
)
# Test 2 : Rate in new PI is lower than PR, so incoming rate should also be lower
self.assertEqual(incoming_rate, 50350)
pi = create_purchase_invoice_from_receipt(pr.name)
for row in pi.items:
row.qty = 1
row.rate = 12
pi.save()
pi.submit()
incoming_rate = frappe.db.get_value(
"Stock Ledger Entry",
{"voucher_type": "Purchase Receipt", "voucher_no": pr.name},
"incoming_rate",
)
# Test 3 : Rate in new PI is higher than PR, so incoming rate should also be higher
self.assertEqual(incoming_rate, 54766.667)
frappe.db.set_single_value("Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate", 0)
frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 1)
def test_opening_invoice_rounding_adjustment_validation(self):
pi = make_purchase_invoice(do_not_save=1)
pi.items[0].rate = 99.98
@@ -2569,6 +2639,50 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
"Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice", original_value
)
def test_trx_currency_debit_credit_for_high_precision(self):
exc_rate = 0.737517516
pi = make_purchase_invoice(
currency="USD", conversion_rate=exc_rate, qty=1, rate=2000, do_not_save=True
)
pi.supplier = "_Test Supplier USD"
pi.save().submit()
expected = (
("_Test Account Cost for Goods Sold - _TC", 1475.04, 0.0, 2000.0, 0.0, "USD", exc_rate),
("_Test Payable USD - _TC", 0.0, 1475.04, 0.0, 2000.0, "USD", exc_rate),
)
actual = frappe.db.get_all(
"GL Entry",
filters={"voucher_no": pi.name},
fields=[
"account",
"debit",
"credit",
"debit_in_transaction_currency",
"credit_in_transaction_currency",
"transaction_currency",
"transaction_exchange_rate",
],
order_by="account",
as_list=1,
)
self.assertEqual(actual, expected)
def test_prevents_fully_returned_invoice_with_zero_quantity(self):
from erpnext.controllers.sales_and_purchase_return import StockOverReturnError, make_return_doc
invoice = make_purchase_invoice(qty=10)
return_doc = make_return_doc(invoice.doctype, invoice.name)
return_doc.items[0].qty = -10
return_doc.save().submit()
return_doc = make_return_doc(invoice.doctype, invoice.name)
return_doc.items[0].qty = 0
self.assertRaises(StockOverReturnError, return_doc.save)
def set_advance_flag(company, flag, default_account):
frappe.db.set_value(

View File

@@ -461,7 +461,8 @@
"depends_on": "eval:!doc.is_fixed_asset && doc.use_serial_batch_fields === 1 && parent.update_stock === 1",
"fieldname": "serial_no",
"fieldtype": "Text",
"label": "Serial No"
"label": "Serial No",
"no_copy": 1
},
{
"depends_on": "eval:!doc.is_fixed_asset && doc.use_serial_batch_fields === 1 && parent.update_stock === 1",
@@ -975,7 +976,7 @@
"idx": 1,
"istable": 1,
"links": [],
"modified": "2024-10-28 15:06:19.246141",
"modified": "2025-03-12 16:33:12.453290",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Item",
@@ -985,4 +986,4 @@
"sort_field": "modified",
"sort_order": "DESC",
"states": []
}
}

View File

@@ -897,8 +897,16 @@ frappe.ui.form.on("Sales Invoice", {
project: function (frm) {
if (frm.doc.project) {
frm.events.add_timesheet_data(frm, {
project: frm.doc.project,
frappe.call({
method: "is_auto_fetch_timesheet_enabled",
doc: frm.doc,
callback: function (r) {
if (cint(r.message)) {
frm.events.add_timesheet_data(frm, {
project: frm.doc.project,
});
}
},
});
}
},
@@ -914,9 +922,25 @@ frappe.ui.form.on("Sales Invoice", {
}
const timesheets = await frm.events.get_timesheet_data(frm, kwargs);
if (kwargs.item_code) {
frm.events.add_timesheet_item(frm, kwargs.item_code, timesheets);
}
return frm.events.set_timesheet_data(frm, timesheets);
},
add_timesheet_item: function (frm, item_code, timesheets) {
const row = frm.add_child("items");
frappe.model.set_value(row.doctype, row.name, "item_code", item_code);
frappe.model.set_value(
row.doctype,
row.name,
"qty",
timesheets.reduce((a, b) => a + (b["billing_hours"] || 0.0), 0.0)
);
},
async get_timesheet_data(frm, kwargs) {
return frappe
.call({
@@ -1014,6 +1038,22 @@ frappe.ui.form.on("Sales Invoice", {
fieldtype: "Date",
reqd: 1,
},
{
label: __("Item Code"),
fieldname: "item_code",
fieldtype: "Link",
options: "Item",
get_query: () => {
return {
query: "erpnext.controllers.queries.item_query",
filters: {
is_sales_item: 1,
customer: frm.doc.customer,
has_variants: 0,
},
};
},
},
{
fieldtype: "Column Break",
fieldname: "col_break_1",
@@ -1038,6 +1078,7 @@ frappe.ui.form.on("Sales Invoice", {
from_time: data.from_time,
to_time: data.to_time,
project: data.project,
item_code: data.item_code,
});
d.hide();
},

View File

@@ -3,6 +3,7 @@
"allow_import": 1,
"autoname": "naming_series:",
"creation": "2022-01-25 10:29:57.771398",
"default_print_format": "Sales Invoice Print",
"doctype": "DocType",
"engine": "InnoDB",
"field_order": [
@@ -299,7 +300,8 @@
"oldfieldname": "project_name",
"oldfieldtype": "Link",
"options": "Project",
"print_hide": 1
"print_hide": 1,
"search_index": 1
},
{
"default": "0",
@@ -2176,6 +2178,7 @@
"print_hide": 1
}
],
"grid_page_length": 50,
"icon": "fa fa-file-text",
"idx": 181,
"is_submittable": 1,
@@ -2186,7 +2189,7 @@
"link_fieldname": "consolidated_invoice"
}
],
"modified": "2025-01-14 11:38:30.446370",
"modified": "2025-03-05 17:06:59.720616",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",
@@ -2232,6 +2235,7 @@
}
],
"quick_entry": 1,
"row_format": "Dynamic",
"search_fields": "posting_date, due_date, customer, base_grand_total, outstanding_amount",
"show_name_in_global_search": 1,
"sort_field": "modified",

View File

@@ -460,6 +460,8 @@ class SalesInvoice(SellingController):
self.make_bundle_for_sales_purchase_return(table_name)
self.make_bundle_using_old_serial_batch_fields(table_name)
self.update_stock_reservation_entries()
self.update_stock_ledger()
# this sequence because outstanding may get -ve
@@ -561,6 +563,7 @@ class SalesInvoice(SellingController):
self.make_gl_entries_on_cancel()
if self.update_stock == 1:
self.update_stock_reservation_entries()
self.repost_future_sle_and_gle()
self.db_set("status", "Cancelled")
@@ -675,7 +678,13 @@ class SalesInvoice(SellingController):
"Account", self.debit_to, "account_currency", cache=True
)
if not self.due_date and self.customer:
self.due_date = get_due_date(self.posting_date, "Customer", self.customer, self.company)
self.due_date = get_due_date(
self.posting_date,
"Customer",
self.customer,
self.company,
template_name=self.payment_terms_template,
)
super().set_missing_values(for_validate)
@@ -1087,11 +1096,15 @@ class SalesInvoice(SellingController):
timesheet.billing_amount = ts_doc.total_billable_amount
def update_timesheet_billing_for_project(self):
if not self.timesheets and self.project:
if not self.timesheets and self.project and self.is_auto_fetch_timesheet_enabled():
self.add_timesheet_data()
else:
self.calculate_billing_amount_for_timesheet()
@frappe.whitelist()
def is_auto_fetch_timesheet_enabled(self):
return frappe.db.get_single_value("Projects Settings", "fetch_timesheet_in_sales_invoice")
@frappe.whitelist()
def add_timesheet_data(self):
self.set("timesheets", [])
@@ -1234,6 +1247,7 @@ class SalesInvoice(SellingController):
self.make_write_off_gl_entry(gl_entries)
self.make_gle_for_rounding_adjustment(gl_entries)
self.set_transaction_currency_and_rate_in_gl_map(gl_entries)
return gl_entries
def make_customer_gl_entry(self, gl_entries):
@@ -1267,6 +1281,7 @@ class SalesInvoice(SellingController):
"debit_in_account_currency": base_grand_total
if self.party_account_currency == self.company_currency
else grand_total,
"debit_in_transaction_currency": grand_total,
"against_voucher": against_voucher,
"against_voucher_type": self.doctype,
"cost_center": self.cost_center,
@@ -1298,6 +1313,9 @@ class SalesInvoice(SellingController):
if account_currency == self.company_currency
else flt(amount, tax.precision("tax_amount_after_discount_amount"))
),
"credit_in_transaction_currency": flt(
amount, tax.precision("tax_amount_after_discount_amount")
),
"cost_center": tax.cost_center,
},
account_currency,
@@ -1315,6 +1333,7 @@ class SalesInvoice(SellingController):
"against": self.customer,
"debit": flt(self.total_taxes_and_charges),
"debit_in_account_currency": flt(self.base_total_taxes_and_charges),
"debit_in_transaction_currency": flt(self.total_taxes_and_charges),
"cost_center": self.cost_center,
},
account_currency,
@@ -1413,6 +1432,7 @@ class SalesInvoice(SellingController):
if account_currency == self.company_currency
else flt(amount, item.precision("net_amount"))
),
"credit_in_transaction_currency": flt(amount, item.precision("net_amount")),
"cost_center": item.cost_center,
"project": item.project or self.project,
},
@@ -1464,6 +1484,7 @@ class SalesInvoice(SellingController):
+ cstr(self.loyalty_redemption_account)
+ " for the Loyalty Program",
"credit": self.loyalty_amount,
"credit_in_transaction_currency": self.loyalty_amount,
"against_voucher": self.return_against if cint(self.is_return) else self.name,
"against_voucher_type": self.doctype,
"cost_center": self.cost_center,
@@ -1478,6 +1499,7 @@ class SalesInvoice(SellingController):
"cost_center": self.cost_center or self.loyalty_redemption_cost_center,
"against": self.customer,
"debit": self.loyalty_amount,
"debit_in_transaction_currency": self.loyalty_amount,
"remark": "Loyalty Points redeemed by the customer",
},
item=self,
@@ -1511,6 +1533,7 @@ class SalesInvoice(SellingController):
"credit_in_account_currency": payment_mode.base_amount
if self.party_account_currency == self.company_currency
else payment_mode.amount,
"credit_in_transaction_currency": payment_mode.amount,
"against_voucher": against_voucher,
"against_voucher_type": self.doctype,
"cost_center": self.cost_center,
@@ -1530,6 +1553,7 @@ class SalesInvoice(SellingController):
"debit_in_account_currency": payment_mode.base_amount
if payment_mode_account_currency == self.company_currency
else payment_mode.amount,
"debit_in_transaction_currency": payment_mode.amount,
"cost_center": self.cost_center,
},
payment_mode_account_currency,
@@ -1554,6 +1578,7 @@ class SalesInvoice(SellingController):
"debit_in_account_currency": flt(self.base_change_amount)
if self.party_account_currency == self.company_currency
else flt(self.change_amount),
"debit_in_transaction_currency": flt(self.change_amount),
"against_voucher": self.return_against
if cint(self.is_return) and self.return_against
else self.name,
@@ -1572,6 +1597,7 @@ class SalesInvoice(SellingController):
"account": self.account_for_change_amount,
"against": self.customer,
"credit": self.base_change_amount,
"credit_in_transaction_currency": self.change_amount,
"cost_center": self.cost_center,
},
item=self,
@@ -1603,6 +1629,9 @@ class SalesInvoice(SellingController):
if self.party_account_currency == self.company_currency
else flt(self.write_off_amount, self.precision("write_off_amount"))
),
"credit_in_transaction_currency": flt(
self.write_off_amount, self.precision("write_off_amount")
),
"against_voucher": self.return_against if cint(self.is_return) else self.name,
"against_voucher_type": self.doctype,
"cost_center": self.cost_center,
@@ -1623,6 +1652,9 @@ class SalesInvoice(SellingController):
if write_off_account_currency == self.company_currency
else flt(self.write_off_amount, self.precision("write_off_amount"))
),
"debit_in_transaction_currency": flt(
self.write_off_amount, self.precision("write_off_amount")
),
"cost_center": self.cost_center or self.write_off_cost_center or default_cost_center,
},
write_off_account_currency,
@@ -1667,6 +1699,9 @@ class SalesInvoice(SellingController):
"credit_in_account_currency": flt(
self.rounding_adjustment, self.precision("rounding_adjustment")
),
"credit_in_transaction_currency": flt(
self.rounding_adjustment, self.precision("rounding_adjustment")
),
"credit": flt(
self.base_rounding_adjustment, self.precision("base_rounding_adjustment")
),
@@ -1930,13 +1965,16 @@ def is_overdue(doc, total):
"base_payment_amount" if doc.party_account_currency != doc.currency else "payment_amount"
)
payable_amount = sum(
payment.get(payment_amount_field)
for payment in doc.payment_schedule
if getdate(payment.due_date) < today
payable_amount = flt(
sum(
payment.get(payment_amount_field)
for payment in doc.payment_schedule
if getdate(payment.due_date) < today
),
doc.precision("outstanding_amount"),
)
return (total - outstanding_amount) < payable_amount
return flt(total - outstanding_amount, doc.precision("outstanding_amount")) < payable_amount
def get_discounting_status(sales_invoice):

View File

@@ -4246,6 +4246,31 @@ class TestSalesInvoice(FrappeTestCase):
doc = frappe.get_doc("Project", project.name)
self.assertEqual(doc.total_billed_amount, si.grand_total)
def test_total_billed_amount_with_different_projects(self):
# This test case is for checking the scenario where project is set at document level and for **some** child items only, not all
from copy import copy
si = create_sales_invoice(do_not_submit=True)
project = frappe.new_doc("Project")
project.company = "_Test Company"
project.project_name = "Test Total Billed Amount"
project.save()
si.project = project.name
si.items.append(copy(si.items[0]))
si.items.append(copy(si.items[0]))
si.items[0].project = project.name
si.items[1].project = project.name
# Not setting project on last item
si.items[1].insert()
si.items[2].insert()
si.submit()
project.reload()
self.assertIsNone(si.items[2].project)
self.assertEqual(project.total_billed_amount, 300)
def test_pos_returns_with_party_account_currency(self):
from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_sales_return
@@ -4270,6 +4295,20 @@ class TestSalesInvoice(FrappeTestCase):
pos_return = make_sales_return(pos.name)
self.assertEqual(abs(pos_return.payments[0].amount), pos.payments[0].amount)
def test_prevents_fully_returned_invoice_with_zero_quantity(self):
from erpnext.controllers.sales_and_purchase_return import StockOverReturnError, make_return_doc
invoice = create_sales_invoice(qty=10)
return_doc = make_return_doc(invoice.doctype, invoice.name)
return_doc.items[0].qty = -10
return_doc.save().submit()
return_doc = make_return_doc(invoice.doctype, invoice.name)
return_doc.items[0].qty = 0
self.assertRaises(StockOverReturnError, return_doc.save)
def set_advance_flag(company, flag, default_account):
frappe.db.set_value(

View File

@@ -105,6 +105,9 @@
"delivery_note",
"dn_detail",
"delivered_qty",
"column_break_vwhb",
"pos_invoice",
"pos_invoice_item",
"internal_transfer_section",
"purchase_order",
"column_break_92",
@@ -630,6 +633,7 @@
"fieldname": "serial_no",
"fieldtype": "Text",
"label": "Serial No",
"no_copy": 1,
"oldfieldname": "serial_no",
"oldfieldtype": "Small Text"
},
@@ -945,19 +949,41 @@
"no_copy": 1,
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "pos_invoice_item",
"fieldtype": "Data",
"ignore_user_permissions": 1,
"label": "POS Invoice Item",
"no_copy": 1,
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "column_break_vwhb",
"fieldtype": "Column Break"
},
{
"fieldname": "pos_invoice",
"fieldtype": "Link",
"label": "POS Invoice",
"no_copy": 1,
"options": "POS Invoice",
"print_hide": 1,
"search_index": 1
}
],
"idx": 1,
"istable": 1,
"links": [],
"modified": "2024-11-25 16:27:33.287341",
"modified": "2025-03-12 16:33:52.503777",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Item",
"naming_rule": "Random",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_field": "creation",
"sort_order": "DESC",
"states": []
}
}

View File

@@ -64,6 +64,8 @@ class SalesInvoiceItem(Document):
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
pos_invoice: DF.Link | None
pos_invoice_item: DF.Data | None
price_list_rate: DF.Currency
pricing_rules: DF.SmallText | None
project: DF.Link | None

View File

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

View File

@@ -18,8 +18,8 @@ class TaxWithheldVouchers(Document):
parentfield: DF.Data
parenttype: DF.Data
taxable_amount: DF.Currency
voucher_name: DF.DynamicLink | None
voucher_type: DF.Link | None
voucher_name: DF.Data | None
voucher_type: DF.Data | None
# end: auto-generated types
pass

View File

@@ -10,6 +10,7 @@ frappe.ui.form.on("Tax Withholding Category", {
filters: {
company: child.company,
root_type: ["in", ["Asset", "Liability"]],
is_group: 0,
},
};
}

View File

@@ -36,27 +36,38 @@ class TaxWithholdingCategory(Document):
def validate(self):
self.validate_dates()
self.validate_accounts()
self.validate_companies_and_accounts()
self.validate_thresholds()
def validate_dates(self):
last_date = None
for d in self.get("rates"):
last_to_date = None
rates = sorted(self.get("rates"), key=lambda d: getdate(d.from_date))
for d in rates:
if getdate(d.from_date) >= getdate(d.to_date):
frappe.throw(_("Row #{0}: From Date cannot be before To Date").format(d.idx))
# validate overlapping of dates
if last_date and getdate(d.to_date) < getdate(last_date):
if last_to_date and getdate(d.from_date) < getdate(last_to_date):
frappe.throw(_("Row #{0}: Dates overlapping with other row").format(d.idx))
def validate_accounts(self):
existing_accounts = []
last_to_date = d.to_date
def validate_companies_and_accounts(self):
existing_accounts = set()
companies = set()
for d in self.get("accounts"):
# validate duplicate company
if d.get("company") in companies:
frappe.throw(_("Company {0} added multiple times").format(frappe.bold(d.get("company"))))
companies.add(d.get("company"))
# validate duplicate account
if d.get("account") in existing_accounts:
frappe.throw(_("Account {0} added multiple times").format(frappe.bold(d.get("account"))))
validate_account_head(d.idx, d.get("account"), d.get("company"))
existing_accounts.append(d.get("account"))
existing_accounts.add(d.get("account"))
def validate_thresholds(self):
for d in self.get("rates"):
@@ -270,7 +281,10 @@ def get_lower_deduction_certificate(company, posting_date, tax_details, pan_no):
def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=None):
vouchers, voucher_wise_amount = get_invoice_vouchers(
parties, tax_details, inv.company, party_type=party_type
parties,
tax_details,
inv.company,
party_type=party_type,
)
payment_entry_vouchers = get_payment_entry_vouchers(
@@ -360,11 +374,23 @@ def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"):
voucher_wise_amount = []
vouchers = []
ldcs = frappe.db.get_all(
"Lower Deduction Certificate",
filters={
"valid_from": [">=", tax_details.from_date],
"valid_upto": ["<=", tax_details.to_date],
"company": company,
"supplier": ["in", parties],
},
fields=["supplier", "valid_from", "valid_upto", "rate"],
)
doctype = "Purchase Invoice" if party_type == "Supplier" else "Sales Invoice"
field = [
"base_tax_withholding_net_total as base_net_total" if party_type == "Supplier" else "base_net_total",
"name",
"grand_total",
"posting_date",
]
filters = {
@@ -383,18 +409,23 @@ def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"):
invoices_details = frappe.get_all(doctype, filters=filters, fields=field)
for d in invoices_details:
vouchers.append(d.name)
voucher_wise_amount.append(
frappe._dict(
{
"voucher_name": d.name,
"voucher_type": doctype,
"taxable_amount": d.base_net_total,
"grand_total": d.grand_total,
}
)
d = frappe._dict(
{
"voucher_name": d.name,
"voucher_type": doctype,
"taxable_amount": d.base_net_total,
"grand_total": d.grand_total,
"posting_date": d.posting_date,
}
)
if ldc := [x for x in ldcs if d.posting_date >= x.valid_from and d.posting_date <= x.valid_upto]:
if ldc[0].supplier in parties and ldc[0].rate == 0:
d.update({"taxable_amount": 0})
vouchers.append(d.voucher_name)
voucher_wise_amount.append(d)
journal_entries_details = frappe.db.sql(
"""
SELECT j.name, ja.credit - ja.debit AS amount, ja.reference_type
@@ -416,6 +447,7 @@ def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"):
tax_details.get("tax_withholding_category"),
company,
),
as_dict=1,
)
for d in journal_entries_details:

View File

@@ -7,7 +7,7 @@ import unittest
import frappe
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import add_days, today
from frappe.utils import add_days, add_months, today
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
from erpnext.accounts.utils import get_fiscal_year
@@ -519,7 +519,7 @@ class TestTaxWithholdingCategory(FrappeTestCase):
payment = get_payment_entry(order.doctype, order.name)
payment.apply_tax_withholding_amount = 1
payment.tax_withholding_category = "Cumulative Threshold TDS"
payment.submit()
payment.save().submit()
self.assertEqual(payment.taxes[0].tax_amount, 4000)
def test_multi_category_single_supplier(self):
@@ -666,6 +666,49 @@ class TestTaxWithholdingCategory(FrappeTestCase):
pi2.cancel()
pi3.cancel()
def test_ldc_at_0_rate(self):
frappe.db.set_value(
"Supplier",
"Test LDC Supplier",
{
"tax_withholding_category": "Test Service Category",
"pan": "ABCTY1234D",
},
)
fiscal_year = get_fiscal_year(today(), company="_Test Company")
valid_from = fiscal_year[1]
valid_upto = add_months(valid_from, 1)
create_lower_deduction_certificate(
supplier="Test LDC Supplier",
certificate_no="1AE0423AAJ",
tax_withholding_category="Test Service Category",
tax_rate=0,
limit=50000,
valid_from=valid_from,
valid_upto=valid_upto,
)
pi1 = create_purchase_invoice(
supplier="Test LDC Supplier", rate=35000, posting_date=valid_from, set_posting_time=True
)
pi1.submit()
self.assertEqual(pi1.taxes, [])
pi2 = create_purchase_invoice(
supplier="Test LDC Supplier",
rate=35000,
posting_date=add_days(valid_upto, 1),
set_posting_time=True,
)
pi2.submit()
self.assertEqual(len(pi2.taxes), 1)
# pi1 net total shouldn't be included as it lies within LDC at rate of '0'
self.assertEqual(pi2.taxes[0].tax_amount, 3500)
pi1.cancel()
pi2.cancel()
def set_previous_fy_and_tax_category(self):
test_company = "_Test Company"
category = "Cumulative Threshold TDS"
@@ -823,7 +866,8 @@ def create_purchase_invoice(**args):
pi = frappe.get_doc(
{
"doctype": "Purchase Invoice",
"posting_date": today(),
"set_posting_time": args.set_posting_time or False,
"posting_date": args.posting_date or today(),
"apply_tds": 0 if args.do_not_apply_tds else 1,
"supplier": args.supplier,
"company": "_Test Company",
@@ -1161,7 +1205,9 @@ def create_tax_withholding_category(
).insert()
def create_lower_deduction_certificate(supplier, tax_withholding_category, tax_rate, certificate_no, limit):
def create_lower_deduction_certificate(
supplier, tax_withholding_category, tax_rate, certificate_no, limit, valid_from=None, valid_upto=None
):
fiscal_year = get_fiscal_year(today(), company="_Test Company")
if not frappe.db.exists("Lower Deduction Certificate", certificate_no):
frappe.get_doc(
@@ -1172,8 +1218,8 @@ def create_lower_deduction_certificate(supplier, tax_withholding_category, tax_r
"certificate_no": certificate_no,
"tax_withholding_category": tax_withholding_category,
"fiscal_year": fiscal_year[0],
"valid_from": fiscal_year[1],
"valid_upto": fiscal_year[2],
"valid_from": valid_from or fiscal_year[1],
"valid_upto": valid_upto or fiscal_year[2],
"rate": tax_rate,
"certificate_limit": limit,
}

View File

@@ -430,7 +430,7 @@ def process_debit_credit_difference(gl_map):
voucher_no = gl_map[0].voucher_no
allowance = get_debit_credit_allowance(voucher_type, precision)
debit_credit_diff = get_debit_credit_difference(gl_map, precision)
debit_credit_diff, trx_cur_debit_credit_diff = get_debit_credit_difference(gl_map, precision)
if abs(debit_credit_diff) > allowance:
if not (
@@ -441,9 +441,9 @@ def process_debit_credit_difference(gl_map):
raise_debit_credit_not_equal_error(debit_credit_diff, voucher_type, voucher_no)
elif abs(debit_credit_diff) >= (1.0 / (10**precision)):
make_round_off_gle(gl_map, debit_credit_diff, precision)
make_round_off_gle(gl_map, debit_credit_diff, trx_cur_debit_credit_diff, precision)
debit_credit_diff = get_debit_credit_difference(gl_map, precision)
debit_credit_diff, trx_cur_debit_credit_diff = get_debit_credit_difference(gl_map, precision)
if abs(debit_credit_diff) > allowance:
if not (
voucher_type == "Journal Entry"
@@ -455,14 +455,23 @@ def process_debit_credit_difference(gl_map):
def get_debit_credit_difference(gl_map, precision):
debit_credit_diff = 0.0
trx_cur_debit_credit_diff = 0
for entry in gl_map:
entry.debit = flt(entry.debit, precision)
entry.credit = flt(entry.credit, precision)
debit_credit_diff += entry.debit - entry.credit
debit_credit_diff = flt(debit_credit_diff, precision)
entry.debit_in_transaction_currency = flt(entry.debit_in_transaction_currency, precision)
entry.credit_in_transaction_currency = flt(entry.credit_in_transaction_currency, precision)
trx_cur_debit_credit_diff += (
entry.debit_in_transaction_currency - entry.credit_in_transaction_currency
)
return debit_credit_diff
debit_credit_diff = flt(debit_credit_diff, precision)
trx_cur_debit_credit_diff = flt(trx_cur_debit_credit_diff, precision)
return debit_credit_diff, trx_cur_debit_credit_diff
def get_debit_credit_allowance(voucher_type, precision):
@@ -489,7 +498,7 @@ def has_opening_entries(gl_map: list) -> bool:
return False
def make_round_off_gle(gl_map, debit_credit_diff, precision):
def make_round_off_gle(gl_map, debit_credit_diff, trx_cur_debit_credit_diff, precision):
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
)
@@ -534,6 +543,12 @@ def make_round_off_gle(gl_map, debit_credit_diff, precision):
"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,
"credit": debit_credit_diff if debit_credit_diff > 0 else 0,
"debit_in_transaction_currency": abs(trx_cur_debit_credit_diff)
if trx_cur_debit_credit_diff < 0
else 0,
"credit_in_transaction_currency": trx_cur_debit_credit_diff
if trx_cur_debit_credit_diff > 0
else 0,
"cost_center": round_off_cost_center,
"party_type": None,
"party": None,
@@ -680,11 +695,15 @@ def make_reverse_gl_entries(
debit_in_account_currency = new_gle.get("debit_in_account_currency", 0)
credit_in_account_currency = new_gle.get("credit_in_account_currency", 0)
debit_in_transaction_currency = new_gle.get("debit_in_transaction_currency", 0)
credit_in_transaction_currency = new_gle.get("credit_in_transaction_currency", 0)
new_gle["debit"] = credit
new_gle["credit"] = debit
new_gle["debit_in_account_currency"] = credit_in_account_currency
new_gle["credit_in_account_currency"] = debit_in_account_currency
new_gle["debit_in_transaction_currency"] = credit_in_transaction_currency
new_gle["credit_in_transaction_currency"] = debit_in_transaction_currency
new_gle["remarks"] = "On cancellation of " + new_gle["voucher_no"]
new_gle["is_cancelled"] = 1

View File

@@ -572,12 +572,13 @@ def validate_party_accounts(doc):
@frappe.whitelist()
def get_due_date(posting_date, party_type, party, company=None, bill_date=None):
def get_due_date(posting_date, party_type, party, company=None, bill_date=None, template_name=None):
"""Get due date from `Payment Terms Template`"""
due_date = None
if (bill_date or posting_date) and party:
due_date = bill_date or posting_date
template_name = get_payment_terms_template(party, party_type, company)
if not template_name:
template_name = get_payment_terms_template(party, party_type, company)
if template_name:
due_date = get_due_date_from_template(template_name, posting_date, bill_date).strftime("%Y-%m-%d")
@@ -765,7 +766,7 @@ def validate_account_party_type(self):
if self.party_type and self.party:
account_type = frappe.get_cached_value("Account", self.account, "account_type")
if account_type and (account_type not in ["Receivable", "Payable"]):
if account_type and (account_type not in ["Receivable", "Payable", "Equity"]):
frappe.throw(
_(
"Party Type and Party can only be set for Receivable / Payable account<br><br>" "{0}"

View File

@@ -44,7 +44,7 @@
{% endfor %}
<tr>
<td class="right" colspan="3" ><strong>Total (debit) </strong></td>
<td class="left" >{{ gl | sum(attribute='debit') }}</td>
<td class="left" >{{ gl | sum(attribute='debit') | round(2) }}</td>
</tr>
<tr>
<td class="top-bottom" colspan="5"><strong>Credit</strong></td>
@@ -61,7 +61,7 @@
{% endfor %}
<tr>
<td class="right" colspan="3"><strong>Total (credit) </strong></td>
<td class="left" >{{ gl | sum(attribute='credit') }}</td>
<td class="left" >{{ gl | sum(attribute='credit') | round(2) }}</td>
</tr>
<tr>
<td class="top-bottom" colspan="5"><b>Narration: </b>{{ gl[0].remarks }}</td>

View File

@@ -0,0 +1,161 @@
{%- macro add_header(page_num, max_pages, doc, letter_head, no_letterhead, footer, print_settings=None, print_heading_template=None) -%}
{% if letter_head and not no_letterhead %}
<div class="letter-head">{{ letter_head }}</div>
{% endif %}
{% if print_heading_template %}
{{ frappe.render_template(print_heading_template, {"doc":doc}) }}
{% else %}
{% endif %}
{%- if doc.meta.is_submittable and doc.docstatus==2-%}
<div class="text-center" document-status="cancelled">
<h4 style="margin: 0px;">{{ _("CANCELLED") }}</h4>
</div>
{%- endif -%}
{%- endmacro -%}
{% for page in layout %}
<div class="page-break">
<div {% if print_settings.repeat_header_footer %} id="header-html" class="hidden-pdf" {% endif %}>
{{ add_header(loop.index, layout|len, doc, letter_head, no_letterhead, footer, print_settings) }}
</div>
<style>
.taxes-section .order-taxes.mt-5{
margin-top: 0px !important;
}
.taxes-section .order-taxes .border-btm.pb-5{
padding-bottom: 0px !important;
}
.print-format label{
color: #74808b;
font-size: 12px;
margin-bottom: 4px;
}
</style>
{% if print_settings.repeat_header_footer %}
<div id="footer-html" class="visible-pdf">
{% if not no_letterhead and footer %}
<div class="letter-head-footer">
{{ footer }}
</div>
{% endif %}
<p class="text-center small page-number visible-pdf">
{{ _("Page {0} of {1}").format('<span class="page"></span>', '<span class="topage"></span>') }}
</p>
</div>
{% endif %}
<div class="row section-break" style="margin-bottom: 10px;">
<div class="col-xs-6 p-0">
<div class="col-xs-12 value text-uppercase"><b>{{ doc.customer }}</b></div>
<div class="col-xs-12">
{{ doc.address_display }}
</div>
<div class="col-xs-12">
{{ _("Contact: ")+doc.contact_display if doc.contact_display else '' }}
</div>
<div class="col-xs-12">
{{ _("Mobile: ")+doc.contact_mobile if doc.contact_mobile else '' }}
</div>
</div>
<div class="col-xs-3"></div>
<div class="col-xs-3" style="padding-left: 5px;">
<div>
<div><label>{{ _("Invoice ID") }}</label></div>
<div>{{ doc.name }}</div>
</div>
<div style="margin-top: 20px;">
<div><label>{{ _("Invoice Date") }}</label></div>
<div>{{ frappe.utils.format_date(doc.posting_date) }}</div>
</div>
<div style="margin-top: 20px;">
<div><label>{{ _("Due Date") }}</label></div>
<div>{{ frappe.utils.format_date(doc.due_date) }}</div>
</div>
</div>
</div>
<div class="section-break">
<table class="table table-bordered table-condensed mb-0" style="width: 100%; border-collapse: collapse; font-size: 12px;">
<colgroup>
<col style="width: 5%">
<col style="width: 45%">
<col style="width: 10%">
<col style="width: 20%">
<col style="width: 20%">
</colgroup>
<thead>
<tr>
<th class="text-uppercase" style="text-align:center">{{ _("Sr") }}</th>
<th class="text-uppercase" style="text-align:center">{{ _("Details") }}</th>
<th class="text-uppercase" style="text-align:center">{{ _("Qty") }}</th>
<th class="text-uppercase" style="text-align:right">{{ _("Rate") }}</th>
<th class="text-uppercase" style="text-align:right">{{ _("Amount") }}</th>
</tr>
</thead>
{% for item in doc.items %}
<tr>
<td style="text-align:center">{{ loop.index }}</td>
<td>
<b>{{ item.item_code }}: {{ item.item_name }}</b>
{% if (item.description != item.item_name) %}
<br>{{ item.description }}
{% endif %}
</td>
<td style="text-align: center;">
{{ item.get_formatted("qty", 0) }}
{{ item.get_formatted("uom", 0) }}
</td>
<td style="text-align: right;">{{ item.get_formatted("net_rate", doc) }}</td>
<td style="text-align: right;">{{ item.get_formatted("net_amount", doc) }}</td>
</tr>
{% endfor %}
</table>
<!-- total -->
<div class="row">
<div class="col-xs-6">
<div>
<label>{{ _("Amount in Words") }}</label>
{{ doc.in_words }}
</div>
<div style="margin-top: 20px;">
<label>{{ _("Payment Status") }}</label>
{{ doc.status }}
</div>
</div>
<div class="col-xs-6">
<div class="row section-break">
<div class="col-xs-7"><div>{{ _("Sub Total") }}</div></div>
<div class="col-xs-5" style="text-align: right;">{{ doc.get_formatted("net_total", doc) }}</div>
</div>
<div>
{% for d in doc.taxes %}
{% if d.tax_amount %}
<div class="row">
<div class="col-xs-8"><div>{{ _(d.description) }}</div></div>
<div class="col-xs-4" style="text-align: right;">{{ d.get_formatted("tax_amount") }}</div>
</div>
{% endif %}
{% endfor %}
</div>
<div class="row">
<div class="col-xs-7"><div>{{ _("Total") }}</div></div>
<div class="col-xs-5" style="text-align: right;">{{ doc.get_formatted("grand_total", doc) }}</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="row important data-field">
<div class="col-xs-12"><label>{{ _("Terms and Conditions") }}: </label></div>
<div class="col-xs-12">{{ doc.terms if doc.terms else '' }}</div>
</div>
</div>
</div>
</div>
{% endfor %}

View File

@@ -0,0 +1,32 @@
{
"absolute_value": 0,
"align_labels_right": 0,
"creation": "2025-01-22 16:23:51.012200",
"css": "",
"custom_format": 0,
"default_print_language": "en",
"disabled": 0,
"doc_type": "Sales Invoice",
"docstatus": 0,
"doctype": "Print Format",
"font": "",
"font_size": 14,
"idx": 0,
"line_breaks": 0,
"margin_bottom": 0.0,
"margin_left": 0.0,
"margin_right": 0.0,
"margin_top": 0.0,
"modified": "2025-01-22 16:23:51.012200",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Print",
"owner": "Administrator",
"page_number": "Hide",
"print_format_builder": 0,
"print_format_builder_beta": 0,
"print_format_type": "Jinja",
"raw_printing": 0,
"show_section_headings": 0,
"standard": "Yes"
}

View File

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

View File

@@ -38,6 +38,23 @@ class TestAccountsPayable(AccountsTestMixin, FrappeTestCase):
self.assertEqual(data[1][0].get("outstanding"), 300)
self.assertEqual(data[1][0].get("currency"), "USD")
def test_account_payable_for_debit_note(self):
pi = self.create_purchase_invoice(do_not_submit=True)
pi.is_return = 1
pi.items[0].qty = -1
pi = pi.save().submit()
filters = {
"company": self.company,
"party_type": "Supplier",
"party": [self.supplier],
"report_date": today(),
"range": "30, 60, 90, 120",
}
data = execute(filters)
self.assertEqual(data[1][0].get("invoiced"), 300)
def create_purchase_invoice(self, do_not_submit=False):
frappe.set_user("Administrator")
pi = make_purchase_invoice(

View File

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

View File

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

View File

@@ -267,6 +267,18 @@ class ReceivablePayableReport:
row.invoiced_in_account_currency += amount_in_account_currency
else:
if self.is_invoice(ple):
# when invoice has is_return marked
if self.invoice_details.get(row.voucher_no, {}).get("is_return"):
# for Credit Note
if row.voucher_type == "Sales Invoice":
row.credit_note -= amount
row.credit_note_in_account_currency -= amount_in_account_currency
# for Debit Note
else:
row.invoiced -= amount
row.invoiced_in_account_currency -= amount_in_account_currency
return
if row.voucher_no == ple.voucher_no == ple.against_voucher_no:
row.paid -= amount
row.paid_in_account_currency -= amount_in_account_currency
@@ -421,7 +433,7 @@ class ReceivablePayableReport:
# nosemgrep
si_list = frappe.db.sql(
"""
select name, due_date, po_no
select name, due_date, po_no, is_return
from `tabSales Invoice`
where posting_date <= %s
and company = %s
@@ -453,7 +465,7 @@ class ReceivablePayableReport:
# nosemgrep
for pi in frappe.db.sql(
"""
select name, due_date, bill_no, bill_date
select name, due_date, bill_no, bill_date, is_return
from `tabPurchase Invoice`
where
posting_date <= %s

View File

@@ -204,7 +204,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
expected_data_after_credit_note = [
[100.0, 100.0, 40.0, 0.0, 60.0, si.name],
[0, 0, 100.0, 0.0, -100.0, cr_note.name],
[0, 0, 0, 100.0, -100.0, cr_note.name],
]
self.assertEqual(len(report[1]), 2)
si_row = next(
@@ -478,13 +478,19 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
report = execute(filters)[1]
self.assertEqual(len(report), 2)
expected_data = {sr.name: [10.0, -10.0, 0.0, -10], si.name: [100.0, 100.0, 10.0, 90.0]}
expected_data = {sr.name: [0.0, 10.0, -10.0, 0.0, -10], si.name: [100.0, 0.0, 100.0, 10.0, 90.0]}
rows = report[:2]
for row in rows:
self.assertEqual(
expected_data[row.voucher_no],
[row.invoiced or row.paid, row.outstanding, row.remaining_balance, row.future_amount],
[
row.invoiced or row.paid,
row.credit_note,
row.outstanding,
row.remaining_balance,
row.future_amount,
],
)
pe.cancel()

View File

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

View File

@@ -50,6 +50,7 @@ def get_group_by_asset_category_data(filters):
flt(row.accumulated_depreciation_as_on_from_date)
+ flt(row.depreciation_amount_during_the_period)
- flt(row.depreciation_eliminated_during_the_period)
- flt(row.depreciation_eliminated_via_reversal)
)
row.net_asset_value_as_on_from_date = flt(row.value_as_on_from_date) - flt(
@@ -247,6 +248,7 @@ def get_group_by_asset_data(filters):
flt(row.accumulated_depreciation_as_on_from_date)
+ flt(row.depreciation_amount_during_the_period)
- flt(row.depreciation_eliminated_during_the_period)
- flt(row.depreciation_eliminated_via_reversal)
)
row.net_asset_value_as_on_from_date = flt(row.value_as_on_from_date) - flt(
@@ -276,6 +278,7 @@ def get_assets_for_grouped_by_category(filters):
f"""
SELECT results.asset_category,
sum(results.accumulated_depreciation_as_on_from_date) as accumulated_depreciation_as_on_from_date,
sum(results.depreciation_eliminated_via_reversal) as depreciation_eliminated_via_reversal,
sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period,
sum(results.depreciation_amount_during_the_period) as depreciation_amount_during_the_period
from (SELECT a.asset_category,
@@ -284,6 +287,11 @@ def get_assets_for_grouped_by_category(filters):
else
0
end), 0) as accumulated_depreciation_as_on_from_date,
ifnull(sum(case when gle.posting_date <= %(to_date)s and ifnull(a.disposal_date, 0) = 0 then
gle.credit
else
0
end), 0) as depreciation_eliminated_via_reversal,
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and a.disposal_date >= %(from_date)s
and a.disposal_date <= %(to_date)s and gle.posting_date <= a.disposal_date then
gle.debit
@@ -307,7 +315,6 @@ def get_assets_for_grouped_by_category(filters):
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}
@@ -319,6 +326,7 @@ def get_assets_for_grouped_by_category(filters):
else
a.opening_accumulated_depreciation
end), 0) as accumulated_depreciation_as_on_from_date,
0 as depreciation_eliminated_via_reversal,
ifnull(sum(case when a.disposal_date >= %(from_date)s and a.disposal_date <= %(to_date)s then
a.opening_accumulated_depreciation
else
@@ -354,6 +362,7 @@ def get_assets_for_grouped_by_asset(filters):
f"""
SELECT results.name as asset,
sum(results.accumulated_depreciation_as_on_from_date) as accumulated_depreciation_as_on_from_date,
sum(results.depreciation_eliminated_via_reversal) as depreciation_eliminated_via_reversal,
sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period,
sum(results.depreciation_amount_during_the_period) as depreciation_amount_during_the_period
from (SELECT a.name as name,
@@ -362,6 +371,11 @@ def get_assets_for_grouped_by_asset(filters):
else
0
end), 0) as accumulated_depreciation_as_on_from_date,
ifnull(sum(case when gle.posting_date <= %(to_date)s and ifnull(a.disposal_date, 0) = 0 then
gle.credit
else
0
end), 0) as depreciation_eliminated_via_reversal,
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and a.disposal_date >= %(from_date)s
and a.disposal_date <= %(to_date)s and gle.posting_date <= a.disposal_date then
gle.debit
@@ -385,7 +399,6 @@ def get_assets_for_grouped_by_asset(filters):
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}
@@ -397,6 +410,7 @@ def get_assets_for_grouped_by_asset(filters):
else
a.opening_accumulated_depreciation
end), 0) as accumulated_depreciation_as_on_from_date,
0 as depreciation_as_on_from_date_credit,
ifnull(sum(case when a.disposal_date >= %(from_date)s and a.disposal_date <= %(to_date)s then
a.opening_accumulated_depreciation
else
@@ -503,6 +517,12 @@ def get_columns(filters):
"fieldtype": "Currency",
"width": 270,
},
{
"label": _("Depreciation eliminated via reversal"),
"fieldname": "depreciation_eliminated_via_reversal",
"fieldtype": "Currency",
"width": 270,
},
{
"label": _("Net Asset value as on") + " " + formatdate(filters.day_before_from_date),
"fieldname": "net_asset_value_as_on_from_date",

View File

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

View File

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

View File

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

View File

@@ -307,6 +307,7 @@ class Deferred_Revenue_and_Expense_Report:
.where(
(inv.docstatus == 1)
& (deferred_flag_field == 1)
& (inv.company == self.filters.company)
& (
(
(self.period_list[0].from_date >= inv_item.service_start_date)

View File

@@ -2,5 +2,27 @@
// For license information, please see license.txt
frappe.query_reports["Delivered Items To Be Billed"] = {
filters: [],
filters: [
{
label: __("Company"),
fieldname: "company",
fieldtype: "Link",
options: "Company",
reqd: 1,
default: frappe.defaults.get_default("Company"),
},
{
label: __("As on Date"),
fieldname: "posting_date",
fieldtype: "Date",
reqd: 1,
default: frappe.datetime.get_today(),
},
{
label: __("Delivery Note"),
fieldname: "delivery_note",
fieldtype: "Link",
options: "Delivery Note",
},
],
};

View File

@@ -3,6 +3,7 @@
from frappe import _
from pypika import Order
from erpnext.accounts.report.non_billed_report import get_ordered_to_be_billed_data
@@ -10,7 +11,7 @@ from erpnext.accounts.report.non_billed_report import get_ordered_to_be_billed_d
def execute(filters=None):
columns = get_column()
args = get_args()
data = get_ordered_to_be_billed_data(args)
data = get_ordered_to_be_billed_data(args, filters)
return columns, data
@@ -76,13 +77,6 @@ def get_column():
"options": "Project",
"width": 120,
},
{
"label": _("Company"),
"fieldname": "company",
"fieldtype": "Link",
"options": "Company",
"width": 120,
},
]
@@ -92,5 +86,6 @@ def get_args():
"party": "customer",
"date": "posting_date",
"order": "name",
"order_by": "desc",
"order_by": Order.desc,
"reference_field": "delivery_note",
}

View File

@@ -630,7 +630,7 @@ def get_cost_centers_with_children(cost_centers):
def get_columns(periodicity, period_list, accumulated_values=1, company=None, cash_flow=False):
columns = [
{
"fieldname": "account",
"fieldname": "account" if not cash_flow else "section",
"label": _("Account") if not cash_flow else _("Section"),
"fieldtype": "Link",
"options": "Account",

View File

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

View File

@@ -534,6 +534,7 @@ def get_accountwise_gle(filters, accounting_dimensions, gl_entries, gle_map, tot
for dim in accounting_dimensions:
keylist.append(gle.get(dim))
keylist.append(gle.get("cost_center"))
keylist.append(gle.get("project"))
key = tuple(keylist)
if key not in consolidated_gle:
@@ -679,10 +680,11 @@ def get_columns(filters):
{"label": _("Against Account"), "fieldname": "against", "width": 120},
{"label": _("Party Type"), "fieldname": "party_type", "width": 100},
{"label": _("Party"), "fieldname": "party", "width": 100},
{"label": _("Project"), "options": "Project", "fieldname": "project", "width": 100},
]
if filters.get("include_dimensions"):
columns.append({"label": _("Project"), "options": "Project", "fieldname": "project", "width": 100})
for dim in get_accounting_dimensions(as_list=False):
columns.append(
{"label": _(dim.label), "options": dim.label, "fieldname": dim.fieldname, "width": 100}

View File

@@ -67,6 +67,7 @@ frappe.query_reports["Gross Profit"] = {
fieldname: "cost_center",
label: __("Cost Center"),
fieldtype: "MultiSelectList",
options: "Cost Center",
get_data: function (txt) {
return frappe.db.get_link_options("Cost Center", txt, {
company: frappe.query_report.get_filter_value("company"),
@@ -77,6 +78,7 @@ frappe.query_reports["Gross Profit"] = {
fieldname: "project",
label: __("Project"),
fieldtype: "MultiSelectList",
options: "Project",
get_data: function (txt) {
return frappe.db.get_link_options("Project", txt, {
company: frappe.query_report.get_filter_value("company"),

View File

@@ -219,15 +219,34 @@ def get_data_when_grouped_by_invoice(columns, gross_profit_data, filters, group_
def get_data_when_not_grouped_by_invoice(gross_profit_data, filters, group_wise_columns, data):
for src in gross_profit_data.grouped_data:
row = []
for col in group_wise_columns.get(scrub(filters.group_by)):
row.append(src.get(col))
total_base_amount = 0
total_buying_amount = 0
row.append(filters.currency)
group_columns = group_wise_columns.get(scrub(filters.group_by))
for src in gross_profit_data.grouped_data:
total_base_amount += src.base_amount or 0.00
total_buying_amount += src.buying_amount or 0.00
row = [src.get(col) for col in group_columns] + [filters.currency]
data.append(row)
total_gross_profit = total_base_amount - total_buying_amount
currency_precision = cint(frappe.db.get_default("currency_precision")) or 3
gross_profit_percent = (total_gross_profit / total_base_amount * 100.0) if total_base_amount else 0
total_row = {
group_columns[0]: "Total",
"base_amount": total_base_amount,
"buying_amount": total_buying_amount,
"gross_profit": total_gross_profit,
"gross_profit_percent": flt(gross_profit_percent, currency_precision),
}
total_row = [total_row.get(col, None) for col in [*group_columns, "currency"]]
data.append(total_row)
def get_columns(group_wise_columns, filters):
columns = []

View File

@@ -4,11 +4,12 @@
import frappe
from frappe.model.meta import get_field_precision
from frappe.query_builder.functions import IfNull, Round
from erpnext import get_default_currency
def get_ordered_to_be_billed_data(args):
def get_ordered_to_be_billed_data(args, filters=None):
doctype, party = args.get("doctype"), args.get("party")
child_tab = doctype + " Item"
precision = (
@@ -18,47 +19,57 @@ def get_ordered_to_be_billed_data(args):
or 2
)
project_field = get_project_field(doctype, party)
doctype = frappe.qb.DocType(doctype)
child_doctype = frappe.qb.DocType(child_tab)
return frappe.db.sql(
"""
Select
`{parent_tab}`.name, `{parent_tab}`.{date_field},
`{parent_tab}`.{party}, `{parent_tab}`.{party}_name,
`{child_tab}`.item_code,
`{child_tab}`.base_amount,
(`{child_tab}`.billed_amt * ifnull(`{parent_tab}`.conversion_rate, 1)),
(`{child_tab}`.base_rate * ifnull(`{child_tab}`.returned_qty, 0)),
(`{child_tab}`.base_amount -
(`{child_tab}`.billed_amt * ifnull(`{parent_tab}`.conversion_rate, 1)) -
(`{child_tab}`.base_rate * ifnull(`{child_tab}`.returned_qty, 0))),
`{child_tab}`.item_name, `{child_tab}`.description,
{project_field}, `{parent_tab}`.company
from
`{parent_tab}`, `{child_tab}`
where
`{parent_tab}`.name = `{child_tab}`.parent and `{parent_tab}`.docstatus = 1
and `{parent_tab}`.status not in ('Closed', 'Completed')
and `{child_tab}`.amount > 0
and (`{child_tab}`.base_amount -
round(`{child_tab}`.billed_amt * ifnull(`{parent_tab}`.conversion_rate, 1), {precision}) -
(`{child_tab}`.base_rate * ifnull(`{child_tab}`.returned_qty, 0))) > 0
order by
`{parent_tab}`.{order} {order_by}
""".format(
parent_tab="tab" + doctype,
child_tab="tab" + child_tab,
precision=precision,
party=party,
date_field=args.get("date"),
project_field=project_field,
order=args.get("order"),
order_by=args.get("order_by"),
docname = filters.get(args.get("reference_field"), None)
project_field = get_project_field(doctype, child_doctype, party)
query = (
frappe.qb.from_(doctype)
.inner_join(child_doctype)
.on(doctype.name == child_doctype.parent)
.select(
doctype.name,
doctype[args.get("date")].as_("date"),
doctype[party],
doctype[party + "_name"],
child_doctype.item_code,
child_doctype.base_amount.as_("amount"),
(child_doctype.billed_amt * IfNull(doctype.conversion_rate, 1)).as_("billed_amount"),
(child_doctype.base_rate * IfNull(child_doctype.returned_qty, 0)).as_("returned_amount"),
(
child_doctype.base_amount
- (child_doctype.billed_amt * IfNull(doctype.conversion_rate, 1))
- (child_doctype.base_rate * IfNull(child_doctype.returned_qty, 0))
).as_("pending_amount"),
child_doctype.item_name,
child_doctype.description,
project_field,
)
.where(
(doctype.docstatus == 1)
& (doctype.status.notin(["Closed", "Completed"]))
& (doctype.company == filters.get("company"))
& (doctype.posting_date <= filters.get("posting_date"))
& (child_doctype.amount > 0)
& (
child_doctype.base_amount
- Round(child_doctype.billed_amt * IfNull(doctype.conversion_rate, 1), precision)
- (child_doctype.base_rate * IfNull(child_doctype.returned_qty, 0))
)
> 0
)
.orderby(doctype[args.get("order")], order=args.get("order_by"))
)
if docname:
query = query.where(doctype.name == docname)
def get_project_field(doctype, party):
return query.run(as_dict=True)
def get_project_field(doctype, child_doctype, party):
if party == "supplier":
doctype = doctype + " Item"
return "`tab%s`.project" % (doctype)
return child_doctype.project
return doctype.project

View File

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

View File

@@ -2,5 +2,27 @@
// For license information, please see license.txt
frappe.query_reports["Received Items To Be Billed"] = {
filters: [],
filters: [
{
label: __("Company"),
fieldname: "company",
fieldtype: "Link",
options: "Company",
reqd: 1,
default: frappe.defaults.get_default("Company"),
},
{
label: __("As on Date"),
fieldname: "posting_date",
fieldtype: "Date",
reqd: 1,
default: frappe.datetime.get_today(),
},
{
label: __("Purchase Receipt"),
fieldname: "purchase_receipt",
fieldtype: "Link",
options: "Purchase Receipt",
},
],
};

View File

@@ -3,6 +3,7 @@
from frappe import _
from pypika import Order
from erpnext.accounts.report.non_billed_report import get_ordered_to_be_billed_data
@@ -10,7 +11,7 @@ from erpnext.accounts.report.non_billed_report import get_ordered_to_be_billed_d
def execute(filters=None):
columns = get_column()
args = get_args()
data = get_ordered_to_be_billed_data(args)
data = get_ordered_to_be_billed_data(args, filters)
return columns, data
@@ -76,13 +77,6 @@ def get_column():
"options": "Project",
"width": 120,
},
{
"label": _("Company"),
"fieldname": "company",
"fieldtype": "Link",
"options": "Company",
"width": 120,
},
]
@@ -92,5 +86,6 @@ def get_args():
"party": "supplier",
"date": "posting_date",
"order": "name",
"order_by": "desc",
"order_by": Order.desc,
"reference_field": "purchase_receipt",
}

View File

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

View File

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

View File

@@ -773,6 +773,8 @@ def update_reference_in_payment_entry(
frappe._dict({"difference_posting_date": d.difference_posting_date}), dimensions_dict
)
# Ledgers will be reposted by Reconciliation tool
payment_entry.flags.ignore_reposting_on_reconciliation = True
if not do_not_save:
payment_entry.save(ignore_permissions=True)
return row, update_advance_paid
@@ -1415,7 +1417,7 @@ def repost_gle_for_stock_vouchers(
if not warehouse_account:
warehouse_account = get_warehouse_account_map(company)
stock_vouchers = sort_stock_vouchers_by_posting_date(stock_vouchers)
stock_vouchers = sort_stock_vouchers_by_posting_date(stock_vouchers, company=company)
if repost_doc and repost_doc.gl_reposting_index:
# Restore progress
stock_vouchers = stock_vouchers[cint(repost_doc.gl_reposting_index) :]
@@ -1468,7 +1470,9 @@ def _delete_accounting_ledger_entries(voucher_type, voucher_no):
_delete_pl_entries(voucher_type, voucher_no)
def sort_stock_vouchers_by_posting_date(stock_vouchers: list[tuple[str, str]]) -> list[tuple[str, str]]:
def sort_stock_vouchers_by_posting_date(
stock_vouchers: list[tuple[str, str]], company=None
) -> list[tuple[str, str]]:
sle = frappe.qb.DocType("Stock Ledger Entry")
voucher_nos = [v[1] for v in stock_vouchers]
@@ -1479,7 +1483,12 @@ def sort_stock_vouchers_by_posting_date(stock_vouchers: list[tuple[str, str]]) -
.groupby(sle.voucher_type, sle.voucher_no)
.orderby(sle.posting_datetime)
.orderby(sle.creation)
).run(as_dict=True)
)
if company:
sles = sles.where(sle.company == company)
sles = sles.run(as_dict=True)
sorted_vouchers = [(sle.voucher_type, sle.voucher_no) for sle in sles]
unknown_vouchers = set(stock_vouchers) - set(sorted_vouchers)

View File

@@ -93,7 +93,7 @@
},
{
"hidden": 0,
"is_query_report": 0,
"is_query_report": 1,
"label": "Accounts Payable",
"link_count": 0,
"link_to": "Accounts Payable",
@@ -103,7 +103,7 @@
},
{
"hidden": 0,
"is_query_report": 0,
"is_query_report": 1,
"label": "Accounts Payable Summary",
"link_count": 0,
"link_to": "Accounts Payable Summary",
@@ -113,7 +113,7 @@
},
{
"hidden": 0,
"is_query_report": 0,
"is_query_report": 1,
"label": "Purchase Register",
"link_count": 0,
"link_to": "Purchase Register",
@@ -123,7 +123,7 @@
},
{
"hidden": 0,
"is_query_report": 0,
"is_query_report": 1,
"label": "Item-wise Purchase Register",
"link_count": 0,
"link_to": "Item-wise Purchase Register",
@@ -133,7 +133,7 @@
},
{
"hidden": 0,
"is_query_report": 0,
"is_query_report": 1,
"label": "Purchase Order Analysis",
"link_count": 0,
"link_to": "Purchase Order Analysis",
@@ -143,7 +143,7 @@
},
{
"hidden": 0,
"is_query_report": 0,
"is_query_report": 1,
"label": "Received Items To Be Billed",
"link_count": 0,
"link_to": "Received Items To Be Billed",
@@ -153,7 +153,7 @@
},
{
"hidden": 0,
"is_query_report": 0,
"is_query_report": 1,
"label": "Supplier Ledger Summary",
"link_count": 0,
"link_to": "Supplier Ledger Summary",

View File

@@ -609,9 +609,7 @@ frappe.ui.form.on("Asset", {
frm.trigger("toggle_reference_doc");
if (frm.doc.purchase_receipt) {
if (frm.doc.item_code) {
frappe.db.get_doc("Purchase Receipt", frm.doc.purchase_receipt).then((pr_doc) => {
frm.events.set_values_from_purchase_doc(frm, "Purchase Receipt", pr_doc);
});
frm.events.set_values_from_purchase_doc(frm, "Purchase Receipt");
} else {
frm.set_value("purchase_receipt", "");
frappe.msgprint({
@@ -626,9 +624,7 @@ frappe.ui.form.on("Asset", {
frm.trigger("toggle_reference_doc");
if (frm.doc.purchase_invoice) {
if (frm.doc.item_code) {
frappe.db.get_doc("Purchase Invoice", frm.doc.purchase_invoice).then((pi_doc) => {
frm.events.set_values_from_purchase_doc(frm, "Purchase Invoice", pi_doc);
});
frm.events.set_values_from_purchase_doc(frm, "Purchase Invoice");
} else {
frm.set_value("purchase_invoice", "");
frappe.msgprint({
@@ -639,45 +635,35 @@ frappe.ui.form.on("Asset", {
}
},
set_values_from_purchase_doc: function (frm, doctype, purchase_doc) {
frm.set_value("company", purchase_doc.company);
if (purchase_doc.bill_date) {
frm.set_value("purchase_date", purchase_doc.bill_date);
} else {
frm.set_value("purchase_date", purchase_doc.posting_date);
}
if (!frm.doc.is_existing_asset && !frm.doc.available_for_use_date) {
frm.set_value("available_for_use_date", frm.doc.purchase_date);
}
const item = purchase_doc.items.find((item) => item.item_code === frm.doc.item_code);
if (!item) {
let doctype_field = frappe.scrub(doctype);
frm.set_value(doctype_field, "");
frappe.msgprint({
title: __("Invalid {0}", [__(doctype)]),
message: __("The selected {0} does not contain the selected Asset Item.", [__(doctype)]),
indicator: "red",
});
}
frappe.db.get_value("Item", item.item_code, "is_grouped_asset", (r) => {
var asset_quantity = r.is_grouped_asset ? item.qty : 1;
var purchase_amount = flt(
item.valuation_rate * asset_quantity,
precision("gross_purchase_amount")
);
set_values_from_purchase_doc: (frm, doctype) => {
frappe.call({
method: "erpnext.assets.doctype.asset.asset.get_values_from_purchase_doc",
args: {
purchase_doc_name: frm.doc.purchase_receipt || frm.doc.purchase_invoice,
item_code: frm.doc.item_code,
doctype: doctype,
},
callback: (r) => {
if (r.message) {
let data = r.message;
frm.set_value("company", data.company);
frm.set_value("purchase_date", data.purchase_date);
frm.set_value("gross_purchase_amount", data.gross_purchase_amount);
frm.set_value("purchase_amount", data.gross_purchase_amount);
frm.set_value("asset_quantity", data.asset_quantity);
frm.set_value("cost_center", data.cost_center);
frm.set_value("gross_purchase_amount", purchase_amount);
frm.set_value("purchase_amount", purchase_amount);
frm.set_value("asset_quantity", asset_quantity);
frm.set_value("cost_center", item.cost_center || purchase_doc.cost_center);
if (item.asset_location) {
frm.set_value("location", item.asset_location);
}
if (doctype === "Purchase Receipt") {
frm.set_value("purchase_receipt_item", item.name);
} else if (doctype === "Purchase Invoice") {
frm.set_value("purchase_invoice_item", item.name);
}
if (doctype === "Purchase Receipt") {
frm.set_value("purchase_receipt_item", data.purchase_receipt_item);
} else {
frm.set_value("purchase_invoice_item", data.purchase_invoice_item);
}
let is_editable = !data.is_multiple_items; // if multiple items, then fields should be read-only
frm.set_df_property("gross_purchase_amount", "read_only", is_editable);
frm.set_df_property("asset_quantity", "read_only", is_editable);
}
},
});
},

View File

@@ -225,10 +225,9 @@
{
"fieldname": "gross_purchase_amount",
"fieldtype": "Currency",
"label": "Gross Purchase Amount",
"label": "Net Purchase Amount",
"mandatory_depends_on": "eval:(!doc.is_composite_asset || doc.docstatus==1)",
"options": "Company:company:default_currency",
"read_only_depends_on": "eval:!doc.is_existing_asset"
"options": "Company:company:default_currency"
},
{
"fieldname": "available_for_use_date",
@@ -470,8 +469,7 @@
"default": "1",
"fieldname": "asset_quantity",
"fieldtype": "Int",
"label": "Asset Quantity",
"read_only_depends_on": "eval:!doc.is_existing_asset && !doc.is_composite_asset"
"label": "Asset Quantity"
},
{
"fieldname": "depr_entry_posting_status",
@@ -541,14 +539,13 @@
},
{
"fieldname": "purchase_receipt_item",
"fieldtype": "Link",
"fieldtype": "Data",
"hidden": 1,
"label": "Purchase Receipt Item",
"options": "Purchase Receipt Item"
"label": "Purchase Receipt Item"
},
{
"fieldname": "purchase_invoice_item",
"fieldtype": "Link",
"fieldtype": "Data",
"hidden": 1,
"label": "Purchase Invoice Item",
"options": "Purchase Invoice Item"
@@ -595,7 +592,7 @@
"link_fieldname": "target_asset"
}
],
"modified": "2024-12-26 14:23:20.968882",
"modified": "2025-02-20 14:09:05.421913",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset",

View File

@@ -95,9 +95,9 @@ class Asset(AccountsController):
purchase_amount: DF.Currency
purchase_date: DF.Date | None
purchase_invoice: DF.Link | None
purchase_invoice_item: DF.Link | None
purchase_invoice_item: DF.Data | None
purchase_receipt: DF.Link | None
purchase_receipt_item: DF.Link | None
purchase_receipt_item: DF.Data | None
split_from: DF.Link | None
status: DF.Literal[
"Draft",
@@ -121,6 +121,7 @@ class Asset(AccountsController):
def validate(self):
self.validate_precision()
self.set_purchase_doc_row_item()
self.validate_asset_values()
self.validate_asset_and_reference()
self.validate_item()
@@ -199,6 +200,38 @@ class Asset(AccountsController):
def after_delete(self):
add_asset_activity(self.name, _("Asset deleted"))
def set_purchase_doc_row_item(self):
if self.is_existing_asset or self.is_composite_asset:
return
self.purchase_amount = self.gross_purchase_amount
purchase_doc_type = "Purchase Receipt" if self.purchase_receipt else "Purchase Invoice"
purchase_doc = self.purchase_receipt or self.purchase_invoice
if not purchase_doc:
return
linked_item = self.get_linked_item(purchase_doc_type, purchase_doc)
if linked_item:
if purchase_doc_type == "Purchase Receipt":
self.purchase_receipt_item = linked_item
else:
self.purchase_invoice_item = linked_item
def get_linked_item(self, purchase_doc_type, purchase_doc):
purchase_doc = frappe.get_doc(purchase_doc_type, purchase_doc)
for item in purchase_doc.items:
if self.asset_quantity > 1:
if item.base_net_amount == self.gross_purchase_amount and item.qty == self.asset_quantity:
return item.name
elif item.qty == self.asset_quantity:
return item.name
else:
if item.base_net_rate == self.gross_purchase_amount and item.qty == self.asset_quantity:
return item.name
def validate_asset_and_reference(self):
if self.purchase_invoice or self.purchase_receipt:
reference_doc = "Purchase Invoice" if self.purchase_invoice else "Purchase Receipt"
@@ -1125,6 +1158,30 @@ def has_active_capitalization(asset):
return active_capitalizations > 0
@frappe.whitelist()
def get_values_from_purchase_doc(purchase_doc_name, item_code, doctype):
purchase_doc = frappe.get_doc(doctype, purchase_doc_name)
matching_items = [item for item in purchase_doc.items if item.item_code == item_code]
if not matching_items:
frappe.throw(_(f"Selected {doctype} does not contain the Item Code {item_code}"))
first_item = matching_items[0]
is_multiple_items = len(matching_items) > 1
return {
"company": purchase_doc.company,
"purchase_date": purchase_doc.get("bill_date") or purchase_doc.get("posting_date"),
"gross_purchase_amount": flt(first_item.base_net_amount),
"asset_quantity": first_item.qty,
"cost_center": first_item.cost_center or purchase_doc.get("cost_center"),
"asset_location": first_item.get("asset_location"),
"is_multiple_items": is_multiple_items,
"purchase_receipt_item": first_item.name if doctype == "Purchase Receipt" else None,
"purchase_invoice_item": first_item.name if doctype == "Purchase Invoice" else None,
}
@frappe.whitelist()
def split_asset(asset_name, split_qty):
asset = frappe.get_doc("Asset", asset_name)

View File

@@ -444,9 +444,9 @@ def scrap_asset(asset_name):
notes = _("This schedule was created when Asset {0} was scrapped.").format(
get_link_to_form(asset.doctype, asset.name)
)
depreciate_asset(asset, date, notes)
asset.reload()
if asset.status != "Fully Depreciated":
depreciate_asset(asset, date, notes)
asset.reload()
depreciation_series = frappe.get_cached_value("Company", asset.company, "series_for_depreciation_entry")

View File

@@ -143,14 +143,19 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s
}
}
set_consumed_stock_items_tagged_to_wip_composite_asset(asset) {
set_consumed_stock_items_tagged_to_wip_composite_asset(target_asset) {
var me = this;
if (asset) {
if (target_asset) {
return me.frm.call({
method: "erpnext.assets.doctype.asset_capitalization.asset_capitalization.get_items_tagged_to_wip_composite_asset",
args: {
asset: asset,
params: {
target_asset: target_asset,
finance_book: me.frm.doc.finance_book,
posting_date: me.frm.doc.posting_date,
posting_time: me.frm.doc.posting_time,
},
},
callback: function (r) {
if (!r.exc && r.message) {

View File

@@ -856,7 +856,10 @@ def get_service_item_details(args):
@frappe.whitelist()
def get_items_tagged_to_wip_composite_asset(asset):
def get_items_tagged_to_wip_composite_asset(params):
if isinstance(params, str):
params = json.loads(params)
fields = [
"item_code",
"item_name",
@@ -871,25 +874,66 @@ def get_items_tagged_to_wip_composite_asset(asset):
"amount",
"is_fixed_asset",
"parent",
"name",
]
pr_items = frappe.get_all(
"Purchase Receipt Item", filters={"wip_composite_asset": asset, "docstatus": 1}, fields=fields
"Purchase Receipt Item",
filters={"wip_composite_asset": params.get("target_asset"), "docstatus": 1},
fields=fields,
)
stock_items = []
asset_items = []
for d in pr_items:
if not d.is_fixed_asset:
stock_items.append(frappe._dict(d))
stock_item = process_stock_item(d)
if stock_item:
stock_items.append(stock_item)
else:
asset_details = frappe.db.get_value(
"Asset",
{"item_code": d.item_code, "purchase_receipt": d.parent},
["name as asset", "asset_name"],
as_dict=1,
)
d.update(asset_details)
asset_items.append(frappe._dict(d))
asset_item = process_fixed_asset(d)
if asset_item:
asset_items.append(asset_item)
return stock_items, asset_items
def process_stock_item(d):
stock_capitalized = frappe.db.exists(
"Asset Capitalization Stock Item",
{
"purchase_receipt_item": d.name,
"parentfield": "stock_items",
"parenttype": "Asset Capitalization",
"docstatus": 1,
},
)
if stock_capitalized:
return None
stock_item_data = frappe._dict(d)
stock_item_data.purchase_receipt_item = d.name
return stock_item_data
def process_fixed_asset(d):
asset_details = frappe.db.get_value(
"Asset",
{
"item_code": d.item_code,
"purchase_receipt": d.parent,
"status": ("not in", ["Draft", "Scrapped", "Sold", "Capitalized"]),
},
["name as asset", "asset_name", "company"],
as_dict=1,
)
if asset_details:
asset_details.update(d)
asset_details.update(get_consumed_asset_details(asset_details))
d.update(asset_details)
return frappe._dict(d)
return None

View File

@@ -10,12 +10,13 @@
"column_break_3",
"warehouse",
"section_break_6",
"purchase_receipt_item",
"stock_qty",
"stock_uom",
"actual_qty",
"column_break_9",
"valuation_rate",
"amount",
"stock_uom",
"batch_and_serial_no_section",
"serial_and_batch_bundle",
"use_serial_batch_fields",
@@ -53,14 +54,14 @@
{
"fieldname": "section_break_6",
"fieldtype": "Section Break",
"label": "Qty and Rate"
"label": "Purchase Details"
},
{
"columns": 1,
"fieldname": "stock_qty",
"fieldtype": "Float",
"in_list_view": 1,
"label": "Qty",
"label": "Quantity",
"non_negative": 1
},
{
@@ -172,18 +173,26 @@
{
"fieldname": "column_break_mbuv",
"fieldtype": "Column Break"
},
{
"fieldname": "purchase_receipt_item",
"fieldtype": "Data",
"hidden": 1,
"label": "Purchase Receipt Item"
}
],
"grid_page_length": 50,
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2024-06-26 17:06:22.564438",
"modified": "2025-03-05 12:46:01.074742",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset Capitalization Stock Item",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"row_format": "Dynamic",
"sort_field": "creation",
"sort_order": "DESC",
"states": [],
"track_changes": 1

View File

@@ -23,6 +23,7 @@ class AssetCapitalizationStockItem(Document):
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
purchase_receipt_item: DF.Data | None
serial_and_batch_bundle: DF.Link | None
serial_no: DF.Text | None
stock_qty: DF.Float

View File

@@ -404,7 +404,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
);
}
} else {
if (!doc.items.every((item) => item.qty == item.sco_qty)) {
if (!doc.items.every((item) => item.qty == item.subcontracted_quantity)) {
this.frm.add_custom_button(
__("Subcontracting Order"),
() => {

View File

@@ -898,7 +898,7 @@ def is_po_fully_subcontracted(po_name):
query = (
frappe.qb.from_(table)
.select(table.name)
.where((table.parent == po_name) & (table.qty != table.sco_qty))
.where((table.parent == po_name) & (table.qty != table.subcontracted_quantity))
)
return not query.run(as_dict=True)
@@ -945,7 +945,7 @@ def get_mapped_subcontracting_order(source_name, target_doc=None):
"material_request_item": "material_request_item",
},
"field_no_map": ["qty", "fg_item_qty", "amount"],
"condition": lambda item: item.qty != item.sco_qty,
"condition": lambda item: item.qty != item.subcontracted_quantity,
},
},
target_doc,

View File

@@ -920,6 +920,7 @@ class TestPurchaseOrder(FrappeTestCase):
automatically_fetch_payment_terms(enable=0)
def test_internal_transfer_flow(self):
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
make_inter_company_purchase_invoice,
)
@@ -935,8 +936,16 @@ class TestPurchaseOrder(FrappeTestCase):
prepare_data_for_internal_transfer()
supplier = "_Test Internal Supplier 2"
create_cost_center(
cost_center_name="_Test Cost Center for perpetual inventory Account",
company="_Test Company with perpetual inventory",
)
mr = make_material_request(
qty=2, company="_Test Company with perpetual inventory", warehouse="Stores - TCP1"
qty=2,
company="_Test Company with perpetual inventory",
warehouse="Stores - TCP1",
cost_center="_Test Cost Center for perpetual inventory Account - TCP1",
)
po = create_purchase_order(
@@ -1076,9 +1085,9 @@ class TestPurchaseOrder(FrappeTestCase):
# Test - 2: Subcontracted Quantity for the PO Items of each line item should be updated accordingly
po.reload()
self.assertEqual(po.items[0].sco_qty, 5)
self.assertEqual(po.items[1].sco_qty, 0)
self.assertEqual(po.items[2].sco_qty, 12.5)
self.assertEqual(po.items[0].subcontracted_quantity, 5)
self.assertEqual(po.items[1].subcontracted_quantity, 0)
self.assertEqual(po.items[2].subcontracted_quantity, 12.5)
# Test - 3: Amount for both FG Item and its Service Item should be updated correctly based on change in Quantity
self.assertEqual(sco.items[0].amount, 2000)
@@ -1114,10 +1123,10 @@ class TestPurchaseOrder(FrappeTestCase):
# Test - 8: Subcontracted Quantity for each PO Item should be subtracted if SCO gets cancelled
po.reload()
self.assertEqual(po.items[2].sco_qty, 25)
self.assertEqual(po.items[2].subcontracted_quantity, 25)
sco.cancel()
po.reload()
self.assertEqual(po.items[2].sco_qty, 12.5)
self.assertEqual(po.items[2].subcontracted_quantity, 12.5)
sco = make_subcontracting_order(po.name)
sco.save()

View File

@@ -26,7 +26,7 @@
"quantity_and_rate",
"qty",
"stock_uom",
"sco_qty",
"subcontracted_quantity",
"col_break2",
"uom",
"conversion_factor",
@@ -913,18 +913,20 @@
},
{
"allow_on_submit": 1,
"fieldname": "sco_qty",
"fieldname": "subcontracted_quantity",
"fieldtype": "Float",
"label": "Subcontracted Quantity",
"no_copy": 1,
"non_negative": 1,
"read_only": 1
}
],
"grid_page_length": 50,
"idx": 1,
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2024-12-10 12:11:18.536089",
"modified": "2025-03-02 16:58:26.059601",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order Item",
@@ -932,6 +934,7 @@
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"row_format": "Dynamic",
"search_fields": "item_name",
"sort_field": "modified",
"sort_order": "DESC",

View File

@@ -80,10 +80,10 @@ class PurchaseOrderItem(Document):
sales_order_item: DF.Data | None
sales_order_packed_item: DF.Data | None
schedule_date: DF.Date
sco_qty: DF.Float
stock_qty: DF.Float
stock_uom: DF.Link
stock_uom_rate: DF.Currency
subcontracted_quantity: DF.Float
supplier_part_no: DF.Data | None
supplier_quotation: DF.Link | None
supplier_quotation_item: DF.Link | None

View File

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

View File

@@ -19,6 +19,10 @@ frappe.query_reports["Purchase Order Analysis"] = {
width: "80",
reqd: 1,
default: frappe.datetime.add_months(frappe.datetime.get_today(), -1),
on_change: (report) => {
report.set_filter_value("name", []);
report.refresh();
},
},
{
fieldname: "to_date",
@@ -27,6 +31,10 @@ frappe.query_reports["Purchase Order Analysis"] = {
width: "80",
reqd: 1,
default: frappe.datetime.get_today(),
on_change: (report) => {
report.set_filter_value("name", []);
report.refresh();
},
},
{
fieldname: "project",
@@ -38,13 +46,17 @@ frappe.query_reports["Purchase Order Analysis"] = {
{
fieldname: "name",
label: __("Purchase Order"),
fieldtype: "Link",
fieldtype: "MultiSelectList",
width: "80",
options: "Purchase Order",
get_query: () => {
return {
filters: { docstatus: 1 },
};
get_data: function (txt) {
let filters = { docstatus: 1 };
const from_date = frappe.query_report.get_filter_value("from_date");
const to_date = frappe.query_report.get_filter_value("to_date");
if (from_date && to_date) filters["transaction_date"] = ["between", [from_date, to_date]];
return frappe.db.get_link_options("Purchase Order", txt, filters);
},
},
{
@@ -52,8 +64,16 @@ frappe.query_reports["Purchase Order Analysis"] = {
label: __("Status"),
fieldtype: "MultiSelectList",
width: "80",
options: ["To Pay", "To Bill", "To Receive", "To Receive and Bill", "Completed", "Closed"],
get_data: function (txt) {
let status = ["To Bill", "To Receive", "To Receive and Bill", "Completed"];
let status = [
"To Pay",
"To Bill",
"To Receive",
"To Receive and Bill",
"Completed",
"Closed",
];
let options = [];
for (let option of status) {
options.push({

View File

@@ -70,14 +70,16 @@ def get_data(filters):
po.company,
po_item.name,
)
.where((po_item.parent == po.name) & (po.status.notin(("Stopped", "Closed"))) & (po.docstatus == 1))
.where((po_item.parent == po.name) & (po.status.notin(("Stopped", "On Hold"))) & (po.docstatus == 1))
.groupby(po_item.name)
.orderby(po.transaction_date)
)
for field in ("company", "name"):
if filters.get(field):
query = query.where(po[field] == filters.get(field))
if filters.get("company"):
query = query.where(po.company == filters.get("company"))
if filters.get("name"):
query = query.where(po.name.isin(filters.get("name")))
if filters.get("from_date") and filters.get("to_date"):
query = query.where(po.transaction_date.between(filters.get("from_date"), filters.get("to_date")))

View File

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

View File

@@ -170,7 +170,7 @@ class AccountsController(TransactionBase):
self.validate_qty_is_not_zero()
if (
self.doctype in ["Sales Invoice", "Purchase Invoice"]
self.doctype in ["Sales Invoice", "Purchase Invoice", "POS Invoice"]
and self.get("is_return")
and self.get("update_stock")
):
@@ -194,6 +194,14 @@ class AccountsController(TransactionBase):
self.set_incoming_rate()
self.init_internal_values()
# Need to set taxes based on taxes_and_charges template
# before calculating taxes and totals
if self.meta.get_field("taxes_and_charges"):
self.validate_enabled_taxes_and_charges()
self.validate_tax_account_company()
self.set_taxes_and_charges()
if self.meta.get_field("currency"):
self.calculate_taxes_and_totals()
@@ -204,10 +212,6 @@ class AccountsController(TransactionBase):
self.validate_all_documents_schedule()
if self.meta.get_field("taxes_and_charges"):
self.validate_enabled_taxes_and_charges()
self.validate_tax_account_company()
self.validate_party()
self.validate_currency()
self.validate_party_account_currency()
@@ -252,8 +256,6 @@ class AccountsController(TransactionBase):
self.validate_deferred_income_expense_account()
self.set_inter_company_account()
self.set_taxes_and_charges()
if self.doctype == "Purchase Invoice":
self.calculate_paid_amount()
# apply tax withholding only if checked and applicable
@@ -417,7 +419,7 @@ class AccountsController(TransactionBase):
.where(doc_field.fieldname == "company")
).run(as_list=True)
dimension_list = sum(dimension_list, ["Project"])
dimension_list = sum(dimension_list, ["Project", "Cost Center"])
self.validate_company(dimension_list)
for child in self.get_all_children() or []:
@@ -821,11 +823,15 @@ class AccountsController(TransactionBase):
and item.get("use_serial_batch_fields")
)
):
if fieldname == "batch_no" and not item.batch_no:
item.set("rate", ret.get("rate"))
item.set("price_list_rate", ret.get("price_list_rate"))
item.set(fieldname, value)
if fieldname == "batch_no" and item.batch_no and not item.is_free_item:
if ret.get("rate"):
item.set("rate", ret.get("rate"))
if not item.get("price_list_rate") and ret.get("price_list_rate"):
item.set("price_list_rate", ret.get("price_list_rate"))
elif fieldname in ["cost_center", "conversion_factor"] and not item.get(
fieldname
):
@@ -1113,20 +1119,19 @@ class AccountsController(TransactionBase):
)
# Update details in transaction currency
gl_dict.update(
{
"transaction_currency": self.get("currency") or self.company_currency,
"transaction_exchange_rate": item.get("exchange_rate", 1)
if self.doctype == "Journal Entry" and item
else self.get("conversion_rate", 1),
"debit_in_transaction_currency": self.get_value_in_transaction_currency(
account_currency, gl_dict, "debit"
),
"credit_in_transaction_currency": self.get_value_in_transaction_currency(
account_currency, gl_dict, "credit"
),
}
)
if self.doctype not in ["Purchase Invoice", "Sales Invoice", "Journal Entry", "Payment Entry"]:
gl_dict.update(
{
"transaction_currency": self.get("currency") or self.company_currency,
"transaction_exchange_rate": self.get("conversion_rate", 1),
"debit_in_transaction_currency": self.get_value_in_transaction_currency(
account_currency, gl_dict, "debit"
),
"credit_in_transaction_currency": self.get_value_in_transaction_currency(
account_currency, gl_dict, "credit"
),
}
)
if not args.get("against_voucher_type") and self.get("against_voucher_type"):
gl_dict.update({"against_voucher_type": self.get("against_voucher_type")})
@@ -1903,22 +1908,22 @@ class AccountsController(TransactionBase):
continue
ref_amt = flt(reference_details.get(item.get(item_ref_dn)), self.precision(based_on, item))
based_on_amt = flt(item.get(based_on))
if not ref_amt:
frappe.msgprint(
_("System will not check over billing since amount for Item {0} in {1} is zero").format(
item.item_code, ref_dt
),
title=_("Warning"),
indicator="orange",
)
if based_on_amt: # Skip warning for free items
frappe.msgprint(
_(
"System will not check over billing since amount for Item {0} in {1} is zero"
).format(item.item_code, ref_dt),
title=_("Warning"),
indicator="orange",
)
continue
already_billed = self.get_billed_amount_for_item(item, item_ref_dn, based_on)
total_billed_amt = flt(
flt(already_billed) + flt(item.get(based_on)), self.precision(based_on, item)
)
total_billed_amt = flt(flt(already_billed) + based_on_amt, self.precision(based_on, item))
allowance, item_allowance, global_qty_allowance, global_amount_allowance = get_allowance_for(
item.item_code, item_allowance, global_qty_allowance, global_amount_allowance, "amount"
@@ -2752,6 +2757,11 @@ class AccountsController(TransactionBase):
elif self.doctype == "Payment Entry":
self.make_advance_payment_ledger_for_payment()
def set_transaction_currency_and_rate_in_gl_map(self, gl_entries):
for x in gl_entries:
x["transaction_currency"] = self.currency
x["transaction_exchange_rate"] = self.get("conversion_rate") or 1
@frappe.whitelist()
def get_tax_rate(account_head):
@@ -3281,6 +3291,7 @@ def set_child_tax_template_and_map(item, child_item, parent_doc):
"posting_date": parent_doc.transaction_date,
"tax_category": parent_doc.get("tax_category"),
"company": parent_doc.get("company"),
"base_net_rate": item.get("base_net_rate"),
}
child_item.item_tax_template = _get_item_tax_template(args, item.taxes)

View File

@@ -333,7 +333,7 @@ class BuyingController(SubcontractingController):
net_rate
+ item.item_tax_amount
+ flt(item.landed_cost_voucher_amount)
+ flt(item.get("rate_difference_with_purchase_invoice"))
+ flt(item.get("amount_difference_with_purchase_invoice"))
) / qty_in_stock_uom
else:
item.valuation_rate = 0.0
@@ -779,8 +779,10 @@ class BuyingController(SubcontractingController):
is_plural = "s" if len(created_assets) != 1 else ""
messages.append(
_("Asset{} {assets_link} created for {}").format(
is_plural, frappe.bold(d.item_code), assets_link=assets_link
_("Asset{is_plural} {assets_link} created for {item_code}").format(
is_plural=is_plural,
assets_link=assets_link,
item_code=frappe.bold(d.item_code),
)
)
else:

View File

@@ -837,6 +837,7 @@ def get_tax_template(doctype, txt, searchfield, start, page_len, filters):
"posting_date": valid_from,
"tax_category": filters.get("tax_category"),
"company": company,
"base_net_rate": filters.get("base_net_rate"),
}
taxes = _get_item_tax_template(args, taxes, for_validate=True)

View File

@@ -25,9 +25,6 @@ def validate_return(doc):
if doc.return_against:
validate_return_against(doc)
if doc.doctype in ("Sales Invoice", "Purchase Invoice") and not doc.update_stock:
return
validate_returned_items(doc)
@@ -118,7 +115,7 @@ def validate_returned_items(doc):
elif doc.doctype == "Delivery Note":
key = (d.item_code, d.get("dn_detail"))
if d.item_code and (flt(d.qty) < 0 or flt(d.get("received_qty")) < 0):
if d.item_code and (flt(d.qty) <= 0 or flt(d.get("received_qty")) <= 0):
if key not in valid_items:
frappe.msgprint(
_("Row # {0}: Returned Item {1} does not exist in {2} {3}").format(
@@ -160,6 +157,9 @@ def validate_returned_items(doc):
def validate_quantity(doc, key, args, ref, valid_items, already_returned_items):
fields = ["stock_qty"]
if (doc.doctype == "Purchase Invoice" or doc.doctype == "Sales Invoice") and not doc.update_stock:
fields = ["qty"]
if doc.doctype in ["Purchase Receipt", "Purchase Invoice", "Subcontracting Receipt"]:
if not args.get("return_qty_from_rejected_warehouse"):
fields.extend(["received_qty", "rejected_qty"])
@@ -169,13 +169,16 @@ def validate_quantity(doc, key, args, ref, valid_items, already_returned_items):
already_returned_data = already_returned_items.get(key) or {}
company_currency = erpnext.get_company_currency(doc.company)
stock_qty_precision = get_field_precision(
frappe.get_meta(doc.doctype + " Item").get_field("stock_qty"), company_currency
field_precision = get_field_precision(
frappe.get_meta(doc.doctype + " Item").get_field(
"stock_qty" if doc.get("update_stock", "") else "qty"
),
company_currency,
)
for column in fields:
returned_qty = (
flt(already_returned_data.get(column, 0), stock_qty_precision)
flt(already_returned_data.get(column, 0), field_precision)
if len(already_returned_data) > 0
else 0
)
@@ -190,17 +193,17 @@ def validate_quantity(doc, key, args, ref, valid_items, already_returned_items):
reference_qty = ref.get(column) * ref.get("conversion_factor", 1.0)
current_stock_qty = args.get(column) * args.get("conversion_factor", 1.0)
max_returnable_qty = flt(flt(reference_qty, stock_qty_precision) - returned_qty, stock_qty_precision)
max_returnable_qty = flt(flt(reference_qty, field_precision) - returned_qty, field_precision)
label = column.replace("_", " ").title()
if reference_qty:
if flt(args.get(column)) > 0:
frappe.throw(_("{0} must be negative in return document").format(label))
elif returned_qty >= reference_qty and args.get(column):
elif returned_qty >= reference_qty and args.get(column) >= 0:
frappe.throw(
_("Item {0} has already been returned").format(args.item_code), StockOverReturnError
)
elif abs(flt(current_stock_qty, stock_qty_precision)) > max_returnable_qty:
elif abs(flt(current_stock_qty, field_precision)) > max_returnable_qty:
frappe.throw(
_("Row # {0}: Cannot return more than {1} for Item {2}").format(
args.idx, max_returnable_qty, args.item_code
@@ -258,7 +261,7 @@ def get_already_returned_items(doc):
field = (
frappe.scrub(doc.doctype) + "_item"
if doc.doctype in ["Purchase Invoice", "Purchase Receipt", "Sales Invoice"]
if doc.doctype in ["Purchase Invoice", "Purchase Receipt", "Sales Invoice", "POS Invoice"]
else "dn_detail"
)
data = frappe.db.sql(
@@ -770,6 +773,7 @@ def get_return_against_item_fields(voucher_type):
"Delivery Note": "dn_detail",
"Sales Invoice": "sales_invoice_item",
"Subcontracting Receipt": "subcontracting_receipt_item",
"POS Invoice": "sales_invoice_item",
}
return return_against_item_fields[voucher_type]

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