Compare commits

..

152 Commits

Author SHA1 Message Date
Frappe PR Bot
6a53e823f0 chore(release): Bumped to Version 13.54.10
## [13.54.10](https://github.com/frappe/erpnext/compare/v13.54.9...v13.54.10) (2023-11-08)

### Bug Fixes

* add missing disbursement account in update_old_loans patch ([37b1a0e](37b1a0e778))
* bump pygithub requirement to handle conflict ([#37800](https://github.com/frappe/erpnext/issues/37800)) ([13d5eec](13d5eec194))
* e-invoice jwt verification error ([#37818](https://github.com/frappe/erpnext/issues/37818)) ([8f4ded6](8f4ded6ad1))
* permission error while creating Supplier Quotation from Portal (backport [#37864](https://github.com/frappe/erpnext/issues/37864)) ([#37872](https://github.com/frappe/erpnext/issues/37872)) ([6952f0f](6952f0f082))
* Quality Inspection Parameter migration - DuplicateEntryError due to case sensitivity (backport [#37499](https://github.com/frappe/erpnext/issues/37499)) ([#37918](https://github.com/frappe/erpnext/issues/37918)) ([4789eca](4789ecacea))
* remove voucher type and no for Item and Warehouse based reposting (backport [#37849](https://github.com/frappe/erpnext/issues/37849)) (backport [#37850](https://github.com/frappe/erpnext/issues/37850)) ([#37938](https://github.com/frappe/erpnext/issues/37938)) ([8cb0f69](8cb0f690d5))
2023-11-08 06:04:16 +00:00
Deepesh Garg
c808607975 Merge pull request #37966 from frappe/version-13-hotfix
chore: release v13
2023-11-08 11:33:04 +05:30
mergify[bot]
8cb0f690d5 fix: remove voucher type and no for Item and Warehouse based reposting (backport #37849) (backport #37850) (#37938)
* fix: remove voucher type and no for Item and Warehouse based reposting (backport #37849) (#37850)

* fix: remove voucher type and no for Item and Warehouse based reposting

(cherry picked from commit 0104897d69)

* chore: fix test cases

---------

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

# Conflicts:
#	erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py
#	erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
#	erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json

* chore: fix conflicts

* chore: fix conflicts

* chore: fix conflicts

* fix: conflicts

---------

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2023-11-07 16:07:17 +05:30
mergify[bot]
4789ecacea fix: Quality Inspection Parameter migration - DuplicateEntryError due to case sensitivity (backport #37499) (#37918)
fix: Quality Inspection Parameter migration - DuplicateEntryError due to case sensitivity (#37499)

* fix: account for case-insensitive database primary key for parameter names

* chore: linting

(cherry picked from commit b099590b2c)

Co-authored-by: Richard Case <110036763+casesolved-co-uk@users.noreply.github.com>
2023-11-05 06:22:42 +00:00
Anand Baburajan
d00257ffd7 Merge pull request #37891 from frappe/mergify/bp/version-13-hotfix/pr-37889
fix: add missing disbursement account in update_old_loans patch (backport #37889)
2023-11-03 21:48:57 +05:30
anandbaburajan
37b1a0e778 fix: add missing disbursement account in update_old_loans patch
(cherry picked from commit ad64065ec6)
2023-11-03 16:17:29 +00:00
mergify[bot]
6952f0f082 fix: permission error while creating Supplier Quotation from Portal (backport #37864) (#37872)
* fix: permission error while creating Supplier Quotation from Portal

(cherry picked from commit e019d43d0b)

# Conflicts:
#	erpnext/controllers/buying_controller.py

* chore: `conflicts`

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-11-03 16:26:45 +05:30
Frappe PR Bot
c84384aa1b chore(release): Bumped to Version 13.54.9
## [13.54.9](https://github.com/frappe/erpnext/compare/v13.54.8...v13.54.9) (2023-11-01)

### Bug Fixes

* e-invoice jwt verification error (backport [#37818](https://github.com/frappe/erpnext/issues/37818)) ([#37820](https://github.com/frappe/erpnext/issues/37820)) ([428f7a6](428f7a61ef))
2023-11-01 07:59:52 +00:00
mergify[bot]
428f7a61ef fix: e-invoice jwt verification error (backport #37818) (#37820)
fix: e-invoice jwt verification error (#37818)

(cherry picked from commit 8f4ded6ad1)

Co-authored-by: Dany Robert <rtdany10@gmail.com>
2023-11-01 13:28:21 +05:30
Dany Robert
8f4ded6ad1 fix: e-invoice jwt verification error (#37818) 2023-11-01 13:27:24 +05:30
Frappe PR Bot
9607d69ada chore(release): Bumped to Version 13.54.8
## [13.54.8](https://github.com/frappe/erpnext/compare/v13.54.7...v13.54.8) (2023-10-31)

### Bug Fixes

* bump pygithub requirement to handle conflict (backport [#37800](https://github.com/frappe/erpnext/issues/37800)) ([#37808](https://github.com/frappe/erpnext/issues/37808)) ([aaba335](aaba335273))
2023-10-31 14:46:40 +00:00
mergify[bot]
aaba335273 fix: bump pygithub requirement to handle conflict (backport #37800) (#37808)
fix: bump pygithub requirement to handle conflict (#37800)

build: bump pygithub requirement

This conflicts with pyjwt

(cherry picked from commit 13d5eec194)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2023-10-31 20:04:31 +05:30
Ankush Menat
13d5eec194 fix: bump pygithub requirement to handle conflict (#37800)
build: bump pygithub requirement

This conflicts with pyjwt
2023-10-31 19:55:56 +05:30
Frappe PR Bot
411bba1b98 chore(release): Bumped to Version 13.54.7
## [13.54.7](https://github.com/frappe/erpnext/compare/v13.54.6...v13.54.7) (2023-10-31)

### Bug Fixes

* wrong german translation ([#37658](https://github.com/frappe/erpnext/issues/37658)) ([5669a89](5669a89afe))
2023-10-31 12:45:41 +00:00
rohitwaghchaure
49f2d868c7 Merge pull request #37786 from frappe/version-13-hotfix
chore: release v13
2023-10-31 18:14:20 +05:30
Raffael Meyer
335b6c84db Merge pull request #37666 from frappe/mergify/bp/version-13-hotfix/pr-37658
fix: wrong german translation (backport #37658)
2023-10-28 23:07:59 +02:00
barredterra
00dff0a219 Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-37658 2023-10-28 22:42:43 +02:00
mergify[bot]
b55b428114 chore: fixed test case non_internal_transfer_delivery_note (backport #37671) (#37677)
chore: fixed test case non_internal_transfer_delivery_note (#37671)

(cherry picked from commit 2bcff4c7f2)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2023-10-25 13:55:17 +05:30
Frappe PR Bot
dd6498d347 chore(release): Bumped to Version 13.54.6
## [13.54.6](https://github.com/frappe/erpnext/compare/v13.54.5...v13.54.6) (2023-10-25)

### Bug Fixes

* incorrect cost center in the purchase invoice (backport [#37591](https://github.com/frappe/erpnext/issues/37591)) ([#37610](https://github.com/frappe/erpnext/issues/37610)) ([4fc45b0](4fc45b035a))
* remove from or target warehouse for non internal transfer entries (backport [#37612](https://github.com/frappe/erpnext/issues/37612)) ([#37629](https://github.com/frappe/erpnext/issues/37629)) ([b5b5187](b5b51879ee))
2023-10-25 03:45:08 +00:00
Raffael Meyer
5669a89afe fix: wrong german translation (#37658)
(cherry picked from commit fa5780ca81)
2023-10-25 03:44:15 +00:00
Deepesh Garg
6e871c51ef Merge pull request #37655 from frappe/version-13-hotfix
chore: release v13
2023-10-25 09:13:26 +05:30
mergify[bot]
b5b51879ee fix: remove from or target warehouse for non internal transfer entries (backport #37612) (#37629)
* fix: remove from or target warehouse for non internal transfer entries (#37612)

(cherry picked from commit 5136fe196b)

* chore: removed test cases

* chore: removed test case

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2023-10-24 08:41:41 +05:30
mergify[bot]
4fc45b035a fix: incorrect cost center in the purchase invoice (backport #37591) (#37610)
* fix: incorrect cost center in the purchase invoice (#37591)

(cherry picked from commit 14b009b093)

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

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2023-10-20 17:03:25 +05:30
Frappe PR Bot
24103554f5 chore(release): Bumped to Version 13.54.5
## [13.54.5](https://github.com/frappe/erpnext/compare/v13.54.4...v13.54.5) (2023-10-19)

### Bug Fixes

* e-commerce permissions for address (backport [#37554](https://github.com/frappe/erpnext/issues/37554)) ([#37560](https://github.com/frappe/erpnext/issues/37560)) ([49d0ab5](49d0ab5867))
* **gp:** wrong `allocated_amount` on multi sales person invoice ([7805c3a](7805c3acf6))
* Issues related to RFQ and Supplier Quotation on Portal (backport [#37565](https://github.com/frappe/erpnext/issues/37565)) (backport [#37577](https://github.com/frappe/erpnext/issues/37577)) ([#37588](https://github.com/frappe/erpnext/issues/37588)) ([bc907b2](bc907b22d4))
* keep customer/supplier website role by default ([52aff1f](52aff1f703))
* negative valuation rate in PR return (backport [#37424](https://github.com/frappe/erpnext/issues/37424)) ([#37462](https://github.com/frappe/erpnext/issues/37462)) ([66ad823](66ad823417))
* use `flt` to ignore TypeError (backport [#37481](https://github.com/frappe/erpnext/issues/37481)) ([#37489](https://github.com/frappe/erpnext/issues/37489)) ([8b4e692](8b4e69235a))
2023-10-19 11:36:26 +00:00
Deepesh Garg
d459421175 Merge pull request #37544 from frappe/version-13-hotfix
chore: release v13
2023-10-19 17:04:53 +05:30
mergify[bot]
bc907b22d4 fix: Issues related to RFQ and Supplier Quotation on Portal (backport #37565) (backport #37577) (#37588)
* fix: Issues related to RFQ and Supplier Quotation on Portal (backport #37565) (#37577)

* fix: Issues related to RFQ and Supplier Quotation on Portal (#37565)

fix: RFQ and Supplier Quotation for Portal
(cherry picked from commit 2851a41310)

* chore: removed backport changes

---------

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

# Conflicts:
#	erpnext/templates/includes/rfq.js

* chore: fix conflicts

---------

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2023-10-19 16:39:38 +05:30
mergify[bot]
7e26449b9f chore: rewrite query using query builder (backport #37310) (#37585)
* chore: rewrite query using query builder

(cherry picked from commit 25718f5cc7)

* chore: fix shopping cart tests

(cherry picked from commit fb51cae88b)

* chore: fix test case

---------

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2023-10-19 15:58:45 +05:30
mergify[bot]
49d0ab5867 fix: e-commerce permissions for address (backport #37554) (#37560)
* fix: E-commerce permissions

(cherry picked from commit f4d74990fe)

# Conflicts:
#	erpnext/accounts/party.py
#	erpnext/controllers/selling_controller.py

* chore: conflicts

---------

Co-authored-by: Ankush Menat <ankush@frappe.io>
2023-10-18 18:46:10 +05:30
Deepesh Garg
ddec91202a Merge branch 'version-13' into version-13-hotfix 2023-10-17 17:59:36 +05:30
Ankush Menat
fe681acaad Merge pull request #37534 from frappe/mergify/bp/version-13-hotfix/pr-37532
fix: keep customer/supplier website role by default (backport #37532)
2023-10-16 17:36:59 +05:30
Ankush Menat
52aff1f703 fix: keep customer/supplier website role by default
(cherry picked from commit d2096cfdb7)
2023-10-16 17:35:44 +05:30
ruthra kumar
1cfc6cfe5d Merge pull request #37509 from frappe/mergify/bp/version-13-hotfix/pr-37435
fix(gp): wrong `allocated_amount` when grouped by Sales Person (backport #37435)
2023-10-15 10:30:28 +05:30
Dany Robert
7805c3acf6 fix(gp): wrong allocated_amount on multi sales person invoice
(cherry picked from commit bda82bf1e9)
2023-10-15 08:14:26 +05:30
mergify[bot]
8b4e69235a fix: use flt to ignore TypeError (backport #37481) (#37489)
fix: use `flt` to ignore TypeError (#37481)

(cherry picked from commit d2b22db500)

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-10-13 10:25:28 +05:30
Frappe PR Bot
35a62e2e8d chore(release): Bumped to Version 13.54.4
## [13.54.4](https://github.com/frappe/erpnext/compare/v13.54.3...v13.54.4) (2023-10-11)

### Bug Fixes

* negative valuation rate in PR return (backport [#37424](https://github.com/frappe/erpnext/issues/37424)) (backport [#37462](https://github.com/frappe/erpnext/issues/37462)) ([#37463](https://github.com/frappe/erpnext/issues/37463)) ([f6b3532](f6b35324ef))
2023-10-11 14:24:45 +00:00
mergify[bot]
f6b35324ef fix: negative valuation rate in PR return (backport #37424) (backport #37462) (#37463)
fix: negative valuation rate in PR return (backport #37424) (#37462)

* fix: negative valuation rate in PR return (#37424)

* fix: negative valuation rate in PR return

* test: add test case for PR return

(cherry picked from commit 26ad688584)

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

* chore: `conflicts`

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
(cherry picked from commit 66ad823417)

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2023-10-11 19:53:10 +05:30
mergify[bot]
66ad823417 fix: negative valuation rate in PR return (backport #37424) (#37462)
* fix: negative valuation rate in PR return (#37424)

* fix: negative valuation rate in PR return

* test: add test case for PR return

(cherry picked from commit 26ad688584)

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

* chore: `conflicts`

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-10-11 19:17:02 +05:30
ruthra kumar
f674635ecf Merge pull request #37440 from ruthra-kumar/patch_for_exchange_rate_service_provider
chore: patch to set default exchange rate service providers
2023-10-10 21:02:10 +05:30
Frappe PR Bot
0000c38563 chore(release): Bumped to Version 13.54.3
## [13.54.3](https://github.com/frappe/erpnext/compare/v13.54.2...v13.54.3) (2023-10-10)

### Bug Fixes

* change currency exchange API to api.frankfurter.app ([76919c4](76919c4af2))
2023-10-10 14:47:51 +00:00
Deepesh Garg
23f12f6463 Merge pull request #37432 from frappe/version-13-hotfix
chore: release v13
2023-10-10 20:16:20 +05:30
ruthra kumar
6f6db432b5 chore: patch to set default exchange rate service provider 2023-10-10 17:37:04 +05:30
ruthra kumar
ba7a822682 Merge pull request #37389 from frappe/mergify/bp/version-13/pr-37385
refactor: multiple service providers for Currency Exchange rates (backport #37385)
2023-10-07 19:28:53 +05:30
ruthra kumar
32ae68eb5c refactor: access_key field for service_provider
(cherry picked from commit 22fb65621c)
2023-10-07 12:46:52 +00:00
ruthra kumar
9eb02637d8 refactor: selectable exchange rate service providers
(cherry picked from commit 55dfd8e995)
2023-10-07 12:46:52 +00:00
ruthra kumar
fd8543ebd3 Merge pull request #37385 from ruthra-kumar/multiple_currency_exchange_provider
refactor: multiple service providers for Currency Exchange rates
2023-10-07 15:34:05 +05:30
ruthra kumar
22fb65621c refactor: access_key field for service_provider 2023-10-07 14:28:32 +05:30
ruthra kumar
55dfd8e995 refactor: selectable exchange rate service providers 2023-10-06 15:05:57 +05:30
Frappe PR Bot
fa0eef96b9 chore(release): Bumped to Version 13.54.1
## [13.54.1](https://github.com/frappe/erpnext/compare/v13.54.0...v13.54.1) (2023-10-05)

### Bug Fixes

* change currency exchange API to api.frankfurter.app ([1da808a](1da808a125))
2023-10-05 03:51:29 +00:00
ruthra kumar
ee42b0a16b Merge pull request #37365 from frappe/mergify/bp/version-13/pr-37355
fix: change currency exchange API to api.frankfurter.app (backport #37355)
2023-10-05 09:20:03 +05:30
ruthra kumar
40e475836e refactor(test): update exchange rate based on 'frankfurter' provider
(cherry picked from commit a3fd4db450)
2023-10-05 03:14:51 +00:00
dsnetprofitxl
1da808a125 fix: change currency exchange API to api.frankfurter.app
(cherry picked from commit 76919c4af2)
2023-10-05 03:14:49 +00:00
ruthra kumar
c86cd99395 Merge pull request #37355 from dsnetprofitxl/fix-exchange-rate
fix: change currency exchange API to api.frankfurter.app
2023-10-04 21:58:13 +05:30
ruthra kumar
a3fd4db450 refactor(test): update exchange rate based on 'frankfurter' provider 2023-10-04 21:33:11 +05:30
dsnetprofitxl
76919c4af2 fix: change currency exchange API to api.frankfurter.app 2023-10-04 16:01:22 +05:30
Frappe PR Bot
b0708d29a8 chore(release): Bumped to Version 13.54.0
# [13.54.0](https://github.com/frappe/erpnext/compare/v13.53.0...v13.54.0) (2023-10-04)

### Bug Fixes

* incorrect stock ledger entries in DN (backport [#36944](https://github.com/frappe/erpnext/issues/36944)) ([#37067](https://github.com/frappe/erpnext/issues/37067)) ([5833c4d](5833c4dae2))
* trial balance report freezes when adding filters (backport [#37264](https://github.com/frappe/erpnext/issues/37264)) ([#37267](https://github.com/frappe/erpnext/issues/37267)) ([ff6b38c](ff6b38c9e7))
* Update `advance_paid` in SO/PO after unlinking from advance entry ([a6bef64](a6bef64c8e))

### Features

* `Stock Ledger Variance` report (backport [#37165](https://github.com/frappe/erpnext/issues/37165)) ([#37184](https://github.com/frappe/erpnext/issues/37184)) ([5092ea1](5092ea175e))
* provision to make reposting entries from Stock and Account Value Comparison Report (backport [#35365](https://github.com/frappe/erpnext/issues/35365)) ([#37171](https://github.com/frappe/erpnext/issues/37171)) ([48eb6a6](48eb6a6573))
2023-10-04 04:15:19 +00:00
ruthra kumar
caa8417306 Merge pull request #37340 from frappe/version-13-hotfix
chore: release v13
2023-10-04 09:43:53 +05:30
mergify[bot]
ff6b38c9e7 fix: trial balance report freezes when adding filters (backport #37264) (#37267)
fix: trial balance report freezes when adding filters (#37264)

fix: Only add onclick if correct data is returned

workaround for https://github.com/frappe/datatable/issues/177

(cherry picked from commit 2dc95e5d59)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2023-09-27 12:37:10 +05:30
Frappe PR Bot
4285bbcdc0 chore: release v13 (#37245)
* feat: provision to make reposting entries from Stock and Account Value Comparison Report (backport #35365) (#37171)

* feat: provision to make reposting entries from Stock and Account Value Comparison Report

(cherry picked from commit 7b818e9d34)

* fix: `linter`

* fix(ux): throw if no row selected to create repost entries

---------

Co-authored-by: Rohit Waghchaure <rohitw1991@gmail.com>
Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>

* fix: incorrect stock ledger entries in DN (backport #36944) (#37067)

* fix: incorrect stock ledger entries in DN (#36944)

(cherry picked from commit 0e83190c19)

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

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>

* feat: `Stock Ledger Variance` report (backport #37165) (#37184)

feat: `Stock Ledger Variance` report (#37165)

* feat: `Stock Ledger Variance` report

* refactor: `get_data()`

(cherry picked from commit acda72d616)

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>

* fix: Update `advance_paid` in SO/PO after unlinking from advance entry

(cherry picked from commit 426350eee6)

* test: Impact on SO of advance PE submit and unlinking/replacement by SI

(cherry picked from commit 8a4954d713)

---------

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Co-authored-by: Rohit Waghchaure <rohitw1991@gmail.com>
Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
Co-authored-by: marination <maricadsouza221197@gmail.com>
Co-authored-by: ruthra kumar <ruthra@erpnext.com>
2023-09-27 09:25:47 +05:30
ruthra kumar
b6dc47ec8a Merge pull request #37197 from frappe/mergify/bp/version-13-hotfix/pr-37069
fix: Recalculate `advance_paid` in SO/PO after unlinking from advance entry (backport #37069)
2023-09-23 11:05:10 +05:30
marination
d0c6f286cf test: Impact on SO of advance PE submit and unlinking/replacement by SI
(cherry picked from commit 8a4954d713)
2023-09-22 03:06:37 +00:00
marination
a6bef64c8e fix: Update advance_paid in SO/PO after unlinking from advance entry
(cherry picked from commit 426350eee6)
2023-09-22 03:06:36 +00:00
mergify[bot]
5092ea175e feat: Stock Ledger Variance report (backport #37165) (#37184)
feat: `Stock Ledger Variance` report (#37165)

* feat: `Stock Ledger Variance` report

* refactor: `get_data()`

(cherry picked from commit acda72d616)

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-09-21 07:59:13 +00:00
mergify[bot]
5833c4dae2 fix: incorrect stock ledger entries in DN (backport #36944) (#37067)
* fix: incorrect stock ledger entries in DN (#36944)

(cherry picked from commit 0e83190c19)

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

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2023-09-20 18:12:08 +05:30
mergify[bot]
48eb6a6573 feat: provision to make reposting entries from Stock and Account Value Comparison Report (backport #35365) (#37171)
* feat: provision to make reposting entries from Stock and Account Value Comparison Report

(cherry picked from commit 7b818e9d34)

* fix: `linter`

* fix(ux): throw if no row selected to create repost entries

---------

Co-authored-by: Rohit Waghchaure <rohitw1991@gmail.com>
Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-09-20 11:14:48 +00:00
Frappe PR Bot
7c1288f726 chore(release): Bumped to Version 13.53.0
# [13.53.0](https://github.com/frappe/erpnext/compare/v13.52.15...v13.53.0) (2023-09-19)

### Bug Fixes

* fetch logic for repay_from_salary in loan_repayment [v13] ([#37136](https://github.com/frappe/erpnext/issues/37136)) ([d4f8b05](d4f8b057e1))

### Features

* provision to create RIV from `Stock Ledger Invariant Check` report (backport [#37115](https://github.com/frappe/erpnext/issues/37115)) ([#37149](https://github.com/frappe/erpnext/issues/37149)) ([86b152f](86b152fe5c))
2023-09-19 13:22:41 +00:00
Deepesh Garg
c89715d6da Merge pull request #37154 from frappe/version-13-hotfix
chore: release v13
2023-09-19 18:50:07 +05:30
Anand Baburajan
d4f8b057e1 fix: fetch logic for repay_from_salary in loan_repayment [v13] (#37136)
fix: fetch logic for repay_from_salary in loan_repayment
2023-09-19 15:18:17 +05:30
mergify[bot]
86b152fe5c feat: provision to create RIV from Stock Ledger Invariant Check report (backport #37115) (#37149)
* feat: provision to create RIV from `Stock Ledger Invariant Check` report (#37115)

* feat: provision to create RIV from `Stock Ledger Invariant Check` report

* fix: `linter`

(cherry picked from commit 9c9d0ecb73)

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

* chore: `conflicts`

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-09-19 09:19:46 +00:00
Frappe PR Bot
11b16569e5 chore(release): Bumped to Version 13.52.15
## [13.52.15](https://github.com/frappe/erpnext/compare/v13.52.14...v13.52.15) (2023-09-12)

### Bug Fixes

* prorate factor for subscription plan ([#36953](https://github.com/frappe/erpnext/issues/36953)) ([9a15ed8](9a15ed8083))
2023-09-12 12:21:41 +00:00
Deepesh Garg
cd99630457 Merge pull request #37044 from frappe/version-13-hotfix
chore: release v13
2023-09-12 17:50:08 +05:30
Frappe PR Bot
efab1e7361 chore(release): Bumped to Version 13.52.14
## [13.52.14](https://github.com/frappe/erpnext/compare/v13.52.13...v13.52.14) (2023-09-10)

### Bug Fixes

* prorate factor for subscription plan ([#36953](https://github.com/frappe/erpnext/issues/36953)) ([d7f09f8](d7f09f8795))
2023-09-10 18:30:02 +00:00
Deepesh Garg
63a6e7e35c Merge pull request #37018 from frappe/mergify/bp/version-13/pr-37009
fix: prorate factor for subscription plan (#36953)
2023-09-10 23:58:35 +05:30
Gursheen Kaur Anand
d7f09f8795 fix: prorate factor for subscription plan (#36953)
(cherry picked from commit fc79af5926)
(cherry picked from commit 9a15ed8083)
2023-09-10 17:58:33 +00:00
Deepesh Garg
437a294621 Merge pull request #37009 from frappe/mergify/bp/version-13-hotfix/pr-36953
fix: prorate factor for subscription plan (#36953)
2023-09-10 12:21:09 +05:30
Gursheen Kaur Anand
9a15ed8083 fix: prorate factor for subscription plan (#36953)
(cherry picked from commit fc79af5926)
2023-09-10 06:06:12 +00:00
mergify[bot]
3a49d4f9b3 chore: asset finance books validation (backport #36979) (#36982)
* chore: asset finance books validation (#36979)

(cherry picked from commit 0077659e93)

# Conflicts:
#	erpnext/assets/doctype/asset/asset.py

* chore: fix conflicts

* chore: fix tests

---------

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
2023-09-06 15:39:44 +05:30
Frappe PR Bot
8b2c6ed61a chore(release): Bumped to Version 13.52.13
## [13.52.13](https://github.com/frappe/erpnext/compare/v13.52.12...v13.52.13) (2023-09-06)

### Bug Fixes

* ask for asset related accounts only when needed (backport [#36960](https://github.com/frappe/erpnext/issues/36960)) ([#36972](https://github.com/frappe/erpnext/issues/36972)) ([3a71fa9](3a71fa9d96))
* update repay_from_salary, payment_account and payroll_payable_account fields ([#36949](https://github.com/frappe/erpnext/issues/36949)) ([6f40d0c](6f40d0cdf6))
2023-09-06 07:24:52 +00:00
Deepesh Garg
4f98e958a1 Merge pull request #36962 from frappe/version-13-hotfix
chore: release v13
2023-09-06 12:52:49 +05:30
mergify[bot]
3a71fa9d96 fix: ask for asset related accounts only when needed (backport #36960) (#36972)
fix: ask for asset related accounts only when needed (#36960)

* fix: only ask for asset_received_but_not_billed account when needed

* chore: remove unnecessary if condition

* fix: only ask for expenses_included_in_asset_valuation account when needed

(cherry picked from commit 174f95d699)

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
2023-09-05 18:14:49 +05:30
Anand Baburajan
6f40d0cdf6 fix: update repay_from_salary, payment_account and payroll_payable_account fields (#36949)
* fix: update repay_from_salary, payment_account and payroll_payable_account fields

* chore: formatting
2023-09-05 12:43:09 +05:30
Anand Baburajan
5bd6c27b05 chore: patch to correct asset values if je has workflow [v13] (#36916)
chore: patch to correct asset values if je has workflow
2023-09-02 18:32:23 +05:30
Frappe PR Bot
9876019c69 chore(release): Bumped to Version 13.52.12
## [13.52.12](https://github.com/frappe/erpnext/compare/v13.52.11...v13.52.12) (2023-08-22)

### Bug Fixes

* disallow mulitple SO with same PO No ([3b9ac9f](3b9ac9f46a))
* timeout error coming during reposting (backport [#36715](https://github.com/frappe/erpnext/issues/36715)) ([#36753](https://github.com/frappe/erpnext/issues/36753)) ([783bb93](783bb93913))
2023-08-22 16:59:52 +00:00
Deepesh Garg
f4fb878282 Merge pull request #36762 from frappe/version-13-hotfix
chore: release v13
2023-08-22 22:28:20 +05:30
mergify[bot]
783bb93913 fix: timeout error coming during reposting (backport #36715) (#36753)
fix: timeout error coming during reposting (#36715)

(cherry picked from commit 620b21fec5)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2023-08-22 13:22:48 +05:30
Frappe PR Bot
cfbd9af100 chore(release): Bumped to Version 13.52.11
## [13.52.11](https://github.com/frappe/erpnext/compare/v13.52.10...v13.52.11) (2023-08-17)

### Bug Fixes

* disallow mulitple SO with same PO No ([bdaae81](bdaae81171))
2023-08-17 10:12:01 +00:00
ruthra kumar
966c296872 Merge pull request #36680 from frappe/mergify/bp/version-13/pr-36590
fix: disallow mulitple SO with same Purchase Order No if not enabled in Settings (backport #36590)
2023-08-17 15:40:34 +05:30
ruthra kumar
0ff871e38e chore: resolve conflict 2023-08-17 09:11:47 +05:30
ruthra kumar
9df10dbc40 refactor(test): don't set po_no by default
(cherry picked from commit 64614cd915)

# Conflicts:
#	erpnext/stock/doctype/delivery_note/test_delivery_note.py
2023-08-17 03:33:56 +00:00
ruthra kumar
bdaae81171 fix: disallow mulitple SO with same PO No
(cherry picked from commit dbd3fdbb41)
2023-08-17 03:33:56 +00:00
ruthra kumar
066cf0e3bc Merge pull request #36671 from frappe/mergify/bp/version-13-hotfix/pr-36590
fix: disallow mulitple SO with same Purchase Order No if not enabled in Settings (backport #36590)
2023-08-16 16:21:33 +05:30
ruthra kumar
829298066f chore: resolve conflict 2023-08-16 15:02:04 +05:30
ruthra kumar
006da22d3f refactor(test): don't set po_no by default
(cherry picked from commit 64614cd915)

# Conflicts:
#	erpnext/stock/doctype/delivery_note/test_delivery_note.py
2023-08-16 09:12:18 +00:00
ruthra kumar
3b9ac9f46a fix: disallow mulitple SO with same PO No
(cherry picked from commit dbd3fdbb41)
2023-08-16 09:12:17 +00:00
Frappe PR Bot
8f977f40f0 chore(release): Bumped to Version 13.52.10
## [13.52.10](https://github.com/frappe/erpnext/compare/v13.52.9...v13.52.10) (2023-08-16)

### Bug Fixes

* Allow backdated repayment cancels for term loans ([c417365](c417365e03))
* payment allocation in invoice payment schedule ([#36440](https://github.com/frappe/erpnext/issues/36440)) ([e5b3860](e5b38607ce))

### Performance Improvements

* **invoice:** Faster return amount query (backport [#36556](https://github.com/frappe/erpnext/issues/36556)) ([#36558](https://github.com/frappe/erpnext/issues/36558)) ([a801bba](a801bba83e))
2023-08-16 02:46:48 +00:00
Deepesh Garg
3bc7c88133 Merge pull request #36657 from frappe/version-13-hotfix
chore: release v13
2023-08-16 08:15:05 +05:30
mergify[bot]
84b6f68108 chore: add validation for depreciation expense account in asset category (backport #36659) (#36662)
chore: add validation for depreciation expense account in asset category (#36659)

(cherry picked from commit e0c79d3b53)

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
2023-08-15 18:26:33 +05:30
mergify[bot]
e5b38607ce fix: payment allocation in invoice payment schedule (#36440)
fix: payment allocation in invoice payment schedule (#36440)
2023-08-13 13:21:04 +05:30
Deepesh Garg
b3c9d0d910 Merge pull request #36624 from frappe/mergify/bp/version-13-hotfix/pr-36614
fix: Allow backdated repayment cancels for term loans (#36614)
2023-08-13 13:20:02 +05:30
Deepesh Garg
c417365e03 fix: Allow backdated repayment cancels for term loans
(cherry picked from commit 1377cf4cf1)
2023-08-13 06:28:38 +00:00
mergify[bot]
b2a4175d43 chore: set default filter dates if missing (backport #36597) (#36599)
chore: set default filter dates if missing (#36597)

(cherry picked from commit 98e82e0d99)

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
2023-08-11 09:17:08 +00:00
mergify[bot]
a801bba83e perf(invoice): Faster return amount query (backport #36556) (#36558)
perf(invoice): Faster return amount query (#36556)

perf: Faster return amount query
(cherry picked from commit b0c79a0467)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2023-08-09 14:07:52 +05:30
Frappe PR Bot
3ccb511e25 chore(release): Bumped to Version 13.52.9
## [13.52.9](https://github.com/frappe/erpnext/compare/v13.52.8...v13.52.9) (2023-08-08)

### Bug Fixes

* Tax withholding against order via Payment Entry ([#36493](https://github.com/frappe/erpnext/issues/36493)) ([5dbca09](5dbca09899))
2023-08-08 18:37:46 +00:00
Deepesh Garg
8c51f2e5a1 Merge pull request #36545 from frappe/version-13-hotfix
chore: release v13
2023-08-09 00:05:16 +05:30
mergify[bot]
5dbca09899 fix: Tax withholding against order via Payment Entry (#36493)
* fix: Tax withholding against order via Payment Entry (#36493)

* fix: Tax withholding against order via Payment Entry

* test: Add test case

* fix: Nonetype exceptions

(cherry picked from commit 93767eb7fc)

# Conflicts:
#	erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py

* chore: resolve conflicts

---------

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-08-07 17:15:22 +05:30
mergify[bot]
9ec7bb9be3 chore: better cost center validation for assets (backport #36477) (#36478)
chore: better cost center validation for assets (#36477)

(cherry picked from commit 38a612c62e)

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
2023-08-03 17:19:05 +05:30
Frappe PR Bot
0281afcead chore(release): Bumped to Version 13.52.8
## [13.52.8](https://github.com/frappe/erpnext/compare/v13.52.7...v13.52.8) (2023-08-01)

### Bug Fixes

* added validation for unique serial numbers in pos invoice ([#36302](https://github.com/frappe/erpnext/issues/36302)) ([a165b37](a165b37fd7))
* only publish repost progress to doc subscriber (backport [#36400](https://github.com/frappe/erpnext/issues/36400)) ([#36403](https://github.com/frappe/erpnext/issues/36403)) ([e9df064](e9df06406f))
* removed "fetch_from" (backport [#36365](https://github.com/frappe/erpnext/issues/36365)) ([#36387](https://github.com/frappe/erpnext/issues/36387)) ([c574494](c574494ddd))

### Performance Improvements

* use `LEFT JOIN` instead of `NOT EXISTS` (backport [#36221](https://github.com/frappe/erpnext/issues/36221)) ([#36384](https://github.com/frappe/erpnext/issues/36384)) ([cdc86bd](cdc86bd76c))
2023-08-01 18:04:50 +00:00
Deepesh Garg
348e4616cb Merge pull request #36441 from frappe/version-13-hotfix
chore: release v13
2023-08-01 23:33:11 +05:30
RitvikSardana
a165b37fd7 fix: added validation for unique serial numbers in pos invoice (#36302)
* fix: added validation for unique serial numbers in pos invoice

* fix: updated title of validation

* fix: removed extra whitespace

* fix: added validation for duplicate batch numbers

---------

Co-authored-by: Ritvik Sardana <ritviksardana@Ritviks-MacBook-Air.local>
2023-07-31 23:28:10 +05:30
mergify[bot]
cdc86bd76c perf: use LEFT JOIN instead of NOT EXISTS (backport #36221) (#36384)
* perf: use `LEFT JOIN` instead of `NOT EXISTS`

(cherry picked from commit 58d867503b)

# Conflicts:
#	erpnext/manufacturing/doctype/bom_update_log/bom_updation_utils.py

* fix: long queue `process_boms_cost_level_wise`

(cherry picked from commit 148d466ae5)

* chore: `conflicts`

* fix: failing test case

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-07-31 17:39:31 +05:30
mergify[bot]
e9df06406f fix: only publish repost progress to doc subscriber (backport #36400) (#36403)
* fix: only publish repost progress to doc subscriber (#36400)

Huge size of string gets blasted to everyone on site. Due to some memory
leak (cause unknown) till sockets are open the strings are also in
process' memory.

related https://github.com/frappe/frappe/issues/21863

(cherry picked from commit c0642cf528)

# Conflicts:
#	erpnext/stock/stock_ledger.py

* chore: conflicts

---------

Co-authored-by: Ankush Menat <ankush@frappe.io>
2023-07-29 18:14:26 +05:30
mergify[bot]
c574494ddd fix: removed "fetch_from" (backport #36365) (#36387)
fix: removed "fetch_from"

* fix: removed ("fetch_from": "goal.objective")
The field ended up being disabled because of this.

(cherry picked from commit 1c687a4afd)

Co-authored-by: xdlumertz <alexandrelumertz@gmail.com>
2023-07-28 18:15:24 +05:30
Frappe PR Bot
46966f4b7c chore(release): Bumped to Version 13.52.7
## [13.52.7](https://github.com/frappe/erpnext/compare/v13.52.6...v13.52.7) (2023-07-26)

### Bug Fixes

* allow both custodian and location while creating asset (backport [#36263](https://github.com/frappe/erpnext/issues/36263)) ([#36270](https://github.com/frappe/erpnext/issues/36270)) ([e4f28e8](e4f28e8a5b))
* apply discount on item after applying price list ([#36125](https://github.com/frappe/erpnext/issues/36125)) ([bde9e89](bde9e89582))
* group by in fixed asset register (copy [#36310](https://github.com/frappe/erpnext/issues/36310)) ([#36312](https://github.com/frappe/erpnext/issues/36312)) ([a5e1c47](a5e1c4798f))
2023-07-26 04:10:14 +00:00
Deepesh Garg
ded3b62c5a Merge pull request #36295 from frappe/version-13-hotfix
chore: release v13
2023-07-26 09:38:32 +05:30
mergify[bot]
a5e1c4798f fix: group by in fixed asset register (copy #36310) (#36312)
fix: group by in fixed asset register

(cherry picked from commit 1151e47f46)

Co-authored-by: anandbaburajan <anandbaburajan@gmail.com>
2023-07-25 21:01:47 +05:30
Anand Baburajan
bde9e89582 fix: apply discount on item after applying price list (#36125) 2023-07-25 20:32:57 +05:30
mergify[bot]
e4f28e8a5b fix: allow both custodian and location while creating asset (backport #36263) (#36270)
fix: allow both custodian and location while creating asset (#36263)

(cherry picked from commit 2b47f5815e)

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
2023-07-24 16:25:23 +05:30
Frappe PR Bot
128ea0d7fc chore(release): Bumped to Version 13.52.6
## [13.52.6](https://github.com/frappe/erpnext/compare/v13.52.5...v13.52.6) (2023-07-18)

### Bug Fixes

* allow manual asset receipt mov from nowhere (backport [#36093](https://github.com/frappe/erpnext/issues/36093)) ([#36095](https://github.com/frappe/erpnext/issues/36095)) ([2993eb5](2993eb5ce9))
* HSN code not showing up in GST itemised register reports ([#36142](https://github.com/frappe/erpnext/issues/36142)) ([1d3917b](1d3917b335))
* improve "Update Items" modal ([#36105](https://github.com/frappe/erpnext/issues/36105)) ([3bc899f](3bc899f354))

### Performance Improvements

* index in `Item` and `Item Variant Attribute` (backport [#36133](https://github.com/frappe/erpnext/issues/36133)) ([#36145](https://github.com/frappe/erpnext/issues/36145)) ([120de24](120de249dd))
2023-07-18 12:21:14 +00:00
Deepesh Garg
9566f4101d Merge pull request #36178 from frappe/version-13-hotfix
chore: release v13
2023-07-18 17:49:47 +05:30
Sagar Vora
4854c2e7f7 Merge pull request #36165 from frappe/mergify/bp/version-13-hotfix/pr-36163 2023-07-17 15:47:54 +05:30
Sagar Vora
a93c4b6c65 chore: use consistent quotes
(cherry picked from commit bccb718cc2)
2023-07-17 10:17:01 +00:00
mergify[bot]
287c67f7a2 chore: add validation for account type of party type and account (#36141)
chore: add validation for account type of party type and account (#36141)

chore: add validation to check if account type of party type and account match
(cherry picked from commit 305c37917f)

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
2023-07-17 11:51:45 +05:30
mergify[bot]
120de249dd perf: index in Item and Item Variant Attribute (backport #36133) (#36145)
* perf: index `variant_of` and `attribute` in `Item Variant Attribute`

(cherry picked from commit e4128a5c91)

* perf: index `disabled` in `Item`

(cherry picked from commit 04400eb2e4)

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

* chore: `conflicts`

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-07-15 10:20:32 +05:30
Frappe PR Bot
cefa78b864 chore(release): Bumped to Version 13.52.5
## [13.52.5](https://github.com/frappe/erpnext/compare/v13.52.4...v13.52.5) (2023-07-14)

### Bug Fixes

* HSN code not showing up in GST itemised register reports (backport [#36142](https://github.com/frappe/erpnext/issues/36142)) ([#36143](https://github.com/frappe/erpnext/issues/36143)) ([73366de](73366de12f))
2023-07-14 14:49:30 +00:00
mergify[bot]
73366de12f fix: HSN code not showing up in GST itemised register reports (backport #36142) (#36143)
fix: HSN code not showing up in GST itemised register reports (#36142)

fix: hsn code not showing up in gst itemised registers
(cherry picked from commit 1d3917b335)

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
2023-07-14 20:18:07 +05:30
Anand Baburajan
1d3917b335 fix: HSN code not showing up in GST itemised register reports (#36142)
fix: hsn code not showing up in gst itemised registers
2023-07-14 20:16:49 +05:30
mergify[bot]
3bc899f354 fix: improve "Update Items" modal (#36105)
fix: improve "Update Items" modal (#36105)

* fix: make "Update Items" modal larger

* fix: remove conversion factor from overview

Conversion factor doesn't make much sense without two different UOMs
next to it, hence moving it to row detail view

(cherry picked from commit d5fe1432f8)

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2023-07-14 13:03:43 +05:30
Ankush Menat
6b113c6abc ci: fix repo name in relase notes workflow
[skip ci]

(cherry picked from commit b5f6a1cc20)
2023-07-14 12:05:21 +05:30
mergify[bot]
82e2ff8731 ci: regen release notes with GH API (backport #36098) (#36100)
ci: regen release notes with GH API (#36098)

[skip ci]

(cherry picked from commit 0340bfc90d)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2023-07-12 12:18:34 +05:30
mergify[bot]
2993eb5ce9 fix: allow manual asset receipt mov from nowhere (backport #36093) (#36095)
* fix: allow manual asset receipt mov from nowhere (#36093)

(cherry picked from commit 4aaa1a15d7)

# Conflicts:
#	erpnext/assets/doctype/asset_movement/asset_movement.py

* chore: fix conflict

---------

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
2023-07-12 09:39:47 +05:30
Frappe PR Bot
12e27e96a8 chore(release): Bumped to Version 13.52.4
## [13.52.4](https://github.com/frappe/erpnext/compare/v13.52.3...v13.52.4) (2023-07-11)

### Bug Fixes

* also check on_hold ([#35910](https://github.com/frappe/erpnext/issues/35910)) ([5aa02b8](5aa02b8571))
* circular dependency during reposting causing timeout error ([ba69be1](ba69be1ced))
* **Employee Advance:** check if return amount is set before validating ([#36080](https://github.com/frappe/erpnext/issues/36080)) ([beaf13e](beaf13e00e))
* error messages while evaluating formulas and handle line boundaries ([#35989](https://github.com/frappe/erpnext/issues/35989)) ([4af57a7](4af57a7318))
* gst_hsn_code is ambiguous on gst reports ([3a00052](3a00052b49))
* incorrect status in MR created from PP (backport [#36085](https://github.com/frappe/erpnext/issues/36085)) ([#36087](https://github.com/frappe/erpnext/issues/36087)) ([e05bb10](e05bb103f3))
* payment entry `voucher_type` error ([#35779](https://github.com/frappe/erpnext/issues/35779)) ([9c3ec41](9c3ec41803))
* **Payment Entry:** compare rounded amount ([#36011](https://github.com/frappe/erpnext/issues/36011)) ([b04c190](b04c190e33))
* precision causing outstanding issue on partly paid invoices ([#36030](https://github.com/frappe/erpnext/issues/36030)) ([cf3ec93](cf3ec935a7))
* single column indexes ([#32425](https://github.com/frappe/erpnext/issues/32425)) ([53f7764](53f7764c67))

### Reverts

* Revert "perf: improve item wise register reports (backport #35908) (#35912)" ([b992366](b992366246)), closes [#35908](https://github.com/frappe/erpnext/issues/35908) [#35912](https://github.com/frappe/erpnext/issues/35912)
2023-07-11 14:12:25 +00:00
Deepesh Garg
30c9b15cdd Merge pull request #36084 from frappe/version-13-hotfix
chore: release v13
2023-07-11 19:41:00 +05:30
rohitwaghchaure
1d6bc68d87 Merge pull request #36090 from frappe/mergify/bp/version-13-hotfix/pr-36088
fix: circular dependency during reposting causing timeout error (backport #36088)
2023-07-11 19:09:36 +05:30
Rohit Waghchaure
ba69be1ced fix: circular dependency during reposting causing timeout error
(cherry picked from commit c16a5814d4)
2023-07-11 12:59:59 +00:00
Saurabh
4af57a7318 fix: error messages while evaluating formulas and handle line boundaries (#35989)
Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2023-07-11 18:04:31 +05:30
mergify[bot]
e05bb103f3 fix: incorrect status in MR created from PP (backport #36085) (#36087)
fix: incorrect status in MR created from PP (#36085)

(cherry picked from commit be5881280f)

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-07-11 17:44:41 +05:30
Deepesh Garg
6f2fb89c49 Merge branch 'version-13' into version-13-hotfix 2023-07-11 16:17:56 +05:30
Rucha Mahabal
beaf13e00e fix(Employee Advance): check if return amount is set before validating (#36080) 2023-07-11 14:12:21 +05:30
mergify[bot]
9c3ec41803 fix: payment entry voucher_type error (#35779)
* fix: payment entry `voucher_type` error (#35779)

* fix: payment entry `voucher_type` error

* chore: linters

(cherry picked from commit 361a357088)

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

* chore: resolve conflicts

---------

Co-authored-by: Dany Robert <danyrt@wahni.com>
Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-07-11 10:19:55 +05:30
Anand Baburajan
06c85fa252 Revert "fix: gst_hsn_code is ambiguous on gst reports" (#36072) 2023-07-10 23:45:11 +05:30
mergify[bot]
0248fee533 refactor(Payment Entry): translatable strings (#36017)
* refactor(Payment Entry): translatable strings (#36017)

* refactor(Payment Entry): translatable strings

* fix: German translations

(cherry picked from commit af28f95c60)

# Conflicts:
#	erpnext/accounts/doctype/payment_entry/payment_entry.py
#	erpnext/translations/de.csv

* chore: resolve conflicts

---------

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2023-07-10 21:02:05 +05:30
RJPvT
5aa02b8571 fix: also check on_hold (#35910) 2023-07-10 19:43:12 +05:30
mergify[bot]
53f7764c67 fix: single column indexes (#32425)
fix: single column indexes (#32425)

refactor: move single column indexes to doctypes
(cherry picked from commit 8d1db0ea3d)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2023-07-10 18:12:55 +05:30
mergify[bot]
cf3ec935a7 fix: precision causing outstanding issue on partly paid invoices (#36030)
fix: precision causing outstanding issue on partly paid invoices (#36030)

* fix: precision causing outstanding issue on partly paid invoices

* chore: linters

(cherry picked from commit 5c820ecc20)

Co-authored-by: Dany Robert <danyrt@wahni.com>
2023-07-10 16:49:41 +05:30
mergify[bot]
2732276498 chore: add asset depr posting error in error log (backport #36052) (#36056)
chore: add asset depr posting error in error log (#36052)

(cherry picked from commit 0f9a6ee70a)

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
2023-07-10 14:12:43 +05:30
Frappe PR Bot
77eb11a6e3 chore(release): Bumped to Version 13.52.3
## [13.52.3](https://github.com/frappe/erpnext/compare/v13.52.2...v13.52.3) (2023-07-09)

### Reverts

* Revert "perf: improve item wise register reports (backport #35908) (#35912)" ([0feb393](0feb393fff)), closes [#35908](https://github.com/frappe/erpnext/issues/35908) [#35912](https://github.com/frappe/erpnext/issues/35912)
2023-07-09 07:53:45 +00:00
Deepesh Garg
5cfe4195ba Merge pull request #36035 from frappe/mergify/bp/version-13/pr-36034
Revert "perf: improve item wise register reports (backport #35908)" (backport #36034)
2023-07-09 13:22:03 +05:30
Deepesh Garg
0feb393fff Revert "perf: improve item wise register reports (backport #35908) (#35912)"
This reverts commit 41344593c9.

(cherry picked from commit b992366246)
2023-07-09 06:15:33 +00:00
Deepesh Garg
4b20f2a083 Merge pull request #36034 from frappe/revert-35912-mergify/bp/version-13-hotfix/pr-35908
Revert "perf: improve item wise register reports (backport #35908)"
2023-07-09 11:44:45 +05:30
Deepesh Garg
b992366246 Revert "perf: improve item wise register reports (backport #35908) (#35912)"
This reverts commit 41344593c9.
2023-07-09 11:42:23 +05:30
ruthra kumar
93acde7748 Merge pull request #36028 from ruthra-kumar/fix_ambiguous_gst_hsn_code
fix: gst_hsn_code is ambiguous on gst reports
2023-07-08 17:51:17 +05:30
ruthra kumar
3a00052b49 fix: gst_hsn_code is ambiguous on gst reports 2023-07-08 06:49:39 +05:30
mergify[bot]
b04c190e33 fix(Payment Entry): compare rounded amount (#36011)
fix(Payment Entry): compare rounded amount (#36011)

(cherry picked from commit 4badac8e9e)

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2023-07-05 21:38:11 +05:30
90 changed files with 2299 additions and 1127 deletions

38
.github/workflows/release_notes.yml vendored Normal file
View File

@@ -0,0 +1,38 @@
# This action:
#
# 1. Generates release notes using github API.
# 2. Strips unnecessary info like chore/style etc from notes.
# 3. Updates release info.
# This action needs to be maintained on all branches that do releases.
name: 'Release Notes'
on:
workflow_dispatch:
inputs:
tag_name:
description: 'Tag of release like v13.0.0'
required: true
type: string
release:
types: [released]
permissions:
contents: read
jobs:
regen-notes:
name: 'Regenerate release notes'
runs-on: ubuntu-latest
steps:
- name: Update notes
run: |
NEW_NOTES=$(gh api --method POST -H "Accept: application/vnd.github+json" /repos/frappe/erpnext/releases/generate-notes -f tag_name=$RELEASE_TAG | jq -r '.body' | sed -E '/^\* (chore|ci|test|docs|style)/d' )
RELEASE_ID=$(gh api -H "Accept: application/vnd.github+json" /repos/frappe/erpnext/releases/tags/$RELEASE_TAG | jq -r '.id')
gh api --method PATCH -H "Accept: application/vnd.github+json" /repos/frappe/erpnext/releases/$RELEASE_ID -f body="$NEW_NOTES"
env:
GH_TOKEN: ${{ secrets.RELEASE_TOKEN }}
RELEASE_TAG: ${{ github.event.inputs.tag_name || github.event.release.tag_name }}

View File

@@ -4,7 +4,7 @@ import frappe
from erpnext.hooks import regional_overrides
__version__ = "13.52.2"
__version__ = "13.54.10"
def get_default_company(user=None):

View File

@@ -4,6 +4,12 @@
frappe.ui.form.on('Accounts Settings', {
refresh: function(frm) {
},
validate_access_key(frm) {
frappe.call({
doc: frm.doc,
method: "validate_access_key"
});
}
});

View File

@@ -46,6 +46,10 @@
"currency_exchange_section",
"allow_stale",
"stale_days",
"service_provider",
"column_break_eiyok",
"access_key",
"validate_access_key",
"report_settings_sb",
"use_custom_cash_flow"
],
@@ -282,32 +286,56 @@
"label": "Enable Common Party Accounting"
},
{
"default": "0",
"description": "Enabling this will allow creation of multi-currency invoices against single party account in company currency",
"fieldname": "allow_multi_currency_invoices_against_single_party_account",
"fieldtype": "Check",
"label": "Allow multi-currency invoices against single party account"
},
{
"default": "0",
"description": "Split Early Payment Discount Loss into Income and Tax Loss",
"fieldname": "book_tax_discount_loss",
"fieldtype": "Check",
"label": "Book Tax Loss on Early Payment Discount"
},
{
"default": "0",
"fieldname": "show_taxes_as_table_in_print",
"fieldtype": "Check",
"label": "Show Taxes as Table in Print"
}
"default": "0",
"description": "Enabling this will allow creation of multi-currency invoices against single party account in company currency",
"fieldname": "allow_multi_currency_invoices_against_single_party_account",
"fieldtype": "Check",
"label": "Allow multi-currency invoices against single party account"
},
{
"default": "0",
"description": "Split Early Payment Discount Loss into Income and Tax Loss",
"fieldname": "book_tax_discount_loss",
"fieldtype": "Check",
"label": "Book Tax Loss on Early Payment Discount"
},
{
"default": "0",
"fieldname": "show_taxes_as_table_in_print",
"fieldtype": "Check",
"label": "Show Taxes as Table in Print"
},
{
"default": "frankfurter.app",
"fieldname": "service_provider",
"fieldtype": "Select",
"label": "Service Provider",
"options": "frankfurter.app\nexchangerate.host"
},
{
"depends_on": "eval:doc.service_provider == \"exchangerate.host\"",
"description": "Access Key is mandatory for exchangerate.host",
"fieldname": "access_key",
"fieldtype": "Data",
"label": "Access Key"
},
{
"depends_on": "eval:doc.service_provider == \"exchangerate.host\"",
"fieldname": "validate_access_key",
"fieldtype": "Button",
"label": "Validate Access Key"
},
{
"fieldname": "column_break_eiyok",
"fieldtype": "Column Break"
}
],
"icon": "icon-cog",
"idx": 1,
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2023-06-13 18:47:46.430291",
"modified": "2023-10-07 14:20:01.779208",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Settings",

View File

@@ -8,12 +8,43 @@ import frappe
from frappe import _
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
from frappe.model.document import Document
from frappe.utils import cint
from frappe.utils import cint, nowdate
from erpnext.stock.utils import check_pending_reposting
class AccountsSettings(Document):
@frappe.whitelist()
def validate_access_key(self):
if self.service_provider == "exchangerate.host":
if not self.access_key:
frappe.throw(_("Access Key is required for exchangerate.host"))
else:
import requests
# Validate access key
api_url = "https://api.exchangerate.host/convert"
response = requests.get(
api_url,
params={
"access_key": self.access_key,
"transaction_date": nowdate(),
"amount": 1,
"from": "USD",
"to": "INR",
},
)
# exchangerate.host return 200 for all requests. Can't rely on it to raise exception
if not response.json()["success"]:
frappe.throw(
title=_("Service Provider Error"),
msg=_("Currency exchange rate serivce provider: {0} returned Error. {1}").format(
frappe.bold(self.service_provider), response.json()
),
exc=frappe.ValidationError,
)
frappe.msgprint(msg=_("Success"), title=_("Access Key Validation"))
def on_update(self):
frappe.clear_cache()

View File

@@ -400,6 +400,15 @@ class JournalEntry(AccountsController):
d.idx, d.account
)
)
elif (
d.party_type
and frappe.db.get_value("Party Type", d.party_type, "account_type") != account_type
):
frappe.throw(
_("Row {0}: Account {1} and Party Type {2} have different account types").format(
d.idx, d.account, d.party_type
)
)
def check_credit_limit(self):
customers = list(

View File

@@ -7,7 +7,16 @@ from functools import reduce
import frappe
from frappe import ValidationError, _, scrub, throw
from frappe.utils import cint, comma_or, flt, get_link_to_form, getdate, nowdate
from frappe.utils import (
cint,
comma_and,
comma_or,
flt,
fmt_money,
get_link_to_form,
getdate,
nowdate,
)
from six import iteritems, string_types
import erpnext
@@ -191,17 +200,17 @@ class PaymentEntry(AccountsController):
# The reference has already been fully paid
if not latest:
frappe.throw(
_("{0} {1} has already been fully paid.").format(d.reference_doctype, d.reference_name)
_("{0} {1} has already been fully paid.").format(_(d.reference_doctype), d.reference_name)
)
# The reference has already been partly paid
elif (
latest.outstanding_amount < latest.invoice_amount
and flt(d.outstanding_amount, d.precision("outstanding_amount")) != latest.outstanding_amount
):
elif latest.outstanding_amount < latest.invoice_amount and flt(
d.outstanding_amount, d.precision("outstanding_amount")
) != flt(latest.outstanding_amount, d.precision("outstanding_amount")):
frappe.throw(
_(
"{0} {1} has already been partly paid. Please use the 'Get Outstanding Invoice' or the 'Get Outstanding Orders' button to get the latest outstanding amounts."
).format(d.reference_doctype, d.reference_name)
).format(_(d.reference_doctype), d.reference_name)
)
fail_message = _("Row #{0}: Allocated Amount cannot be greater than outstanding amount.")
@@ -319,7 +328,7 @@ class PaymentEntry(AccountsController):
def validate_party_details(self):
if self.party:
if not frappe.db.exists(self.party_type, self.party):
frappe.throw(_("Invalid {0}: {1}").format(self.party_type, self.party))
frappe.throw(_("{0} {1} does not exist").format(_(self.party_type), self.party))
def set_exchange_rate(self, ref_doc=None):
self.set_source_exchange_rate(ref_doc)
@@ -376,7 +385,9 @@ class PaymentEntry(AccountsController):
continue
if d.reference_doctype not in valid_reference_doctypes:
frappe.throw(
_("Reference Doctype must be one of {0}").format(comma_or(valid_reference_doctypes))
_("Reference Doctype must be one of {0}").format(
comma_or((_(d) for d in valid_reference_doctypes))
)
)
elif d.reference_name:
@@ -389,7 +400,7 @@ class PaymentEntry(AccountsController):
if self.party != ref_doc.get(scrub(self.party_type)):
frappe.throw(
_("{0} {1} is not associated with {2} {3}").format(
d.reference_doctype, d.reference_name, self.party_type, self.party
_(d.reference_doctype), d.reference_name, _(self.party_type), self.party
)
)
else:
@@ -410,18 +421,18 @@ class PaymentEntry(AccountsController):
if ref_party_account != self.party_account:
frappe.throw(
_("{0} {1} is associated with {2}, but Party Account is {3}").format(
d.reference_doctype, d.reference_name, ref_party_account, self.party_account
_(d.reference_doctype), d.reference_name, ref_party_account, self.party_account
)
)
if ref_doc.doctype == "Purchase Invoice" and ref_doc.get("on_hold"):
frappe.throw(
_("{0} {1} is on hold").format(d.reference_doctype, d.reference_name),
title=_("Invalid Invoice"),
_("{0} {1} is on hold").format(_(d.reference_doctype), d.reference_name),
title=_("Invalid Purchase Invoice"),
)
if ref_doc.docstatus != 1:
frappe.throw(_("{0} {1} must be submitted").format(d.reference_doctype, d.reference_name))
frappe.throw(_("{0} {1} must be submitted").format(_(d.reference_doctype), d.reference_name))
def validate_paid_invoices(self):
no_oustanding_refs = {}
@@ -437,14 +448,13 @@ class PaymentEntry(AccountsController):
if outstanding_amount <= 0 and not is_return:
no_oustanding_refs.setdefault(d.reference_doctype, []).append(d)
for k, v in no_oustanding_refs.items():
for reference_doctype, references in no_oustanding_refs.items():
frappe.msgprint(
_(
"{} - {} now has {} as it had no outstanding amount left before submitting the Payment Entry."
"References {0} of type {1} had no outstanding amount left before submitting the Payment Entry. Now they have a negative outstanding amount."
).format(
_(k),
frappe.bold(", ".join(d.reference_name for d in v)),
frappe.bold(_("negative outstanding amount")),
frappe.bold(comma_and((d.reference_name for d in references))),
_(reference_doctype),
)
+ "<br><br>"
+ _("If this is undesirable please cancel the corresponding Payment Entry."),
@@ -479,7 +489,7 @@ class PaymentEntry(AccountsController):
if not valid:
frappe.throw(
_("Against Journal Entry {0} does not have any unmatched {1} entry").format(
d.reference_name, dr_or_cr
d.reference_name, _(dr_or_cr)
)
)
@@ -546,7 +556,7 @@ class PaymentEntry(AccountsController):
if allocated_amount > outstanding:
frappe.throw(
_("Row #{0}: Cannot allocate more than {1} against payment term {2}").format(
idx, outstanding, key[0]
idx, fmt_money(outstanding), key[0]
)
)
@@ -606,7 +616,9 @@ class PaymentEntry(AccountsController):
if not self.apply_tax_withholding_amount:
return
net_total = self.paid_amount
order_amount = self.get_order_net_total()
net_total = flt(order_amount) + flt(self.unallocated_amount)
# Adding args as purchase invoice to get TDS amount
args = frappe._dict(
@@ -651,6 +663,20 @@ class PaymentEntry(AccountsController):
for d in to_remove:
self.remove(d)
def get_order_net_total(self):
if self.party_type == "Supplier":
doctype = "Purchase Order"
else:
doctype = "Sales Order"
docnames = [d.reference_name for d in self.references if d.reference_doctype == doctype]
tax_withholding_net_total = frappe.db.get_value(
doctype, {"name": ["in", docnames]}, ["sum(base_net_total)"]
)
return tax_withholding_net_total
def apply_taxes(self):
self.initialize_taxes()
self.determine_exclusive_rate()
@@ -823,7 +849,7 @@ class PaymentEntry(AccountsController):
_("Cannot {0} {1} {2} without any negative outstanding invoice").format(
_(self.payment_type),
(_("to") if self.party_type == "Customer" else _("from")),
self.party_type,
_(self.party_type),
),
InvalidPaymentEntry,
)
@@ -831,7 +857,7 @@ class PaymentEntry(AccountsController):
elif paid_amount - additional_charges > total_negative_outstanding:
frappe.throw(
_("Paid Amount cannot be greater than total negative outstanding amount {0}").format(
total_negative_outstanding
fmt_money(total_negative_outstanding)
),
InvalidPaymentEntry,
)
@@ -1327,6 +1353,9 @@ def get_outstanding_reference_documents(args):
if args.get("party_type") == "Member":
return
if not args.get("get_outstanding_invoices") and not args.get("get_orders_to_be_billed"):
args["get_outstanding_invoices"] = True
# confirm that Supplier is not blocked
if args.get("party_type") == "Supplier":
supplier_status = get_supplier_block_status(args["party"])
@@ -1433,7 +1462,7 @@ def get_outstanding_reference_documents(args):
_(
"No outstanding {0} found for the {1} {2} which qualify the filters you have specified."
).format(
ref_document_type, _(args.get("party_type")).lower(), frappe.bold(args.get("party"))
_(ref_document_type), _(args.get("party_type")).lower(), frappe.bold(args.get("party"))
)
)
@@ -1508,60 +1537,59 @@ def get_orders_to_be_billed(
cost_center=None,
filters=None,
):
voucher_type = None
if party_type == "Customer":
voucher_type = "Sales Order"
elif party_type == "Supplier":
voucher_type = "Purchase Order"
elif party_type == "Employee":
voucher_type = None
if not voucher_type:
return []
# Add cost center condition
if voucher_type:
doc = frappe.get_doc({"doctype": voucher_type})
condition = ""
if doc and hasattr(doc, "cost_center") and doc.cost_center:
condition = " and cost_center='%s'" % cost_center
doc = frappe.get_doc({"doctype": voucher_type})
condition = ""
if doc and hasattr(doc, "cost_center") and doc.cost_center:
condition = " and cost_center='%s'" % cost_center
orders = []
if voucher_type:
if party_account_currency == company_currency:
grand_total_field = "base_grand_total"
rounded_total_field = "base_rounded_total"
else:
grand_total_field = "grand_total"
rounded_total_field = "rounded_total"
if party_account_currency == company_currency:
grand_total_field = "base_grand_total"
rounded_total_field = "base_rounded_total"
else:
grand_total_field = "grand_total"
rounded_total_field = "rounded_total"
orders = frappe.db.sql(
"""
select
name as voucher_no,
if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) as invoice_amount,
(if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) - advance_paid) as outstanding_amount,
transaction_date as posting_date
from
`tab{voucher_type}`
where
{party_type} = %s
and docstatus = 1
and company = %s
and ifnull(status, "") != "Closed"
and if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) > advance_paid
and abs(100 - per_billed) > 0.01
{condition}
order by
transaction_date, name
""".format(
**{
"rounded_total_field": rounded_total_field,
"grand_total_field": grand_total_field,
"voucher_type": voucher_type,
"party_type": scrub(party_type),
"condition": condition,
}
),
(party, company),
as_dict=True,
)
orders = frappe.db.sql(
"""
select
name as voucher_no,
if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) as invoice_amount,
(if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) - advance_paid) as outstanding_amount,
transaction_date as posting_date
from
`tab{voucher_type}`
where
{party_type} = %s
and docstatus = 1
and company = %s
and ifnull(status, "") != "Closed"
and if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) > advance_paid
and abs(100 - per_billed) > 0.01
{condition}
order by
transaction_date, name
""".format(
**{
"rounded_total_field": rounded_total_field,
"grand_total_field": grand_total_field,
"voucher_type": voucher_type,
"party_type": scrub(party_type),
"condition": condition,
}
),
(party, company),
as_dict=True,
)
order_list = []
for d in orders:
@@ -1594,6 +1622,8 @@ def get_negative_outstanding_invoices(
cost_center=None,
condition=None,
):
if party_type not in ["Customer", "Supplier"]:
return []
voucher_type = "Sales Invoice" if party_type == "Customer" else "Purchase Invoice"
supplier_condition = ""
if voucher_type == "Purchase Invoice":
@@ -1642,7 +1672,7 @@ def get_negative_outstanding_invoices(
def get_party_details(company, party_type, party, date, cost_center=None):
bank_account = ""
if not frappe.db.exists(party_type, party):
frappe.throw(_("Invalid {0}: {1}").format(party_type, party))
frappe.throw(_("{0} {1} does not exist").format(_(party_type), party))
party_account = get_party_account(party_type, party, company)
@@ -1927,7 +1957,7 @@ def get_payment_entry(
if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) >= (
100.0 + over_billing_allowance
):
frappe.throw(_("Can only make payment against unbilled {0}").format(dt))
frappe.throw(_("Can only make payment against unbilled {0}").format(_(dt)))
party_type = set_party_type(dt)
party_account = set_party_account(dt, dn, doc, party_type)

View File

@@ -345,7 +345,8 @@
"no_copy": 1,
"options": "POS Invoice",
"print_hide": 1,
"read_only": 1
"read_only": 1,
"search_index": 1
},
{
"default": "0",
@@ -1572,7 +1573,7 @@
"icon": "fa fa-file-text",
"is_submittable": 1,
"links": [],
"modified": "2022-09-27 13:00:24.166684",
"modified": "2022-09-30 03:49:50.455199",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Invoice",

View File

@@ -1,6 +1,6 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
# For license information, please see license.txt
import collections
import frappe
from frappe import _
@@ -44,6 +44,7 @@ class POSInvoice(SalesInvoice):
self.validate_debit_to_acc()
self.validate_write_off_account()
self.validate_change_amount()
self.validate_duplicate_serial_and_batch_no()
self.validate_change_account()
self.validate_item_cost_centers()
self.validate_warehouse()
@@ -154,6 +155,27 @@ class POSInvoice(SalesInvoice):
title=_("Item Unavailable"),
)
def validate_duplicate_serial_and_batch_no(self):
serial_nos = []
batch_nos = []
for row in self.get("items"):
if row.serial_no:
serial_nos = row.serial_no.split("\n")
if row.batch_no and not row.serial_no:
batch_nos.append(row.batch_no)
if serial_nos:
for key, value in collections.Counter(serial_nos).items():
if value > 1:
frappe.throw(_("Duplicate Serial No {0} found").format("key"))
if batch_nos:
for key, value in collections.Counter(batch_nos).items():
if value > 1:
frappe.throw(_("Duplicate Batch No {0} found").format("key"))
def validate_pos_reserved_batch_qty(self, item):
filters = {"item_code": item.item_code, "warehouse": item.warehouse, "batch_no": item.batch_no}
@@ -752,7 +774,3 @@ def add_return_modes(doc, pos_profile):
]:
payment_mode = get_mode_of_payment_info(mode_of_payment, doc.company)
append_payment(payment_mode[0])
def on_doctype_update():
frappe.db.add_index("POS Invoice", ["return_against"])

View File

@@ -261,9 +261,7 @@ class PurchaseInvoice(BuyingController):
stock_not_billed_account = self.get_company_default("stock_received_but_not_billed")
stock_items = self.get_stock_items()
asset_items = [d.is_fixed_asset for d in self.items if d.is_fixed_asset]
if len(asset_items) > 0:
asset_received_but_not_billed = self.get_company_default("asset_received_but_not_billed")
asset_received_but_not_billed = None
if self.update_stock:
self.validate_item_code()
@@ -357,6 +355,8 @@ class PurchaseInvoice(BuyingController):
)
item.expense_account = asset_category_account
elif item.is_fixed_asset and item.pr_detail:
if not asset_received_but_not_billed:
asset_received_but_not_billed = self.get_company_default("asset_received_but_not_billed")
item.expense_account = asset_received_but_not_billed
elif not item.expense_account and for_validate:
throw(_("Expense account is mandatory for item {0}").format(item.item_code or item.item_name))
@@ -924,8 +924,9 @@ class PurchaseInvoice(BuyingController):
)
def get_asset_gl_entry(self, gl_entries):
arbnb_account = self.get_company_default("asset_received_but_not_billed")
eiiav_account = self.get_company_default("expenses_included_in_asset_valuation")
arbnb_account = None
eiiav_account = None
asset_eiiav_currency = None
for item in self.get("items"):
if item.is_fixed_asset:
@@ -937,6 +938,8 @@ class PurchaseInvoice(BuyingController):
"Asset Received But Not Billed",
"Fixed Asset",
]:
if not arbnb_account:
arbnb_account = self.get_company_default("asset_received_but_not_billed")
item.expense_account = arbnb_account
if not self.update_stock:
@@ -959,7 +962,10 @@ class PurchaseInvoice(BuyingController):
)
if item.item_tax_amount:
asset_eiiav_currency = get_account_currency(eiiav_account)
if not eiiav_account or not asset_eiiav_currency:
eiiav_account = self.get_company_default("expenses_included_in_asset_valuation")
asset_eiiav_currency = get_account_currency(eiiav_account)
gl_entries.append(
self.get_gl_dict(
{
@@ -1002,7 +1008,10 @@ class PurchaseInvoice(BuyingController):
)
if item.item_tax_amount and not cint(erpnext.is_perpetual_inventory_enabled(self.company)):
asset_eiiav_currency = get_account_currency(eiiav_account)
if not eiiav_account or not asset_eiiav_currency:
eiiav_account = self.get_company_default("expenses_included_in_asset_valuation")
asset_eiiav_currency = get_account_currency(eiiav_account)
gl_entries.append(
self.get_gl_dict(
{
@@ -1022,47 +1031,46 @@ class PurchaseInvoice(BuyingController):
)
)
# When update stock is checked
# Assets are bought through this document then it will be linked to this document
if self.update_stock:
if flt(item.landed_cost_voucher_amount):
gl_entries.append(
self.get_gl_dict(
{
"account": eiiav_account,
"against": cwip_account,
"cost_center": item.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"credit": flt(item.landed_cost_voucher_amount),
"project": item.project or self.project,
},
item=item,
)
)
if flt(item.landed_cost_voucher_amount):
if not eiiav_account:
eiiav_account = self.get_company_default("expenses_included_in_asset_valuation")
gl_entries.append(
self.get_gl_dict(
{
"account": cwip_account,
"against": eiiav_account,
"cost_center": item.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"debit": flt(item.landed_cost_voucher_amount),
"project": item.project or self.project,
},
item=item,
)
gl_entries.append(
self.get_gl_dict(
{
"account": eiiav_account,
"against": cwip_account,
"cost_center": item.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"credit": flt(item.landed_cost_voucher_amount),
"project": item.project or self.project,
},
item=item,
)
# update gross amount of assets bought through this document
assets = frappe.db.get_all(
"Asset", filters={"purchase_invoice": self.name, "item_code": item.item_code}
)
for asset in assets:
frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate))
frappe.db.set_value(
"Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate)
gl_entries.append(
self.get_gl_dict(
{
"account": cwip_account,
"against": eiiav_account,
"cost_center": item.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"debit": flt(item.landed_cost_voucher_amount),
"project": item.project or self.project,
},
item=item,
)
)
# update gross amount of assets bought through this document
assets = frappe.db.get_all(
"Asset", filters={"purchase_invoice": self.name, "item_code": item.item_code}
)
for asset in assets:
frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate))
frappe.db.set_value("Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate))
return gl_entries

View File

@@ -1580,6 +1580,76 @@ class TestPurchaseInvoice(unittest.TestCase, StockTestMixin):
self.assertTrue(return_pi.docstatus == 1)
def test_payment_allocation_for_payment_terms(self):
from erpnext.buying.doctype.purchase_order.test_purchase_order import (
create_pr_against_po,
create_purchase_order,
)
from erpnext.selling.doctype.sales_order.test_sales_order import (
automatically_fetch_payment_terms,
)
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import (
make_purchase_invoice as make_pi_from_pr,
)
automatically_fetch_payment_terms()
frappe.db.set_value(
"Payment Terms Template",
"_Test Payment Term Template",
"allocate_payment_based_on_payment_terms",
0,
)
po = create_purchase_order(do_not_save=1)
po.payment_terms_template = "_Test Payment Term Template"
po.save()
po.submit()
pr = create_pr_against_po(po.name, received_qty=4)
pi = make_pi_from_pr(pr.name)
self.assertEqual(pi.payment_schedule[0].payment_amount, 1000)
frappe.db.set_value(
"Payment Terms Template",
"_Test Payment Term Template",
"allocate_payment_based_on_payment_terms",
1,
)
pi = make_pi_from_pr(pr.name)
self.assertEqual(pi.payment_schedule[0].payment_amount, 2500)
automatically_fetch_payment_terms(enable=0)
frappe.db.set_value(
"Payment Terms Template",
"_Test Payment Term Template",
"allocate_payment_based_on_payment_terms",
0,
)
def test_default_cost_center_for_purchase(self):
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
for c_center in ["_Test Cost Center Selling", "_Test Cost Center Buying"]:
create_cost_center(cost_center_name=c_center)
item = create_item(
"_Test Cost Center Item For Purchase",
is_stock_item=1,
buying_cost_center="_Test Cost Center Buying - _TC",
selling_cost_center="_Test Cost Center Selling - _TC",
)
pi = make_purchase_invoice(
item=item.name, qty=1, rate=1000, update_stock=True, do_not_submit=True, cost_center=""
)
pi.items[0].cost_center = ""
pi.set_missing_values()
pi.calculate_taxes_and_totals()
pi.save()
self.assertEqual(pi.items[0].cost_center, "_Test Cost Center Buying - _TC")
def check_gl_entries(doc, voucher_no, expected_gle, posting_date):
gl_entries = frappe.db.sql(

View File

@@ -1580,15 +1580,13 @@ class SalesInvoice(SellingController):
frappe.db.set_value("Customer", self.customer, "loyalty_program_tier", lp_details.tier_name)
def get_returned_amount(self):
from frappe.query_builder.functions import Coalesce, Sum
from frappe.query_builder.functions import Sum
doc = frappe.qb.DocType(self.doctype)
returned_amount = (
frappe.qb.from_(doc)
.select(Sum(doc.grand_total))
.where(
(doc.docstatus == 1) & (doc.is_return == 1) & (Coalesce(doc.return_against, "") == self.name)
)
.where((doc.docstatus == 1) & (doc.is_return == 1) & (doc.return_against == self.name))
).run()
return abs(returned_amount[0][0]) if returned_amount[0][0] else 0

View File

@@ -1783,6 +1783,10 @@ class TestSalesInvoice(unittest.TestCase):
)
def test_outstanding_amount_after_advance_payment_entry_cancellation(self):
"""Test impact of advance PE submission/cancellation on SI and SO."""
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
sales_order = make_sales_order(item_code="138-CMS Shoe", qty=1, price_list_rate=500)
pe = frappe.get_doc(
{
"doctype": "Payment Entry",
@@ -1802,10 +1806,25 @@ class TestSalesInvoice(unittest.TestCase):
"paid_to": "_Test Cash - _TC",
}
)
pe.append(
"references",
{
"reference_doctype": "Sales Order",
"reference_name": sales_order.name,
"total_amount": sales_order.grand_total,
"outstanding_amount": sales_order.grand_total,
"allocated_amount": 300,
},
)
pe.insert()
pe.submit()
sales_order.reload()
self.assertEqual(sales_order.advance_paid, 300)
si = frappe.copy_doc(test_records[0])
si.items[0].sales_order = sales_order.name
si.items[0].so_detail = sales_order.get("items")[0].name
si.is_pos = 0
si.append(
"advances",
@@ -1813,6 +1832,7 @@ class TestSalesInvoice(unittest.TestCase):
"doctype": "Sales Invoice Advance",
"reference_type": "Payment Entry",
"reference_name": pe.name,
"reference_row": pe.references[0].name,
"advance_amount": 300,
"allocated_amount": 300,
"remarks": pe.remarks,
@@ -1821,7 +1841,13 @@ class TestSalesInvoice(unittest.TestCase):
si.insert()
si.submit()
si.load_from_db()
si.reload()
pe.reload()
sales_order.reload()
# Check if SO is unlinked/replaced by SI in PE & if SO advance paid is 0
self.assertEqual(pe.references[0].reference_name, si.name)
self.assertEqual(sales_order.advance_paid, 0.0)
# check outstanding after advance allocation
self.assertEqual(
@@ -1829,11 +1855,9 @@ class TestSalesInvoice(unittest.TestCase):
flt(si.rounded_total - si.total_advance, si.precision("outstanding_amount")),
)
# added to avoid Document has been modified exception
pe = frappe.get_doc("Payment Entry", pe.name)
pe.cancel()
si.reload()
si.load_from_db()
# check outstanding after advance cancellation
self.assertEqual(
flt(si.outstanding_amount),
@@ -2448,36 +2472,6 @@ class TestSalesInvoice(unittest.TestCase):
self.assertEqual(target_doc.company, "_Test Company 1")
self.assertEqual(target_doc.supplier, "_Test Internal Supplier")
def test_sle_for_target_warehouse(self):
se = make_stock_entry(
item_code="138-CMS Shoe",
target="Finished Goods - _TC",
company="_Test Company",
qty=1,
basic_rate=500,
)
si = frappe.copy_doc(test_records[0])
si.update_stock = 1
si.set_warehouse = "Finished Goods - _TC"
si.set_target_warehouse = "Stores - _TC"
si.get("items")[0].warehouse = "Finished Goods - _TC"
si.get("items")[0].target_warehouse = "Stores - _TC"
si.insert()
si.submit()
sles = frappe.get_all(
"Stock Ledger Entry", filters={"voucher_no": si.name}, fields=["name", "actual_qty"]
)
# check if both SLEs are created
self.assertEqual(len(sles), 2)
self.assertEqual(sum(d.actual_qty for d in sles), 0.0)
# tear down
si.cancel()
se.cancel()
def test_internal_transfer_gl_entry(self):
si = create_sales_invoice(
company="_Test Company with perpetual inventory",

View File

@@ -694,3 +694,23 @@ class TestSubscription(unittest.TestCase):
# Check the currency of the created invoice
currency = frappe.db.get_value("Sales Invoice", subscription.invoices[0].invoice, "currency")
self.assertEqual(currency, "USD")
def test_plan_rate_for_midmonth_start_date(self):
subscription = frappe.new_doc("Subscription")
subscription.party_type = "Supplier"
subscription.party = "_Test Supplier"
subscription.generate_invoice_at_period_start = 1
subscription.follow_calendar_months = 1
subscription.generate_new_invoices_past_due_date = 1
subscription.start_date = "2023-04-08"
subscription.end_date = "2024-02-27"
subscription.append("plans", {"plan": "_Test Plan Name 4", "qty": 1})
subscription.save()
subscription.process()
self.assertEqual(len(subscription.invoices), 1)
pi = frappe.get_doc("Purchase Invoice", subscription.invoices[0].invoice)
self.assertEqual(pi.total, 55333.33)
subscription.delete()

View File

@@ -56,18 +56,17 @@ def get_plan_rate(
prorate = frappe.db.get_single_value("Subscription Settings", "prorate")
if prorate:
prorate_factor = flt(
date_diff(start_date, get_first_day(start_date))
/ date_diff(get_last_day(start_date), get_first_day(start_date)),
1,
)
prorate_factor += flt(
date_diff(get_last_day(end_date), end_date)
/ date_diff(get_last_day(end_date), get_first_day(end_date)),
1,
)
cost -= plan.cost * prorate_factor
cost -= plan.cost * get_prorate_factor(start_date, end_date)
return cost
def get_prorate_factor(start_date, end_date):
total_days_to_skip = date_diff(start_date, get_first_day(start_date))
total_days_in_month = int(get_last_day(start_date).strftime("%d"))
prorate_factor = flt(total_days_to_skip / total_days_in_month)
total_days_to_skip = date_diff(get_last_day(end_date), end_date)
total_days_in_month = int(get_last_day(end_date).strftime("%d"))
prorate_factor += flt(total_days_to_skip / total_days_in_month)
return prorate_factor

View File

@@ -186,6 +186,42 @@ class TestTaxWithholdingCategory(unittest.TestCase):
for d in reversed(invoices):
d.cancel()
def test_tds_deduction_for_po_via_payment_entry(self):
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
frappe.db.set_value(
"Supplier", "Test TDS Supplier8", "tax_withholding_category", "Cumulative Threshold TDS"
)
order = create_purchase_order(supplier="Test TDS Supplier8", rate=40000, do_not_save=True)
# Add some tax on the order
order.append(
"taxes",
{
"category": "Total",
"charge_type": "Actual",
"account_head": "_Test Account VAT - _TC",
"cost_center": "Main - _TC",
"tax_amount": 8000,
"description": "Test",
"add_deduct_tax": "Add",
},
)
order.save()
order.apply_tds = 1
order.tax_withholding_category = "Cumulative Threshold TDS"
order.submit()
self.assertEqual(order.taxes[0].tax_amount, 4000)
payment = get_payment_entry(order.doctype, order.name)
payment.apply_tax_withholding_amount = 1
payment.tax_withholding_category = "Cumulative Threshold TDS"
payment.submit()
self.assertEqual(payment.taxes[0].tax_amount, 4000)
def test_multi_category_single_supplier(self):
frappe.db.set_value(
"Supplier", "Test TDS Supplier5", "tax_withholding_category", "Test Service Category"
@@ -275,6 +311,37 @@ def cancel_invoices():
frappe.get_doc("Sales Invoice", d).cancel()
def create_purchase_order(**args):
# return purchase order doc object
item = frappe.db.get_value("Item", {"item_name": "TDS Item"}, "name")
args = frappe._dict(args)
po = frappe.get_doc(
{
"doctype": "Purchase Order",
"transaction_date": today(),
"schedule_date": today(),
"apply_tds": 0 if args.do_not_apply_tds else 1,
"supplier": args.supplier,
"company": "_Test Company",
"taxes_and_charges": "",
"currency": "INR",
"taxes": [],
"items": [
{
"doctype": "Purchase Order Item",
"item_code": item,
"qty": args.qty or 1,
"rate": args.rate or 10000,
"cost_center": "Main - _TC",
"expense_account": "Stock Received But Not Billed - _TC",
}
],
}
)
po.save()
return po
def create_purchase_invoice(**args):
# return sales invoice doc object
item = frappe.db.get_value("Item", {"item_name": "TDS Item"}, "name")
@@ -351,6 +418,8 @@ def create_records():
"Test TDS Supplier4",
"Test TDS Supplier5",
"Test TDS Supplier6",
"Test TDS Supplier7",
"Test TDS Supplier8",
]:
if frappe.db.exists("Supplier", name):
continue

View File

@@ -4,12 +4,7 @@
import frappe
from frappe import _, msgprint, scrub
from frappe.contacts.doctype.address.address import (
get_address_display,
get_company_address,
get_default_address,
)
from frappe.contacts.doctype.contact.contact import get_contact_details
from frappe.contacts.doctype.address.address import get_company_address, get_default_address
from frappe.core.doctype.user_permission.user_permission import get_permitted_documents
from frappe.model.utils import get_fetch_values
from frappe.utils import (
@@ -120,6 +115,7 @@ def _get_party_details(
party_address,
company_address,
shipping_address,
ignore_permissions=ignore_permissions,
)
set_contact_details(party_details, party, party_type)
set_other_values(party_details, party, party_type)
@@ -183,6 +179,8 @@ def set_address_details(
party_address=None,
company_address=None,
shipping_address=None,
*,
ignore_permissions=False
):
billing_address_field = (
"customer_address" if party_type == "Lead" else party_type.lower() + "_address"
@@ -195,13 +193,17 @@ def set_address_details(
get_fetch_values(doctype, billing_address_field, party_details[billing_address_field])
)
# address display
party_details.address_display = get_address_display(party_details[billing_address_field])
party_details.address_display = render_address(
party_details[billing_address_field], check_permissions=not ignore_permissions
)
# shipping address
if party_type in ["Customer", "Lead"]:
party_details.shipping_address_name = shipping_address or get_party_shipping_address(
party_type, party.name
)
party_details.shipping_address = get_address_display(party_details["shipping_address_name"])
party_details.shipping_address = render_address(
party_details["shipping_address_name"], check_permissions=not ignore_permissions
)
if doctype:
party_details.update(
get_fetch_values(doctype, "shipping_address_name", party_details.shipping_address_name)
@@ -224,7 +226,7 @@ def set_address_details(
party_details.update(
{
"shipping_address": shipping_address,
"shipping_address_display": get_address_display(shipping_address),
"shipping_address_display": render_address(shipping_address),
**get_fetch_values(doctype, "shipping_address", shipping_address),
}
)
@@ -235,7 +237,8 @@ def set_address_details(
{
"billing_address": party_details.company_address,
"billing_address_display": (
party_details.company_address_display or get_address_display(party_details.company_address)
party_details.company_address_display
or render_address(party_details.company_address, check_permissions=True)
),
**get_fetch_values(doctype, "billing_address", party_details.company_address),
}
@@ -277,7 +280,34 @@ def set_contact_details(party_details, party, party_type):
}
)
else:
party_details.update(get_contact_details(party_details.contact_person))
fields = [
"name as contact_person",
"salutation",
"first_name",
"last_name",
"email_id as contact_email",
"mobile_no as contact_mobile",
"phone as contact_phone",
"designation as contact_designation",
"department as contact_department",
]
contact_details = frappe.db.get_value(
"Contact", party_details.contact_person, fields, as_dict=True
)
contact_details.contact_display = " ".join(
filter(
None,
[
contact_details.get("salutation"),
contact_details.get("first_name"),
contact_details.get("last_name"),
],
)
)
party_details.update(contact_details)
def set_other_values(party_details, party, party_type):
@@ -938,3 +968,13 @@ def add_party_account(party_type, party, company, account):
doc.append("accounts", accounts)
doc.save()
def render_address(address, check_permissions=True):
try:
from frappe.contacts.doctype.address.address import render_address as _render
except ImportError:
# Older frappe versions where this function is not available
from frappe.contacts.doctype.address.address import get_address_display as _render
return frappe.call(_render, address, check_permissions=check_permissions)

View File

@@ -457,6 +457,8 @@ class GrossProfitGenerator(object):
new_row.qty += flt(row.qty)
new_row.buying_amount += flt(row.buying_amount, self.currency_precision)
new_row.base_amount += flt(row.base_amount, self.currency_precision)
if self.filters.get("group_by") == "Sales Person":
new_row.allocated_amount += flt(row.allocated_amount, self.currency_precision)
new_row = self.set_average_rate(new_row)
self.grouped_data.append(new_row)
else:

View File

@@ -15,6 +15,7 @@ from erpnext.accounts.report.item_wise_sales_register.item_wise_sales_register i
get_group_by_conditions,
get_tax_accounts,
)
from erpnext.selling.report.item_wise_sales_history.item_wise_sales_history import get_item_details
def execute(filters=None):
@@ -39,16 +40,6 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
tax_doctype="Purchase Taxes and Charges",
)
scrubbed_tax_fields = {}
for tax in tax_columns:
scrubbed_tax_fields.update(
{
tax + " Rate": frappe.scrub(tax + " Rate"),
tax + " Amount": frappe.scrub(tax + " Amount"),
}
)
po_pr_map = get_purchase_receipts_against_purchase_order(item_list)
data = []
@@ -59,7 +50,11 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
if filters.get("group_by"):
grand_total = get_grand_total(filters, "Purchase Invoice")
item_details = get_item_details()
for d in item_list:
item_record = item_details.get(d.item_code)
purchase_receipt = None
if d.purchase_receipt:
purchase_receipt = d.purchase_receipt
@@ -72,8 +67,8 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
row = {
"item_code": d.item_code,
"item_name": d.pi_item_name if d.pi_item_name else d.i_item_name,
"item_group": d.pi_item_group if d.pi_item_group else d.i_item_group,
"item_name": item_record.item_name if item_record else d.item_name,
"item_group": item_record.item_group if item_record else d.item_group,
"description": d.description,
"invoice": d.parent,
"posting_date": d.posting_date,
@@ -106,8 +101,8 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
item_tax = itemised_tax.get(d.name, {}).get(tax, {})
row.update(
{
scrubbed_tax_fields[tax + " Rate"]: item_tax.get("tax_rate", 0),
scrubbed_tax_fields[tax + " Amount"]: item_tax.get("tax_amount", 0),
frappe.scrub(tax + " Rate"): item_tax.get("tax_rate", 0),
frappe.scrub(tax + " Amount"): item_tax.get("tax_amount", 0),
}
)
total_tax += flt(item_tax.get("tax_amount"))
@@ -330,17 +325,15 @@ def get_items(filters, additional_query_columns):
`tabPurchase Invoice`.supplier, `tabPurchase Invoice`.remarks, `tabPurchase Invoice`.base_net_total,
`tabPurchase Invoice`.unrealized_profit_loss_account,
`tabPurchase Invoice Item`.`item_code`, `tabPurchase Invoice Item`.description,
`tabPurchase Invoice Item`.`item_name` as pi_item_name, `tabPurchase Invoice Item`.`item_group` as pi_item_group,
`tabItem`.`item_name` as i_item_name, `tabItem`.`item_group` as i_item_group,
`tabPurchase Invoice Item`.`item_name`, `tabPurchase Invoice Item`.`item_group`,
`tabPurchase Invoice Item`.`project`, `tabPurchase Invoice Item`.`purchase_order`,
`tabPurchase Invoice Item`.`purchase_receipt`, `tabPurchase Invoice Item`.`po_detail`,
`tabPurchase Invoice Item`.`expense_account`, `tabPurchase Invoice Item`.`stock_qty`,
`tabPurchase Invoice Item`.`stock_uom`, `tabPurchase Invoice Item`.`base_net_amount`,
`tabPurchase Invoice`.`supplier_name`, `tabPurchase Invoice`.`mode_of_payment` {0}
from `tabPurchase Invoice`, `tabPurchase Invoice Item`, `tabItem`
from `tabPurchase Invoice`, `tabPurchase Invoice Item`
where `tabPurchase Invoice`.name = `tabPurchase Invoice Item`.`parent` and
`tabItem`.name = `tabPurchase Invoice Item`.`item_code` and
`tabPurchase Invoice`.docstatus = 1 %s
`tabPurchase Invoice`.docstatus = 1 %s
""".format(
additional_query_columns
)

View File

@@ -11,6 +11,7 @@ from frappe.utils.xlsxutils import handle_html
from erpnext.accounts.report.sales_register.sales_register import get_mode_of_payments
from erpnext.selling.report.item_wise_sales_history.item_wise_sales_history import (
get_customer_details,
get_item_details,
)
@@ -34,16 +35,6 @@ def _execute(
if item_list:
itemised_tax, tax_columns = get_tax_accounts(item_list, columns, company_currency)
scrubbed_tax_fields = {}
for tax in tax_columns:
scrubbed_tax_fields.update(
{
tax + " Rate": frappe.scrub(tax + " Rate"),
tax + " Amount": frappe.scrub(tax + " Amount"),
}
)
mode_of_payments = get_mode_of_payments(set(d.parent for d in item_list))
so_dn_map = get_delivery_notes_against_sales_order(item_list)
@@ -56,9 +47,11 @@ def _execute(
grand_total = get_grand_total(filters, "Sales Invoice")
customer_details = get_customer_details()
item_details = get_item_details()
for d in item_list:
customer_record = customer_details.get(d.customer)
item_record = item_details.get(d.item_code)
delivery_note = None
if d.delivery_note:
@@ -71,8 +64,8 @@ def _execute(
row = {
"item_code": d.item_code,
"item_name": d.si_item_name if d.si_item_name else d.i_item_name,
"item_group": d.si_item_group if d.si_item_group else d.i_item_group,
"item_name": item_record.item_name if item_record else d.item_name,
"item_group": item_record.item_group if item_record else d.item_group,
"description": d.description,
"invoice": d.parent,
"posting_date": d.posting_date,
@@ -114,8 +107,8 @@ def _execute(
item_tax = itemised_tax.get(d.name, {}).get(tax, {})
row.update(
{
scrubbed_tax_fields[tax + " Rate"]: item_tax.get("tax_rate", 0),
scrubbed_tax_fields[tax + " Amount"]: item_tax.get("tax_amount", 0),
frappe.scrub(tax + " Rate"): item_tax.get("tax_rate", 0),
frappe.scrub(tax + " Amount"): item_tax.get("tax_amount", 0),
}
)
if item_tax.get("is_other_charges"):
@@ -411,18 +404,15 @@ def get_items(filters, additional_query_columns, additional_conditions=None):
`tabSales Invoice Item`.project,
`tabSales Invoice Item`.item_code, `tabSales Invoice Item`.description,
`tabSales Invoice Item`.`item_name`, `tabSales Invoice Item`.`item_group`,
`tabSales Invoice Item`.`item_name` as si_item_name, `tabSales Invoice Item`.`item_group` as si_item_group,
`tabItem`.`item_name` as i_item_name, `tabItem`.`item_group` as i_item_group,
`tabSales Invoice Item`.sales_order, `tabSales Invoice Item`.delivery_note,
`tabSales Invoice Item`.income_account, `tabSales Invoice Item`.cost_center,
`tabSales Invoice Item`.stock_qty, `tabSales Invoice Item`.stock_uom,
`tabSales Invoice Item`.base_net_rate, `tabSales Invoice Item`.base_net_amount,
`tabSales Invoice`.customer_name, `tabSales Invoice`.customer_group, `tabSales Invoice Item`.so_detail,
`tabSales Invoice`.update_stock, `tabSales Invoice Item`.uom, `tabSales Invoice Item`.qty {0}
from `tabSales Invoice`, `tabSales Invoice Item`, `tabItem`
where `tabSales Invoice`.name = `tabSales Invoice Item`.parent and
`tabItem`.name = `tabSales Invoice Item`.`item_code` and
`tabSales Invoice`.docstatus = 1 {1}
from `tabSales Invoice`, `tabSales Invoice Item`
where `tabSales Invoice`.name = `tabSales Invoice Item`.parent
and `tabSales Invoice`.docstatus = 1 {1}
""".format(
additional_query_columns or "", conditions
),

View File

@@ -537,6 +537,10 @@ def update_reference_in_journal_entry(d, journal_entry, do_not_save=False):
"""
jv_detail = journal_entry.get("accounts", {"name": d["voucher_detail_no"]})[0]
# Update Advance Paid in SO/PO since they might be getting unlinked
if jv_detail.get("reference_type") in ("Sales Order", "Purchase Order"):
frappe.get_doc(jv_detail.reference_type, jv_detail.reference_name).set_total_advance_paid()
if flt(d["unadjusted_amount"]) - flt(d["allocated_amount"]) != 0:
# adjust the unreconciled balance
amount_in_account_currency = flt(d["unadjusted_amount"]) - flt(d["allocated_amount"])
@@ -596,6 +600,13 @@ def update_reference_in_payment_entry(d, payment_entry, do_not_save=False):
if d.voucher_detail_no:
existing_row = payment_entry.get("references", {"name": d["voucher_detail_no"]})[0]
# Update Advance Paid in SO/PO since they are getting unlinked
if existing_row.get("reference_doctype") in ("Sales Order", "Purchase Order"):
frappe.get_doc(
existing_row.reference_doctype, existing_row.reference_name
).set_total_advance_paid()
original_row = existing_row.as_dict().copy()
existing_row.update(reference_details)
@@ -810,7 +821,7 @@ def get_held_invoices(party_type, party):
if party_type == "Supplier":
held_invoices = frappe.db.sql(
"select name from `tabPurchase Invoice` where release_date IS NOT NULL and release_date > CURDATE()",
"select name from `tabPurchase Invoice` where on_hold = 1 and release_date IS NOT NULL and release_date > CURDATE()",
as_dict=1,
)
held_invoices = set(d["name"] for d in held_invoices)

View File

@@ -41,6 +41,7 @@ class Asset(AccountsController):
self.validate_item()
self.validate_cost_center()
self.set_missing_values()
self.validate_finance_books()
self.prepare_depreciation_data()
self.validate_gross_and_purchase_amount()
if self.get("schedules"):
@@ -148,17 +149,33 @@ class Asset(AccountsController):
frappe.throw(_("Item {0} must be a non-stock item").format(self.item_code))
def validate_cost_center(self):
if not self.cost_center:
return
cost_center_company = frappe.db.get_value("Cost Center", self.cost_center, "company")
if cost_center_company != self.company:
frappe.throw(
_("Selected Cost Center {} doesn't belongs to {}").format(
frappe.bold(self.cost_center), frappe.bold(self.company)
),
title=_("Invalid Cost Center"),
if self.cost_center:
cost_center_company, cost_center_is_group = frappe.db.get_value(
"Cost Center", self.cost_center, ["company", "is_group"]
)
if cost_center_company != self.company:
frappe.throw(
_("Cost Center {} doesn't belong to Company {}").format(
frappe.bold(self.cost_center), frappe.bold(self.company)
),
title=_("Invalid Cost Center"),
)
if cost_center_is_group:
frappe.throw(
_(
"Cost Center {} is a group cost center and group cost centers cannot be used in transactions"
).format(frappe.bold(self.cost_center)),
title=_("Invalid Cost Center"),
)
else:
if not frappe.get_cached_value("Company", self.company, "depreciation_cost_center"):
frappe.throw(
_(
"Please set a Cost Center for the Asset or set an Asset Depreciation Cost Center for the Company {}"
).format(frappe.bold(self.company)),
title=_("Missing Cost Center"),
)
def validate_in_use_date(self):
if not self.available_for_use_date:
@@ -181,6 +198,27 @@ class Asset(AccountsController):
finance_books = get_item_details(self.item_code, self.asset_category)
self.set("finance_books", finance_books)
def validate_finance_books(self):
if not self.calculate_depreciation or len(self.finance_books) == 1:
return
finance_books = set()
for d in self.finance_books:
if d.finance_book in finance_books:
frappe.throw(
_("Row #{}: Please use a different Finance Book.").format(d.idx),
title=_("Duplicate Finance Book"),
)
else:
finance_books.add(d.finance_book)
if not d.finance_book:
frappe.throw(
_("Row #{}: Finance Book should not be empty since you're using multiple.").format(d.idx),
title=_("Missing Finance Book"),
)
def validate_asset_values(self):
if not self.asset_category:
self.asset_category = frappe.get_cached_value("Item", self.item_code, "asset_category")

View File

@@ -33,6 +33,7 @@ def post_depreciation_entries(date=None):
date = today()
failed_asset_names = []
error_log_names = []
for asset_name in get_depreciable_assets(date):
try:
@@ -41,10 +42,12 @@ def post_depreciation_entries(date=None):
except Exception as e:
frappe.db.rollback()
failed_asset_names.append(asset_name)
error_log = frappe.log_error(e)
error_log_names.append(error_log.name)
if failed_asset_names:
set_depr_entry_posting_status_for_failed_assets(failed_asset_names)
notify_depr_entry_posting_error(failed_asset_names)
notify_depr_entry_posting_error(failed_asset_names, error_log_names)
frappe.db.commit()
@@ -216,7 +219,7 @@ def set_depr_entry_posting_status_for_failed_assets(failed_asset_names):
frappe.db.set_value("Asset", asset_name, "depr_entry_posting_status", "Failed")
def notify_depr_entry_posting_error(failed_asset_names):
def notify_depr_entry_posting_error(failed_asset_names, error_log_names):
recipients = get_users_with_role("Accounts Manager")
if not recipients:
@@ -224,7 +227,8 @@ def notify_depr_entry_posting_error(failed_asset_names):
subject = _("Error while posting depreciation entries")
asset_links = get_comma_separated_asset_links(failed_asset_names)
asset_links = get_comma_separated_links(failed_asset_names, "Asset")
error_log_links = get_comma_separated_links(error_log_names, "Error Log")
message = (
_("Hello,")
@@ -234,23 +238,26 @@ def notify_depr_entry_posting_error(failed_asset_names):
)
+ "."
+ "<br><br>"
+ _(
"Please raise a support ticket and share this email, or forward this email to your development team so that they can find the issue in the developer console by manually creating the depreciation entry via the asset's depreciation schedule table."
+ _("Here are the error logs for the aforementioned failed depreciation entries: {0}").format(
error_log_links
)
+ "."
+ "<br><br>"
+ _("Please share this email with your support team so that they can find and fix the issue.")
)
frappe.sendmail(recipients=recipients, subject=subject, message=message)
def get_comma_separated_asset_links(asset_names):
asset_links = []
def get_comma_separated_links(names, doctype):
links = []
for asset_name in asset_names:
asset_links.append(get_link_to_form("Asset", asset_name))
for name in names:
links.append(get_link_to_form(doctype, name))
asset_links = ", ".join(asset_links)
links = ", ".join(links)
return asset_links
return links
@frappe.whitelist()

View File

@@ -1287,6 +1287,7 @@ class TestDepreciationBasics(AssetSetup):
asset.append(
"finance_books",
{
"finance_book": "Test Finance Book 1",
"depreciation_method": "Straight Line",
"frequency_of_depreciation": 1,
"total_number_of_depreciations": 3,
@@ -1297,6 +1298,7 @@ class TestDepreciationBasics(AssetSetup):
asset.append(
"finance_books",
{
"finance_book": "Test Finance Book 2",
"depreciation_method": "Straight Line",
"frequency_of_depreciation": 1,
"total_number_of_depreciations": 6,
@@ -1307,6 +1309,7 @@ class TestDepreciationBasics(AssetSetup):
asset.append(
"finance_books",
{
"finance_book": "Test Finance Book 3",
"depreciation_method": "Straight Line",
"frequency_of_depreciation": 12,
"total_number_of_depreciations": 3,
@@ -1336,6 +1339,7 @@ class TestDepreciationBasics(AssetSetup):
asset.append(
"finance_books",
{
"finance_book": "Test Finance Book 1",
"depreciation_method": "Straight Line",
"frequency_of_depreciation": 12,
"total_number_of_depreciations": 3,
@@ -1346,6 +1350,7 @@ class TestDepreciationBasics(AssetSetup):
asset.append(
"finance_books",
{
"finance_book": "Test Finance Book 2",
"depreciation_method": "Straight Line",
"frequency_of_depreciation": 12,
"total_number_of_depreciations": 6,
@@ -1581,6 +1586,15 @@ def create_asset_data():
if not frappe.db.exists("Location", "Test Location"):
frappe.get_doc({"doctype": "Location", "location_name": "Test Location"}).insert()
if not frappe.db.exists("Finance Book", "Test Finance Book 1"):
frappe.get_doc({"doctype": "Finance Book", "finance_book_name": "Test Finance Book 1"}).insert()
if not frappe.db.exists("Finance Book", "Test Finance Book 2"):
frappe.get_doc({"doctype": "Finance Book", "finance_book_name": "Test Finance Book 2"}).insert()
if not frappe.db.exists("Finance Book", "Test Finance Book 3"):
frappe.get_doc({"doctype": "Finance Book", "finance_book_name": "Test Finance Book 3"}).insert()
def create_asset(**args):
args = frappe._dict(args)

View File

@@ -33,6 +33,7 @@ frappe.ui.form.on('Asset Category', {
var d = locals[cdt][cdn];
return {
"filters": {
"account_type": "Depreciation",
"root_type": ["in", ["Expense", "Income"]],
"is_group": 0,
"company": d.company_name

View File

@@ -53,7 +53,7 @@ class AssetCategory(Document):
account_type_map = {
"fixed_asset_account": {"account_type": ["Fixed Asset"]},
"accumulated_depreciation_account": {"account_type": ["Accumulated Depreciation"]},
"depreciation_expense_account": {"root_type": ["Expense", "Income"]},
"depreciation_expense_account": {"account_type": ["Depreciation"]},
"capital_work_in_progress_account": {"account_type": ["Capital Work in Progress"]},
}
for d in self.accounts:

View File

@@ -63,7 +63,7 @@ frappe.ui.form.on('Asset Movement', {
fieldnames_to_be_altered = {
target_location: { read_only: 0, reqd: 1 },
source_location: { read_only: 1, reqd: 0 },
from_employee: { read_only: 0, reqd: 1 },
from_employee: { read_only: 0, reqd: 0 },
to_employee: { read_only: 1, reqd: 0 }
};
}

View File

@@ -63,28 +63,19 @@ class AssetMovement(Document):
frappe.throw(_("Source and Target Location cannot be same"))
if self.purpose == "Receipt":
# only when asset is bought and first entry is made
if not d.source_location and not (d.target_location or d.to_employee):
if not (d.source_location) and not (d.target_location or d.to_employee):
frappe.throw(
_("Target Location or To Employee is required while receiving Asset {0}").format(d.asset)
)
elif d.source_location:
# when asset is received from an employee
if d.target_location and not d.from_employee:
frappe.throw(
_("From employee is required while receiving Asset {0} to a target location").format(
d.asset
)
)
if d.from_employee and not d.target_location:
frappe.throw(
_("Target Location is required while receiving Asset {0} from an employee").format(d.asset)
)
if d.to_employee and d.target_location:
elif d.to_employee and d.target_location:
frappe.throw(
_(
"Asset {0} cannot be received at a location and \
given to employee in a single movement"
"Asset {0} cannot be received at a location and given to an employee in a single movement"
).format(d.asset)
)

View File

@@ -7,13 +7,14 @@ from itertools import chain
import frappe
from frappe import _
from frappe.query_builder.functions import IfNull, Sum
from frappe.utils import cstr, flt, formatdate, getdate
from frappe.utils import add_months, cstr, flt, formatdate, getdate, nowdate, today
from erpnext.accounts.report.financial_statements import (
get_fiscal_year_data,
get_period_list,
validate_fiscal_year,
)
from erpnext.accounts.utils import get_fiscal_year
from erpnext.assets.doctype.asset.asset import get_asset_value_after_depreciation
@@ -37,15 +38,26 @@ def get_conditions(filters):
if filters.get("company"):
conditions["company"] = filters.company
if filters.filter_based_on == "Date Range":
if not filters.from_date and not filters.to_date:
filters.from_date = add_months(nowdate(), -12)
filters.to_date = nowdate()
conditions[date_field] = ["between", [filters.from_date, filters.to_date]]
if filters.filter_based_on == "Fiscal Year":
elif filters.filter_based_on == "Fiscal Year":
if not filters.from_fiscal_year and not filters.to_fiscal_year:
default_fiscal_year = get_fiscal_year(today())[0]
filters.from_fiscal_year = default_fiscal_year
filters.to_fiscal_year = default_fiscal_year
fiscal_year = get_fiscal_year_data(filters.from_fiscal_year, filters.to_fiscal_year)
validate_fiscal_year(fiscal_year, filters.from_fiscal_year, filters.to_fiscal_year)
filters.year_start_date = getdate(fiscal_year.year_start_date)
filters.year_end_date = getdate(fiscal_year.year_end_date)
conditions[date_field] = ["between", [filters.year_start_date, filters.year_end_date]]
if filters.get("only_existing_assets"):
conditions["is_existing_asset"] = filters.get("only_existing_assets")
if filters.get("asset_category"):
@@ -54,12 +66,12 @@ def get_conditions(filters):
conditions["cost_center"] = filters.get("cost_center")
if status:
# In Store assets are those that are not sold or scrapped
# In Store assets are those that are not sold or scrapped or capitalized or decapitalized
operand = "not in"
if status not in "In Location":
operand = "in"
conditions["status"] = (operand, ["Sold", "Scrapped"])
conditions["status"] = (operand, ["Sold", "Scrapped", "Capitalized", "Decapitalized"])
return conditions
@@ -71,36 +83,6 @@ def get_data(filters):
pr_supplier_map = get_purchase_receipt_supplier_map()
pi_supplier_map = get_purchase_invoice_supplier_map()
group_by = frappe.scrub(filters.get("group_by"))
if group_by == "asset_category":
fields = ["asset_category", "gross_purchase_amount", "opening_accumulated_depreciation"]
assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields, group_by=group_by)
elif group_by == "location":
fields = ["location", "gross_purchase_amount", "opening_accumulated_depreciation"]
assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields, group_by=group_by)
else:
fields = [
"name as asset_id",
"asset_name",
"status",
"department",
"company",
"cost_center",
"calculate_depreciation",
"purchase_receipt",
"asset_category",
"purchase_date",
"gross_purchase_amount",
"location",
"available_for_use_date",
"purchase_invoice",
"opening_accumulated_depreciation",
]
assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields)
assets_linked_to_fb = get_assets_linked_to_fb(filters)
company_fb = frappe.get_cached_value("Company", filters.company, "default_finance_book")
@@ -114,6 +96,31 @@ def get_data(filters):
depreciation_amount_map = get_asset_depreciation_amount_map(filters, finance_book)
group_by = frappe.scrub(filters.get("group_by"))
if group_by in ("asset_category", "location"):
data = get_group_by_data(group_by, conditions, assets_linked_to_fb, depreciation_amount_map)
return data
fields = [
"name as asset_id",
"asset_name",
"status",
"department",
"company",
"cost_center",
"calculate_depreciation",
"purchase_receipt",
"asset_category",
"purchase_date",
"gross_purchase_amount",
"location",
"available_for_use_date",
"purchase_invoice",
"opening_accumulated_depreciation",
]
assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields)
for asset in assets_record:
if (
assets_linked_to_fb
@@ -136,7 +143,7 @@ def get_data(filters):
or pi_supplier_map.get(asset.purchase_invoice),
"gross_purchase_amount": asset.gross_purchase_amount,
"opening_accumulated_depreciation": asset.opening_accumulated_depreciation,
"depreciated_amount": get_depreciation_amount_of_asset(asset, depreciation_amount_map),
"depreciated_amount": depreciation_amount_map.get(asset.asset_id) or 0.0,
"available_for_use_date": asset.available_for_use_date,
"location": asset.location,
"asset_category": asset.asset_category,
@@ -230,12 +237,11 @@ def get_assets_linked_to_fb(filters):
return assets_linked_to_fb
def get_depreciation_amount_of_asset(asset, depreciation_amount_map):
return depreciation_amount_map.get(asset.asset_id) or 0.0
def get_asset_depreciation_amount_map(filters, finance_book):
date = filters.to_date if filters.filter_based_on == "Date Range" else filters.year_end_date
start_date = (
filters.from_date if filters.filter_based_on == "Date Range" else filters.year_start_date
)
end_date = filters.to_date if filters.filter_based_on == "Date Range" else filters.year_end_date
asset = frappe.qb.DocType("Asset")
gle = frappe.qb.DocType("GL Entry")
@@ -256,25 +262,77 @@ def get_asset_depreciation_amount_map(filters, finance_book):
)
.where(gle.debit != 0)
.where(gle.is_cancelled == 0)
.where(company.name == filters.company)
.where(asset.docstatus == 1)
.groupby(asset.name)
)
if filters.only_existing_assets:
query = query.where(asset.is_existing_asset == 1)
if filters.asset_category:
query = query.where(asset.asset_category == filters.asset_category)
if filters.cost_center:
query = query.where(asset.cost_center == filters.cost_center)
if filters.status:
if filters.status == "In Location":
query = query.where(asset.status.notin(["Sold", "Scrapped", "Capitalized", "Decapitalized"]))
else:
query = query.where(asset.status.isin(["Sold", "Scrapped", "Capitalized", "Decapitalized"]))
if finance_book:
query = query.where(
(gle.finance_book.isin([cstr(finance_book), ""])) | (gle.finance_book.isnull())
)
else:
query = query.where((gle.finance_book.isin([""])) | (gle.finance_book.isnull()))
if filters.filter_based_on in ("Date Range", "Fiscal Year"):
query = query.where(gle.posting_date <= date)
query = query.where(gle.posting_date >= start_date)
query = query.where(gle.posting_date <= end_date)
query = query.groupby(asset.name)
asset_depr_amount_map = query.run()
return dict(asset_depr_amount_map)
def get_group_by_data(group_by, conditions, assets_linked_to_fb, depreciation_amount_map):
fields = [
group_by,
"name",
"gross_purchase_amount",
"opening_accumulated_depreciation",
"calculate_depreciation",
]
assets = frappe.db.get_all("Asset", filters=conditions, fields=fields)
data = []
for a in assets:
if assets_linked_to_fb and a.calculate_depreciation and a.name not in assets_linked_to_fb:
continue
a["depreciated_amount"] = depreciation_amount_map.get(a["name"], 0.0)
a["asset_value"] = (
a["gross_purchase_amount"] - a["opening_accumulated_depreciation"] - a["depreciated_amount"]
)
del a["name"]
del a["calculate_depreciation"]
idx = ([i for i, d in enumerate(data) if a[group_by] == d[group_by]] or [None])[0]
if idx is None:
data.append(a)
else:
for field in (
"gross_purchase_amount",
"opening_accumulated_depreciation",
"depreciated_amount",
"asset_value",
):
data[idx][field] = data[idx][field] + a[field]
return data
def get_purchase_receipt_supplier_map():
return frappe._dict(
frappe.db.sql(
@@ -313,35 +371,35 @@ def get_columns(filters):
"fieldtype": "Link",
"fieldname": frappe.scrub(filters.get("group_by")),
"options": filters.get("group_by"),
"width": 120,
"width": 216,
},
{
"label": _("Gross Purchase Amount"),
"fieldname": "gross_purchase_amount",
"fieldtype": "Currency",
"options": "company:currency",
"width": 100,
"width": 250,
},
{
"label": _("Opening Accumulated Depreciation"),
"fieldname": "opening_accumulated_depreciation",
"fieldtype": "Currency",
"options": "company:currency",
"width": 90,
"width": 250,
},
{
"label": _("Depreciated Amount"),
"fieldname": "depreciated_amount",
"fieldtype": "Currency",
"options": "company:currency",
"width": 100,
"width": 250,
},
{
"label": _("Asset Value"),
"fieldname": "asset_value",
"fieldtype": "Currency",
"options": "company:currency",
"width": 100,
"width": 250,
},
]

View File

@@ -1635,8 +1635,13 @@ class AccountsController(TransactionBase):
)
self.append("payment_schedule", data)
allocate_payment_based_on_payment_terms = frappe.db.get_value(
"Payment Terms Template", self.payment_terms_template, "allocate_payment_based_on_payment_terms"
)
if not (
automatically_fetch_payment_terms
and allocate_payment_based_on_payment_terms
and self.linked_order_has_payment_terms(po_or_so, fieldname, doctype)
):
for d in self.get("payment_schedule"):

View File

@@ -4,7 +4,7 @@
import frappe
from frappe import ValidationError, _, msgprint
from frappe.contacts.doctype.address.address import get_address_display
from frappe.contacts.doctype.address.address import render_address
from frappe.utils import cint, cstr, flt, getdate
from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget
@@ -14,7 +14,8 @@ from erpnext.controllers.sales_and_purchase_return import get_rate_for_return
from erpnext.controllers.stock_controller import StockController
from erpnext.controllers.subcontracting import Subcontracting
from erpnext.stock.get_item_details import get_conversion_factor
from erpnext.stock.utils import get_incoming_rate
from erpnext.stock.stock_ledger import get_previous_sle
from erpnext.stock.utils import get_incoming_rate, get_valuation_method
class QtyMismatchError(ValidationError):
@@ -186,7 +187,9 @@ class BuyingController(StockController, Subcontracting):
for address_field, address_display_field in address_dict.items():
if self.get(address_field):
self.set(address_display_field, get_address_display(self.get(address_field)))
self.set(
address_display_field, render_address(self.get(address_field), check_permissions=False)
)
def set_total_in_words(self):
from frappe.utils import money_in_words
@@ -504,9 +507,20 @@ class BuyingController(StockController, Subcontracting):
)
if self.is_return:
outgoing_rate = get_rate_for_return(
self.doctype, self.name, d.item_code, self.return_against, item_row=d
)
if get_valuation_method(d.item_code) == "Moving Average":
previous_sle = get_previous_sle(
{
"item_code": d.item_code,
"warehouse": d.warehouse,
"posting_date": self.posting_date,
"posting_time": self.posting_time,
}
)
outgoing_rate = flt(previous_sle.get("valuation_rate"))
else:
outgoing_rate = get_rate_for_return(
self.doctype, self.name, d.item_code, self.return_against, item_row=d
)
sle.update({"outgoing_rate": outgoing_rate, "recalculate_rate": 1})
if d.from_warehouse:

View File

@@ -4,9 +4,9 @@
import frappe
from frappe import _, bold, throw
from frappe.contacts.doctype.address.address import get_address_display
from frappe.utils import cint, cstr, flt, get_link_to_form, nowtime
from erpnext.accounts.party import render_address
from erpnext.controllers.accounts_controller import get_taxes_and_charges
from erpnext.controllers.sales_and_purchase_return import get_rate_for_return
from erpnext.controllers.stock_controller import StockController
@@ -583,7 +583,9 @@ class SellingController(StockController):
for address_field, address_display_field in address_dict.items():
if self.get(address_field):
self.set(address_display_field, get_address_display(self.get(address_field)))
self.set(
address_display_field, render_address(self.get(address_field), check_permissions=False)
)
def validate_for_duplicate_items(self):
check_list, chk_dupl_itm = [], []

View File

@@ -582,13 +582,21 @@ class StockController(AccountsController):
d.stock_uom_rate = d.rate / (d.conversion_factor or 1)
def validate_internal_transfer(self):
if (
self.doctype in ("Sales Invoice", "Delivery Note", "Purchase Invoice", "Purchase Receipt")
and self.is_internal_transfer()
):
self.validate_in_transit_warehouses()
self.validate_multi_currency()
self.validate_packed_items()
if self.doctype in ("Sales Invoice", "Delivery Note", "Purchase Invoice", "Purchase Receipt"):
if self.is_internal_transfer():
self.validate_in_transit_warehouses()
self.validate_multi_currency()
self.validate_packed_items()
else:
self.validate_internal_transfer_warehouse()
def validate_internal_transfer_warehouse(self):
for row in self.items:
if row.get("target_warehouse"):
row.target_warehouse = None
if row.get("from_warehouse"):
row.from_warehouse = None
def validate_in_transit_warehouses(self):
if (
@@ -906,8 +914,6 @@ def create_item_wise_repost_entries(voucher_type, voucher_no, allow_zero_rate=Fa
repost_entry = frappe.new_doc("Repost Item Valuation")
repost_entry.based_on = "Item and Warehouse"
repost_entry.voucher_type = voucher_type
repost_entry.voucher_no = voucher_no
repost_entry.item_code = sle.item_code
repost_entry.warehouse = sle.warehouse

View File

@@ -4,6 +4,7 @@
import frappe
from frappe import _
from frappe.query_builder.functions import Concat_ws, Date
def execute(filters=None):
@@ -69,53 +70,41 @@ def get_columns():
def get_data(filters):
return frappe.db.sql(
"""
SELECT
`tabLead`.name,
`tabLead`.lead_name,
`tabLead`.status,
`tabLead`.lead_owner,
`tabLead`.territory,
`tabLead`.source,
`tabLead`.email_id,
`tabLead`.mobile_no,
`tabLead`.phone,
`tabLead`.owner,
`tabLead`.company,
concat_ws(', ',
trim(',' from `tabAddress`.address_line1),
trim(',' from tabAddress.address_line2)
) AS address,
`tabAddress`.state,
`tabAddress`.pincode,
`tabAddress`.country
FROM
`tabLead` left join `tabDynamic Link` on (
`tabLead`.name = `tabDynamic Link`.link_name and
`tabDynamic Link`.parenttype = 'Address')
left join `tabAddress` on (
`tabAddress`.name=`tabDynamic Link`.parent)
WHERE
company = %(company)s
AND DATE(`tabLead`.creation) BETWEEN %(from_date)s AND %(to_date)s
{conditions}
ORDER BY
`tabLead`.creation asc """.format(
conditions=get_conditions(filters)
),
filters,
as_dict=1,
lead = frappe.qb.DocType("Lead")
address = frappe.qb.DocType("Address")
dynamic_link = frappe.qb.DocType("Dynamic Link")
query = (
frappe.qb.from_(lead)
.left_join(dynamic_link)
.on((lead.name == dynamic_link.link_name) & (dynamic_link.parenttype == "Address"))
.left_join(address)
.on(address.name == dynamic_link.parent)
.select(
lead.name,
lead.lead_name,
lead.status,
lead.lead_owner,
lead.territory,
lead.source,
lead.email_id,
lead.mobile_no,
lead.phone,
lead.owner,
lead.company,
(Concat_ws(", ", address.address_line1, address.address_line2)).as_("address"),
address.state,
address.pincode,
address.country,
)
.where(lead.company == filters.company)
.where(Date(lead.creation).between(filters.from_date, filters.to_date))
)
def get_conditions(filters):
conditions = []
if filters.get("territory"):
conditions.append(" and `tabLead`.territory=%(territory)s")
query = query.where(lead.territory == filters.get("territory"))
if filters.get("status"):
conditions.append(" and `tabLead`.status=%(status)s")
query = query.where(lead.status == filters.get("status"))
return " ".join(conditions) if conditions else ""
return query.run(as_dict=1)

View File

@@ -188,7 +188,8 @@
"in_list_view": 1,
"label": "Item Group",
"options": "Item Group",
"read_only": 1
"read_only": 1,
"search_index": 1
},
{
"default": "1",
@@ -234,7 +235,8 @@
"fieldname": "brand",
"fieldtype": "Link",
"label": "Brand",
"options": "Brand"
"options": "Brand",
"search_index": 1
},
{
"collapsible": 1,
@@ -346,7 +348,7 @@
"index_web_pages_for_search": 1,
"links": [],
"make_attachments_public": 1,
"modified": "2022-09-13 04:05:11.614087",
"modified": "2022-09-30 04:01:52.090732",
"modified_by": "Administrator",
"module": "E-commerce",
"name": "Website Item",

View File

@@ -409,9 +409,6 @@ def on_doctype_update():
# since route is a Text column, it needs a length for indexing
frappe.db.add_index("Website Item", ["route(500)"])
frappe.db.add_index("Website Item", ["item_group"])
frappe.db.add_index("Website Item", ["brand"])
def check_if_user_is_customer(user=None):
from frappe.contacts.doctype.contact.contact import get_contact_name

View File

@@ -17,7 +17,6 @@ from erpnext.e_commerce.shopping_cart.cart import (
request_for_quotation,
update_cart,
)
from erpnext.tests.utils import create_test_contact_and_address
class TestShoppingCart(unittest.TestCase):
@@ -28,7 +27,6 @@ class TestShoppingCart(unittest.TestCase):
def setUp(self):
frappe.set_user("Administrator")
create_test_contact_and_address()
self.enable_shopping_cart()
if not frappe.db.exists("Website Item", {"item_code": "_Test Item"}):
make_website_item(frappe.get_cached_doc("Item", "_Test Item"))
@@ -46,48 +44,57 @@ class TestShoppingCart(unittest.TestCase):
frappe.db.sql("delete from `tabTax Rule`")
def test_get_cart_new_user(self):
self.login_as_new_user()
self.login_as_customer(
"test_contact_two_customer@example.com", "_Test Contact 2 For _Test Customer"
)
create_address_and_contact(
address_title="_Test Address for Customer 2",
first_name="_Test Contact for Customer 2",
email="test_contact_two_customer@example.com",
customer="_Test Customer 2",
)
# test if lead is created and quotation with new lead is fetched
quotation = _get_cart_quotation()
customer = frappe.get_doc("Customer", "_Test Customer 2")
quotation = _get_cart_quotation(party=customer)
self.assertEqual(quotation.quotation_to, "Customer")
self.assertEqual(
quotation.contact_person,
frappe.db.get_value("Contact", dict(email_id="test_cart_user@example.com")),
frappe.db.get_value("Contact", dict(email_id="test_contact_two_customer@example.com")),
)
self.assertEqual(quotation.contact_email, frappe.session.user)
return quotation
def test_get_cart_customer(self):
def validate_quotation():
def test_get_cart_customer(self, customer="_Test Customer 2"):
def validate_quotation(customer_name):
# test if quotation with customer is fetched
quotation = _get_cart_quotation()
party = frappe.get_doc("Customer", customer_name)
quotation = _get_cart_quotation(party=party)
self.assertEqual(quotation.quotation_to, "Customer")
self.assertEqual(quotation.party_name, "_Test Customer")
self.assertEqual(quotation.party_name, customer_name)
self.assertEqual(quotation.contact_email, frappe.session.user)
return quotation
self.login_as_customer(
"test_contact_two_customer@example.com", "_Test Contact 2 For _Test Customer"
)
validate_quotation()
self.login_as_customer()
quotation = validate_quotation()
quotation = validate_quotation(customer)
return quotation
def test_add_to_cart(self):
self.login_as_customer()
self.login_as_customer(
"test_contact_two_customer@example.com", "_Test Contact 2 For _Test Customer"
)
create_address_and_contact(
address_title="_Test Address for Customer 2",
first_name="_Test Contact for Customer 2",
email="test_contact_two_customer@example.com",
customer="_Test Customer 2",
)
# clear existing quotations
self.clear_existing_quotations()
# add first item
update_cart("_Test Item", 1)
quotation = self.test_get_cart_customer()
quotation = self.test_get_cart_customer("_Test Customer 2")
self.assertEqual(quotation.get("items")[0].item_code, "_Test Item")
self.assertEqual(quotation.get("items")[0].qty, 1)
@@ -95,7 +102,7 @@ class TestShoppingCart(unittest.TestCase):
# add second item
update_cart("_Test Item 2", 1)
quotation = self.test_get_cart_customer()
quotation = self.test_get_cart_customer("_Test Customer 2")
self.assertEqual(quotation.get("items")[1].item_code, "_Test Item 2")
self.assertEqual(quotation.get("items")[1].qty, 1)
self.assertEqual(quotation.get("items")[1].amount, 20)
@@ -108,7 +115,7 @@ class TestShoppingCart(unittest.TestCase):
# update first item
update_cart("_Test Item", 5)
quotation = self.test_get_cart_customer()
quotation = self.test_get_cart_customer("_Test Customer 2")
self.assertEqual(quotation.get("items")[0].item_code, "_Test Item")
self.assertEqual(quotation.get("items")[0].qty, 5)
self.assertEqual(quotation.get("items")[0].amount, 50)
@@ -121,7 +128,7 @@ class TestShoppingCart(unittest.TestCase):
# remove first item
update_cart("_Test Item", 0)
quotation = self.test_get_cart_customer()
quotation = self.test_get_cart_customer("_Test Customer 2")
self.assertEqual(quotation.get("items")[0].item_code, "_Test Item 2")
self.assertEqual(quotation.get("items")[0].qty, 1)
@@ -129,9 +136,20 @@ class TestShoppingCart(unittest.TestCase):
self.assertEqual(quotation.net_total, 20)
self.assertEqual(len(quotation.get("items")), 1)
@unittest.skip("Flaky in CI")
def test_tax_rule(self):
self.create_tax_rule()
self.login_as_customer()
self.login_as_customer(
"test_contact_two_customer@example.com", "_Test Contact 2 For _Test Customer"
)
create_address_and_contact(
address_title="_Test Address for Customer 2",
first_name="_Test Contact for Customer 2",
email="test_contact_two_customer@example.com",
customer="_Test Customer 2",
)
quotation = self.create_quotation()
from erpnext.accounts.party import set_taxes
@@ -319,7 +337,7 @@ class TestShoppingCart(unittest.TestCase):
if frappe.db.exists("User", email):
return
frappe.get_doc(
user = frappe.get_doc(
{
"doctype": "User",
"user_type": "Website User",
@@ -329,6 +347,40 @@ class TestShoppingCart(unittest.TestCase):
}
).insert(ignore_permissions=True)
user.add_roles("Customer")
def create_address_and_contact(**kwargs):
if not frappe.db.get_value("Address", {"address_title": kwargs.get("address_title")}):
frappe.get_doc(
{
"doctype": "Address",
"address_title": kwargs.get("address_title"),
"address_type": kwargs.get("address_type") or "Office",
"address_line1": kwargs.get("address_line1") or "Station Road",
"city": kwargs.get("city") or "_Test City",
"state": kwargs.get("state") or "Test State",
"country": kwargs.get("country") or "India",
"links": [
{"link_doctype": "Customer", "link_name": kwargs.get("customer") or "_Test Customer"}
],
}
).insert()
if not frappe.db.get_value("Contact", {"first_name": kwargs.get("first_name")}):
contact = frappe.get_doc(
{
"doctype": "Contact",
"first_name": kwargs.get("first_name"),
"links": [
{"link_doctype": "Customer", "link_name": kwargs.get("customer") or "_Test Customer"}
],
}
)
contact.add_email(kwargs.get("email") or "test_contact_customer@example.com", is_primary=True)
contact.add_phone(kwargs.get("phone") or "+91 0000000000", is_primary_phone=True)
contact.insert()
test_dependencies = [
"Sales Taxes and Charges Template",

View File

@@ -108,8 +108,8 @@ class EmployeeAdvance(Document):
EmployeeAdvanceOverPayment,
)
if flt(return_amount) > self.paid_amount - self.claimed_amount:
frappe.throw(_("Return amount cannot be greater unclaimed amount"))
if flt(return_amount) > 0 and flt(return_amount) > (self.paid_amount - self.claimed_amount):
frappe.throw(_("Return amount cannot be greater than unclaimed amount"))
self.db_set("paid_amount", paid_amount)
self.db_set("return_amount", return_amount)

View File

@@ -6,7 +6,12 @@
frappe.ui.form.on('Loan Repayment', {
// refresh: function(frm) {
// }
// },
setup: function(frm) {
frm.add_fetch("against_loan", "repay_from_salary", "repay_from_salary");
},
onload: function(frm) {
frm.set_query('against_loan', function() {
return {

View File

@@ -15,7 +15,6 @@
"posting_date",
"clearance_date",
"rate_of_interest",
"payroll_payable_account",
"is_term_loan",
"repay_from_salary",
"payment_details_section",
@@ -41,6 +40,7 @@
"amended_from",
"accounting_details_section",
"payment_account",
"payroll_payable_account",
"penalty_income_account",
"column_break_36",
"loan_account"
@@ -262,7 +262,6 @@
},
{
"default": "0",
"fetch_from": "against_loan.repay_from_salary",
"fieldname": "repay_from_salary",
"fieldtype": "Check",
"label": "Repay From Salary"
@@ -280,6 +279,7 @@
"label": "Accounting Details"
},
{
"depends_on": "eval:!doc.repay_from_salary",
"fetch_from": "against_loan.payment_account",
"fetch_if_empty": 1,
"fieldname": "payment_account",
@@ -311,11 +311,10 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2022-08-04 17:13:51.964203",
"modified": "2023-09-18 16:50:32.897005",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Loan Repayment",
"naming_rule": "Expression (old style)",
"owner": "Administrator",
"permissions": [
{
@@ -351,6 +350,5 @@
],
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}
}

View File

@@ -81,6 +81,11 @@ class LoanRepayment(AccountsController):
if amounts.get("due_date"):
self.due_date = amounts.get("due_date")
if self.repay_from_salary and not self.payroll_payable_account:
frappe.throw(_("Please set Payroll Payable Account in Loan Repayment"))
elif not self.repay_from_salary and self.payroll_payable_account:
self.repay_from_salary = 1
def check_future_entries(self):
future_repayment_date = frappe.db.get_value(
"Loan Repayment",
@@ -250,6 +255,9 @@ class LoanRepayment(AccountsController):
)
def check_future_accruals(self):
if self.is_term_loan:
return
future_accrual_date = frappe.db.get_value(
"Loan Interest Accrual",
{"posting_date": (">", self.posting_date), "docstatus": 1, "loan": self.against_loan},

View File

@@ -79,6 +79,7 @@ class BOMUpdateLog(Document):
else:
frappe.enqueue(
method="erpnext.manufacturing.doctype.bom_update_log.bom_update_log.process_boms_cost_level_wise",
queue="long",
update_doc=self,
now=frappe.flags.in_test,
)

View File

@@ -157,12 +157,21 @@ def get_next_higher_level_boms(
def get_leaf_boms() -> List[str]:
"Get BOMs that have no dependencies."
return frappe.db.sql_list(
"""select name from `tabBOM` bom
where docstatus=1 and is_active=1
and not exists(select bom_no from `tabBOM Item`
where parent=bom.name and ifnull(bom_no, '')!='')"""
)
bom = frappe.qb.DocType("BOM")
bom_item = frappe.qb.DocType("BOM Item")
boms = (
frappe.qb.from_(bom)
.left_join(bom_item)
.on((bom.name == bom_item.parent) & (bom_item.bom_no != ""))
.select(bom.name)
.where((bom.docstatus == 1) & (bom.is_active == 1) & (bom_item.bom_no.isnull()))
.distinct()
).run(as_list=True)
boms = [bom[0] for bom in boms]
return boms
def _generate_dependence_map() -> defaultdict:

View File

@@ -675,10 +675,9 @@ class ProductionPlan(Document):
material_request.flags.ignore_permissions = 1
material_request.run_method("set_missing_values")
material_request.save()
if self.get("submit_material_request"):
material_request.submit()
else:
material_request.save()
frappe.flags.mute_messages = False

View File

@@ -377,3 +377,5 @@ execute:frappe.db.set_value("Naming Series", "Naming Series", {"select_doc_for_s
erpnext.patches.v13_0.update_schedule_type_in_loans
erpnext.patches.v13_0.update_asset_value_for_manual_depr_entries
erpnext.patches.v13_0.update_docs_link
erpnext.patches.v13_0.correct_asset_value_if_je_with_workflow
execute:frappe.db.set_value("Accounts Settings", "Accounts Settings", "service_provider", "frankfurter.app")

View File

@@ -3,23 +3,24 @@ import frappe
def execute():
frappe.reload_doc("stock", "doctype", "quality_inspection_parameter")
params = set()
# get all distinct parameters from QI readigs table
reading_params = frappe.db.get_all(
"Quality Inspection Reading", fields=["distinct specification"]
)
reading_params = [d.specification for d in reading_params]
# get all parameters from QI readings table
for (p,) in frappe.db.get_all(
"Quality Inspection Reading", fields=["specification"], as_list=True
):
params.add(p.strip())
# get all distinct parameters from QI Template as some may be unused in QI
template_params = frappe.db.get_all(
"Item Quality Inspection Parameter", fields=["distinct specification"]
)
template_params = [d.specification for d in template_params]
# get all parameters from QI Template as some may be unused in QI
for (p,) in frappe.db.get_all(
"Item Quality Inspection Parameter", fields=["specification"], as_list=True
):
params.add(p.strip())
params = list(set(reading_params + template_params))
# because db primary keys are case insensitive, so duplicates will cause an exception
params = set({x.casefold(): x for x in params}.values())
for parameter in params:
if not frappe.db.exists("Quality Inspection Parameter", parameter):
frappe.get_doc(
{"doctype": "Quality Inspection Parameter", "parameter": parameter, "description": parameter}
).insert(ignore_permissions=True)
frappe.get_doc(
{"doctype": "Quality Inspection Parameter", "parameter": parameter, "description": parameter}
).insert(ignore_permissions=True)

View File

@@ -0,0 +1,119 @@
import frappe
from frappe.model.workflow import get_workflow_name
from frappe.query_builder.functions import IfNull, Sum
def execute():
active_je_workflow = get_workflow_name("Journal Entry")
if not active_je_workflow:
return
correct_value_for_assets_with_manual_depr_entries()
finance_books = frappe.db.get_all("Finance Book", pluck="name")
if finance_books:
for fb_name in finance_books:
correct_value_for_assets_with_auto_depr(fb_name)
correct_value_for_assets_with_auto_depr()
def correct_value_for_assets_with_manual_depr_entries():
asset = frappe.qb.DocType("Asset")
gle = frappe.qb.DocType("GL Entry")
aca = frappe.qb.DocType("Asset Category Account")
company = frappe.qb.DocType("Company")
asset_details_and_depr_amount_map = (
frappe.qb.from_(gle)
.join(asset)
.on(gle.against_voucher == asset.name)
.join(aca)
.on((aca.parent == asset.asset_category) & (aca.company_name == asset.company))
.join(company)
.on(company.name == asset.company)
.select(
asset.name.as_("asset_name"),
asset.gross_purchase_amount.as_("gross_purchase_amount"),
asset.opening_accumulated_depreciation.as_("opening_accumulated_depreciation"),
Sum(gle.debit).as_("depr_amount"),
)
.where(
gle.account == IfNull(aca.depreciation_expense_account, company.depreciation_expense_account)
)
.where(gle.debit != 0)
.where(gle.is_cancelled == 0)
.where(asset.docstatus == 1)
.where(asset.calculate_depreciation == 0)
.groupby(asset.name)
)
frappe.qb.update(asset).join(asset_details_and_depr_amount_map).on(
asset_details_and_depr_amount_map.asset_name == asset.name
).set(
asset.value_after_depreciation,
asset_details_and_depr_amount_map.gross_purchase_amount
- asset_details_and_depr_amount_map.opening_accumulated_depreciation
- asset_details_and_depr_amount_map.depr_amount,
).run()
def correct_value_for_assets_with_auto_depr(fb_name=None):
asset = frappe.qb.DocType("Asset")
gle = frappe.qb.DocType("GL Entry")
aca = frappe.qb.DocType("Asset Category Account")
company = frappe.qb.DocType("Company")
afb = frappe.qb.DocType("Asset Finance Book")
asset_details_and_depr_amount_map = (
frappe.qb.from_(gle)
.join(asset)
.on(gle.against_voucher == asset.name)
.join(aca)
.on((aca.parent == asset.asset_category) & (aca.company_name == asset.company))
.join(company)
.on(company.name == asset.company)
.select(
asset.name.as_("asset_name"),
asset.gross_purchase_amount.as_("gross_purchase_amount"),
asset.opening_accumulated_depreciation.as_("opening_accumulated_depreciation"),
Sum(gle.debit).as_("depr_amount"),
)
.where(
gle.account == IfNull(aca.depreciation_expense_account, company.depreciation_expense_account)
)
.where(gle.debit != 0)
.where(gle.is_cancelled == 0)
.where(asset.docstatus == 1)
.where(asset.calculate_depreciation == 1)
.groupby(asset.name)
)
if fb_name:
asset_details_and_depr_amount_map = asset_details_and_depr_amount_map.where(
gle.finance_book == fb_name
)
else:
asset_details_and_depr_amount_map = asset_details_and_depr_amount_map.where(
(gle.finance_book.isin([""])) | (gle.finance_book.isnull())
)
query = (
frappe.qb.update(afb)
.join(asset_details_and_depr_amount_map)
.on(asset_details_and_depr_amount_map.asset_name == afb.parent)
.set(
afb.value_after_depreciation,
asset_details_and_depr_amount_map.gross_purchase_amount
- asset_details_and_depr_amount_map.opening_accumulated_depreciation
- asset_details_and_depr_amount_map.depr_amount,
)
)
if fb_name:
query = query.where(afb.finance_book == fb_name)
else:
query = query.where((afb.finance_book.isin([""])) | (afb.finance_book.isnull()))
query.run()

View File

@@ -125,6 +125,7 @@ def execute():
loan_type_doc.company = loan.company
loan_type_doc.mode_of_payment = loan.mode_of_payment
loan_type_doc.payment_account = loan.payment_account
loan_type_doc.disbursement_account = loan.payment_account
loan_type_doc.loan_account = loan.loan_account
loan_type_doc.interest_income_account = loan.interest_income_account
loan_type_doc.penalty_income_account = penalty_account

View File

@@ -46,6 +46,7 @@ from erpnext.payroll.doctype.payroll_period.payroll_period import (
get_payroll_period,
get_period_factor,
)
from erpnext.payroll.utils import prepare_error_msg, sanitize_expression
from erpnext.utilities.transaction_base import TransactionBase
@@ -726,32 +727,53 @@ class SalarySlip(TransactionBase):
return data, default_data
def eval_condition_and_formula(self, d, data):
def eval_condition_and_formula(self, struct_row, data):
try:
condition = d.condition.strip().replace("\n", " ") if d.condition else None
condition = sanitize_expression(struct_row.condition)
if condition:
if not frappe.safe_eval(condition, self.whitelisted_globals, data):
return None
amount = d.amount
if d.amount_based_on_formula:
formula = d.formula.strip().replace("\n", " ") if d.formula else None
amount = struct_row.amount
if struct_row.amount_based_on_formula:
formula = sanitize_expression(struct_row.formula)
if formula:
amount = flt(frappe.safe_eval(formula, self.whitelisted_globals, data), d.precision("amount"))
amount = flt(
frappe.safe_eval(formula, self.whitelisted_globals, data), struct_row.precision("amount")
)
if amount:
data[d.abbr] = amount
data[struct_row.abbr] = amount
return amount
except NameError as err:
frappe.throw(
_("{0} <br> This error can be due to missing or deleted field.").format(err),
title=_("Name error"),
except NameError as ne:
message = prepare_error_msg(
row=struct_row,
error=ne,
expression=formula or condition,
description=_("This error can be due to missing or deleted field."),
)
except SyntaxError as err:
frappe.throw(_("Syntax error in formula or condition: {0}").format(err))
frappe.throw(message, title=_("Name error"))
except SyntaxError as se:
message = prepare_error_msg(
row=struct_row,
error=se,
expression=formula or condition,
description=_("Please check the syntax of your formula."),
)
frappe.throw(message, title=_("Syntax error"))
except Exception as e:
frappe.throw(_("Error in formula or condition: {0}").format(e))
raise
message = prepare_error_msg(
row=struct_row,
error=e,
expression=formula or condition,
description=_("This error can be due to invalid formula or condition."),
)
frappe.throw(message, title=_("Error in formula or condition"))
def add_employee_benefits(self, payroll_period):
for struct_row in self._salary_structure_doc.get("earnings"):

66
erpnext/payroll/utils.py Normal file
View File

@@ -0,0 +1,66 @@
from typing import Optional
import frappe
from frappe import _
from frappe.utils import get_link_to_form
def sanitize_expression(string: Optional[str] = None) -> Optional[str]:
"""
Sanitizes an expression string by removing leading/trailing spaces, newlines, and line boundaries.
Args:
string (Optional[str]): The input string to be sanitized (default: None).
Returns:
Optional[str]: The sanitized string or None if the input string is empty or None.
Example:
expression = "\r\n gross_pay > 10000\n "
sanitized_expr = sanitize_expression(expression)
"""
if not string:
return None
parts = string.strip().splitlines()
string = " ".join(parts)
return string
def prepare_error_msg(*, row: dict, error: str, expression: str, description: str) -> str:
"""
Prepares an error message string with formatted information about the error.
Args:
row (dict): A dictionary representing the row data.
error (str): The error message.
expression (str): The expression that caused the error.
description (str): Additional description or hint for the error (optional).
Returns:
str: The formatted error message string.
Example:
row = {
"parenttype": "Salary Structure",
"parent": "Salary Structure-00001",
"parentfield": "earnings",
"idx": 1
}
error = "SyntaxError: invalid syntax"
expression = " 200 if (gross_pay>10000 and month!= 'Feb')) else 0 "
description = "Check the syntax of the expression."
error_msg = prepare_error_msg(row=row, error=error, expression=expression, description=description)
"""
msg = _("Error in {0} while evaluating the {1} {2} at row {3}").format(
row.parentfield.title(), row.parenttype, get_link_to_form(row.parenttype, row.parent), row.idx
)
msg += "<br><br>{0}: {1}<br><br>{2}: {3}".format(
frappe.bold(_("Expression:")), expression, frappe.bold(_("Error:")), error
)
if description:
msg += "<br><br>{0}: {1}".format(frappe.bold(_("Hint:")), description)
return msg

View File

@@ -1068,6 +1068,16 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
this.frm.set_df_property("conversion_rate", "read_only", erpnext.stale_rate_allowed() ? 0 : 1);
},
apply_discount_on_item: function(doc, cdt, cdn, field) {
var item = frappe.get_doc(cdt, cdn);
if(!item.price_list_rate) {
item[field] = 0.0;
} else {
this.price_list_rate(doc, cdt, cdn);
}
this.set_gross_profit(item);
},
shipping_rule: function() {
var me = this;
if(this.frm.doc.shipping_rule) {
@@ -1720,6 +1730,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
() => {
if(args.items.length) {
me._set_values_for_item_list(r.message.children);
$.each(r.message.children || [], function(i, d) {
me.apply_discount_on_item(d, d.doctype, d.name, 'discount_percentage');
});
}
},
() => { me.in_apply_price_list = false; }

View File

@@ -6,8 +6,10 @@ erpnext.financial_statements = {
if (data && column.fieldname=="account") {
value = data.account_name || value;
column.link_onclick =
"erpnext.financial_statements.open_general_ledger(" + JSON.stringify(data) + ")";
if (data.account) {
column.link_onclick =
"erpnext.financial_statements.open_general_ledger(" + JSON.stringify(data) + ")";
}
column.is_tree = true;
}

View File

@@ -566,7 +566,6 @@ erpnext.utils.update_child_items = function(opts) {
fields.splice(3, 0, {
fieldtype: 'Float',
fieldname: "conversion_factor",
in_list_view: 1,
label: __("Conversion Factor"),
precision: get_precision('conversion_factor')
})
@@ -574,6 +573,7 @@ erpnext.utils.update_child_items = function(opts) {
new frappe.ui.Dialog({
title: __("Update Items"),
size: "extra-large",
fields: [
{
fieldname: "trans_items",

View File

@@ -1,4 +1,5 @@
{
"actions": [],
"autoname": "format:{####}",
"creation": "2019-05-26 15:03:43.996455",
"doctype": "DocType",
@@ -12,7 +13,6 @@
],
"fields": [
{
"fetch_from": "goal.objective",
"fieldname": "objective",
"fieldtype": "Text",
"in_list_view": 1,
@@ -38,14 +38,17 @@
}
],
"istable": 1,
"modified": "2019-05-26 16:12:54.832058",
"links": [],
"modified": "2023-07-28 18:10:23.351246",
"modified_by": "Administrator",
"module": "Quality Management",
"name": "Quality Goal Objective",
"naming_rule": "Expression",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}

View File

@@ -1378,7 +1378,7 @@ class GSPConnector:
def set_einvoice_data(self, res):
enc_signed_invoice = res.get("SignedInvoice")
dec_signed_invoice = jwt.decode(enc_signed_invoice, verify=False)["data"]
dec_signed_invoice = jwt.decode(enc_signed_invoice, options={"verify_signature": False})["data"]
self.invoice.irn = res.get("Irn")
self.invoice.ewaybill = res.get("EwbNo")

View File

@@ -28,7 +28,7 @@ def execute(filters=None):
"gst_category",
"export_type",
"ecommerce_gstin",
"`tabPurchase Invoice Item`.gst_hsn_code",
"gst_hsn_code",
"bill_no",
"bill_date",
],

View File

@@ -39,7 +39,7 @@ def execute(filters=None):
"gst_category",
"export_type",
"ecommerce_gstin",
"`tabSales Invoice Item`.gst_hsn_code",
"gst_hsn_code",
]
additional_conditions = get_conditions(filters, additional_query_columns)

View File

@@ -96,18 +96,26 @@ class SalesOrder(SellingController):
and customer = %s",
(self.po_no, self.name, self.customer),
)
if (
so
and so[0][0]
and not cint(
if so and so[0][0]:
if cint(
frappe.db.get_single_value("Selling Settings", "allow_against_multiple_purchase_orders")
)
):
frappe.msgprint(
_("Warning: Sales Order {0} already exists against Customer's Purchase Order {1}").format(
so[0][0], self.po_no
):
frappe.msgprint(
_("Warning: Sales Order {0} already exists against Customer's Purchase Order {1}").format(
frappe.bold(so[0][0]), frappe.bold(self.po_no)
)
)
else:
frappe.throw(
_(
"Sales Order {0} already exists against Customer's Purchase Order {1}. To allow multiple Sales Orders, Enable {2} in {3}"
).format(
frappe.bold(so[0][0]),
frappe.bold(self.po_no),
frappe.bold(_("'Allow Multiple Sales Orders Against a Customer's Purchase Order'")),
get_link_to_form("Selling Settings", "Selling Settings"),
)
)
)
def validate_for_items(self):
for d in self.get("items"):

View File

@@ -1741,7 +1741,7 @@ def make_sales_order(**args):
so.company = args.company or "_Test Company"
so.customer = args.customer or "_Test Customer"
so.currency = args.currency or "INR"
so.po_no = args.po_no or "12345"
so.po_no = args.po_no or ""
if args.selling_price_list:
so.selling_price_list = args.selling_price_list

View File

@@ -145,16 +145,6 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
this.apply_discount_on_item(doc, cdt, cdn, 'discount_amount');
},
apply_discount_on_item: function(doc, cdt, cdn, field) {
var item = frappe.get_doc(cdt, cdn);
if(!item.price_list_rate) {
item[field] = 0.0;
} else {
this.price_list_rate(doc, cdt, cdn);
}
this.set_gross_profit(item);
},
commission_rate: function() {
this.calculate_commission();
},

View File

@@ -75,7 +75,7 @@ class TestCurrencyExchange(unittest.TestCase):
self.clear_cache()
exchange_rate = get_exchange_rate("USD", "INR", "2015-12-15", "for_selling")
self.assertFalse(exchange_rate == 60)
self.assertEqual(flt(exchange_rate, 3), 66.999)
self.assertEqual(flt(exchange_rate, 3), 66.894)
def test_exchange_rate_strict(self):
# strict currency settings
@@ -87,7 +87,7 @@ class TestCurrencyExchange(unittest.TestCase):
self.clear_cache()
exchange_rate = get_exchange_rate("USD", "INR", "2016-01-15", "for_buying")
self.assertEqual(flt(exchange_rate, 3), 67.235)
self.assertEqual(flt(exchange_rate, 3), 67.79)
exchange_rate = get_exchange_rate("USD", "INR", "2016-01-30", "for_selling")
self.assertEqual(exchange_rate, 62.9)
@@ -95,7 +95,7 @@ class TestCurrencyExchange(unittest.TestCase):
# Exchange rate as on 15th Dec, 2015
self.clear_cache()
exchange_rate = get_exchange_rate("USD", "INR", "2015-12-15", "for_buying")
self.assertEqual(flt(exchange_rate, 3), 66.999)
self.assertEqual(flt(exchange_rate, 3), 66.894)
def test_exchange_rate_strict_switched(self):
# Start with allow_stale is True
@@ -108,4 +108,4 @@ class TestCurrencyExchange(unittest.TestCase):
# Will fetch from fixer.io
self.clear_cache()
exchange_rate = get_exchange_rate("USD", "INR", "2016-01-15", "for_buying")
self.assertEqual(flt(exchange_rate, 3), 67.235)
self.assertEqual(flt(exchange_rate, 3), 67.79)

View File

@@ -33,6 +33,7 @@ def after_install():
add_standard_navbar_items()
add_app_name()
add_non_standard_user_types()
update_roles()
frappe.db.commit()
@@ -237,6 +238,12 @@ def create_custom_role(data):
).insert(ignore_permissions=True)
def update_roles():
website_user_roles = ("Customer", "Supplier")
for role in website_user_roles:
frappe.db.set_value("Role", role, "desk_access", 0)
def create_user_type(user_type, data):
if frappe.db.exists("User Type", user_type):
doc = frappe.get_cached_doc("User Type", user_type)

View File

@@ -113,13 +113,30 @@ def get_exchange_rate(from_currency, to_currency, transaction_date=None, args=No
if not value:
import requests
api_url = "https://api.exchangerate.host/convert"
response = requests.get(
api_url, params={"date": transaction_date, "from": from_currency, "to": to_currency}
)
if currency_settings.service_provider == "exchangerate.host":
api_url = "https://api.exchangerate.host/convert"
response = requests.get(
api_url,
params={
"access_key": currency_settings.access_key,
"transaction_date": transaction_date,
"amount": 1,
"from": from_currency,
"to": to_currency,
},
)
# exchangerate.host return 200 for all requests. Can't rely on it to raise exception
value = response.json()["result"]
if not response.json()["success"]:
raise frappe.ValidationError
else:
api_url = f"https://api.frankfurter.app/{transaction_date}"
response = requests.get(api_url, params={"from": from_currency, "to": to_currency})
value = response.json()["rates"][to_currency]
# expire in 6 hours
response.raise_for_status()
value = response.json()["result"]
cache.setex(name=key, time=21600, value=flt(value))
return flt(value)
except Exception:

View File

@@ -1268,6 +1268,7 @@
"depends_on": "eval: doc.is_internal_customer",
"fieldname": "set_target_warehouse",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"in_standard_filter": 1,
"label": "Set Target Warehouse",
"no_copy": 1,
@@ -1335,7 +1336,7 @@
"idx": 146,
"is_submittable": 1,
"links": [],
"modified": "2022-09-16 17:46:17.701904",
"modified": "2023-09-04 14:15:28.363184",
"modified_by": "Administrator",
"module": "Stock",
"name": "Delivery Note",
@@ -1405,4 +1406,4 @@
"title_field": "title",
"track_changes": 1,
"track_seen": 1
}
}

View File

@@ -697,7 +697,7 @@ class TestDeliveryNote(FrappeTestCase):
def test_dn_billing_status_case1(self):
# SO -> DN -> SI
so = make_sales_order()
so = make_sales_order(po_no="12345")
dn = create_dn_against_so(so.name, delivered_qty=2)
self.assertEqual(dn.status, "To Bill")
@@ -724,7 +724,7 @@ class TestDeliveryNote(FrappeTestCase):
make_sales_invoice,
)
so = make_sales_order()
so = make_sales_order(po_no="12345")
si = make_sales_invoice(so.name)
si.get("items")[0].qty = 5
@@ -768,7 +768,7 @@ class TestDeliveryNote(FrappeTestCase):
frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
so = make_sales_order()
so = make_sales_order(po_no="12345")
dn1 = make_delivery_note(so.name)
dn1.get("items")[0].qty = 2
@@ -814,7 +814,7 @@ class TestDeliveryNote(FrappeTestCase):
from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_delivery_note
from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice
so = make_sales_order()
so = make_sales_order(po_no="12345")
si = make_sales_invoice(so.name)
si.submit()
@@ -1180,6 +1180,25 @@ class TestDeliveryNote(FrappeTestCase):
self.assertTrue(return_dn.docstatus == 1)
def tearDown(self):
frappe.db.rollback()
frappe.db.set_single_value("Selling Settings", "dont_reserve_sales_order_qty_on_sales_return", 0)
def test_non_internal_transfer_delivery_note(self):
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
dn = create_delivery_note(do_not_submit=True)
warehouse = create_warehouse("Internal Transfer Warehouse", company=dn.company)
dn.items[0].db_set("target_warehouse", warehouse)
dn.reload()
self.assertEqual(dn.items[0].target_warehouse, warehouse)
dn.save()
dn.reload()
self.assertFalse(dn.items[0].target_warehouse)
def create_delivery_note(**args):
dn = frappe.new_doc("Delivery Note")

View File

@@ -213,7 +213,8 @@
"default": "0",
"fieldname": "disabled",
"fieldtype": "Check",
"label": "Disabled"
"label": "Disabled",
"search_index": 1
},
{
"default": "0",
@@ -956,7 +957,7 @@
"index_web_pages_for_search": 1,
"links": [],
"make_attachments_public": 1,
"modified": "2022-09-13 04:08:17.431731",
"modified": "2023-07-14 17:18:18.658942",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item",

View File

@@ -742,6 +742,8 @@ def create_item(
opening_stock=0,
is_fixed_asset=0,
asset_category=None,
buying_cost_center=None,
selling_cost_center=None,
company="_Test Company",
):
if not frappe.db.exists("Item", item_code):
@@ -759,7 +761,15 @@ def create_item(
item.is_purchase_item = is_purchase_item
item.is_customer_provided_item = is_customer_provided_item
item.customer = customer or ""
item.append("item_defaults", {"default_warehouse": warehouse, "company": company})
item.append(
"item_defaults",
{
"default_warehouse": warehouse,
"company": company,
"selling_cost_center": selling_cost_center,
"buying_cost_center": buying_cost_center,
},
)
item.save()
else:
item = frappe.get_doc("Item", item_code)

View File

@@ -1,370 +1,90 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "",
"beta": 0,
"creation": "2015-05-19 05:12:30.344797",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Other",
"editable_grid": 1,
"actions": [],
"creation": "2015-05-19 05:12:30.344797",
"doctype": "DocType",
"document_type": "Other",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"variant_of",
"attribute",
"column_break_2",
"attribute_value",
"numeric_values",
"section_break_4",
"from_range",
"increment",
"column_break_8",
"to_range"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "variant_of",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Variant Of",
"length": 0,
"no_copy": 0,
"options": "Item",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "variant_of",
"fieldtype": "Link",
"label": "Variant Of",
"options": "Item",
"search_index": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "attribute",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Attribute",
"length": 0,
"no_copy": 0,
"options": "Item Attribute",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "attribute",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Attribute",
"options": "Item Attribute",
"reqd": 1,
"search_index": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_2",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "column_break_2",
"fieldtype": "Column Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fieldname": "attribute_value",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Attribute Value",
"length": 0,
"no_copy": 0,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "attribute_value",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Attribute Value"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "has_variants",
"fieldname": "numeric_values",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Numeric Values",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"default": "0",
"depends_on": "has_variants",
"fieldname": "numeric_values",
"fieldtype": "Check",
"label": "Numeric Values"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "numeric_values",
"fieldname": "section_break_4",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"depends_on": "numeric_values",
"fieldname": "section_break_4",
"fieldtype": "Section Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fieldname": "from_range",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "From Range",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "from_range",
"fieldtype": "Float",
"label": "From Range"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fieldname": "increment",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Increment",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "increment",
"fieldtype": "Float",
"label": "Increment"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_8",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "column_break_8",
"fieldtype": "Column Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fieldname": "to_range",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "To Range",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"fieldname": "to_range",
"fieldtype": "Float",
"label": "To Range"
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "",
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2019-01-03 15:36:59.129006",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item Variant Attribute",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 0,
"track_seen": 0,
"track_views": 0
],
"istable": 1,
"links": [],
"modified": "2023-07-14 17:15:19.112119",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item Variant Attribute",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC",
"states": []
}

View File

@@ -1069,88 +1069,6 @@ class TestPurchaseReceipt(FrappeTestCase):
pr1.reload()
pr1.cancel()
def test_stock_transfer_from_purchase_receipt(self):
pr1 = make_purchase_receipt(
warehouse="Work In Progress - TCP1", company="_Test Company with perpetual inventory"
)
pr = make_purchase_receipt(
company="_Test Company with perpetual inventory", warehouse="Stores - TCP1", do_not_save=1
)
pr.supplier_warehouse = ""
pr.items[0].from_warehouse = "Work In Progress - TCP1"
pr.submit()
gl_entries = get_gl_entries("Purchase Receipt", pr.name)
sl_entries = get_sl_entries("Purchase Receipt", pr.name)
self.assertFalse(gl_entries)
expected_sle = {"Work In Progress - TCP1": -5, "Stores - TCP1": 5}
for sle in sl_entries:
self.assertEqual(expected_sle[sle.warehouse], sle.actual_qty)
pr.cancel()
pr1.cancel()
def test_stock_transfer_from_purchase_receipt_with_valuation(self):
create_warehouse(
"_Test Warehouse for Valuation",
company="_Test Company with perpetual inventory",
properties={"account": "_Test Account Stock In Hand - TCP1"},
)
pr1 = make_purchase_receipt(
warehouse="_Test Warehouse for Valuation - TCP1",
company="_Test Company with perpetual inventory",
)
pr = make_purchase_receipt(
company="_Test Company with perpetual inventory", warehouse="Stores - TCP1", do_not_save=1
)
pr.items[0].from_warehouse = "_Test Warehouse for Valuation - TCP1"
pr.supplier_warehouse = ""
pr.append(
"taxes",
{
"charge_type": "On Net Total",
"account_head": "_Test Account Shipping Charges - TCP1",
"category": "Valuation and Total",
"cost_center": "Main - TCP1",
"description": "Test",
"rate": 9,
},
)
pr.submit()
gl_entries = get_gl_entries("Purchase Receipt", pr.name)
sl_entries = get_sl_entries("Purchase Receipt", pr.name)
expected_gle = [
["Stock In Hand - TCP1", 272.5, 0.0],
["_Test Account Stock In Hand - TCP1", 0.0, 250.0],
["_Test Account Shipping Charges - TCP1", 0.0, 22.5],
]
expected_sle = {"_Test Warehouse for Valuation - TCP1": -5, "Stores - TCP1": 5}
for sle in sl_entries:
self.assertEqual(expected_sle[sle.warehouse], sle.actual_qty)
for i, gle in enumerate(gl_entries):
self.assertEqual(gle.account, expected_gle[i][0])
self.assertEqual(gle.debit, expected_gle[i][1])
self.assertEqual(gle.credit, expected_gle[i][2])
pr.cancel()
pr1.cancel()
def test_subcontracted_pr_for_multi_transfer_batches(self):
from erpnext.buying.doctype.purchase_order.purchase_order import (
make_purchase_receipt,
@@ -1944,6 +1862,32 @@ class TestPurchaseReceipt(FrappeTestCase):
ste5.reload()
self.assertEqual(ste5.items[0].valuation_rate, 275.00)
ste6 = make_stock_entry(
purpose="Material Transfer",
posting_date=add_days(today(), -3),
source=warehouse1,
target=warehouse,
item_code=item_code,
qty=20,
company=pr.company,
)
ste6.reload()
self.assertEqual(ste6.items[0].valuation_rate, 275.00)
ste7 = make_stock_entry(
purpose="Material Transfer",
posting_date=add_days(today(), -3),
source=warehouse,
target=warehouse1,
item_code=item_code,
qty=20,
company=pr.company,
)
ste7.reload()
self.assertEqual(ste7.items[0].valuation_rate, 275.00)
create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company, charges=2500 * -1)
pr.reload()
@@ -1964,6 +1908,81 @@ class TestPurchaseReceipt(FrappeTestCase):
ste5.reload()
self.assertEqual(ste5.items[0].valuation_rate, valuation_rate)
ste6.reload()
self.assertEqual(ste6.items[0].valuation_rate, valuation_rate)
ste7.reload()
self.assertEqual(ste7.items[0].valuation_rate, valuation_rate)
def test_valuation_rate_in_return_purchase_receipt_for_moving_average(self):
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
from erpnext.stock.stock_ledger import get_previous_sle
# Step - 1: Create an Item (Valuation Method = Moving Average)
item_code = make_item(properties={"is_stock_item": 1, "valuation_method": "Moving Average"}).name
# Step - 2: Create a Purchase Receipt (Qty = 10, Rate = 100)
pr = make_purchase_receipt(qty=10, rate=100, item_code=item_code)
# Step - 3: Create a Material Receipt Stock Entry (Qty = 100, Basic Rate = 10)
warehouse = "_Test Warehouse - _TC"
make_stock_entry(
purpose="Material Receipt",
item_code=item_code,
to_warehouse=warehouse,
qty=100,
rate=10,
)
# Step - 4: Create a Material Issue Stock Entry (Qty = 100, Basic Rate = 18.18 [Auto Fetched])
make_stock_entry(
purpose="Material Issue", item_code=item_code, from_warehouse=warehouse, qty=100
)
# Step - 5: Create a Return Purchase Return (Qty = -8, Rate = 100 [Auto fetched])
return_pr = make_purchase_receipt(
is_return=1,
return_against=pr.name,
item_code=item_code,
qty=-8,
)
sle = frappe.db.get_value(
"Stock Ledger Entry",
{"voucher_no": return_pr.name, "voucher_detail_no": return_pr.items[0].name},
["posting_date", "posting_time", "outgoing_rate", "valuation_rate"],
as_dict=1,
)
previous_sle_valuation_rate = get_previous_sle(
{
"item_code": item_code,
"warehouse": warehouse,
"posting_date": sle.posting_date,
"posting_time": sle.posting_time,
}
).get("valuation_rate")
# Test - 1: Valuation Rate should be equal to Outgoing Rate
self.assertEqual(flt(sle.outgoing_rate, 2), flt(sle.valuation_rate, 2))
# Test - 2: Valuation Rate should be equal to Previous SLE Valuation Rate
self.assertEqual(flt(sle.valuation_rate, 2), flt(previous_sle_valuation_rate, 2))
def non_internal_transfer_purchase_receipt(self):
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
pr_doc = make_purchase_receipt(do_not_submit=True)
warehouse = create_warehouse("Internal Transfer Warehouse", pr_doc.company)
pr_doc.items[0].db_set("target_warehouse", "warehouse")
pr_doc.reload()
self.assertEqual(pr_doc.items[0].from_warehouse, warehouse.name)
pr_doc.save()
pr_doc.reload()
self.assertFalse(pr_doc.items[0].from_warehouse)
def prepare_data_for_internal_transfer():
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier

View File

@@ -5,7 +5,7 @@
from unittest.mock import MagicMock, call
import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import nowdate
from frappe.utils.data import add_to_date, today
@@ -173,6 +173,7 @@ class TestRepostItemValuation(FrappeTestCase, StockTestMixin):
riv.set_status("Skipped")
@change_settings("Stock Reposting Settings", {"item_based_reposting": 0})
def test_prevention_of_cancelled_transaction_riv(self):
frappe.flags.dont_execute_stock_reposts = True
@@ -295,6 +296,7 @@ class TestRepostItemValuation(FrappeTestCase, StockTestMixin):
accounts_settings.acc_frozen_upto = ""
accounts_settings.save()
@change_settings("Stock Reposting Settings", {"item_based_reposting": 0})
def test_create_repost_entry_for_cancelled_document(self):
pr = make_purchase_receipt(
company="_Test Company with perpetual inventory",

View File

@@ -1414,6 +1414,7 @@ class TestStockEntry(FrappeTestCase):
self.assertEqual(se.items[0].item_name, item.item_name)
self.assertEqual(se.items[0].stock_uom, item.stock_uom)
@change_settings("Stock Reposting Settings", {"item_based_reposting": 0})
def test_reposting_for_depedent_warehouse(self):
from erpnext.stock.doctype.repost_item_valuation.repost_item_valuation import repost_sl_entries
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse

View File

@@ -48,7 +48,7 @@
"label": "Limit timeslot for Stock Reposting"
},
{
"default": "0",
"default": "1",
"fieldname": "item_based_reposting",
"fieldtype": "Check",
"label": "Use Item based reposting"
@@ -57,7 +57,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2021-11-02 01:22:45.155841",
"modified": "2023-11-01 16:14:29.080697",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Reposting Settings",
@@ -77,4 +77,4 @@
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}
}

View File

@@ -751,6 +751,12 @@ def get_default_cost_center(args, item=None, item_group=None, brand=None, compan
data = frappe.get_attr(path)(args.get("item_code"), company)
if data and (data.selling_cost_center or data.buying_cost_center):
if args.get("customer") and data.selling_cost_center:
return data.selling_cost_center
elif args.get("supplier") and data.buying_cost_center:
return data.buying_cost_center
return data.selling_cost_center or data.buying_cost_center
if not cost_center and args.get("cost_center"):

View File

@@ -33,5 +33,43 @@ frappe.query_reports["Stock and Account Value Comparison"] = {
"fieldtype": "Date",
"default": frappe.datetime.get_today(),
},
]
],
get_datatable_options(options) {
return Object.assign(options, {
checkboxColumn: true,
});
},
onload(report) {
report.page.add_inner_button(__("Create Reposting Entries"), function() {
let message = `
<div>
<p>
Reposting Entries will change the value of
accounts Stock In Hand, and Stock Expenses
in the Trial Balance report and will also change
the Balance Value in the Stock Balance report.
</p>
<p>Are you sure you want to create Reposting Entries?</p>
</div>`;
let indexes = frappe.query_report.datatable.rowmanager.getCheckedRows();
let selected_rows = indexes.map(i => frappe.query_report.data[i]);
if (!selected_rows.length) {
frappe.throw(__("Please select rows to create Reposting Entries"));
}
frappe.confirm(__(message), () => {
frappe.call({
method: "erpnext.stock.report.stock_and_account_value_comparison.stock_and_account_value_comparison.create_reposting_entries",
args: {
rows: selected_rows,
company: frappe.query_report.get_filter_values().company
}
});
});
});
}
};

View File

@@ -4,6 +4,7 @@
import frappe
from frappe import _
from frappe.utils import get_link_to_form, parse_json
import erpnext
from erpnext.accounts.utils import get_currency_precision, get_stock_accounts
@@ -134,3 +135,35 @@ def get_columns(filters):
"width": "120",
},
]
@frappe.whitelist()
def create_reposting_entries(rows, company):
if isinstance(rows, str):
rows = parse_json(rows)
entries = []
for row in rows:
row = frappe._dict(row)
try:
doc = frappe.get_doc(
{
"doctype": "Repost Item Valuation",
"based_on": "Transaction",
"status": "Queued",
"voucher_type": row.voucher_type,
"voucher_no": row.voucher_no,
"posting_date": row.posting_date,
"company": company,
"allow_nagative_stock": 1,
}
).submit()
entries.append(get_link_to_form("Repost Item Valuation", doc.name))
except frappe.DuplicateEntryError:
pass
if entries:
entries = ", ".join(entries)
frappe.msgprint(_("Reposting entries created: {0}").format(entries))

View File

@@ -3,23 +3,23 @@
/* eslint-disable */
const DIFFERNCE_FIELD_NAMES = [
"difference_in_qty",
"fifo_qty_diff",
"fifo_value_diff",
"fifo_valuation_diff",
"valuation_diff",
"fifo_difference_diff",
"diff_value_diff"
'difference_in_qty',
'fifo_qty_diff',
'fifo_value_diff',
'fifo_valuation_diff',
'valuation_diff',
'fifo_difference_diff',
'diff_value_diff'
];
frappe.query_reports["Stock Ledger Invariant Check"] = {
"filters": [
frappe.query_reports['Stock Ledger Invariant Check'] = {
'filters': [
{
"fieldname": "item_code",
"fieldtype": "Link",
"label": "Item",
"mandatory": 1,
"options": "Item",
'fieldname': 'item_code',
'fieldtype': 'Link',
'label': 'Item',
'mandatory': 1,
'options': 'Item',
get_query: function() {
return {
filters: {is_stock_item: 1, has_serial_no: 0}
@@ -27,18 +27,61 @@ frappe.query_reports["Stock Ledger Invariant Check"] = {
}
},
{
"fieldname": "warehouse",
"fieldtype": "Link",
"label": "Warehouse",
"mandatory": 1,
"options": "Warehouse",
'fieldname': 'warehouse',
'fieldtype': 'Link',
'label': 'Warehouse',
'mandatory': 1,
'options': 'Warehouse',
}
],
formatter (value, row, column, data, default_formatter) {
value = default_formatter(value, row, column, data);
if (DIFFERNCE_FIELD_NAMES.includes(column.fieldname) && Math.abs(data[column.fieldname]) > 0.001) {
value = "<span style='color:red'>" + value + "</span>";
value = '<span style="color:red">' + value + '</span>';
}
return value;
},
get_datatable_options(options) {
return Object.assign(options, {
checkboxColumn: true,
});
},
onload(report) {
report.page.add_inner_button(__('Create Reposting Entry'), () => {
let message = `
<div>
<p>
Reposting Entry will change the value of
accounts Stock In Hand, and Stock Expenses
in the Trial Balance report and will also change
the Balance Value in the Stock Balance report.
</p>
<p>Are you sure you want to create a Reposting Entry?</p>
</div>`;
let indexes = frappe.query_report.datatable.rowmanager.getCheckedRows();
let selected_rows = indexes.map(i => frappe.query_report.data[i]);
if (!selected_rows.length) {
frappe.throw(__('Please select a row to create a Reposting Entry'));
}
else if (selected_rows.length > 1) {
frappe.throw(__('Please select only one row to create a Reposting Entry'));
}
else {
frappe.confirm(__(message), () => {
frappe.call({
method: 'erpnext.stock.report.stock_ledger_invariant_check.stock_ledger_invariant_check.create_reposting_entries',
args: {
rows: selected_rows,
item_code: frappe.query_report.get_filter_values().item_code,
warehouse: frappe.query_report.get_filter_values().warehouse,
}
});
});
}
});
},
};

View File

@@ -4,6 +4,8 @@
import json
import frappe
from frappe import _
from frappe.utils import get_link_to_form, parse_json
SLE_FIELDS = (
"name",
@@ -247,3 +249,35 @@ def get_columns():
"label": "H - J",
},
]
@frappe.whitelist()
def create_reposting_entries(rows, item_code=None, warehouse=None):
if isinstance(rows, str):
rows = parse_json(rows)
entries = []
for row in rows:
row = frappe._dict(row)
try:
doc = frappe.get_doc(
{
"doctype": "Repost Item Valuation",
"based_on": "Item and Warehouse",
"status": "Queued",
"item_code": item_code or row.item_code,
"warehouse": warehouse or row.warehouse,
"posting_date": row.posting_date,
"posting_time": row.posting_time,
"allow_nagative_stock": 1,
}
).submit()
entries.append(get_link_to_form("Repost Item Valuation", doc.name))
except frappe.DuplicateEntryError:
continue
if entries:
entries = ", ".join(entries)
frappe.msgprint(_("Reposting entries created: {0}").format(entries))

View File

@@ -0,0 +1,101 @@
// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
const DIFFERENCE_FIELD_NAMES = [
"difference_in_qty",
"fifo_qty_diff",
"fifo_value_diff",
"fifo_valuation_diff",
"valuation_diff",
"fifo_difference_diff",
"diff_value_diff"
];
frappe.query_reports["Stock Ledger Variance"] = {
"filters": [
{
"fieldname": "item_code",
"fieldtype": "Link",
"label": "Item",
"options": "Item",
get_query: function() {
return {
filters: {is_stock_item: 1, has_serial_no: 0}
}
}
},
{
"fieldname": "warehouse",
"fieldtype": "Link",
"label": "Warehouse",
"options": "Warehouse",
get_query: function() {
return {
filters: {is_group: 0, disabled: 0}
}
}
},
{
"fieldname": "difference_in",
"fieldtype": "Select",
"label": "Difference In",
"options": [
"",
"Qty",
"Value",
"Valuation",
],
},
{
"fieldname": "include_disabled",
"fieldtype": "Check",
"label": "Include Disabled",
}
],
formatter (value, row, column, data, default_formatter) {
value = default_formatter(value, row, column, data);
if (DIFFERENCE_FIELD_NAMES.includes(column.fieldname) && Math.abs(data[column.fieldname]) > 0.001) {
value = "<span style='color:red'>" + value + "</span>";
}
return value;
},
get_datatable_options(options) {
return Object.assign(options, {
checkboxColumn: true,
});
},
onload(report) {
report.page.add_inner_button(__('Create Reposting Entries'), () => {
let message = `
<div>
<p>
Reposting Entries will change the value of
accounts Stock In Hand, and Stock Expenses
in the Trial Balance report and will also change
the Balance Value in the Stock Balance report.
</p>
<p>Are you sure you want to create Reposting Entries?</p>
</div>`;
let indexes = frappe.query_report.datatable.rowmanager.getCheckedRows();
let selected_rows = indexes.map(i => frappe.query_report.data[i]);
if (!selected_rows.length) {
frappe.throw(__("Please select rows to create Reposting Entries"));
}
frappe.confirm(__(message), () => {
frappe.call({
method: 'erpnext.stock.report.stock_ledger_invariant_check.stock_ledger_invariant_check.create_reposting_entries',
args: {
rows: selected_rows,
}
});
});
});
},
};

View File

@@ -0,0 +1,22 @@
{
"add_total_row": 0,
"columns": [],
"creation": "2023-09-20 10:44:19.414449",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"filters": [],
"idx": 0,
"is_standard": "Yes",
"letterhead": null,
"modified": "2023-09-20 10:44:19.414449",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Ledger Variance",
"owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "Stock Ledger Entry",
"report_name": "Stock Ledger Variance",
"report_type": "Script Report",
"roles": []
}

View File

@@ -0,0 +1,279 @@
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import frappe
from frappe import _
from frappe.utils import cint, flt
from erpnext.stock.report.stock_ledger_invariant_check.stock_ledger_invariant_check import (
get_data as stock_ledger_invariant_check,
)
def execute(filters=None):
columns, data = [], []
filters = frappe._dict(filters or {})
columns = get_columns()
data = get_data(filters)
return columns, data
def get_columns():
return [
{
"fieldname": "name",
"fieldtype": "Link",
"label": _("Stock Ledger Entry"),
"options": "Stock Ledger Entry",
},
{
"fieldname": "posting_date",
"fieldtype": "Data",
"label": _("Posting Date"),
},
{
"fieldname": "posting_time",
"fieldtype": "Data",
"label": _("Posting Time"),
},
{
"fieldname": "creation",
"fieldtype": "Data",
"label": _("Creation"),
},
{
"fieldname": "item_code",
"fieldtype": "Link",
"label": _("Item"),
"options": "Item",
},
{
"fieldname": "warehouse",
"fieldtype": "Link",
"label": _("Warehouse"),
"options": "Warehouse",
},
{
"fieldname": "voucher_type",
"fieldtype": "Link",
"label": _("Voucher Type"),
"options": "DocType",
},
{
"fieldname": "voucher_no",
"fieldtype": "Dynamic Link",
"label": _("Voucher No"),
"options": "voucher_type",
},
{
"fieldname": "batch_no",
"fieldtype": "Link",
"label": _("Batch"),
"options": "Batch",
},
{
"fieldname": "use_batchwise_valuation",
"fieldtype": "Check",
"label": _("Batchwise Valuation"),
},
{
"fieldname": "actual_qty",
"fieldtype": "Float",
"label": _("Qty Change"),
},
{
"fieldname": "incoming_rate",
"fieldtype": "Float",
"label": _("Incoming Rate"),
},
{
"fieldname": "consumption_rate",
"fieldtype": "Float",
"label": _("Consumption Rate"),
},
{
"fieldname": "qty_after_transaction",
"fieldtype": "Float",
"label": _("(A) Qty After Transaction"),
},
{
"fieldname": "expected_qty_after_transaction",
"fieldtype": "Float",
"label": _("(B) Expected Qty After Transaction"),
},
{
"fieldname": "difference_in_qty",
"fieldtype": "Float",
"label": _("A - B"),
},
{
"fieldname": "stock_queue",
"fieldtype": "Data",
"label": _("FIFO/LIFO Queue"),
},
{
"fieldname": "fifo_queue_qty",
"fieldtype": "Float",
"label": _("(C) Total Qty in Queue"),
},
{
"fieldname": "fifo_qty_diff",
"fieldtype": "Float",
"label": _("A - C"),
},
{
"fieldname": "stock_value",
"fieldtype": "Float",
"label": _("(D) Balance Stock Value"),
},
{
"fieldname": "fifo_stock_value",
"fieldtype": "Float",
"label": _("(E) Balance Stock Value in Queue"),
},
{
"fieldname": "fifo_value_diff",
"fieldtype": "Float",
"label": _("D - E"),
},
{
"fieldname": "stock_value_difference",
"fieldtype": "Float",
"label": _("(F) Change in Stock Value"),
},
{
"fieldname": "stock_value_from_diff",
"fieldtype": "Float",
"label": _("(G) Sum of Change in Stock Value"),
},
{
"fieldname": "diff_value_diff",
"fieldtype": "Float",
"label": _("G - D"),
},
{
"fieldname": "fifo_stock_diff",
"fieldtype": "Float",
"label": _("(H) Change in Stock Value (FIFO Queue)"),
},
{
"fieldname": "fifo_difference_diff",
"fieldtype": "Float",
"label": _("H - F"),
},
{
"fieldname": "valuation_rate",
"fieldtype": "Float",
"label": _("(I) Valuation Rate"),
},
{
"fieldname": "fifo_valuation_rate",
"fieldtype": "Float",
"label": _("(J) Valuation Rate as per FIFO"),
},
{
"fieldname": "fifo_valuation_diff",
"fieldtype": "Float",
"label": _("I - J"),
},
{
"fieldname": "balance_value_by_qty",
"fieldtype": "Float",
"label": _("(K) Valuation = Value (D) ÷ Qty (A)"),
},
{
"fieldname": "valuation_diff",
"fieldtype": "Float",
"label": _("I - K"),
},
]
def get_data(filters=None):
filters = frappe._dict(filters or {})
item_warehouse_map = get_item_warehouse_combinations(filters)
data = []
if item_warehouse_map:
precision = cint(frappe.db.get_single_value("System Settings", "float_precision"))
for item_warehouse in item_warehouse_map:
report_data = stock_ledger_invariant_check(item_warehouse)
if not report_data:
continue
for row in report_data:
if has_difference(row, precision, filters.difference_in):
data.append(add_item_warehouse_details(row, item_warehouse))
break
return data
def get_item_warehouse_combinations(filters: dict = None) -> dict:
filters = frappe._dict(filters or {})
bin = frappe.qb.DocType("Bin")
item = frappe.qb.DocType("Item")
warehouse = frappe.qb.DocType("Warehouse")
query = (
frappe.qb.from_(bin)
.inner_join(item)
.on(bin.item_code == item.name)
.inner_join(warehouse)
.on(bin.warehouse == warehouse.name)
.select(
bin.item_code,
bin.warehouse,
)
.where((item.is_stock_item == 1) & (item.has_serial_no == 0) & (warehouse.is_group == 0))
)
if filters.item_code:
query = query.where(item.name == filters.item_code)
if filters.warehouse:
query = query.where(warehouse.name == filters.warehouse)
if not filters.include_disabled:
query = query.where((item.disabled == 0) & (warehouse.disabled == 0))
return query.run(as_dict=1)
def has_difference(row, precision, difference_in):
has_qty_difference = flt(row.difference_in_qty, precision) or flt(row.fifo_qty_diff, precision)
has_value_difference = (
flt(row.diff_value_diff, precision)
or flt(row.fifo_value_diff, precision)
or flt(row.fifo_difference_diff, precision)
)
has_valuation_difference = flt(row.valuation_diff, precision) or flt(
row.fifo_valuation_diff, precision
)
if difference_in == "Qty" and has_qty_difference:
return True
elif difference_in == "Value" and has_value_difference:
return True
elif difference_in == "Valuation" and has_valuation_difference:
return True
elif difference_in not in ["Qty", "Value", "Valuation"] and (
has_qty_difference or has_value_difference or has_valuation_difference
):
return True
return False
def add_item_warehouse_details(row, item_warehouse):
row.update(
{
"item_code": item_warehouse.item_code,
"warehouse": item_warehouse.warehouse,
}
)
return row

View File

@@ -278,6 +278,8 @@ def update_args_in_repost_item_valuation(
frappe.publish_realtime(
"item_reposting_progress",
{"name": doc.name, "items_to_be_repost": json.dumps(args, default=str), "current_index": index},
doctype=doc.doctype,
docname=doc.name,
)
@@ -510,13 +512,28 @@ class update_entries_after(object):
existing_sle_posting_date = (
self.distinct_item_warehouses[key].get("sle", {}).get("posting_date")
)
dependent_voucher_detail_nos = self.get_dependent_voucher_detail_nos(key)
if getdate(dependant_sle.posting_date) < getdate(existing_sle_posting_date):
val.sle_changed = True
dependent_voucher_detail_nos.append(dependant_sle.voucher_detail_no)
val.dependent_voucher_detail_nos = dependent_voucher_detail_nos
self.distinct_item_warehouses[key] = val
self.new_items_found = True
elif self.distinct_item_warehouses[key].get("reposting_status"):
self.distinct_item_warehouses[key] = val
elif dependant_sle.voucher_detail_no not in set(dependent_voucher_detail_nos):
# Future dependent voucher needs to be repost to get the correct stock value
# If dependent voucher has not reposted, then add it to the list
dependent_voucher_detail_nos.append(dependant_sle.voucher_detail_no)
self.new_items_found = True
val.dependent_voucher_detail_nos = dependent_voucher_detail_nos
self.distinct_item_warehouses[key] = val
def get_dependent_voucher_detail_nos(self, key):
if "dependent_voucher_detail_nos" not in self.distinct_item_warehouses[key]:
self.distinct_item_warehouses[key].dependent_voucher_detail_nos = []
return self.distinct_item_warehouses[key].dependent_voucher_detail_nos
def process_sle(self, sle):
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
@@ -641,14 +658,16 @@ class update_entries_after(object):
get_rate_for_return, # don't move this import to top
)
rate = get_rate_for_return(
sle.voucher_type,
sle.voucher_no,
sle.item_code,
voucher_detail_no=sle.voucher_detail_no,
sle=sle,
)
if self.valuation_method == "Moving Average":
rate = flt(self.data[self.args.warehouse].previous_sle.valuation_rate)
else:
rate = get_rate_for_return(
sle.voucher_type,
sle.voucher_no,
sle.item_code,
voucher_detail_no=sle.voucher_detail_no,
sle=sle,
)
elif (
sle.voucher_type in ["Purchase Receipt", "Purchase Invoice"]
and sle.voucher_detail_no
@@ -1162,6 +1181,7 @@ def get_sle_by_voucher_detail_no(voucher_detail_no, excluded_sle=None):
"qty_after_transaction",
"posting_date",
"posting_time",
"voucher_detail_no",
"timestamp(posting_date, posting_time) as timestamp",
],
as_dict=1,

View File

@@ -15,7 +15,7 @@
{% for item, taxes in itemised_tax.items() %}
<tr>
<td>{{ item }}</td>
<td class='text-right'>
<td class="text-right">
{% if doc.get('is_return') %}
{{ frappe.utils.fmt_money((itemised_taxable_amount.get(item, 0))|abs, None, doc.currency) }}
{% else %}
@@ -25,7 +25,7 @@
{% for tax_account in tax_accounts %}
{% set tax_details = taxes.get(tax_account) %}
{% if tax_details %}
<td class='text-right'>
<td class="text-right">
{% if tax_details.tax_rate or not tax_details.tax_amount %}
({{ tax_details.tax_rate }}%)
{% endif %}

View File

@@ -7,7 +7,7 @@
{% if d.thumbnail or d.image %}
{{ product_image(d.thumbnail or d.image, no_border=True) }}
{% else %}
<div class="no-image-cart-item" style="min-height: 100px;">
<div class="no-image-cart-item" style="min-height: 50px;">
{{ frappe.utils.get_abbr(d.item_name) or "NA" }}
</div>
{% endif %}

View File

@@ -81,7 +81,7 @@ rfq = Class.extend({
doc: doc
},
btn: this,
callback: function(r){
callback: function(r) {
frappe.unfreeze();
if(r.message){
$('.btn-sm').hide()

View File

@@ -1,19 +1,25 @@
{% from "erpnext/templates/includes/macros.html" import product_image_square, product_image %}
{% macro item_name_and_description(d, doc) %}
<div class="row">
<div class="col-3">
{{ product_image(d.image) }}
</div>
<div class="col-9">
{{ d.item_code }}
<p class="text-muted small">{{ d.description }}</p>
<div class="row">
<div class="col-3">
{% if d.image %}
{{ product_image(d.image) }}
{% else %}
<div class="website-image h-100 w-100" style="background-color:var(--gray-100);text-align: center;line-height: 3.6;">
{{ frappe.utils.get_abbr(d.item_name)}}
</div>
{% endif %}
</div>
<div class="col-9">
{{ d.item_code }}
<p class="text-muted small">{{ d.description }}</p>
{% set supplier_part_no = frappe.db.get_value("Item Supplier", {'parent': d.item_code, 'supplier': doc.supplier}, "supplier_part_no") %}
<p class="text-muted small supplier-part-no">
{% if supplier_part_no %}
{{_("Supplier Part No") + ": "+ supplier_part_no}}
{% endif %}
</p>
</div>
</div>
</div>
</div>
{% endmacro %}

View File

@@ -153,7 +153,6 @@
</div>
{% endif %}
{% if attachments %}
<div class="order-item-table">
<div class="row order-items order-item-header text-muted">
@@ -181,6 +180,7 @@
{% endif %}
{% endblock %}
{% block script %}
<script> {% include "templates/pages/order.js" %} </script>
<script>

View File

@@ -1,7 +1,7 @@
{% extends "templates/web.html" %}
{% block header %}
<h1>{{ doc.name }}</h1>
<h1 style="margin-top: 10px;">{{ doc.name }}</h1>
{% endblock %}
{% block script %}
@@ -16,7 +16,7 @@
{% if doc.items %}
<button class="btn btn-primary btn-sm"
type="button">
{{ _("Submit") }}</button>
{{ _("Make Quotation") }}</button>
{% endif %}
{% endblock %}

View File

@@ -158,7 +158,7 @@ Advertising,Werbung,
Aerospace,Luft- und Raumfahrt,
Against,Zu,
Against Account,Gegenkonto,
Against Journal Entry {0} does not have any unmatched {1} entry,"""Zu Buchungssatz"" {0} hat nur abgeglichene {1} Buchungen",
Against Journal Entry {0} does not have any unmatched {1} entry,Buchungssatz {0} hat keinen offenen Eintrag auf der {1}-Seite,
Against Journal Entry {0} is already adjusted against some other voucher,"""Zu Buchungssatz"" {0} ist bereits mit einem anderen Beleg abgeglichen",
Against Supplier Invoice {0} dated {1},Zu Eingangsrechnung {0} vom {1},
Against Voucher,Gegenbeleg,
@@ -209,10 +209,10 @@ Amount of Integrated Tax,Betrag der integrierten Steuer,
Amount of TDS Deducted,Betrag der abgezogenen TDS,
Amount should not be less than zero.,Betrag sollte nicht kleiner als Null sein.,
Amount to Bill,Rechnungsbetrag,
Amount {0} {1} against {2} {3},Menge {0} {1} gegen {2} {3},
Amount {0} {1} deducted against {2},Menge {0} {1} abgezogen gegen {2},
Amount {0} {1} transferred from {2} to {3},Menge {0} {1} übertragen von {2} auf {3},
Amount {0} {1} {2} {3},Menge {0} {1} {2} {3},
Amount {0} {1} against {2} {3},Betrag {0} {1} gegen {2} {3},
Amount {0} {1} deducted against {2},Betrag {0} {1} abgezogen gegen {2},
Amount {0} {1} transferred from {2} to {3},Betrag {0} {1} wurde von {2} zu {3} transferiert,
Amount {0} {1} {2} {3},Betrag {0} {1} {2} {3},
Amt,Menge,
"An Item Group exists with same name, please change the item name or rename the item group",Eine Artikelgruppe mit dem gleichen Namen existiert bereits. Bitte den Artikelnamen ändern oder die Artikelgruppe umbenennen,
An academic term with this 'Academic Year' {0} and 'Term Name' {1} already exists. Please modify these entries and try again.,"Ein Semester mit ""Semesterjahr""'{0} und ""Semesternamen"" {1} ist bereits vorhanden. Bitte ändern Sie diese entsprechend und versuchen Sie es erneut.",
@@ -281,7 +281,7 @@ Asset Name,Name Vermögenswert,
Asset Received But Not Billed,"Vermögenswert erhalten, aber nicht in Rechnung gestellt",
Asset Value Adjustment,Anpassung Vermögenswert,
"Asset cannot be cancelled, as it is already {0}","Vermögenswert kann nicht rückgängig gemacht werden, da es ohnehin schon {0} ist",
Asset scrapped via Journal Entry {0},Vermögenswert über Journaleintrag {0} entsorgt,
Asset scrapped via Journal Entry {0},Vermögenswert über Buchungssatz {0} entsorgt,
"Asset {0} cannot be scrapped, as it is already {1}",Anlagewert-{0} ist bereits entsorgt {1},
Asset {0} does not belong to company {1},Vermögenswert {0} gehört nicht zu Unternehmen {1}.,
Asset {0} must be submitted,Vermögenswert {0} muss eingereicht werden.,
@@ -369,7 +369,7 @@ Base,Basis,
Base URL,Basis-URL,
Based On,Basiert auf,
Based On Payment Terms,Basierend auf Zahlungsbedingungen,
Basic,Grundeinkommen,
Basic,Basic,
Batch,Charge,
Batch Entries,Batch-Einträge,
Batch ID is mandatory,Batch-ID ist obligatorisch,
@@ -442,7 +442,7 @@ Can be approved by {0},Kann von {0} genehmigt werden,
"Can not filter based on Account, if grouped by Account","Wenn nach Konto gruppiert wurde, kann nicht auf Grundlage des Kontos gefiltert werden.",
"Can not filter based on Voucher No, if grouped by Voucher","Wenn nach Beleg gruppiert wurde, kann nicht auf Grundlage von Belegen gefiltert werden.",
"Can not mark Inpatient Record Discharged, there are Unbilled Invoices {0}","Kann den entlassenen Krankenhauskatheter nicht markieren, es gibt keine fakturierten Rechnungen {0}",
Can only make payment against unbilled {0},Zahlung kann nur zu einer noch nicht abgerechneten {0} erstellt werden,
Can only make payment against unbilled {0},Zahlung kann nur zu einem noch nicht abgerechneten Beleg vom Typ {0} erstellt werden,
Can refer row only if the charge type is 'On Previous Row Amount' or 'Previous Row Total',"Kann sich nur auf eine Zeile beziehen, wenn die Berechnungsart der Kosten entweder ""auf vorherige Zeilensumme"" oder ""auf vorherigen Zeilenbetrag"" ist",
"Can't change valuation method, as there are transactions against some items which does not have it's own valuation method","Kann die Bewertungsmethode nicht ändern, da es Transaktionen gegen einige Posten gibt, für die es keine eigene Bewertungsmethode gibt",
Can't create standard criteria. Please rename the criteria,Kann keine Standardkriterien erstellen. Bitte benennen Sie die Kriterien um,
@@ -450,7 +450,7 @@ Cancel,Abbrechen,
Cancel Material Visit {0} before cancelling this Warranty Claim,Materialkontrolle {0} stornieren vor Abbruch dieses Garantieantrags,
Cancel Material Visits {0} before cancelling this Maintenance Visit,Materialkontrolle {0} stornieren vor Abbruch dieses Wartungsbesuchs,
Cancel Subscription,Abonnement beenden,
Cancel the journal entry {0} first,Brechen Sie zuerst den Journaleintrag {0} ab,
Cancel the journal entry {0} first,Brechen Sie zuerst den Buchungssatz {0} ab,
Canceled,Abgebrochen,
"Cannot Submit, Employees left to mark attendance","Kann nicht übergeben werden, Mitarbeiter sind zur Teilnahme zugelassen",
Cannot be a fixed asset item as Stock Ledger is created.,"Kann keine Anlageposition sein, wenn das Stock Ledger erstellt wird.",
@@ -474,11 +474,13 @@ Cannot deduct when category is for 'Valuation' or 'Vaulation and Total',"Kann ni
"Cannot delete Serial No {0}, as it is used in stock transactions","Die Seriennummer {0} kann nicht gelöscht werden, da sie in Lagertransaktionen verwendet wird",
Cannot enroll more than {0} students for this student group.,Kann nicht mehr als {0} Studenten für diese Studentengruppe einschreiben.,
Cannot find active Leave Period,Aktive Abwesenheitszeit kann nicht gefunden werden,
Cannot produce more Item {0} than Sales Order quantity {1},"Es können nicht mehr Artikel {0} produziert werden, als die über Kundenaufträge bestellte Stückzahl {1}",
Cannot produce more Item {0} than Sales Order quantity {1},"Es können nicht mehr Artikel {0} produziert werden, als die über den Auftrag bestellte Stückzahl {1}",
Cannot pay to Customer without any negative outstanding invoice,"Es kann nicht an den Kunden gezahlt werden, ohne dass eine Gutschrift vorhanden ist",
Cannot promote Employee with status Left,Mitarbeiter mit Status &quot;Links&quot; kann nicht gefördert werden,
Cannot receive from Supplier without any negative outstanding invoice,"Es kann nicht vom Lieferanten empfangen werden, ohne dass eine Gutschrift vorhanden ist",
Cannot refer row number greater than or equal to current row number for this Charge type,"Für diese Berechnungsart kann keine Zeilennummern zugeschrieben werden, die größer oder gleich der aktuellen Zeilennummer ist",
Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row,"Die Berechnungsart kann für die erste Zeile nicht auf ""bezogen auf Menge der vorhergenden Zeile"" oder auf ""bezogen auf Gesamtmenge der vorhergenden Zeile"" gesetzt werden",
Cannot set as Lost as Sales Order is made.,"Kann nicht als verloren gekennzeichnet werden, da ein Kundenauftrag dazu existiert.",
Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row,"Die Berechnungsart kann für die erste Zeile nicht auf „Bezogen auf Betrag der vorhergenden Zeile oder auf „Bezogen auf Gesamtbetrag der vorhergenden Zeilen“ gesetzt werden",
Cannot set as Lost as Sales Order is made.,"Kann nicht als verloren gekennzeichnet werden, da ein Auftrag dazu existiert.",
Cannot set authorization on basis of Discount for {0},Genehmigung kann nicht auf der Basis des Rabattes für {0} festgelegt werden,
Cannot set multiple Item Defaults for a company.,Es können nicht mehrere Artikelstandards für ein Unternehmen festgelegt werden.,
Cannot set quantity less than delivered quantity,Menge kann nicht kleiner als gelieferte Menge sein,
@@ -514,7 +516,7 @@ Change Template Code,Vorlagencode ändern,
Changing Customer Group for the selected Customer is not allowed.,Die Änderung der Kundengruppe für den ausgewählten Kunden ist nicht zulässig.,
Chapter,Gruppe,
Chapter information.,Gruppeninformation,
Charge of type 'Actual' in row {0} cannot be included in Item Rate,"Kosten für den Typ ""real"" in Zeile {0} können nicht in den Artikelpreis mit eingeschlossen werden",
Charge of type 'Actual' in row {0} cannot be included in Item Rate or Paid Amount,"Kosten für den Typ „Tatsächlich“ in Zeile {0} können nicht in den Artikelpreis oder den bezahlen Betrag einfließen",
Chargeble,Belastung,
Charges are updated in Purchase Receipt against each item,Kosten werden im Kaufbeleg für jede Position aktualisiert,
"Charges will be distributed proportionately based on item qty or amount, as per your selection",Die Kosten werden gemäß Ihrer Wahl anteilig verteilt basierend auf Artikelmenge oder -preis,
@@ -657,7 +659,7 @@ Create Inter Company Journal Entry,Erstellen Sie einen Inter Company Journal Ein
Create Invoice,Rechnung erstellen,
Create Invoices,Rechnungen erstellen,
Create Job Card,Jobkarte erstellen,
Create Journal Entry,Journaleintrag erstellen,
Create Journal Entry,Buchungssatz erstellen,
Create Lead,Lead erstellen,
Create Leads,Leads erstellen,
Create Maintenance Visit,Wartungsbesuch anlegen,
@@ -837,7 +839,7 @@ Did not find any item called {0},Hat keinen Artikel finden genannt {0},
Diff Qty,Diff Menge,
Difference Account,Differenzkonto,
"Difference Account must be a Asset/Liability type account, since this Stock Reconciliation is an Opening Entry","Differenzkonto muss ein Vermögens-/Verbindlichkeiten-Konto sein, da dieser Lagerabgleich eine Eröffnungsbuchung ist",
Difference Amount,Differenzmenge,
Difference Amount,Differenzbetrag,
Difference Amount must be zero,Differenzbetrag muss Null sein,
Different UOM for items will lead to incorrect (Total) Net Weight value. Make sure that Net Weight of each item is in the same UOM.,"Unterschiedliche Maßeinheiten für Artikel führen zu falschen Werten für das (Gesamt-)Nettogewicht. Es muss sicher gestellt sein, dass das Nettogewicht jedes einzelnen Artikels in der gleichen Maßeinheit angegeben ist.",
Direct Expenses,Direkte Aufwendungen,
@@ -1032,7 +1034,7 @@ Fieldname,Feldname,
Fields,Felder,
Fill the form and save it,Formular ausfüllen und speichern,
Filter Employees By (Optional),Mitarbeiter filtern nach (optional),
"Filter Fields Row #{0}: Fieldname <b>{1}</b> must be of type ""Link"" or ""Table MultiSelect""",Filterfelder Zeile # {0}: Feldname <b>{1}</b> muss vom Typ &quot;Link&quot; oder &quot;Tabelle MultiSelect&quot; sein,
"Filter Fields Row #{0}: Fieldname <b>{1}</b> must be of type ""Link"" or ""Table MultiSelect""",Filterfelder Zeile {0}: Feldname <b>{1}</b> muss vom Typ &quot;Link&quot; oder &quot;Tabelle MultiSelect&quot; sein,
Filter Total Zero Qty,Gesamtmenge filtern,
Finance Book,Finanzbuch,
Financial / accounting year.,Finanz-/Rechnungsjahr,
@@ -1306,6 +1308,7 @@ Invalid GSTIN! A GSTIN must have 15 characters.,Ungültige GSTIN! Eine GSTIN mus
Invalid GSTIN! First 2 digits of GSTIN should match with State number {0}.,Ungültige GSTIN! Die ersten beiden Ziffern von GSTIN sollten mit der Statusnummer {0} übereinstimmen.,
Invalid GSTIN! The input you've entered doesn't match the format of GSTIN.,Ungültige GSTIN! Die von Ihnen eingegebene Eingabe stimmt nicht mit dem Format von GSTIN überein.,
Invalid Posting Time,Ungültige Buchungszeit,
Invalid Purchase Invoice,Ungültige Einkaufsrechnung,
Invalid attribute {0} {1},Ungültiges Attribut {0} {1},
Invalid quantity specified for item {0}. Quantity should be greater than 0.,Ungültzige Anzahl für Artikel {0} angegeben. Anzahl sollte größer als 0 sein.,
Invalid reference {0} {1},Ungültige Referenz {0} {1},
@@ -1771,7 +1774,7 @@ Odometer,Tacho,
Office Equipments,Büroausstattung,
Office Maintenance Expenses,Büro-Wartungskosten,
Office Rent,Büromiete,
On Hold,In Wartestellung,
On Hold,Auf Eis gelegt,
On Net Total,Auf Nettosumme,
One customer can be part of only single Loyalty Program.,Ein Kunde kann Teil eines einzigen Treueprogramms sein.,
Online Auctions,Online-Auktionen,
@@ -1861,7 +1864,7 @@ Packing Slip,Packzettel,
Packing Slip(s) cancelled,Packzettel storniert,
Paid,Bezahlt,
Paid Amount,Gezahlter Betrag,
Paid Amount cannot be greater than total negative outstanding amount {0},Gezahlten Betrag kann nicht größer sein als die Gesamt negativ ausstehenden Betrag {0},
Paid Amount cannot be greater than total negative outstanding amount {0},"Der gezahlte Betrag darf nicht größer sein als der gesamte, negative, ausstehende Betrag {0}",
Paid amount + Write Off Amount can not be greater than Grand Total,Summe aus gezahltem Betrag + ausgebuchter Betrag darf nicht größer der Gesamtsumme sein,
Paid and Not Delivered,Bezahlt und nicht ausgeliefert,
Parameter,Parameter,
@@ -1914,7 +1917,7 @@ Payment Terms,Zahlungsbedingungen,
Payment Terms Template,Vorlage Zahlungsbedingungen,
Payment Terms based on conditions,Zahlungsbedingungen basieren auf Bedingungen,
Payment Type,Zahlungsart,
"Payment Type must be one of Receive, Pay and Internal Transfer","Zahlungsart muss eine der Receive sein, Pay und interne Übertragung",
"Payment Type must be one of Receive, Pay and Internal Transfer","Zahlungsart muss entweder 'Empfangen', 'Zahlen' oder 'Interner Transfer' sein",
Payment against {0} {1} cannot be greater than Outstanding Amount {2},Zahlung zu {0} {1} kann nicht größer als ausstehender Betrag {2} sein,
Payment of {0} from {1} to {2},Zahlung von {0} von {1} an {2},
Payment request {0} created,Zahlungsaufforderung {0} erstellt,
@@ -2299,9 +2302,9 @@ Read blog,Blog lesen,
Read the ERPNext Manual,Lesen Sie das ERPNext-Handbuch,
Reading Uploaded File,Hochgeladene Datei lesen,
Real Estate,Immobilien,
Reason For Putting On Hold,Grund für das Halten,
Reason for Hold,Grund für das Halten,
Reason for hold: ,Grund für das Halten:,
Reason For Putting On Hold,Grund für das auf Eis legen,
Reason for Hold,Grund für das auf Eis legen,
Reason for hold: ,Grund für das auf Eis legen:,
Receipt,Kaufbeleg,
Receipt document must be submitted,Eingangsbeleg muss vorgelegt werden,
Receivable,Forderung,
@@ -2321,18 +2324,20 @@ Ref Date,Ref-Datum,
Reference,Referenz,
Reference #{0} dated {1},Referenz #{0} vom {1},
Reference Date,Referenzdatum,
Reference Doctype must be one of {0},Referenz Doctype muss man von {0},
Reference Doctype must be one of {0},Referenz-Typ muss eine von {0} sein,
Reference Document,Referenzdokument,
Reference Document Type,Referenz-Dokumententyp,
Reference No & Reference Date is required for {0},Referenznr. & Referenz-Tag sind erforderlich für {0},
Reference No and Reference Date is mandatory for Bank transaction,Referenznummer und Referenzdatum ist obligatorisch für Bankengeschäft,
Reference No is mandatory if you entered Reference Date,"Referenznr. ist zwingend erforderlich, wenn Referenz-Tag eingegeben wurde",
Reference No and Reference Date is mandatory for Bank transaction,Referenznummer und Referenzdatum sind Pflichtfelder,
Reference No is mandatory if you entered Reference Date,"Referenznummer ist ein Pflichtfeld, wenn ein Referenzdatum eingegeben wurde",
Reference No.,Referenznummer.,
Reference Number,Referenznummer,
Reference Owner,Referenz Besitzer,
Reference Type,Referenz-Typ,
"Reference: {0}, Item Code: {1} and Customer: {2}","Referenz: {0}, Item Code: {1} und Kunde: {2}",
References,Referenzen,
References {0} of type {1} had no outstanding amount left before submitting the Payment Entry. Now they have a negative outstanding amount.,"Die Referenzen {0} vom Typ {1} hatten keinen ausstehenden Betrag mehr, bevor die Zahlung gebucht wurde. Jetzt haben sie einen negativen ausstehenden Betrag.",
If this is undesirable please cancel the corresponding Payment Entry.,"Falls dies nicht erwünscht ist, stornieren Sie bitte die entsprechende Zahlung.",
Refresh Token,Aktualisieren Token,
Region,Region,
Register,Neu registrieren,
@@ -2410,7 +2415,7 @@ Return,Zurück,
Return / Credit Note,Return / Gutschrift,
Return / Debit Note,Return / Lastschrift,
Returns,Retouren,
Reverse Journal Entry,Journaleintrag umkehren,
Reverse Journal Entry,Buchungssatz umkehren,
Review Invitation Sent,Einladung überprüfen gesendet,
Review and Action,Überprüfung und Aktion,
Role,Rolle,
@@ -2423,63 +2428,64 @@ Root cannot have a parent cost center,Root kann keine übergeordnete Kostenstell
Round Off,Abschliessen,
Rounded Total,Gerundete Gesamtsumme,
Route,Route,
Row # {0}: ,Zeile # {0}:,
Row # {0}: Batch No must be same as {1} {2},Zeile # {0}: Chargennummer muss dieselbe sein wie {1} {2},
Row # {0}: Cannot return more than {1} for Item {2},Zeile # {0}: Es kann nicht mehr als {1} für Artikel {2} zurückgegeben werden,
Row # {0}: Rate cannot be greater than the rate used in {1} {2},"Row # {0}: Die Rate kann nicht größer sein als die Rate, die in {1} {2}",
Row # {0}: Serial No is mandatory,Zeile # {0}: Seriennummer ist zwingend erforderlich,
Row # {0}: Serial No {1} does not match with {2} {3},Zeile # {0}: Seriennummer {1} stimmt nicht mit {2} {3} überein,
Row #{0} (Payment Table): Amount must be negative,Zeilennr. {0} (Zahlungstabelle): Betrag muss negativ sein,
Row #{0} (Payment Table): Amount must be positive,Zeile # {0} (Zahlungstabelle): Betrag muss positiv sein,
Row #{0}: Account {1} does not belong to company {2},Zeile # {0}: Konto {1} gehört nicht zur Unternehmen {2},
Row #{0}: Allocated Amount cannot be greater than outstanding amount.,Zeile # {0}: Zugeordneter Betrag darf nicht größer als ausstehender Betrag sein.,
"Row #{0}: Asset {1} cannot be submitted, it is already {2}","Zeile Nr. {0}: Vermögenswert {1} kann nicht vorgelegt werden, es ist bereits {2}",
Row #{0}: Cannot set Rate if amount is greater than billed amount for Item {1}.,"Zeilennr. {0}: Die Rate kann nicht festgelegt werden, wenn der Betrag für Artikel {1} höher als der Rechnungsbetrag ist.",
Row #{0}: Clearance date {1} cannot be before Cheque Date {2},Row # {0}: Räumungsdatum {1} kann nicht vor dem Scheck Datum sein {2},
Row #{0}: Duplicate entry in References {1} {2},Zeile # {0}: Eintrag in Referenzen {1} {2} duplizieren,
Row #{0}: Expected Delivery Date cannot be before Purchase Order Date,Row # {0}: Voraussichtlicher Liefertermin kann nicht vor Bestelldatum sein,
Row #{0}: Item added,Zeile # {0}: Element hinzugefügt,
Row #{0}: Journal Entry {1} does not have account {2} or already matched against another voucher,Row # {0}: Journal Entry {1} nicht Konto {2} oder bereits abgestimmt gegen einen anderen Gutschein,
Row #{0}: Not allowed to change Supplier as Purchase Order already exists,"Zeile #{0}: Es ist nicht erlaubt den Lieferanten zu wechseln, da bereits ein Lieferantenauftrag vorhanden ist",
Row #{0}: Please set reorder quantity,Zeile #{0}: Bitte Nachbestellmenge angeben,
Row #{0}: Please specify Serial No for Item {1},Zeile #{0}: Bitte Seriennummer für Artikel {1} angeben,
Row #{0}: Qty increased by 1,Zeile # {0}: Menge um 1 erhöht,
Row #{0}: Rate must be same as {1}: {2} ({3} / {4}) ,Zeile #{0}: Preis muss derselbe wie {1}: {2} ({3} / {4}) sein,
Row #{0}: Reference Document Type must be one of Expense Claim or Journal Entry,Row # {0}: Referenzdokumenttyp muss einer der Kostenansprüche oder des Journaleintrags sein,
"Row #{0}: Reference Document Type must be one of Purchase Order, Purchase Invoice or Journal Entry","Row # {0}: Referenzdokumenttyp muss eine der Bestellung, Rechnung oder Kaufjournaleintrag sein",
Row #{0}: Rejected Qty can not be entered in Purchase Return,Zeile #{0}: Abgelehnte Menge kann nicht in Kaufrückgabe eingegeben werden,
Row #{0}: Rejected Warehouse is mandatory against rejected Item {1},Row # {0}: Abgelehnt Warehouse ist obligatorisch gegen zurückgewiesen Artikel {1},
Row #{0}: Reqd by Date cannot be before Transaction Date,Zeilennr. {0}: Erforderlich nach Datum darf nicht vor dem Transaktionsdatum liegen,
Row #{0}: Set Supplier for item {1},Zeile #{0}: Lieferanten für Artikel {1} einstellen,
Row #{0}: Status must be {1} for Invoice Discounting {2},Zeile # {0}: Status muss {1} für Rechnungsrabatt {2} sein,
"Row #{0}: The batch {1} has only {2} qty. Please select another batch which has {3} qty available or split the row into multiple rows, to deliver/issue from multiple batches","Zeile # {0}: Der Batch {1} hat nur {2} Menge. Bitte wähle eine andere Charge aus, die {3} Menge zur Verfügung hat oder die Zeile in mehrere Zeilen aufteilt, um aus mehreren Chargen zu liefern / auszutauschen",
Row #{0}: Timings conflicts with row {1},Zeile #{0}: Timing-Konflikte mit Zeile {1},
Row #{0}: {1} can not be negative for item {2},Row # {0}: {1} kann für Artikel nicht negativ sein {2},
Row No {0}: Amount cannot be greater than Pending Amount against Expense Claim {1}. Pending Amount is {2},Zeile Nr. {0}: Betrag kann nicht größer als der ausstehende Betrag zur Aufwandsabrechnung {1} sein. Ausstehender Betrag ist {2},
Row # {0}: ,Zeile {0}:,
Row # {0}: Batch No must be same as {1} {2},Zeile {0}: Chargennummer muss dieselbe sein wie {1} {2},
Row # {0}: Cannot return more than {1} for Item {2},Zeile {0}: Es kann nicht mehr als {1} für Artikel {2} zurückgegeben werden,
Row # {0}: Rate cannot be greater than the rate used in {1} {2},"Zeile {0}: Die Rate kann nicht größer sein als die Rate, die in {1} {2}",
Row # {0}: Serial No is mandatory,Zeile {0}: Seriennummer ist zwingend erforderlich,
Row # {0}: Serial No {1} does not match with {2} {3},Zeile {0}: Seriennummer {1} stimmt nicht mit {2} {3} überein,
Row #{0} (Payment Table): Amount must be negative,Zeile {0} (Zahlungstabelle): Betrag muss negativ sein,
Row #{0} (Payment Table): Amount must be positive,Zeile {0} (Zahlungstabelle): Betrag muss positiv sein,
Row #{0}: Account {1} does not belong to company {2},Zeile {0}: Konto {1} gehört nicht zur Unternehmen {2},
Row #{0}: Allocated Amount cannot be greater than outstanding amount.,Zeile {0}: Zugeordneter Betrag darf nicht größer als ausstehender Betrag sein.,
"Row #{0}: Asset {1} cannot be submitted, it is already {2}","Zeile {0}: Vermögenswert {1} kann nicht vorgelegt werden, es ist bereits {2}",
Row #{0}: Cannot set Rate if amount is greater than billed amount for Item {1}.,"Zeile {0}: Die Rate kann nicht festgelegt werden, wenn der Betrag für Artikel {1} höher als der Rechnungsbetrag ist.",
Row #{0}: Cannot allocate more than {1} against payment term {2},Zeile {0}: Es kann nicht mehr als {1} zu Zahlungsbedingung {2} zugeordnet werden,
Row #{0}: Clearance date {1} cannot be before Cheque Date {2},Zeile {0}: Räumungsdatum {1} kann nicht vor dem Scheck Datum sein {2},
Row #{0}: Duplicate entry in References {1} {2},Referenz {1} {2} in Zeile {0} kommt doppelt vor,
Row #{0}: Expected Delivery Date cannot be before Purchase Order Date,Zeile {0}: Voraussichtlicher Liefertermin kann nicht vor Bestelldatum sein,
Row #{0}: Item added,Zeile {0}: Element hinzugefügt,
Row #{0}: Journal Entry {1} does not have account {2} or already matched against another voucher,Zeile {0}: Buchungssatz {1} betrifft nicht Konto {2} oder bereits mit einem anderen Beleg verrechnet,
Row #{0}: Not allowed to change Supplier as Purchase Order already exists,"Zeile {0}: Es ist nicht erlaubt den Lieferanten zu wechseln, da bereits eine Bestellung vorhanden ist",
Row #{0}: Please set reorder quantity,Zeile {0}: Bitte Nachbestellmenge angeben,
Row #{0}: Please specify Serial No for Item {1},Zeile {0}: Bitte Seriennummer für Artikel {1} angeben,
Row #{0}: Qty increased by 1,Zeile {0}: Menge um 1 erhöht,
Row #{0}: Rate must be same as {1}: {2} ({3} / {4}) ,Zeile {0}: Preis muss derselbe wie {1}: {2} ({3} / {4}) sein,
Row #{0}: Reference Document Type must be one of Expense Claim or Journal Entry,Zeile {0}: Referenzdokumenttyp muss entweder 'Auslagenabrechnung' oder 'Buchungssatz' sein,
"Row #{0}: Reference Document Type must be one of Purchase Order, Purchase Invoice or Journal Entry","Zeile {0}: Referenzdokumenttyp muss eine der Bestellung, Eingangsrechnung oder Buchungssatz sein",
Row #{0}: Rejected Qty can not be entered in Purchase Return,Zeile {0}: Abgelehnte Menge kann nicht in Kaufrückgabe eingegeben werden,
Row #{0}: Rejected Warehouse is mandatory against rejected Item {1},Zeile {0}: Abgelehnt Warehouse ist obligatorisch gegen zurückgewiesen Artikel {1},
Row #{0}: Reqd by Date cannot be before Transaction Date,Zeile {0}: Erforderlich nach Datum darf nicht vor dem Transaktionsdatum liegen,
Row #{0}: Set Supplier for item {1},Zeile {0}: Lieferanten für Artikel {1} einstellen,
Row #{0}: Status must be {1} for Invoice Discounting {2},Zeile {0}: Status muss {1} für Rechnungsrabatt {2} sein,
"Row #{0}: The batch {1} has only {2} qty. Please select another batch which has {3} qty available or split the row into multiple rows, to deliver/issue from multiple batches","Zeile {0}: Der Batch {1} hat nur {2} Menge. Bitte wähle eine andere Charge aus, die {3} Menge zur Verfügung hat oder die Zeile in mehrere Zeilen aufteilt, um aus mehreren Chargen zu liefern / auszutauschen",
Row #{0}: Timings conflicts with row {1},Zeile {0}: Timing-Konflikte mit Zeile {1},
Row #{0}: {1} can not be negative for item {2},Zeile {0}: {1} kann für Artikel nicht negativ sein {2},
Row No {0}: Amount cannot be greater than Pending Amount against Expense Claim {1}. Pending Amount is {2},Zeile {0}: Der Betrag kann nicht größer als der ausstehende Betrag der Auslagenabrechnung {1} sein. Der ausstehende Betrag ist {2},
Row {0} : Operation is required against the raw material item {1},Zeile {0}: Vorgang ist für die Rohmaterialposition {1} erforderlich,
Row {0}# Allocated amount {1} cannot be greater than unclaimed amount {2},Zeile {0} # Der zugewiesene Betrag {1} darf nicht größer sein als der nicht beanspruchte Betrag {2},
Row {0}# Item {1} cannot be transferred more than {2} against Purchase Order {3},Zeile {0} # Artikel {1} kann nicht mehr als {2} gegen Bestellung {3} übertragen werden.,
Row {0}# Paid Amount cannot be greater than requested advance amount,Zeile Nr. {0}: Bezahlter Betrag darf nicht größer sein als der geforderte Anzahlungsbetrag,
Row {0}: Activity Type is mandatory.,Row {0}: Leistungsart ist obligatorisch.,
Row {0}: Advance against Customer must be credit,Row {0}: Voraus gegen Kunde muss Kredit,
Row {0}: Advance against Supplier must be debit,Row {0}: Voraus gegen Lieferant muss belasten werden,
Row {0}# Paid Amount cannot be greater than requested advance amount,Zeile {0}: Bezahlter Betrag darf nicht größer sein als der geforderte Anzahlungsbetrag,
Row {0}: Activity Type is mandatory.,Zeile {0}: Leistungsart ist obligatorisch.,
Row {0}: Advance against Customer must be credit,Zeile {0}: Voraus gegen Kunde muss Kredit,
Row {0}: Advance against Supplier must be debit,Zeile {0}: Voraus gegen Lieferant muss belasten werden,
Row {0}: Allocated amount {1} must be less than or equals to Payment Entry amount {2},Zeile {0}: Zugewiesener Betrag {1} muss kleiner oder gleich der Zahlungsmenge {2} sein,
Row {0}: Allocated amount {1} must be less than or equals to invoice outstanding amount {2},Zeile {0}: Zugeteilte Menge {1} muss kleiner als oder gleich dem ausstehenden Betrag in Rechnung {2} sein,
Row {0}: An Reorder entry already exists for this warehouse {1},Zeile {0}: Es gibt bereits eine Nachbestellungsbuchung für dieses Lager {1},
Row {0}: Bill of Materials not found for the Item {1},Zeile {0}: Bill of Materials nicht für den Artikel gefunden {1},
Row {0}: Conversion Factor is mandatory,Row {0}: Umrechnungsfaktor ist zwingend erfoderlich,
Row {0}: Conversion Factor is mandatory,Zeile {0}: Umrechnungsfaktor ist zwingend erfoderlich,
Row {0}: Cost center is required for an item {1},Zeile {0}: Kostenstelle ist für einen Eintrag {1} erforderlich,
Row {0}: Credit entry can not be linked with a {1},Zeile {0}: Habenbuchung kann nicht mit ein(em) {1} verknüpft werden,
Row {0}: Currency of the BOM #{1} should be equal to the selected currency {2},Zeile {0}: Währung der Stückliste # {1} sollte der gewählten Währung entsprechen {2},
Row {0}: Debit entry can not be linked with a {1},Zeile {0}: Sollbuchung kann nicht mit ein(em) {1} verknüpft werden,
Row {0}: Depreciation Start Date is required,Zeile {0}: Das Abschreibungsstartdatum ist erforderlich,
Row {0}: Enter location for the asset item {1},Zeile Nr. {0}: Geben Sie den Speicherort für das Vermögenswert {1} ein.,
Row {0}: Enter location for the asset item {1},Zeile {0}: Geben Sie einen Ort für den Vermögenswert {1} ein.,
Row {0}: Exchange Rate is mandatory,Zeile {0}: Wechselkurs ist erforderlich,
Row {0}: Expected Value After Useful Life must be less than Gross Purchase Amount,Zeile {0}: Erwarteter Wert nach Nutzungsdauer muss kleiner als Brutto Kaufbetrag sein,
Row {0}: From Time and To Time is mandatory.,Row {0}: Von Zeit und zu Zeit ist obligatorisch.,
Row {0}: From Time and To Time is mandatory.,Zeile {0}: Von Zeit und zu Zeit ist obligatorisch.,
Row {0}: From Time and To Time of {1} is overlapping with {2},Zeile {0}: Zeitüberlappung in {1} mit {2},
Row {0}: From time must be less than to time,Zeile {0}: Von Zeit zu Zeit muss kleiner sein,
Row {0}: Hours value must be greater than zero.,Row {0}: Stunden-Wert muss größer als Null sein.,
Row {0}: Hours value must be greater than zero.,Zeile {0}: Stunden-Wert muss größer als Null sein.,
Row {0}: Invalid reference {1},Zeile {0}: Ungültige Referenz {1},
Row {0}: Party / Account does not match with {1} / {2} in {3} {4},Zeile {0}: Gruppe / Konto stimmt nicht mit {1} / {2} in {3} {4} überein,
Row {0}: Party Type and Party is required for Receivable / Payable account {1},Zeile {0}: Gruppen-Typ und Gruppe sind für Forderungen-/Verbindlichkeiten-Konto {1} zwingend erforderlich,
@@ -2875,7 +2881,7 @@ Supplier Group master.,Lieferantengruppenstamm,
Supplier Id,Lieferanten-ID,
Supplier Invoice Date cannot be greater than Posting Date,Lieferant Rechnungsdatum kann nicht größer sein als Datum der Veröffentlichung,
Supplier Invoice No,Lieferantenrechnungsnr.,
Supplier Invoice No exists in Purchase Invoice {0},Lieferantenrechnung existiert in Kauf Rechnung {0},
Supplier Invoice No exists in Purchase Invoice {0},Die Rechnungsnummer des Lieferanten wurde bereits in Eingangsrechnung {0} verwendet,
Supplier Name,Lieferantenname,
Supplier Part No,Lieferant Teile-Nr,
Supplier Quotation,Lieferantenangebot,
@@ -2935,7 +2941,7 @@ Template of terms or contract.,Vorlage für Geschäftsbedingungen oder Vertrag,
Templates of supplier scorecard criteria.,Vorlagen der Lieferanten-Scorecard-Kriterien.,
Templates of supplier scorecard variables.,Vorlagen der Lieferanten-Scorecard-Variablen.,
Templates of supplier standings.,Vorlagen der Lieferantenwertung.,
Temporarily on Hold,Vorübergehend in der Warteschleife,
Temporarily on Hold,Vorübergehend auf Eis gelegt,
Temporary,Temporär,
Temporary Accounts,Temporäre Konten,
Temporary Opening,Temporäre Eröffnungskonten,
@@ -3077,7 +3083,7 @@ Total Budget,Gesamtbudget; Gesamtetat,
Total Collected: {0},Gesammelt gesammelt: {0},
Total Commission,Gesamtprovision,
Total Contribution Amount: {0},Gesamtbeitragsbetrag: {0},
Total Credit/ Debit Amount should be same as linked Journal Entry,Der Gesamtkreditbetrag sollte identisch mit dem verknüpften Journaleintrag sein,
Total Credit/ Debit Amount should be same as linked Journal Entry,Der Gesamtkreditbetrag sollte identisch mit dem verknüpften Buchungssatz sein,
Total Debit must be equal to Total Credit. The difference is {0},Gesamt-Soll muss gleich Gesamt-Haben sein. Die Differenz ist {0},
Total Deduction,Gesamtabzug,
Total Invoiced Amount,Gesamtrechnungsbetrag,
@@ -3410,7 +3416,7 @@ on,Am,
{0} is not in Optional Holiday List,{0} befindet sich nicht in der optionalen Feiertagsliste,
{0} is not in a valid Payroll Period,{0} befindet sich nicht in einer gültigen Abrechnungsperiode,
{0} is now the default Fiscal Year. Please refresh your browser for the change to take effect.,"{0} ist jetzt das Standardgeschäftsjahr. Bitte aktualisieren Sie Ihren Browser, damit die Änderungen wirksam werden.",
{0} is on hold till {1},{0} ist zurückgestellt bis {1},
{0} is on hold till {1},{0} ist auf Eis gelegt bis {1},
{0} item found.,{0} Artikel gefunden.,
{0} items found.,{0} Artikel gefunden.,
{0} items in progress,{0} Elemente in Bearbeitung,
@@ -3430,6 +3436,8 @@ on,Am,
{0} variants created.,{0} Varianten erstellt.,
{0} {1} created,{0} {1} erstellt,
{0} {1} does not exist,{0} {1} existiert nicht,
{0} {1} has already been fully paid.,{0} {1} wurde bereits vollständig bezahlt.,
{0} {1} has already been partly paid. Please use the 'Get Outstanding Invoice' or the 'Get Outstanding Orders' button to get the latest outstanding amounts.,"{0} {1} wurde bereits teilweise bezahlt. Bitte nutzen Sie den Button 'Ausstehende Rechnungen aufrufen', um die aktuell ausstehenden Beträge zu erhalten.",
{0} {1} has been modified. Please refresh.,{0} {1} wurde geändert. Bitte aktualisieren.,
{0} {1} has not been submitted so the action cannot be completed,"{0} {1} sind nicht gebucht, deshalb kann die Aktion nicht abgeschlossen werden",
"{0} {1} is associated with {2}, but Party Account is {3}","{0} {1} ist mit {2} verbunden, aber das Gegenkonto ist {3}",
@@ -3441,9 +3449,10 @@ on,Am,
{0} {1} is frozen,{0} {1} ist gesperrt,
{0} {1} is fully billed,{0} {1} wird voll in Rechnung gestellt,
{0} {1} is not active,{0} {1} ist nicht aktiv,
{0} {1} is not associated with {2} {3},{0} {1} ist nicht mit {2} {3} verknüpft,
{0} {1} is not associated with {2} {3},{0} {1} gehört nicht zu {2} {3},
{0} {1} is not present in the parent company,{0} {1} ist in der Muttergesellschaft nicht vorhanden,
{0} {1} is not submitted,{0} {1} wurde nicht übertragen,
{0} {1} is on hold,{0} {1} liegt derzeit auf Eis,
{0} {1} is {2},{0} {1} ist {2},
{0} {1} must be submitted,{0} {1} muss vorgelegt werden,
{0} {1} not in any active Fiscal Year.,{0} {1} nicht in einem aktiven Geschäftsjahr.,
@@ -3575,7 +3584,7 @@ Account is not set for the dashboard chart {0},Konto ist nicht für das Dashboar
Account {0} does not belong to company {1},Konto {0} gehört nicht zu Firma {1},
Account {0} does not exists in the dashboard chart {1},Konto {0} ist im Dashboard-Diagramm {1} nicht vorhanden,
Account: <b>{0}</b> is capital Work in progress and can not be updated by Journal Entry,Konto: <b>{0}</b> ist in Bearbeitung und kann von Journal Entry nicht aktualisiert werden,
Account: {0} is not permitted under Payment Entry,Konto: {0} ist unter Zahlungseingang nicht zulässig,
Account: {0} is not permitted under Payment Entry,Konto {0} kann nicht in Zahlung verwendet werden,
Accounting Dimension <b>{0}</b> is required for 'Balance Sheet' account {1}.,Die Buchhaltungsdimension <b>{0}</b> ist für das Bilanzkonto <b>{1}</b> erforderlich.,
Accounting Dimension <b>{0}</b> is required for 'Profit and Loss' account {1}.,Die Buchhaltungsdimension <b>{0}</b> ist für das Konto {1} &quot;Gewinn und Verlust&quot; erforderlich.,
Accounting Masters,Accounting Masters,
@@ -3639,6 +3648,8 @@ Billing Interval Count cannot be less than 1,Die Anzahl der Abrechnungsintervall
Blue,Blau,
Book,Buchen,
Book Appointment,Einen Termin verabreden,
Book Advance Payments as Liability option is chosen. Paid From account changed from {0} to {1}.,"Die Option 'Anzahlungen als Verbindlichkeit buchen' ist aktiviert. Das Ausgangskonto wurde von {0} auf {1} geändert.",
Book Advance Payments in Separate Party Account,Anzahlungen als Verbindlichkeit buchen,
Brand,Marke,
Browse,Durchsuchen,
Call Connected,Anruf verbunden,
@@ -3860,6 +3871,7 @@ No correct answer is set for {0},Für {0} ist keine richtige Antwort festgelegt.
No description,Keine Beschreibung,
No issue has been raised by the caller.,Der Anrufer hat kein Problem angesprochen.,
No items to publish,Keine Artikel zu veröffentlichen,
No outstanding {0} found for the {1} {2} which qualify the filters you have specified.,"Für {1} {2} wurden kein ausstehender Beleg vom Typ {0} gefunden, der den angegebenen Filtern entspricht.",
No outstanding invoices found,Keine offenen Rechnungen gefunden,
No outstanding invoices found for the {0} {1} which qualify the filters you have specified.,"Für {0} {1} wurden keine ausstehenden Rechnungen gefunden, die die von Ihnen angegebenen Filter qualifizieren.",
No outstanding invoices require exchange rate revaluation,Keine ausstehenden Rechnungen erfordern eine Neubewertung des Wechselkurses,
@@ -3955,8 +3967,8 @@ Publish More Items,Veröffentlichen Sie weitere Elemente,
Publish Your First Items,Veröffentlichen Sie Ihre ersten Artikel,
Publish {0} Items,{0} Elemente veröffentlichen,
Published Items,Veröffentlichte Artikel,
Purchase Invoice cannot be made against an existing asset {0},Kaufrechnung kann nicht für ein vorhandenes Asset erstellt werden {0},
Purchase Invoices,Rechnungen kaufen,
Purchase Invoice cannot be made against an existing asset {0},Eingangsrechnung kann nicht für ein vorhandenes Asset erstellt werden {0},
Purchase Invoices,Eingangsrechnungen,
Purchase Orders,Kauforder,
Purchase Receipt doesn't have any Item for which Retain Sample is enabled.,"Der Kaufbeleg enthält keinen Artikel, für den die Option &quot;Probe aufbewahren&quot; aktiviert ist.",
Purchase Return,Warenrücksendung,
@@ -3995,20 +4007,20 @@ Review,Rezension,
Room,Zimmer,
Room Type,Zimmertyp,
Row # ,Zeile #,
Row #{0}: Accepted Warehouse and Supplier Warehouse cannot be same,Zeile # {0}: Akzeptiertes Lager und Lieferantenlager können nicht identisch sein,
Row #{0}: Cannot delete item {1} which has already been billed.,Zeile # {0}: Der bereits abgerechnete Artikel {1} kann nicht gelöscht werden.,
Row #{0}: Cannot delete item {1} which has already been delivered,"Zeile # {0}: Element {1}, das bereits geliefert wurde, kann nicht gelöscht werden",
Row #{0}: Cannot delete item {1} which has already been received,"Zeile # {0}: Element {1}, das bereits empfangen wurde, kann nicht gelöscht werden",
Row #{0}: Cannot delete item {1} which has work order assigned to it.,"Zeile # {0}: Element {1}, dem ein Arbeitsauftrag zugewiesen wurde, kann nicht gelöscht werden.",
Row #{0}: Cannot delete item {1} which is assigned to customer's purchase order.,"Zeile # {0}: Artikel {1}, der der Bestellung des Kunden zugeordnet ist, kann nicht gelöscht werden.",
Row #{0}: Cannot select Supplier Warehouse while suppling raw materials to subcontractor,"Zeile # {0}: Supplier Warehouse kann nicht ausgewählt werden, während Rohstoffe an Subunternehmer geliefert werden",
Row #{0}: Cost Center {1} does not belong to company {2},Zeile # {0}: Kostenstelle {1} gehört nicht zu Firma {2},
Row #{0}: Operation {1} is not completed for {2} qty of finished goods in Work Order {3}. Please update operation status via Job Card {4}.,Zeile # {0}: Vorgang {1} ist für {2} Fertigwarenmenge im Fertigungsauftrag {3} nicht abgeschlossen. Bitte aktualisieren Sie den Betriebsstatus über die Jobkarte {4}.,
Row #{0}: Payment document is required to complete the transaction,"Zeile # {0}: Der Zahlungsbeleg ist erforderlich, um die Transaktion abzuschließen",
Row #{0}: Serial No {1} does not belong to Batch {2},Zeile # {0}: Seriennummer {1} gehört nicht zu Charge {2},
Row #{0}: Service End Date cannot be before Invoice Posting Date,Zeile # {0}: Das Service-Enddatum darf nicht vor dem Rechnungsbuchungsdatum liegen,
Row #{0}: Service Start Date cannot be greater than Service End Date,Zeile # {0}: Das Servicestartdatum darf nicht höher als das Serviceenddatum sein,
Row #{0}: Service Start and End Date is required for deferred accounting,Zeile # {0}: Das Start- und Enddatum des Service ist für die aufgeschobene Abrechnung erforderlich,
Row #{0}: Accepted Warehouse and Supplier Warehouse cannot be same,Zeile {0}: Akzeptiertes Lager und Lieferantenlager können nicht identisch sein,
Row #{0}: Cannot delete item {1} which has already been billed.,Zeile {0}: Der bereits abgerechnete Artikel {1} kann nicht gelöscht werden.,
Row #{0}: Cannot delete item {1} which has already been delivered,"Zeile {0}: Element {1}, das bereits geliefert wurde, kann nicht gelöscht werden",
Row #{0}: Cannot delete item {1} which has already been received,"Zeile {0}: Element {1}, das bereits empfangen wurde, kann nicht gelöscht werden",
Row #{0}: Cannot delete item {1} which has work order assigned to it.,"Zeile {0}: Element {1}, dem ein Arbeitsauftrag zugewiesen wurde, kann nicht gelöscht werden.",
Row #{0}: Cannot delete item {1} which is assigned to customer's purchase order.,"Zeile {0}: Artikel {1}, der der Bestellung des Kunden zugeordnet ist, kann nicht gelöscht werden.",
Row #{0}: Cannot select Supplier Warehouse while suppling raw materials to subcontractor,"Zeile {0}: Supplier Warehouse kann nicht ausgewählt werden, während Rohstoffe an Subunternehmer geliefert werden",
Row #{0}: Cost Center {1} does not belong to company {2},Zeile {0}: Kostenstelle {1} gehört nicht zu Firma {2},
Row #{0}: Operation {1} is not completed for {2} qty of finished goods in Work Order {3}. Please update operation status via Job Card {4}.,Zeile {0}: Vorgang {1} ist für {2} Fertigwarenmenge im Fertigungsauftrag {3} nicht abgeschlossen. Bitte aktualisieren Sie den Betriebsstatus über die Jobkarte {4}.,
Row #{0}: Payment document is required to complete the transaction,"Zeile {0}: Der Zahlungsbeleg ist erforderlich, um die Transaktion abzuschließen",
Row #{0}: Serial No {1} does not belong to Batch {2},Zeile {0}: Seriennummer {1} gehört nicht zu Charge {2},
Row #{0}: Service End Date cannot be before Invoice Posting Date,Zeile {0}: Das Service-Enddatum darf nicht vor dem Rechnungsbuchungsdatum liegen,
Row #{0}: Service Start Date cannot be greater than Service End Date,Zeile {0}: Das Servicestartdatum darf nicht höher als das Serviceenddatum sein,
Row #{0}: Service Start and End Date is required for deferred accounting,Zeile {0}: Das Start- und Enddatum des Service ist für die aufgeschobene Abrechnung erforderlich,
Row {0}: Invalid Item Tax Template for item {1},Zeile {0}: Ungültige Artikelsteuervorlage für Artikel {1},
Row {0}: Quantity not available for {4} in warehouse {1} at posting time of the entry ({2} {3}),Zeile {0}: Menge für {4} in Lager {1} zum Buchungszeitpunkt des Eintrags nicht verfügbar ({2} {3}),
Row {0}: user has not applied the rule {1} on the item {2},Zeile {0}: Der Nutzer hat die Regel {1} nicht auf das Element {2} angewendet.,
@@ -4275,7 +4287,7 @@ to,An,
Cards,Karten,
Percentage,Prozentsatz,
Failed to setup defaults for country {0}. Please contact support@erpnext.com,Fehler beim Einrichten der Standardeinstellungen für Land {0}. Bitte wenden Sie sich an support@erpnext.com,
Row #{0}: Item {1} is not a Serialized/Batched Item. It cannot have a Serial No/Batch No against it.,Zeile # {0}: Element {1} ist kein serialisiertes / gestapeltes Element. Es kann keine Seriennummer / Chargennummer dagegen haben.,
Row #{0}: Item {1} is not a Serialized/Batched Item. It cannot have a Serial No/Batch No against it.,Zeile {0}: Element {1} ist kein serialisiertes / gestapeltes Element. Es kann keine Seriennummer / Chargennummer dagegen haben.,
Please set {0},Bitte {0} setzen,
Please set {0},Bitte setzen Sie {0},supplier
Draft,Entwurf,"docstatus,=,0"
@@ -4362,7 +4374,7 @@ Row {0}: {1} is required in the expenses table to book an expense claim.,"Zeile
Set the default account for the {0} {1},Legen Sie das Standardkonto für {0} {1} fest,
(Half Day),(Halber Tag),
Income Tax Slab,Einkommensteuerplatte,
Row #{0}: Cannot set amount or formula for Salary Component {1} with Variable Based On Taxable Salary,Zeile # {0}: Betrag oder Formel für Gehaltskomponente {1} mit Variable basierend auf steuerpflichtigem Gehalt kann nicht festgelegt werden,
Row #{0}: Cannot set amount or formula for Salary Component {1} with Variable Based On Taxable Salary,Zeile {0}: Betrag oder Formel für Gehaltskomponente {1} mit Variable basierend auf steuerpflichtigem Gehalt kann nicht festgelegt werden,
Row #{}: {} of {} should be {}. Please modify the account or select a different account.,Zeile # {}: {} von {} sollte {} sein. Bitte ändern Sie das Konto oder wählen Sie ein anderes Konto aus.,
Row #{}: Please asign task to a member.,Zeile # {}: Bitte weisen Sie einem Mitglied eine Aufgabe zu.,
Process Failed,Prozess fehlgeschlagen,
@@ -4371,10 +4383,11 @@ Please set Warehouse in Woocommerce Settings,Bitte stellen Sie Warehouse in den
Row {0}: Delivery Warehouse ({1}) and Customer Warehouse ({2}) can not be same,Zeile {0}: Lieferlager ({1}) und Kundenlager ({2}) können nicht identisch sein,
Row {0}: Due Date in the Payment Terms table cannot be before Posting Date,Zeile {0}: Fälligkeitsdatum in der Tabelle &quot;Zahlungsbedingungen&quot; darf nicht vor dem Buchungsdatum liegen,
Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.,{} Für Element {} kann nicht gefunden werden. Bitte stellen Sie dasselbe in den Artikelstamm- oder Lagereinstellungen ein.,
Row #{0}: The batch {1} has already expired.,Zeile # {0}: Der Stapel {1} ist bereits abgelaufen.,
Row #{0}: The batch {1} has already expired.,Zeile {0}: Der Stapel {1} ist bereits abgelaufen.,
Start Year and End Year are mandatory,Startjahr und Endjahr sind obligatorisch,
GL Entry,Buchung zum Hauptbuch,
Cannot allocate more than {0} against payment term {1},Es kann nicht mehr als {0} für die Zahlungsbedingung {1} zugeordnet werden.,
Cannot receive from customer against negative outstanding,Negativer Gesamtbetrag kann nicht vom Kunden empfangen werden,
The root account {0} must be a group,Das Root-Konto {0} muss eine Gruppe sein,
Shipping rule not applicable for country {0} in Shipping Address,Versandregel gilt nicht für Land {0} in Versandadresse,
Get Payments from,Zahlungen erhalten von,
@@ -4517,7 +4530,7 @@ Determine Address Tax Category From,Adresssteuerkategorie bestimmen von,
Over Billing Allowance (%),Mehr als Abrechnungsbetrag (%),
Credit Controller,Kredit-Controller,
Check Supplier Invoice Number Uniqueness,"Aktivieren, damit dieselbe Lieferantenrechnungsnummer nur einmal vorkommen kann",
Make Payment via Journal Entry,Zahlung über Journaleintrag,
Make Payment via Journal Entry,Zahlung über Buchungssatz,
Unlink Payment on Cancellation of Invoice,Zahlung bei Stornierung der Rechnung aufheben,
Book Asset Depreciation Entry Automatically,Vermögensabschreibung automatisch verbuchen,
Automatically Add Taxes and Charges from Item Tax Template,Steuern und Gebühren aus Artikelsteuervorlage automatisch hinzufügen,
@@ -4586,7 +4599,7 @@ Bank Statement Transaction Entry,Kontoauszug Transaktionseintrag,
Bank Transaction Entries,Banktransaktionseinträge,
New Transactions,Neue Transaktionen,
Match Transaction to Invoices,Transaktion mit Rechnungen abgleichen,
Create New Payment/Journal Entry,Erstellen Sie eine neue Zahlung / Journaleintrag,
Create New Payment/Journal Entry,Erstellen Sie eine neue Zahlung / Buchungssatz,
Submit/Reconcile Payments,Zahlungen absenden / abstimmen,
Matching Invoices,Passende Rechnungen,
Payment Invoice Items,Zahlung Rechnungspositionen,
@@ -4839,6 +4852,7 @@ Account Paid To,Eingangskonto,
Paid Amount (Company Currency),Gezahlter Betrag (Unternehmenswährung),
Received Amount,erhaltenen Betrag,
Received Amount (Company Currency),Erhaltene Menge (Gesellschaft Währung),
Received Amount cannot be greater than Paid Amount,Der erhaltene Betrag darf nicht größer sein als der gezahlte Betrag,
Get Outstanding Invoice,Erhalten Sie eine ausstehende Rechnung,
Payment References,Bezahlung Referenzen,
Writeoff,Abschreiben,
@@ -4986,7 +5000,7 @@ Apply Tax Withholding Amount,Steuereinbehaltungsbetrag anwenden,
Accounting Dimensions ,Buchhaltung Dimensionen,
Supplier Invoice Details,Lieferant Rechnungsdetails,
Supplier Invoice Date,Lieferantenrechnungsdatum,
Return Against Purchase Invoice,Zurück zur Einkaufsrechnung,
Return Against Purchase Invoice,Gutschrift zur Eingangsrechnung,
Select Supplier Address,Lieferantenadresse auswählen,
Contact Person,Kontaktperson,
Select Shipping Address,Lieferadresse auswählen,
@@ -5036,7 +5050,7 @@ Terms,Geschäftsbedingungen,
Terms and Conditions1,Allgemeine Geschäftsbedingungen1,
Group same items,Gruppe gleichen Artikel,
Print Language,Drucksprache,
"Once set, this invoice will be on hold till the set date","Einmal eingestellt, wird diese Rechnung bis zum festgelegten Datum gehalten",
"Once set, this invoice will be on hold till the set date","Einmal eingestellt, liegt diese Rechnung bis zum festgelegten Datum auf Eis",
Credit To,Gutschreiben auf,
Party Account Currency,Gruppenkonten-Währung,
Against Expense Account,Zu Aufwandskonto,
@@ -5360,7 +5374,7 @@ Asset Owner,Eigentümer des Vermögenswertes,
Asset Owner Company,Eigentümergesellschaft,
Custodian,Depotbank,
Disposal Date,Verkauf Datum,
Journal Entry for Scrap,Journaleintrag für Ausschuss,
Journal Entry for Scrap,Buchungssatz für Ausschuss,
Available-for-use Date,Verfügbarkeitsdatum,
Calculate Depreciation,Abschreibung berechnen,
Allow Monthly Depreciation,Monatliche Abschreibung zulassen,
@@ -5522,8 +5536,8 @@ Default Bank Account,Standardbankkonto,
Is Transporter,Ist Transporter,
Represents Company,Repräsentiert das Unternehmen,
Supplier Type,Lieferantentyp,
Allow Purchase Invoice Creation Without Purchase Order,Erstellen von Kaufrechnungen ohne Bestellung zulassen,
Allow Purchase Invoice Creation Without Purchase Receipt,Zulassen der Erstellung von Kaufrechnungen ohne Kaufbeleg,
Allow Purchase Invoice Creation Without Purchase Order,Erstellen von Eingangsrechnung ohne Bestellung zulassen,
Allow Purchase Invoice Creation Without Purchase Receipt,Erstellen von Eingangsrechnung ohne Kaufbeleg ohne Kaufbeleg zulassen,
Warn RFQs,Warnung Ausschreibungen,
Warn POs,Warnen Sie POs,
Prevent RFQs,Vermeidung von Ausschreibungen,
@@ -7802,7 +7816,7 @@ Enable Perpetual Inventory,Permanente Inventur aktivieren,
Default Inventory Account,Standard Inventurkonto,
Stock Adjustment Account,Bestandskorrektur-Konto,
Fixed Asset Depreciation Settings,Einstellungen Abschreibung von Anlagevermögen,
Series for Asset Depreciation Entry (Journal Entry),Serie für Abschreibungs-Eintrag (Journaleintrag),
Series for Asset Depreciation Entry (Journal Entry),Serie für Abschreibungs-Eintrag (Buchungssatz),
Gain/Loss Account on Asset Disposal,Gewinn-/Verlustrechnung auf die Veräußerung von Vermögenswerten,
Asset Depreciation Cost Center,Kostenstelle für Vermögenswertabschreibung,
Budget Detail,Budget-Detail,
@@ -8597,10 +8611,10 @@ Automatically Process Deferred Accounting Entry,Aufgeschobene Buchungsbuchung au
Bank Clearance,Bankfreigabe,
Bank Clearance Detail,Bankfreigabedetail,
Update Cost Center Name / Number,Name / Nummer der Kostenstelle aktualisieren,
Journal Entry Template,Journaleintragsvorlage,
Journal Entry Template,Buchungssatz-Vorlage,
Template Title,Vorlagentitel,
Journal Entry Type,Journaleintragstyp,
Journal Entry Template Account,Journaleintragsvorlagenkonto,
Journal Entry Type,Buchungssatz-Typ,
Journal Entry Template Account,Buchungssatzvorlagenkonto,
Process Deferred Accounting,Aufgeschobene Buchhaltung verarbeiten,
Manual entry cannot be created! Disable automatic entry for deferred accounting in accounts settings and try again,Manuelle Eingabe kann nicht erstellt werden! Deaktivieren Sie die automatische Eingabe für die verzögerte Buchhaltung in den Konteneinstellungen und versuchen Sie es erneut,
End date cannot be before start date,Das Enddatum darf nicht vor dem Startdatum liegen,
@@ -8676,7 +8690,7 @@ Deferred Accounting Settings,Aufgeschobene Buchhaltungseinstellungen,
Book Deferred Entries Based On,Buch verzögerte Einträge basierend auf,
Days,Tage,
Months,Monate,
Book Deferred Entries Via Journal Entry,Buch verzögerte Einträge über Journaleintrag,
Book Deferred Entries Via Journal Entry,Separaten Buchungssatz für latente Buchungen erstellen,
Submit Journal Entries,Journaleinträge senden,
If this is unchecked Journal Entries will be saved in a Draft state and will have to be submitted manually,"Wenn dieses Kontrollkästchen deaktiviert ist, werden Journaleinträge in einem Entwurfsstatus gespeichert und müssen manuell übermittelt werden",
Enable Distributed Cost Center,Aktivieren Sie die verteilte Kostenstelle,
@@ -8704,7 +8718,7 @@ Dunning Letter,Mahnbrief,
Reference Detail No,Referenz Detail Nr,
Custom Remarks,Benutzerdefinierte Bemerkungen,
Please select a Company first.,Bitte wählen Sie zuerst eine Firma aus.,
"Row #{0}: Reference Document Type must be one of Sales Order, Sales Invoice, Journal Entry or Dunning","Zeile # {0}: Der Referenzdokumenttyp muss Kundenauftrag, Verkaufsrechnung, Journaleintrag oder Mahnwesen sein",
"Row #{0}: Reference Document Type must be one of Sales Order, Sales Invoice, Journal Entry or Dunning","Zeile {0}: Der Referenzdokumenttyp muss Auftrag, Ausgangsrechnung, Buchungssatz oder Mahnung sein",
POS Closing Entry,POS Closing Entry,
POS Opening Entry,POS-Eröffnungseintrag,
POS Transactions,POS-Transaktionen,
@@ -8824,9 +8838,9 @@ Depreciation Posting Date,Buchungsdatum der Abschreibung,
"By default, the Supplier Name is set as per the Supplier Name entered. If you want Suppliers to be named by a ","Standardmäßig wird der Lieferantenname gemäß dem eingegebenen Lieferantennamen festgelegt. Wenn Sie möchten, dass Lieferanten von a benannt werden",
choose the 'Naming Series' option.,Wählen Sie die Option &quot;Naming Series&quot;.,
Configure the default Price List when creating a new Purchase transaction. Item prices will be fetched from this Price List.,Konfigurieren Sie die Standardpreisliste beim Erstellen einer neuen Kauftransaktion. Artikelpreise werden aus dieser Preisliste abgerufen.,
"If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice or Receipt without creating a Purchase Order first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Order' checkbox in the Supplier master.","Wenn diese Option auf &quot;Ja&quot; konfiguriert ist, verhindert ERPNext, dass Sie eine Kaufrechnung oder einen Beleg erstellen können, ohne zuvor eine Bestellung zu erstellen. Diese Konfiguration kann für einen bestimmten Lieferanten überschrieben werden, indem das Kontrollkästchen &quot;Erstellung von Einkaufsrechnungen ohne Bestellung zulassen&quot; im Lieferantenstamm aktiviert wird.",
"If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice without creating a Purchase Receipt first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Receipt' checkbox in the Supplier master.","Wenn diese Option auf &quot;Ja&quot; konfiguriert ist, verhindert ERPNext, dass Sie eine Kaufrechnung erstellen können, ohne zuvor einen Kaufbeleg zu erstellen. Diese Konfiguration kann für einen bestimmten Lieferanten überschrieben werden, indem das Kontrollkästchen &quot;Erstellung von Kaufrechnungen ohne Kaufbeleg zulassen&quot; im Lieferantenstamm aktiviert wird.",
Quantity & Stock,Menge &amp; Lager,
"If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice or Receipt without creating a Purchase Order first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Order' checkbox in the Supplier master.","Wenn diese Option auf 'Ja' gesetzt ist, validiert ERPNext, dass Sie eine Bestellung angelegt haben, bevor Sie eine Eingangsrechnung oder einen Kaufbeleg erfassen können. Diese Konfiguration kann für einzelne Lieferanten überschrieben werden, indem Sie die Option 'Erstellung von Eingangsrechnungen ohne Bestellung zulassen' im Lieferantenstamm aktivieren.",
"If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice without creating a Purchase Receipt first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Receipt' checkbox in the Supplier master.","Wenn diese Option auf 'Ja' gesetzt ist, validiert ERPNext, dass Sie einen Kaufbeleg angelegt haben, bevor Sie eine Eingangsrechnung erfasen können. Diese Konfiguration kann für einzelne Lieferanten überschrieben werden, indem Sie die Option 'Erstellung von Kaufrechnungen ohne Kaufbeleg zulassen' im Lieferantenstamm aktivieren.",
Quantity & Stock,Menge & Lager,
Call Details,Anrufdetails,
Authorised By,Authorisiert von,
Signee (Company),Unterzeichner (Firma),
@@ -9063,7 +9077,7 @@ Rented To Date,Bisher vermietet,
Monthly Eligible Amount,Monatlicher förderfähiger Betrag,
Total Eligible HRA Exemption,Insgesamt berechtigte HRA-Befreiung,
Validating Employee Attendance...,Überprüfung der Mitarbeiterbeteiligung ...,
Submitting Salary Slips and creating Journal Entry...,Einreichen von Gehaltsabrechnungen und Erstellen eines Journaleintrags ...,
Submitting Salary Slips and creating Journal Entry...,Gehaltsabrechnungen werden gebucht und Buchungssätze erstellt...,
Calculate Payroll Working Days Based On,Berechnen Sie die Arbeitstage der Personalabrechnung basierend auf,
Consider Unmarked Attendance As,Betrachten Sie die nicht markierte Teilnahme als,
Fraction of Daily Salary for Half Day,Bruchteil des Tagesgehalts für einen halben Tag,
@@ -9116,12 +9130,12 @@ Show 'Scan Barcode' field above every child table to insert Items with ease.,"Ze
"If blank, parent Warehouse Account or company default will be considered in transactions","Wenn leer, wird das übergeordnete Lagerkonto oder der Firmenstandard bei Transaktionen berücksichtigt",
Service Level Agreement Details,Details zum Service Level Agreement,
Service Level Agreement Status,Status des Service Level Agreements,
On Hold Since,In der Warteschleife seit,
On Hold Since,Auf Eis gelegt seit,
Total Hold Time,Gesamte Haltezeit,
Response Details,Antwortdetails,
Average Response Time,Durchschnittliche Reaktionszeit,
User Resolution Time,Benutzerauflösungszeit,
SLA is on hold since {0},"SLA wird gehalten, da {0}",
SLA is on hold since {0},"SLA ist seit {0} auf Eis gelegt",
Pause SLA On Status,SLA On Status anhalten,
Pause SLA On,SLA anhalten Ein,
Greetings Section,Grüße Abschnitt,
@@ -9335,6 +9349,7 @@ Total Assets,Gesamtvermögen,
New Assets (This Year),Neue Vermögenswerte (dieses Jahr),
Row #{}: Depreciation Posting Date should not be equal to Available for Use Date.,Zeile # {}: Das Buchungsdatum der Abschreibung sollte nicht dem Datum der Verfügbarkeit entsprechen.,
Incorrect Date,Falsches Datum,
Incorrect Payment Type,Falsche Zahlungsart,
Invalid Gross Purchase Amount,Ungültiger Bruttokaufbetrag,
There are active maintenance or repairs against the asset. You must complete all of them before cancelling the asset.,"Es gibt aktive Wartungs- oder Reparaturarbeiten am Vermögenswert. Sie müssen alle Schritte ausführen, bevor Sie das Asset stornieren können.",
% Complete,% Komplett,
@@ -9357,7 +9372,7 @@ Please check your Plaid client ID and secret values,Bitte überprüfen Sie Ihre
Bank transaction creation error,Fehler beim Erstellen der Banküberweisung,
Unit of Measurement,Maßeinheit,
Fiscal Year {0} Does Not Exist,Geschäftsjahr {0} existiert nicht,
Row # {0}: Returned Item {1} does not exist in {2} {3},Zeile # {0}: Zurückgegebenes Element {1} ist in {2} {3} nicht vorhanden,
Row # {0}: Returned Item {1} does not exist in {2} {3},Zeile {0}: Zurückgegebenes Element {1} ist in {2} {3} nicht vorhanden,
Valuation type charges can not be marked as Inclusive,Bewertungsgebühren können nicht als Inklusiv gekennzeichnet werden,
You do not have permissions to {} items in a {}.,Sie haben keine Berechtigungen für {} Elemente in einem {}.,
Insufficient Permissions,Nicht ausreichende Berechtigungen,
@@ -9368,7 +9383,7 @@ Invalid Value,Ungültiger Wert,
The value {0} is already assigned to an existing Item {1}.,Der Wert {0} ist bereits einem vorhandenen Element {1} zugeordnet.,
"To still proceed with editing this Attribute Value, enable {0} in Item Variant Settings.","Aktivieren Sie {0} in den Einstellungen für Elementvarianten, um mit der Bearbeitung dieses Attributwerts fortzufahren.",
Edit Not Allowed,Bearbeiten nicht erlaubt,
Row #{0}: Item {1} is already fully received in Purchase Order {2},Zeile # {0}: Artikel {1} ist bereits vollständig in der Bestellung {2} eingegangen.,
Row #{0}: Item {1} is already fully received in Purchase Order {2},Zeile {0}: Artikel {1} ist bereits vollständig in der Bestellung {2} eingegangen.,
You cannot create or cancel any accounting entries with in the closed Accounting Period {0},Sie können im abgeschlossenen Abrechnungszeitraum {0} keine Buchhaltungseinträge mit erstellen oder stornieren.,
POS Invoice should have {} field checked.,Für die POS-Rechnung sollte das Feld {} aktiviert sein.,
Invalid Item,Ungültiger Artikel,
@@ -9470,7 +9485,7 @@ Only submittted unpledge requests can be approved,Es können nur übermittelte n
Interest Amount or Principal Amount is mandatory,Der Zinsbetrag oder der Kapitalbetrag ist obligatorisch,
Disbursed Amount cannot be greater than {0},Der ausgezahlte Betrag darf nicht größer als {0} sein.,
Row {0}: Loan Security {1} added multiple times,Zeile {0}: Darlehenssicherheit {1} wurde mehrmals hinzugefügt,
Row #{0}: Child Item should not be a Product Bundle. Please remove Item {1} and Save,Zeile # {0}: Untergeordnetes Element sollte kein Produktpaket sein. Bitte entfernen Sie Artikel {1} und speichern Sie,
Row #{0}: Child Item should not be a Product Bundle. Please remove Item {1} and Save,Zeile {0}: Untergeordnetes Element sollte kein Produktpaket sein. Bitte entfernen Sie Artikel {1} und speichern Sie,
Credit limit reached for customer {0},Kreditlimit für Kunde erreicht {0},
Could not auto create Customer due to the following missing mandatory field(s):,Der Kunde konnte aufgrund der folgenden fehlenden Pflichtfelder nicht automatisch erstellt werden:,
Please create Customer from Lead {0}.,Bitte erstellen Sie einen Kunden aus Lead {0}.,
@@ -9482,7 +9497,7 @@ Payroll date can not be less than employee's joining date.,Das Abrechnungsdatum
From date can not be less than employee's joining date.,Ab dem Datum darf das Beitrittsdatum des Mitarbeiters nicht unterschritten werden.,
To date can not be greater than employee's relieving date.,Bisher kann das Entlastungsdatum des Mitarbeiters nicht überschritten werden.,
Payroll date can not be greater than employee's relieving date.,Das Abrechnungsdatum darf nicht größer sein als das Entlastungsdatum des Mitarbeiters.,
Row #{0}: Please enter the result value for {1},Zeile # {0}: Bitte geben Sie den Ergebniswert für {1} ein,
Row #{0}: Please enter the result value for {1},Zeile {0}: Bitte geben Sie den Ergebniswert für {1} ein,
Mandatory Results,Obligatorische Ergebnisse,
Sales Invoice or Patient Encounter is required to create Lab Tests,Für die Erstellung von Labortests ist eine Verkaufsrechnung oder eine Patientenbegegnung erforderlich,
Insufficient Data,Unzureichende Daten,
@@ -9490,12 +9505,12 @@ Lab Test(s) {0} created successfully,Labortest (e) {0} erfolgreich erstellt,
Test :,Prüfung :,
Sample Collection {0} has been created,Die Probensammlung {0} wurde erstellt,
Normal Range: ,Normalbereich:,
Row #{0}: Check Out datetime cannot be less than Check In datetime,Zeile # {0}: Die Check-out-Datumszeit darf nicht kleiner als die Check-In-Datumszeit sein,
Row #{0}: Check Out datetime cannot be less than Check In datetime,Zeile {0}: Die Check-out-Datumszeit darf nicht kleiner als die Check-In-Datumszeit sein,
"Missing required details, did not create Inpatient Record","Fehlende erforderliche Details, keine stationäre Aufzeichnung erstellt",
Unbilled Invoices,Nicht in Rechnung gestellte Rechnungen,
Standard Selling Rate should be greater than zero.,Die Standardverkaufsrate sollte größer als Null sein.,
Conversion Factor is mandatory,Der Umrechnungsfaktor ist obligatorisch,
Row #{0}: Conversion Factor is mandatory,Zeile # {0}: Der Umrechnungsfaktor ist obligatorisch,
Row #{0}: Conversion Factor is mandatory,Zeile {0}: Der Umrechnungsfaktor ist obligatorisch,
Sample Quantity cannot be negative or 0,Die Probenmenge darf nicht negativ oder 0 sein,
Invalid Quantity,Ungültige Menge,
"Please set defaults for Customer Group, Territory and Selling Price List in Selling Settings","Bitte legen Sie in den Verkaufseinstellungen die Standardeinstellungen für Kundengruppe, Gebiet und Verkaufspreisliste fest",
@@ -9687,7 +9702,7 @@ Row #{}: No batch selected against item: {}. Please select a batch or remove it
Payment amount cannot be less than or equal to 0,Der Zahlungsbetrag darf nicht kleiner oder gleich 0 sein,
Please enter the phone number first,Bitte geben Sie zuerst die Telefonnummer ein,
Row #{}: {} {} does not exist.,Zeile # {}: {} {} existiert nicht.,
Row #{0}: {1} is required to create the Opening {2} Invoices,"Zeile # {0}: {1} ist erforderlich, um die Eröffnungsrechnungen {2} zu erstellen",
Row #{0}: {1} is required to create the Opening {2} Invoices,"Zeile {0}: {1} ist erforderlich, um die Eröffnungsrechnungen {2} zu erstellen",
You had {} errors while creating opening invoices. Check {} for more details,Beim Erstellen von Eröffnungsrechnungen sind {} Fehler aufgetreten. Überprüfen Sie {} auf weitere Details,
Error Occured,Fehler aufgetreten,
Opening Invoice Creation In Progress,Öffnen der Rechnungserstellung läuft,
@@ -9699,7 +9714,7 @@ Stock Transactions for Item {0} under warehouse {1} cannot be posted before this
Posting future stock transactions are not allowed due to Immutable Ledger,Das Buchen zukünftiger Lagertransaktionen ist aufgrund des unveränderlichen Hauptbuchs nicht zulässig,
A BOM with name {0} already exists for item {1}.,Für Artikel {1} ist bereits eine Stückliste mit dem Namen {0} vorhanden.,
{0}{1} Did you rename the item? Please contact Administrator / Tech support,{0} {1} Haben Sie den Artikel umbenannt? Bitte wenden Sie sich an den Administrator / technischen Support,
At row #{0}: the sequence id {1} cannot be less than previous row sequence id {2},In Zeile # {0}: Die Sequenz-ID {1} darf nicht kleiner sein als die vorherige Zeilen-Sequenz-ID {2}.,
At row #{0}: the sequence id {1} cannot be less than previous row sequence id {2},In Zeile {0}: Die Sequenz-ID {1} darf nicht kleiner sein als die vorherige Zeilen-Sequenz-ID {2}.,
The {0} ({1}) must be equal to {2} ({3}),Die {0} ({1}) muss gleich {2} ({3}) sein.,
"{0}, complete the operation {1} before the operation {2}.","{0}, schließen Sie die Operation {1} vor der Operation {2} ab.",
Cannot ensure delivery by Serial No as Item {0} is added with and without Ensure Delivery by Serial No.,"Die Lieferung per Seriennummer kann nicht sichergestellt werden, da Artikel {0} mit und ohne Lieferung per Seriennummer hinzugefügt wird.",
Can't render this file because it is too large.

View File

@@ -4,7 +4,7 @@ googlemaps # used in ERPNext, but dependency is defined in Frappe
pandas>=1.1.5,<2.0.0
plaid-python~=7.2.1
pycountry~=20.7.3
PyGithub~=1.54.1
PyGithub~=2.1.1
python-stdnum~=1.16
python-youtube~=0.8.0
taxjar~=1.9.2