Compare commits

..

146 Commits

Author SHA1 Message Date
Frappe PR Bot
7f261f3448 chore(release): Bumped to Version 15.34.1
## [15.34.1](https://github.com/frappe/erpnext/compare/v15.34.0...v15.34.1) (2024-09-05)

### Bug Fixes

* add the company in payment request bcz delete company transactions (backport [#42664](https://github.com/frappe/erpnext/issues/42664)) ([#42982](https://github.com/frappe/erpnext/issues/42982)) ([42e7725](42e7725442))
* added app permission check for apps page ([a35ce12](a35ce12d60))
* adjust price insertion logic for internal suppliers/customers ([#42988](https://github.com/frappe/erpnext/issues/42988)) ([daa75ee](daa75eea00))
* auto reorder material request mail issue (backport [#43066](https://github.com/frappe/erpnext/issues/43066)) ([#43068](https://github.com/frappe/erpnext/issues/43068)) ([d2b2002](d2b2002664))
* **capitalization:** debit cwip account instead of fixed asset account ([#42857](https://github.com/frappe/erpnext/issues/42857)) ([f3c60ea](f3c60ea0a7))
* company accounts setup_queries ([b99cdb5](b99cdb5be7))
* disabled batches showing in the list (backport [#43024](https://github.com/frappe/erpnext/issues/43024)) ([#43069](https://github.com/frappe/erpnext/issues/43069)) ([56dad7d](56dad7d365))
* don't allow capitalizing only service item for new composite asset ([a833010](a833010d2b))
* improve asset item matching logic ([3bb1867](3bb186736d))
* indentation ([4d7c0c0](4d7c0c004a))
* link Purchase Invoice and Receipt Items to Asset ([1121c66](1121c6663f))
* retain date filter when redirecting in Profit and Loss report ([f0e3fb4](f0e3fb466a))
* typeerror on Payment Entry ([6d51d14](6d51d14dfd))
* typerror on default_currency ([7d6984c](7d6984c873))
* update develop_version in hooks ([6c8e0fd](6c8e0fd1fb))
* validate component quantity according to BOM (backport [#43011](https://github.com/frappe/erpnext/issues/43011)) ([#43014](https://github.com/frappe/erpnext/issues/43014)) ([fee2255](fee2255661))
2024-09-05 11:44:28 +00:00
ruthra kumar
11a6ebaeef Merge pull request #43026 from frappe/version-15-hotfix
chore: release v15
2024-09-05 17:13:12 +05:30
mergify[bot]
d2b2002664 fix: auto reorder material request mail issue (backport #43066) (#43068)
fix: auto reorder material request mail issue (#43066)

fix: auto reorder matreial request mail issue
(cherry picked from commit a8055a6da9)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-09-05 16:00:47 +05:30
mergify[bot]
56dad7d365 fix: disabled batches showing in the list (backport #43024) (#43069)
fix: disabled batches showing in the list (#43024)

(cherry picked from commit c13a147df1)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-09-05 16:00:33 +05:30
Khushi Rawat
6869e5dde9 Merge pull request #43039 from khushi8112/backport-asset-value-through-landed-cost-voucher
fix: backport asset value through landed cost voucher
2024-09-05 14:53:51 +05:30
Khushi Rawat
5d5ec2ab7c chore: resolve test failing 2024-09-05 14:31:05 +05:30
Khushi Rawat
f5a4ec129b chore: patch correction 2024-09-05 14:31:05 +05:30
Khushi Rawat
e185a06a15 refactor: rename to in SLE query functions 2024-09-05 14:31:05 +05:30
Khushi Rawat
193d7981ea chore: linters/semgrep check 2024-09-05 14:31:05 +05:30
Khushi Rawat
957eabf53e chore: resolved linter warnings with #nosemgrep 2024-09-05 14:31:05 +05:30
Khushi Rawat
3bb186736d fix: improve asset item matching logic 2024-09-05 14:31:05 +05:30
Khushi Rawat
1121c6663f fix: link Purchase Invoice and Receipt Items to Asset 2024-09-05 14:31:05 +05:30
Smit Vora
944479313c Merge pull request #43046 from frappe/mergify/bp/version-15-hotfix/pr-42988
fix: adjust price insertion logic for internal suppliers/customers (backport #42988)
2024-09-04 13:19:52 +05:30
Lakshit Jain
daa75eea00 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:27:47 +00:00
ruthra kumar
a2b7fc18ab Merge pull request #43033 from frappe/mergify/bp/version-15-hotfix/pr-43030
fix: typerror on default_currency (backport #43030)
2024-09-03 16:51:22 +05:30
ruthra kumar
7d6984c873 fix: typerror on default_currency
(cherry picked from commit 4a7cc4da87)
2024-09-03 11:11:29 +00:00
Nihantra C. Patel
64cbf446bd Merge pull request #43031 from frappe/mergify/bp/version-15-hotfix/pr-43029
fix: move setup_queries to refresh event for company (backport #43029)
2024-09-03 16:12:27 +05:30
Nihantra C. Patel
b99cdb5be7 fix: company accounts setup_queries
(cherry picked from commit 80ace72541)
2024-09-03 10:36:29 +00:00
ruthra kumar
d6de50634f Merge pull request #43028 from frappe/mergify/bp/version-15-hotfix/pr-42974
fix: retain date filter when redirecting in Profit and Loss report (backport #42974)
2024-09-03 15:51:46 +05:30
vishnu
4d7c0c004a fix: indentation
(cherry picked from commit 598e9c1390)
2024-09-03 10:14:32 +00:00
vishnu
f0e3fb466a fix: retain date filter when redirecting in Profit and Loss report
(cherry picked from commit bb29fc4c3d)
2024-09-03 10:14:31 +00:00
Khushi Rawat
8337439589 Merge pull request #43018 from frappe/mergify/bp/version-15-hotfix/pr-43015
chore: test case failing issue (backport #43015)
2024-09-03 03:55:32 +05:30
Khushi Rawat
88e5ed7998 chore: test case failing issue
(cherry picked from commit 0bdffdfa98)
2024-09-02 22:09:22 +00:00
mergify[bot]
fee2255661 fix: validate component quantity according to BOM (backport #43011) (#43014)
* fix: validate component quantity according to BOM (#43011)

(cherry picked from commit f3b91d4d62)

# Conflicts:
#	erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-09-02 16:29:30 +05:30
Nihantra C. Patel
23d91145d0 Merge pull request #43009 from frappe/mergify/bp/version-15-hotfix/pr-43008
fix: update develop_version in hooks (backport #43008)
2024-09-02 12:21:03 +05:30
Nihantra C. Patel
6c8e0fd1fb fix: update develop_version in hooks
(cherry picked from commit 01b345e046)
2024-09-02 06:13:37 +00:00
Shariq Ansari
512a171ad5 Merge pull request #42994 from frappe/mergify/bp/version-15-hotfix/pr-42993
fix: added app permission check for apps page (backport #42993)
2024-08-30 18:05:55 +05:30
Shariq Ansari
30f034555b chore: linter fix
(cherry picked from commit 1d9ed27a89)
2024-08-30 12:06:10 +00:00
Shariq Ansari
a35ce12d60 fix: added app permission check for apps page
(cherry picked from commit e8f8fb8a8f)
2024-08-30 12:06:10 +00:00
Khushi Rawat
8cf057849e Merge pull request #42986 from frappe/mergify/bp/version-15-hotfix/pr-42857
fix(capitalization): debit cwip account instead of fixed asset account (backport #42857)
2024-08-30 00:46:06 +05:30
Khushi Rawat
f3c60ea0a7 fix(capitalization): debit cwip account instead of fixed asset account (#42857)
* fix(capitalization): debit cwip account instead of fixed asset account

* fix: post entries for capitalized asset through background jobs

* chore: run pre-commit

* fix: correct GL entries posting for composite assets

* fix(minor): resolve failing check

* chore: update gl entry check logic

* chore: handle none values

(cherry picked from commit 5d99f17583)
2024-08-29 11:01:03 +00:00
ruthra kumar
c724573a18 Merge pull request #42985 from frappe/mergify/bp/version-15-hotfix/pr-42983
refactor: link utility report with bank reconciliation statement (backport #42983)
2024-08-29 16:19:47 +05:30
ruthra kumar
53e1b57354 refactor: link utility report with bank reconciliation statement
(cherry picked from commit 00eac65712)
2024-08-29 10:42:57 +00:00
mergify[bot]
42e7725442 fix: add the company in payment request bcz delete company transactions (backport #42664) (#42982)
* fix: add the company in payment request bcz delete company transactions

(cherry picked from commit 12834ccf9a)

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

* fix: link company when make payment request

(cherry picked from commit e3008843d1)

* fix: add the company in payment request bcz delete company transactions --conflicts

---------

Co-authored-by: Nihantra Patel <nihantra@frappe.io>
Co-authored-by: Nihantra C. Patel <141945075+Nihantra-Patel@users.noreply.github.com>
2024-08-29 15:41:08 +05:30
ruthra kumar
18b7af977c Merge pull request #42981 from frappe/mergify/bp/version-15-hotfix/pr-42979
refactor: better UX on Bank Clearance tool (backport #42979)
2024-08-29 14:07:49 +05:30
ruthra kumar
db746a4def refactor: better UX on Bank Clearance tool
(cherry picked from commit 6a06e26d04)
2024-08-29 08:25:12 +00:00
Khushi Rawat
3d5fb5fc90 Merge pull request #42977 from frappe/mergify/bp/version-15-hotfix/pr-42976
fix: don't allow capitalizing only service item for new composite asset (backport #42976)
2024-08-29 11:47:28 +05:30
Khushi Rawat
a833010d2b fix: don't allow capitalizing only service item for new composite asset
(cherry picked from commit f1d2138258)
2024-08-28 20:43:15 +00:00
ruthra kumar
0d6148f218 Merge pull request #42968 from frappe/mergify/bp/version-15-hotfix/pr-42966
fix: typeerror on Payment Entry (backport #42966)
2024-08-28 13:21:32 +05:30
ruthra kumar
6d51d14dfd fix: typeerror on Payment Entry
(cherry picked from commit e9cf8937cd)
2024-08-28 06:58:04 +00:00
Frappe PR Bot
cb2cb4447a chore(release): Bumped to Version 15.34.0
# [15.34.0](https://github.com/frappe/erpnext/compare/v15.33.5...v15.34.0) (2024-08-28)

### Bug Fixes

* calculation correction for annual depreciation ([fa85482](fa85482662))
* call 'process' directly instead of creating 'process_subscripti ([7582827](758282739e))
* Cannot read properties of null (reading 'doctype') (backport [#42941](https://github.com/frappe/erpnext/issues/42941)) ([#42943](https://github.com/frappe/erpnext/issues/42943)) ([2c99075](2c99075899))
* Column 'valuation_rate' cannot be null (backport [#42909](https://github.com/frappe/erpnext/issues/42909)) ([#42913](https://github.com/frappe/erpnext/issues/42913)) ([8c350d4](8c350d43b2))
* custom stock entry type issue (backport [#42835](https://github.com/frappe/erpnext/issues/42835)) ([#42846](https://github.com/frappe/erpnext/issues/42846)) ([831e2aa](831e2aaf18))
* do not copy date fields in opportunity doctype ([7401dc4](7401dc4015))
* get amount with taxes and charges from payment entry ([c54e97b](c54e97b89a))
* include erpnext in apps page ([7428df8](7428df8778))
* incorrect in and out qty in the Batch-Wise Balance History (backport [#42866](https://github.com/frappe/erpnext/issues/42866)) ([#42876](https://github.com/frappe/erpnext/issues/42876)) ([d9ca680](d9ca680a29))
* incorrect Received Qty Amount in Purchase Order Analysis (backport [#42852](https://github.com/frappe/erpnext/issues/42852)) ([#42854](https://github.com/frappe/erpnext/issues/42854)) ([72c1609](72c16097d6))
* last purchase rate not updated from purchase invoice (backport [#42847](https://github.com/frappe/erpnext/issues/42847)) ([#42853](https://github.com/frappe/erpnext/issues/42853)) ([2203ea9](2203ea9301))
* LCV based on purchase invoice amount with multi-currency (backport [#42890](https://github.com/frappe/erpnext/issues/42890)) ([#42894](https://github.com/frappe/erpnext/issues/42894)) ([ff868a9](ff868a9290))
* make party naming sequential when naming_by set as auto name ([0650c22](0650c22b53))
* not able to make stock entry against MR (backport [#42874](https://github.com/frappe/erpnext/issues/42874)) ([#42875](https://github.com/frappe/erpnext/issues/42875)) ([08bed61](08bed618f6))
* same posting date and time, creation causing incorrect balance qty (backport [#42904](https://github.com/frappe/erpnext/issues/42904)) ([#42920](https://github.com/frappe/erpnext/issues/42920)) ([2624892](26248924b6))
* spec mobile and email fields for notifications ([f56ee58](f56ee58e81))
* timeout while submitting stock entry (backport [#42929](https://github.com/frappe/erpnext/issues/42929)) ([#42931](https://github.com/frappe/erpnext/issues/42931)) ([ec26c92](ec26c92263))
* unsupported operand type(s) for *: 'float' and 'NoneType' (backport [#42916](https://github.com/frappe/erpnext/issues/42916)) ([#42918](https://github.com/frappe/erpnext/issues/42918)) ([8d29dc6](8d29dc6a81))
* update dimesions in exchange_gain_loss jv based on base document ([caa6ca1](caa6ca1d0b))
* Update get_amount to return currency precision grand total ([976abf7](976abf7b3c))
* use of incorrect attribute ([80244ba](80244bafa4))

### Features

* added finance book filter in depreciation and balances report ([5bdd298](5bdd2989c6))
* Disassembly Order (backport [#42655](https://github.com/frappe/erpnext/issues/42655)) ([#42957](https://github.com/frappe/erpnext/issues/42957)) ([8d8dd0c](8d8dd0cd2b))
* report to identify incorrectly cleared cheques ([25193c5](25193c5e92))
2024-08-28 05:04:27 +00:00
ruthra kumar
b3a8fe9391 Merge pull request #42937 from frappe/version-15-hotfix
chore: release v15
2024-08-28 10:33:09 +05:30
ruthra kumar
2a820a85ed Merge pull request #42952 from frappe/mergify/bp/version-15-hotfix/pr-41925
fix: spec mobile and email fields for notifications (backport #41925)
2024-08-28 10:13:26 +05:30
mergify[bot]
8d8dd0cd2b feat: Disassembly Order (backport #42655) (#42957)
* feat: Disassembly Order (#42655)

(cherry picked from commit 663a08e4cd)

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

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-08-27 23:07:37 +05:30
ruthra kumar
3a5f19853a Merge pull request #42947 from frappe/mergify/bp/version-15-hotfix/pr-42923
fix: get amount with taxes and charges from payment entry (backport #42923)
2024-08-27 17:39:30 +05:30
Khushi Rawat
c0dd794e15 Merge pull request #42944 from frappe/mergify/bp/version-15-hotfix/pr-42939
feat: added finance book filter in depreciation and balances report (backport #42939)
2024-08-27 17:18:10 +05:30
David
f56ee58e81 fix: spec mobile and email fields for notifications
(cherry picked from commit 18993a97ce)
2024-08-27 11:45:40 +00:00
mergify[bot]
2c99075899 fix: Cannot read properties of null (reading 'doctype') (backport #42941) (#42943)
fix: Cannot read properties of null (reading 'doctype') (#42941)

(cherry picked from commit 86d3a9ab03)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-08-27 17:15:23 +05:30
ruthra kumar
01cd509113 Merge pull request #42950 from frappe/mergify/bp/version-15-hotfix/pr-41949
refactor: item-wise purchase history (query to script report) (backport #41949)
2024-08-27 17:11:46 +05:30
ruthra kumar
5563bea789 Merge pull request #42949 from frappe/mergify/bp/version-15-hotfix/pr-42936
fix: remove unnecessary condition on 'voucher_no' (backport #42936)
2024-08-27 17:08:56 +05:30
Nihantra Patel
a0a932a235 refactor: item-wise purchase history (query to script report) --upd
(cherry picked from commit 2851764ed6)
2024-08-27 11:19:03 +00:00
Nihantra Patel
6f75a3c617 refactor: item-wise purchase history (query to script report) --prettier
(cherry picked from commit 7bae18aba8)
2024-08-27 11:19:03 +00:00
Nihantra Patel
a4d3934f75 refactor: item-wise purchase history (query to script report) --prettier
(cherry picked from commit f740c94363)
2024-08-27 11:19:03 +00:00
Nihantra Patel
b4171e4bd9 refactor: item-wise purchase history (query to script report) -- formatter
(cherry picked from commit 003a9608dc)
2024-08-27 11:19:03 +00:00
Nihantra Patel
76d32ab07a refactor: item-wise purchase history (query to script report)
(cherry picked from commit 49331e6109)
2024-08-27 11:19:03 +00:00
Nihantra Patel
3d469db47b refactor: item-wise purchase history (query to script report)
(cherry picked from commit 5de91cf55e)
2024-08-27 11:19:02 +00:00
ruthra kumar
80244bafa4 fix: use of incorrect attribute
(cherry picked from commit fb32d2cafb)
2024-08-27 11:06:38 +00:00
venkat102
c54e97b89a fix: get amount with taxes and charges from payment entry
(cherry picked from commit b3a901b631)
2024-08-27 11:01:36 +00:00
ruthra kumar
14202fae06 Merge pull request #42453 from mujeerhashmi/patch-2
fix: Update get_amount to return currency precision grand total
2024-08-27 16:29:08 +05:30
Khushi Rawat
9fc0ac1a92 chore: resolved linter warnings with #nosemgrep
(cherry picked from commit adf1e487e1)
2024-08-27 10:54:11 +00:00
Khushi Rawat
5bdd2989c6 feat: added finance book filter in depreciation and balances report
(cherry picked from commit 45804c68f0)
2024-08-27 10:54:10 +00:00
ruthra kumar
42dccadff1 Merge pull request #42940 from frappe/mergify/bp/version-15-hotfix/pr-42921
fix: make party naming sequential when naming_by set as auto name (backport #42921)
2024-08-27 16:13:00 +05:30
venkat102
0650c22b53 fix: make party naming sequential when naming_by set as auto name
(cherry picked from commit c9015f7c04)
2024-08-27 10:07:50 +00:00
mergify[bot]
ec26c92263 fix: timeout while submitting stock entry (backport #42929) (#42931)
fix: timeout while submitting stock entry (#42929)

(cherry picked from commit ca2fde891e)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-08-27 11:03:45 +05:30
mergify[bot]
26248924b6 fix: same posting date and time, creation causing incorrect balance qty (backport #42904) (#42920)
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)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-08-27 09:30:47 +05:30
mergify[bot]
8d29dc6a81 fix: unsupported operand type(s) for *: 'float' and 'NoneType' (backport #42916) (#42918)
fix: unsupported operand type(s) for *: 'float' and 'NoneType' (#42916)

(cherry picked from commit 10434742e9)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-08-26 18:18:31 +05:30
ruthra kumar
6eb50fa300 Merge pull request #42915 from frappe/mergify/bp/version-15-hotfix/pr-42887
fix: update dimesions in exchange_gain_loss jv based on base document (backport #42887)
2024-08-26 17:56:21 +05:30
mergify[bot]
8c350d43b2 fix: Column 'valuation_rate' cannot be null (backport #42909) (#42913)
fix: Column 'valuation_rate' cannot be null (#42909)

(cherry picked from commit 92bde71ab1)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-08-26 17:35:20 +05:30
ljain112
caa6ca1d0b fix: update dimesions in exchange_gain_loss jv based on base document
(cherry picked from commit 96df19149d)
2024-08-26 12:01:28 +00:00
ruthra kumar
47f1714be4 Merge pull request #42912 from frappe/mergify/bp/version-15-hotfix/pr-42910
feat: utility report to better diagnose incorrectly cleared Cheques and Deposits (backport #42910)
2024-08-26 16:32:29 +05:30
ruthra kumar
ecb0506dba refactor: build dictionary for Journal
remove redundant filter

(cherry picked from commit 2144e0337d)
2024-08-26 10:11:24 +00:00
ruthra kumar
42382b3945 chore: remove redundant column
(cherry picked from commit 74b36db24e)
2024-08-26 10:11:23 +00:00
ruthra kumar
993114942e refactor: build dict for payment entry
(cherry picked from commit 784dec24c8)
2024-08-26 10:11:23 +00:00
ruthra kumar
5a28a1728e refactor: working state with minimum functions
(cherry picked from commit 4cd023444a)
2024-08-26 10:11:23 +00:00
ruthra kumar
80a5df0e96 refactor: barebones functions
(cherry picked from commit ceaa1be729)
2024-08-26 10:11:23 +00:00
ruthra kumar
25193c5e92 feat: report to identify incorrectly cleared cheques
(cherry picked from commit 28890fa833)
2024-08-26 10:11:23 +00:00
ruthra kumar
1ee14ac135 Merge pull request #42907 from frappe/mergify/bp/version-15-hotfix/pr-42905
refactor: better err msg on clearance tool (backport #42905)
2024-08-26 15:29:48 +05:30
ruthra kumar
a3e5ffe915 refactor: better err msg on clearance tool
(cherry picked from commit 092411b54f)
2024-08-26 08:55:01 +00:00
mergify[bot]
ff868a9290 fix: LCV based on purchase invoice amount with multi-currency (backport #42890) (#42894)
fix: LCV based on purchase invoice amount with multi-currency (#42890)

(cherry picked from commit 6721ae76de)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-08-26 13:25:18 +05:30
Smit Vora
247ae7a965 Merge pull request #42892 from frappe/mergify/bp/version-15-hotfix/pr-42848
fix: do not copy date fields in opportunity doctype (backport #42848)
2024-08-24 13:12:13 +05:30
Smit Vora
3a149b3c9b chore: resolve conflicts 2024-08-24 12:38:49 +05:30
mergify[bot]
08bed618f6 fix: not able to make stock entry against MR (backport #42874) (#42875)
fix: not able to make stock entry against MR (#42874)

(cherry picked from commit 63ca1025bc)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-08-23 15:22:39 +05:30
mergify[bot]
d9ca680a29 fix: incorrect in and out qty in the Batch-Wise Balance History (backport #42866) (#42876)
fix: incorrect in and out qty in the Batch-Wise Balance History (#42866)

(cherry picked from commit ce7f6ee71c)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-08-23 15:22:23 +05:30
ljain112
7401dc4015 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:33 +00:00
Frappe PR Bot
eb7e063d5c chore(release): Bumped to Version 15.33.5
## [15.33.5](https://github.com/frappe/erpnext/compare/v15.33.4...v15.33.5) (2024-08-23)

### Bug Fixes

* include erpnext in apps page ([be736cf](be736cf641))
2024-08-23 00:37:45 +00:00
ruthra kumar
0dbe79645c Merge pull request #42882 from frappe/mergify/bp/version-15/pr-42727
fix: include erpnext in apps page (backport #42727)
2024-08-23 06:05:20 +05:30
Shariq Ansari
995773088a chore: renamed include_as_app to include_in_apps_screen
(cherry picked from commit 5280132423)
2024-08-23 00:19:55 +00:00
Shariq Ansari
be736cf641 fix: include erpnext in apps page
(cherry picked from commit 1d52ef7afe)
2024-08-23 00:19:54 +00:00
ruthra kumar
85089d3d64 Merge pull request #42871 from frappe/mergify/bp/version-15-hotfix/pr-42867
fix: call 'process' directly instead of creating 'process_subscription' (backport #42867)
2024-08-22 17:51:37 +05:30
ruthra kumar
eed6d2b81c Merge pull request #42869 from frappe/mergify/bp/version-15-hotfix/pr-42851
refactor: Allow equity type Account in Payment Entry for shareholders (backport #42851)
2024-08-22 17:30:50 +05:30
Shariq Ansari
edf53f1ab7 Merge pull request #42863 from frappe/mergify/bp/version-15-hotfix/pr-42727
fix: include erpnext in apps page (backport #42727)
2024-08-22 17:24:33 +05:30
ruthra kumar
758282739e 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
49d995c3ac refactor: filter shareholder on company
(cherry picked from commit 63ad9f4f86)
2024-08-22 11:40:39 +00:00
ruthra kumar
72ca2ec9a5 refactor: allow equity types on Payment Entry
(cherry picked from commit 6cbf98294a)
2024-08-22 11:40:39 +00:00
Shariq Ansari
4297895bd9 chore: renamed include_as_app to include_in_apps_screen
(cherry picked from commit 5280132423)
2024-08-22 06:57:25 +00:00
Shariq Ansari
7428df8778 fix: include erpnext in apps page
(cherry picked from commit 1d52ef7afe)
2024-08-22 06:57:25 +00:00
Khushi Rawat
767c79663c Merge pull request #42862 from frappe/mergify/bp/version-15-hotfix/pr-42861
fix(minor): calculation correction for annual depreciation (backport #42861)
2024-08-22 11:56:08 +05:30
Khushi Rawat
fa85482662 fix: calculation correction for annual depreciation
(cherry picked from commit f440243b75)
2024-08-22 05:48:09 +00:00
mergify[bot]
831e2aaf18 fix: custom stock entry type issue (backport #42835) (#42846)
* fix: custom stock entry type issue (#42835)

(cherry picked from commit 9c82c2b5d3)

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

* chore: fix conflicts

* chore: fix linters issue

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-08-21 16:46:05 +05:30
Frappe PR Bot
9ac665b4bd chore(release): Bumped to Version 15.33.4
## [15.33.4](https://github.com/frappe/erpnext/compare/v15.33.3...v15.33.4) (2024-08-21)

### Bug Fixes

* incorrect Received Qty Amount in Purchase Order Analysis (backport [#42852](https://github.com/frappe/erpnext/issues/42852)) (backport [#42854](https://github.com/frappe/erpnext/issues/42854)) ([#42856](https://github.com/frappe/erpnext/issues/42856)) ([8d8d84b](8d8d84bae4))
* last purchase rate not updated from purchase invoice (backport [#42847](https://github.com/frappe/erpnext/issues/42847)) (backport [#42853](https://github.com/frappe/erpnext/issues/42853)) ([#42855](https://github.com/frappe/erpnext/issues/42855)) ([9f4cb98](9f4cb98de6))
2024-08-21 11:08:23 +00:00
mergify[bot]
8d8d84bae4 fix: incorrect Received Qty Amount in Purchase Order Analysis (backport #42852) (backport #42854) (#42856)
fix: incorrect Received Qty Amount in Purchase Order Analysis (backport #42852) (#42854)

fix: incorrect Received Qty Amount in Purchase Order Analysis (#42852)

(cherry picked from commit fb846ffa12)

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

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2024-08-21 16:37:08 +05:30
mergify[bot]
9f4cb98de6 fix: last purchase rate not updated from purchase invoice (backport #42847) (backport #42853) (#42855)
fix: last purchase rate not updated from purchase invoice (backport #42847) (#42853)

fix: last purchase rate not updated from purchase invoice (#42847)

(cherry picked from commit 5b9309cf34)

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

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2024-08-21 16:37:00 +05:30
mergify[bot]
72c16097d6 fix: incorrect Received Qty Amount in Purchase Order Analysis (backport #42852) (#42854)
fix: incorrect Received Qty Amount in Purchase Order Analysis (#42852)

(cherry picked from commit fb846ffa12)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-08-21 16:12:33 +05:30
mergify[bot]
2203ea9301 fix: last purchase rate not updated from purchase invoice (backport #42847) (#42853)
fix: last purchase rate not updated from purchase invoice (#42847)

(cherry picked from commit 5b9309cf34)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-08-21 15:20:50 +05:30
Frappe PR Bot
28c9f2adab chore(release): Bumped to Version 15.33.3
## [15.33.3](https://github.com/frappe/erpnext/compare/v15.33.2...v15.33.3) (2024-08-21)

### Bug Fixes

* Auto Create Serial and Batch Bundle For Outward (backport [#42778](https://github.com/frappe/erpnext/issues/42778)) ([#42792](https://github.com/frappe/erpnext/issues/42792)) ([7cc7179](7cc7179b05))
* backport german translations from develop ([9e9de4c](9e9de4c99e))
* bank reconcilation tool cost center company filter adding ([cd59940](cd5994017c))
* Create Sales Order from Quotation for Prospect ([f547bef](f547befeb9))
* create SO from Quot for Prospect --conflicts ([ec0201c](ec0201cb85))
* create SO from Quot for Prospect --conflicts ([5d7fb1d](5d7fb1d945))
* disable rename from warehouse ([3a1ad6e](3a1ad6e844))
* disable rename from warehouse ([40abd82](40abd82e2d))
* dropping index to improve performance (backport [#42820](https://github.com/frappe/erpnext/issues/42820)) ([#42821](https://github.com/frappe/erpnext/issues/42821)) ([b24de3e](b24de3e35b))
* german translations ([751c209](751c20984f))
* german translations of "HR" ([6f7fdbe](6f7fdbefac))
* ignore pricing rule while making DN from Pick List (backport [#42763](https://github.com/frappe/erpnext/issues/42763)) ([#42768](https://github.com/frappe/erpnext/issues/42768)) ([aba54ba](aba54ba18f))
* not able to create the batch (backport [#42784](https://github.com/frappe/erpnext/issues/42784)) ([#42785](https://github.com/frappe/erpnext/issues/42785)) ([0f9849e](0f9849e672))
* **patch:** replace repost with direct sql to update 'against_voucher ([e420fa9](e420fa9779))
* removed extra filter condition ([b84ca04](b84ca04975))
* set up filters for dimensions ([abb8866](abb88662c1))
* translatability of boldened text ([4914481](4914481105))
* update the testcase format ([33542cb](33542cb909))
* update the testcase format ([549dc28](549dc286d0))

### Performance Improvements

* asset creation from purchase receipt ([1040198](1040198ce1))
* data import for stock entries (backport [#42711](https://github.com/frappe/erpnext/issues/42711)) ([#42819](https://github.com/frappe/erpnext/issues/42819)) ([0344442](0344442d42))
2024-08-21 05:22:44 +00:00
ruthra kumar
d04f7ffe87 Merge pull request #42832 from frappe/version-15-hotfix
chore: release v15
2024-08-21 10:51:29 +05:30
Khushi Rawat
21e5c01f11 Merge pull request #42828 from frappe/mergify/bp/version-15-hotfix/pr-42824
perf: asset creation from purchase receipt (backport #42824)
2024-08-20 15:17:51 +05:30
mergify[bot]
ae6c1a30ac refactor: brand js and allow quick entry (backport #42829) (#42830)
* refactor: brand js and allow quick entry (#42829)

* refactor: brand js and allow quick entry

* refactor: brand js and allow quick entry --prettier

* refactor: brand js and allow quick entry --prettier

(cherry picked from commit a1183f0165)

# Conflicts:
#	erpnext/setup/doctype/brand/brand.json

* refactor: brand js and allow quick entry --refactor

---------

Co-authored-by: Nihantra C. Patel <141945075+Nihantra-Patel@users.noreply.github.com>
2024-08-20 15:03:23 +05:30
Khushi Rawat
1040198ce1 perf: asset creation from purchase receipt
(cherry picked from commit 6e84fc5143)
2024-08-20 08:12:48 +00:00
Nihantra C. Patel
69878b7847 Merge pull request #42826 from frappe/mergify/bp/version-15-hotfix/pr-42379
fix: Create Sales Order from Quotation for Prospect (backport #42379)
2024-08-20 11:57:29 +05:30
Nihantra C. Patel
ec0201cb85 fix: create SO from Quot for Prospect --conflicts 2024-08-20 11:34:27 +05:30
Nihantra C. Patel
5d7fb1d945 fix: create SO from Quot for Prospect --conflicts 2024-08-20 11:31:49 +05:30
Nihantra Patel
33542cb909 fix: update the testcase format
(cherry picked from commit ee44022249)
2024-08-20 05:11:02 +00:00
Nihantra Patel
549dc286d0 fix: update the testcase format
(cherry picked from commit 29d50b770e)
2024-08-20 05:11:02 +00:00
Nihantra Patel
f547befeb9 fix: Create Sales Order from Quotation for Prospect
(cherry picked from commit 2f63fae31d)

# Conflicts:
#	erpnext/selling/doctype/quotation/quotation.py
2024-08-20 05:11:02 +00:00
ruthra kumar
a144059c7c Merge pull request #42825 from frappe/mergify/bp/version-15-hotfix/pr-42731
fix: bank reconcilation tool cost center company filter adding (backport #42731)
2024-08-20 10:35:21 +05:30
Parameshwari Palanisamy
b96361e837 refactor: update dialog_manager.js
(cherry picked from commit 6d19aae423)
2024-08-20 04:44:47 +00:00
creative-paramu
cd5994017c fix: bank reconcilation tool cost center company filter adding
(cherry picked from commit 6e2ac09821)
2024-08-20 04:44:47 +00:00
Raffael Meyer
431fb62803 Merge pull request #42823 from barredterra/backport-german-translations 2024-08-19 19:25:16 +02:00
barredterra
9e9de4c99e fix: backport german translations from develop 2024-08-19 16:43:22 +02:00
Raffael Meyer
d3369368db Merge pull request #42804 from frappe/mergify/bp/version-15-hotfix/pr-42800
fix: translatability of boldened text (backport #42800)
2024-08-19 16:03:11 +02:00
mergify[bot]
b24de3e35b fix: dropping index to improve performance (backport #42820) (#42821)
fix: dropping index to improve performance (#42820)

fix: droping index to improve peformance
(cherry picked from commit 5404b21c7d)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-08-19 17:34:24 +05:30
mergify[bot]
0344442d42 perf: data import for stock entries (backport #42711) (#42819)
perf: data import for stock entries (#42711)

(cherry picked from commit 1511280464)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-08-19 14:55:41 +05:30
Raffael Meyer
0decd0955b Merge pull request #42805 from barredterra/de-hr-transl 2024-08-17 02:49:22 +02:00
barredterra
6f7fdbefac fix: german translations of "HR" 2024-08-17 02:48:39 +02:00
barredterra
dbd466b6b2 chore: resolve conflicts 2024-08-17 02:24:58 +02:00
barredterra
4914481105 fix: translatability of boldened text
(cherry picked from commit af0ae930ca)

# Conflicts:
#	erpnext/controllers/accounts_controller.py
2024-08-17 00:21:05 +00:00
Raffael Meyer
8514c01a91 Merge pull request #42798 from barredterra/20240816-de-translations 2024-08-16 19:11:37 +02:00
barredterra
751c20984f fix: german translations 2024-08-16 18:54:06 +02:00
ruthra kumar
528f42c713 Merge pull request #42794 from frappe/mergify/bp/version-15-hotfix/pr-42791
fix(patch): Use sql to update 'against_voucher' rather than reposting (backport #42791)
2024-08-16 15:55:14 +05:30
mergify[bot]
7cc7179b05 fix: Auto Create Serial and Batch Bundle For Outward (backport #42778) (#42792)
fix: Auto Create Serial and Batch Bundle For Outward (#42778)

(cherry picked from commit 48c3b0d094)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-08-16 15:54:14 +05:30
ruthra kumar
cfaad685a4 chore: update patches.txt
(cherry picked from commit 1721175a20)
2024-08-16 10:02:44 +00:00
ruthra kumar
e420fa9779 fix(patch): replace repost with direct sql to update 'against_voucher
(cherry picked from commit 13bb48434f)
2024-08-16 10:02:44 +00:00
mergify[bot]
0f9849e672 fix: not able to create the batch (backport #42784) (#42785)
fix: not able to create the batch (#42784)

(cherry picked from commit 32c4fab14f)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-08-16 11:39:15 +05:30
ruthra kumar
3be037221a Merge pull request #42783 from frappe/mergify/bp/version-15-hotfix/pr-42774
refactor: Loosen account currency validation on groups (backport #42774)
2024-08-16 10:41:56 +05:30
ruthra kumar
b2e108afcc refactor: allow foreign currency accounts in Supplier Group
(cherry picked from commit 164498bafb)
2024-08-16 04:30:12 +00:00
ruthra kumar
114a5f8cca refactor: allow foreign currency accounts in customer group
(cherry picked from commit 066e935892)
2024-08-16 04:30:12 +00:00
mergify[bot]
aba54ba18f fix: ignore pricing rule while making DN from Pick List (backport #42763) (#42768)
fix: ignore pricing rule while making DN from Pick List (#42763)

(cherry picked from commit 0db82ec93a)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-08-14 23:03:01 +05:30
Nihantra C. Patel
6a7768367e Merge pull request #42771 from frappe/mergify/bp/version-15-hotfix/pr-42769
fix: disable rename from warehouse (backport #42769)
2024-08-14 17:15:26 +05:30
Nihantra C. Patel
3a1ad6e844 fix: disable rename from warehouse 2024-08-14 16:37:36 +05:30
Nihantra C. Patel
40abd82e2d fix: disable rename from warehouse
(cherry picked from commit c1812f74e8)

# Conflicts:
#	erpnext/stock/doctype/warehouse/warehouse.json
2024-08-14 11:03:49 +00:00
Khushi Rawat
2933a86458 Merge pull request #42767 from frappe/mergify/bp/version-15-hotfix/pr-42765
fix: set up filters for dimensions (backport #42765)
2024-08-14 14:45:14 +05:30
Khushi Rawat
ecf0d0b388 chore: linters check
(cherry picked from commit 0d42793397)
2024-08-14 09:07:38 +00:00
Khushi Rawat
b84ca04975 fix: removed extra filter condition
(cherry picked from commit 6fa29376a0)
2024-08-14 09:07:38 +00:00
Khushi Rawat
abb88662c1 fix: set up filters for dimensions
(cherry picked from commit 2fd8de2f76)
2024-08-14 09:07:37 +00:00
Syed Mujeer Hashmi
976abf7b3c 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.
2024-07-24 11:59:45 +05:30
99 changed files with 2086 additions and 354 deletions

View File

@@ -2,8 +2,9 @@ import functools
import inspect
import frappe
from frappe.utils.user import is_website_user
__version__ = "15.33.2"
__version__ = "15.34.1"
def get_default_company(user=None):
@@ -149,3 +150,13 @@ def allow_regional(fn):
return frappe.get_attr(overrides[function_path][-1])(*args, **kwargs)
return caller
def check_app_permission():
if frappe.session.user == "Administrator":
return True
if is_website_user():
return False
return True

View File

@@ -202,7 +202,7 @@ class Account(NestedSet):
msg = _(
"There are ledger entries against this account. Changing {0} to non-{1} in live system will cause incorrect output in 'Accounts {2}' report"
).format(
frappe.bold("Account Type"), doc_before_save.account_type, doc_before_save.account_type
frappe.bold(_("Account Type")), doc_before_save.account_type, doc_before_save.account_type
)
frappe.msgprint(msg)
self.add_comment("Comment", msg)

View File

@@ -38,6 +38,11 @@ frappe.ui.form.on("Bank Clearance", {
frm.add_custom_button(__("Get Payment Entries"), () => frm.trigger("get_payment_entries"));
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 +50,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 +59,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
from pypika import Order
import erpnext
@@ -96,8 +96,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

@@ -35,6 +35,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],
@@ -90,6 +95,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],
@@ -297,7 +305,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
? frappe.get_doc(":Company", frm.doc.company)?.default_currency
: "";
frm.set_currency_labels(
@@ -412,6 +420,12 @@ frappe.ui.form.on("Payment Entry", {
return {
query: "erpnext.controllers.queries.employee_query",
};
} else if (frm.doc.party_type == "Shareholder") {
return {
filters: {
company: frm.doc.company,
},
};
}
});
@@ -644,7 +658,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

@@ -1740,7 +1740,7 @@ def get_outstanding_reference_documents(args, validate=False):
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

@@ -9,6 +9,7 @@
"transaction_date",
"column_break_2",
"naming_series",
"company",
"mode_of_payment",
"party_details",
"party_type",
@@ -390,13 +391,20 @@
"options": "Payment Request",
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"options": "Company",
"read_only": 1
}
],
"in_create": 1,
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2024-06-20 13:54:55.245774",
"modified": "2024-08-07 16:39:54.288002",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Request",

View File

@@ -15,7 +15,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.utilities import payment_app_import_guard
@@ -84,6 +84,7 @@ class PaymentRequest(Document):
subscription_plans: DF.Table[SubscriptionPlanDetail]
swift_number: DF.ReadOnly | None
transaction_date: DF.Date | None
company: DF.Link | None
# end: auto-generated types
def validate(self):
@@ -491,6 +492,7 @@ def make_payment_request(**args):
"message": gateway_account.get("message") or get_dummy_message(ref_doc),
"reference_doctype": args.dt,
"reference_name": args.dn,
"company": ref_doc.get("company"),
"party_type": args.get("party_type") or "Customer",
"party": args.get("party") or ref_doc.get("customer"),
"bank_account": bank_account,
@@ -552,7 +554,7 @@ def get_amount(ref_doc, payment_account=None):
grand_total = ref_doc.outstanding_amount
if grand_total > 0:
return grand_total
return flt(grand_total, get_currency_precision())
else:
frappe.throw(_("Payment Entry is already created"))

View File

@@ -87,19 +87,15 @@ class POSClosingEntry(StatusUpdater):
as_dict=1,
)[0]
if pos_invoice.consolidated_invoice:
invalid_row.setdefault("msg", []).append(
_("POS Invoice is {}").format(frappe.bold("already consolidated"))
)
invalid_row.setdefault("msg", []).append(_("POS Invoice is already consolidated"))
invalid_rows.append(invalid_row)
continue
if pos_invoice.pos_profile != self.pos_profile:
invalid_row.setdefault("msg", []).append(
_("POS Profile doesn't matches {}").format(frappe.bold(self.pos_profile))
_("POS Profile doesn't match {}").format(frappe.bold(self.pos_profile))
)
if pos_invoice.docstatus != 1:
invalid_row.setdefault("msg", []).append(
_("POS Invoice is not {}").format(frappe.bold("submitted"))
)
invalid_row.setdefault("msg", []).append(_("POS Invoice is not submitted"))
if pos_invoice.owner != self.user:
invalid_row.setdefault("msg", []).append(
_("POS Invoice isn't created by user {}").format(frappe.bold(self.owner))

View File

@@ -188,7 +188,7 @@ class POSInvoice(SalesInvoice):
def validate(self):
if not cint(self.is_pos):
frappe.throw(
_("POS Invoice should have {} field checked.").format(frappe.bold("Include Payment"))
_("POS Invoice should have the field {0} checked.").format(frappe.bold(_("Include Payment")))
)
# run on validate method of selling controller

View File

@@ -97,16 +97,15 @@ class POSInvoiceMergeLog(Document):
return_against_status = frappe.db.get_value("POS Invoice", return_against, "status")
if return_against_status != "Consolidated":
# if return entry is not getting merged in the current pos closing and if it is not consolidated
bold_unconsolidated = frappe.bold("not Consolidated")
msg = _("Row #{}: Original Invoice {} of return invoice {} is {}.").format(
d.idx, bold_return_against, bold_pos_invoice, bold_unconsolidated
)
msg = _(
"Row #{}: The original Invoice {} of return invoice {} is not consolidated."
).format(d.idx, bold_return_against, bold_pos_invoice)
msg += " "
msg += _(
"Original invoice should be consolidated before or along with the return invoice."
"The original invoice should be consolidated before or along with the return invoice."
)
msg += "<br><br>"
msg += _("You can add original invoice {} manually to proceed.").format(
msg += _("You can add the original invoice {} manually to proceed.").format(
bold_return_against
)
frappe.throw(msg)

View File

@@ -186,7 +186,8 @@ class PricingRule(Document):
if not self.priority:
throw(
_("As the field {0} is enabled, the field {1} is mandatory.").format(
frappe.bold("Apply Discount on Discounted Rate"), frappe.bold("Priority")
frappe.bold(_("Apply Discount on Discounted Rate")),
frappe.bold(_("Priority")),
)
)
@@ -194,7 +195,7 @@ class PricingRule(Document):
throw(
_(
"As the field {0} is enabled, the value of the field {1} should be more than 1."
).format(frappe.bold("Apply Discount on Discounted Rate"), frappe.bold("Priority"))
).format(frappe.bold(_("Apply Discount on Discounted Rate")), frappe.bold(_("Priority")))
)
def validate_applicable_for_selling_or_buying(self):

View File

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

@@ -377,16 +377,16 @@ class PurchaseInvoice(BuyingController):
if account.report_type != "Balance Sheet":
frappe.throw(
_(
"Please ensure {} account is a Balance Sheet account. You can change the parent account to a Balance Sheet account or select a different account."
).format(frappe.bold("Credit To")),
"Please ensure that the {0} account is a Balance Sheet account. You can change the parent account to a Balance Sheet account or select a different account."
).format(frappe.bold(_("Credit To"))),
title=_("Invalid Account"),
)
if self.supplier and account.account_type != "Payable":
frappe.throw(
_(
"Please ensure {} account {} is a Payable account. Change the account type to Payable or select a different account."
).format(frappe.bold("Credit To"), frappe.bold(self.credit_to)),
"Please ensure that the {0} account {1} is a Payable account. You can change the account type to Payable or select a different account."
).format(frappe.bold(_("Credit To")), frappe.bold(self.credit_to)),
title=_("Invalid Account"),
)
@@ -634,7 +634,7 @@ class PurchaseInvoice(BuyingController):
"To submit the invoice without purchase order please set {0} as {1} in {2}"
).format(
frappe.bold(_("Purchase Order Required")),
frappe.bold("No"),
frappe.bold(_("No")),
get_link_to_form("Buying Settings", "Buying Settings", "Buying Settings"),
)
throw(msg, title=_("Mandatory Purchase Order"))
@@ -655,7 +655,7 @@ class PurchaseInvoice(BuyingController):
"To submit the invoice without purchase receipt please set {0} as {1} in {2}"
).format(
frappe.bold(_("Purchase Receipt Required")),
frappe.bold("No"),
frappe.bold(_("No")),
get_link_to_form("Buying Settings", "Buying Settings", "Buying Settings"),
)
throw(msg, title=_("Mandatory Purchase Receipt"))
@@ -1262,7 +1262,11 @@ class PurchaseInvoice(BuyingController):
def update_gross_purchase_amount_for_linked_assets(self, item):
assets = frappe.db.get_all(
"Asset",
filters={"purchase_invoice": self.name, "item_code": item.item_code},
filters={
"purchase_invoice": self.name,
"item_code": item.item_code,
"purchase_invoice_item": ("in", [item.name, ""]),
},
fields=["name", "asset_quantity"],
)
for asset in assets:

View File

@@ -2236,6 +2236,62 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
self.assertEqual(pi_expected_values[i][1], gle.debit)
self.assertEqual(pi_expected_values[i][2], gle.credit)
def test_adjust_incoming_rate_from_pi_with_multi_currency(self):
from erpnext.stock.doctype.landed_cost_voucher.test_landed_cost_voucher import (
make_landed_cost_voucher,
)
frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 0)
frappe.db.set_single_value("Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate", 1)
# Increase the cost of the item
pr = make_purchase_receipt(
qty=10, rate=1, currency="USD", do_not_save=1, supplier="_Test Supplier USD"
)
pr.conversion_rate = 6300
pr.plc_conversion_rate = 1
pr.save()
pr.submit()
self.assertEqual(pr.conversion_rate, 6300)
self.assertEqual(pr.plc_conversion_rate, 1)
self.assertEqual(pr.base_grand_total, 6300 * 10)
stock_value_difference = frappe.db.get_value(
"Stock Ledger Entry",
{"voucher_type": "Purchase Receipt", "voucher_no": pr.name},
"stock_value_difference",
)
self.assertEqual(stock_value_difference, 6300 * 10)
make_landed_cost_voucher(
company=pr.company,
receipt_document_type="Purchase Receipt",
receipt_document=pr.name,
charges=3000,
distribute_charges_based_on="Qty",
)
pi = create_purchase_invoice_from_receipt(pr.name)
for row in pi.items:
row.rate = 1.1
pi.save()
pi.submit()
stock_value_difference = frappe.db.get_value(
"Stock Ledger Entry",
{"voucher_type": "Purchase Receipt", "voucher_no": pr.name},
"stock_value_difference",
)
self.assertEqual(stock_value_difference, 7230 * 10)
frappe.db.set_single_value("Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate", 0)
frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 1)
def set_advance_flag(company, flag, default_account):
frappe.db.set_value(

View File

@@ -514,7 +514,7 @@ class SalesInvoice(SellingController):
)
if pos_closing_entry and pos_closing_entry[0]:
msg = _("To cancel a {} you need to cancel the POS Closing Entry {}.").format(
frappe.bold("Consolidated Sales Invoice"),
frappe.bold(_("Consolidated Sales Invoice")),
get_link_to_form("POS Closing Entry", pos_closing_entry[0]),
)
frappe.throw(msg, title=_("Not Allowed"))
@@ -858,7 +858,7 @@ class SalesInvoice(SellingController):
if account.report_type != "Balance Sheet":
msg = (
_("Please ensure {} account is a Balance Sheet account.").format(frappe.bold("Debit To"))
_("Please ensure {} account is a Balance Sheet account.").format(frappe.bold(_("Debit To")))
+ " "
)
msg += _(
@@ -869,7 +869,7 @@ class SalesInvoice(SellingController):
if self.customer and account.account_type != "Receivable":
msg = (
_("Please ensure {} account {} is a Receivable account.").format(
frappe.bold("Debit To"), frappe.bold(self.debit_to)
frappe.bold(_("Debit To")), frappe.bold(self.debit_to)
)
+ " "
)

View File

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

View File

@@ -46,5 +46,11 @@ frappe.query_reports["Asset Depreciations and Balances"] = {
options: "Asset",
depends_on: "eval: doc.group_by == 'Asset'",
},
{
fieldname: "finance_book",
label: __("Finance Book"),
fieldtype: "Link",
options: "Finance Book",
},
],
};

View File

@@ -69,6 +69,9 @@ def get_asset_categories_for_grouped_by_category(filters):
condition = ""
if filters.get("asset_category"):
condition += " and asset_category = %(asset_category)s"
if filters.get("finance_book"):
condition += " and exists (select 1 from `tabAsset Depreciation Schedule` ads where ads.asset = a.name and ads.finance_book = %(finance_book)s)"
# nosemgrep
return frappe.db.sql(
f"""
@@ -119,6 +122,7 @@ def get_asset_categories_for_grouped_by_category(filters):
"from_date": filters.from_date,
"company": filters.company,
"asset_category": filters.get("asset_category"),
"finance_book": filters.get("finance_book"),
},
as_dict=1,
)
@@ -128,6 +132,10 @@ def get_asset_details_for_grouped_by_category(filters):
condition = ""
if filters.get("asset"):
condition += " and name = %(asset)s"
if filters.get("finance_book"):
condition += " and exists (select 1 from `tabAsset Depreciation Schedule` ads where ads.asset = `tabAsset`.name and ads.finance_book = %(finance_book)s)"
# nosemgrep
return frappe.db.sql(
f"""
SELECT name,
@@ -176,6 +184,7 @@ def get_asset_details_for_grouped_by_category(filters):
"from_date": filters.from_date,
"company": filters.company,
"asset": filters.get("asset"),
"finance_book": filters.get("finance_book"),
},
as_dict=1,
)

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

@@ -154,8 +154,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

@@ -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

@@ -336,7 +336,7 @@ def get_tds_docs(filters):
def get_tds_docs_query(filters, bank_accounts, tds_accounts):
if not tds_accounts:
frappe.throw(
_("No {0} Accounts found for this company.").format(frappe.bold("Tax Withholding")),
_("No {0} Accounts found for this company.").format(frappe.bold(_("Tax Withholding"))),
title=_("Accounts Missing Error"),
)
gle = frappe.qb.DocType("GL Entry")

View File

@@ -670,6 +670,11 @@ frappe.ui.form.on("Asset", {
if (item.asset_location) {
frm.set_value("location", item.asset_location);
}
if (doctype === "Purchase Receipt") {
frm.set_value("purchase_receipt_item", item.name);
} else if (doctype === "Purchase Invoice") {
frm.set_value("purchase_invoice_item", item.name);
}
});
},

View File

@@ -33,14 +33,16 @@
"dimension_col_break",
"purchase_details_section",
"purchase_receipt",
"purchase_receipt_item",
"purchase_invoice",
"purchase_invoice_item",
"purchase_date",
"available_for_use_date",
"total_asset_cost",
"additional_asset_cost",
"column_break_23",
"gross_purchase_amount",
"asset_quantity",
"purchase_date",
"additional_asset_cost",
"total_asset_cost",
"section_break_23",
"calculate_depreciation",
"column_break_33",
@@ -536,6 +538,20 @@
"fieldname": "opening_number_of_booked_depreciations",
"fieldtype": "Int",
"label": "Opening Number of Booked Depreciations"
},
{
"fieldname": "purchase_receipt_item",
"fieldtype": "Link",
"hidden": 1,
"label": "Purchase Receipt Item",
"options": "Purchase Receipt Item"
},
{
"fieldname": "purchase_invoice_item",
"fieldtype": "Link",
"hidden": 1,
"label": "Purchase Invoice Item",
"options": "Purchase Invoice Item"
}
],
"idx": 72,
@@ -579,7 +595,7 @@
"link_fieldname": "target_asset"
}
],
"modified": "2024-08-01 16:39:09.340973",
"modified": "2024-08-26 23:28:29.095139",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset",

View File

@@ -94,7 +94,9 @@ class Asset(AccountsController):
purchase_amount: DF.Currency
purchase_date: DF.Date | None
purchase_invoice: DF.Link | None
purchase_invoice_item: DF.Link | None
purchase_receipt: DF.Link | None
purchase_receipt_item: DF.Link | None
split_from: DF.Link | None
status: DF.Literal[
"Draft",
@@ -669,7 +671,7 @@ class Asset(AccountsController):
if not fixed_asset_account:
frappe.throw(
_("Set {0} in asset category {1} for company {2}").format(
frappe.bold("Fixed Asset Account"),
frappe.bold(_("Fixed Asset Account")),
frappe.bold(self.asset_category),
frappe.bold(self.company),
),
@@ -691,12 +693,17 @@ class Asset(AccountsController):
return cwip_account
def make_gl_entries(self):
if self.check_asset_capitalization_gl_entries():
return
gl_entries = []
purchase_document = self.get_purchase_document()
fixed_asset_account, cwip_account = self.get_fixed_asset_account(), self.get_cwip_account()
if purchase_document and self.purchase_amount and getdate(self.available_for_use_date) <= getdate():
if (self.is_composite_asset or (purchase_document and self.purchase_amount)) and getdate(
self.available_for_use_date
) <= getdate():
gl_entries.append(
self.get_gl_dict(
{
@@ -733,6 +740,24 @@ class Asset(AccountsController):
make_gl_entries(gl_entries)
self.db_set("booked_fixed_asset", 1)
def check_asset_capitalization_gl_entries(self):
if self.is_composite_asset:
result = frappe.db.get_value(
"Asset Capitalization",
{"target_asset": self.name, "docstatus": 1},
["name", "target_fixed_asset_account"],
)
if result:
asset_capitalization, target_fixed_asset_account = result
# Check GL entries for the retrieved Asset Capitalization and target fixed asset account
return has_gl_entries(
"Asset Capitalization", asset_capitalization, target_fixed_asset_account
)
# return if there are no submitted capitalization for given asset
return True
return False
@frappe.whitelist()
def get_depreciation_rate(self, args, on_validate=False):
if isinstance(args, str):
@@ -779,6 +804,22 @@ class Asset(AccountsController):
return flt((100 * (1 - depreciation_rate)), float_precision)
def has_gl_entries(doctype, docname, target_account):
gl_entry = frappe.qb.DocType("GL Entry")
gl_entries = (
frappe.qb.from_(gl_entry)
.select(gl_entry.account)
.where(
(gl_entry.voucher_type == doctype)
& (gl_entry.voucher_no == docname)
& (gl_entry.debit != 0)
& (gl_entry.account == target_account)
)
.run(as_dict=True)
)
return len(gl_entries) > 0
def update_maintenance_status():
assets = frappe.get_all(
"Asset", filters={"docstatus": 1, "maintenance_required": 1, "disposal_date": ("is", "not set")}
@@ -904,7 +945,7 @@ def transfer_asset(args):
@frappe.whitelist()
def get_item_details(item_code, asset_category, gross_purchase_amount):
asset_category_doc = frappe.get_doc("Asset Category", asset_category)
asset_category_doc = frappe.get_cached_doc("Asset Category", asset_category)
books = []
for d in asset_category_doc.finance_books:
books.append(

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

@@ -317,7 +317,16 @@ class AssetCapitalization(StockController):
if not self.target_is_fixed_asset and not self.get("asset_items"):
frappe.throw(_("Consumed Asset Items is mandatory for Decapitalization"))
if not (self.get("stock_items") or self.get("asset_items") or self.get("service_items")):
if self.capitalization_method == "Create a new composite asset" and not (
self.get("stock_items") or self.get("asset_items")
):
frappe.throw(
_(
"Consumed Stock Items or Consumed Asset Items are mandatory for creating new composite asset"
)
)
elif not (self.get("stock_items") or self.get("asset_items") or self.get("service_items")):
frappe.throw(
_(
"Consumed Stock Items, Consumed Asset Items or Consumed Service Items is mandatory for Capitalization"
@@ -460,13 +469,24 @@ class AssetCapitalization(StockController):
self.get_gl_entries_for_consumed_asset_items(gl_entries, target_account, target_against, precision)
self.get_gl_entries_for_consumed_service_items(gl_entries, target_account, target_against, precision)
self.get_gl_entries_for_target_item(gl_entries, target_against, precision)
self.get_gl_entries_for_target_item(gl_entries, target_account, target_against, precision)
return gl_entries
def get_target_account(self):
if self.target_is_fixed_asset:
return self.target_fixed_asset_account
from erpnext.assets.doctype.asset.asset import is_cwip_accounting_enabled
asset_category = frappe.get_cached_value("Asset", self.target_asset, "asset_category")
if is_cwip_accounting_enabled(asset_category):
target_account = get_asset_category_account(
"capital_work_in_progress_account",
asset_category=asset_category,
company=self.company,
)
return target_account if target_account else self.target_fixed_asset_account
else:
return self.target_fixed_asset_account
else:
return self.warehouse_account[self.target_warehouse]["account"]
@@ -554,13 +574,13 @@ class AssetCapitalization(StockController):
)
)
def get_gl_entries_for_target_item(self, gl_entries, target_against, precision):
def get_gl_entries_for_target_item(self, gl_entries, target_account, target_against, precision):
if self.target_is_fixed_asset:
# Capitalization
gl_entries.append(
self.get_gl_dict(
{
"account": self.target_fixed_asset_account,
"account": target_account,
"against": ", ".join(target_against),
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
"debit": flt(self.total_value, precision),

View File

@@ -31,6 +31,12 @@ class TestAssetCapitalization(unittest.TestCase):
def test_capitalization_with_perpetual_inventory(self):
company = "_Test Company with perpetual inventory"
set_depreciation_settings_in_company(company=company)
name = frappe.db.get_value(
"Asset Category Account",
filters={"parent": "Computers", "company_name": company},
fieldname=["name"],
)
frappe.db.set_value("Asset Category Account", name, "capital_work_in_progress_account", "")
# Variables
consumed_asset_value = 100000
@@ -187,9 +193,10 @@ class TestAssetCapitalization(unittest.TestCase):
# Test General Ledger Entries
default_expense_account = frappe.db.get_value("Company", company, "default_expense_account")
expected_gle = {
"_Test Fixed Asset - _TC": 3000,
"Expenses Included In Asset Valuation - _TC": -1000,
default_expense_account: -2000,
"_Test Fixed Asset - _TC": -100000.0,
default_expense_account: -2000.0,
"CWIP Account - _TC": 103000.0,
"Expenses Included In Asset Valuation - _TC": -1000.0,
}
actual_gle = get_actual_gle_dict(asset_capitalization.name)
@@ -214,6 +221,12 @@ class TestAssetCapitalization(unittest.TestCase):
def test_capitalization_with_wip_composite_asset(self):
company = "_Test Company with perpetual inventory"
set_depreciation_settings_in_company(company=company)
name = frappe.db.get_value(
"Asset Category Account",
filters={"parent": "Computers", "company_name": company},
fieldname=["name"],
)
frappe.db.set_value("Asset Category Account", name, "capital_work_in_progress_account", "")
stock_rate = 1000
stock_qty = 2
@@ -424,7 +437,7 @@ class TestAssetCapitalization(unittest.TestCase):
self.assertEqual(target_asset.purchase_amount, total_amount)
expected_gle = {
"_Test Fixed Asset - _TC": 1000.0,
"CWIP Account - _TC": 1000.0,
"Expenses Included In Asset Valuation - _TC": -1000.0,
}

View File

@@ -767,8 +767,12 @@ def get_daily_depr_amount(asset, row, schedule_idx, amount):
every_year_depr = amount / total_years
depr_period_start_date = add_days(
get_last_day(add_months(row.depreciation_start_date, row.frequency_of_depreciation * -1)), 1
)
year_start_date = add_years(
row.depreciation_start_date, (row.frequency_of_depreciation * schedule_idx) // 12
depr_period_start_date, ((row.frequency_of_depreciation * schedule_idx) // 12)
)
year_end_date = add_days(add_years(year_start_date, 1), -1)

View File

@@ -97,7 +97,7 @@ class Supplier(TransactionBase):
elif supp_master_name == "Naming Series":
set_name_by_naming_series(self)
else:
self.name = set_name_from_naming_options(frappe.get_meta(self.doctype).autoname, self)
set_name_from_naming_options(frappe.get_meta(self.doctype).autoname, self)
def on_update(self):
self.create_primary_contact()

View File

@@ -0,0 +1,62 @@
// Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.query_reports["Item-wise Purchase History"] = {
filters: [
{
fieldname: "company",
label: __("Company"),
fieldtype: "Link",
options: "Company",
default: frappe.defaults.get_user_default("Company"),
reqd: 1,
},
{
fieldname: "from_date",
reqd: 1,
label: __("From Date"),
fieldtype: "Date",
default: frappe.datetime.add_months(frappe.datetime.get_today(), -1),
},
{
fieldname: "to_date",
reqd: 1,
default: frappe.datetime.get_today(),
label: __("To Date"),
fieldtype: "Date",
},
{
fieldname: "item_group",
label: __("Item Group"),
fieldtype: "Link",
options: "Item Group",
},
{
fieldname: "item_code",
label: __("Item"),
fieldtype: "Link",
options: "Item",
get_query: () => {
return {
query: "erpnext.controllers.queries.item_query",
};
},
},
{
fieldname: "supplier",
label: __("Supplier"),
fieldtype: "Link",
options: "Supplier",
},
],
formatter: function (value, row, column, data, default_formatter) {
value = default_formatter(value, row, column, data);
let format_fields = ["received_qty", "billed_amt"];
if (format_fields.includes(column.fieldname) && data && data[column.fieldname] > 0) {
value = "<span style='color:green;'>" + value + "</span>";
}
return value;
},
};

View File

@@ -1,30 +1,30 @@
{
"add_total_row": 1,
"apply_user_permissions": 1,
"creation": "2013-05-03 14:55:53",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 3,
"is_standard": "Yes",
"modified": "2017-02-24 20:08:57.446613",
"modified_by": "Administrator",
"module": "Buying",
"name": "Item-wise Purchase History",
"owner": "Administrator",
"query": "select\n po_item.item_code as \"Item Code:Link/Item:120\",\n\tpo_item.item_name as \"Item Name::120\",\n po_item.item_group as \"Item Group:Link/Item Group:120\",\n\tpo_item.description as \"Description::150\",\n\tpo_item.qty as \"Qty:Float:100\",\n\tpo_item.uom as \"UOM:Link/UOM:80\",\n\tpo_item.base_rate as \"Rate:Currency:120\",\n\tpo_item.base_amount as \"Amount:Currency:120\",\n\tpo.name as \"Purchase Order:Link/Purchase Order:120\",\n\tpo.transaction_date as \"Transaction Date:Date:140\",\n\tpo.supplier as \"Supplier:Link/Supplier:130\",\n sup.supplier_name as \"Supplier Name::150\",\n\tpo_item.project as \"Project:Link/Project:130\",\n\tifnull(po_item.received_qty, 0) as \"Received Qty:Float:120\",\n\tpo.company as \"Company:Link/Company:\"\nfrom\n\t`tabPurchase Order` po, `tabPurchase Order Item` po_item, `tabSupplier` sup\nwhere\n\tpo.name = po_item.parent and po.supplier = sup.name and po.docstatus = 1\norder by po.name desc",
"ref_doctype": "Purchase Order",
"report_name": "Item-wise Purchase History",
"report_type": "Query Report",
"add_total_row": 1,
"creation": "2013-05-03 14:55:53",
"disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 5,
"is_standard": "Yes",
"modified": "2024-06-19 12:12:15.418799",
"modified_by": "Administrator",
"module": "Buying",
"name": "Item-wise Purchase History",
"owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "Purchase Order",
"report_name": "Item-wise Purchase History",
"report_type": "Script Report",
"roles": [
{
"role": "Stock User"
},
},
{
"role": "Purchase Manager"
},
},
{
"role": "Purchase User"
}
]
]
}

View File

@@ -0,0 +1,276 @@
# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import frappe
from frappe import _
from frappe.utils import flt
from frappe.utils.nestedset import get_descendants_of
def execute(filters=None):
filters = frappe._dict(filters or {})
if filters.from_date > filters.to_date:
frappe.throw(_("From Date cannot be greater than To Date"))
columns = get_columns(filters)
data = get_data(filters)
chart_data = get_chart_data(data)
return columns, data, None, chart_data
def get_columns(filters):
return [
{
"label": _("Item Code"),
"fieldtype": "Link",
"fieldname": "item_code",
"options": "Item",
"width": 120,
},
{
"label": _("Item Name"),
"fieldtype": "Data",
"fieldname": "item_name",
"width": 140,
},
{
"label": _("Item Group"),
"fieldtype": "Link",
"fieldname": "item_group",
"options": "Item Group",
"width": 120,
},
{
"label": _("Description"),
"fieldtype": "Data",
"fieldname": "description",
"width": 140,
},
{
"label": _("Quantity"),
"fieldtype": "Float",
"fieldname": "quantity",
"width": 120,
},
{
"label": _("UOM"),
"fieldtype": "Link",
"fieldname": "uom",
"options": "UOM",
"width": 90,
},
{
"label": _("Rate"),
"fieldname": "rate",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
},
{
"label": _("Amount"),
"fieldname": "amount",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
},
{
"label": _("Purchase Order"),
"fieldtype": "Link",
"fieldname": "purchase_order",
"options": "Purchase Order",
"width": 160,
},
{
"label": _("Transaction Date"),
"fieldtype": "Date",
"fieldname": "transaction_date",
"width": 110,
},
{
"label": _("Supplier"),
"fieldtype": "Link",
"fieldname": "supplier",
"options": "Supplier",
"width": 100,
},
{
"label": _("Supplier Name"),
"fieldtype": "Data",
"fieldname": "supplier_name",
"width": 140,
},
{
"label": _("Supplier Group"),
"fieldtype": "Link",
"fieldname": "supplier_group",
"options": "Supplier Group",
"width": 120,
},
{
"label": _("Project"),
"fieldtype": "Link",
"fieldname": "project",
"options": "Project",
"width": 100,
},
{
"label": _("Received Quantity"),
"fieldtype": "Float",
"fieldname": "received_qty",
"width": 150,
},
{
"label": _("Billed Amount"),
"fieldtype": "Currency",
"fieldname": "billed_amt",
"options": "currency",
"width": 120,
},
{
"label": _("Company"),
"fieldtype": "Link",
"fieldname": "company",
"options": "Company",
"width": 100,
},
{
"label": _("Currency"),
"fieldtype": "Link",
"fieldname": "currency",
"options": "Currency",
"hidden": 1,
},
]
def get_data(filters):
data = []
company_list = get_descendants_of("Company", filters.get("company"))
company_list.append(filters.get("company"))
supplier_details = get_supplier_details()
item_details = get_item_details()
purchase_order_records = get_purchase_order_details(company_list, filters)
for record in purchase_order_records:
supplier_record = supplier_details.get(record.supplier)
item_record = item_details.get(record.item_code)
row = {
"item_code": record.get("item_code"),
"item_name": item_record.get("item_name"),
"item_group": item_record.get("item_group"),
"description": record.get("description"),
"quantity": record.get("qty"),
"uom": record.get("uom"),
"rate": record.get("base_rate"),
"amount": record.get("base_amount"),
"purchase_order": record.get("name"),
"transaction_date": record.get("transaction_date"),
"supplier": record.get("supplier"),
"supplier_name": supplier_record.get("supplier_name"),
"supplier_group": supplier_record.get("supplier_group"),
"project": record.get("project"),
"received_qty": flt(record.get("received_qty")),
"billed_amt": flt(record.get("billed_amt")),
"company": record.get("company"),
}
row["currency"] = frappe.get_cached_value("Company", row["company"], "default_currency")
data.append(row)
return data
def get_supplier_details():
details = frappe.get_all("Supplier", fields=["name", "supplier_name", "supplier_group"])
supplier_details = {}
for d in details:
supplier_details.setdefault(
d.name,
frappe._dict({"supplier_name": d.supplier_name, "supplier_group": d.supplier_group}),
)
return supplier_details
def get_item_details():
details = frappe.db.get_all("Item", fields=["name", "item_name", "item_group"])
item_details = {}
for d in details:
item_details.setdefault(d.name, frappe._dict({"item_name": d.item_name, "item_group": d.item_group}))
return item_details
def get_purchase_order_details(company_list, filters):
db_po = frappe.qb.DocType("Purchase Order")
db_po_item = frappe.qb.DocType("Purchase Order Item")
query = (
frappe.qb.from_(db_po)
.inner_join(db_po_item)
.on(db_po_item.parent == db_po.name)
.select(
db_po.name,
db_po.supplier,
db_po.transaction_date,
db_po.project,
db_po.company,
db_po_item.item_code,
db_po_item.description,
db_po_item.qty,
db_po_item.uom,
db_po_item.base_rate,
db_po_item.base_amount,
db_po_item.received_qty,
(db_po_item.billed_amt * db_po.conversion_rate).as_("billed_amt"),
)
.where(db_po.docstatus == 1)
.where(db_po.company.isin(tuple(company_list)))
)
for field in ("item_code", "item_group"):
if filters.get(field):
query = query.where(db_po_item[field] == filters[field])
if filters.get("from_date"):
query = query.where(db_po.transaction_date >= filters.from_date)
if filters.get("to_date"):
query = query.where(db_po.transaction_date <= filters.to_date)
if filters.get("supplier"):
query = query.where(db_po.supplier == filters.supplier)
return query.run(as_dict=1)
def get_chart_data(data):
item_wise_purchase_map = {}
labels, datapoints = [], []
for row in data:
item_key = row.get("item_code")
if item_key not in item_wise_purchase_map:
item_wise_purchase_map[item_key] = 0
item_wise_purchase_map[item_key] = flt(item_wise_purchase_map[item_key]) + flt(row.get("amount"))
item_wise_purchase_map = {
item: value
for item, value in (sorted(item_wise_purchase_map.items(), key=lambda i: i[1], reverse=True))
}
for key in item_wise_purchase_map:
labels.append(key)
datapoints.append(item_wise_purchase_map[key])
return {
"data": {
"labels": labels[:30], # show max of 30 items in chart
"datasets": [{"name": _("Total Purchase Amount"), "values": datapoints[:30]}],
},
"type": "bar",
"fieldtype": "Currency",
}

View File

@@ -40,6 +40,7 @@ def get_data(filters):
po = frappe.qb.DocType("Purchase Order")
po_item = frappe.qb.DocType("Purchase Order Item")
pi_item = frappe.qb.DocType("Purchase Invoice Item")
pr_item = frappe.qb.DocType("Purchase Receipt Item")
query = (
frappe.qb.from_(po)
@@ -47,6 +48,8 @@ def get_data(filters):
.on(po_item.parent == po.name)
.left_join(pi_item)
.on((pi_item.po_detail == po_item.name) & (pi_item.docstatus == 1))
.left_join(pr_item)
.on((pr_item.purchase_order_item == po_item.name) & (pr_item.docstatus == 1))
.select(
po.transaction_date.as_("date"),
po_item.schedule_date.as_("required_date"),
@@ -60,7 +63,7 @@ def get_data(filters):
(po_item.qty - po_item.received_qty).as_("pending_qty"),
Sum(IfNull(pi_item.qty, 0)).as_("billed_qty"),
po_item.base_amount.as_("amount"),
(po_item.received_qty * po_item.base_rate).as_("received_qty_amount"),
(pr_item.base_amount).as_("received_qty_amount"),
(po_item.billed_amt * IfNull(po.conversion_rate, 1)).as_("billed_amount"),
(po_item.base_amount - (po_item.billed_amt * IfNull(po.conversion_rate, 1))).as_(
"pending_amount"

View File

@@ -233,7 +233,7 @@ class AccountsController(TransactionBase):
).format(
frappe.bold(document_type),
get_link_to_form(self.doctype, self.get("return_against")),
frappe.bold("Update Outstanding for Self"),
frappe.bold(_("Update Outstanding for Self")),
get_link_to_form("Payment Reconciliation", "Payment Reconciliation"),
)
)
@@ -1334,6 +1334,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.
@@ -1962,7 +1968,9 @@ class AccountsController(TransactionBase):
def raise_missing_debit_credit_account_error(self, party_type, party):
"""Raise an error if debit to/credit to account does not exist."""
db_or_cr = frappe.bold("Debit To") if self.doctype == "Sales Invoice" else frappe.bold("Credit To")
db_or_cr = (
frappe.bold(_("Debit To")) if self.doctype == "Sales Invoice" else frappe.bold(_("Credit To"))
)
rec_or_pay = "Receivable" if self.doctype == "Sales Invoice" else "Payable"
link_to_party = frappe.utils.get_link_to_form(party_type, party)
@@ -3085,9 +3093,9 @@ def set_order_defaults(parent_doctype, parent_doctype_name, child_doctype, child
child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True)
if not child_item.warehouse:
frappe.throw(
_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.").format(
frappe.bold("default warehouse"), frappe.bold(item.item_code)
)
_(
"Cannot find a default warehouse for item {0}. Please set one in the Item Master or in Stock Settings."
).format(frappe.bold(item.item_code))
)
set_child_tax_template_and_map(item, child_item, p_doc)

View File

@@ -689,9 +689,11 @@ class BuyingController(SubcontractingController):
if self.doctype in ["Purchase Receipt", "Purchase Invoice"]:
self.process_fixed_asset()
if self.doctype in ["Purchase Order", "Purchase Receipt"] and not frappe.db.get_single_value(
"Buying Settings", "disable_last_purchase_rate"
):
if self.doctype in [
"Purchase Order",
"Purchase Receipt",
"Purchase Invoice",
] and not frappe.db.get_single_value("Buying Settings", "disable_last_purchase_rate"):
update_last_purchase_rate(self, is_submit=1)
def on_cancel(self):
@@ -820,6 +822,8 @@ class BuyingController(SubcontractingController):
"asset_quantity": asset_quantity,
"purchase_receipt": self.name if self.doctype == "Purchase Receipt" else None,
"purchase_invoice": self.name if self.doctype == "Purchase Invoice" else None,
"purchase_receipt_item": row.name if self.doctype == "Purchase Receipt" else None,
"purchase_invoice_item": row.name if self.doctype == "Purchase Invoice" else None,
}
)

View File

@@ -150,7 +150,7 @@ def validate_item_attribute_value(attributes_list, attribute, attribute_value, i
)
msg += "<br>" + _(
"To still proceed with editing this Attribute Value, enable {0} in Item Variant Settings."
).format(frappe.bold("Allow Rename Attribute Value"))
).format(frappe.bold(_("Allow Rename Attribute Value")))
frappe.throw(msg, InvalidItemAttributeValueError, title=_("Edit Not Allowed"))

View File

@@ -366,7 +366,7 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters):
def get_empty_batches(filters, start, page_len, filtered_batches=None, txt=None):
query_filter = {"item": filters.get("item_code")}
query_filter = {"item": filters.get("item_code"), "disabled": 0}
if txt:
query_filter["name"] = ("like", f"%{txt}%")

View File

@@ -697,7 +697,7 @@ class SellingController(StockController):
duplicate_items_msg = _("Item {0} entered multiple times.").format(frappe.bold(d.item_code))
duplicate_items_msg += "<br><br>"
duplicate_items_msg += _("Please enable {} in {} to allow same item in multiple rows").format(
frappe.bold("Allow Item to Be Added Multiple Times in a Transaction"),
frappe.bold(_("Allow Item to Be Added Multiple Times in a Transaction")),
get_link_to_form("Selling Settings", "Selling Settings"),
)
if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1:

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,6 +27,29 @@ class TestProspect(unittest.TestCase):
address_doc.reload()
self.assertEqual(address_doc.has_link("Prospect", prospect_doc.name), True)
def test_make_customer_from_prospect(self):
from erpnext.crm.doctype.prospect.prospect import make_customer as make_customer_from_prospect
frappe.delete_doc_if_exists("Customer", "_Test Prospect")
prospect = frappe.get_doc(
{
"doctype": "Prospect",
"company_name": "_Test Prospect",
"customer_group": "_Test Customer Group",
}
)
prospect.insert()
customer = make_customer_from_prospect("_Test Prospect")
self.assertEqual(customer.doctype, "Customer")
self.assertEqual(customer.company_name, "_Test Prospect")
self.assertEqual(customer.customer_group, "_Test Customer Group")
customer.company = "_Test Company"
customer.insert()
def make_prospect(**args):
args = frappe._dict(args)

View File

@@ -96,7 +96,7 @@ def add_bank_accounts(response, bank, company):
frappe.throw(
_(
"Please setup and enable a group account with the Account Type - {0} for the company {1}"
).format(frappe.bold("Bank"), company)
).format(frappe.bold(_("Bank")), company)
)
for account in response["accounts"]:

View File

@@ -10,7 +10,17 @@ source_link = "https://github.com/frappe/erpnext"
app_logo_url = "/assets/erpnext/images/erpnext-logo.svg"
develop_version = "14.x.x-develop"
add_to_apps_screen = [
{
"name": "erpnext",
"logo": "/assets/erpnext/images/erpnext-logo-blue.png",
"title": "ERPNext",
"route": "/app/home",
"has_permission": "erpnext.check_app_permission",
}
]
develop_version = "15.x.x-develop"
app_include_js = "erpnext.bundle.js"
app_include_css = "erpnext.bundle.css"

View File

@@ -5,18 +5,17 @@
"document_type": "Document",
"engine": "InnoDB",
"field_order": [
"bom_and_work_order_tab",
"raw_materials_consumption_section",
"material_consumption",
"get_rm_cost_from_consumption_entry",
"column_break_3",
"backflush_raw_materials_based_on",
"capacity_planning",
"disable_capacity_planning",
"allow_overtime",
"allow_production_on_holidays",
"column_break_5",
"capacity_planning_for_days",
"mins_between_operations",
"validate_components_quantities_per_bom",
"bom_section",
"update_bom_costs_automatically",
"column_break_lhyt",
"manufacture_sub_assembly_in_operation",
"section_break_6",
"default_wip_warehouse",
"default_fg_warehouse",
@@ -30,8 +29,14 @@
"add_corrective_operation_cost_in_finished_good_valuation",
"column_break_24",
"job_card_excess_transfer",
"capacity_planning",
"disable_capacity_planning",
"allow_overtime",
"allow_production_on_holidays",
"column_break_5",
"capacity_planning_for_days",
"mins_between_operations",
"other_settings_section",
"update_bom_costs_automatically",
"set_op_cost_and_scrape_from_sub_assemblies",
"column_break_23",
"make_serial_no_batch_from_work_order"
@@ -149,7 +154,7 @@
{
"fieldname": "raw_materials_consumption_section",
"fieldtype": "Section Break",
"label": "Raw Materials Consumption"
"label": "Raw Materials Consumption "
},
{
"fieldname": "column_break_16",
@@ -183,8 +188,8 @@
},
{
"fieldname": "job_card_section",
"fieldtype": "Section Break",
"label": "Job Card"
"fieldtype": "Tab Break",
"label": "Job Card and Capacity Planning"
},
{
"fieldname": "column_break_24",
@@ -210,13 +215,41 @@
"fieldname": "get_rm_cost_from_consumption_entry",
"fieldtype": "Check",
"label": "Get Raw Materials Cost from Consumption Entry"
},
{
"fieldname": "bom_and_work_order_tab",
"fieldtype": "Tab Break",
"label": "BOM and Production"
},
{
"fieldname": "bom_section",
"fieldtype": "Section Break",
"label": "BOM"
},
{
"fieldname": "column_break_lhyt",
"fieldtype": "Column Break"
},
{
"default": "0",
"description": "If enabled then system will manufacture Sub-assembly against the Job Card (operation).",
"fieldname": "manufacture_sub_assembly_in_operation",
"fieldtype": "Check",
"label": "Manufacture Sub-assembly in Operation"
},
{
"default": "0",
"depends_on": "eval:doc.backflush_raw_materials_based_on == \"BOM\"",
"fieldname": "validate_components_quantities_per_bom",
"fieldtype": "Check",
"label": "Validate Components Quantities Per BOM"
}
],
"icon": "icon-wrench",
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2024-02-08 19:00:37.561244",
"modified": "2024-09-02 12:12:03.132567",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Manufacturing Settings",
@@ -234,4 +267,4 @@
"sort_order": "DESC",
"states": [],
"track_changes": 1
}
}

View File

@@ -29,15 +29,22 @@ class ManufacturingSettings(Document):
get_rm_cost_from_consumption_entry: DF.Check
job_card_excess_transfer: DF.Check
make_serial_no_batch_from_work_order: DF.Check
manufacture_sub_assembly_in_operation: DF.Check
material_consumption: DF.Check
mins_between_operations: DF.Int
overproduction_percentage_for_sales_order: DF.Percent
overproduction_percentage_for_work_order: DF.Percent
set_op_cost_and_scrape_from_sub_assemblies: DF.Check
update_bom_costs_automatically: DF.Check
validate_components_quantities_per_bom: DF.Check
# end: auto-generated types
pass
def before_save(self):
self.reset_values()
def reset_values(self):
if self.backflush_raw_materials_based_on != "BOM" and self.validate_components_quantities_per_bom:
self.validate_components_quantities_per_bom = 0
def get_mins_between_operations():

View File

@@ -2053,6 +2053,108 @@ class TestWorkOrder(FrappeTestCase):
"BOM",
)
def test_disassemby_order(self):
fg_item = "Test Disassembly Item"
source_warehouse = "Stores - _TC"
raw_materials = ["Test Disassembly RM Item 1", "Test Disassembly RM Item 2"]
make_item(fg_item, {"is_stock_item": 1})
for item in raw_materials:
make_item(item, {"is_stock_item": 1})
test_stock_entry.make_stock_entry(
item_code=item,
target=source_warehouse,
qty=1,
basic_rate=100,
)
make_bom(item=fg_item, source_warehouse=source_warehouse, raw_materials=raw_materials)
wo = make_wo_order_test_record(
item=fg_item,
qty=1,
source_warehouse=source_warehouse,
skip_transfer=1,
)
stock_entry = frappe.get_doc(make_stock_entry(wo.name, "Manufacture", 1))
for row in stock_entry.items:
if row.item_code in raw_materials:
row.s_warehouse = source_warehouse
stock_entry.submit()
wo.reload()
self.assertEqual(wo.status, "Completed")
stock_entry = frappe.get_doc(make_stock_entry(wo.name, "Disassemble", 1))
stock_entry.save()
self.assertEqual(stock_entry.purpose, "Disassemble")
for row in stock_entry.items:
if row.item_code == fg_item:
self.assertTrue(row.s_warehouse)
self.assertFalse(row.t_warehouse)
else:
self.assertFalse(row.s_warehouse)
self.assertTrue(row.t_warehouse)
stock_entry.submit()
def test_components_qty_for_bom_based_manufacture_entry(self):
frappe.db.set_single_value("Manufacturing Settings", "backflush_raw_materials_based_on", "BOM")
frappe.db.set_single_value("Manufacturing Settings", "validate_components_quantities_per_bom", 1)
fg_item = "Test FG Item For Component Validation"
source_warehouse = "Stores - _TC"
raw_materials = ["Test Component Validation RM Item 1", "Test Component Validation RM Item 2"]
make_item(fg_item, {"is_stock_item": 1})
for item in raw_materials:
make_item(item, {"is_stock_item": 1})
test_stock_entry.make_stock_entry(
item_code=item,
target=source_warehouse,
qty=10,
basic_rate=100,
)
make_bom(item=fg_item, source_warehouse=source_warehouse, raw_materials=raw_materials)
wo = make_wo_order_test_record(
item=fg_item,
qty=10,
source_warehouse=source_warehouse,
)
transfer_entry = frappe.get_doc(make_stock_entry(wo.name, "Material Transfer for Manufacture", 10))
transfer_entry.save()
for row in transfer_entry.items:
row.qty = 5
self.assertRaises(frappe.ValidationError, transfer_entry.save)
transfer_entry.reload()
for row in transfer_entry.items:
self.assertEqual(row.qty, 10)
transfer_entry.submit()
manufacture_entry = frappe.get_doc(make_stock_entry(wo.name, "Manufacture", 10))
manufacture_entry.save()
for row in manufacture_entry.items:
if not row.s_warehouse:
continue
row.qty = 5
self.assertRaises(frappe.ValidationError, manufacture_entry.save)
manufacture_entry.reload()
manufacture_entry.submit()
frappe.db.set_single_value("Manufacturing Settings", "validate_components_quantities_per_bom", 0)
def make_operation(**kwargs):
kwargs = frappe._dict(kwargs)
@@ -2370,6 +2472,7 @@ def make_wo_order_test_record(**args):
wo_order.batch_size = args.batch_size or 0
if args.source_warehouse:
wo_order.source_warehouse = args.source_warehouse
for item in wo_order.get("required_items"):
item.source_warehouse = args.source_warehouse

View File

@@ -183,13 +183,30 @@ frappe.ui.form.on("Work Order", {
}
}
if (frm.doc.status == "Completed") {
if (frm.doc.__onload.backflush_raw_materials_based_on == "Material Transferred for Manufacture") {
frm.add_custom_button(
__("BOM"),
() => {
frm.trigger("make_bom");
},
__("Create")
);
}
}
if (
frm.doc.status == "Completed" &&
frm.doc.__onload.backflush_raw_materials_based_on == "Material Transferred for Manufacture"
frm.doc.docstatus === 1 &&
["Closed", "Completed"].includes(frm.doc.status) &&
frm.doc.produced_qty > 0
) {
frm.add_custom_button(__("Create BOM"), () => {
frm.trigger("make_bom");
});
frm.add_custom_button(
__("Disassembly Order"),
() => {
frm.trigger("make_disassembly_order");
},
__("Create")
);
}
frm.trigger("add_custom_button_to_return_components");
@@ -337,6 +354,23 @@ frappe.ui.form.on("Work Order", {
});
},
make_disassembly_order(frm) {
erpnext.work_order
.show_prompt_for_qty_input(frm, "Disassemble")
.then((data) => {
return frappe.xcall("erpnext.manufacturing.doctype.work_order.work_order.make_stock_entry", {
work_order_id: frm.doc.name,
purpose: "Disassemble",
qty: data.qty,
target_warehouse: data.target_warehouse,
});
})
.then((stock_entry) => {
frappe.model.sync(stock_entry);
frappe.set_route("Form", stock_entry.doctype, stock_entry.name);
});
},
show_progress_for_items: function (frm) {
var bars = [];
var message = "";
@@ -745,6 +779,10 @@ erpnext.work_order = {
get_max_transferable_qty: (frm, purpose) => {
let max = 0;
if (purpose === "Disassemble") {
return flt(frm.doc.produced_qty);
}
if (frm.doc.skip_transfer) {
max = flt(frm.doc.qty) - flt(frm.doc.produced_qty);
} else {
@@ -759,15 +797,38 @@ erpnext.work_order = {
show_prompt_for_qty_input: function (frm, purpose) {
let max = this.get_max_transferable_qty(frm, purpose);
let fields = [
{
fieldtype: "Float",
label: __("Qty for {0}", [__(purpose)]),
fieldname: "qty",
description: __("Max: {0}", [max]),
default: max,
},
];
if (purpose === "Disassemble") {
fields.push({
fieldtype: "Link",
options: "Warehouse",
fieldname: "target_warehouse",
label: __("Target Warehouse"),
default: frm.doc.source_warehouse || frm.doc.wip_warehouse,
get_query() {
return {
filters: {
company: frm.doc.company,
is_group: 0,
},
};
},
});
}
return new Promise((resolve, reject) => {
frappe.prompt(
{
fieldtype: "Float",
label: __("Qty for {0}", [__(purpose)]),
fieldname: "qty",
description: __("Max: {0}", [max]),
default: max,
},
fields,
(data) => {
max += (frm.doc.qty * (frm.doc.__onload.overproduction_percentage || 0.0)) / 100;

View File

@@ -1359,7 +1359,7 @@ def set_work_order_ops(name):
@frappe.whitelist()
def make_stock_entry(work_order_id, purpose, qty=None):
def make_stock_entry(work_order_id, purpose, qty=None, target_warehouse=None):
work_order = frappe.get_doc("Work Order", work_order_id)
if not frappe.db.get_value("Warehouse", work_order.wip_warehouse, "is_group"):
wip_warehouse = work_order.wip_warehouse
@@ -1389,9 +1389,16 @@ def make_stock_entry(work_order_id, purpose, qty=None):
stock_entry.to_warehouse = work_order.fg_warehouse
stock_entry.project = work_order.project
if purpose == "Disassemble":
stock_entry.from_warehouse = work_order.fg_warehouse
stock_entry.to_warehouse = target_warehouse or work_order.source_warehouse
stock_entry.set_stock_entry_type()
stock_entry.get_items()
stock_entry.set_serial_no_batch_for_finished_good()
if purpose != "Disassemble":
stock_entry.set_serial_no_batch_for_finished_good()
return stock_entry.as_dict()

View File

@@ -371,3 +371,7 @@ erpnext.patches.v15_0.update_warehouse_field_in_asset_repair_consumed_item_docty
erpnext.patches.v15_0.update_asset_repair_field_in_stock_entry
erpnext.patches.v15_0.update_total_number_of_booked_depreciations
erpnext.patches.v15_0.do_not_use_batchwise_valuation
erpnext.patches.v15_0.drop_index_posting_datetime_from_sle
erpnext.patches.v15_0.add_disassembly_order_stock_entry_type #1
erpnext.patches.v15_0.set_standard_stock_entry_type
erpnext.patches.v15_0.link_purchase_item_to_asset_doc

View File

@@ -0,0 +1,13 @@
import frappe
def execute():
if not frappe.db.exists("Stock Entry Type", "Disassemble"):
frappe.get_doc(
{
"doctype": "Stock Entry Type",
"name": "Disassemble",
"purpose": "Disassemble",
"is_standard": 1,
}
).insert(ignore_permissions=True)

View File

@@ -0,0 +1,16 @@
import click
import frappe
def execute():
table = "tabStock Ledger Entry"
index = "posting_datetime_creation_index"
if not frappe.db.has_index(table, index):
return
try:
frappe.db.sql_ddl(f"ALTER TABLE `{table}` DROP INDEX `{index}`")
click.echo(f"✓ dropped {index} index from {table}")
except Exception:
frappe.log_error("Failed to drop index")

View File

@@ -0,0 +1,74 @@
import frappe
def execute():
if frappe.db.has_column("Asset", "purchase_invoice_item") and frappe.db.has_column(
"Asset", "purchase_receipt_item"
):
# Get all assets with their related Purchase Invoice and Purchase Receipt
assets = frappe.get_all(
"Asset",
filters={"docstatus": 0},
fields=[
"name",
"item_code",
"purchase_invoice",
"purchase_receipt",
"gross_purchase_amount",
"asset_quantity",
"purchase_invoice_item",
"purchase_receipt_item",
],
)
for asset in assets:
# Get Purchase Invoice Items
if asset.purchase_invoice and not asset.purchase_invoice_item:
purchase_invoice_item = get_linked_item(
"Purchase Invoice Item",
asset.purchase_invoice,
asset.item_code,
asset.gross_purchase_amount,
asset.asset_quantity,
)
frappe.db.set_value("Asset", asset.name, "purchase_invoice_item", purchase_invoice_item)
# Get Purchase Receipt Items
if asset.purchase_receipt and not asset.purchase_receipt_item:
purchase_receipt_item = get_linked_item(
"Purchase Receipt Item",
asset.purchase_receipt,
asset.item_code,
asset.gross_purchase_amount,
asset.asset_quantity,
)
frappe.db.set_value("Asset", asset.name, "purchase_receipt_item", purchase_receipt_item)
def get_linked_item(doctype, parent, item_code, amount, quantity):
items = frappe.get_all(
doctype,
filters={
"parenttype": doctype.replace(" Item", ""),
"parent": parent,
"item_code": item_code,
},
fields=["name", "rate", "amount", "qty", "landed_cost_voucher_amount"],
)
if len(items) == 1:
# If only one item exists, return it directly
return items[0].name
for item in items:
landed_cost = item.get("landed_cost_voucher_amount", 0)
# Check if the asset is grouped
if quantity > 1:
if item.amount + landed_cost == amount and item.qty == quantity:
return item.name
elif item.qty == quantity:
return item.name
else:
if item.rate + (landed_cost / item.qty) == amount:
return item.name
return items[0].name if items else None

View File

@@ -0,0 +1,17 @@
import frappe
def execute():
for stock_entry_type in [
"Material Issue",
"Material Receipt",
"Material Transfer",
"Material Transfer for Manufacture",
"Material Consumption for Manufacture",
"Manufacture",
"Repack",
"Send to Subcontractor",
"Disassemble",
]:
if frappe.db.exists("Stock Entry Type", stock_entry_type):
frappe.db.set_value("Stock Entry Type", stock_entry_type, "is_standard", 1)

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@@ -374,6 +374,14 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager {
label: "Cost Center",
options: "Cost Center",
depends_on: "eval:doc.action=='Create Voucher' && doc.document_type=='Payment Entry'",
get_query: () => {
return {
filters: {
is_group: 0,
company: this.company,
},
};
},
},
{
fieldtype: "Section Break",

View File

@@ -1421,12 +1421,13 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
let show = cint(this.frm.doc.discount_amount) ||
((this.frm.doc.taxes || []).filter(function(d) {return d.included_in_print_rate===1}).length);
if(frappe.meta.get_docfield(cur_frm.doctype, "net_total"))
if(this.frm.doc.doctype && frappe.meta.get_docfield(this.frm.doc.doctype, "net_total")) {
this.frm.toggle_display("net_total", show);
}
if(frappe.meta.get_docfield(cur_frm.doctype, "base_net_total"))
if(this.frm.doc.doctype && frappe.meta.get_docfield(this.frm.doc.doctype, "base_net_total")) {
this.frm.toggle_display("base_net_total", (show && (me.frm.doc.currency != company_currency)));
}
}
change_grid_labels(company_currency) {

View File

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

View File

@@ -368,8 +368,28 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate {
];
}
get_batch_qty(batch_no, callback) {
let warehouse = this.item.s_warehouse || this.item.t_warehouse || this.item.warehouse;
frappe.call({
method: "erpnext.stock.doctype.batch.batch.get_batch_qty",
args: {
batch_no: batch_no,
warehouse: warehouse,
item_code: this.item.item_code,
posting_date: this.frm.doc.posting_date,
posting_time: this.frm.doc.posting_time,
},
callback: (r) => {
if (r.message) {
callback(flt(r.message));
}
},
});
}
get_dialog_table_fields() {
let fields = [];
let me = this;
if (this.item.has_serial_no) {
fields.push({
@@ -395,6 +415,15 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate {
fieldname: "batch_no",
label: __("Batch No"),
in_list_view: 1,
change() {
let doc = this.doc;
if (!doc.qty && me.item.type_of_transaction === "Outward") {
me.get_batch_qty(doc.batch_no, (qty) => {
doc.qty = qty;
this.grid.set_value("qty", qty, doc);
});
}
},
get_query: () => {
let is_inward = false;
if (

View File

@@ -21,6 +21,7 @@
"gender",
"lead_name",
"opportunity_name",
"prospect_name",
"account_manager",
"image",
"defaults_tab",
@@ -307,13 +308,15 @@
"fetch_from": "customer_primary_contact.mobile_no",
"fieldname": "mobile_no",
"fieldtype": "Read Only",
"label": "Mobile No"
"label": "Mobile No",
"options": "Mobile"
},
{
"fetch_from": "customer_primary_contact.email_id",
"fieldname": "email_id",
"fieldtype": "Read Only",
"label": "Email Id"
"label": "Email Id",
"options": "Email"
},
{
"fieldname": "column_break_26",
@@ -570,6 +573,14 @@
{
"fieldname": "column_break_nwor",
"fieldtype": "Column Break"
},
{
"fieldname": "prospect_name",
"fieldtype": "Link",
"label": "From Prospect",
"no_copy": 1,
"options": "Prospect",
"print_hide": 1
}
],
"icon": "fa fa-user",
@@ -583,7 +594,7 @@
"link_fieldname": "party"
}
],
"modified": "2024-05-08 18:03:20.716169",
"modified": "2024-06-17 03:24:59.612974",
"modified_by": "Administrator",
"module": "Selling",
"name": "Customer",
@@ -668,4 +679,4 @@
"states": [],
"title_field": "customer_name",
"track_changes": 1
}
}

View File

@@ -77,6 +77,7 @@ class Customer(TransactionBase):
payment_terms: DF.Link | None
portal_users: DF.Table[PortalUser]
primary_address: DF.Text | None
prospect_name: DF.Link | None
represents_company: DF.Link | None
sales_team: DF.Table[SalesTeam]
salutation: DF.Link | None
@@ -104,7 +105,7 @@ class Customer(TransactionBase):
elif cust_master_name == "Naming Series":
set_name_by_naming_series(self)
else:
self.name = set_name_from_naming_options(frappe.get_meta(self.doctype).autoname, self)
set_name_from_naming_options(frappe.get_meta(self.doctype).autoname, self)
def get_customer_name(self):
if frappe.db.get_value("Customer", self.customer_name) and not frappe.flags.in_import:

View File

@@ -347,8 +347,8 @@ def make_sales_order(source_name: str, target_doc=None):
return _make_sales_order(source_name, target_doc)
def _make_sales_order(source_name, target_doc=None, customer_group=None, ignore_permissions=False):
customer = _make_customer(source_name, ignore_permissions, customer_group)
def _make_sales_order(source_name, target_doc=None, ignore_permissions=False):
customer = _make_customer(source_name, ignore_permissions)
ordered_items = frappe._dict(
frappe.db.get_all(
"Sales Order Item",
@@ -502,51 +502,71 @@ def _make_sales_invoice(source_name, target_doc=None, ignore_permissions=False):
return doclist
def _make_customer(source_name, ignore_permissions=False, customer_group=None):
def _make_customer(source_name, ignore_permissions=False):
quotation = frappe.db.get_value(
"Quotation", source_name, ["order_type", "party_name", "customer_name"], as_dict=1
"Quotation",
source_name,
["order_type", "quotation_to", "party_name", "customer_name"],
as_dict=1,
)
if quotation and quotation.get("party_name"):
if not frappe.db.exists("Customer", quotation.get("party_name")):
lead_name = quotation.get("party_name")
customer_name = frappe.db.get_value(
"Customer", {"lead_name": lead_name}, ["name", "customer_name"], as_dict=True
)
if not customer_name:
from erpnext.crm.doctype.lead.lead import _make_customer
if quotation.quotation_to == "Customer":
return frappe.get_doc("Customer", quotation.party_name)
customer_doclist = _make_customer(lead_name, ignore_permissions=ignore_permissions)
customer = frappe.get_doc(customer_doclist)
customer.flags.ignore_permissions = ignore_permissions
customer.customer_group = customer_group
# Check if a Customer already exists for the Lead or Prospect.
existing_customer = None
if quotation.quotation_to == "Lead":
existing_customer = frappe.db.get_value("Customer", {"lead_name": quotation.party_name})
elif quotation.quotation_to == "Prospect":
existing_customer = frappe.db.get_value("Customer", {"prospect_name": quotation.party_name})
try:
customer.insert()
return customer
except frappe.NameError:
if frappe.defaults.get_global_default("cust_master_name") == "Customer Name":
customer.run_method("autoname")
customer.name += "-" + lead_name
customer.insert()
return customer
else:
raise
except frappe.MandatoryError as e:
mandatory_fields = e.args[0].split(":")[1].split(",")
mandatory_fields = [customer.meta.get_label(field.strip()) for field in mandatory_fields]
if existing_customer:
return frappe.get_doc("Customer", existing_customer)
frappe.local.message_log = []
lead_link = frappe.utils.get_link_to_form("Lead", lead_name)
message = (
_("Could not auto create Customer due to the following missing mandatory field(s):")
+ "<br>"
)
message += "<br><ul><li>" + "</li><li>".join(mandatory_fields) + "</li></ul>"
message += _("Please create Customer from Lead {0}.").format(lead_link)
# If no Customer exists, create a new Customer or Prospect.
if quotation.quotation_to == "Lead":
return create_customer_from_lead(quotation.party_name, ignore_permissions=ignore_permissions)
elif quotation.quotation_to == "Prospect":
return create_customer_from_prospect(quotation.party_name, ignore_permissions=ignore_permissions)
frappe.throw(message, title=_("Mandatory Missing"))
else:
return customer_name
else:
return frappe.get_doc("Customer", quotation.get("party_name"))
return None
def create_customer_from_lead(lead_name, ignore_permissions=False):
from erpnext.crm.doctype.lead.lead import _make_customer
customer = _make_customer(lead_name, ignore_permissions=ignore_permissions)
customer.flags.ignore_permissions = ignore_permissions
try:
customer.insert()
return customer
except frappe.MandatoryError as e:
handle_mandatory_error(e, customer, lead_name)
def create_customer_from_prospect(prospect_name, ignore_permissions=False):
from erpnext.crm.doctype.prospect.prospect import make_customer as make_customer_from_prospect
customer = make_customer_from_prospect(prospect_name)
customer.flags.ignore_permissions = ignore_permissions
try:
customer.insert()
return customer
except frappe.MandatoryError as e:
handle_mandatory_error(e, customer, prospect_name)
def handle_mandatory_error(e, customer, lead_name):
from frappe.utils import get_link_to_form
mandatory_fields = e.args[0].split(":")[1].split(",")
mandatory_fields = [customer.meta.get_label(field.strip()) for field in mandatory_fields]
frappe.local.message_log = []
message = _("Could not auto create Customer due to the following missing mandatory field(s):") + "<br>"
message += "<br><ul><li>" + "</li><li>".join(mandatory_fields) + "</li></ul>"
message += _("Please create Customer from Lead {0}.").format(get_link_to_form("Lead", lead_name))
frappe.throw(message, title=_("Mandatory Missing"))

View File

@@ -936,6 +936,10 @@ def make_delivery_note(source_name, target_doc=None, kwargs=None):
}
def set_missing_values(source, target):
if kwargs.get("ignore_pricing_rule"):
# Skip pricing rule when the dn is creating from the pick list
target.ignore_pricing_rule = 1
target.run_method("set_missing_values")
target.run_method("set_po_nos")
target.run_method("calculate_taxes_and_totals")

View File

@@ -3,22 +3,14 @@
frappe.ui.form.on("Brand", {
setup: (frm) => {
frm.fields_dict["brand_defaults"].grid.get_field("default_warehouse").get_query = function (
doc,
cdt,
cdn
) {
frm.set_query("default_warehouse", "brand_defaults", function (doc, cdt, cdn) {
const row = locals[cdt][cdn];
return {
filters: { company: row.company },
};
};
});
frm.fields_dict["brand_defaults"].grid.get_field("default_discount_account").get_query = function (
doc,
cdt,
cdn
) {
frm.set_query("default_discount_account", "brand_defaults", function (doc, cdt, cdn) {
const row = locals[cdt][cdn];
return {
filters: {
@@ -27,13 +19,9 @@ frappe.ui.form.on("Brand", {
is_group: 0,
},
};
};
});
frm.fields_dict["brand_defaults"].grid.get_field("buying_cost_center").get_query = function (
doc,
cdt,
cdn
) {
frm.set_query("buying_cost_center", "brand_defaults", function (doc, cdt, cdn) {
const row = locals[cdt][cdn];
return {
filters: {
@@ -41,25 +29,17 @@ frappe.ui.form.on("Brand", {
company: row.company,
},
};
};
});
frm.fields_dict["brand_defaults"].grid.get_field("expense_account").get_query = function (
doc,
cdt,
cdn
) {
frm.set_query("expense_account", "brand_defaults", function (doc, cdt, cdn) {
const row = locals[cdt][cdn];
return {
query: "erpnext.controllers.queries.get_expense_account",
filters: { company: row.company },
};
};
});
frm.fields_dict["brand_defaults"].grid.get_field("default_provisional_account").get_query = function (
doc,
cdt,
cdn
) {
frm.set_query("default_provisional_account", "brand_defaults", function (doc, cdt, cdn) {
const row = locals[cdt][cdn];
return {
filters: {
@@ -68,13 +48,9 @@ frappe.ui.form.on("Brand", {
is_group: 0,
},
};
};
});
frm.fields_dict["brand_defaults"].grid.get_field("selling_cost_center").get_query = function (
doc,
cdt,
cdn
) {
frm.set_query("selling_cost_center", "brand_defaults", function (doc, cdt, cdn) {
const row = locals[cdt][cdn];
return {
filters: {
@@ -82,18 +58,14 @@ frappe.ui.form.on("Brand", {
company: row.company,
},
};
};
});
frm.fields_dict["brand_defaults"].grid.get_field("income_account").get_query = function (
doc,
cdt,
cdn
) {
frm.set_query("income_account", "brand_defaults", function (doc, cdt, cdn) {
const row = locals[cdt][cdn];
return {
query: "erpnext.controllers.queries.get_income_account",
filters: { company: row.company },
};
};
});
},
});

View File

@@ -56,10 +56,11 @@
"idx": 1,
"image_field": "image",
"links": [],
"modified": "2021-03-01 15:57:30.005783",
"modified": "2024-08-20 14:10:21.377962",
"modified_by": "Administrator",
"module": "Setup",
"name": "Brand",
"naming_rule": "By fieldname",
"owner": "Administrator",
"permissions": [
{
@@ -108,4 +109,4 @@
"show_name_in_global_search": 1,
"sort_field": "modified",
"sort_order": "ASC"
}
}

View File

@@ -20,7 +20,6 @@ frappe.ui.form.on("Company", {
},
setup: function (frm) {
frm.__rename_queue = "long";
erpnext.company.setup_queries(frm);
frm.set_query("parent_company", function () {
return {
@@ -81,6 +80,8 @@ frappe.ui.form.on("Company", {
},
refresh: function (frm) {
erpnext.company.setup_queries(frm);
frm.toggle_display("address_html", !frm.is_new());
if (!frm.is_new()) {

View File

@@ -203,7 +203,7 @@ class Company(NestedSet):
):
frappe.throw(
_("'{0}' should be in company currency {1}.").format(
frappe.bold("Default Advance Received Account"), frappe.bold(self.default_currency)
frappe.bold(_("Default Advance Received Account")), frappe.bold(self.default_currency)
)
)
@@ -214,7 +214,7 @@ class Company(NestedSet):
):
frappe.throw(
_("'{0}' should be in company currency {1}.").format(
frappe.bold("Default Advance Paid Account"), frappe.bold(self.default_currency)
frappe.bold(_("Default Advance Paid Account")), frappe.bold(self.default_currency)
)
)
@@ -447,7 +447,7 @@ class Company(NestedSet):
):
frappe.throw(
_("Set default {0} account for non stock items").format(
frappe.bold("Provisional Account")
frappe.bold(_("Provisional Account"))
)
)

View File

@@ -42,7 +42,6 @@ class CustomerGroup(NestedSet):
def validate_currency_for_receivable_and_advance_account(self):
for x in self.accounts:
company_default_currency = frappe.get_cached_value("Company", x.company, "default_currency")
receivable_account_currency = None
advance_account_currency = None
@@ -56,21 +55,6 @@ class CustomerGroup(NestedSet):
"Account", x.advance_account, "account_currency"
)
if receivable_account_currency and receivable_account_currency != company_default_currency:
frappe.throw(
_("Receivable Account: {0} must be in Company default currency: {1}").format(
frappe.bold(x.account),
frappe.bold(company_default_currency),
)
)
if advance_account_currency and advance_account_currency != company_default_currency:
frappe.throw(
_("Advance Account: {0} must be in Company default currency: {1}").format(
frappe.bold(x.advance_account), frappe.bold(company_default_currency)
)
)
if (
receivable_account_currency
and advance_account_currency

View File

@@ -37,7 +37,6 @@ class SupplierGroup(NestedSet):
def validate_currency_for_payable_and_advance_account(self):
for x in self.accounts:
company_default_currency = frappe.get_cached_value("Company", x.company, "default_currency")
payable_account_currency = None
advance_account_currency = None
@@ -49,21 +48,6 @@ class SupplierGroup(NestedSet):
"Account", x.advance_account, "account_currency"
)
if payable_account_currency and payable_account_currency != company_default_currency:
frappe.throw(
_("Payable Account: {0} must be in Company default currency: {1}").format(
frappe.bold(x.account),
frappe.bold(company_default_currency),
)
)
if advance_account_currency and advance_account_currency != company_default_currency:
frappe.throw(
_("Advance Account: {0} must be in Company default currency: {1}").format(
frappe.bold(x.advance_account), frappe.bold(company_default_currency)
)
)
if (
payable_account_currency
and advance_account_currency

View File

@@ -66,29 +66,54 @@ def install(country=None):
"parent_item_group": _("All Item Groups"),
},
# Stock Entry Type
{"doctype": "Stock Entry Type", "name": "Material Issue", "purpose": "Material Issue"},
{"doctype": "Stock Entry Type", "name": "Material Receipt", "purpose": "Material Receipt"},
{
"doctype": "Stock Entry Type",
"name": "Material Issue",
"purpose": "Material Issue",
"is_standard": 1,
},
{
"doctype": "Stock Entry Type",
"name": "Material Receipt",
"purpose": "Material Receipt",
"is_standard": 1,
},
{
"doctype": "Stock Entry Type",
"name": "Material Transfer",
"purpose": "Material Transfer",
"is_standard": 1,
},
{"doctype": "Stock Entry Type", "name": "Manufacture", "purpose": "Manufacture"},
{"doctype": "Stock Entry Type", "name": "Repack", "purpose": "Repack"},
{
"doctype": "Stock Entry Type",
"name": "Manufacture",
"purpose": "Manufacture",
"is_standard": 1,
},
{
"doctype": "Stock Entry Type",
"name": "Repack",
"purpose": "Repack",
"is_standard": 1,
},
{"doctype": "Stock Entry Type", "name": "Disassemble", "purpose": "Disassemble", "is_standard": 1},
{
"doctype": "Stock Entry Type",
"name": "Send to Subcontractor",
"purpose": "Send to Subcontractor",
"is_standard": 1,
},
{
"doctype": "Stock Entry Type",
"name": "Material Transfer for Manufacture",
"purpose": "Material Transfer for Manufacture",
"is_standard": 1,
},
{
"doctype": "Stock Entry Type",
"name": "Material Consumption for Manufacture",
"purpose": "Material Consumption for Manufacture",
"is_standard": 1,
},
# territory: with two default territories, one for home country and one named Rest of the World
{

View File

@@ -188,9 +188,9 @@ class Batch(Document):
if has_expiry_date and not self.expiry_date:
frappe.throw(
msg=_("Please set {0} for Batched Item {1}, which is used to set {2} on Submit.").format(
frappe.bold("Shelf Life in Days"),
frappe.bold(_("Shelf Life in Days")),
get_link_to_form("Item", self.item),
frappe.bold("Batch Expiry Date"),
frappe.bold(_("Batch Expiry Date")),
),
title=_("Expiry Date Mandatory"),
)

View File

@@ -267,6 +267,7 @@ class MaterialRequest(BuyingController):
mr_qty_allowance = frappe.db.get_single_value("Stock Settings", "mr_qty_allowance")
for d in self.get("items"):
precision = d.precision("ordered_qty")
if d.name in mr_items:
if self.material_request_type in ("Material Issue", "Material Transfer", "Customer Provided"):
d.ordered_qty = flt(mr_items_ordered_qty.get(d.name))
@@ -276,14 +277,14 @@ class MaterialRequest(BuyingController):
(d.qty + (d.qty * (mr_qty_allowance / 100))), d.precision("ordered_qty")
)
if d.ordered_qty and d.ordered_qty > allowed_qty:
if d.ordered_qty and flt(d.ordered_qty, precision) > flt(allowed_qty, precision):
frappe.throw(
_(
"The total Issue / Transfer quantity {0} in Material Request {1} cannot be greater than allowed requested quantity {2} for Item {3}"
).format(d.ordered_qty, d.parent, allowed_qty, d.item_code)
)
elif d.ordered_qty and d.ordered_qty > d.stock_qty:
elif d.ordered_qty and flt(d.ordered_qty, precision) > flt(d.stock_qty, precision):
frappe.throw(
_(
"The total Issue / Transfer quantity {0} in Material Request {1} cannot be greater than requested quantity {2} for Item {3}"

View File

@@ -19,6 +19,7 @@
"consider_rejected_warehouses",
"get_item_locations",
"pick_manually",
"ignore_pricing_rule",
"section_break_6",
"scan_barcode",
"column_break_13",
@@ -200,11 +201,18 @@
"fieldname": "pick_manually",
"fieldtype": "Check",
"label": "Pick Manually"
},
{
"default": "0",
"description": "If enabled then system won't apply the pricing rule on the delivery note which will be create from the pick list",
"fieldname": "ignore_pricing_rule",
"fieldtype": "Check",
"label": "Ignore Pricing Rule"
}
],
"is_submittable": 1,
"links": [],
"modified": "2024-03-27 22:49:16.954637",
"modified": "2024-08-14 13:20:42.168827",
"modified_by": "Administrator",
"module": "Stock",
"name": "Pick List",

View File

@@ -50,6 +50,7 @@ class PickList(Document):
customer_name: DF.Data | None
for_qty: DF.Float
group_same_items: DF.Check
ignore_pricing_rule: DF.Check
locations: DF.Table[PickListItem]
material_request: DF.Link | None
naming_series: DF.Literal["STO-PICK-.YYYY.-"]
@@ -1144,7 +1145,7 @@ def create_dn_with_so(sales_dict, pick_list):
for customer in sales_dict:
for so in sales_dict[customer]:
delivery_note = None
kwargs = {"skip_item_mapping": True}
kwargs = {"skip_item_mapping": True, "ignore_pricing_rule": pick_list.ignore_pricing_rule}
delivery_note = create_delivery_note_from_sales_order(so, delivery_note, kwargs=kwargs)
break
if delivery_note:

View File

@@ -1205,3 +1205,64 @@ class TestPickList(FrappeTestCase):
pl_doc.submit()
frappe.db.set_single_value("Stock Settings", "over_picking_allowance", 0)
def test_ignore_pricing_rule_in_pick_list(self):
frappe.flags.print_stmt = False
warehouse = "_Test Warehouse - _TC"
item = make_item(
properties={
"is_stock_item": 1,
"has_batch_no": 1,
"batch_number_series": "IPR-PICKLT-.######",
"create_new_batch": 1,
}
).name
make_stock_entry(
item=item,
to_warehouse=warehouse,
qty=2,
basic_rate=100,
)
pricing_rule = frappe.get_doc(
{
"doctype": "Pricing Rule",
"title": "Same Free Item",
"price_or_product_discount": "Product",
"selling": 1,
"apply_on": "Item Code",
"items": [
{
"item_code": item,
}
],
"same_item": 1,
"is_recursive": 1,
"recurse_for": 2,
"free_qty": 1,
"company": "_Test Company",
"customer": "_Test Customer",
}
)
pricing_rule.save()
frappe.flags.print_stmt = True
so = make_sales_order(item_code=item, qty=2, rate=100, do_not_save=True)
so.set_warehouse = warehouse
so.submit()
self.assertEqual(len(so.items), 2)
self.assertTrue(so.items[1].is_free_item)
pl = create_pick_list(so.name)
pl.ignore_pricing_rule = 1
pl.save()
pl.submit()
self.assertEqual(len(pl.locations), 1)
delivery_note = create_delivery_note(pl.name)
self.assertEqual(len(delivery_note.items), 1)

View File

@@ -837,7 +837,11 @@ class PurchaseReceipt(BuyingController):
def update_assets(self, item, valuation_rate):
assets = frappe.db.get_all(
"Asset",
filters={"purchase_receipt": self.name, "item_code": item.item_code},
filters={
"purchase_receipt": self.name,
"item_code": item.item_code,
"purchase_receipt_item": ("in", [item.name, ""]),
},
fields=["name", "asset_quantity"],
)
@@ -1075,6 +1079,7 @@ def update_billing_percentage(pr_doc, update_modified=True, adjust_incoming_rate
if item.billed_amt and item.amount:
adjusted_amt = flt(item.billed_amt) - flt(item.amount)
adjusted_amt = adjusted_amt * flt(pr_doc.conversion_rate)
item.db_set("rate_difference_with_purchase_invoice", adjusted_amt, update_modified=False)
percent_billed = round(100 * (total_billed_amount / (total_amount or 1)), 6)

View File

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

View File

@@ -701,6 +701,57 @@ class TestSerialandBatchBundle(FrappeTestCase):
serial_nos = get_serial_nos_from_bundle(se.items[0].serial_and_batch_bundle)
self.assertEqual(serial_nos, serial_nos1)
def test_auto_create_serial_and_batch_bundle_for_outward_for_batch_item(self):
item_code = make_item(
"Test Auto Create Batch Bundle for Outward 1",
properties={
"is_stock_item": 1,
"has_batch_no": 1,
"batch_number_series": "ACSBBO-TACSB-.#####",
},
).name
if not frappe.db.exists("Batch", "ACSBBO-TACSB-00001"):
frappe.get_doc(
{
"doctype": "Batch",
"batch_id": "ACSBBO-TACSB-00001",
"item": item_code,
"company": "_Test Company",
}
).insert(ignore_permissions=True)
make_stock_entry(
item_code=item_code,
qty=10,
target="_Test Warehouse - _TC",
rate=500,
use_serial_batch_fields=True,
batch_no="ACSBBO-TACSB-00001",
)
dispatch = make_stock_entry(
item_code=item_code,
qty=10,
target="_Test Warehouse - _TC",
rate=500,
do_not_submit=True,
)
original_value = frappe.db.get_single_value(
"Stock Settings", "auto_create_serial_and_batch_bundle_for_outward"
)
frappe.db.set_single_value("Stock Settings", "auto_create_serial_and_batch_bundle_for_outward", 0)
self.assertRaises(frappe.ValidationError, dispatch.submit)
frappe.db.set_single_value("Stock Settings", "auto_create_serial_and_batch_bundle_for_outward", 1)
dispatch.submit()
frappe.db.set_single_value(
"Stock Settings", "auto_create_serial_and_batch_bundle_for_outward", original_value
)
def get_batch_from_bundle(bundle):
from erpnext.stock.serial_batch_bundle import get_batch_nos

View File

@@ -127,7 +127,7 @@
"label": "Purpose",
"oldfieldname": "purpose",
"oldfieldtype": "Select",
"options": "Material Issue\nMaterial Receipt\nMaterial Transfer\nMaterial Transfer for Manufacture\nMaterial Consumption for Manufacture\nManufacture\nRepack\nSend to Subcontractor",
"options": "Material Issue\nMaterial Receipt\nMaterial Transfer\nMaterial Transfer for Manufacture\nMaterial Consumption for Manufacture\nManufacture\nRepack\nSend to Subcontractor\nDisassemble",
"read_only": 1,
"search_index": 1
},
@@ -143,7 +143,7 @@
"reqd": 1
},
{
"depends_on": "eval:in_list([\"Material Transfer for Manufacture\", \"Manufacture\", \"Material Consumption for Manufacture\"], doc.purpose)",
"depends_on": "eval:in_list([\"Material Transfer for Manufacture\", \"Manufacture\", \"Material Consumption for Manufacture\", \"Disassemble\"], doc.purpose)",
"fieldname": "work_order",
"fieldtype": "Link",
"label": "Work Order",
@@ -242,7 +242,7 @@
},
{
"default": "0",
"depends_on": "eval:in_list([\"Material Issue\", \"Material Transfer\", \"Manufacture\", \"Repack\", \"Send to Subcontractor\", \"Material Transfer for Manufacture\", \"Material Consumption for Manufacture\"], doc.purpose)",
"depends_on": "eval:in_list([\"Material Issue\", \"Material Transfer\", \"Manufacture\", \"Repack\", \"Send to Subcontractor\", \"Material Transfer for Manufacture\", \"Material Consumption for Manufacture\", \"Disassemble\"], doc.purpose)",
"fieldname": "from_bom",
"fieldtype": "Check",
"label": "From BOM",
@@ -697,7 +697,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2024-08-13 19:02:42.386955",
"modified": "2024-08-13 19:05:42.386955",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Entry",

View File

@@ -132,6 +132,7 @@ class StockEntry(StockController):
"Manufacture",
"Repack",
"Send to Subcontractor",
"Disassemble",
]
remarks: DF.Text | None
sales_invoice_no: DF.Link | None
@@ -231,6 +232,7 @@ class StockEntry(StockController):
self.validate_serialized_batch()
self.calculate_rate_and_amount()
self.validate_putaway_capacity()
self.validate_component_quantities()
if not self.get("purpose") == "Manufacture":
# ignore scrap item wh difference and empty source/target wh
@@ -337,6 +339,7 @@ class StockEntry(StockController):
"Repack",
"Send to Subcontractor",
"Material Consumption for Manufacture",
"Disassemble",
]
if self.purpose not in valid_purposes:
@@ -616,6 +619,7 @@ class StockEntry(StockController):
"Manufacture",
"Material Transfer for Manufacture",
"Material Consumption for Manufacture",
"Disassemble",
):
# check if work order is entered
@@ -744,6 +748,34 @@ class StockEntry(StockController):
title=_("Insufficient Stock"),
)
def validate_component_quantities(self):
if self.purpose not in ["Manufacture", "Material Transfer for Manufacture"]:
return
if not frappe.db.get_single_value("Manufacturing Settings", "validate_components_quantities_per_bom"):
return
if not self.fg_completed_qty:
return
raw_materials = self.get_bom_raw_materials(self.fg_completed_qty)
precision = frappe.get_precision("Stock Entry Detail", "qty")
for row in self.items:
if not row.s_warehouse:
continue
if details := raw_materials.get(row.item_code):
if flt(details.get("qty"), precision) != flt(row.qty, precision):
frappe.throw(
_("For the item {0}, the quantity should be {1} according to the BOM {2}.").format(
frappe.bold(row.item_code),
flt(details.get("qty"), precision),
get_link_to_form("BOM", self.bom_no),
),
title=_("Incorrect Component Quantity"),
)
@frappe.whitelist()
def get_stock_and_rate(self):
"""
@@ -898,8 +930,8 @@ class StockEntry(StockController):
).format(
item.idx,
frappe.bold(label),
frappe.bold("Manufacture"),
frappe.bold("Material Consumption for Manufacture"),
frappe.bold(_("Manufacture")),
frappe.bold(_("Material Consumption for Manufacture")),
)
)
@@ -909,7 +941,7 @@ class StockEntry(StockController):
):
frappe.throw(
_("Only one {0} entry can be created against the Work Order {1}").format(
frappe.bold("Manufacture"), frappe.bold(self.work_order)
frappe.bold(_("Manufacture")), frappe.bold(self.work_order)
)
)
@@ -983,7 +1015,7 @@ class StockEntry(StockController):
def set_stock_entry_type(self):
if self.purpose:
self.stock_entry_type = frappe.get_cached_value(
"Stock Entry Type", {"purpose": self.purpose}, "name"
"Stock Entry Type", {"purpose": self.purpose, "is_standard": 1}, "name"
)
def set_purpose_for_stock_entry(self):
@@ -1703,11 +1735,63 @@ class StockEntry(StockController):
},
)
def get_items_for_disassembly(self):
"""Get items for Disassembly Order"""
if not self.work_order:
frappe.throw(_("The Work Order is mandatory for Disassembly Order"))
items = self.get_items_from_manufacture_entry()
s_warehouse = ""
if self.work_order:
s_warehouse = frappe.db.get_value("Work Order", self.work_order, "fg_warehouse")
for row in items:
child_row = self.append("items", {})
for field, value in row.items():
if value is not None:
child_row.set(field, value)
child_row.s_warehouse = (self.from_warehouse or s_warehouse) if row.is_finished_item else ""
child_row.t_warehouse = self.to_warehouse if not row.is_finished_item else ""
child_row.is_finished_item = 0 if row.is_finished_item else 1
def get_items_from_manufacture_entry(self):
return frappe.get_all(
"Stock Entry",
fields=[
"`tabStock Entry Detail`.`item_code`",
"`tabStock Entry Detail`.`item_name`",
"`tabStock Entry Detail`.`description`",
"`tabStock Entry Detail`.`qty`",
"`tabStock Entry Detail`.`transfer_qty`",
"`tabStock Entry Detail`.`stock_uom`",
"`tabStock Entry Detail`.`uom`",
"`tabStock Entry Detail`.`basic_rate`",
"`tabStock Entry Detail`.`conversion_factor`",
"`tabStock Entry Detail`.`is_finished_item`",
"`tabStock Entry Detail`.`batch_no`",
"`tabStock Entry Detail`.`serial_no`",
"`tabStock Entry Detail`.`use_serial_batch_fields`",
],
filters=[
["Stock Entry", "purpose", "=", "Manufacture"],
["Stock Entry", "work_order", "=", self.work_order],
["Stock Entry", "docstatus", "=", 1],
["Stock Entry Detail", "docstatus", "=", 1],
],
order_by="`tabStock Entry Detail`.`idx` desc, `tabStock Entry Detail`.`is_finished_item` desc",
)
@frappe.whitelist()
def get_items(self):
self.set("items", [])
self.validate_work_order()
if self.purpose == "Disassemble":
return self.get_items_for_disassembly()
if not self.posting_date or not self.posting_time:
frappe.throw(_("Posting date and posting time is mandatory"))

View File

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

View File

@@ -7,7 +7,8 @@
"engine": "InnoDB",
"field_order": [
"purpose",
"add_to_transit"
"add_to_transit",
"is_standard"
],
"fields": [
{
@@ -16,7 +17,7 @@
"fieldtype": "Select",
"in_list_view": 1,
"label": "Purpose",
"options": "\nMaterial Issue\nMaterial Receipt\nMaterial Transfer\nMaterial Transfer for Manufacture\nMaterial Consumption for Manufacture\nManufacture\nRepack\nSend to Subcontractor",
"options": "\nMaterial Issue\nMaterial Receipt\nMaterial Transfer\nMaterial Transfer for Manufacture\nMaterial Consumption for Manufacture\nManufacture\nRepack\nSend to Subcontractor\nDisassemble",
"reqd": 1,
"set_only_once": 1
},
@@ -26,13 +27,21 @@
"fieldname": "add_to_transit",
"fieldtype": "Check",
"label": "Add to Transit"
},
{
"default": "0",
"fieldname": "is_standard",
"fieldtype": "Check",
"label": "Is Standard",
"read_only": 1
}
],
"links": [],
"modified": "2024-07-08 08:41:19.385020",
"modified": "2024-08-24 16:00:22.696958",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Entry Type",
"naming_rule": "Set by user",
"owner": "Administrator",
"permissions": [
{

View File

@@ -2,7 +2,7 @@
# For license information, please see license.txt
# import frappe
import frappe
from frappe.model.document import Document
@@ -16,6 +16,7 @@ class StockEntryType(Document):
from frappe.types import DF
add_to_transit: DF.Check
is_standard: DF.Check
purpose: DF.Literal[
"",
"Material Issue",
@@ -26,9 +27,25 @@ class StockEntryType(Document):
"Manufacture",
"Repack",
"Send to Subcontractor",
"Disassemble",
]
# end: auto-generated types
def validate(self):
self.validate_standard_type()
if self.add_to_transit and self.purpose != "Material Transfer":
self.add_to_transit = 0
def validate_standard_type(self):
if self.is_standard and self.name not in [
"Material Issue",
"Material Receipt",
"Material Transfer",
"Material Transfer for Manufacture",
"Material Consumption for Manufacture",
"Manufacture",
"Repack",
"Send to Subcontractor",
"Disassemble",
]:
frappe.throw(f"Stock Entry Type {self.name} cannot be set as standard")

View File

@@ -3,6 +3,33 @@
import unittest
import frappe
class TestStockEntryType(unittest.TestCase):
pass
def test_stock_entry_type_non_standard(self):
stock_entry_type = "Test Manufacturing"
doc = frappe.get_doc(
{
"doctype": "Stock Entry Type",
"__newname": stock_entry_type,
"purpose": "Manufacture",
"is_standard": 1,
}
)
self.assertRaises(frappe.ValidationError, doc.insert)
def test_stock_entry_type_is_standard(self):
for stock_entry_type in [
"Material Issue",
"Material Receipt",
"Material Transfer",
"Material Transfer for Manufacture",
"Material Consumption for Manufacture",
"Manufacture",
"Repack",
"Send to Subcontractor",
]:
self.assertTrue(frappe.db.get_value("Stock Entry Type", stock_entry_type, "is_standard"))

View File

@@ -352,7 +352,8 @@
{
"fieldname": "posting_datetime",
"fieldtype": "Datetime",
"label": "Posting Datetime"
"label": "Posting Datetime",
"search_index": 1
}
],
"hide_toolbar": 1,
@@ -361,7 +362,7 @@
"in_create": 1,
"index_web_pages_for_search": 1,
"links": [],
"modified": "2024-06-27 16:23:18.820049",
"modified": "2024-08-27 09:28:03.961443",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Ledger Entry",

View File

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

View File

@@ -255,7 +255,7 @@ class StockReservationEntry(Document):
if self.has_batch_no
else _("Warehouse"),
frappe.bold(self.warehouse),
frappe.bold("Stock Reservation Entry"),
frappe.bold(_("Stock Reservation Entry")),
)
frappe.throw(msg)
@@ -497,7 +497,8 @@ def validate_stock_reservation_settings(voucher: object) -> None:
if not frappe.db.get_single_value("Stock Settings", "enable_stock_reservation"):
msg = _("Please enable {0} in the {1}.").format(
frappe.bold("Stock Reservation"), frappe.bold("Stock Settings")
frappe.bold(_("Stock Reservation")),
frappe.bold(_("Stock Settings")),
)
frappe.throw(msg)

View File

@@ -175,7 +175,7 @@ class StockSettings(Document):
if self.allow_negative_stock and self.enable_stock_reservation:
frappe.throw(
_("As {0} is enabled, you can not enable {1}.").format(
frappe.bold("Stock Reservation"), frappe.bold("Allow Negative Stock")
frappe.bold(_("Stock Reservation")), frappe.bold(_("Allow Negative Stock"))
)
)
@@ -187,7 +187,7 @@ class StockSettings(Document):
if self.allow_negative_stock:
frappe.throw(
_("As {0} is enabled, you can not enable {1}.").format(
frappe.bold("Allow Negative Stock"), frappe.bold("Stock Reservation")
frappe.bold(_("Allow Negative Stock")), frappe.bold(_("Stock Reservation"))
)
)
@@ -207,7 +207,7 @@ class StockSettings(Document):
if bin_with_negative_stock:
frappe.throw(
_("As there are negative stock, you can not enable {0}.").format(
frappe.bold("Stock Reservation")
frappe.bold(_("Stock Reservation"))
)
)
@@ -221,7 +221,7 @@ class StockSettings(Document):
if has_reserved_stock:
frappe.throw(
_("As there are reserved stock, you cannot disable {0}.").format(
frappe.bold("Stock Reservation")
frappe.bold(_("Stock Reservation"))
)
)

View File

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

View File

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

View File

@@ -354,9 +354,14 @@ def get_email_list(company):
def get_comapny_wise_users(company):
companies = [company]
if parent_company := frappe.db.get_value("Company", company, "parent_company"):
companies.append(parent_company)
users = frappe.get_all(
"User Permission",
filters={"allow": "Company", "for_value": company, "apply_to_all_doctypes": 1},
filters={"allow": "Company", "for_value": ("in", companies), "apply_to_all_doctypes": 1},
fields=["user"],
)

View File

@@ -168,7 +168,7 @@ def get_stock_ledger_entries_for_batch_bundle(filters):
& (sle.has_batch_no == 1)
& (sle.posting_date <= filters["to_date"])
)
.groupby(batch_package.batch_no, batch_package.warehouse)
.groupby(sle.voucher_no, batch_package.batch_no, batch_package.warehouse)
.orderby(sle.item_code, sle.warehouse)
)

View File

@@ -183,7 +183,7 @@ class SerialBatchBundle:
}
if self.sle.actual_qty < 0 and self.is_material_transfer():
values_to_update["valuation_rate"] = sn_doc.avg_rate
values_to_update["valuation_rate"] = flt(sn_doc.avg_rate)
if not frappe.db.get_single_value(
"Stock Settings", "do_not_update_serial_batch_on_creation_of_auto_bundle"
@@ -221,7 +221,15 @@ class SerialBatchBundle:
not self.sle.is_cancelled
and not self.sle.serial_and_batch_bundle
and self.item_details.has_batch_no == 1
and self.item_details.create_new_batch
and (
self.item_details.create_new_batch
or (
frappe.db.get_single_value(
"Stock Settings", "auto_create_serial_and_batch_bundle_for_outward"
)
and self.sle.actual_qty < 0
)
)
):
self.make_serial_batch_no_bundle()
elif not self.sle.is_cancelled:

View File

@@ -647,6 +647,7 @@ class update_entries_after:
and (
posting_datetime = %(posting_datetime)s
)
and creation = %(creation)s
order by
creation ASC
for update
@@ -1526,7 +1527,12 @@ def get_previous_sle_of_current_voucher(args, operator="<", exclude_current_vouc
voucher_no = args.get("voucher_no")
voucher_condition = f"and voucher_no != '{voucher_no}'"
sle = frappe.db.sql(
elif args.get("creation"):
creation = args.get("creation")
operator = "<="
voucher_condition = f"and creation < '{creation}'"
sle = frappe.db.sql( # nosemgrep
f"""
select *, posting_datetime as "timestamp"
from `tabStock Ledger Entry`
@@ -1537,7 +1543,7 @@ def get_previous_sle_of_current_voucher(args, operator="<", exclude_current_vouc
and (
posting_datetime {operator} %(posting_datetime)s
)
order by posting_datetime desc, creation desc
order by posting_date desc, posting_time desc, creation desc
limit 1
for update""",
{
@@ -1623,6 +1629,7 @@ def get_stock_ledger_entries(
if extra_cond:
conditions += f"{extra_cond}"
# nosemgrep
return frappe.db.sql(
"""
select *, posting_datetime as "timestamp"
@@ -1630,7 +1637,7 @@ def get_stock_ledger_entries(
where item_code = %(item_code)s
and is_cancelled = 0
{conditions}
order by posting_datetime {order}, creation {order}
order by posting_date {order}, posting_time {order}, creation {order}
{limit} {for_update}""".format(
conditions=conditions,
limit=limit or "",
@@ -1738,7 +1745,7 @@ def get_valuation_rate(
return batch_obj.get_incoming_rate()
# Get valuation rate from last sle for the same item and warehouse
if last_valuation_rate := frappe.db.sql(
if last_valuation_rate := frappe.db.sql( # nosemgrep
"""select valuation_rate
from `tabStock Ledger Entry` force index (item_warehouse)
where
@@ -1747,7 +1754,7 @@ def get_valuation_rate(
AND valuation_rate >= 0
AND is_cancelled = 0
AND NOT (voucher_no = %s AND voucher_type = %s)
order by posting_datetime desc, name desc limit 1""",
order by posting_date desc, posting_time desc, name desc limit 1""",
(item_code, warehouse, voucher_no, voucher_type),
):
return flt(last_valuation_rate[0][0])
@@ -1818,7 +1825,7 @@ def update_qty_in_future_sle(args, allow_negative_stock=False):
detail = next_stock_reco_detail[0]
datetime_limit_condition = get_datetime_limit_condition(detail)
frappe.db.sql(
frappe.db.sql( # nosemgrep
f"""
update `tabStock Ledger Entry`
set qty_after_transaction = qty_after_transaction + {qty_shift}
@@ -1984,8 +1991,8 @@ def is_negative_with_precision(neg_sle, is_batch=False):
return qty_deficit < 0 and abs(qty_deficit) > 0.0001
def get_future_sle_with_negative_qty(args):
return frappe.db.sql(
def get_future_sle_with_negative_qty(sle_args):
return frappe.db.sql( # nosemgrep
"""
select
qty_after_transaction, posting_date, posting_time,
@@ -1998,28 +2005,28 @@ def get_future_sle_with_negative_qty(args):
and posting_datetime >= %(posting_datetime)s
and is_cancelled = 0
and qty_after_transaction < 0
order by posting_datetime asc
order by posting_date asc, posting_time asc
limit 1
""",
args,
sle_args,
as_dict=1,
)
def get_future_sle_with_negative_batch_qty(args):
return frappe.db.sql(
def get_future_sle_with_negative_batch_qty(sle_args):
return frappe.db.sql( # nosemgrep
"""
with batch_ledger as (
select
posting_date, posting_time, posting_datetime, voucher_type, voucher_no,
sum(actual_qty) over (order by posting_datetime, creation) as cumulative_total
sum(actual_qty) over (order by posting_date, posting_time, creation) as cumulative_total
from `tabStock Ledger Entry`
where
item_code = %(item_code)s
and warehouse = %(warehouse)s
and batch_no=%(batch_no)s
and is_cancelled = 0
order by posting_datetime, creation
order by posting_date, posting_time, creation
)
select * from batch_ledger
where
@@ -2027,7 +2034,7 @@ def get_future_sle_with_negative_batch_qty(args):
and posting_datetime >= %(posting_datetime)s
limit 1
""",
args,
sle_args,
as_dict=1,
)

View File

@@ -144,7 +144,7 @@ class ServiceLevelAgreement(Document):
):
frappe.throw(
_("{0} is not enabled in {1}").format(
frappe.bold("Track Service Level Agreement"),
frappe.bold(_("Track Service Level Agreement")),
get_link_to_form("Support Settings", "Support Settings"),
)
)

View File

@@ -1095,7 +1095,7 @@ Guardian1 Name,Guardian1 Namen,
Guardian2 Email ID,Guardian2 E-Mail-ID,
Guardian2 Mobile No,Guardian2 Mobil Nein,
Guardian2 Name,Guardian2 Namen,
HR Manager,Leiter der Personalabteilung,
HR Manager,Personalwesen Leiter,
HSN,HSN,
HSN/SAC,HSN / SAC,
Half Yearly,Halbjährlich,
@@ -3728,6 +3728,7 @@ Subdivision,Teilgebiet,
Submit Review,Bewertung abschicken,
Submitted,Gebucht,
Supplier Addresses And Contacts,Lieferanten-Adressen und Kontaktdaten,
Supplier Address Details,Vorschau Lieferantenadresse,
Synchronize this account,Synchronisieren Sie dieses Konto,
Tag,Etikett,
Target Location is required while receiving Asset {0} from an employee,"Der Zielspeicherort ist erforderlich, wenn Asset {0} von einem Mitarbeiter empfangen wird",
@@ -6027,10 +6028,10 @@ Hotel Room Reservation Item,Hotelzimmer-Reservierungselement,
Hotel Settings,Hoteleinstellungen,
Default Taxes and Charges,Standard-Steuern und -Abgaben,
Default Invoice Naming Series,Standard-Rechnungsnummernkreis,
HR,HR,
HR,Personalwesen,
Date on which this component is applied,"Datum, an dem diese Komponente angewendet wird",
Salary Slip,Gehaltsabrechnung,
HR User,Nutzer Personalabteilung,
HR User,Personalwesen Benutzer,
Job Applicant,Bewerber,
Body,Körper,
Appraisal Template,Bewertungsvorlage,
@@ -9917,8 +9918,8 @@ Bank/Cash Account {0} doesn't belong to company {1},Das Bank- / Kassenkonto {0}
Base Amount,Basisbetrag,
Base Total Billed Amount,Insg. abgerechneter Betrag in Basiswährung,
Base Total Costing Amount,Gesamtkosten in Basiswährung,
"Based on your HR Policy, select your leave allocation period's end date",Wählen Sie auf der Grundlage Ihrer HR-Richtlinien das Enddatum Ihres Abwesenheitskontingents aus,
"Based on your HR Policy, select your leave allocation period's start date",Wählen Sie auf der Grundlage Ihrer HR-Richtlinie das Startdatum Ihres Abwesenheitskontingents aus,
"Based on your HR Policy, select your leave allocation period's end date",Wählen Sie auf der Grundlage Ihrer Personal-Richtlinien das Enddatum Ihres Abwesenheitskontingents aus,
"Based on your HR Policy, select your leave allocation period's start date",Wählen Sie auf der Grundlage Ihrer Personal-Richtlinie das Startdatum Ihres Abwesenheitskontingents aus,
Batch No is mandatory,Chargennummer ist obligatorisch,
Batch No {0} does not exists,Charge Nr. {0} existiert nicht,
Batch No {0} is linked with Item {1} which has serial no. Please scan serial no instead.,"Die Chargennummer {0} ist mit dem Artikel {1} verknüpft, der eine Seriennummer hat. Bitte scannen Sie stattdessen die Seriennummer.",
@@ -11472,7 +11473,7 @@ Shipment Parcel,Versandpaket,
Shipment Parcel Template,Versandpaketvorlage,
Shipment Type,Sendungstyp,
Shipment details,Sendungsdetails,
Shipping Address Details,Lieferadressendetails,
Shipping Address Details,Vorschau Lieferadresse,
Shipping Address Template,Vorlage Lieferadresse,
Show Balances in Chart Of Accounts,Saldo in Kontenplan anzeigen,
Show Barcode Field in Stock Transactions,Barcode-Feld in Lagerbewegungen anzeigen,
@@ -11815,3 +11816,103 @@ will be,wird sein,
{} is a child company.,{} ist ein untergeordnetes Unternehmen.,
{} {} is already linked with another {},{} {} ist bereits mit einem anderen {} verknüpft,
{} {} is already linked with {} {},{} {} ist bereits mit {} {} verknüpft,
A Transaction Deletion Document: {0} is triggered for {0},Eine Transaktion Löschungsdokument: {0} wird für {0} ausgelöst,
About Us Settings,"Einstellungen zu ""Über uns""",
Allow Internal Transfers at Arm's Length Price,Interne Übertragungen zum Fremdvergleichspreis zulassen,
Asset decapitalized after Asset Capitalization {0} was submitted,"Vermögenswert freigegeben, nachdem Anlagenaktivierung {0} gebucht wurde",
Auto Email Report,Auto Email-Bericht,
Auto close Opportunity Replied after the no. of days mentioned above,Automatische Schließungschaltung antwortete nach der oben genannten Anzahl von Tagen,
Avg Rate (Balance Stock),Durchschnittliche Rate (Lagerbestand),
Billing Interval in Subscription Plan must be Month to follow calendar months,"Abrechnungsintervall im Abonnementplan muss ""Monat"" sein, um Kalendermonate zu folgen",
Bulk Update,Massen-Update,
Can't disable batch wise valuation for active batches.,Sie können die chargenweise Bewertung für aktive Chargen nicht deaktivieren.,
Can't disable batch wise valuation for items with FIFO valuation method.,Sie können die chargenweise Bewertung für Artikel mit FIFO-Bewertungsmethode nicht deaktivieren.,
Cannot disable batch wise valuation for FIFO valuation method.,Sie können die chargenweise Bewertung für die FIFO-Bewertungsmethode nicht deaktivieren.,
Cannot enqueue multi docs for one company. {0} is already queued/running for company: {1},Mehrere Dokumente für ein Unternehmen können nicht in die Warteschlange gestellt werden. {0} ist bereits in die Warteschlange gestellt/wird für das Unternehmen ausgeführt: {1},
Contact Us Settings,Einstellungen zu „Kontaktieren Sie uns“,
Create Journal Entries,Buchungssätze erstellen,
Create a variant with the template image.,Eine Variante mit dem Vorlagenbild erstellen.,
Create in Draft Status,In Entwurfsstatus erstellen,
Custom delimiters,Benutzerdefinierte Trennzeichen,
Deleted Documents,Gelöschte Dokumente,
Delimiter options,Trennzeichenoptionen,
Dependent Task {0} is not a Template Task,Abhängige Aufgabe {0} ist keine Vorlage einer Aufgabe,
Depreciation Entry Posting Status,Buchungsstatus des Abschreibungseintrags,
Depreciation Schedule View,Ansicht Abschreibungsplan,
Depreciation cannot be calculated for fully depreciated assets,Für vollständig abgeschriebene Vermögensgegenstände kann keine Abschreibung berechnet werden,
Do Not Use Batch-wise Valuation,Keine chargenweise Bewertung verwenden,
Domain Settings,Domäneneinstellungen,
Email Domain,E-Mail-Domain,
Enable Immutable Ledger,Unveränderliches Hauptbuch aktivieren,
Enable it if users want to consider rejected materials to dispatch.,"Aktivieren Sie diese Option, wenn Benutzer zurückgewiesenes Material für den Versand berücksichtigen möchten.",
Excess Materials Consumed,Überschüssige Materialien verbraucht,
Excess Transfer,Überschuss-Übertragung,
FIFO Queue vs Qty After Transaction Comparison,Vergleich zwischen FIFO-Warteschlange und Menge nach Transaktion,
"For the {0}, the quantity is required to make the return entry","Für die {0} ist die Menge erforderlich, um die Retoure zu erstellen",
"If enabled, the item rate won't adjust to the valuation rate during internal transfers, but accounting will still use the valuation rate.","Falls aktiviert, wird der Artikelkurs bei internen Transfers nicht an den Bewertungskurs angepasst, aber die Buchhaltung verwendet weiterhin den Wertansatz.",
"If enabled, the system will use the moving average valuation method to calculate the valuation rate for the batched items and will not consider the individual batch-wise incoming rate.","Falls aktiviert, verwendet das System die Bewertungsmethode des gleitenden Durchschnitts zur Berechnung des Wertansatzes für die chargenweisen Artikel und berücksichtigt nicht den individuellen chargenweisen Eingangskurs.",
Job Worker,Unterauftragnehmer,
Job Worker Address,Unterauftragnehmer Adresse,
Job Worker Address Details,Vorschau Adresse Unterauftragnehmer,
Job Worker Contact,Vertrag des Unterauftragnehmers,
Job Worker Delivery Note,Lieferschein des Unterauftragnehmers,
Job Worker Name,Name des Unterauftragnehmer,
Job Worker Warehouse,Lagerhaus des Unterauftragnehmers,
"Learn about <a href=""https://docs.erpnext.com/docs/v13/user/manual/en/accounts/articles/common_party_accounting#:~:text=Common%20Party%20Accounting%20in%20ERPNext,Invoice%20against%20a%20primary%20Supplier."">Common Party</a>","Erfahren Sie mehr über die <a href=""https://docs.erpnext.com/docs/v13/user/manual/en/accounts/articles/common_party_accounting#:~:text=Common%20Party%20Accounting%20in%20ERPNext,Invoice%20against%20a%20primary%20Supplier."">Verknüpfung von Kunden und Lieferanten</a>",
Notification,Benachrichtigung,
Notification Settings,Benachrichtigungseinstellungen,
Offsetting for Accounting Dimension,Verrechnung für Buchhaltungsdimension,
Only Include Allocated Payments,Nur zugeordnete Zahlungen einbeziehen,
Only one {0} entry can be created against the Work Order {1},Nur ein {0} Eintrag kann gegen den Arbeitsauftrag {1} erstellt werden,
Over Picking Allowance,Überkommissionierzugabe,
Over Transfer Allowance,Überschlusstransferzugabe,
Overbilling of {} ignored because you have {} role.,"Überhöhte Abrechnung von {} wurde ignoriert, weil Sie die Rolle {} haben.",
Payment Reconciliation Job: {0} is running for this party. Can't reconcile now.,Job für Zahlungsabgleich: {0} läuft für diese Partei. Kann jetzt nicht abgleichen.,
Payment Request created from Sales Order or Purchase Order will be in Draft status. When disabled document will be in unsaved state.,"Eine Zahlungsanforderung, die aus einem Auftrag oder einer Bestellung erstellt wurde, wird im Entwurfsstatus sein. Wenn deaktiviert, wird das Dokument in ungespeichertem Zustand sein.",
Payment Request took too long to respond. Please try requesting for payment again.,"Zahlungsaufforderung hat zu lange gedauert, um zu antworten. Bitte versuchen Sie die Zahlung erneut anzufragen.",
Payment Terms Status for Sales Order,Status für Zahlungsbedingungen für Aufträge,
Pipeline By,Pipeline von,
Please enable Use Old Serial / Batch Fields to make_bundle,"Bitte aktivieren Sie ""Alte Serien-/Batchfelder verwenden"" für make_bundle",
Print Style,Druckstil,
Reconcile All Serial Nos / Batches,Alle Seriennummern/Chargen abgleichen,
Reset Company Default Values,Standardwerte des Unternehmens zurücksetzen,
Reset Raw Materials Table,Tabelle Rohstoffe zurücksetzen,
Return Against Subcontracting Receipt,Retoure gegen Unterauftragsbeleg,
Return Components,Komponenten zurückgeben,
Returned Against,Zurückgegeben gegen,
Returned exchange rate is neither integer not float.,Der zurückgegebene Wechselkurs ist weder Integer noch Float.,
Round Off Tax Amount,Steuerbetrag abrunden,
Rounding Loss Allowance,Rundungsverlusttoleranz,
Rounding Loss Allowance should be between 0 and 1,Rundungsverlusttoleranz muss zwischen 0 und 1 sein,
Row #{0}: Rejected Warehouse is mandatory for the rejected Item {1},Zeile #{0}: Ausschusslager ist für den abgelehnten Artikel {1} obligatorisch,
Row #{0}: You cannot use the inventory dimension '{1}' in Stock Reconciliation to modify the quantity or valuation rate. Stock reconciliation with inventory dimensions is intended solely for performing opening entries.,"Zeile #{0}: Sie können die Bestandsdimension '{1}' in der Bestandsabgleich nicht verwenden, um die Menge oder den Wertansatz zu ändern. Die Bestandsabgleich mit Bestandsdimensionen ist ausschließlich für die Durchführung von Eröffnungsbuchungen vorgesehen.",
Row {0}: Packed Qty must be equal to {1} Qty.,Zeile {0}: Verpackte Menge muss gleich der {1} Menge sein.,
Row {0}: Total Number of Depreciations cannot be less than or equal to Opening Number of Booked Depreciations,Zeile {0}: Die Gesamtzahl der Abschreibungen kann nicht kleiner oder gleich der Anzahl der gebuchten Abschreibungen zu Beginn sein,
SCO Supplied Item,Artikel beigestellt für Unterauftrag,
SLA Fulfilled On Status,SLA erfüllt am Status,
SLA will be applied if {1} is set as {2}{3},"SLA wird angewendet, wenn {1} als {2}{3} eingestellt ist",
SMS Settings,SMS-Einstellungen,
SO Total Qty,Kd.-Auftr.-Gesamtmenge,
"Sales Order {0} already exists against Customer's Purchase Order {1}. To allow multiple Sales Orders, Enable {2} in {3}","Auftrag {0} existiert bereits für die Kundenbestellung {1}. Um mehrere Verkaufsaufträge zuzulassen, aktivieren Sie {2} in {3}",
"Scorecard variables can be used, as well as:
{total_score} (the total score from that period),
{period_number} (the number of periods to present day)
","Variablen der Bewertung können verwendet werden, sowie:
{total_score} (die Gesamtpunktzahl aus diesem Zeitraum),
{period_number} (die Anzahl der Zeiträume bis zum heutigen Tag)
",
Select Accounting Dimension.,Buchhaltungsdimension auswählen,
Select Corrective Operation,Nacharbeit auswählen,
Select Date of Birth. This will validate Employees age and prevent hiring of under-age staff.,Wählen Sie Geburtsdatum. Damit wird das Alter der Mitarbeiter überprüft und die Einstellung von minderjährigen Mitarbeitern verhindert.,
"Select Date of joining. It will have impact on the first salary calculation, Leave allocation on pro-rata bases.",Wählen Sie Eintrittsdatum. Es wirkt sich auf die erste Gehaltsberechnung und die Zuteilung von Abwesenheiten auf Pro-rata-Basis aus.,
Select Dimension,Dimension auswählen,
Select Items for Quality Inspection,Artikel für die Qualitätsprüfung auswählen,
Select Job Worker Address,Unterauftragnehmer Adresse auswählen,
Service Expenses,Wartungsaufwand,
Service Level Agreement for {0} {1} already exists.,Service Level Agreement für {0} {1} existiert bereits.,
System Settings,Systemverwaltung,
Website Script,Webseiten-Skript,
Website Theme,Webseiten-Thema,
Workflow Action,Workflow-Aktion,
Workflow State,Workflow-Status,
{0} is not running. Cannot trigger events for this Document,{0} läuft nicht. Ereignisse für dieses Dokument können nicht ausgelöst werden,
Can't render this file because it is too large.