Compare commits

...

179 Commits

Author SHA1 Message Date
Frappe PR Bot
4d2d38cd9d chore(release): Bumped to Version 15.27.4
## [15.27.4](https://github.com/frappe/erpnext/compare/v15.27.3...v15.27.4) (2024-06-11)

### Bug Fixes

* Add additional condition application ([1fca6ea](1fca6ea5e7))
* Add posting date to key for consolidated view ([9472085](94720851c6))
* Add timestamp to key for immutable views ([9e9bc8b](9e9bc8b59c))
* asset depreciations and balances report correction (backport [#41824](https://github.com/frappe/erpnext/issues/41824)) ([#41851](https://github.com/frappe/erpnext/issues/41851)) ([28b7fad](28b7fad579))
* calculate totals and taxes ([5544309](5544309048))
* Do no apply pricing rule on qty change for mapped docs ([822bc58](822bc581f3))
* do not fetch items with no active BOM in PP (backport [#41833](https://github.com/frappe/erpnext/issues/41833)) ([#41835](https://github.com/frappe/erpnext/issues/41835)) ([30c0b2b](30c0b2bb54))
* enable no_copy for timesheet in sales invoice ([f1ad0f6](f1ad0f6f26))
* fixing Item-wise sales register ([f36db7e](f36db7ebaf))
* fixing Item-wise sales register [#41373](https://github.com/frappe/erpnext/issues/41373) ([5e0fa1b](5e0fa1b75e))
* fixing Item-wise sales register and purchase register [#41373](https://github.com/frappe/erpnext/issues/41373) ([a4b28ba](a4b28ba354))
* Item-wise Sales and Purchase register with no item codes [#41373](https://github.com/frappe/erpnext/issues/41373) ([96405e8](96405e8e49))
* stock_value_change for legacy serial nos (backport [#41825](https://github.com/frappe/erpnext/issues/41825)) ([#41826](https://github.com/frappe/erpnext/issues/41826)) ([a7971cf](a7971cf844))
* terms and conditions for material request (backport [#41834](https://github.com/frappe/erpnext/issues/41834)) ([#41837](https://github.com/frappe/erpnext/issues/41837)) ([1ead667](1ead66721d))
* typo in method - holiday list (backport [#41811](https://github.com/frappe/erpnext/issues/41811)) ([#41812](https://github.com/frappe/erpnext/issues/41812)) ([4d3e883](4d3e883130))
* valuation rate for backdated legacy serial/batches (backport [#41788](https://github.com/frappe/erpnext/issues/41788)) ([#41821](https://github.com/frappe/erpnext/issues/41821)) ([8d31243](8d31243a42))
* valuation rate for serial and batch bundle for current bundle (backport [#41850](https://github.com/frappe/erpnext/issues/41850)) ([#41860](https://github.com/frappe/erpnext/issues/41860)) ([3e4c568](3e4c568ddc))
* **Vehicle:** allow doc renaming and add test (backport [#41729](https://github.com/frappe/erpnext/issues/41729)) ([#41785](https://github.com/frappe/erpnext/issues/41785)) ([4d37739](4d37739dd3))
2024-06-11 13:18:31 +00:00
rohitwaghchaure
b87b438881 Merge pull request #41854 from frappe/version-15-hotfix
chore: release v15
2024-06-11 18:47:08 +05:30
mergify[bot]
3e4c568ddc fix: valuation rate for serial and batch bundle for current bundle (backport #41850) (#41860)
fix: valuation rate for serial and batch bundle for current bundle (#41850)

(cherry picked from commit 7249a691b3)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-06-11 18:29:54 +05:30
mergify[bot]
28b7fad579 fix: asset depreciations and balances report correction (backport #41824) (#41851)
fix: asset depreciations and balances report correction (#41824)

* fix: asset depreciations and balances report correction

* chore: suppress linter warnings with # nosemgrep

(cherry picked from commit 857c689405)

Co-authored-by: Khushi Rawat <142375893+khushi8112@users.noreply.github.com>
2024-06-11 18:18:11 +05:30
Frappe PR Bot
6157119bcf chore(release): Bumped to Version 15.27.3
## [15.27.3](https://github.com/frappe/erpnext/compare/v15.27.2...v15.27.3) (2024-06-11)

### Bug Fixes

* Add timestamp to key for immutable views ([439ef10](439ef109f3))
2024-06-11 09:50:57 +00:00
Deepesh Garg
144d7828e9 Merge pull request #41856 from frappe/mergify/bp/version-15/pr-41855
fix: Add timestamp to key for immutable views (#41852)
2024-06-11 15:17:29 +05:30
Deepesh Garg
439ef109f3 fix: Add timestamp to key for immutable views
(cherry picked from commit 6bded59f1c)
(cherry picked from commit 9e9bc8b59c)
2024-06-11 09:41:12 +00:00
Deepesh Garg
366f68e49c Merge pull request #41855 from frappe/mergify/bp/version-15-hotfix/pr-41852
fix: Add timestamp to key for immutable views (#41852)
2024-06-11 15:06:50 +05:30
Deepesh Garg
9e9bc8b59c fix: Add timestamp to key for immutable views
(cherry picked from commit 6bded59f1c)
2024-06-11 09:36:07 +00:00
ruthra kumar
75f3b47424 Merge pull request #41847 from frappe/mergify/bp/version-15-hotfix/pr-41707
refactor: add `Is opening` flag for payment entry (backport #41707)
2024-06-11 11:02:57 +05:30
ruthra kumar
acc2e88e7b Merge pull request #41846 from frappe/mergify/bp/version-15-hotfix/pr-41685
refactor: enabling partial TDS application on partial invoice (backport #41685)
2024-06-11 10:53:48 +05:30
ruthra kumar
818a553668 test: is_opening flag for advance in separate party account
(cherry picked from commit 17f968e1e1)
2024-06-11 04:57:07 +00:00
ruthra kumar
1fc802da6f refactor: restrict to 'Advance in Separate Party Account' type
(cherry picked from commit c36f0e4a33)
2024-06-11 04:57:07 +00:00
ruthra kumar
c664a27b1e refactor: is_opening in payment entry
(cherry picked from commit 05d17d0d73)
2024-06-11 04:57:07 +00:00
ruthra kumar
c0668373c7 refactor: enabling partial TDS application on partial invoice
(cherry picked from commit 091c5496b2)
2024-06-11 10:23:53 +05:30
ruthra kumar
45edc57af8 Merge pull request #41839 from frappe/mergify/bp/version-15-hotfix/pr-41793
fix: calculate totals and taxes (backport #41793)
2024-06-10 15:23:01 +05:30
Nihantra C. Patel
5544309048 fix: calculate totals and taxes
(cherry picked from commit a7ec0c1cec)
2024-06-10 09:49:03 +00:00
mergify[bot]
1ead66721d fix: terms and conditions for material request (backport #41834) (#41837)
fix: terms and conditions for material request (#41834)

(cherry picked from commit 4b026d66dc)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-06-10 15:17:19 +05:30
mergify[bot]
30c0b2bb54 fix: do not fetch items with no active BOM in PP (backport #41833) (#41835)
fix: do not fetch items with no active BOM in PP (#41833)

(cherry picked from commit 36413d14d8)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-06-10 15:16:57 +05:30
Frappe PR Bot
f2909e9784 chore(release): Bumped to Version 15.27.2
## [15.27.2](https://github.com/frappe/erpnext/compare/v15.27.1...v15.27.2) (2024-06-10)

### Bug Fixes

* Add additional condition application ([45c16f6](45c16f615c))
* fixing Item-wise sales register ([9a629f8](9a629f860c))
* fixing Item-wise sales register [#41373](https://github.com/frappe/erpnext/issues/41373) ([d75d64e](d75d64eb25))
* fixing Item-wise sales register and purchase register [#41373](https://github.com/frappe/erpnext/issues/41373) ([50ef81a](50ef81a997))
* Item-wise Sales and Purchase register with no item codes [#41373](https://github.com/frappe/erpnext/issues/41373) ([3e0123b](3e0123ba7b))
2024-06-10 04:54:40 +00:00
Deepesh Garg
cd478dd74f Merge pull request #41831 from frappe/mergify/bp/version-15/pr-41827
fix: Item-wise Sales and Purchase register with no item codes (#41424)
2024-06-10 10:23:23 +05:30
Deepesh Garg
14c98fe6f0 chore: resolve conflicts
(cherry picked from commit c29db5afcd)
2024-06-10 04:52:42 +00:00
Deepesh Garg
45c16f615c fix: Add additional condition application
(cherry picked from commit 8ec364df6f)

# Conflicts:
#	erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
(cherry picked from commit 1fca6ea5e7)
2024-06-10 04:52:42 +00:00
Deepesh Garg
7ca848c2db chore: update condition queries in qb
(cherry picked from commit d2af36e1eb)

# Conflicts:
#	erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
#	erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
(cherry picked from commit 209af58f83)
2024-06-10 04:52:41 +00:00
Poorvi-R-Bhat
d75d64eb25 fix: fixing Item-wise sales register #41373
(cherry picked from commit eafa88b8e9)

# Conflicts:
#	erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
(cherry picked from commit 5e0fa1b75e)
2024-06-10 04:52:41 +00:00
Poorvi-R-Bhat
50ef81a997 fix: fixing Item-wise sales register and purchase register #41373
(cherry picked from commit 76073ae228)

# Conflicts:
#	erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
(cherry picked from commit a4b28ba354)
2024-06-10 04:52:41 +00:00
Poorvi-R-Bhat
3e0123ba7b fix: Item-wise Sales and Purchase register with no item codes #41373
(cherry picked from commit 1b45ecfcae)

# Conflicts:
#	erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
#	erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
(cherry picked from commit 96405e8e49)
2024-06-10 04:52:41 +00:00
Poorvi
9a629f860c fix: fixing Item-wise sales register
(cherry picked from commit c90185f533)
(cherry picked from commit f36db7ebaf)
2024-06-10 04:52:41 +00:00
Deepesh Garg
873a0dd844 Merge pull request #41827 from frappe/mergify/bp/version-15-hotfix/pr-41424
fix: Item-wise Sales and Purchase register with no item codes (#41424)
2024-06-09 20:19:33 +05:30
Deepesh Garg
c29db5afcd chore: resolve conflicts 2024-06-09 20:02:39 +05:30
Deepesh Garg
1fca6ea5e7 fix: Add additional condition application
(cherry picked from commit 8ec364df6f)

# Conflicts:
#	erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
2024-06-09 06:13:28 +00:00
Deepesh Garg
209af58f83 chore: update condition queries in qb
(cherry picked from commit d2af36e1eb)

# Conflicts:
#	erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
#	erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
2024-06-09 06:13:28 +00:00
Poorvi-R-Bhat
5e0fa1b75e fix: fixing Item-wise sales register #41373
(cherry picked from commit eafa88b8e9)

# Conflicts:
#	erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
2024-06-09 06:13:28 +00:00
Poorvi-R-Bhat
a4b28ba354 fix: fixing Item-wise sales register and purchase register #41373
(cherry picked from commit 76073ae228)

# Conflicts:
#	erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
2024-06-09 06:13:27 +00:00
Poorvi-R-Bhat
96405e8e49 fix: Item-wise Sales and Purchase register with no item codes #41373
(cherry picked from commit 1b45ecfcae)

# Conflicts:
#	erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
#	erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
2024-06-09 06:13:27 +00:00
Poorvi
f36db7ebaf fix: fixing Item-wise sales register
(cherry picked from commit c90185f533)
2024-06-09 06:13:26 +00:00
mergify[bot]
a7971cf844 fix: stock_value_change for legacy serial nos (backport #41825) (#41826)
fix: stock_value_change for legacy serial nos (#41825)

(cherry picked from commit f6a4d391c0)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-06-07 23:05:50 +05:30
ruthra kumar
3f1d97259a Merge pull request #41823 from ruthra-kumar/enable_no_copy_for_timesheet
fix: enable no copy for timesheet in Sales Invoice
2024-06-07 20:24:44 +05:30
mergify[bot]
8d31243a42 fix: valuation rate for backdated legacy serial/batches (backport #41788) (#41821)
fix: valuation rate for backdated legacy serial/batches (#41788)

(cherry picked from commit 3956354e08)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-06-07 18:25:58 +05:30
ruthra kumar
f1ad0f6f26 fix: enable no_copy for timesheet in sales invoice 2024-06-07 16:50:21 +05:30
mergify[bot]
4d37739dd3 fix(Vehicle): allow doc renaming and add test (backport #41729) (#41785)
* fix(Vehicle): allow doc renaming and add test (#41729)

fix(Vehicle): allow renaming doc and add test

(cherry picked from commit c76f466528)

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

* fix: resolve conflicts for backporting

---------

Co-authored-by: Viny Selopal <52369157+vinyselopal@users.noreply.github.com>
Co-authored-by: Viny Selopal <viny0698@gmail.com>
2024-06-07 15:45:50 +05:30
mergify[bot]
4d3e883130 fix: typo in method - holiday list (backport #41811) (#41812)
fix: typo in method - holiday list (#41811)

(cherry picked from commit 699cfd85c6)

Co-authored-by: Nihantra C. Patel <141945075+Nihantra-Patel@users.noreply.github.com>
2024-06-06 19:46:02 +05:30
Frappe PR Bot
df4307fef8 chore(release): Bumped to Version 15.27.1
## [15.27.1](https://github.com/frappe/erpnext/compare/v15.27.0...v15.27.1) (2024-06-06)

### Bug Fixes

* Add posting date to key for consolidated view ([edd4e67](edd4e674d3))
2024-06-06 12:20:49 +00:00
Deepesh Garg
6039fb75e7 Merge pull request #41810 from frappe/mergify/bp/version-15/pr-41809
fix: Add posting date to key for consolidated view (#41808)
2024-06-06 17:49:05 +05:30
Deepesh Garg
edd4e674d3 fix: Add posting date to key for consolidated view
(cherry picked from commit 2c164b47a1)
(cherry picked from commit 94720851c6)
2024-06-06 12:18:29 +00:00
Deepesh Garg
b4838ab098 Merge pull request #41802 from frappe/mergify/bp/version-15-hotfix/pr-41727
fix: Do no apply pricing rule on qty change for mapped docs (#41727)
2024-06-06 17:48:25 +05:30
Deepesh Garg
3507d2e37d Merge pull request #41809 from frappe/mergify/bp/version-15-hotfix/pr-41808
fix: Add posting date to key for consolidated view (#41808)
2024-06-06 17:47:35 +05:30
Deepesh Garg
94720851c6 fix: Add posting date to key for consolidated view
(cherry picked from commit 2c164b47a1)
2024-06-06 12:15:12 +00:00
ruthra kumar
08c06d1142 Merge pull request #41807 from frappe/mergify/bp/version-15-hotfix/pr-41804
chore: repost settings permission overhaul (backport #41804)
2024-06-06 16:34:28 +05:30
ruthra kumar
0e562e9933 chore: repost settings permission overhaul
(cherry picked from commit 458d8f5ed8)
2024-06-06 16:14:27 +05:30
Deepesh Garg
822bc581f3 fix: Do no apply pricing rule on qty change for mapped docs
(cherry picked from commit 2b1242170c)
2024-06-06 03:44:14 +00:00
Frappe PR Bot
dcdaa02173 chore(release): Bumped to Version 15.27.0
# [15.27.0](https://github.com/frappe/erpnext/compare/v15.26.1...v15.27.0) (2024-06-05)

### Bug Fixes

* Auditor permissions (backport [#41659](https://github.com/frappe/erpnext/issues/41659)) ([#41661](https://github.com/frappe/erpnext/issues/41661)) ([27f3981](27f3981269))
* bank account no fieldname mismatch (backport [#41778](https://github.com/frappe/erpnext/issues/41778)) ([#41783](https://github.com/frappe/erpnext/issues/41783)) ([b94a6f2](b94a6f2d5c))
* batch selection issue (backport [#41692](https://github.com/frappe/erpnext/issues/41692)) ([#41693](https://github.com/frappe/erpnext/issues/41693)) ([24775db](24775db97e))
* **Company:** Allow Equity type account in unrealised exchange gain/loss account (backport [#41740](https://github.com/frappe/erpnext/issues/41740)) ([#41747](https://github.com/frappe/erpnext/issues/41747)) ([460ee5d](460ee5d542))
* condition filter for transaction pricing rule ([e493b11](e493b11ae3))
* convert percentage from str to float (backport [#41766](https://github.com/frappe/erpnext/issues/41766)) ([#41776](https://github.com/frappe/erpnext/issues/41776)) ([cd0852f](cd0852f05d))
* Copy depreciation schedule on asset split only if it exists (backport [#41775](https://github.com/frappe/erpnext/issues/41775)) ([#41784](https://github.com/frappe/erpnext/issues/41784)) ([03bf480](03bf48032e))
* filtered out data having none values in date field (backport [#41457](https://github.com/frappe/erpnext/issues/41457)) ([#41765](https://github.com/frappe/erpnext/issues/41765)) ([97b1b7f](97b1b7f842))
* Filters in account balance report ([0b9d89b](0b9d89b966))
* French chart of account the 4191 code must be of type Income Account ([797b718](797b718e5a))
* get assets received but not billed account only if any asset item is received (backport [#41734](https://github.com/frappe/erpnext/issues/41734)) ([#41735](https://github.com/frappe/erpnext/issues/41735)) ([638c944](638c94451e))
* Ignore disabling default currency field while creating new company (backport [#41699](https://github.com/frappe/erpnext/issues/41699)) ([#41761](https://github.com/frappe/erpnext/issues/41761)) ([dc922ab](dc922abfbe))
* incorrect accumulated depr amount in asset depr ledger (backport [#41654](https://github.com/frappe/erpnext/issues/41654)) ([#41763](https://github.com/frappe/erpnext/issues/41763)) ([a93cd02](a93cd02d54))
* incorrect batch selection while sales return (backport [#41768](https://github.com/frappe/erpnext/issues/41768)) ([#41772](https://github.com/frappe/erpnext/issues/41772)) ([95c0dc9](95c0dc9b38))
* key error for stock ledger report (backport [#41700](https://github.com/frappe/erpnext/issues/41700)) ([#41703](https://github.com/frappe/erpnext/issues/41703)) ([b22d714](b22d714103))
* link opportunity in prospect after creating opportunity from prospect (backport [#41769](https://github.com/frappe/erpnext/issues/41769)) ([#41777](https://github.com/frappe/erpnext/issues/41777)) ([d30aee5](d30aee5dd6))
* local var referenced before assignment (backport [#41780](https://github.com/frappe/erpnext/issues/41780)) ([#41782](https://github.com/frappe/erpnext/issues/41782)) ([3777cae](3777cae860))
* **minor:** corrected wrong filter condition (backport [#41755](https://github.com/frappe/erpnext/issues/41755)) ([#41762](https://github.com/frappe/erpnext/issues/41762)) ([a67fa68](a67fa6863e))
* payment term when creating PO from SO (backport [#41376](https://github.com/frappe/erpnext/issues/41376)) ([#41744](https://github.com/frappe/erpnext/issues/41744)) ([806ff5b](806ff5bc19))
* Select both account number and company in the account form (backport [#41731](https://github.com/frappe/erpnext/issues/41731)) ([#41737](https://github.com/frappe/erpnext/issues/41737)) ([c5d72cd](c5d72cd934))
* show material to supplier button (backport [#41686](https://github.com/frappe/erpnext/issues/41686)) ([#41688](https://github.com/frappe/erpnext/issues/41688)) ([1a24914](1a24914aed))
* **stock settings:** Allowed any number in stock frozen upto days field (backport [#41736](https://github.com/frappe/erpnext/issues/41736)) ([#41749](https://github.com/frappe/erpnext/issues/41749)) ([278682f](278682f4df))
* test for pricing rule transaction with cond ([0823de8](0823de86ad))
* Transaction currency value in Journal Entry (backport [#41717](https://github.com/frappe/erpnext/issues/41717)) ([#41720](https://github.com/frappe/erpnext/issues/41720)) ([77f58ca](77f58caa93))
* TypeError: 'float' object is not iterable (backport [#41758](https://github.com/frappe/erpnext/issues/41758)) ([#41764](https://github.com/frappe/erpnext/issues/41764)) ([12addb7](12addb7565))
* Use full name instead of abbreviation ([#41759](https://github.com/frappe/erpnext/issues/41759)) ([5448d5d](5448d5d1cb))
* work order created message pops up if no items are selected (backport [#41677](https://github.com/frappe/erpnext/issues/41677)) ([#41682](https://github.com/frappe/erpnext/issues/41682)) ([4e9faf6](4e9faf6ad6))

### Features

* optional to reconcile all serial nos / batches in stock reconciliation (backport [#41696](https://github.com/frappe/erpnext/issues/41696)) ([#41713](https://github.com/frappe/erpnext/issues/41713)) ([38249f4](38249f4170))
2024-06-05 01:20:36 +00:00
ruthra kumar
cd511c5277 Merge pull request #41773 from frappe/version-15-hotfix
chore: release v15
2024-06-05 06:49:10 +05:30
mergify[bot]
03bf48032e fix: Copy depreciation schedule on asset split only if it exists (backport #41775) (#41784)
fix: Copy depreciation schedule on asset split only if it exists (#41775)

(cherry picked from commit 689e1cfc23)

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2024-06-04 22:25:40 +05:30
mergify[bot]
b94a6f2d5c fix: bank account no fieldname mismatch (backport #41778) (#41783)
fix: bank account no fieldname mismatch (#41778)

(cherry picked from commit d163dbec7d)

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2024-06-04 22:25:28 +05:30
mergify[bot]
3777cae860 fix: local var referenced before assignment (backport #41780) (#41782)
fix: local var referenced before assignment (#41780)

(cherry picked from commit 4ff3e6caaa)

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2024-06-04 22:25:20 +05:30
mergify[bot]
a67fa6863e fix(minor): corrected wrong filter condition (backport #41755) (#41762)
* fix(minor): corrected wrong filter condition (#41755)

(cherry picked from commit 60eb03a6c6)

# Conflicts:
#	erpnext/assets/report/fixed_asset_register/fixed_asset_register.py

* fix: resolved conflict

---------

Co-authored-by: Khushi Rawat <142375893+khushi8112@users.noreply.github.com>
Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2024-06-04 21:53:28 +05:30
mergify[bot]
cd0852f05d fix: convert percentage from str to float (backport #41766) (#41776)
fix: convert percentage from str to float (#41766)

(cherry picked from commit 71f5470dfd)

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2024-06-04 21:50:28 +05:30
mergify[bot]
d30aee5dd6 fix: link opportunity in prospect after creating opportunity from prospect (backport #41769) (#41777)
fix: link opportunity in prospect after creating opportunity from prospect (#41769)

(cherry picked from commit 12e48f0b63)

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2024-06-04 21:50:18 +05:30
mergify[bot]
12addb7565 fix: TypeError: 'float' object is not iterable (backport #41758) (#41764)
* fix: TypeError: 'float' object is not iterable (#41758)

(cherry picked from commit cf508ce1ed)

# Conflicts:
#	erpnext/manufacturing/doctype/bom_creator/bom_creator.py
#	erpnext/public/js/bom_configurator/bom_configurator.bundle.js

* chore: fix conflicts

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-06-04 14:54:34 +05:30
mergify[bot]
95c0dc9b38 fix: incorrect batch selection while sales return (backport #41768) (#41772)
fix: incorrect batch selection while sales return (#41768)

(cherry picked from commit cb6d8afa26)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-06-04 14:54:20 +05:30
mergify[bot]
97b1b7f842 fix: filtered out data having none values in date field (backport #41457) (#41765)
* fix: filtered out data having none values in date field

(cherry picked from commit 3bd455ac85)

* style: code optimization

(cherry picked from commit a36b7fb95a)

---------

Co-authored-by: Khushi Rawat <142375893+khushi8112@users.noreply.github.com>
2024-06-04 12:21:16 +05:30
mergify[bot]
27f3981269 fix: Auditor permissions (backport #41659) (#41661)
* fix: allow Auditor to select a company

(cherry picked from commit 06401cc84f)

* fix: allow Auditor to read a Fiscal Year

(cherry picked from commit eaa4efbc45)

---------

Co-authored-by: barredterra <14891507+barredterra@users.noreply.github.com>
2024-06-04 12:19:36 +05:30
mergify[bot]
77f58caa93 fix: Transaction currency value in Journal Entry (backport #41717) (#41720)
fix: Transaction currency value in Journal Entry

(cherry picked from commit 169d77da54)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2024-06-04 12:18:28 +05:30
mergify[bot]
a93cd02d54 fix: incorrect accumulated depr amount in asset depr ledger (backport #41654) (#41763)
fix: incorrect accumulated depr amount in asset depr ledger (#41654)

* fix: incorrect accumulated depr amount in asset depr ledger

* refactor: depreciation amount retrieval with query builder

* style: apply formatting changes from pre-commit

(cherry picked from commit a8fc32dc32)

Co-authored-by: Khushi Rawat <142375893+khushi8112@users.noreply.github.com>
2024-06-04 12:03:55 +05:30
mergify[bot]
dc922abfbe fix: Ignore disabling default currency field while creating new company (backport #41699) (#41761)
fix: Ignore disabling default currency field while creating new company (#41699)

(cherry picked from commit 80f6228d45)

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2024-06-04 11:55:35 +05:30
Nabin Hait
5448d5d1cb fix: Use full name instead of abbreviation (#41759) 2024-06-04 11:41:30 +05:30
mergify[bot]
278682f4df fix(stock settings): Allowed any number in stock frozen upto days field (backport #41736) (#41749)
fix(stock settings): Allowed any number in stock frozen upto days field (#41736)

(cherry picked from commit 740c495014)

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2024-06-04 11:22:32 +05:30
mergify[bot]
460ee5d542 fix(Company): Allow Equity type account in unrealised exchange gain/loss account (backport #41740) (#41747)
fix(Company): Allow Equity type account in unrealised exchange gain/loss account (#41740)

(cherry picked from commit d30c797d24)

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2024-06-03 18:38:25 +05:30
mergify[bot]
806ff5bc19 fix: payment term when creating PO from SO (backport #41376) (#41744)
fix: payment term when creating PO from SO (#41376)

* fix: payment term when creating PO from SO

* fix: payment term when creating PO from SO

* fix: payment term when creating PO from SO

* fix: payment term when creating PO from SO

(cherry picked from commit 441596f795)

Co-authored-by: Nihantra C. Patel <141945075+Nihantra-Patel@users.noreply.github.com>
2024-06-03 18:37:39 +05:30
mergify[bot]
c5d72cd934 fix: Select both account number and company in the account form (backport #41731) (#41737)
fix: Select both account number and company in the account form (#41731)

* fix: Both the account number and the company should be selected in the account form

* fix: select both account number and company in the account form

(cherry picked from commit 5a75c847fe)

Co-authored-by: Nihantra C. Patel <141945075+Nihantra-Patel@users.noreply.github.com>
2024-06-03 18:36:58 +05:30
ruthra kumar
dd0cd8c4f8 Merge pull request #41746 from frappe/mergify/bp/version-15-hotfix/pr-41742
chore: System Manager should have  submit permission for repost doctypes (backport #41742)
2024-06-03 18:33:44 +05:30
ruthra kumar
d5158c6062 chore: Sys Manager shluld have submit permission for repost doctypes
(cherry picked from commit 4558f64c0f)
2024-06-03 12:21:05 +00:00
mergify[bot]
638c94451e fix: get assets received but not billed account only if any asset item is received (backport #41734) (#41735)
fix: get assets received but not billed account only if any asset item is received (#41734)

fix: get assets received but not billed account only if any asset item received
(cherry picked from commit 39885b2b01)

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2024-06-03 16:18:29 +05:30
ruthra kumar
8ff5a67b99 Merge pull request #41714 from frappe/mergify/bp/version-15-hotfix/pr-41658
fix: condition-wise filter for transaction based pricing rule (backport #41658)
2024-06-01 18:31:11 +05:30
mergify[bot]
38249f4170 feat: optional to reconcile all serial nos / batches in stock reconciliation (backport #41696) (#41713)
* feat: optional to reconcile all serial nos / batches in stock reconciliation (#41696)

feat: optional to reconcile all serial/batch
(cherry picked from commit ee846f5950)

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

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-06-01 18:30:33 +05:30
Dany Robert
0823de86ad fix: test for pricing rule transaction with cond
(cherry picked from commit 3f2343614b)
2024-06-01 12:45:20 +00:00
Dany Robert
e493b11ae3 fix: condition filter for transaction pricing rule
(cherry picked from commit e3fd82766f)
2024-06-01 12:45:19 +00:00
ruthra kumar
f73737db50 Merge pull request #41706 from frappe/mergify/bp/version-15-hotfix/pr-41232
fix: French chart of account the 4191 code must be of type Income Account (backport #41232)
2024-06-01 16:06:59 +05:30
Deepesh Garg
8d997c6703 Merge pull request #41678 from frappe/mergify/bp/version-15-hotfix/pr-41676
fix: Filters in account balance report (#41676)
2024-05-31 20:02:20 +05:30
Florian HENRY
797b718e5a fix: French chart of account the 4191 code must be of type Income Account
(cherry picked from commit 72057adc94)
2024-05-31 09:56:01 +00:00
mergify[bot]
b22d714103 fix: key error for stock ledger report (backport #41700) (#41703)
fix: key error for stock ledger report (#41700)

(cherry picked from commit 4e37ed9033)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-05-31 12:22:07 +05:30
Frappe PR Bot
55121a22a5 chore(release): Bumped to Version 15.26.1
## [15.26.1](https://github.com/frappe/erpnext/compare/v15.26.0...v15.26.1) (2024-05-30)

### Bug Fixes

* batch selection issue (backport [#41692](https://github.com/frappe/erpnext/issues/41692)) (backport [#41693](https://github.com/frappe/erpnext/issues/41693)) ([#41694](https://github.com/frappe/erpnext/issues/41694)) ([64835b9](64835b99ec))
2024-05-30 13:57:20 +00:00
mergify[bot]
64835b99ec fix: batch selection issue (backport #41692) (backport #41693) (#41694)
fix: batch selection issue (backport #41692) (#41693)

fix: batch selection issue (#41692)

(cherry picked from commit 968120d0eb)

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

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2024-05-30 19:26:02 +05:30
mergify[bot]
4e9faf6ad6 fix: work order created message pops up if no items are selected (backport #41677) (#41682)
fix: work order created message pops up if no items are selected (#41677)

(cherry picked from commit 388ba7f945)

Co-authored-by: Nijith anil <83776819+nijithanil@users.noreply.github.com>
2024-05-30 18:51:26 +05:30
mergify[bot]
24775db97e fix: batch selection issue (backport #41692) (#41693)
fix: batch selection issue (#41692)

(cherry picked from commit 968120d0eb)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-05-30 18:49:12 +05:30
mergify[bot]
1a24914aed fix: show material to supplier button (backport #41686) (#41688)
fix: show material to supplier button (#41686)

(cherry picked from commit be9f960705)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-05-30 16:13:53 +05:30
Frappe PR Bot
56d83d44be chore(release): Bumped to Version 15.26.0
# [15.26.0](https://github.com/frappe/erpnext/compare/v15.25.0...v15.26.0) (2024-05-29)

### Bug Fixes

* add in some indices to speed up Purchase Order deletion ([3ed3b21](3ed3b214c8))
* cost center filter according to the company in project ([193b844](193b844bf1))
* cost center filter according to the company in project ([e6aaab3](e6aaab38b0))
* cost center filter according to the company in project ([563e15e](563e15e1c8))
* multiple issues related to serial and batch bundle (backport [#41662](https://github.com/frappe/erpnext/issues/41662)) ([#41668](https://github.com/frappe/erpnext/issues/41668)) ([dc0bb22](dc0bb220ed))
* not allow template item in product bundle item ([8fb3294](8fb3294674))
* not allow template item in product bundle item ([b64f5a4](b64f5a4e54))
* not allow template item in product bundle item - v15/v14 ([9864377](9864377b47))
* opening stock not showing in the stock ledger report for the bat… (backport [#41584](https://github.com/frappe/erpnext/issues/41584)) ([#41590](https://github.com/frappe/erpnext/issues/41590)) ([162ec7d](162ec7d6e8))
* SCR batch qty issue (backport [#41595](https://github.com/frappe/erpnext/issues/41595)) ([#41599](https://github.com/frappe/erpnext/issues/41599)) ([17ea958](17ea958170))
* set expense account as Assets RBNB only if it is booked in linked PR (backport [#41368](https://github.com/frappe/erpnext/issues/41368)) ([#41673](https://github.com/frappe/erpnext/issues/41673)) ([0a0970e](0a0970e0e7))
* Unable to 'Get Suppliers' using tags in Request for Quotation (backport [#41626](https://github.com/frappe/erpnext/issues/41626)) ([#41627](https://github.com/frappe/erpnext/issues/41627)) ([ca855e8](ca855e8ab8))

### Features

* Add Party details to Serial No Ledger Report ([#41656](https://github.com/frappe/erpnext/issues/41656)) ([7462de6](7462de66ce))

### Performance Improvements

* sales order UI render (backport [#41591](https://github.com/frappe/erpnext/issues/41591)) ([#41592](https://github.com/frappe/erpnext/issues/41592)) ([09d112a](09d112a061))
2024-05-29 07:54:16 +00:00
ruthra kumar
e940d13068 Merge pull request #41666 from frappe/version-15-hotfix
chore: release v15
2024-05-29 13:22:52 +05:30
Deepesh Garg
0b9d89b966 fix: Filters in account balance report
(cherry picked from commit 1c9fe691ea)
2024-05-29 07:20:47 +00:00
mergify[bot]
0a0970e0e7 fix: set expense account as Assets RBNB only if it is booked in linked PR (backport #41368) (#41673)
fix: set expense account as Assets RBNB only if it is booked in linked PR (#41368)

* fix: set expense account as Assets RBNB only if it is booked in linked PR

* fix: broken translations

(cherry picked from commit 014486de39)

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2024-05-29 11:07:07 +05:30
Deepesh Garg
d29686f882 Merge pull request #41375 from Nihantra-Patel/no_add_variant_item_15-14
fix: not allow template item in product bundle item - v15/v14
2024-05-29 10:30:47 +05:30
mergify[bot]
dc0bb220ed fix: multiple issues related to serial and batch bundle (backport #41662) (#41668)
* fix: multiple issues related to serial and batch bundle (#41662)

(cherry picked from commit ce834f5dba)

# Conflicts:
#	erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
#	erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json

* chore: fix conflicts

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-05-28 21:57:13 +05:30
Marica
2e8ae4dc30 Merge pull request #41667 from frappe/mergify/bp/version-15-hotfix/pr-41656
feat: Add Party details to Serial No Ledger Report (backport #41656)
2024-05-28 16:36:44 +05:30
Marica
7462de66ce feat: Add Party details to Serial No Ledger Report (#41656)
- Since no party details are present in Serial No anymore, it is hard to get a view of which SN was sold to/purchased from which party
- Add Party fields to report

(cherry picked from commit e3cf53a8b7)
2024-05-28 09:59:35 +00:00
ruthra kumar
c8098e9691 Merge pull request #41649 from frappe/mergify/bp/version-15-hotfix/pr-41401
fix: cost center filter according to the company in project (backport #41401)
2024-05-27 10:42:09 +05:30
Nihantra C. Patel
193b844bf1 fix: cost center filter according to the company in project
(cherry picked from commit 501c6aa126)
2024-05-27 04:41:12 +00:00
Nihantra C. Patel
e6aaab38b0 fix: cost center filter according to the company in project
(cherry picked from commit cfde8088b4)
2024-05-27 04:41:12 +00:00
Nihantra C. Patel
563e15e1c8 fix: cost center filter according to the company in project
(cherry picked from commit 249e8264dd)
2024-05-27 04:41:11 +00:00
mergify[bot]
ca855e8ab8 fix: Unable to 'Get Suppliers' using tags in Request for Quotation (backport #41626) (#41627)
fix: Unable to 'Get Suppliers' using tags in Request for Quotation (#41626)

(cherry picked from commit 683c1f04c8)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-05-24 13:09:15 +05:30
Akhil Narang
7a0aef218f Merge pull request #41625 from frappe/mergify/bp/version-15-hotfix/pr-41601
fix: add in some indices to speed up Purchase Order deletion (backport #41601)
2024-05-24 12:27:29 +05:30
Akhil Narang
8f24fd6a60 Merge pull request #41611 from frappe/mergify/bp/version-15-hotfix/pr-41609
refactor: use `json` directly instead of using `frappe.json` (backport #41609)
2024-05-24 11:07:12 +05:30
Akhil Narang
c68afbc715 chore: resolve conflicts 2024-05-24 11:05:59 +05:30
Akhil Narang
3ed3b214c8 fix: add in some indices to speed up Purchase Order deletion
`tabSales Order`.`inter_company_order_reference`
`tabSales Invoice Item`.`purchase_order`

Signed-off-by: Akhil Narang <me@akhilnarang.dev>
(cherry picked from commit 0303d7dbdc)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
2024-05-24 05:28:24 +00:00
ruthra kumar
26569742fd Merge pull request #41605 from frappe/mergify/bp/version-15-hotfix/pr-41603
refactor: remove 'format:' based naming in internal doctypes (backport #41603)
2024-05-24 10:54:55 +05:30
ruthra kumar
d96c5885a3 Merge pull request #41607 from frappe/mergify/bp/version-15-hotfix/pr-41594
refactor: 'Payment Period Based On Invoice Date' report (backport #41594)
2024-05-24 10:53:51 +05:30
Akhil Narang
7af1b5d4ec refactor: use json directly instead of using frappe.json (#41609)
(cherry picked from commit 3f06f1905f)
Signed-off-by: Akhil Narang <me@akhilnarang.dev>
2024-05-24 10:51:09 +05:30
ruthra kumar
1ad2f06500 chore: resolve conflicts 2024-05-23 17:49:05 +05:30
ruthra kumar
22dd642413 refactor: replace against_voucher with against_voucher_no
(cherry picked from commit c4e2abb973)
2024-05-23 12:16:51 +00:00
ruthra kumar
37c90ef873 refactor: remove debit and credit
(cherry picked from commit 014b542cf3)
2024-05-23 12:16:50 +00:00
ruthra kumar
e0cb3ecc0f refactor: query payment ledger for payments
(cherry picked from commit cb3c20dcd3)
2024-05-23 12:16:50 +00:00
ruthra kumar
3176bb480f refactor: remove 'format:' based naming in internal doctypes
(cherry picked from commit e2ec3e453a)

# Conflicts:
#	erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.json
#	erpnext/accounts/doctype/repost_payment_ledger/repost_payment_ledger.json
2024-05-23 12:15:33 +00:00
mergify[bot]
17ea958170 fix: SCR batch qty issue (backport #41595) (#41599)
fix: SCR batch qty issue (#41595)

(cherry picked from commit 418439b58f)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-05-23 16:30:30 +05:30
Akhil Narang
c16debb8ee Merge pull request #41596 from frappe/mergify/bp/version-15-hotfix/pr-41576
chore: sync ruff config with framework (backport #41576)
2024-05-23 14:15:08 +05:30
Ankush Menat
58feaf1f4a ci: ruff only fix imports
(cherry picked from commit 85b1a8001b)
2024-05-23 07:22:22 +00:00
Ankush Menat
dc813ddaec ci: dont auto apply fixes
I've personally encountered ~5 instances of bad auto fixes, let
developers apply the fixes.

(cherry picked from commit 3b4913ec81)
2024-05-23 07:22:22 +00:00
mergify[bot]
09d112a061 perf: sales order UI render (backport #41591) (#41592)
perf: sales order UI render (#41591)

(cherry picked from commit ad817cddad)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-05-23 11:50:34 +05:30
mergify[bot]
162ec7d6e8 fix: opening stock not showing in the stock ledger report for the bat… (backport #41584) (#41590)
fix: opening stock not showing in the stock ledger report for the bat… (#41584)

fix: opening stock not showing in the stock ledger report for the batch filter
(cherry picked from commit 04a49cef24)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-05-22 19:58:27 +05:30
Frappe PR Bot
a3fdfba46f chore(release): Bumped to Version 15.25.0
# [15.25.0](https://github.com/frappe/erpnext/compare/v15.24.1...v15.25.0) (2024-05-22)

### Bug Fixes

* added validation message for low gross purchase amount (backport [#41502](https://github.com/frappe/erpnext/issues/41502)) ([#41553](https://github.com/frappe/erpnext/issues/41553)) ([89ba7a2](89ba7a22fb))
* Auto reconcile only after selecting bank account (backport [#41489](https://github.com/frappe/erpnext/issues/41489)) ([#41554](https://github.com/frappe/erpnext/issues/41554)) ([33eb251](33eb251727))
* bold total in exponential smoothing forecasting (backport [#41393](https://github.com/frappe/erpnext/issues/41393)) ([#41404](https://github.com/frappe/erpnext/issues/41404)) ([8101b51](8101b51899))
* BOM creator validation for parent row no (backport [#41413](https://github.com/frappe/erpnext/issues/41413)) ([#41558](https://github.com/frappe/erpnext/issues/41558)) ([953de99](953de995bc))
* convert invoice_portion value from str to float (backport [#41485](https://github.com/frappe/erpnext/issues/41485)) ([#41555](https://github.com/frappe/erpnext/issues/41555)) ([0a36139](0a36139ef4))
* correct report name in inner button (backport [#41568](https://github.com/frappe/erpnext/issues/41568)) ([#41572](https://github.com/frappe/erpnext/issues/41572)) ([4ccc426](4ccc426fec))
* create custom field for accounting dimensions only if the field not exists already ([#41557](https://github.com/frappe/erpnext/issues/41557)) ([68fa55c](68fa55ca02))
* Deadlock on submitting Sales Invoice (backport [#41417](https://github.com/frappe/erpnext/issues/41417)) ([#41562](https://github.com/frappe/erpnext/issues/41562)) ([49db19d](49db19d57a))
* Demo data clearing ([46ec61f](46ec61f0c9))
* Filters in trend reports ([52d5d7a](52d5d7a4d3))
* job card time logs overlap issue (backport [#41567](https://github.com/frappe/erpnext/issues/41567)) ([#41571](https://github.com/frappe/erpnext/issues/41571)) ([6554f19](6554f192fb))
* minor Dr and Cr between Purchase Receipt and Purchase Invoice ([3872cdc](3872cdc54a))
* not able to delete line items in the subcontracting receipt (backport [#41569](https://github.com/frappe/erpnext/issues/41569)) ([#41570](https://github.com/frappe/erpnext/issues/41570)) ([26c3235](26c3235bf9))
* not able to submit landed cost voucher (backport [#41481](https://github.com/frappe/erpnext/issues/41481)) ([#41486](https://github.com/frappe/erpnext/issues/41486)) ([a070ad7](a070ad786d))
* possible sql error on General Ledger ([e0a0cb7](e0a0cb7b25))
* print format bold for field "total" ([e36880d](e36880da2d))
* priority not working for multiple pricing rules (backport [#41516](https://github.com/frappe/erpnext/issues/41516)) ([#41525](https://github.com/frappe/erpnext/issues/41525)) ([5fd68f9](5fd68f9fcb))
* Show acheived amount and variance for parent item groups (backport [#41495](https://github.com/frappe/erpnext/issues/41495)) ([#41551](https://github.com/frappe/erpnext/issues/41551)) ([5e0bff3](5e0bff3a60))
* stock levels for batch (backport [#41494](https://github.com/frappe/erpnext/issues/41494)) ([#41501](https://github.com/frappe/erpnext/issues/41501)) ([938888c](938888cddd))
* timeout error while submitting purchase invoice (backport [#41520](https://github.com/frappe/erpnext/issues/41520)) ([#41522](https://github.com/frappe/erpnext/issues/41522)) ([fcd9f89](fcd9f89dee))
* typerror on hide_fields ([8ae9da5](8ae9da5484))
* Unable to add items in POS Invoice ([a90186f](a90186f9ae))
* validate reorder group warehouse (backport [#41478](https://github.com/frappe/erpnext/issues/41478)) ([#41480](https://github.com/frappe/erpnext/issues/41480)) ([a1ce514](a1ce514cf3))
* valuation rate for legacy serial nos (backport [#41543](https://github.com/frappe/erpnext/issues/41543)) ([#41545](https://github.com/frappe/erpnext/issues/41545)) ([f121b33](f121b33e29))

### Features

* Config to enable immutable ledger ([32c935b](32c935bc30))
* enter serial range in Serial/Batch Selector (backport [#41530](https://github.com/frappe/erpnext/issues/41530)) ([#41534](https://github.com/frappe/erpnext/issues/41534)) ([7225347](7225347387))
2024-05-22 08:49:10 +00:00
ruthra kumar
1b4987c5b4 Merge pull request #41574 from frappe/version-15-hotfix
chore: release v15
2024-05-22 14:17:49 +05:30
mergify[bot]
5e0bff3a60 fix: Show acheived amount and variance for parent item groups (backport #41495) (#41551)
fix: Show achieved amount and variance for parent item groups

(cherry picked from commit 8ac11ae88d)

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2024-05-21 18:02:37 +05:30
mergify[bot]
6554f192fb fix: job card time logs overlap issue (backport #41567) (#41571)
* fix: job card time logs overlap issue (#41567)

(cherry picked from commit ac4bcd5ca7)

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

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-05-21 15:01:45 +05:30
mergify[bot]
4ccc426fec fix: correct report name in inner button (backport #41568) (#41572)
fix: correct report name in inner button (#41568)

Previously, the report name was incorrectly listed as "Cash Flow Statement" instead of "Cash Flow". This mismatch caused the "Cash Flow" report to not open correctly in the chart of accounts tree view, as the report ID is "Cash Flow".

(cherry picked from commit d7df2cbdc5)

Co-authored-by: jabir-elat <44110258+jabir-elat@users.noreply.github.com>
2024-05-21 14:44:03 +05:30
mergify[bot]
26c3235bf9 fix: not able to delete line items in the subcontracting receipt (backport #41569) (#41570)
fix: not able to delete line items in the subcontracting receipt (#41569)

(cherry picked from commit 4261c3474b)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-05-21 14:41:13 +05:30
Nabin Hait
68fa55ca02 fix: create custom field for accounting dimensions only if the field not exists already (#41557) 2024-05-21 12:21:15 +05:30
mergify[bot]
49db19d57a fix: Deadlock on submitting Sales Invoice (backport #41417) (#41562)
fix: Deadlock on submitting Sales Invoice (#41417)

(cherry picked from commit 4e4c1e4bef)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-05-21 11:10:26 +05:30
mergify[bot]
953de995bc fix: BOM creator validation for parent row no (backport #41413) (#41558)
fix: BOM creator validation for parent row no (#41413)

fix: bom creator valiation for parent row no
(cherry picked from commit ae3f5a38e1)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-05-20 19:42:54 +05:30
mergify[bot]
0a36139ef4 fix: convert invoice_portion value from str to float (backport #41485) (#41555)
fix: convert invoice_portion value from str to float (#41485)

(cherry picked from commit a1d489195d)

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2024-05-20 18:36:48 +05:30
mergify[bot]
33eb251727 fix: Auto reconcile only after selecting bank account (backport #41489) (#41554)
fix: Auto reconcile only after selecting bank account (#41489)

(cherry picked from commit f24d61daea)

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2024-05-20 18:36:38 +05:30
mergify[bot]
89ba7a22fb fix: added validation message for low gross purchase amount (backport #41502) (#41553)
fix: added validation message for low gross purchase amount

(cherry picked from commit 8a30701893)

Co-authored-by: Khushi Rawat <142375893+khushi8112@users.noreply.github.com>
2024-05-20 18:36:26 +05:30
Frappe PR Bot
b1d345ba0b chore(release): Bumped to Version 15.24.1
## [15.24.1](https://github.com/frappe/erpnext/compare/v15.24.0...v15.24.1) (2024-05-20)

### Bug Fixes

* valuation rate for legacy serial nos (backport [#41543](https://github.com/frappe/erpnext/issues/41543)) (backport [#41545](https://github.com/frappe/erpnext/issues/41545)) ([#41546](https://github.com/frappe/erpnext/issues/41546)) ([4ddd6e6](4ddd6e685b))
2024-05-20 10:22:57 +00:00
mergify[bot]
4ddd6e685b fix: valuation rate for legacy serial nos (backport #41543) (backport #41545) (#41546)
fix: valuation rate for legacy serial nos (backport #41543) (#41545)

fix: valuation rate for legacy serial nos (#41543)

fix: valuation rate for serial nos
(cherry picked from commit 214b38f7c8)

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

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2024-05-20 15:51:42 +05:30
ruthra kumar
a853f6add8 Merge pull request #41547 from frappe/mergify/bp/version-15-hotfix/pr-41492
refactor: For advances booked in Separate account, reconciliation date can be configured (backport #41492)
2024-05-20 15:17:38 +05:30
ruthra kumar
6c455be6a7 test: reconciliation date for advance payments
(cherry picked from commit 30aa4e031d)
2024-05-20 09:30:14 +00:00
ruthra kumar
53e5fc16d5 refactor: enable no-copy on advance payment flags
(cherry picked from commit cafa2f52e5)
2024-05-20 09:30:14 +00:00
ruthra kumar
349caf895b chore: better description
(cherry picked from commit 000c1b49dc)
2024-05-20 09:30:14 +00:00
ruthra kumar
2b307ff526 refactor: advance payments, allow configurable reconciliation date
(cherry picked from commit 070e2d4d26)
2024-05-20 09:30:13 +00:00
mergify[bot]
f121b33e29 fix: valuation rate for legacy serial nos (backport #41543) (#41545)
fix: valuation rate for legacy serial nos (#41543)

fix: valuation rate for serial nos
(cherry picked from commit 214b38f7c8)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-05-20 13:57:40 +05:30
ruthra kumar
0d55a25e6c Merge pull request #41544 from ruthra-kumar/fix_filters_in_trends_reports
fix: Filters in trend reports
2024-05-20 11:10:24 +05:30
ruthra kumar
52d5d7a4d3 fix: Filters in trend reports 2024-05-20 10:36:42 +05:30
ruthra kumar
e5e1aadc9a Merge pull request #41526 from frappe/mergify/bp/version-15-hotfix/pr-41523
fix: minor Dr and Cr between Purchase Receipt and Purchase Invoice (backport #41523)
2024-05-20 09:17:25 +05:30
ruthra kumar
3872cdc54a fix: minor Dr and Cr between Purchase Receipt and Purchase Invoice 2024-05-20 08:31:12 +05:30
SelenSoft
c609436283 chore: Update turkish translations (#41286) 2024-05-19 15:30:17 +02:00
ruthra kumar
6052e37fc2 Merge pull request #41531 from frappe/mergify/bp/version-15-hotfix/pr-41529
refactor: ignore internal doctypes on Dunning cancellation (backport #41529)
2024-05-18 13:37:35 +05:30
mergify[bot]
7225347387 feat: enter serial range in Serial/Batch Selector (backport #41530) (#41534)
feat: enter serial range in Serial/Batch Selector (#41530)

* feat: util for generating serial nos from range string

* feat: enter serial range in Serial/Batch Selector

(cherry picked from commit 7b9fd394b0)

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2024-05-18 10:39:21 +05:30
mergify[bot]
5fd68f9fcb fix: priority not working for multiple pricing rules (backport #41516) (#41525)
* fix: priority not working for multiple pricing rules (#41516)

(cherry picked from commit 5cf5b18aea)

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

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-05-18 07:18:06 +05:30
ruthra kumar
97e6415afa refactor: ignore internal doctypes on Dunning cancellation
(cherry picked from commit 63c2cf13fd)
2024-05-17 15:31:36 +00:00
mergify[bot]
fcd9f89dee fix: timeout error while submitting purchase invoice (backport #41520) (#41522)
fix: timeout error while submitting purchase invoice (#41520)

(cherry picked from commit 61a4188440)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-05-17 16:56:11 +05:30
ruthra kumar
2bac4b1681 Merge pull request #41519 from frappe/mergify/bp/version-15-hotfix/pr-41517
fix: typerror on hide_fields (backport #41517)
2024-05-17 14:52:55 +05:30
ruthra kumar
8ae9da5484 fix: typerror on hide_fields
(cherry picked from commit deb9766f2a)
2024-05-17 09:20:33 +00:00
ruthra kumar
2f2615f9e2 Merge pull request #41513 from frappe/mergify/bp/version-15-hotfix/pr-41511
fix: possible sql error on General Ledger (backport #41511)
2024-05-17 11:11:59 +05:30
Frappe PR Bot
9b7bda958b chore(release): Bumped to Version 15.24.0
# [15.24.0](https://github.com/frappe/erpnext/compare/v15.23.3...v15.24.0) (2024-05-17)

### Features

* Config to enable immutable ledger ([68faf57](68faf575db))
2024-05-17 05:27:58 +00:00
Deepesh Garg
8c8b22bc61 Merge pull request #41509 from frappe/mergify/bp/version-15-hotfix/pr-41451
fix: Demo data clearing (#41451)
2024-05-17 10:57:01 +05:30
ruthra kumar
e0a0cb7b25 fix: possible sql error on General Ledger
(cherry picked from commit 76131f8e10)
2024-05-17 05:26:55 +00:00
Deepesh Garg
00144c92af Merge pull request #41508 from frappe/mergify/bp/version-15/pr-41438
feat: Config to enable immutable ledger
2024-05-17 10:56:45 +05:30
Deepesh Garg
7cbe03174f chore: resolve conflicts 2024-05-17 10:37:32 +05:30
Deepesh Garg
46ec61f0c9 fix: Demo data clearing
(cherry picked from commit d6fe220fab)
2024-05-17 03:47:50 +00:00
Deepesh Garg
e14d4e839f Merge pull request #41505 from frappe/mergify/bp/version-15-hotfix/pr-41504
fix: Unable to add items in POS Invoice (#41504)
2024-05-17 09:15:01 +05:30
Deepesh Garg
ce60b77fa5 chore: Resolve conflicts
(cherry picked from commit 9315607ee7)

# Conflicts:
#	erpnext/accounts/doctype/accounts_settings/accounts_settings.json
2024-05-17 03:42:44 +00:00
Deepesh Garg
f09a6d76a7 chore: Resolve conflicts
(cherry picked from commit 983c55eca1)
2024-05-17 03:42:43 +00:00
Deepesh Garg
68faf575db feat: Config to enable immutable ledger
(cherry picked from commit d56f52b0ba)

# Conflicts:
#	erpnext/accounts/doctype/accounts_settings/accounts_settings.json
#	erpnext/accounts/general_ledger.py
(cherry picked from commit 32c935bc30)

# Conflicts:
#	erpnext/accounts/doctype/accounts_settings/accounts_settings.json
2024-05-17 03:42:43 +00:00
Deepesh Garg
e1f8148129 Merge pull request #41438 from frappe/mergify/bp/version-15-hotfix/pr-41429
feat: Config to enable immutable ledger (#41429)
2024-05-17 09:11:24 +05:30
Deepesh Garg
414a91187f Merge branch 'version-15-hotfix' into mergify/bp/version-15-hotfix/pr-41429 2024-05-16 21:02:55 +05:30
Deepesh Garg
9315607ee7 chore: Resolve conflicts 2024-05-16 20:59:47 +05:30
Deepesh Garg
983c55eca1 chore: Resolve conflicts 2024-05-16 20:27:50 +05:30
Deepesh Garg
a90186f9ae fix: Unable to add items in POS Invoice
(cherry picked from commit 7a3f8a5afb)
2024-05-16 14:02:49 +00:00
mergify[bot]
938888cddd fix: stock levels for batch (backport #41494) (#41501)
fix: stock levels for batch (#41494)

(cherry picked from commit 500c546691)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-05-16 16:33:02 +05:30
mergify[bot]
7620ae203e chore(BOM Explorer): display items in the same order as in the BOM (backport #41496) (#41498)
chore(BOM Explorer): display items in the same order as in the BOM (#41496)

(cherry picked from commit bd381cc0c6)

Co-authored-by: Samuel Danieli <23150094+scdanieli@users.noreply.github.com>
2024-05-16 15:59:06 +05:30
ruthra kumar
2e6c8632a1 Merge pull request #41499 from frappe/mergify/bp/version-15-hotfix/pr-40072
fix: print format bold for field "total" (backport #40072)
2024-05-16 15:32:17 +05:30
Nihantra C. Patel
e36880da2d fix: print format bold for field "total"
(cherry picked from commit 3c9640df27)
2024-05-16 09:50:48 +00:00
mergify[bot]
8101b51899 fix: bold total in exponential smoothing forecasting (backport #41393) (#41404)
fix: bold total in exponential smoothing forecasting (#41393)

* fix: bold total in exponential smoothing forecasting

* fix: bold total in exponential smoothing forecasting

(cherry picked from commit ba60b5911a)

Co-authored-by: Nihantra C. Patel <141945075+Nihantra-Patel@users.noreply.github.com>
2024-05-16 15:14:43 +05:30
Frappe PR Bot
c1f7d5a2d1 chore(release): Bumped to Version 15.23.3
## [15.23.3](https://github.com/frappe/erpnext/compare/v15.23.2...v15.23.3) (2024-05-15)

### Bug Fixes

* not able to submit landed cost voucher (backport [#41481](https://github.com/frappe/erpnext/issues/41481)) (backport [#41486](https://github.com/frappe/erpnext/issues/41486)) ([#41487](https://github.com/frappe/erpnext/issues/41487)) ([66684e7](66684e7e23))
2024-05-15 13:27:07 +00:00
mergify[bot]
66684e7e23 fix: not able to submit landed cost voucher (backport #41481) (backport #41486) (#41487)
fix: not able to submit landed cost voucher (backport #41481) (#41486)

fix: not able to submit landed cost voucher (#41481)

(cherry picked from commit 81a9521f04)

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

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2024-05-15 18:55:40 +05:30
mergify[bot]
a070ad786d fix: not able to submit landed cost voucher (backport #41481) (#41486)
fix: not able to submit landed cost voucher (#41481)

(cherry picked from commit 81a9521f04)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-05-15 18:05:39 +05:30
mergify[bot]
a1ce514cf3 fix: validate reorder group warehouse (backport #41478) (#41480)
fix: validate reorder group warehouse (#41478)

(cherry picked from commit 0363afcfd0)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-05-15 15:15:24 +05:30
Deepesh Garg
32c935bc30 feat: Config to enable immutable ledger
(cherry picked from commit d56f52b0ba)

# Conflicts:
#	erpnext/accounts/doctype/accounts_settings/accounts_settings.json
#	erpnext/accounts/general_ledger.py
2024-05-13 12:14:15 +00:00
Nihantra C. Patel
8fb3294674 fix: not allow template item in product bundle item 2024-05-08 10:40:24 +05:30
Nihantra C. Patel
b64f5a4e54 fix: not allow template item in product bundle item 2024-05-08 10:38:58 +05:30
Nihantra C. Patel
9864377b47 fix: not allow template item in product bundle item - v15/v14 2024-05-08 10:32:28 +05:30
143 changed files with 3212 additions and 1035 deletions

View File

@@ -59,12 +59,14 @@ repos:
rev: v0.2.0
hooks:
- id: ruff
name: "Run ruff linter and apply fixes"
args: ["--fix"]
name: "Run ruff import sorter"
args: ["--select=I", "--fix"]
- id: ruff
name: "Run ruff linter"
- id: ruff-format
name: "Format Python code"
name: "Run ruff formatter"
ci:
autoupdate_schedule: weekly

View File

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

View File

@@ -22,8 +22,7 @@ frappe.ui.form.on("Account", {
// hide fields if group
frm.toggle_display(["tax_rate"], cint(frm.doc.is_group) == 0);
// disable fields
frm.toggle_enable(["is_group", "company"], false);
frm.toggle_enable(["is_group", "company", "account_number"], frm.is_new());
if (cint(frm.doc.is_group) == 0) {
frm.toggle_display("freeze_account", frm.doc.__onload && frm.doc.__onload.can_freeze_account);

View File

@@ -55,8 +55,7 @@
"fieldtype": "Data",
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Account Number",
"read_only": 1
"label": "Account Number"
},
{
"default": "0",
@@ -72,7 +71,6 @@
"oldfieldname": "company",
"oldfieldtype": "Link",
"options": "Company",
"read_only": 1,
"remember_last_selected_value": 1,
"reqd": 1
},

View File

@@ -222,7 +222,7 @@ frappe.treeview_settings["Account"] = {
"General Ledger",
"Balance Sheet",
"Profit and Loss Statement",
"Cash Flow Statement",
"Cash Flow",
"Accounts Payable",
"Accounts Receivable",
]) {

View File

@@ -1525,7 +1525,8 @@
"41-Clients et comptes rattach\u00e9s (PASSIF)": {
"Clients cr\u00e9diteurs": {
"Clients - Avances et acomptes re\u00e7us sur commandes": {
"account_number": "4191"
"account_number": "4191",
"account_type": "Income Account"
},
"Clients - Dettes pour emballages et mat\u00e9riels consign\u00e9s": {
"account_number": "4196"
@@ -3141,4 +3142,4 @@
"account_number": "7"
}
}
}
}

View File

@@ -3,4 +3,23 @@
frappe.ui.form.on("Accounts Settings", {
refresh: function (frm) {},
enable_immutable_ledger: function (frm) {
if (!frm.doc.enable_immutable_ledger) {
return;
}
let msg = __("Enabling this will change the way how cancelled transactions are handled.");
msg += " ";
msg += __("Please enable only if the understand the effects of enabling this.");
msg += "<br>";
msg += "Do you still want to enable immutable ledger?";
frappe.confirm(
msg,
() => {},
() => {
frm.set_value("enable_immutable_ledger", 0);
}
);
},
});

View File

@@ -12,6 +12,7 @@
"unlink_advance_payment_on_cancelation_of_order",
"column_break_13",
"delete_linked_ledger_entries",
"enable_immutable_ledger",
"invoicing_features_section",
"check_supplier_invoice_uniqueness",
"automatically_fetch_payment_terms",
@@ -454,6 +455,13 @@
"fieldname": "remarks_section",
"fieldtype": "Section Break",
"label": "Remarks Column Length"
},
{
"default": "0",
"description": "On enabling this cancellation entries will be posted on the actual cancellation date and reports will consider cancelled entries as well",
"fieldname": "enable_immutable_ledger",
"fieldtype": "Check",
"label": "Enable Immutable Ledger"
}
],
"icon": "icon-cog",
@@ -461,7 +469,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2024-03-15 12:11:36.085158",
"modified": "2024-05-11 23:19:44.673975",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Settings",

View File

@@ -39,6 +39,7 @@ class AccountsSettings(Document):
determine_address_tax_category_from: DF.Literal["Billing Address", "Shipping Address"]
enable_common_party_accounting: DF.Check
enable_fuzzy_matching: DF.Check
enable_immutable_ledger: DF.Check
enable_party_matching: DF.Check
frozen_accounts_modifier: DF.Link | None
general_ledger_remarks_length: DF.Int

View File

@@ -59,6 +59,10 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
);
frm.add_custom_button(__("Auto Reconcile"), function () {
if (!frm.doc.bank_account) {
frappe.msgprint(__("Please select Bank Account"));
return;
}
frappe.call({
method: "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.auto_reconcile_vouchers",
args: {

View File

@@ -495,12 +495,12 @@ def check_matching(
bank_account,
company,
transaction,
document_types,
from_date,
to_date,
filter_by_reference_date,
from_reference_date,
to_reference_date,
document_types=None,
from_date=None,
to_date=None,
filter_by_reference_date=None,
from_reference_date=None,
to_reference_date=None,
):
exact_match = True if "exact_match" in document_types else False
@@ -540,14 +540,14 @@ def get_queries(
bank_account,
company,
transaction,
document_types,
from_date,
to_date,
filter_by_reference_date,
from_reference_date,
to_reference_date,
exact_match,
common_filters,
document_types=None,
from_date=None,
to_date=None,
filter_by_reference_date=None,
from_reference_date=None,
to_reference_date=None,
exact_match=None,
common_filters=None,
):
# get queries to get matching vouchers
account_from_to = "paid_to" if transaction.deposit > 0.0 else "paid_from"
@@ -580,15 +580,15 @@ def get_matching_queries(
bank_account,
company,
transaction,
document_types,
exact_match,
account_from_to,
from_date,
to_date,
filter_by_reference_date,
from_reference_date,
to_reference_date,
common_filters,
document_types=None,
exact_match=None,
account_from_to=None,
from_date=None,
to_date=None,
filter_by_reference_date=None,
from_reference_date=None,
to_reference_date=None,
common_filters=None,
):
queries = []
currency = get_account_currency(bank_account)

View File

@@ -68,6 +68,9 @@ class AutoMatchbyAccountIBAN:
party, or_filters=or_filters, pluck="name", limit_page_length=1
)
if "bank_ac_no" in or_filters:
or_filters["bank_account_no"] = or_filters.pop("bank_ac_no")
if party_result:
result = (
party,

View File

@@ -4,7 +4,7 @@
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils import add_days, format_date, getdate
from frappe.utils import add_days, flt, format_date, getdate
class MainCostCenterCantBeChild(frappe.ValidationError):
@@ -60,7 +60,7 @@ class CostCenterAllocation(Document):
self.validate_child_cost_centers()
def validate_total_allocation_percentage(self):
total_percentage = sum([d.percentage for d in self.get("allocation_percentages", [])])
total_percentage = sum([flt(d.percentage) for d in self.get("allocation_percentages", [])])
if total_percentage != 100:
frappe.throw(_("Total percentage against cost centers should be 100"), WrongPercentageAllocation)

View File

@@ -141,7 +141,19 @@ class Dunning(AccountsController):
def on_cancel(self):
super().on_cancel()
self.ignore_linked_doctypes = ["GL Entry"]
self.ignore_linked_doctypes = [
"GL Entry",
"Stock Ledger Entry",
"Repost Item Valuation",
"Repost Payment Ledger",
"Repost Payment Ledger Items",
"Repost Accounting Ledger",
"Repost Accounting Ledger Items",
"Unreconcile Payment",
"Unreconcile Payment Entries",
"Payment Ledger Entry",
"Serial and Batch Bundle",
]
def resolve_dunning(doc, state):

View File

@@ -82,7 +82,7 @@
"icon": "fa fa-calendar",
"idx": 1,
"links": [],
"modified": "2024-01-30 12:35:38.645968",
"modified": "2024-05-27 17:29:55.560840",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Fiscal Year",
@@ -127,6 +127,10 @@
{
"read": 1,
"role": "Stock Manager"
},
{
"read": 1,
"role": "Auditor"
}
],
"show_name_in_global_search": 1,

View File

@@ -1031,6 +1031,17 @@ class JournalEntry(AccountsController):
def build_gl_map(self):
gl_map = []
company_currency = erpnext.get_company_currency(self.company)
if self.multi_currency:
for row in self.get("accounts"):
if row.account_currency != company_currency:
self.currency = row.account_currency
self.conversion_rate = row.exchange_rate
break
else:
self.currency = company_currency
for d in self.get("accounts"):
if d.debit or d.credit or (self.voucher_type == "Exchange Gain Or Loss"):
r = [d.user_remark, self.remark]

View File

@@ -20,6 +20,7 @@
"party",
"party_name",
"book_advance_payments_in_separate_party_account",
"reconcile_on_advance_payment_date",
"column_break_11",
"bank_account",
"party_bank_account",
@@ -88,6 +89,7 @@
"custom_remarks",
"remarks",
"base_in_words",
"is_opening",
"column_break_16",
"letter_head",
"print_heading",
@@ -750,6 +752,7 @@
"fieldtype": "Check",
"hidden": 1,
"label": "Book Advance Payments in Separate Party Account",
"no_copy": 1,
"read_only": 1
},
{
@@ -765,6 +768,26 @@
"label": "In Words",
"print_hide": 1,
"read_only": 1
},
{
"default": "0",
"fetch_from": "company.reconcile_on_advance_payment_date",
"fieldname": "reconcile_on_advance_payment_date",
"fieldtype": "Check",
"hidden": 1,
"label": "Reconcile on Advance Payment Date",
"no_copy": 1,
"read_only": 1
},
{
"default": "No",
"depends_on": "eval: doc.book_advance_payments_in_separate_party_account == 1",
"fieldname": "is_opening",
"fieldtype": "Select",
"label": "Is Opening",
"options": "No\nYes",
"print_hide": 1,
"search_index": 1
}
],
"index_web_pages_for_search": 1,
@@ -778,7 +801,7 @@
"table_fieldname": "payment_entries"
}
],
"modified": "2024-04-11 11:25:07.366347",
"modified": "2024-05-31 17:07:06.197249",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Entry",

View File

@@ -116,11 +116,13 @@ class PaymentEntry(AccountsController):
self.book_advance_payments_in_separate_party_account = False
if self.party_type not in ("Customer", "Supplier"):
self.is_opening = "No"
return
if not frappe.db.get_value(
"Company", self.company, "book_advance_payments_in_separate_party_account"
):
self.is_opening = "No"
return
# Important to set this flag for the gl building logic to work properly
@@ -132,6 +134,7 @@ class PaymentEntry(AccountsController):
if (account_type == "Payable" and self.party_type == "Customer") or (
account_type == "Receivable" and self.party_type == "Supplier"
):
self.is_opening = "No"
return
if self.references:
@@ -141,6 +144,7 @@ class PaymentEntry(AccountsController):
# If there are referencers other than `allowed_types`, treat this as a normal payment entry
if reference_types - allowed_types:
self.book_advance_payments_in_separate_party_account = False
self.is_opening = "No"
return
liability_account = get_party_account(
@@ -1249,13 +1253,16 @@ class PaymentEntry(AccountsController):
"voucher_detail_no": invoice.name,
}
date_field = "posting_date"
if invoice.reference_doctype in ["Sales Order", "Purchase Order"]:
date_field = "transaction_date"
posting_date = frappe.db.get_value(invoice.reference_doctype, invoice.reference_name, date_field)
if getdate(posting_date) < getdate(self.posting_date):
if self.reconcile_on_advance_payment_date:
posting_date = self.posting_date
else:
date_field = "posting_date"
if invoice.reference_doctype in ["Sales Order", "Purchase Order"]:
date_field = "transaction_date"
posting_date = frappe.db.get_value(invoice.reference_doctype, invoice.reference_name, date_field)
if getdate(posting_date) < getdate(self.posting_date):
posting_date = self.posting_date
dr_or_cr, account = self.get_dr_and_account_for_advances(invoice)
args_dict["account"] = account

View File

@@ -1729,6 +1729,68 @@ class TestPaymentEntry(FrappeTestCase):
self.check_gl_entries()
self.check_pl_entries()
def test_opening_flag_for_advance_as_liability(self):
company = "_Test Company"
advance_account = create_account(
parent_account="Current Assets - _TC",
account_name="Advances Received",
company=company,
account_type="Receivable",
)
# Enable Advance in separate party account
frappe.db.set_value(
"Company",
company,
{
"book_advance_payments_in_separate_party_account": 1,
"default_advance_received_account": advance_account,
},
)
# Advance Payment
adv = create_payment_entry(
party_type="Customer",
party="_Test Customer",
payment_type="Receive",
paid_from="Debtors - _TC",
paid_to="_Test Cash - _TC",
)
adv.is_opening = "Yes"
adv.save() # use save() to trigger set_liability_account()
adv.submit()
gl_with_opening_set = frappe.db.get_all(
"GL Entry", filters={"voucher_no": adv.name, "is_opening": "Yes"}
)
# 'Is Opening' can be 'Yes' for Advances in separate party account
self.assertNotEqual(gl_with_opening_set, [])
# Disable Advance in separate party account
frappe.db.set_value(
"Company",
company,
{
"book_advance_payments_in_separate_party_account": 0,
"default_advance_received_account": None,
},
)
payment = create_payment_entry(
party_type="Customer",
party="_Test Customer",
payment_type="Receive",
paid_from="Debtors - _TC",
paid_to="_Test Cash - _TC",
)
payment.is_opening = "Yes"
payment.save()
payment.submit()
gl_with_opening_set = frappe.db.get_all(
"GL Entry", filters={"voucher_no": payment.name, "is_opening": "Yes"}
)
# 'Is Opening' should always be 'No' for normal advance payments
self.assertEqual(gl_with_opening_set, [])
def create_payment_entry(**args):
payment_entry = frappe.new_doc("Payment Entry")

View File

@@ -1525,6 +1525,55 @@ class TestPaymentReconciliation(FrappeTestCase):
]
self.assertEqual(pl_entries, expected_ple)
def test_advance_payment_reconciliation_date(self):
frappe.db.set_value(
"Company",
self.company,
{
"book_advance_payments_in_separate_party_account": 1,
"default_advance_paid_account": self.advance_payable_account,
"reconcile_on_advance_payment_date": 1,
},
)
self.supplier = "_Test Supplier"
amount = 1500
pe = self.create_payment_entry(amount=amount)
pe.posting_date = add_days(nowdate(), -1)
pe.party_type = "Supplier"
pe.party = self.supplier
pe.payment_type = "Pay"
pe.paid_from = self.cash
pe.paid_to = self.advance_payable_account
pe.save().submit()
pi = self.create_purchase_invoice(qty=10, rate=100)
self.assertNotEqual(pe.posting_date, pi.posting_date)
pr = self.create_payment_reconciliation(party_is_customer=False)
pr.default_advance_account = self.advance_payable_account
pr.from_payment_date = pe.posting_date
pr.get_unreconciled_entries()
self.assertEqual(len(pr.invoices), 1)
self.assertEqual(len(pr.payments), 1)
invoices = [invoice.as_dict() for invoice in pr.invoices]
payments = [payment.as_dict() for payment in pr.payments]
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
pr.reconcile()
# Assert Ledger Entries
gl_entries = frappe.db.get_all(
"GL Entry",
filters={"voucher_no": pe.name, "is_cancelled": 0, "posting_date": pe.posting_date},
)
self.assertEqual(len(gl_entries), 4)
pl_entries = frappe.db.get_all(
"Payment Ledger Entry",
filters={"voucher_no": pe.name, "delinked": 0, "posting_date": pe.posting_date},
)
self.assertEqual(len(pl_entries), 3)
def make_customer(customer_name, currency=None):
if not frappe.db.exists("Customer", customer_name):

View File

@@ -70,7 +70,7 @@ class POSClosingEntry(StatusUpdater):
for key, value in pos_occurences.items():
if len(value) > 1:
error_list.append(
_(f"{frappe.bold(key)} is added multiple times on rows: {frappe.bold(value)}")
_("{0} is added multiple times on rows: {1}").format(frappe.bold(key), frappe.bold(value))
)
if error_list:

View File

@@ -228,6 +228,7 @@ class POSInvoice(SalesInvoice):
self.apply_loyalty_points()
self.check_phone_payments()
self.set_status(update=True)
self.make_bundle_for_sales_purchase_return()
self.submit_serial_batch_bundle()
if self.coupon_code:

View File

@@ -318,29 +318,28 @@ class TestPOSInvoice(unittest.TestCase):
pos.insert()
pos.submit()
pos.reload()
pos_return1 = make_sales_return(pos.name)
# partial return 1
pos_return1.get("items")[0].qty = -1
pos_return1.submit()
pos_return1.reload()
bundle_id = frappe.get_doc(
"Serial and Batch Bundle", pos_return1.get("items")[0].serial_and_batch_bundle
)
bundle_id.remove(bundle_id.entries[1])
bundle_id.save()
bundle_id.load_from_db()
serial_no = bundle_id.entries[0].serial_no
self.assertEqual(serial_no, serial_nos[0])
pos_return1.insert()
pos_return1.submit()
# partial return 2
pos_return2 = make_sales_return(pos.name)
pos_return2.submit()
self.assertEqual(pos_return2.get("items")[0].qty, -1)
serial_no = get_serial_nos_from_bundle(pos_return2.get("items")[0].serial_and_batch_bundle)[0]
self.assertEqual(serial_no, serial_nos[1])

View File

@@ -54,7 +54,7 @@ class POSInvoiceMergeLog(Document):
for key, value in pos_occurences.items():
if len(value) > 1:
error_list.append(
_(f"{frappe.bold(key)} is added multiple times on rows: {frappe.bold(value)}")
_("{0} is added multiple times on rows: {1}").format(frappe.bold(key), frappe.bold(value))
)
if error_list:
@@ -481,7 +481,7 @@ def create_merge_logs(invoice_by_customer, closing_entry=None):
if closing_entry:
closing_entry.set_status(update=True, status="Failed")
if isinstance(error_message, list):
error_message = frappe.json.dumps(error_message)
error_message = json.dumps(error_message)
closing_entry.db_set("error_message", error_message)
raise

View File

@@ -74,15 +74,21 @@
"discount_amount",
"discount_percentage",
"for_price_list",
"section_break_13",
"threshold_percentage",
"priority",
"dynamic_condition_tab",
"condition",
"column_break_66",
"section_break_13",
"apply_multiple_pricing_rules",
"apply_discount_on_rate",
"column_break_66",
"threshold_percentage",
"validate_pricing_rule_section",
"validate_applied_rule",
"column_break_texp",
"rule_description",
"priority_section",
"has_priority",
"column_break_sayg",
"priority",
"help_section",
"pricing_rule_help",
"reference_section",
@@ -477,7 +483,7 @@
{
"collapsible": 1,
"fieldname": "section_break_13",
"fieldtype": "Section Break",
"fieldtype": "Tab Break",
"label": "Advanced Settings"
},
{
@@ -487,6 +493,7 @@
"label": "Threshold for Suggestion (In Percentage)"
},
{
"depends_on": "has_priority",
"description": "Higher the number, higher the priority",
"fieldname": "priority",
"fieldtype": "Select",
@@ -513,6 +520,7 @@
{
"default": "0",
"depends_on": "eval:doc.price_or_product_discount == 'Price'",
"description": "If enabled, then system will only validate the pricing rule and not apply automatically. User has to manually set the discount percentage / margin / free items to validate the pricing rule",
"fieldname": "validate_applied_rule",
"fieldtype": "Check",
"label": "Validate Applied Rule"
@@ -525,7 +533,8 @@
},
{
"fieldname": "help_section",
"fieldtype": "Section Break",
"fieldtype": "Tab Break",
"label": "Help Article",
"options": "Simple"
},
{
@@ -603,12 +612,42 @@
"fieldname": "apply_recursion_over",
"fieldtype": "Float",
"label": "Apply Recursion Over (As Per Transaction UOM)"
},
{
"fieldname": "priority_section",
"fieldtype": "Section Break",
"label": "Priority"
},
{
"fieldname": "dynamic_condition_tab",
"fieldtype": "Tab Break",
"label": "Dynamic Condition"
},
{
"fieldname": "validate_pricing_rule_section",
"fieldtype": "Section Break",
"label": "Validate Pricing Rule"
},
{
"fieldname": "column_break_texp",
"fieldtype": "Column Break"
},
{
"fieldname": "column_break_sayg",
"fieldtype": "Column Break"
},
{
"default": "0",
"description": "Enable this checkbox even if you want to set the zero priority",
"fieldname": "has_priority",
"fieldtype": "Check",
"label": "Has Priority"
}
],
"icon": "fa fa-gift",
"idx": 1,
"links": [],
"modified": "2023-02-14 04:53:34.887358",
"modified": "2024-05-17 13:16:34.496704",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Pricing Rule",

View File

@@ -27,9 +27,7 @@ class PricingRule(Document):
from frappe.types import DF
from erpnext.accounts.doctype.pricing_rule_brand.pricing_rule_brand import PricingRuleBrand
from erpnext.accounts.doctype.pricing_rule_item_code.pricing_rule_item_code import (
PricingRuleItemCode,
)
from erpnext.accounts.doctype.pricing_rule_item_code.pricing_rule_item_code import PricingRuleItemCode
from erpnext.accounts.doctype.pricing_rule_item_group.pricing_rule_item_group import (
PricingRuleItemGroup,
)
@@ -67,6 +65,7 @@ class PricingRule(Document):
free_item_rate: DF.Currency
free_item_uom: DF.Link | None
free_qty: DF.Float
has_priority: DF.Check
is_cumulative: DF.Check
is_recursive: DF.Check
item_groups: DF.Table[PricingRuleItemGroup]
@@ -156,6 +155,12 @@ class PricingRule(Document):
frappe.throw(_("Duplicate {0} found in the table").format(self.apply_on))
def validate_mandatory(self):
if self.has_priority and not self.priority:
throw(_("Priority is mandatory"), frappe.MandatoryError, _("Please Set Priority"))
if self.priority and not self.has_priority:
self.has_priority = 1
for apply_on, field in apply_on_dict.items():
if self.apply_on == apply_on and len(self.get(field) or []) < 1:
throw(_("{0} is not added in the table").format(apply_on), frappe.MandatoryError)

View File

@@ -929,6 +929,30 @@ class TestPricingRule(unittest.TestCase):
for doc in [si, si1]:
doc.delete()
def test_pricing_rule_for_transaction_with_condition(self):
make_item("PR Transaction Condition")
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule")
make_pricing_rule(
selling=1,
min_qty=0,
price_or_product_discount="Product",
apply_on="Transaction",
free_item="PR Transaction Condition",
free_qty=1,
free_item_rate=10,
condition="customer=='_Test Customer 1'",
)
si = create_sales_invoice(qty=5, customer="_Test Customer 1", do_not_submit=True)
self.assertEqual(len(si.items), 2)
self.assertEqual(si.items[1].rate, 10)
si1 = create_sales_invoice(qty=5, customer="_Test Customer 2", do_not_submit=True)
self.assertEqual(len(si1.items), 1)
for doc in [si, si1]:
doc.delete()
def test_remove_pricing_rule(self):
item = make_item("Water Flask")
make_item_price("Water Flask", "_Test Price List", 100)
@@ -1157,6 +1181,62 @@ class TestPricingRule(unittest.TestCase):
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 1")
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 2")
def test_priority_of_multiple_pricing_rules(self):
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 1")
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 2")
test_record = {
"doctype": "Pricing Rule",
"title": "_Test Pricing Rule 1",
"name": "_Test Pricing Rule 1",
"apply_on": "Item Code",
"currency": "USD",
"items": [
{
"item_code": "_Test Item",
}
],
"selling": 1,
"price_or_product_discount": "Price",
"rate_or_discount": "Discount Percentage",
"discount_percentage": 10,
"has_priority": 1,
"priority": 1,
"company": "_Test Company",
}
frappe.get_doc(test_record.copy()).insert()
test_record = {
"doctype": "Pricing Rule",
"title": "_Test Pricing Rule 2",
"name": "_Test Pricing Rule 2",
"apply_on": "Item Code",
"currency": "USD",
"items": [
{
"item_code": "_Test Item",
}
],
"selling": 1,
"price_or_product_discount": "Price",
"rate_or_discount": "Discount Percentage",
"discount_percentage": 20,
"has_priority": 1,
"priority": 3,
"company": "_Test Company",
}
frappe.get_doc(test_record.copy()).insert()
so = make_sales_order(item_code="_Test Item", qty=1, price_list_rate=1000, do_not_submit=True)
self.assertEqual(so.items[0].discount_percentage, 20)
self.assertEqual(so.items[0].rate, 800)
frappe.delete_doc_if_exists("Sales Order", so.name)
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 1")
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 2")
test_dependencies = ["Campaign"]
@@ -1185,6 +1265,7 @@ def make_pricing_rule(**args):
"priority": args.priority or 1,
"discount_amount": args.discount_amount or 0.0,
"apply_multiple_pricing_rules": args.apply_multiple_pricing_rules or 0,
"has_priority": args.has_priority or 0,
}
)

View File

@@ -33,6 +33,9 @@ def get_pricing_rules(args, doc=None):
for apply_on in ["Item Code", "Item Group", "Brand"]:
pricing_rules.extend(_get_pricing_rules(apply_on, args, values))
if pricing_rules and pricing_rules[0].has_priority:
continue
if pricing_rules and not apply_multiple_pricing_rules(pricing_rules):
break
@@ -561,6 +564,7 @@ def apply_pricing_rule_on_transaction(doc):
if pricing_rules:
pricing_rules = filter_pricing_rules_for_qty_amount(doc.total_qty, doc.total, pricing_rules)
pricing_rules = filter_pricing_rule_based_on_condition(pricing_rules, doc)
if not pricing_rules:
remove_free_item(doc)

View File

@@ -1,6 +1,8 @@
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import json
import frappe
from frappe import _, qb
from frappe.model.document import Document
@@ -504,7 +506,7 @@ def is_any_doc_running(for_filter: str | dict | None = None) -> str | None:
running_doc = None
if for_filter:
if isinstance(for_filter, str):
for_filter = frappe.json.loads(for_filter)
for_filter = json.loads(for_filter)
running_doc = frappe.db.get_value(
"Process Payment Reconciliation",

View File

@@ -485,10 +485,12 @@ function hide_fields(doc) {
var item_fields_stock = ["warehouse_section", "received_qty", "rejected_qty"];
cur_frm.fields_dict["items"].grid.set_column_disp(
item_fields_stock,
cint(doc.update_stock) == 1 || cint(doc.is_return) == 1 ? true : false
);
if (cur_frm.fields_dict["items"]) {
cur_frm.fields_dict["items"].grid.set_column_disp(
item_fields_stock,
cint(doc.update_stock) == 1 || cint(doc.is_return) == 1 ? true : false
);
}
cur_frm.refresh_fields();
}
@@ -675,7 +677,7 @@ frappe.ui.form.on("Purchase Invoice", {
if (frm.doc.supplier) {
frm.doc.apply_tds = frm.doc.__onload.supplier_tds ? 1 : 0;
}
if (!frm.doc.__onload.supplier_tds) {
if (!frm.doc.__onload.enable_apply_tds) {
frm.set_df_property("apply_tds", "read_only", 1);
}
}

View File

@@ -3,7 +3,7 @@
import frappe
from frappe import _, throw
from frappe import _, qb, throw
from frappe.model.mapper import get_mapped_doc
from frappe.query_builder.functions import Sum
from frappe.utils import cint, cstr, flt, formatdate, get_link_to_form, getdate, nowdate
@@ -347,6 +347,22 @@ class PurchaseInvoice(BuyingController):
self.tax_withholding_category = tds_category
self.set_onload("supplier_tds", tds_category)
# If Linked Purchase Order has TDS applied, enable 'apply_tds' checkbox
if purchase_orders := [x.purchase_order for x in self.items if x.purchase_order]:
po = qb.DocType("Purchase Order")
po_with_tds = (
qb.from_(po)
.select(po.name)
.where(
po.docstatus.eq(1)
& (po.name.isin(purchase_orders))
& (po.apply_tds.eq(1))
& (po.tax_withholding_category.notnull())
)
.run()
)
self.set_onload("enable_apply_tds", True if po_with_tds else False)
super().set_missing_values(for_validate)
def validate_credit_to_acc(self):
@@ -448,7 +464,7 @@ class PurchaseInvoice(BuyingController):
stock_not_billed_account = self.get_company_default("stock_received_but_not_billed")
stock_items = self.get_stock_items()
asset_received_but_not_billed = None
self.asset_received_but_not_billed = None
if self.update_stock:
self.validate_item_code()
@@ -531,26 +547,45 @@ class PurchaseInvoice(BuyingController):
frappe.msgprint(msg, title=_("Expense Head Changed"))
item.expense_account = stock_not_billed_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 item.is_fixed_asset:
account_type = (
"capital_work_in_progress_account"
if is_cwip_accounting_enabled(item.asset_category)
else "fixed_asset_account"
)
asset_category_account = get_asset_category_account(
account_type, item=item.item_code, company=self.company
)
if not asset_category_account:
form_link = get_link_to_form("Asset Category", item.asset_category)
throw(
_("Please set Fixed Asset Account in {} against {}.").format(form_link, self.company),
title=_("Missing Account"),
account = None
if item.pr_detail:
if not self.asset_received_but_not_billed:
self.asset_received_but_not_billed = self.get_company_default(
"asset_received_but_not_billed"
)
# check if 'Asset Received But Not Billed' account is credited in Purchase receipt or not
arbnb_booked_in_pr = frappe.db.get_value(
"GL Entry",
{
"voucher_type": "Purchase Receipt",
"voucher_no": item.purchase_receipt,
"account": self.asset_received_but_not_billed,
},
"name",
)
item.expense_account = asset_category_account
if arbnb_booked_in_pr:
account = self.asset_received_but_not_billed
if not account:
account_type = (
"capital_work_in_progress_account"
if is_cwip_accounting_enabled(item.asset_category)
else "fixed_asset_account"
)
account = get_asset_category_account(
account_type, item=item.item_code, company=self.company
)
if not account:
form_link = get_link_to_form("Asset Category", item.asset_category)
throw(
_("Please set Fixed Asset Account in {} against {}.").format(
form_link, self.company
),
title=_("Missing Account"),
)
item.expense_account = account
elif not item.expense_account and for_validate:
throw(_("Expense account is mandatory for item {0}").format(item.item_code or item.item_name))
@@ -707,6 +742,7 @@ class PurchaseInvoice(BuyingController):
# Updating stock ledger should always be called after updating prevdoc status,
# because updating ordered qty in bin depends upon updated ordered qty in PO
if self.update_stock == 1:
self.make_bundle_for_sales_purchase_return()
self.make_bundle_using_old_serial_batch_fields()
self.update_stock_ledger()
@@ -1035,10 +1071,10 @@ class PurchaseInvoice(BuyingController):
if provisional_accounting_for_non_stock_items:
if item.purchase_receipt:
provisional_account, pr_qty, pr_base_rate = frappe.get_cached_value(
provisional_account, pr_qty, pr_base_rate, pr_rate = frappe.get_cached_value(
"Purchase Receipt Item",
item.pr_detail,
["provisional_expense_account", "qty", "base_rate"],
["provisional_expense_account", "qty", "base_rate", "rate"],
)
provisional_account = provisional_account or self.get_company_default(
"default_provisional_account"
@@ -1072,7 +1108,10 @@ class PurchaseInvoice(BuyingController):
self.posting_date,
provisional_account,
reverse=1,
item_amount=(min(item.qty, pr_qty) * pr_base_rate),
item_amount=(
(min(item.qty, pr_qty) * pr_rate)
* purchase_receipt_doc.get("conversion_rate")
),
)
if not self.is_internal_transfer():

View File

@@ -1,7 +1,5 @@
{
"actions": [],
"allow_rename": 1,
"autoname": "format:ACC-REPOST-{#####}",
"creation": "2023-07-04 13:07:32.923675",
"default_view": "List",
"doctype": "DocType",
@@ -55,14 +53,15 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2023-09-26 14:21:27.362567",
"modified": "2024-06-03 17:30:37.012593",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Repost Accounting Ledger",
"naming_rule": "Expression",
"owner": "Administrator",
"permissions": [
{
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
@@ -71,7 +70,9 @@
"read": 1,
"report": 1,
"role": "System Manager",
"select": 1,
"share": 1,
"submit": 1,
"write": 1
}
],

View File

@@ -17,7 +17,7 @@
"in_create": 1,
"issingle": 1,
"links": [],
"modified": "2023-11-07 14:24:13.321522",
"modified": "2024-06-06 13:56:37.908879",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Repost Accounting Ledger Settings",
@@ -30,13 +30,17 @@
"print": 1,
"read": 1,
"role": "Administrator",
"select": 1,
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"read": 1,
"role": "System Manager",
"select": 1
"select": 1,
"write": 1
}
],
"sort_field": "modified",

View File

@@ -1,6 +1,5 @@
{
"actions": [],
"allow_rename": 1,
"creation": "2022-10-19 21:59:33.553852",
"doctype": "DocType",
"editable_grid": 1,
@@ -99,13 +98,15 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2023-09-26 14:21:35.719727",
"modified": "2024-06-03 17:31:04.472279",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Repost Payment Ledger",
"owner": "Administrator",
"permissions": [
{
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
@@ -114,7 +115,9 @@
"read": 1,
"report": 1,
"role": "System Manager",
"select": 1,
"share": 1,
"submit": 1,
"write": 1
},
{

View File

@@ -787,6 +787,7 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Time Sheets",
"no_copy": 1,
"options": "Sales Invoice Timesheet",
"print_hide": 1
},
@@ -2187,7 +2188,7 @@
"link_fieldname": "consolidated_invoice"
}
],
"modified": "2024-05-08 18:02:28.549041",
"modified": "2024-06-07 16:49:32.458402",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",

View File

@@ -450,6 +450,7 @@ class SalesInvoice(SellingController):
if not self.get(table_name):
continue
self.make_bundle_for_sales_purchase_return(table_name)
self.make_bundle_using_old_serial_batch_fields(table_name)
self.update_stock_ledger()

View File

@@ -2,6 +2,7 @@
# License: GNU General Public License v3. See license.txt
import copy
import json
import frappe
from frappe.model.dynamic_links import get_dynamic_link_map
@@ -3720,9 +3721,9 @@ class TestSalesInvoice(FrappeTestCase):
map_docs(
method="erpnext.stock.doctype.delivery_note.delivery_note.make_sales_invoice",
source_names=frappe.json.dumps([dn1.name, dn2.name]),
source_names=json.dumps([dn1.name, dn2.name]),
target_doc=si,
args=frappe.json.dumps({"customer": dn1.customer, "merge_taxes": 1, "filtered_children": []}),
args=json.dumps({"customer": dn1.customer, "merge_taxes": 1, "filtered_children": []}),
)
si.save().submit()

View File

@@ -870,7 +870,8 @@
"label": "Purchase Order",
"options": "Purchase Order",
"print_hide": 1,
"read_only": 1
"read_only": 1,
"search_index": 1
},
{
"fieldname": "column_break_92",
@@ -926,7 +927,7 @@
"idx": 1,
"istable": 1,
"links": [],
"modified": "2024-02-25 15:56:44.828634",
"modified": "2024-05-23 16:36:18.970862",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Item",
@@ -936,4 +937,4 @@
"sort_field": "modified",
"sort_order": "DESC",
"states": []
}
}

View File

@@ -1,6 +1,8 @@
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import json
import frappe
from frappe import _, qb
from frappe.model.document import Document
@@ -161,7 +163,7 @@ def get_linked_payments_for_doc(
@frappe.whitelist()
def create_unreconcile_doc_for_selection(selections=None):
if selections:
selections = frappe.json.loads(selections)
selections = json.loads(selections)
# assuming each row is a unique voucher
for row in selections:
unrecon = frappe.new_doc("Unreconcile Payment")

View File

@@ -577,6 +577,8 @@ def make_reverse_gl_entries(
and make reverse gl entries by swapping debit and credit
"""
immutable_ledger_enabled = is_immutable_ledger_enabled()
if not gl_entries:
gl_entry = frappe.qb.DocType("GL Entry")
gl_entries = (
@@ -608,7 +610,6 @@ def make_reverse_gl_entries(
for x in gl_entries:
query = (
frappe.qb.update(gle)
.set(gle.is_cancelled, True)
.set(gle.modified, now())
.set(gle.modified_by, frappe.session.user)
.where(
@@ -623,9 +624,14 @@ def make_reverse_gl_entries(
& (gle.voucher_detail_no == x.voucher_detail_no)
)
)
if not immutable_ledger_enabled:
query = query.set(gle.is_cancelled, True)
query.run()
else:
set_as_cancel(gl_entries[0]["voucher_type"], gl_entries[0]["voucher_no"])
if not immutable_ledger_enabled:
set_as_cancel(gl_entries[0]["voucher_type"], gl_entries[0]["voucher_no"])
for entry in gl_entries:
new_gle = copy.deepcopy(entry)
@@ -644,6 +650,10 @@ def make_reverse_gl_entries(
new_gle["remarks"] = "On cancellation of " + new_gle["voucher_no"]
new_gle["is_cancelled"] = 1
if immutable_ledger_enabled:
new_gle["is_cancelled"] = 0
new_gle["posting_date"] = frappe.form_dict.get("posting_date") or getdate()
if new_gle["debit"] or new_gle["credit"]:
make_entry(new_gle, adv_adj, "Yes")
@@ -736,3 +746,7 @@ def validate_allowed_dimensions(gl_entry, dimension_filter_map):
),
InvalidAccountDimensionError,
)
def is_immutable_ledger_enabled():
return frappe.db.get_single_value("Accounts Settings", "enable_immutable_ledger")

View File

@@ -49,7 +49,6 @@ def get_conditions(filters):
if filters.account_type:
conditions["account_type"] = filters.account_type
return conditions
if filters.company:
conditions["company"] = filters.company

View File

@@ -4,6 +4,7 @@
import frappe
from frappe import _
from frappe.query_builder import DocType
from frappe.utils import cstr, flt
@@ -75,11 +76,24 @@ def get_data(filters):
asset_data = assets_details.get(d.against_voucher)
if asset_data:
if not asset_data.get("accumulated_depreciation_amount"):
asset_data.accumulated_depreciation_amount = d.debit + asset_data.get(
"opening_accumulated_depreciation"
)
AssetDepreciationSchedule = DocType("Asset Depreciation Schedule")
DepreciationSchedule = DocType("Depreciation Schedule")
query = (
frappe.qb.from_(DepreciationSchedule)
.join(AssetDepreciationSchedule)
.on(DepreciationSchedule.parent == AssetDepreciationSchedule.name)
.select(DepreciationSchedule.accumulated_depreciation_amount)
.where(
(AssetDepreciationSchedule.asset == d.against_voucher)
& (DepreciationSchedule.parenttype == "Asset Depreciation Schedule")
& (DepreciationSchedule.schedule_date == d.posting_date)
)
).run(as_dict=True)
asset_data.accumulated_depreciation_amount = query[0]["accumulated_depreciation_amount"]
else:
asset_data.accumulated_depreciation_amount += d.debit
asset_data.opening_accumulated_depreciation = asset_data.accumulated_depreciation_amount - d.debit
row = frappe._dict(asset_data)
row.update(

View File

@@ -69,48 +69,50 @@ def get_asset_categories_for_grouped_by_category(filters):
condition = ""
if filters.get("asset_category"):
condition += " and asset_category = %(asset_category)s"
# nosemgrep
return frappe.db.sql(
f"""
SELECT asset_category,
ifnull(sum(case when purchase_date < %(from_date)s then
case when ifnull(disposal_date, 0) = 0 or disposal_date >= %(from_date)s then
gross_purchase_amount
SELECT a.asset_category,
ifnull(sum(case when a.purchase_date < %(from_date)s then
case when ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s then
a.gross_purchase_amount
else
0
end
else
0
end), 0) as cost_as_on_from_date,
ifnull(sum(case when purchase_date >= %(from_date)s then
gross_purchase_amount
ifnull(sum(case when a.purchase_date >= %(from_date)s then
a.gross_purchase_amount
else
0
end), 0) as cost_of_new_purchase,
ifnull(sum(case when ifnull(disposal_date, 0) != 0
and disposal_date >= %(from_date)s
and disposal_date <= %(to_date)s then
case when status = "Sold" then
gross_purchase_amount
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0
and a.disposal_date >= %(from_date)s
and a.disposal_date <= %(to_date)s then
case when a.status = "Sold" then
a.gross_purchase_amount
else
0
end
else
0
end), 0) as cost_of_sold_asset,
ifnull(sum(case when ifnull(disposal_date, 0) != 0
and disposal_date >= %(from_date)s
and disposal_date <= %(to_date)s then
case when status = "Scrapped" then
gross_purchase_amount
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0
and a.disposal_date >= %(from_date)s
and a.disposal_date <= %(to_date)s then
case when a.status = "Scrapped" then
a.gross_purchase_amount
else
0
end
else
0
end), 0) as cost_of_scrapped_asset
from `tabAsset`
from `tabAsset` a
where docstatus=1 and company=%(company)s and purchase_date <= %(to_date)s {condition}
group by asset_category
and not exists(select name from `tabAsset Capitalization Asset Item` where asset = a.name)
group by a.asset_category
""",
{
"to_date": filters.to_date,

View File

@@ -219,7 +219,8 @@ def get_conditions(filters):
if filters.get("account"):
filters.account = get_accounts_with_children(filters.account)
conditions.append("account in %(account)s")
if filters.account:
conditions.append("account in %(account)s")
if filters.get("cost_center"):
filters.cost_center = get_cost_centers_with_children(filters.cost_center)
@@ -329,7 +330,7 @@ def get_accounts_with_children(accounts):
else:
frappe.throw(_("Account: {0} does not exist").format(d))
return list(set(all_accounts))
return list(set(all_accounts)) if all_accounts else None
def get_data_with_opening_closing(filters, account_details, accounting_dimensions, gl_entries):
@@ -420,6 +421,8 @@ def get_accountwise_gle(filters, accounting_dimensions, gl_entries, gle_map):
if filters.get("show_net_values_in_party_account"):
account_type_map = get_account_type_map(filters.get("company"))
immutable_ledger = frappe.db.get_single_value("Accounts Settings", "enable_immutable_ledger")
def update_value_in_dict(data, key, gle):
data[key].debit += gle.debit
data[key].credit += gle.credit
@@ -481,12 +484,17 @@ def get_accountwise_gle(filters, accounting_dimensions, gl_entries, gle_map):
elif group_by_voucher_consolidated:
keylist = [
gle.get("posting_date"),
gle.get("voucher_type"),
gle.get("voucher_no"),
gle.get("account"),
gle.get("party_type"),
gle.get("party"),
]
if immutable_ledger:
keylist.append(gle.get("creation"))
if filters.get("include_dimensions"):
for dim in accounting_dimensions:
keylist.append(gle.get(dim))

View File

@@ -5,14 +5,15 @@
import frappe
from frappe import _
from frappe.utils import flt
from pypika import Order
import erpnext
from erpnext.accounts.report.item_wise_sales_register.item_wise_sales_register import (
add_sub_total_row,
add_total_row,
apply_group_by_conditions,
get_grand_total,
get_group_by_and_display_fields,
get_group_by_conditions,
get_tax_accounts,
)
from erpnext.accounts.report.utils import get_query_columns, get_values_for_columns
@@ -29,7 +30,7 @@ def _execute(filters=None, additional_table_columns=None):
company_currency = erpnext.get_company_currency(filters.company)
item_list = get_items(filters, get_query_columns(additional_table_columns))
item_list = get_items(filters, additional_table_columns)
aii_account_map = get_aii_accounts()
if item_list:
itemised_tax, tax_columns = get_tax_accounts(
@@ -287,59 +288,87 @@ def get_columns(additional_table_columns, filters):
return columns
def get_conditions(filters):
conditions = ""
def apply_conditions(query, pi, pii, filters):
for opts in ("company", "supplier", "item_code", "mode_of_payment"):
if filters.get(opts):
query = query.where(pi[opts] == filters[opts])
for opts in (
("company", " and `tabPurchase Invoice`.company=%(company)s"),
("supplier", " and `tabPurchase Invoice`.supplier = %(supplier)s"),
("item_code", " and `tabPurchase Invoice Item`.item_code = %(item_code)s"),
("from_date", " and `tabPurchase Invoice`.posting_date>=%(from_date)s"),
("to_date", " and `tabPurchase Invoice`.posting_date<=%(to_date)s"),
("mode_of_payment", " and ifnull(mode_of_payment, '') = %(mode_of_payment)s"),
("item_group", " and ifnull(`tabPurchase Invoice Item`.item_group, '') = %(item_group)s"),
):
if filters.get(opts[0]):
conditions += opts[1]
if filters.get("from_date"):
query = query.where(pi.posting_date >= filters.get("from_date"))
if filters.get("to_date"):
query = query.where(pi.posting_date <= filters.get("to_date"))
if filters.get("item_group"):
query = query.where(pii.item_group == filters.get("item_group"))
if not filters.get("group_by"):
conditions += (
"ORDER BY `tabPurchase Invoice`.posting_date desc, `tabPurchase Invoice Item`.item_code desc"
)
query = query.orderby(pi.posting_date, order=Order.desc)
query = query.orderby(pii.item_group, order=Order.desc)
else:
conditions += get_group_by_conditions(filters, "Purchase Invoice")
query = apply_group_by_conditions(filters, "Purchase Invoice")
return conditions
return query
def get_items(filters, additional_query_columns):
conditions = get_conditions(filters)
if additional_query_columns:
additional_query_columns = "," + ",".join(additional_query_columns)
return frappe.db.sql(
f"""
select
`tabPurchase Invoice Item`.`name`, `tabPurchase Invoice Item`.`parent`,
`tabPurchase Invoice`.posting_date, `tabPurchase Invoice`.credit_to, `tabPurchase Invoice`.company,
`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`.`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` {additional_query_columns}
from `tabPurchase Invoice`, `tabPurchase Invoice Item`, `tabItem`
where `tabPurchase Invoice`.name = `tabPurchase Invoice Item`.`parent` and
`tabItem`.name = `tabPurchase Invoice Item`.`item_code` and
`tabPurchase Invoice`.docstatus = 1 {conditions}
""",
filters,
as_dict=1,
def get_items(filters, additional_table_columns):
pi = frappe.qb.DocType("Purchase Invoice")
pii = frappe.qb.DocType("Purchase Invoice Item")
Item = frappe.qb.DocType("Item")
query = (
frappe.qb.from_(pi)
.join(pii)
.on(pi.name == pii.parent)
.left_join(Item)
.on(pii.item_code == Item.name)
.select(
pii.name.as_("pii_name"),
pii.parent,
pi.posting_date,
pi.credit_to,
pi.company,
pi.supplier,
pi.remarks,
pi.base_net_total,
pi.unrealized_profit_loss_account,
pii.item_code,
pii.description,
pii.item_group,
pii.item_name.as_("pi_item_name"),
pii.item_group.as_("pi_item_group"),
Item.item_name.as_("i_item_name"),
Item.item_group.as_("i_item_group"),
pii.project,
pii.purchase_order,
pii.purchase_receipt,
pii.po_detail,
pii.expense_account,
pii.stock_qty,
pii.stock_uom,
pii.base_net_amount,
pi.supplier_name,
pi.mode_of_payment,
)
.where(pi.docstatus == 1)
)
if filters.get("supplier"):
query = query.where(pi.supplier == filters["supplier"])
if filters.get("company"):
query = query.where(pi.company == filters["company"])
if additional_table_columns:
for column in additional_table_columns:
if column.get("_doctype"):
table = frappe.qb.DocType(column.get("_doctype"))
query = query.select(table[column.get("fieldname")])
else:
query = query.select(pi[column.get("fieldname")])
query = apply_conditions(query, pi, pii, filters)
return query.run(as_dict=True)
def get_aii_accounts():
return dict(frappe.db.sql("select name, stock_received_but_not_billed from tabCompany"))

View File

@@ -41,6 +41,12 @@ frappe.query_reports["Item-wise Sales Register"] = {
label: __("Warehouse"),
fieldtype: "Link",
options: "Warehouse",
get_query: function () {
const company = frappe.query_report.get_filter_value("company");
return {
filters: { company: company },
};
},
},
{
fieldname: "brand",

View File

@@ -7,6 +7,7 @@ from frappe import _
from frappe.model.meta import get_field_precision
from frappe.utils import cstr, flt
from frappe.utils.xlsxutils import handle_html
from pypika import Order
from erpnext.accounts.report.sales_register.sales_register import get_mode_of_payments
from erpnext.accounts.report.utils import get_query_columns, get_values_for_columns
@@ -26,7 +27,7 @@ def _execute(filters=None, additional_table_columns=None, additional_conditions=
company_currency = frappe.get_cached_value("Company", filters.get("company"), "default_currency")
item_list = get_items(filters, get_query_columns(additional_table_columns), additional_conditions)
item_list = get_items(filters, additional_table_columns, additional_conditions)
if item_list:
itemised_tax, tax_columns = get_tax_accounts(item_list, columns, company_currency)
@@ -83,9 +84,7 @@ def _execute(filters=None, additional_table_columns=None, additional_conditions=
"company": d.company,
"sales_order": d.sales_order,
"delivery_note": d.delivery_note,
"income_account": d.unrealized_profit_loss_account
if d.is_internal_customer == 1
else d.income_account,
"income_account": get_income_account(d),
"cost_center": d.cost_center,
"stock_qty": d.stock_qty,
"stock_uom": d.stock_uom,
@@ -150,6 +149,15 @@ def _execute(filters=None, additional_table_columns=None, additional_conditions=
return columns, data, None, None, None, skip_total_row
def get_income_account(row):
if row.enable_deferred_revenue:
return row.deferred_revenue_account
elif row.is_internal_customer == 1:
return row.unrealized_profit_loss_account
else:
return row.income_account
def get_columns(additional_table_columns, filters):
columns = []
@@ -333,93 +341,140 @@ def get_columns(additional_table_columns, filters):
return columns
def get_conditions(filters, additional_conditions=None):
conditions = ""
def apply_conditions(query, si, sii, filters, additional_conditions=None):
for opts in ("company", "customer", "item_code"):
if filters.get(opts):
query = query.where(si[opts] == filters[opts])
for opts in (
("company", " and `tabSales Invoice`.company=%(company)s"),
("customer", " and `tabSales Invoice`.customer = %(customer)s"),
("item_code", " and `tabSales Invoice Item`.item_code = %(item_code)s"),
("from_date", " and `tabSales Invoice`.posting_date>=%(from_date)s"),
("to_date", " and `tabSales Invoice`.posting_date<=%(to_date)s"),
):
if filters.get(opts[0]):
conditions += opts[1]
if filters.get("from_date"):
query = query.where(si.posting_date >= filters.get("from_date"))
if additional_conditions:
conditions += additional_conditions
if filters.get("to_date"):
query = query.where(si.posting_date <= filters.get("to_date"))
if filters.get("mode_of_payment"):
conditions += """ and exists(select name from `tabSales Invoice Payment`
where parent=`tabSales Invoice`.name
and ifnull(`tabSales Invoice Payment`.mode_of_payment, '') = %(mode_of_payment)s)"""
sales_invoice = frappe.db.get_all(
"Sales Invoice Payment", {"mode_of_payment": filters.get("mode_of_payment")}, pluck="parent"
)
query = query.where(si.name.isin(sales_invoice))
if filters.get("warehouse"):
if frappe.db.get_value("Warehouse", filters.get("warehouse"), "is_group"):
lft, rgt = frappe.db.get_all(
"Warehouse", filters={"name": filters.get("warehouse")}, fields=["lft", "rgt"], as_list=True
)[0]
conditions += f"and ifnull(`tabSales Invoice Item`.warehouse, '') in (select name from `tabWarehouse` where lft > {lft} and rgt < {rgt}) "
warehouses = frappe.db.get_all("Warehouse", {"lft": (">", lft), "rgt": ("<", rgt)}, pluck="name")
query = query.where(sii.warehouse.isin(warehouses))
else:
conditions += """and ifnull(`tabSales Invoice Item`.warehouse, '') = %(warehouse)s"""
query = query.where(sii.warehouse == filters.get("warehouse"))
if filters.get("brand"):
conditions += """and ifnull(`tabSales Invoice Item`.brand, '') = %(brand)s"""
query = query.where(sii.brand == filters.get("brand"))
if filters.get("item_group"):
conditions += """and ifnull(`tabSales Invoice Item`.item_group, '') = %(item_group)s"""
query = query.where(sii.item_group == filters.get("item_group"))
if filters.get("income_account"):
query = query.where(
(sii.income_account == filters.get("income_account"))
| (sii.deferred_revenue_account == filters.get("income_account"))
| (si.unrealized_profit_loss_account == filters.get("income_account"))
)
if not filters.get("group_by"):
conditions += "ORDER BY `tabSales Invoice`.posting_date desc, `tabSales Invoice Item`.item_group desc"
query = query.orderby(si.posting_date, order=Order.desc)
query = query.orderby(sii.item_group, order=Order.desc)
else:
conditions += get_group_by_conditions(filters, "Sales Invoice")
query = apply_group_by_conditions(query, si, sii, filters)
return conditions
for key, value in (additional_conditions or {}).items():
query = query.where(si[key] == value)
return query
def get_group_by_conditions(filters, doctype):
def apply_group_by_conditions(query, si, ii, filters):
if filters.get("group_by") == "Invoice":
return f"ORDER BY `tab{doctype} Item`.parent desc"
query = query.orderby(ii.parent, order=Order.desc)
elif filters.get("group_by") == "Item":
return f"ORDER BY `tab{doctype} Item`.`item_code`"
query = query.orderby(ii.item_code)
elif filters.get("group_by") == "Item Group":
return "ORDER BY `tab{} Item`.{}".format(doctype, frappe.scrub(filters.get("group_by")))
query = query.orderby(ii.item_group)
elif filters.get("group_by") in ("Customer", "Customer Group", "Territory", "Supplier"):
return "ORDER BY `tab{}`.{}".format(doctype, frappe.scrub(filters.get("group_by")))
query = query.orderby(si[frappe.scrub(filters.get("group_by"))])
return query
def get_items(filters, additional_query_columns, additional_conditions=None):
conditions = get_conditions(filters, additional_conditions)
si = frappe.qb.DocType("Sales Invoice")
sii = frappe.qb.DocType("Sales Invoice Item")
item = frappe.qb.DocType("Item")
query = (
frappe.qb.from_(si)
.join(sii)
.on(si.name == sii.parent)
.left_join(item)
.on(sii.item_code == item.name)
.select(
sii.name,
sii.parent,
si.posting_date,
si.debit_to,
si.unrealized_profit_loss_account,
si.is_internal_customer,
si.customer,
si.remarks,
si.territory,
si.company,
si.base_net_total,
sii.project,
sii.item_code,
sii.description,
sii.item_name,
sii.item_group,
sii.item_name.as_("si_item_name"),
sii.item_group.as_("si_item_group"),
item.item_name.as_("i_item_name"),
item.item_group.as_("i_item_group"),
sii.sales_order,
sii.delivery_note,
sii.income_account,
sii.cost_center,
sii.enable_deferred_revenue,
sii.deferred_revenue_account,
sii.stock_qty,
sii.stock_uom,
sii.base_net_rate,
sii.base_net_amount,
si.customer_name,
si.customer_group,
sii.so_detail,
si.update_stock,
sii.uom,
sii.qty,
)
.where(si.docstatus == 1)
)
if additional_query_columns:
additional_query_columns = "," + ",".join(additional_query_columns)
return frappe.db.sql(
"""
select
`tabSales Invoice Item`.name, `tabSales Invoice Item`.parent,
`tabSales Invoice`.posting_date, `tabSales Invoice`.debit_to,
`tabSales Invoice`.unrealized_profit_loss_account,
`tabSales Invoice`.is_internal_customer,
`tabSales Invoice`.customer, `tabSales Invoice`.remarks,
`tabSales Invoice`.territory, `tabSales Invoice`.company, `tabSales Invoice`.base_net_total,
`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 {}
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 {}
""".format(additional_query_columns, conditions),
filters,
as_dict=1,
) # nosec
for column in additional_query_columns:
if column.get("_doctype"):
table = frappe.qb.DocType(column.get("_doctype"))
query = query.select(table[column.get("fieldname")])
else:
query = query.select(si[column.get("fieldname")])
if filters.get("customer"):
query = query.where(si.customer == filters["customer"])
if filters.get("customer_group"):
query = query.where(si.customer_group == filters["customer_group"])
query = apply_conditions(query, si, sii, filters, additional_conditions)
return query.run(as_dict=True)
def get_delivery_notes_against_sales_order(item_list):
@@ -427,16 +482,14 @@ def get_delivery_notes_against_sales_order(item_list):
so_item_rows = list(set([d.so_detail for d in item_list]))
if so_item_rows:
delivery_notes = frappe.db.sql(
"""
select parent, so_detail
from `tabDelivery Note Item`
where docstatus=1 and so_detail in (%s)
group by so_detail, parent
"""
% (", ".join(["%s"] * len(so_item_rows))),
tuple(so_item_rows),
as_dict=1,
dn_item = frappe.qb.DocType("Delivery Note Item")
delivery_notes = (
frappe.qb.from_(dn_item)
.select(dn_item.parent, dn_item.so_detail)
.where(dn_item.docstatus == 1)
.where(dn_item.so_detail.isin(so_item_rows))
.groupby(dn_item.so_detail, dn_item.parent)
.run(as_dict=True)
)
for dn in delivery_notes:
@@ -446,15 +499,16 @@ def get_delivery_notes_against_sales_order(item_list):
def get_grand_total(filters, doctype):
return frappe.db.sql(
f""" SELECT
SUM(`tab{doctype}`.base_grand_total)
FROM `tab{doctype}`
WHERE `tab{doctype}`.docstatus = 1
and posting_date between %s and %s
""",
(filters.get("from_date"), filters.get("to_date")),
)[0][0] # nosec
return flt(
frappe.db.get_value(
doctype,
{
"docstatus": 1,
"posting_date": ("between", [filters.get("from_date"), filters.get("to_date")]),
},
"sum(base_grand_total)",
)
)
def get_tax_accounts(

View File

@@ -3,7 +3,9 @@
import frappe
from frappe import _
from frappe import _, qb
from frappe.query_builder import Criterion
from frappe.query_builder.functions import Abs
from frappe.utils import flt, getdate
from erpnext.accounts.report.accounts_receivable.accounts_receivable import ReceivablePayableReport
@@ -21,16 +23,12 @@ def execute(filters=None):
data = []
for d in entries:
invoice = invoice_details.get(d.against_voucher) or frappe._dict()
if d.reference_type == "Purchase Invoice":
payment_amount = flt(d.debit) or -1 * flt(d.credit)
else:
payment_amount = flt(d.credit) or -1 * flt(d.debit)
invoice = invoice_details.get(d.against_voucher_no) or frappe._dict()
payment_amount = d.amount
d.update({"range1": 0, "range2": 0, "range3": 0, "range4": 0, "outstanding": payment_amount})
if d.against_voucher:
if d.against_voucher_no:
ReceivablePayableReport(filters).get_ageing_data(invoice.posting_date, d)
row = [
@@ -39,11 +37,10 @@ def execute(filters=None):
d.party_type,
d.party,
d.posting_date,
d.against_voucher,
d.against_voucher_no,
invoice.posting_date,
invoice.due_date,
d.debit,
d.credit,
d.amount,
d.remarks,
d.age,
d.range1,
@@ -111,8 +108,7 @@ def get_columns(filters):
"width": 100,
},
{"fieldname": "due_date", "label": _("Payment Due Date"), "fieldtype": "Date", "width": 100},
{"fieldname": "debit", "label": _("Debit"), "fieldtype": "Currency", "width": 140},
{"fieldname": "credit", "label": _("Credit"), "fieldtype": "Currency", "width": 140},
{"fieldname": "amount", "label": _("Amount"), "fieldtype": "Currency", "width": 140},
{"fieldname": "remarks", "label": _("Remarks"), "fieldtype": "Data", "width": 200},
{"fieldname": "age", "label": _("Age"), "fieldtype": "Int", "width": 50},
{"fieldname": "range1", "label": _("0-30"), "fieldtype": "Currency", "width": 140},
@@ -129,51 +125,68 @@ def get_columns(filters):
def get_conditions(filters):
ple = qb.DocType("Payment Ledger Entry")
conditions = []
if not filters.party_type:
if filters.payment_type == _("Outgoing"):
filters.party_type = "Supplier"
else:
filters.party_type = "Customer"
if filters.party_type:
conditions.append("party_type=%(party_type)s")
conditions.append(ple.delinked.eq(0))
if filters.payment_type == _("Outgoing"):
conditions.append(ple.party_type.eq("Supplier"))
conditions.append(ple.against_voucher_type.eq("Purchase Invoice"))
else:
conditions.append(ple.party_type.eq("Customer"))
conditions.append(ple.against_voucher_type.eq("Sales Invoice"))
if filters.party:
conditions.append("party=%(party)s")
if filters.party_type:
conditions.append("against_voucher_type=%(reference_type)s")
filters["reference_type"] = (
"Sales Invoice" if filters.party_type == "Customer" else "Purchase Invoice"
)
conditions.append(ple.party.eq(filters.party))
if filters.get("from_date"):
conditions.append("posting_date >= %(from_date)s")
conditions.append(ple.posting_date.gte(filters.get("from_date")))
if filters.get("to_date"):
conditions.append("posting_date <= %(to_date)s")
conditions.append(ple.posting_date.lte(filters.get("to_date")))
return "and " + " and ".join(conditions) if conditions else ""
if filters.get("company"):
conditions.append(ple.company.eq(filters.get("company")))
return conditions
def get_entries(filters):
return frappe.db.sql(
"""select
voucher_type, voucher_no, party_type, party, posting_date, debit, credit, remarks, against_voucher
from `tabGL Entry`
where company=%(company)s and voucher_type in ('Journal Entry', 'Payment Entry') and is_cancelled = 0 {}
""".format(get_conditions(filters)),
filters,
as_dict=1,
ple = qb.DocType("Payment Ledger Entry")
conditions = get_conditions(filters)
query = (
qb.from_(ple)
.select(
ple.voucher_type,
ple.voucher_no,
ple.party_type,
ple.party,
ple.posting_date,
Abs(ple.amount).as_("amount"),
ple.remarks,
ple.against_voucher_no,
)
.where(Criterion.all(conditions))
)
res = query.run(as_dict=True)
return res
def get_invoice_posting_date_map(filters):
invoice_details = {}
dt = "Sales Invoice" if filters.get("payment_type") == _("Incoming") else "Purchase Invoice"
for t in frappe.db.sql(f"select name, posting_date, due_date from `tab{dt}`", as_dict=1):
dt = (
qb.DocType("Sales Invoice")
if filters.get("payment_type") == _("Incoming")
else qb.DocType("Purchase Invoice")
)
res = (
qb.from_(dt)
.select(dt.name, dt.posting_date, dt.due_date)
.where((dt.docstatus.eq(1)) & (dt.company.eq(filters.get("company"))))
.run(as_dict=1)
)
for t in res:
invoice_details[t.name] = t
return invoice_details

View File

@@ -1,8 +1,4 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
frappe.require("assets/erpnext/js/purchase_trends_filters.js", function () {
frappe.query_reports["Purchase Invoice Trends"] = {
filters: erpnext.get_purchase_trends_filters(),
};
});
frappe.query_reports["Purchase Invoice Trends"] = $.extend({}, erpnext.purchase_trends_filters);

View File

@@ -1,8 +1,4 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
frappe.require("assets/erpnext/js/sales_trends_filters.js", function () {
frappe.query_reports["Sales Invoice Trends"] = {
filters: erpnext.get_sales_trends_filters(),
};
});
frappe.query_reports["Sales Invoice Trends"] = $.extend({}, erpnext.sales_trends_filters);

View File

@@ -56,7 +56,7 @@ def get_fiscal_year(
date=None, fiscal_year=None, label="Date", verbose=1, company=None, as_dict=False, boolean=False
):
if isinstance(boolean, str):
boolean = frappe.json.loads(boolean)
boolean = loads(boolean)
fiscal_years = get_fiscal_years(
date, fiscal_year, label, verbose, company, as_dict=as_dict, boolean=boolean

View File

@@ -1140,6 +1140,8 @@ def create_new_asset_after_split(asset, split_qty):
for row in new_asset.get("finance_books"):
current_asset_depr_schedule_doc = get_asset_depr_schedule_doc(asset.name, "Active", row.finance_book)
if not current_asset_depr_schedule_doc:
continue
new_asset_depr_schedule_doc = frappe.copy_doc(current_asset_depr_schedule_doc)
new_asset_depr_schedule_doc.set_draft_asset_depr_schedule_details(new_asset, row)

View File

@@ -363,6 +363,16 @@ class AssetDepreciationSchedule(Document):
row.depreciation_start_date,
has_wdv_or_dd_non_yearly_pro_rata,
)
if flt(depreciation_amount, asset_doc.precision("gross_purchase_amount")) <= 0:
frappe.throw(
_(
"Gross Purchase Amount Too Low: {0} cannot be depreciated over {1} cycles with a frequency of {2} depreciations."
).format(
frappe.bold(asset_doc.gross_purchase_amount),
frappe.bold(row.total_number_of_depreciations),
frappe.bold(row.frequency_of_depreciation),
)
)
elif n == 0 and has_wdv_or_dd_non_yearly_pro_rata and self.opening_accumulated_depreciation:
if not is_first_day_of_the_month(getdate(asset_doc.available_for_use_date)):
from_date = get_last_day(

View File

@@ -159,8 +159,9 @@ def prepare_chart_data(data, filters):
if filters.filter_based_on not in ("Date Range", "Fiscal Year"):
filters_filter_based_on = "Date Range"
date_field = "purchase_date"
filters_from_date = min(data, key=lambda a: a.get(date_field)).get(date_field)
filters_to_date = max(data, key=lambda a: a.get(date_field)).get(date_field)
filtered_data = [d for d in data if d.get(date_field)]
filters_from_date = min(filtered_data, key=lambda a: a.get(date_field)).get(date_field)
filters_to_date = max(filtered_data, key=lambda a: a.get(date_field)).get(date_field)
else:
filters_filter_based_on = filters.filter_based_on
date_field = frappe.scrub(filters.date_based_on)

View File

@@ -902,12 +902,12 @@ def get_mapped_subcontracting_order(source_name, target_doc=None):
)
target_doc.populate_items_table()
source_doc = frappe.get_doc("Purchase Order", source_name)
if target_doc.set_warehouse:
for item in target_doc.items:
item.warehouse = target_doc.set_warehouse
else:
source_doc = frappe.get_doc("Purchase Order", source_name)
if source_doc.set_warehouse:
for item in target_doc.items:
item.warehouse = source_doc.set_warehouse

View File

@@ -513,7 +513,7 @@ erpnext.buying.RequestforQuotationController = class RequestforQuotationControll
method: "frappe.desk.doctype.tag.tag.get_tagged_docs",
args: {
doctype: "Supplier",
tag: args.tag,
tag: "%" + args.tag + "%",
},
callback: load_suppliers,
});

View File

@@ -1,8 +1,4 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
frappe.require("assets/erpnext/js/purchase_trends_filters.js", function () {
frappe.query_reports["Purchase Order Trends"] = {
filters: erpnext.get_purchase_trends_filters(),
};
});
frappe.query_reports["Purchase Order Trends"] = $.extend({}, erpnext.purchase_trends_filters);

View File

@@ -2183,10 +2183,10 @@ class AccountsController(TransactionBase):
for d in self.get("payment_schedule"):
if d.invoice_portion:
d.payment_amount = flt(
grand_total * flt(d.invoice_portion / 100), d.precision("payment_amount")
grand_total * flt(d.invoice_portion) / 100, d.precision("payment_amount")
)
d.base_payment_amount = flt(
base_grand_total * flt(d.invoice_portion / 100), d.precision("base_payment_amount")
base_grand_total * flt(d.invoice_portion) / 100, d.precision("base_payment_amount")
)
d.outstanding = d.payment_amount
elif not d.invoice_portion:

View File

@@ -422,6 +422,7 @@ def get_batches_from_stock_ledger_entries(searchfields, txt, filters, start=0, p
& (stock_ledger_entry.batch_no.isnotnull())
)
.groupby(stock_ledger_entry.batch_no, stock_ledger_entry.warehouse)
.having(Sum(stock_ledger_entry.actual_qty) > 0)
.offset(start)
.limit(page_len)
)
@@ -472,6 +473,7 @@ def get_batches_from_serial_and_batch_bundle(searchfields, txt, filters, start=0
& (stock_ledger_entry.serial_and_batch_bundle.isnotnull())
)
.groupby(bundle.batch_no, bundle.warehouse)
.having(Sum(bundle.qty) > 0)
.offset(start)
.limit(page_len)
)

View File

@@ -1,11 +1,12 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from collections import defaultdict
import frappe
from frappe import _
from frappe.model.meta import get_field_precision
from frappe.utils import flt, format_datetime, get_datetime
from frappe.utils import cint, flt, format_datetime, get_datetime
import erpnext
from erpnext.stock.serial_batch_bundle import get_batches_from_bundle
@@ -513,6 +514,7 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None, return_agai
target_doc.rejected_warehouse = ""
target_doc.warehouse = source_doc.rejected_warehouse
target_doc.received_qty = target_doc.qty
target_doc.return_qty_from_rejected_warehouse = 1
elif doctype == "Purchase Invoice":
returned_qty_map = get_returned_qty_map_for_row(
@@ -570,7 +572,14 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None, return_agai
if default_warehouse_for_sales_return:
target_doc.warehouse = default_warehouse_for_sales_return
if source_doc.item_code:
if (
(source_doc.serial_no or source_doc.batch_no)
and not source_doc.serial_and_batch_bundle
and not source_doc.use_serial_batch_fields
):
target_doc.set("use_serial_batch_fields", 1)
if source_doc.item_code and target_doc.get("use_serial_batch_fields"):
item_details = frappe.get_cached_value(
"Item", source_doc.item_code, ["has_batch_no", "has_serial_no"], as_dict=1
)
@@ -578,14 +587,7 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None, return_agai
if not item_details.has_batch_no and not item_details.has_serial_no:
return
if not target_doc.get("use_serial_batch_fields"):
for qty_field in ["stock_qty", "rejected_qty"]:
if not target_doc.get(qty_field):
continue
update_serial_batch_no(source_doc, target_doc, source_parent, item_details, qty_field)
elif target_doc.get("use_serial_batch_fields"):
update_non_bundled_serial_nos(source_doc, target_doc, source_parent)
update_non_bundled_serial_nos(source_doc, target_doc, source_parent)
def update_non_bundled_serial_nos(source_doc, target_doc, source_parent):
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
@@ -839,3 +841,229 @@ def get_returned_batches(child_doc, parent_doc, batch_no_field=None, ignore_vouc
batches.update(get_batches_from_bundle(ids))
return batches
def available_serial_batch_for_return(field, doctype, reference_ids, is_rejected=False):
available_dict = get_available_serial_batches(field, doctype, reference_ids, is_rejected=is_rejected)
if not available_dict:
frappe.throw(_("No Serial / Batches are available for return"))
return available_dict
def get_available_serial_batches(field, doctype, reference_ids, is_rejected=False):
_bundle_ids = get_serial_and_batch_bundle(field, doctype, reference_ids, is_rejected=is_rejected)
if not _bundle_ids:
return frappe._dict({})
return get_serial_batches_based_on_bundle(field, _bundle_ids)
def get_serial_batches_based_on_bundle(field, _bundle_ids):
available_dict = frappe._dict({})
batch_serial_nos = frappe.get_all(
"Serial and Batch Bundle",
fields=[
"`tabSerial and Batch Entry`.`serial_no`",
"`tabSerial and Batch Entry`.`batch_no`",
"`tabSerial and Batch Entry`.`qty`",
"`tabSerial and Batch Bundle`.`voucher_detail_no`",
"`tabSerial and Batch Bundle`.`voucher_type`",
"`tabSerial and Batch Bundle`.`voucher_no`",
],
filters=[
["Serial and Batch Bundle", "name", "in", _bundle_ids],
["Serial and Batch Entry", "docstatus", "=", 1],
],
order_by="`tabSerial and Batch Bundle`.`creation`, `tabSerial and Batch Entry`.`idx`",
)
for row in batch_serial_nos:
key = row.voucher_detail_no
if frappe.get_cached_value(row.voucher_type, row.voucher_no, "is_return"):
key = frappe.get_cached_value(row.voucher_type + " Item", row.voucher_detail_no, field)
if key not in available_dict:
available_dict[key] = frappe._dict(
{"qty": 0.0, "serial_nos": defaultdict(float), "batches": defaultdict(float)}
)
available_dict[key]["qty"] += row.qty
if row.serial_no:
available_dict[key]["serial_nos"][row.serial_no] += row.qty
elif row.batch_no:
available_dict[key]["batches"][row.batch_no] += row.qty
return available_dict
def get_serial_and_batch_bundle(field, doctype, reference_ids, is_rejected=False):
filters = {"docstatus": 1, "name": ("in", reference_ids), "serial_and_batch_bundle": ("is", "set")}
pluck_field = "serial_and_batch_bundle"
if is_rejected:
del filters["serial_and_batch_bundle"]
filters["rejected_serial_and_batch_bundle"] = ("is", "set")
pluck_field = "rejected_serial_and_batch_bundle"
_bundle_ids = frappe.get_all(
doctype,
filters=filters,
pluck=pluck_field,
)
if not _bundle_ids:
return {}
del filters["name"]
filters[field] = ("in", reference_ids)
if not is_rejected:
_bundle_ids.extend(
frappe.get_all(
doctype,
filters=filters,
pluck="serial_and_batch_bundle",
)
)
else:
fields = [
"serial_and_batch_bundle",
]
if is_rejected:
fields.extend(["rejected_serial_and_batch_bundle", "return_qty_from_rejected_warehouse"])
data = frappe.get_all(
doctype,
fields=fields,
filters=filters,
)
for d in data:
if is_rejected:
if d.get("return_qty_from_rejected_warehouse"):
_bundle_ids.append(d.get("serial_and_batch_bundle"))
else:
_bundle_ids.append(d.get("rejected_serial_and_batch_bundle"))
else:
_bundle_ids.append(d.get("serial_and_batch_bundle"))
return _bundle_ids
def filter_serial_batches(parent_doc, data, row, warehouse_field=None, qty_field=None):
if not qty_field:
qty_field = "qty"
if not warehouse_field:
warehouse_field = "warehouse"
warehouse = row.get(warehouse_field)
qty = abs(row.get(qty_field))
filterd_serial_batch = frappe._dict({"serial_nos": [], "batches": defaultdict(float)})
if data.serial_nos:
available_serial_nos = []
for serial_no, sn_qty in data.serial_nos.items():
if sn_qty != 0:
available_serial_nos.append(serial_no)
if available_serial_nos:
if parent_doc.doctype in ["Purchase Invoice", "Purchase Reecipt"]:
available_serial_nos = get_available_serial_nos(available_serial_nos)
if len(available_serial_nos) > qty:
filterd_serial_batch["serial_nos"] = sorted(available_serial_nos[0 : cint(qty)])
else:
filterd_serial_batch["serial_nos"] = available_serial_nos
elif data.batches:
for batch_no, batch_qty in data.batches.items():
if parent_doc.get("is_internal_customer"):
batch_qty = batch_qty * -1
if batch_qty <= 0:
continue
if parent_doc.doctype in ["Purchase Invoice", "Purchase Reecipt"]:
batch_qty = get_available_batch_qty(
parent_doc,
batch_no,
warehouse,
)
if batch_qty <= 0:
frappe.throw(
_("Batch {0} is not available in warehouse {1}").format(batch_no, warehouse),
title=_("Batch Not Available for Return"),
)
if qty <= 0:
break
if batch_qty > qty:
filterd_serial_batch["batches"][batch_no] = qty
qty = 0
else:
filterd_serial_batch["batches"][batch_no] += batch_qty
qty -= batch_qty
return filterd_serial_batch
def get_available_batch_qty(parent_doc, batch_no, warehouse):
from erpnext.stock.doctype.batch.batch import get_batch_qty
return get_batch_qty(
batch_no,
warehouse,
posting_date=parent_doc.posting_date,
posting_time=parent_doc.posting_time,
for_stock_levels=True,
)
def make_serial_batch_bundle_for_return(data, child_doc, parent_doc, warehouse_field=None):
from erpnext.stock.serial_batch_bundle import SerialBatchCreation
type_of_transaction = "Outward"
if parent_doc.doctype in ["Sales Invoice", "Delivery Note", "POS Invoice"]:
type_of_transaction = "Inward"
if not warehouse_field:
warehouse_field = "warehouse"
warehouse = child_doc.get(warehouse_field)
if parent_doc.get("is_internal_customer"):
warehouse = child_doc.get("target_warehouse")
type_of_transaction = "Outward"
cls_obj = SerialBatchCreation(
{
"type_of_transaction": type_of_transaction,
"item_code": child_doc.item_code,
"warehouse": warehouse,
"serial_nos": data.get("serial_nos"),
"batches": data.get("batches"),
"posting_date": parent_doc.posting_date,
"posting_time": parent_doc.posting_time,
"voucher_type": parent_doc.doctype,
"voucher_no": parent_doc.name,
"voucher_detail_no": child_doc.name,
"qty": child_doc.qty,
"company": parent_doc.company,
"do_not_submit": True,
}
).make_serial_and_batch_bundle()
return cls_obj.name
def get_available_serial_nos(serial_nos, warehouse):
return frappe.get_all(
"Serial No", filters={"warehouse": warehouse, "name": ("in", serial_nos)}, pluck="name"
)

View File

@@ -16,6 +16,11 @@ from erpnext.accounts.general_ledger import (
)
from erpnext.accounts.utils import cancel_exchange_gain_loss_journal, get_fiscal_year
from erpnext.controllers.accounts_controller import AccountsController
from erpnext.controllers.sales_and_purchase_return import (
available_serial_batch_for_return,
filter_serial_batches,
make_serial_batch_bundle_for_return,
)
from erpnext.stock import get_warehouse_account_map
from erpnext.stock.doctype.inventory_dimension.inventory_dimension import (
get_evaluated_inventory_dimension,
@@ -205,6 +210,7 @@ class StockController(AccountsController):
"company": self.company,
"is_rejected": 1 if row.get("rejected_warehouse") else 0,
"use_serial_batch_fields": row.use_serial_batch_fields,
"via_landed_cost_voucher": via_landed_cost_voucher,
"do_not_submit": True if not via_landed_cost_voucher else False,
}
@@ -216,6 +222,125 @@ class StockController(AccountsController):
self.update_bundle_details(bundle_details, table_name, row, is_rejected=True)
self.create_serial_batch_bundle(bundle_details, row)
def make_bundle_for_sales_purchase_return(self, table_name=None):
if not self.get("is_return"):
return
if not table_name:
table_name = "items"
self.make_bundle_for_non_rejected_qty(table_name)
if self.doctype in ["Purchase Invoice", "Purchase Receipt"]:
self.make_bundle_for_rejected_qty(table_name)
def make_bundle_for_rejected_qty(self, table_name=None):
field, reference_ids = self.get_reference_ids(
table_name, "rejected_qty", "rejected_serial_and_batch_bundle"
)
if not reference_ids:
return
child_doctype = self.doctype + " Item"
available_dict = available_serial_batch_for_return(
field, child_doctype, reference_ids, is_rejected=True
)
for row in self.get(table_name):
if data := available_dict.get(row.get(field)):
qty_field = "rejected_qty"
warehouse_field = "rejected_warehouse"
if row.get("return_qty_from_rejected_warehouse"):
qty_field = "qty"
warehouse_field = "warehouse"
data = filter_serial_batches(
self, data, row, warehouse_field=warehouse_field, qty_field=qty_field
)
bundle = make_serial_batch_bundle_for_return(data, row, self, warehouse_field)
if row.get("return_qty_from_rejected_warehouse"):
row.db_set(
{
"serial_and_batch_bundle": bundle,
"batch_no": "",
"serial_no": "",
}
)
else:
row.db_set(
{
"rejected_serial_and_batch_bundle": bundle,
"batch_no": "",
"rejected_serial_no": "",
}
)
def make_bundle_for_non_rejected_qty(self, table_name):
field, reference_ids = self.get_reference_ids(table_name)
if not reference_ids:
return
child_doctype = self.doctype + " Item"
available_dict = available_serial_batch_for_return(field, child_doctype, reference_ids)
for row in self.get(table_name):
if data := available_dict.get(row.get(field)):
data = filter_serial_batches(self, data, row)
bundle = make_serial_batch_bundle_for_return(data, row, self)
row.db_set(
{
"serial_and_batch_bundle": bundle,
"batch_no": "",
"serial_no": "",
}
)
def get_reference_ids(self, table_name, qty_field=None, bundle_field=None) -> tuple[str, list[str]]:
field = {
"Sales Invoice": "sales_invoice_item",
"Delivery Note": "dn_detail",
"Purchase Receipt": "purchase_receipt_item",
"Purchase Invoice": "purchase_invoice_item",
"POS Invoice": "pos_invoice_item",
}.get(self.doctype)
if not bundle_field:
bundle_field = "serial_and_batch_bundle"
if not qty_field:
qty_field = "qty"
reference_ids = []
for row in self.get(table_name):
if not self.is_serial_batch_item(row.item_code):
continue
if (
row.get(field)
and (
qty_field == "qty"
and not row.get("return_qty_from_rejected_warehouse")
or qty_field == "rejected_qty"
and (row.get("return_qty_from_rejected_warehouse") or row.get("rejected_warehouse"))
)
and not row.get("use_serial_batch_fields")
and not row.get(bundle_field)
):
reference_ids.append(row.get(field))
return field, reference_ids
@frappe.request_cache
def is_serial_batch_item(self, item_code) -> bool:
item_details = frappe.db.get_value("Item", item_code, ["has_serial_no", "has_batch_no"], as_dict=1)
if item_details.has_serial_no or item_details.has_batch_no:
return True
return False
def update_bundle_details(self, bundle_details, table_name, row, is_rejected=False):
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
@@ -590,6 +715,9 @@ class StockController(AccountsController):
row.db_set("rejected_serial_and_batch_bundle", None)
if row.get("current_serial_and_batch_bundle"):
row.db_set("current_serial_and_batch_bundle", None)
def set_serial_and_batch_bundle(self, table_name=None, ignore_validate=False):
if not table_name:
table_name = "items"
@@ -610,35 +738,16 @@ class StockController(AccountsController):
def make_package_for_transfer(
self, serial_and_batch_bundle, warehouse, type_of_transaction=None, do_not_submit=None
):
bundle_doc = frappe.get_doc("Serial and Batch Bundle", serial_and_batch_bundle)
if not type_of_transaction:
type_of_transaction = "Inward"
bundle_doc = frappe.copy_doc(bundle_doc)
bundle_doc.warehouse = warehouse
bundle_doc.type_of_transaction = type_of_transaction
bundle_doc.voucher_type = self.doctype
bundle_doc.voucher_no = "" if self.is_new() or self.docstatus == 2 else self.name
bundle_doc.is_cancelled = 0
for row in bundle_doc.entries:
row.is_outward = 0
row.qty = abs(row.qty)
row.stock_value_difference = abs(row.stock_value_difference)
if type_of_transaction == "Outward":
row.qty *= -1
row.stock_value_difference *= row.stock_value_difference
row.is_outward = 1
row.warehouse = warehouse
bundle_doc.calculate_qty_and_amount()
bundle_doc.flags.ignore_permissions = True
bundle_doc.flags.ignore_validate = True
bundle_doc.save(ignore_permissions=True)
return bundle_doc.name
return make_bundle_for_material_transfer(
is_new=self.is_new(),
docstatus=self.docstatus,
voucher_type=self.doctype,
voucher_no=self.name,
serial_and_batch_bundle=serial_and_batch_bundle,
warehouse=warehouse,
type_of_transaction=type_of_transaction,
do_not_submit=do_not_submit,
)
def get_sl_entries(self, d, args):
sl_dict = frappe._dict(
@@ -1556,3 +1665,38 @@ def create_item_wise_repost_entries(
repost_entries.append(repost_entry)
return repost_entries
def make_bundle_for_material_transfer(**kwargs):
if isinstance(kwargs, dict):
kwargs = frappe._dict(kwargs)
bundle_doc = frappe.get_doc("Serial and Batch Bundle", kwargs.serial_and_batch_bundle)
if not kwargs.type_of_transaction:
kwargs.type_of_transaction = "Inward"
bundle_doc = frappe.copy_doc(bundle_doc)
bundle_doc.warehouse = kwargs.warehouse
bundle_doc.type_of_transaction = kwargs.type_of_transaction
bundle_doc.voucher_type = kwargs.voucher_type
bundle_doc.voucher_no = "" if kwargs.is_new or kwargs.docstatus == 2 else kwargs.voucher_no
bundle_doc.is_cancelled = 0
for row in bundle_doc.entries:
row.is_outward = 0
row.qty = abs(row.qty)
row.stock_value_difference = abs(row.stock_value_difference)
if kwargs.type_of_transaction == "Outward":
row.qty *= -1
row.stock_value_difference *= row.stock_value_difference
row.is_outward = 1
row.warehouse = kwargs.warehouse
bundle_doc.calculate_qty_and_amount()
bundle_doc.flags.ignore_permissions = True
bundle_doc.flags.ignore_validate = True
bundle_doc.save(ignore_permissions=True)
return bundle_doc.name

View File

@@ -327,13 +327,13 @@ class SubcontractingController(StockController):
consumed_bundles.batch_nos[batch_no] += abs(qty)
# Will be deprecated in v16
if row.serial_no:
if row.serial_no and not consumed_bundles.serial_nos:
self.available_materials[key]["serial_no"] = list(
set(self.available_materials[key]["serial_no"]) - set(get_serial_nos(row.serial_no))
)
# Will be deprecated in v16
if row.batch_no:
if row.batch_no and not consumed_bundles.batch_nos:
self.available_materials[key]["batch_no"][row.batch_no] -= row.consumed_qty
def get_available_materials(self):

View File

@@ -118,6 +118,8 @@ class Opportunity(TransactionBase, CRMNote):
self.title = self.customer_name
self.calculate_totals()
def on_update(self):
self.update_prospect()
def map_fields(self):

View File

@@ -591,13 +591,13 @@
"default": "0",
"fieldname": "fg_based_operating_cost",
"fieldtype": "Check",
"label": "FG based Operating Cost"
"label": "Finished Goods based Operating Cost"
},
{
"depends_on": "fg_based_operating_cost",
"fieldname": "fg_based_section_section",
"fieldtype": "Section Break",
"label": "FG Based Operating Cost Section"
"label": "Finished Goods Based Operating Cost"
},
{
"depends_on": "fg_based_operating_cost",
@@ -637,7 +637,7 @@
"image_field": "image",
"is_submittable": 1,
"links": [],
"modified": "2024-04-02 16:22:47.518411",
"modified": "2024-06-03 16:24:47.518411",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "BOM",

View File

@@ -80,6 +80,18 @@ class BOMCreator(Document):
if row.is_expandable and row.item_code == self.item_code:
frappe.throw(_("Item {0} cannot be added as a sub-assembly of itself").format(row.item_code))
if not row.parent_row_no and row.fg_item and row.fg_item != self.item_code:
frappe.throw(
_("At row {0}: set Parent Row No for item {1}").format(row.idx, row.item_code),
title=_("Set Parent Row No in Items Table"),
)
elif row.parent_row_no and row.fg_item == self.item_code:
frappe.throw(
_("At row {0}: Parent Row No cannot be set for item {1}").format(row.idx, row.item_code),
title=_("Remove Parent Row No in Items Table"),
)
def set_status(self, save=False):
self.status = {
0: "Draft",
@@ -410,6 +422,10 @@ def add_sub_assembly(**kwargs):
parent_row_no = item_row.idx
name = ""
else:
parent_row_no = [row.idx for row in doc.items if row.name == kwargs.fg_reference_id]
if parent_row_no:
parent_row_no = parent_row_no[0]
for row in bom_item.get("items"):
row = frappe._dict(row)

View File

@@ -70,7 +70,7 @@
"fieldname": "fg_item",
"fieldtype": "Link",
"in_list_view": 1,
"label": "FG Item",
"label": "Finished Goods Item",
"options": "Item",
"reqd": 1
},
@@ -203,7 +203,7 @@
{
"fieldname": "fg_reference_id",
"fieldtype": "Data",
"label": "FG Reference",
"label": "Finished Goods Reference",
"no_copy": 1,
"read_only": 1
},
@@ -230,7 +230,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2023-11-16 13:34:06.321061",
"modified": "2024-06-03 18:45:24.339532",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "BOM Creator Item",

View File

@@ -214,7 +214,11 @@ class JobCard(Document):
if d.to_time and get_datetime(d.from_time) > get_datetime(d.to_time):
frappe.throw(_("Row {0}: From time must be less than to time").format(d.idx))
data = self.get_overlap_for(d)
open_job_cards = []
if d.get("employee"):
open_job_cards = self.get_open_job_cards(d.get("employee"))
data = self.get_overlap_for(d, open_job_cards=open_job_cards)
if data:
frappe.throw(
_("Row {0}: From Time and To Time of {1} is overlapping with {2}").format(
@@ -235,12 +239,12 @@ class JobCard(Document):
for row in self.sub_operations:
self.total_completed_qty += row.completed_qty
def get_overlap_for(self, args):
def get_overlap_for(self, args, open_job_cards=None):
time_logs = []
time_logs.extend(self.get_time_logs(args, "Job Card Time Log"))
time_logs.extend(self.get_time_logs(args, "Job Card Scheduled Time"))
time_logs.extend(self.get_time_logs(args, "Job Card Scheduled Time", open_job_cards=open_job_cards))
if not time_logs:
return {}
@@ -304,7 +308,7 @@ class JobCard(Document):
return True
return overlap
def get_time_logs(self, args, doctype):
def get_time_logs(self, args, doctype, open_job_cards=None):
jc = frappe.qb.DocType("Job Card")
jctl = frappe.qb.DocType(doctype)
@@ -341,8 +345,14 @@ class JobCard(Document):
if self.workstation:
query = query.where(jc.workstation == self.workstation)
if args.get("employee") and doctype == "Job Card Time Log":
query = query.where(jctl.employee == args.get("employee"))
if args.get("employee"):
if not open_job_cards and doctype == "Job Card Scheduled Time":
return []
if doctype == "Job Card Time Log":
query = query.where(jctl.employee == args.get("employee"))
else:
query = query.where(jc.name.isin(open_job_cards))
if doctype != "Job Card Time Log":
query = query.where(jc.total_time_in_mins == 0)
@@ -351,6 +361,27 @@ class JobCard(Document):
return time_logs
def get_open_job_cards(self, employee):
jc = frappe.qb.DocType("Job Card")
jctl = frappe.qb.DocType("Job Card Time Log")
query = (
frappe.qb.from_(jc)
.left_join(jctl)
.on(jc.name == jctl.parent)
.select(jc.name)
.where(
(jctl.parent == jc.name)
& (jc.workstation == self.workstation)
& (jctl.employee == employee)
& (jc.docstatus < 1)
& (jc.name != self.name)
)
)
jobs = query.run(as_dict=True)
return [job.get("name") for job in jobs] if jobs else []
def get_workstation_based_on_available_slot(self, existing_time_logs) -> dict:
workstations = get_workstations(self.workstation_type)
if workstations:

View File

@@ -42,8 +42,7 @@
"fieldname": "completed_qty",
"fieldtype": "Float",
"in_list_view": 1,
"label": "Completed Qty",
"reqd": 1
"label": "Completed Qty"
},
{
"fieldname": "employee",
@@ -64,7 +63,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2020-12-23 14:30:00.970916",
"modified": "2024-05-21 12:40:55.765860",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Job Card Time Log",
@@ -74,4 +73,4 @@
"sort_field": "modified",
"sort_order": "ASC",
"track_changes": 1
}
}

View File

@@ -452,6 +452,10 @@ class ProductionPlan(Document):
{"sales_order": data.parent, "sales_order_item": data.name, "qty": data.pending_qty}
)
bom_no = data.bom_no or item_details and item_details.bom_no or ""
if not bom_no:
continue
pi = self.append(
"po_items",
{
@@ -459,7 +463,7 @@ class ProductionPlan(Document):
"item_code": data.item_code,
"description": data.description or item_details.description,
"stock_uom": item_details and item_details.stock_uom or "",
"bom_no": data.bom_no or item_details and item_details.bom_no or "",
"bom_no": bom_no,
"planned_qty": data.pending_qty,
"pending_qty": data.pending_qty,
"planned_start_date": now_datetime(),

View File

@@ -328,6 +328,28 @@ class TestProductionPlan(FrappeTestCase):
self.assertEqual(pln2.po_items[0].bom_no, bom2.name)
def test_production_plan_with_non_active_bom_item(self):
item = make_item("Test Production Item 1 for Non Active BOM", {"is_stock_item": 1}).name
so1 = make_sales_order(item_code=item, qty=1)
pln = frappe.new_doc("Production Plan")
pln.company = so1.company
pln.get_items_from = "Sales Order"
pln.append(
"sales_orders",
{
"sales_order": so1.name,
"sales_order_date": so1.transaction_date,
"customer": so1.customer,
"grand_total": so1.grand_total,
},
)
pln.get_items()
self.assertFalse(pln.po_items)
def test_production_plan_combine_items(self):
"Test combining FG items in Production Plan."
item = "Test Production Item 1"

View File

@@ -85,7 +85,7 @@
"fieldname": "warehouse",
"fieldtype": "Link",
"in_list_view": 1,
"label": "FG Warehouse",
"label": "Finished Goods Warehouse",
"options": "Warehouse"
},
{
@@ -220,7 +220,7 @@
"idx": 1,
"istable": 1,
"links": [],
"modified": "2024-02-27 13:24:43.571844",
"modified": "2024-06-03 13:10:20.252166",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Production Plan Item",

View File

@@ -21,7 +21,8 @@ def get_exploded_items(bom, data, indent=0, qty=1):
exploded_items = frappe.get_all(
"BOM Item",
filters={"parent": bom},
fields=["qty", "bom_no", "qty", "item_code", "item_name", "description", "uom"],
fields=["qty", "bom_no", "qty", "item_code", "item_name", "description", "uom", "idx"],
order_by="idx ASC",
)
for item in exploded_items:

View File

@@ -93,4 +93,11 @@ frappe.query_reports["Exponential Smoothing Forecasting"] = {
},
},
],
formatter: function (value, row, column, data, default_formatter) {
value = default_formatter(value, row, column, data);
if (column.fieldname === "item_code" && value.includes("Total Quantity")) {
value = "<strong>" + value + "</strong>";
}
return value;
},
};

View File

@@ -144,7 +144,7 @@ class ForecastingReport(ExponentialSmoothingForecast):
if not self.data:
return
total_row = {"item_code": _(frappe.bold("Total Quantity"))}
total_row = {"item_code": _("Total Quantity")}
for value in self.data:
for period in self.period_list:

View File

@@ -102,7 +102,12 @@ def get_columns() -> Columns:
"fieldtype": "Float",
"width": "150",
},
{"label": _("FG Value"), "fieldname": "total_fg_value", "fieldtype": "Float", "width": "150"},
{
"label": _("Finished Goods Value"),
"fieldname": "total_fg_value",
"fieldtype": "Float",
"width": "150",
},
{
"label": _("Raw Material Value"),
"fieldname": "total_rm_value",

View File

@@ -364,3 +364,4 @@ erpnext.patches.v15_0.delete_orphaned_asset_movement_item_records
erpnext.patches.v15_0.fix_debit_credit_in_transaction_currency
erpnext.patches.v15_0.remove_cancelled_asset_capitalization_from_asset
erpnext.patches.v15_0.rename_purchase_receipt_amount_to_purchase_amount
erpnext.patches.v14_0.enable_set_priority_for_pricing_rules #1

View File

@@ -13,8 +13,9 @@ def execute():
for d in accounting_dimensions:
doctype = "Asset Repair"
field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": d.fieldname})
docfield = frappe.db.get_value("DocField", {"parent": doctype, "fieldname": d.fieldname})
if field:
if field or docfield:
continue
df = {

View File

@@ -0,0 +1,10 @@
import frappe
def execute():
pr_table = frappe.qb.DocType("Pricing Rule")
(
frappe.qb.update(pr_table)
.set(pr_table.has_priority, 1)
.where((pr_table.priority.isnotnull()) & (pr_table.priority != ""))
).run()

View File

@@ -55,6 +55,14 @@ frappe.ui.form.on("Project", {
filters: filters,
};
});
frm.set_query("cost_center", () => {
return {
filters: {
company: frm.doc.company,
},
};
});
},
refresh: function (frm) {

View File

@@ -1246,8 +1246,8 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
}
qty(doc, cdt, cdn) {
if (!this.frm.doc.__onload?.load_after_mapping) {
let item = frappe.get_doc(cdt, cdn);
let item = frappe.get_doc(cdt, cdn);
if (!this.is_a_mapped_document(item)) {
// item.pricing_rules = ''
frappe.run_serially([
() => this.remove_pricing_rule_for_item(item),
@@ -2295,6 +2295,9 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
if (doc.is_return) {
filters["is_return"] = 1;
if (["Sales Invoice", "Delivery Note"].includes(doc.doctype)) {
filters["is_inward"] = 1;
}
}
if (item.warehouse) filters["warehouse"] = item.warehouse;

View File

@@ -34,5 +34,7 @@ import "./utils/sales_common.js";
import "./controllers/buying.js";
import "./utils/demo.js";
import "./financial_statements.js";
import "./sales_trends_filters.js";
import "./purchase_trends_filters.js";
// import { sum } from 'frappe/public/utils/util.js'

View File

@@ -1,8 +1,8 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
erpnext.get_purchase_trends_filters = function () {
return [
erpnext.purchase_trends_filters = {
filters: [
{
fieldname: "company",
label: __("Company"),
@@ -63,5 +63,5 @@ erpnext.get_purchase_trends_filters = function () {
options: ["", { value: "Item", label: __("Item") }, { value: "Supplier", label: __("Supplier") }],
default: "",
},
];
],
};

View File

@@ -1,8 +1,8 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
erpnext.get_sales_trends_filters = function () {
return [
erpnext.sales_trends_filters = {
filters: [
{
fieldname: "period",
label: __("Period"),
@@ -53,5 +53,5 @@ erpnext.get_sales_trends_filters = function () {
options: "Company",
default: frappe.defaults.get_user_default("Company"),
},
];
],
};

View File

@@ -1183,4 +1183,39 @@ $.extend(erpnext.stock.utils, {
const barcode_scanner = new erpnext.utils.BarcodeScanner({ frm: frm });
barcode_scanner.scan_api_call(child_row.barcode, callback);
},
get_serial_range(range_string, separator) {
/* Return an array of serial numbers generated from a range string.
Examples (using separator "::"):
- "1::5" => ["1", "2", "3", "4", "5"]
- "SN0009::12" => ["SN0009", "SN0010", "SN0011", "SN0012"]
- "ABC//05::8" => ["ABC//05", "ABC//06", "ABC//07", "ABC//08"]
*/
if (!range_string) {
return;
}
const [start_str, end_str] = range_string.trim().split(separator);
if (!start_str || !end_str) {
return;
}
const end_int = parseInt(end_str);
const length_difference = start_str.length - end_str.length;
const start_int = parseInt(start_str.substring(length_difference));
if (isNaN(start_int) || isNaN(end_int)) {
return;
}
const serial_numbers = Array(end_int - start_int + 1)
.fill(1)
.map((x, y) => x + y)
.map((x) => x + start_int - 1);
return serial_numbers.map((val) => {
return start_str.substring(0, length_difference) + val.toString().padStart(end_str.length, "0");
});
},
});

View File

@@ -206,6 +206,16 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate {
label: __("{0} {1} Manually", [primary_label, label]),
depends_on: "eval:doc.import_using_csv_file === 0",
},
{
fieldtype: "Data",
label: __("Enter Serial No Range"),
fieldname: "serial_no_range",
depends_on: "eval:doc.import_using_csv_file === 0",
description: __('Enter "ABC-001::100" for serial nos "ABC-001" to "ABC-100".'),
onchange: () => {
this.set_serial_nos_from_range();
},
},
{
fieldtype: "Small Text",
label: __("Enter Serial Nos"),
@@ -255,6 +265,20 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate {
return fields;
}
set_serial_nos_from_range() {
const serial_no_range = this.dialog.get_value("serial_no_range");
if (!serial_no_range) {
return;
}
const serial_nos = erpnext.stock.utils.get_serial_range(serial_no_range, "::");
if (serial_nos) {
this.dialog.set_value("upload_serial_nos", serial_nos.join("\n"));
}
}
create_serial_nos() {
let { upload_serial_nos } = this.dialog.get_values();
@@ -589,7 +613,7 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate {
}
render_data() {
if (this.bundle) {
if (this.bundle || this.frm.doc.is_return) {
frappe
.call({
method: "erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle.get_serial_batch_ledgers",
@@ -597,6 +621,7 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate {
item_code: this.item.item_code,
name: this.bundle,
voucher_no: !this.frm.is_new() ? this.item.parent : "",
child_row: this.frm.doc.is_return ? this.item : "",
},
})
.then((r) => {

View File

@@ -2,6 +2,8 @@
# License: GNU General Public License v3. See license.txt
import json
import frappe
from frappe.test_runner import make_test_records
from frappe.tests.utils import FrappeTestCase
@@ -321,7 +323,7 @@ class TestCustomer(FrappeTestCase):
frappe.ValidationError,
update_child_qty_rate,
so.doctype,
frappe.json.dumps([modified_item]),
json.dumps([modified_item]),
so.name,
)

View File

@@ -9,5 +9,13 @@ frappe.ui.form.on("Product Bundle", {
query: "erpnext.selling.doctype.product_bundle.product_bundle.get_new_item_code",
};
});
frm.set_query("item_code", "items", () => {
return {
filters: {
has_variants: 0,
},
};
});
},
});

View File

@@ -867,6 +867,9 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
fields: fields,
primary_action: function () {
var data = { items: d.fields_dict.items.grid.get_selected_children() };
if (!data) {
frappe.throw(__("Please select items"));
}
me.frm.call({
method: "make_work_orders",
args: {

View File

@@ -1139,7 +1139,8 @@
"hide_seconds": 1,
"label": "Inter Company Order Reference",
"options": "Purchase Order",
"read_only": 1
"read_only": 1,
"search_index": 1
},
{
"fieldname": "project",
@@ -1645,7 +1646,7 @@
"idx": 105,
"is_submittable": 1,
"links": [],
"modified": "2024-03-29 16:27:41.539613",
"modified": "2024-05-23 16:35:54.905804",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Order",

View File

@@ -794,6 +794,11 @@ def get_requested_item_qty(sales_order):
def make_material_request(source_name, target_doc=None):
requested_item_qty = get_requested_item_qty(source_name)
def postprocess(source, target):
if source.tc_name and frappe.db.get_value("Terms and Conditions", source.tc_name, "buying") != 1:
target.tc_name = None
target.terms = None
def get_remaining_qty(so_item):
return flt(
flt(so_item.qty)
@@ -849,6 +854,7 @@ def make_material_request(source_name, target_doc=None):
},
},
target_doc,
postprocess,
)
return doc
@@ -1224,11 +1230,19 @@ def make_purchase_order_for_default_supplier(source_name, selected_items=None, t
target.discount_amount = 0.0
target.inter_company_order_reference = ""
target.shipping_rule = ""
target.tc_name = ""
target.terms = ""
target.payment_terms_template = ""
target.payment_schedule = []
default_price_list = frappe.get_value("Supplier", supplier, "default_price_list")
if default_price_list:
target.buying_price_list = default_price_list
default_payment_terms = frappe.get_value("Supplier", supplier, "payment_terms")
if default_payment_terms:
target.payment_terms_template = default_payment_terms
if any(item.delivered_by_supplier == 1 for item in source.items):
if source.shipping_address_name:
target.shipping_address = source.shipping_address_name
@@ -1280,7 +1294,6 @@ def make_purchase_order_for_default_supplier(source_name, selected_items=None, t
"contact_person",
"taxes_and_charges",
"shipping_address",
"terms",
],
"validation": {"docstatus": ["=", 1]},
},
@@ -1348,6 +1361,10 @@ def make_purchase_order(source_name, selected_items=None, target_doc=None):
target.discount_amount = 0.0
target.inter_company_order_reference = ""
target.shipping_rule = ""
target.tc_name = ""
target.terms = ""
target.payment_terms_template = ""
target.payment_schedule = []
if is_drop_ship_order(target):
target.customer = source.customer
@@ -1383,7 +1400,6 @@ def make_purchase_order(source_name, selected_items=None, target_doc=None):
"contact_person",
"taxes_and_charges",
"shipping_address",
"terms",
],
"validation": {"docstatus": ["=", 1]},
},

View File

@@ -684,7 +684,7 @@ erpnext.PointOfSale.Controller = class {
const is_stock_item = resp[1];
frappe.dom.unfreeze();
const bold_uom = item_row.stock_uom.bold();
const bold_uom = item_row.uom.bold();
const bold_item_code = item_row.item_code.bold();
const bold_warehouse = warehouse.bold();
const bold_available_qty = available_qty.toString().bold();

View File

@@ -1,8 +1,4 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
frappe.require("assets/erpnext/js/sales_trends_filters.js", function () {
frappe.query_reports["Quotation Trends"] = {
filters: erpnext.get_sales_trends_filters(),
};
});
frappe.query_reports["Quotation Trends"] = $.extend({}, erpnext.sales_trends_filters);

View File

@@ -1,8 +1,4 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
frappe.require("assets/erpnext/js/sales_trends_filters.js", function () {
frappe.query_reports["Sales Order Trends"] = {
filters: erpnext.get_sales_trends_filters(),
};
});
frappe.query_reports["Sales Order Trends"] = $.extend({}, erpnext.sales_trends_filters);

View File

@@ -164,9 +164,10 @@ def prepare_data(
rows = {}
target_qty_amt_field = "target_qty" if filters.get("target_on") == "Quantity" else "target_amount"
qty_or_amount_field = "stock_qty" if filters.get("target_on") == "Quantity" else "base_net_amount"
item_group_parent_child_map = get_item_group_parent_child_map()
for d in sales_users_data:
key = (d.parent, d.item_group)
dist_data = get_periodwise_distribution_data(d.distribution_id, period_list, filters.get("period"))
@@ -191,7 +192,11 @@ def prepare_data(
r.get(sales_field) == d.parent
and period.from_date <= r.get(date_field)
and r.get(date_field) <= period.to_date
and (not sales_user_wise_item_groups.get(d.parent) or r.item_group == d.item_group)
and (
not sales_user_wise_item_groups.get(d.parent)
or r.item_group == d.item_group
or r.item_group in item_group_parent_child_map.get(d.item_group, [])
)
):
details[p_key] += r.get(qty_or_amount_field, 0)
details[variance_key] = details.get(p_key) - details.get(target_key)
@@ -204,6 +209,25 @@ def prepare_data(
return rows
def get_item_group_parent_child_map():
"""
Returns a dict of all item group parents and leaf children associated with them.
"""
item_groups = frappe.get_all(
"Item Group", fields=["name", "parent_item_group"], order_by="lft desc, rgt desc"
)
item_group_parent_child_map = {}
for item_group in item_groups:
children = item_group_parent_child_map.get(item_group.name, [])
if not children:
children = [item_group.name]
item_group_parent_child_map.setdefault(item_group.parent_item_group, []).extend(children)
return item_group_parent_child_map
def get_actual_data(filters, sales_users_or_territory_data, date_field, sales_field):
fiscal_year = get_fiscal_year(fiscal_year=filters.get("fiscal_year"), as_dict=1)

View File

@@ -205,8 +205,11 @@ def clear_demo_record(document):
if key not in valid_columns:
filters.pop(key, None)
doc = frappe.get_doc(document_type, filters)
doc.delete(ignore_permissions=True)
try:
doc = frappe.get_doc(document_type, filters)
doc.delete(ignore_permissions=True)
except frappe.exceptions.DoesNotExistError:
pass
def delete_company(company):

View File

@@ -12,10 +12,11 @@ frappe.ui.form.on("Company", {
}
});
}
frm.call("check_if_transactions_exist").then((r) => {
frm.toggle_enable("default_currency", !r.message);
});
if (!frm.doc.__islocal) {
frm.call("check_if_transactions_exist").then((r) => {
frm.toggle_enable("default_currency", !r.message);
});
}
},
setup: function (frm) {
frm.__rename_queue = "long";
@@ -251,7 +252,10 @@ erpnext.company.setup_queries = function (frm) {
["discount_allowed_account", { root_type: "Expense" }],
["discount_received_account", { root_type: "Income" }],
["exchange_gain_loss_account", { root_type: ["in", ["Expense", "Income"]] }],
["unrealized_exchange_gain_loss_account", { root_type: ["in", ["Expense", "Income"]] }],
[
"unrealized_exchange_gain_loss_account",
{ root_type: ["in", ["Expense", "Income", "Equity", "Liability"]] },
],
[
"accumulated_depreciation_account",
{ root_type: "Asset", account_type: "Accumulated Depreciation" },

View File

@@ -67,6 +67,7 @@
"default_finance_book",
"advance_payments_section",
"book_advance_payments_in_separate_party_account",
"reconcile_on_advance_payment_date",
"column_break_fwcf",
"default_advance_received_account",
"default_advance_paid_account",
@@ -779,6 +780,14 @@
"fieldtype": "Tab Break",
"label": "Dashboard",
"show_dashboard": 1
},
{
"default": "0",
"depends_on": "eval: doc.book_advance_payments_in_separate_party_account",
"description": "If <b>Enabled</b> - Reconciliation happens on the <b>Advance Payment posting date</b><br>\nIf <b>Disabled</b> - Reconciliation happens on oldest of 2 Dates: <b>Invoice Date</b> or the <b>Advance Payment posting date</b><br>\n",
"fieldname": "reconcile_on_advance_payment_date",
"fieldtype": "Check",
"label": "Reconcile on Advance Payment Date"
}
],
"icon": "fa fa-building",
@@ -786,7 +795,7 @@
"image_field": "company_logo",
"is_tree": 1,
"links": [],
"modified": "2024-04-23 12:38:33.173938",
"modified": "2024-05-27 17:32:49.057386",
"modified_by": "Administrator",
"module": "Setup",
"name": "Company",
@@ -842,6 +851,10 @@
"role": "Accounts Manager",
"share": 1,
"write": 1
},
{
"role": "Auditor",
"select": 1
}
],
"show_name_in_global_search": 1,

View File

@@ -85,6 +85,7 @@ class Company(NestedSet):
parent_company: DF.Link | None
payment_terms: DF.Link | None
phone_no: DF.Data | None
reconcile_on_advance_payment_date: DF.Check
registration_details: DF.Code | None
rgt: DF.Int
round_off_account: DF.Link | None

View File

@@ -42,7 +42,7 @@ class HolidayList(Document):
def validate(self):
self.validate_days()
self.total_holidays = len(self.holidays)
self.validate_dupliacte_date()
self.validate_duplicate_date()
@frappe.whitelist()
def get_weekly_off_dates(self):
@@ -148,7 +148,7 @@ class HolidayList(Document):
def clear_table(self):
self.set("holidays", [])
def validate_dupliacte_date(self):
def validate_duplicate_date(self):
unique_dates = []
for row in self.holidays:
if row.holiday_date in unique_dates:

View File

@@ -26,3 +26,29 @@ class TestVehicle(unittest.TestCase):
}
)
vehicle.insert()
def test_renaming_vehicle(self):
license_plate = random_string(10).upper()
vehicle = frappe.get_doc(
{
"doctype": "Vehicle",
"license_plate": license_plate,
"make": "Skoda",
"model": "Slavia",
"last_odometer": 5000,
"acquisition_date": frappe.utils.nowdate(),
"location": "Mumbai",
"chassis_no": "1234EFGH",
"uom": "Litre",
"vehicle_value": frappe.utils.flt(500000),
}
)
vehicle.insert()
new_license_plate = random_string(10).upper()
frappe.rename_doc("Vehicle", license_plate, new_license_plate)
self.assertEqual(
new_license_plate, frappe.db.get_value("Vehicle", new_license_plate, "license_plate")
)

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