Compare commits

..

367 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
Frappe PR Bot
79edcb2a94 chore(release): Bumped to Version 14.72.1
## [14.72.1](https://github.com/frappe/erpnext/compare/v14.72.0...v14.72.1) (2024-08-16)

### Bug Fixes

* **patch:** replace repost with direct sql to update 'against_voucher ([f6f341a](f6f341ad58))
2024-08-16 11:09:57 +00:00
ruthra kumar
bd2f408f00 Merge pull request #42795 from frappe/mergify/bp/version-14/pr-42791
fix(patch): Use sql to update 'against_voucher' rather than reposting (backport #42791)
2024-08-16 16:38:39 +05:30
ruthra kumar
3b02f80a4e chore: resolve conflict 2024-08-16 16:04:52 +05:30
ruthra kumar
bebd1a0426 chore: update patches.txt
(cherry picked from commit 1721175a20)

# Conflicts:
#	erpnext/patches.txt
2024-08-16 10:26:22 +00:00
ruthra kumar
f6f341ad58 fix(patch): replace repost with direct sql to update 'against_voucher
(cherry picked from commit 13bb48434f)
2024-08-16 10:26:22 +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
Frappe PR Bot
2796aa8b57 chore(release): Bumped to Version 14.72.0
# [14.72.0](https://github.com/frappe/erpnext/compare/v14.71.0...v14.72.0) (2024-08-14)

### Bug Fixes

* dimensions in common party journal entry ([b73df8f](b73df8f5f0))
* expense causing p&l test case to fail ([a9fa9e8](a9fa9e86ea))
* force fetch updates for subcription ([f35bc43](f35bc43242))
* german translation of exit ([db27dd8](db27dd8702))
* ledger entries for pos return with update outstanding for self ([714a432](714a432c1c))
* Maintain same rate on qty change on Quotation to Sales Order ([29449cb](29449cbaf9))
* missing def expense if no exp in first month ([7cd6deb](7cd6debd0e))
* patch to fix incorrect against_voucher references in ledger ([5473546](54735469c1))
* pre-commit for better code formatting ([8f19832](8f19832aa5))
* Sort lists before calling itertools.groupby ([29cfb7c](29cfb7cf25))
* test case for zero deferred expense ([186701b](186701b54f))
* text color in sales funnel report based on theme ([2509618](25096185c9))
* typeerror on payment entry ([c4c001b](c4c001b6f9))
* update 'Paid Amount' on forex payment request ([365ef6b](365ef6b88a))
* warning message for negative stock (backport [#42683](https://github.com/frappe/erpnext/issues/42683)) ([#42709](https://github.com/frappe/erpnext/issues/42709)) ([e44daf7](e44daf73fb))

### Features

* changes in opportunity.py to show contacts and addresses from referenced and opportunities ([8d7e150](8d7e150afb))
2024-08-14 08:00:58 +00:00
ruthra kumar
a5ca5b5081 Merge pull request #42744 from frappe/version-14-hotfix
chore: release v14
2024-08-14 13:29:41 +05:30
ruthra kumar
695297b917 Merge pull request #42760 from frappe/mergify/bp/version-14-hotfix/pr-42720
fix: incorrect 'against_voucher' for Pos return with 'Update Outstanding for Self' disabled. (backport #42720)
2024-08-14 12:27:13 +05:30
ruthra kumar
8882b85888 chore: resolve conflict 2024-08-14 12:04:30 +05:30
ruthra kumar
1cddb4ff39 refactor: move patch to v14 and update patches.txt
(cherry picked from commit da2286802a)

# Conflicts:
#	erpnext/patches.txt
2024-08-14 06:26:35 +00:00
ruthra kumar
aaa6d66666 test: against_voucher for pos_returns without updating for self
(cherry picked from commit 3fb0858321)
2024-08-14 06:26:35 +00:00
ruthra kumar
fa44b0d745 refactor: update patches.txt
(cherry picked from commit 4dc0d3a003)

# Conflicts:
#	erpnext/patches.txt
2024-08-14 06:26:35 +00:00
ruthra kumar
54735469c1 fix: patch to fix incorrect against_voucher references in ledger
(cherry picked from commit 487d0a55f5)

# Conflicts:
#	erpnext/patches/v14_0/update_pos_return_ledger_entries.py
2024-08-14 06:26:34 +00:00
ruthra kumar
714a432c1c fix: ledger entries for pos return with update outstanding for self
(cherry picked from commit 2cd9b28e5b)
2024-08-14 06:26:34 +00:00
ruthra kumar
367b4177c6 Merge pull request #42748 from frappe/mergify/bp/version-14-hotfix/pr-42221
fix: Force Fetching updates on Subscriptions (backport #42221)
2024-08-13 17:24:50 +05:30
ruthra kumar
2d0dca943a chore: resolve conflict 2024-08-13 16:54:35 +05:30
ruthra kumar
456f5b644b Merge pull request #42740 from frappe/mergify/bp/version-14-hotfix/pr-42726
fix: text color in sales funnel report based on theme (backport #42726)
2024-08-13 16:49:01 +05:30
ruthra kumar
cca5aa8a96 chore: resolve conflict 2024-08-13 16:28:07 +05:30
ruthra kumar
366f383d1f refactor: test for force-fetch on future subscription
(cherry picked from commit fd680380bb)

# Conflicts:
#	erpnext/accounts/doctype/subscription/test_subscription.py
2024-08-13 10:53:15 +00:00
ruthra kumar
7752de3a1c chore: minor naming change
(cherry picked from commit d8b6767697)
2024-08-13 10:53:14 +00:00
ruthra kumar
f0f663e552 refactor: don't process future subscriptions
(cherry picked from commit 3a11577411)
2024-08-13 10:53:14 +00:00
ruthra kumar
f35bc43242 fix: force fetch updates for subcription
(cherry picked from commit 1ef890db73)

# Conflicts:
#	erpnext/accounts/doctype/subscription/subscription.js
2024-08-13 10:53:14 +00:00
ruthra kumar
b0b5b25a53 chore: resolve conflict 2024-08-13 14:38:44 +05:30
ljain112
25096185c9 fix: text color in sales funnel report based on theme
(cherry picked from commit 61bc0925d5)

# Conflicts:
#	erpnext/selling/page/sales_funnel/sales_funnel.js
2024-08-13 08:36:21 +00:00
ruthra kumar
a5b5b5e62c Merge pull request #42734 from frappe/mergify/bp/version-14-hotfix/pr-42733
refactor: add a new filter flag in Process Statement of Accounts (backport #42733)
2024-08-13 11:49:19 +05:30
ruthra kumar
e421e16fdc chore: resolve conflicts 2024-08-13 11:22:52 +05:30
ruthra kumar
cef3573d79 refactor: pass filter to General Ledger
(cherry picked from commit 90880c8c01)
2024-08-13 05:41:19 +00:00
ruthra kumar
1ebcc33cdd refactor: cr and dr note filter in Statement of Accounts
(cherry picked from commit 0cf478c4c2)

# Conflicts:
#	erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json
#	erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py
2024-08-13 05:41:19 +00:00
ruthra kumar
a9871e379a refactor: combine vouchers from both ignore
(cherry picked from commit 4cc5cd5a71)
2024-08-13 05:41:19 +00:00
ruthra kumar
34662e6e3c Merge pull request #42724 from frappe/mergify/bp/version-14-hotfix/pr-42672
fix: update 'Paid Amount' on forex payment request (backport #42672)
2024-08-12 16:36:28 +05:30
ruthra kumar
1844cb60a4 chore: resolve conflict 2024-08-12 16:14:29 +05:30
ruthra kumar
71416902f6 test: currency conversion on foreign currency account
(cherry picked from commit f913c0fde1)
2024-08-12 10:38:33 +00:00
ruthra kumar
e289793308 test: make use of test fixture
(cherry picked from commit d6d0a1b38d)

# Conflicts:
#	erpnext/accounts/doctype/payment_request/test_payment_request.py
2024-08-12 10:38:33 +00:00
ruthra kumar
365ef6b88a fix: update 'Paid Amount' on forex payment request
(cherry picked from commit 7b0dfb2a05)
2024-08-12 10:38:33 +00:00
mergify[bot]
e44daf73fb fix: warning message for negative stock (backport #42683) (#42709)
* fix: warning message for negative stock (#42683)

(cherry picked from commit deccb007c1)

# Conflicts:
#	erpnext/stock/stock_ledger.py

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-08-12 11:20:44 +05:30
ruthra kumar
a43136da5d Merge pull request #42694 from frappe/mergify/bp/version-14-hotfix/pr-42428
fix: Sort lists before calling itertools.groupby (backport #42428)
2024-08-09 12:44:24 +05:30
ruthra kumar
d0fbb4d532 Merge pull request #42692 from frappe/mergify/bp/version-14-hotfix/pr-42691
fix: typeerror on payment entry (backport #42691)
2024-08-09 11:55:49 +05:30
Corentin Forler
29cfb7cf25 fix: Sort lists before calling itertools.groupby
(cherry picked from commit 45a6ecbd38)
2024-08-09 06:23:57 +00:00
ruthra kumar
c4c001b6f9 fix: typeerror on payment entry
(cherry picked from commit 9cdca0d662)
2024-08-09 11:53:35 +05:30
Raffael Meyer
93d8e3f3b0 Merge pull request #42687 from frappe/mergify/bp/version-14-hotfix/pr-42684
fix: german translation of exit (backport #42684)
2024-08-08 23:25:58 +02:00
barredterra
db27dd8702 fix: german translation of exit
(cherry picked from commit a2df276880)
2024-08-08 21:15:03 +00:00
ruthra kumar
cc97d4e869 Merge pull request #42651 from frappe/mergify/bp/version-14-hotfix/pr-40865
fix: missing def expense if no exp in first month (backport #40865)
2024-08-08 14:37:39 +05:30
ruthra kumar
eb26610d54 Merge pull request #42677 from frappe/mergify/bp/version-14-hotfix/pr-42673
fix: Maintain same rate on qty change on Quotation to Sales Order (backport #42673)
2024-08-08 13:37:48 +05:30
ruthra kumar
29449cbaf9 fix: Maintain same rate on qty change on Quotation to Sales Order
(cherry picked from commit 91ce9fce9b)
2024-08-08 08:03:37 +00:00
Shariq Ansari
f29cad695a Merge pull request #42667 from frappe/mergify/bp/version-14-hotfix/pr-42596
fix: changes in opportunity.py to show contacts and addresses created from opportunity (backport #42596)
2024-08-08 11:39:47 +05:30
barredterra
003653c572 fix: german translations for incoterms
(cherry picked from commit 9789648175)
2024-08-07 18:54:36 +00:00
Dietmar Fischer
8f19832aa5 fix: pre-commit for better code formatting
(cherry picked from commit 511a0b9f37)
2024-08-07 14:29:48 +00:00
Dietmar Fischer
8d7e150afb feat: changes in opportunity.py to show contacts and addresses from referenced and opportunities
(cherry picked from commit 61576ca030)
2024-08-07 14:29:48 +00:00
ruthra kumar
4a77653e94 Merge pull request #42660 from frappe/mergify/bp/version-14-hotfix/pr-42578
fix: dimensions in common party journal entry (backport #42578)
2024-08-07 16:23:45 +05:30
ruthra kumar
fa74efc1fc test: dimension inheritance on Exc Gain/Loss JV on Common party
(cherry picked from commit 8040544216)
2024-08-07 10:33:23 +00:00
ljain112
b73df8f5f0 fix: dimensions in common party journal entry
(cherry picked from commit ac629ede79)
2024-08-07 10:33:22 +00:00
Frappe PR Bot
a8a8a033a1 chore(release): Bumped to Version 14.71.0
# [14.71.0](https://github.com/frappe/erpnext/compare/v14.70.14...v14.71.0) (2024-08-07)

### Bug Fixes

* 'undefined' in PL and BS report summary on Consolidated report ([a5e1432](a5e14324da))
* company filter for filtring tax withheld vouchers ([b7731c8](b7731c8fd7))
* do not update item price and last purchase rate for inter transf… (backport [#42616](https://github.com/frappe/erpnext/issues/42616)) ([#42632](https://github.com/frappe/erpnext/issues/42632)) ([5c5349e](5c5349ed16))
* filter condition in item-wise purchase register ([bc9b461](bc9b46126b))
* fixed depreciation calculation as per income tax act ([8df1079](8df1079618))
* item_code filter in item-wise sales register ([f0f8a2f](f0f8a2f01b))
* min height for rows in sales funnel ([7086a96](7086a96462))
* set query filters for sales / purchase tax template on PE ([c8d8536](c8d85364b9))
* slowness in reposting dependent vouchers. (backport [#42282](https://github.com/frappe/erpnext/issues/42282)) ([#42569](https://github.com/frappe/erpnext/issues/42569)) ([3fb6f97](3fb6f97f66))

### Features

* expiry date column in Available Batch Report (backport [#42628](https://github.com/frappe/erpnext/issues/42628)) ([#42641](https://github.com/frappe/erpnext/issues/42641)) ([83928bb](83928bbf78))
2024-08-07 09:27:57 +00:00
ruthra kumar
f0230b2dfd Merge pull request #42648 from frappe/version-14-hotfix
chore: release v14
2024-08-07 14:56:41 +05:30
Khushi Rawat
f4811c3846 Merge pull request #42629 from khushi8112/depreciation-adjustment-issue-when-field-for-income-tax-enabled
fix: depreciation calculation as per income tax act
2024-08-06 19:15:03 +05:30
Dany Robert
a9fa9e86ea fix: expense causing p&l test case to fail
(cherry picked from commit 01888c98bc)
2024-08-06 11:01:41 +00:00
Dany Robert
c67be05280 chore: semgrep
(cherry picked from commit 581af4eced)
2024-08-06 11:01:41 +00:00
Dany Robert
186701b54f fix: test case for zero deferred expense
(cherry picked from commit 7ef4dbcaf6)
2024-08-06 11:01:41 +00:00
Dany Robert
7cd6debd0e fix: missing def expense if no exp in first month
(cherry picked from commit 5c9ce575f6)
2024-08-06 11:01:41 +00:00
Nihantra C. Patel
f8fd354d75 Merge pull request #42650 from Nihantra-Patel/fix_item_sales_purchase_register
fix: item_code filter in item-wise sales-purchase register v14
2024-08-06 16:19:16 +05:30
Nihantra Patel
bc9b46126b fix: filter condition in item-wise purchase register 2024-08-06 15:56:00 +05:30
Nihantra Patel
f0f8a2f01b fix: item_code filter in item-wise sales register 2024-08-06 15:54:05 +05:30
mergify[bot]
83928bbf78 feat: expiry date column in Available Batch Report (backport #42628) (#42641)
* feat: expiry date column in Available Batch Report (#42628)

(cherry picked from commit faff84c6e5)

# Conflicts:
#	erpnext/stock/report/available_batch_report/available_batch_report.py

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-08-06 15:24:37 +05:30
ruthra kumar
bc50512c97 Merge pull request #42645 from frappe/mergify/bp/version-14-hotfix/pr-42592
fix: company filter for filtring tax withheld vouchers (backport #42592)
2024-08-06 13:39:17 +05:30
ljain112
b7731c8fd7 fix: company filter for filtring tax withheld vouchers
(cherry picked from commit cfe2ae604b)
2024-08-06 07:47:21 +00:00
ruthra kumar
8667bcd86b Merge pull request #42643 from frappe/mergify/bp/version-14-hotfix/pr-42545
fix: min height for rows in sales funnel (backport #42545)
2024-08-06 13:09:39 +05:30
ljain112
7086a96462 fix: min height for rows in sales funnel
(cherry picked from commit fd71d8af52)
2024-08-06 07:33:07 +00:00
ruthra kumar
2a38e14cca Merge pull request #42637 from frappe/mergify/bp/version-14-hotfix/pr-42636
refactor: posting date should not be considered for ignore filters in GL (backport #42636)
2024-08-06 11:43:54 +05:30
ruthra kumar
f4ba879203 refactor: posting date is not considered for ignore filters in GL
(cherry picked from commit c930f8ba9d)
2024-08-06 05:26:26 +00:00
mergify[bot]
5c5349ed16 fix: do not update item price and last purchase rate for inter transf… (backport #42616) (#42632)
fix: do not update item price and last purchase rate for inter transf… (#42616)

fix: do not update item price and last purchase rate for inter transfer transaction
(cherry picked from commit c8af544ef3)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-08-06 08:19:46 +05:30
Khushi Rawat
8df1079618 fix: fixed depreciation calculation as per income tax act 2024-08-05 18:04:06 +05:30
Khushi Rawat
38739bec45 Merge pull request #42630 from khushi8112/fix-semgrep
ci: fix semgrep version in linters
2024-08-05 18:01:50 +05:30
Khushi Rawat
6ceb3476a6 ci: fix semgrep version in linters 2024-08-05 17:37:29 +05:30
ruthra kumar
6cba69c7da Merge pull request #42625 from frappe/mergify/bp/version-14-hotfix/pr-42555
refactor: date filters should be mandatory in Sales Pipeline Analytics report (backport #42555)
2024-08-05 15:32:28 +05:30
ruthra kumar
ffd3aea07e refactor(test): use test fixture and supply from and to dates
(cherry picked from commit 4253caf910)
2024-08-05 09:39:27 +00:00
ruthra kumar
989ef52f59 refactor: consider empty-string as Not Assigned
(cherry picked from commit 213b2ba942)
2024-08-05 09:39:27 +00:00
ruthra kumar
baa36c6d5e refactor: report columns should be based on from and to dates
(cherry picked from commit 751a25c4b7)
2024-08-05 09:39:27 +00:00
ruthra kumar
f7f191fe50 refactor: make 'from_date' and 'to_date' mandatory
(cherry picked from commit 3617b41b95)
2024-08-05 09:39:27 +00:00
ruthra kumar
703f58ceac refactor: date filters should be explicit
(cherry picked from commit 40c166a0a0)
2024-08-05 09:39:27 +00:00
ruthra kumar
e1da019fe7 Merge pull request #42613 from frappe/mergify/bp/version-14-hotfix/pr-42597
refactor: filter to ignore system generated cr / dr reconciliation journals on general ledger (backport #42597)
2024-08-05 13:20:25 +05:30
ruthra kumar
f0bdc41a94 refactor(test): clear old records 2024-08-05 12:58:21 +05:30
ruthra kumar
392ba36dcf refactor(test): filter and reconcile concerned vouchers
(cherry picked from commit 9ade269b7a)
2024-08-05 05:43:50 +00:00
ruthra kumar
1b6539c3c5 test: clear old data
(cherry picked from commit 991069bfbc)
2024-08-05 05:43:50 +00:00
ruthra kumar
1da744dc4d refactor: make use of date filters on ignore filterss
(cherry picked from commit 03f3ab522f)
2024-08-05 05:43:50 +00:00
ruthra kumar
ac763f8c19 test: ignore filter for system generated cr / dr note journals
(cherry picked from commit 3ffac73598)
2024-08-05 05:43:50 +00:00
ruthra kumar
0a83c8b00c refactor: ignore system generated cr / dr notes on general ledger
(cherry picked from commit bb8c9b5a58)
2024-08-05 05:43:50 +00:00
ruthra kumar
b20b15af74 refactor: ignore filter in general ledger for cr / dr notes
(cherry picked from commit 59d5beee20)
2024-08-05 05:43:50 +00:00
Frappe PR Bot
4089af5a7b chore(release): Bumped to Version 14.70.14
## [14.70.14](https://github.com/frappe/erpnext/compare/v14.70.13...v14.70.14) (2024-08-01)

### Bug Fixes

* slowness in reposting dependent vouchers. (backport [#42282](https://github.com/frappe/erpnext/issues/42282)) ([#42569](https://github.com/frappe/erpnext/issues/42569)) ([cccfbf1](cccfbf193e))
2024-08-01 09:12:20 +00:00
rohitwaghchaure
06c73ef2da Merge pull request #42570 from frappe/mergify/bp/version-14/pr-42569
fix: slowness in reposting dependent vouchers. (backport #42282) (backport #42569)
2024-08-01 14:40:58 +05:30
mergify[bot]
cccfbf193e fix: slowness in reposting dependent vouchers. (backport #42282) (#42569)
fix: slowness in reposting dependent vouchers. (#42282)

(cherry picked from commit b17696a8ae)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
(cherry picked from commit 3fb6f97f66)
2024-08-01 07:52:46 +00:00
mergify[bot]
3fb6f97f66 fix: slowness in reposting dependent vouchers. (backport #42282) (#42569)
fix: slowness in reposting dependent vouchers. (#42282)

(cherry picked from commit b17696a8ae)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-08-01 13:21:37 +05:30
ruthra kumar
35f6657bae Merge pull request #42565 from frappe/mergify/bp/version-14-hotfix/pr-42563
fix: set query filters for sales / purchase tax template on PE (backport #42563)
2024-07-31 17:28:45 +05:30
ruthra kumar
e82c441326 chore: resolve conflict 2024-07-31 17:14:26 +05:30
ruthra kumar
c8d85364b9 fix: set query filters for sales / purchase tax template on PE
(cherry picked from commit 9fe47ac101)

# Conflicts:
#	erpnext/accounts/doctype/payment_entry/payment_entry.js
2024-07-31 11:39:09 +00:00
ruthra kumar
64234a571e Merge pull request #42559 from frappe/mergify/bp/version-14-hotfix/pr-42558
fix: 'undefined' in PL and BS report summary on Consolidated report (backport #42558)
2024-07-31 16:19:17 +05:30
ruthra kumar
a5e14324da fix: 'undefined' in PL and BS report summary on Consolidated report
(cherry picked from commit dd5a5e4919)
2024-07-31 10:03:56 +00:00
Frappe PR Bot
37a7da3371 chore(release): Bumped to Version 14.70.13
## [14.70.13](https://github.com/frappe/erpnext/compare/v14.70.12...v14.70.13) (2024-07-31)

### Bug Fixes

* builtins.KeyError: ('ABC', 'Store - CP') (backport [#42505](https://github.com/frappe/erpnext/issues/42505)) ([#42508](https://github.com/frappe/erpnext/issues/42508)) ([0256c64](0256c64634))
* consider payment entries for checking if tds is deducted ([e7432fc](e7432fc60d))
* dynamic condition in the pricing rule not working (backport [#42467](https://github.com/frappe/erpnext/issues/42467)) ([#42543](https://github.com/frappe/erpnext/issues/42543)) ([53034c3](53034c332b))
* **gross profit:** incorrect valuation rate on different warehouses ([1a7efbb](1a7efbb654))
* ignore duplicates while creating default templates ([99bc8e8](99bc8e849c))
* incorrect cost_center on AR/AP report ([0e2abbd](0e2abbd08e))
* keep status as In Progress for RIV for Timeout Error (backport [#42274](https://github.com/frappe/erpnext/issues/42274)) ([#42504](https://github.com/frappe/erpnext/issues/42504)) ([8e8d0c7](8e8d0c7bd0))
* parenttype in item wise purchase and sales register ([97f2e88](97f2e88f4c))
* performance issue for the report Purchase Order Analysis report (backport [#42503](https://github.com/frappe/erpnext/issues/42503)) ([#42506](https://github.com/frappe/erpnext/issues/42506)) ([f42f1bb](f42f1bb35f))
* price_list_currency not found error (backport [#42534](https://github.com/frappe/erpnext/issues/42534)) ([#42538](https://github.com/frappe/erpnext/issues/42538)) ([3ba6f40](3ba6f40063))
* warehouse filter in Product Bundle Balance (backport [#42532](https://github.com/frappe/erpnext/issues/42532)) ([#42536](https://github.com/frappe/erpnext/issues/42536)) ([0441984](0441984405))
2024-07-31 06:00:37 +00:00
ruthra kumar
81c362dbe4 Merge pull request #42542 from frappe/version-14-hotfix
chore: release v14
2024-07-31 11:29:23 +05:30
mergify[bot]
53034c332b fix: dynamic condition in the pricing rule not working (backport #42467) (#42543)
fix: dynamic condition in the pricing rule not working (#42467)

(cherry picked from commit 0e817f42ef)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-30 17:01:24 +05:30
mergify[bot]
0441984405 fix: warehouse filter in Product Bundle Balance (backport #42532) (#42536)
fix: warehouse filter in Product Bundle Balance (#42532)

(cherry picked from commit 0ecfa709d8)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-30 14:53:31 +05:30
mergify[bot]
3ba6f40063 fix: price_list_currency not found error (backport #42534) (#42538)
fix: price_list_currency not found error (#42534)

(cherry picked from commit 23fed831a0)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-30 14:52:45 +05:30
mergify[bot]
8e8d0c7bd0 fix: keep status as In Progress for RIV for Timeout Error (backport #42274) (#42504)
* fix: keep status as In Progress for RIV for Timeout Error (#42274)

(cherry picked from commit 10280d6140)

# Conflicts:
#	erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-30 14:25:51 +05:30
mergify[bot]
f42f1bb35f fix: performance issue for the report Purchase Order Analysis report (backport #42503) (#42506)
* fix: performance issue for the report Purchase Order Analysis report (#42503)

(cherry picked from commit cb522f8f22)

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

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-30 14:25:23 +05:30
mergify[bot]
0256c64634 fix: builtins.KeyError: ('ABC', 'Store - CP') (backport #42505) (#42508)
fix: builtins.KeyError: ('ABC', 'Store - CP') (#42505)

(cherry picked from commit 25dac1f18e)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-30 14:25:01 +05:30
ruthra kumar
e4f583d25a Merge pull request #42529 from frappe/mergify/bp/version-14-hotfix/pr-42528
chore: button name should reflect what it creates (backport #42528)
2024-07-30 12:12:32 +05:30
ruthra kumar
7259c0fe30 chore: button name should reflect what it creates
(cherry picked from commit 0b6e7f83cd)
2024-07-30 06:31:17 +00:00
ruthra kumar
02547115e5 Merge pull request #42335 from frappe/mergify/bp/version-14-hotfix/pr-42330
fix: incorrect valuation rate for items from different warehouses in Gross Profit (backport #42330)
2024-07-29 10:17:04 +05:30
ruthra kumar
0bc9825238 Merge pull request #42491 from frappe/mergify/bp/version-14-hotfix/pr-42477
fix: incorrect cost_center on AR/AP report (backport #42477)
2024-07-26 20:10:43 +05:30
ruthra kumar
bb66126dfa test: invoice cost center reported in AR/AP report
(cherry picked from commit 9a0894fd65)
2024-07-26 14:14:32 +00:00
ruthra kumar
0e2abbd08e fix: incorrect cost_center on AR/AP report
(cherry picked from commit 3e19041fa3)
2024-07-26 14:14:32 +00:00
Smit Vora
ff78fab176 Merge pull request #42483 from frappe/mergify/bp/version-14-hotfix/pr-42305
fix: consider payment entries for checking if tds is deducted (backport #42305)
2024-07-26 15:17:05 +05:30
Smit Vora
04840762dd Merge pull request #42481 from frappe/mergify/bp/version-14-hotfix/pr-42444
fix: parenttype in purchase and sales item query (backport #42444)
2024-07-26 15:16:44 +05:30
ljain112
e7432fc60d fix: consider payment entries for checking if tds is deducted
(cherry picked from commit 40b59de4cd)
2024-07-26 09:11:50 +00:00
ljain112
97f2e88f4c fix: parenttype in item wise purchase and sales register
(cherry picked from commit 35981b8730)
2024-07-26 08:56:42 +00:00
ruthra kumar
37f24ae763 Merge pull request #42473 from frappe/mergify/bp/version-14-hotfix/pr-42472
refactor: index on Purchase Invoice 'release_date' (backport #42472)
2024-07-25 21:51:16 +05:30
ruthra kumar
25b9127bae refactor: index on Purchase Invoice 'release_date'
(cherry picked from commit 764dd12b10)
2024-07-25 15:57:59 +00:00
ruthra kumar
6e74e6f314 Merge pull request #42464 from frappe/mergify/bp/version-14-hotfix/pr-42462
refactor: provision for re-evaluating Exchange Rates in monthly frequency (backport #42462)
2024-07-24 22:11:22 +05:30
ruthra kumar
240118ee8b chore: resolve conflict 2024-07-24 18:48:32 +05:30
ruthra kumar
c1fd95ac66 refactor: hooks for monthly re-evaluation jobs
(cherry picked from commit fc4e5f165c)

# Conflicts:
#	erpnext/hooks.py
2024-07-24 13:15:15 +00:00
ruthra kumar
8e340bb7fd refactor: provision for monthly re-evaluation
(cherry picked from commit ce2b9e0f1a)

# Conflicts:
#	erpnext/setup/doctype/company/company.json
#	erpnext/setup/doctype/company/company.py
2024-07-24 13:15:15 +00:00
ruthra kumar
96a6172999 Merge pull request #42455 from frappe/mergify/bp/version-14-hotfix/pr-42390
refactor: cleaning up stale code related to reposting (backport #42390)
2024-07-24 17:35:24 +05:30
Smit Vora
d0d587432d Merge pull request #42439 from frappe/mergify/bp/version-14-hotfix/pr-42377
fix: ignore duplicates while creating default templates (backport #42377)
2024-07-24 14:25:46 +05:30
ruthra kumar
07509b5e99 chore: resolve conflicts 2024-07-24 13:07:15 +05:30
ruthra kumar
99d5b6dc71 chore: resolve conflicts 2024-07-24 13:04:57 +05:30
ruthra kumar
56b1582027 refactor(test): remove assert on 'repost_required'
(cherry picked from commit e71cb4eab7)
2024-07-24 07:29:10 +00:00
ruthra kumar
149109649d refactor: remove attribute check on 'repost_required'
(cherry picked from commit 07fc952a43)

# Conflicts:
#	erpnext/accounts/doctype/journal_entry/journal_entry.py
#	erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
#	erpnext/accounts/doctype/sales_invoice/sales_invoice.py
2024-07-24 07:29:09 +00:00
ruthra kumar
0284328e2c refactor: repost without checking on flag
(cherry picked from commit 09f429ffba)
2024-07-24 07:29:09 +00:00
ruthra kumar
a243873ab0 chore: remove stale UI code related to repost
(cherry picked from commit fe46e1d089)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
2024-07-24 07:29:09 +00:00
ruthra kumar
197e043fc9 chore: remove 'repost_required' from Journal Entry
(cherry picked from commit e81373bb6a)

# Conflicts:
#	erpnext/accounts/doctype/journal_entry/journal_entry.json
#	erpnext/accounts/doctype/journal_entry/journal_entry.py
2024-07-24 07:29:09 +00:00
ruthra kumar
925a164101 chore: remove 'repost_required' from purchase invoice
(cherry picked from commit a467888a67)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
#	erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
2024-07-24 07:29:08 +00:00
ruthra kumar
8f1a4b9717 chore: remove stale 'repost_required' flag from sales invoice
(cherry picked from commit 06c5334f2a)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice/sales_invoice.json
#	erpnext/accounts/doctype/sales_invoice/sales_invoice.py
2024-07-24 07:29:08 +00:00
ruthra kumar
67d4020241 chore: remove stale code from sales invoice
(cherry picked from commit f3fda9ce98)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice/sales_invoice.js
2024-07-24 07:29:08 +00:00
Frappe PR Bot
fe4b2e36cc chore(release): Bumped to Version 14.70.12
## [14.70.12](https://github.com/frappe/erpnext/compare/v14.70.11...v14.70.12) (2024-07-24)

### Bug Fixes

* missing cr/dr notes on payment reconciliation ([90ee21f](90ee21f868))
* remove proprietorship and update it with individual (backport [#42307](https://github.com/frappe/erpnext/issues/42307)) ([8c1f619](8c1f6196b8))
* set pos data if not return doc ([6ecb064](6ecb064264))
* Show the rows in AR/AP report where outstanding equals to 0.01 ([2936988](2936988cc6))
2024-07-24 07:26:52 +00:00
ruthra kumar
6759b90f85 Merge pull request #42448 from frappe/version-14-hotfix
chore: release v14
2024-07-24 12:55:36 +05:30
Smit Vora
99bc8e849c fix: ignore duplicates while creating default templates
(cherry picked from commit cf55c2ab3d)
2024-07-23 04:56:31 +00:00
ruthra kumar
7a25d33547 Merge pull request #42408 from frappe/mergify/bp/version-14-hotfix/pr-42407
test: basic test case for item-wise purchase register (backport #42407)
2024-07-21 20:08:19 +05:30
ruthra kumar
2466e28bf5 test: basic test case for item-wise purchase register
(cherry picked from commit c3c5d3f615)
2024-07-19 11:21:08 +00:00
ruthra kumar
1096528bb9 Merge pull request #42401 from frappe/mergify/bp/version-14-hotfix/pr-42386
fix: set pos data if not return doc (backport #42386)
2024-07-19 10:57:20 +05:30
ljain112
6ecb064264 fix: set pos data if not return doc
(cherry picked from commit 65d672da65)
2024-07-19 04:56:26 +00:00
ruthra kumar
92300b27c9 Merge pull request #42384 from frappe/mergify/bp/version-14-hotfix/pr-42374
fix: Show the rows in AR/AP report where outstanding equals to 0.01 (backport #42374)
2024-07-18 15:08:55 +05:30
ruthra kumar
4fa9626de0 chore: resolve conflict 2024-07-18 14:41:09 +05:30
ruthra kumar
9b828b829a test: AR/AP report on miniscule outstanding
(cherry picked from commit bb9e42cce2)
2024-07-18 08:15:39 +00:00
Nabin Hait
2936988cc6 fix: Show the rows in AR/AP report where outstanding equals to 0.01
(cherry picked from commit e1dedc5402)

# Conflicts:
#	erpnext/patches.txt
2024-07-18 08:15:38 +00:00
ruthra kumar
9fca232578 Merge pull request #42382 from frappe/mergify/bp/version-14-hotfix/pr-42369
fix: missing cr/dr notes on payment reconciliation (backport #42369)
2024-07-18 13:43:36 +05:30
ruthra kumar
fac22e93d0 chore: resolve conflict 2024-07-18 12:44:34 +05:30
ruthra kumar
3109efaf09 test: payment filter should not affect dr/cr notes
(cherry picked from commit 2d686c06ea)

# Conflicts:
#	erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py
2024-07-18 07:12:45 +00:00
ruthra kumar
90ee21f868 fix: missing cr/dr notes on payment reconciliation
(cherry picked from commit a30af68e9e)
2024-07-18 07:12:45 +00:00
Smit Vora
36c46bb344 Merge pull request #42366 from vorasmit/update-prop-backport
fix: remove redundant proprietorship field from customer type and supplier type (backport #42307)
2024-07-17 18:55:40 +05:30
Sanket322
8c1f6196b8 fix: remove proprietorship and update it with individual (backport #42307) 2024-07-17 14:03:52 +05:30
Frappe PR Bot
12a31de25a chore(release): Bumped to Version 14.70.11
## [14.70.11](https://github.com/frappe/erpnext/compare/v14.70.10...v14.70.11) (2024-07-17)

### Bug Fixes

* missing discount on POS Credit Notes ([ac48c3d](ac48c3d4e7))
2024-07-17 05:11:39 +00:00
ruthra kumar
3b9400755e Merge pull request #42353 from frappe/version-14-hotfix
chore: release v14
2024-07-17 10:40:24 +05:30
ruthra kumar
2ce7300c3c Merge pull request #42337 from frappe/mergify/bp/version-14-hotfix/pr-42294
refactor: make reposting implicit (backport #42294)
2024-07-15 15:41:23 +05:30
ruthra kumar
b96b3b51b6 chore: resolve conflicts 2024-07-15 15:22:06 +05:30
ruthra kumar
8f03769bf2 chore: contextual comments
(cherry picked from commit 794a62aecb)
2024-07-15 09:41:56 +00:00
ruthra kumar
d20f3ab492 refactor(test): reposting happens implicitly
(cherry picked from commit c283cda169)
2024-07-15 09:41:56 +00:00
ruthra kumar
980ca1d8c5 refactor(test): no need to assert repost_required flag
Reposting happens implicitly upon 'Update After Submit'

(cherry picked from commit 8f135e9859)

# Conflicts:
#	erpnext/accounts/doctype/journal_entry/test_journal_entry.py
2024-07-15 09:41:56 +00:00
ruthra kumar
4668a2d7d8 refactor: make reposting implicit
(cherry picked from commit 722ef92324)

# Conflicts:
#	erpnext/accounts/doctype/journal_entry/journal_entry.py
2024-07-15 09:41:56 +00:00
ruthra kumar
1a7efbb654 fix(gross profit): incorrect valuation rate on different warehouses
(cherry picked from commit f9d2dd0a62)
2024-07-15 08:50:15 +00:00
ruthra kumar
ccc2a47e73 Merge pull request #42290 from frappe/mergify/bp/version-14-hotfix/pr-42192
refactor: tests for item wise sales register report (backport #42192)
2024-07-12 09:28:05 +05:30
ruthra kumar
f98716cc2a refactor(test): clear old records 2024-07-11 21:03:29 +05:30
ruthra kumar
7903e8d669 refactor(test): use each instance UOM for assertion
(cherry picked from commit cf4fbfb601)
2024-07-11 12:34:13 +00:00
ruthra kumar
9a50a0a129 refactor: test suite for item-wise sales register
(cherry picked from commit 3aaa22e672)
2024-07-11 12:34:12 +00:00
ruthra kumar
1646517dc4 chore: rename test suite for payable report
(cherry picked from commit 9474f72776)
2024-07-11 12:34:12 +00:00
ruthra kumar
38811e792c Merge pull request #42288 from frappe/mergify/bp/version-14-hotfix/pr-42287
fix: missing discount on POS Credit Notes (backport #42287)
2024-07-11 17:33:26 +05:30
ruthra kumar
edfb408464 chore: resolve conflict 2024-07-11 17:31:20 +05:30
ruthra kumar
ac48c3d4e7 fix: missing discount on POS Credit Notes
(cherry picked from commit 1049550951)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice/sales_invoice.js
2024-07-11 11:58:57 +00:00
Frappe PR Bot
1d66b7e5a3 chore(release): Bumped to Version 14.70.10
## [14.70.10](https://github.com/frappe/erpnext/compare/v14.70.9...v14.70.10) (2024-07-10)

### Bug Fixes

* add missing german translations ([d5c1c62](d5c1c62622))
* empty item-wise sales/purchase register reports on initial load ([13895fa](13895fa060))
* fetch expence account from asset category ([4d6a71a](4d6a71ab4b))
* group by in item-wise purchase register ([62ad466](62ad466a3b))
* **Holiday List:** sort holidays on save to avoid disorienting the user (backport [#42236](https://github.com/frappe/erpnext/issues/42236)) ([#42251](https://github.com/frappe/erpnext/issues/42251)) ([fcf6500](fcf6500144))
* manual pick allow to pick more than available stock (backport [#42155](https://github.com/frappe/erpnext/issues/42155)) ([#42158](https://github.com/frappe/erpnext/issues/42158)) ([454e147](454e147592))
* multiple free items on same Item Group ([701dd9e](701dd9e19b))
* removed max discount validation for sales return ([4195c50](4195c50f02))
* stock qty validation in SCR (backport [#42124](https://github.com/frappe/erpnext/issues/42124)) ([#42224](https://github.com/frappe/erpnext/issues/42224)) ([e2f8e02](e2f8e02c73))
* tax on stock_rbnb on repost of Purchase Receipt ([106c154](106c154a16))
* **tds:** use doctype reference when mapping keys across multiple doctypes ([51cbbee](51cbbee4ca))
* updated logic for calculating tax_withholding_net_total in payment entry ([49e5066](49e50662b6))
* use standard method to get `_doc_before_save` ([9fde733](9fde7330e0))
2024-07-10 10:43:56 +00:00
ruthra kumar
ab9bde86f9 Merge pull request #42254 from frappe/version-14-hotfix
chore: release v14
2024-07-10 16:12:40 +05:30
ruthra kumar
5c75bb8775 Merge pull request #42271 from frappe/mergify/bp/version-14-hotfix/pr-42247
fix: don't merge tax into stock account on purchase receipt repost (backport #42247)
2024-07-10 15:52:00 +05:30
ruthra kumar
115a0123ed chore: resolve conflict 2024-07-10 15:30:24 +05:30
ruthra kumar
fdf1dfe46e test: tax account heads on PR report without LCV
(cherry picked from commit 9562628ed6)

# Conflicts:
#	erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
2024-07-10 09:58:30 +00:00
ruthra kumar
106c154a16 fix: tax on stock_rbnb on repost of Purchase Receipt
(cherry picked from commit 8633080dff)
2024-07-10 09:58:30 +00:00
Smit Vora
00e8b862dd Merge pull request #42264 from frappe/mergify/bp/version-14-hotfix/pr-42127
fix: removed max discount validation for sales return (backport #42127)
2024-07-10 15:22:51 +05:30
ljain112
4195c50f02 fix: removed max discount validation for sales return
(cherry picked from commit db807d433b)
2024-07-10 07:01:54 +00:00
Smit Vora
fdb8e5b379 Merge pull request #42262 from ljain112/fix-tds-backport
fix(tds): use doctype reference when mapping keys across multiple doctype (backport #42258)
2024-07-10 12:27:28 +05:30
ljain112
51cbbee4ca fix(tds): use doctype reference when mapping keys across multiple doctypes 2024-07-10 11:55:35 +05:30
Sagar Vora
5000c09759 Merge pull request #42261 from frappe/mergify/bp/version-14-hotfix/pr-42060
fix: updated logic for calculating tax_withholding_net_total in payment entry (backport #42060)
2024-07-10 11:24:31 +05:30
ljain112
49e50662b6 fix: updated logic for calculating tax_withholding_net_total in payment entry
(cherry picked from commit c8a34cde7f)
2024-07-10 05:53:37 +00:00
ruthra kumar
f2f1f32826 Merge pull request #42163 from frappe/mergify/bp/version-14-hotfix/pr-42162
refactor: remove obsolete function call (backport #42162)
2024-07-10 10:28:37 +05:30
mergify[bot]
fcf6500144 fix(Holiday List): sort holidays on save to avoid disorienting the user (backport #42236) (#42251)
* fix(Holiday List): sort holidays on save to avoid disorienting the user (#42236)

fix: sort holidays on save to avoid disorienting the user
(cherry picked from commit ad137250fc)

# Conflicts:
#	erpnext/setup/doctype/holiday_list/holiday_list.py

* chore: fix conflicts

---------

Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2024-07-09 18:44:08 +05:30
Raffael Meyer
f2d5a69af4 Merge pull request #42237 from frappe/mergify/bp/version-14-hotfix/pr-42235
fix: add missing german translations (backport #42235)
2024-07-08 19:53:18 +02:00
barredterra
d5c1c62622 fix: add missing german translations
(cherry picked from commit 2f89461ace)
2024-07-08 17:51:41 +00:00
mergify[bot]
e2f8e02c73 fix: stock qty validation in SCR (backport #42124) (#42224)
* fix: stock qty validation in SCR (#42124)

(cherry picked from commit 99f2735ad3)

# Conflicts:
#	erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-08 17:06:23 +05:30
Frappe PR Bot
30cba7ee2c chore(release): Bumped to Version 14.70.9
## [14.70.9](https://github.com/frappe/erpnext/compare/v14.70.8...v14.70.9) (2024-07-05)

### Bug Fixes

* blank item-wise sales/purchase register reports on first load ([0f275a9](0f275a9ff0))
* group by in item-wise purchase register ([1840267](18402677da))
2024-07-05 02:47:14 +00:00
ruthra kumar
21a60c9927 Merge pull request #42190 from frappe/mergify/bp/version-14/pr-41975
fix: group by in item-wise purchase register (backport #41975)
2024-07-05 08:15:53 +05:30
ruthra kumar
0f275a9ff0 fix: blank item-wise sales/purchase register reports on first load 2024-07-05 07:50:37 +05:30
Nihantra C. Patel
18402677da fix: group by in item-wise purchase register
(cherry picked from commit 3fab00135b)
2024-07-05 02:05:26 +00:00
ruthra kumar
e9357c193d Merge pull request #42184 from frappe/mergify/bp/version-14-hotfix/pr-42183
fix: empty item-wise sales/purchase register reports on initial load (backport #42183)
2024-07-04 15:10:30 +05:30
ruthra kumar
13895fa060 fix: empty item-wise sales/purchase register reports on initial load
(cherry picked from commit ee862126e4)
2024-07-04 09:36:58 +00:00
ruthra kumar
64f8498576 Merge pull request #42180 from frappe/mergify/bp/version-14-hotfix/pr-41975
fix: group by in item-wise purchase register (backport #41975)
2024-07-04 14:59:50 +05:30
Nihantra C. Patel
62ad466a3b fix: group by in item-wise purchase register
(cherry picked from commit 3fab00135b)
2024-07-04 09:09:07 +00:00
Khushi Rawat
45899b3017 Merge pull request #42174 from khushi8112/fetch-expence-account-from-asset-category
fix: fetch expense account from asset category
2024-07-04 13:14:39 +05:30
ruthra kumar
d92a042bf7 Merge pull request #42172 from frappe/mergify/bp/version-14-hotfix/pr-42143
refactor: validation to prevent recursion with mixed conditions (backport #42143)
2024-07-04 09:20:35 +05:30
Khushi Rawat
4d6a71ab4b fix: fetch expence account from asset category 2024-07-04 01:45:01 +05:30
ruthra kumar
d5fa968078 chore: resolve conflicts 2024-07-03 20:59:27 +05:30
ruthra kumar
71cbebd31b test: validation on mixed condition and recursion on pricing rule
(cherry picked from commit eb4af58bf0)

# Conflicts:
#	erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
2024-07-03 15:26:56 +00:00
ruthra kumar
99317768f6 test: validation on mixed condition with recursion
(cherry picked from commit 9bd4e7b709)

# Conflicts:
#	erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py
2024-07-03 15:26:56 +00:00
ruthra kumar
9fde7330e0 fix: use standard method to get _doc_before_save
(cherry picked from commit 9d7be293ae)
2024-07-03 15:26:56 +00:00
ruthra kumar
49fb6bec6a refactor: validation to prevent recursion with mixed conditions
(cherry picked from commit 406dfd528f)
2024-07-03 15:26:55 +00:00
ruthra kumar
0f1f5b6f3d Merge pull request #42169 from frappe/mergify/bp/version-14-hotfix/pr-42165
fix: multiple free items on same Item Group (backport #42165)
2024-07-03 20:55:34 +05:30
mergify[bot]
454e147592 fix: manual pick allow to pick more than available stock (backport #42155) (#42158)
* fix: manual pick allow to pick more than available stock (#42155)

(cherry picked from commit 938dd4b2aa)

# Conflicts:
#	erpnext/stock/doctype/pick_list/test_pick_list.py

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-03 19:28:18 +05:30
ruthra kumar
701dd9e19b fix: multiple free items on same Item Group
(cherry picked from commit c4ae0d283f)
2024-07-03 12:28:35 +00:00
Markus Lobedann
2cf82561f6 refactor: remove obsolete function call (#42162)
(cherry picked from commit 4512432816)
2024-07-03 10:22:23 +00:00
Frappe PR Bot
f2d113dcf2 chore(release): Bumped to Version 14.70.8
## [14.70.8](https://github.com/frappe/erpnext/compare/v14.70.7...v14.70.8) (2024-07-03)

### Bug Fixes

* always post to tax account heads if LCV is booked ([650b25f](650b25fc23))
* asset value correction in fixed asset register ([#41943](https://github.com/frappe/erpnext/issues/41943)) ([a2ee463](a2ee4631cc))
* consistent query field name in item wise purchase register with item wise sales register ([9665212](9665212607))
* **Delivery Note:** only show permitted actions ([0aa246c](0aa246c39e))
* dynamic_link for party in customer and supplier dashboard ([6de7a8d](6de7a8de10))
* incorrect against_account upon reposting ([6028a19](6028a19e6b))
* Re-open allows SO's to be over credit limit ([952a7b4](952a7b46d5))
* **Sales Order:** only show permitted actions ([5468a3b](5468a3b0b7))
* timeout while cancelling LCV ([#42030](https://github.com/frappe/erpnext/issues/42030)) ([21bf7fd](21bf7fd1f8))
2024-07-03 05:02:59 +00:00
ruthra kumar
97e4495f1f Merge pull request #42141 from frappe/version-14-hotfix
chore: release v14
2024-07-03 10:31:45 +05:30
ruthra kumar
26c99351fd Merge pull request #42150 from frappe/mergify/bp/version-14-hotfix/pr-42073
fix: always post to tax account heads if LCV is booked (backport #42073)
2024-07-03 07:44:36 +05:30
ruthra kumar
4d99449aa8 chore: resolve conflict 2024-07-03 07:16:08 +05:30
ruthra kumar
72b93805fd refactor(test): fix flaky test
(cherry picked from commit 0e256b8b29)
2024-07-03 01:34:50 +00:00
ruthra kumar
f31ed75578 refactor(test): cleanup test data
(cherry picked from commit 6ba6b5aa33)
2024-07-03 01:34:50 +00:00
ruthra kumar
003d7e9f3e test: Repost should not merge expense accounts from LCV
(cherry picked from commit fa56555150)

# Conflicts:
#	erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
2024-07-03 01:34:49 +00:00
ruthra kumar
650b25fc23 fix: always post to tax account heads if LCV is booked
(cherry picked from commit 0fcd5d5130)
2024-07-03 01:34:49 +00:00
ruthra kumar
d5366c5873 Merge pull request #42131 from frappe/mergify/bp/version-14-hotfix/pr-42126
fix: Re-open allows SO's to be over credit limit (backport #42126)
2024-07-01 17:25:50 +05:30
ruthra kumar
bf36b4fa11 chore: resolve conflict 2024-07-01 17:05:56 +05:30
ruthra kumar
f4fc26b52e test: credit check on Sales Order re-open
(cherry picked from commit 60694e09c4)

# Conflicts:
#	erpnext/selling/doctype/sales_order/test_sales_order.py
2024-07-01 11:33:36 +00:00
ruthra kumar
952a7b46d5 fix: Re-open allows SO's to be over credit limit
(cherry picked from commit 5eed781263)
2024-07-01 11:33:35 +00:00
Smit Vora
cc9857affd Merge pull request #42064 from frappe/mergify/bp/version-14-hotfix/pr-41979
fix: dynamic_link for party in customer and supplier dashboard (backport #41979)
2024-06-28 13:04:35 +05:30
ljain112
6de7a8de10 fix: dynamic_link for party in customer and supplier dashboard
(cherry picked from commit c0a8800832)
2024-06-28 06:35:17 +00:00
Frappe PR Bot
b593f57637 chore: release v14 (#42023)
* fix(Sales Order): only show permitted actions

(cherry picked from commit c29d955371)

# Conflicts:
#	erpnext/selling/doctype/sales_order/sales_order.js

* fix(Delivery Note): only show permitted actions

(cherry picked from commit 418bdc1dcc)

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

* fix: consistent query field name in item wise purchase register with item wise sales register

* chore: resolve conflicts

* refactor: remove use of can_create for Payment Request (#41647)

(cherry picked from commit 47bc5691a1)

* fix: incorrect against_account upon reposting

(cherry picked from commit 20c4098399)

* fix: asset value correction in fixed asset register (#41943)

* fix: timeout while cancelling LCV (#42030)

fix: timeout while canelling LCV

---------

Co-authored-by: barredterra <14891507+barredterra@users.noreply.github.com>
Co-authored-by: ljain112 <ljain112@gmail.com>
Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
Co-authored-by: ruthra kumar <ruthra@erpnext.com>
Co-authored-by: Khushi Rawat <142375893+khushi8112@users.noreply.github.com>
2024-06-26 18:02:23 +05:30
rohitwaghchaure
21bf7fd1f8 fix: timeout while cancelling LCV (#42030)
fix: timeout while canelling LCV
2024-06-25 17:38:03 +05:30
Khushi Rawat
a2ee4631cc fix: asset value correction in fixed asset register (#41943) 2024-06-25 16:04:20 +05:30
ruthra kumar
92ca6c6538 Merge pull request #41983 from frappe/mergify/bp/version-14-hotfix/pr-41981
fix: incorrect against_account upon reposting (backport #41981)
2024-06-21 19:10:41 +05:30
ruthra kumar
6028a19e6b fix: incorrect against_account upon reposting
(cherry picked from commit 20c4098399)
2024-06-21 12:30:39 +00:00
Raffael Meyer
87dc586543 Merge pull request #41440 from frappe/mergify/bp/version-14-hotfix/pr-41384
fix(SO, DN): only show permitted actions (backport #41384)
2024-06-20 15:51:18 +02:00
barredterra
22216b275c refactor: remove use of can_create for Payment Request (#41647)
(cherry picked from commit 47bc5691a1)
2024-06-20 14:21:30 +02:00
barredterra
0e6edf763e chore: resolve conflicts 2024-06-20 14:15:26 +02:00
barredterra
e77534ba2b Merge remote-tracking branch 'upstream/version-14-hotfix' into mergify/bp/version-14-hotfix/pr-41384 2024-06-20 13:47:58 +02:00
rohitwaghchaure
ae14b86a8c Merge pull request #41954 from ljain112/fix-41927
fix: consistent query field name in item wise purchase register with …
2024-06-20 14:03:12 +05:30
ljain112
9665212607 fix: consistent query field name in item wise purchase register with item wise sales register 2024-06-20 11:11:43 +05:30
barredterra
0aa246c39e fix(Delivery Note): only show permitted actions
(cherry picked from commit 418bdc1dcc)

# Conflicts:
#	erpnext/stock/doctype/delivery_note/delivery_note.js
2024-05-13 13:43:07 +00:00
barredterra
5468a3b0b7 fix(Sales Order): only show permitted actions
(cherry picked from commit c29d955371)

# Conflicts:
#	erpnext/selling/doctype/sales_order/sales_order.js
2024-05-13 13:43:07 +00:00
140 changed files with 3426 additions and 682 deletions

View File

@@ -23,7 +23,7 @@ jobs:
run: git clone --depth 1 https://github.com/frappe/semgrep-rules.git frappe-semgrep-rules
- name: Download semgrep
run: pip install semgrep==0.97.0
run: pip install semgrep
- name: Run Semgrep rules
run: semgrep ci --config ./frappe-semgrep-rules/rules --config r/python.lang.correctness

View File

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

View File

@@ -360,45 +360,45 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
)
if not amount:
return
gl_posting_date = end_date
prev_posting_date = None
# check if books nor frozen till endate:
if accounts_frozen_upto and getdate(end_date) <= getdate(accounts_frozen_upto):
gl_posting_date = get_last_day(add_days(accounts_frozen_upto, 1))
prev_posting_date = end_date
if via_journal_entry:
book_revenue_via_journal_entry(
doc,
credit_account,
debit_account,
amount,
base_amount,
gl_posting_date,
project,
account_currency,
item.cost_center,
item,
deferred_process,
submit_journal_entry,
)
else:
make_gl_entries(
doc,
credit_account,
debit_account,
against,
amount,
base_amount,
gl_posting_date,
project,
account_currency,
item.cost_center,
item,
deferred_process,
)
gl_posting_date = end_date
prev_posting_date = None
# check if books nor frozen till endate:
if accounts_frozen_upto and getdate(end_date) <= getdate(accounts_frozen_upto):
gl_posting_date = get_last_day(add_days(accounts_frozen_upto, 1))
prev_posting_date = end_date
if via_journal_entry:
book_revenue_via_journal_entry(
doc,
credit_account,
debit_account,
amount,
base_amount,
gl_posting_date,
project,
account_currency,
item.cost_center,
item,
deferred_process,
submit_journal_entry,
)
else:
make_gl_entries(
doc,
credit_account,
debit_account,
against,
amount,
base_amount,
gl_posting_date,
project,
account_currency,
item.cost_center,
item,
deferred_process,
)
# Returned in case of any errors because it tries to submit the same record again and again in case of errors
if frappe.flags.deferred_accounting_error:

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

@@ -557,7 +557,7 @@
"table_fieldname": "payment_entries"
}
],
"modified": "2023-11-23 12:11:04.128015",
"modified": "2024-07-18 15:32:29.413598",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry",

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],
@@ -145,10 +153,27 @@ frappe.ui.form.on('Payment Entry', {
filters: filters
};
});
},
refresh: function(frm) {
erpnext.hide_company();
frm.set_query("sales_taxes_and_charges_template", function () {
return {
filters: {
company: frm.doc.company,
disabled: false,
},
};
});
frm.set_query("purchase_taxes_and_charges_template", function () {
return {
filters: {
company: frm.doc.company,
disabled: false,
},
};
});
},
refresh: function (frm) {
erpnext.hide_company(frm);
frm.events.hide_unhide_fields(frm);
frm.events.set_dynamic_labels(frm);
frm.events.show_general_ledger(frm);
@@ -179,7 +204,7 @@ frappe.ui.form.on('Payment Entry', {
},
hide_unhide_fields: 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.toggle_display("source_exchange_rate",
(frm.doc.paid_amount && frm.doc.paid_from_account_currency != company_currency));
@@ -225,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",
@@ -309,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,
},
};
}
});
@@ -486,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

@@ -74,7 +74,6 @@ class PaymentEntry(AccountsController):
self.set_exchange_rate()
self.validate_mandatory()
self.validate_reference_documents()
self.set_tax_withholding()
self.set_amounts()
self.validate_amounts()
self.apply_taxes()
@@ -89,6 +88,7 @@ class PaymentEntry(AccountsController):
self.validate_allocated_amount()
self.validate_paid_invoices()
self.ensure_supplier_is_not_blocked()
self.set_tax_withholding()
self.set_status()
def on_submit(self):
@@ -674,9 +674,7 @@ class PaymentEntry(AccountsController):
if not self.apply_tax_withholding_amount:
return
order_amount = self.get_order_net_total()
net_total = flt(order_amount) + flt(self.unallocated_amount)
net_total = self.calculate_tax_withholding_net_total()
# Adding args as purchase invoice to get TDS amount
args = frappe._dict(
@@ -720,7 +718,26 @@ class PaymentEntry(AccountsController):
for d in to_remove:
self.remove(d)
def get_order_net_total(self):
def calculate_tax_withholding_net_total(self):
net_total = 0
order_details = self.get_order_wise_tax_withholding_net_total()
for d in self.references:
tax_withholding_net_total = order_details.get(d.reference_name)
if not tax_withholding_net_total:
continue
net_taxable_outstanding = max(
0, d.outstanding_amount - (d.total_amount - tax_withholding_net_total)
)
net_total += min(net_taxable_outstanding, d.allocated_amount)
net_total += self.unallocated_amount
return net_total
def get_order_wise_tax_withholding_net_total(self):
if self.party_type == "Supplier":
doctype = "Purchase Order"
else:
@@ -728,12 +745,15 @@ class PaymentEntry(AccountsController):
docnames = [d.reference_name for d in self.references if d.reference_doctype == doctype]
tax_withholding_net_total = frappe.db.get_value(
doctype, {"name": ["in", docnames]}, ["sum(base_tax_withholding_net_total)"]
return frappe._dict(
frappe.db.get_all(
doctype,
filters={"name": ["in", docnames]},
fields=["name", "base_tax_withholding_net_total"],
as_list=True,
)
)
return tax_withholding_net_total
def apply_taxes(self):
self.initialize_taxes()
self.determine_exclusive_rate()
@@ -1543,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

@@ -36,7 +36,7 @@ frappe.ui.form.on("Payment Order", {
// payment Entry
if (frm.doc.docstatus === 1 && frm.doc.payment_order_type === "Payment Request") {
frm.add_custom_button(__("Create Payment Entries"), function () {
frm.add_custom_button(__("Create Journal Entries"), function () {
frm.trigger("make_payment_records");
});
}

View File

@@ -200,6 +200,7 @@ class PaymentReconciliation(Document):
conditions.append(doc.docstatus == 1)
conditions.append(doc[frappe.scrub(self.party_type)] == self.party)
conditions.append(doc.is_return == 1)
conditions.append(doc.outstanding_amount != 0)
if self.payment_name:
conditions.append(doc.name.like(f"%{self.payment_name}%"))

View File

@@ -1335,6 +1335,46 @@ class TestPaymentReconciliation(FrappeTestCase):
# Should not raise frappe.exceptions.ValidationError: Payment Entry has been modified after you pulled it. Please pull it again.
pr.reconcile()
def test_cr_note_payment_limit_filter(self):
transaction_date = nowdate()
amount = 100
for _ in range(6):
self.create_sales_invoice(qty=1, rate=amount, posting_date=transaction_date)
cr_note = self.create_sales_invoice(
qty=-1, rate=amount, posting_date=transaction_date, do_not_save=True, do_not_submit=True
)
cr_note.is_return = 1
cr_note = cr_note.save().submit()
pr = self.create_payment_reconciliation()
pr.get_unreconciled_entries()
self.assertEqual(len(pr.invoices), 6)
self.assertEqual(len(pr.payments), 6)
invoices = [x.as_dict() for x in pr.get("invoices")]
payments = [x.as_dict() for x in pr.get("payments")]
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
pr.reconcile()
pr.get_unreconciled_entries()
self.assertEqual(pr.get("invoices"), [])
self.assertEqual(pr.get("payments"), [])
self.create_sales_invoice(qty=1, rate=amount, posting_date=transaction_date)
cr_note = self.create_sales_invoice(
qty=-1, rate=amount, posting_date=transaction_date, do_not_save=True, do_not_submit=True
)
cr_note.is_return = 1
cr_note = cr_note.save().submit()
# Limit should not affect in fetching the unallocated cr_note
pr.invoice_limit = 5
pr.payment_limit = 5
pr.get_unreconciled_entries()
self.assertEqual(len(pr.invoices), 1)
self.assertEqual(len(pr.payments), 1)
def make_customer(customer_name, currency=None):
if not frappe.db.exists("Customer", customer_name):

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
@@ -284,6 +284,17 @@ class PaymentRequest(Document):
payment_entry.received_amount = amount
payment_entry.get("references")[0].allocated_amount = amount
# Update 'Paid Amount' on Forex transactions
if self.currency != ref_doc.company_currency:
if (
self.payment_request_type == "Outward"
and payment_entry.paid_from_account_currency == ref_doc.company_currency
and payment_entry.paid_from_account_currency != payment_entry.paid_to_account_currency
):
payment_entry.paid_amount = payment_entry.base_paid_amount = (
payment_entry.target_exchange_rate * payment_entry.received_amount
)
for dimension in get_accounting_dimensions():
payment_entry.update({dimension: self.get(dimension)})
@@ -509,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

@@ -4,10 +4,12 @@
import unittest
import frappe
from frappe.tests.utils import FrappeTestCase
from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
from erpnext.setup.utils import get_exchange_rate
@@ -32,7 +34,7 @@ payment_method = [
]
class TestPaymentRequest(unittest.TestCase):
class TestPaymentRequest(FrappeTestCase):
def setUp(self):
if not frappe.db.get_value("Payment Gateway", payment_gateway["gateway"], "name"):
frappe.get_doc(payment_gateway).insert(ignore_permissions=True)
@@ -260,3 +262,19 @@ class TestPaymentRequest(unittest.TestCase):
# Try to make Payment Request more than SO amount, should give validation
pr2.grand_total = 900
self.assertRaises(frappe.ValidationError, pr2.save)
def test_conversion_on_foreign_currency_accounts(self):
po_doc = create_purchase_order(supplier="_Test Supplier USD", currency="USD", do_not_submit=1)
po_doc.conversion_rate = 80
po_doc.items[0].qty = 1
po_doc.items[0].rate = 10
po_doc.save().submit()
pr = make_payment_request(dt=po_doc.doctype, dn=po_doc.name, recipient_id="nabin@erpnext.com")
pr = frappe.get_doc(pr).save().submit()
pe = pr.create_payment_entry()
self.assertEqual(pe.base_paid_amount, 800)
self.assertEqual(pe.paid_amount, 800)
self.assertEqual(pe.base_received_amount, 800)
self.assertEqual(pe.received_amount, 10)

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

@@ -31,6 +31,7 @@ class PricingRule(Document):
self.validate_price_list_with_currency()
self.validate_dates()
self.validate_condition()
self.validate_mixed_with_recursion()
if not self.margin_type:
self.margin_rate_or_amount = 0.0
@@ -201,6 +202,10 @@ class PricingRule(Document):
):
frappe.throw(_("Invalid condition expression"))
def validate_mixed_with_recursion(self):
if self.mixed_conditions and self.is_recursive:
frappe.throw(_("Recursive Discounts with Mixed condition is not supported by the system"))
# --------------------------------------------------------------------------------

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()
@@ -1087,6 +1090,81 @@ class TestPricingRule(unittest.TestCase):
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 1")
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 2")
def test_validation_on_mixed_condition_with_recursion(self):
pricing_rule = make_pricing_rule(
discount_percentage=10,
selling=1,
priority=2,
min_qty=4,
title="_Test Pricing Rule with Min Qty - 2",
)
pricing_rule.mixed_conditions = True
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

@@ -16,6 +16,7 @@
"cost_center",
"territory",
"ignore_exchange_rate_revaluation_journals",
"ignore_cr_dr_notes",
"column_break_14",
"to_date",
"finance_book",
@@ -381,10 +382,16 @@
"fieldname": "ignore_exchange_rate_revaluation_journals",
"fieldtype": "Check",
"label": "Ignore Exchange Rate Revaluation Journals"
},
{
"default": "0",
"fieldname": "ignore_cr_dr_notes",
"fieldtype": "Check",
"label": "Ignore System Generated Credit / Debit Notes"
}
],
"links": [],
"modified": "2023-12-18 12:20:08.965120",
"modified": "2024-08-13 10:41:18.381165",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Process Statement Of Accounts",

View File

@@ -79,6 +79,9 @@ def get_statement_dict(doc, get_statement_dict=False):
if doc.ignore_exchange_rate_revaluation_journals:
filters.update({"ignore_err": True})
if doc.ignore_cr_dr_notes:
filters.update({"ignore_cr_dr_notes": True})
if doc.report == "General Ledger":
filters.update(get_gl_filters(doc, entry, tax_id, presentation_currency))
col, res = get_soa(filters)

View File

@@ -77,6 +77,7 @@ class PromotionalScheme(Document):
self.validate_applicable_for()
self.validate_pricing_rules()
self.validate_mixed_with_recursion()
def validate_applicable_for(self):
if self.applicable_for:
@@ -94,7 +95,7 @@ class PromotionalScheme(Document):
docnames = []
# If user has changed applicable for
if self._doc_before_save.applicable_for == self.applicable_for:
if self.get_doc_before_save() and self.get_doc_before_save().applicable_for == self.applicable_for:
return
docnames = frappe.get_all("Pricing Rule", filters={"promotional_scheme": self.name})
@@ -108,6 +109,7 @@ class PromotionalScheme(Document):
frappe.delete_doc("Pricing Rule", docname.name)
def on_update(self):
self.validate()
pricing_rules = (
frappe.get_all(
"Pricing Rule",
@@ -119,6 +121,15 @@ class PromotionalScheme(Document):
)
self.update_pricing_rules(pricing_rules)
def validate_mixed_with_recursion(self):
if self.mixed_conditions:
if self.product_discount_slabs:
for slab in self.product_discount_slabs:
if slab.is_recursive:
frappe.throw(
_("Recursive Discounts with Mixed condition is not supported by the system")
)
def update_pricing_rules(self, pricing_rules):
rules = {}
count = 0

View File

@@ -107,6 +107,25 @@ class TestPromotionalScheme(unittest.TestCase):
price_rules = frappe.get_all("Pricing Rule", filters={"promotional_scheme": ps.name})
self.assertEqual(price_rules, [])
def test_validation_on_recurse_with_mixed_condition(self):
ps = make_promotional_scheme()
ps.set("price_discount_slabs", [])
ps.set(
"product_discount_slabs",
[
{
"rule_description": "12+1",
"min_qty": 12,
"free_item": "_Test Item 2",
"free_qty": 1,
"is_recursive": 1,
"recurse_for": 12,
}
],
)
ps.mixed_conditions = True
self.assertRaises(frappe.ValidationError, ps.save)
def make_promotional_scheme(**args):
args = frappe._dict(args)

View File

@@ -59,25 +59,6 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
this.show_stock_ledger();
}
if (this.frm.doc.repost_required && this.frm.doc.docstatus===1) {
this.frm.set_intro(__("Accounting entries for this invoice need to be reposted. Please click on 'Repost' button to update."));
this.frm.add_custom_button(__('Repost Accounting Entries'),
() => {
this.frm.call({
doc: this.frm.doc,
method: 'repost_accounting_entries',
freeze: true,
freeze_message: __('Reposting...'),
callback: (r) => {
if (!r.exc) {
frappe.msgprint(__('Accounting Entries are reposted.'));
me.frm.refresh();
}
}
});
}).removeClass('btn-default').addClass('btn-warning');
}
if(!doc.is_return && doc.docstatus == 1 && doc.outstanding_amount != 0){
if(doc.on_hold) {
this.frm.add_custom_button(

View File

@@ -170,7 +170,6 @@
"against_expense_account",
"column_break_63",
"unrealized_profit_loss_account",
"repost_required",
"subscription_section",
"auto_repeat",
"update_auto_repeat_reference",
@@ -361,7 +360,8 @@
"description": "Once set, this invoice will be on hold till the set date",
"fieldname": "release_date",
"fieldtype": "Date",
"label": "Release Date"
"label": "Release Date",
"search_index": 1
},
{
"fieldname": "cb_17",
@@ -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
},
@@ -1590,15 +1591,6 @@
"fieldtype": "Check",
"label": "Use Company Default Round Off Cost Center"
},
{
"default": "0",
"fieldname": "repost_required",
"fieldtype": "Check",
"hidden": 1,
"label": "Repost Required",
"options": "Account",
"read_only": 1
},
{
"default": "0",
"fieldname": "use_transaction_date_exchange_rate",
@@ -1619,7 +1611,7 @@
"idx": 204,
"is_submittable": 1,
"links": [],
"modified": "2024-03-20 15:57:00.736868",
"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(
@@ -413,7 +428,7 @@ class PurchaseInvoice(BuyingController):
for item in self.get("items"):
validate_account_head(item.idx, item.expense_account, self.company, "Expense")
def set_against_expense_account(self):
def set_against_expense_account(self, force=False):
against_accounts = []
for item in self.get("items"):
if item.expense_account and (item.expense_account not in against_accounts):
@@ -421,6 +436,10 @@ class PurchaseInvoice(BuyingController):
self.against_expense_account = ",".join(against_accounts)
def force_set_against_expense_account(self):
self.set_against_expense_account()
frappe.db.set_value(self.doctype, self.name, "against_expense_account", self.against_expense_account)
def po_required(self):
if frappe.db.get_value("Buying Settings", None, "po_required") == "Yes":
if frappe.get_value(
@@ -586,17 +605,17 @@ class PurchaseInvoice(BuyingController):
self.process_common_party_accounting()
def on_update_after_submit(self):
if hasattr(self, "repost_required"):
fields_to_check = [
"cash_bank_account",
"write_off_account",
"unrealized_profit_loss_account",
]
child_tables = {"items": ("expense_account",), "taxes": ("account_head",)}
self.needs_repost = self.check_if_fields_updated(fields_to_check, child_tables)
if self.needs_repost:
self.validate_for_repost()
self.db_set("repost_required", self.needs_repost)
fields_to_check = [
"cash_bank_account",
"write_off_account",
"unrealized_profit_loss_account",
"is_opening",
]
child_tables = {"items": ("expense_account",), "taxes": ("account_head",)}
self.needs_repost = self.check_if_fields_updated(fields_to_check, child_tables)
if self.needs_repost:
self.validate_for_repost()
self.repost_accounting_entries()
def make_gl_entries(self, gl_entries=None, from_repost=False):
update_outstanding = "No" if (cint(self.is_paid) or self.write_off_account) else "Yes"
@@ -1494,6 +1513,9 @@ class PurchaseInvoice(BuyingController):
self.db_set("release_date", None)
def set_tax_withholding(self):
self.set("advance_tax", [])
self.set("tax_withheld_vouchers", [])
if not self.apply_tds:
return
@@ -1535,8 +1557,6 @@ class PurchaseInvoice(BuyingController):
self.remove(d)
## Add pending vouchers on which tax was withheld
self.set("tax_withheld_vouchers", [])
for voucher_no, voucher_details in voucher_wise_amount.items():
self.append(
"tax_withheld_vouchers",
@@ -1551,7 +1571,6 @@ class PurchaseInvoice(BuyingController):
self.calculate_taxes_and_totals()
def allocate_advance_tds(self, tax_withholding_details, advance_taxes):
self.set("advance_tax", [])
for tax in advance_taxes:
allocated_amount = 0
pending_amount = flt(tax.tax_amount - tax.allocated_amount)

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
@@ -1908,18 +1911,15 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
check_gl_entries(self, pi.name, expected_gle, nowdate())
pi.items[0].expense_account = "Service - _TC"
# Ledger reposted implicitly upon 'Update After Submit'
pi.save()
pi.load_from_db()
self.assertTrue(pi.repost_required)
pi.repost_accounting_entries()
expected_gle = [
["Creditors - _TC", 0.0, 1000, nowdate()],
["Service - _TC", 1000, 0.0, nowdate()],
]
check_gl_entries(self, pi.name, expected_gle, nowdate())
pi.load_from_db()
self.assertFalse(pi.repost_required)
def test_default_cost_center_for_purchase(self):
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center

View File

@@ -149,6 +149,10 @@ def start_repost(account_repost_doc=str) -> None:
doc.make_gl_entries_on_cancel()
doc.docstatus = 1
if doc.doctype == "Sales Invoice":
doc.force_set_against_income_account()
else:
doc.force_set_against_expense_account()
doc.make_gl_entries()
elif doc.doctype in ["Payment Entry", "Journal Entry", "Expense Claim"]:

View File

@@ -49,25 +49,6 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e
this.frm.toggle_reqd("due_date", !this.frm.doc.is_return);
if (this.frm.doc.repost_required && this.frm.doc.docstatus===1) {
this.frm.set_intro(__("Accounting entries for this invoice needs to be reposted. Please click on 'Repost' button to update."));
this.frm.add_custom_button(__('Repost Accounting Entries'),
() => {
this.frm.call({
doc: this.frm.doc,
method: 'repost_accounting_entries',
freeze: true,
freeze_message: __('Reposting...'),
callback: (r) => {
if (!r.exc) {
frappe.msgprint(__('Accounting Entries are reposted'));
me.frm.refresh();
}
}
});
}).removeClass('btn-default').addClass('btn-warning');
}
if (this.frm.doc.is_return) {
this.frm.return_print_format = "Sales Invoice Return";
}
@@ -428,12 +409,16 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e
frappe.msgprint(__("Please specify Company to proceed"));
} else {
var me = this;
const for_validate = me.frm.doc.is_return ? true : false;
return this.frm.call({
doc: me.frm.doc,
method: "set_missing_values",
callback: function(r) {
if(!r.exc) {
if(r.message && r.message.print_format) {
args: {
for_validate: for_validate,
},
callback: function (r) {
if (!r.exc) {
if (r.message && r.message.print_format) {
me.frm.pos_print_format = r.message.print_format;
}
me.frm.trigger("update_stock");

View File

@@ -213,7 +213,6 @@
"is_internal_customer",
"is_discounted",
"remarks",
"repost_required",
"connections_tab"
],
"fields": [
@@ -2184,7 +2183,7 @@
"link_fieldname": "consolidated_invoice"
}
],
"modified": "2024-05-08 18:02:28.549041",
"modified": "2024-07-18 15:30:39.428519",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",

View File

@@ -385,7 +385,6 @@ class SalesInvoice(SellingController):
self.repost_future_sle_and_gle()
self.db_set("status", "Cancelled")
self.db_set("repost_required", 0)
if frappe.db.get_single_value("Selling Settings", "sales_update_frequency") == "Each Transaction":
update_company_current_month_sales(self.company)
@@ -532,23 +531,23 @@ class SalesInvoice(SellingController):
data.sales_invoice = sales_invoice
def on_update_after_submit(self):
if hasattr(self, "repost_required"):
fields_to_check = [
"additional_discount_account",
"cash_bank_account",
"account_for_change_amount",
"write_off_account",
"loyalty_redemption_account",
"unrealized_profit_loss_account",
]
child_tables = {
"items": ("income_account", "expense_account", "discount_account"),
"taxes": ("account_head",),
}
self.needs_repost = self.check_if_fields_updated(fields_to_check, child_tables)
if self.needs_repost:
self.validate_for_repost()
self.db_set("repost_required", self.needs_repost)
fields_to_check = [
"additional_discount_account",
"cash_bank_account",
"account_for_change_amount",
"write_off_account",
"loyalty_redemption_account",
"unrealized_profit_loss_account",
"is_opening",
]
child_tables = {
"items": ("income_account", "expense_account", "discount_account"),
"taxes": ("account_head",),
}
self.needs_repost = self.check_if_fields_updated(fields_to_check, child_tables)
if self.needs_repost:
self.validate_for_repost()
self.repost_accounting_entries()
def set_paid_amount(self):
paid_amount = 0.0
@@ -768,6 +767,10 @@ class SalesInvoice(SellingController):
against_acc.append(d.income_account)
self.against_income_account = ",".join(against_acc)
def force_set_against_income_account(self):
self.set_against_income_account()
frappe.db.set_value(self.doctype, self.name, "against_income_account", self.against_income_account)
def add_remarks(self):
if not self.remarks:
if self.po_no and self.po_date:
@@ -1291,6 +1294,10 @@ class SalesInvoice(SellingController):
if skip_change_gl_entries and payment_mode.account == self.account_for_change_amount:
payment_mode.base_amount -= flt(self.change_amount)
against_voucher = self.name
if self.is_return and self.return_against and not self.update_outstanding_for_self:
against_voucher = self.return_against
if payment_mode.base_amount:
# POS, make payment entries
gl_entries.append(
@@ -1304,7 +1311,7 @@ class SalesInvoice(SellingController):
"credit_in_account_currency": payment_mode.base_amount
if self.party_account_currency == self.company_currency
else payment_mode.amount,
"against_voucher": self.name,
"against_voucher": against_voucher,
"against_voucher_type": self.doctype,
"cost_center": self.cost_center,
},

View File

@@ -5,6 +5,7 @@ import copy
import json
import frappe
from frappe import qb
from frappe.model.dynamic_links import get_dynamic_link_map
from frappe.model.naming import make_autoname
from frappe.tests.utils import FrappeTestCase, change_settings
@@ -306,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)
@@ -413,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):
@@ -650,7 +652,7 @@ class TestSalesInvoice(FrappeTestCase):
62.5,
625.0,
50,
499.97600115194473,
499.98,
],
"_Test Item Home Desktop 200": [
190.66,
@@ -661,7 +663,7 @@ class TestSalesInvoice(FrappeTestCase):
190.66,
953.3,
150,
749.9968530500239,
750,
],
}
@@ -674,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"):
@@ -723,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,
},
@@ -737,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,
},
@@ -775,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()
@@ -2098,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)
@@ -2125,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",
{
@@ -2151,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(
@@ -2219,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)
@@ -2231,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],
]
)
@@ -2884,13 +2894,9 @@ class TestSalesInvoice(FrappeTestCase):
si.items[0].income_account = "Service - _TC"
si.additional_discount_account = "_Test Account Sales - _TC"
si.taxes[0].account_head = "TDS Payable - _TC"
# Ledger reposted implicitly upon 'Update After Submit'
si.save()
si.load_from_db()
self.assertTrue(si.repost_required)
si.repost_accounting_entries()
expected_gle = [
["_Test Account Sales - _TC", 22.0, 0.0, nowdate()],
["Debtors - _TC", 88, 0.0, nowdate()],
@@ -2900,9 +2906,6 @@ class TestSalesInvoice(FrappeTestCase):
check_gl_entries(self, si.name, expected_gle, add_days(nowdate(), -1))
si.load_from_db()
self.assertFalse(si.repost_required)
def test_asset_depreciation_on_sale_with_pro_rata(self):
"""
Tests if an Asset set to depreciate yearly on June 30, that gets sold on Sept 30, creates an additional depreciation entry on its date of sale.
@@ -3032,6 +3035,128 @@ class TestSalesInvoice(FrappeTestCase):
party_link.delete()
frappe.db.set_value("Accounts Settings", None, "enable_common_party_accounting", 0)
def test_sales_invoice_against_supplier_usd_with_dimensions(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 USD")
cust_doc = frappe.get_doc("Customer", customer)
cust_doc.default_currency = "USD"
cust_doc.save()
# create a supplier
supplier = create_supplier(supplier_name="_Test Common Supplier USD").name
supp_doc = frappe.get_doc("Supplier", supplier)
supp_doc.default_currency = "USD"
supp_doc.save()
# 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 dimension and make it mandatory
if not frappe.get_all("Accounting Dimension", filters={"document_type": "Department"}):
dim = frappe.get_doc(
{
"doctype": "Accounting Dimension",
"document_type": "Department",
"dimension_defaults": [{"company": "_Test Company", "mandatory_for_bs": True}],
}
)
dim.save()
else:
dim = frappe.get_doc(
"Accounting Dimension",
frappe.get_all("Accounting Dimension", filters={"document_type": "Department"})[0],
)
dim.disabled = False
dim.dimension_defaults = []
dim.append("dimension_defaults", {"company": "_Test Company", "mandatory_for_bs": True})
dim.save()
# create a sales invoice
si = create_sales_invoice(
customer=customer, parent_cost_center="_Test Cost Center - _TC", do_not_submit=True
)
si.department = "All Departments"
si.save().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,
"department": "All Departments",
},
pluck="credit_in_account_currency",
)
self.assertTrue(jv)
self.assertEqual(jv[0], si.grand_total)
dim.disabled = True
dim.save()
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
@@ -3519,6 +3644,122 @@ class TestSalesInvoice(FrappeTestCase):
]
self.assertEqual(expected, actual)
def test_pos_returns_without_update_outstanding_for_self(self):
from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_sales_return
pos_profile = make_pos_profile()
pos_profile.payments = []
pos_profile.append("payments", {"default": 1, "mode_of_payment": "Cash"})
pos_profile.save()
pos = create_sales_invoice(qty=10, do_not_save=True)
pos.is_pos = 1
pos.pos_profile = pos_profile.name
pos.append(
"payments", {"mode_of_payment": "Bank Draft", "account": "_Test Bank - _TC", "amount": 500}
)
pos.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 500})
pos.save().submit()
pos_return = make_sales_return(pos.name)
pos_return.update_outstanding_for_self = False
pos_return.save().submit()
gle = qb.DocType("GL Entry")
res = (
qb.from_(gle)
.select(gle.against_voucher)
.distinct()
.where(
gle.is_cancelled.eq(0) & gle.voucher_no.eq(pos_return.name) & gle.against_voucher.notnull()
)
.run(as_list=1)
)
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

@@ -37,6 +37,12 @@ frappe.ui.form.on("Subscription", {
frm.add_custom_button(__("Fetch Subscription Updates"), () =>
frm.events.get_subscription_updates(frm)
);
frm.add_custom_button(
__("Force-Fetch Subscription Updates"),
() => frm.trigger("force_fetch_subscription_updates"),
__("Actions")
);
} else if (frm.doc.status === "Cancelled") {
frm.add_custom_button(__("Restart Subscription"), () =>
frm.events.renew_this_subscription(frm)
@@ -96,4 +102,11 @@ frappe.ui.form.on("Subscription", {
},
});
},
force_fetch_subscription_updates: function (frm) {
frm.call("force_fetch_subscription_updates").then((r) => {
if (!r.exec) {
frm.reload_doc();
}
});
},
});

View File

@@ -674,6 +674,28 @@ class Subscription(Document):
if invoice:
return invoice.precision("grand_total")
@frappe.whitelist()
def force_fetch_subscription_updates(self):
"""
Process Subscription and create Invoices even if current date doesn't lie between current_invoice_start and currenct_invoice_end
It makes use of 'Proces Subscription' to force processing in a specific 'posting_date'
"""
# Don't process future subscriptions
if nowdate() < self.current_invoice_start:
frappe.msgprint(_("Subscription for Future dates cannot be processed."))
return
processing_date = None
if self.generate_invoice_at == "Beginning of the current subscription period":
processing_date = self.current_invoice_start
elif self.generate_invoice_at == "End of the current subscription period":
processing_date = self.current_invoice_end
elif self.generate_invoice_at == "Days before the current subscription period":
processing_date = add_days(self.current_invoice_start, -self.number_of_days)
self.process(posting_date=processing_date)
def get_calendar_months(billing_interval):
calendar_months = []

View File

@@ -712,3 +712,18 @@ class TestSubscription(FrappeTestCase):
self.assertEqual(pi.total, 55333.33)
subscription.delete()
def test_future_subscription(self):
"""Force-Fetch should not process future subscriptions"""
subscription = frappe.new_doc("Subscription")
subscription.party_type = "Customer"
subscription.party = "_Test Customer"
subscription.generate_invoice_at_period_start = 1
subscription.generate_new_invoices_past_due_date = 1
subscription.start_date = add_months(nowdate(), 1)
subscription.append("plans", {"plan": "_Test Plan Name", "qty": 1})
subscription.save()
subscription.force_fetch_subscription_updates()
subscription.reload()
self.assertEqual(len(subscription.invoices), 0)

View File

@@ -236,6 +236,11 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N
vouchers, voucher_wise_amount = get_invoice_vouchers(
parties, tax_details, inv.company, party_type=party_type
)
payment_entry_vouchers = get_payment_entry_vouchers(
parties, tax_details, inv.company, party_type=party_type
)
advance_vouchers = get_advance_vouchers(
parties,
company=inv.company,
@@ -243,7 +248,8 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N
to_date=tax_details.to_date,
party_type=party_type,
)
taxable_vouchers = vouchers + advance_vouchers
taxable_vouchers = vouchers + advance_vouchers + payment_entry_vouchers
tax_deducted_on_advances = 0
if inv.doctype == "Purchase Invoice":
@@ -337,12 +343,14 @@ def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"):
AND ja.party in %s
AND j.apply_tds = 1
AND j.tax_withholding_category = %s
AND j.company = %s
""",
(
tax_details.from_date,
tax_details.to_date,
tuple(parties),
tax_details.get("tax_withholding_category"),
company,
),
as_dict=1,
)
@@ -355,6 +363,20 @@ def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"):
return vouchers, voucher_wise_amount
def get_payment_entry_vouchers(parties, tax_details, company, party_type="Supplier"):
payment_entry_filters = {
"party_type": party_type,
"party": ("in", parties),
"docstatus": 1,
"apply_tax_withholding_amount": 1,
"posting_date": ["between", (tax_details.from_date, tax_details.to_date)],
"tax_withholding_category": tax_details.get("tax_withholding_category"),
"company": company,
}
return frappe.db.get_all("Payment Entry", filters=payment_entry_filters, pluck="name")
def get_advance_vouchers(parties, company=None, from_date=None, to_date=None, party_type="Supplier"):
"""
Use Payment Ledger to fetch unallocated Advance Payments
@@ -445,6 +467,7 @@ def get_tds_amount(ldc, parties, inv, tax_details, vouchers):
"unallocated_amount": (">", 0),
"posting_date": ["between", (tax_details.from_date, tax_details.to_date)],
"tax_withholding_category": tax_details.get("tax_withholding_category"),
"company": inv.company,
}
field = "sum(tax_withholding_net_total)"

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

@@ -7,7 +7,7 @@ from erpnext.accounts.report.accounts_payable.accounts_payable import execute
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
class TestAccountsPayable(AccountsTestMixin, FrappeTestCase):
def setUp(self):
self.create_company()
self.create_customer()

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,23 +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,
)
self.voucher_balance[key] = self.build_voucher_dict(ple)
self.get_invoices(ple)
if self.filters.get("group_by_party"):
@@ -207,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"):
@@ -253,7 +270,7 @@ class ReceivablePayableReport:
row.paid -= amount
row.paid_in_account_currency -= amount_in_account_currency
if ple.cost_center:
if not row.cost_center and ple.cost_center:
row.cost_center = str(ple.cost_center)
def update_sub_total_row(self, row, party):
@@ -288,13 +305,13 @@ class ReceivablePayableReport:
must_consider = False
if self.filters.get("for_revaluation_journals"):
if (abs(row.outstanding) > 0.0 / 10**self.currency_precision) or (
abs(row.outstanding_in_account_currency) > 0.0 / 10**self.currency_precision
if (abs(row.outstanding) >= 0.0 / 10**self.currency_precision) or (
abs(row.outstanding_in_account_currency) >= 0.0 / 10**self.currency_precision
):
must_consider = True
else:
if (abs(row.outstanding) > 1.0 / 10**self.currency_precision) and (
(abs(row.outstanding_in_account_currency) > 1.0 / 10**self.currency_precision)
if (abs(row.outstanding) >= 1.0 / 10**self.currency_precision) and (
(abs(row.outstanding_in_account_currency) >= 1.0 / 10**self.currency_precision)
or (row.voucher_no in self.err_journals)
):
must_consider = True

View File

@@ -53,11 +53,13 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
si = si.submit()
return si
def create_payment_entry(self, docname):
def create_payment_entry(self, docname, do_not_submit=False):
pe = get_payment_entry("Sales Invoice", docname, bank_account=self.cash, party_amount=40)
pe.paid_from = self.debit_to
pe.insert()
pe.submit()
if not do_not_submit:
pe.submit()
return pe
def create_credit_note(self, docname, do_not_submit=False):
credit_note = create_sales_invoice(
@@ -955,3 +957,69 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
self.assertEqual(
expected_data, [row.invoiced, row.outstanding, row.remaining_balance, row.future_amount]
)
def test_accounts_receivable_output_for_minor_outstanding(self):
"""
AR/AP should report miniscule outstanding of 0.01. Or else there will be slight difference with General Ledger/Trial Balance
"""
filters = {
"company": self.company,
"report_date": today(),
"range1": 30,
"range2": 60,
"range3": 90,
"range4": 120,
}
# check invoice grand total and invoiced column's value for 3 payment terms
si = self.create_sales_invoice(no_payment_schedule=True)
pe = get_payment_entry("Sales Invoice", si.name, bank_account=self.cash, party_amount=99.99)
pe.paid_from = self.debit_to
pe.save().submit()
report = execute(filters)
expected_data_after_payment = [100, 100, 99.99, 0.01]
self.assertEqual(len(report[1]), 1)
row = report[1][0]
self.assertEqual(
expected_data_after_payment,
[row.invoice_grand_total, row.invoiced, row.paid, row.outstanding],
)
def test_cost_center_on_report_output(self):
filters = {
"company": self.company,
"report_date": today(),
"range1": 30,
"range2": 60,
"range3": 90,
"range4": 120,
}
# check invoice grand total and invoiced column's value for 3 payment terms
si = self.create_sales_invoice(no_payment_schedule=True, do_not_submit=True)
si.cost_center = self.cost_center
si.save().submit()
new_cc = frappe.get_doc(
{
"doctype": "Cost Center",
"cost_center_name": "East Wing",
"parent_cost_center": self.company + " - " + self.company_abbr,
"company": self.company,
}
)
new_cc.save()
# check invoice grand total, invoiced, paid and outstanding column's value after payment
pe = self.create_payment_entry(si.name, do_not_submit=True)
pe.cost_center = new_cc.name
pe.save().submit()
report = execute(filters)
expected_data_after_payment = [si.name, si.cost_center, 60]
self.assertEqual(len(report[1]), 1)
row = report[1][0]
self.assertEqual(expected_data_after_payment, [row.voucher_no, row.cost_center, row.outstanding])

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

@@ -109,7 +109,7 @@ def get_balance_sheet_data(fiscal_year, companies, columns, filters):
if total_credit:
data.append(total_credit)
report_summary = get_bs_summary(
report_summary, primitive_summary = get_bs_summary(
companies,
asset,
liability,
@@ -180,7 +180,7 @@ def get_profit_loss_data(fiscal_year, companies, columns, filters):
chart = get_pl_chart_data(filters, columns, income, expense, net_profit_loss)
report_summary = get_pl_summary(
report_summary, primitive_summary = get_pl_summary(
companies, "", income, expense, net_profit_loss, company_currency, filters, True
)

View File

@@ -279,3 +279,79 @@ class TestDeferredRevenueAndExpense(FrappeTestCase, AccountsTestMixin):
{"key": "aug_2021", "total": 0, "actual": 0},
]
self.assertEqual(report.period_total, expected)
@change_settings(
"Accounts Settings",
{"book_deferred_entries_based_on": "Months", "book_deferred_entries_via_journal_entry": 0},
)
def test_zero_amount(self):
self.create_item("_Test Office Desk", 0, self.warehouse, self.company)
item = frappe.get_doc("Item", self.item)
item.enable_deferred_expense = 1
item.item_defaults[0].deferred_expense_account = self.deferred_expense_account
item.no_of_months_exp = 12
item.save()
pi = make_purchase_invoice(
item=self.item,
company=self.company,
supplier=self.supplier,
is_return=False,
update_stock=False,
posting_date=frappe.utils.datetime.date(2021, 12, 30),
parent_cost_center=self.cost_center,
cost_center=self.cost_center,
do_not_save=True,
rate=3910,
price_list_rate=3910,
warehouse=self.warehouse,
qty=1,
)
pi.set_posting_time = True
pi.items[0].enable_deferred_expense = 1
pi.items[0].service_start_date = "2021-12-30"
pi.items[0].service_end_date = "2022-12-30"
pi.items[0].deferred_expense_account = self.deferred_expense_account
pi.items[0].expense_account = self.expense_account
pi.save()
pi.submit()
pda = frappe.get_doc(
doctype="Process Deferred Accounting",
posting_date=nowdate(),
start_date="2022-01-01",
end_date="2022-01-31",
type="Expense",
company=self.company,
)
pda.insert()
pda.submit()
# execute report
fiscal_year = frappe.get_doc("Fiscal Year", get_fiscal_year(date="2022-01-31"))
self.filters = frappe._dict(
{
"company": self.company,
"filter_based_on": "Date Range",
"period_start_date": "2022-01-01",
"period_end_date": "2022-01-31",
"from_fiscal_year": fiscal_year.year,
"to_fiscal_year": fiscal_year.year,
"periodicity": "Monthly",
"type": "Expense",
"with_upcoming_postings": False,
}
)
report = Deferred_Revenue_and_Expense_Report(filters=self.filters)
report.run()
# fetch the invoice from deferred invoices list
inv = [d for d in report.deferred_invoices if d.name == pi.name]
# make sure the list isn't empty
self.assertTrue(inv)
# calculate the total deferred expense for the period
inv = inv[0].calculate_invoice_revenue_expense_for_period()
deferred_exp = sum([inv[idx].actual for idx in range(len(report.period_list))])
# make sure the total deferred expense is greater than 0
self.assertLess(deferred_exp, 0)

View File

@@ -199,6 +199,11 @@ frappe.query_reports["General Ledger"] = {
label: __("Ignore Exchange Rate Revaluation Journals"),
fieldtype: "Check",
},
{
fieldname: "ignore_cr_dr_notes",
label: __("Ignore System Generated Credit / Debit Notes"),
fieldtype: "Check",
},
],
};

View File

@@ -234,6 +234,23 @@ def get_conditions(filters):
if err_journals:
filters.update({"voucher_no_not_in": [x[0] for x in err_journals]})
if filters.get("ignore_cr_dr_notes"):
system_generated_cr_dr_journals = frappe.db.get_all(
"Journal Entry",
filters={
"company": filters.get("company"),
"docstatus": 1,
"voucher_type": ("in", ["Credit Note", "Debit Note"]),
"is_system_generated": 1,
},
as_list=True,
)
if system_generated_cr_dr_journals:
vouchers_to_ignore = (filters.get("voucher_no_not_in") or []) + [
x[0] for x in system_generated_cr_dr_journals
]
filters.update({"voucher_no_not_in": vouchers_to_ignore})
if filters.get("voucher_no_not_in"):
conditions.append("voucher_no not in %(voucher_no_not_in)s")

View File

@@ -2,13 +2,32 @@
# MIT License. See license.txt
import frappe
from frappe import qb
from frappe.tests.utils import FrappeTestCase
from frappe.utils import flt, today
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.report.general_ledger.general_ledger import execute
from erpnext.controllers.sales_and_purchase_return import make_return_doc
class TestGeneralLedger(FrappeTestCase):
def setUp(self):
self.company = "_Test Company"
self.clear_old_records()
def clear_old_records(self):
doctype_list = [
"GL Entry",
"Payment Ledger Entry",
"Sales Invoice",
"Purchase Invoice",
"Payment Entry",
"Journal Entry",
]
for doctype in doctype_list:
qb.from_(qb.DocType(doctype)).delete().where(qb.DocType(doctype).company == self.company).run()
def test_foreign_account_balance_after_exchange_rate_revaluation(self):
"""
Checks the correctness of balance after exchange rate revaluation
@@ -248,3 +267,68 @@ class TestGeneralLedger(FrappeTestCase):
)
)
self.assertIn(revaluation_jv.name, set([x.voucher_no for x in data]))
def test_ignore_cr_dr_notes_filter(self):
si = create_sales_invoice()
cr_note = make_return_doc(si.doctype, si.name)
cr_note.submit()
pr = frappe.get_doc("Payment Reconciliation")
pr.company = si.company
pr.party_type = "Customer"
pr.party = si.customer
pr.receivable_payable_account = si.debit_to
pr.get_unreconciled_entries()
invoices = [invoice.as_dict() for invoice in pr.invoices if invoice.invoice_number == si.name]
payments = [payment.as_dict() for payment in pr.payments if payment.reference_name == cr_note.name]
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
pr.reconcile()
system_generated_journal = frappe.db.get_all(
"Journal Entry",
filters={
"docstatus": 1,
"reference_type": si.doctype,
"reference_name": si.name,
"voucher_type": "Credit Note",
"is_system_generated": True,
},
fields=["name"],
)
self.assertEqual(len(system_generated_journal), 1)
expected = set([si.name, cr_note.name, system_generated_journal[0].name])
# Without ignore_cr_dr_notes
columns, data = execute(
frappe._dict(
{
"company": si.company,
"from_date": si.posting_date,
"to_date": si.posting_date,
"account": [si.debit_to],
"group_by": "Group by Voucher (Consolidated)",
"ignore_cr_dr_notes": False,
}
)
)
actual = set([x.voucher_no for x in data if x.voucher_no])
self.assertEqual(expected, actual)
# Without ignore_cr_dr_notes
expected = set([si.name, cr_note.name])
columns, data = execute(
frappe._dict(
{
"company": si.company,
"from_date": si.posting_date,
"to_date": si.posting_date,
"account": [si.debit_to],
"group_by": "Group by Voucher (Consolidated)",
"ignore_cr_dr_notes": True,
}
)
)
actual = set([x.voucher_no for x in data if x.voucher_no])
self.assertEqual(expected, actual)

View File

@@ -694,7 +694,8 @@ class GrossProfitGenerator:
def get_average_buying_rate(self, row, item_code):
args = row
if item_code not in self.average_buying_rate:
key = (item_code, row.warehouse)
if key not in self.average_buying_rate:
args.update(
{
"voucher_type": row.parenttype,
@@ -705,9 +706,9 @@ class GrossProfitGenerator:
)
average_buying_rate = get_incoming_rate(args)
self.average_buying_rate[item_code] = flt(average_buying_rate)
self.average_buying_rate[key] = flt(average_buying_rate)
return self.average_buying_rate[item_code]
return self.average_buying_rate[key]
def get_last_purchase_rate(self, item_code, row):
purchase_invoice = frappe.qb.DocType("Purchase Invoice")

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

@@ -46,7 +46,7 @@ frappe.query_reports["Item-wise Purchase Register"] = {
label: __("Group By"),
fieldname: "group_by",
fieldtype: "Select",
options: ["Supplier", "Item Group", "Item", "Invoice"],
options: ["", "Supplier", "Item Group", "Item", "Invoice"],
},
],
formatter: function (value, row, column, data, default_formatter) {

View File

@@ -289,7 +289,7 @@ def get_columns(additional_table_columns, filters):
def apply_conditions(query, pi, pii, filters):
for opts in ("company", "supplier", "item_code", "mode_of_payment"):
for opts in ("company", "supplier", "mode_of_payment"):
if filters.get(opts):
query = query.where(pi[opts] == filters[opts])
@@ -299,6 +299,9 @@ def apply_conditions(query, pi, pii, filters):
if filters.get("to_date"):
query = query.where(pi.posting_date <= filters.get("to_date"))
if filters.get("item_code"):
query = query.where(pii.item_code == filters.get("item_code"))
if filters.get("item_group"):
query = query.where(pii.item_group == filters.get("item_group"))
@@ -306,14 +309,15 @@ def apply_conditions(query, pi, pii, filters):
query = query.orderby(pi.posting_date, order=Order.desc)
query = query.orderby(pii.item_group, order=Order.desc)
else:
query = apply_group_by_conditions(filters, "Purchase Invoice")
query = apply_group_by_conditions(query, pi, pii, filters)
return query
def get_items(filters, additional_table_columns):
pi = frappe.qb.DocType("Purchase Invoice")
pii = frappe.qb.DocType("Purchase Invoice Item")
doctype = "Purchase Invoice"
pi = frappe.qb.DocType(doctype)
pii = frappe.qb.DocType(f"{doctype} Item")
Item = frappe.qb.DocType("Item")
query = (
frappe.qb.from_(pi)
@@ -322,7 +326,7 @@ def get_items(filters, additional_table_columns):
.left_join(Item)
.on(pii.item_code == Item.name)
.select(
pii.name.as_("pii_name"),
pii.name,
pii.parent,
pi.posting_date,
pi.credit_to,
@@ -350,6 +354,7 @@ def get_items(filters, additional_table_columns):
pi.mode_of_payment,
)
.where(pi.docstatus == 1)
.where(pii.parenttype == doctype)
)
if filters.get("supplier"):

View File

@@ -0,0 +1,63 @@
import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.utils import getdate, today
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
from erpnext.accounts.report.item_wise_purchase_register.item_wise_purchase_register import execute
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
class TestItemWisePurchaseRegister(AccountsTestMixin, FrappeTestCase):
def setUp(self):
self.create_company()
self.create_supplier()
self.create_item()
def tearDown(self):
frappe.db.rollback()
def create_purchase_invoice(self, do_not_submit=False):
pi = make_purchase_invoice(
item=self.item,
company=self.company,
supplier=self.supplier,
is_return=False,
update_stock=False,
do_not_save=1,
rate=100,
price_list_rate=100,
qty=1,
)
pi = pi.save()
if not do_not_submit:
pi = pi.submit()
return pi
def test_basic_report_output(self):
pi = self.create_purchase_invoice()
filters = frappe._dict({"from_date": today(), "to_date": today(), "company": self.company})
report = execute(filters)
self.assertEqual(len(report[1]), 1)
expected_result = {
"item_code": pi.items[0].item_code,
"invoice": pi.name,
"posting_date": getdate(),
"supplier": pi.supplier,
"credit_to": pi.credit_to,
"company": self.company,
"expense_account": pi.items[0].expense_account,
"stock_qty": 1.0,
"stock_uom": pi.items[0].stock_uom,
"rate": 100.0,
"amount": 100.0,
"total_tax": 0,
"total": 100.0,
"currency": "INR",
}
report_output = {k: v for k, v in report[1][0].items() if k in expected_result}
self.assertDictEqual(report_output, expected_result)

View File

@@ -54,6 +54,12 @@ frappe.query_reports["Item-wise Sales Register"] = {
fieldtype: "Link",
options: "Brand",
},
{
fieldname: "item_code",
label: __("Item"),
fieldtype: "Link",
options: "Item",
},
{
fieldname: "item_group",
label: __("Item Group"),
@@ -64,7 +70,7 @@ frappe.query_reports["Item-wise Sales Register"] = {
label: __("Group By"),
fieldname: "group_by",
fieldtype: "Select",
options: ["Customer Group", "Customer", "Item Group", "Item", "Territory", "Invoice"],
options: ["", "Customer Group", "Customer", "Item Group", "Item", "Territory", "Invoice"],
},
],
formatter: function (value, row, column, data, default_formatter) {

View File

@@ -342,7 +342,7 @@ def get_columns(additional_table_columns, filters):
def apply_conditions(query, si, sii, filters, additional_conditions=None):
for opts in ("company", "customer", "item_code"):
for opts in ("company", "customer"):
if filters.get(opts):
query = query.where(si[opts] == filters[opts])
@@ -371,6 +371,9 @@ def apply_conditions(query, si, sii, filters, additional_conditions=None):
if filters.get("brand"):
query = query.where(sii.brand == filters.get("brand"))
if filters.get("item_code"):
query = query.where(sii.item_code == filters.get("item_code"))
if filters.get("item_group"):
query = query.where(sii.item_group == filters.get("item_group"))
@@ -407,8 +410,9 @@ def apply_group_by_conditions(query, si, ii, filters):
def get_items(filters, additional_query_columns, additional_conditions=None):
si = frappe.qb.DocType("Sales Invoice")
sii = frappe.qb.DocType("Sales Invoice Item")
doctype = "Sales Invoice"
si = frappe.qb.DocType(doctype)
sii = frappe.qb.DocType(f"{doctype} Item")
item = frappe.qb.DocType("Item")
query = (
@@ -456,6 +460,7 @@ def get_items(filters, additional_query_columns, additional_conditions=None):
sii.qty,
)
.where(si.docstatus == 1)
.where(sii.parenttype == doctype)
)
if additional_query_columns:

View File

@@ -0,0 +1,65 @@
import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.utils import getdate, today
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.report.item_wise_sales_register.item_wise_sales_register import execute
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
class TestItemWiseSalesRegister(AccountsTestMixin, FrappeTestCase):
def setUp(self):
self.create_company()
self.create_customer()
self.create_item()
self.clear_old_entries()
def tearDown(self):
frappe.db.rollback()
def create_sales_invoice(self, do_not_submit=False):
si = create_sales_invoice(
item=self.item,
company=self.company,
customer=self.customer,
debit_to=self.debit_to,
posting_date=today(),
parent_cost_center=self.cost_center,
cost_center=self.cost_center,
rate=100,
price_list_rate=100,
do_not_save=1,
)
si = si.save()
if not do_not_submit:
si = si.submit()
return si
def test_basic_report_output(self):
si = self.create_sales_invoice()
filters = frappe._dict({"from_date": today(), "to_date": today(), "company": self.company})
report = execute(filters)
self.assertEqual(len(report[1]), 1)
expected_result = {
"item_code": si.items[0].item_code,
"invoice": si.name,
"posting_date": getdate(),
"customer": si.customer,
"debit_to": si.debit_to,
"company": self.company,
"income_account": si.items[0].income_account,
"stock_qty": 1.0,
"stock_uom": si.items[0].stock_uom,
"rate": 100.0,
"amount": 100.0,
"total_tax": 0,
"total_other_charges": 0,
"total": 100.0,
"currency": "INR",
}
report_output = {k: v for k, v in report[1][0].items() if k in expected_result}
self.assertDictEqual(report_output, expected_result)

View File

@@ -12,7 +12,7 @@ def execute(filters=None):
else:
party_naming_by = frappe.db.get_single_value("Buying Settings", "supp_master_name")
filters.update({"naming_series": party_naming_by})
filters["naming_series"] = party_naming_by
validate_filters(filters)
(
@@ -63,21 +63,23 @@ def get_result(filters, tds_docs, tds_accounts, tax_category_map, journal_entry_
tax_withholding_category = tds_accounts.get(entry.account)
# or else the consolidated value from the voucher document
if not tax_withholding_category:
tax_withholding_category = tax_category_map.get(name)
tax_withholding_category = tax_category_map.get((voucher_type, name))
# or else from the party default
if not tax_withholding_category:
tax_withholding_category = party_map.get(party, {}).get("tax_withholding_category")
rate = tax_rate_map.get(tax_withholding_category)
if net_total_map.get(name):
if net_total_map.get((voucher_type, name)):
if voucher_type == "Journal Entry" and tax_amount and rate:
# back calcalute total amount from rate and tax_amount
if rate:
total_amount = grand_total = base_total = tax_amount / (rate / 100)
elif voucher_type == "Purchase Invoice":
total_amount, grand_total, base_total, bill_no, bill_date = net_total_map.get(name)
total_amount, grand_total, base_total, bill_no, bill_date = net_total_map.get(
(voucher_type, name)
)
else:
total_amount, grand_total, base_total = net_total_map.get(name)
total_amount, grand_total, base_total = net_total_map.get((voucher_type, name))
else:
total_amount += entry.credit
@@ -97,7 +99,7 @@ def get_result(filters, tds_docs, tds_accounts, tax_category_map, journal_entry_
}
if filters.naming_series == "Naming Series":
row.update({"party_name": party_map.get(party, {}).get(party_name)})
row["party_name"] = party_map.get(party, {}).get(party_name)
row.update(
{
@@ -279,7 +281,6 @@ def get_tds_docs(filters):
journal_entries = []
tax_category_map = frappe._dict()
net_total_map = frappe._dict()
frappe._dict()
journal_entry_party_map = frappe._dict()
bank_accounts = frappe.get_all("Account", {"is_group": 0, "account_type": "Bank"}, pluck="name")
@@ -412,7 +413,7 @@ def get_doc_info(vouchers, doctype, tax_category_map, net_total_map=None):
)
for entry in entries:
tax_category_map.update({entry.name: entry.tax_withholding_category})
tax_category_map[(doctype, entry.name)] = entry.tax_withholding_category
if doctype == "Purchase Invoice":
value = [
entry.base_tax_withholding_net_total,
@@ -427,7 +428,8 @@ def get_doc_info(vouchers, doctype, tax_category_map, net_total_map=None):
value = [entry.paid_amount, entry.paid_amount_after_tax, entry.base_paid_amount]
else:
value = [entry.total_amount] * 3
net_total_map.update({entry.name: value})
net_total_map[(doctype, entry.name)] = value
def get_tax_rate_map(filters):

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(
@@ -1571,6 +1645,18 @@ def auto_create_exchange_rate_revaluation_weekly() -> None:
create_err_and_its_journals(companies)
def auto_create_exchange_rate_revaluation_monthly() -> None:
"""
Executed by background job
"""
companies = frappe.db.get_all(
"Company",
filters={"auto_exchange_rate_revaluation": 1, "auto_err_frequency": "Montly"},
fields=["name", "submit_err_jv"],
)
create_err_and_its_journals(companies)
def get_payment_ledger_entries(gl_entries, cancel=0):
ple_map = []
if gl_entries:

View File

@@ -362,11 +362,16 @@ class Asset(AccountsController):
final_number_of_depreciations = cint(finance_book.total_number_of_depreciations) - cint(
self.number_of_depreciations_booked
)
has_pro_rata = self.check_is_pro_rata(finance_book)
for_income_tax = 0
if frappe.db.has_column("Finance Book", "for_income_tax"):
for_income_tax = frappe.db.get_value("Finance Book", finance_book.finance_book, "for_income_tax")
has_pro_rata = False
if not for_income_tax:
has_pro_rata = self.check_is_pro_rata(finance_book)
depr_already_booked = any(
[d.journal_entry for d in self.get("schedules") if d.finance_book == finance_book.finance_book]
)
if has_pro_rata and not depr_already_booked:
if has_pro_rata and not depr_already_booked and not for_income_tax:
final_number_of_depreciations += 1
has_wdv_or_dd_non_yearly_pro_rata = False
@@ -517,10 +522,13 @@ class Asset(AccountsController):
)
# Adjust depreciation amount in the last period based on the expected value after useful life
if (
n == cint(final_number_of_depreciations) - 1
and flt(value_after_depreciation) != flt(finance_book.expected_value_after_useful_life)
) or flt(value_after_depreciation) < flt(finance_book.expected_value_after_useful_life):
if not for_income_tax and (
(
n == cint(final_number_of_depreciations) - 1
and flt(value_after_depreciation) != flt(finance_book.expected_value_after_useful_life)
)
or flt(value_after_depreciation) < flt(finance_book.expected_value_after_useful_life)
):
depreciation_amount += flt(value_after_depreciation) - flt(
finance_book.expected_value_after_useful_life
)

View File

@@ -1689,12 +1689,12 @@ def create_asset(**args):
return asset
def create_asset_category():
def create_asset_category(enable_cwip=1):
asset_category = frappe.new_doc("Asset Category")
asset_category.asset_category_name = "Computers"
asset_category.total_number_of_depreciations = 3
asset_category.frequency_of_depreciation = 3
asset_category.enable_cwip_accounting = 1
asset_category.enable_cwip_accounting = enable_cwip
asset_category.append(
"accounts",
{

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

@@ -125,9 +125,10 @@ def get_data(filters):
if assets_linked_to_fb and asset.calculate_depreciation and asset.asset_id not in assets_linked_to_fb:
continue
asset_value = get_asset_value_after_depreciation(
asset.asset_id, finance_book
) or get_asset_value_after_depreciation(asset.asset_id)
depreciation_amount = depreciation_amount_map.get(asset.asset_id) or 0.0
asset_value = (
asset.gross_purchase_amount - asset.opening_accumulated_depreciation - depreciation_amount
)
row = {
"asset_id": asset.asset_id,
@@ -139,7 +140,7 @@ def get_data(filters):
or pi_supplier_map.get(asset.purchase_invoice),
"gross_purchase_amount": asset.gross_purchase_amount,
"opening_accumulated_depreciation": asset.opening_accumulated_depreciation,
"depreciated_amount": depreciation_amount_map.get(asset.asset_id) or 0.0,
"depreciated_amount": depreciation_amount,
"available_for_use_date": asset.available_for_use_date,
"location": asset.location,
"asset_category": asset.asset_category,

View File

@@ -170,7 +170,7 @@
"fieldname": "supplier_type",
"fieldtype": "Select",
"label": "Supplier Type",
"options": "Company\nIndividual\nProprietorship\nPartnership",
"options": "Company\nIndividual\nPartnership",
"reqd": 1
},
{

View File

@@ -9,6 +9,7 @@ def get_data():
),
"fieldname": "supplier",
"non_standard_fieldnames": {"Payment Entry": "party", "Bank Account": "party"},
"dynamic_links": {"party": ["Supplier", "party_type"]},
"transactions": [
{"label": _("Procurement"), "items": ["Request for Quotation", "Supplier Quotation"]},
{"label": _("Orders"), "items": ["Purchase Order", "Purchase Receipt", "Purchase Invoice"]},

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

@@ -43,9 +43,10 @@ def get_data(filters):
query = (
frappe.qb.from_(po)
.from_(po_item)
.inner_join(po_item)
.on(po_item.parent == po.name)
.left_join(pi_item)
.on(pi_item.po_detail == po_item.name)
.on((pi_item.po_detail == po_item.name) & (pi_item.docstatus == 1))
.select(
po.transaction_date.as_("date"),
po_item.schedule_date.as_("required_date"),

View File

@@ -14,6 +14,9 @@ from erpnext.stock.doctype.item.item import get_last_purchase_details, validate_
def update_last_purchase_rate(doc, is_submit) -> None:
"""updates last_purchase_rate in item table for each item"""
if doc.get("is_internal_supplier"):
return
this_purchase_date = getdate(doc.get("posting_date") or doc.get("transaction_date"))
for d in doc.get("items"):

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
@@ -742,6 +748,9 @@ class AccountsController(TransactionBase):
# reset pricing rule fields if pricing_rule_removed
item.set(fieldname, value)
elif fieldname == "expense_account" and not item.get("expense_account"):
item.expense_account = value
if self.doctype in ["Purchase Invoice", "Sales Invoice"] and item.meta.get_field(
"is_fixed_asset"
):
@@ -1189,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.
@@ -1453,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,
)
@@ -1464,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)
@@ -2286,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()
@@ -2309,6 +2329,15 @@ class AccountsController(TransactionBase):
advance_entry.cost_center = self.cost_center or erpnext.get_default_cost_center(self.company)
advance_entry.is_advance = "Yes"
# update dimesions
dimensions_dict = frappe._dict()
active_dimensions = get_dimensions()[0]
for dim in active_dimensions:
dimensions_dict[dim.fieldname] = self.get(dim.fieldname)
reconcilation_entry.update(dimensions_dict)
advance_entry.update(dimensions_dict)
if self.doctype == "Sales Invoice":
reconcilation_entry.credit_in_account_currency = self.outstanding_amount
advance_entry.debit_in_account_currency = self.outstanding_amount
@@ -2316,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)
@@ -2373,16 +2406,12 @@ class AccountsController(TransactionBase):
@frappe.whitelist()
def repost_accounting_entries(self):
if self.repost_required:
repost_ledger = frappe.new_doc("Repost Accounting Ledger")
repost_ledger.company = self.company
repost_ledger.append("vouchers", {"voucher_type": self.doctype, "voucher_no": self.name})
repost_ledger.flags.ignore_permissions = True
repost_ledger.insert()
repost_ledger.submit()
self.db_set("repost_required", 0)
else:
frappe.throw(_("No updates pending for reposting"))
repost_ledger = frappe.new_doc("Repost Accounting Ledger")
repost_ledger.company = self.company
repost_ledger.append("vouchers", {"voucher_type": self.doctype, "voucher_no": self.name})
repost_ledger.flags.ignore_permissions = True
repost_ledger.insert()
repost_ledger.submit()
@frappe.whitelist()

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

@@ -31,7 +31,7 @@ class SellingController(StockController):
def validate(self):
super().validate()
self.validate_items()
if not self.get("is_debit_note"):
if not (self.get("is_debit_note") or self.get("is_return")):
self.validate_max_discount()
self.validate_selling_price()
self.set_qty_as_per_stock_uom()
@@ -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

@@ -27,7 +27,26 @@ from erpnext.utilities.transaction_base import TransactionBase
class Opportunity(TransactionBase, CRMNote):
def onload(self):
ref_doc = frappe.get_doc(self.opportunity_from, self.party_name)
load_address_and_contact(ref_doc)
load_address_and_contact(self)
ref_doc_contact_list = ref_doc.get("__onload").get("contact_list")
opportunity_doc_contact_list = [
contact
for contact in self.get("__onload").get("contact_list")
if contact not in ref_doc_contact_list
]
ref_doc_contact_list.extend(opportunity_doc_contact_list)
ref_doc.set_onload("contact_list", ref_doc_contact_list)
ref_doc_addr_list = ref_doc.get("__onload").get("addr_list")
opportunity_doc_addr_list = [
addr for addr in self.get("__onload").get("addr_list") if addr not in ref_doc_addr_list
]
ref_doc_addr_list.extend(opportunity_doc_addr_list)
ref_doc.set_onload("addr_list", ref_doc_addr_list)
self.set("__onload", ref_doc.get("__onload"))
def after_insert(self):

View File

@@ -108,7 +108,9 @@ class OpportunitySummaryBySalesStage:
self.grouped_data = []
grouping_key = lambda o: (o["sales_stage"], o[based_on]) # noqa
for (sales_stage, _based_on), rows in groupby(self.query_result, grouping_key):
for (sales_stage, _based_on), rows in groupby(
sorted(self.query_result, key=grouping_key), key=grouping_key
):
self.grouped_data.append(
{
"sales_stage": sales_stage,

View File

@@ -8,7 +8,7 @@ from itertools import groupby
import frappe
from dateutil.relativedelta import relativedelta
from frappe import _
from frappe.utils import cint, flt
from frappe.utils import cint, flt, getdate
from erpnext.setup.utils import get_exchange_rate
@@ -21,7 +21,15 @@ class SalesPipelineAnalytics:
def __init__(self, filters=None):
self.filters = frappe._dict(filters or {})
def validate_filters(self):
if not self.filters.from_date:
frappe.throw(_("From Date is mandatory"))
if not self.filters.to_date:
frappe.throw(_("To Date is mandatory"))
def run(self):
self.validate_filters()
self.get_columns()
self.get_data()
self.get_chart_data()
@@ -114,7 +122,9 @@ class SalesPipelineAnalytics:
self.grouped_data = []
grouping_key = lambda o: (o.get(self.pipeline_by) or "Not Assigned", o[self.period_by]) # noqa
for (pipeline_by, period_by), rows in groupby(self.query_result, grouping_key):
for (pipeline_by, period_by), rows in groupby(
sorted(self.query_result, key=grouping_key), grouping_key
):
self.grouped_data.append(
{
self.pipeline_by: pipeline_by,
@@ -185,7 +195,7 @@ class SalesPipelineAnalytics:
count_or_amount = info.get(based_on)
if self.filters.get("pipeline_by") == "Owner":
if value == "Not Assigned" or value == "[]" or value is None:
if value == "Not Assigned" or value == "[]" or value is None or not value:
assigned_to = ["Not Assigned"]
else:
assigned_to = json.loads(value)
@@ -227,10 +237,9 @@ class SalesPipelineAnalytics:
def get_month_list(self):
month_list = []
current_date = date.today()
month_number = date.today().month
current_date = getdate(self.filters.get("from_date"))
for _month in range(month_number, 13):
while current_date < getdate(self.filters.get("to_date")):
month_list.append(current_date.strftime("%B"))
current_date = current_date + relativedelta(months=1)

View File

@@ -1,19 +1,21 @@
import unittest
import frappe
from frappe.tests.utils import FrappeTestCase
from erpnext.crm.report.sales_pipeline_analytics.sales_pipeline_analytics import execute
class TestSalesPipelineAnalytics(unittest.TestCase):
@classmethod
def setUpClass(self):
class TestSalesPipelineAnalytics(FrappeTestCase):
def setUp(self):
frappe.db.delete("Opportunity")
create_company()
create_customer()
create_opportunity()
def test_sales_pipeline_analytics(self):
self.from_date = "2021-01-01"
self.to_date = "2021-12-31"
self.check_for_monthly_and_number()
self.check_for_monthly_and_amount()
self.check_for_quarterly_and_number()
@@ -28,6 +30,8 @@ class TestSalesPipelineAnalytics(unittest.TestCase):
"status": "Open",
"opportunity_type": "Sales",
"company": "Best Test",
"from_date": self.from_date,
"to_date": self.to_date,
}
report = execute(filters)
@@ -43,6 +47,8 @@ class TestSalesPipelineAnalytics(unittest.TestCase):
"status": "Open",
"opportunity_type": "Sales",
"company": "Best Test",
"from_date": self.from_date,
"to_date": self.to_date,
}
report = execute(filters)
@@ -59,6 +65,8 @@ class TestSalesPipelineAnalytics(unittest.TestCase):
"status": "Open",
"opportunity_type": "Sales",
"company": "Best Test",
"from_date": self.from_date,
"to_date": self.to_date,
}
report = execute(filters)
@@ -74,6 +82,8 @@ class TestSalesPipelineAnalytics(unittest.TestCase):
"status": "Open",
"opportunity_type": "Sales",
"company": "Best Test",
"from_date": self.from_date,
"to_date": self.to_date,
}
report = execute(filters)
@@ -90,6 +100,8 @@ class TestSalesPipelineAnalytics(unittest.TestCase):
"status": "Open",
"opportunity_type": "Sales",
"company": "Best Test",
"from_date": self.from_date,
"to_date": self.to_date,
}
report = execute(filters)
@@ -105,6 +117,8 @@ class TestSalesPipelineAnalytics(unittest.TestCase):
"status": "Open",
"opportunity_type": "Sales",
"company": "Best Test",
"from_date": self.from_date,
"to_date": self.to_date,
}
report = execute(filters)
@@ -121,6 +135,8 @@ class TestSalesPipelineAnalytics(unittest.TestCase):
"status": "Open",
"opportunity_type": "Sales",
"company": "Best Test",
"from_date": self.from_date,
"to_date": self.to_date,
}
report = execute(filters)
@@ -136,6 +152,8 @@ class TestSalesPipelineAnalytics(unittest.TestCase):
"status": "Open",
"opportunity_type": "Sales",
"company": "Best Test",
"from_date": self.from_date,
"to_date": self.to_date,
}
report = execute(filters)
@@ -153,8 +171,8 @@ class TestSalesPipelineAnalytics(unittest.TestCase):
"opportunity_type": "Sales",
"company": "Best Test",
"opportunity_source": "Cold Calling",
"from_date": "2021-08-01",
"to_date": "2021-08-31",
"from_date": self.from_date,
"to_date": self.to_date,
}
report = execute(filters)

View File

@@ -463,6 +463,7 @@ scheduler_events = {
"monthly_long": [
"erpnext.accounts.deferred_revenue.process_deferred_accounting",
"erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual.process_loan_interest_accrual_for_demand_loans",
"erpnext.accounts.utils.auto_create_exchange_rate_revaluation_monthly",
],
}

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

@@ -271,6 +271,7 @@ erpnext.patches.v13_0.create_accounting_dimensions_for_asset_repair
erpnext.patches.v14_0.update_reference_due_date_in_journal_entry
erpnext.patches.v14_0.france_depreciation_warning
erpnext.patches.v14_0.clear_reconciliation_values_from_singles
erpnext.patches.v14_0.update_proprietorship_to_individual
[post_model_sync]
execute:frappe.delete_doc_if_exists('Workspace', 'ERPNext Integrations Settings')
@@ -356,6 +357,7 @@ erpnext.patches.v14_0.clear_reconciliation_values_from_singles
erpnext.patches.v14_0.update_total_asset_cost_field
erpnext.patches.v14_0.create_accounting_dimensions_in_reconciliation_tool
erpnext.patches.v14_0.update_flag_for_return_invoices #2024-03-22
erpnext.patches.v14_0.update_pos_return_ledger_entries #2024-08-16
# below migration patch should always run last
erpnext.patches.v14_0.migrate_gl_to_payment_ledger
erpnext.stock.doctype.delivery_note.patches.drop_unused_return_against_index # 2023-12-20

View File

@@ -0,0 +1,127 @@
import frappe
from frappe import qb
from erpnext.accounts.utils import update_voucher_outstanding
def get_valid_against_voucher_ref(pos_returns):
sinv = qb.DocType("Sales Invoice")
res = (
qb.from_(sinv)
.select(sinv.name, sinv.return_against)
.where(sinv.name.isin(pos_returns) & sinv.return_against.notnull())
.orderby(sinv.name)
.run(as_dict=True)
)
return res
def build_dict_of_valid_against_reference(pos_returns):
_against_ref_dict = frappe._dict()
res = get_valid_against_voucher_ref(pos_returns)
for x in res:
_against_ref_dict[x.name] = x.return_against
return _against_ref_dict
def fix_incorrect_against_voucher_ref(affected_pos_returns):
if affected_pos_returns:
valid_against_voucher_dict = build_dict_of_valid_against_reference(affected_pos_returns)
gle = qb.DocType("GL Entry")
gles_with_invalid_against = (
qb.from_(gle)
.select(gle.name, gle.voucher_no)
.where(
gle.voucher_no.isin(affected_pos_returns)
& gle.against_voucher.notnull()
& gle.against_voucher.eq(gle.voucher_no)
& gle.is_cancelled.eq(0)
)
.run(as_dict=True)
)
# Update GL
if gles_with_invalid_against:
for gl in gles_with_invalid_against:
frappe.db.set_value(
"GL Entry",
gl.name,
"against_voucher",
valid_against_voucher_dict[gl.voucher_no],
)
# Update Payment Ledger
ple = qb.DocType("Payment Ledger Entry")
for x in affected_pos_returns:
qb.update(ple).set(ple.against_voucher_no, valid_against_voucher_dict[x]).where(
ple.voucher_no.eq(x) & ple.delinked.eq(0)
).run()
def get_pos_returns_with_invalid_against_ref():
sinv = qb.DocType("Sales Invoice")
pos_returns_without_self = (
qb.from_(sinv)
.select(sinv.name)
.where(
sinv.docstatus.eq(1)
& sinv.is_pos.eq(1)
& sinv.is_return.eq(1)
& sinv.return_against.notnull()
& sinv.update_outstanding_for_self.eq(0)
)
.run()
)
if pos_returns_without_self:
pos_returns_without_self = [x[0] for x in pos_returns_without_self]
gle = qb.DocType("GL Entry")
gl_against_references = (
qb.from_(gle)
.select(gle.voucher_no, gle.against_voucher)
.where(
gle.voucher_no.isin(pos_returns_without_self)
& gle.against_voucher.notnull()
& gle.against_voucher.eq(gle.voucher_no)
& gle.is_cancelled.eq(0)
)
.run()
)
if gl_against_references:
_vouchers = list(set([x[0] for x in gl_against_references]))
invoice_return_against = (
qb.from_(sinv)
.select(sinv.name, sinv.return_against)
.where(sinv.name.isin(_vouchers) & sinv.return_against.notnull())
.orderby(sinv.name)
.run()
)
valid_references = set(invoice_return_against)
actual_references = set(gl_against_references)
invalid_references = actual_references.difference(valid_references)
if invalid_references:
return [x[0] for x in invalid_references]
return None
def update_outstanding_for_affected(affected_pos_returns):
if affected_pos_returns:
sinv = qb.DocType("Sales Invoice")
pos_with_accounts = (
qb.from_(sinv)
.select(sinv.return_against, sinv.debit_to, sinv.customer)
.where(sinv.name.isin(affected_pos_returns))
.run(as_dict=True)
)
for x in pos_with_accounts:
update_voucher_outstanding("Sales Invoice", x.return_against, x.debit_to, "Customer", x.customer)
def execute():
affected_pos_returns = get_pos_returns_with_invalid_against_ref()
fix_incorrect_against_voucher_ref(affected_pos_returns)
update_outstanding_for_affected(affected_pos_returns)

View File

@@ -0,0 +1,7 @@
import frappe
def execute():
for doctype in ["Customer", "Supplier"]:
field = doctype.lower() + "_type"
frappe.db.set_value(doctype, {field: "Proprietorship"}, field, "Individual")

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

@@ -265,7 +265,11 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
}
const me = this;
if (!this.frm.is_new() && this.frm.doc.docstatus === 0) {
if (
!this.frm.is_new()
&& this.frm.doc.docstatus === 0
&& frappe.model.can_create("Quality Inspection")
) {
this.frm.add_custom_button(__("Quality Inspection(s)"), () => {
me.make_quality_inspection();
}, __("Create"));
@@ -468,6 +472,8 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
quotation_to: me.frm.doc.quotation_to,
supplier: me.frm.doc.supplier,
currency: me.frm.doc.currency,
is_internal_supplier: me.frm.doc.is_internal_supplier,
is_internal_customer: me.frm.doc.is_internal_customer,
update_stock: update_stock,
conversion_rate: me.frm.doc.conversion_rate,
price_list: me.frm.doc.selling_price_list || me.frm.doc.buying_price_list,
@@ -1133,6 +1139,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
"Sales Invoice Item": ["dn_detail", "so_detail", "sales_invoice_item"],
"Purchase Receipt Item": ["purchase_order_item", "purchase_invoice_item", "purchase_receipt_item"],
"Purchase Invoice Item": ["purchase_order_item", "pr_detail", "po_detail"],
"Sales Order Item": ["prevdoc_docname", "quotation_item"],
};
const mappped_fields = mapped_item_field_map[item.doctype] || [];
@@ -1485,7 +1492,9 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
"update_stock": ['Sales Invoice', 'Purchase Invoice'].includes(me.frm.doc.doctype) ? cint(me.frm.doc.update_stock) : 0,
"conversion_factor": me.frm.doc.conversion_factor,
"pos_profile": me.frm.doc.doctype == 'Sales Invoice' ? me.frm.doc.pos_profile : '',
"coupon_code": me.frm.doc.coupon_code
"coupon_code": me.frm.doc.coupon_code,
"is_internal_supplier": me.frm.doc.is_internal_supplier,
"is_internal_customer": me.frm.doc.is_internal_customer,
};
}
@@ -1610,12 +1619,15 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
apply_product_discount(args) {
const items = this.frm.doc.items.filter(d => (d.is_free_item)) || [];
const exist_items = items.map(row => (row.item_code, row.pricing_rules));
const exist_items = items.map(row => { return {item_code: row.item_code, pricing_rules: row.pricing_rules};});
args.free_item_data.forEach(pr_row => {
let row_to_modify = {};
if (!items || !in_list(exist_items, (pr_row.item_code, pr_row.pricing_rules))) {
// If there are no free items, or if the current free item doesn't exist in the table, add it
if (!items || !exist_items.filter(e_row => {
return e_row.item_code == pr_row.item_code && e_row.pricing_rules == pr_row.pricing_rules;
}).length) {
row_to_modify = frappe.model.add_child(this.frm.doc,
this.frm.doc.doctype + ' Item', 'items');
@@ -1638,6 +1650,12 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
apply_price_list(item, reset_plc_conversion) {
// We need to reset plc_conversion_rate sometimes because the call to
// `erpnext.stock.get_item_details.apply_price_list` is sensitive to its value
if (this.frm.doc.doctype === "Material Request") {
return;
}
if (!reset_plc_conversion) {
this.frm.set_value("plc_conversion_rate", "");
}
@@ -1653,7 +1671,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
me.in_apply_price_list = true;
return this.frm.call({
method: "erpnext.stock.get_item_details.apply_price_list",
args: { args: args },
args: { args: args, doc: me.frm.doc },
callback: function(r) {
if (!r.exc) {
frappe.run_serially([
@@ -1799,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

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