Compare commits

..

139 Commits

Author SHA1 Message Date
Frappe PR Bot
481610d400 chore(release): Bumped to Version 14.74.1
## [14.74.1](https://github.com/frappe/erpnext/compare/v14.74.0...v14.74.1) (2024-09-18)

### Bug Fixes

* add currency in options for rate field in pricing rule ([c9467f4](c9467f4c1f))
* cancel cost center allocation and journal entry after test ([4646f11](4646f11829))
* delete exchange gain loss journal entry while deleting payment entry ([62f1286](62f12861f1))
* fetch cost center allocation percentage only from the applicable allocation ([a26f0e9](a26f0e9973))
* hide and reset discount control on new POS order ([5434432](5434432cfc))
* invalid gp calculation ([9af2df3](9af2df3810))
2024-09-18 07:32:40 +00:00
ruthra kumar
829a42d679 Merge pull request #43255 from frappe/version-14-hotfix
chore: release v14
2024-09-18 13:01:22 +05:30
ruthra kumar
c442dc6b8e Merge pull request #43267 from frappe/mergify/bp/version-14-hotfix/pr-43239
fix: add currency in options for rate field in pricing rule (backport #43239)
2024-09-18 12:39:25 +05:30
ruthra kumar
fff8f8b83f chore: resolve conflict 2024-09-18 12:12:06 +05:30
krishna
c9467f4c1f fix: add currency in options for rate field in pricing rule
(cherry picked from commit 636c0131fa)

# Conflicts:
#	erpnext/accounts/doctype/pricing_rule/pricing_rule.json
2024-09-18 06:29:34 +00:00
ruthra kumar
eed7eba695 Merge pull request #43265 from frappe/mergify/bp/version-14-hotfix/pr-43216
fix: get cost center allocation percentage only from the applicable allocation (backport #43216)
2024-09-18 11:40:10 +05:30
venkat102
4646f11829 fix: cancel cost center allocation and journal entry after test
(cherry picked from commit 3c65b98b49)
2024-09-18 05:49:31 +00:00
venkat102
872c150ea7 test: add unit test for validating multiple cost center allocation with different child cost center
(cherry picked from commit 4d5d6150e1)
2024-09-18 05:49:30 +00:00
venkat102
a26f0e9973 fix: fetch cost center allocation percentage only from the applicable allocation
(cherry picked from commit 36e5945c66)
2024-09-18 05:49:30 +00:00
ruthra kumar
dc6d3cca70 Merge pull request #43248 from frappe/mergify/bp/version-14-hotfix/pr-43188
fix: invalid gp calculation (backport #43188)
2024-09-17 14:35:06 +05:30
Dany Robert
9af2df3810 fix: invalid gp calculation
(cherry picked from commit c79851239c)
2024-09-17 06:33:14 +00:00
Smit Vora
84447caac7 Merge pull request #43240 from frappe/mergify/bp/version-14-hotfix/pr-43176
fix: hide and reset discount control on new POS order (backport #43176)
2024-09-16 20:01:15 +05:30
ljain112
5434432cfc fix: hide and reset discount control on new POS order
(cherry picked from commit 5b0053f8dd)
2024-09-16 14:25:19 +00:00
ruthra kumar
2193395072 Merge pull request #43201 from frappe/mergify/bp/version-14-hotfix/pr-43191
fix: delete exchange gain loss journal entry while deleting payment entry (backport #43191)
2024-09-13 11:22:56 +05:30
ruthra kumar
73821ac647 Merge pull request #43203 from frappe/mergify/bp/version-14-hotfix/pr-43192
refactor(test): use test fixture on pricing rule test suite (backport #43192)
2024-09-13 11:22:29 +05:30
ruthra kumar
2e323cc126 refactor(test): use test fixture on pricing rule test suite
(cherry picked from commit 0ea1d6d960)
2024-09-13 05:20:23 +00:00
Navin-S-R
95a66b8d8f test: add unit test for deletion of gain loss jv while deleting payment entry
(cherry picked from commit 7855d3034b)
2024-09-13 05:19:04 +00:00
Navin-S-R
62f12861f1 fix: delete exchange gain loss journal entry while deleting payment entry
(cherry picked from commit 9886cf0d46)
2024-09-13 05:19:03 +00:00
ruthra kumar
77bdf18c23 Merge pull request #43170 from frappe/mergify/copy/version-14-hotfix/pr-43169
refactor: enable no-copy on Purchase Invoice status (copy #43169)
2024-09-11 14:20:38 +05:30
ruthra kumar
097eae478d refactor: enable no-copy on Purchase Invoice status
(cherry picked from commit 0d9741fdd7)
2024-09-11 07:56:59 +00:00
Frappe PR Bot
8f02b88119 chore(release): Bumped to Version 14.74.0
# [14.74.0](https://github.com/frappe/erpnext/compare/v14.73.2...v14.74.0) (2024-09-11)

### Bug Fixes

* `default_advance_account` field in Process Payment Reconciliation ([3691a50](3691a500eb))
* bom cost update is not working (backport [#43155](https://github.com/frappe/erpnext/issues/43155)) ([#43156](https://github.com/frappe/erpnext/issues/43156)) ([9110534](91105344bb))
* cancel common party advance jv while canceling the invoice ([f506952](f5069524f3))
* check multi-currency on jv for common party accounting with foreign currency ([a3a40fe](a3a40febf3))
* **Delivery Note:** translatability of validation errors ([5e33961](5e33961448))
* incorrect actual cost in Procurement Tracker report (backport [#43109](https://github.com/frappe/erpnext/issues/43109)) ([#43137](https://github.com/frappe/erpnext/issues/43137)) ([a4b9dda](a4b9dda4b2))
* incorrect qty after transaction in SLE (backport [#43103](https://github.com/frappe/erpnext/issues/43103)) ([#43104](https://github.com/frappe/erpnext/issues/43104)) ([77892f4](77892f4e24))
* return type of `get_party_details` (backport [#43131](https://github.com/frappe/erpnext/issues/43131)) ([#43135](https://github.com/frappe/erpnext/issues/43135)) ([e8aae50](e8aae5018a))
* set today in 'On This Date' in Available Batch Report ([36f65fc](36f65fc592))
* uncomment internal parties ([4dd06b6](4dd06b69a1))
* unreconcile allocation child table redirect url voucher no issue ([53496ed](53496ed79f))

### Features

* utility report to identify invalid ledger entries ([37b8715](37b8715096))
2024-09-11 05:11:21 +00:00
ruthra kumar
a91fb260e1 Merge pull request #43150 from frappe/version-14-hotfix
chore: release v14
2024-09-11 10:39:50 +05:30
mergify[bot]
91105344bb fix: bom cost update is not working (backport #43155) (#43156)
fix: bom cost update is not working (#43155)

(cherry picked from commit 05f9015c0b)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-09-10 23:07:52 +05:30
ruthra kumar
6895efe577 Merge pull request #43147 from frappe/mergify/bp/version-14-hotfix/pr-43144
feat: utility report for identifying invalid ledger entries (backport #43144)
2024-09-10 16:00:10 +05:30
ruthra kumar
2a890f9061 refactor: allow all accounts
(cherry picked from commit 43198c946b)
2024-09-10 09:06:14 +00:00
ruthra kumar
b2d361b495 refactor: fetch as dictionary
(cherry picked from commit 2126b10a92)
2024-09-10 09:06:14 +00:00
ruthra kumar
d51cf281c3 refactor: barebones methods with basic logic
(cherry picked from commit b05b378ef0)
2024-09-10 09:06:14 +00:00
ruthra kumar
3574d11946 refactor: standard filters
(cherry picked from commit dccbc1f432)
2024-09-10 09:06:14 +00:00
ruthra kumar
37b8715096 feat: utility report to identify invalid ledger entries
(cherry picked from commit 832c4aaf82)
2024-09-10 09:06:13 +00:00
mergify[bot]
e8aae5018a fix: return type of get_party_details (backport #43131) (#43135)
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
fix: return type of `get_party_details` (#43131)
2024-09-09 19:26:21 +02:00
mergify[bot]
a4b9dda4b2 fix: incorrect actual cost in Procurement Tracker report (backport #43109) (#43137)
fix: incorrect actual cost in Procurement Tracker report (#43109)

(cherry picked from commit 80f101f92e)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-09-09 20:35:14 +05:30
ruthra kumar
a5a0c0cb07 Merge pull request #43128 from frappe/mergify/bp/version-14-hotfix/pr-42801
fix(Delivery Note): translatability of validation errors (backport #42801)
2024-09-09 15:15:36 +05:30
barredterra
14ae3edc1d refactor: extract common validation method
(cherry picked from commit 08646b7ab7)
2024-09-09 09:14:01 +00:00
barredterra
5e33961448 fix(Delivery Note): translatability of validation errors
(cherry picked from commit 34df6e39dc)
2024-09-09 09:14:00 +00:00
ruthra kumar
2db15d7375 Merge pull request #43126 from frappe/mergify/bp/version-14-hotfix/pr-43051
fix: unreconcile allocation child table redirect url voucher no issue (backport #43051)
2024-09-09 14:34:11 +05:30
Prashant Kamble
53496ed79f fix: unreconcile allocation child table redirect url voucher no issue
(cherry picked from commit 5d6f6a2fb9)
2024-09-09 09:00:19 +00:00
ruthra kumar
4536e8bc47 Merge pull request #43124 from frappe/mergify/bp/version-14-hotfix/pr-43121
fix: set today in 'On This Date' in Available Batch Report (backport #43121)
2024-09-09 14:14:26 +05:30
Nihantra C. Patel
36f65fc592 fix: set today in 'On This Date' in Available Batch Report
(cherry picked from commit 9fd55e4c83)
2024-09-09 08:27:14 +00:00
ruthra kumar
c237f3c4c4 Merge pull request #43119 from frappe/mergify/bp/version-14-hotfix/pr-43095
fix: check multi-currency on jv for common party accounting with foreign currency (backport #43095)
2024-09-09 11:24:00 +05:30
ruthra kumar
cf78f9702c chore: resolve conflict 2024-09-09 11:01:54 +05:30
ruthra kumar
de0b8c07f6 refactor(test): use change_settings decorator
(cherry picked from commit ee94fb37c8)
2024-09-09 05:06:22 +00:00
venkat102
4dd06b69a1 fix: uncomment internal parties
(cherry picked from commit 454e18ad5f)
2024-09-09 05:06:21 +00:00
venkat102
b6f352a024 test: add unit test for common party with foreign currency
(cherry picked from commit 740a04a704)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
2024-09-09 05:06:21 +00:00
venkat102
a3a40febf3 fix: check multi-currency on jv for common party accounting with foreign currency
(cherry picked from commit 00938bfd4d)
2024-09-09 05:06:21 +00:00
Smit Vora
6bfc8a0152 Merge pull request #43091 from frappe/mergify/bp/version-14-hotfix/pr-43013
fix: `default_advance_account` field in Process Payment Reconciliation (backport #43013)
2024-09-09 08:00:21 +05:30
Smit Vora
5113b0063e chore: resolve conflicts with backport 2024-09-09 06:58:34 +05:30
Smit Vora
9e77a0245a chore: resolve conflicts with backport 2024-09-09 06:51:30 +05:30
Frappe PR Bot
7c37d4c71a chore(release): Bumped to Version 14.73.2
## [14.73.2](https://github.com/frappe/erpnext/compare/v14.73.1...v14.73.2) (2024-09-07)

### Bug Fixes

* incorrect qty after transaction in SLE (backport [#43103](https://github.com/frappe/erpnext/issues/43103)) ([#43104](https://github.com/frappe/erpnext/issues/43104)) ([f054261](f0542613e0))
2024-09-07 12:40:10 +00:00
rohitwaghchaure
873962a109 Merge pull request #43108 from frappe/mergify/bp/version-14/pr-43104
fix: incorrect qty after transaction in SLE (backport #43103) (backport #43104)
2024-09-07 18:08:45 +05:30
mergify[bot]
f0542613e0 fix: incorrect qty after transaction in SLE (backport #43103) (#43104)
fix: incorrect qty after transaction in SLE (#43103)

(cherry picked from commit 5ff87edc85)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
(cherry picked from commit 77892f4e24)
2024-09-07 12:05:10 +00:00
mergify[bot]
77892f4e24 fix: incorrect qty after transaction in SLE (backport #43103) (#43104)
fix: incorrect qty after transaction in SLE (#43103)

(cherry picked from commit 5ff87edc85)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-09-07 17:34:22 +05:30
ruthra kumar
4bd7ce3e0b Merge pull request #43096 from frappe/mergify/bp/version-14-hotfix/pr-43077
fix: cancel common party advance jv while canceling the invoice (backport #43077)
2024-09-06 20:48:51 +05:30
venkat102
e7b17e05b0 test: add unit test for canceling the common party advance jv created from sales invoice
(cherry picked from commit 8c6e3f3c12)
2024-09-06 14:50:05 +00:00
venkat102
f5069524f3 fix: cancel common party advance jv while canceling the invoice
(cherry picked from commit 6a928b92df)
2024-09-06 14:50:04 +00:00
ruthra kumar
202ebbe140 refactor: fetch advance account on party seleection
(cherry picked from commit c4ed04cb31)
2024-09-06 09:48:47 +00:00
ljain112
3691a500eb fix: default_advance_account field in Process Payment Reconciliation
(cherry picked from commit 143209f91a)

# Conflicts:
#	erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.json
#	erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.py
2024-09-06 09:48:47 +00:00
ruthra kumar
f4032b64c9 Merge pull request #43073 from frappe/mergify/bp/version-14-hotfix/pr-43070
fix: AP filter to simulate employee advance as a ledger impacting voucher (backport #43070)
2024-09-06 10:09:21 +05:30
ruthra kumar
2913785bac refactor: filter to toggle employee advance scenario in AP
(cherry picked from commit 257e13c299)
2024-09-05 11:44:02 +00:00
ruthra kumar
3a57ef4c5f refactor: Handle Emp Advance as separate row in AP report
(cherry picked from commit eedf22b07a)
2024-09-05 11:44:02 +00:00
Frappe PR Bot
610f74ca62 chore(release): Bumped to Version 14.73.1
## [14.73.1](https://github.com/frappe/erpnext/compare/v14.73.0...v14.73.1) (2024-09-04)

### Bug Fixes

* adjust price insertion logic for internal suppliers/customers ([#42988](https://github.com/frappe/erpnext/issues/42988)) ([3912c68](3912c688ac))
* default fiscal year in report v14 ([#42960](https://github.com/frappe/erpnext/issues/42960)) ([9db7f7b](9db7f7b033))
* Discount and taxes in return document should follow the reference document (backport [#41911](https://github.com/frappe/erpnext/issues/41911)) ([#42573](https://github.com/frappe/erpnext/issues/42573)) ([c55f8e3](c55f8e3388))
* indentation ([e8cfb4d](e8cfb4d602))
* removed extra filter condition ([db319f9](db319f95f1))
* resolve gl entries duplication in asset purchase workflow ([#41845](https://github.com/frappe/erpnext/issues/41845)) ([b3c9697](b3c9697b7c))
* retain date filter when redirecting in Profit and Loss report ([c87f2d2](c87f2d2f7c))
* set up filters for dimensions ([450b3f3](450b3f338e))
* typeerror on Payment Entry ([dda4002](dda4002da4))
* typerror on default_currency ([22b283d](22b283d4a8))
2024-09-04 14:59:27 +00:00
ruthra kumar
012248bd12 Merge pull request #43025 from frappe/version-14-hotfix
chore: release v14
2024-09-04 20:28:07 +05:30
Smit Vora
ed185a6171 Merge pull request #43045 from frappe/mergify/bp/version-14-hotfix/pr-42988
fix: adjust price insertion logic for internal suppliers/customers (backport #42988)
2024-09-04 13:19:09 +05:30
Lakshit Jain
3912c688ac fix: adjust price insertion logic for internal suppliers/customers (#42988)
* fix: adjust price insertion logic for internal suppliers/customers

* refactor: correct indentation, specify conditions within function

* fix: typo

---------

Co-authored-by: Smit Vora <smitvora203@gmail.com>
(cherry picked from commit 38f925b376)
2024-09-04 07:26:53 +00:00
ruthra kumar
c7fb6e5f61 Merge pull request #43032 from frappe/mergify/bp/version-14-hotfix/pr-43030
fix: typerror on default_currency (backport #43030)
2024-09-03 16:51:31 +05:30
ruthra kumar
22b283d4a8 fix: typerror on default_currency
(cherry picked from commit 4a7cc4da87)
2024-09-03 11:11:30 +00:00
ruthra kumar
56b4e79305 Merge pull request #43027 from frappe/mergify/bp/version-14-hotfix/pr-42974
fix: retain date filter when redirecting in Profit and Loss report (backport #42974)
2024-09-03 15:51:57 +05:30
vishnu
e8cfb4d602 fix: indentation
(cherry picked from commit 598e9c1390)
2024-09-03 10:14:32 +00:00
vishnu
c87f2d2f7c fix: retain date filter when redirecting in Profit and Loss report
(cherry picked from commit bb29fc4c3d)
2024-09-03 10:14:32 +00:00
Khushi Rawat
ec00b2cf48 Merge pull request #42965 from frappe/mergify/bp/version-14-hotfix/pr-41845
fix: resolve gl entries duplication in asset purchase workflow (backport #41845)
2024-09-02 16:35:19 +05:30
mergify[bot]
c55f8e3388 fix: Discount and taxes in return document should follow the reference document (backport #41911) (#42573)
* fix: Discount and taxes in return document should follow the reference document (#41911)

* fix: Discount and taxes in return document should follow the reference document

* fix: Ignore Pricing rule on debit/credit note if created against PI/SI with test cases

* fix: linter issue

(cherry picked from commit 281198456d)

# Conflicts:
#	erpnext/controllers/sales_and_purchase_return.py

* fix: resolved conflict

---------

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2024-09-02 11:12:22 +05:30
Khushi Rawat
4951d01102 Merge pull request #42766 from frappe/mergify/bp/version-14-hotfix/pr-42765
fix: set up filters for dimensions (backport #42765)
2024-08-30 13:43:51 +05:30
ruthra kumar
a2d5024d3f Merge pull request #42984 from frappe/mergify/bp/version-14-hotfix/pr-42983
refactor: link utility report with bank reconciliation statement (backport #42983)
2024-08-29 16:16:37 +05:30
ruthra kumar
ef7a83c354 refactor: link utility report with bank reconciliation statement
(cherry picked from commit 00eac65712)
2024-08-29 10:42:52 +00:00
ruthra kumar
810bc65e30 Merge pull request #42980 from frappe/mergify/bp/version-14-hotfix/pr-42979
refactor: better UX on Bank Clearance tool (backport #42979)
2024-08-29 14:15:47 +05:30
ruthra kumar
cfb5a6592c chore: resolve conflict 2024-08-29 14:11:28 +05:30
ruthra kumar
9afbd43f05 refactor: better UX on Bank Clearance tool
(cherry picked from commit 6a06e26d04)

# Conflicts:
#	erpnext/accounts/doctype/bank_clearance/bank_clearance.js
2024-08-29 08:25:09 +00:00
Khushi Rawat
6fda0e8e1b chore: linters check 2024-08-28 15:22:07 +05:30
Nicolas Pereira
9db7f7b033 fix: default fiscal year in report v14 (#42960)
* loads the fiscal year from the calendar year

* loads the fiscal year from the calendar year

* loads the fiscal year from the calendar year

* loads the fiscal year from the calendar year

* fix: default fiscal year in report v14

* fix: default fiscal year in report v14 --prettier

---------

Co-authored-by: Nihantra C. Patel <141945075+Nihantra-Patel@users.noreply.github.com>
2024-08-28 14:18:55 +05:30
ruthra kumar
82b2046805 Merge pull request #42967 from frappe/mergify/bp/version-14-hotfix/pr-42966
fix: typeerror on Payment Entry (backport #42966)
2024-08-28 13:27:24 +05:30
ruthra kumar
6cb9ae2035 chore: resolve conflict 2024-08-28 13:23:49 +05:30
ruthra kumar
dda4002da4 fix: typeerror on Payment Entry
(cherry picked from commit e9cf8937cd)

# Conflicts:
#	erpnext/accounts/doctype/payment_entry/payment_entry.js
2024-08-28 06:57:56 +00:00
Khushi Rawat
91bff147e0 chore: resolved conflicts 2024-08-28 12:05:25 +05:30
Khushi Rawat
b3c9697b7c fix: resolve gl entries duplication in asset purchase workflow (#41845)
* fix: resolve gl entries duplication in asset purchase workflow

* fix: prevent duplicate entry when creating purchase receipt from purchase invoice

* chore: test case added

* fix: fixed missing asset category issue

(cherry picked from commit 55a4bd469b)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
2024-08-28 06:29:56 +00:00
Frappe PR Bot
7648d8db80 chore(release): Bumped to Version 14.73.0
# [14.73.0](https://github.com/frappe/erpnext/compare/v14.72.2...v14.73.0) (2024-08-28)

### Bug Fixes

* call 'process' directly instead of creating 'process_subscripti ([8412938](8412938442))
* currency symbol on `amount` field suppose to show as `Company`'s currency not as default company's currency ([333c757](333c7577eb))
* do not copy date fields in opportunity doctype ([5186872](5186872b9b))
* german translations for incoterms ([003653c](003653c572))
* get amount with taxes and charges from payment entry ([094411f](094411f5f0))
* same posting date and time, creation causing incorrect balance qty (backport [#42904](https://github.com/frappe/erpnext/issues/42904)) ([#42919](https://github.com/frappe/erpnext/issues/42919)) ([c0b5f7c](c0b5f7c8eb))
* specify precision for net_amount ([#39481](https://github.com/frappe/erpnext/issues/39481)) ([cf34219](cf34219c71))
* stock reco with incorrect serial numbers ([#42865](https://github.com/frappe/erpnext/issues/42865)) ([54842ea](54842ea0b3))
* timeout while submitting stock entry (backport [#42929](https://github.com/frappe/erpnext/issues/42929)) ([#42930](https://github.com/frappe/erpnext/issues/42930)) ([73eab91](73eab91631))
* unsupported operand type(s) for *: 'float' and 'NoneType' (backport [#42916](https://github.com/frappe/erpnext/issues/42916)) ([#42917](https://github.com/frappe/erpnext/issues/42917)) ([2f4f2a8](2f4f2a8eec))
* update dimesions in exchange_gain_loss jv based on base document ([820dcd7](820dcd79bb))
* Update get_amount to return currency precision grand total ([0c2d7f2](0c2d7f2d9a))
* use of incorrect attribute ([ff14d72](ff14d72a46))

### Features

* report to identify incorrectly cleared cheques ([c29b95e](c29b95e306))

### Performance Improvements

* data import for stock entries (backport [#42711](https://github.com/frappe/erpnext/issues/42711)) ([#42903](https://github.com/frappe/erpnext/issues/42903)) ([12d5e24](12d5e247c5))
2024-08-28 05:02:39 +00:00
ruthra kumar
578f9b61c6 Merge pull request #42938 from frappe/version-14-hotfix
chore: release v14
2024-08-28 10:31:18 +05:30
ruthra kumar
23c5edaf08 Merge pull request #42788 from kurogeek/fix-share_transfer-amount-currency-symbol
fix: share_transfer amount currency symbol field
2024-08-28 10:05:19 +05:30
Raffael Meyer
35c10fa97f Merge pull request #42671 from frappe/mergify/bp/version-14-hotfix/pr-42669
fix: german translations for incoterms (backport #42669)
2024-08-27 16:54:22 +02:00
ruthra kumar
10025a2c8d Merge pull request #42946 from frappe/mergify/bp/version-14-hotfix/pr-42923
fix: get amount with taxes and charges from payment entry (backport #42923)
2024-08-27 17:39:22 +05:30
ruthra kumar
813b699d5e Merge pull request #42948 from frappe/mergify/bp/version-14-hotfix/pr-42936
fix: remove unnecessary condition on 'voucher_no' (backport #42936)
2024-08-27 17:09:06 +05:30
ruthra kumar
94b09bb836 Merge pull request #42945 from frappe/mergify/bp/version-14-hotfix/pr-42453
fix: Update get_amount to return currency precision grand total (backport #42453)
2024-08-27 17:07:24 +05:30
ruthra kumar
ff14d72a46 fix: use of incorrect attribute
(cherry picked from commit fb32d2cafb)
2024-08-27 11:05:50 +00:00
Syed Mujeer Hashmi
0c2d7f2d9a fix: Update get_amount to return currency precision grand total
In case of multi-currency purchase invoice, we are getting the error "Total Payment Request amount cannot be greater than Purchase Invoice amount" because of rounding difference.

(cherry picked from commit 976abf7b3c)
2024-08-27 16:31:56 +05:30
venkat102
094411f5f0 fix: get amount with taxes and charges from payment entry
(cherry picked from commit b3a901b631)
2024-08-27 11:01:27 +00:00
mergify[bot]
c0b5f7c8eb fix: same posting date and time, creation causing incorrect balance qty (backport #42904) (#42919)
* fix: same posting date and time, creation causing incorrect balance qty (#42904)

fix: same posting date and time, creation causing incorrect balance quantity
(cherry picked from commit 27364b7e6b)

# Conflicts:
#	erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
#	erpnext/stock/stock_ledger.py

* chore: fix conflicts

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-08-27 14:33:56 +05:30
mergify[bot]
73eab91631 fix: timeout while submitting stock entry (backport #42929) (#42930)
* fix: timeout while submitting stock entry (#42929)

(cherry picked from commit ca2fde891e)

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

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-08-27 14:29:41 +05:30
mergify[bot]
2f4f2a8eec fix: unsupported operand type(s) for *: 'float' and 'NoneType' (backport #42916) (#42917)
fix: unsupported operand type(s) for *: 'float' and 'NoneType' (#42916)

(cherry picked from commit 10434742e9)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-08-27 09:30:01 +05:30
ruthra kumar
6a2210e46a Merge pull request #42914 from frappe/mergify/bp/version-14-hotfix/pr-42887
fix: update dimesions in exchange_gain_loss jv based on base document (backport #42887)
2024-08-26 17:56:18 +05:30
ljain112
820dcd79bb fix: update dimesions in exchange_gain_loss jv based on base document
(cherry picked from commit 96df19149d)
2024-08-26 12:01:01 +00:00
ruthra kumar
97205ce355 Merge pull request #42906 from frappe/mergify/bp/version-14-hotfix/pr-42905
refactor: better err msg on clearance tool (backport #42905)
2024-08-26 16:34:06 +05:30
ruthra kumar
8207b12f11 Merge pull request #42911 from frappe/mergify/bp/version-14-hotfix/pr-42910
feat: utility report to better diagnose incorrectly cleared Cheques and Deposits (backport #42910)
2024-08-26 16:32:41 +05:30
ruthra kumar
09946c7ea7 refactor: build dictionary for Journal
remove redundant filter

(cherry picked from commit 2144e0337d)
2024-08-26 10:11:09 +00:00
ruthra kumar
15aeec8a2d chore: remove redundant column
(cherry picked from commit 74b36db24e)
2024-08-26 10:11:09 +00:00
ruthra kumar
29fe7bea6d refactor: build dict for payment entry
(cherry picked from commit 784dec24c8)
2024-08-26 10:11:09 +00:00
ruthra kumar
c58987ba38 refactor: working state with minimum functions
(cherry picked from commit 4cd023444a)
2024-08-26 10:11:09 +00:00
ruthra kumar
ef93c96f43 refactor: barebones functions
(cherry picked from commit ceaa1be729)
2024-08-26 10:11:08 +00:00
ruthra kumar
c29b95e306 feat: report to identify incorrectly cleared cheques
(cherry picked from commit 28890fa833)
2024-08-26 10:11:08 +00:00
ruthra kumar
e6438b293d chore: resolve conflict 2024-08-26 15:30:53 +05:30
ruthra kumar
18a75fef9a refactor: better err msg on clearance tool
(cherry picked from commit 092411b54f)

# Conflicts:
#	erpnext/accounts/doctype/bank_clearance/bank_clearance.py
2024-08-26 08:55:01 +00:00
mergify[bot]
12d5e247c5 perf: data import for stock entries (backport #42711) (#42903)
* perf: data import for stock entries (#42711)

(cherry picked from commit 1511280464)

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

* chore: fix conflicts

* chore: fix linters issue

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-08-26 13:53:23 +05:30
Smit Vora
72cc42997b Merge pull request #42891 from frappe/mergify/bp/version-14-hotfix/pr-42848
fix: do not copy date fields in opportunity doctype (backport #42848)
2024-08-24 13:12:27 +05:30
Smit Vora
3a02800fbc chore: resolve conflicts 2024-08-24 12:38:11 +05:30
ruthra kumar
6cf248578f Merge pull request #42817 from frappe/mergify/bp/version-14-hotfix/pr-39481
fix: specify precision for net_amount (backport #39481)
2024-08-24 07:53:43 +05:30
ljain112
5186872b9b fix: do not copy date fields in opportunity doctype
(cherry picked from commit 74afa57a9f)

# Conflicts:
#	erpnext/crm/doctype/opportunity/opportunity.json
2024-08-23 08:48:29 +00:00
rohitwaghchaure
54842ea0b3 fix: stock reco with incorrect serial numbers (#42865) 2024-08-22 19:33:44 +05:30
ruthra kumar
a95a0524d9 Merge pull request #42870 from frappe/mergify/bp/version-14-hotfix/pr-42867
fix: call 'process' directly instead of creating 'process_subscription' (backport #42867)
2024-08-22 17:51:25 +05:30
ruthra kumar
dffd5f291d Merge pull request #42868 from frappe/mergify/bp/version-14-hotfix/pr-42851
refactor: Allow equity type Account in Payment Entry for shareholders (backport #42851)
2024-08-22 17:50:51 +05:30
ruthra kumar
164b417136 chore: resolve conflict 2024-08-22 17:34:31 +05:30
ruthra kumar
8412938442 fix: call 'process' directly instead of creating 'process_subscripti
reason: 'process' follows simple DB transaction model.
(cherry picked from commit b4d22c2936)
2024-08-22 11:53:32 +00:00
ruthra kumar
30ad25d86c refactor: filter shareholder on company
(cherry picked from commit 63ad9f4f86)

# Conflicts:
#	erpnext/accounts/doctype/payment_entry/payment_entry.js
2024-08-22 11:40:37 +00:00
ruthra kumar
651e853156 refactor: allow equity types on Payment Entry
(cherry picked from commit 6cbf98294a)

# Conflicts:
#	erpnext/accounts/doctype/payment_entry/payment_entry.js
2024-08-22 11:40:37 +00:00
ruthra kumar
c2fdef4d5b refactor(test): update grand total 2024-08-21 17:36:11 +05:30
ruthra kumar
678f08898a refactor(test): update tast case of rounding value 2024-08-21 15:26:47 +05:30
Frappe PR Bot
c79088f46a chore(release): Bumped to Version 14.72.2
## [14.72.2](https://github.com/frappe/erpnext/compare/v14.72.1...v14.72.2) (2024-08-21)

### Bug Fixes

* disable rename from warehouse ([ed568fb](ed568fb01d))
* disable rename from warehouse ([a520d06](a520d06c3f))
* **patch:** replace repost with direct sql to update 'against_voucher ([c98ab15](c98ab15a44))
2024-08-21 05:21:14 +00:00
ruthra kumar
d5896cd8a0 Merge pull request #42833 from frappe/version-14-hotfix
chore: release v14
2024-08-21 10:49:56 +05:30
ruthra kumar
c5dedab304 chore: resolve conflicts 2024-08-19 13:53:29 +05:30
Dany Robert
cf34219c71 fix: specify precision for net_amount (#39481)
* fix: specify precision for net_amount

* fix: correct existing test to account for precision

* fix: rounding issue in test cases

* fix: optional grand total manipulation

* fix: use `grand_total_diff` for manipulation

* fix: patch to set default for grand total manipulation

* fix: wrong rounding assertion for USD

* fix: undefined this.frm error

* chore: linters

* fix: `net_amount` percision and method rename

* fix: missing frm reference

* chore: minor cleanups and depr message

* refactor: remove optional adjusting of grand total

(cherry picked from commit 50d56db0c2)

# Conflicts:
#	erpnext/public/js/controllers/accounts.js
#	erpnext/public/js/controllers/taxes_and_totals.js
2024-08-19 04:36:52 +00:00
ruthra kumar
752f93c630 Merge pull request #42793 from frappe/mergify/bp/version-14-hotfix/pr-42791
fix(patch): Use sql to update 'against_voucher' rather than reposting (backport #42791)
2024-08-16 15:55:22 +05:30
ruthra kumar
d14e386e18 chore: resolve conflict 2024-08-16 15:34:48 +05:30
ruthra kumar
9f687eee4e chore: update patches.txt
(cherry picked from commit 1721175a20)

# Conflicts:
#	erpnext/patches.txt
2024-08-16 10:02:34 +00:00
ruthra kumar
c98ab15a44 fix(patch): replace repost with direct sql to update 'against_voucher
(cherry picked from commit 13bb48434f)
2024-08-16 10:02:33 +00:00
kurogeek
333c7577eb fix: currency symbol on amount field suppose to show as Company's currency not as default company's currency 2024-08-16 13:52:54 +07:00
Khushi Rawat
ea7c82c6a3 chore: resolved conflicts 2024-08-15 13:12:38 +05:30
Nihantra C. Patel
54815b44e7 Merge pull request #42770 from frappe/mergify/bp/version-14-hotfix/pr-42769
fix: disable rename from warehouse (backport #42769)
2024-08-14 17:15:39 +05:30
Nihantra C. Patel
ed568fb01d fix: disable rename from warehouse 2024-08-14 16:35:34 +05:30
Nihantra C. Patel
a520d06c3f fix: disable rename from warehouse
(cherry picked from commit c1812f74e8)

# Conflicts:
#	erpnext/stock/doctype/warehouse/warehouse.json
2024-08-14 11:03:41 +00:00
Khushi Rawat
47a9325177 chore: linters check
(cherry picked from commit 0d42793397)

# Conflicts:
#	erpnext/assets/doctype/asset_capitalization/asset_capitalization.js
2024-08-14 09:07:34 +00:00
Khushi Rawat
db319f95f1 fix: removed extra filter condition
(cherry picked from commit 6fa29376a0)
2024-08-14 09:07:34 +00:00
Khushi Rawat
450b3f338e fix: set up filters for dimensions
(cherry picked from commit 2fd8de2f76)

# Conflicts:
#	erpnext/assets/doctype/asset_capitalization/asset_capitalization.js
2024-08-14 09:07:34 +00:00
barredterra
003653c572 fix: german translations for incoterms
(cherry picked from commit 9789648175)
2024-08-07 18:54:36 +00:00
69 changed files with 1443 additions and 319 deletions

View File

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

View File

@@ -469,7 +469,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2023-11-20 09:37:47.650347",
"modified": "2024-01-22 12:10:10.151819",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Settings",

View File

@@ -36,8 +36,12 @@ frappe.ui.form.on("Bank Clearance", {
refresh: function (frm) {
frm.disable_save();
frm.add_custom_button(__("Get Payment Entries"), () => frm.trigger("get_payment_entries"));
frm.change_custom_button_type("Get Payment Entries", null, "primary");
frm.change_custom_button_type(__("Get Payment Entries"), null, "primary");
if (frm.doc.payment_entries.length) {
frm.add_custom_button(__("Update Clearance Date"), () => frm.trigger("update_clearance_date"));
frm.change_custom_button_type(__("Get Payment Entries"), null, "default");
frm.change_custom_button_type(__("Update Clearance Date"), null, "primary");
}
},
update_clearance_date: function (frm) {
@@ -45,13 +49,7 @@ frappe.ui.form.on("Bank Clearance", {
method: "update_clearance_date",
doc: frm.doc,
callback: function (r, rt) {
frm.refresh_field("payment_entries");
frm.refresh_fields();
if (!frm.doc.payment_entries.length) {
frm.change_custom_button_type("Get Payment Entries", null, "primary");
frm.change_custom_button_type("Update Clearance Date", null, "default");
}
frm.refresh();
},
});
},
@@ -60,17 +58,8 @@ frappe.ui.form.on("Bank Clearance", {
return frappe.call({
method: "get_payment_entries",
doc: frm.doc,
callback: function (r, rt) {
frm.refresh_field("payment_entries");
if (frm.doc.payment_entries.length) {
frm.add_custom_button(__("Update Clearance Date"), () =>
frm.trigger("update_clearance_date")
);
frm.change_custom_button_type("Get Payment Entries", null, "default");
frm.change_custom_button_type("Update Clearance Date", null, "primary");
}
callback: function () {
frm.refresh();
},
});
},

View File

@@ -6,7 +6,7 @@ import frappe
from frappe import _, msgprint
from frappe.model.document import Document
from frappe.query_builder.custom import ConstantColumn
from frappe.utils import flt, fmt_money, getdate
from frappe.utils import flt, fmt_money, get_link_to_form, getdate
import erpnext
@@ -210,8 +210,11 @@ class BankClearance(Document):
if d.cheque_date and getdate(d.clearance_date) < getdate(d.cheque_date):
frappe.throw(
_("Row #{0}: Clearance date {1} cannot be before Cheque Date {2}").format(
d.idx, d.clearance_date, d.cheque_date
_("Row #{0}: For {1} Clearance date {2} cannot be before Cheque Date {3}").format(
d.idx,
get_link_to_form(d.payment_document, d.payment_entry),
d.clearance_date,
d.cheque_date,
)
)

View File

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

View File

@@ -23,6 +23,11 @@ frappe.ui.form.on('Payment Entry', {
var account_types = ["Pay", "Internal Transfer"].includes(frm.doc.payment_type) ?
["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]];
if (frm.doc.party_type == "Shareholder") {
account_types.push("Equity");
}
return {
filters: {
"account_type": ["in", account_types],
@@ -77,6 +82,9 @@ frappe.ui.form.on('Payment Entry', {
var account_types = ["Receive", "Internal Transfer"].includes(frm.doc.payment_type) ?
["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]];
if (frm.doc.party_type == "Shareholder") {
account_types.push("Equity");
}
return {
filters: {
"account_type": ["in", account_types],
@@ -242,7 +250,7 @@ frappe.ui.form.on('Payment Entry', {
},
set_dynamic_labels: function(frm) {
var company_currency = frm.doc.company? frappe.get_doc(":Company", frm.doc.company).default_currency: "";
var company_currency = frm.doc.company? frappe.get_doc(":Company", frm.doc.company)?.default_currency: "";
frm.set_currency_labels(["base_paid_amount", "base_received_amount", "base_total_allocated_amount",
"difference_amount", "base_paid_amount_after_tax", "base_received_amount_after_tax",
@@ -326,6 +334,12 @@ frappe.ui.form.on('Payment Entry', {
return {
query: "erpnext.controllers.queries.customer_query"
}
} else if (frm.doc.party_type == "Shareholder") {
return {
filters: {
company: frm.doc.company,
},
};
}
});
@@ -503,7 +517,7 @@ frappe.ui.form.on('Payment Entry', {
frm.set_value("source_exchange_rate", 1);
} else if (frm.doc.paid_from){
if (["Internal Transfer", "Pay"].includes(frm.doc.payment_type)) {
let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
let company_currency = frappe.get_doc(":Company", frm.doc.company)?.default_currency;
frappe.call({
method: "erpnext.setup.utils.get_exchange_rate",
args: {

View File

@@ -1563,7 +1563,7 @@ def get_outstanding_reference_documents(args):
d["bill_no"] = frappe.db.get_value(d.voucher_type, d.voucher_no, "bill_no")
# Get negative outstanding sales /purchase invoices
if args.get("party_type") != "Employee" and not args.get("voucher_no"):
if args.get("party_type") != "Employee":
negative_outstanding_invoices = get_negative_outstanding_invoices(
args.get("party_type"),
args.get("party"),

View File

@@ -1365,6 +1365,79 @@ class TestPaymentEntry(FrappeTestCase):
expected_out_str = json.dumps(sorted(expected_pl_entries, key=json.dumps))
self.assertEqual(out_str, expected_out_str)
@change_settings("Accounts Settings", {"delete_linked_ledger_entries": 1})
def test_delete_linked_exchange_gain_loss_journal(self):
from erpnext.accounts.doctype.account.test_account import create_account
from erpnext.accounts.doctype.opening_invoice_creation_tool.test_opening_invoice_creation_tool import (
make_customer,
)
debtors = create_account(
account_name="Debtors USD",
parent_account="Accounts Receivable - _TC",
company="_Test Company",
account_currency="USD",
account_type="Receivable",
)
# create a customer
customer = make_customer(customer="_Test Party USD")
cust_doc = frappe.get_doc("Customer", customer)
cust_doc.default_currency = "USD"
test_account_details = {
"company": "_Test Company",
"account": debtors,
}
cust_doc.append("accounts", test_account_details)
cust_doc.save()
# create a sales invoice
si = create_sales_invoice(
customer=customer,
currency="USD",
conversion_rate=83.970000000,
debit_to=debtors,
do_not_save=1,
)
si.party_account_currency = "USD"
si.save()
si.submit()
# create a payment entry for the invoice
pe = get_payment_entry("Sales Invoice", si.name)
pe.reference_no = "1"
pe.reference_date = frappe.utils.nowdate()
pe.paid_amount = 100
pe.source_exchange_rate = 90
pe.append(
"deductions",
{
"account": "_Test Exchange Gain/Loss - _TC",
"cost_center": "_Test Cost Center - _TC",
"amount": 2710,
},
)
pe.save()
pe.submit()
# check creation of journal entry
jv = frappe.get_all(
"Journal Entry Account",
{"reference_type": pe.doctype, "reference_name": pe.name, "docstatus": 1},
pluck="parent",
)
self.assertTrue(jv)
# check cancellation of payment entry and journal entry
pe.cancel()
self.assertTrue(pe.docstatus == 2)
self.assertTrue(frappe.db.get_value("Journal Entry", {"name": jv[0]}, "docstatus") == 2)
# check deletion of payment entry and journal entry
pe.delete()
self.assertRaises(frappe.DoesNotExistError, frappe.get_doc, pe.doctype, pe.name)
self.assertRaises(frappe.DoesNotExistError, frappe.get_doc, "Journal Entry", jv[0])
def create_payment_entry(**args):
payment_entry = frappe.new_doc("Payment Entry")

View File

@@ -19,7 +19,7 @@ from erpnext.accounts.doctype.payment_entry.payment_entry import (
)
from erpnext.accounts.doctype.subscription_plan.subscription_plan import get_plan_rate
from erpnext.accounts.party import get_party_account, get_party_bank_account
from erpnext.accounts.utils import get_account_currency
from erpnext.accounts.utils import get_account_currency, get_currency_precision
from erpnext.erpnext_integrations.stripe_integration import create_stripe_subscription
from erpnext.utilities import payment_app_import_guard
@@ -520,7 +520,7 @@ def get_amount(ref_doc, payment_account=None):
grand_total = ref_doc.outstanding_amount
if grand_total > 0:
return grand_total
return flt(grand_total, get_currency_precision())
else:
frappe.throw(_("Payment Entry is already created"))

View File

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

View File

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

View File

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

View File

@@ -5,14 +5,17 @@
import unittest
import frappe
from frappe.tests.utils import FrappeTestCase, change_settings
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.controllers.sales_and_purchase_return import make_return_doc
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
from erpnext.stock.doctype.item.test_item import make_item
from erpnext.stock.get_item_details import get_item_details
class TestPricingRule(unittest.TestCase):
class TestPricingRule(FrappeTestCase):
def setUp(self):
delete_existing_pricing_rules()
setup_pricing_rule_data()
@@ -1099,6 +1102,69 @@ class TestPricingRule(unittest.TestCase):
pricing_rule.is_recursive = True
self.assertRaises(frappe.ValidationError, pricing_rule.save)
def test_ignore_pricing_rule_for_credit_note(self):
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule")
pricing_rule = make_pricing_rule(
discount_percentage=20,
selling=1,
buying=1,
priority=1,
title="_Test Pricing Rule",
)
si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", qty=1)
item = si.items[0]
si.submit()
self.assertEqual(item.discount_percentage, 20)
self.assertEqual(item.rate, 80)
# change discount on pricing rule
pricing_rule.discount_percentage = 30
pricing_rule.save()
credit_note = make_return_doc(si.doctype, si.name)
credit_note.save()
self.assertEqual(credit_note.ignore_pricing_rule, 1)
self.assertEqual(credit_note.pricing_rules, [])
self.assertEqual(credit_note.items[0].discount_percentage, 20)
self.assertEqual(credit_note.items[0].rate, 80)
self.assertEqual(credit_note.items[0].pricing_rules, None)
credit_note.delete()
si.cancel()
def test_ignore_pricing_rule_for_debit_note(self):
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule")
pricing_rule = make_pricing_rule(
discount_percentage=20,
buying=1,
priority=1,
title="_Test Pricing Rule",
)
pi = make_purchase_invoice(do_not_submit=True, supplier="_Test Supplier 1", qty=1)
item = pi.items[0]
pi.submit()
self.assertEqual(item.discount_percentage, 20)
self.assertEqual(item.rate, 40)
# change discount on pricing rule
pricing_rule.discount_percentage = 30
pricing_rule.save()
# create debit note from purchase invoice
debit_note = make_return_doc(pi.doctype, pi.name)
debit_note.save()
self.assertEqual(debit_note.ignore_pricing_rule, 1)
self.assertEqual(debit_note.pricing_rules, [])
self.assertEqual(debit_note.items[0].discount_percentage, 20)
self.assertEqual(debit_note.items[0].rate, 40)
self.assertEqual(debit_note.items[0].pricing_rules, None)
debit_note.delete()
pi.cancel()
test_dependencies = ["Campaign"]

View File

@@ -485,7 +485,7 @@ def get_qty_and_rate_for_other_item(doc, pr_doc, pricing_rules, row_item):
continue
stock_qty = row.get("qty") * (row.get("conversion_factor") or 1.0)
amount = stock_qty * (row.get("price_list_rate") or row.get("rate"))
amount = stock_qty * (flt(row.get("price_list_rate")) or flt(row.get("rate")))
pricing_rules = filter_pricing_rules_for_qty_amount(stock_qty, amount, pricing_rules, row)
if pricing_rules and pricing_rules[0]:

View File

@@ -20,6 +20,17 @@ frappe.ui.form.on("Process Payment Reconciliation", {
},
};
});
frm.set_query("default_advance_account", function (doc) {
return {
filters: {
company: doc.company,
is_group: 0,
account_type: doc.party_type == "Customer" ? "Receivable" : "Payable",
root_type: doc.party_type == "Customer" ? "Liability" : "Asset",
},
};
});
frm.set_query("cost_center", function (doc) {
return {
filters: {
@@ -102,6 +113,7 @@ frappe.ui.form.on("Process Payment Reconciliation", {
company(frm) {
frm.set_value("party", "");
frm.set_value("receivable_payable_account", "");
frm.set_value("default_advance_account", "");
},
party_type(frm) {
frm.set_value("party", "");
@@ -109,6 +121,7 @@ frappe.ui.form.on("Process Payment Reconciliation", {
party(frm) {
frm.set_value("receivable_payable_account", "");
frm.set_value("default_advance_account", "");
if (!frm.doc.receivable_payable_account && frm.doc.party_type && frm.doc.party) {
return frappe.call({
method: "erpnext.accounts.party.get_party_account",
@@ -116,10 +129,16 @@ frappe.ui.form.on("Process Payment Reconciliation", {
company: frm.doc.company,
party_type: frm.doc.party_type,
party: frm.doc.party,
include_advance: 1,
},
callback: (r) => {
if (!r.exc && r.message) {
frm.set_value("receivable_payable_account", r.message);
if (typeof r.message === "string") {
frm.set_value("receivable_payable_account", r.message);
} else if (Array.isArray(r.message)) {
frm.set_value("receivable_payable_account", r.message[0]);
frm.set_value("default_advance_account", r.message[1]);
}
}
frm.refresh();
},

View File

@@ -13,6 +13,7 @@
"column_break_io6c",
"party",
"receivable_payable_account",
"default_advance_account",
"filter_section",
"from_invoice_date",
"to_invoice_date",
@@ -141,12 +142,23 @@
{
"fieldname": "section_break_a8yx",
"fieldtype": "Section Break"
},
{
"depends_on": "eval:doc.party",
"description": "Only 'Payment Entries' made against this advance account are supported.",
"documentation_url": "https://docs.erpnext.com/docs/user/manual/en/advance-in-separate-party-account",
"fieldname": "default_advance_account",
"fieldtype": "Link",
"label": "Default Advance Account",
"mandatory_depends_on": "doc.party_type",
"options": "Account",
"reqd": 1
}
],
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2023-08-11 10:56:51.699137",
"modified": "2024-08-27 14:48:56.715320",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Process Payment Reconciliation",
@@ -180,4 +192,4 @@
"sort_order": "DESC",
"states": [],
"title_field": "company"
}
}

View File

@@ -76,6 +76,7 @@ def get_pr_instance(doc: str):
"party_type",
"party",
"receivable_payable_account",
"default_advance_account",
"from_invoice_date",
"to_invoice_date",
"from_payment_date",

View File

@@ -1265,6 +1265,7 @@
"fieldtype": "Select",
"in_standard_filter": 1,
"label": "Status",
"no_copy": 1,
"options": "\nDraft\nReturn\nDebit Note Issued\nSubmitted\nPaid\nPartly Paid\nUnpaid\nOverdue\nCancelled\nInternal Transfer",
"print_hide": 1
},
@@ -1610,7 +1611,7 @@
"idx": 204,
"is_submittable": 1,
"links": [],
"modified": "2024-07-25 19:42:36.931278",
"modified": "2024-09-11 12:59:19.130593",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice",

View File

@@ -369,6 +369,21 @@ class PurchaseInvoice(BuyingController):
item.expense_account = stock_not_billed_account
elif item.is_fixed_asset:
account = None
if not item.pr_detail and item.po_detail:
receipt_item = frappe.get_cached_value(
"Purchase Receipt Item",
{
"purchase_order": item.purchase_order,
"purchase_order_item": item.po_detail,
"docstatus": 1,
},
["name", "parent"],
as_dict=1,
)
if receipt_item:
item.pr_detail = receipt_item.name
item.purchase_receipt = receipt_item.parent
if item.pr_detail:
if not self.asset_received_but_not_billed:
self.asset_received_but_not_billed = self.get_company_default(

View File

@@ -10,7 +10,10 @@ import erpnext
from erpnext.accounts.doctype.account.test_account import create_account, get_inventory_account
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
from erpnext.buying.doctype.purchase_order.purchase_order import get_mapped_purchase_invoice
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_invoice as make_pi_from_po
from erpnext.buying.doctype.purchase_order.test_purchase_order import (
create_purchase_order,
)
from erpnext.buying.doctype.supplier.test_supplier import create_supplier
from erpnext.controllers.accounts_controller import get_payment_terms
from erpnext.controllers.buying_controller import QtyMismatchError

View File

@@ -307,7 +307,8 @@ class TestSalesInvoice(FrappeTestCase):
si.insert()
# with inclusive tax
self.assertEqual(si.items[0].net_amount, 3947.368421052631)
self.assertEqual(si.items[0].net_amount, 3947.37)
self.assertEqual(si.net_total, si.base_net_total)
self.assertEqual(si.net_total, 3947.37)
self.assertEqual(si.grand_total, 5000)
@@ -414,8 +415,8 @@ class TestSalesInvoice(FrappeTestCase):
for i, k in enumerate(expected_values["keys"]):
self.assertEqual(d.get(k), expected_values[d.account_head][i])
self.assertEqual(si.base_grand_total, 1500)
self.assertEqual(si.grand_total, 1500)
self.assertEqual(si.base_grand_total, 1500.01)
self.assertEqual(si.grand_total, 1500.01)
self.assertEqual(si.rounding_adjustment, -0.01)
def test_discount_amount_gl_entry(self):
@@ -651,7 +652,7 @@ class TestSalesInvoice(FrappeTestCase):
62.5,
625.0,
50,
499.97600115194473,
499.98,
],
"_Test Item Home Desktop 200": [
190.66,
@@ -662,7 +663,7 @@ class TestSalesInvoice(FrappeTestCase):
190.66,
953.3,
150,
749.9968530500239,
750,
],
}
@@ -675,20 +676,21 @@ class TestSalesInvoice(FrappeTestCase):
self.assertEqual(d.get(k), expected_values[d.item_code][i])
# check net total
self.assertEqual(si.net_total, 1249.97)
self.assertEqual(si.base_net_total, si.net_total)
self.assertEqual(si.net_total, 1249.98)
self.assertEqual(si.total, 1578.3)
# check tax calculation
expected_values = {
"keys": ["tax_amount", "total"],
"_Test Account Excise Duty - _TC": [140, 1389.97],
"_Test Account Education Cess - _TC": [2.8, 1392.77],
"_Test Account S&H Education Cess - _TC": [1.4, 1394.17],
"_Test Account CST - _TC": [27.88, 1422.05],
"_Test Account VAT - _TC": [156.25, 1578.30],
"_Test Account Customs Duty - _TC": [125, 1703.30],
"_Test Account Shipping Charges - _TC": [100, 1803.30],
"_Test Account Discount - _TC": [-180.33, 1622.97],
"_Test Account Excise Duty - _TC": [140, 1389.98],
"_Test Account Education Cess - _TC": [2.8, 1392.78],
"_Test Account S&H Education Cess - _TC": [1.4, 1394.18],
"_Test Account CST - _TC": [27.88, 1422.06],
"_Test Account VAT - _TC": [156.25, 1578.31],
"_Test Account Customs Duty - _TC": [125, 1703.31],
"_Test Account Shipping Charges - _TC": [100, 1803.31],
"_Test Account Discount - _TC": [-180.33, 1622.98],
}
for d in si.get("taxes"):
@@ -724,7 +726,7 @@ class TestSalesInvoice(FrappeTestCase):
"base_rate": 2500,
"base_amount": 25000,
"net_rate": 40,
"net_amount": 399.9808009215558,
"net_amount": 399.98,
"base_net_rate": 2000,
"base_net_amount": 19999,
},
@@ -738,7 +740,7 @@ class TestSalesInvoice(FrappeTestCase):
"base_rate": 7500,
"base_amount": 37500,
"net_rate": 118.01,
"net_amount": 590.0531205155963,
"net_amount": 590.05,
"base_net_rate": 5900.5,
"base_net_amount": 29502.5,
},
@@ -776,8 +778,13 @@ class TestSalesInvoice(FrappeTestCase):
self.assertEqual(si.base_grand_total, 60795)
self.assertEqual(si.grand_total, 1215.90)
self.assertEqual(si.rounding_adjustment, 0.01)
self.assertEqual(si.base_rounding_adjustment, 0.50)
# no rounding adjustment as the Smallest Currency Fraction Value of USD is 0.01
if frappe.db.get_value("Currency", "USD", "smallest_currency_fraction_value") < 0.01:
self.assertEqual(si.rounding_adjustment, 0.10)
self.assertEqual(si.base_rounding_adjustment, 5.0)
else:
self.assertEqual(si.rounding_adjustment, 0.0)
self.assertEqual(si.base_rounding_adjustment, 0.0)
def test_outstanding(self):
w = self.make()
@@ -2099,7 +2106,7 @@ class TestSalesInvoice(FrappeTestCase):
self.assertEqual(si.net_total, 19453.13)
self.assertEqual(si.grand_total, 24900)
self.assertEqual(si.total_taxes_and_charges, 5446.88)
self.assertEqual(si.rounding_adjustment, -0.01)
self.assertEqual(si.rounding_adjustment, 0.00)
expected_values = dict(
(d[0], d)
@@ -2126,7 +2133,7 @@ class TestSalesInvoice(FrappeTestCase):
def test_rounding_adjustment_2(self):
si = create_sales_invoice(rate=400, do_not_save=True)
for rate in [400, 600, 100]:
for rate in [400.25, 600.30, 100.65]:
si.append(
"items",
{
@@ -2152,17 +2159,18 @@ class TestSalesInvoice(FrappeTestCase):
)
si.save()
si.submit()
self.assertEqual(si.net_total, 1271.19)
self.assertEqual(si.grand_total, 1500)
self.assertEqual(si.total_taxes_and_charges, 228.82)
self.assertEqual(si.rounding_adjustment, -0.01)
self.assertEqual(si.net_total, si.base_net_total)
self.assertEqual(si.net_total, 1272.20)
self.assertEqual(si.grand_total, 1501.20)
self.assertEqual(si.total_taxes_and_charges, 229)
self.assertEqual(si.rounding_adjustment, -0.20)
expected_values = [
["_Test Account Service Tax - _TC", 0.0, 114.41],
["_Test Account VAT - _TC", 0.0, 114.41],
[si.debit_to, 1500, 0.0],
["Round Off - _TC", 0.01, 0.01],
["Sales - _TC", 0.0, 1271.18],
["_Test Account Service Tax - _TC", 0.0, 114.50],
["_Test Account VAT - _TC", 0.0, 114.50],
[si.debit_to, 1501, 0.0],
["Round Off - _TC", 0.20, 0.0],
["Sales - _TC", 0.0, 1272.20],
]
gl_entries = frappe.db.sql(
@@ -2220,7 +2228,8 @@ class TestSalesInvoice(FrappeTestCase):
si.save()
si.submit()
self.assertEqual(si.net_total, 4007.16)
self.assertEqual(si.net_total, si.base_net_total)
self.assertEqual(si.net_total, 4007.15)
self.assertEqual(si.grand_total, 4488.02)
self.assertEqual(si.total_taxes_and_charges, 480.86)
self.assertEqual(si.rounding_adjustment, -0.02)
@@ -2232,7 +2241,7 @@ class TestSalesInvoice(FrappeTestCase):
["_Test Account Service Tax - _TC", 0.0, 240.43],
["_Test Account VAT - _TC", 0.0, 240.43],
["Sales - _TC", 0.0, 4007.15],
["Round Off - _TC", 0.02, 0.01],
["Round Off - _TC", 0.01, 0.0],
]
)
@@ -3104,6 +3113,50 @@ class TestSalesInvoice(FrappeTestCase):
party_link.delete()
frappe.db.set_single_value("Accounts Settings", "enable_common_party_accounting", 0)
def test_sales_invoice_cancel_with_common_party_advance_jv(self):
from erpnext.accounts.doctype.opening_invoice_creation_tool.test_opening_invoice_creation_tool import (
make_customer,
)
from erpnext.accounts.doctype.party_link.party_link import create_party_link
from erpnext.buying.doctype.supplier.test_supplier import create_supplier
# create a customer
customer = make_customer(customer="_Test Common Supplier")
# create a supplier
supplier = create_supplier(supplier_name="_Test Common Supplier").name
# create a party link between customer & supplier
party_link = create_party_link("Supplier", supplier, customer)
# enable common party accounting
frappe.db.set_single_value("Accounts Settings", "enable_common_party_accounting", 1)
# create a sales invoice
si = create_sales_invoice(customer=customer)
# check creation of journal entry
jv = frappe.db.get_value(
"Journal Entry Account",
filters={
"reference_type": si.doctype,
"reference_name": si.name,
"docstatus": 1,
},
fieldname="parent",
)
self.assertTrue(jv)
# cancel sales invoice
si.cancel()
# check cancellation of journal entry
jv_status = frappe.db.get_value("Journal Entry", jv, "docstatus")
self.assertEqual(jv_status, 2)
party_link.delete()
frappe.db.set_single_value("Accounts Settings", "enable_common_party_accounting", 0)
def test_payment_statuses(self):
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
@@ -3625,6 +3678,88 @@ class TestSalesInvoice(FrappeTestCase):
self.assertEqual(len(res), 1)
self.assertEqual(res[0][0], pos_return.return_against)
@change_settings("Accounts Settings", {"enable_common_party_accounting": True})
def test_common_party_with_foreign_currency_jv(self):
from erpnext.accounts.doctype.account.test_account import create_account
from erpnext.accounts.doctype.opening_invoice_creation_tool.test_opening_invoice_creation_tool import (
make_customer,
)
from erpnext.accounts.doctype.party_link.party_link import create_party_link
from erpnext.buying.doctype.supplier.test_supplier import create_supplier
from erpnext.setup.utils import get_exchange_rate
creditors = create_account(
account_name="Creditors USD",
parent_account="Accounts Payable - _TC",
company="_Test Company",
account_currency="USD",
account_type="Payable",
)
debtors = create_account(
account_name="Debtors USD",
parent_account="Accounts Receivable - _TC",
company="_Test Company",
account_currency="USD",
account_type="Receivable",
)
# create a customer
customer = make_customer(customer="_Test Common Party USD")
cust_doc = frappe.get_doc("Customer", customer)
cust_doc.default_currency = "USD"
test_account_details = {
"company": "_Test Company",
"account": debtors,
}
cust_doc.append("accounts", test_account_details)
cust_doc.save()
# create a supplier
supplier = create_supplier(supplier_name="_Test Common Party USD").name
supp_doc = frappe.get_doc("Supplier", supplier)
supp_doc.default_currency = "USD"
test_account_details = {
"company": "_Test Company",
"account": creditors,
}
supp_doc.append("accounts", test_account_details)
supp_doc.save()
# create a party link between customer & supplier
create_party_link("Supplier", supplier, customer)
# create a sales invoice
si = create_sales_invoice(
customer=customer,
currency="USD",
conversion_rate=get_exchange_rate("USD", "INR"),
debit_to=debtors,
do_not_save=1,
)
si.party_account_currency = "USD"
si.save()
si.submit()
# check outstanding of sales invoice
si.reload()
self.assertEqual(si.status, "Paid")
self.assertEqual(flt(si.outstanding_amount), 0.0)
# check creation of journal entry
jv = frappe.get_all(
"Journal Entry Account",
{
"account": si.debit_to,
"party_type": "Customer",
"party": si.customer,
"reference_type": si.doctype,
"reference_name": si.name,
},
pluck="credit_in_account_currency",
)
self.assertTrue(jv)
self.assertEqual(jv[0], si.grand_total)
def check_gl_entries(doc, voucher_no, expected_gle, posting_date):
gl_entries = frappe.db.sql(

View File

@@ -147,6 +147,7 @@
"fieldname": "amount",
"fieldtype": "Currency",
"label": "Amount",
"options": "Company:company:default_currency",
"read_only": 1
},
{

View File

@@ -694,10 +694,7 @@ class Subscription(Document):
elif self.generate_invoice_at == "Days before the current subscription period":
processing_date = add_days(self.current_invoice_start, -self.number_of_days)
process_subscription = frappe.new_doc("Process Subscription")
process_subscription.posting_date = processing_date
process_subscription.subscription = self.name
process_subscription.save().submit()
self.process(posting_date=processing_date)
def get_calendar_months(billing_interval):

View File

@@ -179,50 +179,53 @@ def process_gl_map(gl_map, merge_entries=True, precision=None):
def distribute_gl_based_on_cost_center_allocation(gl_map, precision=None):
cost_center_allocation = get_cost_center_allocation_data(gl_map[0]["company"], gl_map[0]["posting_date"])
if not cost_center_allocation:
return gl_map
new_gl_map = []
for d in gl_map:
cost_center = d.get("cost_center")
# Validate budget against main cost center
validate_expense_against_budget(d, expense_amount=flt(d.debit, precision) - flt(d.credit, precision))
if cost_center and cost_center_allocation.get(cost_center):
for sub_cost_center, percentage in cost_center_allocation.get(cost_center, {}).items():
gle = copy.deepcopy(d)
gle.cost_center = sub_cost_center
for field in ("debit", "credit", "debit_in_account_currency", "credit_in_account_currency"):
gle[field] = flt(flt(d.get(field)) * percentage / 100, precision)
new_gl_map.append(gle)
else:
cost_center_allocation = get_cost_center_allocation_data(
gl_map[0]["company"], gl_map[0]["posting_date"], cost_center
)
if not cost_center_allocation:
new_gl_map.append(d)
continue
for sub_cost_center, percentage in cost_center_allocation:
gle = copy.deepcopy(d)
gle.cost_center = sub_cost_center
for field in ("debit", "credit", "debit_in_account_currency", "credit_in_account_currency"):
gle[field] = flt(flt(d.get(field)) * percentage / 100, precision)
new_gl_map.append(gle)
return new_gl_map
def get_cost_center_allocation_data(company, posting_date):
par = frappe.qb.DocType("Cost Center Allocation")
child = frappe.qb.DocType("Cost Center Allocation Percentage")
def get_cost_center_allocation_data(company, posting_date, cost_center):
cost_center_allocation = frappe.db.get_value(
"Cost Center Allocation",
{
"docstatus": 1,
"company": company,
"valid_from": ("<=", posting_date),
"main_cost_center": cost_center,
},
pluck="name",
order_by="valid_from desc",
)
records = (
frappe.qb.from_(par)
.inner_join(child)
.on(par.name == child.parent)
.select(par.main_cost_center, child.cost_center, child.percentage)
.where(par.docstatus == 1)
.where(par.company == company)
.where(par.valid_from <= posting_date)
.orderby(par.valid_from, order=frappe.qb.desc)
).run(as_dict=True)
if not cost_center_allocation:
return []
cc_allocation = frappe._dict()
for d in records:
cc_allocation.setdefault(d.main_cost_center, frappe._dict()).setdefault(d.cost_center, d.percentage)
records = frappe.db.get_all(
"Cost Center Allocation Percentage",
{"parent": cost_center_allocation},
["cost_center", "percentage"],
as_list=True,
)
return cc_allocation
return records
def merge_similar_entries(gl_map, precision=None):

View File

@@ -68,7 +68,7 @@ def get_party_details(
pos_profile=None,
):
if not party:
return {}
return frappe._dict()
if not frappe.db.exists(party_type, party):
frappe.throw(_("{0}: {1} does not exists").format(party_type, party))
return _get_party_details(

View File

@@ -162,6 +162,11 @@ frappe.query_reports["Accounts Payable"] = {
label: __("Group by Voucher"),
fieldtype: "Check",
},
{
fieldname: "handle_employee_advances",
label: __("Handle Employee Advances"),
fieldtype: "Check",
},
],
formatter: function (value, row, column, data, default_formatter) {

View File

@@ -112,6 +112,26 @@ class ReceivablePayableReport:
self.build_data()
def build_voucher_dict(self, ple):
return frappe._dict(
voucher_type=ple.voucher_type,
voucher_no=ple.voucher_no,
party=ple.party,
party_account=ple.account,
posting_date=ple.posting_date,
account_currency=ple.account_currency,
remarks=ple.remarks,
invoiced=0.0,
paid=0.0,
credit_note=0.0,
outstanding=0.0,
invoiced_in_account_currency=0.0,
paid_in_account_currency=0.0,
credit_note_in_account_currency=0.0,
outstanding_in_account_currency=0.0,
cost_center=ple.cost_center,
)
def init_voucher_balance(self):
# build all keys, since we want to exclude vouchers beyond the report date
for ple in self.ple_entries:
@@ -123,24 +143,8 @@ class ReceivablePayableReport:
key = (ple.account, ple.voucher_type, ple.voucher_no, ple.party)
if key not in self.voucher_balance:
self.voucher_balance[key] = frappe._dict(
voucher_type=ple.voucher_type,
voucher_no=ple.voucher_no,
party=ple.party,
party_account=ple.account,
posting_date=ple.posting_date,
account_currency=ple.account_currency,
remarks=ple.remarks,
invoiced=0.0,
paid=0.0,
credit_note=0.0,
outstanding=0.0,
invoiced_in_account_currency=0.0,
paid_in_account_currency=0.0,
credit_note_in_account_currency=0.0,
outstanding_in_account_currency=0.0,
cost_center=ple.cost_center,
)
self.voucher_balance[key] = self.build_voucher_dict(ple)
self.get_invoices(ple)
if self.filters.get("group_by_party"):
@@ -208,6 +212,18 @@ class ReceivablePayableReport:
row = self.voucher_balance.get(key)
# Build and use a separate row for Employee Advances.
# This allows Payments or Journals made against Emp Advance to be processed.
if (
not row
and ple.against_voucher_type == "Employee Advance"
and self.filters.handle_employee_advances
):
_d = self.build_voucher_dict(ple)
_d.voucher_type = ple.against_voucher_type
_d.voucher_no = ple.against_voucher_no
row = self.voucher_balance[key] = _d
if not row:
# no invoice, this is an invoice / stand-alone payment / credit note
if self.filters.get("ignore_accounts"):

View File

@@ -46,4 +46,20 @@ frappe.query_reports["Bank Reconciliation Statement"] = {
fieldtype: "Check",
},
],
formatter: function (value, row, column, data, default_formatter, filter) {
if (column.fieldname == "payment_entry" && value == "Cheques and Deposits incorrectly cleared") {
column.link_onclick =
"frappe.query_reports['Bank Reconciliation Statement'].open_utility_report()";
}
return default_formatter(value, row, column, data);
},
open_utility_report: function () {
frappe.route_options = {
company: frappe.query_report.get_filter_value("company"),
account: frappe.query_report.get_filter_value("account"),
report_date: frappe.query_report.get_filter_value("report_date"),
};
frappe.open_in_new_tab = true;
frappe.set_route("query-report", "Cheques and Deposits Incorrectly cleared");
},
};

View File

@@ -150,8 +150,8 @@ def get_payment_entries(filters):
select
"Payment Entry" as payment_document, name as payment_entry,
reference_no, reference_date as ref_date,
if(paid_to=%(account)s, received_amount, 0) as debit,
if(paid_from=%(account)s, paid_amount, 0) as credit,
if(paid_to=%(account)s, received_amount_after_tax, 0) as debit,
if(paid_from=%(account)s, paid_amount_after_tax, 0) as credit,
posting_date, ifnull(party,if(paid_from=%(account)s,paid_to,paid_from)) as against_account, clearance_date,
if(paid_to=%(account)s, paid_to_account_currency, paid_from_account_currency) as account_currency
from `tabPayment Entry`

View File

@@ -43,7 +43,7 @@ function get_filters() {
label: __("From Fiscal Year"),
fieldtype: "Link",
options: "Fiscal Year",
default: frappe.sys_defaults.fiscal_year,
default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
reqd: 1,
},
{
@@ -51,7 +51,7 @@ function get_filters() {
label: __("To Fiscal Year"),
fieldtype: "Link",
options: "Fiscal Year",
default: frappe.sys_defaults.fiscal_year,
default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
reqd: 1,
},
{

View File

@@ -0,0 +1,44 @@
// Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.query_reports["Cheques and Deposits Incorrectly cleared"] = {
filters: [
{
fieldname: "company",
label: __("Company"),
fieldtype: "Link",
options: "Company",
reqd: 1,
default: frappe.defaults.get_user_default("Company"),
},
{
fieldname: "account",
label: __("Bank Account"),
fieldtype: "Link",
options: "Account",
default: frappe.defaults.get_user_default("Company")
? locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"]
: "",
reqd: 1,
get_query: function () {
var company = frappe.query_report.get_filter_value("company");
return {
query: "erpnext.controllers.queries.get_account_list",
filters: [
["Account", "account_type", "in", "Bank, Cash"],
["Account", "is_group", "=", 0],
["Account", "disabled", "=", 0],
["Account", "company", "=", company],
],
};
},
},
{
fieldname: "report_date",
label: __("Date"),
fieldtype: "Date",
default: frappe.datetime.get_today(),
reqd: 1,
},
],
};

View File

@@ -0,0 +1,29 @@
{
"add_total_row": 0,
"columns": [],
"creation": "2024-07-30 17:20:07.570971",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"filters": [],
"idx": 0,
"is_standard": "Yes",
"letterhead": null,
"modified": "2024-07-30 17:20:07.570971",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Cheques and Deposits Incorrectly cleared",
"owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "Payment Entry",
"report_name": "Cheques and Deposits Incorrectly cleared",
"report_type": "Script Report",
"roles": [
{
"role": "Accounts User"
},
{
"role": "Accounts Manager"
}
]
}

View File

@@ -0,0 +1,153 @@
# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import frappe
from frappe import _, qb
from frappe.query_builder import CustomFunction
from frappe.query_builder.custom import ConstantColumn
def execute(filters=None):
columns = get_columns()
data = build_data(filters)
return columns, data
def build_payment_entry_dict(row: dict) -> dict:
row_dict = frappe._dict()
row_dict.update(
{
"payment_document": row.get("doctype"),
"payment_entry": row.get("name"),
"posting_date": row.get("posting_date"),
"clearance_date": row.get("clearance_date"),
}
)
if row.get("payment_type") == "Receive" and row.get("party_type") in ["Customer", "Supplier"]:
row_dict.update(
{
"debit": row.get("amount"),
"credit": 0,
}
)
else:
row_dict.update(
{
"debit": 0,
"credit": row.get("amount"),
}
)
return row_dict
def build_journal_entry_dict(row: dict) -> dict:
row_dict = frappe._dict()
row_dict.update(
{
"payment_document": row.get("doctype"),
"payment_entry": row.get("name"),
"posting_date": row.get("posting_date"),
"clearance_date": row.get("clearance_date"),
"debit": row.get("debit_in_account_currency"),
"credit": row.get("credit_in_account_currency"),
}
)
return row_dict
def build_data(filters):
vouchers = get_amounts_not_reflected_in_system_for_bank_reconciliation_statement(filters)
data = []
for x in vouchers:
if x.doctype == "Payment Entry":
data.append(build_payment_entry_dict(x))
elif x.doctype == "Journal Entry":
data.append(build_journal_entry_dict(x))
return data
def get_amounts_not_reflected_in_system_for_bank_reconciliation_statement(filters):
je = qb.DocType("Journal Entry")
jea = qb.DocType("Journal Entry Account")
doctype_name = ConstantColumn("Journal Entry")
journals = (
qb.from_(je)
.inner_join(jea)
.on(je.name == jea.parent)
.select(
doctype_name.as_("doctype"),
je.name,
jea.debit_in_account_currency,
jea.credit_in_account_currency,
je.posting_date,
je.clearance_date,
)
.where(
je.docstatus.eq(1)
& jea.account.eq(filters.account)
& je.posting_date.gt(filters.report_date)
& je.clearance_date.lte(filters.report_date)
& (je.is_opening.isnull() | je.is_opening.eq("No"))
)
.run(as_dict=1)
)
ifelse = CustomFunction("IF", ["condition", "then", "else"])
pe = qb.DocType("Payment Entry")
doctype_name = ConstantColumn("Payment Entry")
payments = (
qb.from_(pe)
.select(
doctype_name.as_("doctype"),
pe.name,
ifelse(pe.paid_from.eq(filters.account), pe.paid_amount, pe.received_amount).as_("amount"),
pe.payment_type,
pe.party_type,
pe.posting_date,
pe.clearance_date,
)
.where(
pe.docstatus.eq(1)
& (pe.paid_from.eq(filters.account) | pe.paid_to.eq(filters.account))
& pe.posting_date.gt(filters.report_date)
& pe.clearance_date.lte(filters.report_date)
)
.run(as_dict=1)
)
return journals + payments
def get_columns():
return [
{
"fieldname": "payment_document",
"label": _("Payment Document Type"),
"fieldtype": "Data",
"width": 220,
},
{
"fieldname": "payment_entry",
"label": _("Payment Document"),
"fieldtype": "Dynamic Link",
"options": "payment_document",
"width": 220,
},
{
"fieldname": "debit",
"label": _("Debit"),
"fieldtype": "Currency",
"options": "account_currency",
"width": 120,
},
{
"fieldname": "credit",
"label": _("Credit"),
"fieldtype": "Currency",
"options": "account_currency",
"width": 120,
},
{"fieldname": "posting_date", "label": _("Posting Date"), "fieldtype": "Date", "width": 110},
{"fieldname": "clearance_date", "label": _("Clearance Date"), "fieldtype": "Date", "width": 110},
]

View File

@@ -0,0 +1,51 @@
// Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
function get_filters() {
let filters = [
{
fieldname: "company",
label: __("Company"),
fieldtype: "Link",
options: "Company",
default: frappe.defaults.get_user_default("Company"),
reqd: 1,
},
{
fieldname: "from_date",
label: __("Start Date"),
fieldtype: "Date",
reqd: 1,
default: frappe.datetime.add_months(frappe.datetime.get_today(), -1),
},
{
fieldname: "to_date",
label: __("End Date"),
fieldtype: "Date",
reqd: 1,
default: frappe.datetime.get_today(),
},
{
fieldname: "account",
label: __("Account"),
fieldtype: "MultiSelectList",
options: "Account",
get_data: function (txt) {
return frappe.db.get_link_options("Account", txt, {
company: frappe.query_report.get_filter_value("company"),
});
},
},
{
fieldname: "voucher_no",
label: __("Voucher No"),
fieldtype: "Data",
width: 100,
},
];
return filters;
}
frappe.query_reports["Invalid Ledger Entries"] = {
filters: get_filters(),
};

View File

@@ -0,0 +1,23 @@
{
"add_total_row": 0,
"columns": [],
"creation": "2024-09-09 12:31:25.295976",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"filters": [],
"idx": 0,
"is_standard": "Yes",
"letterhead": null,
"modified": "2024-09-09 12:31:25.295976",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Invalid Ledger Entries",
"owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "GL Entry",
"report_name": "Invalid Ledger Entries",
"report_type": "Script Report",
"roles": [],
"timeout": 0
}

View File

@@ -0,0 +1,137 @@
# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import frappe
from frappe import _, qb
from frappe.query_builder import Criterion
from frappe.query_builder.custom import ConstantColumn
def execute(filters: dict | None = None):
"""Return columns and data for the report.
This is the main entry point for the report. It accepts the filters as a
dictionary and should return columns and data. It is called by the framework
every time the report is refreshed or a filter is updated.
"""
validate_filters(filters)
columns = get_columns()
data = get_data(filters)
return columns, data
def get_columns() -> list[dict]:
"""Return columns for the report.
One field definition per column, just like a DocType field definition.
"""
return [
{"label": _("Voucher Type"), "fieldname": "voucher_type", "fieldtype": "Link", "options": "DocType"},
{
"label": _("Voucher No"),
"fieldname": "voucher_no",
"fieldtype": "Dynamic Link",
"options": "voucher_type",
},
]
def get_data(filters) -> list[list]:
"""Return data for the report.
The report data is a list of rows, with each row being a list of cell values.
"""
active_vouchers = get_active_vouchers_for_period(filters)
invalid_vouchers = identify_cancelled_vouchers(active_vouchers)
return invalid_vouchers
def identify_cancelled_vouchers(active_vouchers: list[dict] | list | None = None) -> list[dict]:
cancelled_vouchers = []
if active_vouchers:
# Group by voucher types and use single query to identify cancelled vouchers
vtypes = set([x.voucher_type for x in active_vouchers])
for _t in vtypes:
_names = [x.voucher_no for x in active_vouchers if x.voucher_type == _t]
dt = qb.DocType(_t)
non_active_vouchers = (
qb.from_(dt)
.select(ConstantColumn(_t).as_("voucher_type"), dt.name.as_("voucher_no"))
.where(dt.docstatus.ne(1) & dt.name.isin(_names))
.run(as_dict=True)
)
if non_active_vouchers:
cancelled_vouchers.extend(non_active_vouchers)
return cancelled_vouchers
def validate_filters(filters: dict | None = None):
if not filters:
frappe.throw(_("Filters missing"))
if not filters.company:
frappe.throw(_("Company is mandatory"))
if filters.from_date > filters.to_date:
frappe.throw(_("Start Date should be lower than End Date"))
def build_query_filters(filters: dict | None = None) -> list:
qb_filters = []
if filters:
if filters.account:
qb_filters.append(qb.Field("account").isin(filters.account))
if filters.voucher_no:
qb_filters.append(qb.Field("voucher_no").eq(filters.voucher_no))
return qb_filters
def get_active_vouchers_for_period(filters: dict | None = None) -> list[dict]:
uniq_vouchers = []
if filters:
gle = qb.DocType("GL Entry")
ple = qb.DocType("Payment Ledger Entry")
qb_filters = build_query_filters(filters)
gl_vouchers = (
qb.from_(gle)
.select(gle.voucher_type)
.distinct()
.select(gle.voucher_no)
.distinct()
.where(
gle.is_cancelled.eq(0)
& gle.company.eq(filters.company)
& gle.posting_date[filters.from_date : filters.to_date]
)
.where(Criterion.all(qb_filters))
.run(as_dict=True)
)
pl_vouchers = (
qb.from_(ple)
.select(ple.voucher_type)
.distinct()
.select(ple.voucher_no)
.distinct()
.where(
ple.delinked.eq(0)
& ple.company.eq(filters.company)
& ple.posting_date[filters.from_date : filters.to_date]
)
.where(Criterion.all(qb_filters))
.run(as_dict=True)
)
uniq_vouchers.extend(gl_vouchers)
uniq_vouchers.extend(pl_vouchers)
return uniq_vouchers

View File

@@ -703,40 +703,114 @@ def cancel_exchange_gain_loss_journal(
Cancel Exchange Gain/Loss for Sales/Purchase Invoice, if they have any.
"""
if parent_doc.doctype in ["Sales Invoice", "Purchase Invoice", "Payment Entry", "Journal Entry"]:
journals = frappe.db.get_all(
"Journal Entry Account",
filters={
"reference_type": parent_doc.doctype,
"reference_name": parent_doc.name,
"docstatus": 1,
},
fields=["parent"],
as_list=1,
gain_loss_journals = get_linked_exchange_gain_loss_journal(
referenced_dt=parent_doc.doctype, referenced_dn=parent_doc.name, je_docstatus=1
)
if journals:
gain_loss_journals = frappe.db.get_all(
"Journal Entry",
filters={
"name": ["in", [x[0] for x in journals]],
"voucher_type": "Exchange Gain Or Loss",
"docstatus": 1,
},
as_list=1,
)
for doc in gain_loss_journals:
gain_loss_je = frappe.get_doc("Journal Entry", doc[0])
if referenced_dt and referenced_dn:
references = [(x.reference_type, x.reference_name) for x in gain_loss_je.accounts]
if (
len(references) == 2
and (referenced_dt, referenced_dn) in references
and (parent_doc.doctype, parent_doc.name) in references
):
# only cancel JE generated against parent_doc and referenced_dn
gain_loss_je.cancel()
else:
for doc in gain_loss_journals:
gain_loss_je = frappe.get_doc("Journal Entry", doc)
if referenced_dt and referenced_dn:
references = [(x.reference_type, x.reference_name) for x in gain_loss_je.accounts]
if (
len(references) == 2
and (referenced_dt, referenced_dn) in references
and (parent_doc.doctype, parent_doc.name) in references
):
# only cancel JE generated against parent_doc and referenced_dn
gain_loss_je.cancel()
else:
gain_loss_je.cancel()
def delete_exchange_gain_loss_journal(
parent_doc: dict | object, referenced_dt: str | None = None, referenced_dn: str | None = None
) -> None:
"""
Delete Exchange Gain/Loss for Sales/Purchase Invoice, if they have any.
"""
if parent_doc.doctype in ["Sales Invoice", "Purchase Invoice", "Payment Entry", "Journal Entry"]:
gain_loss_journals = get_linked_exchange_gain_loss_journal(
referenced_dt=parent_doc.doctype, referenced_dn=parent_doc.name, je_docstatus=2
)
for doc in gain_loss_journals:
gain_loss_je = frappe.get_doc("Journal Entry", doc)
if referenced_dt and referenced_dn:
references = [(x.reference_type, x.reference_name) for x in gain_loss_je.accounts]
if (
len(references) == 2
and (referenced_dt, referenced_dn) in references
and (parent_doc.doctype, parent_doc.name) in references
):
# only delete JE generated against parent_doc and referenced_dn
gain_loss_je.delete()
else:
gain_loss_je.delete()
def get_linked_exchange_gain_loss_journal(referenced_dt: str, referenced_dn: str, je_docstatus: int) -> list:
"""
Get all the linked exchange gain/loss journal entries for a given document.
"""
gain_loss_journals = []
if journals := frappe.db.get_all(
"Journal Entry Account",
{
"reference_type": referenced_dt,
"reference_name": referenced_dn,
"docstatus": je_docstatus,
},
pluck="parent",
):
gain_loss_journals = frappe.db.get_all(
"Journal Entry",
{
"name": ["in", journals],
"voucher_type": "Exchange Gain Or Loss",
"is_system_generated": 1,
"docstatus": je_docstatus,
},
pluck="name",
)
return gain_loss_journals
def cancel_common_party_journal(self):
if self.doctype not in ["Sales Invoice", "Purchase Invoice"]:
return
if not frappe.db.get_single_value("Accounts Settings", "enable_common_party_accounting"):
return
party_link = self.get_common_party_link()
if not party_link:
return
journal_entry = frappe.db.get_value(
"Journal Entry Account",
filters={
"reference_type": self.doctype,
"reference_name": self.name,
"docstatus": 1,
},
fieldname="parent",
)
if not journal_entry:
return
common_party_journal = frappe.db.get_value(
"Journal Entry",
filters={
"name": journal_entry,
"is_system_generated": True,
"docstatus": 1,
},
)
if not common_party_journal:
return
common_party_je = frappe.get_doc("Journal Entry", common_party_journal)
common_party_je.cancel()
def update_accounting_ledgers_after_reference_removal(

View File

@@ -11,6 +11,7 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s
onload() {
this.setup_queries();
erpnext.accounts.dimensions.setup_dimension_filters(this.frm, this.frm.doctype);
}
refresh() {

View File

@@ -175,7 +175,7 @@ def get_data(filters):
"purchase_order": po.parent,
"supplier": po.supplier,
"estimated_cost": flt(mr_record.get("amount")),
"actual_cost": flt(pi_records.get(po.name)),
"actual_cost": flt(pi_records.get(po.name)) or flt(po.amount),
"purchase_order_amt": flt(po.amount),
"purchase_order_amt_in_company_currency": flt(po.base_amount),
"expected_delivery_date": po.schedule_date,

View File

@@ -84,7 +84,6 @@ force_item_fields = (
"brand",
"stock_uom",
"is_fixed_asset",
"item_tax_rate",
"pricing_rules",
"weight_per_unit",
"weight_uom",
@@ -327,11 +326,16 @@ class AccountsController(TransactionBase):
repost_doc.save(ignore_permissions=True)
def on_trash(self):
from erpnext.accounts.utils import delete_exchange_gain_loss_journal
self._remove_references_in_repost_doctypes()
self._remove_references_in_unreconcile()
# delete sl and gl entries on deletion of transaction
if frappe.db.get_single_value("Accounts Settings", "delete_linked_ledger_entries"):
# delete linked exchange gain/loss journal
delete_exchange_gain_loss_journal(self)
ple = frappe.qb.DocType("Payment Ledger Entry")
frappe.qb.from_(ple).delete().where(
(ple.voucher_type == self.doctype) & (ple.voucher_no == self.name)
@@ -707,7 +711,6 @@ class AccountsController(TransactionBase):
args["is_subcontracted"] = self.is_subcontracted
ret = get_item_details(args, self, for_validate=for_validate, overwrite_warehouse=False)
for fieldname, value in ret.items():
if item.meta.get_field(fieldname) and value is not None:
if item.get(fieldname) is None or fieldname in force_item_fields:
@@ -717,7 +720,10 @@ class AccountsController(TransactionBase):
fieldname
):
item.set(fieldname, value)
elif fieldname == "item_tax_rate" and not (
self.get("is_return") and self.get("return_against")
):
item.set(fieldname, value)
elif fieldname == "serial_no":
# Ensure that serial numbers are matched against Stock UOM
item_conversion_factor = item.get("conversion_factor") or 1.0
@@ -1192,6 +1198,12 @@ class AccountsController(TransactionBase):
# Cancelling existing exchange gain/loss journals is handled during the `on_cancel` event.
# see accounts/utils.py:cancel_exchange_gain_loss_journal()
if self.docstatus == 1:
if dimensions_dict is None:
dimensions_dict = frappe._dict()
active_dimensions = get_dimensions()[0]
for dim in active_dimensions:
dimensions_dict[dim.fieldname] = self.get(dim.fieldname)
if self.get("doctype") == "Journal Entry":
# 'args' is populated with exchange gain/loss account and the amount to be booked.
# These are generated by Sales/Purchase Invoice during reconciliation and advance allocation.
@@ -1456,6 +1468,7 @@ class AccountsController(TransactionBase):
remove_from_bank_transaction,
)
from erpnext.accounts.utils import (
cancel_common_party_journal,
cancel_exchange_gain_loss_journal,
unlink_ref_doc_from_payment_entries,
)
@@ -1467,6 +1480,7 @@ class AccountsController(TransactionBase):
# Cancel Exchange Gain/Loss Journal before unlinking
cancel_exchange_gain_loss_journal(self)
cancel_common_party_journal(self)
if frappe.db.get_single_value("Accounts Settings", "unlink_payment_on_cancellation_of_invoice"):
unlink_ref_doc_from_payment_entries(self)
@@ -2289,12 +2303,15 @@ class AccountsController(TransactionBase):
primary_account = get_party_account(primary_party_type, primary_party, self.company)
secondary_account = get_party_account(secondary_party_type, secondary_party, self.company)
primary_account_currency = get_account_currency(primary_account)
secondary_account_currency = get_account_currency(secondary_account)
jv = frappe.new_doc("Journal Entry")
jv.voucher_type = "Journal Entry"
jv.posting_date = self.posting_date
jv.company = self.company
jv.remark = f"Adjustment for {self.doctype} {self.name}"
jv.is_system_generated = True
reconcilation_entry = frappe._dict()
advance_entry = frappe._dict()
@@ -2328,6 +2345,10 @@ class AccountsController(TransactionBase):
advance_entry.credit_in_account_currency = self.outstanding_amount
reconcilation_entry.debit_in_account_currency = self.outstanding_amount
default_currency = erpnext.get_company_currency(self.company)
if primary_account_currency != default_currency or secondary_account_currency != default_currency:
jv.multi_currency = 1
jv.append("accounts", reconcilation_entry)
jv.append("accounts", advance_entry)

View File

@@ -334,6 +334,8 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None):
def set_missing_values(source, target):
doc = frappe.get_doc(target)
doc.is_return = 1
doc.ignore_pricing_rule = 1
doc.pricing_rules = []
doc.return_against = source.name
doc.set_warehouse = ""
if doctype == "Sales Invoice" or doctype == "POS Invoice":
@@ -397,6 +399,7 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None):
def update_item(source_doc, target_doc, source_parent):
target_doc.qty = -1 * source_doc.qty
target_doc.pricing_rules = None
if source_doc.serial_no:
returned_serial_nos = get_returned_serial_nos(source_doc, source_parent)

View File

@@ -591,7 +591,7 @@ class SellingController(StockController):
if self.doctype in ["Sales Order", "Quotation"]:
for item in self.items:
item.gross_profit = flt(
((item.base_rate - flt(item.valuation_rate)) * item.stock_qty),
((flt(item.stock_uom_rate) - flt(item.valuation_rate)) * item.stock_qty),
self.precision("amount", item),
)

View File

@@ -8,6 +8,7 @@ import frappe
from frappe import _, scrub
from frappe.model.document import Document
from frappe.utils import cint, flt, round_based_on_smallest_currency_fraction
from frappe.utils.deprecations import deprecated
import erpnext
from erpnext.accounts.doctype.journal_entry.journal_entry import get_exchange_rate
@@ -71,7 +72,7 @@ class calculate_taxes_and_totals:
self.calculate_net_total()
self.calculate_tax_withholding_net_total()
self.calculate_taxes()
self.manipulate_grand_total_for_inclusive_tax()
self.adjust_grand_total_for_inclusive_tax()
self.calculate_totals()
self._cleanup()
self.calculate_total_net_weight()
@@ -89,6 +90,9 @@ class calculate_taxes_and_totals:
self.doc.base_tax_withholding_net_total = sum_base_net_amount
def validate_item_tax_template(self):
if self.doc.get("is_return") and self.doc.get("return_against"):
return
for item in self._items:
if item.item_code and item.get("item_tax_template"):
item_doc = frappe.get_cached_doc("Item", item.item_code)
@@ -237,7 +241,6 @@ class calculate_taxes_and_totals:
"tax_fraction_for_current_item",
"grand_total_fraction_for_current_item",
]
if tax.charge_type != "Actual" and not (
self.discount_amount_applied and self.doc.apply_discount_on == "Grand Total"
):
@@ -280,7 +283,7 @@ class calculate_taxes_and_totals:
):
amount = flt(item.amount) - total_inclusive_tax_amount_per_qty
item.net_amount = flt(amount / (1 + cumulated_tax_fraction))
item.net_amount = flt(amount / (1 + cumulated_tax_fraction), item.precision("net_amount"))
item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate"))
item.discount_percentage = flt(
item.discount_percentage, item.precision("discount_percentage")
@@ -505,7 +508,12 @@ class calculate_taxes_and_totals:
tax.base_tax_amount = round(tax.base_tax_amount, 0)
tax.base_tax_amount_after_discount_amount = round(tax.base_tax_amount_after_discount_amount, 0)
@deprecated
def manipulate_grand_total_for_inclusive_tax(self):
# for backward compatablility - if in case used by an external application
return self.adjust_grand_total_for_inclusive_tax()
def adjust_grand_total_for_inclusive_tax(self):
# if fully inclusive taxes and diff
if self.doc.get("taxes") and any(cint(t.included_in_print_rate) for t in self.doc.get("taxes")):
last_tax = self.doc.get("taxes")[-1]
@@ -527,17 +535,21 @@ class calculate_taxes_and_totals:
diff = flt(diff, self.doc.precision("rounding_adjustment"))
if diff and abs(diff) <= (5.0 / 10 ** last_tax.precision("tax_amount")):
self.doc.rounding_adjustment = diff
self.doc.grand_total_diff = diff
else:
self.doc.grand_total_diff = 0
def calculate_totals(self):
if self.doc.get("taxes"):
self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(self.doc.rounding_adjustment)
self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(
self.doc.get("grand_total_diff")
)
else:
self.doc.grand_total = flt(self.doc.net_total)
if self.doc.get("taxes"):
self.doc.total_taxes_and_charges = flt(
self.doc.grand_total - self.doc.net_total - flt(self.doc.rounding_adjustment),
self.doc.grand_total - self.doc.net_total - flt(self.doc.get("grand_total_diff")),
self.doc.precision("total_taxes_and_charges"),
)
else:
@@ -600,8 +612,8 @@ class calculate_taxes_and_totals:
self.doc.grand_total, self.doc.currency, self.doc.precision("rounded_total")
)
# if print_in_rate is set, we would have already calculated rounding adjustment
self.doc.rounding_adjustment += flt(
# rounding adjustment should always be the difference vetween grand and rounded total
self.doc.rounding_adjustment = flt(
self.doc.rounded_total - self.doc.grand_total, self.doc.precision("rounding_adjustment")
)

View File

@@ -185,7 +185,8 @@
{
"fieldname": "expected_closing",
"fieldtype": "Date",
"label": "Expected Closing Date"
"label": "Expected Closing Date",
"no_copy": 1
},
{
"fieldname": "section_break_14",
@@ -357,6 +358,7 @@
"fieldname": "transaction_date",
"fieldtype": "Date",
"label": "Opportunity Date",
"no_copy": 1,
"oldfieldname": "transaction_date",
"oldfieldtype": "Date",
"reqd": 1,
@@ -388,6 +390,7 @@
"fieldname": "first_response_time",
"fieldtype": "Duration",
"label": "First Response Time",
"no_copy": 1,
"read_only": 1
},
{
@@ -622,7 +625,7 @@
"icon": "fa fa-info-sign",
"idx": 195,
"links": [],
"modified": "2022-10-13 12:42:21.545636",
"modified": "2024-08-20 04:12:29.095761",
"modified_by": "Administrator",
"module": "CRM",
"name": "Opportunity",

View File

@@ -8,7 +8,7 @@ from frappe import _
from frappe.model.document import Document
from frappe.query_builder import DocType, Interval
from frappe.query_builder.functions import Now
from frappe.utils import cint, cstr
from frappe.utils import cint, cstr, date_diff, today
from erpnext.manufacturing.doctype.bom_update_log.bom_updation_utils import (
get_leaf_boms,
@@ -67,10 +67,12 @@ class BOMUpdateLog(Document):
wip_log = frappe.get_all(
"BOM Update Log",
{"update_type": "Update Cost", "status": ["in", ["Queued", "In Progress"]]},
fields=["name", "modified"],
filters={"update_type": "Update Cost", "status": ["in", ["Queued", "In Progress"]]},
limit_page_length=1,
)
if wip_log:
if wip_log and date_diff(today(), wip_log[0].modified) < 1:
log_link = frappe.utils.get_link_to_form("BOM Update Log", wip_log[0].name)
frappe.throw(
_("BOM Updation already in progress. Please wait until {0} is complete.").format(log_link),

View File

@@ -230,7 +230,8 @@ cur_frm.cscript.validate_taxes_and_charges = function(cdt, cdn) {
}
cur_frm.cscript.validate_inclusive_tax = function(tax) {
cur_frm.cscript.validate_inclusive_tax = function(tax, frm) {
this.frm = this.frm || frm;
var actual_type_error = function() {
var msg = __("Actual type tax cannot be included in Item rate in row {0}", [tax.idx])
frappe.throw(msg);
@@ -246,12 +247,12 @@ cur_frm.cscript.validate_inclusive_tax = function(tax) {
if(tax.charge_type == "Actual") {
// inclusive tax cannot be of type Actual
actual_type_error();
} else if(tax.charge_type == "On Previous Row Amount" &&
} else if(tax.charge_type == "On Previous Row Amount" && this.frm &&
!cint(this.frm.doc["taxes"][tax.row_id - 1].included_in_print_rate)
) {
// referred row should also be an inclusive tax
on_previous_row_error(tax.row_id);
} else if(tax.charge_type == "On Previous Row Total") {
} else if(tax.charge_type == "On Previous Row Total" && this.frm) {
var taxes_not_included = $.map(this.frm.doc["taxes"].slice(0, tax.row_id),
function(t) { return cint(t.included_in_print_rate) ? null : t; });
if(taxes_not_included.length > 0) {
@@ -294,7 +295,7 @@ if(!erpnext.taxes.flags[cur_frm.cscript.tax_table]) {
var tax = frappe.get_doc(cdt, cdn);
try {
cur_frm.cscript.validate_taxes_and_charges(cdt, cdn);
cur_frm.cscript.validate_inclusive_tax(tax);
cur_frm.cscript.validate_inclusive_tax(tax, frm);
} catch(e) {
tax.included_in_print_rate = 0;
refresh_field("included_in_print_rate", tax.name, tax.parentfield);

View File

@@ -103,7 +103,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments {
this.determine_exclusive_rate();
this.calculate_net_total();
this.calculate_taxes();
this.manipulate_grand_total_for_inclusive_tax();
this.adjust_grand_total_for_inclusive_tax();
this.calculate_totals();
this._cleanup();
}
@@ -243,7 +243,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments {
if(!me.discount_amount_applied && item.qty && (total_inclusive_tax_amount_per_qty || cumulated_tax_fraction)) {
var amount = flt(item.amount) - total_inclusive_tax_amount_per_qty;
item.net_amount = flt(amount / (1 + cumulated_tax_fraction));
item.net_amount = flt(amount / (1 + cumulated_tax_fraction), precision("net_amount", item));
item.net_rate = item.qty ? flt(item.net_amount / item.qty, precision("net_rate", item)) : 0;
me.set_in_company_currency(item, ["net_rate", "net_amount"]);
@@ -298,6 +298,8 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments {
me.frm.doc.net_total += item.net_amount;
me.frm.doc.base_net_total += item.base_net_amount;
});
frappe.model.round_floats_in(this.frm.doc, ["total", "base_total", "net_total", "base_net_total"]);
}
calculate_shipping_charges() {
@@ -506,8 +508,17 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments {
}
}
/**
* @deprecated Use adjust_grand_total_for_inclusive_tax instead.
*/
manipulate_grand_total_for_inclusive_tax() {
// for backward compatablility - if in case used by an external application
this.adjust_grand_total_for_inclusive_tax()
}
adjust_grand_total_for_inclusive_tax() {
var me = this;
// if fully inclusive taxes and diff
if (this.frm.doc["taxes"] && this.frm.doc["taxes"].length) {
var any_inclusive_tax = false;
@@ -533,7 +544,9 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments {
diff = flt(diff, precision("rounding_adjustment"));
if ( diff && Math.abs(diff) <= (5.0 / Math.pow(10, precision("tax_amount", last_tax))) ) {
me.frm.doc.rounding_adjustment = diff;
me.frm.doc.grand_total_diff = diff;
} else {
me.frm.doc.grand_total_diff = 0;
}
}
}
@@ -544,7 +557,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments {
var me = this;
var tax_count = this.frm.doc["taxes"] ? this.frm.doc["taxes"].length : 0;
this.frm.doc.grand_total = flt(tax_count
? this.frm.doc["taxes"][tax_count - 1].total + flt(this.frm.doc.rounding_adjustment)
? this.frm.doc["taxes"][tax_count - 1].total + flt(this.frm.doc.grand_total_diff)
: this.frm.doc.net_total);
if(["Quotation", "Sales Order", "Delivery Note", "Sales Invoice", "POS Invoice"].includes(this.frm.doc.doctype)) {
@@ -604,7 +617,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments {
if(frappe.meta.get_docfield(this.frm.doc.doctype, "rounded_total", this.frm.doc.name)) {
this.frm.doc.rounded_total = round_based_on_smallest_currency_fraction(this.frm.doc.grand_total,
this.frm.doc.currency, precision("rounded_total"));
this.frm.doc.rounding_adjustment += flt(this.frm.doc.rounded_total - this.frm.doc.grand_total,
this.frm.doc.rounding_adjustment = flt(this.frm.doc.rounded_total - this.frm.doc.grand_total,
precision("rounding_adjustment"));
this.set_in_company_currency(this.frm.doc, ["rounding_adjustment", "rounded_total"]);
@@ -672,8 +685,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments {
if (total_for_discount_amount) {
$.each(this.frm._items || [], function(i, item) {
distributed_amount = flt(me.frm.doc.discount_amount) * item.net_amount / total_for_discount_amount;
item.net_amount = flt(item.net_amount - distributed_amount,
precision("base_amount", item));
item.net_amount = flt(item.net_amount - distributed_amount, precision("net_amount", item));
net_total += item.net_amount;
// discount amount rounding loss adjustment if no taxes

View File

@@ -1817,6 +1817,8 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
let item_rates = {};
let item_tax_templates = {};
if (me.frm.doc.is_return && me.frm.doc.return_against) return;
$.each(this.frm.doc.items || [], function(i, item) {
if (item.item_code) {
// Use combination of name and item code in case same item is added multiple times

View File

@@ -115,14 +115,17 @@ erpnext.financial_statements = {
erpnext.financial_statements.filters = get_filters();
let fiscal_year = erpnext.utils.get_fiscal_year(frappe.datetime.get_today());
var filters = report.get_values();
frappe.model.with_doc("Fiscal Year", fiscal_year, function (r) {
var fy = frappe.model.get_doc("Fiscal Year", fiscal_year);
frappe.query_report.set_filter_value({
period_start_date: fy.year_start_date,
period_end_date: fy.year_end_date,
if (!filters.period_start_date || !filters.period_end_date) {
frappe.model.with_doc("Fiscal Year", fiscal_year, function (r) {
var fy = frappe.model.get_doc("Fiscal Year", fiscal_year);
frappe.query_report.set_filter_value({
period_start_date: fy.year_start_date,
period_end_date: fy.year_end_date,
});
});
});
}
const views_menu = report.page.add_custom_button_group(__("Financial Statements"));

View File

@@ -69,7 +69,7 @@ erpnext.accounts.unreconcile_payment = {
{
label: __("Voucher Type"),
fieldname: "voucher_type",
fieldtype: "Dynamic Link",
fieldtype: "Link",
options: "DocType",
in_list_view: 1,
read_only: 1,
@@ -77,7 +77,7 @@ erpnext.accounts.unreconcile_payment = {
{
label: __("Voucher No"),
fieldname: "voucher_no",
fieldtype: "Link",
fieldtype: "Dynamic Link",
options: "voucher_type",
in_list_view: 1,
read_only: 1,

View File

@@ -385,28 +385,14 @@ erpnext.PointOfSale.ItemCart = class {
placeholder: discount ? discount + "%" : __("Enter discount percentage."),
input_class: "input-xs",
onchange: function () {
if (flt(this.value) != 0) {
frappe.model.set_value(
frm.doc.doctype,
frm.doc.name,
"additional_discount_percentage",
flt(this.value)
);
me.hide_discount_control(this.value);
} else {
frappe.model.set_value(
frm.doc.doctype,
frm.doc.name,
"additional_discount_percentage",
0
);
me.$add_discount_elem.css({
border: "1px dashed var(--gray-500)",
padding: "var(--padding-sm) var(--padding-md)",
});
me.$add_discount_elem.html(`${me.get_discount_icon()} ${__("Add Discount")}`);
me.discount_field = undefined;
}
this.value = flt(this.value);
frappe.model.set_value(
frm.doc.doctype,
frm.doc.name,
"additional_discount_percentage",
flt(this.value)
);
me.hide_discount_control(this.value);
},
},
parent: this.$add_discount_elem.find(".add-discount-field"),
@@ -417,9 +403,13 @@ erpnext.PointOfSale.ItemCart = class {
}
hide_discount_control(discount) {
if (!discount) {
this.$add_discount_elem.css({ padding: "0px", border: "none" });
this.$add_discount_elem.html(`<div class="add-discount-field"></div>`);
if (!flt(discount)) {
this.$add_discount_elem.css({
border: "1px dashed var(--gray-500)",
padding: "var(--padding-sm) var(--padding-md)",
});
this.$add_discount_elem.html(`${this.get_discount_icon()} ${__("Add Discount")}`);
this.discount_field = undefined;
} else {
this.$add_discount_elem.css({
border: "1px dashed var(--dark-green-500)",
@@ -1044,6 +1034,7 @@ erpnext.PointOfSale.ItemCart = class {
this.highlight_checkout_btn(false);
}
this.hide_discount_control(frm.doc.additional_discount_percentage);
this.update_totals_section(frm);
if (frm.doc.docstatus === 1) {

View File

@@ -16,7 +16,7 @@ frappe.query_reports["Sales Partner Target Variance based on Item Group"] = {
label: __("Fiscal Year"),
fieldtype: "Link",
options: "Fiscal Year",
default: frappe.sys_defaults.fiscal_year,
default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
},
{
fieldname: "doctype",

View File

@@ -16,7 +16,7 @@ frappe.query_reports["Sales Person Target Variance Based On Item Group"] = {
label: __("Fiscal Year"),
fieldtype: "Link",
options: "Fiscal Year",
default: frappe.sys_defaults.fiscal_year,
default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
},
{
fieldname: "doctype",

View File

@@ -16,7 +16,7 @@ frappe.query_reports["Territory Target Variance Based On Item Group"] = {
label: __("Fiscal Year"),
fieldtype: "Link",
options: "Fiscal Year",
default: frappe.sys_defaults.fiscal_year,
default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
},
{
fieldname: "doctype",

View File

@@ -211,52 +211,33 @@ class DeliveryNote(SellingController):
self.validate_sales_invoice_references()
def validate_sales_order_references(self):
err_msg = ""
for item in self.items:
if (item.against_sales_order and not item.so_detail) or (
not item.against_sales_order and item.so_detail
):
if not item.against_sales_order:
err_msg += (
_("'Sales Order' reference ({1}) is missing in row {0}").format(
frappe.bold(item.idx), frappe.bold("against_sales_order")
)
+ "<br>"
)
else:
err_msg += (
_("'Sales Order Item' reference ({1}) is missing in row {0}").format(
frappe.bold(item.idx), frappe.bold("so_detail")
)
+ "<br>"
)
if err_msg:
frappe.throw(err_msg, title=_("References to Sales Orders are Incomplete"))
self._validate_dependent_item_fields(
"against_sales_order", "so_detail", _("References to Sales Orders are Incomplete")
)
def validate_sales_invoice_references(self):
err_msg = ""
for item in self.items:
if (item.against_sales_invoice and not item.si_detail) or (
not item.against_sales_invoice and item.si_detail
):
if not item.against_sales_invoice:
err_msg += (
_("'Sales Invoice' reference ({1}) is missing in row {0}").format(
frappe.bold(item.idx), frappe.bold("against_sales_invoice")
)
+ "<br>"
)
else:
err_msg += (
_("'Sales Invoice Item' reference ({1}) is missing in row {0}").format(
frappe.bold(item.idx), frappe.bold("si_detail")
)
+ "<br>"
)
self._validate_dependent_item_fields(
"against_sales_invoice", "si_detail", _("References to Sales Invoices are Incomplete")
)
if err_msg:
frappe.throw(err_msg, title=_("References to Sales Invoices are Incomplete"))
def _validate_dependent_item_fields(self, field_a: str, field_b: str, error_title: str):
errors = []
for item in self.items:
missing_label = None
if item.get(field_a) and not item.get(field_b):
missing_label = item.meta.get_label(field_b)
elif item.get(field_b) and not item.get(field_a):
missing_label = item.meta.get_label(field_a)
if missing_label and missing_label != "No Label":
errors.append(
_("The field {0} in row {1} is not set").format(
frappe.bold(_(missing_label)), frappe.bold(item.idx)
)
)
if errors:
frappe.throw("<br>".join(errors), title=error_title)
def validate_proj_cust(self):
"""check for does customer belong to same project as entered.."""

View File

@@ -546,7 +546,7 @@ class PurchaseReceipt(BuyingController):
if not (
(erpnext.is_perpetual_inventory_enabled(self.company) and d.item_code in stock_items)
or d.is_fixed_asset
or (d.is_fixed_asset and not d.purchase_invoice)
):
continue

View File

@@ -3,7 +3,7 @@
import frappe
from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import add_days, cint, cstr, flt, nowtime, today
from frappe.utils import add_days, cint, cstr, flt, get_datetime, getdate, nowtime, today
from pypika import functions as fn
import erpnext
@@ -2603,6 +2603,71 @@ class TestPurchaseReceipt(FrappeTestCase):
company_doc.default_inventory_account = None
company_doc.save()
def test_sles_with_same_posting_datetime_and_creation(self):
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
from erpnext.stock.report.stock_balance.stock_balance import execute
item_code = "Test Item for SLE with same posting datetime and creation"
create_item(item_code)
pr = make_purchase_receipt(
item_code=item_code,
qty=10,
rate=100,
posting_date="2023-11-06",
posting_time="00:00:00",
)
sr = make_stock_entry(
item_code=item_code,
source=pr.items[0].warehouse,
qty=10,
posting_date="2023-11-07",
posting_time="14:28:0.330404",
)
sle = frappe.db.get_value(
"Stock Ledger Entry",
{"voucher_type": sr.doctype, "voucher_no": sr.name, "item_code": sr.items[0].item_code},
"name",
)
sle_doc = frappe.get_doc("Stock Ledger Entry", sle)
sle_doc.db_set("creation", "2023-11-07 14:28:01.208930")
sle_doc.reload()
self.assertEqual(get_datetime(sle_doc.creation), get_datetime("2023-11-07 14:28:01.208930"))
sr = make_stock_entry(
item_code=item_code,
target=pr.items[0].warehouse,
qty=50,
posting_date="2023-11-07",
posting_time="14:28:0.920825",
)
sle = frappe.db.get_value(
"Stock Ledger Entry",
{"voucher_type": sr.doctype, "voucher_no": sr.name, "item_code": sr.items[0].item_code},
"name",
)
sle_doc = frappe.get_doc("Stock Ledger Entry", sle)
sle_doc.db_set("creation", "2023-11-07 14:28:01.044561")
sle_doc.reload()
self.assertEqual(get_datetime(sle_doc.creation), get_datetime("2023-11-07 14:28:01.044561"))
pr.repost_future_sle_and_gle(force=True)
columns, data = execute(
filters=frappe._dict(
{"item_code": item_code, "warehouse": pr.items[0].warehouse, "company": pr.company}
)
)
self.assertEqual(data[0].get("bal_qty"), 50.0)
def prepare_data_for_internal_transfer():
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier

View File

@@ -4,7 +4,7 @@
from frappe.permissions import add_user_permission, remove_user_permission
from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import add_days, flt, nowtime, today
from frappe.utils import add_days, cstr, flt, get_time, getdate, nowtime, today
from erpnext.accounts.doctype.account.test_account import get_inventory_account
from erpnext.stock.doctype.item.test_item import (
@@ -1727,6 +1727,74 @@ class TestStockEntry(FrappeTestCase):
mr.cancel()
mr.delete()
def test_stock_entry_for_same_posting_date_and_time(self):
warehouse = "_Test Warehouse - _TC"
item_code = "Test Stock Entry For Same Posting Datetime 1"
make_item(item_code, {"is_stock_item": 1})
posting_date = nowdate()
posting_time = nowtime()
for index in range(25):
se = make_stock_entry(
item_code=item_code,
qty=1,
to_warehouse=warehouse,
posting_date=posting_date,
posting_time=posting_time,
do_not_submit=True,
purpose="Material Receipt",
basic_rate=100,
)
se.append(
"items",
{
"item_code": item_code,
"item_name": se.items[0].item_name,
"description": se.items[0].description,
"t_warehouse": se.items[0].t_warehouse,
"basic_rate": 100,
"qty": 1,
"stock_qty": 1,
"conversion_factor": 1,
"expense_account": se.items[0].expense_account,
"cost_center": se.items[0].cost_center,
"uom": se.items[0].uom,
"stock_uom": se.items[0].stock_uom,
},
)
se.remarks = f"The current number is {cstr(index)}"
se.submit()
sles = frappe.get_all(
"Stock Ledger Entry",
fields=[
"posting_date",
"posting_time",
"actual_qty",
"qty_after_transaction",
"incoming_rate",
"stock_value_difference",
"stock_value",
],
filters={"item_code": item_code, "warehouse": warehouse},
order_by="creation",
)
self.assertEqual(len(sles), 50)
i = 0
for sle in sles:
i += 1
self.assertEqual(getdate(sle.posting_date), getdate(posting_date))
self.assertEqual(get_time(sle.posting_time), get_time(posting_time))
self.assertEqual(sle.actual_qty, 1)
self.assertEqual(sle.qty_after_transaction, i)
self.assertEqual(sle.incoming_rate, 100)
self.assertEqual(sle.stock_value_difference, 100)
self.assertEqual(sle.stock_value, 100 * i)
def make_serialized_item(**args):
args = frappe._dict(args)

View File

@@ -319,7 +319,8 @@
{
"fieldname": "posting_datetime",
"fieldtype": "Datetime",
"label": "Posting Datetime"
"label": "Posting Datetime",
"search_index": 1
}
],
"hide_toolbar": 1,
@@ -328,7 +329,7 @@
"in_create": 1,
"index_web_pages_for_search": 1,
"links": [],
"modified": "2024-02-07 09:18:13.999231",
"modified": "2024-08-27 09:29:03.961443",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Ledger Entry",

View File

@@ -1028,6 +1028,8 @@ class TestStockLedgerEntry(FrappeTestCase, StockTestMixin):
self.assertEqual(50, _get_stock_credit(final_consumption))
def test_tie_breaking(self):
from erpnext.stock.doctype.repost_item_valuation.repost_item_valuation import repost_entries
frappe.flags.dont_execute_stock_reposts = True
self.addCleanup(frappe.flags.pop, "dont_execute_stock_reposts")
@@ -1070,6 +1072,7 @@ class TestStockLedgerEntry(FrappeTestCase, StockTestMixin):
self.assertEqual([10, 11], ordered_qty_after_transaction())
first.cancel()
repost_entries()
self.assertEqual([1], ordered_qty_after_transaction())
backdated = make_stock_entry(
@@ -1169,7 +1172,7 @@ class TestStockLedgerEntry(FrappeTestCase, StockTestMixin):
qty=5,
posting_date="2021-01-01",
rate=10,
posting_time="02:00:00.1234",
posting_time="02:00:00",
)
time.sleep(3)
@@ -1181,7 +1184,7 @@ class TestStockLedgerEntry(FrappeTestCase, StockTestMixin):
qty=100,
rate=10,
posting_date="2021-01-01",
posting_time="02:00:00",
posting_time="02:00:00.1234",
)
sle = frappe.get_all(

View File

@@ -5,7 +5,7 @@
import frappe
from frappe import _, bold, msgprint
from frappe.query_builder.functions import CombineDatetime, Sum
from frappe.utils import add_to_date, cint, cstr, flt
from frappe.utils import add_to_date, cint, cstr, flt, get_link_to_form
import erpnext
from erpnext.accounts.utils import get_company_default
@@ -357,7 +357,6 @@ class StockReconciliation(StockController):
sl_entries.append(args)
qty_after_transaction = 0
for serial_no in serial_nos:
args = self.get_sle_for_items(row, [serial_no])
@@ -373,27 +372,16 @@ class StockReconciliation(StockController):
if previous_sle and row.warehouse != previous_sle.get("warehouse"):
# If serial no exists in different warehouse
warehouse = previous_sle.get("warehouse", "") or row.warehouse
if not qty_after_transaction:
qty_after_transaction = get_stock_balance(
row.item_code, warehouse, self.posting_date, self.posting_time
frappe.throw(
_(
"The Serial No {0} already exists in the warehouse {1}. It cannot be transferred to the warehouse {2}"
).format(
get_link_to_form("Serial No", serial_no),
bold(previous_sle.get("warehouse")),
row.warehouse,
)
qty_after_transaction -= 1
new_args = args.copy()
new_args.update(
{
"actual_qty": -1,
"qty_after_transaction": qty_after_transaction,
"warehouse": warehouse,
"valuation_rate": previous_sle.get("valuation_rate"),
}
)
sl_entries.append(new_args)
if row.qty:
args = self.get_sle_for_items(row)

View File

@@ -1119,6 +1119,33 @@ class TestStockReconciliation(FrappeTestCase, StockTestMixin):
active_serial_no = frappe.get_all("Serial No", filters={"status": "Active", "item_code": item_code})
self.assertEqual(len(active_serial_no), 5)
def test_stock_reco_for_serialized_item_with_different_warehouse(self):
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
# Add new serial nos
serial_item_code = "Stock-Reco-Serial-Item-11"
warehouse = "_Test Warehouse - _TC"
serial_warehouse = "_Test Warehouse for Stock Reco1 - _TC"
self.make_item(
serial_item_code, {"is_stock_item": 1, "has_serial_no": 1, "serial_no_series": "SNT-SRS11.####"}
)
make_stock_entry(item_code=serial_item_code, target=warehouse, qty=10, basic_rate=100)
stock_entry = make_stock_entry(
item_code=serial_item_code, target=serial_warehouse, qty=10, basic_rate=200
)
sr = create_stock_reconciliation(
item_code=serial_item_code, warehouse=warehouse, qty=11, rate=200, do_not_submit=True
)
serial_nos = get_serial_nos(stock_entry.items[0].serial_no)
sr.items[0].serial_no += f"\n{serial_nos[0]}"
sr.save()
self.assertRaises(frappe.ValidationError, sr.submit)
def create_batch_item_with_batch(item_name, batch_id):
batch_item_doc = create_item(item_name, is_stock_item=1)

View File

@@ -263,7 +263,7 @@
"idx": 1,
"is_tree": 1,
"links": [],
"modified": "2024-01-24 16:27:28.299520",
"modified": "2024-08-14 16:08:15.733597",
"modified_by": "Administrator",
"module": "Stock",
"name": "Warehouse",
@@ -320,6 +320,5 @@
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"title_field": "warehouse_name",
"track_changes": 1
}
}

View File

@@ -855,14 +855,10 @@ def get_price_list_rate(args, item_doc, out=None):
if price_list_rate is None or frappe.db.get_single_value(
"Stock Settings", "update_existing_price_list_rate"
):
if args.get("is_internal_supplier") or args.get("is_internal_customer"):
return out
insert_item_price(args)
if args.price_list and args.rate:
insert_item_price(args)
if not price_list_rate:
return out
if price_list_rate is None:
return out
out.price_list_rate = flt(price_list_rate) * flt(args.plc_conversion_rate) / flt(args.conversion_rate)
@@ -883,6 +879,14 @@ def get_price_list_rate(args, item_doc, out=None):
def insert_item_price(args):
"""Insert Item Price if Price List and Price List Rate are specified and currency is the same"""
if (
not args.price_list
or not args.rate
or args.get("is_internal_supplier")
or args.get("is_internal_customer")
):
return
if frappe.db.get_value("Price List", args.price_list, "currency", cache=True) == args.currency and cint(
frappe.db.get_single_value("Stock Settings", "auto_insert_price_list_rate_if_missing")
):

View File

@@ -17,7 +17,7 @@ frappe.query_reports["Available Batch Report"] = {
fieldtype: "Date",
width: "80",
reqd: 1,
default: frappe.datetime.add_months(frappe.datetime.get_today(), -1),
default: frappe.datetime.get_today(),
},
{
fieldname: "item_code",

View File

@@ -445,6 +445,7 @@ class update_entries_after:
and (
posting_datetime = %(posting_datetime)s
)
and creation = %(creation)s
order by
creation ASC
for update
@@ -1236,6 +1237,11 @@ def get_previous_sle_of_current_voucher(args, operator="<", exclude_current_vouc
voucher_no = args.get("voucher_no")
voucher_condition = f"and voucher_no != '{voucher_no}'"
elif args.get("creation") and args.get("sle_id"):
creation = args.get("creation")
operator = "<="
voucher_condition = f"and creation < '{creation}'"
sle = frappe.db.sql(
f"""
select *, posting_datetime as "timestamp"
@@ -1247,7 +1253,7 @@ def get_previous_sle_of_current_voucher(args, operator="<", exclude_current_vouc
and (
posting_datetime {operator} %(posting_datetime)s
)
order by posting_datetime desc, creation desc
order by posting_date desc, posting_time desc, creation desc
limit 1
for update""",
{
@@ -1341,7 +1347,7 @@ def get_stock_ledger_entries(
where item_code = %(item_code)s
and is_cancelled = 0
{conditions}
order by posting_datetime {order}, creation {order}
order by posting_date {order}, posting_time {order}, creation {order}
{limit} {for_update}""".format(
conditions=conditions,
limit=limit or "",
@@ -1446,7 +1452,7 @@ def get_valuation_rate(
AND valuation_rate >= 0
AND is_cancelled = 0
AND NOT (voucher_no = %s AND voucher_type = %s)
order by posting_datetime desc, name desc limit 1""",
order by posting_date desc, posting_time desc, name desc limit 1""",
(item_code, warehouse, voucher_no, voucher_type),
)
@@ -1695,7 +1701,8 @@ def get_future_sle_with_negative_qty(sle):
& (SLE.is_cancelled == 0)
& (SLE.qty_after_transaction < 0)
)
.orderby(SLE.posting_datetime)
.orderby(SLE.posting_date)
.orderby(SLE.posting_time)
.limit(1)
)
@@ -1711,14 +1718,14 @@ def get_future_sle_with_negative_batch_qty(args):
with batch_ledger as (
select
posting_date, posting_time, posting_datetime, voucher_type, voucher_no,
sum(actual_qty) over (order by posting_datetime, creation) as cumulative_total
sum(actual_qty) over (order by posting_date, posting_time, creation) as cumulative_total
from `tabStock Ledger Entry`
where
item_code = %(item_code)s
and warehouse = %(warehouse)s
and batch_no=%(batch_no)s
and is_cancelled = 0
order by posting_datetime, creation
order by posting_date, posting_time, creation
)
select * from batch_ledger
where

View File

@@ -9108,13 +9108,13 @@ Warehouse wise Stock Value,Warenwert nach Lager,
Ex Works,Ab Werk,
Free Carrier,Frei Frachtführer,
Free Alongside Ship,Frei Längsseite Schiff,
Free on Board,Frei an Bord,
Free On Board,Frei an Bord,
Carriage Paid To,Frachtfrei,
Carriage and Insurance Paid to,Frachtfrei versichert,
Cost and Freight,Kosten und Fracht,
"Cost, Insurance and Freight","Kosten, Versicherung und Fracht",
Delivered at Place,Geliefert benannter Ort,
Delivered at Place Unloaded,Geliefert benannter Ort entladen,
Delivered At Place,Geliefert benannter Ort,
Delivered At Place Unloaded,Geliefert benannter Ort entladen,
Delivered Duty Paid,Geliefert verzollt,
Discount Validity,Frist für den Rabatt,
Discount Validity Based On,Frist für den Rabatt berechnet sich nach,
Can't render this file because it is too large.